#[cfg(feature = "std")] use rayon::prelude::*;
use {crate::num::power::modpow, fastrand::Rng, num_bigint::BigUint, thiserror::Error};
#[non_exhaustive]
#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)]
#[error("This number is neither prime nor composite")]
pub struct PrimeStatusError;
#[non_exhaustive]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PrimeStatus {
Prime,
Composite,
ProbablyPrime,
}
impl PrimeStatus {
#[inline]
#[must_use]
pub fn is_prime(self) -> bool { self == Self::Prime }
#[inline]
#[must_use]
pub fn is_probably_prime(self) -> bool { self != Self::Composite }
#[inline]
#[must_use]
pub fn is_composite(self) -> bool { self == Self::Composite }
}
pub trait Prime {
fn is_prime(&self) -> bool;
fn is_probably_prime(&self) -> bool;
fn is_composite(&self) -> bool;
}
impl Prime for usize {
#[inline]
fn is_prime(&self) -> bool { wilson_th(*self) == Ok(PrimeStatus::Prime) }
#[inline]
fn is_probably_prime(&self) -> bool { miller_rabin(*self) != Ok(PrimeStatus::Composite) }
#[inline]
fn is_composite(&self) -> bool { wilson_th(*self) == Ok(PrimeStatus::Composite) }
}
impl Prime for Result<PrimeStatus, PrimeStatusError> {
#[inline]
fn is_prime(&self) -> bool { self.is_ok_and(|st| st == PrimeStatus::Prime) }
#[inline]
fn is_probably_prime(&self) -> bool { self.is_ok_and(|st| st == PrimeStatus::ProbablyPrime) }
#[inline]
fn is_composite(&self) -> bool { self.is_ok_and(|st| st == PrimeStatus::Composite) }
}
pub fn sqrtest(num: usize) -> Result<PrimeStatus, PrimeStatusError> {
if num < 2 {
Err(PrimeStatusError)
} else {
let sqrt_res = num.isqrt() + 1;
let cond = cfg_select! {
feature = "std" => (3..=sqrt_res).into_par_iter().find_any(|&i| num % i == 0).is_some(),
_ => (3..=sqrt_res).any(|i| num & i == 0),
};
if cond { Ok(PrimeStatus::Composite) } else { Ok(PrimeStatus::Prime) }
}
}
pub fn wilson_th(num: usize) -> Result<PrimeStatus, PrimeStatusError> {
if num < 2 {
return Err(PrimeStatusError);
}
let mut fact = BigUint::from(1u8);
for i in 2..num {
fact = (fact * i) % num;
}
if fact + 1u8 == BigUint::from(num) { Ok(PrimeStatus::Prime) } else { Ok(PrimeStatus::Composite) }
}
pub fn miller_rabin(num: usize) -> Result<PrimeStatus, PrimeStatusError> {
match num {
0 | 1 => Err(PrimeStatusError),
5 => Ok(PrimeStatus::Prime),
_ if num % 2 == 0 || num % 3 == 0 => Ok(PrimeStatus::Composite),
_ => {
let log_sqr = (num.ilog2() * num.ilog2()).into();
let (mut temp, mut su) = (num - 1, 0);
while temp % 2 == 0 {
temp /= 2;
su += 1;
}
'outer: for i in 0..log_sqr {
let rand_num = Rng::with_seed(i).usize(2..num - 1);
let mut x_num = modpow(rand_num, temp, num);
if x_num == 1 || x_num == num - 1 {
continue;
}
for _j in 0..su - 1 {
x_num = modpow(x_num, 2, num);
if x_num == 1 {
return Ok(PrimeStatus::Composite);
}
if x_num == num - 1 {
continue 'outer;
}
}
return Ok(PrimeStatus::Composite);
}
Ok(PrimeStatus::ProbablyPrime)
},
}
}