1use crate::core::{Expression, Symbol};
7use crate::educational::message_registry::{MessageBuilder, MessageCategory, MessageType};
8use crate::educational::step_by_step::{Step, StepByStepExplanation};
9
10pub struct IntegrationExplanation {
15 steps: Vec<String>,
16 strategy: String,
17}
18
19impl IntegrationExplanation {
20 pub fn generate(expr: &Expression, var: &Symbol) -> Self {
43 let (steps, strategy) = analyze_and_explain(expr, var);
44 Self { steps, strategy }
45 }
46
47 pub fn steps(&self) -> Vec<String> {
49 self.steps.clone()
50 }
51
52 pub fn strategy_used(&self) -> String {
54 self.strategy.clone()
55 }
56}
57
58fn 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_str(), "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_str() {
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
183pub 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
248pub 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
298pub 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
360pub 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
458pub 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
534pub 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}