number_theory/arithmetic/
mpz_prime.rs

1/*
2
3  Primality
4
5*/
6
7use crate::data::primes::PRIMELIST;
8
9use crate::ntrait::NumberTheory;
10use crate::Mpz;
11use crate::NTResult;
12
13use crate::arithmetic::sliceops::{mod_slice,sub_slice};
14
15
16impl Mpz {
17    /// Strong Fermat test to a selected base
18    pub(crate) fn sprp(&self, base: Self) -> bool {
19        let mut p_minus = self.clone();
20        let one = Mpz::one();
21
22        sub_slice(&mut p_minus.limbs[..], &one.limbs[..]); //subtract one this will always succeed
23
24        let zeroes = p_minus.trailing_zeros() as usize;
25
26        let d = p_minus.shr(zeroes);
27        let mut x = base.u_mod_pow(&d, self);
28
29        if x == Mpz::one() || x == p_minus {
30            return true;
31        }
32
33        for _ in 1..zeroes{
34            x = x.u_quadratic_residue(self);
35
36            if x == p_minus {
37                return true;
38            }
39        }
40        false
41    }
42
43    #[cfg(not(feature = "parallel"))]
44    /** Performs n random base checks, can be combined with is_prime to strengthen it. As is_prime is equivalent to one random sprp check in the worst case, the function below has an error rate of less than 2^-64 in the worst case.
45
46     use number_theory::NumberTheory;
47     use number_theory::Mpz;
48
49     fn strong_check(x: &Mpz) -> bool{
50       if !x.is_prime(){
51         return false
52       }
53       x.sprp_check(31)
54     }
55
56    */
57    pub fn sprp_check(&self, n: usize) -> bool {
58        if self.len() < 2 {
59            return u64::try_from(self.clone()).unwrap().is_prime();
60        } // if fits into u64, reduce to 64-bit check
61
62        if self.is_fermat() {
63            return false;
64        }
65
66        for _ in 0..n {
67            let z = Mpz::rand((self.bit_length()-1) as usize);
68
69            if !self.sprp(z) {
70                return false;
71            } //
72        }
73
74        true
75    }
76
77    #[cfg(feature = "parallel")] // Fix this,likely does not check the correct amount of tests
78    /// Checks n bases 
79    pub fn sprp_check(&self, k: usize) -> bool {
80        if self.len() < 2 {
81            return self.to_u64().unwrap().is_prime();
82        } // if fits into u64, reduce to 64-bit check
83
84        if self.is_fermat() {
85            return false;
86        }
87
88       
89        let single = |x: Mpz, n: usize| {
90            for _ in 0..n {
91                let z = Mpz::rand(x.bit_length() as usize-1);
92                if !x.sprp(z) {
93                    return false;
94                }
95            }
96            return true;
97        };
98
99        let threadcount = usize::from(std::thread::available_parallelism().unwrap()) - 2;
100        let q = self.clone();
101        /*  if k < threadcount{
102           let mut threadvec = vec![];
103           let mut xclone = vec![];
104           for i in 0..k{
105             xclone.push(self.clone())
106           }
107           for i in xclone{
108             threadvec.push(std::thread::spawn(move|| {single(i, 1).clone()}));
109           }
110           for j in threadvec{
111             if !j.join().unwrap(){
112               return false
113             }
114           }
115           return true
116         }
117        // else{*/
118        let mut threadvec = vec![];
119        let mut xclone = vec![];
120        for _ in 0..threadcount {
121            xclone.push(self.clone())
122        }
123
124        for i in xclone {
125            threadvec.push(std::thread::spawn(move || single(i, k / threadcount)));
126        }
127        let tally = single(q, k / threadcount); //threadvec.push(std::thread::spawn(move || {single(q.clone(), k/threadcount)}));
128
129        for j in threadvec {
130            if !j.join().unwrap() {
131                return false;
132            }
133        }
134        return tally;
135    }
136
137    /** Detects if self is a number of various forms and returns the prime evaluation if it is
138     One might be tempted to ask why proth numbers are not evaluated, and it's simply the fact that Proth's theorem is actually an extremely
139     inefficient  primality test, in the worst case strong fermat tests are twice as efficient as a Proth test and far stronger in practice.
140     */
141    pub(crate) fn form_check(&self) -> bool {
142        // Detects if self is of the form r(k^2 + k) + k + 1
143
144        let sqrt = self.sqrt().0;
145        if sqrt.sqr() == self.clone() {
146            // detect perfect square
147            return false;
148        }
149
150        !detect_pseudo_mpz(self)
151    }
152
153    pub(crate) fn trial_div(&self) -> bool {
154        //weighted trial division
155
156        if self.is_even() {
157            return false;
158        }
159
160        let rem = mod_slice(&self.limbs[..], 16294579238595022365);
161
162        for i in PRIMELIST[1..16usize].iter() {
163            if rem % *i as u64 == 0 {
164                return false;
165            }
166        }
167
168        let mut supremum: usize = 2048;
169
170        if self.len() < 40 {
171            supremum = self.len() * 50
172        }
173        for i in PRIMELIST[17..supremum].iter() {
174            if self.congruence_u64(*i as u64, 0) {
175                return false;
176            }
177        }
178
179        // insert a sieve that optimizes to eliminate composites (this does not appear to be any more efficient than using the hardcoded primes )
180        true
181    }
182    
183   pub(crate) fn _trial_list(&self, primes: &[u64]) -> bool{
184      for i in primes{
185        if self.congruence_u64(*i,0){
186         return false
187        }
188      }
189      true
190    }
191
192    // weighted strong fermat bases starting from 2^128
193    pub(crate) fn weighted_sprp(&self) -> bool {
194        const CHECK_LUT: [u8; 5] = [8, 6, 5, 4, 2]; // starting at 2^128    [12, 10, 7, 6, 4, 3, 2, 1];
195        let mut check: usize = 1;
196
197        if self.len() < 8 {
198            check = CHECK_LUT[self.len() - 3] as usize;
199        }
200
201        self.sprp_check(check)
202    }
203
204    pub(crate) fn llt(&self, p: u64) -> bool {
205        // function will never be called in practical implementation as number-theory does not support the scale of computation needed to use it
206        let mut s = Mpz::from(4u64);
207
208        for _ in 0..(p - 2) {
209            s = s.ref_product(&s);
210            s.normalize();
211            sub_slice(&mut s.limbs[..], &[2]);
212
213            s = s.ref_euclidean(self).1;
214        }
215        s.normalize();
216        if s == Mpz::zero() {
217            return true;
218        }
219        false
220    }
221
222    /** Faster than naive evaluation of sophie prime, returns safe prime if true, otherwise None. As this uses is_prime, 
223    the accuracy matches that function exactly as the verification of the safe prime itself is deterministic and solely reliant on the accuracy
224     of the verification of the sophie prime.
225     */
226    pub fn is_sophie(&self) -> Option<Self> {
227        if self.is_prime() {
228            let mut safe = self.shl(1);
229            let p = safe.clone();
230            let two = Mpz::two();
231            safe.successor();
232
233            if two.exp_residue(p, safe.clone()) == Mpz::one() {
234                return Some(safe);
235            }
236        }
237        None
238    }
239
240    pub(crate) fn jacobi_check_mpz(&self) -> bool {
241        // Performs a check of the Jacobi
242        let mut witness = 3u64;
243        loop {
244            if fast_jacobi_mpz(self, witness) == -1 {
245                break;
246            }
247            witness += 1;
248        }
249
250        let witty = Mpz::from(witness);
251        self.sprp(witty)
252    }
253
254/// Returns an integer in the interval 2^(k-1);2^k  that can satisfy the Monier-Rabin bound of passing the Artjuhov-Selfridge test 
255/// with (1/4) probability
256/// 
257pub fn psp(k: usize) -> NTResult<Self>{
258
259 let corrector = 1 + (k&1)*2 ;
260 if k < 14{
261   return NTResult::DNE
262 }
263 if k > (1<<32){
264   return NTResult::CompExceeded
265 }
266 loop {
267  let len = (k-corrector)/2;
268  let mut x = Mpz::rand(len);
269  if corrector == 3 && !x.check_bit(len-2){
270    x.flip_bit(len-2);
271  }
272  if corrector == 1{
273    if x.check_bit(len-2){
274      x.flip_bit(len-2);
275    }
276   if x.check_bit(len-3){
277      x.flip_bit(len-3);
278   }
279  }  
280  
281  let lhs = x.shl(1).ref_addition(&Mpz::one());
282  let rhs = x.shl(2).ref_addition(&Mpz::one());
283  if lhs.is_prime() & rhs.is_prime(){
284   let product = lhs.ref_product(&rhs);
285    assert_eq!(product.bit_length(),k as u64);
286    
287    return NTResult::Eval(product)
288  }
289  }
290}
291/// A weak fermat test
292pub fn fermat(&self, base: &Self) -> bool{
293  base.exp_residue(self.ref_subtraction(&Mpz::one()),self.clone()).is_unit()
294}
295
296/// Deterministic primality test, reliant on GRH
297pub fn miller(&self) -> bool{
298   let sup = (self.ln()*self.ln()*2.0) as u64;
299   
300   match self.to_u128(){
301     Some(x) => {
302       for i in 2..sup{
303         if !x.strong_fermat(i as u128){
304           return false
305         }
306       }
307     }
308     None => {
309       for i in 2..sup{
310         if !self.sprp(Mpz::from(i)){
311           return false
312         }
313       }
314     }
315   }
316   true
317}
318    
319  
320}
321
322pub(crate) fn fast_jacobi_mpz(x: &Mpz, p: u64) -> i8 {
323    let k = x.word_div(p).1;
324    k.jacobi(p).unwrap()
325}
326
327  // detects if the number is in a common pseudoprime form 
328pub(crate) fn detect_pseudo_mpz(x: &Mpz) -> bool {
329    let mut xminus = x.abs();
330    let copy = xminus.clone();
331    let eightprod = copy.scale_add(8,1);
332    let eightsqrt = eightprod.sqrt().0;
333  
334    if eightsqrt.sqr() == eightprod{
335      return true
336    }
337  
338     let threeprod = copy.scale_add(3,1);
339    let threesqrt = threeprod.sqrt().0;
340    if threesqrt.sqr() == threeprod{
341      return true
342    }
343    xminus.predecessor();
344    
345    
346    for i in 1..16 {
347        let sq = xminus.word_div(2 * i + 1).0.sqrt().0; // (k*k + k)*(2*i) + k + 1 == x
348        let lhs = sq.ref_product(&sq).ref_addition(&sq);
349        let lhs2 = lhs.ref_product(&Mpz::from(2 * i + 1));
350        if lhs.ref_addition(&lhs2).ref_addition(&Mpz::one()) == copy {
351            return true;
352        }
353    }
354    false
355}