use core::{cmp::Ordering, fmt::Display, ops::Range};
#[derive(Debug, PartialEq, Eq, Clone)]
pub(crate) struct Interval<T>(Range<T>);
impl<T> Interval<T> {
pub(crate) fn start(&self) -> &T {
&self.0.start
}
pub(crate) fn end(&self) -> &T {
&self.0.end
}
pub(crate) fn as_range(&self) -> &Range<T> {
&self.0
}
pub(crate) fn into_range(self) -> Range<T> {
self.0
}
}
impl<T> Display for Interval<T>
where
T: Display,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}..{}", self.start(), self.end())
}
}
impl<T> Interval<T>
where
T: Ord,
{
pub(crate) fn overlaps(&self, other: &Range<T>) -> bool {
other.end > self.0.start && other.start < self.0.end
}
pub(crate) fn precedes(&self, other: &Range<T>) -> bool {
self.0.end < other.start && self.0.start < other.start
}
pub(crate) fn meets(&self, other: &Range<T>) -> bool {
self.0.end == other.start
}
pub(crate) fn met_by(&self, other: &Range<T>) -> bool {
other.end == self.0.start
}
pub(crate) fn preceded_by(&self, other: &Range<T>) -> bool {
other.start < self.0.start && other.end < self.0.start
}
pub(crate) fn starts(&self, other: &Range<T>) -> bool {
other.start == self.0.start
}
pub(crate) fn finishes(&self, other: &Range<T>) -> bool {
other.end == self.0.end
}
pub(crate) fn during(&self, other: &Range<T>) -> bool {
self.0.start >= other.start && self.0.end <= other.end
}
pub(crate) fn contains(&self, other: &Range<T>) -> bool {
self.0.start <= other.start && self.0.end >= other.end
}
}
impl<T> PartialOrd for Interval<T>
where
T: Ord + Eq,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T> Ord for Interval<T>
where
T: Ord + Eq,
{
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(&other.0).unwrap()
}
}
impl<T> PartialOrd<Range<T>> for Interval<T>
where
T: Ord + Eq,
{
fn partial_cmp(&self, other: &Range<T>) -> Option<Ordering> {
Some(match self.0.start.cmp(&other.start) {
Ordering::Equal => self.0.end.cmp(&other.end),
v => v,
})
}
}
impl<T> PartialEq<Range<T>> for Interval<T>
where
T: PartialEq,
{
fn eq(&self, other: &Range<T>) -> bool {
self.0.start == other.start && self.0.end == other.end
}
}
impl<T> From<Range<T>> for Interval<T> {
fn from(value: Range<T>) -> Self {
Self(value)
}
}
#[cfg(test)]
mod tests {
use crate::test_utils::arbitrary_range;
use super::*;
use proptest::prelude::*;
#[test]
fn test_overlaps() {
let a = 42..100;
assert!(Interval::from(42..100).overlaps(&a));
assert!(Interval::from(42..101).overlaps(&a));
assert!(Interval::from(41..100).overlaps(&a));
assert!(Interval::from(41..101).overlaps(&a));
assert!(Interval::from(42..43).overlaps(&a));
assert!(Interval::from(99..100).overlaps(&a));
assert!(Interval::from(50..51).overlaps(&a));
assert!(!Interval::from(41..42).overlaps(&a)); assert!(!Interval::from(100..101).overlaps(&a));
assert!(Interval::from(50..50).overlaps(&a));
assert!(!Interval::from(40..40).overlaps(&a));
assert!(!Interval::from(101..101).overlaps(&a));
}
#[test]
fn test_precedes() {
let a = 42..100;
assert!(Interval::from(1..2).precedes(&a));
assert!(Interval::from(40..41).precedes(&a));
assert!(!Interval::from(40..42).precedes(&a)); assert!(!Interval::from(40..43).precedes(&a)); assert!(!Interval::from(100..101).precedes(&a)); }
#[test]
fn test_preceded_by() {
let a = 42..100;
assert!(Interval::from(101..102).preceded_by(&a));
assert!(!Interval::from(100..420).preceded_by(&a)); assert!(!Interval::from(99..420).preceded_by(&a)); assert!(!Interval::from(40..41).preceded_by(&a)); }
#[test]
fn test_meets() {
let a = 42..100;
assert!(Interval::from(10..42).meets(&a));
assert!(!Interval::from(100..420).meets(&a));
assert!(!Interval::from(101..420).meets(&a));
assert!(!Interval::from(99..420).meets(&a));
assert!(!Interval::from(10..43).meets(&a));
assert!(!Interval::from(10..41).meets(&a));
}
#[test]
fn test_met_by() {
let a = 42..100;
assert!(Interval::from(100..420).met_by(&a));
assert!(!Interval::from(10..42).met_by(&a));
assert!(!Interval::from(101..420).met_by(&a));
assert!(!Interval::from(99..420).met_by(&a));
assert!(!Interval::from(10..43).met_by(&a));
assert!(!Interval::from(10..41).met_by(&a));
}
#[test]
fn test_starts() {
let a = 42..100;
assert!(Interval::from(42..100).starts(&a));
assert!(Interval::from(42..99).starts(&a));
assert!(Interval::from(42..101).starts(&a));
assert!(!Interval::from(41..100).starts(&a));
assert!(!Interval::from(41..99).starts(&a));
assert!(!Interval::from(41..101).starts(&a));
assert!(!Interval::from(43..100).starts(&a));
assert!(!Interval::from(43..99).starts(&a));
assert!(!Interval::from(43..101).starts(&a));
}
#[test]
fn test_finishes() {
let a = 42..100;
assert!(Interval::from(41..100).finishes(&a));
assert!(Interval::from(42..100).finishes(&a));
assert!(Interval::from(43..100).finishes(&a));
assert!(!Interval::from(41..99).finishes(&a));
assert!(!Interval::from(41..101).finishes(&a));
assert!(!Interval::from(43..99).finishes(&a));
assert!(!Interval::from(43..101).finishes(&a));
}
#[test]
fn test_during() {
let a = 42..100;
assert!(Interval::from(42..100).during(&a));
assert!(Interval::from(42..99).during(&a));
assert!(Interval::from(43..100).during(&a));
assert!(Interval::from(43..99).during(&a));
assert!(!Interval::from(42..101).during(&a));
assert!(!Interval::from(41..99).during(&a));
assert!(!Interval::from(41..101).during(&a));
assert!(!Interval::from(42..101).during(&a));
}
#[test]
fn test_contains() {
let a = 42..100;
assert!(Interval::from(42..100).contains(&a));
assert!(Interval::from(42..101).contains(&a));
assert!(Interval::from(41..100).contains(&a));
assert!(Interval::from(41..101).contains(&a));
assert!(!Interval::from(41..99).contains(&a));
assert!(!Interval::from(42..99).contains(&a));
assert!(!Interval::from(43..99).contains(&a));
assert!(!Interval::from(43..100).contains(&a));
assert!(!Interval::from(43..101).contains(&a));
}
proptest! {
#[test]
fn prop_range_eq(r in any::<Range<usize>>()) {
let interval = Interval::from(r.clone());
assert_eq!(r, *interval.as_range());
assert_eq!(r.start, interval.0.start);
assert_eq!(r.end, *interval.end());
assert_eq!(interval, r);
assert_eq!(interval.partial_cmp(&r).unwrap(), Ordering::Equal);
let other = Interval::from(r.clone());
assert_eq!(interval, other);
assert_eq!(interval.partial_cmp(&other).unwrap(), Ordering::Equal);
assert_eq!(interval.cmp(&other), Ordering::Equal);
}
#[test]
fn prop_range_ord(a in arbitrary_range(), b in arbitrary_range()) {
let interval = Interval::from(a.clone());
let got = interval.partial_cmp(&b).unwrap();
if a.start == b.start {
assert_eq!(got, a.end.cmp(&b.end));
} else {
assert_eq!(got, a.start.cmp(&b.start));
}
}
#[test]
fn prop_exclusive_interval_relations(
interval in arbitrary_range(),
values in prop::collection::vec(arbitrary_range(), 1..20),
) {
let interval = Interval::from(interval);
for v in &values {
let mut truthy = 0;
if interval.precedes(v) {
println!("{interval} precedes {v:?}");
truthy += 1;
}
if interval.preceded_by(v) {
println!("{interval} preceded_by {v:?}");
truthy += 1;
}
if interval.overlaps(v) {
println!("{interval} overlaps {v:?}");
truthy += 1;
}
if interval.meets(v) {
println!("{interval} meets {v:?}");
truthy += 1;
}
if interval.met_by(v) {
println!("{interval} met_by {v:?}");
truthy += 1;
}
if interval.during(v) {
println!("{interval} during {v:?}");
truthy += 1;
}
if interval.contains(v) {
println!("{interval} contains {v:?}");
truthy += 1;
}
if interval.starts(v) {
println!("{interval} starts {v:?}");
truthy += 1;
}
if interval.finishes(v) {
println!("{interval} finishes {v:?}");
truthy += 1;
}
if v.is_empty() || interval.as_range().is_empty() {
continue;
}
match truthy {
1 => {}
2 if interval.overlaps(v) && interval.during(v) => {}
3 if interval.overlaps(v) && interval.during(v) && interval.starts(v) => {
assert_eq!(*interval.start(), v.start);
}
3 if interval.overlaps(v) && interval.during(v) && interval.finishes(v) => {
assert_eq!(*interval.end(), v.end);
}
2 if interval.overlaps(v) && interval.contains(v) => {}
3 if interval.overlaps(v) && interval.contains(v) && interval.starts(v) => {
assert_eq!(*interval.start(), v.start);
}
3 if interval.overlaps(v) && interval.contains(v) && interval.finishes(v) => {
assert_eq!(*interval.end(), v.end);
}
5 if interval.overlaps(v)
&& interval.starts(v)
&& interval.finishes(v)
&& interval.contains(v)
&& interval.during(v) =>
{
assert_eq!(interval.as_range(), v)
}
_ if v.start > v.end => { }
_ if interval.start() > interval.end() => { }
_ => panic!("non-exclusive relation found"),
}
}
}
}
}