#![deny(rustdoc::broken_intra_doc_links)]
use crate::{
Contains, Interval, IntervalTrait,
intervals::{
IntervalBoundsRuntime, LowerBoundRuntime, UpperBoundRuntime,
bounded::{IntervalFiniteLength, IntervalFinitePositiveLength},
unbounded::{IntervalInfiniteLength, IntervalLowerUnboundedUpperUnbounded},
},
};
use duplicate::duplicate_item;
use num_valid::RealScalar;
use serde::{Deserialize, Serialize};
pub trait IntervalOperations: Contains {
#[inline]
fn intersection<IntervalType: IntervalTrait<RealType = Self::RealType>>(
&self,
other: &IntervalType,
) -> Option<Interval<Self::RealType>> {
let lb_max = self.max_lower_bound(other);
let ub_min = self.min_upper_bound(other);
match (lb_max, ub_min) {
(Some(lower_bound), Some(upper_bound)) => {
IntervalFiniteLength::try_from_runtime_bounds(lower_bound, upper_bound)
.ok()
.map(|interval| interval.into())
}
(None, Some(upper_bound)) => {
Some(IntervalInfiniteLength::new_lower_unbounded(upper_bound).into())
}
(Some(lower_bound), None) => {
Some(IntervalInfiniteLength::new_upper_unbounded(lower_bound).into())
}
(None, None) => {
Some(IntervalLowerUnboundedUpperUnbounded::new().into())
}
}
}
#[inline]
fn union<IntervalType: IntervalTrait<RealType = Self::RealType>>(
&self,
other: &IntervalType,
) -> IntervalUnion<Self::RealType>
where
Interval<Self::RealType>: From<Self> + From<IntervalType>,
{
let self_lb = self.lower_bound_runtime();
let self_ub = self.upper_bound_runtime();
let other_lb = other.lower_bound_runtime();
let other_ub = other.upper_bound_runtime();
if self_lb == other_lb && self_ub == other_ub {
IntervalUnion::SingleConnected(self.clone().into())
} else {
let self_is_on_the_left = self_lb <= other_lb;
let a = (self_lb, self_ub);
let b = (other_lb, other_ub);
let (left, right) = if self_is_on_the_left { (a, b) } else { (b, a) };
let upper_bound_left = &left.1;
let lower_bound_right = &right.0;
let intervals_touch_or_overlap = match (upper_bound_left, lower_bound_right) {
(None, _) | (_, None) => true,
(Some(UpperBoundRuntime::Open(ub)), Some(LowerBoundRuntime::Open(lb))) => {
ub.as_ref() > lb.as_ref()
}
(Some(UpperBoundRuntime::Open(ub)), Some(LowerBoundRuntime::Closed(lb))) => {
ub.as_ref() >= lb.as_ref()
}
(Some(UpperBoundRuntime::Closed(ub)), Some(LowerBoundRuntime::Open(lb))) => {
ub.as_ref() >= lb.as_ref()
}
(Some(UpperBoundRuntime::Closed(ub)), Some(LowerBoundRuntime::Closed(lb))) => {
ub.as_ref() >= lb.as_ref()
}
};
if intervals_touch_or_overlap {
let lower_bound = left.0;
let upper_bound = match (left.1, right.1) {
(None, _) | (_, None) => {
None
}
(Some(ub_left), Some(ub_right)) => {
Some(crate::bounds::max_upper_bound(ub_left, ub_right))
}
};
IntervalUnion::SingleConnected(
Interval::try_from_runtime_bounds(lower_bound, upper_bound)
.expect("Failed to create interval for union"),
)
} else if self_is_on_the_left {
IntervalUnion::TwoDisjoint {
left: self.clone().into(),
right: other.clone().into(),
}
} else {
IntervalUnion::TwoDisjoint {
left: other.clone().into(),
right: self.clone().into(),
}
}
}
}
#[inline]
fn difference<IntervalType: IntervalTrait<RealType = Self::RealType>>(
&self,
other: &IntervalType,
) -> Option<IntervalDifference<Self::RealType>>
where
Interval<Self::RealType>: From<Self> + From<IntervalType>,
{
let intersection = self.intersection(other);
if intersection.is_none() {
return Some(IntervalDifference::SingleConnected(self.clone().into()));
}
let self_lb = self.lower_bound_runtime();
let self_ub = self.upper_bound_runtime();
let other_lb = other.lower_bound_runtime();
let other_ub = other.upper_bound_runtime();
let other_contains_self_lower = match (&other_lb, &self_lb) {
(None, _) => true, (_, None) => false, (Some(other_lb_val), Some(self_lb_val)) => other_lb_val <= self_lb_val,
};
let other_contains_self_upper = match (&other_ub, &self_ub) {
(None, _) => true, (_, None) => false, (Some(other_ub_val), Some(self_ub_val)) => other_ub_val >= self_ub_val,
};
if other_contains_self_lower && other_contains_self_upper {
return None;
}
let has_left_part = match (&self_lb, &other_lb) {
(None, None) => false, (None, Some(_)) => true, (Some(_), None) => false, (Some(self_lb_val), Some(other_lb_val)) => self_lb_val < other_lb_val,
};
let has_right_part = match (&self_ub, &other_ub) {
(None, None) => false, (None, Some(_)) => true, (Some(_), None) => false, (Some(self_ub_val), Some(other_ub_val)) => self_ub_val > other_ub_val,
};
match (has_left_part, has_right_part) {
(false, false) => {
unreachable!(
"Invariant violation: if no intersection exists, we return early at line ~1161; \
if other completely contains self, we return None at line ~1188. \
Therefore, at least one of has_left_part or has_right_part must be true."
)
}
(true, false) => {
let left_ub = other_lb
.as_ref()
.map(|lb| lb.clone().flip_bound_side_and_type());
Some(IntervalDifference::SingleConnected(
Interval::try_from_runtime_bounds(self_lb, left_ub)
.expect("Failed to create interval difference"),
))
}
(false, true) => {
let right_lb = other_ub
.as_ref()
.map(|ub| ub.clone().flip_bound_side_and_type());
Some(IntervalDifference::SingleConnected(
Interval::try_from_runtime_bounds(right_lb, self_ub)
.expect("Failed to create interval difference"),
))
}
(true, true) => {
let left_ub = other_lb
.as_ref()
.map(|lb| lb.clone().flip_bound_side_and_type());
let right_lb = other_ub
.as_ref()
.map(|ub| ub.clone().flip_bound_side_and_type());
let left = Interval::try_from_runtime_bounds(self_lb.clone(), left_ub)
.expect("Left interval is guaranteed valid when has_left_part = true");
let right = Interval::try_from_runtime_bounds(right_lb, self_ub.clone())
.expect("Right interval is guaranteed valid when has_right_part = true");
Some(IntervalDifference::TwoDisjoint { left, right })
}
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
pub enum IntervalUnion<RealType: RealScalar> {
SingleConnected(Interval<RealType>),
TwoDisjoint {
left: Interval<RealType>,
right: Interval<RealType>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
pub enum IntervalDifference<RealType: RealScalar> {
SingleConnected(Interval<RealType>),
TwoDisjoint {
left: Interval<RealType>,
right: Interval<RealType>,
},
}
#[duplicate_item(
EnumName contains_doc;
[IntervalUnion] ["contained in this union"];
[IntervalDifference] ["contained in this difference result"];
)]
impl<RealType: RealScalar> EnumName<RealType> {
#[inline(always)]
pub fn is_single_connected(&self) -> bool {
matches!(self, Self::SingleConnected(_))
}
#[inline(always)]
pub fn is_two_disjoint(&self) -> bool {
matches!(self, Self::TwoDisjoint { .. })
}
#[inline(always)]
pub fn as_single_connected(&self) -> Option<&Interval<RealType>> {
match self {
Self::SingleConnected(interval) => Some(interval),
_ => None,
}
}
#[inline(always)]
pub fn as_two_disjoint(&self) -> Option<(&Interval<RealType>, &Interval<RealType>)> {
match self {
Self::TwoDisjoint { left, right } => Some((left, right)),
_ => None,
}
}
#[doc = concat!("Checks if a point is ", contains_doc, ".")]
#[inline(always)]
pub fn contains_point(&self, x: &RealType) -> bool {
match self {
Self::SingleConnected(interval) => interval.contains_point(x),
Self::TwoDisjoint { left, right } => left.contains_point(x) || right.contains_point(x),
}
}
#[inline]
pub fn gap(&self) -> Option<IntervalFinitePositiveLength<RealType>> {
match self {
Self::SingleConnected(_) => None,
Self::TwoDisjoint { left, right } => {
let left_ub = left
.upper_bound_runtime()
.expect("The left interval in a disjoint union cannot be unbounded above");
let right_lb = right
.lower_bound_runtime()
.expect("The right interval in a disjoint union cannot be unbounded below");
let lb = left_ub.flip_bound_side_and_type();
let ub = right_lb.flip_bound_side_and_type();
Some(
IntervalFinitePositiveLength::try_from_runtime_bounds(lb, ub)
.expect("Failed to create the interval representing the gap!"),
)
}
}
}
#[must_use]
pub fn to_intervals(self) -> Vec<Interval<RealType>> {
match self {
Self::SingleConnected(interval) => vec![interval],
Self::TwoDisjoint { left, right } => vec![left, right],
}
}
}
impl<RealType: RealScalar> IntervalDifference<RealType> {}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
SubIntervalInPartition,
intervals::{
GetLowerBoundValue, GetUpperBoundValue, Interval, IntervalClosed,
IntervalFinitePositiveLengthTrait, IntervalLowerClosedUpperOpen,
IntervalLowerClosedUpperUnbounded, IntervalLowerOpenUpperClosed,
IntervalLowerOpenUpperUnbounded, IntervalLowerUnboundedUpperClosed,
IntervalLowerUnboundedUpperOpen, IntervalLowerUnboundedUpperUnbounded, IntervalOpen,
IntervalSingleton, bounded::IntervalFromBounds,
},
};
use num_valid::scalars::PositiveRealScalar;
use try_create::{New, TryNew};
mod difference {
use super::*;
#[test]
fn difference_no_overlap() {
let a = IntervalClosed::new(0.0, 2.0);
let b = IntervalClosed::new(3.0, 5.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(result) => {
let expected: Interval<f64> = IntervalClosed::new(0.0, 2.0).into();
assert_eq!(result, expected);
}
_ => panic!("Expected single interval"),
}
}
#[test]
fn difference_partial_overlap_right() {
let a = IntervalClosed::new(0.0, 3.0);
let b = IntervalClosed::new(2.0, 5.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(result) => {
let expected: Interval<f64> =
IntervalLowerClosedUpperOpen::new(0.0, 2.0).into();
assert_eq!(result, expected);
}
_ => panic!("Expected single interval"),
}
}
#[test]
fn difference_partial_overlap_left() {
let a = IntervalClosed::new(2.0, 5.0);
let b = IntervalClosed::new(0.0, 3.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(result) => {
let expected: Interval<f64> =
IntervalLowerOpenUpperClosed::new(3.0, 5.0).into();
assert_eq!(result, expected);
}
_ => panic!("Expected single interval"),
}
}
#[test]
fn difference_complete_containment() {
let a = IntervalClosed::new(1.0, 2.0);
let b = IntervalClosed::new(0.0, 3.0);
let diff = a.difference(&b);
assert!(diff.is_none());
}
#[test]
fn difference_interior_removal() {
let a = IntervalClosed::new(0.0, 4.0);
let b = IntervalOpen::new(1.0, 3.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::TwoDisjoint { left, right } => {
let expected_left: Interval<f64> = IntervalClosed::new(0.0, 1.0).into();
let expected_right: Interval<f64> = IntervalClosed::new(3.0, 4.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint intervals"),
}
}
#[test]
fn difference_boundary_subtlety_open_closed() {
let a = IntervalClosed::new(0.0, 2.0);
let b = IntervalLowerOpenUpperClosed::new(1.0, 2.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(result) => {
let expected: Interval<f64> = IntervalClosed::new(0.0, 1.0).into();
assert_eq!(result, expected);
}
_ => panic!("Expected single interval"),
}
}
#[test]
fn difference_boundary_subtlety_closed_open() {
let a = IntervalClosed::new(0.0, 2.0);
let b = IntervalLowerClosedUpperOpen::new(1.0, 2.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::TwoDisjoint { left, right } => {
let expected_left: Interval<f64> =
IntervalLowerClosedUpperOpen::new(0.0, 1.0).into();
let expected_right: Interval<f64> = IntervalSingleton::new(2.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint intervals"),
}
}
#[test]
fn difference_with_unbounded_intervals() {
let a = IntervalLowerClosedUpperUnbounded::new(0.0);
let b = IntervalClosed::new(5.0, 10.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::TwoDisjoint { left, right } => {
let expected_left: Interval<f64> =
IntervalLowerClosedUpperOpen::new(0.0, 5.0).into();
let expected_right: Interval<f64> =
IntervalLowerOpenUpperUnbounded::new(10.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint intervals"),
}
}
#[test]
fn difference_unbounded_minus_unbounded() {
let a = IntervalLowerUnboundedUpperUnbounded::new();
let b = IntervalClosed::new(0.0, 10.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::TwoDisjoint { left, right } => {
let expected_left: Interval<f64> =
IntervalLowerUnboundedUpperOpen::new(0.0).into();
let expected_right: Interval<f64> =
IntervalLowerOpenUpperUnbounded::new(10.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint intervals"),
}
}
#[test]
fn difference_unbounded_complete_containment() {
let a = IntervalClosed::new(5.0, 10.0);
let b = IntervalLowerUnboundedUpperUnbounded::new();
let diff = a.difference(&b);
assert!(diff.is_none());
}
#[test]
fn difference_unbounded_no_overlap() {
let a = IntervalClosed::new(0.0, 5.0);
let b = IntervalLowerClosedUpperUnbounded::new(10.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(result) => {
let expected: Interval<f64> = IntervalClosed::new(0.0, 5.0).into();
assert_eq!(result, expected);
}
_ => panic!("Expected single interval"),
}
}
#[test]
fn difference_with_singleton() {
let a = IntervalClosed::new(0.0, 2.0);
let b = IntervalSingleton::new(1.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::TwoDisjoint { left, right } => {
let expected_left: Interval<f64> =
IntervalLowerClosedUpperOpen::new(0.0, 1.0).into();
let expected_right: Interval<f64> =
IntervalLowerOpenUpperClosed::new(1.0, 2.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint intervals"),
}
}
#[test]
fn difference_singleton_minus_interval() {
let a = IntervalSingleton::new(1.0);
let b = IntervalClosed::new(0.0, 2.0);
let diff = a.difference(&b);
assert!(diff.is_none());
}
#[test]
fn difference_singleton_minus_non_overlapping() {
let a = IntervalSingleton::new(5.0);
let b = IntervalClosed::new(0.0, 2.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(result) => {
let expected: Interval<f64> = IntervalSingleton::new(5.0).into();
assert_eq!(result, expected);
}
_ => panic!("Expected single interval"),
}
}
#[test]
fn difference_identical_intervals() {
let a = IntervalClosed::new(0.0, 1.0);
let b = IntervalClosed::new(0.0, 1.0);
let diff = a.difference(&b);
assert!(diff.is_none());
}
#[test]
fn difference_open_vs_closed_same_values() {
let a = IntervalClosed::new(0.0, 2.0);
let b = IntervalOpen::new(0.0, 2.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::TwoDisjoint { left, right } => {
let expected_left: Interval<f64> = IntervalSingleton::new(0.0).into();
let expected_right: Interval<f64> = IntervalSingleton::new(2.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint intervals (singletons at boundaries)"),
}
}
#[test]
fn difference_contains_point_test() {
let a = IntervalClosed::new(0.0, 4.0);
let b = IntervalOpen::new(1.0, 3.0);
let diff = a.difference(&b).unwrap();
assert!(diff.contains_point(&0.0));
assert!(diff.contains_point(&0.5));
assert!(diff.contains_point(&1.0));
assert!(!diff.contains_point(&1.5));
assert!(!diff.contains_point(&2.0));
assert!(!diff.contains_point(&2.5));
assert!(diff.contains_point(&3.0));
assert!(diff.contains_point(&3.5));
assert!(diff.contains_point(&4.0));
assert!(!diff.contains_point(&-1.0));
assert!(!diff.contains_point(&5.0));
}
#[test]
fn difference_to_intervals_test() {
let a = IntervalClosed::new(1.0, 2.0);
let b = IntervalClosed::new(0.0, 3.0);
let diff = a.difference(&b);
assert!(diff.is_none());
let a = IntervalClosed::new(0.0, 2.0);
let b = IntervalClosed::new(3.0, 5.0);
let diff = a.difference(&b);
assert!(diff.is_some());
assert_eq!(diff.unwrap().to_intervals().len(), 1);
let a = IntervalClosed::new(0.0, 4.0);
let b = IntervalOpen::new(1.0, 3.0);
let diff = a.difference(&b);
assert!(diff.is_some());
assert_eq!(diff.unwrap().to_intervals().len(), 2);
}
#[test]
fn difference_helper_methods_test() {
let a = IntervalClosed::new(0.0, 4.0);
let b = IntervalOpen::new(1.0, 3.0);
let diff = a.difference(&b).unwrap();
assert!(!diff.is_single_connected());
assert!(diff.is_two_disjoint());
assert!(diff.as_single_connected().is_none());
assert!(diff.as_two_disjoint().is_some());
let (left, right) = diff.as_two_disjoint().unwrap();
assert!(left.contains_point(&0.5));
assert!(right.contains_point(&3.5));
}
#[test]
fn difference_lower_unbounded_partial() {
let a = IntervalLowerUnboundedUpperClosed::new(5.0);
let b = IntervalClosed::new(0.0, 3.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::TwoDisjoint { left, right } => {
let expected_left: Interval<f64> =
IntervalLowerUnboundedUpperOpen::new(0.0).into();
let expected_right: Interval<f64> =
IntervalLowerOpenUpperClosed::new(3.0, 5.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint intervals"),
}
}
#[test]
fn difference_open_open_no_overlap() {
let a = IntervalOpen::new(0.0, 2.0);
let b = IntervalOpen::new(3.0, 5.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(result) => {
let expected: Interval<f64> = IntervalOpen::new(0.0, 2.0).into();
assert_eq!(result, expected);
}
_ => panic!("Expected single interval"),
}
}
#[test]
fn difference_open_open_partial_overlap() {
let a = IntervalOpen::new(0.0, 4.0);
let b = IntervalOpen::new(2.0, 6.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(result) => {
let expected: Interval<f64> =
IntervalLowerOpenUpperClosed::new(0.0, 2.0).into();
assert_eq!(result, expected);
}
_ => panic!("Expected single interval"),
}
}
#[test]
fn difference_degenerate_left_valid_right() {
let a = IntervalClosed::new(0.0, 4.0);
let b = IntervalLowerClosedUpperOpen::new(0.0, 2.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(result) => {
let expected: Interval<f64> = IntervalClosed::new(2.0, 4.0).into();
assert_eq!(
result, expected,
"Should only have right part, left is degenerate"
);
}
_ => panic!("Expected single interval (right part only)"),
}
}
#[test]
fn difference_valid_left_degenerate_right() {
let a = IntervalClosed::new(0.0, 4.0);
let b = IntervalLowerOpenUpperClosed::new(2.0, 4.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(result) => {
let expected: Interval<f64> = IntervalClosed::new(0.0, 2.0).into();
assert_eq!(
result, expected,
"Should only have left part, right is degenerate"
);
}
_ => panic!("Expected single interval (left part only)"),
}
}
#[test]
fn difference_singleton_same_value() {
let a = IntervalSingleton::new(5.0);
let b = IntervalSingleton::new(5.0);
let diff = a.difference(&b);
assert!(
diff.is_none(),
"Expected None when subtracting identical singletons ({{5}} \\ {{5}} = ∅)"
);
}
#[test]
fn difference_singleton_different_values() {
let a = IntervalSingleton::new(3.0);
let b = IntervalSingleton::new(7.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(interval) => {
let expected: Interval<f64> = IntervalSingleton::new(3.0).into();
assert_eq!(
interval, expected,
"Expected {{3}} when singletons are disjoint"
);
}
_ => panic!("Expected single singleton interval"),
}
}
#[test]
fn difference_singleton_contained_in_interval() {
let a = IntervalSingleton::new(5.0);
let b = IntervalClosed::new(0.0, 10.0);
let diff = a.difference(&b);
assert!(
diff.is_none(),
"Expected None when singleton is contained in interval"
);
}
#[test]
fn difference_interval_minus_singleton_at_endpoint() {
let a = IntervalClosed::new(0.0, 10.0);
let b = IntervalSingleton::new(0.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(interval) => {
let expected: Interval<f64> =
IntervalLowerOpenUpperClosed::new(0.0, 10.0).into();
assert_eq!(
interval, expected,
"Expected (0, 10] when removing left endpoint singleton"
);
}
_ => panic!("Expected single interval"),
}
}
#[test]
fn difference_interval_minus_singleton_interior() {
let a = IntervalClosed::new(0.0, 10.0);
let b = IntervalSingleton::new(5.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::TwoDisjoint { left, right } => {
let expected_left: Interval<f64> =
IntervalLowerClosedUpperOpen::new(0.0, 5.0).into();
let expected_right: Interval<f64> =
IntervalLowerOpenUpperClosed::new(5.0, 10.0).into();
assert_eq!(left, expected_left, "Expected [0, 5) as left part");
assert_eq!(right, expected_right, "Expected (5, 10] as right part");
}
_ => panic!("Expected two disjoint intervals"),
}
}
#[test]
fn difference_open_interval_minus_singleton_at_boundary() {
let a = IntervalOpen::new(0.0, 10.0);
let b = IntervalSingleton::new(0.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(interval) => {
let expected: Interval<f64> = IntervalOpen::new(0.0, 10.0).into();
assert_eq!(
interval, expected,
"Expected (0, 10) unchanged since {{0}} is not in (0, 10)"
);
}
_ => panic!("Expected single interval"),
}
}
#[test]
fn difference_open_interior_removal() {
let a = IntervalOpen::new(0.0, 6.0);
let b = IntervalClosed::new(2.0, 4.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::TwoDisjoint { left, right } => {
let expected_left: Interval<f64> = IntervalOpen::new(0.0, 2.0).into();
let expected_right: Interval<f64> = IntervalOpen::new(4.0, 6.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint open intervals"),
}
}
#[test]
fn difference_half_open_intervals() {
let a = IntervalLowerClosedUpperOpen::new(0.0, 5.0);
let b = IntervalLowerOpenUpperClosed::new(2.0, 4.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::TwoDisjoint { left, right } => {
let expected_left: Interval<f64> = IntervalClosed::new(0.0, 2.0).into();
let expected_right: Interval<f64> = IntervalOpen::new(4.0, 5.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint intervals with mixed bounds"),
}
}
#[test]
fn difference_with_fully_unbounded_other() {
let a = IntervalClosed::new(0.0, 5.0);
let b = IntervalLowerUnboundedUpperUnbounded::new();
let diff = a.difference(&b);
assert!(
diff.is_none(),
"Finite interval minus universal interval should be empty"
);
}
#[test]
fn difference_unbounded_partial_overlap() {
let a = IntervalLowerClosedUpperUnbounded::new(0.0);
let b = IntervalLowerUnboundedUpperClosed::new(5.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(result) => {
let expected: Interval<f64> = IntervalLowerOpenUpperUnbounded::new(5.0).into();
assert_eq!(result, expected);
}
_ => panic!("Expected single unbounded interval"),
}
}
#[test]
fn difference_open_lower_unbounded_minus_finite() {
let a = IntervalLowerUnboundedUpperOpen::new(5.0);
let b = IntervalClosed::new(0.0, 3.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::TwoDisjoint { left, right } => {
let expected_left: Interval<f64> =
IntervalLowerUnboundedUpperOpen::new(0.0).into();
let expected_right: Interval<f64> = IntervalOpen::new(3.0, 5.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint intervals"),
}
}
#[test]
fn difference_open_upper_unbounded_minus_finite() {
let a = IntervalLowerOpenUpperUnbounded::new(0.0);
let b = IntervalClosed::new(5.0, 10.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::TwoDisjoint { left, right } => {
let expected_left: Interval<f64> = IntervalOpen::new(0.0, 5.0).into();
let expected_right: Interval<f64> =
IntervalLowerOpenUpperUnbounded::new(10.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint intervals"),
}
}
#[test]
fn difference_lower_unbounded_minus_open_finite() {
let a = IntervalLowerUnboundedUpperClosed::new(10.0);
let b = IntervalOpen::new(2.0, 5.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::TwoDisjoint { left, right } => {
let expected_left: Interval<f64> =
IntervalLowerUnboundedUpperClosed::new(2.0).into();
let expected_right: Interval<f64> = IntervalClosed::new(5.0, 10.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint intervals"),
}
}
#[test]
fn difference_upper_unbounded_minus_open_finite() {
let a = IntervalLowerClosedUpperUnbounded::new(0.0);
let b = IntervalOpen::new(5.0, 10.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::TwoDisjoint { left, right } => {
let expected_left: Interval<f64> = IntervalClosed::new(0.0, 5.0).into();
let expected_right: Interval<f64> =
IntervalLowerClosedUpperUnbounded::new(10.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint intervals"),
}
}
#[test]
fn difference_same_direction_upper_unbounded_disjoint() {
let a = IntervalLowerClosedUpperUnbounded::new(0.0);
let b = IntervalLowerClosedUpperUnbounded::new(10.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(interval) => {
let expected: Interval<f64> =
IntervalLowerClosedUpperOpen::new(0.0, 10.0).into();
assert_eq!(interval, expected);
}
_ => panic!("Expected single connected interval"),
}
}
#[test]
fn difference_same_direction_upper_unbounded_overlap() {
let a = IntervalLowerClosedUpperUnbounded::new(5.0);
let b = IntervalLowerClosedUpperUnbounded::new(0.0);
let diff = a.difference(&b);
assert!(diff.is_none());
}
#[test]
fn difference_same_direction_lower_unbounded_disjoint() {
let a = IntervalLowerUnboundedUpperClosed::new(10.0);
let b = IntervalLowerUnboundedUpperClosed::new(5.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(interval) => {
let expected: Interval<f64> =
IntervalLowerOpenUpperClosed::new(5.0, 10.0).into();
assert_eq!(interval, expected);
}
_ => panic!("Expected single connected interval"),
}
}
#[test]
fn difference_same_direction_lower_unbounded_overlap() {
let a = IntervalLowerUnboundedUpperClosed::new(5.0);
let b = IntervalLowerUnboundedUpperClosed::new(10.0);
let diff = a.difference(&b);
assert!(diff.is_none());
}
#[test]
fn difference_universal_minus_upper_unbounded() {
let a = IntervalLowerUnboundedUpperUnbounded::new();
let b = IntervalLowerClosedUpperUnbounded::new(5.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(interval) => {
let expected: Interval<f64> = IntervalLowerUnboundedUpperOpen::new(5.0).into();
assert_eq!(interval, expected);
}
_ => panic!("Expected single connected interval"),
}
}
#[test]
fn difference_universal_minus_lower_unbounded() {
let a = IntervalLowerUnboundedUpperUnbounded::new();
let b = IntervalLowerUnboundedUpperClosed::new(5.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(interval) => {
let expected: Interval<f64> = IntervalLowerOpenUpperUnbounded::new(5.0).into();
assert_eq!(interval, expected);
}
_ => panic!("Expected single connected interval"),
}
}
#[test]
fn difference_universal_minus_open_finite() {
let a = IntervalLowerUnboundedUpperUnbounded::new();
let b = IntervalOpen::new(0.0, 10.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::TwoDisjoint { left, right } => {
let expected_left: Interval<f64> =
IntervalLowerUnboundedUpperClosed::new(0.0).into();
let expected_right: Interval<f64> =
IntervalLowerClosedUpperUnbounded::new(10.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint intervals"),
}
}
#[test]
fn difference_universal_minus_universal() {
let a: IntervalLowerUnboundedUpperUnbounded<f64> =
IntervalLowerUnboundedUpperUnbounded::new();
let b: IntervalLowerUnboundedUpperUnbounded<f64> =
IntervalLowerUnboundedUpperUnbounded::new();
let diff = a.difference(&b);
assert!(diff.is_none());
}
#[test]
fn difference_finite_closed_minus_lower_unbounded_disjoint() {
let a = IntervalClosed::new(10.0, 20.0);
let b = IntervalLowerUnboundedUpperClosed::new(5.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(interval) => {
let expected: Interval<f64> = IntervalClosed::new(10.0, 20.0).into();
assert_eq!(interval, expected);
}
_ => panic!("Expected single connected interval"),
}
}
#[test]
fn difference_finite_closed_minus_lower_unbounded_overlap() {
let a = IntervalClosed::new(0.0, 10.0);
let b = IntervalLowerUnboundedUpperClosed::new(5.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(interval) => {
let expected: Interval<f64> =
IntervalLowerOpenUpperClosed::new(5.0, 10.0).into();
assert_eq!(interval, expected);
}
_ => panic!("Expected single connected interval"),
}
}
#[test]
fn difference_finite_open_minus_upper_unbounded_disjoint() {
let a = IntervalOpen::new(0.0, 5.0);
let b = IntervalLowerClosedUpperUnbounded::new(10.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(interval) => {
let expected: Interval<f64> = IntervalOpen::new(0.0, 5.0).into();
assert_eq!(interval, expected);
}
_ => panic!("Expected single connected interval"),
}
}
#[test]
fn difference_finite_open_minus_lower_unbounded_overlap() {
let a = IntervalOpen::new(0.0, 10.0);
let b = IntervalLowerUnboundedUpperClosed::new(5.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(interval) => {
let expected: Interval<f64> = IntervalOpen::new(5.0, 10.0).into();
assert_eq!(interval, expected);
}
_ => panic!("Expected single connected interval"),
}
}
#[test]
fn difference_lower_unbounded_minus_upper_unbounded_disjoint() {
let a = IntervalLowerUnboundedUpperClosed::new(5.0);
let b = IntervalLowerClosedUpperUnbounded::new(10.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(interval) => {
let expected: Interval<f64> =
IntervalLowerUnboundedUpperClosed::new(5.0).into();
assert_eq!(interval, expected);
}
_ => panic!("Expected single connected interval"),
}
}
#[test]
fn difference_lower_unbounded_minus_upper_unbounded_overlap() {
let a = IntervalLowerUnboundedUpperClosed::new(10.0);
let b = IntervalLowerClosedUpperUnbounded::new(5.0);
let diff = a.difference(&b);
assert!(diff.is_some());
match diff.unwrap() {
IntervalDifference::SingleConnected(interval) => {
let expected: Interval<f64> = IntervalLowerUnboundedUpperOpen::new(5.0).into();
assert_eq!(interval, expected);
}
_ => panic!("Expected single connected interval"),
}
}
#[test]
fn difference_none_half_open_contained() {
let a = IntervalLowerClosedUpperOpen::new(1.0, 5.0);
let b = IntervalClosed::new(0.0, 10.0);
let diff = a.difference(&b);
assert!(
diff.is_none(),
"Expected None when interval is completely contained"
);
}
#[test]
fn difference_none_open_interval_contained() {
let a = IntervalOpen::new(2.0, 4.0);
let b = IntervalClosed::new(2.0, 4.0);
let diff = a.difference(&b);
assert!(
diff.is_none(),
"Expected None when open interval is contained in closed interval"
);
}
#[test]
fn difference_none_identical_intervals() {
let a = IntervalClosed::new(3.0, 7.0);
let b = IntervalClosed::new(3.0, 7.0);
let diff = a.difference(&b);
assert!(diff.is_none(), "Expected None when A = B (A \\ A = ∅)");
}
#[test]
fn difference_none_open_interval_subset() {
let a = IntervalOpen::new(1.0, 3.0);
let b = IntervalOpen::new(0.0, 5.0);
let diff = a.difference(&b);
assert!(
diff.is_none(),
"Expected None when open interval is subset of another"
);
}
#[test]
fn difference_none_lower_unbounded_contained() {
let a = IntervalLowerUnboundedUpperClosed::new(5.0);
let b = IntervalLowerUnboundedUpperClosed::new(10.0);
let diff = a.difference(&b);
assert!(
diff.is_none(),
"Expected None when lower unbounded interval is subset"
);
}
#[test]
fn difference_none_upper_unbounded_contained() {
let a = IntervalLowerClosedUpperUnbounded::new(10.0);
let b = IntervalLowerClosedUpperUnbounded::new(5.0);
let diff = a.difference(&b);
assert!(
diff.is_none(),
"Expected None when upper unbounded interval is subset"
);
}
#[test]
fn difference_none_mixed_boundaries_contained() {
let a = IntervalOpen::new(2.0, 8.0);
let b = IntervalClosed::new(1.0, 9.0);
let diff = a.difference(&b);
assert!(
diff.is_none(),
"Expected None when interval with open boundaries is contained"
);
}
}
mod intersection {
use super::*;
#[test]
fn intersection_closed_closed() {
let a = IntervalClosed::new(0.0, 2.0);
let b = IntervalClosed::new(1.0, 3.0);
let intersection = a.intersection(&b).unwrap();
let expected_intersection =
Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::from(IntervalClosed::new(1.0, 2.0)),
));
assert_eq!(intersection, expected_intersection);
}
#[test]
fn intersection_closed_open() {
let a = IntervalClosed::new(0.0, 2.0);
let b = IntervalOpen::new(1.0, 3.0);
let intersection = a.intersection(&b).unwrap();
let expected_intersection =
Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::from(IntervalLowerOpenUpperClosed::new(1.0, 2.0)),
));
assert_eq!(intersection, expected_intersection);
}
#[test]
fn intersection_open_open() {
let a = IntervalOpen::new(0.0, 2.0);
let b = IntervalOpen::new(1.0, 3.0);
let intersection = a.intersection(&b).unwrap();
let expected_intersection =
Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::from(IntervalOpen::new(1.0, 2.0)),
));
assert_eq!(intersection, expected_intersection);
}
#[test]
fn intersection_closed_left_half_open() {
let a = IntervalClosed::new(0.0, 2.0);
let b = IntervalLowerOpenUpperClosed::new(1.0, 3.0);
let intersection = a.intersection(&b).unwrap();
let expected_intersection =
Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::from(IntervalLowerOpenUpperClosed::new(1.0, 2.0)),
));
assert_eq!(intersection, expected_intersection);
}
#[test]
fn intersection_closed_right_half_open() {
let a = IntervalClosed::new(0.0, 2.0);
let b = IntervalLowerClosedUpperOpen::new(1.0, 3.0);
let intersection = a.intersection(&b).unwrap();
let expected_intersection =
Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::from(IntervalClosed::new(1.0, 2.0)),
));
assert_eq!(intersection, expected_intersection);
}
#[test]
fn intersection_singleton_closed() {
let a = IntervalSingleton::new(1.0);
let b = IntervalClosed::new(0.0, 2.0);
let intersection = a.intersection(&b).unwrap();
let expected_intersection = Interval::FiniteLength(IntervalFiniteLength::from(a));
assert_eq!(intersection, expected_intersection);
}
#[test]
fn intersection_open_left_half_open() {
let a = IntervalOpen::new(0.0, 2.0);
let b = IntervalLowerOpenUpperClosed::new(1.0, 3.0);
let intersection = a.intersection(&b).unwrap();
let expected_intersection =
Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::from(IntervalOpen::new(1.0, 2.0)),
));
assert_eq!(intersection, expected_intersection);
}
#[test]
fn intersection_left_half_open_right_half_open() {
let a = IntervalLowerOpenUpperClosed::new(0.0, 2.0);
let b = IntervalLowerClosedUpperOpen::new(1.0, 3.0);
let intersection = a.intersection(&b).unwrap();
let expected_intersection =
Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::from(IntervalClosed::new(1.0, 2.0)),
));
assert_eq!(intersection, expected_intersection);
}
#[test]
fn intersection_disjoint() {
let a = IntervalClosed::new(0.0, 1.0);
let b = IntervalClosed::new(2.0, 3.0);
let intersection = a.intersection(&b);
assert!(intersection.is_none());
}
#[test]
fn intersection_disjoint_open_open() {
let a = IntervalOpen::new(0.0, 1.0);
let b = IntervalOpen::new(2.0, 3.0);
let intersection = a.intersection(&b);
assert!(
intersection.is_none(),
"Disjoint open intervals should have empty intersection"
);
}
#[test]
fn intersection_open_open_touching_boundary() {
let a = IntervalOpen::new(0.0, 1.0);
let b = IntervalOpen::new(1.0, 2.0);
let intersection = a.intersection(&b);
assert!(
intersection.is_none(),
"Open intervals touching at boundary should have empty intersection"
);
}
#[test]
fn intersection_singleton_open() {
let a = IntervalSingleton::new(1.0);
let b = IntervalOpen::new(0.0, 2.0);
let intersection = a.intersection(&b).unwrap();
let expected_intersection = Interval::FiniteLength(IntervalFiniteLength::from(a));
assert_eq!(intersection, expected_intersection);
}
#[test]
fn intersection_unbounded_closed() {
let a = IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
IntervalLowerUnboundedUpperUnbounded::new(),
);
let b = IntervalClosed::new(0.0, 2.0);
let intersection = a.intersection(&b).unwrap();
let expected_intersection = Interval::FiniteLength(
IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::from(b)),
);
assert_eq!(intersection, expected_intersection);
}
#[test]
fn intersection_unbounded_unbounded() {
let a = IntervalInfiniteLength::<f64>::LowerUnboundedUpperUnbounded(
IntervalLowerUnboundedUpperUnbounded::new(),
);
let b = IntervalInfiniteLength::<f64>::LowerUnboundedUpperUnbounded(
IntervalLowerUnboundedUpperUnbounded::new(),
);
let intersection = a.intersection(&b).unwrap();
let expected_intersection =
Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
IntervalLowerUnboundedUpperUnbounded::new(),
));
assert_eq!(intersection, expected_intersection);
}
#[test]
fn intersection_subintervals() {
type S = SubIntervalInPartition<IntervalClosed<f64>>;
let a = S::First(IntervalLowerClosedUpperOpen::new(0.0, 2.0));
let b = S::Last(IntervalClosed::new(1.0, 2.0));
let intersection = a.intersection(&b).unwrap();
let expected_intersection =
Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::from(IntervalLowerClosedUpperOpen::new(1.0, 2.0)),
));
assert_eq!(intersection, expected_intersection);
}
#[test]
fn intersection_none() {
let i1 = IntervalClosed::new(0.0, 1.0);
let i2 = IntervalClosed::new(2.0, 3.0);
assert!(i1.intersection(&i2).is_none());
}
#[test]
fn intersection_singleton() {
let i1 = IntervalClosed::new(0.0, 1.0);
let i2 = IntervalClosed::new(1.0, 2.0);
let intersection = i1.intersection(&i2).unwrap();
let expected_intersection =
Interval::FiniteLength(IntervalFiniteLength::from(IntervalSingleton::new(1.0)));
assert_eq!(intersection, expected_intersection);
}
#[test]
fn intersection_01() {
let lower_bound = 0.;
let upper_bound_0 = 1.;
let upper_bound_1 = 0.5;
let closed_0 = IntervalClosed::new(lower_bound, upper_bound_0); let closed_1 = IntervalClosed::new(lower_bound, upper_bound_1);
let i0: IntervalFinitePositiveLength<_> = closed_0
.intersection(&closed_1)
.unwrap()
.try_into()
.unwrap(); assert_eq!(i0.lower_bound_value(), &0.);
assert_eq!(i0.upper_bound_value(), &0.5);
let open_1 = IntervalOpen::new(lower_bound, upper_bound_1);
let i1: IntervalFinitePositiveLength<_> =
closed_0.intersection(&open_1).unwrap().try_into().unwrap(); assert_eq!(i1.lower_bound_value(), &0.);
assert_eq!(i1.upper_bound_value(), &0.5);
let singleton_1 = IntervalSingleton::new(lower_bound);
let i2 = singleton_1.intersection(&closed_1).unwrap();
match i2 {
Interval::InfiniteLength(_) => panic!("The interval should be a singleton!"),
Interval::FiniteLength(interval_finite_length) => match interval_finite_length {
IntervalFiniteLength::PositiveLength(_) => {
panic!("The interval should be a singleton!")
}
IntervalFiniteLength::ZeroLength(ref singleton) => {
assert_eq!(singleton.value(), &0.);
}
},
};
let i3 = singleton_1.intersection(&open_1);
assert!(i3.is_none());
}
#[test]
fn intersection_02() {
let lower_bound = 0.;
let upper_bound = 0.5;
let open_1 = IntervalOpen::new(lower_bound, upper_bound);
let singleton_1 = IntervalSingleton::new(lower_bound);
let i3 = singleton_1.intersection(&open_1);
assert!(i3.is_none());
}
#[test]
fn intersection_unbounded_lower_open_upper() {
let a = IntervalLowerUnboundedUpperOpen::new(5.0);
let b = IntervalClosed::new(0.0, 10.0);
let intersection = a.intersection(&b).unwrap();
match intersection {
Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::LowerClosedUpperOpen(interval),
)) => {
assert_eq!(interval.lower_bound_value(), &0.0);
assert_eq!(interval.upper_bound_value(), &5.0);
}
_ => panic!("Expected [0.0, 5.0) interval"),
}
}
#[test]
fn intersection_unbounded_lower_open_upper_disjoint() {
let a = IntervalLowerUnboundedUpperOpen::new(0.0);
let b = IntervalClosed::new(1.0, 10.0);
let intersection = a.intersection(&b);
assert!(intersection.is_none());
}
#[test]
fn intersection_unbounded_lower_closed_upper() {
let a = IntervalLowerUnboundedUpperClosed::new(5.0);
let b = IntervalClosed::new(0.0, 10.0);
let intersection = a.intersection(&b).unwrap();
match intersection {
Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::Closed(interval),
)) => {
assert_eq!(interval.lower_bound_value(), &0.0);
assert_eq!(interval.upper_bound_value(), &5.0);
}
_ => panic!("Expected [0.0, 5.0] interval"),
}
}
#[test]
fn intersection_open_lower_unbounded_upper() {
let a = IntervalLowerOpenUpperUnbounded::new(3.0);
let b = IntervalClosed::new(0.0, 10.0);
let intersection = a.intersection(&b).unwrap();
match intersection {
Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::LowerOpenUpperClosed(interval),
)) => {
assert_eq!(interval.lower_bound_value(), &3.0);
assert_eq!(interval.upper_bound_value(), &10.0);
}
_ => panic!("Expected (3.0, 10.0] interval"),
}
}
#[test]
fn intersection_open_lower_unbounded_upper_disjoint() {
let a = IntervalLowerOpenUpperUnbounded::new(10.0);
let b = IntervalClosed::new(0.0, 5.0);
let intersection = a.intersection(&b);
assert!(intersection.is_none());
}
#[test]
fn intersection_closed_lower_unbounded_upper() {
let a = IntervalLowerClosedUpperUnbounded::new(3.0);
let b = IntervalClosed::new(0.0, 10.0);
let intersection = a.intersection(&b).unwrap();
match intersection {
Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::Closed(interval),
)) => {
assert_eq!(interval.lower_bound_value(), &3.0);
assert_eq!(interval.upper_bound_value(), &10.0);
}
_ => panic!("Expected [3.0, 10.0] interval"),
}
}
#[test]
fn intersection_half_unbounded_both() {
let a = IntervalLowerClosedUpperUnbounded::new(3.0);
let b = IntervalLowerUnboundedUpperClosed::new(10.0);
let intersection = a.intersection(&b).unwrap();
match intersection {
Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::Closed(interval),
)) => {
assert_eq!(interval.lower_bound_value(), &3.0);
assert_eq!(interval.upper_bound_value(), &10.0);
}
_ => panic!("Expected [3.0, 10.0] interval"),
}
}
#[test]
fn intersection_half_unbounded_both_open() {
let a = IntervalLowerOpenUpperUnbounded::new(3.0);
let b = IntervalLowerUnboundedUpperOpen::new(10.0);
let intersection = a.intersection(&b).unwrap();
match intersection {
Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::Open(interval),
)) => {
assert_eq!(interval.lower_bound_value(), &3.0);
assert_eq!(interval.upper_bound_value(), &10.0);
}
_ => panic!("Expected (3.0, 10.0) interval"),
}
}
#[test]
fn intersection_half_unbounded_disjoint() {
let a = IntervalLowerClosedUpperUnbounded::new(10.0);
let b = IntervalLowerUnboundedUpperClosed::new(5.0);
let intersection = a.intersection(&b);
assert!(intersection.is_none());
}
#[test]
fn intersection_preserves_unbounded_lower_closed() {
let a = IntervalLowerUnboundedUpperClosed::new(10.0);
let b = IntervalLowerUnboundedUpperClosed::new(5.0);
let intersection = a.intersection(&b).unwrap();
match intersection {
Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
interval,
)) => {
assert_eq!(interval.upper_bound_value(), &5.0);
}
_ => panic!("Expected (-∞, 5.0] interval"),
}
}
#[test]
fn intersection_preserves_unbounded_lower_open() {
let a = IntervalLowerUnboundedUpperOpen::new(10.0);
let b = IntervalLowerUnboundedUpperOpen::new(5.0);
let intersection = a.intersection(&b).unwrap();
match intersection {
Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperOpen(
interval,
)) => {
assert_eq!(interval.upper_bound_value(), &5.0);
}
_ => panic!("Expected (-∞, 5.0) interval"),
}
}
#[test]
fn intersection_preserves_closed_lower_unbounded() {
let a = IntervalLowerClosedUpperUnbounded::new(5.0);
let b = IntervalLowerClosedUpperUnbounded::new(3.0);
let intersection = a.intersection(&b).unwrap();
match intersection {
Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
interval,
)) => {
assert_eq!(interval.lower_bound_value(), &5.0);
}
_ => panic!("Expected [5.0, +∞) interval"),
}
}
#[test]
fn intersection_preserves_open_lower_unbounded() {
let a = IntervalLowerOpenUpperUnbounded::new(5.0);
let b = IntervalLowerOpenUpperUnbounded::new(3.0);
let intersection = a.intersection(&b).unwrap();
match intersection {
Interval::InfiniteLength(IntervalInfiniteLength::LowerOpenUpperUnbounded(
interval,
)) => {
assert_eq!(interval.lower_bound_value(), &5.0);
}
_ => panic!("Expected (5.0, +∞) interval"),
}
}
}
mod union {
use super::*;
use crate::intervals::{
Interval, IntervalClosed, IntervalLowerClosedUpperOpen, IntervalLowerOpenUpperClosed,
IntervalOpen, IntervalSingleton,
};
use try_create::New;
#[test]
fn union_disjoint() {
let i1 = IntervalClosed::new(0.0, 1.0);
let i2 = IntervalClosed::new(2.0, 3.0);
let union = i1.union(&i2);
if let IntervalUnion::TwoDisjoint { left, right } = union {
assert_eq!(left, i1.into());
assert_eq!(right, i2.into());
} else {
panic!("Expected disjoint union");
}
}
#[test]
fn union_disjoint_self_on_right() {
let i1 = IntervalClosed::new(3.0, 4.0); let i2 = IntervalClosed::new(0.0, 1.0); let union = i1.union(&i2);
let i1_interval: Interval<_> = i1.into();
let i2_interval: Interval<_> = i2.into();
if let IntervalUnion::TwoDisjoint {
ref left,
ref right,
} = union
{
assert_eq!(left, &i2_interval, "Left should be the interval [0.0, 1.0]");
assert_eq!(
right, &i1_interval,
"Right should be the interval [3.0, 4.0]"
);
} else {
panic!("Expected disjoint union");
}
let i1 = IntervalClosed::new(3.0, 4.0);
let i2 = IntervalClosed::new(0.0, 1.0);
let union_reversed = i2.union(&i1);
assert_eq!(union, union_reversed, "Union should be commutative");
}
#[test]
fn union_disjoint_open_self_on_right() {
let i1 = IntervalOpen::new(5.0, 6.0); let i2 = IntervalOpen::new(1.0, 2.0); let union = i1.union(&i2);
assert!(union.is_two_disjoint(), "Should be two disjoint intervals");
let gap = union.gap();
assert!(gap.is_some(), "Should have a gap");
if let Some(ref gap_interval) = gap {
assert!(gap_interval.contains_point(&3.0), "Gap should contain 3.0");
assert!(
gap_interval.contains_point(&2.0),
"Gap should include lower boundary"
);
assert!(
gap_interval.contains_point(&5.0),
"Gap should include upper boundary"
);
}
let i1_interval: Interval<_> = i1.into();
let i2_interval: Interval<_> = i2.into();
if let IntervalUnion::TwoDisjoint { left, right } = union {
assert_eq!(left, i2_interval, "Left should be (1.0, 2.0)");
assert_eq!(right, i1_interval, "Right should be (5.0, 6.0)");
} else {
panic!("Expected TwoDisjoint variant");
}
}
#[test]
fn union_overlapping() {
let i1 = IntervalClosed::new(0.0, 2.0);
let i2 = IntervalClosed::new(1.0, 3.0);
let union = i1.union(&i2);
let expected: Interval<f64> = IntervalClosed::new(0.0, 3.0).into();
if let IntervalUnion::SingleConnected(connected) = union {
assert_eq!(connected, expected);
} else {
panic!("Expected connected union");
}
}
#[test]
fn union_adjacent_closed_open() {
let i1 = IntervalClosed::new(0.0, 1.0);
let i2 = IntervalOpen::new(1.0, 2.0);
let union = i1.union(&i2);
assert!(union.is_single_connected());
let expected: Interval<f64> = IntervalLowerClosedUpperOpen::new(0.0, 2.0).into();
if let IntervalUnion::SingleConnected(connected) = union {
assert_eq!(connected, expected);
} else {
panic!("Expected connected union");
}
}
#[test]
fn union_adjacent_open_closed() {
let i1 = IntervalOpen::new(0.0, 1.0);
let i2 = IntervalClosed::new(1.0, 2.0);
let union = i1.union(&i2);
assert!(union.is_single_connected());
let expected: Interval<f64> = IntervalLowerOpenUpperClosed::new(0.0, 2.0).into();
if let IntervalUnion::SingleConnected(ref connected) = union {
assert_eq!(connected, &expected);
} else {
panic!("Expected connected union");
}
let connected = union.as_single_connected().unwrap();
assert_eq!(connected, &expected);
assert!(union.as_two_disjoint().is_none());
assert!(union.contains_point(&1.0));
}
#[test]
fn union_adjacent_open_open() {
let i1 = IntervalOpen::new(0.0, 1.0);
let i2 = IntervalOpen::new(1.0, 2.0);
let union = i1.union(&i2);
assert!(union.is_two_disjoint());
let i1: Interval<_> = i1.into();
let i2: Interval<_> = i2.into();
if let IntervalUnion::TwoDisjoint {
ref left,
ref right,
} = union
{
assert_eq!(left, &i1);
assert_eq!(right, &i2);
} else {
panic!("Expected disjoint union because of the gap at 1.0");
}
let (left, right) = union.as_two_disjoint().unwrap();
assert_eq!(left, &i1);
assert_eq!(right, &i2);
assert!(union.as_single_connected().is_none());
assert!(!union.contains_point(&1.0));
}
#[test]
fn union_disjoint_open_open() {
let i1 = IntervalOpen::new(0.0, 1.0);
let i2 = IntervalOpen::new(3.0, 4.0);
let union = i1.union(&i2);
assert!(union.is_two_disjoint(), "Expected two disjoint intervals");
let gap = union.gap();
assert!(
gap.is_some(),
"Should have a gap between disjoint intervals"
);
if let Some(ref gap_interval) = gap {
assert!(
gap_interval.contains_point(&2.0),
"Gap should contain point 2.0"
);
assert!(
gap_interval.contains_point(&1.0),
"Gap should include lower boundary 1.0"
);
assert!(
gap_interval.contains_point(&3.0),
"Gap should include upper boundary 3.0"
);
assert!(
!gap_interval.contains_point(&0.5),
"Gap should not contain points in left interval"
);
assert!(
!gap_interval.contains_point(&3.5),
"Gap should not contain points in right interval"
);
}
let i1_interval: Interval<_> = i1.into();
let i2_interval: Interval<_> = i2.into();
if let IntervalUnion::TwoDisjoint { left, right } = union {
assert_eq!(left, i1_interval);
assert_eq!(right, i2_interval);
} else {
panic!("Expected TwoDisjoint variant");
}
let i1 = IntervalOpen::new(0.0, 1.0);
let i2 = IntervalOpen::new(3.0, 4.0);
let union = i1.union(&i2);
assert!(
union.contains_point(&0.5),
"Should contain point in first interval"
);
assert!(
union.contains_point(&3.5),
"Should contain point in second interval"
);
assert!(
!union.contains_point(&2.0),
"Should not contain point in gap"
);
assert!(
!union.contains_point(&1.0),
"Should not contain excluded boundary"
);
assert!(
!union.contains_point(&3.0),
"Should not contain excluded boundary"
);
}
#[test]
fn union_disjoint_open_half_open() {
let i1 = IntervalOpen::new(0.0, 1.0);
let i2 = IntervalLowerClosedUpperOpen::new(2.0, 3.0);
let union = i1.union(&i2);
assert!(union.is_two_disjoint());
if let IntervalUnion::TwoDisjoint { left, right } = union {
assert_eq!(left, i1.into());
assert_eq!(right, i2.into());
} else {
panic!("Expected disjoint union");
}
}
#[test]
fn union_one_contains_other() {
let i1 = IntervalClosed::new(0.0, 3.0);
let i2 = IntervalClosed::new(1.0, 2.0);
let union = i1.union(&i2);
if let IntervalUnion::SingleConnected(connected) = union {
assert_eq!(connected, i1.into());
} else {
panic!("Expected connected union");
}
}
#[test]
fn union_with_singleton_inside() {
let i1 = IntervalClosed::new(0.0, 2.0);
let i2 = IntervalSingleton::new(1.0);
let union = i1.union(&i2);
if let IntervalUnion::SingleConnected(connected) = union {
assert_eq!(connected, i1.into());
} else {
panic!("Expected connected union");
}
}
#[test]
fn union_with_singleton_at_boundary_closed() {
let i1 = IntervalClosed::new(0.0, 1.0);
let i2 = IntervalSingleton::new(1.0);
let union = i1.union(&i2);
if let IntervalUnion::SingleConnected(connected) = union {
assert_eq!(connected, i1.into());
} else {
panic!("Expected connected union");
}
}
#[test]
fn union_with_singleton_at_boundary_open() {
let i1 = IntervalLowerClosedUpperOpen::new(0.0, 1.0);
let i2 = IntervalSingleton::new(1.0);
let union = i1.union(&i2);
let expected: Interval<f64> = IntervalClosed::new(0.0, 1.0).into();
if let IntervalUnion::SingleConnected(connected) = union {
assert_eq!(connected, expected);
} else {
panic!("Expected connected union");
}
}
#[test]
fn union_with_singleton_outside() {
let i1 = IntervalClosed::new(0.0, 1.0);
let i2 = IntervalSingleton::new(2.0);
let union = i1.union(&i2);
if let IntervalUnion::TwoDisjoint { left, right } = union {
assert_eq!(left, i1.into());
assert_eq!(right, i2.into());
} else {
panic!("Expected disjoint union");
}
}
#[test]
fn union_unbounded_overlapping() {
let i1 = IntervalLowerClosedUpperUnbounded::new(0.0);
let i2 = IntervalLowerClosedUpperUnbounded::new(1.0);
let union = i1.union(&i2);
if let IntervalUnion::SingleConnected(connected) = union {
assert_eq!(connected, i1.into());
} else {
panic!("Expected connected union");
}
}
#[test]
fn union_lower_unbounded_with_upper_unbounded() {
let i1 = IntervalLowerUnboundedUpperClosed::new(0.0);
let i2 = IntervalLowerClosedUpperUnbounded::new(1.0);
let union = i1.union(&i2);
if let IntervalUnion::TwoDisjoint { left, right } = union {
assert_eq!(left, i1.into());
assert_eq!(right, i2.into());
} else {
panic!("Expected disjoint union for (-inf, 0] U [1, inf)");
}
}
#[test]
fn union_lower_unbounded_with_upper_unbounded_overlapping() {
let i1 = IntervalLowerUnboundedUpperClosed::new(2.0);
let i2 = IntervalLowerClosedUpperUnbounded::new(1.0);
let union = i1.union(&i2);
let expected: Interval<f64> = IntervalLowerUnboundedUpperUnbounded::new().into();
if let IntervalUnion::SingleConnected(connected) = union {
assert_eq!(connected, expected);
} else {
panic!("Expected connected union resulting in (-inf, inf)");
}
}
#[test]
fn union_finite_with_unbounded() {
let i1 = IntervalClosed::new(0.0, 5.0);
let i2 = IntervalLowerClosedUpperUnbounded::new(3.0);
let union = i1.union(&i2);
let expected: Interval<f64> = IntervalLowerClosedUpperUnbounded::new(0.0).into();
if let IntervalUnion::SingleConnected(connected) = union {
assert_eq!(connected, expected);
} else {
panic!("Expected connected union");
}
}
#[test]
fn union_identical_intervals() {
let i1 = IntervalClosed::new(0.0, 1.0);
let i2 = IntervalClosed::new(0.0, 1.0);
let union = i1.union(&i2);
if let IntervalUnion::SingleConnected(connected) = union {
assert_eq!(connected, i1.into());
} else {
panic!("Expected connected union");
}
}
#[test]
fn union_different_bound_types_contained() {
let i1 = IntervalClosed::new(0.0, 2.0);
let i2 = IntervalOpen::new(0.0, 2.0);
let union = i1.union(&i2);
if let IntervalUnion::SingleConnected(connected) = union {
assert_eq!(connected, i1.into());
} else {
panic!("Expected connected union");
}
}
#[test]
fn union_different_bound_types_overlapping() {
let i1 = IntervalLowerClosedUpperOpen::new(0.0, 2.0); let i2 = IntervalLowerOpenUpperClosed::new(1.0, 3.0); let union = i1.union(&i2);
let expected: Interval<f64> = IntervalClosed::new(0.0, 3.0).into();
if let IntervalUnion::SingleConnected(connected) = union {
assert_eq!(connected, expected);
} else {
panic!("Expected connected union");
}
}
#[test]
fn union_with_fully_unbounded() {
let i1 = IntervalClosed::new(0.0, 1.0);
let i2 = IntervalLowerUnboundedUpperUnbounded::new();
let union = i1.union(&i2);
if let IntervalUnion::SingleConnected(connected) = union {
assert_eq!(connected, i2.into());
} else {
panic!("Expected connected union");
}
}
mod gap {
use super::*;
use num_valid::{RealNative64StrictFinite, functions::Abs};
type Real = RealNative64StrictFinite;
#[test]
fn gap_with_disjoint_intervals_positive_gap() {
let interval1 =
IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(2.0).unwrap());
let interval2 =
IntervalClosed::new(Real::try_new(4.0).unwrap(), Real::try_new(5.0).unwrap());
let union = interval1.union(&interval2);
let gap = union.gap().unwrap();
match gap {
IntervalFinitePositiveLength::Open(open_gap) => {
assert_eq!(open_gap.lower_bound_value(), &2.0);
assert_eq!(open_gap.upper_bound_value(), &4.0);
assert_eq!(open_gap.length().as_ref(), &2.0);
}
_ => panic!("Expected open interval for gap"),
}
}
#[test]
fn gap_with_disjoint_intervals_small_gap() {
let interval1 =
IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
let interval2 =
IntervalClosed::new(Real::try_new(1.5).unwrap(), Real::try_new(2.0).unwrap());
let union = interval1.union(&interval2);
let gap = union.gap().unwrap();
match gap {
IntervalFinitePositiveLength::Open(open_gap) => {
assert_eq!(open_gap.lower_bound_value(), &1.0);
assert_eq!(open_gap.upper_bound_value(), &1.5);
assert_eq!(open_gap.length().as_ref(), &0.5);
}
_ => panic!("Expected open interval for gap"),
}
}
#[test]
fn gap_with_disjoint_half_open_intervals() {
let interval1 = IntervalLowerClosedUpperOpen::new(
Real::try_new(0.0).unwrap(),
Real::try_new(1.0).unwrap(),
);
let interval2 = IntervalLowerOpenUpperClosed::new(
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap(),
);
let union = interval1.union(&interval2);
let gap = union.gap().unwrap();
match gap {
IntervalFinitePositiveLength::Closed(closed_gap) => {
assert_eq!(closed_gap.lower_bound_value(), &1.0);
assert_eq!(closed_gap.upper_bound_value(), &2.0);
assert_eq!(closed_gap.length().as_ref(), &1.0);
}
_ => panic!("Expected closed interval for gap"),
}
}
#[test]
fn gap_with_touching_intervals_no_gap() {
let interval1 =
IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(2.0).unwrap());
let interval2 =
IntervalClosed::new(Real::try_new(2.0).unwrap(), Real::try_new(3.0).unwrap());
let union = interval1.union(&interval2);
assert!(union.gap().is_none());
}
#[test]
fn gap_with_overlapping_intervals_no_gap() {
let interval1 =
IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(3.0).unwrap());
let interval2 =
IntervalClosed::new(Real::try_new(2.0).unwrap(), Real::try_new(4.0).unwrap());
let union = interval1.union(&interval2);
assert!(union.gap().is_none());
}
#[test]
fn gap_with_open_intervals_disjoint() {
let interval1 =
IntervalOpen::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
let interval2 =
IntervalOpen::new(Real::try_new(2.0).unwrap(), Real::try_new(3.0).unwrap());
let union = interval1.union(&interval2);
let gap = union.gap().unwrap();
match gap {
IntervalFinitePositiveLength::Closed(closed_gap) => {
assert_eq!(closed_gap.lower_bound_value(), &1.0);
assert_eq!(closed_gap.upper_bound_value(), &2.0);
assert_eq!(closed_gap.length().as_ref(), &1.0);
}
_ => panic!("Expected closed interval for gap"),
}
}
#[test]
fn gap_with_single_connected_interval() {
let interval =
IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(5.0).unwrap());
let union = interval.union(&interval);
assert!(union.gap().is_none());
}
#[test]
fn gap_with_very_small_gap() {
let epsilon = PositiveRealScalar::try_new(Real::try_new(1e-10).unwrap()).unwrap();
let interval1 =
IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
let interval2 = IntervalClosed::new(
Real::try_new(1.0).unwrap() + epsilon.as_ref(),
Real::try_new(2.0).unwrap(),
);
let union = interval1.union(&interval2);
let gap = union.gap().unwrap();
match gap {
IntervalFinitePositiveLength::Open(open_gap) => {
assert_eq!(open_gap.lower_bound_value(), &1.0);
assert_eq!(
open_gap.upper_bound_value(),
&(Real::try_new(1.0).unwrap() + epsilon.as_ref())
);
more_asserts::assert_lt!(
(open_gap.length().into_inner() - epsilon.as_ref()).abs(),
1.0e-16
);
}
_ => panic!("Expected open interval for very small gap"),
}
}
#[test]
fn gap_with_negative_intervals() {
let interval1 =
IntervalClosed::new(Real::try_new(-5.0).unwrap(), Real::try_new(-3.0).unwrap());
let interval2 =
IntervalClosed::new(Real::try_new(-1.0).unwrap(), Real::try_new(1.0).unwrap());
let union = IntervalUnion::TwoDisjoint {
left: interval1.into(),
right: interval2.into(),
};
let gap = union.gap().unwrap();
match gap {
IntervalFinitePositiveLength::Open(open_gap) => {
assert_eq!(open_gap.lower_bound_value(), &-3.0);
assert_eq!(open_gap.upper_bound_value(), &-1.0);
assert_eq!(open_gap.length().as_ref(), &2.0);
}
_ => panic!("Expected open interval for gap"),
}
}
#[test]
fn gap_with_mixed_boundary_types() {
let interval1 = IntervalLowerOpenUpperClosed::new(
Real::try_new(0.0).unwrap(),
Real::try_new(1.0).unwrap(),
);
let interval2 = IntervalLowerClosedUpperOpen::new(
Real::try_new(3.0).unwrap(),
Real::try_new(4.0).unwrap(),
);
let union = IntervalUnion::TwoDisjoint {
left: interval1.into(),
right: interval2.into(),
};
let gap = union.gap().unwrap();
match gap {
IntervalFinitePositiveLength::Open(open_gap) => {
assert_eq!(open_gap.lower_bound_value(), &1.0);
assert_eq!(open_gap.upper_bound_value(), &3.0);
assert_eq!(open_gap.length().as_ref(), &2.0);
}
_ => panic!("Expected open interval for gap"),
}
}
#[test]
fn gap_preserves_boundary_semantics() {
let interval1 = IntervalLowerClosedUpperOpen::new(
Real::try_new(0.0).unwrap(),
Real::try_new(1.0).unwrap(),
);
let interval2 = IntervalLowerOpenUpperClosed::new(
Real::try_new(2.0).unwrap(),
Real::try_new(3.0).unwrap(),
);
let union = IntervalUnion::TwoDisjoint {
left: interval1.into(),
right: interval2.into(),
};
let gap = union.gap().unwrap();
match gap {
IntervalFinitePositiveLength::Closed(closed_gap) => {
assert_eq!(closed_gap.lower_bound_value(), &1.0);
assert_eq!(closed_gap.upper_bound_value(), &2.0);
assert!(closed_gap.contains_point(&Real::try_new(1.0).unwrap()));
assert!(closed_gap.contains_point(&Real::try_new(2.0).unwrap()));
}
_ => panic!("Expected closed interval for gap"),
}
}
#[test]
fn gap_with_singleton_intervals() {
let singleton1 = IntervalSingleton::new(Real::try_new(1.0).unwrap());
let singleton2 = IntervalSingleton::new(Real::try_new(3.0).unwrap());
let union = IntervalUnion::TwoDisjoint {
left: singleton1.into(),
right: singleton2.into(),
};
let gap = union.gap().unwrap();
match gap {
IntervalFinitePositiveLength::Open(open_gap) => {
assert_eq!(open_gap.lower_bound_value(), &1.0);
assert_eq!(open_gap.upper_bound_value(), &3.0);
assert_eq!(open_gap.length().as_ref(), &2.0);
assert!(!open_gap.contains_point(&Real::try_new(1.0).unwrap()));
assert!(!open_gap.contains_point(&Real::try_new(3.0).unwrap()));
}
_ => panic!("Expected open interval for gap between singletons"),
}
}
}
}
}