working overlaps test generation and nice display

This commit is contained in:
ripytide
2022-11-25 01:50:45 +00:00
parent 0aec96e84d
commit 77de22d57f
5 changed files with 198 additions and 48 deletions
-1
View File
@@ -7,5 +7,4 @@ edition = "2021"
[dependencies]
either = "1.8.0"
pretty_trace = "0.5.23"
color-backtrace = "0.5.1"
+115 -16
View File
@@ -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")
}
}
}
}
+75 -27
View File
@@ -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;
+2
View File
@@ -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 {
+6 -4
View File
@@ -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) {