Skip to main content

decimal_scaled/
powers.rs

1//! Power and root methods for [`I128`].
2//!
3//! # Methods
4//!
5//! - [`I128::pow`] — unsigned integer power via square-and-multiply over
6//!   the `Mul` operator. Panics on overflow in debug builds; wraps in
7//!   release builds. Matches `i128::pow` semantics.
8//! - [`I128::powi`] — signed integer power. For negative `exp`, returns
9//!   `I128::ONE / self.pow(exp.unsigned_abs())`.
10//! - [`I128::powf`] — floating-point power via the f64 bridge. Lossy.
11//!   Requires the `std` feature.
12//! - [`I128::sqrt`] — square root via the f64 bridge. IEEE 754 mandates
13//!   that `f64::sqrt` is correctly-rounded, so identical inputs produce
14//!   identical output bit-patterns on every conformant platform.
15//!   Requires the `std` feature.
16//! - [`I128::cbrt`] — cube root via the f64 bridge. Defined for negative
17//!   inputs. Requires the `std` feature.
18//! - [`I128::mul_add`] — `self * a + b` in one call. No hardware FMA;
19//!   mirrors the `f64::mul_add` call shape so generic numeric code can
20//!   monomorphise to `I128`. Always available.
21//! - [`I128::hypot`] — `sqrt(self^2 + other^2)` without intermediate
22//!   overflow, using the scale-trick algorithm. Requires the `std`
23//!   feature via `sqrt`.
24//!
25//! # `std`-feature gating
26//!
27//! `f64::sqrt`, `f64::cbrt`, and `f64::powf` are inherent methods on
28//! `f64` defined only in `std`; they delegate to platform intrinsics
29//! that are absent from `core`. The integer forms (`pow`, `powi`, and
30//! the variant family) work in `no_std` because they use only `I128`
31//! operators.
32//!
33//! When the `strict` feature is enabled, the four f64-bridge functions
34//! (`powf`, `sqrt`, `cbrt`, `hypot`) compile integer-only stub
35//! implementations that do not require `std`. The stubs currently
36//! `todo!`; they exist so that `strict` builds link without pulling in
37//! the f64 platform intrinsics. Callers that need approximate results in
38//! a `strict`/`no_std` context can obtain the raw `i128` mantissa via
39//! `to_bits` and apply their own arithmetic.
40//!
41//! When `strict` is absent and `std` is present, the original f64-bridge
42//! implementations are compiled instead.
43//!
44//! `no_std` callers without `strict` that need square root, cube root,
45//! or float power can compose them via `libm` or hardware-specific
46//! intrinsics applied to the raw f64 obtained from `to_f64_lossy`.
47//!
48//! # Variant family for `pow`
49//!
50//! - [`I128::checked_pow`] — `Option<Self>`, `None` on overflow at any
51//!   step.
52//! - [`I128::wrapping_pow`] — two's-complement wrap at every step.
53//! - [`I128::saturating_pow`] — clamps to `I128::MAX` or `I128::MIN`
54//!   based on the sign of the would-be result.
55//! - [`I128::overflowing_pow`] — `(Self, bool)`; the bool is `true` if
56//!   any step overflowed, with the value equal to the wrapping form.
57//!
58//! # Square-and-multiply algorithm
59//!
60//! Starting from `acc = ONE`, the algorithm walks the bits of `exp` from
61//! low to high. On each iteration:
62//!
63//! 1. If the current bit of `exp` is set, multiply `acc *= base`.
64//! 2. Square `base *= base`.
65//!
66//! This costs `O(log exp)` multiplications rather than `O(exp)`. The
67//! variant family follows the same structure but applies the
68//! corresponding overflow arithmetic at every multiplication step.
69//!
70//! # `i32::MIN` edge case for `powi`
71//!
72//! `i32::unsigned_abs` returns `2_147_483_648_u32` for `i32::MIN`,
73//! avoiding the signed-negation overflow that `(-i32::MIN) as u32` would
74//! cause. `I128::ONE.powi(i32::MIN)` therefore evaluates correctly as
75//! `I128::ONE / I128::ONE.pow(2_147_483_648_u32)`.
76
77use crate::core_type::I128;
78use crate::mg_divide::mul_div_pow10;
79
80impl<const SCALE: u32> I128<SCALE> {
81    /// Raises `self` to the power `exp`.
82    ///
83    /// Uses square-and-multiply: walks the bits of `exp` from low to
84    /// high, squaring the base each step and accumulating when the
85    /// corresponding bit is set. Costs `O(log exp)` multiplications.
86    /// Each multiplication routes through the `I128` `Mul` operator.
87    ///
88    /// `exp = 0` always returns `ONE`, even when `self` is `ZERO`
89    /// (matches `i128::pow` convention).
90    ///
91    /// # Precision
92    ///
93    /// Strict: all arithmetic is integer-only; result is bit-exact.
94    ///
95    /// # Panics
96    ///
97    /// In debug builds, panics on `i128` overflow at any multiplication
98    /// step. In release builds, wraps two's-complement. Matches
99    /// `i128::pow` and `I128::Mul` semantics.
100    ///
101    /// Use [`Self::checked_pow`], [`Self::wrapping_pow`],
102    /// [`Self::saturating_pow`], or [`Self::overflowing_pow`] for
103    /// explicit overflow control.
104    ///
105    /// # Examples
106    ///
107    /// ```ignore
108    /// use decimal_scaled::I128s12;
109    /// let two = I128s12::from_int(2);
110    /// assert_eq!(two.pow(10), I128s12::from_int(1024));
111    /// // exp = 0 returns ONE regardless of base.
112    /// assert_eq!(I128s12::ZERO.pow(0), I128s12::ONE);
113    /// ```
114    #[inline]
115    #[must_use]
116    pub fn pow(self, exp: u32) -> Self {
117        let mut acc = Self::ONE;
118        let mut base = self;
119        let mut e = exp;
120        while e > 0 {
121            if e & 1 == 1 {
122                acc *= base;
123            }
124            e >>= 1;
125            if e > 0 {
126                base *= base;
127            }
128        }
129        acc
130    }
131
132    /// Raises `self` to the signed integer power `exp`.
133    ///
134    /// For non-negative `exp`, equivalent to `self.pow(exp as u32)`.
135    /// For negative `exp`, returns `I128::ONE / self.pow(exp.unsigned_abs())`,
136    /// i.e. the reciprocal of the positive-exponent form.
137    ///
138    /// # Precision
139    ///
140    /// Strict: all arithmetic is integer-only; result is bit-exact.
141    ///
142    /// # Panics
143    ///
144    /// - Overflow of `i128` storage at any step in debug builds (matches
145    ///   [`Self::pow`]).
146    /// - Division by zero when `self == ZERO` and `exp < 0`.
147    ///
148    /// # Examples
149    ///
150    /// ```ignore
151    /// use decimal_scaled::I128s12;
152    /// let two = I128s12::from_int(2);
153    /// assert_eq!(two.powi(-1), I128s12::ONE / two);
154    /// assert_eq!(two.powi(0), I128s12::ONE);
155    /// assert_eq!(two.powi(3), I128s12::from_int(8));
156    /// ```
157    #[inline]
158    #[must_use]
159    pub fn powi(self, exp: i32) -> Self {
160        if exp >= 0 {
161            self.pow(exp as u32)
162        } else {
163            // unsigned_abs handles i32::MIN without signed-negation overflow.
164            Self::ONE / self.pow(exp.unsigned_abs())
165        }
166    }
167
168    /// Raises `self` to the power `exp` (strict integer-only stub).
169    ///
170    /// Converts both operands to f64, calls `f64::powf`, then converts
171    /// the result back. For integer exponents, prefer [`Self::pow`] or
172    /// [`Self::powi`], which are bit-exact.
173    ///
174    /// NaN results map to `ZERO`; infinities clamp to `MAX` or `MIN`,
175    /// following the saturate-vs-error policy of [`Self::from_f64_lossy`].
176    ///
177    /// # Precision
178    ///
179    /// Strict: all arithmetic is integer-only; result is bit-exact.
180    ///
181    /// # Examples
182    ///
183    /// ```ignore
184    /// use decimal_scaled::I128s12;
185    /// let two = I128s12::from_int(2);
186    /// let three = I128s12::from_int(3);
187    /// // 2^3 = 8, within f64 precision.
188    /// assert!((two.powf(three).to_f64_lossy() - 8.0).abs() < 1e-9);
189    /// ```
190    #[cfg(feature = "strict")]
191    #[inline]
192    #[must_use]
193    pub fn powf(self, exp: I128<SCALE>) -> Self {
194        todo!("strict: integer-only powf not yet implemented")
195    }
196
197    /// Raises `self` to the power `exp` via the f64 bridge.
198    ///
199    /// Converts both operands to f64, calls `f64::powf`, then converts
200    /// the result back. For integer exponents, prefer [`Self::pow`] or
201    /// [`Self::powi`], which are bit-exact.
202    ///
203    /// NaN results map to `ZERO`; infinities clamp to `MAX` or `MIN`,
204    /// following the saturate-vs-error policy of [`Self::from_f64_lossy`].
205    ///
206    /// # Precision
207    ///
208    /// Lossy: involves f64 at some point; result may lose precision.
209    ///
210    /// # Examples
211    ///
212    /// ```ignore
213    /// use decimal_scaled::I128s12;
214    /// let two = I128s12::from_int(2);
215    /// let three = I128s12::from_int(3);
216    /// // 2^3 = 8, within f64 precision.
217    /// assert!((two.powf(three).to_f64_lossy() - 8.0).abs() < 1e-9);
218    /// ```
219    #[cfg(all(feature = "std", not(feature = "strict")))]
220    #[inline]
221    #[must_use]
222    pub fn powf(self, exp: I128<SCALE>) -> Self {
223        Self::from_f64_lossy(self.to_f64_lossy().powf(exp.to_f64_lossy()))
224    }
225
226    /// Returns the square root of `self` (strict integer-only stub).
227    ///
228    /// IEEE 754 mandates that `f64::sqrt` is correctly-rounded
229    /// (round-to-nearest, ties-to-even). Combined with the deterministic
230    /// `to_f64_lossy` / `from_f64_lossy` round-trip, this makes
231    /// `I128::sqrt` bit-deterministic: the same input produces the same
232    /// output bit-pattern on every IEEE-754-conformant platform.
233    ///
234    /// Negative inputs produce a NaN from `f64::sqrt`, which
235    /// [`Self::from_f64_lossy`] maps to `ZERO` per the saturate-vs-error
236    /// policy. No panic is raised for negative inputs.
237    ///
238    /// # Precision
239    ///
240    /// Strict: all arithmetic is integer-only; result is bit-exact.
241    ///
242    /// # Examples
243    ///
244    /// ```ignore
245    /// use decimal_scaled::I128s12;
246    /// assert_eq!(I128s12::ZERO.sqrt(), I128s12::ZERO);
247    /// // f64::sqrt(1.0) == 1.0 exactly, so the result is bit-exact.
248    /// assert_eq!(I128s12::ONE.sqrt(), I128s12::ONE);
249    /// ```
250    #[cfg(feature = "strict")]
251    #[inline]
252    #[must_use]
253    pub fn sqrt(self) -> Self {
254        todo!("strict: integer-only sqrt not yet implemented")
255    }
256
257    /// Returns the square root of `self` via the f64 bridge.
258    ///
259    /// IEEE 754 mandates that `f64::sqrt` is correctly-rounded
260    /// (round-to-nearest, ties-to-even). Combined with the deterministic
261    /// `to_f64_lossy` / `from_f64_lossy` round-trip, this makes
262    /// `I128::sqrt` bit-deterministic: the same input produces the same
263    /// output bit-pattern on every IEEE-754-conformant platform.
264    ///
265    /// Negative inputs produce a NaN from `f64::sqrt`, which
266    /// [`Self::from_f64_lossy`] maps to `ZERO` per the saturate-vs-error
267    /// policy. No panic is raised for negative inputs.
268    ///
269    /// # Precision
270    ///
271    /// Lossy: involves f64 at some point; result may lose precision.
272    ///
273    /// # Examples
274    ///
275    /// ```ignore
276    /// use decimal_scaled::I128s12;
277    /// assert_eq!(I128s12::ZERO.sqrt(), I128s12::ZERO);
278    /// // f64::sqrt(1.0) == 1.0 exactly, so the result is bit-exact.
279    /// assert_eq!(I128s12::ONE.sqrt(), I128s12::ONE);
280    /// ```
281    #[cfg(all(feature = "std", not(feature = "strict")))]
282    #[inline]
283    #[must_use]
284    pub fn sqrt(self) -> Self {
285        Self::from_f64_lossy(self.to_f64_lossy().sqrt())
286    }
287
288    /// Returns the cube root of `self` via the f64 bridge.
289    ///
290    /// `f64::cbrt` is defined for the entire real line, including
291    /// negative inputs (`cbrt(-8.0) == -2.0`). The result is
292    /// bit-deterministic across IEEE-754-conformant platforms because
293    /// `f64::cbrt` is correctly-rounded.
294    ///
295    /// # Precision
296    ///
297    /// Lossy: involves f64 at some point; result may lose precision.
298    ///
299    /// # Examples
300    ///
301    /// ```ignore
302    /// use decimal_scaled::I128s12;
303    /// let neg_eight = I128s12::from_int(-8);
304    /// let result = neg_eight.cbrt();
305    /// assert!((result.to_f64_lossy() - (-2.0_f64)).abs() < 1e-9);
306    /// ```
307    #[cfg(feature = "std")]
308    #[inline]
309    #[must_use]
310    pub fn cbrt(self) -> Self {
311        Self::from_f64_lossy(self.to_f64_lossy().cbrt())
312    }
313
314    // Integer power variant family.
315
316    /// Returns `self * a + b`.
317    ///
318    /// Mirrors the `f64::mul_add` call shape so f64-generic numeric code
319    /// can monomorphise to `I128` without rewriting call sites. No
320    /// hardware FMA is involved; the multiply uses the `I128` `Mul`
321    /// operator and the add uses plain `i128` addition.
322    ///
323    /// The result is bit-equal to `self * a + b` on every input,
324    /// including panic or wrap behaviour at overflow boundaries.
325    ///
326    /// Available in `no_std` builds because the underlying `Mul` and
327    /// `Add` operators require only `core`.
328    ///
329    /// # Precision
330    ///
331    /// Strict: all arithmetic is integer-only; result is bit-exact.
332    ///
333    /// # Panics
334    ///
335    /// In debug builds, panics on overflow at the multiply step or the
336    /// add step. In release builds, both steps wrap two's-complement.
337    ///
338    /// # Examples
339    ///
340    /// ```ignore
341    /// use decimal_scaled::I128s12;
342    /// let two   = I128s12::from_int(2);
343    /// let three = I128s12::from_int(3);
344    /// let four  = I128s12::from_int(4);
345    /// // 2 * 3 + 4 = 10
346    /// assert_eq!(two.mul_add(three, four), I128s12::from_int(10));
347    /// ```
348    #[inline]
349    #[must_use]
350    pub fn mul_add(self, a: Self, b: Self) -> Self {
351        self * a + b
352    }
353
354    /// Returns `sqrt(self^2 + other^2)` without intermediate overflow.
355    ///
356    /// The naive form `(self * self + other * other).sqrt()` overflows
357    /// `i128` once either operand approaches `sqrt(I128::MAX)`. This
358    /// method uses the scale trick to avoid that:
359    ///
360    /// ```text
361    /// hypot(a, b) = max(|a|, |b|) * sqrt(1 + (min(|a|, |b|) / max(|a|, |b|))^2)
362    /// ```
363    ///
364    /// The `min/max` ratio is in `[0, 1]`, so `ratio^2` is also in
365    /// `[0, 1]` and cannot overflow. The outer multiply by `large` only
366    /// overflows when the true hypotenuse genuinely exceeds `I128::MAX`,
367    /// which matches `f64::hypot`'s contract.
368    ///
369    /// Both inputs are absolute-valued before processing, so
370    /// `hypot(-a, b) == hypot(a, b)`.
371    ///
372    /// Edge cases: `hypot(0, 0) == 0` (bit-exact via the early return);
373    /// `hypot(0, x) ~= |x|` and `hypot(x, 0) ~= |x|`.
374    ///
375    /// # Precision
376    ///
377    /// Lossy: involves f64 at some point; result may lose precision.
378    ///
379    /// # Examples
380    ///
381    /// ```ignore
382    /// use decimal_scaled::I128s12;
383    /// let three = I128s12::from_int(3);
384    /// let four  = I128s12::from_int(4);
385    /// // Pythagorean triple: hypot(3, 4) ~= 5.
386    /// assert!((three.hypot(four).to_f64_lossy() - 5.0).abs() < 1e-9);
387    /// ```
388    #[cfg(feature = "std")]
389    #[inline]
390    #[must_use]
391    pub fn hypot(self, other: Self) -> Self {
392        let a = self.abs();
393        let b = other.abs();
394        let (large, small) = if a >= b { (a, b) } else { (b, a) };
395        if large == Self::ZERO {
396            // Both inputs are zero; large is the max of two non-negatives,
397            // so this branch is only reached when both are zero.
398            Self::ZERO
399        } else {
400            let ratio = small / large;
401            // ratio^2 is in [0, 1]; ONE + ratio^2 is in [1, 2]; no overflow.
402            // The outer sqrt is in [1, sqrt(2)]; the final multiply by large
403            // only overflows when the true hypotenuse exceeds I128::MAX.
404            let one_plus_sq = Self::ONE + ratio * ratio;
405            large * one_plus_sq.sqrt()
406        }
407    }
408
409    // Overflow-variant family for pow.
410
411    /// Returns `Some(self^exp)`, or `None` if any multiplication step
412    /// overflows `i128`.
413    ///
414    /// Walks the same square-and-multiply as [`Self::pow`] but uses
415    /// `mul_div_pow10` (which returns `Option<i128>`) at each step.
416    /// The first `None` short-circuits to a `None` return.
417    ///
418    /// # Precision
419    ///
420    /// Strict: all arithmetic is integer-only; result is bit-exact.
421    ///
422    /// # Examples
423    ///
424    /// ```ignore
425    /// use decimal_scaled::I128s12;
426    /// // MAX^2 overflows.
427    /// assert!(I128s12::MAX.checked_pow(2).is_none());
428    /// // Any power of ONE is ONE.
429    /// assert_eq!(I128s12::ONE.checked_pow(1_000_000), Some(I128s12::ONE));
430    /// ```
431    #[inline]
432    #[must_use]
433    pub fn checked_pow(self, exp: u32) -> Option<Self> {
434        let mut acc: i128 = Self::ONE.0;
435        let mut base: i128 = self.0;
436        let mut e = exp;
437        while e > 0 {
438            if e & 1 == 1 {
439                acc = mul_div_pow10::<SCALE>(acc, base)?;
440            }
441            e >>= 1;
442            if e > 0 {
443                base = mul_div_pow10::<SCALE>(base, base)?;
444            }
445        }
446        Some(Self(acc))
447    }
448
449    /// Returns `self^exp`, wrapping two's-complement on overflow at
450    /// every multiplication step.
451    ///
452    /// Follows the same square-and-multiply structure as [`Self::pow`].
453    /// When a step overflows `mul_div_pow10`, the fallback is
454    /// `wrapping_mul` followed by `wrapping_div` of the scale
455    /// multiplier. The exact wrap pattern is deterministic and
456    /// reproducible but is not otherwise specified.
457    ///
458    /// # Precision
459    ///
460    /// Strict: all arithmetic is integer-only; result is bit-exact.
461    ///
462    /// # Examples
463    ///
464    /// ```ignore
465    /// use decimal_scaled::I128s12;
466    /// // ONE^N never overflows and returns ONE.
467    /// assert_eq!(I128s12::ONE.wrapping_pow(1_000_000), I128s12::ONE);
468    /// // MAX^2 wraps to a deterministic but unspecified value.
469    /// let _ = I128s12::MAX.wrapping_pow(2);
470    /// ```
471    #[inline]
472    #[must_use]
473    pub fn wrapping_pow(self, exp: u32) -> Self {
474        let mut acc: i128 = Self::ONE.0;
475        let mut base: i128 = self.0;
476        let mut e = exp;
477        let mult = Self::multiplier();
478        while e > 0 {
479            if e & 1 == 1 {
480                acc = match mul_div_pow10::<SCALE>(acc, base) {
481                    Some(q) => q,
482                    None => acc.wrapping_mul(base).wrapping_div(mult),
483                };
484            }
485            e >>= 1;
486            if e > 0 {
487                base = match mul_div_pow10::<SCALE>(base, base) {
488                    Some(q) => q,
489                    None => base.wrapping_mul(base).wrapping_div(mult),
490                };
491            }
492        }
493        Self(acc)
494    }
495
496    /// Returns `self^exp`, clamping to `I128::MAX` or `I128::MIN` on
497    /// overflow at any step.
498    ///
499    /// On the first step that overflows, the result is clamped based on
500    /// the sign of the mathematical result: positive overflows clamp to
501    /// `MAX`, negative overflows clamp to `MIN`. The sign of the result
502    /// is determined by `self.signum()` and whether `exp` is odd.
503    ///
504    /// `exp = 0` always returns `ONE` before entering the loop.
505    ///
506    /// # Precision
507    ///
508    /// Strict: all arithmetic is integer-only; result is bit-exact.
509    ///
510    /// # Examples
511    ///
512    /// ```ignore
513    /// use decimal_scaled::I128s12;
514    /// assert_eq!(I128s12::MAX.saturating_pow(2), I128s12::MAX);
515    /// assert_eq!(I128s12::ONE.saturating_pow(1_000_000), I128s12::ONE);
516    /// ```
517    #[inline]
518    #[must_use]
519    pub fn saturating_pow(self, exp: u32) -> Self {
520        // exp == 0: result is ONE by convention.
521        if exp == 0 {
522            return Self::ONE;
523        }
524        let mut acc: i128 = Self::ONE.0;
525        let mut base: i128 = self.0;
526        let mut e = exp;
527        // The final result is negative iff the base is negative and exp is odd.
528        let result_negative_if_overflow = self.0 < 0 && (exp & 1) == 1;
529        while e > 0 {
530            if e & 1 == 1 {
531                match mul_div_pow10::<SCALE>(acc, base) {
532                    Some(q) => acc = q,
533                    None => {
534                        return if result_negative_if_overflow {
535                            Self::MIN
536                        } else {
537                            Self::MAX
538                        };
539                    }
540                }
541            }
542            e >>= 1;
543            if e > 0 {
544                match mul_div_pow10::<SCALE>(base, base) {
545                    Some(q) => base = q,
546                    None => {
547                        // base*base is non-negative (squared); clamp by the
548                        // sign of the would-be final result.
549                        return if result_negative_if_overflow {
550                            Self::MIN
551                        } else {
552                            Self::MAX
553                        };
554                    }
555                }
556            }
557        }
558        Self(acc)
559    }
560
561    /// Returns `(self^exp, overflowed)`.
562    ///
563    /// `overflowed` is `true` if any multiplication step overflowed
564    /// `i128`. The returned value is the wrapping form (matches
565    /// [`Self::wrapping_pow`]).
566    ///
567    /// # Precision
568    ///
569    /// Strict: all arithmetic is integer-only; result is bit-exact.
570    ///
571    /// # Examples
572    ///
573    /// ```ignore
574    /// use decimal_scaled::I128s12;
575    /// let (_value, overflowed) = I128s12::MAX.overflowing_pow(2);
576    /// assert!(overflowed);
577    /// let (value, overflowed) = I128s12::ONE.overflowing_pow(5);
578    /// assert!(!overflowed);
579    /// assert_eq!(value, I128s12::ONE);
580    /// ```
581    #[inline]
582    #[must_use]
583    pub fn overflowing_pow(self, exp: u32) -> (Self, bool) {
584        let mut acc: i128 = Self::ONE.0;
585        let mut base: i128 = self.0;
586        let mut e = exp;
587        let mut overflowed = false;
588        let mult = Self::multiplier();
589        while e > 0 {
590            if e & 1 == 1 {
591                acc = match mul_div_pow10::<SCALE>(acc, base) {
592                    Some(q) => q,
593                    None => {
594                        overflowed = true;
595                        acc.wrapping_mul(base).wrapping_div(mult)
596                    }
597                };
598            }
599            e >>= 1;
600            if e > 0 {
601                base = match mul_div_pow10::<SCALE>(base, base) {
602                    Some(q) => q,
603                    None => {
604                        overflowed = true;
605                        base.wrapping_mul(base).wrapping_div(mult)
606                    }
607                };
608            }
609        }
610        (Self(acc), overflowed)
611    }
612}
613
614#[cfg(test)]
615mod tests {
616    use crate::core_type::I128s12;
617
618    // Tolerance for f64-bridge tests. Used only by std-feature-gated
619    // tests below; gated to suppress unused-item warnings on no_std builds.
620    #[cfg(feature = "std")]
621    const TWO_LSB: i128 = 2;
622
623    #[cfg(feature = "std")]
624    fn within_lsb(actual: I128s12, expected: I128s12, lsb: i128) -> bool {
625        let diff = (actual.to_bits() - expected.to_bits()).abs();
626        diff <= lsb
627    }
628
629    // pow (integer)
630
631    /// `pow(0)` returns ONE for a nonzero base.
632    #[test]
633    fn pow_zero_is_one_for_nonzero() {
634        let v = I128s12::from_int(7);
635        assert_eq!(v.pow(0), I128s12::ONE);
636    }
637
638    /// `pow(1)` returns self.
639    #[test]
640    fn pow_one_is_self() {
641        let v = I128s12::from_int(7);
642        assert_eq!(v.pow(1), v);
643    }
644
645    /// `pow(2)` equals `self * self` for an integer value.
646    #[test]
647    fn pow_two_matches_mul() {
648        let v = I128s12::from_int(13);
649        assert_eq!(v.pow(2), v * v);
650    }
651
652    /// `pow(2)` equals `self * self` for a fractional value.
653    #[test]
654    fn pow_two_matches_mul_fractional() {
655        // 1.5 in raw bits at SCALE = 12.
656        let v = I128s12::from_bits(1_500_000_000_000);
657        assert_eq!(v.pow(2), v * v);
658    }
659
660    /// `2^10 == 1024`.
661    #[test]
662    fn pow_two_to_the_ten() {
663        let two = I128s12::from_int(2);
664        assert_eq!(two.pow(10), I128s12::from_int(1024));
665    }
666
667    /// `pow(0, 0) == ONE` — matches `i128::pow(0, 0) == 1`.
668    #[test]
669    fn zero_pow_zero_is_one() {
670        assert_eq!(I128s12::ZERO.pow(0), I128s12::ONE);
671    }
672
673    /// `pow(0, n)` for `n > 0` is `ZERO`.
674    #[test]
675    fn zero_pow_positive_is_zero() {
676        assert_eq!(I128s12::ZERO.pow(1), I128s12::ZERO);
677        assert_eq!(I128s12::ZERO.pow(5), I128s12::ZERO);
678    }
679
680    /// `pow(n)` of `ONE` is always `ONE`.
681    #[test]
682    fn one_pow_n_is_one() {
683        assert_eq!(I128s12::ONE.pow(0), I128s12::ONE);
684        assert_eq!(I128s12::ONE.pow(1), I128s12::ONE);
685        assert_eq!(I128s12::ONE.pow(100), I128s12::ONE);
686    }
687
688    /// `(-1)^n` alternates sign.
689    #[test]
690    fn negative_one_pow_alternates() {
691        let neg_one = -I128s12::ONE;
692        assert_eq!(neg_one.pow(0), I128s12::ONE);
693        assert_eq!(neg_one.pow(1), neg_one);
694        assert_eq!(neg_one.pow(2), I128s12::ONE);
695        assert_eq!(neg_one.pow(3), neg_one);
696    }
697
698    // powi (signed integer)
699
700    /// `powi(0)` returns ONE.
701    #[test]
702    fn powi_zero_is_one() {
703        let v = I128s12::from_int(7);
704        assert_eq!(v.powi(0), I128s12::ONE);
705    }
706
707    /// `powi(1)` returns self.
708    #[test]
709    fn powi_one_is_self() {
710        let v = I128s12::from_int(7);
711        assert_eq!(v.powi(1), v);
712    }
713
714    /// `powi(-1)` returns `ONE / self`.
715    #[test]
716    fn powi_minus_one_is_reciprocal() {
717        let v = I128s12::from_int(7);
718        assert_eq!(v.powi(-1), I128s12::ONE / v);
719    }
720
721    /// `powi(-3)` equals `ONE / pow(3)`.
722    #[test]
723    fn powi_negative_matches_reciprocal_of_positive() {
724        let v = I128s12::from_int(2);
725        assert_eq!(v.powi(-3), I128s12::ONE / v.pow(3));
726    }
727
728    /// `powi` agrees with `pow` for non-negative exponents.
729    #[test]
730    fn powi_positive_matches_pow() {
731        let v = I128s12::from_int(3);
732        for e in 0_i32..6 {
733            assert_eq!(v.powi(e), v.pow(e as u32));
734        }
735    }
736
737    /// `i32::MIN` edge: `unsigned_abs` returns 2_147_483_648; for a base
738    /// of ONE the result is ONE.
739    #[test]
740    fn powi_i32_min_for_one_base() {
741        assert_eq!(I128s12::ONE.powi(i32::MIN), I128s12::ONE);
742    }
743
744    // powf — requires std feature
745
746    /// `powf(0.5)` approximates `sqrt` within 2 LSB.
747    #[cfg(feature = "std")]
748    #[test]
749    fn powf_half_matches_sqrt() {
750        let v = I128s12::from_int(4);
751        let half = I128s12::from_bits(500_000_000_000); // 0.5 at SCALE=12
752        let powf_result = v.powf(half);
753        let sqrt_result = v.sqrt();
754        assert!(
755            within_lsb(powf_result, sqrt_result, TWO_LSB),
756            "powf(0.5)={}, sqrt={}, diff={}",
757            powf_result.to_bits(),
758            sqrt_result.to_bits(),
759            (powf_result.to_bits() - sqrt_result.to_bits()).abs(),
760        );
761    }
762
763    /// `powf(2)` agrees with `pow(2)` within 2 LSB.
764    #[cfg(feature = "std")]
765    #[test]
766    fn powf_two_matches_pow_two_within_lsb() {
767        let v = I128s12::from_int(7);
768        let two = I128s12::from_int(2);
769        assert!(within_lsb(v.powf(two), v.pow(2), TWO_LSB));
770    }
771
772    // sqrt — requires std feature
773
774    /// `sqrt(0) == 0`.
775    #[cfg(feature = "std")]
776    #[test]
777    fn sqrt_zero_is_zero() {
778        assert_eq!(I128s12::ZERO.sqrt(), I128s12::ZERO);
779    }
780
781    /// `sqrt(1) == 1` — bit-exact because `f64::sqrt(1.0) == 1.0`.
782    #[cfg(feature = "std")]
783    #[test]
784    fn sqrt_one_is_one_bit_exact() {
785        assert_eq!(I128s12::ONE.sqrt(), I128s12::ONE);
786    }
787
788    /// `sqrt(4) == 2` — bit-exact because `f64::sqrt(4.0) == 2.0`.
789    #[cfg(feature = "std")]
790    #[test]
791    fn sqrt_four_is_two() {
792        let four = I128s12::from_int(4);
793        let two = I128s12::from_int(2);
794        assert_eq!(four.sqrt(), two);
795    }
796
797    /// `sqrt(self * self) ~= self.abs()` within 2 LSB.
798    #[cfg(feature = "std")]
799    #[test]
800    fn sqrt_of_square_recovers_abs() {
801        let v = I128s12::from_bits(1_234_567_890_123);
802        let squared = v * v;
803        let recovered = squared.sqrt();
804        let abs_v = v.abs();
805        assert!(
806            within_lsb(recovered, abs_v, TWO_LSB),
807            "sqrt({})={}, expected~={}, diff={}",
808            squared.to_bits(),
809            recovered.to_bits(),
810            abs_v.to_bits(),
811            (recovered.to_bits() - abs_v.to_bits()).abs(),
812        );
813    }
814
815    /// `sqrt(self * self) ~= self.abs()` within 2 LSB for negative self.
816    #[cfg(feature = "std")]
817    #[test]
818    fn sqrt_of_square_negative_recovers_abs() {
819        let v = -I128s12::from_bits(4_567_891_234_567);
820        let squared = v * v;
821        let recovered = squared.sqrt();
822        let abs_v = v.abs();
823        assert!(within_lsb(recovered, abs_v, TWO_LSB));
824    }
825
826    /// A negative input produces NaN in f64, which maps to ZERO.
827    #[cfg(feature = "std")]
828    #[test]
829    fn sqrt_negative_saturates_to_zero() {
830        let v = -I128s12::from_int(4);
831        assert_eq!(v.sqrt(), I128s12::ZERO);
832    }
833
834    // cbrt — requires std feature
835
836    /// `cbrt(0) == 0`.
837    #[cfg(feature = "std")]
838    #[test]
839    fn cbrt_zero_is_zero() {
840        assert_eq!(I128s12::ZERO.cbrt(), I128s12::ZERO);
841    }
842
843    /// `cbrt(1) == 1`.
844    #[cfg(feature = "std")]
845    #[test]
846    fn cbrt_one_is_one() {
847        assert_eq!(I128s12::ONE.cbrt(), I128s12::ONE);
848    }
849
850    /// `cbrt(8) ~= 2` within 2 LSB.
851    #[cfg(feature = "std")]
852    #[test]
853    fn cbrt_eight_is_two() {
854        let eight = I128s12::from_int(8);
855        let two = I128s12::from_int(2);
856        assert!(within_lsb(eight.cbrt(), two, TWO_LSB));
857    }
858
859    /// `cbrt(-8) ~= -2` within 2 LSB.
860    #[cfg(feature = "std")]
861    #[test]
862    fn cbrt_minus_eight_is_minus_two() {
863        let neg_eight = I128s12::from_int(-8);
864        let neg_two = I128s12::from_int(-2);
865        assert!(
866            within_lsb(neg_eight.cbrt(), neg_two, TWO_LSB),
867            "cbrt(-8) = {}, expected ~ {}",
868            neg_eight.cbrt().to_bits(),
869            neg_two.to_bits(),
870        );
871    }
872
873    // checked_pow / wrapping_pow / saturating_pow / overflowing_pow
874
875    /// `checked_pow(MAX, 2)` is `None` because MAX^2 overflows.
876    #[test]
877    fn checked_pow_max_squared_is_none() {
878        assert!(I128s12::MAX.checked_pow(2).is_none());
879    }
880
881    /// `checked_pow(ONE, N)` is `Some(ONE)` for any N.
882    #[test]
883    fn checked_pow_one_is_some_one() {
884        assert_eq!(I128s12::ONE.checked_pow(1_000_000), Some(I128s12::ONE));
885        assert_eq!(I128s12::ONE.checked_pow(0), Some(I128s12::ONE));
886    }
887
888    /// `checked_pow` agrees with `pow` for non-overflowing inputs.
889    #[test]
890    fn checked_pow_matches_pow_when_no_overflow() {
891        let v = I128s12::from_int(3);
892        assert_eq!(v.checked_pow(0), Some(v.pow(0)));
893        assert_eq!(v.checked_pow(1), Some(v.pow(1)));
894        assert_eq!(v.checked_pow(5), Some(v.pow(5)));
895    }
896
897    /// `saturating_pow(MAX, 2)` clamps to `MAX`.
898    #[test]
899    fn saturating_pow_max_squared_is_max() {
900        assert_eq!(I128s12::MAX.saturating_pow(2), I128s12::MAX);
901    }
902
903    /// `saturating_pow(MIN, 3)` clamps to `MIN` (negative result, odd exp).
904    #[test]
905    fn saturating_pow_min_cubed_is_min() {
906        assert_eq!(I128s12::MIN.saturating_pow(3), I128s12::MIN);
907    }
908
909    /// `saturating_pow(MIN, 2)` clamps to `MAX` (positive result, even exp).
910    #[test]
911    fn saturating_pow_min_squared_is_max() {
912        assert_eq!(I128s12::MIN.saturating_pow(2), I128s12::MAX);
913    }
914
915    /// `saturating_pow(ONE, N)` is ONE for any N.
916    #[test]
917    fn saturating_pow_one_is_one() {
918        assert_eq!(I128s12::ONE.saturating_pow(1_000_000), I128s12::ONE);
919    }
920
921    /// `saturating_pow(v, 0)` is ONE for any base.
922    #[test]
923    fn saturating_pow_zero_exp_is_one() {
924        assert_eq!(I128s12::MAX.saturating_pow(0), I128s12::ONE);
925        assert_eq!(I128s12::MIN.saturating_pow(0), I128s12::ONE);
926    }
927
928    /// `overflowing_pow(MAX, 2)` returns `(wrapping_value, true)`.
929    #[test]
930    fn overflowing_pow_max_squared_flags_overflow() {
931        let (value, overflowed) = I128s12::MAX.overflowing_pow(2);
932        assert!(overflowed);
933        assert_eq!(value, I128s12::MAX.wrapping_pow(2));
934    }
935
936    /// `overflowing_pow(ONE, N)` never overflows.
937    #[test]
938    fn overflowing_pow_one_no_overflow() {
939        let (value, overflowed) = I128s12::ONE.overflowing_pow(1_000_000);
940        assert!(!overflowed);
941        assert_eq!(value, I128s12::ONE);
942    }
943
944    /// `overflowing_pow(v, 0)` is `(ONE, false)`.
945    #[test]
946    fn overflowing_pow_zero_exp_no_overflow() {
947        let (value, overflowed) = I128s12::MAX.overflowing_pow(0);
948        assert!(!overflowed);
949        assert_eq!(value, I128s12::ONE);
950    }
951
952    /// `wrapping_pow(MAX, 2)` matches the value half of `overflowing_pow`.
953    #[test]
954    fn wrapping_pow_max_squared_matches_overflowing() {
955        let wrap = I128s12::MAX.wrapping_pow(2);
956        let (over, _flag) = I128s12::MAX.overflowing_pow(2);
957        assert_eq!(wrap, over);
958    }
959
960    /// `wrapping_pow(ONE, N)` is ONE.
961    #[test]
962    fn wrapping_pow_one_is_one() {
963        assert_eq!(I128s12::ONE.wrapping_pow(1_000_000), I128s12::ONE);
964    }
965
966    /// `wrapping_pow` agrees with `pow` for non-overflowing inputs.
967    #[test]
968    fn wrapping_pow_matches_pow_when_no_overflow() {
969        let v = I128s12::from_int(3);
970        for e in 0..6 {
971            assert_eq!(v.wrapping_pow(e), v.pow(e));
972        }
973    }
974
975    /// `pow(2) == self * self` for several representative raw values.
976    #[test]
977    fn pow_two_property_safe_values() {
978        for raw in [
979            1_234_567_890_123_i128,
980            4_567_891_234_567_i128,
981            7_890_123_456_789_i128,
982        ] {
983            let v = I128s12::from_bits(raw);
984            assert_eq!(v.pow(2), v * v, "raw bits {raw}");
985        }
986    }
987
988    // mul_add (always available)
989
990    /// `mul_add(0, 0, 0) == 0`.
991    #[test]
992    fn mul_add_zero_zero_zero_is_zero() {
993        let z = I128s12::ZERO;
994        assert_eq!(z.mul_add(z, z), I128s12::ZERO);
995    }
996
997    /// `mul_add(2, 3, 4) == 10`.
998    #[test]
999    fn mul_add_two_three_four_is_ten() {
1000        let two = I128s12::from_int(2);
1001        let three = I128s12::from_int(3);
1002        let four = I128s12::from_int(4);
1003        assert_eq!(two.mul_add(three, four), I128s12::from_int(10));
1004    }
1005
1006    /// `mul_add(self, ONE, ZERO) == self`.
1007    #[test]
1008    fn mul_add_identity_collapses() {
1009        let v = I128s12::from_int(7);
1010        assert_eq!(v.mul_add(I128s12::ONE, I128s12::ZERO), v);
1011    }
1012
1013    /// `mul_add(self, ZERO, b) == b`.
1014    #[test]
1015    fn mul_add_zero_factor_yields_addend() {
1016        let v = I128s12::from_int(7);
1017        let b = I128s12::from_int(13);
1018        assert_eq!(v.mul_add(I128s12::ZERO, b), b);
1019    }
1020
1021    /// `mul_add(a, b, c) == a * b + c` for representative raw values.
1022    #[test]
1023    fn mul_add_matches_mul_then_add_safe_values() {
1024        for (a_raw, b_raw, c_raw) in [
1025            (1_234_567_890_123_i128, 2_345_678_901_234_i128, 3_456_789_012_345_i128),
1026            (4_567_891_234_567_i128, 5_678_912_345_678_i128, 6_789_123_456_789_i128),
1027            (7_890_123_456_789_i128, 8_901_234_567_891_i128, 9_012_345_678_912_i128),
1028        ] {
1029            let a = I128s12::from_bits(a_raw);
1030            let b = I128s12::from_bits(b_raw);
1031            let c = I128s12::from_bits(c_raw);
1032            assert_eq!(
1033                a.mul_add(b, c),
1034                a * b + c,
1035                "raw bits ({a_raw}, {b_raw}, {c_raw})",
1036            );
1037        }
1038    }
1039
1040    /// `(-a).mul_add(b, c)` propagates the sign through the multiply step.
1041    #[test]
1042    fn mul_add_sign_propagates_through_factor() {
1043        let a = I128s12::from_int(3);
1044        let b = I128s12::from_int(5);
1045        let c = I128s12::from_int(7);
1046        // (-3) * 5 + 7 = -15 + 7 = -8
1047        assert_eq!((-a).mul_add(b, c), I128s12::from_int(-8));
1048    }
1049
1050    // hypot — requires std feature
1051
1052    // Tolerance for hypot: composes divide + square + add + sqrt + multiply,
1053    // each with up to 1 LSB of f64-bridge slack; sqrt quantisation dominates.
1054    #[cfg(feature = "std")]
1055    const HYPOT_TOLERANCE_LSB: i128 = 32;
1056
1057    /// `hypot(3, 4) ~= 5` — the Pythagorean triple.
1058    #[cfg(feature = "std")]
1059    #[test]
1060    fn hypot_three_four_is_five() {
1061        let three = I128s12::from_int(3);
1062        let four = I128s12::from_int(4);
1063        let five = I128s12::from_int(5);
1064        let result = three.hypot(four);
1065        assert!(
1066            within_lsb(result, five, HYPOT_TOLERANCE_LSB),
1067            "hypot(3, 4)={}, expected~={}, diff={}",
1068            result.to_bits(),
1069            five.to_bits(),
1070            (result.to_bits() - five.to_bits()).abs(),
1071        );
1072    }
1073
1074    /// `hypot(0, 0) == 0` — bit-exact via the early-return path.
1075    #[cfg(feature = "std")]
1076    #[test]
1077    fn hypot_zero_zero_is_zero_bit_exact() {
1078        assert_eq!(I128s12::ZERO.hypot(I128s12::ZERO), I128s12::ZERO);
1079    }
1080
1081    /// `hypot(0, x) ~= x.abs()` for nonzero x.
1082    #[cfg(feature = "std")]
1083    #[test]
1084    fn hypot_zero_x_is_abs_x() {
1085        let x = I128s12::from_int(7);
1086        let result = I128s12::ZERO.hypot(x);
1087        assert!(
1088            within_lsb(result, x.abs(), HYPOT_TOLERANCE_LSB),
1089            "hypot(0, 7)={}, expected~={}",
1090            result.to_bits(),
1091            x.abs().to_bits(),
1092        );
1093    }
1094
1095    /// `hypot(x, 0) ~= x.abs()` for nonzero x.
1096    #[cfg(feature = "std")]
1097    #[test]
1098    fn hypot_x_zero_is_abs_x() {
1099        let x = I128s12::from_int(-9);
1100        let result = x.hypot(I128s12::ZERO);
1101        assert!(
1102            within_lsb(result, x.abs(), HYPOT_TOLERANCE_LSB),
1103            "hypot(-9, 0)={}, expected~={}",
1104            result.to_bits(),
1105            x.abs().to_bits(),
1106        );
1107    }
1108
1109    /// `hypot(-a, b) == hypot(a, b)` — sign invariance from the abs step.
1110    #[cfg(feature = "std")]
1111    #[test]
1112    fn hypot_sign_invariant() {
1113        let three = I128s12::from_int(3);
1114        let four = I128s12::from_int(4);
1115        let pos = three.hypot(four);
1116        let neg_a = (-three).hypot(four);
1117        let neg_b = three.hypot(-four);
1118        let neg_both = (-three).hypot(-four);
1119        assert_eq!(pos, neg_a);
1120        assert_eq!(pos, neg_b);
1121        assert_eq!(pos, neg_both);
1122    }
1123
1124    /// `hypot` does not panic at large magnitudes that the naive form
1125    /// would overflow.
1126    ///
1127    /// At SCALE=12 with `i128::MAX / 2` raw bits, the true hypotenuse
1128    /// is well below `I128::MAX / sqrt(2)`, so no overflow occurs and
1129    /// the result is a nonzero positive value.
1130    #[cfg(feature = "std")]
1131    #[test]
1132    fn hypot_large_magnitudes_does_not_panic() {
1133        let half_max = I128s12::from_bits(i128::MAX / 2);
1134        let result = half_max.hypot(half_max);
1135        assert!(result > I128s12::ZERO);
1136        assert!(result >= half_max);
1137    }
1138
1139    /// `hypot(a, b)` matches the naive `sqrt(a^2 + b^2)` within tolerance
1140    /// for small magnitudes where the naive form does not overflow.
1141    #[cfg(feature = "std")]
1142    #[test]
1143    fn hypot_matches_naive_sqrt_of_sum_of_squares() {
1144        let a = I128s12::from_int(12);
1145        let b = I128s12::from_int(13);
1146        let h = a.hypot(b);
1147        let naive = (a * a + b * b).sqrt();
1148        assert!(
1149            within_lsb(h, naive, HYPOT_TOLERANCE_LSB),
1150            "hypot(12, 13)={}, naive sqrt(a^2+b^2)={}, diff={}",
1151            h.to_bits(),
1152            naive.to_bits(),
1153            (h.to_bits() - naive.to_bits()).abs(),
1154        );
1155    }
1156}