use crate::bound_pair::BoundPair;
use either::Either;
use std::cmp::Ordering;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Interval<T> {
Closed { bound_pair: BoundPair<T> },
Open { bound_pair: BoundPair<T> },
LeftHalfOpen { bound_pair: BoundPair<T> },
RightHalfOpen { bound_pair: BoundPair<T> },
UnboundedClosedRight { right: T },
UnboundedOpenRight { right: T },
UnboundedClosedLeft { left: T },
UnboundedOpenLeft { left: T },
Singleton { at: T },
Unbounded,
Empty,
}
enum Bound<T> {
None,
Unbounded,
Open(T),
Closed(T),
}
type TwoIntervalIter<T> =
std::iter::Chain<std::iter::Once<Interval<T>>, std::iter::Once<Interval<T>>>;
type OneIntervalIter<T> = std::iter::Once<Interval<T>>;
impl<T> Interval<T>
where
T: Copy,
T: std::cmp::PartialOrd,
{
pub fn contains(&self, other: &Interval<T>) -> bool {
let self_right_bound = self.to_right_bound();
let other_right_bound = other.to_right_bound();
let self_left_bound = self.to_left_bound();
let other_left_bound = other.to_left_bound();
let left_contained = match (self_left_bound, other_left_bound) {
(Bound::None, _) => false,
(_, Bound::None) => true,
(Bound::Unbounded, _) => true,
(_, Bound::Unbounded) => false,
(Bound::Closed(ref self_val), Bound::Closed(ref other_val))
| (Bound::Closed(ref self_val), Bound::Open(ref other_val))
| (Bound::Open(ref self_val), Bound::Open(ref other_val)) => self_val <= other_val,
(Bound::Open(ref self_val), Bound::Closed(ref other_val)) => self_val < other_val,
};
let right_contained = match (self_right_bound, other_right_bound) {
(Bound::None, _) => false,
(_, Bound::None) => false,
(Bound::Unbounded, _) => true,
(_, Bound::Unbounded) => false,
(Bound::Closed(ref self_val), Bound::Closed(ref other_val))
| (Bound::Closed(ref self_val), Bound::Open(ref other_val))
| (Bound::Open(ref self_val), Bound::Open(ref other_val)) => self_val >= other_val,
(Bound::Open(ref self_val), Bound::Closed(ref other_val)) => self_val > other_val,
};
left_contained && right_contained
}
pub fn intersect(&self, other: &Interval<T>) -> Interval<T> {
let left_cmp_partial = self.left_partial_cmp(&other);
let right_cmp_partial = self.right_partial_cmp(&other);
if left_cmp_partial.is_none() || right_cmp_partial.is_none() {
return Interval::Empty;
}
let left_bound = if left_cmp_partial != Some(Ordering::Less) {
self.to_left_bound()
} else {
other.to_left_bound()
};
let right_bound = if right_cmp_partial != Some(Ordering::Greater) {
self.to_right_bound()
} else {
other.to_right_bound()
};
match (left_bound, right_bound) {
(Bound::None, _) => Interval::Empty,
(_, Bound::None) => Interval::Empty,
(Bound::Closed(left), Bound::Closed(right)) => {
if left > right {
Interval::Empty
} else {
Interval::Closed {
bound_pair: BoundPair { left, right },
}
}
}
(Bound::Open(left), Bound::Open(right)) => {
if left >= right {
Interval::Empty
} else {
Interval::Open {
bound_pair: BoundPair { left, right },
}
}
}
(Bound::Open(left), Bound::Closed(right)) => {
if left >= right {
Interval::Empty
} else {
Interval::LeftHalfOpen {
bound_pair: BoundPair { left, right },
}
}
}
(Bound::Closed(left), Bound::Open(right)) => {
if left >= right {
Interval::Empty
} else {
Interval::RightHalfOpen {
bound_pair: BoundPair { left, right },
}
}
}
(Bound::Unbounded, Bound::Closed(right)) => Interval::UnboundedClosedRight { right },
(Bound::Unbounded, Bound::Open(right)) => Interval::UnboundedOpenRight { right },
(Bound::Closed(left), Bound::Unbounded) => Interval::UnboundedClosedLeft { left },
(Bound::Open(left), Bound::Unbounded) => Interval::UnboundedOpenLeft { left },
(Bound::Unbounded, Bound::Unbounded) => Interval::Unbounded,
}
}
fn to_left_bound(&self) -> Bound<T> {
match self {
Interval::Empty => Bound::None,
Interval::Singleton { ref at } => Bound::Closed(*at),
Interval::Unbounded
| Interval::UnboundedClosedRight { .. }
| Interval::UnboundedOpenRight { .. } => Bound::Unbounded,
Interval::Closed {
bound_pair: BoundPair { ref left, .. },
}
| Interval::RightHalfOpen {
bound_pair: BoundPair { ref left, .. },
}
| Interval::UnboundedClosedLeft { ref left, .. } => Bound::Closed(*left),
Interval::Open {
bound_pair: BoundPair { ref left, .. },
}
| Interval::LeftHalfOpen {
bound_pair: BoundPair { ref left, .. },
}
| Interval::UnboundedOpenLeft { ref left, .. } => Bound::Open(*left),
}
}
fn to_right_bound(&self) -> Bound<T> {
match self {
Interval::Empty => Bound::None,
Interval::Singleton { ref at } => Bound::Closed(*at),
Interval::Unbounded
| Interval::UnboundedClosedLeft { .. }
| Interval::UnboundedOpenLeft { .. } => Bound::Unbounded,
Interval::Closed {
bound_pair: BoundPair { ref right, .. },
}
| Interval::LeftHalfOpen {
bound_pair: BoundPair { ref right, .. },
}
| Interval::UnboundedClosedRight { ref right, .. } => Bound::Closed(*right),
Interval::Open {
bound_pair: BoundPair { ref right, .. },
}
| Interval::RightHalfOpen {
bound_pair: BoundPair { ref right, .. },
}
| Interval::UnboundedOpenRight { ref right, .. } => Bound::Open(*right),
}
}
pub fn left_partial_cmp(&self, other: &Interval<T>) -> Option<Ordering> {
let self_left_bound = self.to_left_bound();
let other_left_bound = other.to_left_bound();
match (self_left_bound, other_left_bound) {
(Bound::None, _) => None,
(_, Bound::None) => None,
(Bound::Unbounded, Bound::Unbounded) => Some(Ordering::Equal),
(Bound::Unbounded, _) => Some(Ordering::Less),
(_, Bound::Unbounded) => Some(Ordering::Greater),
(Bound::Closed(self_val), Bound::Closed(other_val)) => {
if self_val < other_val {
Some(Ordering::Less)
} else if self_val > other_val {
Some(Ordering::Greater)
} else {
Some(Ordering::Equal)
}
}
(Bound::Closed(self_val), Bound::Open(other_val)) => {
if self_val <= other_val {
Some(Ordering::Less)
} else {
Some(Ordering::Greater)
}
}
(Bound::Open(self_val), Bound::Closed(other_val)) => {
if self_val < other_val {
Some(Ordering::Less)
} else {
Some(Ordering::Greater)
}
}
(Bound::Open(self_val), Bound::Open(other_val)) => {
if self_val < other_val {
Some(Ordering::Less)
} else if self_val > other_val {
Some(Ordering::Greater)
} else {
Some(Ordering::Equal)
}
}
}
}
pub fn right_partial_cmp(&self, other: &Interval<T>) -> Option<Ordering> {
let self_right_bound = self.to_right_bound();
let other_right_bound = other.to_right_bound();
match (self_right_bound, other_right_bound) {
(Bound::None, _) => None,
(_, Bound::None) => None,
(Bound::Unbounded, Bound::Unbounded) => Some(Ordering::Equal),
(Bound::Unbounded, _) => Some(Ordering::Greater),
(_, Bound::Unbounded) => Some(Ordering::Less),
(Bound::Closed(self_val), Bound::Closed(other_val)) => {
if self_val < other_val {
Some(Ordering::Less)
} else if self_val > other_val {
Some(Ordering::Greater)
} else {
Some(Ordering::Equal)
}
}
(Bound::Closed(self_val), Bound::Open(other_val)) => {
if self_val < other_val {
Some(Ordering::Less)
} else {
Some(Ordering::Greater)
}
}
(Bound::Open(self_val), Bound::Closed(other_val)) => {
if self_val <= other_val {
Some(Ordering::Less)
} else {
Some(Ordering::Greater)
}
}
(Bound::Open(self_val), Bound::Open(other_val)) => {
if self_val < other_val {
Some(Ordering::Less)
} else if self_val > other_val {
Some(Ordering::Greater)
} else {
Some(Ordering::Equal)
}
}
}
}
pub fn width(&self) -> Option<<T as std::ops::Sub>::Output>
where
T: std::ops::Sub,
{
let self_left_bound = self.to_left_bound();
let self_right_bound = self.to_right_bound();
match (self_left_bound, self_right_bound) {
(Bound::None, _) => None,
(_, Bound::None) => None,
(Bound::Unbounded, _) => None,
(_, Bound::Unbounded) => None,
(Bound::Closed(left), Bound::Closed(right)) => Some(right - left),
(Bound::Closed(left), Bound::Open(right)) => Some(right - left),
(Bound::Open(left), Bound::Closed(right)) => Some(right - left),
(Bound::Open(left), Bound::Open(right)) => Some(right - left),
}
}
pub fn complement(&self) -> Either<OneIntervalIter<T>, TwoIntervalIter<T>> {
match self {
Interval::Closed { bound_pair: bp } => Either::Right(
std::iter::once(Interval::UnboundedOpenRight { right: bp.left }).chain(
std::iter::once(Interval::UnboundedOpenLeft { left: bp.right }),
),
),
Interval::Open { bound_pair: bp } => Either::Right(
std::iter::once(Interval::UnboundedClosedRight { right: bp.left }).chain(
std::iter::once(Interval::UnboundedClosedLeft { left: bp.right }),
),
),
Interval::LeftHalfOpen { bound_pair: bp } => Either::Right(
std::iter::once(Interval::UnboundedClosedRight { right: bp.left }).chain(
std::iter::once(Interval::UnboundedOpenLeft { left: bp.right }),
),
),
Interval::RightHalfOpen { bound_pair: bp } => Either::Right(
std::iter::once(Interval::UnboundedOpenRight { right: bp.left }).chain(
std::iter::once(Interval::UnboundedClosedLeft { left: bp.right }),
),
),
Interval::UnboundedClosedRight { right: r } => {
Either::Left(std::iter::once(Interval::UnboundedOpenLeft { left: *r }))
}
Interval::UnboundedOpenRight { right: r } => {
Either::Left(std::iter::once(Interval::UnboundedClosedLeft { left: *r }))
}
Interval::UnboundedClosedLeft { left: l } => {
Either::Left(std::iter::once(Interval::UnboundedOpenRight { right: *l }))
}
Interval::UnboundedOpenLeft { left: l } => {
Either::Left(std::iter::once(Interval::UnboundedClosedRight {
right: *l,
}))
}
Interval::Singleton { at: a } => Either::Right(
std::iter::once(Interval::UnboundedOpenRight { right: *a })
.chain(std::iter::once(Interval::UnboundedOpenLeft { left: *a })),
),
Interval::Unbounded => Either::Left(std::iter::once(Interval::Empty)),
Interval::Empty => Either::Left(std::iter::once(Interval::Unbounded)),
}
}
}
impl<T> std::fmt::Display for Interval<T>
where
T: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Interval::Closed {
bound_pair:
BoundPair {
ref left,
ref right,
},
} => write!(f, "[{:?}..{:?}]", left, right),
Interval::Open {
bound_pair:
BoundPair {
ref left,
ref right,
},
} => write!(f, "({:?}..{:?})", left, right),
Interval::LeftHalfOpen {
bound_pair:
BoundPair {
ref left,
ref right,
},
} => write!(f, "({:?}..{:?}]", left, right),
Interval::RightHalfOpen {
bound_pair:
BoundPair {
ref left,
ref right,
},
} => write!(f, "[{:?}..{:?})", left, right),
Interval::UnboundedClosedRight { ref right } => write!(f, "(←..{:?}]", right),
Interval::UnboundedOpenRight { ref right } => write!(f, "(←..{:?})", right),
Interval::UnboundedClosedLeft { ref left } => write!(f, "[{:?}..→)", left),
Interval::UnboundedOpenLeft { ref left } => write!(f, "({:?}..→)", left),
Interval::Singleton { ref at } => write!(f, "[{:?}]", at),
Interval::Unbounded => write!(f, "(←..→)"),
Interval::Empty => write!(f, "Empty"),
}
}
}
#[cfg(test)]
mod tests {
use crate::bound_pair::BoundPair;
use crate::interval::Interval;
use quickcheck::TestResult;
use quickcheck_macros::quickcheck;
#[test]
fn test_bounded_complements() {
let bp = BoundPair::new(1, 5).unwrap();
let mut it = Interval::Closed { bound_pair: bp }.complement();
assert_eq!(it.next(), Some(Interval::UnboundedOpenRight { right: 1 }));
assert_eq!(it.next(), Some(Interval::UnboundedOpenLeft { left: 5 }));
assert_eq!(it.next(), None);
it = Interval::Open { bound_pair: bp }.complement();
assert_eq!(it.next(), Some(Interval::UnboundedClosedRight { right: 1 }));
assert_eq!(it.next(), Some(Interval::UnboundedClosedLeft { left: 5 }));
assert_eq!(it.next(), None);
it = Interval::LeftHalfOpen { bound_pair: bp }.complement();
assert_eq!(it.next(), Some(Interval::UnboundedClosedRight { right: 1 }));
assert_eq!(it.next(), Some(Interval::UnboundedOpenLeft { left: 5 }));
assert_eq!(it.next(), None);
it = Interval::RightHalfOpen { bound_pair: bp }.complement();
assert_eq!(it.next(), Some(Interval::UnboundedOpenRight { right: 1 }));
assert_eq!(it.next(), Some(Interval::UnboundedClosedLeft { left: 5 }));
assert_eq!(it.next(), None);
}
#[test]
fn test_unbounded_complements() {
let mut it = Interval::UnboundedClosedRight { right: 5 }.complement();
assert_eq!(it.next(), Some(Interval::UnboundedOpenLeft { left: 5 }));
assert_eq!(it.next(), None);
it = Interval::UnboundedOpenRight { right: 5 }.complement();
assert_eq!(it.next(), Some(Interval::UnboundedClosedLeft { left: 5 }));
assert_eq!(it.next(), None);
it = Interval::UnboundedClosedLeft { left: 1 }.complement();
assert_eq!(it.next(), Some(Interval::UnboundedOpenRight { right: 1 }));
assert_eq!(it.next(), None);
it = Interval::UnboundedOpenLeft { left: 1 }.complement();
assert_eq!(it.next(), Some(Interval::UnboundedClosedRight { right: 1 }));
assert_eq!(it.next(), None);
let mut it = Interval::Singleton { at: 2.0 }.complement();
assert_eq!(it.next(), Some(Interval::UnboundedOpenRight { right: 2.0 }));
assert_eq!(it.next(), Some(Interval::UnboundedOpenLeft { left: 2.0 }));
assert_eq!(it.next(), None);
it = Interval::Unbounded.complement();
assert_eq!(it.next(), Some(Interval::Empty));
assert_eq!(it.next(), None);
it = Interval::Empty.complement();
assert_eq!(it.next(), Some(Interval::Unbounded));
assert_eq!(it.next(), None);
}
#[test]
fn interval_display() {
let bp = BoundPair::new(1, 5).ok_or("invalid BoundPair").unwrap();
assert_eq!(format!("{}", Interval::Closed { bound_pair: bp }), "[1..5]");
assert_eq!(format!("{}", Interval::Open { bound_pair: bp }), "(1..5)");
assert_eq!(
format!("{}", Interval::LeftHalfOpen { bound_pair: bp }),
"(1..5]"
);
assert_eq!(
format!("{}", Interval::RightHalfOpen { bound_pair: bp }),
"[1..5)"
);
assert_eq!(
format!("{}", Interval::UnboundedClosedRight { right: 5 }),
"(←..5]"
);
assert_eq!(
format!("{}", Interval::UnboundedOpenRight { right: 5 }),
"(←..5)"
);
assert_eq!(
format!("{}", Interval::UnboundedClosedLeft { left: 1 }),
"[1..→)"
);
assert_eq!(
format!("{}", Interval::UnboundedOpenLeft { left: 1 }),
"(1..→)"
);
assert_eq!(format!("{}", Interval::Singleton { at: 3.0 }), "[3.0]");
assert_eq!(format!("{}", Interval::Unbounded::<u32> {}), "(←..→)");
assert_eq!(format!("{}", Interval::Empty::<u32> {}), "Empty");
}
#[quickcheck]
fn intersect_strictly_shrinks_u32(l1: u32, l2: u32, r1: u32, r2: u32) -> TestResult {
if let (Some(bp1), Some(bp2)) = (BoundPair::new(l1, r1), BoundPair::new(l2, r2)) {
let i1 = Interval::LeftHalfOpen { bound_pair: bp1 };
let i2 = Interval::LeftHalfOpen { bound_pair: bp2 };
let intersection = i1.intersect(&i2);
if (intersection.width() > i1.width()) | (intersection.width() > i2.width()) {
TestResult::from_bool(false)
} else {
TestResult::from_bool(true)
}
} else {
TestResult::discard()
}
}
#[quickcheck]
fn intersect_strictly_shrinks_f32(l1: f32, l2: f32, r1: f32, r2: f32) -> TestResult {
if let (Some(bp1), Some(bp2)) = (BoundPair::new(l1, r1), BoundPair::new(l2, r2)) {
let i1 = Interval::LeftHalfOpen { bound_pair: bp1 };
let i2 = Interval::LeftHalfOpen { bound_pair: bp2 };
let intersection = i1.intersect(&i2);
if (intersection.width() > i1.width()) | (intersection.width() > i2.width()) {
TestResult::from_bool(false)
} else {
TestResult::from_bool(true)
}
} else {
TestResult::discard()
}
}
}