mathhook_core/functions/special/
factorial.rs1use crate::core::{Expression, Number};
4use num_bigint::BigInt;
5use num_traits::One;
6
7pub fn factorial(arg: &Expression) -> Expression {
36 match arg {
37 Expression::Number(Number::Integer(n)) if *n >= 0 => {
38 Expression::Number(compute_factorial(*n))
39 }
40 Expression::Number(Number::Integer(n)) if *n < 0 => {
41 Expression::function("factorial", vec![arg.clone()])
42 }
43 _ => Expression::function("factorial", vec![arg.clone()]),
44 }
45}
46
47fn compute_factorial(n: i64) -> Number {
49 if n == 0 || n == 1 {
50 return Number::Integer(1);
51 }
52
53 let mut result: i64 = 1;
55 for i in 2..=n {
56 match result.checked_mul(i) {
57 Some(new_result) => result = new_result,
58 None => {
59 return Number::BigInteger(Box::new(compute_factorial_bigint(n)));
61 }
62 }
63 }
64 Number::Integer(result)
65}
66
67fn compute_factorial_bigint(n: i64) -> BigInt {
69 if n == 0 || n == 1 {
70 return BigInt::one();
71 }
72
73 let mut result = BigInt::one();
74 for i in 2..=n {
75 result *= BigInt::from(i);
76 }
77 result
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn test_factorial_zero() {
86 let result = factorial(&Expression::integer(0));
87 assert_eq!(result, Expression::Number(Number::Integer(1)));
88 }
89
90 #[test]
91 fn test_factorial_one() {
92 let result = factorial(&Expression::integer(1));
93 assert_eq!(result, Expression::Number(Number::Integer(1)));
94 }
95
96 #[test]
97 fn test_factorial_five() {
98 let result = factorial(&Expression::integer(5));
99 assert_eq!(result, Expression::Number(Number::Integer(120)));
100 }
101
102 #[test]
103 fn test_factorial_ten() {
104 let result = factorial(&Expression::integer(10));
105 assert_eq!(result, Expression::Number(Number::Integer(3628800)));
106 }
107
108 #[test]
109 fn test_factorial_large() {
110 let result = factorial(&Expression::integer(21));
112 assert!(matches!(result, Expression::Number(Number::BigInteger(_))));
113
114 if let Expression::Number(Number::BigInteger(bi)) = result {
115 let expected: BigInt = "51090942171709440000".parse().unwrap();
117 assert_eq!(*bi, expected);
118 }
119 }
120}