Skip to content

Commit dbc02cb

Browse files
authored
Add support for computing Uint::gcd with even modulus (#617)
Uses a similar method to `Uint::inv_mod` for extending `Uint::gcd` with support for an even modulus, first dividing out `2^k` from both operands where this operation will ensure at least one value is odd, and then applying Bernstein-Yang to the odd modulus, multiplying the result by `2^k`.
1 parent 27b1a36 commit dbc02cb

File tree

2 files changed

+37
-15
lines changed

2 files changed

+37
-15
lines changed

src/uint/gcd.rs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,34 @@
1-
//! Support for computing greatest common divisor of two `Uint`s.
1+
//! Support for computing the greatest common divisor of two `Uint`s.
22
3-
use crate::{modular::BernsteinYangInverter, ConstCtOption, Gcd, Odd, PrecomputeInverter, Uint};
3+
use crate::{
4+
modular::BernsteinYangInverter, ConstChoice, ConstCtOption, Gcd, Odd, PrecomputeInverter, Uint,
5+
};
46
use subtle::CtOption;
57

68
impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> Uint<SAT_LIMBS>
79
where
810
Odd<Self>: PrecomputeInverter<Inverter = BernsteinYangInverter<SAT_LIMBS, UNSAT_LIMBS>>,
911
{
1012
/// Compute the greatest common divisor (GCD) of this number and another.
11-
///
12-
/// Returns none in the event that `self` is even (i.e. `self` MUST be odd). However, `rhs` may be even.
1313
pub const fn gcd(&self, rhs: &Self) -> ConstCtOption<Self> {
14-
let ret = <Odd<Self> as PrecomputeInverter>::Inverter::gcd(self, rhs);
15-
ConstCtOption::new(ret, self.is_odd())
14+
let k1 = self.trailing_zeros();
15+
let k2 = rhs.trailing_zeros();
16+
17+
// Select the smaller of the two `k` values, making 2^k the common even divisor
18+
let k = ConstChoice::from_u32_lt(k2, k1).select_u32(k1, k2);
19+
20+
// Decompose `self` and `rhs` into `s{1, 2} * 2^k` where either `s1` or `s2` is odd
21+
let s1 = self.overflowing_shr(k).unwrap_or(Self::ZERO);
22+
let s2 = rhs.overflowing_shr(k).unwrap_or(Self::ZERO);
23+
24+
let f = Self::select(&s1, &s2, s2.is_odd().not());
25+
let g = Self::select(&s1, &s2, s2.is_odd());
26+
27+
let ret = <Odd<Self> as PrecomputeInverter>::Inverter::gcd(&f, &g);
28+
ConstCtOption::new(
29+
ret.overflowing_shl(k).unwrap_or(Self::ZERO),
30+
f.is_nonzero().and(g.is_odd()),
31+
)
1632
}
1733
}
1834

@@ -61,8 +77,9 @@ mod tests {
6177

6278
#[test]
6379
fn gcd_zero() {
64-
let f = U256::ZERO;
65-
assert!(f.gcd(&U256::ONE).is_none().is_true_vartime());
80+
assert!(U256::ZERO.gcd(&U256::ZERO).is_none().is_true_vartime());
81+
assert!(U256::ZERO.gcd(&U256::ONE).is_none().is_true_vartime());
82+
assert!(U256::ONE.gcd(&U256::ZERO).is_none().is_true_vartime());
6683
}
6784

6885
#[test]
@@ -71,4 +88,14 @@ mod tests {
7188
assert_eq!(U256::ONE, f.gcd(&U256::ONE).unwrap());
7289
assert_eq!(U256::ONE, f.gcd(&U256::from(2u8)).unwrap());
7390
}
91+
92+
#[test]
93+
fn gcd_two() {
94+
let f = U256::from_u8(2);
95+
assert_eq!(f, f.gcd(&f).unwrap());
96+
97+
let g = U256::from_u8(4);
98+
assert_eq!(f, f.gcd(&g).unwrap());
99+
assert_eq!(f, g.gcd(&f).unwrap());
100+
}
74101
}

tests/uint.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ mod common;
55
use common::to_biguint;
66
use crypto_bigint::{
77
modular::{MontyForm, MontyParams},
8-
Encoding, Integer, Limb, NonZero, Odd, Word, U256,
8+
Encoding, Limb, NonZero, Odd, Word, U256,
99
};
1010
use num_bigint::BigUint;
1111
use num_integer::Integer as _;
@@ -275,12 +275,7 @@ proptest! {
275275
}
276276

277277
#[test]
278-
fn gcd(mut f in uint(), g in uint()) {
279-
if f.is_even().into() {
280-
// Ensure `f` is always odd (required by Bernstein-Yang)
281-
f = f.wrapping_add(&U256::ONE);
282-
}
283-
278+
fn gcd(f in uint(), g in uint()) {
284279
let f_bi = to_biguint(&f);
285280
let g_bi = to_biguint(&g);
286281

0 commit comments

Comments
 (0)