use num_bigint::BigInt;
use num_integer::Integer;
use num_rational::BigRational;
use num_traits::{One, Signed, Zero};
use rustc_hash::FxHashMap;
use std::cmp::Ordering;
#[inline]
pub fn cmp(a: &BigRational, b: &BigRational) -> Ordering {
a.cmp(b)
}
#[inline]
pub fn abs(r: &BigRational) -> BigRational {
r.abs()
}
pub fn floor(r: &BigRational) -> BigInt {
if r.is_integer() {
r.numer().clone()
} else if r.is_positive() {
r.numer() / r.denom()
} else {
(r.numer() / r.denom()) - BigInt::one()
}
}
pub fn ceil(r: &BigRational) -> BigInt {
if r.is_integer() {
r.numer().clone()
} else if r.is_positive() {
(r.numer() / r.denom()) + BigInt::one()
} else {
r.numer() / r.denom()
}
}
pub fn round(r: &BigRational) -> BigInt {
let floor_val = floor(r);
let frac = r - BigRational::from_integer(floor_val.clone());
let half = BigRational::new(BigInt::one(), BigInt::from(2));
if frac < half {
floor_val
} else if frac > half {
floor_val + BigInt::one()
} else {
if floor_val.is_even() {
floor_val
} else {
floor_val + BigInt::one()
}
}
}
pub fn frac(r: &BigRational) -> BigRational {
r - BigRational::from_integer(floor(r))
}
#[inline]
pub fn is_integer(r: &BigRational) -> bool {
r.is_integer()
}
pub fn gcd(a: &BigRational, b: &BigRational) -> BigRational {
if a.is_zero() {
return b.abs();
}
if b.is_zero() {
return a.abs();
}
let gcd_num = gcd_bigint(a.numer().clone(), b.numer().clone());
let lcm_denom = lcm_bigint(a.denom().clone(), b.denom().clone());
BigRational::new(gcd_num, lcm_denom)
}
pub fn lcm(a: &BigRational, b: &BigRational) -> BigRational {
if a.is_zero() || b.is_zero() {
return BigRational::zero();
}
let g = gcd(a, b);
(a * b / g).abs()
}
pub fn gcd_bigint(mut a: BigInt, mut b: BigInt) -> BigInt {
while !b.is_zero() {
let t = &a % &b;
a = b;
b = t;
}
a.abs()
}
pub fn gcd_extended(a: BigInt, b: BigInt) -> (BigInt, BigInt, BigInt) {
if b.is_zero() {
return (
a.abs(),
if a >= BigInt::zero() {
BigInt::one()
} else {
-BigInt::one()
},
BigInt::zero(),
);
}
let (mut old_r, mut r) = (a, b);
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 quotient = &old_r / &r;
let new_r = &old_r - "ient * &r;
old_r = r;
r = new_r;
let new_s = &old_s - "ient * &s;
old_s = s;
s = new_s;
let new_t = &old_t - "ient * &t;
old_t = t;
t = new_t;
}
(old_r, old_s, old_t)
}
pub fn lcm_bigint(a: BigInt, b: BigInt) -> BigInt {
if a.is_zero() || b.is_zero() {
return BigInt::zero();
}
let g = gcd_bigint(a.clone(), b.clone());
(a * b / g).abs()
}
pub fn pow_int(base: &BigRational, exp: i32) -> BigRational {
if exp == 0 {
return BigRational::one();
}
if exp > 0 {
pow_uint(base, exp as u32)
} else {
let p = pow_uint(base, (-exp) as u32);
BigRational::one() / p
}
}
pub fn pow_uint(base: &BigRational, exp: u32) -> BigRational {
if exp == 0 {
return BigRational::one();
}
if exp == 1 {
return base.clone();
}
let mut result = BigRational::one();
let mut b = base.clone();
let mut e = exp;
while e > 0 {
if e & 1 == 1 {
result = &result * &b;
}
b = &b * &b;
e >>= 1;
}
result
}
pub fn normalize(r: &BigRational) -> BigRational {
if r.denom().is_negative() {
BigRational::new(-r.numer(), -r.denom())
} else {
r.clone()
}
}
pub fn approx_eq(a: &BigRational, b: &BigRational, epsilon: &BigRational) -> bool {
(a - b).abs() <= *epsilon
}
pub fn sign(r: &BigRational) -> i8 {
if r.is_positive() {
1
} else if r.is_negative() {
-1
} else {
0
}
}
pub fn clamp(val: &BigRational, min: &BigRational, max: &BigRational) -> BigRational {
if val < min {
min.clone()
} else if val > max {
max.clone()
} else {
val.clone()
}
}
#[inline]
pub fn min(a: &BigRational, b: &BigRational) -> BigRational {
if a < b { a.clone() } else { b.clone() }
}
#[inline]
pub fn max(a: &BigRational, b: &BigRational) -> BigRational {
if a > b { a.clone() } else { b.clone() }
}
#[inline]
pub fn from_integers(num: i64, den: i64) -> BigRational {
BigRational::new(BigInt::from(num), BigInt::from(den))
}
#[inline]
pub fn from_integer(n: i64) -> BigRational {
BigRational::from_integer(BigInt::from(n))
}
#[inline]
pub fn numer(r: &BigRational) -> &BigInt {
r.numer()
}
#[inline]
pub fn denom(r: &BigRational) -> &BigInt {
r.denom()
}
pub fn to_f64(r: &BigRational) -> f64 {
if r.denom().is_one() {
r.numer().to_string().parse().unwrap_or(f64::NAN)
} else {
let num_f64: f64 = r.numer().to_string().parse().unwrap_or(f64::NAN);
let den_f64: f64 = r.denom().to_string().parse().unwrap_or(f64::NAN);
num_f64 / den_f64
}
}
pub fn mediant(a: &BigRational, b: &BigRational) -> BigRational {
let num = a.numer() + b.numer();
let den = a.denom() + b.denom();
BigRational::new(num, den)
}
pub fn continued_fraction(r: &BigRational) -> Vec<BigInt> {
let mut result = Vec::new();
let mut current = r.clone();
while !current.is_integer() {
let int_part = floor(¤t);
result.push(int_part.clone());
current = BigRational::one() / (current - BigRational::from_integer(int_part));
}
result.push(current.numer().clone());
result
}
pub fn from_continued_fraction(coeffs: &[BigInt]) -> BigRational {
if coeffs.is_empty() {
return BigRational::zero();
}
if coeffs.len() == 1 {
return BigRational::from_integer(coeffs[0].clone());
}
let mut result = BigRational::from_integer(coeffs[coeffs.len() - 1].clone());
for i in (0..coeffs.len() - 1).rev() {
result = BigRational::from_integer(coeffs[i].clone()) + BigRational::one() / result;
}
result
}
pub fn convergents(coeffs: &[BigInt]) -> Vec<BigRational> {
if coeffs.is_empty() {
return vec![];
}
let mut result = Vec::with_capacity(coeffs.len());
let mut p_prev2 = BigInt::one();
let mut p_prev1 = coeffs[0].clone();
let mut q_prev2 = BigInt::zero();
let mut q_prev1 = BigInt::one();
result.push(BigRational::from_integer(coeffs[0].clone()));
for coeff in coeffs.iter().skip(1) {
let p = coeff * &p_prev1 + &p_prev2;
let q = coeff * &q_prev1 + &q_prev2;
result.push(BigRational::new(p.clone(), q.clone()));
p_prev2 = p_prev1;
p_prev1 = p;
q_prev2 = q_prev1;
q_prev1 = q;
}
result
}
pub fn best_rational_approximation(target: &BigRational, epsilon: &BigRational) -> BigRational {
let cf = continued_fraction(target);
let convs = convergents(&cf);
for conv in convs {
if (target - &conv).abs() <= *epsilon {
return conv;
}
}
target.clone()
}
pub fn mod_pow(base: &BigInt, exp: &BigInt, m: &BigInt) -> BigInt {
if m.is_one() {
return BigInt::zero();
}
let mut result = BigInt::one();
let mut base = base % m;
let mut exp = exp.clone();
while exp > BigInt::zero() {
if (&exp % BigInt::from(2)).is_one() {
result = (result * &base) % m;
}
exp >>= 1;
base = (&base * &base) % m;
}
result
}
pub fn mod_inverse(a: &BigInt, m: &BigInt) -> Option<BigInt> {
let (gcd, x, _) = gcd_extended(a.clone(), m.clone());
if !gcd.is_one() {
return None; }
let inv = x % m;
Some(if inv < BigInt::zero() { inv + m } else { inv })
}
pub fn chinese_remainder(congruences: &[(BigInt, BigInt)]) -> Option<(BigInt, BigInt)> {
if congruences.is_empty() {
return None;
}
let mut prod_moduli = BigInt::one();
for (_, m_i) in congruences {
prod_moduli = &prod_moduli * m_i;
}
let mut result = BigInt::zero();
for (a_i, m_i) in congruences {
let partial_prod = &prod_moduli / m_i;
let inv = mod_inverse(&partial_prod, m_i)?;
result = (result + a_i * &partial_prod * inv) % &prod_moduli;
}
if result < BigInt::zero() {
result += &prod_moduli;
}
Some((result, prod_moduli))
}
pub fn solve_linear_diophantine(a: &BigInt, b: &BigInt, c: &BigInt) -> Option<(BigInt, BigInt)> {
let (gcd, x0, y0) = gcd_extended(a.clone(), b.clone());
if (c % &gcd) != BigInt::zero() {
return None; }
let factor = c / &gcd;
let x = x0 * &factor;
let y = y0 * factor;
Some((x, y))
}
pub fn is_prime(n: &BigInt, k: usize) -> bool {
use num_traits::One;
use rand::Rng;
if n <= &BigInt::one() {
return false;
}
if n == &BigInt::from(2) || n == &BigInt::from(3) {
return true;
}
if n.is_even() {
return false;
}
let n_minus_1 = n - BigInt::one();
let mut d = n_minus_1.clone();
let mut r = 0u32;
while d.is_even() {
d >>= 1;
r += 1;
}
let two = BigInt::from(2);
let n_minus_3 = n - &two - BigInt::one();
let mut rng = rand::rng();
'witness: for _ in 0..k {
let a = if n_minus_3 <= BigInt::zero() {
two.clone()
} else {
let random_u64 = rng.random_range(0u64..u64::MAX);
(BigInt::from(random_u64) % &n_minus_3) + &two
};
let mut x = mod_pow(&a, &d, n);
if x == BigInt::one() || x == n_minus_1 {
continue 'witness;
}
for _ in 0..(r - 1) {
x = mod_pow(&x, &two, n);
if x == n_minus_1 {
continue 'witness;
}
}
return false; }
true }
pub fn trial_division(n: &BigInt, limit: u64) -> Vec<BigInt> {
let mut factors = Vec::new();
let mut num = n.clone();
while num.is_even() {
factors.push(BigInt::from(2));
num >>= 1;
}
let mut divisor = BigInt::from(3);
let limit_big = BigInt::from(limit);
let two = BigInt::from(2);
while &divisor * &divisor <= num && divisor <= limit_big {
while &num % &divisor == BigInt::zero() {
factors.push(divisor.clone());
num /= &divisor;
}
divisor += &two;
}
if num > BigInt::one() && n != &num {
factors.push(num);
}
factors
}
pub fn pollard_rho(n: &BigInt) -> Option<BigInt> {
use rand::Rng;
if n <= &BigInt::one() {
return None;
}
if n.is_even() {
return Some(BigInt::from(2));
}
let mut rng = rand::rng();
let random_x0 = rng.random_range(0u64..u64::MAX);
let random_c = rng.random_range(1u64..u64::MAX);
let x0 = BigInt::from(random_x0) % n;
let c = BigInt::from(random_c) % n;
let f = |x: &BigInt| -> BigInt { (x * x + &c) % n };
let mut x = x0.clone();
let mut y = x0;
let mut d = BigInt::one();
let max_iterations = 100000;
let mut iterations = 0;
while d == BigInt::one() && iterations < max_iterations {
x = f(&x);
y = f(&f(&y));
let diff = if x >= y { &x - &y } else { &y - &x };
d = gcd_bigint(diff, n.clone());
iterations += 1;
}
if d != *n && d != BigInt::one() {
Some(d)
} else {
None
}
}
pub fn jacobi_symbol(a: &BigInt, n: &BigInt) -> i8 {
let mut a = a % n;
let mut n = n.clone();
let mut result = 1i8;
while a != BigInt::zero() {
while a.is_even() {
a >>= 1;
let n_mod_8 = &n % BigInt::from(8);
if n_mod_8 == BigInt::from(3) || n_mod_8 == BigInt::from(5) {
result = -result;
}
}
std::mem::swap(&mut a, &mut n);
if &a % BigInt::from(4) == BigInt::from(3) && &n % BigInt::from(4) == BigInt::from(3) {
result = -result;
}
a %= &n;
}
if n == BigInt::one() { result } else { 0 }
}
pub fn legendre_symbol(a: &BigInt, p: &BigInt) -> i8 {
jacobi_symbol(a, p)
}
#[allow(dead_code)]
pub fn euler_totient(n: &BigInt) -> BigInt {
if n <= &BigInt::one() {
return BigInt::one();
}
let mut result = n.clone();
let factors = trial_division(n, 1000000);
let mut seen_primes = std::collections::HashSet::new();
for factor in factors {
if seen_primes.insert(factor.clone()) {
result = result * (&factor - BigInt::one()) / &factor;
}
}
let mut temp = n.clone();
let mut divisor = BigInt::from(2);
while &divisor * &divisor <= temp {
if &temp % &divisor == BigInt::zero() {
while &temp % &divisor == BigInt::zero() {
temp /= &divisor;
}
if seen_primes.insert(divisor.clone()) {
result = result * (&divisor - BigInt::one()) / &divisor;
}
}
divisor += BigInt::one();
}
if temp > BigInt::one() && !seen_primes.contains(&temp) {
result = result * (&temp - BigInt::one()) / &temp;
}
result
}
pub fn is_perfect_power(n: &BigInt) -> Option<(BigInt, u32)> {
if n <= &BigInt::one() {
return None;
}
let bit_len = n.bits() as u32;
for b in 2..=bit_len {
let mut low = BigInt::one();
let mut high = n.clone();
while low <= high {
let mid = (&low + &high) / BigInt::from(2);
let power = pow_uint(&BigRational::from_integer(mid.clone()), b);
if power.is_integer() {
let power_int = power.numer().clone();
match power_int.cmp(n) {
Ordering::Equal => return Some((mid, b)),
Ordering::Less => low = mid + BigInt::one(),
Ordering::Greater => high = mid - BigInt::one(),
}
} else {
break;
}
}
}
None
}
pub fn is_square_free(n: &BigInt) -> bool {
if n <= &BigInt::one() {
return n == &BigInt::one();
}
let factors = trial_division(n, 100000);
let mut prev: Option<BigInt> = None;
for factor in factors {
if let Some(ref p) = prev
&& &factor == p
{
return false; }
prev = Some(factor);
}
true
}
pub fn divisor_count(n: &BigInt) -> BigInt {
if n <= &BigInt::zero() {
return BigInt::zero();
}
if n == &BigInt::one() {
return BigInt::one();
}
let factors = trial_division(n, 1000000);
if factors.is_empty() {
return BigInt::from(2);
}
let mut count = BigInt::one();
let mut current_prime = None;
let mut current_count = 0u32;
for factor in factors {
if let Some(ref prime) = current_prime {
if &factor == prime {
current_count += 1;
} else {
count *= current_count + 1;
current_prime = Some(factor);
current_count = 1;
}
} else {
current_prime = Some(factor);
current_count = 1;
}
}
if current_count > 0 {
count *= current_count + 1;
}
count
}
pub fn divisor_sum(n: &BigInt) -> BigInt {
if n <= &BigInt::zero() {
return BigInt::zero();
}
if n == &BigInt::one() {
return BigInt::one();
}
let factors = trial_division(n, 1000000);
if factors.is_empty() {
return BigInt::one() + n;
}
let mut sum = BigInt::one();
let mut current_prime = None;
let mut current_count = 0u32;
for factor in factors {
if let Some(ref prime) = current_prime {
if &factor == prime {
current_count += 1;
} else {
let p_power =
pow_uint(&BigRational::from_integer(prime.clone()), current_count + 1);
let numerator = p_power.numer() - BigInt::one();
let denominator = prime - BigInt::one();
sum *= numerator / denominator;
current_prime = Some(factor);
current_count = 1;
}
} else {
current_prime = Some(factor);
current_count = 1;
}
}
if let Some(prime) = current_prime {
let p_power = pow_uint(&BigRational::from_integer(prime.clone()), current_count + 1);
let numerator = p_power.numer() - BigInt::one();
let denominator = prime - BigInt::one();
sum *= numerator / denominator;
}
sum
}
pub fn mobius(n: &BigInt) -> i8 {
if n <= &BigInt::zero() {
return 0;
}
if n == &BigInt::one() {
return 1;
}
if !is_square_free(n) {
return 0;
}
let factors = trial_division(n, 1000000);
let mut unique_primes = std::collections::HashSet::new();
for factor in factors {
unique_primes.insert(factor);
}
if unique_primes.len() % 2 == 0 { 1 } else { -1 }
}
#[allow(dead_code)]
pub fn carmichael_lambda(n: &BigInt) -> BigInt {
if n <= &BigInt::one() {
return BigInt::one();
}
let factors = trial_division(n, 1000000);
let mut prime_powers: FxHashMap<BigInt, u32> = FxHashMap::default();
for factor in factors {
*prime_powers.entry(factor).or_insert(0) += 1;
}
let mut result = BigInt::one();
for (prime, exp) in prime_powers {
let lambda_p = if prime == BigInt::from(2) && exp >= 3 {
BigInt::from(2).pow(exp - 2)
} else if prime == BigInt::from(2) {
if exp == 1 {
BigInt::one()
} else {
BigInt::from(2)
}
} else {
let p_power = pow_uint(&BigRational::from_integer(prime.clone()), exp - 1);
p_power.numer() * (&prime - BigInt::one())
};
result = lcm_bigint(result, lambda_p);
}
result
}
pub fn gcd_binary(mut a: BigInt, mut b: BigInt) -> BigInt {
if a == BigInt::zero() {
return b.abs();
}
if b == BigInt::zero() {
return a.abs();
}
a = a.abs();
b = b.abs();
let mut shift = 0u32;
while a.is_even() && b.is_even() {
a >>= 1;
b >>= 1;
shift += 1;
}
while a.is_even() {
a >>= 1;
}
loop {
while b.is_even() {
b >>= 1;
}
if a > b {
std::mem::swap(&mut a, &mut b);
}
b -= &a;
if b == BigInt::zero() {
break;
}
}
a << shift
}
pub fn tonelli_shanks(n: &BigInt, p: &BigInt) -> Option<BigInt> {
if legendre_symbol(n, p) != 1 {
return None;
}
if p == &BigInt::from(2) {
return Some(n % p);
}
let p_minus_1 = p - BigInt::one();
let mut q = p_minus_1.clone();
let mut s = 0u32;
while q.is_even() {
q >>= 1;
s += 1;
}
if s == 1 {
let exp = (p + BigInt::one()) / BigInt::from(4);
return Some(mod_pow(n, &exp, p));
}
let mut z = BigInt::from(2);
while legendre_symbol(&z, p) != -1 {
z += BigInt::one();
}
let mut m = s;
let mut c = mod_pow(&z, &q, p);
let mut t = mod_pow(n, &q, p);
let mut r = mod_pow(n, &((&q + BigInt::one()) / BigInt::from(2)), p);
loop {
if t == BigInt::zero() {
return Some(BigInt::zero());
}
if t == BigInt::one() {
return Some(r);
}
let mut i = 1u32;
let mut temp = (&t * &t) % p;
while temp != BigInt::one() && i < m {
temp = (&temp * &temp) % p;
i += 1;
}
let b = mod_pow(&c, &BigInt::from(2u64).pow(m - i - 1), p);
m = i;
c = (&b * &b) % p;
t = (&t * &c) % p;
r = (&r * &b) % p;
}
}
pub fn factorial(n: u32) -> BigInt {
if n == 0 || n == 1 {
return BigInt::one();
}
let mut result = BigInt::one();
for i in 2..=n {
result *= i;
}
result
}
pub fn binomial(n: u32, k: u32) -> BigInt {
if k > n {
return BigInt::zero();
}
if k == 0 || k == n {
return BigInt::one();
}
let k = std::cmp::min(k, n - k);
let mut result = BigInt::one();
for i in 0..k {
result *= n - i;
result /= i + 1;
}
result
}
#[cfg(test)]
mod tests {
use super::*;
fn rat(n: i64, d: i64) -> BigRational {
BigRational::new(BigInt::from(n), BigInt::from(d))
}
fn int_rat(n: i64) -> BigRational {
BigRational::from_integer(BigInt::from(n))
}
#[test]
fn test_floor() {
assert_eq!(floor(&rat(7, 2)), BigInt::from(3));
assert_eq!(floor(&rat(-7, 2)), BigInt::from(-4));
assert_eq!(floor(&int_rat(5)), BigInt::from(5));
}
#[test]
fn test_ceil() {
assert_eq!(ceil(&rat(7, 2)), BigInt::from(4));
assert_eq!(ceil(&rat(-7, 2)), BigInt::from(-3));
assert_eq!(ceil(&int_rat(5)), BigInt::from(5));
}
#[test]
fn test_round() {
assert_eq!(round(&rat(7, 2)), BigInt::from(4)); assert_eq!(round(&rat(5, 2)), BigInt::from(2)); assert_eq!(round(&rat(9, 2)), BigInt::from(4)); }
#[test]
fn test_frac() {
assert_eq!(frac(&rat(7, 2)), rat(1, 2));
assert_eq!(frac(&int_rat(5)), int_rat(0));
}
#[test]
fn test_gcd() {
let a = rat(6, 1);
let b = rat(9, 1);
assert_eq!(gcd(&a, &b), int_rat(3));
}
#[test]
fn test_lcm() {
let a = rat(6, 1);
let b = rat(9, 1);
assert_eq!(lcm(&a, &b), int_rat(18));
}
#[test]
fn test_pow() {
let r = rat(2, 3);
assert_eq!(pow_int(&r, 0), int_rat(1));
assert_eq!(pow_int(&r, 1), rat(2, 3));
assert_eq!(pow_int(&r, 2), rat(4, 9));
assert_eq!(pow_int(&r, -1), rat(3, 2));
}
#[test]
fn test_sign() {
assert_eq!(sign(&rat(5, 1)), 1);
assert_eq!(sign(&rat(-5, 1)), -1);
assert_eq!(sign(&rat(0, 1)), 0);
}
#[test]
fn test_clamp() {
let val = rat(5, 1);
let min_val = rat(0, 1);
let max_val = rat(10, 1);
assert_eq!(clamp(&val, &min_val, &max_val), rat(5, 1));
assert_eq!(clamp(&rat(-5, 1), &min_val, &max_val), rat(0, 1));
assert_eq!(clamp(&rat(15, 1), &min_val, &max_val), rat(10, 1));
}
#[test]
fn test_min_max() {
let a = rat(3, 1);
let b = rat(5, 1);
assert_eq!(min(&a, &b), rat(3, 1));
assert_eq!(max(&a, &b), rat(5, 1));
}
#[test]
fn test_mediant() {
let a = rat(1, 2);
let b = rat(2, 3);
assert_eq!(mediant(&a, &b), rat(3, 5));
}
#[test]
fn test_approx_eq() {
let a = rat(1, 3);
let b = rat(333, 1000);
let epsilon = rat(1, 100);
assert!(approx_eq(&a, &b, &epsilon));
}
#[test]
fn test_gcd_extended() {
let a = BigInt::from(240);
let b = BigInt::from(46);
let (gcd, x, y) = gcd_extended(a.clone(), b.clone());
assert_eq!(gcd, BigInt::from(2));
assert_eq!(&a * &x + &b * &y, gcd);
let a = BigInt::from(17);
let b = BigInt::from(13);
let (gcd, x, y) = gcd_extended(a.clone(), b.clone());
assert_eq!(gcd, BigInt::from(1));
assert_eq!(&a * &x + &b * &y, gcd);
}
#[test]
fn test_continued_fraction() {
let r = rat(22, 7);
let cf = continued_fraction(&r);
assert_eq!(cf, vec![BigInt::from(3), BigInt::from(7)]);
let r = rat(3, 1);
let cf = continued_fraction(&r);
assert_eq!(cf, vec![BigInt::from(3)]);
let r = rat(7, 3);
let cf = continued_fraction(&r);
assert_eq!(cf, vec![BigInt::from(2), BigInt::from(3)]);
}
#[test]
fn test_from_continued_fraction() {
let cf = vec![BigInt::from(3), BigInt::from(7)];
let r = from_continued_fraction(&cf);
assert_eq!(r, rat(22, 7));
let cf = vec![BigInt::from(2), BigInt::from(3)];
let r = from_continued_fraction(&cf);
assert_eq!(r, rat(7, 3));
}
#[test]
fn test_continued_fraction_roundtrip() {
let r = rat(355, 113); let cf = continued_fraction(&r);
let reconstructed = from_continued_fraction(&cf);
assert_eq!(r, reconstructed);
}
#[test]
fn test_convergents() {
let cf = vec![BigInt::from(3), BigInt::from(7)];
let convs = convergents(&cf);
assert_eq!(convs.len(), 2);
assert_eq!(convs[0], rat(3, 1));
assert_eq!(convs[1], rat(22, 7));
}
#[test]
fn test_best_rational_approximation() {
let pi_approx = rat(31416, 10000);
let epsilon = rat(1, 100);
let approx = best_rational_approximation(&pi_approx, &epsilon);
assert!((pi_approx - &approx).abs() <= epsilon);
assert!(approx.denom() < &BigInt::from(1000));
}
#[test]
fn test_mod_pow() {
let base = BigInt::from(2);
let exp = BigInt::from(10);
let m = BigInt::from(1000);
assert_eq!(mod_pow(&base, &exp, &m), BigInt::from(24));
let base = BigInt::from(3);
let exp = BigInt::from(5);
let m = BigInt::from(7);
assert_eq!(mod_pow(&base, &exp, &m), BigInt::from(5));
let base = BigInt::from(0);
let exp = BigInt::from(100);
let m = BigInt::from(7);
assert_eq!(mod_pow(&base, &exp, &m), BigInt::from(0));
let base = BigInt::from(123);
let exp = BigInt::from(0);
let m = BigInt::from(7);
assert_eq!(mod_pow(&base, &exp, &m), BigInt::from(1));
}
#[test]
fn test_mod_inverse() {
let a = BigInt::from(3);
let m = BigInt::from(7);
let inv = mod_inverse(&a, &m).unwrap();
assert_eq!((&a * &inv) % &m, BigInt::from(1));
let a = BigInt::from(15);
let m = BigInt::from(26);
let inv = mod_inverse(&a, &m).unwrap();
assert_eq!((&a * &inv) % &m, BigInt::from(1));
let a = BigInt::from(2);
let m = BigInt::from(4);
assert!(mod_inverse(&a, &m).is_none());
}
#[test]
fn test_chinese_remainder() {
let congruences = vec![
(BigInt::from(2), BigInt::from(3)),
(BigInt::from(3), BigInt::from(5)),
(BigInt::from(2), BigInt::from(7)),
];
let (x, m) = chinese_remainder(&congruences).unwrap();
assert_eq!(m, BigInt::from(105)); assert_eq!(&x % BigInt::from(3), BigInt::from(2));
assert_eq!(&x % BigInt::from(5), BigInt::from(3));
assert_eq!(&x % BigInt::from(7), BigInt::from(2));
let congruences = vec![
(BigInt::from(1), BigInt::from(2)),
(BigInt::from(2), BigInt::from(3)),
];
let (x, m) = chinese_remainder(&congruences).unwrap();
assert_eq!(m, BigInt::from(6));
assert_eq!(&x % BigInt::from(2), BigInt::from(1));
assert_eq!(&x % BigInt::from(3), BigInt::from(2));
}
#[test]
fn test_solve_linear_diophantine() {
let a = BigInt::from(3);
let b = BigInt::from(5);
let c = BigInt::from(1);
let (x, y) = solve_linear_diophantine(&a, &b, &c).unwrap();
assert_eq!(&a * &x + &b * &y, c);
let a = BigInt::from(6);
let b = BigInt::from(9);
let c = BigInt::from(3);
let (x, y) = solve_linear_diophantine(&a, &b, &c).unwrap();
assert_eq!(&a * &x + &b * &y, c);
let a = BigInt::from(2);
let b = BigInt::from(4);
let c = BigInt::from(3);
assert!(solve_linear_diophantine(&a, &b, &c).is_none());
}
#[test]
fn test_mod_inverse_fermat() {
let p = BigInt::from(17); let a = BigInt::from(5);
let inv = mod_inverse(&a, &p).unwrap();
assert_eq!((&a * &inv) % &p, BigInt::from(1));
let fermat_inv = mod_pow(&a, &(&p - BigInt::from(2)), &p);
assert_eq!(inv, fermat_inv);
}
#[test]
fn test_is_prime() {
assert!(is_prime(&BigInt::from(2), 5));
assert!(is_prime(&BigInt::from(3), 5));
assert!(is_prime(&BigInt::from(5), 5));
assert!(is_prime(&BigInt::from(7), 5));
assert!(is_prime(&BigInt::from(11), 5));
assert!(is_prime(&BigInt::from(17), 5));
assert!(is_prime(&BigInt::from(97), 5));
assert!(!is_prime(&BigInt::from(1), 5));
assert!(!is_prime(&BigInt::from(4), 5));
assert!(!is_prime(&BigInt::from(6), 5));
assert!(!is_prime(&BigInt::from(8), 5));
assert!(!is_prime(&BigInt::from(9), 5));
assert!(!is_prime(&BigInt::from(15), 5));
assert!(!is_prime(&BigInt::from(100), 5));
assert!(is_prime(&BigInt::from(1009), 10));
}
#[test]
fn test_trial_division() {
let n = BigInt::from(60);
let factors = trial_division(&n, 100);
assert_eq!(factors.len(), 4);
assert_eq!(factors[0], BigInt::from(2));
assert_eq!(factors[1], BigInt::from(2));
assert_eq!(factors[2], BigInt::from(3));
assert_eq!(factors[3], BigInt::from(5));
let n = BigInt::from(17);
let factors = trial_division(&n, 100);
assert_eq!(factors.len(), 0);
let n = BigInt::from(100);
let factors = trial_division(&n, 100);
assert_eq!(factors.len(), 4);
}
#[test]
fn test_pollard_rho() {
let n = BigInt::from(8051);
if let Some(factor) = pollard_rho(&n) {
assert!(n.clone() % &factor == BigInt::from(0));
assert!(factor > BigInt::from(1) && factor < n);
}
let n = BigInt::from(15);
if let Some(factor) = pollard_rho(&n) {
assert!(n.clone() % &factor == BigInt::from(0));
assert!(factor > BigInt::from(1) && factor < n);
}
}
#[test]
fn test_jacobi_symbol() {
assert_eq!(jacobi_symbol(&BigInt::from(2), &BigInt::from(5)), -1);
assert_eq!(jacobi_symbol(&BigInt::from(3), &BigInt::from(5)), -1);
assert_eq!(jacobi_symbol(&BigInt::from(4), &BigInt::from(5)), 1);
assert_eq!(jacobi_symbol(&BigInt::from(1), &BigInt::from(5)), 1);
assert_eq!(jacobi_symbol(&BigInt::from(0), &BigInt::from(5)), 0);
assert_eq!(jacobi_symbol(&BigInt::from(2), &BigInt::from(15)), 1);
}
#[test]
fn test_legendre_symbol() {
assert_eq!(legendre_symbol(&BigInt::from(1), &BigInt::from(5)), 1);
assert_eq!(legendre_symbol(&BigInt::from(4), &BigInt::from(5)), 1);
assert_eq!(legendre_symbol(&BigInt::from(2), &BigInt::from(5)), -1);
assert_eq!(legendre_symbol(&BigInt::from(3), &BigInt::from(5)), -1);
assert_eq!(legendre_symbol(&BigInt::from(1), &BigInt::from(7)), 1);
assert_eq!(legendre_symbol(&BigInt::from(2), &BigInt::from(7)), 1);
assert_eq!(legendre_symbol(&BigInt::from(4), &BigInt::from(7)), 1);
assert_eq!(legendre_symbol(&BigInt::from(3), &BigInt::from(7)), -1);
}
#[test]
fn test_euler_totient() {
assert_eq!(euler_totient(&BigInt::from(1)), BigInt::from(1));
assert_eq!(euler_totient(&BigInt::from(2)), BigInt::from(1)); assert_eq!(euler_totient(&BigInt::from(9)), BigInt::from(6)); assert_eq!(euler_totient(&BigInt::from(10)), BigInt::from(4)); assert_eq!(euler_totient(&BigInt::from(12)), BigInt::from(4)); assert_eq!(euler_totient(&BigInt::from(15)), BigInt::from(8)); }
#[test]
fn test_is_perfect_power() {
assert_eq!(
is_perfect_power(&BigInt::from(8)),
Some((BigInt::from(2), 3))
);
assert_eq!(
is_perfect_power(&BigInt::from(27)),
Some((BigInt::from(3), 3))
);
let result = is_perfect_power(&BigInt::from(16));
assert!(result.is_some());
let (base, exp) = result.unwrap();
assert_eq!(base.pow(exp), BigInt::from(16));
assert_eq!(is_perfect_power(&BigInt::from(10)), None);
assert_eq!(is_perfect_power(&BigInt::from(15)), None);
}
#[test]
fn test_is_square_free() {
assert!(is_square_free(&BigInt::from(1)));
assert!(is_square_free(&BigInt::from(6))); assert!(is_square_free(&BigInt::from(10))); assert!(is_square_free(&BigInt::from(15)));
assert!(!is_square_free(&BigInt::from(4))); assert!(!is_square_free(&BigInt::from(12))); assert!(!is_square_free(&BigInt::from(18))); assert!(!is_square_free(&BigInt::from(8))); }
#[test]
fn test_divisor_count() {
assert_eq!(divisor_count(&BigInt::from(1)), BigInt::from(1));
assert_eq!(divisor_count(&BigInt::from(12)), BigInt::from(6)); assert_eq!(divisor_count(&BigInt::from(28)), BigInt::from(6)); assert_eq!(divisor_count(&BigInt::from(6)), BigInt::from(4)); }
#[test]
fn test_divisor_sum() {
assert_eq!(divisor_sum(&BigInt::from(1)), BigInt::from(1));
assert_eq!(divisor_sum(&BigInt::from(6)), BigInt::from(12)); assert_eq!(divisor_sum(&BigInt::from(12)), BigInt::from(28)); assert_eq!(divisor_sum(&BigInt::from(28)), BigInt::from(56)); }
#[test]
fn test_mobius() {
assert_eq!(mobius(&BigInt::from(1)), 1);
assert_eq!(mobius(&BigInt::from(2)), -1); assert_eq!(mobius(&BigInt::from(6)), 1); assert_eq!(mobius(&BigInt::from(30)), -1); assert_eq!(mobius(&BigInt::from(12)), 0); assert_eq!(mobius(&BigInt::from(4)), 0); }
#[test]
fn test_carmichael_lambda() {
assert_eq!(carmichael_lambda(&BigInt::from(1)), BigInt::from(1));
assert_eq!(carmichael_lambda(&BigInt::from(8)), BigInt::from(2)); assert_eq!(carmichael_lambda(&BigInt::from(15)), BigInt::from(4)); assert_eq!(carmichael_lambda(&BigInt::from(9)), BigInt::from(6)); }
#[test]
fn test_gcd_binary() {
assert_eq!(
gcd_binary(BigInt::from(48), BigInt::from(18)),
BigInt::from(6)
);
assert_eq!(
gcd_binary(BigInt::from(100), BigInt::from(35)),
BigInt::from(5)
);
assert_eq!(
gcd_binary(BigInt::from(17), BigInt::from(19)),
BigInt::from(1)
);
assert_eq!(
gcd_binary(BigInt::from(0), BigInt::from(5)),
BigInt::from(5)
);
assert_eq!(
gcd_binary(BigInt::from(5), BigInt::from(0)),
BigInt::from(5)
);
let a = BigInt::from(12345);
let b = BigInt::from(67890);
assert_eq!(gcd_binary(a.clone(), b.clone()), gcd_bigint(a, b));
}
#[test]
fn test_tonelli_shanks() {
let result = tonelli_shanks(&BigInt::from(4), &BigInt::from(7));
assert!(result.is_some());
if let Some(x) = result {
let p = BigInt::from(7);
assert_eq!((&x * &x) % &p, BigInt::from(4) % &p);
}
let result = tonelli_shanks(&BigInt::from(2), &BigInt::from(7));
assert!(result.is_some());
if let Some(x) = result {
let p = BigInt::from(7);
assert_eq!((&x * &x) % &p, BigInt::from(2));
}
assert!(tonelli_shanks(&BigInt::from(3), &BigInt::from(7)).is_none());
let result = tonelli_shanks(&BigInt::from(5), &BigInt::from(11));
assert!(result.is_some());
if let Some(x) = result {
let p = BigInt::from(11);
assert_eq!((&x * &x) % &p, BigInt::from(5));
}
}
#[test]
fn test_factorial() {
assert_eq!(factorial(0), BigInt::from(1));
assert_eq!(factorial(1), BigInt::from(1));
assert_eq!(factorial(5), BigInt::from(120));
assert_eq!(factorial(10), BigInt::from(3628800));
assert_eq!(factorial(3), BigInt::from(6));
}
#[test]
fn test_binomial() {
assert_eq!(binomial(5, 2), BigInt::from(10));
assert_eq!(binomial(10, 3), BigInt::from(120));
assert_eq!(binomial(5, 0), BigInt::from(1));
assert_eq!(binomial(5, 5), BigInt::from(1));
assert_eq!(binomial(7, 3), BigInt::from(35));
assert_eq!(binomial(10, 5), BigInt::from(252));
assert_eq!(binomial(5, 6), BigInt::from(0)); }
}