use crate::Modulus64;
static SET3: [u64; 3] = [
4230279247111683200,
14694767155120705706,
16641139526367750375,
];
static SET5: [u64; 5] = [
2,
4130806001517,
149795463772692060,
186635894390467037,
3967304179347715805,
];
static SET7: [u64; 7] = [2, 325, 9375, 28178, 450775, 9780504, 1795265022];
pub const fn primality_test(x: u64) -> bool {
const SMALL_PRIME: u64 = {
let mut test = 0;
let mut x = 64;
while x > 0 {
x -= 1;
test = (test << 1) | if primality_test_naive(x) { 1 } else { 0 };
}
test
};
const MAY_BE_PRIME_30: u32 = {
let mut table = 0;
let mut n = 0;
while n < 30 {
table |= if n % 2 == 0 || n % 3 == 0 || n % 5 == 0 {
0 } else {
1 } << n;
n += 1;
}
table
};
if x < 64 {
return (SMALL_PRIME >> x) & 1 == 1;
} else if (MAY_BE_PRIME_30 >> x % 30) & 1 == 0 || x % 7 == 0 {
return false;
}
let (s, d) = {
let x = x - 1;
let s = x.trailing_zeros();
(s - 1, x >> s)
};
let modulus = Modulus64::new(x);
let one = modulus.residue(1).x;
let neg_one = x - one;
let witness = if x < 350_269_456_337 {
SET3.as_slice()
} else if x < 7_999_252_175_582_851 {
SET5.as_slice()
} else {
SET7.as_slice()
};
let mut i = 0;
'test: while i < witness.len() {
let mut mint = modulus.residue(witness[i]);
i += 1;
if mint.is_zero() {
continue;
}
mint = mint.pow(d);
if mint.x == one || mint.x == neg_one {
continue;
}
let mut s = s;
while s > 0 {
s -= 1;
mint.x = mint.modulus.mul(mint.x, mint.x);
if mint.x == neg_one {
continue 'test;
}
}
return false;
}
true
}
const fn primality_test_naive(x: u64) -> bool {
if x < 2 {
return false;
}
let mut d = 1;
while d < x.isqrt() {
d += 1;
if x % d == 0 {
return false;
}
}
true
}
#[cfg(test)]
mod tests {
use rand::{rng, Rng};
use super::*;
#[test]
fn small() {
for x in 0..500_000 {
assert_eq!(primality_test(x), primality_test_naive(x), "{x}")
}
}
#[test]
fn intermediate() {
let mut rng = rng();
for x in std::iter::repeat_with(|| rng.random_range(1 << 30..1 << 40)).take(100) {
assert_eq!(primality_test(x), primality_test_naive(x), "{x}")
}
}
}