///|
fn[Query : StringLike, Item : StringLike] find_start_end_indices(
  query~ : Query,
  item~ : Item,
) -> StartEndIndices {
  let dst = the_one_and_only
  let mut item_idx = 0
  let mut query_idx = 0
  while item_idx < item.length() && query_idx < query.length() {
    if equal_ignore_case(item[item_idx], query[query_idx]) {
      query_idx += 1
    }
    item_idx += 1
  }
  if query_idx < query.length() {
    dst.found = false
  } else {
    let end_idx = item_idx
    let mut item_idx = end_idx - 1
    let mut query_idx = query.length() - 1
    while query_idx >= 0 {
      if equal_ignore_case(item[item_idx], query[query_idx]) {
        query_idx -= 1
      }
      item_idx -= 1
    }
    let start_idx = item_idx + 1
    dst.found = true
    dst.start_idx = start_idx
    dst.end_idx = end_idx
  }
  dst
}

///|
fn[A, Query : StringLike, Item : StringLike] fold_matching_indices_single_query(
  query : Query,
  item~ : Item,
  init~ : A,
  f~ : (A, Int) -> A,
) -> A {
  let { start_idx, end_idx, found } = find_start_end_indices(query~, item~)
  let mut acc = init
  if found {
    let mut query_idx = 0
    for item_idx in start_idx.. Int {
  let { found, start_idx, end_idx } = find_start_end_indices(query~, item~)
  if not(found) {
    0
  } else {
    let start_char_class = if start_idx == 0 {
      NonWord
    } else {
      CharClass::from_char(item[start_idx - 1])
    }
    let mut query_idx = 0
    let mut score = 0
    let mut in_gap = false
    let mut prev_char_class = start_char_class
    let mut prev_char_score = 0
    for item_idx in start_idx.. start_of_word_bonus
            (Lower, Upper) | (_, Digit) => camel_case_bonus
            (_, NonWord) => non_word_bonus
            _ => match_bonus
          }
          let with_start_of_item_bonus = if item_idx == 0 {
            base_score + start_of_item_bonus
          } else {
            base_score
          }
          let with_consecutive_bonus = if in_gap {
            with_start_of_item_bonus
          } else {
            max(with_start_of_item_bonus, prev_char_score)
          }
          let with_wrong_case_penalty = if item_char == query_char ||
            not(case_sensitive) {
            with_consecutive_bonus
          } else {
            with_consecutive_bonus - wrong_case_penalty
          }
          if query_idx == 0 {
            first_char_multiplier * with_wrong_case_penalty
          } else {
            with_wrong_case_penalty
          }
        }
        query_idx += 1
        score += this_char_score
        prev_char_class = char_class
        prev_char_score = this_char_score
        in_gap = false
      } else {
        let this_char_score = if not(in_gap) {
          -continue_gap_penalty
        } else {
          -start_gap_penalty
        }
        score += this_char_score
        in_gap = true
      }
    }
    score
  }
}