#![deny(rustdoc::broken_intra_doc_links)]
pub mod markers;
pub use markers::{Closed, Lower, Open, Upper};
pub mod runtime;
pub use runtime::{IntervalBoundRuntime, LowerBoundRuntime, UpperBoundRuntime};
pub mod traits;
pub use traits::{
BoundChecks, BoundSide, BoundSideChecks, BoundSideConversion, BoundType, BoundTypeChecks,
BoundTypeConversion, ValueWithinBound,
};
use derive_more::AsRef;
use duplicate::duplicate_item;
use num_valid::RealScalar;
use serde::{Deserialize, Serialize};
use std::{cmp::Ordering, fmt::Debug};
use try_create::{IntoInner, New};
impl<RealType: RealScalar, Type: BoundType> BoundSideConversion<RealType, Type>
for IntervalBound<RealType, Lower, Type>
{
#[inline(always)]
fn into_lower(self) -> IntervalBound<RealType, Lower, Type> {
self
}
#[inline(always)]
fn into_upper(self) -> IntervalBound<RealType, Upper, Type> {
self.flip_bound_side()
}
}
impl<RealType: RealScalar, Type: BoundType> BoundSideConversion<RealType, Type>
for IntervalBound<RealType, Upper, Type>
{
#[inline(always)]
fn into_lower(self) -> IntervalBound<RealType, Lower, Type> {
self.flip_bound_side()
}
#[inline(always)]
fn into_upper(self) -> IntervalBound<RealType, Upper, Type> {
self
}
}
#[derive(Debug, Clone, AsRef, PartialEq, Eq, Serialize, Deserialize)]
#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
pub struct IntervalBound<RealType: RealScalar, Side: BoundSide, Type: BoundType> {
#[as_ref]
pub(crate) value: RealType,
_phantom: std::marker::PhantomData<(Side, Type)>,
}
impl<RealType: RealScalar, Side: BoundSide, Type: BoundType> PartialOrd
for IntervalBound<RealType, Side, Type>
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.value.partial_cmp(&other.value)
}
}
impl<RealType: RealScalar, Side: BoundSide, Type: BoundType> New
for IntervalBound<RealType, Side, Type>
{
#[inline(always)]
fn new(value: RealType) -> Self {
debug_assert!(
value.is_finite(),
"Bound value must be finite, got: {:?}",
value
);
Self {
value,
_phantom: std::marker::PhantomData,
}
}
}
impl<RealType: RealScalar, Side: BoundSide, Type: BoundType> BoundTypeChecks
for IntervalBound<RealType, Side, Type>
{
#[inline(always)]
fn includes_boundary(&self) -> bool {
Type::includes_boundary()
}
}
impl<RealType: RealScalar, Side: BoundSide, Type: BoundType> BoundSideChecks
for IntervalBound<RealType, Side, Type>
{
#[inline(always)]
fn is_upper_bound(&self) -> bool {
Side::is_upper()
}
}
impl<RealType: RealScalar, Side: BoundSide, Type: BoundType> BoundChecks
for IntervalBound<RealType, Side, Type>
{
}
impl<RealType: RealScalar, Side: BoundSide, Type: BoundType> IntervalBound<RealType, Side, Type> {
#[inline(always)]
pub fn with_bound_type<NewType: BoundType>(self) -> IntervalBound<RealType, Side, NewType> {
IntervalBound {
value: self.value,
_phantom: std::marker::PhantomData,
}
}
#[inline(always)]
pub fn with_value(&self, new_value: RealType) -> Self {
Self::new(new_value)
}
#[inline(always)]
pub fn flip_bound_type(self) -> IntervalBound<RealType, Side, Type::Opposite> {
IntervalBound {
value: self.value,
_phantom: std::marker::PhantomData,
}
}
#[inline(always)]
pub fn flip_bound_side(self) -> IntervalBound<RealType, Side::Opposite, Type> {
IntervalBound {
value: self.value,
_phantom: std::marker::PhantomData,
}
}
#[inline(always)]
pub fn flip_bound_side_and_type(
self,
) -> IntervalBound<RealType, Side::Opposite, Type::Opposite> {
IntervalBound {
value: self.value,
_phantom: std::marker::PhantomData,
}
}
}
impl<RealType: RealScalar, Side: BoundSide, Type: BoundType> IntoInner
for IntervalBound<RealType, Side, Type>
{
type InnerType = RealType;
#[inline(always)]
fn into_inner(self) -> Self::InnerType {
self.value
}
}
#[duplicate_item(
BoundSide BoundType ordering_func;
[Lower] [Open] [gt]; // x > value
[Lower] [Closed] [ge]; // x >= value
[Upper] [Open] [lt]; // x < value
[Upper] [Closed] [le]; // x <= value
)]
impl<RealType: RealScalar> ValueWithinBound for IntervalBound<RealType, BoundSide, BoundType> {
type RealType = RealType;
#[inline(always)]
fn value_within_bound(&self, x: &Self::RealType) -> bool {
PartialOrd::ordering_func(x, &self.value)
}
}
pub type LowerBound<RealType, BoundType> = IntervalBound<RealType, Lower, BoundType>;
pub type UpperBound<RealType, BoundType> = IntervalBound<RealType, Upper, BoundType>;
pub type LowerBoundOpen<RealType> = LowerBound<RealType, Open>;
pub type LowerBoundClosed<RealType> = LowerBound<RealType, Closed>;
pub type UpperBoundOpen<RealType> = UpperBound<RealType, Open>;
pub type UpperBoundClosed<RealType> = UpperBound<RealType, Closed>;
impl<RealType: RealScalar, Side: BoundSide> BoundTypeConversion<RealType, Side>
for IntervalBound<RealType, Side, Open>
{
#[inline(always)]
fn into_open(self) -> IntervalBound<RealType, Side, Open> {
self
}
#[inline(always)]
fn into_closed(self) -> IntervalBound<RealType, Side, Closed> {
self.flip_bound_type()
}
}
impl<RealType: RealScalar, Side: BoundSide> BoundTypeConversion<RealType, Side>
for IntervalBound<RealType, Side, Closed>
{
#[inline(always)]
fn into_open(self) -> IntervalBound<RealType, Side, Open> {
self.flip_bound_type()
}
#[inline(always)]
fn into_closed(self) -> IntervalBound<RealType, Side, Closed> {
self
}
}
#[duplicate_item(
bound_lhs bound_rhs;
[LowerBoundOpen<RealType>] [LowerBoundClosed<RealType>];
[LowerBoundClosed<RealType>] [LowerBoundOpen<RealType>] ;
[UpperBoundOpen<RealType>] [UpperBoundClosed<RealType>];
[UpperBoundClosed<RealType>] [UpperBoundOpen<RealType>];
)]
impl<RealType: RealScalar> PartialEq<bound_rhs> for bound_lhs {
#[inline(always)]
fn eq(&self, _other: &bound_rhs) -> bool {
false }
}
#[duplicate_item(
bound_lhs bound_rhs;
[LowerBoundClosed<RealType>] [LowerBoundOpen<RealType>];
[UpperBoundOpen<RealType>] [UpperBoundClosed<RealType>];
)]
impl<RealType: RealScalar> PartialOrd<bound_rhs> for bound_lhs {
#[inline(always)]
fn partial_cmp(&self, other: &bound_rhs) -> Option<Ordering> {
match self.value.partial_cmp(&other.value) {
Some(Ordering::Equal) => {
Some(Ordering::Less)
}
other_ordering => other_ordering,
}
}
}
#[duplicate_item(
bound_lhs bound_rhs;
[UpperBoundClosed<RealType>] [UpperBoundOpen<RealType>];
[LowerBoundOpen<RealType>] [LowerBoundClosed<RealType>];
)]
impl<RealType: RealScalar> PartialOrd<bound_rhs> for bound_lhs {
#[inline(always)]
fn partial_cmp(&self, other: &bound_rhs) -> Option<Ordering> {
match self.value.partial_cmp(&other.value) {
Some(Ordering::Equal) => {
Some(Ordering::Greater)
}
other_ordering => other_ordering,
}
}
}
#[duplicate_item(
func_name bound_type;
[min_lower_bound] [LowerBoundRuntime];
[min_upper_bound] [UpperBoundRuntime];
)]
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn func_name<RealType: RealScalar>(
a: bound_type<RealType>,
b: bound_type<RealType>,
) -> bound_type<RealType> {
if a < b { a } else { b }
}
#[duplicate_item(
func_name bound_type;
[max_lower_bound] [LowerBoundRuntime];
[max_upper_bound] [UpperBoundRuntime];
)]
#[inline(always)]
pub(crate) fn func_name<RealType: RealScalar>(
a: bound_type<RealType>,
b: bound_type<RealType>,
) -> bound_type<RealType> {
if a > b { a } else { b }
}
pub(crate) fn min_lower_bound_maybe_unbounded<RealType: RealScalar>(
a: Option<LowerBoundRuntime<RealType>>,
b: Option<LowerBoundRuntime<RealType>>,
) -> Option<LowerBoundRuntime<RealType>> {
match (a, b) {
(None, _) | (_, None) => None,
(Some(a), Some(b)) => {
if a < b {
Some(a)
} else {
Some(b)
}
}
}
}
pub(crate) fn max_upper_bound_maybe_unbounded<RealType: RealScalar>(
a: Option<UpperBoundRuntime<RealType>>,
b: Option<UpperBoundRuntime<RealType>>,
) -> Option<UpperBoundRuntime<RealType>> {
match (a, b) {
(None, _) | (_, None) => None,
(Some(a), Some(b)) => {
if a > b {
Some(a)
} else {
Some(b)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::intervals::bounded::IntervalFromBounds;
#[test]
fn bound_constraint_satisfaction() {
let lower_closed = LowerBoundClosed::new(5.0);
let lower_open = LowerBoundOpen::new(5.0);
let upper_closed = UpperBoundClosed::new(10.0);
let upper_open = UpperBoundOpen::new(10.0);
assert!(lower_closed.value_within_bound(&5.0)); assert!(!lower_open.value_within_bound(&5.0)); assert!(upper_closed.value_within_bound(&10.0)); assert!(!upper_open.value_within_bound(&10.0));
assert!(lower_closed.value_within_bound(&6.0));
assert!(lower_open.value_within_bound(&6.0));
assert!(upper_closed.value_within_bound(&9.0));
assert!(upper_open.value_within_bound(&9.0));
}
#[test]
fn bound_type_queries() {
let lower_closed = LowerBoundClosed::new(1.0);
let upper_open = UpperBoundOpen::new(1.0);
assert!(lower_closed.is_lower_bound());
assert!(!lower_closed.is_upper_bound());
assert!(lower_closed.is_closed());
assert!(!lower_closed.is_open());
assert!(!upper_open.is_lower_bound());
assert!(upper_open.is_upper_bound());
assert!(!upper_open.is_closed());
assert!(upper_open.is_open());
}
#[test]
fn bound_conversions() {
let lower_open_bound = LowerBoundOpen::new(5.0);
let lower_closed_bound = LowerBoundClosed::new(5.0);
let upper_open_bound = UpperBoundOpen::new(5.0);
let upper_closed_bound = UpperBoundClosed::new(5.0);
assert_eq!(lower_open_bound.clone().into_open(), lower_open_bound);
assert_eq!(lower_open_bound.clone().into_closed(), lower_closed_bound);
assert_eq!(lower_open_bound.clone().into_lower(), lower_open_bound);
assert_eq!(lower_open_bound.clone().into_upper(), upper_open_bound);
assert_eq!(lower_closed_bound.clone().into_open(), lower_open_bound);
assert_eq!(lower_closed_bound.clone().into_closed(), lower_closed_bound);
assert_eq!(lower_closed_bound.clone().into_lower(), lower_closed_bound);
assert_eq!(lower_closed_bound.clone().into_upper(), upper_closed_bound);
assert_eq!(upper_open_bound.clone().into_open(), upper_open_bound);
assert_eq!(upper_open_bound.clone().into_closed(), upper_closed_bound);
assert_eq!(upper_open_bound.clone().into_lower(), lower_open_bound);
assert_eq!(upper_open_bound.clone().into_upper(), upper_open_bound);
assert_eq!(upper_closed_bound.clone().into_open(), upper_open_bound);
assert_eq!(upper_closed_bound.clone().into_closed(), upper_closed_bound);
assert_eq!(upper_closed_bound.clone().into_lower(), lower_closed_bound);
assert_eq!(upper_closed_bound.clone().into_upper(), upper_closed_bound);
}
#[test]
fn lower_bound_open_closed_partial_eq_and_ord() {
let open = LowerBoundOpen::new(1.0);
let closed = LowerBoundClosed::new(1.0);
assert_ne!(open, closed);
assert!(open > closed);
assert!(closed < open);
}
#[test]
fn upper_bound_open_closed_partial_eq_and_ord() {
let open = UpperBoundOpen::new(2.0);
let closed = UpperBoundClosed::new(2.0);
assert_ne!(open, closed);
assert!(open < closed);
assert!(closed > open);
}
#[test]
fn bounds_into_inner() {
let open = LowerBoundOpen::new(1.0);
let closed = LowerBoundClosed::new(2.0);
let up_open = UpperBoundOpen::new(3.0);
let up_closed = UpperBoundClosed::new(4.0);
assert_eq!(open.into_inner(), 1.0);
assert_eq!(closed.into_inner(), 2.0);
assert_eq!(up_open.into_inner(), 3.0);
assert_eq!(up_closed.into_inner(), 4.0);
}
#[test]
fn bounds_new_and_try_new() {
let open = LowerBoundOpen::new(1.0);
let closed = LowerBoundClosed::new(2.0);
let up_open = UpperBoundOpen::new(3.0);
let up_closed = UpperBoundClosed::new(4.0);
assert_eq!(open.as_ref(), &1.0);
assert_eq!(closed.as_ref(), &2.0);
assert_eq!(up_open.as_ref(), &3.0);
assert_eq!(up_closed.as_ref(), &4.0);
}
#[test]
fn bounds_ordering_01() {
let lower_closed_0 = LowerBoundClosed::new(0.0);
assert_eq!(lower_closed_0.clone(), lower_closed_0);
assert!(lower_closed_0 == lower_closed_0);
assert!(!(lower_closed_0 != lower_closed_0));
more_asserts::assert_ge!(lower_closed_0, lower_closed_0);
assert!(lower_closed_0 <= lower_closed_0);
more_asserts::assert_le!(lower_closed_0, lower_closed_0);
assert!(lower_closed_0 >= lower_closed_0);
more_asserts::assert_le!(lower_closed_0, lower_closed_0);
more_asserts::assert_ge!(lower_closed_0, lower_closed_0);
let lower_closed_1 = LowerBoundClosed::new(1.0);
assert_ne!(lower_closed_0, lower_closed_1);
more_asserts::assert_lt!(lower_closed_0, lower_closed_1);
more_asserts::assert_le!(lower_closed_0, lower_closed_1);
more_asserts::assert_gt!(lower_closed_1, lower_closed_0);
more_asserts::assert_ge!(lower_closed_1, lower_closed_0);
let lower_open_0 = LowerBoundOpen::new(0.0);
assert_eq!(lower_open_0.clone(), lower_open_0);
assert!(lower_open_0 == lower_open_0);
assert!(!(lower_open_0 != lower_open_0));
more_asserts::assert_ge!(lower_open_0, lower_open_0);
assert!(lower_open_0 <= lower_open_0);
more_asserts::assert_le!(lower_open_0, lower_open_0);
assert!(lower_open_0 >= lower_open_0);
more_asserts::assert_le!(lower_open_0, lower_open_0);
more_asserts::assert_ge!(lower_open_0, lower_open_0);
let lower_open_1 = LowerBoundOpen::new(1.0);
assert_ne!(lower_open_0, lower_open_1);
more_asserts::assert_lt!(lower_open_0, lower_open_1);
more_asserts::assert_le!(lower_open_0, lower_open_1);
more_asserts::assert_gt!(lower_open_1, lower_open_0);
more_asserts::assert_ge!(lower_open_1, lower_open_0);
let upper_closed_0 = UpperBoundClosed::new(0.0);
assert_eq!(upper_closed_0.clone(), upper_closed_0);
assert!(upper_closed_0 == upper_closed_0);
assert!(!(upper_closed_0 != upper_closed_0));
more_asserts::assert_ge!(upper_closed_0, upper_closed_0);
assert!(upper_closed_0 <= upper_closed_0);
more_asserts::assert_le!(upper_closed_0, upper_closed_0);
assert!(upper_closed_0 >= upper_closed_0);
more_asserts::assert_le!(upper_closed_0, upper_closed_0);
more_asserts::assert_ge!(upper_closed_0, upper_closed_0);
let upper_closed_1 = UpperBoundClosed::new(1.0);
assert_ne!(upper_closed_0, upper_closed_1);
more_asserts::assert_lt!(upper_closed_0, upper_closed_1);
more_asserts::assert_le!(upper_closed_0, upper_closed_1);
more_asserts::assert_gt!(upper_closed_1, upper_closed_0);
more_asserts::assert_ge!(upper_closed_1, upper_closed_0);
let upper_open_0 = UpperBoundOpen::new(0.0);
assert_eq!(upper_open_0.clone(), upper_open_0);
assert!(upper_open_0 == upper_open_0);
assert!(!(upper_open_0 != upper_open_0));
more_asserts::assert_ge!(upper_open_0, upper_open_0);
assert!(upper_open_0 <= upper_open_0);
more_asserts::assert_le!(upper_open_0, upper_open_0);
assert!(upper_open_0 >= upper_open_0);
more_asserts::assert_le!(upper_open_0, upper_open_0);
more_asserts::assert_ge!(upper_open_0, upper_open_0);
let upper_open_1 = UpperBoundOpen::new(1.0);
assert_ne!(upper_open_0, upper_open_1);
more_asserts::assert_lt!(upper_open_0, upper_open_1);
more_asserts::assert_le!(upper_open_0, upper_open_1);
more_asserts::assert_gt!(upper_open_1, upper_open_0);
more_asserts::assert_ge!(upper_open_1, upper_open_0);
}
#[test]
fn bounds_ordering_02() {
let lower_closed = LowerBoundClosed::new(0.0);
let lower_open = LowerBoundOpen::new(0.0);
let upper_closed = UpperBoundClosed::new(0.0);
let upper_open = UpperBoundOpen::new(0.0);
{
assert_ne!(lower_closed, lower_open);
more_asserts::assert_lt!(lower_closed, lower_open);
more_asserts::assert_le!(lower_closed, lower_open);
}
{
assert_ne!(lower_open, lower_closed);
more_asserts::assert_ge!(lower_open, lower_closed);
more_asserts::assert_gt!(lower_open, lower_closed);
}
{
assert_ne!(upper_closed, upper_open);
more_asserts::assert_gt!(upper_closed, upper_open);
more_asserts::assert_ge!(upper_closed, upper_open);
}
{
assert_ne!(upper_open, upper_closed);
more_asserts::assert_lt!(upper_open, upper_closed);
more_asserts::assert_le!(upper_open, upper_closed);
}
}
#[test]
fn bound_utility_methods() {
let lower_closed = LowerBoundClosed::new(5.0);
let new_lower = lower_closed.with_value(7.0);
assert_eq!(new_lower.as_ref(), &7.0);
assert!(new_lower.is_closed());
assert!(new_lower.is_lower_bound());
let open_version = lower_closed.clone().with_bound_type::<Open>();
assert_eq!(open_version.as_ref(), lower_closed.as_ref());
assert!(open_version.is_open());
assert!(open_version.is_lower_bound());
}
mod interval_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 get_interval_bound_trait {
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(), c.upper_bound_runtime());
assert_eq!(
lower.unwrap(),
LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0))
);
assert_eq!(
upper.unwrap(),
UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0))
);
assert_eq!(c.clone().into_bounds_pair(), (1., 2.));
let (lower, upper) = (o.lower_bound_runtime(), o.upper_bound_runtime());
assert_eq!(
lower.unwrap(),
LowerBoundRuntime::Open(LowerBoundOpen::new(1.0))
);
assert_eq!(
upper.unwrap(),
UpperBoundRuntime::Open(UpperBoundOpen::new(2.0))
);
assert_eq!(o.clone().into_bounds_pair(), (1., 2.));
let (lower, upper) = (l.lower_bound_runtime(), l.upper_bound_runtime());
assert_eq!(
lower.unwrap(),
LowerBoundRuntime::Open(LowerBoundOpen::new(1.0))
);
assert_eq!(
upper.unwrap(),
UpperBoundRuntime::Closed(UpperBoundClosed::new(2.0))
);
assert_eq!(l.clone().into_bounds_pair(), (1., 2.));
let (lower, upper) = (r.lower_bound_runtime(), r.upper_bound_runtime());
assert_eq!(
lower.unwrap(),
LowerBoundRuntime::Closed(LowerBoundClosed::new(1.0))
);
assert_eq!(
upper.unwrap(),
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 test_interval_bound_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);
}
}
}