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