mathhook_core/core/polynomial/poly/
division.rs1use super::Poly;
2use crate::core::polynomial::traits::EuclideanDomain;
3use crate::error::MathError;
4
5#[inline(always)]
7fn gcd_i64(mut a: i64, mut b: i64) -> i64 {
8 while b != 0 {
9 let t = b;
10 b = a % b;
11 a = t;
12 }
13 a.abs()
14}
15
16impl<T: EuclideanDomain> Poly<T> {
17 #[inline(always)]
25 pub fn div_rem(&self, divisor: &Self) -> Result<(Self, Self), MathError> {
26 if divisor.is_zero() {
27 return Err(MathError::DivisionByZero);
28 }
29
30 if self.is_zero() {
31 return Ok((Self::zero(), Self::zero()));
32 }
33
34 let self_deg = match self.degree() {
35 Some(d) => d,
36 None => return Ok((Self::zero(), Self::zero())),
37 };
38
39 let divisor_deg = match divisor.degree() {
40 Some(d) => d,
41 None => return Err(MathError::DivisionByZero),
42 };
43
44 if self_deg < divisor_deg {
45 return Ok((Self::zero(), self.clone()));
46 }
47
48 let divisor_lc = divisor.leading_coeff();
49 let mut remainder = self.coeffs.clone();
50 let mut quotient = Vec::with_capacity(self_deg - divisor_deg + 1);
51 for _ in 0..=self_deg - divisor_deg {
52 quotient.push(T::zero());
53 }
54
55 for i in (0..=self_deg - divisor_deg).rev() {
56 let rem_idx = i + divisor_deg;
57 let rem_coeff = remainder[rem_idx].clone();
58
59 let (q_coeff, r) = rem_coeff.div_rem(&divisor_lc);
60 if !r.is_zero() {
61 break;
62 }
63
64 quotient[i] = q_coeff.clone();
65
66 for (j, d_coeff) in divisor.coeffs.iter().enumerate() {
67 let sub_val = q_coeff.wrapping_mul(d_coeff);
68 remainder[i + j] = remainder[i + j].wrapping_sub(&sub_val);
69 }
70 }
71
72 Ok((Self::from_coeffs(quotient), Self::from_coeffs(remainder)))
73 }
74
75 #[inline(always)]
85 pub fn pseudo_div_rem(&self, divisor: &Self) -> Result<(Self, Self, T), MathError> {
86 if divisor.is_zero() {
87 return Err(MathError::DivisionByZero);
88 }
89
90 if self.is_zero() {
91 return Ok((Self::zero(), Self::zero(), T::one()));
92 }
93
94 let self_deg = match self.degree() {
95 Some(d) => d,
96 None => return Ok((Self::zero(), Self::zero(), T::one())),
97 };
98
99 let divisor_deg = match divisor.degree() {
100 Some(d) => d,
101 None => return Err(MathError::DivisionByZero),
102 };
103
104 if self_deg < divisor_deg {
105 return Ok((Self::zero(), self.clone(), T::one()));
106 }
107
108 let divisor_lc = divisor.leading_coeff();
109 let mut remainder = self.coeffs.clone();
110 let mut quotient = Vec::with_capacity(self_deg - divisor_deg + 1);
111 for _ in 0..=self_deg - divisor_deg {
112 quotient.push(T::zero());
113 }
114 let mut multiplier = T::one();
115
116 for i in (0..=self_deg - divisor_deg).rev() {
117 let rem_idx = i + divisor_deg;
118
119 for c in remainder.iter_mut() {
120 *c = c.wrapping_mul(&divisor_lc);
121 }
122 for c in quotient.iter_mut() {
123 *c = c.wrapping_mul(&divisor_lc);
124 }
125 multiplier = multiplier.wrapping_mul(&divisor_lc);
126
127 let (q_coeff, _) = remainder[rem_idx].div_rem(&divisor_lc);
128 quotient[i] = q_coeff.clone();
129
130 for (j, d_coeff) in divisor.coeffs.iter().enumerate() {
131 let sub_val = q_coeff.wrapping_mul(d_coeff);
132 remainder[i + j] = remainder[i + j].wrapping_sub(&sub_val);
133 }
134 }
135
136 Ok((
137 Self::from_coeffs(quotient),
138 Self::from_coeffs(remainder),
139 multiplier,
140 ))
141 }
142
143 #[inline(always)]
151 pub fn gcd(&self, other: &Self) -> Result<Self, MathError> {
152 if self.is_zero() {
153 return other.primitive_part();
154 }
155 if other.is_zero() {
156 return self.primitive_part();
157 }
158
159 let mut a = self.primitive_part()?;
160 let mut b = other.primitive_part()?;
161
162 while !b.is_zero() {
163 let (_, rem, _) = a.pseudo_div_rem(&b)?;
164 a = b;
165 b = if rem.is_zero() {
166 rem
167 } else {
168 rem.primitive_part()?
169 };
170 }
171
172 Ok(a)
173 }
174
175 #[inline(always)]
177 pub fn content(&self) -> T {
178 if self.coeffs.is_empty() {
179 return T::zero();
180 }
181
182 let mut g = self.coeffs[0].abs();
183 for c in &self.coeffs[1..] {
184 g = g.gcd(&c.abs());
185 if g.is_one() {
186 break;
187 }
188 }
189 g
190 }
191
192 #[inline(always)]
197 pub fn primitive_part(&self) -> Result<Self, MathError> {
198 let c = self.content();
199 if c.is_zero() {
200 return Err(MathError::DivisionByZero);
201 }
202 if c.is_one() {
203 return Ok(self.clone());
204 }
205
206 let coeffs: Vec<T> = self
207 .coeffs
208 .iter()
209 .map(|x| {
210 let (quot, _) = x.div_rem(&c);
211 quot
212 })
213 .collect();
214 Ok(Self { coeffs })
215 }
216}
217
218impl super::IntPoly {
219 #[inline(always)]
221 pub fn is_monic(&self) -> bool {
222 self.leading_coeff() == 1
223 }
224
225 #[inline(always)]
228 pub fn try_make_monic(&self) -> Option<Self> {
229 let lc = self.leading_coeff();
230 if lc == 0 {
231 return None;
232 }
233 if lc == 1 {
234 return Some(self.clone());
235 }
236
237 for &c in &self.coeffs {
238 if c % lc != 0 {
239 return None;
240 }
241 }
242
243 let coeffs: Vec<i64> = self.coeffs.iter().map(|&c| c / lc).collect();
244 Some(Self { coeffs })
245 }
246
247 #[inline(always)]
249 pub fn normalize(&self) -> Self {
250 if self.leading_coeff() < 0 {
251 self.negate()
252 } else {
253 self.clone()
254 }
255 }
256
257 #[inline(always)]
259 pub fn content_i64(&self) -> i64 {
260 if self.coeffs.is_empty() {
261 return 0;
262 }
263
264 let mut g = self.coeffs[0].abs();
265 for &c in &self.coeffs[1..] {
266 g = gcd_i64(g, c.abs());
267 if g == 1 {
268 break;
269 }
270 }
271 g
272 }
273
274 #[inline(always)]
279 pub fn primitive_part_i64(&self) -> Result<Self, MathError> {
280 let c = self.content_i64();
281 if c == 0 {
282 return Err(MathError::DivisionByZero);
283 }
284 if c == 1 {
285 return Ok(self.clone());
286 }
287
288 let coeffs: Vec<i64> = self.coeffs.iter().map(|&x| x / c).collect();
289 Ok(Self { coeffs })
290 }
291
292 #[inline(always)]
297 pub fn gcd_i64(&self, other: &Self) -> Result<Self, MathError> {
298 if self.is_zero() {
299 return Ok(other.primitive_part_i64()?.normalize());
300 }
301 if other.is_zero() {
302 return Ok(self.primitive_part_i64()?.normalize());
303 }
304
305 let mut a = self.primitive_part_i64()?;
306 let mut b = other.primitive_part_i64()?;
307
308 while !b.is_zero() {
309 let (_, rem, _) = a.pseudo_div_rem(&b)?;
310 a = b;
311 b = if rem.is_zero() {
312 rem
313 } else {
314 rem.primitive_part_i64()?
315 };
316 }
317
318 Ok(a.normalize())
319 }
320}
321
322#[cfg(test)]
323mod tests {
324 use crate::core::polynomial::poly::IntPoly;
325
326 #[test]
327 fn test_division_exact() {
328 let dividend = IntPoly::from_coeffs(vec![-1, 0, 1]);
329 let divisor = IntPoly::from_coeffs(vec![-1, 1]);
330 let (quot, rem) = dividend.div_rem(&divisor).unwrap();
331
332 assert_eq!(quot.coefficients(), &[1, 1]);
333 assert!(rem.is_zero());
334 }
335
336 #[test]
337 fn test_gcd() {
338 let p1 = IntPoly::from_coeffs(vec![0, 0, 6]);
339 let p2 = IntPoly::from_coeffs(vec![0, 9]);
340 let g = p1.gcd_i64(&p2).unwrap();
341
342 assert_eq!(g.degree(), Some(1));
343 assert_eq!(g.coeff(0), 0);
344 assert!(g.coeff(1) > 0);
345 }
346
347 #[test]
348 fn test_gcd_coprime() {
349 let p1 = IntPoly::from_coeffs(vec![1, 1]);
350 let p2 = IntPoly::from_coeffs(vec![2, 1]);
351 let g = p1.gcd_i64(&p2).unwrap();
352
353 assert!(g.is_constant());
354 assert_eq!(g.coefficients(), &[1]);
355 }
356
357 #[test]
358 fn test_content() {
359 let p = IntPoly::from_coeffs(vec![6, 12, 18]);
360 assert_eq!(p.content_i64(), 6);
361 }
362
363 #[test]
364 fn test_primitive_part() {
365 let p = IntPoly::from_coeffs(vec![6, 12, 18]);
366 let pp = p.primitive_part_i64().unwrap();
367 assert_eq!(pp.coefficients(), &[1, 2, 3]);
368 }
369
370 #[test]
371 fn test_division_by_zero() {
372 let dividend = IntPoly::from_coeffs(vec![1, 2, 3]);
373 let divisor = IntPoly::from_coeffs(vec![]);
374 assert!(dividend.div_rem(&divisor).is_err());
375 }
376}