1use super::methods::LimitMethods;
2use super::Limits;
3use crate::calculus::derivatives::Derivative;
4use crate::educational::enhanced_steps::{EnhancedStepBuilder, EnhancedStepExplanation};
5use crate::formatter::latex::LaTeXFormatter;
6use crate::{Expression, Symbol};
7
8pub struct LimitEducation;
10
11impl LimitEducation {
12 fn format_latex(expr: &Expression) -> String {
14 expr.to_latex(None)
15 .unwrap_or_else(|_| format!("{:?}", expr))
16 }
17
18 fn find_num_denom_in_mul(factors: &[Expression]) -> (Option<&Expression>, Option<&Expression>) {
22 use crate::core::Number;
23 use num_bigint::BigInt;
24
25 fn is_minus_one(exp: &Expression) -> bool {
26 match exp {
27 Expression::Number(Number::Integer(-1)) => true,
28 Expression::Number(Number::Rational(r)) => {
29 r.denom() == &BigInt::from(1) && r.numer() == &BigInt::from(-1)
30 }
31 _ => false,
32 }
33 }
34
35 if factors.len() != 2 {
36 return (None, None);
37 }
38
39 if let Expression::Pow(denom_base, exp) = &factors[1] {
41 if is_minus_one(exp) {
42 return (Some(&factors[0]), Some(denom_base.as_ref()));
43 }
44 }
45
46 if let Expression::Pow(denom_base, exp) = &factors[0] {
48 if is_minus_one(exp) {
49 return (Some(&factors[1]), Some(denom_base.as_ref()));
50 }
51 }
52
53 (None, None)
54 }
55
56 pub fn direct_substitution_explanation(
72 expr: &Expression,
73 variable: &Symbol,
74 point: &Expression,
75 result: &Expression,
76 ) -> EnhancedStepExplanation {
77 let mut steps = Vec::new();
78
79 let expr_latex = Self::format_latex(expr);
80 let var_name = variable.name();
81 let point_latex = Self::format_latex(point);
82 let result_latex = Self::format_latex(result);
83
84 steps.push(
85 EnhancedStepBuilder::new("limit_direct_1")
86 .with_human_message(
87 "Attempt Direct Substitution",
88 &format!(
89 "To find lim({} -> {}) {}, first try direct substitution.\nSubstitute {} = {} into the expression.",
90 var_name, point_latex, expr_latex, var_name, point_latex
91 )
92 )
93 .with_api_data("limit", "direct_substitution", "substitute")
94 .with_input("expression", &expr_latex)
95 .with_input("variable", var_name)
96 .with_input("point", &point_latex)
97 .with_message_key("calculus", "limit_direct", 0)
98 .build()
99 );
100
101 let substituted_expr = LimitMethods::substitute_and_evaluate(expr, variable, point);
102 let substituted_latex = Self::format_latex(&substituted_expr);
103
104 steps.push(
105 EnhancedStepBuilder::new("limit_direct_2")
106 .with_human_message(
107 "Evaluate Expression",
108 &format!(
109 "After substituting {} = {} into {}:\nResult: {}",
110 var_name, point_latex, expr_latex, substituted_latex
111 ),
112 )
113 .with_api_data("limit", "evaluation", "compute")
114 .with_input("substituted_expression", &substituted_latex)
115 .with_output("intermediate_result", &result_latex)
116 .with_message_key("calculus", "limit_direct", 1)
117 .build(),
118 );
119
120 steps.push(
121 EnhancedStepBuilder::new("limit_direct_3")
122 .with_human_message(
123 "Verify No Indeterminate Form",
124 &format!(
125 "The result {} is well-defined (not 0/0 or infinity/infinity).\nTherefore, the limit exists and equals {}.",
126 result_latex, result_latex
127 )
128 )
129 .with_api_data("limit", "verification", "check_form")
130 .with_output("limit_result", &result_latex)
131 .with_output("form_type", "determinate")
132 .with_message_key("calculus", "limit_direct", 1)
133 .build()
134 );
135
136 EnhancedStepExplanation::new(steps)
137 }
138
139 pub fn indeterminate_form_explanation(
155 expr: &Expression,
156 variable: &Symbol,
157 point: &Expression,
158 form_type: &str,
159 ) -> EnhancedStepExplanation {
160 let mut steps = Vec::new();
161
162 let expr_latex = Self::format_latex(expr);
163 let var_name = variable.name();
164 let point_latex = Self::format_latex(point);
165
166 steps.push(
167 EnhancedStepBuilder::new("limit_indet_1")
168 .with_human_message(
169 "Attempt Direct Substitution",
170 &format!(
171 "To find lim({} -> {}) {}, substitute {} = {}:",
172 var_name, point_latex, expr_latex, var_name, point_latex
173 ),
174 )
175 .with_api_data("limit", "substitution", "direct_attempt")
176 .with_input("expression", &expr_latex)
177 .with_input("variable", var_name)
178 .with_input("point", &point_latex)
179 .with_message_key("calculus", "limit_indeterminate", 0)
180 .build(),
181 );
182
183 if let Expression::Mul(factors) = expr {
184 if factors.len() == 2 {
185 let (numerator, denominator) = Self::find_num_denom_in_mul(factors);
188
189 if let Some((num, denom)) = numerator.zip(denominator) {
190 let num_latex = Self::format_latex(num);
191 let den_latex = Self::format_latex(denom);
192
193 let num_at_point = LimitMethods::substitute_and_evaluate(num, variable, point);
194 let den_at_point =
195 LimitMethods::substitute_and_evaluate(denom, variable, point);
196
197 let num_result_latex = Self::format_latex(&num_at_point);
198 let den_result_latex = Self::format_latex(&den_at_point);
199
200 steps.push(
201 EnhancedStepBuilder::new("limit_indet_2")
202 .with_human_message(
203 "Evaluate Components",
204 &format!(
205 "Numerator at {} = {}: {} = {}\nDenominator at {} = {}: {} = {}",
206 var_name, point_latex, num_latex, num_result_latex,
207 var_name, point_latex, den_latex, den_result_latex
208 )
209 )
210 .with_api_data("limit", "component_evaluation", "rational")
211 .with_input("numerator", &num_latex)
212 .with_input("denominator", &den_latex)
213 .with_output("numerator_value", &num_result_latex)
214 .with_output("denominator_value", &den_result_latex)
215 .with_message_key("calculus", "limit_indeterminate", 1)
216 .build()
217 );
218 }
219 }
220 }
221
222 steps.push(
223 EnhancedStepBuilder::new("limit_indet_3")
224 .with_human_message(
225 "Identify Indeterminate Form",
226 &format!(
227 "The result is {} (indeterminate form).\nThis form is undefined and requires further analysis.",
228 form_type
229 )
230 )
231 .with_api_data("limit", "form_detection", "indeterminate")
232 .with_output("form_type", form_type)
233 .with_output("requires_resolution", "true")
234 .with_message_key("calculus", "limit_indeterminate", 0)
235 .build()
236 );
237
238 let resolution_strategy = match form_type {
239 "0/0" => "Factorization: Factor numerator and denominator to cancel common terms, or apply L'Hopital's rule",
240 "infinity/infinity" => "Divide by highest power: Divide numerator and denominator by highest power term",
241 "0*infinity" => "Rewrite as fraction: Convert to 0/0 or infinity/infinity form",
242 _ => "Apply algebraic manipulation or L'Hopital's rule"
243 };
244
245 steps.push(
246 EnhancedStepBuilder::new("limit_indet_4")
247 .with_human_message(
248 "Resolution Strategy",
249 &format!("To resolve {} form:\n{}", form_type, resolution_strategy),
250 )
251 .with_api_data("limit", "strategy", "resolution")
252 .with_input("indeterminate_form", form_type)
253 .with_output("strategy", resolution_strategy)
254 .with_message_key("calculus", "limit_indeterminate", 1)
255 .build(),
256 );
257
258 EnhancedStepExplanation::new(steps)
259 }
260
261 pub fn lhopital_rule_explanation(
277 numerator: &Expression,
278 denominator: &Expression,
279 variable: &Symbol,
280 point: &Expression,
281 ) -> EnhancedStepExplanation {
282 let mut steps = Vec::new();
283
284 let num_latex = Self::format_latex(numerator);
285 let den_latex = Self::format_latex(denominator);
286 let var_name = variable.name();
287 let point_latex = Self::format_latex(point);
288
289 let num_at_point = LimitMethods::substitute_and_evaluate(numerator, variable, point);
290 let den_at_point = LimitMethods::substitute_and_evaluate(denominator, variable, point);
291
292 let form_type = if num_at_point.is_zero() && den_at_point.is_zero() {
293 "0/0"
294 } else {
295 "infinity/infinity"
296 };
297
298 steps.push(
299 EnhancedStepBuilder::new("limit_lhopital_1")
300 .with_human_message(
301 "Check Conditions for L'Hopital's Rule",
302 &format!(
303 "For lim({} -> {}) {}/{}, direct substitution gives {}.\nL'Hopital's rule applies to indeterminate forms 0/0 and infinity/infinity.",
304 var_name, point_latex, num_latex, den_latex, form_type
305 )
306 )
307 .with_api_data("limit", "lhopital_check", "conditions")
308 .with_input("numerator", &num_latex)
309 .with_input("denominator", &den_latex)
310 .with_output("form_type", form_type)
311 .with_output("rule_applies", "true")
312 .with_message_key("calculus", "limit_lhopital", 0)
313 .build()
314 );
315
316 steps.push(
317 EnhancedStepBuilder::new("limit_lhopital_2")
318 .with_human_message(
319 "State L'Hopital's Rule",
320 "If lim(f(x)/g(x)) gives 0/0 or infinity/infinity, then:\nlim(f(x)/g(x)) = lim(f'(x)/g'(x))\nprovided the limit on the right exists."
321 )
322 .with_api_data("limit", "rule_statement", "lhopital_theorem")
323 .with_output("theorem", "lhopital")
324 .with_output("applicable_forms", "0/0, infinity/infinity")
325 .with_message_key("calculus", "limit_lhopital", 0)
326 .build()
327 );
328
329 let num_derivative = numerator.derivative(variable.clone());
330 let num_deriv_latex = Self::format_latex(&num_derivative);
331
332 steps.push(
333 EnhancedStepBuilder::new("limit_lhopital_3")
334 .with_human_message(
335 "Differentiate Numerator",
336 &format!(
337 "Find derivative of numerator with respect to {}:\nd/d{}[{}] = {}",
338 var_name, var_name, num_latex, num_deriv_latex
339 ),
340 )
341 .with_api_data("limit", "differentiation", "numerator")
342 .with_input("original", &num_latex)
343 .with_output("derivative", &num_deriv_latex)
344 .with_message_key("calculus", "limit_lhopital", 1)
345 .build(),
346 );
347
348 let den_derivative = denominator.derivative(variable.clone());
349 let den_deriv_latex = Self::format_latex(&den_derivative);
350
351 steps.push(
352 EnhancedStepBuilder::new("limit_lhopital_4")
353 .with_human_message(
354 "Differentiate Denominator",
355 &format!(
356 "Find derivative of denominator with respect to {}:\nd/d{}[{}] = {}",
357 var_name, var_name, den_latex, den_deriv_latex
358 ),
359 )
360 .with_api_data("limit", "differentiation", "denominator")
361 .with_input("original", &den_latex)
362 .with_output("derivative", &den_deriv_latex)
363 .with_message_key("calculus", "limit_lhopital", 1)
364 .build(),
365 );
366
367 steps.push(
368 EnhancedStepBuilder::new("limit_lhopital_5")
369 .with_human_message(
370 "Apply L'Hopital's Rule",
371 &format!(
372 "By L'Hopital's rule:\nlim({} -> {}) {}/{} = lim({} -> {}) {}/{}",
373 var_name,
374 point_latex,
375 num_latex,
376 den_latex,
377 var_name,
378 point_latex,
379 num_deriv_latex,
380 den_deriv_latex
381 ),
382 )
383 .with_api_data("limit", "rule_application", "substitute_derivatives")
384 .with_input("original_limit", &format!("{}/{}", num_latex, den_latex))
385 .with_output(
386 "new_limit",
387 &format!("{}/{}", num_deriv_latex, den_deriv_latex),
388 )
389 .with_message_key("calculus", "limit_lhopital", 1)
390 .build(),
391 );
392
393 let new_limit_result =
394 LimitMethods::rational_limit(&num_derivative, &den_derivative, variable, point);
395 let result_latex = Self::format_latex(&new_limit_result);
396
397 steps.push(
398 EnhancedStepBuilder::new("limit_lhopital_6")
399 .with_human_message(
400 "Evaluate New Limit",
401 &format!(
402 "Now evaluate lim({} -> {}) {}/{}:\nResult: {}",
403 var_name, point_latex, num_deriv_latex, den_deriv_latex, result_latex
404 ),
405 )
406 .with_api_data("limit", "final_evaluation", "result")
407 .with_output("final_result", &result_latex)
408 .with_message_key("calculus", "limit_lhopital", 1)
409 .build(),
410 );
411
412 EnhancedStepExplanation::new(steps)
413 }
414
415 pub fn limit_laws_explanation(
430 expr: &Expression,
431 variable: &Symbol,
432 point: &Expression,
433 ) -> EnhancedStepExplanation {
434 let mut steps = Vec::new();
435
436 let expr_latex = Self::format_latex(expr);
437 let var_name = variable.name();
438 let point_latex = Self::format_latex(point);
439
440 steps.push(
441 EnhancedStepBuilder::new("limit_laws_1")
442 .with_human_message(
443 "Identify Applicable Limit Laws",
444 &format!(
445 "To find lim({} -> {}) {}, we can apply limit laws:\n• Sum Law: lim[f(x) + g(x)] = lim f(x) + lim g(x)\n• Product Law: lim[f(x) * g(x)] = lim f(x) * lim g(x)\n• Constant Multiple Law: lim[c * f(x)] = c * lim f(x)",
446 var_name, point_latex, expr_latex
447 )
448 )
449 .with_api_data("limit", "laws", "introduction")
450 .with_input("expression", &expr_latex)
451 .with_message_key("calculus", "limit_laws", 0)
452 .build()
453 );
454
455 match expr {
456 Expression::Add(terms) => {
457 steps.push(
458 EnhancedStepBuilder::new("limit_laws_2")
459 .with_human_message(
460 "Apply Sum Law",
461 &format!(
462 "The expression is a sum of {} terms.\nBy Sum Law: lim[f + g] = lim f + lim g\nEvaluate limit of each term separately.",
463 terms.len()
464 )
465 )
466 .with_api_data("limit", "law_application", "sum")
467 .with_input("term_count", &terms.len().to_string())
468 .with_output("law_used", "sum_law")
469 .with_message_key("calculus", "limit_laws", 0)
470 .build()
471 );
472
473 let term_limits: Vec<String> = terms
474 .iter()
475 .map(|term| {
476 let limit_result = term.limit(variable, point);
477 format!(
478 "lim {} = {}",
479 Self::format_latex(term),
480 Self::format_latex(&limit_result)
481 )
482 })
483 .collect();
484
485 steps.push(
486 EnhancedStepBuilder::new("limit_laws_3")
487 .with_human_message(
488 "Evaluate Individual Terms",
489 &format!("Evaluate limit of each term:\n{}", term_limits.join("\n")),
490 )
491 .with_api_data("limit", "term_evaluation", "individual")
492 .with_message_key("calculus", "limit_laws", 1)
493 .build(),
494 );
495 }
496 Expression::Mul(factors) => {
497 let has_constant = factors.iter().any(|f| matches!(f, Expression::Number(_)));
498
499 if has_constant {
500 steps.push(
501 EnhancedStepBuilder::new("limit_laws_2")
502 .with_human_message(
503 "Apply Constant Multiple Law",
504 "By Constant Multiple Law: lim[c * f(x)] = c * lim f(x)\nFactor out constants and evaluate limit of remaining expression."
505 )
506 .with_api_data("limit", "law_application", "constant_multiple")
507 .with_output("law_used", "constant_multiple_law")
508 .with_message_key("calculus", "limit_laws", 0)
509 .build()
510 );
511 } else {
512 steps.push(
513 EnhancedStepBuilder::new("limit_laws_2")
514 .with_human_message(
515 "Apply Product Law",
516 &format!(
517 "The expression is a product of {} factors.\nBy Product Law: lim[f * g] = lim f * lim g\nEvaluate limit of each factor separately.",
518 factors.len()
519 )
520 )
521 .with_api_data("limit", "law_application", "product")
522 .with_input("factor_count", &factors.len().to_string())
523 .with_output("law_used", "product_law")
524 .with_message_key("calculus", "limit_laws", 1)
525 .build()
526 );
527 }
528
529 steps.push(
530 EnhancedStepBuilder::new("limit_laws_3")
531 .with_human_message(
532 "Evaluate Components",
533 "Evaluate limit of each component and combine results using the limit law."
534 )
535 .with_api_data("limit", "component_evaluation", "combine")
536 .with_message_key("calculus", "limit_laws", 1)
537 .build()
538 );
539 }
540 _ => {
541 steps.push(
542 EnhancedStepBuilder::new("limit_laws_2")
543 .with_human_message(
544 "Direct Evaluation",
545 "For simple expressions, directly substitute the limit point.",
546 )
547 .with_api_data("limit", "direct", "simple")
548 .with_message_key("calculus", "limit_laws", 0)
549 .build(),
550 );
551 }
552 }
553
554 let result = expr.limit(variable, point);
555 let result_latex = Self::format_latex(&result);
556
557 steps.push(
558 EnhancedStepBuilder::new("limit_laws_4")
559 .with_human_message(
560 "Combine Results",
561 &format!(
562 "Combining the individual limits:\nlim({} -> {}) {} = {}",
563 var_name, point_latex, expr_latex, result_latex
564 ),
565 )
566 .with_api_data("limit", "combination", "final")
567 .with_output("final_result", &result_latex)
568 .with_message_key("calculus", "limit_laws", 1)
569 .build(),
570 );
571
572 EnhancedStepExplanation::new(steps)
573 }
574
575 pub fn limit_at_infinity_explanation(
589 expr: &Expression,
590 variable: &Symbol,
591 ) -> EnhancedStepExplanation {
592 let mut steps = Vec::new();
593
594 let expr_latex = Self::format_latex(expr);
595 let var_name = variable.name();
596
597 steps.push(
598 EnhancedStepBuilder::new("limit_infinity_1")
599 .with_human_message(
600 "Identify Form",
601 &format!(
602 "To find lim({} -> infinity) {}, first identify the form.\nFor rational functions, both numerator and denominator may approach infinity (infinity/infinity form).",
603 var_name, expr_latex
604 )
605 )
606 .with_api_data("limit", "infinity", "identify_form")
607 .with_input("expression", &expr_latex)
608 .with_input("limit_point", "infinity")
609 .with_message_key("calculus", "limit_infinity", 0)
610 .build()
611 );
612
613 if let Expression::Mul(factors) = expr {
614 if factors.len() == 2 {
615 if let Expression::Pow(denom, exp) = &factors[1] {
616 if **exp == Expression::integer(-1) {
617 let numerator = &factors[0];
618 let num_latex = Self::format_latex(numerator);
619 let den_latex = Self::format_latex(denom);
620
621 steps.push(
622 EnhancedStepBuilder::new("limit_infinity_2")
623 .with_human_message(
624 "Divide by Highest Power",
625 &format!(
626 "For rational function {}/{}:\nDivide both numerator and denominator by the highest power of {} in the denominator.\nThis transforms terms: {}/infinity -> 0",
627 num_latex, den_latex, var_name, var_name
628 )
629 )
630 .with_api_data("limit", "technique", "divide_highest_power")
631 .with_input("numerator", &num_latex)
632 .with_input("denominator", &den_latex)
633 .with_output("technique", "divide_by_highest_power")
634 .with_message_key("calculus", "limit_infinity", 1)
635 .build()
636 );
637
638 steps.push(
639 EnhancedStepBuilder::new("limit_infinity_3")
640 .with_human_message(
641 "Evaluate as x Approaches Infinity",
642 &format!(
643 "As {} -> infinity:\n• Terms like 1/{}, 1/{}^2, etc. approach 0\n• Leading coefficients remain\n• Evaluate the simplified expression",
644 var_name, var_name, var_name
645 )
646 )
647 .with_api_data("limit", "evaluation", "infinity_behavior")
648 .with_message_key("calculus", "limit_infinity", 1)
649 .build()
650 );
651 }
652 }
653 }
654 } else {
655 steps.push(
656 EnhancedStepBuilder::new("limit_infinity_2")
657 .with_human_message(
658 "Analyze Dominant Term",
659 &format!(
660 "For polynomial or simple expressions, identify the dominant term (highest power of {}).\nThe limit behavior is determined by this dominant term.",
661 var_name
662 )
663 )
664 .with_api_data("limit", "analysis", "dominant_term")
665 .with_message_key("calculus", "limit_infinity", 0)
666 .build()
667 );
668
669 steps.push(
670 EnhancedStepBuilder::new("limit_infinity_3")
671 .with_human_message(
672 "Evaluate Limit",
673 &format!(
674 "As {} -> infinity, evaluate the behavior of the expression.",
675 var_name
676 ),
677 )
678 .with_api_data("limit", "evaluation", "determine")
679 .with_message_key("calculus", "limit_infinity", 1)
680 .build(),
681 );
682 }
683
684 let result = expr.limit_at_infinity(variable);
685 let result_latex = Self::format_latex(&result);
686
687 steps.push(
688 EnhancedStepBuilder::new("limit_infinity_4")
689 .with_human_message(
690 "Final Result",
691 &format!(
692 "Therefore:\nlim({} -> infinity) {} = {}",
693 var_name, expr_latex, result_latex
694 ),
695 )
696 .with_api_data("limit", "result", "infinity")
697 .with_output("final_result", &result_latex)
698 .with_message_key("calculus", "limit_infinity", 1)
699 .build(),
700 );
701
702 EnhancedStepExplanation::new(steps)
703 }
704}