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