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}