mathhook_core/algebra/simplification/
trigonometric.rs

1//! Trigonometric Function Simplification Strategies
2//!
3//! Implements algebraic rewrite rules for trigonometric functions (sin, cos, tan, etc.).
4
5use super::strategy::SimplificationStrategy;
6use crate::core::Expression;
7
8/// Sine function simplification strategy
9pub struct SinSimplificationStrategy;
10
11impl SimplificationStrategy for SinSimplificationStrategy {
12    fn simplify(&self, args: &[Expression]) -> Expression {
13        if args.len() == 1 {
14            let arg = &args[0];
15
16            if arg.is_zero() {
17                Expression::integer(0)
18            } else {
19                Expression::function("sin", args.to_vec())
20            }
21        } else {
22            Expression::function("sin", args.to_vec())
23        }
24    }
25
26    fn applies_to(&self, args: &[Expression]) -> bool {
27        args.len() == 1
28    }
29
30    fn name(&self) -> &str {
31        "SinSimplificationStrategy"
32    }
33}
34
35/// Cosine function simplification strategy
36pub struct CosSimplificationStrategy;
37
38impl SimplificationStrategy for CosSimplificationStrategy {
39    fn simplify(&self, args: &[Expression]) -> Expression {
40        if args.len() == 1 {
41            let arg = &args[0];
42
43            if arg.is_zero() {
44                Expression::integer(1)
45            } else {
46                Expression::function("cos", args.to_vec())
47            }
48        } else {
49            Expression::function("cos", args.to_vec())
50        }
51    }
52
53    fn applies_to(&self, args: &[Expression]) -> bool {
54        args.len() == 1
55    }
56
57    fn name(&self) -> &str {
58        "CosSimplificationStrategy"
59    }
60}
61
62/// Tangent function simplification strategy
63pub struct TanSimplificationStrategy;
64
65impl SimplificationStrategy for TanSimplificationStrategy {
66    fn simplify(&self, args: &[Expression]) -> Expression {
67        if args.len() == 1 {
68            let arg = &args[0];
69
70            if arg.is_zero() {
71                Expression::integer(0)
72            } else {
73                Expression::function("tan", args.to_vec())
74            }
75        } else {
76            Expression::function("tan", args.to_vec())
77        }
78    }
79
80    fn applies_to(&self, args: &[Expression]) -> bool {
81        args.len() == 1
82    }
83
84    fn name(&self) -> &str {
85        "TanSimplificationStrategy"
86    }
87}
88
89/// Generic trigonometric simplification strategy
90///
91/// Handles csc, sec, cot, asin, acos, atan, sinh, cosh, tanh
92pub struct GenericTrigSimplificationStrategy {
93    function_name: String,
94}
95
96impl GenericTrigSimplificationStrategy {
97    pub fn new(function_name: &str) -> Self {
98        Self {
99            function_name: function_name.to_owned(),
100        }
101    }
102}
103
104impl SimplificationStrategy for GenericTrigSimplificationStrategy {
105    fn simplify(&self, args: &[Expression]) -> Expression {
106        Expression::function(&self.function_name, args.to_vec())
107    }
108
109    fn applies_to(&self, args: &[Expression]) -> bool {
110        args.len() == 1
111    }
112
113    fn name(&self) -> &str {
114        &self.function_name
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121    use crate::{expr, symbol};
122
123    #[test]
124    fn test_sin_of_zero() {
125        let strategy = SinSimplificationStrategy;
126        let result = strategy.simplify(&[expr!(0)]);
127        assert_eq!(result, expr!(0));
128    }
129
130    #[test]
131    fn test_sin_of_x() {
132        let strategy = SinSimplificationStrategy;
133        let x = symbol!(x);
134        let result = strategy.simplify(&[x.clone().into()]);
135
136        if let Expression::Function { name, args } = result {
137            assert_eq!(name.as_ref(), "sin");
138            assert_eq!(args.len(), 1);
139            assert_eq!(args[0], x.into());
140        } else {
141            panic!("Expected function call");
142        }
143    }
144
145    #[test]
146    fn test_cos_of_zero() {
147        let strategy = CosSimplificationStrategy;
148        let result = strategy.simplify(&[expr!(0)]);
149        assert_eq!(result, expr!(1));
150    }
151
152    #[test]
153    fn test_cos_of_x() {
154        let strategy = CosSimplificationStrategy;
155        let x = symbol!(x);
156        let result = strategy.simplify(&[x.clone().into()]);
157
158        if let Expression::Function { name, args } = result {
159            assert_eq!(name.as_ref(), "cos");
160            assert_eq!(args.len(), 1);
161            assert_eq!(args[0], x.into());
162        } else {
163            panic!("Expected function call");
164        }
165    }
166
167    #[test]
168    fn test_tan_of_zero() {
169        let strategy = TanSimplificationStrategy;
170        let result = strategy.simplify(&[expr!(0)]);
171        assert_eq!(result, expr!(0));
172    }
173
174    #[test]
175    fn test_tan_of_x() {
176        let strategy = TanSimplificationStrategy;
177        let x = symbol!(x);
178        let result = strategy.simplify(&[x.clone().into()]);
179
180        if let Expression::Function { name, args } = result {
181            assert_eq!(name.as_ref(), "tan");
182            assert_eq!(args.len(), 1);
183            assert_eq!(args[0], x.into());
184        } else {
185            panic!("Expected function call");
186        }
187    }
188
189    #[test]
190    fn test_generic_trig() {
191        let strategy = GenericTrigSimplificationStrategy::new("sinh");
192        let x = symbol!(x);
193        let result = strategy.simplify(&[x.clone().into()]);
194
195        if let Expression::Function { name, args } = result {
196            assert_eq!(name.as_ref(), "sinh");
197            assert_eq!(args.len(), 1);
198            assert_eq!(args[0], x.into());
199        } else {
200            panic!("Expected function call");
201        }
202    }
203}