decimal_scaled/overflow_variants.rs
1//! Overflow-aware arithmetic variants for [`I128`].
2//!
3//! Provides the standard Rust explicit-overflow method families as
4//! inherent methods on [`I128<SCALE>`], covering six operations:
5//!
6//! - `add`, `sub`, `mul`, `div`, `rem` (binary)
7//! - `neg` (unary)
8//!
9//! Each operation is available in four forms:
10//!
11//! - **`checked_*`** — returns `Option<Self>`; `None` on overflow or
12//! div-by-zero; never panics.
13//! - **`wrapping_*`** — returns `Self` with two's-complement wrap on
14//! overflow; never panics. `div` and `rem` variants still panic on
15//! `rhs == ZERO` to match `i128::wrapping_div` / `i128::wrapping_rem`
16//! semantics.
17//! - **`saturating_*`** — clamps to `MAX` / `MIN` on overflow; never
18//! panics. No `saturating_rem` is provided because remainder is always
19//! bounded by `|rhs|` and the standard library does not define one for
20//! primitive integers. `div` variants still panic on `rhs == ZERO`.
21//! - **`overflowing_*`** — returns `(Self, bool)` where the `bool`
22//! indicates overflow and `Self` is the wrapping result. `div` and
23//! `rem` variants panic on `rhs == ZERO`.
24//!
25//! # Algorithm notes
26//!
27//! - `add`, `sub`, `neg`, `rem` variants delegate directly to the
28//! corresponding `i128` intrinsics on the raw storage field.
29//! - `mul` variants route through the same widening multiply-then-divide
30//! helper (`crate::mg_divide::mul_div_pow10`) as the default `Mul`
31//! operator. The intermediate product uses 256-bit arithmetic and
32//! cannot observably overflow; the only failure mode is a final `i128`
33//! quotient that does not fit.
34//! - `div` variants route through the same widening long-divide helper
35//! (`crate::mg_divide::div_pow10_div`) as the default `Div` operator.
36
37use crate::core_type::I128;
38
39impl<const SCALE: u32> I128<SCALE> {
40 // Add
41
42 /// Returns `self + rhs`, or `None` if the result overflows `i128`.
43 ///
44 /// # Precision
45 ///
46 /// Strict: operates on integer raw storage with no rounding.
47 ///
48 /// # Examples
49 ///
50 /// ```
51 /// use decimal_scaled::I128s12;
52 ///
53 /// let a = I128s12::from_bits(1_000_000_000_000); // 1.0
54 /// let b = I128s12::from_bits(2_000_000_000_000); // 2.0
55 /// assert_eq!(a.checked_add(b), Some(I128s12::from_bits(3_000_000_000_000)));
56 /// assert_eq!(I128s12::MAX.checked_add(I128s12::ONE), None);
57 /// ```
58 #[inline]
59 #[must_use]
60 pub const fn checked_add(self, rhs: Self) -> Option<Self> {
61 match self.0.checked_add(rhs.0) {
62 Some(v) => Some(Self(v)),
63 None => None,
64 }
65 }
66
67 /// Returns `self + rhs` with two's-complement wrap on overflow.
68 ///
69 /// # Precision
70 ///
71 /// Strict: operates on integer raw storage with no rounding.
72 ///
73 /// # Examples
74 ///
75 /// ```
76 /// use decimal_scaled::I128s12;
77 ///
78 /// let a = I128s12::from_bits(1_000_000_000_000); // 1.0
79 /// let b = I128s12::from_bits(2_000_000_000_000); // 2.0
80 /// assert_eq!(a.wrapping_add(b), I128s12::from_bits(3_000_000_000_000));
81 /// // Overflow wraps to MIN.
82 /// assert_eq!(I128s12::MAX.wrapping_add(I128s12::from_bits(1)), I128s12::MIN);
83 /// ```
84 #[inline]
85 #[must_use]
86 pub const fn wrapping_add(self, rhs: Self) -> Self {
87 Self(self.0.wrapping_add(rhs.0))
88 }
89
90 /// Returns `self + rhs`, clamped to `I128::MAX` or `I128::MIN` on overflow.
91 ///
92 /// # Precision
93 ///
94 /// Strict: operates on integer raw storage with no rounding.
95 ///
96 /// # Examples
97 ///
98 /// ```
99 /// use decimal_scaled::I128s12;
100 ///
101 /// let a = I128s12::from_bits(1_000_000_000_000); // 1.0
102 /// let b = I128s12::from_bits(2_000_000_000_000); // 2.0
103 /// assert_eq!(a.saturating_add(b), I128s12::from_bits(3_000_000_000_000));
104 /// assert_eq!(I128s12::MAX.saturating_add(I128s12::ONE), I128s12::MAX);
105 /// assert_eq!(I128s12::MIN.saturating_add(-I128s12::ONE), I128s12::MIN);
106 /// ```
107 #[inline]
108 #[must_use]
109 pub const fn saturating_add(self, rhs: Self) -> Self {
110 Self(self.0.saturating_add(rhs.0))
111 }
112
113 /// Returns `(self + rhs, did_overflow)` where the value is the
114 /// two's-complement wrapping result.
115 ///
116 /// # Precision
117 ///
118 /// Strict: operates on integer raw storage with no rounding.
119 ///
120 /// # Examples
121 ///
122 /// ```
123 /// use decimal_scaled::I128s12;
124 ///
125 /// let a = I128s12::from_bits(1_000_000_000_000); // 1.0
126 /// let b = I128s12::from_bits(2_000_000_000_000); // 2.0
127 /// assert_eq!(a.overflowing_add(b), (I128s12::from_bits(3_000_000_000_000), false));
128 /// assert_eq!(
129 /// I128s12::MAX.overflowing_add(I128s12::from_bits(1)),
130 /// (I128s12::MIN, true),
131 /// );
132 /// ```
133 #[inline]
134 #[must_use]
135 pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) {
136 let (v, ovf) = self.0.overflowing_add(rhs.0);
137 (Self(v), ovf)
138 }
139
140 // Sub
141
142 /// Returns `self - rhs`, or `None` if the result overflows `i128`.
143 ///
144 /// # Precision
145 ///
146 /// Strict: operates on integer raw storage with no rounding.
147 ///
148 /// # Examples
149 ///
150 /// ```
151 /// use decimal_scaled::I128s12;
152 ///
153 /// let three = I128s12::from_bits(3_000_000_000_000);
154 /// let two = I128s12::from_bits(2_000_000_000_000);
155 /// assert_eq!(three.checked_sub(two), Some(I128s12::ONE));
156 /// assert_eq!(I128s12::MIN.checked_sub(I128s12::ONE), None);
157 /// ```
158 #[inline]
159 #[must_use]
160 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
161 match self.0.checked_sub(rhs.0) {
162 Some(v) => Some(Self(v)),
163 None => None,
164 }
165 }
166
167 /// Returns `self - rhs` with two's-complement wrap on overflow.
168 ///
169 /// # Precision
170 ///
171 /// Strict: operates on integer raw storage with no rounding.
172 ///
173 /// # Examples
174 ///
175 /// ```
176 /// use decimal_scaled::I128s12;
177 ///
178 /// let three = I128s12::from_bits(3_000_000_000_000);
179 /// let two = I128s12::from_bits(2_000_000_000_000);
180 /// assert_eq!(three.wrapping_sub(two), I128s12::ONE);
181 /// // Underflow wraps to MAX.
182 /// assert_eq!(I128s12::MIN.wrapping_sub(I128s12::from_bits(1)), I128s12::MAX);
183 /// ```
184 #[inline]
185 #[must_use]
186 pub const fn wrapping_sub(self, rhs: Self) -> Self {
187 Self(self.0.wrapping_sub(rhs.0))
188 }
189
190 /// Returns `self - rhs`, clamped to `I128::MAX` or `I128::MIN` on overflow.
191 ///
192 /// # Precision
193 ///
194 /// Strict: operates on integer raw storage with no rounding.
195 ///
196 /// # Examples
197 ///
198 /// ```
199 /// use decimal_scaled::I128s12;
200 ///
201 /// let three = I128s12::from_bits(3_000_000_000_000);
202 /// let two = I128s12::from_bits(2_000_000_000_000);
203 /// assert_eq!(three.saturating_sub(two), I128s12::ONE);
204 /// assert_eq!(I128s12::MIN.saturating_sub(I128s12::ONE), I128s12::MIN);
205 /// ```
206 #[inline]
207 #[must_use]
208 pub const fn saturating_sub(self, rhs: Self) -> Self {
209 Self(self.0.saturating_sub(rhs.0))
210 }
211
212 /// Returns `(self - rhs, did_overflow)` where the value is the
213 /// two's-complement wrapping result.
214 ///
215 /// # Precision
216 ///
217 /// Strict: operates on integer raw storage with no rounding.
218 ///
219 /// # Examples
220 ///
221 /// ```
222 /// use decimal_scaled::I128s12;
223 ///
224 /// let three = I128s12::from_bits(3_000_000_000_000);
225 /// let two = I128s12::from_bits(2_000_000_000_000);
226 /// assert_eq!(three.overflowing_sub(two), (I128s12::ONE, false));
227 /// assert_eq!(
228 /// I128s12::MIN.overflowing_sub(I128s12::from_bits(1)),
229 /// (I128s12::MAX, true),
230 /// );
231 /// ```
232 #[inline]
233 #[must_use]
234 pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
235 let (v, ovf) = self.0.overflowing_sub(rhs.0);
236 (Self(v), ovf)
237 }
238
239 // Neg
240
241 /// Returns `-self`, or `None` for `I128::MIN` (whose two's-complement
242 /// negation does not fit in `i128`).
243 ///
244 /// # Precision
245 ///
246 /// Strict: operates on integer raw storage with no rounding.
247 ///
248 /// # Examples
249 ///
250 /// ```
251 /// use decimal_scaled::I128s12;
252 ///
253 /// assert_eq!(I128s12::ONE.checked_neg(), Some(-I128s12::ONE));
254 /// assert_eq!(I128s12::MIN.checked_neg(), None);
255 /// ```
256 #[inline]
257 #[must_use]
258 pub const fn checked_neg(self) -> Option<Self> {
259 match self.0.checked_neg() {
260 Some(v) => Some(Self(v)),
261 None => None,
262 }
263 }
264
265 /// Returns `-self` with two's-complement wrap. For `I128::MIN`
266 /// this returns `I128::MIN` (matches `i128::wrapping_neg`).
267 ///
268 /// # Precision
269 ///
270 /// Strict: operates on integer raw storage with no rounding.
271 ///
272 /// # Examples
273 ///
274 /// ```
275 /// use decimal_scaled::I128s12;
276 ///
277 /// assert_eq!(I128s12::ONE.wrapping_neg(), -I128s12::ONE);
278 /// assert_eq!(I128s12::MIN.wrapping_neg(), I128s12::MIN);
279 /// ```
280 #[inline]
281 #[must_use]
282 pub const fn wrapping_neg(self) -> Self {
283 Self(self.0.wrapping_neg())
284 }
285
286 /// Returns `-self`, clamped to `I128::MAX` for `I128::MIN`
287 /// (matches `i128::saturating_neg`).
288 ///
289 /// # Precision
290 ///
291 /// Strict: operates on integer raw storage with no rounding.
292 ///
293 /// # Examples
294 ///
295 /// ```
296 /// use decimal_scaled::I128s12;
297 ///
298 /// assert_eq!(I128s12::ONE.saturating_neg(), -I128s12::ONE);
299 /// assert_eq!(I128s12::MIN.saturating_neg(), I128s12::MAX);
300 /// ```
301 #[inline]
302 #[must_use]
303 pub const fn saturating_neg(self) -> Self {
304 Self(self.0.saturating_neg())
305 }
306
307 /// Returns `(-self, did_overflow)`. Overflow occurs only for
308 /// `I128::MIN`, in which case the wrapping result is `I128::MIN`.
309 ///
310 /// # Precision
311 ///
312 /// Strict: operates on integer raw storage with no rounding.
313 ///
314 /// # Examples
315 ///
316 /// ```
317 /// use decimal_scaled::I128s12;
318 ///
319 /// assert_eq!(I128s12::ONE.overflowing_neg(), (-I128s12::ONE, false));
320 /// assert_eq!(I128s12::MIN.overflowing_neg(), (I128s12::MIN, true));
321 /// ```
322 #[inline]
323 #[must_use]
324 pub const fn overflowing_neg(self) -> (Self, bool) {
325 let (v, ovf) = self.0.overflowing_neg();
326 (Self(v), ovf)
327 }
328
329 // Mul (rescale-aware via widening multiply-then-divide)
330 //
331 // All mul variants share the same widening boundary as the default
332 // `Mul` operator. The helper `mul_div_pow10::<SCALE>` returns
333 // `Some(q)` when the final i128 quotient fits, or `None` on overflow.
334 // The four variants differ only in how they handle that `None`.
335
336 /// Returns `self * rhs`, or `None` if the rescaled product does not
337 /// fit in `i128`.
338 ///
339 /// The intermediate product is computed with 256-bit arithmetic and
340 /// cannot itself overflow. The only failure mode is a final `i128`
341 /// quotient that exceeds the storage range.
342 ///
343 /// # Precision
344 ///
345 /// Strict: the result is truncated (not rounded) toward zero during
346 /// the scale-restoring divide, identical to the default `*` operator.
347 ///
348 /// # Examples
349 ///
350 /// ```
351 /// use decimal_scaled::I128s12;
352 ///
353 /// let half = I128s12::from_bits(500_000_000_000); // 0.5
354 /// assert_eq!(half.checked_mul(half), Some(I128s12::from_bits(250_000_000_000)));
355 /// assert_eq!(I128s12::MAX.checked_mul(I128s12::from_bits(2_000_000_000_000)), None);
356 /// ```
357 #[inline]
358 #[must_use]
359 pub fn checked_mul(self, rhs: Self) -> Option<Self> {
360 crate::mg_divide::mul_div_pow10::<SCALE>(self.0, rhs.0).map(Self)
361 }
362
363 /// Returns `self * rhs` with two's-complement wrap when the rescaled
364 /// product does not fit in `i128`.
365 ///
366 /// On overflow, falls back to `(a.wrapping_mul(b)).wrapping_div(multiplier())`.
367 /// The exact bit pattern of the wrapping result at extreme magnitudes
368 /// is an implementation detail; only the no-panic contract is guaranteed.
369 ///
370 /// # Precision
371 ///
372 /// Strict: truncates toward zero during the scale-restoring divide.
373 ///
374 /// # Examples
375 ///
376 /// ```
377 /// use decimal_scaled::I128s12;
378 ///
379 /// let half = I128s12::from_bits(500_000_000_000); // 0.5
380 /// assert_eq!(half.wrapping_mul(half), I128s12::from_bits(250_000_000_000));
381 /// // Overflow does not panic.
382 /// let _ = I128s12::MAX.wrapping_mul(I128s12::from_bits(2_000_000_000_000));
383 /// ```
384 #[inline]
385 #[must_use]
386 pub fn wrapping_mul(self, rhs: Self) -> Self {
387 match crate::mg_divide::mul_div_pow10::<SCALE>(self.0, rhs.0) {
388 Some(q) => Self(q),
389 None => Self(
390 self.0
391 .wrapping_mul(rhs.0)
392 .wrapping_div(Self::multiplier()),
393 ),
394 }
395 }
396
397 /// Returns `self * rhs`, clamped to `I128::MAX` or `I128::MIN` on overflow.
398 ///
399 /// The clamp direction is determined by the XOR of operand signs:
400 /// same-sign operands saturate to `MAX`; mixed-sign operands saturate
401 /// to `MIN`.
402 ///
403 /// # Precision
404 ///
405 /// Strict: truncates toward zero during the scale-restoring divide.
406 ///
407 /// # Examples
408 ///
409 /// ```
410 /// use decimal_scaled::I128s12;
411 ///
412 /// let two = I128s12::from_bits(2_000_000_000_000); // 2.0
413 /// assert_eq!(I128s12::MAX.saturating_mul(two), I128s12::MAX);
414 /// assert_eq!(I128s12::MAX.saturating_mul(-two), I128s12::MIN);
415 /// ```
416 #[inline]
417 #[must_use]
418 pub fn saturating_mul(self, rhs: Self) -> Self {
419 match crate::mg_divide::mul_div_pow10::<SCALE>(self.0, rhs.0) {
420 Some(q) => Self(q),
421 None => {
422 // Clamp direction: negative result iff exactly one operand
423 // is negative (a zero operand cannot produce overflow).
424 let neg = (self.0 < 0) ^ (rhs.0 < 0);
425 if neg { Self::MIN } else { Self::MAX }
426 }
427 }
428 }
429
430 /// Returns `(self * rhs, did_overflow)` where the value is the
431 /// wrapping result when overflow occurs.
432 ///
433 /// # Precision
434 ///
435 /// Strict: truncates toward zero during the scale-restoring divide.
436 ///
437 /// # Examples
438 ///
439 /// ```
440 /// use decimal_scaled::I128s12;
441 ///
442 /// let half = I128s12::from_bits(500_000_000_000); // 0.5
443 /// assert_eq!(half.overflowing_mul(half), (I128s12::from_bits(250_000_000_000), false));
444 /// let (_, ovf) = I128s12::MAX.overflowing_mul(I128s12::from_bits(2_000_000_000_000));
445 /// assert!(ovf);
446 /// ```
447 #[inline]
448 #[must_use]
449 pub fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
450 match crate::mg_divide::mul_div_pow10::<SCALE>(self.0, rhs.0) {
451 Some(q) => (Self(q), false),
452 None => (self.wrapping_mul(rhs), true),
453 }
454 }
455
456 // Div (rescale-aware via widening long-divide)
457 //
458 // All div variants use `div_pow10_div` as the default `Div` operator.
459 // Div-by-zero policy:
460 // - `checked_div(_, ZERO)` returns `None`.
461 // - `wrapping_div`, `saturating_div`, `overflowing_div` panic on
462 // `rhs == ZERO`, matching their `i128` counterparts.
463
464 /// Returns `self / rhs`, or `None` on division by zero or if the
465 /// rescaled quotient does not fit in `i128`.
466 ///
467 /// The only finite-operand overflow case is
468 /// `I128::MIN / NEG_ONE` (storage negation of `i128::MIN` overflows).
469 ///
470 /// # Precision
471 ///
472 /// Strict: the widening divide truncates toward zero, identical to
473 /// the default `/` operator.
474 ///
475 /// # Examples
476 ///
477 /// ```
478 /// use decimal_scaled::I128s12;
479 ///
480 /// let six = I128s12::from_bits(6_000_000_000_000); // 6.0
481 /// let two = I128s12::from_bits(2_000_000_000_000); // 2.0
482 /// assert_eq!(six.checked_div(two), Some(I128s12::from_bits(3_000_000_000_000)));
483 /// assert_eq!(I128s12::ONE.checked_div(I128s12::ZERO), None);
484 /// ```
485 #[inline]
486 #[must_use]
487 pub fn checked_div(self, rhs: Self) -> Option<Self> {
488 crate::mg_divide::div_pow10_div::<SCALE>(self.0, rhs.0).map(Self)
489 }
490
491 /// Returns `self / rhs` with two's-complement wrap when the rescaled
492 /// quotient does not fit in `i128`.
493 ///
494 /// On overflow, falls back to
495 /// `(a.wrapping_mul(multiplier())).wrapping_div(b)`.
496 ///
497 /// # Precision
498 ///
499 /// Strict: truncates toward zero.
500 ///
501 /// # Panics
502 ///
503 /// Panics on `rhs == ZERO` (matches `i128::wrapping_div`).
504 ///
505 /// # Examples
506 ///
507 /// ```
508 /// use decimal_scaled::I128s12;
509 ///
510 /// let six = I128s12::from_bits(6_000_000_000_000); // 6.0
511 /// let two = I128s12::from_bits(2_000_000_000_000); // 2.0
512 /// assert_eq!(six.wrapping_div(two), I128s12::from_bits(3_000_000_000_000));
513 /// ```
514 #[inline]
515 #[must_use]
516 pub fn wrapping_div(self, rhs: Self) -> Self {
517 if rhs.0 == 0 {
518 panic!("attempt to divide by zero");
519 }
520 match crate::mg_divide::div_pow10_div::<SCALE>(self.0, rhs.0) {
521 Some(q) => Self(q),
522 None => Self(
523 self.0
524 .wrapping_mul(Self::multiplier())
525 .wrapping_div(rhs.0),
526 ),
527 }
528 }
529
530 /// Returns `self / rhs`, clamped to `I128::MAX` or `I128::MIN` on
531 /// overflow.
532 ///
533 /// The clamp direction is determined by the XOR of operand signs,
534 /// because the scale multiplier is always positive.
535 ///
536 /// # Precision
537 ///
538 /// Strict: truncates toward zero.
539 ///
540 /// # Panics
541 ///
542 /// Panics on `rhs == ZERO` (matches `i128::saturating_div`).
543 ///
544 /// # Examples
545 ///
546 /// ```
547 /// use decimal_scaled::I128s12;
548 ///
549 /// let six = I128s12::from_bits(6_000_000_000_000); // 6.0
550 /// let two = I128s12::from_bits(2_000_000_000_000); // 2.0
551 /// assert_eq!(six.saturating_div(two), I128s12::from_bits(3_000_000_000_000));
552 /// // MAX / 0.5 overflows; both positive so clamp to MAX.
553 /// assert_eq!(I128s12::MAX.saturating_div(I128s12::from_bits(500_000_000_000)), I128s12::MAX);
554 /// ```
555 #[inline]
556 #[must_use]
557 pub fn saturating_div(self, rhs: Self) -> Self {
558 if rhs.0 == 0 {
559 panic!("attempt to divide by zero");
560 }
561 match crate::mg_divide::div_pow10_div::<SCALE>(self.0, rhs.0) {
562 Some(q) => Self(q),
563 None => {
564 // Clamp direction: negative iff exactly one operand is negative.
565 let neg = (self.0 < 0) ^ (rhs.0 < 0);
566 if neg { Self::MIN } else { Self::MAX }
567 }
568 }
569 }
570
571 /// Returns `(self / rhs, did_overflow)` where the value is the
572 /// wrapping result when overflow occurs.
573 ///
574 /// # Precision
575 ///
576 /// Strict: truncates toward zero.
577 ///
578 /// # Panics
579 ///
580 /// Panics on `rhs == ZERO` (matches `i128::overflowing_div`).
581 ///
582 /// # Examples
583 ///
584 /// ```
585 /// use decimal_scaled::I128s12;
586 ///
587 /// let six = I128s12::from_bits(6_000_000_000_000); // 6.0
588 /// let two = I128s12::from_bits(2_000_000_000_000); // 2.0
589 /// assert_eq!(six.overflowing_div(two), (I128s12::from_bits(3_000_000_000_000), false));
590 /// let half = I128s12::from_bits(500_000_000_000); // 0.5
591 /// let (_, ovf) = I128s12::MAX.overflowing_div(half);
592 /// assert!(ovf);
593 /// ```
594 #[inline]
595 #[must_use]
596 pub fn overflowing_div(self, rhs: Self) -> (Self, bool) {
597 if rhs.0 == 0 {
598 panic!("attempt to divide by zero");
599 }
600 match crate::mg_divide::div_pow10_div::<SCALE>(self.0, rhs.0) {
601 Some(q) => (Self(q), false),
602 None => (self.wrapping_div(rhs), true),
603 }
604 }
605
606 // Rem (no rescale; both operands share SCALE)
607 //
608 // Because both operands share `SCALE`, `(a * 10^S) % (b * 10^S)`
609 // already lives in `value * 10^S` form with no further rescaling
610 // needed. Delegates directly to `i128` intrinsics.
611 //
612 // No `saturating_rem` is provided: the result is always bounded by
613 // `|rhs|`, so it cannot exceed the storage range when both operands
614 // fit. The standard library does not define one for primitive integers.
615
616 /// Returns `self % rhs`, or `None` on `rhs == ZERO` or for the
617 /// storage-level overflow case `I128::MIN % from_bits(-1)`
618 /// (matches `i128::checked_rem`).
619 ///
620 /// # Precision
621 ///
622 /// Strict: operates on integer raw storage with no rounding.
623 ///
624 /// # Examples
625 ///
626 /// ```
627 /// use decimal_scaled::I128s12;
628 ///
629 /// let a = I128s12::from_bits(5_500_000_000_000); // 5.5
630 /// let two = I128s12::from_bits(2_000_000_000_000); // 2.0
631 /// assert_eq!(a.checked_rem(two), Some(I128s12::from_bits(1_500_000_000_000)));
632 /// assert_eq!(I128s12::ONE.checked_rem(I128s12::ZERO), None);
633 /// ```
634 #[inline]
635 #[must_use]
636 pub const fn checked_rem(self, rhs: Self) -> Option<Self> {
637 match self.0.checked_rem(rhs.0) {
638 Some(v) => Some(Self(v)),
639 None => None,
640 }
641 }
642
643 /// Returns `self % rhs`. For the storage-level overflow case
644 /// `I128::MIN.wrapping_rem(from_bits(-1))`, returns `ZERO`
645 /// (matches `i128::wrapping_rem`).
646 ///
647 /// # Precision
648 ///
649 /// Strict: operates on integer raw storage with no rounding.
650 ///
651 /// # Panics
652 ///
653 /// Panics on `rhs == ZERO` (matches `i128::wrapping_rem`).
654 ///
655 /// # Examples
656 ///
657 /// ```
658 /// use decimal_scaled::I128s12;
659 ///
660 /// let a = I128s12::from_bits(5_500_000_000_000); // 5.5
661 /// let two = I128s12::from_bits(2_000_000_000_000); // 2.0
662 /// assert_eq!(a.wrapping_rem(two), I128s12::from_bits(1_500_000_000_000));
663 /// ```
664 #[inline]
665 #[must_use]
666 pub const fn wrapping_rem(self, rhs: Self) -> Self {
667 Self(self.0.wrapping_rem(rhs.0))
668 }
669
670 /// Returns `(self % rhs, did_overflow)`. Overflow (`did_overflow == true`)
671 /// occurs only for the storage-level case `I128::MIN % from_bits(-1)`,
672 /// in which case the wrapping result is `ZERO`.
673 ///
674 /// # Precision
675 ///
676 /// Strict: operates on integer raw storage with no rounding.
677 ///
678 /// # Panics
679 ///
680 /// Panics on `rhs == ZERO` (matches `i128::overflowing_rem`).
681 ///
682 /// # Examples
683 ///
684 /// ```
685 /// use decimal_scaled::I128s12;
686 ///
687 /// let a = I128s12::from_bits(5_500_000_000_000); // 5.5
688 /// let two = I128s12::from_bits(2_000_000_000_000); // 2.0
689 /// assert_eq!(a.overflowing_rem(two), (I128s12::from_bits(1_500_000_000_000), false));
690 /// let neg_one_lsb = I128s12::from_bits(-1);
691 /// assert_eq!(I128s12::MIN.overflowing_rem(neg_one_lsb), (I128s12::ZERO, true));
692 /// ```
693 #[inline]
694 #[must_use]
695 pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) {
696 let (v, ovf) = self.0.overflowing_rem(rhs.0);
697 (Self(v), ovf)
698 }
699}
700
701#[cfg(test)]
702#[allow(clippy::arithmetic_side_effects)]
703mod tests {
704 use crate::core_type::{I128, I128s12};
705
706 /// Returns `-ONE` as a convenience value.
707 fn neg_one() -> I128s12 {
708 -I128s12::ONE
709 }
710
711 /// Returns `2.0` in `I128s12` canonical form.
712 fn two() -> I128s12 {
713 I128s12::from_bits(2_000_000_000_000)
714 }
715
716 /// Returns `3.0` in `I128s12` canonical form.
717 fn three() -> I128s12 {
718 I128s12::from_bits(3_000_000_000_000)
719 }
720
721 // Add variants
722
723 #[test]
724 fn checked_add_normal() {
725 assert_eq!(I128s12::ONE.checked_add(I128s12::ONE), Some(two()));
726 }
727
728 #[test]
729 fn checked_add_overflow_returns_none() {
730 // MAX + ONE overflows (MAX is i128::MAX raw; ONE is 10^SCALE raw).
731 assert_eq!(I128s12::MAX.checked_add(I128s12::ONE), None);
732 // Boundary: MAX + 1 LSB also overflows.
733 assert_eq!(
734 I128s12::MAX.checked_add(I128s12::from_bits(1)),
735 None
736 );
737 }
738
739 #[test]
740 fn checked_add_negative_overflow_returns_none() {
741 assert_eq!(I128s12::MIN.checked_add(neg_one()), None);
742 // Boundary: MIN + (-1 LSB) also overflows.
743 assert_eq!(
744 I128s12::MIN.checked_add(I128s12::from_bits(-1)),
745 None
746 );
747 }
748
749 #[test]
750 fn wrapping_add_normal_matches_op() {
751 assert_eq!(I128s12::ONE.wrapping_add(I128s12::ONE), two());
752 }
753
754 #[test]
755 fn wrapping_add_overflow_wraps_to_min() {
756 // MAX + 1 LSB wraps to MIN under two's-complement.
757 assert_eq!(
758 I128s12::MAX.wrapping_add(I128s12::from_bits(1)),
759 I128s12::MIN
760 );
761 }
762
763 #[test]
764 fn wrapping_add_negative_overflow_wraps_to_max() {
765 // MIN + (-1 LSB) wraps to MAX.
766 assert_eq!(
767 I128s12::MIN.wrapping_add(I128s12::from_bits(-1)),
768 I128s12::MAX
769 );
770 }
771
772 #[test]
773 fn saturating_add_normal_matches_op() {
774 assert_eq!(I128s12::ONE.saturating_add(I128s12::ONE), two());
775 }
776
777 #[test]
778 fn saturating_add_overflow_clamps_to_max() {
779 assert_eq!(I128s12::MAX.saturating_add(I128s12::ONE), I128s12::MAX);
780 }
781
782 #[test]
783 fn saturating_add_negative_overflow_clamps_to_min() {
784 assert_eq!(I128s12::MIN.saturating_add(neg_one()), I128s12::MIN);
785 }
786
787 #[test]
788 fn overflowing_add_normal_no_overflow() {
789 assert_eq!(
790 I128s12::ONE.overflowing_add(I128s12::ONE),
791 (two(), false)
792 );
793 }
794
795 #[test]
796 fn overflowing_add_overflow_flagged() {
797 // MAX + 1 LSB wraps exactly to MIN; overflow flag is set.
798 assert_eq!(
799 I128s12::MAX.overflowing_add(I128s12::from_bits(1)),
800 (I128s12::MIN, true)
801 );
802 }
803
804 #[test]
805 fn overflowing_add_negative_overflow_flagged() {
806 // MIN + (-1 LSB) wraps exactly to MAX.
807 assert_eq!(
808 I128s12::MIN.overflowing_add(I128s12::from_bits(-1)),
809 (I128s12::MAX, true)
810 );
811 }
812
813 // Sub variants
814
815 #[test]
816 fn checked_sub_normal() {
817 assert_eq!(three().checked_sub(I128s12::ONE), Some(two()));
818 }
819
820 #[test]
821 fn checked_sub_underflow_returns_none() {
822 assert_eq!(I128s12::MIN.checked_sub(I128s12::ONE), None);
823 }
824
825 #[test]
826 fn checked_sub_positive_overflow_returns_none() {
827 // MAX - (-ONE) = MAX + ONE -> overflows.
828 assert_eq!(I128s12::MAX.checked_sub(neg_one()), None);
829 }
830
831 #[test]
832 fn wrapping_sub_normal() {
833 assert_eq!(three().wrapping_sub(I128s12::ONE), two());
834 }
835
836 #[test]
837 fn wrapping_sub_underflow_wraps_to_max() {
838 // MIN - 1 LSB wraps exactly to MAX.
839 assert_eq!(
840 I128s12::MIN.wrapping_sub(I128s12::from_bits(1)),
841 I128s12::MAX
842 );
843 }
844
845 #[test]
846 fn saturating_sub_normal() {
847 assert_eq!(three().saturating_sub(I128s12::ONE), two());
848 }
849
850 #[test]
851 fn saturating_sub_underflow_clamps_to_min() {
852 assert_eq!(I128s12::MIN.saturating_sub(I128s12::ONE), I128s12::MIN);
853 }
854
855 #[test]
856 fn saturating_sub_overflow_clamps_to_max() {
857 // MAX - (-ONE) saturates to MAX.
858 assert_eq!(I128s12::MAX.saturating_sub(neg_one()), I128s12::MAX);
859 }
860
861 #[test]
862 fn overflowing_sub_normal() {
863 assert_eq!(
864 three().overflowing_sub(I128s12::ONE),
865 (two(), false)
866 );
867 }
868
869 #[test]
870 fn overflowing_sub_underflow_flagged() {
871 // MIN - 1 LSB wraps exactly to MAX.
872 assert_eq!(
873 I128s12::MIN.overflowing_sub(I128s12::from_bits(1)),
874 (I128s12::MAX, true)
875 );
876 }
877
878 // Neg variants
879
880 #[test]
881 fn checked_neg_normal() {
882 assert_eq!(I128s12::ONE.checked_neg(), Some(neg_one()));
883 assert_eq!(neg_one().checked_neg(), Some(I128s12::ONE));
884 assert_eq!(I128s12::ZERO.checked_neg(), Some(I128s12::ZERO));
885 }
886
887 #[test]
888 fn checked_neg_min_returns_none() {
889 assert_eq!(I128s12::MIN.checked_neg(), None);
890 }
891
892 #[test]
893 fn checked_neg_max_succeeds() {
894 // MAX = i128::MAX, -MAX = i128::MIN + 1, fits.
895 let neg_max = I128s12::from_bits(-i128::MAX);
896 assert_eq!(I128s12::MAX.checked_neg(), Some(neg_max));
897 }
898
899 #[test]
900 fn wrapping_neg_normal() {
901 assert_eq!(I128s12::ONE.wrapping_neg(), neg_one());
902 assert_eq!(I128s12::ZERO.wrapping_neg(), I128s12::ZERO);
903 }
904
905 #[test]
906 fn wrapping_neg_min_returns_min() {
907 // -i128::MIN wraps to i128::MIN under two's-complement.
908 assert_eq!(I128s12::MIN.wrapping_neg(), I128s12::MIN);
909 }
910
911 #[test]
912 fn saturating_neg_normal() {
913 assert_eq!(I128s12::ONE.saturating_neg(), neg_one());
914 assert_eq!(I128s12::ZERO.saturating_neg(), I128s12::ZERO);
915 }
916
917 #[test]
918 fn saturating_neg_min_returns_max() {
919 assert_eq!(I128s12::MIN.saturating_neg(), I128s12::MAX);
920 }
921
922 #[test]
923 fn overflowing_neg_normal() {
924 assert_eq!(
925 I128s12::ONE.overflowing_neg(),
926 (neg_one(), false)
927 );
928 assert_eq!(
929 I128s12::ZERO.overflowing_neg(),
930 (I128s12::ZERO, false)
931 );
932 }
933
934 #[test]
935 fn overflowing_neg_min_flagged() {
936 assert_eq!(
937 I128s12::MIN.overflowing_neg(),
938 (I128s12::MIN, true)
939 );
940 }
941
942 // Mul variants
943
944 #[test]
945 fn checked_mul_normal() {
946 let half = I128s12::from_bits(500_000_000_000);
947 let quarter = I128s12::from_bits(250_000_000_000);
948 assert_eq!(half.checked_mul(half), Some(quarter));
949 }
950
951 #[test]
952 fn checked_mul_zero() {
953 assert_eq!(I128s12::MAX.checked_mul(I128s12::ZERO), Some(I128s12::ZERO));
954 assert_eq!(I128s12::ZERO.checked_mul(I128s12::ZERO), Some(I128s12::ZERO));
955 }
956
957 #[test]
958 fn checked_mul_one_identity() {
959 let v = I128s12::from_bits(7_500_000_000_000); // 7.5
960 assert_eq!(v.checked_mul(I128s12::ONE), Some(v));
961 assert_eq!(I128s12::ONE.checked_mul(v), Some(v));
962 }
963
964 #[test]
965 fn checked_mul_overflow_returns_none() {
966 // MAX * 2.0 overflows the final i128 quotient.
967 assert_eq!(I128s12::MAX.checked_mul(two()), None);
968 }
969
970 #[test]
971 fn checked_mul_min_overflow_returns_none() {
972 // MIN * 2.0 overflows.
973 assert_eq!(I128s12::MIN.checked_mul(two()), None);
974 }
975
976 #[test]
977 fn wrapping_mul_normal() {
978 let half = I128s12::from_bits(500_000_000_000);
979 let quarter = I128s12::from_bits(250_000_000_000);
980 assert_eq!(half.wrapping_mul(half), quarter);
981 }
982
983 #[test]
984 fn wrapping_mul_overflow_does_not_panic() {
985 // Verify it does not panic; the exact bit pattern is unspecified.
986 let _ = I128s12::MAX.wrapping_mul(two());
987 let _ = I128s12::MIN.wrapping_mul(two());
988 }
989
990 #[test]
991 fn saturating_mul_normal() {
992 let half = I128s12::from_bits(500_000_000_000);
993 let quarter = I128s12::from_bits(250_000_000_000);
994 assert_eq!(half.saturating_mul(half), quarter);
995 }
996
997 #[test]
998 fn saturating_mul_positive_overflow_clamps_to_max() {
999 // MAX * 2.0 (both positive) saturates to MAX.
1000 assert_eq!(I128s12::MAX.saturating_mul(two()), I128s12::MAX);
1001 }
1002
1003 #[test]
1004 fn saturating_mul_negative_overflow_clamps_to_min() {
1005 // MAX * (-2.0) (mixed sign) saturates to MIN.
1006 assert_eq!(
1007 I128s12::MAX.saturating_mul(-two()),
1008 I128s12::MIN
1009 );
1010 }
1011
1012 #[test]
1013 fn saturating_mul_min_times_two_clamps_to_min() {
1014 // MIN * 2.0 (MIN negative, 2 positive) saturates to MIN.
1015 assert_eq!(I128s12::MIN.saturating_mul(two()), I128s12::MIN);
1016 }
1017
1018 #[test]
1019 fn saturating_mul_min_times_neg_two_clamps_to_max() {
1020 // MIN * -2.0 (both negative) saturates to MAX.
1021 assert_eq!(I128s12::MIN.saturating_mul(-two()), I128s12::MAX);
1022 }
1023
1024 #[test]
1025 fn overflowing_mul_normal_no_overflow() {
1026 let half = I128s12::from_bits(500_000_000_000);
1027 let quarter = I128s12::from_bits(250_000_000_000);
1028 assert_eq!(half.overflowing_mul(half), (quarter, false));
1029 }
1030
1031 #[test]
1032 fn overflowing_mul_overflow_flagged() {
1033 let (_, ovf) = I128s12::MAX.overflowing_mul(two());
1034 assert!(ovf);
1035 }
1036
1037 // Div variants
1038
1039 #[test]
1040 fn checked_div_normal() {
1041 // 6.0 / 2.0 = 3.0
1042 let six = I128s12::from_bits(6_000_000_000_000);
1043 assert_eq!(six.checked_div(two()), Some(three()));
1044 }
1045
1046 #[test]
1047 fn checked_div_by_zero_returns_none() {
1048 assert_eq!(I128s12::ONE.checked_div(I128s12::ZERO), None);
1049 }
1050
1051 #[test]
1052 fn checked_div_overflow_returns_none() {
1053 // MAX / 0.5 = 2 * MAX -> overflows the final quotient.
1054 let half = I128s12::from_bits(500_000_000_000);
1055 assert_eq!(I128s12::MAX.checked_div(half), None);
1056 }
1057
1058 #[test]
1059 fn checked_div_negative_normal() {
1060 let neg_six = I128s12::from_bits(-6_000_000_000_000);
1061 assert_eq!(neg_six.checked_div(two()), Some(-three()));
1062 }
1063
1064 #[test]
1065 fn wrapping_div_normal() {
1066 let six = I128s12::from_bits(6_000_000_000_000);
1067 assert_eq!(six.wrapping_div(two()), three());
1068 }
1069
1070 #[test]
1071 #[should_panic(expected = "attempt to divide by zero")]
1072 fn wrapping_div_by_zero_panics() {
1073 let _ = I128s12::ONE.wrapping_div(I128s12::ZERO);
1074 }
1075
1076 #[test]
1077 fn wrapping_div_overflow_does_not_panic() {
1078 // Verify it does not panic; the exact result is unspecified.
1079 let half = I128s12::from_bits(500_000_000_000);
1080 let _ = I128s12::MAX.wrapping_div(half);
1081 }
1082
1083 #[test]
1084 fn saturating_div_normal() {
1085 let six = I128s12::from_bits(6_000_000_000_000);
1086 assert_eq!(six.saturating_div(two()), three());
1087 }
1088
1089 #[test]
1090 #[should_panic(expected = "attempt to divide by zero")]
1091 fn saturating_div_by_zero_panics() {
1092 let _ = I128s12::ONE.saturating_div(I128s12::ZERO);
1093 }
1094
1095 #[test]
1096 fn saturating_div_overflow_clamps_to_max() {
1097 // MAX / 0.5 (both positive) saturates to MAX.
1098 let half = I128s12::from_bits(500_000_000_000);
1099 assert_eq!(I128s12::MAX.saturating_div(half), I128s12::MAX);
1100 }
1101
1102 #[test]
1103 fn saturating_div_negative_overflow_clamps_to_min() {
1104 // MAX / -0.5 (mixed sign) saturates to MIN.
1105 let neg_half = I128s12::from_bits(-500_000_000_000);
1106 assert_eq!(
1107 I128s12::MAX.saturating_div(neg_half),
1108 I128s12::MIN
1109 );
1110 }
1111
1112 #[test]
1113 fn overflowing_div_normal() {
1114 let six = I128s12::from_bits(6_000_000_000_000);
1115 assert_eq!(six.overflowing_div(two()), (three(), false));
1116 }
1117
1118 #[test]
1119 fn overflowing_div_overflow_flagged() {
1120 let half = I128s12::from_bits(500_000_000_000);
1121 let (_, ovf) = I128s12::MAX.overflowing_div(half);
1122 assert!(ovf);
1123 }
1124
1125 #[test]
1126 #[should_panic(expected = "attempt to divide by zero")]
1127 fn overflowing_div_by_zero_panics() {
1128 let _ = I128s12::ONE.overflowing_div(I128s12::ZERO);
1129 }
1130
1131 // Rem variants
1132
1133 #[test]
1134 fn checked_rem_normal() {
1135 // 5.5 % 2.0 = 1.5
1136 let a = I128s12::from_bits(5_500_000_000_000);
1137 let expected = I128s12::from_bits(1_500_000_000_000);
1138 assert_eq!(a.checked_rem(two()), Some(expected));
1139 }
1140
1141 #[test]
1142 fn checked_rem_by_zero_returns_none() {
1143 assert_eq!(I128s12::ONE.checked_rem(I128s12::ZERO), None);
1144 }
1145
1146 #[test]
1147 fn checked_rem_min_neg_one_lsb_returns_none() {
1148 // The raw overflow case is `i128::MIN % -1` (because i128::MIN / -1
1149 // overflows). The divisor's raw bits are -1, not the decimal -ONE
1150 // (-10^12), which does not trigger this path.
1151 let neg_one_lsb = I128s12::from_bits(-1);
1152 assert_eq!(I128s12::MIN.checked_rem(neg_one_lsb), None);
1153 }
1154
1155 #[test]
1156 fn wrapping_rem_normal() {
1157 let a = I128s12::from_bits(5_500_000_000_000);
1158 let expected = I128s12::from_bits(1_500_000_000_000);
1159 assert_eq!(a.wrapping_rem(two()), expected);
1160 }
1161
1162 #[test]
1163 #[should_panic(expected = "attempt to calculate the remainder with a divisor of zero")]
1164 fn wrapping_rem_by_zero_panics() {
1165 let _ = I128s12::ONE.wrapping_rem(I128s12::ZERO);
1166 }
1167
1168 #[test]
1169 fn wrapping_rem_min_neg_one_lsb_returns_zero() {
1170 // i128::MIN % -1 wraps to 0 (the overflow case).
1171 let neg_one_lsb = I128s12::from_bits(-1);
1172 assert_eq!(
1173 I128s12::MIN.wrapping_rem(neg_one_lsb),
1174 I128s12::ZERO
1175 );
1176 }
1177
1178 #[test]
1179 fn overflowing_rem_normal() {
1180 let a = I128s12::from_bits(5_500_000_000_000);
1181 let expected = I128s12::from_bits(1_500_000_000_000);
1182 assert_eq!(a.overflowing_rem(two()), (expected, false));
1183 }
1184
1185 #[test]
1186 fn overflowing_rem_min_neg_one_lsb_flagged() {
1187 let neg_one_lsb = I128s12::from_bits(-1);
1188 assert_eq!(
1189 I128s12::MIN.overflowing_rem(neg_one_lsb),
1190 (I128s12::ZERO, true)
1191 );
1192 }
1193
1194 // Cross-scale exercise
1195
1196 /// Verifies that the variant family compiles and functions correctly at SCALE = 6.
1197 #[test]
1198 fn variants_at_scale_6() {
1199 type D6 = I128<6>;
1200 let one = D6::ONE;
1201 let two_d6 = D6::from_bits(2_000_000); // 2.0 at SCALE=6
1202 let one_lsb = D6::from_bits(1);
1203
1204 assert_eq!(one.checked_add(one), Some(two_d6));
1205 // MAX + 1 LSB overflows / wraps to MIN under two's-complement.
1206 assert_eq!(D6::MAX.checked_add(one_lsb), None);
1207 assert_eq!(D6::MAX.saturating_add(one_lsb), D6::MAX);
1208 assert_eq!(D6::MAX.wrapping_add(one_lsb), D6::MIN);
1209 assert_eq!(
1210 D6::MAX.overflowing_add(one_lsb),
1211 (D6::MIN, true)
1212 );
1213
1214 assert_eq!(D6::MIN.checked_neg(), None);
1215 assert_eq!(D6::MIN.wrapping_neg(), D6::MIN);
1216 assert_eq!(D6::MIN.saturating_neg(), D6::MAX);
1217 }
1218
1219 /// Verifies that `checked_*` matches the base operator when no overflow occurs.
1220 #[test]
1221 fn checked_matches_op_in_range() {
1222 let a = I128s12::from_bits(7_500_000_000_000); // 7.5
1223 let b = two();
1224 assert_eq!(a.checked_add(b), Some(a + b));
1225 assert_eq!(a.checked_sub(b), Some(a - b));
1226 assert_eq!(a.checked_mul(b), Some(a * b));
1227 assert_eq!(a.checked_div(b), Some(a / b));
1228 assert_eq!(a.checked_rem(b), Some(a % b));
1229 }
1230
1231 /// Verifies that the `overflowing_*` flag agrees with `checked_*` returning `None`.
1232 #[test]
1233 fn overflowing_flag_matches_checked_none() {
1234 // Add: MAX + ONE
1235 let (_, ovf) = I128s12::MAX.overflowing_add(I128s12::ONE);
1236 assert_eq!(ovf, I128s12::MAX.checked_add(I128s12::ONE).is_none());
1237
1238 // Sub: MIN - ONE
1239 let (_, ovf) = I128s12::MIN.overflowing_sub(I128s12::ONE);
1240 assert_eq!(ovf, I128s12::MIN.checked_sub(I128s12::ONE).is_none());
1241
1242 // Mul: MAX * 2
1243 let (_, ovf) = I128s12::MAX.overflowing_mul(two());
1244 assert_eq!(ovf, I128s12::MAX.checked_mul(two()).is_none());
1245
1246 // Neg: MIN
1247 let (_, ovf) = I128s12::MIN.overflowing_neg();
1248 assert_eq!(ovf, I128s12::MIN.checked_neg().is_none());
1249
1250 // Rem: MIN % (-1 LSB) -- the raw i128::MIN % -1 case.
1251 let neg_one_lsb = I128s12::from_bits(-1);
1252 let (_, ovf) = I128s12::MIN.overflowing_rem(neg_one_lsb);
1253 assert_eq!(
1254 ovf,
1255 I128s12::MIN.checked_rem(neg_one_lsb).is_none()
1256 );
1257 }
1258
1259 /// Verifies that `saturating_add`, `saturating_sub`, and `saturating_mul`
1260 /// never panic and always return a value within `[MIN, MAX]`.
1261 #[test]
1262 fn saturating_never_escapes_bounds() {
1263 let extremes = [
1264 I128s12::MIN,
1265 I128s12::from_bits(-1),
1266 I128s12::ZERO,
1267 I128s12::ONE,
1268 I128s12::MAX,
1269 ];
1270 for &a in &extremes {
1271 for &b in &extremes {
1272 let s_add = a.saturating_add(b);
1273 let s_sub = a.saturating_sub(b);
1274 let s_mul = a.saturating_mul(b);
1275 assert!(s_add >= I128s12::MIN && s_add <= I128s12::MAX);
1276 assert!(s_sub >= I128s12::MIN && s_sub <= I128s12::MAX);
1277 assert!(s_mul >= I128s12::MIN && s_mul <= I128s12::MAX);
1278 }
1279 }
1280 }
1281}