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