#![deny(rustdoc::broken_intra_doc_links)]
use crate::{RealScalar, core::errors::capture_backtrace};
use derive_more::{AsRef, Display, LowerExp};
use into_inner::IntoInner;
use serde::{Deserialize, Serialize};
use std::backtrace::Backtrace;
use thiserror::Error;
use try_create::TryNew;
#[derive(Debug, Error)]
pub enum ErrorsTolerance<RealType: RealScalar> {
#[error("Negative value detected: {value}")]
NegativeValue {
value: RealType,
backtrace: Backtrace,
},
}
#[derive(Debug, Error)]
pub enum ErrorsPositiveRealScalar<RealType: RealScalar> {
#[error("Negative value detected: {value}")]
NegativeValue {
value: RealType,
backtrace: Backtrace,
},
#[error("Zero value detected")]
ZeroValue {
backtrace: Backtrace,
},
}
#[derive(Debug, Error)]
pub enum ErrorsNonNegativeRealScalar<RealType: RealScalar> {
#[error("Negative value: {value} (must be non-negative, i.e., ≥ 0)")]
NegativeValue {
value: RealType,
backtrace: Backtrace,
},
}
#[derive(
Debug, Clone, PartialEq, PartialOrd, AsRef, IntoInner, Display, LowerExp, Serialize, Deserialize,
)]
#[repr(transparent)]
pub struct AbsoluteTolerance<RealType>(RealType);
impl<RealType: RealScalar> AbsoluteTolerance<RealType> {
#[inline(always)]
pub fn zero() -> Self {
AbsoluteTolerance(RealType::zero())
}
#[inline(always)]
pub fn epsilon() -> Self {
AbsoluteTolerance(RealType::epsilon())
}
}
impl<RealType: RealScalar> TryNew for AbsoluteTolerance<RealType> {
type Error = ErrorsTolerance<RealType>;
fn try_new(value: RealType) -> Result<Self, Self::Error> {
debug_assert!(value.is_finite(), "The input value {value} is not finite!");
if value.kernel_is_sign_negative() {
Err(ErrorsTolerance::NegativeValue {
value,
backtrace: capture_backtrace(),
})
} else {
Ok(Self(value))
}
}
}
#[derive(
Debug, Clone, PartialEq, PartialOrd, AsRef, IntoInner, Display, LowerExp, Serialize, Deserialize,
)]
#[repr(transparent)]
pub struct RelativeTolerance<RealType>(RealType);
impl<RealType: RealScalar> RelativeTolerance<RealType> {
#[inline(always)]
pub fn zero() -> Self {
RelativeTolerance(RealType::zero())
}
#[inline(always)]
pub fn epsilon() -> Self {
RelativeTolerance(RealType::epsilon())
}
#[inline(always)]
pub fn absolute_tolerance(&self, reference: RealType) -> AbsoluteTolerance<RealType> {
let abs_tol = reference.abs() * &self.0;
AbsoluteTolerance(abs_tol)
}
}
impl<RealType: RealScalar> TryNew for RelativeTolerance<RealType> {
type Error = ErrorsTolerance<RealType>;
fn try_new(value: RealType) -> Result<Self, Self::Error> {
debug_assert!(value.is_finite(), "The input value {value} is not finite!");
if value.kernel_is_sign_negative() {
Err(ErrorsTolerance::NegativeValue {
value,
backtrace: capture_backtrace(),
})
} else {
Ok(Self(value))
}
}
}
#[derive(
Debug, Clone, PartialEq, PartialOrd, AsRef, IntoInner, Display, Serialize, Deserialize,
)]
#[repr(transparent)]
#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
pub struct PositiveRealScalar<RealType: RealScalar>(RealType);
impl<RealType: RealScalar> TryNew for PositiveRealScalar<RealType> {
type Error = ErrorsPositiveRealScalar<RealType>;
fn try_new(value: RealType) -> Result<Self, Self::Error> {
debug_assert!(value.is_finite(), "The input value {value} is not finite!");
if value.kernel_is_sign_negative() {
Err(ErrorsPositiveRealScalar::NegativeValue {
value,
backtrace: capture_backtrace(),
})
} else if value.is_zero() {
Err(ErrorsPositiveRealScalar::ZeroValue {
backtrace: capture_backtrace(),
})
} else {
Ok(Self(value))
}
}
}
#[derive(
Debug, Clone, PartialEq, PartialOrd, AsRef, IntoInner, Display, Serialize, Deserialize,
)]
#[repr(transparent)]
#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
pub struct NonNegativeRealScalar<RealType: RealScalar>(RealType);
impl<RealType: RealScalar> TryNew for NonNegativeRealScalar<RealType> {
type Error = ErrorsNonNegativeRealScalar<RealType>;
fn try_new(value: RealType) -> Result<Self, Self::Error> {
debug_assert!(value.is_finite(), "The input value {value} is not finite!");
if value.kernel_is_sign_negative() {
Err(ErrorsNonNegativeRealScalar::NegativeValue {
value,
backtrace: capture_backtrace(),
})
} else {
Ok(Self(value))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::backends::native64::validated::RealNative64StrictFinite;
mod absolute_tolerance {
use super::*;
#[test]
fn try_new_valid() {
let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
assert_eq!(*tol.as_ref(), 1e-10);
}
#[test]
fn try_new_zero() {
let tol = AbsoluteTolerance::try_new(0.0_f64).unwrap();
assert_eq!(*tol.as_ref(), 0.0);
}
#[test]
fn try_new_negative() {
let result = AbsoluteTolerance::try_new(-1e-10_f64);
assert!(
matches!(result, Err(ErrorsTolerance::NegativeValue { value, .. }) if value == -1e-10)
);
}
#[test]
fn zero_constructor() {
let tol = AbsoluteTolerance::<f64>::zero();
assert_eq!(*tol.as_ref(), 0.0);
}
#[test]
fn epsilon_constructor() {
let tol = AbsoluteTolerance::<f64>::epsilon();
assert_eq!(*tol.as_ref(), f64::EPSILON);
}
#[test]
fn display_trait() {
let tol = AbsoluteTolerance::try_new(0.5_f64).unwrap();
assert_eq!(format!("{}", tol), "0.5");
}
#[test]
fn lower_exp_trait() {
let tol = AbsoluteTolerance::try_new(0.00001_f64).unwrap();
assert_eq!(format!("{:e}", tol), "1e-5");
}
#[test]
fn into_inner() {
let tol = AbsoluteTolerance::try_new(0.5_f64).unwrap();
let inner = tol.into_inner();
assert_eq!(inner, 0.5);
}
#[test]
fn clone_and_partial_eq() {
let tol1 = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
let tol2 = tol1.clone();
assert_eq!(tol1, tol2);
}
#[test]
fn partial_ord() {
let tol1 = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
let tol2 = AbsoluteTolerance::try_new(1e-9_f64).unwrap();
assert!(tol1 < tol2);
}
#[test]
fn serialize_deserialize() {
let tol = AbsoluteTolerance::try_new(0.5_f64).unwrap();
let serialized = serde_json::to_string(&tol).unwrap();
let deserialized: AbsoluteTolerance<f64> = serde_json::from_str(&serialized).unwrap();
assert_eq!(tol, deserialized);
}
#[test]
fn with_validated_type() {
let val = RealNative64StrictFinite::try_from_f64(1e-10).unwrap();
let tol = AbsoluteTolerance::try_new(val).unwrap();
assert_eq!(*tol.as_ref().as_ref(), 1e-10);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "is not finite")]
fn debug_panics_on_nan() {
let _ = AbsoluteTolerance::try_new(f64::NAN);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "is not finite")]
fn debug_panics_on_infinity() {
let _ = AbsoluteTolerance::try_new(f64::INFINITY);
}
#[test]
fn try_new() {
let v = AbsoluteTolerance::<f64>::zero();
assert_eq!(*v.as_ref(), 0.0);
let v = AbsoluteTolerance::try_new(2.0).unwrap();
assert_eq!(*v.as_ref(), 2.0);
}
#[test]
fn try_new_invalid_negative() {
let tol = AbsoluteTolerance::try_new(-0.1);
assert!(matches!(tol, Err(ErrorsTolerance::NegativeValue { .. })));
}
#[test]
#[cfg(debug_assertions)]
#[should_panic = "The input value inf is not finite!"]
fn try_new_invalid_infinite() {
let _tol = AbsoluteTolerance::try_new(f64::INFINITY);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic = "The input value NaN is not finite!"]
fn try_new_invalid_nan() {
let _tol = AbsoluteTolerance::try_new(f64::NAN);
}
#[test]
fn epsilon() {
let epsilon_tol = AbsoluteTolerance::<f64>::epsilon();
assert_eq!(*epsilon_tol.as_ref(), f64::EPSILON);
}
#[test]
fn equality_operator() {
let a = AbsoluteTolerance::<f64>::zero();
let b = AbsoluteTolerance::<f64>::zero();
assert!(a == b);
let c = AbsoluteTolerance::try_new(1.0).unwrap();
let d = AbsoluteTolerance::try_new(1.0).unwrap();
assert!(c == d);
}
#[test]
fn inequality_operator() {
let a = AbsoluteTolerance::<f64>::zero();
let b = AbsoluteTolerance::try_new(1.0).unwrap();
assert!(a != b);
let c = AbsoluteTolerance::try_new(1.0).unwrap();
let d = AbsoluteTolerance::try_new(2.0).unwrap();
assert!(c != d);
}
#[test]
fn debug_trait() {
let tolerance = AbsoluteTolerance::try_new(0.5).unwrap();
assert_eq!(format!("{:?}", tolerance), "AbsoluteTolerance(0.5)");
}
#[test]
fn clone_trait() {
let tolerance = AbsoluteTolerance::try_new(0.5).unwrap();
let cloned_tolerance = tolerance.clone();
assert_eq!(tolerance, cloned_tolerance);
}
#[test]
fn partial_eq_trait() {
let tolerance1 = AbsoluteTolerance::try_new(0.5).unwrap();
let tolerance2 = AbsoluteTolerance::try_new(0.5).unwrap();
assert_eq!(tolerance1, tolerance2);
}
#[test]
fn partial_ord_trait() {
let tolerance1 = AbsoluteTolerance::try_new(0.5).unwrap();
let tolerance2 = AbsoluteTolerance::try_new(1.0).unwrap();
assert!(tolerance1 < tolerance2);
}
#[test]
fn as_ref_trait() {
let tolerance = AbsoluteTolerance::try_new(0.5).unwrap();
let inner: &f64 = tolerance.as_ref();
assert_eq!(*inner, 0.5);
}
#[test]
fn zero() {
let zero_tol = AbsoluteTolerance::<f64>::zero();
assert_eq!(*zero_tol.as_ref(), 0.0);
}
#[test]
fn edge_cases() {
let tiny_tol = AbsoluteTolerance::try_new(f64::MIN_POSITIVE).unwrap();
assert_eq!(*tiny_tol.as_ref(), f64::MIN_POSITIVE);
let large_tol = AbsoluteTolerance::try_new(1e100).unwrap();
assert_eq!(*large_tol.as_ref(), 1e100);
}
}
mod relative_tolerance {
use super::*;
#[test]
fn try_new_valid() {
let tol = RelativeTolerance::try_new(0.01_f64).unwrap();
assert_eq!(*tol.as_ref(), 0.01);
}
#[test]
fn try_new_zero() {
let tol = RelativeTolerance::try_new(0.0_f64).unwrap();
assert_eq!(*tol.as_ref(), 0.0);
}
#[test]
fn try_new_negative() {
let result = RelativeTolerance::try_new(-0.01_f64);
assert!(matches!(result, Err(ErrorsTolerance::NegativeValue { .. })));
}
#[test]
fn zero_constructor() {
let tol = RelativeTolerance::<f64>::zero();
assert_eq!(*tol.as_ref(), 0.0);
}
#[test]
fn epsilon_constructor() {
let tol = RelativeTolerance::<f64>::epsilon();
assert_eq!(*tol.as_ref(), f64::EPSILON);
}
#[test]
fn absolute_tolerance_positive_reference() {
let rel_tol = RelativeTolerance::try_new(0.1_f64).unwrap();
let abs_tol = rel_tol.absolute_tolerance(100.0);
assert_eq!(*abs_tol.as_ref(), 10.0);
}
#[test]
fn absolute_tolerance_negative_reference() {
let rel_tol = RelativeTolerance::try_new(0.1_f64).unwrap();
let abs_tol = rel_tol.absolute_tolerance(-50.0);
assert_eq!(*abs_tol.as_ref(), 5.0);
}
#[test]
fn absolute_tolerance_zero_reference() {
let rel_tol = RelativeTolerance::try_new(0.1_f64).unwrap();
let abs_tol = rel_tol.absolute_tolerance(0.0);
assert_eq!(*abs_tol.as_ref(), 0.0);
}
#[test]
fn display_trait() {
let tol = RelativeTolerance::try_new(0.5_f64).unwrap();
assert_eq!(format!("{}", tol), "0.5");
}
#[test]
fn into_inner() {
let tol = RelativeTolerance::try_new(0.01_f64).unwrap();
let inner = tol.into_inner();
assert_eq!(inner, 0.01);
}
#[test]
fn serialize_deserialize() {
let tol = RelativeTolerance::try_new(0.01_f64).unwrap();
let serialized = serde_json::to_string(&tol).unwrap();
let deserialized: RelativeTolerance<f64> = serde_json::from_str(&serialized).unwrap();
assert_eq!(tol, deserialized);
}
#[test]
fn try_new() {
let v = RelativeTolerance::<f64>::zero();
assert_eq!(*v.as_ref(), 0.0);
let v = RelativeTolerance::try_new(2.0).unwrap();
assert_eq!(*v.as_ref(), 2.0);
}
#[test]
fn try_new_invalid_negative() {
let tol = RelativeTolerance::try_new(-0.1);
assert!(matches!(tol, Err(ErrorsTolerance::NegativeValue { .. })));
}
#[test]
#[cfg(debug_assertions)]
#[should_panic = "The input value inf is not finite!"]
fn try_new_invalid_infinite() {
let _tol = RelativeTolerance::try_new(f64::INFINITY);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic = "The input value NaN is not finite!"]
fn try_new_invalid_nan() {
let _tol = RelativeTolerance::try_new(f64::NAN);
}
#[test]
fn epsilon() {
let epsilon_tol = RelativeTolerance::<f64>::epsilon();
assert_eq!(*epsilon_tol.as_ref(), f64::EPSILON);
}
#[test]
fn absolute_tolerance() {
let rel_tol = RelativeTolerance::try_new(0.1).unwrap();
let reference = 10.0;
let abs_tol = rel_tol.absolute_tolerance(reference);
assert_eq!(*abs_tol.as_ref(), 1.0);
let reference = -5.0;
let abs_tol = rel_tol.absolute_tolerance(reference);
assert_eq!(*abs_tol.as_ref(), 0.5);
let reference = 0.0;
let abs_tol = rel_tol.absolute_tolerance(reference);
assert_eq!(*abs_tol.as_ref(), 0.0); }
#[test]
fn equality_operator() {
let a = RelativeTolerance::<f64>::zero();
let b = RelativeTolerance::<f64>::zero();
assert!(a == b);
let c = RelativeTolerance::try_new(1.0).unwrap();
let d = RelativeTolerance::try_new(1.0).unwrap();
assert!(c == d);
}
#[test]
fn inequality_operator() {
let a = RelativeTolerance::<f64>::zero();
let b = RelativeTolerance::try_new(1.0).unwrap();
assert!(a != b);
let c = RelativeTolerance::try_new(1.0).unwrap();
let d = RelativeTolerance::try_new(2.0).unwrap();
assert!(c != d);
}
#[test]
fn debug_trait() {
let tolerance = RelativeTolerance::try_new(0.5).unwrap();
assert_eq!(format!("{:?}", tolerance), "RelativeTolerance(0.5)");
}
#[test]
fn clone_trait() {
let tolerance = RelativeTolerance::try_new(0.5).unwrap();
let cloned_tolerance = tolerance.clone();
assert_eq!(tolerance, cloned_tolerance);
}
#[test]
fn partial_eq_trait() {
let tolerance1 = RelativeTolerance::try_new(0.5).unwrap();
let tolerance2 = RelativeTolerance::try_new(0.5).unwrap();
assert_eq!(tolerance1, tolerance2);
}
#[test]
fn partial_ord_trait() {
let tolerance1 = RelativeTolerance::try_new(0.5).unwrap();
let tolerance2 = RelativeTolerance::try_new(1.0).unwrap();
assert!(tolerance1 < tolerance2);
}
#[test]
fn as_ref_trait() {
let tolerance = RelativeTolerance::try_new(0.5).unwrap();
let inner: &f64 = tolerance.as_ref();
assert_eq!(*inner, 0.5);
}
#[test]
fn lower_exp_trait() {
let tolerance = RelativeTolerance::try_new(0.00001).unwrap();
assert_eq!(format!("{:e}", tolerance), "1e-5");
}
}
mod positive_real_scalar {
use super::*;
use crate::backends::native64::validated::RealNative64StrictFiniteInDebug;
#[test]
fn try_new_positive() {
let val = PositiveRealScalar::try_new(2.5_f64).unwrap();
assert_eq!(*val.as_ref(), 2.5);
}
#[test]
fn try_new_min_positive() {
let val = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
assert_eq!(*val.as_ref(), f64::MIN_POSITIVE);
}
#[test]
fn try_new_zero_fails() {
let result = PositiveRealScalar::try_new(0.0_f64);
assert!(matches!(
result,
Err(ErrorsPositiveRealScalar::ZeroValue { .. })
));
}
#[test]
fn try_new_negative_fails() {
let result = PositiveRealScalar::try_new(-1.0_f64);
assert!(
matches!(result, Err(ErrorsPositiveRealScalar::NegativeValue { value, .. }) if value == -1.0)
);
}
#[test]
fn display_trait() {
let val = PositiveRealScalar::try_new(1.618_f64).unwrap();
assert_eq!(format!("{}", val), "1.618");
}
#[test]
fn into_inner() {
let val = PositiveRealScalar::try_new(42.0_f64).unwrap();
let inner = val.into_inner();
assert_eq!(inner, 42.0);
}
#[test]
fn partial_ord() {
let a = PositiveRealScalar::try_new(1.0_f64).unwrap();
let b = PositiveRealScalar::try_new(2.0_f64).unwrap();
assert!(a < b);
}
#[test]
fn serialize_deserialize() {
let val = PositiveRealScalar::try_new(3.0_f64).unwrap();
let serialized = serde_json::to_string(&val).unwrap();
let deserialized: PositiveRealScalar<f64> = serde_json::from_str(&serialized).unwrap();
assert_eq!(val, deserialized);
}
#[test]
fn with_validated_type() {
let inner = RealNative64StrictFinite::try_from_f64(5.0).unwrap();
let val = PositiveRealScalar::try_new(inner).unwrap();
assert_eq!(*val.as_ref().as_ref(), 5.0);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "is not finite")]
fn debug_panics_on_nan() {
let _ = PositiveRealScalar::try_new(f64::NAN);
}
#[test]
fn try_new() {
let v = PositiveRealScalar::try_new(2.5).unwrap();
assert_eq!(*v.as_ref(), 2.5);
let v = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
assert_eq!(*v.as_ref(), f64::MIN_POSITIVE);
let v = PositiveRealScalar::try_new(1e100).unwrap();
assert_eq!(*v.as_ref(), 1e100);
}
#[test]
fn try_new_invalid_negative() {
let scalar = PositiveRealScalar::try_new(-0.1);
assert!(matches!(
scalar,
Err(ErrorsPositiveRealScalar::NegativeValue { .. })
));
let scalar = PositiveRealScalar::try_new(-f64::EPSILON);
assert!(matches!(
scalar,
Err(ErrorsPositiveRealScalar::NegativeValue { .. })
));
}
#[test]
fn try_new_invalid_zero() {
let scalar = PositiveRealScalar::try_new(0.0);
assert!(matches!(
scalar,
Err(ErrorsPositiveRealScalar::ZeroValue { .. })
));
}
#[test]
#[cfg(debug_assertions)]
#[should_panic = "The input value inf is not finite!"]
fn try_new_invalid_infinite() {
let _scalar = PositiveRealScalar::try_new(f64::INFINITY);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic = "The input value -inf is not finite!"]
fn try_new_invalid_negative_infinite() {
let _scalar = PositiveRealScalar::try_new(f64::NEG_INFINITY);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic = "The input value NaN is not finite!"]
fn try_new_invalid_nan() {
let _scalar = PositiveRealScalar::try_new(f64::NAN);
}
#[test]
fn equality_operator() {
let a = PositiveRealScalar::try_new(1.5).unwrap();
let b = PositiveRealScalar::try_new(1.5).unwrap();
assert!(a == b);
let c = PositiveRealScalar::try_new(3.1).unwrap();
let d = PositiveRealScalar::try_new(3.1).unwrap();
assert!(c == d);
}
#[test]
fn inequality_operator() {
let a = PositiveRealScalar::try_new(1.0).unwrap();
let b = PositiveRealScalar::try_new(2.0).unwrap();
assert!(a != b);
let c = PositiveRealScalar::try_new(0.5).unwrap();
let d = PositiveRealScalar::try_new(1.5).unwrap();
assert!(c != d);
}
#[test]
fn debug_trait() {
let scalar = PositiveRealScalar::try_new(2.7).unwrap();
assert_eq!(format!("{:?}", scalar), "PositiveRealScalar(2.7)");
}
#[test]
fn clone_trait() {
let scalar = PositiveRealScalar::try_new(1.414).unwrap();
let cloned_scalar = scalar.clone();
assert_eq!(scalar, cloned_scalar);
}
#[test]
fn partial_eq_trait() {
let scalar1 = PositiveRealScalar::try_new(0.577).unwrap();
let scalar2 = PositiveRealScalar::try_new(0.577).unwrap();
assert_eq!(scalar1, scalar2);
}
#[test]
fn partial_ord_trait() {
let scalar1 = PositiveRealScalar::try_new(1.0).unwrap();
let scalar2 = PositiveRealScalar::try_new(2.0).unwrap();
assert!(scalar1 < scalar2);
let scalar3 = PositiveRealScalar::try_new(3.0).unwrap();
let scalar4 = PositiveRealScalar::try_new(3.0).unwrap();
assert!(scalar3 <= scalar4);
assert!(scalar4 >= scalar3);
}
#[test]
fn as_ref_trait() {
let scalar = PositiveRealScalar::try_new(2.0).unwrap();
let inner: &f64 = scalar.as_ref();
assert_eq!(*inner, 2.0);
}
#[test]
fn edge_cases() {
let tiny_scalar = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
assert_eq!(*tiny_scalar.as_ref(), f64::MIN_POSITIVE);
let large_scalar = PositiveRealScalar::try_new(f64::MAX).unwrap();
assert_eq!(*large_scalar.as_ref(), f64::MAX);
let epsilon_scalar = PositiveRealScalar::try_new(f64::EPSILON).unwrap();
assert_eq!(*epsilon_scalar.as_ref(), f64::EPSILON);
}
#[test]
fn generic_scalar_types() {
let scalar =
PositiveRealScalar::try_new(RealNative64StrictFiniteInDebug::try_new(5.0).unwrap())
.unwrap();
assert_eq!(
*scalar.as_ref(),
RealNative64StrictFiniteInDebug::try_new(5.0).unwrap()
);
let zero_value = RealNative64StrictFiniteInDebug::try_new(0.0).unwrap();
let scalar_result = PositiveRealScalar::try_new(zero_value);
assert!(matches!(
scalar_result,
Err(ErrorsPositiveRealScalar::ZeroValue { .. })
));
}
#[test]
fn mathematical_operations() {
let scalar1 = PositiveRealScalar::try_new(2.0).unwrap();
let scalar2 = PositiveRealScalar::try_new(3.0).unwrap();
assert!(scalar1 < scalar2);
assert!(scalar2 > scalar1);
assert_eq!(
scalar1.partial_cmp(&scalar2),
Some(std::cmp::Ordering::Less)
);
}
#[test]
fn error_messages() {
let negative_result = PositiveRealScalar::try_new(-1.0);
match negative_result {
Err(ErrorsPositiveRealScalar::NegativeValue { value, .. }) => {
assert_eq!(value, -1.0);
}
_ => panic!("Expected NegativeValue error"),
}
let zero_result = PositiveRealScalar::try_new(0.0);
match zero_result {
Err(ErrorsPositiveRealScalar::ZeroValue { .. }) => {
}
_ => panic!("Expected ZeroValue error"),
}
}
}
mod non_negative_real_scalar {
use super::*;
use crate::backends::native64::validated::RealNative64StrictFiniteInDebug;
#[test]
fn try_new_negative_fails() {
let result = NonNegativeRealScalar::try_new(-1.0_f64);
assert!(
matches!(result, Err(ErrorsNonNegativeRealScalar::NegativeValue { value, .. }) if value == -1.0)
);
}
#[test]
fn display_trait() {
let val = NonNegativeRealScalar::try_new(2.7_f64).unwrap();
assert_eq!(format!("{}", val), "2.7");
let zero = NonNegativeRealScalar::try_new(0.0_f64).unwrap();
assert_eq!(format!("{}", zero), "0");
}
#[test]
fn partial_ord() {
let zero = NonNegativeRealScalar::try_new(0.0_f64).unwrap();
let positive = NonNegativeRealScalar::try_new(1.0_f64).unwrap();
assert!(zero < positive);
}
#[test]
fn serialize_deserialize() {
let val = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
let serialized = serde_json::to_string(&val).unwrap();
let deserialized: NonNegativeRealScalar<f64> =
serde_json::from_str(&serialized).unwrap();
assert_eq!(val, deserialized);
}
#[test]
fn with_validated_type() {
let inner = RealNative64StrictFinite::try_from_f64(0.0).unwrap();
let val = NonNegativeRealScalar::try_new(inner).unwrap();
assert_eq!(*val.as_ref().as_ref(), 0.0);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "is not finite")]
fn debug_panics_on_nan() {
let _ = NonNegativeRealScalar::try_new(f64::NAN);
}
#[test]
fn try_new_positive() {
let v = NonNegativeRealScalar::try_new(2.5).unwrap();
assert_eq!(*v.as_ref(), 2.5);
let v = NonNegativeRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
assert_eq!(*v.as_ref(), f64::MIN_POSITIVE);
let v = NonNegativeRealScalar::try_new(1e100).unwrap();
assert_eq!(*v.as_ref(), 1e100);
}
#[test]
fn try_new_zero() {
let v = NonNegativeRealScalar::try_new(0.0).unwrap();
assert_eq!(*v.as_ref(), 0.0);
}
#[test]
fn try_new_invalid_negative() {
let scalar = NonNegativeRealScalar::try_new(-0.1);
assert!(matches!(
scalar,
Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
));
let scalar = NonNegativeRealScalar::try_new(-f64::EPSILON);
assert!(matches!(
scalar,
Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
));
let scalar = NonNegativeRealScalar::try_new(-100.0);
assert!(matches!(
scalar,
Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
));
}
#[test]
#[cfg(debug_assertions)]
#[should_panic = "The input value inf is not finite!"]
fn try_new_invalid_infinite() {
let _scalar = NonNegativeRealScalar::try_new(f64::INFINITY);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic = "The input value -inf is not finite!"]
fn try_new_invalid_negative_infinite() {
let _scalar = NonNegativeRealScalar::try_new(f64::NEG_INFINITY);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic = "The input value NaN is not finite!"]
fn try_new_invalid_nan() {
let _scalar = NonNegativeRealScalar::try_new(f64::NAN);
}
#[test]
fn equality_operator() {
let a = NonNegativeRealScalar::try_new(1.5).unwrap();
let b = NonNegativeRealScalar::try_new(1.5).unwrap();
assert!(a == b);
let c = NonNegativeRealScalar::try_new(0.0).unwrap();
let d = NonNegativeRealScalar::try_new(0.0).unwrap();
assert!(c == d);
}
#[test]
fn inequality_operator() {
let a = NonNegativeRealScalar::try_new(0.0).unwrap();
let b = NonNegativeRealScalar::try_new(1.0).unwrap();
assert!(a != b);
let c = NonNegativeRealScalar::try_new(0.5).unwrap();
let d = NonNegativeRealScalar::try_new(1.5).unwrap();
assert!(c != d);
}
#[test]
fn debug_trait() {
let scalar = NonNegativeRealScalar::try_new(2.7).unwrap();
assert_eq!(format!("{:?}", scalar), "NonNegativeRealScalar(2.7)");
let zero = NonNegativeRealScalar::try_new(0.0).unwrap();
assert_eq!(format!("{:?}", zero), "NonNegativeRealScalar(0.0)");
}
#[test]
fn clone_trait() {
let scalar = NonNegativeRealScalar::try_new(1.414).unwrap();
let cloned_scalar = scalar.clone();
assert_eq!(scalar, cloned_scalar);
}
#[test]
fn partial_eq_trait() {
let scalar1 = NonNegativeRealScalar::try_new(0.577).unwrap();
let scalar2 = NonNegativeRealScalar::try_new(0.577).unwrap();
assert_eq!(scalar1, scalar2);
let zero1 = NonNegativeRealScalar::try_new(0.0).unwrap();
let zero2 = NonNegativeRealScalar::try_new(0.0).unwrap();
assert_eq!(zero1, zero2);
}
#[test]
fn partial_ord_trait() {
let scalar1 = NonNegativeRealScalar::try_new(0.0).unwrap();
let scalar2 = NonNegativeRealScalar::try_new(1.0).unwrap();
assert!(scalar1 < scalar2);
let scalar3 = NonNegativeRealScalar::try_new(3.0).unwrap();
let scalar4 = NonNegativeRealScalar::try_new(3.0).unwrap();
assert!(scalar3 <= scalar4);
assert!(scalar4 >= scalar3);
let zero = NonNegativeRealScalar::try_new(0.0).unwrap();
let positive = NonNegativeRealScalar::try_new(0.001).unwrap();
assert!(zero < positive);
}
#[test]
fn as_ref_trait() {
let scalar = NonNegativeRealScalar::try_new(2.0).unwrap();
let inner: &f64 = scalar.as_ref();
assert_eq!(*inner, 2.0);
let zero = NonNegativeRealScalar::try_new(0.0).unwrap();
let inner: &f64 = zero.as_ref();
assert_eq!(*inner, 0.0);
}
#[test]
fn into_inner() {
let scalar = NonNegativeRealScalar::try_new(42.0).unwrap();
let inner = scalar.into_inner();
assert_eq!(inner, 42.0);
let zero = NonNegativeRealScalar::try_new(0.0).unwrap();
let inner = zero.into_inner();
assert_eq!(inner, 0.0);
let scalar = NonNegativeRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
let inner = scalar.into_inner();
assert_eq!(inner, f64::MIN_POSITIVE);
}
#[test]
fn edge_cases() {
let zero_scalar = NonNegativeRealScalar::try_new(0.0).unwrap();
assert_eq!(*zero_scalar.as_ref(), 0.0);
let tiny_scalar = NonNegativeRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
assert_eq!(*tiny_scalar.as_ref(), f64::MIN_POSITIVE);
let large_scalar = NonNegativeRealScalar::try_new(f64::MAX).unwrap();
assert_eq!(*large_scalar.as_ref(), f64::MAX);
let epsilon_scalar = NonNegativeRealScalar::try_new(f64::EPSILON).unwrap();
assert_eq!(*epsilon_scalar.as_ref(), f64::EPSILON);
}
#[test]
fn generic_scalar_types() {
let scalar = NonNegativeRealScalar::try_new(
RealNative64StrictFiniteInDebug::try_new(5.0).unwrap(),
)
.unwrap();
assert_eq!(
*scalar.as_ref(),
RealNative64StrictFiniteInDebug::try_new(5.0).unwrap()
);
let zero_value = RealNative64StrictFiniteInDebug::try_new(0.0).unwrap();
let scalar_result = NonNegativeRealScalar::try_new(zero_value);
assert!(scalar_result.is_ok());
let negative_value = RealNative64StrictFiniteInDebug::try_new(-1.0).unwrap();
let scalar_result = NonNegativeRealScalar::try_new(negative_value);
assert!(matches!(
scalar_result,
Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
));
}
#[test]
fn mathematical_operations() {
let scalar1 = NonNegativeRealScalar::try_new(0.0).unwrap();
let scalar2 = NonNegativeRealScalar::try_new(2.0).unwrap();
let scalar3 = NonNegativeRealScalar::try_new(3.0).unwrap();
assert!(scalar1 < scalar2);
assert!(scalar2 < scalar3);
assert!(scalar3 > scalar1);
assert_eq!(
scalar1.partial_cmp(&scalar2),
Some(std::cmp::Ordering::Less)
);
assert_eq!(
scalar2.partial_cmp(&scalar1),
Some(std::cmp::Ordering::Greater)
);
}
#[test]
fn error_messages() {
let negative_result = NonNegativeRealScalar::try_new(-1.0);
match negative_result {
Err(ErrorsNonNegativeRealScalar::NegativeValue { value, .. }) => {
assert_eq!(value, -1.0);
}
_ => panic!("Expected NegativeValue error"),
}
let large_negative_result = NonNegativeRealScalar::try_new(-100.5);
match large_negative_result {
Err(ErrorsNonNegativeRealScalar::NegativeValue { value, .. }) => {
assert_eq!(value, -100.5);
}
_ => panic!("Expected NegativeValue error"),
}
}
#[test]
fn distinction_from_positive_real_scalar() {
let non_neg_zero = NonNegativeRealScalar::try_new(0.0);
assert!(
non_neg_zero.is_ok(),
"Zero should be valid for NonNegativeRealScalar (x ≥ 0)"
);
let pos_zero = PositiveRealScalar::try_new(0.0);
assert!(
pos_zero.is_err(),
"Zero should be invalid for PositiveRealScalar (x > 0)"
);
assert!(matches!(
pos_zero,
Err(ErrorsPositiveRealScalar::ZeroValue { .. })
));
let non_neg_positive = NonNegativeRealScalar::try_new(1.0);
let pos_positive = PositiveRealScalar::try_new(1.0);
assert!(non_neg_positive.is_ok());
assert!(pos_positive.is_ok());
let non_neg_negative = NonNegativeRealScalar::try_new(-1.0);
let pos_negative = PositiveRealScalar::try_new(-1.0);
assert!(non_neg_negative.is_err());
assert!(pos_negative.is_err());
}
#[test]
fn use_case_distance() {
fn compute_distance(x1: f64, x2: f64) -> NonNegativeRealScalar<f64> {
let diff = (x2 - x1).abs();
NonNegativeRealScalar::try_new(diff).unwrap()
}
let dist1 = compute_distance(0.0, 5.0);
assert_eq!(*dist1.as_ref(), 5.0);
let dist2 = compute_distance(3.0, 3.0);
assert_eq!(*dist2.as_ref(), 0.0);
}
#[test]
fn use_case_absolute_value() {
fn safe_abs(x: f64) -> NonNegativeRealScalar<f64> {
NonNegativeRealScalar::try_new(x.abs()).unwrap()
}
assert_eq!(*safe_abs(-5.0).as_ref(), 5.0);
assert_eq!(*safe_abs(0.0).as_ref(), 0.0); assert_eq!(*safe_abs(3.0).as_ref(), 3.0);
}
}
mod distinction_tests {
use super::*;
#[test]
fn positive_vs_non_negative_zero() {
assert!(PositiveRealScalar::try_new(0.0_f64).is_err());
assert!(NonNegativeRealScalar::try_new(0.0_f64).is_ok());
}
#[test]
fn both_accept_positive() {
let positive = 1.0_f64;
assert!(PositiveRealScalar::try_new(positive).is_ok());
assert!(NonNegativeRealScalar::try_new(positive).is_ok());
}
#[test]
fn both_reject_negative() {
let negative = -1.0_f64;
assert!(PositiveRealScalar::try_new(negative).is_err());
assert!(NonNegativeRealScalar::try_new(negative).is_err());
}
}
}