1use super::gcd::bingcd_step;
3use crate::modular::bingcd::matrix::{DividedIntMatrix, DividedPatternMatrix, PatternMatrix, Unit};
4use crate::primitives::u32_max;
5use crate::{Choice, Int, Limb, NonZeroUint, Odd, OddUint, U64, U128, Uint};
6
7#[inline(always)]
32const fn binxgcd_step<const LIMBS: usize, const MATRIX_LIMBS: usize>(
33 a: &mut Uint<LIMBS>,
34 b: &mut Uint<LIMBS>,
35 matrix: &mut DividedPatternMatrix<MATRIX_LIMBS>,
36 halt_at_zero: Choice,
37) {
38 let (a_odd, swap, _) = bingcd_step(a, b);
39
40 matrix.conditional_swap_rows(swap);
42
43 matrix.conditional_subtract_bottom_row_from_top(a_odd);
45
46 matrix.conditional_double_bottom_row(a.is_nonzero().or(halt_at_zero.not()));
48}
49
50pub(crate) struct RawXgcdOutput<const LIMBS: usize, MATRIX> {
52 gcd: OddUint<LIMBS>,
53 matrix: MATRIX,
54}
55
56pub(crate) type DividedPatternXgcdOutput<const LIMBS: usize> =
57 RawXgcdOutput<LIMBS, DividedPatternMatrix<LIMBS>>;
58
59impl<const LIMBS: usize> DividedPatternXgcdOutput<LIMBS> {
60 pub(crate) const fn divide(self) -> PatternXgcdOutput<LIMBS> {
73 let DividedPatternMatrix {
74 inner: mut matrix,
75 k,
76 k_upper_bound,
77 ..
78 } = self.matrix;
79
80 let PatternMatrix {
81 m00: x,
82 m01: y,
83 m10: rhs_div_gcd,
84 m11: lhs_div_gcd,
85 ..
86 } = &mut matrix;
87
88 if k_upper_bound > 0 {
89 *x = x.bounded_div2k_mod_q(
90 k,
91 k_upper_bound,
92 &rhs_div_gcd.to_odd().expect_copied("odd by construction"),
93 );
94 *y = y.bounded_div2k_mod_q(
95 k,
96 k_upper_bound,
97 &lhs_div_gcd.to_odd().expect_copied("odd by construction"),
98 );
99 }
100
101 PatternXgcdOutput {
102 gcd: self.gcd,
103 matrix,
104 }
105 }
106}
107
108pub(crate) type PatternXgcdOutput<const LIMBS: usize> = RawXgcdOutput<LIMBS, PatternMatrix<LIMBS>>;
109
110impl<const LIMBS: usize> PatternXgcdOutput<LIMBS> {
111 pub(crate) const fn gcd(&self) -> OddUint<LIMBS> {
113 self.gcd
114 }
115
116 pub(crate) const fn bezout_coefficients(&self) -> (Int<LIMBS>, Int<LIMBS>) {
118 let PatternMatrix {
119 m00,
120 m01,
121 m10,
122 m11,
123 pattern,
124 ..
125 } = self.matrix;
126
127 let m10_sub_m00 = m10.wrapping_sub(&m00);
129 let m11_sub_m01 = m11.wrapping_sub(&m01);
130 let apply = Uint::lte(&m10_sub_m00, &m00).and(Uint::lte(&m11_sub_m01, &m01));
131
132 let m00 = *Uint::select(&m00, &m10_sub_m00, apply)
133 .wrapping_neg_if(apply.xor(pattern.not()))
134 .as_int();
135 let m01 = *Uint::select(&m01, &m11_sub_m01, apply)
136 .wrapping_neg_if(apply.xor(pattern))
137 .as_int();
138 (m00, m01)
139 }
140
141 pub(crate) const fn quotients(&self) -> (Uint<LIMBS>, Uint<LIMBS>) {
143 let PatternMatrix {
144 m10: rhs_div_gcd,
145 m11: lhs_div_gcd,
146 ..
147 } = self.matrix;
148 (lhs_div_gcd, rhs_div_gcd)
149 }
150}
151
152const SUMMARY_BITS: u32 = U64::BITS - 1;
154
155const SUMMARY_LIMBS: usize = U64::LIMBS;
157
158const DOUBLE_SUMMARY_LIMBS: usize = U128::LIMBS;
160
161impl<const LIMBS: usize> OddUint<LIMBS> {
162 const MIN_BINXGCD_ITERATIONS: u32 = 2 * Self::BITS - 1;
164
165 pub(crate) const fn binxgcd_nz(&self, rhs: &NonZeroUint<LIMBS>) -> PatternXgcdOutput<LIMBS> {
168 let (lhs_, rhs_) = (self.as_ref(), rhs.as_ref());
169
170 let rhs_is_even = rhs_.is_odd().not();
174 let (abs_diff, rhs_gt_lhs) = lhs_.abs_diff(rhs_);
175 let odd_rhs = Odd::new_unchecked(Uint::select(rhs_, &abs_diff, rhs_is_even));
176
177 let mut output = self.binxgcd_odd(&odd_rhs);
178 let matrix = &mut output.matrix;
179
180 let case_one = rhs_is_even.and(rhs_gt_lhs);
182 matrix.conditional_subtract_right_column_from_left(case_one);
183
184 let case_two = rhs_is_even.and(rhs_gt_lhs.not());
185 matrix.conditional_add_right_column_to_left(case_two);
186 matrix.conditional_negate(case_two);
187
188 output
189 }
190
191 #[inline]
195 pub(crate) const fn binxgcd_odd(&self, rhs: &Self) -> PatternXgcdOutput<LIMBS> {
196 if LIMBS < 4 {
197 self.classic_binxgcd(rhs).divide()
198 } else {
199 self.optimized_binxgcd(rhs).divide()
200 }
201 }
202
203 pub(crate) const fn classic_binxgcd(&self, rhs: &Self) -> DividedPatternXgcdOutput<LIMBS> {
210 let (gcd, _, matrix) =
211 self.partial_binxgcd::<LIMBS>(rhs.as_ref(), Self::MIN_BINXGCD_ITERATIONS, Choice::TRUE);
212 DividedPatternXgcdOutput { gcd, matrix }
213 }
214
215 pub(crate) const fn optimized_binxgcd(&self, rhs: &Self) -> DividedPatternXgcdOutput<LIMBS> {
230 assert!(Self::BITS >= U128::BITS);
231 self.optimized_binxgcd_::<SUMMARY_BITS, SUMMARY_LIMBS, DOUBLE_SUMMARY_LIMBS>(rhs)
232 }
233
234 pub(crate) const fn optimized_binxgcd_<
251 const K: u32,
252 const LIMBS_K: usize,
253 const LIMBS_2K: usize,
254 >(
255 &self,
256 rhs: &Self,
257 ) -> DividedPatternXgcdOutput<LIMBS> {
258 let (mut a, mut b) = (*self.as_ref(), *rhs.as_ref());
259 let mut state = DividedIntMatrix::UNIT;
260
261 let (mut a_is_negative, mut b_is_negative);
262 let mut i = 0;
263 while i < Self::MIN_BINXGCD_ITERATIONS.div_ceil(K - 1) {
264 i += 1;
269
270 let b_bits = b.bits();
272 let n = u32_max(2 * K, u32_max(a.bits(), b_bits));
273 let compact_a = a.compact::<K, LIMBS_2K>(n);
274 let compact_b = b.compact::<K, LIMBS_2K>(n);
275 let b_eq_compact_b =
276 Choice::from_u32_le(b_bits, K - 1).or(Choice::from_u32_eq(n, 2 * K));
277
278 let (.., update_matrix) = compact_a
280 .to_odd()
281 .expect_copied("a is always odd")
282 .partial_binxgcd::<LIMBS_K>(&compact_b, K - 1, b_eq_compact_b);
283
284 let (updated_a, updated_b) = update_matrix.extended_apply_to::<LIMBS, K>((a, b));
286 (a, a_is_negative) = updated_a.dropped_abs_sign();
287 (b, b_is_negative) = updated_b.dropped_abs_sign();
288
289 state = update_matrix.mul_int_matrix(&state);
290
291 state.conditional_negate_top_row(a_is_negative);
293 state.conditional_negate_bottom_row(b_is_negative);
294 }
295
296 let gcd = a
297 .to_odd()
298 .expect_copied("gcd of an odd value with something else is always odd");
299
300 let matrix = state.to_divided_pattern_matrix();
301 DividedPatternXgcdOutput { gcd, matrix }
302 }
303
304 #[inline(always)]
318 pub(super) const fn partial_binxgcd<const UPDATE_LIMBS: usize>(
319 &self,
320 rhs: &Uint<LIMBS>,
321 iterations: u32,
322 halt_at_zero: Choice,
323 ) -> (Self, Uint<LIMBS>, DividedPatternMatrix<UPDATE_LIMBS>) {
324 let (mut a, mut b) = (*self.as_ref(), *rhs);
325 let mut matrix = DividedPatternMatrix::UNIT;
327
328 Uint::swap(&mut a, &mut b);
333 matrix.swap_rows();
334
335 let mut i = 0;
336
337 while i < iterations {
338 binxgcd_step::<LIMBS, UPDATE_LIMBS>(&mut a, &mut b, &mut matrix, halt_at_zero);
339 i += 1;
340 }
341
342 Uint::swap(&mut a, &mut b);
344 matrix.swap_rows();
345
346 let a = a.to_odd().expect_copied("a is always odd");
347 (a, b, matrix)
348 }
349}
350
351impl<const LIMBS: usize> Uint<LIMBS> {
352 const fn abs_diff(&self, rhs: &Self) -> (Self, Choice) {
355 let (diff, borrow) = self.borrowing_sub(rhs, Limb::ZERO);
356 let rhs_gt_self = borrow.is_nonzero();
357 let abs_diff = diff.wrapping_neg_if(rhs_gt_self);
358 (abs_diff, rhs_gt_self)
359 }
360}
361
362#[cfg(all(test, not(miri)))]
363mod tests {
364 use crate::modular::bingcd::xgcd::PatternXgcdOutput;
365 use crate::{Concat, Uint};
366 use core::ops::Div;
367
368 mod test_extract_quotients {
369 use crate::modular::bingcd::matrix::DividedPatternMatrix;
370 use crate::modular::bingcd::xgcd::{DividedPatternXgcdOutput, RawXgcdOutput};
371 use crate::{Choice, U64, Uint};
372
373 fn raw_binxgcdoutput_setup<const LIMBS: usize>(
374 matrix: DividedPatternMatrix<LIMBS>,
375 ) -> DividedPatternXgcdOutput<LIMBS> {
376 RawXgcdOutput {
377 gcd: Uint::<LIMBS>::ONE.to_odd().unwrap(),
378 matrix,
379 }
380 }
381
382 #[test]
383 fn test_extract_quotients_unit() {
384 let output =
385 raw_binxgcdoutput_setup(DividedPatternMatrix::<{ U64::LIMBS }>::UNIT).divide();
386 let (lhs_on_gcd, rhs_on_gcd) = output.quotients();
387 assert_eq!(lhs_on_gcd, Uint::ONE);
388 assert_eq!(rhs_on_gcd, Uint::ZERO);
389 }
390
391 #[test]
392 fn test_extract_quotients_basic() {
393 let output = raw_binxgcdoutput_setup(DividedPatternMatrix::<{ U64::LIMBS }>::new_u64(
394 (0, 0, 5, 7),
395 Choice::FALSE,
396 0,
397 0,
398 ))
399 .divide();
400 let (lhs_on_gcd, rhs_on_gcd) = output.quotients();
401 assert_eq!(lhs_on_gcd, Uint::from(7u32));
402 assert_eq!(rhs_on_gcd, Uint::from(5u32));
403
404 let output = raw_binxgcdoutput_setup(DividedPatternMatrix::<{ U64::LIMBS }>::new_u64(
405 (0, 0, 7u64, 5u64),
406 Choice::TRUE,
407 0,
408 0,
409 ))
410 .divide();
411 let (lhs_on_gcd, rhs_on_gcd) = output.quotients();
412 assert_eq!(lhs_on_gcd, Uint::from(5u32));
413 assert_eq!(rhs_on_gcd, Uint::from(7u32));
414 }
415 }
416
417 mod test_derive_bezout_coefficients {
418 use crate::modular::bingcd::matrix::DividedPatternMatrix;
419 use crate::modular::bingcd::xgcd::RawXgcdOutput;
420 use crate::{Choice, Int, U64, Uint};
421
422 #[test]
423 fn test_derive_bezout_coefficients_unit() {
424 let output = RawXgcdOutput {
425 gcd: Uint::ONE.to_odd().unwrap(),
426 matrix: DividedPatternMatrix::<{ U64::LIMBS }>::UNIT,
427 }
428 .divide();
429 let (x, y) = output.bezout_coefficients();
430 assert_eq!(x, Int::ONE);
431 assert_eq!(y, Int::ZERO);
432 }
433
434 #[test]
435 fn test_derive_bezout_coefficients_basic() {
436 let output = RawXgcdOutput {
437 gcd: U64::ONE.to_odd().unwrap(),
438 matrix: DividedPatternMatrix::new_u64((2u64, 3u64, 5u64, 5u64), Choice::TRUE, 0, 0),
439 }
440 .divide();
441 let (x, y) = output.bezout_coefficients();
442 assert_eq!(x, Int::from(2i32));
443 assert_eq!(y, Int::from(-3i32));
444
445 let output = RawXgcdOutput {
446 gcd: U64::ONE.to_odd().unwrap(),
447 matrix: DividedPatternMatrix::new_u64(
448 (2u64, 3u64, 3u64, 5u64),
449 Choice::FALSE,
450 0,
451 1,
452 ),
453 }
454 .divide();
455 let (x, y) = output.bezout_coefficients();
456 assert_eq!(x, Int::from(1i32));
457 assert_eq!(y, Int::from(-2i32));
458 }
459
460 #[test]
461 fn test_derive_bezout_coefficients_removes_doublings_easy() {
462 let output = RawXgcdOutput {
463 gcd: U64::ONE.to_odd().unwrap(),
464 matrix: DividedPatternMatrix::new_u64((2u64, 6u64, 3u64, 5u64), Choice::TRUE, 1, 1),
465 }
466 .divide();
467 let (x, y) = output.bezout_coefficients();
468 assert_eq!(x, Int::ONE);
469 assert_eq!(y, Int::from(-3i32));
470
471 let output = RawXgcdOutput {
472 gcd: U64::ONE.to_odd().unwrap(),
473 matrix: DividedPatternMatrix::new_u64(
474 (120u64, 64u64, 7u64, 5u64),
475 Choice::FALSE,
476 5,
477 6,
478 ),
479 }
480 .divide();
481 let (x, y) = output.bezout_coefficients();
482 assert_eq!(x, Int::from(-9i32));
483 assert_eq!(y, Int::from(2i32));
484 }
485
486 #[test]
487 fn test_derive_bezout_coefficients_removes_doublings_for_odd_numbers() {
488 let output = RawXgcdOutput {
489 gcd: U64::ONE.to_odd().unwrap(),
490 matrix: DividedPatternMatrix::new_u64(
491 (2u64, 6u64, 7u64, 5u64),
492 Choice::FALSE,
493 3,
494 7,
495 ),
496 }
497 .divide();
498 let (x, y) = output.bezout_coefficients();
499 assert_eq!(x, Int::from(-2i32));
500 assert_eq!(y, Int::from(2i32));
501 }
502 }
503
504 mod test_partial_binxgcd {
505 use crate::modular::bingcd::matrix::DividedPatternMatrix;
506 use crate::{Choice, Gcd, Odd, U64};
507
508 const A: Odd<U64> = Odd::from_be_hex("CA048AFA63CD6A1F");
509 const B: U64 = U64::from_be_hex("AE693BF7BE8E5566");
510
511 #[test]
512 fn test_partial_binxgcd() {
513 let (.., matrix) = A.partial_binxgcd::<{ U64::LIMBS }>(&B, 5, Choice::TRUE);
514 assert_eq!(matrix.k, 5);
515 assert_eq!(
516 matrix,
517 DividedPatternMatrix::new_u64((8u64, 4u64, 2u64, 5u64), Choice::TRUE, 5, 5)
518 );
519 }
520
521 #[test]
522 fn test_partial_binxgcd_constructs_correct_matrix() {
523 let target_a = U64::from_be_hex("1CB3FB3FA1218FDB").to_odd().unwrap();
524 let target_b = U64::from_be_hex("0EA028AF0F8966B6");
525
526 let (new_a, new_b, matrix) = A.partial_binxgcd::<{ U64::LIMBS }>(&B, 5, Choice::TRUE);
527
528 assert_eq!(new_a, target_a);
529 assert_eq!(new_b, target_b);
530
531 let (computed_a, computed_b) =
532 matrix.extended_apply_to::<{ U64::LIMBS }, 6>((A.get(), B));
533 let computed_a = computed_a.dropped_abs_sign().0;
534 let computed_b = computed_b.dropped_abs_sign().0;
535
536 assert_eq!(computed_a, target_a);
537 assert_eq!(computed_b, target_b);
538 }
539
540 const SMALL_A: Odd<U64> = Odd::from_be_hex("0000000003CD6A1F");
541 const SMALL_B: U64 = U64::from_be_hex("000000000E8E5566");
542
543 #[test]
544 fn test_partial_binxgcd_halts() {
545 let (gcd, _, matrix) =
546 SMALL_A.partial_binxgcd::<{ U64::LIMBS }>(&SMALL_B, 60, Choice::TRUE);
547 assert_eq!(matrix.k, 35);
548 assert_eq!(matrix.k_upper_bound, 60);
549 assert_eq!(gcd.get(), SMALL_A.gcd(&SMALL_B));
550 }
551
552 #[test]
553 fn test_partial_binxgcd_does_not_halt() {
554 let (gcd, .., matrix) =
555 SMALL_A.partial_binxgcd::<{ U64::LIMBS }>(&SMALL_B, 60, Choice::FALSE);
556 assert_eq!(matrix.k, 60);
557 assert_eq!(matrix.k_upper_bound, 60);
558 assert_eq!(gcd.get(), SMALL_A.gcd(&SMALL_B));
559 }
560 }
561
562 fn test_xgcd<const LIMBS: usize, const DOUBLE: usize>(
564 lhs: Uint<LIMBS>,
565 rhs: Uint<LIMBS>,
566 output: PatternXgcdOutput<LIMBS>,
567 ) where
568 Uint<LIMBS>: Concat<LIMBS, Output = Uint<DOUBLE>>,
569 {
570 assert_eq!(lhs.gcd(&rhs), output.gcd, "{lhs} {rhs}");
572
573 let (lhs_on_gcd, rhs_on_gcd) = output.quotients();
575 assert_eq!(lhs_on_gcd, lhs.div(output.gcd.as_nz_ref()));
576 assert_eq!(rhs_on_gcd, rhs.div(output.gcd.as_nz_ref()));
577
578 let (x, y) = output.bezout_coefficients();
580 assert_eq!(
581 x.concatenating_mul_unsigned(&lhs) + y.concatenating_mul_unsigned(&rhs),
582 *output.gcd.resize().as_int(),
583 );
584
585 assert!(x.abs() <= rhs.div(output.gcd.as_nz_ref()));
587 assert!(y.abs() <= lhs.div(output.gcd.as_nz_ref()));
588 if lhs != rhs {
589 assert!(x.abs() <= rhs_on_gcd.shr(1) || rhs_on_gcd.is_zero().to_bool());
590 assert!(y.abs() <= lhs_on_gcd.shr(1) || lhs_on_gcd.is_zero().to_bool());
591 }
592 }
593
594 mod test_binxgcd_nz {
595 use crate::modular::bingcd::xgcd::tests::test_xgcd;
596 use crate::{
597 Concat, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, U8192, Uint,
598 };
599
600 fn binxgcd_nz_test<const LIMBS: usize, const DOUBLE: usize>(
601 lhs: Uint<LIMBS>,
602 rhs: Uint<LIMBS>,
603 ) where
604 Uint<LIMBS>: Concat<LIMBS, Output = Uint<DOUBLE>>,
605 {
606 let output = lhs.to_odd().unwrap().binxgcd_nz(&rhs.to_nz().unwrap());
607 test_xgcd(lhs, rhs, output);
608 }
609
610 fn binxgcd_nz_tests<const LIMBS: usize, const DOUBLE: usize>()
611 where
612 Uint<LIMBS>: Concat<LIMBS, Output = Uint<DOUBLE>>,
613 {
614 let max_int = *Int::MAX.as_uint();
615 let int_abs_min = Int::MIN.abs();
616
617 binxgcd_nz_test(Uint::ONE, Uint::ONE);
618 binxgcd_nz_test(Uint::ONE, max_int);
619 binxgcd_nz_test(Uint::ONE, int_abs_min);
620 binxgcd_nz_test(Uint::ONE, Uint::MAX);
621 binxgcd_nz_test(max_int, Uint::ONE);
622 binxgcd_nz_test(max_int, max_int);
623 binxgcd_nz_test(max_int, int_abs_min);
624 binxgcd_nz_test(max_int, Uint::MAX);
625 binxgcd_nz_test(Uint::MAX, Uint::ONE);
626 binxgcd_nz_test(Uint::MAX, max_int);
627 binxgcd_nz_test(Uint::MAX, int_abs_min);
628 binxgcd_nz_test(Uint::MAX, Uint::MAX);
629 }
630
631 #[test]
632 fn test_binxgcd_nz() {
633 binxgcd_nz_tests::<{ U64::LIMBS }, { U128::LIMBS }>();
634 binxgcd_nz_tests::<{ U128::LIMBS }, { U256::LIMBS }>();
635 binxgcd_nz_tests::<{ U192::LIMBS }, { U384::LIMBS }>();
636 binxgcd_nz_tests::<{ U256::LIMBS }, { U512::LIMBS }>();
637 binxgcd_nz_tests::<{ U384::LIMBS }, { U768::LIMBS }>();
638 binxgcd_nz_tests::<{ U512::LIMBS }, { U1024::LIMBS }>();
639 binxgcd_nz_tests::<{ U1024::LIMBS }, { U2048::LIMBS }>();
640 binxgcd_nz_tests::<{ U2048::LIMBS }, { U4096::LIMBS }>();
641 binxgcd_nz_tests::<{ U4096::LIMBS }, { U8192::LIMBS }>();
642 }
643 }
644
645 mod test_classic_binxgcd {
646 use crate::modular::bingcd::xgcd::tests::test_xgcd;
647 use crate::{
648 Concat, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, U8192, Uint,
649 };
650
651 fn classic_binxgcd_test<const LIMBS: usize, const DOUBLE: usize>(
652 lhs: Uint<LIMBS>,
653 rhs: Uint<LIMBS>,
654 ) where
655 Uint<LIMBS>: Concat<LIMBS, Output = Uint<DOUBLE>>,
656 {
657 let output = lhs
658 .to_odd()
659 .unwrap()
660 .classic_binxgcd(&rhs.to_odd().unwrap())
661 .divide();
662 test_xgcd(lhs, rhs, output);
663 }
664
665 fn classic_binxgcd_tests<const LIMBS: usize, const DOUBLE: usize>()
666 where
667 Uint<LIMBS>: Concat<LIMBS, Output = Uint<DOUBLE>>,
668 {
669 let max_int = *Int::MAX.as_uint();
670
671 classic_binxgcd_test(Uint::ONE, Uint::ONE);
672 classic_binxgcd_test(Uint::ONE, max_int);
673 classic_binxgcd_test(Uint::ONE, Uint::MAX);
674 classic_binxgcd_test(max_int, Uint::ONE);
675 classic_binxgcd_test(max_int, max_int);
676 classic_binxgcd_test(max_int, Uint::MAX);
677 classic_binxgcd_test(Uint::MAX, Uint::ONE);
678 classic_binxgcd_test(Uint::MAX, max_int);
679 classic_binxgcd_test(Uint::MAX, Uint::MAX);
680 }
681
682 #[test]
683 fn test_classic_binxgcd() {
684 classic_binxgcd_tests::<{ U64::LIMBS }, { U128::LIMBS }>();
685 classic_binxgcd_tests::<{ U128::LIMBS }, { U256::LIMBS }>();
686 classic_binxgcd_tests::<{ U192::LIMBS }, { U384::LIMBS }>();
687 classic_binxgcd_tests::<{ U256::LIMBS }, { U512::LIMBS }>();
688 classic_binxgcd_tests::<{ U384::LIMBS }, { U768::LIMBS }>();
689 classic_binxgcd_tests::<{ U512::LIMBS }, { U1024::LIMBS }>();
690 classic_binxgcd_tests::<{ U1024::LIMBS }, { U2048::LIMBS }>();
691 classic_binxgcd_tests::<{ U2048::LIMBS }, { U4096::LIMBS }>();
692 classic_binxgcd_tests::<{ U4096::LIMBS }, { U8192::LIMBS }>();
693 }
694 }
695
696 mod test_optimized_binxgcd {
697 use crate::modular::bingcd::xgcd::tests::test_xgcd;
698 use crate::modular::bingcd::xgcd::{DOUBLE_SUMMARY_LIMBS, SUMMARY_BITS, SUMMARY_LIMBS};
699 use crate::{
700 Concat, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, U8192, Uint,
701 };
702
703 fn test<const LIMBS: usize, const DOUBLE: usize>(lhs: Uint<LIMBS>, rhs: Uint<LIMBS>)
704 where
705 Uint<LIMBS>: Concat<LIMBS, Output = Uint<DOUBLE>>,
706 {
707 let output = lhs
708 .to_odd()
709 .unwrap()
710 .optimized_binxgcd(&rhs.to_odd().unwrap())
711 .divide();
712 test_xgcd(lhs, rhs, output);
713 }
714
715 fn run_tests<const LIMBS: usize, const DOUBLE: usize>()
716 where
717 Uint<LIMBS>: Concat<LIMBS, Output = Uint<DOUBLE>>,
718 {
719 let upper_bound = *Int::MAX.as_uint();
720 test(Uint::ONE, Uint::ONE);
721 test(Uint::ONE, upper_bound);
722 test(Uint::ONE, Uint::MAX);
723 test(upper_bound, Uint::ONE);
724 test(upper_bound, upper_bound);
725 test(upper_bound, Uint::MAX);
726 test(Uint::MAX, Uint::ONE);
727 test(Uint::MAX, upper_bound);
728 test(Uint::MAX, Uint::MAX);
729 }
730
731 #[test]
732 fn test_optimized_binxgcd_edge_cases() {
733 assert_eq!(SUMMARY_BITS, 63);
737 assert_eq!(SUMMARY_LIMBS, U64::LIMBS);
738 assert_eq!(DOUBLE_SUMMARY_LIMBS, U128::LIMBS);
739
740 let a = U256::from_be_hex(
742 "1234567890ABCDEF80000000000000000000000000000000BEDCBA0987654321",
743 );
744 let b = U256::from_be_hex(
745 "1234567890ABCDEF800000000000000000000000000000007EDCBA0987654321",
746 );
747 assert!(a > b);
748 assert!(
749 a.compact::<SUMMARY_BITS, DOUBLE_SUMMARY_LIMBS>(U256::BITS)
750 < b.compact::<SUMMARY_BITS, DOUBLE_SUMMARY_LIMBS>(U256::BITS)
751 );
752 test(a, b);
753
754 test(b, a);
756
757 let a = U256::from_be_hex(
759 "1234567890ABCDEF80000000000000000000000000000000FEDCBA0987654321",
760 );
761 let b = U256::from_be_hex(
762 "1234567890ABCDEF800000000000000000000000000000007EDCBA0987654321",
763 );
764 assert!(a > b);
765 assert_eq!(
766 a.compact::<SUMMARY_BITS, DOUBLE_SUMMARY_LIMBS>(U256::BITS),
767 b.compact::<SUMMARY_BITS, DOUBLE_SUMMARY_LIMBS>(U256::BITS)
768 );
769 test(a, b);
770
771 test(b, a);
773 }
774
775 #[test]
776 fn optimized_binxgcd() {
777 run_tests::<{ U128::LIMBS }, { U256::LIMBS }>();
778 run_tests::<{ U192::LIMBS }, { U384::LIMBS }>();
779 run_tests::<{ U256::LIMBS }, { U512::LIMBS }>();
780 run_tests::<{ U384::LIMBS }, { U768::LIMBS }>();
781 run_tests::<{ U512::LIMBS }, { U1024::LIMBS }>();
782 run_tests::<{ U1024::LIMBS }, { U2048::LIMBS }>();
783 run_tests::<{ U2048::LIMBS }, { U4096::LIMBS }>();
784 run_tests::<{ U4096::LIMBS }, { U8192::LIMBS }>();
785 }
786 }
787}