Skip to main content

num_bigint/biguint/
addition.rs

1use super::{BigUint, IntDigits};
2
3use crate::big_digit::{self, BigDigit};
4use crate::UsizePromotion;
5
6use core::iter::Sum;
7use core::ops::{Add, AddAssign};
8use num_traits::CheckedAdd;
9
10#[cfg(target_arch = "x86_64")]
11use core::arch::x86_64 as arch;
12
13#[cfg(target_arch = "x86")]
14use core::arch::x86 as arch;
15
16// Add with carry:
17#[cfg(target_arch = "x86_64")]
18cfg_64!(
19    #[inline]
20    #[allow(unused_unsafe)] // TODO(MSRV 1.93): the intrinsic became safe
21    fn adc(carry: u8, a: u64, b: u64, out: &mut u64) -> u8 {
22        // SAFETY: There are absolutely no safety concerns with calling `_addcarry_u64`.
23        // It's just unsafe for API consistency with other intrinsics.
24        unsafe { arch::_addcarry_u64(carry, a, b, out) }
25    }
26);
27
28#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
29cfg_32!(
30    #[inline]
31    #[allow(unused_unsafe)] // TODO(MSRV 1.93): the intrinsic became safe
32    fn adc(carry: u8, a: u32, b: u32, out: &mut u32) -> u8 {
33        // SAFETY: There are absolutely no safety concerns with calling `_addcarry_u32`.
34        // It's just unsafe for API consistency with other intrinsics.
35        unsafe { arch::_addcarry_u32(carry, a, b, out) }
36    }
37);
38
39// fallback for environments where we don't have an addcarry intrinsic
40// (copied from the standard library's `carrying_add`)
41#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
42#[inline]
43fn adc(carry: u8, lhs: BigDigit, rhs: BigDigit, out: &mut BigDigit) -> u8 {
44    let (a, b) = lhs.overflowing_add(rhs);
45    let (c, d) = a.overflowing_add(carry as BigDigit);
46    *out = c;
47    u8::from(b || d)
48}
49
50/// Two argument addition of raw slices, `a += b`, returning the carry.
51///
52/// This is used when the data `Vec` might need to resize to push a non-zero carry, so we perform
53/// the addition first hoping that it will fit.
54///
55/// The caller _must_ ensure that `a` is at least as long as `b`.
56#[inline]
57pub(super) fn __add2(a: &mut [BigDigit], b: &[BigDigit]) -> BigDigit {
58    debug_assert!(a.len() >= b.len());
59
60    let mut carry = 0;
61    let (a_lo, a_hi) = a.split_at_mut(b.len());
62
63    for (a, b) in a_lo.iter_mut().zip(b) {
64        carry = adc(carry, *a, *b, a);
65    }
66
67    if carry != 0 {
68        for a in a_hi {
69            carry = adc(carry, *a, 0, a);
70            if carry == 0 {
71                break;
72            }
73        }
74    }
75
76    carry as BigDigit
77}
78
79/// Two argument addition of raw slices:
80/// a += b
81///
82/// The caller _must_ ensure that a is big enough to store the result - typically this means
83/// resizing a to max(a.len(), b.len()) + 1, to fit a possible carry.
84pub(super) fn add2(a: &mut [BigDigit], b: &[BigDigit]) {
85    let carry = __add2(a, b);
86
87    debug_assert!(carry == 0);
88}
89
90forward_all_binop_to_val_ref_commutative!(impl Add for BigUint, add);
91forward_val_assign!(impl AddAssign for BigUint, add_assign);
92
93impl Add<&BigUint> for BigUint {
94    type Output = BigUint;
95
96    fn add(mut self, other: &BigUint) -> BigUint {
97        self += other;
98        self
99    }
100}
101impl AddAssign<&BigUint> for BigUint {
102    #[inline]
103    fn add_assign(&mut self, other: &BigUint) {
104        let self_len = self.data.len();
105        let mut other = &*other.data;
106        if self_len < other.len() {
107            let (low, high) = other.split_at(self_len);
108            self.data.extend_from_slice(high);
109            other = low;
110        }
111        let carry = __add2(&mut self.data, other);
112        if carry != 0 {
113            self.data.push(carry);
114        }
115    }
116}
117
118promote_unsigned_scalars!(impl Add for BigUint, add);
119promote_unsigned_scalars_assign!(impl AddAssign for BigUint, add_assign);
120forward_all_scalar_binop_to_val_val_commutative!(impl Add<u32> for BigUint, add);
121forward_all_scalar_binop_to_val_val_commutative!(impl Add<u64> for BigUint, add);
122forward_all_scalar_binop_to_val_val_commutative!(impl Add<u128> for BigUint, add);
123
124impl Add<u32> for BigUint {
125    type Output = BigUint;
126
127    #[inline]
128    fn add(mut self, other: u32) -> BigUint {
129        self += other;
130        self
131    }
132}
133
134impl AddAssign<u32> for BigUint {
135    #[inline]
136    fn add_assign(&mut self, other: u32) {
137        if other != 0 {
138            if self.data.is_empty() {
139                self.data.push(other as BigDigit);
140            } else {
141                let carry = __add2(&mut self.data, &[other as BigDigit]);
142                if carry != 0 {
143                    self.data.push(carry);
144                }
145            }
146        }
147    }
148}
149
150impl Add<u64> for BigUint {
151    type Output = BigUint;
152
153    #[inline]
154    fn add(mut self, other: u64) -> BigUint {
155        self += other;
156        self
157    }
158}
159
160impl AddAssign<u64> for BigUint {
161    cfg_digit!(
162        #[inline]
163        fn add_assign(&mut self, other: u64) {
164            let (hi, lo) = big_digit::from_doublebigdigit(other);
165            if hi == 0 {
166                *self += lo;
167            } else {
168                while self.data.len() < 2 {
169                    self.data.push(0);
170                }
171
172                let carry = __add2(&mut self.data, &[lo, hi]);
173                if carry != 0 {
174                    self.data.push(carry);
175                }
176            }
177        }
178
179        #[inline]
180        fn add_assign(&mut self, other: u64) {
181            if other != 0 {
182                if self.data.is_empty() {
183                    self.data.push(other as BigDigit);
184                } else {
185                    let carry = __add2(&mut self.data, &[other as BigDigit]);
186                    if carry != 0 {
187                        self.data.push(carry);
188                    }
189                }
190            }
191        }
192    );
193}
194
195impl Add<u128> for BigUint {
196    type Output = BigUint;
197
198    #[inline]
199    fn add(mut self, other: u128) -> BigUint {
200        self += other;
201        self
202    }
203}
204
205impl AddAssign<u128> for BigUint {
206    cfg_digit!(
207        #[inline]
208        fn add_assign(&mut self, other: u128) {
209            if other <= u128::from(u64::MAX) {
210                *self += other as u64
211            } else {
212                let (a, b, c, d) = super::u32_from_u128(other);
213                let carry = if a > 0 {
214                    while self.data.len() < 4 {
215                        self.data.push(0);
216                    }
217                    __add2(&mut self.data, &[d, c, b, a])
218                } else {
219                    debug_assert!(b > 0);
220                    while self.data.len() < 3 {
221                        self.data.push(0);
222                    }
223                    __add2(&mut self.data, &[d, c, b])
224                };
225
226                if carry != 0 {
227                    self.data.push(carry);
228                }
229            }
230        }
231
232        #[inline]
233        fn add_assign(&mut self, other: u128) {
234            let (hi, lo) = big_digit::from_doublebigdigit(other);
235            if hi == 0 {
236                *self += lo;
237            } else {
238                while self.data.len() < 2 {
239                    self.data.push(0);
240                }
241
242                let carry = __add2(&mut self.data, &[lo, hi]);
243                if carry != 0 {
244                    self.data.push(carry);
245                }
246            }
247        }
248    );
249}
250
251impl CheckedAdd for BigUint {
252    #[inline]
253    fn checked_add(&self, v: &BigUint) -> Option<BigUint> {
254        Some(self.add(v))
255    }
256}
257
258impl_sum_iter_type!(BigUint);