Skip to main content

decimal_scaled/
arithmetic.rs

1//! Arithmetic operator overloads for [`I128`].
2//!
3//! All operators work directly on the raw `i128` storage value.
4//! Addition, subtraction, and negation require no rescaling because the
5//! scale factor commutes with those operations. Multiplication and division
6//! each require one rescaling step to keep the result in `value * 10^SCALE`
7//! form; remainder requires none because both operands share the same scale.
8//!
9//! # Overflow policy
10//!
11//! Default operator semantics match Rust's `i128` arithmetic: **panics on
12//! overflow in debug builds; wraps two's-complement in release builds.**
13//! This is the standard Rust integer arithmetic contract. Explicit-overflow
14//! variants (`checked_*`, `wrapping_*`, `saturating_*`, `overflowing_*`)
15//! are not provided in this module.
16//!
17//! # Mul / Div algorithm
18//!
19//! `Mul` / `MulAssign` use a 256-bit widening intermediate followed by a
20//! Moller-Granlund 2011 magic-number divide (see the `mg_divide` module).
21//! This replaces a naive `(self.0 * rhs.0) / multiplier()` approach that
22//! would silently overflow `i128` at operand magnitudes beyond
23//! `sqrt(i128::MAX)`. With the widening approach the operand range covers
24//! the full `i128` storage range; the only overflow possible is on the
25//! final `i128` quotient.
26//!
27//! `Div` / `DivAssign` widen the numerator `a * 10^SCALE` to 256 bits via
28//! the same schoolbook multiply, then divide by `b` using a hand-rolled
29//! binary long-divide. MG-style magic does not apply because the divisor is
30//! variable rather than a known power of ten.
31//!
32//! Both paths preserve panic-debug / wrap-release semantics for the final
33//! `i128` result. The intermediate 256-bit calculation never observably
34//! overflows.
35
36use core::ops::{
37    Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
38};
39
40use crate::core_type::I128;
41
42impl<const SCALE: u32> Add for I128<SCALE> {
43    type Output = Self;
44
45    /// Add two values of the same scale.
46    ///
47    /// Because both operands are in `value * 10^S` form, the raw storage
48    /// values can be added directly with no rescaling.
49    ///
50    /// # Precision
51    ///
52    /// Strict: all arithmetic is integer-only; result is bit-exact.
53    ///
54    /// # Examples
55    ///
56    /// ```
57    /// use decimal_scaled::I128s12;
58    ///
59    /// let a = I128s12::from_bits(1_500_000_000_000); // 1.5
60    /// let b = I128s12::from_bits(2_500_000_000_000); // 2.5
61    /// assert_eq!((a + b).to_bits(), 4_000_000_000_000);
62    /// ```
63    #[inline]
64    fn add(self, rhs: Self) -> Self {
65        Self(self.0 + rhs.0)
66    }
67}
68
69impl<const SCALE: u32> AddAssign for I128<SCALE> {
70    /// Add `rhs` to `self` in place.
71    ///
72    /// # Precision
73    ///
74    /// Strict: all arithmetic is integer-only; result is bit-exact.
75    #[inline]
76    fn add_assign(&mut self, rhs: Self) {
77        self.0 += rhs.0;
78    }
79}
80
81impl<const SCALE: u32> Sub for I128<SCALE> {
82    type Output = Self;
83
84    /// Subtract `rhs` from `self`.
85    ///
86    /// No rescaling needed; raw storage values are subtracted directly.
87    ///
88    /// # Precision
89    ///
90    /// Strict: all arithmetic is integer-only; result is bit-exact.
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// use decimal_scaled::I128s12;
96    ///
97    /// let a = I128s12::from_bits(3_000_000_000_000); // 3.0
98    /// let b = I128s12::from_bits(1_500_000_000_000); // 1.5
99    /// assert_eq!((a - b).to_bits(), 1_500_000_000_000);
100    /// ```
101    #[inline]
102    fn sub(self, rhs: Self) -> Self {
103        Self(self.0 - rhs.0)
104    }
105}
106
107impl<const SCALE: u32> SubAssign for I128<SCALE> {
108    /// Subtract `rhs` from `self` in place.
109    ///
110    /// # Precision
111    ///
112    /// Strict: all arithmetic is integer-only; result is bit-exact.
113    #[inline]
114    fn sub_assign(&mut self, rhs: Self) {
115        self.0 -= rhs.0;
116    }
117}
118
119impl<const SCALE: u32> Neg for I128<SCALE> {
120    type Output = Self;
121
122    /// Negate the value.
123    ///
124    /// Negates the raw `i128` storage directly; no rescaling needed.
125    ///
126    /// # Panics
127    ///
128    /// Panics in debug builds when `self == I128::MIN` because `i128::MIN`
129    /// has no positive counterpart in two's-complement. In release builds
130    /// the result wraps to `i128::MIN`.
131    ///
132    /// # Precision
133    ///
134    /// Strict: all arithmetic is integer-only; result is bit-exact.
135    ///
136    /// # Examples
137    ///
138    /// ```
139    /// use decimal_scaled::I128s12;
140    ///
141    /// let x = I128s12::from_bits(1_500_000_000_000); // 1.5
142    /// assert_eq!((-x).to_bits(), -1_500_000_000_000);
143    /// ```
144    #[inline]
145    fn neg(self) -> Self {
146        Self(-self.0)
147    }
148}
149
150impl<const SCALE: u32> Mul for I128<SCALE> {
151    type Output = Self;
152
153    /// Multiply two values, rescaling the result back to `value * 10^S` form.
154    ///
155    /// The raw product `a.0 * b.0` has units `10^(2S)`, so it must be
156    /// divided by `10^S` to restore the canonical scale. This is done via
157    /// a 256-bit widening intermediate and a Moller-Granlund magic-number
158    /// divide (see the `mg_divide` module), which avoids the intermediate
159    /// overflow that would occur with a naive `i128` multiply at large
160    /// operand magnitudes.
161    ///
162    /// # Panics
163    ///
164    /// Panics in debug builds when the final rescaled quotient overflows
165    /// `i128::MAX`. In release builds the result wraps two's-complement.
166    ///
167    /// # Precision
168    ///
169    /// Strict: all arithmetic is integer-only; result is bit-exact.
170    ///
171    /// # Examples
172    ///
173    /// ```
174    /// use decimal_scaled::I128s12;
175    ///
176    /// let a = I128s12::from_bits(1_500_000_000_000); // 1.5
177    /// let b = I128s12::from_bits(2_000_000_000_000); // 2.0
178    /// assert_eq!((a * b).to_bits(), 3_000_000_000_000); // 3.0
179    /// ```
180    #[inline]
181    fn mul(self, rhs: Self) -> Self {
182        match crate::mg_divide::mul_div_pow10::<SCALE>(self.0, rhs.0) {
183            Some(q) => Self(q),
184            None => Self(panic_or_wrap_mul::<SCALE>(self.0, rhs.0)),
185        }
186    }
187}
188
189impl<const SCALE: u32> MulAssign for I128<SCALE> {
190    /// Multiply `self` by `rhs` in place.
191    ///
192    /// # Precision
193    ///
194    /// Strict: all arithmetic is integer-only; result is bit-exact.
195    #[inline]
196    fn mul_assign(&mut self, rhs: Self) {
197        *self = *self * rhs;
198    }
199}
200
201impl<const SCALE: u32> Div for I128<SCALE> {
202    type Output = Self;
203
204    /// Divide `self` by `rhs`, rescaling the numerator to keep the result
205    /// in `value * 10^S` form.
206    ///
207    /// The numerator `self.0` is widened to 256 bits and multiplied by
208    /// `10^SCALE` before dividing by `rhs.0`. This avoids the intermediate
209    /// overflow that would occur with a naive `(self.0 * 10^S) / rhs.0`
210    /// approach at large dividend magnitudes.
211    ///
212    /// # Panics
213    ///
214    /// Panics on division by zero (matching `i128 /`). Also panics in debug
215    /// builds when the final quotient overflows `i128`; wraps in release
216    /// builds.
217    ///
218    /// # Precision
219    ///
220    /// Strict: all arithmetic is integer-only; result is bit-exact.
221    ///
222    /// # Examples
223    ///
224    /// ```
225    /// use decimal_scaled::I128s12;
226    ///
227    /// let a = I128s12::from_bits(3_000_000_000_000); // 3.0
228    /// let b = I128s12::from_bits(2_000_000_000_000); // 2.0
229    /// assert_eq!((a / b).to_bits(), 1_500_000_000_000); // 1.5
230    /// ```
231    #[inline]
232    fn div(self, rhs: Self) -> Self {
233        if rhs.0 == 0 {
234            // Match the panic message from `i128 /`.
235            panic!("attempt to divide by zero");
236        }
237        match crate::mg_divide::div_pow10_div::<SCALE>(self.0, rhs.0) {
238            Some(q) => Self(q),
239            None => Self(panic_or_wrap_div::<SCALE>(self.0, rhs.0)),
240        }
241    }
242}
243
244impl<const SCALE: u32> DivAssign for I128<SCALE> {
245    /// Divide `self` by `rhs` in place.
246    ///
247    /// # Precision
248    ///
249    /// Strict: all arithmetic is integer-only; result is bit-exact.
250    #[inline]
251    fn div_assign(&mut self, rhs: Self) {
252        *self = *self / rhs;
253    }
254}
255
256// Overflow fallback helpers for Mul and Div.
257//
258// The widening multiply/divide paths return `None` when the final `i128`
259// quotient overflows. These helpers reproduce Rust's standard integer
260// overflow contract: panic in debug builds, wrap two's-complement in
261// release builds. The wrapping form re-derives the result from the
262// original operands using `wrapping_*` intrinsics so it matches the naive
263// form's behavior at operand magnitudes where the naive form was correct.
264
265/// Emit a debug panic or return the wrapping result for a Mul overflow.
266///
267/// # Precision
268///
269/// Strict: all arithmetic is integer-only; result is bit-exact.
270#[inline(always)]
271#[allow(clippy::arithmetic_side_effects)]
272fn panic_or_wrap_mul<const SCALE: u32>(a: i128, b: i128) -> i128 {
273    #[cfg(debug_assertions)]
274    {
275        let _ = (a, b);
276        panic!("attempt to multiply with overflow");
277    }
278    #[cfg(not(debug_assertions))]
279    {
280        a.wrapping_mul(b).wrapping_div(I128::<SCALE>::multiplier())
281    }
282}
283
284/// Emit a debug panic or return the wrapping result for a Div overflow.
285///
286/// # Precision
287///
288/// Strict: all arithmetic is integer-only; result is bit-exact.
289#[inline(always)]
290#[allow(clippy::arithmetic_side_effects)]
291fn panic_or_wrap_div<const SCALE: u32>(a: i128, b: i128) -> i128 {
292    #[cfg(debug_assertions)]
293    {
294        let _ = (a, b);
295        panic!("attempt to divide with overflow");
296    }
297    #[cfg(not(debug_assertions))]
298    {
299        a.wrapping_mul(I128::<SCALE>::multiplier()).wrapping_div(b)
300    }
301}
302
303impl<const SCALE: u32> Rem for I128<SCALE> {
304    type Output = Self;
305
306    /// Compute the remainder of dividing `self` by `rhs`, truncated toward
307    /// zero.
308    ///
309    /// Because both operands share the same `SCALE`, the raw remainder
310    /// `self.0 % rhs.0` already lives in `value * 10^S` form without any
311    /// rescaling. Behavior matches `i128 %`: the result has the same sign
312    /// as the dividend.
313    ///
314    /// # Panics
315    ///
316    /// Panics on `rhs == ZERO` (division by zero), matching `i128 %`.
317    ///
318    /// # Precision
319    ///
320    /// Strict: all arithmetic is integer-only; result is bit-exact.
321    ///
322    /// # Examples
323    ///
324    /// ```
325    /// use decimal_scaled::I128s12;
326    ///
327    /// let a = I128s12::from_bits(5_500_000_000_000); // 5.5
328    /// let b = I128s12::from_bits(2_000_000_000_000); // 2.0
329    /// assert_eq!((a % b).to_bits(), 1_500_000_000_000); // 1.5
330    /// ```
331    #[inline]
332    fn rem(self, rhs: Self) -> Self {
333        Self(self.0 % rhs.0)
334    }
335}
336
337impl<const SCALE: u32> RemAssign for I128<SCALE> {
338    /// Compute the remainder of `self / rhs` and store it in `self`.
339    ///
340    /// # Precision
341    ///
342    /// Strict: all arithmetic is integer-only; result is bit-exact.
343    #[inline]
344    fn rem_assign(&mut self, rhs: Self) {
345        self.0 %= rhs.0;
346    }
347}
348
349// Math methods.
350//
351// All methods delegate to `i128` stdlib intrinsics where possible.
352// Default overflow policy matches Rust integer semantics: panic in debug
353// builds, wrap in release builds.
354//
355// Sign notes:
356// - `floor` uses `i128::div_euclid` (rounds the quotient toward negative
357//   infinity for a positive divisor; `multiplier()` is always positive).
358// - `ceil` is implemented as `-floor(-self)`.
359// - `trunc` uses plain truncating `/`, which rounds toward zero — different
360//   from `floor` for negative inputs.
361// - `round` is half-away-from-zero: add half a unit with the sign of
362//   `self`, then truncate.
363// - `div_euclid` and `rem_euclid` share the same scale, so no rescaling
364//   of the remainder is needed.
365// - `div_floor` and `div_ceil` are implemented inline because
366//   `i128::div_floor` / `i128::div_ceil` are still gated behind unstable
367//   features for signed types as of Rust 1.95.
368// - `abs_diff` is implemented as `max - min` so the subtraction is always
369//   non-negative.
370
371impl<const SCALE: u32> I128<SCALE> {
372    /// Return the absolute value of `self`.
373    ///
374    /// # Panics
375    ///
376    /// Panics in debug builds when `self == I128::MIN` because `i128::MIN`
377    /// has no positive counterpart in two's-complement. Wraps to `i128::MIN`
378    /// in release builds.
379    ///
380    /// # Precision
381    ///
382    /// Strict: all arithmetic is integer-only; result is bit-exact.
383    ///
384    /// # Examples
385    ///
386    /// ```
387    /// use decimal_scaled::I128s12;
388    ///
389    /// let x = I128s12::from_bits(-1_500_000_000_000);
390    /// assert_eq!(x.abs().to_bits(), 1_500_000_000_000);
391    /// ```
392    #[inline]
393    pub const fn abs(self) -> Self {
394        Self(self.0.abs())
395    }
396
397    /// Return the sign of `self` as a scaled `I128`.
398    ///
399    /// Returns `-ONE` for negative values, `ZERO` for zero, and `+ONE`
400    /// for positive values, mirroring `f64::signum` / `i128::signum` lifted
401    /// into the `I128` type. Unlike `i128::signum` which returns a bare
402    /// `-1`, `0`, or `1`, this method encodes the result as `N * 10^S`.
403    ///
404    /// # Precision
405    ///
406    /// Strict: all arithmetic is integer-only; result is bit-exact.
407    ///
408    /// # Examples
409    ///
410    /// ```
411    /// use decimal_scaled::I128s12;
412    ///
413    /// assert_eq!(I128s12::from_bits(500_000_000_000).signum(), I128s12::ONE);
414    /// assert_eq!(I128s12::ZERO.signum(), I128s12::ZERO);
415    /// assert_eq!(I128s12::from_bits(-500_000_000_000).signum(), -I128s12::ONE);
416    /// ```
417    #[inline]
418    pub fn signum(self) -> Self {
419        match self.0.signum() {
420            1 => Self::ONE,
421            -1 => Self(-Self::multiplier()),
422            _ => Self::ZERO,
423        }
424    }
425
426    /// Return the largest integer multiple of `ONE` that is less than or
427    /// equal to `self` (round toward negative infinity).
428    ///
429    /// For negative inputs this differs from truncation:
430    /// `I128(-0.5).floor()` returns `I128(-1.0)`, not `I128(0.0)`.
431    ///
432    /// # Precision
433    ///
434    /// Strict: all arithmetic is integer-only; result is bit-exact.
435    ///
436    /// # Examples
437    ///
438    /// ```
439    /// use decimal_scaled::I128s12;
440    ///
441    /// let x = I128s12::from_bits(2_500_000_000_000); // 2.5
442    /// assert_eq!(x.floor().to_bits(), 2_000_000_000_000);
443    ///
444    /// let y = I128s12::from_bits(-2_500_000_000_000); // -2.5
445    /// assert_eq!(y.floor().to_bits(), -3_000_000_000_000);
446    /// ```
447    #[inline]
448    pub fn floor(self) -> Self {
449        let m = Self::multiplier();
450        Self(self.0.div_euclid(m) * m)
451    }
452
453    /// Return the smallest integer multiple of `ONE` that is greater than
454    /// or equal to `self` (round toward positive infinity).
455    ///
456    /// For negative inputs: `I128(-0.5).ceil()` returns `I128(0.0)`.
457    ///
458    /// # Precision
459    ///
460    /// Strict: all arithmetic is integer-only; result is bit-exact.
461    ///
462    /// # Examples
463    ///
464    /// ```
465    /// use decimal_scaled::I128s12;
466    ///
467    /// let x = I128s12::from_bits(2_500_000_000_000); // 2.5
468    /// assert_eq!(x.ceil().to_bits(), 3_000_000_000_000);
469    ///
470    /// let y = I128s12::from_bits(-2_500_000_000_000); // -2.5
471    /// assert_eq!(y.ceil().to_bits(), -2_000_000_000_000);
472    /// ```
473    #[inline]
474    pub fn ceil(self) -> Self {
475        let m = Self::multiplier();
476        // Expand ceil as -floor(-self) to avoid the unstable `int_roundings` feature.
477        Self(-((-self.0).div_euclid(m)) * m)
478    }
479
480    /// Round to the nearest integer using half-away-from-zero.
481    ///
482    /// Ties (values whose fractional part is exactly 0.5) round away from
483    /// zero: `2.5` rounds to `3.0` and `-2.5` rounds to `-3.0`. This
484    /// matches `f64::round`. Half-even ("banker's") rounding is not
485    /// provided; use `floor` and `fract` to compose it if needed.
486    ///
487    /// # Precision
488    ///
489    /// Strict: all arithmetic is integer-only; result is bit-exact.
490    ///
491    /// # Examples
492    ///
493    /// ```
494    /// use decimal_scaled::I128s12;
495    ///
496    /// assert_eq!(I128s12::from_bits(2_500_000_000_000).round().to_bits(), 3_000_000_000_000);
497    /// assert_eq!(I128s12::from_bits(2_400_000_000_000).round().to_bits(), 2_000_000_000_000);
498    /// assert_eq!(I128s12::from_bits(-2_500_000_000_000).round().to_bits(), -3_000_000_000_000);
499    /// ```
500    #[inline]
501    pub fn round(self) -> Self {
502        let m = Self::multiplier();
503        let half = m / 2;
504        let bias = if self.0 >= 0 { half } else { -half };
505        // Truncating divide shifts the half-unit bias away from zero.
506        Self((self.0 + bias) / m * m)
507    }
508
509    /// Drop the fractional part, rounding toward zero.
510    ///
511    /// For negative inputs this differs from `floor`:
512    /// `I128(-2.5).trunc()` returns `I128(-2.0)`, whereas
513    /// `I128(-2.5).floor()` returns `I128(-3.0)`.
514    ///
515    /// # Precision
516    ///
517    /// Strict: all arithmetic is integer-only; result is bit-exact.
518    ///
519    /// # Examples
520    ///
521    /// ```
522    /// use decimal_scaled::I128s12;
523    ///
524    /// assert_eq!(I128s12::from_bits(2_500_000_000_000).trunc().to_bits(), 2_000_000_000_000);
525    /// assert_eq!(I128s12::from_bits(-2_500_000_000_000).trunc().to_bits(), -2_000_000_000_000);
526    /// ```
527    #[inline]
528    pub fn trunc(self) -> Self {
529        let m = Self::multiplier();
530        Self(self.0 / m * m)
531    }
532
533    /// Return only the fractional part: `self - self.trunc()`.
534    ///
535    /// The result has the same sign as `self` because `trunc` rounds toward
536    /// zero. `I128(2.5).fract()` is `I128(0.5)`; `I128(-2.5).fract()` is
537    /// `I128(-0.5)`.
538    ///
539    /// # Precision
540    ///
541    /// Strict: all arithmetic is integer-only; result is bit-exact.
542    ///
543    /// # Examples
544    ///
545    /// ```
546    /// use decimal_scaled::I128s12;
547    ///
548    /// assert_eq!(I128s12::from_bits(2_500_000_000_000).fract().to_bits(), 500_000_000_000);
549    /// assert_eq!(I128s12::from_bits(-2_500_000_000_000).fract().to_bits(), -500_000_000_000);
550    /// assert_eq!(I128s12::from_bits(2_000_000_000_000).fract().to_bits(), 0);
551    /// ```
552    #[inline]
553    pub fn fract(self) -> Self {
554        let m = Self::multiplier();
555        Self(self.0 - (self.0 / m * m))
556    }
557
558    /// Return the lesser of `self` and `other`.
559    ///
560    /// # Precision
561    ///
562    /// Strict: all arithmetic is integer-only; result is bit-exact.
563    ///
564    /// # Examples
565    ///
566    /// ```
567    /// use decimal_scaled::I128s12;
568    ///
569    /// let a = I128s12::from_bits(1_000_000_000_000);
570    /// let b = I128s12::from_bits(2_000_000_000_000);
571    /// assert_eq!(a.min(b), a);
572    /// ```
573    #[inline]
574    pub fn min(self, other: Self) -> Self {
575        Self(self.0.min(other.0))
576    }
577
578    /// Return the greater of `self` and `other`.
579    ///
580    /// # Precision
581    ///
582    /// Strict: all arithmetic is integer-only; result is bit-exact.
583    ///
584    /// # Examples
585    ///
586    /// ```
587    /// use decimal_scaled::I128s12;
588    ///
589    /// let a = I128s12::from_bits(1_000_000_000_000);
590    /// let b = I128s12::from_bits(2_000_000_000_000);
591    /// assert_eq!(a.max(b), b);
592    /// ```
593    #[inline]
594    pub fn max(self, other: Self) -> Self {
595        Self(self.0.max(other.0))
596    }
597
598    /// Restrict `self` to the closed interval `[lo, hi]`.
599    ///
600    /// Returns `lo` if `self < lo`, `hi` if `self > hi`, and `self`
601    /// otherwise.
602    ///
603    /// # Panics
604    ///
605    /// Panics if `lo > hi`, matching `Ord::clamp`.
606    ///
607    /// # Precision
608    ///
609    /// Strict: all arithmetic is integer-only; result is bit-exact.
610    ///
611    /// # Examples
612    ///
613    /// ```
614    /// use decimal_scaled::I128s12;
615    ///
616    /// let lo = I128s12::from_bits(1_000_000_000_000); // 1.0
617    /// let hi = I128s12::from_bits(3_000_000_000_000); // 3.0
618    /// let x  = I128s12::from_bits(5_000_000_000_000); // 5.0
619    /// assert_eq!(x.clamp(lo, hi), hi);
620    /// ```
621    #[inline]
622    pub fn clamp(self, lo: Self, hi: Self) -> Self {
623        Self(self.0.clamp(lo.0, hi.0))
624    }
625
626    /// Return the multiplicative inverse: `ONE / self`.
627    ///
628    /// # Panics
629    ///
630    /// Panics when `self == ZERO` (division by zero).
631    ///
632    /// # Precision
633    ///
634    /// Strict: all arithmetic is integer-only; result is bit-exact.
635    ///
636    /// # Examples
637    ///
638    /// ```
639    /// use decimal_scaled::I128s12;
640    ///
641    /// let two  = I128s12::from_bits(2_000_000_000_000);
642    /// let half = I128s12::from_bits(500_000_000_000);
643    /// assert_eq!(two.recip(), half);
644    /// ```
645    #[inline]
646    pub fn recip(self) -> Self {
647        Self::ONE / self
648    }
649
650    /// Return a value with the magnitude of `self` and the sign of `sign`.
651    ///
652    /// When `sign == ZERO` the result is positive because `i128` has no
653    /// negative-zero representation.
654    ///
655    /// # Precision
656    ///
657    /// Strict: all arithmetic is integer-only; result is bit-exact.
658    ///
659    /// # Examples
660    ///
661    /// ```
662    /// use decimal_scaled::I128s12;
663    ///
664    /// let pos = I128s12::from_bits(1_500_000_000_000);
665    /// let neg = I128s12::from_bits(-2_000_000_000_000);
666    /// assert_eq!(pos.copysign(neg).to_bits(), -1_500_000_000_000);
667    /// ```
668    #[inline]
669    pub fn copysign(self, sign: Self) -> Self {
670        let mag = self.0.abs();
671        if sign.0 < 0 { Self(-mag) } else { Self(mag) }
672    }
673
674    /// Euclidean division: returns the quotient as a `I128` integer multiple
675    /// of `ONE`, chosen so that the remainder is non-negative.
676    ///
677    /// Delegates to `i128::div_euclid` on the raw storage values; the
678    /// quotient is rescaled by `multiplier()` to produce a value in
679    /// `N * 10^S` form.
680    ///
681    /// # Panics
682    ///
683    /// Panics on `rhs == ZERO` and on overflow in debug builds (e.g.
684    /// `I128::MIN.div_euclid(-ONE)` overflows the quotient, mirroring
685    /// `i128::div_euclid`).
686    ///
687    /// # Precision
688    ///
689    /// Strict: all arithmetic is integer-only; result is bit-exact.
690    ///
691    /// # Examples
692    ///
693    /// ```
694    /// use decimal_scaled::I128s12;
695    ///
696    /// let a = I128s12::from_bits(-5_000_000_000_000); // -5.0
697    /// let b = I128s12::from_bits( 2_000_000_000_000); //  2.0
698    /// // Euclidean: quotient = -3, remainder = 1 (always non-negative)
699    /// assert_eq!(a.div_euclid(b).to_bits(), -3_000_000_000_000);
700    /// ```
701    #[inline]
702    pub fn div_euclid(self, rhs: Self) -> Self {
703        Self(self.0.div_euclid(rhs.0) * Self::multiplier())
704    }
705
706    /// Euclidean remainder: `self - rhs * self.div_euclid(rhs)`.
707    ///
708    /// The result is always non-negative when `rhs != ZERO`. Because both
709    /// operands share the same scale, `self.0.rem_euclid(rhs.0)` already
710    /// lives in `value * 10^S` form without rescaling.
711    ///
712    /// # Panics
713    ///
714    /// Panics on `rhs == ZERO`.
715    ///
716    /// # Precision
717    ///
718    /// Strict: all arithmetic is integer-only; result is bit-exact.
719    ///
720    /// # Examples
721    ///
722    /// ```
723    /// use decimal_scaled::I128s12;
724    ///
725    /// let a = I128s12::from_bits(-5_000_000_000_000); // -5.0
726    /// let b = I128s12::from_bits( 2_000_000_000_000); //  2.0
727    /// assert_eq!(a.rem_euclid(b).to_bits(), 1_000_000_000_000); // 1.0, non-negative
728    /// ```
729    #[inline]
730    pub fn rem_euclid(self, rhs: Self) -> Self {
731        Self(self.0.rem_euclid(rhs.0))
732    }
733
734    /// Floor-rounded division: returns `floor(self / rhs)` as a `I128`
735    /// integer multiple of `ONE`.
736    ///
737    /// Differs from `div_euclid` for negative divisors: `div_floor` is
738    /// keyed to the real-number quotient, while `div_euclid` is keyed to
739    /// keeping the remainder non-negative regardless of divisor sign.
740    ///
741    /// Implemented inline because `i128::div_floor` for signed types is
742    /// still behind an unstable feature as of Rust 1.95.
743    ///
744    /// # Panics
745    ///
746    /// Panics on `rhs == ZERO`.
747    ///
748    /// # Precision
749    ///
750    /// Strict: all arithmetic is integer-only; result is bit-exact.
751    ///
752    /// # Examples
753    ///
754    /// ```
755    /// use decimal_scaled::I128s12;
756    ///
757    /// let a = I128s12::from_bits(-5_000_000_000_000); // -5.0
758    /// let b = I128s12::from_bits( 2_000_000_000_000); //  2.0
759    /// // floor(-2.5) = -3
760    /// assert_eq!(a.div_floor(b).to_bits(), -3_000_000_000_000);
761    /// ```
762    #[inline]
763    pub fn div_floor(self, rhs: Self) -> Self {
764        let q = self.0 / rhs.0;
765        let r = self.0 % rhs.0;
766        // Adjust when the truncated quotient rounded the wrong way.
767        // XOR of sign bits detects a sign mismatch between remainder and divisor.
768        let raw = if r != 0 && (r ^ rhs.0) < 0 { q - 1 } else { q };
769        Self(raw * Self::multiplier())
770    }
771
772    /// Ceil-rounded division: returns `ceil(self / rhs)` as a `I128`
773    /// integer multiple of `ONE`.
774    ///
775    /// Implemented inline because `i128::div_ceil` for signed types is
776    /// still behind an unstable feature as of Rust 1.95.
777    ///
778    /// # Panics
779    ///
780    /// Panics on `rhs == ZERO`.
781    ///
782    /// # Precision
783    ///
784    /// Strict: all arithmetic is integer-only; result is bit-exact.
785    ///
786    /// # Examples
787    ///
788    /// ```
789    /// use decimal_scaled::I128s12;
790    ///
791    /// let a = I128s12::from_bits(5_000_000_000_000); // 5.0
792    /// let b = I128s12::from_bits(2_000_000_000_000); // 2.0
793    /// // ceil(2.5) = 3
794    /// assert_eq!(a.div_ceil(b).to_bits(), 3_000_000_000_000);
795    /// ```
796    #[inline]
797    pub fn div_ceil(self, rhs: Self) -> Self {
798        let q = self.0 / rhs.0;
799        let r = self.0 % rhs.0;
800        let raw = if r != 0 && (r ^ rhs.0) >= 0 { q + 1 } else { q };
801        Self(raw * Self::multiplier())
802    }
803
804    /// Return the absolute difference `|self - rhs|` as a `I128`.
805    ///
806    /// Computed as `max(self, rhs) - min(self, rhs)` so the subtraction
807    /// is always non-negative. Returns a signed `I128` rather than a `u128`
808    /// because `I128` uses signed storage. Standard panic-debug /
809    /// wrap-release applies if the difference exceeds `i128::MAX` (only
810    /// possible at the `MAX - MIN` boundary).
811    ///
812    /// # Precision
813    ///
814    /// Strict: all arithmetic is integer-only; result is bit-exact.
815    ///
816    /// # Examples
817    ///
818    /// ```
819    /// use decimal_scaled::I128s12;
820    ///
821    /// let a = I128s12::from_bits( 5_000_000_000_000); //  5.0
822    /// let b = I128s12::from_bits(-2_000_000_000_000); // -2.0
823    /// assert_eq!(a.abs_diff(b).to_bits(), 7_000_000_000_000); // 7.0
824    /// ```
825    #[inline]
826    pub fn abs_diff(self, rhs: Self) -> Self {
827        Self(self.0.max(rhs.0) - self.0.min(rhs.0))
828    }
829
830    /// Return the midpoint of `self` and `rhs` without intermediate
831    /// overflow.
832    ///
833    /// Delegates to `i128::midpoint` (stable since Rust 1.85). Rounds
834    /// toward negative infinity when the exact midpoint is not
835    /// representable, matching `i128::midpoint` semantics.
836    ///
837    /// # Precision
838    ///
839    /// Strict: all arithmetic is integer-only; result is bit-exact.
840    ///
841    /// # Examples
842    ///
843    /// ```
844    /// use decimal_scaled::I128s12;
845    ///
846    /// let a = I128s12::from_bits(1_000_000_000_000); // 1.0
847    /// let b = I128s12::from_bits(3_000_000_000_000); // 3.0
848    /// assert_eq!(a.midpoint(b).to_bits(), 2_000_000_000_000); // 2.0
849    /// ```
850    #[inline]
851    pub fn midpoint(self, rhs: Self) -> Self {
852        Self(self.0.midpoint(rhs.0))
853    }
854
855    // Float-shape compatibility predicates.
856    //
857    // I128 is a deterministic fixed-point type with no NaN, no infinity,
858    // and no subnormals. These predicates allow I128 to satisfy generic
859    // bounds that expect an f64-shaped interface.
860
861    /// Always returns `false`; `I128` has no NaN representation.
862    ///
863    /// # Precision
864    ///
865    /// Strict: all arithmetic is integer-only; result is bit-exact.
866    ///
867    /// # Examples
868    ///
869    /// ```
870    /// use decimal_scaled::I128s12;
871    ///
872    /// assert!(!I128s12::ZERO.is_nan());
873    /// assert!(!I128s12::MAX.is_nan());
874    /// ```
875    #[inline]
876    pub const fn is_nan(self) -> bool {
877        false
878    }
879
880    /// Always returns `false`; `I128` has no infinity representation.
881    ///
882    /// # Precision
883    ///
884    /// Strict: all arithmetic is integer-only; result is bit-exact.
885    ///
886    /// # Examples
887    ///
888    /// ```
889    /// use decimal_scaled::I128s12;
890    ///
891    /// assert!(!I128s12::MAX.is_infinite());
892    /// ```
893    #[inline]
894    pub const fn is_infinite(self) -> bool {
895        false
896    }
897
898    /// Always returns `true`; every `I128` value is finite.
899    ///
900    /// # Precision
901    ///
902    /// Strict: all arithmetic is integer-only; result is bit-exact.
903    ///
904    /// # Examples
905    ///
906    /// ```
907    /// use decimal_scaled::I128s12;
908    ///
909    /// assert!(I128s12::MAX.is_finite());
910    /// assert!(I128s12::MIN.is_finite());
911    /// ```
912    #[inline]
913    pub const fn is_finite(self) -> bool {
914        true
915    }
916
917    /// Returns `true` for any non-zero value.
918    ///
919    /// `I128` has no subnormal representation, so zero is the only value
920    /// that is not normal.
921    ///
922    /// # Precision
923    ///
924    /// Strict: all arithmetic is integer-only; result is bit-exact.
925    ///
926    /// # Examples
927    ///
928    /// ```
929    /// use decimal_scaled::I128s12;
930    ///
931    /// assert!(!I128s12::ZERO.is_normal());
932    /// assert!(I128s12::ONE.is_normal());
933    /// ```
934    #[inline]
935    pub const fn is_normal(self) -> bool {
936        self.0 != 0
937    }
938
939    /// Returns `true` if the value is zero.
940    ///
941    /// # Precision
942    ///
943    /// Strict: all arithmetic is integer-only; result is bit-exact.
944    ///
945    /// # Examples
946    ///
947    /// ```
948    /// use decimal_scaled::I128s12;
949    ///
950    /// assert!(I128s12::ZERO.is_zero());
951    /// assert!(!I128s12::ONE.is_zero());
952    /// ```
953    #[inline]
954    pub const fn is_zero(self) -> bool {
955        self.0 == 0
956    }
957
958    /// Returns `true` if the value is strictly greater than zero.
959    ///
960    /// # Precision
961    ///
962    /// Strict: all arithmetic is integer-only; result is bit-exact.
963    ///
964    /// # Examples
965    ///
966    /// ```
967    /// use decimal_scaled::I128s12;
968    ///
969    /// assert!(I128s12::ONE.is_positive());
970    /// assert!(!I128s12::ZERO.is_positive());
971    /// assert!(!(-I128s12::ONE).is_positive());
972    /// ```
973    #[inline]
974    pub const fn is_positive(self) -> bool {
975        self.0 > 0
976    }
977
978    /// Returns `true` if the value is strictly less than zero.
979    ///
980    /// # Precision
981    ///
982    /// Strict: all arithmetic is integer-only; result is bit-exact.
983    ///
984    /// # Examples
985    ///
986    /// ```
987    /// use decimal_scaled::I128s12;
988    ///
989    /// assert!((-I128s12::ONE).is_negative());
990    /// assert!(!I128s12::ZERO.is_negative());
991    /// assert!(!I128s12::ONE.is_negative());
992    /// ```
993    #[inline]
994    pub const fn is_negative(self) -> bool {
995        self.0 < 0
996    }
997}
998
999#[cfg(test)]
1000mod tests {
1001    use crate::core_type::I128s12;
1002
1003    /// ZERO + ZERO == ZERO.
1004    #[test]
1005    fn add_zero_to_zero_is_zero() {
1006        assert_eq!(I128s12::ZERO + I128s12::ZERO, I128s12::ZERO);
1007    }
1008
1009    /// ZERO - ZERO == ZERO.
1010    #[test]
1011    fn sub_zero_from_zero_is_zero() {
1012        assert_eq!(I128s12::ZERO - I128s12::ZERO, I128s12::ZERO);
1013    }
1014
1015    /// -ZERO == ZERO.
1016    #[test]
1017    fn neg_zero_is_zero() {
1018        assert_eq!(-I128s12::ZERO, I128s12::ZERO);
1019    }
1020
1021    /// AddAssign mutates in place.
1022    #[test]
1023    fn add_assign_zero() {
1024        let mut v = I128s12::ZERO;
1025        v += I128s12::ZERO;
1026        assert_eq!(v, I128s12::ZERO);
1027    }
1028
1029    /// SubAssign mutates in place.
1030    #[test]
1031    fn sub_assign_zero() {
1032        let mut v = I128s12::ZERO;
1033        v -= I128s12::ZERO;
1034        assert_eq!(v, I128s12::ZERO);
1035    }
1036
1037    /// Canonical claim: `(a + b) - b == a` for representative values.
1038    /// At SCALE = 12, `a = 1.5 mm` is bits `1_500_000_000_000`, `b =
1039    /// 0.25 mm` is bits `250_000_000_000`.
1040    #[test]
1041    fn add_sub_round_trip_canonical_claim() {
1042        let a = I128s12::from_bits(1_500_000_000_000);
1043        let b = I128s12::from_bits(250_000_000_000);
1044        assert_eq!((a + b) - b, a);
1045    }
1046
1047    /// Round-trip with a negative `a` to exercise sign handling.
1048    #[test]
1049    fn add_sub_round_trip_negative() {
1050        let a = I128s12::from_bits(-7_321_654_987_000);
1051        let b = I128s12::from_bits(42_000_000_000_000);
1052        assert_eq!((a + b) - b, a);
1053    }
1054
1055    /// `ONE + ONE` is the scaled bit-pattern `2 * 10^12`.
1056    #[test]
1057    fn one_plus_one_is_two_in_scaled_bits() {
1058        let two = I128s12::ONE + I128s12::ONE;
1059        // 2 * 10^12 = 2_000_000_000_000
1060        assert_eq!(two.to_bits(), 2_000_000_000_000);
1061    }
1062
1063    /// `-ONE + ONE == ZERO` -- additive inverse property.
1064    #[test]
1065    fn neg_one_plus_one_is_zero() {
1066        assert_eq!(-I128s12::ONE + I128s12::ONE, I128s12::ZERO);
1067    }
1068
1069    /// Default policy: overflow panics in debug builds. Locks the
1070    /// debug-vs-release split documented in module docs. The matching
1071    /// release-build wrap behaviour is delegated to the toolchain's
1072    /// `i128 +` semantics; testing it here would require a release-
1073    /// only test gate that's overkill for a base-ops slice.
1074    #[test]
1075    #[cfg(debug_assertions)]
1076    #[should_panic(expected = "overflow")]
1077    fn add_overflow_panics_in_debug() {
1078        let _ = I128s12::MAX + I128s12::ONE;
1079    }
1080
1081    /// Default policy: underflow panics in debug builds.
1082    #[test]
1083    #[cfg(debug_assertions)]
1084    #[should_panic(expected = "overflow")]
1085    fn sub_underflow_panics_in_debug() {
1086        let _ = I128s12::MIN - I128s12::ONE;
1087    }
1088
1089    /// Default policy: `-MIN` panics in debug builds (i128::MIN has
1090    /// no positive counterpart in two's-complement).
1091    #[test]
1092    #[cfg(debug_assertions)]
1093    #[should_panic(expected = "overflow")]
1094    fn neg_min_panics_in_debug() {
1095        let _ = -I128s12::MIN;
1096    }
1097
1098    /// AddAssign with non-zero values.
1099    #[test]
1100    fn add_assign_accumulates() {
1101        let mut v = I128s12::from_bits(100);
1102        v += I128s12::from_bits(250);
1103        assert_eq!(v.to_bits(), 350);
1104        v += I128s12::from_bits(-50);
1105        assert_eq!(v.to_bits(), 300);
1106    }
1107
1108    /// SubAssign with non-zero values.
1109    #[test]
1110    fn sub_assign_accumulates() {
1111        let mut v = I128s12::from_bits(1000);
1112        v -= I128s12::from_bits(250);
1113        assert_eq!(v.to_bits(), 750);
1114    }
1115
1116    // ── Mul / Div / Rem ──
1117
1118    /// `ONE * ONE == ONE` -- multiplicative identity.
1119    #[test]
1120    fn mul_one_one_is_one() {
1121        assert_eq!(I128s12::ONE * I128s12::ONE, I128s12::ONE);
1122    }
1123
1124    /// `ONE / ONE == ONE`.
1125    #[test]
1126    fn div_one_one_is_one() {
1127        assert_eq!(I128s12::ONE / I128s12::ONE, I128s12::ONE);
1128    }
1129
1130    /// `ZERO % ONE == ZERO`.
1131    #[test]
1132    fn rem_zero_one_is_zero() {
1133        assert_eq!(I128s12::ZERO % I128s12::ONE, I128s12::ZERO);
1134    }
1135
1136    /// `ZERO * x == ZERO` for representative non-trivial `x`.
1137    #[test]
1138    fn mul_zero_is_zero() {
1139        let x = I128s12::from_bits(1_500_000_000_000); // 1.5
1140        assert_eq!(I128s12::ZERO * x, I128s12::ZERO);
1141        assert_eq!(x * I128s12::ZERO, I128s12::ZERO);
1142    }
1143
1144    /// `ONE * x == x` for representative `x` (left and right identity).
1145    #[test]
1146    fn mul_one_is_identity() {
1147        let x = I128s12::from_bits(1_500_000_000_000); // 1.5
1148        assert_eq!(I128s12::ONE * x, x);
1149        assert_eq!(x * I128s12::ONE, x);
1150
1151        let y = I128s12::from_bits(-7_321_654_987_000); // -7.321...
1152        assert_eq!(I128s12::ONE * y, y);
1153        assert_eq!(y * I128s12::ONE, y);
1154    }
1155
1156    /// `x / ONE == x`.
1157    #[test]
1158    fn div_one_is_identity() {
1159        let x = I128s12::from_bits(1_500_000_000_000);
1160        assert_eq!(x / I128s12::ONE, x);
1161
1162        let y = I128s12::from_bits(-7_321_654_987_000);
1163        assert_eq!(y / I128s12::ONE, y);
1164    }
1165
1166    /// `x / x == ONE` for non-zero x.
1167    #[test]
1168    fn div_self_is_one() {
1169        let x = I128s12::from_bits(1_500_000_000_000); // 1.5
1170        assert_eq!(x / x, I128s12::ONE);
1171
1172        let y = I128s12::from_bits(-7_321_654_987_000);
1173        assert_eq!(y / y, I128s12::ONE);
1174
1175        // ONE / ONE already covered; a smaller value to exercise the
1176        // promotion path.
1177        let small = I128s12::from_bits(1); // 1 LSB
1178        assert_eq!(small / small, I128s12::ONE);
1179    }
1180
1181    /// `(x * 7) % x == 0` -- multiple-of property.
1182    #[test]
1183    fn rem_multiple_is_zero() {
1184        let x = I128s12::from_bits(3_500_000_000_000); // 3.5
1185        let seven = I128s12::ONE + I128s12::ONE + I128s12::ONE + I128s12::ONE
1186            + I128s12::ONE + I128s12::ONE + I128s12::ONE; // 7
1187        assert_eq!((x * seven) % x, I128s12::ZERO);
1188    }
1189
1190    /// `x % x == ZERO` for non-zero x.
1191    #[test]
1192    fn rem_self_is_zero() {
1193        let x = I128s12::from_bits(1_500_000_000_000);
1194        assert_eq!(x % x, I128s12::ZERO);
1195
1196        let y = I128s12::from_bits(-7_321_654_987_000);
1197        assert_eq!(y % y, I128s12::ZERO);
1198    }
1199
1200    /// **Headline claim**: `1.1 + 2.2 == 3.3` exactly. This is the
1201    /// distinguishing property versus binary floats (`f64`'s
1202    /// `1.1 + 2.2 == 3.3000000000000003`). Uses known scaled
1203    /// bit-patterns rather than the (not-yet-shipped) `FromStr`.
1204    #[test]
1205    fn one_point_one_plus_two_point_two_equals_three_point_three() {
1206        let one_point_one = I128s12::from_bits(1_100_000_000_000); // 1.1
1207        let two_point_two = I128s12::from_bits(2_200_000_000_000); // 2.2
1208        let three_point_three = I128s12::from_bits(3_300_000_000_000); // 3.3
1209        assert_eq!(one_point_one + two_point_two, three_point_three);
1210    }
1211
1212    /// `(a * b) / b == a` round-trip for representative non-trivial values.
1213    /// At SCALE = 12, picking moderate operands keeps `a.0 * b.0` well
1214    /// inside the i128 boundary.
1215    #[test]
1216    fn mul_round_trip_canonical_claim() {
1217        // a = 1.5, b = 2.5 -> a * b = 3.75; (3.75 / 2.5) == 1.5
1218        let a = I128s12::from_bits(1_500_000_000_000);
1219        let b = I128s12::from_bits(2_500_000_000_000);
1220        let product = a * b;
1221        assert_eq!(product, I128s12::from_bits(3_750_000_000_000));
1222        assert_eq!(product / b, a);
1223
1224        // Negative-operand round-trip.
1225        let c = I128s12::from_bits(-7_321_654_987_000);
1226        let d = I128s12::from_bits(13_000_000_000); // 0.013
1227        let cd = c * d;
1228        assert_eq!(cd / d, c);
1229    }
1230
1231    /// In-place MulAssign matches `Mul`.
1232    #[test]
1233    fn mul_assign_matches_mul() {
1234        let a = I128s12::from_bits(1_500_000_000_000);
1235        let b = I128s12::from_bits(2_500_000_000_000);
1236        let mut x = a;
1237        x *= b;
1238        assert_eq!(x, a * b);
1239    }
1240
1241    /// In-place DivAssign matches `Div`.
1242    #[test]
1243    fn div_assign_matches_div() {
1244        let a = I128s12::from_bits(3_750_000_000_000);
1245        let b = I128s12::from_bits(2_500_000_000_000);
1246        let mut x = a;
1247        x /= b;
1248        assert_eq!(x, a / b);
1249    }
1250
1251    /// In-place RemAssign matches `Rem`.
1252    #[test]
1253    fn rem_assign_matches_rem() {
1254        let a = I128s12::from_bits(7_500_000_000_000);
1255        let b = I128s12::from_bits(2_000_000_000_000); // 2.0
1256        let mut x = a;
1257        x %= b;
1258        assert_eq!(x, a % b);
1259    }
1260
1261    /// `Mul` is commutative under canonical equality.
1262    #[test]
1263    fn mul_is_commutative() {
1264        let a = I128s12::from_bits(1_500_000_000_000);
1265        let b = I128s12::from_bits(2_500_000_000_000);
1266        assert_eq!(a * b, b * a);
1267    }
1268
1269    /// `Mul` rescales correctly: 0.5 * 0.5 == 0.25 (bit-exact).
1270    #[test]
1271    fn mul_subunit_rescales_exactly() {
1272        let half = I128s12::from_bits(500_000_000_000); // 0.5
1273        let quarter = I128s12::from_bits(250_000_000_000); // 0.25
1274        assert_eq!(half * half, quarter);
1275    }
1276
1277    /// `Div` rescales correctly: 0.5 / 2 == 0.25.
1278    #[test]
1279    fn div_rescales_exactly() {
1280        let half = I128s12::from_bits(500_000_000_000); // 0.5
1281        let two = I128s12::from_bits(2_000_000_000_000); // 2.0
1282        let quarter = I128s12::from_bits(250_000_000_000); // 0.25
1283        assert_eq!(half / two, quarter);
1284    }
1285
1286    /// `Rem` matches `i128 %` truncated-toward-zero semantics.
1287    /// 5.5 % 2.0 == 1.5 (since 5.5 = 2 * 2.0 + 1.5).
1288    #[test]
1289    fn rem_truncates_toward_zero() {
1290        let a = I128s12::from_bits(5_500_000_000_000);
1291        let b = I128s12::from_bits(2_000_000_000_000);
1292        let expected = I128s12::from_bits(1_500_000_000_000);
1293        assert_eq!(a % b, expected);
1294
1295        // Negative dividend keeps the sign of the dividend (matches i128 %).
1296        let neg = I128s12::from_bits(-5_500_000_000_000);
1297        let neg_expected = I128s12::from_bits(-1_500_000_000_000);
1298        assert_eq!(neg % b, neg_expected);
1299    }
1300
1301    /// Default policy: Mul overflow panics in debug. The product
1302    /// `MAX * 2` overflows the FINAL i128 quotient (256-bit
1303    /// intermediate doesn't matter -- the result still can't fit).
1304    #[test]
1305    #[cfg(debug_assertions)]
1306    #[should_panic(expected = "overflow")]
1307    fn mul_overflow_panics_in_debug() {
1308        let two = I128s12::from_bits(2_000_000_000_000);
1309        let _ = I128s12::MAX * two;
1310    }
1311
1312    /// Widening multiply correctness: at operand magnitudes above the
1313    /// naive form's `sqrt(i128::MAX)` ~= 1.3e19 boundary, the widening
1314    /// multiply produces the correct result.
1315    ///
1316    /// Operands chosen around +/- 5e22. Expected result hand-computed:
1317    /// `(5e22 * 3e22) / 10^12 = 1.5e33`, which fits comfortably inside
1318    /// the final i128 range (i128::MAX ~= 1.7e38).
1319    #[test]
1320    fn mul_wide_operands_match_widened_form() {
1321        let a = I128s12::from_bits(50_000_000_000_000_000_000_000);
1322        let b = I128s12::from_bits(30_000_000_000_000_000_000_000);
1323        let expected = I128s12::from_bits(1_500_000_000_000_000_000_000_000_000_000_000);
1324        assert_eq!(a * b, expected);
1325        // Symmetric.
1326        assert_eq!(b * a, expected);
1327    }
1328
1329    /// Signed round-trip at wide operand magnitudes: `(a * b) / b == a`.
1330    #[test]
1331    fn mul_div_wide_round_trip() {
1332        let a = I128s12::from_bits(50_000_000_000_000_000_000_000);
1333        let b = I128s12::from_bits(30_000_000_000_000_000_000_000);
1334        let prod = a * b;
1335        // Round-trip: prod / b should recover a.
1336        assert_eq!(prod / b, a);
1337    }
1338
1339    /// Sign handling at wide operand magnitudes: mixed and same signs.
1340    #[test]
1341    fn mul_wide_negative_signs() {
1342        let a = I128s12::from_bits(50_000_000_000_000_000_000_000);
1343        let b = I128s12::from_bits(30_000_000_000_000_000_000_000);
1344        let neg_a = -a;
1345        let neg_b = -b;
1346        let pos_prod = a * b;
1347        let neg_prod = -pos_prod;
1348        assert_eq!(neg_a * b, neg_prod);
1349        assert_eq!(a * neg_b, neg_prod);
1350        assert_eq!(neg_a * neg_b, pos_prod);
1351    }
1352
1353    /// Widening divide correctness at large dividend magnitudes.
1354    #[test]
1355    fn div_wide_dividend_correct() {
1356        // a = 10^22 raw (~10^10 in scaled value at SCALE=12)
1357        let a = I128s12::from_bits(10_i128.pow(22));
1358        // b = 2 raw (sub-LSB; effectively divides by 2 * 10^-12)
1359        let b = I128s12::from_bits(2);
1360        // Expected: (a.0 * 10^12) / b.0 = (10^34) / 2 = 5e33.
1361        let expected = I128s12::from_bits(5 * 10_i128.pow(33));
1362        assert_eq!(a / b, expected);
1363    }
1364
1365    /// Widening divide round-trip: forces the numerator widening path
1366    /// because `a * 10^12` exceeds `i128::MAX`.
1367    #[test]
1368    fn div_wide_round_trip_exact() {
1369        // a = 10^27 raw: a * 10^12 = 10^39 > i128::MAX (1.7e38).
1370        // Divide by b = 100 raw: q = 10^39 / 100 = 10^37, which fits i128.
1371        let a = I128s12::from_bits(10_i128.pow(27));
1372        let b = I128s12::from_bits(100);
1373        let q = a / b;
1374        // q = (10^27 * 10^12) / 100 = 10^37 raw.
1375        let expected = I128s12::from_bits(10_i128.pow(37));
1376        assert_eq!(q, expected);
1377    }
1378
1379    /// Div at SCALE = 0: reduces to plain `i128 /`.
1380    #[test]
1381    fn div_scale_zero_matches_i128_div() {
1382        type D0 = crate::core_type::I128<0>;
1383        let a = D0::from_bits(15);
1384        let b = D0::from_bits(4);
1385        assert_eq!(a / b, D0::from_bits(3));
1386        assert_eq!((-a) / b, D0::from_bits(-3));
1387    }
1388
1389    /// Mul at SCALE = 0: reduces to plain `i128 *`.
1390    #[test]
1391    fn mul_scale_zero_matches_i128_mul() {
1392        type D0 = crate::core_type::I128<0>;
1393        let a = D0::from_bits(7);
1394        let b = D0::from_bits(11);
1395        assert_eq!(a * b, D0::from_bits(77));
1396        assert_eq!((-a) * b, D0::from_bits(-77));
1397    }
1398
1399    /// Default policy: division by zero panics.
1400    #[test]
1401    #[should_panic]
1402    fn div_by_zero_panics() {
1403        let _ = I128s12::ONE / I128s12::ZERO;
1404    }
1405
1406    /// Default policy: remainder with zero divisor panics.
1407    #[test]
1408    #[should_panic]
1409    fn rem_by_zero_panics() {
1410        let _ = I128s12::ONE % I128s12::ZERO;
1411    }
1412
1413    // ── Math methods ──
1414
1415    // ── abs ──
1416
1417    /// `abs(0) == 0`.
1418    #[test]
1419    fn abs_zero_is_zero() {
1420        assert_eq!(I128s12::ZERO.abs(), I128s12::ZERO);
1421    }
1422
1423    /// `abs(positive) == positive`.
1424    #[test]
1425    fn abs_positive_is_self() {
1426        let x = I128s12::from_bits(1_500_000_000_000); // 1.5
1427        assert_eq!(x.abs(), x);
1428    }
1429
1430    /// `abs(negative) == positive(magnitude)`.
1431    #[test]
1432    fn abs_negative_is_positive() {
1433        let neg = I128s12::from_bits(-1_500_000_000_000);
1434        let pos = I128s12::from_bits(1_500_000_000_000);
1435        assert_eq!(neg.abs(), pos);
1436    }
1437
1438    /// `abs(MIN)` panics in debug builds (no positive counterpart in
1439    /// two's-complement). Locks the panic-debug policy.
1440    #[test]
1441    #[cfg(debug_assertions)]
1442    #[should_panic(expected = "overflow")]
1443    fn abs_min_panics_in_debug() {
1444        let _ = I128s12::MIN.abs();
1445    }
1446
1447    // ── signum ──
1448
1449    /// `signum(0) == ZERO` (no sign for zero).
1450    #[test]
1451    fn signum_zero_is_zero() {
1452        assert_eq!(I128s12::ZERO.signum(), I128s12::ZERO);
1453    }
1454
1455    /// `signum(positive) == ONE`.
1456    #[test]
1457    fn signum_positive_is_one() {
1458        let x = I128s12::from_bits(1_500_000_000_000);
1459        assert_eq!(x.signum(), I128s12::ONE);
1460
1461        // Smallest positive (1 LSB).
1462        let tiny = I128s12::from_bits(1);
1463        assert_eq!(tiny.signum(), I128s12::ONE);
1464    }
1465
1466    /// `signum(negative) == -ONE`.
1467    #[test]
1468    fn signum_negative_is_neg_one() {
1469        let x = I128s12::from_bits(-1_500_000_000_000);
1470        assert_eq!(x.signum(), -I128s12::ONE);
1471
1472        let tiny_neg = I128s12::from_bits(-1);
1473        assert_eq!(tiny_neg.signum(), -I128s12::ONE);
1474    }
1475
1476    // ── floor ──
1477
1478    /// `floor(2.5) == 2.0` (positive fractional rounds down).
1479    #[test]
1480    fn floor_positive_fractional_rounds_down() {
1481        let x = I128s12::from_bits(2_500_000_000_000);
1482        let expected = I128s12::from_bits(2_000_000_000_000);
1483        assert_eq!(x.floor(), expected);
1484    }
1485
1486    /// `floor(-2.5) == -3.0` (negative fractional rounds toward
1487    /// negative infinity, NOT toward zero -- this is the key sign
1488    /// distinction from `trunc`).
1489    #[test]
1490    fn floor_negative_fractional_rounds_down_toward_neg_inf() {
1491        let x = I128s12::from_bits(-2_500_000_000_000);
1492        let expected = I128s12::from_bits(-3_000_000_000_000);
1493        assert_eq!(x.floor(), expected);
1494
1495        // Smaller fractional part: -0.5 -> -1.0
1496        let small_neg = I128s12::from_bits(-500_000_000_000);
1497        let small_expected = I128s12::from_bits(-1_000_000_000_000);
1498        assert_eq!(small_neg.floor(), small_expected);
1499    }
1500
1501    /// `floor(integer) == integer` (already at an integer boundary).
1502    #[test]
1503    fn floor_integer_unchanged() {
1504        let two = I128s12::from_bits(2_000_000_000_000);
1505        assert_eq!(two.floor(), two);
1506
1507        let neg_two = I128s12::from_bits(-2_000_000_000_000);
1508        assert_eq!(neg_two.floor(), neg_two);
1509
1510        assert_eq!(I128s12::ZERO.floor(), I128s12::ZERO);
1511    }
1512
1513    // ── ceil ──
1514
1515    /// `ceil(2.5) == 3.0`.
1516    #[test]
1517    fn ceil_positive_fractional_rounds_up() {
1518        let x = I128s12::from_bits(2_500_000_000_000);
1519        let expected = I128s12::from_bits(3_000_000_000_000);
1520        assert_eq!(x.ceil(), expected);
1521    }
1522
1523    /// `ceil(-2.5) == -2.0`. Sign distinction from `floor`: ceiling
1524    /// rounds toward positive infinity.
1525    #[test]
1526    fn ceil_negative_fractional_rounds_up_toward_pos_inf() {
1527        let x = I128s12::from_bits(-2_500_000_000_000);
1528        let expected = I128s12::from_bits(-2_000_000_000_000);
1529        assert_eq!(x.ceil(), expected);
1530
1531        // -0.5 -> 0
1532        let small_neg = I128s12::from_bits(-500_000_000_000);
1533        assert_eq!(small_neg.ceil(), I128s12::ZERO);
1534    }
1535
1536    /// `ceil(integer) == integer`.
1537    #[test]
1538    fn ceil_integer_unchanged() {
1539        let two = I128s12::from_bits(2_000_000_000_000);
1540        assert_eq!(two.ceil(), two);
1541
1542        let neg_two = I128s12::from_bits(-2_000_000_000_000);
1543        assert_eq!(neg_two.ceil(), neg_two);
1544
1545        assert_eq!(I128s12::ZERO.ceil(), I128s12::ZERO);
1546    }
1547
1548    // ── round ──
1549
1550    /// Half-away-from-zero locked policy. Property test asserting:
1551    /// - 2.5 -> 3.0  (positive half rounds up)
1552    /// - 2.4 -> 2.0
1553    /// - 2.6 -> 3.0
1554    /// - -2.5 -> -3.0  (negative half rounds away from zero, i.e. down)
1555    /// - -2.4 -> -2.0
1556    /// - -2.6 -> -3.0
1557    #[test]
1558    fn round_half_away_from_zero() {
1559        // Positive halves
1560        let two_point_five = I128s12::from_bits(2_500_000_000_000);
1561        assert_eq!(two_point_five.round(), I128s12::from_bits(3_000_000_000_000));
1562
1563        let two_point_four = I128s12::from_bits(2_400_000_000_000);
1564        assert_eq!(two_point_four.round(), I128s12::from_bits(2_000_000_000_000));
1565
1566        let two_point_six = I128s12::from_bits(2_600_000_000_000);
1567        assert_eq!(two_point_six.round(), I128s12::from_bits(3_000_000_000_000));
1568
1569        // Negative halves -- away from zero == toward neg infinity
1570        let neg_two_point_five = I128s12::from_bits(-2_500_000_000_000);
1571        assert_eq!(neg_two_point_five.round(), I128s12::from_bits(-3_000_000_000_000));
1572
1573        let neg_two_point_four = I128s12::from_bits(-2_400_000_000_000);
1574        assert_eq!(neg_two_point_four.round(), I128s12::from_bits(-2_000_000_000_000));
1575
1576        let neg_two_point_six = I128s12::from_bits(-2_600_000_000_000);
1577        assert_eq!(neg_two_point_six.round(), I128s12::from_bits(-3_000_000_000_000));
1578
1579        // Zero
1580        assert_eq!(I128s12::ZERO.round(), I128s12::ZERO);
1581    }
1582
1583    // ── trunc / fract ──
1584
1585    /// `trunc` drops the fractional part (rounds toward zero), unlike
1586    /// `floor` which rounds toward negative infinity.
1587    #[test]
1588    fn trunc_drops_fractional() {
1589        // Positive
1590        let x = I128s12::from_bits(2_500_000_000_000);
1591        assert_eq!(x.trunc(), I128s12::from_bits(2_000_000_000_000));
1592
1593        // Negative -- key sign distinction: trunc(-2.5) == -2.0
1594        // (floor(-2.5) would be -3.0)
1595        let neg = I128s12::from_bits(-2_500_000_000_000);
1596        assert_eq!(neg.trunc(), I128s12::from_bits(-2_000_000_000_000));
1597
1598        // Zero
1599        assert_eq!(I128s12::ZERO.trunc(), I128s12::ZERO);
1600
1601        // Already-integer values
1602        let two = I128s12::from_bits(2_000_000_000_000);
1603        assert_eq!(two.trunc(), two);
1604    }
1605
1606    /// `fract` keeps only the fractional part. Sign matches the sign
1607    /// of `self` (because `trunc` rounds toward zero).
1608    #[test]
1609    fn fract_keeps_only_fractional() {
1610        let x = I128s12::from_bits(2_500_000_000_000);
1611        assert_eq!(x.fract(), I128s12::from_bits(500_000_000_000));
1612
1613        // Negative: fract preserves dividend sign
1614        let neg = I128s12::from_bits(-2_500_000_000_000);
1615        assert_eq!(neg.fract(), I128s12::from_bits(-500_000_000_000));
1616
1617        // Integer values have zero fract
1618        let two = I128s12::from_bits(2_000_000_000_000);
1619        assert_eq!(two.fract(), I128s12::ZERO);
1620
1621        assert_eq!(I128s12::ZERO.fract(), I128s12::ZERO);
1622    }
1623
1624    /// Identity: `trunc(x) + fract(x) == x` for any `x`.
1625    #[test]
1626    fn trunc_plus_fract_equals_self() {
1627        let cases = [
1628            I128s12::from_bits(2_500_000_000_000),
1629            I128s12::from_bits(-2_500_000_000_000),
1630            I128s12::from_bits(7_321_654_987_000),
1631            I128s12::from_bits(-7_321_654_987_000),
1632            I128s12::ZERO,
1633            I128s12::ONE,
1634            -I128s12::ONE,
1635            I128s12::from_bits(1), // sub-LSB fractional
1636            I128s12::from_bits(-1),
1637        ];
1638        for x in cases {
1639            assert_eq!(x.trunc() + x.fract(), x, "failed for {:?}", x);
1640        }
1641    }
1642
1643    // ── min / max / clamp ──
1644
1645    /// Basic min/max/clamp on representative values.
1646    #[test]
1647    fn min_max_clamp_basic() {
1648        let a = I128s12::from_bits(1_000_000_000_000); // 1.0
1649        let b = I128s12::from_bits(2_000_000_000_000); // 2.0
1650        let c = I128s12::from_bits(3_000_000_000_000); // 3.0
1651
1652        assert_eq!(a.min(b), a);
1653        assert_eq!(b.min(a), a);
1654        assert_eq!(a.max(b), b);
1655        assert_eq!(b.max(a), b);
1656
1657        // clamp inside range -- pass through
1658        assert_eq!(b.clamp(a, c), b);
1659        // clamp below lo
1660        assert_eq!(I128s12::ZERO.clamp(a, c), a);
1661        // clamp above hi
1662        let four = I128s12::from_bits(4_000_000_000_000);
1663        assert_eq!(four.clamp(a, c), c);
1664
1665        // Negative values
1666        let neg_a = -a;
1667        let neg_b = -b;
1668        assert_eq!(neg_a.min(neg_b), neg_b); // -2.0 < -1.0
1669        assert_eq!(neg_a.max(neg_b), neg_a);
1670    }
1671
1672    // ── recip ──
1673
1674    /// `recip(2.0) == 0.5`, `recip(0.5) == 2.0`.
1675    #[test]
1676    fn recip_inverts_known_values() {
1677        let two = I128s12::from_bits(2_000_000_000_000);
1678        let half = I128s12::from_bits(500_000_000_000);
1679        assert_eq!(two.recip(), half);
1680        assert_eq!(half.recip(), two);
1681
1682        // recip of ONE is ONE
1683        assert_eq!(I128s12::ONE.recip(), I128s12::ONE);
1684
1685        // recip of -ONE is -ONE
1686        assert_eq!((-I128s12::ONE).recip(), -I128s12::ONE);
1687    }
1688
1689    /// `recip(ZERO)` panics (division by zero).
1690    #[test]
1691    #[should_panic]
1692    fn recip_zero_panics() {
1693        let _ = I128s12::ZERO.recip();
1694    }
1695
1696    // ── copysign ──
1697
1698    /// Magnitude of self, sign of `sign` arg.
1699    #[test]
1700    fn copysign_basic() {
1701        let pos = I128s12::from_bits(1_500_000_000_000);
1702        let neg = I128s12::from_bits(-1_500_000_000_000);
1703
1704        // copysign(pos, pos) == pos
1705        assert_eq!(pos.copysign(pos), pos);
1706        // copysign(pos, neg) == neg
1707        assert_eq!(pos.copysign(neg), neg);
1708        // copysign(neg, pos) == pos
1709        assert_eq!(neg.copysign(pos), pos);
1710        // copysign(neg, neg) == neg
1711        assert_eq!(neg.copysign(neg), neg);
1712    }
1713
1714    /// `copysign(x, ZERO)` -- zero is treated as positive (no negative
1715    /// zero in i128). This locks the v1 policy.
1716    #[test]
1717    fn copysign_zero() {
1718        let neg = I128s12::from_bits(-1_500_000_000_000);
1719        let pos = I128s12::from_bits(1_500_000_000_000);
1720
1721        // sign == ZERO -> positive magnitude
1722        assert_eq!(neg.copysign(I128s12::ZERO), pos);
1723        assert_eq!(pos.copysign(I128s12::ZERO), pos);
1724
1725        // self == ZERO -> result == ZERO regardless of sign
1726        assert_eq!(I128s12::ZERO.copysign(neg), I128s12::ZERO);
1727        assert_eq!(I128s12::ZERO.copysign(pos), I128s12::ZERO);
1728    }
1729
1730    // ── div_euclid / rem_euclid ──
1731
1732    /// Positive operands match plain integer division.
1733    /// 5.0 / 2.0 = 2.5; div_euclid -> floor = 2.0; rem_euclid -> 1.0.
1734    #[test]
1735    fn div_euclid_positive() {
1736        let a = I128s12::from_bits(5_000_000_000_000); // 5.0
1737        let b = I128s12::from_bits(2_000_000_000_000); // 2.0
1738
1739        let q = a.div_euclid(b);
1740        assert_eq!(q, I128s12::from_bits(2_000_000_000_000)); // 2
1741
1742        let r = a.rem_euclid(b);
1743        assert_eq!(r, I128s12::from_bits(1_000_000_000_000)); // 1
1744
1745        // Identity: q*b + r == a
1746        assert_eq!(q * b + r, a);
1747    }
1748
1749    /// Negative dividend: -5.0 div_euclid 2.0 -> -3.0 (Euclidean, with
1750    /// non-negative remainder).
1751    #[test]
1752    fn div_euclid_negative_dividend() {
1753        let a = I128s12::from_bits(-5_000_000_000_000); // -5.0
1754        let b = I128s12::from_bits(2_000_000_000_000); // 2.0
1755
1756        let q = a.div_euclid(b);
1757        // -5 = -3*2 + 1, so quotient = -3, rem = 1
1758        assert_eq!(q, I128s12::from_bits(-3_000_000_000_000));
1759
1760        let r = a.rem_euclid(b);
1761        assert_eq!(r, I128s12::from_bits(1_000_000_000_000));
1762
1763        // Identity: q*b + r == a
1764        assert_eq!(q * b + r, a);
1765    }
1766
1767    /// Negative divisor: 5.0 div_euclid -2.0 -> -2.0 (Euclidean keeps
1768    /// remainder non-negative).
1769    #[test]
1770    fn div_euclid_negative_divisor() {
1771        let a = I128s12::from_bits(5_000_000_000_000); // 5.0
1772        let b = I128s12::from_bits(-2_000_000_000_000); // -2.0
1773
1774        let q = a.div_euclid(b);
1775        assert_eq!(q, I128s12::from_bits(-2_000_000_000_000)); // -2
1776
1777        let r = a.rem_euclid(b);
1778        assert_eq!(r, I128s12::from_bits(1_000_000_000_000)); // 1 (non-negative!)
1779
1780        // Identity: q*b + r == a
1781        assert_eq!(q * b + r, a);
1782    }
1783
1784    /// Property: `(a.div_euclid(b)) * b + a.rem_euclid(b) == a` for
1785    /// representative sign combinations.
1786    #[test]
1787    fn rem_euclid_consistency_with_div_euclid() {
1788        let cases: &[(i128, i128)] = &[
1789            (5_000_000_000_000, 2_000_000_000_000),
1790            (-5_000_000_000_000, 2_000_000_000_000),
1791            (5_000_000_000_000, -2_000_000_000_000),
1792            (-5_000_000_000_000, -2_000_000_000_000),
1793            (7_321_654_987_000, 13_000_000_000),
1794            (-7_321_654_987_000, 13_000_000_000),
1795        ];
1796        for (a_bits, b_bits) in cases {
1797            let a = I128s12::from_bits(*a_bits);
1798            let b = I128s12::from_bits(*b_bits);
1799            let q = a.div_euclid(b);
1800            let r = a.rem_euclid(b);
1801            assert_eq!(q * b + r, a, "failed for a={}, b={}", a_bits, b_bits);
1802            // Remainder must be non-negative (Euclidean property)
1803            assert!(r.0 >= 0, "rem_euclid returned negative for a={}, b={}: {}",
1804                    a_bits, b_bits, r.0);
1805        }
1806    }
1807
1808    // ── div_floor / div_ceil ──
1809
1810    /// `div_floor` rounds toward negative infinity. Positive operands
1811    /// match plain truncating div.
1812    #[test]
1813    fn div_floor_basic() {
1814        // 5.0 / 2.0 -> floor(2.5) = 2.0
1815        let a = I128s12::from_bits(5_000_000_000_000);
1816        let b = I128s12::from_bits(2_000_000_000_000);
1817        assert_eq!(a.div_floor(b), I128s12::from_bits(2_000_000_000_000));
1818
1819        // -5.0 / 2.0 -> floor(-2.5) = -3.0
1820        let neg_a = I128s12::from_bits(-5_000_000_000_000);
1821        assert_eq!(neg_a.div_floor(b), I128s12::from_bits(-3_000_000_000_000));
1822
1823        // -5.0 / -2.0 -> floor(2.5) = 2.0 (sign distinction from div_euclid)
1824        let neg_b = I128s12::from_bits(-2_000_000_000_000);
1825        assert_eq!(neg_a.div_floor(neg_b), I128s12::from_bits(2_000_000_000_000));
1826
1827        // 5.0 / -2.0 -> floor(-2.5) = -3.0
1828        // (div_euclid here would be -2 because rem must be >= 0.)
1829        assert_eq!(a.div_floor(neg_b), I128s12::from_bits(-3_000_000_000_000));
1830    }
1831
1832    /// `div_ceil` rounds toward positive infinity.
1833    #[test]
1834    fn div_ceil_basic() {
1835        // 5.0 / 2.0 -> ceil(2.5) = 3.0
1836        let a = I128s12::from_bits(5_000_000_000_000);
1837        let b = I128s12::from_bits(2_000_000_000_000);
1838        assert_eq!(a.div_ceil(b), I128s12::from_bits(3_000_000_000_000));
1839
1840        // -5.0 / 2.0 -> ceil(-2.5) = -2.0
1841        let neg_a = I128s12::from_bits(-5_000_000_000_000);
1842        assert_eq!(neg_a.div_ceil(b), I128s12::from_bits(-2_000_000_000_000));
1843
1844        // 4.0 / 2.0 -> exact -> 2.0
1845        let four = I128s12::from_bits(4_000_000_000_000);
1846        assert_eq!(four.div_ceil(b), I128s12::from_bits(2_000_000_000_000));
1847    }
1848
1849    // ── abs_diff ──
1850
1851    /// `abs_diff` is commutative and non-negative.
1852    #[test]
1853    fn abs_diff_commutative() {
1854        let a = I128s12::from_bits(5_000_000_000_000); // 5.0
1855        let b = I128s12::from_bits(2_000_000_000_000); // 2.0
1856        let expected = I128s12::from_bits(3_000_000_000_000); // 3.0
1857
1858        assert_eq!(a.abs_diff(b), expected);
1859        assert_eq!(b.abs_diff(a), expected);
1860
1861        // Negative operands
1862        let neg_a = -a;
1863        let neg_b = -b;
1864        // |(-5) - (-2)| = |-3| = 3
1865        assert_eq!(neg_a.abs_diff(neg_b), expected);
1866        assert_eq!(neg_b.abs_diff(neg_a), expected);
1867
1868        // Mixed sign: |5 - (-2)| = 7
1869        let seven = I128s12::from_bits(7_000_000_000_000);
1870        assert_eq!(a.abs_diff(neg_b), seven);
1871        assert_eq!(neg_b.abs_diff(a), seven);
1872    }
1873
1874    /// `abs_diff(x, x) == 0` and `abs_diff(x, 0) == abs(x)`.
1875    #[test]
1876    fn abs_diff_zero() {
1877        let x = I128s12::from_bits(1_500_000_000_000);
1878        assert_eq!(x.abs_diff(x), I128s12::ZERO);
1879        assert_eq!(x.abs_diff(I128s12::ZERO), x.abs());
1880
1881        let neg = -x;
1882        assert_eq!(neg.abs_diff(I128s12::ZERO), x);
1883    }
1884
1885    // ── midpoint ──
1886
1887    /// Midpoint of two representative values.
1888    #[test]
1889    fn midpoint_basic() {
1890        let a = I128s12::from_bits(1_000_000_000_000); // 1.0
1891        let b = I128s12::from_bits(3_000_000_000_000); // 3.0
1892        assert_eq!(a.midpoint(b), I128s12::from_bits(2_000_000_000_000)); // 2.0
1893
1894        // Negative
1895        let neg_a = -a;
1896        let neg_b = -b;
1897        assert_eq!(neg_a.midpoint(neg_b), I128s12::from_bits(-2_000_000_000_000));
1898
1899        // Mixed sign: midpoint(-1, 1) == 0
1900        assert_eq!(neg_a.midpoint(a), I128s12::ZERO);
1901    }
1902
1903    /// Midpoint near MAX must not overflow (the whole point of using
1904    /// `i128::midpoint` over `(a + b) / 2`).
1905    #[test]
1906    fn midpoint_no_overflow_at_max() {
1907        // (MAX + MAX) / 2 == MAX, but a naive (a+b)/2 would overflow.
1908        // i128::midpoint handles this without intermediate overflow.
1909        assert_eq!(I128s12::MAX.midpoint(I128s12::MAX), I128s12::MAX);
1910        assert_eq!(I128s12::MIN.midpoint(I128s12::MIN), I128s12::MIN);
1911        // midpoint(MIN, MAX) -- delegates to i128::midpoint. The
1912        // Rust 1.95 stabilised implementation rounds the average
1913        // toward zero for signed integers (so MIN + MAX = -1 averages
1914        // to 0, not -1). Just assert it doesn't overflow / panic.
1915        let mid = I128s12::MIN.midpoint(I128s12::MAX);
1916        assert!(mid.0 == 0 || mid.0 == -1,
1917                "midpoint(MIN, MAX) should be 0 or -1, got {}", mid.0);
1918    }
1919
1920    // ── Float-shape compat predicates ──
1921
1922    #[test]
1923    fn is_nan_always_false() {
1924        assert!(!I128s12::ZERO.is_nan());
1925        assert!(!I128s12::ONE.is_nan());
1926        assert!(!I128s12::MAX.is_nan());
1927        assert!(!I128s12::MIN.is_nan());
1928    }
1929
1930    #[test]
1931    fn is_infinite_always_false() {
1932        assert!(!I128s12::ZERO.is_infinite());
1933        assert!(!I128s12::MAX.is_infinite());
1934        assert!(!I128s12::MIN.is_infinite());
1935    }
1936
1937    #[test]
1938    fn is_finite_always_true() {
1939        assert!(I128s12::ZERO.is_finite());
1940        assert!(I128s12::ONE.is_finite());
1941        assert!(I128s12::MAX.is_finite());
1942        assert!(I128s12::MIN.is_finite());
1943    }
1944
1945    #[test]
1946    fn is_normal_zero_is_false() {
1947        assert!(!I128s12::ZERO.is_normal());
1948    }
1949
1950    #[test]
1951    fn is_normal_nonzero_is_true() {
1952        assert!(I128s12::ONE.is_normal());
1953        assert!((-I128s12::ONE).is_normal());
1954        assert!(I128s12::from_bits(1).is_normal()); // smallest positive
1955        assert!(I128s12::from_bits(-1).is_normal()); // smallest negative
1956        assert!(I128s12::MAX.is_normal());
1957        assert!(I128s12::MIN.is_normal());
1958    }
1959
1960    /// is_zero / is_positive / is_negative resolve in the foundation
1961    /// slice (cheap predicates).
1962    #[test]
1963    fn is_zero_predicates() {
1964        assert!(I128s12::ZERO.is_zero());
1965        assert!(!I128s12::ZERO.is_positive());
1966        assert!(!I128s12::ZERO.is_negative());
1967
1968        assert!(!I128s12::from_bits(1).is_zero());
1969        assert!(I128s12::from_bits(1).is_positive());
1970        assert!(!I128s12::from_bits(1).is_negative());
1971
1972        assert!(!I128s12::from_bits(-1).is_zero());
1973        assert!(!I128s12::from_bits(-1).is_positive());
1974        assert!(I128s12::from_bits(-1).is_negative());
1975    }
1976}