From ac62c67cbd8acc8551d4f6426216534cac03ca86 Mon Sep 17 00:00:00 2001 From: ripytide Date: Sun, 11 Dec 2022 23:48:05 +0000 Subject: [PATCH] all tests passing --- src/range_bounds_map.rs | 1044 +++++++++++++++++++++++++++++++++++++-- todo.md | 2 + 2 files changed, 1009 insertions(+), 37 deletions(-) diff --git a/src/range_bounds_map.rs b/src/range_bounds_map.rs index 6f31266..f9f4a04 100644 --- a/src/range_bounds_map.rs +++ b/src/range_bounds_map.rs @@ -1337,7 +1337,7 @@ where V: Clone, K: TryFromBounds, { - self.cut(&range_bounds)?; + let _ = self.cut(&range_bounds)?; self.insert_platonic(range_bounds, value).unwrap(); return Ok(()); @@ -1435,25 +1435,25 @@ where } #[derive(Debug, PartialEq)] -enum Config { - LeftFirstNonOverlapping((Bound, Bound), (Bound, Bound)), - LeftFirstPartialOverlap((Bound, Bound), (Bound, Bound)), - LeftContainsRight((Bound, Bound), (Bound, Bound)), +enum Config { + LeftFirstNonOverlapping, + LeftFirstPartialOverlap, + LeftContainsRight, - RightFirstNonOverlapping((Bound, Bound), (Bound, Bound)), - RightFirstPartialOverlap((Bound, Bound), (Bound, Bound)), - RightContainsLeft((Bound, Bound), (Bound, Bound)), + RightFirstNonOverlapping, + RightFirstPartialOverlap, + RightContainsLeft, } #[untested] -fn config<'a, I, A, B>(a: &'a A, b: &'a B) -> Config<&'a I> +fn config<'a, I, A, B>(a: &'a A, b: &'a B) -> Config where A: RangeBounds, B: RangeBounds, I: PartialOrd, { - let a_all @ (a_start, a_end) = (a.start_bound(), a.end_bound()); - let b_all @ (b_start, b_end) = (b.start_bound(), b.end_bound()); + let (a_start, a_end) = expand(a); + let (b_start, b_end) = expand(b); match BoundOrd::start(a_start) < BoundOrd::start(b_start) { true => { @@ -1461,9 +1461,9 @@ where contains_bound_ord(a, BoundOrd::start(b_start)), contains_bound_ord(a, BoundOrd::end(b_end)), ) { - (false, false) => Config::LeftFirstNonOverlapping(a_all, b_all), - (true, false) => Config::LeftFirstPartialOverlap(a_all, b_all), - (true, true) => Config::LeftContainsRight(a_all, b_all), + (false, false) => Config::LeftFirstNonOverlapping, + (true, false) => Config::LeftFirstPartialOverlap, + (true, true) => Config::LeftContainsRight, (false, true) => unreachable!(), } } @@ -1472,11 +1472,9 @@ where contains_bound_ord(b, BoundOrd::start(a_start)), contains_bound_ord(b, BoundOrd::end(a_end)), ) { - (false, false) => { - Config::RightFirstNonOverlapping(a_all, b_all) - } - (true, false) => Config::RightFirstPartialOverlap(a_all, b_all), - (true, true) => Config::RightContainsLeft(a_all, b_all), + (false, false) => Config::RightFirstNonOverlapping, + (true, false) => Config::RightFirstPartialOverlap, + (true, true) => Config::RightContainsLeft, (false, true) => unreachable!(), } } @@ -1491,25 +1489,27 @@ enum SortedConfig { } #[rustfmt::skip] -#[untested] +#[trivial] fn sorted_config<'a, I, A, B>(a: &'a A, b: &'a B) -> SortedConfig<&'a I> where A: RangeBounds, B: RangeBounds, I: PartialOrd, { + let ae = expand(a); + let be = expand(b); match config(a, b) { - Config::LeftFirstNonOverlapping(a, b) => SortedConfig::NonOverlapping(a, b), - Config::LeftFirstPartialOverlap(a, b) => SortedConfig::Swallowed(a, b), - Config::LeftContainsRight(a, b) => SortedConfig::Swallowed(a, b), + Config::LeftFirstNonOverlapping => SortedConfig::NonOverlapping(ae, be), + Config::LeftFirstPartialOverlap => SortedConfig::Swallowed(ae, be), + Config::LeftContainsRight => SortedConfig::Swallowed(ae, be), - Config::RightFirstNonOverlapping(a, b) => SortedConfig::NonOverlapping(b, a), - Config::RightFirstPartialOverlap(a, b) => SortedConfig::PartialOverlap(b, a), - Config::RightContainsLeft(a, b) => SortedConfig::Swallowed(b, a), + Config::RightFirstNonOverlapping => SortedConfig::NonOverlapping(be, ae), + Config::RightFirstPartialOverlap => SortedConfig::PartialOverlap(be, ae), + Config::RightContainsLeft => SortedConfig::Swallowed(be, ae), } } -#[untested] +#[trivial] fn contains_bound_ord(range_bounds: &A, bound_ord: BoundOrd<&I>) -> bool where A: RangeBounds, @@ -1552,27 +1552,33 @@ where }; match config(base_range_bounds, cut_range_bounds) { - Config::LeftFirstNonOverlapping(_, _) => { + Config::LeftFirstNonOverlapping => { result.before_cut = Some(base_all); } - Config::LeftFirstPartialOverlap(_, _) => { + Config::LeftFirstPartialOverlap => { + result.before_cut = Some((base_start, flip_bound(cut_start))); result.inside_cut = Some((cut_start, base_end)); - result.after_cut = Some((base_start, flip_bound(cut_start))); } - Config::LeftContainsRight(_, _) => { + Config::LeftContainsRight => { result.before_cut = Some((base_start, flip_bound(cut_start))); result.inside_cut = Some(cut_all); - result.after_cut = Some((flip_bound(cut_end), base_end)); + // exception for Unbounded-ending things + match cut_end { + Bound::Unbounded => {} + _ => { + result.after_cut = Some((flip_bound(cut_end), base_end)); + } + } } - Config::RightFirstNonOverlapping(_, _) => { + Config::RightFirstNonOverlapping => { result.after_cut = Some(base_all); } - Config::RightFirstPartialOverlap(_, _) => { - result.before_cut = Some((flip_bound(cut_end), base_end)); + Config::RightFirstPartialOverlap => { + result.after_cut = Some((flip_bound(cut_end), base_end)); result.inside_cut = Some((base_start, cut_end)); } - Config::RightContainsLeft(_, _) => { + Config::RightContainsLeft => { result.inside_cut = Some(base_all); } } @@ -1642,7 +1648,7 @@ where K: RangeBounds, I: Clone, { - cloned_bounds((range_bounds.start_bound(), range_bounds.end_bound())) + cloned_bounds(expand(range_bounds)) } #[trivial] @@ -1663,3 +1669,967 @@ fn flip_bound(bound: Bound<&I>) -> Bound<&I> { Bound::Unbounded => Bound::Unbounded, } } + +#[cfg(test)] +mod tests { + use std::ops::{Bound, Range, RangeBounds}; + + use pretty_assertions::assert_eq; + + use super::*; + use crate::bound_ord::BoundOrd; + + type TestBounds = (Bound, Bound); + + //only every other number to allow mathematical_overlapping_definition + //to test between bounds in finite using smaller intervalled finite + pub(crate) const NUMBERS: &'static [u8] = &[2, 4, 6, 8, 10]; + //go a bit around on either side to compensate for Unbounded + pub(crate) const NUMBERS_DOMAIN: &'static [u8] = + &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; + + #[derive(Debug, PartialEq, Clone)] + enum MultiBounds { + Inclusive(u8, u8), + Exclusive(u8, u8), + } + + fn mii(start: u8, end: u8) -> MultiBounds { + MultiBounds::Inclusive(start, end) + } + fn mee(start: u8, end: u8) -> MultiBounds { + MultiBounds::Exclusive(start, end) + } + + impl RangeBounds for MultiBounds { + fn start_bound(&self) -> Bound<&u8> { + match self { + MultiBounds::Inclusive(start, _) => Bound::Included(start), + MultiBounds::Exclusive(start, _) => Bound::Excluded(start), + } + } + fn end_bound(&self) -> Bound<&u8> { + match self { + MultiBounds::Inclusive(_, end) => Bound::Included(end), + MultiBounds::Exclusive(_, end) => Bound::Excluded(end), + } + } + } + impl TryFromBounds for MultiBounds { + fn try_from_bounds( + start_bound: Bound, + end_bound: Bound, + ) -> Option { + match (start_bound, end_bound) { + (Bound::Included(start), Bound::Included(end)) => { + Some(mii(start, end)) + } + (Bound::Excluded(start), Bound::Excluded(end)) => { + Some(mee(start, end)) + } + _ => None, + } + } + } + + #[test] + fn insert_platonic_tests() { + assert_insert_platonic( + basic(), + (ii(0, 4), false), + Err(OverlapError), + None::<[_; 0]>, + ); + assert_insert_platonic( + basic(), + (ii(5, 6), false), + Err(OverlapError), + None::<[_; 0]>, + ); + assert_insert_platonic( + basic(), + (ee(7, 8), false), + Ok(()), + Some([ + (ui(4), false), + (ee(5, 7), true), + (ii(7, 7), false), + (ee(7, 8), false), + (ie(14, 16), true), + ]), + ); + assert_insert_platonic( + basic(), + (ii(4, 5), true), + Err(OverlapError), + None::<[_; 0]>, + ); + assert_insert_platonic( + basic(), + (ei(4, 5), true), + Ok(()), + Some([ + (ui(4), false), + (ei(4, 5), true), + (ee(5, 7), true), + (ii(7, 7), false), + (ie(14, 16), true), + ]), + ); + } + fn assert_insert_platonic( + mut before: RangeBoundsMap, + to_insert: (TestBounds, bool), + result: Result<(), OverlapError>, + after: Option<[(TestBounds, bool); N]>, + ) { + let clone = before.clone(); + assert_eq!(before.insert_platonic(to_insert.0, to_insert.1), result); + match after { + Some(after) => { + assert_eq!(before, RangeBoundsMap::try_from(after).unwrap()) + } + None => assert_eq!(before, clone), + } + } + + #[test] + fn overlapping_tests() { + //case zero + for overlap_range in all_valid_test_bounds() { + //you can't overlap nothing + assert!( + RangeBoundsMap::, ()>::new() + .overlapping(&overlap_range) + .next() + .is_none() + ); + } + + //case one + for overlap_range in all_valid_test_bounds() { + for inside_range in all_valid_test_bounds() { + let mut range_bounds_set = RangeBoundsMap::new(); + range_bounds_set.insert_platonic(inside_range, ()).unwrap(); + + let mut expected_overlapping = Vec::new(); + if overlaps(&overlap_range, &inside_range) { + expected_overlapping.push(inside_range); + } + + let overlapping = range_bounds_set + .overlapping(&overlap_range) + .map(|(key, _)| key) + .copied() + .collect::>(); + + if overlapping != expected_overlapping { + dbg!(overlap_range, inside_range); + dbg!(overlapping, expected_overlapping); + panic!( + "Discrepency in .overlapping() with single inside range detected!" + ); + } + } + } + + //case two + for overlap_range in all_valid_test_bounds() { + for (inside_range1, inside_range2) in + all_non_overlapping_test_bound_pairs() + { + let mut range_bounds_set = RangeBoundsMap::new(); + range_bounds_set.insert_platonic(inside_range1, ()).unwrap(); + range_bounds_set.insert_platonic(inside_range2, ()).unwrap(); + + let mut expected_overlapping = Vec::new(); + if overlaps(&overlap_range, &inside_range1) { + expected_overlapping.push(inside_range1); + } + if overlaps(&overlap_range, &inside_range2) { + expected_overlapping.push(inside_range2); + } + //make our expected_overlapping the correct order + if expected_overlapping.len() > 1 { + if BoundOrd::start(expected_overlapping[0].start_bound()) + > BoundOrd::start(expected_overlapping[1].start_bound()) + { + expected_overlapping.swap(0, 1); + } + } + + let overlapping = range_bounds_set + .overlapping(&overlap_range) + .map(|(key, _)| key) + .copied() + .collect::>(); + + if overlapping != expected_overlapping { + dbg!(overlap_range, inside_range1, inside_range2); + dbg!(overlapping, expected_overlapping); + panic!( + "Discrepency in .overlapping() with two inside ranges detected!" + ); + } + } + } + } + + #[test] + fn remove_overlapping_tests() { + assert_remove_overlapping(basic(), ii(5, 5), [], None::<[_; 0]>); + assert_remove_overlapping( + basic(), + uu(), + [ + (ui(4), false), + (ee(5, 7), true), + (ii(7, 7), false), + (ie(14, 16), true), + ], + Some([]), + ); + assert_remove_overlapping( + basic(), + ii(6, 7), + [(ee(5, 7), true), (ii(7, 7), false)], + Some([(ui(4), false), (ie(14, 16), true)]), + ); + assert_remove_overlapping( + basic(), + iu(6), + [(ee(5, 7), true), (ii(7, 7), false), (ie(14, 16), true)], + Some([(ui(4), false)]), + ); + } + fn assert_remove_overlapping( + mut before: RangeBoundsMap, + to_remove: TestBounds, + result: [(TestBounds, bool); N], + after: Option<[(TestBounds, bool); Y]>, + ) { + let clone = before.clone(); + assert_eq!( + before.remove_overlapping(&to_remove).collect::>(), + result + ); + match after { + Some(after) => { + assert_eq!(before, RangeBoundsMap::try_from(after).unwrap()) + } + None => assert_eq!(before, clone), + } + } + + fn basic() -> RangeBoundsMap { + RangeBoundsMap::try_from([ + (ui(4), false), + (ee(5, 7), true), + (ii(7, 7), false), + (ie(14, 16), true), + ]) + .unwrap() + } + + #[test] + fn cut_tests() { + assert_cut(basic(), ii(50, 60), Ok([]), None::<[_; 0]>); + assert_cut( + basic(), + uu(), + Ok([ + (ui(4), false), + (ee(5, 7), true), + (ii(7, 7), false), + (ie(14, 16), true), + ]), + Some([]), + ); + assert_cut( + basic(), + ui(6), + Ok([(ui(4), false), (ei(5, 6), true)]), + Some([(ee(6, 7), true), (ii(7, 7), false), (ie(14, 16), true)]), + ); + assert_cut( + basic(), + iu(6), + Ok([(ie(6, 7), true), (ii(7, 7), false), (ie(14, 16), true)]), + Some([(ui(4), false), (ee(5, 6), true)]), + ); + + assert_cut( + special(), + mee(5, 7), + Ok([((Bound::Excluded(5), Bound::Included(6)), false)]), + Some([(mii(4, 5), false), (mee(7, 8), true), (mii(8, 12), false)]), + ); + assert_cut(special(), mee(6, 7), Ok([]), None::<[_; 0]>); + assert_cut( + special(), + mii(5, 6), + Err::<[_; 0], _>(TryFromBoundsError), + None::<[_; 0]>, + ); + assert_cut( + special(), + mii(6, 7), + Err::<[_; 0], _>(TryFromBoundsError), + None::<[_; 0]>, + ); + assert_cut( + special(), + 7..8, + Ok([(expand_cloned(&mee(7, 8)), true)]), + Some([(mii(4, 6), false), (mii(8, 12), false)]), + ); + assert_cut( + special(), + mii(7, 10), + Err::<[_; 0], _>(TryFromBoundsError), + None::<[_; 0]>, + ); + assert_cut( + special(), + mee(4, 6), + Ok([(expand_cloned(&mee(4, 6)), false)]), + Some([ + (mii(4, 4), false), + (mii(6, 6), false), + (mee(7, 8), true), + (mii(8, 12), false), + ]), + ); + } + fn special() -> RangeBoundsMap { + RangeBoundsMap::try_from([ + (mii(4, 6), false), + (mee(7, 8), true), + (mii(8, 12), false), + ]) + .unwrap() + } + + fn assert_cut( + mut before: RangeBoundsMap, + to_cut: Q, + result: Result<[((Bound, Bound), V); Y], TryFromBoundsError>, + after: Option<[(K, V); N]>, + ) where + I: Ord + Clone + Debug, + K: Clone + TryFromBounds + Debug + PartialEq, + V: Clone + Debug + PartialEq, + K: RangeBounds, + Q: RangeBounds, + { + let clone = before.clone(); + match before.cut(&to_cut) { + Ok(iter) => { + assert_eq!(iter.collect::>(), result.unwrap()); + } + Err(x) => { + assert_eq!(x, result.unwrap_err()); + } + } + match after { + Some(after) => { + assert_eq!(before, RangeBoundsMap::try_from(after).unwrap()) + } + None => assert_eq!(before, clone), + } + } + + #[test] + fn gaps_tests() { + assert_gaps(basic(), ii(50, 60), [ii(50, 60)]); + assert_gaps(basic(), iu(50), [iu(50)]); + assert_gaps(basic(), ee(3, 16), [ei(4, 5), ee(7, 14)]); + assert_gaps(basic(), ei(3, 16), [ei(4, 5), ee(7, 14), ii(16, 16)]); + assert_gaps(basic(), ue(5), [ee(4, 5)]); + assert_gaps(basic(), ui(3), []); + assert_gaps(basic(), ii(5, 5), [ii(5, 5)]); + assert_gaps(basic(), ii(6, 6), []); + assert_gaps(basic(), ii(7, 7), []); + assert_gaps(basic(), ii(8, 8), [ii(8, 8)]); + } + fn assert_gaps( + range_bounds_map: RangeBoundsMap, + outer_range_bounds: TestBounds, + result: [TestBounds; N], + ) { + assert_eq!( + range_bounds_map + .gaps(&outer_range_bounds) + .map(|(start, end)| (start.cloned(), end.cloned())) + .collect::>(), + result + ); + } + + #[test] + fn insert_coalesce_touching_tests() { + assert_insert_coalesce_touching( + basic(), + (ii(0, 4), false), + Err(OverlapOrTryFromBoundsError::Overlap(OverlapError)), + None::<[_; 0]>, + ); + assert_insert_coalesce_touching( + basic(), + (ee(7, 10), false), + Ok(&ie(7, 10)), + Some([ + (ui(4), false), + (ee(5, 7), true), + (ie(7, 10), false), + (ie(14, 16), true), + ]), + ); + assert_insert_coalesce_touching( + basic(), + (ee(7, 11), true), + Ok(&ie(7, 11)), + Some([ + (ui(4), false), + (ee(5, 7), true), + (ie(7, 11), true), + (ie(14, 16), true), + ]), + ); + assert_insert_coalesce_touching( + basic(), + (ee(12, 13), true), + Ok(&ee(12, 13)), + Some([ + (ui(4), false), + (ee(5, 7), true), + (ii(7, 7), false), + (ee(12, 13), true), + (ie(14, 16), true), + ]), + ); + assert_insert_coalesce_touching( + basic(), + (ee(13, 14), false), + Ok(&ee(13, 16)), + Some([ + (ui(4), false), + (ee(5, 7), true), + (ii(7, 7), false), + (ee(13, 16), false), + ]), + ); + assert_insert_coalesce_touching( + basic(), + (ee(7, 14), false), + Ok(&ie(7, 16)), + Some([(ui(4), false), (ee(5, 7), true), (ie(7, 16), false)]), + ); + + assert_insert_coalesce_touching( + special(), + (mee(6, 7), true), + Err(OverlapOrTryFromBoundsError::TryFromBounds( + TryFromBoundsError, + )), + None::<[_; 0]>, + ); + assert_insert_coalesce_touching( + special(), + (mii(6, 7), true), + Err(OverlapOrTryFromBoundsError::Overlap(OverlapError)), + None::<[_; 0]>, + ); + assert_insert_coalesce_touching( + special(), + (mee(12, 15), true), + Err(OverlapOrTryFromBoundsError::TryFromBounds( + TryFromBoundsError, + )), + None::<[_; 0]>, + ); + assert_insert_coalesce_touching( + special(), + (mii(12, 15), true), + Err(OverlapOrTryFromBoundsError::Overlap(OverlapError)), + None::<[_; 0]>, + ); + } + fn assert_insert_coalesce_touching( + mut before: RangeBoundsMap, + to_insert: (K, V), + result: Result<&K, OverlapOrTryFromBoundsError>, + after: Option<[(K, V); N]>, + ) where + I: Ord + Clone + Debug, + K: Clone + TryFromBounds + Debug + PartialEq, + V: Clone + Debug + PartialEq, + K: RangeBounds, + { + let clone = before.clone(); + assert_eq!( + before.insert_coalesce_touching(to_insert.0, to_insert.1), + result + ); + match after { + Some(after) => { + assert_eq!(before, RangeBoundsMap::try_from(after).unwrap()) + } + None => assert_eq!(before, clone), + } + } + + #[test] + fn insert_coalesce_overlapping_tests() { + assert_insert_coalesce_overlapping( + basic(), + (ii(0, 2), true), + Ok(&(ui(4))), + Some([ + (ui(4), true), + (ee(5, 7), true), + (ii(7, 7), false), + (ie(14, 16), true), + ]), + ); + assert_insert_coalesce_overlapping( + basic(), + (ie(14, 16), false), + Ok(&ie(14, 16)), + Some([ + (ui(4), false), + (ee(5, 7), true), + (ii(7, 7), false), + (ie(14, 16), false), + ]), + ); + assert_insert_coalesce_overlapping( + basic(), + (ii(6, 11), false), + Ok(&ei(5, 11)), + Some([(ui(4), false), (ei(5, 11), false), (ie(14, 16), true)]), + ); + assert_insert_coalesce_overlapping( + basic(), + (ii(15, 18), true), + Ok(&ii(14, 18)), + Some([ + (ui(4), false), + (ee(5, 7), true), + (ii(7, 7), false), + (ii(14, 18), true), + ]), + ); + assert_insert_coalesce_overlapping( + basic(), + (uu(), false), + Ok(&uu()), + Some([(uu(), false)]), + ); + + assert_insert_coalesce_overlapping( + special(), + (mii(10, 18), true), + Ok(&mii(8, 18)), + Some([(mii(4, 6), false), (mee(7, 8), true), (mii(8, 18), true)]), + ); + assert_insert_coalesce_overlapping( + special(), + (mee(10, 18), true), + Err(TryFromBoundsError), + None::<[_; 0]>, + ); + assert_insert_coalesce_overlapping( + special(), + (mee(8, 12), true), + Ok(&mii(8, 12)), + Some([(mii(4, 6), false), (mee(7, 8), true), (mii(8, 12), true)]), + ); + assert_insert_coalesce_overlapping( + special(), + (mee(7, 8), false), + Ok(&mee(7, 8)), + Some([(mii(4, 6), false), (mee(7, 8), false), (mii(8, 12), false)]), + ); + assert_insert_coalesce_overlapping( + special(), + (mii(7, 8), false), + Ok(&mii(7, 12)), + Some([(mii(4, 6), false), (mii(7, 12), false)]), + ); + } + fn assert_insert_coalesce_overlapping( + mut before: RangeBoundsMap, + to_insert: (K, V), + result: Result<&K, TryFromBoundsError>, + after: Option<[(K, V); N]>, + ) where + I: Ord + Clone + Debug, + K: Clone + TryFromBounds + Debug + PartialEq, + V: Clone + Debug + PartialEq, + K: RangeBounds, + { + let clone = before.clone(); + assert_eq!( + before.insert_coalesce_overlapping(to_insert.0, to_insert.1), + result + ); + match after { + Some(after) => { + assert_eq!(before, RangeBoundsMap::try_from(after).unwrap()) + } + None => assert_eq!(before, clone), + } + } + + #[test] + fn insert_coalesce_touching_or_overlapping_tests() { + assert_insert_coalesce_touching_or_overlapping( + RangeBoundsMap::try_from([(1..4, false)]).unwrap(), + (-4..1, true), + Ok(&(-4..4)), + Some([(-4..4, true)]), + ); + + //copied from insert_coalesce_overlapping_tests + assert_insert_coalesce_touching_or_overlapping( + basic(), + (ii(0, 2), true), + Ok(&(ui(4))), + Some([ + (ui(4), true), + (ee(5, 7), true), + (ii(7, 7), false), + (ie(14, 16), true), + ]), + ); + assert_insert_coalesce_touching_or_overlapping( + basic(), + (ie(14, 16), false), + Ok(&ie(14, 16)), + Some([ + (ui(4), false), + (ee(5, 7), true), + (ii(7, 7), false), + (ie(14, 16), false), + ]), + ); + assert_insert_coalesce_touching_or_overlapping( + basic(), + (ii(6, 11), false), + Ok(&ei(5, 11)), + Some([(ui(4), false), (ei(5, 11), false), (ie(14, 16), true)]), + ); + assert_insert_coalesce_touching_or_overlapping( + basic(), + (ii(15, 18), true), + Ok(&ii(14, 18)), + Some([ + (ui(4), false), + (ee(5, 7), true), + (ii(7, 7), false), + (ii(14, 18), true), + ]), + ); + assert_insert_coalesce_touching_or_overlapping( + basic(), + (uu(), false), + Ok(&uu()), + Some([(uu(), false)]), + ); + //the only difference from the insert_coalesce_overlapping + assert_insert_coalesce_touching_or_overlapping( + basic(), + (ii(7, 14), false), + Ok(&ee(5, 16)), + Some([(ui(4), false), (ee(5, 16), false)]), + ); + + //copied from insert_coalesce_overlapping_tests + assert_insert_coalesce_touching_or_overlapping( + special(), + (mii(10, 18), true), + Ok(&mii(8, 18)), + Some([(mii(4, 6), false), (mee(7, 8), true), (mii(8, 18), true)]), + ); + assert_insert_coalesce_touching_or_overlapping( + special(), + (mee(10, 18), true), + Err(TryFromBoundsError), + None::<[_; 0]>, + ); + assert_insert_coalesce_touching_or_overlapping( + special(), + (mee(8, 12), true), + Ok(&mii(8, 12)), + Some([(mii(4, 6), false), (mee(7, 8), true), (mii(8, 12), true)]), + ); + assert_insert_coalesce_touching_or_overlapping( + special(), + (mee(7, 8), false), + Err(TryFromBoundsError), + None::<[_; 0]>, + ); + assert_insert_coalesce_touching_or_overlapping( + special(), + (mii(7, 8), false), + Ok(&mii(7, 12)), + Some([(mii(4, 6), false), (mii(7, 12), false)]), + ); + //copied from insert_coalesce_touching_tests + assert_insert_coalesce_touching_or_overlapping( + special(), + (mee(6, 7), true), + Err(TryFromBoundsError), + None::<[_; 0]>, + ); + assert_insert_coalesce_touching_or_overlapping( + special(), + (mee(12, 15), true), + Err(TryFromBoundsError), + None::<[_; 0]>, + ); + } + fn assert_insert_coalesce_touching_or_overlapping( + mut before: RangeBoundsMap, + to_insert: (K, V), + result: Result<&K, TryFromBoundsError>, + after: Option<[(K, V); N]>, + ) where + I: Ord + Clone + Debug, + K: Clone + TryFromBounds + Debug + PartialEq, + V: Clone + Debug + PartialEq, + K: RangeBounds, + { + let clone = before.clone(); + assert_eq!( + before.insert_coalesce_touching_or_overlapping( + to_insert.0, + to_insert.1 + ), + result + ); + match after { + Some(after) => { + assert_eq!(before, RangeBoundsMap::try_from(after).unwrap()) + } + None => assert_eq!(before, clone), + } + } + + #[test] + fn config_tests() { + assert_eq!(config(&(1..4), &(6..8)), Config::LeftFirstNonOverlapping); + assert_eq!(config(&(1..4), &(2..8)), Config::LeftFirstPartialOverlap); + assert_eq!(config(&(1..4), &(2..3)), Config::LeftContainsRight); + + assert_eq!(config(&(6..8), &(1..4)), Config::RightFirstNonOverlapping); + assert_eq!(config(&(2..8), &(1..4)), Config::RightFirstPartialOverlap); + assert_eq!(config(&(2..3), &(1..4)), Config::RightContainsLeft); + } + + #[test] + fn overlaps_tests() { + for range_bounds1 in all_valid_test_bounds() { + for range_bounds2 in all_valid_test_bounds() { + let our_answer = overlaps(&range_bounds1, &range_bounds2); + + let mathematical_definition_of_overlap = + NUMBERS_DOMAIN.iter().any(|x| { + range_bounds1.contains(x) && range_bounds2.contains(x) + }); + + if our_answer != mathematical_definition_of_overlap { + dbg!(range_bounds1, range_bounds2); + dbg!(mathematical_definition_of_overlap, our_answer); + panic!("Discrepency in overlaps() detected!"); + } + } + } + } + + #[test] + fn cut_range_bounds_tests() { + for base in all_valid_test_bounds() { + for cut in all_valid_test_bounds() { + let cut_result @ CutResult { + before_cut: b, + inside_cut: i, + after_cut: a, + } = cut_range_bounds(&base, &cut); + + let mut on_left = true; + + // The definition of a cut is: A && NOT B + for x in NUMBERS_DOMAIN { + let base_contains = base.contains(x); + let cut_contains = cut.contains(x); + + if cut_contains { + on_left = false; + } + + let invariant = match (base_contains, cut_contains) { + (false, _) => !con(b, x) && !con(i, x) && !con(a, x), + (true, false) => { + if on_left { + con(b, x) && !con(i, x) && !con(a, x) + } else { + !con(b, x) && !con(i, x) && con(a, x) + } + } + (true, true) => !con(b, x) && con(i, x) && !con(a, x), + }; + + if !invariant { + dbg!(base_contains); + dbg!(cut_contains); + + dbg!(on_left); + + dbg!(base); + dbg!(cut); + dbg!(cut_result); + + dbg!(x); + + panic!("Invariant Broken!"); + } + } + } + } + } + fn con(x: Option<(Bound<&u8>, Bound<&u8>)>, point: &u8) -> bool { + match x { + Some(y) => y.contains(point), + None => false, + } + } + + #[test] + fn touches_tests() { + for range_bounds1 in all_valid_test_bounds() { + for range_bounds2 in all_valid_test_bounds() { + let our_answer = touches(&range_bounds1, &range_bounds2); + + let mathematical_definition_of_touches = + NUMBERS_DOMAIN.iter().tuple_windows().any(|(x1, x2)| { + (range_bounds1.contains(x1) + && !range_bounds1.contains(x2) + && range_bounds2.contains(x2) + && !range_bounds2.contains(x1)) + || (range_bounds1.contains(x2) + && !range_bounds1.contains(x1) && range_bounds2 + .contains(x1) && !range_bounds2.contains(x2)) + }); + + if our_answer != mathematical_definition_of_touches { + dbg!(range_bounds1, range_bounds2); + dbg!(mathematical_definition_of_touches, our_answer); + panic!("Discrepency in touches() detected!"); + } + } + } + } + + // Test Helper Functions + //====================== + fn all_non_overlapping_test_bound_pairs() -> Vec<(TestBounds, TestBounds)> { + let mut output = Vec::new(); + for test_bounds1 in all_valid_test_bounds() { + for test_bounds2 in all_valid_test_bounds() { + if !overlaps(&test_bounds1, &test_bounds2) { + output.push((test_bounds1, test_bounds2)); + } + } + } + + return output; + } + + fn all_valid_test_bounds() -> Vec { + let mut output = Vec::new(); + + //bounded-bounded + output.append(&mut all_finite_bounded_pairs()); + //bounded-unbounded + for start_bound in all_finite_bounded() { + output.push((start_bound, u())); + } + //unbounded-bounded + for end_bound in all_finite_bounded() { + output.push((u(), end_bound)); + } + //unbounded-unbounded + output.push(uu()); + + return output; + } + + fn all_finite_bounded_pairs() -> Vec<(Bound, Bound)> { + let mut output = Vec::new(); + for i in NUMBERS { + for j in NUMBERS { + for i_ex in [false, true] { + for j_ex in [false, true] { + if j > i || (j == i && !i_ex && !j_ex) { + output.push(( + finite_bound(*i, i_ex), + finite_bound(*j, j_ex), + )); + } + } + } + } + } + return output; + } + + fn all_finite_bounded() -> Vec> { + let mut output = Vec::new(); + for i in NUMBERS { + for j in 0..=1 { + output.push(finite_bound(*i, j == 1)); + } + } + return output; + } + + fn finite_bound(x: u8, included: bool) -> Bound { + match included { + false => Bound::Included(x), + true => Bound::Excluded(x), + } + } + + fn uu() -> TestBounds { + (Bound::Unbounded, Bound::Unbounded) + } + fn ui(x: u8) -> TestBounds { + (Bound::Unbounded, Bound::Included(x)) + } + fn ue(x: u8) -> TestBounds { + (Bound::Unbounded, Bound::Excluded(x)) + } + fn iu(x: u8) -> TestBounds { + (Bound::Included(x), Bound::Unbounded) + } + //fn eu(x: u8) -> TestBounds { + //(Bound::Excluded(x), Bound::Unbounded) + //} + fn ii(x1: u8, x2: u8) -> TestBounds { + (Bound::Included(x1), Bound::Included(x2)) + } + fn ie(x1: u8, x2: u8) -> TestBounds { + (Bound::Included(x1), Bound::Excluded(x2)) + } + fn ei(x1: u8, x2: u8) -> TestBounds { + (Bound::Excluded(x1), Bound::Included(x2)) + } + fn ee(x1: u8, x2: u8) -> TestBounds { + (Bound::Excluded(x1), Bound::Excluded(x2)) + } + fn u() -> Bound { + Bound::Unbounded + } +} diff --git a/todo.md b/todo.md index f4ff394..d5f1d85 100644 --- a/todo.md +++ b/todo.md @@ -9,6 +9,8 @@ - make an expand function to go RangeBounds -> (Bound, Bound) rather than doing it manually everywhere +- put basic() back in place and special() + # Documentation - replace `RangeBounds` with `K` where applicatble in docs