|
1 | 1 | //! Support for computing greatest common divisor of two `BoxedUint`s. |
2 | 2 |
|
3 | 3 | use super::BoxedUint; |
4 | | -use crate::{modular::bernstein_yang, Gcd, Integer, Odd}; |
5 | | -use subtle::CtOption; |
| 4 | +use crate::{modular::bernstein_yang, ConstantTimeSelect, Gcd, Integer, Odd}; |
| 5 | +use subtle::{ConditionallySelectable, ConstantTimeLess}; |
6 | 6 |
|
7 | 7 | impl Gcd for BoxedUint { |
8 | | - type Output = CtOption<Self>; |
| 8 | + type Output = Self; |
9 | 9 |
|
10 | | - fn gcd(&self, rhs: &Self) -> CtOption<Self> { |
11 | | - let ret = bernstein_yang::boxed::gcd(self, rhs); |
12 | | - CtOption::new(ret, self.is_odd()) |
| 10 | + /// Compute the greatest common divisor (GCD) of this number and another. |
| 11 | + fn gcd(&self, rhs: &Self) -> Self { |
| 12 | + let k1 = self.trailing_zeros(); |
| 13 | + let k2 = rhs.trailing_zeros(); |
| 14 | + |
| 15 | + // Select the smaller of the two `k` values, making 2^k the common even divisor |
| 16 | + let k = u32::conditional_select(&k1, &k2, u32::ct_lt(&k2, &k1)); |
| 17 | + |
| 18 | + // Decompose `self` and `rhs` into `s{1, 2} * 2^k` where either `s1` or `s2` is odd |
| 19 | + let s1 = self.overflowing_shr(k).0; |
| 20 | + let s2 = rhs.overflowing_shr(k).0; |
| 21 | + |
| 22 | + let f = Self::ct_select(&s1, &s2, !s2.is_odd()); |
| 23 | + let g = Self::ct_select(&s1, &s2, s2.is_odd()); |
| 24 | + bernstein_yang::boxed::gcd(&f, &g).overflowing_shl(k).0 |
13 | 25 | } |
14 | 26 | } |
15 | 27 |
|
@@ -41,4 +53,31 @@ mod tests { |
41 | 53 | let gcd = f.gcd(&g); |
42 | 54 | assert_eq!(gcd, BoxedUint::from(1763u32)); |
43 | 55 | } |
| 56 | + |
| 57 | + #[test] |
| 58 | + fn gcd_zero() { |
| 59 | + let zero = BoxedUint::from(0u32); |
| 60 | + let one = BoxedUint::from(1u32); |
| 61 | + |
| 62 | + assert_eq!(zero.gcd(&zero), zero); |
| 63 | + assert_eq!(zero.gcd(&one), one); |
| 64 | + assert_eq!(one.gcd(&zero), one); |
| 65 | + } |
| 66 | + |
| 67 | + #[test] |
| 68 | + fn gcd_one() { |
| 69 | + let f = BoxedUint::from(1u32); |
| 70 | + assert_eq!(BoxedUint::from(1u32), f.gcd(&BoxedUint::from(1u32))); |
| 71 | + assert_eq!(BoxedUint::from(1u32), f.gcd(&BoxedUint::from(2u8))); |
| 72 | + } |
| 73 | + |
| 74 | + #[test] |
| 75 | + fn gcd_two() { |
| 76 | + let f = BoxedUint::from(2u32); |
| 77 | + assert_eq!(f, f.gcd(&f)); |
| 78 | + |
| 79 | + let g = BoxedUint::from(4u32); |
| 80 | + assert_eq!(f, f.gcd(&g)); |
| 81 | + assert_eq!(f, g.gcd(&f)); |
| 82 | + } |
44 | 83 | } |
0 commit comments