#![deny(rustdoc::broken_intra_doc_links)]

//! # Numerical Value Validation Module
//!
//! This module provides a framework for validating numerical scalar values,
//! particularly floating-point and complex numbers, including those with arbitrary precision
//! using the [`rug`](https://crates.io/crates/rug) library (when the "rug" feature is enabled).
//!
//! It defines:
//! - **Error Types**: Such as [`ErrorsValidationRawReal`], [`ErrorsValidationRawComplex`],
//!   and [`ErrorsTryFromf64`].
//!   These errors detail various validation failures like non-finite values,
//!   subnormal numbers (for `f64` and [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)), and precision mismatches for `rug` types.
//! - **Validation Policies**: Implemented via the [`ValidationPolicy`] trait from the
//!   [`try_create`](https://crates.io/crates/try_create) crate.
//!   The primary policy provided is [`StrictFinitePolicy`], which ensures that
//!   numbers are finite (not NaN or Infinity) and, for `f64` and [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html),
//!   not subnormal.
//! - **Floating-Point Checks Trait**: The [`FpChecks`] trait, defined within this module,
//!   provides common classification methods like `is_finite`, `is_nan`, `is_infinite`,
//!   and `is_normal`. This trait is implemented for standard and `rug`-based scalar types.
//! - **Enhanced Type Safety for HashMap Keys**: The [`GuaranteesFiniteValues`] marker trait enables
//!   full equality ([`Eq`]) and hashing ([`Hash`]) for validated types when their
//!   validation policy guarantees finite values. This allows validated types to be used
//!   as keys in [`HashMap`](std::collections::HashMap) and other hash-based collections.
//! - **Implementations**: Validation logic for [`StrictFinitePolicy`] and implementations of
//!   [`FpChecks`] are provided for [`f64`] and [`num::Complex<f64>`]. When the "rug" feature is enabled,
//!   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
//!   `RealRugStrictFinite` and `ComplexRugStrictFinite` (which utilize these underlying `rug` types).
//!
//! The goal of this module is to ensure numerical stability and correctness by allowing
//! functions to easily validate their inputs and outputs according to defined policies,
//! and to provide a common interface for basic floating-point characteristic checks.
//!
//! ## Enhanced Type Safety with [`GuaranteesFiniteValues`]
//!
//! One of the key innovations of this validation framework is the [`GuaranteesFiniteValues`]
//! marker trait. This trait enables compile-time verification that a validation policy
//! ensures all validated values are finite, which unlocks additional type system guarantees:
//!
//! - **Full Equality**: These types implement [`Eq`], making equality comparisons well-defined
//!   and symmetric.
//! - **Hashing Support**: With both [`Eq`] and [`Hash`] implemented, these types can be used
//!   as keys in hash-based collections like [`HashMap`](std::collections::HashMap) and [`HashSet`](std::collections::HashSet).
//!
//! **Note on Ordering**: The library intentionally does NOT implement [`Ord`]
//! even for finite-guaranteed types, as our custom [`Max`](crate::functions::Max)
//! and [`Min`](crate::functions::Min) traits provide more efficient reference-based
//! operations compared to [`Ord`]'s value-based interface.
//!
//! Currently, [`StrictFinitePolicy`] and [`DebugValidationPolicy<StrictFinitePolicy>`]
//! implement this marker trait.
//!
//!
//! ## **Usage Examples**
//!
//! ### Basic Validation
//!
//! ```rust
//! use num_valid::validation::{StrictFinitePolicy, ErrorsValidationRawReal};
//! use try_create::ValidationPolicy; // For the validate() method
//!
//! // Valid finite number
//! assert!(StrictFinitePolicy::<f64, 53>::validate(1.0).is_ok());
//!
//! // Invalid: NaN
//! let result = StrictFinitePolicy::<f64, 53>::validate(f64::NAN);
//! assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
//!
//! // Invalid: Infinity  
//! let result = StrictFinitePolicy::<f64, 53>::validate(f64::INFINITY);
//! assert!(matches!(result, Err(ErrorsValidationRawReal::IsPosInfinity { .. })));
//!
//! // Invalid: Subnormal
//! let subnormal_val = f64::from_bits(1); // Smallest positive subnormal f64
//! let result = StrictFinitePolicy::<f64, 53>::validate(subnormal_val);
//! assert!(matches!(result, Err(ErrorsValidationRawReal::IsSubnormal { .. })));
//! ```
//!
//! ### Using [`FpChecks`]
//!
//! ```rust
//! use num_valid::validation::FpChecks;
//! use num::Complex;
//!
//! let val_f64 = 1.0f64;
//! let nan_f64 = f64::NAN;
//! let inf_f64 = f64::INFINITY;
//!
//! assert!(val_f64.is_finite());
//! assert!(val_f64.is_normal());
//! assert!(nan_f64.is_nan());
//! assert!(!nan_f64.is_finite());
//! assert!(inf_f64.is_infinite());
//!
//! let val_complex = Complex::new(1.0, 2.0);
//! let nan_complex_real = Complex::new(f64::NAN, 2.0);
//! let inf_complex_imag = Complex::new(1.0, f64::NEG_INFINITY);
//!
//! assert!(val_complex.is_finite());
//! assert!(val_complex.is_normal());
//! assert!(nan_complex_real.is_nan());
//! assert!(!nan_complex_real.is_finite());
//! assert!(inf_complex_imag.is_infinite());
//! assert!(!inf_complex_imag.is_finite());
//! assert!(!inf_complex_imag.is_nan());
//! ```
//!
//! ### HashMap Usage with Finite Guarantees
//! ```rust
//! use num_valid::kernels::native64_validated::RealNative64StrictFinite;
//! use std::collections::HashMap;
//! use try_create::TryNew;
//!
//! let mut scores = HashMap::new();
//! let player1 = RealNative64StrictFinite::try_new(1.5).unwrap();
//! let player2 = RealNative64StrictFinite::try_new(2.0).unwrap();
//!
//! scores.insert(player1, 100);  // Works because Eq + Hash are implemented
//! scores.insert(player2, 95);
//!
//! assert_eq!(scores.len(), 2);
//! ```
//!
//! ### Conditional Debug Validation
//! ```rust
//! use num_valid::validation::{DebugValidationPolicy, Native64RawRealStrictFinitePolicy};
//! use try_create::ValidationPolicy;
//!
//! type ConditionalPolicy = DebugValidationPolicy<Native64RawRealStrictFinitePolicy>;
//!
//! let value = 42.0;
//! // Validates only in debug builds, no-op in release
//! assert!(ConditionalPolicy::validate(value).is_ok());
//! ```

use crate::{
    ComplexValidated, NumKernel, RealValidated,
    kernels::{RawComplexTrait, RawRealTrait, RawScalarTrait},
};
use duplicate::duplicate_item;
use num::{Complex, Integer};
use std::{
    backtrace::Backtrace,
    fmt::Display,
    hash::{Hash, Hasher},
    marker::PhantomData,
    num::FpCategory,
};
use thiserror::Error;
use try_create::ValidationPolicy;

//--------------------------------------------------------------------------------------------------
// Conditional Backtrace Support
//--------------------------------------------------------------------------------------------------

/// Captures a backtrace if the `backtrace` feature is enabled.
///
/// When the `backtrace` feature is enabled, this function captures a full backtrace
/// using [`capture_backtrace()`]. When disabled (default), it returns a
/// disabled backtrace with zero overhead.
///
/// # Performance
///
/// Backtrace capture is expensive (~1-10μs per capture). For performance-critical
/// code, leave the `backtrace` feature disabled. Enable it during development or
/// debugging to get detailed error traces.
///
/// # Example
///
/// ```toml
/// # In Cargo.toml, enable backtraces for debugging:
/// [dependencies]
/// num-valid = { version = "0.2", features = ["backtrace"] }
/// ```
#[inline(always)]
pub fn capture_backtrace() -> Backtrace {
    #[cfg(feature = "backtrace")]
    {
        Backtrace::force_capture()
    }
    #[cfg(not(feature = "backtrace"))]
    {
        Backtrace::disabled()
    }
}

/// Returns `true` if the `backtrace` feature is enabled.
///
/// This can be used to conditionally check whether backtraces are being captured.
///
/// # Example
///
/// ```rust
/// use num_valid::validation::is_backtrace_enabled;
///
/// if is_backtrace_enabled() {
///     println!("Backtraces are enabled - errors will include stack traces");
/// } else {
///     println!("Backtraces are disabled for performance");
/// }
/// ```
#[inline(always)]
pub const fn is_backtrace_enabled() -> bool {
    #[cfg(feature = "backtrace")]
    {
        true
    }
    #[cfg(not(feature = "backtrace"))]
    {
        false
    }
}

//--------------------------------------------------------------------------------------------------

/// A marker for policies that apply to real types.
///
/// This trait refines the generic [`ValidationPolicy`] for types that implement
/// [`RawRealTrait`]. It adds two key constraints:
///
/// 1.  It associates a `const PRECISION` with the policy, essential for backends
///     like `rug`.
/// 2.  It enforces that the policy's `Error` type must be the same as the canonical
///     `ValidationErrors` type defined on the raw scalar itself. This architectural
///     choice ensures consistent error handling throughout the library.
pub trait ValidationPolicyReal:
    ValidationPolicy<
        Value: RawRealTrait,
        Error = <<Self as ValidationPolicy>::Value as RawScalarTrait>::ValidationErrors,
    >
{
    const PRECISION: u32;
}

/// A marker for policies that apply to complex types.
///
/// This trait refines the generic [`ValidationPolicy`] for types that implement
/// [`RawComplexTrait`]. It adds two key constraints:
///
/// 1.  It associates a `const PRECISION` with the policy, essential for backends
///     like `rug`.
/// 2.  It enforces that the policy's `Error` type must be the same as the canonical
///     `ValidationErrors` type defined on the raw scalar itself. This architectural
///     choice ensures consistent error handling throughout the library.
pub trait ValidationPolicyComplex:
    ValidationPolicy<
        Value: RawComplexTrait,
        Error = <<Self as ValidationPolicy>::Value as RawScalarTrait>::ValidationErrors,
    >
{
    const PRECISION: u32;
}

//--------------------------------------------------------------------------------------------------
// Error types
//--------------------------------------------------------------------------------------------------

//--------------------------------------------
// Errors for raw real number validation
//--------------------------------------------
/// Errors that can occur during the validation of a raw real number.
///
/// This enum is generic over `RawReal`, which is typically `f64` or [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html).
/// It covers common validation failures like non-finite values, subnormality, infinity, and precision mismatch.
#[derive(Debug, Error)]
pub enum ErrorsValidationRawReal<RawReal: std::fmt::Debug + Display + Clone> {
    /// The value is subnormal.
    #[error("Value is subnormal: {value}")]
    IsSubnormal {
        /// The subnormal value that failed validation.
        value: RawReal,

        /// A captured backtrace for debugging purposes.
        backtrace: Backtrace,
    },

    /// The value is NaN (Not a Number).
    #[error("Value is NaN: {value}")]
    IsNaN {
        /// The NaN value that failed validation.
        value: RawReal,

        /// A captured backtrace for debugging purposes.
        backtrace: Backtrace,
    },

    /// The value is positive infinity.
    #[error("Value is positive infinity")]
    IsPosInfinity {
        /// The positive infinity value that failed validation.
        backtrace: Backtrace,
    },

    /// The value is negative infinity.
    #[error("Value is negative infinity")]
    IsNegInfinity {
        /// The negative infinity value that failed validation.
        backtrace: Backtrace,
    },

    /// The precision of the input does not match the requested precision.
    #[error(
        "precision mismatch: input real has precision {actual_precision}, \
        but the requested precision is {requested_precision}"
    )]
    PrecisionMismatch {
        /// The input value.
        input_value: RawReal,

        /// The precision of the input value.
        actual_precision: u32,

        /// The requested precision.
        requested_precision: u32,

        /// A captured backtrace for debugging purposes.
        backtrace: Backtrace,
    },
    /*
    // The following variants are placeholders for potential future validation policies
    // and are not currently used by StrictFinitePolicy.

    #[error("Value is not strictly positive: {value}")]
    NotPositive {
        value: RawReal,
        backtrace: Backtrace,
    },

    #[error("Value is negative: {value}")]
    Negative {
        value: RawReal,
        backtrace: Backtrace,
    },

    #[error("Value is zero: {value}")]
    IsZero {
        value: RawReal,
        backtrace: Backtrace,
    },

    #[error("Value {value} is outside the allowed range [{min}, {max}]")]
    OutOfRange {
        value: RawReal,
        min: RawReal,
        max: RawReal,
        backtrace: Backtrace,
    },
    */
}

//--------------------------------------------
// Errors for complex number validation
//--------------------------------------------
/// Errors that can occur during the validation of a complex number.
///
/// This enum is generic over `ErrorValidationReal`, which is the error type returned
/// by validating the real and imaginary parts of the complex number (e.g.,
/// [`ErrorsValidationRawReal<f64>`](ErrorsValidationRawReal)).
///
/// It indicates whether the real part, the imaginary part, or both parts failed validation.
#[derive(Debug, Error)]
pub enum ErrorsValidationRawComplex<ErrorValidationReal: std::error::Error> {
    /// The real part of the complex number failed validation.
    #[error("Real part validation error: {source}")]
    InvalidRealPart {
        /// The underlying validation error for the real part.
        #[source]
        #[backtrace]
        source: ErrorValidationReal,
    },

    /// The imaginary part of the complex number failed validation.
    #[error("Imaginary part validation error: {source}")]
    InvalidImaginaryPart {
        /// The underlying validation error for the imaginary part.
        #[source]
        #[backtrace]
        source: ErrorValidationReal,
    },

    /// Both the real and imaginary parts of the complex number failed validation.
    #[error("Both real and imaginary parts are invalid: real={real_error}, imag={imag_error}")]
    InvalidBothParts {
        /// The validation error for the real part.
        real_error: ErrorValidationReal,

        /// The validation error for the imaginary part.
        imag_error: ErrorValidationReal,
    },
}

//-------------------------------------------------------------
/// Errors that can occur when trying to convert an `f64` to another real-type scalar of type `RawReal`.
///
/// This enum distinguishes between:
/// 1.  Failures due to the `f64` not being exactly representable at the target `PRECISION`
///     of the `RawReal` type.
/// 2.  General validation failures of the output `RawReal` (e.g., NaN, Infinity, subnormal).
#[derive(Debug, Error)]
pub enum ErrorsTryFromf64<RawReal: RawRealTrait> {
    /// The input `f64` value is not representable exactly at the target `precision` using the output `RawReal` type.
    #[error(
        "the input f64 value ({value_in:?}) cannot be exactly represented with the specific precision ({precision:?}). Try to increase the precision."
    )]
    NonRepresentableExactly {
        /// The `f64` value that could not be exactly represented.
        value_in: f64,

        /// The input `value_in` after the conversion to the type `RawReal` with the requested `precision`.
        value_converted_from_f64: RawReal,

        /// The precision that was requested.
        precision: u32,

        /// A captured backtrace for debugging purposes.
        backtrace: Backtrace,
    },

    /// The output value failed validation.
    #[error("the output value is invalid!")]
    Output {
        /// The underlying validation error for the `output` value.
        #[from]
        source: ErrorsValidationRawReal<RawReal>,
    },
}
//-------------------------------------------------------------

//-------------------------------------------------------------
#[derive(Debug, Error)]
pub enum ErrorsRawRealToInteger<RawReal: RawRealTrait, IntType: Integer> {
    /// The `RawReal` value is not finite (i.e., it is NaN or Infinity).
    #[error("Value is not finite: {value}")]
    NotFinite {
        /// The non-finite `RawReal` value that caused the error.
        value: RawReal,

        /// A captured backtrace for debugging purposes.
        backtrace: Backtrace,
    },

    /// The `RawReal` value is not an integer (i.e., it has a fractional part).
    #[error("Value is not an integer: {value}")]
    NotAnInteger {
        /// The `RawReal` value that is not an integer.
        value: RawReal,

        /// A captured backtrace for debugging purposes.
        backtrace: Backtrace,
    },

    /// The `RawReal` value is outside the range representable by the target integer type `IntType`.
    #[error("Value {value} is out of range [{min}, {max}] for target integer type")]
    OutOfRange {
        /// The `RawReal` value that is out of range.
        value: RawReal,

        /// The minimum representable value of the target integer type `IntType`.
        min: IntType,

        /// The maximum representable value of the target integer type `IntType`.
        max: IntType,

        /// A captured backtrace for debugging purposes.
        backtrace: Backtrace,
    },
}
//-------------------------------------------------------------

/// A validation policy that checks for strict finiteness.
///
/// 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:
/// - Not NaN (Not a Number).
/// - Not positive or negative Infinity.
/// - Not subnormal (for `f64`).
///   While [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) maintains its specified precision rather than having distinct subnormal
///   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
///   [`FpCategory::Subnormal`] (e.g., results of underflow that are tiny but not exactly zero).
///
/// For complex types (`ScalarType = `[`Complex<f64>`], `ScalarType = `[`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html)), this policy applies
/// the strict finiteness check to both the real and imaginary parts.
///
/// This struct is a Zero-Sized Type (ZST) and uses [`PhantomData`] to associate
/// with the `ScalarType` it validates.
pub struct StrictFinitePolicy<ScalarType: Sized, const PRECISION: u32>(PhantomData<ScalarType>);

/// A type alias for a validation policy that enforces strict finiteness for the raw [`f64`] type.
///
/// This is a convenient alias for [`StrictFinitePolicy<f64, 53>`], configured specifically
/// for Rust's native 64-bit floating-point number. It is used to validate that a given [`f64`]
/// value is suitable for use within the library's validated types.
///
/// This policy ensures that a `f64` value is:
/// - Not `NaN` (Not a Number).
/// - Not positive or negative `Infinity`.
/// - Not a subnormal number.
///
/// It is a fundamental building block for the native `f64` kernel, providing the validation
/// logic for [`RealNative64StrictFinite`](crate::kernels::native64_validated::RealNative64StrictFinite).
///
/// # Example
///
/// ```rust
/// use num_valid::validation::{Native64RawRealStrictFinitePolicy, ErrorsValidationRawReal};
/// use try_create::ValidationPolicy;
///
/// // A valid finite number passes validation.
/// assert!(Native64RawRealStrictFinitePolicy::validate(1.0).is_ok());
///
/// // NaN fails validation.
/// let result_nan = Native64RawRealStrictFinitePolicy::validate(f64::NAN);
/// assert!(matches!(result_nan, Err(ErrorsValidationRawReal::IsNaN { .. })));
///
/// // Infinity fails validation.
/// let result_inf = Native64RawRealStrictFinitePolicy::validate(f64::INFINITY);
/// assert!(matches!(result_inf, Err(ErrorsValidationRawReal::IsPosInfinity { .. })));
/// ```
pub type Native64RawRealStrictFinitePolicy = StrictFinitePolicy<f64, 53>;

/// A type alias for a validation policy that enforces strict finiteness for the raw [`num::Complex<f64>`] type.
///
/// This is a convenient alias for [`StrictFinitePolicy<Complex<f64>, 53>`], configured for
/// Rust's native 64-bit complex numbers. It is used to validate that a given [`num::Complex<f64>`]
/// value is suitable for use within the library's validated types.
///
/// This policy applies the strict finiteness check (as defined by [`Native64RawRealStrictFinitePolicy`])
/// to both the real and imaginary parts of the complex number. A [`num::Complex<f64>`] value is considered
/// valid if and only if both of its components are finite (not `NaN`, `Infinity`, or subnormal).
///
/// It is a fundamental building block for the native `f64` kernel, providing the validation
/// logic for [`ComplexNative64StrictFinite`](crate::kernels::native64_validated::ComplexNative64StrictFinite).
///
/// # Example
///
/// ```rust
/// use num_valid::validation::{Native64RawComplexStrictFinitePolicy, ErrorsValidationRawComplex, ErrorsValidationRawReal};
/// use try_create::ValidationPolicy;
/// use num::Complex;
///
/// // A complex number with valid finite parts passes validation.
/// let valid_complex = Complex::new(1.0, -2.0);
/// assert!(Native64RawComplexStrictFinitePolicy::validate(valid_complex).is_ok());
///
/// // A complex number with NaN in the real part fails validation.
/// let nan_complex = Complex::new(f64::NAN, 2.0);
/// let result_nan = Native64RawComplexStrictFinitePolicy::validate(nan_complex);
/// assert!(matches!(result_nan, Err(ErrorsValidationRawComplex::InvalidRealPart { .. })));
///
/// // A complex number with Infinity in the imaginary part fails validation.
/// let inf_complex = Complex::new(1.0, f64::INFINITY);
/// let result_inf = Native64RawComplexStrictFinitePolicy::validate(inf_complex);
/// assert!(matches!(result_inf, Err(ErrorsValidationRawComplex::InvalidImaginaryPart { .. })));
///
/// // A complex number with invalid parts in both also fails.
/// let both_invalid = Complex::new(f64::NAN, f64::INFINITY);
/// let result_both = Native64RawComplexStrictFinitePolicy::validate(both_invalid);
/// assert!(matches!(result_both, Err(ErrorsValidationRawComplex::InvalidBothParts { .. })));
/// ```
pub type Native64RawComplexStrictFinitePolicy = StrictFinitePolicy<Complex<f64>, 53>;

/// Ensures the `f64` value is strictly finite.
///
/// This policy checks if the `f64` value is:
/// - Not NaN (Not a Number).
/// - Not positive or negative Infinity.
/// - Not subnormal.
///
/// # Errors
///
/// Returns [`ErrorsValidationRawReal<f64>`](ErrorsValidationRawReal) if the value
/// fails any of these checks.
impl ValidationPolicy for Native64RawRealStrictFinitePolicy {
    type Value = f64;
    type Error = ErrorsValidationRawReal<f64>;

    fn validate_ref(value: &f64) -> Result<(), Self::Error> {
        match value.classify() {
            FpCategory::Nan => Err(ErrorsValidationRawReal::IsNaN {
                value: *value,
                backtrace: capture_backtrace(),
            }),
            FpCategory::Infinite => {
                if value.is_sign_positive() {
                    Err(ErrorsValidationRawReal::IsPosInfinity {
                        backtrace: capture_backtrace(),
                    })
                } else {
                    Err(ErrorsValidationRawReal::IsNegInfinity {
                        backtrace: capture_backtrace(),
                    })
                }
            }
            FpCategory::Subnormal => Err(ErrorsValidationRawReal::IsSubnormal {
                value: *value,
                backtrace: capture_backtrace(),
            }),
            FpCategory::Normal | FpCategory::Zero => Ok(()),
        }
    }
}

/// Validates a complex number by checking both its real and imaginary parts.
///
/// This function uses the provided real validation policy `P` to validate both parts.
/// If both parts are valid, it returns `Ok(())`. If either part is invalid,
/// it returns an appropriate error variant from the `ErrorsValidationRawComplex` enum.
///
/// # Errors
/// Returns [`ErrorsValidationRawComplex<P::Error>`](ErrorsValidationRawComplex)
/// if either the real part, the imaginary part, or both parts fail validation
/// using the policy `P`.
pub(crate) fn validate_complex<P: ValidationPolicyReal>(
    real_part: &P::Value,
    imag_part: &P::Value,
) -> Result<(), ErrorsValidationRawComplex<P::Error>> {
    let real_validation = P::validate_ref(real_part);
    let imag_validation = P::validate_ref(imag_part);
    match (real_validation, imag_validation) {
        (Ok(()), Ok(())) => Ok(()),
        (Ok(()), Err(imag_err)) => {
            Err(ErrorsValidationRawComplex::InvalidImaginaryPart { source: imag_err })
        }
        (Err(real_err), Ok(())) => {
            Err(ErrorsValidationRawComplex::InvalidRealPart { source: real_err })
        }
        (Err(real_err), Err(imag_err)) => Err(ErrorsValidationRawComplex::InvalidBothParts {
            real_error: real_err,
            imag_error: imag_err,
        }),
    }
}

/// Ensures both the real and imaginary parts of a `Complex<f64>` value are strictly finite.
///
/// This policy applies the [`StrictFinitePolicy<f64>`](StrictFinitePolicy) to both
/// the real and imaginary components of the complex number.
///
/// # Errors
///
/// Returns [`ErrorsValidationRawComplex<ErrorsValidationRawReal<f64>>`](ErrorsValidationRawComplex)
/// if either the real part, the imaginary part, or both parts fail the
/// `StrictFinitePolicy<f64>` checks.
impl ValidationPolicy for Native64RawComplexStrictFinitePolicy {
    type Value = Complex<f64>;
    type Error = ErrorsValidationRawComplex<ErrorsValidationRawReal<f64>>;

    fn validate_ref(value: &Complex<f64>) -> Result<(), Self::Error> {
        validate_complex::<Native64RawRealStrictFinitePolicy>(&value.re, &value.im)
    }
}

impl<RawReal: RawRealTrait, const PRECISION: u32> ValidationPolicyReal
    for StrictFinitePolicy<RawReal, PRECISION>
where
    StrictFinitePolicy<RawReal, PRECISION>:
        ValidationPolicy<Value = RawReal, Error = <RawReal as RawScalarTrait>::ValidationErrors>,
{
    const PRECISION: u32 = PRECISION;
}

impl<RawComplex: RawComplexTrait, const PRECISION: u32> ValidationPolicyComplex
    for StrictFinitePolicy<RawComplex, PRECISION>
where
    StrictFinitePolicy<RawComplex, PRECISION>: ValidationPolicy<
            Value = RawComplex,
            Error = <RawComplex as RawScalarTrait>::ValidationErrors,
        >,
{
    const PRECISION: u32 = PRECISION;
}

//--------------------------------------------------------------------------------------------------
// Rug Implementations (conditional compilation)
//--------------------------------------------------------------------------------------------------

#[cfg(feature = "rug")]
pub(crate) mod rug_impls {
    use super::*; // Imports items from the parent module (validation)

    /// Ensures the [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) value is strictly finite.
    ///
    /// This policy checks if the underlying [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) value:
    /// - Is not NaN (Not a Number).
    /// - Is not positive nor negative Infinity.
    /// - Has the specified precision (equal to `PRECISION`).
    ///
    /// Note: [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) does not have a direct concept of "subnormal" in the same
    /// way IEEE 754 floats do; it maintains its specified precision.
    ///
    /// # Errors
    ///
    /// Returns [`ErrorsValidationRawReal<rug::Float>`](ErrorsValidationRawReal)
    /// if the value fails any of these checks.
    impl<const PRECISION: u32> ValidationPolicy for StrictFinitePolicy<rug::Float, PRECISION> {
        type Value = rug::Float;
        type Error = ErrorsValidationRawReal<rug::Float>;

        fn validate_ref(value: &rug::Float) -> Result<(), Self::Error> {
            let actual_precision = value.prec();
            if actual_precision == PRECISION {
                // rug::Float uses std::num::FpCategory via its own classify method
                match value.classify() {
                    FpCategory::Infinite => {
                        if value.is_sign_positive() {
                            Err(ErrorsValidationRawReal::IsPosInfinity {
                                backtrace: capture_backtrace(),
                            })
                        } else {
                            Err(ErrorsValidationRawReal::IsNegInfinity {
                                backtrace: capture_backtrace(),
                            })
                        }
                    }
                    FpCategory::Nan => Err(ErrorsValidationRawReal::IsNaN {
                        value: value.clone(),
                        backtrace: capture_backtrace(),
                    }),
                    FpCategory::Subnormal => Err(ErrorsValidationRawReal::IsSubnormal {
                        value: value.clone(),
                        backtrace: capture_backtrace(),
                    }),
                    FpCategory::Zero | FpCategory::Normal => Ok(()),
                }
            } else {
                Err(ErrorsValidationRawReal::PrecisionMismatch {
                    input_value: value.clone(),
                    actual_precision,
                    requested_precision: PRECISION,
                    backtrace: capture_backtrace(),
                })
            }
        }
    }

    /// Ensures both the real and imaginary parts of a [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html) value are strictly finite.
    ///
    /// This policy applies the [`StrictFinitePolicy<rug::Float>`](StrictFinitePolicy)
    /// (which checks the underlying [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)) to both the real and imaginary
    /// components of the [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html).
    ///
    /// # Errors
    ///
    /// Returns [`ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>`](ErrorsValidationRawComplex)
    /// if either the real part, the imaginary part, or both parts fail the
    /// `StrictFinitePolicy<RealRugStrictFinite<Precision>>` checks.
    impl<const PRECISION: u32> ValidationPolicy for StrictFinitePolicy<rug::Complex, PRECISION> {
        type Value = rug::Complex;
        type Error = ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>;

        fn validate_ref(value: &rug::Complex) -> Result<(), Self::Error> {
            validate_complex::<StrictFinitePolicy<rug::Float, PRECISION>>(
                value.real(),
                value.imag(),
            )
        }
    }
}
//-------------------------------------------------------------

//------------------------------------------------------------------------------------------------
/// Provides a set of fundamental floating-point classification checks.
///
/// This trait defines common methods to determine the characteristics of a numerical value,
/// such as whether it is finite, infinite, NaN (Not a Number), or normal.
/// It is implemented for standard floating-point types (`f64`), complex numbers
/// (`num::Complex<f64>`), and their arbitrary-precision counterparts from the `rug`
/// library (via `RealRugStrictFinite<P>` and `ComplexRugStrictFinite<P>` which internally use [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)
/// and [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html)) when the "rug" feature is enabled.
///
/// `FpChecks` is often used as a supertrait for more general numerical traits like
/// [`FpScalar`](crate::FpScalar), ensuring that types representing floating-point
/// scalars can be queried for these basic properties. This is essential for
/// validation policies and writing robust numerical algorithms.
///
/// Note that this trait focuses on these specific classifications. For more detailed
/// categorization (like distinguishing subnormals or zero explicitly through this trait),
/// types may provide other methods (e.g., `classify()` for `f64` and [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html),
/// or `is_zero()` from [`FpScalar`](crate::FpScalar)).
pub trait FpChecks {
    /// Returns [`true`] if `self` is finite (i.e., not infinite and not NaN).
    ///
    /// For complex numbers, this typically means both the real and imaginary parts are finite.
    fn is_finite(&self) -> bool;

    /// Returns [`true`] if `self` is positive infinity or negative infinity.
    ///
    /// For complex numbers, this typically means at least one part is infinite,
    /// and no part is NaN.
    fn is_infinite(&self) -> bool;

    /// Returns [`true`] if `self` is NaN (Not a Number).
    ///
    /// For complex numbers, this typically means at least one part is NaN.
    fn is_nan(&self) -> bool;

    /// Returns [`true`] if `self` is a *normal* number.
    ///
    /// A normal number is a finite, non-zero number that is not subnormal.
    /// For complex numbers, this typically means both real and imaginary parts are normal
    /// numbers (and thus individually finite, non-zero, and not subnormal).
    fn is_normal(&self) -> bool;
}

#[duplicate_item(
    T;
    [f64];
    [Complex::<f64>];
)]
impl FpChecks for T {
    #[inline(always)]
    fn is_finite(&self) -> bool {
        T::is_finite(*self)
    }

    #[inline(always)]
    fn is_infinite(&self) -> bool {
        T::is_infinite(*self)
    }

    #[inline(always)]
    fn is_nan(&self) -> bool {
        T::is_nan(*self)
    }

    #[inline(always)]
    fn is_normal(&self) -> bool {
        T::is_normal(*self)
    }
}
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
/// A validation policy that is active only in debug builds.
///
/// This struct acts as a wrapper around another [`ValidationPolicy`].
///
/// - In **debug builds** (`#[cfg(debug_assertions)]`), it delegates validation
///   directly to the inner policy `P`.
/// - In **release builds** (`#[cfg(not(debug_assertions))]`), it becomes a "no-op"
///   (no operation), meaning its `validate` and `validate_ref` methods do nothing
///   and always return `Ok`.
///
/// This is useful for enforcing strict, potentially expensive validations during
/// development and testing, while eliminating their performance overhead in production code.
///
/// # Generic Parameters
///
/// - `P`: The inner [`ValidationPolicy`] to use during debug builds.
///
/// # Example
///
/// ```rust,ignore
/// use num_valid::{validation::{DebugValidationPolicy, StrictFinitePolicy, Native64RawRealStrictFinitePolicy}};
/// use try_create::ValidationPolicy;
///
/// // Define a policy that uses Native64RawRealStrictFinitePolicy
/// // (i.e. StrictFinitePolicy<f64, 53>) only in debug builds.
/// type MyPolicy = DebugValidationPolicy<Native64RawRealStrictFinitePolicy>;
///
/// let nan_value = f64::NAN;
/// let result = MyPolicy::validate(nan_value);
///
/// #[cfg(debug_assertions)]
/// {
///     // In debug mode, the validation fails because StrictFinitePolicy catches NaN.
///     assert!(result.is_err());
/// }
///
/// #[cfg(not(debug_assertions))]
/// {
///     // In release mode, the validation is skipped and always succeeds.
///     assert!(result.is_ok());
/// }
/// ```
pub struct DebugValidationPolicy<P: ValidationPolicy>(PhantomData<P>);

impl<P: ValidationPolicy> ValidationPolicy for DebugValidationPolicy<P> {
    type Value = P::Value;
    type Error = P::Error;

    #[inline(always)]
    fn validate_ref(value: &Self::Value) -> Result<(), Self::Error> {
        // In debug builds, this implementation delegates to the inner policy `P`.
        #[cfg(debug_assertions)]
        {
            // Delegate to the inner policy's validation logic.
            P::validate_ref(value)
        }
        #[cfg(not(debug_assertions))]
        {
            let _ = value; // Avoid unused variable warning in release mode.
            // In release mode, this is a no-op.
            // The validation is skipped, and we always return Ok.
            // This allows the compiler to optimize away the validation logic entirely.
            // This is useful for performance-critical code where validation is not needed in production.
            // Note: The `value` is not used here, but we keep it to match the signature of `validate_ref`.
            // This way, we can still call this method without needing to change the function signature.
            // This is particularly useful when this policy is used in generic contexts
            // where the `validate_ref` method is expected to take a reference to the value.
            // The `PhantomData` ensures that the type system knows about `P` even
            // though we don't use it directly in the release build.
            // This is a common pattern in Rust to create zero-sized types that carry type information.
            Ok(())
        }
    }
}

impl<P> ValidationPolicyReal for DebugValidationPolicy<P>
where
    P: ValidationPolicyReal,
{
    const PRECISION: u32 = P::PRECISION;
}

impl<P> ValidationPolicyComplex for DebugValidationPolicy<P>
where
    P: ValidationPolicyComplex,
{
    const PRECISION: u32 = P::PRECISION;
}
//------------------------------------------------------------------------------------------------

/// Validates a value using the given policy, but only in debug builds.
/// In release builds, this function is a no-op and should be optimized away.
///
/// # Panics
///
/// In debug builds, this function will panic if `P::validate_ref(value)` returns an `Err`.
#[inline(always)]
pub fn debug_validate<P: ValidationPolicy>(value: &P::Value, msg: &str) {
    #[cfg(debug_assertions)]
    {
        if let Err(e) = P::validate_ref(value) {
            panic!("Debug validation of {msg} value failed: {e}");
        }
    }
    #[cfg(not(debug_assertions))]
    {
        // These are no-op in release builds
        let _ = value;
        let _ = msg;
    }
}
//-------------------------------------------------------------

//-------------------------------------------------------------
/// A marker trait for validation policies that guarantee finite real values.
///
/// This trait serves as a compile-time marker to identify policies that ensure
/// all validated real values are always finite (never NaN or infinite). It enables
/// the implementation of full equality ([`Eq`]) and hashing ([`Hash`]) for
/// validated types, allowing them to be used as keys in hash-based collections
/// like [`HashMap`](std::collections::HashMap) and [`HashSet`](std::collections::HashSet).
///
/// ## Enabled Features
///
/// When a validation policy implements this trait, the corresponding validated types
/// (like [`RealValidated<K>`](crate::RealValidated)) automatically gain:
///
/// - **Full Equality ([`Eq`])**: Equality becomes reflexive, symmetric, and transitive
///   since NaN values (which violate these properties) are excluded.
/// - **Hashing ([`Hash`])**: Combined with [`Eq`], this allows validated types to be
///   used as keys in hash-based collections like [`HashMap`](std::collections::HashMap) and [`HashSet`](std::collections::HashSet).
///
/// ## Hashing Implementation Details
///
/// The [`Hash`] implementation for validated types:
/// - Delegates to the underlying raw type's [`RawScalarTrait::compute_hash`] method
/// - Handles IEEE 754 floating-point edge cases correctly (e.g., signed zeros)
/// - Maintains the contract that `a == b` implies `hash(a) == hash(b)`
/// - Works with both native `f64`/`Complex<f64>` and arbitrary-precision `rug` types
///
/// ## Design Decision: No Total Ordering
///
/// This trait intentionally does **not** enable total ordering ([`Ord`]) because:
/// 1. The library's comparison functions use efficient reference-based [`PartialOrd`]
/// 2. [`Max`](crate::functions::Max)/[`Min`](crate::functions::Min) traits provide better performance than value-based [`Ord`]
/// 3. Mathematical operations work naturally with partial ordering semantics
///
/// ## Use Cases
///
/// This marker trait enables validated numerical types to be used in contexts that
/// require [`Eq`] and [`Hash`]:
///
/// ```rust
/// use num_valid::{RealNative64StrictFinite, functions::Max};
/// use std::collections::{HashMap, HashSet};
/// use try_create::TryNew;
///
/// // Use as HashMap keys (requires Eq + Hash)
/// let mut scores = HashMap::new();
/// let player1 = RealNative64StrictFinite::try_new(1.5).unwrap();
/// scores.insert(player1, 100);
///
/// // Use in HashSet (requires Eq + Hash)
/// let mut unique_values = HashSet::new();
/// unique_values.insert(RealNative64StrictFinite::try_new(3.14).unwrap());
///
/// // Comparison still works efficiently with PartialOrd
/// let a = RealNative64StrictFinite::try_new(1.0).unwrap();
/// let b = RealNative64StrictFinite::try_new(2.0).unwrap();
/// assert!(a < b);  // Uses PartialOrd
/// assert_eq!(a.max(&b), &b);  // Uses library's Max trait
/// ```
///
/// ## Hash Collision Considerations
///
/// The hashing implementation ensures:
/// - Consistent hashes for mathematically equal values
/// - Proper handling of floating-point edge cases
/// - Compatibility with both native and arbitrary-precision backends
/// - No hash collisions due to NaN values (which are excluded by design)
///
/// ## Currently Implemented By
///
/// - [`StrictFinitePolicy<T, P>`](StrictFinitePolicy): The primary policy that
///   rejects NaN, infinity, and subnormal values.
/// - [`DebugValidationPolicy<StrictFinitePolicy<T, P>>`](DebugValidationPolicy):
///   A wrapper that applies strict validation only in debug builds.
///
/// ## Safety and Correctness
///
/// This trait should only be implemented by validation policies that genuinely
/// guarantee finite values. Incorrect implementation could lead to hash collisions
/// or violated equality contracts in the methods that rely on this guarantee.
///
/// The trait is designed as a marker trait (no methods) to emphasize that it's
/// purely a compile-time contract rather than runtime functionality.
pub trait GuaranteesFiniteValues: ValidationPolicyReal {}

/// Marker trait indicating that a validation policy guarantees finite complex values.
///
/// This trait serves as a compile-time indicator that a validation policy for
/// complex numbers ensures all validated values have finite real and imaginary components
/// (i.e., neither component is NaN or infinity). This guarantee enables the
/// implementation of [`Eq`] and [`Hash`] for [`ComplexValidated`] types, allowing
/// them to be used as keys in hash-based collections like [`HashMap`](std::collections::HashMap)
/// and [`HashSet`](std::collections::HashSet).
///
/// ## Purpose
///
/// Complex numbers can have NaN or infinite components, making equality comparison
/// problematic (NaN != NaN). By guaranteeing finiteness at the policy level,
/// this trait allows:
/// - Full equality ([`Eq`]) implementation for complex validated types
/// - Consistent hashing ([`Hash`]) for use in hash-based collections
/// - Compile-time verification of these properties
///
/// ## Design Rationale
///
/// This trait works in tandem with [`GuaranteesFiniteValues`] for real numbers:
/// - [`GuaranteesFiniteValues`]: For real number policies
/// - [`GuaranteesFiniteComplexValues`]: For complex number policies
///
/// This separation allows different validation strategies for real and complex
/// components while maintaining type safety.
///
/// ## Currently Implemented By
///
/// - [`StrictFinitePolicy<T, P>`](StrictFinitePolicy): Validates both real and imaginary
///   components for finiteness and non-subnormal values.
/// - [`DebugValidationPolicy<StrictFinitePolicy<T, P>>`](DebugValidationPolicy):
///   Applies strict validation only in debug builds.
///
/// ## Safety and Correctness
///
/// This trait should only be implemented by validation policies that genuinely
/// guarantee finite values in both components. Incorrect implementation could lead
/// to hash collisions or violated equality contracts.
pub trait GuaranteesFiniteComplexValues: ValidationPolicyComplex {}

// Implement the marker for `StrictFinitePolicy` for any real scalar type.
impl<RawReal: RawRealTrait, const PRECISION: u32> GuaranteesFiniteValues
    for StrictFinitePolicy<RawReal, PRECISION>
where
    // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteValues
    StrictFinitePolicy<RawReal, PRECISION>: ValidationPolicyReal,
{
}

// Implement the marker for `DebugValidationPolicy<StrictFinitePolicy>` for any real scalar type.
impl<RawReal: RawRealTrait, const PRECISION: u32> GuaranteesFiniteValues
    for DebugValidationPolicy<StrictFinitePolicy<RawReal, PRECISION>>
where
    // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteValues
    StrictFinitePolicy<RawReal, PRECISION>: ValidationPolicyReal,
{
}

// Implement the marker for `StrictFinitePolicy` for any complex scalar type.
impl<RawComplex: RawComplexTrait, const PRECISION: u32> GuaranteesFiniteComplexValues
    for StrictFinitePolicy<RawComplex, PRECISION>
where
    // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteComplexValues
    StrictFinitePolicy<RawComplex, PRECISION>: ValidationPolicyComplex,
{
}

// Implement the marker for `DebugValidationPolicy<StrictFinitePolicy>` for any complex scalar type.
impl<RawComplex: RawComplexTrait, const PRECISION: u32> GuaranteesFiniteComplexValues
    for DebugValidationPolicy<StrictFinitePolicy<RawComplex, PRECISION>>
where
    // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteComplexValues
    StrictFinitePolicy<RawComplex, PRECISION>: ValidationPolicyComplex,
{
}
//-------------------------------------------------------------

//-------------------------------------------------------------
/// Implements total equality (`Eq`) for `RealValidated` types.
///
/// This implementation is only available when the kernel's `RealPolicy`
/// implements the `GuaranteesFiniteValues` marker trait. This ensures that
/// the underlying value is never `NaN`, making the equality relation reflexive,
/// symmetric, and transitive, as required by `Eq`.
impl<K: NumKernel> Eq for RealValidated<K>
where
    K::RealPolicy: GuaranteesFiniteValues,
{
    // `Eq` is a marker trait and requires no methods. Its presence simply
    // asserts that `PartialEq`'s `eq` method forms an equivalence relation.
}

/// Implements total equality (`Eq`) for `ComplexValidated` types.
///
/// This implementation is only available when the kernel's `ComplexPolicy`
/// implements the `GuaranteesFiniteComplexValues` marker trait. This ensures that
/// the underlying complex number components are never `NaN`, making the equality
/// relation reflexive, symmetric, and transitive, as required by `Eq`.
impl<K: NumKernel> Eq for ComplexValidated<K>
where
    K::ComplexPolicy: GuaranteesFiniteComplexValues,
{
    // `Eq` is a marker trait and requires no methods. Its presence simply
    // asserts that `PartialEq`'s `eq` method forms an equivalence relation.
}
//-------------------------------------------------------------

//-------------------------------------------------------------
/// Implements [`Hash`] for validated real number types with finite value guarantees.
///
/// This implementation is only available when the kernel's `RealPolicy`
/// implements the [`GuaranteesFiniteValues`] marker trait. This ensures that
/// the underlying value is never `NaN`, making hash values consistent and
/// allowing these types to be used as keys in hash-based collections.
///
/// ## Hashing Strategy
///
/// The implementation delegates to the [`RawScalarTrait::compute_hash()`] method provided by the
/// underlying raw scalar type (via [`RawScalarTrait`]). This method:
///
/// - For `f64` types: Uses IEEE 754 bit representation via `f64::to_bits()`
/// - For `rug::Float` types: Uses integer representation via `rug::Float::to_integer()`
/// - Handles signed zeros correctly (both `+0.0` and `-0.0` produce the same hash)
/// - Maintains the hash contract: `a == b` implies `hash(a) == hash(b)`
///
/// ## Safety and Correctness
///
/// This implementation is safe because:
/// - [`GuaranteesFiniteValues`] ensures no `NaN` values can exist
/// - The [`Eq`] implementation is well-defined for finite values
/// - Signed zeros are handled consistently in both equality and hashing
///
/// ## Usage Example
///
/// ```rust
/// use num_valid::RealNative64StrictFinite;
/// use std::collections::HashMap;
/// use try_create::TryNew;
///
/// let mut map = HashMap::new();
/// let key = RealNative64StrictFinite::try_new(3.14).unwrap();
/// map.insert(key, "pi approximation");
/// assert_eq!(map.len(), 1);
/// ```
impl<K: NumKernel> Hash for RealValidated<K>
where
    K::RealPolicy: GuaranteesFiniteValues,
{
    #[inline(always)]
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.value.compute_hash(state);
    }
}

/// Implements [`Hash`] for validated complex number types with finite value guarantees.
///
/// This implementation is only available when the kernel's `ComplexPolicy`
/// implements the [`GuaranteesFiniteComplexValues`] marker trait. This ensures that
/// both the real and imaginary parts are never `NaN`, making hash values
/// consistent and allowing these types to be used as keys in hash-based collections.
///
/// ## Hashing Strategy
///
/// The implementation delegates to the [`RawScalarTrait::compute_hash()`] method provided by the
/// underlying raw complex type (via [`RawComplexTrait`]). This method:
///
/// - For `Complex<f64>` types: Hashes real and imaginary parts sequentially using `f64::to_bits()`
/// - For `rug::Complex` types: Hashes real and imaginary components using their `compute_hash()` methods
/// - Handles signed zeros correctly in both real and imaginary parts
/// - Maintains the hash contract: `a == b` implies `hash(a) == hash(b)`
///
/// ## Safety and Correctness
///
/// This implementation is safe because:
/// - [`GuaranteesFiniteComplexValues`] ensures no `NaN` values can exist in either component
/// - The [`Eq`] implementation is well-defined for finite complex values
/// - Signed zeros are handled consistently in both equality and hashing
/// - Complex numbers with different component values produce different hashes
///
/// ## Usage Example
///
/// ```rust
/// use num_valid::ComplexNative64StrictFinite;
/// use num::Complex;
/// use std::collections::HashMap;
/// use try_create::TryNew;
///
/// let mut map = HashMap::new();
/// let key = ComplexNative64StrictFinite::try_new(Complex::new(1.0, 2.0)).unwrap();
/// map.insert(key, "complex value");
/// assert_eq!(map.len(), 1);
/// ```
impl<K: NumKernel> Hash for ComplexValidated<K>
where
    K::ComplexPolicy: GuaranteesFiniteComplexValues,
{
    #[inline(always)]
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.value.compute_hash(state);
    }
}
//-------------------------------------------------------------

//-------------------------------------------------------------
#[cfg(test)]
mod tests {
    use super::*;

    #[cfg(feature = "rug")]
    use crate::{ComplexRugStrictFinite, RealRugStrictFinite};

    mod fp_checks {
        use super::*;
        use num::Complex;

        mod native64 {
            use super::*;

            mod real {
                use super::*;

                #[test]
                fn test_is_finite() {
                    assert!(<f64 as FpChecks>::is_finite(&1.0));
                    assert!(!<f64 as FpChecks>::is_finite(&f64::INFINITY));
                    assert!(!<f64 as FpChecks>::is_finite(&f64::NEG_INFINITY));
                    assert!(!<f64 as FpChecks>::is_finite(&f64::NAN));
                }

                #[test]
                fn test_is_infinite() {
                    assert!(!<f64 as FpChecks>::is_infinite(&1.0));
                    assert!(<f64 as FpChecks>::is_infinite(&f64::INFINITY));
                    assert!(<f64 as FpChecks>::is_infinite(&f64::NEG_INFINITY));
                    assert!(!<f64 as FpChecks>::is_infinite(&f64::NAN));
                }

                #[test]
                fn test_is_nan() {
                    assert!(!<f64 as FpChecks>::is_nan(&1.0));
                    assert!(!<f64 as FpChecks>::is_nan(&f64::INFINITY));
                    assert!(!<f64 as FpChecks>::is_nan(&f64::NEG_INFINITY));
                    assert!(<f64 as FpChecks>::is_nan(&f64::NAN));
                }

                #[test]
                fn test_is_normal() {
                    assert!(<f64 as FpChecks>::is_normal(&1.0));
                    assert!(!<f64 as FpChecks>::is_normal(&0.0));
                    assert!(!<f64 as FpChecks>::is_normal(&f64::INFINITY));
                    assert!(!<f64 as FpChecks>::is_normal(&f64::NEG_INFINITY));
                    assert!(!<f64 as FpChecks>::is_normal(&f64::NAN));
                    assert!(<f64 as FpChecks>::is_normal(&f64::MIN_POSITIVE));
                }
            }

            mod complex {
                use super::*;

                #[test]
                fn test_is_finite() {
                    assert!(<Complex<f64> as FpChecks>::is_finite(&Complex::new(
                        1.0, 1.0
                    )));
                    assert!(!<Complex<f64> as FpChecks>::is_finite(&Complex::new(
                        f64::INFINITY,
                        1.0
                    )));
                    assert!(!<Complex<f64> as FpChecks>::is_finite(&Complex::new(
                        1.0,
                        f64::NAN
                    )));
                }

                #[test]
                fn test_is_infinite() {
                    assert!(!<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
                        1.0, 1.0
                    )));
                    assert!(<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
                        f64::INFINITY,
                        1.0
                    )));
                    assert!(!<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
                        1.0,
                        f64::NAN
                    )));
                }

                #[test]
                fn test_is_nan() {
                    assert!(!<Complex<f64> as FpChecks>::is_nan(&Complex::new(1.0, 1.0)));
                    assert!(<Complex<f64> as FpChecks>::is_nan(&Complex::new(
                        f64::NAN,
                        1.0
                    )));
                    assert!(<Complex<f64> as FpChecks>::is_nan(&Complex::new(
                        1.0,
                        f64::NAN
                    )));
                }

                #[test]
                fn test_is_normal() {
                    assert!(<Complex<f64> as FpChecks>::is_normal(&Complex::new(
                        1.0, 1.0
                    )));
                    assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
                        0.0, 0.0
                    )));
                    assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
                        f64::INFINITY,
                        1.0
                    )));
                    assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
                        1.0,
                        f64::NAN
                    )));
                }
            }
        }
    }

    mod strict_finite_policy {
        use super::*;

        mod raw_types {
            use super::*;

            mod native64 {
                use super::*;

                mod real {
                    use super::*;

                    #[test]
                    fn f64_infinity() {
                        let result = StrictFinitePolicy::<f64, 53>::validate(f64::INFINITY);
                        assert!(matches!(
                            result,
                            Err(ErrorsValidationRawReal::IsPosInfinity { .. })
                        ));

                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&f64::INFINITY);
                        assert!(matches!(
                            result,
                            Err(ErrorsValidationRawReal::IsPosInfinity { .. })
                        ));

                        let result = StrictFinitePolicy::<f64, 53>::validate(f64::NEG_INFINITY);
                        assert!(matches!(
                            result,
                            Err(ErrorsValidationRawReal::IsNegInfinity { .. })
                        ));

                        let result =
                            StrictFinitePolicy::<f64, 53>::validate_ref(&f64::NEG_INFINITY);
                        assert!(matches!(
                            result,
                            Err(ErrorsValidationRawReal::IsNegInfinity { .. })
                        ));
                    }

                    #[test]
                    fn f64_nan() {
                        let result = StrictFinitePolicy::<f64, 53>::validate(f64::NAN);
                        assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));

                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&f64::NAN);
                        assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
                    }

                    #[test]
                    fn f64_subnormal() {
                        // let subnormal = f64::MIN_POSITIVE / 2.0;
                        let subnormal = f64::from_bits(1); // the smallest positive subnormal

                        let result = StrictFinitePolicy::<f64, 53>::validate(subnormal);
                        assert!(matches!(
                            result,
                            Err(ErrorsValidationRawReal::<f64>::IsSubnormal { .. })
                        ));

                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&subnormal);
                        assert!(matches!(
                            result,
                            Err(ErrorsValidationRawReal::<f64>::IsSubnormal { .. })
                        ));
                    }

                    #[test]
                    fn f64_zero() {
                        let value = 0.0;
                        let result = StrictFinitePolicy::<f64, 53>::validate(value);
                        assert!(matches!(result, Ok(0.0)));
                    }

                    #[test]
                    fn f64_normal() {
                        let value = 1.0;
                        let result = StrictFinitePolicy::<f64, 53>::validate(value);
                        assert!(matches!(result, Ok(1.0)));

                        let result = StrictFinitePolicy::<f64, 53>::validate_ref(&value);
                        assert!(matches!(result, Ok(())));
                    }
                }

                mod complex {
                    use super::*;
                    use num::Complex;

                    #[test]
                    fn test_invalid_both_parts() {
                        let z = Complex::new(f64::NAN, f64::NEG_INFINITY);
                        let err =
                            StrictFinitePolicy::<Complex<f64>, 53>::validate_ref(&z).unwrap_err();
                        matches!(err, ErrorsValidationRawComplex::InvalidBothParts { .. });
                    }

                    #[test]
                    fn complex_f64_invalid_real_part() {
                        let value = Complex::new(f64::INFINITY, 1.0);
                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
                        assert!(matches!(
                            result,
                            Err(ErrorsValidationRawComplex::InvalidRealPart {
                                source: ErrorsValidationRawReal::IsPosInfinity { .. },
                                ..
                            })
                        ));

                        let value = Complex::new(f64::NAN, 1.0);
                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
                        assert!(matches!(
                            result,
                            Err(ErrorsValidationRawComplex::InvalidRealPart {
                                source: ErrorsValidationRawReal::IsNaN { .. },
                                ..
                            })
                        ));

                        let value = Complex::new(f64::MIN_POSITIVE / 2.0, 1.0);
                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
                        assert!(matches!(
                            result,
                            Err(ErrorsValidationRawComplex::InvalidRealPart {
                                source: ErrorsValidationRawReal::IsSubnormal { .. },
                                ..
                            })
                        ));
                    }

                    #[test]
                    fn complex_f64_invalid_imaginary_part() {
                        let value = Complex::new(1.0, f64::INFINITY);
                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
                        assert!(matches!(
                            result,
                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
                                source: ErrorsValidationRawReal::IsPosInfinity { .. },
                                ..
                            })
                        ));

                        let value = Complex::new(1.0, f64::NAN);
                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
                        assert!(matches!(
                            result,
                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
                                source: ErrorsValidationRawReal::IsNaN { .. },
                                ..
                            })
                        ));

                        let value = Complex::new(1.0, f64::MIN_POSITIVE / 2.0);
                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
                        assert!(matches!(
                            result,
                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
                                source: ErrorsValidationRawReal::IsSubnormal { .. },
                                ..
                            })
                        ));
                    }

                    #[test]
                    fn complex_f64_zero() {
                        let value = Complex::new(0.0, 0.0);
                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
                        assert!(result.is_ok());
                    }

                    #[test]
                    fn complex_f64_normal_value() {
                        let value = Complex::new(1.0, 1.0);
                        let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
                        assert!(result.is_ok());
                    }
                }
            }

            #[cfg(feature = "rug")]
            mod rug53 {
                use super::*;

                const PRECISION: u32 = 53; // Default precision for rug tests

                mod float {
                    use super::*;
                    use rug::Float;

                    #[test]
                    fn rug_float_valid() {
                        let x = Float::with_val(PRECISION, 1.0);
                        assert!(StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).is_ok());
                        assert_eq!(
                            StrictFinitePolicy::<Float, PRECISION>::validate(x.clone()).unwrap(),
                            x
                        );
                    }

                    #[test]
                    fn rug_float_nan() {
                        let x = Float::with_val(PRECISION, Float::parse("nan").unwrap());
                        let err =
                            StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
                        assert!(matches!(err, ErrorsValidationRawReal::IsNaN { .. }));
                        let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
                        assert!(matches!(err, ErrorsValidationRawReal::IsNaN { .. }));
                    }

                    #[test]
                    fn rug_float_infinity() {
                        use rug::Float;

                        let x = Float::with_val(PRECISION, Float::parse("inf").unwrap());
                        let err =
                            StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
                        assert!(matches!(err, ErrorsValidationRawReal::IsPosInfinity { .. }));
                        let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
                        assert!(matches!(err, ErrorsValidationRawReal::IsPosInfinity { .. }));

                        let x = Float::with_val(PRECISION, Float::parse("-inf").unwrap());
                        let err =
                            StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
                        assert!(matches!(err, ErrorsValidationRawReal::IsNegInfinity { .. }));
                        let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
                        assert!(matches!(err, ErrorsValidationRawReal::IsNegInfinity { .. }));
                    }

                    #[test]
                    fn rug_float_zero() {
                        let value = Float::with_val(PRECISION, 0.0);
                        let result = StrictFinitePolicy::<Float, PRECISION>::validate_ref(&value);
                        assert!(result.is_ok());
                        let result =
                            StrictFinitePolicy::<Float, PRECISION>::validate(value.clone())
                                .unwrap();
                        assert_eq!(result, value);
                    }
                }

                mod complex {
                    use super::*;
                    use rug::{Complex, Float};

                    #[test]
                    fn rug_complex_valid() {
                        let z = Complex::with_val(PRECISION, (1.0, -2.0));
                        assert!(StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).is_ok());
                        assert_eq!(
                            StrictFinitePolicy::<Complex, PRECISION>::validate(z.clone()).unwrap(),
                            z
                        );
                    }

                    #[test]
                    fn rug_complex_real_invalid() {
                        let nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
                        let z = Complex::with_val(PRECISION, (&nan, 1.0));
                        let err =
                            StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
                        assert!(matches!(
                            err,
                            ErrorsValidationRawComplex::InvalidRealPart {
                                source: ErrorsValidationRawReal::IsNaN { .. }
                            }
                        ));
                    }

                    #[test]
                    fn rug_complex_imag_invalid() {
                        let inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
                        let z = Complex::with_val(PRECISION, (1.0, &inf));
                        let err =
                            StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
                        assert!(matches!(
                            err,
                            ErrorsValidationRawComplex::InvalidImaginaryPart {
                                source: ErrorsValidationRawReal::IsPosInfinity { .. }
                            }
                        ));
                    }

                    #[test]
                    fn rug_complex_both_invalid() {
                        let nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
                        let inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
                        let z = Complex::with_val(PRECISION, (&nan, &inf));
                        let err =
                            StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
                        assert!(matches!(
                            err,
                            ErrorsValidationRawComplex::InvalidBothParts {
                                real_error: ErrorsValidationRawReal::IsNaN { .. },
                                imag_error: ErrorsValidationRawReal::IsPosInfinity { .. },
                                ..
                            }
                        ));
                    }

                    #[test]
                    fn rug_complex_zero() {
                        let zero = Complex::with_val(
                            PRECISION,
                            (
                                Float::with_val(PRECISION, 0.0),
                                Float::with_val(PRECISION, 0.0),
                            ),
                        );
                        let result = StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&zero);
                        assert!(result.is_ok());
                        assert_eq!(
                            StrictFinitePolicy::<Complex, PRECISION>::validate(zero.clone())
                                .unwrap(),
                            zero
                        );
                    }
                }
            }
        }

        // Module for testing the wrapper types that implement TryNew
        #[cfg(feature = "rug")]
        mod wrapper_types {
            use super::*;

            mod rug_wrappers {
                use super::*;
                use crate::RealScalar;
                use num::One;
                use rug::{Complex as RugComplex, Float};
                use try_create::{IntoInner, TryNew};

                const PRECISION: u32 = 53; // Default precision for rug tests

                mod real {
                    use super::*;

                    #[test]
                    fn try_new_real_rug_correct_precision() {
                        // Test RealRugStrictFinite with valid rug::Float and correct precision
                        let val = Float::with_val(PRECISION, 1.0);
                        let res = RealRugStrictFinite::<PRECISION>::try_new(val.clone());
                        assert!(res.is_ok(),);
                        assert_eq!(res.unwrap().into_inner(), val);
                    }

                    #[test]
                    fn try_new_real_rug_incorrect_precision() {
                        // Test RealRugStrictFinite with valid rug::Float but incorrect precision
                        let val_wrong_prec = Float::with_val(PRECISION + 10, 1.0); // Different precision
                        let res = RealRugStrictFinite::<PRECISION>::try_new(val_wrong_prec);
                        assert!(res.is_err());
                        matches!(res, Err(ErrorsValidationRawReal::PrecisionMismatch { actual_precision, .. })
                            if actual_precision == PRECISION + 10
                        );
                    }

                    #[test]
                    fn try_new_real_rug_nan() {
                        // Test RealRugStrictFinite with rug::Float NaN
                        let val_nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
                        let res = RealRugStrictFinite::<PRECISION>::try_new(val_nan);
                        assert!(res.is_err());
                        assert!(matches!(
                            res,
                            Err(ErrorsValidationRawReal::<Float>::IsNaN { .. })
                        ));
                    }

                    #[test]
                    fn try_new_real_rug_infinity() {
                        // Test RealRugStrictFinite with rug::Float positive infinity
                        let val_inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
                        let res_pos_inf = RealRugStrictFinite::<PRECISION>::try_new(val_inf);
                        assert!(res_pos_inf.is_err());
                        assert!(matches!(
                            res_pos_inf,
                            Err(ErrorsValidationRawReal::<Float>::IsPosInfinity { .. })
                        ));

                        // Test RealRugStrictFinite with rug::Float negative infinity
                        let val_neg_inf = Float::with_val(PRECISION, Float::parse("-inf").unwrap());
                        let res_neg_inf = RealRugStrictFinite::<PRECISION>::try_new(val_neg_inf);
                        assert!(res_neg_inf.is_err());
                        assert!(matches!(
                            res_neg_inf,
                            Err(ErrorsValidationRawReal::<Float>::IsNegInfinity { .. })
                        ));
                    }
                }

                mod complex {
                    use super::*;
                    use crate::ComplexScalarGetParts;

                    #[test]
                    fn try_new_complex_rug_correct_precision() {
                        // Test ComplexRugStrictFinite with valid rug::Complex and correct precision for both parts
                        let val = RugComplex::with_val(PRECISION, (1.0, -2.0));
                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val.clone());
                        assert!(res.is_ok());
                        assert_eq!(res.unwrap().into_inner(), val);
                    }

                    /*
                    #[test]
                    #[ignore = "because the precision used to construct a rug::Complex override the precision of the real and imaginary parts"]
                    fn try_new_complex_rug_precision_mismatch_real() {
                        // Test ComplexRugStrictFinite where real part has incorrect precision
                        let real_part_wrong_prec = Float::with_val(PRECISION - 1, 1.0);
                        let imag_part_correct_prec = Float::with_val(PRECISION, -2.0);
                        let val = RugComplex::with_val(
                            PRECISION,
                            (real_part_wrong_prec, imag_part_correct_prec),
                        ); // Construct with Float parts

                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
                        assert!(res.is_err());
                        assert!(matches!(
                            res,
                            Err(ErrorsValidationRawComplex::InvalidRealPart {
                                source: ErrorsValidationRawReal::PrecisionMismatch { .. }
                            })
                        ));
                    }

                    #[test]
                    #[ignore = "because the precision used to construct a rug::Complex override the precision of the real and imaginary parts"]
                    fn try_new_complex_rug_precision_mismatch_imag() {
                        // Test ComplexRugStrictFinite where imaginary part has incorrect precision
                        let real_part_correct_prec = Float::with_val(PRECISION, 1.0);
                        let imag_part_wrong_prec = Float::with_val(PRECISION - 1, -2.0);
                        let val = RugComplex::with_val(
                            PRECISION,
                            (real_part_correct_prec, imag_part_wrong_prec),
                        ); // Construct with Float parts

                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
                        assert!(res.is_err());
                        matches!(res, Err(ErrorsValidationRawComplex::InvalidImaginaryPart { source })
                            if matches!(source, ErrorsValidationRawReal::PrecisionMismatch { .. })
                        );
                    }
                    */

                    #[test]
                    fn try_new_complex_rug_precision_mismatch_both() {
                        // Test ComplexRugStrictFinite where both parts have incorrect precision
                        let real_part_wrong_prec = Float::with_val(PRECISION - 1, 1.0);
                        let imag_part_wrong_prec = Float::with_val(PRECISION - 1, -2.0);
                        let val = RugComplex::with_val(
                            PRECISION - 1,
                            (real_part_wrong_prec, imag_part_wrong_prec),
                        ); // Construct with Float parts

                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
                        assert!(res.is_err());
                        assert!(matches!(
                            res,
                            Err(ErrorsValidationRawComplex::InvalidBothParts {
                                real_error: ErrorsValidationRawReal::PrecisionMismatch { .. },
                                imag_error: ErrorsValidationRawReal::PrecisionMismatch { .. },
                                ..
                            })
                        ));
                    }

                    #[test]
                    fn try_new_complex_rug_invalid_real_nan() {
                        // Test ComplexRugStrictFinite where real part is NaN (value validation failure)
                        let nan_real = Float::with_val(PRECISION, Float::parse("nan").unwrap());
                        let imag_part = Float::with_val(PRECISION, 1.0);
                        let val = RugComplex::with_val(PRECISION, (nan_real, imag_part));

                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
                        assert!(res.is_err());
                        assert!(matches!(
                            res,
                            Err(ErrorsValidationRawComplex::InvalidRealPart {
                                source: ErrorsValidationRawReal::<Float>::IsNaN { .. },
                            })
                        ));
                    }

                    #[test]
                    fn try_new_complex_rug_invalid_imag_inf() {
                        // Test ComplexRugStrictFinite where imaginary part is Infinity (value validation failure)
                        let real_part = Float::with_val(PRECISION, 1.0);
                        let inf_imag = Float::with_val(PRECISION, Float::parse("inf").unwrap());
                        let val = RugComplex::with_val(PRECISION, (real_part, inf_imag));

                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
                        assert!(res.is_err());
                        assert!(matches!(
                            res,
                            Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
                                source: ErrorsValidationRawReal::<Float>::IsPosInfinity { .. }
                            })
                        ));
                    }

                    #[test]
                    fn try_new_complex_rug_invalid_both() {
                        // Test ComplexRugStrictFinite where both parts are invalid (value validation failure)
                        let nan_real = Float::with_val(PRECISION, Float::parse("nan").unwrap());
                        let neg_inf_imag =
                            Float::with_val(PRECISION, Float::parse("-inf").unwrap());
                        let val = RugComplex::with_val(PRECISION, (nan_real, neg_inf_imag));

                        let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
                        assert!(res.is_err());
                        assert!(matches!(
                            res,
                            Err(ErrorsValidationRawComplex::InvalidBothParts {
                                real_error: ErrorsValidationRawReal::<Float>::IsNaN { .. },
                                imag_error: ErrorsValidationRawReal::<Float>::IsNegInfinity { .. },
                                ..
                            })
                        ));
                    }

                    #[test]
                    fn try_from_complex_f64_to_complex_rug() {
                        // Test valid conversion
                        let z = num::Complex::new(1., -2.);
                        let z_rug = ComplexRugStrictFinite::<PRECISION>::try_from(z).unwrap();
                        let expected_real = RealRugStrictFinite::<PRECISION>::one();
                        let expected_imaginary =
                            RealRugStrictFinite::<PRECISION>::try_from_f64(-2.).unwrap();
                        assert_eq!(z_rug.real_part(), expected_real);
                        assert_eq!(z_rug.imag_part(), expected_imaginary);

                        // Test invalid real part
                        let z = num::Complex::new(f64::INFINITY, -2.0);
                        let result = ComplexRugStrictFinite::<PRECISION>::try_from(z);
                        assert!(result.is_err());
                        assert!(matches!(
                            result,
                            Err(ErrorsValidationRawComplex::InvalidRealPart {
                                source: ErrorsTryFromf64::Output {
                                    source: ErrorsValidationRawReal::IsPosInfinity { .. }
                                },
                            })
                        ));

                        // Test invalid imaginary part
                        let z = num::Complex::new(1.0, f64::NAN);
                        let result = ComplexRugStrictFinite::<PRECISION>::try_from(z).unwrap_err();
                        //println!("result = {result}");
                        assert!(matches!(
                            result,
                            ErrorsValidationRawComplex::InvalidImaginaryPart {
                                source: ErrorsTryFromf64::Output {
                                    source: ErrorsValidationRawReal::IsNaN { .. }
                                },
                            }
                        ));
                    }
                }
            }
        }
    }

    mod conditional_validation_policy {
        use core::f64;

        use super::*;

        type F64StrictFinitePolicy = Native64RawRealStrictFinitePolicy;

        // Define a policy that is strict in debug, and no-op in release.
        type MyConditionalPolicy = DebugValidationPolicy<F64StrictFinitePolicy>;

        #[test]
        #[should_panic(expected = "Debug validation of input value failed: Value is NaN: NaN")]
        #[cfg(debug_assertions)]
        fn debug_validate_function() {
            // This should always succeed, regardless of build mode.
            let nan_value = f64::NAN;
            debug_validate::<F64StrictFinitePolicy>(&nan_value, "input");
        }

        #[test]
        fn test_conditional_validation_on_valid_value() {
            let valid_value = 42.0;
            // This should always succeed, regardless of build mode.
            assert!(MyConditionalPolicy::validate(valid_value).is_ok());
        }

        #[test]
        fn test_conditional_validation_on_invalid_value() {
            let invalid_value = f64::NAN;
            let result = MyConditionalPolicy::validate(invalid_value);

            if cfg!(debug_assertions) {
                // In debug mode, we expect an error from StrictFinitePolicy.
                assert!(result.is_err(), "Expected validation to fail in debug mode");
                assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
            } else {
                // In release mode, we expect it to succeed (no-op).
                assert!(
                    result.is_ok(),
                    "Expected validation to succeed in release mode"
                );
            }
        }
    }

    mod conditional_backtrace_tests {
        use super::*;
        use std::backtrace::BacktraceStatus;

        type F64StrictFinitePolicy = Native64RawRealStrictFinitePolicy;

        /// Tests that `capture_backtrace()` returns the correct backtrace status
        /// based on the `backtrace` feature flag.
        #[test]
        fn test_capture_backtrace_status() {
            let bt = capture_backtrace();

            if cfg!(feature = "backtrace") {
                // With backtrace feature enabled, should capture a real backtrace
                assert_ne!(
                    bt.status(),
                    BacktraceStatus::Disabled,
                    "Backtrace should not be disabled when 'backtrace' feature is enabled"
                );
            } else {
                // Without backtrace feature, should return a disabled backtrace
                assert_eq!(
                    bt.status(),
                    BacktraceStatus::Disabled,
                    "Backtrace should be disabled when 'backtrace' feature is not enabled"
                );
            }
        }

        /// Tests that `is_backtrace_enabled()` correctly reports the feature state.
        #[test]
        fn test_is_backtrace_enabled() {
            let enabled = is_backtrace_enabled();

            if cfg!(feature = "backtrace") {
                assert!(
                    enabled,
                    "is_backtrace_enabled() should return true when feature is enabled"
                );
            } else {
                assert!(
                    !enabled,
                    "is_backtrace_enabled() should return false when feature is disabled"
                );
            }
        }

        /// Tests that error types contain appropriate backtraces based on feature.
        #[test]
        fn test_nan_error_backtrace_status() {
            // Create an error by validating an invalid value
            let result = F64StrictFinitePolicy::validate(f64::NAN);

            assert!(result.is_err());

            if let Err(ErrorsValidationRawReal::IsNaN { backtrace, .. }) = result {
                if cfg!(feature = "backtrace") {
                    assert_ne!(
                        backtrace.status(),
                        BacktraceStatus::Disabled,
                        "Error backtrace should be captured when feature is enabled"
                    );
                } else {
                    assert_eq!(
                        backtrace.status(),
                        BacktraceStatus::Disabled,
                        "Error backtrace should be disabled when feature is not enabled"
                    );
                }
            } else {
                panic!("Expected IsNaN error");
            }
        }

        /// Tests that subnormal error backtraces are correct.
        #[test]
        fn test_subnormal_error_backtrace() {
            let subnormal = f64::MIN_POSITIVE / 2.0;
            let result = F64StrictFinitePolicy::validate(subnormal);

            assert!(result.is_err());

            if let Err(ErrorsValidationRawReal::IsSubnormal { backtrace, .. }) = result {
                if cfg!(feature = "backtrace") {
                    assert_ne!(
                        backtrace.status(),
                        BacktraceStatus::Disabled,
                        "Subnormal error backtrace should be captured when feature is enabled"
                    );
                } else {
                    assert_eq!(
                        backtrace.status(),
                        BacktraceStatus::Disabled,
                        "Subnormal error backtrace should be disabled when feature is not enabled"
                    );
                }
            } else {
                panic!("Expected IsSubnormal error");
            }
        }

        /// Tests that infinity error backtraces are correct.
        #[test]
        fn test_infinity_error_backtrace() {
            let result = F64StrictFinitePolicy::validate(f64::INFINITY);

            assert!(result.is_err());

            if let Err(ErrorsValidationRawReal::IsPosInfinity { backtrace, .. }) = result {
                if cfg!(feature = "backtrace") {
                    assert_ne!(
                        backtrace.status(),
                        BacktraceStatus::Disabled,
                        "Infinity error backtrace should be captured when feature is enabled"
                    );
                } else {
                    assert_eq!(
                        backtrace.status(),
                        BacktraceStatus::Disabled,
                        "Infinity error backtrace should be disabled when feature is not enabled"
                    );
                }
            } else {
                panic!("Expected IsPosInfinity error");
            }
        }

        /// Tests that negative infinity error backtraces are correct.
        #[test]
        fn test_neg_infinity_error_backtrace() {
            let result = F64StrictFinitePolicy::validate(f64::NEG_INFINITY);

            assert!(result.is_err());

            if let Err(ErrorsValidationRawReal::IsNegInfinity { backtrace, .. }) = result {
                if cfg!(feature = "backtrace") {
                    assert_ne!(
                        backtrace.status(),
                        BacktraceStatus::Disabled,
                        "NegInfinity error backtrace should be captured when feature is enabled"
                    );
                } else {
                    assert_eq!(
                        backtrace.status(),
                        BacktraceStatus::Disabled,
                        "NegInfinity error backtrace should be disabled when feature is not enabled"
                    );
                }
            } else {
                panic!("Expected IsNegInfinity error");
            }
        }
    }
}
