mod payload_range;
mod simple_range;
pub use payload_range::PayloadRange;
pub use simple_range::SimpleRange;
#[derive(Debug, PartialEq, Eq)]
pub enum RangeCompareResult {
LessThanNoOverlap,
OverlapLower,
Contained,
Contains,
Equal,
OverlapUpper,
GreaterNoOverlap,
}
pub fn range_compare<R1: Range, R2: Range>(r1: &R1, r2: &R2) -> RangeCompareResult {
let self_min_incl = r1.min_incl();
let self_max_incl = r1.max_incl();
let other_min_incl = r2.min_incl();
let other_max_incl = r2.max_incl();
if self_min_incl == other_min_incl && self_max_incl == other_max_incl {
return RangeCompareResult::Equal;
}
if self_min_incl < other_min_incl {
if self_max_incl < other_min_incl {
return RangeCompareResult::LessThanNoOverlap;
}
if other_min_incl <= self_max_incl && self_max_incl < other_max_incl {
return RangeCompareResult::OverlapLower;
}
return RangeCompareResult::Contains;
}
if self_min_incl == other_min_incl {
if self_max_incl < other_max_incl {
return RangeCompareResult::Contained;
}
if self_max_incl == other_max_incl {
return RangeCompareResult::Equal;
}
return RangeCompareResult::Contains;
}
if self_min_incl > other_min_incl {
if other_max_incl < self_min_incl {
return RangeCompareResult::GreaterNoOverlap;
}
if other_max_incl >= self_min_incl && other_max_incl < self_max_incl {
return RangeCompareResult::OverlapUpper;
}
return RangeCompareResult::Contained;
}
unreachable!("Non-exhaustive comparison!")
}
pub trait Range {
fn min(&self) -> usize;
fn max(&self) -> usize;
fn min_incl(&self) -> usize;
fn max_incl(&self) -> usize;
fn includes(&self, value: usize) -> bool;
}
#[derive(Debug, PartialEq, Eq)]
pub enum BadRange {
MinGreaterThanMax,
InclusiveMinOverflows,
InclusiveMaxUnderflows,
}
#[cfg(test)]
mod tests {
use super::*;
use quickcheck::{quickcheck, TestResult};
#[test]
fn test_range_compare_eq() {
fn prop_comp_eq(r1: SimpleRange, r2: SimpleRange) -> TestResult {
match range_compare(&r1, &r2) {
RangeCompareResult::Equal => {
let valid = r1.min_incl() == r2.min_incl() && r1.max_incl() == r2.max_incl();
TestResult::from_bool(valid)
}
_ => TestResult::discard(),
}
}
fn prop_range_eq_self(r: SimpleRange) -> bool {
range_compare(&r, &r) == RangeCompareResult::Equal
}
quickcheck(prop_range_eq_self as fn(SimpleRange) -> bool);
quickcheck(prop_comp_eq as fn(SimpleRange, SimpleRange) -> TestResult);
}
#[quickcheck]
fn test_range_less_than_no_overlap(r1: SimpleRange, r2: SimpleRange) -> TestResult {
match range_compare(&r1, &r2) {
RangeCompareResult::LessThanNoOverlap => {
TestResult::from_bool(r1.max_incl() < r2.min_incl())
}
_ => TestResult::discard(),
}
}
#[quickcheck]
fn test_range_compare_overlap_lower(r1: SimpleRange, r2: SimpleRange) -> TestResult {
match range_compare(&r1, &r2) {
RangeCompareResult::OverlapLower => TestResult::from_bool(
r1.min_incl() < r2.min_incl()
&& r1.max_incl() >= r2.min_incl()
&& r1.min_incl() <= r2.max_incl(),
),
_ => TestResult::discard(),
}
}
#[quickcheck]
fn test_range_compare_contained(r1: SimpleRange, r2: SimpleRange) -> TestResult {
match range_compare(&r1, &r2) {
RangeCompareResult::Contained => TestResult::from_bool(
!(r1.min_incl() == r2.min_incl() && r1.max_incl() == r2.max_incl())
&& r1.min_incl() >= r2.min_incl()
&& r1.max_incl() <= r2.max_incl(),
),
_ => TestResult::discard(),
}
}
#[quickcheck]
fn test_range_compare_contains(r1: SimpleRange, r2: SimpleRange) -> TestResult {
match range_compare(&r1, &r2) {
RangeCompareResult::Contains => TestResult::from_bool(
!(r1.min_incl() == r2.min_incl() && r1.max_incl() == r2.max_incl())
&& r1.min_incl() <= r2.min_incl()
&& r1.max_incl() >= r2.max_incl(),
),
_ => TestResult::discard(),
}
}
#[quickcheck]
fn test_range_compare_contains_inverse_is_contained(
r1: SimpleRange,
r2: SimpleRange,
) -> TestResult {
match range_compare(&r1, &r2) {
RangeCompareResult::Contains => {
TestResult::from_bool(range_compare(&r2, &r1) == RangeCompareResult::Contained)
}
_ => TestResult::discard(),
}
}
#[quickcheck]
fn test_range_compare_contained_inverse_is_contains(
r1: SimpleRange,
r2: SimpleRange,
) -> TestResult {
match range_compare(&r1, &r2) {
RangeCompareResult::Contained => {
TestResult::from_bool(range_compare(&r2, &r1) == RangeCompareResult::Contains)
}
_ => TestResult::discard(),
}
}
#[quickcheck]
fn test_range_compare_overlap_upper(r1: SimpleRange, r2: SimpleRange) -> TestResult {
match range_compare(&r1, &r2) {
RangeCompareResult::OverlapUpper => TestResult::from_bool(
(r1.min_incl() >= r2.min_incl() && r1.min_incl() <= r2.max_incl())
&& r1.max_incl() > r2.max_incl(),
),
_ => TestResult::discard(),
}
}
#[quickcheck]
fn test_range_compare_greater_no_overlap(r1: SimpleRange, r2: SimpleRange) -> TestResult {
match range_compare(&r1, &r2) {
RangeCompareResult::GreaterNoOverlap => {
TestResult::from_bool(r1.min_incl() > r2.max_incl())
}
_ => TestResult::discard(),
}
}
}