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