mathhook_core/core/polynomial/
gcd_ops.rs1use super::classification::PolynomialClassification;
7use super::error::PolynomialError;
8use super::integer_gcd;
9use crate::algebra::gcd::{polynomial_gcd, PolynomialGcd as PolynomialGcdTrait};
10use crate::core::Expression;
11use crate::simplify::Simplify;
12
13pub trait PolynomialGcdOps: PolynomialClassification {
35 fn polynomial_gcd(&self, other: &Self) -> Result<Expression, PolynomialError>;
48
49 fn polynomial_lcm(&self, other: &Self) -> Result<Expression, PolynomialError>;
53
54 fn polynomial_cofactors(
58 &self,
59 other: &Self,
60 ) -> Result<(Expression, Expression, Expression), PolynomialError>;
61}
62
63impl PolynomialGcdOps for Expression {
64 fn polynomial_gcd(&self, other: &Self) -> Result<Expression, PolynomialError> {
65 polynomial_gcd(self, other)
66 }
67
68 fn polynomial_lcm(&self, other: &Self) -> Result<Expression, PolynomialError> {
69 use crate::core::Number;
70
71 if let (Expression::Number(Number::Integer(a)), Expression::Number(Number::Integer(b))) =
73 (self, other)
74 {
75 let gcd_val = integer_gcd(*a, *b);
76 if gcd_val == 0 {
77 return Ok(Expression::integer(0));
78 }
79 let lcm_val = (*a / gcd_val) * *b;
80 return Ok(Expression::integer(lcm_val.abs()));
81 }
82
83 let gcd = self.polynomial_gcd(other)?;
84
85 if gcd.is_zero() {
86 return Ok(Expression::integer(0));
87 }
88
89 let product = Expression::mul(vec![self.clone(), other.clone()]);
90 let vars = self.find_variables();
91 if vars.is_empty() {
92 return Ok(product.simplify());
94 }
95 Ok(PolynomialGcdTrait::quo_polynomial(&product, &gcd, &vars[0]))
96 }
97
98 fn polynomial_cofactors(
99 &self,
100 other: &Self,
101 ) -> Result<(Expression, Expression, Expression), PolynomialError> {
102 let gcd = self.polynomial_gcd(other)?;
103
104 if gcd.is_zero() || gcd.is_one() {
105 return Ok((gcd, self.clone(), other.clone()));
106 }
107
108 let vars = self.find_variables();
109 if vars.len() == 1 {
110 let var = &vars[0];
111 let (q1, r1) = PolynomialGcdTrait::div_polynomial(self, &gcd, var);
112 let (q2, r2) = PolynomialGcdTrait::div_polynomial(other, &gcd, var);
113
114 if r1.is_zero() && r2.is_zero() {
115 return Ok((gcd, q1, q2));
116 }
117 }
118
119 Ok((gcd, self.clone(), other.clone()))
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use crate::{expr, symbol};
127
128 #[test]
129 fn test_polynomial_gcd_basic() {
130 let _x = symbol!(x);
131 let p1 = expr!((x ^ 2) - 1);
132 let p2 = expr!(x - 1);
133
134 let gcd = p1.polynomial_gcd(&p2).unwrap();
135 assert!(!gcd.is_zero());
136 }
137
138 #[test]
139 fn test_polynomial_lcm_basic() {
140 let a = Expression::integer(6);
141 let b = Expression::integer(8);
142
143 let lcm = a.polynomial_lcm(&b).unwrap();
144 assert!(!lcm.is_zero());
145 }
146
147 #[test]
148 fn test_polynomial_cofactors_basic() {
149 let _x = symbol!(x);
150 let p1 = expr!((x ^ 2) - 1);
151 let p2 = expr!(x - 1);
152
153 let (gcd, cof1, cof2) = p1.polynomial_cofactors(&p2).unwrap();
154 assert!(!gcd.is_zero());
155 assert!(!cof1.is_zero());
156 assert!(!cof2.is_zero());
157 }
158}