use super::uint::BigUint;
pub fn factorial(n: u64) -> BigUint {
if n == 0 {
return BigUint::one();
}
balanced_product(1, n)
}
fn balanced_product(lo: u64, hi: u64) -> BigUint {
debug_assert!(lo <= hi, "balanced_product: lo={lo} > hi={hi}");
if lo == hi {
return BigUint::from(lo);
}
if hi == lo + 1 {
return BigUint::from(lo) * BigUint::from(hi);
}
let mid = lo + (hi - lo) / 2;
let left = balanced_product(lo, mid);
let right = balanced_product(mid + 1, hi);
left * right
}
#[cfg(test)]
mod tests {
use super::*;
fn bu(n: u64) -> BigUint {
BigUint::from(n)
}
#[test]
fn factorial_base_cases() {
assert_eq!(factorial(0), bu(1));
assert_eq!(factorial(1), bu(1));
assert_eq!(factorial(2), bu(2));
assert_eq!(factorial(3), bu(6));
}
#[test]
fn factorial_small_exact() {
assert_eq!(factorial(5), bu(120));
assert_eq!(factorial(10), bu(3_628_800));
assert_eq!(factorial(20), bu(2_432_902_008_176_640_000u64));
}
#[test]
fn factorial_cross_validate_naive() {
for n in 0u64..=200 {
let via_fn = factorial(n);
let naive: BigUint = (1..=n).fold(BigUint::one(), |acc, k| acc * BigUint::from(k));
assert_eq!(via_fn, naive, "factorial({}) mismatch", n);
}
}
#[test]
fn factorial_100_digit_count() {
let f100 = factorial(100);
let decimal = f100.to_string();
assert_eq!(decimal.len(), 158, "100! should have 158 decimal digits");
}
}