use std::sync::OnceLock;
use num_bigint::BigInt;
use num_integer::{ExtendedGcd, Integer, Roots};
use num_rational::Ratio;
use num_traits::{One, Signed, ToPrimitive, Zero};
use rustc_hash::FxHashMap;
use crate::api::context::Context;
use crate::api::expr::Ex;
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>) -> ExtendedGcd<BigInt> {
let a: BigInt = a.into();
let b: BigInt = b.into();
Integer::extended_gcd(&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
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PeriodicContinuedFraction {
pub pre_period: Vec<BigInt>,
pub period: Vec<BigInt>,
}
pub fn continued_fraction_periodic(d: impl Into<BigInt>) -> Option<PeriodicContinuedFraction> {
let d: BigInt = d.into();
if d.is_negative() {
return None;
}
let a0 = d.sqrt();
if &a0 * &a0 == d {
return Some(PeriodicContinuedFraction {
pre_period: vec![a0],
period: 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(PeriodicContinuedFraction {
pre_period: 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)
}
pub fn nthroot_mod(
a: impl Into<BigInt>,
n: impl Into<BigInt>,
m: impl Into<BigInt>,
all_roots: bool,
) -> Option<Vec<BigInt>> {
let n: BigInt = n.into();
let m: BigInt = m.into();
if n < BigInt::one() || m < BigInt::one() {
return None;
}
let a: BigInt = a.into().mod_floor(&m);
if m.is_one() {
return Some(vec![BigInt::zero()]);
}
if n.is_one() {
return Some(vec![a]);
}
let roots = if n == BigInt::from(2) {
sqrt_mod_all(a, m)
} else {
let mut per_prime_power = Vec::new();
for (p, k) in factorint(m.clone()) {
let roots = nthroot_mod_prime_power(&a, &n, &p, k);
if roots.is_empty() {
return None;
}
per_prime_power.push((p.pow(k), roots));
}
crt_combine_roots(per_prime_power)
};
if roots.is_empty() {
return None;
}
if all_roots {
Some(roots)
} else {
roots.into_iter().next().map(|r| vec![r])
}
}
fn crt_combine_roots(per_prime_power: Vec<(BigInt, Vec<BigInt>)>) -> Vec<BigInt> {
let mut combined: Vec<(BigInt, BigInt)> = vec![(BigInt::zero(), BigInt::one())];
for (pk, roots) in per_prime_power {
let mut next = Vec::with_capacity(combined.len() * roots.len());
for (res, modulus) in &combined {
for r in &roots {
if let Some(x) = crt(&[res.clone(), r.clone()], &[modulus.clone(), pk.clone()]) {
next.push((x, modulus * &pk));
}
}
}
combined = next;
}
let mut out: Vec<BigInt> = combined.into_iter().map(|(x, _)| x).collect();
out.sort();
out.dedup();
out
}
fn is_nthpow_residue_prime_power(a: &BigInt, n: &BigInt, p: &BigInt, k: u32) -> bool {
let pk = p.pow(k);
let mut a = a.mod_floor(&pk);
if a.is_zero() {
return true;
}
let mut k = k;
if (&a % p).is_zero() {
let mut mu = 0u32;
while (&a % p).is_zero() {
a /= p;
mu += 1;
}
if !(BigInt::from(mu) % n).is_zero() {
return false;
}
k = k.saturating_sub(mu).max(1);
}
if *p != BigInt::from(2) {
let phi = p.pow(k - 1) * (p - BigInt::one());
let e = &phi / phi.gcd(n);
return a.modpow(&e, &p.pow(k)).is_one();
}
if n.is_odd() {
return true;
}
let v = n.trailing_zeros().unwrap_or(0);
let c = (v + 2).min(u64::from(k));
a.mod_floor(&(BigInt::one() << c)).is_one()
}
fn nthroot_mod_prime_power(a: &BigInt, n: &BigInt, p: &BigInt, k: u32) -> Vec<BigInt> {
let pk = p.pow(k);
let a = a.mod_floor(&pk);
if !is_nthpow_residue_prime_power(&a, n, p, k) {
return vec![];
}
let a_mod_p = a.mod_floor(p);
let base_roots = if a_mod_p.is_zero() {
vec![BigInt::zero()]
} else {
nthroot_mod_prime(&a_mod_p, n, p)
};
if k == 1 {
return base_roots;
}
let n_minus_1 = n - BigInt::one();
hensel_lift_all(
base_roots,
p,
k,
|x, modulus| (x.modpow(n, modulus) - &a).mod_floor(modulus),
|x, modulus| (n * x.modpow(&n_minus_1, modulus)).mod_floor(modulus),
)
}
fn hensel_lift_all(
base_roots: Vec<BigInt>,
p: &BigInt,
k: u32,
f: impl Fn(&BigInt, &BigInt) -> BigInt,
df: impl Fn(&BigInt, &BigInt) -> BigInt,
) -> Vec<BigInt> {
let mut stack: Vec<(BigInt, u32)> = base_roots.into_iter().map(|x| (x, 1)).collect();
let mut out = Vec::new();
while let Some((x, s)) = stack.pop() {
if s >= k {
out.push(x);
continue;
}
let ps = p.pow(s);
let next = &ps * p;
let alpha = df(&x, p);
let beta = (-(f(&x, &next) / &ps)).mod_floor(p);
if alpha.is_zero() {
if beta.is_zero() {
let Some(count) = p.to_u64() else {
continue;
};
let mut y = x;
for _ in 0..count {
stack.push((y.clone(), s + 1));
y += &ps;
}
}
} else if let Some(inv) = mod_inverse(alpha, p.clone()) {
let v = (inv * beta).mod_floor(p);
stack.push((x + v * &ps, s + 1));
}
}
out.sort();
out.dedup();
out
}
const NTHROOT_BRUTE_FORCE_LIMIT: u64 = 1 << 10;
fn nthroot_mod_prime(a: &BigInt, n: &BigInt, p: &BigInt) -> Vec<BigInt> {
if *p == BigInt::from(2) {
return vec![BigInt::one()];
}
let pm1 = p - BigInt::one();
if let (Some(pu), Some(au)) = (p.to_u64(), a.to_u64())
&& pu < NTHROOT_BRUTE_FORCE_LIMIT
{
let mut e = n.mod_floor(&pm1).to_u64().unwrap_or(0);
if e == 0 {
e = pu - 1;
}
return (1..pu)
.filter(|&x| mod_pow_u64(x, e, pu) == au)
.map(BigInt::from)
.collect();
}
let d = n.gcd(&pm1);
if !a.modpow(&(&pm1 / &d), p).is_one() {
return vec![];
}
let (mut pa, mut pb) = (n.clone(), pm1.clone());
let (mut ca, mut cb) = (a.clone(), BigInt::one());
if pa < pb {
std::mem::swap(&mut pa, &mut pb);
std::mem::swap(&mut ca, &mut cb);
}
while !pb.is_zero() {
let (q, r) = pa.div_rem(&pb);
let Some(cb_inv) = mod_inverse(cb.clone(), p.clone()) else {
return vec![];
};
let c = (cb_inv.modpow(&q, p) * &ca).mod_floor(p);
pa = pb;
pb = r;
ca = cb;
cb = c;
}
if pa.is_one() {
return vec![ca];
}
if pa == BigInt::from(2) {
return sqrt_mod_all(ca, p.clone());
}
nthroot_mod_prime_divisor(&ca, &pa, p)
}
fn nthroot_mod_prime_divisor(s: &BigInt, q: &BigInt, p: &BigInt) -> Vec<BigInt> {
let Some(g) = primitive_root(p.clone()) else {
return vec![];
};
let pm1 = p - BigInt::one();
let mut r = s.clone();
for (qx, ex) in factorint(q.clone()) {
let mut f = &pm1 / qx.pow(ex);
while (&f % &qx).is_zero() {
f /= &qx;
}
let Some(neg_f_inv) = mod_inverse(-&f, qx.clone()) else {
return vec![];
};
let z = &f * neg_f_inv;
let x = (&z + BigInt::one()) / &qx;
let h = g.modpow(&(&f * &qx), p);
let Some(mut t) = discrete_log(h, r.modpow(&f, p), p.clone()) else {
return vec![];
};
for _ in 0..ex {
let zt = (&z * &t).mod_floor(&pm1);
let g_pow = g.modpow(&(&pm1 - &zt), p);
r = (r.modpow(&x, p) * g_pow).mod_floor(p);
t /= &qx;
}
}
let Some(count) = q.to_u64() else {
return vec![];
};
let zeta = g.modpow(&(&pm1 / q), p);
let mut out = Vec::with_capacity(count as usize);
let mut cur = r;
for _ in 0..count {
out.push(cur.clone());
cur = (&cur * &zeta).mod_floor(p);
}
out.sort();
out.dedup();
out
}
pub fn quadratic_residues(n: impl Into<BigInt>) -> Vec<BigInt> {
let n: BigInt = n.into();
if n < BigInt::one() {
return vec![];
}
let Some(nu) = n.to_u64() else {
return vec![];
};
let mut squares: Vec<u64> = (0..=nu / 2).map(|x| mod_mul_u64(x, x, nu)).collect();
squares.sort_unstable();
squares.dedup();
squares.into_iter().map(BigInt::from).collect()
}
pub fn is_nthpow_residue(a: impl Into<BigInt>, n: impl Into<BigInt>, m: impl Into<BigInt>) -> bool {
let n: BigInt = n.into();
let m: BigInt = m.into();
if m < BigInt::one() || n.is_negative() {
return false;
}
if m.is_one() {
return true;
}
let a: BigInt = a.into().mod_floor(&m);
if n.is_zero() {
return a.is_one();
}
if a.is_zero() || n.is_one() {
return true;
}
if n == BigInt::from(2) {
return is_quad_residue(a, m);
}
factorint(m)
.iter()
.all(|(p, k)| is_nthpow_residue_prime_power(&a, &n, p, *k))
}
pub fn polynomial_congruence(coeffs: &[BigInt], m: impl Into<BigInt>) -> Vec<BigInt> {
let m: BigInt = m.into();
if m < BigInt::one() {
return vec![];
}
if m.is_one() {
return vec![BigInt::zero()];
}
let reduced: Vec<BigInt> = coeffs.iter().map(|c| c.mod_floor(&m)).collect();
let first_nonzero = reduced
.iter()
.position(|c| !c.is_zero())
.unwrap_or(reduced.len());
let c = &reduced[first_nonzero..];
match c {
[] => all_residues(&m),
[_] => vec![],
[a, b] => linear_congruence(a, &-b, &m),
[a, b, cc] => quadratic_congruence(a, b, cc, &m),
[lead, middle @ .., last] => {
if lead.is_one() && middle.iter().all(Zero::is_zero) {
let degree = BigInt::from(c.len() - 1);
return nthroot_mod(-last, degree, m, true).unwrap_or_default();
}
polynomial_congruence_general(c, &m)
}
}
}
fn all_residues(m: &BigInt) -> Vec<BigInt> {
match m.to_u64() {
Some(count) => (0..count).map(BigInt::from).collect(),
None => vec![],
}
}
fn linear_congruence(a: &BigInt, b: &BigInt, m: &BigInt) -> Vec<BigInt> {
let a = a.mod_floor(m);
let b = b.mod_floor(m);
if a.is_zero() {
return if b.is_zero() { all_residues(m) } else { vec![] };
}
let (g, x, _) = extended_gcd_big(&a, m);
if !(&b % &g).is_zero() {
return vec![];
}
let step = m / &g;
let x0 = (x * (&b / &g)).mod_floor(&step);
let Some(count) = g.to_u64() else {
return vec![];
};
(0..count).map(|t| &x0 + BigInt::from(t) * &step).collect()
}
fn quadratic_congruence(a: &BigInt, b: &BigInt, c: &BigInt, m: &BigInt) -> Vec<BigInt> {
let two_a = a * 2;
let disc = b * b - &two_a * 2 * c;
let modulus = &two_a * 2 * m;
let mut out: Vec<BigInt> = sqrt_mod_all(disc, modulus)
.into_iter()
.filter_map(|i| {
let (q, r) = (i - b).div_mod_floor(&two_a);
r.is_zero().then(|| q.mod_floor(m))
})
.collect();
out.sort();
out.dedup();
out
}
fn polynomial_congruence_general(c: &[BigInt], m: &BigInt) -> Vec<BigInt> {
let degree = c.len() - 1;
let derivative: Vec<BigInt> = c[..degree]
.iter()
.enumerate()
.map(|(i, ci)| ci * BigInt::from(degree - i))
.collect();
let mut per_prime_power = Vec::new();
for (p, k) in factorint(m.clone()) {
let base = poly_roots_mod_prime(c, &p);
if base.is_empty() {
return vec![];
}
let roots = if k == 1 {
base
} else {
hensel_lift_all(
base,
&p,
k,
|x, modulus| poly_eval_mod(c, x, modulus),
|x, modulus| poly_eval_mod(&derivative, x, modulus),
)
};
if roots.is_empty() {
return vec![];
}
per_prime_power.push((p.pow(k), roots));
}
crt_combine_roots(per_prime_power)
}
fn poly_eval_mod(c: &[BigInt], x: &BigInt, m: &BigInt) -> BigInt {
let mut acc = BigInt::zero();
for ci in c {
acc = (acc * x + ci).mod_floor(m);
}
acc
}
const BRUTE_FORCE_ROOT_LIMIT: u64 = 1 << 16;
fn poly_roots_mod_prime(c: &[BigInt], p: &BigInt) -> Vec<BigInt> {
let Some(pu) = p.to_u64() else {
return vec![];
};
if pu >= 1 << 63 {
return vec![];
}
let cu: Vec<u64> = c
.iter()
.map(|x| x.mod_floor(p).to_u64().unwrap_or(0))
.collect();
if pu <= BRUTE_FORCE_ROOT_LIMIT {
return (0..pu)
.filter(|&x| {
cu.iter()
.fold(0u64, |acc, &ci| (mod_mul_u64(acc, x, pu) + ci) % pu)
== 0
})
.map(BigInt::from)
.collect();
}
let mut f: FpPoly = cu.iter().rev().copied().collect();
fpp_trim(&mut f);
if f.is_empty() {
return (0..pu).map(BigInt::from).collect();
}
fpp_roots(&f, pu).into_iter().map(BigInt::from).collect()
}
type FpPoly = Vec<u64>;
fn fpp_trim(v: &mut FpPoly) {
while v.last().is_some_and(|c| *c == 0) {
v.pop();
}
}
fn fpp_rem(a: &[u64], m: &[u64], p: u64) -> FpPoly {
let Some(dm) = m.len().checked_sub(1) else {
return vec![];
};
let mut r = a.to_vec();
fpp_trim(&mut r);
while r.len() > dm {
let lc = r[r.len() - 1];
let shift = r.len() - 1 - dm;
if lc != 0 {
for (i, &mi) in m.iter().enumerate() {
let sub = mod_mul_u64(lc, mi, p);
r[shift + i] = (r[shift + i] + p - sub) % p;
}
}
r.pop();
fpp_trim(&mut r);
}
r
}
fn fpp_div(a: &[u64], m: &[u64], p: u64) -> FpPoly {
let Some(dm) = m.len().checked_sub(1) else {
return vec![];
};
let mut r = a.to_vec();
fpp_trim(&mut r);
if r.len() <= dm {
return vec![];
}
let mut q = vec![0u64; r.len() - dm];
while r.len() > dm {
let lc = r[r.len() - 1];
let shift = r.len() - 1 - dm;
q[shift] = lc;
if lc != 0 {
for (i, &mi) in m.iter().enumerate() {
let sub = mod_mul_u64(lc, mi, p);
r[shift + i] = (r[shift + i] + p - sub) % p;
}
}
r.pop();
}
fpp_trim(&mut q);
q
}
fn fpp_mulmod(a: &[u64], b: &[u64], m: &[u64], p: u64) -> FpPoly {
if a.is_empty() || b.is_empty() {
return vec![];
}
let mut prod = vec![0u64; a.len() + b.len() - 1];
for (i, &ai) in a.iter().enumerate() {
if ai == 0 {
continue;
}
for (j, &bj) in b.iter().enumerate() {
prod[i + j] = mod_add_u64(prod[i + j], mod_mul_u64(ai, bj, p), p);
}
}
fpp_rem(&prod, m, p)
}
fn fpp_powmod(base: &[u64], mut e: u64, m: &[u64], p: u64) -> FpPoly {
let mut result = vec![1u64];
let mut b = fpp_rem(base, m, p);
while e > 0 {
if e & 1 == 1 {
result = fpp_mulmod(&result, &b, m, p);
}
e >>= 1;
if e > 0 {
b = fpp_mulmod(&b, &b, m, p);
}
}
result
}
fn fpp_monic(a: &[u64], p: u64) -> FpPoly {
let mut a = a.to_vec();
fpp_trim(&mut a);
let Some(&lc) = a.last() else {
return vec![];
};
let inv = mod_pow_u64(lc, p - 2, p);
a.iter().map(|&c| mod_mul_u64(c, inv, p)).collect()
}
fn fpp_gcd(a: &[u64], b: &[u64], p: u64) -> FpPoly {
let mut a = fpp_monic(a, p);
let mut b = b.to_vec();
fpp_trim(&mut b);
while !b.is_empty() {
let bm = fpp_monic(&b, p);
let r = fpp_rem(&a, &bm, p);
a = bm;
b = r;
}
a
}
fn fpp_roots(f: &[u64], p: u64) -> Vec<u64> {
let f = fpp_monic(f, p);
if f.len() <= 1 {
return vec![];
}
let mut xp_minus_x = fpp_powmod(&[0, 1], p, &f, p);
if xp_minus_x.len() < 2 {
xp_minus_x.resize(2, 0);
}
xp_minus_x[1] = (xp_minus_x[1] + p - 1) % p;
fpp_trim(&mut xp_minus_x);
let g = fpp_gcd(&f, &xp_minus_x, p);
let mut out = Vec::new();
let mut rng = XorShift::new(p ^ 0x9E37_79B9_7F4A_7C15);
let mut stack = vec![g];
let mut attempts = 0u32;
while let Some(g) = stack.pop() {
let deg = g.len().saturating_sub(1);
if deg == 0 {
continue;
}
if deg == 1 {
out.push((p - g[0]) % p);
continue;
}
loop {
attempts += 1;
if attempts > 4096 {
return out;
}
let a = rng.next_u64() % p;
let mut pw = fpp_powmod(&[a, 1], (p - 1) / 2, &g, p);
if pw.is_empty() {
pw.push(0);
}
pw[0] = (pw[0] + p - 1) % p;
fpp_trim(&mut pw);
let h = fpp_gcd(&g, &pw, p);
let dh = h.len().saturating_sub(1);
if dh > 0 && dh < deg {
let q = fpp_div(&g, &h, p);
stack.push(h);
stack.push(q);
break;
}
}
}
out.sort_unstable();
out
}
pub fn multiplicity(p: impl Into<BigInt>, n: impl Into<BigInt>) -> u32 {
let p: BigInt = p.into().abs();
let mut n: BigInt = n.into().abs();
if p <= BigInt::one() || n.is_zero() {
return 0;
}
let mut k = 0u32;
while (&n % &p).is_zero() {
n /= &p;
k += 1;
}
k
}
pub fn primenu(n: impl Into<BigInt>) -> u32 {
let n: BigInt = n.into();
u32::try_from(factorint(n).len()).unwrap_or(u32::MAX)
}
pub fn primeomega(n: impl Into<BigInt>) -> u32 {
let n: BigInt = n.into();
factorint(n).iter().map(|(_, e)| *e).sum()
}
fn product_tree(mut values: Vec<BigInt>) -> BigInt {
if values.is_empty() {
return BigInt::one();
}
while values.len() > 1 {
let mut next = Vec::with_capacity(values.len().div_ceil(2));
let mut it = values.into_iter();
while let Some(a) = it.next() {
match it.next() {
Some(b) => next.push(a * b),
None => next.push(a),
}
}
values = next;
}
values.into_iter().next().unwrap_or_else(BigInt::one)
}
pub fn primorial(n: u64) -> BigInt {
if n == 0 {
return BigInt::one();
}
if n <= 10_000_000 {
let bound = if n < 6 {
12
} else {
let nf = n as f64;
(nf * (nf.ln() + nf.ln().ln())).ceil() as u64 + 10
};
let sieve = BitSieve::new(bound);
let mut primes = Vec::with_capacity(n as usize);
let mut k = 2u64;
while k <= bound && (primes.len() as u64) < n {
if sieve.is_prime(k) {
primes.push(BigInt::from(k));
}
k += if k == 2 { 1 } else { 2 };
}
if primes.len() as u64 == n {
return product_tree(primes);
}
}
let mut primes = Vec::new();
let mut p = BigInt::from(2);
for _ in 0..n {
primes.push(p.clone());
p = nextprime(p);
}
product_tree(primes)
}
pub fn primorial_up_to(n: impl Into<BigInt>) -> BigInt {
let n: BigInt = n.into();
if n < BigInt::from(2) {
return BigInt::one();
}
product_tree(primerange(2, n + BigInt::one()))
}
pub fn is_carmichael(n: impl Into<BigInt>) -> bool {
let n: BigInt = n.into();
if n < BigInt::from(3) || n.is_even() {
return false;
}
let factors = factorint(n.clone());
if factors.len() < 2 {
return false;
}
let n_minus_1 = &n - BigInt::one();
factors
.iter()
.all(|(p, e)| *e == 1 && (&n_minus_1 % (p - BigInt::one())).is_zero())
}
pub fn is_amicable(a: impl Into<BigInt>, b: impl Into<BigInt>) -> bool {
let a: BigInt = a.into();
let b: BigInt = b.into();
if !a.is_positive() || !b.is_positive() || a == b {
return false;
}
let sum = &a + &b;
divisor_sigma(a, 1) == sum && divisor_sigma(b, 1) == sum
}
pub fn binomial_coefficients_list(n: u32) -> Vec<BigInt> {
let n = u64::from(n);
let mut row = Vec::new();
let mut c = BigInt::one();
row.push(c.clone());
for k in 0..n {
c = c * BigInt::from(n - k) / BigInt::from(k + 1);
row.push(c.clone());
}
row
}
pub fn binomial_coefficients(n: u32) -> Vec<((u32, u32), BigInt)> {
binomial_coefficients_list(n)
.into_iter()
.enumerate()
.map(|(k, c)| {
let k1 = u32::try_from(k).unwrap_or(u32::MAX);
((k1, n - k1), c)
})
.collect()
}
pub fn continued_fraction_reduce(terms: &[BigInt]) -> Option<Ratio<BigInt>> {
let (last, init) = terms.split_last()?;
let mut x = Ratio::from_integer(last.clone());
for a in init.iter().rev() {
if x.is_zero() {
return None;
}
x = Ratio::from_integer(a.clone()) + x.recip();
}
Some(x)
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct QuadraticSurd {
pub p: BigInt,
pub q: BigInt,
pub d: BigInt,
}
pub fn continued_fraction_reduce_periodic(
pre: &[BigInt],
period: &[BigInt],
) -> Option<QuadraticSurd> {
if period.is_empty() || period.iter().any(|b| !b.is_positive()) {
return None;
}
let (h, h_prev, k, k_prev) = convergent_pair(period);
let s = &h - &k_prev;
let disc = &s * &s + BigInt::from(4) * &k * &h_prev;
let t: BigInt = &k * 2;
if is_square_big(&disc) || t.is_zero() {
return None;
}
let (pp, pp_prev, qq, qq_prev) = convergent_pair(pre);
let a = &pp * &s + &pp_prev * &t;
let b = pp;
let c = &qq * &s + &qq_prev * &t;
let e = qq;
let mut alpha = &a * &c - &b * &e * &disc;
let mut beta = &b * &c - &a * &e;
let mut gamma = &c * &c - &e * &e * &disc;
if gamma.is_zero() || beta.is_zero() {
return None;
}
if beta.is_negative() {
alpha = -alpha;
gamma = -gamma;
beta = -beta;
}
let mut d = &beta * &beta * &disc;
let g = alpha.gcd(&gamma);
for (prime, _) in factorint(g) {
let square = &prime * ′
while (&alpha % &prime).is_zero() && (&gamma % &prime).is_zero() && (&d % &square).is_zero()
{
alpha /= ′
gamma /= ′
d /= □
}
}
Some(QuadraticSurd {
p: alpha,
q: gamma,
d,
})
}
fn convergent_pair(terms: &[BigInt]) -> (BigInt, BigInt, BigInt, BigInt) {
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);
}
(h, h_prev, k, k_prev)
}
pub fn continued_fraction_reduce_periodic_ex(
ctx: &Context,
pre: &[BigInt],
period: &[BigInt],
) -> Option<Ex> {
let QuadraticSurd { p, q, d } = continued_fraction_reduce_periodic(pre, period)?;
let radical = ctx.from_bigint(d).sqrt();
Some((ctx.from_bigint(p) + radical) / ctx.from_bigint(q))
}
#[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 cf = continued_fraction_periodic(d).unwrap();
let hi: Vec<i64> = cf
.pre_period
.iter()
.map(|t| t.try_into().unwrap())
.collect();
let pi: Vec<i64> = cf.period.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 = gcdex(a, b);
assert_eq!(g.gcd, gcd(a, b));
assert_eq!(bi(a) * &g.x + bi(b) * &g.y, g.gcd);
}
}
#[test]
fn gcdex_matches_internal_extended_gcd() {
for a in -40i64..=40 {
for b in -40i64..=40 {
let g = gcdex(a, b);
let (g0, x0, y0) = extended_gcd_big(&bi(a), &bi(b));
assert_eq!((g.gcd, g.x, g.y), (g0, x0, y0), "gcdex({a}, {b})");
}
}
}
#[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));
}
}