diff --git a/src/btreeset.rs b/src/btreeset.rs index 63133c2e..25f19476 100644 --- a/src/btreeset.rs +++ b/src/btreeset.rs @@ -545,6 +545,7 @@ where // Use a closure to find common elements by traversing both iterators simultaneously. std::iter::from_fn(move || { + // Loop until we find a common element or exhaust either iterator. while let (Some(ref a), Some(ref b)) = (next_self.clone(), next_other.clone()) { match a.cmp(b) { std::cmp::Ordering::Less => { @@ -567,6 +568,196 @@ where None }) } + + /// Returns `true` if this set has no elements in common with another set. + /// + /// # Example + /// + /// ```rust + /// use ic_stable_structures::{BTreeSet, DefaultMemoryImpl}; + /// + /// let mut set1: BTreeSet = BTreeSet::new(DefaultMemoryImpl::default()); + /// let mut set2: BTreeSet = BTreeSet::new(DefaultMemoryImpl::default()); + /// + /// set1.insert(1); + /// set1.insert(2); + /// set2.insert(3); + /// set2.insert(4); + /// + /// assert!(set1.is_disjoint(&set2)); + /// set2.insert(2); + /// assert!(!set1.is_disjoint(&set2)); + /// ``` + pub fn is_disjoint(&self, other: &BTreeSet) -> bool { + let mut iter_self = self.iter(); + let mut iter_other = other.iter(); + let mut next_self = iter_self.next(); + let mut next_other = iter_other.next(); + + while let (Some(a), Some(b)) = (next_self.as_ref(), next_other.as_ref()) { + match a.cmp(b) { + std::cmp::Ordering::Less => next_self = iter_self.next(), + std::cmp::Ordering::Greater => next_other = iter_other.next(), + std::cmp::Ordering::Equal => return false, // Common element found + } + } + + true // No common elements + } + + /// Returns `true` if this set is a subset of another set. + /// + /// A set `A` is a subset of a set `B` if all elements of `A` are also elements of `B`. + /// + /// # Example + /// + /// ```rust + /// use ic_stable_structures::{BTreeSet, DefaultMemoryImpl}; + /// + /// let mut set1: BTreeSet = BTreeSet::new(DefaultMemoryImpl::default()); + /// let mut set2: BTreeSet = BTreeSet::new(DefaultMemoryImpl::default()); + /// + /// set1.insert(1); + /// set1.insert(2); + /// set2.insert(1); + /// set2.insert(2); + /// set2.insert(3); + /// + /// assert!(set1.is_subset(&set2)); + /// assert!(!set2.is_subset(&set1)); + /// ``` + pub fn is_subset(&self, other: &BTreeSet) -> bool { + let mut self_iter = self.iter(); + let mut other_iter = other.iter(); + + let mut self_next = self_iter.next(); + let mut other_next = other_iter.next(); + + while let Some(ref self_key) = self_next { + match other_next { + Some(ref other_key) => match self_key.cmp(other_key) { + std::cmp::Ordering::Equal => { + // Keys match, advance both iterators. + self_next = self_iter.next(); + other_next = other_iter.next(); + } + std::cmp::Ordering::Greater => { + // Advance the `other` iterator if its key is smaller. + other_next = other_iter.next(); + } + std::cmp::Ordering::Less => { + // `self_key` is smaller than the current smallest item of + // other which means that it cannot be found in `other`, + // so return false. + return false; + } + }, + None => { + // If `other` is exhausted but `self` is not, return false. + return false; + } + } + } + + // If we exhaust `self`, it is a subset of `other`. + true + } + + /// Returns `true` if this set is a superset of another set. + /// + /// A set `A` is a superset of a set `B` if all elements of `B` are also elements of `A`. + /// + /// # Example + /// + /// ```rust + /// use ic_stable_structures::{BTreeSet, DefaultMemoryImpl}; + /// + /// let mut set1: BTreeSet = BTreeSet::new(DefaultMemoryImpl::default()); + /// let mut set2: BTreeSet = BTreeSet::new(DefaultMemoryImpl::default()); + /// + /// set1.insert(1); + /// set1.insert(2); + /// set1.insert(3); + /// set2.insert(1); + /// set2.insert(2); + /// + /// assert!(set1.is_superset(&set2)); + /// assert!(!set2.is_superset(&set1)); + /// ``` + pub fn is_superset(&self, other: &BTreeSet) -> bool { + other.is_subset(self) + } + + /// Returns an iterator over the symmetric difference of this set and another. + /// + /// The symmetric difference of two sets is the set of elements that are in either of the sets, + /// but not in their intersection. + /// + /// # Example + /// + /// ```rust + /// use ic_stable_structures::{BTreeSet, DefaultMemoryImpl}; + /// + /// let mut set1: BTreeSet = BTreeSet::new(DefaultMemoryImpl::default()); + /// let mut set2: BTreeSet = BTreeSet::new(DefaultMemoryImpl::default()); + /// + /// set1.insert(1); + /// set1.insert(2); + /// set2.insert(2); + /// set2.insert(3); + /// + /// let symmetric_diff: Vec<_> = set1.symmetric_difference(&set2).collect(); + /// assert_eq!(symmetric_diff, vec![1, 3]); + /// ``` + pub fn symmetric_difference<'a>( + &'a self, + other: &'a BTreeSet, + ) -> impl Iterator + 'a { + let mut iter_self = self.iter(); + let mut iter_other = other.iter(); + let mut next_self = iter_self.next(); + let mut next_other = iter_other.next(); + + // Use a closure to find common elements by traversing both iterators simultaneously. + std::iter::from_fn(move || { + // Loop until we detect a difference or exhaust either iterator. + loop { + return match (next_self.clone(), next_other.clone()) { + (Some(ref a), Some(ref b)) => { + match a.cmp(b) { + std::cmp::Ordering::Less => { + // If the element from `self` is smaller, yield it and advance `self`. + next_self = iter_self.next(); + Some(a.clone()) + } + std::cmp::Ordering::Greater => { + // If the element from `other` is smaller, yield it and advance `other`. + next_other = iter_other.next(); + Some(b.clone()) + } + std::cmp::Ordering::Equal => { + // Skip elements that are in both sets and advance both iterators. + next_self = iter_self.next(); + next_other = iter_other.next(); + continue; + } + } + } + (Some(ref a), None) => { + // If only `self` has elements remaining, yield them. + next_self = iter_self.next(); + Some(a.clone()) + } + (None, Some(ref b)) => { + // If only `other` has elements remaining, yield them. + next_other = iter_other.next(); + Some(b.clone()) + } + (None, None) => None, + }; + } + }) + } } #[cfg(test)] @@ -698,6 +889,45 @@ mod test { assert_eq!(union[1499], 1499); } + #[test] + fn test_union_odd_even() { + let mem1 = Rc::new(RefCell::new(Vec::new())); + let mem2 = Rc::new(RefCell::new(Vec::new())); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + for i in 0..1000 { + if i % 2 != 0 { + set1.insert(i); + } else { + set2.insert(i); + } + } + + let intersection: Vec<_> = set1.union(&set2).collect(); + assert_eq!(intersection.len(), 1000); + assert_eq!(intersection, (0..1000).collect::>()); + } + + #[test] + fn test_intersection_even() { + let mem1 = Rc::new(RefCell::new(Vec::new())); + let mem2 = Rc::new(RefCell::new(Vec::new())); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + for i in 0..1000 { + set1.insert(i); + if i % 2 == 0 { + set2.insert(i); + } + } + + let intersection: Vec<_> = set1.intersection(&set2).collect(); + assert_eq!(intersection.len(), 500); + assert_eq!(intersection, set2.iter().collect::>()); + } + #[test] fn test_intersection_with_large_sets() { let mem1 = Rc::new(RefCell::new(Vec::new())); @@ -1070,4 +1300,342 @@ mod test { assert_eq!(btreeset.iter_upper_bound(&5).next(), Some(4)); // Largest element below 5 assert_eq!(btreeset.iter_upper_bound(&11).next(), Some(10)); // Largest element below 11 } + + #[test] + fn test_is_disjoint_with_disjoint_sets() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + set1.insert(1); + set1.insert(2); + + set2.insert(3); + set2.insert(4); + + assert!(set1.is_disjoint(&set2)); + } + + #[test] + fn test_is_disjoint_with_overlapping_sets() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + set1.insert(1); + set1.insert(2); + + set2.insert(2); + set2.insert(3); + + assert!(!set1.is_disjoint(&set2)); + } + + #[test] + fn test_is_disjoint_with_large_sets() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + for i in 0..1000 { + set1.insert(i); + } + for i in 1000..2000 { + set2.insert(i); + } + + assert!(set1.is_disjoint(&set2)); + + set2.insert(500); + assert!(!set1.is_disjoint(&set2)); + } + + #[test] + fn test_is_subset_with_subset() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + set1.insert(1); + set1.insert(2); + + set2.insert(1); + set2.insert(2); + set2.insert(3); + + assert!(set1.is_subset(&set2)); + } + + #[test] + fn test_is_subset_with_non_subset() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + set1.insert(1); + set1.insert(4); + + set2.insert(1); + set2.insert(2); + set2.insert(3); + + assert!(!set1.is_subset(&set2)); + } + + #[test] + fn test_is_subset_with_large_sets() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + for i in 0..500 { + set1.insert(i); + } + for i in 0..1500 { + set2.insert(i); + } + + assert!(set1.is_subset(&set2)); + + set1.insert(1500); + assert!(!set1.is_subset(&set2)); + } + + #[test] + fn test_is_superset_with_superset() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + set1.insert(1); + set1.insert(2); + set1.insert(3); + + set2.insert(1); + set2.insert(2); + + assert!(set1.is_superset(&set2)); + } + + #[test] + fn test_is_superset_with_non_superset() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + set1.insert(1); + set1.insert(2); + + set2.insert(1); + set2.insert(3); + + assert!(!set1.is_superset(&set2)); + } + + #[test] + fn test_is_superset_with_large_sets() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + for i in 0..1000 { + set1.insert(i); + } + for i in 500..1000 { + set2.insert(i); + } + + assert!(set1.is_superset(&set2)); + + set2.insert(1500); + assert!(!set1.is_superset(&set2)); + } + + #[test] + fn test_symmetric_difference_with_disjoint_sets() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + set1.insert(1); + set1.insert(2); + + set2.insert(3); + set2.insert(4); + + let symmetric_diff: Vec<_> = set1.symmetric_difference(&set2).collect(); + assert_eq!(symmetric_diff, vec![1, 2, 3, 4]); + } + + #[test] + fn test_symmetric_difference_with_overlapping_sets() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + set1.insert(1); + set1.insert(2); + set1.insert(3); + + set2.insert(2); + set2.insert(3); + set2.insert(4); + + let symmetric_diff: Vec<_> = set1.symmetric_difference(&set2).collect(); + assert_eq!(symmetric_diff, vec![1, 4]); + } + + #[test] + fn test_symmetric_difference_with_identical_sets() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + set1.insert(1); + set1.insert(2); + set1.insert(3); + + set2.insert(1); + set2.insert(2); + set2.insert(3); + + let symmetric_diff: Vec<_> = set1.symmetric_difference(&set2).collect(); + assert!(symmetric_diff.is_empty()); + } + + #[test] + fn test_symmetric_difference_with_large_sets() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + for i in 0..1000 { + set1.insert(i); + } + for i in 500..1500 { + set2.insert(i); + } + + let symmetric_diff: Vec<_> = set1.symmetric_difference(&set2).collect(); + assert_eq!(symmetric_diff.len(), 1000); + assert_eq!(symmetric_diff[..500], (0..500).collect::>()); + assert_eq!(symmetric_diff[500..], (1000..1500).collect::>()); + } + + #[test] + fn test_symmetric_difference_odd_even() { + let mem1 = Rc::new(RefCell::new(Vec::new())); + let mem2 = Rc::new(RefCell::new(Vec::new())); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + for i in 0..1000 { + if i % 2 != 0 { + set1.insert(i); + } else { + set2.insert(i); + } + } + + let intersection: Vec<_> = set1.symmetric_difference(&set2).collect(); + assert_eq!(intersection.len(), 1000); + assert_eq!(intersection, (0..1000).collect::>()); + } + + #[test] + fn test_symmetric_difference_even() { + let mem1 = Rc::new(RefCell::new(Vec::new())); + let mem2 = Rc::new(RefCell::new(Vec::new())); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + let mut expected_res = vec![]; + + for i in 0..1000 { + set1.insert(i); + + if i % 2 == 0 { + set2.insert(i); + } else { + expected_res.push(i); + } + } + + let intersection: Vec<_> = set1.symmetric_difference(&set2).collect(); + assert_eq!(intersection.len(), 500); + assert_eq!(intersection, expected_res); + } + + #[test] + fn test_is_subset_with_sparse_elements() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + for i in (0..1000).step_by(10) { + set1.insert(i); + } + for i in (0..2000).step_by(5) { + set2.insert(i); + } + + assert!(set1.is_subset(&set2)); + + set1.insert(2001); + assert!(!set1.is_subset(&set2)); + } + + #[test] + fn test_is_disjoint_with_sparse_elements() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + for i in (0..1000).step_by(10) { + set1.insert(i); + } + for i in (1..1000).step_by(10) { + set2.insert(i); + } + + assert!(set1.is_disjoint(&set2)); + + set2.insert(20); + assert!(!set1.is_disjoint(&set2)); + } + + #[test] + fn test_is_superset_with_sparse_elements() { + let mem1 = make_memory(); + let mem2 = make_memory(); + let mut set1: BTreeSet = BTreeSet::new(mem1); + let mut set2: BTreeSet = BTreeSet::new(mem2); + + for i in (0..2000).step_by(5) { + set1.insert(i); + } + for i in (0..1000).step_by(10) { + set2.insert(i); + } + + assert!(set1.is_superset(&set2)); + + set2.insert(2001); + assert!(!set1.is_superset(&set2)); + } } diff --git a/src/btreeset/proptests.rs b/src/btreeset/proptests.rs index 8e8dd91f..0a2b4978 100644 --- a/src/btreeset/proptests.rs +++ b/src/btreeset/proptests.rs @@ -175,9 +175,9 @@ fn execute_operation( } #[proptest] -fn test_union( - #[strategy(pvec(any::(), 1..100))] keys1: Vec, - #[strategy(pvec(any::(), 1..100))] keys2: Vec, +fn test_set_operations( + #[strategy(pvec(any::(), 1..1000))] keys1: Vec, + #[strategy(pvec(any::(), 1..1000))] keys2: Vec, ) { crate::btreeset::test::run_btree_test(|mut set1| { let mut set2 = BTreeSet::new(crate::btreeset::test::make_memory()); @@ -194,40 +194,31 @@ fn test_union( std_set2.insert(*key); } - let union: Vec<_> = set1.union(&set2).collect(); - let std_union: Vec<_> = std_set1.union(&std_set2).cloned().collect(); + let is_subset = set1.is_subset(&set2); + let std_is_subset = std_set1.is_subset(&std_set2); + prop_assert_eq!(is_subset, std_is_subset); - prop_assert_eq!(union, std_union); + let is_superset = set1.is_superset(&set2); + let std_is_superset = std_set1.is_superset(&std_set2); + prop_assert_eq!(is_superset, std_is_superset); - Ok(()) - }); -} - -#[proptest] -fn test_intersection( - #[strategy(pvec(any::(), 1..100))] keys1: Vec, - #[strategy(pvec(any::(), 1..100))] keys2: Vec, -) { - crate::btreeset::test::run_btree_test(|mut set1| { - let mut set2 = BTreeSet::new(crate::btreeset::test::make_memory()); - let mut std_set1 = StdBTreeSet::new(); - let mut std_set2 = StdBTreeSet::new(); - - for key in &keys1 { - set1.insert(*key); - std_set1.insert(*key); - } - - for key in &keys2 { - set2.insert(*key); - std_set2.insert(*key); - } + let is_disjoint = set1.is_disjoint(&set2); + let std_is_disjoint = std_set1.is_disjoint(&std_set2); + prop_assert_eq!(is_disjoint, std_is_disjoint); let intersection: Vec<_> = set1.intersection(&set2).collect(); let std_intersection: Vec<_> = std_set1.intersection(&std_set2).cloned().collect(); - prop_assert_eq!(intersection, std_intersection); + let union: Vec<_> = set1.union(&set2).collect(); + let std_union: Vec<_> = std_set1.union(&std_set2).cloned().collect(); + prop_assert_eq!(union, std_union); + + let symmetric_diff: Vec<_> = set1.symmetric_difference(&set2).collect(); + let std_symmetric_diff: Vec<_> = + std_set1.symmetric_difference(&std_set2).cloned().collect(); + prop_assert_eq!(symmetric_diff, std_symmetric_diff); + Ok(()) }); }