1use super::super::structure::*;
2use crate::{
3 matrix::*,
4 parsing::{parse_integer_polynomial, parse_rational_polynomial},
5};
6use algebraeon_nzq::*;
7use algebraeon_sets::structure::*;
8use itertools::Itertools;
9use std::hash::Hash;
10use std::{
11 borrow::{Borrow, Cow},
12 fmt::Display,
13 marker::PhantomData,
14};
15
16#[derive(Debug, Clone)]
17pub struct Polynomial<Set> {
18 coeffs: Vec<Set>,
21}
22
23impl<Set> Polynomial<Set> {
24 pub fn from_coeffs(coeffs: Vec<impl Into<Set>>) -> Self {
25 #[allow(clippy::redundant_closure_for_method_calls)]
26 Self {
27 coeffs: coeffs.into_iter().map(|x| x.into()).collect(),
28 }
29 }
30
31 pub fn constant(x: Set) -> Self {
32 Self::from_coeffs(vec![x])
33 }
34
35 pub fn apply_map<ImgSet>(&self, f: impl Fn(&Set) -> ImgSet) -> Polynomial<ImgSet> {
36 Polynomial::from_coeffs(self.coeffs.iter().map(f).collect())
37 }
38
39 pub fn apply_map_into<ImgSet>(self, f: impl Fn(Set) -> ImgSet) -> Polynomial<ImgSet> {
40 Polynomial::from_coeffs(self.coeffs.into_iter().map(f).collect())
41 }
42
43 pub fn apply_map_with_powers<ImgSet>(
44 &self,
45 f: impl Fn((usize, &Set)) -> ImgSet,
46 ) -> Polynomial<ImgSet> {
47 Polynomial::from_coeffs(self.coeffs.iter().enumerate().map(f).collect())
48 }
49
50 pub fn apply_map_into_with_powers<ImgSet>(
51 self,
52 f: impl Fn((usize, Set)) -> ImgSet,
53 ) -> Polynomial<ImgSet> {
54 Polynomial::from_coeffs(self.coeffs.into_iter().enumerate().map(f).collect())
55 }
56}
57
58pub trait PolynomialFromStr: Sized {
59 type Err;
60
61 fn from_str(polynomial_str: &str, var: &str) -> Result<Self, Self::Err>;
62}
63
64impl PolynomialFromStr for Polynomial<Natural> {
65 type Err = ();
66
67 fn from_str(polynomial_str: &str, var: &str) -> Result<Self, ()> {
68 Ok(Self::from_coeffs(
69 Polynomial::<Integer>::from_str(polynomial_str, var)?
70 .into_coeffs()
71 .into_iter()
72 .map(Natural::try_from)
73 .collect::<Result<Vec<_>, _>>()?,
74 ))
75 }
76}
77
78impl PolynomialFromStr for Polynomial<Integer> {
79 type Err = ();
80
81 fn from_str(polynomial_str: &str, var: &str) -> Result<Self, ()> {
82 parse_integer_polynomial(polynomial_str, var).map_err(|_| ())
83 }
84}
85
86impl PolynomialFromStr for Polynomial<Rational> {
87 type Err = ();
88
89 fn from_str(polynomial_str: &str, var: &str) -> Result<Self, ()> {
90 parse_rational_polynomial(polynomial_str, var).map_err(|_| ())
91 }
92}
93
94#[derive(Debug, Clone)]
95pub struct PolynomialStructure<RS: Signature, RSB: BorrowedStructure<RS>> {
96 _coeff_ring: PhantomData<RS>,
97 coeff_ring: RSB,
98}
99
100impl<RS: Signature, RSB: BorrowedStructure<RS>> PolynomialStructure<RS, RSB> {
101 pub fn new(coeff_ring: RSB) -> Self {
102 Self {
103 _coeff_ring: PhantomData,
104 coeff_ring,
105 }
106 }
107
108 pub fn coeff_ring(&self) -> &RS {
109 self.coeff_ring.borrow()
110 }
111
112 pub fn into_coeff_ring(self) -> RSB {
113 self.coeff_ring
114 }
115}
116
117pub trait ToPolynomialSignature: Signature {
118 fn polynomials(&self) -> PolynomialStructure<Self, &Self> {
119 PolynomialStructure::new(self)
120 }
121
122 fn into_polynomials(self) -> PolynomialStructure<Self, Self> {
123 PolynomialStructure::new(self)
124 }
125}
126
127impl<RS: Signature> ToPolynomialSignature for RS {}
128
129impl<RS: Signature, RSB: BorrowedStructure<RS>> Signature for PolynomialStructure<RS, RSB> {}
130
131impl<RS: SetSignature, RSB: BorrowedStructure<RS>> SetSignature for PolynomialStructure<RS, RSB> {
132 type Set = Polynomial<RS::Set>;
133
134 fn is_element(&self, _x: &Self::Set) -> Result<(), String> {
135 Ok(())
136 }
137}
138
139impl<RS: Signature, RSB: BorrowedStructure<RS>> PartialEq for PolynomialStructure<RS, RSB> {
140 fn eq(&self, other: &Self) -> bool {
141 self.coeff_ring == other.coeff_ring
142 }
143}
144
145impl<RS: Signature, RSB: BorrowedStructure<RS>> Eq for PolynomialStructure<RS, RSB> {}
146
147impl<RS: SemiRingSignature + EqSignature + ToStringSignature, RSB: BorrowedStructure<RS>>
148 ToStringSignature for PolynomialStructure<RS, RSB>
149{
150 fn to_string(&self, elem: &Self::Set) -> String {
151 if self.num_coeffs(elem) == 0 {
152 "0".into()
153 } else {
154 let try_to_int = |c: &RS::Set| -> Option<Integer> {
155 if let Some(coeff_ring) = self.coeff_ring().try_char_zero_ring_restructure() {
156 coeff_ring.try_to_int(c)
157 } else {
158 None
159 }
160 };
161
162 let mut s = String::new();
163 for (idx, (power, coeff)) in elem.coeffs.iter().enumerate().rev().enumerate() {
164 let first = idx == 0;
165 let constant = power == 0;
166
167 if !self.coeff_ring().is_zero(coeff) {
168 if let Some(c) = try_to_int(coeff) {
169 if c > Integer::ZERO && !first {
170 s += "+";
171 }
172 if c == Integer::ONE && !constant {
173 } else if c == -Integer::ONE && !constant {
174 s += "-";
175 } else {
176 s += &c.to_string();
177 }
178 } else {
179 if !first {
180 s += "+";
181 }
182 s += "(";
183 s += &self.coeff_ring().to_string(coeff);
184 s += ")";
185 }
186 if power == 0 {
187 } else if power == 1 {
188 s += "λ";
189 } else {
190 s += "λ";
191 s += "^";
192 s += &power.to_string();
193 }
194 }
195 }
196 s
197 }
198 }
199}
200
201impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> EqSignature
202 for PolynomialStructure<RS, RSB>
203{
204 fn equal(&self, a: &Self::Set, b: &Self::Set) -> bool {
205 for i in 0..std::cmp::max(a.coeffs.len(), b.coeffs.len()) {
206 if !self
207 .coeff_ring()
208 .equal(self.coeff(a, i).as_ref(), self.coeff(b, i).as_ref())
209 {
210 return false;
211 }
212 }
213 true
214 }
215}
216
217impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> PolynomialStructure<RS, RSB> {
218 pub fn add_impl<'a, C: Borrow<RS::Set>>(
219 &self,
220 mut a: &'a Polynomial<C>,
221 mut b: &'a Polynomial<C>,
222 ) -> Polynomial<RS::Set> {
223 if a.coeffs.len() > b.coeffs.len() {
224 (a, b) = (b, a);
225 }
226 let x = a.coeffs.len();
227 let y = b.coeffs.len();
228
229 let mut coeffs = vec![];
230 let mut i = 0;
231 while i < x {
232 coeffs.push(
233 self.coeff_ring()
234 .add(a.coeffs[i].borrow(), b.coeffs[i].borrow()),
235 );
236 i += 1;
237 }
238 while i < y {
239 coeffs.push(b.coeffs[i].borrow().to_owned());
240 i += 1;
241 }
242 Polynomial::from_coeffs(coeffs)
243 }
244
245 pub fn mul_naive(
246 &self,
247 a: &Polynomial<impl Borrow<RS::Set>>,
248 b: &Polynomial<impl Borrow<RS::Set>>,
249 ) -> Polynomial<RS::Set> {
250 let mut coeffs = Vec::with_capacity(a.coeffs.len() + b.coeffs.len());
251 for _k in 0..a.coeffs.len() + b.coeffs.len() {
252 coeffs.push(self.coeff_ring().zero());
253 }
254 for i in 0..a.coeffs.len() {
255 for j in 0..b.coeffs.len() {
256 self.coeff_ring().add_mut(
257 &mut coeffs[i + j],
258 &self
259 .coeff_ring()
260 .mul(a.coeffs[i].borrow(), b.coeffs[j].borrow()),
261 );
262 }
263 }
264 self.reduce_poly(Polynomial::from_coeffs(coeffs))
265 }
266}
267
268impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> PolynomialStructure<RS, RSB> {
269 fn mul_karatsuba<'a, C: Borrow<RS::Set>>(
302 &self,
303 a: &'a Polynomial<C>,
304 b: &'a Polynomial<C>,
305 ) -> Polynomial<RS::Set> {
306 let n = std::cmp::max(a.coeffs.len(), b.coeffs.len());
307 if n <= 10 {
308 return self.mul_naive(a, b);
309 }
310
311 let k = n / 2;
312
313 let empty_slice: &[C] = &[];
314 let (a0, a1) = if k <= a.coeffs.len() {
315 a.coeffs.split_at(k)
316 } else {
317 (a.coeffs.as_slice(), empty_slice)
318 };
319 let (b0, b1) = if k <= b.coeffs.len() {
320 b.coeffs.split_at(k)
321 } else {
322 (b.coeffs.as_slice(), empty_slice)
323 };
324 let a0 = Polynomial::<&RS::Set> {
325 coeffs: a0.iter().map(|c| c.borrow()).collect(),
326 };
327 let a1 = Polynomial::<&RS::Set> {
328 coeffs: a1.iter().map(|c| c.borrow()).collect(),
329 };
330 let b0 = Polynomial::<&RS::Set> {
331 coeffs: b0.iter().map(|c| c.borrow()).collect(),
332 };
333 let b1 = Polynomial::<&RS::Set> {
334 coeffs: b1.iter().map(|c| c.borrow()).collect(),
335 };
336
337 debug_assert!(self.equal(
338 &a.apply_map(|c| c.borrow().clone()),
339 &self.add_impl(
340 &a0.clone().apply_map_into(|c| c.clone()),
341 &self.mul_var_pow(&a1.clone().apply_map_into(|c| c.clone()), k)
342 )
343 ));
344 debug_assert!(self.equal(
345 &b.apply_map(|c| c.borrow().clone()),
346 &self.add_impl(
347 &b0.clone().apply_map_into(|c| c.clone()),
348 &self.mul_var_pow(&b1.clone().apply_map_into(|c| c.clone()), k)
349 )
350 ));
351
352 let f0 = self.mul_karatsuba(&a0, &b0);
353 let f2 = self.mul_karatsuba(&a1, &b1);
354 let f3 = self.mul_karatsuba(&self.add_impl(&a0, &a1), &self.add_impl(&b0, &b1));
355 let f1 = self.sub(&f3, &self.add(&f2, &f0));
356
357 self.reduce_poly(self.add(
358 &self.add(&f0, &self.mul_var_pow(&f1, k)),
359 &self.mul_var_pow(&f2, 2 * k),
360 ))
361 }
362}
363
364impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> RinglikeSpecializationSignature
365 for PolynomialStructure<RS, RSB>
366{
367 fn try_ring_restructure(&self) -> Option<impl EqSignature<Set = Self::Set> + RingSignature> {
368 self.coeff_ring()
369 .try_ring_restructure()
370 .map(|coeff_ring| coeff_ring.into_polynomials())
371 }
372}
373
374impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> ZeroSignature
375 for PolynomialStructure<RS, RSB>
376{
377 fn zero(&self) -> Self::Set {
378 Polynomial { coeffs: vec![] }
379 }
380}
381
382impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> AdditionSignature
383 for PolynomialStructure<RS, RSB>
384{
385 fn add(&self, a: &Self::Set, b: &Self::Set) -> Self::Set {
386 self.add_impl(a, b)
387 }
388}
389
390impl<RS: SemiRingEqSignature + CancellativeAdditionSignature, RSB: BorrowedStructure<RS>>
391 CancellativeAdditionSignature for PolynomialStructure<RS, RSB>
392{
393 fn try_sub(&self, a: &Self::Set, b: &Self::Set) -> Option<Self::Set> {
394 Some(Polynomial::from_coeffs(
395 (0..std::cmp::max(a.coeffs.len(), b.coeffs.len()))
396 .map(|i| {
397 self.coeff_ring()
398 .try_sub(self.coeff(a, i).as_ref(), self.coeff(b, i).as_ref())
399 })
400 .collect::<Option<_>>()?,
401 ))
402 }
403}
404
405impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> TryNegateSignature
406 for PolynomialStructure<RS, RSB>
407{
408 fn try_neg(&self, a: &Self::Set) -> Option<Self::Set> {
409 Some(Polynomial::from_coeffs(
410 a.coeffs
411 .iter()
412 .map(|c| self.coeff_ring().try_neg(c))
413 .collect::<Option<_>>()?,
414 ))
415 }
416}
417
418impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> AdditiveMonoidSignature
419 for PolynomialStructure<RS, RSB>
420{
421}
422
423impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> AdditiveGroupSignature
424 for PolynomialStructure<RS, RSB>
425{
426 fn neg(&self, a: &Self::Set) -> Self::Set {
427 Polynomial::from_coeffs(a.coeffs.iter().map(|c| self.coeff_ring().neg(c)).collect())
428 }
429
430 fn sub(&self, a: &Self::Set, b: &Self::Set) -> Self::Set {
431 self.reduce_poly(Polynomial::from_coeffs(
432 (0..std::cmp::max(a.coeffs.len(), b.coeffs.len()))
433 .map(|i| {
434 self.coeff_ring()
435 .sub(self.coeff(a, i).as_ref(), self.coeff(b, i).as_ref())
436 })
437 .collect(),
438 ))
439 }
440}
441
442impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> OneSignature
443 for PolynomialStructure<RS, RSB>
444{
445 fn one(&self) -> Self::Set {
446 Polynomial::from_coeffs(vec![self.coeff_ring().one()])
447 }
448}
449
450impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> MultiplicationSignature
451 for PolynomialStructure<RS, RSB>
452{
453 fn mul(&self, a: &Self::Set, b: &Self::Set) -> Self::Set {
454 if let Some(coeff_ring) = self.coeff_ring().try_ring_restructure() {
455 coeff_ring.polynomials().mul_karatsuba(a, b)
456 } else {
457 self.mul_naive(a, b)
458 }
459 }
460}
461
462impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> CommutativeMultiplicationSignature
463 for PolynomialStructure<RS, RSB>
464{
465}
466
467impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> MultiplicativeMonoidSignature
468 for PolynomialStructure<RS, RSB>
469{
470}
471
472impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> MultiplicativeAbsorptionMonoidSignature
473 for PolynomialStructure<RS, RSB>
474{
475}
476
477impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> LeftDistributiveMultiplicationOverAddition
478 for PolynomialStructure<RS, RSB>
479{
480}
481
482impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>>
483 RightDistributiveMultiplicationOverAddition for PolynomialStructure<RS, RSB>
484{
485}
486
487impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> SemiRingSignature
488 for PolynomialStructure<RS, RSB>
489{
490}
491
492impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> SemiModuleSignature<RS>
493 for PolynomialStructure<RS, RSB>
494{
495 fn ring(&self) -> &RS {
496 self.coeff_ring()
497 }
498
499 fn scalar_mul(&self, p: &Self::Set, x: &RS::Set) -> Self::Set {
500 self.reduce_poly(Polynomial::from_coeffs(
501 p.coeffs
502 .iter()
503 .map(|c| self.coeff_ring().mul(c, x))
504 .collect(),
505 ))
506 }
507}
508
509impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> AlgebraSignature<RS>
510 for PolynomialStructure<RS, RSB>
511{
512}
513
514impl<RS: SemiRingEqSignature + CharacteristicSignature, RSB: BorrowedStructure<RS>>
515 CharacteristicSignature for PolynomialStructure<RS, RSB>
516{
517 fn characteristic(&self) -> Natural {
518 self.coeff_ring().characteristic()
519 }
520}
521
522impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> RingSignature
523 for PolynomialStructure<RS, RSB>
524{
525}
526
527impl<R: MetaType> Polynomial<R>
528where
529 R::Signature: SemiRingEqSignature,
530{
531 pub fn into_coeffs(self) -> Vec<R> {
532 Self::structure().into_coeffs(self)
533 }
534
535 pub fn coeffs(&self) -> impl Iterator<Item = &R> {
536 Self::structure().structure_into_coeffs(self)
537 }
538}
539
540impl<RS: SemiRingEqSignature, RSB: BorrowedStructure<RS>> PolynomialStructure<RS, RSB> {
541 fn structure_into_coeffs(self, a: &Polynomial<RS::Set>) -> impl Iterator<Item = &RS::Set> {
542 (0..self.num_coeffs(a)).map(|i| &a.coeffs[i])
543 }
544
545 pub fn coeffs<'a>(&self, a: &'a Polynomial<RS::Set>) -> impl Iterator<Item = &'a RS::Set> {
546 (0..self.num_coeffs(a)).map(|i| &a.coeffs[i])
547 }
548
549 pub fn into_coeffs(&self, a: Polynomial<RS::Set>) -> Vec<RS::Set> {
550 self.reduce_poly(a).coeffs
551 }
552
553 pub fn num_coeffs(&self, p: &Polynomial<RS::Set>) -> usize {
554 let k = match self.degree(p) {
555 Some(n) => n + 1,
556 None => 0,
557 };
558 debug_assert_eq!(k, self.into_coeffs(p.clone()).len());
559 k
560 }
561
562 pub fn reduce_poly(&self, mut a: Polynomial<RS::Set>) -> Polynomial<RS::Set> {
563 loop {
564 if a.coeffs.is_empty() {
565 break;
566 }
567 if self
568 .coeff_ring()
569 .equal(a.coeffs.last().unwrap(), &self.coeff_ring().zero())
570 {
571 a.coeffs.pop();
572 } else {
573 break;
574 }
575 }
576 a
577 }
578
579 pub fn var(&self) -> Polynomial<RS::Set> {
580 Polynomial::from_coeffs(vec![self.coeff_ring().zero(), self.coeff_ring().one()])
581 }
582
583 pub fn var_pow(&self, n: usize) -> Polynomial<RS::Set> {
584 Polynomial::from_coeffs(
585 (0..=n)
586 .map(|i| {
587 if i < n {
588 self.coeff_ring().zero()
589 } else {
590 self.coeff_ring().one()
591 }
592 })
593 .collect(),
594 )
595 }
596
597 pub fn constant_var_pow(&self, x: RS::Set, n: usize) -> Polynomial<RS::Set> {
598 Polynomial::from_coeffs(
599 (0..=n)
600 .map(|i| {
601 if i < n {
602 self.coeff_ring().zero()
603 } else {
604 x.clone()
605 }
606 })
607 .collect(),
608 )
609 }
610
611 pub fn coeff<'a>(&self, a: &'a Polynomial<RS::Set>, i: usize) -> Cow<'a, RS::Set> {
612 match a.coeffs.get(i) {
613 Some(c) => Cow::Borrowed(c),
614 None => Cow::Owned(self.coeff_ring().zero()),
615 }
616 }
617
618 pub fn leading_coeff<'a>(&self, a: &'a Polynomial<RS::Set>) -> Option<&'a RS::Set> {
619 Some(a.coeffs.get(self.degree(a)?).unwrap())
620 }
621
622 pub fn evaluate(&self, p: &Polynomial<RS::Set>, x: &RS::Set) -> RS::Set {
623 let mut y = self.coeff_ring().zero();
626 for c in p.coeffs.iter().rev() {
627 self.coeff_ring().mul_mut(&mut y, x);
628 self.coeff_ring().add_mut(&mut y, c);
629 }
630 y
631 }
632
633 pub fn evaluate_at_var_pow(&self, p: Polynomial<RS::Set>, k: usize) -> Polynomial<RS::Set> {
635 if k == 0 {
636 Polynomial::constant(self.coeff_ring().sum(p.coeffs))
637 } else {
638 let mut coeffs = vec![];
639 for (i, c) in p.coeffs.into_iter().enumerate() {
640 if i != 0 {
641 for _j in 0..(k - 1) {
642 coeffs.push(self.coeff_ring().zero());
643 }
644 }
645 coeffs.push(c);
646 }
647 Polynomial::from_coeffs(coeffs)
648 }
649 }
650
651 pub fn compose(&self, p: &Polynomial<RS::Set>, q: &Polynomial<RS::Set>) -> Polynomial<RS::Set> {
653 PolynomialStructure::new(self.clone())
654 .evaluate(&p.apply_map(|c| Polynomial::constant(c.clone())), q)
655 }
656
657 pub fn reversed(&self, p: &Polynomial<RS::Set>) -> Polynomial<RS::Set> {
660 Polynomial::from_coeffs(
661 self.reduce_poly(p.clone())
662 .coeffs
663 .into_iter()
664 .rev()
665 .collect(),
666 )
667 }
668
669 pub fn mul_var_pow(&self, p: &Polynomial<RS::Set>, n: usize) -> Polynomial<RS::Set> {
670 let mut coeffs = vec![];
671 for _i in 0..n {
672 coeffs.push(self.coeff_ring().zero());
673 }
674 for c in &p.coeffs {
675 coeffs.push(c.clone());
676 }
677 Polynomial { coeffs }
678 }
679
680 pub fn eval_var_pow(&self, p: &Polynomial<RS::Set>, n: usize) -> Polynomial<RS::Set> {
681 if n == 0 {
682 Polynomial::constant(self.coeff_ring().sum(p.coeffs.iter().collect()))
683 } else {
684 let gap = n - 1;
685 let mut coeffs = vec![];
686 for (i, coeff) in p.coeffs.iter().enumerate() {
687 if i != 0 {
688 for _ in 0..gap {
689 coeffs.push(self.coeff_ring().zero());
690 }
691 }
692 coeffs.push(coeff.clone());
693 }
694 Polynomial { coeffs }
695 }
696 }
697
698 pub fn mul_scalar(&self, p: &Polynomial<RS::Set>, x: &RS::Set) -> Polynomial<RS::Set> {
699 self.reduce_poly(Polynomial::from_coeffs(
700 p.coeffs
701 .iter()
702 .map(|c| self.coeff_ring().mul(c, x))
703 .collect(),
704 ))
705 }
706
707 pub fn degree(&self, p: &Polynomial<RS::Set>) -> Option<usize> {
713 #[allow(clippy::manual_find)]
715 for i in (0..p.coeffs.len()).rev() {
716 if !self.coeff_ring().is_zero(&p.coeffs[i]) {
717 return Some(i);
718 }
719 }
720 None
721 }
722
723 pub fn as_constant(&self, p: &Polynomial<RS::Set>) -> Option<RS::Set> {
724 if self.num_coeffs(p) == 0 {
725 Some(self.coeff_ring().zero())
726 } else if self.num_coeffs(p) == 1 {
727 Some(p.coeffs[0].clone())
728 } else {
729 None
730 }
731 }
732
733 pub fn is_monic(&self, p: &Polynomial<RS::Set>) -> bool {
734 match self.leading_coeff(p) {
735 Some(lc) => self.coeff_ring().equal(lc, &self.coeff_ring().one()),
736 None => false,
737 }
738 }
739
740 pub fn derivative(&self, mut p: Polynomial<RS::Set>) -> Polynomial<RS::Set> {
741 p = self.reduce_poly(p);
742 if self.num_coeffs(&p) > 0 {
743 for i in 0..self.num_coeffs(&p) - 1 {
744 p.coeffs[i] = p.coeffs[i + 1].clone();
745 self.coeff_ring().mul_mut(
746 &mut p.coeffs[i],
747 &self.coeff_ring().from_nat(Natural::from(i + 1)),
748 );
749 }
750 p.coeffs.pop();
751 }
752 p
753 }
754}
755
756impl<RS: IntegralDomainSignature, RSB: BorrowedStructure<RS>> PolynomialStructure<RS, RSB> {
757 pub fn try_quorem(
758 &self,
759 a: &Polynomial<RS::Set>,
760 b: &Polynomial<RS::Set>,
761 ) -> Option<(Polynomial<RS::Set>, Polynomial<RS::Set>)> {
762 let mut a = a.clone();
767
768 let m = self.num_coeffs(&a);
769 let n = self.num_coeffs(b);
770 if n == 0 {
771 None
772 } else if m < n {
773 Some((self.zero(), a))
774 } else {
775 let k = m - n + 1;
776 let mut q_coeffs = (0..k).map(|_i| self.coeff_ring().zero()).collect_vec();
777 for i in (0..k).rev() {
778 debug_assert!(!self.coeff_ring().is_zero(self.coeff(b, n - 1).as_ref()));
780 match self.coeff_ring().try_divide(
781 self.coeff(&a, i + n - 1).as_ref(),
782 self.coeff(b, n - 1).as_ref(),
783 ) {
784 Some(qc) => {
785 self.add_mut(
787 &mut a,
788 &self.neg(&self.mul_var_pow(&self.mul_scalar(b, &qc), i)),
789 );
790 q_coeffs[i] = qc;
791 }
792 None => {
793 return None;
794 }
795 }
796 }
797 Some((Polynomial::from_coeffs(q_coeffs), a))
798 }
799 }
800
801 pub fn div_impl(
802 &self,
803 a: &Polynomial<RS::Set>,
804 b: &Polynomial<RS::Set>,
805 ) -> Option<Polynomial<RS::Set>> {
806 match self.try_quorem(a, b) {
807 Some((q, r)) => {
808 debug_assert!(self.equal(&self.add(&self.mul(&q, b), &r), a));
809 if self.is_zero(&r) { Some(q) } else { None }
810 }
811 None => None,
812 }
813 }
814
815 pub fn pseudorem(
818 &self,
819 mut a: Polynomial<RS::Set>,
820 b: &Polynomial<RS::Set>,
821 ) -> Option<Result<Polynomial<RS::Set>, &'static str>> {
822 let m = self.num_coeffs(&a);
823 let n = self.num_coeffs(b);
824
825 if n == 0 {
826 None
827 } else if m < n {
828 Some(Err("Should have deg(a) >= deg(b) for pseudo remainder"))
829 } else {
830 self.mul_mut(
831 &mut a,
832 &Polynomial::constant(
833 self.coeff_ring()
834 .nat_pow(self.coeff(b, n - 1).as_ref(), &Natural::from(m - n + 1)),
835 ),
836 );
837
838 if let Some((_q, r)) = self.try_quorem(&a, b) {
839 Some(Ok(r))
840 } else {
841 panic!();
842 }
843 }
844 }
845
846 pub fn pseudo_remainder_subresultant_sequence(
850 &self,
851 mut a: Polynomial<RS::Set>,
852 mut b: Polynomial<RS::Set>,
853 ) -> (Vec<Polynomial<RS::Set>>, Vec<RS::Set>) {
854 match (self.degree(&a), self.degree(&b)) {
855 (None, None) => (vec![], vec![]),
856 (None, Some(_)) => (vec![b], vec![self.coeff_ring().one()]),
857 (Some(_), None) => (vec![a], vec![self.coeff_ring().one()]),
858 (Some(mut a_deg), Some(mut b_deg)) => {
859 if a_deg < b_deg {
860 (a, b, a_deg, b_deg) = (b, a, b_deg, a_deg);
861 }
862 debug_assert!(a_deg >= b_deg);
863 let mut prs = vec![a.clone(), b.clone()];
864 let mut diff_deg = a_deg - b_deg;
865 let mut beta = self.coeff_ring().nat_pow(
866 &self.coeff_ring().neg(&self.coeff_ring().one()),
867 &Natural::from(diff_deg + 1),
868 );
869 let mut r = self.mul_scalar(&self.pseudorem(a, &b).unwrap().unwrap(), &beta);
870 let mut lc_b = self.leading_coeff(&b).unwrap().clone();
871 let mut gamma = self.coeff_ring().nat_pow(&lc_b, &Natural::from(diff_deg));
872 let mut ssres = vec![self.coeff_ring().one(), gamma.clone()];
873 gamma = self.coeff_ring().neg(&gamma);
874 loop {
875 #[allow(clippy::single_match_else)]
876 match self.degree(&r) {
877 Some(r_deg) => {
878 prs.push(r.clone());
879 (a, b, b_deg, diff_deg) = (b, r, r_deg, b_deg - r_deg);
880 beta = self.coeff_ring().mul(
881 &self.coeff_ring().neg(&lc_b),
882 &self.coeff_ring().nat_pow(&gamma, &Natural::from(diff_deg)),
883 );
884 r = self
885 .try_divide(
886 &self.pseudorem(a, &b).unwrap().unwrap(),
887 &Polynomial::constant(beta),
888 )
889 .unwrap();
890 lc_b = self.leading_coeff(&b).unwrap().clone();
891 gamma = if diff_deg > 1 {
892 self.coeff_ring()
893 .try_divide(
894 &self.coeff_ring().nat_pow(
895 &self.coeff_ring().neg(&lc_b),
896 &Natural::from(diff_deg),
897 ),
898 &self
899 .coeff_ring()
900 .nat_pow(&gamma, &Natural::from(diff_deg - 1)),
901 )
902 .unwrap()
903 } else {
904 self.coeff_ring().neg(&lc_b)
905 };
906 ssres.push(self.coeff_ring().neg(&gamma));
907 }
908 None => {
909 debug_assert!(self.is_zero(&r));
910 break;
911 }
912 }
913 }
914 (prs, ssres)
915 }
916 }
917 }
918
919 pub fn subresultant_gcd(
921 &self,
922 a: Polynomial<RS::Set>,
923 b: Polynomial<RS::Set>,
924 ) -> Polynomial<RS::Set> {
925 let (mut prs, _) = self.pseudo_remainder_subresultant_sequence(a, b);
927 prs.pop().unwrap()
928 }
929
930 pub fn resultant(&self, a: Polynomial<RS::Set>, b: Polynomial<RS::Set>) -> RS::Set {
931 if self.is_zero(&a) || self.is_zero(&b) {
932 self.coeff_ring().zero()
933 } else {
934 let (mut prs, mut ssres) = self.pseudo_remainder_subresultant_sequence(a, b);
935 if self.degree(&prs.pop().unwrap()).unwrap() > 0 {
936 self.coeff_ring().zero()
937 } else {
938 ssres.pop().unwrap()
939 }
940 }
941 }
942
943 pub fn is_squarefree(&self, p: &Polynomial<RS::Set>) -> bool {
944 let dp = self.derivative(p.clone());
945 self.degree(&self.subresultant_gcd(p.clone(), dp)).unwrap() == 0
946 }
947
948 pub fn discriminant(&self, p: Polynomial<RS::Set>) -> Result<RS::Set, &'static str> {
949 match self.degree(&p) {
950 Some(n) => {
951 if n == 0 {
952 Err("Discriminant of a constant polynomial is undefined.")
953 } else {
954 let an = self.coeff(&p, n).as_ref().clone(); let dp = self.derivative(p.clone());
956 let disc = self
957 .coeff_ring()
958 .try_divide(&self.resultant(p, dp), &an)
959 .unwrap();
960 match n % 4 {
962 0 | 1 => Ok(disc),
963 2 | 3 => Ok(self.coeff_ring().neg(&disc)),
964 _ => unreachable!(),
965 }
966 }
967 }
968 None => Err("Discriminant of zero polynomial is undefined."),
969 }
970 }
971}
972
973impl<RS: IntegralDomainSignature, RSB: BorrowedStructure<RS>> TryReciprocalSignature
974 for PolynomialStructure<RS, RSB>
975{
976 fn try_reciprocal(&self, a: &Self::Set) -> Option<Self::Set> {
977 self.try_divide(&self.one(), a)
978 }
979}
980
981impl<RS: IntegralDomainSignature, RSB: BorrowedStructure<RS>> CancellativeMultiplicationSignature
982 for PolynomialStructure<RS, RSB>
983{
984 fn try_divide(&self, a: &Self::Set, b: &Self::Set) -> Option<Self::Set> {
985 self.div_impl(a, b)
986 }
987}
988
989impl<RS: IntegralDomainSignature, RSB: BorrowedStructure<RS>> MultiplicativeIntegralMonoidSignature
990 for PolynomialStructure<RS, RSB>
991{
992}
993
994impl<RS: IntegralDomainSignature, RSB: BorrowedStructure<RS>> IntegralDomainSignature
995 for PolynomialStructure<RS, RSB>
996{
997}
998
999impl<RS: UniqueFactorizationMonoidSignature + IntegralDomainSignature, RSB: BorrowedStructure<RS>>
1052 UniqueFactorizationMonoidSignature for PolynomialStructure<RS, RSB>
1053{
1054 type FactoredExponent = NaturalCanonicalStructure;
1055
1056 fn factorization_exponents(&self) -> &Self::FactoredExponent {
1057 Natural::structure_ref()
1058 }
1059
1060 fn into_factorization_exponents(self) -> Self::FactoredExponent {
1061 Natural::structure()
1062 }
1063
1064 fn try_is_irreducible(&self, _a: &Self::Set) -> Option<bool> {
1065 None
1066 }
1067
1068 fn factorization_pow(&self, a: &Self::Set, k: &Natural) -> Self::Set {
1069 self.nat_pow(a, k)
1070 }
1071}
1072
1073impl<RS: GreatestCommonDivisorSignature, RSB: BorrowedStructure<RS>> PolynomialStructure<RS, RSB> {
1074 pub fn factor_primitive(
1075 &self,
1076 mut p: Polynomial<RS::Set>,
1077 ) -> Option<(RS::Set, Polynomial<RS::Set>)> {
1078 if self.is_zero(&p) {
1079 None
1080 } else {
1081 let g = self.coeff_ring().gcd_list(p.coeffs.iter().collect());
1082 for i in 0..p.coeffs.len() {
1083 p.coeffs[i] = self.coeff_ring().try_divide(&p.coeffs[i], &g).unwrap();
1084 }
1085 Some((g, p))
1086 }
1087 }
1088
1089 pub fn is_primitive(&self, p: Polynomial<RS::Set>) -> bool {
1090 match self.factor_primitive(p) {
1091 Some((unit, _)) => self.coeff_ring().is_unit(&unit),
1092 None => false,
1093 }
1094 }
1095
1096 pub fn primitive_part(&self, p: Polynomial<RS::Set>) -> Option<Polynomial<RS::Set>> {
1097 self.factor_primitive(p).map(|(_unit, prim)| prim)
1098 }
1099
1100 pub fn gcd_by_primitive_subresultant(
1101 &self,
1102 a: Polynomial<RS::Set>,
1103 b: Polynomial<RS::Set>,
1104 ) -> Polynomial<RS::Set> {
1105 if self.is_zero(&a) {
1106 b
1107 } else if self.is_zero(&b) {
1108 a
1109 } else {
1110 let (a_content, a_prim) = self.factor_primitive(a).unwrap();
1111 let (b_content, b_prim) = self.factor_primitive(b).unwrap();
1112 let g_content = self.coeff_ring().gcd(&a_content, &b_content);
1113 let g_prim = self
1114 .factor_primitive(self.subresultant_gcd(a_prim, b_prim))
1115 .unwrap()
1116 .1;
1117 self.mul(&Polynomial::constant(g_content), &g_prim)
1118 }
1119 }
1120}
1121
1122impl<FS: FieldSignature, FSB: BorrowedStructure<FS>> GreatestCommonDivisorSignature
1123 for PolynomialStructure<FS, FSB>
1124{
1125 fn gcd(&self, x: &Self::Set, y: &Self::Set) -> Self::Set {
1126 self.euclidean_gcd(x.clone(), y.clone())
1127 }
1128}
1129
1130impl<FS: FieldSignature, FSB: BorrowedStructure<FS>> BezoutDomainSignature
1131 for PolynomialStructure<FS, FSB>
1132{
1133 fn xgcd(&self, x: &Self::Set, y: &Self::Set) -> (Self::Set, Self::Set, Self::Set) {
1134 self.euclidean_xgcd(x.clone(), y.clone())
1135 }
1136}
1137
1138impl<RS: GreatestCommonDivisorSignature + CharZeroRingSignature, RSB: BorrowedStructure<RS>>
1139 PolynomialStructure<RS, RSB>
1140{
1141 #[allow(clippy::let_and_return)]
1142 pub fn primitive_squarefree_part(&self, f: Polynomial<RS::Set>) -> Polynomial<RS::Set> {
1143 if self.is_zero(&f) {
1144 f
1145 } else {
1146 let g = self.subresultant_gcd(f.clone(), self.derivative(f.clone()));
1147 let (_c, g_prim) = self.factor_primitive(g).unwrap();
1148 let (_c, f_prim) = self.factor_primitive(f).unwrap();
1149 let f_prim_sqfree = self.try_divide(&f_prim, &g_prim).unwrap();
1150 f_prim_sqfree
1151 }
1152 }
1153}
1154
1155impl<RS: FavoriteAssociateSignature + IntegralDomainSignature, RSB: BorrowedStructure<RS>>
1156 FavoriteAssociateSignature for PolynomialStructure<RS, RSB>
1157{
1158 fn factor_fav_assoc(
1159 &self,
1160 a: &Polynomial<RS::Set>,
1161 ) -> (Polynomial<RS::Set>, Polynomial<RS::Set>) {
1162 if self.is_zero(a) {
1163 (self.one(), self.zero())
1164 } else {
1165 let mut a = a.clone();
1166 let (u, _c) = self
1167 .coeff_ring()
1168 .factor_fav_assoc(&a.coeffs[self.num_coeffs(&a) - 1]);
1169 for i in 0..a.coeffs.len() {
1170 a.coeffs[i] = self.coeff_ring().try_divide(&a.coeffs[i], &u).unwrap();
1171 }
1172 (Polynomial::constant(u), a.clone())
1173 }
1174 }
1175}
1176
1177impl<RS: CharZeroRingSignature + EqSignature, RSB: BorrowedStructure<RS>> CharZeroRingSignature
1178 for PolynomialStructure<RS, RSB>
1179{
1180 fn try_to_int(&self, x: &Self::Set) -> Option<Integer> {
1181 self.coeff_ring().try_to_int(&self.as_constant(x)?)
1182 }
1183}
1184
1185impl<
1186 RS: IntegralDomainSignature + TryReciprocalSignature,
1187 RSB: BorrowedStructure<RS>,
1188 B: BorrowedStructure<PolynomialStructure<RS, RSB>>,
1189> CountableSetSignature for MultiplicativeMonoidUnitsStructure<PolynomialStructure<RS, RSB>, B>
1190where
1191 for<'a> MultiplicativeMonoidUnitsStructure<RS, &'a RS>: FiniteSetSignature<Set = RS::Set>,
1192{
1193 fn generate_all_elements(&self) -> impl Iterator<Item = Self::Set> + Clone {
1194 self.list_all_elements().into_iter()
1195 }
1196}
1197
1198impl<
1199 RS: IntegralDomainSignature + TryReciprocalSignature,
1200 RSB: BorrowedStructure<RS>,
1201 B: BorrowedStructure<PolynomialStructure<RS, RSB>>,
1202> FiniteSetSignature for MultiplicativeMonoidUnitsStructure<PolynomialStructure<RS, RSB>, B>
1203where
1204 for<'a> MultiplicativeMonoidUnitsStructure<RS, &'a RS>: FiniteSetSignature<Set = RS::Set>,
1205{
1206 fn list_all_elements(&self) -> Vec<Self::Set> {
1207 self.monoid()
1208 .coeff_ring()
1209 .units()
1210 .list_all_elements()
1211 .into_iter()
1212 .map(Polynomial::constant)
1213 .collect()
1214 }
1215}
1216
1217impl<FS: FieldSignature, FSB: BorrowedStructure<FS>> EuclideanDivisionSignature
1222 for PolynomialStructure<FS, FSB>
1223{
1224 fn norm(&self, elem: &Self::Set) -> Option<Natural> {
1225 Some(Natural::from(self.degree(elem)?))
1226 }
1227
1228 fn quorem(&self, a: &Self::Set, b: &Self::Set) -> Option<(Self::Set, Self::Set)> {
1229 if self.is_zero(b) {
1230 None
1231 } else {
1232 Some(self.try_quorem(a, b).unwrap())
1233 }
1234 }
1235}
1236
1237impl<RS: IntegralDomainSignature, RSB: BorrowedStructure<RS>> PolynomialStructure<RS, RSB> {
1238 pub fn interpolate_by_lagrange_basis(
1239 &self,
1240 points: &[(RS::Set, RS::Set)],
1241 ) -> Option<Polynomial<RS::Set>> {
1242 let mut numerator = self.zero();
1260 for i in 0..points.len() {
1261 let (_xi, yi) = &points[i];
1262 let mut term = Polynomial::constant(yi.clone());
1263
1264 #[allow(clippy::needless_range_loop)]
1265 for j in i + 1..points.len() {
1266 let (xj, _yj) = &points[j];
1268 self.mul_mut(
1269 &mut term,
1270 &Polynomial::from_coeffs(vec![
1271 self.coeff_ring().neg(xj),
1272 self.coeff_ring().one(),
1273 ]),
1274 );
1275 }
1276 #[allow(clippy::needless_range_loop)]
1277 for j in 0..i {
1278 let (xj, _yj) = &points[j];
1280 self.mul_mut(
1281 &mut term,
1282 &Polynomial::from_coeffs(vec![
1283 xj.clone(),
1284 self.coeff_ring().neg(&self.coeff_ring().one()),
1285 ]),
1286 );
1287 }
1288
1289 for j in 0..points.len() {
1290 for k in j + 1..points.len() {
1291 if i != j && i != k {
1292 let (xj, _yj) = &points[j];
1293 let (xk, _yk) = &points[k];
1294 self.mul_mut(
1295 &mut term,
1296 &Polynomial::constant(
1297 self.coeff_ring().add(&self.coeff_ring().neg(xk), xj),
1298 ),
1299 );
1300 }
1301 }
1302 }
1303 self.add_mut(&mut numerator, &term);
1304 }
1305
1306 let mut denominator = self.one();
1307 for i in 0..points.len() {
1308 let (xi, _yi) = &points[i];
1309 #[allow(clippy::needless_range_loop)]
1310 for j in i + 1..points.len() {
1311 let (xj, _yj) = &points[j];
1312 self.mul_mut(
1313 &mut denominator,
1314 &Polynomial::constant(self.coeff_ring().add(&self.coeff_ring().neg(xj), xi)),
1315 );
1316 }
1317 }
1318
1319 if self.is_zero(&denominator) {
1320 panic!("are the input points distinct?");
1321 }
1322
1323 self.try_divide(&numerator, &denominator)
1324 }
1325}
1326
1327impl<RS: ReducedHermiteAlgorithmSignature, RSB: BorrowedStructure<RS>>
1328 PolynomialStructure<RS, RSB>
1329{
1330 pub fn interpolate_by_linear_system(
1331 &self,
1332 points: &[(RS::Set, RS::Set)],
1333 ) -> Option<Polynomial<RS::Set>> {
1334 let matrix_structure = MatrixStructure::new(self.coeff_ring().clone());
1346
1347 let n = points.len();
1348 let mut mat = matrix_structure.zero(n, n);
1349 #[allow(clippy::needless_range_loop)]
1350 for r in 0..n {
1351 let (x, _y) = &points[r];
1352 let mut x_pow = self.coeff_ring().one();
1353 for c in 0..n {
1354 *mat.at_mut(r, c).unwrap() = x_pow.clone();
1355 self.coeff_ring().mul_mut(&mut x_pow, x);
1356 }
1357 }
1358
1359 matrix_structure
1366 .col_solve(mat, &points.iter().map(|(_x, y)| y.clone()).collect())
1367 .map(Polynomial::from_coeffs)
1368 }
1369}
1370
1371pub fn factor_primitive_fof<
1372 Ring: GreatestCommonDivisorSignature,
1373 Field: FieldSignature,
1374 Fof: FieldOfFractionsInclusion<Ring, Field>,
1375>(
1376 fof_inclusion: &Fof,
1377 p: &Polynomial<Field::Set>,
1378) -> (Field::Set, Polynomial<Ring::Set>) {
1379 let ring = fof_inclusion.domain();
1380 let field = fof_inclusion.range();
1381 let poly_ring = PolynomialStructure::new(ring.clone());
1382
1383 let div = fof_inclusion.domain().lcm_list(
1384 p.coeffs
1385 .iter()
1386 .map(|c| fof_inclusion.denominator(c))
1387 .collect(),
1388 );
1389
1390 let (mul, prim) = poly_ring
1391 .factor_primitive(p.apply_map(|c| {
1392 fof_inclusion
1393 .try_preimage(&field.mul(&fof_inclusion.image(&div), c))
1394 .unwrap()
1395 }))
1396 .unwrap();
1397
1398 (
1399 field
1400 .try_divide(&fof_inclusion.image(&mul), &fof_inclusion.image(&div))
1401 .unwrap(),
1402 prim,
1403 )
1404}
1405
1406impl<Field: MetaType> Polynomial<Field>
1407where
1408 Field::Signature: FieldSignature,
1409 Polynomial<Field>:
1410 MetaType<Signature = PolynomialStructure<Field::Signature, Field::Signature>>,
1411 PrincipalIntegerMap<Field::Signature, Field::Signature>:
1412 FieldOfFractionsInclusion<IntegerCanonicalStructure, Field::Signature>,
1413{
1414 pub fn factor_primitive_fof(&self) -> (Field, Polynomial<Integer>) {
1415 factor_primitive_fof(
1416 &PrincipalIntegerMap::new(Self::structure().coeff_ring().clone()),
1417 self,
1418 )
1419 }
1420
1421 pub fn primitive_part_fof(&self) -> Polynomial<Integer> {
1422 self.factor_primitive_fof().1
1423 }
1424}
1425
1426impl<R: MetaType> MetaType for Polynomial<R> {
1427 type Signature = PolynomialStructure<R::Signature, R::Signature>;
1428
1429 fn structure() -> Self::Signature {
1430 PolynomialStructure::new(R::structure())
1431 }
1432}
1433
1434impl<R: MetaType> Polynomial<R>
1435where
1436 R::Signature: RingEqSignature<Set = R>,
1437{
1438 #[allow(unused)]
1439 fn reduce(self) -> Self {
1440 Self::structure().reduce_poly(self)
1441 }
1442}
1443
1444impl<R: MetaType> Display for Polynomial<R>
1445where
1446 R::Signature: SemiRingSignature + EqSignature + ToStringSignature,
1447{
1448 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1449 write!(f, "{}", Self::structure().to_string(self))
1450 }
1451}
1452
1453impl<R: MetaType> PartialEq for Polynomial<R>
1454where
1455 R::Signature: SemiRingEqSignature,
1456{
1457 fn eq(&self, other: &Self) -> bool {
1458 Self::structure().equal(self, other)
1459 }
1460}
1461
1462impl<R: MetaType> Eq for Polynomial<R> where R::Signature: SemiRingEqSignature {}
1463
1464impl<R: MetaType + Hash> Hash for Polynomial<R>
1465where
1466 R::Signature: SemiRingEqSignature,
1467{
1468 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1469 Self::structure()
1470 .reduce_poly(self.clone())
1471 .coeffs
1472 .hash(state);
1473 }
1474}
1475
1476impl<R: MetaType> Polynomial<R>
1477where
1478 R::Signature: SemiRingEqSignature<Set = R>,
1479{
1480 pub fn var() -> Self {
1481 Self::structure().var()
1482 }
1483
1484 pub fn var_pow(n: usize) -> Self {
1485 Self::structure().var_pow(n)
1486 }
1487
1488 pub fn coeff<'a>(&'a self, i: usize) -> Cow<'a, R> {
1489 Self::structure().coeff(self, i).clone()
1490 }
1491
1492 pub fn leading_coeff(&self) -> Option<R> {
1493 Self::structure().leading_coeff(self).cloned()
1494 }
1495
1496 pub fn mul_scalar(&self, x: &R) -> Self {
1497 Self::structure().mul_scalar(self, x)
1498 }
1499
1500 pub fn evaluate(&self, x: &R) -> R {
1501 Self::structure().evaluate(self, x)
1502 }
1503
1504 pub fn evaluate_at_var_pow(self, k: usize) -> Self {
1505 Self::structure().evaluate_at_var_pow(self, k)
1506 }
1507
1508 pub fn mul_var_pow(&self, n: usize) -> Self {
1509 Self::structure().mul_var_pow(self, n)
1510 }
1511
1512 pub fn eval_var_pow(&self, n: usize) -> Self {
1513 Self::structure().eval_var_pow(self, n)
1514 }
1515
1516 pub fn compose(p: &Self, q: &Self) -> Self {
1518 Self::structure().compose(p, q)
1519 }
1520
1521 pub fn num_coeffs(&self) -> usize {
1522 Self::structure().num_coeffs(self)
1523 }
1524
1525 pub fn reversed(&self) -> Self {
1528 Self::structure().reversed(self)
1529 }
1530
1531 pub fn degree(&self) -> Option<usize> {
1532 Self::structure().degree(self)
1533 }
1534
1535 pub fn as_constant(&self) -> Option<R> {
1536 Self::structure().as_constant(self)
1537 }
1538
1539 pub fn is_monic(&self) -> bool {
1540 Self::structure().is_monic(self)
1541 }
1542
1543 pub fn derivative(self) -> Self {
1544 Self::structure().derivative(self)
1545 }
1546}
1547
1548impl<R: MetaType> Polynomial<R>
1549where
1550 R::Signature: IntegralDomainSignature,
1551{
1552 pub fn try_quorem(a: &Self, b: &Self) -> Option<(Self, Self)> {
1553 Self::structure().try_quorem(a, b)
1554 }
1555
1556 pub fn pseudorem(a: &Self, b: &Self) -> Option<Result<Polynomial<R>, &'static str>> {
1557 Self::structure().pseudorem(a.clone(), b)
1558 }
1559
1560 pub fn pseudo_remainder_subresultant_sequence(a: Self, b: Self) -> (Vec<Self>, Vec<R>) {
1561 Self::structure().pseudo_remainder_subresultant_sequence(a, b)
1562 }
1563
1564 pub fn subresultant_gcd(a: &Self, b: &Self) -> Self {
1565 Self::structure().subresultant_gcd(a.clone(), b.clone())
1566 }
1567
1568 pub fn resultant(a: &Self, b: &Self) -> R {
1569 Self::structure().resultant(a.clone(), b.clone())
1570 }
1571
1572 pub fn is_squarefree(&self) -> bool {
1573 Self::structure().is_squarefree(self)
1574 }
1575
1576 pub fn discriminant(self) -> Result<R, &'static str> {
1577 Self::structure().discriminant(self)
1578 }
1579
1580 pub fn interpolate_by_lagrange_basis(points: &[(R, R)]) -> Option<Self> {
1581 Self::structure().interpolate_by_lagrange_basis(points)
1582 }
1583}
1584
1585impl<R: MetaType> Polynomial<R>
1586where
1587 R::Signature: ReducedHermiteAlgorithmSignature,
1588{
1589 pub fn interpolate_by_linear_system(points: &[(R, R)]) -> Option<Self> {
1590 Self::structure().interpolate_by_linear_system(points)
1591 }
1592}
1593
1594impl<R: MetaType> Polynomial<R>
1595where
1596 R::Signature: GreatestCommonDivisorSignature,
1597{
1598 pub fn factor_primitive(self) -> Option<(R, Polynomial<R>)> {
1599 Self::structure().factor_primitive(self)
1600 }
1601
1602 pub fn primitive_part(self) -> Option<Polynomial<R>> {
1603 Self::structure().primitive_part(self)
1604 }
1605
1606 pub fn gcd_by_primitive_subresultant(a: Polynomial<R>, b: Polynomial<R>) -> Polynomial<R> {
1607 Self::structure().gcd_by_primitive_subresultant(a, b)
1608 }
1609}
1610
1611impl<R: MetaType> Polynomial<R>
1612where
1613 R::Signature: GreatestCommonDivisorSignature + CharZeroRingSignature,
1614{
1615 pub fn primitive_squarefree_part(&self) -> Self {
1616 Self::structure().primitive_squarefree_part(self.clone())
1617 }
1618}
1619
1620#[allow(clippy::single_match, clippy::single_match_else, clippy::erasing_op)]
1621#[cfg(test)]
1622mod tests {
1623 use super::*;
1624 use crate::finite_fields::quaternary_field::*;
1625
1626 #[test]
1627 fn nat_poly_display() {
1628 let p = Polynomial::<Natural>::from_coeffs(vec![Natural::ONE, Natural::ONE, Natural::ONE]);
1629 println!("{}", p);
1630 }
1631
1632 #[test]
1633 fn test_constant_var_pow() {
1634 let ring = Polynomial::<Integer>::structure();
1635 let p = ring.constant_var_pow(Integer::from(2), 7);
1636 let q = ring.mul(&ring.from_int(Integer::from(2)), &ring.var_pow(7));
1637 assert_eq!(p, q);
1638 }
1639
1640 #[test]
1641 fn test_display_poly_over_display_canonical_ring() {
1642 let f = Polynomial {
1643 coeffs: vec![
1644 Integer::from(-2),
1645 Integer::from(1),
1646 Integer::from(2),
1647 Integer::from(4),
1648 ],
1649 };
1650
1651 println!("{}", f);
1653 println!("{}", f.into_ergonomic());
1654 }
1660
1661 #[test]
1662 fn invariant_reduction() {
1663 let unreduced = Polynomial::<Integer>::from_coeffs(vec![
1664 Integer::from(0),
1665 Integer::from(1),
1666 Integer::from(0),
1667 Integer::from(0),
1668 ]);
1669 let reduced = Polynomial::from_coeffs(vec![Integer::from(0), Integer::from(1)]);
1670 assert_eq!(unreduced, reduced);
1671
1672 let unreduced = Polynomial::from_coeffs(vec![
1673 Integer::from(0),
1674 Integer::from(0),
1675 Integer::from(0),
1676 Integer::from(0),
1677 ]);
1678 let reduced = Polynomial::<Integer>::zero();
1679 assert_eq!(unreduced, reduced);
1680 }
1681
1682 #[test]
1683 fn divisibility_over_integers() {
1684 let x = &Polynomial::<Integer>::var().into_ergonomic();
1685
1686 let a = (2 * x + 1) * (3 * x + 2) * (4 * x + 5) * (5 * x + 6) * (6 * x + 7);
1687 let b = (2 * x + 1) * (3 * x + 2) * (4 * x + 5);
1688 match Polynomial::try_divide(a.ref_set(), b.ref_set()) {
1689 Some(c) => {
1690 println!("{:?} {:?} {:?}", a, b, c);
1691 assert_eq!(a, b * c.into_ergonomic());
1692 }
1693 None => panic!(),
1694 }
1695
1696 let a = (2 * x + 1) * (3 * x + 2) * (4 * x + 5) * (5 * x + 6) * (6 * x + 7);
1697 let b = (2 * x + 1) * (3 * x + 2) * (4 * x + 5) + 1;
1698 match Polynomial::try_divide(a.ref_set(), b.ref_set()) {
1699 Some(_) => panic!(),
1700 None => {}
1701 }
1702
1703 let a = (2 * x + 1) * (3 * x + 2) * (4 * x + 5);
1704 let b = (2 * x + 1) * (3 * x + 2) * (4 * x + 5) * (5 * x + 6) * (6 * x + 7);
1705 match Polynomial::try_divide(a.ref_set(), b.ref_set()) {
1706 Some(_) => panic!(),
1707 None => {}
1708 }
1709
1710 let a = (2 * x + 1) * (3 * x + 2) * (4 * x + 5);
1711 let b = 0 * x;
1712 match Polynomial::try_divide(a.ref_set(), b.ref_set()) {
1713 Some(_) => panic!(),
1714 None => {}
1715 }
1716
1717 let a = 0 * x;
1718 let b = (x - x) + 5;
1719 match Polynomial::try_divide(a.ref_set(), b.ref_set()) {
1720 Some(c) => {
1721 assert_eq!(c, Polynomial::zero());
1722 }
1723 None => panic!(),
1724 }
1725
1726 let a = 3087 * x - 8805 * x.pow(2) + 607 * x.pow(3) + x.pow(4);
1727 let b = (x - x) + 1;
1728 match Polynomial::try_divide(a.ref_set(), b.ref_set()) {
1729 Some(c) => {
1730 assert_eq!(c.into_ergonomic(), a);
1731 }
1732 None => panic!(),
1733 }
1734 }
1735
1736 #[test]
1737 fn divisibility_over_f4() {
1738 let x = &Polynomial::<QuaternaryField>::var().into_ergonomic();
1739
1740 let a = 1 + x + x.pow(2);
1741 let b = Polynomial::constant(QuaternaryField::Alpha).into_ergonomic() + x;
1742 match Polynomial::try_divide(a.ref_set(), b.ref_set()) {
1743 Some(c) => {
1744 println!("{:?} {:?} {:?}", a, b, c);
1745 assert_eq!(a, b * c.into_ergonomic());
1746 }
1747 None => panic!(),
1748 }
1749 }
1750
1751 #[test]
1752 fn euclidean() {
1753 let x = &Polynomial::<Rational>::var().into_ergonomic();
1754
1755 let a = 1 + x + 3 * x.pow(2) + x.pow(3) + 7 * x.pow(4) + x.pow(5);
1756 let b = 1 + x + 3 * x.pow(2) + 2 * x.pow(3);
1757 let (q, r) = Polynomial::quorem(a.ref_set(), b.ref_set()).unwrap();
1758 let (q, r) = (q.into_ergonomic(), r.into_ergonomic());
1759 println!("{:?} = {:?} * {:?} + {:?}", a, b, q, r);
1760 assert_eq!(a, &b * &q + &r);
1761
1762 let a = 3 * x;
1763 let b = 2 * x;
1764 let (q, r) = Polynomial::quorem(a.ref_set(), b.ref_set()).unwrap();
1765 let (q, r) = (q.into_ergonomic(), r.into_ergonomic());
1766 println!("{:?} = {:?} * {:?} + {:?}", a, b, q, r);
1767 assert_eq!(a, &b * &q + &r);
1768
1769 let a = 3 * x + 5;
1770 let b = 2 * x + 1;
1771 let c = 1 + x + x.pow(2);
1772 let x = &a * &b;
1773 let y = &b * &c;
1774
1775 let g = Polynomial::gcd(x.ref_set(), y.ref_set());
1776
1777 println!("gcd({:?} , {:?}) = {:?}", x, y, g);
1778 Polynomial::try_divide(&g, b.ref_set()).unwrap();
1779 Polynomial::try_divide(b.ref_set(), &g).unwrap();
1780 }
1781
1782 #[test]
1783 fn test_pseudo_remainder() {
1784 let x = &Polynomial::<Integer>::var().into_ergonomic();
1785 {
1786 let f = (x.pow(8) + x.pow(6) - 3 * x.pow(4) - 3 * x.pow(3) + 8 * x.pow(2) + 2 * x - 5)
1787 .into_verbose();
1788 let g = (3 * x.pow(6) + 5 * x.pow(4) - 4 * x.pow(2) - 9 * x + 21).into_verbose();
1789
1790 println!("f = {}", f);
1791 println!("g = {}", g);
1792
1793 let r1 = Polynomial::pseudorem(&f, &g).unwrap().unwrap();
1794 println!("r1 = {}", r1);
1795 assert_eq!(
1796 r1.clone().into_ergonomic(),
1797 -15 * x.pow(4) + 3 * x.pow(2) - 9
1798 );
1799
1800 let r2 = Polynomial::pseudorem(&g, &r1).unwrap().unwrap();
1801 println!("r2 = {}", r2);
1802 assert_eq!(r2.into_ergonomic(), 15795 * x.pow(2) + 30375 * x - 59535);
1803 }
1804 println!();
1805 {
1806 let f = (4 * x.pow(3) + 2 * x - 7).into_verbose();
1807 let g = Polynomial::zero();
1808
1809 println!("f = {}", f);
1810 println!("g = {}", g);
1811
1812 if Polynomial::pseudorem(&f, &g).is_some() {
1813 panic!()
1814 }
1815 }
1816 println!();
1817 {
1818 let f = (3 * x.pow(6) + 5 * x.pow(4) - 4 * x.pow(2) - 9 * x + 21).into_verbose();
1819 let g = (x.pow(8) + x.pow(6) - 3 * x.pow(4) - 3 * x.pow(3) + 8 * x.pow(2) + 2 * x - 5)
1820 .into_verbose();
1821
1822 println!("f = {}", f);
1823 println!("g = {}", g);
1824
1825 if let Err(_msg) = Polynomial::pseudorem(&f, &g).unwrap() {
1826 } else {
1827 panic!();
1828 }
1829 }
1830 }
1831
1832 #[test]
1833 fn integer_primitive_and_assoc() {
1834 let x = &Polynomial::<Integer>::var().into_ergonomic();
1835 let p1 = (-2 - 4 * x.pow(2)).into_verbose();
1836 let (g, p2) = p1.factor_primitive().unwrap();
1837 assert_eq!(g, Integer::from(2));
1838 let (u, p3) = p2.factor_fav_assoc();
1839 assert_eq!(u.coeffs[0], Integer::from(-1));
1840 assert_eq!(p3.into_ergonomic(), 1 + 2 * x.pow(2));
1841 }
1842
1843 #[test]
1844 fn test_evaluate() {
1845 let x = &Polynomial::<Integer>::var().into_ergonomic();
1846 let f = (1 + x + 3 * x.pow(2) + x.pow(3) + 7 * x.pow(4) + x.pow(5)).into_verbose();
1847 assert_eq!(f.evaluate(&Integer::from(3)), Integer::from(868));
1848
1849 let f = Polynomial::zero();
1850 assert_eq!(f.evaluate(&Integer::from(3)), Integer::from(0));
1851 }
1852
1853 #[test]
1854 fn test_interpolate_by_lagrange_basis() {
1855 for points in [
1856 vec![
1857 (Rational::from(-2), Rational::from(-5)),
1858 (Rational::from(7), Rational::from(4)),
1859 (Rational::from(-1), Rational::from(-3)),
1860 (Rational::from(4), Rational::from(1)),
1861 ],
1862 vec![(Rational::from(0), Rational::from(0))],
1863 vec![(Rational::from(0), Rational::from(1))],
1864 vec![],
1865 vec![
1866 (Rational::from(0), Rational::from(0)),
1867 (Rational::from(1), Rational::from(1)),
1868 (Rational::from(2), Rational::from(2)),
1869 ],
1870 ] {
1871 let f = Polynomial::interpolate_by_lagrange_basis(&points).unwrap();
1872 for (inp, out) in &points {
1873 assert_eq!(&f.evaluate(inp), out);
1874 }
1875 }
1876
1877 if let Some(f) = Polynomial::interpolate_by_lagrange_basis(&[
1879 (Integer::from(0), Integer::from(0)),
1880 (Integer::from(1), Integer::from(2)),
1881 ]) {
1882 assert_eq!(
1883 f,
1884 Polynomial::from_coeffs(vec![Integer::from(0), Integer::from(2)])
1885 );
1886 } else {
1887 panic!();
1888 }
1889
1890 if let Some(_f) = Polynomial::interpolate_by_lagrange_basis(&[
1892 (Integer::from(0), Integer::from(0)),
1893 (Integer::from(2), Integer::from(1)),
1894 ]) {
1895 panic!();
1896 }
1897 }
1898
1899 #[test]
1900 fn test_interpolate_by_linear_system() {
1901 for points in [
1902 vec![
1903 (Rational::from(-2), Rational::from(-5)),
1904 (Rational::from(7), Rational::from(4)),
1905 (Rational::from(-1), Rational::from(-3)),
1906 (Rational::from(4), Rational::from(1)),
1907 ],
1908 vec![(Rational::from(0), Rational::from(0))],
1909 vec![(Rational::from(0), Rational::from(1))],
1910 vec![],
1911 vec![
1912 (Rational::from(0), Rational::from(0)),
1913 (Rational::from(1), Rational::from(1)),
1914 (Rational::from(2), Rational::from(2)),
1915 ],
1916 ] {
1917 let f = Polynomial::interpolate_by_linear_system(&points).unwrap();
1918 for (inp, out) in &points {
1919 assert_eq!(&f.evaluate(inp), out);
1920 }
1921 }
1922
1923 if let Some(f) = Polynomial::interpolate_by_linear_system(&[
1925 (Integer::from(0), Integer::from(0)),
1926 (Integer::from(1), Integer::from(2)),
1927 ]) {
1928 assert_eq!(
1929 f,
1930 Polynomial::from_coeffs(vec![Integer::from(0), Integer::from(2)])
1931 );
1932 } else {
1933 panic!();
1934 }
1935
1936 if let Some(_f) = Polynomial::interpolate_by_linear_system(&[
1938 (Integer::from(0), Integer::from(0)),
1939 (Integer::from(2), Integer::from(1)),
1940 ]) {
1941 panic!();
1942 }
1943 }
1944
1945 #[test]
1946 fn test_derivative() {
1947 let x = &Polynomial::<Integer>::var().into_ergonomic();
1948 let f = (2 + 3 * x - x.pow(2) + 7 * x.pow(3)).into_verbose();
1949 let g = (3 - 2 * x + 21 * x.pow(2)).into_verbose();
1950 assert_eq!(f.derivative(), g);
1951
1952 let f = Polynomial::<Integer>::zero();
1953 let g = Polynomial::<Integer>::zero();
1954 assert_eq!(f.derivative(), g);
1955
1956 let f = Polynomial::<Integer>::one();
1957 let g = Polynomial::<Integer>::zero();
1958 assert_eq!(f.derivative(), g);
1959 }
1960
1961 #[test]
1962 fn test_monic() {
1963 assert!(!Polynomial::<Integer>::zero().is_monic());
1964 assert!(Polynomial::<Integer>::one().is_monic());
1965 let x = &Polynomial::<Integer>::var().into_ergonomic();
1966 let f = (2 + 3 * x - x.pow(2) + 7 * x.pow(3) + x.pow(4)).into_verbose();
1967 let g = (3 - 2 * x + 21 * x.pow(2)).into_verbose();
1968 assert!(f.is_monic());
1969 assert!(!g.is_monic());
1970 }
1971
1972 #[test]
1973 fn test_nat_var() {
1974 let x = Polynomial::<Natural>::var();
1975 println!("{}", x);
1976 }
1977
1978 #[test]
1979 fn test_eval_var_pow() {
1980 let p = Polynomial::<Integer>::from_coeffs(vec![1, 2, 3]);
1981 assert_eq!(p.eval_var_pow(0), Polynomial::from_coeffs(vec![6]));
1982 assert_eq!(p.eval_var_pow(1), Polynomial::from_coeffs(vec![1, 2, 3]));
1983 assert_eq!(
1984 p.eval_var_pow(2),
1985 Polynomial::from_coeffs(vec![1, 0, 2, 0, 3])
1986 );
1987 assert_eq!(
1988 p.eval_var_pow(3),
1989 Polynomial::from_coeffs(vec![1, 0, 0, 2, 0, 0, 3])
1990 );
1991
1992 let p = Polynomial::<Integer>::from_coeffs(vec![4]);
1993 assert_eq!(p.eval_var_pow(0), Polynomial::from_coeffs(vec![4]));
1994 assert_eq!(p.eval_var_pow(1), Polynomial::from_coeffs(vec![4]));
1995 assert_eq!(p.eval_var_pow(2), Polynomial::from_coeffs(vec![4]));
1996
1997 let p = Polynomial::<Integer>::zero();
1998 assert_eq!(p.eval_var_pow(0), Polynomial::zero());
1999 assert_eq!(p.eval_var_pow(1), Polynomial::zero());
2000 assert_eq!(p.eval_var_pow(2), Polynomial::zero());
2001 }
2002
2003 #[test]
2004 fn test_subresultant_gcd() {
2005 let x = &Polynomial::<Integer>::var().into_ergonomic();
2006
2007 let f = (x.pow(8) + x.pow(6) - 3 * x.pow(4) - 3 * x.pow(3) + 8 * x.pow(2) + 2 * x - 5)
2008 .into_verbose();
2009 let g = (3 * x.pow(6) + 5 * x.pow(4) - 4 * x.pow(2) - 9 * x + 21).into_verbose();
2010 assert_eq!(
2011 Polynomial::subresultant_gcd(&f, &g),
2012 Polynomial::constant(Integer::from(260_708))
2013 );
2014
2015 let f = (3 * x.pow(6) + 5 * x.pow(4) - 4 * x.pow(2) - 9 * x + 21).into_verbose();
2016 let g = (x.pow(8) + x.pow(6) - 3 * x.pow(4) - 3 * x.pow(3) + 8 * x.pow(2) + 2 * x - 5)
2017 .into_verbose();
2018 assert_eq!(
2019 Polynomial::subresultant_gcd(&f, &g),
2020 Polynomial::constant(Integer::from(260_708))
2021 );
2022
2023 let f = ((x + 2).pow(2) * (2 * x - 3).pow(2)).into_verbose();
2024 let g = ((3 * x - 1) * (2 * x - 3).pow(2)).into_verbose();
2025 assert_eq!(
2026 Polynomial::subresultant_gcd(&f, &g).into_ergonomic(),
2027 7056 - 9408 * x + 3136 * x.pow(2)
2028 );
2029
2030 let f = (x.pow(4) + 1).into_verbose();
2031 let g = (3 * x.pow(2)).into_verbose();
2032 println!(
2033 "{:#?}",
2034 Polynomial::pseudo_remainder_subresultant_sequence(f.clone(), g.clone())
2035 );
2036 println!("{:#?}", Polynomial::resultant(&f, &g));
2037 }
2038
2039 #[test]
2048 fn test_discriminant() {
2049 let x = &Polynomial::<Integer>::var().into_ergonomic();
2050 debug_assert!((0 * x.pow(0)).into_verbose().discriminant().is_err());
2052 debug_assert!((1 * x.pow(0)).into_verbose().discriminant().is_err());
2053 debug_assert!((2 * x.pow(0)).into_verbose().discriminant().is_err());
2054
2055 debug_assert_eq!(
2057 (3 * x.pow(1) + 2).into_verbose().discriminant().unwrap(),
2058 Integer::from(1)
2059 );
2060
2061 debug_assert_eq!(
2063 (x.pow(2) + 1).into_verbose().discriminant().unwrap(),
2064 Integer::from(-4)
2065 );
2066 debug_assert_eq!(
2067 (3 * x.pow(2) + 1).into_verbose().discriminant().unwrap(),
2068 Integer::from(-12)
2069 );
2070 debug_assert_eq!(
2071 (x.pow(2) + 3).into_verbose().discriminant().unwrap(),
2072 Integer::from(-12)
2073 );
2074 debug_assert_eq!(
2075 (3 * x.pow(2) + 3).into_verbose().discriminant().unwrap(),
2076 Integer::from(-36)
2077 );
2078 debug_assert_eq!(
2079 (x.pow(2) + x.pow(1) + 1)
2080 .into_verbose()
2081 .discriminant()
2082 .unwrap(),
2083 Integer::from(1 - 4)
2084 );
2085 debug_assert_eq!(
2086 (x.pow(2) + 2 * x.pow(1) + 1)
2087 .into_verbose()
2088 .discriminant()
2089 .unwrap(),
2090 Integer::from(4 - 4)
2091 );
2092 debug_assert_eq!(
2093 (x.pow(2) + 3 * x.pow(1) + 1)
2094 .into_verbose()
2095 .discriminant()
2096 .unwrap(),
2097 Integer::from(9 - 4)
2098 );
2099
2100 for (a, b, c, d) in [
2102 (1, 0, 0, 0),
2103 (1, 0, 1, 0),
2104 (1, 0, 0, 1),
2105 (1, 1, 1, 1),
2106 (3, 1, 1, 1),
2107 (3, 3, 3, 3),
2108 (2, 3, 5, 7),
2109 (7, 5, 3, 2),
2110 ] {
2111 println!(
2112 "{}",
2113 (a * x.pow(3) + b * x.pow(2) + c * x.pow(1) + d).into_verbose()
2114 );
2115 println!(
2116 "{:?}",
2117 (a * x.pow(3) + b * x.pow(2) + c * x.pow(1) + d)
2118 .into_verbose()
2119 .discriminant()
2120 );
2121 debug_assert_eq!(
2122 (a * x.pow(3) + b * x.pow(2) + c * x.pow(1) + d)
2123 .into_verbose()
2124 .discriminant()
2125 .unwrap(),
2126 Integer::from(
2127 b * b * c * c - 4 * a * c * c * c - 4 * b * b * b * d - 27 * a * a * d * d
2128 + 18 * a * b * c * d
2129 )
2130 );
2131 }
2132 }
2133
2134 #[test]
2135 fn test_factor_primitive_fof() {
2136 for (f, exp) in [
2137 (
2138 Polynomial::from_coeffs(vec![
2139 Rational::from_integers(1, 2),
2140 Rational::from_integers(1, 3),
2141 ]),
2142 Polynomial::from_coeffs(vec![Integer::from(3), Integer::from(2)]),
2143 ),
2144 (
2145 Polynomial::from_coeffs(vec![
2146 Rational::from_integers(4, 1),
2147 Rational::from_integers(6, 1),
2148 ]),
2149 Polynomial::from_coeffs(vec![Integer::from(2), Integer::from(3)]),
2150 ),
2151 ] {
2152 let (mul, ans) = f.factor_primitive_fof();
2153 assert!(Polynomial::are_associate(&ans, &exp));
2154 assert_eq!(
2155 Polynomial::mul(
2156 &ans.apply_map(|c| Rational::from(c)),
2157 &Polynomial::constant(mul)
2158 ),
2159 f
2160 );
2161 }
2162 }
2163}