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    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
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 [`RawRealTrait::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` 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// Implement the marker for `StrictFinitePolicy` for any real scalar type.
969impl<RawReal: RawRealTrait, const PRECISION: u32> GuaranteesFiniteValues
970    for StrictFinitePolicy<RawReal, PRECISION>
971where
972    // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteValues
973    StrictFinitePolicy<RawReal, PRECISION>: ValidationPolicyReal,
974{
975}
976
977// Implement the marker for `DebugValidationPolicy<StrictFinitePolicy>` for any real scalar type.
978impl<RawReal: RawRealTrait, const PRECISION: u32> GuaranteesFiniteValues
979    for DebugValidationPolicy<StrictFinitePolicy<RawReal, PRECISION>>
980where
981    // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteValues
982    StrictFinitePolicy<RawReal, PRECISION>: ValidationPolicyReal,
983{
984}
985//-------------------------------------------------------------
986
987//-------------------------------------------------------------
988/// Implements total equality (`Eq`) for `RealValidated` types.
989///
990/// This implementation is only available when the kernel's `RealPolicy`
991/// implements the `GuaranteesFiniteValues` marker trait. This ensures that
992/// the underlying value is never `NaN`, making the equality relation reflexive,
993/// symmetric, and transitive, as required by `Eq`.
994impl<K: NumKernel> Eq for RealValidated<K>
995where
996    K::RealPolicy: GuaranteesFiniteValues,
997{
998    // `Eq` is a marker trait and requires no methods. Its presence simply
999    // asserts that `PartialEq`'s `eq` method forms an equivalence relation.
1000}
1001//-------------------------------------------------------------
1002
1003//-------------------------------------------------------------
1004/// Implements [`Hash`] for validated real number types with finite value guarantees.
1005///
1006/// This implementation is only available when the kernel's `RealPolicy`
1007/// implements the [`GuaranteesFiniteValues`] marker trait. This ensures that
1008/// the underlying value is never `NaN`, making hash values consistent and
1009/// allowing these types to be used as keys in hash-based collections.
1010///
1011/// ## Hashing Strategy
1012///
1013/// The implementation delegates to the [`RawRealTrait::compute_hash()`] method provided by the
1014/// underlying raw real type (via [`RawRealTrait`]). This method:
1015///
1016/// - For `f64` types: Uses IEEE 754 bit representation via `f64::to_bits()`
1017/// - For `rug::Float` types: Uses integer representation via `rug::Float::to_integer()`
1018/// - Handles signed zeros correctly (both `+0.0` and `-0.0` produce the same hash)
1019/// - Maintains the hash contract: `a == b` implies `hash(a) == hash(b)`
1020///
1021/// ## Safety and Correctness
1022///
1023/// This implementation is safe because:
1024/// - [`GuaranteesFiniteValues`] ensures no `NaN` values can exist
1025/// - The [`Eq`] implementation is well-defined for finite values
1026/// - Signed zeros are handled consistently in both equality and hashing
1027///
1028/// ## Usage Example
1029///
1030/// ```rust
1031/// use num_valid::RealNative64StrictFinite;
1032/// use std::collections::HashMap;
1033/// use try_create::TryNew;
1034///
1035/// let mut map = HashMap::new();
1036/// let key = RealNative64StrictFinite::try_new(3.14).unwrap();
1037/// map.insert(key, "pi approximation");
1038/// assert_eq!(map.len(), 1);
1039/// ```
1040impl<K: NumKernel> Hash for RealValidated<K>
1041where
1042    K::RealPolicy: GuaranteesFiniteValues,
1043{
1044    #[inline(always)]
1045    fn hash<H: Hasher>(&self, state: &mut H) {
1046        self.value.compute_hash(state);
1047    }
1048}
1049//-------------------------------------------------------------
1050
1051//-------------------------------------------------------------
1052#[cfg(test)]
1053mod tests {
1054    use super::*;
1055
1056    #[cfg(feature = "rug")]
1057    use crate::{ComplexRugStrictFinite, RealRugStrictFinite};
1058
1059    mod fp_checks {
1060        use super::*;
1061        use num::Complex;
1062
1063        mod native64 {
1064            use super::*;
1065
1066            mod real {
1067                use super::*;
1068
1069                #[test]
1070                fn test_is_finite() {
1071                    assert!(<f64 as FpChecks>::is_finite(&1.0));
1072                    assert!(!<f64 as FpChecks>::is_finite(&f64::INFINITY));
1073                    assert!(!<f64 as FpChecks>::is_finite(&f64::NEG_INFINITY));
1074                    assert!(!<f64 as FpChecks>::is_finite(&f64::NAN));
1075                }
1076
1077                #[test]
1078                fn test_is_infinite() {
1079                    assert!(!<f64 as FpChecks>::is_infinite(&1.0));
1080                    assert!(<f64 as FpChecks>::is_infinite(&f64::INFINITY));
1081                    assert!(<f64 as FpChecks>::is_infinite(&f64::NEG_INFINITY));
1082                    assert!(!<f64 as FpChecks>::is_infinite(&f64::NAN));
1083                }
1084
1085                #[test]
1086                fn test_is_nan() {
1087                    assert!(!<f64 as FpChecks>::is_nan(&1.0));
1088                    assert!(!<f64 as FpChecks>::is_nan(&f64::INFINITY));
1089                    assert!(!<f64 as FpChecks>::is_nan(&f64::NEG_INFINITY));
1090                    assert!(<f64 as FpChecks>::is_nan(&f64::NAN));
1091                }
1092
1093                #[test]
1094                fn test_is_normal() {
1095                    assert!(<f64 as FpChecks>::is_normal(&1.0));
1096                    assert!(!<f64 as FpChecks>::is_normal(&0.0));
1097                    assert!(!<f64 as FpChecks>::is_normal(&f64::INFINITY));
1098                    assert!(!<f64 as FpChecks>::is_normal(&f64::NEG_INFINITY));
1099                    assert!(!<f64 as FpChecks>::is_normal(&f64::NAN));
1100                    assert!(<f64 as FpChecks>::is_normal(&f64::MIN_POSITIVE));
1101                }
1102            }
1103
1104            mod complex {
1105                use super::*;
1106
1107                #[test]
1108                fn test_is_finite() {
1109                    assert!(<Complex<f64> as FpChecks>::is_finite(&Complex::new(
1110                        1.0, 1.0
1111                    )));
1112                    assert!(!<Complex<f64> as FpChecks>::is_finite(&Complex::new(
1113                        f64::INFINITY,
1114                        1.0
1115                    )));
1116                    assert!(!<Complex<f64> as FpChecks>::is_finite(&Complex::new(
1117                        1.0,
1118                        f64::NAN
1119                    )));
1120                }
1121
1122                #[test]
1123                fn test_is_infinite() {
1124                    assert!(!<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
1125                        1.0, 1.0
1126                    )));
1127                    assert!(<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
1128                        f64::INFINITY,
1129                        1.0
1130                    )));
1131                    assert!(!<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
1132                        1.0,
1133                        f64::NAN
1134                    )));
1135                }
1136
1137                #[test]
1138                fn test_is_nan() {
1139                    assert!(!<Complex<f64> as FpChecks>::is_nan(&Complex::new(1.0, 1.0)));
1140                    assert!(<Complex<f64> as FpChecks>::is_nan(&Complex::new(
1141                        f64::NAN,
1142                        1.0
1143                    )));
1144                    assert!(<Complex<f64> as FpChecks>::is_nan(&Complex::new(
1145                        1.0,
1146                        f64::NAN
1147                    )));
1148                }
1149
1150                #[test]
1151                fn test_is_normal() {
1152                    assert!(<Complex<f64> as FpChecks>::is_normal(&Complex::new(
1153                        1.0, 1.0
1154                    )));
1155                    assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
1156                        0.0, 0.0
1157                    )));
1158                    assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
1159                        f64::INFINITY,
1160                        1.0
1161                    )));
1162                    assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
1163                        1.0,
1164                        f64::NAN
1165                    )));
1166                }
1167            }
1168        }
1169    }
1170
1171    mod strict_finite_policy {
1172        use super::*;
1173
1174        mod raw_types {
1175            use super::*;
1176
1177            mod native64 {
1178                use super::*;
1179
1180                mod real {
1181                    use super::*;
1182
1183                    #[test]
1184                    fn f64_infinity() {
1185                        let result = StrictFinitePolicy::<f64, 53>::validate(f64::INFINITY);
1186                        assert!(matches!(
1187                            result,
1188                            Err(ErrorsValidationRawReal::IsPosInfinity { .. })
1189                        ));
1190
1191                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&f64::INFINITY);
1192                        assert!(matches!(
1193                            result,
1194                            Err(ErrorsValidationRawReal::IsPosInfinity { .. })
1195                        ));
1196
1197                        let result = StrictFinitePolicy::<f64, 53>::validate(f64::NEG_INFINITY);
1198                        assert!(matches!(
1199                            result,
1200                            Err(ErrorsValidationRawReal::IsNegInfinity { .. })
1201                        ));
1202
1203                        let result =
1204                            StrictFinitePolicy::<f64, 53>::validate_ref(&f64::NEG_INFINITY);
1205                        assert!(matches!(
1206                            result,
1207                            Err(ErrorsValidationRawReal::IsNegInfinity { .. })
1208                        ));
1209                    }
1210
1211                    #[test]
1212                    fn f64_nan() {
1213                        let result = StrictFinitePolicy::<f64, 53>::validate(f64::NAN);
1214                        assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
1215
1216                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&f64::NAN);
1217                        assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
1218                    }
1219
1220                    #[test]
1221                    fn f64_subnormal() {
1222                        // let subnormal = f64::MIN_POSITIVE / 2.0;
1223                        let subnormal = f64::from_bits(1); // the smallest positive subnormal
1224
1225                        let result = StrictFinitePolicy::<f64, 53>::validate(subnormal);
1226                        assert!(matches!(
1227                            result,
1228                            Err(ErrorsValidationRawReal::<f64>::IsSubnormal { .. })
1229                        ));
1230
1231                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&subnormal);
1232                        assert!(matches!(
1233                            result,
1234                            Err(ErrorsValidationRawReal::<f64>::IsSubnormal { .. })
1235                        ));
1236                    }
1237
1238                    #[test]
1239                    fn f64_zero() {
1240                        let value = 0.0;
1241                        let result = StrictFinitePolicy::<f64, 53>::validate(value);
1242                        assert!(matches!(result, Ok(0.0)));
1243                    }
1244
1245                    #[test]
1246                    fn f64_normal() {
1247                        let value = 1.0;
1248                        let result = StrictFinitePolicy::<f64, 53>::validate(value);
1249                        assert!(matches!(result, Ok(1.0)));
1250
1251                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&value);
1252                        assert!(matches!(result, Ok(())));
1253                    }
1254                }
1255
1256                mod complex {
1257                    use super::*;
1258                    use num::Complex;
1259
1260                    #[test]
1261                    fn test_invalid_both_parts() {
1262                        let z = Complex::new(f64::NAN, f64::NEG_INFINITY);
1263                        let err =
1264                            StrictFinitePolicy::<Complex<f64>, 53>::validate_ref(&z).unwrap_err();
1265                        matches!(err, ErrorsValidationRawComplex::InvalidBothParts { .. });
1266                    }
1267
1268                    #[test]
1269                    fn complex_f64_invalid_real_part() {
1270                        let value = Complex::new(f64::INFINITY, 1.0);
1271                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1272                        assert!(matches!(
1273                            result,
1274                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1275                                source: ErrorsValidationRawReal::IsPosInfinity { .. },
1276                                ..
1277                            })
1278                        ));
1279
1280                        let value = Complex::new(f64::NAN, 1.0);
1281                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1282                        assert!(matches!(
1283                            result,
1284                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1285                                source: ErrorsValidationRawReal::IsNaN { .. },
1286                                ..
1287                            })
1288                        ));
1289
1290                        let value = Complex::new(f64::MIN_POSITIVE / 2.0, 1.0);
1291                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1292                        assert!(matches!(
1293                            result,
1294                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1295                                source: ErrorsValidationRawReal::IsSubnormal { .. },
1296                                ..
1297                            })
1298                        ));
1299                    }
1300
1301                    #[test]
1302                    fn complex_f64_invalid_imaginary_part() {
1303                        let value = Complex::new(1.0, f64::INFINITY);
1304                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1305                        assert!(matches!(
1306                            result,
1307                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1308                                source: ErrorsValidationRawReal::IsPosInfinity { .. },
1309                                ..
1310                            })
1311                        ));
1312
1313                        let value = Complex::new(1.0, f64::NAN);
1314                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1315                        assert!(matches!(
1316                            result,
1317                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1318                                source: ErrorsValidationRawReal::IsNaN { .. },
1319                                ..
1320                            })
1321                        ));
1322
1323                        let value = Complex::new(1.0, f64::MIN_POSITIVE / 2.0);
1324                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1325                        assert!(matches!(
1326                            result,
1327                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1328                                source: ErrorsValidationRawReal::IsSubnormal { .. },
1329                                ..
1330                            })
1331                        ));
1332                    }
1333
1334                    #[test]
1335                    fn complex_f64_zero() {
1336                        let value = Complex::new(0.0, 0.0);
1337                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1338                        assert!(result.is_ok());
1339                    }
1340
1341                    #[test]
1342                    fn complex_f64_normal_value() {
1343                        let value = Complex::new(1.0, 1.0);
1344                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1345                        assert!(result.is_ok());
1346                    }
1347                }
1348            }
1349
1350            #[cfg(feature = "rug")]
1351            mod rug53 {
1352                use super::*;
1353
1354                const PRECISION: u32 = 53; // Default precision for rug tests
1355
1356                mod float {
1357                    use super::*;
1358                    use rug::Float;
1359
1360                    #[test]
1361                    fn rug_float_valid() {
1362                        let x = Float::with_val(PRECISION, 1.0);
1363                        assert!(StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).is_ok());
1364                        assert_eq!(
1365                            StrictFinitePolicy::<Float, PRECISION>::validate(x.clone()).unwrap(),
1366                            x
1367                        );
1368                    }
1369
1370                    #[test]
1371                    fn rug_float_nan() {
1372                        let x = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1373                        let err =
1374                            StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
1375                        assert!(matches!(err, ErrorsValidationRawReal::IsNaN { .. }));
1376                        let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
1377                        assert!(matches!(err, ErrorsValidationRawReal::IsNaN { .. }));
1378                    }
1379
1380                    #[test]
1381                    fn rug_float_infinity() {
1382                        use rug::Float;
1383
1384                        let x = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1385                        let err =
1386                            StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
1387                        assert!(matches!(err, ErrorsValidationRawReal::IsPosInfinity { .. }));
1388                        let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
1389                        assert!(matches!(err, ErrorsValidationRawReal::IsPosInfinity { .. }));
1390
1391                        let x = Float::with_val(PRECISION, Float::parse("-inf").unwrap());
1392                        let err =
1393                            StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
1394                        assert!(matches!(err, ErrorsValidationRawReal::IsNegInfinity { .. }));
1395                        let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
1396                        assert!(matches!(err, ErrorsValidationRawReal::IsNegInfinity { .. }));
1397                    }
1398
1399                    #[test]
1400                    fn rug_float_zero() {
1401                        let value = Float::with_val(PRECISION, 0.0);
1402                        let result = StrictFinitePolicy::<Float, PRECISION>::validate_ref(&value);
1403                        assert!(result.is_ok());
1404                        let result =
1405                            StrictFinitePolicy::<Float, PRECISION>::validate(value.clone())
1406                                .unwrap();
1407                        assert_eq!(result, value);
1408                    }
1409                }
1410
1411                mod complex {
1412                    use super::*;
1413                    use rug::{Complex, Float};
1414
1415                    #[test]
1416                    fn rug_complex_valid() {
1417                        let z = Complex::with_val(PRECISION, (1.0, -2.0));
1418                        assert!(StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).is_ok());
1419                        assert_eq!(
1420                            StrictFinitePolicy::<Complex, PRECISION>::validate(z.clone()).unwrap(),
1421                            z
1422                        );
1423                    }
1424
1425                    #[test]
1426                    fn rug_complex_real_invalid() {
1427                        let nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1428                        let z = Complex::with_val(PRECISION, (&nan, 1.0));
1429                        let err =
1430                            StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
1431                        assert!(matches!(
1432                            err,
1433                            ErrorsValidationRawComplex::InvalidRealPart {
1434                                source: ErrorsValidationRawReal::IsNaN { .. }
1435                            }
1436                        ));
1437                    }
1438
1439                    #[test]
1440                    fn rug_complex_imag_invalid() {
1441                        let inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1442                        let z = Complex::with_val(PRECISION, (1.0, &inf));
1443                        let err =
1444                            StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
1445                        assert!(matches!(
1446                            err,
1447                            ErrorsValidationRawComplex::InvalidImaginaryPart {
1448                                source: ErrorsValidationRawReal::IsPosInfinity { .. }
1449                            }
1450                        ));
1451                    }
1452
1453                    #[test]
1454                    fn rug_complex_both_invalid() {
1455                        let nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1456                        let inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1457                        let z = Complex::with_val(PRECISION, (&nan, &inf));
1458                        let err =
1459                            StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
1460                        assert!(matches!(
1461                            err,
1462                            ErrorsValidationRawComplex::InvalidBothParts {
1463                                real_error: ErrorsValidationRawReal::IsNaN { .. },
1464                                imag_error: ErrorsValidationRawReal::IsPosInfinity { .. },
1465                                ..
1466                            }
1467                        ));
1468                    }
1469
1470                    #[test]
1471                    fn rug_complex_zero() {
1472                        let zero = Complex::with_val(
1473                            PRECISION,
1474                            (
1475                                Float::with_val(PRECISION, 0.0),
1476                                Float::with_val(PRECISION, 0.0),
1477                            ),
1478                        );
1479                        let result = StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&zero);
1480                        assert!(result.is_ok());
1481                        assert_eq!(
1482                            StrictFinitePolicy::<Complex, PRECISION>::validate(zero.clone())
1483                                .unwrap(),
1484                            zero
1485                        );
1486                    }
1487                }
1488            }
1489        }
1490
1491        // Module for testing the wrapper types that implement TryNew
1492        #[cfg(feature = "rug")]
1493        mod wrapper_types {
1494            use super::*;
1495
1496            mod rug_wrappers {
1497                use super::*;
1498                use crate::RealScalar;
1499                use num::One;
1500                use rug::{Complex as RugComplex, Float};
1501                use try_create::{IntoInner, TryNew};
1502
1503                const PRECISION: u32 = 53; // Default precision for rug tests
1504
1505                mod real {
1506                    use super::*;
1507
1508                    #[test]
1509                    fn try_new_real_rug_correct_precision() {
1510                        // Test RealRugStrictFinite with valid rug::Float and correct precision
1511                        let val = Float::with_val(PRECISION, 1.0);
1512                        let res = RealRugStrictFinite::<PRECISION>::try_new(val.clone());
1513                        assert!(res.is_ok(),);
1514                        assert_eq!(res.unwrap().into_inner(), val);
1515                    }
1516
1517                    #[test]
1518                    fn try_new_real_rug_incorrect_precision() {
1519                        // Test RealRugStrictFinite with valid rug::Float but incorrect precision
1520                        let val_wrong_prec = Float::with_val(PRECISION + 10, 1.0); // Different precision
1521                        let res = RealRugStrictFinite::<PRECISION>::try_new(val_wrong_prec);
1522                        assert!(res.is_err());
1523                        matches!(res, Err(ErrorsValidationRawReal::PrecisionMismatch { actual_precision, .. })
1524                            if actual_precision == PRECISION + 10
1525                        );
1526                    }
1527
1528                    #[test]
1529                    fn try_new_real_rug_nan() {
1530                        // Test RealRugStrictFinite with rug::Float NaN
1531                        let val_nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1532                        let res = RealRugStrictFinite::<PRECISION>::try_new(val_nan);
1533                        assert!(res.is_err());
1534                        assert!(matches!(
1535                            res,
1536                            Err(ErrorsValidationRawReal::<Float>::IsNaN { .. })
1537                        ));
1538                    }
1539
1540                    #[test]
1541                    fn try_new_real_rug_infinity() {
1542                        // Test RealRugStrictFinite with rug::Float positive infinity
1543                        let val_inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1544                        let res_pos_inf = RealRugStrictFinite::<PRECISION>::try_new(val_inf);
1545                        assert!(res_pos_inf.is_err());
1546                        assert!(matches!(
1547                            res_pos_inf,
1548                            Err(ErrorsValidationRawReal::<Float>::IsPosInfinity { .. })
1549                        ));
1550
1551                        // Test RealRugStrictFinite with rug::Float negative infinity
1552                        let val_neg_inf = Float::with_val(PRECISION, Float::parse("-inf").unwrap());
1553                        let res_neg_inf = RealRugStrictFinite::<PRECISION>::try_new(val_neg_inf);
1554                        assert!(res_neg_inf.is_err());
1555                        assert!(matches!(
1556                            res_neg_inf,
1557                            Err(ErrorsValidationRawReal::<Float>::IsNegInfinity { .. })
1558                        ));
1559                    }
1560                }
1561
1562                mod complex {
1563                    use super::*;
1564                    use crate::ComplexScalarGetParts;
1565
1566                    #[test]
1567                    fn try_new_complex_rug_correct_precision() {
1568                        // Test ComplexRugStrictFinite with valid rug::Complex and correct precision for both parts
1569                        let val = RugComplex::with_val(PRECISION, (1.0, -2.0));
1570                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val.clone());
1571                        assert!(res.is_ok());
1572                        assert_eq!(res.unwrap().into_inner(), val);
1573                    }
1574
1575                    /*
1576                    #[test]
1577                    #[ignore = "because the precision used to construct a rug::Complex override the precision of the real and imaginary parts"]
1578                    fn try_new_complex_rug_precision_mismatch_real() {
1579                        // Test ComplexRugStrictFinite where real part has incorrect precision
1580                        let real_part_wrong_prec = Float::with_val(PRECISION - 1, 1.0);
1581                        let imag_part_correct_prec = Float::with_val(PRECISION, -2.0);
1582                        let val = RugComplex::with_val(
1583                            PRECISION,
1584                            (real_part_wrong_prec, imag_part_correct_prec),
1585                        ); // Construct with Float parts
1586
1587                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1588                        assert!(res.is_err());
1589                        assert!(matches!(
1590                            res,
1591                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1592                                source: ErrorsValidationRawReal::PrecisionMismatch { .. }
1593                            })
1594                        ));
1595                    }
1596
1597                    #[test]
1598                    #[ignore = "because the precision used to construct a rug::Complex override the precision of the real and imaginary parts"]
1599                    fn try_new_complex_rug_precision_mismatch_imag() {
1600                        // Test ComplexRugStrictFinite where imaginary part has incorrect precision
1601                        let real_part_correct_prec = Float::with_val(PRECISION, 1.0);
1602                        let imag_part_wrong_prec = Float::with_val(PRECISION - 1, -2.0);
1603                        let val = RugComplex::with_val(
1604                            PRECISION,
1605                            (real_part_correct_prec, imag_part_wrong_prec),
1606                        ); // Construct with Float parts
1607
1608                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1609                        assert!(res.is_err());
1610                        matches!(res, Err(ErrorsValidationRawComplex::InvalidImaginaryPart { source })
1611                            if matches!(source, ErrorsValidationRawReal::PrecisionMismatch { .. })
1612                        );
1613                    }
1614                    */
1615
1616                    #[test]
1617                    fn try_new_complex_rug_precision_mismatch_both() {
1618                        // Test ComplexRugStrictFinite where both parts have incorrect precision
1619                        let real_part_wrong_prec = Float::with_val(PRECISION - 1, 1.0);
1620                        let imag_part_wrong_prec = Float::with_val(PRECISION - 1, -2.0);
1621                        let val = RugComplex::with_val(
1622                            PRECISION - 1,
1623                            (real_part_wrong_prec, imag_part_wrong_prec),
1624                        ); // Construct with Float parts
1625
1626                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1627                        assert!(res.is_err());
1628                        assert!(matches!(
1629                            res,
1630                            Err(ErrorsValidationRawComplex::InvalidBothParts {
1631                                real_error: ErrorsValidationRawReal::PrecisionMismatch { .. },
1632                                imag_error: ErrorsValidationRawReal::PrecisionMismatch { .. },
1633                                ..
1634                            })
1635                        ));
1636                    }
1637
1638                    #[test]
1639                    fn try_new_complex_rug_invalid_real_nan() {
1640                        // Test ComplexRugStrictFinite where real part is NaN (value validation failure)
1641                        let nan_real = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1642                        let imag_part = Float::with_val(PRECISION, 1.0);
1643                        let val = RugComplex::with_val(PRECISION, (nan_real, imag_part));
1644
1645                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1646                        assert!(res.is_err());
1647                        assert!(matches!(
1648                            res,
1649                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1650                                source: ErrorsValidationRawReal::<Float>::IsNaN { .. },
1651                            })
1652                        ));
1653                    }
1654
1655                    #[test]
1656                    fn try_new_complex_rug_invalid_imag_inf() {
1657                        // Test ComplexRugStrictFinite where imaginary part is Infinity (value validation failure)
1658                        let real_part = Float::with_val(PRECISION, 1.0);
1659                        let inf_imag = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1660                        let val = RugComplex::with_val(PRECISION, (real_part, inf_imag));
1661
1662                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1663                        assert!(res.is_err());
1664                        assert!(matches!(
1665                            res,
1666                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1667                                source: ErrorsValidationRawReal::<Float>::IsPosInfinity { .. }
1668                            })
1669                        ));
1670                    }
1671
1672                    #[test]
1673                    fn try_new_complex_rug_invalid_both() {
1674                        // Test ComplexRugStrictFinite where both parts are invalid (value validation failure)
1675                        let nan_real = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1676                        let neg_inf_imag =
1677                            Float::with_val(PRECISION, Float::parse("-inf").unwrap());
1678                        let val = RugComplex::with_val(PRECISION, (nan_real, neg_inf_imag));
1679
1680                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1681                        assert!(res.is_err());
1682                        assert!(matches!(
1683                            res,
1684                            Err(ErrorsValidationRawComplex::InvalidBothParts {
1685                                real_error: ErrorsValidationRawReal::<Float>::IsNaN { .. },
1686                                imag_error: ErrorsValidationRawReal::<Float>::IsNegInfinity { .. },
1687                                ..
1688                            })
1689                        ));
1690                    }
1691
1692                    #[test]
1693                    fn try_from_complex_f64_to_complex_rug() {
1694                        // Test valid conversion
1695                        let z = num::Complex::new(1., -2.);
1696                        let z_rug = ComplexRugStrictFinite::<PRECISION>::try_from(z).unwrap();
1697                        let expected_real = RealRugStrictFinite::<PRECISION>::one();
1698                        let expected_imaginary =
1699                            RealRugStrictFinite::<PRECISION>::try_from_f64(-2.).unwrap();
1700                        assert_eq!(z_rug.real_part(), expected_real);
1701                        assert_eq!(z_rug.imag_part(), expected_imaginary);
1702
1703                        // Test invalid real part
1704                        let z = num::Complex::new(f64::INFINITY, -2.0);
1705                        let result = ComplexRugStrictFinite::<PRECISION>::try_from(z);
1706                        assert!(result.is_err());
1707                        assert!(matches!(
1708                            result,
1709                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1710                                source: ErrorsTryFromf64::Output {
1711                                    source: ErrorsValidationRawReal::IsPosInfinity { .. }
1712                                },
1713                            })
1714                        ));
1715
1716                        // Test invalid imaginary part
1717                        let z = num::Complex::new(1.0, f64::NAN);
1718                        let result = ComplexRugStrictFinite::<PRECISION>::try_from(z).unwrap_err();
1719                        //println!("result = {result}");
1720                        assert!(matches!(
1721                            result,
1722                            ErrorsValidationRawComplex::InvalidImaginaryPart {
1723                                source: ErrorsTryFromf64::Output {
1724                                    source: ErrorsValidationRawReal::IsNaN { .. }
1725                                },
1726                            }
1727                        ));
1728                    }
1729                }
1730            }
1731        }
1732    }
1733
1734    mod conditional_validation_policy {
1735        use core::f64;
1736
1737        use super::*;
1738
1739        type F64StrictFinitePolicy = Native64RawRealStrictFinitePolicy;
1740
1741        // Define a policy that is strict in debug, and no-op in release.
1742        type MyConditionalPolicy = DebugValidationPolicy<F64StrictFinitePolicy>;
1743
1744        #[test]
1745        #[should_panic(expected = "Debug validation of input value failed: Value is NaN: NaN")]
1746        #[cfg(debug_assertions)]
1747        fn debug_validate_function() {
1748            // This should always succeed, regardless of build mode.
1749            let nan_value = f64::NAN;
1750            debug_validate::<F64StrictFinitePolicy>(&nan_value, "input");
1751        }
1752
1753        #[test]
1754        fn test_conditional_validation_on_valid_value() {
1755            let valid_value = 42.0;
1756            // This should always succeed, regardless of build mode.
1757            assert!(MyConditionalPolicy::validate(valid_value).is_ok());
1758        }
1759
1760        #[test]
1761        fn test_conditional_validation_on_invalid_value() {
1762            let invalid_value = f64::NAN;
1763            let result = MyConditionalPolicy::validate(invalid_value);
1764
1765            if cfg!(debug_assertions) {
1766                // In debug mode, we expect an error from StrictFinitePolicy.
1767                assert!(result.is_err(), "Expected validation to fail in debug mode");
1768                assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
1769            } else {
1770                // In release mode, we expect it to succeed (no-op).
1771                assert!(
1772                    result.is_ok(),
1773                    "Expected validation to succeed in release mode"
1774                );
1775            }
1776        }
1777    }
1778}