mathhook_core/core/polynomial/educational/
gcd.rs1use super::super::algorithms::integer_gcd;
4use super::super::algorithms::zippel_gcd::educational;
5use super::super::classification::PolynomialClassification;
6use super::super::gcd_ops::PolynomialGcdOps;
7use super::super::properties::PolynomialProperties;
8use super::create_explanation;
9use crate::core::{Expression, Number};
10use crate::educational::step_by_step::{Step, StepByStepExplanation};
11
12pub fn explain_poly_gcd_impl(expr: &Expression, other: &Expression) -> StepByStepExplanation {
13 let mut steps = Vec::new();
14
15 steps.push(Step {
16 title: "Polynomial GCD".to_owned(),
17 description: format!("Compute GCD of {} and {}", expr, other),
18 expression: expr.clone(),
19 rule_applied: "Initial".to_owned(),
20 latex: None,
21 });
22
23 if let (Expression::Number(Number::Integer(n1)), Expression::Number(Number::Integer(n2))) =
24 (expr, other)
25 {
26 let gcd = integer_gcd(*n1, *n2);
27 steps.push(Step {
28 title: "Integer GCD".to_owned(),
29 description: format!(
30 "Both inputs are integers. Using Euclidean algorithm:\n\
31 GCD({}, {}) = {}",
32 n1, n2, gcd
33 ),
34 expression: Expression::integer(gcd),
35 rule_applied: "Euclidean Algorithm".to_owned(),
36 latex: None,
37 });
38
39 return create_explanation(expr.clone(), Expression::integer(gcd), steps);
40 }
41
42 if expr.is_zero() {
43 steps.push(Step {
44 title: "Zero Input".to_owned(),
45 description: format!("GCD(0, b) = |b|\nResult: {}", other),
46 expression: other.clone(),
47 rule_applied: "Zero Property".to_owned(),
48 latex: None,
49 });
50 return create_explanation(expr.clone(), other.clone(), steps);
51 }
52
53 if other.is_zero() {
54 steps.push(Step {
55 title: "Zero Input".to_owned(),
56 description: format!("GCD(a, 0) = |a|\nResult: {}", expr),
57 expression: expr.clone(),
58 rule_applied: "Zero Property".to_owned(),
59 latex: None,
60 });
61 return create_explanation(expr.clone(), expr.clone(), steps);
62 }
63
64 if expr.is_one() || other.is_one() {
65 steps.push(Step {
66 title: "Coprime with 1".to_owned(),
67 description: "GCD with 1 is always 1".to_owned(),
68 expression: Expression::integer(1),
69 rule_applied: "Identity Property".to_owned(),
70 latex: None,
71 });
72 return create_explanation(expr.clone(), Expression::integer(1), steps);
73 }
74
75 let vars_self = expr.polynomial_variables();
76 let vars_other = other.polynomial_variables();
77 let mut all_vars: Vec<_> = vars_self.clone();
78 for v in vars_other.iter() {
79 if !all_vars.iter().any(|s| s.name() == v.name()) {
80 all_vars.push(v.clone());
81 }
82 }
83 let is_univariate = all_vars.len() <= 1;
84
85 let max_degree = if let Some(var) = all_vars.first() {
86 let d1 = expr.degree(var).unwrap_or(0);
87 let d2 = other.degree(var).unwrap_or(0);
88 d1.max(d2)
89 } else {
90 0
91 };
92
93 let has_large_coeffs = max_degree >= 20;
94 let is_sparse = max_degree >= 15;
95
96 steps.push(Step {
97 title: "Analyze Polynomial Characteristics".to_owned(),
98 description: format!(
99 "Variables: {} ({})\n\
100 Maximum degree: {}\n\
101 Characteristics: {}",
102 if all_vars.is_empty() {
103 "constant".to_owned()
104 } else {
105 all_vars
106 .iter()
107 .map(|v| v.name().to_owned())
108 .collect::<Vec<_>>()
109 .join(", ")
110 },
111 if is_univariate {
112 "univariate"
113 } else {
114 "multivariate"
115 },
116 max_degree,
117 if max_degree >= 10 {
118 "high-degree (modular methods preferred)"
119 } else {
120 "low-degree (classical methods efficient)"
121 }
122 ),
123 expression: expr.clone(),
124 rule_applied: "Analysis".to_owned(),
125 latex: None,
126 });
127
128 let rationale = educational::explain_selection_rationale(
129 is_univariate,
130 max_degree,
131 is_sparse,
132 has_large_coeffs,
133 );
134 steps.push(Step {
135 title: "Algorithm Selection".to_owned(),
136 description: rationale,
137 expression: expr.clone(),
138 rule_applied: "Selection".to_owned(),
139 latex: None,
140 });
141
142 let use_modular = is_sparse || has_large_coeffs || max_degree >= 10 || !is_univariate;
143
144 if use_modular {
145 steps.push(Step {
146 title: "Zippel Modular GCD Overview".to_owned(),
147 description: educational::algorithm_overview().to_owned(),
148 expression: expr.clone(),
149 rule_applied: "Algorithm Description".to_owned(),
150 latex: None,
151 });
152
153 steps.push(Step {
154 title: "Content Extraction".to_owned(),
155 description: educational::explain_content_extraction().to_owned(),
156 expression: expr.clone(),
157 rule_applied: "Step 1".to_owned(),
158 latex: None,
159 });
160
161 steps.push(Step {
162 title: "CRT Reconstruction".to_owned(),
163 description: educational::explain_crt_reconstruction().to_owned(),
164 expression: expr.clone(),
165 rule_applied: "Step 2-3".to_owned(),
166 latex: None,
167 });
168
169 steps.push(Step {
170 title: "Trial Division Verification".to_owned(),
171 description: educational::explain_trial_division().to_owned(),
172 expression: expr.clone(),
173 rule_applied: "Step 4".to_owned(),
174 latex: None,
175 });
176 } else {
177 steps.push(Step {
178 title: "Euclidean Algorithm".to_owned(),
179 description: "The polynomial GCD is computed using the Euclidean algorithm:\n\
180 1. Divide larger degree polynomial by smaller\n\
181 2. Replace larger with remainder\n\
182 3. Repeat until remainder is zero\n\
183 4. Last non-zero remainder is the GCD"
184 .to_owned(),
185 expression: expr.clone(),
186 rule_applied: "Algorithm".to_owned(),
187 latex: None,
188 });
189 }
190
191 match expr.polynomial_gcd(other) {
192 Ok(gcd) => {
193 steps.push(Step {
194 title: "GCD Result".to_owned(),
195 description: format!("GCD({}, {}) = {}", expr, other, gcd),
196 expression: gcd.clone(),
197 rule_applied: "Final".to_owned(),
198 latex: None,
199 });
200
201 create_explanation(expr.clone(), gcd, steps)
202 }
203 Err(e) => {
204 steps.push(Step {
205 title: "Error".to_owned(),
206 description: format!("GCD computation failed: {}", e),
207 expression: expr.clone(),
208 rule_applied: "Error".to_owned(),
209 latex: None,
210 });
211
212 create_explanation(expr.clone(), Expression::integer(1), steps)
213 }
214 }
215}