#![deny(rustdoc::broken_intra_doc_links)]
use crate::bounds::{
IntervalBound,
markers::{Closed, Lower, Open, Upper},
traits::{BoundChecks, BoundSide, BoundSideChecks, BoundTypeChecks, ValueWithinBound},
};
use duplicate::duplicate_item;
use num_valid::RealScalar;
use serde::{Deserialize, Serialize};
use std::{cmp::Ordering, fmt::Debug};
use try_create::{IntoInner, New};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
pub enum IntervalBoundRuntime<RealType: RealScalar, Side: BoundSide> {
Open(IntervalBound<RealType, Side, Open>),
Closed(IntervalBound<RealType, Side, Closed>),
}
impl<RealType: RealScalar, Side: BoundSide> IntervalBoundRuntime<RealType, Side> {
#[must_use]
pub fn new_open(value: RealType) -> Self {
Self::Open(IntervalBound::<RealType, Side, Open>::new(value))
}
#[must_use]
pub fn new_closed(value: RealType) -> Self {
Self::Closed(IntervalBound::<RealType, Side, Closed>::new(value))
}
#[inline(always)]
pub fn is_closed_variant(&self) -> bool {
matches!(self, Self::Closed(_))
}
#[inline(always)]
pub fn is_open_variant(&self) -> bool {
matches!(self, Self::Open(_))
}
#[inline(always)]
pub fn flip_bound_type(self) -> Self {
match self {
Self::Open(bound) => Self::Closed(IntervalBound::new(bound.into_inner())),
Self::Closed(bound) => Self::Open(IntervalBound::new(bound.into_inner())),
}
}
#[inline(always)]
pub fn flip_bound_side(self) -> IntervalBoundRuntime<RealType, Side::Opposite> {
match self {
Self::Open(bound) => IntervalBoundRuntime::Open(IntervalBound::new(bound.into_inner())),
Self::Closed(bound) => {
IntervalBoundRuntime::Closed(IntervalBound::new(bound.into_inner()))
}
}
}
#[inline(always)]
pub fn flip_bound_side_and_type(self) -> IntervalBoundRuntime<RealType, Side::Opposite> {
match self {
Self::Open(bound) => {
IntervalBoundRuntime::Closed(IntervalBound::new(bound.into_inner()))
}
Self::Closed(bound) => {
IntervalBoundRuntime::Open(IntervalBound::new(bound.into_inner()))
}
}
}
}
pub type LowerBoundRuntime<RealType> = IntervalBoundRuntime<RealType, Lower>;
pub type UpperBoundRuntime<RealType> = IntervalBoundRuntime<RealType, Upper>;
impl<RealType: RealScalar, Side: BoundSide> BoundTypeChecks
for IntervalBoundRuntime<RealType, Side>
{
#[inline(always)]
fn includes_boundary(&self) -> bool {
matches!(self, IntervalBoundRuntime::Closed(_))
}
}
impl<RealType: RealScalar> BoundSideChecks for LowerBoundRuntime<RealType> {
#[inline(always)]
fn is_upper_bound(&self) -> bool {
false
}
}
impl<RealType: RealScalar> BoundSideChecks for UpperBoundRuntime<RealType> {
#[inline(always)]
fn is_upper_bound(&self) -> bool {
true
}
}
impl<RealType: RealScalar, Side: BoundSide> ValueWithinBound
for IntervalBoundRuntime<RealType, Side>
where
IntervalBound<RealType, Side, Open>: ValueWithinBound<RealType = RealType>,
IntervalBound<RealType, Side, Closed>: ValueWithinBound<RealType = RealType>,
{
type RealType = RealType;
#[inline(always)]
fn value_within_bound(&self, value: &RealType) -> bool {
match self {
IntervalBoundRuntime::Open(bound) => bound.value_within_bound(value),
IntervalBoundRuntime::Closed(bound) => bound.value_within_bound(value),
}
}
}
impl<RealType: RealScalar, Side: BoundSide> AsRef<RealType>
for IntervalBoundRuntime<RealType, Side>
{
#[inline(always)]
fn as_ref(&self) -> &RealType {
match self {
IntervalBoundRuntime::Open(bound) => bound.as_ref(),
IntervalBoundRuntime::Closed(bound) => bound.as_ref(),
}
}
}
impl<RealType: RealScalar, Side: BoundSide> IntoInner for IntervalBoundRuntime<RealType, Side> {
type InnerType = RealType;
#[inline(always)]
fn into_inner(self) -> RealType {
match self {
IntervalBoundRuntime::Open(bound) => bound.into_inner(),
IntervalBoundRuntime::Closed(bound) => bound.into_inner(),
}
}
}
impl<RealType: RealScalar, Side: BoundSide> BoundChecks for IntervalBoundRuntime<RealType, Side> where
Self: BoundSideChecks
{
}
#[duplicate_item(
Type;
[Open];
[Closed];
)]
impl<RealType: RealScalar, Side: BoundSide> From<IntervalBound<RealType, Side, Type>>
for IntervalBoundRuntime<RealType, Side>
{
#[inline(always)]
fn from(bound: IntervalBound<RealType, Side, Type>) -> Self {
IntervalBoundRuntime::Type(bound)
}
}
impl<RealType: RealScalar> PartialOrd for LowerBoundRuntime<RealType> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.as_ref() == other.as_ref() {
Some(match (self, other) {
(LowerBoundRuntime::Closed(_), LowerBoundRuntime::Open(_)) => Ordering::Less,
(LowerBoundRuntime::Open(_), LowerBoundRuntime::Closed(_)) => Ordering::Greater,
_ => Ordering::Equal,
})
} else {
self.as_ref().partial_cmp(other.as_ref())
}
}
}
impl<RealType: RealScalar> PartialOrd for UpperBoundRuntime<RealType> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.as_ref() == other.as_ref() {
Some(match (self, other) {
(UpperBoundRuntime::Closed(_), UpperBoundRuntime::Open(_)) => Ordering::Greater,
(UpperBoundRuntime::Open(_), UpperBoundRuntime::Closed(_)) => Ordering::Less,
_ => Ordering::Equal,
})
} else {
self.as_ref().partial_cmp(other.as_ref())
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::bounds::*;
mod lower_bound {
use super::*;
use crate::bounds::{LowerBoundClosed, LowerBoundOpen};
use std::cmp::Ordering;
#[test]
fn test_interval_lower_bound_equality() {
let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
let closed_3 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));
let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
let open_2 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
assert_eq!(closed_1, closed_2);
assert_eq!(open_1, open_2);
assert_ne!(closed_1, closed_3);
assert_ne!(closed_1, open_1);
}
mod interval_upper_bound {
use super::*;
use crate::bounds::{UpperBoundClosed, UpperBoundOpen};
use std::cmp::Ordering;
#[test]
fn test_interval_upper_bound_equality() {
let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
let closed_3 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));
let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
let open_2 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
assert_eq!(closed_1, closed_2);
assert_eq!(open_1, open_2);
assert_ne!(closed_1, closed_3);
assert_ne!(closed_1, open_1);
}
#[test]
fn test_interval_upper_bound_partial_ord() {
let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));
let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
let open_2 = UpperBoundRuntime::Open(UpperBoundOpen::new(2.0));
assert_eq!(closed_1.partial_cmp(&closed_1), Some(Ordering::Equal));
assert_eq!(closed_1.partial_cmp(&closed_2), Some(Ordering::Less));
assert_eq!(closed_2.partial_cmp(&closed_1), Some(Ordering::Greater));
assert_eq!(open_1.partial_cmp(&open_1), Some(Ordering::Equal));
assert_eq!(open_1.partial_cmp(&open_2), Some(Ordering::Less));
assert_eq!(open_2.partial_cmp(&open_1), Some(Ordering::Greater));
assert_eq!(open_1.partial_cmp(&closed_1), Some(Ordering::Less));
assert_eq!(closed_1.partial_cmp(&open_1), Some(Ordering::Greater));
assert_eq!(open_1.partial_cmp(&closed_2), Some(Ordering::Less));
assert_eq!(closed_1.partial_cmp(&open_2), Some(Ordering::Less));
}
#[test]
fn test_interval_upper_bound_ordering_consistency() {
let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));
let open_2 = UpperBoundRuntime::Open(UpperBoundOpen::new(2.0));
let bounds = vec![
open_1.clone(),
closed_1.clone(),
open_2.clone(),
closed_2.clone(),
];
for i in 0..bounds.len() {
for j in i + 1..bounds.len() {
assert!(bounds[i] < bounds[j],);
assert!(bounds[j] > bounds[i],);
}
}
let mut shuffled = bounds.clone();
shuffled.reverse();
shuffled.sort_by(|a, b| a.partial_cmp(b).unwrap());
assert_eq!(shuffled, bounds);
}
#[test]
fn test_interval_upper_bound_edge_cases() {
let closed_zero = UpperBoundRuntime::Closed(UpperBoundClosed::new(0.0));
let closed_neg_zero = UpperBoundRuntime::Closed(UpperBoundClosed::new(-0.0));
let open_zero = UpperBoundRuntime::Open(UpperBoundOpen::new(0.0));
assert_eq!(closed_zero, closed_neg_zero);
assert_ne!(closed_zero, open_zero);
assert!(open_zero < closed_zero);
let closed_small = UpperBoundRuntime::Closed(UpperBoundClosed::new(1e-15));
let open_small = UpperBoundRuntime::Open(UpperBoundOpen::new(1e-15));
assert!(open_small < closed_small);
}
#[test]
fn test_interval_upper_bound_transitivity() {
let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));
assert!(open_1 < closed_1);
assert!(closed_1 < closed_2);
assert!(open_1 < closed_2); }
#[test]
fn test_interval_upper_bound_reflexivity() {
let closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
let open = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
assert_eq!(closed.partial_cmp(&closed), Some(Ordering::Equal));
assert_eq!(open.partial_cmp(&open), Some(Ordering::Equal));
}
#[test]
fn test_interval_upper_bound_antisymmetry() {
let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));
let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
assert_eq!(closed_1.partial_cmp(&closed_1), Some(Ordering::Equal));
assert_eq!(closed_1.partial_cmp(&closed_2), Some(Ordering::Less));
assert_eq!(closed_2.partial_cmp(&closed_1), Some(Ordering::Greater));
assert_eq!(open_1.partial_cmp(&closed_1), Some(Ordering::Less));
assert_eq!(closed_1.partial_cmp(&open_1), Some(Ordering::Greater));
}
#[test]
fn test_min_upper_bound() {
let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));
assert_eq!(min_upper_bound(closed_1.clone(), open_1.clone()), open_1);
assert_eq!(min_upper_bound(open_1.clone(), closed_1.clone()), open_1);
assert_eq!(
min_upper_bound(closed_1.clone(), closed_2.clone()),
closed_1
);
assert_eq!(
min_upper_bound(closed_2.clone(), closed_1.clone()),
closed_1
);
assert_eq!(
min_upper_bound(closed_1.clone(), closed_1.clone()),
closed_1
);
}
#[test]
fn test_max_upper_bound() {
let closed_1 = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
let open_1 = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
let closed_2 = UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0));
assert_eq!(max_upper_bound(closed_1.clone(), open_1.clone()), closed_1);
assert_eq!(max_upper_bound(open_1.clone(), closed_1.clone()), closed_1);
assert_eq!(
max_upper_bound(closed_1.clone(), closed_2.clone()),
closed_2
);
assert_eq!(
max_upper_bound(closed_2.clone(), closed_1.clone()),
closed_2
);
assert_eq!(
max_upper_bound(closed_1.clone(), closed_1.clone()),
closed_1
);
}
#[test]
fn test_interval_upper_bound_with_negative_values() {
let closed_neg = UpperBoundRuntime::Closed(UpperBoundClosed::new(-5.0));
let open_neg = UpperBoundRuntime::Open(UpperBoundOpen::new(-5.0));
let closed_pos = UpperBoundRuntime::Closed(UpperBoundClosed::new(5.0));
assert!(open_neg < closed_neg);
assert!(closed_neg < closed_pos);
let closed_more_neg = UpperBoundRuntime::Closed(UpperBoundClosed::new(-10.0));
assert!(closed_more_neg < closed_neg);
}
#[test]
fn test_interval_upper_bound_with_large_values() {
let closed_large = UpperBoundRuntime::Closed(UpperBoundClosed::new(1e6));
let open_large = UpperBoundRuntime::Open(UpperBoundOpen::new(1e6));
let closed_larger = UpperBoundRuntime::Closed(UpperBoundClosed::new(1e9));
assert!(open_large < closed_large);
assert!(closed_large < closed_larger);
let closed_small = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
assert!(closed_small < closed_large);
}
#[test]
fn test_interval_upper_bound_boundary_semantics() {
let open_5 = UpperBoundRuntime::Open(UpperBoundOpen::new(5.0));
let closed_5 = UpperBoundRuntime::Closed(UpperBoundClosed::new(5.0));
assert!(open_5 < closed_5);
assert!(closed_5 > open_5);
}
}
#[test]
fn test_interval_lower_bound_partial_ord() {
let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));
let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
let open_2 = LowerBoundRuntime::Open(LowerBoundOpen::new(2.0));
assert_eq!(closed_1.partial_cmp(&closed_1), Some(Ordering::Equal));
assert_eq!(closed_1.partial_cmp(&closed_2), Some(Ordering::Less));
assert_eq!(closed_2.partial_cmp(&closed_1), Some(Ordering::Greater));
assert_eq!(open_1.partial_cmp(&open_1), Some(Ordering::Equal));
assert_eq!(open_1.partial_cmp(&open_2), Some(Ordering::Less));
assert_eq!(open_2.partial_cmp(&open_1), Some(Ordering::Greater));
assert_eq!(closed_1.partial_cmp(&open_1), Some(Ordering::Less));
assert_eq!(open_1.partial_cmp(&closed_1), Some(Ordering::Greater));
assert_eq!(closed_1.partial_cmp(&open_2), Some(Ordering::Less));
assert_eq!(open_1.partial_cmp(&closed_2), Some(Ordering::Less));
}
#[test]
fn test_interval_lower_bound_ordering_consistency() {
let closed_0 = LowerBoundRuntime::Closed(LowerBoundClosed::new(0.0));
let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
let open_0 = LowerBoundRuntime::Open(LowerBoundOpen::new(0.0));
let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
let bounds = vec![
closed_0.clone(),
open_0.clone(),
closed_1.clone(),
open_1.clone(),
];
for i in 0..bounds.len() {
for j in i + 1..bounds.len() {
assert!(bounds[i] < bounds[j],);
assert!(bounds[j] > bounds[i],);
}
}
let mut shuffled = bounds.clone();
shuffled.reverse();
shuffled.sort_by(|a, b| a.partial_cmp(b).unwrap());
assert_eq!(shuffled, bounds);
}
#[test]
fn test_interval_lower_bound_edge_cases() {
let closed_zero = LowerBoundRuntime::Closed(LowerBoundClosed::new(0.0));
let closed_neg_zero = LowerBoundRuntime::Closed(LowerBoundClosed::new(-0.0));
let open_zero = LowerBoundRuntime::Open(LowerBoundOpen::new(0.0));
assert_eq!(closed_zero, closed_neg_zero);
assert_ne!(closed_zero, open_zero);
assert!(closed_zero < open_zero);
let closed_small = LowerBoundRuntime::Closed(LowerBoundClosed::new(1e-15));
let open_small = LowerBoundRuntime::Open(LowerBoundOpen::new(1e-15));
assert!(closed_small < open_small);
}
#[test]
fn test_interval_lower_bound_transitivity() {
let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));
assert!(closed_1 < open_1);
assert!(open_1 < closed_2);
assert!(closed_1 < closed_2); }
#[test]
fn test_interval_lower_bound_reflexivity() {
let closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
let open = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
assert_eq!(closed.partial_cmp(&closed), Some(Ordering::Equal));
assert_eq!(open.partial_cmp(&open), Some(Ordering::Equal));
}
#[test]
fn test_interval_lower_bound_antisymmetry() {
let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));
let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
assert_eq!(closed_1.partial_cmp(&closed_1), Some(Ordering::Equal));
assert_eq!(closed_1.partial_cmp(&closed_2), Some(Ordering::Less));
assert_eq!(closed_2.partial_cmp(&closed_1), Some(Ordering::Greater));
assert_eq!(closed_1.partial_cmp(&open_1), Some(Ordering::Less));
assert_eq!(open_1.partial_cmp(&closed_1), Some(Ordering::Greater));
}
#[test]
fn test_min_lower_bound() {
let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));
assert_eq!(min_lower_bound(closed_1.clone(), open_1.clone()), closed_1);
assert_eq!(min_lower_bound(open_1.clone(), closed_1.clone()), closed_1);
assert_eq!(
min_lower_bound(closed_1.clone(), closed_2.clone()),
closed_1
);
assert_eq!(
min_lower_bound(closed_2.clone(), closed_1.clone()),
closed_1
);
assert_eq!(
min_lower_bound(closed_1.clone(), closed_1.clone()),
closed_1
);
}
#[test]
fn test_max_lower_bound() {
let closed_1 = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
let open_1 = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
let closed_2 = LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0));
assert_eq!(max_lower_bound(closed_1.clone(), open_1.clone()), open_1);
assert_eq!(max_lower_bound(open_1.clone(), closed_1.clone()), open_1);
assert_eq!(
max_lower_bound(closed_1.clone(), closed_2.clone()),
closed_2
);
assert_eq!(
max_lower_bound(closed_2.clone(), closed_1.clone()),
closed_2
);
assert_eq!(
max_lower_bound(closed_1.clone(), closed_1.clone()),
closed_1
);
}
#[test]
fn test_interval_lower_bound_with_negative_values() {
let closed_neg = LowerBoundRuntime::Closed(LowerBoundClosed::new(-5.0));
let open_neg = LowerBoundRuntime::Open(LowerBoundOpen::new(-5.0));
let closed_pos = LowerBoundRuntime::Closed(LowerBoundClosed::new(5.0));
assert!(closed_neg < open_neg);
assert!(open_neg < closed_pos);
let closed_more_neg = LowerBoundRuntime::Closed(LowerBoundClosed::new(-10.0));
assert!(closed_more_neg < closed_neg);
}
#[test]
fn test_interval_lower_bound_with_large_values() {
let closed_large = LowerBoundRuntime::Closed(LowerBoundClosed::new(1e6));
let open_large = LowerBoundRuntime::Open(LowerBoundOpen::new(1e6));
let closed_larger = LowerBoundRuntime::Closed(LowerBoundClosed::new(1e9));
assert!(closed_large < open_large);
assert!(open_large < closed_larger);
let closed_small = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
assert!(closed_small < closed_large);
}
}
mod interval_bounds_runtime {
use super::*;
use crate::intervals::*;
#[test]
fn get_interval_bound_closed() {
let interval = IntervalClosed::new(1.0, 2.0);
let lower = interval.lower_bound_runtime().unwrap();
assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));
let upper = interval.upper_bound_runtime().unwrap();
assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));
}
#[test]
fn get_interval_bound_open() {
let interval = IntervalOpen::new(1.0, 2.0);
let lower = interval.lower_bound_runtime().unwrap();
assert_eq!(lower, LowerBoundRuntime::Open(LowerBoundOpen::new(1.0)));
let upper = interval.upper_bound_runtime().unwrap();
assert_eq!(upper, UpperBoundRuntime::Open(UpperBoundOpen::new(2.0)));
}
#[test]
fn get_interval_bound_left_open_upper_closed() {
let interval = IntervalLowerOpenUpperClosed::new(1.0, 2.0);
let lower = interval.lower_bound_runtime().unwrap();
assert_eq!(lower, LowerBoundRuntime::Open(LowerBoundOpen::new(1.0)));
let upper = interval.upper_bound_runtime().unwrap();
assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));
}
#[test]
fn get_interval_bound_left_closed_upper_open() {
let interval = IntervalLowerClosedUpperOpen::new(1.0, 2.0);
let lower = interval.lower_bound_runtime().unwrap();
assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));
let upper = interval.upper_bound_runtime().unwrap();
assert_eq!(upper, UpperBoundRuntime::Open(UpperBoundOpen::new(2.0)));
}
#[test]
fn get_interval_bound_lower_closed_upper_unbounded() {
let interval = IntervalLowerClosedUpperUnbounded::new(1.0);
let lower = interval.lower_bound_runtime().unwrap();
assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));
let upper = interval.upper_bound_runtime();
assert_eq!(upper, None);
}
#[test]
fn get_interval_bound_lower_open_upper_unbounded() {
let interval = IntervalLowerOpenUpperUnbounded::new(1.0);
let lower = interval.lower_bound_runtime().unwrap();
assert_eq!(lower, LowerBoundRuntime::Open(LowerBoundOpen::new(1.0)));
let upper = interval.upper_bound_runtime();
assert_eq!(upper, None);
}
#[test]
fn get_interval_bound_lower_unbounded_upper_closed() {
let interval = IntervalLowerUnboundedUpperClosed::new(2.0);
let lower = interval.lower_bound_runtime();
assert_eq!(lower, None);
let upper = interval.upper_bound_runtime().unwrap();
assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));
}
#[test]
fn get_interval_bound_lower_unbounded_upper_open() {
let interval = IntervalLowerUnboundedUpperOpen::new(2.0);
let lower = interval.lower_bound_runtime();
assert_eq!(lower, None);
let upper = interval.upper_bound_runtime().unwrap();
assert_eq!(upper, UpperBoundRuntime::Open(UpperBoundOpen::new(2.0)));
}
#[test]
fn get_interval_bound_singleton() {
let interval = IntervalSingleton::new(1.5);
let lower = interval.lower_bound_runtime().unwrap();
assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.5)));
let upper = interval.upper_bound_runtime().unwrap();
assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(1.5)));
}
#[test]
fn get_interval_bound_finite_length() {
let closed = IntervalClosed::new(1.0, 2.0);
let singleton = IntervalSingleton::new(1.5);
let finite =
IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::Closed(closed));
let zero = IntervalFiniteLength::ZeroLength(singleton);
let lower = finite.lower_bound_runtime().unwrap();
let upper = finite.upper_bound_runtime().unwrap();
assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));
assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));
let lower = zero.lower_bound_runtime().unwrap();
let upper = zero.upper_bound_runtime().unwrap();
assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.5)));
assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(1.5)));
}
#[test]
fn get_interval_bound_finite_positive_length() {
use crate::intervals::IntervalFinitePositiveLengthTrait;
let closed = IntervalClosed::new(1.0, 2.0);
let open = IntervalOpen::new(1.0, 2.0);
let left = IntervalLowerOpenUpperClosed::new(1.0, 2.0);
let right = IntervalLowerClosedUpperOpen::new(1.0, 2.0);
let c = IntervalFinitePositiveLength::Closed(closed);
let o = IntervalFinitePositiveLength::Open(open);
let l = IntervalFinitePositiveLength::LowerOpenUpperClosed(left);
let r = IntervalFinitePositiveLength::LowerClosedUpperOpen(right);
let (lower, upper) = (
c.lower_bound_runtime().unwrap(),
c.upper_bound_runtime().unwrap(),
);
assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));
assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));
assert_eq!(c.clone().into_bounds_pair(), (1., 2.));
let (lower, upper) = (
o.lower_bound_runtime().unwrap(),
o.upper_bound_runtime().unwrap(),
);
assert_eq!(lower, LowerBoundRuntime::Open(LowerBoundOpen::new(1.0)));
assert_eq!(upper, UpperBoundRuntime::Open(UpperBoundOpen::new(2.0)));
assert_eq!(o.clone().into_bounds_pair(), (1., 2.));
let (lower, upper) = (
l.lower_bound_runtime().unwrap(),
l.upper_bound_runtime().unwrap(),
);
assert_eq!(lower, LowerBoundRuntime::Open(LowerBoundOpen::new(1.0)));
assert_eq!(upper, UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0)));
assert_eq!(l.clone().into_bounds_pair(), (1., 2.));
let (lower, upper) = (
r.lower_bound_runtime().unwrap(),
r.upper_bound_runtime().unwrap(),
);
assert_eq!(lower, LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)));
assert_eq!(upper, UpperBoundRuntime::Open(UpperBoundOpen::new(2.0)));
assert_eq!(r.clone().into_bounds_pair(), (1., 2.));
}
#[test]
fn get_interval_bound_infinite_length() {
let closed = IntervalLowerClosedUpperUnbounded::new(1.0);
let open = IntervalLowerOpenUpperUnbounded::new(2.0);
let right_closed = IntervalLowerUnboundedUpperClosed::new(3.0);
let right_open = IntervalLowerUnboundedUpperOpen::new(4.0);
let unbounded = IntervalLowerUnboundedUpperUnbounded::<f64>::new();
let c = IntervalInfiniteLength::LowerClosedUpperUnbounded(closed);
let o = IntervalInfiniteLength::LowerOpenUpperUnbounded(open);
let rc = IntervalInfiniteLength::LowerUnboundedUpperClosed(right_closed);
let ro = IntervalInfiniteLength::LowerUnboundedUpperOpen(right_open);
let u = IntervalInfiniteLength::LowerUnboundedUpperUnbounded(unbounded);
let (lower, upper) = (c.lower_bound_runtime(), c.upper_bound_runtime());
assert_eq!(
lower.unwrap(),
LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0))
);
assert_eq!(upper, None);
let (lower, upper) = (o.lower_bound_runtime(), o.upper_bound_runtime());
assert_eq!(
lower.unwrap(),
LowerBoundRuntime::Open(LowerBoundOpen::new(2.0))
);
assert_eq!(upper, None);
let (lower, upper) = (rc.lower_bound_runtime(), rc.upper_bound_runtime());
assert_eq!(lower, None);
assert_eq!(
upper.unwrap(),
UpperBoundRuntime::Closed(UpperBoundClosed::new(3.0))
);
let (lower, upper) = (ro.lower_bound_runtime(), ro.upper_bound_runtime());
assert_eq!(lower, None);
assert_eq!(
upper.unwrap(),
UpperBoundRuntime::Open(UpperBoundOpen::new(4.0))
);
let (lower, upper) = (u.lower_bound_runtime(), u.upper_bound_runtime());
assert_eq!(lower, None);
assert_eq!(upper, None);
}
#[test]
fn get_interval_bound_enum_wrappers() {
let closed = IntervalClosed::new(1.0, 2.0);
let finite =
IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::Closed(closed));
let inf = IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
IntervalLowerUnboundedUpperUnbounded::<f64>::new(),
);
let interval = Interval::FiniteLength(finite.clone());
let interval_inf = Interval::InfiniteLength(inf.clone());
let (lower, upper) = (
interval.lower_bound_runtime(),
interval.upper_bound_runtime(),
);
assert_eq!(
lower.unwrap(),
LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0))
);
assert_eq!(
upper.unwrap(),
UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0))
);
let (lower, upper) = (
interval_inf.lower_bound_runtime(),
interval_inf.upper_bound_runtime(),
);
assert_eq!(lower, None);
assert_eq!(upper, None);
}
#[test]
fn get_interval_bound_subinterval_in_partition() {
type S = SubIntervalInPartition<IntervalClosed<f64>>;
let first = IntervalLowerClosedUpperOpen::new(1.0, 2.0);
let last = IntervalClosed::new(2.0, 3.0);
let s1 = S::First(first);
let s2 = S::Last(last);
let (lower, upper) = (s1.lower_bound_runtime(), s1.upper_bound_runtime());
assert_eq!(
lower,
Some(LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0)))
);
assert_eq!(
upper,
Some(UpperBoundRuntime::Open(UpperBoundOpen::new(2.0)))
);
let (lower, upper) = (s2.lower_bound_runtime(), s2.upper_bound_runtime());
assert_eq!(
lower,
Some(LowerBoundRuntime::Closed(LowerBoundClosed::new(2.0)))
);
assert_eq!(
upper,
Some(UpperBoundRuntime::Closed(UpperBoundClosed::new(3.0)))
);
}
}
mod partial_ord {
use super::*;
#[test]
fn partial_ord_lower_bound_structs() {
let closed1 = LowerBoundClosed::new(1.0);
let closed2 = LowerBoundClosed::new(2.0);
let open1 = LowerBoundOpen::new(1.0);
let open2 = LowerBoundOpen::new(2.0);
assert!(closed1 < closed2);
assert!(open1 < open2);
assert!(closed1 < open2);
assert!(open1 < closed2);
assert!(closed1 < open1); assert!(open2 > closed1);
assert!(closed1 <= closed1);
assert!(open2 >= open2);
assert!(!(closed1 == open1));
}
#[test]
fn partial_ord_upper_bound_structs() {
let closed1 = UpperBoundClosed::new(1.0);
let closed2 = UpperBoundClosed::new(2.0);
let open1 = UpperBoundOpen::new(1.0);
let open2 = UpperBoundOpen::new(2.0);
assert!(closed1 < closed2);
assert!(open1 < open2);
assert!(closed1 < open2);
assert!(open1 < closed2);
assert!(open1 < closed1); assert!(closed2 > open1);
assert!(closed1 <= closed1);
assert!(open2 >= open2);
assert!(!(closed1 == open1));
}
#[test]
fn partial_ord_interval_lower_bound_enum() {
let closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
let open = LowerBoundRuntime::Open(LowerBoundOpen::new(2.0));
assert_eq!(closed.partial_cmp(&open), Some(std::cmp::Ordering::Less));
assert_eq!(open.partial_cmp(&closed), Some(std::cmp::Ordering::Greater));
assert_eq!(closed.partial_cmp(&closed), Some(std::cmp::Ordering::Equal));
assert_eq!(open.partial_cmp(&open), Some(std::cmp::Ordering::Equal));
}
#[test]
fn partial_ord_interval_upper_bound_enum() {
let closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
let open = UpperBoundRuntime::Open(UpperBoundOpen::new(2.0));
assert_eq!(closed.partial_cmp(&open), Some(std::cmp::Ordering::Less));
assert_eq!(open.partial_cmp(&closed), Some(std::cmp::Ordering::Greater));
assert_eq!(closed.partial_cmp(&closed), Some(std::cmp::Ordering::Equal));
assert_eq!(open.partial_cmp(&open), Some(std::cmp::Ordering::Equal));
}
#[test]
fn partial_ord_interval_lower_bound_enum_same_value() {
let closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0));
let open = LowerBoundRuntime::Open(LowerBoundOpen::new(1.0));
assert_eq!(closed.partial_cmp(&open), Some(std::cmp::Ordering::Less));
assert_eq!(open.partial_cmp(&closed), Some(std::cmp::Ordering::Greater));
}
#[test]
fn partial_ord_interval_upper_bound_enum_same_value() {
let closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(1.0));
let open = UpperBoundRuntime::Open(UpperBoundOpen::new(1.0));
assert_eq!(open.partial_cmp(&closed), Some(std::cmp::Ordering::Less));
assert_eq!(closed.partial_cmp(&open), Some(std::cmp::Ordering::Greater));
}
}
mod bounds_comparisons {
use super::*;
use try_create::TryNew;
type Real = f64;
#[test]
fn test_interval_lower_bound_comparison() {
let lower_bound_closed_5 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
let lower_bound_open_5 =
LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));
let lower_bound_closed_3 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(3.0).unwrap()));
let lower_bound_open_3 =
LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(3.0).unwrap()));
let lower_bound_closed_7 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(7.0).unwrap()));
assert_eq!(
lower_bound_closed_3.partial_cmp(&lower_bound_closed_5),
Some(Ordering::Less)
);
assert_eq!(
lower_bound_closed_5.partial_cmp(&lower_bound_closed_3),
Some(Ordering::Greater)
);
assert_eq!(
lower_bound_closed_5.partial_cmp(&lower_bound_closed_5),
Some(Ordering::Equal)
);
assert_eq!(
lower_bound_closed_5.partial_cmp(&lower_bound_open_5),
Some(Ordering::Less)
);
assert_eq!(
lower_bound_open_5.partial_cmp(&lower_bound_closed_5),
Some(Ordering::Greater)
);
assert_eq!(
lower_bound_closed_3.partial_cmp(&lower_bound_open_5),
Some(Ordering::Less)
);
assert_eq!(
lower_bound_open_3.partial_cmp(&lower_bound_closed_5),
Some(Ordering::Less)
);
assert_eq!(
lower_bound_closed_5.partial_cmp(&lower_bound_open_3),
Some(Ordering::Greater)
);
assert_eq!(
lower_bound_closed_3.partial_cmp(&lower_bound_closed_7),
Some(Ordering::Less)
);
assert_eq!(
lower_bound_open_3.partial_cmp(&lower_bound_closed_7),
Some(Ordering::Less)
);
}
#[test]
fn test_interval_upper_bound_comparison() {
let upper_bound_closed_5 =
UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
let upper_bound_open_5 =
UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(5.0).unwrap()));
let upper_bound_closed_3 =
UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(3.0).unwrap()));
let upper_bound_open_3 =
UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(3.0).unwrap()));
let upper_bound_closed_7 =
UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(7.0).unwrap()));
assert_eq!(
upper_bound_closed_3.partial_cmp(&upper_bound_closed_5),
Some(Ordering::Less)
);
assert_eq!(
upper_bound_closed_5.partial_cmp(&upper_bound_closed_3),
Some(Ordering::Greater)
);
assert_eq!(
upper_bound_closed_5.partial_cmp(&upper_bound_closed_5),
Some(Ordering::Equal)
);
assert_eq!(
upper_bound_open_5.partial_cmp(&upper_bound_closed_5),
Some(Ordering::Less)
);
assert_eq!(
upper_bound_closed_5.partial_cmp(&upper_bound_open_5),
Some(Ordering::Greater)
);
assert_eq!(
upper_bound_closed_3.partial_cmp(&upper_bound_open_5),
Some(Ordering::Less)
);
assert_eq!(
upper_bound_open_3.partial_cmp(&upper_bound_closed_5),
Some(Ordering::Less)
);
assert_eq!(
upper_bound_closed_5.partial_cmp(&upper_bound_open_3),
Some(Ordering::Greater)
);
assert_eq!(
upper_bound_closed_3.partial_cmp(&upper_bound_closed_7),
Some(Ordering::Less)
);
assert_eq!(
upper_bound_open_3.partial_cmp(&upper_bound_closed_7),
Some(Ordering::Less)
);
}
#[test]
fn test_lower_bound_semantics() {
let closed_5 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
let open_5 = LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));
more_asserts::assert_lt!(closed_5, open_5);
more_asserts::assert_ge!(open_5, closed_5);
assert_ne!(closed_5, open_5);
}
#[test]
fn test_upper_bound_semantics() {
let closed_5 =
UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
let open_5 = UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(5.0).unwrap()));
more_asserts::assert_lt!(open_5, closed_5);
more_asserts::assert_ge!(closed_5, open_5);
assert_ne!(open_5, closed_5);
}
#[test]
fn test_mathematical_consistency() {
let lower_closed_1 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(1.0).unwrap()));
let lower_open_1 =
LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(1.0).unwrap()));
let upper_open_2 =
UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(2.0).unwrap()));
let upper_closed_2 =
UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(2.0).unwrap()));
assert!(lower_closed_1 < lower_open_1);
assert!(upper_open_2 < upper_closed_2);
}
#[test]
fn test_reflexivity() {
let lower_closed =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
let lower_open =
LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));
let upper_closed =
UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
let upper_open =
UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(5.0).unwrap()));
assert_eq!(
lower_closed.partial_cmp(&lower_closed),
Some(Ordering::Equal)
);
assert_eq!(lower_open.partial_cmp(&lower_open), Some(Ordering::Equal));
assert_eq!(
upper_closed.partial_cmp(&upper_closed),
Some(Ordering::Equal)
);
assert_eq!(upper_open.partial_cmp(&upper_open), Some(Ordering::Equal));
}
#[test]
fn test_antisymmetry() {
let lower_closed_3 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(3.0).unwrap()));
let lower_closed_5 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
if lower_closed_3.partial_cmp(&lower_closed_5) == Some(Ordering::Less) {
assert_eq!(
lower_closed_5.partial_cmp(&lower_closed_3),
Some(Ordering::Greater)
);
}
}
#[test]
fn test_transitivity() {
let lower_closed_1 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(1.0).unwrap()));
let lower_open_3 =
LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(3.0).unwrap()));
let lower_closed_5 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
let ord_1_3 = lower_closed_1.partial_cmp(&lower_open_3);
let ord_3_5 = lower_open_3.partial_cmp(&lower_closed_5);
let ord_1_5 = lower_closed_1.partial_cmp(&lower_closed_5);
if ord_1_3 == Some(Ordering::Less) && ord_3_5 == Some(Ordering::Less) {
assert_eq!(ord_1_5, Some(Ordering::Less));
}
}
#[test]
fn test_edge_cases_same_values() {
let value = Real::try_new(42.0).unwrap();
let lower_closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(value));
let lower_open = LowerBoundRuntime::Open(LowerBoundOpen::new(value));
let upper_closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(value));
let upper_open = UpperBoundRuntime::Open(UpperBoundOpen::new(value));
assert_ne!(lower_closed.partial_cmp(&lower_open), Some(Ordering::Equal));
assert_ne!(upper_closed.partial_cmp(&upper_open), Some(Ordering::Equal));
assert_eq!(lower_closed.partial_cmp(&lower_open), Some(Ordering::Less));
assert_eq!(upper_open.partial_cmp(&upper_closed), Some(Ordering::Less));
}
#[test]
fn test_extreme_values() {
let min_val = Real::try_new(f64::MIN).unwrap();
let max_val = Real::try_new(f64::MAX).unwrap();
let lower_min_closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(min_val));
let lower_max_open = LowerBoundRuntime::Open(LowerBoundOpen::new(max_val));
let upper_min_open = UpperBoundRuntime::Open(UpperBoundOpen::new(min_val));
let upper_max_closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(max_val));
assert_eq!(
lower_min_closed.partial_cmp(&lower_max_open),
Some(Ordering::Less)
);
assert_eq!(
upper_min_open.partial_cmp(&upper_max_closed),
Some(Ordering::Less)
);
}
#[test]
fn test_min_max_lower_bound() {
let lower_closed_3 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(3.0).unwrap()));
let lower_open_5 =
LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));
let min_result = min_lower_bound(lower_closed_3.clone(), lower_open_5.clone());
assert_eq!(min_result, lower_closed_3);
let max_result = max_lower_bound(lower_closed_3.clone(), lower_open_5.clone());
assert_eq!(max_result, lower_open_5);
}
#[test]
fn test_min_max_upper_bound() {
let upper_open_3 =
UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(3.0).unwrap()));
let upper_closed_5 =
UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
let min_result = min_upper_bound(upper_open_3.clone(), upper_closed_5.clone());
assert_eq!(min_result, upper_open_3);
let max_result = max_upper_bound(upper_open_3.clone(), upper_closed_5.clone());
assert_eq!(max_result, upper_closed_5);
}
#[test]
fn test_lower_bound_runtime_constructors() {
let value = Real::try_new(5.0).unwrap();
let open_bound = LowerBoundRuntime::new_open(value);
assert!(open_bound.is_open());
assert!(open_bound.is_open_variant());
assert!(!open_bound.is_closed());
assert!(!open_bound.is_closed_variant());
assert_eq!(open_bound.as_ref(), &value);
let closed_bound = LowerBoundRuntime::new_closed(value);
assert!(closed_bound.is_closed());
assert!(closed_bound.is_closed_variant());
assert!(!closed_bound.is_open());
assert!(!closed_bound.is_open_variant());
assert_eq!(closed_bound.as_ref(), &value);
assert!(!open_bound.value_within_bound(&value)); assert!(open_bound.value_within_bound(&Real::try_new(5.1).unwrap())); assert!(!open_bound.value_within_bound(&Real::try_new(4.9).unwrap()));
assert!(closed_bound.value_within_bound(&value)); assert!(closed_bound.value_within_bound(&Real::try_new(5.1).unwrap())); assert!(!closed_bound.value_within_bound(&Real::try_new(4.9).unwrap())); }
#[test]
fn test_upper_bound_runtime_constructors() {
let value = Real::try_new(10.0).unwrap();
let open_bound = UpperBoundRuntime::new_open(value);
assert!(open_bound.is_open());
assert!(open_bound.is_open_variant());
assert!(!open_bound.is_closed());
assert!(!open_bound.is_closed_variant());
assert_eq!(open_bound.as_ref(), &value);
let closed_bound = UpperBoundRuntime::new_closed(value);
assert!(closed_bound.is_closed());
assert!(closed_bound.is_closed_variant());
assert!(!closed_bound.is_open());
assert!(!closed_bound.is_open_variant());
assert_eq!(closed_bound.as_ref(), &value);
assert!(!open_bound.value_within_bound(&value)); assert!(open_bound.value_within_bound(&Real::try_new(9.9).unwrap())); assert!(!open_bound.value_within_bound(&Real::try_new(10.1).unwrap()));
assert!(closed_bound.value_within_bound(&value)); assert!(closed_bound.value_within_bound(&Real::try_new(9.9).unwrap())); assert!(!closed_bound.value_within_bound(&Real::try_new(10.1).unwrap())); }
#[test]
fn test_constructor_equivalence() {
let value = Real::try_new(7.5).unwrap();
let lower_open_convenience = LowerBoundRuntime::new_open(value);
let lower_open_manual = LowerBoundRuntime::Open(LowerBoundOpen::new(value));
assert_eq!(lower_open_convenience, lower_open_manual);
let lower_closed_convenience = LowerBoundRuntime::new_closed(value);
let lower_closed_manual = LowerBoundRuntime::Closed(LowerBoundClosed::new(value));
assert_eq!(lower_closed_convenience, lower_closed_manual);
let upper_open_convenience = UpperBoundRuntime::new_open(value);
let upper_open_manual = UpperBoundRuntime::Open(UpperBoundOpen::new(value));
assert_eq!(upper_open_convenience, upper_open_manual);
let upper_closed_convenience = UpperBoundRuntime::new_closed(value);
let upper_closed_manual = UpperBoundRuntime::Closed(UpperBoundClosed::new(value));
assert_eq!(upper_closed_convenience, upper_closed_manual);
}
#[test]
fn test_variant_check_consistency() {
let value = Real::try_new(3.5).unwrap();
let lower_open = LowerBoundRuntime::new_open(value);
let lower_closed = LowerBoundRuntime::new_closed(value);
assert_eq!(lower_open.is_open_variant(), lower_open.is_open());
assert_eq!(lower_closed.is_open_variant(), lower_closed.is_open());
assert_eq!(lower_open.is_closed_variant(), lower_open.is_closed());
assert_eq!(lower_closed.is_closed_variant(), lower_closed.is_closed());
assert_eq!(
lower_open.is_open_variant(),
!lower_open.is_closed_variant()
);
assert_eq!(
lower_closed.is_open_variant(),
!lower_closed.is_closed_variant()
);
let upper_open = UpperBoundRuntime::new_open(value);
let upper_closed = UpperBoundRuntime::new_closed(value);
assert_eq!(upper_open.is_open_variant(), upper_open.is_open());
assert_eq!(upper_closed.is_open_variant(), upper_closed.is_open());
assert_eq!(upper_open.is_closed_variant(), upper_open.is_closed());
assert_eq!(upper_closed.is_closed_variant(), upper_closed.is_closed());
assert_eq!(
upper_open.is_open_variant(),
!upper_open.is_closed_variant()
);
assert_eq!(
upper_closed.is_open_variant(),
!upper_closed.is_closed_variant()
);
}
#[test]
fn test_variant_checks_for_filtering() {
let bounds = [
LowerBoundRuntime::new_open(Real::try_new(1.0).unwrap()),
LowerBoundRuntime::new_closed(Real::try_new(2.0).unwrap()),
LowerBoundRuntime::new_open(Real::try_new(3.0).unwrap()),
LowerBoundRuntime::new_closed(Real::try_new(4.0).unwrap()),
LowerBoundRuntime::new_open(Real::try_new(5.0).unwrap()),
];
let open_bounds: Vec<_> = bounds.iter().filter(|b| b.is_open_variant()).collect();
let closed_bounds: Vec<_> = bounds.iter().filter(|b| b.is_closed_variant()).collect();
assert_eq!(open_bounds.len(), 3);
assert_eq!(closed_bounds.len(), 2);
for bound in &open_bounds {
assert!(bound.is_open_variant());
assert!(!bound.is_closed_variant());
}
for bound in &closed_bounds {
assert!(bound.is_closed_variant());
assert!(!bound.is_open_variant());
}
}
#[test]
fn test_variant_checks_non_consuming() {
let bound = LowerBoundRuntime::new_open(Real::try_new(42.0).unwrap());
assert!(bound.is_open_variant());
assert!(bound.is_open_variant());
assert!(!bound.is_closed_variant());
assert!(!bound.is_closed_variant());
assert!(bound.value_within_bound(&Real::try_new(50.0).unwrap()));
assert_eq!(bound.as_ref(), &Real::try_new(42.0).unwrap());
}
#[test]
fn test_constructors_with_different_scalars() {
let f64_open = LowerBoundRuntime::new_open(5.0_f64);
let f64_closed = LowerBoundRuntime::new_closed(5.0_f64);
assert!(f64_open.is_open_variant());
assert!(f64_closed.is_closed_variant());
let validated_value = Real::try_new(10.0).unwrap();
let validated_open = UpperBoundRuntime::new_open(validated_value);
let validated_closed = UpperBoundRuntime::new_closed(validated_value);
assert!(validated_open.is_open_variant());
assert!(validated_closed.is_closed_variant());
}
#[test]
fn test_constructors_dynamic_interval_creation() {
let create_bound_from_config =
|value: f64, inclusive: bool| -> LowerBoundRuntime<Real> {
let real_value = Real::try_new(value).unwrap();
if inclusive {
LowerBoundRuntime::new_closed(real_value)
} else {
LowerBoundRuntime::new_open(real_value)
}
};
let inclusive_bound = create_bound_from_config(0.0, true);
assert!(inclusive_bound.is_closed_variant());
assert!(inclusive_bound.value_within_bound(&Real::try_new(0.0).unwrap()));
let exclusive_bound = create_bound_from_config(0.0, false);
assert!(exclusive_bound.is_open_variant());
assert!(!exclusive_bound.value_within_bound(&Real::try_new(0.0).unwrap()));
}
#[test]
fn test_constructor_ordering() {
let value = Real::try_new(5.0).unwrap();
let lower_closed = LowerBoundRuntime::new_closed(value);
let lower_open = LowerBoundRuntime::new_open(value);
assert!(lower_closed < lower_open);
let upper_open = UpperBoundRuntime::new_open(value);
let upper_closed = UpperBoundRuntime::new_closed(value);
assert!(upper_open < upper_closed);
}
}
mod maybe_unbounded_helpers {
use super::*;
use num_valid::RealNative64StrictFiniteInDebug;
use try_create::TryNew;
type Real = RealNative64StrictFiniteInDebug;
#[test]
fn test_min_lower_bound_both_present_closed_wins() {
let value = Real::try_new(5.0).unwrap();
let closed = LowerBoundRuntime::new_closed(value);
let open = LowerBoundRuntime::new_open(value);
let result = min_lower_bound_maybe_unbounded(Some(closed), Some(open));
assert!(result.is_some());
assert!(
result.unwrap().is_closed_variant(),
"Closed should win over open at same value"
);
}
#[test]
fn test_min_lower_bound_different_values() {
let value1 = Real::try_new(3.0).unwrap();
let value2 = Real::try_new(7.0).unwrap();
let bound1 = LowerBoundRuntime::new_closed(value1);
let bound2 = LowerBoundRuntime::new_open(value2);
let result = min_lower_bound_maybe_unbounded(Some(bound1.clone()), Some(bound2));
assert!(result.is_some());
let result_bound = result.unwrap();
assert_eq!(result_bound.as_ref(), &3.0, "Should select minimum value");
assert!(result_bound.is_closed_variant());
}
#[test]
fn test_min_lower_bound_first_none() {
let value = Real::try_new(5.0).unwrap();
let bound = LowerBoundRuntime::new_closed(value);
let result = min_lower_bound_maybe_unbounded(None, Some(bound));
assert!(result.is_none(), "None (unbounded) should win");
}
#[test]
fn test_min_lower_bound_second_none() {
let value = Real::try_new(5.0).unwrap();
let bound = LowerBoundRuntime::new_closed(value);
let result = min_lower_bound_maybe_unbounded(Some(bound), None);
assert!(result.is_none(), "None (unbounded) should win");
}
#[test]
fn test_min_lower_bound_both_none() {
let result = min_lower_bound_maybe_unbounded::<Real>(None, None);
assert!(result.is_none());
}
#[test]
fn test_max_upper_bound_both_present_closed_wins() {
let value = Real::try_new(5.0).unwrap();
let closed = UpperBoundRuntime::new_closed(value);
let open = UpperBoundRuntime::new_open(value);
let result = max_upper_bound_maybe_unbounded(Some(closed.clone()), Some(open.clone()));
assert!(result.is_some());
assert!(
result.unwrap().is_closed_variant(),
"Closed should win over open at same value"
);
}
#[test]
fn test_max_upper_bound_different_values() {
let value1 = Real::try_new(3.0).unwrap();
let value2 = Real::try_new(7.0).unwrap();
let bound1 = UpperBoundRuntime::new_open(value1);
let bound2 = UpperBoundRuntime::new_closed(value2);
let result = max_upper_bound_maybe_unbounded(Some(bound1), Some(bound2.clone()));
assert!(result.is_some());
let result_bound = result.unwrap();
assert_eq!(result_bound.as_ref(), &7.0, "Should select maximum value");
assert!(result_bound.is_closed_variant());
}
#[test]
fn test_max_upper_bound_first_none() {
let value = Real::try_new(5.0).unwrap();
let bound = UpperBoundRuntime::new_closed(value);
let result = max_upper_bound_maybe_unbounded(None, Some(bound));
assert!(result.is_none(), "None (unbounded) should win");
}
#[test]
fn test_max_upper_bound_second_none() {
let value = Real::try_new(5.0).unwrap();
let bound = UpperBoundRuntime::new_closed(value);
let result = max_upper_bound_maybe_unbounded(Some(bound), None);
assert!(result.is_none(), "None (unbounded) should win");
}
#[test]
fn test_max_upper_bound_both_none() {
let result = max_upper_bound_maybe_unbounded::<Real>(None, None);
assert!(result.is_none());
}
#[test]
fn test_min_lower_bound_symmetry() {
let value1 = Real::try_new(3.0).unwrap();
let value2 = Real::try_new(7.0).unwrap();
let bound1 = LowerBoundRuntime::new_closed(value1);
let bound2 = LowerBoundRuntime::new_open(value2);
let result1 =
min_lower_bound_maybe_unbounded(Some(bound1.clone()), Some(bound2.clone()));
let result2 = min_lower_bound_maybe_unbounded(Some(bound2), Some(bound1));
assert_eq!(result1, result2, "Operation should be commutative");
}
#[test]
fn test_max_upper_bound_symmetry() {
let value1 = Real::try_new(3.0).unwrap();
let value2 = Real::try_new(7.0).unwrap();
let bound1 = UpperBoundRuntime::new_closed(value1);
let bound2 = UpperBoundRuntime::new_open(value2);
let result1 =
max_upper_bound_maybe_unbounded(Some(bound1.clone()), Some(bound2.clone()));
let result2 = max_upper_bound_maybe_unbounded(Some(bound2), Some(bound1));
assert_eq!(result1, result2, "Operation should be commutative");
}
#[test]
fn test_consistency_with_existing_min_lower_bound() {
let value1 = Real::try_new(3.0).unwrap();
let value2 = Real::try_new(7.0).unwrap();
let bound1 = LowerBoundRuntime::new_closed(value1);
let bound2 = LowerBoundRuntime::new_open(value2);
let result_maybe =
min_lower_bound_maybe_unbounded(Some(bound1.clone()), Some(bound2.clone()))
.unwrap();
let result_direct = min_lower_bound(bound1, bound2);
assert_eq!(
result_maybe, result_direct,
"Should be consistent with existing function"
);
}
#[test]
fn test_consistency_with_existing_max_upper_bound() {
let value1 = Real::try_new(3.0).unwrap();
let value2 = Real::try_new(7.0).unwrap();
let bound1 = UpperBoundRuntime::new_closed(value1);
let bound2 = UpperBoundRuntime::new_open(value2);
let result_maybe =
max_upper_bound_maybe_unbounded(Some(bound1.clone()), Some(bound2.clone()))
.unwrap();
let result_direct = max_upper_bound(bound1, bound2);
assert_eq!(
result_maybe, result_direct,
"Should be consistent with existing function"
);
}
#[test]
fn test_min_lower_bound_associativity() {
let v1 = Real::try_new(1.0).unwrap();
let v2 = Real::try_new(2.0).unwrap();
let v3 = Real::try_new(3.0).unwrap();
let b1 = LowerBoundRuntime::new_closed(v1);
let b2 = LowerBoundRuntime::new_open(v2);
let b3 = LowerBoundRuntime::new_closed(v3);
let temp = min_lower_bound_maybe_unbounded(Some(b1.clone()), Some(b2.clone()));
let result1 = min_lower_bound_maybe_unbounded(temp, Some(b3.clone()));
let temp = min_lower_bound_maybe_unbounded(Some(b2), Some(b3));
let result2 = min_lower_bound_maybe_unbounded(Some(b1), temp);
assert_eq!(result1, result2, "Operation should be associative");
}
}
mod transformations {
use super::*;
#[test]
fn flip_bound_type_lower_open_to_closed() {
let lower_open = LowerBoundRuntime::new_open(5.0);
let lower_closed = lower_open.flip_bound_type();
assert!(lower_closed.is_closed_variant());
assert!(!lower_closed.is_open_variant());
assert_eq!(*lower_closed.as_ref(), 5.0);
}
#[test]
fn flip_bound_type_lower_closed_to_open() {
let lower_closed = LowerBoundRuntime::new_closed(10.0);
let lower_open = lower_closed.flip_bound_type();
assert!(lower_open.is_open_variant());
assert!(!lower_open.is_closed_variant());
assert_eq!(*lower_open.as_ref(), 10.0);
}
#[test]
fn flip_bound_type_upper_open_to_closed() {
let upper_open = UpperBoundRuntime::new_open(20.0);
let upper_closed = upper_open.flip_bound_type();
assert!(upper_closed.is_closed_variant());
assert!(!upper_closed.is_open_variant());
assert_eq!(*upper_closed.as_ref(), 20.0);
}
#[test]
fn flip_bound_type_upper_closed_to_open() {
let upper_closed = UpperBoundRuntime::new_closed(15.0);
let upper_open = upper_closed.flip_bound_type();
assert!(upper_open.is_open_variant());
assert!(!upper_open.is_closed_variant());
assert_eq!(*upper_open.as_ref(), 15.0);
}
#[test]
fn flip_bound_type_idempotent() {
let lower_open_original = LowerBoundRuntime::new_open(3.0);
let lower_open_roundtrip = lower_open_original
.clone()
.flip_bound_type()
.flip_bound_type();
assert_eq!(lower_open_original, lower_open_roundtrip);
let lower_closed_original = LowerBoundRuntime::new_closed(7.0);
let lower_closed_roundtrip = lower_closed_original
.clone()
.flip_bound_type()
.flip_bound_type();
assert_eq!(lower_closed_original, lower_closed_roundtrip);
let upper_open_original = UpperBoundRuntime::new_open(12.0);
let upper_open_roundtrip = upper_open_original
.clone()
.flip_bound_type()
.flip_bound_type();
assert_eq!(upper_open_original, upper_open_roundtrip);
let upper_closed_original = UpperBoundRuntime::new_closed(18.0);
let upper_closed_roundtrip = upper_closed_original
.clone()
.flip_bound_type()
.flip_bound_type();
assert_eq!(upper_closed_original, upper_closed_roundtrip);
}
#[test]
fn flip_bound_side_lower_open_to_upper_open() {
let lower_open = LowerBoundRuntime::new_open(5.0);
let upper_open: UpperBoundRuntime<f64> = lower_open.flip_bound_side();
assert!(upper_open.is_open_variant());
assert_eq!(*upper_open.as_ref(), 5.0);
}
#[test]
fn flip_bound_side_lower_closed_to_upper_closed() {
let lower_closed = LowerBoundRuntime::new_closed(10.0);
let upper_closed: UpperBoundRuntime<f64> = lower_closed.flip_bound_side();
assert!(upper_closed.is_closed_variant());
assert_eq!(*upper_closed.as_ref(), 10.0);
}
#[test]
fn flip_bound_side_upper_open_to_lower_open() {
let upper_open = UpperBoundRuntime::new_open(20.0);
let lower_open: LowerBoundRuntime<f64> = upper_open.flip_bound_side();
assert!(lower_open.is_open_variant());
assert_eq!(*lower_open.as_ref(), 20.0);
}
#[test]
fn flip_bound_side_upper_closed_to_lower_closed() {
let upper_closed = UpperBoundRuntime::new_closed(15.0);
let lower_closed: LowerBoundRuntime<f64> = upper_closed.flip_bound_side();
assert!(lower_closed.is_closed_variant());
assert_eq!(*lower_closed.as_ref(), 15.0);
}
#[test]
fn flip_bound_side_roundtrip() {
let lower_open_original = LowerBoundRuntime::new_open(3.0);
let upper_open: UpperBoundRuntime<f64> = lower_open_original.clone().flip_bound_side();
let lower_open_back: LowerBoundRuntime<f64> = upper_open.flip_bound_side();
assert_eq!(lower_open_original, lower_open_back);
let lower_closed_original = LowerBoundRuntime::new_closed(7.0);
let upper_closed: UpperBoundRuntime<f64> =
lower_closed_original.clone().flip_bound_side();
let lower_closed_back: LowerBoundRuntime<f64> = upper_closed.flip_bound_side();
assert_eq!(lower_closed_original, lower_closed_back);
let upper_open_original = UpperBoundRuntime::new_open(12.0);
let lower_open_temp: LowerBoundRuntime<f64> =
upper_open_original.clone().flip_bound_side();
let upper_open_back: UpperBoundRuntime<f64> = lower_open_temp.flip_bound_side();
assert_eq!(upper_open_original, upper_open_back);
let upper_closed_original = UpperBoundRuntime::new_closed(18.0);
let lower_closed_temp: LowerBoundRuntime<f64> =
upper_closed_original.clone().flip_bound_side();
let upper_closed_back: UpperBoundRuntime<f64> = lower_closed_temp.flip_bound_side();
assert_eq!(upper_closed_original, upper_closed_back);
}
#[test]
fn combined_flip_lower_closed_to_upper_open() {
let lower_closed = LowerBoundRuntime::new_closed(3.0);
let upper_open: UpperBoundRuntime<f64> = lower_closed
.flip_bound_side() .flip_bound_type();
assert!(upper_open.is_open_variant());
assert_eq!(*upper_open.as_ref(), 3.0);
}
#[test]
fn combined_flip_lower_open_to_upper_closed() {
let lower_open = LowerBoundRuntime::new_open(8.0);
let upper_closed: UpperBoundRuntime<f64> = lower_open
.flip_bound_side() .flip_bound_type();
assert!(upper_closed.is_closed_variant());
assert_eq!(*upper_closed.as_ref(), 8.0);
}
#[test]
fn combined_flip_upper_closed_to_lower_open() {
let upper_closed = UpperBoundRuntime::new_closed(15.0);
let lower_open: LowerBoundRuntime<f64> = upper_closed
.flip_bound_side() .flip_bound_type();
assert!(lower_open.is_open_variant());
assert_eq!(*lower_open.as_ref(), 15.0);
}
#[test]
fn combined_flip_upper_open_to_lower_closed() {
let upper_open = UpperBoundRuntime::new_open(22.0);
let lower_closed: LowerBoundRuntime<f64> = upper_open
.flip_bound_side() .flip_bound_type();
assert!(lower_closed.is_closed_variant());
assert_eq!(*lower_closed.as_ref(), 22.0);
}
#[test]
fn flip_bound_type_negative_values() {
let lower_open = LowerBoundRuntime::new_open(-5.0);
let lower_closed = lower_open.flip_bound_type();
assert_eq!(*lower_closed.as_ref(), -5.0);
assert!(lower_closed.is_closed_variant());
let upper_closed = UpperBoundRuntime::new_closed(-10.0);
let upper_open = upper_closed.flip_bound_type();
assert_eq!(*upper_open.as_ref(), -10.0);
assert!(upper_open.is_open_variant());
}
#[test]
fn flip_bound_side_negative_values() {
let lower_open = LowerBoundRuntime::new_open(-3.0);
let upper_open: UpperBoundRuntime<f64> = lower_open.flip_bound_side();
assert_eq!(*upper_open.as_ref(), -3.0);
assert!(upper_open.is_open_variant());
let upper_closed = UpperBoundRuntime::new_closed(-7.0);
let lower_closed: LowerBoundRuntime<f64> = upper_closed.flip_bound_side();
assert_eq!(*lower_closed.as_ref(), -7.0);
assert!(lower_closed.is_closed_variant());
}
#[test]
fn transformations_with_zero() {
let lower_zero_open = LowerBoundRuntime::new_open(0.0);
let lower_zero_closed = lower_zero_open.flip_bound_type();
assert_eq!(*lower_zero_closed.as_ref(), 0.0);
assert!(lower_zero_closed.is_closed_variant());
let upper_zero_closed = UpperBoundRuntime::new_closed(0.0);
let lower_zero_from_upper: LowerBoundRuntime<f64> = upper_zero_closed.flip_bound_side();
assert_eq!(*lower_zero_from_upper.as_ref(), 0.0);
assert!(lower_zero_from_upper.is_closed_variant());
}
#[test]
fn transformations_with_small_values() {
let tiny_value = 1e-100;
let lower_open = LowerBoundRuntime::new_open(tiny_value);
let lower_closed = lower_open.flip_bound_type();
assert_eq!(*lower_closed.as_ref(), tiny_value);
let upper_open = UpperBoundRuntime::new_open(tiny_value);
let lower_open_from_upper: LowerBoundRuntime<f64> = upper_open.flip_bound_side();
assert_eq!(*lower_open_from_upper.as_ref(), tiny_value);
}
#[test]
fn transformations_with_large_values() {
let large_value = 1e100;
let upper_closed = UpperBoundRuntime::new_closed(large_value);
let upper_open = upper_closed.flip_bound_type();
assert_eq!(*upper_open.as_ref(), large_value);
assert!(upper_open.is_open_variant());
let lower_closed = LowerBoundRuntime::new_closed(large_value);
let upper_closed_from_lower: UpperBoundRuntime<f64> = lower_closed.flip_bound_side();
assert_eq!(*upper_closed_from_lower.as_ref(), large_value);
assert!(upper_closed_from_lower.is_closed_variant());
}
#[test]
fn flip_operations_commute() {
let original = LowerBoundRuntime::new_closed(5.0);
let result1: UpperBoundRuntime<f64> = original
.clone()
.flip_bound_side() .flip_bound_type();
let result2: UpperBoundRuntime<f64> = original
.flip_bound_type() .flip_bound_side();
assert_eq!(result1, result2);
assert!(result1.is_open_variant());
assert_eq!(*result1.as_ref(), 5.0);
}
#[test]
fn flip_bound_side_preserves_type() {
let lower_open = LowerBoundRuntime::new_open(5.0);
let upper_open: UpperBoundRuntime<f64> = lower_open.flip_bound_side();
assert!(upper_open.is_open_variant());
let upper_open2 = UpperBoundRuntime::new_open(10.0);
let lower_open2: LowerBoundRuntime<f64> = upper_open2.flip_bound_side();
assert!(lower_open2.is_open_variant());
let lower_closed = LowerBoundRuntime::new_closed(15.0);
let upper_closed: UpperBoundRuntime<f64> = lower_closed.flip_bound_side();
assert!(upper_closed.is_closed_variant());
let upper_closed2 = UpperBoundRuntime::new_closed(20.0);
let lower_closed2: LowerBoundRuntime<f64> = upper_closed2.flip_bound_side();
assert!(lower_closed2.is_closed_variant());
}
#[test]
fn flip_bound_type_preserves_side() {
let lower_open = LowerBoundRuntime::new_open(5.0);
let lower_closed = lower_open.flip_bound_type();
assert_eq!(*lower_closed.as_ref(), 5.0);
let upper_closed = UpperBoundRuntime::new_closed(10.0);
let upper_open = upper_closed.flip_bound_type();
assert_eq!(*upper_open.as_ref(), 10.0);
}
#[test]
fn interval_difference_use_case() {
let b_lower = LowerBoundRuntime::new_closed(5.0);
let left_upper: UpperBoundRuntime<f64> = b_lower
.flip_bound_side() .flip_bound_type();
assert!(left_upper.is_open_variant());
assert_eq!(*left_upper.as_ref(), 5.0);
let b_upper = UpperBoundRuntime::new_closed(7.0);
let right_lower: LowerBoundRuntime<f64> = b_upper
.flip_bound_side() .flip_bound_type();
assert!(right_lower.is_open_variant());
assert_eq!(*right_lower.as_ref(), 7.0);
}
#[test]
fn transformations_with_validated_types() {
use num_valid::RealNative64StrictFiniteInDebug;
use try_create::TryNew;
let value = RealNative64StrictFiniteInDebug::try_new(5.0).unwrap();
let lower_open = LowerBoundRuntime::new_open(value);
let lower_closed = lower_open.flip_bound_type();
assert!(lower_closed.is_closed_variant());
assert_eq!(*lower_closed.as_ref(), value);
let upper_closed = UpperBoundRuntime::new_closed(value);
let lower_closed_from_upper: LowerBoundRuntime<_> = upper_closed.flip_bound_side();
assert!(lower_closed_from_upper.is_closed_variant());
assert_eq!(*lower_closed_from_upper.as_ref(), value);
}
#[test]
fn multiple_sequential_transformations() {
let original = LowerBoundRuntime::new_open(10.0);
let step1 = original.flip_bound_type(); let step2: UpperBoundRuntime<f64> = step1.flip_bound_side(); let step3 = step2.flip_bound_type(); let step4: LowerBoundRuntime<f64> = step3.flip_bound_side();
assert!(step4.is_open_variant());
assert_eq!(*step4.as_ref(), 10.0);
}
#[test]
fn test_flip_bound_side_and_type() {
let lower_closed = IntervalBound::<f64, Lower, Closed>::new(5.0);
let upper_open: IntervalBound<f64, Upper, Open> =
lower_closed.flip_bound_side_and_type();
assert_eq!(*upper_open.as_ref(), 5.0);
let upper_open2 = IntervalBound::<f64, Upper, Open>::new(10.0);
let lower_closed2: IntervalBound<f64, Lower, Closed> =
upper_open2.flip_bound_side_and_type();
assert_eq!(*lower_closed2.as_ref(), 10.0);
let lower_open = IntervalBound::<f64, Lower, Open>::new(3.0);
let upper_closed: IntervalBound<f64, Upper, Closed> =
lower_open.flip_bound_side_and_type();
assert_eq!(*upper_closed.as_ref(), 3.0);
let upper_closed2 = IntervalBound::<f64, Upper, Closed>::new(7.0);
let lower_open2: IntervalBound<f64, Lower, Open> =
upper_closed2.flip_bound_side_and_type();
assert_eq!(*lower_open2.as_ref(), 7.0);
}
#[test]
fn test_bound_side() {
assert!(Upper::is_upper());
assert!(!Upper::is_lower());
assert!(!Lower::is_upper());
assert!(Lower::is_lower());
}
#[test]
fn test_bound_side_checks() {
let lower = LowerBoundRuntime::new_closed(5.0);
assert!(!lower.is_upper_bound());
let upper = UpperBoundRuntime::new_open(10.0);
assert!(upper.is_upper_bound());
}
#[test]
fn test_interval_bound_runtime_into_inner() {
let lower_open = LowerBoundRuntime::new_open(5.0);
let value = lower_open.into_inner();
assert_eq!(value, 5.0);
let lower_closed = LowerBoundRuntime::new_closed(7.0);
let value2 = lower_closed.into_inner();
assert_eq!(value2, 7.0);
let upper_open = UpperBoundRuntime::new_open(3.0);
let value3 = upper_open.into_inner();
assert_eq!(value3, 3.0);
let upper_closed = UpperBoundRuntime::new_closed(9.0);
let value4 = upper_closed.into_inner();
assert_eq!(value4, 9.0);
}
#[test]
fn test_bound_type_is_open() {
assert!(Closed::includes_boundary() != Closed::is_open());
assert!(Open::is_open());
assert!(!Closed::is_open());
}
}
mod comparisons {
use super::*;
use try_create::TryNew;
type Real = f64;
#[test]
fn test_interval_lower_bound_comparison() {
let lower_bound_closed_5 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
let lower_bound_open_5 =
LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));
let lower_bound_closed_3 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(3.0).unwrap()));
let lower_bound_open_3 =
LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(3.0).unwrap()));
let lower_bound_closed_7 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(7.0).unwrap()));
assert_eq!(
lower_bound_closed_3.partial_cmp(&lower_bound_closed_5),
Some(Ordering::Less)
);
assert_eq!(
lower_bound_closed_5.partial_cmp(&lower_bound_closed_3),
Some(Ordering::Greater)
);
assert_eq!(
lower_bound_closed_5.partial_cmp(&lower_bound_closed_5),
Some(Ordering::Equal)
);
assert_eq!(
lower_bound_closed_5.partial_cmp(&lower_bound_open_5),
Some(Ordering::Less)
);
assert_eq!(
lower_bound_open_5.partial_cmp(&lower_bound_closed_5),
Some(Ordering::Greater)
);
assert_eq!(
lower_bound_closed_3.partial_cmp(&lower_bound_open_5),
Some(Ordering::Less)
);
assert_eq!(
lower_bound_open_3.partial_cmp(&lower_bound_closed_5),
Some(Ordering::Less)
);
assert_eq!(
lower_bound_closed_5.partial_cmp(&lower_bound_open_3),
Some(Ordering::Greater)
);
assert_eq!(
lower_bound_closed_3.partial_cmp(&lower_bound_closed_7),
Some(Ordering::Less)
);
assert_eq!(
lower_bound_open_3.partial_cmp(&lower_bound_closed_7),
Some(Ordering::Less)
);
}
#[test]
fn test_interval_upper_bound_comparison() {
let upper_bound_closed_5 =
UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
let upper_bound_open_5 =
UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(5.0).unwrap()));
let upper_bound_closed_3 =
UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(3.0).unwrap()));
let upper_bound_open_3 =
UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(3.0).unwrap()));
let upper_bound_closed_7 =
UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(7.0).unwrap()));
assert_eq!(
upper_bound_closed_3.partial_cmp(&upper_bound_closed_5),
Some(Ordering::Less)
);
assert_eq!(
upper_bound_closed_5.partial_cmp(&upper_bound_closed_3),
Some(Ordering::Greater)
);
assert_eq!(
upper_bound_closed_5.partial_cmp(&upper_bound_closed_5),
Some(Ordering::Equal)
);
assert_eq!(
upper_bound_open_5.partial_cmp(&upper_bound_closed_5),
Some(Ordering::Less)
);
assert_eq!(
upper_bound_closed_5.partial_cmp(&upper_bound_open_5),
Some(Ordering::Greater)
);
assert_eq!(
upper_bound_closed_3.partial_cmp(&upper_bound_open_5),
Some(Ordering::Less)
);
assert_eq!(
upper_bound_open_3.partial_cmp(&upper_bound_closed_5),
Some(Ordering::Less)
);
assert_eq!(
upper_bound_closed_5.partial_cmp(&upper_bound_open_3),
Some(Ordering::Greater)
);
assert_eq!(
upper_bound_closed_3.partial_cmp(&upper_bound_closed_7),
Some(Ordering::Less)
);
assert_eq!(
upper_bound_open_3.partial_cmp(&upper_bound_closed_7),
Some(Ordering::Less)
);
}
#[test]
fn test_lower_bound_semantics() {
let closed_5 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
let open_5 = LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));
more_asserts::assert_lt!(closed_5, open_5);
more_asserts::assert_ge!(open_5, closed_5);
assert_ne!(closed_5, open_5);
}
#[test]
fn test_upper_bound_semantics() {
let closed_5 =
UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
let open_5 = UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(5.0).unwrap()));
more_asserts::assert_lt!(open_5, closed_5);
more_asserts::assert_ge!(closed_5, open_5);
assert_ne!(open_5, closed_5);
}
#[test]
fn test_mathematical_consistency() {
let lower_closed_1 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(1.0).unwrap()));
let lower_open_1 =
LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(1.0).unwrap()));
let upper_open_2 =
UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(2.0).unwrap()));
let upper_closed_2 =
UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(2.0).unwrap()));
assert!(lower_closed_1 < lower_open_1);
assert!(upper_open_2 < upper_closed_2);
}
#[test]
fn test_reflexivity() {
let lower_closed =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
let lower_open =
LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));
let upper_closed =
UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
let upper_open =
UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(5.0).unwrap()));
assert_eq!(
lower_closed.partial_cmp(&lower_closed),
Some(Ordering::Equal)
);
assert_eq!(lower_open.partial_cmp(&lower_open), Some(Ordering::Equal));
assert_eq!(
upper_closed.partial_cmp(&upper_closed),
Some(Ordering::Equal)
);
assert_eq!(upper_open.partial_cmp(&upper_open), Some(Ordering::Equal));
}
#[test]
fn test_antisymmetry() {
let lower_closed_3 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(3.0).unwrap()));
let lower_closed_5 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
if lower_closed_3.partial_cmp(&lower_closed_5) == Some(Ordering::Less) {
assert_eq!(
lower_closed_5.partial_cmp(&lower_closed_3),
Some(Ordering::Greater)
);
}
}
#[test]
fn test_transitivity() {
let lower_closed_1 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(1.0).unwrap()));
let lower_open_3 =
LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(3.0).unwrap()));
let lower_closed_5 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(5.0).unwrap()));
let ord_1_3 = lower_closed_1.partial_cmp(&lower_open_3);
let ord_3_5 = lower_open_3.partial_cmp(&lower_closed_5);
let ord_1_5 = lower_closed_1.partial_cmp(&lower_closed_5);
if ord_1_3 == Some(Ordering::Less) && ord_3_5 == Some(Ordering::Less) {
assert_eq!(ord_1_5, Some(Ordering::Less));
}
}
#[test]
fn test_edge_cases_same_values() {
let value = Real::try_new(42.0).unwrap();
let lower_closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(value));
let lower_open = LowerBoundRuntime::Open(LowerBoundOpen::new(value));
let upper_closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(value));
let upper_open = UpperBoundRuntime::Open(UpperBoundOpen::new(value));
assert_ne!(lower_closed.partial_cmp(&lower_open), Some(Ordering::Equal));
assert_ne!(upper_closed.partial_cmp(&upper_open), Some(Ordering::Equal));
assert_eq!(lower_closed.partial_cmp(&lower_open), Some(Ordering::Less));
assert_eq!(upper_open.partial_cmp(&upper_closed), Some(Ordering::Less));
}
#[test]
fn test_extreme_values() {
let min_val = Real::try_new(f64::MIN).unwrap();
let max_val = Real::try_new(f64::MAX).unwrap();
let lower_min_closed = LowerBoundRuntime::Closed(LowerBoundClosed::new(min_val));
let lower_max_open = LowerBoundRuntime::Open(LowerBoundOpen::new(max_val));
let upper_min_open = UpperBoundRuntime::Open(UpperBoundOpen::new(min_val));
let upper_max_closed = UpperBoundRuntime::Closed(UpperBoundClosed::new(max_val));
assert_eq!(
lower_min_closed.partial_cmp(&lower_max_open),
Some(Ordering::Less)
);
assert_eq!(
upper_min_open.partial_cmp(&upper_max_closed),
Some(Ordering::Less)
);
}
#[test]
fn test_min_max_lower_bound() {
let lower_closed_3 =
LowerBoundRuntime::Closed(LowerBoundClosed::new(Real::try_new(3.0).unwrap()));
let lower_open_5 =
LowerBoundRuntime::Open(LowerBoundOpen::new(Real::try_new(5.0).unwrap()));
let min_result = min_lower_bound(lower_closed_3.clone(), lower_open_5.clone());
assert_eq!(min_result, lower_closed_3);
let max_result = max_lower_bound(lower_closed_3.clone(), lower_open_5.clone());
assert_eq!(max_result, lower_open_5);
}
#[test]
fn test_min_max_upper_bound() {
let upper_open_3 =
UpperBoundRuntime::Open(UpperBoundOpen::new(Real::try_new(3.0).unwrap()));
let upper_closed_5 =
UpperBoundRuntime::Closed(UpperBoundClosed::new(Real::try_new(5.0).unwrap()));
let min_result = min_upper_bound(upper_open_3.clone(), upper_closed_5.clone());
assert_eq!(min_result, upper_open_3);
let max_result = max_upper_bound(upper_open_3.clone(), upper_closed_5.clone());
assert_eq!(max_result, upper_closed_5);
}
}
}