use rug::{Integer, integer::IsPrime};
use std::collections::HashMap;
type PrimeCountCache = HashMap<(i64, i64), i64>;
pub fn is_prime(n: &Integer) -> bool {
if n < &2 {
return false;
}
if n < &1_000_000 {
let n_i64 = n.to_i64_wrapping();
return is_prime_small(n_i64);
}
matches!(n.is_probably_prime(25), IsPrime::Yes)
}
fn is_prime_small(n: i64) -> bool {
if n < 2 {
return false;
}
if n <= 3 {
return true;
}
if n % 2 == 0 || n % 3 == 0 {
return false;
}
let mut i = 5;
while i * i <= n {
if n % i == 0 || n % (i + 2) == 0 {
return false;
}
i += 6;
}
true
}
pub fn next_prime(n: &Integer) -> Integer {
if n < &2 {
return Integer::from(2);
}
let mut candidate = n.clone();
if candidate.is_even() {
candidate += 1;
}
loop {
if is_prime(&candidate) {
return candidate;
}
candidate += 2;
}
}
pub fn prev_prime(n: &Integer) -> Integer {
if n <= &2 {
return Integer::from(2);
}
let mut candidate = n.clone();
if candidate.is_even() {
candidate -= 1;
} else if is_prime(&candidate) {
return candidate;
} else {
candidate -= 2;
}
loop {
if candidate <= 2 {
return Integer::from(2);
}
if is_prime(&candidate) {
return candidate;
}
candidate -= 2;
}
}
pub fn nth_prime(n: u64) -> Integer {
assert!(n > 0, "n must be positive");
if n <= 10000 {
get_small_prime((n - 1) as usize)
} else {
let n_float = n as f64;
let ln_n = n_float.ln();
let ln_ln_n = ln_n.ln();
let estimate = (n_float * (ln_n + ln_ln_n)) as u64;
let mut candidate = Integer::from(estimate);
if candidate <= 0 {
candidate = Integer::from(2);
}
let upper = if n > 6 {
(n as f64 * (ln_n + ln_ln_n)) as u64 + 1000
} else {
estimate + 10
};
let mut count = prime_count(&candidate);
while count < n as i64 {
candidate = next_prime(&candidate);
count += 1;
if candidate > upper {
break;
}
}
candidate
}
}
fn get_small_prime(n: usize) -> Integer {
include!("small_primes.inc")[n].into()
}
pub fn prime_count(x: &Integer) -> i64 {
if x < &2 {
return 0;
}
let x_bits = x.significant_bits();
let x_i64 = if x_bits <= 63 {
x.to_i64_wrapping().abs()
} else {
return prime_count_approx(x);
};
if x_i64 <= 104_729 {
return count_primes_binary(x_i64);
}
let mut cache = PrimeCountCache::new();
legendre_phi(
x_i64,
prime_count_small(&Integer::from((x_i64 as f64).sqrt() as i64)),
&mut cache,
) + prime_count_small(&Integer::from((x_i64 as f64).sqrt() as i64))
- 1
}
fn prime_count_approx(x: &Integer) -> i64 {
if x < &2 {
return 0;
}
let x_f64 = x.to_f64();
if x_f64.is_finite() && x_f64 > 1.0 {
(x_f64 / x_f64.ln()) as i64
} else {
i64::MAX
}
}
fn count_primes_binary(x: i64) -> i64 {
let primes = include!("small_primes.inc");
match primes.binary_search(&x) {
Ok(i) => (i + 1) as i64,
Err(i) => i as i64,
}
}
fn prime_count_small(x: &Integer) -> i64 {
if x < &2 {
return 0;
}
let x_i64 = x.to_i64_wrapping().min(100000);
let mut sieve = vec![true; (x_i64 + 1) as usize];
sieve[0] = false;
if x_i64 >= 1 {
sieve[1] = false;
}
let mut i = 2;
while i * i <= x_i64 {
if sieve[i as usize] {
let mut j = i * i;
while j <= x_i64 {
sieve[j as usize] = false;
j += i;
}
}
i += 1;
}
sieve.iter().filter(|&&is_prime| is_prime).count() as i64
}
fn legendre_phi(x: i64, a: i64, cache: &mut PrimeCountCache) -> i64 {
if a == 1 {
return (x + 1) / 2;
}
let key = (x, a);
if let Some(&result) = cache.get(&key) {
return result;
}
let primes = include!("small_primes.inc");
let pa = if (a - 1) as usize >= primes.len() {
nth_prime(a as u64).to_i64_wrapping()
} else {
primes[(a - 1) as usize]
};
let result = legendre_phi(x, a - 1, cache) - legendre_phi(x / pa, a - 1, cache);
cache.insert(key, result);
result
}
pub fn primes_up_to(n: &Integer) -> Vec<Integer> {
if n < &2 {
return Vec::new();
}
let n_i64 = if let Some(v) = n.to_u64() {
if v > 10_000_000 {
return Vec::new();
}
v as usize
} else {
return Vec::new();
};
let mut sieve = vec![true; n_i64 + 1];
sieve[0] = false;
sieve[1] = false;
let mut i = 2;
while i * i <= n_i64 {
if sieve[i] {
let mut j = i * i;
while j <= n_i64 {
sieve[j] = false;
j += i;
}
}
i += 1;
}
sieve
.iter()
.enumerate()
.filter(|&(_, &is_prime)| is_prime)
.map(|(i, _)| Integer::from(i as u64))
.collect()
}
pub fn bernoulli(n: u64) -> Option<(i64, i64)> {
if n == 1 {
return Some((-1, 2));
}
if n == 0 {
return Some((1, 1));
}
if n % 2 == 1 && n > 1 {
return Some((0, 1));
}
let m = n as usize;
let mut a: Vec<(i64, i64)> = (0..=m).map(|j| (1, j as i64 + 1)).collect();
for m_curr in 1..=m {
for k in (1..=m_curr).rev() {
let (num1, den1) = a[k - 1];
let (num2, den2) = a[k];
let diff_num = num1 * den2 - num2 * den1;
let diff_den = den1 * den2;
let k_val = k as i64;
let new_num = k_val * diff_num;
let new_den = diff_den;
let g = gcd_int(new_num.abs(), new_den);
a[k - 1] = (new_num / g, new_den / g);
}
}
Some(a[0])
}
fn gcd_int(a: i64, b: i64) -> i64 {
if b == 0 { a } else { gcd_int(b, a % b) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_prime() {
assert!(is_prime(&Integer::from(2)));
assert!(is_prime(&Integer::from(3)));
assert!(is_prime(&Integer::from(17)));
assert!(is_prime(&Integer::from(7919))); assert!(!is_prime(&Integer::from(1)));
assert!(!is_prime(&Integer::from(4)));
assert!(!is_prime(&Integer::from(100)));
}
#[test]
fn test_next_prime() {
assert_eq!(next_prime(&Integer::from(10)), Integer::from(11));
assert_eq!(next_prime(&Integer::from(11)), Integer::from(11));
assert_eq!(next_prime(&Integer::from(14)), Integer::from(17));
assert_eq!(next_prime(&Integer::from(0)), Integer::from(2));
}
#[test]
fn test_prev_prime() {
assert_eq!(prev_prime(&Integer::from(10)), Integer::from(7));
assert_eq!(prev_prime(&Integer::from(7)), Integer::from(7));
assert_eq!(prev_prime(&Integer::from(2)), Integer::from(2));
}
#[test]
fn test_nth_prime() {
assert_eq!(nth_prime(1), 2);
assert_eq!(nth_prime(2), 3);
assert_eq!(nth_prime(3), 5);
assert_eq!(nth_prime(5), 11);
assert_eq!(nth_prime(10), 29);
}
#[test]
fn test_prime_count() {
assert_eq!(prime_count(&Integer::from(0)), 0);
assert_eq!(prime_count(&Integer::from(1)), 0);
assert_eq!(prime_count(&Integer::from(2)), 1);
assert_eq!(prime_count(&Integer::from(10)), 4);
assert_eq!(prime_count(&Integer::from(100)), 25);
}
#[test]
fn test_primes_up_to() {
let primes = primes_up_to(&Integer::from(20));
assert_eq!(primes, vec![2, 3, 5, 7, 11, 13, 17, 19]);
let primes_empty = primes_up_to(&Integer::from(1));
assert!(primes_empty.is_empty());
}
#[test]
fn test_bernoulli() {
assert_eq!(bernoulli(0), Some((1, 1)));
assert_eq!(bernoulli(1), Some((-1, 2)));
assert_eq!(bernoulli(2), Some((1, 6)));
assert_eq!(bernoulli(3), Some((0, 1)));
assert_eq!(bernoulli(4), Some((-1, 30)));
}
}