use crate::num::arithmetic::mod_pow::mul_mod_helper;
use crate::num::arithmetic::traits::{
Gcd, JacobiSymbol, ModAdd, ModInverse, ModMulPrecomputed, ModMulPrecomputedAssign, ModSub,
Parity, PowerOf2, WrappingAddAssign, WrappingNegAssign, XMulYToZZ, XXAddYYToZZ,
};
use crate::num::basic::integers::{PrimitiveInt, USIZE_IS_U32};
use crate::num::comparison::traits::PartialOrdAbs;
use crate::num::conversion::traits::WrappingFrom;
use crate::num::factorization::traits::IsPrime;
use crate::num::logic::traits::{BitAccess, LeadingZeros, SignificantBits, TrailingZeros};
const ODD_PRIME_LOOKUP_U64: [u64; 32] = [
0x816d129a64b4cb6e,
0x2196820d864a4c32,
0xa48961205a0434c9,
0x4a2882d129861144,
0x834992132424030,
0x148a48844225064b,
0xb40b4086c304205,
0x65048928125108a0,
0x80124496804c3098,
0xc02104c941124221,
0x804490000982d32,
0x220825b082689681,
0x9004265940a28948,
0x6900924430434006,
0x12410da408088210,
0x86122d22400c060,
0x110d301821b0484,
0x14916022c044a002,
0x92094d204a6400c,
0x4ca2100800522094,
0xa48b081051018200,
0x34c108144309a25,
0x2084490880522502,
0x241140a218003250,
0xa41a00101840128,
0x2926000836004512,
0x10100480c0618283,
0xc20c26584822006d,
0x4520582024894810,
0x10c0250219002488,
0x802832ca01140868,
0x60901300264b0400,
];
const ODD_PRIME_LOOKUP_U32: [u32; 64] = [
0x64b4cb6e, 0x816d129a, 0x864a4c32, 0x2196820d, 0x5a0434c9, 0xa4896120, 0x29861144, 0x4a2882d1,
0x32424030, 0x8349921, 0x4225064b, 0x148a4884, 0x6c304205, 0xb40b408, 0x125108a0, 0x65048928,
0x804c3098, 0x80124496, 0x41124221, 0xc02104c9, 0x982d32, 0x8044900, 0x82689681, 0x220825b0,
0x40a28948, 0x90042659, 0x30434006, 0x69009244, 0x8088210, 0x12410da4, 0x2400c060, 0x86122d2,
0x821b0484, 0x110d301, 0xc044a002, 0x14916022, 0x4a6400c, 0x92094d2, 0x522094, 0x4ca21008,
0x51018200, 0xa48b0810, 0x44309a25, 0x34c1081, 0x80522502, 0x20844908, 0x18003250, 0x241140a2,
0x1840128, 0xa41a001, 0x36004512, 0x29260008, 0xc0618283, 0x10100480, 0x4822006d, 0xc20c2658,
0x24894810, 0x45205820, 0x19002488, 0x10c02502, 0x1140868, 0x802832ca, 0x264b0400, 0x60901300,
];
const FLINT_D_BITS: u64 = 53;
#[inline]
fn is_odd_prime_small_u64(n: u64) -> bool {
ODD_PRIME_LOOKUP_U64[(n >> 7) as usize].get_bit((n >> 1) & u64::WIDTH_MASK)
}
#[inline]
fn is_odd_prime_small_u32(n: u32) -> bool {
ODD_PRIME_LOOKUP_U32[(n >> 6) as usize].get_bit(u64::from(n >> 1) & u32::WIDTH_MASK)
}
fn mod_preinverted_u32(a: u32, mut n: u32, inverse: u32) -> u32 {
assert_ne!(n, 0);
let norm = LeadingZeros::leading_zeros(n);
n <<= norm;
let u1 = a >> (u32::WIDTH - norm);
let u0 = a << norm;
let (mut q1, mut q0) = u32::x_mul_y_to_zz(inverse, u1);
(q1, q0) = u32::xx_add_yy_to_zz(q1, q0, u1, u0);
let mut r = u0 - (q1 + 1) * n;
if r > q0 {
r += n;
}
if r < n { r >> norm } else { (r - n) >> norm }
}
fn mod_preinverted_u64(a: u64, mut n: u64, inverse: u64) -> u64 {
assert_ne!(n, 0);
let norm = LeadingZeros::leading_zeros(n);
n <<= norm;
let u1 = a >> (u64::WIDTH - norm);
let u0 = a << norm;
let (mut q1, mut q0) = u64::x_mul_y_to_zz(inverse, u1);
(q1, q0) = u64::xx_add_yy_to_zz(q1, q0, u1, u0);
let mut r = u0 - (q1 + 1) * n;
if r > q0 {
r += n;
}
if r < n { r >> norm } else { (r - n) >> norm }
}
fn mod_pow_preinverted_u32(mut a: u32, mut exp: u32, mut n: u32, inverse: u32) -> u32 {
assert_ne!(n, 0);
if exp == 0 {
return u32::from(n != 1);
}
if a == 0 {
return 0;
}
if a >= n {
a = mod_preinverted_u32(a, n, inverse);
}
let norm = LeadingZeros::leading_zeros(n);
a <<= norm;
n <<= norm;
while exp.even() {
a = mul_mod_helper::<u32, u64>(a, a, n, inverse, norm);
exp >>= 1;
}
let mut x = a;
loop {
exp >>= 1;
if exp == 0 {
break;
}
a = mul_mod_helper::<u32, u64>(a, a, n, inverse, norm);
if exp.odd() {
x = mul_mod_helper::<u32, u64>(x, a, n, inverse, norm);
}
}
x >> norm
}
fn mod_pow_preinverted_u64(mut a: u64, mut exp: u64, mut n: u64, inverse: u64) -> u64 {
assert_ne!(n, 0);
if exp == 0 {
return u64::from(n != 1);
}
if a == 0 {
return 0;
}
if a >= n {
a = mod_preinverted_u64(a, n, inverse);
}
let norm = LeadingZeros::leading_zeros(n);
a <<= norm;
n <<= norm;
while exp.even() {
a = mul_mod_helper::<u64, u128>(a, a, n, inverse, norm);
exp >>= 1;
}
let mut x = a;
loop {
exp >>= 1;
if exp == 0 {
break;
}
a = mul_mod_helper::<u64, u128>(a, a, n, inverse, norm);
if exp.odd() {
x = mul_mod_helper::<u64, u128>(x, a, n, inverse, norm);
}
}
x >> norm
}
fn mod_mul_preinverted_float(a: u64, b: u64, n: u64, inverse: f64) -> u64 {
let q = ((a as f64) * (b as f64) * inverse) as u64;
let mut r = (a.wrapping_mul(b)).wrapping_sub(q.wrapping_mul(n));
if r.get_highest_bit() {
r.wrapping_add_assign(n);
if r.get_highest_bit() {
return r.wrapping_add(n);
}
} else if r >= n {
return r - n;
}
r
}
pub(crate) fn mod_pow_preinverted_float(a: u64, mut exp: u64, n: u64, inverse: f64) -> u64 {
if n == 1 {
return 0;
}
let mut x = 1;
let mut y = a;
while exp != 0 {
if exp.odd() {
x = mod_mul_preinverted_float(x, y, n, inverse);
}
exp >>= 1;
if exp != 0 {
y = mod_mul_preinverted_float(y, y, n, inverse);
}
}
x
}
fn is_probable_prime_fermat(n: u64, i: u64) -> bool {
(if n.significant_bits() <= FLINT_D_BITS {
mod_pow_preinverted_float(i, n - 1, n, 1.0 / (n as f64))
} else {
mod_pow_preinverted_u64(i, n - 1, n, u64::precompute_mod_mul_data(&n))
}) == 1
}
fn fibonacci_chain_precomputed(m: u64, n: u64, inverse: f64) -> (u64, u64) {
let mut x = 2;
let mut y = n - 3;
let mut power = u64::power_of_2(m.significant_bits() - 1);
while power != 0 {
let xy = mod_mul_preinverted_float(x, y, n, inverse).mod_add(3, n);
(x, y) = if m & power != 0 {
(
xy,
mod_mul_preinverted_float(y, y, n, inverse).mod_sub(2, n),
)
} else {
(
mod_mul_preinverted_float(x, x, n, inverse).mod_sub(2, n),
xy,
)
};
power >>= 1;
}
(x, y)
}
fn fibonacci_chain_preinvert(m: u64, n: u64, ninv: u64) -> (u64, u64) {
let mut x = 2;
let mut y = n - 3;
let mut power = u64::power_of_2(m.significant_bits() - 1);
while power != 0 {
let xy = x.mod_mul_precomputed(y, n, &ninv).mod_add(3, n);
(x, y) = if m & power != 0 {
(xy, y.mod_mul_precomputed(y, n, &ninv).mod_sub(2, n))
} else {
(x.mod_mul_precomputed(x, n, &ninv).mod_sub(2, n), xy)
};
power >>= 1;
}
(x, y)
}
fn is_probable_prime_fibonacci(n: u64) -> bool {
if i64::wrapping_from(n).le_abs(&3) {
return n >= 2;
}
let m = n.wrapping_sub(u64::wrapping_from(5.jacobi_symbol(n))) >> 1;
if n.significant_bits() <= FLINT_D_BITS {
let inverse = 1.0 / (n as f64);
let (x, y) = fibonacci_chain_precomputed(m, n, inverse);
mod_mul_preinverted_float(n - 3, x, n, inverse)
== mod_mul_preinverted_float(2, y, n, inverse)
} else {
let inverse = u64::precompute_mod_mul_data(&n);
let (x, y) = fibonacci_chain_preinvert(m, n, inverse);
(n - 3).mod_mul_precomputed(x, n, &inverse) == 2.mod_mul_precomputed(y, n, &inverse)
}
}
fn lucas_chain_precomputed(m: u64, a: u64, n: u64, npre: f64) -> (u64, u64) {
let mut x = 2;
let mut y = n - 3;
let mut power = u64::power_of_2(m.significant_bits() - 1);
while power != 0 {
let xy = mod_mul_preinverted_float(x, y, n, npre).mod_sub(a, n);
(x, y) = if m & power != 0 {
(xy, mod_mul_preinverted_float(y, y, n, npre).mod_sub(2, n))
} else {
(mod_mul_preinverted_float(x, x, n, npre).mod_sub(2, n), xy)
};
power >>= 1;
}
(x, y)
}
fn lucas_chain_preinvert(m: u64, a: u64, n: u64, ninv: u64) -> (u64, u64) {
let mut x = 2;
let mut y = a;
let mut power = u64::power_of_2(m.significant_bits() - 1);
while power != 0 {
let xy = x.mod_mul_precomputed(y, n, &ninv).mod_sub(a, n);
(x, y) = if m & power != 0 {
(xy, y.mod_mul_precomputed(y, n, &ninv).mod_sub(2, n))
} else {
(x.mod_mul_precomputed(x, n, &ninv).mod_sub(2, n), xy)
};
power >>= 1;
}
(x, y)
}
fn is_probable_prime_lucas(n: u64) -> bool {
let mut d = 0u64;
if i64::wrapping_from(n).le_abs(&2) {
return n == 2;
}
let mut neg_d = false;
let mut j = 0;
for i in 0..100 {
d = 5 + (i << 1);
neg_d = false;
if d.gcd(n % d) == 1 {
if i.odd() {
neg_d = true;
}
let jacobi = if neg_d {
(-i128::from(d)).jacobi_symbol(i128::from(n))
} else {
d.jacobi_symbol(n)
};
if jacobi == -1 {
break;
}
} else if n != d {
return false;
}
j += 1;
}
if j == 100 {
return true;
}
if neg_d {
d.wrapping_neg_assign();
}
let mut q = u64::wrapping_from(1i64.wrapping_sub(i64::wrapping_from(d)) / 4);
if q.get_highest_bit() {
q.wrapping_add_assign(n);
}
let a = q.mod_inverse(n).unwrap().mod_sub(2, n);
let (left, right) = if n <= FLINT_D_BITS {
let inverse = 1.0 / (n as f64);
let (x, y) = lucas_chain_precomputed(n + 1, a, n, inverse);
(
mod_mul_preinverted_float(a, x, n, inverse),
mod_mul_preinverted_float(2, y, n, inverse),
)
} else {
let inverse = u64::precompute_mod_mul_data(&n);
let (x, y) = lucas_chain_preinvert(n + 1, a, n, inverse);
(
a.mod_mul_precomputed(x, n, &inverse),
2.mod_mul_precomputed(y, n, &inverse),
)
};
left == right
}
fn is_strong_probable_prime_preinverted_u32(n: u32, inverse: u32, a: u32, d: u32) -> bool {
assert!(a < n);
let nm1 = n - 1;
if a <= 1 || a == nm1 {
return true;
}
let mut t = d;
let mut y = mod_pow_preinverted_u32(a, t, n, inverse);
if y == 1 {
return true;
}
t <<= 1;
while t != nm1 && y != nm1 {
y.mod_mul_precomputed_assign(y, n, &inverse);
t <<= 1;
}
y == nm1
}
fn is_strong_probable_prime_preinverted_u64(n: u64, inverse: u64, a: u64, d: u64) -> bool {
assert!(a < n);
let nm1 = n - 1;
if a <= 1 || a == nm1 {
return true;
}
let mut t = d;
let mut y = mod_pow_preinverted_u64(a, t, n, inverse);
if y == 1 {
return true;
}
t <<= 1;
while t != nm1 && y != nm1 {
y.mod_mul_precomputed_assign(y, n, &inverse);
t <<= 1;
}
y == nm1
}
fn mod_preinverted_float(a: u64, n: u64, inverse: f64) -> u64 {
if a < n {
return a;
}
let ni = i64::wrapping_from(n);
if ni < 0 {
return a.wrapping_sub(n);
}
let (mut q, mut r) = if n == 1 {
(a, 0)
} else {
let q = ((a as f64) * inverse) as u64;
(
q,
i64::wrapping_from(a).wrapping_sub(i64::wrapping_from(q.wrapping_mul(n))),
)
};
if r < ni.wrapping_neg() {
q -= ((r.wrapping_neg() as f64) * inverse) as u64;
} else if r >= ni {
q += ((r as f64) * inverse) as u64;
} else if r < 0 {
return u64::wrapping_from(r + ni);
} else {
return u64::wrapping_from(r);
}
r = i64::wrapping_from(a) - i64::wrapping_from(q.wrapping_mul(n));
u64::wrapping_from(if r >= ni {
r.wrapping_sub(ni)
} else if r < 0 {
r.wrapping_add(ni)
} else {
r
})
}
fn is_strong_probable_prime_preinverted_float(n: u64, inverse: f64, mut a: u64, d: u64) -> bool {
if a >= n {
a = mod_preinverted_float(a, n, inverse);
}
let nm1 = n - 1;
if a <= 1 || a == nm1 {
return true;
}
let mut t = d;
let mut y = mod_pow_preinverted_float(a, t, n, inverse);
if y == 1 {
return true;
}
t <<= 1;
while t != nm1 && y != nm1 {
y = mod_mul_preinverted_float(y, y, n, inverse);
t <<= 1;
}
y == nm1
}
fn is_probable_prime_bpsw(n: u64) -> bool {
let nm10 = n % 10;
if nm10 == 3 || nm10 == 7 {
return is_probable_prime_fermat(n, 2) && is_probable_prime_fibonacci(n);
}
let mut d = n - 1;
while d.even() {
d >>= 1;
}
let result = if n.significant_bits() <= FLINT_D_BITS {
is_strong_probable_prime_preinverted_float(n, 1.0 / (n as f64), 2, d)
} else {
is_strong_probable_prime_preinverted_u64(n, u64::precompute_mod_mul_data(&n), 2, d)
};
if !result {
return false;
}
is_probable_prime_lucas(n)
}
const FLINT_ODDPRIME_SMALL_CUTOFF: u32 = 4096;
fn is_probable_prime_u32(n: u32) -> bool {
if n < FLINT_ODDPRIME_SMALL_CUTOFF {
return is_odd_prime_small_u32(n);
}
let mut d = n - 1;
d >>= TrailingZeros::trailing_zeros(d);
let inverse = u32::precompute_mod_mul_data(&n);
if n < 9080191 {
is_strong_probable_prime_preinverted_u32(n, inverse, 31, d)
&& is_strong_probable_prime_preinverted_u32(n, inverse, 73, d)
} else {
is_strong_probable_prime_preinverted_u32(n, inverse, 2, d)
&& is_strong_probable_prime_preinverted_u32(n, inverse, 7, d)
&& is_strong_probable_prime_preinverted_u32(n, inverse, 61, d)
}
}
fn is_probable_prime_u64(n: u64) -> bool {
if n < u64::from(FLINT_ODDPRIME_SMALL_CUTOFF) {
return is_odd_prime_small_u64(n);
} else if n >= 1050535501 {
return is_probable_prime_bpsw(n);
}
let mut d = n - 1;
d >>= TrailingZeros::trailing_zeros(d);
let inverse = 1.0 / (n as f64);
if n < 341531 {
is_strong_probable_prime_preinverted_float(n, inverse, 9345883071009581737, d)
} else {
is_strong_probable_prime_preinverted_float(n, inverse, 336781006125, d)
&& is_strong_probable_prime_preinverted_float(n, inverse, 9639812373923155, d)
}
}
impl IsPrime for u8 {
fn is_prime(&self) -> bool {
let n = *self;
if n < 11 {
n == 2 || n == 3 || n == 5 || n == 7
} else if n.even() || n.is_multiple_of(3) || n.is_multiple_of(5) || n.is_multiple_of(7) {
false
} else {
n < 121 || !n.is_multiple_of(11) && !n.is_multiple_of(13)
}
}
}
impl IsPrime for u16 {
fn is_prime(&self) -> bool {
let n = *self;
if n < 11 {
n == 2 || n == 3 || n == 5 || n == 7
} else if n.even() || n.is_multiple_of(3) || n.is_multiple_of(5) || n.is_multiple_of(7) {
false
} else if n < 121 {
true
} else if n.is_multiple_of(11)
|| n.is_multiple_of(13)
|| n.is_multiple_of(17)
|| n.is_multiple_of(19)
|| n.is_multiple_of(23)
|| n.is_multiple_of(29)
|| n.is_multiple_of(31)
|| n.is_multiple_of(37)
|| n.is_multiple_of(41)
|| n.is_multiple_of(43)
|| n.is_multiple_of(47)
|| n.is_multiple_of(53)
{
false
} else {
n < 3481 || is_probable_prime_u32(u32::from(n))
}
}
}
impl IsPrime for u32 {
fn is_prime(&self) -> bool {
let n = *self;
if n < 11 {
n == 2 || n == 3 || n == 5 || n == 7
} else if n.even() || n.is_multiple_of(3) || n.is_multiple_of(5) || n.is_multiple_of(7) {
false
} else if n < 121 {
true
} else if n.is_multiple_of(11)
|| n.is_multiple_of(13)
|| n.is_multiple_of(17)
|| n.is_multiple_of(19)
|| n.is_multiple_of(23)
|| n.is_multiple_of(29)
|| n.is_multiple_of(31)
|| n.is_multiple_of(37)
|| n.is_multiple_of(41)
|| n.is_multiple_of(43)
|| n.is_multiple_of(47)
|| n.is_multiple_of(53)
{
false
} else if n < 3481 {
true
} else if n > 1000000
&& (n.is_multiple_of(59)
|| n.is_multiple_of(61)
|| n.is_multiple_of(67)
|| n.is_multiple_of(71)
|| n.is_multiple_of(73)
|| n.is_multiple_of(79)
|| n.is_multiple_of(83)
|| n.is_multiple_of(89)
|| n.is_multiple_of(97)
|| n.is_multiple_of(101)
|| n.is_multiple_of(103)
|| n.is_multiple_of(107)
|| n.is_multiple_of(109)
|| n.is_multiple_of(113)
|| n.is_multiple_of(127)
|| n.is_multiple_of(131)
|| n.is_multiple_of(137)
|| n.is_multiple_of(139)
|| n.is_multiple_of(149))
{
false
} else {
is_probable_prime_u32(n)
}
}
}
impl IsPrime for u64 {
fn is_prime(&self) -> bool {
let n = *self;
if n < 11 {
n == 2 || n == 3 || n == 5 || n == 7
} else if n.even() || n.is_multiple_of(3) || n.is_multiple_of(5) || n.is_multiple_of(7) {
false
} else if n < 121 {
true
} else if n.is_multiple_of(11)
|| n.is_multiple_of(13)
|| n.is_multiple_of(17)
|| n.is_multiple_of(19)
|| n.is_multiple_of(23)
|| n.is_multiple_of(29)
|| n.is_multiple_of(31)
|| n.is_multiple_of(37)
|| n.is_multiple_of(41)
|| n.is_multiple_of(43)
|| n.is_multiple_of(47)
|| n.is_multiple_of(53)
{
false
} else if n < 3481 {
true
} else if n > 1000000
&& (n.is_multiple_of(59)
|| n.is_multiple_of(61)
|| n.is_multiple_of(67)
|| n.is_multiple_of(71)
|| n.is_multiple_of(73)
|| n.is_multiple_of(79)
|| n.is_multiple_of(83)
|| n.is_multiple_of(89)
|| n.is_multiple_of(97)
|| n.is_multiple_of(101)
|| n.is_multiple_of(103)
|| n.is_multiple_of(107)
|| n.is_multiple_of(109)
|| n.is_multiple_of(113)
|| n.is_multiple_of(127)
|| n.is_multiple_of(131)
|| n.is_multiple_of(137)
|| n.is_multiple_of(139)
|| n.is_multiple_of(149))
{
false
} else {
is_probable_prime_u64(n)
}
}
}
impl IsPrime for usize {
#[inline]
fn is_prime(&self) -> bool {
if USIZE_IS_U32 {
u32::wrapping_from(*self).is_prime()
} else {
u64::wrapping_from(*self).is_prime()
}
}
}