mathhook_core/educational/step_by_step.rs
1//! Step-by-step explanation system for educational purposes
2//! Provides detailed explanations of simplification and algebraic operations
3
4use crate::core::Expression;
5use serde::{Deserialize, Serialize};
6
7/// Represents a single step in a mathematical operation
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9pub struct Step {
10 pub title: String,
11 pub description: String,
12 pub expression: Expression,
13 pub rule_applied: String,
14 pub latex: Option<String>,
15}
16
17impl Step {
18 /// Create a new step with title and description
19 pub fn new<T: Into<String>, D: Into<String>>(title: T, description: D) -> Self {
20 Self {
21 title: title.into(),
22 description: description.into(),
23 expression: Expression::integer(0), // Default
24 rule_applied: "Custom".to_owned(),
25 latex: None,
26 }
27 }
28}
29
30/// Complete step-by-step explanation of a mathematical operation
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32pub struct StepByStepExplanation {
33 pub initial_expression: Expression,
34 pub final_expression: Expression,
35 pub steps: Vec<Step>,
36 pub total_steps: usize,
37 pub rules_used: Vec<String>,
38}
39
40impl StepByStepExplanation {
41 /// Create a new step-by-step explanation
42 pub fn new(steps: Vec<Step>) -> Self {
43 let total_steps = steps.len();
44 let initial_expr = Expression::integer(0); // Default
45 let final_expr = Expression::integer(0); // Default
46 let rules_used = steps.iter().map(|s| s.rule_applied.clone()).collect();
47
48 Self {
49 initial_expression: initial_expr,
50 final_expression: final_expr,
51 steps,
52 total_steps,
53 rules_used,
54 }
55 }
56}
57
58/// Trait for generating step-by-step explanations
59pub trait StepByStep {
60 fn explain_simplification(&self) -> StepByStepExplanation;
61 fn explain_expansion(&self) -> StepByStepExplanation;
62 fn explain_factorization(&self) -> StepByStepExplanation;
63}
64
65impl StepByStep for Expression {
66 /// Generate step-by-step explanation for simplification
67 fn explain_simplification(&self) -> StepByStepExplanation {
68 // Unused message registry imports removed
69
70 let mut steps = Vec::new();
71 let initial_expr = self.clone();
72
73 steps.push(Step {
74 title: "Given Expression".to_owned(),
75 description: format!("Simplify: {}", self),
76 expression: self.clone(),
77 rule_applied: "Initial".to_owned(),
78 latex: None,
79 });
80
81 let mut current = self.clone();
82 let mut changed = true;
83
84 while changed {
85 changed = false;
86 let before = current.clone();
87
88 if let Some((new_expr, step)) = simplify_step_combine_like_terms(¤t) {
89 if new_expr != current {
90 steps.push(step);
91 current = new_expr;
92 changed = true;
93 continue;
94 }
95 }
96
97 if let Some((new_expr, step)) = simplify_step_identity_rules(¤t) {
98 if new_expr != current {
99 steps.push(step);
100 current = new_expr;
101 changed = true;
102 continue;
103 }
104 }
105
106 if let Some((new_expr, step)) = simplify_step_power_rules(¤t) {
107 if new_expr != current {
108 steps.push(step);
109 current = new_expr;
110 changed = true;
111 continue;
112 }
113 }
114
115 if let Some((new_expr, step)) = simplify_step_coefficient_multiplication(¤t) {
116 if new_expr != current {
117 steps.push(step);
118 current = new_expr;
119 changed = true;
120 continue;
121 }
122 }
123
124 if before == current {
125 break;
126 }
127 }
128
129 steps.push(Step {
130 title: "Final Simplified Form".to_owned(),
131 description: format!("Fully simplified: {}", current),
132 expression: current.clone(),
133 rule_applied: "Final".to_owned(),
134 latex: None,
135 });
136
137 let total_steps = steps.len().saturating_sub(2);
138
139 StepByStepExplanation {
140 initial_expression: initial_expr,
141 final_expression: current,
142 steps,
143 total_steps,
144 rules_used: vec!["Simplification".to_owned()],
145 }
146 }
147
148 // Temporarily disabled - complex implementation
149 /*
150 fn explain_simplification_full(&self) -> StepByStepExplanation {
151 let mut steps = Vec::new();
152 let mut current = self.clone();
153 let mut step_count = 0;
154 let mut rules_used = Vec::new();
155
156 // Step 1: Initial expression
157 steps.push(Step {
158 title: "Starting Expression".to_string(),
159 description: "Starting expression".to_string(),
160 expression: current.clone(),
161 rule_applied: "Initial".to_string(),
162 latex: Some(self.to_latex(None).unwrap_or_else(|_| "expression".to_string())),
163 });
164
165 // Apply simplification rules step by step
166 current = self.apply_simplification_steps(&mut steps, &mut rules_used, &mut step_count);
167
168 StepByStepExplanation {
169 initial_expression: self.clone(),
170 final_expression: current,
171 steps,
172 total_steps: step_count,
173 rules_used,
174 }
175 }
176
177 */
178
179 /// Generate step-by-step explanation for expansion
180 fn explain_expansion(&self) -> StepByStepExplanation {
181 // Unused message registry imports removed
182
183 let mut steps = Vec::new();
184 let initial_expr = self.clone();
185
186 steps.push(Step {
187 title: "Given Expression".to_owned(),
188 description: format!("Expand: {}", self),
189 expression: self.clone(),
190 rule_applied: "Initial".to_owned(),
191 latex: None,
192 });
193
194 if let Some((expanded, method_steps)) = expand_expression(self) {
195 steps.extend(method_steps);
196 steps.push(Step {
197 title: "Final Expanded Form".to_owned(),
198 description: format!("Fully expanded: {}", expanded),
199 expression: expanded.clone(),
200 rule_applied: "Final".to_owned(),
201 latex: None,
202 });
203
204 let total_steps = steps.len().saturating_sub(2);
205
206 StepByStepExplanation {
207 initial_expression: initial_expr,
208 final_expression: expanded,
209 steps,
210 total_steps,
211 rules_used: vec!["Expansion".to_owned()],
212 }
213 } else {
214 steps.push(Step {
215 title: "Already Expanded".to_owned(),
216 description: "Expression is already in expanded form".to_owned(),
217 expression: self.clone(),
218 rule_applied: "None".to_owned(),
219 latex: None,
220 });
221
222 StepByStepExplanation {
223 initial_expression: initial_expr.clone(),
224 final_expression: initial_expr,
225 steps,
226 total_steps: 0,
227 rules_used: vec![],
228 }
229 }
230 }
231
232 /*
233 fn explain_expansion_full(&self) -> StepByStepExplanation {
234 let mut steps = Vec::new();
235 let mut rules_used = Vec::new();
236
237 steps.push(Step {
238 description: "Starting expression".to_string(),
239 expression: self.clone(),
240 rule_applied: "Initial".to_string(),
241 latex: Some(self.to_latex(None).unwrap_or_else(|_| "expression".to_string())),
242 });
243
244 // For now, just show the final expanded form
245 // Full implementation would show each expansion step
246 let expanded = self.clone(); // Would call expand() when implemented
247
248 steps.push(Step {
249 description: "Applied expansion rules".to_string(),
250 expression: expanded.clone(),
251 rule_applied: "Expansion".to_string(),
252 latex: Some(expanded.to_latex(None).unwrap_or_else(|_| "expression".to_string())),
253 });
254
255 rules_used.push("Expansion".to_string());
256
257 StepByStepExplanation {
258 initial_expression: self.clone(),
259 final_expression: expanded,
260 steps,
261 total_steps: 1,
262 rules_used,
263 }
264 }
265
266 */
267
268 /// Generate step-by-step explanation for factorization
269 fn explain_factorization(&self) -> StepByStepExplanation {
270 // Unused message registry imports removed
271
272 let mut steps = Vec::new();
273 let initial_expr = self.clone();
274
275 steps.push(Step {
276 title: "Given Expression".to_owned(),
277 description: format!("Factor: {}", self),
278 expression: self.clone(),
279 rule_applied: "Initial".to_owned(),
280 latex: None,
281 });
282
283 if let Some((factored, method_steps)) = factor_expression(self) {
284 steps.extend(method_steps);
285 steps.push(Step {
286 title: "Final Factored Form".to_owned(),
287 description: format!("Fully factored: {}", factored),
288 expression: factored.clone(),
289 rule_applied: "Final".to_owned(),
290 latex: None,
291 });
292
293 let total_steps = steps.len().saturating_sub(2);
294
295 StepByStepExplanation {
296 initial_expression: initial_expr,
297 final_expression: factored,
298 steps,
299 total_steps,
300 rules_used: vec!["Factorization".to_owned()],
301 }
302 } else {
303 steps.push(Step {
304 title: "Already Factored".to_owned(),
305 description: "Expression cannot be factored further".to_owned(),
306 expression: self.clone(),
307 rule_applied: "None".to_owned(),
308 latex: None,
309 });
310
311 StepByStepExplanation {
312 initial_expression: initial_expr.clone(),
313 final_expression: initial_expr,
314 steps,
315 total_steps: 0,
316 rules_used: vec![],
317 }
318 }
319 }
320}
321
322/// Helper function to combine like terms in an expression
323fn simplify_step_combine_like_terms(expr: &Expression) -> Option<(Expression, Step)> {
324 use crate::core::Number;
325 use std::collections::HashMap;
326
327 match expr {
328 Expression::Add(terms) => {
329 let mut symbol_terms: HashMap<String, Vec<(i64, Expression)>> = HashMap::new();
330 let mut constant_sum = 0i64;
331 let mut non_simplifiable = Vec::new();
332
333 for term in terms.iter() {
334 match term {
335 Expression::Number(Number::Integer(n)) => {
336 constant_sum += n;
337 }
338 Expression::Symbol(s) => {
339 symbol_terms
340 .entry(s.name().to_owned())
341 .or_default()
342 .push((1, term.clone()));
343 }
344 Expression::Mul(factors) => {
345 let mut coeff = 1i64;
346 let mut var_part = Vec::new();
347
348 for factor in factors.iter() {
349 match factor {
350 Expression::Number(Number::Integer(n)) => coeff *= n,
351 _ => var_part.push(factor.clone()),
352 }
353 }
354
355 if !var_part.is_empty() {
356 let key = format!("{:?}", var_part);
357 symbol_terms
358 .entry(key)
359 .or_default()
360 .push((coeff, Expression::Mul(Box::new(var_part))));
361 } else {
362 constant_sum += coeff;
363 }
364 }
365 _ => non_simplifiable.push(term.clone()),
366 }
367 }
368
369 let mut combined_changed = false;
370 let mut new_terms = Vec::new();
371
372 for (_key, occurrences) in symbol_terms.iter() {
373 if occurrences.len() > 1 {
374 combined_changed = true;
375 let total_coeff: i64 = occurrences.iter().map(|(c, _)| c).sum();
376 if total_coeff != 0 {
377 if total_coeff == 1 {
378 new_terms.push(occurrences[0].1.clone());
379 } else {
380 new_terms.push(Expression::Mul(Box::new(vec![
381 Expression::integer(total_coeff),
382 occurrences[0].1.clone(),
383 ])));
384 }
385 }
386 } else if occurrences.len() == 1 {
387 let (coeff, var) = &occurrences[0];
388 if *coeff == 1 {
389 new_terms.push(var.clone());
390 } else {
391 new_terms.push(Expression::Mul(Box::new(vec![
392 Expression::integer(*coeff),
393 var.clone(),
394 ])));
395 }
396 }
397 }
398
399 if constant_sum != 0 {
400 new_terms.push(Expression::integer(constant_sum));
401 }
402
403 new_terms.extend(non_simplifiable);
404
405 if combined_changed && !new_terms.is_empty() {
406 let result = if new_terms.len() == 1 {
407 new_terms[0].clone()
408 } else {
409 Expression::Add(Box::new(new_terms))
410 };
411
412 let step = Step {
413 title: "Combine Like Terms".to_owned(),
414 description: format!("Identify and combine like terms\nResult: {}", result),
415 expression: result.clone(),
416 rule_applied: "Combine Like Terms".to_owned(),
417 latex: None,
418 };
419
420 return Some((result, step));
421 }
422
423 None
424 }
425 _ => None,
426 }
427}
428
429/// Helper function to apply identity rules
430fn simplify_step_identity_rules(expr: &Expression) -> Option<(Expression, Step)> {
431 use crate::core::Number;
432
433 match expr {
434 Expression::Add(terms) => {
435 let non_zero_terms: Vec<Expression> = terms
436 .iter()
437 .filter(|t| !matches!(t, Expression::Number(Number::Integer(0))))
438 .cloned()
439 .collect();
440
441 if non_zero_terms.len() != terms.len() {
442 let result = if non_zero_terms.is_empty() {
443 Expression::integer(0)
444 } else if non_zero_terms.len() == 1 {
445 non_zero_terms[0].clone()
446 } else {
447 Expression::Add(Box::new(non_zero_terms))
448 };
449
450 let step = Step {
451 title: "Apply Identity Rules".to_owned(),
452 description: format!("Remove additive identity (+ 0)\nResult: {}", result),
453 expression: result.clone(),
454 rule_applied: "Identity Rules".to_owned(),
455 latex: None,
456 };
457
458 return Some((result, step));
459 }
460 }
461 Expression::Mul(factors) => {
462 if factors
463 .iter()
464 .any(|f| matches!(f, Expression::Number(Number::Integer(0))))
465 {
466 let result = Expression::integer(0);
467 let step = Step {
468 title: "Zero Property".to_owned(),
469 description: "Any expression multiplied by 0 equals 0\nResult: 0".to_owned(),
470 expression: result.clone(),
471 rule_applied: "Zero Property".to_owned(),
472 latex: None,
473 };
474 return Some((result, step));
475 }
476
477 let non_one_factors: Vec<Expression> = factors
478 .iter()
479 .filter(|f| !matches!(f, Expression::Number(Number::Integer(1))))
480 .cloned()
481 .collect();
482
483 if non_one_factors.len() != factors.len() {
484 let result = if non_one_factors.is_empty() {
485 Expression::integer(1)
486 } else if non_one_factors.len() == 1 {
487 non_one_factors[0].clone()
488 } else {
489 Expression::Mul(Box::new(non_one_factors))
490 };
491
492 let step = Step {
493 title: "Apply Identity Rules".to_owned(),
494 description: format!(
495 "Remove multiplicative identity (* 1)\nResult: {}",
496 result
497 ),
498 expression: result.clone(),
499 rule_applied: "Identity Rules".to_owned(),
500 latex: None,
501 };
502
503 return Some((result, step));
504 }
505 }
506 _ => {}
507 }
508
509 None
510}
511
512/// Helper function to apply power rules
513fn simplify_step_power_rules(expr: &Expression) -> Option<(Expression, Step)> {
514 use crate::core::Number;
515
516 match expr {
517 Expression::Pow(base, exp) => match (exp.as_ref(), base.as_ref()) {
518 (Expression::Number(Number::Integer(0)), _) => {
519 let result = Expression::integer(1);
520 let step = Step {
521 title: "Power Rule: x^0 = 1".to_owned(),
522 description: "Any expression to the power of 0 equals 1\nResult: 1".to_owned(),
523 expression: result.clone(),
524 rule_applied: "Power Rules".to_owned(),
525 latex: None,
526 };
527 return Some((result, step));
528 }
529 (Expression::Number(Number::Integer(1)), _) => {
530 let result = base.as_ref().clone();
531 let step = Step {
532 title: "Power Rule: x^1 = x".to_owned(),
533 description: format!(
534 "Any expression to the power of 1 equals itself\nResult: {}",
535 result
536 ),
537 expression: result.clone(),
538 rule_applied: "Power Rules".to_owned(),
539 latex: None,
540 };
541 return Some((result, step));
542 }
543 (_, Expression::Number(Number::Integer(1))) => {
544 let result = Expression::integer(1);
545 let step = Step {
546 title: "Power Rule: 1^n = 1".to_owned(),
547 description: "1 to any power equals 1\nResult: 1".to_owned(),
548 expression: result.clone(),
549 rule_applied: "Power Rules".to_owned(),
550 latex: None,
551 };
552 return Some((result, step));
553 }
554 _ => {}
555 },
556 Expression::Mul(factors) => {
557 for (i, factor) in factors.iter().enumerate() {
558 if let Expression::Pow(_base, _exp) = factor {
559 if let Some((simplified, _)) = simplify_step_power_rules(factor) {
560 let mut new_factors = factors.to_vec();
561 new_factors[i] = simplified;
562 let result = Expression::Mul(Box::new(new_factors));
563 let step = Step {
564 title: "Simplify Power in Product".to_owned(),
565 description: format!(
566 "Apply power rule in multiplication\nResult: {}",
567 result
568 ),
569 expression: result.clone(),
570 rule_applied: "Power Rules".to_owned(),
571 latex: None,
572 };
573 return Some((result, step));
574 }
575 }
576 }
577 }
578 _ => {}
579 }
580
581 None
582}
583
584/// Helper function to multiply coefficients
585fn simplify_step_coefficient_multiplication(expr: &Expression) -> Option<(Expression, Step)> {
586 use crate::core::Number;
587
588 if let Expression::Mul(factors) = expr {
589 let mut numeric_product = 1i64;
590 let mut non_numeric = Vec::new();
591 let mut has_numeric = false;
592
593 for factor in factors.iter() {
594 match factor {
595 Expression::Number(Number::Integer(n)) => {
596 numeric_product *= n;
597 has_numeric = true;
598 }
599 _ => non_numeric.push(factor.clone()),
600 }
601 }
602
603 if has_numeric && factors.len() > 1 {
604 let result = if non_numeric.is_empty() {
605 Expression::integer(numeric_product)
606 } else if numeric_product == 1 {
607 if non_numeric.len() == 1 {
608 non_numeric[0].clone()
609 } else {
610 Expression::Mul(Box::new(non_numeric))
611 }
612 } else {
613 let mut new_factors = vec![Expression::integer(numeric_product)];
614 new_factors.extend(non_numeric);
615 if new_factors.len() == 1 {
616 new_factors[0].clone()
617 } else {
618 Expression::Mul(Box::new(new_factors))
619 }
620 };
621
622 let step = Step {
623 title: "Multiply Coefficients".to_owned(),
624 description: format!("Multiply numeric coefficients together\nResult: {}", result),
625 expression: result.clone(),
626 rule_applied: "Coefficient Multiplication".to_owned(),
627 latex: None,
628 };
629
630 return Some((result, step));
631 }
632 }
633
634 None
635}
636
637/// Helper function to expand an expression
638fn expand_expression(expr: &Expression) -> Option<(Expression, Vec<Step>)> {
639 let mut steps = Vec::new();
640
641 match expr {
642 Expression::Mul(factors) => {
643 if factors.len() == 2 {
644 if let (Expression::Add(terms1), Expression::Add(terms2)) =
645 (&factors[0], &factors[1])
646 {
647 steps.push(Step {
648 title: "Identify Pattern".to_owned(),
649 description: format!(
650 "Two binomials - use FOIL method\n({}) * ({})",
651 factors[0], factors[1]
652 ),
653 expression: expr.clone(),
654 rule_applied: "FOIL Method".to_owned(),
655 latex: None,
656 });
657
658 let mut all_terms = Vec::new();
659 for t1 in terms1.iter() {
660 for t2 in terms2.iter() {
661 all_terms.push(Expression::Mul(Box::new(vec![t1.clone(), t2.clone()])));
662 }
663 }
664
665 let intermediate = Expression::Add(Box::new(all_terms));
666 steps.push(Step {
667 title: "Apply FOIL".to_owned(),
668 description: format!("Multiply each term: {}", intermediate),
669 expression: intermediate.clone(),
670 rule_applied: "Distribution".to_owned(),
671 latex: None,
672 });
673
674 return Some((intermediate, steps));
675 } else if let Expression::Add(terms) = &factors[0] {
676 steps.push(Step {
677 title: "Distribute Factor".to_owned(),
678 description: format!("Distribute {} over {}", factors[1], factors[0]),
679 expression: expr.clone(),
680 rule_applied: "Distributive Property".to_owned(),
681 latex: None,
682 });
683
684 let mut distributed_terms = Vec::new();
685 for term in terms.iter() {
686 distributed_terms.push(Expression::Mul(Box::new(vec![
687 term.clone(),
688 factors[1].clone(),
689 ])));
690 }
691
692 let result = Expression::Add(Box::new(distributed_terms));
693 steps.push(Step {
694 title: "Result of Distribution".to_owned(),
695 description: format!("Expanded form: {}", result),
696 expression: result.clone(),
697 rule_applied: "Distribution Complete".to_owned(),
698 latex: None,
699 });
700
701 return Some((result, steps));
702 } else if let Expression::Add(terms) = &factors[1] {
703 steps.push(Step {
704 title: "Distribute Factor".to_owned(),
705 description: format!("Distribute {} over {}", factors[0], factors[1]),
706 expression: expr.clone(),
707 rule_applied: "Distributive Property".to_owned(),
708 latex: None,
709 });
710
711 let mut distributed_terms = Vec::new();
712 for term in terms.iter() {
713 distributed_terms.push(Expression::Mul(Box::new(vec![
714 factors[0].clone(),
715 term.clone(),
716 ])));
717 }
718
719 let result = Expression::Add(Box::new(distributed_terms));
720 steps.push(Step {
721 title: "Result of Distribution".to_owned(),
722 description: format!("Expanded form: {}", result),
723 expression: result.clone(),
724 rule_applied: "Distribution Complete".to_owned(),
725 latex: None,
726 });
727
728 return Some((result, steps));
729 }
730 }
731 }
732 Expression::Pow(base, exp) => {
733 use crate::core::Number;
734 if let (Expression::Add(_), Expression::Number(Number::Integer(n))) =
735 (base.as_ref(), exp.as_ref())
736 {
737 if *n == 2 {
738 steps.push(Step {
739 title: "Binomial Square".to_owned(),
740 description: format!(
741 "Expand ({})^2 using (a + b)^2 = a^2 + 2ab + b^2",
742 base
743 ),
744 expression: expr.clone(),
745 rule_applied: "Binomial Theorem".to_owned(),
746 latex: None,
747 });
748
749 let expanded = Expression::Mul(Box::new(vec![
750 base.as_ref().clone(),
751 base.as_ref().clone(),
752 ]));
753 if let Some((result, expand_steps)) = expand_expression(&expanded) {
754 steps.extend(expand_steps);
755 return Some((result, steps));
756 }
757 }
758 }
759 }
760 _ => {}
761 }
762
763 None
764}
765
766/// Helper function to factor an expression
767fn factor_expression(expr: &Expression) -> Option<(Expression, Vec<Step>)> {
768 use crate::core::Number;
769 let mut steps = Vec::new();
770
771 if let Expression::Add(terms) = expr {
772 let mut gcd_coeff = None;
773 let mut all_have_gcd = true;
774
775 for term in terms.iter() {
776 match term {
777 Expression::Number(Number::Integer(n)) => {
778 if let Some(g) = gcd_coeff {
779 gcd_coeff = Some(gcd_i64(g, *n));
780 } else {
781 gcd_coeff = Some(*n);
782 }
783 }
784 Expression::Mul(factors) => {
785 if let Some(Expression::Number(Number::Integer(n))) = factors.first() {
786 if let Some(g) = gcd_coeff {
787 gcd_coeff = Some(gcd_i64(g, *n));
788 } else {
789 gcd_coeff = Some(*n);
790 }
791 } else {
792 all_have_gcd = false;
793 }
794 }
795 _ => {
796 all_have_gcd = false;
797 }
798 }
799 }
800
801 if let Some(gcd) = gcd_coeff {
802 if gcd > 1 && all_have_gcd {
803 steps.push(Step {
804 title: "Find Greatest Common Factor".to_owned(),
805 description: format!("GCF of all terms = {}", gcd),
806 expression: expr.clone(),
807 rule_applied: "GCF Extraction".to_owned(),
808 latex: None,
809 });
810
811 let mut factored_terms = Vec::new();
812 for term in terms.iter() {
813 match term {
814 Expression::Number(Number::Integer(n)) => {
815 let quotient = n / gcd;
816 if quotient != 0 {
817 factored_terms.push(Expression::integer(quotient));
818 }
819 }
820 Expression::Mul(factors) => {
821 if let Some(Expression::Number(Number::Integer(n))) = factors.first() {
822 let quotient = n / gcd;
823 let mut new_factors = vec![Expression::integer(quotient)];
824 new_factors.extend(factors[1..].to_vec());
825 factored_terms.push(Expression::Mul(Box::new(new_factors)));
826 }
827 }
828 _ => {}
829 }
830 }
831
832 let inner = if factored_terms.len() == 1 {
833 factored_terms[0].clone()
834 } else {
835 Expression::Add(Box::new(factored_terms))
836 };
837
838 let result =
839 Expression::Mul(Box::new(vec![Expression::integer(gcd), inner.clone()]));
840
841 steps.push(Step {
842 title: "Factor Out GCF".to_owned(),
843 description: format!("{} * ({})", gcd, inner),
844 expression: result.clone(),
845 rule_applied: "GCF Factored".to_owned(),
846 latex: None,
847 });
848
849 return Some((result, steps));
850 }
851 }
852 }
853
854 None
855}
856
857/// Helper function to compute GCD of two integers
858fn gcd_i64(a: i64, b: i64) -> i64 {
859 let (mut a, mut b) = (a.abs(), b.abs());
860 while b != 0 {
861 let temp = b;
862 b = a % b;
863 a = temp;
864 }
865 a
866}
867
868/*
869// Temporarily commented out - complex implementation with Step struct issues
870impl Expression {
871 description: "Starting expression".to_string(),
872 expression: self.clone(),
873 rule_applied: "Initial".to_string(),
874 latex: Some(self.to_latex(None).unwrap_or_else(|_| "expression".to_string())),
875 });
876
877 // For now, just show the factored form
878 let factored = self.clone(); // Would call factor() when implemented
879
880 steps.push(Step {
881 description: "Applied factorization rules".to_string(),
882 expression: factored.clone(),
883 rule_applied: "Factorization".to_string(),
884 latex: Some(factored.to_latex(None).unwrap_or_else(|_| "expression".to_string())),
885 });
886
887 rules_used.push("Factorization".to_string());
888
889 StepByStepExplanation {
890 initial_expression: self.clone(),
891 final_expression: factored,
892 steps,
893 total_steps: 1,
894 rules_used,
895 }
896 }
897}
898
899impl Expression {
900 /// Apply simplification rules step by step
901 fn apply_simplification_steps(
902 &self,
903 steps: &mut Vec<Step>,
904 rules_used: &mut Vec<String>,
905 step_count: &mut usize,
906 ) -> Expression {
907 let mut current = self.clone();
908
909 // Step 1: Combine numeric terms
910 if let Some(numeric_simplified) = self.try_numeric_simplification(¤t) {
911 if numeric_simplified != current {
912 *step_count += 1;
913 steps.push(Step {
914 title: "Combine Terms".to_string(),
915 description: "Combine numeric terms".to_string(),
916 expression: numeric_simplified.clone(),
917 rule_applied: "Numeric Combination".to_string(),
918 latex: Some(numeric_simplified.to_latex(None).unwrap_or_else(|_| "expression".to_string())),
919 });
920 rules_used.push("Numeric Combination".to_string());
921 current = numeric_simplified;
922 }
923 }
924
925 // Step 2: Apply identity rules (x + 0 = x, x * 1 = x, etc.)
926 if let Some(identity_simplified) = self.try_identity_simplification(¤t) {
927 if identity_simplified != current {
928 *step_count += 1;
929 steps.push(Step {
930 title: "Combine Terms".to_string(),
931 description: "Apply identity rules".to_string(),
932 expression: identity_simplified.clone(),
933 rule_applied: "Identity Rules".to_string(),
934 latex: Some(identity_simplified.to_latex(None).unwrap_or_else(|_| "expression".to_string())),
935 });
936 rules_used.push("Identity Rules".to_string());
937 current = identity_simplified;
938 }
939 }
940
941 // Step 3: Apply zero rules (x * 0 = 0, 0^n = 0, etc.)
942 if let Some(zero_simplified) = self.try_zero_simplification(¤t) {
943 if zero_simplified != current {
944 *step_count += 1;
945 steps.push(Step {
946 title: "Combine Terms".to_string(),
947 description: "Apply zero rules".to_string(),
948 expression: zero_simplified.clone(),
949 rule_applied: "Zero Rules".to_string(),
950 latex: Some(zero_simplified.to_latex(None).unwrap_or_else(|_| "expression".to_string())),
951 });
952 rules_used.push("Zero Rules".to_string());
953 current = zero_simplified;
954 }
955 }
956
957 // Step 4: Apply power rules (x^0 = 1, x^1 = x, etc.)
958 if let Some(power_simplified) = self.try_power_simplification(¤t) {
959 if power_simplified != current {
960 *step_count += 1;
961 steps.push(Step {
962 title: "Combine Terms".to_string(),
963 description: "Apply power rules".to_string(),
964 expression: power_simplified.clone(),
965 rule_applied: "Power Rules".to_string(),
966 latex: Some(power_simplified.to_latex(None).unwrap_or_else(|_| "expression".to_string())),
967 });
968 rules_used.push("Power Rules".to_string());
969 current = power_simplified;
970 }
971 }
972
973 // Final step: Standard simplification
974 let final_simplified = current.simplify();
975 if final_simplified != current {
976 *step_count += 1;
977 steps.push(Step {
978 description: "Final simplification".to_string(),
979 expression: final_simplified.clone(),
980 rule_applied: "Standard Simplification".to_string(),
981 latex: Some(final_simplified.to_latex(None).unwrap_or_else(|_| "expression".to_string())),
982 });
983 rules_used.push("Standard Simplification".to_string());
984 current = final_simplified;
985 }
986
987 current
988 }
989
990 /// Try numeric simplification
991 fn try_numeric_simplification(&self, expr: &Expression) -> Option<Expression> {
992 match expr {
993 Expression::Add(terms) => {
994 let mut numeric_sum = num_bigint::BigInt::from(0);
995 let mut non_numeric = Vec::new();
996 let mut has_numeric = false;
997
998 for term in terms.iter() {
999 if let Expression::Number(Number::SmallInt(n)) = term {
1000 numeric_sum += BigInt::from(*n);
1001 has_numeric = true;
1002 } else {
1003 non_numeric.push(term.clone());
1004 }
1005 }
1006
1007 if has_numeric {
1008 if !numeric_sum.is_zero() {
1009 non_numeric.insert(0, Expression::integer(numeric_sum));
1010 }
1011 Some(Expression::add(non_numeric))
1012 } else {
1013 None
1014 }
1015 },
1016 Expression::Mul(factors) => {
1017 let mut numeric_product = num_bigint::BigInt::from(1);
1018 let mut non_numeric = Vec::new();
1019 let mut has_numeric = false;
1020
1021 for factor in factors.iter() {
1022 if let Expression::Number(Number::SmallInt(n)) = factor {
1023 numeric_product *= BigInt::from(*n);
1024 has_numeric = true;
1025 } else {
1026 non_numeric.push(factor.clone());
1027 }
1028 }
1029
1030 if has_numeric {
1031 if !numeric_product.is_one() {
1032 non_numeric.insert(0, Expression::integer(numeric_product));
1033 }
1034 Some(Expression::mul(non_numeric))
1035 } else {
1036 None
1037 }
1038 },
1039 _ => None,
1040 }
1041 }
1042
1043 /// Try identity simplification
1044 fn try_identity_simplification(&self, expr: &Expression) -> Option<Expression> {
1045 match expr {
1046 Expression::Add(terms) => {
1047 let non_zero_terms: Vec<Expression> = terms.iter()
1048 .filter(|t| !t.is_zero())
1049 .cloned()
1050 .collect();
1051
1052 if non_zero_terms.len() != terms.len() {
1053 Some(Expression::add(non_zero_terms))
1054 } else {
1055 None
1056 }
1057 },
1058 Expression::Mul(factors) => {
1059 let non_one_factors: Vec<Expression> = factors.iter()
1060 .filter(|f| !f.is_one())
1061 .cloned()
1062 .collect();
1063
1064 if non_one_factors.len() != factors.len() {
1065 Some(Expression::mul(non_one_factors))
1066 } else {
1067 None
1068 }
1069 },
1070 _ => None,
1071 }
1072 }
1073
1074 /// Try zero simplification
1075 fn try_zero_simplification(&self, expr: &Expression) -> Option<Expression> {
1076 match expr {
1077 Expression::Mul(factors) => {
1078 if factors.iter().any(|f| f.is_zero()) {
1079 Some(Expression::integer(0))
1080 } else {
1081 None
1082 }
1083 },
1084 _ => None,
1085 }
1086 }
1087
1088 /// Try power simplification
1089 fn try_power_simplification(&self, expr: &Expression) -> Option<Expression> {
1090 match expr {
1091 Expression::Pow(base, exp) => {
1092 if exp.is_zero() {
1093 Some(Expression::integer(1))
1094 } else if exp.is_one() {
1095 Some(base.as_ref().clone())
1096 } else if base.is_zero() {
1097 Some(Expression::integer(0))
1098 } else if base.is_one() {
1099 Some(Expression::integer(1))
1100 } else {
1101 None
1102 }
1103 },
1104 _ => None,
1105 }
1106 }
1107
1108 /// Convert expression to LaTeX format
1109 pub fn to_latex(&self) -> String {
1110 match self {
1111 Expression::Number(Number::SmallInt(n)) => n.to_string(),
1112 Expression::Number(Number::Rational(r)) => {
1113 if r.denom().is_one() {
1114 r.numer().to_string()
1115 } else {
1116 format!("\\frac{{{}}}{{{}}}", r.numer(), r.denom())
1117 }
1118 },
1119 Expression::Number(Number::Float(f)) => f.to_string(),
1120 Expression::Number(Number::BigInteger(n)) => n.to_string(),
1121 Expression::Symbol(s) => s.name().to_string(),
1122 Expression::Add(terms) => {
1123 if terms.is_empty() {
1124 "0".to_string()
1125 } else {
1126 let term_strs: Vec<String> = terms.iter()
1127 .map(|t| t.to_latex(None).unwrap_or_else(|_| "term".to_string()))
1128 .collect();
1129 term_strs.join(" + ")
1130 }
1131 },
1132 Expression::Mul(factors) => {
1133 if factors.is_empty() {
1134 "1".to_string()
1135 } else {
1136 let factor_strs: Vec<String> = factors.iter()
1137 .map(|f| {
1138 match f {
1139 Expression::Add(_) => format!("({})", f.to_latex(None).unwrap_or_else(|_| "factor".to_string())),
1140 _ => f.to_latex(None).unwrap_or_else(|_| "factor".to_string()),
1141 }
1142 })
1143 .collect();
1144 factor_strs.join(" \\cdot ")
1145 }
1146 },
1147 Expression::Pow(base, exp) => {
1148 let base_latex = match base.as_ref() {
1149 Expression::Add(_) | Expression::Mul(_) => format!("({})", base.to_latex(None).unwrap_or_else(|_| "base".to_string())),
1150 _ => base.to_latex(None).unwrap_or_else(|_| "base".to_string()),
1151 };
1152 format!("{}^{{{}}}", base_latex, exp.to_latex(None).unwrap_or_else(|_| "exp".to_string()))
1153 },
1154 Expression::Function { name, args } => {
1155 if args.is_empty() {
1156 format!("\\{}", name)
1157 } else {
1158 let arg_strs: Vec<String> = args.iter()
1159 .map(|a| a.to_latex(None).unwrap_or_else(|_| "arg".to_string()))
1160 .collect();
1161 format!("\\{}({})", name, arg_strs.join(", "))
1162 }
1163 }
1164 }
1165 }
1166
1167 /// Parse LaTeX input to Expression (simplified for now)
1168 pub fn from_latex(latex: &str) -> Result<Expression, String> {
1169 // Simplified LaTeX parsing - full implementation would be more complex
1170
1171 // Handle basic cases
1172 if latex.trim().chars().all(|c| c.is_ascii_digit() || c == '-') {
1173 if let Ok(n) = latex.trim().parse::<i64>() {
1174 return Ok(Expression::integer(n));
1175 }
1176 }
1177
1178 // Handle single variables
1179 if latex.trim().chars().all(|c| c.is_ascii_alphabetic()) {
1180 return Ok(Expression::symbol(Symbol::new(latex.trim())));
1181 }
1182
1183 Err(format!("Cannot parse LaTeX: {} (full parser not implemented yet)", latex))
1184 }
1185
1186 /// Parse LaTeX fraction: \frac{numerator}{denominator}
1187 #[allow(dead_code)]
1188 fn parse_latex_fraction(&self, latex: &str) -> Option<Expression> {
1189 // Simplified fraction parsing
1190 if let Some(start) = latex.find("\\frac{") {
1191 let content = &latex[start + 6..];
1192 if let Some(close_num) = content.find("}{") {
1193 let numerator = &content[..close_num];
1194 let rest = &content[close_num + 2..];
1195 if let Some(close_den) = rest.find('}') {
1196 let denominator = &rest[..close_den];
1197
1198 if let (Ok(num), Ok(den)) = (numerator.parse::<i64>(), denominator.parse::<i64>()) {
1199 let rational = num_rational::BigRational::new(
1200 num_bigint::BigInt::from(num),
1201 num_bigint::BigInt::from(den)
1202 );
1203 return Some(Expression::number(Number::rational(rational)));
1204 }
1205 }
1206 }
1207 }
1208 None
1209 }
1210
1211 /// Parse LaTeX power: base^{exponent}
1212 #[allow(dead_code)]
1213 fn parse_latex_power(&self, latex: &str) -> Option<Expression> {
1214 if let Some(caret_pos) = latex.find("^{") {
1215 let base_str = &latex[..caret_pos];
1216 let exp_start = caret_pos + 2;
1217 if let Some(close_brace) = latex[exp_start..].find('}') {
1218 let exp_str = &latex[exp_start..exp_start + close_brace];
1219
1220 // Parse base and exponent
1221 if let (Ok(base_expr), Ok(exp_expr)) = (
1222 Expression::from_latex(base_str),
1223 Expression::from_latex(exp_str)
1224 ) {
1225 return Some(Expression::pow(base_expr, exp_expr));
1226 }
1227 }
1228 }
1229 None
1230 }
1231
1232 /// Parse LaTeX function: \function_name(args)
1233 #[allow(dead_code)]
1234 fn parse_latex_function(&self, latex: &str) -> Option<Expression> {
1235 if let Some(backslash_pos) = latex.find('\\') {
1236 let after_backslash = &latex[backslash_pos + 1..];
1237 if let Some(paren_pos) = after_backslash.find('(') {
1238 let func_name = &after_backslash[..paren_pos];
1239 let args_start = backslash_pos + 1 + paren_pos + 1;
1240 if let Some(close_paren) = latex[args_start..].rfind(')') {
1241 let args_str = &latex[args_start..args_start + close_paren];
1242
1243 // Parse arguments (simplified - would need full parser)
1244 if let Ok(arg_expr) = Expression::from_latex(args_str) {
1245 return Some(Expression::function(func_name, vec![arg_expr]));
1246 }
1247 }
1248 }
1249 }
1250 None
1251 }
1252
1253 /// Generate educational explanation text
1254 pub fn explain_rule(&self, rule: &str) -> String {
1255 match rule {
1256 "Numeric Combination" => {
1257 "Combine numeric terms by performing arithmetic operations".to_string()
1258 },
1259 "Identity Rules" => {
1260 "Apply identity rules: x + 0 = x, x * 1 = x, x - x = 0".to_string()
1261 },
1262 "Zero Rules" => {
1263 "Apply zero rules: x * 0 = 0, 0 + x = x, 0^n = 0".to_string()
1264 },
1265 "Power Rules" => {
1266 "Apply power rules: x^0 = 1, x^1 = x, 1^n = 1".to_string()
1267 },
1268 "Expansion" => {
1269 "Expand expressions by distributing multiplication over addition".to_string()
1270 },
1271 "Factorization" => {
1272 "Factor expressions by extracting common factors".to_string()
1273 },
1274 _ => format!("Applied rule: {}", rule),
1275 }
1276 }
1277}
1278
1279/// Step-by-step explanation builder
1280pub struct StepByStepBuilder {
1281 steps: Vec<Step>,
1282 rules_used: Vec<String>,
1283}
1284
1285impl StepByStepBuilder {
1286 /// Create a new builder
1287 pub fn new() -> Self {
1288 Self {
1289 steps: Vec::new(),
1290 rules_used: Vec::new(),
1291 }
1292 }
1293
1294 /// Add a step to the explanation
1295 pub fn add_step(&mut self, description: String, expression: Expression, rule: String) {
1296 let latex = expression.to_latex(None).unwrap_or_else(|_| "expression".to_string());
1297 self.steps.push(Step {
1298 description,
1299 expression,
1300 rule_applied: rule.clone(),
1301 latex: Some(latex),
1302 });
1303 self.rules_used.push(rule);
1304 }
1305
1306 /// Build the final explanation
1307 pub fn build(self, initial: Expression, final_expr: Expression) -> StepByStepExplanation {
1308 StepByStepExplanation {
1309 initial_expression: initial,
1310 final_expression: final_expr,
1311 total_steps: self.steps.len(),
1312 steps: self.steps,
1313 rules_used: self.rules_used,
1314 }
1315 }
1316}
1317
1318impl Default for StepByStepBuilder {
1319 fn default() -> Self {
1320 Self::new()
1321 }
1322}
1323
1324/// Helper function to combine like terms in an expression
1325fn simplify_step_combine_like_terms(expr: &Expression) -> Option<(Expression, Step)> {
1326 use crate::core::Number;
1327 use std::collections::HashMap;
1328
1329 match expr {
1330 Expression::Add(terms) => {
1331 let mut symbol_terms: HashMap<String, Vec<(i64, Expression)>> = HashMap::new();
1332 let mut constant_sum = 0i64;
1333 let mut non_simplifiable = Vec::new();
1334
1335 for term in terms.iter() {
1336 match term {
1337 Expression::Number(Number::Integer(n)) => {
1338 constant_sum += n;
1339 }
1340 Expression::Symbol(s) => {
1341 symbol_terms
1342 .entry(s.name().to_string())
1343 .or_insert_with(Vec::new)
1344 .push((1, term.clone()));
1345 }
1346 Expression::Mul(factors) => {
1347 let mut coeff = 1i64;
1348 let mut var_part = Vec::new();
1349
1350 for factor in factors.iter() {
1351 match factor {
1352 Expression::Number(Number::Integer(n)) => coeff *= n,
1353 _ => var_part.push(factor.clone()),
1354 }
1355 }
1356
1357 if !var_part.is_empty() {
1358 let key = format!("{:?}", var_part);
1359 symbol_terms
1360 .entry(key)
1361 .or_insert_with(Vec::new)
1362 .push((coeff, Expression::Mul(Box::new(var_part))));
1363 } else {
1364 constant_sum += coeff;
1365 }
1366 }
1367 _ => non_simplifiable.push(term.clone()),
1368 }
1369 }
1370
1371 let mut combined_changed = false;
1372 let mut new_terms = Vec::new();
1373
1374 for (_key, occurrences) in symbol_terms.iter() {
1375 if occurrences.len() > 1 {
1376 combined_changed = true;
1377 let total_coeff: i64 = occurrences.iter().map(|(c, _)| c).sum();
1378 if total_coeff != 0 {
1379 if total_coeff == 1 {
1380 new_terms.push(occurrences[0].1.clone());
1381 } else {
1382 new_terms.push(Expression::Mul(Box::new(vec![
1383 Expression::integer(total_coeff),
1384 occurrences[0].1.clone(),
1385 ])));
1386 }
1387 }
1388 } else if occurrences.len() == 1 {
1389 let (coeff, var) = &occurrences[0];
1390 if *coeff == 1 {
1391 new_terms.push(var.clone());
1392 } else {
1393 new_terms.push(Expression::Mul(Box::new(vec![
1394 Expression::integer(*coeff),
1395 var.clone(),
1396 ])));
1397 }
1398 }
1399 }
1400
1401 if constant_sum != 0 {
1402 new_terms.push(Expression::integer(constant_sum));
1403 }
1404
1405 new_terms.extend(non_simplifiable);
1406
1407 if combined_changed && !new_terms.is_empty() {
1408 let result = if new_terms.len() == 1 {
1409 new_terms[0].clone()
1410 } else {
1411 Expression::Add(Box::new(new_terms))
1412 };
1413
1414 let step = Step {
1415 title: "Combine Like Terms".to_string(),
1416 description: format!(
1417 "Identify and combine like terms\nResult: {}",
1418 result
1419 ),
1420 expression: result.clone(),
1421 rule_applied: "Combine Like Terms".to_string(),
1422 latex: None,
1423 };
1424
1425 return Some((result, step));
1426 }
1427
1428 None
1429 }
1430 _ => None,
1431 }
1432}
1433
1434/// Helper function to apply identity rules
1435fn simplify_step_identity_rules(expr: &Expression) -> Option<(Expression, Step)> {
1436 use crate::core::Number;
1437
1438 match expr {
1439 Expression::Add(terms) => {
1440 let non_zero_terms: Vec<Expression> = terms
1441 .iter()
1442 .filter(|t| !matches!(t, Expression::Number(Number::Integer(0))))
1443 .cloned()
1444 .collect();
1445
1446 if non_zero_terms.len() != terms.len() {
1447 let result = if non_zero_terms.is_empty() {
1448 Expression::integer(0)
1449 } else if non_zero_terms.len() == 1 {
1450 non_zero_terms[0].clone()
1451 } else {
1452 Expression::Add(Box::new(non_zero_terms))
1453 };
1454
1455 let step = Step {
1456 title: "Apply Identity Rules".to_string(),
1457 description: format!("Remove additive identity (+ 0)\nResult: {}", result),
1458 expression: result.clone(),
1459 rule_applied: "Identity Rules".to_string(),
1460 latex: None,
1461 };
1462
1463 return Some((result, step));
1464 }
1465 }
1466 Expression::Mul(factors) => {
1467 if factors.iter().any(|f| matches!(f, Expression::Number(Number::Integer(0)))) {
1468 let result = Expression::integer(0);
1469 let step = Step {
1470 title: "Zero Property".to_string(),
1471 description: "Any expression multiplied by 0 equals 0\nResult: 0".to_string(),
1472 expression: result.clone(),
1473 rule_applied: "Zero Property".to_string(),
1474 latex: None,
1475 };
1476 return Some((result, step));
1477 }
1478
1479 let non_one_factors: Vec<Expression> = factors
1480 .iter()
1481 .filter(|f| !matches!(f, Expression::Number(Number::Integer(1))))
1482 .cloned()
1483 .collect();
1484
1485 if non_one_factors.len() != factors.len() {
1486 let result = if non_one_factors.is_empty() {
1487 Expression::integer(1)
1488 } else if non_one_factors.len() == 1 {
1489 non_one_factors[0].clone()
1490 } else {
1491 Expression::Mul(Box::new(non_one_factors))
1492 };
1493
1494 let step = Step {
1495 title: "Apply Identity Rules".to_string(),
1496 description: format!(
1497 "Remove multiplicative identity (* 1)\nResult: {}",
1498 result
1499 ),
1500 expression: result.clone(),
1501 rule_applied: "Identity Rules".to_string(),
1502 latex: None,
1503 };
1504
1505 return Some((result, step));
1506 }
1507 }
1508 _ => {}
1509 }
1510
1511 None
1512}
1513
1514/// Helper function to apply power rules
1515fn simplify_step_power_rules(expr: &Expression) -> Option<(Expression, Step)> {
1516 use crate::core::Number;
1517
1518 match expr {
1519 Expression::Pow(base, exp) => {
1520 match (exp.as_ref(), base.as_ref()) {
1521 (Expression::Number(Number::Integer(0)), _) => {
1522 let result = Expression::integer(1);
1523 let step = Step {
1524 title: "Power Rule: x^0 = 1".to_string(),
1525 description: "Any expression to the power of 0 equals 1\nResult: 1"
1526 .to_string(),
1527 expression: result.clone(),
1528 rule_applied: "Power Rules".to_string(),
1529 latex: None,
1530 };
1531 return Some((result, step));
1532 }
1533 (Expression::Number(Number::Integer(1)), _) => {
1534 let result = base.as_ref().clone();
1535 let step = Step {
1536 title: "Power Rule: x^1 = x".to_string(),
1537 description: format!(
1538 "Any expression to the power of 1 equals itself\nResult: {}",
1539 result
1540 ),
1541 expression: result.clone(),
1542 rule_applied: "Power Rules".to_string(),
1543 latex: None,
1544 };
1545 return Some((result, step));
1546 }
1547 (_, Expression::Number(Number::Integer(1))) => {
1548 let result = Expression::integer(1);
1549 let step = Step {
1550 title: "Power Rule: 1^n = 1".to_string(),
1551 description: "1 to any power equals 1\nResult: 1".to_string(),
1552 expression: result.clone(),
1553 rule_applied: "Power Rules".to_string(),
1554 latex: None,
1555 };
1556 return Some((result, step));
1557 }
1558 _ => {}
1559 }
1560 }
1561 Expression::Mul(factors) => {
1562 for (i, factor) in factors.iter().enumerate() {
1563 if let Expression::Pow(_base, _exp) = factor {
1564 if let Some((simplified, _)) = simplify_step_power_rules(factor) {
1565 let mut new_factors = factors.to_vec();
1566 new_factors[i] = simplified.clone();
1567 let result = Expression::Mul(Box::new(new_factors));
1568 let step = Step {
1569 title: "Simplify Power in Product".to_string(),
1570 description: format!("Apply power rule in multiplication\nResult: {}", result),
1571 expression: result.clone(),
1572 rule_applied: "Power Rules".to_string(),
1573 latex: None,
1574 };
1575 return Some((result, step));
1576 }
1577 }
1578 }
1579 }
1580 _ => {}
1581 }
1582
1583 None
1584}
1585
1586/// Helper function to multiply coefficients
1587fn simplify_step_coefficient_multiplication(expr: &Expression) -> Option<(Expression, Step)> {
1588 use crate::core::Number;
1589
1590 match expr {
1591 Expression::Mul(factors) => {
1592 let mut numeric_product = 1i64;
1593 let mut non_numeric = Vec::new();
1594 let mut has_numeric = false;
1595
1596 for factor in factors.iter() {
1597 match factor {
1598 Expression::Number(Number::Integer(n)) => {
1599 numeric_product *= n;
1600 has_numeric = true;
1601 }
1602 _ => non_numeric.push(factor.clone()),
1603 }
1604 }
1605
1606 if has_numeric && factors.len() > 1 {
1607 let result = if non_numeric.is_empty() {
1608 Expression::integer(numeric_product)
1609 } else if numeric_product == 1 {
1610 if non_numeric.len() == 1 {
1611 non_numeric[0].clone()
1612 } else {
1613 Expression::Mul(Box::new(non_numeric))
1614 }
1615 } else {
1616 let mut new_factors = vec![Expression::integer(numeric_product)];
1617 new_factors.extend(non_numeric);
1618 if new_factors.len() == 1 {
1619 new_factors[0].clone()
1620 } else {
1621 Expression::Mul(Box::new(new_factors))
1622 }
1623 };
1624
1625 let step = Step {
1626 title: "Multiply Coefficients".to_string(),
1627 description: format!(
1628 "Multiply numeric coefficients together\nResult: {}",
1629 result
1630 ),
1631 expression: result.clone(),
1632 rule_applied: "Coefficient Multiplication".to_string(),
1633 latex: None,
1634 };
1635
1636 return Some((result, step));
1637 }
1638 }
1639 _ => {}
1640 }
1641
1642 None
1643}
1644
1645/// Helper function to expand an expression
1646fn expand_expression(expr: &Expression) -> Option<(Expression, Vec<Step>)> {
1647 let mut steps = Vec::new();
1648
1649 match expr {
1650 Expression::Mul(factors) => {
1651 if factors.len() == 2 {
1652 if let (Expression::Add(terms1), Expression::Add(terms2)) =
1653 (&factors[0], &factors[1])
1654 {
1655 steps.push(Step {
1656 title: "Identify Pattern".to_string(),
1657 description: format!(
1658 "Two binomials - use FOIL method\n({}) * ({})",
1659 factors[0], factors[1]
1660 ),
1661 expression: expr.clone(),
1662 rule_applied: "FOIL Method".to_string(),
1663 latex: None,
1664 });
1665
1666 let mut all_terms = Vec::new();
1667 for t1 in terms1.iter() {
1668 for t2 in terms2.iter() {
1669 all_terms.push(Expression::Mul(Box::new(vec![t1.clone(), t2.clone()])));
1670 }
1671 }
1672
1673 let intermediate = Expression::Add(Box::new(all_terms));
1674 steps.push(Step {
1675 title: "Apply FOIL".to_string(),
1676 description: format!("Multiply each term: {}", intermediate),
1677 expression: intermediate.clone(),
1678 rule_applied: "Distribution".to_string(),
1679 latex: None,
1680 });
1681
1682 return Some((intermediate, steps));
1683 } else if let Expression::Add(terms) = &factors[0] {
1684 steps.push(Step {
1685 title: "Distribute Factor".to_string(),
1686 description: format!(
1687 "Distribute {} over {}",
1688 factors[1], factors[0]
1689 ),
1690 expression: expr.clone(),
1691 rule_applied: "Distributive Property".to_string(),
1692 latex: None,
1693 });
1694
1695 let mut distributed_terms = Vec::new();
1696 for term in terms.iter() {
1697 distributed_terms.push(Expression::Mul(Box::new(vec![
1698 term.clone(),
1699 factors[1].clone(),
1700 ])));
1701 }
1702
1703 let result = Expression::Add(Box::new(distributed_terms));
1704 steps.push(Step {
1705 title: "Result of Distribution".to_string(),
1706 description: format!("Expanded form: {}", result),
1707 expression: result.clone(),
1708 rule_applied: "Distribution Complete".to_string(),
1709 latex: None,
1710 });
1711
1712 return Some((result, steps));
1713 } else if let Expression::Add(terms) = &factors[1] {
1714 steps.push(Step {
1715 title: "Distribute Factor".to_string(),
1716 description: format!(
1717 "Distribute {} over {}",
1718 factors[0], factors[1]
1719 ),
1720 expression: expr.clone(),
1721 rule_applied: "Distributive Property".to_string(),
1722 latex: None,
1723 });
1724
1725 let mut distributed_terms = Vec::new();
1726 for term in terms.iter() {
1727 distributed_terms.push(Expression::Mul(Box::new(vec![
1728 factors[0].clone(),
1729 term.clone(),
1730 ])));
1731 }
1732
1733 let result = Expression::Add(Box::new(distributed_terms));
1734 steps.push(Step {
1735 title: "Result of Distribution".to_string(),
1736 description: format!("Expanded form: {}", result),
1737 expression: result.clone(),
1738 rule_applied: "Distribution Complete".to_string(),
1739 latex: None,
1740 });
1741
1742 return Some((result, steps));
1743 }
1744 }
1745 }
1746 Expression::Pow(base, exp) => {
1747 use crate::core::Number;
1748 if let (Expression::Add(_), Expression::Number(Number::Integer(n))) =
1749 (base.as_ref(), exp.as_ref())
1750 {
1751 if *n == 2 {
1752 steps.push(Step {
1753 title: "Binomial Square".to_string(),
1754 description: format!(
1755 "Expand ({})^2 using (a + b)^2 = a^2 + 2ab + b^2",
1756 base
1757 ),
1758 expression: expr.clone(),
1759 rule_applied: "Binomial Theorem".to_string(),
1760 latex: None,
1761 });
1762
1763 let expanded = Expression::Mul(Box::new(vec![base.as_ref().clone(), base.as_ref().clone()]));
1764 if let Some((result, expand_steps)) = expand_expression(&expanded) {
1765 steps.extend(expand_steps);
1766 return Some((result, steps));
1767 }
1768 }
1769 }
1770 }
1771 _ => {}
1772 }
1773
1774 None
1775}
1776
1777/// Helper function to factor an expression
1778fn factor_expression(expr: &Expression) -> Option<(Expression, Vec<Step>)> {
1779 use crate::core::Number;
1780 let mut steps = Vec::new();
1781
1782 match expr {
1783 Expression::Add(terms) => {
1784 let mut gcd_coeff = None;
1785 let mut all_have_gcd = true;
1786
1787 for term in terms.iter() {
1788 match term {
1789 Expression::Number(Number::Integer(n)) => {
1790 if let Some(g) = gcd_coeff {
1791 gcd_coeff = Some(gcd_i64(g, *n));
1792 } else {
1793 gcd_coeff = Some(*n);
1794 }
1795 }
1796 Expression::Mul(factors) => {
1797 if let Some(Expression::Number(Number::Integer(n))) = factors.first() {
1798 if let Some(g) = gcd_coeff {
1799 gcd_coeff = Some(gcd_i64(g, *n));
1800 } else {
1801 gcd_coeff = Some(*n);
1802 }
1803 } else {
1804 all_have_gcd = false;
1805 }
1806 }
1807 _ => {
1808 all_have_gcd = false;
1809 }
1810 }
1811 }
1812
1813 if let Some(gcd) = gcd_coeff {
1814 if gcd > 1 && all_have_gcd {
1815 steps.push(Step {
1816 title: "Find Greatest Common Factor".to_string(),
1817 description: format!("GCF of all terms = {}", gcd),
1818 expression: expr.clone(),
1819 rule_applied: "GCF Extraction".to_string(),
1820 latex: None,
1821 });
1822
1823 let mut factored_terms = Vec::new();
1824 for term in terms.iter() {
1825 match term {
1826 Expression::Number(Number::Integer(n)) => {
1827 let quotient = n / gcd;
1828 if quotient != 0 {
1829 factored_terms.push(Expression::integer(quotient));
1830 }
1831 }
1832 Expression::Mul(factors) => {
1833 if let Some(Expression::Number(Number::Integer(n))) = factors.first()
1834 {
1835 let quotient = n / gcd;
1836 let mut new_factors = vec![Expression::integer(quotient)];
1837 new_factors.extend(factors[1..].to_vec());
1838 factored_terms.push(Expression::Mul(Box::new(new_factors)));
1839 }
1840 }
1841 _ => {}
1842 }
1843 }
1844
1845 let inner = if factored_terms.len() == 1 {
1846 factored_terms[0].clone()
1847 } else {
1848 Expression::Add(Box::new(factored_terms))
1849 };
1850
1851 let result = Expression::Mul(Box::new(vec![
1852 Expression::integer(gcd),
1853 inner.clone(),
1854 ]));
1855
1856 steps.push(Step {
1857 title: "Factor Out GCF".to_string(),
1858 description: format!("{} * ({})", gcd, inner),
1859 expression: result.clone(),
1860 rule_applied: "GCF Factored".to_string(),
1861 latex: None,
1862 });
1863
1864 return Some((result, steps));
1865 }
1866 }
1867 }
1868 _ => {}
1869 }
1870
1871 None
1872}
1873
1874/// Helper function to compute GCD of two integers
1875fn gcd_i64(a: i64, b: i64) -> i64 {
1876 let (mut a, mut b) = (a.abs(), b.abs());
1877 while b != 0 {
1878 let temp = b;
1879 b = a % b;
1880 a = temp;
1881 }
1882 a
1883}
1884
1885#[cfg(test)]
1886mod tests {
1887 use super::*;
1888 use crate::{symbol, Expression};
1889
1890 #[test]
1891 fn test_step_by_step_explanation() {
1892 let x = symbol!(x);
1893 let expr = Expression::add(vec![
1894 Expression::integer(2),
1895 Expression::integer(3),
1896 Expression::symbol(x.clone())
1897 ]);
1898
1899 let explanation = expr.explain_simplification();
1900
1901 assert!(!explanation.steps.is_empty());
1902 assert!(explanation.total_steps > 0);
1903 assert!(!explanation.rules_used.is_empty());
1904
1905 println!("Step-by-step explanation:");
1906 for (i, step) in explanation.steps.iter().enumerate() {
1907 println!("Step {}: {} - {}", i + 1, step.description, step.expression);
1908 }
1909 }
1910
1911 #[test]
1912 fn test_latex_generation() {
1913 let x = symbol!(x);
1914 let expr = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
1915
1916 let latex = expr.to_latex(None).unwrap();
1917 assert_eq!(latex, "x^{2}");
1918
1919 let rational = Expression::number(Number::rational(
1920 num_rational::BigRational::new(num_bigint::BigInt::from(3), num_bigint::BigInt::from(4))
1921 ));
1922 let latex = rational.to_latex(None).unwrap();
1923 assert_eq!(latex, "\\frac{3}{4}");
1924 }
1925
1926 #[test]
1927 fn test_latex_parsing() {
1928 // Test simple number
1929 let expr = Expression::from_latex("42").unwrap();
1930 assert_eq!(expr, expr!(42));
1931
1932 // Test variable
1933 let expr = Expression::from_latex("x").unwrap();
1934 assert_eq!(expr, expr!(x));
1935
1936 // Test fraction
1937 let expr = Expression::from_latex("\\frac{3}{4}").unwrap();
1938 if let Expression::Number(Number::Rational(r)) = expr {
1939 assert_eq!(r.numer(), &num_bigint::BigInt::from(3));
1940 assert_eq!(r.denom(), &num_bigint::BigInt::from(4));
1941 } else {
1942 panic!("Expected rational number");
1943 }
1944 }
1945
1946 #[test]
1947 fn test_educational_explanations() {
1948 let expr = Expression::integer(1);
1949
1950 let explanation = expr.explain_rule("Identity Rules");
1951 assert!(explanation.contains("identity"));
1952
1953 let explanation = expr.explain_rule("Zero Rules");
1954 assert!(explanation.contains("zero"));
1955
1956 let explanation = expr.explain_rule("Power Rules");
1957 assert!(explanation.contains("power"));
1958 }
1959
1960 #[test]
1961 fn test_step_builder() {
1962 let mut builder = StepByStepBuilder::new();
1963
1964 let x = symbol!(x);
1965 let initial = Expression::add(vec![
1966 Expression::symbol(x.clone()),
1967 Expression::integer(0)
1968 ]);
1969
1970 builder.add_step(
1971 "Remove zero term".to_string(),
1972 Expression::symbol(x.clone()),
1973 "Identity Rules".to_string()
1974 );
1975
1976 let explanation = builder.build(initial.clone(), Expression::symbol(x.clone()));
1977
1978 assert_eq!(explanation.initial_expression, initial);
1979 assert_eq!(explanation.final_expression, Expression::symbol(x));
1980 assert_eq!(explanation.total_steps, 1);
1981 assert_eq!(explanation.rules_used, vec!["Identity Rules"]);
1982 }
1983}
1984*/