#![deny(rustdoc::broken_intra_doc_links)]

use crate::{
    RealScalar,
    functions::{Conjugate, NegAssign, Rounding, Sign},
    kernels::{
        ComplexValidated, NumKernelStrictFinite, RawComplexTrait, RawRealTrait, RawScalarTrait,
        RealValidated,
    },
    validation::{
        ErrorsRawRealToInteger, ErrorsTryFromf64, ErrorsValidationRawComplex,
        ErrorsValidationRawReal, FpChecks, ValidationPolicyReal, capture_backtrace,
    },
};
use derive_more::with_trait::Neg;
use duplicate::duplicate_item;
use num::Complex;
use rug::float::Constant as MpfrConstant;
use rug::ops::{CompleteRound, Pow as RugPow};
use std::{
    cmp::Ordering,
    hash::{Hash, Hasher},
    marker::PhantomData,
    num::FpCategory,
};
use try_create::IntoInner;

impl RawScalarTrait for rug::Float {
    type ValidationErrors = ErrorsValidationRawReal<rug::Float>;

    fn raw_zero(precision: u32) -> Self {
        rug::Float::with_val(precision, 0.)
    }

    fn is_zero(&self) -> bool {
        rug::Float::is_zero(self)
    }

    fn raw_one(precision: u32) -> Self {
        rug::Float::with_val(precision, 1.)
    }

    #[duplicate_item(
        unchecked_method       method;
        [unchecked_reciprocal] [recip];
        [unchecked_exp]        [exp];
        [unchecked_sqrt]       [sqrt];
        [unchecked_sin]        [sin];
        [unchecked_asin]       [asin];
        [unchecked_cos]        [cos];
        [unchecked_acos]       [acos];
        [unchecked_tan]        [tan];
        [unchecked_atan]       [atan];
        [unchecked_sinh]       [sinh];
        [unchecked_asinh]      [asinh];
        [unchecked_cosh]       [cosh];
        [unchecked_acosh]      [acosh];
        [unchecked_tanh]       [tanh];
        [unchecked_atanh]      [atanh];
        [unchecked_ln]         [ln];
        [unchecked_log2]       [log2];
        [unchecked_log10]      [log10];
    )]
    #[inline(always)]
    fn unchecked_method(self) -> Self {
        rug::Float::method(self)
    }

    #[duplicate_item(
        unchecked_method               exponent_type;
        [unchecked_pow_exponent_i8]    [i8];
        [unchecked_pow_exponent_i16]   [i16];
        [unchecked_pow_exponent_i32]   [i32];
        [unchecked_pow_exponent_i64]   [i64];
        [unchecked_pow_exponent_i128]  [i128];
        [unchecked_pow_exponent_isize] [isize];
        [unchecked_pow_exponent_u8]    [u8];
        [unchecked_pow_exponent_u16]   [u16];
        [unchecked_pow_exponent_u32]   [u32];
        [unchecked_pow_exponent_u64]   [u64];
        [unchecked_pow_exponent_u128]  [u128];
        [unchecked_pow_exponent_usize] [usize];
    )]
    #[inline(always)]
    fn unchecked_method(self, exponent: &exponent_type) -> Self {
        rug::Float::pow(self, exponent)
    }

    /// Multiplies and adds in one fused operation, rounding to the nearest with only one rounding error.
    ///
    /// `a.mul_add(b, c)` produces a result like `a * &b + &c`.
    #[inline(always)]
    fn unchecked_mul_add(self, b: &Self, c: &Self) -> Self {
        rug::Float::mul_add(self, b, c)
    }

    #[inline(always)]
    fn compute_hash<H: Hasher>(&self, state: &mut H) {
        debug_assert!(
            self.is_finite(),
            "Hashing a non-finite rug::Float value (i.e., NaN or Infinity) may lead to inconsistent results."
        );
        // Always include precision in the hash
        self.prec().hash(state);

        // Handle signed zeros by normalizing to positive zero
        if self.is_zero() {
            // Ensure that -0.0 and 0.0 hash to the same value
            rug::Float::with_val(self.prec(), 0.0)
                .to_string_radix(10, None)
                .hash(state);
        } else {
            // For non-zero values, create a deterministic string representation
            // Use enough decimal places to capture the full precision
            self.to_string_radix(10, None).hash(state)
        }
    }
}

impl RawRealTrait for rug::Float {
    type RawComplex = rug::Complex;

    #[inline(always)]
    fn unchecked_abs(self) -> rug::Float {
        rug::Float::abs(self)
    }

    #[inline(always)]
    fn unchecked_atan2(self, denominator: &Self) -> Self {
        rug::Float::atan2(self, denominator)
    }

    #[inline(always)]
    fn unchecked_pow_exponent_real(self, exponent: &Self) -> Self {
        rug::Float::pow(self, exponent)
    }

    #[inline(always)]
    fn unchecked_hypot(self, other: &Self) -> Self {
        rug::Float::hypot(self, other)
    }

    #[inline(always)]
    fn unchecked_ln_1p(self) -> Self {
        rug::Float::ln_1p(self)
    }

    #[inline(always)]
    fn unchecked_exp_m1(self) -> Self {
        rug::Float::exp_m1(self)
    }

    /// Multiplies two pairs and adds them in one fused operation, rounding to the nearest with only one rounding error.
    /// `a.unchecked_mul_add_mul_mut(&b, &c, &d)` produces a result like `&a * &b + &c * &d`, but stores the result in `a` using its precision.
    #[inline(always)]
    fn unchecked_mul_add_mul_mut(&mut self, mul: &Self, add_mul1: &Self, add_mul2: &Self) {
        self.mul_add_mul_mut(mul, add_mul1, add_mul2);
    }

    /// Multiplies two pairs and subtracts them in one fused operation, rounding to the nearest with only one rounding error.
    /// `a.unchecked_mul_sub_mul_mut(&b, &c, &d)` produces a result like `&a * &b - &c * &d`, but stores the result in `a` using its precision.
    #[inline(always)]
    fn unchecked_mul_sub_mul_mut(&mut self, mul: &Self, sub_mul1: &Self, sub_mul2: &Self) {
        self.mul_sub_mul_mut(mul, sub_mul1, sub_mul2);
    }

    #[inline(always)]
    fn raw_total_cmp(&self, other: &Self) -> Ordering {
        rug::Float::total_cmp(self, other)
    }

    /// Clamps the value within the specified bounds.
    #[inline(always)]
    fn raw_clamp(self, min: &Self, max: &Self) -> Self {
        rug::Float::clamp(self, min, max)
    }

    #[inline(always)]
    fn raw_classify(&self) -> FpCategory {
        rug::Float::classify(self)
    }

    #[inline(always)]
    fn raw_two(precision: u32) -> Self {
        rug::Float::with_val(precision, 2.)
    }

    #[inline(always)]
    fn raw_one_div_2(precision: u32) -> Self {
        rug::Float::with_val(precision, 0.5)
    }

    #[inline(always)]
    fn raw_pi(precision: u32) -> Self {
        // Use MPFR's optimized precomputed π constant (10x faster than acos(-1))
        rug::Float::with_val(precision, MpfrConstant::Pi)
    }

    #[inline(always)]
    fn raw_two_pi(precision: u32) -> Self {
        rug::Float::with_val(precision, MpfrConstant::Pi) * 2
    }

    #[inline(always)]
    fn raw_pi_div_2(precision: u32) -> Self {
        rug::Float::with_val(precision, MpfrConstant::Pi) / 2
    }

    #[inline(always)]
    fn raw_max_finite(precision: u32) -> Self {
        // Create a Float with the desired precision
        // let f = rug::Float::new(PRECISION);

        // Fill the significand with all 1s: 1 - 2^(-precision)
        // This gives you the largest significand before it rolls over
        let one = rug::Float::with_val(precision, 1);
        let eps = rug::Float::with_val(precision, rug::Float::u_pow_u(2, precision)).recip();
        let significand = one - &eps;

        // Scale it to the maximum exponent (subtract 1 to avoid overflow to infinity)
        let max_exp = rug::float::exp_max() - 1;
        //        println!("max_exp = {max_exp}");
        significand * rug::Float::with_val(precision, rug::Float::u_pow_u(2, max_exp as u32))
    }

    #[inline(always)]
    fn raw_min_finite(precision: u32) -> Self {
        Self::raw_max_finite(precision).neg()
    }

    #[inline(always)]
    fn raw_epsilon(precision: u32) -> Self {
        rug::Float::u_pow_u(2, precision - 1)
            .complete(precision)
            .recip()
    }

    #[inline(always)]
    fn raw_ln_2(precision: u32) -> Self {
        // Use MPFR's optimized precomputed ln(2) constant
        rug::Float::with_val(precision, MpfrConstant::Log2)
    }

    #[inline(always)]
    fn raw_ln_10(precision: u32) -> Self {
        // ln(10) = ln(2) * log₂(10)
        // More efficient than computing ln(10) directly
        let ln2 = rug::Float::with_val(precision, MpfrConstant::Log2);
        let log2_10 = rug::Float::with_val(precision, 10).log2();
        ln2 * log2_10
    }

    #[inline(always)]
    fn raw_log10_2(precision: u32) -> Self {
        rug::Float::with_val(precision, 2.).log10()
    }

    #[inline(always)]
    fn raw_log2_10(precision: u32) -> Self {
        rug::Float::with_val(precision, 10.).log2()
    }

    #[inline(always)]
    fn raw_log2_e(precision: u32) -> Self {
        // log₂(e) = 1/ln(2)
        // More efficient than computing e then taking log₂
        rug::Float::with_val(precision, MpfrConstant::Log2).recip()
    }

    #[inline(always)]
    fn raw_log10_e(precision: u32) -> Self {
        // log₁₀(e) = 1/ln(10) = 1/(ln(2) * log₂(10))
        let ln2 = rug::Float::with_val(precision, MpfrConstant::Log2);
        let log2_10 = rug::Float::with_val(precision, 10).log2();
        (ln2 * log2_10).recip()
    }

    #[inline(always)]
    fn raw_e(precision: u32) -> Self {
        rug::Float::with_val(precision, 1.).exp()
    }

    #[inline(always)]
    fn try_new_raw_real_from_f64<RealPolicy: ValidationPolicyReal<Value = Self>>(
        value: f64,
    ) -> Result<Self, ErrorsTryFromf64<Self>> {
        let precision = RealPolicy::PRECISION;
        let value_rug = RealPolicy::validate(rug::Float::with_val(precision, value))
            .map_err(|e| ErrorsTryFromf64::Output { source: e })?;
        if value_rug == value {
            Ok(value_rug)
        } else {
            Err(ErrorsTryFromf64::NonRepresentableExactly {
                value_in: value,
                value_converted_from_f64: value_rug,
                precision,
                backtrace: capture_backtrace(),
            })
        }
    }

    #[inline(always)]
    fn precision(&self) -> u32 {
        rug::Float::prec(self)
    }

    #[inline(always)]
    fn truncate_to_usize(self) -> Result<usize, ErrorsRawRealToInteger<rug::Float, usize>> {
        if !self.is_finite() {
            return Err(ErrorsRawRealToInteger::NotFinite {
                value: self,
                backtrace: capture_backtrace(),
            });
        }

        let truncation = self.clone().trunc();

        let out_of_range = || ErrorsRawRealToInteger::OutOfRange {
            value: self,
            min: usize::MIN,
            max: usize::MAX,
            backtrace: capture_backtrace(),
        };

        let truncation_as_rug_integer = truncation.to_integer().ok_or_else(out_of_range.clone())?;

        truncation_as_rug_integer
            .to_usize()
            .ok_or_else(out_of_range)
    }
}

impl RawScalarTrait for rug::Complex {
    type ValidationErrors = ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>;

    fn raw_zero(precision: u32) -> Self {
        rug::Complex::with_val(precision, (0., 0.))
    }

    fn is_zero(&self) -> bool {
        rug::Complex::is_zero(self)
    }

    fn raw_one(precision: u32) -> Self {
        rug::Complex::with_val(precision, (1., 0.))
    }

    #[duplicate_item(
        unchecked_method       method;
        [unchecked_reciprocal] [recip];
        [unchecked_exp]        [exp];
        [unchecked_sqrt]       [sqrt];
        [unchecked_sin]        [sin];
        [unchecked_asin]       [asin];
        [unchecked_cos]        [cos];
        [unchecked_acos]       [acos];
        [unchecked_tan]        [tan];
        [unchecked_atan]       [atan];
        [unchecked_sinh]       [sinh];
        [unchecked_asinh]      [asinh];
        [unchecked_cosh]       [cosh];
        [unchecked_acosh]      [acosh];
        [unchecked_tanh]       [tanh];
        [unchecked_atanh]      [atanh];
        [unchecked_ln]         [ln];
        [unchecked_log10]      [log10];
    )]
    #[inline(always)]
    fn unchecked_method(self) -> Self {
        rug::Complex::method(self)
    }

    #[inline(always)]
    fn unchecked_log2(self) -> Self {
        let ln_2 = rug::Float::with_val(self.real().prec(), 2.).ln();
        rug::Complex::ln(self) / ln_2
    }

    #[duplicate_item(
        unchecked_method               exponent_type;
        [unchecked_pow_exponent_i8]    [i8];
        [unchecked_pow_exponent_i16]   [i16];
        [unchecked_pow_exponent_i32]   [i32];
        [unchecked_pow_exponent_i64]   [i64];
        [unchecked_pow_exponent_i128]  [i128];
        [unchecked_pow_exponent_isize] [isize];
        [unchecked_pow_exponent_u8]    [u8];
        [unchecked_pow_exponent_u16]   [u16];
        [unchecked_pow_exponent_u32]   [u32];
        [unchecked_pow_exponent_u64]   [u64];
        [unchecked_pow_exponent_u128]  [u128];
        [unchecked_pow_exponent_usize] [usize];
    )]
    #[inline(always)]
    fn unchecked_method(self, exponent: &exponent_type) -> Self {
        rug::Complex::pow(self, exponent)
    }

    /// Multiplies and adds in one fused operation, rounding to the nearest with only one rounding error.
    ///
    /// `a.mul_add(b, c)` produces a result like `a * &b + &c`.
    #[inline(always)]
    fn unchecked_mul_add(self, b: &Self, c: &Self) -> Self {
        rug::Complex::mul_add(self, b, c)
    }

    fn compute_hash<H: Hasher>(&self, state: &mut H) {
        self.raw_real_part().compute_hash(state);
        self.raw_imag_part().compute_hash(state);
    }
}

impl Conjugate for rug::Complex {
    #[inline(always)]
    fn conjugate(self) -> Self {
        rug::Complex::conj(self)
    }
}

impl RawComplexTrait for rug::Complex {
    type RawReal = rug::Float;

    fn new_unchecked_raw_complex(real: rug::Float, imag: rug::Float) -> Self {
        debug_assert_eq!(
            real.prec(),
            imag.prec(),
            "Different precision between real and imaginary part!"
        );
        rug::Complex::with_val(real.prec(), (real, imag))
    }

    /// Returns a mutable reference to the real part of the complex number.
    fn mut_raw_real_part(&mut self) -> &mut rug::Float {
        self.mut_real()
    }

    /// Returns a mutable reference to the imaginary part of the complex number.
    fn mut_raw_imag_part(&mut self) -> &mut rug::Float {
        self.mut_imag()
    }

    #[inline(always)]
    fn unchecked_abs(self) -> rug::Float {
        rug::Complex::abs(self).into_real_imag().0
    }

    #[inline(always)]
    fn raw_real_part(&self) -> &rug::Float {
        self.real()
    }

    #[inline(always)]
    fn raw_imag_part(&self) -> &rug::Float {
        self.imag()
    }

    #[inline(always)]
    fn unchecked_arg(self) -> rug::Float {
        rug::Complex::arg(self).into_real_imag().0
    }

    #[inline(always)]
    fn unchecked_pow_exponent_real(self, exponent: &rug::Float) -> Self {
        rug::Complex::pow(self, exponent)
    }
}

impl FpChecks for rug::Float {
    fn is_finite(&self) -> bool {
        rug::Float::is_finite(self)
    }

    fn is_infinite(&self) -> bool {
        rug::Float::is_infinite(self)
    }

    fn is_nan(&self) -> bool {
        rug::Float::is_nan(self)
    }
    fn is_normal(&self) -> bool {
        rug::Float::is_normal(self)
    }
}

impl FpChecks for rug::Complex {
    /// Returns `true` if the real and imaginary parts of `self` are neither infinite nor NaN.
    #[inline(always)]
    fn is_finite(&self) -> bool {
        self.real().is_finite() && self.imag().is_finite()
    }

    /// Returns `true` if the real or the imaginary part of `self` are positive infinity or negative infinity.
    #[inline(always)]
    fn is_infinite(&self) -> bool {
        !self.is_nan() && (self.real().is_infinite() || self.imag().is_infinite())
    }

    /// Returns `true` if the real or the imaginary part of `self` are NaN.
    #[inline(always)]
    fn is_nan(&self) -> bool {
        self.real().is_nan() || self.imag().is_nan()
    }

    /// Returns `true` if the real and the imaginary part of `self` are *normal* (i.e. neither zero, infinite, subnormal, or NaN).
    #[inline(always)]
    fn is_normal(&self) -> bool {
        self.real().is_normal() && self.imag().is_normal()
    }
}

//------------------------------------------------------------------------------------------------------------
/// A type alias for a numerical kernel that uses `rug` for arbitrary-precision arithmetic with strict finiteness checks.
///
/// This policy is a specialization of [`NumKernelStrictFinite`] for the `rug` backend.
/// It bundles the raw `rug` types ([`rug::Float`] and [`rug::Complex`]) with their corresponding
/// validation logic, [`StrictFinitePolicy`](crate::validation::StrictFinitePolicy).
///
/// The primary role of this policy is to serve as the generic parameter for the main validated
/// types, [`RealRugStrictFinite<PRECISION>`] and [`ComplexRugStrictFinite<PRECISION>`], thereby
/// defining their behavior and ensuring their correctness.
///
/// # Validation
///
/// This policy enforces the following rules on the underlying [`rug::Float`] values, for both
/// real numbers and the components of complex numbers:
///
/// 1.  **Finiteness**: The value must not be `NaN` or `Infinity`.
/// 2.  **Precision**: The precision of the [`rug::Float`] must exactly match the `PRECISION`
///     constant specified in the type. This is a critical check for `rug` types.
///
/// # Example
///
/// While you typically won't use `RugStrictFinite` directly, it's the engine that
/// powers the validated `rug` types.
///
/// ```rust
/// use num_valid::{RealRugStrictFinite, ComplexRugStrictFinite};
/// use rug::{Float, Complex};
/// use try_create::TryNew;
///
/// const PRECISION: u32 = 100;
///
/// // This works because the value is finite and has the correct precision.
/// let valid_real = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.14)).unwrap();
///
/// // This fails because the precision is wrong.
/// let wrong_precision_real = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION + 1, 3.14));
/// assert!(wrong_precision_real.is_err());
///
/// // This fails because the value is NaN.
/// let nan_real = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, f64::NAN));
/// assert!(nan_real.is_err());
///
/// // The same rules apply to complex numbers.
/// let valid_complex = ComplexRugStrictFinite::<PRECISION>::try_new(
///     Complex::with_val(PRECISION, (1.0, 2.0))
/// ).unwrap();
///
/// let invalid_complex = ComplexRugStrictFinite::<PRECISION>::try_new(
///     Complex::with_val(PRECISION, (1.0, f64::INFINITY))
/// );
/// assert!(invalid_complex.is_err());
/// ```
pub type RugStrictFinite<const PRECISION: u32> = NumKernelStrictFinite<rug::Float, PRECISION>;
//------------------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------------------
/// A type alias for a validated real scalar using the `Rug` kernel with a specified precision and the `NumKernelStrictFinite` validation policy.
pub type RealRugStrictFinite<const PRECISION: u32> = RealValidated<RugStrictFinite<PRECISION>>;

/// A type alias for a validated complex scalar using the `Rug` kernel with a specified precision and the `NumKernelStrictFinite` validation policy.
pub type ComplexRugStrictFinite<const PRECISION: u32> =
    ComplexValidated<RugStrictFinite<PRECISION>>;

impl Sign for rug::Float {
    /// Returns a number with the magnitude of `self` and the sign of `sign`.
    #[inline(always)]
    fn kernel_copysign(self, sign: &Self) -> Self {
        self.copysign(sign)
    }

    /// Returns `true` if the value is negative, −0 or NaN with a negative sign.
    #[inline(always)]
    fn kernel_is_sign_negative(&self) -> bool {
        self.is_sign_negative()
    }

    /// Returns `true` if the value is positive, +0 or NaN with a positive sign.
    #[inline(always)]
    fn kernel_is_sign_positive(&self) -> bool {
        self.is_sign_positive()
    }

    /// Returns the signum of the number.
    ///
    /// This method relies on `rug::Float::signum()`.
    /// The behavior for special values is as follows:
    /// - If the number is positive and not zero, returns `1.0`.
    /// - If the number is negative and not zero, returns `-1.0`.
    /// - If the number is zero, `rug::Float::signum()` returns `1.0` (representing positive zero).
    #[inline(always)]
    fn kernel_signum(self) -> Self {
        self.signum()
    }
}

impl Rounding for rug::Float {
    /// Returns the smallest integer greater than or equal to `self`.
    #[inline(always)]
    fn kernel_ceil(self) -> Self {
        self.ceil()
    }

    /// Returns the largest integer smaller than or equal to `self`.
    #[inline(always)]
    fn kernel_floor(self) -> Self {
        self.floor()
    }

    /// Returns the fractional part of `self`.
    #[inline(always)]
    fn kernel_fract(self) -> Self {
        self.fract()
    }

    /// Rounds `self` to the nearest integer, rounding half-way cases away from zero.
    #[inline(always)]
    fn kernel_round(self) -> Self {
        self.round()
    }

    /// Returns the nearest integer to a number. Rounds half-way cases to the number with an even least significant digit.
    ///
    /// This function always returns the precise result.
    ///
    /// # Examples
    /// ```
    /// use num_valid::{RealScalar, RealRugStrictFinite, functions::Rounding};
    ///
    /// const PRECISION: u32 = 100;
    ///
    /// let f = RealRugStrictFinite::<PRECISION>::try_from_f64(3.3).unwrap();
    /// let g = RealRugStrictFinite::<PRECISION>::try_from_f64(-3.3).unwrap();
    /// let h = RealRugStrictFinite::<PRECISION>::try_from_f64(3.5).unwrap();
    /// let i = RealRugStrictFinite::<PRECISION>::try_from_f64(-4.5).unwrap();
    ///
    /// assert_eq!(f.kernel_round_ties_even(), RealRugStrictFinite::<PRECISION>::try_from_f64(3.).unwrap());
    /// assert_eq!(g.kernel_round_ties_even(), RealRugStrictFinite::<PRECISION>::try_from_f64(-3.).unwrap());
    /// assert_eq!(h.kernel_round_ties_even(), RealRugStrictFinite::<PRECISION>::try_from_f64(4.).unwrap());
    /// assert_eq!(i.kernel_round_ties_even(), RealRugStrictFinite::<PRECISION>::try_from_f64(-4.).unwrap());
    /// ```
    #[inline(always)]
    fn kernel_round_ties_even(self) -> Self {
        self.round_even()
    }

    /// Returns the integer part of `self`. This means that non-integer numbers are always truncated towards zero.
    ///    
    /// # Examples
    /// ```
    /// use num_valid::{RealScalar, RealRugStrictFinite, functions::Rounding};
    ///
    /// const PRECISION: u32 = 100;
    ///
    /// let f = RealRugStrictFinite::<PRECISION>::try_from_f64(3.7).unwrap();
    /// let g = RealRugStrictFinite::<PRECISION>::try_from_f64(3.).unwrap();
    /// let h = RealRugStrictFinite::<PRECISION>::try_from_f64(-3.7).unwrap();
    ///
    /// assert_eq!(f.kernel_trunc(), RealRugStrictFinite::<PRECISION>::try_from_f64(3.).unwrap());
    /// assert_eq!(g.kernel_trunc(), RealRugStrictFinite::<PRECISION>::try_from_f64(3.).unwrap());
    /// assert_eq!(h.kernel_trunc(), RealRugStrictFinite::<PRECISION>::try_from_f64(-3.).unwrap());
    /// ```
    #[inline(always)]
    fn kernel_trunc(self) -> Self {
        self.trunc()
    }
}

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

//-------------------------------------------------------------
impl<const PRECISION: u32> TryFrom<Complex<f64>> for ComplexRugStrictFinite<PRECISION> {
    type Error = ErrorsValidationRawComplex<ErrorsTryFromf64<rug::Float>>;

    fn try_from(value: Complex<f64>) -> Result<Self, Self::Error> {
        let real_part = RealRugStrictFinite::<PRECISION>::try_from_f64(value.re);
        let imag_part = RealRugStrictFinite::<PRECISION>::try_from_f64(value.im);

        match (real_part, imag_part) {
            (Ok(real_part), Ok(imag_part)) => Ok(Self {
                value: rug::Complex::with_val(
                    PRECISION,
                    (real_part.into_inner(), imag_part.into_inner()),
                ),
                _phantom: PhantomData,
            }),
            (Err(error_real_part), Ok(_)) => Err(ErrorsValidationRawComplex::InvalidRealPart {
                source: error_real_part,
            }),
            (Ok(_), Err(error_imaginary_part)) => {
                Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
                    source: error_imaginary_part,
                })
            }
            (Err(error_real_part), Err(error_imaginary_part)) => {
                Err(ErrorsValidationRawComplex::InvalidBothParts {
                    real_error: error_real_part,
                    imag_error: error_imaginary_part,
                })
            }
        }
    }
}
//------------------------------------------------------------------------------------------------

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

#[duplicate_item(
    T;
    [rug::Float];
    [rug::Complex];
)]
/// Compound negation and assignment.
impl NegAssign for T {
    /// Performs the negation of `self`.
    fn neg_assign(&mut self) {
        <T as rug::ops::NegAssign>::neg_assign(self);
    }
}
//------------------------------------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        ACosH, ComplexRugStrictFinite, ComplexScalarConstructors, ComplexScalarGetParts,
        ComplexScalarMutateParts, ComplexScalarSetParts, Constants, Max, Min,
        functions::{
            ACos, ACosHErrors, ACosHInputErrors, ACosRealErrors, ACosRealInputErrors, ASin,
            ASinRealErrors, ASinRealInputErrors, ATan, ATan2, ATan2Errors, ATanComplexErrors,
            ATanComplexInputErrors, ATanH, ATanHErrors, ATanHInputErrors, Abs, Clamp, Exp,
            ExpErrors, Hypot, Ln, Log2, LogarithmComplexErrors, LogarithmComplexInputErrors,
            NegAssign, Pow, PowComplexBaseRealExponentErrors, PowIntExponentErrors,
            PowIntExponentInputErrors, PowRealBaseRealExponentErrors, Reciprocal, ReciprocalErrors,
            Sqrt, SqrtRealErrors, TotalCmp,
        },
        validation::ErrorsValidationRawReal,
    };
    use num::{One, Zero};
    use rug::Float;
    use try_create::{TryNew, TryNewValidated};

    const PRECISION: u32 = 53;

    mod fp_checks {
        use super::*;
        use crate::kernels::rug::{ComplexRugStrictFinite, RealRugStrictFinite};
        use rug::Float;

        #[test]
        fn is_finite() {
            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
            assert!(real.is_finite());

            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, f64::INFINITY));
            assert!(real.is_err());

            let complex = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                53,
                (Float::with_val(53, 1.0), Float::with_val(53, 1.0)),
            ))
            .unwrap();
            assert!(complex.is_finite());

            let complex = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                53,
                (Float::with_val(53, f64::INFINITY), Float::with_val(53, 1.0)),
            ));
            assert!(complex.is_err());
        }

        #[test]
        fn is_infinite() {
            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
            assert!(!real.is_infinite());

            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, f64::INFINITY));
            assert!(real.is_err());

            let complex = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                53,
                (Float::with_val(53, 1.0), Float::with_val(53, 1.0)),
            ))
            .unwrap();
            assert!(!complex.is_infinite());

            let complex = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                53,
                (Float::with_val(53, f64::INFINITY), Float::with_val(53, 1.0)),
            ));
            assert!(complex.is_err());
        }

        #[test]
        fn is_nan() {
            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
            assert!(!real.is_nan());

            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, f64::NAN));
            assert!(matches!(real, Err(ErrorsValidationRawReal::IsNaN { .. })));

            let complex = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                53,
                (Float::with_val(53, 1.0), Float::with_val(53, 1.0)),
            ))
            .unwrap();
            assert!(!complex.is_nan());

            let complex = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                53,
                (Float::with_val(53, f64::NAN), Float::with_val(53, 1.0)),
            ));
            assert!(matches!(
                complex,
                Err(ErrorsValidationRawComplex::InvalidRealPart {
                    source: ErrorsValidationRawReal::IsNaN { .. }
                })
            ));
        }

        #[test]
        fn is_normal() {
            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
            assert!(real.is_normal());

            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.0)).unwrap();
            assert!(!real.is_normal());

            let complex = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                53,
                (Float::with_val(53, 1.0), Float::with_val(53, 1.0)),
            ))
            .unwrap();
            assert!(complex.is_normal());

            let complex = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                53,
                (Float::with_val(53, 0.0), Float::with_val(53, 0.0)),
            ))
            .unwrap();
            assert!(!complex.is_normal());
        }
    }

    mod abs {
        use super::*;

        mod real {
            use super::*;

            #[test]
            fn abs_valid() {
                let value =
                    RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, -4.0)).unwrap();

                let expected_result =
                    RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 4.0)).unwrap();
                assert_eq!(value.clone().try_abs().unwrap(), expected_result);
                assert_eq!(value.abs(), expected_result);
            }

            #[test]
            fn abs_zero() {
                let value =
                    RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 0.0)).unwrap();

                let expected_result =
                    RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 0.0)).unwrap();
                assert_eq!(value.clone().try_abs().unwrap(), expected_result);
                assert_eq!(value.abs(), expected_result);
            }

            /*
            #[cfg(feature = "rug")]
            #[test]
            fn abs_nan() {
                let value =
                    RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, rug::float::Special::Nan))
                        .unwrap();
                let result = value.try_abs();
                assert!(matches!(result, Err(AbsRealErrors::Input { .. })));
            }

            #[cfg(feature = "rug")]
            #[test]
            fn abs_infinite() {
                let value = RealRugStrictFinite::<53>::try_new(rug::Float::with_val(
                    53,
                    rug::float::Special::Infinity,
                ))
                .unwrap();
                let result = value.try_abs();
                assert!(matches!(result, Err(AbsRealErrors::Input { .. })));
            }
            */
        }

        mod complex {
            use super::*;

            #[test]
            fn abs_valid() {
                let value = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                    53,
                    (rug::Float::with_val(53, 3.0), rug::Float::with_val(53, 4.0)),
                ))
                .unwrap();

                let expected_result =
                    RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 5.0)).unwrap();
                assert_eq!(value.clone().try_abs().unwrap(), expected_result);
                assert_eq!(value.abs(), expected_result);
            }

            #[test]
            fn abs_zero() {
                let value = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                    53,
                    (rug::Float::with_val(53, 0.0), rug::Float::with_val(53, 0.0)),
                ))
                .unwrap();

                let expected_result =
                    RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 0.0)).unwrap();
                assert_eq!(value.clone().try_abs().unwrap(), expected_result);
                assert_eq!(value.abs(), expected_result);
            }
            /*
            #[test]
            fn abs_nan() {
                let value = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                    53,
                    (
                        rug::Float::with_val(53, rug::float::Special::Nan),
                        rug::Float::with_val(53, 0.0),
                    ),
                ))
                .unwrap();
                assert!(matches!(
                    value.try_abs(),
                    Err(AbsComplexErrors::Input { .. })
                ));
            }

            #[test]
            fn abs_infinite() {
                let value = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                    53,
                    (
                        rug::Float::with_val(53, rug::float::Special::Infinity),
                        rug::Float::with_val(53, 0.0),
                    ),
                ))
                .unwrap();
                assert!(matches!(
                    value.try_abs(),
                    Err(AbsComplexErrors::Input { .. })
                ));
            }
            */
        }
    }

    mod max_min {
        use super::*;

        #[test]
        fn test_realrug_max() {
            let value =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.0)).unwrap();
            let other =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 5.0)).unwrap();
            let expected =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 5.0)).unwrap();
            assert_eq!(Max::max(&value, &other), &expected);

            let neg_value =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -3.0))
                    .unwrap();
            let neg_other =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -5.0))
                    .unwrap();
            let neg_expected =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -3.0))
                    .unwrap();
            assert_eq!(Max::max(&neg_value, &neg_other), &neg_expected);
        }

        #[test]
        fn test_realrug_min() {
            let value =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.0)).unwrap();
            let other =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 5.0)).unwrap();
            let expected =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.0)).unwrap();
            assert_eq!(Min::min(&value, &other), &expected);

            let neg_value =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -3.0))
                    .unwrap();
            let neg_other =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -5.0))
                    .unwrap();
            let neg_expected =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -5.0))
                    .unwrap();
            assert_eq!(Min::min(&neg_value, &neg_other), &neg_expected);
        }
    }

    mod builders {
        use super::*;

        mod real {
            use super::*;

            #[test]
            fn try_new_nan() {
                let nan = rug::Float::with_val(PRECISION, f64::NAN);
                let err = RealRugStrictFinite::<PRECISION>::try_new(nan).unwrap_err();
                assert!(matches!(err, ErrorsValidationRawReal::IsNaN { .. }));
            }

            #[test]
            fn try_new_pos_infinity() {
                let pos_infinity = rug::Float::with_val(PRECISION, f64::INFINITY);
                let err = RealRugStrictFinite::<PRECISION>::try_new(pos_infinity).unwrap_err();
                assert!(matches!(err, ErrorsValidationRawReal::IsPosInfinity { .. }));
            }

            #[test]
            fn try_new_neg_infinity() {
                let neg_infinity = rug::Float::with_val(PRECISION, f64::NEG_INFINITY);
                let err = RealRugStrictFinite::<PRECISION>::try_new(neg_infinity).unwrap_err();
                assert!(matches!(err, ErrorsValidationRawReal::IsNegInfinity { .. }));
            }
        }

        mod complex {
            use super::*;

            #[test]
            fn real_part() {
                let c1 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (1.23, 4.56)),
                )
                .unwrap();
                assert_eq!(c1.real_part(), 1.23);

                let c2 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (-7.89, 0.12)),
                )
                .unwrap();
                assert_eq!(c2.real_part(), -7.89);

                let c3 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (0.0, 10.0)),
                )
                .unwrap();
                assert_eq!(c3.real_part(), 0.0);

                let c_nan_re = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (f64::NAN, 5.0)),
                )
                .unwrap_err();
                assert!(matches!(
                    c_nan_re,
                    ErrorsValidationRawComplex::InvalidRealPart {
                        source: ErrorsValidationRawReal::IsNaN { .. }
                    }
                ));

                let c_inf_re = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (f64::INFINITY, 5.0)),
                )
                .unwrap_err();
                assert!(matches!(
                    c_inf_re,
                    ErrorsValidationRawComplex::InvalidRealPart {
                        source: ErrorsValidationRawReal::IsPosInfinity { .. }
                    }
                ));

                let c_neg_inf_re = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (f64::NEG_INFINITY, 5.0)),
                )
                .unwrap_err();
                assert!(matches!(
                    c_neg_inf_re,
                    ErrorsValidationRawComplex::InvalidRealPart {
                        source: ErrorsValidationRawReal::IsNegInfinity { .. }
                    }
                ));
            }

            #[test]
            fn imag_part() {
                let c1 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (1.23, 4.56)),
                )
                .unwrap();
                assert_eq!(c1.imag_part(), 4.56);

                let c2 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (-7.89, 0.12)),
                )
                .unwrap();
                assert_eq!(c2.imag_part(), 0.12);

                let c3 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (10.0, 0.0)),
                )
                .unwrap();
                assert_eq!(c3.imag_part(), 0.0);

                let c_nan_im = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (5.0, f64::NAN)),
                )
                .unwrap_err();
                assert!(matches!(
                    c_nan_im,
                    ErrorsValidationRawComplex::InvalidImaginaryPart {
                        source: ErrorsValidationRawReal::IsNaN { .. }
                    }
                ));

                let c_inf_im = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (5.0, f64::INFINITY)),
                )
                .unwrap_err();
                assert!(matches!(
                    c_inf_im,
                    ErrorsValidationRawComplex::InvalidImaginaryPart {
                        source: ErrorsValidationRawReal::IsPosInfinity { .. }
                    }
                ));

                let c_neg_inf_im = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (5.0, f64::NEG_INFINITY)),
                )
                .unwrap_err();
                assert!(matches!(
                    c_neg_inf_im,
                    ErrorsValidationRawComplex::InvalidImaginaryPart {
                        source: ErrorsValidationRawReal::IsNegInfinity { .. }
                    }
                ));
            }

            #[test]
            fn try_new_complex() {
                let r1 = RealRugStrictFinite::<PRECISION>::try_from_f64(1.23).unwrap();
                let i1 = RealRugStrictFinite::<PRECISION>::try_from_f64(4.56).unwrap();
                let c1 = ComplexRugStrictFinite::<PRECISION>::try_new_complex(
                    r1.as_ref().clone(),
                    i1.as_ref().clone(),
                )
                .unwrap();
                assert_eq!(c1.real_part(), r1);
                assert_eq!(c1.imag_part(), i1);

                let r2 = RealRugStrictFinite::<PRECISION>::try_from_f64(-7.89).unwrap();
                let i2 = RealRugStrictFinite::<PRECISION>::try_from_f64(-0.12).unwrap();
                let c2 = ComplexRugStrictFinite::<PRECISION>::try_new_complex(
                    r2.as_ref().clone(),
                    i2.as_ref().clone(),
                )
                .unwrap();
                assert_eq!(c2.real_part(), r2);
                assert_eq!(c2.imag_part(), i2);

                let r3 = RealRugStrictFinite::<PRECISION>::try_from_f64(0.0).unwrap();
                let i3 = RealRugStrictFinite::<PRECISION>::try_from_f64(0.0).unwrap();
                let c3 = ComplexRugStrictFinite::<PRECISION>::try_new_complex(
                    r3.as_ref().clone(),
                    i3.as_ref().clone(),
                )
                .unwrap();
                assert_eq!(c3.real_part(), r3);
                assert_eq!(c3.real_part(), i3);
                assert!(c3.is_zero());

                let c_nan_re = ComplexRugStrictFinite::<PRECISION>::try_new_complex(
                    Float::with_val(PRECISION, f64::NAN),
                    Float::with_val(PRECISION, 5.0),
                )
                .unwrap_err();
                assert!(matches!(
                    c_nan_re,
                    ErrorsValidationRawComplex::InvalidRealPart { .. }
                ));

                let c_inf_im = ComplexRugStrictFinite::<PRECISION>::try_new_complex(
                    Float::with_val(PRECISION, 10.0),
                    Float::with_val(PRECISION, f64::INFINITY),
                )
                .unwrap_err();
                assert!(matches!(
                    c_inf_im,
                    ErrorsValidationRawComplex::InvalidImaginaryPart { .. }
                ));

                let c_nan_re_inf_im = ComplexRugStrictFinite::<PRECISION>::try_new_complex(
                    Float::with_val(PRECISION, f64::NAN),
                    Float::with_val(PRECISION, f64::INFINITY),
                )
                .unwrap_err();
                assert!(matches!(
                    c_nan_re_inf_im,
                    ErrorsValidationRawComplex::InvalidBothParts { .. }
                ));
            }

            #[test]
            fn try_from_complexf64() {
                let c1_in = num::Complex::new(1.23, 4.56);
                let c1 = ComplexRugStrictFinite::<PRECISION>::try_from(c1_in).unwrap();
                assert_eq!(c1.real_part(), c1_in.re);
                assert_eq!(c1.imag_part(), c1_in.im);

                let c2_in = num::Complex::new(-7.89, -0.12);
                let c2 = ComplexRugStrictFinite::<PRECISION>::try_from(c2_in).unwrap();
                assert_eq!(c2.real_part(), c2_in.re);
                assert_eq!(c2.imag_part(), c2_in.im);

                let c3_in = num::Complex::new(0., 0.);
                let c3 = ComplexRugStrictFinite::<PRECISION>::try_from(c3_in).unwrap();
                assert_eq!(c3.real_part(), c3_in.re);
                assert_eq!(c3.imag_part(), c3_in.im);

                let c_nan_re =
                    ComplexRugStrictFinite::<PRECISION>::try_from(num::Complex::new(f64::NAN, 5.0))
                        .unwrap_err();
                assert!(matches!(
                    c_nan_re,
                    ErrorsValidationRawComplex::InvalidRealPart { .. }
                ));

                let c_inf_im = ComplexRugStrictFinite::<PRECISION>::try_from(num::Complex::new(
                    10.0,
                    f64::INFINITY,
                ))
                .unwrap_err();
                assert!(matches!(
                    c_inf_im,
                    ErrorsValidationRawComplex::InvalidImaginaryPart { .. }
                ));

                let c_nan_re_inf_im = ComplexRugStrictFinite::<PRECISION>::try_from(
                    num::Complex::new(f64::NAN, f64::INFINITY),
                )
                .unwrap_err();
                assert!(matches!(
                    c_nan_re_inf_im,
                    ErrorsValidationRawComplex::InvalidBothParts { .. }
                ));
            }

            #[test]
            fn try_new_pure_real() {
                let r1 = RealRugStrictFinite::<PRECISION>::try_from_f64(1.23).unwrap();
                let c1 =
                    ComplexRugStrictFinite::<PRECISION>::try_new_pure_real(r1.as_ref().clone())
                        .unwrap();
                assert_eq!(c1.real_part(), r1);
                assert!(c1.imag_part().is_zero());

                let c_nan = ComplexRugStrictFinite::<PRECISION>::try_new_pure_real(
                    Float::with_val(PRECISION, f64::NAN),
                )
                .unwrap_err();
                assert!(matches!(
                    c_nan,
                    ErrorsValidationRawComplex::InvalidRealPart {
                        source: ErrorsValidationRawReal::IsNaN { .. }
                    }
                ));
            }

            #[test]
            fn try_new_pure_imaginary() {
                let i1 = RealRugStrictFinite::<PRECISION>::try_from_f64(1.23).unwrap();
                let c1 = ComplexRugStrictFinite::<PRECISION>::try_new_pure_imaginary(
                    i1.as_ref().clone(),
                )
                .unwrap();
                assert!(c1.real_part().is_zero());
                assert_eq!(c1.imag_part(), i1);

                let c_nan = ComplexRugStrictFinite::<PRECISION>::try_new_pure_imaginary(
                    Float::with_val(PRECISION, f64::NAN),
                )
                .unwrap_err();
                assert!(matches!(
                    c_nan,
                    ErrorsValidationRawComplex::InvalidImaginaryPart {
                        source: ErrorsValidationRawReal::IsNaN { .. }
                    }
                ));
            }

            #[test]
            fn add_to_real_part() {
                let mut c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (1.0, 2.0)),
                )
                .unwrap();
                c.add_to_real_part(&RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap());
                assert_eq!(c.real_part(), 4.0);
                assert_eq!(c.imag_part(), 2.0);

                c.add_to_real_part(&RealRugStrictFinite::<PRECISION>::try_from_f64(-5.0).unwrap());
                assert_eq!(c.real_part(), -1.0);
                assert_eq!(c.imag_part(), 2.0);
            }

            #[test]
            fn add_to_imaginary_part() {
                let mut c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (1.0, 2.0)),
                )
                .unwrap();
                c.add_to_imaginary_part(
                    &RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap(),
                );
                assert_eq!(c.real_part(), 1.0);
                assert_eq!(c.imag_part(), 5.0);

                c.add_to_imaginary_part(
                    &RealRugStrictFinite::<PRECISION>::try_from_f64(-4.0).unwrap(),
                );
                assert_eq!(c.real_part(), 1.0);
                assert_eq!(c.imag_part(), 1.0);
            }

            #[test]
            fn multiply_real_part() {
                let mut c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (1.0, 2.0)),
                )
                .unwrap();
                c.multiply_real_part(&RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap());
                assert_eq!(c.real_part(), 3.0);
                assert_eq!(c.imag_part(), 2.0);

                c.multiply_real_part(
                    &RealRugStrictFinite::<PRECISION>::try_from_f64(-2.0).unwrap(),
                );
                assert_eq!(c.real_part(), -6.0);
                assert_eq!(c.imag_part(), 2.0);
            }

            #[test]
            fn multiply_imaginary_part() {
                let mut c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (1.0, 2.0)),
                )
                .unwrap();
                c.multiply_imaginary_part(
                    &RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap(),
                );
                assert_eq!(c.real_part(), 1.0);
                assert_eq!(c.imag_part(), 6.0);

                c.multiply_imaginary_part(
                    &RealRugStrictFinite::<PRECISION>::try_from_f64(-0.5).unwrap(),
                );
                assert_eq!(c.real_part(), 1.0);
                assert_eq!(c.imag_part(), -3.0);
            }

            #[test]
            fn set_real_part() {
                let mut c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (1.0, 2.0)),
                )
                .unwrap();
                c.set_real_part(RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap());
                assert_eq!(c.real_part(), 3.0);
                assert_eq!(c.imag_part(), 2.0);

                c.set_real_part(RealRugStrictFinite::<PRECISION>::try_from_f64(-4.0).unwrap());
                assert_eq!(c.real_part(), -4.0);
                assert_eq!(c.imag_part(), 2.0);
            }

            #[test]
            fn set_imaginary_part() {
                let mut c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (1.0, 2.0)),
                )
                .unwrap();
                c.set_imaginary_part(RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap());
                assert_eq!(c.real_part(), 1.0);
                assert_eq!(c.imag_part(), 3.0);

                c.set_imaginary_part(RealRugStrictFinite::<PRECISION>::try_from_f64(-4.0).unwrap());
                assert_eq!(c.real_part(), 1.0);
                assert_eq!(c.imag_part(), -4.0);
            }
        }
    }

    mod mul {
        use super::*;

        mod real {
            use super::*;

            #[test]
            fn multiply_ref() {
                let r1 = RealRugStrictFinite::<PRECISION>::try_new_validated(rug::Float::with_val(
                    PRECISION, 3.0,
                ))
                .unwrap();
                let r2 = RealRugStrictFinite::<PRECISION>::try_new_validated(rug::Float::with_val(
                    PRECISION, 4.0,
                ))
                .unwrap();
                let result = r1 * &r2;
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new_validated(rug::Float::with_val(
                        PRECISION, 12.0
                    ))
                    .unwrap()
                );
            }
        }

        mod complex {
            use super::*;

            #[test]
            fn multiply_ref() {
                let c1 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (1.0, 2.0)),
                )
                .unwrap();
                let c2 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (3.0, 4.0)),
                )
                .unwrap();
                let result = c1 * &c2;
                assert_eq!(
                    result,
                    ComplexRugStrictFinite::<PRECISION>::try_new_validated(rug::Complex::with_val(
                        PRECISION,
                        (-5.0, 10.0),
                    ))
                    .unwrap()
                ); // (1*3 - 2*4) + (1*4 + 2*3)i
            }

            #[test]
            fn complex_times_real() {
                let r = RealRugStrictFinite::<PRECISION>::try_new_validated(rug::Float::with_val(
                    PRECISION, 2.0,
                ))
                .unwrap();
                let c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (3.0, 4.0)),
                )
                .unwrap();

                let result_expected = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (6.0, 8.0)),
                )
                .unwrap(); // 2 * (3 + 4i) = 6 + 8i

                let result = c.clone() * &r;
                assert_eq!(&result, &result_expected);

                let result = c.clone() * r.clone();
                assert_eq!(&result, &result_expected);

                let mut result = c.clone();
                result *= &r;
                assert_eq!(&result, &result_expected);

                let mut result = c.clone();
                result *= r;
                assert_eq!(&result, &result_expected);
            }

            #[test]
            fn real_times_complex() {
                let r = RealRugStrictFinite::<PRECISION>::try_new_validated(rug::Float::with_val(
                    PRECISION, 2.0,
                ))
                .unwrap();
                let c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (3.0, 4.0)),
                )
                .unwrap();

                let result_expected = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Complex::with_val(PRECISION, (6.0, 8.0)),
                )
                .unwrap(); // 2 * (3 + 4i) = 6 + 8i

                let result = &r * c.clone();
                assert_eq!(&result, &result_expected);

                let result = r * c.clone();
                assert_eq!(&result, &result_expected);
            }
        }
    }

    mod arithmetic {
        use super::*;

        mod real {
            use super::*;

            #[test]
            fn neg_assign() {
                let mut a = rug::Float::with_val(PRECISION, 1.);
                a.neg_assign();
                let a_expected = rug::Float::with_val(PRECISION, -1.);
                assert_eq!(a, a_expected);

                let mut b = RealRugStrictFinite::<PRECISION>::one();
                b.neg_assign();
                let b_expected = RealRugStrictFinite::<PRECISION>::try_new_validated(
                    rug::Float::with_val(PRECISION, -1.),
                )
                .unwrap();
                assert_eq!(&b, &b_expected);
            }

            #[test]
            #[should_panic(expected = "Division failed validation")]
            fn div_by_zero() {
                let one = RealRugStrictFinite::<PRECISION>::one();
                let zero = RealRugStrictFinite::<PRECISION>::zero();
                let _ = &one / &zero;
            }

            #[test]
            #[should_panic(expected = "Division failed validation")]
            fn div_assign_by_zero() {
                let mut num = RealRugStrictFinite::<PRECISION>::one();
                let zero_ref = &RealRugStrictFinite::<PRECISION>::zero();
                num /= zero_ref;
            }
        }

        mod complex {
            use super::*;

            #[test]
            fn neg_assign() {
                let re = rug::Float::with_val(PRECISION, 1.);
                let im = rug::Float::with_val(PRECISION, 2.);
                let mut num = rug::Complex::with_val(PRECISION, (re, im));
                num.neg_assign();
                let expected = rug::Complex::with_val(
                    PRECISION,
                    (
                        rug::Float::with_val(PRECISION, -1.),
                        rug::Float::with_val(PRECISION, -2.),
                    ),
                );
                assert_eq!(&num, &expected);

                let mut num = ComplexRugStrictFinite::<PRECISION>::try_new_validated(num).unwrap();
                let expected = num.clone().neg();
                num.neg_assign();
                assert_eq!(&num, &expected);
            }

            #[test]
            #[should_panic(expected = "Division failed validation")]
            fn div_by_zero() {
                let one = ComplexRugStrictFinite::<PRECISION>::one();
                let zero = ComplexRugStrictFinite::<PRECISION>::zero();
                let _ = &one / &zero;
            }

            #[test]
            #[should_panic(expected = "Division failed validation")]
            fn div_assign_by_zero() {
                let mut num = ComplexRugStrictFinite::<PRECISION>::one();
                let zero_ref = &ComplexRugStrictFinite::<PRECISION>::zero();
                num /= zero_ref;
            }
        }
    }

    mod real_scalar_methods {
        use super::*;

        #[test]
        fn epsilon() {
            let eps = RealRugStrictFinite::<PRECISION>::epsilon();
            assert!(eps.is_finite() && eps > RealRugStrictFinite::<PRECISION>::zero());
            let expected_eps_val =
                rug::Float::with_val(PRECISION, 2.0).pow(1i32 - PRECISION as i32);
            let expected_eps = RealRugStrictFinite::<PRECISION>::try_new(expected_eps_val).unwrap();
            assert_eq!(eps, expected_eps, "Epsilon value mismatch");
        }

        #[test]
        fn clamp() {
            let val = RealRugStrictFinite::<PRECISION>::try_from_f64(5.0).unwrap();
            let min_val = RealRugStrictFinite::<PRECISION>::try_from_f64(0.0).unwrap();
            let max_val = RealRugStrictFinite::<PRECISION>::try_from_f64(10.0).unwrap();

            assert_eq!(val.clone().clamp(&min_val, &max_val), val);
            assert_eq!(
                RealRugStrictFinite::<PRECISION>::try_from_f64(-5.0)
                    .unwrap()
                    .clamp(&min_val, &max_val),
                min_val
            );
            assert_eq!(
                RealRugStrictFinite::<PRECISION>::try_from_f64(15.0)
                    .unwrap()
                    .clamp(&min_val, &max_val),
                max_val
            );
        }

        #[test]
        fn hypot() {
            let a = RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap();
            let b = RealRugStrictFinite::<PRECISION>::try_from_f64(4.0).unwrap();
            let expected = RealRugStrictFinite::<PRECISION>::try_from_f64(5.0).unwrap();
            assert_eq!(a.hypot(&b), expected);
        }

        #[test]
        fn signum() {
            assert_eq!(
                RealRugStrictFinite::<PRECISION>::try_from_f64(5.0)
                    .unwrap()
                    .kernel_signum(),
                RealRugStrictFinite::<PRECISION>::one()
            );
            assert_eq!(
                RealRugStrictFinite::<PRECISION>::try_from_f64(-5.0)
                    .unwrap()
                    .kernel_signum(),
                RealRugStrictFinite::<PRECISION>::negative_one()
            );
            // rug::Float::signum of 0.0 is 1.0
            assert_eq!(
                RealRugStrictFinite::<PRECISION>::zero().kernel_signum(),
                RealRugStrictFinite::<PRECISION>::one()
            );
        }

        #[test]
        fn total_cmp() {
            let r1 = RealRugStrictFinite::<PRECISION>::try_from_f64(1.0).unwrap();
            let r2 = RealRugStrictFinite::<PRECISION>::try_from_f64(2.0).unwrap();
            assert_eq!(r1.total_cmp(&r1), Ordering::Equal);
            assert_eq!(r1.total_cmp(&r2), Ordering::Less);
            assert_eq!(r2.total_cmp(&r1), Ordering::Greater);
        }

        #[test]
        fn mul_add_mul_mut() {
            let mut a = RealRugStrictFinite::<PRECISION>::try_from_f64(2.0).unwrap();
            let b = RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap(); // mul
            let c = RealRugStrictFinite::<PRECISION>::try_from_f64(4.0).unwrap(); // add_mul1
            let d = RealRugStrictFinite::<PRECISION>::try_from_f64(5.0).unwrap(); // add_mul2
            // Expected: a = a*b + c*d = 2*3 + 4*5 = 6 + 20 = 26
            a.kernel_mul_add_mul_mut(&b, &c, &d);
            assert_eq!(
                a,
                RealRugStrictFinite::<PRECISION>::try_from_f64(26.0).unwrap()
            );
        }

        #[test]
        fn mul_sub_mul_mut() {
            let mut a = RealRugStrictFinite::<PRECISION>::try_from_f64(10.0).unwrap();
            let b = RealRugStrictFinite::<PRECISION>::try_from_f64(2.0).unwrap(); // mul
            let c = RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap(); // sub_mul1
            let d = RealRugStrictFinite::<PRECISION>::try_from_f64(4.0).unwrap(); // sub_mul2
            // Expected: a = a*b - c*d = 10*2 - 3*4 = 20 - 12 = 8
            a.kernel_mul_sub_mul_mut(&b, &c, &d);
            assert_eq!(
                a,
                RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
            );
        }

        #[test]
        fn test_real_rug_constants() {
            // Helper to create a raw rug::Float for comparison
            let raw_float = |f: f64| Float::with_val(PRECISION, f);

            // PI - should use MPFR Constant::Pi
            let pi = RealRugStrictFinite::<PRECISION>::pi();
            let expected_pi = Float::with_val(PRECISION, MpfrConstant::Pi);
            assert_eq!(pi.as_ref(), &expected_pi);

            // TWO_PI - should be 2 * MPFR Pi
            let two_pi = RealRugStrictFinite::<PRECISION>::two_pi();
            let expected_two_pi = Float::with_val(PRECISION, MpfrConstant::Pi) * 2;
            assert_eq!(two_pi.as_ref(), &expected_two_pi);

            // PI_DIV_2 - should be MPFR Pi / 2
            let pi_div_2 = RealRugStrictFinite::<PRECISION>::pi_div_2();
            let expected_pi_div_2 = Float::with_val(PRECISION, MpfrConstant::Pi) / 2;
            assert_eq!(pi_div_2.as_ref(), &expected_pi_div_2);

            // E - must be calculated as exp(1)
            let e = RealRugStrictFinite::<PRECISION>::e();
            let expected_e = raw_float(1.0).exp();
            assert_eq!(e.as_ref(), &expected_e);

            // LOG2_E - should use 1/MPFR Log2
            let log2_e = RealRugStrictFinite::<PRECISION>::log2_e();
            let expected_log2_e = Float::with_val(PRECISION, MpfrConstant::Log2).recip();
            assert_eq!(log2_e.as_ref(), &expected_log2_e);

            // LOG10_E - should be 1/(MPFR Log2 * log2(10))
            let log10_e = RealRugStrictFinite::<PRECISION>::log10_e();
            let ln2 = Float::with_val(PRECISION, MpfrConstant::Log2);
            let log2_10 = raw_float(10.0).log2();
            let expected_log10_e = (ln2 * log2_10).recip();
            assert_eq!(log10_e.as_ref(), &expected_log10_e);

            // LN_2 - should use MPFR Constant::Log2
            let ln_2 = RealRugStrictFinite::<PRECISION>::ln_2();
            let expected_ln_2 = Float::with_val(PRECISION, MpfrConstant::Log2);
            assert_eq!(ln_2.as_ref(), &expected_ln_2);

            // LN_10 - should be MPFR Log2 * log2(10)
            let ln_10 = RealRugStrictFinite::<PRECISION>::ln_10();
            let ln2 = Float::with_val(PRECISION, MpfrConstant::Log2);
            let log2_10 = raw_float(10.0).log2();
            let expected_ln_10 = ln2 * log2_10;
            assert_eq!(ln_10.as_ref(), &expected_ln_10);

            // LOG2_10
            let log2_10 = RealRugStrictFinite::<PRECISION>::log2_10();
            let expected_log2_10 = raw_float(10.0).log2();
            assert_eq!(log2_10.as_ref(), &expected_log2_10);

            // LOG10_2
            let log10_2 = RealRugStrictFinite::<PRECISION>::log10_2();
            let expected_log10_2 = raw_float(2.0).log10();
            assert_eq!(log10_2.as_ref(), &expected_log10_2);

            // MAX_FINITE
            let max_finite = RealRugStrictFinite::<PRECISION>::max_finite();
            let expected_max_finite = <rug::Float as RawRealTrait>::raw_max_finite(PRECISION);
            assert_eq!(max_finite.as_ref(), &expected_max_finite);

            // MIN_FINITE
            let min_finite = RealRugStrictFinite::<PRECISION>::min_finite();
            let expected_min_finite = <rug::Float as RawRealTrait>::raw_min_finite(PRECISION);
            assert_eq!(min_finite.as_ref(), &expected_min_finite);

            // EPSILON
            let epsilon = RealRugStrictFinite::<PRECISION>::epsilon();
            let expected_epsilon = <rug::Float as RawRealTrait>::raw_epsilon(PRECISION);
            assert_eq!(epsilon.as_ref(), &expected_epsilon);

            // NEGATIVE_ONE
            assert_eq!(
                RealRugStrictFinite::<PRECISION>::negative_one().as_ref(),
                &raw_float(-1.0)
            );

            // TWO
            assert_eq!(
                RealRugStrictFinite::<PRECISION>::two().as_ref(),
                &raw_float(2.0)
            );

            // ONE_DIV_2
            assert_eq!(
                RealRugStrictFinite::<PRECISION>::one_div_2().as_ref(),
                &raw_float(0.5)
            );
        }

        /// Test that MPFR optimized constants are used instead of calculations.
        /// This verifies that pi() uses Constant::Pi (not acos(-1)) and
        /// ln_2() uses Constant::Log2 (not ln(2)).
        #[test]
        fn test_real_rug_constants_mpfr_optimization() {
            // π should use MpfrConstant::Pi (not acos(-1))
            let pi = RealRugStrictFinite::<PRECISION>::pi();
            let expected_pi = Float::with_val(PRECISION, MpfrConstant::Pi);
            assert_eq!(pi.as_ref(), &expected_pi);

            // ln(2) should use MpfrConstant::Log2 (not ln(2) calculation)
            let ln_2 = RealRugStrictFinite::<PRECISION>::ln_2();
            let expected_ln_2 = Float::with_val(PRECISION, MpfrConstant::Log2);
            assert_eq!(ln_2.as_ref(), &expected_ln_2);

            // Derived constants should be computed from MPFR base constants
            let two_pi = RealRugStrictFinite::<PRECISION>::two_pi();
            let expected_two_pi = Float::with_val(PRECISION, MpfrConstant::Pi) * 2;
            assert_eq!(two_pi.as_ref(), &expected_two_pi);

            let pi_div_2 = RealRugStrictFinite::<PRECISION>::pi_div_2();
            let expected_pi_div_2 = Float::with_val(PRECISION, MpfrConstant::Pi) / 2;
            assert_eq!(pi_div_2.as_ref(), &expected_pi_div_2);
        }

        /// Test that constants with different precisions are independent and correct.
        #[test]
        fn test_real_rug_constants_precision_independence() {
            type Real100 = RealRugStrictFinite<100>;
            type Real200 = RealRugStrictFinite<200>;
            type Real500 = RealRugStrictFinite<500>;

            // Get π at different precisions
            let pi_100 = Real100::pi();
            let pi_200 = Real200::pi();
            let pi_500 = Real500::pi();

            // They should all represent π, but with different precisions
            // Convert to f64 for comparison (all should be close to π)
            let pi_100_f64 = pi_100.as_ref().to_f64();
            let pi_200_f64 = pi_200.as_ref().to_f64();
            let pi_500_f64 = pi_500.as_ref().to_f64();

            let pi_expected = std::f64::consts::PI;
            assert!((pi_100_f64 - pi_expected).abs() < 1e-15);
            assert!((pi_200_f64 - pi_expected).abs() < 1e-15);
            assert!((pi_500_f64 - pi_expected).abs() < 1e-15);

            // Test e at different precisions
            let e_100 = Real100::e();
            let e_200 = Real200::e();

            let e_100_f64 = e_100.as_ref().to_f64();
            let e_200_f64 = e_200.as_ref().to_f64();

            let e_expected = std::f64::consts::E;
            assert!((e_100_f64 - e_expected).abs() < 1e-15);
            assert!((e_200_f64 - e_expected).abs() < 1e-15);
        }

        /// Test mathematical correctness of constant relationships.
        #[test]
        fn test_real_rug_constants_mathematical_relationships() {
            let pi = RealRugStrictFinite::<PRECISION>::pi();
            let two_pi = RealRugStrictFinite::<PRECISION>::two_pi();
            let pi_div_2 = RealRugStrictFinite::<PRECISION>::pi_div_2();

            // two_pi = 2 * pi
            let expected_two_pi = pi.as_ref() * Float::with_val(PRECISION, 2);
            assert_eq!(two_pi.as_ref(), &expected_two_pi);

            // pi_div_2 = pi / 2
            let expected_pi_div_2 = pi.as_ref() / Float::with_val(PRECISION, 2);
            assert_eq!(pi_div_2.as_ref(), &expected_pi_div_2);

            let ln_2 = RealRugStrictFinite::<PRECISION>::ln_2();
            let ln_10 = RealRugStrictFinite::<PRECISION>::ln_10();
            let log2_e = RealRugStrictFinite::<PRECISION>::log2_e();
            let log10_e = RealRugStrictFinite::<PRECISION>::log10_e();

            // log2_e * ln_2 should equal 1 (since log₂(e) = 1/ln(2))
            let product = (log2_e.as_ref() * ln_2.as_ref()).complete(PRECISION);
            let one = Float::with_val(PRECISION, 1);
            let epsilon = Float::with_val(PRECISION, 1e-10);
            let diff = (product - &one).abs();
            assert!(diff < epsilon);

            // log10_e * ln_10 should equal 1
            let product = (log10_e.as_ref() * ln_10.as_ref()).complete(PRECISION);
            let diff = (product - &one).abs();
            assert!(diff < epsilon);
        }

        /// Test that multiple calls to the same constant produce identical values.
        /// This verifies computational consistency (MPFR constants are deterministic).
        #[test]
        fn test_real_rug_constants_consistency() {
            // Call each constant twice and verify they produce identical values
            let pi1 = RealRugStrictFinite::<PRECISION>::pi();
            let pi2 = RealRugStrictFinite::<PRECISION>::pi();
            assert_eq!(pi1.as_ref(), pi2.as_ref());

            let e1 = RealRugStrictFinite::<PRECISION>::e();
            let e2 = RealRugStrictFinite::<PRECISION>::e();
            assert_eq!(e1.as_ref(), e2.as_ref());

            let ln2_1 = RealRugStrictFinite::<PRECISION>::ln_2();
            let ln2_2 = RealRugStrictFinite::<PRECISION>::ln_2();
            assert_eq!(ln2_1.as_ref(), ln2_2.as_ref());

            let two_pi1 = RealRugStrictFinite::<PRECISION>::two_pi();
            let two_pi2 = RealRugStrictFinite::<PRECISION>::two_pi();
            assert_eq!(two_pi1.as_ref(), two_pi2.as_ref());

            let pi_div_2_1 = RealRugStrictFinite::<PRECISION>::pi_div_2();
            let pi_div_2_2 = RealRugStrictFinite::<PRECISION>::pi_div_2();
            assert_eq!(pi_div_2_1.as_ref(), pi_div_2_2.as_ref());
        }
    }

    mod scalar_constructors {
        use super::*;
        use crate::{
            functions::{
                ATan2InputErrors, LogarithmRealErrors, LogarithmRealInputErrors,
                PowComplexBaseRealExponentInputErrors, PowRealBaseRealExponentInputErrors,
                ReciprocalInputErrors, SqrtRealInputErrors,
            },
            validation::ErrorsValidationRawReal,
        };

        mod real {
            use super::*;

            #[test]
            fn exp_overflow() {
                let large_val = RealRugStrictFinite::<PRECISION>::try_from_f64(1.0e60).unwrap(); // exp(1.0e60) is very large
                let res_large = large_val.try_exp();
                assert!(matches!(
                    res_large,
                    Err(ExpErrors::Output {
                        source: ErrorsValidationRawReal::IsPosInfinity { .. }
                    })
                ),);
            }

            #[test]
            fn ln_domain_errors() {
                let neg_val = RealRugStrictFinite::<PRECISION>::try_from_f64(-1.0).unwrap();
                assert!(matches!(
                    neg_val.try_ln(),
                    Err(LogarithmRealErrors::Input {
                        source: LogarithmRealInputErrors::NegativeArgument { .. }
                    })
                ));

                let zero_val = RealRugStrictFinite::<PRECISION>::zero();
                assert!(matches!(
                    zero_val.try_ln(),
                    Err(LogarithmRealErrors::Input {
                        source: LogarithmRealInputErrors::ZeroArgument { .. }
                    })
                ));
            }
        } // end mod real

        mod complex {
            use super::*;

            #[test]
            fn log2_zero() {
                let zero_val = ComplexRugStrictFinite::<PRECISION>::zero();
                assert!(matches!(
                    zero_val.try_log2(),
                    Err(LogarithmComplexErrors::Input {
                        source: LogarithmComplexInputErrors::ZeroArgument { .. }
                    })
                ));
            }
        } // end mod complex

        mod pow {
            use super::*;

            mod real_base {
                use super::*;

                #[test]
                fn negative_base_real_exponent_error() {
                    let base = RealRugStrictFinite::<PRECISION>::try_from_f64(-2.0).unwrap();
                    let exponent = RealRugStrictFinite::<PRECISION>::try_from_f64(0.5).unwrap();
                    let res = base.try_pow(&exponent);
                    assert!(matches!(
                        res,
                        Err(PowRealBaseRealExponentErrors::Input {
                            source: PowRealBaseRealExponentInputErrors::NegativeBase { .. }
                        })
                    ));
                }

                #[test]
                fn real_base_uint_exponent() {
                    let base = RealRugStrictFinite::<PRECISION>::try_from_f64(2.0).unwrap();
                    assert_eq!(
                        base.clone().try_pow(3u8).unwrap(),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().try_pow(3u16).unwrap(),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().try_pow(3u32).unwrap(),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().try_pow(3u64).unwrap(),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().try_pow(3u128).unwrap(),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().try_pow(3usize).unwrap(),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );

                    assert_eq!(
                        base.clone().pow(3u8),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().pow(3u16),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().pow(3u32),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().pow(3u64),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().pow(3u128),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().pow(3usize),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                }

                #[test]
                fn real_base_int_exponent() {
                    let base = RealRugStrictFinite::<PRECISION>::try_from_f64(2.0).unwrap();
                    assert_eq!(
                        base.clone().try_pow(3i8).unwrap(),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().try_pow(3i16).unwrap(),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().try_pow(3i32).unwrap(),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().try_pow(3i64).unwrap(),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().try_pow(3i128).unwrap(),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().try_pow(3isize).unwrap(),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );

                    assert_eq!(
                        base.clone().pow(3i8),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().pow(3i16),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().pow(3i32),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().pow(3i64),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().pow(3i128),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                    assert_eq!(
                        base.clone().pow(3isize),
                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
                    );
                }

                #[test]
                fn real_base_int_exponent_zero_neg_exp_error() {
                    let base = RealRugStrictFinite::<PRECISION>::zero();
                    let exponent: i32 = -2;
                    let res = base.try_pow(exponent);
                    assert!(matches!(
                        res,
                        Err(PowIntExponentErrors::Input {
                            source: PowIntExponentInputErrors::ZeroBaseNegativeExponent { .. }
                        })
                    ));
                }
            }

            mod complex_base {
                use super::*;

                #[test]
                fn complex_base_uint_exponent() {
                    let base = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (2.0, 3.0)),
                    )
                    .unwrap();
                    let expected_res = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (-46.0, 9.0)),
                    )
                    .unwrap();

                    assert_eq!(&base.clone().try_pow(3u8).unwrap(), &expected_res);
                    assert_eq!(&base.clone().try_pow(3u16).unwrap(), &expected_res);
                    assert_eq!(&base.clone().try_pow(3u32).unwrap(), &expected_res);
                    assert_eq!(&base.clone().try_pow(3u64).unwrap(), &expected_res);
                    assert_eq!(&base.clone().try_pow(3u128).unwrap(), &expected_res);
                    assert_eq!(&base.clone().try_pow(3usize).unwrap(), &expected_res);

                    assert_eq!(&base.clone().pow(3u8), &expected_res);
                    assert_eq!(&base.clone().pow(3u16), &expected_res);
                    assert_eq!(&base.clone().pow(3u32), &expected_res);
                    assert_eq!(&base.clone().pow(3u64), &expected_res);
                    assert_eq!(&base.clone().pow(3u128), &expected_res);
                    assert_eq!(&base.clone().pow(3usize), &expected_res);
                }

                #[test]
                fn complex_base_int_exponent() {
                    let base = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (2.0, 3.0)),
                    )
                    .unwrap();
                    let expected_res = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (-46.0, 9.0)),
                    )
                    .unwrap();

                    assert_eq!(&base.clone().try_pow(3i8).unwrap(), &expected_res);
                    assert_eq!(&base.clone().try_pow(3i16).unwrap(), &expected_res);
                    assert_eq!(&base.clone().try_pow(3i32).unwrap(), &expected_res);
                    assert_eq!(&base.clone().try_pow(3i64).unwrap(), &expected_res);
                    assert_eq!(&base.clone().try_pow(3i128).unwrap(), &expected_res);
                    assert_eq!(&base.clone().try_pow(3isize).unwrap(), &expected_res);

                    assert_eq!(&base.clone().pow(3i8), &expected_res);
                    assert_eq!(&base.clone().pow(3i16), &expected_res);
                    assert_eq!(&base.clone().pow(3i32), &expected_res);
                    assert_eq!(&base.clone().pow(3i64), &expected_res);
                    assert_eq!(&base.clone().pow(3i128), &expected_res);
                    assert_eq!(&base.clone().pow(3isize), &expected_res);
                }

                #[test]
                fn complex_zero_base_negative_real_exponent_error() {
                    let base = ComplexRugStrictFinite::<PRECISION>::zero();
                    let exponent = RealRugStrictFinite::<PRECISION>::try_from_f64(-2.0).unwrap();
                    let res = base.try_pow(&exponent);
                    assert!(matches!(
                        res,
                        Err(PowComplexBaseRealExponentErrors::Input {
                            source:
                                PowComplexBaseRealExponentInputErrors::ZeroBaseNegativeExponent { .. }
                        })
                    ));
                }

                #[test]
                fn complex_zero_base_zero_real_exponent() {
                    let base = ComplexRugStrictFinite::<PRECISION>::zero();
                    let exponent = RealRugStrictFinite::<PRECISION>::zero();
                    let res = base.try_pow(&exponent).unwrap();
                    assert_eq!(res, ComplexRugStrictFinite::<PRECISION>::one());
                }

                #[test]
                fn complex_base_int_exponent_zero_neg_exp_error() {
                    let base = ComplexRugStrictFinite::<PRECISION>::zero();
                    let exponent: i32 = -2;
                    let res = base.try_pow(exponent);
                    assert!(matches!(
                        res,
                        Err(PowIntExponentErrors::Input {
                            source: PowIntExponentInputErrors::ZeroBaseNegativeExponent { .. }
                        })
                    ));
                }
            }
        }

        #[test]
        fn reciprocal_real_rug_zero() {
            let zero_val = RealRugStrictFinite::<PRECISION>::zero();
            let res = zero_val.try_reciprocal();
            assert!(matches!(
                res,
                Err(ReciprocalErrors::Input {
                    source: ReciprocalInputErrors::DivisionByZero { .. }
                })
            ));
        }

        #[test]
        fn reciprocal_complex_rug_zero() {
            let zero_val = ComplexRugStrictFinite::<PRECISION>::zero();
            let res = zero_val.try_reciprocal();
            assert!(matches!(
                res,
                Err(ReciprocalErrors::Input {
                    source: ReciprocalInputErrors::DivisionByZero { .. }
                })
            ));
        }

        #[test]
        fn sqrt_real_rug_negative_input() {
            let neg_val = RealRugStrictFinite::<PRECISION>::try_from_f64(-4.0).unwrap();
            let res = neg_val.try_sqrt();
            assert!(matches!(
                res,
                Err(SqrtRealErrors::Input {
                    source: SqrtRealInputErrors::NegativeValue { .. }
                })
            ));
        }

        mod trigonometric {
            use super::*;

            #[test]
            fn atan2_real_rug_zero_over_zero() {
                let zero_val = RealRugStrictFinite::<PRECISION>::zero();
                let res = zero_val.try_atan2(&RealRugStrictFinite::<PRECISION>::zero());
                assert!(matches!(
                    res,
                    Err(ATan2Errors::Input {
                        source: ATan2InputErrors::ZeroOverZero { .. }
                    })
                ));
            }

            /*
            #[test]
            #[ignore = "at the moment we cannot create a pole for the Tan function"]
            fn tan_real_rug_pole() {
                // tan(PI/2) is a pole
                let pi_half = RealRugStrictFinite::<PRECISION>::pi_div_2();
                let res = pi_half.try_tan();
                println!("Result: {:?}", res);
                assert!(matches!(
                    res,
                    Err(TanRealErrors::Input {
                        source: TanRealInputErrors::ArgumentIsPole { .. }
                    })
                ));
            }
            */

            #[test]
            fn asin_real_rug_out_of_domain() {
                let val_gt_1 = RealRugStrictFinite::<PRECISION>::try_from_f64(1.5).unwrap();
                assert!(matches!(
                    val_gt_1.try_asin(),
                    Err(ASinRealErrors::Input {
                        source: ASinRealInputErrors::OutOfDomain { .. }
                    })
                ));
                let val_lt_neg1 = RealRugStrictFinite::<PRECISION>::try_from_f64(-1.5).unwrap();
                assert!(matches!(
                    val_lt_neg1.try_asin(),
                    Err(ASinRealErrors::Input {
                        source: ASinRealInputErrors::OutOfDomain { .. }
                    })
                ));
            }

            #[test]
            fn acos_real_rug_out_of_domain() {
                let val_gt_1 = RealRugStrictFinite::<PRECISION>::try_from_f64(1.5).unwrap();
                assert!(matches!(
                    val_gt_1.try_acos(),
                    Err(ACosRealErrors::Input {
                        source: ACosRealInputErrors::OutOfDomain { .. }
                    })
                ));
                let val_lt_neg1 = RealRugStrictFinite::<PRECISION>::try_from_f64(-1.5).unwrap();
                assert!(matches!(
                    val_lt_neg1.try_acos(),
                    Err(ACosRealErrors::Input {
                        source: ACosRealInputErrors::OutOfDomain { .. }
                    })
                ));
            }

            #[test]
            fn atan_complex_rug_pole() {
                // atan(i) and atan(-i) are poles
                let i_val = ComplexRugStrictFinite::<PRECISION>::try_new_pure_imaginary(
                    Float::with_val(PRECISION, 1.0),
                )
                .unwrap();
                assert!(matches!(
                    i_val.try_atan(),
                    Err(ATanComplexErrors::Input {
                        source: ATanComplexInputErrors::ArgumentIsPole { .. }
                    })
                ));

                let neg_i_val = ComplexRugStrictFinite::<PRECISION>::try_new_pure_imaginary(
                    Float::with_val(PRECISION, -1.0),
                )
                .unwrap();
                assert!(matches!(
                    neg_i_val.try_atan(),
                    Err(ATanComplexErrors::Input {
                        source: ATanComplexInputErrors::ArgumentIsPole { .. }
                    })
                ));
            }
        } // endmod trigonometric

        mod hyperbolic {
            use super::*;

            mod real {
                use super::*;

                #[test]
                fn atanh_real_rug_out_of_domain() {
                    let val_ge_1 = RealRugStrictFinite::<PRECISION>::one(); // atanh(1) is Inf
                    assert!(matches!(
                        val_ge_1.try_atanh(),
                        Err(ATanHErrors::Input {
                            source: ATanHInputErrors::OutOfDomain { .. }
                        })
                    ));

                    let val_le_neg1 = RealRugStrictFinite::<PRECISION>::negative_one(); // atanh(-1) is -Inf
                    assert!(matches!(
                        val_le_neg1.try_atanh(),
                        Err(ATanHErrors::Input {
                            source: ATanHInputErrors::OutOfDomain { .. }
                        })
                    ));
                }

                #[test]
                fn acosh_real_rug_out_of_domain() {
                    let val_lt_1 = RealRugStrictFinite::<PRECISION>::try_from_f64(0.5).unwrap();
                    assert!(matches!(
                        val_lt_1.try_acosh(),
                        Err(ACosHErrors::Input {
                            source: ACosHInputErrors::OutOfDomain { .. }
                        })
                    ));
                }
            }

            mod complex {
                use super::*;

                /*
                #[test]
                #[ignore = "at the moment we cannot create a pole for the ATanH function"]
                fn tanh_complex_rug_pole() {
                    // tanh(z) has poles where cosh(z) = 0. e.g. z = i * (PI/2 + k*PI)
                    let pi_half = RealRugStrictFinite::<PRECISION>::pi_div_2().into_inner();
                    let pole_val =
                        ComplexRugStrictFinite::<PRECISION>::try_new_pure_imaginary(pi_half).unwrap();
                    println!("Atanh(Pole value): {:?}", pole_val.clone().try_tanh());
                    assert!(matches!(
                        pole_val.try_tanh(),
                        Err(TanHComplexErrors::Input {
                            source: TanHComplexInputErrors::OutOfDomain { .. }
                        })
                    ));
                }
                */

                #[test]
                fn acosh_out_of_domain() {
                    // acosh(z) domain is C \ (-inf, 1) on real axis
                    let val_on_branch_cut = ComplexRugStrictFinite::<PRECISION>::try_new_pure_real(
                        Float::with_val(PRECISION, 0.5),
                    )
                    .unwrap();
                    assert!(matches!(
                        val_on_branch_cut.try_acosh(),
                        Err(ACosHErrors::Input {
                            source: ACosHInputErrors::OutOfDomain { .. }
                        })
                    ));

                    let val_on_branch_cut_neg =
                        ComplexRugStrictFinite::<PRECISION>::try_new_pure_real(Float::with_val(
                            PRECISION, -5.0,
                        ))
                        .unwrap();
                    assert!(matches!(
                        val_on_branch_cut_neg.try_acosh(),
                        Err(ACosHErrors::Input {
                            source: ACosHInputErrors::OutOfDomain { .. }
                        })
                    ));
                }

                #[test]
                fn atanh_out_of_domain() {
                    let val_ge_1 = ComplexRugStrictFinite::<PRECISION>::try_new_pure_real(
                        Float::with_val(PRECISION, 1.0),
                    )
                    .unwrap();
                    assert!(matches!(
                        val_ge_1.try_atanh(),
                        Err(ATanHErrors::Input {
                            source: ATanHInputErrors::OutOfDomain { .. }
                        })
                    ));

                    let val_le_neg1 = ComplexRugStrictFinite::<PRECISION>::try_new_pure_real(
                        Float::with_val(PRECISION, -1.0),
                    )
                    .unwrap();
                    assert!(matches!(
                        val_le_neg1.try_atanh(),
                        Err(ATanHErrors::Input {
                            source: ATanHInputErrors::OutOfDomain { .. }
                        })
                    ));
                }
            }

            /*
            #[test]
            #[ignore = "at the moment we cannot create a pole for the TanH function"]
            fn tanh_out_of_domain() {
                // tanh(z) has poles where cosh(z) = 0. e.g. z = i * (PI/2 + k*PI)
                let pi_half = RealRugStrictFinite::<PRECISION>::pi_div_2().into_inner();
                let pole_val = ComplexRugStrictFinite::<PRECISION>::try_new_pure_imaginary(pi_half).unwrap();
                println!("TanH(Pole value): {:?}", pole_val.clone().try_tanh());
                assert!(matches!(
                    pole_val.try_tanh(),
                    Err(TanHComplexErrors::Input {
                        source: TanHComplexInputErrors::OutOfDomain { .. }
                    })
                ));
            }
            */
        } // end mod hyperbolic
    }

    mod summation {
        use super::*;

        const PRECISION: u32 = 53;

        type RealValidated = RealRugStrictFinite<PRECISION>;
        type ComplexValidated = ComplexRugStrictFinite<PRECISION>;

        #[test]
        fn sum_real() {
            let values = vec![
                RealValidated::try_from_f64(1.0).unwrap(),
                RealValidated::try_from_f64(2.0).unwrap(),
                RealValidated::try_from_f64(3.0).unwrap(),
                RealValidated::try_from_f64(4.0).unwrap(),
                RealValidated::try_from_f64(5.0).unwrap(),
            ];
            let sum: RealValidated = values.into_iter().sum();
            assert_eq!(sum, RealValidated::try_from_f64(15.0).unwrap());
        }

        #[test]
        fn sum_real_compensated() {
            // Test case where simple summation might lose precision
            let values = vec![
                RealValidated::try_from_f64(1.0e100).unwrap(),
                RealValidated::try_from_f64(1.0).unwrap(),
                RealValidated::try_from_f64(-1.0e100).unwrap(),
            ];
            let sum: RealValidated = values.into_iter().sum();
            // The Neumaier sum should correctly result in 1.0
            assert_eq!(sum, RealValidated::try_from_f64(1.0).unwrap());
        }

        #[test]
        fn sum_complex() {
            let values = vec![
                ComplexValidated::try_new_complex(
                    rug::Float::with_val(PRECISION, 1.),
                    rug::Float::with_val(PRECISION, 2.),
                )
                .unwrap(),
                ComplexValidated::try_new_complex(
                    rug::Float::with_val(PRECISION, 3.),
                    rug::Float::with_val(PRECISION, 4.),
                )
                .unwrap(),
                ComplexValidated::try_new_complex(
                    rug::Float::with_val(PRECISION, 5.),
                    rug::Float::with_val(PRECISION, 6.),
                )
                .unwrap(),
            ];
            let sum: ComplexValidated = values.into_iter().sum();
            assert_eq!(
                sum,
                ComplexValidated::try_new_complex(
                    rug::Float::with_val(PRECISION, 9.),
                    rug::Float::with_val(PRECISION, 12.)
                )
                .unwrap()
            );
        }

        #[test]
        fn sum_complex_compensated() {
            let values = [
                ComplexValidated::try_new_complex(
                    rug::Float::with_val(PRECISION, 1.0e100),
                    rug::Float::with_val(PRECISION, -1.0e100),
                )
                .unwrap(),
                ComplexValidated::try_new_complex(
                    rug::Float::with_val(PRECISION, 1.),
                    rug::Float::with_val(PRECISION, 2.),
                )
                .unwrap(),
                ComplexValidated::try_new_complex(
                    rug::Float::with_val(PRECISION, -1.0e100),
                    rug::Float::with_val(PRECISION, 1.0e100),
                )
                .unwrap(),
            ];
            let sum: ComplexValidated = values.iter().cloned().sum();
            assert_eq!(
                sum,
                ComplexValidated::try_new_complex(
                    rug::Float::with_val(PRECISION, 1.),
                    rug::Float::with_val(PRECISION, 2.)
                )
                .unwrap()
            );
        }
    } // end mod summation

    mod random {
        use super::*;
        use crate::{RandomSampleFromF64, new_random_vec};
        use rand::{Rng, SeedableRng, distr::Uniform, rngs::StdRng};

        const PRECISION: u32 = 53;

        type RealValidated = RealRugStrictFinite<PRECISION>;
        type ComplexValidated = ComplexRugStrictFinite<PRECISION>;

        /// Tests the random generation of a `RealValidated` value.
        /// It uses a seeded RNG to ensure deterministic results and checks
        /// if the generated value falls within the expected [0, 1) range.
        #[test]
        fn test_random_real_validated() {
            let seed = [42; 32];
            let mut rng = StdRng::from_seed(seed);

            let random_real: RealValidated = rng.random();

            // rng.random::<f64>() produces a value in [0, 1), so the converted value should be in the same range.
            assert_eq!(random_real, 0.23713468825474326);

            // Check for determinism
            let mut rng2 = StdRng::from_seed(seed);
            let random_real2: RealValidated = rng2.random();
            assert_eq!(random_real, random_real2);
        }

        /// Tests the random generation of a `ComplexValidated` value.
        /// It uses a seeded RNG for determinism and verifies that both the real
        /// and imaginary parts of the generated complex number are within the
        /// expected [0, 1) range.
        #[test]
        fn test_random_complex_validated() {
            let seed = [99; 32];
            let mut rng = StdRng::from_seed(seed);

            let random_complex: ComplexValidated = rng.random();

            // The real and imaginary parts are generated independently,
            // so both should be in the [0, 1) range.
            let real_part = random_complex.real_part();
            let imag_part = random_complex.imag_part();

            assert_eq!(real_part, 0.9995546882627792);
            assert_eq!(imag_part, 0.08932180682540247);

            // Check for determinism
            let mut rng2 = StdRng::from_seed(seed);
            let random_complex2: ComplexValidated = rng2.random();
            assert_eq!(random_complex, random_complex2);
        }

        const SEED: [u8; 32] = [42; 32];

        #[test]
        fn test_sample_real_validated() {
            let mut rng = StdRng::from_seed(SEED);
            let dist = Uniform::new(-10.0, 10.0).unwrap();

            let val = RealValidated::sample_from(&dist, &mut rng);
            assert_eq!(val, -5.257306234905137);

            // Check determinism
            let mut rng2 = StdRng::from_seed(SEED);
            let val2 = RealValidated::sample_from(&dist, &mut rng2);
            assert_eq!(val, val2);
        }

        #[test]
        fn test_sample_complex_validated() {
            let mut rng = StdRng::from_seed(SEED);
            let dist = Uniform::new(-10.0, 10.0).unwrap();

            let val = ComplexValidated::sample_from(&dist, &mut rng);
            assert_eq!(val.real_part(), -5.257306234905137);
            assert_eq!(val.imag_part(), 7.212119776268775);

            // Check determinism
            let mut rng2 = StdRng::from_seed(SEED);
            let val2 = ComplexValidated::sample_from(&dist, &mut rng2);
            assert_eq!(val, val2);
        }

        #[test]
        fn new_random_vec_real() {
            let mut rng = StdRng::from_seed(SEED);
            let dist = Uniform::new(-10.0, 10.0).unwrap();
            let vec: Vec<RealValidated> = new_random_vec(3, &dist, &mut rng);
            assert_eq!(vec.len(), 3);
            assert_eq!(vec[0], -5.257306234905137);
            assert_eq!(vec[1], 7.212119776268775);
            assert_eq!(vec[2], -4.666248990558111);

            // Check determinism
            let mut rng2 = StdRng::from_seed(SEED);
            let vec2: Vec<RealValidated> = new_random_vec(3, &dist, &mut rng2);
            assert_eq!(vec, vec2);
        }

        #[test]
        fn new_random_vec_complex() {
            let mut rng = StdRng::from_seed(SEED);
            let dist = Uniform::new(-10.0, 10.0).unwrap();
            let vec: Vec<ComplexValidated> = new_random_vec(3, &dist, &mut rng);
            assert_eq!(vec.len(), 3);
            assert_eq!(vec[0].real_part(), -5.257306234905137);
            assert_eq!(vec[0].imag_part(), 7.212119776268775);
            assert_eq!(vec[1].real_part(), -4.666248990558111);
            assert_eq!(vec[1].imag_part(), 9.66047141517383);
            assert_eq!(vec[2].real_part(), -9.04279551029691);
            assert_eq!(vec[2].imag_part(), -1.026624649331671);

            // Check determinism
            let mut rng2 = StdRng::from_seed(SEED);
            let vec2: Vec<ComplexValidated> = new_random_vec(3, &dist, &mut rng2);
            assert_eq!(vec, vec2);
        }
    }

    mod hash_map_key_usage {
        use crate::kernels::rug::RealRugStrictFinite;
        use rug::Float;
        use std::collections::HashMap;
        use try_create::TryNew;

        const PRECISION: u32 = 128;
        type RealRugValidated = RealRugStrictFinite<PRECISION>;

        #[test]
        fn test_rug_as_hashmap_key() {
            let mut map = HashMap::new();
            let key1 = RealRugValidated::try_new(Float::with_val(PRECISION, 1.0)).unwrap();
            let key2 = RealRugValidated::try_new(Float::with_val(PRECISION, 2.5)).unwrap();

            map.insert(key1.clone(), "one_rug");
            map.insert(key2.clone(), "two_point_five_rug");

            assert_eq!(map.get(&key1), Some(&"one_rug"));
            assert_eq!(map.len(), 2);

            // Overwrite an existing key
            let old_value = map.insert(key1.clone(), "new_one_rug");
            assert_eq!(old_value, Some("one_rug"));
            assert_eq!(map.get(&key1), Some(&"new_one_rug"));
        }

        #[test]
        fn test_hash_signed_zero() {
            use crate::functions::Sign;
            use std::collections::hash_map::DefaultHasher;
            use std::hash::{Hash, Hasher};

            let val1 = RealRugValidated::try_new(Float::with_val(PRECISION, 0.0)).unwrap();
            assert!(val1.kernel_is_sign_positive());
            let val2 = RealRugValidated::try_new(Float::with_val(PRECISION, -0.0)).unwrap();
            assert!(val2.kernel_is_sign_negative());

            // Equal values should have equal hashes
            let mut hasher1 = DefaultHasher::new();
            let mut hasher2 = DefaultHasher::new();

            val1.hash(&mut hasher1);
            val2.hash(&mut hasher2);

            assert_eq!(hasher1.finish(), hasher2.finish());
            assert_eq!(val1, val2); // Verify they're actually equal
        }

        #[test]
        fn test_complex_as_hashmap_key() {
            use crate::ComplexRugStrictFinite;
            type ComplexRugValidated = ComplexRugStrictFinite<PRECISION>;

            let mut map = HashMap::new();
            let key1 = ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (1.0, 2.0)))
                .unwrap();
            let key2 = ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (3.0, 4.0)))
                .unwrap();

            map.insert(key1.clone(), "one_plus_two_i_rug");
            map.insert(key2.clone(), "three_plus_four_i_rug");

            assert_eq!(map.get(&key1), Some(&"one_plus_two_i_rug"));
            assert_eq!(map.len(), 2);

            // Overwrite an existing key
            let old_value = map.insert(key1.clone(), "updated_complex_rug");
            assert_eq!(old_value, Some("one_plus_two_i_rug"));
            assert_eq!(map.get(&key1), Some(&"updated_complex_rug"));
        }

        #[test]
        fn test_complex_hash_consistency() {
            use crate::ComplexRugStrictFinite;
            use std::collections::hash_map::DefaultHasher;
            use std::hash::{Hash, Hasher};
            type ComplexRugValidated = ComplexRugStrictFinite<PRECISION>;

            let val1 =
                ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (1.234, 5.678)))
                    .unwrap();
            let val2 =
                ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (1.234, 5.678)))
                    .unwrap();

            // Equal values should have equal hashes
            let mut hasher1 = DefaultHasher::new();
            let mut hasher2 = DefaultHasher::new();

            val1.hash(&mut hasher1);
            val2.hash(&mut hasher2);

            assert_eq!(hasher1.finish(), hasher2.finish());
            assert_eq!(val1, val2);
        }

        #[test]
        fn test_complex_hash_signed_zero() {
            use crate::ComplexRugStrictFinite;
            use std::collections::hash_map::DefaultHasher;
            use std::hash::{Hash, Hasher};
            type ComplexRugValidated = ComplexRugStrictFinite<PRECISION>;

            // Test all combinations of signed zeros
            let val1 = ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (0.0, 0.0)))
                .unwrap();
            let val2 = ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (-0.0, 0.0)))
                .unwrap();
            let val3 = ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (0.0, -0.0)))
                .unwrap();
            let val4 =
                ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (-0.0, -0.0)))
                    .unwrap();

            // All should be equal
            assert_eq!(val1, val2);
            assert_eq!(val1, val3);
            assert_eq!(val1, val4);

            // All should have the same hash
            let mut hasher1 = DefaultHasher::new();
            let mut hasher2 = DefaultHasher::new();
            let mut hasher3 = DefaultHasher::new();
            let mut hasher4 = DefaultHasher::new();

            val1.hash(&mut hasher1);
            val2.hash(&mut hasher2);
            val3.hash(&mut hasher3);
            val4.hash(&mut hasher4);

            let hash1 = hasher1.finish();
            let hash2 = hasher2.finish();
            let hash3 = hasher3.finish();
            let hash4 = hasher4.finish();

            assert_eq!(hash1, hash2);
            assert_eq!(hash1, hash3);
            assert_eq!(hash1, hash4);
        }

        #[test]
        fn test_complex_hashset_operations() {
            use crate::ComplexRugStrictFinite;
            use std::collections::HashSet;
            type ComplexRugValidated = ComplexRugStrictFinite<PRECISION>;

            let mut set = HashSet::new();

            let val1 = ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (1.0, 2.0)))
                .unwrap();
            let val2 = ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (3.0, 4.0)))
                .unwrap();
            let val1_duplicate =
                ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (1.0, 2.0)))
                    .unwrap();

            assert!(set.insert(val1.clone()));
            assert!(set.insert(val2.clone()));
            assert!(!set.insert(val1_duplicate)); // Should return false (already exists)

            assert_eq!(set.len(), 2);
            assert!(set.contains(&val1));
        }

        #[test]
        fn test_complex_different_precision_different_hash() {
            use crate::ComplexRugStrictFinite;
            use std::collections::hash_map::DefaultHasher;
            use std::hash::{Hash, Hasher};

            const PRECISION_A: u32 = 64;
            const PRECISION_B: u32 = 128;

            let val1 = ComplexRugStrictFinite::<PRECISION_A>::try_new(rug::Complex::with_val(
                PRECISION_A,
                (1.0, 2.0),
            ))
            .unwrap();
            let val2 = ComplexRugStrictFinite::<PRECISION_B>::try_new(rug::Complex::with_val(
                PRECISION_B,
                (1.0, 2.0),
            ))
            .unwrap();

            // Values with different precision should have different hashes
            let mut hasher1 = DefaultHasher::new();
            let mut hasher2 = DefaultHasher::new();

            val1.hash(&mut hasher1);
            val2.hash(&mut hasher2);

            let hash1 = hasher1.finish();
            let hash2 = hasher2.finish();

            // Different precisions should produce different hashes
            assert_ne!(hash1, hash2);
        }
    }

    mod rug_float {

        mod from_f64 {
            use crate::{RealRugStrictFinite, RealScalar};

            const PRECISION: u32 = 100;

            #[test]
            fn test_from_f64_valid_constants() {
                // Test with mathematical constants (known valid values)
                let pi = RealRugStrictFinite::<PRECISION>::from_f64(std::f64::consts::PI);
                let pi_f64 = pi.as_ref().to_f64();
                assert!((pi_f64 - std::f64::consts::PI).abs() < 1e-15);

                let e = RealRugStrictFinite::<PRECISION>::from_f64(std::f64::consts::E);
                let e_f64 = e.as_ref().to_f64();
                assert!((e_f64 - std::f64::consts::E).abs() < 1e-15);
            }

            #[test]
            fn test_from_f64_valid_simple_values() {
                // Test with simple values that are exactly representable
                let x = RealRugStrictFinite::<PRECISION>::from_f64(42.0);
                assert_eq!(x.as_ref().to_f64(), 42.0);

                let y = RealRugStrictFinite::<PRECISION>::from_f64(-3.0);
                assert_eq!(y.as_ref().to_f64(), -3.0);

                let z = RealRugStrictFinite::<PRECISION>::from_f64(0.0);
                assert_eq!(z.as_ref().to_f64(), 0.0);
            }

            #[test]
            #[should_panic(expected = "RealScalar::from_f64() failed")]
            fn test_from_f64_nan_panics() {
                let _ = RealRugStrictFinite::<PRECISION>::from_f64(f64::NAN);
            }

            #[test]
            #[should_panic(expected = "RealScalar::from_f64() failed")]
            fn test_from_f64_infinity_panics() {
                let _ = RealRugStrictFinite::<PRECISION>::from_f64(f64::INFINITY);
            }

            #[test]
            #[should_panic(expected = "RealScalar::from_f64() failed")]
            fn test_from_f64_neg_infinity_panics() {
                let _ = RealRugStrictFinite::<PRECISION>::from_f64(f64::NEG_INFINITY);
            }

            #[test]
            fn test_try_from_f64_exact_representation() {
                // Values like 0.1 are not exactly representable in binary
                // but rug accepts them and represents them at the given precision
                let result = RealRugStrictFinite::<PRECISION>::try_from_f64(0.1);
                assert!(result.is_ok());

                // Valid simple values
                assert!(RealRugStrictFinite::<PRECISION>::try_from_f64(2.5).is_ok());
                assert!(RealRugStrictFinite::<PRECISION>::try_from_f64(0.0).is_ok());

                // Invalid values
                assert!(RealRugStrictFinite::<PRECISION>::try_from_f64(f64::NAN).is_err());
                assert!(RealRugStrictFinite::<PRECISION>::try_from_f64(f64::INFINITY).is_err());
            }
        }

        mod truncate_to_usize {
            use crate::kernels::RawRealTrait;
            use crate::validation::ErrorsRawRealToInteger;
            use rug::Float;

            const PRECISION: u32 = 128;

            #[test]
            fn test_rug_truncate_to_usize_valid() {
                assert_eq!(
                    Float::with_val(PRECISION, 42.0)
                        .truncate_to_usize()
                        .unwrap(),
                    42
                );
                assert_eq!(
                    Float::with_val(PRECISION, 42.9)
                        .truncate_to_usize()
                        .unwrap(),
                    42
                );
                assert_eq!(
                    Float::with_val(PRECISION, 0.0).truncate_to_usize().unwrap(),
                    0
                );
                assert_eq!(
                    Float::with_val(PRECISION, usize::MAX)
                        .truncate_to_usize()
                        .unwrap(),
                    usize::MAX
                );
            }

            #[test]
            fn test_rug_truncate_to_usize_not_finite() {
                assert!(matches!(
                    Float::with_val(PRECISION, f64::NAN).truncate_to_usize(),
                    Err(ErrorsRawRealToInteger::NotFinite { .. })
                ));
                assert!(matches!(
                    Float::with_val(PRECISION, f64::INFINITY).truncate_to_usize(),
                    Err(ErrorsRawRealToInteger::NotFinite { .. })
                ));
                assert!(matches!(
                    Float::with_val(PRECISION, f64::NEG_INFINITY).truncate_to_usize(),
                    Err(ErrorsRawRealToInteger::NotFinite { .. })
                ));
            }

            #[test]
            fn test_rug_truncate_to_usize_out_of_range() {
                // Negative value
                assert!(matches!(
                    Float::with_val(PRECISION, -1.0).truncate_to_usize(),
                    Err(ErrorsRawRealToInteger::OutOfRange { .. })
                ));

                // Value too large
                let mut too_large = Float::with_val(PRECISION, usize::MAX);
                too_large += 1;
                assert!(matches!(
                    too_large.truncate_to_usize(),
                    Err(ErrorsRawRealToInteger::OutOfRange { .. })
                ));
            }
        }
    }

    mod truncate_to_usize {
        use super::*;
        use crate::kernels::rug::RealRugStrictFinite;
        use rug::Float;
        use try_create::TryNew;

        const PRECISION: u32 = 1000;

        #[test]
        fn test_positive_integers() {
            let value = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 42.0))
                .unwrap();
            assert_eq!(value.truncate_to_usize().unwrap(), 42);

            let value =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1.0)).unwrap();
            assert_eq!(value.truncate_to_usize().unwrap(), 1);
        }

        #[test]
        fn test_positive_fractionals_truncate() {
            let value = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 42.9))
                .unwrap();
            assert_eq!(value.truncate_to_usize().unwrap(), 42);

            let value =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.9)).unwrap();
            assert_eq!(value.truncate_to_usize().unwrap(), 0);
        }

        #[test]
        fn test_zero_cases() {
            let value =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.0)).unwrap();
            assert_eq!(value.truncate_to_usize().unwrap(), 0);
        }

        #[test]
        fn test_negative_values_error() {
            let value = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -1.0))
                .unwrap();
            let result = value.truncate_to_usize();
            assert!(matches!(
                result,
                Err(ErrorsRawRealToInteger::OutOfRange { .. })
            ));

            let value =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -10.5))
                    .unwrap();
            let result = value.truncate_to_usize();
            assert!(matches!(
                result,
                Err(ErrorsRawRealToInteger::OutOfRange { .. })
            ));
        }

        #[test]
        fn test_large_values() {
            // Test with large but valid values
            let value =
                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1_000_000.0))
                    .unwrap();
            assert_eq!(value.truncate_to_usize().unwrap(), 1_000_000);

            // Test with values too large for usize
            let value = RealRugStrictFinite::<PRECISION>::try_new(
                Float::with_val(PRECISION, 1e50), // Much larger than any usize
            )
            .unwrap();
            let result = value.truncate_to_usize();
            assert!(matches!(
                result,
                Err(ErrorsRawRealToInteger::OutOfRange { .. })
            ));
        }

        #[test]
        fn test_high_precision_truncation() {
            // Test truncation with high precision values
            let value = RealRugStrictFinite::<PRECISION>::try_new(
                Float::parse("42.99999999999999999999999999999")
                    .unwrap()
                    .complete(PRECISION),
            )
            .unwrap();
            assert_eq!(value.truncate_to_usize().unwrap(), 42);

            // Very small positive value should truncate to 0
            let value = RealRugStrictFinite::<PRECISION>::try_new(
                Float::parse("0.99999999999999999999999999999")
                    .unwrap()
                    .complete(PRECISION),
            )
            .unwrap();
            assert_eq!(value.truncate_to_usize().unwrap(), 0);
        }

        #[test]
        fn test_conversion_from_f64() {
            // Test truncation after conversion from f64
            let value = RealRugStrictFinite::<PRECISION>::try_from_f64(42.7).unwrap();
            assert_eq!(value.truncate_to_usize().unwrap(), 42);

            let value = RealRugStrictFinite::<PRECISION>::try_from_f64(0.5).unwrap();
            assert_eq!(value.truncate_to_usize().unwrap(), 0);
        }
    }
}
