use std;
use num_traits::PrimInt;
#[derive(Debug,Eq,PartialEq)]
pub enum RangeCompare {
Disjoint (RangeDisjoint),
Intersect (RangeIntersect)
}
#[derive(Debug,Eq,PartialEq)]
pub enum RangeDisjoint {
EmptyBoth,
EmptyLhs,
EmptyRhs,
LessThanProper,
LessThanAdjacent,
GreaterThanProper,
GreaterThanAdjacent
}
#[derive(Debug,Eq,PartialEq)]
pub enum RangeIntersect {
EqualTo,
OverlapsLeft,
OverlapsRight,
ContainsFirst,
ContainsProper,
ContainsLast,
ContainedByFirst,
ContainedByProper,
ContainedByLast
}
pub fn range_compare <T : PrimInt> (
left : &std::ops::RangeInclusive <T>, right : &std::ops::RangeInclusive <T>
) -> RangeCompare {
if let Some (disjoint) = RangeDisjoint::compare (left, right) {
disjoint.into()
} else if let Some (intersect) = RangeIntersect::compare (left, right) {
intersect.into()
} else {
unreachable!()
}
}
pub fn intersection <T : PrimInt> (
left : &std::ops::RangeInclusive <T>, right : &std::ops::RangeInclusive <T>
) -> std::ops::RangeInclusive <T> {
if RangeDisjoint::compare (left, right).is_none() {
std::cmp::max (*left.start(), *right.start())..=
std::cmp::min (*left.end(), *right.end())
} else {
T::one()..=T::zero()
}
}
impl From <RangeDisjoint> for RangeCompare {
fn from (disjoint : RangeDisjoint) -> Self {
RangeCompare::Disjoint (disjoint)
}
}
impl From <RangeIntersect> for RangeCompare {
fn from (intersect : RangeIntersect) -> Self {
RangeCompare::Intersect (intersect)
}
}
impl RangeDisjoint {
pub fn compare <T : PrimInt> (
left : &std::ops::RangeInclusive <T>, right : &std::ops::RangeInclusive <T>
) -> Option <Self> {
match (left.is_empty(), right.is_empty()) {
(true, true) => Some (RangeDisjoint::EmptyBoth),
(true, false) => Some (RangeDisjoint::EmptyLhs),
(false, true) => Some (RangeDisjoint::EmptyRhs),
(false, false) => if right.start() <= left.end()
&& left.start() <= right.end()
|| left.start() <= right.end() && right.start() <= left.end()
{
None } else {
Some (
if left.end() < right.start() {
match *right.start() - *left.end() {
x if x == T::one() => RangeDisjoint::LessThanAdjacent,
_ => RangeDisjoint::LessThanProper
}
} else {
debug_assert!(right.end() < left.start());
match *left.start() - *right.end() {
x if x == T::one() => RangeDisjoint::GreaterThanAdjacent,
_ => RangeDisjoint::GreaterThanProper
}
}
)
}
}
}
}
impl RangeIntersect {
pub fn compare <T : PrimInt> (
left : &std::ops::RangeInclusive <T>, right : &std::ops::RangeInclusive <T>
) -> Option <Self> {
match (left.is_empty(), right.is_empty()) {
(true, true) | (true, false) | (false, true) => None,
(false, false) => if left.end() < right.start() ||
right.end() < left.start()
{
None } else {
Some (if left == right {
RangeIntersect::EqualTo
} else {
match left.start().cmp (right.start()) {
std::cmp::Ordering::Less => match left.end().cmp (right.end()) {
std::cmp::Ordering::Less => RangeIntersect::OverlapsLeft,
std::cmp::Ordering::Equal => RangeIntersect::ContainsLast,
std::cmp::Ordering::Greater => RangeIntersect::ContainsProper
}
std::cmp::Ordering::Equal => match left.end().cmp (right.end()) {
std::cmp::Ordering::Less => RangeIntersect::ContainedByFirst,
std::cmp::Ordering::Equal => unreachable!(),
std::cmp::Ordering::Greater => RangeIntersect::ContainsFirst
}
std::cmp::Ordering::Greater => match left.end().cmp (right.end()) {
std::cmp::Ordering::Less => RangeIntersect::ContainedByProper,
std::cmp::Ordering::Equal => RangeIntersect::ContainedByLast,
std::cmp::Ordering::Greater => RangeIntersect::OverlapsRight
}
}
})
}
}
}
}