num_valid/kernels/
rug.rs

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