Skip to content

Commit 25ec62a

Browse files
authored
Add BoxedUint::gcd (#497)
Support for computing the greatest common divisor of two `BoxedUint`s using Bernstein-Yang
1 parent 336dda1 commit 25ec62a

File tree

6 files changed

+77
-2
lines changed

6 files changed

+77
-2
lines changed

src/modular.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ mod monty_form;
2121
mod reduction;
2222

2323
mod add;
24-
mod bernstein_yang;
24+
pub(crate) mod bernstein_yang;
2525
mod div_by_2;
2626
mod mul;
2727
mod pow;

src/modular/bernstein_yang/boxed.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,24 @@ impl Inverter for BoxedBernsteinYangInverter {
7474
}
7575
}
7676

77+
/// Returns the greatest common divisor (GCD) of the two given numbers.
78+
pub(crate) fn gcd(f: &BoxedUint, g: &BoxedUint) -> BoxedUint {
79+
let bits_precision = f.bits_precision();
80+
let inverse = inv_mod2_62(f.as_words());
81+
let f = BoxedInt62L::from(f);
82+
let mut g = BoxedInt62L::from(g);
83+
let mut d = BoxedInt62L::zero(f.0.len());
84+
let e = BoxedInt62L::one(f.0.len());
85+
86+
let mut f = divsteps(&mut d, &e, &f, &mut g, inverse);
87+
88+
if f.is_negative() {
89+
f = f.neg();
90+
}
91+
92+
f.to_uint(bits_precision)
93+
}
94+
7795
/// Algorithm `divsteps2` to compute (δₙ, fₙ, gₙ) = divstepⁿ(δ, f, g) as described in Figure 10.1
7896
/// of <https://eprint.iacr.org/2019/266.pdf>.
7997
fn divsteps(
@@ -322,6 +340,13 @@ impl BoxedInt62L {
322340
Self(vec![0; nlimbs].into())
323341
}
324342

343+
/// Get the value zero for the given number of limbs.
344+
pub fn one(nlimbs: usize) -> Self {
345+
let mut ret = Self::zero(nlimbs);
346+
ret.0[0] = 0;
347+
ret
348+
}
349+
325350
/// Widen self to the given number of limbs.
326351
pub fn widen(self, nlimbs: usize) -> Self {
327352
let mut limbs = Vec::from(self.0);

src/uint/boxed.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ mod div;
1313
mod div_limb;
1414
pub(crate) mod encoding;
1515
mod from;
16+
mod gcd;
1617
mod inv_mod;
1718
mod mul;
1819
mod mul_mod;

src/uint/boxed/gcd.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//! Support for computing greatest common divisor of two `BoxedUint`s.
2+
3+
use super::BoxedUint;
4+
use crate::{modular::bernstein_yang, Odd};
5+
6+
impl Odd<BoxedUint> {
7+
/// Compute the greatest common divisor (GCD) of this number and another.
8+
pub fn gcd(&self, rhs: &BoxedUint) -> BoxedUint {
9+
bernstein_yang::boxed::gcd(self, rhs)
10+
}
11+
}
12+
13+
#[cfg(test)]
14+
mod tests {
15+
use crate::BoxedUint;
16+
17+
#[test]
18+
fn gcd_relatively_prime() {
19+
// Two semiprimes with no common factors
20+
let f = BoxedUint::from(59u32 * 67).to_odd().unwrap();
21+
let g = BoxedUint::from(61u32 * 71);
22+
let gcd = f.gcd(&g);
23+
assert_eq!(gcd, BoxedUint::one());
24+
}
25+
26+
#[test]
27+
fn gcd_nonprime() {
28+
let f = BoxedUint::from(4391633u32).to_odd().unwrap();
29+
let g = BoxedUint::from(2022161u32);
30+
let gcd = f.gcd(&g);
31+
assert_eq!(gcd, BoxedUint::from(1763u32));
32+
}
33+
}

tests/boxed_uint_proptests.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use core::cmp::Ordering;
66
use crypto_bigint::{BoxedUint, CheckedAdd, Integer, Limb, NonZero};
77
use num_bigint::{BigUint, ModInverse};
8+
use num_integer::Integer as _;
89
use num_traits::identities::One;
910
use proptest::prelude::*;
1011

@@ -141,6 +142,22 @@ proptest! {
141142
prop_assert_eq!(expected_remainder, to_biguint(&actual_remainder));
142143
}
143144

145+
#[test]
146+
fn gcd((mut f, g) in uint_pair()) {
147+
if f.is_even().into() {
148+
// Ensure `f` is always odd (required by Bernstein-Yang)
149+
f = f.wrapping_add(&BoxedUint::one());
150+
}
151+
152+
let f = f.to_odd().unwrap();
153+
let f_bi = to_biguint(&f);
154+
let g_bi = to_biguint(&g);
155+
156+
let expected = to_uint(f_bi.gcd(&g_bi));
157+
let actual = f.gcd(&g);
158+
assert_eq!(expected, actual);
159+
}
160+
144161
#[test]
145162
fn mod_inv((mut a, mut b) in uint_pair()) {
146163
if a.is_zero().into() {

tests/uint_proptests.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,6 @@ proptest! {
287287

288288
let expected = to_uint(f_bi.gcd(&g_bi));
289289
let actual = f.gcd(&g).unwrap();
290-
291290
assert_eq!(expected, actual);
292291
}
293292

0 commit comments

Comments
 (0)