diff --git a/range_bounds_set/Cargo.toml b/range_bounds_set/Cargo.toml index 6542f63..e46feba 100644 --- a/range_bounds_set/Cargo.toml +++ b/range_bounds_set/Cargo.toml @@ -7,5 +7,4 @@ edition = "2021" [dependencies] either = "1.8.0" -pretty_trace = "0.5.23" color-backtrace = "0.5.1" diff --git a/range_bounds_set/src/bounds.rs b/range_bounds_set/src/bounds.rs index c704241..c257db2 100644 --- a/range_bounds_set/src/bounds.rs +++ b/range_bounds_set/src/bounds.rs @@ -5,6 +5,9 @@ pub enum StartBound { Included(T), Excluded(T), Unbounded, + //a workaround type used only for allowing bounded-unbounded range searches + //in overlapping() + ReverseUnbounded, } impl StartBound { @@ -12,7 +15,7 @@ impl StartBound { match self { StartBound::Included(inner) => Some(inner), StartBound::Excluded(inner) => Some(inner), - StartBound::Unbounded => None, + _ => None, } } @@ -28,6 +31,18 @@ impl StartBound { StartBound::Included(point) => StartBound::Included(point), StartBound::Excluded(point) => StartBound::Excluded(point), StartBound::Unbounded => StartBound::Unbounded, + StartBound::ReverseUnbounded => StartBound::ReverseUnbounded, + } + } + + //when using this as an end value in a range search + pub fn as_end_value(self) -> StartBound { + match self { + StartBound::Included(point) => StartBound::Included(point), + StartBound::Excluded(point) => StartBound::Excluded(point), + //flip Unbounded with ReverseUnbounded + StartBound::Unbounded => StartBound::ReverseUnbounded, + StartBound::ReverseUnbounded => panic!("unsuitable operation"), } } } @@ -37,9 +52,14 @@ where { pub fn cloned(&self) -> StartBound { match self { - StartBound::Included(point) => StartBound::Included((*point).clone()), - StartBound::Excluded(point) => StartBound::Excluded((*point).clone()), + StartBound::Included(point) => { + StartBound::Included((*point).clone()) + } + StartBound::Excluded(point) => { + StartBound::Excluded((*point).clone()) + } StartBound::Unbounded => StartBound::Unbounded, + StartBound::ReverseUnbounded => StartBound::ReverseUnbounded, } } } @@ -59,21 +79,60 @@ where impl Eq for StartBound where T: PartialEq {} +#[rustfmt::skip] impl PartialOrd for StartBound where T: PartialOrd, { fn partial_cmp(&self, other: &Self) -> Option { - match (self.inner(), other.inner()) { - //todo fix meh - (Some(start1), Some(start2)) => start1.partial_cmp(start2), - (None, Some(_)) => Some(Ordering::Less), - (Some(_), None) => Some(Ordering::Greater), - (None, None) => Some(Ordering::Equal), + match (self, other) { + (StartBound::Included(start1), StartBound::Included(start2)) => start1.partial_cmp(start2), + (StartBound::Excluded(start1), StartBound::Excluded(start2)) => start1.partial_cmp(start2), + + (StartBound::Included(start1), StartBound::Excluded(start2)) => partial_cmp_with_priority(start1, start2, true), + (StartBound::Excluded(start1), StartBound::Included(start2)) => partial_cmp_with_priority(start1, start2, false), + + (StartBound::Included(_), StartBound::Unbounded) => Some(Ordering::Greater), + (StartBound::Excluded(_), StartBound::Unbounded) => Some(Ordering::Greater), + (StartBound::Unbounded, StartBound::Included(_)) => Some(Ordering::Less), + (StartBound::Unbounded, StartBound::Excluded(_)) => Some(Ordering::Less), + + (StartBound::Included(_), StartBound::ReverseUnbounded) => Some(Ordering::Less), + (StartBound::Excluded(_), StartBound::ReverseUnbounded) => Some(Ordering::Less), + (StartBound::ReverseUnbounded, StartBound::Included(_)) => Some(Ordering::Greater), + (StartBound::ReverseUnbounded, StartBound::Excluded(_)) => Some(Ordering::Greater), + + + (StartBound::Unbounded, StartBound::Unbounded) => Some(Ordering::Equal), + (StartBound::ReverseUnbounded, StartBound::ReverseUnbounded) => Some(Ordering::Equal), + + (StartBound::Unbounded, StartBound::ReverseUnbounded) => Some(Ordering::Less), + (StartBound::ReverseUnbounded, StartBound::Unbounded) => Some(Ordering::Greater), } } } +//if they are equal say the item with priority is larger +//where false means left has priority and true means right +fn partial_cmp_with_priority( + left: &T, + right: &T, + priority: bool, +) -> Option +where + T: PartialOrd, +{ + let result = left.partial_cmp(right)?; + + Some(match result { + Ordering::Equal => match priority { + false => Ordering::Greater, + true => Ordering::Less, + }, + x => x, + }) +} + impl Ord for StartBound where T: PartialOrd, @@ -89,6 +148,7 @@ impl From> for StartBound { EndBound::Included(point) => StartBound::Included(point), EndBound::Excluded(point) => StartBound::Excluded(point), EndBound::Unbounded => StartBound::Unbounded, + EndBound::ReverseUnbounded => panic!("unsuitable operation"), } } } @@ -107,6 +167,7 @@ impl From> for Bound { StartBound::Included(point) => Bound::Included(point), StartBound::Excluded(point) => Bound::Excluded(point), StartBound::Unbounded => Bound::Unbounded, + StartBound::ReverseUnbounded => panic!("unsuitable operation"), } } } @@ -115,6 +176,9 @@ pub enum EndBound { Included(T), Excluded(T), Unbounded, + //a workaround type used only for allowing bounded-unbounded range searches + //in overlapping() + ReverseUnbounded, } impl EndBound { @@ -122,7 +186,7 @@ impl EndBound { match self { EndBound::Included(inner) => Some(inner), EndBound::Excluded(inner) => Some(inner), - EndBound::Unbounded => None, + _ => None, } } @@ -138,6 +202,18 @@ impl EndBound { EndBound::Included(point) => EndBound::Included(point), EndBound::Excluded(point) => EndBound::Excluded(point), EndBound::Unbounded => EndBound::Unbounded, + EndBound::ReverseUnbounded => EndBound::ReverseUnbounded, + } + } + + //when using this as an start value in a range search + pub fn as_start_value(self) -> EndBound { + match self { + EndBound::Included(point) => EndBound::Included(point), + EndBound::Excluded(point) => EndBound::Excluded(point), + //flip Unbounded with ReverseUnbounded + EndBound::Unbounded => EndBound::ReverseUnbounded, + EndBound::ReverseUnbounded => EndBound::ReverseUnbounded, } } } @@ -150,6 +226,7 @@ where EndBound::Included(point) => EndBound::Included((*point).clone()), EndBound::Excluded(point) => EndBound::Excluded((*point).clone()), EndBound::Unbounded => EndBound::Unbounded, + EndBound::ReverseUnbounded => EndBound::ReverseUnbounded, } } } @@ -169,17 +246,35 @@ where impl Eq for EndBound where T: PartialEq {} +#[rustfmt::skip] impl PartialOrd for EndBound where T: PartialOrd, { fn partial_cmp(&self, other: &Self) -> Option { - match (self.inner(), other.inner()) { - //todo fix meh - (Some(end1), Some(end2)) => end1.partial_cmp(end2), - (None, Some(_)) => Some(Ordering::Greater), - (Some(_), None) => Some(Ordering::Less), - (None, None) => Some(Ordering::Equal), + match (self, other) { + (EndBound::Included(end1), EndBound::Included(end2)) => end1.partial_cmp(end2), + (EndBound::Excluded(end1), EndBound::Excluded(end2)) => end1.partial_cmp(end2), + + (EndBound::Included(end1), EndBound::Excluded(end2)) => partial_cmp_with_priority(end1, end2, false), + (EndBound::Excluded(end1), EndBound::Included(end2)) => partial_cmp_with_priority(end1, end2, true), + + (EndBound::Included(_), EndBound::Unbounded) => Some(Ordering::Less), + (EndBound::Excluded(_), EndBound::Unbounded) => Some(Ordering::Less), + (EndBound::Unbounded, EndBound::Included(_)) => Some(Ordering::Greater), + (EndBound::Unbounded, EndBound::Excluded(_)) => Some(Ordering::Greater), + + (EndBound::Included(_), EndBound::ReverseUnbounded) => Some(Ordering::Greater), + (EndBound::Excluded(_), EndBound::ReverseUnbounded) => Some(Ordering::Greater), + (EndBound::ReverseUnbounded, EndBound::Included(_)) => Some(Ordering::Less), + (EndBound::ReverseUnbounded, EndBound::Excluded(_)) => Some(Ordering::Less), + + + (EndBound::Unbounded, EndBound::Unbounded) => Some(Ordering::Equal), + (EndBound::ReverseUnbounded, EndBound::ReverseUnbounded) => Some(Ordering::Equal), + + (EndBound::Unbounded, EndBound::ReverseUnbounded) => Some(Ordering::Greater), + (EndBound::ReverseUnbounded, EndBound::Unbounded) => Some(Ordering::Less), } } } @@ -199,6 +294,7 @@ impl From> for EndBound { StartBound::Included(point) => EndBound::Included(point), StartBound::Excluded(point) => EndBound::Excluded(point), StartBound::Unbounded => EndBound::Unbounded, + StartBound::ReverseUnbounded => EndBound::ReverseUnbounded, } } } @@ -217,6 +313,9 @@ impl From> for Bound { EndBound::Included(point) => Bound::Included(point), EndBound::Excluded(point) => Bound::Excluded(point), EndBound::Unbounded => Bound::Unbounded, + EndBound::ReverseUnbounded => { + panic!("You can't convert SpecialErg to a Bound") + } } } } diff --git a/range_bounds_set/src/main.rs b/range_bounds_set/src/main.rs index d89ae04..a615b6b 100644 --- a/range_bounds_set/src/main.rs +++ b/range_bounds_set/src/main.rs @@ -10,19 +10,40 @@ static NICE_NUMBERS: &'static [u8] = &[2, 4, 6, 8, 10]; type TestBounds = (Bound, Bound); fn main() { - //pretty_trace::PrettyTrace::new().on(); - color_backtrace::install(); - for test_case in generate_overlap_test_cases() { + color_backtrace::install(); + for test_case in generate_overlaps_test_cases() { println!("{}", test_case); } } -struct OverlapTestCase { +struct OverlapsTestCase { + range_bounds1: TestBounds, + range_bounds2: TestBounds, +} +struct OverlapsTestCaseWithAnswer { + test_case: OverlapsTestCase, + answer: bool, +} + +struct OverlappingTestCase { range_bounds_set: RangeBoundsSet, overlap_range: TestBounds, } +struct OverlappingTestCaseWithAnswer { + test_case: OverlappingTestCase, + answer: Vec, +} -impl Display for OverlapTestCase { +impl Display for OverlapsTestCase { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "=== 1 2 3 4 5 6 7 8 9 10 ===")?; + writeln!(f, "{}", display_test_bounds(&self.range_bounds1))?; + writeln!(f, "{}", display_test_bounds(&self.range_bounds2))?; + Ok(()) + } +} + +impl Display for OverlappingTestCase { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let range_bounds_iterator = self.range_bounds_set.iter(); @@ -42,13 +63,31 @@ impl Display for OverlapTestCase { } fn display_test_bounds(test_bounds: &TestBounds) -> String { - let first_symbol_position = inner(&test_bounds.0).unwrap_or(&0); - let second_symbol_position = inner(&test_bounds.1).unwrap_or(&16); + let first_scale = 3; + let first_offset = 2; + + let second_scale = 3; + let second_offset = 1; + + let first_unbounded_position = 0; + let second_unbounded_position = 36; + + let first_symbol_position = inner(&test_bounds.0) + .map(|x| x * first_scale + first_offset) + .unwrap_or(first_unbounded_position); + + let second_symbol_position = inner(&test_bounds.1) + .map(|x| x * second_scale + second_offset) + .unwrap_or(second_unbounded_position); + format!( "{}{}{}{}", - " ".repeat(*first_symbol_position as usize), + " ".repeat(first_symbol_position as usize), bound_symbol(&test_bounds.0), - "-".repeat(*second_symbol_position as usize), + "-".repeat( + (second_symbol_position.saturating_sub(first_symbol_position)) + as usize + ), bound_symbol(&test_bounds.1) ) } @@ -61,16 +100,26 @@ fn bound_symbol(bound: &Bound) -> String { } } -struct OverlapTestCaseWithAnswer { - test_case: OverlapTestCase, - answer: Vec, +fn generate_overlaps_test_cases() -> Vec { + let mut output = Vec::new(); + + for overlap_range in all_valid_test_bounds() { + for (range_bounds1, range_bounds2) in all_valid_test_bounds_pairs() { + output.push(OverlapsTestCase { + range_bounds1, + range_bounds2, + }) + } + } + + return output; } -fn generate_overlap_test_cases() -> Vec { +fn generate_overlapping_test_cases() -> Vec { let mut output = Vec::new(); //case zero for overlap_range in all_valid_test_bounds() { - output.push(OverlapTestCase { + output.push(OverlappingTestCase { range_bounds_set: RangeBoundsSet::new(), overlap_range, }) @@ -81,7 +130,7 @@ fn generate_overlap_test_cases() -> Vec { for inside_range in all_valid_test_bounds() { let mut range_bounds_set = range_bounds_set::RangeBoundsSet::new(); range_bounds_set.insert(inside_range).unwrap(); - output.push(OverlapTestCase { + output.push(OverlappingTestCase { range_bounds_set, overlap_range, }) @@ -93,9 +142,8 @@ fn generate_overlap_test_cases() -> Vec { for (test_bounds1, test_bounds2) in all_valid_test_bounds_pairs() { let mut range_bounds_set = range_bounds_set::RangeBoundsSet::new(); range_bounds_set.insert(test_bounds1).unwrap(); - dbg!(test_bounds2); range_bounds_set.insert(test_bounds2).unwrap(); - output.push(OverlapTestCase { + output.push(OverlappingTestCase { range_bounds_set, overlap_range, }) @@ -135,15 +183,15 @@ fn all_valid_test_bounds() -> Vec { } } //bounded-unbounded - for start_bound in all_finite_bounded() { - output.push((start_bound, Bound::Unbounded)); - } + for start_bound in all_finite_bounded() { + output.push((start_bound, Bound::Unbounded)); + } //unbounded-bounded - for end_bound in all_finite_bounded() { - output.push((Bound::Unbounded, end_bound)); - } - //bounded-bounded - output.push((Bound::Unbounded, Bound::Unbounded)); + for end_bound in all_finite_bounded() { + output.push((Bound::Unbounded, end_bound)); + } + //unbounded-unbounded + output.push((Bound::Unbounded, Bound::Unbounded)); output.retain(is_valid_test_bounds); return output; @@ -173,9 +221,9 @@ fn inner(bound: &Bound) -> Option<&u8> { fn all_finite_bounded() -> Vec> { let mut output = Vec::new(); - for i in 0..5 { + for i in NICE_NUMBERS { for k in 0..=1 { - output.push(finite_bound(i, k == 0)); + output.push(finite_bound(*i, k == 0)); } } return output; diff --git a/range_bounds_set/src/range_bounds.rs b/range_bounds_set/src/range_bounds.rs index 49d9081..11b0ea1 100644 --- a/range_bounds_set/src/range_bounds.rs +++ b/range_bounds_set/src/range_bounds.rs @@ -16,10 +16,12 @@ where StartBound::Included(start) => start <= item, StartBound::Excluded(start) => start < item, StartBound::Unbounded => true, + StartBound::ReverseUnbounded => panic!("unsuitable operation"), }) && (match self.end_bound() { EndBound::Included(end) => item <= end, EndBound::Excluded(end) => item < end, EndBound::Unbounded => true, + EndBound::ReverseUnbounded => panic!("unsuitable operation"), }) } fn overlaps(&self, other: &Self) -> bool { diff --git a/range_bounds_set/src/range_bounds_map.rs b/range_bounds_set/src/range_bounds_map.rs index 1b8c793..609ec25 100644 --- a/range_bounds_set/src/range_bounds_map.rs +++ b/range_bounds_set/src/range_bounds_map.rs @@ -30,7 +30,7 @@ where return Err(()); } - //todo panic on invalid inputs + //todo panic on invalid inputs self.starts .insert(range_bounds.start_bound().cloned(), (range_bounds, value)); @@ -53,9 +53,10 @@ where let start_range_bounds = ( //Included is lossless regarding meta-bounds searches Bound::Included(search_range_bounds.start_bound().cloned()), - Bound::Included(StartBound::from( - search_range_bounds.end_bound().cloned(), - )), + Bound::Included( + StartBound::from(search_range_bounds.end_bound().cloned()) + .as_end_value(), + ), ); //this range will hold all the ranges we want except possibly //the first RangeBound in the range @@ -67,6 +68,7 @@ where //coverded in the previous step self.starts.next_below_upper_bound(Bound::Excluded( //optimisation fix this without cloning + //todo should probably use .as_end_value() &search_range_bounds.start_bound().cloned(), )) { if possible_missing_range_bounds.overlaps(&search_range_bounds) {