superset_map

B-tree-backed map whose keys are distinct supersets.
git clone https://git.philomathiclife.com/repos/superset_map
Log | Files | Refs | README

commit c4a185bad9b56ef0366f09354784f25ad09aeb5a
parent 99526edfaeed8e16d7c8e977db692de5b5fadd8f
Author: Zack Newman <zack@philomathiclife.com>
Date:   Mon, 13 Jul 2026 13:31:49 -0600

update deps and lints. move unit tests

Diffstat:
MCargo.toml | 10+++++++---
Msrc/superset_map.rs | 315+------------------------------------------------------------------------------
Asrc/superset_map/tests.rs | 308+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/superset_set.rs | 177++-----------------------------------------------------------------------------
Asrc/superset_set/tests.rs | 181+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 502 insertions(+), 489 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" name = "superset_map" readme = "README.md" repository = "https://git.philomathiclife.com/repos/superset_map/" -version = "0.3.6" +version = "0.3.7" [lints.rust] deprecated-safe = { level = "deny", priority = -1 } @@ -27,12 +27,14 @@ unused = { level = "deny", priority = -1 } warnings = { level = "deny", priority = -1 } ambiguous-negative-literals = { level = "deny", priority = -1 } closure-returning-async-block = { level = "deny", priority = -1 } +dead-code-pub-in-binary = { level = "deny", priority = -1 } deprecated-in-future = { level = "deny", priority = -1 } +#deprecated-llvm-intrinsic = { level = "deny", priority = -1 } deref-into-dyn-supertrait = { level = "deny", priority = -1 } ffi-unwind-calls = { level = "deny", priority = -1 } #fuzzy-provenance-casts = { level = "deny", priority = -1 } impl-trait-redundant-captures = { level = "deny", priority = -1 } -linker-messages = { level = "deny", priority = -1 } +linker-info = { level = "deny", priority = -1 } #lossy-provenance-casts = { level = "deny", priority = -1 } macro-use-extern-crate = { level = "deny", priority = -1 } meta-variable-misuse = { level = "deny", priority = -1 } @@ -77,10 +79,12 @@ suspicious = { level = "deny", priority = -1 } arbitrary_source_item_ordering = "allow" blanket_clippy_restriction_lints = "allow" implicit_return = "allow" +inline_trait_bounds = "allow" min_ident_chars = "allow" missing_trait_methods = "allow" pub_use = "allow" return_and_then = "allow" +self_named_module_files = "allow" semicolon_outside_block = "allow" single_char_lifetime_names = "allow" unseparated_literal_suffix = "allow" @@ -104,4 +108,4 @@ targets = [ ] [dependencies] -zfc = { version = "0.4.7", default-features = false } +zfc = { version = "0.5.0", default-features = false } diff --git a/src/superset_map.rs b/src/superset_map.rs @@ -1,4 +1,7 @@ extern crate alloc; +/// Unit tests. +#[cfg(test)] +mod tests; use crate::SetOrd; use alloc::collections::btree_map::{ BTreeMap, CursorMut, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, OccupiedEntry, Range, @@ -713,315 +716,3 @@ impl<'a, K, V> IntoIterator for &'a mut SupersetMap<K, V> { self.iter_mut() } } -#[cfg(test)] -mod tests { - use super::{ - super::zfc::{BoundedCardinality, Cardinality, Set, num_bigint::BigUint}, - SetOrd, SupersetMap, - }; - use core::{borrow::Borrow, cmp::Ordering}; - #[derive(Eq, PartialEq)] - struct ClosedInterval { - min: usize, - max: usize, - } - impl PartialOrd<Self> for ClosedInterval { - fn partial_cmp(&self, other: &Self) -> Option<Ordering> { - Some(self.cmp(other)) - } - } - impl Ord for ClosedInterval { - fn cmp(&self, other: &Self) -> Ordering { - match self.min.cmp(&other.min) { - Ordering::Less => { - if self.max >= other.max { - Ordering::Greater - } else { - Ordering::Less - } - } - Ordering::Equal => self.max.cmp(&other.max), - Ordering::Greater => { - if self.max > other.max { - Ordering::Greater - } else { - Ordering::Less - } - } - } - } - } - impl Set for ClosedInterval { - type Elem = usize; - #[expect(clippy::panic, reason = "want to crash when there is a bug")] - #[expect( - clippy::arithmetic_side_effects, - reason = "comment justifies correctness" - )] - fn bounded_cardinality(&self) -> BoundedCardinality { - BoundedCardinality::from_biguint_exact( - // We can add `BigUint`s. - BigUint::from(self.max.checked_sub(self.min).unwrap_or_else(|| { - panic!( - "superset_map::test::ClosedInterval must be created such that max is >= min" - ) - })) + BigUint::from(1usize), - ) - } - #[expect(clippy::panic, reason = "want to crash when there is a bug")] - #[expect( - clippy::arithmetic_side_effects, - reason = "comment justifies correctness" - )] - fn cardinality(&self) -> Option<Cardinality> { - Some(Cardinality::Finite( - // We can add `BigUint`s. - BigUint::from(self.max.checked_sub(self.min).unwrap_or_else(|| { - panic!( - "superset_map::test::ClosedInterval must be created such that max is >= min" - ) - })) + BigUint::from(1usize), - )) - } - fn contains<Q>(&self, elem: &Q) -> bool - where - Q: ?Sized + Borrow<Self::Elem> + Eq, - { - self.min <= *elem.borrow() && self.max >= *elem.borrow() - } - fn is_proper_subset(&self, val: &Self) -> bool { - match self.min.cmp(&val.min) { - Ordering::Less => false, - Ordering::Equal => self.max < val.max, - Ordering::Greater => self.max <= val.max, - } - } - fn is_subset(&self, val: &Self) -> bool { - self.min >= val.min && self.max <= val.max - } - } - impl SetOrd for ClosedInterval {} - #[test] - fn insert() { - let mut map = SupersetMap::new(); - _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); - _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); - _ = map.insert(ClosedInterval { min: 0, max: 4 }, ()); - _ = map.insert(ClosedInterval { min: 1, max: 4 }, ()); - _ = map.insert(ClosedInterval { min: 4, max: 6 }, ()); - _ = map.insert(ClosedInterval { min: 2, max: 7 }, ()); - _ = map.insert(ClosedInterval { min: 10, max: 17 }, ()); - let mut keys = map.into_keys(); - assert!( - keys.next() - .is_some_and(|set| set == ClosedInterval { min: 0, max: 4 }) - ); - assert!( - keys.next() - .is_some_and(|set| set == ClosedInterval { min: 2, max: 7 }) - ); - assert!( - keys.next() - .is_some_and(|set| set == ClosedInterval { min: 10, max: 17 }) - ); - assert!(keys.next().is_none()); - assert!(keys.next().is_none()); - assert!(keys.next().is_none()); - } - #[test] - fn append() { - let mut map = SupersetMap::new(); - _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); - _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); - _ = map.insert(ClosedInterval { min: 0, max: 4 }, ()); - _ = map.insert(ClosedInterval { min: 1, max: 4 }, ()); - _ = map.insert(ClosedInterval { min: 4, max: 6 }, ()); - _ = map.insert(ClosedInterval { min: 2, max: 7 }, ()); - _ = map.insert(ClosedInterval { min: 10, max: 17 }, ()); - let mut map2 = SupersetMap::new(); - _ = map2.insert(ClosedInterval { min: 0, max: 1 }, ()); - _ = map2.insert(ClosedInterval { min: 1, max: 14 }, ()); - _ = map2.insert(ClosedInterval { min: 11, max: 18 }, ()); - map.append(map2); - let mut keys = map.into_keys(); - assert!( - keys.next() - .is_some_and(|set| set == ClosedInterval { min: 0, max: 4 }) - ); - assert!( - keys.next() - .is_some_and(|set| set == ClosedInterval { min: 1, max: 14 }) - ); - assert!( - keys.next() - .is_some_and(|set| set == ClosedInterval { min: 10, max: 17 }) - ); - assert!( - keys.next() - .is_some_and(|set| set == ClosedInterval { min: 11, max: 18 }) - ); - assert!(keys.next().is_none()); - assert!(keys.next().is_none()); - assert!(keys.next().is_none()); - } - #[test] - fn contains() { - let mut map = SupersetMap::new(); - _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); - _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); - _ = map.insert(ClosedInterval { min: 0, max: 4 }, ()); - _ = map.insert(ClosedInterval { min: 1, max: 4 }, ()); - _ = map.insert(ClosedInterval { min: 4, max: 6 }, ()); - _ = map.insert(ClosedInterval { min: 2, max: 7 }, ()); - _ = map.insert(ClosedInterval { min: 10, max: 17 }, ()); - assert!(map.contains_proper_subset(&ClosedInterval { min: 0, max: 10 })); - assert!(!map.contains_proper_subset(&ClosedInterval { min: 10, max: 17 })); - assert!(!map.contains_proper_subset(&ClosedInterval { min: 210, max: 217 })); - assert!(map.contains_proper_superset(&ClosedInterval { min: 0, max: 1 })); - assert!(!map.contains_proper_superset(&ClosedInterval { min: 10, max: 17 })); - assert!(!map.contains_proper_superset(&ClosedInterval { min: 210, max: 217 })); - assert!(map.contains_subset(&ClosedInterval { min: 0, max: 10 })); - assert!(map.contains_subset(&ClosedInterval { min: 10, max: 17 })); - assert!(!map.contains_subset(&ClosedInterval { min: 210, max: 217 })); - assert!(map.contains_superset(&ClosedInterval { min: 0, max: 1 })); - assert!(map.contains_superset(&ClosedInterval { min: 10, max: 17 })); - assert!(!map.contains_superset(&ClosedInterval { min: 210, max: 217 })); - } - #[test] - fn get() { - let mut map = SupersetMap::new(); - _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); - _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); - _ = map.insert(ClosedInterval { min: 0, max: 4 }, ()); - _ = map.insert(ClosedInterval { min: 1, max: 4 }, ()); - _ = map.insert(ClosedInterval { min: 4, max: 6 }, ()); - _ = map.insert(ClosedInterval { min: 2, max: 7 }, ()); - _ = map.insert(ClosedInterval { min: 10, max: 17 }, ()); - assert!( - map.get_greatest_proper_subset_key_value(&ClosedInterval { min: 0, max: 10 }) - .is_some_and(|info| *info.0 == ClosedInterval { min: 2, max: 7 }) - ); - assert!( - map.get_greatest_proper_subset_key_value(&ClosedInterval { min: 10, max: 17 }) - .is_none() - ); - assert!( - map.get_greatest_proper_subset_key_value(&ClosedInterval { min: 210, max: 217 }) - .is_none() - ); - assert!( - map.get_least_proper_superset_key_value(&ClosedInterval { min: 3, max: 4 }) - .is_some_and(|info| *info.0 == ClosedInterval { min: 0, max: 4 }) - ); - assert!( - map.get_least_proper_superset_key_value(&ClosedInterval { min: 10, max: 17 }) - .is_none() - ); - assert!( - map.get_least_proper_superset_key_value(&ClosedInterval { min: 210, max: 217 }) - .is_none() - ); - assert!( - map.get_greatest_subset_key_value(&ClosedInterval { min: 0, max: 10 }) - .is_some_and(|info| *info.0 == ClosedInterval { min: 2, max: 7 }) - ); - assert!( - map.get_greatest_subset_key_value(&ClosedInterval { min: 10, max: 17 }) - .is_some_and(|info| *info.0 == ClosedInterval { min: 10, max: 17 }) - ); - assert!( - map.get_greatest_subset_key_value(&ClosedInterval { min: 210, max: 217 }) - .is_none() - ); - assert!( - map.get_least_superset_key_value(&ClosedInterval { min: 3, max: 4 }) - .is_some_and(|info| *info.0 == ClosedInterval { min: 0, max: 4 }) - ); - assert!( - map.get_least_superset_key_value(&ClosedInterval { min: 10, max: 17 }) - .is_some_and(|info| *info.0 == ClosedInterval { min: 10, max: 17 }) - ); - assert!( - map.get_least_superset_key_value(&ClosedInterval { min: 210, max: 217 }) - .is_none() - ); - } - #[test] - fn remove() { - let mut map = SupersetMap::new(); - _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); - _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); - _ = map.insert(ClosedInterval { min: 0, max: 4 }, ()); - _ = map.insert(ClosedInterval { min: 1, max: 4 }, ()); - _ = map.insert(ClosedInterval { min: 4, max: 6 }, ()); - _ = map.insert(ClosedInterval { min: 2, max: 7 }, ()); - _ = map.insert(ClosedInterval { min: 10, max: 17 }, ()); - assert!( - map.remove_greatest_proper_subset(&ClosedInterval { min: 0, max: 10 }) - .is_some_and(|info| info.0 == ClosedInterval { min: 2, max: 7 }) - ); - assert!( - map.remove_greatest_proper_subset(&ClosedInterval { min: 10, max: 17 }) - .is_none() - ); - assert!( - map.remove_greatest_proper_subset(&ClosedInterval { min: 210, max: 217 }) - .is_none() - ); - assert!( - map.remove_least_proper_superset(&ClosedInterval { min: 3, max: 4 }) - .is_some_and(|info| info.0 == ClosedInterval { min: 0, max: 4 }) - ); - assert!( - map.remove_least_proper_superset(&ClosedInterval { min: 10, max: 17 }) - .is_none() - ); - assert!( - map.remove_least_proper_superset(&ClosedInterval { min: 210, max: 217 }) - .is_none() - ); - assert!( - map.remove_greatest_subset(&ClosedInterval { min: 0, max: 10 }) - .is_none() - ); - assert!( - map.remove_greatest_subset(&ClosedInterval { min: 10, max: 17 }) - .is_some_and(|info| info.0 == ClosedInterval { min: 10, max: 17 }) - ); - assert!( - map.remove_greatest_subset(&ClosedInterval { min: 210, max: 217 }) - .is_none() - ); - assert!( - map.remove_least_superset(&ClosedInterval { min: 3, max: 4 }) - .is_none() - ); - assert!( - map.remove_least_superset(&ClosedInterval { min: 10, max: 17 }) - .is_none() - ); - assert!( - map.remove_least_superset(&ClosedInterval { min: 210, max: 217 }) - .is_none() - ); - let mut map2 = SupersetMap::new(); - _ = map2.insert(ClosedInterval { min: 0, max: 3 }, ()); - _ = map2.insert(ClosedInterval { min: 0, max: 3 }, ()); - _ = map2.insert(ClosedInterval { min: 0, max: 4 }, ()); - _ = map2.insert(ClosedInterval { min: 1, max: 4 }, ()); - _ = map2.insert(ClosedInterval { min: 4, max: 6 }, ()); - _ = map2.insert(ClosedInterval { min: 2, max: 7 }, ()); - _ = map2.insert(ClosedInterval { min: 10, max: 17 }, ()); - assert!(map2.remove_supersets(&ClosedInterval { min: 0, max: 20 }) == 0); - assert!(map2.remove_subsets(&ClosedInterval { min: 0, max: 1 }) == 0); - assert!(map2.remove_subsets(&ClosedInterval { min: 0, max: 20 }) == 3); - _ = map2.insert(ClosedInterval { min: 0, max: 3 }, ()); - _ = map2.insert(ClosedInterval { min: 0, max: 3 }, ()); - _ = map2.insert(ClosedInterval { min: 0, max: 4 }, ()); - _ = map2.insert(ClosedInterval { min: 1, max: 4 }, ()); - _ = map2.insert(ClosedInterval { min: 4, max: 6 }, ()); - _ = map2.insert(ClosedInterval { min: 2, max: 7 }, ()); - _ = map2.insert(ClosedInterval { min: 10, max: 17 }, ()); - assert!(map2.remove_supersets(&ClosedInterval { min: 3, max: 4 }) == 2); - } -} diff --git a/src/superset_map/tests.rs b/src/superset_map/tests.rs @@ -0,0 +1,308 @@ +use super::{ + super::zfc::{BoundedCardinality, Cardinality, Set, num_bigint::BigUint}, + SetOrd, SupersetMap, +}; +use core::{borrow::Borrow, cmp::Ordering}; +#[derive(Eq, PartialEq)] +struct ClosedInterval { + min: usize, + max: usize, +} +impl PartialOrd<Self> for ClosedInterval { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} +impl Ord for ClosedInterval { + fn cmp(&self, other: &Self) -> Ordering { + match self.min.cmp(&other.min) { + Ordering::Less => { + if self.max >= other.max { + Ordering::Greater + } else { + Ordering::Less + } + } + Ordering::Equal => self.max.cmp(&other.max), + Ordering::Greater => { + if self.max > other.max { + Ordering::Greater + } else { + Ordering::Less + } + } + } + } +} +impl Set for ClosedInterval { + type Elem = usize; + #[expect(clippy::panic, reason = "want to crash when there is a bug")] + #[expect( + clippy::arithmetic_side_effects, + reason = "comment justifies correctness" + )] + fn bounded_cardinality(&self) -> BoundedCardinality { + BoundedCardinality::from_biguint_exact( + // We can add `BigUint`s. + BigUint::from(self.max.checked_sub(self.min).unwrap_or_else(|| { + panic!("superset_map::test::ClosedInterval must be created such that max is >= min") + })) + BigUint::from(1usize), + ) + } + #[expect(clippy::panic, reason = "want to crash when there is a bug")] + #[expect( + clippy::arithmetic_side_effects, + reason = "comment justifies correctness" + )] + fn cardinality(&self) -> Option<Cardinality> { + Some(Cardinality::Finite( + // We can add `BigUint`s. + BigUint::from(self.max.checked_sub(self.min).unwrap_or_else(|| { + panic!("superset_map::test::ClosedInterval must be created such that max is >= min") + })) + BigUint::from(1usize), + )) + } + fn contains<Q>(&self, elem: &Q) -> bool + where + Q: ?Sized + Borrow<Self::Elem> + Eq, + { + self.min <= *elem.borrow() && self.max >= *elem.borrow() + } + fn is_proper_subset(&self, val: &Self) -> bool { + match self.min.cmp(&val.min) { + Ordering::Less => false, + Ordering::Equal => self.max < val.max, + Ordering::Greater => self.max <= val.max, + } + } + fn is_subset(&self, val: &Self) -> bool { + self.min >= val.min && self.max <= val.max + } +} +impl SetOrd for ClosedInterval {} +#[test] +fn insert() { + let mut map = SupersetMap::new(); + _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); + _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); + _ = map.insert(ClosedInterval { min: 0, max: 4 }, ()); + _ = map.insert(ClosedInterval { min: 1, max: 4 }, ()); + _ = map.insert(ClosedInterval { min: 4, max: 6 }, ()); + _ = map.insert(ClosedInterval { min: 2, max: 7 }, ()); + _ = map.insert(ClosedInterval { min: 10, max: 17 }, ()); + let mut keys = map.into_keys(); + assert!( + keys.next() + .is_some_and(|set| set == ClosedInterval { min: 0, max: 4 }) + ); + assert!( + keys.next() + .is_some_and(|set| set == ClosedInterval { min: 2, max: 7 }) + ); + assert!( + keys.next() + .is_some_and(|set| set == ClosedInterval { min: 10, max: 17 }) + ); + assert!(keys.next().is_none()); + assert!(keys.next().is_none()); + assert!(keys.next().is_none()); +} +#[test] +fn append() { + let mut map = SupersetMap::new(); + _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); + _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); + _ = map.insert(ClosedInterval { min: 0, max: 4 }, ()); + _ = map.insert(ClosedInterval { min: 1, max: 4 }, ()); + _ = map.insert(ClosedInterval { min: 4, max: 6 }, ()); + _ = map.insert(ClosedInterval { min: 2, max: 7 }, ()); + _ = map.insert(ClosedInterval { min: 10, max: 17 }, ()); + let mut map2 = SupersetMap::new(); + _ = map2.insert(ClosedInterval { min: 0, max: 1 }, ()); + _ = map2.insert(ClosedInterval { min: 1, max: 14 }, ()); + _ = map2.insert(ClosedInterval { min: 11, max: 18 }, ()); + map.append(map2); + let mut keys = map.into_keys(); + assert!( + keys.next() + .is_some_and(|set| set == ClosedInterval { min: 0, max: 4 }) + ); + assert!( + keys.next() + .is_some_and(|set| set == ClosedInterval { min: 1, max: 14 }) + ); + assert!( + keys.next() + .is_some_and(|set| set == ClosedInterval { min: 10, max: 17 }) + ); + assert!( + keys.next() + .is_some_and(|set| set == ClosedInterval { min: 11, max: 18 }) + ); + assert!(keys.next().is_none()); + assert!(keys.next().is_none()); + assert!(keys.next().is_none()); +} +#[test] +fn contains() { + let mut map = SupersetMap::new(); + _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); + _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); + _ = map.insert(ClosedInterval { min: 0, max: 4 }, ()); + _ = map.insert(ClosedInterval { min: 1, max: 4 }, ()); + _ = map.insert(ClosedInterval { min: 4, max: 6 }, ()); + _ = map.insert(ClosedInterval { min: 2, max: 7 }, ()); + _ = map.insert(ClosedInterval { min: 10, max: 17 }, ()); + assert!(map.contains_proper_subset(&ClosedInterval { min: 0, max: 10 })); + assert!(!map.contains_proper_subset(&ClosedInterval { min: 10, max: 17 })); + assert!(!map.contains_proper_subset(&ClosedInterval { min: 210, max: 217 })); + assert!(map.contains_proper_superset(&ClosedInterval { min: 0, max: 1 })); + assert!(!map.contains_proper_superset(&ClosedInterval { min: 10, max: 17 })); + assert!(!map.contains_proper_superset(&ClosedInterval { min: 210, max: 217 })); + assert!(map.contains_subset(&ClosedInterval { min: 0, max: 10 })); + assert!(map.contains_subset(&ClosedInterval { min: 10, max: 17 })); + assert!(!map.contains_subset(&ClosedInterval { min: 210, max: 217 })); + assert!(map.contains_superset(&ClosedInterval { min: 0, max: 1 })); + assert!(map.contains_superset(&ClosedInterval { min: 10, max: 17 })); + assert!(!map.contains_superset(&ClosedInterval { min: 210, max: 217 })); +} +#[test] +fn get() { + let mut map = SupersetMap::new(); + _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); + _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); + _ = map.insert(ClosedInterval { min: 0, max: 4 }, ()); + _ = map.insert(ClosedInterval { min: 1, max: 4 }, ()); + _ = map.insert(ClosedInterval { min: 4, max: 6 }, ()); + _ = map.insert(ClosedInterval { min: 2, max: 7 }, ()); + _ = map.insert(ClosedInterval { min: 10, max: 17 }, ()); + assert!( + map.get_greatest_proper_subset_key_value(&ClosedInterval { min: 0, max: 10 }) + .is_some_and(|info| *info.0 == ClosedInterval { min: 2, max: 7 }) + ); + assert!( + map.get_greatest_proper_subset_key_value(&ClosedInterval { min: 10, max: 17 }) + .is_none() + ); + assert!( + map.get_greatest_proper_subset_key_value(&ClosedInterval { min: 210, max: 217 }) + .is_none() + ); + assert!( + map.get_least_proper_superset_key_value(&ClosedInterval { min: 3, max: 4 }) + .is_some_and(|info| *info.0 == ClosedInterval { min: 0, max: 4 }) + ); + assert!( + map.get_least_proper_superset_key_value(&ClosedInterval { min: 10, max: 17 }) + .is_none() + ); + assert!( + map.get_least_proper_superset_key_value(&ClosedInterval { min: 210, max: 217 }) + .is_none() + ); + assert!( + map.get_greatest_subset_key_value(&ClosedInterval { min: 0, max: 10 }) + .is_some_and(|info| *info.0 == ClosedInterval { min: 2, max: 7 }) + ); + assert!( + map.get_greatest_subset_key_value(&ClosedInterval { min: 10, max: 17 }) + .is_some_and(|info| *info.0 == ClosedInterval { min: 10, max: 17 }) + ); + assert!( + map.get_greatest_subset_key_value(&ClosedInterval { min: 210, max: 217 }) + .is_none() + ); + assert!( + map.get_least_superset_key_value(&ClosedInterval { min: 3, max: 4 }) + .is_some_and(|info| *info.0 == ClosedInterval { min: 0, max: 4 }) + ); + assert!( + map.get_least_superset_key_value(&ClosedInterval { min: 10, max: 17 }) + .is_some_and(|info| *info.0 == ClosedInterval { min: 10, max: 17 }) + ); + assert!( + map.get_least_superset_key_value(&ClosedInterval { min: 210, max: 217 }) + .is_none() + ); +} +#[test] +fn remove() { + let mut map = SupersetMap::new(); + _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); + _ = map.insert(ClosedInterval { min: 0, max: 3 }, ()); + _ = map.insert(ClosedInterval { min: 0, max: 4 }, ()); + _ = map.insert(ClosedInterval { min: 1, max: 4 }, ()); + _ = map.insert(ClosedInterval { min: 4, max: 6 }, ()); + _ = map.insert(ClosedInterval { min: 2, max: 7 }, ()); + _ = map.insert(ClosedInterval { min: 10, max: 17 }, ()); + assert!( + map.remove_greatest_proper_subset(&ClosedInterval { min: 0, max: 10 }) + .is_some_and(|info| info.0 == ClosedInterval { min: 2, max: 7 }) + ); + assert!( + map.remove_greatest_proper_subset(&ClosedInterval { min: 10, max: 17 }) + .is_none() + ); + assert!( + map.remove_greatest_proper_subset(&ClosedInterval { min: 210, max: 217 }) + .is_none() + ); + assert!( + map.remove_least_proper_superset(&ClosedInterval { min: 3, max: 4 }) + .is_some_and(|info| info.0 == ClosedInterval { min: 0, max: 4 }) + ); + assert!( + map.remove_least_proper_superset(&ClosedInterval { min: 10, max: 17 }) + .is_none() + ); + assert!( + map.remove_least_proper_superset(&ClosedInterval { min: 210, max: 217 }) + .is_none() + ); + assert!( + map.remove_greatest_subset(&ClosedInterval { min: 0, max: 10 }) + .is_none() + ); + assert!( + map.remove_greatest_subset(&ClosedInterval { min: 10, max: 17 }) + .is_some_and(|info| info.0 == ClosedInterval { min: 10, max: 17 }) + ); + assert!( + map.remove_greatest_subset(&ClosedInterval { min: 210, max: 217 }) + .is_none() + ); + assert!( + map.remove_least_superset(&ClosedInterval { min: 3, max: 4 }) + .is_none() + ); + assert!( + map.remove_least_superset(&ClosedInterval { min: 10, max: 17 }) + .is_none() + ); + assert!( + map.remove_least_superset(&ClosedInterval { min: 210, max: 217 }) + .is_none() + ); + let mut map2 = SupersetMap::new(); + _ = map2.insert(ClosedInterval { min: 0, max: 3 }, ()); + _ = map2.insert(ClosedInterval { min: 0, max: 3 }, ()); + _ = map2.insert(ClosedInterval { min: 0, max: 4 }, ()); + _ = map2.insert(ClosedInterval { min: 1, max: 4 }, ()); + _ = map2.insert(ClosedInterval { min: 4, max: 6 }, ()); + _ = map2.insert(ClosedInterval { min: 2, max: 7 }, ()); + _ = map2.insert(ClosedInterval { min: 10, max: 17 }, ()); + assert_eq!( + map2.remove_supersets(&ClosedInterval { min: 0, max: 20 }), + 0 + ); + assert_eq!(map2.remove_subsets(&ClosedInterval { min: 0, max: 1 }), 0); + assert_eq!(map2.remove_subsets(&ClosedInterval { min: 0, max: 20 }), 3); + _ = map2.insert(ClosedInterval { min: 0, max: 3 }, ()); + _ = map2.insert(ClosedInterval { min: 0, max: 3 }, ()); + _ = map2.insert(ClosedInterval { min: 0, max: 4 }, ()); + _ = map2.insert(ClosedInterval { min: 1, max: 4 }, ()); + _ = map2.insert(ClosedInterval { min: 4, max: 6 }, ()); + _ = map2.insert(ClosedInterval { min: 2, max: 7 }, ()); + _ = map2.insert(ClosedInterval { min: 10, max: 17 }, ()); + assert_eq!(map2.remove_supersets(&ClosedInterval { min: 3, max: 4 }), 2); +} diff --git a/src/superset_set.rs b/src/superset_set.rs @@ -1,4 +1,7 @@ extern crate alloc; +/// Unit tests. +#[cfg(test)] +mod tests; use crate::{ SetOrd, SupersetMap, zfc::{BoundedCardinality, Cardinality, Set}, @@ -803,177 +806,3 @@ where } } } -#[cfg(test)] -mod tests { - use super::{ - super::zfc::{BoundedCardinality, Cardinality, Set, num_bigint::BigUint}, - SetOrd, SupersetSet, - }; - use core::{borrow::Borrow, cmp::Ordering}; - #[derive(Eq, PartialEq)] - struct ClosedInterval { - min: usize, - max: usize, - } - impl PartialOrd<Self> for ClosedInterval { - fn partial_cmp(&self, other: &Self) -> Option<Ordering> { - Some(self.cmp(other)) - } - } - impl Ord for ClosedInterval { - fn cmp(&self, other: &Self) -> Ordering { - match self.min.cmp(&other.min) { - Ordering::Less => { - if self.max >= other.max { - Ordering::Greater - } else { - Ordering::Less - } - } - Ordering::Equal => self.max.cmp(&other.max), - Ordering::Greater => { - if self.max > other.max { - Ordering::Greater - } else { - Ordering::Less - } - } - } - } - } - impl Set for ClosedInterval { - type Elem = usize; - #[expect(clippy::panic, reason = "want to crash when there is a bug")] - #[expect( - clippy::arithmetic_side_effects, - reason = "comment justifies correctness" - )] - fn bounded_cardinality(&self) -> BoundedCardinality { - BoundedCardinality::from_biguint_exact( - // `BigUint`s can always be added together. - BigUint::from(self.max.checked_sub(self.min).unwrap_or_else(|| { - panic!("superset_set::test::ClosedInterval must have max >= min") - })) + BigUint::from(1usize), - ) - } - #[expect(clippy::panic, reason = "want to crash when there is a bug")] - #[expect( - clippy::arithmetic_side_effects, - reason = "comment justifies correctness" - )] - fn cardinality(&self) -> Option<Cardinality> { - Some(Cardinality::Finite( - // `BigUint`s can always be added together. - BigUint::from(self.max.checked_sub(self.min).unwrap_or_else(|| { - panic!("superset_set::test::ClosedInterval must have max >= min") - })) + BigUint::from(1usize), - )) - } - fn contains<Q>(&self, elem: &Q) -> bool - where - Q: ?Sized + Borrow<Self::Elem> + Eq, - { - self.min <= *elem.borrow() && self.max >= *elem.borrow() - } - fn is_proper_subset(&self, val: &Self) -> bool { - match self.min.cmp(&val.min) { - Ordering::Less => false, - Ordering::Equal => self.max < val.max, - Ordering::Greater => self.max <= val.max, - } - } - fn is_subset(&self, val: &Self) -> bool { - self.min >= val.min && self.max <= val.max - } - } - impl SetOrd for ClosedInterval {} - #[test] - fn union() { - let mut set1 = SupersetSet::new(); - _ = set1.insert(ClosedInterval { min: 14, max: 19 }); - _ = set1.insert(ClosedInterval { min: 10, max: 12 }); - _ = set1.insert(ClosedInterval { min: 0, max: 3 }); - _ = set1.insert(ClosedInterval { min: 0, max: 2 }); - _ = set1.insert(ClosedInterval { min: 1, max: 4 }); - _ = set1.insert(ClosedInterval { min: 20, max: 22 }); - _ = set1.insert(ClosedInterval { min: 25, max: 26 }); - _ = set1.insert(ClosedInterval { min: 26, max: 27 }); - let mut set2 = SupersetSet::new(); - _ = set2.insert(ClosedInterval { min: 10, max: 12 }); - _ = set2.insert(ClosedInterval { min: 14, max: 16 }); - _ = set2.insert(ClosedInterval { min: 0, max: 3 }); - _ = set2.insert(ClosedInterval { min: 3, max: 4 }); - _ = set2.insert(ClosedInterval { min: 23, max: 25 }); - _ = set2.insert(ClosedInterval { min: 25, max: 27 }); - let mut union = set1.union(&set2); - assert!(union.next() == Some(&ClosedInterval { min: 0, max: 3 })); - assert!(union.next() == Some(&ClosedInterval { min: 1, max: 4 })); - assert!(union.next() == Some(&ClosedInterval { min: 10, max: 12 })); - assert!(union.next() == Some(&ClosedInterval { min: 14, max: 19 })); - assert!(union.next() == Some(&ClosedInterval { min: 20, max: 22 })); - assert!(union.next() == Some(&ClosedInterval { min: 23, max: 25 })); - assert!(union.next() == Some(&ClosedInterval { min: 25, max: 27 })); - assert!(union.next().is_none()); - assert!(union.next().is_none()); - assert!(union.next().is_none()); - } - #[test] - fn intersection() { - let mut set1 = SupersetSet::new(); - _ = set1.insert(ClosedInterval { min: 14, max: 19 }); - _ = set1.insert(ClosedInterval { min: 10, max: 12 }); - _ = set1.insert(ClosedInterval { min: 0, max: 3 }); - _ = set1.insert(ClosedInterval { min: 0, max: 2 }); - _ = set1.insert(ClosedInterval { min: 1, max: 4 }); - _ = set1.insert(ClosedInterval { min: 20, max: 22 }); - _ = set1.insert(ClosedInterval { min: 25, max: 26 }); - _ = set1.insert(ClosedInterval { min: 26, max: 27 }); - let mut set2 = SupersetSet::new(); - _ = set2.insert(ClosedInterval { min: 10, max: 12 }); - _ = set2.insert(ClosedInterval { min: 14, max: 16 }); - _ = set2.insert(ClosedInterval { min: 0, max: 3 }); - _ = set2.insert(ClosedInterval { min: 3, max: 4 }); - _ = set2.insert(ClosedInterval { min: 23, max: 25 }); - _ = set2.insert(ClosedInterval { min: 25, max: 27 }); - let mut intersection = set1.intersection(&set2); - assert!(intersection.next() == Some(&ClosedInterval { min: 0, max: 3 })); - assert!(intersection.next() == Some(&ClosedInterval { min: 3, max: 4 })); - assert!(intersection.next() == Some(&ClosedInterval { min: 10, max: 12 })); - assert!(intersection.next() == Some(&ClosedInterval { min: 14, max: 16 })); - assert!(intersection.next() == Some(&ClosedInterval { min: 25, max: 26 })); - assert!(intersection.next() == Some(&ClosedInterval { min: 26, max: 27 })); - assert!(intersection.next().is_none()); - assert!(intersection.next().is_none()); - assert!(intersection.next().is_none()); - } - #[test] - fn replace() { - let mut set = SupersetSet::new(); - _ = set.insert(ClosedInterval { min: 14, max: 19 }); - _ = set.insert(ClosedInterval { min: 10, max: 12 }); - _ = set.insert(ClosedInterval { min: 0, max: 3 }); - _ = set.insert(ClosedInterval { min: 0, max: 2 }); - _ = set.insert(ClosedInterval { min: 1, max: 4 }); - _ = set.insert(ClosedInterval { min: 20, max: 22 }); - _ = set.insert(ClosedInterval { min: 25, max: 26 }); - _ = set.insert(ClosedInterval { min: 26, max: 27 }); - // Does not replace proper supersets. - assert!( - set.replace(ClosedInterval { min: 20, max: 21 }).is_none() - && set.contains(&ClosedInterval { min: 20, max: 22 }) - && set.contains_proper_superset(&ClosedInterval { min: 20, max: 21 }) - && !set.contains(&ClosedInterval { min: 20, max: 21 }) - ); - // Successful replace. - assert!( - set.replace(ClosedInterval { min: 0, max: 3 }) - == Some(ClosedInterval { min: 0, max: 3 }) - && set.contains(&ClosedInterval { min: 0, max: 3 }) - ); - // Replace is just an insert when a superset does not exist. - assert!( - set.replace(ClosedInterval { min: 100, max: 300 }).is_none() - && set.contains(&ClosedInterval { min: 100, max: 300 }) - ); - } -} diff --git a/src/superset_set/tests.rs b/src/superset_set/tests.rs @@ -0,0 +1,181 @@ +extern crate alloc; +use super::Range; +use super::{ + super::zfc::{BoundedCardinality, Cardinality, Set, num_bigint::BigUint}, + SetOrd, SupersetSet, +}; +use alloc::collections::btree_map::BTreeMap; +use core::{borrow::Borrow, cmp::Ordering}; +#[derive(Eq, PartialEq)] +struct ClosedInterval { + min: usize, + max: usize, +} +impl PartialOrd<Self> for ClosedInterval { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} +impl Ord for ClosedInterval { + fn cmp(&self, other: &Self) -> Ordering { + match self.min.cmp(&other.min) { + Ordering::Less => { + if self.max >= other.max { + Ordering::Greater + } else { + Ordering::Less + } + } + Ordering::Equal => self.max.cmp(&other.max), + Ordering::Greater => { + if self.max > other.max { + Ordering::Greater + } else { + Ordering::Less + } + } + } + } +} +impl Set for ClosedInterval { + type Elem = usize; + #[expect(clippy::panic, reason = "want to crash when there is a bug")] + #[expect( + clippy::arithmetic_side_effects, + reason = "comment justifies correctness" + )] + fn bounded_cardinality(&self) -> BoundedCardinality { + BoundedCardinality::from_biguint_exact( + // `BigUint`s can always be added together. + BigUint::from(self.max.checked_sub(self.min).unwrap_or_else(|| { + panic!("superset_set::test::ClosedInterval must have max >= min") + })) + BigUint::from(1usize), + ) + } + #[expect(clippy::panic, reason = "want to crash when there is a bug")] + #[expect( + clippy::arithmetic_side_effects, + reason = "comment justifies correctness" + )] + fn cardinality(&self) -> Option<Cardinality> { + Some(Cardinality::Finite( + // `BigUint`s can always be added together. + BigUint::from(self.max.checked_sub(self.min).unwrap_or_else(|| { + panic!("superset_set::test::ClosedInterval must have max >= min") + })) + BigUint::from(1usize), + )) + } + fn contains<Q>(&self, elem: &Q) -> bool + where + Q: ?Sized + Borrow<Self::Elem> + Eq, + { + self.min <= *elem.borrow() && self.max >= *elem.borrow() + } + fn is_proper_subset(&self, val: &Self) -> bool { + match self.min.cmp(&val.min) { + Ordering::Less => false, + Ordering::Equal => self.max < val.max, + Ordering::Greater => self.max <= val.max, + } + } + fn is_subset(&self, val: &Self) -> bool { + self.min >= val.min && self.max <= val.max + } +} +impl SetOrd for ClosedInterval {} +#[test] +fn union() { + let mut set1 = SupersetSet::new(); + _ = set1.insert(ClosedInterval { min: 14, max: 19 }); + _ = set1.insert(ClosedInterval { min: 10, max: 12 }); + _ = set1.insert(ClosedInterval { min: 0, max: 3 }); + _ = set1.insert(ClosedInterval { min: 0, max: 2 }); + _ = set1.insert(ClosedInterval { min: 1, max: 4 }); + _ = set1.insert(ClosedInterval { min: 20, max: 22 }); + _ = set1.insert(ClosedInterval { min: 25, max: 26 }); + _ = set1.insert(ClosedInterval { min: 26, max: 27 }); + let mut set2 = SupersetSet::new(); + _ = set2.insert(ClosedInterval { min: 10, max: 12 }); + _ = set2.insert(ClosedInterval { min: 14, max: 16 }); + _ = set2.insert(ClosedInterval { min: 0, max: 3 }); + _ = set2.insert(ClosedInterval { min: 3, max: 4 }); + _ = set2.insert(ClosedInterval { min: 23, max: 25 }); + _ = set2.insert(ClosedInterval { min: 25, max: 27 }); + let mut union = set1.union(&set2); + assert!(union.next() == Some(&ClosedInterval { min: 0, max: 3 })); + assert!(union.next() == Some(&ClosedInterval { min: 1, max: 4 })); + assert!(union.next() == Some(&ClosedInterval { min: 10, max: 12 })); + assert!(union.next() == Some(&ClosedInterval { min: 14, max: 19 })); + assert!(union.next() == Some(&ClosedInterval { min: 20, max: 22 })); + assert!(union.next() == Some(&ClosedInterval { min: 23, max: 25 })); + assert!(union.next() == Some(&ClosedInterval { min: 25, max: 27 })); + assert!(union.next().is_none()); + assert!(union.next().is_none()); + assert!(union.next().is_none()); +} +#[test] +fn intersection() { + let mut set1 = SupersetSet::new(); + _ = set1.insert(ClosedInterval { min: 14, max: 19 }); + _ = set1.insert(ClosedInterval { min: 10, max: 12 }); + _ = set1.insert(ClosedInterval { min: 0, max: 3 }); + _ = set1.insert(ClosedInterval { min: 0, max: 2 }); + _ = set1.insert(ClosedInterval { min: 1, max: 4 }); + _ = set1.insert(ClosedInterval { min: 20, max: 22 }); + _ = set1.insert(ClosedInterval { min: 25, max: 26 }); + _ = set1.insert(ClosedInterval { min: 26, max: 27 }); + let mut set2 = SupersetSet::new(); + _ = set2.insert(ClosedInterval { min: 10, max: 12 }); + _ = set2.insert(ClosedInterval { min: 14, max: 16 }); + _ = set2.insert(ClosedInterval { min: 0, max: 3 }); + _ = set2.insert(ClosedInterval { min: 3, max: 4 }); + _ = set2.insert(ClosedInterval { min: 23, max: 25 }); + _ = set2.insert(ClosedInterval { min: 25, max: 27 }); + let mut intersection = set1.intersection(&set2); + assert!(intersection.next() == Some(&ClosedInterval { min: 0, max: 3 })); + assert!(intersection.next() == Some(&ClosedInterval { min: 3, max: 4 })); + assert!(intersection.next() == Some(&ClosedInterval { min: 10, max: 12 })); + assert!(intersection.next() == Some(&ClosedInterval { min: 14, max: 16 })); + assert!(intersection.next() == Some(&ClosedInterval { min: 25, max: 26 })); + assert!(intersection.next() == Some(&ClosedInterval { min: 26, max: 27 })); + assert!(intersection.next().is_none()); + assert!(intersection.next().is_none()); + assert!(intersection.next().is_none()); +} +#[test] +fn replace() { + let mut set = SupersetSet::new(); + _ = set.insert(ClosedInterval { min: 14, max: 19 }); + _ = set.insert(ClosedInterval { min: 10, max: 12 }); + _ = set.insert(ClosedInterval { min: 0, max: 3 }); + _ = set.insert(ClosedInterval { min: 0, max: 2 }); + _ = set.insert(ClosedInterval { min: 1, max: 4 }); + _ = set.insert(ClosedInterval { min: 20, max: 22 }); + _ = set.insert(ClosedInterval { min: 25, max: 26 }); + _ = set.insert(ClosedInterval { min: 26, max: 27 }); + // Does not replace proper supersets. + assert!( + set.replace(ClosedInterval { min: 20, max: 21 }).is_none() + && set.contains(&ClosedInterval { min: 20, max: 22 }) + && set.contains_proper_superset(&ClosedInterval { min: 20, max: 21 }) + && !set.contains(&ClosedInterval { min: 20, max: 21 }) + ); + // Successful replace. + assert!( + set.replace(ClosedInterval { min: 0, max: 3 }) == Some(ClosedInterval { min: 0, max: 3 }) + && set.contains(&ClosedInterval { min: 0, max: 3 }) + ); + // Replace is just an insert when a superset does not exist. + assert!( + set.replace(ClosedInterval { min: 100, max: 300 }).is_none() + && set.contains(&ClosedInterval { min: 100, max: 300 }) + ); +} +#[expect(clippy::zero_sized_map_values, reason = "set is defined as it")] +#[ignore = "silence lint"] +#[test] +fn range() { + _ = Range { + iter: BTreeMap::<u32, ()>::new().range(..), + }; +}