///|
/// A summary of building a word dictionary from plain text. `parsed_tokens`
/// counts every occurrence, including repetitions and below-threshold words.
/// `added_words` counts terms newly admitted to the suggestion index.
pub(all) struct CorpusReport {
  parsed_tokens : Int32
  added_words : Int32
} derive(Eq, Debug)

///|
/// Count words in plain text, merging them into this dictionary. Each token
/// adds one occurrence using the existing frequency and threshold rules.
/// Tokens are maximal runs of Unicode 16.0 letters, numbers, and straight or
/// curly apostrophes (U+0027 and U+2019). All other characters are separators,
/// including underscores, hyphens, combining marks, and emoji. Apostrophes
/// are retained even at token edges or on their own, as in symspellpy.
/// Casing and normalization stay exact; normalize text and queries in the
/// application if needed. This method builds only unigrams and leaves the
/// configured `corpus_size` unchanged. Each call ends its final token, so
/// incremental input should be split at token boundaries, e.g. whole lines.
pub fn SymSpell::create_dictionary(
  self : SymSpell,
  text : String,
) -> CorpusReport {
  let builder = StringBuilder()
  let mut parsed_tokens = 0
  let mut added_words = 0
  for character in text {
    if is_corpus_alphanumeric(character) || character is ('\'' | '’') {
      builder.write_char(character)
    } else if !builder.is_empty() {
      parsed_tokens = parsed_tokens + 1
      if self.create_dictionary_entry(builder.to_string(), 1L) {
        added_words = added_words + 1
      }
      builder.reset()
    }
  }
  if !builder.is_empty() {
    parsed_tokens = parsed_tokens + 1
    if self.create_dictionary_entry(builder.to_string(), 1L) {
      added_words = added_words + 1
    }
  }
  { parsed_tokens, added_words, }
}

///|
fn is_corpus_alphanumeric(character : Char) -> Bool {
  if character.is_ascii() {
    return character.is_ascii_alphabetic() || character.is_ascii_digit()
  }
  let code = character.to_int()
  let mut low = 0
  let mut high = corpus_alphanumeric_ranges.length()
  while low < high {
    let middle = low + (high - low) / 2
    let (first, last) = corpus_alphanumeric_ranges[middle]
    if code < first {
      high = middle
    } else if code > last {
      low = middle + 1
    } else {
      return true
    }
  }
  false
}