///|
/// What detection needs to know about a text: its length, how many
/// characters each script has, its trigrams and its words. `detect` builds
/// it from one text; an `Accumulator` adds to it chunk by chunk.
priv struct TextStats {
/// UTF-16 code units of the text.
mut length : Int32
/// UTF-16 code units of the part franc samples: the first
/// `max_sample_length` of one text, all of an accumulated stream.
mut sample_length : Int32
/// Code points per script, as `script_counts` counts them.
scripts : FixedArray[Int32]
/// Trigram key (see `pack`) -> occurrences in the sample.
grams : Map[Int64, Int32]
/// Lowercase word (see `words_of`) -> occurrences, in order of first
/// occurrence. Only filled for `Mode::Enhanced`.
words : Map[String, Int32]
}
///|
/// Adds the occurrences of `words` to `counts`.
fn count_words(counts : Map[String, Int32], words : Array[String]) -> Unit {
for word in words {
counts[word] = counts.get_or_default(word, 0) + 1
}
}
///|
/// The statistics of `text`. Words are only counted `with_words`.
fn TextStats::of(text : String, with_words~ : Bool) -> TextStats {
let sample = if text.length() > max_sample_length {
text.view(end_offset=max_sample_length).to_owned()
} else {
text
}
let grams = trigram_counts(sample)
let words : Map[String, Int32] = Map([])
if with_words {
count_words(words, words_of(text))
}
{
length: text.length(),
sample_length: sample.length(),
scripts: script_counts(text),
grams,
words,
}
}
///|
/// Index into `all_scripts` of the most frequent script, ties to the
/// script listed first, or -1 when no character belongs to any script.
fn TextStats::top_script(self : TextStats) -> Int32 {
let mut best = -1
let mut best_count = 0
for s in 0.. best_count {
best = s
best_count = self.scripts[s]
}
}
best
}