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