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