1use ocas_domain::EuclideanDomain;
8
9use crate::dense::DenseUnivariatePolynomial;
10
11impl<D: EuclideanDomain> DenseUnivariatePolynomial<D> {
12 pub(crate) fn pseudo_remainder(&self, divisor: &Self) -> Option<Self> {
22 let self_deg = self.degree()?;
23 let div_deg = divisor.degree()?;
24 if self_deg < div_deg {
25 return Some(self.clone());
26 }
27
28 let d = self.domain();
29 let div_lc = divisor.leading_coeff()?;
30 let mut remainder = self.clone();
31
32 let exponent = self_deg - div_deg + 1;
33
34 let factor = d.pow(div_lc, exponent as u64);
36 remainder = remainder.mul_scalar(&factor);
37
38 let mut quot_coeffs = vec![d.zero(); self_deg - div_deg + 1];
40
41 while let Some(deg) = remainder.degree() {
42 if deg < div_deg {
43 break;
44 }
45 let lc = remainder.leading_coeff().unwrap().clone();
46 let (q, _) = d.div_rem(&lc, div_lc)?;
47 let term_degree = deg - div_deg;
48 quot_coeffs[term_degree] = d.add("_coeffs[term_degree], &q);
49
50 let mut sub_coeffs = vec![d.zero(); term_degree];
51 sub_coeffs.extend(divisor.coeffs().iter().map(|c| d.mul(c, &q)));
52 let sub = Self::from_coeffs(d.clone(), sub_coeffs);
53 remainder = remainder.sub(&sub);
54
55 if let Some(rem_deg) = remainder.degree() {
56 if rem_deg >= deg {
57 break;
58 }
59 } else {
60 break;
61 }
62 }
63
64 Some(remainder)
65 }
66
67 pub fn gcd(&self, other: &Self) -> Self {
89 if other.is_zero() {
90 return self.primitive_part();
91 }
92 if self.is_zero() {
93 return other.primitive_part();
94 }
95
96 let mut a = self.clone();
97 let mut b = other.clone();
98
99 while !b.is_zero() {
100 let r = match a.pseudo_remainder(&b) {
102 Some(rem) => rem,
103 None => break,
104 };
105
106 a = b;
107 b = r;
108 }
109
110 a.primitive_part()
111 }
112
113 pub fn content(&self) -> D::Element {
117 if self.is_zero() {
118 return self.domain().zero();
119 }
120 let coeffs = self.coeffs();
121 let mut g = coeffs[0].clone();
122 for c in &coeffs[1..] {
123 g = self.domain().gcd(&g, c);
124 if self.domain().is_one(&g) {
125 break;
126 }
127 }
128 g
129 }
130
131 pub fn primitive_part(&self) -> Self {
133 if self.is_zero() {
134 return self.zero();
135 }
136 let content = self.content();
137 let coeffs: Vec<D::Element> = self
138 .coeffs()
139 .iter()
140 .map(|c| self.domain().div(c, &content).unwrap_or_else(|| c.clone()))
141 .collect();
142 Self::from_coeffs(self.domain().clone(), coeffs)
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use ocas_domain::{Integer, IntegerDomain};
150
151 fn i(n: i64) -> Integer {
152 Integer::from(n)
153 }
154
155 #[test]
156 fn gcd_x2_minus_1_and_x_plus_1() {
157 let d = IntegerDomain;
158 let a = DenseUnivariatePolynomial::from_coeffs(d, vec![i(-1), i(0), i(1)]);
159 let b = DenseUnivariatePolynomial::from_coeffs(d, vec![i(1), i(1)]);
160 let g = a.gcd(&b);
161 assert_eq!(g.coeffs(), &[i(1), i(1)]);
162 }
163
164 #[test]
165 fn gcd_x2_minus_1_and_x2_plus_2x_plus_1() {
166 let d = IntegerDomain;
167 let a = DenseUnivariatePolynomial::from_coeffs(d, vec![i(-1), i(0), i(1)]);
168 let b = DenseUnivariatePolynomial::from_coeffs(d, vec![i(1), i(2), i(1)]);
169 let g = a.gcd(&b);
170 assert_eq!(g.coeffs(), &[i(1), i(1)]);
171 }
172
173 #[test]
174 fn gcd_coprime() {
175 let d = IntegerDomain;
176 let a = DenseUnivariatePolynomial::from_coeffs(d, vec![i(1), i(1)]);
177 let b = DenseUnivariatePolynomial::from_coeffs(d, vec![i(2), i(1)]);
178 let g = a.gcd(&b);
179 assert_eq!(g.degree(), Some(0));
180 assert!(!g.is_zero());
181 }
182
183 #[test]
184 fn gcd_with_zero() {
185 let d = IntegerDomain;
186 let a = DenseUnivariatePolynomial::from_coeffs(d, vec![i(2), i(4), i(2)]);
187 let g = a.gcd(&a.zero());
188 assert_eq!(g.coeffs(), &[i(1), i(2), i(1)]);
189 }
190
191 #[test]
192 fn primitive_part_of_scaled_polynomial() {
193 let d = IntegerDomain;
194 let p = DenseUnivariatePolynomial::from_coeffs(d, vec![i(2), i(4), i(6)]);
195 let prim = p.primitive_part();
196 assert_eq!(prim.coeffs(), &[i(1), i(2), i(3)]);
197 }
198}