use std::sync::OnceLock;
use num_bigint::BigInt;
use num_integer::{Integer, Roots};
use num_rational::Ratio;
use num_traits::{One, Signed, ToPrimitive, Zero};
use rustc_hash::FxHashMap;
use crate::base::errors::SymplexError;
pub use crate::domains::combinatorics::{
PartitionIter, bell, binomial, catalan, derangements, npartitions, partitions,
};
#[inline]
fn mod_mul_u64(a: u64, b: u64, m: u64) -> u64 {
((a as u128 * b as u128) % m as u128) as u64
}
#[inline]
fn mod_add_u64(a: u64, b: u64, m: u64) -> u64 {
((a as u128 + b as u128) % m as u128) as u64
}
fn mod_pow_u64(base: u64, mut exp: u64, modulus: u64) -> u64 {
if modulus == 1 {
return 0;
}
let mut result: u128 = 1;
let m = modulus as u128;
let mut b = (base % modulus) as u128;
while exp > 0 {
if exp % 2 == 1 {
result = (result * b) % m;
}
exp /= 2;
b = (b * b) % m;
}
result as u64
}
fn gcd_u64(mut a: u64, mut b: u64) -> u64 {
while b != 0 {
let t = a % b;
a = b;
b = t;
}
a
}
struct XorShift(u64);
impl XorShift {
fn new(seed: u64) -> Self {
XorShift(seed.max(1) ^ 0x9E37_79B9_7F4A_7C15)
}
fn next_u64(&mut self) -> u64 {
let mut x = self.0;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.0 = x;
x.wrapping_mul(0x2545_F491_4F6C_DD1D)
}
fn next_big_below(&mut self, n: &BigInt) -> BigInt {
let bits = n.bits() as usize + 64;
let words = bits.div_ceil(64);
let mut acc = BigInt::zero();
for _ in 0..words {
acc = (acc << 64usize) + BigInt::from(self.next_u64());
}
acc.mod_floor(n)
}
}
const TRIAL_DIVISION_LIMIT: u32 = 1 << 16;
fn small_primes() -> &'static [u32] {
static TABLE: OnceLock<Vec<u32>> = OnceLock::new();
TABLE.get_or_init(|| sieve_u32(TRIAL_DIVISION_LIMIT))
}
fn sieve_u32(limit: u32) -> Vec<u32> {
if limit < 3 {
return if limit > 2 { vec![2] } else { vec![] };
}
let n = limit as usize;
let half = n / 2;
let mut composite = vec![false; half];
let mut primes = vec![2u32];
let mut i = 1usize;
while (2 * i + 1) * (2 * i + 1) < n {
if !composite[i] {
let p = 2 * i + 1;
let mut j = (p * p) / 2;
while j < half {
composite[j] = true;
j += p;
}
}
i += 1;
}
for (i, &c) in composite.iter().enumerate().skip(1) {
if !c {
primes.push((2 * i + 1) as u32);
}
}
primes
}
struct BitSieve {
limit: u64,
bits: Vec<u64>, }
impl BitSieve {
fn new(limit: u64) -> Self {
let half = (limit / 2 + 1) as usize;
let mut bits = vec![0u64; half.div_ceil(64)];
let mut i = 1u64;
while (2 * i + 1) * (2 * i + 1) <= limit {
if bits[(i / 64) as usize] & (1 << (i % 64)) == 0 {
let p = 2 * i + 1;
let mut j = (p * p) / 2;
while j < half as u64 {
bits[(j / 64) as usize] |= 1 << (j % 64);
j += p;
}
}
i += 1;
}
BitSieve { limit, bits }
}
fn is_prime(&self, k: u64) -> bool {
if k < 2 || k > self.limit {
return false;
}
if k == 2 {
return true;
}
if k.is_multiple_of(2) {
return false;
}
let i = k / 2;
self.bits[(i / 64) as usize] & (1 << (i % 64)) == 0
}
}
const DETERMINISTIC_WITNESSES: [u64; 13] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41];
fn miller_rabin(n: u64, witnesses: &[u64]) -> bool {
if n < 2 {
return false;
}
if n < 4 {
return true;
}
if n.is_multiple_of(2) {
return false;
}
let mut d = n - 1;
let mut r = 0u32;
while d.is_multiple_of(2) {
d /= 2;
r += 1;
}
'outer: for &a in witnesses {
let a = a % n;
if a == 0 {
continue;
}
let mut x = mod_pow_u64(a, d, n);
if x == 1 || x == n - 1 {
continue;
}
for _ in 0..r - 1 {
x = mod_mul_u64(x, x, n);
if x == n - 1 {
continue 'outer;
}
}
return false;
}
true
}
fn strong_fermat_big(n: &BigInt, base: &BigInt) -> bool {
let one = BigInt::one();
let two = BigInt::from(2);
let n_minus_1 = n - &one;
let mut d = n_minus_1.clone();
let mut r = 0u32;
while d.is_even() {
d /= &two;
r += 1;
}
let a = base.mod_floor(n);
if a.is_zero() {
return true;
}
let mut x = a.modpow(&d, n);
if x.is_one() || x == n_minus_1 {
return true;
}
for _ in 1..r {
x = (&x * &x) % n;
if x == n_minus_1 {
return true;
}
}
false
}
fn miller_rabin_big(n: &BigInt, witnesses: &[u64]) -> bool {
for &a in witnesses {
let a_big = BigInt::from(a);
if &a_big >= n {
continue;
}
if !strong_fermat_big(n, &a_big) {
return false;
}
}
true
}
fn strong_lucas_big(n: &BigInt) -> bool {
let mut d_i: i64 = 5;
let d = loop {
let d_big = BigInt::from(d_i);
match jacobi_big(&d_big, n) {
-1 => break d_big,
0 => {
return n.abs() == d_big.abs();
}
_ => {}
}
d_i = if d_i > 0 { -(d_i + 2) } else { -(d_i - 2) };
if d_i.abs() > 1_000_000 {
return false;
}
};
let p = BigInt::one();
let q = (BigInt::one() - &d) / BigInt::from(4);
let q = q.mod_floor(n);
let n_plus_1 = n + BigInt::one();
let mut k = n_plus_1.clone();
let mut s = 0u32;
while k.is_even() {
k /= 2;
s += 1;
}
let half = |x: BigInt| -> BigInt {
let x = x.mod_floor(n);
if x.is_even() { x / 2 } else { (x + n) / 2 }
};
let mut u = BigInt::one();
let mut v = p.clone();
let mut qk = q.clone();
let bits = k.bits();
for i in (0..bits - 1).rev() {
u = (&u * &v).mod_floor(n);
v = (&v * &v - BigInt::from(2) * &qk).mod_floor(n);
qk = (&qk * &qk).mod_floor(n);
if k.bit(i) {
let u_new = half(&p * &u + &v);
let v_new = half(&d * &u + &p * &v);
u = u_new;
v = v_new;
qk = (&qk * &q).mod_floor(n);
}
}
if u.is_zero() || v.is_zero() {
return true;
}
for _ in 1..s {
v = (&v * &v - BigInt::from(2) * &qk).mod_floor(n);
qk = (&qk * &qk).mod_floor(n);
if v.is_zero() {
return true;
}
}
false
}
fn bpsw_big(n: &BigInt) -> bool {
if !strong_fermat_big(n, &BigInt::from(2)) {
return false;
}
if is_square_big(n) {
return false;
}
strong_lucas_big(n)
}
fn extended_gcd_big(a: &BigInt, b: &BigInt) -> (BigInt, BigInt, BigInt) {
let (mut old_r, mut r) = (a.clone(), b.clone());
let (mut old_s, mut s) = (BigInt::one(), BigInt::zero());
let (mut old_t, mut t) = (BigInt::zero(), BigInt::one());
while !r.is_zero() {
let q = &old_r / &r;
let new_r = &old_r - &q * &r;
old_r = std::mem::replace(&mut r, new_r);
let new_s = &old_s - &q * &s;
old_s = std::mem::replace(&mut s, new_s);
let new_t = &old_t - &q * &t;
old_t = std::mem::replace(&mut t, new_t);
}
if old_r.is_negative() {
(-old_r, -old_s, -old_t)
} else {
(old_r, old_s, old_t)
}
}
fn isprime_u64(n: u64) -> bool {
if n < 2 {
return false;
}
for &p in &[2u64, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47] {
if n == p {
return true;
}
if n.is_multiple_of(p) {
return false;
}
}
miller_rabin(n, &DETERMINISTIC_WITNESSES)
}
fn isprime_i64(n: i64) -> bool {
if n < 2 {
return false;
}
isprime_u64(n as u64)
}
fn isprime_big_internal(n: &BigInt) -> bool {
if *n < BigInt::from(2) {
return false;
}
if let Some(u) = n.to_u64() {
return isprime_u64(u);
}
if n.is_even() {
return false;
}
for &p in small_primes().iter().take(200) {
if (n % p).is_zero() {
return false;
}
}
static BOUND: OnceLock<BigInt> = OnceLock::new();
let bound = BOUND.get_or_init(|| BigInt::from(33u64) * BigInt::from(10u64).pow(23));
if n < bound {
return miller_rabin_big(n, &DETERMINISTIC_WITNESSES);
}
bpsw_big(n)
}
fn pollard_brent_u64(n: u64, seed: u64) -> Option<u64> {
if n.is_multiple_of(2) {
return Some(2);
}
let c = 1 + seed % (n - 1);
let f = |x: u64| mod_add_u64(mod_mul_u64(x, x, n), c, n);
let mut y = (seed.wrapping_mul(6364136223846793005).wrapping_add(1)) % n;
let mut x = y;
let mut ys = y;
let mut g = 1u64;
let mut q = 1u64;
let mut r = 1u64;
let m = 128u64;
while g == 1 {
x = y;
for _ in 0..r {
y = f(y);
}
let mut k = 0u64;
while k < r && g == 1 {
ys = y;
let lim = m.min(r - k);
for _ in 0..lim {
y = f(y);
q = mod_mul_u64(q, x.abs_diff(y), n);
}
g = gcd_u64(q, n);
k += lim;
}
r *= 2;
if r > (1u64 << 40) {
return None;
}
}
if g == n {
loop {
ys = f(ys);
g = gcd_u64(x.abs_diff(ys), n);
if g > 1 {
break;
}
}
}
if g == n { None } else { Some(g) }
}
fn factor_u64_no_small(n: u64, out: &mut Vec<u64>) {
let mut stack = vec![n];
while let Some(m) = stack.pop() {
if m == 1 {
continue;
}
if isprime_u64(m) {
out.push(m);
continue;
}
let s = m.sqrt();
if s * s == m {
stack.push(s);
stack.push(s);
continue;
}
let mut seed = 1u64;
let d = loop {
if let Some(d) = pollard_brent_u64(m, seed) {
break d;
}
seed += 1;
};
stack.push(d);
stack.push(m / d);
}
}
fn factorint_i64(n: i64) -> Vec<(i64, u32)> {
if n == 0 {
return vec![];
}
let mut n = n.unsigned_abs();
let mut factors: Vec<(i64, u32)> = Vec::new();
for &p in small_primes() {
let p = p as u64;
if p * p > n {
break;
}
if n.is_multiple_of(p) {
let mut count = 0u32;
while n.is_multiple_of(p) {
n /= p;
count += 1;
}
factors.push((p as i64, count));
}
}
if n > 1 {
if n < (TRIAL_DIVISION_LIMIT as u64) * (TRIAL_DIVISION_LIMIT as u64) {
factors.push((n as i64, 1));
} else {
let mut rest = Vec::new();
factor_u64_no_small(n, &mut rest);
rest.sort_unstable();
for p in rest {
match factors.last_mut() {
Some((q, e)) if *q as u64 == p => *e += 1,
_ => factors.push((p as i64, 1)),
}
}
}
}
factors.sort_by_key(|(p, _)| *p);
factors
}
trait ModRing {
type El: Clone + PartialEq;
fn embed(&self, x: &BigInt) -> Self::El;
fn add(&self, a: &Self::El, b: &Self::El) -> Self::El;
fn sub(&self, a: &Self::El, b: &Self::El) -> Self::El;
fn mul(&self, a: &Self::El, b: &Self::El) -> Self::El;
fn is_zero(&self, a: &Self::El) -> bool;
fn gcd_with_n(&self, a: &Self::El) -> BigInt;
}
struct BigRing {
n: BigInt,
}
impl ModRing for BigRing {
type El = BigInt;
fn embed(&self, x: &BigInt) -> BigInt {
x.mod_floor(&self.n)
}
fn add(&self, a: &BigInt, b: &BigInt) -> BigInt {
let s = a + b;
if s >= self.n { s - &self.n } else { s }
}
fn sub(&self, a: &BigInt, b: &BigInt) -> BigInt {
if a >= b { a - b } else { a + &self.n - b }
}
fn mul(&self, a: &BigInt, b: &BigInt) -> BigInt {
(a * b) % &self.n
}
fn is_zero(&self, a: &BigInt) -> bool {
a.is_zero()
}
fn gcd_with_n(&self, a: &BigInt) -> BigInt {
a.gcd(&self.n)
}
}
struct Mont128 {
n: u128,
n_inv_neg: u128,
r2: u128,
}
#[inline]
fn mul_wide_u128(a: u128, b: u128) -> (u128, u128) {
const M64: u128 = (1u128 << 64) - 1;
let (a1, a0) = (a >> 64, a & M64);
let (b1, b0) = (b >> 64, b & M64);
let p00 = a0 * b0;
let p01 = a0 * b1;
let p10 = a1 * b0;
let p11 = a1 * b1;
let (mid, mid_carry) = p01.overflowing_add(p10);
let (lo, lo_carry) = p00.overflowing_add(mid << 64);
let hi = p11 + (mid >> 64) + ((mid_carry as u128) << 64) + (lo_carry as u128);
(hi, lo)
}
fn gcd_u128(mut a: u128, mut b: u128) -> u128 {
while b != 0 {
let t = a % b;
a = b;
b = t;
}
a
}
impl Mont128 {
fn new(n: u128) -> Self {
debug_assert!(n % 2 == 1 && n < (1u128 << 127));
let mut inv: u128 = 1;
for _ in 0..7 {
inv = inv.wrapping_mul(2u128.wrapping_sub(n.wrapping_mul(inv)));
}
let n_inv_neg = 0u128.wrapping_sub(inv);
let nb = BigInt::from(n);
let r2 = ((BigInt::one() << 256usize) % &nb).to_u128().unwrap_or(0);
Mont128 { n, n_inv_neg, r2 }
}
#[inline]
fn redc(&self, hi: u128, lo: u128) -> u128 {
let m = lo.wrapping_mul(self.n_inv_neg);
let (mn_hi, mn_lo) = mul_wide_u128(m, self.n);
let (_, carry) = lo.overflowing_add(mn_lo);
let t = hi + mn_hi + carry as u128;
if t >= self.n { t - self.n } else { t }
}
#[inline]
fn mont_mul(&self, a: u128, b: u128) -> u128 {
let (hi, lo) = mul_wide_u128(a, b);
self.redc(hi, lo)
}
fn to_mont(&self, x: u128) -> u128 {
self.mont_mul(x % self.n, self.r2)
}
fn mont_to_plain(&self, x: u128) -> u128 {
self.redc(0, x)
}
}
impl ModRing for Mont128 {
type El = u128;
fn embed(&self, x: &BigInt) -> u128 {
let r = x.mod_floor(&BigInt::from(self.n)).to_u128().unwrap_or(0);
self.to_mont(r)
}
#[inline]
fn add(&self, a: &u128, b: &u128) -> u128 {
let s = a + b;
if s >= self.n { s - self.n } else { s }
}
#[inline]
fn sub(&self, a: &u128, b: &u128) -> u128 {
if a >= b { a - b } else { a + self.n - b }
}
#[inline]
fn mul(&self, a: &u128, b: &u128) -> u128 {
self.mont_mul(*a, *b)
}
fn is_zero(&self, a: &u128) -> bool {
*a == 0
}
fn gcd_with_n(&self, a: &u128) -> BigInt {
BigInt::from(gcd_u128(self.mont_to_plain(*a), self.n))
}
}
fn pollard_brent_ring<R: ModRing>(
ring: &R,
n: &BigInt,
seed: u64,
max_iters: u64,
) -> Option<BigInt> {
let c = ring.embed(&BigInt::from(1 + seed));
let f = |x: &R::El| -> R::El { ring.add(&ring.mul(x, x), &c) };
let mut rng = XorShift::new(seed.wrapping_mul(0x1234_5678_9ABC_DEF1));
let mut y = ring.embed(&rng.next_big_below(n));
let mut x = y.clone();
let mut ys = y.clone();
let one = ring.embed(&BigInt::one());
let mut g = BigInt::one();
let mut q = one.clone();
let mut r = 1u64;
let m = 256u64;
let mut iters = 0u64;
while g.is_one() {
x = y.clone();
for _ in 0..r {
y = f(&y);
}
let mut k = 0u64;
while k < r && g.is_one() {
ys = y.clone();
let lim = m.min(r - k);
for _ in 0..lim {
y = f(&y);
q = ring.mul(&q, &ring.sub(&x, &y));
}
g = ring.gcd_with_n(&q);
k += lim;
iters += lim;
}
r *= 2;
if iters > max_iters {
return None;
}
}
if &g == n {
let mut extra = 0u64;
loop {
ys = f(&ys);
g = ring.gcd_with_n(&ring.sub(&x, &ys));
if g > BigInt::one() {
break;
}
extra += 1;
if extra > max_iters {
return None;
}
}
}
if &g == n || g.is_one() { None } else { Some(g) }
}
fn ecm_stage1_ring<R: ModRing>(ring: &R, n: &BigInt, sigma: u64, b1: u64) -> Option<BigInt> {
let sigma = BigInt::from(sigma);
let u = (&sigma * &sigma - BigInt::from(5)).mod_floor(n);
let v = (BigInt::from(4) * &sigma).mod_floor(n);
let u3 = (&u * &u * &u).mod_floor(n);
let v3 = (&v * &v * &v).mod_floor(n);
let vmu = (&v - &u).mod_floor(n);
let numer = (&vmu * &vmu * &vmu * (BigInt::from(3) * &u + &v)).mod_floor(n);
let denom = (BigInt::from(16) * &u3 * &v).mod_floor(n);
let g = denom.gcd(n);
if !g.is_one() {
return if &g == n { None } else { Some(g) };
}
let (_, inv, _) = extended_gcd_big(&denom, n);
let a24 = ring.embed(&(numer * inv).mod_floor(n));
let mut x = ring.embed(&u3);
let mut z = ring.embed(&v3);
let dbl = |x: &R::El, z: &R::El| -> (R::El, R::El) {
let s = ring.add(x, z);
let d = ring.sub(x, z);
let t1 = ring.mul(&s, &s);
let t2 = ring.mul(&d, &d);
let diff = ring.sub(&t1, &t2);
let x2 = ring.mul(&t1, &t2);
let z2 = ring.mul(&diff, &ring.add(&t2, &ring.mul(&a24, &diff)));
(x2, z2)
};
let dadd = |x1: &R::El, z1: &R::El, x2: &R::El, z2: &R::El, x0: &R::El, z0: &R::El| {
let a = ring.mul(&ring.sub(x1, z1), &ring.add(x2, z2));
let b = ring.mul(&ring.add(x1, z1), &ring.sub(x2, z2));
let s = ring.add(&a, &b);
let d = ring.sub(&a, &b);
let x3 = ring.mul(z0, &ring.mul(&s, &s));
let z3 = ring.mul(x0, &ring.mul(&d, &d));
(x3, z3)
};
let ladder = |k: u64, x: &R::El, z: &R::El| -> (R::El, R::El) {
let (mut x1, mut z1) = (x.clone(), z.clone());
let (mut x2, mut z2) = dbl(x, z);
let bits = 64 - k.leading_zeros();
for i in (0..bits - 1).rev() {
if (k >> i) & 1 == 1 {
let (nx1, nz1) = dadd(&x1, &z1, &x2, &z2, x, z);
let (nx2, nz2) = dbl(&x2, &z2);
x1 = nx1;
z1 = nz1;
x2 = nx2;
z2 = nz2;
} else {
let (nx2, nz2) = dadd(&x1, &z1, &x2, &z2, x, z);
let (nx1, nz1) = dbl(&x1, &z1);
x1 = nx1;
z1 = nz1;
x2 = nx2;
z2 = nz2;
}
}
(x1, z1)
};
for &p in small_primes() {
let p = p as u64;
if p > b1 {
break;
}
let mut pk = p;
while pk * p <= b1 {
pk *= p;
}
let (nx, nz) = ladder(pk, &x, &z);
x = nx;
z = nz;
if ring.is_zero(&z) {
return None;
}
}
if b1 > TRIAL_DIVISION_LIMIT as u64 {
let sieve = BitSieve::new(b1);
let mut p = TRIAL_DIVISION_LIMIT as u64 + 1;
while p <= b1 {
if sieve.is_prime(p) {
let (nx, nz) = ladder(p, &x, &z);
x = nx;
z = nz;
}
p += 2;
}
}
let g = ring.gcd_with_n(&z);
if g.is_one() || &g == n { None } else { Some(g) }
}
const ECM_SCHEDULE: [(u64, u64); 4] = [(2_000, 40), (11_000, 120), (50_000, 400), (250_000, 1_000)];
fn split_with_ring<R: ModRing>(ring: &R, n: &BigInt) -> BigInt {
for seed in 1..=3u64 {
if let Some(d) = pollard_brent_ring(ring, n, seed, 1 << 14) {
return d;
}
}
let mut sigma = 6u64;
for (b1, curves) in ECM_SCHEDULE {
for _ in 0..curves {
if let Some(d) = ecm_stage1_ring(ring, n, sigma, b1) {
return d;
}
sigma += 1;
}
}
let mut seed = 10u64;
loop {
if let Some(d) = pollard_brent_ring(ring, n, seed, u64::MAX) {
return d;
}
seed += 1;
}
}
fn split_big_composite(n: &BigInt) -> BigInt {
if let Some((base, _)) = perfect_power_big(n) {
return base;
}
if n.is_even() {
return BigInt::from(2);
}
match n.to_u128() {
Some(nu) if nu < (1u128 << 127) => split_with_ring(&Mont128::new(nu), n),
_ => split_with_ring(&BigRing { n: n.clone() }, n),
}
}
fn factorint_big_internal(n: &BigInt) -> Vec<(BigInt, u32)> {
if n.is_zero() {
return vec![];
}
let mut n = n.abs();
if n <= BigInt::one() {
return vec![];
}
let mut factors: Vec<(BigInt, u32)> = Vec::new();
for &p in small_primes() {
let pb = BigInt::from(p);
if &pb * &pb > n {
break;
}
if (&n % p).is_zero() {
let mut count = 0u32;
while (&n % p).is_zero() {
n /= p;
count += 1;
}
factors.push((pb, count));
}
}
let mut primes_found: Vec<BigInt> = Vec::new();
let mut stack: Vec<BigInt> = vec![n];
while let Some(m) = stack.pop() {
if m.is_one() {
continue;
}
if let Some(u) = m.to_u64() {
if u < (TRIAL_DIVISION_LIMIT as u64) * (TRIAL_DIVISION_LIMIT as u64) {
primes_found.push(m);
} else {
let mut out = Vec::new();
factor_u64_no_small(u, &mut out);
primes_found.extend(out.into_iter().map(BigInt::from));
}
continue;
}
if isprime_big_internal(&m) {
primes_found.push(m);
continue;
}
let d = split_big_composite(&m);
let q = &m / &d;
stack.push(d);
stack.push(q);
}
primes_found.sort();
for p in primes_found {
match factors.last_mut() {
Some((q, e)) if *q == p => *e += 1,
_ => factors.push((p, 1)),
}
}
factors.sort_by(|a, b| a.0.cmp(&b.0));
factors
}
fn nextprime_i64(n: i64) -> Option<i64> {
if n < 2 {
return Some(2);
}
let mut candidate = if n % 2 == 0 {
n.checked_add(1)?
} else {
n.checked_add(2)?
};
while !isprime_i64(candidate) {
candidate = candidate.checked_add(2)?;
}
Some(candidate)
}
fn prevprime_i64(n: i64) -> Option<i64> {
if n <= 2 {
return None;
}
if n == 3 {
return Some(2);
}
let mut candidate = if n % 2 == 0 { n - 1 } else { n - 2 };
while candidate >= 2 && !isprime_i64(candidate) {
candidate -= 2;
}
if candidate >= 2 {
Some(candidate)
} else {
None
}
}
fn isqrt_i64(n: i64) -> Option<i64> {
if n < 0 {
return None;
}
Some((n as u64).sqrt() as i64)
}
fn isqrt_big(n: &BigInt) -> Option<BigInt> {
if n.is_negative() {
return None;
}
Some(n.sqrt())
}
fn is_square_big(n: &BigInt) -> bool {
if n.is_negative() {
return false;
}
let s = n.sqrt();
&s * &s == *n
}
fn perfect_power_big(n: &BigInt) -> Option<(BigInt, u32)> {
let negative = n.is_negative();
let m = n.abs();
if m < BigInt::from(2) {
return None;
}
let max_exp = m.bits() as u32; let mut result: Option<(BigInt, u32)> = None;
for &q in small_primes() {
if q > max_exp {
break;
}
if negative && q == 2 {
continue;
}
let r = m.nth_root(q);
if r.pow(q) == m {
let base = if negative { -r } else { r };
let (b, e) = perfect_power_big(&base).unwrap_or((base, 1));
result = Some((b, e * q));
break;
}
}
result
}
fn jacobi_big(a: &BigInt, n: &BigInt) -> i8 {
debug_assert!(n.is_positive() && n.is_odd());
let mut a = a.mod_floor(n);
let mut n = n.clone();
let mut result: i8 = 1;
let three = BigInt::from(3);
let five = BigInt::from(5);
let eight = BigInt::from(8);
let four = BigInt::from(4);
while !a.is_zero() {
while a.is_even() {
a /= 2;
let r = n.mod_floor(&eight);
if r == three || r == five {
result = -result;
}
}
std::mem::swap(&mut a, &mut n);
if a.mod_floor(&four) == three && n.mod_floor(&four) == three {
result = -result;
}
a = a.mod_floor(&n);
}
if n.is_one() { result } else { 0 }
}
pub fn isprime(n: impl Into<BigInt>) -> bool {
let n: BigInt = n.into();
if let Some(n_i64) = n.to_i64() {
return isprime_i64(n_i64);
}
isprime_big_internal(&n)
}
pub fn is_probable_prime(n: impl Into<BigInt>, rounds: u32) -> bool {
let n: BigInt = n.into();
if n < BigInt::from(2) {
return false;
}
if n.to_u64().is_some() {
return isprime(n);
}
if n.is_even() {
return false;
}
if !strong_fermat_big(&n, &BigInt::from(2)) {
return false;
}
let seed = n.to_u64_digits().1.first().copied().unwrap_or(1);
let mut rng = XorShift::new(seed);
let n_minus_2 = &n - BigInt::from(2);
for _ in 0..rounds {
let a = rng.next_big_below(&n_minus_2) + BigInt::from(2);
if !strong_fermat_big(&n, &a) {
return false;
}
}
true
}
pub fn factorint(n: impl Into<BigInt>) -> Vec<(BigInt, u32)> {
let n: BigInt = n.into();
if let Some(n_i64) = n.to_i64() {
return factorint_i64(n_i64)
.into_iter()
.map(|(p, e)| (BigInt::from(p), e))
.collect();
}
factorint_big_internal(&n)
}
pub fn factorint_bounded(n: &BigInt, max_bits: u64) -> (Vec<(BigInt, u32)>, BigInt) {
let mut n = n.abs();
if n <= BigInt::one() {
return (vec![], BigInt::one());
}
let mut factors: Vec<(BigInt, u32)> = Vec::new();
if let Some(mut small) = n.to_u64() {
for &p in small_primes() {
let p = p as u64;
if p * p > small {
break;
}
if small.is_multiple_of(p) {
let mut count = 0u32;
while small.is_multiple_of(p) {
small /= p;
count += 1;
}
factors.push((BigInt::from(p), count));
}
}
n = BigInt::from(small);
} else {
for &p in small_primes() {
if (&n % p).is_zero() {
let mut count = 0u32;
while (&n % p).is_zero() {
n /= p;
count += 1;
}
factors.push((BigInt::from(p), count));
}
}
}
if n.is_one() {
return (factors, n);
}
if n < BigInt::from((TRIAL_DIVISION_LIMIT as u64) * (TRIAL_DIVISION_LIMIT as u64)) {
merge_factors(&mut factors, vec![(n, 1)]);
return (factors, BigInt::one());
}
if n.bits() <= max_bits {
merge_factors(&mut factors, factorint(n));
return (factors, BigInt::one());
}
if isprime_big_internal(&n) {
merge_factors(&mut factors, vec![(n, 1)]);
return (factors, BigInt::one());
}
if let Some((base, e)) = perfect_power_big(&n) {
let (inner, cof) = factorint_bounded(&base, max_bits);
merge_factors(
&mut factors,
inner.into_iter().map(|(p, k)| (p, k * e)).collect(),
);
return (factors, cof.pow(e));
}
(factors, n)
}
fn merge_factors(factors: &mut Vec<(BigInt, u32)>, more: Vec<(BigInt, u32)>) {
for (p, e) in more {
match factors.iter_mut().find(|(q, _)| *q == p) {
Some((_, k)) => *k += e,
None => factors.push((p, e)),
}
}
factors.sort_by(|a, b| a.0.cmp(&b.0));
}
pub fn nextprime(n: impl Into<BigInt>) -> BigInt {
let n: BigInt = n.into();
if let Some(n_i64) = n.to_i64() {
if n_i64 <= i64::MAX - 1000
&& let Some(result) = nextprime_i64(n_i64)
{
return BigInt::from(result);
}
}
let two = BigInt::from(2);
if n < two {
return two;
}
let mut candidate = if n.is_even() { &n + 1 } else { &n + 2 };
while !isprime_big_internal(&candidate) {
candidate += &two;
}
candidate
}
pub fn prevprime(n: impl Into<BigInt>) -> Option<BigInt> {
let n: BigInt = n.into();
if let Some(n_i64) = n.to_i64() {
return prevprime_i64(n_i64).map(BigInt::from);
}
let two = BigInt::from(2);
let mut candidate = if n.is_even() { &n - 1 } else { &n - 2 };
while candidate >= two && !isprime_big_internal(&candidate) {
candidate -= &two;
}
if candidate >= two {
Some(candidate)
} else {
None
}
}
pub fn prime(n: impl Into<BigInt>) -> Option<BigInt> {
let n: BigInt = n.into();
let n = n.to_u64()?;
if n == 0 || n > 10_000_000 {
return None;
}
if n < 6 {
return Some(BigInt::from([2u64, 3, 5, 7, 11][n as usize - 1]));
}
let nf = n as f64;
let bound = (nf * (nf.ln() + nf.ln().ln())).ceil() as u64 + 10;
let sieve = BitSieve::new(bound);
let mut count = 0u64;
let mut k = 2u64;
while k <= bound {
if sieve.is_prime(k) {
count += 1;
if count == n {
return Some(BigInt::from(k));
}
}
k += if k == 2 { 1 } else { 2 };
}
None
}
pub fn primepi(n: impl Into<BigInt>) -> Option<u64> {
let n: BigInt = n.into();
if n < BigInt::from(2) {
return Some(0);
}
let n = n.to_u64()?;
if n > 1_000_000_000_000 {
return None;
}
Some(primepi_u64(n))
}
fn primepi_u64(n: u64) -> u64 {
if n < 2 {
return 0;
}
let r = n.sqrt();
let rs = r as usize;
let mut lo: Vec<u64> = (0..=rs).map(|v| v.saturating_sub(1) as u64).collect();
let mut hi: Vec<u64> = (0..=rs)
.map(|i| if i == 0 { 0 } else { n / i as u64 - 1 })
.collect();
for p in 2..=rs {
if lo[p] == lo[p - 1] {
continue; }
let sp = lo[p - 1];
let p2 = (p * p) as u64;
let pu = p as u64;
let i_max = (n / p2).min(r) as usize;
for i in 1..=i_max {
let ip = i as u64 * pu;
let sub = if ip <= r {
hi[ip as usize]
} else {
lo[(n / ip) as usize]
};
hi[i] -= sub - sp;
}
let mut v = rs;
while v as u64 >= p2 {
lo[v] -= lo[v / p] - sp;
v -= 1;
}
}
hi[1]
}
pub fn primerange(a: impl Into<BigInt>, b: impl Into<BigInt>) -> Vec<BigInt> {
let a: BigInt = a.into();
let b: BigInt = b.into();
let a = if a < BigInt::from(2) {
BigInt::from(2)
} else {
a
};
if a >= b {
return vec![];
}
let width = &b - &a;
if let (Some(lo), Some(hi)) = (a.to_u64(), b.to_u64())
&& hi <= 100_000_000_000_000
&& width <= BigInt::from(10_000_000u64)
{
return segmented_sieve(lo, hi)
.into_iter()
.map(BigInt::from)
.collect();
}
let mut out = Vec::new();
let mut p = if isprime_big_internal(&a) {
a.clone()
} else {
nextprime(a)
};
while p < b {
out.push(p.clone());
p = nextprime(p);
}
out
}
fn segmented_sieve(lo: u64, hi: u64) -> Vec<u64> {
if hi <= 2 || lo >= hi {
return vec![];
}
let root = (hi - 1).sqrt() + 1;
let base_primes: Vec<u64> = sieve_u32((root + 1).min(u32::MAX as u64) as u32)
.into_iter()
.map(u64::from)
.collect();
let len = (hi - lo) as usize;
let mut composite = vec![false; len];
for &p in &base_primes {
if p * p >= hi {
break;
}
let mut start = lo.div_ceil(p) * p;
if start < p * p {
start = p * p;
}
let mut m = start;
while m < hi {
composite[(m - lo) as usize] = true;
m += p;
}
}
(0..len)
.filter(|&i| {
let v = lo + i as u64;
v >= 2 && !composite[i]
})
.map(|i| lo + i as u64)
.collect()
}
pub fn divisors(n: impl Into<BigInt>) -> Vec<BigInt> {
let n: BigInt = n.into();
if n.is_zero() {
return vec![];
}
let n_abs = n.abs();
let factors = factorint(n_abs);
let mut divs = vec![BigInt::one()];
for (p, e) in factors {
let current_len = divs.len();
let mut power = BigInt::one();
for _ in 0..e {
power *= &p;
for j in 0..current_len {
divs.push(&divs[j] * &power);
}
}
}
divs.sort();
divs
}
pub fn divisor_count(n: impl Into<BigInt>) -> usize {
let n: BigInt = n.into();
if n.is_zero() {
return 0;
}
let factors = factorint(n.abs());
factors.iter().map(|(_, e)| (*e + 1) as usize).product()
}
pub fn divisor_sum(n: impl Into<BigInt>) -> BigInt {
divisor_sigma(n, 1)
}
pub fn divisor_sigma(n: impl Into<BigInt>, k: u32) -> BigInt {
let n: BigInt = n.into();
if n.is_zero() {
return BigInt::zero();
}
let mut result = BigInt::one();
for (p, e) in factorint(n.abs()) {
let pk = p.pow(k);
let mut term = BigInt::one();
let mut acc = BigInt::one();
for _ in 0..e {
acc *= &pk;
term += &acc;
}
result *= term;
}
result
}
pub fn is_perfect(n: impl Into<BigInt>) -> bool {
let n: BigInt = n.into();
n.is_positive() && divisor_sigma(n.clone(), 1) == &n * 2
}
pub fn is_abundant(n: impl Into<BigInt>) -> bool {
let n: BigInt = n.into();
n.is_positive() && divisor_sigma(n.clone(), 1) > &n * 2
}
pub fn is_deficient(n: impl Into<BigInt>) -> bool {
let n: BigInt = n.into();
n.is_positive() && divisor_sigma(n.clone(), 1) < &n * 2
}
pub fn totient(n: impl Into<BigInt>) -> BigInt {
let n: BigInt = n.into();
if n <= BigInt::zero() {
return BigInt::zero();
}
let factors = factorint(n.clone());
let mut result = n;
for (p, _) in factors {
result = &result / &p * (&p - BigInt::one());
}
result
}
pub fn carmichael_lambda(n: impl Into<BigInt>) -> BigInt {
let n: BigInt = n.into();
if n <= BigInt::zero() {
return BigInt::zero();
}
let mut result = BigInt::one();
for (p, e) in factorint(n) {
let lam = if p == BigInt::from(2) {
match e {
1 => BigInt::one(),
2 => BigInt::from(2),
_ => BigInt::one() << (e - 2) as usize,
}
} else {
p.pow(e - 1) * (&p - BigInt::one())
};
result = result.lcm(&lam);
}
result
}
pub fn mobius(n: impl Into<BigInt>) -> i8 {
let n: BigInt = n.into();
if n <= BigInt::zero() {
return 0;
}
let factors = factorint(n);
for (_, e) in &factors {
if *e > 1 {
return 0;
}
}
if factors.len().is_multiple_of(2) {
1
} else {
-1
}
}
pub fn perfect_power(n: impl Into<BigInt>) -> Option<(BigInt, u32)> {
let n: BigInt = n.into();
perfect_power_big(&n)
}
pub fn is_perfect_power(n: impl Into<BigInt>) -> bool {
perfect_power(n).is_some()
}
pub fn is_mersenne_prime(p: impl Into<BigInt>) -> bool {
let p: BigInt = p.into();
let Some(p) = p.to_u64() else {
return false;
};
if !isprime_u64(p) {
return false;
}
if p == 2 {
return true;
}
let m = (BigInt::one() << p as usize) - BigInt::one();
let mut s = BigInt::from(4);
for _ in 0..(p - 2) {
s = (&s * &s - BigInt::from(2)) % &m;
}
s.is_zero()
}
pub fn mod_inverse(a: impl Into<BigInt>, n: impl Into<BigInt>) -> Option<BigInt> {
let a: BigInt = a.into();
let n: BigInt = n.into();
if n <= BigInt::one() {
return None;
}
let (g, x, _) = extended_gcd_big(&a, &n);
if !g.is_one() {
return None;
}
Some(x.mod_floor(&n))
}
pub fn crt(remainders: &[BigInt], moduli: &[BigInt]) -> Option<BigInt> {
if remainders.len() != moduli.len() || remainders.is_empty() {
return None;
}
if moduli.iter().any(|m| !m.is_positive()) {
return None;
}
let mut result = remainders[0].mod_floor(&moduli[0]);
let mut modulus = moduli[0].clone();
for i in 1..remainders.len() {
let (g, p, _) = extended_gcd_big(&modulus, &moduli[i]);
let diff = &remainders[i] - &result;
if !(&diff % &g).is_zero() {
return None;
}
let step = &modulus * ((&diff / &g).mod_floor(&(&moduli[i] / &g))) * &p;
result = &result + &step;
modulus = &modulus / &g * &moduli[i];
result = result.mod_floor(&modulus);
}
Some(result)
}
pub fn crt_i64(remainders: &[i64], moduli: &[i64]) -> Option<i64> {
let r: Vec<BigInt> = remainders.iter().map(|&r| BigInt::from(r)).collect();
let m: Vec<BigInt> = moduli.iter().map(|&m| BigInt::from(m)).collect();
crt(&r, &m).and_then(|x| (&x).try_into().ok())
}
pub fn gcd(a: impl Into<BigInt>, b: impl Into<BigInt>) -> BigInt {
let a: BigInt = a.into();
let b: BigInt = b.into();
num_integer::Integer::gcd(&a, &b)
}
pub fn gcdex(a: impl Into<BigInt>, b: impl Into<BigInt>) -> (BigInt, BigInt, BigInt) {
let a: BigInt = a.into();
let b: BigInt = b.into();
extended_gcd_big(&a, &b)
}
pub fn lcm(a: impl Into<BigInt>, b: impl Into<BigInt>) -> BigInt {
let a: BigInt = a.into();
let b: BigInt = b.into();
num_integer::Integer::lcm(&a, &b)
}
pub fn is_coprime(a: impl Into<BigInt>, b: impl Into<BigInt>) -> bool {
gcd(a, b).is_one()
}
pub fn mod_pow(
base: impl Into<BigInt>,
exp: impl Into<BigInt>,
modulus: impl Into<BigInt>,
) -> BigInt {
let base: BigInt = base.into();
let exp: BigInt = exp.into();
let modulus: BigInt = modulus.into();
if modulus <= BigInt::zero() || exp < BigInt::zero() {
return BigInt::zero();
}
let base_mod = base.mod_floor(&modulus);
base_mod.modpow(&exp, &modulus)
}
pub fn is_square(n: impl Into<BigInt>) -> bool {
let n: BigInt = n.into();
is_square_big(&n)
}
pub fn isqrt(n: impl Into<BigInt>) -> Option<BigInt> {
let n: BigInt = n.into();
isqrt_dispatch(&n)
}
fn isqrt_dispatch(n: &BigInt) -> Option<BigInt> {
if let Some(n_i64) = n.to_i64() {
return isqrt_i64(n_i64).map(BigInt::from);
}
isqrt_big(n)
}
pub fn iroot(n: impl Into<BigInt>, k: u32) -> Option<BigInt> {
let n: BigInt = n.into();
if k == 0 {
return None;
}
if n.is_negative() {
if k.is_multiple_of(2) {
return None;
}
let r = n.abs().nth_root(k);
return if r.pow(k) == n.abs() {
Some(-r)
} else {
Some(-(r + BigInt::one()))
};
}
Some(n.nth_root(k))
}
pub fn primes_up_to(limit: i64) -> Vec<i64> {
if limit < 2 {
return vec![];
}
if limit < u32::MAX as i64 {
return sieve_u32(limit as u32 + 1)
.into_iter()
.map(i64::from)
.collect();
}
segmented_sieve(2, limit as u64 + 1)
.into_iter()
.map(|p| p as i64)
.collect()
}
pub fn legendre_symbol(a: impl Into<BigInt>, p: impl Into<BigInt>) -> i8 {
let a: BigInt = a.into();
let p: BigInt = p.into();
assert!(
p > BigInt::from(2) && isprime_big_internal(&p),
"p must be an odd prime"
);
jacobi_big(&a, &p)
}
pub fn jacobi_symbol(a: impl Into<BigInt>, n: impl Into<BigInt>) -> Result<i8, SymplexError> {
let a: BigInt = a.into();
let n: BigInt = n.into();
if !n.is_positive() || n.is_even() {
return Err(SymplexError::InvalidArgument {
operation: "jacobi_symbol",
reason: format!("modulus {n} must be a positive odd integer"),
});
}
Ok(jacobi_big(&a, &n))
}
pub fn kronecker_symbol(a: impl Into<BigInt>, n: impl Into<BigInt>) -> i8 {
let a: BigInt = a.into();
let n: BigInt = n.into();
if n.is_zero() {
return i8::from(a.abs().is_one());
}
let mut result: i8 = 1;
let mut n = n;
if n.is_negative() {
n = -n;
if a.is_negative() {
result = -result;
}
}
let mut twos = 0u32;
while n.is_even() {
n /= 2;
twos += 1;
}
if twos > 0 {
if a.is_even() {
return 0;
}
let r = a.mod_floor(&BigInt::from(8)).to_u32().unwrap_or(0);
let k2: i8 = if r == 1 || r == 7 { 1 } else { -1 };
if twos % 2 == 1 {
result *= k2;
}
}
if n.is_one() {
return result;
}
result * jacobi_big(&a, &n)
}
pub fn is_quad_residue(a: impl Into<BigInt>, n: impl Into<BigInt>) -> bool {
let a: BigInt = a.into();
let n: BigInt = n.into();
if n < BigInt::one() {
return false;
}
if n.is_one() {
return true;
}
if n.is_odd() && isprime_big_internal(&n) {
let am = a.mod_floor(&n);
return am.is_zero() || jacobi_big(&am, &n) == 1;
}
!sqrt_mod_all(a, n).is_empty()
}
fn sqrt_mod_prime(a: &BigInt, p: &BigInt) -> Option<BigInt> {
let a = a.mod_floor(p);
if a.is_zero() {
return Some(BigInt::zero());
}
if *p == BigInt::from(2) {
return Some(a);
}
if jacobi_big(&a, p) != 1 {
return None;
}
let one = BigInt::one();
let two = BigInt::from(2);
if p.mod_floor(&BigInt::from(4)) == BigInt::from(3) {
let r = a.modpow(&((p + &one) / 4), p);
return Some(r);
}
let mut q = p - &one;
let mut s = 0u32;
while q.is_even() {
q /= 2;
s += 1;
}
let mut z = two.clone();
while jacobi_big(&z, p) != -1 {
z += &one;
}
let mut m = s;
let mut c = z.modpow(&q, p);
let mut t = a.modpow(&q, p);
let mut r = a.modpow(&((&q + &one) / 2), p);
while !t.is_one() {
let mut i = 0u32;
let mut tt = t.clone();
while !tt.is_one() {
tt = (&tt * &tt) % p;
i += 1;
if i == m {
return None;
}
}
let mut b = c.clone();
for _ in 0..(m - i - 1) {
b = (&b * &b) % p;
}
m = i;
c = (&b * &b) % p;
t = (&t * &c) % p;
r = (&r * &b) % p;
}
Some(r)
}
fn sqrt_mod_prime_power_all(a: &BigInt, p: &BigInt, k: u32) -> Vec<BigInt> {
let pk = p.pow(k);
let a = a.mod_floor(&pk);
if pk <= BigInt::from(4096) {
let m = pk.to_u64().unwrap_or(0);
let av = a.to_u64().unwrap_or(0);
return (0..m)
.filter(|&x| (x * x) % m == av)
.map(BigInt::from)
.collect();
}
if a.is_zero() {
let h = k.div_ceil(2);
let step = p.pow(h);
let count = p.pow(k - h);
let Some(count) = count.to_u64() else {
return vec![];
};
let mut out = Vec::with_capacity(count as usize);
let mut x = BigInt::zero();
for _ in 0..count {
out.push(x.clone());
x += &step;
}
return out;
}
let mut t = 0u32;
let mut b = a.clone();
while (&b % p).is_zero() {
b /= p;
t += 1;
}
if t % 2 == 1 {
return vec![];
}
if t > 0 {
let half = t / 2;
let inner = sqrt_mod_prime_power_all(&b, p, k - t);
let scale = p.pow(half);
let period = p.pow(k - half);
let Some(reps) = p.pow(half).to_u64() else {
return vec![];
};
let mut out = Vec::new();
for y in inner {
let base = (&scale * &y).mod_floor(&pk);
let mut x = base;
for _ in 0..reps {
out.push(x.clone());
x = (&x + &period).mod_floor(&pk);
}
}
out.sort();
out.dedup();
return out;
}
if *p == BigInt::from(2) {
if a.mod_floor(&BigInt::from(8)) != BigInt::one() {
return vec![];
}
let mut r = BigInt::one();
for j in 3..k {
let modulus_next = BigInt::one() << (j + 1) as usize;
if !((&r * &r - &a).mod_floor(&modulus_next)).is_zero() {
r += BigInt::one() << (j - 1) as usize;
}
}
let half = BigInt::one() << (k - 1) as usize;
let mut roots = vec![
r.mod_floor(&pk),
(-&r).mod_floor(&pk),
(&r + &half).mod_floor(&pk),
(-&r + &half).mod_floor(&pk),
];
roots.sort();
roots.dedup();
return roots;
}
let r0 = match sqrt_mod_prime(&a, p) {
Some(r) => r,
None => return vec![],
};
let mut r = r0;
let mut modulus = p.clone();
for _ in 1..k {
let next = &modulus * p;
let two_r = (BigInt::from(2) * &r).mod_floor(&next);
let (_, inv, _) = extended_gcd_big(&two_r, &next);
let corr = ((&r * &r - &a) * inv).mod_floor(&next);
r = (&r - corr).mod_floor(&next);
modulus = next;
}
let mut out = vec![r.clone(), (-&r).mod_floor(&pk)];
out.sort();
out.dedup();
out
}
pub fn sqrt_mod_all(a: impl Into<BigInt>, n: impl Into<BigInt>) -> Vec<BigInt> {
let a: BigInt = a.into();
let n: BigInt = n.into();
if n < BigInt::one() {
return vec![];
}
if n.is_one() {
return vec![BigInt::zero()];
}
let mut combined: Vec<(BigInt, BigInt)> = vec![(BigInt::zero(), BigInt::one())]; for (p, k) in factorint(n.clone()) {
let pk = p.pow(k);
let roots = sqrt_mod_prime_power_all(&a, &p, k);
if roots.is_empty() {
return vec![];
}
let mut next = Vec::with_capacity(combined.len() * roots.len());
for (res, m) in &combined {
for r in &roots {
if let Some(x) = crt(&[res.clone(), r.clone()], &[m.clone(), pk.clone()]) {
next.push((x, m * &pk));
}
}
}
combined = next;
}
let mut out: Vec<BigInt> = combined.into_iter().map(|(x, _)| x).collect();
out.sort();
out.dedup();
out
}
pub fn sqrt_mod(a: impl Into<BigInt>, n: impl Into<BigInt>) -> Option<BigInt> {
let a: BigInt = a.into();
let n: BigInt = n.into();
if n < BigInt::one() {
return None;
}
if n.is_odd() && isprime_big_internal(&n) {
let r = sqrt_mod_prime(&a, &n)?;
let other = (-&r).mod_floor(&n);
return Some(r.min(other));
}
sqrt_mod_all(a, n).into_iter().next()
}
pub fn n_order(a: impl Into<BigInt>, n: impl Into<BigInt>) -> Option<BigInt> {
let a: BigInt = a.into();
let n: BigInt = n.into();
if n < BigInt::one() {
return None;
}
if n.is_one() {
return Some(BigInt::one());
}
let a = a.mod_floor(&n);
if !a.gcd(&n).is_one() {
return None;
}
let phi = totient(n.clone());
Some(order_from_group_order(&a, &n, &phi))
}
pub fn multiplicative_order(a: impl Into<BigInt>, n: impl Into<BigInt>) -> Option<BigInt> {
n_order(a, n)
}
fn order_from_group_order(a: &BigInt, n: &BigInt, m: &BigInt) -> BigInt {
let mut order = m.clone();
for (q, _) in factorint(m.clone()) {
while (&order % &q).is_zero() && a.modpow(&(&order / &q), n).is_one() {
order /= &q;
}
}
order
}
fn has_primitive_root(n: &BigInt) -> bool {
if *n <= BigInt::from(4) {
return n.is_positive();
}
let mut m = n.clone();
if m.is_even() {
m /= 2;
if m.is_even() {
return false;
}
}
let f = factorint(m);
f.len() == 1
}
pub fn is_primitive_root(g: impl Into<BigInt>, n: impl Into<BigInt>) -> bool {
let g: BigInt = g.into();
let n: BigInt = n.into();
if n < BigInt::one() || !has_primitive_root(&n) {
return false;
}
if n.is_one() {
return true;
}
let g = g.mod_floor(&n);
if !g.gcd(&n).is_one() {
return false;
}
let phi = totient(n.clone());
factorint(phi.clone())
.iter()
.all(|(q, _)| !g.modpow(&(&phi / q), &n).is_one())
}
pub fn primitive_root(n: impl Into<BigInt>) -> Option<BigInt> {
let n: BigInt = n.into();
if n < BigInt::one() || !has_primitive_root(&n) {
return None;
}
if n.is_one() {
return Some(BigInt::zero());
}
if n == BigInt::from(2) {
return Some(BigInt::one());
}
let phi = totient(n.clone());
let phi_factors = factorint(phi.clone());
let mut g = BigInt::from(2);
while g < n {
if g.gcd(&n).is_one()
&& phi_factors
.iter()
.all(|(q, _)| !g.modpow(&(&phi / q), &n).is_one())
{
return Some(g);
}
g += 1;
}
None
}
pub fn discrete_log(
a: impl Into<BigInt>,
b: impl Into<BigInt>,
n: impl Into<BigInt>,
) -> Option<BigInt> {
let n: BigInt = n.into();
if n < BigInt::one() {
return None;
}
let a: BigInt = a.into();
let b: BigInt = b.into();
let a = a.mod_floor(&n);
let b = b.mod_floor(&n);
if n.is_one() {
return Some(BigInt::zero());
}
if b.is_one() {
return Some(BigInt::zero());
}
if !a.gcd(&n).is_one() {
if let Some(nu) = n.to_u64()
&& nu <= 1_000_000
{
let mut x = BigInt::one();
let mut cur = a.clone();
while x <= n {
if cur == b {
return Some(x);
}
cur = (&cur * &a) % &n;
x += 1;
}
}
return None;
}
let order = n_order(a.clone(), n.clone())?;
if !b.modpow(&order, &n).is_one() {
return None;
}
let mut residues: Vec<BigInt> = Vec::new();
let mut moduli: Vec<BigInt> = Vec::new();
for (q, e) in factorint(order.clone()) {
let qe = q.pow(e);
let cof = &order / &qe;
let a_sub = a.modpow(&cof, &n); let b_sub = b.modpow(&cof, &n);
let gamma = a_sub.modpow(&q.pow(e - 1), &n); let a_inv = mod_inverse(a_sub.clone(), n.clone())?;
let mut x = BigInt::zero();
let mut qk = BigInt::one(); for k in 0..e {
let h = (&b_sub * a_inv.modpow(&x, &n)).mod_floor(&n);
let h = h.modpow(&q.pow(e - 1 - k), &n);
let d = bsgs(&gamma, &h, &n, &q)?;
x += &d * &qk;
qk *= &q;
}
residues.push(x);
moduli.push(qe);
}
let x = crt(&residues, &moduli)?;
if a.modpow(&x, &n) == b { Some(x) } else { None }
}
fn bsgs(g: &BigInt, h: &BigInt, n: &BigInt, order: &BigInt) -> Option<BigInt> {
let m = order.sqrt() + BigInt::one();
let m_u = m.to_u64()?;
if m_u > 50_000_000 {
return None; }
let mut table: FxHashMap<BigInt, u64> = FxHashMap::default();
let mut cur = BigInt::one();
for j in 0..m_u {
table.entry(cur.clone()).or_insert(j);
cur = (&cur * g) % n;
}
let g_inv = mod_inverse(g.clone(), n.clone())?;
let factor = g_inv.modpow(&m, n);
let mut gamma = h.mod_floor(n);
for i in 0..=m_u {
if let Some(&j) = table.get(&gamma) {
let x = BigInt::from(i) * &m + BigInt::from(j);
if &x < order {
return Some(x);
}
}
gamma = (&gamma * &factor) % n;
}
None
}
pub fn digits(n: impl Into<BigInt>, base: u32) -> Result<Vec<u32>, SymplexError> {
if base < 2 {
return Err(SymplexError::InvalidArgument {
operation: "digits",
reason: format!("base must be ≥ 2, got {base}"),
});
}
let n: BigInt = n.into();
let mut m = n.abs();
if m.is_zero() {
return Ok(vec![0]);
}
let b = BigInt::from(base);
let mut out = Vec::new();
while !m.is_zero() {
let (q, r) = m.div_rem(&b);
out.push(r.to_u32().unwrap_or(0));
m = q;
}
out.reverse();
Ok(out)
}
pub fn is_palindromic(n: impl Into<BigInt>, base: u32) -> bool {
match digits(n, base) {
Ok(d) => d.iter().eq(d.iter().rev()),
Err(_) => false,
}
}
pub fn continued_fraction(r: &Ratio<BigInt>) -> Vec<BigInt> {
let mut out = Vec::new();
let mut num = r.numer().clone();
let mut den = r.denom().clone();
while !den.is_zero() {
let a = num.div_floor(&den);
let rem = &num - &a * &den;
out.push(a);
num = den;
den = rem;
}
out
}
pub fn continued_fraction_periodic(d: impl Into<BigInt>) -> Option<(Vec<BigInt>, Vec<BigInt>)> {
let d: BigInt = d.into();
if d.is_negative() {
return None;
}
let a0 = d.sqrt();
if &a0 * &a0 == d {
return Some((vec![a0], vec![]));
}
let mut m = BigInt::zero();
let mut dd = BigInt::one();
let mut a = a0.clone();
let two_a0 = &a0 * 2;
let mut period = Vec::new();
loop {
m = &dd * &a - &m;
dd = (&d - &m * &m) / ⅆ
a = (&a0 + &m) / ⅆ
period.push(a.clone());
if a == two_a0 {
break;
}
}
Some((vec![a0], period))
}
pub fn continued_fraction_convergents(terms: &[BigInt]) -> Vec<Ratio<BigInt>> {
let mut out = Vec::with_capacity(terms.len());
let (mut h_prev, mut h) = (BigInt::zero(), BigInt::one()); let (mut k_prev, mut k) = (BigInt::one(), BigInt::zero()); for a in terms {
let h_next = a * &h + &h_prev;
let k_next = a * &k + &k_prev;
h_prev = std::mem::replace(&mut h, h_next);
k_prev = std::mem::replace(&mut k, k_next);
if k.is_zero() {
continue;
}
out.push(Ratio::new(h.clone(), k.clone()));
}
out
}
pub fn egyptian_fraction(r: &Ratio<BigInt>) -> Option<Vec<BigInt>> {
if !r.is_positive() || *r > Ratio::one() {
return None;
}
let mut out = Vec::new();
let mut rem = r.clone();
while !rem.is_zero() {
let d = rem.denom().div_ceil(rem.numer());
out.push(d.clone());
rem -= Ratio::new(BigInt::one(), d);
}
Some(out)
}
pub fn fibonacci(n: impl Into<BigInt>) -> BigInt {
let n: BigInt = n.into();
let neg = n.is_negative();
let m = n.abs().to_u64().unwrap_or(u64::MAX);
let (f, _) = fib_pair(m);
if neg && m.is_multiple_of(2) { -f } else { f }
}
fn fib_pair(n: u64) -> (BigInt, BigInt) {
let mut a = BigInt::zero(); let mut b = BigInt::one(); let bits = 64 - n.leading_zeros();
for i in (0..bits).rev() {
let a2 = &a * (&b * 2 - &a); let b2 = &a * &a + &b * &b; if (n >> i) & 1 == 1 {
a = b2.clone();
b = a2 + b2; } else {
a = a2;
b = b2;
}
}
(a, b)
}
pub fn lucas(n: impl Into<BigInt>) -> BigInt {
let n: BigInt = n.into();
let neg = n.is_negative();
let m = n.abs().to_u64().unwrap_or(u64::MAX);
let (f, f1) = fib_pair(m);
let l: BigInt = &f1 * 2 - &f;
if neg && m % 2 == 1 { -l } else { l }
}
pub fn bernoulli(n: impl Into<BigInt>) -> Option<Ratio<BigInt>> {
let n: BigInt = n.into();
if n.is_negative() {
return None;
}
let n: usize = n.to_usize()?;
Some(crate::base::bernoulli::bernoulli(n))
}
pub fn euler_number(n: impl Into<BigInt>) -> Option<BigInt> {
let n: BigInt = n.into();
if n.is_negative() {
return None;
}
let n = n.to_usize()?;
if n % 2 == 1 {
return Some(BigInt::zero());
}
let m = n / 2;
let mut e: Vec<BigInt> = Vec::with_capacity(m + 1);
e.push(BigInt::one());
for i in 1..=m {
let mut sum = BigInt::zero();
for (k, ek) in e.iter().enumerate().take(i) {
sum += binomial_u64((2 * i) as u64, (2 * k) as u64) * ek;
}
e.push(-sum);
}
Some(e[m].clone())
}
fn binomial_u64(n: u64, k: u64) -> BigInt {
if k > n {
return BigInt::zero();
}
let k = k.min(n - k);
let mut r = BigInt::one();
for i in 0..k {
r = r * BigInt::from(n - i) / BigInt::from(i + 1);
}
r
}
pub fn harmonic(n: impl Into<BigInt>) -> Option<Ratio<BigInt>> {
let n: BigInt = n.into();
if n.is_negative() {
return None;
}
let n = n.to_u64()?;
let mut sum = Ratio::zero();
for k in 1..=n {
sum += Ratio::new(BigInt::one(), BigInt::from(k));
}
Some(sum)
}
pub fn gcd_many(values: &[BigInt]) -> BigInt {
let mut g = BigInt::zero();
for v in values {
g = num_integer::Integer::gcd(&g, v);
if g.is_one() {
break;
}
}
g
}
pub fn lcm_many(values: &[BigInt]) -> BigInt {
let mut l = BigInt::one();
for v in values {
if v.is_zero() {
return BigInt::zero();
}
l = num_integer::Integer::lcm(&l, v);
}
l
}
pub fn igcd<I: Into<BigInt> + Clone>(values: &[I]) -> BigInt {
let big: Vec<BigInt> = values.iter().cloned().map(Into::into).collect();
gcd_many(&big)
}
pub fn ilcm<I: Into<BigInt> + Clone>(values: &[I]) -> BigInt {
let big: Vec<BigInt> = values.iter().cloned().map(Into::into).collect();
lcm_many(&big)
}
pub fn rational_lcm_of_denominators(values: &[Ratio<BigInt>]) -> BigInt {
let denoms: Vec<BigInt> = values.iter().map(|r| r.denom().clone()).collect();
lcm_many(&denoms)
}
#[cfg(test)]
mod tests {
use super::*;
fn bi(n: i64) -> BigInt {
BigInt::from(n)
}
#[test]
fn test_isprime_small() {
assert!(isprime(2));
assert!(isprime(3));
assert!(!isprime(4));
assert!(isprime(5));
}
#[test]
fn test_isprime_larger() {
assert!(isprime(104729));
assert!(!isprime(104730));
}
#[test]
fn test_isprime_mersenne() {
assert!(isprime(2_147_483_647)); }
#[test]
fn test_isprime_edge() {
assert!(!isprime(-1));
assert!(!isprime(0));
assert!(!isprime(1));
assert!(isprime(2));
assert!(!isprime(9));
}
#[test]
fn test_isprime_bigint_small() {
assert!(isprime(BigInt::from(2)));
assert!(isprime(BigInt::from(3)));
assert!(!isprime(BigInt::from(4)));
assert!(isprime(BigInt::from(5)));
assert!(!isprime(BigInt::from(0)));
assert!(!isprime(BigInt::from(-7)));
}
#[test]
fn test_isprime_bigint_mersenne_m31() {
let m31 = BigInt::from(2_147_483_647i64);
assert!(isprime(m31));
}
#[test]
fn test_isprime_bigint_mersenne_m61() {
let m61 = BigInt::from(2u64.pow(61) - 1);
assert!(isprime(m61));
}
#[test]
fn test_isprime_bigint_large_composite() {
let m31 = BigInt::from(2_147_483_647i64);
let other = BigInt::from(2_147_483_659i64);
let product = &m31 * &other;
assert!(!isprime(product));
}
#[test]
fn test_isprime_bigint_carmichael() {
for &c in &[561i64, 1105, 1729, 2465, 2821, 6601, 8911] {
assert!(
!isprime(BigInt::from(c)),
"Carmichael number {c} should not be prime"
);
}
}
#[test]
fn test_factorint_60() {
assert_eq!(factorint(60), vec![(bi(2), 2), (bi(3), 1), (bi(5), 1)]);
}
#[test]
fn test_factorint_1() {
assert_eq!(factorint(1), vec![]);
}
#[test]
fn test_factorint_prime() {
assert_eq!(factorint(97), vec![(bi(97), 1)]);
}
#[test]
fn test_factorint_power_of_two() {
assert_eq!(factorint(1024), vec![(bi(2), 10)]);
}
#[test]
fn test_factorint_negative() {
assert_eq!(factorint(-60), vec![(bi(2), 2), (bi(3), 1), (bi(5), 1)]);
}
#[test]
fn test_factorint_bigint_60() {
let factors = factorint(BigInt::from(60));
assert_eq!(factors, vec![(bi(2), 2), (bi(3), 1), (bi(5), 1)]);
}
#[test]
fn test_factorint_bigint_zero_and_one() {
assert_eq!(factorint(BigInt::from(0)), vec![]);
assert_eq!(factorint(BigInt::from(1)), vec![]);
}
#[test]
fn test_factorint_bigint_negative() {
let factors = factorint(BigInt::from(-60));
assert_eq!(factors, vec![(bi(2), 2), (bi(3), 1), (bi(5), 1)]);
}
#[test]
fn test_factorint_bigint_mersenne_prime_m31() {
let m31 = BigInt::from(2_147_483_647i64);
let factors = factorint(m31.clone());
assert_eq!(factors, vec![(m31, 1)]);
}
#[test]
fn test_factorint_bigint_m61_is_prime() {
let m61 = BigInt::from(2u64.pow(61) - 1);
let factors = factorint(m61.clone());
assert_eq!(factors, vec![(m61, 1)]);
}
#[test]
fn test_factorint_bigint_large_power_of_two() {
let n = BigInt::from(1u64 << 40);
let factors = factorint(n);
assert_eq!(factors, vec![(bi(2), 40)]);
}
#[test]
fn test_factorint_bigint_roundtrip() {
for val in [60i64, 360, 2310, 100_000, 123456789, 999_999_937] {
let n = BigInt::from(val);
let factors = factorint(n.clone());
let mut product = BigInt::one();
for (p, e) in &factors {
for _ in 0..*e {
product *= p;
}
}
assert_eq!(product, n, "factorization of {val} doesn't multiply back");
}
}
#[test]
fn test_factorint_bigint_all_factors_prime() {
for val in [60i64, 360, 2310, 100_000, 123456789] {
let n = BigInt::from(val);
let factors = factorint(n);
for (p, _) in &factors {
assert!(isprime(p.clone()), "factor {p} of {val} is not prime");
}
}
}
#[test]
fn test_factorint_bigint_beyond_f64_precision() {
let n = BigInt::from(2u64.pow(53) + 1);
let factors = factorint(n.clone());
let mut product = BigInt::one();
for (p, e) in &factors {
for _ in 0..*e {
product *= p;
}
}
assert_eq!(
product, n,
"factorization beyond f64 precision must be exact"
);
for (p, _) in &factors {
assert!(isprime(p.clone()), "factor {p} should be prime");
}
}
#[test]
fn test_nextprime() {
assert_eq!(nextprime(10), bi(11));
assert_eq!(nextprime(11), bi(13));
}
#[test]
fn test_nextprime_from_negative() {
assert_eq!(nextprime(-10), bi(2));
}
#[test]
fn test_prevprime() {
assert_eq!(prevprime(10), Some(bi(7)));
assert_eq!(prevprime(2), None);
}
#[test]
fn test_prevprime_3() {
assert_eq!(prevprime(3), Some(bi(2)));
}
#[test]
fn test_divisors_12() {
assert_eq!(
divisors(12),
vec![bi(1), bi(2), bi(3), bi(4), bi(6), bi(12)]
);
}
#[test]
fn test_divisors_1() {
assert_eq!(divisors(1), vec![bi(1)]);
}
#[test]
fn test_divisor_count_12() {
assert_eq!(divisor_count(12), 6);
}
#[test]
fn test_divisor_sum_12() {
assert_eq!(divisor_sum(12), bi(28));
}
#[test]
fn test_totient_12() {
assert_eq!(totient(12), bi(4));
}
#[test]
fn test_totient_prime() {
assert_eq!(totient(13), bi(12));
}
#[test]
fn test_totient_1() {
assert_eq!(totient(1), bi(1));
}
#[test]
fn test_mobius() {
assert_eq!(mobius(1), 1);
assert_eq!(mobius(6), 1); assert_eq!(mobius(4), 0); assert_eq!(mobius(30), -1); }
#[test]
fn test_mod_inverse() {
assert_eq!(mod_inverse(3, 7), Some(bi(5)));
assert_eq!(mod_inverse(2, 4), None);
}
#[test]
fn test_mod_inverse_verify() {
let inv = mod_inverse(17, 43).unwrap();
assert_eq!((bi(17) * &inv) % bi(43), bi(1));
}
#[test]
fn test_crt() {
let r: Vec<BigInt> = vec![bi(2), bi(3), bi(2)];
let m: Vec<BigInt> = vec![bi(3), bi(5), bi(7)];
assert_eq!(crt(&r, &m), Some(bi(23)));
}
#[test]
fn test_crt_no_solution() {
let r: Vec<BigInt> = vec![bi(0), bi(1)];
let m: Vec<BigInt> = vec![bi(2), bi(4)];
assert_eq!(crt(&r, &m), None);
}
#[test]
fn test_crt_i64() {
assert_eq!(crt_i64(&[2, 3, 2], &[3, 5, 7]), Some(23));
}
#[test]
fn test_crt_i64_no_solution() {
assert_eq!(crt_i64(&[0, 1], &[2, 4]), None);
}
#[test]
fn test_gcd_lcm() {
assert_eq!(gcd(12, 8), bi(4));
assert_eq!(lcm(4, 6), bi(12));
}
#[test]
fn test_is_coprime() {
assert!(is_coprime(8, 15));
assert!(!is_coprime(8, 12));
}
#[test]
fn test_mod_pow() {
assert_eq!(mod_pow(2, 10, 1000), bi(24));
assert_eq!(mod_pow(3, 4, 17), bi(13));
}
#[test]
fn test_is_square() {
assert!(is_square(0));
assert!(is_square(1));
assert!(is_square(144));
assert!(!is_square(145));
assert!(!is_square(-4));
}
#[test]
fn test_isqrt() {
assert_eq!(isqrt(0), Some(bi(0)));
assert_eq!(isqrt(9), Some(bi(3)));
assert_eq!(isqrt(10), Some(bi(3)));
assert_eq!(isqrt(-1i64), None);
}
#[test]
fn test_primes_up_to() {
assert_eq!(primes_up_to(30), vec![2, 3, 5, 7, 11, 13, 17, 19, 23, 29]);
}
#[test]
fn test_legendre_symbol() {
assert_eq!(legendre_symbol(2, 7), 1);
assert_eq!(legendre_symbol(3, 7), -1);
assert_eq!(legendre_symbol(7, 7), 0);
}
fn big(s: &str) -> BigInt {
BigInt::parse_bytes(s.as_bytes(), 10).unwrap()
}
fn product_of(factors: &[(BigInt, u32)]) -> BigInt {
factors.iter().map(|(p, e)| p.pow(*e)).product()
}
#[test]
fn factorint_2_pow_64_plus_1() {
let n = big("18446744073709551617");
let f = factorint(n.clone());
assert_eq!(f, vec![(bi(274177), 1), (big("67280421310721"), 1)]);
assert_eq!(product_of(&f), n);
}
#[test]
fn factorint_10_pow_18_plus_9_is_prime() {
let n = 1_000_000_000_000_000_009i64;
let f = factorint(n);
assert_eq!(f, vec![(bi(n), 1)]);
let f = factorint(n + 2); assert_eq!(product_of(&f), bi(n + 2));
assert!(f.iter().all(|(p, _)| isprime(p.clone())));
}
#[test]
fn factorint_semiprime_of_two_32_bit_primes() {
let p = 4_294_967_291u64; let q = 4_294_967_279u64;
let n = BigInt::from(p) * BigInt::from(q);
let f = factorint(n.clone());
assert_eq!(f, vec![(BigInt::from(q), 1), (BigInt::from(p), 1)]);
}
#[test]
fn factorint_30_digit_semiprime_two_15_digit_primes() {
let p = big("1000000000000037");
let q = big("1000000000000091");
assert!(isprime(p.clone()) && isprime(q.clone()));
let n = &p * &q;
let start = std::time::Instant::now();
let f = factorint(n.clone());
let elapsed = start.elapsed();
assert_eq!(f, vec![(p, 1), (q, 1)]);
eprintln!("30-digit semiprime factored in {elapsed:?}");
}
#[test]
#[ignore = "slow in debug builds (~13 s); passes in ~1.5 s with --release"]
fn factorint_2_pow_128_plus_1() {
let n: BigInt = (BigInt::one() << 128usize) + 1;
let start = std::time::Instant::now();
let f = factorint(n.clone());
let elapsed = start.elapsed();
assert_eq!(
f,
vec![
(big("59649589127497217"), 1),
(big("5704689200685129054721"), 1)
]
);
eprintln!("2^128 + 1 factored in {elapsed:?}");
}
#[test]
#[ignore = "slow (~7 s in release); ECM with 20-digit factors"]
fn factorint_40_digit_with_20_digit_factor() {
let p = big("10000000000000000051");
let q = big("100000000000000000039");
assert!(isprime(p.clone()) && isprime(q.clone()));
let n = &p * &q;
let start = std::time::Instant::now();
let f = factorint(n);
eprintln!("40-digit semiprime factored in {:?}", start.elapsed());
assert_eq!(f, vec![(p, 1), (q, 1)]);
}
#[test]
fn factorint_prime_power_beyond_u64() {
let p = BigInt::from(4_294_967_291u64);
let n = p.pow(3);
assert_eq!(factorint(n), vec![(p, 3)]);
}
#[test]
fn factorint_mixed_big() {
let n: BigInt = (BigInt::one() << 70usize) * 3 * 1_000_000_007i64 * 274177i64;
let f = factorint(n.clone());
assert_eq!(product_of(&f), n);
assert_eq!(f[0], (bi(2), 70));
assert!(f.iter().all(|(p, _)| isprime(p.clone())));
}
#[test]
fn factorint_u64_range_exhaustive_small_random() {
let ps = [1_000_000_007u64, 998_244_353, 1_000_000_009, 999_999_937];
for i in 0..ps.len() {
for j in i..ps.len() {
let n = ps[i] * ps[j];
let f = factorint(BigInt::from(n));
assert_eq!(product_of(&f), BigInt::from(n));
assert!(f.iter().all(|(p, _)| isprime(p.clone())));
}
}
}
#[test]
fn isprime_mersenne_127() {
let m127: BigInt = (BigInt::one() << 127usize) - 1;
assert!(isprime(m127));
}
#[test]
fn isprime_large_carmichael_like_composites() {
let m127: BigInt = (BigInt::one() << 127usize) - 1;
assert!(!isprime(&m127 * &m127));
assert!(!isprime(&m127 * 3));
assert!(!isprime(2047));
let a: BigInt = (BigInt::one() << 89usize) - 1;
let b: BigInt = (BigInt::one() << 107usize) - 1;
assert!(isprime(a.clone()) && isprime(b.clone()));
assert!(!isprime(&a * &b));
}
#[test]
fn isprime_large_known_primes() {
assert!(isprime(big("1000000000000000000000000000057")));
assert!(!isprime(big("1000000000000000000000000000056")));
assert!(isprime(big("10000000000000000000000000000000000000121")));
}
#[test]
fn is_probable_prime_agrees_with_isprime() {
for n in [2i64, 3, 97, 561, 1105, 104729, 1_000_000_007] {
assert_eq!(is_probable_prime(n, 10), isprime(n), "{n}");
}
let m127: BigInt = (BigInt::one() << 127usize) - 1;
assert!(is_probable_prime(m127.clone(), 8));
assert!(!is_probable_prime(&m127 * 7, 8));
}
#[test]
fn strong_lucas_rejects_strong_base2_pseudoprimes() {
for &n in &[2047u64, 3277, 4033, 4681, 8321, 15841, 29341, 42799, 49141] {
let nb = BigInt::from(n);
assert!(strong_fermat_big(&nb, &BigInt::from(2)), "{n} is spsp(2)");
assert!(!strong_lucas_big(&nb), "{n} must fail strong Lucas");
}
for &p in &[101u64, 1_000_003, 2_147_483_647] {
assert!(strong_lucas_big(&BigInt::from(p)), "{p} is prime");
}
}
#[test]
fn primepi_known_values() {
assert_eq!(primepi(1), Some(0));
assert_eq!(primepi(2), Some(1));
assert_eq!(primepi(10), Some(4));
assert_eq!(primepi(1000), Some(168));
assert_eq!(primepi(10_000), Some(1229));
assert_eq!(primepi(100_000), Some(9592));
assert_eq!(primepi(1_000_000), Some(78_498));
assert_eq!(primepi(10_000_000), Some(664_579));
}
#[test]
fn primepi_matches_sieve_for_small_n() {
let ps = primes_up_to(2000);
for n in 0..=2000i64 {
let expected = ps.iter().filter(|&&p| p <= n).count() as u64;
assert_eq!(primepi(n), Some(expected), "π({n})");
}
}
#[test]
fn nth_prime_known_values() {
assert_eq!(prime(1), Some(bi(2)));
assert_eq!(prime(2), Some(bi(3)));
assert_eq!(prime(6), Some(bi(13)));
assert_eq!(prime(25), Some(bi(97)));
assert_eq!(prime(1000), Some(bi(7919)));
assert_eq!(prime(100_000), Some(bi(1_299_709)));
assert_eq!(prime(0), None);
}
#[test]
fn primerange_segmented_and_iterative() {
let small: Vec<i64> = primerange(0, 30)
.iter()
.map(|p| p.try_into().unwrap())
.collect();
assert_eq!(small, vec![2, 3, 5, 7, 11, 13, 17, 19, 23, 29]);
let seg = primerange(1_000_000, 1_000_100);
assert_eq!(seg.len(), 6);
assert_eq!(seg[0], bi(1_000_003));
let start = big("1000000000000000000000000000000");
let end = &start + 200;
let big_ps = primerange(start.clone(), end);
assert_eq!(big_ps[0], &start + 57);
assert!(big_ps.iter().all(|p| isprime(p.clone())));
}
#[test]
fn primes_up_to_matches_primepi() {
assert_eq!(
primes_up_to(100_000).len() as u64,
primepi(100_000).unwrap()
);
}
#[test]
fn divisor_sigma_values() {
assert_eq!(divisor_sigma(1, 0), bi(1));
assert_eq!(divisor_sigma(12, 0), bi(6));
assert_eq!(divisor_sigma(12, 1), bi(28));
assert_eq!(divisor_sigma(12, 2), bi(210));
assert_eq!(divisor_sigma(0, 1), bi(0));
for n in 1..200i64 {
assert_eq!(divisor_sigma(n, 1), divisor_sum(n));
assert_eq!(divisor_sigma(n, 0), bi(divisor_count(n) as i64));
}
}
#[test]
fn perfect_abundant_deficient_classification() {
let perfect: Vec<i64> = (1..10_000).filter(|&n| is_perfect(n)).collect();
assert_eq!(perfect, vec![6, 28, 496, 8128]);
assert!(is_abundant(12) && is_abundant(945)); assert!(is_deficient(1) && is_deficient(7) && is_deficient(16));
let abundant_below_50: Vec<i64> = (1..50).filter(|&n| is_abundant(n)).collect();
assert_eq!(abundant_below_50, vec![12, 18, 20, 24, 30, 36, 40, 42, 48]);
}
#[test]
fn carmichael_lambda_oeis_a002322() {
let expected = [
1i64, 1, 2, 2, 4, 2, 6, 2, 6, 4, 10, 2, 12, 6, 4, 4, 16, 6, 18, 4, 6, 10, 22, 2, 20,
12, 18, 6, 28, 4,
];
for (i, &e) in expected.iter().enumerate() {
assert_eq!(carmichael_lambda(i as i64 + 1), bi(e), "λ({})", i + 1);
}
}
#[test]
fn perfect_power_detection() {
assert_eq!(perfect_power(64), Some((bi(2), 6)));
assert_eq!(perfect_power(1024), Some((bi(2), 10)));
assert_eq!(perfect_power(36), Some((bi(6), 2)));
assert_eq!(perfect_power(216), Some((bi(6), 3)));
assert_eq!(perfect_power(-8), Some((bi(-2), 3)));
assert_eq!(perfect_power(-4), None);
assert_eq!(perfect_power(1), None);
assert_eq!(perfect_power(0), None);
assert_eq!(perfect_power(10), None);
let big_pow = BigInt::from(1_000_000_007u64).pow(5);
assert_eq!(perfect_power(big_pow), Some((bi(1_000_000_007), 5)));
assert!(is_perfect_power(3u64.pow(20)));
assert!(!is_perfect_power(3u64.pow(20) + 1));
}
#[test]
fn mersenne_prime_exponents() {
let known = [2i64, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127];
for p in 2..=130i64 {
assert_eq!(is_mersenne_prime(p), known.contains(&p), "p = {p}");
}
assert!(is_mersenne_prime(521));
assert!(!is_mersenne_prime(-3));
}
#[test]
fn jacobi_and_kronecker() {
assert_eq!(jacobi_symbol(1001, 9907).unwrap(), -1);
assert_eq!(jacobi_symbol(19, 45).unwrap(), 1);
assert_eq!(jacobi_symbol(8, 21).unwrap(), -1);
assert_eq!(jacobi_symbol(5, 21).unwrap(), 1);
assert_eq!(jacobi_symbol(0, 21).unwrap(), 0);
assert!(jacobi_symbol(1, 10).is_err());
assert!(jacobi_symbol(1, -3).is_err());
for p in [3i64, 5, 7, 11, 13, 17, 19, 23] {
for a in -10..30i64 {
assert_eq!(
jacobi_symbol(a, p).unwrap(),
legendre_symbol(a, p),
"({a}/{p})"
);
assert_eq!(kronecker_symbol(a, p), legendre_symbol(a, p));
}
}
assert_eq!(kronecker_symbol(1, 2), 1);
assert_eq!(kronecker_symbol(7, 2), 1);
assert_eq!(kronecker_symbol(3, 2), -1);
assert_eq!(kronecker_symbol(4, 2), 0);
assert_eq!(kronecker_symbol(-3, -1), -1);
assert_eq!(kronecker_symbol(3, -1), 1);
assert_eq!(kronecker_symbol(1, 0), 1);
assert_eq!(kronecker_symbol(2, 0), 0);
for a in -7..8i64 {
assert_eq!(
kronecker_symbol(a, 12),
kronecker_symbol(a, 4) * kronecker_symbol(a, 3)
);
}
}
#[test]
fn sqrt_mod_primes_brute_force_check() {
for p in [3i64, 5, 7, 11, 13, 17, 41, 97, 101, 113, 193, 257, 65537] {
for a in 0..p.min(60) {
let r = sqrt_mod(a, p);
let exists = (0..p).any(|x| (x * x) % p == a);
assert_eq!(r.is_some(), exists, "a={a} p={p}");
if let Some(r) = r {
let r: i64 = r.try_into().unwrap();
assert_eq!((r * r) % p, a);
}
assert_eq!(is_quad_residue(a, p), exists);
}
}
}
#[test]
fn sqrt_mod_all_composite_brute_force_check() {
for n in [
1i64, 2, 4, 8, 9, 12, 15, 16, 27, 36, 45, 64, 72, 100, 105, 128, 225, 4096, 8192,
] {
for a in 0..n.min(40) {
let roots = sqrt_mod_all(a, n);
let expected: Vec<BigInt> =
(0..n).filter(|x| (x * x) % n == a % n).map(bi).collect();
assert_eq!(roots, expected, "a={a} n={n}");
}
}
}
#[test]
fn sqrt_mod_large_prime_and_prime_power() {
let p = big("1000000000000000000000000000057");
let a = BigInt::from(123456789u64);
let a2 = (&a * &a) % &p;
let r = sqrt_mod(a2.clone(), p.clone()).unwrap();
assert_eq!((&r * &r) % &p, a2);
let n = BigInt::from(7).pow(10);
let a = BigInt::from(9) * BigInt::from(7).pow(4);
let roots = sqrt_mod_all(a.clone(), n.clone());
assert!(!roots.is_empty());
for r in &roots {
assert_eq!((r * r) % &n, a);
}
let n = BigInt::one() << 20usize;
let roots = sqrt_mod_all(17, n.clone());
assert_eq!(roots.len(), 4);
for r in &roots {
assert_eq!((r * r) % &n, bi(17));
}
}
#[test]
fn n_order_values() {
assert_eq!(n_order(2, 7), Some(bi(3)));
assert_eq!(n_order(3, 7), Some(bi(6)));
assert_eq!(n_order(1, 7), Some(bi(1)));
assert_eq!(n_order(2, 8), None);
assert_eq!(n_order(5, 1), Some(bi(1)));
assert_eq!(n_order(10, 561), multiplicative_order(10, 561));
for n in 2..60i64 {
for a in 1..n {
if gcd(a, n).is_one() {
let ord = n_order(a, n).unwrap();
assert!((carmichael_lambda(n) % &ord).is_zero());
assert_eq!(mod_pow(a, ord, n), bi(1));
}
}
}
}
#[test]
fn primitive_roots_oeis_a001918() {
let primes = [
3i64, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83,
89, 97,
];
let expected = [
2i64, 2, 3, 2, 2, 3, 2, 5, 2, 3, 2, 6, 3, 5, 2, 2, 2, 2, 7, 5, 3, 2, 3, 5,
];
for (p, g) in primes.iter().zip(expected.iter()) {
assert_eq!(primitive_root(*p), Some(bi(*g)), "p = {p}");
assert!(is_primitive_root(*g, *p));
}
assert_eq!(primitive_root(1), Some(bi(0)));
assert_eq!(primitive_root(2), Some(bi(1)));
assert_eq!(primitive_root(4), Some(bi(3)));
assert_eq!(primitive_root(9), Some(bi(2)));
assert_eq!(primitive_root(18), Some(bi(5)));
assert_eq!(primitive_root(8), None);
assert_eq!(primitive_root(15), None);
assert_eq!(primitive_root(0), None);
assert!(!is_primitive_root(2, 15));
}
#[test]
fn discrete_log_brute_force_check() {
for n in [7i64, 11, 13, 17, 25, 27, 31, 49, 97, 101, 128, 255, 1009] {
for a in 2..n.min(12) {
if !gcd(a, n).is_one() {
continue;
}
for x in 0..20i64 {
let b = mod_pow(a, x, n);
let got = discrete_log(a, b.clone(), n).expect("solution exists");
assert_eq!(mod_pow(a, got.clone(), n), b, "a={a} b={b} n={n}");
let got_i: i64 = got.try_into().unwrap();
assert!(
(0..got_i).all(|y| mod_pow(a, y, n) != b),
"a={a} n={n}: {got_i} is not minimal"
);
}
}
}
}
#[test]
fn discrete_log_pohlig_hellman_large() {
let p = 469_762_049i64;
assert!(isprime(p));
let g = primitive_root(p).unwrap();
let x = bi(123_456_789);
let b = mod_pow(g.clone(), x.clone(), p);
assert_eq!(discrete_log(g, b, p), Some(x));
let p = 1_000_000_007i64;
let g = primitive_root(p).unwrap();
let x = bi(987_654_321);
let b = mod_pow(g.clone(), x.clone(), p);
assert_eq!(discrete_log(g, b, p), Some(x));
}
#[test]
fn discrete_log_no_solution_and_non_coprime() {
assert_eq!(discrete_log(2, 3, 7), None); assert_eq!(discrete_log(4, 2, 8), None); assert_eq!(discrete_log(2, 4, 8), Some(bi(2)));
assert_eq!(discrete_log(3, 1, 7), Some(bi(0)));
}
#[test]
fn digits_and_palindromes() {
assert_eq!(digits(0, 10).unwrap(), vec![0]);
assert_eq!(digits(1234, 10).unwrap(), vec![1, 2, 3, 4]);
assert_eq!(digits(255, 2).unwrap(), vec![1; 8]);
assert_eq!(digits(-255, 16).unwrap(), vec![15, 15]);
assert!(digits(5, 0).is_err());
assert!(is_palindromic(0, 10));
assert!(is_palindromic(1221, 10));
assert!(is_palindromic(585, 2)); assert!(!is_palindromic(10, 10));
assert!(!is_palindromic(10, 1));
}
#[test]
fn continued_fraction_roundtrip_via_convergents() {
for (p, q) in [
(415i64, 93i64),
(-7, 3),
(1, 1),
(0, 1),
(355, 113),
(-1, 7),
(100, 3),
] {
let r = Ratio::new(bi(p), bi(q));
let cf = continued_fraction(&r);
let conv = continued_fraction_convergents(&cf);
assert_eq!(*conv.last().unwrap(), r, "{p}/{q}: {cf:?}");
if cf.len() > 1 {
assert!(*cf.last().unwrap() > BigInt::one());
}
}
}
#[test]
fn continued_fraction_periodic_known() {
let cf = |d: i64| {
let (h, p) = continued_fraction_periodic(d).unwrap();
let hi: Vec<i64> = h.iter().map(|t| t.try_into().unwrap()).collect();
let pi: Vec<i64> = p.iter().map(|t| t.try_into().unwrap()).collect();
(hi, pi)
};
assert_eq!(cf(2), (vec![1], vec![2]));
assert_eq!(cf(3), (vec![1], vec![1, 2]));
assert_eq!(cf(5), (vec![2], vec![4]));
assert_eq!(cf(7), (vec![2], vec![1, 1, 1, 4]));
assert_eq!(cf(13), (vec![3], vec![1, 1, 1, 1, 6]));
assert_eq!(cf(61), (vec![7], vec![1, 4, 3, 1, 2, 2, 1, 3, 4, 1, 14]));
assert_eq!(cf(16), (vec![4], vec![]));
assert_eq!(cf(0), (vec![0], vec![]));
assert!(continued_fraction_periodic(-2).is_none());
}
#[test]
fn egyptian_fraction_greedy() {
let to_i =
|v: Vec<BigInt>| -> Vec<i64> { v.iter().map(|t| t.try_into().unwrap()).collect() };
assert_eq!(
to_i(egyptian_fraction(&Ratio::new(bi(4), bi(13))).unwrap()),
vec![4, 18, 468]
);
let e = egyptian_fraction(&Ratio::new(bi(5), bi(121))).unwrap();
assert_eq!(e.len(), 5);
assert_eq!(e[0], bi(25));
assert_eq!(e[1], bi(757));
assert_eq!(e[2], bi(763309));
assert_eq!(e[3], bi(873960180913));
assert_eq!(e[4], big("1527612795642093418846225"));
assert_eq!(
to_i(egyptian_fraction(&Ratio::new(bi(1), bi(1))).unwrap()),
vec![1]
);
assert_eq!(
to_i(egyptian_fraction(&Ratio::new(bi(3), bi(4))).unwrap()),
vec![2, 4]
);
assert!(egyptian_fraction(&Ratio::new(bi(0), bi(1))).is_none());
assert!(egyptian_fraction(&Ratio::new(bi(-1), bi(2))).is_none());
let r = Ratio::new(bi(7), bi(15));
let sum: Ratio<BigInt> = egyptian_fraction(&r)
.unwrap()
.iter()
.map(|d| Ratio::new(BigInt::one(), d.clone()))
.sum();
assert_eq!(sum, r);
}
#[test]
fn fibonacci_and_lucas_values() {
let fibs = [0i64, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144];
for (i, &f) in fibs.iter().enumerate() {
assert_eq!(fibonacci(i as i64), bi(f), "F({i})");
}
assert_eq!(fibonacci(-1), bi(1));
assert_eq!(fibonacci(-2), bi(-1));
assert_eq!(fibonacci(-8), bi(-21));
assert_eq!(fibonacci(-9), bi(34));
assert_eq!(fibonacci(100), big("354224848179261915075"));
assert_eq!(fibonacci(300).to_string().len(), 63);
let lucas_vals = [2i64, 1, 3, 4, 7, 11, 18, 29, 47, 76, 123];
for (i, &l) in lucas_vals.iter().enumerate() {
assert_eq!(lucas(i as i64), bi(l), "L({i})");
}
assert_eq!(lucas(-1), bi(-1));
assert_eq!(lucas(-2), bi(3));
assert_eq!(lucas(-3), bi(-4));
for n in 1..40i64 {
assert_eq!(lucas(n), fibonacci(n - 1) + fibonacci(n + 1));
}
}
#[test]
fn bernoulli_euler_harmonic_values() {
let r = |p: i64, q: i64| Ratio::new(bi(p), bi(q));
assert_eq!(bernoulli(0), Some(r(1, 1)));
assert_eq!(bernoulli(1), Some(r(-1, 2)));
assert_eq!(bernoulli(2), Some(r(1, 6)));
assert_eq!(bernoulli(4), Some(r(-1, 30)));
assert_eq!(bernoulli(6), Some(r(1, 42)));
assert_eq!(bernoulli(8), Some(r(-1, 30)));
assert_eq!(bernoulli(10), Some(r(5, 66)));
assert_eq!(bernoulli(12), Some(r(-691, 2730)));
assert_eq!(bernoulli(20), Some(r(-174611, 330)));
assert_eq!(bernoulli(-1), None);
let euler = [1i64, -1, 5, -61, 1385, -50521, 2_702_765, -199_360_981];
for (i, &e) in euler.iter().enumerate() {
assert_eq!(euler_number(2 * i as i64), Some(bi(e)), "E({})", 2 * i);
}
assert_eq!(euler_number(5), Some(bi(0)));
assert_eq!(euler_number(-2), None);
assert_eq!(harmonic(1), Some(r(1, 1)));
assert_eq!(harmonic(2), Some(r(3, 2)));
assert_eq!(harmonic(3), Some(r(11, 6)));
assert_eq!(harmonic(10), Some(r(7381, 2520)));
assert_eq!(harmonic(-1), None);
}
#[test]
fn gcdex_bezout() {
for (a, b) in [(240i64, 46i64), (0, 5), (5, 0), (-12, 18), (17, 31), (0, 0)] {
let (g, x, y) = gcdex(a, b);
assert_eq!(g, gcd(a, b));
assert_eq!(bi(a) * x + bi(b) * y, g);
}
}
#[test]
fn iroot_values() {
assert_eq!(iroot(1000, 3), Some(bi(10)));
assert_eq!(iroot(1001, 3), Some(bi(10)));
assert_eq!(iroot(999, 3), Some(bi(9)));
assert_eq!(iroot(-8, 3), Some(bi(-2)));
assert_eq!(iroot(-9, 3), Some(bi(-3)));
assert_eq!(iroot(-8, 2), None);
assert_eq!(iroot(8, 0), None);
assert_eq!(iroot(0, 5), Some(bi(0)));
}
#[test]
fn re_exported_combinatorics_reachable() {
assert_eq!(bell(5), Some(bi(52)));
assert_eq!(catalan(5), Some(bi(42)));
assert_eq!(binomial(10, 3), bi(120));
assert_eq!(derangements(4), Some(bi(9)));
assert_eq!(npartitions(5), Some(bi(7)));
assert_eq!(partitions(4).count(), 5);
let _: PartitionIter = partitions(3);
}
#[test]
fn gcd_many_basic_and_negatives() {
assert_eq!(gcd_many(&[bi(12), bi(18), bi(30)]), bi(6));
assert_eq!(gcd_many(&[bi(-12), bi(18)]), bi(6));
assert_eq!(gcd_many(&[bi(-7)]), bi(7));
assert_eq!(gcd_many(&[bi(0), bi(0)]), bi(0));
assert_eq!(gcd_many(&[bi(0), bi(5)]), bi(5));
assert_eq!(gcd_many(&[bi(7), bi(11), bi(13)]), bi(1));
}
#[test]
fn gcd_many_empty_is_zero() {
assert_eq!(gcd_many(&[]), bi(0));
assert_eq!(igcd::<i64>(&[]), bi(0));
}
#[test]
fn lcm_many_basic_and_zero() {
assert_eq!(lcm_many(&[bi(4), bi(6), bi(10)]), bi(60));
assert_eq!(lcm_many(&[bi(-4), bi(6)]), bi(12));
assert_eq!(lcm_many(&[bi(3), bi(0)]), bi(0));
assert_eq!(lcm_many(&[bi(9)]), bi(9));
}
#[test]
fn lcm_many_empty_is_one() {
assert_eq!(lcm_many(&[]), bi(1));
assert_eq!(ilcm::<i64>(&[]), bi(1));
}
#[test]
fn igcd_ilcm_accept_i64_slices() {
assert_eq!(igcd(&[12i64, 18, 30]), bi(6));
assert_eq!(ilcm(&[2i64, 3, 4]), bi(12));
assert_eq!(igcd(&[bi(100), bi(75)]), bi(25));
}
#[test]
fn rational_lcm_of_denominators_clears() {
let q = |n: i64, d: i64| Ratio::new(bi(n), bi(d));
let v = [q(1, 3), q(1, 7), q(5, 21), q(2, 1)];
let l = rational_lcm_of_denominators(&v);
assert_eq!(l, bi(21));
for r in &v {
assert!((r * Ratio::from_integer(l.clone())).is_integer());
}
assert_eq!(rational_lcm_of_denominators(&[]), bi(1));
assert_eq!(rational_lcm_of_denominators(&[q(4, 1)]), bi(1));
}
}