#![deny(rustdoc::broken_intra_doc_links)]
use crate::{
FpScalar, Pow, RealScalar, ScalarCore,
algorithms::accumulators::{NeumaierAddable, neumaier_sum_and_compensation_real},
core::{errors::capture_backtrace, traits::raw::RawScalarTrait},
functions::{Max, Min, PowIntExponentErrors},
};
use derive_more::{AsRef, Display, LowerExp};
use duplicate::duplicate_item;
use into_inner::IntoInner;
use num::{One, Zero};
use serde::{Deserialize, Serialize};
use std::{
backtrace::Backtrace,
hash::{Hash, Hasher},
ops::{Add, AddAssign, Mul, MulAssign},
};
use thiserror::Error;
use try_create::TryNew;
#[derive(Debug, Error)]
pub enum ErrorsPositiveRealScalar<RealType: RealScalar> {
#[error("Negative value detected: {value} (valid value must be strictly positive, i.e., > 0)")]
NegativeValue {
value: RealType,
backtrace: Backtrace,
},
#[error("Zero value detected (valid value must be strictly positive, i.e., > 0)")]
ZeroValue {
backtrace: Backtrace,
},
}
#[derive(Debug, Error)]
pub enum ErrorsNonNegativeRealScalar<RealType: RealScalar> {
#[error("Negative value detected: {value} (valid value must be non-negative, i.e., ≥ 0)")]
NegativeValue {
value: RealType,
backtrace: Backtrace,
},
}
#[derive(
Debug, Clone, PartialEq, PartialOrd, AsRef, IntoInner, Display, LowerExp, Serialize, Deserialize,
)]
#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
#[repr(transparent)]
pub struct AbsoluteTolerance<RealType: RealScalar>(NonNegativeRealScalar<RealType>);
impl<RealType: RealScalar> AbsoluteTolerance<RealType> {
#[inline(always)]
pub fn zero() -> Self {
AbsoluteTolerance(NonNegativeRealScalar::zero())
}
#[inline(always)]
pub fn epsilon() -> Self {
AbsoluteTolerance(NonNegativeRealScalar(RealType::epsilon()))
}
pub fn new(value: NonNegativeRealScalar<RealType>) -> Self {
AbsoluteTolerance(value)
}
pub fn try_new(value: RealType) -> Result<Self, ErrorsNonNegativeRealScalar<RealType>> {
NonNegativeRealScalar::try_new(value).map(AbsoluteTolerance)
}
}
impl<RealType: RealScalar> ScalarCore for AbsoluteTolerance<RealType> {
type RawScalarType = RealType::RawScalarType;
#[inline(always)]
fn as_raw_scalar_ref(&self) -> &Self::RawScalarType {
self.0.as_raw_scalar_ref()
}
fn into_raw_scalar(self) -> Self::RawScalarType {
self.0.into_raw_scalar()
}
}
pub fn approximately_equal<T: RealScalar>(a: T, b: T, tolerance: &AbsoluteTolerance<T>) -> bool {
let diff = (a - b).abs();
&diff <= tolerance.as_ref()
}
#[derive(
Debug, Clone, PartialEq, PartialOrd, AsRef, IntoInner, Display, LowerExp, Serialize, Deserialize,
)]
#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
#[repr(transparent)]
pub struct RelativeTolerance<RealType: RealScalar>(NonNegativeRealScalar<RealType>);
impl<RealType: RealScalar> RelativeTolerance<RealType> {
#[inline(always)]
pub fn zero() -> Self {
RelativeTolerance(NonNegativeRealScalar::zero())
}
#[inline(always)]
pub fn epsilon() -> Self {
RelativeTolerance(NonNegativeRealScalar(RealType::epsilon()))
}
#[inline(always)]
pub fn absolute_tolerance(&self, reference: RealType) -> AbsoluteTolerance<RealType> {
let abs_tol = reference.abs() * &self.0;
AbsoluteTolerance(abs_tol)
}
pub fn new(value: NonNegativeRealScalar<RealType>) -> Self {
RelativeTolerance(value)
}
pub fn try_new(value: RealType) -> Result<Self, ErrorsNonNegativeRealScalar<RealType>> {
NonNegativeRealScalar::try_new(value).map(RelativeTolerance)
}
}
impl<RealType: RealScalar> ScalarCore for RelativeTolerance<RealType> {
type RawScalarType = RealType::RawScalarType;
#[inline(always)]
fn as_raw_scalar_ref(&self) -> &Self::RawScalarType {
self.0.as_raw_scalar_ref()
}
fn into_raw_scalar(self) -> Self::RawScalarType {
self.0.into_raw_scalar()
}
}
#[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> PositiveRealScalar<RealType> {
pub fn sqrt(self) -> Self {
PositiveRealScalar(self.0.sqrt())
}
#[inline(always)]
pub fn new(value: RealType) -> Self {
#[cfg(debug_assertions)]
{
Self::try_new(value)
.expect("Error in the creation of the positive real scalar in debug mode")
}
#[cfg(not(debug_assertions))]
{
Self(value)
}
}
}
impl<RealType: RealScalar> ScalarCore for PositiveRealScalar<RealType> {
type RawScalarType = RealType::RawScalarType;
#[inline(always)]
fn as_raw_scalar_ref(&self) -> &Self::RawScalarType {
self.0.as_raw_scalar_ref()
}
fn into_raw_scalar(self) -> Self::RawScalarType {
self.0.into_raw_scalar()
}
}
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))
}
}
}
impl<RealType: RealScalar> Add for PositiveRealScalar<RealType> {
type Output = Self;
fn add(self, other: Self) -> Self {
PositiveRealScalar(self.0 + other.0)
}
}
impl<RealType: RealScalar> Mul for PositiveRealScalar<RealType> {
type Output = Self;
fn mul(self, other: Self) -> Self {
PositiveRealScalar(self.0 * other.0)
}
}
impl<'a, RealType: RealScalar> Add<&'a PositiveRealScalar<RealType>>
for PositiveRealScalar<RealType>
{
type Output = PositiveRealScalar<RealType>;
fn add(self, other: &'a PositiveRealScalar<RealType>) -> PositiveRealScalar<RealType> {
PositiveRealScalar(self.0 + other.0.clone())
}
}
impl<'a, RealType: RealScalar> Mul<&'a PositiveRealScalar<RealType>>
for PositiveRealScalar<RealType>
{
type Output = PositiveRealScalar<RealType>;
fn mul(self, other: &'a PositiveRealScalar<RealType>) -> PositiveRealScalar<RealType> {
PositiveRealScalar(self.0 * other.0.clone())
}
}
impl<'a, RealType: RealScalar> AddAssign<&'a PositiveRealScalar<RealType>>
for PositiveRealScalar<RealType>
{
fn add_assign(&mut self, other: &'a PositiveRealScalar<RealType>) {
self.0 += &other.0;
}
}
impl<RealType: RealScalar> AddAssign<PositiveRealScalar<RealType>>
for PositiveRealScalar<RealType>
{
fn add_assign(&mut self, other: PositiveRealScalar<RealType>) {
self.0 += other.0;
}
}
impl<'a, RealType: RealScalar> MulAssign<&'a PositiveRealScalar<RealType>>
for PositiveRealScalar<RealType>
{
fn mul_assign(&mut self, other: &'a PositiveRealScalar<RealType>) {
self.0 *= &other.0;
}
}
impl<RealType: RealScalar> MulAssign<PositiveRealScalar<RealType>>
for PositiveRealScalar<RealType>
{
fn mul_assign(&mut self, other: PositiveRealScalar<RealType>) {
self.0 *= other.0;
}
}
impl<RealType: RealScalar> One for PositiveRealScalar<RealType> {
fn one() -> Self {
PositiveRealScalar(RealType::one())
}
}
#[duplicate_item(
exponent_type;
[u8];
[i8];
[u16];
[i16];
[u32];
[i32];
[u64];
[i64];
[u128];
[i128];
[usize];
[isize];
)]
impl<RealType: RealScalar> Pow<exponent_type> for PositiveRealScalar<RealType> {
type Error = PowIntExponentErrors<<RealType as IntoInner>::InnerType, exponent_type>;
fn try_pow(self, exponent: exponent_type) -> Result<Self, Self::Error> {
self.0.try_pow(exponent).map(PositiveRealScalar)
}
#[inline(always)]
fn pow(self, exponent: exponent_type) -> Self {
#[cfg(debug_assertions)]
{
self.try_pow(exponent).unwrap()
}
#[cfg(not(debug_assertions))]
{
PositiveRealScalar(self.0.pow(exponent))
}
}
}
impl<RealType: RealScalar> Max for PositiveRealScalar<RealType> {}
impl<RealType: RealScalar> Min for PositiveRealScalar<RealType> {}
impl<RealType: RealScalar> NeumaierAddable for PositiveRealScalar<RealType> {
#[inline(always)]
fn neumaier_compensated_sum(value: Self, sum: &mut Self, compensation: &mut Self) {
neumaier_sum_and_compensation_real(value.into_inner(), &mut sum.0, &mut compensation.0)
}
}
#[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> NonNegativeRealScalar<RealType> {
pub fn sqrt(self) -> Self {
NonNegativeRealScalar(self.0.sqrt())
}
#[inline(always)]
pub fn new(value: RealType) -> Self {
#[cfg(debug_assertions)]
{
Self::try_new(value)
.expect("Error in the creation of the non-negative real scalar in debug mode")
}
#[cfg(not(debug_assertions))]
{
Self(value)
}
}
}
impl<RealType: RealScalar> ScalarCore for NonNegativeRealScalar<RealType> {
type RawScalarType = RealType::RawScalarType;
#[inline(always)]
fn as_raw_scalar_ref(&self) -> &Self::RawScalarType {
self.0.as_raw_scalar_ref()
}
fn into_raw_scalar(self) -> Self::RawScalarType {
self.0.into_raw_scalar()
}
}
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))
}
}
}
impl<RealType: RealScalar> Add for NonNegativeRealScalar<RealType> {
type Output = Self;
fn add(self, other: Self) -> Self {
NonNegativeRealScalar(self.0 + other.0)
}
}
impl<RealType: RealScalar> Mul for NonNegativeRealScalar<RealType> {
type Output = Self;
fn mul(self, other: Self) -> Self {
NonNegativeRealScalar(self.0 * other.0)
}
}
impl<'a, RealType: RealScalar> Add<&'a NonNegativeRealScalar<RealType>>
for NonNegativeRealScalar<RealType>
{
type Output = NonNegativeRealScalar<RealType>;
fn add(self, other: &'a NonNegativeRealScalar<RealType>) -> NonNegativeRealScalar<RealType> {
NonNegativeRealScalar(self.0 + other.0.clone())
}
}
impl<'a, RealType: RealScalar> Mul<&'a NonNegativeRealScalar<RealType>>
for NonNegativeRealScalar<RealType>
{
type Output = NonNegativeRealScalar<RealType>;
fn mul(self, other: &'a NonNegativeRealScalar<RealType>) -> NonNegativeRealScalar<RealType> {
NonNegativeRealScalar(self.0 * other.0.clone())
}
}
impl<'a, RealType: RealScalar> AddAssign<&'a NonNegativeRealScalar<RealType>>
for NonNegativeRealScalar<RealType>
{
fn add_assign(&mut self, other: &'a NonNegativeRealScalar<RealType>) {
self.0 += &other.0;
}
}
impl<RealType: RealScalar> AddAssign<NonNegativeRealScalar<RealType>>
for NonNegativeRealScalar<RealType>
{
fn add_assign(&mut self, other: NonNegativeRealScalar<RealType>) {
self.0 += other.0;
}
}
impl<'a, RealType: RealScalar> MulAssign<&'a NonNegativeRealScalar<RealType>>
for NonNegativeRealScalar<RealType>
{
fn mul_assign(&mut self, other: &'a NonNegativeRealScalar<RealType>) {
self.0 *= &other.0;
}
}
impl<RealType: RealScalar> MulAssign<NonNegativeRealScalar<RealType>>
for NonNegativeRealScalar<RealType>
{
fn mul_assign(&mut self, other: NonNegativeRealScalar<RealType>) {
self.0 *= other.0;
}
}
impl<RealType: RealScalar> Zero for NonNegativeRealScalar<RealType> {
fn is_zero(&self) -> bool {
self.0.is_zero()
}
fn zero() -> Self {
NonNegativeRealScalar(RealType::zero())
}
}
impl<RealType: RealScalar> One for NonNegativeRealScalar<RealType> {
fn one() -> Self {
NonNegativeRealScalar(RealType::one())
}
}
#[duplicate_item(
exponent_type;
[u8];
[i8];
[u16];
[i16];
[u32];
[i32];
[u64];
[i64];
[u128];
[i128];
[usize];
[isize];
)]
impl<RealType: RealScalar> Pow<exponent_type> for NonNegativeRealScalar<RealType> {
type Error = PowIntExponentErrors<<RealType as IntoInner>::InnerType, exponent_type>;
fn try_pow(self, exponent: exponent_type) -> Result<Self, Self::Error> {
self.0.try_pow(exponent).map(NonNegativeRealScalar)
}
#[inline(always)]
fn pow(self, exponent: exponent_type) -> Self {
#[cfg(debug_assertions)]
{
self.try_pow(exponent).unwrap()
}
#[cfg(not(debug_assertions))]
{
NonNegativeRealScalar(self.0.pow(exponent))
}
}
}
impl<RealType: RealScalar> Max for NonNegativeRealScalar<RealType> {}
impl<RealType: RealScalar> Min for NonNegativeRealScalar<RealType> {}
impl<RealType: RealScalar> NeumaierAddable for NonNegativeRealScalar<RealType> {
#[inline(always)]
fn neumaier_compensated_sum(value: Self, sum: &mut Self, compensation: &mut Self) {
neumaier_sum_and_compensation_real(value.into_inner(), &mut sum.0, &mut compensation.0)
}
}
impl<RealType: RealScalar> From<PositiveRealScalar<RealType>> for NonNegativeRealScalar<RealType> {
fn from(positive: PositiveRealScalar<RealType>) -> Self {
NonNegativeRealScalar(positive.into_inner())
}
}
impl<RealType: RealScalar> Add<NonNegativeRealScalar<RealType>> for PositiveRealScalar<RealType> {
type Output = PositiveRealScalar<RealType>;
fn add(self, other: NonNegativeRealScalar<RealType>) -> PositiveRealScalar<RealType> {
PositiveRealScalar(self.0 + other.0)
}
}
impl<'a, RealType: RealScalar> Add<&'a NonNegativeRealScalar<RealType>>
for PositiveRealScalar<RealType>
{
type Output = PositiveRealScalar<RealType>;
fn add(self, other: &'a NonNegativeRealScalar<RealType>) -> PositiveRealScalar<RealType> {
PositiveRealScalar(self.0 + &other.0)
}
}
impl<RealType: RealScalar> Add<PositiveRealScalar<RealType>> for NonNegativeRealScalar<RealType> {
type Output = PositiveRealScalar<RealType>;
fn add(self, other: PositiveRealScalar<RealType>) -> PositiveRealScalar<RealType> {
PositiveRealScalar(self.0 + other.0)
}
}
impl<'a, RealType: RealScalar> Add<&'a PositiveRealScalar<RealType>>
for NonNegativeRealScalar<RealType>
{
type Output = PositiveRealScalar<RealType>;
fn add(self, other: &'a PositiveRealScalar<RealType>) -> PositiveRealScalar<RealType> {
PositiveRealScalar(self.0 + &other.0)
}
}
#[derive(Debug, Error)]
pub enum ErrorsHashableScalar {
#[error("HashableScalar can only be constructed from finite scalar values")]
NonFiniteValue {
backtrace: Backtrace,
},
}
#[derive(Debug, Clone, PartialEq, PartialOrd, AsRef, Serialize, Deserialize)]
#[serde(bound(deserialize = "ScalarType: for<'a> Deserialize<'a>"))]
pub struct HashableScalar<ScalarType: FpScalar> {
value: ScalarType,
}
impl<ScalarType: FpScalar> Eq for HashableScalar<ScalarType> {}
impl<ScalarType: FpScalar> HashableScalar<ScalarType> {
#[must_use]
pub fn new(value: ScalarType) -> Self {
Self::try_new(value)
.expect("HashableScalar can only be constructed from finite scalar values")
}
pub fn try_new(value: ScalarType) -> Result<Self, ErrorsHashableScalar> {
if value.is_finite() {
Ok(Self { value })
} else {
Err(ErrorsHashableScalar::NonFiniteValue {
backtrace: capture_backtrace(),
})
}
}
}
impl<ScalarType: FpScalar> Hash for HashableScalar<ScalarType> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.value
.as_raw_scalar_ref()
.compute_hash_raw_scalar(state);
}
}
impl<ScalarType: FpScalar> IntoInner for HashableScalar<ScalarType> {
type InnerType = ScalarType;
#[inline(always)]
fn into_inner(self) -> ScalarType {
self.value
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::backends::native::validated::RealNative64StrictFinite;
mod hashable_scalar {
use super::*;
#[test]
fn try_new_rejects_non_finite_values() {
assert!(matches!(
HashableScalar::try_new(f64::NAN),
Err(ErrorsHashableScalar::NonFiniteValue { .. })
));
assert!(matches!(
HashableScalar::try_new(f64::INFINITY),
Err(ErrorsHashableScalar::NonFiniteValue { .. })
));
}
}
mod absolute_tolerance {
use super::*;
#[test]
fn try_new_valid() {
let tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1e-10_f64).unwrap());
assert_eq!(*tol.as_raw_scalar_ref(), 1e-10);
}
#[test]
fn try_new_zero() {
let tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.0_f64).unwrap());
assert_eq!(*tol.as_raw_scalar_ref(), 0.0);
}
#[test]
fn zero_constructor() {
let tol = AbsoluteTolerance::<f64>::zero();
assert_eq!(*tol.as_raw_scalar_ref(), 0.0);
}
#[test]
fn epsilon_constructor() {
let tol = AbsoluteTolerance::<f64>::epsilon();
assert_eq!(*tol.as_raw_scalar_ref(), f64::EPSILON);
}
#[test]
fn display_trait() {
let tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5_f64).unwrap());
assert_eq!(format!("{}", tol), "0.5");
}
#[test]
fn lower_exp_trait() {
let tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.00001_f64).unwrap());
assert_eq!(format!("{:e}", tol.into_inner().into_inner()), "1e-5");
}
#[test]
fn into_inner() {
let tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5_f64).unwrap());
let inner = tol.into_inner().into_inner();
assert_eq!(inner, 0.5);
}
#[test]
fn clone_and_partial_eq() {
let tol1 = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1e-10_f64).unwrap());
let tol2 = tol1.clone();
assert_eq!(tol1, tol2);
}
#[test]
fn partial_ord() {
let tol1 = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1e-10_f64).unwrap());
let tol2 = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1e-9_f64).unwrap());
assert!(tol1 < tol2);
}
#[test]
fn serialize_deserialize() {
let tol = AbsoluteTolerance::new(NonNegativeRealScalar::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::new(NonNegativeRealScalar::try_new(val).unwrap());
assert_eq!(tol.as_raw_scalar_ref(), &1e-10);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "is not finite")]
fn debug_panics_on_nan() {
let _ = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(f64::NAN).unwrap());
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "is not finite")]
fn debug_panics_on_infinity() {
let _ = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(f64::INFINITY).unwrap());
}
#[test]
fn try_new() {
let v = AbsoluteTolerance::<f64>::zero();
assert_eq!(*v.as_raw_scalar_ref(), 0.0);
let v = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(2.0).unwrap());
assert_eq!(*v.as_raw_scalar_ref(), 2.0);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic = "The input value inf is not finite!"]
fn try_new_invalid_infinite() {
let _tol =
AbsoluteTolerance::new(NonNegativeRealScalar::try_new(f64::INFINITY).unwrap());
}
#[test]
#[cfg(debug_assertions)]
#[should_panic = "The input value NaN is not finite!"]
fn try_new_invalid_nan() {
let _tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(f64::NAN).unwrap());
}
#[test]
fn epsilon() {
let epsilon_tol = AbsoluteTolerance::<f64>::epsilon();
assert_eq!(*epsilon_tol.as_raw_scalar_ref(), f64::EPSILON);
}
#[test]
fn equality_operator() {
let a = AbsoluteTolerance::<f64>::zero();
let b = AbsoluteTolerance::<f64>::zero();
assert!(a == b);
let c = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1.0).unwrap());
let d = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1.0).unwrap());
assert!(c == d);
}
#[test]
fn inequality_operator() {
let a = AbsoluteTolerance::<f64>::zero();
let b = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1.0).unwrap());
assert!(a != b);
let c = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1.0).unwrap());
let d = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(2.0).unwrap());
assert!(c != d);
}
#[test]
fn debug_trait() {
let tolerance = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5).unwrap());
assert_eq!(
format!("{:?}", tolerance),
"AbsoluteTolerance(NonNegativeRealScalar(0.5))"
);
}
#[test]
fn clone_trait() {
let tolerance = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5).unwrap());
let cloned_tolerance = tolerance.clone();
assert_eq!(tolerance, cloned_tolerance);
}
#[test]
fn partial_eq_trait() {
let tolerance1 = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5).unwrap());
let tolerance2 = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5).unwrap());
assert_eq!(tolerance1, tolerance2);
}
#[test]
fn partial_ord_trait() {
let tolerance1 = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5).unwrap());
let tolerance2 = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1.0).unwrap());
assert!(tolerance1 < tolerance2);
}
#[test]
fn as_ref_trait() {
let tolerance = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5).unwrap());
let inner: &f64 = tolerance.as_raw_scalar_ref();
assert_eq!(*inner, 0.5);
}
#[test]
fn zero() {
let zero_tol = AbsoluteTolerance::<f64>::zero();
assert_eq!(*zero_tol.as_raw_scalar_ref(), 0.0);
}
#[test]
fn edge_cases() {
let tiny_tol =
AbsoluteTolerance::new(NonNegativeRealScalar::try_new(f64::MIN_POSITIVE).unwrap());
assert_eq!(*tiny_tol.as_raw_scalar_ref(), f64::MIN_POSITIVE);
let large_tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1e100).unwrap());
assert_eq!(*large_tol.as_raw_scalar_ref(), 1e100);
}
#[test]
fn approximately_equal_exact_match() {
let a = 1.0_f64;
let b = 1.0_f64;
let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
assert!(approximately_equal(a, b, &tol));
}
#[test]
fn approximately_equal_within_tolerance() {
let a = 1.0_f64;
let b = 1.0 + 1e-11_f64;
let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
assert!(approximately_equal(a, b, &tol));
}
#[test]
fn approximately_equal_outside_tolerance() {
let a = 1.0_f64;
let b = 1.0 + 1e-9_f64;
let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
assert!(!approximately_equal(a, b, &tol));
}
#[test]
fn approximately_equal_negative_values() {
let a = -5.0_f64;
let b = -5.0 + 1e-12_f64;
let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
assert!(approximately_equal(a, b, &tol));
}
#[test]
fn approximately_equal_zero_tolerance() {
let a = 1.0_f64;
let b = 1.0_f64;
let tol = AbsoluteTolerance::<f64>::zero();
assert!(approximately_equal(a, b, &tol));
let c = 1.0_f64 + 1e-15;
assert!(!approximately_equal(a, c, &tol)); }
#[test]
fn approximately_equal_large_difference_outside_tolerance() {
let a = 1.0_f64;
let b = 2.0_f64;
let tol = AbsoluteTolerance::try_new(0.1_f64).unwrap();
assert!(!approximately_equal(a, b, &tol));
}
#[test]
fn approximately_equal_symmetric() {
let a = 1.0_f64;
let b = 1.0 + 1e-12_f64;
let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
assert_eq!(
approximately_equal(a, b, &tol),
approximately_equal(b, a, &tol)
);
}
#[test]
fn approximately_equal_with_validated_type() {
let a = RealNative64StrictFinite::try_from_f64(1.0).unwrap();
let b = RealNative64StrictFinite::try_from_f64(1.0 + 1e-11).unwrap();
let tol = AbsoluteTolerance::new(
NonNegativeRealScalar::try_new(
RealNative64StrictFinite::try_from_f64(1e-10).unwrap(),
)
.unwrap(),
);
assert!(approximately_equal(a, b, &tol));
}
}
mod relative_tolerance {
use super::*;
#[test]
fn try_new_valid() {
let tol = RelativeTolerance::try_new(0.01_f64).unwrap();
assert_eq!(*tol.as_raw_scalar_ref(), 0.01);
}
#[test]
fn try_new_zero() {
let tol = RelativeTolerance::try_new(0.0_f64).unwrap();
assert_eq!(*tol.as_raw_scalar_ref(), 0.0);
}
#[test]
fn try_new_negative() {
let result = RelativeTolerance::try_new(-0.01_f64);
assert!(matches!(
result,
Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
));
}
#[test]
fn zero_constructor() {
let tol = RelativeTolerance::<f64>::zero();
assert_eq!(*tol.as_raw_scalar_ref(), 0.0);
}
#[test]
fn epsilon_constructor() {
let tol = RelativeTolerance::<f64>::epsilon();
assert_eq!(*tol.as_raw_scalar_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_raw_scalar_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_raw_scalar_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_raw_scalar_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.as_raw_scalar_ref(), &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_raw_scalar_ref(), 0.0);
let v = RelativeTolerance::try_new(2.0).unwrap();
assert_eq!(*v.as_raw_scalar_ref(), 2.0);
}
#[test]
fn try_new_invalid_negative() {
let tol = RelativeTolerance::try_new(-0.1);
assert!(matches!(
tol,
Err(ErrorsNonNegativeRealScalar::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_raw_scalar_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_raw_scalar_ref(), 1.0);
let reference = -5.0;
let abs_tol = rel_tol.absolute_tolerance(reference);
assert_eq!(*abs_tol.as_raw_scalar_ref(), 0.5);
let reference = 0.0;
let abs_tol = rel_tol.absolute_tolerance(reference);
assert_eq!(*abs_tol.as_raw_scalar_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(NonNegativeRealScalar(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_raw_scalar_ref();
assert_eq!(*inner, 0.5);
}
#[test]
fn lower_exp_trait() {
let tolerance = RelativeTolerance::try_new(0.00001).unwrap();
assert_eq!(format!("{:e}", tolerance.as_raw_scalar_ref()), "1e-5");
}
}
mod positive_real_scalar {
use super::*;
use crate::backends::native::validated::RealNative64StrictFiniteInDebug;
#[test]
fn try_new_positive() {
let val = PositiveRealScalar::try_new(2.5_f64).unwrap();
assert_eq!(*val.as_raw_scalar_ref(), 2.5);
}
#[test]
fn try_new_min_positive() {
let val = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
assert_eq!(*val.as_raw_scalar_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_raw_scalar_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_raw_scalar_ref(), 2.5);
let v = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
assert_eq!(*v.as_raw_scalar_ref(), f64::MIN_POSITIVE);
let v = PositiveRealScalar::try_new(1e100).unwrap();
assert_eq!(*v.as_raw_scalar_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_raw_scalar_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_raw_scalar_ref(), f64::MIN_POSITIVE);
let large_scalar = PositiveRealScalar::try_new(f64::MAX).unwrap();
assert_eq!(*large_scalar.as_raw_scalar_ref(), f64::MAX);
let epsilon_scalar = PositiveRealScalar::try_new(f64::EPSILON).unwrap();
assert_eq!(*epsilon_scalar.as_raw_scalar_ref(), f64::EPSILON);
}
#[test]
fn generic_scalar_types() {
let scalar =
PositiveRealScalar::try_new(RealNative64StrictFiniteInDebug::try_new(5.0).unwrap())
.unwrap();
assert_eq!(scalar.as_raw_scalar_ref(), &5.0);
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"),
}
}
#[test]
fn sqrt_preserves_positivity() {
let base = PositiveRealScalar::try_new(4.0_f64).unwrap();
let result = base.sqrt();
assert_eq!(*result.as_raw_scalar_ref(), 2.0);
let base = PositiveRealScalar::try_new(9.0_f64).unwrap();
let result = base.sqrt();
assert_eq!(*result.as_raw_scalar_ref(), 3.0);
}
#[test]
fn sqrt_with_small_base() {
let base = PositiveRealScalar::try_new(0.01_f64).unwrap();
let result = base.sqrt();
assert_eq!(*result.as_raw_scalar_ref(), 0.1);
}
#[test]
fn sqrt_with_generic_types() {
let base_val = RealNative64StrictFiniteInDebug::try_new(4.0).unwrap();
let base = PositiveRealScalar::try_new(base_val).unwrap();
let result = base.sqrt();
assert_eq!(*result.as_raw_scalar_ref(), 2.0);
}
#[test]
fn new_constructor_debug_validates() {
#[cfg(debug_assertions)]
{
let val = PositiveRealScalar::new(2.5_f64);
assert_eq!(*val.as_raw_scalar_ref(), 2.5);
}
#[cfg(not(debug_assertions))]
{
let val = PositiveRealScalar::new(2.5_f64);
assert_eq!(*val.as_raw_scalar_ref(), 2.5);
}
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "Error in the creation")]
fn new_constructor_panics_on_zero_debug() {
let _ = PositiveRealScalar::new(0.0_f64);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "Error in the creation")]
fn new_constructor_panics_on_negative_debug() {
let _ = PositiveRealScalar::new(-1.0_f64);
}
#[test]
fn one_trait_identity() {
let one = PositiveRealScalar::<f64>::one();
assert_eq!(*one.as_raw_scalar_ref(), 1.0);
}
#[test]
fn one_trait_multiplicative_identity() {
let value = PositiveRealScalar::try_new(5.5_f64).unwrap();
let one = PositiveRealScalar::<f64>::one();
let result = value.clone() * one.clone();
assert_eq!(*result.as_raw_scalar_ref(), 5.5);
let result = one * value;
assert_eq!(*result.as_raw_scalar_ref(), 5.5);
}
#[test]
fn one_trait_with_generic_types() {
let one = PositiveRealScalar::<RealNative64StrictFiniteInDebug>::one();
assert_eq!(*one.as_raw_scalar_ref(), 1.0);
}
#[test]
fn pow_integer_exponents() {
let base = PositiveRealScalar::try_new(2.0_f64).unwrap();
let result = base.clone().pow(3u32);
assert_eq!(*result.as_raw_scalar_ref(), 8.0);
let result = base.clone().pow(2u8);
assert_eq!(*result.as_raw_scalar_ref(), 4.0);
let result = base.clone().pow(2i32);
assert_eq!(*result.as_raw_scalar_ref(), 4.0);
}
#[test]
fn pow_zero_exponent() {
let base = PositiveRealScalar::try_new(5.5_f64).unwrap();
let result = base.pow(0u32);
assert_eq!(*result.as_raw_scalar_ref(), 1.0);
}
#[test]
fn pow_one_exponent() {
let base = PositiveRealScalar::try_new(3.7_f64).unwrap();
let result = base.clone().pow(1u32);
assert_eq!(*result.as_raw_scalar_ref(), 3.7);
}
#[test]
fn pow_preserves_positivity() {
let base = PositiveRealScalar::try_new(2.5_f64).unwrap();
let result = base.clone().pow(5u32);
assert!(*result.as_raw_scalar_ref() > 0.0);
assert_eq!(*result.as_raw_scalar_ref(), 2.5_f64.powi(5));
}
#[test]
fn pow_with_try_pow() {
let base = PositiveRealScalar::try_new(3.0_f64).unwrap();
let result = base.clone().try_pow(2u32);
assert!(result.is_ok());
assert_eq!(*result.unwrap().as_raw_scalar_ref(), 9.0);
let result = base.try_pow(3i32);
assert!(result.is_ok());
assert_eq!(*result.unwrap().as_raw_scalar_ref(), 27.0);
}
#[test]
fn pow_large_exponents() {
let base = PositiveRealScalar::try_new(1.1_f64).unwrap();
let result = base.pow(10u32);
let expected = 1.1_f64.powi(10);
assert!((result.as_raw_scalar_ref() - expected).abs() < 1e-10);
}
#[test]
fn pow_small_base() {
let base = PositiveRealScalar::try_new(0.5_f64).unwrap();
let result = base.clone().pow(2u32);
assert_eq!(*result.as_raw_scalar_ref(), 0.25);
let result = base.pow(3u32);
assert_eq!(*result.as_raw_scalar_ref(), 0.125);
}
#[test]
fn pow_with_generic_types() {
let base_val = RealNative64StrictFiniteInDebug::try_new(2.0).unwrap();
let base = PositiveRealScalar::try_new(base_val).unwrap();
let result = base.pow(3u32);
assert_eq!(*result.as_raw_scalar_ref(), 8.0);
}
#[test]
fn pow_chain_operations() {
let base = PositiveRealScalar::try_new(2.0_f64).unwrap();
let result = base.pow(3u32).pow(2u32);
assert_eq!(*result.as_raw_scalar_ref(), 64.0);
}
#[test]
fn add_trait_value_semantics() {
let a = PositiveRealScalar::try_new(2.0_f64).unwrap();
let b = PositiveRealScalar::try_new(3.0_f64).unwrap();
let sum = a + b;
assert_eq!(*sum.as_raw_scalar_ref(), 5.0);
assert!(*sum.as_raw_scalar_ref() > 0.0);
}
#[test]
fn add_trait_reference_semantics() {
let a = PositiveRealScalar::try_new(2.0_f64).unwrap();
let b = PositiveRealScalar::try_new(3.0_f64).unwrap();
let sum = a + &b;
assert_eq!(*sum.as_raw_scalar_ref(), 5.0);
}
#[test]
fn mul_trait_value_semantics() {
let a = PositiveRealScalar::try_new(2.5_f64).unwrap();
let b = PositiveRealScalar::try_new(4.0_f64).unwrap();
let product = a * b;
assert_eq!(*product.as_raw_scalar_ref(), 10.0);
assert!(*product.as_raw_scalar_ref() > 0.0);
}
#[test]
fn mul_trait_reference_semantics() {
let a = PositiveRealScalar::try_new(2.5_f64).unwrap();
let b = PositiveRealScalar::try_new(4.0_f64).unwrap();
let product = a * &b;
assert_eq!(*product.as_raw_scalar_ref(), 10.0);
}
#[test]
fn add_assign_value_semantics() {
let mut a = PositiveRealScalar::try_new(2.0_f64).unwrap();
let b = PositiveRealScalar::try_new(3.0_f64).unwrap();
a += b;
assert_eq!(*a.as_raw_scalar_ref(), 5.0);
}
#[test]
fn add_assign_reference_semantics() {
let mut a = PositiveRealScalar::try_new(2.0_f64).unwrap();
let b = PositiveRealScalar::try_new(3.0_f64).unwrap();
a += &b;
assert_eq!(*a.as_raw_scalar_ref(), 5.0);
}
#[test]
fn mul_assign_value_semantics() {
let mut a = PositiveRealScalar::try_new(2.0_f64).unwrap();
let b = PositiveRealScalar::try_new(3.0_f64).unwrap();
a *= b;
assert_eq!(*a.as_raw_scalar_ref(), 6.0);
}
#[test]
fn mul_assign_reference_semantics() {
let mut a = PositiveRealScalar::try_new(2.0_f64).unwrap();
let b = PositiveRealScalar::try_new(3.0_f64).unwrap();
a *= &b;
assert_eq!(*a.as_raw_scalar_ref(), 6.0);
}
#[test]
fn max_min_traits() {
let a = PositiveRealScalar::try_new(1.0_f64).unwrap();
let b = PositiveRealScalar::try_new(3.0_f64).unwrap();
let max_val = Max::max_by_ref(&a, &b);
assert_eq!(*max_val.as_raw_scalar_ref(), 3.0);
let min_val = Min::min_by_ref(&a, &b);
assert_eq!(*min_val.as_raw_scalar_ref(), 1.0);
}
}
mod non_negative_real_scalar {
use super::*;
use crate::backends::native::validated::RealNative64StrictFiniteInDebug;
use crate::functions::{Max, Min};
use num::Zero;
#[test]
fn add_trait_preserves_non_negativity() {
let a = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
let b = NonNegativeRealScalar::try_new(3.5_f64).unwrap();
let sum = a + b;
assert_eq!(*sum.as_raw_scalar_ref(), 5.5);
assert!(*sum.as_raw_scalar_ref() >= 0.0);
}
#[test]
fn add_trait_zero_is_identity() {
let value = NonNegativeRealScalar::try_new(4.25_f64).unwrap();
let zero = NonNegativeRealScalar::<f64>::zero();
assert_eq!(*(value.clone() + zero.clone()).as_raw_scalar_ref(), 4.25);
assert_eq!(*(zero + value).as_raw_scalar_ref(), 4.25);
}
#[test]
fn zero_trait_methods() {
let zero = NonNegativeRealScalar::<f64>::zero();
let non_zero = NonNegativeRealScalar::try_new(0.5_f64).unwrap();
assert!(zero.is_zero());
assert!(!non_zero.is_zero());
assert_eq!(*zero.as_raw_scalar_ref(), 0.0);
}
#[test]
fn max_trait_by_ref_and_by_value() {
let small = NonNegativeRealScalar::try_new(1.0_f64).unwrap();
let large = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
let by_ref = Max::max_by_ref(&small, &large);
assert_eq!(*by_ref.as_raw_scalar_ref(), 3.0);
let by_value = Max::max_by_value(small, large);
assert_eq!(*by_value.as_raw_scalar_ref(), 3.0);
}
#[test]
fn min_trait_by_ref_and_by_value() {
let small = NonNegativeRealScalar::try_new(1.0_f64).unwrap();
let large = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
let by_ref = Min::min_by_ref(&small, &large);
assert_eq!(*by_ref.as_raw_scalar_ref(), 1.0);
let by_value = Min::min_by_value(small, large);
assert_eq!(*by_value.as_raw_scalar_ref(), 1.0);
}
#[test]
fn max_min_by_ref_equal_values_returns_left_operand() {
let left_max = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
let right_max = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
let selected_max = Max::max_by_ref(&left_max, &right_max);
assert!(std::ptr::eq(selected_max, &left_max));
let left_min = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
let right_min = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
let selected_min = Min::min_by_ref(&left_min, &right_min);
assert!(std::ptr::eq(selected_min, &left_min));
}
#[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_raw_scalar_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_raw_scalar_ref(), 2.5);
let v = NonNegativeRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
assert_eq!(*v.as_raw_scalar_ref(), f64::MIN_POSITIVE);
let v = NonNegativeRealScalar::try_new(1e100).unwrap();
assert_eq!(*v.as_raw_scalar_ref(), 1e100);
}
#[test]
fn try_new_zero() {
let v = NonNegativeRealScalar::try_new(0.0).unwrap();
assert_eq!(*v.as_raw_scalar_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_raw_scalar_ref();
assert_eq!(*inner, 2.0);
let zero = NonNegativeRealScalar::try_new(0.0).unwrap();
let inner: &f64 = zero.as_raw_scalar_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_raw_scalar_ref(), 0.0);
let tiny_scalar = NonNegativeRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
assert_eq!(*tiny_scalar.as_raw_scalar_ref(), f64::MIN_POSITIVE);
let large_scalar = NonNegativeRealScalar::try_new(f64::MAX).unwrap();
assert_eq!(*large_scalar.as_raw_scalar_ref(), f64::MAX);
let epsilon_scalar = NonNegativeRealScalar::try_new(f64::EPSILON).unwrap();
assert_eq!(*epsilon_scalar.as_raw_scalar_ref(), f64::EPSILON);
}
#[test]
fn generic_scalar_types() {
let scalar = NonNegativeRealScalar::try_new(
RealNative64StrictFiniteInDebug::try_new(5.0).unwrap(),
)
.unwrap();
assert_eq!(scalar.as_raw_scalar_ref(), &5.0);
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_raw_scalar_ref(), 5.0);
let dist2 = compute_distance(3.0, 3.0);
assert_eq!(*dist2.as_raw_scalar_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_raw_scalar_ref(), 5.0);
assert_eq!(*safe_abs(0.0).as_raw_scalar_ref(), 0.0); assert_eq!(*safe_abs(3.0).as_raw_scalar_ref(), 3.0);
}
#[test]
fn one_trait_identity() {
let one = NonNegativeRealScalar::<f64>::one();
assert_eq!(*one.as_raw_scalar_ref(), 1.0);
}
#[test]
fn one_trait_multiplicative_identity() {
let value = NonNegativeRealScalar::try_new(5.5_f64).unwrap();
let one = NonNegativeRealScalar::<f64>::one();
let result = value.clone() * one.clone();
assert_eq!(*result.as_raw_scalar_ref(), 5.5);
let result = one * value;
assert_eq!(*result.as_raw_scalar_ref(), 5.5);
}
#[test]
fn one_trait_with_generic_types() {
let one = NonNegativeRealScalar::<RealNative64StrictFiniteInDebug>::one();
assert_eq!(*one.as_raw_scalar_ref(), 1.0);
}
#[test]
fn pow_integer_exponents() {
let base = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
let result = base.clone().pow(3u32);
assert_eq!(*result.as_raw_scalar_ref(), 8.0);
let result = base.clone().pow(2u8);
assert_eq!(*result.as_raw_scalar_ref(), 4.0);
let result = base.clone().pow(2i32);
assert_eq!(*result.as_raw_scalar_ref(), 4.0);
}
#[test]
fn pow_zero_exponent() {
let base = NonNegativeRealScalar::try_new(5.5_f64).unwrap();
let result = base.clone().pow(0u32);
assert_eq!(*result.as_raw_scalar_ref(), 1.0);
let zero = NonNegativeRealScalar::try_new(0.0_f64).unwrap();
let result = zero.pow(0u32);
assert_eq!(*result.as_raw_scalar_ref(), 1.0);
}
#[test]
fn pow_one_exponent() {
let base = NonNegativeRealScalar::try_new(3.7_f64).unwrap();
let result = base.clone().pow(1u32);
assert_eq!(*result.as_raw_scalar_ref(), 3.7);
}
#[test]
fn pow_preserves_non_negativity() {
let base = NonNegativeRealScalar::try_new(2.5_f64).unwrap();
let result = base.clone().pow(5u32);
assert!(*result.as_raw_scalar_ref() >= 0.0);
assert_eq!(*result.as_raw_scalar_ref(), 2.5_f64.powi(5));
let zero = NonNegativeRealScalar::try_new(0.0_f64).unwrap();
let result = zero.pow(10u32);
assert_eq!(*result.as_raw_scalar_ref(), 0.0);
}
#[test]
fn pow_with_try_pow() {
let base = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
let result = base.clone().try_pow(2u32);
assert!(result.is_ok());
assert_eq!(*result.unwrap().as_raw_scalar_ref(), 9.0);
let result = base.clone().try_pow(3i32);
assert!(result.is_ok());
assert_eq!(*result.unwrap().as_raw_scalar_ref(), 27.0);
}
#[test]
fn pow_large_exponents() {
let base = NonNegativeRealScalar::try_new(1.1_f64).unwrap();
let result = base.pow(10u32);
let expected = 1.1_f64.powi(10);
assert!((result.as_raw_scalar_ref() - expected).abs() < 1e-10);
}
#[test]
fn pow_small_base() {
let base = NonNegativeRealScalar::try_new(0.5_f64).unwrap();
let result = base.clone().pow(2u32);
assert_eq!(*result.as_raw_scalar_ref(), 0.25);
let result = base.clone().pow(3u32);
assert_eq!(*result.as_raw_scalar_ref(), 0.125);
}
#[test]
fn pow_with_generic_types() {
let base_val = RealNative64StrictFiniteInDebug::try_new(2.0).unwrap();
let base = NonNegativeRealScalar::try_new(base_val).unwrap();
let result = base.pow(3u32);
assert_eq!(*result.as_raw_scalar_ref(), 8.0);
}
#[test]
fn pow_chain_operations() {
let base = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
let result = base.clone().pow(3u32).pow(2u32);
assert_eq!(*result.as_raw_scalar_ref(), 64.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());
}
}
mod conversion_tests {
use super::*;
use crate::backends::native::validated::RealNative64StrictFiniteInDebug;
#[test]
fn from_positive_to_non_negative() {
let positive = PositiveRealScalar::try_new(5.0_f64).unwrap();
let non_negative: NonNegativeRealScalar<f64> = positive.into();
assert_eq!(*non_negative.as_raw_scalar_ref(), 5.0);
}
#[test]
fn from_positive_to_non_negative_min_positive() {
let positive = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
let non_negative: NonNegativeRealScalar<f64> = positive.into();
assert_eq!(*non_negative.as_raw_scalar_ref(), f64::MIN_POSITIVE);
}
#[test]
fn from_positive_to_non_negative_large_value() {
let positive = PositiveRealScalar::try_new(f64::MAX).unwrap();
let non_negative: NonNegativeRealScalar<f64> = positive.into();
assert_eq!(*non_negative.as_raw_scalar_ref(), f64::MAX);
}
#[test]
fn from_positive_to_non_negative_one() {
let positive = PositiveRealScalar::try_new(1.0_f64).unwrap();
let non_negative: NonNegativeRealScalar<f64> = positive.into();
assert_eq!(*non_negative.as_raw_scalar_ref(), 1.0);
}
#[test]
fn from_positive_to_non_negative_preserves_value() {
let test_values = vec![0.5, 1.0, 2.5, 10.0, 99.9, 1e10, 1e-10];
for value in test_values {
let positive = PositiveRealScalar::try_new(value).unwrap();
let non_negative: NonNegativeRealScalar<f64> = positive.into();
assert_eq!(*non_negative.as_raw_scalar_ref(), value);
}
}
#[test]
fn from_positive_to_non_negative_with_generic_type() {
let inner = RealNative64StrictFiniteInDebug::try_new(5.0).unwrap();
let positive = PositiveRealScalar::try_new(inner).unwrap();
let non_negative: NonNegativeRealScalar<RealNative64StrictFiniteInDebug> =
positive.into();
assert_eq!(*non_negative.as_raw_scalar_ref(), 5.0);
}
#[test]
fn from_positive_consumed_by_conversion() {
let positive = PositiveRealScalar::try_new(5.0_f64).unwrap();
let _non_negative: NonNegativeRealScalar<f64> = positive.into();
}
#[test]
fn from_positive_creates_valid_non_negative() {
let positive = PositiveRealScalar::try_new(3.5_f64).unwrap();
let non_negative: NonNegativeRealScalar<f64> = positive.into();
assert!(*non_negative.as_raw_scalar_ref() >= 0.0);
assert!(!non_negative.is_zero()); }
#[test]
fn from_positive_mathematical_properties() {
let positive_a = PositiveRealScalar::try_new(2.0_f64).unwrap();
let positive_b = PositiveRealScalar::try_new(3.0_f64).unwrap();
let non_neg_a: NonNegativeRealScalar<f64> = positive_a.into();
let non_neg_b: NonNegativeRealScalar<f64> = positive_b.into();
let sum = non_neg_a + non_neg_b;
assert_eq!(*sum.as_raw_scalar_ref(), 5.0);
}
#[test]
fn from_positive_with_chained_operations() {
let positive = PositiveRealScalar::try_new(2.0_f64).unwrap();
let non_negative: NonNegativeRealScalar<f64> = positive.into();
let one = NonNegativeRealScalar::<f64>::one();
let result = non_negative * one;
assert_eq!(*result.as_raw_scalar_ref(), 2.0);
}
#[test]
fn from_positive_comparison_after_conversion() {
let positive_small = PositiveRealScalar::try_new(1.0_f64).unwrap();
let positive_large = PositiveRealScalar::try_new(3.0_f64).unwrap();
let non_neg_small: NonNegativeRealScalar<f64> = positive_small.into();
let non_neg_large: NonNegativeRealScalar<f64> = positive_large.into();
assert!(non_neg_small < non_neg_large);
assert!(non_neg_large > non_neg_small);
}
#[test]
fn from_positive_pow_after_conversion() {
let positive = PositiveRealScalar::try_new(2.0_f64).unwrap();
let non_negative: NonNegativeRealScalar<f64> = positive.into();
let squared = non_negative.pow(2u32);
assert_eq!(*squared.as_raw_scalar_ref(), 4.0);
}
#[test]
fn from_positive_sqrt_after_conversion() {
let positive = PositiveRealScalar::try_new(4.0_f64).unwrap();
let non_negative: NonNegativeRealScalar<f64> = positive.into();
let sqrt = non_negative.sqrt();
assert_eq!(*sqrt.as_raw_scalar_ref(), 2.0);
}
}
mod cross_type_arithmetic_tests {
use super::*;
use crate::backends::native::validated::RealNative64StrictFiniteInDebug;
#[test]
fn pos_add_non_neg_value() {
let pos = PositiveRealScalar::try_new(2.0_f64).unwrap();
let non_neg = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
let result: PositiveRealScalar<f64> = pos + non_neg;
assert_eq!(*result.as_raw_scalar_ref(), 5.0);
}
#[test]
fn pos_add_non_neg_ref() {
let pos = PositiveRealScalar::try_new(2.0_f64).unwrap();
let non_neg = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
let result: PositiveRealScalar<f64> = pos + &non_neg;
assert_eq!(*result.as_raw_scalar_ref(), 5.0);
assert_eq!(*non_neg.as_raw_scalar_ref(), 3.0);
}
#[test]
fn pos_add_non_neg_zero_preserves_positivity() {
let pos = PositiveRealScalar::try_new(5.0_f64).unwrap();
let zero = NonNegativeRealScalar::<f64>::zero();
let result: PositiveRealScalar<f64> = pos + zero;
assert_eq!(*result.as_raw_scalar_ref(), 5.0);
assert!(*result.as_raw_scalar_ref() > 0.0);
}
#[test]
fn pos_add_non_neg_zero_ref_preserves_positivity() {
let pos = PositiveRealScalar::try_new(5.0_f64).unwrap();
let zero = NonNegativeRealScalar::<f64>::zero();
let result: PositiveRealScalar<f64> = pos + &zero;
assert_eq!(*result.as_raw_scalar_ref(), 5.0);
}
#[test]
fn non_neg_add_pos_value() {
let non_neg = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
let pos = PositiveRealScalar::try_new(2.0_f64).unwrap();
let result: PositiveRealScalar<f64> = non_neg + pos;
assert_eq!(*result.as_raw_scalar_ref(), 5.0);
}
#[test]
fn non_neg_add_pos_ref() {
let non_neg = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
let pos = PositiveRealScalar::try_new(2.0_f64).unwrap();
let result: PositiveRealScalar<f64> = non_neg + &pos;
assert_eq!(*result.as_raw_scalar_ref(), 5.0);
assert_eq!(*pos.as_raw_scalar_ref(), 2.0);
}
#[test]
fn zero_add_pos_value() {
let zero = NonNegativeRealScalar::<f64>::zero();
let pos = PositiveRealScalar::try_new(5.0_f64).unwrap();
let result: PositiveRealScalar<f64> = zero + pos;
assert_eq!(*result.as_raw_scalar_ref(), 5.0);
assert!(*result.as_raw_scalar_ref() > 0.0);
}
#[test]
fn zero_add_pos_ref() {
let zero = NonNegativeRealScalar::<f64>::zero();
let pos = PositiveRealScalar::try_new(5.0_f64).unwrap();
let result: PositiveRealScalar<f64> = zero + &pos;
assert_eq!(*result.as_raw_scalar_ref(), 5.0);
}
#[test]
fn commutativity() {
let pos = PositiveRealScalar::try_new(2.0_f64).unwrap();
let non_neg = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
let left: PositiveRealScalar<f64> = pos.clone() + non_neg.clone();
let right: PositiveRealScalar<f64> = non_neg + pos;
assert_eq!(*left.as_raw_scalar_ref(), *right.as_raw_scalar_ref());
}
#[test]
fn cross_type_add_with_generic_type() {
let pos_inner = RealNative64StrictFiniteInDebug::try_new(2.0).unwrap();
let non_neg_inner = RealNative64StrictFiniteInDebug::try_new(3.0).unwrap();
let pos = PositiveRealScalar::try_new(pos_inner).unwrap();
let non_neg = NonNegativeRealScalar::try_new(non_neg_inner).unwrap();
let result: PositiveRealScalar<RealNative64StrictFiniteInDebug> = pos + non_neg;
assert_eq!(*result.as_raw_scalar_ref(), 5.0);
}
}
}