Skip to content

Commit 8af2deb

Browse files
committed
Implement Int::gcd
1 parent e83d77c commit 8af2deb

3 files changed

Lines changed: 157 additions & 0 deletions

File tree

benches/int.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,13 +332,92 @@ fn bench_sub(c: &mut Criterion) {
332332
group.finish();
333333
}
334334

335+
fn bench_gcd(c: &mut Criterion) {
336+
let mut group = c.benchmark_group("gcd");
337+
338+
group.bench_function("gcd, I128-I128", |b| {
339+
b.iter_batched(
340+
|| {
341+
let x = I128::random(&mut OsRng);
342+
let y = I128::random(&mut OsRng);
343+
(x, y)
344+
},
345+
|(x, y)| black_box(x.gcd(&y)),
346+
BatchSize::SmallInput,
347+
)
348+
});
349+
350+
group.bench_function("gcd, I256-I256", |b| {
351+
b.iter_batched(
352+
|| {
353+
let x = I256::random(&mut OsRng);
354+
let y = I256::random(&mut OsRng);
355+
(x, y)
356+
},
357+
|(x, y)| black_box(x.gcd(&y)),
358+
BatchSize::SmallInput,
359+
)
360+
});
361+
362+
group.bench_function("gcd, I512-I512", |b| {
363+
b.iter_batched(
364+
|| {
365+
let x = I512::random(&mut OsRng);
366+
let y = I512::random(&mut OsRng);
367+
(x, y)
368+
},
369+
|(x, y)| black_box(x.gcd(&y)),
370+
BatchSize::SmallInput,
371+
)
372+
});
373+
374+
group.bench_function("gcd, I1024-I1024", |b| {
375+
b.iter_batched(
376+
|| {
377+
let x = I1024::random(&mut OsRng);
378+
let y = I1024::random(&mut OsRng);
379+
(x, y)
380+
},
381+
|(x, y)| black_box(x.gcd(&y)),
382+
BatchSize::SmallInput,
383+
)
384+
});
385+
386+
group.bench_function("gcd, I2048-I2048", |b| {
387+
b.iter_batched(
388+
|| {
389+
let x = I2048::random(&mut OsRng);
390+
let y = I2048::random(&mut OsRng);
391+
(x, y)
392+
},
393+
|(x, y)| black_box(x.gcd(&y)),
394+
BatchSize::SmallInput,
395+
)
396+
});
397+
398+
group.bench_function("gcd, I4096-I4096", |b| {
399+
b.iter_batched(
400+
|| {
401+
let x = I4096::random(&mut OsRng);
402+
let y = I4096::random(&mut OsRng);
403+
(x, y)
404+
},
405+
|(x, y)| black_box(x.gcd(&y)),
406+
BatchSize::SmallInput,
407+
)
408+
});
409+
410+
group.finish();
411+
}
412+
335413
criterion_group!(
336414
benches,
337415
bench_mul,
338416
bench_widening_mul,
339417
bench_div,
340418
bench_add,
341419
bench_sub,
420+
bench_gcd,
342421
);
343422

344423
criterion_main!(benches);

src/int.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ mod cmp;
2323
mod div;
2424
mod encoding;
2525
mod from;
26+
mod gcd;
2627
mod mul;
2728
mod neg;
2829
mod resize;

src/int/gcd.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//! Support for computing the greatest common divisor of two [`Int`]s.
2+
3+
use crate::modular::SafeGcdInverter;
4+
use crate::{Int, Odd, PrecomputeInverter, Uint};
5+
6+
impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> Int<SAT_LIMBS>
7+
where
8+
Odd<Uint<SAT_LIMBS>>: PrecomputeInverter<Inverter = SafeGcdInverter<SAT_LIMBS, UNSAT_LIMBS>>,
9+
{
10+
/// Compute the greatest common divisor (`gcd`) of `self` and `rhs`.
11+
/// Always returns a non-negative value.
12+
pub const fn gcd(&self, rhs: &Self) -> Uint<SAT_LIMBS> {
13+
self.abs().gcd(&rhs.abs())
14+
}
15+
}
16+
17+
impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> Odd<Int<SAT_LIMBS>>
18+
where
19+
Odd<Uint<SAT_LIMBS>>: PrecomputeInverter<Inverter = SafeGcdInverter<SAT_LIMBS, UNSAT_LIMBS>>,
20+
{
21+
/// Compute the greatest common divisor (GCD) of this number and another.
22+
///
23+
/// Runs in variable time with respect to `rhs`.
24+
pub fn gcd_vartime(&self, rhs: &Int<SAT_LIMBS>) -> Uint<SAT_LIMBS> {
25+
// safe to unwrap; self is odd
26+
self.abs().to_odd().unwrap().gcd_vartime(&rhs.abs())
27+
}
28+
}
29+
30+
// TODO: implement Gcd trait. Depends on Integer trait.
31+
32+
#[cfg(test)]
33+
mod tests {
34+
use crate::{Int, I1024, I128, U1024, U128};
35+
36+
#[test]
37+
fn gcd() {
38+
// Odd GCD
39+
assert_eq!(I128::from(-77).gcd(&I128::from(14)), U128::from(7u32));
40+
assert_eq!(I128::from(-77).gcd(&I128::from(-14)), U128::from(7u32));
41+
assert_eq!(I128::from(77).gcd(&I128::from(14)), U128::from(7u32));
42+
assert_eq!(I128::from(77).gcd(&I128::from(-14)), U128::from(7u32));
43+
44+
// Even GCD
45+
assert_eq!(I128::from(-144).gcd(&I128::from(28)), U128::from(4u32));
46+
assert_eq!(I128::from(-144).gcd(&I128::from(-28)), U128::from(4u32));
47+
assert_eq!(I128::from(144).gcd(&I128::from(28)), U128::from(4u32));
48+
assert_eq!(I128::from(144).gcd(&I128::from(-28)), U128::from(4u32));
49+
}
50+
51+
#[test]
52+
fn gcd_large() {
53+
// larger values
54+
let x = I1024::from_be_hex("0084A671979467BD329796EF6B55CC555C4B6DE1DA7425F7DF0175C04164A2F1D333D2DD4BCD1BE078E0FC9C1616F8532F3A4AB2CA9102B948B7217955344BF3FBD687F205789E5118FF43B372AE93F131BF6624721518CF73BE04DA1645495B12DDE8032226F1D02E1939631CC5D0B43AC47212CF819447C05F8899EFD13C80");
55+
let y = I1024::from_be_hex("6FE1503B3DD76A256360BC23041D2D47D47DA27E2474C1CB8ABCBB0617320996C9319B3DC29FC24F38E2D9B9BE9B48BFB0A7B955B03F784F3DCE963CFD03ED07C0A89583B00BE7EDDFFC229087CF08DADF838B3A65EE5F85BAE06132B1FED2DAB3FF12CCB8E0357AEAF95195E59A0D06E0626B894A20DFDC7B1252A5E1ABF000").neg().unwrap();
56+
let target = U1024::from_be_hex("0000000000000000000000000000000000000000000000000000106F3345CEC3099E40DD1CD11AA51AA63D0351C8B83B200CAC0563F10BA25470C66EDE1E1A98FFFD0EBC98641BE024071CC9798D213826A24786FF1A2D26E5C819DFBF3E907112EA34CE9E40A3AB8D59190A361A1AE6E3D9393211621B27EEFB4D5FD3DF5580");
57+
58+
let res = x.gcd(&y);
59+
assert_eq!(res, target);
60+
61+
// divide out factors
62+
let x_on_gcd = x.checked_div(&Int::new_from_uint(res)).unwrap();
63+
let y_on_gcd = y.checked_div(&Int::new_from_uint(res)).unwrap();
64+
65+
assert_eq!(x_on_gcd.gcd(&y_on_gcd), U1024::ONE);
66+
}
67+
68+
#[test]
69+
fn gcd_vartime() {
70+
let min_77 = I128::from(-77).to_odd().unwrap();
71+
assert_eq!(min_77.gcd_vartime(&I128::from(21)), U128::from(7u32));
72+
assert_eq!(min_77.gcd_vartime(&I128::from(-21)), U128::from(7u32));
73+
let pos_77 = I128::from(77).to_odd().unwrap();
74+
assert_eq!(pos_77.gcd_vartime(&I128::from(21)), U128::from(7u32));
75+
assert_eq!(pos_77.gcd_vartime(&I128::from(-21)), U128::from(7u32));
76+
}
77+
}

0 commit comments

Comments
 (0)