working overlaps test generation and nice display
This commit is contained in:
@@ -7,5 +7,4 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
either = "1.8.0"
|
||||
pretty_trace = "0.5.23"
|
||||
color-backtrace = "0.5.1"
|
||||
|
||||
+115
-16
@@ -5,6 +5,9 @@ pub enum StartBound<T> {
|
||||
Included(T),
|
||||
Excluded(T),
|
||||
Unbounded,
|
||||
//a workaround type used only for allowing bounded-unbounded range searches
|
||||
//in overlapping()
|
||||
ReverseUnbounded,
|
||||
}
|
||||
|
||||
impl<T> StartBound<T> {
|
||||
@@ -12,7 +15,7 @@ impl<T> StartBound<T> {
|
||||
match self {
|
||||
StartBound::Included(inner) => Some(inner),
|
||||
StartBound::Excluded(inner) => Some(inner),
|
||||
StartBound::Unbounded => None,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +31,18 @@ impl<T> StartBound<T> {
|
||||
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<T> {
|
||||
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<T> {
|
||||
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<T> Eq for StartBound<T> where T: PartialEq {}
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl<T> PartialOrd for StartBound<T>
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
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<T>(
|
||||
left: &T,
|
||||
right: &T,
|
||||
priority: bool,
|
||||
) -> Option<Ordering>
|
||||
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<T> Ord for StartBound<T>
|
||||
where
|
||||
T: PartialOrd,
|
||||
@@ -89,6 +148,7 @@ impl<T> From<EndBound<T>> for StartBound<T> {
|
||||
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<T> From<StartBound<T>> for Bound<T> {
|
||||
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<T> {
|
||||
Included(T),
|
||||
Excluded(T),
|
||||
Unbounded,
|
||||
//a workaround type used only for allowing bounded-unbounded range searches
|
||||
//in overlapping()
|
||||
ReverseUnbounded,
|
||||
}
|
||||
|
||||
impl<T> EndBound<T> {
|
||||
@@ -122,7 +186,7 @@ impl<T> EndBound<T> {
|
||||
match self {
|
||||
EndBound::Included(inner) => Some(inner),
|
||||
EndBound::Excluded(inner) => Some(inner),
|
||||
EndBound::Unbounded => None,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +202,18 @@ impl<T> EndBound<T> {
|
||||
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<T> {
|
||||
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<T> Eq for EndBound<T> where T: PartialEq {}
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl<T> PartialOrd for EndBound<T>
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
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<T> From<StartBound<T>> for EndBound<T> {
|
||||
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<T> From<EndBound<T>> for Bound<T> {
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,19 +10,40 @@ static NICE_NUMBERS: &'static [u8] = &[2, 4, 6, 8, 10];
|
||||
type TestBounds = (Bound<u8>, Bound<u8>);
|
||||
|
||||
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<u8, TestBounds>,
|
||||
overlap_range: TestBounds,
|
||||
}
|
||||
struct OverlappingTestCaseWithAnswer {
|
||||
test_case: OverlappingTestCase,
|
||||
answer: Vec<TestBounds>,
|
||||
}
|
||||
|
||||
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<u8>) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
struct OverlapTestCaseWithAnswer {
|
||||
test_case: OverlapTestCase,
|
||||
answer: Vec<TestBounds>,
|
||||
fn generate_overlaps_test_cases() -> Vec<OverlapsTestCase> {
|
||||
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<OverlapTestCase> {
|
||||
fn generate_overlapping_test_cases() -> Vec<OverlappingTestCase> {
|
||||
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<OverlapTestCase> {
|
||||
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<OverlapTestCase> {
|
||||
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<TestBounds> {
|
||||
}
|
||||
}
|
||||
//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<u8>) -> Option<&u8> {
|
||||
|
||||
fn all_finite_bounded() -> Vec<Bound<u8>> {
|
||||
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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user