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