///|
fn caverphone_clean(input : String) -> String {
  ascii_lower_text(normalize_ascii_letters(input))
}

///|
fn replace_literal_all(
  input : String,
  target : String,
  replacement : String,
) -> String {
  if target.length() == 0 {
    return input
  }
  let mut output = ""
  let mut index = 0
  while index < input.length() {
    if ascii_starts_with_at(input, index, target) {
      output = output + replacement
      index = index + target.length()
    } else {
      output = output + input[index].unsafe_to_char().to_string()
      index = index + 1
    }
  }
  output
}

///|
fn replace_literal_start(
  input : String,
  target : String,
  replacement : String,
) -> String {
  if ascii_starts_with_at(input, 0, target) {
    replacement + ascii_slice(input, target.length(), input.length())
  } else {
    input
  }
}

///|
fn replace_literal_end(
  input : String,
  target : String,
  replacement : String,
) -> String {
  if ascii_ends_with(input, target) {
    ascii_slice(input, 0, input.length() - target.length()) + replacement
  } else {
    input
  }
}

///|
fn replace_initial_vowel(input : String) -> String {
  if input.length() > 0 && encoder_ascii_is_vowel(input[0] - 32) {
    "A" + ascii_slice(input, 1, input.length())
  } else {
    input
  }
}

///|
fn replace_lowercase_vowels(input : String) -> String {
  let mut output = ""
  for i = 0; i < input.length(); i = i + 1 {
    let code = input[i]
    if code == 97 || code == 101 || code == 105 || code == 111 || code == 117 {
      output = output + "3"
    } else {
      output = output + code.unsafe_to_char().to_string()
    }
  }
  output
}

///|
fn collapse_lowercase_run(
  input : String,
  target : UInt16,
  replacement : String,
) -> String {
  let mut output = ""
  let mut index = 0
  while index < input.length() {
    if input[index] == target {
      output = output + replacement
      index = index + 1
      while index < input.length() && input[index] == target {
        index = index + 1
      }
    } else {
      output = output + input[index].unsafe_to_char().to_string()
      index = index + 1
    }
  }
  output
}

///|
fn caverphone_collapse_consonants(input : String) -> String {
  let mut value = input
  value = collapse_lowercase_run(value, 115, "S")
  value = collapse_lowercase_run(value, 116, "T")
  value = collapse_lowercase_run(value, 112, "P")
  value = collapse_lowercase_run(value, 107, "K")
  value = collapse_lowercase_run(value, 102, "F")
  value = collapse_lowercase_run(value, 109, "M")
  value = collapse_lowercase_run(value, 110, "N")
  value
}

///|
fn remove_caverphone_markers(input : String) -> String {
  replace_literal_all(replace_literal_all(input, "2", ""), "3", "")
}

///|
fn pad_caverphone(input : String, length : Int) -> String {
  let mut output = input
  while output.length() < length {
    output = output + "1"
  }
  ascii_slice(output, 0, length)
}

///|
fn caverphone_common_replacements(input : String) -> String {
  let mut value = input
  value = replace_literal_all(value, "cq", "2q")
  value = replace_literal_all(value, "ci", "si")
  value = replace_literal_all(value, "ce", "se")
  value = replace_literal_all(value, "cy", "sy")
  value = replace_literal_all(value, "tch", "2ch")
  value = replace_literal_all(value, "c", "k")
  value = replace_literal_all(value, "q", "k")
  value = replace_literal_all(value, "x", "k")
  value = replace_literal_all(value, "v", "f")
  value = replace_literal_all(value, "dg", "2g")
  value = replace_literal_all(value, "tio", "sio")
  value = replace_literal_all(value, "tia", "sia")
  value = replace_literal_all(value, "d", "t")
  value = replace_literal_all(value, "ph", "fh")
  value = replace_literal_all(value, "b", "p")
  value = replace_literal_all(value, "sh", "s2")
  replace_literal_all(value, "z", "s")
}

///|
fn caverphone_initial_exceptions(
  input : String,
  include_trough : Bool,
) -> String {
  let mut value = input
  value = replace_literal_start(value, "cough", "cou2f")
  value = replace_literal_start(value, "rough", "rou2f")
  value = replace_literal_start(value, "tough", "tou2f")
  value = replace_literal_start(value, "enough", "enou2f")
  if include_trough {
    value = replace_literal_start(value, "trough", "trou2f")
  }
  replace_literal_start(value, "gn", "2n")
}