use super::keychecker::KeyChecker; use super::trienode::TrieKey; use super::trienode::TrieNode; use std::collections::BTreeSet; use std::iter::Iterator; use std::marker::PhantomData; pub enum VisitOutcome { Continue, Skip, EarlyExit, } pub struct Trie where KEY: TrieKey, KEYCHECKER: KeyChecker, { pub(crate) nodes: Vec>, tails: BTreeSet, checker: PhantomData, } impl Default for Trie where KEY: TrieKey, KEYCHECKER: KeyChecker, { fn default() -> Self { Trie { nodes: vec![TrieNode::new0(None)], tails: BTreeSet::new(), checker: PhantomData, } } } impl Trie where KEY: TrieKey, KEYCHECKER: KeyChecker, { pub fn trie_from_words(wordlist: U) -> Trie where T: IntoIterator, U: IntoIterator, { let mut result = Trie::default(); for word in wordlist { result.add(word); } result } pub(crate) fn get_node_mut(&mut self, index: usize) -> &mut TrieNode { &mut self.nodes[index] } pub(crate) fn get_node(&self, index: usize) -> &TrieNode { &self.nodes[index] } pub(crate) fn nodes(&self) -> usize { self.nodes.len() } fn add_node(&mut self, key: Option, parent: usize, prev: Option) -> usize { let mut result = TrieNode::new0(key); let result_index = self.nodes(); result.parent = Some(parent); match prev { Some(prev_node) => { self.get_node_mut(prev_node).next = Some(result_index); result.prev = prev; } None => { let parent_node = self.get_node_mut(parent); match parent_node.child { None => { parent_node.child = Some(result_index); } Some(parent_child) => { let mut node = parent_child; loop { let next = self.get_node(node).next; match next { Some(next_node) => { node = next_node; } None => { break; } } } self.get_node_mut(node).next = Some(result_index); result.prev = Some(node) } } } } self.nodes.push(result); result_index } pub fn add(&mut self, path: T) -> (bool, usize) where T: IntoIterator, { let mut result = false; let mut pnode = 0; 'wordLoop: for key in path { let mut cnode = self.get_node(pnode).child; while let Some(cnode_index) = cnode { let cnode_node = self.get_node(cnode_index); if KEYCHECKER::check(cnode_node.key, Some(key)) { pnode = cnode_index; continue 'wordLoop; } else if self.get_node(cnode_index).next.is_none() { break; } else { cnode = self.get_node(cnode_index).next; } } pnode = self.add_node(Some(key), pnode, cnode); result = true; } if result { let tail = self.add_node(None, pnode, None); self.tails.insert(tail); let mut node = Some(tail); while let Some(n) = node { let current_node = self.get_node_mut(n); current_node.ref_count += 1; node = current_node.parent; } (true, tail) } else { (false, pnode) } } pub fn search(&mut self, path: Vec) -> Option { let mut result: Option = None; let visit_pre = |stack: &Vec| -> VisitOutcome { if stack.len() == 1 { VisitOutcome::Continue } else { let last = *stack.last().expect(""); let index = stack.len() - 2; let node = self.get_node(last); if index < path.len() { if KEYCHECKER::check(node.key, Some(path[index])) { VisitOutcome::Continue } else { VisitOutcome::Skip } } else { if node.key.is_none() { result = Some(last); } VisitOutcome::EarlyExit } } }; let visit_post = |_: &Vec| {}; self.walk(visit_pre, visit_post); result } pub fn lineal_descendant(&self, start: usize) -> impl Iterator { let mut nodes: Vec = vec![]; let mut node_option = Some(start); while let Some(node) = node_option { let key = &self.get_node(node).key; if key.is_some() { nodes.push(node); } node_option = self.get_node(node).parent; } nodes .into_iter() .rev() .map(|node_index| self.get_node(node_index).key.as_ref().unwrap()) } pub(crate) fn walk(&self, mut visit_pre: CB1, mut visit_post: CB2) where CB1: FnMut(&Vec) -> VisitOutcome, CB2: FnMut(&Vec), { let mut stack: Vec<(usize, Option)> = vec![]; let mut public_stack: Vec = vec![]; let root_node = self.get_node(0); stack.push((0, root_node.child)); public_stack.push(0); visit_pre(&public_stack); while !stack.is_empty() { let last = &mut stack.last_mut().unwrap(); match last.1 { Some(child_node_id) => { let child_node = self.get_node(child_node_id); last.1 = child_node.next; public_stack.push(child_node_id); let visit_pre_outcome = visit_pre(&public_stack); match visit_pre_outcome { VisitOutcome::Continue => { stack.push((child_node_id, child_node.child)); } VisitOutcome::Skip => { stack.push((child_node_id, None)); } VisitOutcome::EarlyExit => return, } } None => { visit_post(&public_stack); stack.pop(); public_stack.pop(); } } } } pub fn tails(&self) -> &BTreeSet { &self.tails } }