num_valid/scalars.rs
1#![deny(rustdoc::broken_intra_doc_links)]
2
3//! Type-safe scalar wrappers for numerical tolerances and constrained real values.
4//!
5//! This module provides strongly-typed wrappers around primitive scalar types to prevent
6//! value confusion and enforce mathematical constraints at compile time. These types are
7//! fundamental building blocks for numerical computations that require validated tolerances
8//! and constrained real number values.
9//!
10//! # Overview
11//!
12//! The module provides four primary types:
13//!
14//! | Type | Constraint | Zero Valid? | Use Cases |
15//! |------|------------|-------------|-----------|
16//! | [`AbsoluteTolerance<T>`] | `≥ 0` | ✅ Yes | Fixed error bounds for comparisons |
17//! | [`RelativeTolerance<T>`] | `≥ 0` | ✅ Yes | Proportional error bounds |
18//! | [`PositiveRealScalar<T>`] | `> 0` | ❌ No | Lengths, positive quantities |
19//! | [`NonNegativeRealScalar<T>`] | `≥ 0` | ✅ Yes | Distances, magnitudes, absolute values |
20//!
21//! All types are generic over [`RealScalar`], enabling consistent validation across
22//! different numerical backends (native `f64`, arbitrary-precision `rug`, etc.).
23//!
24//! ## Architectural Design
25//!
26//! - **Tolerance Types** ([`AbsoluteTolerance<T>`], [`RelativeTolerance<T>`]): Semantically specialized
27//! wrappers built on top of [`NonNegativeRealScalar<T>`]. They inherit all validation and provide
28//! tolerance-specific methods like [`RelativeTolerance::absolute_tolerance()`].
29//! - **Constrained Scalar Types** ([`PositiveRealScalar<T>`], [`NonNegativeRealScalar<T>`]): Full-featured
30//! types with algebraic operations (addition, multiplication, powers), comparison traits, and immutable
31//! access patterns. They form the foundation for general mathematical computations.
32//! - **Comparative Utilities**: The [`approximately_equal()`] function provides precise, tolerance-aware
33//! value comparisons across all scalar types.
34//!
35//! # Design Philosophy
36//!
37//! ## Type Safety Through Distinct Types
38//!
39//! Rather than using raw primitives like `f64` directly, this module provides
40//! semantically meaningful types that encode mathematical constraints:
41//!
42//! ```rust
43//! use num_valid::{
44//! backends::native::validated::RealNative64StrictFinite, RealScalar,
45//! scalars::{AbsoluteTolerance, RelativeTolerance, PositiveRealScalar},
46//! };
47//! use try_create::TryNew;
48//!
49//! // These are different types that cannot be confused:
50//! let abs_tol = AbsoluteTolerance::try_new(
51//! RealNative64StrictFinite::try_new(1e-10_f64).unwrap()
52//! ).unwrap(); // For absolute comparisons
53//! let rel_tol = RelativeTolerance::try_new(1e-6_f64).unwrap(); // For relative comparisons
54//! let length = PositiveRealScalar::try_new(5.0_f64).unwrap(); // Must be > 0
55//!
56//! // Type system prevents mixing them up:
57//! // let wrong: AbsoluteTolerance<f64> = rel_tol; // ← Compilation error!
58//! ```
59//!
60//! ## Validation at Construction Time
61//!
62//! All types validate their input at construction time, failing fast on invalid values:
63//!
64//! ```rust
65//! use num_valid::{*, scalars::*};
66//! use try_create::TryNew;
67//! use std::assert_matches;
68//!
69//! // Negative tolerances are rejected
70//! let invalid_tol = AbsoluteTolerance::try_new(
71//! RealNative64StrictFinite::try_new(-1e-6_f64).unwrap()
72//! ).unwrap_err();
73//! assert_matches!(invalid_tol, ErrorsNonNegativeRealScalar::NegativeValue { .. });
74//!
75//! // Zero is not positive
76//! let invalid_pos = PositiveRealScalar::try_new(0.0_f64).unwrap_err();
77//! assert_matches!(invalid_pos, ErrorsPositiveRealScalar::ZeroValue { .. });
78//! ```
79//!
80//! # Error Handling
81//!
82//! Both [`AbsoluteTolerance<T>`] and [`RelativeTolerance<T>`] use [`ErrorsNonNegativeRealScalar`]
83//! for error reporting, since they are wrappers around [`NonNegativeRealScalar<T>`]. This provides
84//! consistent error semantics:
85//!
86//! - **No negative values:** Both types reject negative inputs
87//! - **Debug validation:** NaN and infinity are panicked on in debug builds
88//! - **Backtrace support:** When the `backtrace` feature is enabled
89//!
90//! # Tolerance Types
91//!
92//! ## [`AbsoluteTolerance<T>`]
93//!
94//! Represents an absolute error bound for numerical comparisons. The tolerance value
95//! must be non-negative (≥ 0).
96//!
97//! ```rust
98//! use num_valid::{*, scalars::*};
99//! use try_create::TryNew;
100//!
101//! // Create tolerances
102//! let tight = AbsoluteTolerance::try_new(1e-12_f64).unwrap();
103//! let loose = AbsoluteTolerance::try_new(1e-6_f64).unwrap();
104//! let zero = AbsoluteTolerance::<f64>::zero(); // Exact comparison
105//! let epsilon = AbsoluteTolerance::<f64>::epsilon(); // Machine epsilon
106//!
107//! // Use in approximate comparison
108//! assert!(approximately_equal(1.0, 1.0 + 1e-13, &tight));
109//! assert!(!approximately_equal(1.0, 1.0 + 1e-11, &tight));
110//! ```
111//!
112//! ## [`RelativeTolerance<T>`]
113//!
114//! Represents a relative (proportional) error bound. Can be converted to an absolute
115//! tolerance based on a reference value.
116//!
117//! ```rust
118//! use num_valid::scalars::RelativeTolerance;
119//! use try_create::TryNew;
120//!
121//! let rel_tol = RelativeTolerance::try_new(0.01_f64).unwrap(); // 1% tolerance
122//!
123//! // Convert to absolute tolerance based on reference value
124//! let reference = 1000.0_f64;
125//! let abs_tol = rel_tol.absolute_tolerance(reference);
126//! assert_eq!(*abs_tol.as_ref().as_ref(), 10.0); // 1% of 1000 = 10
127//! ```
128//!
129//! # Constrained Real Number Types
130//!
131//! ## [`PositiveRealScalar<T>`]
132//!
133//! Wraps a real scalar that must be strictly positive (> 0). Zero is **not** valid.
134//!
135//! ```rust
136//! use num_valid::scalars::{PositiveRealScalar, ErrorsPositiveRealScalar};
137//! use try_create::TryNew;
138//!
139//! // Valid positive values
140//! let length = PositiveRealScalar::try_new(2.5_f64).unwrap();
141//! let tiny = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
142//!
143//! // Zero is NOT positive (x > 0 required)
144//! let zero_result = PositiveRealScalar::try_new(0.0_f64);
145//! assert!(matches!(zero_result, Err(ErrorsPositiveRealScalar::ZeroValue { .. })));
146//!
147//! // Negative values rejected
148//! let neg_result = PositiveRealScalar::try_new(-1.0_f64);
149//! assert!(matches!(neg_result, Err(ErrorsPositiveRealScalar::NegativeValue { .. })));
150//! ```
151//!
152//! ## [`NonNegativeRealScalar<T>`]
153//!
154//! Wraps a real scalar that must be non-negative (≥ 0). Zero **is** valid.
155//!
156//! ```rust
157//! use num_valid::scalars::{NonNegativeRealScalar, PositiveRealScalar};
158//! use try_create::TryNew;
159//!
160//! // Zero is valid for NonNegativeRealScalar
161//! let zero = NonNegativeRealScalar::try_new(0.0_f64).unwrap();
162//! assert_eq!(*zero.as_ref(), 0.0);
163//!
164//! // But NOT for PositiveRealScalar
165//! assert!(PositiveRealScalar::try_new(0.0_f64).is_err());
166//!
167//! // Use case: computing distances (can be zero)
168//! fn distance(a: f64, b: f64) -> NonNegativeRealScalar<f64> {
169//! NonNegativeRealScalar::try_new((a - b).abs()).unwrap()
170//! }
171//!
172//! let d = distance(5.0, 5.0); // Zero distance is valid
173//! assert_eq!(*d.as_ref(), 0.0);
174//! ```
175//!
176//! # Generic Programming with [`RealScalar`]
177//!
178//! All types work seamlessly with any scalar type implementing [`RealScalar`]:
179//!
180//! ```rust
181//! use num_valid::{
182//! backends::native::validated::{RealNative64StrictFinite, RealNative64StrictFiniteInDebug},
183//! RealScalar,
184//! scalars::AbsoluteTolerance
185//! };
186//! use try_create::TryNew;
187//!
188//! // Same tolerance type works with different backends
189//! type FastTol = AbsoluteTolerance<f64>;
190//! type SafeTol = AbsoluteTolerance<RealNative64StrictFinite>;
191//! type DebugTol = AbsoluteTolerance<RealNative64StrictFiniteInDebug>;
192//!
193//! let fast = FastTol::try_new(1e-10).unwrap();
194//! let safe = SafeTol::try_new(RealNative64StrictFinite::try_from_f64(1e-10).unwrap()).unwrap();
195//! ```
196//!
197//! # Performance Characteristics
198//!
199//! All wrapper types are designed as zero-cost abstractions:
200//!
201//! | Type | Memory Layout | Runtime Overhead |
202//! |------|---------------|------------------|
203//! | [`AbsoluteTolerance<T>`] | Same as `T` | Zero (validation at construction only) |
204//! | [`RelativeTolerance<T>`] | Same as `T` | Zero (validation at construction only) |
205//! | [`PositiveRealScalar<T>`] | Same as `T` | Zero (validation at construction only) |
206//! | [`NonNegativeRealScalar<T>`] | Same as `T` | Zero (validation at construction only) |
207//!
208//! The `#[repr(transparent)]` attribute ensures that each wrapper has the exact same
209//! memory layout as its underlying type.
210//!
211//! # Relationship to `approx` Crate
212//!
213//! These tolerance types complement the [`approx`] crate's comparison traits. While `approx`
214//! uses raw `f64` for epsilon values (which can be negative, causing silent failures),
215//! these wrappers guarantee non-negativity at construction time:
216//!
217//! ```rust
218//! use num_valid::scalars::AbsoluteTolerance;
219//! use num_valid::approx::assert_abs_diff_eq;
220//!
221//! // Create a validated tolerance
222//! let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
223//!
224//! // Use with approx (extract the inner value)
225//! let a = 1.0_f64;
226//! let b = 1.0 + 1e-11;
227//! assert_abs_diff_eq!(a, b, epsilon = *tol.as_ref().as_ref());
228//! ```
229
230use crate::{
231 Pow, RealScalar,
232 algorithms::accumulators::{NeumaierAddable, neumaier_sum_and_compensation_real},
233 core::errors::capture_backtrace,
234 functions::{Max, Min, PowIntExponentErrors},
235};
236use derive_more::{AsRef, Display, LowerExp};
237use duplicate::duplicate_item;
238use into_inner::IntoInner;
239use num::{One, Zero};
240use serde::{Deserialize, Serialize};
241use std::{
242 backtrace::Backtrace,
243 ops::{Add, AddAssign, Mul, MulAssign},
244};
245use thiserror::Error;
246use try_create::TryNew;
247
248//------------------------------------------------------------------------------------------------------------
249// Error Types
250//------------------------------------------------------------------------------------------------------------
251
252/// Error type for [`PositiveRealScalar<T>`] validation failures.
253///
254/// This enum provides detailed error information when attempting to construct
255/// a [`PositiveRealScalar<T>`] with invalid input values.
256///
257/// # Error Variants
258///
259/// - [`ErrorsPositiveRealScalar::NegativeValue`]: The input was negative (< 0).
260/// - [`ErrorsPositiveRealScalar::ZeroValue`]: The input was zero (not strictly positive).
261///
262/// # Mathematical Distinction
263///
264/// Positive real numbers are defined as `ℝ⁺ = {x ∈ ℝ : x > 0}`, which **excludes** zero.
265/// This is distinct from non-negative reals `ℝ₀⁺ = {x ∈ ℝ : x ≥ 0}`.
266///
267/// # Examples
268///
269/// ```rust
270/// use num_valid::scalars::{PositiveRealScalar, ErrorsPositiveRealScalar};
271/// use try_create::TryNew;
272///
273/// // Zero is NOT positive
274/// match PositiveRealScalar::try_new(0.0_f64) {
275/// Err(ErrorsPositiveRealScalar::ZeroValue { .. }) => {
276/// println!("Zero is not strictly positive (x > 0 required)");
277/// }
278/// _ => unreachable!(),
279/// }
280///
281/// // Negative values rejected with the value included in the error
282/// match PositiveRealScalar::try_new(-2.5_f64) {
283/// Err(ErrorsPositiveRealScalar::NegativeValue { value, .. }) => {
284/// assert_eq!(value, -2.5);
285/// }
286/// _ => unreachable!(),
287/// }
288/// ```
289#[derive(Debug, Error)]
290pub enum ErrorsPositiveRealScalar<RealType: RealScalar> {
291 /// The input value was negative (< 0).
292 #[error("Negative value detected: {value} (valid value must be strictly positive, i.e., > 0)")]
293 NegativeValue {
294 /// The specific negative value that was rejected.
295 value: RealType,
296 /// Stack trace for debugging.
297 backtrace: Backtrace,
298 },
299
300 /// The input value was exactly zero (not strictly positive).
301 ///
302 /// Zero is NOT positive in mathematical terms. The positive real numbers
303 /// are defined as `ℝ⁺ = {x ∈ ℝ : x > 0}`, which excludes zero.
304 #[error("Zero value detected (valid value must be strictly positive, i.e., > 0)")]
305 ZeroValue {
306 /// Stack trace for debugging.
307 backtrace: Backtrace,
308 },
309}
310
311/// Error type for [`NonNegativeRealScalar<T>`] validation failures.
312///
313/// This enum provides error information when attempting to construct a
314/// [`NonNegativeRealScalar<T>`] with a negative value.
315///
316/// # Error Variants
317///
318/// - [`ErrorsNonNegativeRealScalar::NegativeValue`]: The input was negative (< 0).
319///
320/// Note that zero is **valid** for [`NonNegativeRealScalar`], unlike [`PositiveRealScalar`].
321///
322/// # Examples
323///
324/// ```rust
325/// use num_valid::scalars::{NonNegativeRealScalar, ErrorsNonNegativeRealScalar};
326/// use try_create::TryNew;
327///
328/// // Zero is valid for NonNegativeRealScalar
329/// let zero = NonNegativeRealScalar::try_new(0.0_f64);
330/// assert!(zero.is_ok());
331///
332/// // Negative values are rejected
333/// match NonNegativeRealScalar::try_new(-1.0_f64) {
334/// Err(ErrorsNonNegativeRealScalar::NegativeValue { value, .. }) => {
335/// assert_eq!(value, -1.0);
336/// }
337/// _ => unreachable!(),
338/// }
339/// ```
340#[derive(Debug, Error)]
341pub enum ErrorsNonNegativeRealScalar<RealType: RealScalar> {
342 /// The input value was negative (must be ≥ 0).
343 #[error("Negative value detected: {value} (valid value must be non-negative, i.e., ≥ 0)")]
344 NegativeValue {
345 /// The negative value that was rejected.
346 value: RealType,
347 /// Stack trace for debugging.
348 backtrace: Backtrace,
349 },
350}
351
352//------------------------------------------------------------------------------------------------------------
353// AbsoluteTolerance
354//------------------------------------------------------------------------------------------------------------
355
356/// Type-safe wrapper for absolute tolerance values.
357///
358/// [`AbsoluteTolerance<T>`] represents a non-negative (≥ 0) tolerance value used for
359/// absolute error comparisons. It ensures that the tolerance is always valid by
360/// rejecting negative values at construction time.
361///
362/// # Implementation
363///
364/// This type is a `#[repr(transparent)]` wrapper around [`NonNegativeRealScalar<T>`].
365/// All validation is performed by the underlying `NonNegativeRealScalar`, and errors
366/// are reported as [`ErrorsNonNegativeRealScalar`].
367///
368/// # Mathematical Definition
369///
370/// An absolute tolerance `ε` is used in comparisons like:
371/// ```text
372/// |a - b| ≤ ε
373/// ```
374///
375/// Since `ε` represents a distance or error bound, it must be non-negative.
376///
377/// # Type Parameters
378///
379/// - `RealType`: The underlying real scalar type (e.g., `f64`, [`RealNative64StrictFinite`](crate::RealNative64StrictFinite))
380///
381/// # Examples
382///
383/// ## Basic Usage
384///
385/// ```rust
386/// use num_valid::scalars::AbsoluteTolerance;
387///
388/// // Create from f64
389/// let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
390/// assert_eq!(*tol.as_ref().as_ref(), 1e-10);
391///
392/// // Use convenience constructors
393/// let zero = AbsoluteTolerance::<f64>::zero();
394/// let eps = AbsoluteTolerance::<f64>::epsilon();
395/// ```
396///
397/// ## In Numerical Comparisons
398///
399/// ```rust
400/// use num_valid::{*, scalars::*};
401/// use try_create::{IntoInner, TryNew};
402///
403/// let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
404/// assert!(approximately_equal(1.0, 1.0 + 1e-11, &tol));
405/// assert!(!approximately_equal(1.0, 1.0 + 1e-9, &tol));
406/// ```
407///
408/// # Zero-Cost Abstraction
409///
410/// This type uses `#[repr(transparent)]` and has zero runtime overhead beyond
411/// the initial validation at construction time.
412#[derive(
413 Debug, Clone, PartialEq, PartialOrd, AsRef, IntoInner, Display, LowerExp, Serialize, Deserialize,
414)]
415#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
416#[repr(transparent)]
417pub struct AbsoluteTolerance<RealType: RealScalar>(NonNegativeRealScalar<RealType>);
418
419impl<RealType: RealScalar> AbsoluteTolerance<RealType> {
420 /// Creates an absolute tolerance of zero.
421 ///
422 /// A zero tolerance represents an exact comparison (no error allowed).
423 ///
424 /// # Examples
425 ///
426 /// ```rust
427 /// use num_valid::scalars::AbsoluteTolerance;
428 ///
429 /// let zero_tol = AbsoluteTolerance::<f64>::zero();
430 /// assert_eq!(*zero_tol.as_ref().as_ref(), 0.0);
431 /// ```
432 #[inline(always)]
433 pub fn zero() -> Self {
434 AbsoluteTolerance(NonNegativeRealScalar::zero())
435 }
436
437 /// Creates an absolute tolerance equal to machine epsilon.
438 ///
439 /// Machine epsilon is the smallest value such that `1.0 + epsilon != 1.0`.
440 /// This is often a good default tolerance for floating-point comparisons.
441 ///
442 /// # Examples
443 ///
444 /// ```rust
445 /// use num_valid::scalars::AbsoluteTolerance;
446 ///
447 /// let eps_tol = AbsoluteTolerance::<f64>::epsilon();
448 /// assert_eq!(*eps_tol.as_ref().as_ref(), f64::EPSILON);
449 /// ```
450 #[inline(always)]
451 pub fn epsilon() -> Self {
452 AbsoluteTolerance(NonNegativeRealScalar(RealType::epsilon()))
453 }
454
455 /// Wraps a [`NonNegativeRealScalar<T>`] as an [`AbsoluteTolerance<T>`].
456 ///
457 /// This constructor allows explicit conversion from a validated non-negative scalar
458 /// to a tolerance type, providing semantic clarity.
459 ///
460 /// # Examples
461 ///
462 /// ```rust
463 /// use num_valid::scalars::{AbsoluteTolerance, NonNegativeRealScalar};
464 /// use try_create::TryNew;
465 ///
466 /// let non_neg = NonNegativeRealScalar::try_new(1e-10_f64).unwrap();
467 /// let tol = AbsoluteTolerance::new(non_neg);
468 /// assert_eq!(*tol.as_ref().as_ref(), 1e-10);
469 /// ```
470 pub fn new(value: NonNegativeRealScalar<RealType>) -> Self {
471 AbsoluteTolerance(value)
472 }
473
474 /// Attempts to create a [`AbsoluteTolerance`] from a value.
475 ///
476 /// # Errors
477 ///
478 /// Returns [`ErrorsNonNegativeRealScalar::NegativeValue`] if the input is negative.
479 ///
480 /// # Panics (Debug Mode Only)
481 ///
482 /// In debug builds, panics if the input is not finite (NaN or infinity).
483 pub fn try_new(value: RealType) -> Result<Self, ErrorsNonNegativeRealScalar<RealType>> {
484 NonNegativeRealScalar::try_new(value).map(AbsoluteTolerance)
485 }
486}
487
488/// Compares two scalar values within an absolute tolerance.
489///
490/// Tests whether the absolute difference between two values is less than or equal to
491/// a specified tolerance. This is the standard approach for approximate floating-point
492/// comparisons when the expected magnitude of values is known.
493///
494/// # Mathematical Definition
495///
496/// Returns `true` if and only if:
497/// ```text
498/// |a - b| ≤ tolerance
499/// ```
500///
501/// # Symmetry
502///
503/// The comparison is symmetric: `approximately_equal(a, b, tol) == approximately_equal(b, a, tol)`
504///
505/// # Type Parameters
506///
507/// - `T`: Any scalar type implementing [`RealScalar`]
508///
509/// # Arguments
510///
511/// - `a`: First value
512/// - `b`: Second value to compare with
513/// - `tolerance`: The maximum allowed absolute difference (must be non-negative)
514///
515/// # Examples
516///
517/// ## Basic Floating-Point Comparison
518///
519/// ```rust
520/// use num_valid::scalars::{AbsoluteTolerance, approximately_equal};
521/// use try_create::TryNew;
522///
523/// let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
524///
525/// // Values within tolerance
526/// assert!(approximately_equal(1.0, 1.0 + 1e-11, &tol));
527///
528/// // Values outside tolerance
529/// assert!(!approximately_equal(1.0, 1.0 + 1e-9, &tol));
530///
531/// // Exact match
532/// assert!(approximately_equal(1.0, 1.0, &tol));
533/// ```
534///
535/// ## Zero Tolerance (Exact Comparison)
536///
537/// ```rust
538/// use num_valid::scalars::{AbsoluteTolerance, approximately_equal};
539///
540/// let exact_tol = AbsoluteTolerance::<f64>::zero();
541///
542/// // Only exact matches pass
543/// assert!(approximately_equal(1.0, 1.0, &exact_tol));
544/// assert!(!approximately_equal(1.0, 1.0 + 1e-15, &exact_tol));
545/// ```
546///
547/// ## Negative Values
548///
549/// ```rust
550/// use num_valid::scalars::{AbsoluteTolerance, approximately_equal};
551/// use try_create::TryNew;
552///
553/// let tol = AbsoluteTolerance::try_new(0.1_f64).unwrap();
554///
555/// // Works with negative values
556/// assert!(approximately_equal(-5.0, -5.05, &tol));
557/// assert!(!approximately_equal(-5.0, -5.2, &tol));
558/// ```
559///
560/// # When to Use
561///
562/// - **Use `approximately_equal()`** when you have a fixed maximum allowable error (absolute tolerance)
563/// - **Use [`RelativeTolerance::absolute_tolerance()`]** when the tolerance is proportional to values
564///
565/// # See Also
566///
567/// - [`AbsoluteTolerance`] for creating and managing absolute tolerances
568/// - [`RelativeTolerance`] for proportional error bounds
569pub fn approximately_equal<T: RealScalar>(a: T, b: T, tolerance: &AbsoluteTolerance<T>) -> bool {
570 let diff = (a - b).abs();
571 &diff <= tolerance.as_ref()
572}
573
574//------------------------------------------------------------------------------------------------------------
575// RelativeTolerance
576//------------------------------------------------------------------------------------------------------------
577
578/// Type-safe wrapper for relative tolerance values.
579///
580/// [`RelativeTolerance<T>`] represents a non-negative (≥ 0) tolerance value used for
581/// relative (proportional) error comparisons. It can be converted to an absolute
582/// tolerance based on a reference value.
583///
584/// # Implementation
585///
586/// This type is a `#[repr(transparent)]` wrapper around [`NonNegativeRealScalar<T>`].
587/// All validation is performed by the underlying `NonNegativeRealScalar`, and errors
588/// are reported as [`ErrorsNonNegativeRealScalar`].
589///
590/// # Mathematical Definition
591///
592/// A relative tolerance `ε_rel` is used in comparisons like:
593/// ```text
594/// |a - b| ≤ ε_rel × |reference|
595/// ```
596///
597/// Common relative tolerances:
598/// - `0.01` = 1% tolerance
599/// - `0.001` = 0.1% tolerance
600/// - `1e-6` = one part per million
601///
602/// # Examples
603///
604/// ## Basic Usage
605///
606/// ```rust
607/// use num_valid::scalars::RelativeTolerance;
608/// use try_create::TryNew;
609///
610/// let one_percent = RelativeTolerance::try_new(0.01_f64).unwrap();
611/// assert_eq!(*one_percent.as_ref().as_ref(), 0.01);
612/// ```
613///
614/// ## Converting to Absolute Tolerance
615///
616/// ```rust
617/// use num_valid::scalars::RelativeTolerance;
618/// use try_create::TryNew;
619///
620/// let rel_tol = RelativeTolerance::try_new(0.01_f64).unwrap(); // 1%
621///
622/// // Convert based on reference value
623/// let abs_tol = rel_tol.absolute_tolerance(1000.0);
624/// assert_eq!(*abs_tol.as_ref().as_ref(), 10.0); // 1% of 1000 = 10
625///
626/// // Works with negative references (uses absolute value)
627/// let abs_tol_neg = rel_tol.absolute_tolerance(-500.0);
628/// assert_eq!(*abs_tol_neg.as_ref().as_ref(), 5.0); // 1% of |-500| = 5
629/// ```
630#[derive(
631 Debug, Clone, PartialEq, PartialOrd, AsRef, IntoInner, Display, LowerExp, Serialize, Deserialize,
632)]
633#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
634#[repr(transparent)]
635pub struct RelativeTolerance<RealType: RealScalar>(NonNegativeRealScalar<RealType>);
636
637impl<RealType: RealScalar> RelativeTolerance<RealType> {
638 /// Creates a relative tolerance of zero.
639 ///
640 /// A zero relative tolerance represents an exact comparison.
641 ///
642 /// # Examples
643 ///
644 /// ```rust
645 /// use num_valid::scalars::RelativeTolerance;
646 ///
647 /// let zero_tol = RelativeTolerance::<f64>::zero();
648 /// assert_eq!(*zero_tol.as_ref().as_ref(), 0.0);
649 /// ```
650 #[inline(always)]
651 pub fn zero() -> Self {
652 RelativeTolerance(NonNegativeRealScalar::zero())
653 }
654
655 /// Creates a relative tolerance equal to machine epsilon.
656 ///
657 /// # Examples
658 ///
659 /// ```rust
660 /// use num_valid::scalars::RelativeTolerance;
661 ///
662 /// let eps_tol = RelativeTolerance::<f64>::epsilon();
663 /// assert_eq!(*eps_tol.as_ref().as_ref(), f64::EPSILON);
664 /// ```
665 #[inline(always)]
666 pub fn epsilon() -> Self {
667 RelativeTolerance(NonNegativeRealScalar(RealType::epsilon()))
668 }
669
670 /// Converts this relative tolerance to an absolute tolerance based on a reference value.
671 ///
672 /// The absolute tolerance is computed as:
673 /// ```text
674 /// absolute_tolerance = relative_tolerance × |reference|
675 /// ```
676 ///
677 /// # Parameters
678 ///
679 /// - `reference`: The reference value to scale by. The absolute value is used.
680 ///
681 /// # Examples
682 ///
683 /// ```rust
684 /// use num_valid::scalars::RelativeTolerance;
685 /// use try_create::TryNew;
686 ///
687 /// let rel_tol = RelativeTolerance::try_new(0.1_f64).unwrap(); // 10%
688 ///
689 /// // 10% of 100 = 10
690 /// let abs_tol = rel_tol.absolute_tolerance(100.0);
691 /// assert_eq!(*abs_tol.as_ref().as_ref(), 10.0);
692 ///
693 /// // 10% of |-50| = 5
694 /// let abs_tol_neg = rel_tol.absolute_tolerance(-50.0);
695 /// assert_eq!(*abs_tol_neg.as_ref().as_ref(), 5.0);
696 ///
697 /// // 10% of 0 = 0
698 /// let abs_tol_zero = rel_tol.absolute_tolerance(0.0);
699 /// assert_eq!(*abs_tol_zero.as_ref().as_ref(), 0.0);
700 /// ```
701 #[inline(always)]
702 pub fn absolute_tolerance(&self, reference: RealType) -> AbsoluteTolerance<RealType> {
703 let abs_tol = reference.abs() * &self.0;
704 AbsoluteTolerance(abs_tol)
705 }
706
707 /// Wraps a [`NonNegativeRealScalar<T>`] as a [`RelativeTolerance<T>`].
708 ///
709 /// This constructor allows explicit conversion from a validated non-negative scalar
710 /// to a tolerance type, providing semantic clarity.
711 ///
712 /// # Examples
713 ///
714 /// ```rust
715 /// use num_valid::scalars::{RelativeTolerance, NonNegativeRealScalar};
716 /// use try_create::TryNew;
717 ///
718 /// let non_neg = NonNegativeRealScalar::try_new(0.01_f64).unwrap(); // 1%
719 /// let tol = RelativeTolerance::new(non_neg);
720 /// assert_eq!(*tol.as_ref().as_ref(), 0.01);
721 /// ```
722 pub fn new(value: NonNegativeRealScalar<RealType>) -> Self {
723 RelativeTolerance(value)
724 }
725
726 /// Attempts to create a [`RelativeTolerance`] from a value.
727 ///
728 /// # Errors
729 ///
730 /// Returns [`ErrorsNonNegativeRealScalar::NegativeValue`] if the input is negative.
731 ///
732 /// # Panics (Debug Mode Only)
733 ///
734 /// In debug builds, panics if the input is not finite (NaN or infinity).
735 pub fn try_new(value: RealType) -> Result<Self, ErrorsNonNegativeRealScalar<RealType>> {
736 NonNegativeRealScalar::try_new(value).map(RelativeTolerance)
737 }
738}
739
740//------------------------------------------------------------------------------------------------------------
741// PositiveRealScalar
742//------------------------------------------------------------------------------------------------------------
743
744/// Type-safe wrapper for strictly positive real scalar values.
745///
746/// [`PositiveRealScalar<T>`] guarantees that wrapped values are strictly greater than zero.
747/// Zero is **not** valid for this type. For values that can be zero, use [`NonNegativeRealScalar`].
748///
749/// # Mathematical Definition
750///
751/// ```text
752/// PositiveRealScalar<T> = { x ∈ T : x > 0 }
753/// ```
754///
755/// This is the set of positive real numbers `ℝ⁺`, which **excludes** zero.
756///
757/// # Use Cases
758///
759/// - Lengths and distances that cannot be zero
760/// - Positive tolerances
761/// - Scaling factors that must be positive
762/// - Any quantity that is mathematically required to be > 0
763///
764/// # Examples
765///
766/// ## Basic Usage
767///
768/// ```rust
769/// use num_valid::scalars::{PositiveRealScalar, ErrorsPositiveRealScalar};
770/// use try_create::TryNew;
771///
772/// // Valid positive values
773/// let length = PositiveRealScalar::try_new(2.5_f64).unwrap();
774/// let tiny = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
775///
776/// // Zero is NOT positive
777/// assert!(matches!(
778/// PositiveRealScalar::try_new(0.0_f64),
779/// Err(ErrorsPositiveRealScalar::ZeroValue { .. })
780/// ));
781///
782/// // Negative values rejected
783/// assert!(matches!(
784/// PositiveRealScalar::try_new(-1.0_f64),
785/// Err(ErrorsPositiveRealScalar::NegativeValue { .. })
786/// ));
787/// ```
788///
789/// ## Difference from [`NonNegativeRealScalar`]
790///
791/// ```rust
792/// use num_valid::scalars::{PositiveRealScalar, NonNegativeRealScalar};
793/// use try_create::TryNew;
794///
795/// // Zero is INVALID for PositiveRealScalar (x > 0)
796/// assert!(PositiveRealScalar::try_new(0.0_f64).is_err());
797///
798/// // Zero is VALID for NonNegativeRealScalar (x ≥ 0)
799/// assert!(NonNegativeRealScalar::try_new(0.0_f64).is_ok());
800/// ```
801///
802/// ## Algebraic and Ordering Operations
803///
804/// [`PositiveRealScalar`] supports:
805/// - **Addition**: Sum of positive values is positive
806/// - **Multiplication**: Product of positive values is positive
807/// - **Integer powers**: Raising positive to any integer exponent yields positive
808/// - **Square root**: Preserves positivity (√x > 0 if x > 0)
809/// - **Multiplicative identity** ([`One`]): For neutral multiplication operations
810/// - **Comparison** ([`Max`], [`Min`]): Ordering and selection helpers
811///
812/// ```rust
813/// use num::One;
814/// use num_valid::{
815/// functions::{Max, Min, Pow},
816/// scalars::PositiveRealScalar,
817/// };
818/// use try_create::{IntoInner, TryNew};
819///
820/// let a = PositiveRealScalar::try_new(2.0_f64).unwrap();
821/// let b = PositiveRealScalar::try_new(3.0_f64).unwrap();
822///
823/// // Add preserves positivity for valid operands.
824/// let sum = a.clone() + b.clone();
825/// assert_eq!(*sum.as_ref(), 5.0);
826///
827/// // Multiply preserves positivity
828/// let product = a.clone() * b.clone();
829/// assert_eq!(*product.as_ref(), 6.0);
830///
831/// // Integer powers preserve positivity
832/// let squared = a.clone().pow(2u32);
833/// assert_eq!(*squared.as_ref(), 4.0);
834///
835/// // Square root preserves positivity
836/// let sqrt = PositiveRealScalar::try_new(4.0_f64).unwrap().sqrt();
837/// assert_eq!(*sqrt.as_ref(), 2.0);
838///
839/// // One is the multiplicative identity.
840/// let one = PositiveRealScalar::<f64>::one();
841/// let result = a.clone() * one;
842/// assert_eq!(*result.as_ref(), 2.0);
843///
844/// let x = PositiveRealScalar::try_new(1.0_f64).unwrap();
845/// let y = PositiveRealScalar::try_new(3.0_f64).unwrap();
846/// assert_eq!(*Max::max_by_ref(&x, &y).as_ref(), 3.0);
847/// assert_eq!(*Min::min_by_value(x, y).as_ref(), 1.0);
848/// ```
849#[derive(
850 Debug, Clone, PartialEq, PartialOrd, AsRef, IntoInner, Display, Serialize, Deserialize,
851)]
852#[repr(transparent)]
853#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
854pub struct PositiveRealScalar<RealType: RealScalar>(RealType);
855
856impl<RealType: RealScalar> PositiveRealScalar<RealType> {
857 /// Computes the square root of this positive scalar.
858 ///
859 /// Since the input is guaranteed to be positive, the result is always defined
860 /// and remains positive.
861 ///
862 /// # Examples
863 ///
864 /// ```rust
865 /// use num_valid::scalars::PositiveRealScalar;
866 /// use try_create::TryNew;
867 ///
868 /// let value = PositiveRealScalar::try_new(4.0_f64).unwrap();
869 /// let sqrt = value.sqrt();
870 /// assert_eq!(*sqrt.as_ref(), 2.0);
871 /// ```
872 pub fn sqrt(self) -> Self {
873 PositiveRealScalar(self.0.sqrt())
874 }
875
876 #[inline(always)]
877 pub fn new(value: RealType) -> Self {
878 #[cfg(debug_assertions)]
879 {
880 Self::try_new(value)
881 .expect("Error in the creation of the positive real scalar in debug mode")
882 }
883 #[cfg(not(debug_assertions))]
884 {
885 Self(value)
886 }
887 }
888}
889
890impl<RealType: RealScalar> TryNew for PositiveRealScalar<RealType> {
891 type Error = ErrorsPositiveRealScalar<RealType>;
892
893 /// Attempts to create a [`PositiveRealScalar`] from a value.
894 ///
895 /// # Errors
896 ///
897 /// - [`ErrorsPositiveRealScalar::NegativeValue`]: If the input is negative (< 0).
898 /// - [`ErrorsPositiveRealScalar::ZeroValue`]: If the input is zero.
899 ///
900 /// # Panics (Debug Mode Only)
901 ///
902 /// In debug builds, panics if the input is not finite (NaN or infinity).
903 ///
904 /// # Examples
905 ///
906 /// ```rust
907 /// use num_valid::scalars::{PositiveRealScalar, ErrorsPositiveRealScalar};
908 /// use try_create::TryNew;
909 ///
910 /// // Positive value succeeds
911 /// assert!(PositiveRealScalar::try_new(1.0_f64).is_ok());
912 ///
913 /// // Zero fails
914 /// assert!(matches!(
915 /// PositiveRealScalar::try_new(0.0_f64),
916 /// Err(ErrorsPositiveRealScalar::ZeroValue { .. })
917 /// ));
918 ///
919 /// // Negative fails
920 /// assert!(matches!(
921 /// PositiveRealScalar::try_new(-1.0_f64),
922 /// Err(ErrorsPositiveRealScalar::NegativeValue { value: v, .. }) if v == -1.0
923 /// ));
924 /// ```
925 fn try_new(value: RealType) -> Result<Self, Self::Error> {
926 debug_assert!(value.is_finite(), "The input value {value} is not finite!");
927 if value.kernel_is_sign_negative() {
928 Err(ErrorsPositiveRealScalar::NegativeValue {
929 value,
930 backtrace: capture_backtrace(),
931 })
932 } else if value.is_zero() {
933 Err(ErrorsPositiveRealScalar::ZeroValue {
934 backtrace: capture_backtrace(),
935 })
936 } else {
937 Ok(Self(value))
938 }
939 }
940}
941
942/// Implements additive composition for [`PositiveRealScalar`].
943///
944/// Since both operands are guaranteed to be positive, their sum is also positive.
945impl<RealType: RealScalar> Add for PositiveRealScalar<RealType> {
946 type Output = Self;
947
948 fn add(self, other: Self) -> Self {
949 PositiveRealScalar(self.0 + other.0)
950 }
951}
952
953/// Implements multiplicative composition for [`PositiveRealScalar`].
954///
955/// Since both operands are guaranteed to be positive, their product is also positive.
956impl<RealType: RealScalar> Mul for PositiveRealScalar<RealType> {
957 type Output = Self;
958
959 fn mul(self, other: Self) -> Self {
960 PositiveRealScalar(self.0 * other.0)
961 }
962}
963
964/// Implements reference-based addition for [`PositiveRealScalar`].
965///
966/// This allows adding a reference to a positive scalar without consuming it.
967/// Since both operands are guaranteed to be positive, their sum is also positive.
968impl<'a, RealType: RealScalar> Add<&'a PositiveRealScalar<RealType>>
969 for PositiveRealScalar<RealType>
970{
971 type Output = PositiveRealScalar<RealType>;
972
973 fn add(self, other: &'a PositiveRealScalar<RealType>) -> PositiveRealScalar<RealType> {
974 PositiveRealScalar(self.0 + other.0.clone())
975 }
976}
977
978/// Implements reference-based multiplication for [`PositiveRealScalar`].
979///
980/// This allows multiplying a positive scalar by a reference to another positive scalar
981/// without consuming it. Since both operands are guaranteed to be positive, their product is also positive.
982impl<'a, RealType: RealScalar> Mul<&'a PositiveRealScalar<RealType>>
983 for PositiveRealScalar<RealType>
984{
985 type Output = PositiveRealScalar<RealType>;
986
987 fn mul(self, other: &'a PositiveRealScalar<RealType>) -> PositiveRealScalar<RealType> {
988 PositiveRealScalar(self.0 * other.0.clone())
989 }
990}
991
992/// Implements in-place reference-based addition for [`PositiveRealScalar`].
993///
994/// This allows adding a reference to a positive scalar to `self` without consuming `self`.
995impl<'a, RealType: RealScalar> AddAssign<&'a PositiveRealScalar<RealType>>
996 for PositiveRealScalar<RealType>
997{
998 fn add_assign(&mut self, other: &'a PositiveRealScalar<RealType>) {
999 self.0 += &other.0;
1000 }
1001}
1002
1003/// Implements in-place addition for [`PositiveRealScalar`] by value.
1004///
1005/// This allows adding a positive scalar value to `self`.
1006impl<RealType: RealScalar> AddAssign<PositiveRealScalar<RealType>>
1007 for PositiveRealScalar<RealType>
1008{
1009 fn add_assign(&mut self, other: PositiveRealScalar<RealType>) {
1010 self.0 += other.0;
1011 }
1012}
1013
1014/// Implements in-place reference-based multiplication for [`PositiveRealScalar`].
1015///
1016/// This allows multiplying `self` by a reference to another positive scalar without consuming `self`.
1017impl<'a, RealType: RealScalar> MulAssign<&'a PositiveRealScalar<RealType>>
1018 for PositiveRealScalar<RealType>
1019{
1020 fn mul_assign(&mut self, other: &'a PositiveRealScalar<RealType>) {
1021 self.0 *= &other.0;
1022 }
1023}
1024
1025/// Implements in-place multiplication for [`PositiveRealScalar`] by value.
1026///
1027/// This allows multiplying `self` by a positive scalar value.
1028impl<RealType: RealScalar> MulAssign<PositiveRealScalar<RealType>>
1029 for PositiveRealScalar<RealType>
1030{
1031 fn mul_assign(&mut self, other: PositiveRealScalar<RealType>) {
1032 self.0 *= other.0;
1033 }
1034}
1035
1036impl<RealType: RealScalar> One for PositiveRealScalar<RealType> {
1037 fn one() -> Self {
1038 PositiveRealScalar(RealType::one())
1039 }
1040}
1041
1042#[duplicate_item(
1043 exponent_type;
1044 [u8];
1045 [i8];
1046 [u16];
1047 [i16];
1048 [u32];
1049 [i32];
1050 [u64];
1051 [i64];
1052 [u128];
1053 [i128];
1054 [usize];
1055 [isize];
1056)]
1057impl<RealType: RealScalar> Pow<exponent_type> for PositiveRealScalar<RealType> {
1058 type Error = PowIntExponentErrors<<RealType as IntoInner>::InnerType, exponent_type>;
1059
1060 fn try_pow(self, exponent: exponent_type) -> Result<Self, Self::Error> {
1061 self.0.try_pow(exponent).map(PositiveRealScalar)
1062 }
1063
1064 #[inline(always)]
1065 /// Raises the number `self` to the power `exponent`.
1066 fn pow(self, exponent: exponent_type) -> Self {
1067 #[cfg(debug_assertions)]
1068 {
1069 self.try_pow(exponent).unwrap()
1070 }
1071 #[cfg(not(debug_assertions))]
1072 {
1073 PositiveRealScalar(self.0.pow(exponent))
1074 }
1075 }
1076}
1077
1078/// Implements [`Max`] for [`PositiveRealScalar`].
1079///
1080/// Maximum selection is delegated to the default trait behavior based on [`PartialOrd`].
1081impl<RealType: RealScalar> Max for PositiveRealScalar<RealType> {}
1082/// Implements [`Min`] for [`PositiveRealScalar`].
1083///
1084/// Minimum selection is delegated to the default trait behavior based on [`PartialOrd`].
1085impl<RealType: RealScalar> Min for PositiveRealScalar<RealType> {}
1086
1087/// Implements Neumaier compensated summation for [`PositiveRealScalar`].
1088///
1089/// This implementation delegates to the underlying real scalar type's Neumaier
1090/// compensated sum operation, preserving numerical stability for sums of positive values.
1091impl<RealType: RealScalar> NeumaierAddable for PositiveRealScalar<RealType> {
1092 #[inline(always)]
1093 fn neumaier_compensated_sum(value: Self, sum: &mut Self, compensation: &mut Self) {
1094 neumaier_sum_and_compensation_real(value.into_inner(), &mut sum.0, &mut compensation.0)
1095 }
1096}
1097
1098//------------------------------------------------------------------------------------------------------------
1099// NonNegativeRealScalar
1100//------------------------------------------------------------------------------------------------------------
1101
1102/// Type-safe wrapper for non-negative real scalar values.
1103///
1104/// [`NonNegativeRealScalar<T>`] guarantees that wrapped values are greater than or equal to zero.
1105/// Zero **is** valid for this type. For values that must be strictly positive, use [`PositiveRealScalar`].
1106///
1107/// # Mathematical Definition
1108///
1109/// ```text
1110/// NonNegativeRealScalar<T> = { x ∈ T : x ≥ 0 }
1111/// ```
1112///
1113/// This is the set of non-negative real numbers `ℝ₀⁺`, which **includes** zero.
1114///
1115/// # Use Cases
1116///
1117/// - Distances (can be zero when comparing a value to itself)
1118/// - Absolute values
1119/// - Magnitudes
1120/// - Any quantity that is mathematically required to be ≥ 0
1121///
1122/// # Examples
1123///
1124/// ## Basic Usage
1125///
1126/// ```rust
1127/// use num_valid::scalars::{NonNegativeRealScalar, ErrorsNonNegativeRealScalar};
1128/// use try_create::TryNew;
1129///
1130/// // Zero is valid
1131/// let zero = NonNegativeRealScalar::try_new(0.0_f64).unwrap();
1132/// assert_eq!(*zero.as_ref(), 0.0);
1133///
1134/// // Positive values are valid
1135/// let positive = NonNegativeRealScalar::try_new(5.0_f64).unwrap();
1136///
1137/// // Negative values are rejected
1138/// assert!(matches!(
1139/// NonNegativeRealScalar::try_new(-1.0_f64),
1140/// Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
1141/// ));
1142/// ```
1143///
1144/// ## Use Case: Computing Distances
1145///
1146/// ```rust
1147/// use num_valid::scalars::NonNegativeRealScalar;
1148/// use try_create::TryNew;
1149///
1150/// fn distance(a: f64, b: f64) -> NonNegativeRealScalar<f64> {
1151/// NonNegativeRealScalar::try_new((a - b).abs()).unwrap()
1152/// }
1153///
1154/// // Different points
1155/// let d1 = distance(0.0, 5.0);
1156/// assert_eq!(*d1.as_ref(), 5.0);
1157///
1158/// // Same point (zero distance is valid!)
1159/// let d2 = distance(3.0, 3.0);
1160/// assert_eq!(*d2.as_ref(), 0.0);
1161/// ```
1162///
1163/// ## Algebraic and Ordering Operations
1164///
1165/// [`NonNegativeRealScalar`] supports:
1166/// - **Addition**: Sum of non-negative values is non-negative
1167/// - **Multiplication**: Product of non-negative values is non-negative
1168/// - **Integer powers**: Raising non-negative to any integer exponent yields non-negative
1169/// - **Additive identity** ([`Zero`]): For neutral addition operations
1170/// - **Multiplicative identity** ([`One`]): For neutral multiplication operations
1171/// - **Comparison** ([`Max`], [`Min`]): Ordering and selection helpers
1172///
1173/// ```rust
1174/// use num::{One, Zero};
1175/// use num_valid::{
1176/// functions::{Max, Min, Pow},
1177/// scalars::NonNegativeRealScalar,
1178/// };
1179/// use try_create::TryNew;
1180///
1181/// let a = NonNegativeRealScalar::try_new(1.25_f64).unwrap();
1182/// let b = NonNegativeRealScalar::try_new(2.75_f64).unwrap();
1183///
1184/// // Add preserves non-negativity for valid operands.
1185/// let sum = a.clone() + &b;
1186/// assert_eq!(*sum.as_ref(), 4.0);
1187///
1188/// // Multiply preserves non-negativity
1189/// let product = a * b;
1190/// assert!(*product.as_ref() > 0.0);
1191///
1192/// // Integer powers preserve non-negativity
1193/// let base = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
1194/// let squared = base.clone().pow(2u32);
1195/// assert_eq!(*squared.as_ref(), 4.0);
1196///
1197/// // Zero is the additive identity.
1198/// let z = NonNegativeRealScalar::<f64>::zero();
1199/// assert!(z.is_zero());
1200/// assert_eq!(*(sum + z).as_ref(), 4.0);
1201///
1202/// // One is the multiplicative identity.
1203/// let one = NonNegativeRealScalar::<f64>::one();
1204/// let result = base.clone() * one;
1205/// assert_eq!(*result.as_ref(), 2.0);
1206///
1207/// let x = NonNegativeRealScalar::try_new(1.0_f64).unwrap();
1208/// let y = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
1209/// assert_eq!(*Max::max_by_ref(&x, &y).as_ref(), 3.0);
1210/// assert_eq!(*Min::min_by_value(x, y).as_ref(), 1.0);
1211/// ```
1212#[derive(
1213 Debug, Clone, PartialEq, PartialOrd, AsRef, IntoInner, Display, Serialize, Deserialize,
1214)]
1215#[repr(transparent)]
1216#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
1217pub struct NonNegativeRealScalar<RealType: RealScalar>(RealType);
1218
1219impl<RealType: RealScalar> NonNegativeRealScalar<RealType> {
1220 /// Computes the square root of this non-negative scalar.
1221 ///
1222 /// Since the input is guaranteed to be non-negative, the result is always defined
1223 /// and remains non-negative.
1224 ///
1225 /// # Examples
1226 ///
1227 /// ```rust
1228 /// use num_valid::scalars::NonNegativeRealScalar;
1229 /// use try_create::TryNew;
1230 ///
1231 /// let value = NonNegativeRealScalar::try_new(4.0_f64).unwrap();
1232 /// let sqrt = value.sqrt();
1233 /// assert_eq!(*sqrt.as_ref(), 2.0);
1234 /// ```
1235 pub fn sqrt(self) -> Self {
1236 NonNegativeRealScalar(self.0.sqrt())
1237 }
1238
1239 #[inline(always)]
1240 pub fn new(value: RealType) -> Self {
1241 #[cfg(debug_assertions)]
1242 {
1243 Self::try_new(value)
1244 .expect("Error in the creation of the non-negative real scalar in debug mode")
1245 }
1246 #[cfg(not(debug_assertions))]
1247 {
1248 Self(value)
1249 }
1250 }
1251}
1252
1253impl<RealType: RealScalar> TryNew for NonNegativeRealScalar<RealType> {
1254 type Error = ErrorsNonNegativeRealScalar<RealType>;
1255
1256 /// Attempts to create a [`NonNegativeRealScalar`] from a value.
1257 ///
1258 /// # Errors
1259 ///
1260 /// Returns [`ErrorsNonNegativeRealScalar::NegativeValue`] if the input is negative.
1261 ///
1262 /// # Panics (Debug Mode Only)
1263 ///
1264 /// In debug builds, panics if the input is not finite (NaN or infinity).
1265 ///
1266 /// # Examples
1267 ///
1268 /// ```rust
1269 /// use num_valid::scalars::{NonNegativeRealScalar, ErrorsNonNegativeRealScalar};
1270 /// use try_create::TryNew;
1271 ///
1272 /// // Zero is valid
1273 /// assert!(NonNegativeRealScalar::try_new(0.0_f64).is_ok());
1274 ///
1275 /// // Positive is valid
1276 /// assert!(NonNegativeRealScalar::try_new(1.0_f64).is_ok());
1277 ///
1278 /// // Negative is rejected
1279 /// assert!(matches!(
1280 /// NonNegativeRealScalar::try_new(-1.0_f64),
1281 /// Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
1282 /// ));
1283 /// ```
1284 fn try_new(value: RealType) -> Result<Self, Self::Error> {
1285 debug_assert!(value.is_finite(), "The input value {value} is not finite!");
1286 if value.kernel_is_sign_negative() {
1287 Err(ErrorsNonNegativeRealScalar::NegativeValue {
1288 value,
1289 backtrace: capture_backtrace(),
1290 })
1291 } else {
1292 Ok(Self(value))
1293 }
1294 }
1295}
1296
1297/// Implements additive composition for [`NonNegativeRealScalar`].
1298///
1299/// Since both operands are guaranteed to be non-negative, their sum is also non-negative.
1300impl<RealType: RealScalar> Add for NonNegativeRealScalar<RealType> {
1301 type Output = Self;
1302
1303 fn add(self, other: Self) -> Self {
1304 NonNegativeRealScalar(self.0 + other.0)
1305 }
1306}
1307
1308/// Implements multiplicative composition for [`NonNegativeRealScalar`].
1309///
1310/// Since both operands are guaranteed to be non-negative, their product is also non-negative.
1311impl<RealType: RealScalar> Mul for NonNegativeRealScalar<RealType> {
1312 type Output = Self;
1313
1314 fn mul(self, other: Self) -> Self {
1315 NonNegativeRealScalar(self.0 * other.0)
1316 }
1317}
1318
1319/// Implements reference-based addition for [`NonNegativeRealScalar`].
1320///
1321/// This allows adding a reference to a non-negative scalar without consuming it.
1322/// Since both operands are guaranteed to be non-negative, their sum is also non-negative.
1323impl<'a, RealType: RealScalar> Add<&'a NonNegativeRealScalar<RealType>>
1324 for NonNegativeRealScalar<RealType>
1325{
1326 type Output = NonNegativeRealScalar<RealType>;
1327
1328 fn add(self, other: &'a NonNegativeRealScalar<RealType>) -> NonNegativeRealScalar<RealType> {
1329 NonNegativeRealScalar(self.0 + other.0.clone())
1330 }
1331}
1332
1333/// Implements reference-based multiplication for [`NonNegativeRealScalar`].
1334///
1335/// This allows multiplying a non-negative scalar by a reference to another non-negative scalar
1336/// without consuming it. Since both operands are guaranteed to be non-negative, their product is also non-negative.
1337impl<'a, RealType: RealScalar> Mul<&'a NonNegativeRealScalar<RealType>>
1338 for NonNegativeRealScalar<RealType>
1339{
1340 type Output = NonNegativeRealScalar<RealType>;
1341
1342 fn mul(self, other: &'a NonNegativeRealScalar<RealType>) -> NonNegativeRealScalar<RealType> {
1343 NonNegativeRealScalar(self.0 * other.0.clone())
1344 }
1345}
1346
1347/// Implements in-place reference-based addition for [`NonNegativeRealScalar`].
1348///
1349/// This allows adding a reference to a non-negative scalar to `self` without consuming `self`.
1350impl<'a, RealType: RealScalar> AddAssign<&'a NonNegativeRealScalar<RealType>>
1351 for NonNegativeRealScalar<RealType>
1352{
1353 fn add_assign(&mut self, other: &'a NonNegativeRealScalar<RealType>) {
1354 self.0 += &other.0;
1355 }
1356}
1357
1358/// Implements in-place addition for [`NonNegativeRealScalar`] by value.
1359///
1360/// This allows adding a non-negative scalar value to `self`.
1361impl<RealType: RealScalar> AddAssign<NonNegativeRealScalar<RealType>>
1362 for NonNegativeRealScalar<RealType>
1363{
1364 fn add_assign(&mut self, other: NonNegativeRealScalar<RealType>) {
1365 self.0 += other.0;
1366 }
1367}
1368
1369/// Implements in-place reference-based multiplication for [`NonNegativeRealScalar`].
1370///
1371/// This allows multiplying `self` by a reference to another non-negative scalar without consuming `self`.
1372impl<'a, RealType: RealScalar> MulAssign<&'a NonNegativeRealScalar<RealType>>
1373 for NonNegativeRealScalar<RealType>
1374{
1375 fn mul_assign(&mut self, other: &'a NonNegativeRealScalar<RealType>) {
1376 self.0 *= &other.0;
1377 }
1378}
1379
1380/// Implements in-place multiplication for [`NonNegativeRealScalar`] by value.
1381///
1382/// This allows multiplying `self` by a non-negative scalar value.
1383impl<RealType: RealScalar> MulAssign<NonNegativeRealScalar<RealType>>
1384 for NonNegativeRealScalar<RealType>
1385{
1386 fn mul_assign(&mut self, other: NonNegativeRealScalar<RealType>) {
1387 self.0 *= other.0;
1388 }
1389}
1390
1391/// Implements [`Zero`] for [`NonNegativeRealScalar`].
1392///
1393/// The zero value is always valid, and acts as the additive identity.
1394impl<RealType: RealScalar> Zero for NonNegativeRealScalar<RealType> {
1395 fn is_zero(&self) -> bool {
1396 self.0.is_zero()
1397 }
1398
1399 fn zero() -> Self {
1400 NonNegativeRealScalar(RealType::zero())
1401 }
1402}
1403
1404impl<RealType: RealScalar> One for NonNegativeRealScalar<RealType> {
1405 fn one() -> Self {
1406 NonNegativeRealScalar(RealType::one())
1407 }
1408}
1409
1410#[duplicate_item(
1411 exponent_type;
1412 [u8];
1413 [i8];
1414 [u16];
1415 [i16];
1416 [u32];
1417 [i32];
1418 [u64];
1419 [i64];
1420 [u128];
1421 [i128];
1422 [usize];
1423 [isize];
1424)]
1425impl<RealType: RealScalar> Pow<exponent_type> for NonNegativeRealScalar<RealType> {
1426 type Error = PowIntExponentErrors<<RealType as IntoInner>::InnerType, exponent_type>;
1427
1428 fn try_pow(self, exponent: exponent_type) -> Result<Self, Self::Error> {
1429 self.0.try_pow(exponent).map(NonNegativeRealScalar)
1430 }
1431
1432 #[inline(always)]
1433 /// Raises the number `self` to the power `exponent`.
1434 fn pow(self, exponent: exponent_type) -> Self {
1435 #[cfg(debug_assertions)]
1436 {
1437 self.try_pow(exponent).unwrap()
1438 }
1439 #[cfg(not(debug_assertions))]
1440 {
1441 NonNegativeRealScalar(self.0.pow(exponent))
1442 }
1443 }
1444}
1445
1446/// Implements [`Max`] for [`NonNegativeRealScalar`].
1447///
1448/// Maximum selection is delegated to the default trait behavior based on [`PartialOrd`].
1449impl<RealType: RealScalar> Max for NonNegativeRealScalar<RealType> {}
1450/// Implements [`Min`] for [`NonNegativeRealScalar`].
1451///
1452/// Minimum selection is delegated to the default trait behavior based on [`PartialOrd`].
1453impl<RealType: RealScalar> Min for NonNegativeRealScalar<RealType> {}
1454
1455/// Implements Neumaier compensated summation for [`NonNegativeRealScalar`].
1456///
1457/// This implementation delegates to the underlying real scalar type's Neumaier
1458/// compensated sum operation, preserving numerical stability for sums of non-negative values.
1459impl<RealType: RealScalar> NeumaierAddable for NonNegativeRealScalar<RealType> {
1460 #[inline(always)]
1461 fn neumaier_compensated_sum(value: Self, sum: &mut Self, compensation: &mut Self) {
1462 neumaier_sum_and_compensation_real(value.into_inner(), &mut sum.0, &mut compensation.0)
1463 }
1464}
1465
1466//------------------------------------------------------------------------------------------------------------
1467// Conversions
1468//------------------------------------------------------------------------------------------------------------
1469
1470/// Converts a [`PositiveRealScalar<T>`] to a [`NonNegativeRealScalar<T>`].
1471///
1472/// This conversion is mathematically valid because every positive number (x > 0) is
1473/// also non-negative (x ≥ 0). The conversion is infallible and zero-cost.
1474///
1475/// # Examples
1476///
1477/// ```rust
1478/// use num_valid::scalars::{PositiveRealScalar, NonNegativeRealScalar};
1479/// use try_create::TryNew;
1480///
1481/// let positive = PositiveRealScalar::try_new(5.0_f64).unwrap();
1482/// let non_negative: NonNegativeRealScalar<f64> = positive.into();
1483///
1484/// assert_eq!(*non_negative.as_ref(), 5.0);
1485/// ```
1486impl<RealType: RealScalar> From<PositiveRealScalar<RealType>> for NonNegativeRealScalar<RealType> {
1487 fn from(positive: PositiveRealScalar<RealType>) -> Self {
1488 NonNegativeRealScalar(positive.into_inner())
1489 }
1490}
1491
1492//------------------------------------------------------------------------------------------------------------
1493// Cross-type arithmetic operations
1494//------------------------------------------------------------------------------------------------------------
1495
1496/// Adds a [`NonNegativeRealScalar`] to a [`PositiveRealScalar`], yielding a [`PositiveRealScalar`].
1497///
1498/// Since `self` is strictly positive (x > 0) and `other` is non-negative (y ≥ 0),
1499/// their sum is strictly positive: x + y > 0.
1500///
1501/// # Examples
1502///
1503/// ```rust
1504/// use num_valid::scalars::{PositiveRealScalar, NonNegativeRealScalar};
1505/// use try_create::TryNew;
1506///
1507/// let pos = PositiveRealScalar::try_new(2.0_f64).unwrap();
1508/// let non_neg = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
1509///
1510/// let result: PositiveRealScalar<f64> = pos + non_neg;
1511/// assert_eq!(*result.as_ref(), 5.0);
1512/// ```
1513impl<RealType: RealScalar> Add<NonNegativeRealScalar<RealType>> for PositiveRealScalar<RealType> {
1514 type Output = PositiveRealScalar<RealType>;
1515
1516 fn add(self, other: NonNegativeRealScalar<RealType>) -> PositiveRealScalar<RealType> {
1517 PositiveRealScalar(self.0 + other.0)
1518 }
1519}
1520
1521/// Adds a [`NonNegativeRealScalar`] by reference to a [`PositiveRealScalar`], yielding a [`PositiveRealScalar`].
1522///
1523/// Since `self` is strictly positive (x > 0) and `other` is non-negative (y ≥ 0),
1524/// their sum is strictly positive: x + y > 0.
1525impl<'a, RealType: RealScalar> Add<&'a NonNegativeRealScalar<RealType>>
1526 for PositiveRealScalar<RealType>
1527{
1528 type Output = PositiveRealScalar<RealType>;
1529
1530 fn add(self, other: &'a NonNegativeRealScalar<RealType>) -> PositiveRealScalar<RealType> {
1531 PositiveRealScalar(self.0 + &other.0)
1532 }
1533}
1534
1535/// Adds a [`PositiveRealScalar`] to a [`NonNegativeRealScalar`], yielding a [`PositiveRealScalar`].
1536///
1537/// Since `other` is strictly positive (x > 0) and `self` is non-negative (y ≥ 0),
1538/// their sum is strictly positive: x + y > 0.
1539///
1540/// # Examples
1541///
1542/// ```rust
1543/// use num_valid::scalars::{PositiveRealScalar, NonNegativeRealScalar};
1544/// use try_create::TryNew;
1545///
1546/// let non_neg = NonNegativeRealScalar::try_new(0.0_f64).unwrap(); // zero!
1547/// let pos = PositiveRealScalar::try_new(5.0_f64).unwrap();
1548///
1549/// let result: PositiveRealScalar<f64> = non_neg + pos;
1550/// assert_eq!(*result.as_ref(), 5.0);
1551/// ```
1552impl<RealType: RealScalar> Add<PositiveRealScalar<RealType>> for NonNegativeRealScalar<RealType> {
1553 type Output = PositiveRealScalar<RealType>;
1554
1555 fn add(self, other: PositiveRealScalar<RealType>) -> PositiveRealScalar<RealType> {
1556 PositiveRealScalar(self.0 + other.0)
1557 }
1558}
1559
1560/// Adds a [`PositiveRealScalar`] by reference to a [`NonNegativeRealScalar`], yielding a [`PositiveRealScalar`].
1561///
1562/// Since `other` is strictly positive (x > 0) and `self` is non-negative (y ≥ 0),
1563/// their sum is strictly positive: x + y > 0.
1564impl<'a, RealType: RealScalar> Add<&'a PositiveRealScalar<RealType>>
1565 for NonNegativeRealScalar<RealType>
1566{
1567 type Output = PositiveRealScalar<RealType>;
1568
1569 fn add(self, other: &'a PositiveRealScalar<RealType>) -> PositiveRealScalar<RealType> {
1570 PositiveRealScalar(self.0 + &other.0)
1571 }
1572}
1573
1574//------------------------------------------------------------------------------------------------------------
1575// Tests
1576//------------------------------------------------------------------------------------------------------------
1577
1578#[cfg(test)]
1579mod tests {
1580 use super::*;
1581 use crate::backends::native::validated::RealNative64StrictFinite;
1582
1583 mod absolute_tolerance {
1584 use super::*;
1585
1586 #[test]
1587 fn try_new_valid() {
1588 let tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1e-10_f64).unwrap());
1589 assert_eq!(*tol.as_ref().as_ref(), 1e-10);
1590 }
1591
1592 #[test]
1593 fn try_new_zero() {
1594 let tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.0_f64).unwrap());
1595 assert_eq!(*tol.as_ref().as_ref(), 0.0);
1596 }
1597
1598 #[test]
1599 fn zero_constructor() {
1600 let tol = AbsoluteTolerance::<f64>::zero();
1601 assert_eq!(*tol.as_ref().as_ref(), 0.0);
1602 }
1603
1604 #[test]
1605 fn epsilon_constructor() {
1606 let tol = AbsoluteTolerance::<f64>::epsilon();
1607 assert_eq!(*tol.as_ref().as_ref(), f64::EPSILON);
1608 }
1609
1610 #[test]
1611 fn display_trait() {
1612 let tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5_f64).unwrap());
1613 assert_eq!(format!("{}", tol), "0.5");
1614 }
1615
1616 #[test]
1617 fn lower_exp_trait() {
1618 let tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.00001_f64).unwrap());
1619 assert_eq!(format!("{:e}", tol.into_inner().into_inner()), "1e-5");
1620 }
1621
1622 #[test]
1623 fn into_inner() {
1624 let tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5_f64).unwrap());
1625 let inner = tol.into_inner().into_inner();
1626 assert_eq!(inner, 0.5);
1627 }
1628
1629 #[test]
1630 fn clone_and_partial_eq() {
1631 let tol1 = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1e-10_f64).unwrap());
1632 let tol2 = tol1.clone();
1633 assert_eq!(tol1, tol2);
1634 }
1635
1636 #[test]
1637 fn partial_ord() {
1638 let tol1 = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1e-10_f64).unwrap());
1639 let tol2 = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1e-9_f64).unwrap());
1640 assert!(tol1 < tol2);
1641 }
1642
1643 #[test]
1644 fn serialize_deserialize() {
1645 let tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5_f64).unwrap());
1646 let serialized = serde_json::to_string(&tol).unwrap();
1647 let deserialized: AbsoluteTolerance<f64> = serde_json::from_str(&serialized).unwrap();
1648 assert_eq!(tol, deserialized);
1649 }
1650
1651 #[test]
1652 fn with_validated_type() {
1653 let val = RealNative64StrictFinite::try_from_f64(1e-10).unwrap();
1654 let tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(val).unwrap());
1655 assert_eq!(tol.as_ref().as_ref().as_ref(), &1e-10);
1656 }
1657
1658 #[test]
1659 #[cfg(debug_assertions)]
1660 #[should_panic(expected = "is not finite")]
1661 fn debug_panics_on_nan() {
1662 let _ = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(f64::NAN).unwrap());
1663 }
1664
1665 #[test]
1666 #[cfg(debug_assertions)]
1667 #[should_panic(expected = "is not finite")]
1668 fn debug_panics_on_infinity() {
1669 let _ = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(f64::INFINITY).unwrap());
1670 }
1671
1672 #[test]
1673 fn try_new() {
1674 // Test creating an AbsoluteTolerance instance with a value of 0.0
1675 let v = AbsoluteTolerance::<f64>::zero();
1676 assert_eq!(*v.as_ref().as_ref(), 0.0);
1677
1678 // Test creating an AbsoluteTolerance instance with a positive value
1679 let v = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(2.0).unwrap());
1680 assert_eq!(*v.as_ref().as_ref(), 2.0);
1681 }
1682
1683 #[test]
1684 #[cfg(debug_assertions)]
1685 #[should_panic = "The input value inf is not finite!"]
1686 fn try_new_invalid_infinite() {
1687 // Test creating an invalid AbsoluteTolerance instance with an infinite value
1688 let _tol =
1689 AbsoluteTolerance::new(NonNegativeRealScalar::try_new(f64::INFINITY).unwrap());
1690 }
1691
1692 #[test]
1693 #[cfg(debug_assertions)]
1694 #[should_panic = "The input value NaN is not finite!"]
1695 fn try_new_invalid_nan() {
1696 // Test creating an invalid AbsoluteTolerance instance with a NaN value
1697 let _tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(f64::NAN).unwrap());
1698 }
1699
1700 #[test]
1701 fn epsilon() {
1702 // Test creating an AbsoluteTolerance instance with epsilon value
1703 let epsilon_tol = AbsoluteTolerance::<f64>::epsilon();
1704 assert_eq!(*epsilon_tol.as_ref().as_ref(), f64::EPSILON);
1705 }
1706
1707 #[test]
1708 fn equality_operator() {
1709 // Test equality operator for AbsoluteTolerance instances
1710 let a = AbsoluteTolerance::<f64>::zero();
1711 let b = AbsoluteTolerance::<f64>::zero();
1712 assert!(a == b);
1713
1714 let c = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1.0).unwrap());
1715 let d = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1.0).unwrap());
1716 assert!(c == d);
1717 }
1718
1719 #[test]
1720 fn inequality_operator() {
1721 // Test inequality operator for AbsoluteTolerance instances
1722 let a = AbsoluteTolerance::<f64>::zero();
1723 let b = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1.0).unwrap());
1724 assert!(a != b);
1725
1726 let c = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1.0).unwrap());
1727 let d = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(2.0).unwrap());
1728 assert!(c != d);
1729 }
1730
1731 #[test]
1732 fn debug_trait() {
1733 // Test the Debug trait
1734 let tolerance = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5).unwrap());
1735 assert_eq!(
1736 format!("{:?}", tolerance),
1737 "AbsoluteTolerance(NonNegativeRealScalar(0.5))"
1738 );
1739 }
1740
1741 #[test]
1742 fn clone_trait() {
1743 // Test the Clone trait
1744 let tolerance = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5).unwrap());
1745 let cloned_tolerance = tolerance.clone();
1746 assert_eq!(tolerance, cloned_tolerance);
1747 }
1748
1749 #[test]
1750 fn partial_eq_trait() {
1751 // Test the PartialEq trait
1752 let tolerance1 = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5).unwrap());
1753 let tolerance2 = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5).unwrap());
1754 assert_eq!(tolerance1, tolerance2);
1755 }
1756
1757 #[test]
1758 fn partial_ord_trait() {
1759 // Test the PartialOrd trait
1760 let tolerance1 = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5).unwrap());
1761 let tolerance2 = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1.0).unwrap());
1762 assert!(tolerance1 < tolerance2);
1763 }
1764
1765 #[test]
1766 fn as_ref_trait() {
1767 // Test the AsRef trait
1768 let tolerance = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(0.5).unwrap());
1769 let inner: &f64 = tolerance.as_ref().as_ref();
1770 assert_eq!(*inner, 0.5);
1771 }
1772
1773 #[test]
1774 fn zero() {
1775 // Test the zero constructor
1776 let zero_tol = AbsoluteTolerance::<f64>::zero();
1777 assert_eq!(*zero_tol.as_ref().as_ref(), 0.0);
1778 }
1779
1780 #[test]
1781 fn edge_cases() {
1782 // Test with very small positive values
1783 let tiny_tol =
1784 AbsoluteTolerance::new(NonNegativeRealScalar::try_new(f64::MIN_POSITIVE).unwrap());
1785 assert_eq!(*tiny_tol.as_ref().as_ref(), f64::MIN_POSITIVE);
1786
1787 // Test with large positive values
1788 let large_tol = AbsoluteTolerance::new(NonNegativeRealScalar::try_new(1e100).unwrap());
1789 assert_eq!(*large_tol.as_ref().as_ref(), 1e100);
1790 }
1791
1792 #[test]
1793 fn approximately_equal_exact_match() {
1794 let a = 1.0_f64;
1795 let b = 1.0_f64;
1796 let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
1797 assert!(approximately_equal(a, b, &tol));
1798 }
1799
1800 #[test]
1801 fn approximately_equal_within_tolerance() {
1802 let a = 1.0_f64;
1803 let b = 1.0 + 1e-11_f64;
1804 let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
1805 assert!(approximately_equal(a, b, &tol));
1806 }
1807
1808 #[test]
1809 fn approximately_equal_outside_tolerance() {
1810 let a = 1.0_f64;
1811 let b = 1.0 + 1e-9_f64;
1812 let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
1813 assert!(!approximately_equal(a, b, &tol));
1814 }
1815
1816 #[test]
1817 fn approximately_equal_negative_values() {
1818 let a = -5.0_f64;
1819 let b = -5.0 + 1e-12_f64;
1820 let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
1821 assert!(approximately_equal(a, b, &tol));
1822 }
1823
1824 #[test]
1825 fn approximately_equal_zero_tolerance() {
1826 let a = 1.0_f64;
1827 let b = 1.0_f64;
1828 let tol = AbsoluteTolerance::<f64>::zero();
1829 assert!(approximately_equal(a, b, &tol));
1830
1831 let c = 1.0_f64 + 1e-15;
1832 assert!(!approximately_equal(a, c, &tol)); // Even tiny difference fails with zero tolerance
1833 }
1834
1835 #[test]
1836 fn approximately_equal_large_difference_outside_tolerance() {
1837 let a = 1.0_f64;
1838 let b = 2.0_f64;
1839 let tol = AbsoluteTolerance::try_new(0.1_f64).unwrap();
1840 assert!(!approximately_equal(a, b, &tol));
1841 }
1842
1843 #[test]
1844 fn approximately_equal_symmetric() {
1845 let a = 1.0_f64;
1846 let b = 1.0 + 1e-12_f64;
1847 let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
1848 assert_eq!(
1849 approximately_equal(a, b, &tol),
1850 approximately_equal(b, a, &tol)
1851 );
1852 }
1853
1854 #[test]
1855 fn approximately_equal_with_validated_type() {
1856 let a = RealNative64StrictFinite::try_from_f64(1.0).unwrap();
1857 let b = RealNative64StrictFinite::try_from_f64(1.0 + 1e-11).unwrap();
1858 let tol = AbsoluteTolerance::new(
1859 NonNegativeRealScalar::try_new(
1860 RealNative64StrictFinite::try_from_f64(1e-10).unwrap(),
1861 )
1862 .unwrap(),
1863 );
1864 assert!(approximately_equal(a, b, &tol));
1865 }
1866 }
1867
1868 mod relative_tolerance {
1869 use super::*;
1870
1871 #[test]
1872 fn try_new_valid() {
1873 let tol = RelativeTolerance::try_new(0.01_f64).unwrap();
1874 assert_eq!(*tol.as_ref().as_ref(), 0.01);
1875 }
1876
1877 #[test]
1878 fn try_new_zero() {
1879 let tol = RelativeTolerance::try_new(0.0_f64).unwrap();
1880 assert_eq!(*tol.as_ref().as_ref(), 0.0);
1881 }
1882
1883 #[test]
1884 fn try_new_negative() {
1885 let result = RelativeTolerance::try_new(-0.01_f64);
1886 assert!(matches!(
1887 result,
1888 Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
1889 ));
1890 }
1891
1892 #[test]
1893 fn zero_constructor() {
1894 let tol = RelativeTolerance::<f64>::zero();
1895 assert_eq!(*tol.as_ref().as_ref(), 0.0);
1896 }
1897
1898 #[test]
1899 fn epsilon_constructor() {
1900 let tol = RelativeTolerance::<f64>::epsilon();
1901 assert_eq!(*tol.as_ref().as_ref(), f64::EPSILON);
1902 }
1903
1904 #[test]
1905 fn absolute_tolerance_positive_reference() {
1906 let rel_tol = RelativeTolerance::try_new(0.1_f64).unwrap();
1907 let abs_tol = rel_tol.absolute_tolerance(100.0);
1908 assert_eq!(*abs_tol.as_ref().as_ref(), 10.0);
1909 }
1910
1911 #[test]
1912 fn absolute_tolerance_negative_reference() {
1913 let rel_tol = RelativeTolerance::try_new(0.1_f64).unwrap();
1914 let abs_tol = rel_tol.absolute_tolerance(-50.0);
1915 assert_eq!(*abs_tol.as_ref().as_ref(), 5.0);
1916 }
1917
1918 #[test]
1919 fn absolute_tolerance_zero_reference() {
1920 let rel_tol = RelativeTolerance::try_new(0.1_f64).unwrap();
1921 let abs_tol = rel_tol.absolute_tolerance(0.0);
1922 assert_eq!(*abs_tol.as_ref().as_ref(), 0.0);
1923 }
1924
1925 #[test]
1926 fn display_trait() {
1927 let tol = RelativeTolerance::try_new(0.5_f64).unwrap();
1928 assert_eq!(format!("{}", tol), "0.5");
1929 }
1930
1931 #[test]
1932 fn into_inner() {
1933 let tol = RelativeTolerance::try_new(0.01_f64).unwrap();
1934 let inner = tol.into_inner();
1935 assert_eq!(inner.as_ref(), &0.01);
1936 }
1937
1938 #[test]
1939 fn serialize_deserialize() {
1940 let tol = RelativeTolerance::try_new(0.01_f64).unwrap();
1941 let serialized = serde_json::to_string(&tol).unwrap();
1942 let deserialized: RelativeTolerance<f64> = serde_json::from_str(&serialized).unwrap();
1943 assert_eq!(tol, deserialized);
1944 }
1945
1946 #[test]
1947 fn try_new() {
1948 // Test creating a RelativeTolerance instance with a value of 0.0
1949 let v = RelativeTolerance::<f64>::zero();
1950 assert_eq!(*v.as_ref().as_ref(), 0.0);
1951
1952 // Test creating a RelativeTolerance instance with a positive value
1953 let v = RelativeTolerance::try_new(2.0).unwrap();
1954 assert_eq!(*v.as_ref().as_ref(), 2.0);
1955 }
1956
1957 #[test]
1958 fn try_new_invalid_negative() {
1959 // Test creating an invalid RelativeTolerance instance with a negative value
1960 let tol = RelativeTolerance::try_new(-0.1);
1961 assert!(matches!(
1962 tol,
1963 Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
1964 ));
1965 }
1966
1967 #[test]
1968 #[cfg(debug_assertions)]
1969 #[should_panic = "The input value inf is not finite!"]
1970 fn try_new_invalid_infinite() {
1971 // Test creating an invalid RelativeTolerance instance with an infinite value
1972 let _tol = RelativeTolerance::try_new(f64::INFINITY);
1973 }
1974
1975 #[test]
1976 #[cfg(debug_assertions)]
1977 #[should_panic = "The input value NaN is not finite!"]
1978 fn try_new_invalid_nan() {
1979 // Test creating an invalid RelativeTolerance instance with a NaN value
1980 let _tol = RelativeTolerance::try_new(f64::NAN);
1981 }
1982
1983 #[test]
1984 fn epsilon() {
1985 // Test creating a RelativeTolerance instance with epsilon value
1986 let epsilon_tol = RelativeTolerance::<f64>::epsilon();
1987 assert_eq!(*epsilon_tol.as_ref().as_ref(), f64::EPSILON);
1988 }
1989
1990 #[test]
1991 fn absolute_tolerance() {
1992 // Test converting relative tolerance to absolute tolerance
1993 let rel_tol = RelativeTolerance::try_new(0.1).unwrap();
1994 let reference = 10.0;
1995 let abs_tol = rel_tol.absolute_tolerance(reference);
1996 assert_eq!(*abs_tol.as_ref().as_ref(), 1.0); // 0.1 * |10.0| = 1.0
1997
1998 // Test with negative reference value
1999 let reference = -5.0;
2000 let abs_tol = rel_tol.absolute_tolerance(reference);
2001 assert_eq!(*abs_tol.as_ref().as_ref(), 0.5); // 0.1 * |-5.0| = 0.5
2002
2003 // Test with zero reference value
2004 let reference = 0.0;
2005 let abs_tol = rel_tol.absolute_tolerance(reference);
2006 assert_eq!(*abs_tol.as_ref().as_ref(), 0.0); // 0.1 * |0.0| = 0.0
2007 }
2008
2009 #[test]
2010 fn equality_operator() {
2011 // Test equality operator for RelativeTolerance instances
2012 let a = RelativeTolerance::<f64>::zero();
2013 let b = RelativeTolerance::<f64>::zero();
2014 assert!(a == b);
2015
2016 let c = RelativeTolerance::try_new(1.0).unwrap();
2017 let d = RelativeTolerance::try_new(1.0).unwrap();
2018 assert!(c == d);
2019 }
2020
2021 #[test]
2022 fn inequality_operator() {
2023 // Test inequality operator for RelativeTolerance instances
2024 let a = RelativeTolerance::<f64>::zero();
2025 let b = RelativeTolerance::try_new(1.0).unwrap();
2026 assert!(a != b);
2027
2028 let c = RelativeTolerance::try_new(1.0).unwrap();
2029 let d = RelativeTolerance::try_new(2.0).unwrap();
2030 assert!(c != d);
2031 }
2032
2033 #[test]
2034 fn debug_trait() {
2035 // Test the Debug trait
2036 let tolerance = RelativeTolerance::try_new(0.5).unwrap();
2037 assert_eq!(
2038 format!("{:?}", tolerance),
2039 "RelativeTolerance(NonNegativeRealScalar(0.5))"
2040 );
2041 }
2042
2043 #[test]
2044 fn clone_trait() {
2045 // Test the Clone trait
2046 let tolerance = RelativeTolerance::try_new(0.5).unwrap();
2047 let cloned_tolerance = tolerance.clone();
2048 assert_eq!(tolerance, cloned_tolerance);
2049 }
2050
2051 #[test]
2052 fn partial_eq_trait() {
2053 // Test the PartialEq trait
2054 let tolerance1 = RelativeTolerance::try_new(0.5).unwrap();
2055 let tolerance2 = RelativeTolerance::try_new(0.5).unwrap();
2056 assert_eq!(tolerance1, tolerance2);
2057 }
2058
2059 #[test]
2060 fn partial_ord_trait() {
2061 // Test the PartialOrd trait
2062 let tolerance1 = RelativeTolerance::try_new(0.5).unwrap();
2063 let tolerance2 = RelativeTolerance::try_new(1.0).unwrap();
2064 assert!(tolerance1 < tolerance2);
2065 }
2066
2067 #[test]
2068 fn as_ref_trait() {
2069 // Test the AsRef trait
2070 let tolerance = RelativeTolerance::try_new(0.5).unwrap();
2071 let inner: &f64 = tolerance.as_ref().as_ref();
2072 assert_eq!(*inner, 0.5);
2073 }
2074
2075 #[test]
2076 fn lower_exp_trait() {
2077 // Test the LowerExp trait (scientific notation formatting)
2078 let tolerance = RelativeTolerance::try_new(0.00001).unwrap();
2079 assert_eq!(format!("{:e}", tolerance.as_ref().as_ref()), "1e-5");
2080 }
2081 }
2082
2083 mod positive_real_scalar {
2084 use super::*;
2085 use crate::backends::native::validated::RealNative64StrictFiniteInDebug;
2086
2087 #[test]
2088 fn try_new_positive() {
2089 let val = PositiveRealScalar::try_new(2.5_f64).unwrap();
2090 assert_eq!(*val.as_ref(), 2.5);
2091 }
2092
2093 #[test]
2094 fn try_new_min_positive() {
2095 let val = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
2096 assert_eq!(*val.as_ref(), f64::MIN_POSITIVE);
2097 }
2098
2099 #[test]
2100 fn try_new_zero_fails() {
2101 let result = PositiveRealScalar::try_new(0.0_f64);
2102 assert!(matches!(
2103 result,
2104 Err(ErrorsPositiveRealScalar::ZeroValue { .. })
2105 ));
2106 }
2107
2108 #[test]
2109 fn try_new_negative_fails() {
2110 let result = PositiveRealScalar::try_new(-1.0_f64);
2111 assert!(
2112 matches!(result, Err(ErrorsPositiveRealScalar::NegativeValue { value, .. }) if value == -1.0)
2113 );
2114 }
2115
2116 #[test]
2117 fn display_trait() {
2118 let val = PositiveRealScalar::try_new(1.618_f64).unwrap();
2119 assert_eq!(format!("{}", val), "1.618");
2120 }
2121
2122 #[test]
2123 fn into_inner() {
2124 let val = PositiveRealScalar::try_new(42.0_f64).unwrap();
2125 let inner = val.into_inner();
2126 assert_eq!(inner, 42.0);
2127 }
2128
2129 #[test]
2130 fn partial_ord() {
2131 let a = PositiveRealScalar::try_new(1.0_f64).unwrap();
2132 let b = PositiveRealScalar::try_new(2.0_f64).unwrap();
2133 assert!(a < b);
2134 }
2135
2136 #[test]
2137 fn serialize_deserialize() {
2138 let val = PositiveRealScalar::try_new(3.0_f64).unwrap();
2139 let serialized = serde_json::to_string(&val).unwrap();
2140 let deserialized: PositiveRealScalar<f64> = serde_json::from_str(&serialized).unwrap();
2141 assert_eq!(val, deserialized);
2142 }
2143
2144 #[test]
2145 fn with_validated_type() {
2146 let inner = RealNative64StrictFinite::try_from_f64(5.0).unwrap();
2147 let val = PositiveRealScalar::try_new(inner).unwrap();
2148 assert_eq!(*val.as_ref().as_ref(), 5.0);
2149 }
2150
2151 #[test]
2152 #[cfg(debug_assertions)]
2153 #[should_panic(expected = "is not finite")]
2154 fn debug_panics_on_nan() {
2155 let _ = PositiveRealScalar::try_new(f64::NAN);
2156 }
2157
2158 #[test]
2159 fn try_new() {
2160 // Test creating a PositiveRealScalar instance with a positive value
2161 let v = PositiveRealScalar::try_new(2.5).unwrap();
2162 assert_eq!(*v.as_ref(), 2.5);
2163
2164 // Test creating a PositiveRealScalar instance with a very small positive value
2165 let v = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
2166 assert_eq!(*v.as_ref(), f64::MIN_POSITIVE);
2167
2168 // Test creating a PositiveRealScalar instance with a large positive value
2169 let v = PositiveRealScalar::try_new(1e100).unwrap();
2170 assert_eq!(*v.as_ref(), 1e100);
2171 }
2172
2173 #[test]
2174 fn try_new_invalid_negative() {
2175 // Test creating an invalid PositiveRealScalar instance with a negative value
2176 let scalar = PositiveRealScalar::try_new(-0.1);
2177 assert!(matches!(
2178 scalar,
2179 Err(ErrorsPositiveRealScalar::NegativeValue { .. })
2180 ));
2181
2182 // Test with a negative value close to zero
2183 let scalar = PositiveRealScalar::try_new(-f64::EPSILON);
2184 assert!(matches!(
2185 scalar,
2186 Err(ErrorsPositiveRealScalar::NegativeValue { .. })
2187 ));
2188 }
2189
2190 #[test]
2191 fn try_new_invalid_zero() {
2192 // Test creating an invalid PositiveRealScalar instance with zero
2193 let scalar = PositiveRealScalar::try_new(0.0);
2194 assert!(matches!(
2195 scalar,
2196 Err(ErrorsPositiveRealScalar::ZeroValue { .. })
2197 ));
2198 }
2199
2200 #[test]
2201 #[cfg(debug_assertions)]
2202 #[should_panic = "The input value inf is not finite!"]
2203 fn try_new_invalid_infinite() {
2204 // Test creating an invalid PositiveRealScalar instance with an infinite value
2205 let _scalar = PositiveRealScalar::try_new(f64::INFINITY);
2206 }
2207
2208 #[test]
2209 #[cfg(debug_assertions)]
2210 #[should_panic = "The input value -inf is not finite!"]
2211 fn try_new_invalid_negative_infinite() {
2212 // Test creating an invalid PositiveRealScalar instance with negative infinity
2213 let _scalar = PositiveRealScalar::try_new(f64::NEG_INFINITY);
2214 }
2215
2216 #[test]
2217 #[cfg(debug_assertions)]
2218 #[should_panic = "The input value NaN is not finite!"]
2219 fn try_new_invalid_nan() {
2220 // Test creating an invalid PositiveRealScalar instance with a NaN value
2221 let _scalar = PositiveRealScalar::try_new(f64::NAN);
2222 }
2223
2224 #[test]
2225 fn equality_operator() {
2226 // Test equality operator for PositiveRealScalar instances
2227 let a = PositiveRealScalar::try_new(1.5).unwrap();
2228 let b = PositiveRealScalar::try_new(1.5).unwrap();
2229 assert!(a == b);
2230
2231 let c = PositiveRealScalar::try_new(3.1).unwrap();
2232 let d = PositiveRealScalar::try_new(3.1).unwrap();
2233 assert!(c == d);
2234 }
2235
2236 #[test]
2237 fn inequality_operator() {
2238 // Test inequality operator for PositiveRealScalar instances
2239 let a = PositiveRealScalar::try_new(1.0).unwrap();
2240 let b = PositiveRealScalar::try_new(2.0).unwrap();
2241 assert!(a != b);
2242
2243 let c = PositiveRealScalar::try_new(0.5).unwrap();
2244 let d = PositiveRealScalar::try_new(1.5).unwrap();
2245 assert!(c != d);
2246 }
2247
2248 #[test]
2249 fn debug_trait() {
2250 // Test the Debug trait
2251 let scalar = PositiveRealScalar::try_new(2.7).unwrap();
2252 assert_eq!(format!("{:?}", scalar), "PositiveRealScalar(2.7)");
2253 }
2254
2255 #[test]
2256 fn clone_trait() {
2257 // Test the Clone trait
2258 let scalar = PositiveRealScalar::try_new(1.414).unwrap();
2259 let cloned_scalar = scalar.clone();
2260 assert_eq!(scalar, cloned_scalar);
2261 }
2262
2263 #[test]
2264 fn partial_eq_trait() {
2265 // Test the PartialEq trait
2266 let scalar1 = PositiveRealScalar::try_new(0.577).unwrap();
2267 let scalar2 = PositiveRealScalar::try_new(0.577).unwrap();
2268 assert_eq!(scalar1, scalar2);
2269 }
2270
2271 #[test]
2272 fn partial_ord_trait() {
2273 // Test the PartialOrd trait
2274 let scalar1 = PositiveRealScalar::try_new(1.0).unwrap();
2275 let scalar2 = PositiveRealScalar::try_new(2.0).unwrap();
2276 assert!(scalar1 < scalar2);
2277
2278 let scalar3 = PositiveRealScalar::try_new(3.0).unwrap();
2279 let scalar4 = PositiveRealScalar::try_new(3.0).unwrap();
2280 assert!(scalar3 <= scalar4);
2281 assert!(scalar4 >= scalar3);
2282 }
2283
2284 #[test]
2285 fn as_ref_trait() {
2286 // Test the AsRef trait
2287 let scalar = PositiveRealScalar::try_new(2.0).unwrap();
2288 let inner: &f64 = scalar.as_ref();
2289 assert_eq!(*inner, 2.0);
2290 }
2291
2292 #[test]
2293 fn edge_cases() {
2294 // Test with very small positive values
2295 let tiny_scalar = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
2296 assert_eq!(*tiny_scalar.as_ref(), f64::MIN_POSITIVE);
2297
2298 // Test with large positive values
2299 let large_scalar = PositiveRealScalar::try_new(f64::MAX).unwrap();
2300 assert_eq!(*large_scalar.as_ref(), f64::MAX);
2301
2302 // Test with epsilon
2303 let epsilon_scalar = PositiveRealScalar::try_new(f64::EPSILON).unwrap();
2304 assert_eq!(*epsilon_scalar.as_ref(), f64::EPSILON);
2305 }
2306
2307 #[test]
2308 fn generic_scalar_types() {
2309 // Test with RealNative64StrictFiniteInDebug type
2310 let scalar =
2311 PositiveRealScalar::try_new(RealNative64StrictFiniteInDebug::try_new(5.0).unwrap())
2312 .unwrap();
2313 assert_eq!(
2314 *scalar.as_ref(),
2315 RealNative64StrictFiniteInDebug::try_new(5.0).unwrap()
2316 );
2317
2318 // Test error case with generic type
2319 let zero_value = RealNative64StrictFiniteInDebug::try_new(0.0).unwrap();
2320 let scalar_result = PositiveRealScalar::try_new(zero_value);
2321 assert!(matches!(
2322 scalar_result,
2323 Err(ErrorsPositiveRealScalar::ZeroValue { .. })
2324 ));
2325 }
2326
2327 #[test]
2328 fn mathematical_operations() {
2329 // Test that the wrapped values behave correctly in mathematical contexts
2330 let scalar1 = PositiveRealScalar::try_new(2.0).unwrap();
2331 let scalar2 = PositiveRealScalar::try_new(3.0).unwrap();
2332
2333 // Test comparison operations
2334 assert!(scalar1 < scalar2);
2335 assert!(scalar2 > scalar1);
2336 assert_eq!(
2337 scalar1.partial_cmp(&scalar2),
2338 Some(std::cmp::Ordering::Less)
2339 );
2340 }
2341
2342 #[test]
2343 fn error_messages() {
2344 // Test that error messages are descriptive
2345 let negative_result = PositiveRealScalar::try_new(-1.0);
2346 match negative_result {
2347 Err(ErrorsPositiveRealScalar::NegativeValue { value, .. }) => {
2348 assert_eq!(value, -1.0);
2349 }
2350 _ => panic!("Expected NegativeValue error"),
2351 }
2352
2353 let zero_result = PositiveRealScalar::try_new(0.0);
2354 match zero_result {
2355 Err(ErrorsPositiveRealScalar::ZeroValue { .. }) => {
2356 // Expected
2357 }
2358 _ => panic!("Expected ZeroValue error"),
2359 }
2360 }
2361
2362 #[test]
2363 fn sqrt_preserves_positivity() {
2364 // Test that sqrt of a positive value is also positive
2365 let base = PositiveRealScalar::try_new(4.0_f64).unwrap();
2366 let result = base.sqrt();
2367 assert_eq!(*result.as_ref(), 2.0);
2368
2369 // Test with other values
2370 let base = PositiveRealScalar::try_new(9.0_f64).unwrap();
2371 let result = base.sqrt();
2372 assert_eq!(*result.as_ref(), 3.0);
2373 }
2374
2375 #[test]
2376 fn sqrt_with_small_base() {
2377 // Test sqrt with very small positive value
2378 let base = PositiveRealScalar::try_new(0.01_f64).unwrap();
2379 let result = base.sqrt();
2380 assert_eq!(*result.as_ref(), 0.1);
2381 }
2382
2383 #[test]
2384 fn sqrt_with_generic_types() {
2385 // Test sqrt with validated types
2386 let base_val = RealNative64StrictFiniteInDebug::try_new(4.0).unwrap();
2387 let base = PositiveRealScalar::try_new(base_val).unwrap();
2388
2389 let result = base.sqrt();
2390 assert_eq!(*result.as_ref().as_ref(), 2.0);
2391 }
2392
2393 #[test]
2394 fn new_constructor_debug_validates() {
2395 // Test the new() constructor (validates in debug, skips in release)
2396 // In debug mode, new() should behave like try_new().unwrap()
2397 #[cfg(debug_assertions)]
2398 {
2399 let val = PositiveRealScalar::new(2.5_f64);
2400 assert_eq!(*val.as_ref(), 2.5);
2401 }
2402
2403 #[cfg(not(debug_assertions))]
2404 {
2405 // In release mode, new() trusts the input
2406 let val = PositiveRealScalar::new(2.5_f64);
2407 assert_eq!(*val.as_ref(), 2.5);
2408 }
2409 }
2410
2411 #[test]
2412 #[cfg(debug_assertions)]
2413 #[should_panic(expected = "Error in the creation")]
2414 fn new_constructor_panics_on_zero_debug() {
2415 // In debug mode, new() should panic on zero
2416 let _ = PositiveRealScalar::new(0.0_f64);
2417 }
2418
2419 #[test]
2420 #[cfg(debug_assertions)]
2421 #[should_panic(expected = "Error in the creation")]
2422 fn new_constructor_panics_on_negative_debug() {
2423 // In debug mode, new() should panic on negative
2424 let _ = PositiveRealScalar::new(-1.0_f64);
2425 }
2426
2427 #[test]
2428 fn one_trait_identity() {
2429 // Test the One trait: one() should return PositiveRealScalar(1.0)
2430 let one = PositiveRealScalar::<f64>::one();
2431 assert_eq!(*one.as_ref(), 1.0);
2432 }
2433
2434 #[test]
2435 fn one_trait_multiplicative_identity() {
2436 // Test that one acts as multiplicative identity
2437 let value = PositiveRealScalar::try_new(5.5_f64).unwrap();
2438 let one = PositiveRealScalar::<f64>::one();
2439
2440 let result = value.clone() * one.clone();
2441 assert_eq!(*result.as_ref(), 5.5);
2442
2443 let result = one * value;
2444 assert_eq!(*result.as_ref(), 5.5);
2445 }
2446
2447 #[test]
2448 fn one_trait_with_generic_types() {
2449 // Test One trait with validated types
2450 let one = PositiveRealScalar::<RealNative64StrictFiniteInDebug>::one();
2451 assert_eq!(*one.as_ref().as_ref(), 1.0);
2452 }
2453
2454 #[test]
2455 fn pow_integer_exponents() {
2456 // Test Pow trait with different integer exponent types
2457 let base = PositiveRealScalar::try_new(2.0_f64).unwrap();
2458
2459 // Test with u32
2460 let result = base.clone().pow(3u32);
2461 assert_eq!(*result.as_ref(), 8.0);
2462
2463 // Test with u8
2464 let result = base.clone().pow(2u8);
2465 assert_eq!(*result.as_ref(), 4.0);
2466
2467 // Test with i32 (positive)
2468 let result = base.clone().pow(2i32);
2469 assert_eq!(*result.as_ref(), 4.0);
2470 }
2471
2472 #[test]
2473 fn pow_zero_exponent() {
2474 // Test that any number raised to power 0 equals 1
2475 let base = PositiveRealScalar::try_new(5.5_f64).unwrap();
2476
2477 let result = base.pow(0u32);
2478 assert_eq!(*result.as_ref(), 1.0);
2479 }
2480
2481 #[test]
2482 fn pow_one_exponent() {
2483 // Test that any number raised to power 1 equals itself
2484 let base = PositiveRealScalar::try_new(3.7_f64).unwrap();
2485
2486 let result = base.clone().pow(1u32);
2487 assert_eq!(*result.as_ref(), 3.7);
2488 }
2489
2490 #[test]
2491 fn pow_preserves_positivity() {
2492 // Test that raising positive to any integer power preserves positivity
2493 let base = PositiveRealScalar::try_new(2.5_f64).unwrap();
2494
2495 let result = base.clone().pow(5u32);
2496 assert!(*result.as_ref() > 0.0);
2497 assert_eq!(*result.as_ref(), 2.5_f64.powi(5));
2498 }
2499
2500 #[test]
2501 fn pow_with_try_pow() {
2502 // Test the try_pow method
2503 let base = PositiveRealScalar::try_new(3.0_f64).unwrap();
2504
2505 let result = base.clone().try_pow(2u32);
2506 assert!(result.is_ok());
2507 assert_eq!(*result.unwrap().as_ref(), 9.0);
2508
2509 // Test with different exponent types
2510 let result = base.try_pow(3i32);
2511 assert!(result.is_ok());
2512 assert_eq!(*result.unwrap().as_ref(), 27.0);
2513 }
2514
2515 #[test]
2516 fn pow_large_exponents() {
2517 // Test with large exponents
2518 let base = PositiveRealScalar::try_new(1.1_f64).unwrap();
2519 let result = base.pow(10u32);
2520 let expected = 1.1_f64.powi(10);
2521 assert!((result.as_ref() - expected).abs() < 1e-10);
2522 }
2523
2524 #[test]
2525 fn pow_small_base() {
2526 // Test with small base values
2527 let base = PositiveRealScalar::try_new(0.5_f64).unwrap();
2528
2529 let result = base.clone().pow(2u32);
2530 assert_eq!(*result.as_ref(), 0.25);
2531
2532 let result = base.pow(3u32);
2533 assert_eq!(*result.as_ref(), 0.125);
2534 }
2535
2536 #[test]
2537 fn pow_with_generic_types() {
2538 // Test Pow with validated types
2539 let base_val = RealNative64StrictFiniteInDebug::try_new(2.0).unwrap();
2540 let base = PositiveRealScalar::try_new(base_val).unwrap();
2541
2542 let result = base.pow(3u32);
2543 assert_eq!(*result.as_ref().as_ref(), 8.0);
2544 }
2545
2546 #[test]
2547 fn pow_chain_operations() {
2548 // Test chaining pow operations
2549 let base = PositiveRealScalar::try_new(2.0_f64).unwrap();
2550
2551 // (2^3)^2 = 2^6 = 64
2552 let result = base.pow(3u32).pow(2u32);
2553 assert_eq!(*result.as_ref(), 64.0);
2554 }
2555
2556 #[test]
2557 fn add_trait_value_semantics() {
2558 // Test Add trait for by-value operands
2559 let a = PositiveRealScalar::try_new(2.0_f64).unwrap();
2560 let b = PositiveRealScalar::try_new(3.0_f64).unwrap();
2561
2562 let sum = a + b;
2563 assert_eq!(*sum.as_ref(), 5.0);
2564 assert!(*sum.as_ref() > 0.0);
2565 }
2566
2567 #[test]
2568 fn add_trait_reference_semantics() {
2569 // Test Add trait with reference operand
2570 let a = PositiveRealScalar::try_new(2.0_f64).unwrap();
2571 let b = PositiveRealScalar::try_new(3.0_f64).unwrap();
2572
2573 let sum = a + &b;
2574 assert_eq!(*sum.as_ref(), 5.0);
2575 }
2576
2577 #[test]
2578 fn mul_trait_value_semantics() {
2579 // Test Mul trait for by-value operands
2580 let a = PositiveRealScalar::try_new(2.5_f64).unwrap();
2581 let b = PositiveRealScalar::try_new(4.0_f64).unwrap();
2582
2583 let product = a * b;
2584 assert_eq!(*product.as_ref(), 10.0);
2585 assert!(*product.as_ref() > 0.0);
2586 }
2587
2588 #[test]
2589 fn mul_trait_reference_semantics() {
2590 // Test Mul trait with reference operand
2591 let a = PositiveRealScalar::try_new(2.5_f64).unwrap();
2592 let b = PositiveRealScalar::try_new(4.0_f64).unwrap();
2593
2594 let product = a * &b;
2595 assert_eq!(*product.as_ref(), 10.0);
2596 }
2597
2598 #[test]
2599 fn add_assign_value_semantics() {
2600 // Test AddAssign trait for by-value operand
2601 let mut a = PositiveRealScalar::try_new(2.0_f64).unwrap();
2602 let b = PositiveRealScalar::try_new(3.0_f64).unwrap();
2603
2604 a += b;
2605 assert_eq!(*a.as_ref(), 5.0);
2606 }
2607
2608 #[test]
2609 fn add_assign_reference_semantics() {
2610 // Test AddAssign trait with reference operand
2611 let mut a = PositiveRealScalar::try_new(2.0_f64).unwrap();
2612 let b = PositiveRealScalar::try_new(3.0_f64).unwrap();
2613
2614 a += &b;
2615 assert_eq!(*a.as_ref(), 5.0);
2616 }
2617
2618 #[test]
2619 fn mul_assign_value_semantics() {
2620 // Test MulAssign trait for by-value operand
2621 let mut a = PositiveRealScalar::try_new(2.0_f64).unwrap();
2622 let b = PositiveRealScalar::try_new(3.0_f64).unwrap();
2623
2624 a *= b;
2625 assert_eq!(*a.as_ref(), 6.0);
2626 }
2627
2628 #[test]
2629 fn mul_assign_reference_semantics() {
2630 // Test MulAssign trait with reference operand
2631 let mut a = PositiveRealScalar::try_new(2.0_f64).unwrap();
2632 let b = PositiveRealScalar::try_new(3.0_f64).unwrap();
2633
2634 a *= &b;
2635 assert_eq!(*a.as_ref(), 6.0);
2636 }
2637
2638 #[test]
2639 fn max_min_traits() {
2640 // Test Max/Min traits (delegated to default behavior)
2641 let a = PositiveRealScalar::try_new(1.0_f64).unwrap();
2642 let b = PositiveRealScalar::try_new(3.0_f64).unwrap();
2643
2644 let max_val = Max::max_by_ref(&a, &b);
2645 assert_eq!(*max_val.as_ref(), 3.0);
2646
2647 let min_val = Min::min_by_ref(&a, &b);
2648 assert_eq!(*min_val.as_ref(), 1.0);
2649 }
2650 }
2651
2652 mod non_negative_real_scalar {
2653 use super::*;
2654 use crate::backends::native::validated::RealNative64StrictFiniteInDebug;
2655 use crate::functions::{Max, Min};
2656 use num::Zero;
2657
2658 #[test]
2659 fn add_trait_preserves_non_negativity() {
2660 let a = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
2661 let b = NonNegativeRealScalar::try_new(3.5_f64).unwrap();
2662
2663 let sum = a + b;
2664 assert_eq!(*sum.as_ref(), 5.5);
2665 assert!(*sum.as_ref() >= 0.0);
2666 }
2667
2668 #[test]
2669 fn add_trait_zero_is_identity() {
2670 let value = NonNegativeRealScalar::try_new(4.25_f64).unwrap();
2671 let zero = NonNegativeRealScalar::<f64>::zero();
2672
2673 assert_eq!(*(value.clone() + zero.clone()).as_ref(), 4.25);
2674 assert_eq!(*(zero + value).as_ref(), 4.25);
2675 }
2676
2677 #[test]
2678 fn zero_trait_methods() {
2679 let zero = NonNegativeRealScalar::<f64>::zero();
2680 let non_zero = NonNegativeRealScalar::try_new(0.5_f64).unwrap();
2681
2682 assert!(zero.is_zero());
2683 assert!(!non_zero.is_zero());
2684 assert_eq!(*zero.as_ref(), 0.0);
2685 }
2686
2687 #[test]
2688 fn max_trait_by_ref_and_by_value() {
2689 let small = NonNegativeRealScalar::try_new(1.0_f64).unwrap();
2690 let large = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
2691
2692 let by_ref = Max::max_by_ref(&small, &large);
2693 assert_eq!(*by_ref.as_ref(), 3.0);
2694
2695 let by_value = Max::max_by_value(small, large);
2696 assert_eq!(*by_value.as_ref(), 3.0);
2697 }
2698
2699 #[test]
2700 fn min_trait_by_ref_and_by_value() {
2701 let small = NonNegativeRealScalar::try_new(1.0_f64).unwrap();
2702 let large = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
2703
2704 let by_ref = Min::min_by_ref(&small, &large);
2705 assert_eq!(*by_ref.as_ref(), 1.0);
2706
2707 let by_value = Min::min_by_value(small, large);
2708 assert_eq!(*by_value.as_ref(), 1.0);
2709 }
2710
2711 #[test]
2712 fn max_min_by_ref_equal_values_returns_left_operand() {
2713 let left_max = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
2714 let right_max = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
2715 let selected_max = Max::max_by_ref(&left_max, &right_max);
2716 assert!(std::ptr::eq(selected_max, &left_max));
2717
2718 let left_min = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
2719 let right_min = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
2720 let selected_min = Min::min_by_ref(&left_min, &right_min);
2721 assert!(std::ptr::eq(selected_min, &left_min));
2722 }
2723
2724 #[test]
2725 fn try_new_negative_fails() {
2726 let result = NonNegativeRealScalar::try_new(-1.0_f64);
2727 assert!(
2728 matches!(result, Err(ErrorsNonNegativeRealScalar::NegativeValue { value, .. }) if value == -1.0)
2729 );
2730 }
2731
2732 #[test]
2733 fn display_trait() {
2734 let val = NonNegativeRealScalar::try_new(2.7_f64).unwrap();
2735 assert_eq!(format!("{}", val), "2.7");
2736
2737 let zero = NonNegativeRealScalar::try_new(0.0_f64).unwrap();
2738 assert_eq!(format!("{}", zero), "0");
2739 }
2740
2741 #[test]
2742 fn partial_ord() {
2743 let zero = NonNegativeRealScalar::try_new(0.0_f64).unwrap();
2744 let positive = NonNegativeRealScalar::try_new(1.0_f64).unwrap();
2745 assert!(zero < positive);
2746 }
2747
2748 #[test]
2749 fn serialize_deserialize() {
2750 let val = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
2751 let serialized = serde_json::to_string(&val).unwrap();
2752 let deserialized: NonNegativeRealScalar<f64> =
2753 serde_json::from_str(&serialized).unwrap();
2754 assert_eq!(val, deserialized);
2755 }
2756
2757 #[test]
2758 fn with_validated_type() {
2759 let inner = RealNative64StrictFinite::try_from_f64(0.0).unwrap();
2760 let val = NonNegativeRealScalar::try_new(inner).unwrap();
2761 assert_eq!(*val.as_ref().as_ref(), 0.0);
2762 }
2763
2764 #[test]
2765 #[cfg(debug_assertions)]
2766 #[should_panic(expected = "is not finite")]
2767 fn debug_panics_on_nan() {
2768 let _ = NonNegativeRealScalar::try_new(f64::NAN);
2769 }
2770
2771 #[test]
2772 fn try_new_positive() {
2773 // Test creating a NonNegativeRealScalar instance with positive values
2774 let v = NonNegativeRealScalar::try_new(2.5).unwrap();
2775 assert_eq!(*v.as_ref(), 2.5);
2776
2777 // Test with very small positive value
2778 let v = NonNegativeRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
2779 assert_eq!(*v.as_ref(), f64::MIN_POSITIVE);
2780
2781 // Test with large positive value
2782 let v = NonNegativeRealScalar::try_new(1e100).unwrap();
2783 assert_eq!(*v.as_ref(), 1e100);
2784 }
2785
2786 #[test]
2787 fn try_new_zero() {
2788 // Test creating a NonNegativeRealScalar instance with zero (VALID case)
2789 let v = NonNegativeRealScalar::try_new(0.0).unwrap();
2790 assert_eq!(*v.as_ref(), 0.0);
2791
2792 // This is the key difference from PositiveRealScalar:
2793 // Zero is VALID for NonNegativeRealScalar (x ≥ 0)
2794 // but INVALID for PositiveRealScalar (x > 0)
2795 }
2796
2797 #[test]
2798 fn try_new_invalid_negative() {
2799 // Test creating an invalid NonNegativeRealScalar instance with negative values
2800 let scalar = NonNegativeRealScalar::try_new(-0.1);
2801 assert!(matches!(
2802 scalar,
2803 Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
2804 ));
2805
2806 // Test with negative value close to zero
2807 let scalar = NonNegativeRealScalar::try_new(-f64::EPSILON);
2808 assert!(matches!(
2809 scalar,
2810 Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
2811 ));
2812
2813 // Test with large negative value
2814 let scalar = NonNegativeRealScalar::try_new(-100.0);
2815 assert!(matches!(
2816 scalar,
2817 Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
2818 ));
2819 }
2820
2821 #[test]
2822 #[cfg(debug_assertions)]
2823 #[should_panic = "The input value inf is not finite!"]
2824 fn try_new_invalid_infinite() {
2825 // Test creating an invalid NonNegativeRealScalar instance with infinity
2826 let _scalar = NonNegativeRealScalar::try_new(f64::INFINITY);
2827 }
2828
2829 #[test]
2830 #[cfg(debug_assertions)]
2831 #[should_panic = "The input value -inf is not finite!"]
2832 fn try_new_invalid_negative_infinite() {
2833 // Test creating an invalid NonNegativeRealScalar instance with negative infinity
2834 let _scalar = NonNegativeRealScalar::try_new(f64::NEG_INFINITY);
2835 }
2836
2837 #[test]
2838 #[cfg(debug_assertions)]
2839 #[should_panic = "The input value NaN is not finite!"]
2840 fn try_new_invalid_nan() {
2841 // Test creating an invalid NonNegativeRealScalar instance with NaN
2842 let _scalar = NonNegativeRealScalar::try_new(f64::NAN);
2843 }
2844
2845 #[test]
2846 fn equality_operator() {
2847 // Test equality operator for NonNegativeRealScalar instances
2848 let a = NonNegativeRealScalar::try_new(1.5).unwrap();
2849 let b = NonNegativeRealScalar::try_new(1.5).unwrap();
2850 assert!(a == b);
2851
2852 let c = NonNegativeRealScalar::try_new(0.0).unwrap();
2853 let d = NonNegativeRealScalar::try_new(0.0).unwrap();
2854 assert!(c == d);
2855 }
2856
2857 #[test]
2858 fn inequality_operator() {
2859 // Test inequality operator for NonNegativeRealScalar instances
2860 let a = NonNegativeRealScalar::try_new(0.0).unwrap();
2861 let b = NonNegativeRealScalar::try_new(1.0).unwrap();
2862 assert!(a != b);
2863
2864 let c = NonNegativeRealScalar::try_new(0.5).unwrap();
2865 let d = NonNegativeRealScalar::try_new(1.5).unwrap();
2866 assert!(c != d);
2867 }
2868
2869 #[test]
2870 fn debug_trait() {
2871 // Test the Debug trait
2872 let scalar = NonNegativeRealScalar::try_new(2.7).unwrap();
2873 assert_eq!(format!("{:?}", scalar), "NonNegativeRealScalar(2.7)");
2874
2875 let zero = NonNegativeRealScalar::try_new(0.0).unwrap();
2876 assert_eq!(format!("{:?}", zero), "NonNegativeRealScalar(0.0)");
2877 }
2878
2879 #[test]
2880 fn clone_trait() {
2881 // Test the Clone trait
2882 let scalar = NonNegativeRealScalar::try_new(1.414).unwrap();
2883 let cloned_scalar = scalar.clone();
2884 assert_eq!(scalar, cloned_scalar);
2885 }
2886
2887 #[test]
2888 fn partial_eq_trait() {
2889 // Test the PartialEq trait
2890 let scalar1 = NonNegativeRealScalar::try_new(0.577).unwrap();
2891 let scalar2 = NonNegativeRealScalar::try_new(0.577).unwrap();
2892 assert_eq!(scalar1, scalar2);
2893
2894 // Test with zero
2895 let zero1 = NonNegativeRealScalar::try_new(0.0).unwrap();
2896 let zero2 = NonNegativeRealScalar::try_new(0.0).unwrap();
2897 assert_eq!(zero1, zero2);
2898 }
2899
2900 #[test]
2901 fn partial_ord_trait() {
2902 // Test the PartialOrd trait
2903 let scalar1 = NonNegativeRealScalar::try_new(0.0).unwrap();
2904 let scalar2 = NonNegativeRealScalar::try_new(1.0).unwrap();
2905 assert!(scalar1 < scalar2);
2906
2907 let scalar3 = NonNegativeRealScalar::try_new(3.0).unwrap();
2908 let scalar4 = NonNegativeRealScalar::try_new(3.0).unwrap();
2909 assert!(scalar3 <= scalar4);
2910 assert!(scalar4 >= scalar3);
2911
2912 // Test that zero is less than positive values
2913 let zero = NonNegativeRealScalar::try_new(0.0).unwrap();
2914 let positive = NonNegativeRealScalar::try_new(0.001).unwrap();
2915 assert!(zero < positive);
2916 }
2917
2918 #[test]
2919 fn as_ref_trait() {
2920 // Test the AsRef trait
2921 let scalar = NonNegativeRealScalar::try_new(2.0).unwrap();
2922 let inner: &f64 = scalar.as_ref();
2923 assert_eq!(*inner, 2.0);
2924
2925 let zero = NonNegativeRealScalar::try_new(0.0).unwrap();
2926 let inner: &f64 = zero.as_ref();
2927 assert_eq!(*inner, 0.0);
2928 }
2929
2930 #[test]
2931 fn into_inner() {
2932 // Test converting a NonNegativeRealScalar instance into its inner value
2933 let scalar = NonNegativeRealScalar::try_new(42.0).unwrap();
2934 let inner = scalar.into_inner();
2935 assert_eq!(inner, 42.0);
2936
2937 // Test with zero
2938 let zero = NonNegativeRealScalar::try_new(0.0).unwrap();
2939 let inner = zero.into_inner();
2940 assert_eq!(inner, 0.0);
2941
2942 // Test with very small positive value
2943 let scalar = NonNegativeRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
2944 let inner = scalar.into_inner();
2945 assert_eq!(inner, f64::MIN_POSITIVE);
2946 }
2947
2948 #[test]
2949 fn edge_cases() {
2950 // Test with zero (key edge case)
2951 let zero_scalar = NonNegativeRealScalar::try_new(0.0).unwrap();
2952 assert_eq!(*zero_scalar.as_ref(), 0.0);
2953
2954 // Test with very small positive values
2955 let tiny_scalar = NonNegativeRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
2956 assert_eq!(*tiny_scalar.as_ref(), f64::MIN_POSITIVE);
2957
2958 // Test with large positive values
2959 let large_scalar = NonNegativeRealScalar::try_new(f64::MAX).unwrap();
2960 assert_eq!(*large_scalar.as_ref(), f64::MAX);
2961
2962 // Test with epsilon
2963 let epsilon_scalar = NonNegativeRealScalar::try_new(f64::EPSILON).unwrap();
2964 assert_eq!(*epsilon_scalar.as_ref(), f64::EPSILON);
2965 }
2966
2967 #[test]
2968 fn generic_scalar_types() {
2969 // Test with RealNative64StrictFiniteInDebug type
2970 let scalar = NonNegativeRealScalar::try_new(
2971 RealNative64StrictFiniteInDebug::try_new(5.0).unwrap(),
2972 )
2973 .unwrap();
2974 assert_eq!(
2975 *scalar.as_ref(),
2976 RealNative64StrictFiniteInDebug::try_new(5.0).unwrap()
2977 );
2978
2979 // Test with zero (valid for NonNegativeRealScalar)
2980 let zero_value = RealNative64StrictFiniteInDebug::try_new(0.0).unwrap();
2981 let scalar_result = NonNegativeRealScalar::try_new(zero_value);
2982 assert!(scalar_result.is_ok()); // Should succeed!
2983
2984 // Test error case with generic type
2985 let negative_value = RealNative64StrictFiniteInDebug::try_new(-1.0).unwrap();
2986 let scalar_result = NonNegativeRealScalar::try_new(negative_value);
2987 assert!(matches!(
2988 scalar_result,
2989 Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
2990 ));
2991 }
2992
2993 #[test]
2994 fn mathematical_operations() {
2995 // Test that the wrapped values behave correctly in mathematical contexts
2996 let scalar1 = NonNegativeRealScalar::try_new(0.0).unwrap();
2997 let scalar2 = NonNegativeRealScalar::try_new(2.0).unwrap();
2998 let scalar3 = NonNegativeRealScalar::try_new(3.0).unwrap();
2999
3000 // Test comparison operations
3001 assert!(scalar1 < scalar2);
3002 assert!(scalar2 < scalar3);
3003 assert!(scalar3 > scalar1);
3004 assert_eq!(
3005 scalar1.partial_cmp(&scalar2),
3006 Some(std::cmp::Ordering::Less)
3007 );
3008 assert_eq!(
3009 scalar2.partial_cmp(&scalar1),
3010 Some(std::cmp::Ordering::Greater)
3011 );
3012 }
3013
3014 #[test]
3015 fn error_messages() {
3016 // Test that error messages are descriptive
3017 let negative_result = NonNegativeRealScalar::try_new(-1.0);
3018 match negative_result {
3019 Err(ErrorsNonNegativeRealScalar::NegativeValue { value, .. }) => {
3020 assert_eq!(value, -1.0);
3021 }
3022 _ => panic!("Expected NegativeValue error"),
3023 }
3024
3025 let large_negative_result = NonNegativeRealScalar::try_new(-100.5);
3026 match large_negative_result {
3027 Err(ErrorsNonNegativeRealScalar::NegativeValue { value, .. }) => {
3028 assert_eq!(value, -100.5);
3029 }
3030 _ => panic!("Expected NegativeValue error"),
3031 }
3032 }
3033
3034 #[test]
3035 fn distinction_from_positive_real_scalar() {
3036 // Critical test: demonstrate the key difference between
3037 // NonNegativeRealScalar (x ≥ 0) and PositiveRealScalar (x > 0)
3038
3039 // Zero is VALID for NonNegativeRealScalar
3040 let non_neg_zero = NonNegativeRealScalar::try_new(0.0);
3041 assert!(
3042 non_neg_zero.is_ok(),
3043 "Zero should be valid for NonNegativeRealScalar (x ≥ 0)"
3044 );
3045
3046 // Zero is INVALID for PositiveRealScalar
3047 let pos_zero = PositiveRealScalar::try_new(0.0);
3048 assert!(
3049 pos_zero.is_err(),
3050 "Zero should be invalid for PositiveRealScalar (x > 0)"
3051 );
3052 assert!(matches!(
3053 pos_zero,
3054 Err(ErrorsPositiveRealScalar::ZeroValue { .. })
3055 ));
3056
3057 // Positive values are valid for both
3058 let non_neg_positive = NonNegativeRealScalar::try_new(1.0);
3059 let pos_positive = PositiveRealScalar::try_new(1.0);
3060 assert!(non_neg_positive.is_ok());
3061 assert!(pos_positive.is_ok());
3062
3063 // Negative values are invalid for both
3064 let non_neg_negative = NonNegativeRealScalar::try_new(-1.0);
3065 let pos_negative = PositiveRealScalar::try_new(-1.0);
3066 assert!(non_neg_negative.is_err());
3067 assert!(pos_negative.is_err());
3068 }
3069
3070 #[test]
3071 fn use_case_distance() {
3072 // Real-world use case: computing distances (which can be zero)
3073 fn compute_distance(x1: f64, x2: f64) -> NonNegativeRealScalar<f64> {
3074 let diff = (x2 - x1).abs();
3075 NonNegativeRealScalar::try_new(diff).unwrap()
3076 }
3077
3078 // Distance between different points
3079 let dist1 = compute_distance(0.0, 5.0);
3080 assert_eq!(*dist1.as_ref(), 5.0);
3081
3082 // Distance from a point to itself (zero distance is valid!)
3083 let dist2 = compute_distance(3.0, 3.0);
3084 assert_eq!(*dist2.as_ref(), 0.0);
3085 }
3086
3087 #[test]
3088 fn use_case_absolute_value() {
3089 // Real-world use case: absolute values (which can be zero)
3090 fn safe_abs(x: f64) -> NonNegativeRealScalar<f64> {
3091 NonNegativeRealScalar::try_new(x.abs()).unwrap()
3092 }
3093
3094 assert_eq!(*safe_abs(-5.0).as_ref(), 5.0);
3095 assert_eq!(*safe_abs(0.0).as_ref(), 0.0); // Zero is valid!
3096 assert_eq!(*safe_abs(3.0).as_ref(), 3.0);
3097 }
3098
3099 #[test]
3100 fn one_trait_identity() {
3101 // Test the One trait: one() should return NonNegativeRealScalar(1.0)
3102 let one = NonNegativeRealScalar::<f64>::one();
3103 assert_eq!(*one.as_ref(), 1.0);
3104 }
3105
3106 #[test]
3107 fn one_trait_multiplicative_identity() {
3108 // Test that one acts as multiplicative identity
3109 let value = NonNegativeRealScalar::try_new(5.5_f64).unwrap();
3110 let one = NonNegativeRealScalar::<f64>::one();
3111
3112 let result = value.clone() * one.clone();
3113 assert_eq!(*result.as_ref(), 5.5);
3114
3115 let result = one * value;
3116 assert_eq!(*result.as_ref(), 5.5);
3117 }
3118
3119 #[test]
3120 fn one_trait_with_generic_types() {
3121 // Test One trait with validated types
3122 let one = NonNegativeRealScalar::<RealNative64StrictFiniteInDebug>::one();
3123 assert_eq!(*one.as_ref().as_ref(), 1.0);
3124 }
3125
3126 #[test]
3127 fn pow_integer_exponents() {
3128 // Test Pow trait with different integer exponent types
3129 let base = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
3130
3131 // Test with u32
3132 let result = base.clone().pow(3u32);
3133 assert_eq!(*result.as_ref(), 8.0);
3134
3135 // Test with u8
3136 let result = base.clone().pow(2u8);
3137 assert_eq!(*result.as_ref(), 4.0);
3138
3139 // Test with i32 (positive)
3140 let result = base.clone().pow(2i32);
3141 assert_eq!(*result.as_ref(), 4.0);
3142 }
3143
3144 #[test]
3145 fn pow_zero_exponent() {
3146 // Test that any number raised to power 0 equals 1
3147 let base = NonNegativeRealScalar::try_new(5.5_f64).unwrap();
3148
3149 let result = base.clone().pow(0u32);
3150 assert_eq!(*result.as_ref(), 1.0);
3151
3152 // Even 0 raised to power 0 should be 1
3153 let zero = NonNegativeRealScalar::try_new(0.0_f64).unwrap();
3154 let result = zero.pow(0u32);
3155 assert_eq!(*result.as_ref(), 1.0);
3156 }
3157
3158 #[test]
3159 fn pow_one_exponent() {
3160 // Test that any number raised to power 1 equals itself
3161 let base = NonNegativeRealScalar::try_new(3.7_f64).unwrap();
3162
3163 let result = base.clone().pow(1u32);
3164 assert_eq!(*result.as_ref(), 3.7);
3165 }
3166
3167 #[test]
3168 fn pow_preserves_non_negativity() {
3169 // Test that raising non-negative to any integer power preserves non-negativity
3170 let base = NonNegativeRealScalar::try_new(2.5_f64).unwrap();
3171
3172 let result = base.clone().pow(5u32);
3173 assert!(*result.as_ref() >= 0.0);
3174 assert_eq!(*result.as_ref(), 2.5_f64.powi(5));
3175
3176 // Even 0 raised to positive power stays non-negative
3177 let zero = NonNegativeRealScalar::try_new(0.0_f64).unwrap();
3178 let result = zero.pow(10u32);
3179 assert_eq!(*result.as_ref(), 0.0);
3180 }
3181
3182 #[test]
3183 fn pow_with_try_pow() {
3184 // Test the try_pow method
3185 let base = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
3186
3187 let result = base.clone().try_pow(2u32);
3188 assert!(result.is_ok());
3189 assert_eq!(*result.unwrap().as_ref(), 9.0);
3190
3191 // Test with different exponent types
3192 let result = base.clone().try_pow(3i32);
3193 assert!(result.is_ok());
3194 assert_eq!(*result.unwrap().as_ref(), 27.0);
3195 }
3196
3197 #[test]
3198 fn pow_large_exponents() {
3199 // Test with large exponents
3200 let base = NonNegativeRealScalar::try_new(1.1_f64).unwrap();
3201 let result = base.pow(10u32);
3202 let expected = 1.1_f64.powi(10);
3203 assert!((result.as_ref() - expected).abs() < 1e-10);
3204 }
3205
3206 #[test]
3207 fn pow_small_base() {
3208 // Test with small base values
3209 let base = NonNegativeRealScalar::try_new(0.5_f64).unwrap();
3210
3211 let result = base.clone().pow(2u32);
3212 assert_eq!(*result.as_ref(), 0.25);
3213
3214 let result = base.clone().pow(3u32);
3215 assert_eq!(*result.as_ref(), 0.125);
3216 }
3217
3218 #[test]
3219 fn pow_with_generic_types() {
3220 // Test Pow with validated types
3221 let base_val = RealNative64StrictFiniteInDebug::try_new(2.0).unwrap();
3222 let base = NonNegativeRealScalar::try_new(base_val).unwrap();
3223
3224 let result = base.pow(3u32);
3225 assert_eq!(*result.as_ref().as_ref(), 8.0);
3226 }
3227
3228 #[test]
3229 fn pow_chain_operations() {
3230 // Test chaining pow operations
3231 let base = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
3232
3233 // (2^3)^2 = 2^6 = 64
3234 let result = base.clone().pow(3u32).pow(2u32);
3235 assert_eq!(*result.as_ref(), 64.0);
3236 }
3237 }
3238
3239 mod distinction_tests {
3240 use super::*;
3241
3242 #[test]
3243 fn positive_vs_non_negative_zero() {
3244 // This is the KEY difference between the two types
3245
3246 // Zero is INVALID for PositiveRealScalar (x > 0)
3247 assert!(PositiveRealScalar::try_new(0.0_f64).is_err());
3248
3249 // Zero is VALID for NonNegativeRealScalar (x ≥ 0)
3250 assert!(NonNegativeRealScalar::try_new(0.0_f64).is_ok());
3251 }
3252
3253 #[test]
3254 fn both_accept_positive() {
3255 let positive = 1.0_f64;
3256 assert!(PositiveRealScalar::try_new(positive).is_ok());
3257 assert!(NonNegativeRealScalar::try_new(positive).is_ok());
3258 }
3259
3260 #[test]
3261 fn both_reject_negative() {
3262 let negative = -1.0_f64;
3263 assert!(PositiveRealScalar::try_new(negative).is_err());
3264 assert!(NonNegativeRealScalar::try_new(negative).is_err());
3265 }
3266 }
3267
3268 mod conversion_tests {
3269 use super::*;
3270 use crate::backends::native::validated::RealNative64StrictFiniteInDebug;
3271
3272 #[test]
3273 fn from_positive_to_non_negative() {
3274 // Test basic conversion from PositiveRealScalar to NonNegativeRealScalar
3275 let positive = PositiveRealScalar::try_new(5.0_f64).unwrap();
3276 let non_negative: NonNegativeRealScalar<f64> = positive.into();
3277
3278 assert_eq!(*non_negative.as_ref(), 5.0);
3279 }
3280
3281 #[test]
3282 fn from_positive_to_non_negative_min_positive() {
3283 // Test conversion with f64::MIN_POSITIVE (smallest positive value)
3284 let positive = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
3285 let non_negative: NonNegativeRealScalar<f64> = positive.into();
3286
3287 assert_eq!(*non_negative.as_ref(), f64::MIN_POSITIVE);
3288 }
3289
3290 #[test]
3291 fn from_positive_to_non_negative_large_value() {
3292 // Test conversion with large value
3293 let positive = PositiveRealScalar::try_new(f64::MAX).unwrap();
3294 let non_negative: NonNegativeRealScalar<f64> = positive.into();
3295
3296 assert_eq!(*non_negative.as_ref(), f64::MAX);
3297 }
3298
3299 #[test]
3300 fn from_positive_to_non_negative_one() {
3301 // Test conversion with the multiplicative identity
3302 let positive = PositiveRealScalar::try_new(1.0_f64).unwrap();
3303 let non_negative: NonNegativeRealScalar<f64> = positive.into();
3304
3305 assert_eq!(*non_negative.as_ref(), 1.0);
3306 }
3307
3308 #[test]
3309 fn from_positive_to_non_negative_preserves_value() {
3310 // Test that the conversion preserves the exact value
3311 let test_values = vec![0.5, 1.0, 2.5, 10.0, 99.9, 1e10, 1e-10];
3312
3313 for value in test_values {
3314 let positive = PositiveRealScalar::try_new(value).unwrap();
3315 let non_negative: NonNegativeRealScalar<f64> = positive.into();
3316
3317 assert_eq!(*non_negative.as_ref(), value);
3318 }
3319 }
3320
3321 #[test]
3322 fn from_positive_to_non_negative_with_generic_type() {
3323 // Test conversion with validated types
3324 let inner = RealNative64StrictFiniteInDebug::try_new(5.0).unwrap();
3325 let positive = PositiveRealScalar::try_new(inner).unwrap();
3326 let non_negative: NonNegativeRealScalar<RealNative64StrictFiniteInDebug> =
3327 positive.into();
3328
3329 assert_eq!(*non_negative.as_ref().as_ref(), 5.0);
3330 }
3331
3332 #[test]
3333 fn from_positive_consumed_by_conversion() {
3334 // Test that the conversion consumes the PositiveRealScalar
3335 let positive = PositiveRealScalar::try_new(5.0_f64).unwrap();
3336 let _non_negative: NonNegativeRealScalar<f64> = positive.into();
3337
3338 // If we try to use `positive` here, it would be a compile error
3339 // because it was consumed by the conversion.
3340 // This test just documents the behavior.
3341 }
3342
3343 #[test]
3344 fn from_positive_creates_valid_non_negative() {
3345 // Test that the converted value is indeed non-negative
3346 let positive = PositiveRealScalar::try_new(3.5_f64).unwrap();
3347 let non_negative: NonNegativeRealScalar<f64> = positive.into();
3348
3349 // The converted value should pass all non-negative validation
3350 assert!(*non_negative.as_ref() >= 0.0);
3351 assert!(!non_negative.is_zero()); // Since it came from a positive value
3352 }
3353
3354 #[test]
3355 fn from_positive_mathematical_properties() {
3356 // Test that mathematical properties are preserved after conversion
3357 let positive_a = PositiveRealScalar::try_new(2.0_f64).unwrap();
3358 let positive_b = PositiveRealScalar::try_new(3.0_f64).unwrap();
3359
3360 let non_neg_a: NonNegativeRealScalar<f64> = positive_a.into();
3361 let non_neg_b: NonNegativeRealScalar<f64> = positive_b.into();
3362
3363 // Arithmetic should work the same way after conversion
3364 let sum = non_neg_a + non_neg_b;
3365 assert_eq!(*sum.as_ref(), 5.0);
3366 }
3367
3368 #[test]
3369 fn from_positive_with_chained_operations() {
3370 // Test conversion followed by operations
3371 let positive = PositiveRealScalar::try_new(2.0_f64).unwrap();
3372 let non_negative: NonNegativeRealScalar<f64> = positive.into();
3373
3374 // Test that we can use trait methods on the converted value
3375 let one = NonNegativeRealScalar::<f64>::one();
3376 let result = non_negative * one;
3377
3378 assert_eq!(*result.as_ref(), 2.0);
3379 }
3380
3381 #[test]
3382 fn from_positive_comparison_after_conversion() {
3383 // Test comparison operations work correctly after conversion
3384 let positive_small = PositiveRealScalar::try_new(1.0_f64).unwrap();
3385 let positive_large = PositiveRealScalar::try_new(3.0_f64).unwrap();
3386
3387 let non_neg_small: NonNegativeRealScalar<f64> = positive_small.into();
3388 let non_neg_large: NonNegativeRealScalar<f64> = positive_large.into();
3389
3390 assert!(non_neg_small < non_neg_large);
3391 assert!(non_neg_large > non_neg_small);
3392 }
3393
3394 #[test]
3395 fn from_positive_pow_after_conversion() {
3396 // Test that Pow trait works correctly after conversion
3397 let positive = PositiveRealScalar::try_new(2.0_f64).unwrap();
3398 let non_negative: NonNegativeRealScalar<f64> = positive.into();
3399
3400 let squared = non_negative.pow(2u32);
3401 assert_eq!(*squared.as_ref(), 4.0);
3402 }
3403
3404 #[test]
3405 fn from_positive_sqrt_after_conversion() {
3406 // Test that sqrt works correctly after conversion
3407 let positive = PositiveRealScalar::try_new(4.0_f64).unwrap();
3408 let non_negative: NonNegativeRealScalar<f64> = positive.into();
3409
3410 let sqrt = non_negative.sqrt();
3411 assert_eq!(*sqrt.as_ref(), 2.0);
3412 }
3413 }
3414
3415 mod cross_type_arithmetic_tests {
3416 use super::*;
3417 use crate::backends::native::validated::RealNative64StrictFiniteInDebug;
3418
3419 // --- Pos + NonNeg → Pos ---
3420
3421 #[test]
3422 fn pos_add_non_neg_value() {
3423 // PositiveRealScalar + NonNegativeRealScalar (by value) → PositiveRealScalar
3424 let pos = PositiveRealScalar::try_new(2.0_f64).unwrap();
3425 let non_neg = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
3426
3427 let result: PositiveRealScalar<f64> = pos + non_neg;
3428 assert_eq!(*result.as_ref(), 5.0);
3429 }
3430
3431 #[test]
3432 fn pos_add_non_neg_ref() {
3433 // PositiveRealScalar + &NonNegativeRealScalar (by reference) → PositiveRealScalar
3434 let pos = PositiveRealScalar::try_new(2.0_f64).unwrap();
3435 let non_neg = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
3436
3437 let result: PositiveRealScalar<f64> = pos + &non_neg;
3438 assert_eq!(*result.as_ref(), 5.0);
3439 // non_neg is still usable here (not consumed)
3440 assert_eq!(*non_neg.as_ref(), 3.0);
3441 }
3442
3443 #[test]
3444 fn pos_add_non_neg_zero_preserves_positivity() {
3445 // Adding zero (NonNeg) to a positive value should remain positive
3446 let pos = PositiveRealScalar::try_new(5.0_f64).unwrap();
3447 let zero = NonNegativeRealScalar::<f64>::zero();
3448
3449 let result: PositiveRealScalar<f64> = pos + zero;
3450 assert_eq!(*result.as_ref(), 5.0);
3451 assert!(*result.as_ref() > 0.0);
3452 }
3453
3454 #[test]
3455 fn pos_add_non_neg_zero_ref_preserves_positivity() {
3456 // Adding zero (NonNeg ref) to a positive value should remain positive
3457 let pos = PositiveRealScalar::try_new(5.0_f64).unwrap();
3458 let zero = NonNegativeRealScalar::<f64>::zero();
3459
3460 let result: PositiveRealScalar<f64> = pos + &zero;
3461 assert_eq!(*result.as_ref(), 5.0);
3462 }
3463
3464 // --- NonNeg + Pos → Pos ---
3465
3466 #[test]
3467 fn non_neg_add_pos_value() {
3468 // NonNegativeRealScalar + PositiveRealScalar (by value) → PositiveRealScalar
3469 let non_neg = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
3470 let pos = PositiveRealScalar::try_new(2.0_f64).unwrap();
3471
3472 let result: PositiveRealScalar<f64> = non_neg + pos;
3473 assert_eq!(*result.as_ref(), 5.0);
3474 }
3475
3476 #[test]
3477 fn non_neg_add_pos_ref() {
3478 // NonNegativeRealScalar + &PositiveRealScalar (by reference) → PositiveRealScalar
3479 let non_neg = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
3480 let pos = PositiveRealScalar::try_new(2.0_f64).unwrap();
3481
3482 let result: PositiveRealScalar<f64> = non_neg + &pos;
3483 assert_eq!(*result.as_ref(), 5.0);
3484 // pos is still usable here (not consumed)
3485 assert_eq!(*pos.as_ref(), 2.0);
3486 }
3487
3488 #[test]
3489 fn zero_add_pos_value() {
3490 // Zero (NonNeg) + PositiveRealScalar → PositiveRealScalar
3491 // Key test: zero + positive should give the positive value
3492 let zero = NonNegativeRealScalar::<f64>::zero();
3493 let pos = PositiveRealScalar::try_new(5.0_f64).unwrap();
3494
3495 let result: PositiveRealScalar<f64> = zero + pos;
3496 assert_eq!(*result.as_ref(), 5.0);
3497 assert!(*result.as_ref() > 0.0);
3498 }
3499
3500 #[test]
3501 fn zero_add_pos_ref() {
3502 // Zero (NonNeg) + &PositiveRealScalar → PositiveRealScalar
3503 let zero = NonNegativeRealScalar::<f64>::zero();
3504 let pos = PositiveRealScalar::try_new(5.0_f64).unwrap();
3505
3506 let result: PositiveRealScalar<f64> = zero + &pos;
3507 assert_eq!(*result.as_ref(), 5.0);
3508 }
3509
3510 // --- Commutativity ---
3511
3512 #[test]
3513 fn commutativity() {
3514 // Pos + NonNeg and NonNeg + Pos should give the same numeric value
3515 let pos = PositiveRealScalar::try_new(2.0_f64).unwrap();
3516 let non_neg = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
3517
3518 let left: PositiveRealScalar<f64> = pos.clone() + non_neg.clone();
3519 let right: PositiveRealScalar<f64> = non_neg + pos;
3520
3521 assert_eq!(*left.as_ref(), *right.as_ref());
3522 }
3523
3524 // --- Generic types ---
3525
3526 #[test]
3527 fn cross_type_add_with_generic_type() {
3528 let pos_inner = RealNative64StrictFiniteInDebug::try_new(2.0).unwrap();
3529 let non_neg_inner = RealNative64StrictFiniteInDebug::try_new(3.0).unwrap();
3530
3531 let pos = PositiveRealScalar::try_new(pos_inner).unwrap();
3532 let non_neg = NonNegativeRealScalar::try_new(non_neg_inner).unwrap();
3533
3534 let result: PositiveRealScalar<RealNative64StrictFiniteInDebug> = pos + non_neg;
3535 assert_eq!(*result.as_ref().as_ref(), 5.0);
3536 }
3537 }
3538}