use crate::core::{Expression, Number};
use num_bigint::BigInt;
use num_traits::One;
pub fn factorial(arg: &Expression) -> Expression {
match arg {
Expression::Number(Number::Integer(n)) if *n >= 0 => {
Expression::Number(compute_factorial(*n))
}
Expression::Number(Number::Integer(n)) if *n < 0 => {
Expression::function("factorial", vec![arg.clone()])
}
_ => Expression::function("factorial", vec![arg.clone()]),
}
}
fn compute_factorial(n: i64) -> Number {
if n == 0 || n == 1 {
return Number::Integer(1);
}
let mut result: i64 = 1;
for i in 2..=n {
match result.checked_mul(i) {
Some(new_result) => result = new_result,
None => {
return Number::BigInteger(Box::new(compute_factorial_bigint(n)));
}
}
}
Number::Integer(result)
}
fn compute_factorial_bigint(n: i64) -> BigInt {
if n == 0 || n == 1 {
return BigInt::one();
}
let mut result = BigInt::one();
for i in 2..=n {
result *= BigInt::from(i);
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_factorial_zero() {
let result = factorial(&Expression::integer(0));
assert_eq!(result, Expression::Number(Number::Integer(1)));
}
#[test]
fn test_factorial_one() {
let result = factorial(&Expression::integer(1));
assert_eq!(result, Expression::Number(Number::Integer(1)));
}
#[test]
fn test_factorial_five() {
let result = factorial(&Expression::integer(5));
assert_eq!(result, Expression::Number(Number::Integer(120)));
}
#[test]
fn test_factorial_ten() {
let result = factorial(&Expression::integer(10));
assert_eq!(result, Expression::Number(Number::Integer(3628800)));
}
#[test]
fn test_factorial_large() {
let result = factorial(&Expression::integer(21));
assert!(matches!(result, Expression::Number(Number::BigInteger(_))));
if let Expression::Number(Number::BigInteger(bi)) = result {
let expected: BigInt = "51090942171709440000".parse().unwrap();
assert_eq!(*bi, expected);
}
}
}