use crate::{
map::Trie,
try_collect::{TryCollect, TryFromIterator},
};
use louds_rs::LoudsNodeNum;
#[derive(Debug, Clone)]
pub struct IncSearch<'a, Label, Value> {
trie: &'a Trie<Label, Value>,
node: LoudsNodeNum,
}
pub type Position = LoudsNodeNum;
impl<'a, L, V> From<IncSearch<'a, L, V>> for Position {
fn from(inc_search: IncSearch<'a, L, V>) -> Self {
inc_search.node
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Answer {
Prefix,
Match,
PrefixAndMatch,
}
impl Answer {
pub fn is_prefix(&self) -> bool {
matches!(self, Answer::Prefix | Answer::PrefixAndMatch)
}
pub fn is_match(&self) -> bool {
matches!(self, Answer::Match | Answer::PrefixAndMatch)
}
fn new(is_prefix: bool, is_match: bool) -> Option<Self> {
match (is_prefix, is_match) {
(true, false) => Some(Answer::Prefix),
(false, true) => Some(Answer::Match),
(true, true) => Some(Answer::PrefixAndMatch),
(false, false) => None,
}
}
}
impl<'a, Label: Ord, Value> IncSearch<'a, Label, Value> {
pub fn new(trie: &'a Trie<Label, Value>) -> Self {
Self {
trie,
node: LoudsNodeNum(1),
}
}
pub fn resume(trie: &'a Trie<Label, Value>, position: Position) -> Self {
Self {
trie,
node: position,
}
}
pub fn peek(&self, chr: &Label) -> Option<Answer> {
let children_node_nums: Vec<_> = self.trie.children_node_nums(self.node).collect();
let res = self
.trie
.bin_search_by_children_labels(chr, &children_node_nums[..]);
match res {
Ok(j) => {
let node = children_node_nums[j];
let is_prefix = self.trie.has_children_node_nums(node);
let is_match = self.trie.value(node).is_some();
Answer::new(is_prefix, is_match)
}
Err(_) => None,
}
}
pub fn query(&mut self, chr: &Label) -> Option<Answer> {
let children_node_nums: Vec<_> = self.trie.children_node_nums(self.node).collect();
let res = self
.trie
.bin_search_by_children_labels(chr, &children_node_nums[..]);
match res {
Ok(j) => {
self.node = children_node_nums[j];
let is_prefix = self.trie.has_children_node_nums(self.node);
let is_match = self.trie.value(self.node).is_some();
Answer::new(is_prefix, is_match)
}
Err(_) => None,
}
}
pub fn query_until(&mut self, query: impl AsRef<[Label]>) -> Result<Answer, usize> {
let mut result = None;
let mut i = 0;
for chr in query.as_ref().iter() {
result = self.query(chr);
if result.is_none() {
return Err(i);
}
i += 1;
}
result.ok_or(i)
}
pub fn value(&self) -> Option<&'a Value> {
self.trie.value(self.node)
}
pub fn goto_longest_prefix(&mut self) -> Result<usize, usize> {
let mut count = 0;
while count == 0 || !self.trie.is_terminal(self.node) {
let mut iter = self.trie.children_node_nums(self.node);
let first = iter.next();
let second = iter.next();
match (first, second) {
(Some(child_node_num), None) => {
self.node = child_node_num;
count += 1;
}
(None, _) => {
assert_eq!(count, 0);
return Ok(count);
}
_ => {
return Err(count);
}
}
}
Ok(count)
}
pub fn prefix<C, M>(&self) -> C
where
C: TryFromIterator<Label, M>,
Label: Clone,
{
let mut v: Vec<Label> = self
.trie
.child_to_ancestors(self.node)
.map(|node| self.trie.label(node).clone())
.collect();
v.reverse();
v.into_iter().try_collect().expect("Could not collect")
}
pub fn prefix_len(&self) -> usize {
self.trie.child_to_ancestors(self.node).count()
}
pub fn reset(&mut self) {
self.node = LoudsNodeNum(1);
}
}
#[cfg(test)]
mod search_tests {
use super::*;
use crate::map::{Trie, TrieBuilder};
fn build_trie() -> Trie<u8, u8> {
let mut builder = TrieBuilder::new();
builder.push("a", 0);
builder.push("app", 1);
builder.push("apple", 2);
builder.push("better", 3);
builder.push("application", 4);
builder.push("アップル🍎", 5);
builder.build()
}
#[test]
fn inc_search() {
let trie = build_trie();
let mut search = trie.inc_search();
assert_eq!("", search.prefix::<String, _>());
assert_eq!(0, search.prefix_len());
assert_eq!(None, search.query(&b'z'));
assert_eq!("", search.prefix::<String, _>());
assert_eq!(0, search.prefix_len());
assert_eq!(Answer::PrefixAndMatch, search.query(&b'a').unwrap());
assert_eq!("a", search.prefix::<String, _>());
assert_eq!(1, search.prefix_len());
assert_eq!(Answer::Prefix, search.query(&b'p').unwrap());
assert_eq!("ap", search.prefix::<String, _>());
assert_eq!(2, search.prefix_len());
assert_eq!(Answer::PrefixAndMatch, search.query(&b'p').unwrap());
assert_eq!("app", search.prefix::<String, _>());
assert_eq!(3, search.prefix_len());
assert_eq!(Answer::Prefix, search.query(&b'l').unwrap());
assert_eq!("appl", search.prefix::<String, _>());
assert_eq!(4, search.prefix_len());
assert_eq!(Answer::Match, search.query(&b'e').unwrap());
assert_eq!("apple", search.prefix::<String, _>());
assert_eq!(5, search.prefix_len());
}
#[test]
fn inc_search_value() {
let trie = build_trie();
let mut search = trie.inc_search();
assert_eq!("", search.prefix::<String, _>());
assert_eq!(None, search.query(&b'z'));
assert_eq!("", search.prefix::<String, _>());
assert_eq!(Answer::PrefixAndMatch, search.query(&b'a').unwrap());
assert_eq!("a", search.prefix::<String, _>());
assert_eq!(Answer::Prefix, search.query(&b'p').unwrap());
assert_eq!("ap", search.prefix::<String, _>());
assert_eq!(Answer::PrefixAndMatch, search.query(&b'p').unwrap());
assert_eq!("app", search.prefix::<String, _>());
assert_eq!(Answer::Prefix, search.query(&b'l').unwrap());
assert_eq!("appl", search.prefix::<String, _>());
assert_eq!(Answer::Match, search.query(&b'e').unwrap());
assert_eq!("apple", search.prefix::<String, _>());
assert_eq!(Some(&2), search.value());
}
#[test]
fn inc_search_query_until() {
let trie = build_trie();
let mut search = trie.inc_search();
assert_eq!(Err(0), search.query_until("zoo"));
assert_eq!("", search.prefix::<String, _>());
search.reset();
assert_eq!(Err(1), search.query_until("blue"));
assert_eq!("b", search.prefix::<String, _>());
search.reset();
assert_eq!(Answer::Match, search.query_until("apple").unwrap());
assert_eq!("apple", search.prefix::<String, _>());
assert_eq!(Some(&2), search.value());
}
#[test]
fn inc_search_goto_longest_prefix() {
let trie = build_trie();
let mut search = trie.inc_search();
assert_eq!(Err(0), search.goto_longest_prefix());
assert_eq!("", search.prefix::<String, _>());
search.reset();
assert_eq!(Ok(Answer::PrefixAndMatch), search.query_until("a"));
assert_eq!("a", search.prefix::<String, _>());
assert_eq!(Ok(2), search.goto_longest_prefix());
assert_eq!("app", search.prefix::<String, _>());
assert_eq!(Err(1), search.goto_longest_prefix());
assert_eq!("appl", search.prefix::<String, _>());
assert_eq!(Err(0), search.goto_longest_prefix());
assert_eq!(Ok(Answer::Prefix), search.query_until("i"));
assert_eq!(Ok(6), search.goto_longest_prefix());
assert_eq!(Ok(0), search.goto_longest_prefix());
assert_eq!("application", search.prefix::<String, _>());
search.reset();
assert_eq!(Answer::Match, search.query_until("apple").unwrap());
assert_eq!("apple", search.prefix::<String, _>());
assert_eq!(Some(&2), search.value());
}
}