num_valid/kernels/
native64.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>`]).
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`] and [`ComplexScalar`]) and Rust's
14//! concrete, hardware-accelerated numeric types. It adapts [`f64`] and [`Complex<f64>`]
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`] 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`] and its
22//! super-traits (e.g., [`FpScalar`], [`Rounding`], [`Sign`], [`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`] for [`num::Complex<f64>`]. 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`], [`ComplexScalarGetParts`], [`ComplexScalarSetParts`], and
35//! [`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 [`NumKernel`](crate::NumKernel) trait. It is used
43//!   in generic contexts to select this native kernel, specifying [`f64`] as its
44//!   [`Real`](crate::NumKernel::Real) type and [`num::Complex<f64>`] as its
45//!   [`Complex`](crate::NumKernel::Complex) type.
46//! - **Trait Implementations**: This module is primarily composed of `impl` blocks that
47//!   satisfy the contracts of core traits (`FpScalar`, `RealScalar`, `ComplexScalar`,
48//!   `Arithmetic`, `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`] for `f64`
57//! uses a [`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 [`native64_validated`](crate::kernels::native64_validated) module.
63
64use crate::{
65    Arithmetic, ComplexScalar, Constants, FpScalar, MulAddRef, RealScalar,
66    functions::{
67        Clamp, Classify, ExpM1, Hypot, Ln1p, Rounding, Sign, TotalCmp,
68        complex::{
69            ComplexScalarConstructors, ComplexScalarGetParts, ComplexScalarMutateParts,
70            ComplexScalarSetParts,
71        },
72    },
73    kernels::RawKernel,
74    scalar_kind,
75    validation::{
76        ErrorsTryFromf64, ErrorsValidationRawComplex, ErrorsValidationRawReal,
77        Native64RawRealStrictFinitePolicy, StrictFinitePolicy, validate_complex,
78    },
79};
80use num::Complex;
81use num_traits::MulAddAssign;
82use std::{cmp::Ordering, num::FpCategory};
83use try_create::ValidationPolicy;
84
85//----------------------------------------------------------------------------------------------
86/// Numerical kernel specifier for Rust's native 64-bit floating-point types.
87///
88/// This struct is used as a generic argument or associated type to indicate
89/// that the floating-point values for computations should be [`f64`] for real numbers
90/// and [`num::Complex<f64>`] for complex numbers.
91///
92/// It implements the [`RawKernel`] trait, bridging the native raw types to the
93/// [`num-valid`](crate)'s generic numerical abstraction.
94#[derive(Debug, Clone, PartialEq, PartialOrd)]
95pub struct Native64;
96
97impl RawKernel for Native64 {
98    /// The type for a real scalar value in this numerical kernel.
99    type RawReal = f64;
100
101    /// The type for a complex scalar value in this numerical kernel.
102    type RawComplex = Complex<f64>;
103}
104
105//----------------------------------------------------------------------------------------------
106
107//----------------------------------------------------------------------------------------------
108/*
109/// Implements the [`NumKernel`] trait for [`Native64`].
110impl NumKernel for Native64 {
111    /// The type for a real scalar value in this numerical kernel.
112    type Real = f64;
113
114    /// The type for a complex scalar value in this numerical kernel.
115    type Complex = Complex<f64>;
116}
117*/
118//----------------------------------------------------------------------------------------------
119
120//----------------------------------------------------------------------------------------------
121/// Marker implementation of [`Arithmetic`] for [`f64`].
122///
123/// This signifies that [`f64`] supports the standard arithmetic operations
124/// (addition, subtraction, multiplication, division, remainder, negation)
125/// as defined by the traits aggregated within [`Arithmetic`].
126impl Arithmetic for f64 {}
127
128/// Marker implementation of [`Arithmetic`] for [`num::Complex<f64>`].
129///
130/// This signifies that [`Complex<f64>`] supports the standard arithmetic operations
131/// (addition, subtraction, multiplication, division, negation)
132/// as defined by the traits aggregated within [`Arithmetic`].
133impl Arithmetic for Complex<f64> {}
134//----------------------------------------------------------------------------------------------
135
136//----------------------------------------------------------------------------------------------
137/// Implements the [`FpScalar`] trait for [`f64`].
138///
139/// This trait provides a common interface for floating-point scalar types within
140/// the [`num-valid`](crate) ecosystem.
141impl FpScalar for f64 {
142    type Kind = scalar_kind::Real;
143
144    /// Defines the associated real type, which is [`f64`] itself.
145    type RealType = f64;
146}
147
148/// Implements the [`FpScalar`] trait for [`num::Complex<f64>`].
149///
150/// This trait provides a common interface for floating-point scalar types within
151/// the [`num-valid`](crate) ecosystem.
152impl FpScalar for Complex<f64> {
153    type Kind = scalar_kind::Complex;
154
155    /// Defines the associated real type for complex numbers, which is [`f64`].
156    type RealType = f64;
157}
158//----------------------------------------------------------------------------------------------
159
160//----------------------------------------------------------------------------------------------
161impl Sign for f64 {
162    /// Returns a number with the magnitude of `self` and the sign of `sign`.
163    #[inline(always)]
164    fn kernel_copysign(self, sign: &Self) -> Self {
165        self.copysign(*sign)
166    }
167
168    /// Returns `true` if the value is negative, −0 or NaN with a negative sign.
169    #[inline(always)]
170    fn kernel_is_sign_negative(&self) -> bool {
171        self.is_sign_negative()
172    }
173
174    /// Returns `true` if the value is positive, +0 or NaN with a positive sign.
175    #[inline(always)]
176    fn kernel_is_sign_positive(&self) -> bool {
177        self.is_sign_positive()
178    }
179
180    /// Computes the signum.
181    ///
182    /// The returned value is:
183    /// - `1.0` if the value is positive, +0.0 or +∞
184    /// - `−1.0` if the value is negative, −0.0 or −∞
185    /// - `NaN` if the value is NaN
186    #[inline(always)]
187    fn kernel_signum(self) -> Self {
188        self.signum()
189    }
190}
191
192impl Rounding for f64 {
193    /// Returns the smallest integer greater than or equal to `self`.
194    #[inline(always)]
195    fn kernel_ceil(self) -> Self {
196        self.ceil()
197    }
198
199    /// Returns the largest integer smaller than or equal to `self`.
200    #[inline(always)]
201    fn kernel_floor(self) -> Self {
202        self.floor()
203    }
204
205    /// Returns the fractional part of `self`.
206    #[inline(always)]
207    fn kernel_fract(self) -> Self {
208        self.fract()
209    }
210
211    /// Rounds `self` to the nearest integer, rounding half-way cases away from zero.
212    #[inline(always)]
213    fn kernel_round(self) -> Self {
214        self.round()
215    }
216
217    /// Returns the nearest integer to a number. Rounds half-way cases to the number with an even least significant digit.
218    ///
219    /// This function always returns the precise result.
220    ///
221    /// # Examples
222    /// ```
223    /// use num_valid::{RealScalar, functions::Rounding};
224    ///
225    /// let f = 3.3_f64;
226    /// let g = -3.3_f64;
227    /// let h = 3.5_f64;
228    /// let i = 4.5_f64;
229    ///
230    /// assert_eq!(f.kernel_round_ties_even(), 3.0);
231    /// assert_eq!(g.kernel_round_ties_even(), -3.0);
232    /// assert_eq!(h.kernel_round_ties_even(), 4.0);
233    /// assert_eq!(i.kernel_round_ties_even(), 4.0);
234    /// ```
235    #[inline(always)]
236    fn kernel_round_ties_even(self) -> Self {
237        self.round_ties_even()
238    }
239
240    /// Returns the integer part of `self`. This means that non-integer numbers are always truncated towards zero.
241    ///    
242    /// # Examples
243    /// ```
244    /// use num_valid::{RealScalar, functions::Rounding};
245    ///
246    /// let f = 3.7_f64;
247    /// let g = 3.0_f64;
248    /// let h = -3.7_f64;
249    ///
250    /// assert_eq!(f.kernel_trunc(), 3.0);
251    /// assert_eq!(g.kernel_trunc(), 3.0);
252    /// assert_eq!(h.kernel_trunc(), -3.0);
253    /// ```
254    #[inline(always)]
255    fn kernel_trunc(self) -> Self {
256        self.trunc()
257    }
258}
259
260impl Constants for f64 {
261    /// [Machine epsilon] value for `f64`.
262    ///
263    /// This is the difference between `1.0` and the next larger representable number.
264    ///
265    /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon
266    #[inline(always)]
267    fn epsilon() -> Self {
268        f64::EPSILON
269    }
270
271    /// Build and return the (floating point) value -1. represented by a `f64`.
272    #[inline(always)]
273    fn negative_one() -> Self {
274        -1.
275    }
276
277    /// Build and return the (floating point) value 0.5 represented by the proper type.
278    #[inline(always)]
279    fn one_div_2() -> Self {
280        0.5
281    }
282
283    /// Build and return the (floating point) value `2.0`.
284    #[inline(always)]
285    fn two() -> Self {
286        2.
287    }
288
289    /// Build and return the maximum finite value allowed by the current floating point representation.
290    #[inline(always)]
291    fn max_finite() -> Self {
292        f64::MAX
293    }
294
295    /// Build and return the minimum finite (i.e., the most negative) value allowed by the current floating point representation.
296    #[inline(always)]
297    fn min_finite() -> Self {
298        f64::MIN
299    }
300
301    /// Build and return the (floating point) value `π`.
302    #[inline(always)]
303    fn pi() -> Self {
304        std::f64::consts::PI
305    }
306
307    /// Build and return the (floating point) value `2 π`.
308    #[inline(always)]
309    fn two_pi() -> Self {
310        std::f64::consts::PI * 2.
311    }
312
313    /// Build and return the (floating point) value `π/2`.
314    #[inline(always)]
315    fn pi_div_2() -> Self {
316        std::f64::consts::FRAC_PI_2
317    }
318
319    /// Build and return the natural logarithm of 2, i.e. the (floating point) value `ln(2)`.
320    #[inline(always)]
321    fn ln_2() -> Self {
322        std::f64::consts::LN_2
323    }
324
325    /// Build and return the natural logarithm of 10, i.e. the (floating point) value `ln(10)`.
326    #[inline(always)]
327    fn ln_10() -> Self {
328        std::f64::consts::LN_10
329    }
330
331    /// Build and return the base-10 logarithm of 2, i.e. the (floating point) value `Log_10(2)`.
332    #[inline(always)]
333    fn log10_2() -> Self {
334        std::f64::consts::LOG10_2
335    }
336
337    /// Build and return the base-2 logarithm of 10, i.e. the (floating point) value `Log_2(10)`.
338    #[inline(always)]
339    fn log2_10() -> Self {
340        std::f64::consts::LOG2_10
341    }
342
343    /// Build and return the base-2 logarithm of `e`, i.e. the (floating point) value `Log_2(e)`.
344    #[inline(always)]
345    fn log2_e() -> Self {
346        std::f64::consts::LOG2_E
347    }
348
349    /// Build and return the base-10 logarithm of `e`, i.e. the (floating point) value `Log_10(e)`.
350    #[inline(always)]
351    fn log10_e() -> Self {
352        std::f64::consts::LOG10_E
353    }
354
355    /// Build and return the (floating point) value `e` represented by the proper type.
356    #[inline(always)]
357    fn e() -> Self {
358        std::f64::consts::E
359    }
360}
361
362impl Clamp for f64 {
363    /// Clamp the value within the specified bounds.
364    ///
365    /// Returns `max` if `self` is greater than `max`, and `min` if `self` is less than `min`.
366    /// Otherwise this returns `self`.
367    ///
368    /// Note that this function returns `NaN` if the initial value was `NaN` as well.
369    ///
370    /// # Panics
371    /// Panics if `min` > `max`, `min` is `NaN`, or `max` is `NaN`.
372    /// ```
373    /// use num_valid::functions::Clamp;
374    ///
375    /// assert!((-3.0f64).kernel_clamp(&-2.0, &1.0) == -2.0);
376    /// assert!((0.0f64).kernel_clamp(&-2.0, &1.0) == 0.0);
377    /// assert!((2.0f64).kernel_clamp(&-2.0, &1.0) == 1.0);
378    /// assert!((f64::NAN).kernel_clamp(&-2.0, &1.0).is_nan());
379    /// ```
380    #[inline(always)]
381    fn kernel_clamp(self, min: &Self, max: &Self) -> Self {
382        self.clamp(*min, *max)
383    }
384}
385
386impl Classify for f64 {
387    /// Returns the floating point category of the number. If only one property is going to be tested,
388    /// it is generally faster to use the specific predicate instead.
389    /// ```
390    /// use num_valid::functions::Classify;
391    /// use std::num::FpCategory;
392    ///
393    /// let num = 12.4_f64;
394    /// let inf = f64::INFINITY;
395    ///
396    /// assert_eq!(num.kernel_classify(), FpCategory::Normal);
397    /// assert_eq!(inf.kernel_classify(), FpCategory::Infinite);
398    /// ```
399    #[inline(always)]
400    fn kernel_classify(&self) -> FpCategory {
401        self.classify()
402    }
403}
404
405impl ExpM1 for f64 {
406    /// Returns `e^(self) - 1`` in a way that is accurate even if the number is close to zero.
407    #[inline(always)]
408    fn kernel_exp_m1(self) -> Self {
409        self.exp_m1()
410    }
411}
412
413impl Hypot for f64 {
414    /// Compute the distance between the origin and a point (`self`, `other`) on the Euclidean plane.
415    /// Equivalently, compute the length of the hypotenuse of a right-angle triangle with other sides having length `self.abs()` and `other.abs()`.
416    #[inline(always)]
417    fn kernel_hypot(self, other: &Self) -> Self {
418        self.hypot(*other)
419    }
420}
421
422impl Ln1p for f64 {
423    /// Returns `ln(1. + self)` (natural logarithm) more accurately than if the operations were performed separately.
424    #[inline(always)]
425    fn kernel_ln_1p(self) -> Self {
426        self.ln_1p()
427    }
428}
429
430impl TotalCmp for f64 {
431    #[inline(always)]
432    fn total_cmp(&self, other: &Self) -> Ordering {
433        self.total_cmp(other)
434    }
435}
436
437impl RealScalar for f64 {
438    type RawReal = f64;
439
440    /// Multiplies two products and adds them in one fused operation, rounding to the nearest with only one rounding error.
441    /// `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.
442    #[inline(always)]
443    fn kernel_mul_add_mul_mut(&mut self, mul: &Self, add_mul1: &Self, add_mul2: &Self) {
444        self.mul_add_assign(*mul, add_mul1 * add_mul2);
445    }
446
447    /// Multiplies two products and subtracts them in one fused operation, rounding to the nearest with only one rounding error.
448    /// `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.
449    #[inline(always)]
450    fn kernel_mul_sub_mul_mut(&mut self, mul: &Self, sub_mul1: &Self, sub_mul2: &Self) {
451        self.mul_add_assign(*mul, -sub_mul1 * sub_mul2);
452    }
453
454    /// Try to build a [`f64`] instance from a [`f64`].
455    /// The returned value is `Ok` if the input `value` is finite,
456    /// otherwise the returned value is `ErrorsTryFromf64`.
457    #[inline(always)]
458    fn try_from_f64(value: f64) -> Result<Self, ErrorsTryFromf64<f64>> {
459        StrictFinitePolicy::<f64, 53>::validate(value)
460            .map_err(|e| ErrorsTryFromf64::Output { source: e })
461    }
462}
463
464impl ComplexScalarConstructors for Complex<f64> {
465    type RawComplex = Complex<f64>;
466
467    fn try_new_complex(
468        real_part: f64,
469        imag_part: f64,
470    ) -> Result<Self, ErrorsValidationRawComplex<ErrorsValidationRawReal<f64>>> {
471        validate_complex::<Native64RawRealStrictFinitePolicy>(&real_part, &imag_part)
472            .map(|_| Complex::new(real_part, imag_part))
473    }
474}
475
476/// Implement the [`ComplexScalarGetParts`] trait for the `Complex<f64>` type.
477impl ComplexScalarGetParts for Complex<f64> {
478    /// Get the real part of the complex number.
479    #[inline(always)]
480    fn real_part(&self) -> f64 {
481        self.re
482    }
483
484    /// Get the imaginary part of the complex number.
485    #[inline(always)]
486    fn imag_part(&self) -> f64 {
487        self.im
488    }
489
490    /// Returns a reference to the raw real part of the complex number.
491    #[inline(always)]
492    fn raw_real_part(&self) -> &f64 {
493        &self.re
494    }
495
496    /// Returns a reference to the raw imaginary part of the complex number.
497    #[inline(always)]
498    fn raw_imag_part(&self) -> &f64 {
499        &self.im
500    }
501}
502
503/// Implement the [`ComplexScalarSetParts`] trait for the `Complex<f64>` type.
504impl ComplexScalarSetParts for Complex<f64> {
505    /// Set the value of the real part.
506    ///
507    /// # Panics
508    /// In **debug builds**, this will panic if `real_part` is not finite.
509    /// This check is disabled in release builds for performance.
510    #[inline(always)]
511    fn set_real_part(&mut self, real_part: f64) {
512        debug_assert!(
513            real_part.is_finite(),
514            "The real part is not finite (i.e. is infinite or NaN)."
515        );
516        self.re = real_part;
517    }
518
519    /// Set the value of the imaginary part.
520    ///
521    /// # Panics
522    /// In **debug builds**, this will panic if `imag_part` is not finite.
523    /// This check is disabled in release builds for performance.
524    #[inline(always)]
525    fn set_imaginary_part(&mut self, imag_part: f64) {
526        debug_assert!(
527            imag_part.is_finite(),
528            "The imaginary part is not finite (i.e. is infinite or NaN)."
529        );
530        self.im = imag_part;
531    }
532}
533
534/// Implement the [`ComplexScalarMutateParts`] trait for the `Complex<f64>` type.
535impl ComplexScalarMutateParts for Complex<f64> {
536    /// Add the value of the the real coefficient `c` to real part of `self`.
537    ///
538    /// # Panics
539    /// In **debug builds**, this will panic if the real part of `self` is not finite after the addition.
540    /// This check is disabled in release builds for performance.
541    #[inline(always)]
542    fn add_to_real_part(&mut self, c: &f64) {
543        self.re += c;
544
545        debug_assert!(
546            self.re.is_finite(),
547            "The real part is not finite (i.e. is infinite or NaN)."
548        );
549    }
550
551    /// Add the value of the the real coefficient `c` to imaginary part of `self`.
552    ///
553    /// # Panics
554    /// In **debug builds**, this will panic if the imaginary part of `self` is not finite after the addition.
555    /// This check is disabled in release builds for performance.
556    #[inline(always)]
557    fn add_to_imaginary_part(&mut self, c: &f64) {
558        self.im += c;
559
560        debug_assert!(
561            self.im.is_finite(),
562            "The imaginary part is not finite (i.e. is infinite or NaN)."
563        );
564    }
565
566    /// Multiply the value of the real part by the real coefficient `c`.
567    ///
568    /// # Panics
569    /// In **debug builds**, this will panic if the real part of `self` is not finite after the multiplication.
570    /// This check is disabled in release builds for performance.
571    #[inline(always)]
572    fn multiply_real_part(&mut self, c: &f64) {
573        self.re *= c;
574
575        debug_assert!(
576            self.re.is_finite(),
577            "The real part is not finite (i.e. is infinite or NaN)."
578        );
579    }
580
581    /// Multiply the value of the imaginary part by the real coefficient `c`.
582    ///
583    /// # Panics
584    /// In **debug builds**, this will panic if the imaginary part of `self` is not finite after the multiplication.
585    /// This check is disabled in release builds for performance.
586    #[inline(always)]
587    fn multiply_imaginary_part(&mut self, c: &f64) {
588        self.im *= c;
589
590        debug_assert!(
591            self.im.is_finite(),
592            "The imaginary part is not finite (i.e. is infinite or NaN)."
593        );
594    }
595}
596
597/// Implement the [`ComplexScalar`] trait for the `Complex<f64>` type.
598impl ComplexScalar for Complex<f64> {}
599
600//----------------------------------------------------------------------------------------------
601
602//----------------------------------------------------------------------------------------------
603#[duplicate::duplicate_item(
604    T trait_comment;
605    [f64] ["Implementation of the [`MulAddRef`] trait for `f64`."];
606    [Complex<f64>] ["Implementation of the [`MulAddRef`] trait for `Complex<f64>`."];
607)]
608#[doc = trait_comment]
609impl MulAddRef for T {
610    /// Multiplies and adds in one fused operation, rounding to the nearest with only one rounding error.
611    ///
612    /// `a.mul_add(b, c)` produces a result like `a * &b + &c`.
613    #[inline(always)]
614    fn mul_add_ref(self, b: &Self, c: &Self) -> Self {
615        <Self as num::traits::MulAdd>::mul_add(self, *b, *c)
616    }
617}
618//----------------------------------------------------------------------------------------------
619
620#[cfg(test)]
621mod tests {
622    use crate::{
623        functions::TotalCmp,
624        validation::{ErrorsValidationRawComplex, ErrorsValidationRawReal},
625    };
626
627    mod real {
628        use super::*;
629        use crate::Constants;
630
631        #[test]
632        fn test_constants() {
633            assert_eq!(<f64 as Constants>::epsilon(), f64::EPSILON);
634            assert_eq!(<f64 as Constants>::negative_one(), -1.0);
635            assert_eq!(<f64 as Constants>::one_div_2(), 0.5);
636            assert_eq!(<f64 as Constants>::two(), 2.0);
637            assert_eq!(<f64 as Constants>::max_finite(), f64::MAX);
638            assert_eq!(<f64 as Constants>::min_finite(), f64::MIN);
639            assert_eq!(<f64 as Constants>::pi(), std::f64::consts::PI);
640            assert_eq!(<f64 as Constants>::two_pi(), std::f64::consts::PI * 2.0);
641            assert_eq!(<f64 as Constants>::pi_div_2(), std::f64::consts::FRAC_PI_2);
642            assert_eq!(<f64 as Constants>::ln_2(), std::f64::consts::LN_2);
643            assert_eq!(<f64 as Constants>::ln_10(), std::f64::consts::LN_10);
644            assert_eq!(<f64 as Constants>::log10_2(), std::f64::consts::LOG10_2);
645            assert_eq!(<f64 as Constants>::log2_10(), std::f64::consts::LOG2_10);
646            assert_eq!(<f64 as Constants>::log2_e(), std::f64::consts::LOG2_E);
647            assert_eq!(<f64 as Constants>::log10_e(), std::f64::consts::LOG10_E);
648            assert_eq!(<f64 as Constants>::e(), std::f64::consts::E);
649        }
650
651        #[test]
652        #[allow(clippy::op_ref)]
653        fn multiply_ref() {
654            let a = 2.0f64;
655            let b = 3.0f64;
656            let result = a * &b;
657            assert_eq!(result, 6.0);
658        }
659
660        #[test]
661        fn total_cmp() {
662            let a = 2.0f64;
663            let b = 3.0f64;
664            assert_eq!(
665                <f64 as TotalCmp>::total_cmp(&a, &b),
666                std::cmp::Ordering::Less
667            );
668            assert_eq!(
669                <f64 as TotalCmp>::total_cmp(&b, &a),
670                std::cmp::Ordering::Greater
671            );
672            assert_eq!(
673                <f64 as TotalCmp>::total_cmp(&a, &a),
674                std::cmp::Ordering::Equal
675            );
676        }
677    }
678
679    mod complex {
680        use super::*;
681        use crate::{
682            ComplexScalarConstructors, ComplexScalarGetParts, ComplexScalarMutateParts,
683            ComplexScalarSetParts,
684        };
685        use num::{Complex, Zero};
686
687        #[test]
688        fn real_part() {
689            let c1 = Complex::new(1.23, 4.56);
690            assert_eq!(c1.real_part(), 1.23);
691
692            let c2 = Complex::new(-7.89, 0.12);
693            assert_eq!(c2.real_part(), -7.89);
694
695            let c3 = Complex::new(0.0, 10.0);
696            assert_eq!(c3.real_part(), 0.0);
697
698            let c_nan_re = Complex::new(f64::NAN, 5.0);
699            assert!(c_nan_re.real_part().is_nan());
700
701            let c_inf_re = Complex::new(f64::INFINITY, 5.0);
702            assert!(c_inf_re.real_part().is_infinite());
703            assert!(c_inf_re.real_part().is_sign_positive());
704
705            let c_neg_inf_re = Complex::new(f64::NEG_INFINITY, 5.0);
706            assert!(c_neg_inf_re.real_part().is_infinite());
707            assert!(c_neg_inf_re.real_part().is_sign_negative());
708        }
709
710        #[test]
711        fn imag_part() {
712            let c1 = Complex::new(1.23, 4.56);
713            assert_eq!(c1.imag_part(), 4.56);
714
715            let c2 = Complex::new(7.89, -0.12);
716            assert_eq!(c2.imag_part(), -0.12);
717
718            let c3 = Complex::new(10.0, 0.0);
719            assert_eq!(c3.imag_part(), 0.0);
720
721            let c_nan_im = Complex::new(5.0, f64::NAN);
722            assert!(c_nan_im.imag_part().is_nan());
723
724            let c_inf_im = Complex::new(5.0, f64::INFINITY);
725            assert!(c_inf_im.imag_part().is_infinite());
726            assert!(c_inf_im.imag_part().is_sign_positive());
727
728            let c_neg_inf_im = Complex::new(5.0, f64::NEG_INFINITY);
729            assert!(c_neg_inf_im.imag_part().is_infinite());
730            assert!(c_neg_inf_im.imag_part().is_sign_negative());
731        }
732
733        #[test]
734        fn try_new_complex() {
735            let r1 = 1.23;
736            let i1 = 4.56;
737            let c1 = Complex::<f64>::try_new_complex(r1, i1).unwrap();
738            assert_eq!(c1.re, r1);
739            assert_eq!(c1.im, i1);
740            assert_eq!(c1.real_part(), r1);
741            assert_eq!(c1.imag_part(), i1);
742
743            let r2 = -7.89;
744            let i2 = -0.12;
745            let c2 = Complex::<f64>::try_new_complex(r2, i2).unwrap();
746            assert_eq!(c2.re, r2);
747            assert_eq!(c2.im, i2);
748            assert_eq!(c2.real_part(), r2);
749            assert_eq!(c2.imag_part(), i2);
750
751            let r3 = 0.0;
752            let i3 = 0.0;
753            let c3 = Complex::<f64>::try_new_complex(r3, i3).unwrap();
754            assert_eq!(c3.re, r3);
755            assert_eq!(c3.im, i3);
756            assert!(c3.is_zero()); // Assuming Zero trait is available and implemented
757
758            let c_nan_re = Complex::<f64>::try_new_complex(f64::NAN, 5.0).unwrap_err();
759            assert!(matches!(
760                c_nan_re,
761                ErrorsValidationRawComplex::InvalidRealPart { .. }
762            ));
763
764            let c_inf_im = Complex::<f64>::try_new_complex(10.0, f64::INFINITY).unwrap_err();
765            assert!(matches!(
766                c_inf_im,
767                ErrorsValidationRawComplex::InvalidImaginaryPart { .. }
768            ));
769
770            let c_nan_re_inf_im =
771                Complex::<f64>::try_new_complex(f64::NAN, f64::INFINITY).unwrap_err();
772            assert!(matches!(
773                c_nan_re_inf_im,
774                ErrorsValidationRawComplex::InvalidBothParts { .. }
775            ));
776        }
777
778        #[test]
779        fn try_new_pure_real() {
780            let r1 = 1.23;
781            let c1 = Complex::<f64>::try_new_pure_real(r1).unwrap();
782            assert_eq!(c1.re, r1);
783            assert_eq!(c1.im, 0.0);
784
785            let c_nan = Complex::<f64>::try_new_pure_real(f64::NAN).unwrap_err();
786            assert!(matches!(
787                c_nan,
788                ErrorsValidationRawComplex::InvalidRealPart {
789                    source: ErrorsValidationRawReal::IsNaN { .. }
790                }
791            ));
792        }
793
794        #[test]
795        fn try_new_pure_imaginary() {
796            let i1 = 1.23;
797            let c1 = Complex::<f64>::try_new_pure_imaginary(i1).unwrap();
798            assert_eq!(c1.re, 0.0);
799            assert_eq!(c1.im, i1);
800
801            let c_nan = Complex::<f64>::try_new_pure_imaginary(f64::NAN).unwrap_err();
802            assert!(matches!(
803                c_nan,
804                ErrorsValidationRawComplex::InvalidImaginaryPart {
805                    source: ErrorsValidationRawReal::IsNaN { .. }
806                }
807            ));
808        }
809
810        #[test]
811        fn add_to_real_part() {
812            let mut c = Complex::new(1.0, 2.0);
813            c.add_to_real_part(&3.0);
814            assert_eq!(c.re, 4.0);
815            assert_eq!(c.im, 2.0);
816
817            c.add_to_real_part(&-5.0);
818            assert_eq!(c.re, -1.0);
819            assert_eq!(c.im, 2.0);
820        }
821
822        #[cfg(debug_assertions)]
823        #[test]
824        #[should_panic(expected = "The real part is not finite (i.e. is infinite or NaN).")]
825        fn add_to_real_part_nan() {
826            let mut c = Complex::new(1.0, 2.0);
827            c.add_to_real_part(&f64::NAN);
828        }
829
830        #[test]
831        fn add_to_imaginary_part() {
832            let mut c = Complex::new(1.0, 2.0);
833            c.add_to_imaginary_part(&3.0);
834            assert_eq!(c.re, 1.0);
835            assert_eq!(c.im, 5.0);
836
837            c.add_to_imaginary_part(&-4.0);
838            assert_eq!(c.re, 1.0);
839            assert_eq!(c.im, 1.0);
840        }
841
842        #[cfg(debug_assertions)]
843        #[test]
844        #[should_panic(expected = "The imaginary part is not finite (i.e. is infinite or NaN).")]
845        fn add_to_imaginary_part_nan() {
846            let mut c = Complex::new(1.0, 2.0);
847            c.add_to_imaginary_part(&f64::NAN);
848        }
849
850        #[test]
851        fn multiply_real_part() {
852            let mut c = Complex::new(1.0, 2.0);
853            c.multiply_real_part(&3.0);
854            assert_eq!(c.re, 3.0);
855            assert_eq!(c.im, 2.0);
856
857            c.multiply_real_part(&-2.0);
858            assert_eq!(c.re, -6.0);
859            assert_eq!(c.im, 2.0);
860        }
861
862        #[cfg(debug_assertions)]
863        #[test]
864        #[should_panic(expected = "The real part is not finite (i.e. is infinite or NaN).")]
865        fn multiply_real_part_nan() {
866            let mut c = Complex::new(1.0, 2.0);
867            c.multiply_real_part(&f64::NAN);
868        }
869
870        #[test]
871        fn multiply_imaginary_part() {
872            let mut c = Complex::new(1.0, 2.0);
873            c.multiply_imaginary_part(&3.0);
874            assert_eq!(c.re, 1.0);
875            assert_eq!(c.im, 6.0);
876
877            c.multiply_imaginary_part(&-0.5);
878            assert_eq!(c.re, 1.0);
879            assert_eq!(c.im, -3.0);
880        }
881
882        #[cfg(debug_assertions)]
883        #[test]
884        #[should_panic(expected = "The imaginary part is not finite (i.e. is infinite or NaN).")]
885        fn multiply_imaginary_part_nan() {
886            let mut c = Complex::new(1.0, 2.0);
887            c.multiply_imaginary_part(&f64::NAN);
888        }
889
890        #[test]
891        fn set_real_part() {
892            let mut c = Complex::new(1.0, 2.0);
893            c.set_real_part(3.0);
894            assert_eq!(c.re, 3.0);
895            assert_eq!(c.im, 2.0);
896
897            c.set_real_part(-4.0);
898            assert_eq!(c.re, -4.0);
899            assert_eq!(c.im, 2.0);
900        }
901
902        #[cfg(debug_assertions)]
903        #[test]
904        #[should_panic(expected = "The real part is not finite (i.e. is infinite or NaN).")]
905        fn set_real_part_nan() {
906            let mut c = Complex::new(1.0, 2.0);
907            c.set_real_part(f64::NAN);
908        }
909
910        #[test]
911        fn set_imaginary_part() {
912            let mut c = Complex::new(1.0, 2.0);
913            c.set_imaginary_part(3.0);
914            assert_eq!(c.re, 1.0);
915            assert_eq!(c.im, 3.0);
916
917            c.set_imaginary_part(-4.0);
918            assert_eq!(c.re, 1.0);
919            assert_eq!(c.im, -4.0);
920        }
921
922        #[cfg(debug_assertions)]
923        #[test]
924        #[should_panic(expected = "The imaginary part is not finite (i.e. is infinite or NaN).")]
925        fn set_imaginary_part_nan() {
926            let mut c = Complex::new(1.0, 2.0);
927            c.set_imaginary_part(f64::NAN);
928        }
929
930        #[test]
931        #[allow(clippy::op_ref)]
932        fn multiply_ref() {
933            let c1 = Complex::new(1.0, 2.0);
934            let c2 = Complex::new(3.0, 4.0);
935            let result = c1 * &c2;
936            assert_eq!(result, Complex::new(-5.0, 10.0)); // (1*3 - 2*4) + (1*4 + 2*3)i
937        }
938    }
939}