use super::super::mul::limbs::add_1;
use super::super::mul::{Operand, Product, STACK_LIMBS, on_heap, on_stack, to_limbs};
use super::super::pow::montgomery::even;
use super::super::{ByteOrder, Number};
use super::bpsw::{Bits, reciprocal, rem_by};
use super::tables::{GROUP, GROUPS, PRIMES};
use super::{is_prime_u64, prime_itch, probable_prime_odd};
use core::mem::MaybeUninit;
const LAST_PRIME_64: u64 = u64::MAX - 58;
pub(super) fn next_prime(x: &Number) -> Number {
let order = x.byte_order;
let n = Operand::of(x);
if x.negative || n.significant <= 8 {
let v = if x.negative || n.significant == 0 {
0
} else {
let mut v = [0u64];
unsafe { to_limbs(n, v.as_mut_ptr()) };
v[0]
};
if v < LAST_PRIME_64 {
return number(&[next_u64(v)], order);
}
}
let nn = n.significant.div_ceil(8);
let need = even(nn + 2) + prime_itch(nn + 1);
let compute = |s: *mut MaybeUninit<u64>| {
let s = s.cast::<u64>();
unsafe {
to_limbs(n, s);
*s.add(nn) = 0;
*s.add(nn + 1) = 0;
add_1(s, nn + 1, if *s & 1 == 1 { 2 } else { 1 });
let len = if *s.add(nn) != 0 { nn + 1 } else { nn };
search(s, len, s.add(even(nn + 2)), order)
}
};
if need <= STACK_LIMBS {
on_stack(compute)
} else {
on_heap(need, compute)
}
}
fn number(l: &[u64], order: ByteOrder) -> Number {
let top = l[l.len() - 1];
let len = 8 * l.len() - top.leading_zeros() as usize / 8;
let mut v = Vec::with_capacity(len);
unsafe {
Product {
r: l.as_ptr(),
len,
negative: false,
}
.write(v.as_mut_ptr(), order);
v.set_len(len);
}
Number {
value: v,
negative: false,
byte_order: order,
}
}
const WHEEL: [u8; 8] = [6, 4, 2, 4, 2, 4, 6, 2];
fn next_u64(n: u64) -> u64 {
if n < 7 {
return [2, 2, 3, 5, 5, 7, 7][n as usize];
}
const START: [u8; 30] = [
1, 7, 7, 7, 7, 7, 7, 11, 11, 11, 11, 13, 13, 17, 17, 17, 17, 19, 19, 23, 23, 23, 23, 29,
29, 29, 29, 29, 29, 31,
];
const INDEX: [u8; 32] = [
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 3, 0, 0, 0, 4, 0, 5, 0, 0, 0, 6, 0, 0, 0, 0, 0, 7,
0, 0,
];
let base = n - n % 30;
let mut c = base + u64::from(START[(n % 30) as usize]);
let mut i = INDEX[(c % 30) as usize] as usize;
loop {
if is_prime_u64(c) {
return c;
}
c += u64::from(WHEEL[i]);
i = (i + 1) & 7;
}
}
fn sieve_limit(bits: usize) -> u64 {
let b = bits as f64;
((b * b * b.sqrt() / 124.0) as u64).clamp(1000, 1 << 24)
}
fn interval(bits: usize) -> usize {
5 * bits
}
fn primes_beyond_tables(limit: u64) -> Vec<u32> {
let first = PRIMES[PRIMES.len() - 1].p + 2;
if limit <= first {
return Vec::new();
}
let half = (limit / 2) as usize;
let mut composite = vec![false; half];
let mut i = 1;
while (2 * i + 1) * (2 * i + 1) < limit as usize {
if !composite[i] {
let p = 2 * i + 1;
let mut j = p * p / 2;
while j < half {
composite[j] = true;
j += p;
}
}
i += 1;
}
(first as usize / 2..half)
.filter(|&i| !composite[i])
.map(|i| (2 * i + 1) as u32)
.collect()
}
unsafe fn search(start: *mut u64, nn: usize, s: *mut u64, order: ByteOrder) -> Number {
unsafe {
let top = *start.add(nn - 1);
let bits = 64 * nn - top.leading_zeros() as usize;
let limit = sieve_limit(bits);
let odds = interval(bits);
let more = primes_beyond_tables(limit);
let mut composite = vec![false; odds];
let mut nn = nn;
loop {
composite.iter_mut().for_each(|c| *c = false);
let mut mark = |p: u64, r: u64| {
let t = if r == 0 { 0 } else { p - r };
let mut i = if t & 1 == 0 { t / 2 } else { (t + p) / 2 } as usize;
while i < odds {
composite[i] = true;
i += p as usize;
}
};
for g in &GROUP[..GROUPS] {
let r = rem_by(start, nn, g.d, g.v);
for p in &PRIMES[g.first as usize..g.end as usize] {
if p.p < limit {
mark(p.p, r % p.p);
}
}
}
let mut i = 0;
while i < more.len() {
let mut product = 1u64;
let mut j = i;
while j < more.len() {
match product.checked_mul(u64::from(more[j])) {
Some(x) => product = x,
None => break,
}
j += 1;
}
let d = product << product.leading_zeros();
let r = rem_by(start, nn, d, reciprocal(d));
for &p in &more[i..j] {
mark(u64::from(p), r % u64::from(p));
}
i = j;
}
let mut at = 0;
for (i, &c) in composite.iter().enumerate() {
if c {
continue;
}
let step = 2 * (i - at) as u64;
at = i;
if add_1(start, nn + 1, step) != 0 || *start.add(nn) != 0 {
nn += 1;
}
let bits = Bits {
n: start,
len: 64 * nn - (*start.add(nn - 1)).leading_zeros() as usize,
};
let prime = if nn == 1 {
is_prime_u64(*start)
} else {
probable_prime_odd(start, nn, bits, s)
};
if prime {
return number(core::slice::from_raw_parts(start, nn), order);
}
}
if add_1(start, nn + 1, 2 * (odds - at) as u64) != 0 || *start.add(nn) != 0 {
nn += 1;
}
}
}
}