use crate::buffer::{NaiveBuffer, PrimeBufferExt};
use crate::factor::{one_line, pollard_rho, squfof, SQUFOF_MULTIPLIERS};
use crate::mint::SmallMint;
use crate::primality::{PrimalityBase, PrimalityRefBase};
use crate::tables::{
MOEBIUS_ODD, SMALL_PRIMES, SMALL_PRIMES_NEXT, WHEEL_NEXT, WHEEL_PREV, WHEEL_SIZE,
};
#[cfg(feature = "big-table")]
use crate::tables::{SMALL_PRIMES_INV, ZETA_LOG_TABLE};
use crate::traits::{FactorizationConfig, Primality, PrimalityTestConfig, PrimalityUtils};
use crate::{BitTest, ExactRoots};
use num_integer::Roots;
#[cfg(feature = "big-table")]
use num_modular::DivExact;
use num_modular::{ModularCoreOps, ModularInteger, MontgomeryInt};
use num_traits::{CheckedAdd, FromPrimitive, Num, RefNum, ToPrimitive};
use rand::random;
use std::collections::BTreeMap;
use std::convert::TryFrom;
#[cfg(feature = "big-table")]
use crate::tables::{MILLER_RABIN_BASE32, MILLER_RABIN_BASE64};
#[cfg(not(feature = "big-table"))]
pub fn is_prime64(target: u64) -> bool {
if target < 2 {
return false;
}
if target & 1 == 0 {
return target == 2;
}
if let Ok(u) = u8::try_from(target) {
return SMALL_PRIMES.binary_search(&u).is_ok();
} else {
let pos = (target % WHEEL_SIZE as u64) as usize;
if pos == 0 || WHEEL_NEXT[pos] < WHEEL_NEXT[pos - 1] {
return false;
}
}
is_prime64_miller(target)
}
#[cfg(feature = "big-table")]
#[must_use]
pub fn is_prime64(target: u64) -> bool {
if target < 2 {
return false;
}
if target & 1 == 0 {
return target == 2;
}
if target < SMALL_PRIMES_NEXT {
return SMALL_PRIMES.binary_search(&(target as u16)).is_ok();
} else {
let pos = (target % u64::from(WHEEL_SIZE)) as usize;
if pos == 0 || WHEEL_NEXT[pos] < WHEEL_NEXT[pos - 1] {
return false;
}
}
is_prime64_miller(target)
}
#[cfg(not(feature = "big-table"))]
fn is_prime64_miller(target: u64) -> bool {
if let Ok(u) = u32::try_from(target) {
const WITNESS32: [u32; 3] = [2, 7, 61];
let u = SmallMint::from(u);
WITNESS32.iter().all(|&x| u.is_sprp(SmallMint::from(x)))
} else {
const WITNESS64: [u64; 7] = [2, 325, 9375, 28178, 450775, 9780504, 1795265022];
let u = SmallMint::from(target);
WITNESS64.iter().all(|&x| u.is_sprp(SmallMint::from(x)))
}
}
#[cfg(feature = "big-table")]
fn is_prime32_miller(target: u32) -> bool {
let h = u64::from(target);
let h = ((h >> 16) ^ h).wrapping_mul(0x045d_9f3b);
let h = ((h >> 16) ^ h).wrapping_mul(0x045d_9f3b);
let h = ((h >> 16) ^ h) & 255;
let u = SmallMint::from(target);
u.is_sprp(SmallMint::from(u32::from(MILLER_RABIN_BASE32[h as usize])))
}
#[cfg(feature = "big-table")]
fn is_prime64_miller(target: u64) -> bool {
if let Ok(u) = u32::try_from(target) {
return is_prime32_miller(u);
}
let u = SmallMint::from(target);
if !u.is_sprp(2.into()) {
return false;
}
let h = target;
let h = ((h >> 32) ^ h).wrapping_mul(0x045d_9f3b_3335_b369);
let h = ((h >> 32) ^ h).wrapping_mul(0x0333_5b36_945d_9f3b);
let h = ((h >> 32) ^ h) & 16383;
let b = MILLER_RABIN_BASE64[h as usize];
u.is_sprp((u64::from(b) & 4095).into()) && u.is_sprp((u64::from(b) >> 12).into())
}
#[must_use]
pub fn factorize64(target: u64) -> BTreeMap<u64, usize> {
let mut result = BTreeMap::new();
let f2 = target.trailing_zeros();
if f2 == 0 {
if is_prime64(target) {
result.insert(target, 1);
return result;
}
} else {
result.insert(2, f2 as usize);
}
let tsqrt = target.sqrt() + 1;
let mut residual = target >> f2;
let mut factored = false;
#[cfg(not(feature = "big-table"))]
for p in SMALL_PRIMES.iter().skip(1).map(|&v| v as u64) {
if p > tsqrt {
factored = true;
break;
}
while residual % p == 0 {
residual = residual / p;
*result.entry(p).or_insert(0) += 1;
}
if residual == 1 {
factored = true;
break;
}
}
#[cfg(feature = "big-table")]
for (p, &pinv) in SMALL_PRIMES
.iter()
.map(|&p| u64::from(p))
.zip(SMALL_PRIMES_INV.iter())
.skip(1)
{
if p > tsqrt {
factored = true;
break;
}
let mut exp: usize = 0;
while let Some(q) = DivExact::div_exact(residual, p, &pinv) {
exp += 1;
residual = q;
}
if exp > 0 {
result.insert(p, exp);
}
if residual == 1 {
factored = true;
break;
}
}
if factored {
if residual != 1 {
result.insert(residual, 1);
}
return result;
}
for (p, exp) in factorize64_advanced(&[(residual, 1usize)]) {
*result.entry(p).or_insert(0) += exp;
}
result
}
pub(crate) fn factorize64_advanced(cofactors: &[(u64, usize)]) -> Vec<(u64, usize)> {
let mut todo: Vec<_> = cofactors.to_vec();
let mut factored: Vec<(u64, usize)> = Vec::new();
while let Some((target, exp)) = todo.pop() {
if is_prime64_miller(target) {
factored.push((target, exp));
continue;
}
if let Some(d) = target.sqrt_exact() {
todo.push((d, exp * 2));
continue;
}
if let Some(d) = target.cbrt_exact() {
todo.push((d, exp * 3));
continue;
}
let mut i = 0usize;
let mut max_iter_ratio = 1; let divisor = loop {
const NMETHODS: usize = 3;
match i % NMETHODS {
0 => {
let start = MontgomeryInt::new(random::<u64>(), &target);
let offset = start.convert(random::<u64>());
let max_iter = max_iter_ratio << (target.bits() / 6); if let (Some(p), _) = pollard_rho(
&SmallMint::from(target),
start.into(),
offset.into(),
max_iter,
) {
break p.value();
}
}
1 => {
let mul_target = target.checked_mul(480).unwrap_or(target);
let max_iter = max_iter_ratio << (mul_target.bits() / 6); if let (Some(p), _) = one_line(&target, mul_target, max_iter) {
break p;
}
}
2 => {
let mut d = None;
for &k in &SQUFOF_MULTIPLIERS {
if let Some(mul_target) = target.checked_mul(u64::from(k)) {
let max_iter = max_iter_ratio * 2 * mul_target.sqrt().sqrt() as usize;
if let (Some(p), _) = squfof(&target, mul_target, max_iter) {
d = Some(p);
break;
}
}
}
if let Some(p) = d {
break p;
}
}
_ => unreachable!(),
}
i += 1;
if i % NMETHODS == 0 {
max_iter_ratio *= 2;
}
};
todo.push((divisor, exp));
todo.push((target / divisor, exp));
}
factored
}
#[must_use]
pub fn factorize128(target: u128) -> BTreeMap<u128, usize> {
if target < (1u128 << 64) {
return factorize64(target as u64)
.into_iter()
.map(|(k, v)| (u128::from(k), v))
.collect();
}
let mut result = BTreeMap::new();
let f2 = target.trailing_zeros();
if f2 != 0 {
result.insert(2, f2 as usize);
}
let mut residual = target >> f2;
#[cfg(not(feature = "big-table"))]
for p in SMALL_PRIMES.iter().skip(1).map(|&v| v as u128) {
while residual % p == 0 {
residual = residual / p;
*result.entry(p).or_insert(0) += 1;
}
if residual == 1 {
return result;
}
}
#[cfg(feature = "big-table")]
for (p, &pinv) in SMALL_PRIMES
.iter()
.map(|&p| u64::from(p))
.zip(SMALL_PRIMES_INV.iter())
.skip(1)
{
let mut exp: usize = 0;
while let Some(q) = DivExact::div_exact(residual, p, &pinv) {
exp += 1;
residual = q;
}
if exp > 0 {
result.insert(u128::from(p), exp);
}
if residual == 1 {
return result;
}
}
for (p, exp) in factorize128_advanced(&[(residual, 1usize)]) {
*result.entry(p).or_insert(0) += exp;
}
result
}
pub(crate) fn factorize128_advanced(cofactors: &[(u128, usize)]) -> Vec<(u128, usize)> {
let (mut todo128, mut todo64) = (Vec::new(), Vec::new()); let mut factored: Vec<(u128, usize)> = Vec::new(); for &(co, e) in cofactors {
if let Ok(co64) = u64::try_from(co) {
todo64.push((co64, e));
} else {
todo128.push((co, e));
};
}
while let Some((target, exp)) = todo128.pop() {
if is_prime(&SmallMint::from(target), Some(PrimalityTestConfig::bpsw())).probably() {
factored.push((target, exp));
continue;
}
if let Some(d) = target.sqrt_exact() {
if let Ok(d64) = u64::try_from(d) {
todo64.push((d64, exp * 2));
} else {
todo128.push((d, exp * 2));
}
continue;
}
if let Some(d) = target.cbrt_exact() {
if let Ok(d64) = u64::try_from(d) {
todo64.push((d64, exp * 3));
} else {
todo128.push((d, exp * 3));
}
continue;
}
let mut i = 0usize;
let mut max_iter_ratio = 1;
let divisor = loop {
const NMETHODS: usize = 3;
match i % NMETHODS {
0 => {
let start = MontgomeryInt::new(random::<u128>(), &target);
let offset = start.convert(random::<u128>());
let max_iter = max_iter_ratio << (target.bits() / 6); if let (Some(p), _) = pollard_rho(
&SmallMint::from(target),
start.into(),
offset.into(),
max_iter,
) {
break p.value();
}
}
1 => {
let mul_target = target.checked_mul(480).unwrap_or(target);
let max_iter = max_iter_ratio << (mul_target.bits() / 6); if let (Some(p), _) = one_line(&target, mul_target, max_iter) {
break p;
}
}
2 => {
let mut d = None;
for &k in &SQUFOF_MULTIPLIERS {
if let Some(mul_target) = target.checked_mul(u128::from(k)) {
let max_iter = max_iter_ratio * 2 * mul_target.sqrt().sqrt() as usize;
if let (Some(p), _) = squfof(&target, mul_target, max_iter) {
d = Some(p);
break;
}
}
}
if let Some(p) = d {
break p;
}
}
_ => unreachable!(),
}
i += 1;
if i % NMETHODS == 0 {
max_iter_ratio *= 2;
}
};
if let Ok(d64) = u64::try_from(divisor) {
todo64.push((d64, exp));
} else {
todo128.push((divisor, exp));
}
let co = target / divisor;
if let Ok(d64) = u64::try_from(co) {
todo64.push((d64, exp));
} else {
todo128.push((co, exp));
}
}
factored.extend(
factorize64_advanced(&todo64)
.into_iter()
.map(|(p, exp)| (u128::from(p), exp)),
);
factored
}
pub fn is_prime<T: PrimalityBase>(target: &T, config: Option<PrimalityTestConfig>) -> Primality
where
for<'r> &'r T: PrimalityRefBase<T>,
{
NaiveBuffer::new().is_prime(target, config)
}
pub fn factors<T: PrimalityBase>(
target: T,
config: Option<FactorizationConfig>,
) -> (BTreeMap<T, usize>, Option<Vec<T>>)
where
for<'r> &'r T: PrimalityRefBase<T>,
{
NaiveBuffer::new().factors(target, config)
}
pub fn factorize<T: PrimalityBase>(target: T) -> BTreeMap<T, usize>
where
for<'r> &'r T: PrimalityRefBase<T>,
{
NaiveBuffer::new().factorize(target)
}
#[must_use]
pub fn primes(limit: u64) -> Vec<u64> {
NaiveBuffer::new().into_primes(limit).collect()
}
#[must_use]
pub fn nprimes(count: usize) -> Vec<u64> {
NaiveBuffer::new().into_nprimes(count).collect()
}
#[must_use]
pub fn prime_pi(limit: u64) -> u64 {
NaiveBuffer::new().prime_pi(limit)
}
#[must_use]
pub fn nth_prime(n: u64) -> u64 {
NaiveBuffer::new().nth_prime(n)
}
#[must_use]
pub fn primorial<T: PrimalityBase + std::iter::Product>(n: usize) -> T {
NaiveBuffer::new()
.into_nprimes(n)
.map(|p| T::from_u64(p).unwrap())
.product()
}
pub fn moebius<T: PrimalityBase>(target: &T) -> i8
where
for<'r> &'r T: PrimalityRefBase<T>,
{
if target.is_even() {
let two = T::one() + T::one();
let four = &two + &two;
if (target % four).is_zero() {
return 0;
} else {
return -moebius(&(target / &two));
}
}
if let Some(v) = (target - T::one()).to_u8() {
let m = MOEBIUS_ODD[(v >> 6) as usize];
let m = m & (3 << (v & 63));
let m = m >> (v & 63);
return m as i8 - 1;
}
let three_sq = T::from_u8(9).unwrap();
let five_sq = T::from_u8(25).unwrap();
let seven_sq = T::from_u8(49).unwrap();
if (target % three_sq).is_zero()
|| (target % five_sq).is_zero()
|| (target % seven_sq).is_zero()
{
return 0;
}
moebius_factorized(&factorize(target.clone()))
}
#[must_use]
pub fn moebius_factorized<T>(factors: &BTreeMap<T, usize>) -> i8 {
if factors.values().any(|exp| exp > &1) {
0
} else if factors.len() % 2 == 0 {
1
} else {
-1
}
}
pub fn is_square_free<T: PrimalityBase>(target: &T) -> bool
where
for<'r> &'r T: PrimalityRefBase<T>,
{
moebius(target) != 0
}
pub fn prime_pi_bounds<T: ToPrimitive + FromPrimitive>(target: &T) -> (T, T) {
if let Some(x) = target.to_u64() {
if x <= u64::from(*SMALL_PRIMES.last().unwrap()) {
#[cfg(not(feature = "big-table"))]
let pos = SMALL_PRIMES.binary_search(&(x as u8));
#[cfg(feature = "big-table")]
let pos = SMALL_PRIMES.binary_search(&(x as u16));
let n = match pos {
Ok(p) => p + 1,
Err(p) => p,
};
return (T::from_usize(n).unwrap(), T::from_usize(n).unwrap());
}
let n = x as f64;
let ln = n.ln();
let invln = ln.recip();
let lo = match () {
() if x >= 468_049 => n / (ln - 1. - invln),
() if x >= 88789 => n * invln * (1. + invln * (1. + 2. * invln)),
() if x >= 5393 => n / (ln - 1.),
() if x >= 599 => n * invln * (1. + invln),
() => n * invln,
};
let hi = match () {
() if x >= 7_398_600_000 => {
n * invln * (1. + invln * (1. + invln * (2. + invln * 7.59)))
}
() if x >= 2_953_652_287 => n * invln * (1. + invln * (1. + invln * 2.334)),
() if x >= 467_345 => n / (ln - 1. - 1.2311 * invln),
() if x >= 29927 => n * invln * (1. + invln * (1. + invln * 2.53816)),
() if x >= 5668 => n / (ln - 1.112),
() if x >= 148 => n * invln * (1. + invln * 1.2762),
() => 1.25506 * n * invln,
};
(T::from_f64(lo).unwrap(), T::from_f64(hi).unwrap())
} else {
let n = target.to_f64().unwrap();
let ln = n.ln();
let invln = ln.recip();
let lo = n / (ln - 1. - invln);
let hi = n * invln * (1. + invln * (1. + invln * (2. + invln * 7.59)));
(T::from_f64(lo).unwrap(), T::from_f64(hi).unwrap())
}
}
pub fn nth_prime_bounds<T: ToPrimitive + FromPrimitive>(target: &T) -> Option<(T, T)> {
if let Some(x) = target.to_usize() {
if x == 0 {
return Some((T::from_u8(0).unwrap(), T::from_u8(0).unwrap()));
}
if x <= SMALL_PRIMES.len() {
let p = SMALL_PRIMES[x - 1];
#[cfg(not(feature = "big-table"))]
return Some((T::from_u8(p).unwrap(), T::from_u8(p).unwrap()));
#[cfg(feature = "big-table")]
return Some((T::from_u16(p).unwrap(), T::from_u16(p).unwrap()));
}
let n = x as f64;
let ln = n.ln();
let lnln = ln.ln();
let lo = match () {
() if x >= 317_200 => {
n * (ln + lnln - 1. + (lnln - 2.) / ln
- (lnln * lnln - 6. * lnln + 11.321) / (2. * ln * ln))
}
() if x >= 3520 => n * (ln + lnln - 1. + (lnln - 2.1) / ln),
() => n * (ln + lnln - 1.),
};
let hi = match () {
() if x >= 46_254_381 => {
n * (ln + lnln - 1. + (lnln - 2.) / ln
- (lnln * lnln - 6. * lnln + 10.667) / (2. * ln * ln))
}
() if x >= 8_009_824 => {
n * (ln + lnln - 1. + (lnln - 2.) / ln
- (lnln * lnln - 6. * lnln + 10.273) / (2. * ln * ln))
}
() if x >= 688_383 => n * (ln + lnln - 1. + (lnln - 2.) / ln),
() if x >= 178_974 => n * (ln + lnln - 1. + (lnln - 1.95) / ln),
() if x >= 39017 => n * (ln + lnln - 0.9484),
() if x >= 27076 => n * (ln + lnln - 1. + (lnln - 1.8) / ln),
() => n * (ln + lnln - 0.5),
};
Some((T::from_f64(lo)?, T::from_f64(hi)?))
} else {
let n = target.to_f64().unwrap();
let ln = n.ln();
let lnln = ln.ln();
let lo = n
* (ln + lnln - 1. + (lnln - 2.) / ln
- (lnln * lnln - 6. * lnln + 11.321) / (2. * ln * ln));
let hi = n
* (ln + lnln - 1. + (lnln - 2.) / ln
- (lnln * lnln - 6. * lnln + 10.667) / (2. * ln * ln));
Some((T::from_f64(lo)?, T::from_f64(hi)?))
}
}
pub fn is_safe_prime<T: PrimalityBase>(target: &T) -> Primality
where
for<'r> &'r T: PrimalityRefBase<T>,
{
let buf = NaiveBuffer::new();
let config = Some(PrimalityTestConfig::strict());
let sophie_p = buf.is_prime(&(target >> 1), config);
if matches!(sophie_p, Primality::No) {
return sophie_p;
}
let target_p = buf.is_prime(target, config);
target_p & sophie_p
}
#[cfg(not(feature = "big-table"))]
pub fn next_prime<T: PrimalityBase + CheckedAdd>(
target: &T,
config: Option<PrimalityTestConfig>,
) -> Option<T>
where
for<'r> &'r T: PrimalityRefBase<T>,
{
if let Some(x) = target.to_u8() {
return match SMALL_PRIMES.binary_search(&x) {
Ok(pos) => {
if pos + 1 == SMALL_PRIMES.len() {
T::from_u64(SMALL_PRIMES_NEXT)
} else {
T::from_u8(SMALL_PRIMES[pos + 1])
}
}
Err(pos) if pos < SMALL_PRIMES.len() => T::from_u8(SMALL_PRIMES[pos]),
Err(_) => T::from_u64(SMALL_PRIMES_NEXT),
};
}
let mut i = (target % T::from_u8(WHEEL_SIZE).unwrap()).to_u8().unwrap();
let mut t = target.clone();
loop {
let offset = WHEEL_NEXT[i as usize];
t = t.checked_add(&T::from_u8(offset).unwrap())?;
i = i.addm(offset, &WHEEL_SIZE);
if is_prime(&t, config).probably() {
break Some(t);
}
}
}
#[cfg(feature = "big-table")]
pub fn next_prime<T: PrimalityBase + CheckedAdd>(
target: &T,
config: Option<PrimalityTestConfig>,
) -> Option<T>
where
for<'r> &'r T: PrimalityRefBase<T>,
{
if target <= &T::from_u8(255).unwrap() || target < &T::from_u16(*SMALL_PRIMES.last().unwrap()).unwrap()
{
let next = match SMALL_PRIMES.binary_search(&target.to_u16().unwrap()) {
Ok(pos) => SMALL_PRIMES[pos + 1],
Err(pos) => SMALL_PRIMES[pos],
};
return T::from_u16(next);
}
let mut i = (target % T::from_u16(WHEEL_SIZE).unwrap())
.to_u16()
.unwrap();
let mut t = target.clone();
loop {
let offset = WHEEL_NEXT[i as usize];
t = t.checked_add(&T::from_u8(offset).unwrap())?;
i = i.addm(u16::from(offset), &WHEEL_SIZE);
if is_prime(&t, config).probably() {
break Some(t);
}
}
}
#[cfg(not(feature = "big-table"))]
pub fn prev_prime<T: PrimalityBase>(target: &T, config: Option<PrimalityTestConfig>) -> Option<T>
where
for<'r> &'r T: PrimalityRefBase<T>,
{
if target <= &(T::one() + T::one()) {
return None;
}
if let Some(x) = target.to_u8() {
let next = match SMALL_PRIMES.binary_search(&x) {
Ok(pos) => SMALL_PRIMES[pos - 1],
Err(pos) => SMALL_PRIMES[pos - 1],
};
return Some(T::from_u8(next).unwrap());
}
let mut i = (target % T::from_u8(WHEEL_SIZE).unwrap()).to_u8().unwrap();
let mut t = target.clone();
loop {
let offset = WHEEL_PREV[i as usize];
t = t - T::from_u8(offset).unwrap();
i = i.subm(offset, &WHEEL_SIZE);
if is_prime(&t, config).probably() {
break Some(t);
}
}
}
#[cfg(feature = "big-table")]
pub fn prev_prime<T: PrimalityBase>(target: &T, config: Option<PrimalityTestConfig>) -> Option<T>
where
for<'r> &'r T: PrimalityRefBase<T>,
{
if target <= &(T::one() + T::one()) {
return None;
}
if target <= &T::from_u8(255).unwrap() || target < &T::from_u16(*SMALL_PRIMES.last().unwrap()).unwrap()
{
let next = match SMALL_PRIMES.binary_search(&target.to_u16().unwrap()) {
Ok(pos) => SMALL_PRIMES[pos - 1],
Err(pos) => SMALL_PRIMES[pos - 1],
};
return Some(T::from_u16(next).unwrap());
}
let mut i = (target % T::from_u16(WHEEL_SIZE).unwrap())
.to_u16()
.unwrap();
let mut t = target.clone();
loop {
let offset = WHEEL_PREV[i as usize];
t = t - T::from_u8(offset).unwrap();
i = i.subm(u16::from(offset), &WHEEL_SIZE);
if is_prime(&t, config).probably() {
break Some(t);
}
}
}
#[cfg(not(feature = "big-table"))]
pub fn prime_pi_est<T: Num + ToPrimitive + FromPrimitive>(target: &T) -> T {
let (lo, hi) = prime_pi_bounds(target);
(lo + hi) / T::from_u8(2).unwrap()
}
#[cfg(feature = "big-table")]
pub fn prime_pi_est<T: ToPrimitive + FromPrimitive>(target: &T) -> T {
if let Some(x) = target.to_u16() {
if x <= { *SMALL_PRIMES.last().unwrap() } {
let (lo, hi) = prime_pi_bounds(&x);
debug_assert_eq!(lo, hi);
return T::from_u16(lo).unwrap();
}
}
let lnln = target.to_f64().unwrap().ln().ln();
let mut total = 0f64;
let mut lnp = 0f64; let mut lnfac = 0f64;
for k in 1usize..100 {
lnp += lnln;
let lnk = (k as f64).ln();
lnfac += lnk;
let lnzeta = if k > 64 { 0f64 } else { ZETA_LOG_TABLE[k - 1] };
let t = lnp - lnk - lnfac - lnzeta;
if t < -4. {
break;
}
total += t.exp();
}
T::from_f64(total + 1f64).unwrap()
}
pub fn nth_prime_est<T: ToPrimitive + FromPrimitive + Num + PartialOrd>(target: &T) -> Option<T>
where
for<'r> &'r T: RefNum<T>,
{
let (mut lo, mut hi) = nth_prime_bounds(target)?;
if lo == hi {
return Some(lo);
}
while lo != &hi - T::from_u8(1).unwrap() {
let x = (&lo + &hi) / T::from_u8(2).unwrap();
let mid = prime_pi_est(&x);
if &mid < target {
lo = x;
} else if &mid > target {
hi = x;
} else {
return Some(x);
}
}
Some(lo)
}
#[cfg(test)]
mod tests {
use super::*;
use rand::{prelude::SliceRandom, random};
use std::iter::FromIterator;
#[test]
fn is_prime64_test() {
for x in 2..100 {
assert_eq!(SMALL_PRIMES.contains(&x), is_prime64(u64::from(x)));
}
assert!(is_prime64(677));
assert!(!is_prime64(9773));
assert!(!is_prime64(13357));
assert!(!is_prime64(18769));
assert!(is_prime64(6_469_693_333));
assert!(is_prime64(13_756_265_695_458_089_029));
assert!(is_prime64(13_496_181_268_022_124_907));
assert!(is_prime64(10_953_742_525_620_032_441));
assert!(is_prime64(17_908_251_027_575_790_097));
assert!(is_prime64(480_194_653));
assert!(!is_prime64(20_074_069));
assert!(is_prime64(8_718_775_377_449));
assert!(is_prime64(3_315_293_452_192_821_991));
assert!(!is_prime64(8_651_776_913_431));
assert!(!is_prime64(1_152_965_996_591_997_761));
assert!(!is_prime64(600_437_059_821_397));
assert!(!is_prime64(3_866_032_210_719_337));
assert!(!is_prime64(4_100_599_722_623_587));
let mut rng = rand::thread_rng();
for _ in 0..100 {
let x = random();
if !is_prime64(x) {
continue;
}
assert_ne!(x % u64::from(*SMALL_PRIMES.choose(&mut rng).unwrap()), 0);
}
for _ in 0..100 {
let x = u64::from(random::<u32>());
let y = u64::from(random::<u32>());
assert!(!is_prime64(x * y));
}
}
#[test]
fn factorize64_test() {
let fac4095 = BTreeMap::from_iter([(3, 2), (5, 1), (7, 1), (13, 1)]);
let fac = factorize64(4095);
assert_eq!(fac, fac4095);
let fac123456789 = BTreeMap::from_iter([(3, 2), (3803, 1), (3607, 1)]);
let fac = factorize64(123_456_789);
assert_eq!(fac, fac123456789);
let fac1_17 = BTreeMap::from_iter([(2_071_723, 1), (5_363_222_357, 1)]);
let fac = factorize64(11_111_111_111_111_111);
assert_eq!(fac, fac1_17);
for exp in 2u32..5 {
assert_eq!(
factorize128(8167u128.pow(exp)),
BTreeMap::from_iter([(8167, exp as usize)])
);
}
for _ in 0..100 {
let x = random();
let fac = factorize64(x);
let mut prod = 1;
for (p, exp) in fac {
assert!(
is_prime64(p),
"factorization result should have prime factors! (get {})",
p
);
prod *= p.pow(exp as u32);
}
assert_eq!(x, prod, "factorization check failed! ({x} != {prod})");
}
}
#[test]
fn factorize128_test() {
let fac_primorial19 =
BTreeMap::from_iter(SMALL_PRIMES.iter().take(19).map(|&p| (u128::from(p), 1)));
let fac = factorize128(7_858_321_551_080_267_055_879_090);
assert_eq!(fac, fac_primorial19);
let fac_smallbig = BTreeMap::from_iter([(167, 1), (2_417_851_639_229_258_349_412_369, 1)]);
let fac = factorize128(403_781_223_751_286_144_351_865_623);
assert_eq!(fac, fac_smallbig);
for exp in 5u32..10 {
assert_eq!(
factorize128(8167u128.pow(exp)),
BTreeMap::from_iter([(8167, exp as usize)])
);
}
for _ in 0..4 {
let x = random::<u128>() >> 28; let fac = factorize128(x);
let mut prod = 1;
for (p, exp) in fac {
assert!(
is_prime(&p, None).probably(),
"factorization result should have prime factors! (get {})",
p
);
prod *= p.pow(exp as u32);
}
assert_eq!(x, prod, "factorization check failed! ({x} != {prod})");
}
}
#[test]
fn is_safe_prime_test() {
let safe_primes = [
5u16, 7, 11, 23, 47, 59, 83, 107, 167, 179, 227, 263, 347, 359, 383, 467, 479, 503,
563, 587, 719, 839, 863, 887, 983, 1019, 1187, 1283, 1307, 1319, 1367, 1439, 1487,
1523, 1619, 1823, 1907,
];
for p in SMALL_PRIMES {
#[allow(clippy::unnecessary_cast)]
let p = p as u16;
if p > 1500 {
break;
}
assert_eq!(
is_safe_prime(&p).probably(),
safe_primes.iter().any(|v| &p == v)
);
}
}
#[test]
fn moebius_test() {
let mu20: [i8; 20] = [
1, -1, -1, 0, -1, 1, -1, 0, 0, 1, -1, 0, -1, 1, 1, 0, -1, 0, -1, 0,
];
for (i, &expected) in mu20.iter().enumerate() {
assert_eq!(moebius(&(i + 1)), expected, "moebius on {i}");
}
assert_eq!(moebius(&1024u32), 0);
assert_eq!(moebius(&(8081u32 * 8081)), 0);
let sphenic3: [u8; 20] = [
30, 42, 66, 70, 78, 102, 105, 110, 114, 130, 138, 154, 165, 170, 174, 182, 186, 190,
195, 222,
]; for &val in &sphenic3 {
assert_eq!(moebius(&val), -1i8, "moebius on {val}");
}
let sphenic5: [u16; 23] = [
2310, 2730, 3570, 3990, 4290, 4830, 5610, 6006, 6090, 6270, 6510, 6630, 7410, 7590,
7770, 7854, 8610, 8778, 8970, 9030, 9282, 9570, 9690,
]; for &val in sphenic5.iter().take(20) {
assert_eq!(moebius(&val), -1i8, "moebius on {val}");
}
}
#[test]
fn prime_pi_bounds_test() {
fn check(n: u64, pi: u64) {
let (lo, hi) = prime_pi_bounds(&n);
let est = prime_pi_est(&n);
assert!(
lo <= pi && pi <= hi,
"fail to satisfy {} <= pi({}) = {} <= {}",
lo,
n,
pi,
hi
);
assert!(lo <= est && est <= hi);
}
let mut pb = NaiveBuffer::new();
let mut last = 0;
for (i, p) in pb.primes(100_000).copied().enumerate() {
for j in last..p {
check(j, i as u64);
}
last = p;
}
let pow10_values = [
0,
4,
25,
168,
1229,
9592,
78498,
664_579,
5_761_455,
50_847_534,
455_052_511,
4_118_054_813,
37_607_912_018,
346_065_536_839,
3_204_941_750_802,
29_844_570_422_669,
279_238_341_033_925,
2_623_557_157_654_233,
];
for (exponent, gt) in pow10_values.iter().enumerate() {
let n = 10u64.pow(exponent as u32);
check(n, *gt);
}
}
#[test]
fn nth_prime_bounds_test() {
fn check(n: u64, p: u64) {
let (lo, hi) = super::nth_prime_bounds(&n).unwrap();
assert!(
lo <= p && p <= hi,
"fail to satisfy: {} <= {}-th prime = {} <= {}",
lo,
n,
p,
hi
);
let est = super::nth_prime_est(&n).unwrap();
assert!(lo <= est && est <= hi);
}
let mut pb = NaiveBuffer::new();
for (i, p) in pb.primes(100_000).copied().enumerate() {
check(i as u64 + 1, p);
}
let pow10_values = [
2,
29,
541,
7919,
104_729,
1_299_709,
15_485_863,
179_424_673,
2_038_074_743,
22_801_763_489,
252_097_800_623,
2_760_727_302_517,
29_996_224_275_833,
323_780_508_946_331,
3_475_385_758_524_527,
37_124_508_045_065_437,
];
for (exponent, nth_prime) in pow10_values.iter().enumerate() {
let n = 10u64.pow(exponent as u32);
check(n, *nth_prime);
}
}
#[test]
fn prev_next_test() {
assert_eq!(prev_prime(&2u32, None), None);
assert_eq!(prev_prime(&257u16, None), Some(251));
assert_eq!(next_prime(&251u16, None), Some(257));
assert_eq!(next_prime(&251u8, None), None);
assert_eq!(prev_prime(&8167u16, None), Some(8161));
assert_eq!(next_prime(&8161u16, None), Some(8167));
let twine_primes: [(u32, u32); 8] = [
(2, 3), (3, 5),
(5, 7),
(11, 13),
(17, 19),
(29, 31),
(41, 43),
(617, 619),
];
for (p1, p2) in twine_primes {
assert_eq!(prev_prime(&p2, None).unwrap(), p1);
assert_eq!(next_prime(&p1, None).unwrap(), p2);
}
let adj10_primes: [(u32, u32); 7] = [
(7, 11),
(97, 101),
(997, 1009),
(9973, 10007),
(99991, 100_003),
(999_983, 1_000_003),
(9_999_991, 10_000_019),
];
for (i, (p1, p2)) in adj10_primes.iter().enumerate() {
assert_eq!(prev_prime(p2, None).unwrap(), *p1);
assert_eq!(next_prime(p1, None).unwrap(), *p2);
let pow = 10u32.pow((i + 1) as u32);
assert_eq!(prev_prime(&pow, None).unwrap(), *p1);
assert_eq!(next_prime(&pow, None).unwrap(), *p2);
}
}
#[test]
fn factors_overflow_test() {
let problematic_number = 0x3ffffffffffffff8u64;
let (factors_map, cofactors) = factors(problematic_number, None);
assert!(!factors_map.is_empty(), "Should find at least some factors");
let mut product = 1u64;
for (factor, count) in &factors_map {
product = product
.checked_mul(factor.pow(*count as u32))
.expect("Product computation should not overflow");
}
if let Some(ref remaining) = cofactors {
for cofactor in remaining {
product = product
.checked_mul(*cofactor)
.expect("Product computation should not overflow");
}
}
assert_eq!(
product, problematic_number,
"Product of factors should equal original number"
);
}
}