use crate::suffix_automaton::{SuffixAutomaton, SuffixAutomatonInner};
use crate::value::DictionaryValue;
use crate::zipper::{DictZipper, ValuedDictZipper};
use std::sync::Arc;
use crate::sync_compat::RwLock;
#[derive(Clone)]
pub struct SuffixAutomatonZipper<V: DictionaryValue = ()> {
inner: Arc<RwLock<SuffixAutomatonInner<V>>>,
state_id: usize,
path: Vec<u8>,
}
impl<V: DictionaryValue> SuffixAutomatonZipper<V> {
pub fn new_from_dict(dict: &SuffixAutomaton<V>) -> Self {
SuffixAutomatonZipper {
inner: dict.inner.clone(),
state_id: 0, path: Vec::new(),
}
}
pub fn state_id(&self) -> usize {
self.state_id
}
}
impl<V: DictionaryValue> DictZipper for SuffixAutomatonZipper<V> {
type Unit = u8;
fn is_final(&self) -> bool {
let inner = self.inner.read();
if self.state_id < inner.nodes.len() {
inner.nodes[self.state_id].is_final
} else {
false
}
}
fn descend(&self, label: Self::Unit) -> Option<Self> {
let inner = self.inner.read();
if self.state_id >= inner.nodes.len() {
return None;
}
for (edge_label, target_state) in &inner.nodes[self.state_id].edges {
if *edge_label == label {
let mut new_path = self.path.clone();
new_path.push(label);
return Some(SuffixAutomatonZipper {
inner: self.inner.clone(),
state_id: *target_state,
path: new_path,
});
}
}
None
}
fn path(&self) -> Vec<Self::Unit> {
self.path.clone()
}
fn children(&self) -> impl Iterator<Item = (Self::Unit, Self)> {
let edges: Vec<(u8, usize)> = {
let inner = self.inner.read();
if self.state_id < inner.nodes.len() {
inner.nodes[self.state_id].edges.clone()
} else {
Vec::new()
}
};
let inner = self.inner.clone();
let base_path = self.path.clone();
edges.into_iter().map(move |(label, target)| {
let mut new_path = base_path.clone();
new_path.push(label);
(
label,
SuffixAutomatonZipper {
inner: inner.clone(),
state_id: target,
path: new_path,
},
)
})
}
}
impl<V: DictionaryValue> ValuedDictZipper for SuffixAutomatonZipper<V> {
type Value = V;
fn value(&self) -> Option<Self::Value> {
let inner = self.inner.read();
if self.state_id < inner.nodes.len() && inner.nodes[self.state_id].is_final {
inner.nodes[self.state_id].value.clone()
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_root_zipper_not_final() {
let dict = SuffixAutomaton::<()>::from_text("test");
let zipper = SuffixAutomatonZipper::new_from_dict(&dict);
assert!(!zipper.is_final());
assert_eq!(zipper.state_id(), 0);
}
#[test]
fn test_descend_nonexistent() {
let dict = SuffixAutomaton::<()>::from_text("test");
let zipper = SuffixAutomatonZipper::new_from_dict(&dict);
assert!(zipper.descend(b'x').is_none());
}
#[test]
fn test_descend_and_finality() {
let dict = SuffixAutomaton::<()>::from_text("test");
let zipper = SuffixAutomatonZipper::new_from_dict(&dict);
let t = zipper.descend(b't').expect("Should descend to 't'");
assert!(t.is_final(), "'t' should be final (it's a suffix)");
let e = t.descend(b'e').expect("Should descend to 'e'");
assert!(e.is_final(), "'te' should be final (it's a suffix)");
let s = e.descend(b's').expect("Should descend to 's'");
assert!(s.is_final(), "'tes' should be final (it's a suffix)");
let t2 = s.descend(b't').expect("Should descend to 't'");
assert!(t2.is_final(), "'test' should be final (it's a suffix)");
}
#[test]
fn test_substring_matching() {
let dict = SuffixAutomaton::<()>::from_text("testing");
let zipper = SuffixAutomatonZipper::new_from_dict(&dict);
let e = zipper.descend(b'e').expect("Should descend to 'e'");
let s = e.descend(b's').expect("Should descend to 's'");
let t = s.descend(b't').expect("Should descend to 't'");
assert!(t.is_final(), "'est' should be final");
}
#[test]
fn test_children_iteration() {
let dict = SuffixAutomaton::<()>::from_text("test");
let zipper = SuffixAutomatonZipper::new_from_dict(&dict);
let children: Vec<u8> = zipper.children().map(|(label, _)| label).collect();
assert!(children.contains(&b't'));
assert!(children.contains(&b'e'));
assert!(children.contains(&b's'));
}
#[test]
fn test_valued_zipper() {
use crate::MutableMappedDictionary;
let dict: SuffixAutomaton<u32> = SuffixAutomaton::new();
dict.insert_with_value("test", 1);
dict.insert_with_value("testing", 2);
let zipper = SuffixAutomatonZipper::new_from_dict(&dict);
let test_zipper = zipper
.descend(b't')
.and_then(|z| z.descend(b'e'))
.and_then(|z| z.descend(b's'))
.and_then(|z| z.descend(b't'))
.expect("Should navigate to 'test'");
assert!(test_zipper.is_final());
}
#[test]
fn test_clone_independence() {
let dict = SuffixAutomaton::<()>::from_text("test");
let zipper1 = SuffixAutomatonZipper::new_from_dict(&dict);
let zipper2 = zipper1.clone();
let z1_t = zipper1.descend(b't');
let z2_t = zipper2.descend(b't');
assert!(z1_t.is_some());
assert!(z2_t.is_some());
assert_eq!(z1_t.unwrap().state_id(), z2_t.unwrap().state_id());
}
#[test]
fn test_empty_dictionary() {
let dict: SuffixAutomaton<()> = SuffixAutomaton::new();
let zipper = SuffixAutomatonZipper::new_from_dict(&dict);
assert!(!zipper.is_final());
assert_eq!(zipper.children().count(), 0);
}
#[test]
fn test_value_none_for_non_final() {
use crate::MutableMappedDictionary;
let dict: SuffixAutomaton<u32> = SuffixAutomaton::new();
dict.insert_with_value("test", 42);
let zipper = SuffixAutomatonZipper::new_from_dict(&dict);
assert_eq!(zipper.value(), None, "Root should have no value");
}
#[test]
fn test_path_tracking() {
let dict = SuffixAutomaton::<()>::from_text("test");
let zipper = SuffixAutomatonZipper::new_from_dict(&dict);
let t = zipper.descend(b't').unwrap();
assert_eq!(t.path(), vec![b't']);
let e = t.descend(b'e').unwrap();
assert_eq!(e.path(), vec![b't', b'e']);
let s = e.descend(b's').unwrap();
assert_eq!(s.path(), vec![b't', b'e', b's']);
let t2 = s.descend(b't').unwrap();
assert_eq!(t2.path(), vec![b't', b'e', b's', b't']);
}
#[test]
fn test_concurrent_access() {
use std::sync::Arc as StdArc;
use std::thread;
let dict = StdArc::new(SuffixAutomaton::<()>::from_text("testing"));
let handles: Vec<_> = (0..4)
.map(|_| {
let dict_clone = dict.clone();
thread::spawn(move || {
let zipper = SuffixAutomatonZipper::new_from_dict(&dict_clone);
zipper.descend(b't').is_some()
})
})
.collect();
for handle in handles {
assert!(handle.join().unwrap());
}
}
#[test]
fn test_multiple_strings() {
let dict = SuffixAutomaton::<()>::from_texts(vec!["cat", "car", "dog"]);
let zipper = SuffixAutomatonZipper::new_from_dict(&dict);
let children: Vec<u8> = zipper.children().map(|(label, _)| label).collect();
assert!(children.contains(&b'c'));
assert!(children.contains(&b'd'));
}
}