include!("primality_kernel.rs");
include!(concat!(env!("OUT_DIR"), "/prime_tables.rs"));
#[inline]
pub fn is_prime_u32(n: u32) -> bool {
mr_is_prime_u32(n)
}
#[inline]
pub fn is_prime_u64(n: u64) -> bool {
mr_is_prime_u64(n)
}
#[inline]
pub fn prev_prime_below_pow2(k: u32) -> u64 {
debug_assert!((8..=64).contains(&k), "k out of table range [8, 64]");
PRIMES_BELOW_2K[(k - 8) as usize]
}
#[inline]
pub fn next_prime_above_pow2(k: u32) -> u64 {
debug_assert!(
(8..=63).contains(&k),
"k out of table range [8, 63]; PRIMES_ABOVE_2K[64] is a sentinel"
);
PRIMES_ABOVE_2K[(k - 8) as usize]
}
#[inline]
pub fn prev_prime_u64(n: u64) -> u64 {
if n.is_power_of_two() {
let k = n.trailing_zeros();
if (8..=64).contains(&k) {
return PRIMES_BELOW_2K[(k - 8) as usize];
}
}
mr_prev_prime_u64(n)
}
#[inline]
pub fn next_prime_u64(n: u64) -> u64 {
if n.is_power_of_two() {
let k = n.trailing_zeros();
if (8..=63).contains(&k) {
return PRIMES_ABOVE_2K[(k - 8) as usize];
}
}
mr_next_prime_u64(n)
}
#[inline]
pub fn ephemeral_prime(seed: u64) -> u64 {
let mask = (1u64 << 61) - 1;
let s = (seed | 1) & mask;
if mr_is_prime_u64(s) {
s
} else {
mr_next_prime_u64(s)
}
}
#[cfg(feature = "unstable-u128")]
pub fn is_prime_u128(n: u128, rounds: u8) -> bool {
if n < 2 {
return false;
}
const SMALL_PRIMES: [u128; 12] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37];
for &p in &SMALL_PRIMES {
if n == p {
return true;
}
if n.is_multiple_of(p) {
return false;
}
}
if n <= u64::MAX as u128 {
return mr_is_prime_u64(n as u64);
}
let nm1 = n - 1;
let s = nm1.trailing_zeros();
let d = nm1 >> s;
let mut state: u128 = n ^ 0x9E37_79B9_7F4A_7C15_F39C_C060_5CED_C835u128;
for _ in 0..rounds {
state = state
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let a = 2u128 + (state % (n - 3));
if mr_is_composite_u128(n, d, s, a) {
return false;
}
}
true
}
#[cfg(feature = "unstable-u128")]
#[inline]
fn mr_is_composite_u128(n: u128, d: u128, s: u32, a: u128) -> bool {
let mut x = powmod_u128(a, d, n);
if x == 1 || x == n - 1 {
return false;
}
for _ in 0..s.saturating_sub(1) {
x = mulmod_u128(x, x, n);
if x == n - 1 {
return false;
}
}
true
}
#[cfg(feature = "unstable-u128")]
#[inline]
fn powmod_u128(mut base: u128, mut exp: u128, m: u128) -> u128 {
if m == 1 {
return 0;
}
let mut acc: u128 = 1 % m;
base %= m;
while exp > 0 {
if exp & 1 == 1 {
acc = mulmod_u128(acc, base, m);
}
exp >>= 1;
if exp > 0 {
base = mulmod_u128(base, base, m);
}
}
acc
}
#[cfg(feature = "unstable-u128")]
#[inline]
fn mulmod_u128(mut a: u128, mut b: u128, m: u128) -> u128 {
let mut acc: u128 = 0;
a %= m;
while b > 0 {
if b & 1 == 1 {
acc = mod_add_u128(acc, a, m);
}
a = mod_add_u128(a, a, m);
b >>= 1;
}
acc
}
#[cfg(feature = "unstable-u128")]
#[inline]
fn mod_add_u128(a: u128, b: u128, m: u128) -> u128 {
let sum = a.wrapping_add(b);
if sum < a || sum >= m {
sum.wrapping_sub(m)
} else {
sum
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn small_primes_under_100() {
let known: [u64; 25] = [
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83,
89, 97,
];
for n in 0u64..100 {
assert_eq!(is_prime_u64(n), known.contains(&n), "is_prime_u64({n})");
}
}
#[test]
fn edges() {
assert!(!is_prime_u64(0));
assert!(!is_prime_u64(1));
assert!(!is_prime_u64(u64::MAX));
assert!(is_prime_u64(u64::MAX - 58), "largest u64 prime");
}
#[test]
fn table_index_round_trip() {
assert_eq!(prev_prime_below_pow2(32), 4_294_967_291);
assert_eq!(prev_prime_below_pow2(8), 251);
assert_eq!(prev_prime_below_pow2(64), u64::MAX - 58);
}
#[cfg(feature = "unstable-u128")]
#[test]
fn u128_probabilistic_smoke() {
use super::is_prime_u128;
assert!(is_prime_u128(7, 40));
assert!(!is_prime_u128(9, 40));
assert!(is_prime_u128(u64::MAX as u128 - 58, 40));
let m89: u128 = (1u128 << 89) - 1;
assert!(is_prime_u128(m89, 40), "M_89 = 2^89 - 1 is prime");
let composite: u128 = (1u128 << 65) + 1; assert!(!is_prime_u128(composite, 40));
}
#[test]
fn ephemeral_prime_is_prime_for_assorted_seeds() {
for seed in [0u64, 1, 42, 0xDEAD_BEEF, u64::MAX, 1_000_003] {
let p = ephemeral_prime(seed);
assert!(is_prime_u64(p), "ephemeral_prime({seed}) = {p} not prime");
assert!(p < (1u64 << 62), "ephemeral_prime overshot expected window");
}
}
}