From 3874a2b55073cbf417dbdae51abdb0ab8b9352ed Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Tue, 22 Oct 2024 17:14:30 +0200 Subject: [PATCH 1/2] Implement `Int::gcd` --- benches/int.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/int.rs | 1 + src/int/gcd.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 src/int/gcd.rs diff --git a/benches/int.rs b/benches/int.rs index 466eb6b7..bfff8588 100644 --- a/benches/int.rs +++ b/benches/int.rs @@ -332,6 +332,84 @@ fn bench_sub(c: &mut Criterion) { group.finish(); } +fn bench_gcd(c: &mut Criterion) { + let mut group = c.benchmark_group("gcd"); + + group.bench_function("gcd, I128-I128", |b| { + b.iter_batched( + || { + let x = I128::random(&mut OsRng); + let y = I128::random(&mut OsRng); + (x, y) + }, + |(x, y)| black_box(x.gcd(&y)), + BatchSize::SmallInput, + ) + }); + + group.bench_function("gcd, I256-I256", |b| { + b.iter_batched( + || { + let x = I256::random(&mut OsRng); + let y = I256::random(&mut OsRng); + (x, y) + }, + |(x, y)| black_box(x.gcd(&y)), + BatchSize::SmallInput, + ) + }); + + group.bench_function("gcd, I512-I512", |b| { + b.iter_batched( + || { + let x = I512::random(&mut OsRng); + let y = I512::random(&mut OsRng); + (x, y) + }, + |(x, y)| black_box(x.gcd(&y)), + BatchSize::SmallInput, + ) + }); + + group.bench_function("gcd, I1024-I1024", |b| { + b.iter_batched( + || { + let x = I1024::random(&mut OsRng); + let y = I1024::random(&mut OsRng); + (x, y) + }, + |(x, y)| black_box(x.gcd(&y)), + BatchSize::SmallInput, + ) + }); + + group.bench_function("gcd, I2048-I2048", |b| { + b.iter_batched( + || { + let x = I2048::random(&mut OsRng); + let y = I2048::random(&mut OsRng); + (x, y) + }, + |(x, y)| black_box(x.gcd(&y)), + BatchSize::SmallInput, + ) + }); + + group.bench_function("gcd, I4096-I4096", |b| { + b.iter_batched( + || { + let x = I4096::random(&mut OsRng); + let y = I4096::random(&mut OsRng); + (x, y) + }, + |(x, y)| black_box(x.gcd(&y)), + BatchSize::SmallInput, + ) + }); + + group.finish(); +} + criterion_group!( benches, bench_mul, @@ -339,6 +417,7 @@ criterion_group!( bench_div, bench_add, bench_sub, + bench_gcd, ); criterion_main!(benches); diff --git a/src/int.rs b/src/int.rs index 66a8d3e8..71fac389 100644 --- a/src/int.rs +++ b/src/int.rs @@ -20,6 +20,7 @@ mod cmp; mod div; mod encoding; mod from; +mod gcd; mod mul; mod neg; mod resize; diff --git a/src/int/gcd.rs b/src/int/gcd.rs new file mode 100644 index 00000000..50201ec0 --- /dev/null +++ b/src/int/gcd.rs @@ -0,0 +1,77 @@ +//! Support for computing the greatest common divisor of two [`Int`]s. + +use crate::modular::SafeGcdInverter; +use crate::{Int, Odd, PrecomputeInverter, Uint}; + +impl Int +where + Odd>: PrecomputeInverter>, +{ + /// Compute the greatest common divisor (`gcd`) of `self` and `rhs`. + /// Always returns a non-negative value. + pub const fn gcd(&self, rhs: &Self) -> Uint { + self.abs().gcd(&rhs.abs()) + } +} + +impl Odd> +where + Odd>: PrecomputeInverter>, +{ + /// Compute the greatest common divisor (GCD) of this number and another. + /// + /// Runs in variable time with respect to `rhs`. + pub fn gcd_vartime(&self, rhs: &Int) -> Uint { + // safe to unwrap; self is odd + self.abs().to_odd().unwrap().gcd_vartime(&rhs.abs()) + } +} + +// TODO: implement Gcd trait. Depends on Integer trait. + +#[cfg(test)] +mod tests { + use crate::{Int, I1024, I128, U1024, U128}; + + #[test] + fn gcd() { + // Odd GCD + assert_eq!(I128::from(-77).gcd(&I128::from(14)), U128::from(7u32)); + assert_eq!(I128::from(-77).gcd(&I128::from(-14)), U128::from(7u32)); + assert_eq!(I128::from(77).gcd(&I128::from(14)), U128::from(7u32)); + assert_eq!(I128::from(77).gcd(&I128::from(-14)), U128::from(7u32)); + + // Even GCD + assert_eq!(I128::from(-144).gcd(&I128::from(28)), U128::from(4u32)); + assert_eq!(I128::from(-144).gcd(&I128::from(-28)), U128::from(4u32)); + assert_eq!(I128::from(144).gcd(&I128::from(28)), U128::from(4u32)); + assert_eq!(I128::from(144).gcd(&I128::from(-28)), U128::from(4u32)); + } + + #[test] + fn gcd_large() { + // larger values + let x = I1024::from_be_hex("0084A671979467BD329796EF6B55CC555C4B6DE1DA7425F7DF0175C04164A2F1D333D2DD4BCD1BE078E0FC9C1616F8532F3A4AB2CA9102B948B7217955344BF3FBD687F205789E5118FF43B372AE93F131BF6624721518CF73BE04DA1645495B12DDE8032226F1D02E1939631CC5D0B43AC47212CF819447C05F8899EFD13C80"); + let y = I1024::from_be_hex("6FE1503B3DD76A256360BC23041D2D47D47DA27E2474C1CB8ABCBB0617320996C9319B3DC29FC24F38E2D9B9BE9B48BFB0A7B955B03F784F3DCE963CFD03ED07C0A89583B00BE7EDDFFC229087CF08DADF838B3A65EE5F85BAE06132B1FED2DAB3FF12CCB8E0357AEAF95195E59A0D06E0626B894A20DFDC7B1252A5E1ABF000").neg().unwrap(); + let target = U1024::from_be_hex("0000000000000000000000000000000000000000000000000000106F3345CEC3099E40DD1CD11AA51AA63D0351C8B83B200CAC0563F10BA25470C66EDE1E1A98FFFD0EBC98641BE024071CC9798D213826A24786FF1A2D26E5C819DFBF3E907112EA34CE9E40A3AB8D59190A361A1AE6E3D9393211621B27EEFB4D5FD3DF5580"); + + let res = x.gcd(&y); + assert_eq!(res, target); + + // divide out factors + let x_on_gcd = x.checked_div(&Int::new_from_uint(res)).unwrap(); + let y_on_gcd = y.checked_div(&Int::new_from_uint(res)).unwrap(); + + assert_eq!(x_on_gcd.gcd(&y_on_gcd), U1024::ONE); + } + + #[test] + fn gcd_vartime() { + let min_77 = I128::from(-77).to_odd().unwrap(); + assert_eq!(min_77.gcd_vartime(&I128::from(21)), U128::from(7u32)); + assert_eq!(min_77.gcd_vartime(&I128::from(-21)), U128::from(7u32)); + let pos_77 = I128::from(77).to_odd().unwrap(); + assert_eq!(pos_77.gcd_vartime(&I128::from(21)), U128::from(7u32)); + assert_eq!(pos_77.gcd_vartime(&I128::from(-21)), U128::from(7u32)); + } +} From f51c7e1721803baf2051708fdd726920466bb489 Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Fri, 15 Nov 2024 12:19:28 +0100 Subject: [PATCH 2/2] Clean up `Int::gcd` tests --- src/int/gcd.rs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/int/gcd.rs b/src/int/gcd.rs index 50201ec0..78b223c3 100644 --- a/src/int/gcd.rs +++ b/src/int/gcd.rs @@ -1,7 +1,7 @@ //! Support for computing the greatest common divisor of two [`Int`]s. -use crate::modular::SafeGcdInverter; use crate::{Int, Odd, PrecomputeInverter, Uint}; +use crate::modular::SafeGcdInverter; impl Int where @@ -31,7 +31,7 @@ where #[cfg(test)] mod tests { - use crate::{Int, I1024, I128, U1024, U128}; + use crate::{I1024, I128, Int, U1024, U128}; #[test] fn gcd() { @@ -51,9 +51,24 @@ mod tests { #[test] fn gcd_large() { // larger values - let x = I1024::from_be_hex("0084A671979467BD329796EF6B55CC555C4B6DE1DA7425F7DF0175C04164A2F1D333D2DD4BCD1BE078E0FC9C1616F8532F3A4AB2CA9102B948B7217955344BF3FBD687F205789E5118FF43B372AE93F131BF6624721518CF73BE04DA1645495B12DDE8032226F1D02E1939631CC5D0B43AC47212CF819447C05F8899EFD13C80"); - let y = I1024::from_be_hex("6FE1503B3DD76A256360BC23041D2D47D47DA27E2474C1CB8ABCBB0617320996C9319B3DC29FC24F38E2D9B9BE9B48BFB0A7B955B03F784F3DCE963CFD03ED07C0A89583B00BE7EDDFFC229087CF08DADF838B3A65EE5F85BAE06132B1FED2DAB3FF12CCB8E0357AEAF95195E59A0D06E0626B894A20DFDC7B1252A5E1ABF000").neg().unwrap(); - let target = U1024::from_be_hex("0000000000000000000000000000000000000000000000000000106F3345CEC3099E40DD1CD11AA51AA63D0351C8B83B200CAC0563F10BA25470C66EDE1E1A98FFFD0EBC98641BE024071CC9798D213826A24786FF1A2D26E5C819DFBF3E907112EA34CE9E40A3AB8D59190A361A1AE6E3D9393211621B27EEFB4D5FD3DF5580"); + let x = I1024::from_be_hex(concat![ + "0084A671979467BD329796EF6B55CC555C4B6DE1DA7425F7DF0175C04164A2F1", + "D333D2DD4BCD1BE078E0FC9C1616F8532F3A4AB2CA9102B948B7217955344BF3", + "FBD687F205789E5118FF43B372AE93F131BF6624721518CF73BE04DA1645495B", + "12DDE8032226F1D02E1939631CC5D0B43AC47212CF819447C05F8899EFD13C80" + ]); + let y = I1024::from_be_hex(concat![ + "6FE1503B3DD76A256360BC23041D2D47D47DA27E2474C1CB8ABCBB0617320996", + "C9319B3DC29FC24F38E2D9B9BE9B48BFB0A7B955B03F784F3DCE963CFD03ED07", + "C0A89583B00BE7EDDFFC229087CF08DADF838B3A65EE5F85BAE06132B1FED2DA", + "B3FF12CCB8E0357AEAF95195E59A0D06E0626B894A20DFDC7B1252A5E1ABF000" + ]).checked_neg().unwrap(); + let target = U1024::from_be_hex(concat![ + "0000000000000000000000000000000000000000000000000000106F3345CEC3", + "099E40DD1CD11AA51AA63D0351C8B83B200CAC0563F10BA25470C66EDE1E1A98", + "FFFD0EBC98641BE024071CC9798D213826A24786FF1A2D26E5C819DFBF3E9071", + "12EA34CE9E40A3AB8D59190A361A1AE6E3D9393211621B27EEFB4D5FD3DF5580" + ]); let res = x.gcd(&y); assert_eq!(res, target);