///|
fn metaphone_is_blocking_h_predecessor(code : UInt16) -> Bool {
code == 67 || code == 71 || code == 80 || code == 83 || code == 84
}
///|
fn metaphone_is_soft_g_follower(code : UInt16) -> Bool {
code == 69 || code == 73 || code == 89
}
///|
fn metaphone_prepare_cursor(letters : String) -> RuleCursor {
let maximum = letters.length() * 2 + 4
let cursor = RuleCursor::new(letters, maximum)
if ascii_starts_with_at(letters, 0, "AE") {
cursor.advance(1)
} else if ascii_starts_with_at(letters, 0, "GN") ||
ascii_starts_with_at(letters, 0, "KN") ||
ascii_starts_with_at(letters, 0, "PN") ||
ascii_starts_with_at(letters, 0, "WR") {
cursor.advance(1)
} else if ascii_starts_with_at(letters, 0, "WH") {
cursor.append("W").advance(2)
} else if ascii_starts_with_at(letters, 0, "X") {
cursor.append("S").advance(1)
} else {
cursor
}
}
///|
fn metaphone_encode_c(cursor : RuleCursor) -> RuleCursor {
let previous = cursor.peek(-1)
let next = cursor.peek(1)
if ascii_starts_with_at(cursor.input, cursor.index, "CIA") {
cursor.append("X").advance(3)
} else if ascii_starts_with_at(cursor.input, cursor.index, "CH") {
if previous == 83 {
cursor.append("K").advance(2)
} else {
cursor.append("X").advance(2)
}
} else if metaphone_is_soft_g_follower(next) {
if previous == 83 {
cursor.advance(1)
} else {
cursor.append("S").advance(1)
}
} else {
cursor.append("K").advance(1)
}
}
///|
fn metaphone_encode_g(cursor : RuleCursor) -> RuleCursor {
let previous = cursor.peek(-1)
let next = cursor.peek(1)
if next == 72 {
if previous == 85 {
cursor.append("F").advance(2)
} else {
cursor.advance(2)
}
} else if next == 78 &&
(
cursor.index + 2 == cursor.input.length() ||
ascii_ends_with(cursor.input, "GNED")
) {
cursor.advance(1)
} else if metaphone_is_soft_g_follower(next) {
cursor.append("J").advance(1)
} else {
cursor.append("K").advance(1)
}
}
///|
fn metaphone_encode_s(cursor : RuleCursor) -> RuleCursor {
if ascii_starts_with_at(cursor.input, cursor.index, "SH") {
cursor.append("X").advance(2)
} else if ascii_starts_with_at(cursor.input, cursor.index, "SIO") ||
ascii_starts_with_at(cursor.input, cursor.index, "SIA") {
cursor.append("X").advance(3)
} else {
cursor.append("S").advance(1)
}
}
///|
fn metaphone_encode_t(cursor : RuleCursor) -> RuleCursor {
if ascii_starts_with_at(cursor.input, cursor.index, "TIA") ||
ascii_starts_with_at(cursor.input, cursor.index, "TIO") {
cursor.append("X").advance(3)
} else if ascii_starts_with_at(cursor.input, cursor.index, "TH") {
cursor.append("0").advance(2)
} else if ascii_starts_with_at(cursor.input, cursor.index, "TCH") {
cursor.advance(1)
} else {
cursor.append("T").advance(1)
}
}
///|
fn metaphone_step(cursor : RuleCursor) -> RuleCursor {
let current = cursor.peek(0)
let previous = cursor.peek(-1)
let next = cursor.peek(1)
if cursor.index > 0 && current == previous && current != 67 {
return cursor.advance(1)
}
match current {
65 | 69 | 73 | 79 | 85 =>
if cursor.output.length() == 0 {
cursor.append(current.unsafe_to_char().to_string()).advance(1)
} else {
cursor.advance(1)
}
66 =>
if previous == 77 && cursor.remaining() == 1 {
cursor.advance(1)
} else {
cursor.append("B").advance(1)
}
67 => metaphone_encode_c(cursor)
68 =>
if next == 71 && metaphone_is_soft_g_follower(cursor.peek(2)) {
cursor.append("J").advance(3)
} else {
cursor.append("T").advance(1)
}
70 => cursor.append("F").advance(1)
71 => metaphone_encode_g(cursor)
72 =>
if encoder_ascii_is_vowel(next) &&
!metaphone_is_blocking_h_predecessor(previous) {
cursor.append("H").advance(1)
} else {
cursor.advance(1)
}
74 => cursor.append("J").advance(1)
75 =>
if previous == 67 {
cursor.advance(1)
} else {
cursor.append("K").advance(1)
}
76 => cursor.append("L").advance(1)
77 => cursor.append("M").advance(1)
78 => cursor.append("N").advance(1)
80 =>
if next == 72 {
cursor.append("F").advance(2)
} else {
cursor.append("P").advance(1)
}
81 => cursor.append("K").advance(1)
82 => cursor.append("R").advance(1)
83 => metaphone_encode_s(cursor)
84 => metaphone_encode_t(cursor)
86 => cursor.append("F").advance(1)
87 | 89 =>
if encoder_ascii_is_vowel(next) {
cursor.append(current.unsafe_to_char().to_string()).advance(1)
} else {
cursor.advance(1)
}
88 => cursor.append("KS").advance(1)
90 => cursor.append("S").advance(1)
_ => cursor.advance(1)
}
}
///|
/// Encodes ASCII letters with the original Metaphone rule families.
pub fn metaphone(input : String) -> String {
let letters = normalize_ascii_letters(input)
if letters.length() == 0 {
return ""
}
let mut cursor = metaphone_prepare_cursor(letters)
while cursor.remaining() > 0 {
let next = metaphone_step(cursor)
if next.index <= cursor.index {
abort("Metaphone rule did not advance")
}
cursor = next
}
cursor.output
}