Skip to main content

const_num_traits/ops/
saturating.rs

1//! Saturating arithmetic.
2//!
3//! **CT tier A (CT-implementable)** for add/sub/mul/neg/abs/pow-style
4//! saturation: it is expressible as a branchless select on an overflow mask.
5//! `SaturatingDiv` is CT-hostile — it performs data-dependent division and
6//! panics on a zero divisor — so it is Tier C for secret inputs.
7
8c0nst::c0nst! {
9/// Saturating math operations. Deprecated, use `SaturatingAdd`, `SaturatingSub` and
10/// `SaturatingMul` instead.
11pub c0nst trait Saturating {
12    /// The result type (`Self` for the primitive impls).
13    type Output;
14    /// Saturating addition operator.
15    /// Returns a+b, saturating at the numeric bounds instead of overflowing.
16    fn saturating_add(self, v: Self) -> Self::Output;
17
18    /// Saturating subtraction operator.
19    /// Returns a-b, saturating at the numeric bounds instead of overflowing.
20    fn saturating_sub(self, v: Self) -> Self::Output;
21}
22}
23
24macro_rules! deprecated_saturating_impl {
25    ($trait_name:ident for $($t:ty)*) => {$(
26        c0nst::c0nst! {
27        c0nst impl $trait_name for $t {
28            type Output = $t;
29            #[inline]
30            fn saturating_add(self, v: Self) -> Self {
31                Self::saturating_add(self, v)
32            }
33
34            #[inline]
35            fn saturating_sub(self, v: Self) -> Self {
36                Self::saturating_sub(self, v)
37            }
38        }
39        }
40    )*}
41}
42
43deprecated_saturating_impl!(Saturating for isize i8 i16 i32 i64 i128);
44deprecated_saturating_impl!(Saturating for usize u8 u16 u32 u64 u128);
45
46macro_rules! saturating_impl {
47    ($trait_name:ident, $method:ident, $t:ty) => {
48        c0nst::c0nst! {
49        c0nst impl $trait_name for $t {
50            type Output = $t;
51            #[inline]
52            fn $method(self, v: Self) -> Self {
53                <$t>::$method(self, v)
54            }
55        }
56        }
57    };
58}
59
60c0nst::c0nst! {
61/// Performs addition that saturates at the numeric bounds instead of overflowing.
62pub c0nst trait SaturatingAdd: Sized {
63    /// The result type (`Self` for the primitive impls).
64    type Output;
65    /// Saturating addition. Computes `self + other`, saturating at the relevant high or low boundary of
66    /// the type.
67    fn saturating_add(self, v: Self) -> Self::Output;
68}
69}
70
71saturating_impl!(SaturatingAdd, saturating_add, u8);
72saturating_impl!(SaturatingAdd, saturating_add, u16);
73saturating_impl!(SaturatingAdd, saturating_add, u32);
74saturating_impl!(SaturatingAdd, saturating_add, u64);
75saturating_impl!(SaturatingAdd, saturating_add, usize);
76saturating_impl!(SaturatingAdd, saturating_add, u128);
77
78saturating_impl!(SaturatingAdd, saturating_add, i8);
79saturating_impl!(SaturatingAdd, saturating_add, i16);
80saturating_impl!(SaturatingAdd, saturating_add, i32);
81saturating_impl!(SaturatingAdd, saturating_add, i64);
82saturating_impl!(SaturatingAdd, saturating_add, isize);
83saturating_impl!(SaturatingAdd, saturating_add, i128);
84
85c0nst::c0nst! {
86/// Performs subtraction that saturates at the numeric bounds instead of overflowing.
87pub c0nst trait SaturatingSub: Sized {
88    /// The result type (`Self` for the primitive impls).
89    type Output;
90    /// Saturating subtraction. Computes `self - other`, saturating at the relevant high or low boundary of
91    /// the type.
92    fn saturating_sub(self, v: Self) -> Self::Output;
93}
94}
95
96saturating_impl!(SaturatingSub, saturating_sub, u8);
97saturating_impl!(SaturatingSub, saturating_sub, u16);
98saturating_impl!(SaturatingSub, saturating_sub, u32);
99saturating_impl!(SaturatingSub, saturating_sub, u64);
100saturating_impl!(SaturatingSub, saturating_sub, usize);
101saturating_impl!(SaturatingSub, saturating_sub, u128);
102
103saturating_impl!(SaturatingSub, saturating_sub, i8);
104saturating_impl!(SaturatingSub, saturating_sub, i16);
105saturating_impl!(SaturatingSub, saturating_sub, i32);
106saturating_impl!(SaturatingSub, saturating_sub, i64);
107saturating_impl!(SaturatingSub, saturating_sub, isize);
108saturating_impl!(SaturatingSub, saturating_sub, i128);
109
110c0nst::c0nst! {
111/// Performs multiplication that saturates at the numeric bounds instead of overflowing.
112pub c0nst trait SaturatingMul: Sized {
113    /// The result type (`Self` for the primitive impls).
114    type Output;
115    /// Saturating multiplication. Computes `self * other`, saturating at the relevant high or low boundary of
116    /// the type.
117    fn saturating_mul(self, v: Self) -> Self::Output;
118}
119}
120
121saturating_impl!(SaturatingMul, saturating_mul, u8);
122saturating_impl!(SaturatingMul, saturating_mul, u16);
123saturating_impl!(SaturatingMul, saturating_mul, u32);
124saturating_impl!(SaturatingMul, saturating_mul, u64);
125saturating_impl!(SaturatingMul, saturating_mul, usize);
126saturating_impl!(SaturatingMul, saturating_mul, u128);
127
128saturating_impl!(SaturatingMul, saturating_mul, i8);
129saturating_impl!(SaturatingMul, saturating_mul, i16);
130saturating_impl!(SaturatingMul, saturating_mul, i32);
131saturating_impl!(SaturatingMul, saturating_mul, i64);
132saturating_impl!(SaturatingMul, saturating_mul, isize);
133saturating_impl!(SaturatingMul, saturating_mul, i128);
134
135c0nst::c0nst! {
136/// Performs division that saturates at the numeric bounds instead of overflowing.
137pub c0nst trait SaturatingDiv: Sized {
138    /// The result type (`Self` for the primitive impls).
139    type Output;
140    /// Saturating division. Computes `self / other`, saturating at the
141    /// relevant high or low boundary of the type. The only saturating case is
142    /// `MIN / -1` on a signed type, which saturates to `MAX`.
143    ///
144    /// # Panics
145    ///
146    /// Panics if `other` is zero.
147    ///
148    /// ```
149    /// use const_num_traits::SaturatingDiv;
150    ///
151    /// assert_eq!(SaturatingDiv::saturating_div(5i32, 2), 2);
152    /// assert_eq!(SaturatingDiv::saturating_div(i32::MIN, -1), i32::MAX);
153    /// ```
154    fn saturating_div(self, v: Self) -> Self::Output;
155}
156}
157
158saturating_impl!(SaturatingDiv, saturating_div, u8);
159saturating_impl!(SaturatingDiv, saturating_div, u16);
160saturating_impl!(SaturatingDiv, saturating_div, u32);
161saturating_impl!(SaturatingDiv, saturating_div, u64);
162saturating_impl!(SaturatingDiv, saturating_div, usize);
163saturating_impl!(SaturatingDiv, saturating_div, u128);
164
165saturating_impl!(SaturatingDiv, saturating_div, i8);
166saturating_impl!(SaturatingDiv, saturating_div, i16);
167saturating_impl!(SaturatingDiv, saturating_div, i32);
168saturating_impl!(SaturatingDiv, saturating_div, i64);
169saturating_impl!(SaturatingDiv, saturating_div, isize);
170saturating_impl!(SaturatingDiv, saturating_div, i128);
171
172macro_rules! saturating_unary_impl {
173    ($trait_name:ident, $method:ident, $t:ty) => {
174        c0nst::c0nst! {
175        c0nst impl $trait_name for $t {
176            type Output = $t;
177            #[inline]
178            fn $method(self) -> $t {
179                <$t>::$method(self)
180            }
181        }
182        }
183    };
184}
185
186c0nst::c0nst! {
187/// Performs negation that saturates at the numeric bounds instead of overflowing.
188pub c0nst trait SaturatingNeg: Sized {
189    /// The result type (`Self` for the primitive impls).
190    type Output;
191    /// Saturating negation. Computes `-self`, saturating at the numeric
192    /// bounds: `MIN.saturating_neg()` is `MAX`.
193    ///
194    /// ```
195    /// use const_num_traits::SaturatingNeg;
196    ///
197    /// assert_eq!(SaturatingNeg::saturating_neg(-5i32), 5);
198    /// assert_eq!(SaturatingNeg::saturating_neg(i32::MIN), i32::MAX);
199    /// ```
200    fn saturating_neg(self) -> Self::Output;
201}
202}
203
204saturating_unary_impl!(SaturatingNeg, saturating_neg, i8);
205saturating_unary_impl!(SaturatingNeg, saturating_neg, i16);
206saturating_unary_impl!(SaturatingNeg, saturating_neg, i32);
207saturating_unary_impl!(SaturatingNeg, saturating_neg, i64);
208saturating_unary_impl!(SaturatingNeg, saturating_neg, isize);
209saturating_unary_impl!(SaturatingNeg, saturating_neg, i128);
210
211c0nst::c0nst! {
212/// Computes the absolute value, saturating at the numeric bounds instead of overflowing.
213pub c0nst trait SaturatingAbs: Sized {
214    /// The result type (`Self` for the primitive impls).
215    type Output;
216    /// Saturating absolute value. Computes `self.abs()`, saturating at the
217    /// numeric bounds: `MIN.saturating_abs()` is `MAX`.
218    ///
219    /// ```
220    /// use const_num_traits::SaturatingAbs;
221    ///
222    /// assert_eq!(SaturatingAbs::saturating_abs(-5i32), 5);
223    /// assert_eq!(SaturatingAbs::saturating_abs(i32::MIN), i32::MAX);
224    /// ```
225    fn saturating_abs(self) -> Self::Output;
226}
227}
228
229saturating_unary_impl!(SaturatingAbs, saturating_abs, i8);
230saturating_unary_impl!(SaturatingAbs, saturating_abs, i16);
231saturating_unary_impl!(SaturatingAbs, saturating_abs, i32);
232saturating_unary_impl!(SaturatingAbs, saturating_abs, i64);
233saturating_unary_impl!(SaturatingAbs, saturating_abs, isize);
234saturating_unary_impl!(SaturatingAbs, saturating_abs, i128);
235
236macro_rules! saturating_pow_impl {
237    ($t:ty) => {
238        c0nst::c0nst! {
239        c0nst impl SaturatingPow for $t {
240            type Output = $t;
241            #[inline]
242            fn saturating_pow(self, exp: u32) -> $t {
243                <$t>::saturating_pow(self, exp)
244            }
245        }
246        }
247    };
248}
249
250c0nst::c0nst! {
251/// Performs exponentiation that saturates at the numeric bounds instead of overflowing.
252pub c0nst trait SaturatingPow: Sized {
253    /// The result type (`Self` for the primitive impls).
254    type Output;
255    /// Saturating exponentiation. Computes `self.pow(exp)`, saturating at the
256    /// relevant high or low boundary of the type.
257    ///
258    /// ```
259    /// use const_num_traits::SaturatingPow;
260    ///
261    /// assert_eq!(SaturatingPow::saturating_pow(4u8, 3), 64);
262    /// assert_eq!(SaturatingPow::saturating_pow(4u8, 4), u8::MAX);
263    /// assert_eq!(SaturatingPow::saturating_pow(-2i8, 7), i8::MIN);
264    /// ```
265    fn saturating_pow(self, exp: u32) -> Self::Output;
266}
267}
268
269saturating_pow_impl!(u8);
270saturating_pow_impl!(u16);
271saturating_pow_impl!(u32);
272saturating_pow_impl!(u64);
273saturating_pow_impl!(usize);
274saturating_pow_impl!(u128);
275
276saturating_pow_impl!(i8);
277saturating_pow_impl!(i16);
278saturating_pow_impl!(i32);
279saturating_pow_impl!(i64);
280saturating_pow_impl!(isize);
281saturating_pow_impl!(i128);
282
283#[test]
284fn test_saturating_div_neg_abs_pow() {
285    assert_eq!(SaturatingDiv::saturating_div(i8::MIN, -1), i8::MAX);
286    assert_eq!(SaturatingDiv::saturating_div(100u8, 7), 14);
287    assert_eq!(SaturatingNeg::saturating_neg(i16::MIN), i16::MAX);
288    assert_eq!(SaturatingNeg::saturating_neg(7i16), -7);
289    assert_eq!(SaturatingAbs::saturating_abs(i16::MIN), i16::MAX);
290    assert_eq!(SaturatingAbs::saturating_abs(-7i16), 7);
291    assert_eq!(SaturatingPow::saturating_pow(2u16, 17), u16::MAX);
292    assert_eq!(SaturatingPow::saturating_pow(-2i16, 15), i16::MIN);
293}
294
295#[test]
296fn test_saturating_traits() {
297    fn saturating_add<T: SaturatingAdd<Output = T>>(a: T, b: T) -> T {
298        a.saturating_add(b)
299    }
300    fn saturating_sub<T: SaturatingSub<Output = T>>(a: T, b: T) -> T {
301        a.saturating_sub(b)
302    }
303    fn saturating_mul<T: SaturatingMul<Output = T>>(a: T, b: T) -> T {
304        a.saturating_mul(b)
305    }
306    assert_eq!(saturating_add(255, 1), 255u8);
307    assert_eq!(saturating_add(127, 1), 127i8);
308    assert_eq!(saturating_add(-128, -1), -128i8);
309    assert_eq!(saturating_sub(0, 1), 0u8);
310    assert_eq!(saturating_sub(-128, 1), -128i8);
311    assert_eq!(saturating_sub(127, -1), 127i8);
312    assert_eq!(saturating_mul(255, 2), 255u8);
313    assert_eq!(saturating_mul(127, 2), 127i8);
314    assert_eq!(saturating_mul(-128, 2), -128i8);
315}