mathhook_core/algebra/simplification/
special.rs

1//! Special Function Simplification Strategies
2//!
3//! Implements algebraic rewrite rules for special functions (gamma, factorial).
4
5use super::strategy::SimplificationStrategy;
6use crate::core::{Expression, Number};
7use num_bigint::BigInt;
8use num_traits::{One, ToPrimitive};
9
10/// Gamma function simplification strategy
11pub struct GammaSimplificationStrategy;
12
13impl GammaSimplificationStrategy {
14    fn factorial_i64(&self, n: u64) -> BigInt {
15        if n <= 1 {
16            BigInt::one()
17        } else {
18            let mut result = BigInt::one();
19            for i in 2..=n {
20                result *= BigInt::from(i);
21            }
22            result
23        }
24    }
25}
26
27impl SimplificationStrategy for GammaSimplificationStrategy {
28    fn simplify(&self, args: &[Expression]) -> Expression {
29        if args.len() == 1 {
30            match &args[0] {
31                Expression::Number(Number::Integer(n)) => {
32                    if let Some(val) = n.to_i64() {
33                        if val > 0 && val <= 10 {
34                            let factorial_result = self.factorial_i64((val - 1) as u64);
35                            Expression::big_integer(factorial_result)
36                        } else {
37                            Expression::function("gamma", args.to_vec())
38                        }
39                    } else {
40                        Expression::function("gamma", args.to_vec())
41                    }
42                }
43                _ => Expression::function("gamma", args.to_vec()),
44            }
45        } else {
46            Expression::function("gamma", args.to_vec())
47        }
48    }
49
50    fn applies_to(&self, args: &[Expression]) -> bool {
51        args.len() == 1
52    }
53
54    fn name(&self) -> &str {
55        "GammaSimplificationStrategy"
56    }
57}
58
59/// Factorial function simplification strategy
60///
61/// Delegates to the official factorial implementation
62pub struct FactorialSimplificationStrategy;
63
64impl SimplificationStrategy for FactorialSimplificationStrategy {
65    fn simplify(&self, args: &[Expression]) -> Expression {
66        if args.len() == 1 {
67            crate::functions::special::factorial(&args[0])
68        } else {
69            Expression::function("factorial", args.to_vec())
70        }
71    }
72
73    fn applies_to(&self, args: &[Expression]) -> bool {
74        args.len() == 1
75    }
76
77    fn name(&self) -> &str {
78        "FactorialSimplificationStrategy"
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use crate::expr;
86
87    #[test]
88    fn test_gamma_of_one() {
89        let strategy = GammaSimplificationStrategy;
90        let result = strategy.simplify(&[expr!(1)]);
91        assert_eq!(result, expr!(1));
92    }
93
94    #[test]
95    fn test_gamma_of_two() {
96        let strategy = GammaSimplificationStrategy;
97        let result = strategy.simplify(&[expr!(2)]);
98        assert_eq!(result, expr!(1));
99    }
100
101    #[test]
102    fn test_gamma_of_three() {
103        let strategy = GammaSimplificationStrategy;
104        let result = strategy.simplify(&[expr!(3)]);
105        assert_eq!(result, expr!(2));
106    }
107
108    #[test]
109    fn test_gamma_of_four() {
110        let strategy = GammaSimplificationStrategy;
111        let result = strategy.simplify(&[expr!(4)]);
112        assert_eq!(result, expr!(6));
113    }
114
115    #[test]
116    fn test_gamma_of_five() {
117        let strategy = GammaSimplificationStrategy;
118        let result = strategy.simplify(&[expr!(5)]);
119        assert_eq!(result, expr!(24));
120    }
121
122    #[test]
123    fn test_factorial_of_zero() {
124        let strategy = FactorialSimplificationStrategy;
125        let result = strategy.simplify(&[expr!(0)]);
126        assert_eq!(result, expr!(1));
127    }
128
129    #[test]
130    fn test_factorial_of_one() {
131        let strategy = FactorialSimplificationStrategy;
132        let result = strategy.simplify(&[expr!(1)]);
133        assert_eq!(result, expr!(1));
134    }
135
136    #[test]
137    fn test_factorial_of_five() {
138        let strategy = FactorialSimplificationStrategy;
139        let result = strategy.simplify(&[expr!(5)]);
140        assert_eq!(result, expr!(120));
141    }
142
143    #[test]
144    fn test_factorial_of_ten() {
145        let strategy = FactorialSimplificationStrategy;
146        let result = strategy.simplify(&[expr!(10)]);
147        assert_eq!(result, expr!(3628800));
148    }
149}