Skip to main content

num_valid/backends/rug/
raw.rs

1#![deny(rustdoc::broken_intra_doc_links)]
2
3//! # Arbitrary-Precision Floating-Point Raw Implementations
4//!
5//! This module provides raw trait implementations for the [`rug`](https://docs.rs/rug/) backend,
6//! enabling arbitrary-precision floating-point arithmetic with configurable precision.
7//!
8//! ## Purpose and Role
9//!
10//! The primary role of this module is to implement the core raw traits
11//! ([`RawScalarTrait`](crate::core::traits::raw::RawScalarTrait),
12//! [`RawRealTrait`](crate::core::traits::raw::RawRealTrait), etc.) for [`rug::Float`]
13//! and [`rug::Complex`]. These implementations provide the computational foundations
14//! that validated wrappers build upon.
15//!
16//! ## Precision Model
17//!
18//! Unlike the native `f64` backend with fixed 53-bit precision, the rug backend uses
19//! **const generic precision**:
20//!
21//! - Precision is specified in bits (e.g., 100, 200, 500)
22//! - All operations preserve precision of the operands
23//! - Precision mismatches are detected during validation
24//!
25//! ## MPFR Constant Optimization
26//!
27//! For mathematical constants, this module uses MPFR's precomputed constants
28//! when available, providing significant performance improvements:
29//!
30//! - [`rug::float::Constant::Pi`] - ~10x faster than computing `acos(-1)`
31//! - [`rug::float::Constant::Log2`] - ~10x faster than computing `ln(2)`
32//!
33//! ## Memory and Performance Characteristics
34//!
35//! - `rug::Float` and `rug::Complex` are heap-allocated, non-`Copy` types
36//! - All operations support reference-based variants to minimize cloning
37//! - Memory usage scales with precision (approximately `precision/8` bytes per value)
38
39use crate::{
40    IntegerScalar, ScalarCore,
41    core::{
42        errors::{
43            ErrorsRawRealToInteger, ErrorsValidationRawComplex, ErrorsValidationRawReal,
44            capture_backtrace,
45        },
46        traits::{
47            raw::{
48                RawComplexTrait, RawRealTrait, RawScalarHyperbolic, RawScalarPow, RawScalarTrait,
49                RawScalarTrigonometric, RawScalarValidate,
50            },
51            validation::FpChecks,
52        },
53    },
54    functions::{Conjugate, NegAssign, Rounding, Sign},
55    kernels::PrecisionBits,
56};
57use duplicate::duplicate_item;
58use rug::{
59    float::Constant as MpfrConstant,
60    ops::{CompleteRound, Pow},
61};
62use std::{
63    cmp::Ordering,
64    hash::{Hash, Hasher},
65    num::FpCategory,
66    ops::Neg,
67};
68
69#[duplicate_item(
70    T;
71    [rug::Float];
72    [rug::Complex];
73)]
74impl RawScalarTrigonometric for T {
75    #[duplicate_item(
76        unchecked_method method;
77        [unchecked_sin]  [sin];
78        [unchecked_asin] [asin];
79        [unchecked_cos]  [cos];
80        [unchecked_acos] [acos];
81        [unchecked_tan]  [tan];
82        [unchecked_atan] [atan];)]
83    #[inline(always)]
84    fn unchecked_method(self) -> Self {
85        T::method(self)
86    }
87}
88
89#[duplicate_item(
90    T;
91    [rug::Float];
92    [rug::Complex];
93)]
94impl RawScalarHyperbolic for T {
95    #[duplicate_item(
96        unchecked_method  method;
97        [unchecked_sinh]  [sinh];
98        [unchecked_asinh] [asinh];
99        [unchecked_cosh]  [cosh];
100        [unchecked_acosh] [acosh];
101        [unchecked_tanh]  [tanh];
102        [unchecked_atanh] [atanh];)]
103    #[inline(always)]
104    fn unchecked_method(self) -> Self {
105        T::method(self)
106    }
107}
108
109#[duplicate_item(
110    T;
111    [rug::Float];
112    [rug::Complex];
113)]
114impl RawScalarPow for T {
115    #[duplicate_item(
116        unchecked_method               exponent_type;
117        [unchecked_pow_exponent_i8]    [i8];
118        [unchecked_pow_exponent_i16]   [i16];
119        [unchecked_pow_exponent_i32]   [i32];
120        [unchecked_pow_exponent_i64]   [i64];
121        [unchecked_pow_exponent_i128]  [i128];
122        [unchecked_pow_exponent_isize] [isize];
123        [unchecked_pow_exponent_u8]    [u8];
124        [unchecked_pow_exponent_u16]   [u16];
125        [unchecked_pow_exponent_u32]   [u32];
126        [unchecked_pow_exponent_u64]   [u64];
127        [unchecked_pow_exponent_u128]  [u128];
128        [unchecked_pow_exponent_usize] [usize];
129    )]
130    #[inline(always)]
131    fn unchecked_method(self, exponent: &exponent_type) -> Self {
132        T::pow(self, exponent)
133    }
134}
135
136impl RawScalarValidate for rug::Float {
137    type ValidationError = ErrorsValidationRawReal<rug::Float>;
138
139    #[inline(always)]
140    fn validate_raw_scalar_ref(&self) -> Result<(), Self::ValidationError> {
141        match self.raw_classify() {
142            FpCategory::Nan => Err(ErrorsValidationRawReal::<rug::Float>::IsNaN {
143                value: self.clone(),
144                backtrace: capture_backtrace(),
145            }),
146            FpCategory::Infinite => {
147                if self.is_sign_positive() {
148                    Err(ErrorsValidationRawReal::<rug::Float>::IsPosInfinity {
149                        backtrace: capture_backtrace(),
150                    })
151                } else {
152                    Err(ErrorsValidationRawReal::<rug::Float>::IsNegInfinity {
153                        backtrace: capture_backtrace(),
154                    })
155                }
156            }
157            FpCategory::Subnormal => {
158                unreachable!("rug::Float should not produce subnormal values");
159            }
160            FpCategory::Zero | FpCategory::Normal => {
161                // No action needed
162                Ok(())
163            }
164        }
165    }
166}
167
168#[duplicate_item(
169    T;
170    [rug::Float];
171    [rug::Complex];
172)]
173impl ScalarCore for T {
174    type RawScalarType = Self;
175
176    fn as_raw_scalar_ref(&self) -> &Self::RawScalarType {
177        self
178    }
179
180    fn into_raw_scalar(self) -> Self::RawScalarType {
181        self
182    }
183}
184
185impl RawScalarTrait for rug::Float {
186    fn raw_zero(precision: u32) -> Self {
187        rug::Float::with_val(precision, 0.)
188    }
189
190    fn is_zero(&self) -> bool {
191        rug::Float::is_zero(self)
192    }
193
194    fn raw_one(precision: u32) -> Self {
195        rug::Float::with_val(precision, 1.)
196    }
197
198    #[duplicate_item(
199        unchecked_method       method;
200        [unchecked_reciprocal] [recip];
201        [unchecked_exp]        [exp];
202        [unchecked_sqrt]       [sqrt];
203        [unchecked_ln]         [ln];
204        [unchecked_log2]       [log2];
205        [unchecked_log10]      [log10];
206    )]
207    #[inline(always)]
208    fn unchecked_method(self) -> Self {
209        rug::Float::method(self)
210    }
211
212    /// Multiplies and adds in one fused operation, rounding to the nearest with only one rounding error.
213    ///
214    /// `a.mul_add(b, c)` produces a result like `a * &b + &c`.
215    #[inline(always)]
216    fn unchecked_mul_add(self, b: &Self, c: &Self) -> Self {
217        rug::Float::mul_add(self, b, c)
218    }
219
220    #[inline(always)]
221    fn compute_hash_raw_scalar<H: Hasher>(&self, state: &mut H) {
222        debug_assert!(
223            self.is_finite(),
224            "Hashing a non-finite rug::Float value (i.e., NaN or Infinity) may lead to inconsistent results."
225        );
226        // Always include precision in the hash
227        self.prec().hash(state);
228
229        // Handle signed zeros by normalizing to positive zero
230        if self.is_zero() {
231            // Ensure that -0.0 and 0.0 hash to the same value
232            rug::Float::with_val(self.prec(), 0.0)
233                .to_string_radix(10, None)
234                .hash(state);
235        } else {
236            // For non-zero values, create a deterministic string representation
237            // Use enough decimal places to capture the full precision
238            self.to_string_radix(10, None).hash(state)
239        }
240    }
241}
242
243impl PrecisionBits for rug::Float {
244    #[inline(always)]
245    fn precision_bits(&self) -> u32 {
246        self.prec()
247    }
248}
249
250impl RawRealTrait for rug::Float {
251    type RawComplex = rug::Complex;
252
253    #[inline(always)]
254    fn unchecked_abs(self) -> rug::Float {
255        rug::Float::abs(self)
256    }
257
258    #[inline(always)]
259    fn unchecked_atan2(self, denominator: &Self) -> Self {
260        rug::Float::atan2(self, denominator)
261    }
262
263    #[inline(always)]
264    fn unchecked_pow_exponent_real(self, exponent: &Self) -> Self {
265        rug::Float::pow(self, exponent)
266    }
267
268    #[inline(always)]
269    fn unchecked_hypot(self, other: &Self) -> Self {
270        rug::Float::hypot(self, other)
271    }
272
273    #[inline(always)]
274    fn unchecked_ln_1p(self) -> Self {
275        rug::Float::ln_1p(self)
276    }
277
278    #[inline(always)]
279    fn unchecked_exp_m1(self) -> Self {
280        rug::Float::exp_m1(self)
281    }
282
283    /// Multiplies two pairs and adds them in one fused operation, rounding to the nearest with only one rounding error.
284    /// `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.
285    #[inline(always)]
286    fn unchecked_mul_add_mul_mut(&mut self, mul: &Self, add_mul1: &Self, add_mul2: &Self) {
287        self.mul_add_mul_mut(mul, add_mul1, add_mul2);
288    }
289
290    /// Multiplies two pairs and subtracts them in one fused operation, rounding to the nearest with only one rounding error.
291    /// `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.
292    #[inline(always)]
293    fn unchecked_mul_sub_mul_mut(&mut self, mul: &Self, sub_mul1: &Self, sub_mul2: &Self) {
294        self.mul_sub_mul_mut(mul, sub_mul1, sub_mul2);
295    }
296
297    #[inline(always)]
298    fn raw_total_cmp(&self, other: &Self) -> Ordering {
299        rug::Float::total_cmp(self, other)
300    }
301
302    /// Clamps the value within the specified bounds.
303    #[inline(always)]
304    fn raw_clamp(self, min: &Self, max: &Self) -> Self {
305        rug::Float::clamp(self, min, max)
306    }
307
308    #[inline(always)]
309    fn raw_classify(&self) -> FpCategory {
310        rug::Float::classify(self)
311    }
312
313    #[inline(always)]
314    fn raw_two(precision: u32) -> Self {
315        rug::Float::with_val(precision, 2.)
316    }
317
318    #[inline(always)]
319    fn raw_one_div_2(precision: u32) -> Self {
320        rug::Float::with_val(precision, 0.5)
321    }
322
323    #[inline(always)]
324    fn raw_pi(precision: u32) -> Self {
325        // Use MPFR's optimized precomputed π constant (10x faster than acos(-1))
326        rug::Float::with_val(precision, MpfrConstant::Pi)
327    }
328
329    #[inline(always)]
330    fn raw_two_pi(precision: u32) -> Self {
331        rug::Float::with_val(precision, MpfrConstant::Pi) * 2
332    }
333
334    #[inline(always)]
335    fn raw_pi_div_2(precision: u32) -> Self {
336        rug::Float::with_val(precision, MpfrConstant::Pi) / 2
337    }
338
339    #[inline(always)]
340    fn raw_max_finite(precision: u32) -> Self {
341        // Create a Float with the desired precision
342        // let f = rug::Float::new(PRECISION);
343
344        // Fill the significand with all 1s: 1 - 2^(-precision)
345        // This gives you the largest significand before it rolls over
346        let one = rug::Float::with_val(precision, 1);
347        let eps = rug::Float::with_val(precision, rug::Float::u_pow_u(2, precision)).recip();
348        let significand = one - &eps;
349
350        // Scale it to the maximum exponent (subtract 1 to avoid overflow to infinity)
351        let max_exp = rug::float::exp_max() - 1;
352        //        println!("max_exp = {max_exp}");
353        significand * rug::Float::with_val(precision, rug::Float::u_pow_u(2, max_exp as u32))
354    }
355
356    #[inline(always)]
357    fn raw_min_finite(precision: u32) -> Self {
358        Self::raw_max_finite(precision).neg()
359    }
360
361    #[inline(always)]
362    fn raw_epsilon(precision: u32) -> Self {
363        rug::Float::u_pow_u(2, precision - 1)
364            .complete(precision)
365            .recip()
366    }
367
368    #[inline(always)]
369    fn raw_ln_2(precision: u32) -> Self {
370        // Use MPFR's optimized precomputed ln(2) constant
371        rug::Float::with_val(precision, MpfrConstant::Log2)
372    }
373
374    #[inline(always)]
375    fn raw_ln_10(precision: u32) -> Self {
376        // ln(10) = ln(2) * log₂(10)
377        // More efficient than computing ln(10) directly
378        let ln2 = rug::Float::with_val(precision, MpfrConstant::Log2);
379        let log2_10 = rug::Float::with_val(precision, 10).log2();
380        ln2 * log2_10
381    }
382
383    #[inline(always)]
384    fn raw_log10_2(precision: u32) -> Self {
385        rug::Float::with_val(precision, 2.).log10()
386    }
387
388    #[inline(always)]
389    fn raw_log2_10(precision: u32) -> Self {
390        rug::Float::with_val(precision, 10.).log2()
391    }
392
393    #[inline(always)]
394    fn raw_log2_e(precision: u32) -> Self {
395        // log₂(e) = 1/ln(2)
396        // More efficient than computing e then taking log₂
397        rug::Float::with_val(precision, MpfrConstant::Log2).recip()
398    }
399
400    #[inline(always)]
401    fn raw_log10_e(precision: u32) -> Self {
402        // log₁₀(e) = 1/ln(10) = 1/(ln(2) * log₂(10))
403        let ln2 = rug::Float::with_val(precision, MpfrConstant::Log2);
404        let log2_10 = rug::Float::with_val(precision, 10).log2();
405        (ln2 * log2_10).recip()
406    }
407
408    #[inline(always)]
409    fn raw_e(precision: u32) -> Self {
410        rug::Float::with_val(precision, 1.).exp()
411    }
412
413    #[inline(always)]
414    fn truncate_to_usize(self) -> Result<usize, ErrorsRawRealToInteger<rug::Float, usize>> {
415        if !self.is_finite() {
416            return Err(ErrorsRawRealToInteger::NotFinite {
417                value: self,
418                backtrace: capture_backtrace(),
419            });
420        }
421
422        let truncation = self.clone().trunc();
423
424        let out_of_range = || ErrorsRawRealToInteger::OutOfRange {
425            value: self,
426            min: usize::MIN,
427            max: usize::MAX,
428            backtrace: capture_backtrace(),
429        };
430
431        let truncation_as_rug_integer = truncation.to_integer().ok_or_else(out_of_range.clone())?;
432
433        truncation_as_rug_integer
434            .to_usize()
435            .ok_or_else(out_of_range)
436    }
437
438    fn unchecked_mul_assign_integer<I>(&mut self, integer: I)
439    where
440        I: IntegerScalar,
441    {
442        // rug::Integer has arbitrary precision, so every primitive integer converts exactly.
443        let as_rug_integer = match integer.to_i128() {
444            Some(value) => rug::Integer::from(value),
445            None => rug::Integer::from(
446                integer
447                    .to_u128()
448                    .expect("IntegerScalar values always fit in i128 or u128"),
449            ),
450        };
451        *self *= as_rug_integer;
452    }
453
454    fn unchecked_div_assign_integer<I>(&mut self, integer: I)
455    where
456        I: IntegerScalar,
457    {
458        // rug::Integer has arbitrary precision, so every primitive integer converts exactly.
459        let as_rug_integer = match integer.to_i128() {
460            Some(value) => rug::Integer::from(value),
461            None => rug::Integer::from(
462                integer
463                    .to_u128()
464                    .expect("IntegerScalar values always fit in i128 or u128"),
465            ),
466        };
467        *self /= as_rug_integer;
468    }
469}
470
471impl RawScalarValidate for rug::Complex {
472    type ValidationError = ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>;
473
474    fn validate_raw_scalar_ref(&self) -> Result<(), Self::ValidationError> {
475        let real_validation = self.real().validate_raw_scalar_ref();
476        let imag_validation = self.imag().validate_raw_scalar_ref();
477        match (real_validation, imag_validation) {
478            (Ok(()), Ok(())) => Ok(()),
479            (Ok(()), Err(imag_err)) => Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
480                source: Box::new(imag_err),
481            }),
482            (Err(real_err), Ok(())) => Err(ErrorsValidationRawComplex::InvalidRealPart {
483                source: Box::new(real_err),
484            }),
485            (Err(real_err), Err(imag_err)) => Err(ErrorsValidationRawComplex::InvalidBothParts {
486                real_error: Box::new(real_err),
487                imag_error: Box::new(imag_err),
488            }),
489        }
490    }
491}
492
493impl RawScalarTrait for rug::Complex {
494    fn raw_zero(precision: u32) -> Self {
495        rug::Complex::with_val(precision, (0., 0.))
496    }
497
498    fn is_zero(&self) -> bool {
499        rug::Complex::is_zero(self)
500    }
501
502    fn raw_one(precision: u32) -> Self {
503        rug::Complex::with_val(precision, (1., 0.))
504    }
505
506    #[duplicate_item(
507        unchecked_method       method;
508        [unchecked_reciprocal] [recip];
509        [unchecked_exp]        [exp];
510        [unchecked_sqrt]       [sqrt];
511        [unchecked_ln]         [ln];
512        [unchecked_log10]      [log10];
513    )]
514    #[inline(always)]
515    fn unchecked_method(self) -> Self {
516        rug::Complex::method(self)
517    }
518
519    #[inline(always)]
520    fn unchecked_log2(self) -> Self {
521        let ln_2 = rug::Float::with_val(self.real().prec(), 2.).ln();
522        rug::Complex::ln(self) / ln_2
523    }
524
525    /// Multiplies and adds in one fused operation, rounding to the nearest with only one rounding error.
526    ///
527    /// `a.mul_add(b, c)` produces a result like `a * &b + &c`.
528    #[inline(always)]
529    fn unchecked_mul_add(self, b: &Self, c: &Self) -> Self {
530        rug::Complex::mul_add(self, b, c)
531    }
532
533    fn compute_hash_raw_scalar<H: Hasher>(&self, state: &mut H) {
534        self.raw_real_part().compute_hash_raw_scalar(state);
535        self.raw_imag_part().compute_hash_raw_scalar(state);
536    }
537}
538
539impl Conjugate for rug::Complex {
540    #[inline(always)]
541    fn conjugate(self) -> Self {
542        rug::Complex::conj(self)
543    }
544}
545
546impl RawComplexTrait for rug::Complex {
547    type RawReal = rug::Float;
548
549    fn new_unchecked_raw_complex(real: rug::Float, imag: rug::Float) -> Self {
550        debug_assert_eq!(
551            real.prec(),
552            imag.prec(),
553            "Different precision between real and imaginary part!"
554        );
555        rug::Complex::with_val(real.prec(), (real, imag))
556    }
557
558    /// Returns a mutable reference to the real part of the complex number.
559    fn mut_raw_real_part(&mut self) -> &mut rug::Float {
560        self.mut_real()
561    }
562
563    /// Returns a mutable reference to the imaginary part of the complex number.
564    fn mut_raw_imag_part(&mut self) -> &mut rug::Float {
565        self.mut_imag()
566    }
567
568    #[inline(always)]
569    fn unchecked_abs(self) -> rug::Float {
570        rug::Complex::abs(self).into_real_imag().0
571    }
572
573    #[inline(always)]
574    fn raw_real_part(&self) -> &rug::Float {
575        self.real()
576    }
577
578    #[inline(always)]
579    fn raw_imag_part(&self) -> &rug::Float {
580        self.imag()
581    }
582
583    #[inline(always)]
584    fn unchecked_arg(self) -> rug::Float {
585        rug::Complex::arg(self).into_real_imag().0
586    }
587
588    #[inline(always)]
589    fn unchecked_pow_exponent_real(self, exponent: &rug::Float) -> Self {
590        rug::Complex::pow(self, exponent)
591    }
592}
593
594impl FpChecks for rug::Float {
595    fn is_finite(&self) -> bool {
596        rug::Float::is_finite(self)
597    }
598
599    fn is_infinite(&self) -> bool {
600        rug::Float::is_infinite(self)
601    }
602
603    fn is_nan(&self) -> bool {
604        rug::Float::is_nan(self)
605    }
606    fn is_normal(&self) -> bool {
607        rug::Float::is_normal(self)
608    }
609}
610
611impl FpChecks for rug::Complex {
612    /// Returns `true` if the real and imaginary parts of `self` are neither infinite nor NaN.
613    #[inline(always)]
614    fn is_finite(&self) -> bool {
615        self.real().is_finite() && self.imag().is_finite()
616    }
617
618    /// Returns `true` if the real or the imaginary part of `self` are positive infinity or negative infinity.
619    #[inline(always)]
620    fn is_infinite(&self) -> bool {
621        !self.is_nan() && (self.real().is_infinite() || self.imag().is_infinite())
622    }
623
624    /// Returns `true` if the real or the imaginary part of `self` are NaN.
625    #[inline(always)]
626    fn is_nan(&self) -> bool {
627        self.real().is_nan() || self.imag().is_nan()
628    }
629
630    /// Returns `true` if the real and the imaginary part of `self` are *normal* (i.e. neither zero, infinite, subnormal, or NaN).
631    #[inline(always)]
632    fn is_normal(&self) -> bool {
633        self.real().is_normal() && self.imag().is_normal()
634    }
635}
636
637//------------------------------------------------------------------------------------------------
638#[duplicate_item(
639    T;
640    [rug::Float];
641    [rug::Complex];
642)]
643/// Compound negation and assignment.
644impl NegAssign for T {
645    /// Performs the negation of `self`.
646    fn neg_assign(&mut self) {
647        <T as rug::ops::NegAssign>::neg_assign(self);
648    }
649}
650//------------------------------------------------------------------------------------------------
651
652impl Sign for rug::Float {
653    /// Returns a number with the magnitude of `self` and the sign of `sign`.
654    #[inline(always)]
655    fn kernel_copysign(self, sign: &Self) -> Self {
656        self.copysign(sign)
657    }
658
659    /// Returns `true` if the value is negative, −0 or NaN with a negative sign.
660    #[inline(always)]
661    fn kernel_is_sign_negative(&self) -> bool {
662        self.is_sign_negative()
663    }
664
665    /// Returns `true` if the value is positive, +0 or NaN with a positive sign.
666    #[inline(always)]
667    fn kernel_is_sign_positive(&self) -> bool {
668        self.is_sign_positive()
669    }
670
671    /// Returns the signum of the number.
672    ///
673    /// This method relies on `rug::Float::signum()`.
674    /// The behavior for special values is as follows:
675    /// - If the number is positive and not zero, returns `1.0`.
676    /// - If the number is negative and not zero, returns `-1.0`.
677    /// - If the number is zero, `rug::Float::signum()` returns `1.0` (representing positive zero).
678    #[inline(always)]
679    fn kernel_signum(self) -> Self {
680        self.signum()
681    }
682}
683
684impl Rounding for rug::Float {
685    /// Returns the smallest integer greater than or equal to `self`.
686    #[inline(always)]
687    fn kernel_ceil(self) -> Self {
688        self.ceil()
689    }
690
691    /// Returns the largest integer smaller than or equal to `self`.
692    #[inline(always)]
693    fn kernel_floor(self) -> Self {
694        self.floor()
695    }
696
697    /// Returns the fractional part of `self`.
698    #[inline(always)]
699    fn kernel_fract(self) -> Self {
700        self.fract()
701    }
702
703    /// Rounds `self` to the nearest integer, rounding half-way cases away from zero.
704    #[inline(always)]
705    fn kernel_round(self) -> Self {
706        self.round()
707    }
708
709    /// Returns the nearest integer to a number. Rounds half-way cases to the number with an even least significant digit.
710    ///
711    /// This function always returns the precise result.
712    ///
713    /// # Examples
714    /// ```
715    /// use num_valid::{RealScalar, RealRugStrictFinite, functions::Rounding};
716    ///
717    /// const PRECISION: u32 = 100;
718    ///
719    /// let f = RealRugStrictFinite::<PRECISION>::try_from_f64(3.3).unwrap();
720    /// let g = RealRugStrictFinite::<PRECISION>::try_from_f64(-3.3).unwrap();
721    /// let h = RealRugStrictFinite::<PRECISION>::try_from_f64(3.5).unwrap();
722    /// let i = RealRugStrictFinite::<PRECISION>::try_from_f64(-4.5).unwrap();
723    ///
724    /// assert_eq!(f.kernel_round_ties_even(), RealRugStrictFinite::<PRECISION>::try_from_f64(3.).unwrap());
725    /// assert_eq!(g.kernel_round_ties_even(), RealRugStrictFinite::<PRECISION>::try_from_f64(-3.).unwrap());
726    /// assert_eq!(h.kernel_round_ties_even(), RealRugStrictFinite::<PRECISION>::try_from_f64(4.).unwrap());
727    /// assert_eq!(i.kernel_round_ties_even(), RealRugStrictFinite::<PRECISION>::try_from_f64(-4.).unwrap());
728    /// ```
729    #[inline(always)]
730    fn kernel_round_ties_even(self) -> Self {
731        self.round_even()
732    }
733
734    /// Returns the integer part of `self`. This means that non-integer numbers are always truncated towards zero.
735    ///    
736    /// # Examples
737    /// ```
738    /// use num_valid::{RealScalar, RealRugStrictFinite, functions::Rounding};
739    ///
740    /// const PRECISION: u32 = 100;
741    ///
742    /// let f = RealRugStrictFinite::<PRECISION>::try_from_f64(3.7).unwrap();
743    /// let g = RealRugStrictFinite::<PRECISION>::try_from_f64(3.).unwrap();
744    /// let h = RealRugStrictFinite::<PRECISION>::try_from_f64(-3.7).unwrap();
745    ///
746    /// assert_eq!(f.kernel_trunc(), RealRugStrictFinite::<PRECISION>::try_from_f64(3.).unwrap());
747    /// assert_eq!(g.kernel_trunc(), RealRugStrictFinite::<PRECISION>::try_from_f64(3.).unwrap());
748    /// assert_eq!(h.kernel_trunc(), RealRugStrictFinite::<PRECISION>::try_from_f64(-3.).unwrap());
749    /// ```
750    #[inline(always)]
751    fn kernel_trunc(self) -> Self {
752        self.trunc()
753    }
754}
755
756#[cfg(test)]
757mod tests {
758    use crate::kernels::RawRealTrait;
759
760    mod mul_assign_integer {
761        use super::*;
762
763        #[test]
764        fn multiplies_by_signed_integer() {
765            let mut real = rug::Float::with_val(100, 3.5);
766            real.unchecked_mul_assign_integer(-4_i32);
767            assert_eq!(real, rug::Float::with_val(100, -14));
768        }
769
770        #[test]
771        fn multiplies_by_unsigned_integer() {
772            let mut real = rug::Float::with_val(100, 3.5);
773            real.unchecked_mul_assign_integer(4_u16);
774            assert_eq!(real, rug::Float::with_val(100, 14));
775        }
776
777        #[test]
778        fn multiplies_by_i128_beyond_f64_precision() {
779            let mut real = rug::Float::with_val(200, 1);
780            real.unchecked_mul_assign_integer((1_i128 << 100) + 1);
781            assert_eq!(real, rug::Float::with_val(200, (1_i128 << 100) + 1));
782        }
783
784        #[test]
785        fn multiplies_by_u128_beyond_i128_range() {
786            let mut real = rug::Float::with_val(200, 1);
787            real.unchecked_mul_assign_integer(u128::MAX);
788            assert_eq!(
789                real,
790                rug::Float::with_val(200, rug::Integer::from(u128::MAX))
791            );
792        }
793    }
794
795    mod div_assign_integer {
796        use super::*;
797
798        #[test]
799        fn divides_by_signed_integer() {
800            let mut real = rug::Float::with_val(100, 14);
801            real.unchecked_div_assign_integer(-4_i32);
802            assert_eq!(real, rug::Float::with_val(100, -3.5));
803        }
804
805        #[test]
806        fn divides_by_unsigned_integer() {
807            let mut real = rug::Float::with_val(100, 14);
808            real.unchecked_div_assign_integer(4_u16);
809            assert_eq!(real, rug::Float::with_val(100, 3.5));
810        }
811
812        #[test]
813        fn divides_by_i128_beyond_f64_precision() {
814            let mut real = rug::Float::with_val(200, (1_i128 << 100) + 1);
815            real.unchecked_div_assign_integer((1_i128 << 100) + 1);
816            assert_eq!(real, rug::Float::with_val(200, 1));
817        }
818
819        #[test]
820        fn divides_by_u128_beyond_i128_range() {
821            let mut real = rug::Float::with_val(200, rug::Integer::from(u128::MAX));
822            real.unchecked_div_assign_integer(u128::MAX);
823            assert_eq!(real, rug::Float::with_val(200, 1));
824        }
825    }
826
827    mod mul_integer {
828        use super::*;
829
830        #[test]
831        fn returns_product_without_mutating_in_place() {
832            let real = rug::Float::with_val(100, 3.5);
833            assert_eq!(
834                real.unchecked_mul_integer(-4_i32),
835                rug::Float::with_val(100, -14)
836            );
837        }
838    }
839
840    mod div_integer {
841        use super::*;
842
843        #[test]
844        fn returns_quotient_without_mutating_in_place() {
845            let real = rug::Float::with_val(100, 14);
846            assert_eq!(
847                real.unchecked_div_integer(-4_i32),
848                rug::Float::with_val(100, -3.5)
849            );
850        }
851    }
852}