1use crate::core::{Expression, Symbol};
7use crate::expr;
8use crate::symbol;
9
10use crate::functions::properties::*;
11use std::collections::HashMap;
12use std::sync::Arc;
13
14pub 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 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 pub fn get_properties(&self) -> HashMap<String, FunctionProperties> {
43 self.properties.clone()
44 }
45
46 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}