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//! - **Implementations**: Validation logic for [`StrictFinitePolicy`] and implementations of
23//!   [`FpChecks`] are provided for `f64`, `num::Complex<f64>`. When the "rug" feature is enabled,
24//!   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
25//!   `RealRugStrictFinite` and `ComplexRugStrictFinite` (which utilize these underlying `rug` types).
26//!
27//! The goal of this module is to ensure numerical stability and correctness by allowing
28//! functions to easily validate their inputs and outputs according to defined policies,
29//! and to provide a common interface for basic floating-point characteristic checks.
30//!
31//! ## Example: Using [`StrictFinitePolicy`]
32//!
33//! ```rust
34//! use num_valid::validation::{StrictFinitePolicy, ErrorsValidationRawReal};
35//! use try_create::ValidationPolicy; // For the validate method
36//!
37//! let finite_val = 10.0f64;
38//! let nan_val = f64::NAN;
39//! let subnormal_val = f64::from_bits(1); // Smallest positive subnormal f64
40//!
41//! assert!(StrictFinitePolicy::<f64, 53>::validate(finite_val).is_ok());
42//!
43//! let nan_validation_result = StrictFinitePolicy::<f64, 53>::validate(nan_val);
44//! assert!(nan_validation_result.is_err());
45//! if let Err(ErrorsValidationRawReal::IsNaN { value, .. }) = nan_validation_result {
46//!     assert!(value.is_nan());
47//! } else {
48//!     panic!("Expected NaN error for f64::NAN");
49//! }
50//!
51//! let subnormal_validation_result = StrictFinitePolicy::<f64, 53>::validate(subnormal_val);
52//! assert!(subnormal_validation_result.is_err());
53//! if let Err(ErrorsValidationRawReal::IsSubnormal { value, .. }) = subnormal_validation_result {
54//!     assert_eq!(value, subnormal_val);
55//! } else {
56//!     panic!("Expected Subnormal error for subnormal f64");
57//! }
58//! ```
59//!
60//! ## Example: Using [`FpChecks`]
61//!
62//! ```rust
63//! use num_valid::validation::FpChecks;
64//! use num::Complex;
65//!
66//! let val_f64 = 1.0f64;
67//! let nan_f64 = f64::NAN;
68//! let inf_f64 = f64::INFINITY;
69//!
70//! assert!(val_f64.is_finite());
71//! assert!(val_f64.is_normal());
72//! assert!(nan_f64.is_nan());
73//! assert!(!nan_f64.is_finite());
74//! assert!(inf_f64.is_infinite());
75//!
76//! let val_complex = Complex::new(1.0, 2.0);
77//! let nan_complex_real = Complex::new(f64::NAN, 2.0);
78//! let inf_complex_imag = Complex::new(1.0, f64::NEG_INFINITY);
79//!
80//! assert!(val_complex.is_finite());
81//! assert!(val_complex.is_normal());
82//! assert!(nan_complex_real.is_nan());
83//! assert!(!nan_complex_real.is_finite());
84//! assert!(inf_complex_imag.is_infinite());
85//! assert!(!inf_complex_imag.is_finite());
86//! assert!(!inf_complex_imag.is_nan());
87//! ```
88
89use crate::kernels::{RawComplexTrait, RawRealTrait, RawScalarTrait};
90use duplicate::duplicate_item;
91use num::Complex;
92use std::{backtrace::Backtrace, fmt::Display, marker::PhantomData, num::FpCategory};
93use thiserror::Error;
94use try_create::ValidationPolicy;
95
96/// A marker for policies that apply to real types.
97///
98/// This trait refines the generic [`ValidationPolicy`] for types that implement
99/// [`RawRealTrait`]. It adds two key constraints:
100///
101/// 1.  It associates a `const PRECISION` with the policy, essential for backends
102///     like `rug`.
103/// 2.  It enforces that the policy's `Error` type must be the same as the canonical
104///     `ValidationErrors` type defined on the raw scalar itself. This architectural
105///     choice ensures consistent error handling throughout the library.
106pub trait ValidationPolicyReal:
107    ValidationPolicy<
108        Value: RawRealTrait,
109        Error = <<Self as ValidationPolicy>::Value as RawScalarTrait>::ValidationErrors,
110    >
111{
112    const PRECISION: u32;
113}
114
115/// A marker for policies that apply to complex types.
116///
117/// This trait refines the generic [`ValidationPolicy`] for types that implement
118/// [`RawComplexTrait`]. It adds two key constraints:
119///
120/// 1.  It associates a `const PRECISION` with the policy, essential for backends
121///     like `rug`.
122/// 2.  It enforces that the policy's `Error` type must be the same as the canonical
123///     `ValidationErrors` type defined on the raw scalar itself. This architectural
124///     choice ensures consistent error handling throughout the library.
125pub trait ValidationPolicyComplex:
126    ValidationPolicy<
127        Value: RawComplexTrait,
128        Error = <<Self as ValidationPolicy>::Value as RawScalarTrait>::ValidationErrors,
129    >
130{
131    const PRECISION: u32;
132}
133
134//--------------------------------------------------------------------------------------------------
135// Error types
136//--------------------------------------------------------------------------------------------------
137
138//--------------------------------------------
139// Errors for raw real number validation
140//--------------------------------------------
141/// Errors that can occur during the validation of a raw real number.
142///
143/// This enum is generic over `RawReal`, which is typically `f64` or [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html).
144/// It covers common validation failures like non-finite values, subnormality, infinity, and precision mismatch.
145#[derive(Debug, Error)]
146pub enum ErrorsValidationRawReal<RawReal: std::fmt::Debug + Display + Clone> {
147    /// The value is subnormal.
148    #[error("Value is subnormal: {value}")]
149    IsSubnormal {
150        /// The subnormal value that failed validation.
151        value: RawReal,
152
153        /// A captured backtrace for debugging purposes.
154        backtrace: Backtrace,
155    },
156
157    /// The value is NaN (Not a Number).
158    #[error("Value is NaN: {value}")]
159    IsNaN {
160        /// The NaN value that failed validation.
161        value: RawReal,
162
163        /// A captured backtrace for debugging purposes.
164        backtrace: Backtrace,
165    },
166
167    /// The value is positive infinity.
168    #[error("Value is positive infinity")]
169    IsPosInfinity {
170        /// The positive infinity value that failed validation.
171        backtrace: Backtrace,
172    },
173
174    /// The value is negative infinity.
175    #[error("Value is negative infinity")]
176    IsNegInfinity {
177        /// The negative infinity value that failed validation.
178        backtrace: Backtrace,
179    },
180
181    /// The precision of the input does not match the requested precision.
182    #[error(
183        "precision mismatch: input real has precision {actual_precision}, \
184        but the requested precision is {requested_precision}"
185    )]
186    PrecisionMismatch {
187        /// The input value.
188        input_value: RawReal,
189
190        /// The precision of the input value.
191        actual_precision: u32,
192
193        /// The requested precision.
194        requested_precision: u32,
195
196        /// A captured backtrace for debugging purposes.
197        backtrace: Backtrace,
198    },
199    /*
200    // The following variants are placeholders for potential future validation policies
201    // and are not currently used by StrictFinitePolicy.
202
203    #[error("Value is not strictly positive: {value}")]
204    NotPositive {
205        value: RawReal,
206        backtrace: Backtrace,
207    },
208
209    #[error("Value is negative: {value}")]
210    Negative {
211        value: RawReal,
212        backtrace: Backtrace,
213    },
214
215    #[error("Value is zero: {value}")]
216    IsZero {
217        value: RawReal,
218        backtrace: Backtrace,
219    },
220
221    #[error("Value {value} is outside the allowed range [{min}, {max}]")]
222    OutOfRange {
223        value: RawReal,
224        min: RawReal,
225        max: RawReal,
226        backtrace: Backtrace,
227    },
228    */
229}
230
231//--------------------------------------------
232// Errors for complex number validation
233//--------------------------------------------
234/// Errors that can occur during the validation of a complex number.
235///
236/// This enum is generic over `ErrorValidationReal`, which is the error type returned
237/// by validating the real and imaginary parts of the complex number (e.g.,
238/// [`ErrorsValidationRawReal<f64>`](ErrorsValidationRawReal)).
239///
240/// It indicates whether the real part, the imaginary part, or both parts failed validation.
241#[derive(Debug, Error)]
242pub enum ErrorsValidationRawComplex<ErrorValidationReal: std::error::Error> {
243    /// The real part of the complex number failed validation.
244    #[error("Real part validation error: {source}")]
245    InvalidRealPart {
246        /// The underlying validation error for the real part.
247        #[source]
248        #[backtrace]
249        source: ErrorValidationReal,
250    },
251
252    /// The imaginary part of the complex number failed validation.
253    #[error("Imaginary part validation error: {source}")]
254    InvalidImaginaryPart {
255        /// The underlying validation error for the imaginary part.
256        #[source]
257        #[backtrace]
258        source: ErrorValidationReal,
259    },
260
261    /// Both the real and imaginary parts of the complex number failed validation.
262    #[error("Both real and imaginary parts are invalid: real={real_error}, imag={imag_error}")]
263    InvalidBothParts {
264        /// The validation error for the real part.
265        real_error: ErrorValidationReal,
266
267        /// The validation error for the imaginary part.
268        imag_error: ErrorValidationReal,
269    },
270}
271
272//-------------------------------------------------------------
273/// Errors that can occur when trying to convert an `f64` to another real-type scalar of type `RawReal`.
274///
275/// This enum distinguishes between:
276/// 1.  Failures due to the `f64` not being exactly representable at the target `PRECISION`
277///     of the `RawReal` type.
278/// 2.  General validation failures of the output `RawReal` (e.g., NaN, Infinity, subnormal).
279#[derive(Debug, Error)]
280pub enum ErrorsTryFromf64<RawReal: RawRealTrait> {
281    /// The input `f64` value is not representable exactly at the target `precision` using the output `RawReal` type.
282    #[error(
283        "the input f64 value ({value_in:?}) cannot be exactly represented with the specific precision ({precision:?}). Try to increase the precision."
284    )]
285    NonRepresentableExactly {
286        /// The `f64` value that could not be exactly represented.
287        value_in: f64,
288
289        /// The input `value_in` after the conversion to the type `RawReal` with the requested `precision`.
290        value_converted_from_f64: RawReal,
291
292        /// The precision that was requested.
293        precision: u32,
294
295        /// A captured backtrace for debugging purposes.
296        backtrace: Backtrace,
297    },
298
299    /// The output value failed validation.
300    #[error("the output value is invalid!")]
301    Output {
302        /// The underlying validation error for the `output` value.
303        #[from]
304        source: ErrorsValidationRawReal<RawReal>,
305    },
306}
307//-------------------------------------------------------------
308
309/// A validation policy that checks for strict finiteness.
310///
311/// 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:
312/// - Not NaN (Not a Number).
313/// - Not positive or negative Infinity.
314/// - Not subnormal (for `f64`).
315///   While [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) maintains its specified precision rather than having distinct subnormal
316///   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
317///   [`FpCategory::Subnormal`] (e.g., results of underflow that are tiny but not exactly zero).
318///
319/// For complex types (`ScalarType = `[`Complex<f64>`], `ScalarType = `[`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html)), this policy applies
320/// the strict finiteness check to both the real and imaginary parts.
321///
322/// This struct is a Zero-Sized Type (ZST) and uses [`PhantomData`] to associate
323/// with the `ScalarType` it validates.
324pub struct StrictFinitePolicy<ScalarType: Sized, const PRECISION: u32>(PhantomData<ScalarType>);
325
326/// A type alias for a validation policy that enforces strict finiteness for the raw [`f64`] type.
327///
328/// This is a convenient alias for [`StrictFinitePolicy<f64, 53>`], configured specifically
329/// for Rust's native 64-bit floating-point number. It is used to validate that a given [`f64`]
330/// value is suitable for use within the library's validated types.
331///
332/// This policy ensures that a `f64` value is:
333/// - Not `NaN` (Not a Number).
334/// - Not positive or negative `Infinity`.
335/// - Not a subnormal number.
336///
337/// It is a fundamental building block for the native `f64` kernel, providing the validation
338/// logic for [`RealNative64StrictFinite`](crate::kernels::native64_validated::RealNative64StrictFinite).
339///
340/// # Example
341///
342/// ```rust
343/// use num_valid::validation::{Native64RawRealStrictFinitePolicy, ErrorsValidationRawReal};
344/// use try_create::ValidationPolicy;
345///
346/// // A valid finite number passes validation.
347/// assert!(Native64RawRealStrictFinitePolicy::validate(1.0).is_ok());
348///
349/// // NaN fails validation.
350/// let result_nan = Native64RawRealStrictFinitePolicy::validate(f64::NAN);
351/// assert!(matches!(result_nan, Err(ErrorsValidationRawReal::IsNaN { .. })));
352///
353/// // Infinity fails validation.
354/// let result_inf = Native64RawRealStrictFinitePolicy::validate(f64::INFINITY);
355/// assert!(matches!(result_inf, Err(ErrorsValidationRawReal::IsPosInfinity { .. })));
356/// ```
357pub type Native64RawRealStrictFinitePolicy = StrictFinitePolicy<f64, 53>;
358
359/// A type alias for a validation policy that enforces strict finiteness for the raw [`num::Complex<f64>`] type.
360///
361/// This is a convenient alias for [`StrictFinitePolicy<Complex<f64>, 53>`], configured for
362/// Rust's native 64-bit complex numbers. It is used to validate that a given [`num::Complex<f64>`]
363/// value is suitable for use within the library's validated types.
364///
365/// This policy applies the strict finiteness check (as defined by [`Native64RawRealStrictFinitePolicy`])
366/// to both the real and imaginary parts of the complex number. A [`num::Complex<f64>`] value is considered
367/// valid if and only if both of its components are finite (not `NaN`, `Infinity`, or subnormal).
368///
369/// It is a fundamental building block for the native `f64` kernel, providing the validation
370/// logic for [`ComplexNative64StrictFinite`](crate::kernels::native64_validated::ComplexNative64StrictFinite).
371///
372/// # Example
373///
374/// ```rust
375/// use num_valid::validation::{Native64RawComplexStrictFinitePolicy, ErrorsValidationRawComplex, ErrorsValidationRawReal};
376/// use try_create::ValidationPolicy;
377/// use num::Complex;
378///
379/// // A complex number with valid finite parts passes validation.
380/// let valid_complex = Complex::new(1.0, -2.0);
381/// assert!(Native64RawComplexStrictFinitePolicy::validate(valid_complex).is_ok());
382///
383/// // A complex number with NaN in the real part fails validation.
384/// let nan_complex = Complex::new(f64::NAN, 2.0);
385/// let result_nan = Native64RawComplexStrictFinitePolicy::validate(nan_complex);
386/// assert!(matches!(result_nan, Err(ErrorsValidationRawComplex::InvalidRealPart { .. })));
387///
388/// // A complex number with Infinity in the imaginary part fails validation.
389/// let inf_complex = Complex::new(1.0, f64::INFINITY);
390/// let result_inf = Native64RawComplexStrictFinitePolicy::validate(inf_complex);
391/// assert!(matches!(result_inf, Err(ErrorsValidationRawComplex::InvalidImaginaryPart { .. })));
392///
393/// // A complex number with invalid parts in both also fails.
394/// let both_invalid = Complex::new(f64::NAN, f64::INFINITY);
395/// let result_both = Native64RawComplexStrictFinitePolicy::validate(both_invalid);
396/// assert!(matches!(result_both, Err(ErrorsValidationRawComplex::InvalidBothParts { .. })));
397/// ```
398pub type Native64RawComplexStrictFinitePolicy = StrictFinitePolicy<Complex<f64>, 53>;
399
400/// Ensures the `f64` value is strictly finite.
401///
402/// This policy checks if the `f64` value is:
403/// - Not NaN (Not a Number).
404/// - Not positive or negative Infinity.
405/// - Not subnormal.
406///
407/// # Errors
408///
409/// Returns [`ErrorsValidationRawReal<f64>`](ErrorsValidationRawReal) if the value
410/// fails any of these checks.
411impl ValidationPolicy for Native64RawRealStrictFinitePolicy {
412    type Value = f64;
413    type Error = ErrorsValidationRawReal<f64>;
414
415    fn validate_ref(value: &f64) -> Result<(), Self::Error> {
416        match value.classify() {
417            FpCategory::Nan => Err(ErrorsValidationRawReal::IsNaN {
418                value: *value,
419                backtrace: Backtrace::force_capture(),
420            }),
421            FpCategory::Infinite => {
422                if value.is_sign_positive() {
423                    Err(ErrorsValidationRawReal::IsPosInfinity {
424                        backtrace: Backtrace::force_capture(),
425                    })
426                } else {
427                    Err(ErrorsValidationRawReal::IsNegInfinity {
428                        backtrace: Backtrace::force_capture(),
429                    })
430                }
431            }
432            FpCategory::Subnormal => Err(ErrorsValidationRawReal::IsSubnormal {
433                value: *value,
434                backtrace: Backtrace::force_capture(),
435            }),
436            FpCategory::Normal | FpCategory::Zero => Ok(()),
437        }
438    }
439}
440
441/// Validates a complex number by checking both its real and imaginary parts.
442///
443/// This function uses the provided real validation policy `P` to validate both parts.
444/// If both parts are valid, it returns `Ok(())`. If either part is invalid,
445/// it returns an appropriate error variant from the `ErrorsValidationRawComplex` enum.
446///
447/// # Errors
448/// Returns [`ErrorsValidationRawComplex<P::Error>`](ErrorsValidationRawComplex)
449/// if either the real part, the imaginary part, or both parts fail validation
450/// using the policy `P`.
451pub(crate) fn validate_complex<P: ValidationPolicyReal>(
452    real_part: &P::Value,
453    imag_part: &P::Value,
454) -> Result<(), ErrorsValidationRawComplex<P::Error>> {
455    let real_validation = P::validate_ref(real_part);
456    let imag_validation = P::validate_ref(imag_part);
457    match (real_validation, imag_validation) {
458        (Ok(()), Ok(())) => Ok(()),
459        (Ok(()), Err(imag_err)) => {
460            Err(ErrorsValidationRawComplex::InvalidImaginaryPart { source: imag_err })
461        }
462        (Err(real_err), Ok(())) => {
463            Err(ErrorsValidationRawComplex::InvalidRealPart { source: real_err })
464        }
465        (Err(real_err), Err(imag_err)) => Err(ErrorsValidationRawComplex::InvalidBothParts {
466            real_error: real_err,
467            imag_error: imag_err,
468        }),
469    }
470}
471
472/// Ensures both the real and imaginary parts of a `Complex<f64>` value are strictly finite.
473///
474/// This policy applies the [`StrictFinitePolicy<f64>`](StrictFinitePolicy) to both
475/// the real and imaginary components of the complex number.
476///
477/// # Errors
478///
479/// Returns [`ErrorsValidationRawComplex<ErrorsValidationRawReal<f64>>`](ErrorsValidationRawComplex)
480/// if either the real part, the imaginary part, or both parts fail the
481/// `StrictFinitePolicy<f64>` checks.
482impl ValidationPolicy for Native64RawComplexStrictFinitePolicy {
483    type Value = Complex<f64>;
484    type Error = ErrorsValidationRawComplex<ErrorsValidationRawReal<f64>>;
485
486    fn validate_ref(value: &Complex<f64>) -> Result<(), Self::Error> {
487        validate_complex::<Native64RawRealStrictFinitePolicy>(&value.re, &value.im)
488    }
489}
490
491impl<RawReal: RawRealTrait, const PRECISION: u32> ValidationPolicyReal
492    for StrictFinitePolicy<RawReal, PRECISION>
493where
494    StrictFinitePolicy<RawReal, PRECISION>:
495        ValidationPolicy<Value = RawReal, Error = <RawReal as RawScalarTrait>::ValidationErrors>,
496{
497    const PRECISION: u32 = PRECISION;
498}
499
500impl<RawComplex: RawComplexTrait, const PRECISION: u32> ValidationPolicyComplex
501    for StrictFinitePolicy<RawComplex, PRECISION>
502where
503    StrictFinitePolicy<RawComplex, PRECISION>: ValidationPolicy<
504            Value = RawComplex,
505            Error = <RawComplex as RawScalarTrait>::ValidationErrors,
506        >,
507{
508    const PRECISION: u32 = PRECISION;
509}
510
511//--------------------------------------------------------------------------------------------------
512// Rug Implementations (conditional compilation)
513//--------------------------------------------------------------------------------------------------
514
515#[cfg(feature = "rug")]
516pub(crate) mod rug_impls {
517    use super::*; // Imports items from the parent module (validation)
518
519    /// Ensures the [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) value is strictly finite.
520    ///
521    /// This policy checks if the underlying [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) value:
522    /// - Is not NaN (Not a Number).
523    /// - Is not positive nor negative Infinity.
524    /// - Has the specified precision (equal to `PRECISION`).
525    ///
526    /// Note: [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) does not have a direct concept of "subnormal" in the same
527    /// way IEEE 754 floats do; it maintains its specified precision.
528    ///
529    /// # Errors
530    ///
531    /// Returns [`ErrorsValidationRawReal<rug::Float>`](ErrorsValidationRawReal)
532    /// if the value fails any of these checks.
533    impl<const PRECISION: u32> ValidationPolicy for StrictFinitePolicy<rug::Float, PRECISION> {
534        type Value = rug::Float;
535        type Error = ErrorsValidationRawReal<rug::Float>;
536
537        fn validate_ref(value: &rug::Float) -> Result<(), Self::Error> {
538            let actual_precision = value.prec();
539            if actual_precision == PRECISION {
540                // rug::Float uses std::num::FpCategory via its own classify method
541                match value.classify() {
542                    FpCategory::Infinite => {
543                        if value.is_sign_positive() {
544                            Err(ErrorsValidationRawReal::IsPosInfinity {
545                                backtrace: Backtrace::force_capture(),
546                            })
547                        } else {
548                            Err(ErrorsValidationRawReal::IsNegInfinity {
549                                backtrace: Backtrace::force_capture(),
550                            })
551                        }
552                    }
553                    FpCategory::Nan => Err(ErrorsValidationRawReal::IsNaN {
554                        value: value.clone(),
555                        backtrace: Backtrace::force_capture(),
556                    }),
557                    FpCategory::Subnormal => Err(ErrorsValidationRawReal::IsSubnormal {
558                        value: value.clone(),
559                        backtrace: Backtrace::force_capture(),
560                    }),
561                    FpCategory::Zero | FpCategory::Normal => Ok(()),
562                }
563            } else {
564                Err(ErrorsValidationRawReal::PrecisionMismatch {
565                    input_value: value.clone(),
566                    actual_precision,
567                    requested_precision: PRECISION,
568                    backtrace: Backtrace::force_capture(),
569                })
570            }
571        }
572    }
573
574    /// Ensures both the real and imaginary parts of a [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html) value are strictly finite.
575    ///
576    /// This policy applies the [`StrictFinitePolicy<rug::Float>`](StrictFinitePolicy)
577    /// (which checks the underlying [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)) to both the real and imaginary
578    /// components of the [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html).
579    ///
580    /// # Errors
581    ///
582    /// Returns [`ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>`](ErrorsValidationRawComplex)
583    /// if either the real part, the imaginary part, or both parts fail the
584    /// `StrictFinitePolicy<RealRugStrictFinite<Precision>>` checks.
585    impl<const PRECISION: u32> ValidationPolicy for StrictFinitePolicy<rug::Complex, PRECISION> {
586        type Value = rug::Complex;
587        type Error = ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>;
588
589        fn validate_ref(value: &rug::Complex) -> Result<(), Self::Error> {
590            validate_complex::<StrictFinitePolicy<rug::Float, PRECISION>>(
591                value.real(),
592                value.imag(),
593            )
594        }
595    }
596}
597//-------------------------------------------------------------
598
599//------------------------------------------------------------------------------------------------
600/// Provides a set of fundamental floating-point classification checks.
601///
602/// This trait defines common methods to determine the characteristics of a numerical value,
603/// such as whether it is finite, infinite, NaN (Not a Number), or normal.
604/// It is implemented for standard floating-point types (`f64`), complex numbers
605/// (`num::Complex<f64>`), and their arbitrary-precision counterparts from the `rug`
606/// library (via `RealRugStrictFinite<P>` and `ComplexRugStrictFinite<P>` which internally use [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)
607/// and [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html)) when the "rug" feature is enabled.
608///
609/// `FpChecks` is often used as a supertrait for more general numerical traits like
610/// [`FpScalar`](crate::FpScalar), ensuring that types representing floating-point
611/// scalars can be queried for these basic properties. This is essential for
612/// validation policies and writing robust numerical algorithms.
613///
614/// Note that this trait focuses on these specific classifications. For more detailed
615/// categorization (like distinguishing subnormals or zero explicitly through this trait),
616/// types may provide other methods (e.g., `classify()` for `f64` and [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html),
617/// or `is_zero()` from [`FpScalar`](crate::FpScalar)).
618pub trait FpChecks {
619    /// Returns [`true`] if `self` is finite (i.e., not infinite and not NaN).
620    ///
621    /// For complex numbers, this typically means both the real and imaginary parts are finite.
622    fn is_finite(&self) -> bool;
623
624    /// Returns [`true`] if `self` is positive infinity or negative infinity.
625    ///
626    /// For complex numbers, this typically means at least one part is infinite,
627    /// and no part is NaN.
628    fn is_infinite(&self) -> bool;
629
630    /// Returns [`true`] if `self` is NaN (Not a Number).
631    ///
632    /// For complex numbers, this typically means at least one part is NaN.
633    fn is_nan(&self) -> bool;
634
635    /// Returns [`true`] if `self` is a *normal* number.
636    ///
637    /// A normal number is a finite, non-zero number that is not subnormal.
638    /// For complex numbers, this typically means both real and imaginary parts are normal
639    /// numbers (and thus individually finite, non-zero, and not subnormal).
640    fn is_normal(&self) -> bool;
641}
642
643#[duplicate_item(
644    T;
645    [f64];
646    [Complex::<f64>];
647)]
648impl FpChecks for T {
649    #[inline(always)]
650    fn is_finite(&self) -> bool {
651        T::is_finite(*self)
652    }
653
654    #[inline(always)]
655    fn is_infinite(&self) -> bool {
656        T::is_infinite(*self)
657    }
658
659    #[inline(always)]
660    fn is_nan(&self) -> bool {
661        T::is_nan(*self)
662    }
663
664    #[inline(always)]
665    fn is_normal(&self) -> bool {
666        T::is_normal(*self)
667    }
668}
669//------------------------------------------------------------------------------------------------
670
671//------------------------------------------------------------------------------------------------
672/// A validation policy that is active only in debug builds.
673///
674/// This struct acts as a wrapper around another [`ValidationPolicy`].
675///
676/// - In **debug builds** (`#[cfg(debug_assertions)]`), it delegates validation
677///   directly to the inner policy `P`.
678/// - In **release builds** (`#[cfg(not(debug_assertions))]`), it becomes a "no-op"
679///   (no operation), meaning its `validate` and `validate_ref` methods do nothing
680///   and always return `Ok`.
681///
682/// This is useful for enforcing strict, potentially expensive validations during
683/// development and testing, while eliminating their performance overhead in production code.
684///
685/// # Generic Parameters
686///
687/// - `P`: The inner [`ValidationPolicy`] to use during debug builds.
688///
689/// # Example
690///
691/// ```rust,ignore
692/// use num_valid::validation::{DebugValidationPolicy, StrictFinitePolicy};
693/// use try_create::ValidationPolicy;
694///
695/// // Define a policy that uses StrictFinitePolicy only in debug builds.
696/// type MyPolicy = DebugValidationPolicy<Native64RawRealStrictFinitePolicy>;
697///
698/// let nan_value = f64::NAN;
699/// let result = MyPolicy::validate(nan_value);
700///
701/// #[cfg(debug_assertions)]
702/// {
703///     // In debug mode, the validation fails because StrictFinitePolicy catches NaN.
704///     assert!(result.is_err());
705/// }
706///
707/// #[cfg(not(debug_assertions))]
708/// {
709///     // In release mode, the validation is skipped and always succeeds.
710///     assert!(result.is_ok());
711/// }
712/// ```
713pub struct DebugValidationPolicy<P: ValidationPolicy>(PhantomData<P>);
714
715impl<P: ValidationPolicy> ValidationPolicy for DebugValidationPolicy<P> {
716    type Value = P::Value;
717    type Error = P::Error;
718
719    #[inline(always)]
720    fn validate_ref(value: &Self::Value) -> Result<(), Self::Error> {
721        // In debug builds, this implementation delegates to the inner policy `P`.
722        #[cfg(debug_assertions)]
723        {
724            // Delegate to the inner policy's validation logic.
725            P::validate_ref(value)
726        }
727        #[cfg(not(debug_assertions))]
728        {
729            let _ = value; // Avoid unused variable warning in release mode.
730            // In release mode, this is a no-op.
731            // The validation is skipped, and we always return Ok.
732            // This allows the compiler to optimize away the validation logic entirely.
733            // This is useful for performance-critical code where validation is not needed in production.
734            // Note: The `value` is not used here, but we keep it to match the signature of `validate_ref`.
735            // This way, we can still call this method without needing to change the function signature.
736            // This is particularly useful when this policy is used in generic contexts
737            // where the `validate_ref` method is expected to take a reference to the value.
738            // The `PhantomData` ensures that the type system knows about `P` even
739            // though we don't use it directly in the release build.
740            // This is a common pattern in Rust to create zero-sized types that carry type information.
741            Ok(())
742        }
743    }
744}
745
746impl<P> ValidationPolicyReal for DebugValidationPolicy<P>
747where
748    P: ValidationPolicyReal,
749{
750    const PRECISION: u32 = P::PRECISION;
751}
752
753impl<P> ValidationPolicyComplex for DebugValidationPolicy<P>
754where
755    P: ValidationPolicyComplex,
756{
757    const PRECISION: u32 = P::PRECISION;
758}
759//------------------------------------------------------------------------------------------------
760
761/// Validates a value using the given policy, but only in debug builds.
762/// In release builds, this function is a no-op and should be optimized away.
763///
764/// # Panics
765///
766/// In debug builds, this function will panic if `P::validate_ref(value)` returns an `Err`.
767#[inline(always)]
768pub fn debug_validate<P: ValidationPolicy>(value: &P::Value, msg: &str) {
769    #[cfg(debug_assertions)]
770    {
771        if let Err(e) = P::validate_ref(value) {
772            panic!("Debug validation of {msg} value failed: {e}");
773        }
774    }
775    #[cfg(not(debug_assertions))]
776    {
777        // These are no-op in release builds
778        let _ = value;
779        let _ = msg;
780    }
781}
782
783//-------------------------------------------------------------
784#[cfg(test)]
785mod tests {
786    use super::*;
787
788    #[cfg(feature = "rug")]
789    use crate::{ComplexRugStrictFinite, RealRugStrictFinite};
790
791    mod fp_checks {
792        use super::*;
793        use num::Complex;
794
795        mod native64 {
796            use super::*;
797
798            mod real {
799                use super::*;
800
801                #[test]
802                fn test_is_finite() {
803                    assert!(<f64 as FpChecks>::is_finite(&1.0));
804                    assert!(!<f64 as FpChecks>::is_finite(&f64::INFINITY));
805                    assert!(!<f64 as FpChecks>::is_finite(&f64::NEG_INFINITY));
806                    assert!(!<f64 as FpChecks>::is_finite(&f64::NAN));
807                }
808
809                #[test]
810                fn test_is_infinite() {
811                    assert!(!<f64 as FpChecks>::is_infinite(&1.0));
812                    assert!(<f64 as FpChecks>::is_infinite(&f64::INFINITY));
813                    assert!(<f64 as FpChecks>::is_infinite(&f64::NEG_INFINITY));
814                    assert!(!<f64 as FpChecks>::is_infinite(&f64::NAN));
815                }
816
817                #[test]
818                fn test_is_nan() {
819                    assert!(!<f64 as FpChecks>::is_nan(&1.0));
820                    assert!(!<f64 as FpChecks>::is_nan(&f64::INFINITY));
821                    assert!(!<f64 as FpChecks>::is_nan(&f64::NEG_INFINITY));
822                    assert!(<f64 as FpChecks>::is_nan(&f64::NAN));
823                }
824
825                #[test]
826                fn test_is_normal() {
827                    assert!(<f64 as FpChecks>::is_normal(&1.0));
828                    assert!(!<f64 as FpChecks>::is_normal(&0.0));
829                    assert!(!<f64 as FpChecks>::is_normal(&f64::INFINITY));
830                    assert!(!<f64 as FpChecks>::is_normal(&f64::NEG_INFINITY));
831                    assert!(!<f64 as FpChecks>::is_normal(&f64::NAN));
832                    assert!(<f64 as FpChecks>::is_normal(&f64::MIN_POSITIVE));
833                }
834            }
835
836            mod complex {
837                use super::*;
838
839                #[test]
840                fn test_is_finite() {
841                    assert!(<Complex<f64> as FpChecks>::is_finite(&Complex::new(
842                        1.0, 1.0
843                    )));
844                    assert!(!<Complex<f64> as FpChecks>::is_finite(&Complex::new(
845                        f64::INFINITY,
846                        1.0
847                    )));
848                    assert!(!<Complex<f64> as FpChecks>::is_finite(&Complex::new(
849                        1.0,
850                        f64::NAN
851                    )));
852                }
853
854                #[test]
855                fn test_is_infinite() {
856                    assert!(!<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
857                        1.0, 1.0
858                    )));
859                    assert!(<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
860                        f64::INFINITY,
861                        1.0
862                    )));
863                    assert!(!<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
864                        1.0,
865                        f64::NAN
866                    )));
867                }
868
869                #[test]
870                fn test_is_nan() {
871                    assert!(!<Complex<f64> as FpChecks>::is_nan(&Complex::new(1.0, 1.0)));
872                    assert!(<Complex<f64> as FpChecks>::is_nan(&Complex::new(
873                        f64::NAN,
874                        1.0
875                    )));
876                    assert!(<Complex<f64> as FpChecks>::is_nan(&Complex::new(
877                        1.0,
878                        f64::NAN
879                    )));
880                }
881
882                #[test]
883                fn test_is_normal() {
884                    assert!(<Complex<f64> as FpChecks>::is_normal(&Complex::new(
885                        1.0, 1.0
886                    )));
887                    assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
888                        0.0, 0.0
889                    )));
890                    assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
891                        f64::INFINITY,
892                        1.0
893                    )));
894                    assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
895                        1.0,
896                        f64::NAN
897                    )));
898                }
899            }
900        }
901    }
902
903    mod strict_finite_policy {
904        use super::*;
905
906        mod raw_types {
907            use super::*;
908
909            mod native64 {
910                use super::*;
911
912                mod real {
913                    use super::*;
914
915                    #[test]
916                    fn f64_infinity() {
917                        let result = StrictFinitePolicy::<f64, 53>::validate(f64::INFINITY);
918                        assert!(matches!(
919                            result,
920                            Err(ErrorsValidationRawReal::IsPosInfinity { .. })
921                        ));
922
923                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&f64::INFINITY);
924                        assert!(matches!(
925                            result,
926                            Err(ErrorsValidationRawReal::IsPosInfinity { .. })
927                        ));
928
929                        let result = StrictFinitePolicy::<f64, 53>::validate(f64::NEG_INFINITY);
930                        assert!(matches!(
931                            result,
932                            Err(ErrorsValidationRawReal::IsNegInfinity { .. })
933                        ));
934
935                        let result =
936                            StrictFinitePolicy::<f64, 53>::validate_ref(&f64::NEG_INFINITY);
937                        assert!(matches!(
938                            result,
939                            Err(ErrorsValidationRawReal::IsNegInfinity { .. })
940                        ));
941                    }
942
943                    #[test]
944                    fn f64_nan() {
945                        let result = StrictFinitePolicy::<f64, 53>::validate(f64::NAN);
946                        assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
947
948                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&f64::NAN);
949                        assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
950                    }
951
952                    #[test]
953                    fn f64_subnormal() {
954                        // let subnormal = f64::MIN_POSITIVE / 2.0;
955                        let subnormal = f64::from_bits(1); // the smallest positive subnormal
956
957                        let result = StrictFinitePolicy::<f64, 53>::validate(subnormal);
958                        assert!(matches!(
959                            result,
960                            Err(ErrorsValidationRawReal::<f64>::IsSubnormal { .. })
961                        ));
962
963                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&subnormal);
964                        assert!(matches!(
965                            result,
966                            Err(ErrorsValidationRawReal::<f64>::IsSubnormal { .. })
967                        ));
968                    }
969
970                    #[test]
971                    fn f64_zero() {
972                        let value = 0.0;
973                        let result = StrictFinitePolicy::<f64, 53>::validate(value);
974                        assert!(matches!(result, Ok(0.0)));
975                    }
976
977                    #[test]
978                    fn f64_normal() {
979                        let value = 1.0;
980                        let result = StrictFinitePolicy::<f64, 53>::validate(value);
981                        assert!(matches!(result, Ok(1.0)));
982
983                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&value);
984                        assert!(matches!(result, Ok(())));
985                    }
986                }
987
988                mod complex {
989                    use super::*;
990                    use num::Complex;
991
992                    #[test]
993                    fn test_invalid_both_parts() {
994                        let z = Complex::new(f64::NAN, f64::NEG_INFINITY);
995                        let err =
996                            StrictFinitePolicy::<Complex<f64>, 53>::validate_ref(&z).unwrap_err();
997                        matches!(err, ErrorsValidationRawComplex::InvalidBothParts { .. });
998                    }
999
1000                    #[test]
1001                    fn complex_f64_invalid_real_part() {
1002                        let value = Complex::new(f64::INFINITY, 1.0);
1003                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1004                        assert!(matches!(
1005                            result,
1006                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1007                                source: ErrorsValidationRawReal::IsPosInfinity { .. },
1008                                ..
1009                            })
1010                        ));
1011
1012                        let value = Complex::new(f64::NAN, 1.0);
1013                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1014                        assert!(matches!(
1015                            result,
1016                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1017                                source: ErrorsValidationRawReal::IsNaN { .. },
1018                                ..
1019                            })
1020                        ));
1021
1022                        let value = Complex::new(f64::MIN_POSITIVE / 2.0, 1.0);
1023                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1024                        assert!(matches!(
1025                            result,
1026                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1027                                source: ErrorsValidationRawReal::IsSubnormal { .. },
1028                                ..
1029                            })
1030                        ));
1031                    }
1032
1033                    #[test]
1034                    fn complex_f64_invalid_imaginary_part() {
1035                        let value = Complex::new(1.0, f64::INFINITY);
1036                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1037                        assert!(matches!(
1038                            result,
1039                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1040                                source: ErrorsValidationRawReal::IsPosInfinity { .. },
1041                                ..
1042                            })
1043                        ));
1044
1045                        let value = Complex::new(1.0, f64::NAN);
1046                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1047                        assert!(matches!(
1048                            result,
1049                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1050                                source: ErrorsValidationRawReal::IsNaN { .. },
1051                                ..
1052                            })
1053                        ));
1054
1055                        let value = Complex::new(1.0, f64::MIN_POSITIVE / 2.0);
1056                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1057                        assert!(matches!(
1058                            result,
1059                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1060                                source: ErrorsValidationRawReal::IsSubnormal { .. },
1061                                ..
1062                            })
1063                        ));
1064                    }
1065
1066                    #[test]
1067                    fn complex_f64_zero() {
1068                        let value = Complex::new(0.0, 0.0);
1069                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1070                        assert!(result.is_ok());
1071                    }
1072
1073                    #[test]
1074                    fn complex_f64_normal_value() {
1075                        let value = Complex::new(1.0, 1.0);
1076                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1077                        assert!(result.is_ok());
1078                    }
1079                }
1080            }
1081
1082            #[cfg(feature = "rug")]
1083            mod rug53 {
1084                use super::*;
1085
1086                const PRECISION: u32 = 53; // Default precision for rug tests
1087
1088                mod float {
1089                    use super::*;
1090                    use rug::Float;
1091
1092                    #[test]
1093                    fn rug_float_valid() {
1094                        let x = Float::with_val(PRECISION, 1.0);
1095                        assert!(StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).is_ok());
1096                        assert_eq!(
1097                            StrictFinitePolicy::<Float, PRECISION>::validate(x.clone()).unwrap(),
1098                            x
1099                        );
1100                    }
1101
1102                    #[test]
1103                    fn rug_float_nan() {
1104                        let x = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1105                        let err =
1106                            StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
1107                        assert!(matches!(err, ErrorsValidationRawReal::IsNaN { .. }));
1108                        let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
1109                        assert!(matches!(err, ErrorsValidationRawReal::IsNaN { .. }));
1110                    }
1111
1112                    #[test]
1113                    fn rug_float_infinity() {
1114                        use rug::Float;
1115
1116                        let x = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1117                        let err =
1118                            StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
1119                        assert!(matches!(err, ErrorsValidationRawReal::IsPosInfinity { .. }));
1120                        let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
1121                        assert!(matches!(err, ErrorsValidationRawReal::IsPosInfinity { .. }));
1122
1123                        let x = Float::with_val(PRECISION, Float::parse("-inf").unwrap());
1124                        let err =
1125                            StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
1126                        assert!(matches!(err, ErrorsValidationRawReal::IsNegInfinity { .. }));
1127                        let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
1128                        assert!(matches!(err, ErrorsValidationRawReal::IsNegInfinity { .. }));
1129                    }
1130
1131                    #[test]
1132                    fn rug_float_zero() {
1133                        let value = Float::with_val(PRECISION, 0.0);
1134                        let result = StrictFinitePolicy::<Float, PRECISION>::validate_ref(&value);
1135                        assert!(result.is_ok());
1136                        let result =
1137                            StrictFinitePolicy::<Float, PRECISION>::validate(value.clone())
1138                                .unwrap();
1139                        assert_eq!(result, value);
1140                    }
1141                }
1142
1143                mod complex {
1144                    use super::*;
1145                    use rug::{Complex, Float};
1146
1147                    #[test]
1148                    fn rug_complex_valid() {
1149                        let z = Complex::with_val(PRECISION, (1.0, -2.0));
1150                        assert!(StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).is_ok());
1151                        assert_eq!(
1152                            StrictFinitePolicy::<Complex, PRECISION>::validate(z.clone()).unwrap(),
1153                            z
1154                        );
1155                    }
1156
1157                    #[test]
1158                    fn rug_complex_real_invalid() {
1159                        let nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1160                        let z = Complex::with_val(PRECISION, (&nan, 1.0));
1161                        let err =
1162                            StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
1163                        assert!(matches!(
1164                            err,
1165                            ErrorsValidationRawComplex::InvalidRealPart {
1166                                source: ErrorsValidationRawReal::IsNaN { .. }
1167                            }
1168                        ));
1169                    }
1170
1171                    #[test]
1172                    fn rug_complex_imag_invalid() {
1173                        let inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1174                        let z = Complex::with_val(PRECISION, (1.0, &inf));
1175                        let err =
1176                            StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
1177                        assert!(matches!(
1178                            err,
1179                            ErrorsValidationRawComplex::InvalidImaginaryPart {
1180                                source: ErrorsValidationRawReal::IsPosInfinity { .. }
1181                            }
1182                        ));
1183                    }
1184
1185                    #[test]
1186                    fn rug_complex_both_invalid() {
1187                        let nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1188                        let inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1189                        let z = Complex::with_val(PRECISION, (&nan, &inf));
1190                        let err =
1191                            StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
1192                        assert!(matches!(
1193                            err,
1194                            ErrorsValidationRawComplex::InvalidBothParts {
1195                                real_error: ErrorsValidationRawReal::IsNaN { .. },
1196                                imag_error: ErrorsValidationRawReal::IsPosInfinity { .. },
1197                                ..
1198                            }
1199                        ));
1200                    }
1201
1202                    #[test]
1203                    fn rug_complex_zero() {
1204                        let zero = Complex::with_val(
1205                            PRECISION,
1206                            (
1207                                Float::with_val(PRECISION, 0.0),
1208                                Float::with_val(PRECISION, 0.0),
1209                            ),
1210                        );
1211                        let result = StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&zero);
1212                        assert!(result.is_ok());
1213                        assert_eq!(
1214                            StrictFinitePolicy::<Complex, PRECISION>::validate(zero.clone())
1215                                .unwrap(),
1216                            zero
1217                        );
1218                    }
1219                }
1220            }
1221        }
1222
1223        // Module for testing the wrapper types that implement TryNew
1224        #[cfg(feature = "rug")]
1225        mod wrapper_types {
1226            use super::*;
1227
1228            mod rug_wrappers {
1229                use super::*;
1230                use crate::RealScalar;
1231                use num::One;
1232                use rug::{Complex as RugComplex, Float};
1233                use try_create::{IntoInner, TryNew};
1234
1235                const PRECISION: u32 = 53; // Default precision for rug tests
1236
1237                mod real {
1238                    use super::*;
1239
1240                    #[test]
1241                    fn try_new_real_rug_correct_precision() {
1242                        // Test RealRugStrictFinite with valid rug::Float and correct precision
1243                        let val = Float::with_val(PRECISION, 1.0);
1244                        let res = RealRugStrictFinite::<PRECISION>::try_new(val.clone());
1245                        assert!(res.is_ok(),);
1246                        assert_eq!(res.unwrap().into_inner(), val);
1247                    }
1248
1249                    #[test]
1250                    fn try_new_real_rug_incorrect_precision() {
1251                        // Test RealRugStrictFinite with valid rug::Float but incorrect precision
1252                        let val_wrong_prec = Float::with_val(PRECISION + 10, 1.0); // Different precision
1253                        let res = RealRugStrictFinite::<PRECISION>::try_new(val_wrong_prec);
1254                        assert!(res.is_err());
1255                        matches!(res, Err(ErrorsValidationRawReal::PrecisionMismatch { actual_precision, .. }) 
1256                    if actual_precision == PRECISION + 10);
1257                    }
1258
1259                    #[test]
1260                    fn try_new_real_rug_nan() {
1261                        // Test RealRugStrictFinite with rug::Float NaN
1262                        let val_nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1263                        let res = RealRugStrictFinite::<PRECISION>::try_new(val_nan);
1264                        assert!(res.is_err());
1265                        assert!(matches!(
1266                            res,
1267                            Err(ErrorsValidationRawReal::<Float>::IsNaN { .. })
1268                        ));
1269                    }
1270
1271                    #[test]
1272                    fn try_new_real_rug_infinity() {
1273                        // Test RealRugStrictFinite with rug::Float positive infinity
1274                        let val_inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1275                        let res_pos_inf = RealRugStrictFinite::<PRECISION>::try_new(val_inf);
1276                        assert!(res_pos_inf.is_err());
1277                        assert!(matches!(
1278                            res_pos_inf,
1279                            Err(ErrorsValidationRawReal::<Float>::IsPosInfinity { .. })
1280                        ));
1281
1282                        // Test RealRugStrictFinite with rug::Float negative infinity
1283                        let val_neg_inf = Float::with_val(PRECISION, Float::parse("-inf").unwrap());
1284                        let res_neg_inf = RealRugStrictFinite::<PRECISION>::try_new(val_neg_inf);
1285                        assert!(res_neg_inf.is_err());
1286                        assert!(matches!(
1287                            res_neg_inf,
1288                            Err(ErrorsValidationRawReal::<Float>::IsNegInfinity { .. })
1289                        ));
1290                    }
1291                }
1292
1293                mod complex {
1294                    use super::*;
1295                    use crate::ComplexScalarGetParts;
1296
1297                    #[test]
1298                    fn try_new_complex_rug_correct_precision() {
1299                        // Test ComplexRugStrictFinite with valid rug::Complex and correct precision for both parts
1300                        let val = RugComplex::with_val(PRECISION, (1.0, -2.0));
1301                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val.clone());
1302                        assert!(res.is_ok());
1303                        assert_eq!(res.unwrap().into_inner(), val);
1304                    }
1305
1306                    /*
1307                    #[test]
1308                    #[ignore = "because the precision used to construct a rug::Complex override the precision of the real and imaginary parts"]
1309                    fn try_new_complex_rug_precision_mismatch_real() {
1310                        // Test ComplexRugStrictFinite where real part has incorrect precision
1311                        let real_part_wrong_prec = Float::with_val(PRECISION - 1, 1.0);
1312                        let imag_part_correct_prec = Float::with_val(PRECISION, -2.0);
1313                        let val = RugComplex::with_val(
1314                            PRECISION,
1315                            (real_part_wrong_prec, imag_part_correct_prec),
1316                        ); // Construct with Float parts
1317
1318                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1319                        assert!(res.is_err());
1320                        assert!(matches!(
1321                            res,
1322                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1323                                source: ErrorsValidationRawReal::PrecisionMismatch { .. }
1324                            })
1325                        ));
1326                    }
1327
1328                    #[test]
1329                    #[ignore = "because the precision used to construct a rug::Complex override the precision of the real and imaginary parts"]
1330                    fn try_new_complex_rug_precision_mismatch_imag() {
1331                        // Test ComplexRugStrictFinite where imaginary part has incorrect precision
1332                        let real_part_correct_prec = Float::with_val(PRECISION, 1.0);
1333                        let imag_part_wrong_prec = Float::with_val(PRECISION - 1, -2.0);
1334                        let val = RugComplex::with_val(
1335                            PRECISION,
1336                            (real_part_correct_prec, imag_part_wrong_prec),
1337                        ); // Construct with Float parts
1338
1339                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1340                        assert!(res.is_err());
1341                        matches!(res, Err(ErrorsValidationRawComplex::InvalidImaginaryPart { source })
1342                            if matches!(source, ErrorsValidationRawReal::PrecisionMismatch { .. })
1343                        );
1344                    }
1345                    */
1346
1347                    #[test]
1348                    fn try_new_complex_rug_precision_mismatch_both() {
1349                        // Test ComplexRugStrictFinite where both parts have incorrect precision
1350                        let real_part_wrong_prec = Float::with_val(PRECISION - 1, 1.0);
1351                        let imag_part_wrong_prec = Float::with_val(PRECISION - 1, -2.0);
1352                        let val = RugComplex::with_val(
1353                            PRECISION - 1,
1354                            (real_part_wrong_prec, imag_part_wrong_prec),
1355                        ); // Construct with Float parts
1356
1357                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1358                        assert!(res.is_err());
1359                        assert!(matches!(
1360                            res,
1361                            Err(ErrorsValidationRawComplex::InvalidBothParts {
1362                                real_error: ErrorsValidationRawReal::PrecisionMismatch { .. },
1363                                imag_error: ErrorsValidationRawReal::PrecisionMismatch { .. },
1364                                ..
1365                            })
1366                        ));
1367                    }
1368
1369                    #[test]
1370                    fn try_new_complex_rug_invalid_real_nan() {
1371                        // Test ComplexRugStrictFinite where real part is NaN (value validation failure)
1372                        let nan_real = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1373                        let imag_part = Float::with_val(PRECISION, 1.0);
1374                        let val = RugComplex::with_val(PRECISION, (nan_real, imag_part));
1375
1376                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1377                        assert!(res.is_err());
1378                        assert!(matches!(
1379                            res,
1380                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1381                                source: ErrorsValidationRawReal::<Float>::IsNaN { .. },
1382                            })
1383                        ));
1384                    }
1385
1386                    #[test]
1387                    fn try_new_complex_rug_invalid_imag_inf() {
1388                        // Test ComplexRugStrictFinite where imaginary part is Infinity (value validation failure)
1389                        let real_part = Float::with_val(PRECISION, 1.0);
1390                        let inf_imag = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1391                        let val = RugComplex::with_val(PRECISION, (real_part, inf_imag));
1392
1393                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1394                        assert!(res.is_err());
1395                        assert!(matches!(
1396                            res,
1397                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1398                                source: ErrorsValidationRawReal::<Float>::IsPosInfinity { .. }
1399                            })
1400                        ));
1401                    }
1402
1403                    #[test]
1404                    fn try_new_complex_rug_invalid_both() {
1405                        // Test ComplexRugStrictFinite where both parts are invalid (value validation failure)
1406                        let nan_real = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1407                        let neg_inf_imag =
1408                            Float::with_val(PRECISION, Float::parse("-inf").unwrap());
1409                        let val = RugComplex::with_val(PRECISION, (nan_real, neg_inf_imag));
1410
1411                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1412                        assert!(res.is_err());
1413                        assert!(matches!(
1414                            res,
1415                            Err(ErrorsValidationRawComplex::InvalidBothParts {
1416                                real_error: ErrorsValidationRawReal::<Float>::IsNaN { .. },
1417                                imag_error: ErrorsValidationRawReal::<Float>::IsNegInfinity { .. },
1418                                ..
1419                            })
1420                        ));
1421                    }
1422
1423                    #[test]
1424                    fn try_from_complex_f64_to_complex_rug() {
1425                        // Test valid conversion
1426                        let z = num::Complex::new(1., -2.);
1427                        let z_rug = ComplexRugStrictFinite::<PRECISION>::try_from(z).unwrap();
1428                        let expected_real = RealRugStrictFinite::<PRECISION>::one();
1429                        let expected_imaginary =
1430                            RealRugStrictFinite::<PRECISION>::try_from_f64(-2.).unwrap();
1431                        assert_eq!(z_rug.real_part(), expected_real);
1432                        assert_eq!(z_rug.imag_part(), expected_imaginary);
1433
1434                        // Test invalid real part
1435                        let z = num::Complex::new(f64::INFINITY, -2.0);
1436                        let result = ComplexRugStrictFinite::<PRECISION>::try_from(z);
1437                        assert!(result.is_err());
1438                        assert!(matches!(
1439                            result,
1440                            Err(ErrorsValidationRawComplex::InvalidRealPart {
1441                                source: ErrorsTryFromf64::Output {
1442                                    source: ErrorsValidationRawReal::IsPosInfinity { .. }
1443                                },
1444                            })
1445                        ));
1446
1447                        // Test invalid imaginary part
1448                        let z = num::Complex::new(1.0, f64::NAN);
1449                        let result = ComplexRugStrictFinite::<PRECISION>::try_from(z).unwrap_err();
1450                        //println!("result = {result}");
1451                        assert!(matches!(
1452                            result,
1453                            ErrorsValidationRawComplex::InvalidImaginaryPart {
1454                                source: ErrorsTryFromf64::Output {
1455                                    source: ErrorsValidationRawReal::IsNaN { .. }
1456                                },
1457                            }
1458                        ));
1459                    }
1460                }
1461            }
1462        }
1463    }
1464
1465    mod conditional_validation_policy {
1466        use core::f64;
1467
1468        use super::*;
1469
1470        type F64StrictFinitePolicy = Native64RawRealStrictFinitePolicy;
1471
1472        // Define a policy that is strict in debug, and no-op in release.
1473        type MyConditionalPolicy = DebugValidationPolicy<F64StrictFinitePolicy>;
1474
1475        #[test]
1476        #[should_panic(expected = "Debug validation of input value failed: Value is NaN: NaN")]
1477        #[cfg(debug_assertions)]
1478        fn debug_validate_function() {
1479            // This should always succeed, regardless of build mode.
1480            let nan_value = f64::NAN;
1481            debug_validate::<F64StrictFinitePolicy>(&nan_value, "input");
1482        }
1483
1484        #[test]
1485        fn test_conditional_validation_on_valid_value() {
1486            let valid_value = 42.0;
1487            // This should always succeed, regardless of build mode.
1488            assert!(MyConditionalPolicy::validate(valid_value).is_ok());
1489        }
1490
1491        #[test]
1492        fn test_conditional_validation_on_invalid_value() {
1493            let invalid_value = f64::NAN;
1494            let result = MyConditionalPolicy::validate(invalid_value);
1495
1496            if cfg!(debug_assertions) {
1497                // In debug mode, we expect an error from StrictFinitePolicy.
1498                assert!(result.is_err(), "Expected validation to fail in debug mode");
1499                assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
1500            } else {
1501                // In release mode, we expect it to succeed (no-op).
1502                assert!(
1503                    result.is_ok(),
1504                    "Expected validation to succeed in release mode"
1505                );
1506            }
1507        }
1508    }
1509}