machine_prime/
check.rs

1#[cfg(any(feature = "table", feature = "ssmr"))]
2use crate::hashbase::FERMAT_TABLE;
3
4#[cfg(any(feature = "lucas", feature = "table", feature = "ssmr"))]
5use crate::primes::{INV_8, PRIME_INV_64};
6
7#[cfg(all(feature = "lucas", not(any(feature = "table", feature = "ssmr"))))]
8use crate::primes::LUCAS_PARAM;
9
10#[cfg(feature = "wide")]
11use crate::double::{is_prime_128, is_prime_wc_128};
12
13/// Multiplicative inverse over Z/2^64
14///
15///  In:  n \in 2Z + 1
16///
17/// Out: n^-1
18pub const fn mul_inv2(n: u64) -> u64 {
19    #[cfg(not(any(feature = "lucas", feature = "table", feature = "ssmr")))]
20    {
21        let mut est: u64 = 3u64.wrapping_mul(n) ^ 2;
22        est = 2u64.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est);
23        est = 2u64.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est);
24        est = 2u64.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est);
25        est = 2u64.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est);
26        est
27    }
28
29    #[cfg(any(feature = "lucas", feature = "table", feature = "ssmr"))]
30    {
31        let mut est: u64 = INV_8[(n.wrapping_shr(1) & 0x7F) as usize] as u64;
32        est = 2u64.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est);
33        est = 2u64.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est);
34        est = 2u64.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est);
35        est
36    }
37}
38
39/// Product in Montgomery form
40///
41/// In: Mont(X,N),Mont(Y,N), N, N^-1
42///
43/// Out: Mont(X*Y,N)
44pub const fn mont_prod(x: u64, y: u64, n: u64, inv: u64) -> u64 {
45    let full_prod: u128 = (x as u128).wrapping_mul(y as u128);
46    let lo: u64 = (full_prod as u64).wrapping_mul(inv);
47    let lo: u64 = (lo as u128).wrapping_mul(n as u128).wrapping_shr(64) as u64;
48    let hi: u64 = full_prod.wrapping_shr(64) as u64;
49
50    if hi < lo {
51        hi.wrapping_sub(lo).wrapping_add(n)
52    } else {
53        hi.wrapping_sub(lo)
54    }
55}
56
57/// Convert to Montgomery form
58///
59/// In: X, N
60///
61/// Out: Mont(X,N)
62#[inline]
63pub const fn to_mont(x: u64, n: u64) -> u64 {
64   ((x as u128).wrapping_shl(64) % (n as u128)) as u64
65}
66
67/// One in Montgomery form
68///
69/// In: N
70///
71/// Out: Mont(1,N)
72#[inline]
73pub const fn one_mont(n: u64) -> u64 {
74    (u64::MAX % n).wrapping_add(1)
75}
76
77/// Two in Montgomery form
78///
79/// In: Mont(1,N), N
80///
81/// Out: Mont(2,N)
82pub const fn two_mont(one: u64, n: u64) -> u64 {
83    let two = one.wrapping_add(one);
84    if two > n {
85        return two.wrapping_sub(n);
86    }
87    two
88}
89
90/// Subtraction in Montgomery form   
91///   
92/// In: X,Y,N
93///  
94/// Out: X-Y mod N
95pub const fn mont_sub(x: u64, y: u64, n: u64) -> u64 {
96    if y > x {
97        return n.wrapping_sub(y.wrapping_sub(x));
98    }
99    x.wrapping_sub(y)
100}
101
102/// Check if non-quadratic residue
103///
104///  In: A,K
105///
106/// Out: Jacobi(A,K) == -1
107#[cfg(not(any(feature = "table", feature = "ssmr")))]
108pub const fn nqr(a: u64, k: u64) -> bool {
109    let mut n = a;
110    let mut p = k;
111    let mut t = false;
112
113    while n != 0 {
114        let zeros = n.trailing_zeros();
115        n = n.wrapping_shr(zeros);
116
117        if (p & 7 == 3 || p & 7 == 5) && (zeros & 1 == 1) {
118            t ^= true;
119        }
120
121        if p & 3 == 3 && n & 3 == 3 {
122            t ^= true;
123        }
124
125        let interim = p;
126        p = n;
127        n = interim;
128
129        n %= p;
130    }
131
132    if p == 1 {
133        t
134    } else {
135        false
136    }
137}
138
139/// Lucas parameter search
140///
141///  In: N
142///
143/// Out: x := jacobi(x*x-4,N) == -1
144#[cfg(not(any(feature = "table", feature = "ssmr")))]
145pub const fn param_search(n: u64) -> u64 {
146    #[cfg(all(feature = "lucas", not(any(feature = "table", feature = "ssmr"))))]
147    {
148        let rem = n % 5;
149
150        if rem == 3 || rem == 2 {
151            return 3;
152        }
153
154        let rem = n % 12;
155
156        if rem == 2 || rem == 5 || rem == 7 || rem == 8 {
157            return 4;
158        }
159
160        let rem = n % 21;
161
162        if rem == 2 || rem == 8 || rem == 11 || rem == 10 || rem == 13 || rem == 19 {
163            return 5;
164        }
165
166        let mut idx: usize = 0;
167
168        while idx < 27 {
169            let i = LUCAS_PARAM[idx] as u64;
170            if nqr(i.wrapping_mul(i).wrapping_sub(4u64), n) {
171                return i;
172            }
173            idx = idx.wrapping_add(1);
174        }
175        0u64
176    }
177
178    #[cfg(not(any(feature = "lucas", feature = "table", feature = "ssmr")))]
179    {
180        let mut d: u64;
181        let mut p = 3u64;
182        loop {
183            d = p.wrapping_mul(p).wrapping_sub(4);
184            if nqr(d, n) {
185                break;
186            }
187            p = p.wrapping_add(1);
188        }
189        p
190    }
191}
192
193///  Lucas-V sequence test with Selfridge parameters
194/// 
195/// In: N,Mont(1,N), Mont(2,N), N^-1
196///
197/// Out: Lucas_V(n)
198#[cfg(not(any(feature = "table", feature = "ssmr")))]
199pub const fn lucas(n: u64, one: u64, two: u64, npi: u64) -> bool {
200    let param = param_search(n);
201
202    #[cfg(all(feature = "lucas", not(any(feature = "table", feature = "ssmr"))))]
203    {
204        if param == 0 {
205            return true;
206        }
207    }
208
209    let n_plus = n.wrapping_add(1);
210    let s = n_plus.trailing_zeros();
211    let d = n_plus.wrapping_shr(s);
212
213    let m_param = to_mont(param, n);
214
215    let m_2_inv = mont_prod(mont_sub(n, two, n), one, n, npi);
216
217    let mut w = mont_sub(mont_prod(m_param, m_param, n, npi), two, n);
218    let mut v = m_param;
219
220    let b = 64u32.wrapping_sub(d.leading_zeros());
221
222    let mut i = 2;
223
224    while i < b.wrapping_add(1) {
225        let t = mont_sub(mont_prod(v, w, n, npi), m_param, n);
226
227        if d.wrapping_shr(b.wrapping_sub(i)) & 1 == 1 {
228            v = t;
229            w = mont_sub(mont_prod(w, w, n, npi), two, n);
230        } else {
231            w = t;
232            v = mont_sub(mont_prod(v, v, n, npi), two, n);
233        }
234        i = i.wrapping_add(1);
235    }
236
237    if v == two || v == m_2_inv {
238        return true;
239    }
240
241    let mut counter = 1;
242
243    while counter < s {
244        if v == 0 {
245            return true;
246        }
247        v = mont_sub(mont_prod(v, v, n, npi), two, n);
248        if v == two {
249            return false;
250        }
251        counter = counter.wrapping_add(1);
252    }
253    false
254}
255
256/// Modular exponentiation in Montgomery form
257///
258///  In: Mont(base),Mont(1),pow,n, inv
259///
260/// Out: base^pow mod n
261pub const fn mont_pow(mut base: u64, mut one: u64, mut pow: u64, n: u64, npi: u64) -> u64 {
262    while pow > 1 {
263        if pow & 1 == 0 {
264            base = mont_prod(base, base, n, npi);
265            pow = pow.wrapping_shr(1);
266        } else {
267            one = mont_prod(one, base, n, npi);
268            base = mont_prod(base, base, n, npi);
269            pow = pow.wrapping_sub(1).wrapping_shr(1);
270        }
271    }
272    mont_prod(one, base, n, npi)
273}
274
275/// Fermat base selection for n < 2^64
276#[cfg(any(feature = "table", feature = "ssmr"))]
277#[inline]
278pub const fn base_selector(x: u64) -> u64 {
279    FERMAT_TABLE[(x as u32).wrapping_mul(811484239).wrapping_shr(14) as usize] as u64
280}
281
282/// Strong Fermat test
283///
284/// In: N,tz := a*2^tz+1 =N, Mont(base,N), Mont(1,N), Mont(N-1,N),
285///
286/// Out: SPRP(N,base)
287pub const fn strong_fermat(n: u64, tz: u32, base: u64, one: u64, oneinv: u64, inv: u64) -> bool {
288    let d = n.wrapping_sub(1).wrapping_shr(tz);
289
290    let mut result = mont_pow(base, one, d, n, inv);
291
292    if result == one || result == oneinv {
293        return true;
294    }
295
296    let mut count = 1;
297
298    while count < tz {
299        count = count.wrapping_add(1);
300        result = mont_prod(result, result, n, inv);
301
302        if result == oneinv {
303            return true;
304        }
305    }
306    false
307}
308
309const fn core_primality(x: u64) -> bool {
310    let inv = mul_inv2(x);
311
312    let tzc = x.wrapping_sub(1).trailing_zeros();
313    let one = one_mont(x);
314    let oneinv = mont_prod(mont_sub(x, one, x), one, x, inv);
315
316    #[cfg(feature = "ssmr")]
317    {
318        let base = base_selector(x);
319        if !strong_fermat(x, tzc, to_mont(base, x), one, oneinv, inv) {
320            return false;
321        }
322
323        if x > 0x800000000000{
324            let two = two_mont(one, x);
325            return strong_fermat(x, tzc, two, one, oneinv, inv);
326        }
327        return true;
328    }
329
330    #[cfg(all(feature = "table", not(feature = "ssmr")))]
331    {
332        let two = two_mont(one, x);
333        if !strong_fermat(x, tzc, two, one, oneinv, inv) {
334            return false;
335        }
336
337        let base = base_selector(x);
338        strong_fermat(x, tzc,to_mont(base, x), one, oneinv, inv)
339    }
340
341    #[cfg(not(any(feature = "table", feature = "ssmr")))]
342    {
343        let two = two_mont(one, x);
344        if !strong_fermat(x, tzc, two, one, oneinv, inv) {
345            return false;
346        }
347
348        if x < 2047 {
349            return true;
350        }
351
352        lucas(x, one, two, inv)
353    }
354}
355
356/// Primality testing optimized for the average case in the interval 0;2^64.
357///
358/// Approximately 5 times faster than is_prime_wc in the average case, but slightly slower in the worst case.
359#[no_mangle]
360pub const extern "C" fn is_prime(x: u64) -> bool {
361    if x == 1 {
362        return false;
363    }
364
365    #[cfg(not(any(feature = "table", feature = "ssmr")))]
366    {
367        // Two perfect squares that infinitely loop in Lucas test due to Jacobi symbol search
368        // There is only 1 in 2^63 probability of being encountered at random
369        if x == 1194649 || x == 12327121 {
370            return false;
371        }
372    }
373
374    if x == 2 {
375        return true;
376    }
377
378    if x & 1 == 0 {
379        return false;
380    }
381
382    #[cfg(any(feature = "lucas", feature = "table", feature = "ssmr"))]
383    {
384        if x < 55730344633563600 {
385            let mut idx: usize = 0;
386
387            while idx < 66 {
388                let prod = x.wrapping_mul(PRIME_INV_64[idx]);
389                if prod == 1 {
390                    return true;
391                }
392                if prod < x {
393                    return false;
394                }
395                idx = idx.wrapping_add(1);
396            }
397        }
398
399        if x > 55730344633563600 {
400            let residue = x % 13082761331670030u64;
401
402            let mut idx: usize = 0;
403
404            while idx < 13 {
405                if residue.wrapping_mul(PRIME_INV_64[idx]) < residue {
406                    return false;
407                }
408                idx = idx.wrapping_add(1);
409            }
410
411            let residue = x % 10575651537777253u64;
412
413            while idx < 21 {
414                if residue.wrapping_mul(PRIME_INV_64[idx]) < residue {
415                    return false;
416                }
417                idx = idx.wrapping_add(1)
418            }
419
420            let residue = x % 9823972789433423u64;
421
422            while idx < 29 {
423                if residue.wrapping_mul(PRIME_INV_64[idx]) < residue {
424                    return false;
425                }
426                idx = idx.wrapping_add(1);
427            }
428
429            let residue = x % 805474958639317u64;
430
431            while idx < 35 {
432                if residue.wrapping_mul(PRIME_INV_64[idx]) < residue {
433                    return false;
434                }
435                idx = idx.wrapping_add(1);
436            }
437
438            let residue = x % 4575249731290429u64;
439
440            while idx < 42 {
441                if residue.wrapping_mul(PRIME_INV_64[idx]) < residue {
442                    return false;
443                }
444                idx = idx.wrapping_add(1);
445            }
446
447            let residue = x % 18506541671175721u64;
448
449            while idx < 49 {
450                if residue.wrapping_mul(PRIME_INV_64[idx]) < residue {
451                    return false;
452                }
453                idx = idx.wrapping_add(1);
454            }
455
456            let residue = x % 61247129307885343u64;
457
458            while idx < 56 {
459                if residue.wrapping_mul(PRIME_INV_64[idx]) < residue {
460                    return false;
461                }
462                idx = idx.wrapping_add(1);
463            }
464
465            let residue = x % 536967265590991u64;
466
467            while idx < 62 {
468                if residue.wrapping_mul(PRIME_INV_64[idx]) < residue {
469                    return false;
470                }
471                idx = idx.wrapping_add(1);
472            }
473
474            let residue = x % 3442087319857u64;
475
476            while idx < 66 {
477                if residue.wrapping_mul(PRIME_INV_64[idx]) < residue {
478                    return false;
479                }
480                idx = idx.wrapping_add(1);
481            }
482        } //end if
483    } // end conditional block
484
485    core_primality(x)
486}
487
488/// Primality testing for the worst case.
489///
490/// Panics at zero, flags 1 as prime, 2 as composite.
491/// # SSMR
492/// May pass some even numbers as prime
493/// # Lucas
494/// "Erroneously" returns true for the perfect squares 1194649 (1093^2) and 12327121 (3511^2). This is due to slightly faster parameter selection
495/// # Tiny
496/// Infinitely loops at the perfect squares 1194649 and 12327121.
497/// # Wide
498/// No additional known errors
499#[no_mangle]
500pub const extern "C" fn is_prime_wc(x: u64) -> bool {
501    /*
502    Alerts for the failure points
503    compiled library from Makefile does not have this check
504    */
505    debug_assert!(x != 1 && x != 2 && x != 0);
506    #[cfg(feature = "ssmr")]
507    {
508        debug_assert!(x&1==1);
509    }
510    #[cfg(not(any(feature = "table", feature = "ssmr")))]
511    {
512        debug_assert!(x != 1194649 && x != 12327121);
513    }
514
515    core_primality(x)
516}