mathhook_core/calculus/integrals/
educational.rs

1//! Educational explanations for integration operations
2//!
3//! Provides step-by-step explanations for various integration techniques
4//! including power rule, substitution, integration by parts, and definite integrals.
5
6use crate::core::{Expression, Symbol};
7use crate::educational::message_registry::{MessageBuilder, MessageCategory, MessageType};
8use crate::educational::step_by_step::{Step, StepByStepExplanation};
9
10/// Comprehensive integration explanation with strategy attribution
11///
12/// Provides detailed step-by-step explanations for integration operations,
13/// including which technique was used and why.
14pub struct IntegrationExplanation {
15    steps: Vec<String>,
16    strategy: String,
17}
18
19impl IntegrationExplanation {
20    /// Generate explanation for integrating an expression
21    ///
22    /// Analyzes the integrand and provides step-by-step explanation
23    /// of the integration process, automatically selecting the appropriate
24    /// technique (power rule, substitution, trigonometric, etc.)
25    ///
26    /// # Arguments
27    ///
28    /// * `expr` - The integrand to explain
29    /// * `var` - The variable of integration
30    ///
31    /// # Examples
32    ///
33    /// ```rust
34    /// use mathhook_core::{symbol, Expression};
35    /// use mathhook_core::calculus::integrals::educational::IntegrationExplanation;
36    ///
37    /// let x = symbol!(x);
38    /// let expr = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
39    /// let explanation = IntegrationExplanation::generate(&expr, &x);
40    /// assert!(!explanation.steps().is_empty());
41    /// ```
42    pub fn generate(expr: &Expression, var: &Symbol) -> Self {
43        let (steps, strategy) = analyze_and_explain(expr, var);
44        Self { steps, strategy }
45    }
46
47    /// Get the step-by-step explanation as a vector of strings
48    pub fn steps(&self) -> Vec<String> {
49        self.steps.clone()
50    }
51
52    /// Get the integration strategy/technique that was used
53    pub fn strategy_used(&self) -> String {
54        self.strategy.clone()
55    }
56}
57
58/// Analyze integrand and generate appropriate explanation
59fn analyze_and_explain(expr: &Expression, var: &Symbol) -> (Vec<String>, String) {
60    match expr {
61        Expression::Number(_) => (
62            vec![
63                format!("Integrating constant: {}", expr),
64                format!("Result: {}*{} + C", expr, var.name()),
65            ],
66            "constant".to_owned(),
67        ),
68        Expression::Symbol(s) if s.name() == var.name() => (
69            vec![
70                format!("Integrating variable {}", var.name()),
71                format!(
72                    "Power rule: integral({} d{}) = {}^2/2 + C",
73                    var.name(),
74                    var.name(),
75                    var.name()
76                ),
77            ],
78            "power".to_owned(),
79        ),
80        Expression::Pow(base, exp) => {
81            if matches!(base.as_ref(), Expression::Symbol(s) if s.name() == var.name()) {
82                (
83                    vec![
84                        format!(
85                            "Power rule: integral({}^n d{}) = {}^(n+1)/(n+1) + C",
86                            var.name(),
87                            var.name(),
88                            var.name()
89                        ),
90                        format!("For n = {}", exp),
91                        format!("Result: {}^({} + 1)/({} + 1) + C", var.name(), exp, exp),
92                    ],
93                    "power".to_owned(),
94                )
95            } else if matches!(base.as_ref(), Expression::Function { name, .. } if matches!(name.as_ref(), "sin" | "cos" | "tan"))
96            {
97                (
98                    vec![
99                        format!("Trigonometric power: {}^{}", base, exp),
100                        format!("Apply power reduction identity for {}", base),
101                        format!("Rewrite using trig identities"),
102                        format!("Integrate the simplified form"),
103                    ],
104                    "trig".to_owned(),
105                )
106            } else {
107                (
108                    vec![
109                        format!("Complex power expression: {}", expr),
110                        format!("May require advanced techniques"),
111                    ],
112                    "complex".to_owned(),
113                )
114            }
115        }
116        Expression::Function { name, args: _ } => {
117            let strategy = match name.as_ref() {
118                "sin" | "cos" | "tan" | "sec" | "csc" | "cot" => "trig",
119                "exp" | "ln" | "log" => "table",
120                _ => "function",
121            };
122            (
123                vec![
124                    format!("Table lookup for function: {}", name),
125                    format!("Standard formula for integral({} d{})", expr, var.name()),
126                ],
127                strategy.to_owned(),
128            )
129        }
130        Expression::Mul(factors) => {
131            let has_const = factors.iter().any(|f| matches!(f, Expression::Number(_)));
132            let has_var = factors
133                .iter()
134                .any(|f| matches!(f, Expression::Symbol(s) if s.name() == var.name()));
135            let has_func = factors
136                .iter()
137                .any(|f| matches!(f, Expression::Function { .. }));
138
139            if has_const && has_var && has_func {
140                (
141                    vec![
142                        format!("Possible substitution pattern detected"),
143                        format!("Checking for u-substitution opportunity"),
144                    ],
145                    "substitution".to_owned(),
146                )
147            } else if has_const {
148                (
149                    vec![
150                        format!("Constant multiple: factor out constant"),
151                        format!("Apply linearity of integration"),
152                    ],
153                    "basic".to_owned(),
154                )
155            } else {
156                (
157                    vec![
158                        format!("Product integration"),
159                        format!("Consider integration by parts or substitution"),
160                    ],
161                    "by_parts".to_owned(),
162                )
163            }
164        }
165        Expression::Add(terms) => (
166            vec![
167                format!("Sum rule: integral of sum = sum of integrals"),
168                format!("Split into {} terms", terms.len()),
169                format!("Integrate each term separately"),
170            ],
171            "sum".to_owned(),
172        ),
173        _ => (
174            vec![
175                format!("Complex expression: {}", expr),
176                format!("May require advanced techniques"),
177            ],
178            "advanced".to_owned(),
179        ),
180    }
181}
182
183/// Generate educational explanation for power rule integration
184///
185/// # Arguments
186///
187/// * `base` - The base expression (typically a variable)
188/// * `exponent` - The exponent expression
189/// * `variable` - The variable of integration
190///
191/// # Examples
192///
193/// ```rust
194/// use mathhook_core::{symbol, Expression};
195/// use mathhook_core::calculus::integrals::educational::explain_power_rule;
196///
197/// let x = symbol!(x);
198/// let base = Expression::symbol(x.clone());
199/// let exponent = Expression::integer(2);
200/// let explanation = explain_power_rule(&base, &exponent, &x);
201/// assert!(explanation.steps.len() >= 3);
202/// ```
203pub fn explain_power_rule(
204    base: &Expression,
205    exponent: &Expression,
206    variable: &Symbol,
207) -> StepByStepExplanation {
208    let mut steps = Vec::new();
209    let original_expr = Expression::pow(base.clone(), exponent.clone());
210
211    let step1 = MessageBuilder::new(MessageCategory::Calculus, MessageType::IntegralPowerRule, 0)
212        .with_substitution("expression", format!("{}", original_expr))
213        .with_substitution("exponent", format!("{}", exponent))
214        .build();
215    if let Some(s) = step1 {
216        steps.push(s);
217    }
218
219    let exponent_plus_one = Expression::add(vec![exponent.clone(), Expression::integer(1)]);
220    let step2 = MessageBuilder::new(MessageCategory::Calculus, MessageType::IntegralPowerRule, 1)
221        .with_substitution("expression", format!("{}", original_expr))
222        .with_substitution("base", format!("{}", base))
223        .with_substitution("exponent", format!("{}", exponent))
224        .with_substitution("variable", variable.name())
225        .with_substitution("exponent_plus_one", format!("{}", exponent_plus_one))
226        .build();
227    if let Some(s) = step2 {
228        steps.push(s);
229    }
230
231    let result = Expression::mul(vec![
232        Expression::pow(base.clone(), exponent_plus_one.clone()),
233        Expression::pow(exponent_plus_one, Expression::integer(-1)),
234    ]);
235
236    let step3 = Step::new("Simplify Result", format!("Result: {} + C", result));
237    steps.push(step3);
238
239    StepByStepExplanation {
240        initial_expression: original_expr,
241        final_expression: result,
242        steps,
243        total_steps: 3,
244        rules_used: vec!["Power Rule for Integration".to_owned()],
245    }
246}
247
248/// Generate educational explanation for constant integration
249///
250/// # Arguments
251///
252/// * `constant` - The constant expression
253/// * `variable` - The variable of integration
254///
255/// # Examples
256///
257/// ```rust
258/// use mathhook_core::{symbol, Expression};
259/// use mathhook_core::calculus::integrals::educational::explain_constant_rule;
260///
261/// let x = symbol!(x);
262/// let constant = Expression::integer(5);
263/// let explanation = explain_constant_rule(&constant, &x);
264/// assert!(explanation.steps.len() >= 2);
265/// ```
266pub fn explain_constant_rule(constant: &Expression, variable: &Symbol) -> StepByStepExplanation {
267    let mut steps = Vec::new();
268
269    let step1 = MessageBuilder::new(MessageCategory::Calculus, MessageType::IntegralConstant, 0)
270        .with_substitution("constant", format!("{}", constant))
271        .build();
272    if let Some(s) = step1 {
273        steps.push(s);
274    }
275
276    let result = Expression::mul(vec![constant.clone(), Expression::symbol(variable.clone())]);
277
278    let step2 = Step::new(
279        "Apply Integration",
280        format!(
281            "integral({} dx) = {}*{} + C",
282            constant,
283            constant,
284            variable.name()
285        ),
286    );
287    steps.push(step2);
288
289    StepByStepExplanation {
290        initial_expression: constant.clone(),
291        final_expression: result,
292        steps,
293        total_steps: 2,
294        rules_used: vec!["Constant Rule".to_owned()],
295    }
296}
297
298/// Generate educational explanation for sum rule integration
299///
300/// # Arguments
301///
302/// * `terms` - The terms being integrated
303/// * `variable` - The variable of integration
304///
305/// # Examples
306///
307/// ```rust
308/// use mathhook_core::{symbol, Expression};
309/// use mathhook_core::calculus::integrals::educational::explain_sum_rule;
310///
311/// let x = symbol!(x);
312/// let terms = vec![
313///     Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
314///     Expression::mul(vec![Expression::integer(3), Expression::symbol(x.clone())]),
315///     Expression::integer(5),
316/// ];
317/// let explanation = explain_sum_rule(&terms, &x);
318/// assert!(explanation.steps.len() >= 3);
319/// ```
320pub fn explain_sum_rule(terms: &[Expression], _variable: &Symbol) -> StepByStepExplanation {
321    let mut steps = Vec::new();
322    let original_expr = Expression::add(terms.to_vec());
323
324    let step1 = Step::new(
325        "Apply Sum Rule",
326        format!("integral((f + g + h) dx) = integral(f dx) + integral(g dx) + integral(h dx)\nSeparate into: {}",
327            terms.iter().map(|t| format!("integral({} dx)", t)).collect::<Vec<_>>().join(" + "))
328    );
329    steps.push(step1);
330
331    let mut integrated_terms = Vec::new();
332    for term in terms {
333        integrated_terms.push(format!("integral({} dx)", term));
334    }
335
336    let step2 = Step::new(
337        "Integrate Each Term",
338        format!(
339            "Evaluating each integral separately:\n{}",
340            integrated_terms.join("\n")
341        ),
342    );
343    steps.push(step2);
344
345    let step3 = Step::new(
346        "Combine Results",
347        "Sum all antiderivatives and add constant C",
348    );
349    steps.push(step3);
350
351    StepByStepExplanation {
352        initial_expression: original_expr.clone(),
353        final_expression: original_expr,
354        steps,
355        total_steps: 3,
356        rules_used: vec!["Sum Rule".to_owned()],
357    }
358}
359
360/// Generate educational explanation for u-substitution
361///
362/// # Arguments
363///
364/// * `integrand` - The integrand expression
365/// * `substitution` - The u substitution
366/// * `variable` - The variable of integration
367///
368/// # Examples
369///
370/// ```rust
371/// use mathhook_core::{symbol, Expression};
372/// use mathhook_core::calculus::integrals::educational::explain_u_substitution;
373///
374/// let x = symbol!(x);
375/// let integrand = Expression::mul(vec![
376///     Expression::integer(2),
377///     Expression::symbol(x.clone()),
378///     Expression::function("sin", vec![Expression::pow(Expression::symbol(x.clone()), Expression::integer(2))]),
379/// ]);
380/// let substitution = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
381/// let explanation = explain_u_substitution(&integrand, &substitution, &x);
382/// assert!(explanation.steps.len() >= 6);
383/// ```
384pub fn explain_u_substitution(
385    integrand: &Expression,
386    substitution: &Expression,
387    variable: &Symbol,
388) -> StepByStepExplanation {
389    let mut steps = Vec::new();
390
391    let step1 = Step::new(
392        "Identify Substitution Candidate",
393        format!(
394            "Inner function: {} (whose derivative appears in integrand)",
395            substitution
396        ),
397    );
398    steps.push(step1);
399
400    let step2 = MessageBuilder::new(
401        MessageCategory::Calculus,
402        MessageType::IntegralUSubstitution,
403        1,
404    )
405    .with_substitution("substitution", format!("{}", substitution))
406    .with_substitution("expression", format!("{}", integrand))
407    .build();
408    if let Some(s) = step2 {
409        steps.push(s);
410    }
411
412    let step3 = Step::new(
413        "Find du",
414        format!(
415            "du/d{} = d/d{}({})\ndu = ... d{}",
416            variable.name(),
417            variable.name(),
418            substitution,
419            variable.name()
420        ),
421    );
422    steps.push(step3);
423
424    let step4 = Step::new(
425        "Rewrite Integral",
426        "Substitute u and du into the integral\nintegral(...) du",
427    );
428    steps.push(step4);
429
430    let step5 = Step::new(
431        "Integrate with Respect to u",
432        "Perform integration in terms of u",
433    );
434    steps.push(step5);
435
436    let step6 = MessageBuilder::new(
437        MessageCategory::Calculus,
438        MessageType::IntegralUSubstitution,
439        4,
440    )
441    .with_substitution("result_in_u", "F(u)")
442    .with_substitution("substitution", format!("{}", substitution))
443    .with_substitution("final_result", format!("F({})", substitution))
444    .build();
445    if let Some(s) = step6 {
446        steps.push(s);
447    }
448
449    StepByStepExplanation {
450        initial_expression: integrand.clone(),
451        final_expression: integrand.clone(),
452        steps,
453        total_steps: 6,
454        rules_used: vec!["U-Substitution".to_owned()],
455    }
456}
457
458/// Generate educational explanation for integration by parts
459///
460/// # Arguments
461///
462/// * `u_choice` - The u function choice
463/// * `dv_choice` - The dv function choice
464/// * `variable` - The variable of integration
465///
466/// # Examples
467///
468/// ```rust
469/// use mathhook_core::{symbol, Expression};
470/// use mathhook_core::calculus::integrals::educational::explain_integration_by_parts;
471///
472/// let x = symbol!(x);
473/// let u_choice = Expression::symbol(x.clone());
474/// let dv_choice = Expression::function("exp", vec![Expression::symbol(x.clone())]);
475/// let explanation = explain_integration_by_parts(&u_choice, &dv_choice, &x);
476/// assert!(explanation.steps.len() >= 7);
477/// ```
478pub fn explain_integration_by_parts(
479    u_choice: &Expression,
480    dv_choice: &Expression,
481    variable: &Symbol,
482) -> StepByStepExplanation {
483    let mut steps = Vec::new();
484    let product = Expression::mul(vec![u_choice.clone(), dv_choice.clone()]);
485
486    let step1 = Step::new(
487        "Identify Integration by Parts",
488        format!("Product of functions: {} and {}", u_choice, dv_choice),
489    );
490    steps.push(step1);
491
492    let step2 = Step::new("State Formula", "integral(u dv) = uv - integral(v du)");
493    steps.push(step2);
494
495    let step3 = MessageBuilder::new(MessageCategory::Calculus, MessageType::IntegralByParts, 0)
496        .with_substitution("u_choice", format!("{}", u_choice))
497        .with_substitution("dv_choice", format!("{} d{}", dv_choice, variable.name()))
498        .build();
499    if let Some(s) = step3 {
500        steps.push(s);
501    }
502
503    let step4 = Step::new(
504        "Find du and v",
505        format!(
506            "du = d/d{}({}) d{}\nv = integral({} d{})",
507            variable.name(),
508            u_choice,
509            variable.name(),
510            dv_choice,
511            variable.name()
512        ),
513    );
514    steps.push(step4);
515
516    let step5 = Step::new("Apply Formula", format!("{}*v - integral(v du)", u_choice));
517    steps.push(step5);
518
519    let step6 = Step::new("Evaluate Remaining Integral", "Compute integral(v du)");
520    steps.push(step6);
521
522    let step7 = Step::new("Complete Solution", "Combine terms and add constant C");
523    steps.push(step7);
524
525    StepByStepExplanation {
526        initial_expression: product.clone(),
527        final_expression: product,
528        steps,
529        total_steps: 7,
530        rules_used: vec!["Integration by Parts".to_owned()],
531    }
532}
533
534/// Generate educational explanation for definite integral evaluation
535///
536/// # Arguments
537///
538/// * `integrand` - The integrand expression
539/// * `variable` - The variable of integration
540/// * `lower_bound` - The lower limit of integration
541/// * `upper_bound` - The upper limit of integration
542///
543/// # Examples
544///
545/// ```rust
546/// use mathhook_core::{symbol, Expression};
547/// use mathhook_core::calculus::integrals::educational::explain_definite_integral;
548///
549/// let x = symbol!(x);
550/// let integrand = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
551/// let lower = Expression::integer(0);
552/// let upper = Expression::integer(2);
553/// let explanation = explain_definite_integral(&integrand, &x, &lower, &upper);
554/// assert!(explanation.steps.len() >= 5);
555/// ```
556pub fn explain_definite_integral(
557    integrand: &Expression,
558    variable: &Symbol,
559    lower_bound: &Expression,
560    upper_bound: &Expression,
561) -> StepByStepExplanation {
562    let mut steps = Vec::new();
563
564    let step1 = Step::new(
565        "Find Antiderivative",
566        format!(
567            "integral({} d{}) = F({})",
568            integrand,
569            variable.name(),
570            variable.name()
571        ),
572    );
573    steps.push(step1);
574
575    let step2 = MessageBuilder::new(MessageCategory::Calculus, MessageType::IntegralDefinite, 1)
576        .with_substitution("lower_bound", format!("{}", lower_bound))
577        .with_substitution("upper_bound", format!("{}", upper_bound))
578        .with_substitution("antiderivative", "F(x)")
579        .build();
580    if let Some(s) = step2 {
581        steps.push(s);
582    }
583
584    let step3 = Step::new(
585        "Evaluate at Upper Bound",
586        format!("F({}) = ...", upper_bound),
587    );
588    steps.push(step3);
589
590    let step4 = Step::new(
591        "Evaluate at Lower Bound",
592        format!("F({}) = ...", lower_bound),
593    );
594    steps.push(step4);
595
596    let step5 = Step::new(
597        "Calculate Difference",
598        format!("F({}) - F({})", upper_bound, lower_bound),
599    );
600    steps.push(step5);
601
602    let definite_expr = Expression::definite_integral(
603        integrand.clone(),
604        variable.clone(),
605        lower_bound.clone(),
606        upper_bound.clone(),
607    );
608
609    StepByStepExplanation {
610        initial_expression: integrand.clone(),
611        final_expression: definite_expr,
612        steps,
613        total_steps: 5,
614        rules_used: vec!["Fundamental Theorem of Calculus".to_owned()],
615    }
616}
617
618#[cfg(test)]
619mod tests {
620    use super::*;
621    use crate::symbol;
622
623    #[test]
624    fn test_explain_power_rule_has_minimum_steps() {
625        let x = symbol!(x);
626        let base = Expression::symbol(x.clone());
627        let exponent = Expression::integer(2);
628        let explanation = explain_power_rule(&base, &exponent, &x);
629        assert!(explanation.steps.len() >= 3);
630        assert_eq!(explanation.total_steps, 3);
631    }
632
633    #[test]
634    fn test_explain_constant_rule_has_minimum_steps() {
635        let x = symbol!(x);
636        let constant = Expression::integer(5);
637        let explanation = explain_constant_rule(&constant, &x);
638        assert!(explanation.steps.len() >= 2);
639        assert_eq!(explanation.total_steps, 2);
640    }
641
642    #[test]
643    fn test_explain_sum_rule_has_minimum_steps() {
644        let x = symbol!(x);
645        let terms = vec![
646            Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
647            Expression::mul(vec![Expression::integer(3), Expression::symbol(x.clone())]),
648            Expression::integer(5),
649        ];
650        let explanation = explain_sum_rule(&terms, &x);
651        assert!(explanation.steps.len() >= 3);
652        assert_eq!(explanation.total_steps, 3);
653    }
654
655    #[test]
656    fn test_explain_u_substitution_has_minimum_steps() {
657        let x = symbol!(x);
658        let integrand =
659            Expression::mul(vec![Expression::integer(2), Expression::symbol(x.clone())]);
660        let substitution = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
661        let explanation = explain_u_substitution(&integrand, &substitution, &x);
662        assert!(explanation.steps.len() >= 6);
663        assert_eq!(explanation.total_steps, 6);
664    }
665
666    #[test]
667    fn test_explain_integration_by_parts_has_minimum_steps() {
668        let x = symbol!(x);
669        let u_choice = Expression::symbol(x.clone());
670        let dv_choice = Expression::function("exp", vec![Expression::symbol(x.clone())]);
671        let explanation = explain_integration_by_parts(&u_choice, &dv_choice, &x);
672        assert!(explanation.steps.len() >= 7);
673        assert_eq!(explanation.total_steps, 7);
674    }
675
676    #[test]
677    fn test_explain_definite_integral_has_minimum_steps() {
678        let x = symbol!(x);
679        let integrand = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
680        let lower = Expression::integer(0);
681        let upper = Expression::integer(2);
682        let explanation = explain_definite_integral(&integrand, &x, &lower, &upper);
683        assert!(explanation.steps.len() >= 5);
684        assert_eq!(explanation.total_steps, 5);
685    }
686}