1use crate::core::polynomial::IntPoly;
4use crate::core::polynomial::PolynomialError;
5use crate::core::{Expression, Number, Symbol};
6use num_integer::Integer;
7
8pub trait PolynomialGcd {
10 fn gcd(&self, other: &Self) -> Self;
11 fn lcm(&self, other: &Self) -> Self;
12 fn factor_gcd(&self) -> Self;
13 fn cofactors(&self, other: &Self) -> (Expression, Expression, Expression);
14
15 fn div_polynomial(&self, divisor: &Expression, var: &Symbol) -> (Expression, Expression);
44
45 fn quo_polynomial(&self, divisor: &Expression, var: &Symbol) -> Expression;
71
72 fn rem_polynomial(&self, divisor: &Expression, var: &Symbol) -> Expression;
98}
99
100impl PolynomialGcd for Expression {
101 #[inline(always)]
106 fn gcd(&self, other: &Self) -> Self {
107 if let (Expression::Number(Number::Integer(a)), Expression::Number(Number::Integer(b))) =
109 (self, other)
110 {
111 return Expression::integer(a.gcd(b));
112 }
113
114 if self == other {
115 return self.clone();
116 }
117
118 if self.is_zero() {
119 return other.clone();
120 }
121 if other.is_zero() {
122 return self.clone();
123 }
124
125 let vars = self.find_variables();
127 if vars.len() == 1 {
128 let var = &vars[0];
129 if IntPoly::can_convert(self, var) && IntPoly::can_convert(other, var) {
130 if let (Some(poly1), Some(poly2)) = (
131 IntPoly::try_from_expression(self, var),
132 IntPoly::try_from_expression(other, var),
133 ) {
134 if let Ok(gcd_poly) = poly1.gcd_i64(&poly2) {
135 let result = gcd_poly.to_expression(var);
136 if let Expression::Number(Number::Integer(n)) = &result {
138 if *n < 0 {
139 return Expression::integer(n.abs());
140 }
141 }
142 return result;
143 }
144 }
145 }
146 }
147
148 if vars.len() == 1 {
150 let var = &vars[0];
151 let result = symbolic_gcd_euclidean(self, other, var);
152 if let Expression::Number(Number::Integer(n)) = &result {
154 if *n < 0 {
155 return Expression::integer(n.abs());
156 }
157 }
158 return result;
159 }
160
161 Expression::integer(1)
163 }
164
165 #[inline(always)]
167 fn lcm(&self, other: &Self) -> Self {
168 let gcd_val = self.gcd(other);
169
170 if gcd_val.is_zero() {
171 return Expression::integer(0);
172 }
173
174 let product = Expression::mul(vec![self.clone(), other.clone()]);
175 Expression::div(product, gcd_val)
176 }
177
178 #[inline(always)]
180 fn factor_gcd(&self) -> Self {
181 match self {
182 Expression::Add(terms) => {
183 if terms.len() < 2 {
184 return self.clone();
185 }
186
187 let mut common_gcd = terms[0].clone();
188 for term in &terms[1..] {
189 common_gcd = common_gcd.gcd(term);
190 if common_gcd.is_one() {
191 return self.clone();
192 }
193 }
194
195 common_gcd
196 }
197 Expression::Mul(_factors) => self.clone(),
198 _ => self.clone(),
199 }
200 }
201
202 fn cofactors(&self, other: &Self) -> (Expression, Expression, Expression) {
204 let gcd_val = self.gcd(other);
205
206 if gcd_val.is_zero() || gcd_val.is_one() {
207 return (gcd_val, self.clone(), other.clone());
208 }
209
210 let vars = self.find_variables();
212 if vars.len() == 1 {
213 let var = &vars[0];
214 if IntPoly::can_convert(self, var)
215 && IntPoly::can_convert(other, var)
216 && IntPoly::can_convert(&gcd_val, var)
217 {
218 if let (Some(poly1), Some(poly2), Some(gcd_poly)) = (
219 IntPoly::try_from_expression(self, var),
220 IntPoly::try_from_expression(other, var),
221 IntPoly::try_from_expression(&gcd_val, var),
222 ) {
223 if let (Ok((cof1, _)), Ok((cof2, _))) =
224 (poly1.div_rem(&gcd_poly), poly2.div_rem(&gcd_poly))
225 {
226 return (gcd_val, cof1.to_expression(var), cof2.to_expression(var));
227 }
228 }
229 }
230
231 let (q1, r1) = crate::algebra::polynomial_division::polynomial_div(self, &gcd_val, var);
233 let (q2, r2) =
234 crate::algebra::polynomial_division::polynomial_div(other, &gcd_val, var);
235 if r1.is_zero() && r2.is_zero() {
236 return (gcd_val, q1, q2);
237 }
238 }
239
240 (gcd_val, self.clone(), other.clone())
241 }
242
243 fn div_polynomial(&self, divisor: &Expression, var: &Symbol) -> (Expression, Expression) {
244 crate::algebra::polynomial_division::polynomial_div(self, divisor, var)
245 }
246
247 fn quo_polynomial(&self, divisor: &Expression, var: &Symbol) -> Expression {
248 self.div_polynomial(divisor, var).0
249 }
250
251 fn rem_polynomial(&self, divisor: &Expression, var: &Symbol) -> Expression {
252 self.div_polynomial(divisor, var).1
253 }
254}
255
256fn symbolic_gcd_euclidean(p1: &Expression, p2: &Expression, var: &Symbol) -> Expression {
261 let mut a = p1.clone();
262 let mut b = p2.clone();
263
264 for _ in 0..10 {
266 if b.is_zero() {
267 if let Expression::Number(Number::Integer(n)) = &a {
269 if *n < 0 {
270 return Expression::integer(n.abs());
271 }
272 }
273 return a;
274 }
275
276 let remainder = crate::algebra::polynomial_division::polynomial_rem(&a, &b, var);
277 a = b;
278 b = remainder;
279 }
280
281 Expression::integer(1)
283}
284
285pub fn polynomial_gcd(p1: &Expression, p2: &Expression) -> Result<Expression, PolynomialError> {
310 use crate::expr;
311
312 if let (Expression::Number(Number::Integer(n1)), Expression::Number(Number::Integer(n2))) =
314 (p1, p2)
315 {
316 return Ok(Expression::integer(n1.gcd(n2)));
317 }
318
319 if p1.is_zero() {
321 return Ok(p2.clone());
322 }
323 if p2.is_zero() {
324 return Ok(p1.clone());
325 }
326
327 if p1.is_one() || p2.is_one() {
329 return Ok(expr!(1));
330 }
331
332 if let (Expression::Symbol(s1), Expression::Symbol(s2)) = (p1, p2) {
334 if s1 == s2 {
335 return Ok(p1.clone());
336 }
337 return Ok(expr!(1));
338 }
339
340 let vars_p1 = p1.find_variables();
342 if vars_p1.len() == 1 {
343 let var = &vars_p1[0];
344 if IntPoly::can_convert(p1, var) && IntPoly::can_convert(p2, var) {
345 if let (Some(poly1), Some(poly2)) = (
346 IntPoly::try_from_expression(p1, var),
347 IntPoly::try_from_expression(p2, var),
348 ) {
349 return Ok(poly1
350 .gcd_i64(&poly2)
351 .map_err(|e| PolynomialError::GcdComputationFailed {
352 reason: format!("{:?}", e),
353 })?
354 .to_expression(var));
355 }
356 }
357 }
358
359 Ok(p1.gcd(p2))
361}
362
363pub fn univariate_gcd(
377 p1: &Expression,
378 p2: &Expression,
379 var: &Symbol,
380) -> Result<Expression, PolynomialError> {
381 if IntPoly::can_convert(p1, var) && IntPoly::can_convert(p2, var) {
383 if let (Some(poly1), Some(poly2)) = (
384 IntPoly::try_from_expression(p1, var),
385 IntPoly::try_from_expression(p2, var),
386 ) {
387 let gcd_poly =
388 poly1
389 .gcd_i64(&poly2)
390 .map_err(|e| PolynomialError::GcdComputationFailed {
391 reason: format!("{:?}", e),
392 })?;
393 return Ok(gcd_poly.to_expression(var));
394 }
395 }
396
397 Ok(p1.gcd(p2))
399}
400
401pub fn univariate_gcd_modular(
415 p1: &Expression,
416 p2: &Expression,
417 var: &Symbol,
418) -> Result<(Expression, Expression, Expression), PolynomialError> {
419 if IntPoly::can_convert(p1, var) && IntPoly::can_convert(p2, var) {
421 if let (Some(poly1), Some(poly2)) = (
422 IntPoly::try_from_expression(p1, var),
423 IntPoly::try_from_expression(p2, var),
424 ) {
425 let gcd_poly =
426 poly1
427 .gcd_i64(&poly2)
428 .map_err(|e| PolynomialError::GcdComputationFailed {
429 reason: format!("{:?}", e),
430 })?;
431
432 let (cof1, _) = poly1
434 .div_rem(&gcd_poly)
435 .map_err(|_| PolynomialError::DivisionByZero)?;
436 let (cof2, _) = poly2
437 .div_rem(&gcd_poly)
438 .map_err(|_| PolynomialError::DivisionByZero)?;
439
440 return Ok((
441 gcd_poly.to_expression(var),
442 cof1.to_expression(var),
443 cof2.to_expression(var),
444 ));
445 }
446 }
447
448 let gcd = p1.gcd(p2);
450 Ok((gcd.clone(), p1.clone(), p2.clone()))
451}
452
453#[cfg(test)]
454mod tests {
455 use super::*;
456 use crate::expr;
457 use crate::symbol;
458
459 #[test]
460 fn test_number_gcd() {
461 let a = Expression::integer(12);
462 let b = Expression::integer(8);
463 let result = a.gcd(&b);
464 assert_eq!(result, Expression::integer(4));
465
466 let a = Expression::integer(17);
467 let b = Expression::integer(13);
468 let result = a.gcd(&b);
469 assert_eq!(result, Expression::integer(1));
470 }
471
472 #[test]
473 fn test_gcd_with_zero() {
474 let a = Expression::integer(5);
475 let zero = Expression::integer(0);
476
477 let result = a.gcd(&zero);
478 assert_eq!(result, Expression::integer(5));
479
480 let result = zero.gcd(&a);
481 assert_eq!(result, Expression::integer(5));
482 }
483
484 #[test]
485 fn test_identical_expressions() {
486 let x = expr!(x);
487 let result = x.gcd(&x);
488 assert_eq!(result, x);
489 }
490
491 #[test]
492 fn test_gcd_performance_benchmark() {
493 use std::time::Instant;
494
495 let start = Instant::now();
496
497 for i in 1..10_000 {
498 let a = Expression::integer(i * 6);
499 let b = Expression::integer(i * 9);
500 let _result = a.gcd(&b);
501 }
502
503 let duration = start.elapsed();
504 let ops_per_sec = 10_000.0 / duration.as_secs_f64();
505
506 println!("GCD Performance: {:.2}M ops/sec", ops_per_sec / 1_000_000.0);
507
508 assert!(
509 ops_per_sec > 100_000.0,
510 "Expected >100K ops/sec, got {:.2}",
511 ops_per_sec
512 );
513 }
514
515 #[test]
516 fn test_polynomial_gcd_basic() {
517 let x = symbol!(x);
518
519 let poly1 = Expression::mul(vec![Expression::integer(6), Expression::symbol(x.clone())]);
520 let poly2 = Expression::mul(vec![Expression::integer(9), Expression::symbol(x.clone())]);
521
522 let result = poly1.gcd(&poly2);
523
524 println!("Polynomial GCD result: {}", result);
525 assert!(!result.is_zero());
526 }
527
528 #[test]
529 fn test_factor_gcd() {
530 let x = symbol!(x);
531
532 let term1 = Expression::mul(vec![Expression::integer(6), Expression::symbol(x.clone())]);
533 let term2 = Expression::mul(vec![Expression::integer(9), Expression::symbol(x.clone())]);
534 let sum = Expression::add(vec![term1, term2]);
535
536 let gcd_factor = sum.factor_gcd();
537 println!("Factored GCD: {}", gcd_factor);
538
539 assert!(!gcd_factor.is_zero());
540 }
541
542 #[test]
543 fn test_lcm_basic() {
544 let a = Expression::integer(6);
545 let b = Expression::integer(8);
546 let result = a.lcm(&b);
547
548 println!("LCM result: {}", result);
549 assert!(!result.is_zero());
550 }
551
552 #[test]
553 fn test_int_poly_fast_path() {
554 let _x = symbol!(x);
555
556 let poly1 = expr!((x ^ 5) + (2 * (x ^ 4)) + (3 * (x ^ 3)) + (4 * (x ^ 2)) + (5 * x) + 6);
557 let poly2 =
558 expr!((2 * (x ^ 5)) + (4 * (x ^ 4)) + (6 * (x ^ 3)) + (8 * (x ^ 2)) + (10 * x) + 12);
559
560 let result = poly1.gcd(&poly2);
561
562 println!("IntPoly fast-path GCD: {}", result);
563 assert!(!result.is_zero());
564 }
565
566 #[test]
567 fn test_intpoly_gcd_direct() {
568 let _x = symbol!(x);
569
570 let p1 = expr!((x ^ 2) - 1);
571 let p2 = expr!(x - 1);
572
573 let gcd = p1.gcd(&p2);
574
575 println!("Direct IntPoly GCD: {}", gcd);
576 assert!(!gcd.is_zero());
577 }
578
579 #[test]
580 fn test_cofactors_intpoly() {
581 let _x = symbol!(x);
582
583 let p1 = expr!((x ^ 2) - 1);
584 let p2 = expr!(x - 1);
585
586 let (gcd, cof1, cof2) = p1.cofactors(&p2);
587
588 println!("GCD: {}, Cofactor1: {}, Cofactor2: {}", gcd, cof1, cof2);
589 assert!(!gcd.is_zero());
590 }
591
592 #[test]
593 fn test_polynomial_gcd_function() {
594 let a = Expression::integer(12);
595 let b = Expression::integer(18);
596 let gcd = polynomial_gcd(&a, &b).unwrap();
597 assert_eq!(gcd, Expression::integer(6));
598 }
599
600 #[test]
601 fn test_univariate_gcd_function() {
602 let _x = symbol!(x);
603 let p1 = expr!((x ^ 2) - 1);
604 let p2 = expr!(x - 1);
605 let gcd = univariate_gcd(&p1, &p2, &_x).unwrap();
606 assert!(!gcd.is_zero());
607 }
608
609 #[test]
610 fn test_gcd_coprime_expressions() {
611 let _x = symbol!(x);
612 let a = expr!(x + 1);
613 let b = expr!(x + 2);
614 let result = a.gcd(&b);
615 println!("GCD result: {:?}", result);
616 assert_eq!(result, Expression::integer(1));
617 }
618}