num_valid/
validation.rs

1#![deny(rustdoc::broken_intra_doc_links)]
2
3//! # Numerical Value Validation Module
4//!
5//! This module provides a framework for validating numerical scalar values,
6//! particularly floating-point and complex numbers, including those with arbitrary precision
7//! using the [`rug`](https://crates.io/crates/rug) library (when the "rug" feature is enabled).
8//!
9//! It defines:
10//! - **Error Types**: Such as [`ErrorsValidationRawReal`], [`ErrorsValidationRawComplex`],
11//!   and [`ErrorsTryFromf64`].
12//!   These errors detail various validation failures like non-finite values,
13//!   subnormal numbers (for `f64` and [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)), and precision mismatches for `rug` types.
14//! - **Validation Policies**: Implemented via the [`ValidationPolicy`] trait from the
15//!   [`try_create`](https://crates.io/crates/try_create) crate.
16//!   The primary policy provided is [`StrictFinitePolicy`], which ensures that
17//!   numbers are finite (not NaN or Infinity) and, for `f64` and [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html),
18//!   not subnormal.
19//! - **Floating-Point Checks Trait**: The [`FpChecks`] trait, defined within this module,
20//!   provides common classification methods like `is_finite`, `is_nan`, `is_infinite`,
21//!   and `is_normal`. This trait is implemented for standard and `rug`-based scalar types.
22//! - **Enhanced Type Safety for HashMap Keys**: The [`GuaranteesFiniteValues`] marker trait enables
23//!   full equality ([`Eq`]) and hashing ([`Hash`]) for validated types when their
24//!   validation policy guarantees finite values. This allows validated types to be used
25//!   as keys in [`HashMap`](std::collections::HashMap) and other hash-based collections.
26//! - **Implementations**: Validation logic for [`StrictFinitePolicy`] and implementations of
27//!   [`FpChecks`] are provided for [`f64`] and [`num::Complex<f64>`]. When the "rug" feature is enabled,
28//!   support extends to [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html), [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html), and indirectly to the wrapper types
29//!   `RealRugStrictFinite` and `ComplexRugStrictFinite` (which utilize these underlying `rug` types).
30//!
31//! The goal of this module is to ensure numerical stability and correctness by allowing
32//! functions to easily validate their inputs and outputs according to defined policies,
33//! and to provide a common interface for basic floating-point characteristic checks.
34//!
35//! ## Enhanced Type Safety with [`GuaranteesFiniteValues`]
36//!
37//! One of the key innovations of this validation framework is the [`GuaranteesFiniteValues`]
38//! marker trait. This trait enables compile-time verification that a validation policy
39//! ensures all validated values are finite, which unlocks additional type system guarantees:
40//!
41//! - **Full Equality**: These types implement [`Eq`], making equality comparisons well-defined
42//!   and symmetric.
43//! - **Hashing Support**: With both [`Eq`] and [`Hash`] implemented, these types can be used
44//!   as keys in hash-based collections like [`HashMap`](std::collections::HashMap) and [`HashSet`](std::collections::HashSet).
45//!
46//! **Note on Ordering**: The library intentionally does NOT implement [`Ord`]
47//! even for finite-guaranteed types, as our custom [`Max`](crate::functions::Max)
48//! and [`Min`](crate::functions::Min) traits provide more efficient reference-based
49//! operations compared to [`Ord`]'s value-based interface.
50//!
51//! Currently, [`StrictFinitePolicy`] and [`DebugValidationPolicy<StrictFinitePolicy>`]
52//! implement this marker trait.
53//!
54//!
55//! ## **Usage Examples**
56//!
57//! ### Basic Validation
58//!
59//! ```rust
60//! use num_valid::validation::{StrictFinitePolicy, ErrorsValidationRawReal};
61//! use try_create::ValidationPolicy; // For the validate() method
62//!
63//! // Valid finite number
64//! assert!(StrictFinitePolicy::<f64, 53>::validate(1.0).is_ok());
65//!
66//! // Invalid: NaN
67//! let result = StrictFinitePolicy::<f64, 53>::validate(f64::NAN);
68//! assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
69//!
70//! // Invalid: Infinity  
71//! let result = StrictFinitePolicy::<f64, 53>::validate(f64::INFINITY);
72//! assert!(matches!(result, Err(ErrorsValidationRawReal::IsPosInfinity { .. })));
73//!
74//! // Invalid: Subnormal
75//! let subnormal_val = f64::from_bits(1); // Smallest positive subnormal f64
76//! let result = StrictFinitePolicy::<f64, 53>::validate(subnormal_val);
77//! assert!(matches!(result, Err(ErrorsValidationRawReal::IsSubnormal { .. })));
78//! ```
79//!
80//! ### Using [`FpChecks`]
81//!
82//! ```rust
83//! use num_valid::validation::FpChecks;
84//! use num::Complex;
85//!
86//! let val_f64 = 1.0f64;
87//! let nan_f64 = f64::NAN;
88//! let inf_f64 = f64::INFINITY;
89//!
90//! assert!(val_f64.is_finite());
91//! assert!(val_f64.is_normal());
92//! assert!(nan_f64.is_nan());
93//! assert!(!nan_f64.is_finite());
94//! assert!(inf_f64.is_infinite());
95//!
96//! let val_complex = Complex::new(1.0, 2.0);
97//! let nan_complex_real = Complex::new(f64::NAN, 2.0);
98//! let inf_complex_imag = Complex::new(1.0, f64::NEG_INFINITY);
99//!
100//! assert!(val_complex.is_finite());
101//! assert!(val_complex.is_normal());
102//! assert!(nan_complex_real.is_nan());
103//! assert!(!nan_complex_real.is_finite());
104//! assert!(inf_complex_imag.is_infinite());
105//! assert!(!inf_complex_imag.is_finite());
106//! assert!(!inf_complex_imag.is_nan());
107//! ```
108//!
109//! ### HashMap Usage with Finite Guarantees
110//! ```rust
111//! use num_valid::kernels::native64_validated::RealNative64StrictFinite;
112//! use std::collections::HashMap;
113//! use try_create::TryNew;
114//!
115//! let mut scores = HashMap::new();
116//! let player1 = RealNative64StrictFinite::try_new(1.5).unwrap();
117//! let player2 = RealNative64StrictFinite::try_new(2.0).unwrap();
118//!
119//! scores.insert(player1, 100);  // Works because Eq + Hash are implemented
120//! scores.insert(player2, 95);
121//!
122//! assert_eq!(scores.len(), 2);
123//! ```
124//!
125//! ### Conditional Debug Validation
126//! ```rust
127//! use num_valid::validation::{DebugValidationPolicy, Native64RawRealStrictFinitePolicy};
128//! use try_create::ValidationPolicy;
129//!
130//! type ConditionalPolicy = DebugValidationPolicy<Native64RawRealStrictFinitePolicy>;
131//!
132//! let value = 42.0;
133//! // Validates only in debug builds, no-op in release
134//! assert!(ConditionalPolicy::validate(value).is_ok());
135//! ```
136
137use crate::{
138    ComplexValidated, NumKernel, RealValidated,
139    kernels::{RawComplexTrait, RawRealTrait, RawScalarTrait},
140};
141use duplicate::duplicate_item;
142use num::{Complex, Integer};
143use std::{
144    backtrace::Backtrace,
145    fmt::Display,
146    hash::{Hash, Hasher},
147    marker::PhantomData,
148    num::FpCategory,
149};
150use thiserror::Error;
151use try_create::ValidationPolicy;
152
153//--------------------------------------------------------------------------------------------------
154// Conditional Backtrace Support
155//--------------------------------------------------------------------------------------------------
156
157/// Captures a backtrace if the `backtrace` feature is enabled.
158///
159/// When the `backtrace` feature is enabled, this function captures a full backtrace
160/// using [`capture_backtrace()`]. When disabled (default), it returns a
161/// disabled backtrace with zero overhead.
162///
163/// # Performance
164///
165/// Backtrace capture is expensive (~1-10μs per capture). For performance-critical
166/// code, leave the `backtrace` feature disabled. Enable it during development or
167/// debugging to get detailed error traces.
168///
169/// # Example
170///
171/// ```toml
172/// # In Cargo.toml, enable backtraces for debugging:
173/// [dependencies]
174/// num-valid = { version = "0.2", features = ["backtrace"] }
175/// ```
176#[inline(always)]
177pub fn capture_backtrace() -> Backtrace {
178    #[cfg(feature = "backtrace")]
179    {
180        Backtrace::force_capture()
181    }
182    #[cfg(not(feature = "backtrace"))]
183    {
184        Backtrace::disabled()
185    }
186}
187
188/// Returns `true` if the `backtrace` feature is enabled.
189///
190/// This can be used to conditionally check whether backtraces are being captured.
191///
192/// # Example
193///
194/// ```rust
195/// use num_valid::validation::is_backtrace_enabled;
196///
197/// if is_backtrace_enabled() {
198///     println!("Backtraces are enabled - errors will include stack traces");
199/// } else {
200///     println!("Backtraces are disabled for performance");
201/// }
202/// ```
203#[inline(always)]
204pub const fn is_backtrace_enabled() -> bool {
205    #[cfg(feature = "backtrace")]
206    {
207        true
208    }
209    #[cfg(not(feature = "backtrace"))]
210    {
211        false
212    }
213}
214
215//--------------------------------------------------------------------------------------------------
216
217/// A marker for policies that apply to real types.
218///
219/// This trait refines the generic [`ValidationPolicy`] for types that implement
220/// [`RawRealTrait`]. It adds two key constraints:
221///
222/// 1.  It associates a `const PRECISION` with the policy, essential for backends
223///     like `rug`.
224/// 2.  It enforces that the policy's `Error` type must be the same as the canonical
225///     `ValidationErrors` type defined on the raw scalar itself. This architectural
226///     choice ensures consistent error handling throughout the library.
227pub trait ValidationPolicyReal:
228    ValidationPolicy<
229        Value: RawRealTrait,
230        Error = <<Self as ValidationPolicy>::Value as RawScalarTrait>::ValidationErrors,
231    >
232{
233    const PRECISION: u32;
234}
235
236/// A marker for policies that apply to complex types.
237///
238/// This trait refines the generic [`ValidationPolicy`] for types that implement
239/// [`RawComplexTrait`]. It adds two key constraints:
240///
241/// 1.  It associates a `const PRECISION` with the policy, essential for backends
242///     like `rug`.
243/// 2.  It enforces that the policy's `Error` type must be the same as the canonical
244///     `ValidationErrors` type defined on the raw scalar itself. This architectural
245///     choice ensures consistent error handling throughout the library.
246pub trait ValidationPolicyComplex:
247    ValidationPolicy<
248        Value: RawComplexTrait,
249        Error = <<Self as ValidationPolicy>::Value as RawScalarTrait>::ValidationErrors,
250    >
251{
252    const PRECISION: u32;
253}
254
255//--------------------------------------------------------------------------------------------------
256// Error types
257//--------------------------------------------------------------------------------------------------
258
259//--------------------------------------------
260// Errors for raw real number validation
261//--------------------------------------------
262/// Errors that can occur during the validation of a raw real number.
263///
264/// This enum is generic over `RawReal`, which is typically `f64` or [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html).
265/// It covers common validation failures like non-finite values, subnormality, infinity, and precision mismatch.
266#[derive(Debug, Error)]
267pub enum ErrorsValidationRawReal<RawReal: std::fmt::Debug + Display + Clone> {
268    /// The value is subnormal.
269    #[error("Value is subnormal: {value}")]
270    IsSubnormal {
271        /// The subnormal value that failed validation.
272        value: RawReal,
273
274        /// A captured backtrace for debugging purposes.
275        backtrace: Backtrace,
276    },
277
278    /// The value is NaN (Not a Number).
279    #[error("Value is NaN: {value}")]
280    IsNaN {
281        /// The NaN value that failed validation.
282        value: RawReal,
283
284        /// A captured backtrace for debugging purposes.
285        backtrace: Backtrace,
286    },
287
288    /// The value is positive infinity.
289    #[error("Value is positive infinity")]
290    IsPosInfinity {
291        /// The positive infinity value that failed validation.
292        backtrace: Backtrace,
293    },
294
295    /// The value is negative infinity.
296    #[error("Value is negative infinity")]
297    IsNegInfinity {
298        /// The negative infinity value that failed validation.
299        backtrace: Backtrace,
300    },
301
302    /// The precision of the input does not match the requested precision.
303    #[error(
304        "precision mismatch: input real has precision {actual_precision}, \
305        but the requested precision is {requested_precision}"
306    )]
307    PrecisionMismatch {
308        /// The input value.
309        input_value: RawReal,
310
311        /// The precision of the input value.
312        actual_precision: u32,
313
314        /// The requested precision.
315        requested_precision: u32,
316
317        /// A captured backtrace for debugging purposes.
318        backtrace: Backtrace,
319    },
320    /*
321    // The following variants are placeholders for potential future validation policies
322    // and are not currently used by StrictFinitePolicy.
323
324    #[error("Value is not strictly positive: {value}")]
325    NotPositive {
326        value: RawReal,
327        backtrace: Backtrace,
328    },
329
330    #[error("Value is negative: {value}")]
331    Negative {
332        value: RawReal,
333        backtrace: Backtrace,
334    },
335
336    #[error("Value is zero: {value}")]
337    IsZero {
338        value: RawReal,
339        backtrace: Backtrace,
340    },
341
342    #[error("Value {value} is outside the allowed range [{min}, {max}]")]
343    OutOfRange {
344        value: RawReal,
345        min: RawReal,
346        max: RawReal,
347        backtrace: Backtrace,
348    },
349    */
350}
351
352//--------------------------------------------
353// Errors for complex number validation
354//--------------------------------------------
355/// Errors that can occur during the validation of a complex number.
356///
357/// This enum is generic over `ErrorValidationReal`, which is the error type returned
358/// by validating the real and imaginary parts of the complex number (e.g.,
359/// [`ErrorsValidationRawReal<f64>`](ErrorsValidationRawReal)).
360///
361/// It indicates whether the real part, the imaginary part, or both parts failed validation.
362#[derive(Debug, Error)]
363pub enum ErrorsValidationRawComplex<ErrorValidationReal: std::error::Error> {
364    /// The real part of the complex number failed validation.
365    #[error("Real part validation error: {source}")]
366    InvalidRealPart {
367        /// The underlying validation error for the real part.
368        #[source]
369        #[backtrace]
370        source: ErrorValidationReal,
371    },
372
373    /// The imaginary part of the complex number failed validation.
374    #[error("Imaginary part validation error: {source}")]
375    InvalidImaginaryPart {
376        /// The underlying validation error for the imaginary part.
377        #[source]
378        #[backtrace]
379        source: ErrorValidationReal,
380    },
381
382    /// Both the real and imaginary parts of the complex number failed validation.
383    #[error("Both real and imaginary parts are invalid: real={real_error}, imag={imag_error}")]
384    InvalidBothParts {
385        /// The validation error for the real part.
386        real_error: ErrorValidationReal,
387
388        /// The validation error for the imaginary part.
389        imag_error: ErrorValidationReal,
390    },
391}
392
393//-------------------------------------------------------------
394/// Errors that can occur when trying to convert an `f64` to another real-type scalar of type `RawReal`.
395///
396/// This enum distinguishes between:
397/// 1.  Failures due to the `f64` not being exactly representable at the target `PRECISION`
398///     of the `RawReal` type.
399/// 2.  General validation failures of the output `RawReal` (e.g., NaN, Infinity, subnormal).
400#[derive(Debug, Error)]
401pub enum ErrorsTryFromf64<RawReal: RawRealTrait> {
402    /// The input `f64` value is not representable exactly at the target `precision` using the output `RawReal` type.
403    #[error(
404        "the input f64 value ({value_in:?}) cannot be exactly represented with the specific precision ({precision:?}). Try to increase the precision."
405    )]
406    NonRepresentableExactly {
407        /// The `f64` value that could not be exactly represented.
408        value_in: f64,
409
410        /// The input `value_in` after the conversion to the type `RawReal` with the requested `precision`.
411        value_converted_from_f64: RawReal,
412
413        /// The precision that was requested.
414        precision: u32,
415
416        /// A captured backtrace for debugging purposes.
417        backtrace: Backtrace,
418    },
419
420    /// The output value failed validation.
421    #[error("the output value is invalid!")]
422    Output {
423        /// The underlying validation error for the `output` value.
424        #[from]
425        source: ErrorsValidationRawReal<RawReal>,
426    },
427}
428//-------------------------------------------------------------
429
430//-------------------------------------------------------------
431#[derive(Debug, Error)]
432pub enum ErrorsRawRealToInteger<RawReal: RawRealTrait, IntType: Integer> {
433    /// The `RawReal` value is not finite (i.e., it is NaN or Infinity).
434    #[error("Value is not finite: {value}")]
435    NotFinite {
436        /// The non-finite `RawReal` value that caused the error.
437        value: RawReal,
438
439        /// A captured backtrace for debugging purposes.
440        backtrace: Backtrace,
441    },
442
443    /// The `RawReal` value is not an integer (i.e., it has a fractional part).
444    #[error("Value is not an integer: {value}")]
445    NotAnInteger {
446        /// The `RawReal` value that is not an integer.
447        value: RawReal,
448
449        /// A captured backtrace for debugging purposes.
450        backtrace: Backtrace,
451    },
452
453    /// The `RawReal` value is outside the range representable by the target integer type `IntType`.
454    #[error("Value {value} is out of range [{min}, {max}] for target integer type")]
455    OutOfRange {
456        /// The `RawReal` value that is out of range.
457        value: RawReal,
458
459        /// The minimum representable value of the target integer type `IntType`.
460        min: IntType,
461
462        /// The maximum representable value of the target integer type `IntType`.
463        max: IntType,
464
465        /// A captured backtrace for debugging purposes.
466        backtrace: Backtrace,
467    },
468}
469//-------------------------------------------------------------
470
471/// A validation policy that checks for strict finiteness.
472///
473/// For floating-point types (`ScalarType = `[`f64`], `ScalarType = `[`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)), this policy ensures that the value is:
474/// - Not NaN (Not a Number).
475/// - Not positive or negative Infinity.
476/// - Not subnormal (for `f64`).
477///   While [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) maintains its specified precision rather than having distinct subnormal
478///   representation in the IEEE 754 sense, this policy will also reject [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) values that are classified as
479///   [`FpCategory::Subnormal`] (e.g., results of underflow that are tiny but not exactly zero).
480///
481/// For complex types (`ScalarType = `[`Complex<f64>`], `ScalarType = `[`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html)), this policy applies
482/// the strict finiteness check to both the real and imaginary parts.
483///
484/// This struct is a Zero-Sized Type (ZST) and uses [`PhantomData`] to associate
485/// with the `ScalarType` it validates.
486pub struct StrictFinitePolicy<ScalarType: Sized, const PRECISION: u32>(PhantomData<ScalarType>);
487
488/// A type alias for a validation policy that enforces strict finiteness for the raw [`f64`] type.
489///
490/// This is a convenient alias for [`StrictFinitePolicy<f64, 53>`], configured specifically
491/// for Rust's native 64-bit floating-point number. It is used to validate that a given [`f64`]
492/// value is suitable for use within the library's validated types.
493///
494/// This policy ensures that a `f64` value is:
495/// - Not `NaN` (Not a Number).
496/// - Not positive or negative `Infinity`.
497/// - Not a subnormal number.
498///
499/// It is a fundamental building block for the native `f64` kernel, providing the validation
500/// logic for [`RealNative64StrictFinite`](crate::kernels::native64_validated::RealNative64StrictFinite).
501///
502/// # Example
503///
504/// ```rust
505/// use num_valid::validation::{Native64RawRealStrictFinitePolicy, ErrorsValidationRawReal};
506/// use try_create::ValidationPolicy;
507///
508/// // A valid finite number passes validation.
509/// assert!(Native64RawRealStrictFinitePolicy::validate(1.0).is_ok());
510///
511/// // NaN fails validation.
512/// let result_nan = Native64RawRealStrictFinitePolicy::validate(f64::NAN);
513/// assert!(matches!(result_nan, Err(ErrorsValidationRawReal::IsNaN { .. })));
514///
515/// // Infinity fails validation.
516/// let result_inf = Native64RawRealStrictFinitePolicy::validate(f64::INFINITY);
517/// assert!(matches!(result_inf, Err(ErrorsValidationRawReal::IsPosInfinity { .. })));
518/// ```
519pub type Native64RawRealStrictFinitePolicy = StrictFinitePolicy<f64, 53>;
520
521/// A type alias for a validation policy that enforces strict finiteness for the raw [`num::Complex<f64>`] type.
522///
523/// This is a convenient alias for [`StrictFinitePolicy<Complex<f64>, 53>`], configured for
524/// Rust's native 64-bit complex numbers. It is used to validate that a given [`num::Complex<f64>`]
525/// value is suitable for use within the library's validated types.
526///
527/// This policy applies the strict finiteness check (as defined by [`Native64RawRealStrictFinitePolicy`])
528/// to both the real and imaginary parts of the complex number. A [`num::Complex<f64>`] value is considered
529/// valid if and only if both of its components are finite (not `NaN`, `Infinity`, or subnormal).
530///
531/// It is a fundamental building block for the native `f64` kernel, providing the validation
532/// logic for [`ComplexNative64StrictFinite`](crate::kernels::native64_validated::ComplexNative64StrictFinite).
533///
534/// # Example
535///
536/// ```rust
537/// use num_valid::validation::{Native64RawComplexStrictFinitePolicy, ErrorsValidationRawComplex, ErrorsValidationRawReal};
538/// use try_create::ValidationPolicy;
539/// use num::Complex;
540///
541/// // A complex number with valid finite parts passes validation.
542/// let valid_complex = Complex::new(1.0, -2.0);
543/// assert!(Native64RawComplexStrictFinitePolicy::validate(valid_complex).is_ok());
544///
545/// // A complex number with NaN in the real part fails validation.
546/// let nan_complex = Complex::new(f64::NAN, 2.0);
547/// let result_nan = Native64RawComplexStrictFinitePolicy::validate(nan_complex);
548/// assert!(matches!(result_nan, Err(ErrorsValidationRawComplex::InvalidRealPart { .. })));
549///
550/// // A complex number with Infinity in the imaginary part fails validation.
551/// let inf_complex = Complex::new(1.0, f64::INFINITY);
552/// let result_inf = Native64RawComplexStrictFinitePolicy::validate(inf_complex);
553/// assert!(matches!(result_inf, Err(ErrorsValidationRawComplex::InvalidImaginaryPart { .. })));
554///
555/// // A complex number with invalid parts in both also fails.
556/// let both_invalid = Complex::new(f64::NAN, f64::INFINITY);
557/// let result_both = Native64RawComplexStrictFinitePolicy::validate(both_invalid);
558/// assert!(matches!(result_both, Err(ErrorsValidationRawComplex::InvalidBothParts { .. })));
559/// ```
560pub type Native64RawComplexStrictFinitePolicy = StrictFinitePolicy<Complex<f64>, 53>;
561
562/// Ensures the `f64` value is strictly finite.
563///
564/// This policy checks if the `f64` value is:
565/// - Not NaN (Not a Number).
566/// - Not positive or negative Infinity.
567/// - Not subnormal.
568///
569/// # Errors
570///
571/// Returns [`ErrorsValidationRawReal<f64>`](ErrorsValidationRawReal) if the value
572/// fails any of these checks.
573impl ValidationPolicy for Native64RawRealStrictFinitePolicy {
574    type Value = f64;
575    type Error = ErrorsValidationRawReal<f64>;
576
577    fn validate_ref(value: &f64) -> Result<(), Self::Error> {
578        match value.classify() {
579            FpCategory::Nan => Err(ErrorsValidationRawReal::IsNaN {
580                value: *value,
581                backtrace: capture_backtrace(),
582            }),
583            FpCategory::Infinite => {
584                if value.is_sign_positive() {
585                    Err(ErrorsValidationRawReal::IsPosInfinity {
586                        backtrace: capture_backtrace(),
587                    })
588                } else {
589                    Err(ErrorsValidationRawReal::IsNegInfinity {
590                        backtrace: capture_backtrace(),
591                    })
592                }
593            }
594            FpCategory::Subnormal => Err(ErrorsValidationRawReal::IsSubnormal {
595                value: *value,
596                backtrace: capture_backtrace(),
597            }),
598            FpCategory::Normal | FpCategory::Zero => Ok(()),
599        }
600    }
601}
602
603/// Validates a complex number by checking both its real and imaginary parts.
604///
605/// This function uses the provided real validation policy `P` to validate both parts.
606/// If both parts are valid, it returns `Ok(())`. If either part is invalid,
607/// it returns an appropriate error variant from the `ErrorsValidationRawComplex` enum.
608///
609/// # Errors
610/// Returns [`ErrorsValidationRawComplex<P::Error>`](ErrorsValidationRawComplex)
611/// if either the real part, the imaginary part, or both parts fail validation
612/// using the policy `P`.
613pub(crate) fn validate_complex<P: ValidationPolicyReal>(
614    real_part: &P::Value,
615    imag_part: &P::Value,
616) -> Result<(), ErrorsValidationRawComplex<P::Error>> {
617    let real_validation = P::validate_ref(real_part);
618    let imag_validation = P::validate_ref(imag_part);
619    match (real_validation, imag_validation) {
620        (Ok(()), Ok(())) => Ok(()),
621        (Ok(()), Err(imag_err)) => {
622            Err(ErrorsValidationRawComplex::InvalidImaginaryPart { source: imag_err })
623        }
624        (Err(real_err), Ok(())) => {
625            Err(ErrorsValidationRawComplex::InvalidRealPart { source: real_err })
626        }
627        (Err(real_err), Err(imag_err)) => Err(ErrorsValidationRawComplex::InvalidBothParts {
628            real_error: real_err,
629            imag_error: imag_err,
630        }),
631    }
632}
633
634/// Ensures both the real and imaginary parts of a `Complex<f64>` value are strictly finite.
635///
636/// This policy applies the [`StrictFinitePolicy<f64>`](StrictFinitePolicy) to both
637/// the real and imaginary components of the complex number.
638///
639/// # Errors
640///
641/// Returns [`ErrorsValidationRawComplex<ErrorsValidationRawReal<f64>>`](ErrorsValidationRawComplex)
642/// if either the real part, the imaginary part, or both parts fail the
643/// `StrictFinitePolicy<f64>` checks.
644impl ValidationPolicy for Native64RawComplexStrictFinitePolicy {
645    type Value = Complex<f64>;
646    type Error = ErrorsValidationRawComplex<ErrorsValidationRawReal<f64>>;
647
648    fn validate_ref(value: &Complex<f64>) -> Result<(), Self::Error> {
649        validate_complex::<Native64RawRealStrictFinitePolicy>(&value.re, &value.im)
650    }
651}
652
653impl<RawReal: RawRealTrait, const PRECISION: u32> ValidationPolicyReal
654    for StrictFinitePolicy<RawReal, PRECISION>
655where
656    StrictFinitePolicy<RawReal, PRECISION>:
657        ValidationPolicy<Value = RawReal, Error = <RawReal as RawScalarTrait>::ValidationErrors>,
658{
659    const PRECISION: u32 = PRECISION;
660}
661
662impl<RawComplex: RawComplexTrait, const PRECISION: u32> ValidationPolicyComplex
663    for StrictFinitePolicy<RawComplex, PRECISION>
664where
665    StrictFinitePolicy<RawComplex, PRECISION>: ValidationPolicy<
666            Value = RawComplex,
667            Error = <RawComplex as RawScalarTrait>::ValidationErrors,
668        >,
669{
670    const PRECISION: u32 = PRECISION;
671}
672
673//--------------------------------------------------------------------------------------------------
674// Rug Implementations (conditional compilation)
675//--------------------------------------------------------------------------------------------------
676
677#[cfg(feature = "rug")]
678pub(crate) mod rug_impls {
679    use super::*; // Imports items from the parent module (validation)
680
681    /// Ensures the [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) value is strictly finite.
682    ///
683    /// This policy checks if the underlying [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) value:
684    /// - Is not NaN (Not a Number).
685    /// - Is not positive nor negative Infinity.
686    /// - Has the specified precision (equal to `PRECISION`).
687    ///
688    /// Note: [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) does not have a direct concept of "subnormal" in the same
689    /// way IEEE 754 floats do; it maintains its specified precision.
690    ///
691    /// # Errors
692    ///
693    /// Returns [`ErrorsValidationRawReal<rug::Float>`](ErrorsValidationRawReal)
694    /// if the value fails any of these checks.
695    impl<const PRECISION: u32> ValidationPolicy for StrictFinitePolicy<rug::Float, PRECISION> {
696        type Value = rug::Float;
697        type Error = ErrorsValidationRawReal<rug::Float>;
698
699        fn validate_ref(value: &rug::Float) -> Result<(), Self::Error> {
700            let actual_precision = value.prec();
701            if actual_precision == PRECISION {
702                // rug::Float uses std::num::FpCategory via its own classify method
703                match value.classify() {
704                    FpCategory::Infinite => {
705                        if value.is_sign_positive() {
706                            Err(ErrorsValidationRawReal::IsPosInfinity {
707                                backtrace: capture_backtrace(),
708                            })
709                        } else {
710                            Err(ErrorsValidationRawReal::IsNegInfinity {
711                                backtrace: capture_backtrace(),
712                            })
713                        }
714                    }
715                    FpCategory::Nan => Err(ErrorsValidationRawReal::IsNaN {
716                        value: value.clone(),
717                        backtrace: capture_backtrace(),
718                    }),
719                    FpCategory::Subnormal => Err(ErrorsValidationRawReal::IsSubnormal {
720                        value: value.clone(),
721                        backtrace: capture_backtrace(),
722                    }),
723                    FpCategory::Zero | FpCategory::Normal => Ok(()),
724                }
725            } else {
726                Err(ErrorsValidationRawReal::PrecisionMismatch {
727                    input_value: value.clone(),
728                    actual_precision,
729                    requested_precision: PRECISION,
730                    backtrace: capture_backtrace(),
731                })
732            }
733        }
734    }
735
736    /// Ensures both the real and imaginary parts of a [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html) value are strictly finite.
737    ///
738    /// This policy applies the [`StrictFinitePolicy<rug::Float>`](StrictFinitePolicy)
739    /// (which checks the underlying [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)) to both the real and imaginary
740    /// components of the [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html).
741    ///
742    /// # Errors
743    ///
744    /// Returns [`ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>`](ErrorsValidationRawComplex)
745    /// if either the real part, the imaginary part, or both parts fail the
746    /// `StrictFinitePolicy<RealRugStrictFinite<Precision>>` checks.
747    impl<const PRECISION: u32> ValidationPolicy for StrictFinitePolicy<rug::Complex, PRECISION> {
748        type Value = rug::Complex;
749        type Error = ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>;
750
751        fn validate_ref(value: &rug::Complex) -> Result<(), Self::Error> {
752            validate_complex::<StrictFinitePolicy<rug::Float, PRECISION>>(
753                value.real(),
754                value.imag(),
755            )
756        }
757    }
758}
759//-------------------------------------------------------------
760
761//------------------------------------------------------------------------------------------------
762/// Provides a set of fundamental floating-point classification checks.
763///
764/// This trait defines common methods to determine the characteristics of a numerical value,
765/// such as whether it is finite, infinite, NaN (Not a Number), or normal.
766/// It is implemented for standard floating-point types (`f64`), complex numbers
767/// (`num::Complex<f64>`), and their arbitrary-precision counterparts from the `rug`
768/// library (via `RealRugStrictFinite<P>` and `ComplexRugStrictFinite<P>` which internally use [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)
769/// and [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html)) when the "rug" feature is enabled.
770///
771/// `FpChecks` is often used as a supertrait for more general numerical traits like
772/// [`FpScalar`](crate::FpScalar), ensuring that types representing floating-point
773/// scalars can be queried for these basic properties. This is essential for
774/// validation policies and writing robust numerical algorithms.
775///
776/// Note that this trait focuses on these specific classifications. For more detailed
777/// categorization (like distinguishing subnormals or zero explicitly through this trait),
778/// types may provide other methods (e.g., `classify()` for `f64` and [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html),
779/// or `is_zero()` from [`FpScalar`](crate::FpScalar)).
780pub trait FpChecks {
781    /// Returns [`true`] if `self` is finite (i.e., not infinite and not NaN).
782    ///
783    /// For complex numbers, this typically means both the real and imaginary parts are finite.
784    fn is_finite(&self) -> bool;
785
786    /// Returns [`true`] if `self` is positive infinity or negative infinity.
787    ///
788    /// For complex numbers, this typically means at least one part is infinite,
789    /// and no part is NaN.
790    fn is_infinite(&self) -> bool;
791
792    /// Returns [`true`] if `self` is NaN (Not a Number).
793    ///
794    /// For complex numbers, this typically means at least one part is NaN.
795    fn is_nan(&self) -> bool;
796
797    /// Returns [`true`] if `self` is a *normal* number.
798    ///
799    /// A normal number is a finite, non-zero number that is not subnormal.
800    /// For complex numbers, this typically means both real and imaginary parts are normal
801    /// numbers (and thus individually finite, non-zero, and not subnormal).
802    fn is_normal(&self) -> bool;
803}
804
805#[duplicate_item(
806    T;
807    [f64];
808    [Complex::<f64>];
809)]
810impl FpChecks for T {
811    #[inline(always)]
812    fn is_finite(&self) -> bool {
813        T::is_finite(*self)
814    }
815
816    #[inline(always)]
817    fn is_infinite(&self) -> bool {
818        T::is_infinite(*self)
819    }
820
821    #[inline(always)]
822    fn is_nan(&self) -> bool {
823        T::is_nan(*self)
824    }
825
826    #[inline(always)]
827    fn is_normal(&self) -> bool {
828        T::is_normal(*self)
829    }
830}
831//------------------------------------------------------------------------------------------------
832
833//------------------------------------------------------------------------------------------------
834/// A validation policy that is active only in debug builds.
835///
836/// This struct acts as a wrapper around another [`ValidationPolicy`].
837///
838/// - In **debug builds** (`#[cfg(debug_assertions)]`), it delegates validation
839///   directly to the inner policy `P`.
840/// - In **release builds** (`#[cfg(not(debug_assertions))]`), it becomes a "no-op"
841///   (no operation), meaning its `validate` and `validate_ref` methods do nothing
842///   and always return `Ok`.
843///
844/// This is useful for enforcing strict, potentially expensive validations during
845/// development and testing, while eliminating their performance overhead in production code.
846///
847/// # Generic Parameters
848///
849/// - `P`: The inner [`ValidationPolicy`] to use during debug builds.
850///
851/// # Example
852///
853/// ```rust,ignore
854/// use num_valid::{validation::{DebugValidationPolicy, StrictFinitePolicy, Native64RawRealStrictFinitePolicy}};
855/// use try_create::ValidationPolicy;
856///
857/// // Define a policy that uses Native64RawRealStrictFinitePolicy
858/// // (i.e. StrictFinitePolicy<f64, 53>) only in debug builds.
859/// type MyPolicy = DebugValidationPolicy<Native64RawRealStrictFinitePolicy>;
860///
861/// let nan_value = f64::NAN;
862/// let result = MyPolicy::validate(nan_value);
863///
864/// #[cfg(debug_assertions)]
865/// {
866///     // In debug mode, the validation fails because StrictFinitePolicy catches NaN.
867///     assert!(result.is_err());
868/// }
869///
870/// #[cfg(not(debug_assertions))]
871/// {
872///     // In release mode, the validation is skipped and always succeeds.
873///     assert!(result.is_ok());
874/// }
875/// ```
876pub struct DebugValidationPolicy<P: ValidationPolicy>(PhantomData<P>);
877
878impl<P: ValidationPolicy> ValidationPolicy for DebugValidationPolicy<P> {
879    type Value = P::Value;
880    type Error = P::Error;
881
882    #[inline(always)]
883    fn validate_ref(value: &Self::Value) -> Result<(), Self::Error> {
884        // In debug builds, this implementation delegates to the inner policy `P`.
885        #[cfg(debug_assertions)]
886        {
887            // Delegate to the inner policy's validation logic.
888            P::validate_ref(value)
889        }
890        #[cfg(not(debug_assertions))]
891        {
892            let _ = value; // Avoid unused variable warning in release mode.
893            // In release mode, this is a no-op.
894            // The validation is skipped, and we always return Ok.
895            // This allows the compiler to optimize away the validation logic entirely.
896            // This is useful for performance-critical code where validation is not needed in production.
897            // Note: The `value` is not used here, but we keep it to match the signature of `validate_ref`.
898            // This way, we can still call this method without needing to change the function signature.
899            // This is particularly useful when this policy is used in generic contexts
900            // where the `validate_ref` method is expected to take a reference to the value.
901            // The `PhantomData` ensures that the type system knows about `P` even
902            // though we don't use it directly in the release build.
903            // This is a common pattern in Rust to create zero-sized types that carry type information.
904            Ok(())
905        }
906    }
907}
908
909impl<P> ValidationPolicyReal for DebugValidationPolicy<P>
910where
911    P: ValidationPolicyReal,
912{
913    const PRECISION: u32 = P::PRECISION;
914}
915
916impl<P> ValidationPolicyComplex for DebugValidationPolicy<P>
917where
918    P: ValidationPolicyComplex,
919{
920    const PRECISION: u32 = P::PRECISION;
921}
922//------------------------------------------------------------------------------------------------
923
924/// Validates a value using the given policy, but only in debug builds.
925/// In release builds, this function is a no-op and should be optimized away.
926///
927/// # Panics
928///
929/// In debug builds, this function will panic if `P::validate_ref(value)` returns an `Err`.
930#[inline(always)]
931pub fn debug_validate<P: ValidationPolicy>(value: &P::Value, msg: &str) {
932    #[cfg(debug_assertions)]
933    {
934        if let Err(e) = P::validate_ref(value) {
935            panic!("Debug validation of {msg} value failed: {e}");
936        }
937    }
938    #[cfg(not(debug_assertions))]
939    {
940        // These are no-op in release builds
941        let _ = value;
942        let _ = msg;
943    }
944}
945//-------------------------------------------------------------
946
947//-------------------------------------------------------------
948/// A marker trait for validation policies that guarantee finite real values.
949///
950/// This trait serves as a compile-time marker to identify policies that ensure
951/// all validated real values are always finite (never NaN or infinite). It enables
952/// the implementation of full equality ([`Eq`]) and hashing ([`Hash`]) for
953/// validated types, allowing them to be used as keys in hash-based collections
954/// like [`HashMap`](std::collections::HashMap) and [`HashSet`](std::collections::HashSet).
955///
956/// ## Enabled Features
957///
958/// When a validation policy implements this trait, the corresponding validated types
959/// (like [`RealValidated<K>`](crate::RealValidated)) automatically gain:
960///
961/// - **Full Equality ([`Eq`])**: Equality becomes reflexive, symmetric, and transitive
962///   since NaN values (which violate these properties) are excluded.
963/// - **Hashing ([`Hash`])**: Combined with [`Eq`], this allows validated types to be
964///   used as keys in hash-based collections like [`HashMap`](std::collections::HashMap) and [`HashSet`](std::collections::HashSet).
965///
966/// ## Hashing Implementation Details
967///
968/// The [`Hash`] implementation for validated types:
969/// - Delegates to the underlying raw type's [`RawScalarTrait::compute_hash`] method
970/// - Handles IEEE 754 floating-point edge cases correctly (e.g., signed zeros)
971/// - Maintains the contract that `a == b` implies `hash(a) == hash(b)`
972/// - Works with both native `f64`/`Complex<f64>` and arbitrary-precision `rug` types
973///
974/// ## Design Decision: No Total Ordering
975///
976/// This trait intentionally does **not** enable total ordering ([`Ord`]) because:
977/// 1. The library's comparison functions use efficient reference-based [`PartialOrd`]
978/// 2. [`Max`](crate::functions::Max)/[`Min`](crate::functions::Min) traits provide better performance than value-based [`Ord`]
979/// 3. Mathematical operations work naturally with partial ordering semantics
980///
981/// ## Use Cases
982///
983/// This marker trait enables validated numerical types to be used in contexts that
984/// require [`Eq`] and [`Hash`]:
985///
986/// ```rust
987/// use num_valid::{RealNative64StrictFinite, functions::Max};
988/// use std::collections::{HashMap, HashSet};
989/// use try_create::TryNew;
990///
991/// // Use as HashMap keys (requires Eq + Hash)
992/// let mut scores = HashMap::new();
993/// let player1 = RealNative64StrictFinite::try_new(1.5).unwrap();
994/// scores.insert(player1, 100);
995///
996/// // Use in HashSet (requires Eq + Hash)
997/// let mut unique_values = HashSet::new();
998/// unique_values.insert(RealNative64StrictFinite::try_new(3.14).unwrap());
999///
1000/// // Comparison still works efficiently with PartialOrd
1001/// let a = RealNative64StrictFinite::try_new(1.0).unwrap();
1002/// let b = RealNative64StrictFinite::try_new(2.0).unwrap();
1003/// assert!(a < b);  // Uses PartialOrd
1004/// assert_eq!(a.max(&b), &b);  // Uses library's Max trait
1005/// ```
1006///
1007/// ## Hash Collision Considerations
1008///
1009/// The hashing implementation ensures:
1010/// - Consistent hashes for mathematically equal values
1011/// - Proper handling of floating-point edge cases
1012/// - Compatibility with both native and arbitrary-precision backends
1013/// - No hash collisions due to NaN values (which are excluded by design)
1014///
1015/// ## Currently Implemented By
1016///
1017/// - [`StrictFinitePolicy<T, P>`](StrictFinitePolicy): The primary policy that
1018///   rejects NaN, infinity, and subnormal values.
1019/// - [`DebugValidationPolicy<StrictFinitePolicy<T, P>>`](DebugValidationPolicy):
1020///   A wrapper that applies strict validation only in debug builds.
1021///
1022/// ## Safety and Correctness
1023///
1024/// This trait should only be implemented by validation policies that genuinely
1025/// guarantee finite values. Incorrect implementation could lead to hash collisions
1026/// or violated equality contracts in the methods that rely on this guarantee.
1027///
1028/// The trait is designed as a marker trait (no methods) to emphasize that it's
1029/// purely a compile-time contract rather than runtime functionality.
1030pub trait GuaranteesFiniteValues: ValidationPolicyReal {}
1031
1032/// Marker trait indicating that a validation policy guarantees finite complex values.
1033///
1034/// This trait serves as a compile-time indicator that a validation policy for
1035/// complex numbers ensures all validated values have finite real and imaginary components
1036/// (i.e., neither component is NaN or infinity). This guarantee enables the
1037/// implementation of [`Eq`] and [`Hash`] for [`ComplexValidated`] types, allowing
1038/// them to be used as keys in hash-based collections like [`HashMap`](std::collections::HashMap)
1039/// and [`HashSet`](std::collections::HashSet).
1040///
1041/// ## Purpose
1042///
1043/// Complex numbers can have NaN or infinite components, making equality comparison
1044/// problematic (NaN != NaN). By guaranteeing finiteness at the policy level,
1045/// this trait allows:
1046/// - Full equality ([`Eq`]) implementation for complex validated types
1047/// - Consistent hashing ([`Hash`]) for use in hash-based collections
1048/// - Compile-time verification of these properties
1049///
1050/// ## Design Rationale
1051///
1052/// This trait works in tandem with [`GuaranteesFiniteValues`] for real numbers:
1053/// - [`GuaranteesFiniteValues`]: For real number policies
1054/// - [`GuaranteesFiniteComplexValues`]: For complex number policies
1055///
1056/// This separation allows different validation strategies for real and complex
1057/// components while maintaining type safety.
1058///
1059/// ## Currently Implemented By
1060///
1061/// - [`StrictFinitePolicy<T, P>`](StrictFinitePolicy): Validates both real and imaginary
1062///   components for finiteness and non-subnormal values.
1063/// - [`DebugValidationPolicy<StrictFinitePolicy<T, P>>`](DebugValidationPolicy):
1064///   Applies strict validation only in debug builds.
1065///
1066/// ## Safety and Correctness
1067///
1068/// This trait should only be implemented by validation policies that genuinely
1069/// guarantee finite values in both components. Incorrect implementation could lead
1070/// to hash collisions or violated equality contracts.
1071pub trait GuaranteesFiniteComplexValues: ValidationPolicyComplex {}
1072
1073// Implement the marker for `StrictFinitePolicy` for any real scalar type.
1074impl<RawReal: RawRealTrait, const PRECISION: u32> GuaranteesFiniteValues
1075    for StrictFinitePolicy<RawReal, PRECISION>
1076where
1077    // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteValues
1078    StrictFinitePolicy<RawReal, PRECISION>: ValidationPolicyReal,
1079{
1080}
1081
1082// Implement the marker for `DebugValidationPolicy<StrictFinitePolicy>` for any real scalar type.
1083impl<RawReal: RawRealTrait, const PRECISION: u32> GuaranteesFiniteValues
1084    for DebugValidationPolicy<StrictFinitePolicy<RawReal, PRECISION>>
1085where
1086    // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteValues
1087    StrictFinitePolicy<RawReal, PRECISION>: ValidationPolicyReal,
1088{
1089}
1090
1091// Implement the marker for `StrictFinitePolicy` for any complex scalar type.
1092impl<RawComplex: RawComplexTrait, const PRECISION: u32> GuaranteesFiniteComplexValues
1093    for StrictFinitePolicy<RawComplex, PRECISION>
1094where
1095    // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteComplexValues
1096    StrictFinitePolicy<RawComplex, PRECISION>: ValidationPolicyComplex,
1097{
1098}
1099
1100// Implement the marker for `DebugValidationPolicy<StrictFinitePolicy>` for any complex scalar type.
1101impl<RawComplex: RawComplexTrait, const PRECISION: u32> GuaranteesFiniteComplexValues
1102    for DebugValidationPolicy<StrictFinitePolicy<RawComplex, PRECISION>>
1103where
1104    // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteComplexValues
1105    StrictFinitePolicy<RawComplex, PRECISION>: ValidationPolicyComplex,
1106{
1107}
1108//-------------------------------------------------------------
1109
1110//-------------------------------------------------------------
1111/// Implements total equality (`Eq`) for `RealValidated` types.
1112///
1113/// This implementation is only available when the kernel's `RealPolicy`
1114/// implements the `GuaranteesFiniteValues` marker trait. This ensures that
1115/// the underlying value is never `NaN`, making the equality relation reflexive,
1116/// symmetric, and transitive, as required by `Eq`.
1117impl<K: NumKernel> Eq for RealValidated<K>
1118where
1119    K::RealPolicy: GuaranteesFiniteValues,
1120{
1121    // `Eq` is a marker trait and requires no methods. Its presence simply
1122    // asserts that `PartialEq`'s `eq` method forms an equivalence relation.
1123}
1124
1125/// Implements total equality (`Eq`) for `ComplexValidated` types.
1126///
1127/// This implementation is only available when the kernel's `ComplexPolicy`
1128/// implements the `GuaranteesFiniteComplexValues` marker trait. This ensures that
1129/// the underlying complex number components are never `NaN`, making the equality
1130/// relation reflexive, symmetric, and transitive, as required by `Eq`.
1131impl<K: NumKernel> Eq for ComplexValidated<K>
1132where
1133    K::ComplexPolicy: GuaranteesFiniteComplexValues,
1134{
1135    // `Eq` is a marker trait and requires no methods. Its presence simply
1136    // asserts that `PartialEq`'s `eq` method forms an equivalence relation.
1137}
1138//-------------------------------------------------------------
1139
1140//-------------------------------------------------------------
1141/// Implements [`Hash`] for validated real number types with finite value guarantees.
1142///
1143/// This implementation is only available when the kernel's `RealPolicy`
1144/// implements the [`GuaranteesFiniteValues`] marker trait. This ensures that
1145/// the underlying value is never `NaN`, making hash values consistent and
1146/// allowing these types to be used as keys in hash-based collections.
1147///
1148/// ## Hashing Strategy
1149///
1150/// The implementation delegates to the [`RawScalarTrait::compute_hash()`] method provided by the
1151/// underlying raw scalar type (via [`RawScalarTrait`]). This method:
1152///
1153/// - For `f64` types: Uses IEEE 754 bit representation via `f64::to_bits()`
1154/// - For `rug::Float` types: Uses integer representation via `rug::Float::to_integer()`
1155/// - Handles signed zeros correctly (both `+0.0` and `-0.0` produce the same hash)
1156/// - Maintains the hash contract: `a == b` implies `hash(a) == hash(b)`
1157///
1158/// ## Safety and Correctness
1159///
1160/// This implementation is safe because:
1161/// - [`GuaranteesFiniteValues`] ensures no `NaN` values can exist
1162/// - The [`Eq`] implementation is well-defined for finite values
1163/// - Signed zeros are handled consistently in both equality and hashing
1164///
1165/// ## Usage Example
1166///
1167/// ```rust
1168/// use num_valid::RealNative64StrictFinite;
1169/// use std::collections::HashMap;
1170/// use try_create::TryNew;
1171///
1172/// let mut map = HashMap::new();
1173/// let key = RealNative64StrictFinite::try_new(3.14).unwrap();
1174/// map.insert(key, "pi approximation");
1175/// assert_eq!(map.len(), 1);
1176/// ```
1177impl<K: NumKernel> Hash for RealValidated<K>
1178where
1179    K::RealPolicy: GuaranteesFiniteValues,
1180{
1181    #[inline(always)]
1182    fn hash<H: Hasher>(&self, state: &mut H) {
1183        self.value.compute_hash(state);
1184    }
1185}
1186
1187/// Implements [`Hash`] for validated complex number types with finite value guarantees.
1188///
1189/// This implementation is only available when the kernel's `ComplexPolicy`
1190/// implements the [`GuaranteesFiniteComplexValues`] marker trait. This ensures that
1191/// both the real and imaginary parts are never `NaN`, making hash values
1192/// consistent and allowing these types to be used as keys in hash-based collections.
1193///
1194/// ## Hashing Strategy
1195///
1196/// The implementation delegates to the [`RawScalarTrait::compute_hash()`] method provided by the
1197/// underlying raw complex type (via [`RawComplexTrait`]). This method:
1198///
1199/// - For `Complex<f64>` types: Hashes real and imaginary parts sequentially using `f64::to_bits()`
1200/// - For `rug::Complex` types: Hashes real and imaginary components using their `compute_hash()` methods
1201/// - Handles signed zeros correctly in both real and imaginary parts
1202/// - Maintains the hash contract: `a == b` implies `hash(a) == hash(b)`
1203///
1204/// ## Safety and Correctness
1205///
1206/// This implementation is safe because:
1207/// - [`GuaranteesFiniteComplexValues`] ensures no `NaN` values can exist in either component
1208/// - The [`Eq`] implementation is well-defined for finite complex values
1209/// - Signed zeros are handled consistently in both equality and hashing
1210/// - Complex numbers with different component values produce different hashes
1211///
1212/// ## Usage Example
1213///
1214/// ```rust
1215/// use num_valid::ComplexNative64StrictFinite;
1216/// use num::Complex;
1217/// use std::collections::HashMap;
1218/// use try_create::TryNew;
1219///
1220/// let mut map = HashMap::new();
1221/// let key = ComplexNative64StrictFinite::try_new(Complex::new(1.0, 2.0)).unwrap();
1222/// map.insert(key, "complex value");
1223/// assert_eq!(map.len(), 1);
1224/// ```
1225impl<K: NumKernel> Hash for ComplexValidated<K>
1226where
1227    K::ComplexPolicy: GuaranteesFiniteComplexValues,
1228{
1229    #[inline(always)]
1230    fn hash<H: Hasher>(&self, state: &mut H) {
1231        self.value.compute_hash(state);
1232    }
1233}
1234//-------------------------------------------------------------
1235
1236//-------------------------------------------------------------
1237#[cfg(test)]
1238mod tests {
1239    use super::*;
1240
1241    #[cfg(feature = "rug")]
1242    use crate::{ComplexRugStrictFinite, RealRugStrictFinite};
1243
1244    mod fp_checks {
1245        use super::*;
1246        use num::Complex;
1247
1248        mod native64 {
1249            use super::*;
1250
1251            mod real {
1252                use super::*;
1253
1254                #[test]
1255                fn test_is_finite() {
1256                    assert!(<f64 as FpChecks>::is_finite(&1.0));
1257                    assert!(!<f64 as FpChecks>::is_finite(&f64::INFINITY));
1258                    assert!(!<f64 as FpChecks>::is_finite(&f64::NEG_INFINITY));
1259                    assert!(!<f64 as FpChecks>::is_finite(&f64::NAN));
1260                }
1261
1262                #[test]
1263                fn test_is_infinite() {
1264                    assert!(!<f64 as FpChecks>::is_infinite(&1.0));
1265                    assert!(<f64 as FpChecks>::is_infinite(&f64::INFINITY));
1266                    assert!(<f64 as FpChecks>::is_infinite(&f64::NEG_INFINITY));
1267                    assert!(!<f64 as FpChecks>::is_infinite(&f64::NAN));
1268                }
1269
1270                #[test]
1271                fn test_is_nan() {
1272                    assert!(!<f64 as FpChecks>::is_nan(&1.0));
1273                    assert!(!<f64 as FpChecks>::is_nan(&f64::INFINITY));
1274                    assert!(!<f64 as FpChecks>::is_nan(&f64::NEG_INFINITY));
1275                    assert!(<f64 as FpChecks>::is_nan(&f64::NAN));
1276                }
1277
1278                #[test]
1279                fn test_is_normal() {
1280                    assert!(<f64 as FpChecks>::is_normal(&1.0));
1281                    assert!(!<f64 as FpChecks>::is_normal(&0.0));
1282                    assert!(!<f64 as FpChecks>::is_normal(&f64::INFINITY));
1283                    assert!(!<f64 as FpChecks>::is_normal(&f64::NEG_INFINITY));
1284                    assert!(!<f64 as FpChecks>::is_normal(&f64::NAN));
1285                    assert!(<f64 as FpChecks>::is_normal(&f64::MIN_POSITIVE));
1286                }
1287            }
1288
1289            mod complex {
1290                use super::*;
1291
1292                #[test]
1293                fn test_is_finite() {
1294                    assert!(<Complex<f64> as FpChecks>::is_finite(&Complex::new(
1295                        1.0, 1.0
1296                    )));
1297                    assert!(!<Complex<f64> as FpChecks>::is_finite(&Complex::new(
1298                        f64::INFINITY,
1299                        1.0
1300                    )));
1301                    assert!(!<Complex<f64> as FpChecks>::is_finite(&Complex::new(
1302                        1.0,
1303                        f64::NAN
1304                    )));
1305                }
1306
1307                #[test]
1308                fn test_is_infinite() {
1309                    assert!(!<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
1310                        1.0, 1.0
1311                    )));
1312                    assert!(<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
1313                        f64::INFINITY,
1314                        1.0
1315                    )));
1316                    assert!(!<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
1317                        1.0,
1318                        f64::NAN
1319                    )));
1320                }
1321
1322                #[test]
1323                fn test_is_nan() {
1324                    assert!(!<Complex<f64> as FpChecks>::is_nan(&Complex::new(1.0, 1.0)));
1325                    assert!(<Complex<f64> as FpChecks>::is_nan(&Complex::new(
1326                        f64::NAN,
1327                        1.0
1328                    )));
1329                    assert!(<Complex<f64> as FpChecks>::is_nan(&Complex::new(
1330                        1.0,
1331                        f64::NAN
1332                    )));
1333                }
1334
1335                #[test]
1336                fn test_is_normal() {
1337                    assert!(<Complex<f64> as FpChecks>::is_normal(&Complex::new(
1338                        1.0, 1.0
1339                    )));
1340                    assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
1341                        0.0, 0.0
1342                    )));
1343                    assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
1344                        f64::INFINITY,
1345                        1.0
1346                    )));
1347                    assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
1348                        1.0,
1349                        f64::NAN
1350                    )));
1351                }
1352            }
1353        }
1354    }
1355
1356    mod strict_finite_policy {
1357        use super::*;
1358
1359        mod raw_types {
1360            use super::*;
1361
1362            mod native64 {
1363                use super::*;
1364
1365                mod real {
1366                    use super::*;
1367
1368                    #[test]
1369                    fn f64_infinity() {
1370                        let result = StrictFinitePolicy::<f64, 53>::validate(f64::INFINITY);
1371                        assert!(matches!(
1372                            result,
1373                            Err(ErrorsValidationRawReal::IsPosInfinity { .. })
1374                        ));
1375
1376                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&f64::INFINITY);
1377                        assert!(matches!(
1378                            result,
1379                            Err(ErrorsValidationRawReal::IsPosInfinity { .. })
1380                        ));
1381
1382                        let result = StrictFinitePolicy::<f64, 53>::validate(f64::NEG_INFINITY);
1383                        assert!(matches!(
1384                            result,
1385                            Err(ErrorsValidationRawReal::IsNegInfinity { .. })
1386                        ));
1387
1388                        let result =
1389                            StrictFinitePolicy::<f64, 53>::validate_ref(&f64::NEG_INFINITY);
1390                        assert!(matches!(
1391                            result,
1392                            Err(ErrorsValidationRawReal::IsNegInfinity { .. })
1393                        ));
1394                    }
1395
1396                    #[test]
1397                    fn f64_nan() {
1398                        let result = StrictFinitePolicy::<f64, 53>::validate(f64::NAN);
1399                        assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
1400
1401                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&f64::NAN);
1402                        assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
1403                    }
1404
1405                    #[test]
1406                    fn f64_subnormal() {
1407                        // let subnormal = f64::MIN_POSITIVE / 2.0;
1408                        let subnormal = f64::from_bits(1); // the smallest positive subnormal
1409
1410                        let result = StrictFinitePolicy::<f64, 53>::validate(subnormal);
1411                        assert!(matches!(
1412                            result,
1413                            Err(ErrorsValidationRawReal::<f64>::IsSubnormal { .. })
1414                        ));
1415
1416                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&subnormal);
1417                        assert!(matches!(
1418                            result,
1419                            Err(ErrorsValidationRawReal::<f64>::IsSubnormal { .. })
1420                        ));
1421                    }
1422
1423                    #[test]
1424                    fn f64_zero() {
1425                        let value = 0.0;
1426                        let result = StrictFinitePolicy::<f64, 53>::validate(value);
1427                        assert!(matches!(result, Ok(0.0)));
1428                    }
1429
1430                    #[test]
1431                    fn f64_normal() {
1432                        let value = 1.0;
1433                        let result = StrictFinitePolicy::<f64, 53>::validate(value);
1434                        assert!(matches!(result, Ok(1.0)));
1435
1436                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&value);
1437                        assert!(matches!(result, Ok(())));
1438                    }
1439                }
1440
1441                mod complex {
1442                    use super::*;
1443                    use num::Complex;
1444
1445                    #[test]
1446                    fn test_invalid_both_parts() {
1447                        let z = Complex::new(f64::NAN, f64::NEG_INFINITY);
1448                        let err =
1449                            StrictFinitePolicy::<Complex<f64>, 53>::validate_ref(&z).unwrap_err();
1450                        matches!(err, ErrorsValidationRawComplex::InvalidBothParts { .. });
1451                    }
1452
1453                    #[test]
1454                    fn complex_f64_invalid_real_part() {
1455                        let value = Complex::new(f64::INFINITY, 1.0);
1456                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1457                        assert!(matches!(
1458                            result,
1459                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1460                                source: ErrorsValidationRawReal::IsPosInfinity { .. },
1461                                ..
1462                            })
1463                        ));
1464
1465                        let value = Complex::new(f64::NAN, 1.0);
1466                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1467                        assert!(matches!(
1468                            result,
1469                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1470                                source: ErrorsValidationRawReal::IsNaN { .. },
1471                                ..
1472                            })
1473                        ));
1474
1475                        let value = Complex::new(f64::MIN_POSITIVE / 2.0, 1.0);
1476                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1477                        assert!(matches!(
1478                            result,
1479                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1480                                source: ErrorsValidationRawReal::IsSubnormal { .. },
1481                                ..
1482                            })
1483                        ));
1484                    }
1485
1486                    #[test]
1487                    fn complex_f64_invalid_imaginary_part() {
1488                        let value = Complex::new(1.0, f64::INFINITY);
1489                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1490                        assert!(matches!(
1491                            result,
1492                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1493                                source: ErrorsValidationRawReal::IsPosInfinity { .. },
1494                                ..
1495                            })
1496                        ));
1497
1498                        let value = Complex::new(1.0, f64::NAN);
1499                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1500                        assert!(matches!(
1501                            result,
1502                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1503                                source: ErrorsValidationRawReal::IsNaN { .. },
1504                                ..
1505                            })
1506                        ));
1507
1508                        let value = Complex::new(1.0, f64::MIN_POSITIVE / 2.0);
1509                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1510                        assert!(matches!(
1511                            result,
1512                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1513                                source: ErrorsValidationRawReal::IsSubnormal { .. },
1514                                ..
1515                            })
1516                        ));
1517                    }
1518
1519                    #[test]
1520                    fn complex_f64_zero() {
1521                        let value = Complex::new(0.0, 0.0);
1522                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1523                        assert!(result.is_ok());
1524                    }
1525
1526                    #[test]
1527                    fn complex_f64_normal_value() {
1528                        let value = Complex::new(1.0, 1.0);
1529                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1530                        assert!(result.is_ok());
1531                    }
1532                }
1533            }
1534
1535            #[cfg(feature = "rug")]
1536            mod rug53 {
1537                use super::*;
1538
1539                const PRECISION: u32 = 53; // Default precision for rug tests
1540
1541                mod float {
1542                    use super::*;
1543                    use rug::Float;
1544
1545                    #[test]
1546                    fn rug_float_valid() {
1547                        let x = Float::with_val(PRECISION, 1.0);
1548                        assert!(StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).is_ok());
1549                        assert_eq!(
1550                            StrictFinitePolicy::<Float, PRECISION>::validate(x.clone()).unwrap(),
1551                            x
1552                        );
1553                    }
1554
1555                    #[test]
1556                    fn rug_float_nan() {
1557                        let x = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1558                        let err =
1559                            StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
1560                        assert!(matches!(err, ErrorsValidationRawReal::IsNaN { .. }));
1561                        let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
1562                        assert!(matches!(err, ErrorsValidationRawReal::IsNaN { .. }));
1563                    }
1564
1565                    #[test]
1566                    fn rug_float_infinity() {
1567                        use rug::Float;
1568
1569                        let x = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1570                        let err =
1571                            StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
1572                        assert!(matches!(err, ErrorsValidationRawReal::IsPosInfinity { .. }));
1573                        let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
1574                        assert!(matches!(err, ErrorsValidationRawReal::IsPosInfinity { .. }));
1575
1576                        let x = Float::with_val(PRECISION, Float::parse("-inf").unwrap());
1577                        let err =
1578                            StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
1579                        assert!(matches!(err, ErrorsValidationRawReal::IsNegInfinity { .. }));
1580                        let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
1581                        assert!(matches!(err, ErrorsValidationRawReal::IsNegInfinity { .. }));
1582                    }
1583
1584                    #[test]
1585                    fn rug_float_zero() {
1586                        let value = Float::with_val(PRECISION, 0.0);
1587                        let result = StrictFinitePolicy::<Float, PRECISION>::validate_ref(&value);
1588                        assert!(result.is_ok());
1589                        let result =
1590                            StrictFinitePolicy::<Float, PRECISION>::validate(value.clone())
1591                                .unwrap();
1592                        assert_eq!(result, value);
1593                    }
1594                }
1595
1596                mod complex {
1597                    use super::*;
1598                    use rug::{Complex, Float};
1599
1600                    #[test]
1601                    fn rug_complex_valid() {
1602                        let z = Complex::with_val(PRECISION, (1.0, -2.0));
1603                        assert!(StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).is_ok());
1604                        assert_eq!(
1605                            StrictFinitePolicy::<Complex, PRECISION>::validate(z.clone()).unwrap(),
1606                            z
1607                        );
1608                    }
1609
1610                    #[test]
1611                    fn rug_complex_real_invalid() {
1612                        let nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1613                        let z = Complex::with_val(PRECISION, (&nan, 1.0));
1614                        let err =
1615                            StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
1616                        assert!(matches!(
1617                            err,
1618                            ErrorsValidationRawComplex::InvalidRealPart {
1619                                source: ErrorsValidationRawReal::IsNaN { .. }
1620                            }
1621                        ));
1622                    }
1623
1624                    #[test]
1625                    fn rug_complex_imag_invalid() {
1626                        let inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1627                        let z = Complex::with_val(PRECISION, (1.0, &inf));
1628                        let err =
1629                            StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
1630                        assert!(matches!(
1631                            err,
1632                            ErrorsValidationRawComplex::InvalidImaginaryPart {
1633                                source: ErrorsValidationRawReal::IsPosInfinity { .. }
1634                            }
1635                        ));
1636                    }
1637
1638                    #[test]
1639                    fn rug_complex_both_invalid() {
1640                        let nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1641                        let inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1642                        let z = Complex::with_val(PRECISION, (&nan, &inf));
1643                        let err =
1644                            StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
1645                        assert!(matches!(
1646                            err,
1647                            ErrorsValidationRawComplex::InvalidBothParts {
1648                                real_error: ErrorsValidationRawReal::IsNaN { .. },
1649                                imag_error: ErrorsValidationRawReal::IsPosInfinity { .. },
1650                                ..
1651                            }
1652                        ));
1653                    }
1654
1655                    #[test]
1656                    fn rug_complex_zero() {
1657                        let zero = Complex::with_val(
1658                            PRECISION,
1659                            (
1660                                Float::with_val(PRECISION, 0.0),
1661                                Float::with_val(PRECISION, 0.0),
1662                            ),
1663                        );
1664                        let result = StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&zero);
1665                        assert!(result.is_ok());
1666                        assert_eq!(
1667                            StrictFinitePolicy::<Complex, PRECISION>::validate(zero.clone())
1668                                .unwrap(),
1669                            zero
1670                        );
1671                    }
1672                }
1673            }
1674        }
1675
1676        // Module for testing the wrapper types that implement TryNew
1677        #[cfg(feature = "rug")]
1678        mod wrapper_types {
1679            use super::*;
1680
1681            mod rug_wrappers {
1682                use super::*;
1683                use crate::RealScalar;
1684                use num::One;
1685                use rug::{Complex as RugComplex, Float};
1686                use try_create::{IntoInner, TryNew};
1687
1688                const PRECISION: u32 = 53; // Default precision for rug tests
1689
1690                mod real {
1691                    use super::*;
1692
1693                    #[test]
1694                    fn try_new_real_rug_correct_precision() {
1695                        // Test RealRugStrictFinite with valid rug::Float and correct precision
1696                        let val = Float::with_val(PRECISION, 1.0);
1697                        let res = RealRugStrictFinite::<PRECISION>::try_new(val.clone());
1698                        assert!(res.is_ok(),);
1699                        assert_eq!(res.unwrap().into_inner(), val);
1700                    }
1701
1702                    #[test]
1703                    fn try_new_real_rug_incorrect_precision() {
1704                        // Test RealRugStrictFinite with valid rug::Float but incorrect precision
1705                        let val_wrong_prec = Float::with_val(PRECISION + 10, 1.0); // Different precision
1706                        let res = RealRugStrictFinite::<PRECISION>::try_new(val_wrong_prec);
1707                        assert!(res.is_err());
1708                        matches!(res, Err(ErrorsValidationRawReal::PrecisionMismatch { actual_precision, .. })
1709                            if actual_precision == PRECISION + 10
1710                        );
1711                    }
1712
1713                    #[test]
1714                    fn try_new_real_rug_nan() {
1715                        // Test RealRugStrictFinite with rug::Float NaN
1716                        let val_nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1717                        let res = RealRugStrictFinite::<PRECISION>::try_new(val_nan);
1718                        assert!(res.is_err());
1719                        assert!(matches!(
1720                            res,
1721                            Err(ErrorsValidationRawReal::<Float>::IsNaN { .. })
1722                        ));
1723                    }
1724
1725                    #[test]
1726                    fn try_new_real_rug_infinity() {
1727                        // Test RealRugStrictFinite with rug::Float positive infinity
1728                        let val_inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1729                        let res_pos_inf = RealRugStrictFinite::<PRECISION>::try_new(val_inf);
1730                        assert!(res_pos_inf.is_err());
1731                        assert!(matches!(
1732                            res_pos_inf,
1733                            Err(ErrorsValidationRawReal::<Float>::IsPosInfinity { .. })
1734                        ));
1735
1736                        // Test RealRugStrictFinite with rug::Float negative infinity
1737                        let val_neg_inf = Float::with_val(PRECISION, Float::parse("-inf").unwrap());
1738                        let res_neg_inf = RealRugStrictFinite::<PRECISION>::try_new(val_neg_inf);
1739                        assert!(res_neg_inf.is_err());
1740                        assert!(matches!(
1741                            res_neg_inf,
1742                            Err(ErrorsValidationRawReal::<Float>::IsNegInfinity { .. })
1743                        ));
1744                    }
1745                }
1746
1747                mod complex {
1748                    use super::*;
1749                    use crate::ComplexScalarGetParts;
1750
1751                    #[test]
1752                    fn try_new_complex_rug_correct_precision() {
1753                        // Test ComplexRugStrictFinite with valid rug::Complex and correct precision for both parts
1754                        let val = RugComplex::with_val(PRECISION, (1.0, -2.0));
1755                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val.clone());
1756                        assert!(res.is_ok());
1757                        assert_eq!(res.unwrap().into_inner(), val);
1758                    }
1759
1760                    /*
1761                    #[test]
1762                    #[ignore = "because the precision used to construct a rug::Complex override the precision of the real and imaginary parts"]
1763                    fn try_new_complex_rug_precision_mismatch_real() {
1764                        // Test ComplexRugStrictFinite where real part has incorrect precision
1765                        let real_part_wrong_prec = Float::with_val(PRECISION - 1, 1.0);
1766                        let imag_part_correct_prec = Float::with_val(PRECISION, -2.0);
1767                        let val = RugComplex::with_val(
1768                            PRECISION,
1769                            (real_part_wrong_prec, imag_part_correct_prec),
1770                        ); // Construct with Float parts
1771
1772                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1773                        assert!(res.is_err());
1774                        assert!(matches!(
1775                            res,
1776                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1777                                source: ErrorsValidationRawReal::PrecisionMismatch { .. }
1778                            })
1779                        ));
1780                    }
1781
1782                    #[test]
1783                    #[ignore = "because the precision used to construct a rug::Complex override the precision of the real and imaginary parts"]
1784                    fn try_new_complex_rug_precision_mismatch_imag() {
1785                        // Test ComplexRugStrictFinite where imaginary part has incorrect precision
1786                        let real_part_correct_prec = Float::with_val(PRECISION, 1.0);
1787                        let imag_part_wrong_prec = Float::with_val(PRECISION - 1, -2.0);
1788                        let val = RugComplex::with_val(
1789                            PRECISION,
1790                            (real_part_correct_prec, imag_part_wrong_prec),
1791                        ); // Construct with Float parts
1792
1793                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1794                        assert!(res.is_err());
1795                        matches!(res, Err(ErrorsValidationRawComplex::InvalidImaginaryPart { source })
1796                            if matches!(source, ErrorsValidationRawReal::PrecisionMismatch { .. })
1797                        );
1798                    }
1799                    */
1800
1801                    #[test]
1802                    fn try_new_complex_rug_precision_mismatch_both() {
1803                        // Test ComplexRugStrictFinite where both parts have incorrect precision
1804                        let real_part_wrong_prec = Float::with_val(PRECISION - 1, 1.0);
1805                        let imag_part_wrong_prec = Float::with_val(PRECISION - 1, -2.0);
1806                        let val = RugComplex::with_val(
1807                            PRECISION - 1,
1808                            (real_part_wrong_prec, imag_part_wrong_prec),
1809                        ); // Construct with Float parts
1810
1811                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1812                        assert!(res.is_err());
1813                        assert!(matches!(
1814                            res,
1815                            Err(ErrorsValidationRawComplex::InvalidBothParts {
1816                                real_error: ErrorsValidationRawReal::PrecisionMismatch { .. },
1817                                imag_error: ErrorsValidationRawReal::PrecisionMismatch { .. },
1818                                ..
1819                            })
1820                        ));
1821                    }
1822
1823                    #[test]
1824                    fn try_new_complex_rug_invalid_real_nan() {
1825                        // Test ComplexRugStrictFinite where real part is NaN (value validation failure)
1826                        let nan_real = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1827                        let imag_part = Float::with_val(PRECISION, 1.0);
1828                        let val = RugComplex::with_val(PRECISION, (nan_real, imag_part));
1829
1830                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1831                        assert!(res.is_err());
1832                        assert!(matches!(
1833                            res,
1834                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1835                                source: ErrorsValidationRawReal::<Float>::IsNaN { .. },
1836                            })
1837                        ));
1838                    }
1839
1840                    #[test]
1841                    fn try_new_complex_rug_invalid_imag_inf() {
1842                        // Test ComplexRugStrictFinite where imaginary part is Infinity (value validation failure)
1843                        let real_part = Float::with_val(PRECISION, 1.0);
1844                        let inf_imag = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1845                        let val = RugComplex::with_val(PRECISION, (real_part, inf_imag));
1846
1847                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1848                        assert!(res.is_err());
1849                        assert!(matches!(
1850                            res,
1851                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1852                                source: ErrorsValidationRawReal::<Float>::IsPosInfinity { .. }
1853                            })
1854                        ));
1855                    }
1856
1857                    #[test]
1858                    fn try_new_complex_rug_invalid_both() {
1859                        // Test ComplexRugStrictFinite where both parts are invalid (value validation failure)
1860                        let nan_real = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1861                        let neg_inf_imag =
1862                            Float::with_val(PRECISION, Float::parse("-inf").unwrap());
1863                        let val = RugComplex::with_val(PRECISION, (nan_real, neg_inf_imag));
1864
1865                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1866                        assert!(res.is_err());
1867                        assert!(matches!(
1868                            res,
1869                            Err(ErrorsValidationRawComplex::InvalidBothParts {
1870                                real_error: ErrorsValidationRawReal::<Float>::IsNaN { .. },
1871                                imag_error: ErrorsValidationRawReal::<Float>::IsNegInfinity { .. },
1872                                ..
1873                            })
1874                        ));
1875                    }
1876
1877                    #[test]
1878                    fn try_from_complex_f64_to_complex_rug() {
1879                        // Test valid conversion
1880                        let z = num::Complex::new(1., -2.);
1881                        let z_rug = ComplexRugStrictFinite::<PRECISION>::try_from(z).unwrap();
1882                        let expected_real = RealRugStrictFinite::<PRECISION>::one();
1883                        let expected_imaginary =
1884                            RealRugStrictFinite::<PRECISION>::try_from_f64(-2.).unwrap();
1885                        assert_eq!(z_rug.real_part(), expected_real);
1886                        assert_eq!(z_rug.imag_part(), expected_imaginary);
1887
1888                        // Test invalid real part
1889                        let z = num::Complex::new(f64::INFINITY, -2.0);
1890                        let result = ComplexRugStrictFinite::<PRECISION>::try_from(z);
1891                        assert!(result.is_err());
1892                        assert!(matches!(
1893                            result,
1894                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1895                                source: ErrorsTryFromf64::Output {
1896                                    source: ErrorsValidationRawReal::IsPosInfinity { .. }
1897                                },
1898                            })
1899                        ));
1900
1901                        // Test invalid imaginary part
1902                        let z = num::Complex::new(1.0, f64::NAN);
1903                        let result = ComplexRugStrictFinite::<PRECISION>::try_from(z).unwrap_err();
1904                        //println!("result = {result}");
1905                        assert!(matches!(
1906                            result,
1907                            ErrorsValidationRawComplex::InvalidImaginaryPart {
1908                                source: ErrorsTryFromf64::Output {
1909                                    source: ErrorsValidationRawReal::IsNaN { .. }
1910                                },
1911                            }
1912                        ));
1913                    }
1914                }
1915            }
1916        }
1917    }
1918
1919    mod conditional_validation_policy {
1920        use core::f64;
1921
1922        use super::*;
1923
1924        type F64StrictFinitePolicy = Native64RawRealStrictFinitePolicy;
1925
1926        // Define a policy that is strict in debug, and no-op in release.
1927        type MyConditionalPolicy = DebugValidationPolicy<F64StrictFinitePolicy>;
1928
1929        #[test]
1930        #[should_panic(expected = "Debug validation of input value failed: Value is NaN: NaN")]
1931        #[cfg(debug_assertions)]
1932        fn debug_validate_function() {
1933            // This should always succeed, regardless of build mode.
1934            let nan_value = f64::NAN;
1935            debug_validate::<F64StrictFinitePolicy>(&nan_value, "input");
1936        }
1937
1938        #[test]
1939        fn test_conditional_validation_on_valid_value() {
1940            let valid_value = 42.0;
1941            // This should always succeed, regardless of build mode.
1942            assert!(MyConditionalPolicy::validate(valid_value).is_ok());
1943        }
1944
1945        #[test]
1946        fn test_conditional_validation_on_invalid_value() {
1947            let invalid_value = f64::NAN;
1948            let result = MyConditionalPolicy::validate(invalid_value);
1949
1950            if cfg!(debug_assertions) {
1951                // In debug mode, we expect an error from StrictFinitePolicy.
1952                assert!(result.is_err(), "Expected validation to fail in debug mode");
1953                assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
1954            } else {
1955                // In release mode, we expect it to succeed (no-op).
1956                assert!(
1957                    result.is_ok(),
1958                    "Expected validation to succeed in release mode"
1959                );
1960            }
1961        }
1962    }
1963
1964    mod conditional_backtrace_tests {
1965        use super::*;
1966        use std::backtrace::BacktraceStatus;
1967
1968        type F64StrictFinitePolicy = Native64RawRealStrictFinitePolicy;
1969
1970        /// Tests that `capture_backtrace()` returns the correct backtrace status
1971        /// based on the `backtrace` feature flag.
1972        #[test]
1973        fn test_capture_backtrace_status() {
1974            let bt = capture_backtrace();
1975
1976            if cfg!(feature = "backtrace") {
1977                // With backtrace feature enabled, should capture a real backtrace
1978                assert_ne!(
1979                    bt.status(),
1980                    BacktraceStatus::Disabled,
1981                    "Backtrace should not be disabled when 'backtrace' feature is enabled"
1982                );
1983            } else {
1984                // Without backtrace feature, should return a disabled backtrace
1985                assert_eq!(
1986                    bt.status(),
1987                    BacktraceStatus::Disabled,
1988                    "Backtrace should be disabled when 'backtrace' feature is not enabled"
1989                );
1990            }
1991        }
1992
1993        /// Tests that `is_backtrace_enabled()` correctly reports the feature state.
1994        #[test]
1995        fn test_is_backtrace_enabled() {
1996            let enabled = is_backtrace_enabled();
1997
1998            if cfg!(feature = "backtrace") {
1999                assert!(
2000                    enabled,
2001                    "is_backtrace_enabled() should return true when feature is enabled"
2002                );
2003            } else {
2004                assert!(
2005                    !enabled,
2006                    "is_backtrace_enabled() should return false when feature is disabled"
2007                );
2008            }
2009        }
2010
2011        /// Tests that error types contain appropriate backtraces based on feature.
2012        #[test]
2013        fn test_nan_error_backtrace_status() {
2014            // Create an error by validating an invalid value
2015            let result = F64StrictFinitePolicy::validate(f64::NAN);
2016
2017            assert!(result.is_err());
2018
2019            if let Err(ErrorsValidationRawReal::IsNaN { backtrace, .. }) = result {
2020                if cfg!(feature = "backtrace") {
2021                    assert_ne!(
2022                        backtrace.status(),
2023                        BacktraceStatus::Disabled,
2024                        "Error backtrace should be captured when feature is enabled"
2025                    );
2026                } else {
2027                    assert_eq!(
2028                        backtrace.status(),
2029                        BacktraceStatus::Disabled,
2030                        "Error backtrace should be disabled when feature is not enabled"
2031                    );
2032                }
2033            } else {
2034                panic!("Expected IsNaN error");
2035            }
2036        }
2037
2038        /// Tests that subnormal error backtraces are correct.
2039        #[test]
2040        fn test_subnormal_error_backtrace() {
2041            let subnormal = f64::MIN_POSITIVE / 2.0;
2042            let result = F64StrictFinitePolicy::validate(subnormal);
2043
2044            assert!(result.is_err());
2045
2046            if let Err(ErrorsValidationRawReal::IsSubnormal { backtrace, .. }) = result {
2047                if cfg!(feature = "backtrace") {
2048                    assert_ne!(
2049                        backtrace.status(),
2050                        BacktraceStatus::Disabled,
2051                        "Subnormal error backtrace should be captured when feature is enabled"
2052                    );
2053                } else {
2054                    assert_eq!(
2055                        backtrace.status(),
2056                        BacktraceStatus::Disabled,
2057                        "Subnormal error backtrace should be disabled when feature is not enabled"
2058                    );
2059                }
2060            } else {
2061                panic!("Expected IsSubnormal error");
2062            }
2063        }
2064
2065        /// Tests that infinity error backtraces are correct.
2066        #[test]
2067        fn test_infinity_error_backtrace() {
2068            let result = F64StrictFinitePolicy::validate(f64::INFINITY);
2069
2070            assert!(result.is_err());
2071
2072            if let Err(ErrorsValidationRawReal::IsPosInfinity { backtrace, .. }) = result {
2073                if cfg!(feature = "backtrace") {
2074                    assert_ne!(
2075                        backtrace.status(),
2076                        BacktraceStatus::Disabled,
2077                        "Infinity error backtrace should be captured when feature is enabled"
2078                    );
2079                } else {
2080                    assert_eq!(
2081                        backtrace.status(),
2082                        BacktraceStatus::Disabled,
2083                        "Infinity error backtrace should be disabled when feature is not enabled"
2084                    );
2085                }
2086            } else {
2087                panic!("Expected IsPosInfinity error");
2088            }
2089        }
2090
2091        /// Tests that negative infinity error backtraces are correct.
2092        #[test]
2093        fn test_neg_infinity_error_backtrace() {
2094            let result = F64StrictFinitePolicy::validate(f64::NEG_INFINITY);
2095
2096            assert!(result.is_err());
2097
2098            if let Err(ErrorsValidationRawReal::IsNegInfinity { backtrace, .. }) = result {
2099                if cfg!(feature = "backtrace") {
2100                    assert_ne!(
2101                        backtrace.status(),
2102                        BacktraceStatus::Disabled,
2103                        "NegInfinity error backtrace should be captured when feature is enabled"
2104                    );
2105                } else {
2106                    assert_eq!(
2107                        backtrace.status(),
2108                        BacktraceStatus::Disabled,
2109                        "NegInfinity error backtrace should be disabled when feature is not enabled"
2110                    );
2111                }
2112            } else {
2113                panic!("Expected IsNegInfinity error");
2114            }
2115        }
2116    }
2117}