use num_bigint::{BigUint, ToBigUint};
use num_iter::range_inclusive;
pub fn factorial_big<T: ToBigUint>(n: T) -> Option<BigUint> {
let n = n.to_biguint()?;
let biguint_1 = 1.to_biguint()?;
let biguint_2 = 2.to_biguint()?;
if n <= biguint_1 {
Some(biguint_1)
} else if n == biguint_2 {
Some(biguint_2)
} else {
let mut factorial_big = biguint_2;
for i in range_inclusive(3.to_biguint()?, n) {
factorial_big *= i;
}
Some(factorial_big)
}
}
pub fn permutation_big<T: ToBigUint>(n: T, k: T) -> Option<BigUint> {
let n = n.to_biguint()?;
let k = k.to_biguint()?;
let biguint_0 = 0.to_biguint()?;
let biguint_1 = 1.to_biguint()?;
if n < k {
Some(biguint_0)
} else if n == biguint_0 || k == biguint_0 {
Some(biguint_1)
} else {
let mut permutation_big = &n - &k + &biguint_1;
for i in range_inclusive(&n - &k + 2.to_biguint()?, n) {
permutation_big *= i;
}
Some(permutation_big)
}
}
pub fn combination_big<T: ToBigUint>(n: T, k: T) -> Option<BigUint> {
let n = n.to_biguint()?;
let k = k.to_biguint()?;
let biguint_0 = 0.to_biguint()?;
let biguint_1 = 1.to_biguint()?;
if n < k {
Some(biguint_0)
} else if n == biguint_0 || k == biguint_0 || n == k {
Some(biguint_1)
} else if k == biguint_1 {
Some(n)
} else {
Some(permutation_big(n, k.clone())? / factorial_big(k)?)
}
}
pub fn gcd_big<T: ToBigUint>(n: T, m: T) -> Option<BigUint> {
let mut n = n.to_biguint()?;
let mut m = m.to_biguint()?;
let biguint_0 = 0.to_biguint()?;
if n == biguint_0 {
return Some(m);
}
if m == biguint_0 {
return Some(n);
}
let gcd_exponent_on_two = (&n | &m).trailing_zeros()?;
n >>= n.trailing_zeros()?;
m >>= m.trailing_zeros()?;
while n != m {
if n < m {
std::mem::swap(&mut n, &mut m);
}
n -= &m;
n >>= n.trailing_zeros()?;
}
Some(n << gcd_exponent_on_two)
}
pub fn lcm_big<T: ToBigUint>(n: T, m: T) -> Option<BigUint> {
let biguint_n = n.to_biguint()?;
let biguint_m = m.to_biguint()?;
Some(biguint_n / gcd_big(n, m)? * biguint_m)
}