use std::collections::HashSet;
use crate::zipper::{DictZipper, ValuedDictZipper};
#[derive(Clone, Debug)]
pub struct DifferenceZipper<Z: DictZipper> {
left: Option<Z>,
right: Option<Z>,
path: Vec<Z::Unit>,
}
impl<Z: DictZipper> DifferenceZipper<Z> {
pub fn new(left: Z, right: Z) -> Self {
Self {
left: Some(left),
right: Some(right),
path: Vec::new(),
}
}
pub fn new_with_optional_right(left: Z, right: Option<Z>) -> Self {
Self {
left: Some(left),
right,
path: Vec::new(),
}
}
pub fn left_is_active(&self) -> bool {
self.left.is_some()
}
pub fn right_is_active(&self) -> bool {
self.right.is_some()
}
pub fn iter(&self) -> DifferenceIterator<Z> {
DifferenceIterator::new(self.clone())
}
}
impl<Z: DictZipper> DictZipper for DifferenceZipper<Z> {
type Unit = Z::Unit;
fn is_final(&self) -> bool {
let left_final = self.left.as_ref().is_some_and(|z| z.is_final());
let right_final = self.right.as_ref().is_some_and(|z| z.is_final());
left_final && !right_final
}
fn descend(&self, label: Self::Unit) -> Option<Self> {
let new_left = self.left.as_ref().and_then(|z| z.descend(label));
if new_left.is_none() {
return None;
}
let new_right = self.right.as_ref().and_then(|z| z.descend(label));
let mut new_path = self.path.clone();
new_path.push(label);
Some(Self {
left: new_left,
right: new_right,
path: new_path,
})
}
fn children(&self) -> impl Iterator<Item = (Self::Unit, Self)> {
let left_children: Vec<(Z::Unit, Z)> = self
.left
.as_ref()
.map(|z| z.children().collect())
.unwrap_or_default();
let self_clone = self.clone();
left_children
.into_iter()
.filter_map(move |(label, _)| self_clone.descend(label).map(|child| (label, child)))
}
fn path(&self) -> Vec<Self::Unit> {
self.path.clone()
}
}
impl<Z: ValuedDictZipper> ValuedDictZipper for DifferenceZipper<Z> {
type Value = Z::Value;
fn value(&self) -> Option<Self::Value> {
if self.is_final() {
self.left.as_ref().and_then(|z| z.value())
} else {
None
}
}
}
pub struct DifferenceIterator<Z: DictZipper> {
stack: Vec<DifferenceZipper<Z>>,
seen: HashSet<Vec<Z::Unit>>,
}
impl<Z: DictZipper> DifferenceIterator<Z> {
fn new(zipper: DifferenceZipper<Z>) -> Self {
let mut stack = Vec::with_capacity(16);
stack.push(zipper);
Self {
stack,
seen: HashSet::new(),
}
}
}
impl<Z: DictZipper> Iterator for DifferenceIterator<Z> {
type Item = (Vec<Z::Unit>, DifferenceZipper<Z>);
fn next(&mut self) -> Option<Self::Item> {
while let Some(zipper) = self.stack.pop() {
for (_label, child) in zipper.children() {
self.stack.push(child);
}
if zipper.is_final() {
let path = zipper.path();
if self.seen.insert(path.clone()) {
return Some((path, zipper));
}
}
}
None
}
}
pub struct ValuedDifferenceIterator<Z: ValuedDictZipper> {
inner: DifferenceIterator<Z>,
}
impl<Z: ValuedDictZipper> ValuedDifferenceIterator<Z> {
pub fn new(zipper: DifferenceZipper<Z>) -> Self {
Self {
inner: DifferenceIterator::new(zipper),
}
}
}
impl<Z: ValuedDictZipper> Iterator for ValuedDifferenceIterator<Z> {
type Item = (Vec<Z::Unit>, Z::Value);
fn next(&mut self) -> Option<Self::Item> {
loop {
let (path, zipper) = self.inner.next()?;
if let Some(value) = zipper.value() {
return Some((path, value));
}
}
}
}
pub trait DifferenceZipperExt: DictZipper + Sized {
fn difference_from(self, other: Self) -> DifferenceZipper<Self> {
DifferenceZipper::new(self, other)
}
fn difference_from_optional(self, other: Option<Self>) -> DifferenceZipper<Self> {
DifferenceZipper::new_with_optional_right(self, other)
}
}
impl<Z: DictZipper> DifferenceZipperExt for Z {}
#[cfg(test)]
mod tests {
use super::*;
use crate::double_array_trie::DoubleArrayTrie;
use crate::double_array_trie_zipper::DoubleArrayTrieZipper;
fn sorted_strings(mut v: Vec<String>) -> Vec<String> {
v.sort();
v
}
#[test]
fn test_difference_basic() {
let dict_a = DoubleArrayTrie::from_terms(vec!["cat", "dog", "fish"].iter());
let dict_b = DoubleArrayTrie::from_terms(vec!["dog", "bird"].iter());
let z_a = DoubleArrayTrieZipper::new_from_dict(&dict_a);
let z_b = DoubleArrayTrieZipper::new_from_dict(&dict_b);
let difference = DifferenceZipper::new(z_a, z_b);
let results: Vec<String> = sorted_strings(
difference
.iter()
.map(|(path, _)| String::from_utf8(path).unwrap())
.collect(),
);
assert_eq!(results, vec!["cat", "fish"]);
}
#[test]
fn test_difference_a_minus_empty() {
let dict_a = DoubleArrayTrie::from_terms(vec!["cat", "dog"].iter());
let dict_b: DoubleArrayTrie = DoubleArrayTrie::new();
let z_a = DoubleArrayTrieZipper::new_from_dict(&dict_a);
let z_b = DoubleArrayTrieZipper::new_from_dict(&dict_b);
let difference = z_a.difference_from(z_b);
let results: Vec<String> = sorted_strings(
difference
.iter()
.map(|(path, _)| String::from_utf8(path).unwrap())
.collect(),
);
assert_eq!(results, vec!["cat", "dog"]);
}
#[test]
fn test_difference_empty_minus_b() {
let dict_a: DoubleArrayTrie = DoubleArrayTrie::new();
let dict_b = DoubleArrayTrie::from_terms(vec!["cat", "dog"].iter());
let z_a = DoubleArrayTrieZipper::new_from_dict(&dict_a);
let z_b = DoubleArrayTrieZipper::new_from_dict(&dict_b);
let difference = z_a.difference_from(z_b);
let count = difference.iter().count();
assert_eq!(count, 0);
}
#[test]
fn test_difference_a_minus_a() {
let dict_a = DoubleArrayTrie::from_terms(vec!["cat", "dog"].iter());
let dict_b = DoubleArrayTrie::from_terms(vec!["cat", "dog"].iter());
let z_a = DoubleArrayTrieZipper::new_from_dict(&dict_a);
let z_b = DoubleArrayTrieZipper::new_from_dict(&dict_b);
let difference = z_a.difference_from(z_b);
let count = difference.iter().count();
assert_eq!(count, 0);
}
#[test]
fn test_difference_disjoint() {
let dict_a = DoubleArrayTrie::from_terms(vec!["cat", "dog"].iter());
let dict_b = DoubleArrayTrie::from_terms(vec!["fish", "bird"].iter());
let z_a = DoubleArrayTrieZipper::new_from_dict(&dict_a);
let z_b = DoubleArrayTrieZipper::new_from_dict(&dict_b);
let difference = z_a.difference_from(z_b);
let results: Vec<String> = sorted_strings(
difference
.iter()
.map(|(path, _)| String::from_utf8(path).unwrap())
.collect(),
);
assert_eq!(results, vec!["cat", "dog"]);
}
#[test]
fn test_difference_b_superset_of_a() {
let dict_a = DoubleArrayTrie::from_terms(vec!["cat", "dog"].iter());
let dict_b = DoubleArrayTrie::from_terms(vec!["cat", "dog", "fish", "bird"].iter());
let z_a = DoubleArrayTrieZipper::new_from_dict(&dict_a);
let z_b = DoubleArrayTrieZipper::new_from_dict(&dict_b);
let difference = z_a.difference_from(z_b);
let count = difference.iter().count();
assert_eq!(count, 0);
}
#[test]
fn test_difference_descend() {
let dict_a = DoubleArrayTrie::from_terms(vec!["cat", "car", "cab"].iter());
let dict_b = DoubleArrayTrie::from_terms(vec!["cat", "can"].iter());
let z_a = DoubleArrayTrieZipper::new_from_dict(&dict_a);
let z_b = DoubleArrayTrieZipper::new_from_dict(&dict_b);
let difference = z_a.difference_from(z_b);
let cat = difference
.descend(b'c')
.and_then(|z| z.descend(b'a'))
.and_then(|z| z.descend(b't'));
assert!(cat.is_some());
assert!(!cat.unwrap().is_final());
let car = difference
.descend(b'c')
.and_then(|z| z.descend(b'a'))
.and_then(|z| z.descend(b'r'));
assert!(car.is_some());
assert!(car.unwrap().is_final());
let cab = difference
.descend(b'c')
.and_then(|z| z.descend(b'a'))
.and_then(|z| z.descend(b'b'));
assert!(cab.is_some());
assert!(cab.unwrap().is_final());
}
#[test]
fn test_difference_children() {
let dict_a = DoubleArrayTrie::from_terms(vec!["ab", "ac", "ad"].iter());
let dict_b = DoubleArrayTrie::from_terms(vec!["ac"].iter());
let z_a = DoubleArrayTrieZipper::new_from_dict(&dict_a);
let z_b = DoubleArrayTrieZipper::new_from_dict(&dict_b);
let difference = z_a.difference_from(z_b);
let a = difference.descend(b'a').unwrap();
let mut children: Vec<u8> = a.children().map(|(label, _)| label).collect();
children.sort();
assert_eq!(children, vec![b'b', b'c', b'd']);
}
#[test]
fn test_difference_with_values() {
let dict_a = DoubleArrayTrie::from_terms_with_values(
vec![("cat", 1usize), ("dog", 2), ("fish", 3)].into_iter(),
);
let dict_b = DoubleArrayTrie::from_terms_with_values(vec![("dog", 0usize)].into_iter());
let z_a = DoubleArrayTrieZipper::new_from_dict(&dict_a);
let z_b = DoubleArrayTrieZipper::new_from_dict(&dict_b);
let difference = z_a.difference_from(z_b);
let cat = difference
.descend(b'c')
.and_then(|z| z.descend(b'a'))
.and_then(|z| z.descend(b't'))
.unwrap();
assert!(cat.is_final());
assert_eq!(cat.value(), Some(1));
let dog = difference
.descend(b'd')
.and_then(|z| z.descend(b'o'))
.and_then(|z| z.descend(b'g'))
.unwrap();
assert!(!dog.is_final());
assert_eq!(dog.value(), None); }
#[test]
fn test_difference_valued_iterator() {
let dict_a = DoubleArrayTrie::from_terms_with_values(
vec![("cat", 1usize), ("dog", 2), ("fish", 3)].into_iter(),
);
let dict_b = DoubleArrayTrie::from_terms_with_values(vec![("dog", 0usize)].into_iter());
let z_a = DoubleArrayTrieZipper::new_from_dict(&dict_a);
let z_b = DoubleArrayTrieZipper::new_from_dict(&dict_b);
let difference = z_a.difference_from(z_b);
let valued_iter = ValuedDifferenceIterator::new(difference);
let mut results: Vec<(String, usize)> = valued_iter
.map(|(path, val)| (String::from_utf8(path).unwrap(), val))
.collect();
results.sort_by(|a, b| a.0.cmp(&b.0));
assert_eq!(
results,
vec![("cat".to_string(), 1), ("fish".to_string(), 3),]
);
}
#[test]
fn test_difference_nested_prefix() {
let dict_a = DoubleArrayTrie::from_terms(vec!["app", "apple"].iter());
let dict_b = DoubleArrayTrie::from_terms(vec!["apple"].iter());
let z_a = DoubleArrayTrieZipper::new_from_dict(&dict_a);
let z_b = DoubleArrayTrieZipper::new_from_dict(&dict_b);
let difference = z_a.difference_from(z_b);
let results: Vec<String> = sorted_strings(
difference
.iter()
.map(|(path, _)| String::from_utf8(path).unwrap())
.collect(),
);
assert_eq!(results, vec!["app"]);
}
#[test]
fn test_difference_optional_right() {
let dict_a = DoubleArrayTrie::from_terms(vec!["cat", "dog"].iter());
let z_a = DoubleArrayTrieZipper::new_from_dict(&dict_a);
let difference = z_a.difference_from_optional(None);
let results: Vec<String> = sorted_strings(
difference
.iter()
.map(|(path, _)| String::from_utf8(path).unwrap())
.collect(),
);
assert_eq!(results, vec!["cat", "dog"]);
}
#[test]
fn test_left_right_active() {
let dict_a = DoubleArrayTrie::from_terms(vec!["cat", "car"].iter());
let dict_b = DoubleArrayTrie::from_terms(vec!["cat"].iter());
let z_a = DoubleArrayTrieZipper::new_from_dict(&dict_a);
let z_b = DoubleArrayTrieZipper::new_from_dict(&dict_b);
let difference = z_a.difference_from(z_b);
assert!(difference.left_is_active());
assert!(difference.right_is_active());
let car = difference
.descend(b'c')
.and_then(|z| z.descend(b'a'))
.and_then(|z| z.descend(b'r'))
.unwrap();
assert!(car.left_is_active());
assert!(!car.right_is_active()); }
}