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