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