#[allow(unused_imports)]
use crate::prelude::*;
use core::cmp::Ordering;
use num_bigint::BigInt;
use num_integer::Integer;
use num_rational::BigRational;
use num_traits::{One, Signed, Zero};
#[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))
}
#[cfg(feature = "std")]
pub fn is_prime(n: &BigInt, k: usize) -> bool {
use num_traits::One;
use rand::RngExt;
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
}
#[cfg(feature = "std")]
pub fn pollard_rho(n: &BigInt) -> Option<BigInt> {
use rand::RngExt;
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;
}
}
core::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)
}
struct SplitMix64(u64);
impl SplitMix64 {
fn new(seed: u64) -> Self {
SplitMix64(seed)
}
fn next_u64(&mut self) -> u64 {
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = self.0;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
}
fn seed_from_bigint(n: &BigInt, salt: u64) -> u64 {
let bytes = n.to_signed_bytes_le();
let mut h: u64 = 0xcbf2_9ce4_8422_2325 ^ salt;
for &b in &bytes {
h ^= b as u64;
h = h.wrapping_mul(0x0000_0100_0000_01B3);
}
h
}
const SMALL_WITNESS_PRIMES: [u64; 20] = [
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
];
fn is_prime_fixed_witnesses(n: &BigInt) -> bool {
let zero = BigInt::zero();
let one = BigInt::one();
let two = BigInt::from(2);
if n <= &one {
return false;
}
if n == &two {
return true;
}
if n.is_even() {
return false;
}
for &p in &SMALL_WITNESS_PRIMES {
let bp = BigInt::from(p);
if n == &bp {
return true;
}
if n > &bp && (n % &bp) == zero {
return false;
}
}
let n_minus_1 = n - &one;
let mut d = n_minus_1.clone();
let mut r: u32 = 0;
while d.is_even() {
d >>= 1;
r += 1;
}
'witness: for &a_val in &SMALL_WITNESS_PRIMES {
let a = BigInt::from(a_val);
if &a >= n {
continue;
}
let mut x = mod_pow(&a, &d, n);
if x == one || x == n_minus_1 {
continue 'witness;
}
for _ in 0..r.saturating_sub(1) {
x = mod_pow(&x, &two, n);
if x == n_minus_1 {
continue 'witness;
}
}
return false;
}
true
}
fn pollard_rho_attempt(n: &BigInt, attempt: u64) -> Option<BigInt> {
let one = BigInt::one();
if n.is_even() {
return Some(BigInt::from(2));
}
let mut rng = SplitMix64::new(seed_from_bigint(n, attempt));
let x0 = BigInt::from(rng.next_u64()) % n;
let mut c = BigInt::from(rng.next_u64()) % n;
if c.is_zero() {
c = one.clone();
}
let f = |x: &BigInt| -> BigInt { (x * x + &c) % n };
let mut x = x0.clone();
let mut y = x0;
let mut d = one.clone();
let max_iterations = 100_000;
let mut iterations = 0;
while d == 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 != one { Some(d) } else { None }
}
fn factorize_verified(n: &BigInt) -> Result<Vec<BigInt>, (Vec<BigInt>, BigInt)> {
let one = BigInt::one();
debug_assert!(n > &one, "factorize_verified expects n > 1");
let mut factors = Vec::new();
let mut remaining = n.clone();
while remaining.is_even() {
factors.push(BigInt::from(2));
remaining >>= 1;
}
let mut d = BigInt::from(3);
let small_limit = BigInt::from(1_000_000u64);
while &d * &d <= remaining && d <= small_limit {
while (&remaining % &d).is_zero() {
factors.push(d.clone());
remaining /= &d;
}
d += BigInt::from(2);
}
const MAX_POLLARD_ATTEMPTS: u32 = 64;
let mut attempts_left = MAX_POLLARD_ATTEMPTS;
let mut stack = vec![remaining];
while let Some(m) = stack.pop() {
if m <= one {
continue;
}
if is_prime_fixed_witnesses(&m) {
factors.push(m);
continue;
}
let mut split = None;
let mut attempt_idx: u64 = 0;
while attempts_left > 0 {
attempts_left -= 1;
attempt_idx += 1;
if let Some(f) = pollard_rho_attempt(&m, attempt_idx)
&& f > one
&& f < m
{
split = Some(f);
break;
}
}
match split {
Some(f) => {
let cofactor = &m / &f;
stack.push(f);
stack.push(cofactor);
}
None => {
factors.sort();
return Err((factors, m));
}
}
}
factors.sort();
Ok(factors)
}
fn factorize_or_best_effort(n: &BigInt) -> Vec<BigInt> {
match factorize_verified(n) {
Ok(factors) => factors,
Err((mut factors, residual)) => {
factors.push(residual);
factors.sort();
factors
}
}
}
pub fn factorize(n: &BigInt) -> Result<Vec<BigInt>, String> {
if n <= &BigInt::one() {
return Ok(Vec::new());
}
factorize_verified(n).map_err(|(_partial, residual)| {
format!(
"factorize: exhausted retry budget without fully factoring residual cofactor {residual}"
)
})
}
pub fn euler_totient(n: &BigInt) -> BigInt {
if n <= &BigInt::one() {
return BigInt::one();
}
let mut result = n.clone();
let mut seen_primes = crate::prelude::HashSet::new();
for factor in factorize_or_best_effort(n) {
if seen_primes.insert(factor.clone()) {
result = result * (&factor - BigInt::one()) / &factor;
}
}
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 = factorize_or_best_effort(n);
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 = factorize_or_best_effort(n);
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 = factorize_or_best_effort(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;
}
let factors = factorize_or_best_effort(n);
let mut unique_primes = crate::prelude::HashSet::new();
for factor in factors {
if !unique_primes.insert(factor) {
return 0; }
}
if unique_primes.len() % 2 == 0 { 1 } else { -1 }
}
pub fn carmichael_lambda(n: &BigInt) -> BigInt {
if n <= &BigInt::one() {
return BigInt::one();
}
let factors = factorize_or_best_effort(n);
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 {
core::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 = core::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;