use std::borrow::Borrow;
use std::ops::{Bound, RangeBounds};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RangeOrdering {
Below,
Inside,
Above,
}
pub trait BorrowRange<T: ?Sized, R>: Borrow<R> {}
impl<T, R: RangeBounds<T>> BorrowRange<T, R> for R {}
impl<T, R: RangeBounds<T>> BorrowRange<T, R> for &R {}
pub trait RangeComparable {
fn range_cmp<R: RangeBounds<Self>, B: BorrowRange<Self, R>>(&self, range: B) -> RangeOrdering;
}
impl<T: Ord> RangeComparable for T {
fn range_cmp<R: RangeBounds<Self>, B: BorrowRange<Self, R>>(&self, range: B) -> RangeOrdering {
let range = range.borrow();
if range.contains(self) {
return RangeOrdering::Inside;
}
if match range.start_bound() {
Bound::Included(key) => self < key,
Bound::Excluded(key) => self <= key,
_ => false,
} {
return RangeOrdering::Below;
}
if match range.end_bound() {
Bound::Included(key) => self > key,
Bound::Excluded(key) => self >= key,
_ => false,
} {
return RangeOrdering::Above;
}
unreachable!()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn range_full() {
assert_eq!(1.range_cmp(..), RangeOrdering::Inside);
}
#[test]
fn range_from() {
assert_eq!(1.range_cmp(1..), RangeOrdering::Inside);
assert_eq!(1.range_cmp(&1..), RangeOrdering::Inside);
assert_eq!(1.range_cmp(2..), RangeOrdering::Below);
assert_eq!(1.range_cmp(&2..), RangeOrdering::Below);
}
#[test]
fn range_to() {
assert_eq!(1.range_cmp(..1), RangeOrdering::Above);
assert_eq!(1.range_cmp(..&1), RangeOrdering::Above);
assert_eq!(1.range_cmp(..2), RangeOrdering::Inside);
assert_eq!(1.range_cmp(..&2), RangeOrdering::Inside);
}
#[test]
fn range() {
assert_eq!(1.range_cmp(0..1), RangeOrdering::Above);
assert_eq!(1.range_cmp(&0..&1), RangeOrdering::Above);
assert_eq!(1.range_cmp(1..2), RangeOrdering::Inside);
assert_eq!(1.range_cmp(&1..&2), RangeOrdering::Inside);
assert_eq!(1.range_cmp(2..3), RangeOrdering::Below);
assert_eq!(1.range_cmp(&2..&3), RangeOrdering::Below);
}
#[test]
fn range_inclusive() {
assert_eq!(1.range_cmp(0..=0), RangeOrdering::Above);
assert_eq!(1.range_cmp(&0..=&0), RangeOrdering::Above);
assert_eq!(1.range_cmp(1..=1), RangeOrdering::Inside);
assert_eq!(1.range_cmp(&1..=&1), RangeOrdering::Inside);
assert_eq!(1.range_cmp(2..=2), RangeOrdering::Below);
assert_eq!(1.range_cmp(&2..=&2), RangeOrdering::Below);
}
#[test]
fn range_to_inclusive() {
assert_eq!(1.range_cmp(..=0), RangeOrdering::Above);
assert_eq!(1.range_cmp(..=&0), RangeOrdering::Above);
assert_eq!(1.range_cmp(..=1), RangeOrdering::Inside);
assert_eq!(1.range_cmp(..=&1), RangeOrdering::Inside);
}
#[test]
fn bounds_full() {
let bounds: (Bound<i32>, Bound<i32>) = (Bound::Unbounded, Bound::Unbounded);
assert_eq!(1.range_cmp(bounds), RangeOrdering::Inside);
}
#[test]
fn bounds_from() {
let bounds = (Bound::Included(1), Bound::Unbounded);
assert_eq!(1.range_cmp(bounds), RangeOrdering::Inside);
let bounds = (Bound::Included(&1), Bound::Unbounded);
assert_eq!(1.range_cmp(bounds), RangeOrdering::Inside);
let bounds = (Bound::Included(2), Bound::Unbounded);
assert_eq!(1.range_cmp(bounds), RangeOrdering::Below);
let bounds = (Bound::Included(&2), Bound::Unbounded);
assert_eq!(1.range_cmp(bounds), RangeOrdering::Below);
}
#[test]
fn bounds_to() {
let bounds = (Bound::Unbounded, Bound::Excluded(1));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Above);
let bounds = (Bound::Unbounded, Bound::Excluded(&1));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Above);
let bounds = (Bound::Unbounded, Bound::Excluded(2));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Inside);
let bounds = (Bound::Unbounded, Bound::Excluded(&2));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Inside);
}
#[test]
fn bounds() {
let bounds = (Bound::Included(0), Bound::Excluded(1));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Above);
let bounds = (Bound::Included(&0), Bound::Excluded(&1));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Above);
let bounds = (Bound::Included(1), Bound::Excluded(2));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Inside);
let bounds = (Bound::Included(&1), Bound::Excluded(&2));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Inside);
let bounds = (Bound::Included(2), Bound::Excluded(3));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Below);
let bounds = (Bound::Included(&2), Bound::Excluded(&3));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Below);
}
#[test]
fn bounds_inclusive() {
let bounds = (Bound::Included(0), Bound::Included(0));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Above);
let bounds = (Bound::Included(&0), Bound::Included(&0));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Above);
let bounds = (Bound::Included(1), Bound::Included(1));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Inside);
let bounds = (Bound::Included(&1), Bound::Included(&1));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Inside);
let bounds = (Bound::Included(2), Bound::Included(2));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Below);
let bounds = (Bound::Included(&2), Bound::Included(&2));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Below);
}
#[test]
fn bounds_to_inclusive() {
let bounds = (Bound::Unbounded, Bound::Included(0));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Above);
let bounds = (Bound::Unbounded, Bound::Included(&0));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Above);
let bounds = (Bound::Unbounded, Bound::Included(1));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Inside);
let bounds = (Bound::Unbounded, Bound::Included(&1));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Inside);
}
#[test]
fn bounds_exclusive_inclusive() {
let bounds: (Bound<i32>, Bound<i32>) = (Bound::Excluded(-1), Bound::Included(0));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Above);
let bounds: (Bound<&i32>, Bound<&i32>) = (Bound::Excluded(&-1), Bound::Included(&0));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Above);
let bounds: (Bound<i32>, Bound<i32>) = (Bound::Excluded(0), Bound::Included(1));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Inside);
let bounds: (Bound<&i32>, Bound<&i32>) = (Bound::Excluded(&0), Bound::Included(&1));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Inside);
let bounds: (Bound<i32>, Bound<i32>) = (Bound::Excluded(1), Bound::Included(2));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Below);
let bounds: (Bound<&i32>, Bound<&i32>) = (Bound::Excluded(&1), Bound::Included(&2));
assert_eq!(1.range_cmp(bounds), RangeOrdering::Below);
}
#[test]
fn bounds_as_reference() {
let bounds = 0..2;
assert_eq!(1.range_cmp(&bounds), RangeOrdering::Inside);
assert_eq!(1.range_cmp(bounds), RangeOrdering::Inside);
}
#[test]
fn empty_ranges() {
assert_eq!(0.range_cmp(0..0), RangeOrdering::Above);
assert_eq!(0.range_cmp(&0..&0), RangeOrdering::Above);
assert_eq!(0.range_cmp(..0u32), RangeOrdering::Above);
assert_eq!(0.range_cmp(..&0u32), RangeOrdering::Above);
assert_eq!(30.range_cmp(45..35), RangeOrdering::Below);
assert_eq!(30.range_cmp(&45..&35), RangeOrdering::Below);
assert_eq!(30.range_cmp(25..15), RangeOrdering::Above);
assert_eq!(30.range_cmp(&25..&15), RangeOrdering::Above);
}
}