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 fn into_parts(self) -> (Self::RealType, Self::RealType) {
610 (self.re, self.im)
611 }
612}
613
614//----------------------------------------------------------------------------------------------
615
616//----------------------------------------------------------------------------------------------
617#[duplicate::duplicate_item(
618 T trait_comment;
619 [f64] ["Implementation of the [`MulAddRef`] trait for `f64`."];
620 [Complex<f64>] ["Implementation of the [`MulAddRef`] trait for `Complex<f64>`."];
621)]
622#[doc = trait_comment]
623impl MulAddRef for T {
624 /// Multiplies and adds in one fused operation, rounding to the nearest with only one rounding error.
625 ///
626 /// `a.mul_add(b, c)` produces a result like `a * &b + &c`.
627 #[inline(always)]
628 fn mul_add_ref(self, b: &Self, c: &Self) -> Self {
629 <Self as num::traits::MulAdd>::mul_add(self, *b, *c)
630 }
631}
632//----------------------------------------------------------------------------------------------
633
634#[cfg(test)]
635mod tests {
636 use crate::{
637 functions::TotalCmp,
638 validation::{ErrorsValidationRawComplex, ErrorsValidationRawReal},
639 };
640
641 mod real {
642 use super::*;
643 use crate::Constants;
644
645 #[test]
646 fn test_constants() {
647 assert_eq!(<f64 as Constants>::epsilon(), f64::EPSILON);
648 assert_eq!(<f64 as Constants>::negative_one(), -1.0);
649 assert_eq!(<f64 as Constants>::one_div_2(), 0.5);
650 assert_eq!(<f64 as Constants>::two(), 2.0);
651 assert_eq!(<f64 as Constants>::max_finite(), f64::MAX);
652 assert_eq!(<f64 as Constants>::min_finite(), f64::MIN);
653 assert_eq!(<f64 as Constants>::pi(), std::f64::consts::PI);
654 assert_eq!(<f64 as Constants>::two_pi(), std::f64::consts::PI * 2.0);
655 assert_eq!(<f64 as Constants>::pi_div_2(), std::f64::consts::FRAC_PI_2);
656 assert_eq!(<f64 as Constants>::ln_2(), std::f64::consts::LN_2);
657 assert_eq!(<f64 as Constants>::ln_10(), std::f64::consts::LN_10);
658 assert_eq!(<f64 as Constants>::log10_2(), std::f64::consts::LOG10_2);
659 assert_eq!(<f64 as Constants>::log2_10(), std::f64::consts::LOG2_10);
660 assert_eq!(<f64 as Constants>::log2_e(), std::f64::consts::LOG2_E);
661 assert_eq!(<f64 as Constants>::log10_e(), std::f64::consts::LOG10_E);
662 assert_eq!(<f64 as Constants>::e(), std::f64::consts::E);
663 }
664
665 #[test]
666 #[allow(clippy::op_ref)]
667 fn multiply_ref() {
668 let a = 2.0f64;
669 let b = 3.0f64;
670 let result = a * &b;
671 assert_eq!(result, 6.0);
672 }
673
674 #[test]
675 fn total_cmp() {
676 let a = 2.0f64;
677 let b = 3.0f64;
678 assert_eq!(
679 <f64 as TotalCmp>::total_cmp(&a, &b),
680 std::cmp::Ordering::Less
681 );
682 assert_eq!(
683 <f64 as TotalCmp>::total_cmp(&b, &a),
684 std::cmp::Ordering::Greater
685 );
686 assert_eq!(
687 <f64 as TotalCmp>::total_cmp(&a, &a),
688 std::cmp::Ordering::Equal
689 );
690 }
691
692 mod truncate_to_usize {
693 use crate::{kernels::RawRealTrait, validation::ErrorsRawRealToInteger};
694
695 #[test]
696 fn test_f64_truncate_to_usize_valid() {
697 assert_eq!(42.0_f64.truncate_to_usize().unwrap(), 42);
698 assert_eq!(42.9_f64.truncate_to_usize().unwrap(), 42);
699 assert_eq!(0.0_f64.truncate_to_usize().unwrap(), 0);
700 }
701
702 #[test]
703 fn test_f64_truncate_to_usize_not_finite() {
704 assert!(matches!(
705 f64::NAN.truncate_to_usize(),
706 Err(ErrorsRawRealToInteger::NotFinite { .. })
707 ));
708 assert!(matches!(
709 f64::INFINITY.truncate_to_usize(),
710 Err(ErrorsRawRealToInteger::NotFinite { .. })
711 ));
712 assert!(matches!(
713 f64::NEG_INFINITY.truncate_to_usize(),
714 Err(ErrorsRawRealToInteger::NotFinite { .. })
715 ));
716 }
717
718 #[test]
719 fn test_f64_truncate_to_usize_out_of_range() {
720 // Negative value
721 assert!(matches!(
722 (-1.0_f64).truncate_to_usize(),
723 Err(ErrorsRawRealToInteger::OutOfRange { .. })
724 ));
725 // Value too large
726 assert!(matches!(
727 ((usize::MAX as f64) + 1.0).truncate_to_usize(),
728 Err(ErrorsRawRealToInteger::OutOfRange { .. })
729 ));
730
731 // this is exactly usize::MAX, which is out of range because f64 cannot represent all integers above 2^53 exactly
732 assert!(matches!(
733 (usize::MAX as f64).truncate_to_usize(),
734 Err(ErrorsRawRealToInteger::OutOfRange { .. })
735 ));
736 }
737 }
738 }
739
740 mod complex {
741 use super::*;
742 use crate::{
743 ComplexScalarConstructors, ComplexScalarGetParts, ComplexScalarMutateParts,
744 ComplexScalarSetParts,
745 };
746 use num::{Complex, Zero};
747
748 #[test]
749 fn real_part() {
750 let c1 = Complex::new(1.23, 4.56);
751 assert_eq!(c1.real_part(), 1.23);
752
753 let c2 = Complex::new(-7.89, 0.12);
754 assert_eq!(c2.real_part(), -7.89);
755
756 let c3 = Complex::new(0.0, 10.0);
757 assert_eq!(c3.real_part(), 0.0);
758
759 let c_nan_re = Complex::new(f64::NAN, 5.0);
760 assert!(c_nan_re.real_part().is_nan());
761
762 let c_inf_re = Complex::new(f64::INFINITY, 5.0);
763 assert!(c_inf_re.real_part().is_infinite());
764 assert!(c_inf_re.real_part().is_sign_positive());
765
766 let c_neg_inf_re = Complex::new(f64::NEG_INFINITY, 5.0);
767 assert!(c_neg_inf_re.real_part().is_infinite());
768 assert!(c_neg_inf_re.real_part().is_sign_negative());
769 }
770
771 #[test]
772 fn imag_part() {
773 let c1 = Complex::new(1.23, 4.56);
774 assert_eq!(c1.imag_part(), 4.56);
775
776 let c2 = Complex::new(7.89, -0.12);
777 assert_eq!(c2.imag_part(), -0.12);
778
779 let c3 = Complex::new(10.0, 0.0);
780 assert_eq!(c3.imag_part(), 0.0);
781
782 let c_nan_im = Complex::new(5.0, f64::NAN);
783 assert!(c_nan_im.imag_part().is_nan());
784
785 let c_inf_im = Complex::new(5.0, f64::INFINITY);
786 assert!(c_inf_im.imag_part().is_infinite());
787 assert!(c_inf_im.imag_part().is_sign_positive());
788
789 let c_neg_inf_im = Complex::new(5.0, f64::NEG_INFINITY);
790 assert!(c_neg_inf_im.imag_part().is_infinite());
791 assert!(c_neg_inf_im.imag_part().is_sign_negative());
792 }
793
794 #[test]
795 fn try_new_complex() {
796 let r1 = 1.23;
797 let i1 = 4.56;
798 let c1 = Complex::<f64>::try_new_complex(r1, i1).unwrap();
799 assert_eq!(c1.re, r1);
800 assert_eq!(c1.im, i1);
801 assert_eq!(c1.real_part(), r1);
802 assert_eq!(c1.imag_part(), i1);
803
804 let r2 = -7.89;
805 let i2 = -0.12;
806 let c2 = Complex::<f64>::try_new_complex(r2, i2).unwrap();
807 assert_eq!(c2.re, r2);
808 assert_eq!(c2.im, i2);
809 assert_eq!(c2.real_part(), r2);
810 assert_eq!(c2.imag_part(), i2);
811
812 let r3 = 0.0;
813 let i3 = 0.0;
814 let c3 = Complex::<f64>::try_new_complex(r3, i3).unwrap();
815 assert_eq!(c3.re, r3);
816 assert_eq!(c3.im, i3);
817 assert!(c3.is_zero()); // Assuming Zero trait is available and implemented
818
819 let c_nan_re = Complex::<f64>::try_new_complex(f64::NAN, 5.0).unwrap_err();
820 assert!(matches!(
821 c_nan_re,
822 ErrorsValidationRawComplex::InvalidRealPart { .. }
823 ));
824
825 let c_inf_im = Complex::<f64>::try_new_complex(10.0, f64::INFINITY).unwrap_err();
826 assert!(matches!(
827 c_inf_im,
828 ErrorsValidationRawComplex::InvalidImaginaryPart { .. }
829 ));
830
831 let c_nan_re_inf_im =
832 Complex::<f64>::try_new_complex(f64::NAN, f64::INFINITY).unwrap_err();
833 assert!(matches!(
834 c_nan_re_inf_im,
835 ErrorsValidationRawComplex::InvalidBothParts { .. }
836 ));
837 }
838
839 #[test]
840 fn try_new_pure_real() {
841 let r1 = 1.23;
842 let c1 = Complex::<f64>::try_new_pure_real(r1).unwrap();
843 assert_eq!(c1.re, r1);
844 assert_eq!(c1.im, 0.0);
845
846 let c_nan = Complex::<f64>::try_new_pure_real(f64::NAN).unwrap_err();
847 assert!(matches!(
848 c_nan,
849 ErrorsValidationRawComplex::InvalidRealPart {
850 source: ErrorsValidationRawReal::IsNaN { .. }
851 }
852 ));
853 }
854
855 #[test]
856 fn try_new_pure_imaginary() {
857 let i1 = 1.23;
858 let c1 = Complex::<f64>::try_new_pure_imaginary(i1).unwrap();
859 assert_eq!(c1.re, 0.0);
860 assert_eq!(c1.im, i1);
861
862 let c_nan = Complex::<f64>::try_new_pure_imaginary(f64::NAN).unwrap_err();
863 assert!(matches!(
864 c_nan,
865 ErrorsValidationRawComplex::InvalidImaginaryPart {
866 source: ErrorsValidationRawReal::IsNaN { .. }
867 }
868 ));
869 }
870
871 #[test]
872 fn add_to_real_part() {
873 let mut c = Complex::new(1.0, 2.0);
874 c.add_to_real_part(&3.0);
875 assert_eq!(c.re, 4.0);
876 assert_eq!(c.im, 2.0);
877
878 c.add_to_real_part(&-5.0);
879 assert_eq!(c.re, -1.0);
880 assert_eq!(c.im, 2.0);
881 }
882
883 #[cfg(debug_assertions)]
884 #[test]
885 #[should_panic(expected = "The real part is not finite (i.e. is infinite or NaN).")]
886 fn add_to_real_part_nan() {
887 let mut c = Complex::new(1.0, 2.0);
888 c.add_to_real_part(&f64::NAN);
889 }
890
891 #[test]
892 fn add_to_imaginary_part() {
893 let mut c = Complex::new(1.0, 2.0);
894 c.add_to_imaginary_part(&3.0);
895 assert_eq!(c.re, 1.0);
896 assert_eq!(c.im, 5.0);
897
898 c.add_to_imaginary_part(&-4.0);
899 assert_eq!(c.re, 1.0);
900 assert_eq!(c.im, 1.0);
901 }
902
903 #[cfg(debug_assertions)]
904 #[test]
905 #[should_panic(expected = "The imaginary part is not finite (i.e. is infinite or NaN).")]
906 fn add_to_imaginary_part_nan() {
907 let mut c = Complex::new(1.0, 2.0);
908 c.add_to_imaginary_part(&f64::NAN);
909 }
910
911 #[test]
912 fn multiply_real_part() {
913 let mut c = Complex::new(1.0, 2.0);
914 c.multiply_real_part(&3.0);
915 assert_eq!(c.re, 3.0);
916 assert_eq!(c.im, 2.0);
917
918 c.multiply_real_part(&-2.0);
919 assert_eq!(c.re, -6.0);
920 assert_eq!(c.im, 2.0);
921 }
922
923 #[cfg(debug_assertions)]
924 #[test]
925 #[should_panic(expected = "The real part is not finite (i.e. is infinite or NaN).")]
926 fn multiply_real_part_nan() {
927 let mut c = Complex::new(1.0, 2.0);
928 c.multiply_real_part(&f64::NAN);
929 }
930
931 #[test]
932 fn multiply_imaginary_part() {
933 let mut c = Complex::new(1.0, 2.0);
934 c.multiply_imaginary_part(&3.0);
935 assert_eq!(c.re, 1.0);
936 assert_eq!(c.im, 6.0);
937
938 c.multiply_imaginary_part(&-0.5);
939 assert_eq!(c.re, 1.0);
940 assert_eq!(c.im, -3.0);
941 }
942
943 #[cfg(debug_assertions)]
944 #[test]
945 #[should_panic(expected = "The imaginary part is not finite (i.e. is infinite or NaN).")]
946 fn multiply_imaginary_part_nan() {
947 let mut c = Complex::new(1.0, 2.0);
948 c.multiply_imaginary_part(&f64::NAN);
949 }
950
951 #[test]
952 fn set_real_part() {
953 let mut c = Complex::new(1.0, 2.0);
954 c.set_real_part(3.0);
955 assert_eq!(c.re, 3.0);
956 assert_eq!(c.im, 2.0);
957
958 c.set_real_part(-4.0);
959 assert_eq!(c.re, -4.0);
960 assert_eq!(c.im, 2.0);
961 }
962
963 #[cfg(debug_assertions)]
964 #[test]
965 #[should_panic(expected = "The real part is not finite (i.e. is infinite or NaN).")]
966 fn set_real_part_nan() {
967 let mut c = Complex::new(1.0, 2.0);
968 c.set_real_part(f64::NAN);
969 }
970
971 #[test]
972 fn set_imaginary_part() {
973 let mut c = Complex::new(1.0, 2.0);
974 c.set_imaginary_part(3.0);
975 assert_eq!(c.re, 1.0);
976 assert_eq!(c.im, 3.0);
977
978 c.set_imaginary_part(-4.0);
979 assert_eq!(c.re, 1.0);
980 assert_eq!(c.im, -4.0);
981 }
982
983 #[cfg(debug_assertions)]
984 #[test]
985 #[should_panic(expected = "The imaginary part is not finite (i.e. is infinite or NaN).")]
986 fn set_imaginary_part_nan() {
987 let mut c = Complex::new(1.0, 2.0);
988 c.set_imaginary_part(f64::NAN);
989 }
990
991 #[test]
992 #[allow(clippy::op_ref)]
993 fn multiply_ref() {
994 let c1 = Complex::new(1.0, 2.0);
995 let c2 = Complex::new(3.0, 4.0);
996 let result = c1 * &c2;
997 assert_eq!(result, Complex::new(-5.0, 10.0)); // (1*3 - 2*4) + (1*4 + 2*3)i
998 }
999 }
1000}