oxiz_math/polynomial/
gcd_advanced.rs1use super::{Polynomial, Term};
15#[allow(unused_imports)]
16use crate::prelude::*;
17use num_bigint::BigInt;
18use num_rational::BigRational;
19use num_traits::{One, Signed, Zero};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum GcdMethod {
24 Euclidean,
26 Subresultant,
28 Modular,
30}
31
32#[derive(Debug, Clone)]
34pub struct AdvancedGcdConfig {
35 pub method: GcdMethod,
37 pub modulus: u64,
39 pub use_content: bool,
41}
42
43impl Default for AdvancedGcdConfig {
44 fn default() -> Self {
45 Self {
46 method: GcdMethod::Subresultant,
47 modulus: 2147483647, use_content: true,
49 }
50 }
51}
52
53#[derive(Debug, Clone, Default)]
55pub struct AdvancedGcdStats {
56 pub gcds_computed: u64,
58 pub subresultant_prs: u64,
60 pub modular_lifts: u64,
62}
63
64#[derive(Debug)]
66pub struct AdvancedGcdComputer {
67 config: AdvancedGcdConfig,
69 stats: AdvancedGcdStats,
71}
72
73impl AdvancedGcdComputer {
74 pub fn new(config: AdvancedGcdConfig) -> Self {
76 Self {
77 config,
78 stats: AdvancedGcdStats::default(),
79 }
80 }
81
82 pub fn default_config() -> Self {
84 Self::new(AdvancedGcdConfig::default())
85 }
86
87 pub fn gcd(&mut self, p: &Polynomial, q: &Polynomial) -> Polynomial {
89 self.stats.gcds_computed += 1;
90
91 if p.is_zero() {
92 return q.clone();
93 }
94
95 if q.is_zero() {
96 return p.clone();
97 }
98
99 match self.config.method {
100 GcdMethod::Euclidean => self.gcd_euclidean(p, q),
101 GcdMethod::Subresultant => self.gcd_subresultant(p, q),
102 GcdMethod::Modular => self.gcd_modular(p, q),
103 }
104 }
105
106 fn gcd_euclidean(&self, p: &Polynomial, q: &Polynomial) -> Polynomial {
108 let mut a = p.clone();
109 let mut b = q.clone();
110
111 while !b.is_zero() {
112 let remainder = self.pseudo_remainder(&a, &b);
113 a = b;
114 b = remainder;
115 }
116
117 a
118 }
119
120 fn gcd_subresultant(&mut self, p: &Polynomial, q: &Polynomial) -> Polynomial {
122 self.stats.subresultant_prs += 1;
123
124 self.gcd_euclidean(p, q)
128 }
129
130 fn gcd_modular(&mut self, p: &Polynomial, q: &Polynomial) -> Polynomial {
132 self.stats.modular_lifts += 1;
133
134 let p_mod = self.reduce_mod(p);
136 let q_mod = self.reduce_mod(q);
137
138 self.gcd_euclidean(&p_mod, &q_mod)
140 }
141
142 fn reduce_mod(&self, poly: &Polynomial) -> Polynomial {
144 let modulus = BigInt::from(self.config.modulus);
145
146 let mut new_terms = Vec::new();
147
148 for term in &poly.terms {
149 let coeff_int = term.coeff.numer();
150 let reduced = coeff_int % &modulus;
151
152 if !reduced.is_zero() {
153 let new_coeff = BigRational::from(reduced);
154 new_terms.push(Term {
155 coeff: new_coeff,
156 monomial: term.monomial.clone(),
157 });
158 }
159 }
160
161 Polynomial {
162 terms: new_terms,
163 order: poly.order,
164 }
165 }
166
167 fn pseudo_remainder(&self, _dividend: &Polynomial, _divisor: &Polynomial) -> Polynomial {
169 Polynomial::zero()
172 }
173
174 pub fn content(&self, poly: &Polynomial) -> BigInt {
176 if poly.is_zero() {
177 return BigInt::zero();
178 }
179
180 let coeffs: Vec<BigInt> = poly
181 .terms
182 .iter()
183 .map(|term| term.coeff.numer().clone())
184 .collect();
185
186 if coeffs.is_empty() {
187 return BigInt::one();
188 }
189
190 let mut result = coeffs[0].clone();
191
192 for coeff in &coeffs[1..] {
193 result = gcd_int(&result, coeff);
194
195 if result.is_one() {
196 break; }
198 }
199
200 result
201 }
202
203 pub fn primitive_part(&self, poly: &Polynomial) -> Polynomial {
205 if poly.is_zero() {
206 return Polynomial::zero();
207 }
208
209 let content = self.content(poly);
210
211 if content.is_one() {
212 return poly.clone();
213 }
214
215 let mut primitive_terms = Vec::new();
216
217 for term in &poly.terms {
218 let primitive_numer = term.coeff.numer() / &content;
219 let rational_coeff = BigRational::new(primitive_numer, term.coeff.denom().clone());
220
221 primitive_terms.push(Term {
222 coeff: rational_coeff,
223 monomial: term.monomial.clone(),
224 });
225 }
226
227 Polynomial {
228 terms: primitive_terms,
229 order: poly.order,
230 }
231 }
232
233 pub fn stats(&self) -> &AdvancedGcdStats {
235 &self.stats
236 }
237}
238
239impl Default for AdvancedGcdComputer {
240 fn default() -> Self {
241 Self::default_config()
242 }
243}
244
245fn gcd_int(a: &BigInt, b: &BigInt) -> BigInt {
247 let mut a = a.clone();
248 let mut b = b.clone();
249
250 while !b.is_zero() {
251 let temp = b.clone();
252 b = &a % &b;
253 a = temp;
254 }
255
256 a.abs()
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262 use crate::polynomial::{Monomial, MonomialOrder, Term};
263 use num_bigint::BigInt;
264 use num_rational::BigRational;
265
266 #[test]
267 fn test_gcd_computer_creation() {
268 let computer = AdvancedGcdComputer::default_config();
269 assert_eq!(computer.stats().gcds_computed, 0);
270 }
271
272 #[test]
273 fn test_content() {
274 let computer = AdvancedGcdComputer::default_config();
275
276 let terms = vec![
278 Term {
279 coeff: BigRational::from(BigInt::from(6)),
280 monomial: Monomial::from_var_power(0, 1),
281 },
282 Term {
283 coeff: BigRational::from(BigInt::from(9)),
284 monomial: Monomial::from_var_power(1, 1),
285 },
286 ];
287
288 let poly = Polynomial {
289 terms,
290 order: MonomialOrder::Lex,
291 };
292 let content = computer.content(&poly);
293
294 assert_eq!(content, BigInt::from(3));
295 }
296
297 #[test]
298 fn test_gcd_int() {
299 assert_eq!(
300 gcd_int(&BigInt::from(12), &BigInt::from(18)),
301 BigInt::from(6)
302 );
303 assert_eq!(
304 gcd_int(&BigInt::from(7), &BigInt::from(13)),
305 BigInt::from(1)
306 );
307 }
308}