#![deny(rustdoc::broken_intra_doc_links)]

//! This module provides traits, error types, and implementations for various
//! trigonometric functions.
//!
//! It defines a set of traits (e.g., [`Sin`], [`Cos`], [`ATan2`]) for standard
//! trigonometric operations and their inverses. These traits are implemented
//! for [`f64`], [`num::Complex<f64>`], [`RealValidated`](crate::RealValidated), [`ComplexValidated`](crate::ComplexValidated)
//! and can be extended to other numeric types satisfying the [`RealScalar`](crate::RealScalar) or [`ComplexScalar`](crate::ComplexScalar) traits.
//!
//! A comprehensive error handling system is in place, utilizing specific input
//! error enums (e.g., [`ASinRealInputErrors`], [`ATanComplexInputErrors`]) and
//! general function error type aliases (e.g., [`SinErrors`], [`CosErrors`])
//! built upon the [`FunctionErrors`] struct. This allows for granular reporting
//! of issues such as invalid arguments (NaN, infinity), out-of-domain values,
//! or poles.
//!
//! The design emphasizes robustness through strict validation of inputs and outputs,
//! primarily using the [`StrictFinitePolicy`].

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

//------------------------------------------------------------------------------------------------
// Real and Complex Number Input Errors (for functions like sin, cos)
//------------------------------------------------------------------------------------------------
#[duplicate_item(
    enum_name             enum_doc;
    [CosInputErrors]  ["Errors that can occur during the input validation phase when computing the *cosine* of a real or complex number.\n\nThis enum is used as a source for the `Input` variant of [`CosErrors`].\n\n# Type Parameters\n\n- `RawScalar`: A type that implements the [`RawScalarTrait`] trait. This type parameter is used to specify the numeric type for the computation and its associated raw error type `RawScalar::ValidationErrors` (e.g., [`crate::validation::ErrorsValidationRawReal`], [`crate::validation::ErrorsValidationRawComplex`], etc.).\n\n# Variants\n\n- `InvalidArgument`: Indicates that the input argument failed general validation checks (e.g., NaN, Infinity). The `source` field provides the specific raw validation error."];
    [SinInputErrors]  ["Errors that can occur during the input validation phase when computing the *sine* of a real or complex number.\n\nThis enum is used as a source for the `Input` variant of [`SinErrors`].\n\n# Type Parameters\n\n- `RawScalar`: A type that implements the [`RawScalarTrait`] trait. This type parameter is used to specify the numeric type for the computation and its associated raw error type `RawScalar::ValidationErrors` (e.g., [`crate::validation::ErrorsValidationRawReal`], [`crate::validation::ErrorsValidationRawComplex`], etc.).\n\n# Variants\n\n- `InvalidArgument`: Indicates that the input argument failed general validation checks (e.g., NaN, Infinity). The `source` field provides the specific raw validation error."];
)]
#[derive(Debug, Error)]
#[doc = enum_doc]
pub enum enum_name<RawScalar: RawScalarTrait> {
    /// The argument of the function is invalid.
    ///
    /// This variant indicates that the argument failed general validation checks
    /// according to the chosen validation policy (e.g., NaN, Infinity).
    #[error("the argument of the function is invalid!")]
    InvalidArgument {
        /// The underlying validation error from the input's raw scalar type.
        #[source]
        #[backtrace]
        source: RawScalar::ValidationErrors,
    },
}

//------------------------------------------------------------------------------------------------
// Real Number Input Errors (for functions like tan, atan)
//------------------------------------------------------------------------------------------------

/// Errors that can occur during the input validation phase when computing the *tangent* of a *real number*.
///
/// This enum is used as a source for the `Input` variant of [`TanRealErrors`].
///
/// # Type Parameters
///
/// - `RawReal`: A type that implements the [`RawRealTrait`] trait.
///
/// # Variants
///
/// - `ArgumentIsPole`: Indicates the input argument is a mathematical pole (e.g., π/2 + kπ).
/// - `InvalidArgument`: Indicates that the input argument failed general validation checks (e.g., NaN, Infinity).
#[derive(Debug, Error)]
pub enum TanRealInputErrors<RawReal: RawRealTrait> {
    /// The argument of the function is a mathematical pole (e.g., π/2 + kπ).
    #[error("the argument ({value}) is a mathematical pole for the tangent function!")]
    ArgumentIsPole {
        /// The value that is a pole.
        value: RawReal,
        /// The backtrace of the error.
        backtrace: Backtrace,
    },

    /// The argument of the function is invalid.
    ///
    /// This variant indicates that the argument failed general validation checks
    /// according to the chosen validation policy (e.g., NaN, Infinity).
    #[error("the argument of the function is invalid!")]
    InvalidArgument {
        /// The underlying validation error from the input's raw real type.
        #[source]
        #[backtrace]
        source: <RawReal as RawScalarTrait>::ValidationErrors,
    },
}

/// Errors that can occur during the input validation phase when computing the *inverse tangent* of a *real number*.
///
/// This enum is used as a source for the `Input` variant of [`ATanRealErrors`].
///
/// # Type Parameters
///
/// - `RawReal`: A type that implements the [`RawRealTrait`] trait. This type parameter is used to specify the numeric type for the computation and its associated raw error type `<RawReal as RawScalarTrait>::ValidationErrors` (e.g., [`crate::validation::ErrorsValidationRawReal<f64>`]).
///
/// # Variants
/// - `InvalidArgument`: Indicates that the input argument failed general validation checks (e.g., NaN, Infinity). The `source` field provides the specific raw validation error.
#[derive(Debug, Error)]
pub enum ATanRealInputErrors<RawReal: RawRealTrait> {
    /// The argument of the function is invalid.
    ///
    /// This variant indicates that the argument failed general validation checks
    /// according to the chosen validation policy (e.g., NaN, Infinity).
    #[error("the argument of the function is invalid!")]
    InvalidArgument {
        /// The underlying validation error from the input's raw real type.
        #[source]
        #[backtrace]
        source: RawReal::ValidationErrors,
    },
}

//------------------------------------------------------------------------------------------------
// Real Number Input Errors (for functions like asin, acos with domain constraints)
//------------------------------------------------------------------------------------------------
#[duplicate_item(
    enum_name             enum_doc;
    [ASinRealInputErrors] ["Errors that can occur during the input validation phase when computing the *inverse sine* of a *real number*.\n\nThis enum is used as a source for the `Input` variant of [`ASinRealErrors`].\n\n# Type Parameters\n\n- `RawReal`: A type that implements the [`RawRealTrait`] trait. This type parameter is used to specify the numeric type for the computation and its associated raw error type `<RawReal as RawScalarTrait>::ValidationErrors` (e.g., [`crate::validation::ErrorsValidationRawReal<f64>`]).\n\n# Variants\n\n- `OutOfDomain`: Indicates the input argument is outside the valid domain `[-1, 1]`.\n- `InvalidArgument`: Indicates that the input argument failed general validation checks (e.g., NaN, Infinity). The `source` field provides the specific raw validation error."];
    [ACosRealInputErrors] ["Errors that can occur during the input validation phase when computing the *inverse cosine* of a *real number*.\n\nThis enum is used as a source for the `Input` variant of [`ACosRealErrors`].\n\n# Type Parameters\n\n- `RawReal`: A type that implements the [`RawRealTrait`] trait. This type parameter is used to specify the numeric type for the computation and its associated raw error type `<RawReal as RawScalarTrait>::ValidationErrors` (e.g., [`crate::validation::ErrorsValidationRawReal<f64>`]).\n\n# Variants\n\n- `OutOfDomain`: Indicates the input argument is outside the valid domain `[-1, 1]`.\n- `InvalidArgument`: Indicates that the input argument failed general validation checks (e.g., NaN, Infinity). The `source` field provides the specific raw validation error."];
)]
#[derive(Debug, Error)]
#[doc = enum_doc]
pub enum enum_name<RawReal: RawRealTrait> {
    /// The argument of the function is not in the valid domain `[-1, 1]`.
    #[error("the argument of the function ({value}) is not in the domain [-1.,1.]!")]
    OutOfDomain {
        /// The value that is out of the domain [-1.,1.].
        value: RawReal,

        /// The backtrace of the error.
        backtrace: Backtrace,
    },

    /// The argument of the function is invalid (e.g., NaN, Infinity, or subnormal).
    ///
    /// This variant indicates that the argument failed general validation checks
    /// according to the chosen validation policy (e.g., NaN, Infinity).
    #[error("the argument of the function is invalid!")]
    InvalidArgument {
        /// The source error that occurred during validation.
        #[source]
        #[backtrace]
        source: <RawReal as RawScalarTrait>::ValidationErrors,
    },
}

//------------------------------------------------------------------------------------------------
// Real and Complex Number General Function Errors (Type Aliases for FunctionErrors)
//------------------------------------------------------------------------------------------------
#[duplicate_item(
    enum_name   ErrIn            enum_doc;
    [CosErrors] [CosInputErrors] ["A type alias for [`FunctionErrors`], specialized for errors during *cosine* computation on a real or complex number.\n\nRepresents failures from [`Cos::try_cos()`].\n\n# Type Parameters\n\n- `RawScalar`: Implements [`RawScalarTrait`]. Defines input error type via `ErrIn<RawScalar>` and output raw error via `RawScalar::ValidationErrors`.\n\n# Variants\n\n- `Input { source: ErrIn<RawScalar> }`: Input was invalid (e.g., NaN, Infinity).\n- `Output { source: RawScalar::ValidationErrors }`: Computed output was invalid (e.g., NaN, Infinity)."];
    [SinErrors] [SinInputErrors] ["A type alias for [`FunctionErrors`], specialized for errors during *sine* computation on a real or complex number.\n\nRepresents failures from [`Sin::try_sin()`].\n\n# Type Parameters\n\n- `RawScalar`: Implements [`RawScalarTrait`]. Defines input error type via `ErrIn<RawScalar>` and output raw error via `RawScalar::ValidationErrors`.\n\n# Variants\n\n- `Input { source: ErrIn<RawScalar> }`: Input was invalid (e.g., NaN, Infinity).\n- `Output { source: RawScalar::ValidationErrors }`: Computed output was invalid (e.g., NaN, Infinity)."];
)]
#[doc = enum_doc]
pub type enum_name<RawScalar> =
    FunctionErrors<ErrIn<RawScalar>, <RawScalar as RawScalarTrait>::ValidationErrors>;

//------------------------------------------------------------------------------------------------
// Real Number General Function Errors (Type Aliases for FunctionErrors)
//------------------------------------------------------------------------------------------------
#[duplicate_item(
    enum_name        ErrIn                 enum_doc;
    [ACosRealErrors] [ACosRealInputErrors] ["A type alias for [`FunctionErrors`], specialized for errors during *inverse cosine* computation on a *real number*.\n\nRepresents failures from [`ACos::try_acos()`].\n\n# Type Parameters\n\n- `RawReal`: Implements [`RawRealTrait`]. Defines input error type via `ErrIn<RawReal>` and output raw error via `<RawReal as RawScalarTrait>::ValidationErrors`.\n\n# Variants\n\n- `Input { source: ErrIn<RawReal> }`: Input was invalid (e.g., out of domain `[-1,1]`, NaN, Infinity).\n- `Output { source: <RawReal as RawScalarTrait>::ValidationErrors }`: Computed output was invalid (e.g., NaN, Infinity)."];
    [ASinRealErrors] [ASinRealInputErrors] ["A type alias for [`FunctionErrors`], specialized for errors during *inverse sine* computation on a *real number*.\n\nRepresents failures from [`ASin::try_asin()`].\n\n# Type Parameters\n\n- `RawReal`: Implements [`RawRealTrait`]. Defines input error type via `ErrIn<RawReal>` and output raw error via `<RawReal as RawScalarTrait>::ValidationErrors`.\n\n# Variants\n\n- `Input { source: ErrIn<RawReal> }`: Input was invalid (e.g., out of domain `[-1,1]`, NaN, Infinity).\n- `Output { source: <RawReal as RawScalarTrait>::ValidationErrors }`: Computed output was invalid (e.g., NaN, Infinity)."];
    [ATanRealErrors] [ATanRealInputErrors] ["A type alias for [`FunctionErrors`], specialized for errors during *inverse tangent* computation on a *real number*.\n\nRepresents failures from [`ATan::try_atan()`].\n\n# Type Parameters\n\n- `RawReal`: Implements [`RawRealTrait`]. Defines input error type via `ErrIn<RawReal>` and output raw error via `<RawReal as RawScalarTrait>::ValidationErrors`.\n\n# Variants\n\n- `Input { source: ErrIn<RawReal> }`: Input was invalid (e.g., NaN, Infinity).\n- `Output { source: <RawReal as RawScalarTrait>::ValidationErrors }`: Computed output was invalid (e.g., NaN, Infinity)."];
    [TanRealErrors]  [TanRealInputErrors]  ["A type alias for [`FunctionErrors`], specialized for errors during *tangent* computation on a *real number*.\n\nRepresents failures from [`Tan::try_tan()`].\n\n# Type Parameters\n\n- `RawReal`: Implements [`RawRealTrait`]. Defines input error type via `ErrIn<RawReal>` and output raw error via `<RawReal as RawScalarTrait>::ValidationErrors`.\n\n# Variants\n\n- `Input { source: ErrIn<RawReal> }`: Input was invalid (e.g., NaN, Infinity).\n- `Output { source: <RawReal as RawScalarTrait>::ValidationErrors }`: Computed output was invalid (e.g., NaN, Infinity)."];
)]
#[doc = enum_doc]
pub type enum_name<RawReal> =
    FunctionErrors<ErrIn<RawReal>, <RawReal as RawScalarTrait>::ValidationErrors>;

//------------------------------------------------------------------------------------------------
// Complex Number Input Errors (for functions like tan, asin, acos)
//------------------------------------------------------------------------------------------------
#[duplicate_item(
    enum_name                enum_doc;
    [ACosComplexInputErrors] ["Errors that can occur during the input validation phase when computing the *inverse cosine* of a *complex number*.\n\nThis enum is used as a source for the `Input` variant of [`ACosComplexErrors`].\n\n# Type Parameters\n\n- `RawComplex`: A type that implements the [`RawComplexTrait`] trait. This type parameter is used to specify the numeric type for the computation and its associated raw error type `<RawComplex as RawScalarTrait>::ValidationErrors`.\n\n# Variants\n\n- `InvalidArgument`: Indicates that the input argument failed general validation checks (e.g., components are NaN, Infinity). The `source` field provides the specific raw validation error."];
    [ASinComplexInputErrors] ["Errors that can occur during the input validation phase when computing the *inverse sine* of a *complex number*.\n\nThis enum is used as a source for the `Input` variant of [`ASinComplexErrors`].\n\n# Type Parameters\n\n- `RawComplex`: A type that implements the [`RawComplexTrait`] trait. This type parameter is used to specify the numeric type for the computation and its associated raw error type `<RawComplex as RawScalarTrait>::ValidationErrors`.\n\n# Variants\n\n- `InvalidArgument`: Indicates that the input argument failed general validation checks (e.g., components are NaN, Infinity). The `source` field provides the specific raw validation error."];
    [TanComplexInputErrors]  ["Errors that can occur during the input validation phase when computing the *tangent* of a *complex number*.\n\nThis enum is used as a source for the `Input` variant of [`TanComplexErrors`].\n\n# Type Parameters\n\n- `RawComplex`: A type that implements the [`RawComplexTrait`] trait. This type parameter is used to specify the numeric type for the computation and its associated raw error type `<RawComplex as RawScalarTrait>::ValidationErrors`.\n\n# Variants\n\n- `InvalidArgument`: Indicates that the input argument failed general validation checks (e.g., components are NaN, Infinity). The `source` field provides the specific raw validation error."];
)]
#[derive(Debug, Error)]
#[doc = enum_doc]
pub enum enum_name<RawComplex: RawComplexTrait> {
    /// The argument of the function is invalid (e.g., one or both components are NaN, Infinity, or subnormal).
    ///
    /// This variant indicates that the argument failed general validation checks
    /// according to the chosen validation policy.
    #[error("the argument of the function is invalid!")]
    InvalidArgument {
        /// The underlying validation error from the input's raw complex type.
        #[source]
        #[backtrace]
        source: <RawComplex as RawScalarTrait>::ValidationErrors,
    },
}

//------------------------------------------------------------------------------------------------
// Complex ATan Input Errors (Specific due to poles)
//------------------------------------------------------------------------------------------------
/// Errors that can occur during the input validation phase when attempting to compute
/// the arctangent of a complex number.
///
/// This enum is used as a source for the `Input` variant of [`ATanComplexErrors`].
/// It is generic over `RawComplex`, which must implement [`RawComplexTrait`].
/// This enum addresses failures in the initial validation of the input complex
/// number (e.g., components being NaN, Infinity, or subnormal) or if the
/// argument is a pole for the complex arctangent function (e.g., `0 +/- 1i`).
#[derive(Debug, Error)]
pub enum ATanComplexInputErrors<RawComplex: RawComplexTrait> {
    /// The input complex number is a pole for the arctangent function (e.g., `i` or `-i`).
    ///
    /// The complex arctangent function is undefined at these points.
    #[error("the argument ({value:?}) is a pole for the function!")]
    ArgumentIsPole {
        /// The argument value that is a pole.
        value: RawComplex,

        /// The backtrace of the error.
        backtrace: std::backtrace::Backtrace,
    },

    /// The argument of the function is invalid due to failing general validation checks.
    ///
    /// This variant indicates that the argument of the function is invalid w.r.t. the chosen validation policy (e.g. NaN, Infinity).
    /// It includes the source error that occurred during validation.
    #[error("the argument of the function is invalid!")]
    InvalidArgument {
        /// The source error that occurred during validation.
        #[source]
        #[backtrace]
        source: <RawComplex as RawScalarTrait>::ValidationErrors,
    },
}

//------------------------------------------------------------------------------------------------
// Complex Number General Function Errors (Type Aliases for FunctionErrors)
//------------------------------------------------------------------------------------------------
#[duplicate_item(
    enum_name           ErrIn                    enum_doc;
    [ACosComplexErrors] [ACosComplexInputErrors] ["A type alias for [`FunctionErrors`], specialized for errors during *inverse cosine* computation on a *complex number*.\n\nRepresents failures from [`ACos::try_acos()`].\n\n# Type Parameters\n\n- `RawComplex`: Implements [`RawComplexTrait`]. Defines input error type via `ErrIn<RawComplex>` and output raw error via `<RawComplex as RawScalarTrait>::ValidationErrors`.\n\n# Variants\n\n- `Input { source: ErrIn<RawComplex> }`: Input was invalid (e.g., components are NaN, Infinity).\n- `Output { source: <RawComplex as RawScalarTrait>::ValidationErrors }`: Computed output was invalid (e.g., components are NaN, Infinity)."];
    [ASinComplexErrors] [ASinComplexInputErrors] ["A type alias for [`FunctionErrors`], specialized for errors during *inverse sine* computation on a *complex number*.\n\nRepresents failures from [`ASin::try_asin()`].\n\n# Type Parameters\n\n- `RawComplex`: Implements [`RawComplexTrait`]. Defines input error type via `ErrIn<RawComplex>` and output raw error via `<RawComplex as RawScalarTrait>::ValidationErrors`.\n\n# Variants\n\n- `Input { source: ErrIn<RawComplex> }`: Input was invalid (e.g., components are NaN, Infinity).\n- `Output { source: <RawComplex as RawScalarTrait>::ValidationErrors }`: Computed output was invalid (e.g., components are NaN, Infinity)."];
    [TanComplexErrors]  [TanComplexInputErrors]  ["A type alias for [`FunctionErrors`], specialized for errors during *tangent* computation on a *complex number*.\n\nRepresents failures from [`Tan::try_tan()`].\n\n# Type Parameters\n\n- `RawComplex`: Implements [`RawComplexTrait`]. Defines input error type via `ErrIn<RawComplex>` and output raw error via `<RawComplex as RawScalarTrait>::ValidationErrors`.\n\n# Variants\n\n- `Input { source: ErrIn<RawComplex> }`: Input was invalid (e.g., components are NaN, Infinity).\n- `Output { source: <RawComplex as RawScalarTrait>::ValidationErrors }`: Computed output was invalid (e.g., components are NaN, Infinity)."];
    [ATanComplexErrors] [ATanComplexInputErrors] ["A type alias for [`FunctionErrors`], specialized for errors during *inverse tangent* computation on a *complex number*.\n\nRepresents failures from [`ATan::try_atan()`].\n\n# Type Parameters\n\n- `RawComplex`: Implements [`RawComplexTrait`]. Defines input error type via `ErrIn<RawComplex>` and output raw error via `<RawComplex as RawScalarTrait>::ValidationErrors`.\n\n# Variants\n\n- `Input { source: ErrIn<RawComplex> }`: Input was invalid (e.g., components are NaN, Infinity, or a pole like `0 +/- 1i`).\n- `Output { source: <RawComplex as RawScalarTrait>::ValidationErrors }`: Computed output was invalid (e.g., components are NaN, Infinity)."];
)]
#[doc = enum_doc]
pub type enum_name<RawComplex> =
    FunctionErrors<ErrIn<RawComplex>, <RawComplex as RawScalarTrait>::ValidationErrors>;

//------------------------------------------------------------------------------------------------
// Trigonometric Traits (Sin, Cos, Tan, ASin, ACos, ATan)
//------------------------------------------------------------------------------------------------
#[duplicate_item(
    T      try_func   func   trait_doc try_func_doc func_doc err_doc;
    [ACos] [try_acos] [acos] ["Trait for computing the *inverse cosine* of a number.\n\nThis trait defines methods for computing the *inverse cosine* of a number. Provides both a fallible method that returns a [`Result`] and a panicking method that directly returns the computed value or panics on invalid input.\n\n# Associated Types\n\n- `Error`: The error type that is returned by the `try_acos` method. This type must implement the [`std::error::Error`] trait.\n\n# Required Methods\n\n - `try_acos`: Computes the inverse cosine of the number and returns a [`Result`]. If the computation is successful, it returns [`Ok`] with the computed value. If an error occurs, it returns [`Err`] with the associated error.\n\n - `acos`: Computes the inverse cosine of the number and directly returns the computed value. In Debug mode this method may panic if the computation fails."]     ["Computes the *inverse cosine* of `self` and returns a [`Result`].\n\nIf the computation is successful, it returns [`Ok`] with the computed value. If an error occurs, it returns [`Err`] with the associated error.\n\n# Errors\n\nThis method returns an error if the computation fails. The error type is defined by the associated [`Error`] type."]  ["Computes and returns the *inverse cosine* of `self`.\n\nIn Debug mode this method may panic if the computation fails.\n\n# Panics\n\nIn Debug mode this method may panic if the computation fails. It is recommended to use the `try_acos` method for safe computations."]  ["The error type that is returned by the `try_acos` method."];
    [ASin] [try_asin] [asin] ["Trait for computing the *inverse sine* of a number.\n\nThis trait defines methods for computing the *inverse sine* of a number. Provides both a fallible method that returns a [`Result`] and a panicking method that directly returns the computed value or panics on invalid input.\n\n# Associated Types\n\n- `Error`: The error type that is returned by the `try_asin` method. This type must implement the [`std::error::Error`] trait.\n\n# Required Methods\n\n - `try_asin`: Computes the inverse sine of the number and returns a [`Result`]. If the computation is successful, it returns [`Ok`] with the computed value. If an error occurs, it returns [`Err`] with the associated error.\n\n - `asin`: Computes the inverse sine of the number and directly returns the computed value. In Debug mode this method may panic if the computation fails."]             ["Computes the *inverse sine* of `self` and returns a [`Result`].\n\nIf the computation is successful, it returns [`Ok`] with the computed value. If an error occurs, it returns [`Err`] with the associated error.\n\n# Errors\n\nThis method returns an error if the computation fails. The error type is defined by the associated [`Error`] type."]    ["Computes and returns the *inverse sine* of `self`.\n\nIn Debug mode this method may panic if the computation fails.\n\n# Panics\n\nIn Debug mode this method may panic if the computation fails. It is recommended to use the `try_asin` method for safe computations."]    ["The error type that is returned by the `try_asin` method."];
    [ATan] [try_atan] [atan] ["Trait for computing the *inverse tangent* of a number.\n\nThis trait defines methods for computing the *inverse tangent* of a number. Provides both a fallible method that returns a [`Result`] and a panicking method that directly returns the computed value or panics on invalid input.\n\n# Associated Types\n\n- `Error`: The error type that is returned by the `try_atan` method. This type must implement the [`std::error::Error`] trait.\n\n# Required Methods\n\n - `try_atan`: Computes the inverse tangent of the number and returns a [`Result`]. If the computation is successful, it returns [`Ok`] with the computed value. If an error occurs, it returns [`Err`] with the associated error.\n\n - `atan`: Computes the inverse tangent of the number and directly returns the computed value. In Debug mode this method may panic if the computation fails."] ["Computes the *inverse tangent* of `self` and returns a [`Result`].\n\nIf the computation is successful, it returns [`Ok`] with the computed value. If an error occurs, it returns [`Err`] with the associated error.\n\n# Errors\n\nThis method returns an error if the computation fails. The error type is defined by the associated [`Error`] type."] ["Computes and returns the *inverse tangent* of `self`.\n\nIn Debug mode this method may panic if the computation fails.\n\n# Panics\n\nIn Debug mode this method may panic if the computation fails. It is recommended to use the `try_atan` method for safe computations."] ["The error type that is returned by the `try_atan` method."];
    [Cos]  [try_cos]  [cos]  ["Trait for computing the *cosine* of a number.\n\nThis trait defines methods for computing the *cosine* of a number. Provides both a fallible method that returns a [`Result`] and a panicking method that directly returns the computed value or panics on invalid input.\n\n# Associated Types\n\n- `Error`: The error type that is returned by the `try_cos` method. This type must implement the [`std::error::Error`] trait.\n\n# Required Methods\n\n - `try_cos`: Computes the cosine of the number and returns a [`Result`]. If the computation is successful, it returns [`Ok`] with the computed value. If an error occurs, it returns [`Err`] with the associated error.\n\n - `cos`: Computes the cosine of the number and directly returns the computed value. In Debug mode this method may panic if the computation fails."]                                        ["Computes the *cosine* of `self` and returns a [`Result`].\n\nIf the computation is successful, it returns [`Ok`] with the computed value. If an error occurs, it returns [`Err`] with the associated error.\n\n# Errors\n\nThis method returns an error if the computation fails. The error type is defined by the associated [`Error`] type."]          ["Computes and returns the *cosine* of `self`.\n\nIn Debug mode this method may panic if the computation fails.\n\n# Panics\n\nIn Debug mode this method may panic if the computation fails. It is recommended to use the `try_cos` method for safe computations."]           ["The error type that is returned by the `try_cos` method."];
    [Sin]  [try_sin]  [sin]  ["Trait for computing the *sine* of a number.\n\nThis trait defines methods for computing the *sine* of a number. Provides both a fallible method that returns a [`Result`] and a panicking method that directly returns the computed value or panics on invalid input.\n\n# Associated Types\n\n- `Error`: The error type that is returned by the `try_sin` method. This type must implement the [`std::error::Error`] trait.\n\n# Required Methods\n\n - `try_sin`: Computes the sine of the number and returns a [`Result`]. If the computation is successful, it returns [`Ok`] with the computed value. If an error occurs, it returns [`Err`] with the associated error.\n\n - `sin`: Computes the sine of the number and directly returns the computed value. In Debug mode this method may panic if the computation fails."]                                                ["Computes the *sine* of `self` and returns a [`Result`].\n\nIf the computation is successful, it returns [`Ok`] with the computed value. If an error occurs, it returns [`Err`] with the associated error.\n\n# Errors\n\nThis method returns an error if the computation fails. The error type is defined by the associated [`Error`] type."]            ["Computes and returns the *sine* of `self`.\n\nIn Debug mode this method may panic if the computation fails.\n\n# Panics\n\nIn Debug mode this method may panic if the computation fails. It is recommended to use the `try_sin` method for safe computations."]             ["The error type that is returned by the `try_sin` method."]; 
    [Tan]  [try_tan]  [tan]  ["Trait for computing the *tangent* of a number.\n\nThis trait defines methods for computing the *tangent* of a number. Provides both a fallible method that returns a [`Result`] and a panicking method that directly returns the computed value or panics on invalid input.\n\n# Associated Types\n\n- `Error`: The error type that is returned by the `try_tan` method. This type must implement the [`std::error::Error`] trait.\n\n# Required Methods\n\n - `try_tan`: Computes the tangent of the number and returns a [`Result`]. If the computation is successful, it returns [`Ok`] with the computed value. If an error occurs, it returns [`Err`] with the associated error.\n\n - `tan`: Computes the tangent of the number and directly returns the computed value. In Debug mode this method may panic if the computation fails."]                                    ["Computes the *tangent* of `self` and returns a [`Result`].\n\nIf the computation is successful, it returns [`Ok`] with the computed value. If an error occurs, it returns [`Err`] with the associated error.\n\n# Errors\n\nThis method returns an error if the computation fails. The error type is defined by the associated [`Error`] type."]         ["Computes and returns the *tangent* of `self`.\n\nIn Debug mode this method may panic if the computation fails.\n\n# Panics\n\nIn Debug mode this method may panic if the computation fails. It is recommended to use the `try_tan` method for safe computations."]          ["The error type that is returned by the `try_tan` method."];
)]
#[doc = trait_doc]
pub trait T: Sized {
    #[doc = err_doc]
    type Error: std::error::Error;

    #[doc = try_func_doc]
    fn try_func(self) -> Result<Self, <Self as T>::Error>;

    #[doc = func_doc]
    fn func(self) -> Self;
}

#[duplicate_item(
    T                trait_name Err                 ErrIn                    try_func   func;
    [f64]            [Sin]      [SinErrors]         [SinInputErrors]         [try_sin]  [sin];
    [f64]            [Cos]      [CosErrors]         [CosInputErrors]         [try_cos]  [cos];
    [f64]            [ATan]     [ATanRealErrors]    [ATanRealInputErrors]    [try_atan] [atan];
    [Complex::<f64>] [Sin]      [SinErrors]         [SinInputErrors]         [try_sin]  [sin];
    [Complex::<f64>] [Cos]      [CosErrors]         [CosInputErrors]         [try_cos]  [cos];
    [Complex::<f64>] [Tan]      [TanComplexErrors]  [TanComplexInputErrors]  [try_tan]  [tan];
    [Complex::<f64>] [ASin]     [ASinComplexErrors] [ASinComplexInputErrors] [try_asin] [asin];
    [Complex::<f64>] [ACos]     [ACosComplexErrors] [ACosComplexInputErrors] [try_acos] [acos];

)]
impl trait_name for T {
    type Error = Err<T>;

    #[inline(always)]
    fn try_func(self) -> Result<Self, Self::Error> {
        StrictFinitePolicy::<T, 53>::validate(self)
            .map_err(|e| ErrIn::InvalidArgument { source: e }.into())
            .and_then(|v| {
                StrictFinitePolicy::<T, 53>::validate(T::func(v))
                    .map_err(|e| Err::Output { source: e })
            })
    }

    #[inline(always)]
    fn func(self) -> Self {
        #[cfg(debug_assertions)]
        {
            self.try_func().unwrap()
        }
        #[cfg(not(debug_assertions))]
        {
            T::func(self)
        }
    }
}

impl Tan for f64 {
    type Error = TanRealErrors<f64>;

    #[inline(always)]
    fn try_tan(self) -> Result<Self, Self::Error> {
        StrictFinitePolicy::<f64, 53>::validate(self)
            .map_err(|e| TanRealInputErrors::InvalidArgument { source: e }.into())
            .and_then(|v| {
                // Check if the cosine of the value is zero, which would indicate a pole
                // for the tangent function (e.g., π/2, 3π/2, etc.).
                // If it is, return an error indicating the argument is a pole.
                // This is a more efficient check than computing the tangent directly.
                // Note: This check is valid because tan(x) = sin(x) / cos(x), and if cos(x) == 0,
                // then tan(x) is undefined (pole).
                if v.cos() == 0. {
                    Err(TanRealInputErrors::ArgumentIsPole {
                        value: v,
                        backtrace: capture_backtrace(),
                    }
                    .into())
                } else {
                    StrictFinitePolicy::<f64, 53>::validate(f64::tan(v))
                        .map_err(|e| TanRealErrors::Output { source: e })
                }
            })
    }

    #[inline(always)]
    fn tan(self) -> Self {
        #[cfg(debug_assertions)]
        {
            self.try_tan().unwrap()
        }
        #[cfg(not(debug_assertions))]
        {
            f64::tan(self)
        }
    }
}

#[duplicate_item(
    T      E                ErrIn                 try_func   func;
    [ASin] [ASinRealErrors] [ASinRealInputErrors] [try_asin] [asin];
    [ACos] [ACosRealErrors] [ACosRealInputErrors] [try_acos] [acos];
)]
impl T for f64 {
    type Error = E<f64>;

    #[inline(always)]
    fn try_func(self) -> Result<Self, <Self as T>::Error> {
        StrictFinitePolicy::<f64, 53>::validate(self)
            .map_err(|e| ErrIn::InvalidArgument { source: e }.into())
            .and_then(|v| {
                if !(-1.0..=1.0).contains(&v) {
                    Err(ErrIn::OutOfDomain {
                        value: v,
                        backtrace: capture_backtrace(),
                    }
                    .into())
                } else {
                    StrictFinitePolicy::<f64, 53>::validate(f64::func(v))
                        .map_err(|e| E::Output { source: e })
                }
            })
    }

    #[inline(always)]
    fn func(self) -> Self {
        #[cfg(debug_assertions)]
        {
            self.try_func().unwrap()
        }
        #[cfg(not(debug_assertions))]
        {
            f64::func(self)
        }
    }
}

impl ATan for Complex<f64> {
    type Error = ATanComplexErrors<Complex<f64>>;

    #[inline(always)]
    fn try_atan(self) -> Result<Self, <Self as ATan>::Error> {
        if self.re == 0. && self.im.abs() == 1. {
            Err(ATanComplexInputErrors::ArgumentIsPole {
                value: self,
                backtrace: capture_backtrace(),
            }
            .into())
        } else {
            StrictFinitePolicy::<Complex<f64>, 53>::validate(self)
                .map_err(|e| ATanComplexInputErrors::InvalidArgument { source: e }.into())
                .and_then(|v| {
                    StrictFinitePolicy::<Complex<f64>, 53>::validate(Complex::<f64>::atan(v))
                        .map_err(|e| Self::Error::Output { source: e })
                })
        }
    }

    #[inline(always)]
    fn atan(self) -> Self {
        #[cfg(debug_assertions)]
        {
            self.try_atan().unwrap()
        }
        #[cfg(not(debug_assertions))]
        {
            Complex::<f64>::atan(self)
        }
    }
}

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

//------------------------------------------------------------------------------------------------
/// Errors that can occur during the input validation phase when attempting to compute
/// the 2-argument arctangent (`atan2`).
///
/// This enum is used as a source for the `Input` variant of [`ATan2Errors`].
/// It is generic over `RawReal: RawRealTrait`, which defines the specific raw real number type
/// (via `RawReal: RawRealTrait`) and its associated validation error type (via `<RawReal as RawScalarTrait>::ValidationErrors`)
/// for the numerator and denominator inputs.
///
/// `atan2(y, x)` computes the principal value of the arctangent of `y/x`, using the
/// signs of both arguments to determine the quadrant of the result.
#[derive(Debug, Error)]
pub enum ATan2InputErrors<RawReal: RawRealTrait> {
    /// The numerator (`y` in `atan2(y, x)`) failed basic validation checks.
    ///
    /// This error occurs if the numerator itself is considered invalid
    /// according to the validation policy (e.g., [`StrictFinitePolicy`]),
    /// such as being NaN or Infinity.
    #[error("the numerator is invalid!")]
    InvalidNumerator {
        /// The underlying validation error from the numerator's raw real type.
        #[source]
        #[backtrace]
        source: <RawReal as RawScalarTrait>::ValidationErrors,
    },

    /// The denominator (`x` in `atan2(y, x)`) failed basic validation checks.
    ///
    /// This error occurs if the denominator itself is considered invalid
    /// according to the validation policy (e.g., [`StrictFinitePolicy`]),
    /// such as being NaN or Infinity.
    #[error("the denominator is invalid!")]
    InvalidDenominator {
        /// The underlying validation error from the denominator's raw real type.
        #[source]
        #[backtrace]
        source: <RawReal as RawScalarTrait>::ValidationErrors,
    },

    /// Both the numerator (`y`) and the denominator (`x`) are zero.
    ///
    /// The `atan2(0, 0)` case is undefined or implementation-specific in many contexts.
    /// This library considers it an error.
    #[error("the numerator and the denominator are both zero!")]
    ZeroOverZero { backtrace: Backtrace },
}

/// A type alias for [`FunctionErrors`], specialized for errors that can occur during
/// the computation of the 2-argument arctangent (`atan2`).
///
/// This type represents the possible failures when calling [`ATan2::try_atan2()`].
///
/// # Generic Parameters
///
/// - `RawReal`: A type that implements [`RawRealTrait`]. This type parameter defines:
///   - The type for the input arguments of `atan2` via [`ATan2InputErrors<RawReal>`].
///   - The raw error type for validating the real number inputs and the output, via `<RawReal as RawScalarTrait>::RawErrors`.
///     This is typically [`crate::validation::ErrorsValidationRawReal<f64>`] for `f64` or a similar type
///     for other numeric backends.
///
/// # Variants
///
/// This type alias wraps [`FunctionErrors`], which has the following variants in this context:
///
/// - `Input { source: ATan2InputErrors<RawReal> }`:
///   Indicates that one or both input arguments (`y` or `x` in `atan2(y, x)`) were invalid.
///   This could be due to:
///     - The numerator failing general validation checks (e.g., NaN, Infinity).
///     - The denominator failing general validation checks (e.g., NaN, Infinity).
///     - Both numerator and denominator being zero.
///
///   The `source` field provides more specific details via [`ATan2InputErrors`].
///
/// - `Output { source: <RawReal as RawScalarTrait>::RawErrors }`:
///   Indicates that the computed result of `atan2` itself failed validation.
///   This typically means the result yielded a non-finite value (e.g., NaN or Infinity),
///   which should generally not happen if the inputs are valid and finite (excluding 0/0).
///   The `source` field contains the raw validation error for the output real number.
pub type ATan2Errors<RawReal> =
    FunctionErrors<ATan2InputErrors<RawReal>, <RawReal as RawScalarTrait>::ValidationErrors>;

/// Trait for computing the [*2-argument arctangent*](https://en.wikipedia.org/wiki/Atan2)
/// of two numbers, `y` (self) and `x` (denominator).
///
/// The `atan2(y, x)` function calculates the principal value of the arctangent of `y/x`,
/// using the signs of both arguments to determine the correct quadrant of the result.
/// The result is an angle in radians, typically in the range `(-π, π]`.
///
/// This trait provides two methods:
/// - [`try_atan2`](ATan2::try_atan2): A fallible version that performs validation on
///   both inputs (numerator `self` and denominator `x`) and potentially the output.
///   It returns a [`Result`]. This is the preferred method for robust error handling.
/// - [`atan2`](ATan2::atan2): A convenient infallible version that panics if `try_atan2`
///   would return an error in debug builds, or directly computes the value in release builds.
///   Use this when inputs are known to be valid or when panicking on error is acceptable.
///
/// # Associated Types
///
/// - `Error`: The error type returned by the [`try_atan2`](ATan2::try_atan2) method.
///   This type must implement [`std::error::Error`]. For `f64`, this is typically
///   [`ATan2Errors<f64>`].
///
/// # Required Methods
///
/// - `try_atan2(self, denominator: &Self) -> Result<Self, Self::Error>`:
///   Attempts to compute `atan2(self, denominator)`.
///   Validates both `self` (numerator) and `denominator` using [`StrictFinitePolicy`].
///   Also considers the case `atan2(0, 0)` as an error ([`ATan2InputErrors::ZeroOverZero`]).
///   The output is also validated using [`StrictFinitePolicy`].
///
/// - `atan2(self, denominator: &Self) -> Self`:
///   Computes `atan2(self, denominator)` directly.
///   In debug builds, this calls `try_atan2` and unwraps.
///   In release builds, this typically calls the underlying standard library's `atan2` function.
///
/// # Example
///
/// ```
/// use num_valid::{functions::ATan2, Native64}; // Assuming Native64 implements ATan2
///
/// let y = 1.0; // Represents the numerator
/// let x = 1.0; // Represents the denominator
///
/// // Using the fallible method
/// match y.try_atan2(&x) {
///     Ok(result) => println!("atan2(y, x): {}", result), // Expected: π/4 (approx 0.785)
///     Err(e) => println!("Error: {:?}", e),
/// }
///
/// // Using the infallible method (panics on error in debug)
/// let result = y.atan2(x);
/// println!("atan2(y, x): {}", result);
///
/// // Example of an error case (0/0)
/// let y_zero = 0.0;
/// let x_zero = 0.0;
/// match y_zero.try_atan2(&x_zero) {
///     Ok(_) => println!("This should not happen for 0/0"),
///     Err(e) => println!("Error for atan2(0,0): {:?}", e), // Expected: ZeroOverZero error
/// }
/// ```
pub trait ATan2: Sized {
    /// The error type that is returned by the `try_atan2` method.
    type Error: std::error::Error;

    /// Computes the arctangent of `self` (numerator `y`) and `denominator` (`x`),
    /// returning a [`Result`].
    ///
    /// This method validates both inputs to ensure they are finite numbers
    /// (not NaN, Infinity, or subnormal) using the [`StrictFinitePolicy`].
    /// The specific case where both `self` and `denominator` are zero is
    /// considered an error ([`ATan2InputErrors::ZeroOverZero`]).
    /// The computed result is also validated to be a finite number.
    ///
    /// # Arguments
    ///
    /// * `self`: The numerator `y` of the `atan2(y, x)` operation.
    /// * `denominator`: A reference to the denominator `x` of the `atan2(y, x)` operation.
    ///
    /// # Errors
    ///
    /// Returns `Err(Self::Error)` if:
    /// - Either `self` or `denominator` fails validation (e.g., NaN, Infinity).
    /// - Both `self` and `denominator` are zero.
    /// - The computed result fails validation (e.g., results in NaN or Infinity,
    ///   though this is less common for `atan2` with valid finite inputs).
    fn try_atan2(self, denominator: &Self) -> Result<Self, Self::Error>;

    /// Computes the arctangent of `self` (numerator `y`) and `denominator` (`x`),
    /// returning the result directly.
    fn atan2(self, denominator: &Self) -> Self;
}

impl ATan2 for f64 {
    /// The error type that is returned by the `try_atan2` method.
    type Error = ATan2Errors<f64>;

    /// Computes the arctangent of `self` (numerator `y`) and `denominator` (`x`),
    /// returning a [`Result`].
    ///
    /// This method validates both inputs to ensure they are finite numbers
    /// (not NaN, Infinity, or subnormal) using the [`StrictFinitePolicy`].
    /// The specific case where both `self` and `denominator` are zero is
    /// considered an error ([`ATan2InputErrors::ZeroOverZero`]).
    /// The computed result is also validated to be a finite number.
    ///
    /// # Arguments
    ///
    /// * `self`: The numerator `y` of the `atan2(y, x)` operation.
    /// * `denominator`: A reference to the denominator `x` of the `atan2(y, x)` operation.
    ///
    /// # Errors
    ///
    /// Returns `Err(Self::Error)` if:
    /// - Either `self` or `denominator` fails validation (e.g., NaN, Infinity).
    /// - Both `self` and `denominator` are zero.
    /// - The computed result fails validation (e.g., results in NaN or Infinity,
    ///   though this is less common for `atan2` with valid finite inputs).
    ///
    /// # Note on Infinite Inputs
    ///
    /// This implementation, using `StrictFinitePolicy`, rejects infinite inputs and
    /// returns an `InvalidArgument` error. This differs from some standard library
    /// implementations (like `libm` or `std::f64::atan2`) which may return specific
    /// values for infinite arguments.
    fn try_atan2(self, denominator: &Self) -> Result<Self, Self::Error> {
        // Validate the denominator
        let denominator = StrictFinitePolicy::<f64, 53>::validate(*denominator)
            .map_err(|e| ATan2InputErrors::InvalidDenominator { source: e })?;

        // Validate the numerator
        let numerator = StrictFinitePolicy::<f64, 53>::validate(self)
            .map_err(|e| ATan2InputErrors::InvalidNumerator { source: e })?;

        // Check for the specific case of 0/0
        // This is a special case that is often considered undefined or implementation-specific.
        // Here, we treat it as an error.
        // Note: This is different from the standard library's behavior, which returns NaN.
        if numerator == 0.0 && denominator == 0.0 {
            Err(ATan2InputErrors::ZeroOverZero {
                backtrace: capture_backtrace(),
            }
            .into())
        } else {
            StrictFinitePolicy::<f64, 53>::validate(f64::atan2(self, denominator))
                .map_err(|e| ATan2Errors::Output { source: e })
        }
    }

    /// Computes the arctangent of `self` (numerator `y`) and `denominator` (`x`),
    /// returning the result directly.
    ///
    /// # Behavior
    ///
    /// - In **debug builds** (`#[cfg(debug_assertions)]`): This method calls
    ///   [`try_atan2()`](ATan2::try_atan2) and unwraps the result. It will panic if `try_atan2`
    ///   returns an error.
    /// - In **release builds** (`#[cfg(not(debug_assertions))]`): This method typically
    ///   calls the underlying standard library's `atan2` function directly for performance,
    ///   bypassing the explicit validations performed by `try_atan2`.
    ///
    /// # Arguments
    ///
    /// * `self`: The numerator `y` of the `atan2(y, x)` operation.
    /// * `denominator`: A reference to the denominator `x` of the `atan2(y, x)` operation.
    ///
    /// # Panics
    ///
    /// This method will panic in debug builds if `try_atan2()` would return an `Err`.
    /// In release builds, the behavior for invalid inputs (like NaN or Infinity)
    /// will match the underlying standard library function (e.g., `f64::atan2(f64::NAN, 1.0)` is `NAN`).
    fn atan2(self, denominator: &Self) -> Self {
        #[cfg(debug_assertions)]
        {
            self.try_atan2(denominator).unwrap()
        }
        #[cfg(not(debug_assertions))]
        {
            f64::atan2(self, *denominator)
        }
    }
}
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
/// A convenience trait that aggregates the standard trigonometric functions and their inverses.
///
/// This trait serves as a shorthand for requiring a type to implement all the fundamental
/// trigonometric operations:
/// - [`Sin`] and [`ASin`]
/// - [`Cos`] and [`ACos`]
/// - [`Tan`] and [`ATan`]
///
/// It is primarily used as a super-trait for [`FpScalar`](crate::FpScalar) to simplify trait bounds
/// and ensure that any scalar type in the library provides a comprehensive set of trigonometric
/// capabilities. By using `TrigonometricFunctions` as a bound, you can write generic functions that
/// utilize any of its constituent trait methods.
///
/// # Examples
///
/// ```
/// use num_valid::{FpScalar ,functions::{TrigonometricFunctions, Sin, Cos}};
/// use std::ops::{Add, Mul};
///
/// // A generic function that verifies the identity sin(x)^2 + cos(x)^2 = 1.
/// // We bound T by FpScalar which implies TrigonometricFunctions, Clone, and arithmetic ops.
/// fn verify_trig_identity<T>(x: T) -> T
/// where
///     T: TrigonometricFunctions + Clone + Mul<Output = T> + Add<Output = T>,
/// {
///     let sin_x = x.clone().sin();
///     let cos_x = x.cos();
///     // This works because FpScalar requires the necessary arithmetic traits.
///     sin_x.clone() * sin_x + cos_x.clone() * cos_x
/// }
///
/// let angle = 0.5f64;
/// let identity = verify_trig_identity(angle);
///
/// // The result should be very close to 1.0.
/// assert!((identity - 1.0).abs() < 1e-15);
/// ```
pub trait TrigonometricFunctions: Sin + ASin + Cos + ACos + Tan + ATan {}

#[duplicate_item(
    T;
    [f64];
    [Complex<f64>];
)]
impl TrigonometricFunctions for T {}
//------------------------------------------------------------------------------------------------

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

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

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

    mod sin {
        use super::*;

        mod native64 {
            use super::*;

            mod real {
                use super::*;

                #[test]
                fn sin_valid() {
                    let value = 4.0;
                    let expected_result = if cfg!(target_arch = "x86_64") {
                        -0.7568024953079282
                    } else if cfg!(target_arch = "aarch64") {
                        -0.7568024953079283
                    } else {
                        todo!("Unknown target architecture");
                    };
                    assert_eq!(value.try_sin().unwrap(), expected_result);
                    assert_eq!(value.sin(), expected_result);
                }

                #[test]
                fn sin_negative() {
                    let value = -4.0;
                    let expected_result = if cfg!(target_arch = "x86_64") {
                        0.7568024953079282
                    } else if cfg!(target_arch = "aarch64") {
                        0.7568024953079283
                    } else {
                        todo!("Unknown target architecture");
                    };
                    assert_eq!(value.try_sin().unwrap(), expected_result);
                    assert_eq!(value.sin(), expected_result);
                }

                #[test]
                fn sin_zero() {
                    let value = 0.0;
                    assert_eq!(value.try_sin().unwrap(), 0.0);
                    assert_eq!(value.sin(), 0.0);
                }

                #[test]
                fn sin_nan() {
                    let value = f64::NAN;
                    let result = value.try_sin();
                    assert!(matches!(result, Err(SinErrors::Input { .. })));
                }

                #[test]
                fn sin_infinity() {
                    let value = f64::INFINITY;
                    assert!(matches!(value.try_sin(), Err(SinErrors::Input { .. })));
                }

                #[test]
                fn sin_subnormal() {
                    let value = f64::MIN_POSITIVE / 2.0;
                    assert!(matches!(value.try_sin(), Err(SinErrors::Input { .. })));
                }
            }

            mod complex {
                use super::*;

                #[test]
                fn sin_valid() {
                    let value = Complex::new(4.0, 1.0);
                    let expected_result = if cfg!(target_arch = "x86_64") {
                        Complex::new(-1.1678072748895183, -0.7681627634565731)
                    } else if cfg!(target_arch = "aarch64") {
                        Complex::new(-1.1678072748895185, -0.7681627634565731)
                    } else {
                        todo!("Unknown target architecture");
                    };

                    assert_eq!(value.try_sin().unwrap(), expected_result);
                    assert_eq!(<Complex<f64> as Sin>::sin(value), expected_result);
                    assert_eq!(value.sin(), expected_result);
                }

                #[test]
                fn sin_zero() {
                    let value = Complex::new(0.0, 0.0);
                    let expected_result = Complex::new(0.0, 0.0);
                    assert_eq!(value.try_sin().unwrap(), expected_result);
                    assert_eq!(value.sin(), expected_result);
                }

                #[test]
                fn sin_nan() {
                    let value = Complex::new(f64::NAN, 0.0);
                    assert!(matches!(value.try_sin(), Err(SinErrors::Input { .. })));

                    let value = Complex::new(0.0, f64::NAN);
                    assert!(matches!(value.try_sin(), Err(SinErrors::Input { .. })));
                }

                #[test]
                fn sin_infinity() {
                    let value = Complex::new(f64::INFINITY, 0.0);
                    assert!(matches!(value.try_sin(), Err(SinErrors::Input { .. })));

                    let value = Complex::new(0.0, f64::INFINITY);
                    assert!(matches!(value.try_sin(), Err(SinErrors::Input { .. })));
                }

                #[test]
                fn sin_subnormal() {
                    let value = Complex::new(f64::MIN_POSITIVE / 2.0, 0.0);
                    assert!(matches!(value.try_sin(), Err(SinErrors::Input { .. })));

                    let value = Complex::new(0.0, f64::MIN_POSITIVE / 2.0);
                    assert!(matches!(value.try_sin(), Err(SinErrors::Input { .. })));
                }
            }
        }

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

            mod real {
                use super::*;

                #[test]
                fn test_rug_float_sin_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,
                        7.568024953079282e-1,
                    ))
                    .unwrap();
                    assert_eq!(value.clone().try_sin().unwrap(), expected_result);
                    assert_eq!(value.sin(), expected_result);
                }

                #[test]
                fn test_rug_float_sin_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_sin().unwrap(), expected_result);
                    assert_eq!(value.sin(), expected_result);
                }
            }

            mod complex {
                use super::*;

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

                    let expected_result =
                        ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                            53,
                            (
                                rug::Float::with_val(53, -1.1678072748895185),
                                rug::Float::with_val(53, -7.681627634565731e-1),
                            ),
                        ))
                        .unwrap();
                    assert_eq!(value.clone().try_sin().unwrap(), expected_result);
                    assert_eq!(value.sin(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_sin_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 =
                        ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                            53,
                            (rug::Float::with_val(53, 0.), rug::Float::with_val(53, 0.)),
                        ))
                        .unwrap();
                    assert_eq!(value.clone().try_sin().unwrap(), expected_result);
                    assert_eq!(value.sin(), expected_result);
                }
            }
        }
    }

    mod cos {
        use super::*;

        mod native64 {
            use super::*;

            mod real {
                use super::*;

                #[test]
                fn cos_valid() {
                    let value = 4.0;
                    let expected_result = -0.6536436208636119;
                    assert_eq!(value.try_cos().unwrap(), expected_result);
                    assert_eq!(value.cos(), expected_result);
                }

                #[test]
                fn cos_negative() {
                    let value = -4.0;
                    let expected_result = -0.6536436208636119;
                    assert_eq!(value.try_cos().unwrap(), expected_result);
                    assert_eq!(value.cos(), expected_result);
                }

                #[test]
                fn cos_zero() {
                    let value = 0.0;
                    assert_eq!(value.try_cos().unwrap(), 1.0);
                    assert_eq!(value.cos(), 1.0);
                }

                #[test]
                fn cos_nan() {
                    let value = f64::NAN;
                    let result = value.try_cos();
                    assert!(matches!(result, Err(CosErrors::Input { .. })));
                }

                #[test]
                fn cos_infinity() {
                    let value = f64::INFINITY;
                    assert!(matches!(value.try_cos(), Err(CosErrors::Input { .. })));
                }

                #[test]
                fn cos_subnormal() {
                    let value = f64::MIN_POSITIVE / 2.0;
                    assert!(matches!(value.try_cos(), Err(CosErrors::Input { .. })));
                }
            }

            mod complex {
                use super::*;

                #[test]
                fn cos_valid() {
                    let value = Complex::new(4.0, 1.0);
                    let expected_result = if cfg!(target_arch = "x86_64") {
                        Complex::new(-1.0086248134251568, 0.8893951958384846)
                    } else if cfg!(target_arch = "aarch64") {
                        Complex::new(-1.0086248134251568, 0.8893951958384847)
                    } else {
                        todo!("Unknown target architecture");
                    };
                    assert_eq!(value.try_cos().unwrap(), expected_result);
                    assert_eq!(<Complex<f64> as Cos>::cos(value), expected_result);
                    assert_eq!(value.cos(), expected_result);
                }

                #[test]
                fn cos_zero() {
                    let value = Complex::new(0.0, 0.0);
                    let expected_result = Complex::new(1.0, 0.0);
                    assert_eq!(value.try_cos().unwrap(), expected_result);
                    assert_eq!(value.cos(), expected_result);
                }

                #[test]
                fn cos_nan() {
                    let value = Complex::new(f64::NAN, 0.0);
                    assert!(matches!(value.try_cos(), Err(CosErrors::Input { .. })));

                    let value = Complex::new(0.0, f64::NAN);
                    assert!(matches!(value.try_cos(), Err(CosErrors::Input { .. })));
                }

                #[test]
                fn cos_infinity() {
                    let value = Complex::new(f64::INFINITY, 0.0);
                    assert!(matches!(value.try_cos(), Err(CosErrors::Input { .. })));

                    let value = Complex::new(0.0, f64::INFINITY);
                    assert!(matches!(value.try_cos(), Err(CosErrors::Input { .. })));
                }

                #[test]
                fn cos_subnormal() {
                    let value = Complex::new(f64::MIN_POSITIVE / 2.0, 0.0);
                    assert!(matches!(value.try_cos(), Err(CosErrors::Input { .. })));

                    let value = Complex::new(0.0, f64::MIN_POSITIVE / 2.0);
                    assert!(matches!(value.try_cos(), Err(CosErrors::Input { .. })));
                }
            }
        }

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

            mod real {
                use super::*;

                #[test]
                fn test_rug_float_cos_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,
                        -0.6536436208636119,
                    ))
                    .unwrap();
                    assert_eq!(value.clone().try_cos().unwrap(), expected_result);
                    assert_eq!(value.cos(), expected_result);
                }

                #[test]
                fn test_rug_float_cos_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, 1.0)).unwrap();
                    assert_eq!(value.clone().try_cos().unwrap(), expected_result);
                    assert_eq!(value.cos(), expected_result);
                }
            }

            mod complex {
                use super::*;

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

                    let expected_result =
                        ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                            53,
                            (
                                rug::Float::with_val(53, -1.0086248134251568),
                                rug::Float::with_val(53, 8.893951958384847e-1),
                            ),
                        ))
                        .unwrap();
                    assert_eq!(value.clone().try_cos().unwrap(), expected_result);
                    assert_eq!(value.cos(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_cos_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 =
                        ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                            53,
                            (rug::Float::with_val(53, 1.), rug::Float::with_val(53, 0.)),
                        ))
                        .unwrap();
                    assert_eq!(value.clone().try_cos().unwrap(), expected_result);
                    assert_eq!(value.cos(), expected_result);
                }
            }
        }
    }

    mod tan {
        use super::*;

        mod native64 {
            use super::*;

            mod real {
                use super::*;

                #[test]
                fn tan_valid() {
                    let value = 4.0;
                    let expected_result = 1.1578212823495775;
                    assert_eq!(value.try_tan().unwrap(), expected_result);
                    assert_eq!(<f64 as Tan>::tan(value), expected_result);
                    assert_eq!(value.tan(), expected_result);
                }

                /*
                #[test]
                #[ignore = "at the moment we cannot generate a pole for the Tan function"]
                fn tan_argument_pole() {
                    let value = f64::pi_div_2();
                    let err = value.try_tan().unwrap_err();
                    assert!(matches!(
                        err,
                        TanRealErrors::Input {
                            source: TanRealInputErrors::ArgumentIsPole { .. }
                        }
                    ));
                }
                */

                #[test]
                fn tan_negative() {
                    let value = -4.0;
                    let expected_result = -1.1578212823495775;
                    assert_eq!(value.try_tan().unwrap(), expected_result);
                    assert_eq!(value.tan(), expected_result);
                }

                #[test]
                fn tan_zero() {
                    let value = 0.0;
                    assert_eq!(value.try_tan().unwrap(), 0.0);
                    assert_eq!(value.tan(), 0.0);
                }

                #[test]
                fn tan_nan() {
                    let value = f64::NAN;
                    let result = value.try_tan();
                    assert!(matches!(result, Err(TanRealErrors::Input { .. })));
                }

                #[test]
                fn tan_infinity() {
                    let value = f64::INFINITY;
                    assert!(matches!(value.try_tan(), Err(TanRealErrors::Input { .. })));
                }

                #[test]
                fn tan_subnormal() {
                    let value = f64::MIN_POSITIVE / 2.0;
                    assert!(matches!(value.try_tan(), Err(TanRealErrors::Input { .. })));
                }
            }

            mod complex {
                use super::*;

                #[test]
                fn tan_valid() {
                    let value = Complex::new(4.0, 1.0);
                    let expected_result = Complex::new(0.27355308280730734, 1.002810507583505);
                    assert_eq!(value.try_tan().unwrap(), expected_result);
                    assert_eq!(<Complex<f64> as Tan>::tan(value), expected_result);
                    assert_eq!(value.tan(), expected_result);
                }

                #[test]
                fn tan_zero() {
                    let value = Complex::new(0.0, 0.0);
                    let expected_result = Complex::new(0.0, 0.0);
                    assert_eq!(value.try_tan().unwrap(), expected_result);
                    assert_eq!(value.tan(), expected_result);
                }

                #[test]
                fn tan_nan() {
                    let value = Complex::new(f64::NAN, 0.0);
                    assert!(matches!(
                        value.try_tan(),
                        Err(TanComplexErrors::Input { .. })
                    ));

                    let value = Complex::new(0.0, f64::NAN);
                    assert!(matches!(
                        value.try_tan(),
                        Err(TanComplexErrors::Input { .. })
                    ));
                }

                #[test]
                fn tan_infinity() {
                    let value = Complex::new(f64::INFINITY, 0.0);
                    assert!(matches!(
                        value.try_tan(),
                        Err(TanComplexErrors::Input { .. })
                    ));

                    let value = Complex::new(0.0, f64::INFINITY);
                    assert!(matches!(
                        value.try_tan(),
                        Err(TanComplexErrors::Input { .. })
                    ));
                }

                #[test]
                fn tan_subnormal() {
                    let value = Complex::new(f64::MIN_POSITIVE / 2.0, 0.0);
                    assert!(matches!(
                        value.try_tan(),
                        Err(TanComplexErrors::Input { .. })
                    ));

                    let value = Complex::new(0.0, f64::MIN_POSITIVE / 2.0);
                    assert!(matches!(
                        value.try_tan(),
                        Err(TanComplexErrors::Input { .. })
                    ));
                }
            }
        }

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

            mod real {
                use super::*;

                #[test]
                fn test_rug_float_tan_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,
                        -1.1578212823495775,
                    ))
                    .unwrap();
                    assert_eq!(value.clone().try_tan().unwrap(), expected_result);
                    assert_eq!(value.tan(), expected_result);
                }

                #[cfg(feature = "rug")]
                #[test]
                fn test_rug_float_tan_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_tan().unwrap(), expected_result);
                    assert_eq!(value.tan(), expected_result);
                }

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


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

            mod complex {
                use super::*;

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

                    let expected_result =
                        ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                            53,
                            (
                                rug::Float::with_val(53, 2.7355308280730734e-1),
                                rug::Float::with_val(53, 1.002810507583505),
                            ),
                        ))
                        .unwrap();
                    assert_eq!(value.clone().try_tan().unwrap(), expected_result);
                    assert_eq!(value.tan(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_tan_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 =
                        ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                            53,
                            (rug::Float::with_val(53, 0.), rug::Float::with_val(53, 0.)),
                        ))
                        .unwrap();
                    assert_eq!(value.clone().try_tan().unwrap(), expected_result);
                    assert_eq!(value.tan(), expected_result);
                }

                /*

                #[test]
                fn test_complex_rug_float_tan_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_tan(),
                        Err(TanComplexErrors::Input { .. })
                    ));
                }

                #[test]
                fn test_complex_rug_float_tan_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_tan(),
                        Err(TanComplexErrors::Input { .. })
                    ));
                }
                */
            }
        }
    }

    mod atan {
        use super::*;

        mod native64 {
            use super::*;

            mod real {
                use super::*;

                #[test]
                fn atan_valid() {
                    let value = 4.0;
                    let expected_result = 1.3258176636680326;
                    assert_eq!(value.try_atan().unwrap(), expected_result);
                    assert_eq!(value.atan(), expected_result);
                }

                #[test]
                fn atan_negative() {
                    let value = -4.0;
                    let expected_result = -1.3258176636680326;
                    assert_eq!(value.try_atan().unwrap(), expected_result);
                    assert_eq!(value.atan(), expected_result);
                }

                #[test]
                fn atan_zero() {
                    let value = 0.0;
                    assert_eq!(value.try_atan().unwrap(), 0.0);
                    assert_eq!(value.atan(), 0.0);
                }

                #[test]
                fn atan_nan() {
                    let value = f64::NAN;
                    let result = value.try_atan();
                    assert!(matches!(result, Err(ATanRealErrors::Input { .. })));
                }

                #[test]
                fn atan_infinity() {
                    let value = f64::INFINITY;
                    assert!(matches!(
                        value.try_atan(),
                        Err(ATanRealErrors::Input { .. })
                    ));
                }

                #[test]
                fn atan_subnormal() {
                    let value = f64::MIN_POSITIVE / 2.0;
                    assert!(matches!(
                        value.try_atan(),
                        Err(ATanRealErrors::Input { .. })
                    ));
                }
            }

            mod complex {
                use super::*;

                #[test]
                fn atan_valid() {
                    let value = Complex::new(4.0, 1.0);
                    let expected_result = Complex::new(1.3389725222944935, 0.05578588782855254);
                    assert_eq!(value.try_atan().unwrap(), expected_result);
                    assert_eq!(<Complex<f64> as ATan>::atan(value), expected_result);
                    assert_eq!(value.atan(), expected_result);
                }

                #[test]
                fn atan_out_of_domain() {
                    let value = Complex::new(0.0, 1.0);
                    let err = value.try_atan().unwrap_err();
                    assert!(matches!(
                        err,
                        ATanComplexErrors::Input {
                            source: ATanComplexInputErrors::ArgumentIsPole { .. }
                        }
                    ));

                    let value = Complex::new(0.0, -1.0);
                    let err = value.try_atan().unwrap_err();
                    assert!(matches!(
                        err,
                        ATanComplexErrors::Input {
                            source: ATanComplexInputErrors::ArgumentIsPole { .. }
                        }
                    ));
                }

                #[test]
                fn atan_zero() {
                    let value = Complex::new(0.0, 0.0);
                    let expected_result = Complex::new(0.0, 0.0);
                    assert_eq!(value.try_atan().unwrap(), expected_result);
                    assert_eq!(value.atan(), expected_result);
                }

                #[test]
                fn atan_nan() {
                    let value = Complex::new(f64::NAN, 0.0);
                    assert!(matches!(
                        value.try_atan(),
                        Err(ATanComplexErrors::Input { .. })
                    ));

                    let value = Complex::new(0.0, f64::NAN);
                    assert!(matches!(
                        value.try_atan(),
                        Err(ATanComplexErrors::Input { .. })
                    ));
                }

                #[test]
                fn atan_infinity() {
                    let value = Complex::new(f64::INFINITY, 0.0);
                    assert!(matches!(
                        value.try_atan(),
                        Err(ATanComplexErrors::Input { .. })
                    ));

                    let value = Complex::new(0.0, f64::INFINITY);
                    assert!(matches!(
                        value.try_atan(),
                        Err(ATanComplexErrors::Input { .. })
                    ));
                }

                #[test]
                fn atan_subnormal() {
                    let value = Complex::new(f64::MIN_POSITIVE / 2.0, 0.0);
                    assert!(matches!(
                        value.try_atan(),
                        Err(ATanComplexErrors::Input { .. })
                    ));

                    let value = Complex::new(0.0, f64::MIN_POSITIVE / 2.0);
                    assert!(matches!(
                        value.try_atan(),
                        Err(ATanComplexErrors::Input { .. })
                    ));
                }
            }
        }

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

            mod real {
                use super::*;

                #[test]
                fn test_rug_float_atan_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,
                        -1.3258176636680326,
                    ))
                    .unwrap();
                    assert_eq!(value.clone().try_atan().unwrap(), expected_result);
                    assert_eq!(value.atan(), expected_result);
                }

                #[test]
                fn test_rug_float_atan_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_atan().unwrap(), expected_result);
                    assert_eq!(value.atan(), expected_result);
                }
            }

            mod complex {
                use super::*;

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

                    let expected_result =
                        ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                            53,
                            (
                                rug::Float::with_val(53, 1.3389725222944935),
                                rug::Float::with_val(53, 5.578588782855244e-2),
                            ),
                        ))
                        .unwrap();
                    assert_eq!(value.clone().try_atan().unwrap(), expected_result);
                    assert_eq!(value.atan(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_atan_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 =
                        ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                            53,
                            (rug::Float::with_val(53, 0.), rug::Float::with_val(53, 0.)),
                        ))
                        .unwrap();
                    assert_eq!(value.clone().try_atan().unwrap(), expected_result);
                    assert_eq!(value.atan(), expected_result);
                }
            }
        }
    }

    mod asin {
        use super::*;

        mod native64 {
            use super::*;

            mod real {
                use super::*;

                #[test]
                fn asin_valid() {
                    let value = 0.5;
                    let expected_result = if cfg!(target_arch = "x86_64") {
                        std::f64::consts::FRAC_PI_6
                    } else if cfg!(target_arch = "aarch64") {
                        0.5235987755982988
                    } else {
                        todo!("Unknown target architecture");
                    };

                    assert_eq!(value.try_asin().unwrap(), expected_result);
                    assert_eq!(value.asin(), expected_result);
                }

                #[test]
                fn asin_negative() {
                    let value = -0.5;
                    let expected_result = if cfg!(target_arch = "x86_64") {
                        -std::f64::consts::FRAC_PI_6
                    } else if cfg!(target_arch = "aarch64") {
                        -0.5235987755982988
                    } else {
                        todo!("Unknown target architecture");
                    };

                    assert_eq!(value.try_asin().unwrap(), expected_result);
                    assert_eq!(value.asin(), expected_result);
                }

                #[test]
                fn asin_zero() {
                    let value = 0.0;
                    assert_eq!(value.try_asin().unwrap(), 0.0);
                    assert_eq!(value.asin(), 0.0);
                }

                #[test]
                fn test_rug_float_asin_out_of_bound() {
                    let value = 2.0;
                    let result = value.try_asin();
                    println!("result: {result:?}");
                    assert!(matches!(result, Err(ASinRealErrors::Input { .. })));
                }

                #[test]
                fn asin_nan() {
                    let value = f64::NAN;
                    let result = value.try_asin();
                    assert!(matches!(result, Err(ASinRealErrors::Input { .. })));
                }

                #[test]
                fn asin_infinity() {
                    let value = f64::INFINITY;
                    assert!(matches!(
                        value.try_asin(),
                        Err(ASinRealErrors::Input { .. })
                    ));
                }

                #[test]
                fn asin_subnormal() {
                    let value = f64::MIN_POSITIVE / 2.0;
                    assert!(matches!(
                        value.try_asin(),
                        Err(ASinRealErrors::Input { .. })
                    ));
                }
            }

            mod complex {
                use super::*;

                #[test]
                fn asin_valid() {
                    let value = Complex::new(0.5, 0.5);
                    let expected_result = Complex::new(0.4522784471511907, 0.5306375309525178);
                    assert_eq!(value.try_asin().unwrap(), expected_result);
                    assert_eq!(<Complex<f64> as ASin>::asin(value), expected_result);
                    assert_eq!(value.asin(), expected_result);
                }

                #[test]
                fn asin_zero() {
                    let value = Complex::new(0.0, 0.0);
                    let expected_result = Complex::new(0.0, 0.0);
                    assert_eq!(value.try_asin().unwrap(), expected_result);
                    assert_eq!(value.asin(), expected_result);
                }

                #[test]
                fn asin_nan() {
                    let value = Complex::new(f64::NAN, 0.0);
                    assert!(matches!(
                        value.try_asin(),
                        Err(ASinComplexErrors::Input { .. })
                    ));

                    let value = Complex::new(0.0, f64::NAN);
                    assert!(matches!(
                        value.try_asin(),
                        Err(ASinComplexErrors::Input { .. })
                    ));
                }

                #[test]
                fn asin_infinity() {
                    let value = Complex::new(f64::INFINITY, 0.0);
                    assert!(matches!(
                        value.try_asin(),
                        Err(ASinComplexErrors::Input { .. })
                    ));

                    let value = Complex::new(0.0, f64::INFINITY);
                    assert!(matches!(
                        value.try_asin(),
                        Err(ASinComplexErrors::Input { .. })
                    ));
                }

                #[test]
                fn asin_subnormal() {
                    let value = Complex::new(f64::MIN_POSITIVE / 2.0, 0.0);
                    assert!(matches!(
                        value.try_asin(),
                        Err(ASinComplexErrors::Input { .. })
                    ));

                    let value = Complex::new(0.0, f64::MIN_POSITIVE / 2.0);
                    assert!(matches!(
                        value.try_asin(),
                        Err(ASinComplexErrors::Input { .. })
                    ));
                }
            }
        }

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

            mod real {
                use super::*;

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

                    let expected_result = RealRugStrictFinite::<53>::try_new(rug::Float::with_val(
                        53,
                        std::f64::consts::FRAC_PI_6,
                    ))
                    .unwrap();
                    assert_eq!(value.clone().try_asin().unwrap(), expected_result);
                    assert_eq!(value.asin(), expected_result);
                }

                #[test]
                fn test_rug_float_asin_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_asin().unwrap(), expected_result);
                    assert_eq!(value.asin(), expected_result);
                }

                #[test]
                fn test_rug_float_asin_out_of_bound() {
                    let value =
                        RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 2.0)).unwrap();
                    let result = value.try_asin();
                    println!("result: {result:?}");
                    assert!(matches!(result, Err(ASinRealErrors::Input { .. })));
                }
            }

            mod complex {
                use super::*;

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

                    let expected_result =
                        ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                            53,
                            (
                                rug::Float::with_val(53, 0.4522784471511907),
                                rug::Float::with_val(53, 0.5306375309525179),
                            ),
                        ))
                        .unwrap();
                    assert_eq!(value.clone().try_asin().unwrap(), expected_result);
                    assert_eq!(value.asin(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_asin_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 =
                        ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                            53,
                            (rug::Float::with_val(53, 0.), rug::Float::with_val(53, 0.)),
                        ))
                        .unwrap();
                    assert_eq!(value.clone().try_asin().unwrap(), expected_result);
                    assert_eq!(value.asin(), expected_result);
                }
            }
        }
    }

    mod acos {
        use super::*;

        mod native64 {
            use super::*;

            mod real {
                use super::*;

                #[test]
                fn acos_valid() {
                    let value = 0.5;
                    let expected_result = if cfg!(target_arch = "x86_64") {
                        std::f64::consts::FRAC_PI_3
                    } else if cfg!(target_arch = "aarch64") {
                        1.0471975511965976
                    } else {
                        todo!("Unknown target architecture");
                    };

                    assert_eq!(value.try_acos().unwrap(), expected_result);
                    assert_eq!(value.acos(), expected_result);
                }

                #[test]
                fn acos_negative() {
                    let value = -0.5;
                    let expected_result = 2.0943951023931957;
                    assert_eq!(value.try_acos().unwrap(), expected_result);
                    assert_eq!(value.acos(), expected_result);
                }

                #[test]
                fn acos_zero() {
                    let value = 0.0;
                    let expected_result = std::f64::consts::FRAC_PI_2;
                    assert_eq!(value.try_acos().unwrap(), expected_result);
                    assert_eq!(value.acos(), expected_result);
                }

                #[test]
                fn test_rug_float_acos_out_of_bound() {
                    let value = 2.0;
                    let result = value.try_acos();
                    println!("result: {result:?}");
                    assert!(matches!(result, Err(ACosRealErrors::Input { .. })));
                }

                #[test]
                fn acos_nan() {
                    let value = f64::NAN;
                    let result = value.try_acos();
                    assert!(matches!(result, Err(ACosRealErrors::Input { .. })));
                }

                #[test]
                fn acos_infinity() {
                    let value = f64::INFINITY;
                    assert!(matches!(
                        value.try_acos(),
                        Err(ACosRealErrors::Input { .. })
                    ));
                }

                #[test]
                fn acos_subnormal() {
                    let value = f64::MIN_POSITIVE / 2.0;
                    assert!(matches!(
                        value.try_acos(),
                        Err(ACosRealErrors::Input { .. })
                    ));
                }
            }

            mod complex {
                use super::*;

                #[test]
                fn acos_valid() {
                    let value = Complex::new(0.5, 0.5);
                    let expected_result = Complex::new(1.118517879643706, -0.5306375309525179);
                    assert_eq!(value.try_acos().unwrap(), expected_result);
                    assert_eq!(<Complex<f64> as ACos>::acos(value), expected_result);
                    assert_eq!(value.acos(), expected_result);
                }

                #[test]
                fn acos_zero() {
                    let value = Complex::new(0.0, 0.0);
                    let expected_result = Complex::new(std::f64::consts::FRAC_PI_2, 0.0);
                    assert_eq!(value.try_acos().unwrap(), expected_result);
                    assert_eq!(value.acos(), expected_result);
                }

                #[test]
                fn acos_nan() {
                    let value = Complex::new(f64::NAN, 0.0);
                    assert!(matches!(
                        value.try_acos(),
                        Err(ACosComplexErrors::Input { .. })
                    ));

                    let value = Complex::new(0.0, f64::NAN);
                    assert!(matches!(
                        value.try_acos(),
                        Err(ACosComplexErrors::Input { .. })
                    ));
                }

                #[test]
                fn acos_infinity() {
                    let value = Complex::new(f64::INFINITY, 0.0);
                    assert!(matches!(
                        value.try_acos(),
                        Err(ACosComplexErrors::Input { .. })
                    ));

                    let value = Complex::new(0.0, f64::INFINITY);
                    assert!(matches!(
                        value.try_acos(),
                        Err(ACosComplexErrors::Input { .. })
                    ));
                }

                #[test]
                fn acos_subnormal() {
                    let value = Complex::new(f64::MIN_POSITIVE / 2.0, 0.0);
                    assert!(matches!(
                        value.try_acos(),
                        Err(ACosComplexErrors::Input { .. })
                    ));

                    let value = Complex::new(0.0, f64::MIN_POSITIVE / 2.0);
                    assert!(matches!(
                        value.try_acos(),
                        Err(ACosComplexErrors::Input { .. })
                    ));
                }
            }
        }

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

            mod real {
                use super::*;

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

                    let expected_result = RealRugStrictFinite::<53>::try_new(rug::Float::with_val(
                        53,
                        std::f64::consts::FRAC_PI_3,
                    ))
                    .unwrap();
                    assert_eq!(value.clone().try_acos().unwrap(), expected_result);
                    assert_eq!(value.acos(), expected_result);
                }

                #[test]
                fn test_rug_float_acos_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,
                        std::f64::consts::FRAC_PI_2,
                    ))
                    .unwrap();
                    assert_eq!(value.clone().try_acos().unwrap(), expected_result);
                    assert_eq!(value.acos(), expected_result);
                }

                #[test]
                fn test_rug_float_acos_out_of_bound() {
                    let value =
                        RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 2.0)).unwrap();
                    let result = value.try_acos();
                    println!("result: {result:?}");
                    assert!(matches!(result, Err(ACosRealErrors::Input { .. })));
                }
            }

            mod complex {
                use super::*;

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

                    let expected_result =
                        ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                            53,
                            (
                                rug::Float::with_val(53, 1.1185178796437059),
                                rug::Float::with_val(53, -5.306375309525179e-1),
                            ),
                        ))
                        .unwrap();
                    assert_eq!(value.clone().try_acos().unwrap(), expected_result);
                    assert_eq!(value.acos(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_acos_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 =
                        ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                            53,
                            (
                                rug::Float::with_val(53, std::f64::consts::FRAC_PI_2),
                                rug::Float::with_val(53, 0.),
                            ),
                        ))
                        .unwrap();
                    assert_eq!(value.clone().try_acos().unwrap(), expected_result);
                    assert_eq!(value.acos(), expected_result);
                }
            }
        }
    }

    mod atan2 {
        use super::*;

        mod native64 {
            use super::*;

            #[test]
            fn atan2_valid() {
                let numerator = 1.0;
                let denominator = 1.0;
                let expected_result = std::f64::consts::FRAC_PI_4; // 45 degrees in radians
                assert_eq!(numerator.atan2(&denominator), expected_result);
                assert_eq!(numerator.try_atan2(&denominator).unwrap(), expected_result);
            }

            #[test]
            fn atan2_zero_numerator() {
                let numerator = 0.0;
                let denominator = 1.0;
                let expected_result = 0.0;
                assert_eq!(numerator.atan2(&denominator), expected_result);
                assert_eq!(numerator.try_atan2(&denominator).unwrap(), expected_result);
            }

            #[test]
            fn atan2_zero_denominator() {
                let numerator = 1.0;
                let denominator = 0.0;
                let expected_result = std::f64::consts::FRAC_PI_2; // 90 degrees in radians
                assert_eq!(numerator.atan2(&denominator), expected_result);
                assert_eq!(numerator.try_atan2(&denominator).unwrap(), expected_result);
            }

            #[test]
            fn atan2_zero_over_zero() {
                let numerator = 0.0;
                let denominator = 0.0;
                let result = numerator.try_atan2(&denominator);
                assert!(matches!(
                    result,
                    Err(ATan2Errors::Input {
                        source: ATan2InputErrors::ZeroOverZero { .. }
                    })
                ));
            }

            #[test]
            fn atan2_negative_numerator() {
                let numerator = -1.0;
                let denominator = 1.0;
                let expected_result = -std::f64::consts::FRAC_PI_4; // -45 degrees in radians
                assert_eq!(numerator.atan2(&denominator), expected_result);
                assert_eq!(numerator.try_atan2(&denominator).unwrap(), expected_result);
            }

            #[test]
            fn atan2_negative_denominator() {
                let numerator = 1.0;
                let denominator = -1.0;
                let expected_result = 2.356194490192345; // 135 degrees in radians
                assert_eq!(numerator.atan2(&denominator), expected_result);
                assert_eq!(numerator.try_atan2(&denominator).unwrap(), expected_result);
            }

            #[test]
            fn atan2_nan_numerator() {
                let numerator = f64::NAN;
                let denominator = 1.0;
                let result = numerator.try_atan2(&denominator);
                assert!(result.is_err());
            }

            #[test]
            fn atan2_nan_denominator() {
                let numerator = 1.0;
                let denominator = f64::NAN;
                let result = numerator.try_atan2(&denominator);
                assert!(result.is_err());
            }
        }

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

            #[test]
            fn test_realrug_atan2_valid() {
                let numerator =
                    RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
                let denominator =
                    RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
                let expected_result = RealRugStrictFinite::<53>::try_new(Float::with_val(
                    53,
                    std::f64::consts::FRAC_PI_4,
                ))
                .unwrap(); // 45 degrees in radians
                assert_eq!(numerator.clone().atan2(&denominator), expected_result);
                assert_eq!(numerator.try_atan2(&denominator).unwrap(), expected_result);
            }

            #[test]
            fn test_realrug_atan2_zero_numerator() {
                let numerator =
                    RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.0)).unwrap();
                let denominator =
                    RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
                let expected_result =
                    RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.0)).unwrap();
                assert_eq!(numerator.clone().atan2(&denominator), expected_result);
                assert_eq!(numerator.try_atan2(&denominator).unwrap(), expected_result);
            }

            #[test]
            fn test_realrug_atan2_zero_denominator() {
                let numerator =
                    RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
                let denominator =
                    RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.0)).unwrap();
                let expected_result = RealRugStrictFinite::<53>::try_new(Float::with_val(
                    53,
                    std::f64::consts::FRAC_PI_2,
                ))
                .unwrap(); // 90 degrees in radians
                assert_eq!(numerator.clone().atan2(&denominator), expected_result);
                assert_eq!(numerator.try_atan2(&denominator).unwrap(), expected_result);
            }

            #[test]
            fn test_realrug_atan2_zero_over_zero() {
                let numerator =
                    RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.0)).unwrap();
                let denominator =
                    RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.0)).unwrap();
                let result = numerator.try_atan2(&denominator);
                assert!(matches!(
                    result,
                    Err(ATan2Errors::Input {
                        source: ATan2InputErrors::ZeroOverZero { .. }
                    })
                ));
            }

            #[test]
            fn test_realrug_atan2_negative_numerator() {
                let numerator =
                    RealRugStrictFinite::<53>::try_new(Float::with_val(53, -1.0)).unwrap();
                let denominator =
                    RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
                let expected_result = RealRugStrictFinite::<53>::try_new(Float::with_val(
                    53,
                    -std::f64::consts::FRAC_PI_4,
                ))
                .unwrap(); // -45 degrees in radians
                assert_eq!(numerator.clone().atan2(&denominator), expected_result);
                assert_eq!(numerator.try_atan2(&denominator).unwrap(), expected_result);
            }

            #[test]
            fn test_realrug_atan2_negative_denominator() {
                let numerator =
                    RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
                let denominator =
                    RealRugStrictFinite::<53>::try_new(Float::with_val(53, -1.0)).unwrap();
                let expected_result =
                    RealRugStrictFinite::<53>::try_new(Float::with_val(53, 2.356194490192345))
                        .unwrap(); // 135 degrees in radians
                assert_eq!(numerator.clone().atan2(&denominator), expected_result);
                assert_eq!(numerator.try_atan2(&denominator).unwrap(), expected_result);
            }
        }
    }
}
//------------------------------------------------------------------------------------------------
