#![deny(rustdoc::broken_intra_doc_links)]

use crate::{
    functions::FunctionErrors,
    kernels::{RawComplexTrait, RawRealTrait, RawScalarTrait},
    validation::{StrictFinitePolicy, capture_backtrace},
};
use duplicate::duplicate_item;
use num::{Complex, Zero};
use num_traits::int::PrimInt;
use std::backtrace::Backtrace;
use thiserror::Error;
use try_create::ValidationPolicy;

//------------------------------------------------------------------
/// A marker trait for types that can be used as integer exponents.
pub trait IntegerExponent: PrimInt {}

impl IntegerExponent for i8 {}
impl IntegerExponent for u8 {}
impl IntegerExponent for i16 {}
impl IntegerExponent for u16 {}
impl IntegerExponent for i32 {}
impl IntegerExponent for u32 {}
impl IntegerExponent for i64 {}
impl IntegerExponent for u64 {}
impl IntegerExponent for i128 {}
impl IntegerExponent for u128 {}
impl IntegerExponent for isize {}
impl IntegerExponent for usize {}
//------------------------------------------------------------------

//------------------------------------------------------------------
/// Errors that can occur during input validation when computing `base^exponent`
/// where both the base and exponent are real numbers.
///
/// This enum is typically used as the `InputErrorSource` for a [`FunctionErrors`]
/// wrapper, such as [`PowRealBaseRealExponentErrors`]. It is generic over
/// `RawReal`, which must implement [`RawRealTrait`].
#[derive(Debug, Error)]
pub enum PowRealBaseRealExponentInputErrors<RawReal: RawRealTrait> {
    /// The base is negative, and the exponent is a non-integer real number.
    /// This operation is undefined in the real domain and typically results
    /// in a complex number or NaN.
    #[error("detected a negative base ({base}) with a real exponent ({exponent}) !")]
    NegativeBase {
        /// The negative base value.
        base: RawReal,

        /// The exponent value.
        exponent: RawReal,

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

    /// The input base value failed validation according to the active policy.
    ///
    /// For example, using [`StrictFinitePolicy`],
    /// this would trigger if the base is NaN or Infinity.
    #[error("the base is invalid!")]
    InvalidBase {
        /// The underlying validation error for the base.
        /// The type of this field is `<RawReal as RawScalarTrait>::ValidationErrors`.
        #[source]
        #[backtrace]
        source: <RawReal as RawScalarTrait>::ValidationErrors,
    },

    /// The input exponent value failed validation according to the active policy.
    ///
    /// For example, using [`StrictFinitePolicy`],
    /// this would trigger if the exponent is NaN or Infinity.
    #[error("the exponent is invalid!")]
    InvalidExponent {
        /// The underlying validation error for the exponent.
        /// The type of this field is `<RawReal as RawScalarTrait>::ValidationErrors`.
        #[source]
        #[backtrace]
        source: <RawReal as RawScalarTrait>::ValidationErrors,
    },

    #[error("the base and the exponent are invalid!")]
    InvalidBaseAndExponent {
        /// The source error that occurred during validation for the base.
        error_base: <RawReal as RawScalarTrait>::ValidationErrors,

        /// The source error that occurred during validation for the exponent.
        error_exponent: <RawReal as RawScalarTrait>::ValidationErrors,

        #[backtrace]
        backtrace: Backtrace,
    },
}
//------------------------------------------------------------------

//------------------------------------------------------------------
/// Errors that can occur during input validation when computing `base^exponent`
/// where the base is a complex number and the exponent is a real number.
///
/// This enum is typically used as the `InputErrorSource` for a [`FunctionErrors`]
/// wrapper, such as [`PowComplexBaseRealExponentErrors`]. It is generic over
/// `ComplexType`, which must implement [`ComplexScalar`](crate::ComplexScalar).
#[derive(Debug, Error)]
pub enum PowComplexBaseRealExponentInputErrors<RawComplex: RawComplexTrait> {
    /// The input complex base value failed validation according to the active policy.
    ///
    /// For example, using [`StrictFinitePolicy`],
    /// this would trigger if any part of the complex base is NaN or Infinity.
    #[error("the input complex base is invalid according to validation policy")]
    InvalidBase {
        /// The underlying validation error for the complex base.
        /// The type of this field is `<RawComplex as RawScalarTrait>::ValidationErrors`.
        #[source]
        #[backtrace]
        source: <RawComplex as RawScalarTrait>::ValidationErrors,
    },

    /// The input real exponent value failed validation according to the active policy.
    ///
    /// For example, using [`StrictFinitePolicy`],
    /// this would trigger if the exponent is NaN or Infinity.
    #[error("the input real exponent is invalid according to validation policy")]
    InvalidExponent {
        /// The underlying validation error for the real exponent.
        /// The type of this field is `<RawReal as RawScalarTrait>::ValidationErrors`.
        #[source]
        #[backtrace]
        source: <RawComplex::RawReal as RawScalarTrait>::ValidationErrors,
    },

    /// The input complex base value and the real exponent failed validation according to the active policy.
    ///
    /// For example, using [`StrictFinitePolicy`],
    /// this would trigger if any part of the complex base is NaN or Infinity.
    #[error(
        "the input complex base and the real exponent is invalid according to validation policy"
    )]
    InvalidBaseAndExponent {
        /// The source error that occurred during validation for the base.
        error_base: <RawComplex as RawScalarTrait>::ValidationErrors,

        /// The source error that occurred during validation for the exponent.
        error_exponent: <RawComplex::RawReal as RawScalarTrait>::ValidationErrors,

        #[backtrace]
        backtrace: Backtrace,
    },

    /// The complex base is zero, and the real exponent is negative.
    /// This operation results in division by zero.
    #[error("the base is zero and the exponent (={exponent}) is negative!")]
    ZeroBaseNegativeExponent {
        /// The negative real exponent value.
        exponent: RawComplex::RawReal,

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

/*
//------------------------------------------------------------------
/// An enum that unifies input errors for power functions with a real exponent.
#[derive(Debug, Error)]
pub enum PowRealExponentInputError<RawReal, RawComplex>
where
    RawReal: RawRealTrait,
    RawComplex: RawComplexTrait<RawReal = RawReal>,
{
    #[error("Error with real base")]
    ForRealBase(#[from] PowRealBaseRealExponentInputErrors<RawReal>),

    #[error("Error with complex base")]
    ForComplexBase(#[from] PowComplexBaseRealExponentInputErrors<RawComplex>),
}
//------------------------------------------------------------------
*/

//------------------------------------------------------------------
/// Errors that can occur during input validation when computing `base^exponent`
/// where the exponent is an integer type.
///
/// This enum is typically used as the `InputErrorSource` for [`PowIntExponentErrors`].
/// It is generic over `RawScalar`, which must implement [`RawScalarTrait`],
/// and `IntExponentType`, which is the integer type of the exponent.
#[derive(Debug, Error)]
pub enum PowIntExponentInputErrors<RawScalar: RawScalarTrait, ExponentType: IntegerExponent> {
    /// The input base value failed validation according to the validation policy.
    ///
    /// For example, using [`StrictFinitePolicy`],
    /// this would trigger if the base is NaN or Infinity.
    #[error("the input base is invalid according to validation policy")]
    InvalidBase {
        /// The underlying validation error for the base.
        /// The type of this field is `<RawScalar as RawScalarTrait>::ValidationErrors`.
        #[source]
        #[backtrace]
        source: <RawScalar as RawScalarTrait>::ValidationErrors,
    },

    /// The base is zero, and the integer exponent is negative.
    /// This operation results in division by zero.
    #[error("detected a zero base with a negative integer exponent ({exponent:?})!")]
    ZeroBaseNegativeExponent {
        /// The negative integer exponent value.
        exponent: ExponentType,

        /// A captured backtrace for debugging purposes.
        backtrace: Backtrace,
    },
    // Note: Integer exponents themselves are not typically validated by StrictFinitePolicy,
    // as they are exact and finite by nature (unless they are too large for conversion
    // to the type expected by the underlying power function, e.g., i32 for powi).
    // Such conversion errors might be handled separately or lead to panics if unwrap is used.
}
//------------------------------------------------------------------

//------------------------------------------------------------------
/// Errors that can occur when computing `base^exponent` where both base and exponent
/// are real numbers.
///
/// This type represents failures from [`Pow::try_pow()`] for real base and real exponent.
/// It is generic over `RawReal: RawRealTrait`.
/// - The `Input` variant wraps [`PowRealBaseRealExponentInputErrors<RawReal>`].
/// - The `Output` variant wraps `<RawReal as RawScalarTrait>::ValidationErrors` for issues
///   with the computed result (e.g., overflow, NaN from valid inputs if possible).
#[derive(Debug, Error)]
pub enum PowRealBaseRealExponentErrors<RawReal: RawRealTrait> {
    /// The input value is invalid.
    #[error("the input value is invalid!")]
    Input {
        /// The source error that occurred during validation.
        #[from]
        source: PowRealBaseRealExponentInputErrors<RawReal>,
    },

    /// The output value is invalid.
    #[error("the output value is invalid!")]
    Output {
        /// The source error that occurred during validation.
        #[source]
        #[backtrace]
        source: <RawReal as RawScalarTrait>::ValidationErrors,
    },
}
//------------------------------------------------------------------

//------------------------------------------------------------------
/// Errors that can occur when computing `base^exponent` where the base is complex and the exponent is real.
///
/// This type represents failures from [`Pow::try_pow()`] for complex base and real exponent.
/// It is generic over `RawComplex: RawComplexTrait` and `RawReal: RawRealTrait`.
/// - The `Input` variant wraps [`PowComplexBaseRealExponentInputErrors<RawComplex>`].
/// - The `Output` variant wraps `<RawComplex as RawScalarTrait>::ValidationErrors` for issues
///   with the computed result.
#[derive(Debug, Error)]
pub enum PowComplexBaseRealExponentErrors<RawComplex: RawComplexTrait> {
    /// The input value is invalid.
    #[error("the input value is invalid!")]
    Input {
        /// The source error that occurred during validation.
        #[from]
        source: PowComplexBaseRealExponentInputErrors<RawComplex>,
    },

    /// The output value is invalid.
    #[error("the output value is invalid!")]
    Output {
        /// The source error that occurred during validation.
        #[source]
        #[backtrace]
        source: <RawComplex as RawScalarTrait>::ValidationErrors,
    },
}
//------------------------------------------------------------------

//------------------------------------------------------------------
/// A type alias for [`FunctionErrors`], specialized for errors that can occur
/// when computing `base^exponent` where the exponent is an integer type.
///
/// This type represents failures from [`Pow::try_pow()`] when using an integer exponent.
/// It is generic over `RawScalar: RawScalarTrait` and `IntExponentType: Clone + Debug`.
/// - The `Input` variant wraps [`PowIntExponentInputErrors<RawScalar, IntExponentType>`].
/// - The `Output` variant wraps `<RawScalar as RawScalarTrait>::ValidationErrors` for issues
///   with the computed result (e.g., overflow).
pub type PowIntExponentErrors<RawScalar, ExponentType> = FunctionErrors<
    PowIntExponentInputErrors<RawScalar, ExponentType>,
    <RawScalar as RawScalarTrait>::ValidationErrors,
>;
//------------------------------------------------------------------

//------------------------------------------------------------------
/// A trait for computing the power of a number (`base^exponent`).
///
/// This trait provides an interface for raising a `Self` type (the base)
/// to a power specified by `ExponentType`. It includes both a fallible version
/// (`try_pow`) that performs validation and an infallible version (`pow`) that
/// may panic in debug builds if validation fails.
///
/// # Type Parameters
///
/// - `ExponentType`: The type of the exponent. This can be a real number type
///   (like `f64` or `RealRugStrictFinite<P>`) or an integer type (like `i32`, `u32`, etc.).
pub trait Pow<ExponentType>: Sized {
    /// The error type that can be returned by the `try_pow` method.
    /// This type must implement `std::error::Error` for proper error handling.
    type Error: std::error::Error;

    /// Attempts to compute `self` (the base) raised to the power of `exponent`.
    ///
    /// This method performs validation on the base and exponent according to
    /// defined policies (e.g., [`StrictFinitePolicy`]).
    /// It also checks for domain errors specific to the power operation, such as
    /// a negative real base with a non-integer real exponent, or a zero base
    /// with a negative exponent. Finally, it validates the computed result.
    ///
    /// # Arguments
    ///
    /// * `exponent`: The exponent to raise `self` to.
    fn try_pow(self, exponent: ExponentType) -> Result<Self, Self::Error>;

    /// Computes `self` (the base) raised to the power of `exponent`.
    ///
    /// This method provides a potentially more performant way to compute the power,
    /// especially in release builds, by possibly omitting some checks performed by
    /// `try_pow`.
    ///
    /// # Behavior
    ///
    /// - In **debug builds**, this method typically calls `try_pow(exponent).unwrap()`,
    ///   meaning it will panic if `try_pow` would return an error.
    /// - In **release builds**, it may call the underlying power function directly.
    ///   While input validation might be skipped, output validation (e.g., checking
    ///   if the result is finite) might still occur implicitly if the underlying
    ///   power function produces non-finite results (like NaN or Infinity) from
    ///   finite inputs, which are then caught by policies like
    ///   [`StrictFinitePolicy`] if applied
    ///   to the output.
    ///
    /// # Arguments
    ///
    /// * `exponent`: The exponent to raise `self` to.
    ///
    /// # Returns
    ///
    /// The result of `self` raised to the power of `exponent`.
    ///
    /// # Panics
    ///
    /// - Panics in debug builds if `try_pow` would return an error.
    /// - Behavior regarding panics in release builds depends on the specific
    ///   implementation and the underlying power function. For example, `f64::powf`
    ///   itself does not panic but can return `NaN` or `Infinity`.
    fn pow(self, exponent: ExponentType) -> Self;
}

impl Pow<&f64> for f64 {
    type Error = PowRealBaseRealExponentErrors<f64>;

    #[inline(always)]
    fn try_pow(self, exponent: &f64) -> Result<Self, Self::Error> {
        let validation_base = StrictFinitePolicy::<f64, 53>::validate(self);
        let validation_exponent = StrictFinitePolicy::<f64, 53>::validate_ref(exponent);
        match (validation_base, validation_exponent) {
            (Ok(base), Ok(())) => {
                if base < 0.0 {
                    Err(PowRealBaseRealExponentInputErrors::NegativeBase {
                        base,
                        exponent: *exponent,
                        backtrace: capture_backtrace(),
                    }
                    .into())
                } else {
                    let result = base.powf(*exponent);
                    StrictFinitePolicy::<f64, 53>::validate(result)
                        .map_err(|e| PowRealBaseRealExponentErrors::Output { source: e })
                }
            }
            (Ok(_), Err(error_exponent)) => {
                Err(PowRealBaseRealExponentInputErrors::InvalidExponent {
                    source: error_exponent,
                }
                .into())
            }
            (Err(error_base), Ok(_)) => {
                Err(PowRealBaseRealExponentInputErrors::InvalidBase { source: error_base }.into())
            }
            (Err(error_base), Err(error_exponent)) => {
                Err(PowRealBaseRealExponentInputErrors::InvalidBaseAndExponent {
                    error_base,
                    error_exponent,
                    backtrace: capture_backtrace(),
                }
                .into())
            }
        }
    }

    #[inline(always)]
    /// Raises th number `self` to the power `exponent`.
    fn pow(self, exponent: &f64) -> Self {
        #[cfg(debug_assertions)]
        {
            self.try_pow(exponent).unwrap()
        }
        #[cfg(not(debug_assertions))]
        {
            self.powf(*exponent)
        }
    }
}

impl Pow<&f64> for Complex<f64> {
    type Error = PowComplexBaseRealExponentErrors<Self>;

    #[inline(always)]
    fn try_pow(self, exponent: &f64) -> Result<Self, Self::Error> {
        let validation_base = StrictFinitePolicy::<Complex<f64>, 53>::validate(self);
        let validation_exponent = StrictFinitePolicy::<f64, 53>::validate_ref(exponent);
        match (validation_base, validation_exponent) {
            (Ok(base), Ok(())) => {
                if Zero::is_zero(&base) {
                    if *exponent < 0.0 {
                        Err(
                            PowComplexBaseRealExponentInputErrors::ZeroBaseNegativeExponent {
                                exponent: *exponent,
                                backtrace: capture_backtrace(),
                            }
                            .into(),
                        )
                    } else if *exponent == 0.0 {
                        Ok(Complex::new(1.0, 0.0))
                    } else {
                        Ok(Complex::new(0.0, 0.0))
                    }
                } else {
                    let result = base.powf(*exponent);
                    StrictFinitePolicy::<Complex<f64>, 53>::validate(result)
                        .map_err(|e| PowComplexBaseRealExponentErrors::Output { source: e })
                }
            }
            (Ok(_), Err(error_exponent)) => {
                Err(PowComplexBaseRealExponentInputErrors::InvalidExponent {
                    source: error_exponent,
                }
                .into())
            }
            (Err(error_base), Ok(_)) => {
                Err(
                    PowComplexBaseRealExponentInputErrors::InvalidBase { source: error_base }
                        .into(),
                )
            }
            (Err(error_base), Err(error_exponent)) => Err(
                PowComplexBaseRealExponentInputErrors::InvalidBaseAndExponent {
                    error_base,
                    error_exponent,
                    backtrace: capture_backtrace(),
                }
                .into(),
            ),
        }
    }

    #[inline(always)]
    /// Raises th number `self` to the power `exponent`.
    fn pow(self, exponent: &f64) -> Self {
        #[cfg(debug_assertions)]
        {
            self.try_pow(exponent)
                .expect("Error raised by Pow::try_pow() in the function Pow::pow() (debug mode)")
        }
        #[cfg(not(debug_assertions))]
        {
            self.powf(*exponent)
        }
    }
}

#[duplicate_item(
    exponent_type exponent_func;
    [i8]    [exponent.into()];
    [i16]   [exponent.into()];
    [i32]   [exponent];
    [i64]   [exponent.try_into().unwrap()];
    [i128]  [exponent.try_into().unwrap()];
    [isize] [exponent.try_into().unwrap()];
)]
impl Pow<exponent_type> for f64 {
    type Error = PowIntExponentErrors<f64, exponent_type>;

    fn try_pow(self, exponent: exponent_type) -> Result<Self, Self::Error> {
        StrictFinitePolicy::<f64, 53>::validate(self)
            .map_err(|e| PowIntExponentInputErrors::InvalidBase { source: e }.into())
            .and_then(|base| {
                if base == 0. && exponent < 0 {
                    Err(PowIntExponentInputErrors::ZeroBaseNegativeExponent {
                        exponent,
                        backtrace: capture_backtrace(),
                    }
                    .into())
                } else {
                    let res = base.powi(exponent_func);
                    StrictFinitePolicy::<f64, 53>::validate(res)
                        .map_err(|e| Self::Error::Output { source: e })
                }
            })
    }

    #[inline(always)]
    /// Raises th number `self` to the power `exponent`.
    fn pow(self, exponent: exponent_type) -> Self {
        #[cfg(debug_assertions)]
        {
            self.try_pow(exponent).unwrap()
        }
        #[cfg(not(debug_assertions))]
        {
            self.powi(exponent_func)
        }
    }
}

#[duplicate_item(
    exponent_type exponent_func;
    [u8]    [exponent.into()];
    [u16]   [exponent.into()];
    [u32]   [exponent.try_into().unwrap()];
    [u64]   [exponent.try_into().unwrap()];
    [u128]  [exponent.try_into().unwrap()];
    [usize] [exponent.try_into().unwrap()];
)]
impl Pow<exponent_type> for f64 {
    type Error = PowIntExponentErrors<f64, exponent_type>;

    fn try_pow(self, exponent: exponent_type) -> Result<Self, Self::Error> {
        StrictFinitePolicy::<f64, 53>::validate(self)
            .map_err(|e| PowIntExponentInputErrors::InvalidBase { source: e }.into())
            .and_then(|base| {
                let res = base.powi(exponent_func);
                StrictFinitePolicy::<f64, 53>::validate(res)
                    .map_err(|e| Self::Error::Output { source: e })
            })
    }

    #[inline(always)]
    /// Raises th number `self` to the power `exponent`.
    fn pow(self, exponent: exponent_type) -> Self {
        #[cfg(debug_assertions)]
        {
            self.try_pow(exponent).unwrap()
        }
        #[cfg(not(debug_assertions))]
        {
            self.powi(exponent_func)
        }
    }
}

#[duplicate_item(
    exponent_type func_name exponent_func;
    [i8]    [powi] [exponent.into()];
    [i16]   [powi] [exponent.into()];
    [i32]   [powi] [exponent];
    [i64]   [powi] [exponent.try_into().unwrap()];
    [i128]  [powi] [exponent.try_into().unwrap()];
    [isize] [powi] [exponent.try_into().unwrap()];
)]
impl Pow<exponent_type> for Complex<f64> {
    type Error = PowIntExponentErrors<Complex<f64>, exponent_type>;

    #[inline(always)]
    fn try_pow(self, exponent: exponent_type) -> Result<Self, Self::Error> {
        StrictFinitePolicy::<Self, 53>::validate(self)
            .map_err(|e| PowIntExponentInputErrors::InvalidBase { source: e }.into())
            .and_then(|base| {
                if Zero::is_zero(&base) && exponent < 0 {
                    Err(PowIntExponentInputErrors::ZeroBaseNegativeExponent {
                        exponent,
                        backtrace: capture_backtrace(),
                    }
                    .into())
                } else {
                    StrictFinitePolicy::<Self, 53>::validate(base.func_name(exponent_func))
                        .map_err(|e| Self::Error::Output { source: e })
                }
            })
    }

    #[inline(always)]
    /// Raises th number `self` to the power `exponent`.
    fn pow(self, exponent: exponent_type) -> Self {
        #[cfg(debug_assertions)]
        {
            self.try_pow(exponent).unwrap()
        }
        #[cfg(not(debug_assertions))]
        {
            self.func_name(exponent_func)
        }
    }
}

#[duplicate_item(
    exponent_type func_name exponent_func;
    [u8]    [powu] [exponent.into()];
    [u16]   [powu] [exponent.into()];
    [u32]   [powu] [exponent];
    [u64]   [powu] [exponent.try_into().unwrap()];
    [u128]  [powu] [exponent.try_into().unwrap()];
    [usize] [powu] [exponent.try_into().unwrap()];
)]
impl Pow<exponent_type> for Complex<f64> {
    type Error = PowIntExponentErrors<Complex<f64>, exponent_type>;

    #[inline(always)]
    fn try_pow(self, exponent: exponent_type) -> Result<Self, Self::Error> {
        StrictFinitePolicy::<Self, 53>::validate(self)
            .map_err(|e| PowIntExponentInputErrors::InvalidBase { source: e }.into())
            .and_then(|base| {
                StrictFinitePolicy::<Self, 53>::validate(base.func_name(exponent_func))
                    .map_err(|e| Self::Error::Output { source: e })
            })
    }

    #[inline(always)]
    /// Raises th number `self` to the power `exponent`.
    fn pow(self, exponent: exponent_type) -> Self {
        #[cfg(debug_assertions)]
        {
            self.try_pow(exponent).unwrap()
        }
        #[cfg(not(debug_assertions))]
        {
            self.func_name(exponent_func)
        }
    }
}
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
/// An aggregate trait for types that can be raised to the power of any primitive integer.
pub trait PowIntExponent:
    Pow<i8, Error = PowIntExponentErrors<Self::RawType, i8>>
    + Pow<u8, Error = PowIntExponentErrors<Self::RawType, u8>>
    + Pow<i16, Error = PowIntExponentErrors<Self::RawType, i16>>
    + Pow<u16, Error = PowIntExponentErrors<Self::RawType, u16>>
    + Pow<i32, Error = PowIntExponentErrors<Self::RawType, i32>>
    + Pow<u32, Error = PowIntExponentErrors<Self::RawType, u32>>
    + Pow<i64, Error = PowIntExponentErrors<Self::RawType, i64>>
    + Pow<u64, Error = PowIntExponentErrors<Self::RawType, u64>>
    + Pow<i128, Error = PowIntExponentErrors<Self::RawType, i128>>
    + Pow<u128, Error = PowIntExponentErrors<Self::RawType, u128>>
    + Pow<isize, Error = PowIntExponentErrors<Self::RawType, isize>>
    + Pow<usize, Error = PowIntExponentErrors<Self::RawType, usize>>
{
    type RawType: RawScalarTrait;
}

impl PowIntExponent for f64 {
    type RawType = f64;
}

impl PowIntExponent for Complex<f64> {
    type RawType = Complex<f64>;
}

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

//------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        RealScalar,
        functions::{
            Pow, PowIntExponentErrors, PowIntExponentInputErrors,
            PowRealBaseRealExponentInputErrors,
        },
        validation::{ErrorsValidationRawComplex, ErrorsValidationRawReal},
    };
    use num::Complex;

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

    #[cfg(feature = "rug")]
    use try_create::TryNew;

    mod pow_u32_exponent {
        use super::*;

        mod real_base {
            use super::*;

            fn pow_generic_real_u32_valid<RealType: RealScalar>() {
                let base = RealType::try_from_f64(2.0).unwrap();
                let exponent = 3u32;
                let expected_result = RealType::try_from_f64(8.0).unwrap();
                assert_eq!(Pow::pow(base.clone(), exponent), expected_result);
                assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
            }

            #[test]
            fn test_pow_generic_real_u32_valid() {
                pow_generic_real_u32_valid::<f64>();
            }

            #[test]
            fn test_f64_pow_u32_valid() {
                let base = 2.0;
                let exponent = 3u32;
                let expected_result = 8.0;
                assert_eq!(Pow::pow(base, exponent), expected_result);
                assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
            }

            #[test]
            fn test_f64_pow_u32_zero_exponent() {
                let base = 2.0;
                let exponent = 0u32;
                let expected_result = 1.0;
                assert_eq!(Pow::pow(base, exponent), expected_result);
                assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
            }

            #[test]
            fn test_f64_pow_u32_one_exponent() {
                let base = 2.0;
                let exponent = 1u32;
                let expected_result = 2.0;
                assert_eq!(Pow::pow(base, exponent), expected_result);
                assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
            }

            #[test]
            fn test_f64_pow_u32_zero_base() {
                let base = 0.0;
                let exponent = 3u32;
                let expected_result = 0.0;
                assert_eq!(Pow::pow(base, exponent), expected_result);
                assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
            }

            #[test]
            fn test_f64_pow_u32_nan_base() {
                let base = f64::NAN;
                let exponent = 3u32;
                let result = base.try_pow(exponent);
                assert!(result.is_err());
            }

            #[test]
            fn test_f64_pow_u32_infinity_base() {
                let base = f64::INFINITY;
                let exponent = 3u32;
                let result = base.try_pow(exponent);
                assert!(result.is_err());
            }
        }

        mod complex_base {
            use super::*;

            #[test]
            fn test_complex_f64_pow_u32_valid() {
                let base = Complex::new(2.0, 3.0);
                let exponent = 2u32;
                let expected_result = Complex::new(-5.0, 12.0);
                assert_eq!(Pow::pow(base, exponent), expected_result);
                assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
            }

            #[test]
            fn test_complex_f64_pow_u32_zero_exponent() {
                let base = Complex::new(2.0, 3.0);
                let exponent = 0u32;
                let expected_result = Complex::new(1.0, 0.0);
                assert_eq!(Pow::pow(base, exponent), expected_result);
                assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
            }

            #[test]
            fn test_complex_f64_pow_u32_one_exponent() {
                let base = Complex::new(2.0, 3.0);
                let exponent = 1u32;
                let expected_result = Complex::new(2.0, 3.0);
                assert_eq!(Pow::pow(base, exponent), expected_result);
                assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
            }

            #[test]
            fn test_complex_f64_pow_u32_zero_base() {
                let base = Complex::new(0.0, 0.0);
                let exponent = 3u32;
                let expected_result = Complex::new(0.0, 0.0);
                assert_eq!(Pow::pow(base, exponent), expected_result);
                assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
            }

            #[test]
            fn test_complex_f64_pow_u32_nan_base() {
                let base = Complex::new(f64::NAN, 0.0);
                let exponent = 3u32;
                let result = base.try_pow(exponent);
                assert!(result.is_err());

                let base = Complex::new(0.0, f64::NAN);
                let result = base.try_pow(exponent);
                assert!(result.is_err());
            }

            #[test]
            fn test_complex_f64_pow_u32_infinity_base() {
                let base = Complex::new(f64::INFINITY, 0.0);
                let exponent = 3u32;
                let result = base.try_pow(exponent);
                assert!(result.is_err());

                let base = Complex::new(0.0, f64::INFINITY);
                let result = base.try_pow(exponent);
                assert!(result.is_err());
            }
        }
    }

    mod pow_i32_exponent {
        use super::*;

        mod native64 {
            use super::*;

            mod real_base {
                use super::*;

                #[test]
                fn test_f64_pow_valid() {
                    let base = 2.0;
                    let exponent = 3i32;
                    let expected_result = 8.0;
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                    assert_eq!(<f64 as Pow<i32>>::pow(base, exponent), expected_result);
                }

                #[test]
                fn test_f64_pow_zero_exponent() {
                    let base = 2.0;
                    let exponent = 0i32;
                    let expected_result = 1.0;
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_f64_pow_negative_exponent() {
                    let base = 2.0;
                    let exponent = -3i32;
                    let expected_result = 0.125;
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_f64_pow_zero_base() {
                    let base = 0.0;
                    let exponent = 3i32;
                    let expected_result = 0.0;
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_f64_pow_zero_base_zero_exponent() {
                    let base = 0.0;
                    let exponent = 0i32;
                    let expected_result = 1.0;
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_f64_pow_zero_base_negative_exponent() {
                    let base = 0.0;
                    let err = base.try_pow(-1i32).unwrap_err();
                    assert!(matches!(
                        err,
                        PowIntExponentErrors::Input {
                            source: PowIntExponentInputErrors::ZeroBaseNegativeExponent {
                                exponent: -1,
                                ..
                            }
                        }
                    ));
                }

                #[test]
                fn test_f64_pow_negative_base_positive_exponent() {
                    let base = -2.0;
                    let exponent = 3i32;
                    let expected_result = -8.0;
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_f64_pow_negative_base_negative_exponent() {
                    let base = -2.0;
                    let exponent = -3i32;
                    let expected_result = -0.125;
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }
            }

            mod complex_base {
                use super::*;

                #[test]
                fn test_complex_f64_pow_valid() {
                    let base = Complex::new(2.0, 3.0);
                    let exponent = 2i32;
                    let expected_result = Complex::new(-5.0, 12.0);
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                    assert_eq!(
                        <Complex<f64> as Pow<i32>>::pow(base, exponent),
                        expected_result
                    );
                }

                #[test]
                fn test_complex_f64_pow_zero_exponent() {
                    let base = Complex::new(2.0, 3.0);
                    let exponent = 0i32;
                    let expected_result = Complex::new(1.0, 0.0);
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_f64_pow_negative_exponent() {
                    let base = Complex::new(2.0, 3.0);
                    let exponent = -2i32;
                    let expected_result = Complex::new(-0.029585798816568053, -0.07100591715976332);
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_f64_pow_zero_base() {
                    let base = Complex::new(0.0, 0.0);
                    let exponent = 3i32;
                    let expected_result = Complex::new(0.0, 0.0);
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_f64_pow_zero_base_zero_exponent() {
                    let base = Complex::new(0.0, 0.0);
                    let exponent = 0i32;
                    let expected_result = Complex::new(1.0, 0.0);
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_f64_pow_zero_base_negative_exponent() {
                    let base = Complex::new(0.0, 0.0);
                    let err = base.try_pow(-1i32).unwrap_err();
                    assert!(matches!(
                        err,
                        PowIntExponentErrors::Input {
                            source: PowIntExponentInputErrors::ZeroBaseNegativeExponent {
                                exponent: -1,
                                ..
                            }
                        }
                    ));
                }

                #[test]
                fn test_complex_f64_pow_negative_base_positive_exponent() {
                    let base = Complex::new(-2.0, -3.0);
                    let exponent = 3i32;
                    let expected_result = Complex::new(46.0, -9.0);
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_f64_pow_negative_base_negative_exponent() {
                    let base = Complex::new(-2.0, -3.0);
                    let exponent = -3i32;
                    let expected_result = Complex::new(0.02093764223941739, 0.004096495220755574);
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }
            }
        }

        #[cfg(feature = "rug")]
        mod rug53 {
            use super::*;
            use crate::kernels::rug::{ComplexRugStrictFinite, RealRugStrictFinite};
            use rug::Float;

            mod real {
                use super::*;

                #[test]
                fn test_rug_float_pow_valid() {
                    let base =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 2.0)).unwrap();
                    let exponent = 3;
                    let expected_result =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 8.0)).unwrap();
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_rug_float_pow_zero_exponent() {
                    let base =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 2.0)).unwrap();
                    let exponent = 0;
                    let expected_result =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_rug_float_pow_negative_exponent() {
                    let base =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 2.0)).unwrap();
                    let exponent = -3;
                    let expected_result =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.125)).unwrap();
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_rug_float_pow_zero_base() {
                    let base =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.0)).unwrap();
                    let exponent = 3;
                    let expected_result =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.0)).unwrap();
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_rug_float_pow_zero_base_zero_exponent() {
                    let base =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.0)).unwrap();
                    let exponent = 0;
                    let expected_result =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_rug_float_pow_negative_base_positive_exponent() {
                    let base =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, -2.0)).unwrap();
                    let exponent = 3;
                    let expected_result =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, -8.0)).unwrap();
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_rug_float_pow_negative_base_negative_exponent() {
                    let base =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, -2.0)).unwrap();
                    let exponent = -3;
                    let expected_result =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, -0.125)).unwrap();
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }
            }

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

                #[test]
                fn test_complex_rug_float_pow_valid() {
                    let base = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 2.0), Float::with_val(53, 3.0)),
                    ))
                    .unwrap();
                    let exponent = 2;
                    let expected_result = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, -5.0), Float::with_val(53, 12.0)),
                    ))
                    .unwrap();
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_pow_zero_exponent() {
                    let base = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 2.0), Float::with_val(53, 3.0)),
                    ))
                    .unwrap();
                    let exponent = 0;
                    let expected_result = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 1.0), Float::with_val(53, 0.0)),
                    ))
                    .unwrap();
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_pow_negative_exponent() {
                    let base = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 2.0), Float::with_val(53, 3.0)),
                    ))
                    .unwrap();
                    let exponent = -2;
                    let expected_result = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (
                            Float::with_val(53, -2.9585798816568046e-2),
                            Float::with_val(53, -7.100591715976332e-2),
                        ),
                    ))
                    .unwrap();
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_pow_zero_base() {
                    let base = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 0.0), Float::with_val(53, 0.0)),
                    ))
                    .unwrap();
                    let exponent = 3;
                    let expected_result = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 0.0), Float::with_val(53, 0.0)),
                    ))
                    .unwrap();
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_pow_zero_base_zero_exponent() {
                    let base = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 0.0), Float::with_val(53, 0.0)),
                    ))
                    .unwrap();
                    let exponent = 0;
                    let expected_result = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 1.0), Float::with_val(53, 0.0)),
                    ))
                    .unwrap();
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_pow_negative_base_positive_exponent() {
                    let base = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, -2.0), Float::with_val(53, -3.0)),
                    ))
                    .unwrap();
                    let exponent = 3;
                    let expected_result = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 46.0), Float::with_val(53, -9.0)),
                    ))
                    .unwrap();
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_pow_negative_base_negative_exponent() {
                    let base = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, -2.0), Float::with_val(53, -3.0)),
                    ))
                    .unwrap();
                    let exponent = -3;
                    let expected_result = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (
                            Float::with_val(53, 2.0937642239417388e-2),
                            Float::with_val(53, 4.096495220755576e-3),
                        ),
                    ))
                    .unwrap();
                    assert_eq!(base.try_pow(exponent).unwrap(), expected_result);
                }
            }
        }
    }

    mod pow_real_exponent {
        use super::*;

        mod native64 {
            use super::*;

            mod real_base {
                use super::*;
                use core::f64;

                #[test]
                fn test_f64_pow_f64_valid() {
                    let base = 2.0;
                    let exponent = 3.0;
                    let expected_result = 8.0;
                    assert_eq!(Pow::pow(base, &exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_f64_pow_f64_zero_exponent() {
                    let base = 2.0;
                    let exponent = 0.0;
                    let expected_result = 1.0;
                    assert_eq!(Pow::pow(base, &exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_f64_pow_f64_invalid_exponent() {
                    let base = 2.0;
                    let err = base.try_pow(&f64::NAN).unwrap_err();
                    assert!(matches!(
                        err,
                        PowRealBaseRealExponentErrors::Input {
                            source: PowRealBaseRealExponentInputErrors::InvalidExponent {
                                source: ErrorsValidationRawReal::IsNaN { .. }
                            }
                        }
                    ))
                }

                #[test]
                fn test_f64_pow_f64_invalid_base_invalid_exponent() {
                    let err = (f64::INFINITY).try_pow(&f64::NAN).unwrap_err();
                    assert!(matches!(
                        err,
                        PowRealBaseRealExponentErrors::Input {
                            source: PowRealBaseRealExponentInputErrors::InvalidBaseAndExponent {
                                error_base: ErrorsValidationRawReal::IsPosInfinity { .. },
                                error_exponent: ErrorsValidationRawReal::IsNaN { .. },
                                ..
                            }
                        }
                    ))
                }

                #[test]
                fn test_f64_pow_f64_one_exponent() {
                    let base = 2.0;
                    let exponent = 1.0;
                    let expected_result = 2.0;
                    assert_eq!(Pow::pow(base, &exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_f64_pow_f64_zero_base() {
                    let base = 0.0;
                    let exponent = 3.0;
                    let expected_result = 0.0;
                    assert_eq!(Pow::pow(base, &exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_f64_pow_f64_nan_base() {
                    let base = f64::NAN;
                    let exponent = 3.0;
                    let result = base.try_pow(&exponent);
                    assert!(result.is_err());
                }

                #[test]
                fn test_f64_pow_f64_infinity_base() {
                    let base = f64::INFINITY;
                    let exponent = 3.0;
                    let result = base.try_pow(&exponent);
                    assert!(matches!(
                        result,
                        Err(PowRealBaseRealExponentErrors::Input { .. })
                    ));
                }

                #[test]
                fn test_f64_pow_f64_negative_exponent() {
                    let base = 2.0;
                    let exponent = -2.0;
                    let expected_result = 0.25;
                    assert_eq!(Pow::pow(base, &exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_f64_pow_f64_negative_base_positive_exponent() {
                    let base = -2.0;
                    let exponent = 3.0;
                    let result = base.try_pow(&exponent);
                    assert!(matches!(
                        result,
                        Err(PowRealBaseRealExponentErrors::Input { .. })
                    ));
                }

                #[test]
                fn test_f64_pow_f64_negative_base_negative_exponent() {
                    let base = -2.0;
                    let exponent = -3.0;
                    let result = base.try_pow(&exponent);
                    assert!(result.is_err());
                }
            }

            mod complex_base {
                use super::*;

                #[test]
                fn test_complex_f64_pow_ok_base_ok_exponent() {
                    let base = Complex::new(2.0, 3.0);
                    let exponent = 2.0;
                    let expected_result = Complex::new(-4.999999999999999, 11.999999999999998);
                    assert_eq!(Pow::pow(base, &exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_f64_pow_ok_base_zero_exponent() {
                    let base = Complex::new(2.0, 3.0);
                    let exponent = 0.0;
                    let expected_result = Complex::new(1.0, 0.0);
                    assert_eq!(Pow::pow(base, &exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_f64_pow_zero_base_zero_exponent() {
                    let base = Complex::new(0.0, 0.0);
                    let exponent = 0.0;
                    let expected_result = Complex::new(1.0, 0.0);
                    assert_eq!(Pow::pow(base, &exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_f64_pow_zero_base_negative_exponent() {
                    let base = Complex::new(0.0, 0.0);
                    let exponent = -1.0;
                    let err = base.try_pow(&exponent).unwrap_err();
                    assert!(matches!(
                        err,
                        PowComplexBaseRealExponentErrors::Input {
                            source:
                                PowComplexBaseRealExponentInputErrors::ZeroBaseNegativeExponent {
                                    exponent: -1.,
                                    ..
                                }
                        }
                    ));
                }

                #[test]
                fn test_complex_f64_pow_ok_base_nan_exponent() {
                    let base = Complex::new(0.0, 0.0);
                    let exponent = f64::NAN;
                    let err = base.try_pow(&exponent).unwrap_err();
                    assert!(matches!(
                        err,
                        PowComplexBaseRealExponentErrors::Input {
                            source: PowComplexBaseRealExponentInputErrors::InvalidExponent {
                                source: ErrorsValidationRawReal::IsNaN { .. }
                            }
                        }
                    ));
                }

                #[test]
                fn test_complex_f64_pow_infinity_base_nan_exponent() {
                    let base = Complex::new(f64::INFINITY, 0.0);
                    let exponent = f64::NAN;
                    let err = base.try_pow(&exponent).unwrap_err();
                    assert!(matches!(
                        err,
                        PowComplexBaseRealExponentErrors::Input {
                            source: PowComplexBaseRealExponentInputErrors::InvalidBaseAndExponent {
                                error_base: ErrorsValidationRawComplex::InvalidRealPart {
                                    source: ErrorsValidationRawReal::IsPosInfinity { .. }
                                },
                                error_exponent: ErrorsValidationRawReal::IsNaN { .. },
                                ..
                            }
                        }
                    ));
                }

                #[test]
                fn test_complex_f64_pow_ok_base_one_exponent() {
                    let base = Complex::new(2.0, 3.0);
                    let exponent = 1.0;
                    let expected_result = Complex::new(2.0, 3.0);
                    assert_eq!(Pow::pow(base, &exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_f64_pow_zero_base_ok_exponent() {
                    let base = Complex::new(0.0, 0.0);
                    let exponent = 3.0;
                    let expected_result = Complex::new(0.0, 0.0);
                    assert_eq!(Pow::pow(base, &exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_f64_pow_nan_base_ok_exponent() {
                    let base = Complex::new(f64::NAN, 0.0);
                    let exponent = 3.0;
                    let result = base.try_pow(&exponent);
                    assert!(result.is_err());

                    let base = Complex::new(0.0, f64::NAN);
                    let result = base.try_pow(&exponent);
                    assert!(result.is_err());
                }

                #[test]
                fn test_complex_f64_pow_infinity_base_ok_exponent() {
                    let base = Complex::new(f64::INFINITY, 0.0);
                    let exponent = 3.0;
                    let result = base.try_pow(&exponent);
                    assert!(result.is_err());

                    let base = Complex::new(0.0, f64::INFINITY);
                    let result = base.try_pow(&exponent);
                    assert!(result.is_err());
                }

                #[test]
                fn test_complex_f64_pow_ok_base_negative_exponent() {
                    let base = Complex::new(2.0, 3.0);
                    let exponent = -2.0;
                    let expected_result = Complex::new(-0.029585798816568046, -0.07100591715976332);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_f64_pow_negative_base_positive_exponent() {
                    let base = Complex::new(-2.0, -3.0);
                    let exponent = 3.0;
                    let expected_result = Complex::new(46.0, -8.99999999999999);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_f64_pow_negative_base_negative_exponent() {
                    let base = Complex::new(-2.0, -3.0);
                    let exponent = -3.0;
                    let expected_result = Complex::new(0.02093764223941739, 0.004096495220755572);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }
            }
        }

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

            mod real_base {
                use super::*;

                #[test]
                fn test_realrug_pow_valid() {
                    let base =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 2.0)).unwrap();
                    let exponent =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 3.0)).unwrap();
                    let expected_result =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 8.0)).unwrap();
                    assert_eq!(base.clone().pow(&exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_realrug_pow_zero_exponent() {
                    let base =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 2.0)).unwrap();
                    let exponent =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.0)).unwrap();
                    let expected_result =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
                    assert_eq!(base.clone().pow(&exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_realrug_pow_one_exponent() {
                    let base =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 2.0)).unwrap();
                    let exponent =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
                    let expected_result =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 2.0)).unwrap();
                    assert_eq!(base.clone().pow(&exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_realrug_pow_zero_base() {
                    let base =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.0)).unwrap();
                    let exponent =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 3.0)).unwrap();
                    let expected_result =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.0)).unwrap();
                    assert_eq!(base.clone().pow(&exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_realrug_pow_negative_exponent() {
                    let base =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 2.0)).unwrap();
                    let exponent =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, -2.0)).unwrap();
                    let expected_result =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.25)).unwrap();
                    assert_eq!(base.clone().pow(&exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_realrug_pow_negative_base_positive_exponent() {
                    let base =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, -2.0)).unwrap();
                    let exponent =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 3.0)).unwrap();
                    let result = base.try_pow(&exponent);
                    assert!(matches!(
                        result,
                        Err(PowRealBaseRealExponentErrors::Input { .. })
                    ));
                }

                #[test]
                fn test_realrug_pow_negative_base_negative_exponent() {
                    let base =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, -2.0)).unwrap();
                    let exponent =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, -3.0)).unwrap();
                    let result = base.try_pow(&exponent);
                    assert!(matches!(
                        result,
                        Err(PowRealBaseRealExponentErrors::Input { .. })
                    ));
                }
            }

            mod complex_base {
                use super::*;

                #[test]
                fn test_complex_rug_float_pow_valid() {
                    let base = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 2.0), Float::with_val(53, 3.0)),
                    ))
                    .unwrap();
                    let exponent =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 2.0)).unwrap();
                    let expected_result = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, -5.0), Float::with_val(53, 12.0)),
                    ))
                    .unwrap();
                    assert_eq!(base.clone().pow(&exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_pow_zero_exponent() {
                    let base = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 2.0), Float::with_val(53, 3.0)),
                    ))
                    .unwrap();
                    let exponent =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.0)).unwrap();
                    let expected_result = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 1.0), Float::with_val(53, 0.0)),
                    ))
                    .unwrap();
                    assert_eq!(base.clone().pow(&exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_pow_one_exponent() {
                    let base = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 2.0), Float::with_val(53, 3.0)),
                    ))
                    .unwrap();
                    let exponent =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
                    let expected_result = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 2.0), Float::with_val(53, 3.0)),
                    ))
                    .unwrap();
                    assert_eq!(base.clone().pow(&exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_pow_zero_base() {
                    let base = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 0.0), Float::with_val(53, 0.0)),
                    ))
                    .unwrap();
                    let exponent =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 3.0)).unwrap();
                    let expected_result = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 0.0), Float::with_val(53, 0.0)),
                    ))
                    .unwrap();
                    assert_eq!(base.clone().pow(&exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_pow_negative_exponent() {
                    let base = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 2.0), Float::with_val(53, 3.0)),
                    ))
                    .unwrap();
                    let exponent =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, -2.0)).unwrap();
                    let expected_result = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (
                            Float::with_val(53, -0.029585798816568046),
                            Float::with_val(53, -0.07100591715976332),
                        ),
                    ))
                    .unwrap();
                    assert_eq!(base.clone().pow(&exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_pow_negative_base_positive_exponent() {
                    let base = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, -2.0), Float::with_val(53, -3.0)),
                    ))
                    .unwrap();
                    let exponent =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, 3.0)).unwrap();
                    let expected_result = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, 46.0), Float::with_val(53, -9.0)),
                    ))
                    .unwrap();
                    assert_eq!(base.clone().pow(&exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_pow_negative_base_negative_exponent() {
                    let base = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (Float::with_val(53, -2.0), Float::with_val(53, -3.0)),
                    ))
                    .unwrap();
                    let exponent =
                        RealRugStrictFinite::<53>::try_new(Float::with_val(53, -3.0)).unwrap();
                    let expected_result = ComplexRugStrictFinite::<53>::try_new(Complex::with_val(
                        53,
                        (
                            Float::with_val(53, 2.0937642239417388e-2),
                            Float::with_val(53, 4.096495220755576e-3),
                        ),
                    ))
                    .unwrap();
                    assert_eq!(base.clone().pow(&exponent), expected_result);
                    assert_eq!(base.try_pow(&exponent).unwrap(), expected_result);
                }
            }
        }
    }
}
//------------------------------------------------------------------------------------------------
