Skip to main content

const_num_traits/ops/
carrying.rs

1//! Carry/borrow-propagating and widening integer-arithmetic primitives for
2//! chaining multi-word ("bigint") arithmetic, mirroring std's unstable
3//! `bigint_helper_methods` (`carrying_add` / `borrowing_sub` / `carrying_mul`
4//! / `widening_mul`). For unsigned types these stabilized in Rust 1.91; the
5//! signed forms and `widening_mul` are still unstable.
6//!
7//! All bodies are hand-rolled (ported from core's portable intrinsic
8//! fallbacks) so they work on the crate's MSRV and inside `const` on nightly.
9//!
10//! **CT tier A (CT-implementable)**: carries and borrows are returned for
11//! arithmetic consumption; all bodies are branchless on the data.
12
13#[cfg(target_pointer_width = "16")]
14type UDoubleSize = u32;
15#[cfg(target_pointer_width = "32")]
16type UDoubleSize = u64;
17#[cfg(target_pointer_width = "64")]
18type UDoubleSize = u128;
19
20#[cfg(target_pointer_width = "16")]
21type IDoubleSize = i32;
22#[cfg(target_pointer_width = "32")]
23type IDoubleSize = i64;
24#[cfg(target_pointer_width = "64")]
25type IDoubleSize = i128;
26
27c0nst::c0nst! {
28/// Performs addition with a carry bit, for chaining multi-word additions.
29pub c0nst trait CarryingAdd: Sized {
30    /// The sum type (`Self` for the primitive impls).
31    type Output;
32    /// Calculates `self + rhs + carry` and returns a tuple containing the sum
33    /// and the output carry, performing the full addition rather than
34    /// stopping at the first overflow.
35    ///
36    /// For unsigned types this allows chaining together multiple additions
37    /// to create a wider addition. For signed types the second tuple field
38    /// signals two's-complement overflow instead of a carry.
39    ///
40    /// ```
41    /// use const_num_traits::CarryingAdd;
42    ///
43    /// // u8::MAX + 1 + carry
44    /// assert_eq!(CarryingAdd::carrying_add(u8::MAX, 1, true), (1, true));
45    /// assert_eq!(CarryingAdd::carrying_add(5u8, 2, false), (7, false));
46    /// ```
47    fn carrying_add(self, rhs: Self, carry: bool) -> (Self::Output, bool);
48}
49}
50
51macro_rules! carrying_add_impl {
52    // unsigned: carries combine with OR
53    (unsigned $($t:ty)*) => {$(
54        c0nst::c0nst! {
55        c0nst impl CarryingAdd for $t {
56            type Output = $t;
57            #[inline]
58            fn carrying_add(self, rhs: Self, carry: bool) -> ($t, bool) {
59                let (a, c1) = <$t>::overflowing_add(self, rhs);
60                let (b, c2) = <$t>::overflowing_add(a, carry as $t);
61                // only one overflow can happen, so c1 | c2 never double-counts
62                (b, c1 | c2)
63            }
64        }
65        }
66    )*};
67    // signed: overflows cancel out pairwise, so XOR
68    (signed $($t:ty)*) => {$(
69        c0nst::c0nst! {
70        c0nst impl CarryingAdd for $t {
71            type Output = $t;
72            #[inline]
73            fn carrying_add(self, rhs: Self, carry: bool) -> ($t, bool) {
74                let (a, b) = <$t>::overflowing_add(self, rhs);
75                let (c, d) = <$t>::overflowing_add(a, carry as $t);
76                (c, b != d)
77            }
78        }
79        }
80    )*};
81}
82
83carrying_add_impl!(unsigned usize u8 u16 u32 u64 u128);
84carrying_add_impl!(signed isize i8 i16 i32 i64 i128);
85
86c0nst::c0nst! {
87/// Performs subtraction with a borrow bit, for chaining multi-word subtractions.
88pub c0nst trait BorrowingSub: Sized {
89    /// The difference type (`Self` for the primitive impls).
90    type Output;
91    /// Calculates `self - rhs - borrow` and returns a tuple containing the
92    /// difference and the output borrow, performing the full subtraction
93    /// rather than stopping at the first overflow.
94    ///
95    /// For unsigned types this allows chaining together multiple
96    /// subtractions to create a wider subtraction. For signed types the
97    /// second tuple field signals two's-complement overflow instead.
98    ///
99    /// ```
100    /// use const_num_traits::BorrowingSub;
101    ///
102    /// assert_eq!(BorrowingSub::borrowing_sub(0u8, 0, true), (u8::MAX, true));
103    /// assert_eq!(BorrowingSub::borrowing_sub(7u8, 2, false), (5, false));
104    /// ```
105    fn borrowing_sub(self, rhs: Self, borrow: bool) -> (Self::Output, bool);
106}
107}
108
109macro_rules! borrowing_sub_impl {
110    (unsigned $($t:ty)*) => {$(
111        c0nst::c0nst! {
112        c0nst impl BorrowingSub for $t {
113            type Output = $t;
114            #[inline]
115            fn borrowing_sub(self, rhs: Self, borrow: bool) -> ($t, bool) {
116                let (a, c1) = <$t>::overflowing_sub(self, rhs);
117                let (b, c2) = <$t>::overflowing_sub(a, borrow as $t);
118                (b, c1 | c2)
119            }
120        }
121        }
122    )*};
123    (signed $($t:ty)*) => {$(
124        c0nst::c0nst! {
125        c0nst impl BorrowingSub for $t {
126            type Output = $t;
127            #[inline]
128            fn borrowing_sub(self, rhs: Self, borrow: bool) -> ($t, bool) {
129                let (a, b) = <$t>::overflowing_sub(self, rhs);
130                let (c, d) = <$t>::overflowing_sub(a, borrow as $t);
131                (c, b != d)
132            }
133        }
134        }
135    )*};
136}
137
138borrowing_sub_impl!(unsigned usize u8 u16 u32 u64 u128);
139borrowing_sub_impl!(signed isize i8 i16 i32 i64 i128);
140
141c0nst::c0nst! {
142/// Performs full double-width multiplication with carry and addend inputs,
143/// the workhorse for multi-word ("bigint") multiplication.
144pub c0nst trait CarryingMul: Sized {
145    /// The type of the low half of the result: `Self` for unsigned types,
146    /// the unsigned counterpart for signed types (matching std's
147    /// `carrying_mul` signatures).
148    type Unsigned;
149
150    /// The type of the high half of the result (`Self` for the primitive impls).
151    type Output;
152
153    /// Calculates the "full multiplication" `self * rhs + carry` without the
154    /// possibility to overflow, returning `(low, high)` words of the result.
155    ///
156    /// ```
157    /// use const_num_traits::CarryingMul;
158    ///
159    /// assert_eq!(CarryingMul::carrying_mul(u8::MAX, u8::MAX, u8::MAX), (0, u8::MAX));
160    /// assert_eq!(CarryingMul::carrying_mul(5u8, 7, 3), (38, 0));
161    /// ```
162    fn carrying_mul(self, rhs: Self, carry: Self) -> (Self::Unsigned, Self::Output);
163
164    /// Calculates the "full multiplication" `self * rhs + carry + add`
165    /// without the possibility to overflow, returning `(low, high)` words.
166    ///
167    /// This cannot overflow because `MAX * MAX + MAX + MAX` still fits in
168    /// twice the bit width.
169    fn carrying_mul_add(self, rhs: Self, carry: Self, add: Self) -> (Self::Unsigned, Self::Output);
170}
171}
172
173macro_rules! carrying_mul_impl {
174    // widening through a double-width type; for signed types the low half
175    // comes back as the unsigned counterpart, like std
176    ($($t:ty, $u:ty, $w:ty;)*) => {$(
177        c0nst::c0nst! {
178        c0nst impl CarryingMul for $t {
179            type Unsigned = $u;
180            type Output = $t;
181
182            #[inline]
183            fn carrying_mul(self, rhs: Self, carry: Self) -> ($u, $t) {
184                let wide = (self as $w) * (rhs as $w) + (carry as $w);
185                (wide as $u, (wide >> <$t>::BITS) as $t)
186            }
187
188            #[inline]
189            fn carrying_mul_add(self, rhs: Self, carry: Self, add: Self) -> ($u, $t) {
190                let wide = (self as $w) * (rhs as $w) + (carry as $w) + (add as $w);
191                (wide as $u, (wide >> <$t>::BITS) as $t)
192            }
193        }
194        }
195    )*};
196}
197
198carrying_mul_impl! {
199    u8, u8, u16;
200    u16, u16, u32;
201    u32, u32, u64;
202    u64, u64, u128;
203    usize, usize, UDoubleSize;
204    i8, u8, i16;
205    i16, u16, i32;
206    i32, u32, i64;
207    i64, u64, i128;
208    isize, usize, IDoubleSize;
209}
210
211// 128-bit types have no wider type to widen through; multiply in 64-bit
212// limbs instead. Ported from core's portable intrinsic fallback.
213pub(crate) const fn wide_mul_u128(a: u128, b: u128) -> (u128, u128) {
214    const MASK: u128 = u64::MAX as u128;
215    let (a_lo, a_hi) = (a & MASK, a >> 64);
216    let (b_lo, b_hi) = (b & MASK, b >> 64);
217
218    // low = a * b_lo, expressed in three 64-bit limbs
219    let t = a_lo * b_lo;
220    let (low0, c) = (t & MASK, t >> 64);
221    let t = a_hi * b_lo + c;
222    let (low1, low2) = (t & MASK, t >> 64);
223
224    // high = a * b_hi, shifted up by one limb
225    let t = a_lo * b_hi;
226    let (high0, c) = (t & MASK, t >> 64);
227    let t = a_hi * b_hi + c;
228    let (high1, high2) = (t & MASK, t >> 64);
229
230    let t = low1 + high0;
231    let (r1, c) = (t & MASK, t >> 64);
232    let t = low2 + high1 + c;
233    let (r2, c) = (t & MASK, t >> 64);
234    let r3 = high2 + c;
235    (low0 | (r1 << 64), r2 | (r3 << 64))
236}
237
238c0nst::c0nst! {
239c0nst impl CarryingMul for u128 {
240    type Unsigned = u128;
241    type Output = u128;
242
243    #[inline]
244    fn carrying_mul(self, rhs: Self, carry: Self) -> (u128, u128) {
245        let (low, mut high) = wide_mul_u128(self, rhs);
246        let (low, c) = u128::overflowing_add(low, carry);
247        high += c as u128;
248        (low, high)
249    }
250
251    #[inline]
252    fn carrying_mul_add(self, rhs: Self, carry: Self, add: Self) -> (u128, u128) {
253        let (low, mut high) = wide_mul_u128(self, rhs);
254        let (low, c) = u128::overflowing_add(low, carry);
255        high += c as u128;
256        let (low, c) = u128::overflowing_add(low, add);
257        high += c as u128;
258        (low, high)
259    }
260}
261}
262
263c0nst::c0nst! {
264c0nst impl CarryingMul for i128 {
265    type Unsigned = u128;
266    type Output = i128;
267
268    #[inline]
269    fn carrying_mul(self, rhs: Self, carry: Self) -> (u128, i128) {
270        let zero = 0i128;
271        CarryingMul::carrying_mul_add(self, rhs, carry, zero)
272    }
273
274    // Unsigned 128x128 multiply, then correct the high word for the signs
275    // of the inputs (`x >> 127` is 0 or -1) and sign-extend the addends.
276    #[inline]
277    fn carrying_mul_add(self, rhs: Self, carry: Self, add: Self) -> (u128, i128) {
278        let (low, high) = wide_mul_u128(self as u128, rhs as u128);
279        let mut high = high as i128;
280        high = high.wrapping_add(i128::wrapping_mul(self >> 127, rhs));
281        high = high.wrapping_add(i128::wrapping_mul(self, rhs >> 127));
282        let (low, c) = u128::overflowing_add(low, carry as u128);
283        high = high.wrapping_add((c as i128) + (carry >> 127));
284        let (low, c) = u128::overflowing_add(low, add as u128);
285        high = high.wrapping_add((c as i128) + (add >> 127));
286        (low, high)
287    }
288}
289}
290
291c0nst::c0nst! {
292/// Performs multiplication that widens into the next-larger integer type, so
293/// it cannot overflow.
294pub c0nst trait WideningMul: Sized {
295    /// The double-width result type.
296    type Wide;
297
298    /// Widening multiplication. Computes the complete product `self * rhs`
299    /// in the next-larger primitive, mirroring core's `widening_mul`
300    /// (`fn widening_mul(self, rhs) -> $WideT` — the single-wide form, as of
301    /// rust-lang/rust#156644).
302    ///
303    /// Because it promotes to a wider *primitive*, it is only provided for
304    /// types that have one (`u8`–`u64`, `i8`–`i64`). `u128`/`usize` and
305    /// arbitrary-precision types — which have no wider primitive — should
306    /// use [`CarryingMul::carrying_mul`] instead, which returns the product
307    /// as `(low, high)` words and works for every width.
308    ///
309    /// ```
310    /// use const_num_traits::WideningMul;
311    ///
312    /// assert_eq!(WideningMul::widening_mul(u8::MAX, u8::MAX), 65025u16);
313    /// assert_eq!(WideningMul::widening_mul(-128i8, -128), 16384i16);
314    /// ```
315    fn widening_mul(self, rhs: Self) -> Self::Wide;
316}
317}
318
319macro_rules! widening_mul_impl {
320    ($($t:ty => $w:ty;)*) => {$(
321        c0nst::c0nst! {
322        c0nst impl WideningMul for $t {
323            type Wide = $w;
324
325            #[inline]
326            fn widening_mul(self, rhs: Self) -> $w {
327                (self as $w) * (rhs as $w)
328            }
329        }
330        }
331    )*};
332}
333
334widening_mul_impl! {
335    u8 => u16;
336    u16 => u32;
337    u32 => u64;
338    u64 => u128;
339    i8 => i16;
340    i16 => i32;
341    i32 => i64;
342    i64 => i128;
343}
344
345#[cfg(test)]
346mod tests {
347    use super::*;
348
349    #[test]
350    fn carrying_add_borrowing_sub() {
351        assert_eq!(
352            CarryingAdd::carrying_add(u8::MAX, u8::MAX, true),
353            (255, true)
354        );
355        assert_eq!(
356            CarryingAdd::carrying_add(0xFFFF_FFFF_FFFF_FFFFu64, 0, true),
357            (0, true)
358        );
359        assert_eq!(CarryingAdd::carrying_add(i8::MAX, 0, true), (i8::MIN, true));
360        assert_eq!(CarryingAdd::carrying_add(-1i8, 1, false), (0, false));
361        assert_eq!(BorrowingSub::borrowing_sub(0u8, u8::MAX, true), (0, true));
362        assert_eq!(BorrowingSub::borrowing_sub(10u8, 3, true), (6, false));
363        assert_eq!(
364            BorrowingSub::borrowing_sub(i8::MIN, 0, true),
365            (i8::MAX, true)
366        );
367    }
368
369    #[test]
370    fn carrying_mul_small() {
371        // chain two words of 255*255
372        assert_eq!(CarryingMul::carrying_mul(u8::MAX, u8::MAX, 0), (1, 254));
373        assert_eq!(
374            CarryingMul::carrying_mul_add(u8::MAX, u8::MAX, u8::MAX, u8::MAX),
375            (255, 255)
376        );
377        assert_eq!(CarryingMul::carrying_mul(-1i8, -1, 0), (1, 0));
378        assert_eq!(CarryingMul::carrying_mul(i8::MIN, i8::MIN, 0), (0, 64));
379        assert_eq!(CarryingMul::carrying_mul(-1i8, 1, 0), (255, -1));
380    }
381
382    #[test]
383    fn carrying_mul_128() {
384        // (2^127) * 2 = 2^128 -> low 0, high 1
385        assert_eq!(CarryingMul::carrying_mul(1u128 << 127, 2, 0), (0, 1));
386        assert_eq!(
387            CarryingMul::carrying_mul(u128::MAX, u128::MAX, u128::MAX),
388            (0, u128::MAX)
389        );
390        // cross-check a "random" value against 64-bit limb math
391        let a = 0x0123_4567_89ab_cdef_fedc_ba98_7654_3210u128;
392        let b = 0x0fed_cba9_8765_4321_1234_5678_9abc_def0u128;
393        let (low, high) = CarryingMul::carrying_mul(a, b, 0);
394        // verify via the identity (a*b) mod 2^128 == low
395        assert_eq!(a.wrapping_mul(b), low);
396        // and high via floor(a*b / 2^128) computed with 64-bit limbs
397        let (l2, h2) = super::wide_mul_u128(a, b);
398        assert_eq!((low, high), (l2, h2));
399
400        // signed: -1 * -1 = 1
401        assert_eq!(CarryingMul::carrying_mul(-1i128, -1, 0), (1, 0));
402        // signed: MIN * -1 = 2^126 * 2 = +2^127 -> (1<<127, 0)
403        assert_eq!(
404            CarryingMul::carrying_mul(i128::MIN, -1, 0),
405            (1u128 << 127, 0)
406        );
407        assert_eq!(CarryingMul::carrying_mul(-1i128, 1, 0), (u128::MAX, -1));
408    }
409
410    #[test]
411    fn widening_mul() {
412        // single wide type, matching core's `widening_mul -> $WideT`
413        assert_eq!(WideningMul::widening_mul(u8::MAX, u8::MAX), 65025u16);
414        assert_eq!(
415            WideningMul::widening_mul(u64::MAX, u64::MAX),
416            u64::MAX as u128 * u64::MAX as u128
417        );
418        assert_eq!(WideningMul::widening_mul(i8::MIN, i8::MIN), 16384i16);
419        assert_eq!(
420            WideningMul::widening_mul(-1i32, i32::MAX),
421            -(i32::MAX as i64)
422        );
423    }
424}