use std::cmp::Ordering;
use crate::{
Bb, Be, Bounds, Eb, Ee, FromIntervals, Interval, IntervalBounds, IntervalError, IntervalFrom,
IntervalFull, IntervalTo, NonEmpty, TryFromIntervals,
};
mod contains;
mod equals;
mod finishes;
mod meets;
mod overlaps;
mod precedes;
mod starts;
pub use self::{
contains::*, equals::*, finishes::*, meets::*, overlaps::*, precedes::*, starts::*,
};
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
enum RelationOrder {
Precedes,
Meets,
Overlaps,
IsFinishedBy,
Contains,
Starts,
Equals,
IsStartedBy,
IsContainedBy,
Finishes,
IsOverlappedBy,
IsMetBy,
IsPrecededBy,
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum Relation {
Precedes {
is_inverted: bool,
},
Meets {
is_inverted: bool,
},
Overlaps {
is_inverted: bool,
},
Finishes {
is_inverted: bool,
},
Contains {
is_inverted: bool,
},
Starts {
is_inverted: bool,
},
Equals,
}
impl Relation {
#[inline]
fn from_bounds<T>(s: &Bounds<T>, t: &Bounds<T>) -> Self
where
T: Ord,
{
let bb = Bb::from_bounds(&s.start, &t.start);
let be = Be::from_bounds(&s.start, &t.end);
let eb = Eb::from_bounds(&s.end, &t.start);
let ee = Ee::from_bounds(&s.end, &t.end);
Self::from_atomic_relations(bb, be, eb, ee)
}
#[inline]
fn try_from_bounds<T>(s: &Bounds<T>, t: &Bounds<T>) -> Result<Self, IntervalError>
where
T: PartialOrd,
{
let bb = Bb::try_from_bounds(&s.start, &t.start)?;
let be = Be::try_from_bounds(&s.start, &t.end)?;
let eb = Eb::try_from_bounds(&s.end, &t.start)?;
let ee = Ee::try_from_bounds(&s.end, &t.end)?;
Ok(Self::from_atomic_relations(bb, be, eb, ee))
}
#[inline]
fn from_atomic_relations(bb: Bb, be: Be, eb: Eb, ee: Ee) -> Self {
use Ordering::*;
match (bb.0, be.0, eb.0, ee.0) {
(_, _, Less, _) => Self::Precedes { is_inverted: false },
(_, Greater, _, _) => Self::Precedes { is_inverted: true },
(_, _, Equal, _) => Self::Meets { is_inverted: false },
(_, Equal, _, _) => Self::Meets { is_inverted: true },
(Greater, _, _, Equal) => Self::Finishes { is_inverted: false },
(Less, _, _, Equal) => Self::Finishes { is_inverted: true },
(Equal, _, _, Less) => Self::Starts { is_inverted: false },
(Equal, _, _, Greater) => Self::Starts { is_inverted: true },
(Less, _, _, Greater) => Self::Contains { is_inverted: false },
(Greater, _, _, Less) => Self::Contains { is_inverted: true },
(Equal, _, _, Equal) => Self::Equals,
(Less, _, Greater, Less) => Self::Overlaps { is_inverted: false },
(Greater, Less, _, Greater) => Self::Overlaps { is_inverted: true },
}
}
fn order(&self) -> RelationOrder {
match self {
Relation::Precedes { is_inverted: false } => RelationOrder::Precedes,
Relation::Precedes { is_inverted: true } => RelationOrder::IsPrecededBy,
Relation::Meets { is_inverted: false } => RelationOrder::Meets,
Relation::Meets { is_inverted: true } => RelationOrder::IsMetBy,
Relation::Overlaps { is_inverted: false } => RelationOrder::Overlaps,
Relation::Overlaps { is_inverted: true } => RelationOrder::IsOverlappedBy,
Relation::Finishes { is_inverted: false } => RelationOrder::Finishes,
Relation::Finishes { is_inverted: true } => RelationOrder::IsFinishedBy,
Relation::Contains { is_inverted: false } => RelationOrder::Contains,
Relation::Contains { is_inverted: true } => RelationOrder::IsContainedBy,
Relation::Starts { is_inverted: false } => RelationOrder::IsStartedBy,
Relation::Starts { is_inverted: true } => RelationOrder::Starts,
Relation::Equals => RelationOrder::Equals,
}
}
pub fn as_converse(&self) -> Self {
match self {
Self::Precedes { is_inverted } => Self::Precedes {
is_inverted: !is_inverted,
},
Self::Meets { is_inverted } => Self::Meets {
is_inverted: !is_inverted,
},
Self::Overlaps { is_inverted } => Self::Overlaps {
is_inverted: !is_inverted,
},
Self::Finishes { is_inverted } => Self::Finishes {
is_inverted: !is_inverted,
},
Self::Contains { is_inverted } => Self::Contains {
is_inverted: !is_inverted,
},
Self::Starts { is_inverted } => Self::Starts {
is_inverted: !is_inverted,
},
Self::Equals => Self::Equals,
}
}
}
impl Ord for Relation {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.order().cmp(&other.order())
}
}
impl PartialOrd for Relation {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl FromIntervals<IntervalFull, IntervalFull> for Relation {
#[inline]
fn from_intervals(s: &NonEmpty<IntervalFull>, t: &NonEmpty<IntervalFull>) -> Self {
assert_eq!(s, t);
let bb = Bb(Ordering::Equal);
let be = Be(Ordering::Less);
let eb = Eb(Ordering::Greater);
let ee = Ee(Ordering::Equal);
Self::from_atomic_relations(bb, be, eb, ee)
}
}
impl TryFromIntervals<IntervalFull, IntervalFull> for Relation {
#[inline]
fn try_from_intervals(
s: &NonEmpty<IntervalFull>,
t: &NonEmpty<IntervalFull>,
) -> Result<Self, IntervalError> {
assert_eq!(s, t);
let bb = Bb(Ordering::Equal);
let be = Be(Ordering::Less);
let eb = Eb(Ordering::Greater);
let ee = Ee(Ordering::Equal);
Ok(Self::from_atomic_relations(bb, be, eb, ee))
}
}
macro_rules! from_intervals_impl {
($s:ty, $t:ty) => {
impl<T> FromIntervals<$s, $t> for Relation
where
T: Ord + Copy,
{
fn from_intervals(s: &NonEmpty<$s>, t: &NonEmpty<$t>) -> Self {
Self::from_bounds(&s.0.bounds(), &t.0.bounds())
}
}
impl<T> TryFromIntervals<$s, $t> for Relation
where
T: PartialOrd + Copy,
{
fn try_from_intervals(
s: &NonEmpty<$s>,
t: &NonEmpty<$t>,
) -> Result<Self, IntervalError> {
Self::try_from_bounds(&s.0.bounds(), &t.0.bounds())
}
}
};
}
from_intervals_impl!(IntervalFull, IntervalTo<T>);
from_intervals_impl!(IntervalFull, IntervalFrom<T>);
from_intervals_impl!(IntervalFull, Interval<T>);
from_intervals_impl!(IntervalTo<T>, IntervalFull);
from_intervals_impl!(IntervalTo<T>, IntervalTo<T>);
from_intervals_impl!(IntervalTo<T>, IntervalFrom<T>);
from_intervals_impl!(IntervalTo<T>, Interval<T>);
from_intervals_impl!(IntervalFrom<T>, IntervalFull);
from_intervals_impl!(IntervalFrom<T>, IntervalTo<T>);
from_intervals_impl!(IntervalFrom<T>, IntervalFrom<T>);
from_intervals_impl!(IntervalFrom<T>, Interval<T>);
from_intervals_impl!(Interval<T>, IntervalFull);
from_intervals_impl!(Interval<T>, IntervalTo<T>);
from_intervals_impl!(Interval<T>, IntervalFrom<T>);
from_intervals_impl!(Interval<T>, Interval<T>);
#[cfg(test)]
mod tests;