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