use crate::double_array_trie_char::{DATSharedChar, DoubleArrayTrieChar};
use crate::value::DictionaryValue;
use crate::zipper::{DictZipper, ValuedDictZipper};
#[derive(Clone, Debug)]
pub struct DoubleArrayTrieCharZipper<V: DictionaryValue = ()> {
state: usize,
path: Vec<char>,
shared: DATSharedChar<V>,
}
impl<V: DictionaryValue> DoubleArrayTrieCharZipper<V> {
pub fn new_from_dict(dict: &DoubleArrayTrieChar<V>) -> Self {
Self {
state: 0, path: Vec::new(),
shared: dict.shared.clone(),
}
}
pub fn state(&self) -> usize {
self.state
}
}
impl<V: DictionaryValue> DictZipper for DoubleArrayTrieCharZipper<V> {
type Unit = char;
fn is_final(&self) -> bool {
self.state < self.shared.is_final.len() && self.shared.is_final[self.state]
}
fn descend(&self, label: Self::Unit) -> Option<Self> {
if self.state >= self.shared.base.len() {
return None;
}
let base = self.shared.base[self.state];
if base < 0 {
return None;
}
let char_code = label as u32;
let next = (base as u32).wrapping_add(char_code) as usize;
if next >= self.shared.check.len() || self.shared.check[next] != self.state as i32 {
return None;
}
let mut new_path = self.path.clone();
new_path.push(label);
Some(Self {
state: next,
path: new_path,
shared: self.shared.clone(),
})
}
fn children(&self) -> impl Iterator<Item = (Self::Unit, Self)> {
let edges: &[char] = if self.state < self.shared.edges.len() {
&self.shared.edges[self.state]
} else {
&[]
};
edges
.iter()
.filter_map(move |&c| self.descend(c).map(|child| (c, child)))
}
fn path(&self) -> Vec<Self::Unit> {
self.path.clone()
}
}
impl<V: DictionaryValue> ValuedDictZipper for DoubleArrayTrieCharZipper<V> {
type Value = V;
fn value(&self) -> Option<Self::Value> {
if self.is_final() && self.state < self.shared.values.len() {
self.shared.values[self.state].clone()
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::double_array_trie_char::DoubleArrayTrieChar;
#[test]
fn test_zipper_root_not_final() {
let dict: DoubleArrayTrieChar<()> = DoubleArrayTrieChar::from_terms(vec!["test"]);
let zipper = DoubleArrayTrieCharZipper::new_from_dict(&dict);
assert!(!zipper.is_final());
assert_eq!(zipper.state(), 0); }
#[test]
fn test_zipper_unicode_navigation() {
let dict = DoubleArrayTrieChar::from_terms(vec!["café", "中文"]);
let zipper = DoubleArrayTrieCharZipper::new_from_dict(&dict);
let z = zipper
.descend('c')
.and_then(|z| z.descend('a'))
.and_then(|z| z.descend('f'))
.and_then(|z| z.descend('é'))
.unwrap();
assert!(z.is_final());
let z = zipper.descend('中').and_then(|z| z.descend('文')).unwrap();
assert!(z.is_final());
}
#[test]
fn test_zipper_nonexistent_path() {
let dict = DoubleArrayTrieChar::from_terms(vec!["test"]);
let zipper = DoubleArrayTrieCharZipper::new_from_dict(&dict);
let result = zipper.descend('x');
assert!(result.is_none());
}
#[test]
fn test_zipper_with_unicode_values() {
let dict = DoubleArrayTrieChar::from_terms_with_values(vec![("café", 1), ("中文", 2)]);
let zipper = DoubleArrayTrieCharZipper::new_from_dict(&dict);
let z = zipper
.descend('c')
.and_then(|z| z.descend('a'))
.and_then(|z| z.descend('f'))
.and_then(|z| z.descend('é'))
.unwrap();
assert!(z.is_final());
assert_eq!(z.value(), Some(1));
let z = zipper.descend('中').and_then(|z| z.descend('文')).unwrap();
assert!(z.is_final());
assert_eq!(z.value(), Some(2));
}
#[test]
fn test_zipper_children_iteration() {
let dict = DoubleArrayTrieChar::from_terms(vec!["ab", "ac", "ad"]);
let zipper = DoubleArrayTrieCharZipper::new_from_dict(&dict);
let a_zipper = zipper.descend('a').unwrap();
let children: Vec<char> = a_zipper.children().map(|(label, _)| label).collect();
assert!(children.contains(&'b'));
assert!(children.contains(&'c'));
assert!(children.contains(&'d'));
assert_eq!(children.len(), 3);
}
#[test]
fn test_zipper_value_at_non_final() {
let dict = DoubleArrayTrieChar::from_terms_with_values(vec![("test", 42)]);
let zipper = DoubleArrayTrieCharZipper::new_from_dict(&dict);
let z = zipper.descend('t').and_then(|z| z.descend('e')).unwrap();
assert!(!z.is_final());
assert_eq!(z.value(), None); }
#[test]
fn test_zipper_empty_dict() {
let dict = DoubleArrayTrieChar::<i32>::from_terms_with_values(Vec::<(&str, i32)>::new());
let zipper = DoubleArrayTrieCharZipper::new_from_dict(&dict);
assert!(!zipper.is_final());
assert_eq!(zipper.children().count(), 0);
}
#[test]
fn test_zipper_string_values() {
let dict = DoubleArrayTrieChar::from_terms_with_values(vec![
("hello", "greeting".to_string()),
("世界", "world".to_string()),
]);
let zipper = DoubleArrayTrieCharZipper::new_from_dict(&dict);
let z = zipper
.descend('h')
.and_then(|z| z.descend('e'))
.and_then(|z| z.descend('l'))
.and_then(|z| z.descend('l'))
.and_then(|z| z.descend('o'))
.unwrap();
assert!(z.is_final());
assert_eq!(z.value(), Some("greeting".to_string()));
}
#[test]
fn test_zipper_emoji() {
let dict = DoubleArrayTrieChar::from_terms_with_values(vec![
("🎉", "party".to_string()),
("🌍", "earth".to_string()),
]);
let zipper = DoubleArrayTrieCharZipper::new_from_dict(&dict);
let z = zipper.descend('🎉').unwrap();
assert!(z.is_final());
assert_eq!(z.value(), Some("party".to_string()));
}
#[test]
fn test_zipper_path_tracking() {
let dict = DoubleArrayTrieChar::from_terms(vec!["café"]);
let zipper = DoubleArrayTrieCharZipper::new_from_dict(&dict);
let z = zipper
.descend('c')
.and_then(|z| z.descend('a'))
.and_then(|z| z.descend('f'))
.and_then(|z| z.descend('é'))
.unwrap();
let path = z.path();
assert_eq!(path, vec!['c', 'a', 'f', 'é']);
}
}