use super::common::phonetic_unit::PhoneticUnit;
use super::types::{Context, ContextChar, Phone, PhoneByte, PhoneChar};
pub fn context_matches<U: PhoneticUnit>(
ctx: &Context<U>,
s: &[Phone<U>],
match_start: usize,
pattern_len: usize,
) -> bool {
match ctx {
Context::Initial => match_start == 0,
Context::Final => match_start + pattern_len == s.len(),
Context::BeforeVowel(vowels) => {
let pos = match_start + pattern_len;
if let Some(Phone::Vowel(v)) = s.get(pos) {
vowels.contains(v)
} else {
false
}
}
Context::AfterConsonant(consonants) => {
if match_start == 0 {
false
} else if let Some(phone) = s.get(match_start - 1) {
match phone {
Phone::Consonant(c) => consonants.contains(c),
Phone::Digraph(c1, _) => consonants.contains(c1),
Phone::Trigraph(c1, _, _) => consonants.contains(c1),
_ => false,
}
} else {
false
}
}
Context::BeforeConsonant(consonants) => {
let pos = match_start + pattern_len;
if let Some(phone) = s.get(pos) {
match phone {
Phone::Consonant(c) => consonants.contains(c),
Phone::Digraph(c1, _) => consonants.contains(c1),
Phone::Trigraph(c1, _, _) => consonants.contains(c1),
_ => false,
}
} else {
false
}
}
Context::AfterVowel(vowels) => {
if match_start == 0 {
false
} else if let Some(Phone::Vowel(v)) = s.get(match_start - 1) {
vowels.contains(v)
} else {
false
}
}
Context::Anywhere => true,
Context::And(a, b) => {
context_matches(a, s, match_start, pattern_len)
&& context_matches(b, s, match_start, pattern_len)
}
Context::Or(a, b) => {
context_matches(a, s, match_start, pattern_len)
|| context_matches(b, s, match_start, pattern_len)
}
Context::Not(inner) => !context_matches(inner, s, match_start, pattern_len),
}
}
pub fn pattern_matches_at<U: PhoneticUnit>(
pattern: &[Phone<U>],
s: &[Phone<U>],
pos: usize,
) -> bool {
if pattern.is_empty() {
return true;
}
if pos + pattern.len() > s.len() {
return false;
}
for (i, p) in pattern.iter().enumerate() {
if s.get(pos + i) != Some(p) {
return false;
}
}
true
}
#[inline]
pub fn context_matches_char(
ctx: &ContextChar,
s: &[PhoneChar],
match_start: usize,
pattern_len: usize,
) -> bool {
context_matches(ctx, s, match_start, pattern_len)
}
#[inline]
pub fn pattern_matches_at_char(pattern: &[PhoneChar], s: &[PhoneChar], pos: usize) -> bool {
pattern_matches_at(pattern, s, pos)
}
#[inline]
pub fn phone_eq(p1: &PhoneByte, p2: &PhoneByte) -> bool {
p1 == p2
}
#[inline]
pub fn phone_eq_char(p1: &PhoneChar, p2: &PhoneChar) -> bool {
p1 == p2
}
#[inline]
pub fn is_vowel_char(c: u8) -> bool {
u8::is_vowel(c)
}
#[inline]
pub fn is_vowel(p: &PhoneByte) -> bool {
p.is_vowel()
}
#[inline]
pub fn is_consonant(p: &PhoneByte) -> bool {
p.is_consonant()
}
#[inline]
pub fn is_vowel_char_char(c: char) -> bool {
char::is_vowel(c)
}
#[inline]
pub fn is_vowel_char_type(p: &PhoneChar) -> bool {
p.is_vowel()
}
#[inline]
pub fn is_consonant_char(p: &PhoneChar) -> bool {
p.is_consonant()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_vowel_char() {
assert!(is_vowel_char(b'a'));
assert!(is_vowel_char(b'e'));
assert!(is_vowel_char(b'i'));
assert!(is_vowel_char(b'o'));
assert!(is_vowel_char(b'u'));
assert!(!is_vowel_char(b'k'));
assert!(!is_vowel_char(b'b'));
}
#[test]
fn test_is_vowel() {
assert!(is_vowel(&Phone::Vowel(b'a')));
assert!(!is_vowel(&Phone::Consonant(b'k')));
assert!(!is_vowel(&Phone::Silent));
}
#[test]
fn test_is_consonant() {
assert!(is_consonant(&Phone::Consonant(b'k')));
assert!(is_consonant(&Phone::Digraph(b'c', b'h')));
assert!(!is_consonant(&Phone::Vowel(b'a')));
assert!(!is_consonant(&Phone::Silent));
}
#[test]
fn test_phone_eq() {
assert!(phone_eq(&Phone::Vowel(b'a'), &Phone::Vowel(b'a')));
assert!(!phone_eq(&Phone::Vowel(b'a'), &Phone::Vowel(b'e')));
assert!(phone_eq(&Phone::Silent, &Phone::Silent));
assert!(phone_eq(
&Phone::Digraph(b'c', b'h'),
&Phone::Digraph(b'c', b'h')
));
assert!(!phone_eq(
&Phone::Digraph(b'c', b'h'),
&Phone::Digraph(b's', b'h')
));
}
#[test]
fn test_context_matches_initial() {
let s: Vec<Phone<u8>> = vec![Phone::Consonant(b'k'), Phone::Vowel(b'a')];
assert!(context_matches(&Context::Initial, &s, 0, 1));
assert!(!context_matches(&Context::Initial, &s, 1, 1));
}
#[test]
fn test_context_matches_final() {
let s: Vec<Phone<u8>> = vec![Phone::Consonant(b'k'), Phone::Vowel(b'a')];
assert!(context_matches(&Context::Final, &s, 1, 1));
assert!(!context_matches(&Context::Final, &s, 0, 1));
}
#[test]
fn test_context_matches_anywhere() {
let s: Vec<Phone<u8>> = vec![Phone::Consonant(b'k'), Phone::Vowel(b'a')];
assert!(context_matches(&Context::Anywhere, &s, 0, 1));
assert!(context_matches(&Context::Anywhere, &s, 1, 1));
}
#[test]
fn test_context_matches_before_vowel() {
let s: Vec<Phone<u8>> = vec![Phone::Consonant(b'k'), Phone::Vowel(b'a')];
let ctx: Context<u8> = Context::BeforeVowel(vec![b'a', b'e', b'i']);
assert!(context_matches(&ctx, &s, 0, 1));
assert!(!context_matches(&ctx, &s, 1, 1));
}
#[test]
fn test_pattern_matches_at() {
let pattern: Vec<Phone<u8>> = vec![Phone::Consonant(b'c'), Phone::Consonant(b'h')];
let s: Vec<Phone<u8>> = vec![
Phone::Consonant(b'c'),
Phone::Consonant(b'h'),
Phone::Vowel(b'a'),
];
assert!(pattern_matches_at(&pattern, &s, 0));
assert!(!pattern_matches_at(&pattern, &s, 1));
assert!(!pattern_matches_at(&pattern, &s, 2));
}
#[test]
fn test_pattern_matches_at_empty() {
let pattern: Vec<Phone<u8>> = vec![];
let s: Vec<Phone<u8>> = vec![Phone::Vowel(b'a')];
assert!(pattern_matches_at(&pattern, &s, 0));
}
#[test]
fn test_is_vowel_char_char() {
assert!(is_vowel_char_char('a'));
assert!(is_vowel_char_char('e'));
assert!(!is_vowel_char_char('k'));
}
#[test]
fn test_phone_eq_char() {
assert!(phone_eq_char(
&PhoneChar::Vowel('a'),
&PhoneChar::Vowel('a')
));
assert!(!phone_eq_char(
&PhoneChar::Vowel('a'),
&PhoneChar::Vowel('e')
));
assert!(phone_eq_char(&PhoneChar::Silent, &PhoneChar::Silent));
}
#[test]
fn test_context_matches_char_initial() {
let s: Vec<PhoneChar> = vec![PhoneChar::Consonant('k'), PhoneChar::Vowel('a')];
assert!(context_matches_char(&ContextChar::Initial, &s, 0, 1));
assert!(!context_matches_char(&ContextChar::Initial, &s, 1, 1));
}
#[test]
fn test_pattern_matches_at_char() {
let pattern: Vec<PhoneChar> = vec![PhoneChar::Consonant('c'), PhoneChar::Consonant('h')];
let s: Vec<PhoneChar> = vec![
PhoneChar::Consonant('c'),
PhoneChar::Consonant('h'),
PhoneChar::Vowel('a'),
];
assert!(pattern_matches_at_char(&pattern, &s, 0));
assert!(!pattern_matches_at_char(&pattern, &s, 1));
}
#[test]
fn test_context_matches_and() {
let s: Vec<Phone<u8>> = vec![
Phone::Vowel(b'e'), Phone::Consonant(b'x'), Phone::Vowel(b'a'), Phone::Consonant(b'c'), Phone::Consonant(b't'), ];
let ctx_exact: Context<u8> = Context::And(
Box::new(Context::AfterVowel(vec![b'a', b'e', b'i', b'o', b'u'])),
Box::new(Context::BeforeVowel(vec![b'a', b'e', b'i', b'o', b'u'])),
);
assert!(context_matches(&ctx_exact, &s, 1, 1));
let s2: Vec<Phone<u8>> = vec![
Phone::Vowel(b'a'), Phone::Vowel(b'e'), ];
let ctx2: Context<u8> = Context::And(
Box::new(Context::AfterVowel(vec![b'a'])),
Box::new(Context::BeforeVowel(vec![b'e'])),
);
assert!(!context_matches(&ctx2, &s2, 1, 1));
assert!(!context_matches(&ctx2, &s2, 0, 1));
let s3: Vec<Phone<u8>> = vec![
Phone::Vowel(b'a'), Phone::Consonant(b'x'), Phone::Vowel(b'e'), ];
let ctx3: Context<u8> = Context::And(
Box::new(Context::AfterVowel(vec![b'a'])),
Box::new(Context::BeforeVowel(vec![b'e'])),
);
assert!(context_matches(&ctx3, &s3, 1, 1));
}
#[test]
fn test_context_matches_or() {
let s: Vec<Phone<u8>> = vec![Phone::Consonant(b'k'), Phone::Vowel(b'a')];
let ctx: Context<u8> = Context::Or(Box::new(Context::Initial), Box::new(Context::Final));
assert!(context_matches(&ctx, &s, 0, 1));
assert!(context_matches(&ctx, &s, 1, 1));
assert!(context_matches(&ctx, &s, 0, 2));
}
#[test]
fn test_context_matches_not() {
let s: Vec<Phone<u8>> = vec![Phone::Consonant(b'k'), Phone::Vowel(b'a')];
let ctx: Context<u8> = Context::Not(Box::new(Context::Initial));
assert!(!context_matches(&ctx, &s, 0, 1));
assert!(context_matches(&ctx, &s, 1, 1));
}
#[test]
fn test_context_matches_nested_compound() {
let s: Vec<Phone<u8>> = vec![
Phone::Vowel(b'a'), Phone::Consonant(b'k'), Phone::Vowel(b'e'), ];
let ctx: Context<u8> = Context::And(
Box::new(Context::Not(Box::new(Context::Initial))),
Box::new(Context::Or(
Box::new(Context::AfterVowel(vec![b'a', b'e', b'i', b'o', b'u'])),
Box::new(Context::BeforeVowel(vec![b'a', b'e', b'i', b'o', b'u'])),
)),
);
assert!(!context_matches(&ctx, &s, 0, 1));
assert!(context_matches(&ctx, &s, 1, 1));
assert!(!context_matches(&ctx, &s, 2, 1));
}
#[test]
fn test_context_matches_char_and() {
let s: Vec<PhoneChar> = vec![
PhoneChar::Vowel('a'), PhoneChar::Consonant('x'), PhoneChar::Vowel('e'), ];
let ctx: ContextChar = ContextChar::And(
Box::new(ContextChar::AfterVowel(vec!['a'])),
Box::new(ContextChar::BeforeVowel(vec!['e'])),
);
assert!(context_matches_char(&ctx, &s, 1, 1));
}
#[test]
fn test_context_matches_char_or() {
let s: Vec<PhoneChar> = vec![PhoneChar::Consonant('k'), PhoneChar::Vowel('a')];
let ctx: ContextChar =
ContextChar::Or(Box::new(ContextChar::Initial), Box::new(ContextChar::Final));
assert!(context_matches_char(&ctx, &s, 0, 1));
assert!(context_matches_char(&ctx, &s, 1, 1));
assert!(context_matches_char(&ctx, &s, 0, 2));
}
#[test]
fn test_context_matches_char_not() {
let s: Vec<PhoneChar> = vec![PhoneChar::Consonant('k'), PhoneChar::Vowel('a')];
let ctx: ContextChar = ContextChar::Not(Box::new(ContextChar::Initial));
assert!(!context_matches_char(&ctx, &s, 0, 1));
assert!(context_matches_char(&ctx, &s, 1, 1));
}
}