///|
fn find_value_frequency(
frequencies : Array[RomanIndexValueFrequency],
value : Int,
) -> Int {
for index = 0; index < frequencies.length(); index = index + 1 {
if frequencies[index].value == value {
return index
}
}
-1
}
///|
/// Count values in first-occurrence order using Int64 accumulators.
pub fn roman_index_value_frequencies(
index : RomanDocumentIndex,
) -> Array[RomanIndexValueFrequency] {
let frequencies : Array[RomanIndexValueFrequency] = []
for entry in index.entries {
let position = find_value_frequency(frequencies, entry.value)
if position < 0 {
frequencies.push({ value: entry.value, count: 1L })
} else {
frequencies[position] = {
value: entry.value,
count: frequencies[position].count + 1L,
}
}
}
frequencies
}
///|
fn find_canonical_frequency(
frequencies : Array[RomanIndexCanonicalFrequency],
canonical : String,
) -> Int {
for index = 0; index < frequencies.length(); index = index + 1 {
if frequencies[index].canonical == canonical {
return index
}
}
-1
}
///|
/// Count canonical spellings in first-occurrence order.
pub fn roman_index_canonical_frequencies(
index : RomanDocumentIndex,
) -> Array[RomanIndexCanonicalFrequency] {
let frequencies : Array[RomanIndexCanonicalFrequency] = []
for entry in index.entries {
let position = find_canonical_frequency(frequencies, entry.canonical)
if position < 0 {
frequencies.push({ canonical: entry.canonical, count: 1L })
} else {
frequencies[position] = {
canonical: entry.canonical,
count: frequencies[position].count + 1L,
}
}
}
frequencies
}
///|
fn find_mode_frequency(
frequencies : Array[RomanIndexModeFrequency],
mode : RomanMode,
) -> Int {
for index = 0; index < frequencies.length(); index = index + 1 {
if frequencies[index].mode == mode {
return index
}
}
-1
}
///|
/// Count notation modes in first-occurrence order.
pub fn roman_index_mode_frequencies(
index : RomanDocumentIndex,
) -> Array[RomanIndexModeFrequency] {
let frequencies : Array[RomanIndexModeFrequency] = []
for entry in index.entries {
let position = find_mode_frequency(frequencies, entry.mode)
if position < 0 {
frequencies.push({ mode: entry.mode, count: 1L })
} else {
frequencies[position] = {
mode: entry.mode,
count: frequencies[position].count + 1L,
}
}
}
frequencies
}
///|
fn count_entries_for_document(
entries : Array[RomanIndexEntry],
document_id : String,
) -> Int64 {
let mut count = 0L
for entry in entries {
if entry.document_id == document_id {
count = count + 1L
}
}
count
}
///|
/// Count accepted entries for every document, including empty documents.
pub fn roman_index_document_frequencies(
index : RomanDocumentIndex,
) -> Array[RomanIndexDocumentFrequency] {
let frequencies : Array[RomanIndexDocumentFrequency] = []
for document in index.documents {
frequencies.push({
document_id: document.id,
count: count_entries_for_document(index.entries, document.id),
})
}
frequencies
}
///|
fn find_source_frequency(
frequencies : Array[RomanIndexSourceFrequency],
source_text : String,
) -> Int {
for index = 0; index < frequencies.length(); index = index + 1 {
if frequencies[index].source_text == source_text {
return index
}
}
-1
}
///|
/// Count exact source spellings in first-occurrence order.
pub fn roman_index_source_frequencies(
index : RomanDocumentIndex,
) -> Array[RomanIndexSourceFrequency] {
let frequencies : Array[RomanIndexSourceFrequency] = []
for entry in index.entries {
let position = find_source_frequency(frequencies, entry.source_text)
if position < 0 {
frequencies.push({ source_text: entry.source_text, count: 1L })
} else {
frequencies[position] = {
source_text: entry.source_text,
count: frequencies[position].count + 1L,
}
}
}
frequencies
}
///|
fn find_normalized_frequency(
frequencies : Array[RomanIndexNormalizedFrequency],
normalized_text : String,
) -> Int {
for index = 0; index < frequencies.length(); index = index + 1 {
if frequencies[index].normalized_text == normalized_text {
return index
}
}
-1
}
///|
/// Count normalized spellings in first-occurrence order.
pub fn roman_index_normalized_frequencies(
index : RomanDocumentIndex,
) -> Array[RomanIndexNormalizedFrequency] {
let frequencies : Array[RomanIndexNormalizedFrequency] = []
for entry in index.entries {
let position = find_normalized_frequency(frequencies, entry.normalized_text)
if position < 0 {
frequencies.push({ normalized_text: entry.normalized_text, count: 1L })
} else {
frequencies[position] = {
normalized_text: entry.normalized_text,
count: frequencies[position].count + 1L,
}
}
}
frequencies
}
///|
fn find_token_count_frequency(
frequencies : Array[RomanIndexTokenCountFrequency],
token_count : Int,
) -> Int {
for index = 0; index < frequencies.length(); index = index + 1 {
if frequencies[index].token_count == token_count {
return index
}
}
-1
}
///|
/// Count parser token lengths in first-occurrence order.
pub fn roman_index_token_count_frequencies(
index : RomanDocumentIndex,
) -> Array[RomanIndexTokenCountFrequency] {
let frequencies : Array[RomanIndexTokenCountFrequency] = []
for entry in index.entries {
let position = find_token_count_frequency(frequencies, entry.token_count)
if position < 0 {
frequencies.push({ token_count: entry.token_count, count: 1L })
} else {
frequencies[position] = {
token_count: entry.token_count,
count: frequencies[position].count + 1L,
}
}
}
frequencies
}
///|
fn count_retained_rejections_for_document(
rejections : Array[RomanIndexRejectionSummary],
document_id : String,
) -> Int64 {
let mut count = 0L
for rejection in rejections {
if rejection.document_id == document_id {
count = count + 1L
}
}
count
}
///|
fn document_entry_extrema(
entries : Array[RomanIndexEntry],
document_id : String,
) -> (Int?, Int?) {
let mut minimum : Int? = None
let mut maximum : Int? = None
for entry in entries {
if entry.document_id == document_id {
match minimum {
None => minimum = Some(entry.value)
Some(current) if entry.value < current => minimum = Some(entry.value)
_ => ()
}
match maximum {
None => maximum = Some(entry.value)
Some(current) if entry.value > current => maximum = Some(entry.value)
_ => ()
}
}
}
(minimum, maximum)
}
///|
fn document_distinct_value_count(
entries : Array[RomanIndexEntry],
document_id : String,
) -> Int64 {
let values : Array[Int] = []
for entry in entries {
if entry.document_id == document_id {
let mut seen = false
for value in values {
if value == entry.value {
seen = true
break
}
}
if !seen {
values.push(entry.value)
}
}
}
values.length().to_int64()
}
///|
fn count_document_unicode_entries(
entries : Array[RomanIndexEntry],
document_id : String,
) -> Int64 {
let mut count = 0L
for entry in entries {
if entry.document_id == document_id && entry.used_unicode_compatibility {
count = count + 1L
}
}
count
}
///|
fn count_document_normalized_entries(
entries : Array[RomanIndexEntry],
document_id : String,
) -> Int64 {
let mut count = 0L
for entry in entries {
if entry.document_id == document_id && entry.normalized_input {
count = count + 1L
}
}
count
}
///|
fn summarize_index_document(
index : RomanDocumentIndex,
document : RomanIndexDocumentMetadata,
) -> RomanIndexDocumentSummary {
let (minimum, maximum) = document_entry_extrema(index.entries, document.id)
{
document_id: document.id,
parse_mode: document.parse_mode,
source_length: document.source_length,
candidates_examined: document.candidates_examined.to_int64(),
accepted: count_entries_for_document(index.entries, document.id),
rejected: document.rejected_count.to_int64(),
retained_rejections: count_retained_rejections_for_document(
index.rejections,
document.id,
),
first_ordinal: document.first_ordinal,
min_value: minimum,
max_value: maximum,
distinct_values: document_distinct_value_count(index.entries, document.id),
used_unicode: count_document_unicode_entries(index.entries, document.id),
normalized_inputs: count_document_normalized_entries(
index.entries,
document.id,
),
}
}
///|
fn index_entry_extrema(entries : Array[RomanIndexEntry]) -> (Int?, Int?) {
let mut minimum : Int? = None
let mut maximum : Int? = None
for entry in entries {
match minimum {
None => minimum = Some(entry.value)
Some(current) if entry.value < current => minimum = Some(entry.value)
_ => ()
}
match maximum {
None => maximum = Some(entry.value)
Some(current) if entry.value > current => maximum = Some(entry.value)
_ => ()
}
}
(minimum, maximum)
}
///|
fn count_index_rejections(
documents : Array[RomanIndexDocumentMetadata],
) -> Int64 {
let mut count = 0L
for document in documents {
count = count + document.rejected_count.to_int64()
}
count
}
///|
fn count_index_unicode_entries(entries : Array[RomanIndexEntry]) -> Int64 {
let mut count = 0L
for entry in entries {
if entry.used_unicode_compatibility {
count = count + 1L
}
}
count
}
///|
fn count_index_normalized_entries(entries : Array[RomanIndexEntry]) -> Int64 {
let mut count = 0L
for entry in entries {
if entry.normalized_input {
count = count + 1L
}
}
count
}
///|
/// Recompute deterministic index statistics from retained entries and metadata.
pub fn summarize_roman_document_index(
index : RomanDocumentIndex,
) -> RomanIndexSummary {
let documents : Array[RomanIndexDocumentSummary] = []
for document in index.documents {
documents.push(summarize_index_document(index, document))
}
let (minimum, maximum) = index_entry_extrema(index.entries)
{
document_count: index.documents.length().to_int64(),
entry_count: index.entries.length().to_int64(),
rejected_count: count_index_rejections(index.documents),
min_value: minimum,
max_value: maximum,
distinct_values: roman_index_value_frequencies(index).length().to_int64(),
used_unicode: count_index_unicode_entries(index.entries),
normalized_inputs: count_index_normalized_entries(index.entries),
documents,
}
}