#![deny(rustdoc::broken_intra_doc_links)]
use crate::{
ComplexScalar, Constants, FpScalar, IntegerScalar, MulAddRef, RealScalar, ScalarCore,
core::{
errors::{
ErrorsRawRealToInteger, ErrorsValidationRawComplex, ErrorsValidationRawReal,
capture_backtrace,
},
policies::validate_complex_from_parts,
traits::{RawKernel, raw::RawScalarValidate, validation::sealed::NativeReal},
},
functions::{
Clamp, Classify, ExpM1, Hypot, Ln1p, Rounding, Sign, TotalCmp,
complex::{
ComplexScalarConstructors, ComplexScalarGetParts, ComplexScalarMutateParts,
ComplexScalarSetParts,
},
},
kernels::{
PrecisionBits, RawComplexTrait, RawRealTrait, RawScalarHyperbolic, RawScalarPow,
RawScalarTrait, RawScalarTrigonometric,
},
scalar_kind,
};
use az::CheckedAs;
use duplicate::duplicate_item;
use num::{Complex, Zero};
use num_traits::{Float, FloatConst, MulAdd, MulAddAssign};
use std::{
cmp::Ordering,
hash::{Hash, Hasher},
num::FpCategory,
};
use try_create::IntoInner;
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct Native<T: NativeReal> {
_marker: std::marker::PhantomData<T>,
}
impl<T: NativeReal> RawKernel for Native<T>
where
T: RawRealTrait<RawComplex = Complex<T>>,
Complex<T>: RawComplexTrait<RawReal = T>,
{
type RawReal = T;
type RawComplex = Complex<T>;
}
pub type Native64 = Native<f64>;
pub type Native32 = Native<f32>;
#[duplicate_item(
T;
[f64];
[f32];
)]
impl FpScalar for T {
type Kind = scalar_kind::Real;
type RealType = T;
}
impl<T> FpScalar for Complex<T>
where
Self: RawComplexTrait<RawReal = T>
+ IntoInner<InnerType = Self>
+ ScalarCore<RawScalarType = Self>,
T: NativeReal<ValidationError = ErrorsValidationRawReal<T>> + RealScalar,
{
type Kind = scalar_kind::Complex;
type RealType = T;
}
impl<T: NativeReal> Sign for T {
#[inline(always)]
fn kernel_copysign(self, sign: &Self) -> Self {
self.copysign(*sign)
}
#[inline(always)]
fn kernel_is_sign_negative(&self) -> bool {
self.is_sign_negative()
}
#[inline(always)]
fn kernel_is_sign_positive(&self) -> bool {
self.is_sign_positive()
}
#[inline(always)]
fn kernel_signum(self) -> Self {
self.signum()
}
}
impl<T: NativeReal> Rounding for T {
#[inline(always)]
fn kernel_ceil(self) -> Self {
self.ceil()
}
#[inline(always)]
fn kernel_floor(self) -> Self {
self.floor()
}
#[inline(always)]
fn kernel_fract(self) -> Self {
self.fract()
}
#[inline(always)]
fn kernel_round(self) -> Self {
self.round()
}
#[inline(always)]
fn kernel_round_ties_even(self) -> Self {
self.round_ties_even_native_real()
}
#[inline(always)]
fn kernel_trunc(self) -> Self {
self.trunc()
}
}
impl<T: NativeReal> Constants for T {
#[inline(always)]
fn epsilon() -> Self {
<T as Float>::epsilon()
}
#[inline(always)]
fn negative_one() -> Self {
T::raw_negative_one(T::MANTISSA_DIGITS)
}
#[inline(always)]
fn one_div_2() -> Self {
T::raw_one_div_2(T::MANTISSA_DIGITS)
}
#[inline(always)]
fn two() -> Self {
T::raw_two(T::MANTISSA_DIGITS)
}
#[inline(always)]
fn max_finite() -> Self {
<T as Float>::max_value()
}
#[inline(always)]
fn min_finite() -> Self {
<T as Float>::min_value()
}
#[inline(always)]
fn pi() -> Self {
<T as FloatConst>::PI()
}
#[inline(always)]
fn two_pi() -> Self {
<T as FloatConst>::TAU()
}
#[inline(always)]
fn pi_div_2() -> Self {
<T as FloatConst>::FRAC_PI_2()
}
#[inline(always)]
fn ln_2() -> Self {
<T as FloatConst>::LN_2()
}
#[inline(always)]
fn ln_10() -> Self {
<T as FloatConst>::LN_10()
}
#[inline(always)]
fn log10_2() -> Self {
<T as FloatConst>::LOG10_2()
}
#[inline(always)]
fn log2_10() -> Self {
<T as FloatConst>::LOG2_10()
}
#[inline(always)]
fn log2_e() -> Self {
<T as FloatConst>::LOG2_E()
}
#[inline(always)]
fn log10_e() -> Self {
<T as FloatConst>::LOG10_E()
}
#[inline(always)]
fn e() -> Self {
<T as FloatConst>::E()
}
}
impl<T: NativeReal> Clamp for T {
#[inline(always)]
fn clamp_ref(self, min: &Self, max: &Self) -> Self {
<T as Float>::clamp(self, *min, *max)
}
}
impl<T: NativeReal> Classify for T {
#[inline(always)]
fn classify(&self) -> FpCategory {
<T as Float>::classify(*self)
}
}
impl<T: NativeReal> ExpM1 for T {
#[inline(always)]
fn exp_m1(self) -> Self {
<T as Float>::exp_m1(self)
}
}
impl<T: NativeReal> Hypot for T {
#[inline(always)]
fn hypot(self, other: &Self) -> Self {
<T as Float>::hypot(self, *other)
}
}
impl<T: NativeReal> Ln1p for T {
#[inline(always)]
fn ln_1p(self) -> Self {
<T as Float>::ln_1p(self)
}
}
impl<T: NativeReal> TotalCmp for T {
#[inline(always)]
fn total_cmp(&self, other: &Self) -> Ordering {
self.raw_total_cmp(other)
}
}
impl<T: NativeReal> PrecisionBits for T {
#[inline(always)]
fn precision_bits(&self) -> u32 {
T::MANTISSA_DIGITS
}
}
#[duplicate_item(
T;
[f64];
[f32];
)]
impl RealScalar for T {
type RawReal = T;
#[inline(always)]
fn kernel_mul_add_mul_mut(&mut self, mul: &Self, add_mul1: &Self, add_mul2: &Self) {
self.mul_add_assign(*mul, add_mul1 * add_mul2);
}
#[inline(always)]
fn kernel_mul_sub_mul_mut(&mut self, mul: &Self, sub_mul1: &Self, sub_mul2: &Self) {
self.mul_add_assign(*mul, -sub_mul1 * sub_mul2);
}
#[inline(always)]
fn try_new_raw(value: Self::RawReal) -> Result<Self, ErrorsValidationRawReal<Self::RawReal>> {
Ok(value)
}
#[inline(always)]
fn target_precision() -> u32 {
T::MANTISSA_DIGITS
}
}
impl<T> ComplexScalarConstructors for Complex<T>
where
Self: RawComplexTrait<RawReal = T>
+ IntoInner<InnerType = Self>
+ ScalarCore<RawScalarType = Self>,
T: NativeReal<ValidationError = ErrorsValidationRawReal<T>> + RealScalar<RawReal = T>,
{
type RawComplex = Complex<T>;
fn try_new_complex(
real_part: T,
imag_part: T,
) -> Result<Self, ErrorsValidationRawComplex<ErrorsValidationRawReal<T>>> {
validate_complex_from_parts(&real_part, &imag_part, T::validate_raw_scalar_ref)
.map(|_| Complex::new(real_part, imag_part))
}
}
impl<T> ComplexScalarGetParts for Complex<T>
where
Self: RawComplexTrait<RawReal = T>
+ IntoInner<InnerType = Self>
+ ScalarCore<RawScalarType = Self>,
T: NativeReal<ValidationError = ErrorsValidationRawReal<T>> + RealScalar<RawReal = T>,
{
#[inline(always)]
fn real_part(&self) -> T {
self.re
}
#[inline(always)]
fn imag_part(&self) -> T {
self.im
}
#[inline(always)]
fn raw_real_part(&self) -> &T {
&self.re
}
#[inline(always)]
fn raw_imag_part(&self) -> &T {
&self.im
}
}
impl<T> ComplexScalarSetParts for Complex<T>
where
Self: RawComplexTrait<RawReal = T>
+ IntoInner<InnerType = Self>
+ ScalarCore<RawScalarType = Self>,
T: NativeReal<ValidationError = ErrorsValidationRawReal<T>> + RealScalar<RawReal = T>,
{
#[inline(always)]
fn set_real_part(&mut self, real_part: T) {
debug_assert!(
real_part.is_finite(),
"The real part is not finite (i.e. is infinite or NaN)."
);
self.re = real_part;
}
#[inline(always)]
fn set_imaginary_part(&mut self, imag_part: T) {
debug_assert!(
imag_part.is_finite(),
"The imaginary part is not finite (i.e. is infinite or NaN)."
);
self.im = imag_part;
}
}
impl<T> ComplexScalarMutateParts for Complex<T>
where
Self: RawComplexTrait<RawReal = T>
+ IntoInner<InnerType = Self>
+ ScalarCore<RawScalarType = Self>,
T: NativeReal<ValidationError = ErrorsValidationRawReal<T>> + RealScalar<RawReal = T>,
{
#[inline(always)]
fn add_to_real_part(&mut self, c: &T) {
self.re += c;
debug_assert!(
self.re.is_finite(),
"The real part is not finite (i.e. is infinite or NaN)."
);
}
#[inline(always)]
fn add_to_imaginary_part(&mut self, c: &T) {
self.im += c;
debug_assert!(
self.im.is_finite(),
"The imaginary part is not finite (i.e. is infinite or NaN)."
);
}
#[inline(always)]
fn multiply_real_part(&mut self, c: &T) {
self.re *= c;
debug_assert!(
self.re.is_finite(),
"The real part is not finite (i.e. is infinite or NaN)."
);
}
#[inline(always)]
fn multiply_imaginary_part(&mut self, c: &T) {
self.im *= c;
debug_assert!(
self.im.is_finite(),
"The imaginary part is not finite (i.e. is infinite or NaN)."
);
}
}
impl<T> ComplexScalar for Complex<T>
where
Self: RawComplexTrait<RawReal = T>
+ IntoInner<InnerType = Self>
+ ScalarCore<RawScalarType = Self>,
T: NativeReal<ValidationError = ErrorsValidationRawReal<T>> + RealScalar<RawReal = T>,
{
fn into_parts(self) -> (Self::RealType, Self::RealType) {
(self.re, self.im)
}
}
impl<T> MulAddRef for T
where
T: MulAdd<Output = T> + Copy,
{
#[inline(always)]
fn mul_add_ref(self, b: &Self, c: &Self) -> Self {
Self::mul_add(self, *b, *c)
}
}
impl<T: NativeReal> RawScalarTrigonometric for T {
#[duplicate_item(
unchecked_method method;
[unchecked_sin] [sin];
[unchecked_asin] [asin];
[unchecked_cos] [cos];
[unchecked_acos] [acos];
[unchecked_tan] [tan];
[unchecked_atan] [atan];
)]
#[inline(always)]
fn unchecked_method(self) -> Self {
T::method(self)
}
}
impl<T: NativeReal> RawScalarTrigonometric for Complex<T> {
#[duplicate_item(
unchecked_method method;
[unchecked_sin] [sin];
[unchecked_asin] [asin];
[unchecked_cos] [cos];
[unchecked_acos] [acos];
[unchecked_tan] [tan];
[unchecked_atan] [atan];
)]
#[inline(always)]
fn unchecked_method(self) -> Self {
Complex::<T>::method(self)
}
}
impl<T: NativeReal> RawScalarHyperbolic for T {
#[duplicate_item(
unchecked_method method;
[unchecked_sinh] [sinh];
[unchecked_asinh] [asinh];
[unchecked_cosh] [cosh];
[unchecked_acosh] [acosh];
[unchecked_tanh] [tanh];
[unchecked_atanh] [atanh];
)]
#[inline(always)]
fn unchecked_method(self) -> Self {
T::method(self)
}
}
impl<T: NativeReal> RawScalarHyperbolic for Complex<T> {
#[duplicate_item(
unchecked_method method;
[unchecked_sinh] [sinh];
[unchecked_asinh] [asinh];
[unchecked_cosh] [cosh];
[unchecked_acosh] [acosh];
[unchecked_tanh] [tanh];
[unchecked_atanh] [atanh];
)]
#[inline(always)]
fn unchecked_method(self) -> Self {
Complex::<T>::method(self)
}
}
impl<T: NativeReal> RawScalarPow for T {
#[duplicate_item(
unchecked_method exponent_type;
[unchecked_pow_exponent_i8] [i8];
[unchecked_pow_exponent_i16] [i16];
[unchecked_pow_exponent_u8] [u8];
[unchecked_pow_exponent_u16] [u16];
)]
#[inline(always)]
fn unchecked_method(self, exponent: &exponent_type) -> T {
T::powi(self, (*exponent).into())
}
#[inline(always)]
fn unchecked_pow_exponent_i32(self, exponent: &i32) -> Self {
T::powi(self, *exponent)
}
#[duplicate_item(
unchecked_method exponent_type;
[unchecked_pow_exponent_i64] [i64];
[unchecked_pow_exponent_i128] [i128];
[unchecked_pow_exponent_isize] [isize];
[unchecked_pow_exponent_u32] [u32];
[unchecked_pow_exponent_u64] [u64];
[unchecked_pow_exponent_u128] [u128];
[unchecked_pow_exponent_usize] [usize];
)]
#[inline(always)]
fn unchecked_method(self, exponent: &exponent_type) -> T {
T::powi(
self,
(*exponent)
.try_into()
.expect("The exponent {exponent} cannot be converted to an integer of type i32"),
)
}
}
impl<T: NativeReal> RawScalarValidate for T {
type ValidationError = ErrorsValidationRawReal<T>;
fn validate_raw_scalar_ref(&self) -> Result<(), Self::ValidationError> {
match self.classify() {
FpCategory::Nan => Err(ErrorsValidationRawReal::IsNaN {
value: *self,
backtrace: capture_backtrace(),
}),
FpCategory::Infinite => {
if self.is_sign_positive() {
Err(ErrorsValidationRawReal::IsPosInfinity {
backtrace: capture_backtrace(),
})
} else {
Err(ErrorsValidationRawReal::IsNegInfinity {
backtrace: capture_backtrace(),
})
}
}
FpCategory::Subnormal => Err(ErrorsValidationRawReal::IsSubnormal {
value: *self,
backtrace: capture_backtrace(),
}),
FpCategory::Normal | FpCategory::Zero => Ok(()),
}
}
}
#[inline(always)]
fn debug_assert_if_invalid_precision<T: NativeReal>(precision: u32) {
debug_assert_eq!(
precision,
T::MANTISSA_DIGITS,
"Precision {precision} must be equal to the mantissa digits for type {} (mantissa digits = {}).",
std::any::type_name::<T>(),
T::MANTISSA_DIGITS
);
}
impl<T: NativeReal> RawScalarTrait for T {
#[inline(always)]
fn raw_zero(_precision: u32) -> T {
debug_assert_if_invalid_precision::<T>(_precision);
T::zero()
}
#[inline(always)]
fn is_zero(&self) -> bool {
<Self as Zero>::is_zero(self)
}
#[inline(always)]
fn raw_one(_precision: u32) -> T {
debug_assert_if_invalid_precision::<T>(_precision);
T::one()
}
#[duplicate_item(
unchecked_method method;
[unchecked_reciprocal] [recip];
[unchecked_exp] [exp];
[unchecked_sqrt] [sqrt];
[unchecked_ln] [ln];
[unchecked_log2] [log2];
[unchecked_log10] [log10];
)]
#[inline(always)]
fn unchecked_method(self) -> T {
<T as Float>::method(self)
}
#[inline(always)]
fn unchecked_mul_add(self, b: &Self, c: &Self) -> Self {
Float::mul_add(self, *b, *c)
}
#[inline(always)]
fn compute_hash_raw_scalar<H: Hasher>(&self, state: &mut H) {
debug_assert!(
self.is_finite(),
concat!(
"Hashing a non-finite ",
stringify!(T),
" floating-point value (i.e., NaN or Infinity) may lead to inconsistent results."
)
);
let zero = T::zero();
if self == &zero {
zero.to_bits_native_real().hash(state);
} else {
self.to_bits_native_real().hash(state);
}
}
}
#[duplicate_item(
T cast_int_to_float from_raw_real;
[f64] [to_f64()] [from_f64];
[f32] [to_f32()] [from_f32];
)]
impl RawRealTrait for T {
type RawComplex = Complex<T>;
#[inline(always)]
fn unchecked_abs(self) -> Self {
<T as Float>::abs(self)
}
#[inline(always)]
fn unchecked_atan2(self, denominator: &Self) -> Self {
<T as Float>::atan2(self, *denominator)
}
#[inline(always)]
fn unchecked_pow_exponent_real(self, exponent: &Self) -> Self {
<T as Float>::powf(self, *exponent)
}
#[inline(always)]
fn unchecked_hypot(self, other: &Self) -> Self {
<T as Float>::hypot(self, *other)
}
#[inline(always)]
fn unchecked_ln_1p(self) -> Self {
<T as Float>::ln_1p(self)
}
#[inline(always)]
fn unchecked_exp_m1(self) -> Self {
<T as Float>::exp_m1(self)
}
#[inline(always)]
fn unchecked_mul_add_mul_mut(&mut self, mul: &Self, add_mul1: &Self, add_mul2: &Self) {
self.mul_add_assign(*mul, add_mul1 * add_mul2);
}
#[inline(always)]
fn unchecked_mul_sub_mul_mut(&mut self, mul: &Self, sub_mul1: &Self, sub_mul2: &Self) {
self.mul_add_assign(*mul, -sub_mul1 * sub_mul2);
}
#[inline(always)]
fn raw_total_cmp(&self, other: &Self) -> Ordering {
T::total_cmp(self, other)
}
#[inline(always)]
fn raw_clamp(self, min: &Self, max: &Self) -> Self {
<T as Float>::clamp(self, *min, *max)
}
#[inline(always)]
fn raw_classify(&self) -> FpCategory {
<T as Float>::classify(*self)
}
#[inline(always)]
fn raw_two(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
2.
}
#[inline(always)]
fn raw_one_div_2(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
0.5
}
#[inline(always)]
fn raw_pi(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
<T as FloatConst>::PI()
}
#[inline(always)]
fn raw_two_pi(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
<T as FloatConst>::TAU()
}
#[inline(always)]
fn raw_pi_div_2(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
<T as FloatConst>::FRAC_PI_2()
}
#[inline(always)]
fn raw_max_finite(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
<T as Float>::max_value()
}
#[inline(always)]
fn raw_min_finite(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
<T as Float>::min_value()
}
#[inline(always)]
fn raw_epsilon(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
<T as Float>::epsilon()
}
#[inline(always)]
fn raw_ln_2(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
<T as FloatConst>::LN_2()
}
#[inline(always)]
fn raw_ln_10(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
<T as FloatConst>::LN_10()
}
#[inline(always)]
fn raw_log10_2(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
<T as FloatConst>::LOG10_2()
}
#[inline(always)]
fn raw_log2_10(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
<T as FloatConst>::LOG2_10()
}
#[inline(always)]
fn raw_log2_e(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
<T as FloatConst>::LOG2_E()
}
#[inline(always)]
fn raw_log10_e(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
<T as FloatConst>::LOG10_E()
}
#[inline(always)]
fn raw_e(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
<T as FloatConst>::E()
}
#[inline(always)]
fn truncate_to_usize(self) -> Result<usize, ErrorsRawRealToInteger<T, usize>> {
if !self.is_finite() {
return Err(ErrorsRawRealToInteger::NotFinite {
value: self,
backtrace: capture_backtrace(),
});
}
match self.checked_as::<usize>() {
Some(value) => Ok(value),
None => Err(ErrorsRawRealToInteger::OutOfRange {
value: self,
min: usize::MIN,
max: usize::MAX,
backtrace: capture_backtrace(),
}),
}
}
fn unchecked_mul_assign_integer<I>(&mut self, integer: I)
where
I: IntegerScalar,
{
let as_raw_real = integer
.cast_int_to_float
.expect("Failed to cast integer to raw float");
debug_assert!(
I::from_raw_real(as_raw_real) == Some(integer),
"unchecked_mul_assign_integer requires an exact conversion, but the integer is not exactly representable in the target raw real type"
);
*self *= as_raw_real;
}
fn unchecked_div_assign_integer<I>(&mut self, integer: I)
where
I: IntegerScalar,
{
let as_raw_real = integer
.cast_int_to_float
.expect("Failed to cast integer to raw float");
debug_assert!(
I::from_raw_real(as_raw_real) == Some(integer),
"unchecked_div_assign_integer requires an exact conversion, but the integer is not exactly representable in the target raw real type"
);
*self /= as_raw_real;
}
}
impl<T: NativeReal> RawScalarPow for Complex<T> {
#[duplicate_item(
unchecked_method exponent_type;
[unchecked_pow_exponent_i8] [i8];
[unchecked_pow_exponent_i16] [i16];
)]
#[inline(always)]
fn unchecked_method(self, exponent: &exponent_type) -> Self {
Complex::<T>::powi(&self, (*exponent).into())
}
#[inline(always)]
fn unchecked_pow_exponent_i32(self, exponent: &i32) -> Self {
Complex::<T>::powi(&self, *exponent)
}
#[duplicate_item(
unchecked_method exponent_type;
[unchecked_pow_exponent_i64] [i64];
[unchecked_pow_exponent_i128] [i128];
[unchecked_pow_exponent_isize] [isize];
)]
#[inline(always)]
fn unchecked_method(self, exponent: &exponent_type) -> Self {
Complex::<T>::powi(
&self,
(*exponent)
.try_into()
.expect("The exponent {exponent} cannot be converted to an integer of type i32"),
)
}
#[duplicate_item(
unchecked_method exponent_type;
[unchecked_pow_exponent_u8] [u8];
[unchecked_pow_exponent_u16] [u16];
)]
#[inline(always)]
fn unchecked_method(self, exponent: &exponent_type) -> Self {
Complex::<T>::powu(&self, (*exponent).into())
}
#[inline(always)]
fn unchecked_pow_exponent_u32(self, exponent: &u32) -> Self {
Complex::<T>::powu(&self, *exponent)
}
#[duplicate_item(
unchecked_method exponent_type;
[unchecked_pow_exponent_u64] [u64];
[unchecked_pow_exponent_u128] [u128];
[unchecked_pow_exponent_usize] [usize];
)]
#[inline(always)]
fn unchecked_method(self, exponent: &exponent_type) -> Self {
Complex::<T>::powu(
&self,
(*exponent)
.try_into()
.expect("The exponent {exponent} cannot be converted to an integer of type u32"),
)
}
}
impl<T: NativeReal> RawScalarValidate for Complex<T> {
type ValidationError = ErrorsValidationRawComplex<ErrorsValidationRawReal<T>>;
#[inline(always)]
fn validate_raw_scalar_ref(&self) -> Result<(), Self::ValidationError> {
validate_complex_from_parts(&self.re, &self.im, T::validate_raw_scalar_ref)
}
}
impl<T: NativeReal> RawScalarTrait for Complex<T> {
#[inline(always)]
fn raw_zero(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
Complex::new(T::zero(), T::zero())
}
#[inline(always)]
fn is_zero(&self) -> bool {
<Self as Zero>::is_zero(self)
}
#[inline(always)]
fn raw_one(_precision: u32) -> Self {
debug_assert_if_invalid_precision::<T>(_precision);
Complex::new(T::one(), T::zero())
}
#[duplicate_item(
unchecked_method method;
[unchecked_exp] [exp];
[unchecked_sqrt] [sqrt];
[unchecked_ln] [ln];
[unchecked_log10] [log10];
)]
#[inline(always)]
fn unchecked_method(self) -> Self {
Complex::<T>::method(self)
}
#[inline(always)]
fn unchecked_reciprocal(self) -> Self {
Complex::<T>::inv(&self)
}
#[inline(always)]
fn unchecked_log2(self) -> Self {
Complex::<T>::ln(self) / <T as FloatConst>::LN_2()
}
#[inline(always)]
fn unchecked_mul_add(self, b: &Self, c: &Self) -> Self {
Complex::<T>::mul_add(self, *b, *c)
}
#[inline(always)]
fn compute_hash_raw_scalar<H: Hasher>(&self, state: &mut H) {
self.re.compute_hash_raw_scalar(state);
self.im.compute_hash_raw_scalar(state);
}
}
impl<T> RawComplexTrait for Complex<T>
where
T: NativeReal<ValidationError = ErrorsValidationRawReal<T>> + RawRealTrait<RawComplex = Self>,
{
type RawReal = T;
fn new_unchecked_raw_complex(real: T, imag: T) -> Self {
Complex::<T>::new(real, imag)
}
fn mut_raw_real_part(&mut self) -> &mut T {
&mut self.re
}
fn mut_raw_imag_part(&mut self) -> &mut T {
&mut self.im
}
#[inline(always)]
fn unchecked_abs(self) -> T {
Complex::<T>::norm(self)
}
#[inline(always)]
fn raw_real_part(&self) -> &T {
&self.re
}
#[inline(always)]
fn raw_imag_part(&self) -> &T {
&self.im
}
#[inline(always)]
fn unchecked_arg(self) -> T {
Complex::<T>::arg(self)
}
#[inline(always)]
fn unchecked_pow_exponent_real(self, exponent: &T) -> Self {
Complex::<T>::powf(self, *exponent)
}
}
#[cfg(test)]
mod tests {
use crate::{
core::errors::{ErrorsValidationRawComplex, ErrorsValidationRawReal},
functions::TotalCmp,
};
use std::assert_matches;
mod real {
use super::*;
use crate::Constants;
#[test]
fn test_constants() {
assert_eq!(<f64 as Constants>::epsilon(), f64::EPSILON);
assert_eq!(<f64 as Constants>::negative_one(), -1.0);
assert_eq!(<f64 as Constants>::one_div_2(), 0.5);
assert_eq!(<f64 as Constants>::two(), 2.0);
assert_eq!(<f64 as Constants>::max_finite(), f64::MAX);
assert_eq!(<f64 as Constants>::min_finite(), f64::MIN);
assert_eq!(<f64 as Constants>::pi(), std::f64::consts::PI);
assert_eq!(<f64 as Constants>::two_pi(), std::f64::consts::PI * 2.0);
assert_eq!(<f64 as Constants>::pi_div_2(), std::f64::consts::FRAC_PI_2);
assert_eq!(<f64 as Constants>::ln_2(), std::f64::consts::LN_2);
assert_eq!(<f64 as Constants>::ln_10(), std::f64::consts::LN_10);
assert_eq!(<f64 as Constants>::log10_2(), std::f64::consts::LOG10_2);
assert_eq!(<f64 as Constants>::log2_10(), std::f64::consts::LOG2_10);
assert_eq!(<f64 as Constants>::log2_e(), std::f64::consts::LOG2_E);
assert_eq!(<f64 as Constants>::log10_e(), std::f64::consts::LOG10_E);
assert_eq!(<f64 as Constants>::e(), std::f64::consts::E);
}
#[test]
#[allow(clippy::op_ref)]
fn multiply_ref() {
let a = 2.0f64;
let b = 3.0f64;
let result = a * &b;
assert_eq!(result, 6.0);
}
#[test]
fn total_cmp() {
let a = 2.0f64;
let b = 3.0f64;
assert_eq!(
<f64 as TotalCmp>::total_cmp(&a, &b),
std::cmp::Ordering::Less
);
assert_eq!(
<f64 as TotalCmp>::total_cmp(&b, &a),
std::cmp::Ordering::Greater
);
assert_eq!(
<f64 as TotalCmp>::total_cmp(&a, &a),
std::cmp::Ordering::Equal
);
}
mod from_f64 {
use crate::{backends::native::validated::RealNative64StrictFinite, *};
#[test]
fn test_from_f64_valid_constants() {
let pi = RealNative64StrictFinite::from_f64(std::f64::consts::PI);
assert_eq!(pi.as_raw_scalar_ref(), &std::f64::consts::PI);
let e = RealNative64StrictFinite::from_f64(std::f64::consts::E);
assert_eq!(e.as_raw_scalar_ref(), &std::f64::consts::E);
let sqrt2 = RealNative64StrictFinite::from_f64(std::f64::consts::SQRT_2);
assert_eq!(sqrt2.as_raw_scalar_ref(), &std::f64::consts::SQRT_2);
}
#[test]
fn test_from_f64_valid_values() {
let x = RealNative64StrictFinite::from_f64(42.0);
assert_eq!(x.as_raw_scalar_ref(), &42.0);
let y = RealNative64StrictFinite::from_f64(-3.0);
assert_eq!(y.as_raw_scalar_ref(), &-3.0);
let z = RealNative64StrictFinite::from_f64(0.0);
assert_eq!(z.as_raw_scalar_ref(), &0.0);
}
#[test]
#[should_panic(
expected = "RealScalar::try_from_f64() failed: invalid conversion from f64 value"
)]
fn test_from_f64_nan_panics() {
let _ = RealNative64StrictFinite::from_f64(f64::NAN);
}
#[test]
#[should_panic(
expected = "RealScalar::try_from_f64() failed: invalid conversion from f64 value"
)]
fn test_from_f64_infinity_panics() {
let _ = RealNative64StrictFinite::from_f64(f64::INFINITY);
}
#[test]
#[should_panic(
expected = "RealScalar::try_from_f64() failed: invalid conversion from f64 value"
)]
fn test_from_f64_neg_infinity_panics() {
let _ = RealNative64StrictFinite::from_f64(f64::NEG_INFINITY);
}
#[test]
#[should_panic(
expected = "RealScalar::try_from_f64() failed: invalid conversion from f64 value"
)]
fn test_from_f64_subnormal_panics() {
let _ = RealNative64StrictFinite::from_f64(f64::MIN_POSITIVE / 2.0);
}
#[test]
fn test_try_from_f64_error_handling() {
assert!(RealNative64StrictFinite::try_from_f64(f64::NAN).is_err());
assert!(RealNative64StrictFinite::try_from_f64(f64::INFINITY).is_err());
assert!(RealNative64StrictFinite::try_from_f64(f64::NEG_INFINITY).is_err());
assert!(RealNative64StrictFinite::try_from_f64(3.0).is_ok());
assert!(RealNative64StrictFinite::try_from_f64(0.0).is_ok());
}
}
mod truncate_to_usize {
use crate::{core::errors::ErrorsRawRealToInteger, kernels::RawRealTrait};
#[test]
fn test_f64_truncate_to_usize_valid() {
assert_eq!(42.0_f64.truncate_to_usize().unwrap(), 42);
assert_eq!(42.9_f64.truncate_to_usize().unwrap(), 42);
assert_eq!(0.0_f64.truncate_to_usize().unwrap(), 0);
}
#[test]
fn test_f64_truncate_to_usize_not_finite() {
assert!(matches!(
f64::NAN.truncate_to_usize(),
Err(ErrorsRawRealToInteger::NotFinite { .. })
));
assert!(matches!(
f64::INFINITY.truncate_to_usize(),
Err(ErrorsRawRealToInteger::NotFinite { .. })
));
assert!(matches!(
f64::NEG_INFINITY.truncate_to_usize(),
Err(ErrorsRawRealToInteger::NotFinite { .. })
));
}
#[test]
fn test_f64_truncate_to_usize_out_of_range() {
assert!(matches!(
(-1.0_f64).truncate_to_usize(),
Err(ErrorsRawRealToInteger::OutOfRange { .. })
));
assert!(matches!(
((usize::MAX as f64) + 1.0).truncate_to_usize(),
Err(ErrorsRawRealToInteger::OutOfRange { .. })
));
assert!(matches!(
(usize::MAX as f64).truncate_to_usize(),
Err(ErrorsRawRealToInteger::OutOfRange { .. })
));
}
}
mod mul_assign_integer {
use crate::kernels::RawRealTrait;
#[test]
fn f64_multiplies_by_signed_integer() {
let mut real = 3.5_f64;
real.unchecked_mul_assign_integer(-4_i32);
assert_eq!(real, -14.0);
}
#[test]
fn f32_multiplies_by_unsigned_integer() {
let mut real = 3.5_f32;
real.unchecked_mul_assign_integer(4_u16);
assert_eq!(real, 14.0);
}
#[test]
fn zeroes_the_value() {
let mut real_f64 = 3.5_f64;
let mut real_f32 = 3.5_f32;
real_f64.unchecked_mul_assign_integer(0_i8);
real_f32.unchecked_mul_assign_integer(0_i8);
assert_eq!(real_f64, 0.0);
assert_eq!(real_f32, 0.0);
}
#[test]
fn f64_accepts_integer_at_precision_boundary() {
let mut real = 1.0_f64;
real.unchecked_mul_assign_integer(1_i128 << 53);
assert_eq!(real, 9_007_199_254_740_992.0);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "unchecked_mul_assign_integer requires an exact conversion")]
fn f64_panics_on_non_exact_conversion() {
let mut real = 1.0_f64;
real.unchecked_mul_assign_integer((1_i128 << 53) + 1);
}
}
mod div_assign_integer {
use crate::kernels::RawRealTrait;
#[test]
fn f64_divides_by_signed_integer() {
let mut real = 14.0_f64;
real.unchecked_div_assign_integer(-4_i32);
assert_eq!(real, -3.5);
}
#[test]
fn f32_divides_by_unsigned_integer() {
let mut real = 14.0_f32;
real.unchecked_div_assign_integer(4_u16);
assert_eq!(real, 3.5);
}
#[test]
fn f64_accepts_integer_at_precision_boundary() {
let mut real = 9_007_199_254_740_992.0_f64;
real.unchecked_div_assign_integer(1_i128 << 53);
assert_eq!(real, 1.0);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "unchecked_div_assign_integer requires an exact conversion")]
fn f64_panics_on_non_exact_conversion() {
let mut real = 1.0_f64;
real.unchecked_div_assign_integer((1_i128 << 53) + 1);
}
}
mod mul_integer {
use crate::kernels::RawRealTrait;
#[test]
fn f64_returns_product_without_mutating_in_place() {
let real = 3.5_f64;
assert_eq!(real.unchecked_mul_integer(-4_i32), -14.0);
}
}
mod div_integer {
use crate::kernels::RawRealTrait;
#[test]
fn f64_returns_quotient_without_mutating_in_place() {
let real = 14.0_f64;
assert_eq!(real.unchecked_div_integer(-4_i32), -3.5);
}
}
}
mod complex {
use super::*;
use crate::{
ComplexScalarConstructors, ComplexScalarGetParts, ComplexScalarMutateParts,
ComplexScalarSetParts,
};
use num::{Complex, Zero};
#[test]
fn real_part() {
let c1 = Complex::new(1.23, 4.56);
assert_eq!(c1.real_part(), 1.23);
let c2 = Complex::new(-7.89, 0.12);
assert_eq!(c2.real_part(), -7.89);
let c3 = Complex::new(0.0, 10.0);
assert_eq!(c3.real_part(), 0.0);
let c_nan_re = Complex::new(f64::NAN, 5.0);
assert!(c_nan_re.real_part().is_nan());
let c_inf_re = Complex::new(f64::INFINITY, 5.0);
assert!(c_inf_re.real_part().is_infinite());
assert!(c_inf_re.real_part().is_sign_positive());
let c_neg_inf_re = Complex::new(f64::NEG_INFINITY, 5.0);
assert!(c_neg_inf_re.real_part().is_infinite());
assert!(c_neg_inf_re.real_part().is_sign_negative());
}
#[test]
fn imag_part() {
let c1 = Complex::new(1.23, 4.56);
assert_eq!(c1.imag_part(), 4.56);
let c2 = Complex::new(7.89, -0.12);
assert_eq!(c2.imag_part(), -0.12);
let c3 = Complex::new(10.0, 0.0);
assert_eq!(c3.imag_part(), 0.0);
let c_nan_im = Complex::new(5.0, f64::NAN);
assert!(c_nan_im.imag_part().is_nan());
let c_inf_im = Complex::new(5.0, f64::INFINITY);
assert!(c_inf_im.imag_part().is_infinite());
assert!(c_inf_im.imag_part().is_sign_positive());
let c_neg_inf_im = Complex::new(5.0, f64::NEG_INFINITY);
assert!(c_neg_inf_im.imag_part().is_infinite());
assert!(c_neg_inf_im.imag_part().is_sign_negative());
}
#[test]
fn try_new_complex() {
let r1 = 1.23;
let i1 = 4.56;
let c1 = Complex::<f64>::try_new_complex(r1, i1).unwrap();
assert_eq!(c1.re, r1);
assert_eq!(c1.im, i1);
assert_eq!(c1.real_part(), r1);
assert_eq!(c1.imag_part(), i1);
let r2 = -7.89;
let i2 = -0.12;
let c2 = Complex::<f64>::try_new_complex(r2, i2).unwrap();
assert_eq!(c2.re, r2);
assert_eq!(c2.im, i2);
assert_eq!(c2.real_part(), r2);
assert_eq!(c2.imag_part(), i2);
let r3 = 0.0;
let i3 = 0.0;
let c3 = Complex::<f64>::try_new_complex(r3, i3).unwrap();
assert_eq!(c3.re, r3);
assert_eq!(c3.im, i3);
assert!(c3.is_zero());
let c_nan_re = Complex::<f64>::try_new_complex(f64::NAN, 5.0).unwrap_err();
assert!(matches!(
c_nan_re,
ErrorsValidationRawComplex::InvalidRealPart { .. }
));
let c_inf_im = Complex::<f64>::try_new_complex(10.0, f64::INFINITY).unwrap_err();
assert!(matches!(
c_inf_im,
ErrorsValidationRawComplex::InvalidImaginaryPart { .. }
));
let c_nan_re_inf_im =
Complex::<f64>::try_new_complex(f64::NAN, f64::INFINITY).unwrap_err();
assert!(matches!(
c_nan_re_inf_im,
ErrorsValidationRawComplex::InvalidBothParts { .. }
));
}
#[test]
fn try_new_pure_real() {
let r1 = 1.23;
let c1 = Complex::<f64>::try_new_pure_real(r1).unwrap();
assert_eq!(c1.re, r1);
assert_eq!(c1.im, 0.0);
let c_nan = Complex::<f64>::try_new_pure_real(f64::NAN).unwrap_err();
assert_matches!(
c_nan,
ErrorsValidationRawComplex::InvalidRealPart {
source
} if matches!(*source, ErrorsValidationRawReal::IsNaN { .. })
);
}
#[test]
fn try_new_pure_imaginary() {
let i1 = 1.23;
let c1 = Complex::<f64>::try_new_pure_imaginary(i1).unwrap();
assert_eq!(c1.re, 0.0);
assert_eq!(c1.im, i1);
let c_nan = Complex::<f64>::try_new_pure_imaginary(f64::NAN).unwrap_err();
assert_matches!(
c_nan,
ErrorsValidationRawComplex::InvalidImaginaryPart {
source
} if matches!(*source, ErrorsValidationRawReal::IsNaN { .. })
);
}
#[test]
fn add_to_real_part() {
let mut c = Complex::new(1.0, 2.0);
c.add_to_real_part(&3.0);
assert_eq!(c.re, 4.0);
assert_eq!(c.im, 2.0);
c.add_to_real_part(&-5.0);
assert_eq!(c.re, -1.0);
assert_eq!(c.im, 2.0);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic(expected = "The real part is not finite (i.e. is infinite or NaN).")]
fn add_to_real_part_nan() {
let mut c = Complex::new(1.0, 2.0);
c.add_to_real_part(&f64::NAN);
}
#[test]
fn add_to_imaginary_part() {
let mut c = Complex::new(1.0, 2.0);
c.add_to_imaginary_part(&3.0);
assert_eq!(c.re, 1.0);
assert_eq!(c.im, 5.0);
c.add_to_imaginary_part(&-4.0);
assert_eq!(c.re, 1.0);
assert_eq!(c.im, 1.0);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic(expected = "The imaginary part is not finite (i.e. is infinite or NaN).")]
fn add_to_imaginary_part_nan() {
let mut c = Complex::new(1.0, 2.0);
c.add_to_imaginary_part(&f64::NAN);
}
#[test]
fn multiply_real_part() {
let mut c = Complex::new(1.0, 2.0);
c.multiply_real_part(&3.0);
assert_eq!(c.re, 3.0);
assert_eq!(c.im, 2.0);
c.multiply_real_part(&-2.0);
assert_eq!(c.re, -6.0);
assert_eq!(c.im, 2.0);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic(expected = "The real part is not finite (i.e. is infinite or NaN).")]
fn multiply_real_part_nan() {
let mut c = Complex::new(1.0, 2.0);
c.multiply_real_part(&f64::NAN);
}
#[test]
fn multiply_imaginary_part() {
let mut c = Complex::new(1.0, 2.0);
c.multiply_imaginary_part(&3.0);
assert_eq!(c.re, 1.0);
assert_eq!(c.im, 6.0);
c.multiply_imaginary_part(&-0.5);
assert_eq!(c.re, 1.0);
assert_eq!(c.im, -3.0);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic(expected = "The imaginary part is not finite (i.e. is infinite or NaN).")]
fn multiply_imaginary_part_nan() {
let mut c = Complex::new(1.0, 2.0);
c.multiply_imaginary_part(&f64::NAN);
}
#[test]
fn set_real_part() {
let mut c = Complex::new(1.0, 2.0);
c.set_real_part(3.0);
assert_eq!(c.re, 3.0);
assert_eq!(c.im, 2.0);
c.set_real_part(-4.0);
assert_eq!(c.re, -4.0);
assert_eq!(c.im, 2.0);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic(expected = "The real part is not finite (i.e. is infinite or NaN).")]
fn set_real_part_nan() {
let mut c = Complex::new(1.0, 2.0);
c.set_real_part(f64::NAN);
}
#[test]
fn set_imaginary_part() {
let mut c = Complex::new(1.0, 2.0);
c.set_imaginary_part(3.0);
assert_eq!(c.re, 1.0);
assert_eq!(c.im, 3.0);
c.set_imaginary_part(-4.0);
assert_eq!(c.re, 1.0);
assert_eq!(c.im, -4.0);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic(expected = "The imaginary part is not finite (i.e. is infinite or NaN).")]
fn set_imaginary_part_nan() {
let mut c = Complex::new(1.0, 2.0);
c.set_imaginary_part(f64::NAN);
}
#[test]
#[allow(clippy::op_ref)]
fn multiply_ref() {
let c1 = Complex::new(1.0, 2.0);
let c2 = Complex::new(3.0, 4.0);
let result = c1 * &c2;
assert_eq!(result, Complex::new(-5.0, 10.0)); }
}
}