mathhook_core/functions/
education.rs

1//! Educational function system
2//!
3//! Provides step-by-step explanations for ALL function operations
4//! to comply with MathHook's educational integration requirements.
5
6use super::properties::PolynomialFamily;
7use crate::core::Expression;
8use crate::educational::step_by_step::{Step, StepByStepExplanation};
9use std::collections::HashMap;
10
11/// Function educator for step-by-step explanations
12///
13/// Provides educational explanations for all function operations
14/// with proper mathematical notation and domain restrictions.
15pub struct FunctionEducator {
16    step_generators: HashMap<String, Box<dyn StepGenerator>>,
17}
18
19/// Step generator trait for educational explanations
20pub trait StepGenerator: Send + Sync {
21    /// Generate step-by-step explanation for function evaluation
22    fn generate_steps(&self, name: &str, args: &[Expression]) -> Vec<Step>;
23
24    /// Get mathematical context for the function
25    fn get_mathematical_context(&self, name: &str) -> String;
26}
27
28impl FunctionEducator {
29    /// Create new function educator with all 20+ functions
30    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    /// Generate comprehensive step-by-step explanation
44    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    /// Initialize trigonometric function education (8 functions)
64    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    /// Initialize exponential/logarithmic education (6 functions)
182    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    /// Initialize polynomial families education (4 families)
257    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    /// Initialize number theory education (3 functions)
304    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
353/// Trigonometric function step generator (sin, cos, tan, csc, sec, cot)
354struct 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
399/// Inverse trigonometric step generator (arcsin, arccos, arctan)
400struct 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
452/// Exponential/logarithmic step generator
453struct 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
495/// Polynomial family step generator
496struct 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
546/// Number theory step generator
547struct 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
590/// Format args for display
591fn 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}