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