use crate::modulus::barrett::BarrettPrecomp; use crate::modulus::montgomery::{Montgomery, MontgomeryPrecomp}; use crate::modulus::ReduceOnce; use crate::modulus::{ONCE, REDUCEMOD}; extern crate test; /// MontgomeryPrecomp is a set of methods implemented for MontgomeryPrecomp /// enabling Montgomery arithmetic over u64 values. impl MontgomeryPrecomp { /// Returns an new instance of MontgomeryPrecomp. /// This method will fail if gcd(q, 2^64) != 1. #[inline(always)] pub fn new(q: u64) -> MontgomeryPrecomp { assert!( q & 1 != 0, "Invalid argument: gcd(q={}, radix=2^64) != 1", q ); let mut q_inv: u64 = 1; let mut q_pow = q; for _i in 0..63 { q_inv = q_inv.wrapping_mul(q_pow); q_pow = q_pow.wrapping_mul(q_pow); } let mut precomp: MontgomeryPrecomp = Self { q: q, two_q: q << 1, four_q: q << 2, barrett: BarrettPrecomp::new(q), q_inv: q_inv, one: 0, minus_one: 0, }; precomp.one = precomp.prepare::(1); precomp.minus_one = q - precomp.one; precomp } /// Returns 2^64 mod q as a Montgomery. #[inline(always)] pub fn one(&self) -> Montgomery { self.one } /// Returns (q-1) * 2^64 mod q as a Montgomery. #[inline(always)] pub fn minus_one(&self) -> Montgomery { self.minus_one } /// Applies a modular reduction on x based on REDUCE: /// - LAZY: no modular reduction. /// - ONCE: subtracts q if x >= q. /// - FULL: maps x to x mod q using Barrett reduction. #[inline(always)] pub fn reduce(&self, x: u64) -> u64 { let mut r: u64 = x; self.reduce_assign::(&mut r); r } /// Applies a modular reduction on x based on REDUCE: /// - LAZY: no modular reduction. /// - ONCE: subtracts q if x >= q. /// - FULL: maps x to x mod q using Barrett reduction. #[inline(always)] pub fn reduce_assign(&self, x: &mut u64) { self.barrett.reduce_assign::(x); } /// Returns lhs * 2^64 mod q as a Montgomery. #[inline(always)] pub fn prepare(&self, lhs: u64) -> Montgomery { let mut rhs: u64 = 0; self.prepare_assign::(lhs, &mut rhs); rhs } /// Assigns lhs * 2^64 mod q to rhs. #[inline(always)] pub fn prepare_assign(&self, lhs: u64, rhs: &mut Montgomery) { let (_, mhi) = lhs.widening_mul(*self.barrett.value_lo()); *rhs = (lhs.wrapping_mul(*self.barrett.value_hi()).wrapping_add(mhi)) .wrapping_mul(self.q) .wrapping_neg(); self.reduce_assign::(rhs); } /// Returns lhs * (2^64)^-1 mod q as a u64. #[inline(always)] pub fn unprepare(&self, lhs: Montgomery) -> u64 { let mut rhs = 0u64; self.unprepare_assign::(lhs, &mut rhs); rhs } /// Assigns lhs * (2^64)^-1 mod q to rhs. #[inline(always)] pub fn unprepare_assign(&self, lhs: Montgomery, rhs: &mut u64) { let (_, r) = self.q.widening_mul(lhs.wrapping_mul(self.q_inv)); *rhs = self.reduce::(self.q.wrapping_sub(r)); } /// Returns lhs * rhs * (2^{64})^-1 mod q. #[inline(always)] pub fn mul_external(&self, lhs: Montgomery, rhs: u64) -> u64 { let mut r: u64 = rhs; self.mul_external_assign::(lhs, &mut r); r } /// Assigns lhs * rhs * (2^{64})^-1 mod q to rhs. #[inline(always)] pub fn mul_external_assign( &self, lhs: Montgomery, rhs: &mut u64, ) { let (mlo, mhi) = lhs.widening_mul(*rhs); let (_, hhi) = self.q.widening_mul(mlo.wrapping_mul(self.q_inv)); *rhs = self.reduce::(mhi.wrapping_sub(hhi).wrapping_add(self.q)); } /// Returns lhs * rhs * (2^{64})^-1 mod q in range [0, 2q-1]. #[inline(always)] pub fn mul_internal( &self, lhs: Montgomery, rhs: Montgomery, ) -> Montgomery { self.mul_external::(lhs, rhs) } /// Assigns lhs * rhs * (2^{64})^-1 mod q to rhs. #[inline(always)] pub fn mul_internal_assign( &self, lhs: Montgomery, rhs: &mut Montgomery, ) { self.mul_external_assign::(lhs, rhs); } #[inline(always)] pub fn add_internal(&self, lhs: Montgomery, rhs: Montgomery) -> Montgomery { rhs + lhs } /// Assigns lhs + rhs to rhs. #[inline(always)] pub fn add_internal_lazy_assign(&self, lhs: Montgomery, rhs: &mut Montgomery) { *rhs += lhs } /// Assigns lhs + rhs - q if (lhs + rhs) >= q to rhs. #[inline(always)] pub fn add_internal_reduce_once_assign( &self, lhs: Montgomery, rhs: &mut Montgomery, ) { self.add_internal_lazy_assign(lhs, rhs); rhs.reduce_once_assign(self.q); } /// Returns (x^exponent) * 2^64 mod q. #[inline(always)] pub fn pow(&self, x: Montgomery, exponent: u64) -> Montgomery { let mut y: Montgomery = self.one(); let mut x_mut: Montgomery = x; let mut i: u64 = exponent; while i > 0 { if i & 1 == 1 { self.mul_internal_assign::(x_mut, &mut y); } self.mul_internal_assign::(x_mut, &mut x_mut); i >>= 1; } y.reduce_once_assign(self.q); y } } #[cfg(test)] mod tests { use super::*; use crate::modulus::montgomery; use test::Bencher; #[test] fn test_mul_external() { let q: u64 = 0x1fffffffffe00001; let m_precomp = montgomery::MontgomeryPrecomp::new(q); let x: u64 = 0x5f876e514845cc8b; let y: u64 = 0xad726f98f24a761a; let y_mont = m_precomp.prepare::(y); assert!( m_precomp.mul_external::(y_mont, x) == (x as u128 * y as u128 % q as u128) as u64 ); } #[bench] fn bench_mul_external(b: &mut Bencher) { let q: u64 = 0x1fffffffffe00001; let m_precomp = montgomery::MontgomeryPrecomp::new(q); let mut x: u64 = 0x5f876e514845cc8b; let y: u64 = 0xad726f98f24a761a; let y_mont = m_precomp.prepare::(y); b.iter(|| m_precomp.mul_external_assign::(y_mont, &mut x)); } }