arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/src/modulus/montgomery.rs
src/modulus/montgomery.rsRaw
  1use crate::modulus::barrett::BarrettPrecomp;
  2use crate::modulus::ReduceOnce;
  3
  4extern crate test;
  5
  6/// Montgomery is a generic struct storing
  7/// an element in the Montgomery domain.
  8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
  9pub struct Montgomery<O>(pub O);
 10
 11/// Implements helper methods on the struct Montgomery<O>.
 12impl<O> Montgomery<O>{
 13
 14    #[inline(always)]
 15    pub fn new(lhs: O) -> Self{
 16        Self(lhs)
 17    }
 18
 19    #[inline(always)]
 20    pub fn value(&self) -> &O{
 21        &self.0
 22    }
 23
 24    pub fn value_mut(&mut self) -> &mut O{
 25        &mut self.0
 26    }
 27}
 28
 29/// MontgomeryPrecomp is a generic struct storing 
 30/// precomputations for Montgomery arithmetic.
 31#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 32pub struct MontgomeryPrecomp<O>{
 33    q: O,
 34    barrett: BarrettPrecomp<O>,
 35    q_inv: O,
 36    one: Montgomery<O>,
 37    minus_one: Montgomery<O>,
 38}
 39
 40/// MontgomeryPrecomp is a set of methods implemented for MontgomeryPrecomp<u64>
 41/// enabling Montgomery arithmetic over u64 values.
 42impl MontgomeryPrecomp<u64>{
 43
 44    /// Returns an new instance of MontgomeryPrecomp<u64>.
 45    /// This method will fail if gcd(q, 2^64) != 1.
 46    #[inline(always)]
 47    pub fn new(q: u64) -> MontgomeryPrecomp<u64>{
 48        assert!(q & 1 != 0, "Invalid argument: gcd(q={}, radix=2^64) != 1", q);
 49        let mut q_inv: u64 = 1;
 50        let mut q_pow = q;
 51        for _i in 0..63{
 52            q_inv = q_inv.wrapping_mul(q_pow);
 53            q_pow = q_pow.wrapping_mul(q_pow);
 54        }
 55        let mut precomp = Self{ 
 56            q: q, 
 57            barrett: BarrettPrecomp::new(q), 
 58            q_inv: q_inv,
 59            one: Montgomery(0),
 60            minus_one: Montgomery(0),
 61        };
 62
 63        precomp.one = precomp.prepare(1);
 64        precomp.minus_one = Montgomery(q-precomp.one.value());
 65
 66        precomp
 67    }
 68
 69    /// Returns 2^64 mod q as a Montgomery<u64>.
 70    #[inline(always)]
 71    pub fn one(&self) -> Montgomery<u64>{
 72        self.one
 73    }
 74
 75    /// Returns (q-1) * 2^64 mod q as a Montgomery<u64>.
 76    #[inline(always)]
 77    pub fn minus_one(&self) -> Montgomery<u64>{
 78        self.minus_one
 79    }
 80
 81    /// Returns lhs * 2^64 mod q as a Montgomery<u64>.
 82    #[inline(always)]
 83    pub fn prepare(&self, lhs: u64) -> Montgomery<u64>{
 84        let mut rhs = Montgomery(0);
 85        self.prepare_assign(lhs, &mut rhs);
 86        rhs
 87    }
 88
 89    /// Assigns lhs * 2^64 mod q to rhs.
 90    #[inline(always)]
 91    pub fn prepare_assign(&self, lhs: u64, rhs: &mut Montgomery<u64>){
 92        self.prepare_lazy_assign(lhs, rhs);
 93        rhs.value_mut().reduce_once_assign(self.q);
 94    }
 95
 96    /// Returns lhs * 2^64 mod q in range [0, 2q-1] as a Montgomery<u64>.
 97    #[inline(always)]
 98    pub fn prepare_lazy(&self, lhs: u64) -> Montgomery<u64>{
 99        let mut rhs = Montgomery(0);
100        self.prepare_lazy_assign(lhs, &mut rhs);
101        rhs
102    }
103
104    /// Assigns lhs * 2^64 mod q in range [0, 2q-1] to rhs.
105    #[inline(always)]
106    pub fn prepare_lazy_assign(&self, lhs: u64, rhs: &mut Montgomery<u64>){
107        let (_, mhi) = lhs.widening_mul(*self.barrett.value_lo());
108        *rhs = Montgomery((lhs.wrapping_mul(*self.barrett.value_hi()).wrapping_add(mhi)).wrapping_mul(self.q).wrapping_neg());
109    }
110
111    /// Returns lhs * (2^64)^-1 mod q as a u64.
112    #[inline(always)]
113    pub fn unprepare(&self, lhs: Montgomery<u64>) -> u64{
114        let mut rhs = 0u64;
115        self.unprepare_assign(lhs, &mut rhs);
116        rhs
117    }
118
119    /// Assigns lhs * (2^64)^-1 mod q to rhs.
120    #[inline(always)]
121    pub fn unprepare_assign(&self, lhs: Montgomery<u64>, rhs: &mut u64){
122        self.unprepare_lazy_assign(lhs, rhs);
123        rhs.reduce_once_assign(self.q);
124        
125    }
126
127    /// Returns lhs * (2^64)^-1 mod q in range [0, 2q-1].
128    #[inline(always)]
129    pub fn unprepare_lazy(&self, lhs: Montgomery<u64>) -> u64{
130        let mut rhs = 0u64;
131        self.unprepare_lazy_assign(lhs, &mut rhs);
132        rhs
133    }
134
135    /// Assigns lhs * (2^64)^-1 mod q in range [0, 2q-1] to rhs.
136    #[inline(always)]
137    pub fn unprepare_lazy_assign(&self, lhs: Montgomery<u64>, rhs: &mut u64){
138        let (_, r) = self.q.widening_mul(lhs.value().wrapping_mul(self.q_inv));
139        *rhs = self.q - r
140    }
141
142    /// Returns lhs * rhs * (2^{64})^-1 mod q.
143    #[inline(always)]
144    pub fn mul_external(&self, lhs: Montgomery<u64>, rhs: u64) -> u64{
145        let mut r = self.mul_external_lazy(lhs, rhs);
146        r.reduce_once_assign(self.q);
147        r
148    }
149
150    /// Assigns lhs * rhs * (2^{64})^-1 mod q to rhs.
151    #[inline(always)]
152    pub fn mul_external_assign(&self, lhs: Montgomery<u64>, rhs: &mut u64){
153        self.mul_external_lazy_assign(lhs, rhs);
154        rhs.reduce_once_assign(self.q);
155    }
156
157    /// Returns lhs * rhs * (2^{64})^-1 mod q in range [0, 2q-1].
158    #[inline(always)]
159    pub fn mul_external_lazy(&self, lhs: Montgomery<u64>, rhs: u64) -> u64{
160        let mut result = rhs;
161        self.mul_external_lazy_assign(lhs, &mut result);
162        result
163    }
164
165    /// Assigns lhs * rhs * (2^{64})^-1 mod q in range [0, 2q-1] to rhs.
166    #[inline(always)]
167    pub fn mul_external_lazy_assign(&self, lhs: Montgomery<u64>, rhs: &mut u64){
168        let (mlo, mhi) = lhs.value().widening_mul(*rhs);
169        let (_, hhi) = self.q.widening_mul(mlo.wrapping_mul(self.q_inv));
170        *rhs = mhi.wrapping_add(self.q - hhi)
171    }
172
173    /// Returns lhs * rhs * (2^{64})^-1 mod q in range [0, 2q-1].
174    #[inline(always)]
175    pub fn mul_internal(&self, lhs: Montgomery<u64>, rhs: Montgomery<u64>) -> Montgomery<u64>{
176        Montgomery(self.mul_external(lhs, *rhs.value()))
177    }
178
179    /// Assigns lhs * rhs * (2^{64})^-1 mod q to rhs.
180    #[inline(always)]
181    pub fn mul_internal_assign(&self, lhs: Montgomery<u64>, rhs: &mut Montgomery<u64>){
182        self.mul_external_assign(lhs, rhs.value_mut());
183    }
184
185    /// Returns lhs * rhs * (2^{64})^-1 mod q in range [0, 2q-1].
186    #[inline(always)]
187    pub fn mul_internal_lazy(&self, lhs: Montgomery<u64>, rhs: Montgomery<u64>) -> Montgomery<u64>{
188        Montgomery(self.mul_external_lazy(lhs, *rhs.value()))
189    }
190
191    /// Assigns lhs * rhs * (2^{64})^-1 mod q in range [0, 2q-1] to rhs.
192    #[inline(always)]
193    pub fn mul_internal_lazy_assign(&self, lhs: Montgomery<u64>, rhs: &mut Montgomery<u64>){
194        self.mul_external_lazy_assign(lhs, rhs.value_mut());
195    }
196
197    #[inline(always)]
198    pub fn add_internal(&self, lhs: Montgomery<u64>, rhs: Montgomery<u64>) -> Montgomery<u64>{
199        Montgomery(self.barrett.reduce(rhs.value() + lhs.value()))
200    }
201
202    /// Assigns lhs + rhs to rhs.
203    #[inline(always)]
204    pub fn add_internal_lazy_assign(&self, lhs: Montgomery<u64>, rhs: &mut Montgomery<u64>){
205        *rhs.value_mut() += lhs.value()
206    }
207
208    /// Assigns lhs + rhs - q if (lhs + rhs) >= q to rhs.
209    #[inline(always)]
210    pub fn add_internal_reduce_once_assign(&self, lhs: Montgomery<u64>, rhs: &mut Montgomery<u64>){
211        self.add_internal_lazy_assign(lhs, rhs);
212        rhs.value_mut().reduce_once_assign(self.q);
213    }
214
215    /// Returns (x^exponent) * 2^64 mod q.
216    #[inline(always)]
217    pub fn pow(&self, x: Montgomery<u64>, exponent:u64) -> Montgomery<u64>{
218        let mut y: Montgomery<u64> = self.one();
219        let mut x_mut: Montgomery<u64> = x;
220        let mut i: u64 = exponent;
221        while i > 0{
222            if i & 1 == 1{
223                self.mul_internal_assign(x_mut, &mut y);
224            }
225            self.mul_internal_assign(x_mut, &mut x_mut);
226            i >>= 1;
227        }
228
229        y.value_mut().reduce_once_assign(self.q);
230        y
231    }
232}
233
234/// Returns x^exponent mod q.
235/// This function internally instantiate a new MontgomeryPrecomp<u64>
236/// To be used when called only a few times and if there 
237/// is no Prime instantiated with q.
238fn pow(x:u64, exponent:u64, q:u64) -> u64{
239    let montgomery: MontgomeryPrecomp<u64> = MontgomeryPrecomp::<u64>::new(q);
240    let mut y_mont: Montgomery<u64> = montgomery.one();
241    let mut x_mont: Montgomery<u64> = montgomery.prepare(x);
242    while exponent > 0{
243        if exponent & 1 == 1{
244            montgomery.mul_internal_assign(x_mont, &mut y_mont);
245        }
246
247        montgomery.mul_internal_assign(x_mont, &mut x_mont);
248    }
249    
250    montgomery.unprepare(y_mont)
251}
252
253#[cfg(test)]
254mod tests {
255    use crate::modulus::montgomery;
256    use super::*;
257    use test::Bencher;
258
259    #[test]
260    fn test_mul_external() {
261        let q: u64 = 0x1fffffffffe00001;
262    	let m_precomp = montgomery::MontgomeryPrecomp::new(q);
263        let x: u64 = 0x5f876e514845cc8b;
264        let y: u64 = 0xad726f98f24a761a;
265        let y_mont = m_precomp.prepare(y);
266    	assert!(m_precomp.mul_external(y_mont, x) == (x as u128 * y as u128 % q as u128) as u64);
267    }
268
269    #[bench]
270    fn bench_mul_external(b: &mut Bencher){
271        let q: u64 = 0x1fffffffffe00001;
272    	let m_precomp = montgomery::MontgomeryPrecomp::new(q);
273        let mut x: u64 = 0x5f876e514845cc8b;
274        let y: u64 = 0xad726f98f24a761a;
275        let y_mont = m_precomp.prepare(y);
276        b.iter(|| m_precomp.mul_external_assign(y_mont, &mut x));
277    }
278}