mathhook_core/functions/elementary/trigonometric/
trig_circular.rs

1//! Circular Trigonometric Functions (sin, cos, tan, cot, sec, csc)
2//!
3//! Complete mathematical intelligence for circular trigonometric functions
4//! with derivatives, identities, and special values.
5
6use crate::core::{Expression, Symbol};
7use crate::expr;
8use crate::symbol;
9
10use crate::functions::properties::*;
11use std::collections::HashMap;
12use std::sync::Arc;
13
14/// Circular Trigonometric Function Intelligence
15///
16/// Dedicated intelligence for sin, cos, tan, cot, sec, csc
17pub struct CircularTrigIntelligence {
18    properties: HashMap<String, FunctionProperties>,
19}
20
21impl Default for CircularTrigIntelligence {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27impl CircularTrigIntelligence {
28    /// Create new circular trigonometric intelligence system
29    pub fn new() -> Self {
30        let mut intelligence = Self {
31            properties: HashMap::with_capacity(6),
32        };
33
34        intelligence.initialize_sin_cos();
35        intelligence.initialize_tan_cot();
36        intelligence.initialize_sec_csc();
37
38        intelligence
39    }
40
41    /// Get all circular trigonometric function properties
42    pub fn get_properties(&self) -> HashMap<String, FunctionProperties> {
43        self.properties.clone()
44    }
45
46    /// Check if function is circular trigonometric
47    pub fn has_function(&self, name: &str) -> bool {
48        self.properties.contains_key(name)
49    }
50
51    fn initialize_sin_cos(&mut self) {
52        self.properties.insert(
53            "sin".to_owned(),
54            FunctionProperties::Elementary(Box::new(ElementaryProperties {
55                derivative_rule: Some(DerivativeRule {
56                    rule_type: DerivativeRuleType::SimpleFunctionSubstitution("cos".to_owned()),
57                    result_template: "cos(x)".to_owned(),
58                }),
59                antiderivative_rule: Some(AntiderivativeRule {
60                    rule_type: AntiderivativeRuleType::Simple {
61                        antiderivative_fn: "cos".to_owned(),
62                        coefficient: expr!(-1),
63                    },
64                    result_template: "∫sin(x)dx = -cos(x) + C".to_owned(),
65                    constant_handling: ConstantOfIntegration::AddConstant,
66                }),
67                special_values: vec![
68                    SpecialValue {
69                        input: "0".to_owned(),
70                        output: expr!(0),
71                        latex_explanation: "\\sin(0) = 0".to_owned(),
72                    },
73                    SpecialValue {
74                        input: "π/2".to_owned(),
75                        output: expr!(1),
76                        latex_explanation: "\\sin(\\frac{\\pi}{2}) = 1".to_owned(),
77                    },
78                    SpecialValue {
79                        input: "π".to_owned(),
80                        output: expr!(0),
81                        latex_explanation: "\\sin(\\pi) = 0".to_owned(),
82                    },
83                ],
84                identities: Box::new(vec![MathIdentity {
85                    name: "Pythagorean Identity".to_owned(),
86                    lhs: Expression::add(vec![
87                        Expression::pow(
88                            Expression::function("sin", vec![symbol!(x).into()]),
89                            expr!(2),
90                        ),
91                        Expression::pow(
92                            Expression::function("cos", vec![symbol!(x).into()]),
93                            expr!(2),
94                        ),
95                    ]),
96                    rhs: expr!(1),
97                    conditions: vec!["x ∈ ℝ".to_owned()],
98                }]),
99                domain_range: Box::new(DomainRangeData {
100                    domain: Domain::Real,
101                    range: Range::Bounded(expr!(-1), expr!(1)),
102                    singularities: vec![],
103                }),
104                wolfram_name: Some("Sin"),
105                periodicity: Some(Expression::mul(vec![expr!(2), Expression::pi()])),
106            })),
107        );
108
109        self.properties.insert(
110            "cos".to_owned(),
111            FunctionProperties::Elementary(Box::new(ElementaryProperties {
112                derivative_rule: Some(DerivativeRule {
113                    rule_type: DerivativeRuleType::Custom {
114                        builder: Arc::new(|arg: &Expression| {
115                            Expression::mul(vec![
116                                expr!(-1),
117                                Expression::function("sin", vec![arg.clone()]),
118                            ])
119                        }),
120                    },
121                    result_template: "-sin(x)".to_owned(),
122                }),
123                antiderivative_rule: Some(AntiderivativeRule {
124                    rule_type: AntiderivativeRuleType::Simple {
125                        antiderivative_fn: "sin".to_owned(),
126                        coefficient: expr!(1),
127                    },
128                    result_template: "∫cos(x)dx = sin(x) + C".to_owned(),
129                    constant_handling: ConstantOfIntegration::AddConstant,
130                }),
131                special_values: vec![
132                    SpecialValue {
133                        input: "0".to_owned(),
134                        output: expr!(1),
135                        latex_explanation: "\\cos(0) = 1".to_owned(),
136                    },
137                    SpecialValue {
138                        input: "π/2".to_owned(),
139                        output: expr!(0),
140                        latex_explanation: "\\cos(\\frac{\\pi}{2}) = 0".to_owned(),
141                    },
142                    SpecialValue {
143                        input: "π".to_owned(),
144                        output: expr!(-1),
145                        latex_explanation: "\\cos(\\pi) = -1".to_owned(),
146                    },
147                    SpecialValue {
148                        input: "3π/2".to_owned(),
149                        output: expr!(0),
150                        latex_explanation: "\\cos(\\frac{3\\pi}{2}) = 0".to_owned(),
151                    },
152                    SpecialValue {
153                        input: "2π".to_owned(),
154                        output: expr!(1),
155                        latex_explanation: "\\cos(2\\pi) = 1".to_owned(),
156                    },
157                ],
158                identities: Box::new(vec![]),
159                domain_range: Box::new(DomainRangeData {
160                    domain: Domain::Real,
161                    range: Range::Bounded(expr!(-1), expr!(1)),
162                    singularities: vec![],
163                }),
164                wolfram_name: Some("Cos"),
165                periodicity: Some(Expression::mul(vec![expr!(2), Expression::pi()])),
166            })),
167        );
168    }
169
170    fn initialize_tan_cot(&mut self) {
171        self.properties.insert(
172            "tan".to_owned(),
173            FunctionProperties::Elementary(Box::new(ElementaryProperties {
174                derivative_rule: Some(DerivativeRule {
175                    rule_type: DerivativeRuleType::Custom {
176                        builder: Arc::new(|arg: &Expression| {
177                            let sec_arg = Expression::function("sec", vec![arg.clone()]);
178                            Expression::pow(sec_arg, expr!(2))
179                        }),
180                    },
181                    result_template: "sec²(x)".to_owned(),
182                }),
183                antiderivative_rule: Some(AntiderivativeRule {
184                    rule_type: AntiderivativeRuleType::Custom {
185                        builder: Arc::new(|var: Symbol| {
186                            Expression::mul(vec![
187                                expr!(-1),
188                                Expression::function(
189                                    "ln",
190                                    vec![Expression::function(
191                                        "abs",
192                                        vec![Expression::function("cos", vec![var.into()])],
193                                    )],
194                                ),
195                            ])
196                        }),
197                    },
198                    result_template: "∫tan(x)dx = -ln|cos(x)| + C".to_owned(),
199                    constant_handling: ConstantOfIntegration::AddConstant,
200                }),
201                special_values: vec![
202                    SpecialValue {
203                        input: "0".to_owned(),
204                        output: expr!(0),
205                        latex_explanation: "\\tan(0) = 0".to_owned(),
206                    },
207                    SpecialValue {
208                        input: "π/4".to_owned(),
209                        output: expr!(1),
210                        latex_explanation: "\\tan(\\frac{\\pi}{4}) = 1".to_owned(),
211                    },
212                ],
213                identities: Box::new(vec![MathIdentity {
214                    name: "Tangent Identity".to_owned(),
215                    lhs: Expression::function("tan", vec![symbol!(x).into()]),
216                    rhs: Expression::function("sin_over_cos", vec![symbol!(x).into()]),
217                    conditions: vec!["cos(x) ≠ 0".to_owned()],
218                }]),
219                domain_range: Box::new(DomainRangeData {
220                    domain: Domain::Real,
221                    range: Range::Real,
222                    singularities: vec![expr!((pi) * (1 / 2))],
223                }),
224                wolfram_name: Some("Tan"),
225                periodicity: Some(Expression::pi()),
226            })),
227        );
228
229        self.properties.insert(
230            "cot".to_owned(),
231            FunctionProperties::Elementary(Box::new(ElementaryProperties {
232                derivative_rule: Some(DerivativeRule {
233                    rule_type: DerivativeRuleType::Custom {
234                        builder: Arc::new(|arg: &Expression| {
235                            let csc_arg = Expression::function("csc", vec![arg.clone()]);
236                            let csc_squared = Expression::pow(csc_arg, expr!(2));
237                            Expression::mul(vec![expr!(-1), csc_squared])
238                        }),
239                    },
240                    result_template: "-csc²(x)".to_owned(),
241                }),
242                antiderivative_rule: Some(AntiderivativeRule {
243                    rule_type: AntiderivativeRuleType::Custom {
244                        builder: Arc::new(|var: Symbol| {
245                            Expression::function(
246                                "ln",
247                                vec![Expression::function(
248                                    "abs",
249                                    vec![Expression::function("sin", vec![var.into()])],
250                                )],
251                            )
252                        }),
253                    },
254                    result_template: "∫cot(x)dx = ln|sin(x)| + C".to_owned(),
255                    constant_handling: ConstantOfIntegration::AddConstant,
256                }),
257                special_values: vec![SpecialValue {
258                    input: "π/4".to_owned(),
259                    output: expr!(1),
260                    latex_explanation: "\\cot(\\frac{\\pi}{4}) = 1".to_owned(),
261                }],
262                identities: Box::new(vec![MathIdentity {
263                    name: "Cotangent Identity".to_owned(),
264                    lhs: Expression::function("cot", vec![symbol!(x).into()]),
265                    rhs: Expression::function("cos_over_sin", vec![symbol!(x).into()]),
266                    conditions: vec!["sin(x) ≠ 0".to_owned()],
267                }]),
268                domain_range: Box::new(DomainRangeData {
269                    domain: Domain::Real,
270                    range: Range::Real,
271                    singularities: vec![expr!(0)],
272                }),
273                wolfram_name: Some("Cot"),
274                periodicity: Some(Expression::pi()),
275            })),
276        );
277    }
278
279    fn initialize_sec_csc(&mut self) {
280        self.properties.insert(
281            "sec".to_owned(),
282            FunctionProperties::Elementary(Box::new(ElementaryProperties {
283                derivative_rule: Some(DerivativeRule {
284                    rule_type: DerivativeRuleType::Custom {
285                        builder: Arc::new(|arg: &Expression| {
286                            let sec_arg = Expression::function("sec", vec![arg.clone()]);
287                            let tan_arg = Expression::function("tan", vec![arg.clone()]);
288                            Expression::mul(vec![sec_arg, tan_arg])
289                        }),
290                    },
291                    result_template: "sec(x)·tan(x)".to_owned(),
292                }),
293                antiderivative_rule: Some(AntiderivativeRule {
294                    rule_type: AntiderivativeRuleType::Custom {
295                        builder: Arc::new(|var: Symbol| {
296                            Expression::function(
297                                "ln",
298                                vec![Expression::function(
299                                    "abs",
300                                    vec![Expression::add(vec![
301                                        Expression::function("sec", vec![var.clone().into()]),
302                                        Expression::function("tan", vec![var.into()]),
303                                    ])],
304                                )],
305                            )
306                        }),
307                    },
308                    result_template: "∫sec(x)dx = ln|sec(x)+tan(x)| + C".to_owned(),
309                    constant_handling: ConstantOfIntegration::AddConstant,
310                }),
311                special_values: vec![],
312                identities: Box::new(vec![]),
313                domain_range: Box::new(DomainRangeData {
314                    domain: Domain::Real,
315                    range: Range::Real,
316                    singularities: vec![expr!((pi) * (1 / 2))],
317                }),
318                wolfram_name: Some("Sec"),
319                periodicity: Some(Expression::mul(vec![expr!(2), Expression::pi()])),
320            })),
321        );
322
323        self.properties.insert(
324            "csc".to_owned(),
325            FunctionProperties::Elementary(Box::new(ElementaryProperties {
326                derivative_rule: Some(DerivativeRule {
327                    rule_type: DerivativeRuleType::Custom {
328                        builder: Arc::new(|arg: &Expression| {
329                            let csc_arg = Expression::function("csc", vec![arg.clone()]);
330                            let cot_arg = Expression::function("cot", vec![arg.clone()]);
331                            Expression::mul(vec![expr!(-1), csc_arg, cot_arg])
332                        }),
333                    },
334                    result_template: "-csc(x)·cot(x)".to_owned(),
335                }),
336                antiderivative_rule: Some(AntiderivativeRule {
337                    rule_type: AntiderivativeRuleType::Custom {
338                        builder: Arc::new(|var: Symbol| {
339                            Expression::mul(vec![
340                                expr!(-1),
341                                Expression::function(
342                                    "ln",
343                                    vec![Expression::function(
344                                        "abs",
345                                        vec![Expression::add(vec![
346                                            Expression::function("csc", vec![var.clone().into()]),
347                                            Expression::function("cot", vec![var.into()]),
348                                        ])],
349                                    )],
350                                ),
351                            ])
352                        }),
353                    },
354                    result_template: "∫csc(x)dx = -ln|csc(x)+cot(x)| + C".to_owned(),
355                    constant_handling: ConstantOfIntegration::AddConstant,
356                }),
357                special_values: vec![],
358                identities: Box::new(vec![]),
359                domain_range: Box::new(DomainRangeData {
360                    domain: Domain::Real,
361                    range: Range::Real,
362                    singularities: vec![expr!(0)],
363                }),
364                wolfram_name: Some("Csc"),
365                periodicity: Some(Expression::mul(vec![expr!(2), Expression::pi()])),
366            })),
367        );
368    }
369}
370
371#[cfg(test)]
372mod tests {
373    use super::*;
374
375    #[test]
376    fn test_circular_trig_intelligence() {
377        let trig = CircularTrigIntelligence::new();
378
379        assert!(trig.has_function("sin"));
380        assert!(trig.has_function("cos"));
381        assert!(trig.has_function("tan"));
382        assert!(trig.has_function("cot"));
383        assert!(trig.has_function("sec"));
384        assert!(trig.has_function("csc"));
385        assert!(!trig.has_function("arcsin"));
386
387        let properties = trig.get_properties();
388        assert_eq!(properties.len(), 6);
389    }
390
391    #[test]
392    fn test_circular_trig_derivative_rules() {
393        let trig = CircularTrigIntelligence::new();
394        let properties = trig.get_properties();
395
396        if let Some(FunctionProperties::Elementary(tan_props)) = properties.get("tan") {
397            assert!(tan_props.derivative_rule.is_some());
398            let deriv = tan_props.derivative_rule.as_ref().unwrap();
399            assert!(matches!(deriv.rule_type, DerivativeRuleType::Custom { .. }));
400        } else {
401            panic!("tan properties not found");
402        }
403
404        if let Some(FunctionProperties::Elementary(sec_props)) = properties.get("sec") {
405            assert!(sec_props.derivative_rule.is_some());
406            let deriv = sec_props.derivative_rule.as_ref().unwrap();
407            assert!(matches!(deriv.rule_type, DerivativeRuleType::Custom { .. }));
408        } else {
409            panic!("sec properties not found");
410        }
411    }
412}