num_valid/backends/native64/
raw.rs

1#![deny(rustdoc::broken_intra_doc_links)]
2
3//! # Native 64-bit Floating-Point Kernel
4//!
5//! This module provides a numerical kernel implementation based on Rust's native
6//! 64-bit floating-point numbers ([`f64`]) and complex numbers ([`num::Complex<f64>`](num::Complex)).
7//! It serves as the standard, high-performance double-precision backend for the
8//! [`num-valid`](crate) library.
9//!
10//! ## Purpose and Role
11//!
12//! The primary role of this module is to act as a **bridge** between the abstract traits
13//! defined by [`num-valid`](crate) (like [`RealScalar`](crate::RealScalar) and [`ComplexScalar`](crate::ComplexScalar)) and Rust's
14//! concrete, hardware-accelerated numeric types. It adapts [`f64`] and [`Complex<f64>`](num::Complex)
15//! so they can be used seamlessly in generic code written against the library's traits.
16//!
17//! ### `f64` as a `RealScalar`
18//!
19//! By implementing [`RealScalar`](crate::RealScalar) for [`f64`], this module makes Rust's native float
20//! a "first-class citizen" in the [`num-valid`](crate) ecosystem. This implementation
21//! provides concrete logic for all the operations required by [`RealScalar`](crate::RealScalar) and its
22//! super-traits (e.g., [`FpScalar`](crate::FpScalar), [`Rounding`](crate::functions::Rounding), [`Sign`](crate::functions::Sign), [`Constants`](crate::Constants)).
23//! Most of these implementations simply delegate to the highly optimized methods
24//! available in Rust's standard library (e.g., `f64::sin`, `f64::sqrt`).
25//!
26//! This allows generic functions bounded by `T: RealScalar` to be instantiated with `f64`,
27//! leveraging direct CPU floating-point support for maximum performance.
28//!
29//! ### `Complex<f64>` as a `ComplexScalar`
30//!
31//! Similarly, this module implements [`ComplexScalar`](crate::ComplexScalar) for [`num::Complex<f64>`](num::Complex). This
32//! makes the standard complex number type from the `num` crate compatible with the
33//! [`num-valid`](crate) abstraction for complex numbers. It implements all required traits,
34//! including [`FpScalar`](crate::FpScalar), [`ComplexScalarGetParts`](crate::functions::ComplexScalarGetParts), [`ComplexScalarSetParts`](crate::functions::ComplexScalarSetParts), and
35//! [`ComplexScalarMutateParts`](crate::functions::ComplexScalarMutateParts), by delegating to the methods of `num::Complex`.
36//!
37//! This enables generic code written against `T: ComplexScalar` to operate on
38//! `Complex<f64>` values, providing a performant backend for complex arithmetic.
39//!
40//! ## Core Components
41//!
42//! - **`Native64`**: A marker struct that implements the [`RawKernel`](crate::core::traits::RawKernel) trait. It is used
43//!   in generic contexts to select this native kernel, specifying [`f64`] as its
44//!   [`RawReal`](crate::core::traits::RawKernel::RawReal) type and [`num::Complex<f64>`](num::Complex) as its
45//!   [`RawComplex`](crate::core::traits::RawKernel::RawComplex) type.
46//! - **Trait Implementations**: This module is primarily composed of `impl` blocks that
47//!   satisfy the contracts of core traits ([`FpScalar`](crate::FpScalar), [`RealScalar`](crate::RealScalar), [`ComplexScalar`](crate::ComplexScalar),
48//!   [`Arithmetic`](crate::functions::Arithmetic), [`MulAddRef`](crate::MulAddRef), etc.) for `f64` and `Complex<f64>`.
49//!
50//! ## Validation
51//!
52//! It is crucial to understand that the base types `f64` and `Complex<f64>` do not
53//! inherently enforce properties like finiteness (they can be `NaN` or `Infinity`).
54//!
55//! The [`num-valid`](crate) library introduces validation at the **trait and wrapper level**,
56//! not at the base type level. For instance, [`RealScalar::try_from_f64`](crate::RealScalar::try_from_f64) for `f64`
57//! uses a [`StrictFinitePolicy`](crate::core::policies::StrictFinitePolicy) to ensure the input is finite before creating an instance.
58//! This design separates the raw, high-performance types from the validated, safer
59//! abstractions built on top of them.
60//!
61//! For scenarios requiring types that are guaranteed to be valid by construction,
62//! consider using the validated wrappers from the [`validated`](crate::backends::native64::validated) module.
63
64use crate::{
65    ComplexScalar, Constants, FpScalar, MulAddRef, RealScalar,
66    core::{
67        errors::{
68            ErrorsRawRealToInteger, ErrorsTryFromf64, ErrorsValidationRawComplex,
69            ErrorsValidationRawReal, capture_backtrace,
70        },
71        policies::{Native64RawRealStrictFinitePolicy, StrictFinitePolicy, validate_complex},
72        traits::{RawKernel, validation::ValidationPolicyReal},
73    },
74    functions::{
75        Clamp, Classify, ExpM1, Hypot, Ln1p, Rounding, Sign, TotalCmp,
76        complex::{
77            ComplexScalarConstructors, ComplexScalarGetParts, ComplexScalarMutateParts,
78            ComplexScalarSetParts,
79        },
80    },
81    kernels::{
82        RawComplexTrait, RawRealTrait, RawScalarHyperbolic, RawScalarPow, RawScalarTrait,
83        RawScalarTrigonometric,
84    },
85    scalar_kind,
86};
87use az::CheckedAs;
88use duplicate::duplicate_item;
89use num::{Complex, Zero};
90use num_traits::{MulAdd, MulAddAssign};
91use std::{
92    cmp::Ordering,
93    hash::{Hash, Hasher},
94    num::FpCategory,
95};
96use try_create::ValidationPolicy;
97
98//----------------------------------------------------------------------------------------------
99/// Numerical kernel specifier for Rust's native 64-bit floating-point types.
100///
101/// This struct is used as a generic argument or associated type to indicate
102/// that the floating-point values for computations should be [`f64`] for real numbers
103/// and [`num::Complex<f64>`] for complex numbers.
104///
105/// It implements the [`RawKernel`] trait, bridging the native raw types to the
106/// [`num-valid`](crate)'s generic numerical abstraction.
107#[derive(Debug, Clone, PartialEq, PartialOrd)]
108pub struct Native64;
109
110impl RawKernel for Native64 {
111    /// The type for a real scalar value in this numerical kernel.
112    type RawReal = f64;
113
114    /// The type for a complex scalar value in this numerical kernel.
115    type RawComplex = Complex<f64>;
116}
117
118//----------------------------------------------------------------------------------------------
119
120//----------------------------------------------------------------------------------------------
121/// Implements the [`FpScalar`] trait for [`f64`].
122///
123/// This trait provides a common interface for floating-point scalar types within
124/// the [`num-valid`](crate) ecosystem.
125impl FpScalar for f64 {
126    type Kind = scalar_kind::Real;
127
128    /// Defines the associated real type, which is [`f64`] itself.
129    type RealType = f64;
130
131    /// Returns a reference to the underlying raw real value.
132    fn as_raw_ref(&self) -> &Self::InnerType {
133        self
134    }
135}
136
137/// Implements the [`FpScalar`] trait for [`num::Complex<f64>`].
138///
139/// This trait provides a common interface for floating-point scalar types within
140/// the [`num-valid`](crate) ecosystem.
141impl FpScalar for Complex<f64> {
142    type Kind = scalar_kind::Complex;
143
144    /// Defines the associated real type for complex numbers, which is [`f64`].
145    type RealType = f64;
146
147    /// Returns a reference to the underlying raw complex value.
148    fn as_raw_ref(&self) -> &Self::InnerType {
149        self
150    }
151}
152//----------------------------------------------------------------------------------------------
153
154//----------------------------------------------------------------------------------------------
155impl Sign for f64 {
156    /// Returns a number with the magnitude of `self` and the sign of `sign`.
157    #[inline(always)]
158    fn kernel_copysign(self, sign: &Self) -> Self {
159        self.copysign(*sign)
160    }
161
162    /// Returns `true` if the value is negative, −0 or NaN with a negative sign.
163    #[inline(always)]
164    fn kernel_is_sign_negative(&self) -> bool {
165        self.is_sign_negative()
166    }
167
168    /// Returns `true` if the value is positive, +0 or NaN with a positive sign.
169    #[inline(always)]
170    fn kernel_is_sign_positive(&self) -> bool {
171        self.is_sign_positive()
172    }
173
174    /// Computes the signum.
175    ///
176    /// The returned value is:
177    /// - `1.0` if the value is positive, +0.0 or +∞
178    /// - `−1.0` if the value is negative, −0.0 or −∞
179    /// - `NaN` if the value is NaN
180    #[inline(always)]
181    fn kernel_signum(self) -> Self {
182        self.signum()
183    }
184}
185
186impl Rounding for f64 {
187    /// Returns the smallest integer greater than or equal to `self`.
188    #[inline(always)]
189    fn kernel_ceil(self) -> Self {
190        self.ceil()
191    }
192
193    /// Returns the largest integer smaller than or equal to `self`.
194    #[inline(always)]
195    fn kernel_floor(self) -> Self {
196        self.floor()
197    }
198
199    /// Returns the fractional part of `self`.
200    #[inline(always)]
201    fn kernel_fract(self) -> Self {
202        self.fract()
203    }
204
205    /// Rounds `self` to the nearest integer, rounding half-way cases away from zero.
206    #[inline(always)]
207    fn kernel_round(self) -> Self {
208        self.round()
209    }
210
211    /// Returns the nearest integer to a number. Rounds half-way cases to the number with an even least significant digit.
212    ///
213    /// This function always returns the precise result.
214    ///
215    /// # Examples
216    /// ```
217    /// use num_valid::{RealScalar, functions::Rounding};
218    ///
219    /// let f = 3.3_f64;
220    /// let g = -3.3_f64;
221    /// let h = 3.5_f64;
222    /// let i = 4.5_f64;
223    ///
224    /// assert_eq!(f.kernel_round_ties_even(), 3.0);
225    /// assert_eq!(g.kernel_round_ties_even(), -3.0);
226    /// assert_eq!(h.kernel_round_ties_even(), 4.0);
227    /// assert_eq!(i.kernel_round_ties_even(), 4.0);
228    /// ```
229    #[inline(always)]
230    fn kernel_round_ties_even(self) -> Self {
231        self.round_ties_even()
232    }
233
234    /// Returns the integer part of `self`. This means that non-integer numbers are always truncated towards zero.
235    ///    
236    /// # Examples
237    /// ```
238    /// use num_valid::{RealScalar, functions::Rounding};
239    ///
240    /// let f = 3.7_f64;
241    /// let g = 3.0_f64;
242    /// let h = -3.7_f64;
243    ///
244    /// assert_eq!(f.kernel_trunc(), 3.0);
245    /// assert_eq!(g.kernel_trunc(), 3.0);
246    /// assert_eq!(h.kernel_trunc(), -3.0);
247    /// ```
248    #[inline(always)]
249    fn kernel_trunc(self) -> Self {
250        self.trunc()
251    }
252}
253
254impl Constants for f64 {
255    /// [Machine epsilon] value for `f64`.
256    ///
257    /// This is the difference between `1.0` and the next larger representable number.
258    ///
259    /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon
260    #[inline(always)]
261    fn epsilon() -> Self {
262        f64::EPSILON
263    }
264
265    /// Build and return the (floating point) value -1. represented by a `f64`.
266    #[inline(always)]
267    fn negative_one() -> Self {
268        -1.
269    }
270
271    /// Build and return the (floating point) value 0.5 represented by the proper type.
272    #[inline(always)]
273    fn one_div_2() -> Self {
274        0.5
275    }
276
277    /// Build and return the (floating point) value `2.0`.
278    #[inline(always)]
279    fn two() -> Self {
280        2.
281    }
282
283    /// Build and return the maximum finite value allowed by the current floating point representation.
284    #[inline(always)]
285    fn max_finite() -> Self {
286        f64::MAX
287    }
288
289    /// Build and return the minimum finite (i.e., the most negative) value allowed by the current floating point representation.
290    #[inline(always)]
291    fn min_finite() -> Self {
292        f64::MIN
293    }
294
295    /// Build and return the (floating point) value `π`.
296    #[inline(always)]
297    fn pi() -> Self {
298        std::f64::consts::PI
299    }
300
301    /// Build and return the (floating point) value `2 π`.
302    #[inline(always)]
303    fn two_pi() -> Self {
304        std::f64::consts::PI * 2.
305    }
306
307    /// Build and return the (floating point) value `π/2`.
308    #[inline(always)]
309    fn pi_div_2() -> Self {
310        std::f64::consts::FRAC_PI_2
311    }
312
313    /// Build and return the natural logarithm of 2, i.e. the (floating point) value `ln(2)`.
314    #[inline(always)]
315    fn ln_2() -> Self {
316        std::f64::consts::LN_2
317    }
318
319    /// Build and return the natural logarithm of 10, i.e. the (floating point) value `ln(10)`.
320    #[inline(always)]
321    fn ln_10() -> Self {
322        std::f64::consts::LN_10
323    }
324
325    /// Build and return the base-10 logarithm of 2, i.e. the (floating point) value `Log_10(2)`.
326    #[inline(always)]
327    fn log10_2() -> Self {
328        std::f64::consts::LOG10_2
329    }
330
331    /// Build and return the base-2 logarithm of 10, i.e. the (floating point) value `Log_2(10)`.
332    #[inline(always)]
333    fn log2_10() -> Self {
334        std::f64::consts::LOG2_10
335    }
336
337    /// Build and return the base-2 logarithm of `e`, i.e. the (floating point) value `Log_2(e)`.
338    #[inline(always)]
339    fn log2_e() -> Self {
340        std::f64::consts::LOG2_E
341    }
342
343    /// Build and return the base-10 logarithm of `e`, i.e. the (floating point) value `Log_10(e)`.
344    #[inline(always)]
345    fn log10_e() -> Self {
346        std::f64::consts::LOG10_E
347    }
348
349    /// Build and return the (floating point) value `e` represented by the proper type.
350    #[inline(always)]
351    fn e() -> Self {
352        std::f64::consts::E
353    }
354}
355
356impl Clamp for f64 {
357    /// Clamp the value within the specified bounds.
358    ///
359    /// Returns `max` if `self` is greater than `max`, and `min` if `self` is less than `min`.
360    /// Otherwise this returns `self`.
361    ///
362    /// Note that this function returns `NaN` if the initial value was `NaN` as well.
363    ///
364    /// # Panics
365    /// Panics if `min` > `max`, `min` is `NaN`, or `max` is `NaN`.
366    /// ```
367    /// use num_valid::functions::Clamp;
368    ///
369    /// assert!(Clamp::clamp_ref(-3.0f64, &-2.0, &1.0) == -2.0);
370    /// assert!(Clamp::clamp_ref(0.0f64, &-2.0, &1.0) == 0.0);
371    /// assert!(Clamp::clamp_ref(2.0f64, &-2.0, &1.0) == 1.0);
372    /// assert!(Clamp::clamp_ref(f64::NAN, &-2.0, &1.0).is_nan());
373    /// ```
374    #[inline(always)]
375    fn clamp_ref(self, min: &Self, max: &Self) -> Self {
376        f64::clamp(self, *min, *max)
377    }
378}
379
380impl Classify for f64 {
381    /// Returns the floating point category of the number. If only one property is going to be tested,
382    /// it is generally faster to use the specific predicate instead.
383    /// ```
384    /// use num_valid::functions::Classify;
385    /// use std::num::FpCategory;
386    ///
387    /// let num = 12.4_f64;
388    /// let inf = f64::INFINITY;
389    ///
390    /// assert_eq!(Classify::classify(&num), FpCategory::Normal);
391    /// assert_eq!(Classify::classify(&inf), FpCategory::Infinite);
392    /// ```
393    #[inline(always)]
394    fn classify(&self) -> FpCategory {
395        f64::classify(*self)
396    }
397}
398
399impl ExpM1 for f64 {
400    /// Returns `e^(self) - 1`` in a way that is accurate even if the number is close to zero.
401    #[inline(always)]
402    fn exp_m1(self) -> Self {
403        f64::exp_m1(self)
404    }
405}
406
407impl Hypot for f64 {
408    /// Compute the distance between the origin and a point (`self`, `other`) on the Euclidean plane.
409    /// Equivalently, compute the length of the hypotenuse of a right-angle triangle with other sides having length `self.abs()` and `other.abs()`.
410    #[inline(always)]
411    fn hypot(self, other: &Self) -> Self {
412        f64::hypot(self, *other)
413    }
414}
415
416impl Ln1p for f64 {
417    /// Returns `ln(1. + self)` (natural logarithm) more accurately than if the operations were performed separately.
418    #[inline(always)]
419    fn ln_1p(self) -> Self {
420        f64::ln_1p(self)
421    }
422}
423
424impl TotalCmp for f64 {
425    #[inline(always)]
426    fn total_cmp(&self, other: &Self) -> Ordering {
427        self.total_cmp(other)
428    }
429}
430
431impl RealScalar for f64 {
432    type RawReal = f64;
433
434    /// Multiplies two products and adds them in one fused operation, rounding to the nearest with only one rounding error.
435    /// `a.kernel_mul_add_mul_mut(&b, &c, &d)` produces a result like `&a * &b + &c * &d`, but stores the result in `a` using its precision.
436    #[inline(always)]
437    fn kernel_mul_add_mul_mut(&mut self, mul: &Self, add_mul1: &Self, add_mul2: &Self) {
438        self.mul_add_assign(*mul, add_mul1 * add_mul2);
439    }
440
441    /// Multiplies two products and subtracts them in one fused operation, rounding to the nearest with only one rounding error.
442    /// `a.kernel_mul_sub_mul_mut(&b, &c, &d)` produces a result like `&a * &b - &c * &d`, but stores the result in `a` using its precision.
443    #[inline(always)]
444    fn kernel_mul_sub_mul_mut(&mut self, mul: &Self, sub_mul1: &Self, sub_mul2: &Self) {
445        self.mul_add_assign(*mul, -sub_mul1 * sub_mul2);
446    }
447
448    /// Try to build a [`f64`] instance from a [`f64`].
449    /// The returned value is `Ok` if the input `value` is finite,
450    /// otherwise the returned value is `ErrorsTryFromf64`.
451    #[inline(always)]
452    fn try_from_f64(value: f64) -> Result<Self, ErrorsTryFromf64<f64>> {
453        StrictFinitePolicy::<f64, 53>::validate(value)
454            .map_err(|e| ErrorsTryFromf64::Output { source: e })
455    }
456}
457
458impl ComplexScalarConstructors for Complex<f64> {
459    type RawComplex = Complex<f64>;
460
461    fn try_new_complex(
462        real_part: f64,
463        imag_part: f64,
464    ) -> Result<Self, ErrorsValidationRawComplex<ErrorsValidationRawReal<f64>>> {
465        validate_complex::<Native64RawRealStrictFinitePolicy>(&real_part, &imag_part)
466            .map(|_| Complex::new(real_part, imag_part))
467    }
468}
469
470/// Implement the [`ComplexScalarGetParts`] trait for the `Complex<f64>` type.
471impl ComplexScalarGetParts for Complex<f64> {
472    /// Get the real part of the complex number.
473    #[inline(always)]
474    fn real_part(&self) -> f64 {
475        self.re
476    }
477
478    /// Get the imaginary part of the complex number.
479    #[inline(always)]
480    fn imag_part(&self) -> f64 {
481        self.im
482    }
483
484    /// Returns a reference to the raw real part of the complex number.
485    #[inline(always)]
486    fn raw_real_part(&self) -> &f64 {
487        &self.re
488    }
489
490    /// Returns a reference to the raw imaginary part of the complex number.
491    #[inline(always)]
492    fn raw_imag_part(&self) -> &f64 {
493        &self.im
494    }
495}
496
497/// Implement the [`ComplexScalarSetParts`] trait for the `Complex<f64>` type.
498impl ComplexScalarSetParts for Complex<f64> {
499    /// Set the value of the real part.
500    ///
501    /// # Panics
502    /// In **debug builds**, this will panic if `real_part` is not finite.
503    /// This check is disabled in release builds for performance.
504    #[inline(always)]
505    fn set_real_part(&mut self, real_part: f64) {
506        debug_assert!(
507            real_part.is_finite(),
508            "The real part is not finite (i.e. is infinite or NaN)."
509        );
510        self.re = real_part;
511    }
512
513    /// Set the value of the imaginary part.
514    ///
515    /// # Panics
516    /// In **debug builds**, this will panic if `imag_part` is not finite.
517    /// This check is disabled in release builds for performance.
518    #[inline(always)]
519    fn set_imaginary_part(&mut self, imag_part: f64) {
520        debug_assert!(
521            imag_part.is_finite(),
522            "The imaginary part is not finite (i.e. is infinite or NaN)."
523        );
524        self.im = imag_part;
525    }
526}
527
528/// Implement the [`ComplexScalarMutateParts`] trait for the `Complex<f64>` type.
529impl ComplexScalarMutateParts for Complex<f64> {
530    /// Add the value of the the real coefficient `c` to real part of `self`.
531    ///
532    /// # Panics
533    /// In **debug builds**, this will panic if the real part of `self` is not finite after the addition.
534    /// This check is disabled in release builds for performance.
535    #[inline(always)]
536    fn add_to_real_part(&mut self, c: &f64) {
537        self.re += c;
538
539        debug_assert!(
540            self.re.is_finite(),
541            "The real part is not finite (i.e. is infinite or NaN)."
542        );
543    }
544
545    /// Add the value of the the real coefficient `c` to imaginary part of `self`.
546    ///
547    /// # Panics
548    /// In **debug builds**, this will panic if the imaginary part of `self` is not finite after the addition.
549    /// This check is disabled in release builds for performance.
550    #[inline(always)]
551    fn add_to_imaginary_part(&mut self, c: &f64) {
552        self.im += c;
553
554        debug_assert!(
555            self.im.is_finite(),
556            "The imaginary part is not finite (i.e. is infinite or NaN)."
557        );
558    }
559
560    /// Multiply the value of the real part by the real coefficient `c`.
561    ///
562    /// # Panics
563    /// In **debug builds**, this will panic if the real part of `self` is not finite after the multiplication.
564    /// This check is disabled in release builds for performance.
565    #[inline(always)]
566    fn multiply_real_part(&mut self, c: &f64) {
567        self.re *= c;
568
569        debug_assert!(
570            self.re.is_finite(),
571            "The real part is not finite (i.e. is infinite or NaN)."
572        );
573    }
574
575    /// Multiply the value of the imaginary part by the real coefficient `c`.
576    ///
577    /// # Panics
578    /// In **debug builds**, this will panic if the imaginary part of `self` is not finite after the multiplication.
579    /// This check is disabled in release builds for performance.
580    #[inline(always)]
581    fn multiply_imaginary_part(&mut self, c: &f64) {
582        self.im *= c;
583
584        debug_assert!(
585            self.im.is_finite(),
586            "The imaginary part is not finite (i.e. is infinite or NaN)."
587        );
588    }
589}
590
591/// Implement the [`ComplexScalar`] trait for the `Complex<f64>` type.
592impl ComplexScalar for Complex<f64> {
593    fn into_parts(self) -> (Self::RealType, Self::RealType) {
594        (self.re, self.im)
595    }
596}
597
598//----------------------------------------------------------------------------------------------
599
600//----------------------------------------------------------------------------------------------
601#[duplicate_item(
602    T trait_comment;
603    [f64] ["Implementation of the [`MulAddRef`] trait for `f64`."];
604    [Complex<f64>] ["Implementation of the [`MulAddRef`] trait for `Complex<f64>`."];
605)]
606#[doc = trait_comment]
607impl MulAddRef for T {
608    /// Multiplies and adds in one fused operation, rounding to the nearest with only one rounding error.
609    ///
610    /// `a.mul_add(b, c)` produces a result like `a * &b + &c`.
611    #[inline(always)]
612    fn mul_add_ref(self, b: &Self, c: &Self) -> Self {
613        <Self as num::traits::MulAdd>::mul_add(self, *b, *c)
614    }
615}
616//----------------------------------------------------------------------------------------------
617
618// =============================================================================
619// Implementations for f64 and Complex<f64>
620// =============================================================================
621
622#[duplicate_item(
623    T;
624    [f64];
625    [Complex::<f64>];
626)]
627impl RawScalarTrigonometric for T {
628    #[duplicate_item(
629        unchecked_method method;
630        [unchecked_sin]  [sin];
631        [unchecked_asin] [asin];
632        [unchecked_cos]  [cos];
633        [unchecked_acos] [acos];
634        [unchecked_tan]  [tan];
635        [unchecked_atan] [atan];
636    )]
637    #[inline(always)]
638    fn unchecked_method(self) -> Self {
639        T::method(self)
640    }
641}
642
643#[duplicate_item(
644    T;
645    [f64];
646    [Complex::<f64>];
647)]
648impl RawScalarHyperbolic for T {
649    #[duplicate_item(
650        unchecked_method  method;
651        [unchecked_sinh]  [sinh];
652        [unchecked_asinh] [asinh];
653        [unchecked_cosh]  [cosh];
654        [unchecked_acosh] [acosh];
655        [unchecked_tanh]  [tanh];
656        [unchecked_atanh] [atanh];
657    )]
658    #[inline(always)]
659    fn unchecked_method(self) -> Self {
660        T::method(self)
661    }
662}
663
664impl RawScalarPow for f64 {
665    #[duplicate_item(
666        unchecked_method             exponent_type;
667        [unchecked_pow_exponent_i8]  [i8];
668        [unchecked_pow_exponent_i16] [i16];
669        [unchecked_pow_exponent_u8]  [u8];
670        [unchecked_pow_exponent_u16] [u16];
671    )]
672    #[inline(always)]
673    fn unchecked_method(self, exponent: &exponent_type) -> f64 {
674        f64::powi(self, (*exponent).into())
675    }
676
677    #[inline(always)]
678    fn unchecked_pow_exponent_i32(self, exponent: &i32) -> Self {
679        f64::powi(self, *exponent)
680    }
681
682    #[duplicate_item(
683        unchecked_method               exponent_type;
684        [unchecked_pow_exponent_i64]   [i64];
685        [unchecked_pow_exponent_i128]  [i128];
686        [unchecked_pow_exponent_isize] [isize];
687        [unchecked_pow_exponent_u32]   [u32];
688        [unchecked_pow_exponent_u64]   [u64];
689        [unchecked_pow_exponent_u128]  [u128];
690        [unchecked_pow_exponent_usize] [usize];
691    )]
692    #[inline(always)]
693    fn unchecked_method(self, exponent: &exponent_type) -> f64 {
694        f64::powi(
695            self,
696            (*exponent)
697                .try_into()
698                .expect("The exponent {exponent} cannot be converted to an integer of type i32"),
699        )
700    }
701}
702
703impl RawScalarTrait for f64 {
704    type ValidationErrors = ErrorsValidationRawReal<f64>;
705
706    #[inline(always)]
707    fn raw_zero(_precision: u32) -> f64 {
708        0.
709    }
710
711    #[inline(always)]
712    fn is_zero(&self) -> bool {
713        <Self as Zero>::is_zero(self)
714    }
715    #[inline(always)]
716    fn raw_one(_precision: u32) -> f64 {
717        1.
718    }
719
720    #[duplicate_item(
721        unchecked_method       method;
722        [unchecked_reciprocal] [recip];
723        [unchecked_exp]        [exp];
724        [unchecked_sqrt]       [sqrt];
725        [unchecked_ln]         [ln];
726        [unchecked_log2]       [log2];
727        [unchecked_log10]      [log10];
728    )]
729    #[inline(always)]
730    fn unchecked_method(self) -> f64 {
731        f64::method(self)
732    }
733
734    /// Multiplies and adds in one fused operation, rounding to the nearest with only one rounding error.
735    ///
736    /// `a.mul_add(b, c)` produces a result like `a * &b + &c`.
737    #[inline(always)]
738    fn unchecked_mul_add(self, b: &Self, c: &Self) -> Self {
739        f64::mul_add(self, *b, *c)
740    }
741
742    #[inline(always)]
743    fn compute_hash<H: Hasher>(&self, state: &mut H) {
744        debug_assert!(
745            self.is_finite(),
746            "Hashing a non-finite f64 value (i.e., NaN or Infinity) may lead to inconsistent results."
747        );
748        if self == &0.0 {
749            // Hash all zeros (positive and negative) to the same value
750            0.0f64.to_bits().hash(state);
751        } else {
752            self.to_bits().hash(state);
753        }
754    }
755}
756
757impl RawRealTrait for f64 {
758    type RawComplex = Complex<f64>;
759
760    #[inline(always)]
761    fn unchecked_abs(self) -> f64 {
762        f64::abs(self)
763    }
764
765    #[inline(always)]
766    fn unchecked_atan2(self, denominator: &Self) -> Self {
767        f64::atan2(self, *denominator)
768    }
769
770    #[inline(always)]
771    fn unchecked_pow_exponent_real(self, exponent: &Self) -> Self {
772        f64::powf(self, *exponent)
773    }
774
775    #[inline(always)]
776    fn unchecked_hypot(self, other: &Self) -> Self {
777        f64::hypot(self, *other)
778    }
779
780    #[inline(always)]
781    fn unchecked_ln_1p(self) -> Self {
782        f64::ln_1p(self)
783    }
784
785    #[inline(always)]
786    fn unchecked_exp_m1(self) -> Self {
787        f64::exp_m1(self)
788    }
789
790    /// Multiplies two pairs and adds them in one fused operation, rounding to the nearest with only one rounding error.
791    /// `a.unchecked_mul_add_mul_mut(&b, &c, &d)` produces a result like `&a * &b + &c * &d`, but stores the result in `a` using its precision.
792    #[inline(always)]
793    fn unchecked_mul_add_mul_mut(&mut self, mul: &Self, add_mul1: &Self, add_mul2: &Self) {
794        self.mul_add_assign(*mul, add_mul1 * add_mul2);
795    }
796
797    /// Multiplies two pairs and subtracts them in one fused operation, rounding to the nearest with only one rounding error.
798    /// `a.unchecked_mul_sub_mul_mut(&b, &c, &d)` produces a result like `&a * &b - &c * &d`, but stores the result in `a` using its precision.
799    #[inline(always)]
800    fn unchecked_mul_sub_mul_mut(&mut self, mul: &Self, sub_mul1: &Self, sub_mul2: &Self) {
801        self.mul_add_assign(*mul, -sub_mul1 * sub_mul2);
802    }
803
804    #[inline(always)]
805    fn raw_total_cmp(&self, other: &Self) -> Ordering {
806        f64::total_cmp(self, other)
807    }
808
809    /// Clamps the value within the specified bounds.
810    #[inline(always)]
811    fn raw_clamp(self, min: &Self, max: &Self) -> Self {
812        f64::clamp(self, *min, *max)
813    }
814
815    #[inline(always)]
816    fn raw_classify(&self) -> FpCategory {
817        f64::classify(*self)
818    }
819
820    #[inline(always)]
821    fn raw_two(_precision: u32) -> Self {
822        2.
823    }
824
825    #[inline(always)]
826    fn raw_one_div_2(_precision: u32) -> Self {
827        0.5
828    }
829
830    #[inline(always)]
831    fn raw_pi(_precision: u32) -> Self {
832        std::f64::consts::PI
833    }
834
835    #[inline(always)]
836    fn raw_two_pi(_precision: u32) -> Self {
837        2. * std::f64::consts::PI
838    }
839
840    #[inline(always)]
841    fn raw_pi_div_2(_precision: u32) -> Self {
842        std::f64::consts::FRAC_PI_2
843    }
844
845    #[inline(always)]
846    fn raw_max_finite(_precision: u32) -> Self {
847        f64::MAX
848    }
849
850    #[inline(always)]
851    fn raw_min_finite(_precision: u32) -> Self {
852        f64::MIN
853    }
854
855    #[inline(always)]
856    fn raw_epsilon(_precision: u32) -> Self {
857        f64::EPSILON
858    }
859
860    #[inline(always)]
861    fn raw_ln_2(_precision: u32) -> Self {
862        std::f64::consts::LN_2
863    }
864
865    #[inline(always)]
866    fn raw_ln_10(_precision: u32) -> Self {
867        std::f64::consts::LN_10
868    }
869
870    #[inline(always)]
871    fn raw_log10_2(_precision: u32) -> Self {
872        std::f64::consts::LOG10_2
873    }
874
875    #[inline(always)]
876    fn raw_log2_10(_precision: u32) -> Self {
877        std::f64::consts::LOG2_10
878    }
879
880    #[inline(always)]
881    fn raw_log2_e(_precision: u32) -> Self {
882        std::f64::consts::LOG2_E
883    }
884
885    #[inline(always)]
886    fn raw_log10_e(_precision: u32) -> Self {
887        std::f64::consts::LOG10_E
888    }
889
890    #[inline(always)]
891    fn raw_e(_precision: u32) -> Self {
892        std::f64::consts::E
893    }
894
895    #[inline(always)]
896    fn try_new_raw_real_from_f64<RealPolicy: ValidationPolicyReal<Value = Self>>(
897        value: f64,
898    ) -> Result<Self, ErrorsTryFromf64<f64>> {
899        RealPolicy::validate(value).map_err(|e| ErrorsTryFromf64::Output { source: e })
900    }
901
902    #[inline(always)]
903    fn precision(&self) -> u32 {
904        53 // f64 has 53 bits of precision
905    }
906
907    #[inline(always)]
908    fn truncate_to_usize(self) -> Result<usize, ErrorsRawRealToInteger<f64, usize>> {
909        if !self.is_finite() {
910            return Err(ErrorsRawRealToInteger::NotFinite {
911                value: self,
912                backtrace: capture_backtrace(),
913            });
914        }
915
916        match self.checked_as::<usize>() {
917            Some(value) => Ok(value),
918            None => Err(ErrorsRawRealToInteger::OutOfRange {
919                value: self,
920                min: usize::MIN,
921                max: usize::MAX,
922                backtrace: capture_backtrace(),
923            }),
924        }
925    }
926}
927
928impl RawScalarPow for Complex<f64> {
929    #[duplicate_item(
930        unchecked_method             exponent_type;
931        [unchecked_pow_exponent_i8]  [i8];
932        [unchecked_pow_exponent_i16] [i16];
933    )]
934    #[inline(always)]
935    fn unchecked_method(self, exponent: &exponent_type) -> Self {
936        Complex::<f64>::powi(&self, (*exponent).into())
937    }
938
939    #[inline(always)]
940    fn unchecked_pow_exponent_i32(self, exponent: &i32) -> Self {
941        Complex::<f64>::powi(&self, *exponent)
942    }
943
944    #[duplicate_item(
945        unchecked_method               exponent_type;
946        [unchecked_pow_exponent_i64]   [i64];
947        [unchecked_pow_exponent_i128]  [i128];
948        [unchecked_pow_exponent_isize] [isize];
949    )]
950    #[inline(always)]
951    fn unchecked_method(self, exponent: &exponent_type) -> Self {
952        Complex::<f64>::powi(
953            &self,
954            (*exponent)
955                .try_into()
956                .expect("The exponent {exponent} cannot be converted to an integer of type i32"),
957        )
958    }
959
960    #[duplicate_item(
961        unchecked_method             exponent_type;
962        [unchecked_pow_exponent_u8]  [u8];
963        [unchecked_pow_exponent_u16] [u16];
964    )]
965    #[inline(always)]
966    fn unchecked_method(self, exponent: &exponent_type) -> Self {
967        Complex::<f64>::powu(&self, (*exponent).into())
968    }
969
970    #[inline(always)]
971    fn unchecked_pow_exponent_u32(self, exponent: &u32) -> Self {
972        Complex::<f64>::powu(&self, *exponent)
973    }
974
975    #[duplicate_item(
976        unchecked_method               exponent_type;
977        [unchecked_pow_exponent_u64]   [u64];
978        [unchecked_pow_exponent_u128]  [u128];
979        [unchecked_pow_exponent_usize] [usize];
980    )]
981    #[inline(always)]
982    fn unchecked_method(self, exponent: &exponent_type) -> Self {
983        Complex::<f64>::powu(
984            &self,
985            (*exponent)
986                .try_into()
987                .expect("The exponent {exponent} cannot be converted to an integer of type u32"),
988        )
989    }
990}
991
992impl RawScalarTrait for Complex<f64> {
993    type ValidationErrors = ErrorsValidationRawComplex<ErrorsValidationRawReal<f64>>;
994
995    #[inline(always)]
996    fn raw_zero(_precision: u32) -> Self {
997        Complex::new(0., 0.)
998    }
999
1000    #[inline(always)]
1001    fn is_zero(&self) -> bool {
1002        <Self as Zero>::is_zero(self)
1003    }
1004
1005    #[inline(always)]
1006    fn raw_one(_precision: u32) -> Self {
1007        Complex::new(1., 0.)
1008    }
1009
1010    #[duplicate_item(
1011        unchecked_method       method;
1012        [unchecked_exp]        [exp];
1013        [unchecked_sqrt]       [sqrt];
1014        [unchecked_ln]         [ln];
1015        [unchecked_log10]      [log10];
1016    )]
1017    #[inline(always)]
1018    fn unchecked_method(self) -> Self {
1019        Complex::<f64>::method(self)
1020    }
1021
1022    #[inline(always)]
1023    fn unchecked_reciprocal(self) -> Self {
1024        Complex::<f64>::inv(&self)
1025    }
1026
1027    #[inline(always)]
1028    fn unchecked_log2(self) -> Self {
1029        Complex::<f64>::ln(self) / std::f64::consts::LN_2
1030    }
1031
1032    /// Multiplies and adds in one fused operation, rounding to the nearest with only one rounding error.
1033    ///
1034    /// `a.mul_add(b, c)` produces a result like `a * &b + &c`.
1035    #[inline(always)]
1036    fn unchecked_mul_add(self, b: &Self, c: &Self) -> Self {
1037        Complex::<f64>::mul_add(self, *b, *c)
1038    }
1039
1040    fn compute_hash<H: Hasher>(&self, state: &mut H) {
1041        RawComplexTrait::raw_real_part(self).compute_hash(state);
1042        RawComplexTrait::raw_imag_part(self).compute_hash(state);
1043    }
1044}
1045
1046impl RawComplexTrait for Complex<f64> {
1047    type RawReal = f64;
1048
1049    fn new_unchecked_raw_complex(real: f64, imag: f64) -> Self {
1050        Complex::<f64>::new(real, imag)
1051    }
1052
1053    /// Returns a mutable reference to the real part of the complex number.
1054    fn mut_raw_real_part(&mut self) -> &mut f64 {
1055        &mut self.re
1056    }
1057
1058    /// Returns a mutable reference to the imaginary part of the complex number.
1059    fn mut_raw_imag_part(&mut self) -> &mut f64 {
1060        &mut self.im
1061    }
1062
1063    #[inline(always)]
1064    fn unchecked_abs(self) -> f64 {
1065        Complex::<f64>::norm(self)
1066    }
1067
1068    #[inline(always)]
1069    fn raw_real_part(&self) -> &f64 {
1070        &self.re
1071    }
1072
1073    #[inline(always)]
1074    fn raw_imag_part(&self) -> &f64 {
1075        &self.im
1076    }
1077
1078    #[inline(always)]
1079    fn unchecked_arg(self) -> f64 {
1080        Complex::<f64>::arg(self)
1081    }
1082
1083    #[inline(always)]
1084    fn unchecked_pow_exponent_real(self, exponent: &f64) -> Self {
1085        Complex::<f64>::powf(self, *exponent)
1086    }
1087}
1088
1089#[cfg(test)]
1090mod tests {
1091    use crate::{
1092        core::errors::{ErrorsValidationRawComplex, ErrorsValidationRawReal},
1093        functions::TotalCmp,
1094    };
1095
1096    mod real {
1097        use super::*;
1098        use crate::Constants;
1099
1100        #[test]
1101        fn test_constants() {
1102            assert_eq!(<f64 as Constants>::epsilon(), f64::EPSILON);
1103            assert_eq!(<f64 as Constants>::negative_one(), -1.0);
1104            assert_eq!(<f64 as Constants>::one_div_2(), 0.5);
1105            assert_eq!(<f64 as Constants>::two(), 2.0);
1106            assert_eq!(<f64 as Constants>::max_finite(), f64::MAX);
1107            assert_eq!(<f64 as Constants>::min_finite(), f64::MIN);
1108            assert_eq!(<f64 as Constants>::pi(), std::f64::consts::PI);
1109            assert_eq!(<f64 as Constants>::two_pi(), std::f64::consts::PI * 2.0);
1110            assert_eq!(<f64 as Constants>::pi_div_2(), std::f64::consts::FRAC_PI_2);
1111            assert_eq!(<f64 as Constants>::ln_2(), std::f64::consts::LN_2);
1112            assert_eq!(<f64 as Constants>::ln_10(), std::f64::consts::LN_10);
1113            assert_eq!(<f64 as Constants>::log10_2(), std::f64::consts::LOG10_2);
1114            assert_eq!(<f64 as Constants>::log2_10(), std::f64::consts::LOG2_10);
1115            assert_eq!(<f64 as Constants>::log2_e(), std::f64::consts::LOG2_E);
1116            assert_eq!(<f64 as Constants>::log10_e(), std::f64::consts::LOG10_E);
1117            assert_eq!(<f64 as Constants>::e(), std::f64::consts::E);
1118        }
1119
1120        #[test]
1121        #[allow(clippy::op_ref)]
1122        fn multiply_ref() {
1123            let a = 2.0f64;
1124            let b = 3.0f64;
1125            let result = a * &b;
1126            assert_eq!(result, 6.0);
1127        }
1128
1129        #[test]
1130        fn total_cmp() {
1131            let a = 2.0f64;
1132            let b = 3.0f64;
1133            assert_eq!(
1134                <f64 as TotalCmp>::total_cmp(&a, &b),
1135                std::cmp::Ordering::Less
1136            );
1137            assert_eq!(
1138                <f64 as TotalCmp>::total_cmp(&b, &a),
1139                std::cmp::Ordering::Greater
1140            );
1141            assert_eq!(
1142                <f64 as TotalCmp>::total_cmp(&a, &a),
1143                std::cmp::Ordering::Equal
1144            );
1145        }
1146
1147        mod from_f64 {
1148            use crate::{RealScalar, backends::native64::validated::RealNative64StrictFinite};
1149
1150            #[test]
1151            fn test_from_f64_valid_constants() {
1152                // Test with mathematical constants (known valid values)
1153                let pi = RealNative64StrictFinite::from_f64(std::f64::consts::PI);
1154                assert_eq!(pi.as_ref(), &std::f64::consts::PI);
1155
1156                let e = RealNative64StrictFinite::from_f64(std::f64::consts::E);
1157                assert_eq!(e.as_ref(), &std::f64::consts::E);
1158
1159                let sqrt2 = RealNative64StrictFinite::from_f64(std::f64::consts::SQRT_2);
1160                assert_eq!(sqrt2.as_ref(), &std::f64::consts::SQRT_2);
1161            }
1162
1163            #[test]
1164            fn test_from_f64_valid_values() {
1165                // Test with regular valid values
1166                let x = RealNative64StrictFinite::from_f64(42.0);
1167                assert_eq!(x.as_ref(), &42.0);
1168
1169                let y = RealNative64StrictFinite::from_f64(-3.0);
1170                assert_eq!(y.as_ref(), &-3.0);
1171
1172                let z = RealNative64StrictFinite::from_f64(0.0);
1173                assert_eq!(z.as_ref(), &0.0);
1174            }
1175
1176            #[test]
1177            #[should_panic(expected = "RealScalar::from_f64() failed")]
1178            fn test_from_f64_nan_panics() {
1179                let _ = RealNative64StrictFinite::from_f64(f64::NAN);
1180            }
1181
1182            #[test]
1183            #[should_panic(expected = "RealScalar::from_f64() failed")]
1184            fn test_from_f64_infinity_panics() {
1185                let _ = RealNative64StrictFinite::from_f64(f64::INFINITY);
1186            }
1187
1188            #[test]
1189            #[should_panic(expected = "RealScalar::from_f64() failed")]
1190            fn test_from_f64_neg_infinity_panics() {
1191                let _ = RealNative64StrictFinite::from_f64(f64::NEG_INFINITY);
1192            }
1193
1194            #[test]
1195            #[should_panic(expected = "RealScalar::from_f64() failed")]
1196            fn test_from_f64_subnormal_panics() {
1197                // Test with a subnormal value (very small but non-zero)
1198                let _ = RealNative64StrictFinite::from_f64(f64::MIN_POSITIVE / 2.0);
1199            }
1200
1201            #[test]
1202            fn test_try_from_f64_error_handling() {
1203                // Verify try_from_f64 still works for error handling
1204                assert!(RealNative64StrictFinite::try_from_f64(f64::NAN).is_err());
1205                assert!(RealNative64StrictFinite::try_from_f64(f64::INFINITY).is_err());
1206                assert!(RealNative64StrictFinite::try_from_f64(f64::NEG_INFINITY).is_err());
1207
1208                // Valid values work with try_from_f64
1209                assert!(RealNative64StrictFinite::try_from_f64(3.0).is_ok());
1210                assert!(RealNative64StrictFinite::try_from_f64(0.0).is_ok());
1211            }
1212        }
1213
1214        mod truncate_to_usize {
1215            use crate::{core::errors::ErrorsRawRealToInteger, kernels::RawRealTrait};
1216
1217            #[test]
1218            fn test_f64_truncate_to_usize_valid() {
1219                assert_eq!(42.0_f64.truncate_to_usize().unwrap(), 42);
1220                assert_eq!(42.9_f64.truncate_to_usize().unwrap(), 42);
1221                assert_eq!(0.0_f64.truncate_to_usize().unwrap(), 0);
1222            }
1223
1224            #[test]
1225            fn test_f64_truncate_to_usize_not_finite() {
1226                assert!(matches!(
1227                    f64::NAN.truncate_to_usize(),
1228                    Err(ErrorsRawRealToInteger::NotFinite { .. })
1229                ));
1230                assert!(matches!(
1231                    f64::INFINITY.truncate_to_usize(),
1232                    Err(ErrorsRawRealToInteger::NotFinite { .. })
1233                ));
1234                assert!(matches!(
1235                    f64::NEG_INFINITY.truncate_to_usize(),
1236                    Err(ErrorsRawRealToInteger::NotFinite { .. })
1237                ));
1238            }
1239
1240            #[test]
1241            fn test_f64_truncate_to_usize_out_of_range() {
1242                // Negative value
1243                assert!(matches!(
1244                    (-1.0_f64).truncate_to_usize(),
1245                    Err(ErrorsRawRealToInteger::OutOfRange { .. })
1246                ));
1247                // Value too large
1248                assert!(matches!(
1249                    ((usize::MAX as f64) + 1.0).truncate_to_usize(),
1250                    Err(ErrorsRawRealToInteger::OutOfRange { .. })
1251                ));
1252
1253                // this is exactly usize::MAX, which is out of range because f64 cannot represent all integers above 2^53 exactly
1254                assert!(matches!(
1255                    (usize::MAX as f64).truncate_to_usize(),
1256                    Err(ErrorsRawRealToInteger::OutOfRange { .. })
1257                ));
1258            }
1259        }
1260    }
1261
1262    mod complex {
1263        use super::*;
1264        use crate::{
1265            ComplexScalarConstructors, ComplexScalarGetParts, ComplexScalarMutateParts,
1266            ComplexScalarSetParts,
1267        };
1268        use num::{Complex, Zero};
1269
1270        #[test]
1271        fn real_part() {
1272            let c1 = Complex::new(1.23, 4.56);
1273            assert_eq!(c1.real_part(), 1.23);
1274
1275            let c2 = Complex::new(-7.89, 0.12);
1276            assert_eq!(c2.real_part(), -7.89);
1277
1278            let c3 = Complex::new(0.0, 10.0);
1279            assert_eq!(c3.real_part(), 0.0);
1280
1281            let c_nan_re = Complex::new(f64::NAN, 5.0);
1282            assert!(c_nan_re.real_part().is_nan());
1283
1284            let c_inf_re = Complex::new(f64::INFINITY, 5.0);
1285            assert!(c_inf_re.real_part().is_infinite());
1286            assert!(c_inf_re.real_part().is_sign_positive());
1287
1288            let c_neg_inf_re = Complex::new(f64::NEG_INFINITY, 5.0);
1289            assert!(c_neg_inf_re.real_part().is_infinite());
1290            assert!(c_neg_inf_re.real_part().is_sign_negative());
1291        }
1292
1293        #[test]
1294        fn imag_part() {
1295            let c1 = Complex::new(1.23, 4.56);
1296            assert_eq!(c1.imag_part(), 4.56);
1297
1298            let c2 = Complex::new(7.89, -0.12);
1299            assert_eq!(c2.imag_part(), -0.12);
1300
1301            let c3 = Complex::new(10.0, 0.0);
1302            assert_eq!(c3.imag_part(), 0.0);
1303
1304            let c_nan_im = Complex::new(5.0, f64::NAN);
1305            assert!(c_nan_im.imag_part().is_nan());
1306
1307            let c_inf_im = Complex::new(5.0, f64::INFINITY);
1308            assert!(c_inf_im.imag_part().is_infinite());
1309            assert!(c_inf_im.imag_part().is_sign_positive());
1310
1311            let c_neg_inf_im = Complex::new(5.0, f64::NEG_INFINITY);
1312            assert!(c_neg_inf_im.imag_part().is_infinite());
1313            assert!(c_neg_inf_im.imag_part().is_sign_negative());
1314        }
1315
1316        #[test]
1317        fn try_new_complex() {
1318            let r1 = 1.23;
1319            let i1 = 4.56;
1320            let c1 = Complex::<f64>::try_new_complex(r1, i1).unwrap();
1321            assert_eq!(c1.re, r1);
1322            assert_eq!(c1.im, i1);
1323            assert_eq!(c1.real_part(), r1);
1324            assert_eq!(c1.imag_part(), i1);
1325
1326            let r2 = -7.89;
1327            let i2 = -0.12;
1328            let c2 = Complex::<f64>::try_new_complex(r2, i2).unwrap();
1329            assert_eq!(c2.re, r2);
1330            assert_eq!(c2.im, i2);
1331            assert_eq!(c2.real_part(), r2);
1332            assert_eq!(c2.imag_part(), i2);
1333
1334            let r3 = 0.0;
1335            let i3 = 0.0;
1336            let c3 = Complex::<f64>::try_new_complex(r3, i3).unwrap();
1337            assert_eq!(c3.re, r3);
1338            assert_eq!(c3.im, i3);
1339            assert!(c3.is_zero()); // Assuming Zero trait is available and implemented
1340
1341            let c_nan_re = Complex::<f64>::try_new_complex(f64::NAN, 5.0).unwrap_err();
1342            assert!(matches!(
1343                c_nan_re,
1344                ErrorsValidationRawComplex::InvalidRealPart { .. }
1345            ));
1346
1347            let c_inf_im = Complex::<f64>::try_new_complex(10.0, f64::INFINITY).unwrap_err();
1348            assert!(matches!(
1349                c_inf_im,
1350                ErrorsValidationRawComplex::InvalidImaginaryPart { .. }
1351            ));
1352
1353            let c_nan_re_inf_im =
1354                Complex::<f64>::try_new_complex(f64::NAN, f64::INFINITY).unwrap_err();
1355            assert!(matches!(
1356                c_nan_re_inf_im,
1357                ErrorsValidationRawComplex::InvalidBothParts { .. }
1358            ));
1359        }
1360
1361        #[test]
1362        fn try_new_pure_real() {
1363            let r1 = 1.23;
1364            let c1 = Complex::<f64>::try_new_pure_real(r1).unwrap();
1365            assert_eq!(c1.re, r1);
1366            assert_eq!(c1.im, 0.0);
1367
1368            let c_nan = Complex::<f64>::try_new_pure_real(f64::NAN).unwrap_err();
1369            assert!(matches!(
1370                c_nan,
1371                ErrorsValidationRawComplex::InvalidRealPart {
1372                    source: ErrorsValidationRawReal::IsNaN { .. }
1373                }
1374            ));
1375        }
1376
1377        #[test]
1378        fn try_new_pure_imaginary() {
1379            let i1 = 1.23;
1380            let c1 = Complex::<f64>::try_new_pure_imaginary(i1).unwrap();
1381            assert_eq!(c1.re, 0.0);
1382            assert_eq!(c1.im, i1);
1383
1384            let c_nan = Complex::<f64>::try_new_pure_imaginary(f64::NAN).unwrap_err();
1385            assert!(matches!(
1386                c_nan,
1387                ErrorsValidationRawComplex::InvalidImaginaryPart {
1388                    source: ErrorsValidationRawReal::IsNaN { .. }
1389                }
1390            ));
1391        }
1392
1393        #[test]
1394        fn add_to_real_part() {
1395            let mut c = Complex::new(1.0, 2.0);
1396            c.add_to_real_part(&3.0);
1397            assert_eq!(c.re, 4.0);
1398            assert_eq!(c.im, 2.0);
1399
1400            c.add_to_real_part(&-5.0);
1401            assert_eq!(c.re, -1.0);
1402            assert_eq!(c.im, 2.0);
1403        }
1404
1405        #[cfg(debug_assertions)]
1406        #[test]
1407        #[should_panic(expected = "The real part is not finite (i.e. is infinite or NaN).")]
1408        fn add_to_real_part_nan() {
1409            let mut c = Complex::new(1.0, 2.0);
1410            c.add_to_real_part(&f64::NAN);
1411        }
1412
1413        #[test]
1414        fn add_to_imaginary_part() {
1415            let mut c = Complex::new(1.0, 2.0);
1416            c.add_to_imaginary_part(&3.0);
1417            assert_eq!(c.re, 1.0);
1418            assert_eq!(c.im, 5.0);
1419
1420            c.add_to_imaginary_part(&-4.0);
1421            assert_eq!(c.re, 1.0);
1422            assert_eq!(c.im, 1.0);
1423        }
1424
1425        #[cfg(debug_assertions)]
1426        #[test]
1427        #[should_panic(expected = "The imaginary part is not finite (i.e. is infinite or NaN).")]
1428        fn add_to_imaginary_part_nan() {
1429            let mut c = Complex::new(1.0, 2.0);
1430            c.add_to_imaginary_part(&f64::NAN);
1431        }
1432
1433        #[test]
1434        fn multiply_real_part() {
1435            let mut c = Complex::new(1.0, 2.0);
1436            c.multiply_real_part(&3.0);
1437            assert_eq!(c.re, 3.0);
1438            assert_eq!(c.im, 2.0);
1439
1440            c.multiply_real_part(&-2.0);
1441            assert_eq!(c.re, -6.0);
1442            assert_eq!(c.im, 2.0);
1443        }
1444
1445        #[cfg(debug_assertions)]
1446        #[test]
1447        #[should_panic(expected = "The real part is not finite (i.e. is infinite or NaN).")]
1448        fn multiply_real_part_nan() {
1449            let mut c = Complex::new(1.0, 2.0);
1450            c.multiply_real_part(&f64::NAN);
1451        }
1452
1453        #[test]
1454        fn multiply_imaginary_part() {
1455            let mut c = Complex::new(1.0, 2.0);
1456            c.multiply_imaginary_part(&3.0);
1457            assert_eq!(c.re, 1.0);
1458            assert_eq!(c.im, 6.0);
1459
1460            c.multiply_imaginary_part(&-0.5);
1461            assert_eq!(c.re, 1.0);
1462            assert_eq!(c.im, -3.0);
1463        }
1464
1465        #[cfg(debug_assertions)]
1466        #[test]
1467        #[should_panic(expected = "The imaginary part is not finite (i.e. is infinite or NaN).")]
1468        fn multiply_imaginary_part_nan() {
1469            let mut c = Complex::new(1.0, 2.0);
1470            c.multiply_imaginary_part(&f64::NAN);
1471        }
1472
1473        #[test]
1474        fn set_real_part() {
1475            let mut c = Complex::new(1.0, 2.0);
1476            c.set_real_part(3.0);
1477            assert_eq!(c.re, 3.0);
1478            assert_eq!(c.im, 2.0);
1479
1480            c.set_real_part(-4.0);
1481            assert_eq!(c.re, -4.0);
1482            assert_eq!(c.im, 2.0);
1483        }
1484
1485        #[cfg(debug_assertions)]
1486        #[test]
1487        #[should_panic(expected = "The real part is not finite (i.e. is infinite or NaN).")]
1488        fn set_real_part_nan() {
1489            let mut c = Complex::new(1.0, 2.0);
1490            c.set_real_part(f64::NAN);
1491        }
1492
1493        #[test]
1494        fn set_imaginary_part() {
1495            let mut c = Complex::new(1.0, 2.0);
1496            c.set_imaginary_part(3.0);
1497            assert_eq!(c.re, 1.0);
1498            assert_eq!(c.im, 3.0);
1499
1500            c.set_imaginary_part(-4.0);
1501            assert_eq!(c.re, 1.0);
1502            assert_eq!(c.im, -4.0);
1503        }
1504
1505        #[cfg(debug_assertions)]
1506        #[test]
1507        #[should_panic(expected = "The imaginary part is not finite (i.e. is infinite or NaN).")]
1508        fn set_imaginary_part_nan() {
1509            let mut c = Complex::new(1.0, 2.0);
1510            c.set_imaginary_part(f64::NAN);
1511        }
1512
1513        #[test]
1514        #[allow(clippy::op_ref)]
1515        fn multiply_ref() {
1516            let c1 = Complex::new(1.0, 2.0);
1517            let c2 = Complex::new(3.0, 4.0);
1518            let result = c1 * &c2;
1519            assert_eq!(result, Complex::new(-5.0, 10.0)); // (1*3 - 2*4) + (1*4 + 2*3)i
1520        }
1521    }
1522}