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