1use crate::{
2 Choice, Limb, Odd, U64, U128, Uint, Word,
3 primitives::{u32_max, u32_min},
4 word,
5};
6
7#[inline(always)]
22pub(super) const fn bingcd_step<const LIMBS: usize>(
23 a: &mut Uint<LIMBS>,
24 b: &mut Uint<LIMBS>,
25) -> (Choice, Choice, Word) {
26 let a_b_mod_4 = (a.limbs[0].0 & b.limbs[0].0) & 3;
27
28 let a_odd = a.is_odd();
29 let (a_sub_b, borrow) = a.borrowing_sub(&Uint::select(&Uint::ZERO, b, a_odd), Limb::ZERO);
30 let swap = borrow.is_nonzero();
31 *b = Uint::select(b, a, swap);
32 *a = a_sub_b.wrapping_neg_if(swap).shr1();
33
34 let mut jacobi_neg = word::select(0, a_b_mod_4 & (a_b_mod_4 >> 1) & 1, swap);
36
37 let b_lo = b.limbs[0].0;
40 jacobi_neg ^= ((b_lo >> 1) ^ (b_lo >> 2)) & 1;
41
42 (a_odd, swap, jacobi_neg)
43}
44
45impl<const LIMBS: usize> Odd<Uint<LIMBS>> {
46 pub(crate) const MINIMAL_BINGCD_ITERATIONS: u32 = 2 * Self::BITS - 1;
48
49 #[inline]
57 pub(crate) const fn classic_bingcd(&self, rhs: &Uint<LIMBS>) -> Self {
58 self.classic_bingcd_(rhs).0
59 }
60
61 #[inline(always)]
72 pub(crate) const fn classic_bingcd_(&self, rhs: &Uint<LIMBS>) -> (Self, Word) {
73 let (mut a, mut b) = (*rhs, *self.as_ref());
75 let mut i = 0;
76 let mut jacobi_neg = 0;
77
78 while i < Self::MINIMAL_BINGCD_ITERATIONS {
79 jacobi_neg ^= bingcd_step(&mut a, &mut b).2;
80 i += 1;
81 }
82
83 let gcd = b
84 .to_odd()
85 .expect_copied("gcd of an odd value with something else is always odd");
86
87 (gcd, jacobi_neg)
88 }
89
90 #[inline]
92 pub(crate) const fn classic_bingcd_vartime(&self, rhs: &Uint<LIMBS>) -> Self {
93 self.classic_bingcd_vartime_(rhs).0
94 }
95
96 #[inline(always)]
98 pub(crate) const fn classic_bingcd_vartime_(&self, rhs: &Uint<LIMBS>) -> (Self, Word) {
99 let (mut a, mut b) = (*rhs, *self.as_ref());
101 let mut jacobi_neg = 0;
102 while !a.is_zero_vartime() {
103 jacobi_neg ^= bingcd_step(&mut a, &mut b).2;
104 }
105
106 let gcd = b
107 .to_odd()
108 .expect_copied("gcd of an odd value with something else is always odd");
109
110 (gcd, jacobi_neg)
111 }
112
113 #[inline(always)]
125 pub(crate) const fn optimized_bingcd(&self, rhs: &Uint<LIMBS>) -> Self {
126 self.optimized_bingcd_::<{ U64::BITS }, { U64::LIMBS }, { U128::LIMBS }>(rhs, U64::BITS - 1)
127 .0
128 }
129
130 #[inline(always)]
149 pub(crate) const fn optimized_bingcd_<
150 const K: u32,
151 const LIMBS_K: usize,
152 const LIMBS_2K: usize,
153 >(
154 &self,
155 rhs: &Uint<LIMBS>,
156 batch_max: u32,
157 ) -> (Self, Word) {
158 let (mut a, mut b) = (*self.as_ref(), *rhs);
159 let mut jacobi_neg = 0;
160
161 let mut iterations = Self::MINIMAL_BINGCD_ITERATIONS;
162 while iterations > 0 {
163 let batch = u32_min(iterations, batch_max);
164 iterations -= batch;
165
166 let n = u32_max(2 * K, a.bitor(&b).bits());
168 let a_ = a.compact::<K, LIMBS_2K>(n);
169 let b_ = b.compact::<K, LIMBS_2K>(n);
170
171 let (.., matrix, j_neg) = a_
173 .to_odd()
174 .expect_copied("a_ is always odd")
175 .partial_binxgcd::<LIMBS_K>(&b_, batch, Choice::FALSE);
176 jacobi_neg ^= j_neg;
177
178 let (updated_a, updated_b) = matrix.extended_apply_to_vartime::<LIMBS>((a, b));
181 (a, _) = updated_a.dropped_abs_sign();
182 (b, _) = updated_b.dropped_abs_sign();
183 }
184
185 (
186 a.to_odd()
187 .expect_copied("gcd of an odd value is always odd"),
188 jacobi_neg,
189 )
190 }
191
192 #[inline(always)]
194 pub(crate) const fn optimized_bingcd_vartime(&self, rhs: &Uint<LIMBS>) -> Self {
195 self.optimized_bingcd_vartime_::<{ U64::BITS }, { U64::LIMBS }, { U128::LIMBS }>(
196 rhs,
197 U64::BITS - 1,
198 )
199 .0
200 }
201
202 #[inline(always)]
204 pub(crate) const fn optimized_bingcd_vartime_<
205 const K: u32,
206 const LIMBS_K: usize,
207 const LIMBS_2K: usize,
208 >(
209 &self,
210 rhs: &Uint<LIMBS>,
211 batch_max: u32,
212 ) -> (Self, Word) {
213 let (mut a, mut b) = (*self.as_ref(), *rhs);
214 let mut jacobi_neg = 0;
215
216 while !b.is_zero_vartime() {
217 let n = u32_max(2 * K, u32_max(a.bits_vartime(), b.bits_vartime()));
219 let mut a_ = a.compact_vartime::<K, LIMBS_2K>(n);
220 let mut b_ = b.compact_vartime::<K, LIMBS_2K>(n);
221
222 if n <= Uint::<LIMBS_2K>::BITS {
223 loop {
224 jacobi_neg ^= bingcd_step(&mut b_, &mut a_).2;
225 if b_.is_zero_vartime() {
226 break;
227 }
228 }
229 a = a_.resize();
230 break;
231 }
232
233 let (.., matrix, j_neg) = a_
235 .to_odd()
236 .expect_copied("a_ is always odd")
237 .partial_binxgcd::<LIMBS_K>(&b_, batch_max, Choice::FALSE);
238 jacobi_neg ^= j_neg;
239
240 let (updated_a, updated_b) = matrix.extended_apply_to_vartime((a, b));
242 (a, _) = updated_a.dropped_abs_sign();
243 (b, _) = updated_b.dropped_abs_sign();
244 }
245
246 (
247 a.to_odd()
248 .expect_copied("gcd of an odd value with something else is always odd"),
249 jacobi_neg,
250 )
251 }
252}
253
254#[cfg(all(test, not(miri)))]
255mod tests {
256
257 mod test_classic_bingcd {
258 use crate::{Int, U64, U128, U192, U256, U384, U512, U1024, U2048, U4096, Uint};
259
260 fn classic_bingcd_test<const LIMBS: usize>(
261 lhs: Uint<LIMBS>,
262 rhs: Uint<LIMBS>,
263 target: Uint<LIMBS>,
264 ) {
265 assert_eq!(
266 lhs.to_odd().unwrap().classic_bingcd(&rhs),
267 target.to_odd().unwrap()
268 );
269 assert_eq!(
270 lhs.to_odd().unwrap().classic_bingcd_vartime(&rhs),
271 target.to_odd().unwrap()
272 );
273 }
274
275 fn classic_bingcd_tests<const LIMBS: usize>() {
276 classic_bingcd_test(Uint::<LIMBS>::ONE, Uint::ZERO, Uint::ONE);
277 classic_bingcd_test(Uint::<LIMBS>::ONE, Uint::ONE, Uint::ONE);
278 classic_bingcd_test(Uint::<LIMBS>::ONE, Int::MAX.abs(), Uint::ONE);
279 classic_bingcd_test(Uint::<LIMBS>::ONE, Int::MIN.abs(), Uint::ONE);
280 classic_bingcd_test(Uint::<LIMBS>::ONE, Uint::MAX, Uint::ONE);
281 classic_bingcd_test(Int::<LIMBS>::MAX.abs(), Uint::ZERO, Int::MAX.abs());
282 classic_bingcd_test(Int::<LIMBS>::MAX.abs(), Uint::ONE, Uint::ONE);
283 classic_bingcd_test(Int::<LIMBS>::MAX.abs(), Int::MAX.abs(), Int::MAX.abs());
284 classic_bingcd_test(Int::<LIMBS>::MAX.abs(), Int::MIN.abs(), Uint::ONE);
285 classic_bingcd_test(Int::<LIMBS>::MAX.abs(), Uint::MAX, Uint::ONE);
286 classic_bingcd_test(Uint::<LIMBS>::MAX, Uint::ZERO, Uint::MAX);
287 classic_bingcd_test(Uint::<LIMBS>::MAX, Uint::ONE, Uint::ONE);
288 classic_bingcd_test(Uint::<LIMBS>::MAX, Int::MAX.abs(), Uint::ONE);
289 classic_bingcd_test(Uint::<LIMBS>::MAX, Int::MIN.abs(), Uint::ONE);
290 classic_bingcd_test(Uint::<LIMBS>::MAX, Uint::MAX, Uint::MAX);
291 }
292
293 #[test]
294 fn test_classic_bingcd() {
295 classic_bingcd_tests::<{ U64::LIMBS }>();
296 classic_bingcd_tests::<{ U128::LIMBS }>();
297 classic_bingcd_tests::<{ U192::LIMBS }>();
298 classic_bingcd_tests::<{ U256::LIMBS }>();
299 classic_bingcd_tests::<{ U384::LIMBS }>();
300 classic_bingcd_tests::<{ U512::LIMBS }>();
301 classic_bingcd_tests::<{ U1024::LIMBS }>();
302 classic_bingcd_tests::<{ U2048::LIMBS }>();
303 classic_bingcd_tests::<{ U4096::LIMBS }>();
304 }
305 }
306
307 mod test_optimized_bingcd {
308 use crate::{Int, U128, U192, U256, U384, U512, U1024, U2048, U4096, Uint};
309
310 fn optimized_bingcd_test<const LIMBS: usize>(
311 lhs: Uint<LIMBS>,
312 rhs: Uint<LIMBS>,
313 target: Uint<LIMBS>,
314 ) {
315 assert_eq!(
316 lhs.to_odd().unwrap().optimized_bingcd(&rhs),
317 target.to_odd().unwrap()
318 );
319 assert_eq!(
320 lhs.to_odd().unwrap().optimized_bingcd_vartime(&rhs),
321 target.to_odd().unwrap()
322 );
323 }
324
325 fn optimized_bingcd_tests<const LIMBS: usize>() {
326 optimized_bingcd_test(Uint::<LIMBS>::ONE, Uint::ZERO, Uint::ONE);
327 optimized_bingcd_test(Uint::<LIMBS>::ONE, Uint::ONE, Uint::ONE);
328 optimized_bingcd_test(Uint::<LIMBS>::ONE, Int::MAX.abs(), Uint::ONE);
329 optimized_bingcd_test(Uint::<LIMBS>::ONE, Int::MIN.abs(), Uint::ONE);
330 optimized_bingcd_test(Uint::<LIMBS>::ONE, Uint::MAX, Uint::ONE);
331 optimized_bingcd_test(Int::<LIMBS>::MAX.abs(), Uint::ZERO, Int::MAX.abs());
332 optimized_bingcd_test(Int::<LIMBS>::MAX.abs(), Uint::ONE, Uint::ONE);
333 optimized_bingcd_test(Int::<LIMBS>::MAX.abs(), Int::MAX.abs(), Int::MAX.abs());
334 optimized_bingcd_test(Int::<LIMBS>::MAX.abs(), Int::MIN.abs(), Uint::ONE);
335 optimized_bingcd_test(Int::<LIMBS>::MAX.abs(), Uint::MAX, Uint::ONE);
336 optimized_bingcd_test(Uint::<LIMBS>::MAX, Uint::ZERO, Uint::MAX);
337 optimized_bingcd_test(Uint::<LIMBS>::MAX, Uint::ONE, Uint::ONE);
338 optimized_bingcd_test(Uint::<LIMBS>::MAX, Int::MAX.abs(), Uint::ONE);
339 optimized_bingcd_test(Uint::<LIMBS>::MAX, Int::MIN.abs(), Uint::ONE);
340 optimized_bingcd_test(Uint::<LIMBS>::MAX, Uint::MAX, Uint::MAX);
341 }
342
343 #[test]
344 fn test_optimized_bingcd() {
345 optimized_bingcd_tests::<{ U128::LIMBS }>();
347 optimized_bingcd_tests::<{ U192::LIMBS }>();
348 optimized_bingcd_tests::<{ U256::LIMBS }>();
349 optimized_bingcd_tests::<{ U384::LIMBS }>();
350 optimized_bingcd_tests::<{ U512::LIMBS }>();
351 optimized_bingcd_tests::<{ U1024::LIMBS }>();
352 optimized_bingcd_tests::<{ U2048::LIMBS }>();
353 optimized_bingcd_tests::<{ U4096::LIMBS }>();
354 }
355 }
356}