Skip to main content

decimal_scaled/types/traits/
arithmetic.rs

1//! The [`DecimalArithmetic`] trait — type info, operators, sign,
2//! pow / overflow-variant arithmetic, and reductions shared by every
3//! decimal width.
4//!
5//! Split out of the original [`crate::Decimal`] trait alongside
6//! [`crate::DecimalConvert`], [`crate::DecimalTranscendental`], and
7//! [`crate::DecimalConstants`]; `Decimal` is now a marker supertrait
8//! that requires all four. Callers who only need arithmetic (not
9//! conversions or transcendentals) can target this narrower bound:
10//!
11//! ```ignore
12//! use decimal_scaled::DecimalArithmetic;
13//!
14//! fn dot<T: DecimalArithmetic + Copy>(a: &[T], b: &[T]) -> T {
15//!     a.iter().zip(b).map(|(x, y)| (*x) * (*y))
16//!         .fold(T::ZERO, |acc, p| acc + p)
17//! }
18//! ```
19//!
20//! See [`crate::types::traits::decimal`] for the full scope rationale.
21
22/// Arithmetic surface shared by every decimal width: type info,
23/// constants, operators, sign methods, integer-shape predicates,
24/// integer-style division helpers, integer-exponent powers, the
25/// checked / wrapping / saturating / overflowing variants of every
26/// operator, and reductions (`sum` / `product`).
27///
28/// See module-level docs for usage; see [`crate::DecimalConvert`]
29/// for the conversion surface and [`crate::Decimal`] for the marker
30/// supertrait that combines all four halves.
31pub trait DecimalArithmetic:
32    Copy
33    + PartialEq
34    + Eq
35    + PartialOrd
36    + Ord
37    + Default
38    + core::fmt::Debug
39    + core::fmt::Display
40    + core::hash::Hash
41    + core::ops::Add<Output = Self>
42    + core::ops::Sub<Output = Self>
43    + core::ops::Mul<Output = Self>
44    + core::ops::Div<Output = Self>
45    + core::ops::Rem<Output = Self>
46    + core::ops::Neg<Output = Self>
47    + core::ops::AddAssign
48    + core::ops::SubAssign
49    + core::ops::MulAssign
50    + core::ops::DivAssign
51    + core::ops::RemAssign
52    + core::ops::BitAnd<Output = Self>
53    + core::ops::BitOr<Output = Self>
54    + core::ops::BitXor<Output = Self>
55    + core::ops::Not<Output = Self>
56    + core::ops::Shl<u32, Output = Self>
57    + core::ops::Shr<u32, Output = Self>
58{
59    /// Underlying integer storage type (e.g. `i128` for `D38<SCALE>`).
60    type Storage: Copy + PartialEq + Eq;
61
62    /// The decimal scale of this type.
63    const SCALE: u32;
64
65    /// The maximum legal `SCALE` for this width.
66    const MAX_SCALE: u32;
67
68    /// The additive identity.
69    const ZERO: Self;
70
71    /// The multiplicative identity.
72    const ONE: Self;
73
74    /// The largest representable value.
75    const MAX: Self;
76
77    /// The smallest representable value.
78    const MIN: Self;
79
80    /// Returns `10^SCALE`.
81    fn multiplier() -> Self::Storage;
82
83    // ── Sign ──────────────────────────────────────────────────────────
84
85    fn abs(self) -> Self;
86    fn signum(self) -> Self;
87    fn is_positive(self) -> bool;
88    fn is_negative(self) -> bool;
89
90    // ── Integer-shape predicates (always-const for decimals) ────────
91
92    fn is_nan(self) -> bool;
93    fn is_infinite(self) -> bool;
94    fn is_finite(self) -> bool;
95
96    // ── Integer methods ──────────────────────────────────────────────
97
98    fn div_euclid(self, rhs: Self) -> Self;
99    fn rem_euclid(self, rhs: Self) -> Self;
100    fn div_floor(self, rhs: Self) -> Self;
101    fn div_ceil(self, rhs: Self) -> Self;
102    fn abs_diff(self, rhs: Self) -> Self;
103    fn midpoint(self, rhs: Self) -> Self;
104    fn mul_add(self, a: Self, b: Self) -> Self;
105
106    // ── Integer-exponent powers ──────────────────────────────────────
107
108    fn pow(self, exp: u32) -> Self;
109    fn powi(self, exp: i32) -> Self;
110    fn checked_pow(self, exp: u32) -> Option<Self>;
111    fn wrapping_pow(self, exp: u32) -> Self;
112    fn saturating_pow(self, exp: u32) -> Self;
113    fn overflowing_pow(self, exp: u32) -> (Self, bool);
114
115    // ── Overflow-variant arithmetic ─────────────────────────────────
116
117    fn checked_add(self, rhs: Self) -> Option<Self>;
118    fn checked_sub(self, rhs: Self) -> Option<Self>;
119    fn checked_mul(self, rhs: Self) -> Option<Self>;
120    fn checked_div(self, rhs: Self) -> Option<Self>;
121    fn checked_neg(self) -> Option<Self>;
122    fn checked_rem(self, rhs: Self) -> Option<Self>;
123
124    fn wrapping_add(self, rhs: Self) -> Self;
125    fn wrapping_sub(self, rhs: Self) -> Self;
126    fn wrapping_mul(self, rhs: Self) -> Self;
127    fn wrapping_div(self, rhs: Self) -> Self;
128    fn wrapping_neg(self) -> Self;
129    fn wrapping_rem(self, rhs: Self) -> Self;
130
131    fn saturating_add(self, rhs: Self) -> Self;
132    fn saturating_sub(self, rhs: Self) -> Self;
133    fn saturating_mul(self, rhs: Self) -> Self;
134    fn saturating_div(self, rhs: Self) -> Self;
135    fn saturating_neg(self) -> Self;
136
137    fn overflowing_add(self, rhs: Self) -> (Self, bool);
138    fn overflowing_sub(self, rhs: Self) -> (Self, bool);
139    fn overflowing_mul(self, rhs: Self) -> (Self, bool);
140    fn overflowing_div(self, rhs: Self) -> (Self, bool);
141    fn overflowing_neg(self) -> (Self, bool);
142    fn overflowing_rem(self, rhs: Self) -> (Self, bool);
143
144    // ── Default reductions ───────────────────────────────────────────
145
146    #[inline]
147    fn is_zero(self) -> bool {
148        self == Self::ZERO
149    }
150
151    #[inline]
152    fn is_one(self) -> bool {
153        self == Self::ONE
154    }
155
156    #[inline]
157    fn is_normal(self) -> bool {
158        !self.is_zero()
159    }
160
161    #[inline]
162    fn sum<I>(iter: I) -> Self
163    where
164        I: IntoIterator<Item = Self>,
165    {
166        iter.into_iter().fold(Self::ZERO, |acc, x| acc + x)
167    }
168
169    #[inline]
170    fn product<I>(iter: I) -> Self
171    where
172        I: IntoIterator<Item = Self>,
173    {
174        iter.into_iter().fold(Self::ONE, |acc, x| acc * x)
175    }
176}