1use super::properties::PolynomialFamily;
7use crate::core::Expression;
8use crate::educational::step_by_step::{Step, StepByStepExplanation};
9use std::collections::HashMap;
10
11pub struct FunctionEducator {
16 step_generators: HashMap<String, Box<dyn StepGenerator>>,
17}
18
19pub trait StepGenerator: Send + Sync {
21 fn generate_steps(&self, name: &str, args: &[Expression]) -> Vec<Step>;
23
24 fn get_mathematical_context(&self, name: &str) -> String;
26}
27
28impl FunctionEducator {
29 pub fn new() -> Self {
31 let mut educator = Self {
32 step_generators: HashMap::with_capacity(32),
33 };
34
35 educator.initialize_trigonometric();
36 educator.initialize_exponential_logarithmic();
37 educator.initialize_polynomial_families();
38 educator.initialize_number_theory();
39
40 educator
41 }
42
43 pub fn explain_function_operation(
45 &self,
46 name: &str,
47 args: &[Expression],
48 _operation: &str,
49 ) -> StepByStepExplanation {
50 let steps = if let Some(generator) = self.step_generators.get(name) {
51 generator.generate_steps(name, args)
52 } else {
53 vec![
54 Step::new("Function Identification", format!("Function: {}", name)),
55 Step::new("Arguments", format!("Args: {}", format_args(args))),
56 Step::new("Result", "Computing...".to_owned()),
57 ]
58 };
59
60 StepByStepExplanation::new(steps)
61 }
62
63 fn initialize_trigonometric(&mut self) {
65 self.step_generators.insert(
66 "sin".to_owned(),
67 Box::new(TrigStepGenerator {
68 latex_name: "\\sin".to_owned(),
69 context: "Sine: y-coordinate on unit circle".to_owned(),
70 special_values: vec![
71 ("0", "0"),
72 ("π/6", "1/2"),
73 ("π/4", "√2/2"),
74 ("π/3", "√3/2"),
75 ("π/2", "1"),
76 ("π", "0"),
77 ("2π", "0"),
78 ],
79 domain: "All real numbers".to_owned(),
80 range: "[-1, 1]".to_owned(),
81 }),
82 );
83
84 self.step_generators.insert(
85 "cos".to_owned(),
86 Box::new(TrigStepGenerator {
87 latex_name: "\\cos".to_owned(),
88 context: "Cosine: x-coordinate on unit circle".to_owned(),
89 special_values: vec![
90 ("0", "1"),
91 ("π/6", "√3/2"),
92 ("π/4", "√2/2"),
93 ("π/3", "1/2"),
94 ("π/2", "0"),
95 ("π", "-1"),
96 ("2π", "1"),
97 ],
98 domain: "All real numbers".to_owned(),
99 range: "[-1, 1]".to_owned(),
100 }),
101 );
102
103 self.step_generators.insert(
104 "tan".to_owned(),
105 Box::new(TrigStepGenerator {
106 latex_name: "\\tan".to_owned(),
107 context: "Tangent: sin(x)/cos(x), slope of angle".to_owned(),
108 special_values: vec![("0", "0"), ("π/4", "1"), ("π", "0")],
109 domain: "x ≠ π/2 + nπ (undefined at vertical asymptotes)".to_owned(),
110 range: "All real numbers".to_owned(),
111 }),
112 );
113
114 self.step_generators.insert(
115 "csc".to_owned(),
116 Box::new(TrigStepGenerator {
117 latex_name: "\\csc".to_owned(),
118 context: "Cosecant: 1/sin(x), reciprocal of sine".to_owned(),
119 special_values: vec![("π/6", "2"), ("π/2", "1")],
120 domain: "x ≠ nπ (undefined where sin(x) = 0)".to_owned(),
121 range: "(-∞, -1] ∪ [1, ∞)".to_owned(),
122 }),
123 );
124
125 self.step_generators.insert(
126 "sec".to_owned(),
127 Box::new(TrigStepGenerator {
128 latex_name: "\\sec".to_owned(),
129 context: "Secant: 1/cos(x), reciprocal of cosine".to_owned(),
130 special_values: vec![("0", "1"), ("π/3", "2")],
131 domain: "x ≠ π/2 + nπ (undefined where cos(x) = 0)".to_owned(),
132 range: "(-∞, -1] ∪ [1, ∞)".to_owned(),
133 }),
134 );
135
136 self.step_generators.insert(
137 "cot".to_owned(),
138 Box::new(TrigStepGenerator {
139 latex_name: "\\cot".to_owned(),
140 context: "Cotangent: cos(x)/sin(x), reciprocal of tangent".to_owned(),
141 special_values: vec![("π/4", "1")],
142 domain: "x ≠ nπ (undefined where sin(x) = 0)".to_owned(),
143 range: "All real numbers".to_owned(),
144 }),
145 );
146
147 self.step_generators.insert(
148 "arcsin".to_owned(),
149 Box::new(InverseTrigStepGenerator {
150 function_name: "arcsin".to_owned(),
151 latex_name: "\\arcsin".to_owned(),
152 context: "Inverse sine: angle whose sine is x".to_owned(),
153 domain: "[-1, 1] (must be valid sine value)".to_owned(),
154 range: "[-π/2, π/2] (principal branch)".to_owned(),
155 }),
156 );
157
158 self.step_generators.insert(
159 "arccos".to_owned(),
160 Box::new(InverseTrigStepGenerator {
161 function_name: "arccos".to_owned(),
162 latex_name: "\\arccos".to_owned(),
163 context: "Inverse cosine: angle whose cosine is x".to_owned(),
164 domain: "[-1, 1] (must be valid cosine value)".to_owned(),
165 range: "[0, π] (principal branch)".to_owned(),
166 }),
167 );
168
169 self.step_generators.insert(
170 "arctan".to_owned(),
171 Box::new(InverseTrigStepGenerator {
172 function_name: "arctan".to_owned(),
173 latex_name: "\\arctan".to_owned(),
174 context: "Inverse tangent: angle whose tangent is x".to_owned(),
175 domain: "All real numbers".to_owned(),
176 range: "(-π/2, π/2) (principal branch)".to_owned(),
177 }),
178 );
179 }
180
181 fn initialize_exponential_logarithmic(&mut self) {
183 self.step_generators.insert(
184 "exp".to_owned(),
185 Box::new(ExpLogStepGenerator {
186 latex_name: "e^x".to_owned(),
187 context: "Natural exponential: base e (≈2.718)".to_owned(),
188 special_values: vec![("0", "1"), ("1", "e"), ("ln(a)", "a")],
189 domain: "All real numbers".to_owned(),
190 range: "(0, ∞) (always positive)".to_owned(),
191 }),
192 );
193
194 self.step_generators.insert(
195 "ln".to_owned(),
196 Box::new(ExpLogStepGenerator {
197 latex_name: "\\ln".to_owned(),
198 context: "Natural logarithm: inverse of e^x, base e".to_owned(),
199 special_values: vec![("1", "0"), ("e", "1"), ("e^k", "k")],
200 domain: "(0, ∞) (only positive numbers)".to_owned(),
201 range: "All real numbers".to_owned(),
202 }),
203 );
204
205 self.step_generators.insert(
206 "log".to_owned(),
207 Box::new(ExpLogStepGenerator {
208 latex_name: "\\log".to_owned(),
209 context: "Common logarithm: base 10 logarithm".to_owned(),
210 special_values: vec![("1", "0"), ("10", "1"), ("100", "2"), ("1000", "3")],
211 domain: "(0, ∞) (only positive numbers)".to_owned(),
212 range: "All real numbers".to_owned(),
213 }),
214 );
215
216 self.step_generators.insert(
217 "log10".to_owned(),
218 Box::new(ExpLogStepGenerator {
219 latex_name: "\\log_{10}".to_owned(),
220 context: "Base-10 logarithm: same as log".to_owned(),
221 special_values: vec![("1", "0"), ("10", "1"), ("100", "2")],
222 domain: "(0, ∞) (only positive numbers)".to_owned(),
223 range: "All real numbers".to_owned(),
224 }),
225 );
226
227 self.step_generators.insert(
228 "sqrt".to_owned(),
229 Box::new(ExpLogStepGenerator {
230 latex_name: "\\sqrt{x}".to_owned(),
231 context: "Square root: principal (positive) square root".to_owned(),
232 special_values: vec![("0", "0"), ("1", "1"), ("4", "2"), ("9", "3"), ("16", "4")],
233 domain: "[0, ∞) (non-negative for real numbers)".to_owned(),
234 range: "[0, ∞) (non-negative result)".to_owned(),
235 }),
236 );
237
238 self.step_generators.insert(
239 "cbrt".to_owned(),
240 Box::new(ExpLogStepGenerator {
241 latex_name: "\\sqrt[3]{x}".to_owned(),
242 context: "Cube root: real cube root of x".to_owned(),
243 special_values: vec![
244 ("0", "0"),
245 ("1", "1"),
246 ("8", "2"),
247 ("27", "3"),
248 ("-8", "-2"),
249 ],
250 domain: "All real numbers".to_owned(),
251 range: "All real numbers".to_owned(),
252 }),
253 );
254 }
255
256 fn initialize_polynomial_families(&mut self) {
258 self.step_generators.insert("legendre_p".to_owned(), Box::new(PolynomialStepGenerator {
259 function_name: "legendre_p".to_owned(),
260 family: PolynomialFamily::Legendre,
261 latex_name: "P_n".to_owned(),
262 context: "Legendre polynomials: orthogonal on [-1,1], arise in physics (spherical harmonics)".to_owned(),
263 recurrence: "P_{n+1}(x) = [(2n+1)xP_n(x) - nP_{n-1}(x)]/(n+1)".to_owned(),
264 base_cases: vec![("P_0(x)", "1"), ("P_1(x)", "x"), ("P_2(x)", "(3x²-1)/2")],
265 }));
266
267 self.step_generators.insert(
268 "chebyshev_t".to_owned(),
269 Box::new(PolynomialStepGenerator {
270 function_name: "chebyshev_t".to_owned(),
271 family: PolynomialFamily::Chebyshev,
272 latex_name: "T_n".to_owned(),
273 context: "Chebyshev (first kind): minimize max error in approximation theory"
274 .to_owned(),
275 recurrence: "T_{n+1}(x) = 2xT_n(x) - T_{n-1}(x)".to_owned(),
276 base_cases: vec![("T_0(x)", "1"), ("T_1(x)", "x"), ("T_2(x)", "2x²-1")],
277 }),
278 );
279
280 self.step_generators.insert(
281 "hermite_h".to_owned(),
282 Box::new(PolynomialStepGenerator {
283 function_name: "hermite_h".to_owned(),
284 family: PolynomialFamily::Hermite,
285 latex_name: "H_n".to_owned(),
286 context: "Hermite polynomials: orthogonal with Gaussian weight, quantum mechanics"
287 .to_owned(),
288 recurrence: "H_{n+1}(x) = 2xH_n(x) - 2nH_{n-1}(x)".to_owned(),
289 base_cases: vec![("H_0(x)", "1"), ("H_1(x)", "2x"), ("H_2(x)", "4x²-2")],
290 }),
291 );
292
293 self.step_generators.insert("laguerre_l".to_owned(), Box::new(PolynomialStepGenerator {
294 function_name: "laguerre_l".to_owned(),
295 family: PolynomialFamily::Laguerre,
296 latex_name: "L_n".to_owned(),
297 context: "Laguerre polynomials: orthogonal with exponential weight, quantum mechanics (radial wavefunctions)".to_owned(),
298 recurrence: "L_{n+1}(x) = [(2n+1-x)L_n(x) - nL_{n-1}(x)]/(n+1)".to_owned(),
299 base_cases: vec![("L_0(x)", "1"), ("L_1(x)", "1-x"), ("L_2(x)", "(2-4x+x²)/2")],
300 }));
301 }
302
303 fn initialize_number_theory(&mut self) {
305 self.step_generators.insert(
306 "factorial".to_owned(),
307 Box::new(NumberTheoryStepGenerator {
308 function_name: "factorial".to_owned(),
309 latex_name: "n!".to_owned(),
310 context: "Factorial: product of positive integers up to n".to_owned(),
311 formula: "n! = n × (n-1) × ... × 2 × 1, with 0! = 1".to_owned(),
312 special_values: vec![
313 ("0!", "1"),
314 ("1!", "1"),
315 ("2!", "2"),
316 ("3!", "6"),
317 ("4!", "24"),
318 ("5!", "120"),
319 ],
320 domain: "Non-negative integers".to_owned(),
321 }),
322 );
323
324 self.step_generators.insert(
325 "gcd".to_owned(),
326 Box::new(NumberTheoryStepGenerator {
327 function_name: "gcd".to_owned(),
328 latex_name: "\\gcd".to_owned(),
329 context: "Greatest common divisor: largest integer dividing both numbers"
330 .to_owned(),
331 formula: "Euclidean algorithm: gcd(a,b) = gcd(b, a mod b)".to_owned(),
332 special_values: vec![("gcd(12,18)", "6"), ("gcd(15,25)", "5"), ("gcd(7,11)", "1")],
333 domain: "Integers".to_owned(),
334 }),
335 );
336
337 self.step_generators.insert(
338 "lcm".to_owned(),
339 Box::new(NumberTheoryStepGenerator {
340 function_name: "lcm".to_owned(),
341 latex_name: "\\text{lcm}".to_owned(),
342 context:
343 "Least common multiple: smallest positive integer divisible by both numbers"
344 .to_owned(),
345 formula: "lcm(a,b) = |a×b| / gcd(a,b)".to_owned(),
346 special_values: vec![("lcm(4,6)", "12"), ("lcm(3,5)", "15"), ("lcm(6,8)", "24")],
347 domain: "Positive integers".to_owned(),
348 }),
349 );
350 }
351}
352
353struct TrigStepGenerator {
355 latex_name: String,
356 context: String,
357 special_values: Vec<(&'static str, &'static str)>,
358 domain: String,
359 range: String,
360}
361
362impl StepGenerator for TrigStepGenerator {
363 fn generate_steps(&self, _name: &str, args: &[Expression]) -> Vec<Step> {
364 vec![
365 Step::new(
366 "Function Type",
367 format!("Trigonometric function: {}", self.latex_name),
368 ),
369 Step::new("Mathematical Context", self.context.clone()),
370 Step::new("Domain", format!("Valid inputs: {}", self.domain)),
371 Step::new("Range", format!("Possible outputs: {}", self.range)),
372 Step::new(
373 "Input Analysis",
374 format!("Evaluating {}({})", self.latex_name, format_args(args)),
375 ),
376 Step::new(
377 "Special Values",
378 format!(
379 "Checking {} known special values",
380 self.special_values.len()
381 ),
382 ),
383 Step::new(
384 "Result",
385 format!(
386 "{}({}) = computed result",
387 self.latex_name,
388 format_args(args)
389 ),
390 ),
391 ]
392 }
393
394 fn get_mathematical_context(&self, _name: &str) -> String {
395 self.context.clone()
396 }
397}
398
399struct InverseTrigStepGenerator {
401 function_name: String,
402 latex_name: String,
403 context: String,
404 domain: String,
405 range: String,
406}
407
408impl StepGenerator for InverseTrigStepGenerator {
409 fn generate_steps(&self, _name: &str, args: &[Expression]) -> Vec<Step> {
410 vec![
411 Step::new(
412 "Function Type",
413 format!("Inverse trigonometric: {}", self.latex_name),
414 ),
415 Step::new("Mathematical Context", self.context.clone()),
416 Step::new(
417 "Domain Restriction",
418 format!("Input must be in: {}", self.domain),
419 ),
420 Step::new(
421 "Range (Principal Branch)",
422 format!("Output will be in: {}", self.range),
423 ),
424 Step::new(
425 "Input Validation",
426 format!("Checking if {} is in valid domain", format_args(args)),
427 ),
428 Step::new(
429 "Computation",
430 format!(
431 "Finding angle whose {} gives {}",
432 self.function_name.replace("arc", ""),
433 format_args(args)
434 ),
435 ),
436 Step::new(
437 "Result",
438 format!(
439 "{}({}) = computed angle",
440 self.latex_name,
441 format_args(args)
442 ),
443 ),
444 ]
445 }
446
447 fn get_mathematical_context(&self, _name: &str) -> String {
448 self.context.clone()
449 }
450}
451
452struct ExpLogStepGenerator {
454 latex_name: String,
455 context: String,
456 special_values: Vec<(&'static str, &'static str)>,
457 domain: String,
458 range: String,
459}
460
461impl StepGenerator for ExpLogStepGenerator {
462 fn generate_steps(&self, _name: &str, args: &[Expression]) -> Vec<Step> {
463 vec![
464 Step::new(
465 "Function Type",
466 format!("Exponential/Logarithmic: {}", self.latex_name),
467 ),
468 Step::new("Mathematical Context", self.context.clone()),
469 Step::new("Domain", format!("Valid inputs: {}", self.domain)),
470 Step::new("Range", format!("Possible outputs: {}", self.range)),
471 Step::new(
472 "Input Analysis",
473 format!("Evaluating {} at {}", self.latex_name, format_args(args)),
474 ),
475 Step::new(
476 "Special Value Check",
477 format!("Checking {} special values", self.special_values.len()),
478 ),
479 Step::new(
480 "Result",
481 format!(
482 "{}({}) = computed result",
483 self.latex_name,
484 format_args(args)
485 ),
486 ),
487 ]
488 }
489
490 fn get_mathematical_context(&self, _name: &str) -> String {
491 self.context.clone()
492 }
493}
494
495struct PolynomialStepGenerator {
497 function_name: String,
498 family: PolynomialFamily,
499 latex_name: String,
500 context: String,
501 recurrence: String,
502 base_cases: Vec<(&'static str, &'static str)>,
503}
504
505impl StepGenerator for PolynomialStepGenerator {
506 fn generate_steps(&self, _name: &str, args: &[Expression]) -> Vec<Step> {
507 vec![
508 Step::new(
509 "Polynomial Family",
510 format!("{:?} polynomials", self.family),
511 ),
512 Step::new("Mathematical Context", self.context.clone()),
513 Step::new(
514 "Input",
515 format!("Computing {} for {}", self.function_name, format_args(args)),
516 ),
517 Step::new(
518 "Notation",
519 format!("{}({})", self.latex_name, format_args(args)),
520 ),
521 Step::new("Recurrence Relation", self.recurrence.clone()),
522 Step::new(
523 "Base Cases",
524 format!("{} base cases known", self.base_cases.len()),
525 ),
526 Step::new(
527 "Computation Method",
528 "Using three-term recurrence relation".to_owned(),
529 ),
530 Step::new(
531 "Result",
532 format!(
533 "{}({}) = computed polynomial",
534 self.latex_name,
535 format_args(args)
536 ),
537 ),
538 ]
539 }
540
541 fn get_mathematical_context(&self, _name: &str) -> String {
542 self.context.clone()
543 }
544}
545
546struct NumberTheoryStepGenerator {
548 function_name: String,
549 latex_name: String,
550 context: String,
551 formula: String,
552 special_values: Vec<(&'static str, &'static str)>,
553 domain: String,
554}
555
556impl StepGenerator for NumberTheoryStepGenerator {
557 fn generate_steps(&self, _name: &str, args: &[Expression]) -> Vec<Step> {
558 vec![
559 Step::new(
560 "Function Type",
561 format!("Number Theory: {}", self.latex_name),
562 ),
563 Step::new("Mathematical Context", self.context.clone()),
564 Step::new("Formula", self.formula.clone()),
565 Step::new("Domain", format!("Valid inputs: {}", self.domain)),
566 Step::new(
567 "Input",
568 format!("Computing {} for {}", self.function_name, format_args(args)),
569 ),
570 Step::new(
571 "Known Values",
572 format!("{} special values available", self.special_values.len()),
573 ),
574 Step::new(
575 "Result",
576 format!(
577 "{}({}) = computed result",
578 self.latex_name,
579 format_args(args)
580 ),
581 ),
582 ]
583 }
584
585 fn get_mathematical_context(&self, _name: &str) -> String {
586 self.context.clone()
587 }
588}
589
590fn format_args(args: &[Expression]) -> String {
592 args.iter()
593 .map(|a| format!("{}", a))
594 .collect::<Vec<_>>()
595 .join(", ")
596}
597
598impl Default for FunctionEducator {
599 fn default() -> Self {
600 Self::new()
601 }
602}
603
604#[cfg(test)]
605mod tests {
606 use super::*;
607
608 #[test]
609 fn test_function_count() {
610 let educator = FunctionEducator::new();
611 assert!(
612 educator.step_generators.len() >= 20,
613 "Should have 20+ functions"
614 );
615 }
616
617 #[test]
618 fn test_trig_education() {
619 let educator = FunctionEducator::new();
620 let args = vec![Expression::integer(0)];
621 let explanation = educator.explain_function_operation("sin", &args, "evaluation");
622 assert!(explanation.steps.len() >= 5);
623 assert!(explanation
624 .steps
625 .iter()
626 .any(|s| s.title.contains("Domain") || s.description.contains("domain")));
627 }
628
629 #[test]
630 fn test_special_value_mentions() {
631 let educator = FunctionEducator::new();
632 let args = vec![Expression::integer(1)];
633 let explanation = educator.explain_function_operation("ln", &args, "evaluation");
634 assert!(explanation
635 .steps
636 .iter()
637 .any(|s| s.description.contains("special")));
638 }
639}