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