Skip to main content

decimal_scaled/
num_traits_impls.rs

1//! `num-traits` 0.2 trait implementations for [`I128`].
2//!
3//! Allows generic numeric code (nalgebra, ndarray, statrs, and other
4//! crates that accept "any number type") to use `I128<SCALE>` as a
5//! scalar. Crates that provide generic numeric algorithms almost
6//! universally bound on [`num_traits`] traits rather than defining
7//! their own numeric interfaces.
8//!
9//! # Trait coverage
10//!
11//! - [`num_traits::Zero`] / [`num_traits::One`] — additive and
12//!   multiplicative identities.
13//! - [`num_traits::Num`] — umbrella numeric trait combining
14//!   `Zero + One + PartialEq + Add + Sub + Mul + Div + Rem` with a
15//!   `from_str_radix` constructor.
16//! - [`num_traits::Bounded`] — `min_value()` / `max_value()` for
17//!   generic clamping code.
18//! - [`num_traits::Signed`] — `abs`, `signum`, `is_positive`,
19//!   `is_negative`, `abs_sub`.
20//! - [`num_traits::FromPrimitive`] / [`num_traits::ToPrimitive`] —
21//!   fallible conversions to and from the primitive numeric types.
22//! - [`num_traits::CheckedAdd`] / [`num_traits::CheckedSub`] /
23//!   [`num_traits::CheckedMul`] / [`num_traits::CheckedDiv`] /
24//!   [`num_traits::CheckedRem`] / [`num_traits::CheckedNeg`] —
25//!   overflow-safe variants returning `Option<Self>`.
26//!
27//! # `from_str_radix`
28//!
29//! [`num_traits::Num::from_str_radix`] delegates to
30//! [`core::str::FromStr`] for `radix == 10` and rejects every other
31//! radix. The compile-time signature is stable regardless of whether
32//! the underlying `FromStr` implementation is complete.
33//!
34//! # `CheckedMul` / `CheckedDiv`
35//!
36//! Both traits delegate to the inherent [`I128::checked_mul`] and
37//! [`I128::checked_div`] methods. The trait and inherent paths are
38//! bit-identical. `CheckedAdd`, `CheckedSub`, `CheckedRem`, and
39//! `CheckedNeg` operate directly on the raw `i128` storage and
40//! delegate to `i128`'s own checked intrinsics; no rescaling is
41//! needed for those operations.
42
43use num_traits::{
44    Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedSub,
45    FromPrimitive, Num, NumCast, One, Signed, ToPrimitive, Zero,
46};
47
48use crate::core_type::{I128, ParseDecimalError};
49
50// ---------------------------------------------------------------------------
51// Zero / One
52// ---------------------------------------------------------------------------
53
54impl<const SCALE: u32> Zero for I128<SCALE> {
55    /// Returns the additive identity for `I128<SCALE>`.
56    ///
57    /// Equivalent to [`I128::ZERO`].
58    ///
59    /// # Precision
60    ///
61    /// Strict: all arithmetic is integer-only; result is bit-exact.
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// use decimal_scaled::I128s12;
67    /// use num_traits::Zero;
68    ///
69    /// assert_eq!(I128s12::zero(), I128s12::ZERO);
70    /// ```
71    #[inline]
72    fn zero() -> Self {
73        Self::ZERO
74    }
75
76    /// Returns `true` if `self` equals the additive identity.
77    ///
78    /// # Precision
79    ///
80    /// Strict: all arithmetic is integer-only; result is bit-exact.
81    ///
82    /// # Examples
83    ///
84    /// ```
85    /// use decimal_scaled::I128s12;
86    /// use num_traits::Zero;
87    ///
88    /// assert!(I128s12::ZERO.is_zero());
89    /// assert!(!I128s12::ONE.is_zero());
90    /// ```
91    #[inline]
92    fn is_zero(&self) -> bool {
93        self.0 == 0
94    }
95}
96
97impl<const SCALE: u32> One for I128<SCALE> {
98    /// Returns the multiplicative identity for `I128<SCALE>`.
99    ///
100    /// Equivalent to [`I128::ONE`].
101    ///
102    /// # Precision
103    ///
104    /// Strict: all arithmetic is integer-only; result is bit-exact.
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// use decimal_scaled::I128s12;
110    /// use num_traits::One;
111    ///
112    /// assert_eq!(I128s12::one(), I128s12::ONE);
113    /// ```
114    #[inline]
115    fn one() -> Self {
116        Self::ONE
117    }
118
119    /// Returns `true` if `self` equals the multiplicative identity.
120    ///
121    /// Provided explicitly rather than relying on the default so that
122    /// the check is a single integer comparison instead of going through
123    /// the multiplication chain.
124    ///
125    /// # Precision
126    ///
127    /// Strict: all arithmetic is integer-only; result is bit-exact.
128    ///
129    /// # Examples
130    ///
131    /// ```
132    /// use decimal_scaled::I128s12;
133    /// use num_traits::One;
134    ///
135    /// assert!(I128s12::ONE.is_one());
136    /// assert!(!I128s12::ZERO.is_one());
137    /// ```
138    #[inline]
139    fn is_one(&self) -> bool {
140        self.0 == Self::multiplier()
141    }
142}
143
144// ---------------------------------------------------------------------------
145// Num
146// ---------------------------------------------------------------------------
147
148impl<const SCALE: u32> Num for I128<SCALE> {
149    type FromStrRadixErr = ParseDecimalError;
150
151    /// Parses a decimal string in the given radix.
152    ///
153    /// Only `radix == 10` is supported; `I128<SCALE>` is a base-10 type
154    /// by construction. Any other radix returns `ParseDecimalError::InvalidChar`
155    /// without attempting to parse the string.
156    ///
157    /// For `radix == 10` the call delegates to the [`core::str::FromStr`]
158    /// implementation on `I128<SCALE>`.
159    ///
160    /// # Precision
161    ///
162    /// Strict: all arithmetic is integer-only; result is bit-exact.
163    ///
164    /// # Examples
165    ///
166    /// ```
167    /// use decimal_scaled::I128s12;
168    /// use num_traits::Num;
169    ///
170    /// let v = I128s12::from_str_radix("1", 10).expect("parse 1");
171    /// assert_eq!(v, I128s12::ONE);
172    ///
173    /// assert!(I128s12::from_str_radix("1", 16).is_err());
174    /// ```
175    fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
176        if radix != 10 {
177            return Err(ParseDecimalError::InvalidChar);
178        }
179        s.parse::<Self>()
180    }
181}
182
183// ---------------------------------------------------------------------------
184// Bounded
185// ---------------------------------------------------------------------------
186
187impl<const SCALE: u32> Bounded for I128<SCALE> {
188    /// Returns the smallest representable value, equal to [`I128::MIN`].
189    ///
190    /// The raw storage is `i128::MIN`.
191    ///
192    /// # Precision
193    ///
194    /// Strict: all arithmetic is integer-only; result is bit-exact.
195    ///
196    /// # Examples
197    ///
198    /// ```
199    /// use decimal_scaled::I128s12;
200    /// use num_traits::Bounded;
201    ///
202    /// assert_eq!(I128s12::min_value(), I128s12::MIN);
203    /// ```
204    #[inline]
205    fn min_value() -> Self {
206        Self::MIN
207    }
208
209    /// Returns the largest representable value, equal to [`I128::MAX`].
210    ///
211    /// The raw storage is `i128::MAX`.
212    ///
213    /// # Precision
214    ///
215    /// Strict: all arithmetic is integer-only; result is bit-exact.
216    ///
217    /// # Examples
218    ///
219    /// ```
220    /// use decimal_scaled::I128s12;
221    /// use num_traits::Bounded;
222    ///
223    /// assert_eq!(I128s12::max_value(), I128s12::MAX);
224    /// ```
225    #[inline]
226    fn max_value() -> Self {
227        Self::MAX
228    }
229}
230
231// ---------------------------------------------------------------------------
232// Signed
233// ---------------------------------------------------------------------------
234
235impl<const SCALE: u32> Signed for I128<SCALE> {
236    /// Returns the absolute value of `self`.
237    ///
238    /// Delegates to the inherent [`I128::abs`] method.
239    ///
240    /// # Panics
241    ///
242    /// Panics in debug mode when called on `I128::MIN` because the
243    /// positive counterpart of `i128::MIN` is not representable. In
244    /// release mode the result wraps.
245    ///
246    /// # Precision
247    ///
248    /// Strict: all arithmetic is integer-only; result is bit-exact.
249    ///
250    /// # Examples
251    ///
252    /// ```
253    /// use decimal_scaled::I128s12;
254    /// use num_traits::Signed;
255    ///
256    /// let pos = I128s12::from_bits(1_500_000_000_000);
257    /// let neg = I128s12::from_bits(-1_500_000_000_000);
258    /// assert_eq!(neg.abs(), pos);
259    /// ```
260    #[inline]
261    fn abs(&self) -> Self {
262        I128::abs(*self)
263    }
264
265    /// Returns the sign of `self` as a scaled `I128`: `-ONE`, `ZERO`, or `+ONE`.
266    ///
267    /// Delegates to the inherent [`I128::signum`] method.
268    ///
269    /// # Precision
270    ///
271    /// Strict: all arithmetic is integer-only; result is bit-exact.
272    ///
273    /// # Examples
274    ///
275    /// ```
276    /// use decimal_scaled::I128s12;
277    /// use num_traits::Signed;
278    ///
279    /// assert_eq!(I128s12::from_bits(5).signum(), I128s12::ONE);
280    /// assert_eq!(I128s12::from_bits(-5).signum(), -I128s12::ONE);
281    /// assert_eq!(I128s12::ZERO.signum(), I128s12::ZERO);
282    /// ```
283    #[inline]
284    fn signum(&self) -> Self {
285        I128::signum(*self)
286    }
287
288    /// Returns `true` if `self` is strictly greater than zero.
289    ///
290    /// # Precision
291    ///
292    /// Strict: all arithmetic is integer-only; result is bit-exact.
293    ///
294    /// # Examples
295    ///
296    /// ```
297    /// use decimal_scaled::I128s12;
298    /// use num_traits::Signed;
299    ///
300    /// assert!(I128s12::ONE.is_positive());
301    /// assert!(!I128s12::ZERO.is_positive());
302    /// assert!(!(-I128s12::ONE).is_positive());
303    /// ```
304    #[inline]
305    fn is_positive(&self) -> bool {
306        self.0 > 0
307    }
308
309    /// Returns `true` if `self` is strictly less than zero.
310    ///
311    /// # Precision
312    ///
313    /// Strict: all arithmetic is integer-only; result is bit-exact.
314    ///
315    /// # Examples
316    ///
317    /// ```
318    /// use decimal_scaled::I128s12;
319    /// use num_traits::Signed;
320    ///
321    /// assert!((-I128s12::ONE).is_negative());
322    /// assert!(!I128s12::ZERO.is_negative());
323    /// assert!(!I128s12::ONE.is_negative());
324    /// ```
325    #[inline]
326    fn is_negative(&self) -> bool {
327        self.0 < 0
328    }
329
330    /// Returns `self - other` when `self > other`, otherwise `ZERO`.
331    ///
332    /// Matches the `num_traits::Signed::abs_sub` contract: the result is
333    /// never negative. This is not the same as `(self - other).abs()`.
334    ///
335    /// # Precision
336    ///
337    /// Strict: all arithmetic is integer-only; result is bit-exact.
338    ///
339    /// # Examples
340    ///
341    /// ```
342    /// use decimal_scaled::I128s12;
343    /// use num_traits::Signed;
344    ///
345    /// let two = I128s12::from_bits(2_000_000_000_000);
346    /// let five = I128s12::from_bits(5_000_000_000_000);
347    /// let three = I128s12::from_bits(3_000_000_000_000);
348    ///
349    /// assert_eq!(five.abs_sub(&two), three);
350    /// assert_eq!(two.abs_sub(&five), I128s12::ZERO);
351    /// assert_eq!(five.abs_sub(&five), I128s12::ZERO);
352    /// ```
353    #[inline]
354    fn abs_sub(&self, other: &Self) -> Self {
355        if *self <= *other {
356            Self::ZERO
357        } else {
358            *self - *other
359        }
360    }
361}
362
363// ---------------------------------------------------------------------------
364// FromPrimitive / ToPrimitive
365// ---------------------------------------------------------------------------
366
367impl<const SCALE: u32> FromPrimitive for I128<SCALE> {
368    /// Constructs a `I128<SCALE>` from an `i64` integer value.
369    ///
370    /// Scales `n` by `10^SCALE`. Returns `None` when the multiplication
371    /// overflows `i128`, which can occur at pathologically large scale
372    /// values. At `SCALE <= 18` the call always succeeds for any `i64`.
373    ///
374    /// # Precision
375    ///
376    /// Strict: all arithmetic is integer-only; result is bit-exact.
377    ///
378    /// # Examples
379    ///
380    /// ```
381    /// use decimal_scaled::I128s12;
382    /// use num_traits::FromPrimitive;
383    ///
384    /// assert_eq!(I128s12::from_i64(0), Some(I128s12::ZERO));
385    /// assert_eq!(I128s12::from_i64(1), Some(I128s12::ONE));
386    /// assert_eq!(I128s12::from_i64(-42), Some(I128s12::from_bits(-42_000_000_000_000)));
387    /// ```
388    #[inline]
389    fn from_i64(n: i64) -> Option<Self> {
390        (n as i128)
391            .checked_mul(Self::multiplier())
392            .map(Self)
393    }
394
395    /// Constructs a `I128<SCALE>` from a `u64` integer value.
396    ///
397    /// Scales `n` by `10^SCALE`. Returns `None` on overflow. At
398    /// `SCALE <= 18` the call always succeeds for any `u64`.
399    ///
400    /// # Precision
401    ///
402    /// Strict: all arithmetic is integer-only; result is bit-exact.
403    ///
404    /// # Examples
405    ///
406    /// ```
407    /// use decimal_scaled::I128s12;
408    /// use num_traits::FromPrimitive;
409    ///
410    /// assert_eq!(I128s12::from_u64(0), Some(I128s12::ZERO));
411    /// assert_eq!(I128s12::from_u64(42), Some(I128s12::from_bits(42_000_000_000_000)));
412    /// ```
413    #[inline]
414    fn from_u64(n: u64) -> Option<Self> {
415        (n as i128)
416            .checked_mul(Self::multiplier())
417            .map(Self)
418    }
419
420    /// Constructs a `I128<SCALE>` from an `i128` integer value.
421    ///
422    /// Returns `None` when scaling overflows. Delegates to the existing
423    /// [`TryFrom<i128>`] implementation.
424    ///
425    /// # Precision
426    ///
427    /// Strict: all arithmetic is integer-only; result is bit-exact.
428    ///
429    /// # Examples
430    ///
431    /// ```
432    /// use decimal_scaled::I128s12;
433    /// use num_traits::FromPrimitive;
434    ///
435    /// assert_eq!(I128s12::from_i128(7), Some(I128s12::from_bits(7_000_000_000_000)));
436    /// assert_eq!(I128s12::from_i128(i128::MAX), None);
437    /// ```
438    #[inline]
439    fn from_i128(n: i128) -> Option<Self> {
440        Self::try_from(n).ok()
441    }
442
443    /// Constructs a `I128<SCALE>` from a `u128` integer value.
444    ///
445    /// Returns `None` when `n > i128::MAX` or when scaling overflows.
446    /// Delegates to the existing [`TryFrom<u128>`] implementation.
447    ///
448    /// # Precision
449    ///
450    /// Strict: all arithmetic is integer-only; result is bit-exact.
451    ///
452    /// # Examples
453    ///
454    /// ```
455    /// use decimal_scaled::I128s12;
456    /// use num_traits::FromPrimitive;
457    ///
458    /// assert_eq!(I128s12::from_u128(99), Some(I128s12::from_bits(99_000_000_000_000)));
459    /// assert_eq!(I128s12::from_u128(u128::MAX), None);
460    /// ```
461    #[inline]
462    fn from_u128(n: u128) -> Option<Self> {
463        Self::try_from(n).ok()
464    }
465
466    /// Constructs a `I128<SCALE>` from an `f32` value.
467    ///
468    /// Returns `None` for `NaN`, infinities, or finite values whose
469    /// scaled representation overflows `i128`. Delegates to the existing
470    /// [`TryFrom<f32>`] implementation.
471    ///
472    /// # Precision
473    ///
474    /// Lossy: involves f32 or f64 at some point; result may lose precision.
475    ///
476    /// # Examples
477    ///
478    /// ```
479    /// use decimal_scaled::I128s12;
480    /// use num_traits::FromPrimitive;
481    ///
482    /// assert_eq!(I128s12::from_f32(0.0), Some(I128s12::ZERO));
483    /// assert_eq!(I128s12::from_f32(1.0), Some(I128s12::ONE));
484    /// assert_eq!(I128s12::from_f32(f32::NAN), None);
485    /// assert_eq!(I128s12::from_f32(f32::INFINITY), None);
486    /// ```
487    #[inline]
488    fn from_f32(n: f32) -> Option<Self> {
489        Self::try_from(n).ok()
490    }
491
492    /// Constructs a `I128<SCALE>` from an `f64` value.
493    ///
494    /// Returns `None` for `NaN`, infinities, or finite values whose
495    /// scaled representation overflows `i128`. Delegates to the existing
496    /// [`TryFrom<f64>`] implementation.
497    ///
498    /// # Precision
499    ///
500    /// Lossy: involves f32 or f64 at some point; result may lose precision.
501    ///
502    /// # Examples
503    ///
504    /// ```
505    /// use decimal_scaled::I128s12;
506    /// use num_traits::FromPrimitive;
507    ///
508    /// assert_eq!(I128s12::from_f64(0.0), Some(I128s12::ZERO));
509    /// assert_eq!(I128s12::from_f64(1.0), Some(I128s12::ONE));
510    /// assert_eq!(I128s12::from_f64(f64::NAN), None);
511    /// assert_eq!(I128s12::from_f64(1e30), None);
512    /// ```
513    #[inline]
514    fn from_f64(n: f64) -> Option<Self> {
515        Self::try_from(n).ok()
516    }
517}
518
519impl<const SCALE: u32> ToPrimitive for I128<SCALE> {
520    /// Converts `self` to `i64` by truncating the fractional part toward zero.
521    ///
522    /// Returns `None` when the integer part of `self` falls outside
523    /// `i64`'s range. Unlike `to_int_lossy`, which saturates, this method
524    /// is contractually fallible.
525    ///
526    /// # Precision
527    ///
528    /// Strict: all arithmetic is integer-only; result is bit-exact.
529    ///
530    /// # Examples
531    ///
532    /// ```
533    /// use decimal_scaled::I128s12;
534    /// use num_traits::ToPrimitive;
535    ///
536    /// let v = I128s12::from_bits(2_500_000_000_000); // 2.5
537    /// assert_eq!(v.to_i64(), Some(2));
538    /// assert_eq!(I128s12::MAX.to_i64(), None);
539    /// ```
540    #[inline]
541    fn to_i64(&self) -> Option<i64> {
542        let raw = self.0 / Self::multiplier();
543        i64::try_from(raw).ok()
544    }
545
546    /// Converts `self` to `u64` by truncating the fractional part toward zero.
547    ///
548    /// Returns `None` for negative values and for values whose integer part
549    /// exceeds `u64::MAX`.
550    ///
551    /// # Precision
552    ///
553    /// Strict: all arithmetic is integer-only; result is bit-exact.
554    ///
555    /// # Examples
556    ///
557    /// ```
558    /// use decimal_scaled::I128s12;
559    /// use num_traits::ToPrimitive;
560    ///
561    /// let v = I128s12::from_bits(42_000_000_000_000);
562    /// assert_eq!(v.to_u64(), Some(42));
563    /// assert_eq!((-I128s12::ONE).to_u64(), None);
564    /// ```
565    #[inline]
566    fn to_u64(&self) -> Option<u64> {
567        if self.0 < 0 {
568            return None;
569        }
570        let raw = self.0 / Self::multiplier();
571        u64::try_from(raw).ok()
572    }
573
574    /// Converts `self` to `i128` by truncating the fractional part toward zero.
575    ///
576    /// Always returns `Some` because `self.0 / 10^SCALE` fits in `i128`
577    /// by construction.
578    ///
579    /// # Precision
580    ///
581    /// Strict: all arithmetic is integer-only; result is bit-exact.
582    ///
583    /// # Examples
584    ///
585    /// ```
586    /// use decimal_scaled::I128s12;
587    /// use num_traits::ToPrimitive;
588    ///
589    /// let v = I128s12::from_bits(42_000_000_000_000);
590    /// assert_eq!(v.to_i128(), Some(42));
591    /// assert!(I128s12::MAX.to_i128().is_some());
592    /// ```
593    #[inline]
594    fn to_i128(&self) -> Option<i128> {
595        Some(self.0 / Self::multiplier())
596    }
597
598    /// Converts `self` to `u128` by truncating the fractional part toward zero.
599    ///
600    /// Returns `None` for negative values.
601    ///
602    /// # Precision
603    ///
604    /// Strict: all arithmetic is integer-only; result is bit-exact.
605    ///
606    /// # Examples
607    ///
608    /// ```
609    /// use decimal_scaled::I128s12;
610    /// use num_traits::ToPrimitive;
611    ///
612    /// assert_eq!(I128s12::ZERO.to_u128(), Some(0));
613    /// assert_eq!(I128s12::from_bits(-1).to_u128(), None);
614    /// ```
615    #[inline]
616    fn to_u128(&self) -> Option<u128> {
617        if self.0 < 0 {
618            return None;
619        }
620        u128::try_from(self.0 / Self::multiplier()).ok()
621    }
622
623    /// Converts `self` to `f32`.
624    ///
625    /// Uses the [`I128::to_f32_lossy`] helper. Always returns `Some`; the
626    /// result may be `+/-inf` for very large magnitudes.
627    ///
628    /// # Precision
629    ///
630    /// Lossy: involves f32 or f64 at some point; result may lose precision.
631    ///
632    /// # Examples
633    ///
634    /// ```
635    /// use decimal_scaled::I128s12;
636    /// use num_traits::ToPrimitive;
637    ///
638    /// let v = I128s12::from_bits(1_500_000_000_000); // 1.5
639    /// assert_eq!(v.to_f32(), Some(1.5_f32));
640    /// ```
641    #[inline]
642    fn to_f32(&self) -> Option<f32> {
643        Some((*self).to_f32_lossy())
644    }
645
646    /// Converts `self` to `f64`.
647    ///
648    /// Uses the [`I128::to_f64_lossy`] helper. Always returns `Some`; the
649    /// result may be `+/-inf` for very large magnitudes.
650    ///
651    /// # Precision
652    ///
653    /// Lossy: involves f32 or f64 at some point; result may lose precision.
654    ///
655    /// # Examples
656    ///
657    /// ```
658    /// use decimal_scaled::I128s12;
659    /// use num_traits::ToPrimitive;
660    ///
661    /// let v = I128s12::from_bits(1_500_000_000_000); // 1.5
662    /// assert_eq!(v.to_f64(), Some(1.5_f64));
663    /// ```
664    #[inline]
665    fn to_f64(&self) -> Option<f64> {
666        Some((*self).to_f64_lossy())
667    }
668}
669
670// ---------------------------------------------------------------------------
671// NumCast
672// ---------------------------------------------------------------------------
673//
674// `NumCast` is the generic-cast bridge in the `num-traits` ecosystem:
675// any `T: ToPrimitive` can be cast to a `NumCast` type, with the
676// implementation choosing which `to_X` / `from_X` pair to dispatch
677// through.
678//
679// Dispatch strategy: prefer the `f64` path for inputs that carry
680// fractional information (e.g. `1.5_f64`), and fall back to the
681// lossless `i128` path for integer inputs. The decision uses a
682// round-trip equality check: if `(int as f64) == n.to_f64()` the
683// input is integer-shaped and the integer path is taken, preserving
684// precision even for `i64`/`u64` values above f64's 53-bit mantissa.
685
686impl<const SCALE: u32> NumCast for I128<SCALE> {
687    /// Casts any `T: ToPrimitive` value to `I128<SCALE>`.
688    ///
689    /// Uses the `f64` conversion path for inputs that carry fractional
690    /// information, and the `i128` path for integer-shaped inputs. The
691    /// integer path is taken when `(to_i128() as f64) == to_f64()`, which
692    /// holds for true integer types even when their magnitude exceeds f64's
693    /// exact-integer range of 2^53. Returns `None` when neither path
694    /// produces a representable value.
695    ///
696    /// # Precision
697    ///
698    /// Lossy: involves f32 or f64 at some point; result may lose precision.
699    ///
700    /// # Examples
701    ///
702    /// ```
703    /// use decimal_scaled::I128s12;
704    /// use num_traits::NumCast;
705    ///
706    /// let from_int: I128s12 = NumCast::from(42_i32).unwrap();
707    /// assert_eq!(from_int, I128s12::from_bits(42_000_000_000_000));
708    ///
709    /// let from_float: I128s12 = NumCast::from(1.5_f64).unwrap();
710    /// assert_eq!(from_float, I128s12::from_f64_lossy(1.5));
711    ///
712    /// let nan: Option<I128s12> = NumCast::from(f64::NAN);
713    /// assert!(nan.is_none());
714    /// ```
715    #[inline]
716    fn from<T: ToPrimitive>(n: T) -> Option<Self> {
717        // Read f64 early so we can distinguish integer vs. fractional inputs.
718        let f = n.to_f64();
719        // Integer fast path: if `n` round-trips through i128 and the f64
720        // value matches, take the integer path. This preserves precision for
721        // i64/u64 values above f64's 2^53 exact-integer boundary.
722        if let Some(int) = n.to_i128() {
723            let take_int_path = match f {
724                None => true,
725                Some(fv) => fv.is_finite() && ((int as f64) == fv),
726            };
727            if take_int_path {
728                return <Self as FromPrimitive>::from_i128(int);
729            }
730        }
731        // Float path — preserves fractional information for f32/f64 inputs.
732        // Returns None for NaN, infinity, or out-of-range values.
733        if let Some(fv) = f {
734            return <Self as FromPrimitive>::from_f64(fv);
735        }
736        None
737    }
738}
739
740// ---------------------------------------------------------------------------
741// Checked* family
742// ---------------------------------------------------------------------------
743//
744// CheckedAdd, CheckedSub, CheckedRem, and CheckedNeg delegate directly to
745// i128's checked intrinsics; no rescaling is required for those operations.
746//
747// CheckedMul and CheckedDiv delegate to the inherent I128::checked_mul and
748// I128::checked_div methods. The trait and inherent paths are bit-identical.
749
750impl<const SCALE: u32> CheckedAdd for I128<SCALE> {
751    /// Adds `rhs` to `self`, returning `None` on overflow.
752    ///
753    /// # Precision
754    ///
755    /// Strict: all arithmetic is integer-only; result is bit-exact.
756    ///
757    /// # Examples
758    ///
759    /// ```
760    /// use decimal_scaled::I128s12;
761    /// use num_traits::CheckedAdd;
762    ///
763    /// let two = I128s12::from_bits(2_000_000_000_000);
764    /// assert_eq!(I128s12::ONE.checked_add(I128s12::ONE), Some(two));
765    /// assert_eq!(I128s12::MAX.checked_add(I128s12::ONE), None);
766    /// ```
767    #[inline]
768    fn checked_add(&self, rhs: &Self) -> Option<Self> {
769        self.0.checked_add(rhs.0).map(Self)
770    }
771}
772
773impl<const SCALE: u32> CheckedSub for I128<SCALE> {
774    /// Subtracts `rhs` from `self`, returning `None` on underflow.
775    ///
776    /// # Precision
777    ///
778    /// Strict: all arithmetic is integer-only; result is bit-exact.
779    ///
780    /// # Examples
781    ///
782    /// ```
783    /// use decimal_scaled::I128s12;
784    /// use num_traits::CheckedSub;
785    ///
786    /// let three = I128s12::from_bits(3_000_000_000_000);
787    /// let two = I128s12::from_bits(2_000_000_000_000);
788    /// assert_eq!(three.checked_sub(two), Some(I128s12::ONE));
789    /// assert_eq!(I128s12::MIN.checked_sub(I128s12::ONE), None);
790    /// ```
791    #[inline]
792    fn checked_sub(&self, rhs: &Self) -> Option<Self> {
793        self.0.checked_sub(rhs.0).map(Self)
794    }
795}
796
797impl<const SCALE: u32> CheckedMul for I128<SCALE> {
798    /// Multiplies `self` by `v`, returning `None` when the result overflows `i128`.
799    ///
800    /// Delegates to the inherent [`I128::checked_mul`] method, which uses a
801    /// 256-bit intermediate product so that only the final result needs to
802    /// fit in `i128`. Returns `None` only when the quotient after rescaling
803    /// overflows. The trait and inherent paths are bit-identical.
804    ///
805    /// # Precision
806    ///
807    /// Strict: all arithmetic is integer-only; result is bit-exact.
808    ///
809    /// # Examples
810    ///
811    /// ```
812    /// use decimal_scaled::I128s12;
813    /// use num_traits::CheckedMul;
814    ///
815    /// let half = I128s12::from_bits(500_000_000_000);
816    /// let quarter = I128s12::from_bits(250_000_000_000);
817    /// assert_eq!(half.checked_mul(half), Some(quarter));
818    ///
819    /// let two = I128s12::from_bits(2_000_000_000_000);
820    /// assert_eq!(I128s12::MAX.checked_mul(two), None);
821    /// ```
822    #[inline]
823    fn checked_mul(&self, v: &Self) -> Option<Self> {
824        (*self).checked_mul(*v)
825    }
826}
827
828impl<const SCALE: u32> CheckedDiv for I128<SCALE> {
829    /// Divides `self` by `v`, returning `None` on division by zero or overflow.
830    ///
831    /// Delegates to the inherent [`I128::checked_div`] method, which uses a
832    /// 256-bit widening divide. Returns `None` on division by zero or when the
833    /// result overflows `i128` (the only case is `I128::MIN / -ONE`). The
834    /// trait and inherent paths are bit-identical.
835    ///
836    /// # Precision
837    ///
838    /// Strict: all arithmetic is integer-only; result is bit-exact.
839    ///
840    /// # Examples
841    ///
842    /// ```
843    /// use decimal_scaled::I128s12;
844    /// use num_traits::CheckedDiv;
845    ///
846    /// let half = I128s12::from_bits(500_000_000_000);
847    /// let two = I128s12::from_bits(2_000_000_000_000);
848    /// let quarter = I128s12::from_bits(250_000_000_000);
849    /// assert_eq!(half.checked_div(two), Some(quarter));
850    /// assert_eq!(I128s12::ONE.checked_div(I128s12::ZERO), None);
851    /// ```
852    #[inline]
853    fn checked_div(&self, v: &Self) -> Option<Self> {
854        (*self).checked_div(*v)
855    }
856}
857
858impl<const SCALE: u32> CheckedRem for I128<SCALE> {
859    /// Computes `self % rhs`, returning `None` when `rhs` is zero.
860    ///
861    /// Because both operands share the same `SCALE`, no rescaling is needed.
862    /// Delegates directly to `i128::checked_rem`.
863    ///
864    /// # Precision
865    ///
866    /// Strict: all arithmetic is integer-only; result is bit-exact.
867    ///
868    /// # Examples
869    ///
870    /// ```
871    /// use decimal_scaled::I128s12;
872    /// use num_traits::CheckedRem;
873    ///
874    /// let a = I128s12::from_bits(5_500_000_000_000); // 5.5
875    /// let b = I128s12::from_bits(2_000_000_000_000); // 2.0
876    /// let expected = I128s12::from_bits(1_500_000_000_000); // 1.5
877    /// assert_eq!(a.checked_rem(b), Some(expected));
878    /// assert_eq!(a.checked_rem(I128s12::ZERO), None);
879    /// ```
880    #[inline]
881    fn checked_rem(&self, rhs: &Self) -> Option<Self> {
882        self.0.checked_rem(rhs.0).map(Self)
883    }
884}
885
886impl<const SCALE: u32> CheckedNeg for I128<SCALE> {
887    /// Negates `self`, returning `None` for `I128::MIN`.
888    ///
889    /// `i128::MIN` has no positive counterpart in two's-complement, so
890    /// negating it overflows. All other values succeed.
891    ///
892    /// # Precision
893    ///
894    /// Strict: all arithmetic is integer-only; result is bit-exact.
895    ///
896    /// # Examples
897    ///
898    /// ```
899    /// use decimal_scaled::I128s12;
900    /// use num_traits::CheckedNeg;
901    ///
902    /// assert_eq!(I128s12::ONE.checked_neg(), Some(-I128s12::ONE));
903    /// assert_eq!(I128s12::ZERO.checked_neg(), Some(I128s12::ZERO));
904    /// assert_eq!(I128s12::MIN.checked_neg(), None);
905    /// ```
906    #[inline]
907    fn checked_neg(&self) -> Option<Self> {
908        self.0.checked_neg().map(Self)
909    }
910}
911
912#[cfg(test)]
913mod tests {
914    use super::*;
915    use crate::core_type::{I128, I128s12};
916
917    // ---------------------------------------------------------------------------
918    // Zero / One
919    // ---------------------------------------------------------------------------
920
921    #[test]
922    fn zero_is_zero_const() {
923        assert_eq!(<I128s12 as Zero>::zero(), I128s12::ZERO);
924    }
925
926    #[test]
927    fn zero_is_zero_predicate() {
928        assert!(<I128s12 as Zero>::is_zero(&I128s12::ZERO));
929        assert!(!<I128s12 as Zero>::is_zero(&I128s12::ONE));
930        assert!(!<I128s12 as Zero>::is_zero(&I128s12::from_bits(1)));
931    }
932
933    #[test]
934    fn one_is_one_const() {
935        assert_eq!(<I128s12 as One>::one(), I128s12::ONE);
936    }
937
938    #[test]
939    fn one_is_one_predicate() {
940        assert!(<I128s12 as One>::is_one(&I128s12::ONE));
941        assert!(!<I128s12 as One>::is_one(&I128s12::ZERO));
942        // A non-canonical raw value (1 LSB) is not "one".
943        assert!(!<I128s12 as One>::is_one(&I128s12::from_bits(1)));
944    }
945
946    // ---------------------------------------------------------------------------
947    // Bounded
948    // ---------------------------------------------------------------------------
949
950    #[test]
951    fn bounded_min_max() {
952        assert_eq!(<I128s12 as Bounded>::min_value(), I128s12::MIN);
953        assert_eq!(<I128s12 as Bounded>::max_value(), I128s12::MAX);
954    }
955
956    // ---------------------------------------------------------------------------
957    // Signed
958    // ---------------------------------------------------------------------------
959
960    #[test]
961    fn signed_abs_basic() {
962        let pos = I128s12::from_bits(1_500_000_000_000);
963        let neg = I128s12::from_bits(-1_500_000_000_000);
964        assert_eq!(<I128s12 as Signed>::abs(&pos), pos);
965        assert_eq!(<I128s12 as Signed>::abs(&neg), pos);
966        assert_eq!(<I128s12 as Signed>::abs(&I128s12::ZERO), I128s12::ZERO);
967    }
968
969    #[test]
970    fn signed_signum_basic() {
971        let pos = I128s12::from_bits(1_500_000_000_000);
972        let neg = I128s12::from_bits(-1_500_000_000_000);
973        assert_eq!(<I128s12 as Signed>::signum(&pos), I128s12::ONE);
974        assert_eq!(<I128s12 as Signed>::signum(&neg), -I128s12::ONE);
975        assert_eq!(<I128s12 as Signed>::signum(&I128s12::ZERO), I128s12::ZERO);
976    }
977
978    #[test]
979    fn signed_is_positive_negative() {
980        let pos = I128s12::from_bits(1_500_000_000_000);
981        let neg = I128s12::from_bits(-1_500_000_000_000);
982        assert!(<I128s12 as Signed>::is_positive(&pos));
983        assert!(!<I128s12 as Signed>::is_positive(&neg));
984        assert!(!<I128s12 as Signed>::is_positive(&I128s12::ZERO));
985
986        assert!(!<I128s12 as Signed>::is_negative(&pos));
987        assert!(<I128s12 as Signed>::is_negative(&neg));
988        assert!(!<I128s12 as Signed>::is_negative(&I128s12::ZERO));
989    }
990
991    /// `abs_sub(a, b)` clamps to zero when `a <= b`.
992    #[test]
993    fn signed_abs_sub_clamps_to_zero() {
994        let two = I128s12::from_bits(2_000_000_000_000);
995        let five = I128s12::from_bits(5_000_000_000_000);
996
997        // 5 - 2 = 3 (positive case)
998        let three = I128s12::from_bits(3_000_000_000_000);
999        assert_eq!(<I128s12 as Signed>::abs_sub(&five, &two), three);
1000
1001        // 2 - 5 clamps to ZERO (a <= b)
1002        assert_eq!(<I128s12 as Signed>::abs_sub(&two, &five), I128s12::ZERO);
1003
1004        // 5 - 5 = ZERO (equal inputs)
1005        assert_eq!(<I128s12 as Signed>::abs_sub(&five, &five), I128s12::ZERO);
1006    }
1007
1008    // ---------------------------------------------------------------------------
1009    // FromPrimitive
1010    // ---------------------------------------------------------------------------
1011
1012    #[test]
1013    fn from_primitive_i64_in_range() {
1014        assert_eq!(
1015            <I128s12 as FromPrimitive>::from_i64(0),
1016            Some(I128s12::ZERO)
1017        );
1018        assert_eq!(
1019            <I128s12 as FromPrimitive>::from_i64(1),
1020            Some(I128s12::ONE)
1021        );
1022        assert_eq!(
1023            <I128s12 as FromPrimitive>::from_i64(42),
1024            Some(I128s12::from_bits(42_000_000_000_000))
1025        );
1026        assert_eq!(
1027            <I128s12 as FromPrimitive>::from_i64(-42),
1028            Some(I128s12::from_bits(-42_000_000_000_000))
1029        );
1030    }
1031
1032    #[test]
1033    fn from_primitive_u64_in_range() {
1034        assert_eq!(
1035            <I128s12 as FromPrimitive>::from_u64(0),
1036            Some(I128s12::ZERO)
1037        );
1038        assert_eq!(
1039            <I128s12 as FromPrimitive>::from_u64(42),
1040            Some(I128s12::from_bits(42_000_000_000_000))
1041        );
1042        // u64::MAX * 10^12 fits in i128, so this succeeds.
1043        let large = <I128s12 as FromPrimitive>::from_u64(u64::MAX);
1044        assert!(large.is_some());
1045    }
1046
1047    #[test]
1048    fn from_primitive_i128_overflow_returns_none() {
1049        // i128::MAX cannot be scaled by 10^12 — TryFrom returns Err,
1050        // FromPrimitive surfaces that as None.
1051        assert_eq!(<I128s12 as FromPrimitive>::from_i128(i128::MAX), None);
1052        assert_eq!(<I128s12 as FromPrimitive>::from_i128(i128::MIN), None);
1053
1054        // Small values succeed.
1055        assert_eq!(
1056            <I128s12 as FromPrimitive>::from_i128(7),
1057            Some(I128s12::from_bits(7_000_000_000_000))
1058        );
1059    }
1060
1061    #[test]
1062    fn from_primitive_u128_overflow_returns_none() {
1063        // u128::MAX > i128::MAX — the first try_from step fails.
1064        assert_eq!(<I128s12 as FromPrimitive>::from_u128(u128::MAX), None);
1065
1066        // Small values succeed.
1067        assert_eq!(
1068            <I128s12 as FromPrimitive>::from_u128(99),
1069            Some(I128s12::from_bits(99_000_000_000_000))
1070        );
1071    }
1072
1073    #[test]
1074    fn from_primitive_f32_basic() {
1075        assert_eq!(
1076            <I128s12 as FromPrimitive>::from_f32(0.0),
1077            Some(I128s12::ZERO)
1078        );
1079        assert_eq!(
1080            <I128s12 as FromPrimitive>::from_f32(1.0),
1081            Some(I128s12::ONE)
1082        );
1083        // Non-finite inputs return None.
1084        assert_eq!(<I128s12 as FromPrimitive>::from_f32(f32::NAN), None);
1085        assert_eq!(<I128s12 as FromPrimitive>::from_f32(f32::INFINITY), None);
1086        assert_eq!(<I128s12 as FromPrimitive>::from_f32(f32::NEG_INFINITY), None);
1087    }
1088
1089    #[test]
1090    fn from_primitive_f64_basic() {
1091        assert_eq!(
1092            <I128s12 as FromPrimitive>::from_f64(0.0),
1093            Some(I128s12::ZERO)
1094        );
1095        assert_eq!(
1096            <I128s12 as FromPrimitive>::from_f64(1.0),
1097            Some(I128s12::ONE)
1098        );
1099        // Use a value that is not close to any well-known math constant
1100        // so the approx_constant lint stays quiet.
1101        let v = <I128s12 as FromPrimitive>::from_f64(1.234567890123_f64);
1102        assert!(v.is_some());
1103
1104        // Non-finite inputs return None.
1105        assert_eq!(<I128s12 as FromPrimitive>::from_f64(f64::NAN), None);
1106        assert_eq!(<I128s12 as FromPrimitive>::from_f64(f64::INFINITY), None);
1107
1108        // Finite but out-of-range: 1e30 * 10^12 = 1e42 > i128::MAX.
1109        assert_eq!(<I128s12 as FromPrimitive>::from_f64(1e30), None);
1110    }
1111
1112    /// `FromPrimitive` provides default impls for `from_i32`, `from_u32`, etc.
1113    /// via `from_i64` / `from_u64`. Verify the delegation chain works.
1114    #[test]
1115    fn from_primitive_smaller_int_types_via_default_impl() {
1116        assert_eq!(
1117            <I128s12 as FromPrimitive>::from_i32(7),
1118            Some(I128s12::from_bits(7_000_000_000_000))
1119        );
1120        assert_eq!(
1121            <I128s12 as FromPrimitive>::from_i16(-3),
1122            Some(I128s12::from_bits(-3_000_000_000_000))
1123        );
1124        assert_eq!(
1125            <I128s12 as FromPrimitive>::from_i8(0),
1126            Some(I128s12::ZERO)
1127        );
1128        assert_eq!(
1129            <I128s12 as FromPrimitive>::from_u32(7),
1130            Some(I128s12::from_bits(7_000_000_000_000))
1131        );
1132        assert_eq!(
1133            <I128s12 as FromPrimitive>::from_u16(3),
1134            Some(I128s12::from_bits(3_000_000_000_000))
1135        );
1136        assert_eq!(
1137            <I128s12 as FromPrimitive>::from_u8(255),
1138            Some(I128s12::from_bits(255_000_000_000_000))
1139        );
1140    }
1141
1142    // ---------------------------------------------------------------------------
1143    // ToPrimitive
1144    // ---------------------------------------------------------------------------
1145
1146    #[test]
1147    fn to_primitive_i64_in_range() {
1148        let v = I128s12::from_bits(42_000_000_000_000);
1149        assert_eq!(<I128s12 as ToPrimitive>::to_i64(&v), Some(42_i64));
1150
1151        let neg = I128s12::from_bits(-42_000_000_000_000);
1152        assert_eq!(<I128s12 as ToPrimitive>::to_i64(&neg), Some(-42_i64));
1153
1154        assert_eq!(<I128s12 as ToPrimitive>::to_i64(&I128s12::ZERO), Some(0_i64));
1155    }
1156
1157    #[test]
1158    fn to_primitive_i64_truncates_toward_zero() {
1159        // 2.5 truncates to 2
1160        let v = I128s12::from_bits(2_500_000_000_000);
1161        assert_eq!(<I128s12 as ToPrimitive>::to_i64(&v), Some(2_i64));
1162
1163        // -2.5 truncates to -2 (toward zero, not toward -inf)
1164        let neg = I128s12::from_bits(-2_500_000_000_000);
1165        assert_eq!(<I128s12 as ToPrimitive>::to_i64(&neg), Some(-2_i64));
1166    }
1167
1168    #[test]
1169    fn to_primitive_i64_out_of_range_returns_none() {
1170        // I128::MAX integer part ~= 1.7e26, which exceeds i64::MAX.
1171        assert_eq!(<I128s12 as ToPrimitive>::to_i64(&I128s12::MAX), None);
1172        assert_eq!(<I128s12 as ToPrimitive>::to_i64(&I128s12::MIN), None);
1173    }
1174
1175    #[test]
1176    fn to_primitive_u64_negative_returns_none() {
1177        let neg = I128s12::from_bits(-1_000_000_000_000);
1178        assert_eq!(<I128s12 as ToPrimitive>::to_u64(&neg), None);
1179    }
1180
1181    #[test]
1182    fn to_primitive_u64_in_range() {
1183        let v = I128s12::from_bits(42_000_000_000_000);
1184        assert_eq!(<I128s12 as ToPrimitive>::to_u64(&v), Some(42_u64));
1185
1186        assert_eq!(<I128s12 as ToPrimitive>::to_u64(&I128s12::ZERO), Some(0_u64));
1187    }
1188
1189    #[test]
1190    fn to_primitive_i128_always_succeeds() {
1191        // Even MAX and MIN succeed because the integer part is bounded
1192        // by i128::MAX / 10^12, which is well within i128.
1193        assert!(<I128s12 as ToPrimitive>::to_i128(&I128s12::MAX).is_some());
1194        assert!(<I128s12 as ToPrimitive>::to_i128(&I128s12::MIN).is_some());
1195        assert_eq!(
1196            <I128s12 as ToPrimitive>::to_i128(&I128s12::ZERO),
1197            Some(0_i128)
1198        );
1199        assert_eq!(
1200            <I128s12 as ToPrimitive>::to_i128(&I128s12::from_bits(42_000_000_000_000)),
1201            Some(42_i128)
1202        );
1203    }
1204
1205    #[test]
1206    fn to_primitive_u128_negative_returns_none() {
1207        assert_eq!(
1208            <I128s12 as ToPrimitive>::to_u128(&I128s12::from_bits(-1)),
1209            None
1210        );
1211    }
1212
1213    #[test]
1214    fn to_primitive_u128_in_range() {
1215        assert_eq!(
1216            <I128s12 as ToPrimitive>::to_u128(&I128s12::ZERO),
1217            Some(0_u128)
1218        );
1219        assert_eq!(
1220            <I128s12 as ToPrimitive>::to_u128(&I128s12::from_bits(99_000_000_000_000)),
1221            Some(99_u128)
1222        );
1223    }
1224
1225    #[test]
1226    fn to_primitive_f64_round_trip_within_lsb() {
1227        let lsb = 1.0 / (I128s12::multiplier() as f64);
1228        // Use a value not close to any well-known math constant.
1229        let v = I128s12::from_f64_lossy(1.234567890123_f64);
1230        let back = <I128s12 as ToPrimitive>::to_f64(&v).expect("to_f64 always returns Some");
1231        assert!(
1232            (back - 1.234567890123_f64).abs() <= lsb * 2.0,
1233            "round-trip exceeded 2 LSB: back = {back}, lsb = {lsb}"
1234        );
1235    }
1236
1237    #[test]
1238    fn to_primitive_f32_matches_to_f32_lossy() {
1239        let v = I128s12::from_bits(1_500_000_000_000);
1240        assert_eq!(
1241            <I128s12 as ToPrimitive>::to_f32(&v),
1242            Some(v.to_f32_lossy())
1243        );
1244    }
1245
1246    /// `ToPrimitive` provides default impls for `to_i32`, `to_u32`, etc.
1247    /// via `to_i64` / `to_u64`. Verify the delegation chain works.
1248    #[test]
1249    fn to_primitive_smaller_int_types_via_default_impl() {
1250        let v = I128s12::from_bits(42_000_000_000_000);
1251        assert_eq!(<I128s12 as ToPrimitive>::to_i32(&v), Some(42_i32));
1252        assert_eq!(<I128s12 as ToPrimitive>::to_u32(&v), Some(42_u32));
1253        assert_eq!(<I128s12 as ToPrimitive>::to_i16(&v), Some(42_i16));
1254        assert_eq!(<I128s12 as ToPrimitive>::to_u16(&v), Some(42_u16));
1255        assert_eq!(<I128s12 as ToPrimitive>::to_i8(&v), Some(42_i8));
1256        assert_eq!(<I128s12 as ToPrimitive>::to_u8(&v), Some(42_u8));
1257
1258        // Out-of-range narrowing returns None.
1259        let big = I128s12::from_bits(40_000_000_000_000_000); // 40_000
1260        assert_eq!(<I128s12 as ToPrimitive>::to_i8(&big), None);
1261        assert_eq!(<I128s12 as ToPrimitive>::to_u8(&big), None);
1262    }
1263
1264    // ---------------------------------------------------------------------------
1265    // CheckedAdd / CheckedSub
1266    // ---------------------------------------------------------------------------
1267
1268    #[test]
1269    fn checked_add_basic() {
1270        let one = I128s12::ONE;
1271        let two = I128s12::from_bits(2_000_000_000_000);
1272        assert_eq!(
1273            <I128s12 as CheckedAdd>::checked_add(&one, &one),
1274            Some(two)
1275        );
1276    }
1277
1278    #[test]
1279    fn checked_add_overflow_returns_none() {
1280        // MAX + ONE overflows.
1281        assert_eq!(
1282            <I128s12 as CheckedAdd>::checked_add(&I128s12::MAX, &I128s12::ONE),
1283            None
1284        );
1285        // MAX + ZERO is fine.
1286        assert_eq!(
1287            <I128s12 as CheckedAdd>::checked_add(&I128s12::MAX, &I128s12::ZERO),
1288            Some(I128s12::MAX)
1289        );
1290    }
1291
1292    #[test]
1293    fn checked_sub_basic() {
1294        let three = I128s12::from_bits(3_000_000_000_000);
1295        let two = I128s12::from_bits(2_000_000_000_000);
1296        assert_eq!(
1297            <I128s12 as CheckedSub>::checked_sub(&three, &two),
1298            Some(I128s12::ONE)
1299        );
1300    }
1301
1302    #[test]
1303    fn checked_sub_underflow_returns_none() {
1304        // MIN - ONE underflows.
1305        assert_eq!(
1306            <I128s12 as CheckedSub>::checked_sub(&I128s12::MIN, &I128s12::ONE),
1307            None
1308        );
1309    }
1310
1311    // ---------------------------------------------------------------------------
1312    // CheckedMul / CheckedDiv / CheckedRem
1313    // ---------------------------------------------------------------------------
1314
1315    #[test]
1316    fn checked_mul_basic() {
1317        let half = I128s12::from_bits(500_000_000_000); // 0.5
1318        let quarter = I128s12::from_bits(250_000_000_000); // 0.25
1319        assert_eq!(
1320            <I128s12 as CheckedMul>::checked_mul(&half, &half),
1321            Some(quarter)
1322        );
1323    }
1324
1325    #[test]
1326    fn checked_mul_overflow_returns_none() {
1327        // MAX * 2 overflows.
1328        let two = I128s12::from_bits(2_000_000_000_000);
1329        assert_eq!(
1330            <I128s12 as CheckedMul>::checked_mul(&I128s12::MAX, &two),
1331            None
1332        );
1333    }
1334
1335    #[test]
1336    fn checked_div_basic() {
1337        let half = I128s12::from_bits(500_000_000_000); // 0.5
1338        let quarter = I128s12::from_bits(250_000_000_000); // 0.25
1339        let two = I128s12::from_bits(2_000_000_000_000); // 2.0
1340        // 0.5 / 2.0 == 0.25
1341        assert_eq!(
1342            <I128s12 as CheckedDiv>::checked_div(&half, &two),
1343            Some(quarter)
1344        );
1345    }
1346
1347    #[test]
1348    fn checked_div_by_zero_returns_none() {
1349        assert_eq!(
1350            <I128s12 as CheckedDiv>::checked_div(&I128s12::ONE, &I128s12::ZERO),
1351            None
1352        );
1353    }
1354
1355    #[test]
1356    fn checked_div_overflow_returns_none() {
1357        // The only true checked_div overflow is MIN / -ONE (negating i128::MIN
1358        // overflows in two's-complement).
1359        let neg_one = -I128s12::ONE;
1360        assert_eq!(
1361            <I128s12 as CheckedDiv>::checked_div(&I128s12::MIN, &neg_one),
1362            None
1363        );
1364        // MAX / ONE returns Some(MAX) via the widening path.
1365        assert_eq!(
1366            <I128s12 as CheckedDiv>::checked_div(&I128s12::MAX, &I128s12::ONE),
1367            Some(I128s12::MAX)
1368        );
1369    }
1370
1371    #[test]
1372    fn checked_rem_basic() {
1373        let a = I128s12::from_bits(5_500_000_000_000); // 5.5
1374        let b = I128s12::from_bits(2_000_000_000_000); // 2.0
1375        let expected = I128s12::from_bits(1_500_000_000_000); // 1.5
1376        assert_eq!(
1377            <I128s12 as CheckedRem>::checked_rem(&a, &b),
1378            Some(expected)
1379        );
1380    }
1381
1382    #[test]
1383    fn checked_rem_by_zero_returns_none() {
1384        assert_eq!(
1385            <I128s12 as CheckedRem>::checked_rem(&I128s12::ONE, &I128s12::ZERO),
1386            None
1387        );
1388    }
1389
1390    // ---------------------------------------------------------------------------
1391    // CheckedNeg
1392    // ---------------------------------------------------------------------------
1393
1394    #[test]
1395    fn checked_neg_basic() {
1396        let one = I128s12::ONE;
1397        let neg_one = -I128s12::ONE;
1398        assert_eq!(
1399            <I128s12 as CheckedNeg>::checked_neg(&one),
1400            Some(neg_one)
1401        );
1402        assert_eq!(
1403            <I128s12 as CheckedNeg>::checked_neg(&I128s12::ZERO),
1404            Some(I128s12::ZERO)
1405        );
1406    }
1407
1408    #[test]
1409    fn checked_neg_min_returns_none() {
1410        // i128::MIN has no positive counterpart, so checked_neg returns None.
1411        assert_eq!(
1412            <I128s12 as CheckedNeg>::checked_neg(&I128s12::MIN),
1413            None
1414        );
1415    }
1416
1417    // ---------------------------------------------------------------------------
1418    // CheckedMul / CheckedDiv trait-vs-inherent alignment
1419    // ---------------------------------------------------------------------------
1420    //
1421    // Assert that the num-traits trait impls and the inherent methods
1422    // produce bit-identical results for 256 deterministic pairs plus
1423    // boundary cases. A failure here means the two paths diverged.
1424
1425    /// Generates a deterministic sequence of `i128` values using a
1426    /// linear congruential generator seeded from `seed`.
1427    fn lcg_i128_seq(seed: i128, n: usize) -> Vec<i128> {
1428        // LCG constants from Knuth TAOCP Vol 2 (applied in i128 with wrapping).
1429        let mut state: i128 = seed;
1430        let mut out = Vec::with_capacity(n);
1431        for _ in 0..n {
1432            state = state
1433                .wrapping_mul(6_364_136_223_846_793_005_i128)
1434                .wrapping_add(1_442_695_040_888_963_407_i128);
1435            out.push(state);
1436        }
1437        out
1438    }
1439
1440    /// For 256 deterministic pairs, `<I128 as CheckedMul>::checked_mul`
1441    /// must equal `I128::checked_mul` (the inherent method).
1442    #[test]
1443    fn checked_mul_trait_matches_inherent_256_pairs() {
1444        let seeds = lcg_i128_seq(0x1234_5678_9ABC_DEF0, 512);
1445        for pair in seeds.chunks_exact(2) {
1446            let a = I128s12::from_bits(pair[0]);
1447            let b = I128s12::from_bits(pair[1]);
1448            let trait_result = <I128s12 as CheckedMul>::checked_mul(&a, &b);
1449            let inherent_result = a.checked_mul(b);
1450            assert_eq!(
1451                trait_result, inherent_result,
1452                "CheckedMul trait != inherent for a={a:?} b={b:?}"
1453            );
1454        }
1455    }
1456
1457    /// For 256 deterministic pairs, `<I128 as CheckedDiv>::checked_div`
1458    /// must equal `I128::checked_div` (the inherent method).
1459    #[test]
1460    fn checked_div_trait_matches_inherent_256_pairs() {
1461        let seeds = lcg_i128_seq(0xDEAD_BEEF_CAFE_0001, 512);
1462        for pair in seeds.chunks_exact(2) {
1463            let a = I128s12::from_bits(pair[0]);
1464            // Avoid divide-by-zero: if the LCG lands on zero, substitute ONE.
1465            // The by-zero case is covered by a dedicated test.
1466            let b_bits = if pair[1] == 0 { I128s12::multiplier() } else { pair[1] };
1467            let b = I128s12::from_bits(b_bits);
1468            let trait_result = <I128s12 as CheckedDiv>::checked_div(&a, &b);
1469            let inherent_result = a.checked_div(b);
1470            assert_eq!(
1471                trait_result, inherent_result,
1472                "CheckedDiv trait != inherent for a={a:?} b={b:?}"
1473            );
1474        }
1475    }
1476
1477    /// Boundary cases for CheckedMul trait-vs-inherent alignment.
1478    #[test]
1479    fn checked_mul_trait_matches_inherent_boundary() {
1480        let cases: &[(I128s12, I128s12)] = &[
1481            (I128s12::MAX, I128s12::ZERO),
1482            (I128s12::MIN, I128s12::ZERO),
1483            (I128s12::MAX, I128s12::ONE),
1484            (I128s12::MIN, I128s12::ONE),
1485            (I128s12::MAX, I128s12::MAX),
1486            (I128s12::MIN, I128s12::MIN),
1487            (I128s12::from_bits(0), I128s12::from_bits(0)),
1488            (I128s12::from_bits(1), I128s12::from_bits(1)),
1489            (I128s12::from_bits(-1), I128s12::from_bits(1)),
1490            (I128s12::from_bits(1), I128s12::from_bits(-1)),
1491            (I128s12::from_bits(-1), I128s12::from_bits(-1)),
1492        ];
1493        for &(a, b) in cases {
1494            let trait_result = <I128s12 as CheckedMul>::checked_mul(&a, &b);
1495            let inherent_result = a.checked_mul(b);
1496            assert_eq!(
1497                trait_result, inherent_result,
1498                "CheckedMul trait != inherent at boundary a={a:?} b={b:?}"
1499            );
1500        }
1501    }
1502
1503    /// Boundary cases for CheckedDiv trait-vs-inherent alignment.
1504    #[test]
1505    fn checked_div_trait_matches_inherent_boundary() {
1506        let neg_one = -I128s12::ONE;
1507        let cases: &[(I128s12, I128s12)] = &[
1508            (I128s12::MAX, I128s12::ONE),
1509            (I128s12::MIN, I128s12::ONE),
1510            (I128s12::MAX, I128s12::MAX),
1511            (I128s12::MIN, I128s12::MIN),
1512            (I128s12::ZERO, I128s12::ONE),
1513            (I128s12::ONE, I128s12::MAX),
1514            // divide by zero — both must return None
1515            (I128s12::ONE, I128s12::ZERO),
1516            (I128s12::MAX, I128s12::ZERO),
1517            // true overflow case: MIN / -ONE
1518            (I128s12::MIN, neg_one),
1519            (I128s12::from_bits(1), I128s12::from_bits(1)),
1520            (I128s12::from_bits(-1), I128s12::from_bits(1)),
1521            (I128s12::from_bits(1), I128s12::from_bits(-1)),
1522            (I128s12::from_bits(-1), I128s12::from_bits(-1)),
1523        ];
1524        for &(a, b) in cases {
1525            let trait_result = <I128s12 as CheckedDiv>::checked_div(&a, &b);
1526            let inherent_result = a.checked_div(b);
1527            assert_eq!(
1528                trait_result, inherent_result,
1529                "CheckedDiv trait != inherent at boundary a={a:?} b={b:?}"
1530            );
1531        }
1532    }
1533
1534    // ---------------------------------------------------------------------------
1535    // Num::from_str_radix
1536    // ---------------------------------------------------------------------------
1537
1538    /// Non-base-10 radix is rejected without delegating to FromStr.
1539    #[test]
1540    fn from_str_radix_non_ten_returns_invalid() {
1541        let result = <I128s12 as Num>::from_str_radix("1", 16);
1542        assert!(result.is_err());
1543
1544        let result_2 = <I128s12 as Num>::from_str_radix("1", 2);
1545        assert!(result_2.is_err());
1546    }
1547
1548    /// Base-10 delegates to the FromStr implementation.
1549    #[test]
1550    fn from_str_radix_base_ten_delegates_to_from_str() {
1551        let parsed = <I128s12 as Num>::from_str_radix("1", 10).expect("parse 1");
1552        assert_eq!(parsed, I128s12::ONE);
1553    }
1554
1555    // ---------------------------------------------------------------------------
1556    // Cross-scale exercise — non-default SCALE
1557    // ---------------------------------------------------------------------------
1558
1559    /// At SCALE = 6 the trait surface works correctly.
1560    #[test]
1561    fn traits_compile_at_scale_6() {
1562        type D6 = I128<6>;
1563        assert_eq!(<D6 as Zero>::zero(), D6::ZERO);
1564        assert_eq!(<D6 as One>::one(), D6::ONE);
1565        assert_eq!(<D6 as Bounded>::min_value(), D6::MIN);
1566        assert_eq!(<D6 as Bounded>::max_value(), D6::MAX);
1567
1568        let v: D6 = <D6 as FromPrimitive>::from_i64(42).unwrap();
1569        assert_eq!(<D6 as ToPrimitive>::to_i64(&v), Some(42_i64));
1570    }
1571
1572    // ---------------------------------------------------------------------------
1573    // NumCast
1574    // ---------------------------------------------------------------------------
1575
1576    /// `NumCast::from` round-trips an in-range `i32` exactly.
1577    #[test]
1578    fn numcast_from_i32() {
1579        let v: I128s12 = <I128s12 as NumCast>::from(42_i32).expect("in-range");
1580        assert_eq!(v, <I128s12 as From<i32>>::from(42_i32));
1581    }
1582
1583    /// `NumCast::from` preserves the fractional part of an `f64` input
1584    /// because the float path runs before the integer truncation path.
1585    #[test]
1586    fn numcast_from_f64_preserves_fractional() {
1587        let v: I128s12 = <I128s12 as NumCast>::from(1.5_f64).expect("in-range");
1588        assert_eq!(v, I128s12::from_f64_lossy(1.5_f64));
1589    }
1590
1591    /// `NumCast::from` returns `None` for `f64::NAN`.
1592    #[test]
1593    fn numcast_from_f64_nan_returns_none() {
1594        assert!(<I128s12 as NumCast>::from(f64::NAN).is_none());
1595    }
1596
1597    /// `NumCast::from` returns `None` for finite out-of-range `f64`.
1598    #[test]
1599    fn numcast_from_f64_out_of_range_returns_none() {
1600        assert!(<I128s12 as NumCast>::from(1e30_f64).is_none());
1601    }
1602
1603    /// `NumCast::from` keeps integer inputs exact for `i64` values above
1604    /// f64's 53-bit mantissa range, validating the integer fast path.
1605    #[test]
1606    fn numcast_from_i64_above_f64_mantissa_is_exact() {
1607        // 2^54 = 18_014_398_509_481_984 — above f64's exact-integer range.
1608        let v: i64 = 1_i64 << 54;
1609        let d: I128s12 = <I128s12 as NumCast>::from(v).expect("in-range");
1610        assert_eq!(<I128s12 as ToPrimitive>::to_i64(&d), Some(v));
1611    }
1612}