Skip to main content

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//! # Design Philosophy
25//!
26//! ## Type Safety Through Distinct Types
27//!
28//! Rather than using raw primitives like `f64` directly, this module provides
29//! semantically meaningful types that encode mathematical constraints:
30//!
31//! ```rust
32//! use num_valid::{
33//!     backends::native64::validated::RealNative64StrictFinite, RealScalar,
34//!     scalars::{AbsoluteTolerance, RelativeTolerance, PositiveRealScalar},
35//! };
36//! use try_create::TryNew;
37//!
38//! // These are different types that cannot be confused:
39//! let abs_tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();   // For absolute comparisons
40//! let rel_tol = RelativeTolerance::try_new(1e-6_f64).unwrap();    // For relative comparisons
41//! let length = PositiveRealScalar::try_new(5.0_f64).unwrap();     // Must be > 0
42//!
43//! // Type system prevents mixing them up:
44//! // let wrong: AbsoluteTolerance<f64> = rel_tol;  // ← Compilation error!
45//! ```
46//!
47//! ## Validation at Construction Time
48//!
49//! All types validate their input at construction time, failing fast on invalid values:
50//!
51//! ```rust
52//! use num_valid::scalars::{AbsoluteTolerance, PositiveRealScalar, ErrorsTolerance, ErrorsPositiveRealScalar};
53//! use try_create::TryNew;
54//!
55//! // Negative tolerances are rejected
56//! let invalid_tol = AbsoluteTolerance::try_new(-1e-6_f64);
57//! assert!(matches!(invalid_tol, Err(ErrorsTolerance::NegativeValue { .. })));
58//!
59//! // Zero is not positive
60//! let invalid_pos = PositiveRealScalar::try_new(0.0_f64);
61//! assert!(matches!(invalid_pos, Err(ErrorsPositiveRealScalar::ZeroValue { .. })));
62//! ```
63//!
64//! # Tolerance Types
65//!
66//! ## [`AbsoluteTolerance<T>`]
67//!
68//! Represents an absolute error bound for numerical comparisons. The tolerance value
69//! must be non-negative (≥ 0).
70//!
71//! ```rust
72//! use num_valid::scalars::AbsoluteTolerance;
73//! use try_create::TryNew;
74//!
75//! // Create tolerances
76//! let tight = AbsoluteTolerance::try_new(1e-12_f64).unwrap();
77//! let loose = AbsoluteTolerance::try_new(1e-6_f64).unwrap();
78//! let zero = AbsoluteTolerance::<f64>::zero();       // Exact comparison
79//! let epsilon = AbsoluteTolerance::<f64>::epsilon(); // Machine epsilon
80//!
81//! // Use in approximate comparison
82//! fn approximately_equal<T: num_valid::RealScalar + Clone>(
83//!     a: T,
84//!     b: T,
85//!     tolerance: &AbsoluteTolerance<T>
86//! ) -> bool {
87//!     let diff = (a - b).abs();
88//!     &diff <= tolerance.as_ref()
89//! }
90//!
91//! assert!(approximately_equal(1.0, 1.0 + 1e-13, &tight));
92//! assert!(!approximately_equal(1.0, 1.0 + 1e-11, &tight));
93//! ```
94//!
95//! ## [`RelativeTolerance<T>`]
96//!
97//! Represents a relative (proportional) error bound. Can be converted to an absolute
98//! tolerance based on a reference value.
99//!
100//! ```rust
101//! use num_valid::scalars::RelativeTolerance;
102//! use try_create::TryNew;
103//!
104//! let rel_tol = RelativeTolerance::try_new(0.01_f64).unwrap(); // 1% tolerance
105//!
106//! // Convert to absolute tolerance based on reference value
107//! let reference = 1000.0_f64;
108//! let abs_tol = rel_tol.absolute_tolerance(reference);
109//! assert_eq!(*abs_tol.as_ref(), 10.0); // 1% of 1000 = 10
110//! ```
111//!
112//! # Constrained Real Number Types
113//!
114//! ## [`PositiveRealScalar<T>`]
115//!
116//! Wraps a real scalar that must be strictly positive (> 0). Zero is **not** valid.
117//!
118//! ```rust
119//! use num_valid::scalars::{PositiveRealScalar, ErrorsPositiveRealScalar};
120//! use try_create::TryNew;
121//!
122//! // Valid positive values
123//! let length = PositiveRealScalar::try_new(2.5_f64).unwrap();
124//! let tiny = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
125//!
126//! // Zero is NOT positive (x > 0 required)
127//! let zero_result = PositiveRealScalar::try_new(0.0_f64);
128//! assert!(matches!(zero_result, Err(ErrorsPositiveRealScalar::ZeroValue { .. })));
129//!
130//! // Negative values rejected
131//! let neg_result = PositiveRealScalar::try_new(-1.0_f64);
132//! assert!(matches!(neg_result, Err(ErrorsPositiveRealScalar::NegativeValue { .. })));
133//! ```
134//!
135//! ## [`NonNegativeRealScalar<T>`]
136//!
137//! Wraps a real scalar that must be non-negative (≥ 0). Zero **is** valid.
138//!
139//! ```rust
140//! use num_valid::scalars::{NonNegativeRealScalar, PositiveRealScalar};
141//! use try_create::TryNew;
142//!
143//! // Zero is valid for NonNegativeRealScalar
144//! let zero = NonNegativeRealScalar::try_new(0.0_f64).unwrap();
145//! assert_eq!(*zero.as_ref(), 0.0);
146//!
147//! // But NOT for PositiveRealScalar
148//! assert!(PositiveRealScalar::try_new(0.0_f64).is_err());
149//!
150//! // Use case: computing distances (can be zero)
151//! fn distance(a: f64, b: f64) -> NonNegativeRealScalar<f64> {
152//!     NonNegativeRealScalar::try_new((a - b).abs()).unwrap()
153//! }
154//!
155//! let d = distance(5.0, 5.0); // Zero distance is valid
156//! assert_eq!(*d.as_ref(), 0.0);
157//! ```
158//!
159//! # Generic Programming with [`RealScalar`]
160//!
161//! All types work seamlessly with any scalar type implementing [`RealScalar`]:
162//!
163//! ```rust
164//! use num_valid::{
165//!     backends::native64::validated::{RealNative64StrictFinite, RealNative64StrictFiniteInDebug},
166//!     RealScalar,
167//!     scalars::AbsoluteTolerance
168//! };
169//! use try_create::TryNew;
170//!
171//! // Same tolerance type works with different backends
172//! type FastTol = AbsoluteTolerance<f64>;
173//! type SafeTol = AbsoluteTolerance<RealNative64StrictFinite>;
174//! type DebugTol = AbsoluteTolerance<RealNative64StrictFiniteInDebug>;
175//!
176//! let fast = FastTol::try_new(1e-10).unwrap();
177//! let safe = SafeTol::try_new(RealNative64StrictFinite::try_from_f64(1e-10).unwrap()).unwrap();
178//! ```
179//!
180//! # Performance Characteristics
181//!
182//! All wrapper types are designed as zero-cost abstractions:
183//!
184//! | Type | Memory Layout | Runtime Overhead |
185//! |------|---------------|------------------|
186//! | [`AbsoluteTolerance<T>`] | Same as `T` | Zero (validation at construction only) |
187//! | [`RelativeTolerance<T>`] | Same as `T` | Zero (validation at construction only) |
188//! | [`PositiveRealScalar<T>`] | Same as `T` | Zero (validation at construction only) |
189//! | [`NonNegativeRealScalar<T>`] | Same as `T` | Zero (validation at construction only) |
190//!
191//! The `#[repr(transparent)]` attribute ensures that each wrapper has the exact same
192//! memory layout as its underlying type.
193//!
194//! # Relationship to `approx` Crate
195//!
196//! These tolerance types complement the [`approx`] crate's comparison traits. While `approx`
197//! uses raw `f64` for epsilon values (which can be negative, causing silent failures),
198//! these wrappers guarantee non-negativity at construction time:
199//!
200//! ```rust
201//! use num_valid::scalars::AbsoluteTolerance;
202//! use num_valid::approx::assert_abs_diff_eq;
203//! use try_create::TryNew;
204//!
205//! // Create a validated tolerance
206//! let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
207//!
208//! // Use with approx (extract the inner value)
209//! let a = 1.0_f64;
210//! let b = 1.0 + 1e-11;
211//! assert_abs_diff_eq!(a, b, epsilon = *tol.as_ref());
212//! ```
213
214use crate::{
215    RealScalar,
216    core::errors::capture_backtrace,
217    functions::{Max, Min},
218};
219use derive_more::{AsRef, Display, LowerExp};
220use into_inner::IntoInner;
221use num::Zero;
222use serde::{Deserialize, Serialize};
223use std::{backtrace::Backtrace, ops::Add};
224use thiserror::Error;
225use try_create::TryNew;
226
227//------------------------------------------------------------------------------------------------------------
228// Error Types
229//------------------------------------------------------------------------------------------------------------
230
231/// Error type for tolerance validation failures.
232///
233/// This enum provides detailed error information when attempting to construct
234/// tolerance types ([`AbsoluteTolerance<T>`] and [`RelativeTolerance<T>`]) with
235/// invalid input values.
236///
237/// # Error Variants
238///
239/// - [`ErrorsTolerance::NegativeValue`]: The input value was negative, which violates
240///   the non-negativity constraint required for all tolerance values.
241///
242/// # Examples
243///
244/// ```rust
245/// use num_valid::scalars::{AbsoluteTolerance, ErrorsTolerance};
246/// use try_create::TryNew;
247///
248/// // Negative values are rejected
249/// match AbsoluteTolerance::try_new(-1e-6_f64) {
250///     Err(ErrorsTolerance::NegativeValue { value, .. }) => {
251///         println!("Rejected negative tolerance: {}", value);
252///         assert_eq!(value, -1e-6);
253///     }
254///     _ => unreachable!(),
255/// }
256/// ```
257///
258/// # Backtrace Support
259///
260/// The error includes backtrace information when the `backtrace` feature is enabled,
261/// which can aid in debugging by showing where the invalid value originated.
262#[derive(Debug, Error)]
263pub enum ErrorsTolerance<RealType: RealScalar> {
264    /// The input value was negative (must be ≥ 0).
265    #[error("Negative value detected: {value}")]
266    NegativeValue {
267        /// The negative value that was rejected.
268        value: RealType,
269        /// Captured backtrace for debugging.
270        backtrace: Backtrace,
271    },
272}
273
274/// Error type for [`PositiveRealScalar<T>`] validation failures.
275///
276/// This enum provides detailed error information when attempting to construct
277/// a [`PositiveRealScalar<T>`] with invalid input values.
278///
279/// # Error Variants
280///
281/// - [`ErrorsPositiveRealScalar::NegativeValue`]: The input was negative (< 0).
282/// - [`ErrorsPositiveRealScalar::ZeroValue`]: The input was zero (not strictly positive).
283///
284/// # Mathematical Distinction
285///
286/// Positive real numbers are defined as `ℝ⁺ = {x ∈ ℝ : x > 0}`, which **excludes** zero.
287/// This is distinct from non-negative reals `ℝ₀⁺ = {x ∈ ℝ : x ≥ 0}`.
288///
289/// # Examples
290///
291/// ```rust
292/// use num_valid::scalars::{PositiveRealScalar, ErrorsPositiveRealScalar};
293/// use try_create::TryNew;
294///
295/// // Zero is NOT positive
296/// match PositiveRealScalar::try_new(0.0_f64) {
297///     Err(ErrorsPositiveRealScalar::ZeroValue { .. }) => {
298///         println!("Zero is not strictly positive (x > 0 required)");
299///     }
300///     _ => unreachable!(),
301/// }
302///
303/// // Negative values rejected with the value included in the error
304/// match PositiveRealScalar::try_new(-2.5_f64) {
305///     Err(ErrorsPositiveRealScalar::NegativeValue { value, .. }) => {
306///         assert_eq!(value, -2.5);
307///     }
308///     _ => unreachable!(),
309/// }
310/// ```
311#[derive(Debug, Error)]
312pub enum ErrorsPositiveRealScalar<RealType: RealScalar> {
313    /// The input value was negative (< 0).
314    #[error("Negative value detected: {value}")]
315    NegativeValue {
316        /// The specific negative value that was rejected.
317        value: RealType,
318        /// Stack trace for debugging.
319        backtrace: Backtrace,
320    },
321
322    /// The input value was exactly zero (not strictly positive).
323    ///
324    /// Zero is NOT positive in mathematical terms. The positive real numbers
325    /// are defined as `ℝ⁺ = {x ∈ ℝ : x > 0}`, which excludes zero.
326    #[error("Zero value detected")]
327    ZeroValue {
328        /// Stack trace for debugging.
329        backtrace: Backtrace,
330    },
331}
332
333/// Error type for [`NonNegativeRealScalar<T>`] validation failures.
334///
335/// This enum provides error information when attempting to construct a
336/// [`NonNegativeRealScalar<T>`] with a negative value.
337///
338/// # Error Variants
339///
340/// - [`ErrorsNonNegativeRealScalar::NegativeValue`]: The input was negative (< 0).
341///
342/// Note that zero is **valid** for [`NonNegativeRealScalar`], unlike [`PositiveRealScalar`].
343///
344/// # Examples
345///
346/// ```rust
347/// use num_valid::scalars::{NonNegativeRealScalar, ErrorsNonNegativeRealScalar};
348/// use try_create::TryNew;
349///
350/// // Zero is valid for NonNegativeRealScalar
351/// let zero = NonNegativeRealScalar::try_new(0.0_f64);
352/// assert!(zero.is_ok());
353///
354/// // Negative values are rejected
355/// match NonNegativeRealScalar::try_new(-1.0_f64) {
356///     Err(ErrorsNonNegativeRealScalar::NegativeValue { value, .. }) => {
357///         assert_eq!(value, -1.0);
358///     }
359///     _ => unreachable!(),
360/// }
361/// ```
362#[derive(Debug, Error)]
363pub enum ErrorsNonNegativeRealScalar<RealType: RealScalar> {
364    /// The input value was negative (must be ≥ 0).
365    #[error("Negative value: {value} (must be non-negative, i.e., ≥ 0)")]
366    NegativeValue {
367        /// The negative value that was rejected.
368        value: RealType,
369        /// Stack trace for debugging.
370        backtrace: Backtrace,
371    },
372}
373
374//------------------------------------------------------------------------------------------------------------
375// AbsoluteTolerance
376//------------------------------------------------------------------------------------------------------------
377
378/// Type-safe wrapper for absolute tolerance values.
379///
380/// [`AbsoluteTolerance<T>`] represents a non-negative (≥ 0) tolerance value used for
381/// absolute error comparisons. It ensures that the tolerance is always valid by
382/// rejecting negative values at construction time.
383///
384/// # Mathematical Definition
385///
386/// An absolute tolerance `ε` is used in comparisons like:
387/// ```text
388/// |a - b| ≤ ε
389/// ```
390///
391/// Since `ε` represents a distance or error bound, it must be non-negative.
392///
393/// # Type Parameters
394///
395/// - `RealType`: The underlying real scalar type (e.g., `f64`, [`RealNative64StrictFinite`](crate::RealNative64StrictFinite))
396///
397/// # Examples
398///
399/// ## Basic Usage
400///
401/// ```rust
402/// use num_valid::scalars::AbsoluteTolerance;
403/// use try_create::TryNew;
404///
405/// // Create from f64
406/// let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
407/// assert_eq!(*tol.as_ref(), 1e-10);
408///
409/// // Use convenience constructors
410/// let zero = AbsoluteTolerance::<f64>::zero();
411/// let eps = AbsoluteTolerance::<f64>::epsilon();
412/// ```
413///
414/// ## In Numerical Comparisons
415///
416/// ```rust
417/// use num_valid::scalars::AbsoluteTolerance;
418/// use num_valid::RealScalar;
419/// use try_create::TryNew;
420///
421/// fn approximately_equal<T: RealScalar + Clone>(a: T, b: T, tol: &AbsoluteTolerance<T>) -> bool {
422///     let diff = (a - b).abs();
423///     &diff <= tol.as_ref()
424/// }
425///
426/// let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
427/// assert!(approximately_equal(1.0, 1.0 + 1e-11, &tol));
428/// assert!(!approximately_equal(1.0, 1.0 + 1e-9, &tol));
429/// ```
430///
431/// # Zero-Cost Abstraction
432///
433/// This type uses `#[repr(transparent)]` and has zero runtime overhead beyond
434/// the initial validation at construction time.
435#[derive(
436    Debug, Clone, PartialEq, PartialOrd, AsRef, IntoInner, Display, LowerExp, Serialize, Deserialize,
437)]
438#[repr(transparent)]
439pub struct AbsoluteTolerance<RealType>(RealType);
440
441impl<RealType: RealScalar> AbsoluteTolerance<RealType> {
442    /// Creates an absolute tolerance of zero.
443    ///
444    /// A zero tolerance represents an exact comparison (no error allowed).
445    ///
446    /// # Examples
447    ///
448    /// ```rust
449    /// use num_valid::scalars::AbsoluteTolerance;
450    ///
451    /// let zero_tol = AbsoluteTolerance::<f64>::zero();
452    /// assert_eq!(*zero_tol.as_ref(), 0.0);
453    /// ```
454    #[inline(always)]
455    pub fn zero() -> Self {
456        AbsoluteTolerance(RealType::zero())
457    }
458
459    /// Creates an absolute tolerance equal to machine epsilon.
460    ///
461    /// Machine epsilon is the smallest value such that `1.0 + epsilon != 1.0`.
462    /// This is often a good default tolerance for floating-point comparisons.
463    ///
464    /// # Examples
465    ///
466    /// ```rust
467    /// use num_valid::scalars::AbsoluteTolerance;
468    ///
469    /// let eps_tol = AbsoluteTolerance::<f64>::epsilon();
470    /// assert_eq!(*eps_tol.as_ref(), f64::EPSILON);
471    /// ```
472    #[inline(always)]
473    pub fn epsilon() -> Self {
474        AbsoluteTolerance(RealType::epsilon())
475    }
476}
477
478impl<RealType: RealScalar> TryNew for AbsoluteTolerance<RealType> {
479    type Error = ErrorsTolerance<RealType>;
480
481    /// Attempts to create an [`AbsoluteTolerance`] from a value.
482    ///
483    /// # Errors
484    ///
485    /// Returns [`ErrorsTolerance::NegativeValue`] if the input is negative.
486    ///
487    /// # Panics (Debug Mode Only)
488    ///
489    /// In debug builds, panics if the input is not finite (NaN or infinity).
490    ///
491    /// # Examples
492    ///
493    /// ```rust
494    /// use num_valid::scalars::{AbsoluteTolerance, ErrorsTolerance};
495    /// use try_create::TryNew;
496    ///
497    /// // Valid tolerances
498    /// assert!(AbsoluteTolerance::try_new(1e-10_f64).is_ok());
499    /// assert!(AbsoluteTolerance::try_new(0.0_f64).is_ok());
500    ///
501    /// // Invalid (negative)
502    /// assert!(matches!(
503    ///     AbsoluteTolerance::try_new(-1e-10_f64),
504    ///     Err(ErrorsTolerance::NegativeValue { .. })
505    /// ));
506    /// ```
507    fn try_new(value: RealType) -> Result<Self, Self::Error> {
508        debug_assert!(value.is_finite(), "The input value {value} is not finite!");
509        if value.kernel_is_sign_negative() {
510            Err(ErrorsTolerance::NegativeValue {
511                value,
512                backtrace: capture_backtrace(),
513            })
514        } else {
515            Ok(Self(value))
516        }
517    }
518}
519
520//------------------------------------------------------------------------------------------------------------
521// RelativeTolerance
522//------------------------------------------------------------------------------------------------------------
523
524/// Type-safe wrapper for relative tolerance values.
525///
526/// [`RelativeTolerance<T>`] represents a non-negative (≥ 0) tolerance value used for
527/// relative (proportional) error comparisons. It can be converted to an absolute
528/// tolerance based on a reference value.
529///
530/// # Mathematical Definition
531///
532/// A relative tolerance `ε_rel` is used in comparisons like:
533/// ```text
534/// |a - b| ≤ ε_rel × |reference|
535/// ```
536///
537/// Common relative tolerances:
538/// - `0.01` = 1% tolerance
539/// - `0.001` = 0.1% tolerance
540/// - `1e-6` = one part per million
541///
542/// # Examples
543///
544/// ## Basic Usage
545///
546/// ```rust
547/// use num_valid::scalars::RelativeTolerance;
548/// use try_create::TryNew;
549///
550/// let one_percent = RelativeTolerance::try_new(0.01_f64).unwrap();
551/// assert_eq!(*one_percent.as_ref(), 0.01);
552/// ```
553///
554/// ## Converting to Absolute Tolerance
555///
556/// ```rust
557/// use num_valid::scalars::RelativeTolerance;
558/// use try_create::TryNew;
559///
560/// let rel_tol = RelativeTolerance::try_new(0.01_f64).unwrap(); // 1%
561///
562/// // Convert based on reference value
563/// let abs_tol = rel_tol.absolute_tolerance(1000.0);
564/// assert_eq!(*abs_tol.as_ref(), 10.0); // 1% of 1000 = 10
565///
566/// // Works with negative references (uses absolute value)
567/// let abs_tol_neg = rel_tol.absolute_tolerance(-500.0);
568/// assert_eq!(*abs_tol_neg.as_ref(), 5.0); // 1% of |-500| = 5
569/// ```
570#[derive(
571    Debug, Clone, PartialEq, PartialOrd, AsRef, IntoInner, Display, LowerExp, Serialize, Deserialize,
572)]
573#[repr(transparent)]
574pub struct RelativeTolerance<RealType>(RealType);
575
576impl<RealType: RealScalar> RelativeTolerance<RealType> {
577    /// Creates a relative tolerance of zero.
578    ///
579    /// A zero relative tolerance represents an exact comparison.
580    ///
581    /// # Examples
582    ///
583    /// ```rust
584    /// use num_valid::scalars::RelativeTolerance;
585    ///
586    /// let zero_tol = RelativeTolerance::<f64>::zero();
587    /// assert_eq!(*zero_tol.as_ref(), 0.0);
588    /// ```
589    #[inline(always)]
590    pub fn zero() -> Self {
591        RelativeTolerance(RealType::zero())
592    }
593
594    /// Creates a relative tolerance equal to machine epsilon.
595    ///
596    /// # Examples
597    ///
598    /// ```rust
599    /// use num_valid::scalars::RelativeTolerance;
600    ///
601    /// let eps_tol = RelativeTolerance::<f64>::epsilon();
602    /// assert_eq!(*eps_tol.as_ref(), f64::EPSILON);
603    /// ```
604    #[inline(always)]
605    pub fn epsilon() -> Self {
606        RelativeTolerance(RealType::epsilon())
607    }
608
609    /// Converts this relative tolerance to an absolute tolerance based on a reference value.
610    ///
611    /// The absolute tolerance is computed as:
612    /// ```text
613    /// absolute_tolerance = relative_tolerance × |reference|
614    /// ```
615    ///
616    /// # Parameters
617    ///
618    /// - `reference`: The reference value to scale by. The absolute value is used.
619    ///
620    /// # Examples
621    ///
622    /// ```rust
623    /// use num_valid::scalars::RelativeTolerance;
624    /// use try_create::TryNew;
625    ///
626    /// let rel_tol = RelativeTolerance::try_new(0.1_f64).unwrap(); // 10%
627    ///
628    /// // 10% of 100 = 10
629    /// let abs_tol = rel_tol.absolute_tolerance(100.0);
630    /// assert_eq!(*abs_tol.as_ref(), 10.0);
631    ///
632    /// // 10% of |-50| = 5
633    /// let abs_tol_neg = rel_tol.absolute_tolerance(-50.0);
634    /// assert_eq!(*abs_tol_neg.as_ref(), 5.0);
635    ///
636    /// // 10% of 0 = 0
637    /// let abs_tol_zero = rel_tol.absolute_tolerance(0.0);
638    /// assert_eq!(*abs_tol_zero.as_ref(), 0.0);
639    /// ```
640    #[inline(always)]
641    pub fn absolute_tolerance(&self, reference: RealType) -> AbsoluteTolerance<RealType> {
642        let abs_tol = reference.abs() * &self.0;
643        AbsoluteTolerance(abs_tol)
644    }
645}
646
647impl<RealType: RealScalar> TryNew for RelativeTolerance<RealType> {
648    type Error = ErrorsTolerance<RealType>;
649
650    /// Attempts to create a [`RelativeTolerance`] from a value.
651    ///
652    /// # Errors
653    ///
654    /// Returns [`ErrorsTolerance::NegativeValue`] if the input is negative.
655    ///
656    /// # Panics (Debug Mode Only)
657    ///
658    /// In debug builds, panics if the input is not finite (NaN or infinity).
659    fn try_new(value: RealType) -> Result<Self, Self::Error> {
660        debug_assert!(value.is_finite(), "The input value {value} is not finite!");
661        if value.kernel_is_sign_negative() {
662            Err(ErrorsTolerance::NegativeValue {
663                value,
664                backtrace: capture_backtrace(),
665            })
666        } else {
667            Ok(Self(value))
668        }
669    }
670}
671
672//------------------------------------------------------------------------------------------------------------
673// PositiveRealScalar
674//------------------------------------------------------------------------------------------------------------
675
676/// Type-safe wrapper for strictly positive real scalar values.
677///
678/// [`PositiveRealScalar<T>`] guarantees that wrapped values are strictly greater than zero.
679/// Zero is **not** valid for this type. For values that can be zero, use [`NonNegativeRealScalar`].
680///
681/// # Mathematical Definition
682///
683/// ```text
684/// PositiveRealScalar<T> = { x ∈ T : x > 0 }
685/// ```
686///
687/// This is the set of positive real numbers `ℝ⁺`, which **excludes** zero.
688///
689/// # Use Cases
690///
691/// - Lengths and distances that cannot be zero
692/// - Positive tolerances
693/// - Scaling factors that must be positive
694/// - Any quantity that is mathematically required to be > 0
695///
696/// # Examples
697///
698/// ## Basic Usage
699///
700/// ```rust
701/// use num_valid::scalars::{PositiveRealScalar, ErrorsPositiveRealScalar};
702/// use try_create::TryNew;
703///
704/// // Valid positive values
705/// let length = PositiveRealScalar::try_new(2.5_f64).unwrap();
706/// let tiny = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
707///
708/// // Zero is NOT positive
709/// assert!(matches!(
710///     PositiveRealScalar::try_new(0.0_f64),
711///     Err(ErrorsPositiveRealScalar::ZeroValue { .. })
712/// ));
713///
714/// // Negative values rejected
715/// assert!(matches!(
716///     PositiveRealScalar::try_new(-1.0_f64),
717///     Err(ErrorsPositiveRealScalar::NegativeValue { .. })
718/// ));
719/// ```
720///
721/// ## Difference from [`NonNegativeRealScalar`]
722///
723/// ```rust
724/// use num_valid::scalars::{PositiveRealScalar, NonNegativeRealScalar};
725/// use try_create::TryNew;
726///
727/// // Zero is INVALID for PositiveRealScalar (x > 0)
728/// assert!(PositiveRealScalar::try_new(0.0_f64).is_err());
729///
730/// // Zero is VALID for NonNegativeRealScalar (x ≥ 0)
731/// assert!(NonNegativeRealScalar::try_new(0.0_f64).is_ok());
732/// ```
733#[derive(
734    Debug, Clone, PartialEq, PartialOrd, AsRef, IntoInner, Display, Serialize, Deserialize,
735)]
736#[repr(transparent)]
737#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
738pub struct PositiveRealScalar<RealType: RealScalar>(RealType);
739
740impl<RealType: RealScalar> TryNew for PositiveRealScalar<RealType> {
741    type Error = ErrorsPositiveRealScalar<RealType>;
742
743    /// Attempts to create a [`PositiveRealScalar`] from a value.
744    ///
745    /// # Errors
746    ///
747    /// - [`ErrorsPositiveRealScalar::NegativeValue`]: If the input is negative (< 0).
748    /// - [`ErrorsPositiveRealScalar::ZeroValue`]: If the input is zero.
749    ///
750    /// # Panics (Debug Mode Only)
751    ///
752    /// In debug builds, panics if the input is not finite (NaN or infinity).
753    ///
754    /// # Examples
755    ///
756    /// ```rust
757    /// use num_valid::scalars::{PositiveRealScalar, ErrorsPositiveRealScalar};
758    /// use try_create::TryNew;
759    ///
760    /// // Positive value succeeds
761    /// assert!(PositiveRealScalar::try_new(1.0_f64).is_ok());
762    ///
763    /// // Zero fails
764    /// assert!(matches!(
765    ///     PositiveRealScalar::try_new(0.0_f64),
766    ///     Err(ErrorsPositiveRealScalar::ZeroValue { .. })
767    /// ));
768    ///
769    /// // Negative fails
770    /// assert!(matches!(
771    ///     PositiveRealScalar::try_new(-1.0_f64),
772    ///     Err(ErrorsPositiveRealScalar::NegativeValue { value: v, .. }) if v == -1.0
773    /// ));
774    /// ```
775    fn try_new(value: RealType) -> Result<Self, Self::Error> {
776        debug_assert!(value.is_finite(), "The input value {value} is not finite!");
777        if value.kernel_is_sign_negative() {
778            Err(ErrorsPositiveRealScalar::NegativeValue {
779                value,
780                backtrace: capture_backtrace(),
781            })
782        } else if value.is_zero() {
783            Err(ErrorsPositiveRealScalar::ZeroValue {
784                backtrace: capture_backtrace(),
785            })
786        } else {
787            Ok(Self(value))
788        }
789    }
790}
791
792//------------------------------------------------------------------------------------------------------------
793// NonNegativeRealScalar
794//------------------------------------------------------------------------------------------------------------
795
796/// Type-safe wrapper for non-negative real scalar values.
797///
798/// [`NonNegativeRealScalar<T>`] guarantees that wrapped values are greater than or equal to zero.
799/// Zero **is** valid for this type. For values that must be strictly positive, use [`PositiveRealScalar`].
800///
801/// # Mathematical Definition
802///
803/// ```text
804/// NonNegativeRealScalar<T> = { x ∈ T : x ≥ 0 }
805/// ```
806///
807/// This is the set of non-negative real numbers `ℝ₀⁺`, which **includes** zero.
808///
809/// # Use Cases
810///
811/// - Distances (can be zero when comparing a value to itself)
812/// - Absolute values
813/// - Magnitudes
814/// - Any quantity that is mathematically required to be ≥ 0
815///
816/// # Examples
817///
818/// ## Basic Usage
819///
820/// ```rust
821/// use num_valid::scalars::{NonNegativeRealScalar, ErrorsNonNegativeRealScalar};
822/// use try_create::TryNew;
823///
824/// // Zero is valid
825/// let zero = NonNegativeRealScalar::try_new(0.0_f64).unwrap();
826/// assert_eq!(*zero.as_ref(), 0.0);
827///
828/// // Positive values are valid
829/// let positive = NonNegativeRealScalar::try_new(5.0_f64).unwrap();
830///
831/// // Negative values are rejected
832/// assert!(matches!(
833///     NonNegativeRealScalar::try_new(-1.0_f64),
834///     Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
835/// ));
836/// ```
837///
838/// ## Use Case: Computing Distances
839///
840/// ```rust
841/// use num_valid::scalars::NonNegativeRealScalar;
842/// use try_create::TryNew;
843///
844/// fn distance(a: f64, b: f64) -> NonNegativeRealScalar<f64> {
845///     NonNegativeRealScalar::try_new((a - b).abs()).unwrap()
846/// }
847///
848/// // Different points
849/// let d1 = distance(0.0, 5.0);
850/// assert_eq!(*d1.as_ref(), 5.0);
851///
852/// // Same point (zero distance is valid!)
853/// let d2 = distance(3.0, 3.0);
854/// assert_eq!(*d2.as_ref(), 0.0);
855/// ```
856///
857/// ## Algebraic and Ordering Operations
858///
859/// [`NonNegativeRealScalar`] supports additive composition, the [`Zero`] trait,
860/// and ordering helpers from [`Max`] and [`Min`]:
861///
862/// ```rust
863/// use num::Zero;
864/// use num_valid::{
865///     functions::{Max, Min},
866///     scalars::NonNegativeRealScalar,
867/// };
868/// use try_create::TryNew;
869///
870/// let a = NonNegativeRealScalar::try_new(1.25_f64).unwrap();
871/// let b = NonNegativeRealScalar::try_new(2.75_f64).unwrap();
872///
873/// // Add preserves non-negativity for valid operands.
874/// let sum = a + b;
875/// assert_eq!(*sum.as_ref(), 4.0);
876///
877/// // Zero is the additive identity.
878/// let z = NonNegativeRealScalar::<f64>::zero();
879/// assert!(z.is_zero());
880/// assert_eq!(*(sum + z).as_ref(), 4.0);
881///
882/// let x = NonNegativeRealScalar::try_new(1.0_f64).unwrap();
883/// let y = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
884/// assert_eq!(*Max::max_by_ref(&x, &y).as_ref(), 3.0);
885/// assert_eq!(*Min::min_by_value(x, y).as_ref(), 1.0);
886/// ```
887#[derive(
888    Debug, Clone, PartialEq, PartialOrd, AsRef, IntoInner, Display, Serialize, Deserialize,
889)]
890#[repr(transparent)]
891#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
892pub struct NonNegativeRealScalar<RealType: RealScalar>(RealType);
893
894impl<RealType: RealScalar> TryNew for NonNegativeRealScalar<RealType> {
895    type Error = ErrorsNonNegativeRealScalar<RealType>;
896
897    /// Attempts to create a [`NonNegativeRealScalar`] from a value.
898    ///
899    /// # Errors
900    ///
901    /// Returns [`ErrorsNonNegativeRealScalar::NegativeValue`] if the input is negative.
902    ///
903    /// # Panics (Debug Mode Only)
904    ///
905    /// In debug builds, panics if the input is not finite (NaN or infinity).
906    ///
907    /// # Examples
908    ///
909    /// ```rust
910    /// use num_valid::scalars::{NonNegativeRealScalar, ErrorsNonNegativeRealScalar};
911    /// use try_create::TryNew;
912    ///
913    /// // Zero is valid
914    /// assert!(NonNegativeRealScalar::try_new(0.0_f64).is_ok());
915    ///
916    /// // Positive is valid
917    /// assert!(NonNegativeRealScalar::try_new(1.0_f64).is_ok());
918    ///
919    /// // Negative is rejected
920    /// assert!(matches!(
921    ///     NonNegativeRealScalar::try_new(-1.0_f64),
922    ///     Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
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(ErrorsNonNegativeRealScalar::NegativeValue {
929                value,
930                backtrace: capture_backtrace(),
931            })
932        } else {
933            Ok(Self(value))
934        }
935    }
936}
937
938/// Implements additive composition for [`NonNegativeRealScalar`].
939///
940/// Since both operands are guaranteed to be non-negative, their sum is also non-negative.
941impl<RealType: RealScalar> Add for NonNegativeRealScalar<RealType> {
942    type Output = Self;
943
944    fn add(self, other: Self) -> Self {
945        NonNegativeRealScalar(self.0 + other.0)
946    }
947}
948
949/// Implements [`Zero`] for [`NonNegativeRealScalar`].
950///
951/// The zero value is always valid, and acts as the additive identity.
952impl<RealType: RealScalar> Zero for NonNegativeRealScalar<RealType> {
953    fn is_zero(&self) -> bool {
954        self.0.is_zero()
955    }
956
957    fn zero() -> Self {
958        NonNegativeRealScalar(RealType::zero())
959    }
960}
961
962/// Implements [`Max`] for [`NonNegativeRealScalar`].
963///
964/// Maximum selection is delegated to the default trait behavior based on [`PartialOrd`].
965impl<RealType: RealScalar> Max for NonNegativeRealScalar<RealType> {}
966/// Implements [`Min`] for [`NonNegativeRealScalar`].
967///
968/// Minimum selection is delegated to the default trait behavior based on [`PartialOrd`].
969impl<RealType: RealScalar> Min for NonNegativeRealScalar<RealType> {}
970
971//------------------------------------------------------------------------------------------------------------
972// Tests
973//------------------------------------------------------------------------------------------------------------
974
975#[cfg(test)]
976mod tests {
977    use super::*;
978    use crate::backends::native64::validated::RealNative64StrictFinite;
979
980    mod absolute_tolerance {
981        use super::*;
982
983        #[test]
984        fn try_new_valid() {
985            let tol = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
986            assert_eq!(*tol.as_ref(), 1e-10);
987        }
988
989        #[test]
990        fn try_new_zero() {
991            let tol = AbsoluteTolerance::try_new(0.0_f64).unwrap();
992            assert_eq!(*tol.as_ref(), 0.0);
993        }
994
995        #[test]
996        fn try_new_negative() {
997            let result = AbsoluteTolerance::try_new(-1e-10_f64);
998            assert!(
999                matches!(result, Err(ErrorsTolerance::NegativeValue { value, .. }) if value == -1e-10)
1000            );
1001        }
1002
1003        #[test]
1004        fn zero_constructor() {
1005            let tol = AbsoluteTolerance::<f64>::zero();
1006            assert_eq!(*tol.as_ref(), 0.0);
1007        }
1008
1009        #[test]
1010        fn epsilon_constructor() {
1011            let tol = AbsoluteTolerance::<f64>::epsilon();
1012            assert_eq!(*tol.as_ref(), f64::EPSILON);
1013        }
1014
1015        #[test]
1016        fn display_trait() {
1017            let tol = AbsoluteTolerance::try_new(0.5_f64).unwrap();
1018            assert_eq!(format!("{}", tol), "0.5");
1019        }
1020
1021        #[test]
1022        fn lower_exp_trait() {
1023            let tol = AbsoluteTolerance::try_new(0.00001_f64).unwrap();
1024            assert_eq!(format!("{:e}", tol), "1e-5");
1025        }
1026
1027        #[test]
1028        fn into_inner() {
1029            let tol = AbsoluteTolerance::try_new(0.5_f64).unwrap();
1030            let inner = tol.into_inner();
1031            assert_eq!(inner, 0.5);
1032        }
1033
1034        #[test]
1035        fn clone_and_partial_eq() {
1036            let tol1 = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
1037            let tol2 = tol1.clone();
1038            assert_eq!(tol1, tol2);
1039        }
1040
1041        #[test]
1042        fn partial_ord() {
1043            let tol1 = AbsoluteTolerance::try_new(1e-10_f64).unwrap();
1044            let tol2 = AbsoluteTolerance::try_new(1e-9_f64).unwrap();
1045            assert!(tol1 < tol2);
1046        }
1047
1048        #[test]
1049        fn serialize_deserialize() {
1050            let tol = AbsoluteTolerance::try_new(0.5_f64).unwrap();
1051            let serialized = serde_json::to_string(&tol).unwrap();
1052            let deserialized: AbsoluteTolerance<f64> = serde_json::from_str(&serialized).unwrap();
1053            assert_eq!(tol, deserialized);
1054        }
1055
1056        #[test]
1057        fn with_validated_type() {
1058            let val = RealNative64StrictFinite::try_from_f64(1e-10).unwrap();
1059            let tol = AbsoluteTolerance::try_new(val).unwrap();
1060            assert_eq!(*tol.as_ref().as_ref(), 1e-10);
1061        }
1062
1063        #[test]
1064        #[cfg(debug_assertions)]
1065        #[should_panic(expected = "is not finite")]
1066        fn debug_panics_on_nan() {
1067            let _ = AbsoluteTolerance::try_new(f64::NAN);
1068        }
1069
1070        #[test]
1071        #[cfg(debug_assertions)]
1072        #[should_panic(expected = "is not finite")]
1073        fn debug_panics_on_infinity() {
1074            let _ = AbsoluteTolerance::try_new(f64::INFINITY);
1075        }
1076
1077        #[test]
1078        fn try_new() {
1079            // Test creating an AbsoluteTolerance instance with a value of 0.0
1080            let v = AbsoluteTolerance::<f64>::zero();
1081            assert_eq!(*v.as_ref(), 0.0);
1082
1083            // Test creating an AbsoluteTolerance instance with a positive value
1084            let v = AbsoluteTolerance::try_new(2.0).unwrap();
1085            assert_eq!(*v.as_ref(), 2.0);
1086        }
1087
1088        #[test]
1089        fn try_new_invalid_negative() {
1090            // Test creating an invalid AbsoluteTolerance instance with a negative value
1091            let tol = AbsoluteTolerance::try_new(-0.1);
1092            assert!(matches!(tol, Err(ErrorsTolerance::NegativeValue { .. })));
1093        }
1094
1095        #[test]
1096        #[cfg(debug_assertions)]
1097        #[should_panic = "The input value inf is not finite!"]
1098        fn try_new_invalid_infinite() {
1099            // Test creating an invalid AbsoluteTolerance instance with an infinite value
1100            let _tol = AbsoluteTolerance::try_new(f64::INFINITY);
1101        }
1102
1103        #[test]
1104        #[cfg(debug_assertions)]
1105        #[should_panic = "The input value NaN is not finite!"]
1106        fn try_new_invalid_nan() {
1107            // Test creating an invalid AbsoluteTolerance instance with a NaN value
1108            let _tol = AbsoluteTolerance::try_new(f64::NAN);
1109        }
1110
1111        #[test]
1112        fn epsilon() {
1113            // Test creating an AbsoluteTolerance instance with epsilon value
1114            let epsilon_tol = AbsoluteTolerance::<f64>::epsilon();
1115            assert_eq!(*epsilon_tol.as_ref(), f64::EPSILON);
1116        }
1117
1118        #[test]
1119        fn equality_operator() {
1120            // Test equality operator for AbsoluteTolerance instances
1121            let a = AbsoluteTolerance::<f64>::zero();
1122            let b = AbsoluteTolerance::<f64>::zero();
1123            assert!(a == b);
1124
1125            let c = AbsoluteTolerance::try_new(1.0).unwrap();
1126            let d = AbsoluteTolerance::try_new(1.0).unwrap();
1127            assert!(c == d);
1128        }
1129
1130        #[test]
1131        fn inequality_operator() {
1132            // Test inequality operator for AbsoluteTolerance instances
1133            let a = AbsoluteTolerance::<f64>::zero();
1134            let b = AbsoluteTolerance::try_new(1.0).unwrap();
1135            assert!(a != b);
1136
1137            let c = AbsoluteTolerance::try_new(1.0).unwrap();
1138            let d = AbsoluteTolerance::try_new(2.0).unwrap();
1139            assert!(c != d);
1140        }
1141
1142        #[test]
1143        fn debug_trait() {
1144            // Test the Debug trait
1145            let tolerance = AbsoluteTolerance::try_new(0.5).unwrap();
1146            assert_eq!(format!("{:?}", tolerance), "AbsoluteTolerance(0.5)");
1147        }
1148
1149        #[test]
1150        fn clone_trait() {
1151            // Test the Clone trait
1152            let tolerance = AbsoluteTolerance::try_new(0.5).unwrap();
1153            let cloned_tolerance = tolerance.clone();
1154            assert_eq!(tolerance, cloned_tolerance);
1155        }
1156
1157        #[test]
1158        fn partial_eq_trait() {
1159            // Test the PartialEq trait
1160            let tolerance1 = AbsoluteTolerance::try_new(0.5).unwrap();
1161            let tolerance2 = AbsoluteTolerance::try_new(0.5).unwrap();
1162            assert_eq!(tolerance1, tolerance2);
1163        }
1164
1165        #[test]
1166        fn partial_ord_trait() {
1167            // Test the PartialOrd trait
1168            let tolerance1 = AbsoluteTolerance::try_new(0.5).unwrap();
1169            let tolerance2 = AbsoluteTolerance::try_new(1.0).unwrap();
1170            assert!(tolerance1 < tolerance2);
1171        }
1172
1173        #[test]
1174        fn as_ref_trait() {
1175            // Test the AsRef trait
1176            let tolerance = AbsoluteTolerance::try_new(0.5).unwrap();
1177            let inner: &f64 = tolerance.as_ref();
1178            assert_eq!(*inner, 0.5);
1179        }
1180
1181        #[test]
1182        fn zero() {
1183            // Test the zero constructor
1184            let zero_tol = AbsoluteTolerance::<f64>::zero();
1185            assert_eq!(*zero_tol.as_ref(), 0.0);
1186        }
1187
1188        #[test]
1189        fn edge_cases() {
1190            // Test with very small positive values
1191            let tiny_tol = AbsoluteTolerance::try_new(f64::MIN_POSITIVE).unwrap();
1192            assert_eq!(*tiny_tol.as_ref(), f64::MIN_POSITIVE);
1193
1194            // Test with large positive values
1195            let large_tol = AbsoluteTolerance::try_new(1e100).unwrap();
1196            assert_eq!(*large_tol.as_ref(), 1e100);
1197        }
1198    }
1199
1200    mod relative_tolerance {
1201        use super::*;
1202
1203        #[test]
1204        fn try_new_valid() {
1205            let tol = RelativeTolerance::try_new(0.01_f64).unwrap();
1206            assert_eq!(*tol.as_ref(), 0.01);
1207        }
1208
1209        #[test]
1210        fn try_new_zero() {
1211            let tol = RelativeTolerance::try_new(0.0_f64).unwrap();
1212            assert_eq!(*tol.as_ref(), 0.0);
1213        }
1214
1215        #[test]
1216        fn try_new_negative() {
1217            let result = RelativeTolerance::try_new(-0.01_f64);
1218            assert!(matches!(result, Err(ErrorsTolerance::NegativeValue { .. })));
1219        }
1220
1221        #[test]
1222        fn zero_constructor() {
1223            let tol = RelativeTolerance::<f64>::zero();
1224            assert_eq!(*tol.as_ref(), 0.0);
1225        }
1226
1227        #[test]
1228        fn epsilon_constructor() {
1229            let tol = RelativeTolerance::<f64>::epsilon();
1230            assert_eq!(*tol.as_ref(), f64::EPSILON);
1231        }
1232
1233        #[test]
1234        fn absolute_tolerance_positive_reference() {
1235            let rel_tol = RelativeTolerance::try_new(0.1_f64).unwrap();
1236            let abs_tol = rel_tol.absolute_tolerance(100.0);
1237            assert_eq!(*abs_tol.as_ref(), 10.0);
1238        }
1239
1240        #[test]
1241        fn absolute_tolerance_negative_reference() {
1242            let rel_tol = RelativeTolerance::try_new(0.1_f64).unwrap();
1243            let abs_tol = rel_tol.absolute_tolerance(-50.0);
1244            assert_eq!(*abs_tol.as_ref(), 5.0);
1245        }
1246
1247        #[test]
1248        fn absolute_tolerance_zero_reference() {
1249            let rel_tol = RelativeTolerance::try_new(0.1_f64).unwrap();
1250            let abs_tol = rel_tol.absolute_tolerance(0.0);
1251            assert_eq!(*abs_tol.as_ref(), 0.0);
1252        }
1253
1254        #[test]
1255        fn display_trait() {
1256            let tol = RelativeTolerance::try_new(0.5_f64).unwrap();
1257            assert_eq!(format!("{}", tol), "0.5");
1258        }
1259
1260        #[test]
1261        fn into_inner() {
1262            let tol = RelativeTolerance::try_new(0.01_f64).unwrap();
1263            let inner = tol.into_inner();
1264            assert_eq!(inner, 0.01);
1265        }
1266
1267        #[test]
1268        fn serialize_deserialize() {
1269            let tol = RelativeTolerance::try_new(0.01_f64).unwrap();
1270            let serialized = serde_json::to_string(&tol).unwrap();
1271            let deserialized: RelativeTolerance<f64> = serde_json::from_str(&serialized).unwrap();
1272            assert_eq!(tol, deserialized);
1273        }
1274
1275        #[test]
1276        fn try_new() {
1277            // Test creating a RelativeTolerance instance with a value of 0.0
1278            let v = RelativeTolerance::<f64>::zero();
1279            assert_eq!(*v.as_ref(), 0.0);
1280
1281            // Test creating a RelativeTolerance instance with a positive value
1282            let v = RelativeTolerance::try_new(2.0).unwrap();
1283            assert_eq!(*v.as_ref(), 2.0);
1284        }
1285
1286        #[test]
1287        fn try_new_invalid_negative() {
1288            // Test creating an invalid RelativeTolerance instance with a negative value
1289            let tol = RelativeTolerance::try_new(-0.1);
1290            assert!(matches!(tol, Err(ErrorsTolerance::NegativeValue { .. })));
1291        }
1292
1293        #[test]
1294        #[cfg(debug_assertions)]
1295        #[should_panic = "The input value inf is not finite!"]
1296        fn try_new_invalid_infinite() {
1297            // Test creating an invalid RelativeTolerance instance with an infinite value
1298            let _tol = RelativeTolerance::try_new(f64::INFINITY);
1299        }
1300
1301        #[test]
1302        #[cfg(debug_assertions)]
1303        #[should_panic = "The input value NaN is not finite!"]
1304        fn try_new_invalid_nan() {
1305            // Test creating an invalid RelativeTolerance instance with a NaN value
1306            let _tol = RelativeTolerance::try_new(f64::NAN);
1307        }
1308
1309        #[test]
1310        fn epsilon() {
1311            // Test creating a RelativeTolerance instance with epsilon value
1312            let epsilon_tol = RelativeTolerance::<f64>::epsilon();
1313            assert_eq!(*epsilon_tol.as_ref(), f64::EPSILON);
1314        }
1315
1316        #[test]
1317        fn absolute_tolerance() {
1318            // Test converting relative tolerance to absolute tolerance
1319            let rel_tol = RelativeTolerance::try_new(0.1).unwrap();
1320            let reference = 10.0;
1321            let abs_tol = rel_tol.absolute_tolerance(reference);
1322            assert_eq!(*abs_tol.as_ref(), 1.0); // 0.1 * |10.0| = 1.0
1323
1324            // Test with negative reference value
1325            let reference = -5.0;
1326            let abs_tol = rel_tol.absolute_tolerance(reference);
1327            assert_eq!(*abs_tol.as_ref(), 0.5); // 0.1 * |-5.0| = 0.5
1328
1329            // Test with zero reference value
1330            let reference = 0.0;
1331            let abs_tol = rel_tol.absolute_tolerance(reference);
1332            assert_eq!(*abs_tol.as_ref(), 0.0); // 0.1 * |0.0| = 0.0
1333        }
1334
1335        #[test]
1336        fn equality_operator() {
1337            // Test equality operator for RelativeTolerance instances
1338            let a = RelativeTolerance::<f64>::zero();
1339            let b = RelativeTolerance::<f64>::zero();
1340            assert!(a == b);
1341
1342            let c = RelativeTolerance::try_new(1.0).unwrap();
1343            let d = RelativeTolerance::try_new(1.0).unwrap();
1344            assert!(c == d);
1345        }
1346
1347        #[test]
1348        fn inequality_operator() {
1349            // Test inequality operator for RelativeTolerance instances
1350            let a = RelativeTolerance::<f64>::zero();
1351            let b = RelativeTolerance::try_new(1.0).unwrap();
1352            assert!(a != b);
1353
1354            let c = RelativeTolerance::try_new(1.0).unwrap();
1355            let d = RelativeTolerance::try_new(2.0).unwrap();
1356            assert!(c != d);
1357        }
1358
1359        #[test]
1360        fn debug_trait() {
1361            // Test the Debug trait
1362            let tolerance = RelativeTolerance::try_new(0.5).unwrap();
1363            assert_eq!(format!("{:?}", tolerance), "RelativeTolerance(0.5)");
1364        }
1365
1366        #[test]
1367        fn clone_trait() {
1368            // Test the Clone trait
1369            let tolerance = RelativeTolerance::try_new(0.5).unwrap();
1370            let cloned_tolerance = tolerance.clone();
1371            assert_eq!(tolerance, cloned_tolerance);
1372        }
1373
1374        #[test]
1375        fn partial_eq_trait() {
1376            // Test the PartialEq trait
1377            let tolerance1 = RelativeTolerance::try_new(0.5).unwrap();
1378            let tolerance2 = RelativeTolerance::try_new(0.5).unwrap();
1379            assert_eq!(tolerance1, tolerance2);
1380        }
1381
1382        #[test]
1383        fn partial_ord_trait() {
1384            // Test the PartialOrd trait
1385            let tolerance1 = RelativeTolerance::try_new(0.5).unwrap();
1386            let tolerance2 = RelativeTolerance::try_new(1.0).unwrap();
1387            assert!(tolerance1 < tolerance2);
1388        }
1389
1390        #[test]
1391        fn as_ref_trait() {
1392            // Test the AsRef trait
1393            let tolerance = RelativeTolerance::try_new(0.5).unwrap();
1394            let inner: &f64 = tolerance.as_ref();
1395            assert_eq!(*inner, 0.5);
1396        }
1397
1398        #[test]
1399        fn lower_exp_trait() {
1400            // Test the LowerExp trait (scientific notation formatting)
1401            let tolerance = RelativeTolerance::try_new(0.00001).unwrap();
1402            assert_eq!(format!("{:e}", tolerance), "1e-5");
1403        }
1404    }
1405
1406    mod positive_real_scalar {
1407        use super::*;
1408        use crate::backends::native64::validated::RealNative64StrictFiniteInDebug;
1409
1410        #[test]
1411        fn try_new_positive() {
1412            let val = PositiveRealScalar::try_new(2.5_f64).unwrap();
1413            assert_eq!(*val.as_ref(), 2.5);
1414        }
1415
1416        #[test]
1417        fn try_new_min_positive() {
1418            let val = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
1419            assert_eq!(*val.as_ref(), f64::MIN_POSITIVE);
1420        }
1421
1422        #[test]
1423        fn try_new_zero_fails() {
1424            let result = PositiveRealScalar::try_new(0.0_f64);
1425            assert!(matches!(
1426                result,
1427                Err(ErrorsPositiveRealScalar::ZeroValue { .. })
1428            ));
1429        }
1430
1431        #[test]
1432        fn try_new_negative_fails() {
1433            let result = PositiveRealScalar::try_new(-1.0_f64);
1434            assert!(
1435                matches!(result, Err(ErrorsPositiveRealScalar::NegativeValue { value, .. }) if value == -1.0)
1436            );
1437        }
1438
1439        #[test]
1440        fn display_trait() {
1441            let val = PositiveRealScalar::try_new(1.618_f64).unwrap();
1442            assert_eq!(format!("{}", val), "1.618");
1443        }
1444
1445        #[test]
1446        fn into_inner() {
1447            let val = PositiveRealScalar::try_new(42.0_f64).unwrap();
1448            let inner = val.into_inner();
1449            assert_eq!(inner, 42.0);
1450        }
1451
1452        #[test]
1453        fn partial_ord() {
1454            let a = PositiveRealScalar::try_new(1.0_f64).unwrap();
1455            let b = PositiveRealScalar::try_new(2.0_f64).unwrap();
1456            assert!(a < b);
1457        }
1458
1459        #[test]
1460        fn serialize_deserialize() {
1461            let val = PositiveRealScalar::try_new(3.0_f64).unwrap();
1462            let serialized = serde_json::to_string(&val).unwrap();
1463            let deserialized: PositiveRealScalar<f64> = serde_json::from_str(&serialized).unwrap();
1464            assert_eq!(val, deserialized);
1465        }
1466
1467        #[test]
1468        fn with_validated_type() {
1469            let inner = RealNative64StrictFinite::try_from_f64(5.0).unwrap();
1470            let val = PositiveRealScalar::try_new(inner).unwrap();
1471            assert_eq!(*val.as_ref().as_ref(), 5.0);
1472        }
1473
1474        #[test]
1475        #[cfg(debug_assertions)]
1476        #[should_panic(expected = "is not finite")]
1477        fn debug_panics_on_nan() {
1478            let _ = PositiveRealScalar::try_new(f64::NAN);
1479        }
1480
1481        #[test]
1482        fn try_new() {
1483            // Test creating a PositiveRealScalar instance with a positive value
1484            let v = PositiveRealScalar::try_new(2.5).unwrap();
1485            assert_eq!(*v.as_ref(), 2.5);
1486
1487            // Test creating a PositiveRealScalar instance with a very small positive value
1488            let v = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
1489            assert_eq!(*v.as_ref(), f64::MIN_POSITIVE);
1490
1491            // Test creating a PositiveRealScalar instance with a large positive value
1492            let v = PositiveRealScalar::try_new(1e100).unwrap();
1493            assert_eq!(*v.as_ref(), 1e100);
1494        }
1495
1496        #[test]
1497        fn try_new_invalid_negative() {
1498            // Test creating an invalid PositiveRealScalar instance with a negative value
1499            let scalar = PositiveRealScalar::try_new(-0.1);
1500            assert!(matches!(
1501                scalar,
1502                Err(ErrorsPositiveRealScalar::NegativeValue { .. })
1503            ));
1504
1505            // Test with a negative value close to zero
1506            let scalar = PositiveRealScalar::try_new(-f64::EPSILON);
1507            assert!(matches!(
1508                scalar,
1509                Err(ErrorsPositiveRealScalar::NegativeValue { .. })
1510            ));
1511        }
1512
1513        #[test]
1514        fn try_new_invalid_zero() {
1515            // Test creating an invalid PositiveRealScalar instance with zero
1516            let scalar = PositiveRealScalar::try_new(0.0);
1517            assert!(matches!(
1518                scalar,
1519                Err(ErrorsPositiveRealScalar::ZeroValue { .. })
1520            ));
1521        }
1522
1523        #[test]
1524        #[cfg(debug_assertions)]
1525        #[should_panic = "The input value inf is not finite!"]
1526        fn try_new_invalid_infinite() {
1527            // Test creating an invalid PositiveRealScalar instance with an infinite value
1528            let _scalar = PositiveRealScalar::try_new(f64::INFINITY);
1529        }
1530
1531        #[test]
1532        #[cfg(debug_assertions)]
1533        #[should_panic = "The input value -inf is not finite!"]
1534        fn try_new_invalid_negative_infinite() {
1535            // Test creating an invalid PositiveRealScalar instance with negative infinity
1536            let _scalar = PositiveRealScalar::try_new(f64::NEG_INFINITY);
1537        }
1538
1539        #[test]
1540        #[cfg(debug_assertions)]
1541        #[should_panic = "The input value NaN is not finite!"]
1542        fn try_new_invalid_nan() {
1543            // Test creating an invalid PositiveRealScalar instance with a NaN value
1544            let _scalar = PositiveRealScalar::try_new(f64::NAN);
1545        }
1546
1547        #[test]
1548        fn equality_operator() {
1549            // Test equality operator for PositiveRealScalar instances
1550            let a = PositiveRealScalar::try_new(1.5).unwrap();
1551            let b = PositiveRealScalar::try_new(1.5).unwrap();
1552            assert!(a == b);
1553
1554            let c = PositiveRealScalar::try_new(3.1).unwrap();
1555            let d = PositiveRealScalar::try_new(3.1).unwrap();
1556            assert!(c == d);
1557        }
1558
1559        #[test]
1560        fn inequality_operator() {
1561            // Test inequality operator for PositiveRealScalar instances
1562            let a = PositiveRealScalar::try_new(1.0).unwrap();
1563            let b = PositiveRealScalar::try_new(2.0).unwrap();
1564            assert!(a != b);
1565
1566            let c = PositiveRealScalar::try_new(0.5).unwrap();
1567            let d = PositiveRealScalar::try_new(1.5).unwrap();
1568            assert!(c != d);
1569        }
1570
1571        #[test]
1572        fn debug_trait() {
1573            // Test the Debug trait
1574            let scalar = PositiveRealScalar::try_new(2.7).unwrap();
1575            assert_eq!(format!("{:?}", scalar), "PositiveRealScalar(2.7)");
1576        }
1577
1578        #[test]
1579        fn clone_trait() {
1580            // Test the Clone trait
1581            let scalar = PositiveRealScalar::try_new(1.414).unwrap();
1582            let cloned_scalar = scalar.clone();
1583            assert_eq!(scalar, cloned_scalar);
1584        }
1585
1586        #[test]
1587        fn partial_eq_trait() {
1588            // Test the PartialEq trait
1589            let scalar1 = PositiveRealScalar::try_new(0.577).unwrap();
1590            let scalar2 = PositiveRealScalar::try_new(0.577).unwrap();
1591            assert_eq!(scalar1, scalar2);
1592        }
1593
1594        #[test]
1595        fn partial_ord_trait() {
1596            // Test the PartialOrd trait
1597            let scalar1 = PositiveRealScalar::try_new(1.0).unwrap();
1598            let scalar2 = PositiveRealScalar::try_new(2.0).unwrap();
1599            assert!(scalar1 < scalar2);
1600
1601            let scalar3 = PositiveRealScalar::try_new(3.0).unwrap();
1602            let scalar4 = PositiveRealScalar::try_new(3.0).unwrap();
1603            assert!(scalar3 <= scalar4);
1604            assert!(scalar4 >= scalar3);
1605        }
1606
1607        #[test]
1608        fn as_ref_trait() {
1609            // Test the AsRef trait
1610            let scalar = PositiveRealScalar::try_new(2.0).unwrap();
1611            let inner: &f64 = scalar.as_ref();
1612            assert_eq!(*inner, 2.0);
1613        }
1614
1615        #[test]
1616        fn edge_cases() {
1617            // Test with very small positive values
1618            let tiny_scalar = PositiveRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
1619            assert_eq!(*tiny_scalar.as_ref(), f64::MIN_POSITIVE);
1620
1621            // Test with large positive values
1622            let large_scalar = PositiveRealScalar::try_new(f64::MAX).unwrap();
1623            assert_eq!(*large_scalar.as_ref(), f64::MAX);
1624
1625            // Test with epsilon
1626            let epsilon_scalar = PositiveRealScalar::try_new(f64::EPSILON).unwrap();
1627            assert_eq!(*epsilon_scalar.as_ref(), f64::EPSILON);
1628        }
1629
1630        #[test]
1631        fn generic_scalar_types() {
1632            // Test with RealNative64StrictFiniteInDebug type
1633            let scalar =
1634                PositiveRealScalar::try_new(RealNative64StrictFiniteInDebug::try_new(5.0).unwrap())
1635                    .unwrap();
1636            assert_eq!(
1637                *scalar.as_ref(),
1638                RealNative64StrictFiniteInDebug::try_new(5.0).unwrap()
1639            );
1640
1641            // Test error case with generic type
1642            let zero_value = RealNative64StrictFiniteInDebug::try_new(0.0).unwrap();
1643            let scalar_result = PositiveRealScalar::try_new(zero_value);
1644            assert!(matches!(
1645                scalar_result,
1646                Err(ErrorsPositiveRealScalar::ZeroValue { .. })
1647            ));
1648        }
1649
1650        #[test]
1651        fn mathematical_operations() {
1652            // Test that the wrapped values behave correctly in mathematical contexts
1653            let scalar1 = PositiveRealScalar::try_new(2.0).unwrap();
1654            let scalar2 = PositiveRealScalar::try_new(3.0).unwrap();
1655
1656            // Test comparison operations
1657            assert!(scalar1 < scalar2);
1658            assert!(scalar2 > scalar1);
1659            assert_eq!(
1660                scalar1.partial_cmp(&scalar2),
1661                Some(std::cmp::Ordering::Less)
1662            );
1663        }
1664
1665        #[test]
1666        fn error_messages() {
1667            // Test that error messages are descriptive
1668            let negative_result = PositiveRealScalar::try_new(-1.0);
1669            match negative_result {
1670                Err(ErrorsPositiveRealScalar::NegativeValue { value, .. }) => {
1671                    assert_eq!(value, -1.0);
1672                }
1673                _ => panic!("Expected NegativeValue error"),
1674            }
1675
1676            let zero_result = PositiveRealScalar::try_new(0.0);
1677            match zero_result {
1678                Err(ErrorsPositiveRealScalar::ZeroValue { .. }) => {
1679                    // Expected
1680                }
1681                _ => panic!("Expected ZeroValue error"),
1682            }
1683        }
1684    }
1685
1686    mod non_negative_real_scalar {
1687        use super::*;
1688        use crate::backends::native64::validated::RealNative64StrictFiniteInDebug;
1689        use crate::functions::{Max, Min};
1690        use num::Zero;
1691
1692        #[test]
1693        fn add_trait_preserves_non_negativity() {
1694            let a = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
1695            let b = NonNegativeRealScalar::try_new(3.5_f64).unwrap();
1696
1697            let sum = a + b;
1698            assert_eq!(*sum.as_ref(), 5.5);
1699            assert!(*sum.as_ref() >= 0.0);
1700        }
1701
1702        #[test]
1703        fn add_trait_zero_is_identity() {
1704            let value = NonNegativeRealScalar::try_new(4.25_f64).unwrap();
1705            let zero = NonNegativeRealScalar::<f64>::zero();
1706
1707            assert_eq!(*(value.clone() + zero.clone()).as_ref(), 4.25);
1708            assert_eq!(*(zero + value).as_ref(), 4.25);
1709        }
1710
1711        #[test]
1712        fn zero_trait_methods() {
1713            let zero = NonNegativeRealScalar::<f64>::zero();
1714            let non_zero = NonNegativeRealScalar::try_new(0.5_f64).unwrap();
1715
1716            assert!(zero.is_zero());
1717            assert!(!non_zero.is_zero());
1718            assert_eq!(*zero.as_ref(), 0.0);
1719        }
1720
1721        #[test]
1722        fn max_trait_by_ref_and_by_value() {
1723            let small = NonNegativeRealScalar::try_new(1.0_f64).unwrap();
1724            let large = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
1725
1726            let by_ref = Max::max_by_ref(&small, &large);
1727            assert_eq!(*by_ref.as_ref(), 3.0);
1728
1729            let by_value = Max::max_by_value(small, large);
1730            assert_eq!(*by_value.as_ref(), 3.0);
1731        }
1732
1733        #[test]
1734        fn min_trait_by_ref_and_by_value() {
1735            let small = NonNegativeRealScalar::try_new(1.0_f64).unwrap();
1736            let large = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
1737
1738            let by_ref = Min::min_by_ref(&small, &large);
1739            assert_eq!(*by_ref.as_ref(), 1.0);
1740
1741            let by_value = Min::min_by_value(small, large);
1742            assert_eq!(*by_value.as_ref(), 1.0);
1743        }
1744
1745        #[test]
1746        fn max_min_by_ref_equal_values_returns_left_operand() {
1747            let left_max = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
1748            let right_max = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
1749            let selected_max = Max::max_by_ref(&left_max, &right_max);
1750            assert!(std::ptr::eq(selected_max, &left_max));
1751
1752            let left_min = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
1753            let right_min = NonNegativeRealScalar::try_new(2.0_f64).unwrap();
1754            let selected_min = Min::min_by_ref(&left_min, &right_min);
1755            assert!(std::ptr::eq(selected_min, &left_min));
1756        }
1757
1758        #[test]
1759        fn try_new_negative_fails() {
1760            let result = NonNegativeRealScalar::try_new(-1.0_f64);
1761            assert!(
1762                matches!(result, Err(ErrorsNonNegativeRealScalar::NegativeValue { value, .. }) if value == -1.0)
1763            );
1764        }
1765
1766        #[test]
1767        fn display_trait() {
1768            let val = NonNegativeRealScalar::try_new(2.7_f64).unwrap();
1769            assert_eq!(format!("{}", val), "2.7");
1770
1771            let zero = NonNegativeRealScalar::try_new(0.0_f64).unwrap();
1772            assert_eq!(format!("{}", zero), "0");
1773        }
1774
1775        #[test]
1776        fn partial_ord() {
1777            let zero = NonNegativeRealScalar::try_new(0.0_f64).unwrap();
1778            let positive = NonNegativeRealScalar::try_new(1.0_f64).unwrap();
1779            assert!(zero < positive);
1780        }
1781
1782        #[test]
1783        fn serialize_deserialize() {
1784            let val = NonNegativeRealScalar::try_new(3.0_f64).unwrap();
1785            let serialized = serde_json::to_string(&val).unwrap();
1786            let deserialized: NonNegativeRealScalar<f64> =
1787                serde_json::from_str(&serialized).unwrap();
1788            assert_eq!(val, deserialized);
1789        }
1790
1791        #[test]
1792        fn with_validated_type() {
1793            let inner = RealNative64StrictFinite::try_from_f64(0.0).unwrap();
1794            let val = NonNegativeRealScalar::try_new(inner).unwrap();
1795            assert_eq!(*val.as_ref().as_ref(), 0.0);
1796        }
1797
1798        #[test]
1799        #[cfg(debug_assertions)]
1800        #[should_panic(expected = "is not finite")]
1801        fn debug_panics_on_nan() {
1802            let _ = NonNegativeRealScalar::try_new(f64::NAN);
1803        }
1804
1805        #[test]
1806        fn try_new_positive() {
1807            // Test creating a NonNegativeRealScalar instance with positive values
1808            let v = NonNegativeRealScalar::try_new(2.5).unwrap();
1809            assert_eq!(*v.as_ref(), 2.5);
1810
1811            // Test with very small positive value
1812            let v = NonNegativeRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
1813            assert_eq!(*v.as_ref(), f64::MIN_POSITIVE);
1814
1815            // Test with large positive value
1816            let v = NonNegativeRealScalar::try_new(1e100).unwrap();
1817            assert_eq!(*v.as_ref(), 1e100);
1818        }
1819
1820        #[test]
1821        fn try_new_zero() {
1822            // Test creating a NonNegativeRealScalar instance with zero (VALID case)
1823            let v = NonNegativeRealScalar::try_new(0.0).unwrap();
1824            assert_eq!(*v.as_ref(), 0.0);
1825
1826            // This is the key difference from PositiveRealScalar:
1827            // Zero is VALID for NonNegativeRealScalar (x ≥ 0)
1828            // but INVALID for PositiveRealScalar (x > 0)
1829        }
1830
1831        #[test]
1832        fn try_new_invalid_negative() {
1833            // Test creating an invalid NonNegativeRealScalar instance with negative values
1834            let scalar = NonNegativeRealScalar::try_new(-0.1);
1835            assert!(matches!(
1836                scalar,
1837                Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
1838            ));
1839
1840            // Test with negative value close to zero
1841            let scalar = NonNegativeRealScalar::try_new(-f64::EPSILON);
1842            assert!(matches!(
1843                scalar,
1844                Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
1845            ));
1846
1847            // Test with large negative value
1848            let scalar = NonNegativeRealScalar::try_new(-100.0);
1849            assert!(matches!(
1850                scalar,
1851                Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
1852            ));
1853        }
1854
1855        #[test]
1856        #[cfg(debug_assertions)]
1857        #[should_panic = "The input value inf is not finite!"]
1858        fn try_new_invalid_infinite() {
1859            // Test creating an invalid NonNegativeRealScalar instance with infinity
1860            let _scalar = NonNegativeRealScalar::try_new(f64::INFINITY);
1861        }
1862
1863        #[test]
1864        #[cfg(debug_assertions)]
1865        #[should_panic = "The input value -inf is not finite!"]
1866        fn try_new_invalid_negative_infinite() {
1867            // Test creating an invalid NonNegativeRealScalar instance with negative infinity
1868            let _scalar = NonNegativeRealScalar::try_new(f64::NEG_INFINITY);
1869        }
1870
1871        #[test]
1872        #[cfg(debug_assertions)]
1873        #[should_panic = "The input value NaN is not finite!"]
1874        fn try_new_invalid_nan() {
1875            // Test creating an invalid NonNegativeRealScalar instance with NaN
1876            let _scalar = NonNegativeRealScalar::try_new(f64::NAN);
1877        }
1878
1879        #[test]
1880        fn equality_operator() {
1881            // Test equality operator for NonNegativeRealScalar instances
1882            let a = NonNegativeRealScalar::try_new(1.5).unwrap();
1883            let b = NonNegativeRealScalar::try_new(1.5).unwrap();
1884            assert!(a == b);
1885
1886            let c = NonNegativeRealScalar::try_new(0.0).unwrap();
1887            let d = NonNegativeRealScalar::try_new(0.0).unwrap();
1888            assert!(c == d);
1889        }
1890
1891        #[test]
1892        fn inequality_operator() {
1893            // Test inequality operator for NonNegativeRealScalar instances
1894            let a = NonNegativeRealScalar::try_new(0.0).unwrap();
1895            let b = NonNegativeRealScalar::try_new(1.0).unwrap();
1896            assert!(a != b);
1897
1898            let c = NonNegativeRealScalar::try_new(0.5).unwrap();
1899            let d = NonNegativeRealScalar::try_new(1.5).unwrap();
1900            assert!(c != d);
1901        }
1902
1903        #[test]
1904        fn debug_trait() {
1905            // Test the Debug trait
1906            let scalar = NonNegativeRealScalar::try_new(2.7).unwrap();
1907            assert_eq!(format!("{:?}", scalar), "NonNegativeRealScalar(2.7)");
1908
1909            let zero = NonNegativeRealScalar::try_new(0.0).unwrap();
1910            assert_eq!(format!("{:?}", zero), "NonNegativeRealScalar(0.0)");
1911        }
1912
1913        #[test]
1914        fn clone_trait() {
1915            // Test the Clone trait
1916            let scalar = NonNegativeRealScalar::try_new(1.414).unwrap();
1917            let cloned_scalar = scalar.clone();
1918            assert_eq!(scalar, cloned_scalar);
1919        }
1920
1921        #[test]
1922        fn partial_eq_trait() {
1923            // Test the PartialEq trait
1924            let scalar1 = NonNegativeRealScalar::try_new(0.577).unwrap();
1925            let scalar2 = NonNegativeRealScalar::try_new(0.577).unwrap();
1926            assert_eq!(scalar1, scalar2);
1927
1928            // Test with zero
1929            let zero1 = NonNegativeRealScalar::try_new(0.0).unwrap();
1930            let zero2 = NonNegativeRealScalar::try_new(0.0).unwrap();
1931            assert_eq!(zero1, zero2);
1932        }
1933
1934        #[test]
1935        fn partial_ord_trait() {
1936            // Test the PartialOrd trait
1937            let scalar1 = NonNegativeRealScalar::try_new(0.0).unwrap();
1938            let scalar2 = NonNegativeRealScalar::try_new(1.0).unwrap();
1939            assert!(scalar1 < scalar2);
1940
1941            let scalar3 = NonNegativeRealScalar::try_new(3.0).unwrap();
1942            let scalar4 = NonNegativeRealScalar::try_new(3.0).unwrap();
1943            assert!(scalar3 <= scalar4);
1944            assert!(scalar4 >= scalar3);
1945
1946            // Test that zero is less than positive values
1947            let zero = NonNegativeRealScalar::try_new(0.0).unwrap();
1948            let positive = NonNegativeRealScalar::try_new(0.001).unwrap();
1949            assert!(zero < positive);
1950        }
1951
1952        #[test]
1953        fn as_ref_trait() {
1954            // Test the AsRef trait
1955            let scalar = NonNegativeRealScalar::try_new(2.0).unwrap();
1956            let inner: &f64 = scalar.as_ref();
1957            assert_eq!(*inner, 2.0);
1958
1959            let zero = NonNegativeRealScalar::try_new(0.0).unwrap();
1960            let inner: &f64 = zero.as_ref();
1961            assert_eq!(*inner, 0.0);
1962        }
1963
1964        #[test]
1965        fn into_inner() {
1966            // Test converting a NonNegativeRealScalar instance into its inner value
1967            let scalar = NonNegativeRealScalar::try_new(42.0).unwrap();
1968            let inner = scalar.into_inner();
1969            assert_eq!(inner, 42.0);
1970
1971            // Test with zero
1972            let zero = NonNegativeRealScalar::try_new(0.0).unwrap();
1973            let inner = zero.into_inner();
1974            assert_eq!(inner, 0.0);
1975
1976            // Test with very small positive value
1977            let scalar = NonNegativeRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
1978            let inner = scalar.into_inner();
1979            assert_eq!(inner, f64::MIN_POSITIVE);
1980        }
1981
1982        #[test]
1983        fn edge_cases() {
1984            // Test with zero (key edge case)
1985            let zero_scalar = NonNegativeRealScalar::try_new(0.0).unwrap();
1986            assert_eq!(*zero_scalar.as_ref(), 0.0);
1987
1988            // Test with very small positive values
1989            let tiny_scalar = NonNegativeRealScalar::try_new(f64::MIN_POSITIVE).unwrap();
1990            assert_eq!(*tiny_scalar.as_ref(), f64::MIN_POSITIVE);
1991
1992            // Test with large positive values
1993            let large_scalar = NonNegativeRealScalar::try_new(f64::MAX).unwrap();
1994            assert_eq!(*large_scalar.as_ref(), f64::MAX);
1995
1996            // Test with epsilon
1997            let epsilon_scalar = NonNegativeRealScalar::try_new(f64::EPSILON).unwrap();
1998            assert_eq!(*epsilon_scalar.as_ref(), f64::EPSILON);
1999        }
2000
2001        #[test]
2002        fn generic_scalar_types() {
2003            // Test with RealNative64StrictFiniteInDebug type
2004            let scalar = NonNegativeRealScalar::try_new(
2005                RealNative64StrictFiniteInDebug::try_new(5.0).unwrap(),
2006            )
2007            .unwrap();
2008            assert_eq!(
2009                *scalar.as_ref(),
2010                RealNative64StrictFiniteInDebug::try_new(5.0).unwrap()
2011            );
2012
2013            // Test with zero (valid for NonNegativeRealScalar)
2014            let zero_value = RealNative64StrictFiniteInDebug::try_new(0.0).unwrap();
2015            let scalar_result = NonNegativeRealScalar::try_new(zero_value);
2016            assert!(scalar_result.is_ok()); // Should succeed!
2017
2018            // Test error case with generic type
2019            let negative_value = RealNative64StrictFiniteInDebug::try_new(-1.0).unwrap();
2020            let scalar_result = NonNegativeRealScalar::try_new(negative_value);
2021            assert!(matches!(
2022                scalar_result,
2023                Err(ErrorsNonNegativeRealScalar::NegativeValue { .. })
2024            ));
2025        }
2026
2027        #[test]
2028        fn mathematical_operations() {
2029            // Test that the wrapped values behave correctly in mathematical contexts
2030            let scalar1 = NonNegativeRealScalar::try_new(0.0).unwrap();
2031            let scalar2 = NonNegativeRealScalar::try_new(2.0).unwrap();
2032            let scalar3 = NonNegativeRealScalar::try_new(3.0).unwrap();
2033
2034            // Test comparison operations
2035            assert!(scalar1 < scalar2);
2036            assert!(scalar2 < scalar3);
2037            assert!(scalar3 > scalar1);
2038            assert_eq!(
2039                scalar1.partial_cmp(&scalar2),
2040                Some(std::cmp::Ordering::Less)
2041            );
2042            assert_eq!(
2043                scalar2.partial_cmp(&scalar1),
2044                Some(std::cmp::Ordering::Greater)
2045            );
2046        }
2047
2048        #[test]
2049        fn error_messages() {
2050            // Test that error messages are descriptive
2051            let negative_result = NonNegativeRealScalar::try_new(-1.0);
2052            match negative_result {
2053                Err(ErrorsNonNegativeRealScalar::NegativeValue { value, .. }) => {
2054                    assert_eq!(value, -1.0);
2055                }
2056                _ => panic!("Expected NegativeValue error"),
2057            }
2058
2059            let large_negative_result = NonNegativeRealScalar::try_new(-100.5);
2060            match large_negative_result {
2061                Err(ErrorsNonNegativeRealScalar::NegativeValue { value, .. }) => {
2062                    assert_eq!(value, -100.5);
2063                }
2064                _ => panic!("Expected NegativeValue error"),
2065            }
2066        }
2067
2068        #[test]
2069        fn distinction_from_positive_real_scalar() {
2070            // Critical test: demonstrate the key difference between
2071            // NonNegativeRealScalar (x ≥ 0) and PositiveRealScalar (x > 0)
2072
2073            // Zero is VALID for NonNegativeRealScalar
2074            let non_neg_zero = NonNegativeRealScalar::try_new(0.0);
2075            assert!(
2076                non_neg_zero.is_ok(),
2077                "Zero should be valid for NonNegativeRealScalar (x ≥ 0)"
2078            );
2079
2080            // Zero is INVALID for PositiveRealScalar
2081            let pos_zero = PositiveRealScalar::try_new(0.0);
2082            assert!(
2083                pos_zero.is_err(),
2084                "Zero should be invalid for PositiveRealScalar (x > 0)"
2085            );
2086            assert!(matches!(
2087                pos_zero,
2088                Err(ErrorsPositiveRealScalar::ZeroValue { .. })
2089            ));
2090
2091            // Positive values are valid for both
2092            let non_neg_positive = NonNegativeRealScalar::try_new(1.0);
2093            let pos_positive = PositiveRealScalar::try_new(1.0);
2094            assert!(non_neg_positive.is_ok());
2095            assert!(pos_positive.is_ok());
2096
2097            // Negative values are invalid for both
2098            let non_neg_negative = NonNegativeRealScalar::try_new(-1.0);
2099            let pos_negative = PositiveRealScalar::try_new(-1.0);
2100            assert!(non_neg_negative.is_err());
2101            assert!(pos_negative.is_err());
2102        }
2103
2104        #[test]
2105        fn use_case_distance() {
2106            // Real-world use case: computing distances (which can be zero)
2107            fn compute_distance(x1: f64, x2: f64) -> NonNegativeRealScalar<f64> {
2108                let diff = (x2 - x1).abs();
2109                NonNegativeRealScalar::try_new(diff).unwrap()
2110            }
2111
2112            // Distance between different points
2113            let dist1 = compute_distance(0.0, 5.0);
2114            assert_eq!(*dist1.as_ref(), 5.0);
2115
2116            // Distance from a point to itself (zero distance is valid!)
2117            let dist2 = compute_distance(3.0, 3.0);
2118            assert_eq!(*dist2.as_ref(), 0.0);
2119        }
2120
2121        #[test]
2122        fn use_case_absolute_value() {
2123            // Real-world use case: absolute values (which can be zero)
2124            fn safe_abs(x: f64) -> NonNegativeRealScalar<f64> {
2125                NonNegativeRealScalar::try_new(x.abs()).unwrap()
2126            }
2127
2128            assert_eq!(*safe_abs(-5.0).as_ref(), 5.0);
2129            assert_eq!(*safe_abs(0.0).as_ref(), 0.0); // Zero is valid!
2130            assert_eq!(*safe_abs(3.0).as_ref(), 3.0);
2131        }
2132    }
2133
2134    mod distinction_tests {
2135        use super::*;
2136
2137        #[test]
2138        fn positive_vs_non_negative_zero() {
2139            // This is the KEY difference between the two types
2140
2141            // Zero is INVALID for PositiveRealScalar (x > 0)
2142            assert!(PositiveRealScalar::try_new(0.0_f64).is_err());
2143
2144            // Zero is VALID for NonNegativeRealScalar (x ≥ 0)
2145            assert!(NonNegativeRealScalar::try_new(0.0_f64).is_ok());
2146        }
2147
2148        #[test]
2149        fn both_accept_positive() {
2150            let positive = 1.0_f64;
2151            assert!(PositiveRealScalar::try_new(positive).is_ok());
2152            assert!(NonNegativeRealScalar::try_new(positive).is_ok());
2153        }
2154
2155        #[test]
2156        fn both_reject_negative() {
2157            let negative = -1.0_f64;
2158            assert!(PositiveRealScalar::try_new(negative).is_err());
2159            assert!(NonNegativeRealScalar::try_new(negative).is_err());
2160        }
2161    }
2162}