Skip to main content

num_valid/backends/rug/
validated.rs

1#![deny(rustdoc::broken_intra_doc_links)]
2
3//! # Validated Arbitrary-Precision Types
4//!
5//! This module provides validated wrapper types for arbitrary-precision arithmetic using
6//! the [`rug`](https://docs.rs/rug/) library, which wraps GNU MPFR.
7//!
8//! ## Overview
9//!
10//! The types in this module combine the power of arbitrary-precision arithmetic with
11//! the safety guarantees of [`num-valid`](crate)'s validation system. All values are
12//! guaranteed to be finite and have the correct precision.
13//!
14//! ## Core Types
15//!
16//! - `RugStrictFinite<PRECISION>`: Kernel configuration for arbitrary-precision with strict validation
17//! - `RealRugStrictFinite<PRECISION>`: Validated arbitrary-precision real numbers
18//! - `ComplexRugStrictFinite<PRECISION>`: Validated arbitrary-precision complex numbers
19//!
20//! ## Precision
21//!
22//! Precision is specified as a const generic parameter representing the number of bits
23//! in the significand:
24//!
25//! - `53` bits: Equivalent to `f64` precision
26//! - `100` bits: ~30 decimal digits
27//! - `200` bits: ~60 decimal digits
28//!
29//! ## Example
30//!
31//! ```rust
32//! use num_valid::{RealRugStrictFinite, functions::Sqrt};
33//! use rug::Float;
34//! use try_create::TryNew;
35//!
36//! const PRECISION: u32 = 100;
37//!
38//! // Create a validated value from a rug::Float
39//! let x = RealRugStrictFinite::<PRECISION>::try_new(
40//!     Float::with_val(PRECISION, 2.0)
41//! ).unwrap();
42//!
43//! // Mathematical operations are available
44//! let sqrt_x = x.sqrt();
45//! ```
46//!
47//! ## Memory Considerations
48//!
49//! Unlike the native `f64` backend, `rug` types are heap-allocated and do **not** implement
50//! `Copy`. Use references where possible to avoid unnecessary cloning.
51
52use crate::{
53    ComplexScalarGetParts, Constants, RealScalar,
54    core::errors::{ErrorsValidationRawComplex, RawRealConversionError},
55    kernels::{ComplexValidated, NumKernelStrictFinite, RealValidated},
56    scalars::{AbsoluteTolerance, NonNegativeRealScalar},
57};
58use num::Complex;
59use rug::ops::CompleteRound;
60use std::marker::PhantomData;
61use try_create::{IntoInner, TryNew};
62
63//------------------------------------------------------------------------------------------------------------
64/// A type alias for a numerical kernel that uses `rug` for arbitrary-precision arithmetic with strict finiteness checks.
65///
66/// This policy is a specialization of [`NumKernelStrictFinite`] for the `rug` backend.
67/// It bundles the raw `rug` types ([`rug::Float`] and [`rug::Complex`]) with their corresponding
68/// validation logic, [`StrictFinitePolicy`](crate::core::policies::StrictFinitePolicy).
69///
70/// The primary role of this policy is to serve as the generic parameter for the main validated
71/// types, [`RealRugStrictFinite<PRECISION>`] and [`ComplexRugStrictFinite<PRECISION>`], thereby
72/// defining their behavior and ensuring their correctness.
73///
74/// # Validation
75///
76/// This policy enforces the following rules on the underlying [`rug::Float`] values, for both
77/// real numbers and the components of complex numbers:
78///
79/// 1.  **Finiteness**: The value must not be `NaN` or `Infinity`.
80/// 2.  **Precision**: The precision of the [`rug::Float`] must exactly match the `PRECISION`
81///     constant specified in the type. This is a critical check for `rug` types.
82///
83/// # Example
84///
85/// While you typically won't use `RugStrictFinite` directly, it's the engine that
86/// powers the validated `rug` types.
87///
88/// ```rust
89/// use num_valid::{RealRugStrictFinite, ComplexRugStrictFinite};
90/// use rug::{Float, Complex};
91/// use try_create::TryNew;
92///
93/// const PRECISION: u32 = 100;
94///
95/// // This works because the value is finite and has the correct precision.
96/// let valid_real = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.14)).unwrap();
97///
98/// // This fails because the precision is wrong.
99/// let wrong_precision_real = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION + 1, 3.14));
100/// assert!(wrong_precision_real.is_err());
101///
102/// // This fails because the value is NaN.
103/// let nan_real = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, f64::NAN));
104/// assert!(nan_real.is_err());
105///
106/// // The same rules apply to complex numbers.
107/// let valid_complex = ComplexRugStrictFinite::<PRECISION>::try_new(
108///     Complex::with_val(PRECISION, (1.0, 2.0))
109/// ).unwrap();
110///
111/// let invalid_complex = ComplexRugStrictFinite::<PRECISION>::try_new(
112///     Complex::with_val(PRECISION, (1.0, f64::INFINITY))
113/// );
114/// assert!(invalid_complex.is_err());
115/// ```
116pub type RugStrictFinite<const PRECISION: u32> = NumKernelStrictFinite<rug::Float, PRECISION>;
117//------------------------------------------------------------------------------------------------------------
118
119//------------------------------------------------------------------------------------------------------------
120/// A type alias for a validated real scalar using the `Rug` kernel with a specified precision and the `NumKernelStrictFinite` validation policy.
121pub type RealRugStrictFinite<const PRECISION: u32> = RealValidated<RugStrictFinite<PRECISION>>;
122
123/// A type alias for a validated complex scalar using the `Rug` kernel with a specified precision and the `NumKernelStrictFinite` validation policy.
124pub type ComplexRugStrictFinite<const PRECISION: u32> =
125    ComplexValidated<RugStrictFinite<PRECISION>>;
126
127//------------------------------------------------------------------------------------------------------------
128
129//-------------------------------------------------------------
130impl<const PRECISION: u32> TryFrom<Complex<f64>> for ComplexRugStrictFinite<PRECISION> {
131    type Error = ErrorsValidationRawComplex<RawRealConversionError<f64, rug::Float>>;
132
133    fn try_from(value: Complex<f64>) -> Result<Self, Self::Error> {
134        let real_part = RealRugStrictFinite::<PRECISION>::try_from_f64(value.re);
135        let imag_part = RealRugStrictFinite::<PRECISION>::try_from_f64(value.im);
136
137        match (real_part, imag_part) {
138            (Ok(real_part), Ok(imag_part)) => Ok(Self {
139                value: rug::Complex::with_val(
140                    PRECISION,
141                    (real_part.into_inner(), imag_part.into_inner()),
142                ),
143                _phantom: PhantomData,
144            }),
145            (Err(error_real_part), Ok(_)) => Err(ErrorsValidationRawComplex::InvalidRealPart {
146                source: Box::new(error_real_part),
147            }),
148            (Ok(_), Err(error_imaginary_part)) => {
149                Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
150                    source: Box::new(error_imaginary_part),
151                })
152            }
153            (Err(error_real_part), Err(error_imaginary_part)) => {
154                Err(ErrorsValidationRawComplex::InvalidBothParts {
155                    real_error: Box::new(error_real_part),
156                    imag_error: Box::new(error_imaginary_part),
157                })
158            }
159        }
160    }
161}
162//------------------------------------------------------------------------------------------------
163
164//------------------------------------------------------------------------------------------------
165// Approx trait implementations for rug-backed validated types
166//
167// Since `rug::Float` and `rug::Complex` do not implement the `approx` crate traits,
168// we provide manual implementations for `RealRugStrictFinite<P>` and `ComplexRugStrictFinite<P>`.
169// These implementations use the kernel's `epsilon()` as the default epsilon value.
170//
171// # Note on UlpsEq
172//
173// `UlpsEq` (Units in Last Place) is not implemented for rug types because:
174// 1. Arbitrary-precision floats don't have a fixed-size mantissa
175// 2. The concept of ULP is tied to IEEE 754 binary representation
176// 3. For arbitrary precision, `AbsDiffEq` and `RelativeEq` are more appropriate
177//------------------------------------------------------------------------------------------------
178
179impl<const PRECISION: u32> approx::AbsDiffEq for RealRugStrictFinite<PRECISION> {
180    type Epsilon = AbsoluteTolerance<Self>;
181
182    /// Returns the machine epsilon for the given precision.
183    ///
184    /// For `rug::Float`, this is 2^(1-PRECISION), which represents the
185    /// smallest difference between 1.0 and the next representable value.
186    #[inline]
187    fn default_epsilon() -> Self::Epsilon {
188        AbsoluteTolerance::epsilon()
189    }
190
191    /// Compares two values for approximate equality using absolute difference.
192    ///
193    /// Returns `true` if `|self - other| <= epsilon`.
194    #[inline]
195    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
196        let diff = (self.as_ref() - other.as_ref()).complete(PRECISION);
197        diff.abs() <= *epsilon.as_ref().as_ref().as_ref()
198    }
199}
200
201impl<const PRECISION: u32> approx::RelativeEq for RealRugStrictFinite<PRECISION> {
202    /// Returns a reasonable default for relative comparisons.
203    ///
204    /// Uses `epsilon() * 8` as a default, allowing for a few ULPs of error.
205    #[inline]
206    fn default_max_relative() -> Self::Epsilon {
207        let eps = Self::epsilon();
208        // Allow for approximately 3 bits of error (8 = 2^3)
209        let eight = rug::Float::with_val(PRECISION, 8);
210        let scaled = NonNegativeRealScalar::new(
211            Self::try_new(eps.into_inner() * eight)
212                .expect("default_max_relative: validation failed unexpectedly"),
213        );
214        AbsoluteTolerance::new(scaled)
215    }
216
217    /// Compares two values for approximate equality using relative difference.
218    ///
219    /// Returns `true` if `|self - other| <= max(epsilon, max_relative * max(|self|, |other|))`.
220    #[inline]
221    fn relative_eq(
222        &self,
223        other: &Self,
224        epsilon: Self::Epsilon,
225        max_relative: Self::Epsilon,
226    ) -> bool {
227        let diff = (self.as_ref() - other.as_ref()).complete(PRECISION).abs();
228
229        if diff <= *epsilon.as_ref().as_ref().as_ref() {
230            return true;
231        }
232
233        let abs_self = self.as_ref().clone().abs();
234        let abs_other = other.as_ref().clone().abs();
235        let largest = if abs_self > abs_other {
236            abs_self
237        } else {
238            abs_other
239        };
240
241        diff <= (max_relative.as_ref().as_ref().as_ref() * &largest).complete(PRECISION)
242    }
243}
244
245impl<const PRECISION: u32> approx::AbsDiffEq for ComplexRugStrictFinite<PRECISION> {
246    type Epsilon = AbsoluteTolerance<RealRugStrictFinite<PRECISION>>;
247
248    /// Returns the machine epsilon for the given precision.
249    #[inline]
250    fn default_epsilon() -> Self::Epsilon {
251        AbsoluteTolerance::epsilon()
252    }
253
254    /// Compares two complex values for approximate equality using absolute difference.
255    ///
256    /// Returns `true` if both real and imaginary parts satisfy `|a - b| <= epsilon`.
257    #[inline]
258    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
259        let real_diff = (self.raw_real_part() - other.raw_real_part()).complete(PRECISION);
260        let imag_diff = (self.raw_imag_part() - other.raw_imag_part()).complete(PRECISION);
261        let eps = epsilon.as_ref().as_ref().as_ref();
262
263        real_diff.abs() <= *eps && imag_diff.abs() <= *eps
264    }
265}
266
267impl<const PRECISION: u32> approx::RelativeEq for ComplexRugStrictFinite<PRECISION> {
268    /// Returns a reasonable default for relative comparisons.
269    #[inline]
270    fn default_max_relative() -> Self::Epsilon {
271        RealRugStrictFinite::<PRECISION>::default_max_relative()
272    }
273
274    /// Compares two complex values for approximate equality using relative difference.
275    ///
276    /// Checks that both real and imaginary parts are relatively equal.
277    #[inline]
278    fn relative_eq(
279        &self,
280        other: &Self,
281        epsilon: Self::Epsilon,
282        max_relative: Self::Epsilon,
283    ) -> bool {
284        // Compare real parts
285        let real_self = self.real_part();
286        let real_other = other.real_part();
287        if !real_self.relative_eq(&real_other, epsilon.clone(), max_relative.clone()) {
288            return false;
289        }
290
291        // Compare imaginary parts
292        let imag_self = self.imag_part();
293        let imag_other = other.imag_part();
294        imag_self.relative_eq(&imag_other, epsilon, max_relative)
295    }
296}
297//------------------------------------------------------------------------------------------------
298
299#[cfg(test)]
300mod approx_tests {
301    use super::*;
302    use crate::scalars::AbsoluteTolerance;
303    use approx::{AbsDiffEq, assert_abs_diff_eq, assert_abs_diff_ne, assert_relative_eq};
304    use rug::Float;
305    use try_create::TryNew;
306
307    const PRECISION: u32 = 100;
308
309    mod real_rug {
310        use super::*;
311
312        #[test]
313        fn abs_diff_eq_equal_values() {
314            let a =
315                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1.0)).unwrap();
316            let b =
317                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1.0)).unwrap();
318            assert_abs_diff_eq!(a, b);
319        }
320
321        #[test]
322        fn abs_diff_eq_close_values() {
323            let a =
324                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1.0)).unwrap();
325            // Add a very small difference
326            let mut b_val = Float::with_val(PRECISION, 1.0);
327            b_val += Float::with_val(PRECISION, 1e-25);
328            let b = RealRugStrictFinite::<PRECISION>::try_new(b_val).unwrap();
329
330            // Should be equal with a tolerance larger than 1e-25
331            let eps_val = NonNegativeRealScalar::new(
332                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1e-20))
333                    .unwrap(),
334            );
335            let eps = AbsoluteTolerance::new(eps_val);
336            assert_abs_diff_eq!(a, b, epsilon = eps);
337        }
338
339        #[test]
340        fn abs_diff_ne_different_values() {
341            let a =
342                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1.0)).unwrap();
343            let b =
344                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 2.0)).unwrap();
345            assert_abs_diff_ne!(a, b);
346        }
347
348        #[test]
349        fn default_epsilon_is_machine_epsilon() {
350            let eps = RealRugStrictFinite::<PRECISION>::default_epsilon();
351            // Machine epsilon for 100-bit precision is 2^(1-100) = 2^(-99)
352            let expected = AbsoluteTolerance::new(NonNegativeRealScalar::new(
353                RealRugStrictFinite::<PRECISION>::epsilon(),
354            ));
355            assert_eq!(*eps.as_ref(), *expected.as_ref());
356        }
357
358        #[test]
359        fn relative_eq_close_values() {
360            let a = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 100.0))
361                .unwrap();
362            // Use a value within the default relative tolerance (epsilon * 8)
363            let eps = RealRugStrictFinite::<PRECISION>::epsilon();
364            let mut b_val = Float::with_val(PRECISION, 100.0);
365            // Add a tiny relative difference (much smaller than eps * 8)
366            b_val += eps.as_ref();
367            let b = RealRugStrictFinite::<PRECISION>::try_new(b_val).unwrap();
368            assert_relative_eq!(a, b);
369        }
370
371        #[test]
372        fn relative_eq_with_custom_tolerance() {
373            let a =
374                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1.0)).unwrap();
375            let b = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1.001))
376                .unwrap();
377            let max_rel_val = NonNegativeRealScalar::new(
378                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.01))
379                    .unwrap(),
380            );
381            let max_rel = AbsoluteTolerance::new(max_rel_val);
382            let eps = RealRugStrictFinite::<PRECISION>::default_epsilon();
383            assert_relative_eq!(a, b, epsilon = eps, max_relative = max_rel);
384        }
385    }
386
387    mod complex_rug {
388        use super::*;
389
390        #[test]
391        fn abs_diff_eq_equal_values() {
392            let a = ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
393                PRECISION,
394                (
395                    Float::with_val(PRECISION, 1.0),
396                    Float::with_val(PRECISION, 2.0),
397                ),
398            ))
399            .unwrap();
400            let b = ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
401                PRECISION,
402                (
403                    Float::with_val(PRECISION, 1.0),
404                    Float::with_val(PRECISION, 2.0),
405                ),
406            ))
407            .unwrap();
408            assert_abs_diff_eq!(a, b);
409        }
410
411        #[test]
412        fn abs_diff_eq_close_values() {
413            let a = ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
414                PRECISION,
415                (
416                    Float::with_val(PRECISION, 1.0),
417                    Float::with_val(PRECISION, 2.0),
418                ),
419            ))
420            .unwrap();
421            let b = ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
422                PRECISION,
423                (
424                    Float::with_val(PRECISION, 1.0) + Float::with_val(PRECISION, 1e-25),
425                    Float::with_val(PRECISION, 2.0) + Float::with_val(PRECISION, 1e-25),
426                ),
427            ))
428            .unwrap();
429            let eps_val = NonNegativeRealScalar::new(
430                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1e-20))
431                    .unwrap(),
432            );
433            let eps = AbsoluteTolerance::new(eps_val);
434            assert_abs_diff_eq!(a, b, epsilon = eps);
435        }
436
437        #[test]
438        fn abs_diff_ne_different_values() {
439            let a = ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
440                PRECISION,
441                (
442                    Float::with_val(PRECISION, 1.0),
443                    Float::with_val(PRECISION, 2.0),
444                ),
445            ))
446            .unwrap();
447            let b = ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
448                PRECISION,
449                (
450                    Float::with_val(PRECISION, 3.0),
451                    Float::with_val(PRECISION, 4.0),
452                ),
453            ))
454            .unwrap();
455            assert_abs_diff_ne!(a, b);
456        }
457
458        #[test]
459        fn relative_eq_close_values() {
460            let a = ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
461                PRECISION,
462                (
463                    Float::with_val(PRECISION, 100.0),
464                    Float::with_val(PRECISION, 200.0),
465                ),
466            ))
467            .unwrap();
468            // Use values within the default relative tolerance
469            let eps = RealRugStrictFinite::<PRECISION>::epsilon();
470            let b = ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
471                PRECISION,
472                (
473                    Float::with_val(PRECISION, 100.0) + eps.as_ref(),
474                    Float::with_val(PRECISION, 200.0) + eps.as_ref(),
475                ),
476            ))
477            .unwrap();
478            assert_relative_eq!(a, b);
479        }
480    }
481}
482
483#[cfg(test)]
484mod tests {
485    use super::*;
486    use crate::{
487        ComplexRugStrictFinite, ComplexScalarConstructors, ComplexScalarGetParts,
488        ComplexScalarMutateParts, ComplexScalarSetParts, Constants, Max, Min, RawRealTrait,
489        core::{
490            errors::{ErrorsRawRealToInteger, ErrorsValidationRawReal, RawRealConversionError},
491            traits::validation::FpChecks,
492        },
493        functions::{
494            ACos, ACosH, ACosHErrors, ACosHInputErrors, ACosRealErrors, ACosRealInputErrors, ASin,
495            ASinRealErrors, ASinRealInputErrors, ATan, ATan2, ATan2Errors, ATanComplexErrors,
496            ATanComplexInputErrors, ATanH, ATanHErrors, ATanHInputErrors, Abs, Clamp, Exp,
497            ExpErrors, Hypot, Ln, Log2, LogarithmComplexErrors, LogarithmComplexInputErrors,
498            NegAssign, Pow, PowComplexBaseRealExponentErrors, PowIntExponentErrors,
499            PowIntExponentInputErrors, PowRealBaseRealExponentErrors, Reciprocal, ReciprocalErrors,
500            Sign, Sqrt, SqrtRealErrors, TotalCmp,
501        },
502    };
503    use num::{One, Zero};
504    use rug::{Float, float::Constant as MpfrConstant, ops::Pow as RugPow};
505    use std::{assert_matches, cmp::Ordering, ops::Neg};
506    use try_create::{TryNew, TryNewValidated};
507
508    const PRECISION: u32 = 53;
509
510    mod fp_checks {
511        use super::*;
512        use rug::Float;
513
514        #[test]
515        fn is_finite() {
516            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
517            assert!(real.is_finite());
518
519            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, f64::INFINITY));
520            assert!(real.is_err());
521
522            let complex = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
523                53,
524                (Float::with_val(53, 1.0), Float::with_val(53, 1.0)),
525            ))
526            .unwrap();
527            assert!(complex.is_finite());
528
529            let complex = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
530                53,
531                (Float::with_val(53, f64::INFINITY), Float::with_val(53, 1.0)),
532            ));
533            assert!(complex.is_err());
534        }
535
536        #[test]
537        fn is_infinite() {
538            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
539            assert!(!real.is_infinite());
540
541            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, f64::INFINITY));
542            assert!(real.is_err());
543
544            let complex = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
545                53,
546                (Float::with_val(53, 1.0), Float::with_val(53, 1.0)),
547            ))
548            .unwrap();
549            assert!(!complex.is_infinite());
550
551            let complex = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
552                53,
553                (Float::with_val(53, f64::INFINITY), Float::with_val(53, 1.0)),
554            ));
555            assert!(complex.is_err());
556        }
557
558        #[test]
559        fn is_nan() {
560            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
561            assert!(!real.is_nan());
562
563            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, f64::NAN));
564            assert!(matches!(real, Err(ErrorsValidationRawReal::IsNaN { .. })));
565
566            let complex = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
567                53,
568                (Float::with_val(53, 1.0), Float::with_val(53, 1.0)),
569            ))
570            .unwrap();
571            assert!(!complex.is_nan());
572
573            let err = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
574                53,
575                (Float::with_val(53, f64::NAN), Float::with_val(53, 1.0)),
576            ))
577            .unwrap_err();
578            assert_matches!(
579                err,
580                ErrorsValidationRawComplex::InvalidRealPart {
581                    source
582                } if matches!(*source, ErrorsValidationRawReal::IsNaN { .. })
583            );
584        }
585
586        #[test]
587        fn is_normal() {
588            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, 1.0)).unwrap();
589            assert!(real.is_normal());
590
591            let real = RealRugStrictFinite::<53>::try_new(Float::with_val(53, 0.0)).unwrap();
592            assert!(!real.is_normal());
593
594            let complex = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
595                53,
596                (Float::with_val(53, 1.0), Float::with_val(53, 1.0)),
597            ))
598            .unwrap();
599            assert!(complex.is_normal());
600
601            let complex = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
602                53,
603                (Float::with_val(53, 0.0), Float::with_val(53, 0.0)),
604            ))
605            .unwrap();
606            assert!(!complex.is_normal());
607        }
608    }
609
610    mod abs {
611        use super::*;
612
613        mod real {
614            use super::*;
615
616            #[test]
617            fn abs_valid() {
618                let value =
619                    RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, -4.0)).unwrap();
620
621                let expected_result =
622                    RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 4.0)).unwrap();
623                assert_eq!(
624                    value.clone().try_abs().unwrap().into_inner(),
625                    expected_result
626                );
627                assert_eq!(value.abs().into_inner(), expected_result);
628            }
629
630            #[test]
631            fn abs_zero() {
632                let value =
633                    RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 0.0)).unwrap();
634
635                let expected_result =
636                    RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 0.0)).unwrap();
637                assert_eq!(
638                    value.clone().try_abs().unwrap().into_inner(),
639                    expected_result
640                );
641                assert_eq!(value.abs().into_inner(), expected_result);
642            }
643
644            /*
645            #[cfg(feature = "rug")]
646            #[test]
647            fn abs_nan() {
648                let value =
649                    RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, rug::float::Special::Nan))
650                        .unwrap();
651                let result = value.try_abs();
652                assert!(matches!(result, Err(AbsRealErrors::Input(..))));
653            }
654
655            #[cfg(feature = "rug")]
656            #[test]
657            fn abs_infinite() {
658                let value = RealRugStrictFinite::<53>::try_new(rug::Float::with_val(
659                    53,
660                    rug::float::Special::Infinity,
661                ))
662                .unwrap();
663                let result = value.try_abs();
664                assert!(matches!(result, Err(AbsRealErrors::Input(..))));
665            }
666            */
667        }
668
669        mod complex {
670            use super::*;
671
672            #[test]
673            fn abs_valid() {
674                let value = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
675                    53,
676                    (rug::Float::with_val(53, 3.0), rug::Float::with_val(53, 4.0)),
677                ))
678                .unwrap();
679
680                let expected_result =
681                    RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 5.0)).unwrap();
682                assert_eq!(
683                    value.clone().try_abs().unwrap().into_inner(),
684                    expected_result
685                );
686                assert_eq!(value.abs().into_inner(), expected_result);
687            }
688
689            #[test]
690            fn abs_zero() {
691                let value = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
692                    53,
693                    (rug::Float::with_val(53, 0.0), rug::Float::with_val(53, 0.0)),
694                ))
695                .unwrap();
696
697                let expected_result =
698                    RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 0.0)).unwrap();
699                assert_eq!(
700                    value.clone().try_abs().unwrap().into_inner(),
701                    expected_result
702                );
703                assert_eq!(value.abs().into_inner(), expected_result);
704            }
705            /*
706            #[test]
707            fn abs_nan() {
708                let value = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
709                    53,
710                    (
711                        rug::Float::with_val(53, rug::float::Special::Nan),
712                        rug::Float::with_val(53, 0.0),
713                    ),
714                ))
715                .unwrap();
716                assert!(matches!(
717                    value.try_abs(),
718                    Err(AbsComplexErrors::Input(..))
719                ));
720            }
721
722            #[test]
723            fn abs_infinite() {
724                let value = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
725                    53,
726                    (
727                        rug::Float::with_val(53, rug::float::Special::Infinity),
728                        rug::Float::with_val(53, 0.0),
729                    ),
730                ))
731                .unwrap();
732                assert!(matches!(
733                    value.try_abs(),
734                    Err(AbsComplexErrors::Input(..))
735                ));
736            }
737            */
738        }
739    }
740
741    mod max_min {
742        use super::*;
743
744        #[test]
745        fn test_realrug_max() {
746            let value =
747                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.0)).unwrap();
748            let other =
749                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 5.0)).unwrap();
750            let expected =
751                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 5.0)).unwrap();
752            assert_eq!(Max::max_by_ref(&value, &other), &expected);
753
754            let neg_value =
755                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -3.0))
756                    .unwrap();
757            let neg_other =
758                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -5.0))
759                    .unwrap();
760            let neg_expected =
761                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -3.0))
762                    .unwrap();
763            assert_eq!(Max::max_by_ref(&neg_value, &neg_other), &neg_expected);
764        }
765
766        #[test]
767        fn test_realrug_min() {
768            let value =
769                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.0)).unwrap();
770            let other =
771                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 5.0)).unwrap();
772            let expected =
773                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.0)).unwrap();
774            assert_eq!(Min::min_by_ref(&value, &other), &expected);
775
776            let neg_value =
777                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -3.0))
778                    .unwrap();
779            let neg_other =
780                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -5.0))
781                    .unwrap();
782            let neg_expected =
783                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -5.0))
784                    .unwrap();
785            assert_eq!(Min::min_by_ref(&neg_value, &neg_other), &neg_expected);
786        }
787    }
788
789    mod builders {
790        use super::*;
791
792        mod real {
793            use super::*;
794
795            #[test]
796            fn try_new_nan() {
797                let nan = rug::Float::with_val(PRECISION, f64::NAN);
798                let err = RealRugStrictFinite::<PRECISION>::try_new(nan).unwrap_err();
799                assert!(matches!(err, ErrorsValidationRawReal::IsNaN { .. }));
800            }
801
802            #[test]
803            fn try_new_pos_infinity() {
804                let pos_infinity = rug::Float::with_val(PRECISION, f64::INFINITY);
805                let err = RealRugStrictFinite::<PRECISION>::try_new(pos_infinity).unwrap_err();
806                assert!(matches!(err, ErrorsValidationRawReal::IsPosInfinity { .. }));
807            }
808
809            #[test]
810            fn try_new_neg_infinity() {
811                let neg_infinity = rug::Float::with_val(PRECISION, f64::NEG_INFINITY);
812                let err = RealRugStrictFinite::<PRECISION>::try_new(neg_infinity).unwrap_err();
813                assert!(matches!(err, ErrorsValidationRawReal::IsNegInfinity { .. }));
814            }
815        }
816
817        mod complex {
818            use super::*;
819
820            #[test]
821            fn real_part() {
822                let c1 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
823                    rug::Complex::with_val(PRECISION, (1.23, 4.56)),
824                )
825                .unwrap();
826                assert_eq!(c1.real_part().as_ref(), &1.23);
827
828                let c2 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
829                    rug::Complex::with_val(PRECISION, (-7.89, 0.12)),
830                )
831                .unwrap();
832                assert_eq!(c2.real_part().as_ref(), &-7.89);
833
834                let c3 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
835                    rug::Complex::with_val(PRECISION, (0.0, 10.0)),
836                )
837                .unwrap();
838                assert_eq!(c3.real_part().as_ref(), &0.0);
839
840                let c_nan_re = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
841                    rug::Complex::with_val(PRECISION, (f64::NAN, 5.0)),
842                )
843                .unwrap_err();
844                assert_matches!(
845                    c_nan_re,
846                    ErrorsValidationRawComplex::InvalidRealPart {
847                        source
848                    } if matches!(*source, ErrorsValidationRawReal::IsNaN { .. })
849                );
850
851                let c_inf_re = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
852                    rug::Complex::with_val(PRECISION, (f64::INFINITY, 5.0)),
853                )
854                .unwrap_err();
855                assert_matches!(
856                    c_inf_re,
857                    ErrorsValidationRawComplex::InvalidRealPart {
858                        source
859                    } if matches!(*source, ErrorsValidationRawReal::IsPosInfinity { .. })
860                );
861
862                let c_neg_inf_re = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
863                    rug::Complex::with_val(PRECISION, (f64::NEG_INFINITY, 5.0)),
864                )
865                .unwrap_err();
866                assert_matches!(
867                    c_neg_inf_re,
868                    ErrorsValidationRawComplex::InvalidRealPart {
869                        source
870                    } if matches!(*source, ErrorsValidationRawReal::IsNegInfinity { .. })
871                );
872            }
873
874            #[test]
875            fn imag_part() {
876                let c1 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
877                    rug::Complex::with_val(PRECISION, (1.23, 4.56)),
878                )
879                .unwrap();
880                assert_eq!(c1.imag_part().as_ref(), &4.56);
881
882                let c2 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
883                    rug::Complex::with_val(PRECISION, (-7.89, 0.12)),
884                )
885                .unwrap();
886                assert_eq!(c2.imag_part().as_ref(), &0.12);
887
888                let c3 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
889                    rug::Complex::with_val(PRECISION, (10.0, 0.0)),
890                )
891                .unwrap();
892                assert_eq!(c3.imag_part().as_ref(), &0.0);
893
894                let c_nan_im = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
895                    rug::Complex::with_val(PRECISION, (5.0, f64::NAN)),
896                )
897                .unwrap_err();
898                assert_matches!(
899                    c_nan_im,
900                    ErrorsValidationRawComplex::InvalidImaginaryPart {
901                        source
902                    } if matches!(*source, ErrorsValidationRawReal::IsNaN { .. })
903                );
904
905                let c_inf_im = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
906                    rug::Complex::with_val(PRECISION, (5.0, f64::INFINITY)),
907                )
908                .unwrap_err();
909                assert_matches!(
910                    c_inf_im,
911                    ErrorsValidationRawComplex::InvalidImaginaryPart {
912                        source
913                    } if matches!(*source, ErrorsValidationRawReal::IsPosInfinity { .. })
914                );
915
916                let c_neg_inf_im = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
917                    rug::Complex::with_val(PRECISION, (5.0, f64::NEG_INFINITY)),
918                )
919                .unwrap_err();
920                assert_matches!(
921                    c_neg_inf_im,
922                    ErrorsValidationRawComplex::InvalidImaginaryPart {
923                        source
924                    } if matches!(*source, ErrorsValidationRawReal::IsNegInfinity { .. })
925                );
926            }
927
928            #[test]
929            fn try_new_complex() {
930                let r1 = RealRugStrictFinite::<PRECISION>::try_from_f64(1.23).unwrap();
931                let i1 = RealRugStrictFinite::<PRECISION>::try_from_f64(4.56).unwrap();
932                let c1 = ComplexRugStrictFinite::<PRECISION>::try_new_complex(
933                    r1.as_ref().clone(),
934                    i1.as_ref().clone(),
935                )
936                .unwrap();
937                assert_eq!(c1.real_part(), r1);
938                assert_eq!(c1.imag_part(), i1);
939
940                let r2 = RealRugStrictFinite::<PRECISION>::try_from_f64(-7.89).unwrap();
941                let i2 = RealRugStrictFinite::<PRECISION>::try_from_f64(-0.12).unwrap();
942                let c2 = ComplexRugStrictFinite::<PRECISION>::try_new_complex(
943                    r2.as_ref().clone(),
944                    i2.as_ref().clone(),
945                )
946                .unwrap();
947                assert_eq!(c2.real_part(), r2);
948                assert_eq!(c2.imag_part(), i2);
949
950                let r3 = RealRugStrictFinite::<PRECISION>::try_from_f64(0.0).unwrap();
951                let i3 = RealRugStrictFinite::<PRECISION>::try_from_f64(0.0).unwrap();
952                let c3 = ComplexRugStrictFinite::<PRECISION>::try_new_complex(
953                    r3.as_ref().clone(),
954                    i3.as_ref().clone(),
955                )
956                .unwrap();
957                assert_eq!(c3.real_part(), r3);
958                assert_eq!(c3.real_part(), i3);
959                assert!(c3.is_zero());
960
961                let c_nan_re = ComplexRugStrictFinite::<PRECISION>::try_new_complex(
962                    Float::with_val(PRECISION, f64::NAN),
963                    Float::with_val(PRECISION, 5.0),
964                )
965                .unwrap_err();
966                assert!(matches!(
967                    c_nan_re,
968                    ErrorsValidationRawComplex::InvalidRealPart { .. }
969                ));
970
971                let c_inf_im = ComplexRugStrictFinite::<PRECISION>::try_new_complex(
972                    Float::with_val(PRECISION, 10.0),
973                    Float::with_val(PRECISION, f64::INFINITY),
974                )
975                .unwrap_err();
976                assert!(matches!(
977                    c_inf_im,
978                    ErrorsValidationRawComplex::InvalidImaginaryPart { .. }
979                ));
980
981                let c_nan_re_inf_im = ComplexRugStrictFinite::<PRECISION>::try_new_complex(
982                    Float::with_val(PRECISION, f64::NAN),
983                    Float::with_val(PRECISION, f64::INFINITY),
984                )
985                .unwrap_err();
986                assert!(matches!(
987                    c_nan_re_inf_im,
988                    ErrorsValidationRawComplex::InvalidBothParts { .. }
989                ));
990            }
991
992            #[test]
993            fn try_from_complexf64() {
994                let c1_in = num::Complex::new(1.23, 4.56);
995                let c1 = ComplexRugStrictFinite::<PRECISION>::try_from(c1_in).unwrap();
996                assert_eq!(c1.real_part().as_ref(), &c1_in.re);
997                assert_eq!(c1.imag_part().as_ref(), &c1_in.im);
998
999                let c2_in = num::Complex::new(-7.89, -0.12);
1000                let c2 = ComplexRugStrictFinite::<PRECISION>::try_from(c2_in).unwrap();
1001                assert_eq!(c2.real_part().as_ref(), &c2_in.re);
1002                assert_eq!(c2.imag_part().as_ref(), &c2_in.im);
1003
1004                let c3_in = num::Complex::new(0., 0.);
1005                let c3 = ComplexRugStrictFinite::<PRECISION>::try_from(c3_in).unwrap();
1006                assert_eq!(c3.real_part().as_ref(), &c3_in.re);
1007                assert_eq!(c3.imag_part().as_ref(), &c3_in.im);
1008
1009                let c_nan_re =
1010                    ComplexRugStrictFinite::<PRECISION>::try_from(num::Complex::new(f64::NAN, 5.0))
1011                        .unwrap_err();
1012                assert!(matches!(
1013                    c_nan_re,
1014                    ErrorsValidationRawComplex::InvalidRealPart { .. }
1015                ));
1016
1017                let c_inf_im = ComplexRugStrictFinite::<PRECISION>::try_from(num::Complex::new(
1018                    10.0,
1019                    f64::INFINITY,
1020                ))
1021                .unwrap_err();
1022                assert!(matches!(
1023                    c_inf_im,
1024                    ErrorsValidationRawComplex::InvalidImaginaryPart { .. }
1025                ));
1026
1027                let c_nan_re_inf_im = ComplexRugStrictFinite::<PRECISION>::try_from(
1028                    num::Complex::new(f64::NAN, f64::INFINITY),
1029                )
1030                .unwrap_err();
1031                assert!(matches!(
1032                    c_nan_re_inf_im,
1033                    ErrorsValidationRawComplex::InvalidBothParts { .. }
1034                ));
1035            }
1036
1037            #[test]
1038            fn try_new_pure_real() {
1039                let r1 = RealRugStrictFinite::<PRECISION>::try_from_f64(1.23).unwrap();
1040                let c1 =
1041                    ComplexRugStrictFinite::<PRECISION>::try_new_pure_real(r1.as_ref().clone())
1042                        .unwrap();
1043                assert_eq!(c1.real_part(), r1);
1044                assert!(c1.imag_part().is_zero());
1045
1046                let c_nan = ComplexRugStrictFinite::<PRECISION>::try_new_pure_real(
1047                    Float::with_val(PRECISION, f64::NAN),
1048                )
1049                .unwrap_err();
1050                assert_matches!(
1051                    c_nan,
1052                    ErrorsValidationRawComplex::InvalidRealPart {
1053                        source
1054                    } if matches!(*source, ErrorsValidationRawReal::IsNaN { .. })
1055                );
1056            }
1057
1058            #[test]
1059            fn try_new_pure_imaginary() {
1060                let i1 = RealRugStrictFinite::<PRECISION>::try_from_f64(1.23).unwrap();
1061                let c1 = ComplexRugStrictFinite::<PRECISION>::try_new_pure_imaginary(
1062                    i1.as_ref().clone(),
1063                )
1064                .unwrap();
1065                assert!(c1.real_part().is_zero());
1066                assert_eq!(c1.imag_part(), i1);
1067
1068                let c_nan = ComplexRugStrictFinite::<PRECISION>::try_new_pure_imaginary(
1069                    Float::with_val(PRECISION, f64::NAN),
1070                )
1071                .unwrap_err();
1072                assert_matches!(
1073                    c_nan,
1074                    ErrorsValidationRawComplex::InvalidImaginaryPart {
1075                        source
1076                    } if matches!(*source, ErrorsValidationRawReal::IsNaN { .. })
1077                );
1078            }
1079
1080            #[test]
1081            fn add_to_real_part() {
1082                let mut c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
1083                    rug::Complex::with_val(PRECISION, (1.0, 2.0)),
1084                )
1085                .unwrap();
1086                c.add_to_real_part(&RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap());
1087                assert_eq!(c.real_part().as_ref(), &4.0);
1088                assert_eq!(c.imag_part().as_ref(), &2.0);
1089
1090                c.add_to_real_part(&RealRugStrictFinite::<PRECISION>::try_from_f64(-5.0).unwrap());
1091                assert_eq!(c.real_part().as_ref(), &-1.0);
1092                assert_eq!(c.imag_part().as_ref(), &2.0);
1093            }
1094
1095            #[test]
1096            fn add_to_imaginary_part() {
1097                let mut c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
1098                    rug::Complex::with_val(PRECISION, (1.0, 2.0)),
1099                )
1100                .unwrap();
1101                c.add_to_imaginary_part(
1102                    &RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap(),
1103                );
1104                assert_eq!(c.real_part().as_ref(), &1.0);
1105                assert_eq!(c.imag_part().as_ref(), &5.0);
1106
1107                c.add_to_imaginary_part(
1108                    &RealRugStrictFinite::<PRECISION>::try_from_f64(-4.0).unwrap(),
1109                );
1110                assert_eq!(c.real_part().as_ref(), &1.0);
1111                assert_eq!(c.imag_part().as_ref(), &1.0);
1112            }
1113
1114            #[test]
1115            fn multiply_real_part() {
1116                let mut c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
1117                    rug::Complex::with_val(PRECISION, (1.0, 2.0)),
1118                )
1119                .unwrap();
1120                c.multiply_real_part(&RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap());
1121                assert_eq!(c.real_part().as_ref(), &3.0);
1122                assert_eq!(c.imag_part().as_ref(), &2.0);
1123
1124                c.multiply_real_part(
1125                    &RealRugStrictFinite::<PRECISION>::try_from_f64(-2.0).unwrap(),
1126                );
1127                assert_eq!(c.real_part().as_ref(), &-6.0);
1128                assert_eq!(c.imag_part().as_ref(), &2.0);
1129            }
1130
1131            #[test]
1132            fn multiply_imaginary_part() {
1133                let mut c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
1134                    rug::Complex::with_val(PRECISION, (1.0, 2.0)),
1135                )
1136                .unwrap();
1137                c.multiply_imaginary_part(
1138                    &RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap(),
1139                );
1140                assert_eq!(c.real_part().as_ref(), &1.0);
1141                assert_eq!(c.imag_part().as_ref(), &6.0);
1142
1143                c.multiply_imaginary_part(
1144                    &RealRugStrictFinite::<PRECISION>::try_from_f64(-0.5).unwrap(),
1145                );
1146                assert_eq!(c.real_part().as_ref(), &1.0);
1147                assert_eq!(c.imag_part().as_ref(), &-3.0);
1148            }
1149
1150            #[test]
1151            fn set_real_part() {
1152                let mut c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
1153                    rug::Complex::with_val(PRECISION, (1.0, 2.0)),
1154                )
1155                .unwrap();
1156                c.set_real_part(RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap());
1157                assert_eq!(c.real_part().as_ref(), &3.0);
1158                assert_eq!(c.imag_part().as_ref(), &2.0);
1159
1160                c.set_real_part(RealRugStrictFinite::<PRECISION>::try_from_f64(-4.0).unwrap());
1161                assert_eq!(c.real_part().as_ref(), &-4.0);
1162                assert_eq!(c.imag_part().as_ref(), &2.0);
1163            }
1164
1165            #[test]
1166            fn set_imaginary_part() {
1167                let mut c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
1168                    rug::Complex::with_val(PRECISION, (1.0, 2.0)),
1169                )
1170                .unwrap();
1171                c.set_imaginary_part(RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap());
1172                assert_eq!(c.real_part().as_ref(), &1.0);
1173                assert_eq!(c.imag_part().as_ref(), &3.0);
1174
1175                c.set_imaginary_part(RealRugStrictFinite::<PRECISION>::try_from_f64(-4.0).unwrap());
1176                assert_eq!(c.real_part().as_ref(), &1.0);
1177                assert_eq!(c.imag_part().as_ref(), &-4.0);
1178            }
1179        }
1180    }
1181
1182    mod mul {
1183        use super::*;
1184
1185        mod real {
1186            use super::*;
1187
1188            #[test]
1189            fn multiply_ref() {
1190                let r1 = RealRugStrictFinite::<PRECISION>::try_new_validated(rug::Float::with_val(
1191                    PRECISION, 3.0,
1192                ))
1193                .unwrap();
1194                let r2 = RealRugStrictFinite::<PRECISION>::try_new_validated(rug::Float::with_val(
1195                    PRECISION, 4.0,
1196                ))
1197                .unwrap();
1198                let result = r1 * &r2;
1199                assert_eq!(
1200                    result,
1201                    RealRugStrictFinite::<PRECISION>::try_new_validated(rug::Float::with_val(
1202                        PRECISION, 12.0
1203                    ))
1204                    .unwrap()
1205                );
1206            }
1207        }
1208
1209        mod complex {
1210            use super::*;
1211
1212            #[test]
1213            fn multiply_ref() {
1214                let c1 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
1215                    rug::Complex::with_val(PRECISION, (1.0, 2.0)),
1216                )
1217                .unwrap();
1218                let c2 = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
1219                    rug::Complex::with_val(PRECISION, (3.0, 4.0)),
1220                )
1221                .unwrap();
1222                let result = c1 * &c2;
1223                assert_eq!(
1224                    result,
1225                    ComplexRugStrictFinite::<PRECISION>::try_new_validated(rug::Complex::with_val(
1226                        PRECISION,
1227                        (-5.0, 10.0),
1228                    ))
1229                    .unwrap()
1230                ); // (1*3 - 2*4) + (1*4 + 2*3)i
1231            }
1232
1233            #[test]
1234            fn complex_times_real() {
1235                let r = RealRugStrictFinite::<PRECISION>::try_new_validated(rug::Float::with_val(
1236                    PRECISION, 2.0,
1237                ))
1238                .unwrap();
1239                let c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
1240                    rug::Complex::with_val(PRECISION, (3.0, 4.0)),
1241                )
1242                .unwrap();
1243
1244                let result_expected = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
1245                    rug::Complex::with_val(PRECISION, (6.0, 8.0)),
1246                )
1247                .unwrap(); // 2 * (3 + 4i) = 6 + 8i
1248
1249                let result = c.clone() * &r;
1250                assert_eq!(&result, &result_expected);
1251
1252                let result = c.clone() * r.clone();
1253                assert_eq!(&result, &result_expected);
1254
1255                let mut result = c.clone();
1256                result *= &r;
1257                assert_eq!(&result, &result_expected);
1258
1259                let mut result = c.clone();
1260                result *= r;
1261                assert_eq!(&result, &result_expected);
1262            }
1263
1264            #[test]
1265            fn real_times_complex() {
1266                let r = RealRugStrictFinite::<PRECISION>::try_new_validated(rug::Float::with_val(
1267                    PRECISION, 2.0,
1268                ))
1269                .unwrap();
1270                let c = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
1271                    rug::Complex::with_val(PRECISION, (3.0, 4.0)),
1272                )
1273                .unwrap();
1274
1275                let result_expected = ComplexRugStrictFinite::<PRECISION>::try_new_validated(
1276                    rug::Complex::with_val(PRECISION, (6.0, 8.0)),
1277                )
1278                .unwrap(); // 2 * (3 + 4i) = 6 + 8i
1279
1280                let result = &r * c.clone();
1281                assert_eq!(&result, &result_expected);
1282
1283                let result = r * c.clone();
1284                assert_eq!(&result, &result_expected);
1285            }
1286        }
1287    }
1288
1289    mod arithmetic {
1290        use super::*;
1291
1292        mod real {
1293            use super::*;
1294
1295            #[test]
1296            fn neg_assign() {
1297                let mut a = rug::Float::with_val(PRECISION, 1.);
1298                a.neg_assign();
1299                let a_expected = rug::Float::with_val(PRECISION, -1.);
1300                assert_eq!(a, a_expected);
1301
1302                let mut b = RealRugStrictFinite::<PRECISION>::one();
1303                b.neg_assign();
1304                let b_expected = RealRugStrictFinite::<PRECISION>::try_new_validated(
1305                    rug::Float::with_val(PRECISION, -1.),
1306                )
1307                .unwrap();
1308                assert_eq!(&b, &b_expected);
1309            }
1310
1311            #[test]
1312            #[should_panic(expected = "Division failed validation")]
1313            fn div_by_zero() {
1314                let one = RealRugStrictFinite::<PRECISION>::one();
1315                let zero = RealRugStrictFinite::<PRECISION>::zero();
1316                let _ = &one / &zero;
1317            }
1318
1319            #[test]
1320            #[should_panic(expected = "Division failed validation")]
1321            fn div_assign_by_zero() {
1322                let mut num = RealRugStrictFinite::<PRECISION>::one();
1323                let zero_ref = &RealRugStrictFinite::<PRECISION>::zero();
1324                num /= zero_ref;
1325            }
1326        }
1327
1328        mod complex {
1329            use super::*;
1330
1331            #[test]
1332            fn neg_assign() {
1333                let re = rug::Float::with_val(PRECISION, 1.);
1334                let im = rug::Float::with_val(PRECISION, 2.);
1335                let mut num = rug::Complex::with_val(PRECISION, (re, im));
1336                num.neg_assign();
1337                let expected = rug::Complex::with_val(
1338                    PRECISION,
1339                    (
1340                        rug::Float::with_val(PRECISION, -1.),
1341                        rug::Float::with_val(PRECISION, -2.),
1342                    ),
1343                );
1344                assert_eq!(&num, &expected);
1345
1346                let mut num = ComplexRugStrictFinite::<PRECISION>::try_new_validated(num).unwrap();
1347                let expected = num.clone().neg();
1348                num.neg_assign();
1349                assert_eq!(&num, &expected);
1350            }
1351
1352            #[test]
1353            #[should_panic(expected = "Division failed validation")]
1354            fn div_by_zero() {
1355                let one = ComplexRugStrictFinite::<PRECISION>::one();
1356                let zero = ComplexRugStrictFinite::<PRECISION>::zero();
1357                let _ = &one / &zero;
1358            }
1359
1360            #[test]
1361            #[should_panic(expected = "Division failed validation")]
1362            fn div_assign_by_zero() {
1363                let mut num = ComplexRugStrictFinite::<PRECISION>::one();
1364                let zero_ref = &ComplexRugStrictFinite::<PRECISION>::zero();
1365                num /= zero_ref;
1366            }
1367        }
1368    }
1369
1370    mod real_scalar_methods {
1371        use super::*;
1372
1373        #[test]
1374        fn epsilon() {
1375            let eps = RealRugStrictFinite::<PRECISION>::epsilon();
1376            assert!(eps.is_finite() && eps > RealRugStrictFinite::<PRECISION>::zero());
1377            let expected_eps_val =
1378                rug::Float::with_val(PRECISION, 2.0).pow(1i32 - PRECISION as i32);
1379            let expected_eps = RealRugStrictFinite::<PRECISION>::try_new(expected_eps_val).unwrap();
1380            assert_eq!(eps, expected_eps, "Epsilon value mismatch");
1381        }
1382
1383        #[test]
1384        fn clamp_ref() {
1385            let val = RealRugStrictFinite::<PRECISION>::try_from_f64(5.0).unwrap();
1386            let min_val = RealRugStrictFinite::<PRECISION>::try_from_f64(0.0).unwrap();
1387            let max_val = RealRugStrictFinite::<PRECISION>::try_from_f64(10.0).unwrap();
1388
1389            assert_eq!(val.clone().clamp_ref(&min_val, &max_val), val);
1390            assert_eq!(
1391                RealRugStrictFinite::<PRECISION>::try_from_f64(-5.0)
1392                    .unwrap()
1393                    .clamp_ref(&min_val, &max_val),
1394                min_val
1395            );
1396            assert_eq!(
1397                RealRugStrictFinite::<PRECISION>::try_from_f64(15.0)
1398                    .unwrap()
1399                    .clamp_ref(&min_val, &max_val),
1400                max_val
1401            );
1402        }
1403
1404        #[test]
1405        fn hypot() {
1406            let a = RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap();
1407            let b = RealRugStrictFinite::<PRECISION>::try_from_f64(4.0).unwrap();
1408            let expected = RealRugStrictFinite::<PRECISION>::try_from_f64(5.0).unwrap();
1409            assert_eq!(a.hypot(&b), expected);
1410        }
1411
1412        #[test]
1413        fn signum() {
1414            assert_eq!(
1415                RealRugStrictFinite::<PRECISION>::try_from_f64(5.0)
1416                    .unwrap()
1417                    .kernel_signum(),
1418                RealRugStrictFinite::<PRECISION>::one()
1419            );
1420            assert_eq!(
1421                RealRugStrictFinite::<PRECISION>::try_from_f64(-5.0)
1422                    .unwrap()
1423                    .kernel_signum(),
1424                RealRugStrictFinite::<PRECISION>::negative_one()
1425            );
1426            // rug::Float::signum of 0.0 is 1.0
1427            assert_eq!(
1428                RealRugStrictFinite::<PRECISION>::zero().kernel_signum(),
1429                RealRugStrictFinite::<PRECISION>::one()
1430            );
1431        }
1432
1433        #[test]
1434        fn total_cmp() {
1435            let r1 = RealRugStrictFinite::<PRECISION>::try_from_f64(1.0).unwrap();
1436            let r2 = RealRugStrictFinite::<PRECISION>::try_from_f64(2.0).unwrap();
1437            assert_eq!(r1.total_cmp(&r1), Ordering::Equal);
1438            assert_eq!(r1.total_cmp(&r2), Ordering::Less);
1439            assert_eq!(r2.total_cmp(&r1), Ordering::Greater);
1440        }
1441
1442        #[test]
1443        fn mul_add_mul_mut() {
1444            let mut a = RealRugStrictFinite::<PRECISION>::try_from_f64(2.0).unwrap();
1445            let b = RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap(); // mul
1446            let c = RealRugStrictFinite::<PRECISION>::try_from_f64(4.0).unwrap(); // add_mul1
1447            let d = RealRugStrictFinite::<PRECISION>::try_from_f64(5.0).unwrap(); // add_mul2
1448            // Expected: a = a*b + c*d = 2*3 + 4*5 = 6 + 20 = 26
1449            a.kernel_mul_add_mul_mut(&b, &c, &d);
1450            assert_eq!(
1451                a,
1452                RealRugStrictFinite::<PRECISION>::try_from_f64(26.0).unwrap()
1453            );
1454        }
1455
1456        #[test]
1457        fn mul_sub_mul_mut() {
1458            let mut a = RealRugStrictFinite::<PRECISION>::try_from_f64(10.0).unwrap();
1459            let b = RealRugStrictFinite::<PRECISION>::try_from_f64(2.0).unwrap(); // mul
1460            let c = RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap(); // sub_mul1
1461            let d = RealRugStrictFinite::<PRECISION>::try_from_f64(4.0).unwrap(); // sub_mul2
1462            // Expected: a = a*b - c*d = 10*2 - 3*4 = 20 - 12 = 8
1463            a.kernel_mul_sub_mul_mut(&b, &c, &d);
1464            assert_eq!(
1465                a,
1466                RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1467            );
1468        }
1469
1470        #[test]
1471        fn test_real_rug_constants() {
1472            // Helper to create a raw rug::Float for comparison
1473            let raw_float = |f: f64| Float::with_val(PRECISION, f);
1474
1475            // PI - should use MPFR Constant::Pi
1476            let pi = RealRugStrictFinite::<PRECISION>::pi();
1477            let expected_pi = Float::with_val(PRECISION, MpfrConstant::Pi);
1478            assert_eq!(pi.as_ref(), &expected_pi);
1479
1480            // TWO_PI - should be 2 * MPFR Pi
1481            let two_pi = RealRugStrictFinite::<PRECISION>::two_pi();
1482            let expected_two_pi = Float::with_val(PRECISION, MpfrConstant::Pi) * 2;
1483            assert_eq!(two_pi.as_ref(), &expected_two_pi);
1484
1485            // PI_DIV_2 - should be MPFR Pi / 2
1486            let pi_div_2 = RealRugStrictFinite::<PRECISION>::pi_div_2();
1487            let expected_pi_div_2 = Float::with_val(PRECISION, MpfrConstant::Pi) / 2;
1488            assert_eq!(pi_div_2.as_ref(), &expected_pi_div_2);
1489
1490            // E - must be calculated as exp(1)
1491            let e = RealRugStrictFinite::<PRECISION>::e();
1492            let expected_e = raw_float(1.0).exp();
1493            assert_eq!(e.as_ref(), &expected_e);
1494
1495            // LOG2_E - should use 1/MPFR Log2
1496            let log2_e = RealRugStrictFinite::<PRECISION>::log2_e();
1497            let expected_log2_e = Float::with_val(PRECISION, MpfrConstant::Log2).recip();
1498            assert_eq!(log2_e.as_ref(), &expected_log2_e);
1499
1500            // LOG10_E - should be 1/(MPFR Log2 * log2(10))
1501            let log10_e = RealRugStrictFinite::<PRECISION>::log10_e();
1502            let ln2 = Float::with_val(PRECISION, MpfrConstant::Log2);
1503            let log2_10 = raw_float(10.0).log2();
1504            let expected_log10_e = (ln2 * log2_10).recip();
1505            assert_eq!(log10_e.as_ref(), &expected_log10_e);
1506
1507            // LN_2 - should use MPFR Constant::Log2
1508            let ln_2 = RealRugStrictFinite::<PRECISION>::ln_2();
1509            let expected_ln_2 = Float::with_val(PRECISION, MpfrConstant::Log2);
1510            assert_eq!(ln_2.as_ref(), &expected_ln_2);
1511
1512            // LN_10 - should be MPFR Log2 * log2(10)
1513            let ln_10 = RealRugStrictFinite::<PRECISION>::ln_10();
1514            let ln2 = Float::with_val(PRECISION, MpfrConstant::Log2);
1515            let log2_10 = raw_float(10.0).log2();
1516            let expected_ln_10 = ln2 * log2_10;
1517            assert_eq!(ln_10.as_ref(), &expected_ln_10);
1518
1519            // LOG2_10
1520            let log2_10 = RealRugStrictFinite::<PRECISION>::log2_10();
1521            let expected_log2_10 = raw_float(10.0).log2();
1522            assert_eq!(log2_10.as_ref(), &expected_log2_10);
1523
1524            // LOG10_2
1525            let log10_2 = RealRugStrictFinite::<PRECISION>::log10_2();
1526            let expected_log10_2 = raw_float(2.0).log10();
1527            assert_eq!(log10_2.as_ref(), &expected_log10_2);
1528
1529            // MAX_FINITE
1530            let max_finite = RealRugStrictFinite::<PRECISION>::max_finite();
1531            let expected_max_finite = <rug::Float as RawRealTrait>::raw_max_finite(PRECISION);
1532            assert_eq!(max_finite.as_ref(), &expected_max_finite);
1533
1534            // MIN_FINITE
1535            let min_finite = RealRugStrictFinite::<PRECISION>::min_finite();
1536            let expected_min_finite = <rug::Float as RawRealTrait>::raw_min_finite(PRECISION);
1537            assert_eq!(min_finite.as_ref(), &expected_min_finite);
1538
1539            // EPSILON
1540            let epsilon = RealRugStrictFinite::<PRECISION>::epsilon();
1541            let expected_epsilon = <rug::Float as RawRealTrait>::raw_epsilon(PRECISION);
1542            assert_eq!(epsilon.as_ref(), &expected_epsilon);
1543
1544            // NEGATIVE_ONE
1545            assert_eq!(
1546                RealRugStrictFinite::<PRECISION>::negative_one().as_ref(),
1547                &raw_float(-1.0)
1548            );
1549
1550            // TWO
1551            assert_eq!(
1552                RealRugStrictFinite::<PRECISION>::two().as_ref(),
1553                &raw_float(2.0)
1554            );
1555
1556            // ONE_DIV_2
1557            assert_eq!(
1558                RealRugStrictFinite::<PRECISION>::one_div_2().as_ref(),
1559                &raw_float(0.5)
1560            );
1561        }
1562
1563        /// Test that MPFR optimized constants are used instead of calculations.
1564        /// This verifies that pi() uses Constant::Pi (not acos(-1)) and
1565        /// ln_2() uses Constant::Log2 (not ln(2)).
1566        #[test]
1567        fn test_real_rug_constants_mpfr_optimization() {
1568            // π should use MpfrConstant::Pi (not acos(-1))
1569            let pi = RealRugStrictFinite::<PRECISION>::pi();
1570            let expected_pi = Float::with_val(PRECISION, MpfrConstant::Pi);
1571            assert_eq!(pi.as_ref(), &expected_pi);
1572
1573            // ln(2) should use MpfrConstant::Log2 (not ln(2) calculation)
1574            let ln_2 = RealRugStrictFinite::<PRECISION>::ln_2();
1575            let expected_ln_2 = Float::with_val(PRECISION, MpfrConstant::Log2);
1576            assert_eq!(ln_2.as_ref(), &expected_ln_2);
1577
1578            // Derived constants should be computed from MPFR base constants
1579            let two_pi = RealRugStrictFinite::<PRECISION>::two_pi();
1580            let expected_two_pi = Float::with_val(PRECISION, MpfrConstant::Pi) * 2;
1581            assert_eq!(two_pi.as_ref(), &expected_two_pi);
1582
1583            let pi_div_2 = RealRugStrictFinite::<PRECISION>::pi_div_2();
1584            let expected_pi_div_2 = Float::with_val(PRECISION, MpfrConstant::Pi) / 2;
1585            assert_eq!(pi_div_2.as_ref(), &expected_pi_div_2);
1586        }
1587
1588        /// Test that constants with different precisions are independent and correct.
1589        #[test]
1590        fn test_real_rug_constants_precision_independence() {
1591            type Real100 = RealRugStrictFinite<100>;
1592            type Real200 = RealRugStrictFinite<200>;
1593            type Real500 = RealRugStrictFinite<500>;
1594
1595            // Get π at different precisions
1596            let pi_100 = Real100::pi();
1597            let pi_200 = Real200::pi();
1598            let pi_500 = Real500::pi();
1599
1600            // They should all represent π, but with different precisions
1601            // Convert to f64 for comparison (all should be close to π)
1602            let pi_100_f64 = pi_100.as_ref().to_f64();
1603            let pi_200_f64 = pi_200.as_ref().to_f64();
1604            let pi_500_f64 = pi_500.as_ref().to_f64();
1605
1606            let pi_expected = std::f64::consts::PI;
1607            assert!((pi_100_f64 - pi_expected).abs() < 1e-15);
1608            assert!((pi_200_f64 - pi_expected).abs() < 1e-15);
1609            assert!((pi_500_f64 - pi_expected).abs() < 1e-15);
1610
1611            // Test e at different precisions
1612            let e_100 = Real100::e();
1613            let e_200 = Real200::e();
1614
1615            let e_100_f64 = e_100.as_ref().to_f64();
1616            let e_200_f64 = e_200.as_ref().to_f64();
1617
1618            let e_expected = std::f64::consts::E;
1619            assert!((e_100_f64 - e_expected).abs() < 1e-15);
1620            assert!((e_200_f64 - e_expected).abs() < 1e-15);
1621        }
1622
1623        /// Test mathematical correctness of constant relationships.
1624        #[test]
1625        fn test_real_rug_constants_mathematical_relationships() {
1626            let pi = RealRugStrictFinite::<PRECISION>::pi();
1627            let two_pi = RealRugStrictFinite::<PRECISION>::two_pi();
1628            let pi_div_2 = RealRugStrictFinite::<PRECISION>::pi_div_2();
1629
1630            // two_pi = 2 * pi
1631            let expected_two_pi = pi.as_ref() * Float::with_val(PRECISION, 2);
1632            assert_eq!(two_pi.as_ref(), &expected_two_pi);
1633
1634            // pi_div_2 = pi / 2
1635            let expected_pi_div_2 = pi.as_ref() / Float::with_val(PRECISION, 2);
1636            assert_eq!(pi_div_2.as_ref(), &expected_pi_div_2);
1637
1638            let ln_2 = RealRugStrictFinite::<PRECISION>::ln_2();
1639            let ln_10 = RealRugStrictFinite::<PRECISION>::ln_10();
1640            let log2_e = RealRugStrictFinite::<PRECISION>::log2_e();
1641            let log10_e = RealRugStrictFinite::<PRECISION>::log10_e();
1642
1643            // log2_e * ln_2 should equal 1 (since logâ‚‚(e) = 1/ln(2))
1644            let product = (log2_e.as_ref() * ln_2.as_ref()).complete(PRECISION);
1645            let one = Float::with_val(PRECISION, 1);
1646            let epsilon = Float::with_val(PRECISION, 1e-10);
1647            let diff = (product - &one).abs();
1648            assert!(diff < epsilon);
1649
1650            // log10_e * ln_10 should equal 1
1651            let product = (log10_e.as_ref() * ln_10.as_ref()).complete(PRECISION);
1652            let diff = (product - &one).abs();
1653            assert!(diff < epsilon);
1654        }
1655
1656        /// Test that multiple calls to the same constant produce identical values.
1657        /// This verifies computational consistency (MPFR constants are deterministic).
1658        #[test]
1659        fn test_real_rug_constants_consistency() {
1660            // Call each constant twice and verify they produce identical values
1661            let pi1 = RealRugStrictFinite::<PRECISION>::pi();
1662            let pi2 = RealRugStrictFinite::<PRECISION>::pi();
1663            assert_eq!(pi1.as_ref(), pi2.as_ref());
1664
1665            let e1 = RealRugStrictFinite::<PRECISION>::e();
1666            let e2 = RealRugStrictFinite::<PRECISION>::e();
1667            assert_eq!(e1.as_ref(), e2.as_ref());
1668
1669            let ln2_1 = RealRugStrictFinite::<PRECISION>::ln_2();
1670            let ln2_2 = RealRugStrictFinite::<PRECISION>::ln_2();
1671            assert_eq!(ln2_1.as_ref(), ln2_2.as_ref());
1672
1673            let two_pi1 = RealRugStrictFinite::<PRECISION>::two_pi();
1674            let two_pi2 = RealRugStrictFinite::<PRECISION>::two_pi();
1675            assert_eq!(two_pi1.as_ref(), two_pi2.as_ref());
1676
1677            let pi_div_2_1 = RealRugStrictFinite::<PRECISION>::pi_div_2();
1678            let pi_div_2_2 = RealRugStrictFinite::<PRECISION>::pi_div_2();
1679            assert_eq!(pi_div_2_1.as_ref(), pi_div_2_2.as_ref());
1680        }
1681    }
1682
1683    mod scalar_constructors {
1684        use super::*;
1685        use crate::{
1686            core::errors::ErrorsValidationRawReal,
1687            functions::{
1688                ATan2InputErrors, LogarithmRealErrors, LogarithmRealInputErrors,
1689                PowComplexBaseRealExponentInputErrors, PowRealBaseRealExponentInputErrors,
1690                ReciprocalInputErrors, SqrtRealInputErrors,
1691            },
1692        };
1693
1694        mod real {
1695            use super::*;
1696
1697            #[test]
1698            fn exp_overflow() {
1699                let large_val = RealRugStrictFinite::<PRECISION>::try_from_f64(1.0e60).unwrap(); // exp(1.0e60) is very large
1700                let res_large = large_val.try_exp();
1701                assert!(matches!(
1702                    res_large,
1703                    Err(ExpErrors::Output {
1704                        source: ErrorsValidationRawReal::IsPosInfinity { .. }
1705                    })
1706                ),);
1707            }
1708
1709            #[test]
1710            fn ln_domain_errors() {
1711                let neg_val = RealRugStrictFinite::<PRECISION>::try_from_f64(-1.0).unwrap();
1712                assert!(matches!(
1713                    neg_val.try_ln(),
1714                    Err(LogarithmRealErrors::Input(
1715                        LogarithmRealInputErrors::NegativeArgument { .. }
1716                    ))
1717                ));
1718
1719                let zero_val = RealRugStrictFinite::<PRECISION>::zero();
1720                assert!(matches!(
1721                    zero_val.try_ln(),
1722                    Err(LogarithmRealErrors::Input(
1723                        LogarithmRealInputErrors::ZeroArgument { .. }
1724                    ))
1725                ));
1726            }
1727        } // end mod real
1728
1729        mod complex {
1730            use super::*;
1731
1732            #[test]
1733            fn log2_zero() {
1734                let zero_val = ComplexRugStrictFinite::<PRECISION>::zero();
1735                assert!(matches!(
1736                    zero_val.try_log2(),
1737                    Err(LogarithmComplexErrors::Input(
1738                        LogarithmComplexInputErrors::ZeroArgument { .. }
1739                    ))
1740                ));
1741            }
1742        } // end mod complex
1743
1744        mod pow {
1745            use super::*;
1746
1747            mod real_base {
1748                use super::*;
1749
1750                #[test]
1751                fn negative_base_real_exponent_error() {
1752                    let base = RealRugStrictFinite::<PRECISION>::try_from_f64(-2.0).unwrap();
1753                    let exponent = RealRugStrictFinite::<PRECISION>::try_from_f64(0.5).unwrap();
1754                    let res = base.try_pow(&exponent);
1755                    assert!(matches!(
1756                        res,
1757                        Err(PowRealBaseRealExponentErrors::Input(
1758                            PowRealBaseRealExponentInputErrors::NegativeBase { .. }
1759                        ))
1760                    ));
1761                }
1762
1763                #[test]
1764                fn real_base_uint_exponent() {
1765                    let base = RealRugStrictFinite::<PRECISION>::try_from_f64(2.0).unwrap();
1766                    assert_eq!(
1767                        base.clone().try_pow(3u8).unwrap(),
1768                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1769                    );
1770                    assert_eq!(
1771                        base.clone().try_pow(3u16).unwrap(),
1772                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1773                    );
1774                    assert_eq!(
1775                        base.clone().try_pow(3u32).unwrap(),
1776                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1777                    );
1778                    assert_eq!(
1779                        base.clone().try_pow(3u64).unwrap(),
1780                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1781                    );
1782                    assert_eq!(
1783                        base.clone().try_pow(3u128).unwrap(),
1784                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1785                    );
1786                    assert_eq!(
1787                        base.clone().try_pow(3usize).unwrap(),
1788                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1789                    );
1790
1791                    assert_eq!(
1792                        base.clone().pow(3u8),
1793                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1794                    );
1795                    assert_eq!(
1796                        base.clone().pow(3u16),
1797                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1798                    );
1799                    assert_eq!(
1800                        base.clone().pow(3u32),
1801                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1802                    );
1803                    assert_eq!(
1804                        base.clone().pow(3u64),
1805                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1806                    );
1807                    assert_eq!(
1808                        base.clone().pow(3u128),
1809                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1810                    );
1811                    assert_eq!(
1812                        base.clone().pow(3usize),
1813                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1814                    );
1815                }
1816
1817                #[test]
1818                fn real_base_int_exponent() {
1819                    let base = RealRugStrictFinite::<PRECISION>::try_from_f64(2.0).unwrap();
1820                    assert_eq!(
1821                        base.clone().try_pow(3i8).unwrap(),
1822                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1823                    );
1824                    assert_eq!(
1825                        base.clone().try_pow(3i16).unwrap(),
1826                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1827                    );
1828                    assert_eq!(
1829                        base.clone().try_pow(3i32).unwrap(),
1830                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1831                    );
1832                    assert_eq!(
1833                        base.clone().try_pow(3i64).unwrap(),
1834                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1835                    );
1836                    assert_eq!(
1837                        base.clone().try_pow(3i128).unwrap(),
1838                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1839                    );
1840                    assert_eq!(
1841                        base.clone().try_pow(3isize).unwrap(),
1842                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1843                    );
1844
1845                    assert_eq!(
1846                        base.clone().pow(3i8),
1847                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1848                    );
1849                    assert_eq!(
1850                        base.clone().pow(3i16),
1851                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1852                    );
1853                    assert_eq!(
1854                        base.clone().pow(3i32),
1855                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1856                    );
1857                    assert_eq!(
1858                        base.clone().pow(3i64),
1859                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1860                    );
1861                    assert_eq!(
1862                        base.clone().pow(3i128),
1863                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1864                    );
1865                    assert_eq!(
1866                        base.clone().pow(3isize),
1867                        RealRugStrictFinite::<PRECISION>::try_from_f64(8.0).unwrap()
1868                    );
1869                }
1870
1871                #[test]
1872                fn real_base_int_exponent_zero_neg_exp_error() {
1873                    let base = RealRugStrictFinite::<PRECISION>::zero();
1874                    let exponent: i32 = -2;
1875                    let res = base.try_pow(exponent);
1876                    assert!(matches!(
1877                        res,
1878                        Err(PowIntExponentErrors::Input(
1879                            PowIntExponentInputErrors::ZeroBaseNegativeExponent { .. }
1880                        ))
1881                    ));
1882                }
1883            }
1884
1885            mod complex_base {
1886                use super::*;
1887
1888                #[test]
1889                fn complex_base_uint_exponent() {
1890                    let base = ComplexRugStrictFinite::<PRECISION>::try_new(
1891                        rug::Complex::with_val(PRECISION, (2.0, 3.0)),
1892                    )
1893                    .unwrap();
1894                    let expected_res = ComplexRugStrictFinite::<PRECISION>::try_new(
1895                        rug::Complex::with_val(PRECISION, (-46.0, 9.0)),
1896                    )
1897                    .unwrap();
1898
1899                    assert_eq!(&base.clone().try_pow(3u8).unwrap(), &expected_res);
1900                    assert_eq!(&base.clone().try_pow(3u16).unwrap(), &expected_res);
1901                    assert_eq!(&base.clone().try_pow(3u32).unwrap(), &expected_res);
1902                    assert_eq!(&base.clone().try_pow(3u64).unwrap(), &expected_res);
1903                    assert_eq!(&base.clone().try_pow(3u128).unwrap(), &expected_res);
1904                    assert_eq!(&base.clone().try_pow(3usize).unwrap(), &expected_res);
1905
1906                    assert_eq!(&base.clone().pow(3u8), &expected_res);
1907                    assert_eq!(&base.clone().pow(3u16), &expected_res);
1908                    assert_eq!(&base.clone().pow(3u32), &expected_res);
1909                    assert_eq!(&base.clone().pow(3u64), &expected_res);
1910                    assert_eq!(&base.clone().pow(3u128), &expected_res);
1911                    assert_eq!(&base.clone().pow(3usize), &expected_res);
1912                }
1913
1914                #[test]
1915                fn complex_base_int_exponent() {
1916                    let base = ComplexRugStrictFinite::<PRECISION>::try_new(
1917                        rug::Complex::with_val(PRECISION, (2.0, 3.0)),
1918                    )
1919                    .unwrap();
1920                    let expected_res = ComplexRugStrictFinite::<PRECISION>::try_new(
1921                        rug::Complex::with_val(PRECISION, (-46.0, 9.0)),
1922                    )
1923                    .unwrap();
1924
1925                    assert_eq!(&base.clone().try_pow(3i8).unwrap(), &expected_res);
1926                    assert_eq!(&base.clone().try_pow(3i16).unwrap(), &expected_res);
1927                    assert_eq!(&base.clone().try_pow(3i32).unwrap(), &expected_res);
1928                    assert_eq!(&base.clone().try_pow(3i64).unwrap(), &expected_res);
1929                    assert_eq!(&base.clone().try_pow(3i128).unwrap(), &expected_res);
1930                    assert_eq!(&base.clone().try_pow(3isize).unwrap(), &expected_res);
1931
1932                    assert_eq!(&base.clone().pow(3i8), &expected_res);
1933                    assert_eq!(&base.clone().pow(3i16), &expected_res);
1934                    assert_eq!(&base.clone().pow(3i32), &expected_res);
1935                    assert_eq!(&base.clone().pow(3i64), &expected_res);
1936                    assert_eq!(&base.clone().pow(3i128), &expected_res);
1937                    assert_eq!(&base.clone().pow(3isize), &expected_res);
1938                }
1939
1940                #[test]
1941                fn complex_zero_base_negative_real_exponent_error() {
1942                    let base = ComplexRugStrictFinite::<PRECISION>::zero();
1943                    let exponent = RealRugStrictFinite::<PRECISION>::try_from_f64(-2.0).unwrap();
1944                    let res = base.try_pow(&exponent);
1945                    assert!(matches!(
1946                        res,
1947                        Err(PowComplexBaseRealExponentErrors::Input(
1948                            PowComplexBaseRealExponentInputErrors::ZeroBaseNegativeExponent { .. }
1949                        ))
1950                    ));
1951                }
1952
1953                #[test]
1954                fn complex_zero_base_zero_real_exponent() {
1955                    let base = ComplexRugStrictFinite::<PRECISION>::zero();
1956                    let exponent = RealRugStrictFinite::<PRECISION>::zero();
1957                    let res = base.try_pow(&exponent).unwrap();
1958                    assert_eq!(res, ComplexRugStrictFinite::<PRECISION>::one());
1959                }
1960
1961                #[test]
1962                fn complex_base_int_exponent_zero_neg_exp_error() {
1963                    let base = ComplexRugStrictFinite::<PRECISION>::zero();
1964                    let exponent: i32 = -2;
1965                    let res = base.try_pow(exponent);
1966                    assert!(matches!(
1967                        res,
1968                        Err(PowIntExponentErrors::Input(
1969                            PowIntExponentInputErrors::ZeroBaseNegativeExponent { .. }
1970                        ))
1971                    ));
1972                }
1973            }
1974        }
1975
1976        #[test]
1977        fn reciprocal_real_rug_zero() {
1978            let zero_val = RealRugStrictFinite::<PRECISION>::zero();
1979            let res = zero_val.try_reciprocal();
1980            assert!(matches!(
1981                res,
1982                Err(ReciprocalErrors::Input(
1983                    ReciprocalInputErrors::DivisionByZero { .. }
1984                ))
1985            ));
1986        }
1987
1988        #[test]
1989        fn reciprocal_complex_rug_zero() {
1990            let zero_val = ComplexRugStrictFinite::<PRECISION>::zero();
1991            let res = zero_val.try_reciprocal();
1992            assert!(matches!(
1993                res,
1994                Err(ReciprocalErrors::Input(
1995                    ReciprocalInputErrors::DivisionByZero { .. }
1996                ))
1997            ));
1998        }
1999
2000        #[test]
2001        fn sqrt_real_rug_negative_input() {
2002            let neg_val = RealRugStrictFinite::<PRECISION>::try_from_f64(-4.0).unwrap();
2003            let res = neg_val.try_sqrt();
2004            assert!(matches!(
2005                res,
2006                Err(SqrtRealErrors::Input(
2007                    SqrtRealInputErrors::NegativeValue { .. }
2008                ))
2009            ));
2010        }
2011
2012        mod trigonometric {
2013            use super::*;
2014
2015            #[test]
2016            fn atan2_real_rug_zero_over_zero() {
2017                let zero_val = RealRugStrictFinite::<PRECISION>::zero();
2018                let res = zero_val.try_atan2(&RealRugStrictFinite::<PRECISION>::zero());
2019                assert!(matches!(
2020                    res,
2021                    Err(ATan2Errors::Input(ATan2InputErrors::ZeroOverZero { .. }))
2022                ));
2023            }
2024
2025            /*
2026            #[test]
2027            #[ignore = "at the moment we cannot create a pole for the Tan function"]
2028            fn tan_real_rug_pole() {
2029                // tan(PI/2) is a pole
2030                let pi_half = RealRugStrictFinite::<PRECISION>::pi_div_2();
2031                let res = pi_half.try_tan();
2032                println!("Result: {:?}", res);
2033                assert!(matches!(
2034                    res,
2035                    Err(TanRealErrors::Input(TanRealInputErrors::ArgumentIsPole { .. }))
2036                ));
2037            }
2038            */
2039
2040            #[test]
2041            fn asin_real_rug_out_of_domain() {
2042                let val_gt_1 = RealRugStrictFinite::<PRECISION>::try_from_f64(1.5).unwrap();
2043                assert!(matches!(
2044                    val_gt_1.try_asin(),
2045                    Err(ASinRealErrors::Input(
2046                        ASinRealInputErrors::OutOfDomain { .. }
2047                    ))
2048                ));
2049                let val_lt_neg1 = RealRugStrictFinite::<PRECISION>::try_from_f64(-1.5).unwrap();
2050                assert!(matches!(
2051                    val_lt_neg1.try_asin(),
2052                    Err(ASinRealErrors::Input(
2053                        ASinRealInputErrors::OutOfDomain { .. }
2054                    ))
2055                ));
2056            }
2057
2058            #[test]
2059            fn acos_real_rug_out_of_domain() {
2060                let val_gt_1 = RealRugStrictFinite::<PRECISION>::try_from_f64(1.5).unwrap();
2061                assert!(matches!(
2062                    val_gt_1.try_acos(),
2063                    Err(ACosRealErrors::Input(
2064                        ACosRealInputErrors::OutOfDomain { .. }
2065                    ))
2066                ));
2067                let val_lt_neg1 = RealRugStrictFinite::<PRECISION>::try_from_f64(-1.5).unwrap();
2068                assert!(matches!(
2069                    val_lt_neg1.try_acos(),
2070                    Err(ACosRealErrors::Input(
2071                        ACosRealInputErrors::OutOfDomain { .. }
2072                    ))
2073                ));
2074            }
2075
2076            #[test]
2077            fn atan_complex_rug_pole() {
2078                // atan(i) and atan(-i) are poles
2079                let i_val = ComplexRugStrictFinite::<PRECISION>::try_new_pure_imaginary(
2080                    Float::with_val(PRECISION, 1.0),
2081                )
2082                .unwrap();
2083                assert!(matches!(
2084                    i_val.try_atan(),
2085                    Err(ATanComplexErrors::Input(
2086                        ATanComplexInputErrors::ArgumentIsPole { .. }
2087                    ))
2088                ));
2089
2090                let neg_i_val = ComplexRugStrictFinite::<PRECISION>::try_new_pure_imaginary(
2091                    Float::with_val(PRECISION, -1.0),
2092                )
2093                .unwrap();
2094                assert!(matches!(
2095                    neg_i_val.try_atan(),
2096                    Err(ATanComplexErrors::Input(
2097                        ATanComplexInputErrors::ArgumentIsPole { .. }
2098                    ))
2099                ));
2100            }
2101        } // endmod trigonometric
2102
2103        mod hyperbolic {
2104            use super::*;
2105
2106            mod real {
2107                use super::*;
2108
2109                #[test]
2110                fn atanh_real_rug_out_of_domain() {
2111                    let val_ge_1 = RealRugStrictFinite::<PRECISION>::one(); // atanh(1) is Inf
2112                    assert!(matches!(
2113                        val_ge_1.try_atanh(),
2114                        Err(ATanHErrors::Input(ATanHInputErrors::OutOfDomain { .. }))
2115                    ));
2116
2117                    let val_le_neg1 = RealRugStrictFinite::<PRECISION>::negative_one(); // atanh(-1) is -Inf
2118                    assert!(matches!(
2119                        val_le_neg1.try_atanh(),
2120                        Err(ATanHErrors::Input(ATanHInputErrors::OutOfDomain { .. }))
2121                    ));
2122                }
2123
2124                #[test]
2125                fn acosh_real_rug_out_of_domain() {
2126                    let val_lt_1 = RealRugStrictFinite::<PRECISION>::try_from_f64(0.5).unwrap();
2127                    assert!(matches!(
2128                        val_lt_1.try_acosh(),
2129                        Err(ACosHErrors::Input(ACosHInputErrors::OutOfDomain { .. }))
2130                    ));
2131                }
2132            }
2133
2134            mod complex {
2135                use super::*;
2136
2137                /*
2138                #[test]
2139                #[ignore = "at the moment we cannot create a pole for the ATanH function"]
2140                fn tanh_complex_rug_pole() {
2141                    // tanh(z) has poles where cosh(z) = 0. e.g. z = i * (PI/2 + k*PI)
2142                    let pi_half = RealRugStrictFinite::<PRECISION>::pi_div_2().into_inner();
2143                    let pole_val =
2144                        ComplexRugStrictFinite::<PRECISION>::try_new_pure_imaginary(pi_half).unwrap();
2145                    println!("Atanh(Pole value): {:?}", pole_val.clone().try_tanh());
2146                    assert!(matches!(
2147                        pole_val.try_tanh(),
2148                        Err(TanHComplexErrors::Input(TanHComplexInputErrors::OutOfDomain { .. }))
2149                    ));
2150                }
2151                */
2152
2153                #[test]
2154                fn acosh_out_of_domain() {
2155                    // acosh(z) domain is C \ (-inf, 1) on real axis
2156                    let val_on_branch_cut = ComplexRugStrictFinite::<PRECISION>::try_new_pure_real(
2157                        Float::with_val(PRECISION, 0.5),
2158                    )
2159                    .unwrap();
2160                    assert!(matches!(
2161                        val_on_branch_cut.try_acosh(),
2162                        Err(ACosHErrors::Input(ACosHInputErrors::OutOfDomain { .. }))
2163                    ));
2164
2165                    let val_on_branch_cut_neg =
2166                        ComplexRugStrictFinite::<PRECISION>::try_new_pure_real(Float::with_val(
2167                            PRECISION, -5.0,
2168                        ))
2169                        .unwrap();
2170                    assert!(matches!(
2171                        val_on_branch_cut_neg.try_acosh(),
2172                        Err(ACosHErrors::Input(ACosHInputErrors::OutOfDomain { .. }))
2173                    ));
2174                }
2175
2176                #[test]
2177                fn atanh_out_of_domain() {
2178                    let val_ge_1 = ComplexRugStrictFinite::<PRECISION>::try_new_pure_real(
2179                        Float::with_val(PRECISION, 1.0),
2180                    )
2181                    .unwrap();
2182                    assert!(matches!(
2183                        val_ge_1.try_atanh(),
2184                        Err(ATanHErrors::Input(ATanHInputErrors::OutOfDomain { .. }))
2185                    ));
2186
2187                    let val_le_neg1 = ComplexRugStrictFinite::<PRECISION>::try_new_pure_real(
2188                        Float::with_val(PRECISION, -1.0),
2189                    )
2190                    .unwrap();
2191                    assert!(matches!(
2192                        val_le_neg1.try_atanh(),
2193                        Err(ATanHErrors::Input(ATanHInputErrors::OutOfDomain { .. }))
2194                    ));
2195                }
2196            }
2197
2198            /*
2199            #[test]
2200            #[ignore = "at the moment we cannot create a pole for the TanH function"]
2201            fn tanh_out_of_domain() {
2202                // tanh(z) has poles where cosh(z) = 0. e.g. z = i * (PI/2 + k*PI)
2203                let pi_half = RealRugStrictFinite::<PRECISION>::pi_div_2().into_inner();
2204                let pole_val = ComplexRugStrictFinite::<PRECISION>::try_new_pure_imaginary(pi_half).unwrap();
2205                println!("TanH(Pole value): {:?}", pole_val.clone().try_tanh());
2206                assert!(matches!(
2207                    pole_val.try_tanh(),
2208                    Err(TanHComplexErrors::Input(TanHComplexInputErrors::OutOfDomain { .. }))
2209                ));
2210            }
2211            */
2212        } // end mod hyperbolic
2213    }
2214
2215    mod summation {
2216        use super::*;
2217
2218        const PRECISION: u32 = 53;
2219
2220        type RealValidated = RealRugStrictFinite<PRECISION>;
2221        type ComplexValidated = ComplexRugStrictFinite<PRECISION>;
2222
2223        #[test]
2224        fn sum_real() {
2225            let values = vec![
2226                RealValidated::try_from_f64(1.0).unwrap(),
2227                RealValidated::try_from_f64(2.0).unwrap(),
2228                RealValidated::try_from_f64(3.0).unwrap(),
2229                RealValidated::try_from_f64(4.0).unwrap(),
2230                RealValidated::try_from_f64(5.0).unwrap(),
2231            ];
2232            let sum: RealValidated = values.into_iter().sum();
2233            assert_eq!(sum, RealValidated::try_from_f64(15.0).unwrap());
2234        }
2235
2236        #[test]
2237        fn sum_real_compensated() {
2238            // Test case where simple summation might lose precision
2239            let values = vec![
2240                RealValidated::try_from_f64(1.0e100).unwrap(),
2241                RealValidated::try_from_f64(1.0).unwrap(),
2242                RealValidated::try_from_f64(-1.0e100).unwrap(),
2243            ];
2244            let sum: RealValidated = values.into_iter().sum();
2245            // The Neumaier sum should correctly result in 1.0
2246            assert_eq!(sum, RealValidated::try_from_f64(1.0).unwrap());
2247        }
2248
2249        #[test]
2250        fn sum_complex() {
2251            let values = vec![
2252                ComplexValidated::try_new_complex(
2253                    rug::Float::with_val(PRECISION, 1.),
2254                    rug::Float::with_val(PRECISION, 2.),
2255                )
2256                .unwrap(),
2257                ComplexValidated::try_new_complex(
2258                    rug::Float::with_val(PRECISION, 3.),
2259                    rug::Float::with_val(PRECISION, 4.),
2260                )
2261                .unwrap(),
2262                ComplexValidated::try_new_complex(
2263                    rug::Float::with_val(PRECISION, 5.),
2264                    rug::Float::with_val(PRECISION, 6.),
2265                )
2266                .unwrap(),
2267            ];
2268            let sum: ComplexValidated = values.into_iter().sum();
2269            assert_eq!(
2270                sum,
2271                ComplexValidated::try_new_complex(
2272                    rug::Float::with_val(PRECISION, 9.),
2273                    rug::Float::with_val(PRECISION, 12.)
2274                )
2275                .unwrap()
2276            );
2277        }
2278
2279        #[test]
2280        fn sum_complex_compensated() {
2281            let values = [
2282                ComplexValidated::try_new_complex(
2283                    rug::Float::with_val(PRECISION, 1.0e100),
2284                    rug::Float::with_val(PRECISION, -1.0e100),
2285                )
2286                .unwrap(),
2287                ComplexValidated::try_new_complex(
2288                    rug::Float::with_val(PRECISION, 1.),
2289                    rug::Float::with_val(PRECISION, 2.),
2290                )
2291                .unwrap(),
2292                ComplexValidated::try_new_complex(
2293                    rug::Float::with_val(PRECISION, -1.0e100),
2294                    rug::Float::with_val(PRECISION, 1.0e100),
2295                )
2296                .unwrap(),
2297            ];
2298            let sum: ComplexValidated = values.iter().cloned().sum();
2299            assert_eq!(
2300                sum,
2301                ComplexValidated::try_new_complex(
2302                    rug::Float::with_val(PRECISION, 1.),
2303                    rug::Float::with_val(PRECISION, 2.)
2304                )
2305                .unwrap()
2306            );
2307        }
2308    } // end mod summation
2309
2310    mod random {
2311        use super::*;
2312        use crate::{RandomSampleFromF64, new_random_vec};
2313        use rand::{RngExt, SeedableRng, distr::Uniform, rngs::StdRng};
2314
2315        const PRECISION: u32 = 53;
2316
2317        type RealValidated = RealRugStrictFinite<PRECISION>;
2318        type ComplexValidated = ComplexRugStrictFinite<PRECISION>;
2319
2320        /// Tests the random generation of a `RealValidated` value.
2321        /// It uses a seeded RNG to ensure deterministic results and checks
2322        /// if the generated value falls within the expected [0, 1) range.
2323        #[test]
2324        fn test_random_real_validated() {
2325            let seed = [42; 32];
2326            let mut rng = StdRng::from_seed(seed);
2327
2328            let random_real: RealValidated = rng.random();
2329
2330            // rng.random::<f64>() produces a value in [0, 1), so the converted value should be in the same range.
2331            assert_eq!(random_real.as_ref(), &0.23713468825474326);
2332
2333            // Check for determinism
2334            let mut rng2 = StdRng::from_seed(seed);
2335            let random_real2: RealValidated = rng2.random();
2336            assert_eq!(random_real, random_real2);
2337        }
2338
2339        /// Tests the random generation of a `ComplexValidated` value.
2340        /// It uses a seeded RNG for determinism and verifies that both the real
2341        /// and imaginary parts of the generated complex number are within the
2342        /// expected [0, 1) range.
2343        #[test]
2344        fn test_random_complex_validated() {
2345            let seed = [99; 32];
2346            let mut rng = StdRng::from_seed(seed);
2347
2348            let random_complex: ComplexValidated = rng.random();
2349
2350            // The real and imaginary parts are generated independently,
2351            // so both should be in the [0, 1) range.
2352            let real_part = random_complex.real_part();
2353            let imag_part = random_complex.imag_part();
2354
2355            assert_eq!(real_part.as_ref(), &0.9995546882627792);
2356            assert_eq!(imag_part.as_ref(), &0.08932180682540247);
2357
2358            // Check for determinism
2359            let mut rng2 = StdRng::from_seed(seed);
2360            let random_complex2: ComplexValidated = rng2.random();
2361            assert_eq!(random_complex, random_complex2);
2362        }
2363
2364        const SEED: [u8; 32] = [42; 32];
2365
2366        #[test]
2367        fn test_sample_real_validated() {
2368            let mut rng = StdRng::from_seed(SEED);
2369            let dist = Uniform::new(-10.0, 10.0).unwrap();
2370
2371            let val = RealValidated::sample_from(&dist, &mut rng);
2372            assert_eq!(val.as_ref(), &-5.257306234905137);
2373
2374            // Check determinism
2375            let mut rng2 = StdRng::from_seed(SEED);
2376            let val2 = RealValidated::sample_from(&dist, &mut rng2);
2377            assert_eq!(val, val2);
2378        }
2379
2380        #[test]
2381        fn test_sample_complex_validated() {
2382            let mut rng = StdRng::from_seed(SEED);
2383            let dist = Uniform::new(-10.0, 10.0).unwrap();
2384
2385            let val = ComplexValidated::sample_from(&dist, &mut rng);
2386            assert_eq!(val.real_part().as_ref(), &-5.257306234905137);
2387            assert_eq!(val.imag_part().as_ref(), &7.212119776268775);
2388
2389            // Check determinism
2390            let mut rng2 = StdRng::from_seed(SEED);
2391            let val2 = ComplexValidated::sample_from(&dist, &mut rng2);
2392            assert_eq!(val, val2);
2393        }
2394
2395        #[test]
2396        fn new_random_vec_real() {
2397            let mut rng = StdRng::from_seed(SEED);
2398            let dist = Uniform::new(-10.0, 10.0).unwrap();
2399            let vec: Vec<RealValidated> = new_random_vec(3, &dist, &mut rng);
2400            assert_eq!(vec.len(), 3);
2401            assert_eq!(vec[0].as_ref(), &-5.257306234905137);
2402            assert_eq!(vec[1].as_ref(), &7.212119776268775);
2403            assert_eq!(vec[2].as_ref(), &-4.666248990558111);
2404
2405            // Check determinism
2406            let mut rng2 = StdRng::from_seed(SEED);
2407            let vec2: Vec<RealValidated> = new_random_vec(3, &dist, &mut rng2);
2408            assert_eq!(vec, vec2);
2409        }
2410
2411        #[test]
2412        fn new_random_vec_complex() {
2413            let mut rng = StdRng::from_seed(SEED);
2414            let dist = Uniform::new(-10.0, 10.0).unwrap();
2415            let vec: Vec<ComplexValidated> = new_random_vec(3, &dist, &mut rng);
2416            assert_eq!(vec.len(), 3);
2417            assert_eq!(vec[0].real_part().as_ref(), &-5.257306234905137);
2418            assert_eq!(vec[0].imag_part().as_ref(), &7.212119776268775);
2419            assert_eq!(vec[1].real_part().as_ref(), &-4.666248990558111);
2420            assert_eq!(vec[1].imag_part().as_ref(), &9.66047141517383);
2421            assert_eq!(vec[2].real_part().as_ref(), &-9.04279551029691);
2422            assert_eq!(vec[2].imag_part().as_ref(), &-1.026624649331671);
2423
2424            // Check determinism
2425            let mut rng2 = StdRng::from_seed(SEED);
2426            let vec2: Vec<ComplexValidated> = new_random_vec(3, &dist, &mut rng2);
2427            assert_eq!(vec, vec2);
2428        }
2429    }
2430
2431    mod hash_map_key_usage {
2432        use super::*;
2433        use rug::Float;
2434        use std::collections::HashMap;
2435        use try_create::TryNew;
2436
2437        const PRECISION: u32 = 128;
2438        type RealRugValidated = RealRugStrictFinite<PRECISION>;
2439
2440        #[test]
2441        fn test_rug_as_hashmap_key() {
2442            let mut map = HashMap::new();
2443            let key1 = RealRugValidated::try_new(Float::with_val(PRECISION, 1.0)).unwrap();
2444            let key2 = RealRugValidated::try_new(Float::with_val(PRECISION, 2.5)).unwrap();
2445
2446            map.insert(key1.clone(), "one_rug");
2447            map.insert(key2.clone(), "two_point_five_rug");
2448
2449            assert_eq!(map.get(&key1), Some(&"one_rug"));
2450            assert_eq!(map.len(), 2);
2451
2452            // Overwrite an existing key
2453            let old_value = map.insert(key1.clone(), "new_one_rug");
2454            assert_eq!(old_value, Some("one_rug"));
2455            assert_eq!(map.get(&key1), Some(&"new_one_rug"));
2456        }
2457
2458        #[test]
2459        fn test_hash_signed_zero() {
2460            use crate::functions::Sign;
2461            use std::collections::hash_map::DefaultHasher;
2462            use std::hash::{Hash, Hasher};
2463
2464            let val1 = RealRugValidated::try_new(Float::with_val(PRECISION, 0.0)).unwrap();
2465            assert!(val1.kernel_is_sign_positive());
2466            let val2 = RealRugValidated::try_new(Float::with_val(PRECISION, -0.0)).unwrap();
2467            assert!(val2.kernel_is_sign_negative());
2468
2469            // Equal values should have equal hashes
2470            let mut hasher1 = DefaultHasher::new();
2471            let mut hasher2 = DefaultHasher::new();
2472
2473            val1.hash(&mut hasher1);
2474            val2.hash(&mut hasher2);
2475
2476            assert_eq!(hasher1.finish(), hasher2.finish());
2477            assert_eq!(val1, val2); // Verify they're actually equal
2478        }
2479
2480        #[test]
2481        fn test_complex_as_hashmap_key() {
2482            use crate::ComplexRugStrictFinite;
2483            type ComplexRugValidated = ComplexRugStrictFinite<PRECISION>;
2484
2485            let mut map = HashMap::new();
2486            let key1 = ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (1.0, 2.0)))
2487                .unwrap();
2488            let key2 = ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (3.0, 4.0)))
2489                .unwrap();
2490
2491            map.insert(key1.clone(), "one_plus_two_i_rug");
2492            map.insert(key2.clone(), "three_plus_four_i_rug");
2493
2494            assert_eq!(map.get(&key1), Some(&"one_plus_two_i_rug"));
2495            assert_eq!(map.len(), 2);
2496
2497            // Overwrite an existing key
2498            let old_value = map.insert(key1.clone(), "updated_complex_rug");
2499            assert_eq!(old_value, Some("one_plus_two_i_rug"));
2500            assert_eq!(map.get(&key1), Some(&"updated_complex_rug"));
2501        }
2502
2503        #[test]
2504        fn test_complex_hash_consistency() {
2505            use crate::ComplexRugStrictFinite;
2506            use std::collections::hash_map::DefaultHasher;
2507            use std::hash::{Hash, Hasher};
2508            type ComplexRugValidated = ComplexRugStrictFinite<PRECISION>;
2509
2510            let val1 =
2511                ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (1.234, 5.678)))
2512                    .unwrap();
2513            let val2 =
2514                ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (1.234, 5.678)))
2515                    .unwrap();
2516
2517            // Equal values should have equal hashes
2518            let mut hasher1 = DefaultHasher::new();
2519            let mut hasher2 = DefaultHasher::new();
2520
2521            val1.hash(&mut hasher1);
2522            val2.hash(&mut hasher2);
2523
2524            assert_eq!(hasher1.finish(), hasher2.finish());
2525            assert_eq!(val1, val2);
2526        }
2527
2528        #[test]
2529        fn test_complex_hash_signed_zero() {
2530            use crate::ComplexRugStrictFinite;
2531            use std::collections::hash_map::DefaultHasher;
2532            use std::hash::{Hash, Hasher};
2533            type ComplexRugValidated = ComplexRugStrictFinite<PRECISION>;
2534
2535            // Test all combinations of signed zeros
2536            let val1 = ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (0.0, 0.0)))
2537                .unwrap();
2538            let val2 = ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (-0.0, 0.0)))
2539                .unwrap();
2540            let val3 = ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (0.0, -0.0)))
2541                .unwrap();
2542            let val4 =
2543                ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (-0.0, -0.0)))
2544                    .unwrap();
2545
2546            // All should be equal
2547            assert_eq!(val1, val2);
2548            assert_eq!(val1, val3);
2549            assert_eq!(val1, val4);
2550
2551            // All should have the same hash
2552            let mut hasher1 = DefaultHasher::new();
2553            let mut hasher2 = DefaultHasher::new();
2554            let mut hasher3 = DefaultHasher::new();
2555            let mut hasher4 = DefaultHasher::new();
2556
2557            val1.hash(&mut hasher1);
2558            val2.hash(&mut hasher2);
2559            val3.hash(&mut hasher3);
2560            val4.hash(&mut hasher4);
2561
2562            let hash1 = hasher1.finish();
2563            let hash2 = hasher2.finish();
2564            let hash3 = hasher3.finish();
2565            let hash4 = hasher4.finish();
2566
2567            assert_eq!(hash1, hash2);
2568            assert_eq!(hash1, hash3);
2569            assert_eq!(hash1, hash4);
2570        }
2571
2572        #[test]
2573        fn test_complex_hashset_operations() {
2574            use crate::ComplexRugStrictFinite;
2575            use std::collections::HashSet;
2576            type ComplexRugValidated = ComplexRugStrictFinite<PRECISION>;
2577
2578            let mut set = HashSet::new();
2579
2580            let val1 = ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (1.0, 2.0)))
2581                .unwrap();
2582            let val2 = ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (3.0, 4.0)))
2583                .unwrap();
2584            let val1_duplicate =
2585                ComplexRugValidated::try_new(rug::Complex::with_val(PRECISION, (1.0, 2.0)))
2586                    .unwrap();
2587
2588            assert!(set.insert(val1.clone()));
2589            assert!(set.insert(val2.clone()));
2590            assert!(!set.insert(val1_duplicate)); // Should return false (already exists)
2591
2592            assert_eq!(set.len(), 2);
2593            assert!(set.contains(&val1));
2594        }
2595
2596        #[test]
2597        fn test_complex_different_precision_different_hash() {
2598            use crate::ComplexRugStrictFinite;
2599            use std::collections::hash_map::DefaultHasher;
2600            use std::hash::{Hash, Hasher};
2601
2602            const PRECISION_A: u32 = 64;
2603            const PRECISION_B: u32 = 128;
2604
2605            let val1 = ComplexRugStrictFinite::<PRECISION_A>::try_new(rug::Complex::with_val(
2606                PRECISION_A,
2607                (1.0, 2.0),
2608            ))
2609            .unwrap();
2610            let val2 = ComplexRugStrictFinite::<PRECISION_B>::try_new(rug::Complex::with_val(
2611                PRECISION_B,
2612                (1.0, 2.0),
2613            ))
2614            .unwrap();
2615
2616            // Values with different precision should have different hashes
2617            let mut hasher1 = DefaultHasher::new();
2618            let mut hasher2 = DefaultHasher::new();
2619
2620            val1.hash(&mut hasher1);
2621            val2.hash(&mut hasher2);
2622
2623            let hash1 = hasher1.finish();
2624            let hash2 = hasher2.finish();
2625
2626            // Different precisions should produce different hashes
2627            assert_ne!(hash1, hash2);
2628        }
2629    }
2630
2631    mod rug_float {
2632        use super::*;
2633
2634        mod from_f64 {
2635            use super::*;
2636            use crate::{RealRugStrictFinite, RealScalar};
2637
2638            const PRECISION: u32 = 100;
2639
2640            #[test]
2641            fn test_from_f64_valid_constants() {
2642                // Test with mathematical constants (known valid values)
2643                let pi = RealRugStrictFinite::<PRECISION>::from_f64(std::f64::consts::PI);
2644                let pi_f64 = pi.as_ref().to_f64();
2645                assert!((pi_f64 - std::f64::consts::PI).abs() < 1e-15);
2646
2647                let e = RealRugStrictFinite::<PRECISION>::from_f64(std::f64::consts::E);
2648                let e_f64 = e.as_ref().to_f64();
2649                assert!((e_f64 - std::f64::consts::E).abs() < 1e-15);
2650            }
2651
2652            #[test]
2653            fn test_from_f64_valid_simple_values() {
2654                // Test with simple values that are exactly representable
2655                let x = RealRugStrictFinite::<PRECISION>::from_f64(42.0);
2656                assert_eq!(x.as_ref().to_f64(), 42.0);
2657
2658                let y = RealRugStrictFinite::<PRECISION>::from_f64(-3.0);
2659                assert_eq!(y.as_ref().to_f64(), -3.0);
2660
2661                let z = RealRugStrictFinite::<PRECISION>::from_f64(0.0);
2662                assert_eq!(z.as_ref().to_f64(), 0.0);
2663            }
2664
2665            #[test]
2666            #[should_panic(expected = "RealScalar::from_f64() failed")]
2667            fn test_from_f64_nan_panics() {
2668                let _ = RealRugStrictFinite::<PRECISION>::from_f64(f64::NAN);
2669            }
2670
2671            #[test]
2672            #[should_panic(expected = "RealScalar::from_f64() failed")]
2673            fn test_from_f64_infinity_panics() {
2674                let _ = RealRugStrictFinite::<PRECISION>::from_f64(f64::INFINITY);
2675            }
2676
2677            #[test]
2678            #[should_panic(expected = "RealScalar::from_f64() failed")]
2679            fn test_from_f64_neg_infinity_panics() {
2680                let _ = RealRugStrictFinite::<PRECISION>::from_f64(f64::NEG_INFINITY);
2681            }
2682
2683            #[test]
2684            fn test_try_from_f64_exact_representation() {
2685                // PRECISION = 100 bits (> 53, so all f64 values are exactly representable)
2686
2687                // All finite f64 values should be accepted
2688                assert!(
2689                    RealRugStrictFinite::<PRECISION>::try_from_f64(0.1).is_ok(),
2690                    "0.1 (as f64 approximation) should be accepted at 100-bit precision"
2691                );
2692                assert!(
2693                    RealRugStrictFinite::<PRECISION>::try_from_f64(0.3).is_ok(),
2694                    "0.3 (as f64 approximation) should be accepted at 100-bit precision"
2695                );
2696                assert!(RealRugStrictFinite::<PRECISION>::try_from_f64(0.5).is_ok());
2697                assert!(RealRugStrictFinite::<PRECISION>::try_from_f64(2.5).is_ok());
2698                assert!(RealRugStrictFinite::<PRECISION>::try_from_f64(0.0).is_ok());
2699
2700                // Invalid values (NaN, infinity) should be rejected
2701                assert!(RealRugStrictFinite::<PRECISION>::try_from_f64(f64::NAN).is_err());
2702                assert!(RealRugStrictFinite::<PRECISION>::try_from_f64(f64::INFINITY).is_err());
2703
2704                // `1.0 + f64::EPSILON` uses the least significant f64 mantissa bit,
2705                // so it requires the full 53-bit f64 precision and must fail at 52 bits.
2706                match RealRugStrictFinite::<52>::try_from_f64(1.0 + f64::EPSILON) {
2707                    Err(RawRealConversionError::NonRepresentableExactly {
2708                        precision_out, ..
2709                    }) => {
2710                        assert_eq!(precision_out, 52, "Error should report precision 52");
2711                    }
2712                    _ => panic!("Should reject precision < 53"),
2713                }
2714            }
2715        }
2716
2717        mod truncate_to_usize {
2718            use super::*;
2719            use rug::Float;
2720
2721            const PRECISION: u32 = 128;
2722
2723            #[test]
2724            fn test_rug_truncate_to_usize_valid() {
2725                assert_eq!(
2726                    Float::with_val(PRECISION, 42.0)
2727                        .truncate_to_usize()
2728                        .unwrap(),
2729                    42
2730                );
2731                assert_eq!(
2732                    Float::with_val(PRECISION, 42.9)
2733                        .truncate_to_usize()
2734                        .unwrap(),
2735                    42
2736                );
2737                assert_eq!(
2738                    Float::with_val(PRECISION, 0.0).truncate_to_usize().unwrap(),
2739                    0
2740                );
2741                assert_eq!(
2742                    Float::with_val(PRECISION, usize::MAX)
2743                        .truncate_to_usize()
2744                        .unwrap(),
2745                    usize::MAX
2746                );
2747            }
2748
2749            #[test]
2750            fn test_rug_truncate_to_usize_not_finite() {
2751                assert!(matches!(
2752                    Float::with_val(PRECISION, f64::NAN).truncate_to_usize(),
2753                    Err(ErrorsRawRealToInteger::NotFinite { .. })
2754                ));
2755                assert!(matches!(
2756                    Float::with_val(PRECISION, f64::INFINITY).truncate_to_usize(),
2757                    Err(ErrorsRawRealToInteger::NotFinite { .. })
2758                ));
2759                assert!(matches!(
2760                    Float::with_val(PRECISION, f64::NEG_INFINITY).truncate_to_usize(),
2761                    Err(ErrorsRawRealToInteger::NotFinite { .. })
2762                ));
2763            }
2764
2765            #[test]
2766            fn test_rug_truncate_to_usize_out_of_range() {
2767                // Negative value
2768                assert!(matches!(
2769                    Float::with_val(PRECISION, -1.0).truncate_to_usize(),
2770                    Err(ErrorsRawRealToInteger::OutOfRange { .. })
2771                ));
2772
2773                // Value too large
2774                let mut too_large = Float::with_val(PRECISION, usize::MAX);
2775                too_large += 1;
2776                assert!(matches!(
2777                    too_large.truncate_to_usize(),
2778                    Err(ErrorsRawRealToInteger::OutOfRange { .. })
2779                ));
2780            }
2781        }
2782    }
2783
2784    mod truncate_to_usize {
2785        use super::*;
2786        use rug::Float;
2787        use try_create::TryNew;
2788
2789        const PRECISION: u32 = 1000;
2790
2791        #[test]
2792        fn test_positive_integers() {
2793            let value = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 42.0))
2794                .unwrap();
2795            assert_eq!(value.truncate_to_usize().unwrap(), 42);
2796
2797            let value =
2798                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1.0)).unwrap();
2799            assert_eq!(value.truncate_to_usize().unwrap(), 1);
2800        }
2801
2802        #[test]
2803        fn test_positive_fractionals_truncate() {
2804            let value = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 42.9))
2805                .unwrap();
2806            assert_eq!(value.truncate_to_usize().unwrap(), 42);
2807
2808            let value =
2809                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.9)).unwrap();
2810            assert_eq!(value.truncate_to_usize().unwrap(), 0);
2811        }
2812
2813        #[test]
2814        fn test_zero_cases() {
2815            let value =
2816                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.0)).unwrap();
2817            assert_eq!(value.truncate_to_usize().unwrap(), 0);
2818        }
2819
2820        #[test]
2821        fn test_negative_values_error() {
2822            let value = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -1.0))
2823                .unwrap();
2824            let result = value.truncate_to_usize();
2825            assert!(matches!(
2826                result,
2827                Err(ErrorsRawRealToInteger::OutOfRange { .. })
2828            ));
2829
2830            let value =
2831                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -10.5))
2832                    .unwrap();
2833            let result = value.truncate_to_usize();
2834            assert!(matches!(
2835                result,
2836                Err(ErrorsRawRealToInteger::OutOfRange { .. })
2837            ));
2838        }
2839
2840        #[test]
2841        fn test_large_values() {
2842            // Test with large but valid values
2843            let value =
2844                RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1_000_000.0))
2845                    .unwrap();
2846            assert_eq!(value.truncate_to_usize().unwrap(), 1_000_000);
2847
2848            // Test with values too large for usize
2849            let value = RealRugStrictFinite::<PRECISION>::try_new(
2850                Float::with_val(PRECISION, 1e50), // Much larger than any usize
2851            )
2852            .unwrap();
2853            let result = value.truncate_to_usize();
2854            assert!(matches!(
2855                result,
2856                Err(ErrorsRawRealToInteger::OutOfRange { .. })
2857            ));
2858        }
2859
2860        #[test]
2861        fn test_high_precision_truncation() {
2862            // Test truncation with high precision values
2863            let value = RealRugStrictFinite::<PRECISION>::try_new(
2864                Float::parse("42.99999999999999999999999999999")
2865                    .unwrap()
2866                    .complete(PRECISION),
2867            )
2868            .unwrap();
2869            assert_eq!(value.truncate_to_usize().unwrap(), 42);
2870
2871            // Very small positive value should truncate to 0
2872            let value = RealRugStrictFinite::<PRECISION>::try_new(
2873                Float::parse("0.99999999999999999999999999999")
2874                    .unwrap()
2875                    .complete(PRECISION),
2876            )
2877            .unwrap();
2878            assert_eq!(value.truncate_to_usize().unwrap(), 0);
2879        }
2880
2881        #[test]
2882        fn test_conversion_from_f64() {
2883            // Test truncation after conversion from f64
2884            let value = RealRugStrictFinite::<PRECISION>::try_from_f64(42.7).unwrap();
2885            assert_eq!(value.truncate_to_usize().unwrap(), 42);
2886
2887            let value = RealRugStrictFinite::<PRECISION>::try_from_f64(0.5).unwrap();
2888            assert_eq!(value.truncate_to_usize().unwrap(), 0);
2889        }
2890    }
2891}