mod bpsw;
mod next;
mod tables;
use super::add::{SMALL, read_small};
use super::mul::{Operand, STACK_LIMBS, on_heap, on_stack, to_limbs};
use super::pow::montgomery::{Mont1, Mont2, MontN, even};
use super::{ByteOrder, Number};
use bpsw::{Bits, LimbField, bpsw, bpsw_limbs, bpsw_limbs_itch, reciprocal, rem_by};
use core::mem::MaybeUninit;
use tables::{GROUP, Group, PRIMES, groups_below};
impl Number {
pub fn is_probable_prime(&self) -> bool {
if self.value.len() <= SMALL {
let x = unsafe { read_small(&self.value, self.byte_order) };
if x == 0 || self.negative {
return false;
}
if x >> 64 == 0 {
return is_prime_u64(x as u64);
}
return x & 1 == 1 && is_prime_limbs_small(&[x as u64, (x >> 64) as u64]);
}
let n = Operand::of(self);
if n.significant == 0 || self.negative {
return false;
}
let nn = n.significant.div_ceil(8);
let compute = |s: *mut MaybeUninit<u64>| {
let s = s.cast::<u64>();
unsafe {
to_limbs(n, s);
if nn == 1 {
return is_prime_u64(*s);
}
*s & 1 == 1 && is_prime_odd(s, nn, s.add(even(nn)))
}
};
let need = even(nn) + prime_itch(nn);
if need <= STACK_LIMBS {
on_stack(compute)
} else {
on_heap(need, compute)
}
}
pub fn next_prime(&self) -> Number {
next::next_prime(self)
}
}
const TRIAL_64: u64 = 256;
const TRIAL_64_END: usize = {
let mut i = 0;
while PRIMES[i].p <= TRIAL_64 {
i += 1;
}
i
};
fn is_prime_u64(n: u64) -> bool {
if n < 4 {
return n >= 2;
}
if n & 1 == 0 {
return false;
}
for p in &PRIMES[..TRIAL_64_END] {
if p.divides(n) {
return n == p.p;
}
}
let next = PRIMES[TRIAL_64_END].p;
if n < next * next {
return true;
}
let f = Mont1::new(n);
let bits = Bits {
n: &n,
len: 64 - n.leading_zeros() as usize,
};
unsafe { bpsw(&f, bits, || selfridge_u64(n)) }
}
fn selfridge_u64(n: u64) -> Option<i64> {
if is_square_u64(n) {
return None;
}
let mut a = 5u64;
loop {
if a >= n {
return Some(0);
}
let d = if a & 2 == 0 { a as i64 } else { -(a as i64) };
match symbol(d, n % a, n & 3) {
-1 => return Some(d),
0 => return None,
_ => a += 2,
}
}
}
fn is_square_u64(n: u64) -> bool {
if (0x0213 >> (n & 15)) & 1 == 0 {
return false;
}
let mut r = (n as f64).sqrt() as u64;
while u128::from(r) * u128::from(r) > u128::from(n) {
r -= 1;
}
while u128::from(r + 1) * u128::from(r + 1) <= u128::from(n) {
r += 1;
}
r * r == n
}
fn symbol(d: i64, n_mod_a: u64, n_mod_4: u64) -> i32 {
let a = d.unsigned_abs();
let mut j = jacobi(n_mod_a, a);
if a & 3 == 3 && n_mod_4 == 3 {
j = -j;
}
if d < 0 && n_mod_4 == 3 {
j = -j;
}
j
}
fn jacobi(mut x: u64, mut a: u64) -> i32 {
debug_assert!(a & 1 == 1);
let mut t = 1;
if x >= a {
x %= a;
}
while x != 0 {
let z = x.trailing_zeros();
x >>= z;
if z & 1 == 1 && matches!(a & 7, 3 | 5) {
t = -t;
}
if x < a {
if x & a & 2 != 0 {
t = -t;
}
core::mem::swap(&mut x, &mut a);
}
x -= a;
}
if a == 1 { t } else { 0 }
}
fn trial_bound(bits: usize) -> u64 {
(bits as u64 * 4).clamp(256, tables::LIMIT as u64)
}
fn prime_itch(nn: usize) -> usize {
if nn >= 8 { bpsw_limbs_itch(nn) } else { 0 }
}
fn is_prime_limbs_small(n: &[u64; 2]) -> bool {
unsafe { is_prime_odd(n.as_ptr(), 2, core::ptr::null_mut()) }
}
#[inline]
unsafe fn rem_by_4(n: *const u64, nn: usize, g: &[Group; 4]) -> [u64; 4] {
let mut r = [0u64; 4];
unsafe {
for i in (0..nn).rev() {
let x = *n.add(i);
for k in 0..4 {
r[k] = super::div::limbs::div_2by1(r[k], x, g[k].d, g[k].v).1;
}
}
}
r
}
unsafe fn has_factor_in(n: *const u64, nn: usize, groups: &[Group]) -> bool {
let (chunks, rest) = groups.as_chunks::<4>();
for g in chunks {
let r = unsafe { rem_by_4(n, nn, g) };
for k in 0..4 {
let primes = &PRIMES[g[k].first as usize..g[k].end as usize];
if primes.iter().any(|p| p.divides(r[k])) {
return true;
}
}
}
for g in rest {
let r = unsafe { rem_by(n, nn, g.d, g.v) };
let primes = &PRIMES[g.first as usize..g.end as usize];
if primes.iter().any(|p| p.divides(r)) {
return true;
}
}
false
}
unsafe fn is_prime_odd(n: *const u64, nn: usize, s: *mut u64) -> bool {
unsafe {
let bits = Bits {
n,
len: 64 * nn - (*n.add(nn - 1)).leading_zeros() as usize,
};
let g = groups_below(trial_bound(bits.len));
if has_factor_in(n, nn, &GROUP[..g]) {
return false;
}
probable_prime_odd(n, nn, bits, s)
}
}
unsafe fn probable_prime_odd(n: *const u64, nn: usize, bits: Bits, s: *mut u64) -> bool {
let select = || {
unsafe { selfridge(n, nn) }
};
unsafe {
match nn {
2 => bpsw(&Mont2::new([*n, *n.add(1)]), bits, select),
3 => bpsw(&MontN::<3>::new(limbs(n)), bits, select),
4 => bpsw(&MontN::<4>::new(limbs(n)), bits, select),
5 => bpsw(&MontN::<5>::new(limbs(n)), bits, select),
6 => bpsw(&MontN::<6>::new(limbs(n)), bits, select),
7 => bpsw(&MontN::<7>::new(limbs(n)), bits, select),
_ => {
let f = LimbField::new(n, nn, s);
bpsw_limbs(&f, bits, s.add(LimbField::itch(nn)), select)
}
}
}
}
#[inline(always)]
unsafe fn limbs<const N: usize>(n: *const u64) -> [u64; N] {
let mut a = [0; N];
unsafe { core::ptr::copy_nonoverlapping(n, a.as_mut_ptr(), N) };
a
}
const SELFRIDGE: u64 = 9 * 5 * 7 * 11 * 13 * 17 * 19 * 23 * 29 * 31 * 37 * 41 * 43 * 47;
const SELFRIDGE_D: u64 = SELFRIDGE << SELFRIDGE.leading_zeros();
const SELFRIDGE_V: u64 = (u128::MAX / SELFRIDGE_D as u128) as u64;
const SQUARE_PRIMES: [u64; 14] = [3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47];
unsafe fn selfridge(n: *const u64, nn: usize) -> Option<i64> {
unsafe {
let r = rem_by(n, nn, SELFRIDGE_D, SELFRIDGE_V) % SELFRIDGE;
let low = *n;
if low & 7 == 1
&& SQUARE_PRIMES
.iter()
.all(|&p| is_quadratic_residue(r % p, p))
&& is_square(n, nn)
{
return None;
}
let mut a = 5u64;
loop {
let d = if a & 2 == 0 { a as i64 } else { -(a as i64) };
let n_mod_a = if SELFRIDGE.is_multiple_of(a) {
r % a
} else {
let s = a.leading_zeros();
rem_by(n, nn, a << s, reciprocal(a << s)) % a
};
match symbol(d, n_mod_a, low & 3) {
-1 => return Some(d),
0 => return None,
_ => a += 2,
}
}
}
}
fn is_quadratic_residue(r: u64, p: u64) -> bool {
r == 0 || jacobi(r, p) == 1
}
#[cold]
#[inline(never)]
unsafe fn is_square(n: *const u64, nn: usize) -> bool {
let bytes: Vec<u8> = (0..nn)
.flat_map(|i| unsafe { *n.add(i) }.to_le_bytes())
.collect();
let x = Number::new(bytes, ByteOrder::LittleEndian);
let bits = 64 * nn - unsafe { *n.add(nn - 1) }.leading_zeros() as usize;
let mut r = &Number::new_from_u8(1, ByteOrder::LittleEndian) << bits.div_ceil(2);
loop {
let next = &(&r + &(&x / &r)) >> 1;
if !(&next - &r).is_negative() {
break;
}
r = next;
}
(&r * &r).value == x.value
}