arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/src/modulus/impl_u64/prime.rs
src/modulus/impl_u64/prime.rsRaw
  1use crate::modulus::prime::Prime;
  2use crate::modulus::montgomery::{Montgomery, MontgomeryPrecomp};
  3use crate::modulus::barrett::BarrettPrecomp;
  4use crate::modulus::ONCE;
  5use primality_test::is_prime;
  6use prime_factorization::Factorization;
  7
  8impl Prime<u64>{
  9
 10    /// Returns a new instance of Prime<u64>.
 11    /// Panics if q_base is not a prime > 2 and
 12    /// if q_base^q_power would overflow u64.
 13    pub fn new(q_base: u64, q_power: usize) -> Self{
 14		assert!(is_prime(q_base) && q_base > 2);
 15        Self::new_unchecked(q_base, q_power)
 16	}
 17
 18    /// Returns a new instance of Prime<u64>.
 19    /// Does not check if q_base is a prime > 2.
 20    /// Panics if q_base^q_power would overflow u64.
 21	pub fn new_unchecked(q_base: u64, q_power: usize) -> Self {
 22
 23        let mut q = q_base;
 24        for _i in 1..q_power{
 25            q *= q_base
 26        }
 27
 28        assert!(q.next_power_of_two().ilog2() <= 61);
 29
 30        let mut phi = q_base-1;
 31        for _i in 1..q_power{
 32            phi *= q_base
 33        }
 34
 35        let mut prime: Prime<u64> = Self {
 36            q:q,
 37            two_q:q<<1,
 38            four_q:q<<2,
 39            q_base:q_base,
 40            q_power:q_power,
 41            factors: Vec::new(),
 42            montgomery:MontgomeryPrecomp::new(q),
 43            barrett:BarrettPrecomp::new(q),
 44            phi:phi,
 45        };
 46
 47        prime.check_factors();
 48
 49        prime
 50
 51    }
 52
 53    pub fn q(&self) -> u64{
 54        self.q
 55    }
 56
 57    pub fn q_base(&self) -> u64{
 58        self.q_base
 59    }
 60
 61    pub fn q_power(&self) -> usize{
 62        self.q_power
 63    }
 64
 65    /// Returns x^exponen mod q.
 66    #[inline(always)]
 67    pub fn pow(&self, x: u64, exponent: u64) -> u64{
 68        let mut y_mont: Montgomery<u64> = self.montgomery.one();
 69        let mut x_mont: Montgomery<u64> = self.montgomery.prepare::<ONCE>(x);
 70        let mut i: u64 = exponent;
 71        while i > 0{
 72            if i & 1 == 1{
 73                self.montgomery.mul_internal_assign::<ONCE>(x_mont, &mut y_mont);
 74            }
 75
 76            self.montgomery.mul_internal_assign::<ONCE>(x_mont, &mut x_mont);
 77
 78            i >>= 1;
 79
 80        }
 81        
 82        self.montgomery.unprepare::<ONCE>(y_mont)
 83    }
 84
 85    /// Returns x^-1 mod q.
 86    /// User must ensure that x is not divisible by q_base.
 87    #[inline(always)]
 88    pub fn inv(&self, x: u64) -> u64{
 89        self.pow(x, self.phi-1)
 90    }
 91}
 92
 93impl Prime<u64>{
 94    /// Returns the smallest nth primitive root of q_base.
 95    pub fn primitive_root(&self) -> u64{
 96
 97        let mut candidate: u64 = 1u64;
 98        let mut not_found: bool = true;
 99
100        while not_found{
101
102            candidate += 1;
103
104            for &factor in &self.factors{
105
106                if  pow(candidate, (self.q_base-1)/factor, self.q_base) == 1{
107                    not_found = true;
108                    break
109                }
110                not_found = false;
111            }
112        }
113
114        if not_found{
115            panic!("failed to find a primitive root for q_base={}", self.q_base)
116        }
117
118        candidate
119    }
120
121    /// Returns an nth primitive root of q = q_base^q_power in Montgomery.
122    pub fn primitive_nth_root(&self, nth_root:u64) -> u64{
123
124        assert!(self.q & (nth_root-1) == 1, "invalid prime: q = {} % nth_root = {} = {} != 1", self.q, nth_root, self.q & (nth_root-1));
125
126        let psi: u64 = self.primitive_root();
127
128        // nth primitive root mod q_base: psi_nth^(prime.q_base-1)/nth_root mod q_base
129        let psi_nth_q_base: u64 = pow(psi, (self.q_base-1)/nth_root, self.q_base);
130
131        // lifts nth primitive root mod q_base to q = q_base^q_power
132        let psi_nth_q: u64 = self.hensel_lift(psi_nth_q_base, nth_root);
133
134        assert!(self.pow(psi_nth_q, nth_root) == 1, "invalid nth primitive root: psi^nth_root != 1 mod q");
135        assert!(self.pow(psi_nth_q, nth_root>>1) == self.q-1, "invalid nth primitive root: psi^(nth_root/2) != -1 mod q");
136
137        psi_nth_q
138    }
139
140    /// Checks if the field self.factor is populated.
141    /// If not, factorize q_base-1 and populates self.factor.
142    /// If yes, checks that it contains the unique factors of q_base-1.
143    pub fn check_factors(&mut self){
144
145        if self.factors.len() == 0{
146
147            let factors = Factorization::run(self.q_base-1).prime_factor_repr();
148            let mut distincts_factors: Vec<u64> = Vec::with_capacity(factors.len());
149            for factor in factors.iter(){
150                distincts_factors.push(factor.0)
151            }
152            self.factors = distincts_factors
153            
154        }else{
155            let mut q_base: u64 = self.q_base;
156
157            for &factor in &self.factors{
158                if !is_prime(factor){
159                    panic!("invalid factor list: factor {} is not prime", factor)
160                }
161    
162                while q_base%factor != 0{
163                    q_base /= factor
164                }
165            }
166    
167            if q_base != 1{
168                panic!("invalid factor list: does not fully divide q_base: q_base % (all factors) = {}", q_base)
169            }
170        }        
171    }
172
173    /// Returns (psi + a * q_base)^{nth_root} = 1 mod q = q_base^q_power given psi^{nth_root} = 1 mod q_base.
174    /// Panics if psi^{nth_root} != 1 mod q_base.
175    fn hensel_lift(&self, psi: u64, nth_root: u64) -> u64{
176        assert!(pow(psi, nth_root, self.q_base)==1, "invalid argument psi: psi^nth_root = {} != 1", pow(psi, nth_root, self.q_base));
177
178        let mut psi_mont: Montgomery<u64> = self.montgomery.prepare::<ONCE>(psi);
179        let nth_root_mont: Montgomery<u64> = self.montgomery.prepare::<ONCE>(nth_root);
180
181        for _i in 1..self.q_power{
182
183            let psi_pow: Montgomery<u64> = self.montgomery.pow(psi_mont, nth_root-1);
184
185            let num: Montgomery<u64> = self.montgomery.one() + self.q - self.montgomery.mul_internal::<ONCE>(psi_pow, psi_mont);
186
187            let mut den: Montgomery<u64> = self.montgomery.mul_internal::<ONCE>(nth_root_mont, psi_pow);
188
189            den = self.montgomery.pow(den, self.phi-1);
190
191            psi_mont = self.montgomery.add_internal(psi_mont, self.montgomery.mul_internal::<ONCE>(num, den));
192        }
193        
194        self.montgomery.unprepare::<ONCE>(psi_mont)
195    }
196}
197
198/// Returns x^exponent mod q.
199/// This function internally instantiate a new MontgomeryPrecomp<u64>
200/// To be used when called only a few times and if there 
201/// is no Prime instantiated with q.
202pub fn pow(x:u64, exponent:u64, q:u64) -> u64{
203    let montgomery: MontgomeryPrecomp<u64> = MontgomeryPrecomp::<u64>::new(q);
204    let mut y_mont: Montgomery<u64> = montgomery.one();
205    let mut x_mont: Montgomery<u64> = montgomery.prepare::<ONCE>(x);
206    let mut i: u64 = exponent;
207    while i > 0{
208        if i & 1 == 1{
209            montgomery.mul_internal_assign::<ONCE>(x_mont, &mut y_mont);
210        }
211
212        montgomery.mul_internal_assign::<ONCE>(x_mont, &mut x_mont);
213
214        i >>= 1;
215    }
216    
217    montgomery.unprepare::<ONCE>(y_mont)
218}