#![deny(missing_docs)]
use num_bigint::BigUint;
use num_traits::One;
use std::collections::BTreeMap;
use std::fmt;
use std::ops::Mul;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FactorialError {
NotAFactorial(u128),
Overflow,
}
impl fmt::Display for FactorialError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FactorialError::NotAFactorial(v) => {
write!(f, "{v} is not the factorial of any integer")
}
FactorialError::Overflow => {
write!(f, "value is too large to be represented as a factorial")
}
}
}
}
impl std::error::Error for FactorialError {}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct SymbolicFactorial {
factors: BTreeMap<u64, u64>,
}
impl SymbolicFactorial {
pub fn factors(&self) -> &BTreeMap<u64, u64> {
&self.factors
}
pub fn exponent_of(&self, prime: u64) -> u64 {
self.factors.get(&prime).copied().unwrap_or(0)
}
pub fn multiply(&self, rhs: &Self) -> Self {
let mut factors = self.factors.clone();
for (&p, &e) in &rhs.factors {
*factors.entry(p).or_insert(0) += e;
}
Self { factors }
}
pub fn checked_divide(&self, rhs: &Self) -> Option<Self> {
let mut factors = self.factors.clone();
for (&p, &e) in &rhs.factors {
let cur = factors.get(&p).copied().unwrap_or(0);
if cur < e {
return None;
}
let next = cur - e;
if next == 0 {
factors.remove(&p);
} else {
factors.insert(p, next);
}
}
Some(Self { factors })
}
pub fn pow(&self, k: u32) -> Self {
let factors = self
.factors
.iter()
.map(|(&p, &e)| (p, e * k as u64))
.collect();
Self { factors }
}
pub fn to_biguint(&self) -> BigUint {
self.factors
.iter()
.fold(BigUint::one(), |acc, (&p, &e)| {
acc * BigUint::from(p).pow(e as u32)
})
}
}
impl Mul for &SymbolicFactorial {
type Output = SymbolicFactorial;
fn mul(self, rhs: &SymbolicFactorial) -> SymbolicFactorial {
self.multiply(rhs)
}
}
impl fmt::Display for SymbolicFactorial {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.factors.is_empty() {
return write!(f, "1");
}
let terms: Vec<String> = self
.factors
.iter()
.map(|(p, e)| {
if *e == 1 {
p.to_string()
} else {
format!("{p}^{e}")
}
})
.collect();
write!(f, "{}", terms.join(" \u{d7} "))
}
}
pub struct FactorialEngine {
primes_cache: Vec<u64>,
}
impl Default for FactorialEngine {
fn default() -> Self {
Self::new(None)
}
}
impl FactorialEngine {
pub fn new(sieve_up_to: Option<u64>) -> Self {
let mut engine = FactorialEngine {
primes_cache: Vec::new(),
};
if let Some(limit) = sieve_up_to {
engine.sieve_primes(limit);
}
engine
}
fn sieve_primes(&mut self, limit: u64) {
if limit < 2 {
return;
}
let mut is_prime = vec![true; (limit + 1) as usize];
is_prime[0] = false;
is_prime[1] = false;
for p in 2..=(limit as f64).sqrt() as u64 {
if is_prime[p as usize] {
for i in (p * p..=limit).step_by(p as usize) {
is_prime[i as usize] = false;
}
}
}
self.primes_cache = is_prime
.iter()
.enumerate()
.filter(|&(_, &is_p)| is_p)
.map(|(p, _)| p as u64)
.collect();
}
fn calculate_exponent(&self, n: u64, p: u64) -> u64 {
let mut exponent = 0;
let mut p_power = p;
while p_power <= n {
exponent += n / p_power;
if p > u64::MAX / p_power {
break;
}
p_power *= p;
}
exponent
}
pub fn symbolic_factorial(&mut self, n: u64) -> SymbolicFactorial {
if n < 2 {
return SymbolicFactorial::default(); }
if self.primes_cache.last().is_none_or(|&max_p| max_p < n) {
self.sieve_primes(n);
}
let mut factors = BTreeMap::new();
for &p in self.primes_cache.iter().take_while(|&&pr| pr <= n) {
let exponent = self.calculate_exponent(n, p);
if exponent > 0 {
factors.insert(p, exponent);
}
}
SymbolicFactorial { factors }
}
pub fn factorial_biguint(&mut self, n: u64) -> BigUint {
self.symbolic_factorial(n).to_biguint()
}
pub fn binomial(&mut self, n: u64, k: u64) -> Option<BigUint> {
if k > n {
return None;
}
let numerator = self.symbolic_factorial(n);
let denominator = self
.symbolic_factorial(k)
.multiply(&self.symbolic_factorial(n - k));
numerator.checked_divide(&denominator).map(|result| result.to_biguint())
}
#[deprecated(since = "0.3.0", note = "use `symbolic_factorial` instead")]
pub fn get_factorial_factorization(&mut self, n: u64) -> std::collections::HashMap<u64, u64> {
self.symbolic_factorial(n).factors.into_iter().collect()
}
}
pub fn reverse_factorial(value: u128) -> Result<u64, FactorialError> {
let mut n: u64 = 0;
let mut acc: u128 = 1;
while acc < value {
n = n.checked_add(1).ok_or(FactorialError::Overflow)?;
acc = acc.checked_mul(n as u128).ok_or(FactorialError::Overflow)?;
}
if acc == value {
Ok(n)
} else {
Err(FactorialError::NotAFactorial(value))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn symbolic_factorial_of_fifty_matches_known_exponents() {
let mut engine = FactorialEngine::new(Some(100));
let factors = engine.symbolic_factorial(50);
assert_eq!(factors.exponent_of(2), 47);
assert_eq!(factors.exponent_of(3), 22);
}
#[test]
fn symbolic_factorial_of_zero_and_one_is_empty() {
let mut engine = FactorialEngine::new(None);
assert!(engine.symbolic_factorial(0).factors().is_empty());
assert!(engine.symbolic_factorial(1).factors().is_empty());
}
#[test]
fn symbolic_factorial_display_format() {
let mut engine = FactorialEngine::new(None);
let factors = engine.symbolic_factorial(6); assert_eq!(factors.to_string(), "2^4 \u{d7} 3^2 \u{d7} 5");
}
#[test]
fn factorial_biguint_matches_expected_value() {
let mut engine = FactorialEngine::new(None);
assert_eq!(engine.factorial_biguint(10), BigUint::from(3628800u64));
}
#[test]
fn multiply_and_checked_divide_round_trip() {
let mut engine = FactorialEngine::new(None);
let five = engine.symbolic_factorial(5);
let three = engine.symbolic_factorial(3);
let product = five.multiply(&three);
assert_eq!(product.to_biguint(), BigUint::from(120u64 * 6u64));
assert_eq!(product.checked_divide(&three).unwrap(), five);
assert!(three.checked_divide(&five).is_none()); }
#[test]
fn pow_multiplies_exponents() {
let mut engine = FactorialEngine::new(None);
let three = engine.symbolic_factorial(3); let squared = three.pow(2);
assert_eq!(squared.to_biguint(), BigUint::from(36u64));
}
#[test]
fn binomial_matches_known_values() {
let mut engine = FactorialEngine::new(None);
assert_eq!(engine.binomial(5, 2), Some(BigUint::from(10u64)));
assert_eq!(engine.binomial(10, 0), Some(BigUint::from(1u64)));
assert_eq!(engine.binomial(3, 5), None);
}
#[test]
fn reverse_factorial_finds_known_values() {
assert_eq!(reverse_factorial(1), Ok(0)); assert_eq!(reverse_factorial(120), Ok(5));
assert_eq!(reverse_factorial(3628800), Ok(10));
}
#[test]
fn reverse_factorial_rejects_non_factorials() {
assert_eq!(
reverse_factorial(121),
Err(FactorialError::NotAFactorial(121))
);
assert_eq!(reverse_factorial(0), Err(FactorialError::NotAFactorial(0)));
}
}