|
| 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