safe_bigmath/
integer.rs

1extern crate alloc;
2
3use core::{cmp::Ordering, fmt::Display, ops::*, str::FromStr};
4use num_bigint::{BigInt, BigUint, Sign};
5use num_integer::Integer;
6use num_traits::{One, Signed, ToPrimitive, Zero};
7use quoth::Parsable;
8
9#[cfg(test)]
10use alloc::format;
11
12use crate::parsing::ParsedSafeInt;
13
14/// Arbitrary-precision integer wrapper that exposes safe, non-panicking operations.
15///
16/// # Examples
17/// Create values from primitives and perform safe division (returns `Option` to avoid panics):
18/// ```
19/// use safe_bigmath::SafeInt;
20///
21/// let a = SafeInt::from(10);
22/// let b = SafeInt::from(3);
23/// assert_eq!((&a / &b).unwrap(), SafeInt::from(3));
24/// assert_eq!(&a + &b, SafeInt::from(13));
25/// assert_eq!(SafeInt::from(5) / SafeInt::from(0), None);
26/// ```
27#[derive(Clone, Debug, Eq, Ord, Hash, Default, PartialEq, PartialOrd)]
28#[repr(transparent)]
29pub struct SafeInt(BigInt);
30
31/// Default iteration cap for the fixed-point approximation used by `pow_ratio_scaled` when
32/// large exponents require the fallback path.
33pub const DEFAULT_MAX_ITERS: usize = 4_096;
34const MAX_EXACT_EXPONENT: u32 = 1_024;
35
36impl FromStr for SafeInt {
37    type Err = quoth::Error;
38
39    fn from_str(s: &str) -> Result<Self, Self::Err> {
40        let mut stream = quoth::ParseStream::from(s);
41        let parsed = ParsedSafeInt::parse(&mut stream)?;
42        Ok(parsed.value)
43    }
44}
45
46impl Display for SafeInt {
47    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
48        write!(f, "{}", self.0)
49    }
50}
51
52impl SafeInt {
53    /// Zero value.
54    pub fn zero() -> SafeInt {
55        SafeInt(BigInt::zero())
56    }
57    /// One value.
58    pub fn one() -> SafeInt {
59        SafeInt(BigInt::one())
60    }
61    /// Negative one value.
62    pub fn neg_one() -> SafeInt {
63        -SafeInt::one()
64    }
65    /// Constant one value as a compile-time byte representation.
66    pub const ONE: ConstSafeInt<2> = ConstSafeInt::from_bytes([0, 1]);
67    /// Constant negative one value as a compile-time byte representation.
68    pub const NEG_ONE: ConstSafeInt<2> = ConstSafeInt::from_bytes([1, 1]);
69
70    /// Returns the underlying `BigInt` reference.
71    #[inline(always)]
72    pub const fn raw(&self) -> &BigInt {
73        &self.0
74    }
75
76    /// Constructs a `SafeInt` from a raw `BigInt`.
77    #[inline(always)]
78    pub const fn from_raw(value: BigInt) -> SafeInt {
79        SafeInt(value)
80    }
81
82    /// Returns `true` if the value is negative.
83    #[inline(always)]
84    pub fn is_negative(&self) -> bool {
85        self.0.sign() == Sign::Minus
86    }
87
88    /// Returns `true` if the value is evenly divisible by 2.
89    #[inline(always)]
90    pub fn is_even(&self) -> bool {
91        self.0.is_even()
92    }
93
94    /// Returns `true` if the value is not evenly divisible by 2.
95    #[inline(always)]
96    pub fn is_odd(&self) -> bool {
97        self.0.is_odd()
98    }
99
100    /// Returns `true` if the value is exactly zero.
101    #[inline(always)]
102    pub fn is_zero(&self) -> bool {
103        self.0.is_zero()
104    }
105
106    /// Returns the absolute value.
107    #[inline(always)]
108    pub fn abs(self) -> SafeInt {
109        SafeInt(self.0.abs())
110    }
111
112    /// Raises the number to an unsigned integer power.
113    #[inline(always)]
114    pub fn pow(self, exp: u32) -> SafeInt {
115        SafeInt(self.0.pow(exp))
116    }
117
118    /// Computes quotient and remainder simultaneously.
119    /// Returns `None` if `other` is zero.
120    #[inline(always)]
121    pub fn div_rem(self, other: SafeInt) -> Option<(SafeInt, SafeInt)> {
122        if other.0.is_zero() {
123            None
124        } else {
125            let (div, rem) = self.0.div_rem(&other.0);
126            Some((SafeInt(div), SafeInt(rem)))
127        }
128    }
129
130    /// Converts to `u8` if the value fits.
131    #[inline(always)]
132    pub fn to_u8(&self) -> Option<u8> {
133        self.0.to_u8()
134    }
135
136    /// Converts to `u16` if the value fits.
137    #[inline(always)]
138    pub fn to_u16(&self) -> Option<u16> {
139        self.0.to_u16()
140    }
141
142    /// Converts to `u32` if the value fits.
143    #[inline(always)]
144    pub fn to_u32(&self) -> Option<u32> {
145        self.0.to_u32()
146    }
147
148    /// Converts to `u64` if the value fits.
149    #[inline(always)]
150    pub fn to_u64(&self) -> Option<u64> {
151        self.0.to_u64()
152    }
153
154    /// Converts to `u128` if the value fits.
155    #[inline(always)]
156    pub fn to_u128(&self) -> Option<u128> {
157        self.0.to_u128()
158    }
159
160    /// Converts to `i8` if the value fits.
161    #[inline(always)]
162    pub fn to_i8(&self) -> Option<i8> {
163        self.0.to_i8()
164    }
165
166    /// Converts to `i16` if the value fits.
167    #[inline(always)]
168    pub fn to_i16(&self) -> Option<i16> {
169        self.0.to_i16()
170    }
171
172    /// Converts to `i32` if the value fits.
173    #[inline(always)]
174    pub fn to_i32(&self) -> Option<i32> {
175        self.0.to_i32()
176    }
177
178    /// Converts to `i64` if the value fits.
179    #[inline(always)]
180    pub fn to_i64(&self) -> Option<i64> {
181        self.0.to_i64()
182    }
183
184    /// Converts to `i128` if the value fits.
185    #[inline(always)]
186    pub fn to_i128(&self) -> Option<i128> {
187        self.0.to_i128()
188    }
189
190    /// Converts to `usize` if the value fits.
191    #[inline(always)]
192    pub fn to_usize(&self) -> Option<usize> {
193        self.0.to_usize()
194    }
195
196    /// Converts to `isize` if the value fits.
197    #[inline(always)]
198    pub fn to_isize(&self) -> Option<isize> {
199        self.0.to_isize()
200    }
201
202    /// Performs integer ceiling division (`self / b`, rounded up).
203    #[inline(always)]
204    pub fn ceil_div(&self, b: SafeInt) -> Option<SafeInt> {
205        let one = SafeInt::from(1);
206        Some(((self - one.clone()) / b)? + one)
207    }
208
209    /// Computes `(base_numerator / base_denominator)^(exponent_numerator / exponent_denominator)`
210    /// scaled by the provided factor. Returns `None` if the base or exponent denominator is zero
211    /// or if the base is non-positive. Uses an exact integer path when the exponent fits in 32
212    /// bits and is below `MAX_EXACT_EXPONENT`, and falls back to a fixed-point approximation with
213    /// the requested precision otherwise.
214    /// The fallback uses `DEFAULT_MAX_ITERS` as its iteration cap and stops earlier once rounded
215    /// digits converge.
216    ///
217    /// # Examples
218    /// ```rust
219    /// use safe_bigmath::SafeInt;
220    ///
221    /// // (2 / 3) ^ (1 / 2) * 1000 (approx) => 816 when floored
222    /// let result = SafeInt::pow_ratio_scaled(
223    ///     &SafeInt::from(2),
224    ///     &SafeInt::from(3),
225    ///     &SafeInt::from(1),
226    ///     &SafeInt::from(2),
227    ///     0,
228    ///     &SafeInt::from(1_000),
229    /// )
230    /// .unwrap();
231    /// assert_eq!(result, SafeInt::from(816));
232    /// ```
233    pub fn pow_ratio_scaled(
234        base_numerator: &SafeInt,
235        base_denominator: &SafeInt,
236        exponent_numerator: &SafeInt,
237        exponent_denominator: &SafeInt,
238        precision: u32,
239        scale: &SafeInt,
240    ) -> Option<SafeInt> {
241        Self::pow_ratio_scaled_with_max_iters(
242            base_numerator,
243            base_denominator,
244            exponent_numerator,
245            exponent_denominator,
246            precision,
247            scale,
248            None,
249        )
250    }
251
252    /// Same as [`pow_ratio_scaled`] but allows specifying a maximum iteration cap for the
253    /// fixed-point approximation path. When `None`, `DEFAULT_MAX_ITERS` is used. The approximation
254    /// still exits early when rounded digits converge.
255    pub fn pow_ratio_scaled_with_max_iters(
256        base_numerator: &SafeInt,
257        base_denominator: &SafeInt,
258        exponent_numerator: &SafeInt,
259        exponent_denominator: &SafeInt,
260        precision: u32,
261        scale: &SafeInt,
262        max_iters: Option<usize>,
263    ) -> Option<SafeInt> {
264        if base_denominator.is_zero() || exponent_denominator.is_zero() {
265            return None;
266        }
267        if base_numerator.is_zero() {
268            return Some(SafeInt::zero());
269        }
270        if scale.is_negative() {
271            return None;
272        }
273        // Restrict to positive base
274        if base_numerator.is_negative() || base_denominator.is_negative() {
275            return None;
276        }
277
278        let base_num = base_numerator.0.to_biguint()?;
279        let base_den = base_denominator.0.to_biguint()?;
280        let mut exp_num = exponent_numerator.0.to_biguint()?;
281        let mut exp_den = exponent_denominator.0.to_biguint()?;
282
283        if exp_num.is_zero() {
284            return Some(scale.clone());
285        }
286
287        let g = gcd_biguint(exp_num.clone(), exp_den.clone());
288        if g > BigUint::one() {
289            exp_num /= g.clone();
290            exp_den /= g;
291        }
292
293        let scale_abs = scale.0.to_biguint()?;
294        let scale_bits = u32::try_from(scale_abs.bits()).unwrap_or(u32::MAX);
295
296        let exp_num_bits = exp_num.bits();
297        let exp_den_bits = exp_den.bits();
298        if exp_num_bits <= 32 && exp_den_bits <= 32 {
299            let exp_num_u32 = exp_num.to_u32()?;
300            let exp_den_u32 = exp_den.to_u32()?;
301            if exp_den_u32 == 0 {
302                return None;
303            }
304
305            if exp_num_u32 <= MAX_EXACT_EXPONENT {
306                let base_num_pow = base_num.pow(exp_num_u32);
307                let base_den_pow = base_den.pow(exp_num_u32);
308                let scale_pow = scale_abs.pow(exp_den_u32);
309
310                let target_num = base_num_pow * scale_pow;
311                let target_den = base_den_pow;
312
313                let root = nth_root_ratio_floor(&target_num, &target_den, exp_den_u32);
314                return Some(SafeInt(BigInt::from_biguint(Sign::Plus, root)));
315            }
316        }
317
318        // Fallback path for large exponents: approximate using fixed-point log/exp with guard bits.
319        // Allow arbitrarily high requested precision (callers can cap via `max_iters`); enforce
320        // only a reasonable floor to keep the series stable.
321        // Increase the minimum precision based on the magnitude of `scale` so that even when
322        // callers request coarse precision, we retain enough fractional bits to keep the final
323        // scaled integer accurate.
324        let requested_precision = precision.max(32).max(scale_bits.saturating_add(8));
325        let guard_bits: u32 = 24;
326        let internal_precision = requested_precision.saturating_add(guard_bits);
327        let default_max_iters = DEFAULT_MAX_ITERS.min(internal_precision as usize + 128);
328        let max_iters = max_iters.unwrap_or(default_max_iters).max(1);
329
330        let target_scale_uint = BigUint::one() << requested_precision;
331        let guard_factor_uint = BigUint::one() << guard_bits;
332        let internal_scale_uint = &target_scale_uint << guard_bits;
333
334        let target_scale = BigInt::from_biguint(Sign::Plus, target_scale_uint.clone());
335        let guard_factor = BigInt::from_biguint(Sign::Plus, guard_factor_uint.clone());
336        let internal_scale = BigInt::from_biguint(Sign::Plus, internal_scale_uint.clone());
337
338        let ln_half = ln1p_fixed(
339            &(-(&internal_scale >> 1usize)),
340            &internal_scale,
341            &guard_factor,
342            max_iters,
343        );
344        let ln_two = -ln_half;
345
346        // Compute ln(base_num) - ln(base_den) using normalized mantissas near 1.0 for better
347        // convergence, regardless of how small or large the ratio is.
348        let ln_num = ln_biguint(
349            &base_num,
350            internal_precision,
351            &internal_scale_uint,
352            &internal_scale,
353            &guard_factor,
354            &ln_two,
355            max_iters,
356        );
357        let ln_den = ln_biguint(
358            &base_den,
359            internal_precision,
360            &internal_scale_uint,
361            &internal_scale,
362            &guard_factor,
363            &ln_two,
364            max_iters,
365        );
366        let ln_base = ln_num - ln_den;
367
368        let ln_scaled = (ln_base * BigInt::from_biguint(Sign::Plus, exp_num))
369            .div_floor(&BigInt::from_biguint(Sign::Plus, exp_den));
370        let exp_fp = exp_fixed(&ln_scaled, &internal_scale, &guard_factor, max_iters);
371        let exp_requested = round_to_precision(&exp_fp, &guard_factor);
372        let result =
373            (exp_requested * BigInt::from_biguint(Sign::Plus, scale_abs)).div_floor(&target_scale);
374
375        Some(SafeInt(result))
376    }
377
378    /// Exponentiate base to exponent. Base can be large integer number betwen 0 and u64::MAX
379    /// Optimal exponent values are between 0.1 and 0.9
380    pub fn pow_bigint_base(
381        base: &SafeInt,
382        exponent_numerator: &SafeInt,
383        exponent_denominator: &SafeInt,
384        precision: u32,
385        scale: &SafeInt,
386    ) -> Option<SafeInt> {
387        Self::pow_bigint_base_scaled_with_max_iters(
388            base,
389            exponent_numerator,
390            exponent_denominator,
391            precision,
392            scale,
393            None,
394        )
395    }
396
397    fn pow_bigint_base_scaled_with_max_iters(
398        base: &SafeInt,
399        exponent_numerator: &SafeInt,
400        exponent_denominator: &SafeInt,
401        precision: u32,
402        scale: &SafeInt,
403        max_iters: Option<usize>,
404    ) -> Option<SafeInt> {
405        use num_bigint::{BigInt, BigUint, Sign};
406        use num_integer::Integer;
407        use num_traits::{One, ToPrimitive, Zero};
408
409        // Guard rails
410        if exponent_denominator.is_zero() {
411            return None;
412        }
413        if base.is_zero() {
414            // Keep the same semantics as pow_ratio_scaled_with_max_iters:
415            // 0^anything -> 0 (including 0^0).
416            return Some(SafeInt::zero());
417        }
418        if scale.is_negative() {
419            return None;
420        }
421        // Restrict to positive base
422        if base.is_negative() {
423            return None;
424        }
425
426        // base is an integer; convert to BigUint
427        let base_uint = base.0.to_biguint()?;
428
429        let mut exp_num = exponent_numerator.0.to_biguint()?;
430        let mut exp_den = exponent_denominator.0.to_biguint()?;
431
432        if exp_num.is_zero() {
433            // base^0 ~= 1, scaled by `scale`
434            return Some(scale.clone());
435        }
436
437        // Reduce exponent fraction exp_num/exp_den
438        let g = gcd_biguint(exp_num.clone(), exp_den.clone());
439        if g > BigUint::one() {
440            exp_num /= g.clone();
441            exp_den /= g;
442        }
443
444        let scale_abs = scale.0.to_biguint()?;
445        let scale_bits = u32::try_from(scale_abs.bits()).unwrap_or(u32::MAX);
446
447        // ---- Fast path: small rational exponent, exact pow/root on integers ----
448        let exp_num_bits = exp_num.bits();
449        let exp_den_bits = exp_den.bits();
450        if exp_num_bits <= 32 && exp_den_bits <= 32 {
451            let exp_num_u32 = exp_num.to_u32()?;
452            let exp_den_u32 = exp_den.to_u32()?;
453            if exp_den_u32 == 0 {
454                return None;
455            }
456
457            if exp_num_u32 <= MAX_EXACT_EXPONENT {
458                // base^(exp_num/exp_den) * scale
459                //
460                // Compute:
461                //   (base^exp_num * scale^exp_den)^(1/exp_den)
462                // via nth_root_ratio_floor, same as in ratio version but with
463                // denominator fixed to 1.
464                let base_pow = base_uint.pow(exp_num_u32);
465                let scale_pow = scale_abs.pow(exp_den_u32);
466
467                let target_num = base_pow * scale_pow;
468                let target_den = BigUint::one();
469
470                let root = nth_root_ratio_floor(&target_num, &target_den, exp_den_u32);
471                return Some(SafeInt(BigInt::from_biguint(Sign::Plus, root)));
472            }
473        }
474
475        // ---- Fallback path: fixed-point log/exp with guard bits ----
476
477        // Same heuristic as in pow_ratio_scaled_with_max_iters:
478        let requested_precision = precision.max(32).max(scale_bits.saturating_add(8));
479        let guard_bits: u32 = 24;
480        let internal_precision = requested_precision.saturating_add(guard_bits);
481        let default_max_iters = DEFAULT_MAX_ITERS.min(internal_precision as usize + 128);
482        let max_iters = max_iters.unwrap_or(default_max_iters).max(1);
483
484        let target_scale_uint = BigUint::one() << requested_precision;
485        let guard_factor_uint = BigUint::one() << guard_bits;
486        let internal_scale_uint = &target_scale_uint << guard_bits;
487
488        let target_scale = BigInt::from_biguint(Sign::Plus, target_scale_uint.clone());
489        let guard_factor = BigInt::from_biguint(Sign::Plus, guard_factor_uint.clone());
490        let internal_scale = BigInt::from_biguint(Sign::Plus, internal_scale_uint.clone());
491
492        // ln(2) via ln1p_fixed(-1/2)
493        let ln_half = ln1p_fixed(
494            &(-(&internal_scale >> 1usize)),
495            &internal_scale,
496            &guard_factor,
497            max_iters,
498        );
499        let ln_two = -ln_half;
500
501        // ln(base) using normalized mantissa (same helper as ratio version)
502        let ln_base = ln_biguint(
503            &base_uint,
504            internal_precision,
505            &internal_scale_uint,
506            &internal_scale,
507            &guard_factor,
508            &ln_two,
509            max_iters,
510        );
511
512        // ln(base) * exp_num / exp_den
513        let ln_scaled = (ln_base * BigInt::from_biguint(Sign::Plus, exp_num))
514            .div_floor(&BigInt::from_biguint(Sign::Plus, exp_den));
515
516        // exp(ln_scaled) in fixed-point
517        let exp_fp = exp_fixed(&ln_scaled, &internal_scale, &guard_factor, max_iters);
518
519        // Drop guard bits
520        let exp_requested = round_to_precision(&exp_fp, &guard_factor);
521
522        // Scale by `scale_abs` and rescale to integer
523        let result =
524            (exp_requested * BigInt::from_biguint(Sign::Plus, scale_abs)).div_floor(&target_scale);
525
526        Some(SafeInt(result))
527    }
528}
529
530fn gcd_biguint(mut a: BigUint, mut b: BigUint) -> BigUint {
531    while !b.is_zero() {
532        let r = &a % &b;
533        a = b;
534        b = r;
535    }
536    a
537}
538
539fn pow_biguint(base: &BigUint, exp: u32) -> BigUint {
540    base.pow(exp)
541}
542
543fn nth_root_ratio_floor(target_num: &BigUint, target_den: &BigUint, q: u32) -> BigUint {
544    if q == 0 {
545        return BigUint::zero();
546    }
547
548    let mut low = BigUint::zero();
549    let mut high = BigUint::one();
550    while pow_biguint(&high, q) * target_den <= *target_num {
551        high <<= 1;
552    }
553
554    while low < high {
555        let mid = (&low + &high + 1u32) >> 1;
556        if pow_biguint(&mid, q) * target_den <= *target_num {
557            low = mid;
558        } else {
559            high = mid - BigUint::one();
560        }
561    }
562
563    low
564}
565
566fn ln1p_fixed(x_fp: &BigInt, scale: &BigInt, guard_factor: &BigInt, max_iters: usize) -> BigInt {
567    // Fixed-point natural log using the Taylor series for ln(1 + x), stopping once the rounded
568    // value at the target precision stops changing or the incremental term is below guard bits.
569    let mut term = x_fp.clone();
570    let mut result = term.clone();
571    let mut prev_rounded = round_to_precision(&result, guard_factor);
572    for n in 2..=max_iters {
573        term = (&term * x_fp).div_floor(scale);
574        if term.is_zero() {
575            break;
576        }
577
578        let next = term.div_floor(&BigInt::from(n as u32));
579        if next.is_zero() {
580            break;
581        }
582
583        if n % 2 == 0 {
584            result -= &next;
585        } else {
586            result += &next;
587        }
588
589        let rounded = round_to_precision(&result, guard_factor);
590        if next.abs() < guard_factor.abs() || rounded == prev_rounded {
591            break;
592        }
593        prev_rounded = rounded;
594    }
595
596    result
597}
598
599fn exp_fixed(x_fp: &BigInt, scale: &BigInt, guard_factor: &BigInt, max_iters: usize) -> BigInt {
600    // Fixed-point exponential using the Taylor series for exp(x), stopping once rounded digits
601    // stabilize at the target precision or the incremental term is below guard bits.
602    let mut term = scale.clone(); // 1.0 in fixed-point space
603    let mut result = term.clone();
604    let mut prev_rounded = round_to_precision(&result, guard_factor);
605    for n in 1..=max_iters {
606        term = (&term * x_fp).div_floor(&(scale * BigInt::from(n as u32)));
607        if term.is_zero() {
608            break;
609        }
610        result += &term;
611
612        let rounded = round_to_precision(&result, guard_factor);
613        if term.abs() < guard_factor.abs() || rounded == prev_rounded {
614            break;
615        }
616        prev_rounded = rounded;
617    }
618
619    result
620}
621
622fn ln_biguint(
623    value: &BigUint,
624    internal_precision: u32,
625    internal_scale_uint: &BigUint,
626    internal_scale: &BigInt,
627    guard_factor: &BigInt,
628    ln_two: &BigInt,
629    max_iters: usize,
630) -> BigInt {
631    debug_assert!(!value.is_zero());
632    if value.is_zero() {
633        return BigInt::zero();
634    }
635
636    let int_prec = internal_precision as usize;
637    let mut shift = value.bits().saturating_sub(1);
638    let mut mantissa = value.clone() << int_prec;
639    mantissa >>= shift;
640
641    // Keep the mantissa close to 1.0 (in [0.5, 1.5)) for fast ln1p convergence.
642    let half_scale = internal_scale_uint >> 1;
643    let scale_plus_half = internal_scale_uint + &half_scale;
644    if mantissa >= scale_plus_half {
645        mantissa >>= 1;
646        shift = shift.saturating_add(1);
647    }
648
649    let mantissa_int = BigInt::from_biguint(Sign::Plus, mantissa);
650    let ln_mantissa = ln1p_fixed(
651        &(mantissa_int - internal_scale),
652        internal_scale,
653        guard_factor,
654        max_iters,
655    );
656
657    ln_mantissa + ln_two * BigInt::from(shift)
658}
659
660fn round_to_precision(value: &BigInt, guard_factor: &BigInt) -> BigInt {
661    let (mut truncated, remainder) = value.div_rem(guard_factor);
662    if !remainder.is_zero() && (remainder.abs() << 1) >= guard_factor.abs() {
663        truncated += remainder.signum();
664    }
665    truncated
666}
667
668impl Neg for SafeInt {
669    type Output = SafeInt;
670
671    #[inline(always)]
672    fn neg(self) -> SafeInt {
673        SafeInt(-self.0)
674    }
675}
676
677impl Neg for &SafeInt {
678    type Output = SafeInt;
679
680    #[inline(always)]
681    fn neg(self) -> SafeInt {
682        SafeInt(-self.0.clone())
683    }
684}
685
686macro_rules! impl_pair_ops {
687    ($trait:ident, $method:ident) => {
688        impl $trait for SafeInt {
689            type Output = SafeInt;
690
691            #[inline(always)]
692            fn $method(self, other: SafeInt) -> SafeInt {
693                SafeInt(self.0.$method(other.0))
694            }
695        }
696
697        impl $trait<&SafeInt> for SafeInt {
698            type Output = SafeInt;
699
700            #[inline(always)]
701            fn $method(self, other: &SafeInt) -> SafeInt {
702                SafeInt(self.0.$method(&other.0))
703            }
704        }
705
706        impl $trait<SafeInt> for &SafeInt {
707            type Output = SafeInt;
708
709            #[inline(always)]
710            fn $method(self, other: SafeInt) -> SafeInt {
711                SafeInt(self.0.clone().$method(other.0))
712            }
713        }
714
715        impl $trait<&SafeInt> for &SafeInt {
716            type Output = SafeInt;
717
718            #[inline(always)]
719            fn $method(self, other: &SafeInt) -> SafeInt {
720                SafeInt(self.0.clone().$method(&other.0))
721            }
722        }
723    };
724}
725
726macro_rules! impl_pair_rem_ops {
727    () => {
728        impl Rem for SafeInt {
729            type Output = Option<SafeInt>;
730
731            #[inline(always)]
732            fn rem(self, other: SafeInt) -> Option<SafeInt> {
733                if other.0.is_zero() {
734                    None
735                } else {
736                    Some(SafeInt(self.0 % other.0))
737                }
738            }
739        }
740
741        impl Rem<&SafeInt> for SafeInt {
742            type Output = Option<SafeInt>;
743
744            #[inline(always)]
745            fn rem(self, other: &SafeInt) -> Option<SafeInt> {
746                if other.0.is_zero() {
747                    None
748                } else {
749                    Some(SafeInt(self.0 % &other.0))
750                }
751            }
752        }
753
754        impl Rem<SafeInt> for &SafeInt {
755            type Output = Option<SafeInt>;
756
757            #[inline(always)]
758            fn rem(self, other: SafeInt) -> Option<SafeInt> {
759                if other.0.is_zero() {
760                    None
761                } else {
762                    Some(SafeInt(self.0.clone() % other.0))
763                }
764            }
765        }
766
767        impl Rem<&SafeInt> for &SafeInt {
768            type Output = Option<SafeInt>;
769
770            #[inline(always)]
771            fn rem(self, other: &SafeInt) -> Option<SafeInt> {
772                if other.0.is_zero() {
773                    None
774                } else {
775                    Some(SafeInt(self.0.clone() % &other.0))
776                }
777            }
778        }
779    };
780}
781
782impl_pair_ops!(Add, add);
783impl_pair_ops!(Sub, sub);
784impl_pair_ops!(Mul, mul);
785impl_pair_rem_ops!();
786impl_pair_ops!(BitAnd, bitand);
787impl_pair_ops!(BitOr, bitor);
788impl_pair_ops!(BitXor, bitxor);
789
790macro_rules! impl_prim_ops {
791    ($trait:ident, $method:ident, [$($t:ty),*]) => {
792        $(
793            impl $trait<$t> for SafeInt {
794                type Output = SafeInt;
795
796                #[inline(always)]
797                fn $method(self, other: $t) -> SafeInt {
798                    SafeInt(self.0.$method(BigInt::from(other)))
799                }
800            }
801
802            impl $trait<$t> for &SafeInt {
803                type Output = SafeInt;
804
805                #[inline(always)]
806                fn $method(self, other: $t) -> SafeInt {
807                    SafeInt(self.0.clone().$method(BigInt::from(other)))
808                }
809            }
810
811            impl $trait<SafeInt> for $t {
812                type Output = SafeInt;
813
814                #[inline(always)]
815                fn $method(self, other: SafeInt) -> SafeInt {
816                    SafeInt(BigInt::from(self).$method(other.0))
817                }
818            }
819
820            impl $trait<&SafeInt> for $t {
821                type Output = SafeInt;
822
823                #[inline(always)]
824                fn $method(self, other: &SafeInt) -> SafeInt {
825                    SafeInt(BigInt::from(self).$method(other.0.clone()))
826                }
827            }
828        )*
829    };
830}
831
832macro_rules! impl_prim_rem_ops {
833    ($($t:ty),*) => {
834        $(
835            impl Rem<$t> for SafeInt {
836                type Output = Option<SafeInt>;
837
838                #[inline(always)]
839                fn rem(self, other: $t) -> Option<SafeInt> {
840                    if other == 0 {
841                        None
842                    } else {
843                        Some(SafeInt(self.0 % BigInt::from(other)))
844                    }
845                }
846            }
847
848            impl Rem<$t> for &SafeInt {
849                type Output = Option<SafeInt>;
850
851                #[inline(always)]
852                fn rem(self, other: $t) -> Option<SafeInt> {
853                    if other == 0 {
854                        None
855                    } else {
856                        Some(SafeInt(self.0.clone() % BigInt::from(other)))
857                    }
858                }
859            }
860
861            impl Rem<SafeInt> for $t {
862                type Output = Option<SafeInt>;
863
864                #[inline(always)]
865                fn rem(self, other: SafeInt) -> Option<SafeInt> {
866                    if other.0.is_zero() {
867                        None
868                    } else {
869                        Some(SafeInt(BigInt::from(self) % other.0))
870                    }
871                }
872            }
873
874            impl Rem<&SafeInt> for $t {
875                type Output = Option<SafeInt>;
876
877                #[inline(always)]
878                fn rem(self, other: &SafeInt) -> Option<SafeInt> {
879                    if other.0.is_zero() {
880                        None
881                    } else {
882                        Some(SafeInt(BigInt::from(self) % other.0.clone()))
883                    }
884                }
885            }
886        )*
887    };
888}
889
890impl_prim_ops!(
891    Add,
892    add,
893    [
894        u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
895    ]
896);
897impl_prim_ops!(
898    Sub,
899    sub,
900    [
901        u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
902    ]
903);
904impl_prim_ops!(
905    Mul,
906    mul,
907    [
908        u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
909    ]
910);
911impl_prim_rem_ops!(
912    u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
913);
914impl_prim_ops!(
915    BitAnd,
916    bitand,
917    [
918        u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
919    ]
920);
921impl_prim_ops!(
922    BitOr,
923    bitor,
924    [
925        u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
926    ]
927);
928impl_prim_ops!(
929    BitXor,
930    bitxor,
931    [
932        u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
933    ]
934);
935
936impl AddAssign<SafeInt> for SafeInt {
937    #[inline(always)]
938    fn add_assign(&mut self, rhs: SafeInt) {
939        self.0 += rhs.0;
940    }
941}
942
943impl AddAssign<&SafeInt> for SafeInt {
944    #[inline(always)]
945    fn add_assign(&mut self, rhs: &SafeInt) {
946        self.0 += &rhs.0;
947    }
948}
949
950impl SubAssign<SafeInt> for SafeInt {
951    #[inline(always)]
952    fn sub_assign(&mut self, rhs: SafeInt) {
953        self.0 -= rhs.0;
954    }
955}
956
957impl SubAssign<&SafeInt> for SafeInt {
958    #[inline(always)]
959    fn sub_assign(&mut self, rhs: &SafeInt) {
960        self.0 -= &rhs.0;
961    }
962}
963
964impl MulAssign<SafeInt> for SafeInt {
965    #[inline(always)]
966    fn mul_assign(&mut self, rhs: SafeInt) {
967        self.0 *= rhs.0;
968    }
969}
970
971impl MulAssign<&SafeInt> for SafeInt {
972    #[inline(always)]
973    fn mul_assign(&mut self, rhs: &SafeInt) {
974        self.0 *= &rhs.0;
975    }
976}
977
978impl RemAssign<SafeInt> for SafeInt {
979    #[inline(always)]
980    fn rem_assign(&mut self, rhs: SafeInt) {
981        if !rhs.0.is_zero() {
982            self.0 %= rhs.0;
983        }
984    }
985}
986
987impl RemAssign<&SafeInt> for SafeInt {
988    #[inline(always)]
989    fn rem_assign(&mut self, rhs: &SafeInt) {
990        if !rhs.0.is_zero() {
991            self.0 %= &rhs.0;
992        }
993    }
994}
995
996impl BitAndAssign<SafeInt> for SafeInt {
997    #[inline(always)]
998    fn bitand_assign(&mut self, rhs: SafeInt) {
999        self.0 &= rhs.0;
1000    }
1001}
1002
1003impl BitAndAssign<&SafeInt> for SafeInt {
1004    #[inline(always)]
1005    fn bitand_assign(&mut self, rhs: &SafeInt) {
1006        self.0 &= &rhs.0;
1007    }
1008}
1009
1010impl BitOrAssign<SafeInt> for SafeInt {
1011    #[inline(always)]
1012    fn bitor_assign(&mut self, rhs: SafeInt) {
1013        self.0 |= rhs.0;
1014    }
1015}
1016
1017impl BitOrAssign<&SafeInt> for SafeInt {
1018    #[inline(always)]
1019    fn bitor_assign(&mut self, rhs: &SafeInt) {
1020        self.0 |= &rhs.0;
1021    }
1022}
1023
1024impl BitXorAssign<SafeInt> for SafeInt {
1025    #[inline(always)]
1026    fn bitxor_assign(&mut self, rhs: SafeInt) {
1027        self.0 ^= rhs.0;
1028    }
1029}
1030
1031impl BitXorAssign<&SafeInt> for SafeInt {
1032    #[inline(always)]
1033    fn bitxor_assign(&mut self, rhs: &SafeInt) {
1034        self.0 ^= &rhs.0;
1035    }
1036}
1037
1038macro_rules! impl_assign_prim {
1039    ($trait:ident, $method:ident, $op:tt, [$($t:ty),*]) => {
1040        $(
1041            impl $trait<$t> for SafeInt {
1042                #[inline(always)]
1043                fn $method(&mut self, rhs: $t) {
1044                    self.0 $op BigInt::from(rhs);
1045                }
1046            }
1047        )*
1048    };
1049}
1050
1051macro_rules! impl_rem_assign_prim {
1052    ($($t:ty),*) => {
1053        $(
1054            impl RemAssign<$t> for SafeInt {
1055                #[inline(always)]
1056                fn rem_assign(&mut self, rhs: $t) {
1057                    if rhs != 0 {
1058                        self.0 %= BigInt::from(rhs);
1059                    }
1060                }
1061            }
1062        )*
1063    };
1064}
1065
1066impl_assign_prim!(AddAssign, add_assign, +=, [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]);
1067impl_assign_prim!(SubAssign, sub_assign, -=, [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]);
1068impl_assign_prim!(MulAssign, mul_assign, *=, [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]);
1069impl_rem_assign_prim!(
1070    u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
1071);
1072impl_assign_prim!(BitAndAssign, bitand_assign, &=, [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]);
1073impl_assign_prim!(BitOrAssign, bitor_assign, |=, [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]);
1074impl_assign_prim!(BitXorAssign, bitxor_assign, ^=, [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]);
1075
1076impl Div for SafeInt {
1077    type Output = Option<SafeInt>;
1078
1079    #[inline(always)]
1080    fn div(self, other: SafeInt) -> Option<SafeInt> {
1081        if other.0.is_zero() {
1082            None
1083        } else {
1084            Some(SafeInt(self.0 / other.0))
1085        }
1086    }
1087}
1088
1089impl Div<&SafeInt> for SafeInt {
1090    type Output = Option<SafeInt>;
1091
1092    #[inline(always)]
1093    fn div(self, other: &SafeInt) -> Option<SafeInt> {
1094        if other.0.is_zero() {
1095            None
1096        } else {
1097            Some(SafeInt(self.0 / &other.0))
1098        }
1099    }
1100}
1101
1102impl Div<SafeInt> for &SafeInt {
1103    type Output = Option<SafeInt>;
1104
1105    #[inline(always)]
1106    fn div(self, other: SafeInt) -> Option<SafeInt> {
1107        if other.0.is_zero() {
1108            None
1109        } else {
1110            Some(SafeInt(self.0.clone() / other.0))
1111        }
1112    }
1113}
1114
1115impl Div<&SafeInt> for &SafeInt {
1116    type Output = Option<SafeInt>;
1117
1118    #[inline(always)]
1119    fn div(self, other: &SafeInt) -> Option<SafeInt> {
1120        if other.0.is_zero() {
1121            None
1122        } else {
1123            Some(SafeInt(self.0.clone() / &other.0))
1124        }
1125    }
1126}
1127
1128macro_rules! impl_div_safeint_rhs_prim {
1129    ($($t:ty),*) => {
1130        $(
1131            impl Div<$t> for SafeInt {
1132                type Output = Option<SafeInt>;
1133
1134                #[inline(always)]
1135                fn div(self, other: $t) -> Option<SafeInt> {
1136                    if other == 0 {
1137                        None
1138                    } else {
1139                        Some(SafeInt(self.0 / BigInt::from(other)))
1140                    }
1141                }
1142            }
1143
1144            impl Div<$t> for &SafeInt {
1145                type Output = Option<SafeInt>;
1146
1147                #[inline(always)]
1148                fn div(self, other: $t) -> Option<SafeInt> {
1149                    if other == 0 {
1150                        None
1151                    } else {
1152                        Some(SafeInt(self.0.clone() / BigInt::from(other)))
1153                    }
1154                }
1155            }
1156        )*
1157    };
1158}
1159
1160macro_rules! impl_div_prim_lhs_safeint {
1161    ($($t:ty),*) => {
1162        $(
1163            impl Div<SafeInt> for $t {
1164                type Output = Option<SafeInt>;
1165
1166                #[inline(always)]
1167                fn div(self, other: SafeInt) -> Option<SafeInt> {
1168                    if other.0.is_zero() {
1169                        None
1170                    } else {
1171                        Some(SafeInt(BigInt::from(self) / other.0))
1172                    }
1173                }
1174            }
1175
1176            impl Div<&SafeInt> for $t {
1177                type Output = Option<SafeInt>;
1178
1179                #[inline(always)]
1180                fn div(self, other: &SafeInt) -> Option<SafeInt> {
1181                    if other.0.is_zero() {
1182                        None
1183                    } else {
1184                        Some(SafeInt(BigInt::from(self) / other.0.clone()))
1185                    }
1186                }
1187            }
1188        )*
1189    };
1190}
1191
1192impl_div_safeint_rhs_prim!(
1193    u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
1194);
1195impl_div_prim_lhs_safeint!(
1196    u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
1197);
1198
1199impl<T: Into<BigInt>> From<T> for SafeInt {
1200    #[inline(always)]
1201    fn from(value: T) -> SafeInt {
1202        SafeInt(value.into())
1203    }
1204}
1205
1206impl<T> PartialEq<T> for SafeInt
1207where
1208    T: Copy,
1209    BigInt: From<T>,
1210{
1211    #[inline(always)]
1212    fn eq(&self, other: &T) -> bool {
1213        self.0 == BigInt::from(*other)
1214    }
1215}
1216
1217impl<T> PartialOrd<T> for SafeInt
1218where
1219    T: Copy,
1220    BigInt: From<T>,
1221{
1222    #[inline(always)]
1223    fn partial_cmp(&self, other: &T) -> Option<Ordering> {
1224        self.0.partial_cmp(&BigInt::from(*other))
1225    }
1226}
1227
1228macro_rules! impl_prim_cmp {
1229    ($($t:ty),*) => {
1230        $(
1231            impl PartialEq<SafeInt> for $t {
1232                #[inline(always)]
1233                fn eq(&self, other: &SafeInt) -> bool {
1234                    BigInt::from(*self) == other.0
1235                }
1236            }
1237
1238            impl PartialOrd<SafeInt> for $t {
1239                #[inline(always)]
1240                fn partial_cmp(&self, other: &SafeInt) -> Option<Ordering> {
1241                    BigInt::from(*self).partial_cmp(&other.0)
1242                }
1243            }
1244        )*
1245    };
1246}
1247
1248impl_prim_cmp!(
1249    u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
1250);
1251
1252/// Fixed-size, byte-backed integer that can be converted into `SafeInt`.
1253///
1254/// # Examples
1255/// ```
1256/// use safe_bigmath::integer::ConstSafeInt;
1257/// use safe_bigmath::SafeInt;
1258///
1259/// const ONE: ConstSafeInt<2> = ConstSafeInt::from_bytes([0, 1]);
1260/// assert_eq!(SafeInt::from(ONE), SafeInt::from(1));
1261/// ```
1262#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
1263#[repr(C)]
1264pub struct ConstSafeInt<const N: usize>([u8; N]);
1265
1266impl<const N: usize> ConstSafeInt<N> {
1267    /// Creates a constant from big-endian two's-complement bytes, where the first byte encodes the sign.
1268    pub const fn from_bytes(value: [u8; N]) -> Self {
1269        Self(value)
1270    }
1271
1272    /// Returns the underlying bytes.
1273    pub const fn as_bytes(&self) -> &[u8; N] {
1274        &self.0
1275    }
1276
1277    /// Converts into a runtime `SafeInt`.
1278    pub fn to_val(self) -> SafeInt {
1279        self.into()
1280    }
1281}
1282
1283impl ConstSafeInt<17> {
1284    /// Builds a 17-byte representation from an `i128` while preserving the sign bit.
1285    ///
1286    /// # Examples
1287    /// ```
1288    /// use safe_bigmath::integer::ConstSafeInt;
1289    /// use safe_bigmath::SafeInt;
1290    ///
1291    /// const NEG_ONE: ConstSafeInt<17> = ConstSafeInt::from_i128(-1);
1292    /// assert_eq!(SafeInt::from(NEG_ONE), SafeInt::from(-1));
1293    /// ```
1294    pub const fn from_i128(value: i128) -> Self {
1295        let is_neg = value < 0;
1296        let value = if value == i128::MIN {
1297            i128::MAX as u128 + 1
1298        } else {
1299            value.unsigned_abs()
1300        };
1301        let mut res = Self::from_u128(value);
1302        if is_neg {
1303            res.0[0] = 1;
1304        }
1305        res
1306    }
1307    /// Builds a 17-byte representation from an unsigned 128-bit value.
1308    pub const fn from_u128(value: u128) -> Self {
1309        let mut res = [0; 17];
1310        let mut value = value;
1311        let mut i = 17;
1312        while i > 0 {
1313            i -= 1;
1314            res[i] = (value & 0xff) as u8;
1315            value >>= 8;
1316        }
1317        Self(res)
1318    }
1319}
1320
1321impl<const N: usize> From<ConstSafeInt<N>> for SafeInt {
1322    #[inline(always)]
1323    fn from(value: ConstSafeInt<N>) -> SafeInt {
1324        let pos = value.0.first().cloned().unwrap_or(0) == 0;
1325        let magnitude = BigUint::from_bytes_be(&value.0[1..]);
1326        let mut res = SafeInt(BigInt::from_biguint(Sign::Plus, magnitude));
1327        if !pos {
1328            res = -res;
1329        }
1330        res
1331    }
1332}
1333
1334impl<const N: usize> From<&ConstSafeInt<N>> for SafeInt {
1335    #[inline(always)]
1336    fn from(value: &ConstSafeInt<N>) -> SafeInt {
1337        let pos = value.0.first().cloned().unwrap_or(0) == 0;
1338        let magnitude = BigUint::from_bytes_be(&value.0[1..]);
1339        let mut res = SafeInt(BigInt::from_biguint(Sign::Plus, magnitude));
1340        if !pos {
1341            res = -res;
1342        }
1343        res
1344    }
1345}
1346
1347#[test]
1348fn test_const_safe_int() {
1349    assert_eq!(
1350        SafeInt::from(ConstSafeInt::<4>::from_bytes([0, 0, 0, 1])),
1351        1
1352    );
1353    assert_eq!(SafeInt::from(ConstSafeInt::<2>::from_bytes([0, 1])), 1);
1354    assert_eq!(SafeInt::from(ConstSafeInt::<2>::from_bytes([1, 1])), -1);
1355    assert_eq!(SafeInt::from(ConstSafeInt::<2>::from_bytes([1, 0])), -0);
1356    assert_eq!(
1357        SafeInt::from(ConstSafeInt::<3>::from_bytes([1, 5, 254])),
1358        -1534
1359    );
1360    assert_eq!(
1361        SafeInt::from(ConstSafeInt::<17>::from_i128(-538525)),
1362        -538525
1363    );
1364    assert_eq!(
1365        SafeInt::from(ConstSafeInt::<17>::from_i128(123456789)),
1366        123456789
1367    );
1368    assert_eq!(
1369        SafeInt::from(ConstSafeInt::<17>::from_i128(i128::MIN)),
1370        i128::MIN
1371    );
1372    assert_eq!(
1373        SafeInt::from(ConstSafeInt::<17>::from_i128(i128::MAX)),
1374        i128::MAX
1375    );
1376    assert_eq!(
1377        SafeInt::from(ConstSafeInt::<17>::from_u128(u128::MAX)),
1378        u128::MAX
1379    );
1380    assert_eq!(
1381        SafeInt::from(ConstSafeInt::<17>::from_u128(39874398749837343434343434344)),
1382        39874398749837343434343434344u128
1383    );
1384    assert_eq!(SafeInt::from(ConstSafeInt::<17>::from_u128(0)), 0);
1385}
1386
1387#[test]
1388fn general() {
1389    let a = SafeInt::from(10);
1390    let b = SafeInt::from(20);
1391    let c = &a + &b;
1392    let d = a.clone() + c.clone();
1393    let e = a.clone() + &b;
1394    let f = &a + b.clone();
1395    assert_eq!(c, 30);
1396    assert!(d > a);
1397    assert!(a < d);
1398    assert!(a < b);
1399    assert_eq!(e, f);
1400    assert_eq!(f, a + b);
1401    assert_eq!((SafeInt::from(10) / SafeInt::from(3)).unwrap(), 3);
1402    assert_eq!(SafeInt::from(10) / SafeInt::from(0), None);
1403    assert_ne!(SafeInt::from(10), SafeInt::from(20));
1404    assert!(SafeInt::from(37984739847983497938479797988798789783u128).is_odd());
1405    assert!(
1406        SafeInt::from_str("3798473984798349793847979798879878978334738744739847983749837").unwrap()
1407            > 10
1408    );
1409    assert_eq!(
1410        SafeInt::from(33) / SafeInt::from(3),
1411        Some(SafeInt::from(11))
1412    );
1413    assert_eq!(33 / SafeInt::from(3), Some(SafeInt::from(11)));
1414    assert_eq!(SafeInt::from(33) / 3, Some(SafeInt::from(11)));
1415    assert_eq!(SafeInt::from(10) % SafeInt::from(3), Some(SafeInt::from(1)));
1416    assert_eq!(SafeInt::from(10) % SafeInt::from(0), None);
1417    assert_eq!(10 % SafeInt::from(3), Some(SafeInt::from(1)));
1418    assert_eq!(10 % SafeInt::from(0), None);
1419    assert_eq!(SafeInt::from(10) % 3, Some(SafeInt::from(1)));
1420    assert_eq!(SafeInt::from(10) % 0, None);
1421    assert_eq!(
1422        SafeInt::from(10).div_rem(SafeInt::from(3)),
1423        Some((SafeInt::from(3), SafeInt::from(1)))
1424    );
1425    assert_eq!(SafeInt::from(10).div_rem(SafeInt::from(0)), None);
1426    assert_eq!(33 + SafeInt::from(2), 35);
1427    assert_eq!(SafeInt::from(33) + 2, 35);
1428    assert_eq!(SafeInt::from(5) / SafeInt::from(0), None);
1429    assert_eq!(5 / SafeInt::from(0), None);
1430    assert_eq!(SafeInt::from(5) / 0, None);
1431    assert_eq!(&SafeInt::from(789) / 893797983, Some(SafeInt::from(0)));
1432    assert_eq!(&SafeInt::from(28249) / SafeInt::zero(), None);
1433}
1434
1435#[test]
1436fn test_perquintill_power() {
1437    const PRECISION: u32 = 256;
1438    const PERQUINTILL: u128 = 1_000_000_000_000_000_000;
1439
1440    let x = SafeInt::from(21_000_000_000_000_000u64);
1441    let delta = SafeInt::from(7_000_000_000_000_000u64);
1442    let w1 = SafeInt::from(600_000_000_000_000_000u128);
1443    let w2 = SafeInt::from(400_000_000_000_000_000u128);
1444    let denominator = &x + &delta;
1445    assert_eq!(w1.clone() + w2.clone(), SafeInt::from(PERQUINTILL));
1446
1447    let perquintill_result = SafeInt::pow_ratio_scaled(
1448        &x,
1449        &denominator,
1450        &w1,
1451        &w2,
1452        PRECISION,
1453        &SafeInt::from(PERQUINTILL),
1454    )
1455    .expect("perquintill integer result");
1456
1457    assert_eq!(
1458        perquintill_result,
1459        SafeInt::from(649_519_052_838_328_985u128)
1460    );
1461    let readable = crate::SafeDec::<18>::from_raw(perquintill_result);
1462    assert_eq!(format!("{}", readable), "0.649519052838328985");
1463}
1464
1465#[test]
1466fn pow_ratio_scaled_handles_large_weight_denominators() {
1467    let x = SafeInt::from(21_000_000_000_000_000i128);
1468    let denominator = SafeInt::from(21_000_000_000_000_100i128);
1469    let perquintill = SafeInt::from(1_000_000_000_000_000_000i128);
1470
1471    let cases = [
1472        (
1473            SafeInt::from(500_000_000_000_000_000i128),
1474            SafeInt::from(500_000_000_000_000_000i128),
1475        ),
1476        (
1477            SafeInt::from(499_999_999_999_500_000i128),
1478            SafeInt::from(500_000_000_000_500_000i128),
1479        ),
1480        (
1481            SafeInt::from(500_000_000_000_250_000i128),
1482            SafeInt::from(499_999_999_999_750_000i128),
1483        ),
1484    ];
1485
1486    for (w1, w2) in cases {
1487        let result =
1488            SafeInt::pow_ratio_scaled(&x, &denominator, &w1, &w2, 256, &perquintill).unwrap();
1489        assert_eq!(result, SafeInt::from(999_999_999_999_995_238i128));
1490    }
1491}
1492
1493#[test]
1494fn pow_ratio_scaled_converges_on_boundary_weights() {
1495    let x = SafeInt::from(21_000_000_000_000_000i128);
1496    let denominator = SafeInt::from(21_000_000_000_000_001i128);
1497    let w1 = SafeInt::from(499_999_999_500_000_000i128);
1498    let w2 = SafeInt::from(500_000_000_500_000_000i128);
1499    let scale = SafeInt::from(1_000_000_000_000_000_000i128);
1500    let precision = 256u32;
1501
1502    let start = std::time::Instant::now();
1503    let result = SafeInt::pow_ratio_scaled(&x, &denominator, &w1, &w2, precision, &scale).unwrap();
1504    let elapsed = start.elapsed();
1505
1506    assert_eq!(result, SafeInt::from(999_999_999_999_999_952i128));
1507    assert!(
1508        elapsed < core::time::Duration::from_secs(1),
1509        "pow_ratio_scaled took {:?}",
1510        elapsed
1511    );
1512}
1513
1514#[test]
1515fn pow_ratio_scaled_exact_path_handles_high_exponent() {
1516    let base_num = SafeInt::from(999_999_999i128);
1517    let base_den = SafeInt::from(1_000_000_001i128);
1518    let exp_num = SafeInt::from(MAX_EXACT_EXPONENT as i128 - 1);
1519    let exp_den = SafeInt::one();
1520    let scale = SafeInt::from(1_000_000i128);
1521    let precision = 64u32;
1522
1523    let start = std::time::Instant::now();
1524    let result =
1525        SafeInt::pow_ratio_scaled(&base_num, &base_den, &exp_num, &exp_den, precision, &scale)
1526            .expect("exact path should return");
1527    let elapsed = start.elapsed();
1528
1529    assert!(result > SafeInt::zero());
1530    assert!(
1531        elapsed < core::time::Duration::from_secs(1),
1532        "exact path took {:?}",
1533        elapsed
1534    );
1535}
1536
1537#[test]
1538fn pow_ratio_scaled_default_max_iters_completes_quickly() {
1539    let base_num = SafeInt::from(1i128);
1540    let base_den = SafeInt::from(1_000_000_000_000i128);
1541    let exp_num = SafeInt::one();
1542    let exp_den = SafeInt::from(1u128 << 40);
1543    let scale = SafeInt::one();
1544    let precision = 32u32;
1545
1546    let start = std::time::Instant::now();
1547    let default_iter =
1548        SafeInt::pow_ratio_scaled(&base_num, &base_den, &exp_num, &exp_den, precision, &scale)
1549            .unwrap();
1550    let elapsed = start.elapsed();
1551
1552    let explicit_default = SafeInt::pow_ratio_scaled_with_max_iters(
1553        &base_num,
1554        &base_den,
1555        &exp_num,
1556        &exp_den,
1557        precision,
1558        &scale,
1559        Some(DEFAULT_MAX_ITERS),
1560    )
1561    .unwrap();
1562
1563    assert_eq!(default_iter, explicit_default);
1564    assert!(
1565        elapsed < core::time::Duration::from_secs(2),
1566        "default iterations took {:?}",
1567        elapsed
1568    );
1569}
1570
1571#[test]
1572fn pow_ratio_scaled_uses_scale_to_pick_precision() {
1573    // Force the fallback path (large exponent bits) and make sure the minimum precision we pick
1574    // based on `scale` is close to a much higher requested precision.
1575    let base_num = SafeInt::from(123_456_789u64);
1576    let base_den = SafeInt::from(987_654_321u64);
1577    let exp_num = SafeInt::from(987_654_321_123_456_789u128);
1578    let exp_den = SafeInt::from(123_456_789_987_654_321u128);
1579    let scale = SafeInt::from(1_000_000_000_000_000_000i128);
1580
1581    let coarse = SafeInt::pow_ratio_scaled(&base_num, &base_den, &exp_num, &exp_den, 0, &scale)
1582        .expect("coarse precision result");
1583    let precise = SafeInt::pow_ratio_scaled(&base_num, &base_den, &exp_num, &exp_den, 256, &scale)
1584        .expect("high precision result");
1585    let delta = (precise.clone() - coarse.clone()).abs();
1586
1587    assert!(
1588        delta <= SafeInt::from(1u32),
1589        "coarse {coarse} vs precise {precise} differed by {delta}"
1590    );
1591}
1592
1593#[test]
1594fn pow_ratio_scaled_handles_small_base_fractional_exponent() {
1595    // Base far from 1 with a fractional exponent; previously this would under-approximate badly.
1596    let base_num = SafeInt::from(1u8);
1597    let base_den = SafeInt::from(10u8);
1598    let exp_num = SafeInt::from(500_000_000_001u64);
1599    let exp_den = SafeInt::from(1_000_000_000_000u64);
1600    let scale = SafeInt::from(1_000_000_000_000_000_000u128);
1601    let precision = 128u32;
1602
1603    let result =
1604        SafeInt::pow_ratio_scaled(&base_num, &base_den, &exp_num, &exp_den, precision, &scale)
1605            .expect("small base fractional exponent");
1606    let expected =
1607        ((0.1f64).powf(0.500000000001f64) * 1_000_000_000_000_000_000f64).floor() as u128;
1608    let delta = (result.clone() - SafeInt::from(expected)).abs();
1609
1610    assert!(
1611        delta <= SafeInt::from(128u32),
1612        "result {result} vs expected {expected} (delta {delta})"
1613    );
1614}
1615
1616#[test]
1617fn pow_ratio_scaled_handles_extreme_delta_x() {
1618    let x = SafeInt::from(400_775_553u64);
1619    let dx = SafeInt::from(14_446_633_907_665_582u64);
1620    let base_den = &x + &dx;
1621    let w1 = SafeInt::from(102_337_248_363_782_924u128);
1622    let w2 = SafeInt::from(1_000_000_000_000_000_000u128) - &w1;
1623    let scale = SafeInt::from(1_000_000_000_000_000_000u128);
1624    let precision = 256u32;
1625
1626    let result = SafeInt::pow_ratio_scaled(&x, &base_den, &w1, &w2, precision, &scale)
1627        .expect("extreme delta x");
1628
1629    let expected = ((x.0.to_f64().unwrap() / base_den.0.to_f64().unwrap())
1630        .powf(w1.0.to_f64().unwrap() / w2.0.to_f64().unwrap())
1631        * 1_000_000_000_000_000_000f64)
1632        .floor() as u128;
1633    let delta = (result.clone() - SafeInt::from(expected)).abs();
1634
1635    assert!(
1636        delta <= SafeInt::from(1_000_000u128),
1637        "result {result} vs expected {expected} (delta {delta})"
1638    );
1639}
1640
1641#[test]
1642fn test_zero() {
1643    assert_eq!(SafeInt::zero(), 0);
1644    assert!(SafeInt::zero().is_zero());
1645}
1646
1647#[test]
1648fn test_one() {
1649    let one = SafeInt::one();
1650    assert_eq!(one, 1);
1651}