#![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::{
core::{errors::capture_backtrace, policies::StrictFinitePolicy},
functions::FunctionErrors,
kernels::{RawComplexTrait, RawRealTrait, RawScalarTrait},
};
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::core::errors::ErrorsValidationRawReal`], [`crate::core::errors::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::core::errors::ErrorsValidationRawReal`], [`crate::core::errors::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::core::errors::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::core::errors::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::core::errors::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]
#[must_use = "this `Result` may contain an error that should be handled"]
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 {
/// A captured backtrace for debugging purposes.
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::core::errors::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, backends::native64::raw::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).
#[must_use = "this `Result` may contain an error that should be handled"]
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 approx::assert_ulps_eq;
use num::Complex;
#[cfg(feature = "rug")]
use crate::backends::rug::validated::{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 = -0.7568024953079282;
assert_ulps_eq!(value.try_sin().unwrap(), expected_result);
assert_ulps_eq!(value.sin(), expected_result);
}
#[test]
fn sin_negative() {
let value = -4.0;
let expected_result = 0.7568024953079282;
assert_ulps_eq!(value.try_sin().unwrap(), expected_result);
assert_ulps_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!("Architecture not-tested");
};
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!("Architecture not-tested");
};
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 = std::f64::consts::FRAC_PI_6;
assert_ulps_eq!(value.try_asin().unwrap(), expected_result);
assert_ulps_eq!(value.asin(), expected_result);
}
#[test]
fn asin_negative() {
let value = -0.5;
let expected_result = -std::f64::consts::FRAC_PI_6;
assert_ulps_eq!(value.try_asin().unwrap(), expected_result);
assert_ulps_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 = std::f64::consts::FRAC_PI_3;
assert_ulps_eq!(value.try_acos().unwrap(), expected_result);
assert_ulps_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);
}
}
}
}
//------------------------------------------------------------------------------------------------