1use crate::numeric::{BigInt, Rational};
21use std::cmp::Ordering;
22use std::fmt;
23
24#[derive(Clone, PartialEq, Eq, Hash, Debug)]
26pub struct Poly {
27 c: Vec<Rational>,
28}
29
30#[derive(Clone, PartialEq, Eq, Debug)]
34pub struct RootInterval {
35 pub lo: Rational,
36 pub hi: Rational,
37}
38
39fn trimmed(mut c: Vec<Rational>) -> Vec<Rational> {
40 while c.last().is_some_and(Rational::is_zero) {
41 c.pop();
42 }
43 c
44}
45
46fn sign(r: &Rational) -> i32 {
47 if r.is_zero() {
48 0
49 } else if r.is_negative() {
50 -1
51 } else {
52 1
53 }
54}
55
56fn half_of_sum(a: &Rational, b: &Rational) -> Rational {
57 a.add(b).mul(&Rational::from_ratio_i64(1, 2).expect("2 is nonzero"))
58}
59
60impl Poly {
61 pub fn zero() -> Poly {
62 Poly { c: Vec::new() }
63 }
64
65 pub fn one() -> Poly {
66 Poly {
67 c: vec![Rational::one()],
68 }
69 }
70
71 pub fn x() -> Poly {
72 Poly {
73 c: vec![Rational::zero(), Rational::one()],
74 }
75 }
76
77 pub fn constant(c: Rational) -> Poly {
78 Poly { c: trimmed(vec![c]) }
79 }
80
81 pub fn from_coeffs(c: Vec<Rational>) -> Poly {
83 Poly { c: trimmed(c) }
84 }
85
86 pub fn from_i64s(c: &[i64]) -> Poly {
87 Self::from_coeffs(c.iter().map(|&x| Rational::from_i64(x)).collect())
88 }
89
90 pub fn from_bigints(c: &[BigInt]) -> Poly {
91 Self::from_coeffs(c.iter().cloned().map(Rational::from_bigint).collect())
92 }
93
94 pub fn coeffs(&self) -> &[Rational] {
95 &self.c
96 }
97
98 pub fn coeff(&self, i: usize) -> Rational {
100 self.c.get(i).cloned().unwrap_or_else(Rational::zero)
101 }
102
103 pub fn degree(&self) -> Option<usize> {
105 self.c.len().checked_sub(1)
106 }
107
108 pub fn is_zero(&self) -> bool {
109 self.c.is_empty()
110 }
111
112 pub fn leading(&self) -> Option<&Rational> {
113 self.c.last()
114 }
115
116 pub fn add(&self, o: &Poly) -> Poly {
117 let n = self.c.len().max(o.c.len());
118 Self::from_coeffs((0..n).map(|i| self.coeff(i).add(&o.coeff(i))).collect())
119 }
120
121 pub fn sub(&self, o: &Poly) -> Poly {
122 let n = self.c.len().max(o.c.len());
123 Self::from_coeffs((0..n).map(|i| self.coeff(i).sub(&o.coeff(i))).collect())
124 }
125
126 pub fn neg(&self) -> Poly {
127 Poly {
128 c: self.c.iter().map(Rational::negated).collect(),
129 }
130 }
131
132 pub fn mul(&self, o: &Poly) -> Poly {
133 if self.is_zero() || o.is_zero() {
134 return Poly::zero();
135 }
136 let mut out = vec![Rational::zero(); self.c.len() + o.c.len() - 1];
137 for (i, a) in self.c.iter().enumerate() {
138 if a.is_zero() {
139 continue;
140 }
141 for (j, b) in o.c.iter().enumerate() {
142 if b.is_zero() {
143 continue;
144 }
145 out[i + j] = out[i + j].add(&a.mul(b));
146 }
147 }
148 Self::from_coeffs(out)
149 }
150
151 pub fn scale(&self, k: &Rational) -> Poly {
152 Self::from_coeffs(self.c.iter().map(|a| a.mul(k)).collect())
153 }
154
155 pub fn pow(&self, mut e: u32) -> Poly {
156 let mut base = self.clone();
157 let mut acc = Poly::one();
158 while e > 0 {
159 if e & 1 == 1 {
160 acc = acc.mul(&base);
161 }
162 e >>= 1;
163 if e > 0 {
164 base = base.mul(&base);
165 }
166 }
167 acc
168 }
169
170 pub fn div_rem(&self, d: &Poly) -> Option<(Poly, Poly)> {
172 let dd = d.degree()?;
173 let lc_inv = d.c[dd].recip()?;
174 if self.c.len() <= dd {
175 return Some((Poly::zero(), self.clone()));
176 }
177 let mut r = self.c.clone();
178 let mut q = vec![Rational::zero(); r.len() - dd];
179 for k in (0..q.len()).rev() {
180 let coef = r[k + dd].mul(&lc_inv);
181 if !coef.is_zero() {
182 for (j, dj) in d.c.iter().enumerate() {
183 r[k + j] = r[k + j].sub(&coef.mul(dj));
184 }
185 }
186 q[k] = coef;
187 }
188 r.truncate(dd);
189 Some((Self::from_coeffs(q), Self::from_coeffs(r)))
190 }
191
192 pub fn gcd(&self, o: &Poly) -> Poly {
194 let (mut a, mut b) = (self.monic(), o.monic());
195 while !b.is_zero() {
196 let r = a.div_rem(&b).expect("b is nonzero").1;
197 a = b;
198 b = r.monic();
199 }
200 a
201 }
202
203 pub fn monic(&self) -> Poly {
205 match self.leading() {
206 None => Poly::zero(),
207 Some(lc) => self.scale(&lc.recip().expect("the leading coefficient is nonzero")),
208 }
209 }
210
211 pub fn derivative(&self) -> Poly {
212 Self::from_coeffs(
213 self.c
214 .iter()
215 .enumerate()
216 .skip(1)
217 .map(|(i, a)| a.mul(&Rational::from_i64(i as i64)))
218 .collect(),
219 )
220 }
221
222 pub fn eval(&self, x: &Rational) -> Rational {
224 self.c
225 .iter()
226 .rev()
227 .fold(Rational::zero(), |acc, a| acc.mul(x).add(a))
228 }
229
230 pub fn compose(&self, inner: &Poly) -> Poly {
232 self.c.iter().rev().fold(Poly::zero(), |acc, a| {
233 acc.mul(inner).add(&Poly::constant(a.clone()))
234 })
235 }
236
237 pub fn reverse(&self) -> Poly {
239 let mut c = self.c.clone();
240 c.reverse();
241 Self::from_coeffs(c)
242 }
243
244 pub fn neg_var(&self) -> Poly {
246 Poly {
247 c: self
248 .c
249 .iter()
250 .enumerate()
251 .map(|(i, a)| if i % 2 == 1 { a.negated() } else { a.clone() })
252 .collect(),
253 }
254 }
255
256 pub fn squarefree_part(&self) -> Poly {
258 if self.is_zero() {
259 return Poly::zero();
260 }
261 let g = self.gcd(&self.derivative());
262 self.div_rem(&g).expect("the gcd of a nonzero polynomial is nonzero").0.monic()
263 }
264
265 pub fn primitive_int(&self) -> Vec<BigInt> {
268 if self.is_zero() {
269 return Vec::new();
270 }
271 let den = self
272 .c
273 .iter()
274 .fold(BigInt::from_i64(1), |l, r| lcm(&l, r.denominator()));
275 let ints: Vec<BigInt> = self
276 .c
277 .iter()
278 .map(|r| {
279 let scale = den.div_rem(r.denominator()).expect("denominators are nonzero").0;
280 r.numerator().mul(&scale)
281 })
282 .collect();
283 z_primitive(&ints)
284 }
285
286 pub fn root_bound(&self) -> Rational {
288 let Some(n) = self.degree() else {
289 return Rational::one();
290 };
291 let lc = self.c[n].abs();
292 let max = self.c[..n]
293 .iter()
294 .map(|a| a.abs().div(&lc).expect("the leading coefficient is nonzero"))
295 .max()
296 .unwrap_or_else(Rational::zero);
297 Rational::one().add(&max)
298 }
299
300 pub fn count_real_roots(&self) -> usize {
302 match self.degree() {
303 None | Some(0) => 0,
304 Some(_) => {
305 let chain = self.squarefree_part().sturm_chain();
306 variations_at_neg_inf(&chain) - variations_at_pos_inf(&chain)
307 }
308 }
309 }
310
311 pub fn count_real_roots_in(&self, lo: &Rational, hi: &Rational) -> usize {
313 match self.degree() {
314 None | Some(0) => 0,
315 Some(_) if lo >= hi => 0,
316 Some(_) => {
317 let chain = self.squarefree_part().sturm_chain();
318 variations_at(&chain, lo) - variations_at(&chain, hi)
319 }
320 }
321 }
322
323 pub fn isolate_real_roots(&self) -> Vec<RootInterval> {
325 match self.degree() {
326 None | Some(0) => Vec::new(),
327 Some(_) => {
328 let s = self.squarefree_part();
329 let chain = s.sturm_chain();
330 let hi = s.root_bound();
331 let lo = hi.negated();
332 let n = variations_at(&chain, &lo) - variations_at(&chain, &hi);
333 let mut out = Vec::new();
334 isolate(&s, &chain, lo, hi, n, &mut out);
335 out
336 }
337 }
338 }
339
340 pub fn refine(&self, iv: &RootInterval) -> RootInterval {
343 if iv.lo == iv.hi {
344 return iv.clone();
345 }
346 let s = self.squarefree_part();
347 let mid = half_of_sum(&iv.lo, &iv.hi);
348 let at_mid = sign(&s.eval(&mid));
349 if at_mid == 0 {
350 RootInterval {
351 lo: mid.clone(),
352 hi: mid,
353 }
354 } else if at_mid == sign(&s.eval(&iv.lo)) {
355 RootInterval {
356 lo: mid,
357 hi: iv.hi.clone(),
358 }
359 } else {
360 RootInterval {
361 lo: iv.lo.clone(),
362 hi: mid,
363 }
364 }
365 }
366
367 pub fn resultant(&self, o: &Poly) -> Rational {
370 let (Some(m), Some(n)) = (self.degree(), o.degree()) else {
371 return Rational::zero();
372 };
373 if n == 0 {
374 return o.c[0].pow(m as i32).expect("a nonzero constant has every power");
375 }
376 if m == 0 {
377 return self.c[0].pow(n as i32).expect("a nonzero constant has every power");
378 }
379 let r = self.div_rem(o).expect("o is nonzero").1;
380 let Some(dr) = r.degree() else {
381 return Rational::zero();
382 };
383 let lc = o.c[n]
384 .pow((m - dr) as i32)
385 .expect("a nonzero leading coefficient has every power");
386 let v = lc.mul(&o.resultant(&r));
387 if (m * n) % 2 == 1 {
388 v.negated()
389 } else {
390 v
391 }
392 }
393
394 pub fn factor_over_q(&self) -> Vec<(Poly, u32)> {
398 match self.degree() {
399 None | Some(0) => return Vec::new(),
400 Some(_) => {}
401 }
402 let mut out = Vec::new();
403 for (part, mult) in self.squarefree_decomposition() {
404 for g in factor_squarefree_primitive(part.primitive_int()) {
405 out.push((Poly::from_bigints(&g), mult));
406 }
407 }
408 out.sort_by(|a, b| Poly::canonical_order(&a.0, &b.0));
409 out
410 }
411
412 pub fn is_irreducible(&self) -> bool {
414 if self.degree().unwrap_or(0) == 0 {
415 return false;
416 }
417 let fs = self.factor_over_q();
418 fs.len() == 1 && fs[0].1 == 1
419 }
420
421 pub fn canonical_order(a: &Poly, b: &Poly) -> Ordering {
424 a.degree().cmp(&b.degree()).then_with(|| {
425 for i in (0..a.c.len()).rev() {
426 match a.c[i].cmp(&b.c[i]) {
427 Ordering::Equal => continue,
428 other => return other,
429 }
430 }
431 Ordering::Equal
432 })
433 }
434
435 fn squarefree_decomposition(&self) -> Vec<(Poly, u32)> {
438 let f = self.monic();
439 let fp = f.derivative();
440 let a0 = f.gcd(&fp);
441 let mut b = f.div_rem(&a0).expect("gcd is nonzero").0;
442 let c = fp.div_rem(&a0).expect("gcd is nonzero").0;
443 let mut d = c.sub(&b.derivative());
444 let mut i = 1u32;
445 let mut out = Vec::new();
446 while b.degree().unwrap_or(0) > 0 {
447 let a = b.gcd(&d);
448 if a.degree().unwrap_or(0) > 0 {
449 out.push((a.clone(), i));
450 }
451 b = b.div_rem(&a).expect("gcd is nonzero").0;
452 let c_next = d.div_rem(&a).expect("gcd is nonzero").0;
453 d = c_next.sub(&b.derivative());
454 i += 1;
455 }
456 out
457 }
458
459 fn sturm_chain(&self) -> Vec<Poly> {
462 let mut chain = vec![self.clone()];
463 let d = self.derivative();
464 if d.is_zero() {
465 return chain;
466 }
467 chain.push(d);
468 loop {
469 let n = chain.len();
470 let r = chain[n - 2].div_rem(&chain[n - 1]).expect("chain members are nonzero").1;
471 if r.is_zero() {
472 return chain;
473 }
474 let lc = r.leading().expect("r is nonzero").abs();
475 chain.push(r.neg().scale(&lc.recip().expect("lc is nonzero")));
476 }
477 }
478}
479
480fn sign_changes(signs: impl Iterator<Item = i32>) -> usize {
481 let mut prev = 0;
482 let mut n = 0;
483 for s in signs {
484 if s == 0 {
485 continue;
486 }
487 if prev != 0 && s != prev {
488 n += 1;
489 }
490 prev = s;
491 }
492 n
493}
494
495fn variations_at(chain: &[Poly], x: &Rational) -> usize {
496 sign_changes(chain.iter().map(|p| sign(&p.eval(x))))
497}
498
499fn variations_at_pos_inf(chain: &[Poly]) -> usize {
500 sign_changes(chain.iter().map(|p| sign(p.leading().expect("chain members are nonzero"))))
501}
502
503fn variations_at_neg_inf(chain: &[Poly]) -> usize {
504 sign_changes(chain.iter().map(|p| {
505 let s = sign(p.leading().expect("chain members are nonzero"));
506 if p.degree().unwrap_or(0) % 2 == 1 {
507 -s
508 } else {
509 s
510 }
511 }))
512}
513
514fn isolate(
518 s: &Poly,
519 chain: &[Poly],
520 lo: Rational,
521 hi: Rational,
522 n: usize,
523 out: &mut Vec<RootInterval>,
524) {
525 if n == 0 {
526 return;
527 }
528 if n == 1 {
529 out.push(RootInterval { lo, hi });
530 return;
531 }
532 let mid = half_of_sum(&lo, &hi);
533 if !s.eval(&mid).is_zero() {
534 let left = variations_at(chain, &lo) - variations_at(chain, &mid);
535 isolate(s, chain, lo, mid.clone(), left, out);
536 isolate(s, chain, mid, hi, n - left, out);
537 return;
538 }
539 let quarter = Rational::from_ratio_i64(1, 4).expect("4 is nonzero");
540 let half = Rational::from_ratio_i64(1, 2).expect("2 is nonzero");
541 let mut delta = hi.sub(&lo).mul(&quarter);
542 let (a, b) = loop {
543 let a = mid.sub(&delta);
544 let b = mid.add(&delta);
545 if !s.eval(&a).is_zero()
546 && !s.eval(&b).is_zero()
547 && variations_at(chain, &a) - variations_at(chain, &b) == 1
548 {
549 break (a, b);
550 }
551 delta = delta.mul(&half);
552 };
553 let left = variations_at(chain, &lo) - variations_at(chain, &a);
554 isolate(s, chain, lo, a, left, out);
555 out.push(RootInterval {
556 lo: mid.clone(),
557 hi: mid,
558 });
559 isolate(s, chain, b, hi, n - left - 1, out);
560}
561
562impl fmt::Display for Poly {
563 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
565 if self.is_zero() {
566 return write!(f, "0");
567 }
568 let mut first = true;
569 for i in (0..self.c.len()).rev() {
570 let c = &self.c[i];
571 if c.is_zero() {
572 continue;
573 }
574 let neg = c.is_negative();
575 if first {
576 if neg {
577 write!(f, "-")?;
578 }
579 } else {
580 write!(f, "{}", if neg { " - " } else { " + " })?;
581 }
582 first = false;
583 let a = c.abs();
584 let coef = if i == 0 || a.is_integer() {
585 if i > 0 && a == Rational::one() {
586 String::new()
587 } else {
588 a.to_string()
589 }
590 } else {
591 format!("({a})")
592 };
593 match i {
594 0 => write!(f, "{coef}")?,
595 1 => write!(f, "{coef}x")?,
596 _ => write!(f, "{coef}x^{i}")?,
597 }
598 }
599 Ok(())
600 }
601}
602
603fn lcm(a: &BigInt, b: &BigInt) -> BigInt {
606 let g = a.gcd(b);
607 a.div_rem(&g).expect("gcd of nonzero is nonzero").0.mul(b).abs()
608}
609
610fn z_trim(mut v: Vec<BigInt>) -> Vec<BigInt> {
611 while v.last().is_some_and(BigInt::is_zero) {
612 v.pop();
613 }
614 v
615}
616
617fn z_add(a: &[BigInt], b: &[BigInt]) -> Vec<BigInt> {
618 let n = a.len().max(b.len());
619 let zero = BigInt::zero();
620 z_trim(
621 (0..n)
622 .map(|i| a.get(i).unwrap_or(&zero).add(b.get(i).unwrap_or(&zero)))
623 .collect(),
624 )
625}
626
627fn z_sub(a: &[BigInt], b: &[BigInt]) -> Vec<BigInt> {
628 let n = a.len().max(b.len());
629 let zero = BigInt::zero();
630 z_trim(
631 (0..n)
632 .map(|i| a.get(i).unwrap_or(&zero).sub(b.get(i).unwrap_or(&zero)))
633 .collect(),
634 )
635}
636
637fn z_mul(a: &[BigInt], b: &[BigInt]) -> Vec<BigInt> {
638 if a.is_empty() || b.is_empty() {
639 return Vec::new();
640 }
641 let mut out = vec![BigInt::zero(); a.len() + b.len() - 1];
642 for (i, x) in a.iter().enumerate() {
643 if x.is_zero() {
644 continue;
645 }
646 for (j, y) in b.iter().enumerate() {
647 out[i + j] = out[i + j].add(&x.mul(y));
648 }
649 }
650 z_trim(out)
651}
652
653fn z_scale(a: &[BigInt], k: &BigInt) -> Vec<BigInt> {
654 z_trim(a.iter().map(|x| x.mul(k)).collect())
655}
656
657fn mod_nonneg(x: &BigInt, m: &BigInt) -> BigInt {
658 let r = x.div_rem(m).expect("the modulus is nonzero").1;
659 if r.is_negative() {
660 r.add(m)
661 } else {
662 r
663 }
664}
665
666fn zm_reduce(a: &[BigInt], m: &BigInt) -> Vec<BigInt> {
667 z_trim(a.iter().map(|x| mod_nonneg(x, m)).collect())
668}
669
670fn symmetric(x: &BigInt, m: &BigInt) -> BigInt {
672 let r = mod_nonneg(x, m);
673 if r.add(&r) > *m {
674 r.sub(m)
675 } else {
676 r
677 }
678}
679
680fn z_primitive(a: &[BigInt]) -> Vec<BigInt> {
682 let a = z_trim(a.to_vec());
683 let Some(lead) = a.last() else {
684 return a;
685 };
686 let content = a.iter().fold(BigInt::zero(), |g, x| g.gcd(x));
687 let content = if lead.is_negative() {
688 content.negated()
689 } else {
690 content
691 };
692 a.iter()
693 .map(|x| x.div_rem(&content).expect("content of a nonzero polynomial is nonzero").0)
694 .collect()
695}
696
697fn z_div_exact(f: &[BigInt], g: &[BigInt]) -> Option<Vec<BigInt>> {
699 let dg = g.len().checked_sub(1)?;
700 if f.len() <= dg {
701 return if f.is_empty() { Some(Vec::new()) } else { None };
702 }
703 let lc = &g[dg];
704 let mut r = f.to_vec();
705 let mut q = vec![BigInt::zero(); r.len() - dg];
706 for k in (0..q.len()).rev() {
707 let (coef, rem) = r[k + dg].div_rem(lc).expect("lc is nonzero");
708 if !rem.is_zero() {
709 return None;
710 }
711 if !coef.is_zero() {
712 for (j, gj) in g.iter().enumerate() {
713 r[k + j] = r[k + j].sub(&coef.mul(gj));
714 }
715 }
716 q[k] = coef;
717 }
718 if r[..dg].iter().all(BigInt::is_zero) {
719 Some(z_trim(q))
720 } else {
721 None
722 }
723}
724
725fn bigint_mod_inverse(a: &BigInt, m: &BigInt) -> BigInt {
726 let (mut r0, mut r1) = (mod_nonneg(a, m), m.clone());
727 let (mut s0, mut s1) = (BigInt::from_i64(1), BigInt::zero());
728 while !r1.is_zero() {
729 let (q, r) = r0.div_rem(&r1).expect("r1 is nonzero");
730 r0 = std::mem::replace(&mut r1, r);
731 let s2 = s0.sub(&q.mul(&s1));
732 s0 = std::mem::replace(&mut s1, s2);
733 }
734 mod_nonneg(&s0, m)
735}
736
737fn fp_trim(mut v: Vec<u64>) -> Vec<u64> {
740 while v.last() == Some(&0) {
741 v.pop();
742 }
743 v
744}
745
746fn pow_mod(mut b: u64, mut e: u64, p: u64) -> u64 {
747 let mut acc = 1u64;
748 b %= p;
749 while e > 0 {
750 if e & 1 == 1 {
751 acc = acc * b % p;
752 }
753 b = b * b % p;
754 e >>= 1;
755 }
756 acc
757}
758
759fn fp_inv(a: u64, p: u64) -> u64 {
760 pow_mod(a, p - 2, p)
761}
762
763fn modp(x: &BigInt, p: u64) -> u64 {
764 mod_nonneg(x, &BigInt::from_u64(p))
765 .to_u64()
766 .expect("a residue below p fits u64")
767}
768
769fn to_fp(f: &[BigInt], p: u64) -> Vec<u64> {
770 fp_trim(f.iter().map(|c| modp(c, p)).collect())
771}
772
773fn from_fp(v: &[u64]) -> Vec<BigInt> {
774 v.iter().map(|&x| BigInt::from_u64(x)).collect()
775}
776
777fn fp_add(a: &[u64], b: &[u64], p: u64) -> Vec<u64> {
778 let n = a.len().max(b.len());
779 fp_trim(
780 (0..n)
781 .map(|i| (a.get(i).copied().unwrap_or(0) + b.get(i).copied().unwrap_or(0)) % p)
782 .collect(),
783 )
784}
785
786fn fp_sub(a: &[u64], b: &[u64], p: u64) -> Vec<u64> {
787 let n = a.len().max(b.len());
788 fp_trim(
789 (0..n)
790 .map(|i| (a.get(i).copied().unwrap_or(0) + p - b.get(i).copied().unwrap_or(0)) % p)
791 .collect(),
792 )
793}
794
795fn fp_mul(a: &[u64], b: &[u64], p: u64) -> Vec<u64> {
796 if a.is_empty() || b.is_empty() {
797 return Vec::new();
798 }
799 let mut out = vec![0u64; a.len() + b.len() - 1];
800 for (i, &x) in a.iter().enumerate() {
801 if x == 0 {
802 continue;
803 }
804 for (j, &y) in b.iter().enumerate() {
805 out[i + j] = (out[i + j] + x * y) % p;
806 }
807 }
808 fp_trim(out)
809}
810
811fn fp_scale(a: &[u64], k: u64, p: u64) -> Vec<u64> {
812 fp_trim(a.iter().map(|&x| x * k % p).collect())
813}
814
815fn fp_divrem(a: &[u64], b: &[u64], p: u64) -> (Vec<u64>, Vec<u64>) {
816 let db = b.len() - 1;
817 if a.len() <= db {
818 return (Vec::new(), a.to_vec());
819 }
820 let inv = fp_inv(b[db], p);
821 let mut r = a.to_vec();
822 let mut q = vec![0u64; r.len() - db];
823 for k in (0..q.len()).rev() {
824 let coef = r[k + db] * inv % p;
825 if coef != 0 {
826 for (j, &bj) in b.iter().enumerate() {
827 r[k + j] = (r[k + j] + p - coef * bj % p) % p;
828 }
829 }
830 q[k] = coef;
831 }
832 r.truncate(db);
833 (fp_trim(q), fp_trim(r))
834}
835
836fn fp_monic(a: &[u64], p: u64) -> Vec<u64> {
837 match a.last() {
838 None => Vec::new(),
839 Some(&lc) => fp_scale(a, fp_inv(lc, p), p),
840 }
841}
842
843fn fp_gcd(a: &[u64], b: &[u64], p: u64) -> Vec<u64> {
844 let (mut a, mut b) = (fp_monic(a, p), fp_monic(b, p));
845 while !b.is_empty() {
846 let r = fp_divrem(&a, &b, p).1;
847 a = b;
848 b = fp_monic(&r, p);
849 }
850 a
851}
852
853fn fp_ext_gcd(a: &[u64], b: &[u64], p: u64) -> (Vec<u64>, Vec<u64>, Vec<u64>) {
855 let (mut r0, mut r1) = (a.to_vec(), b.to_vec());
856 let (mut s0, mut s1) = (vec![1u64], Vec::new());
857 let (mut t0, mut t1) = (Vec::new(), vec![1u64]);
858 while !r1.is_empty() {
859 let (q, r) = fp_divrem(&r0, &r1, p);
860 r0 = std::mem::replace(&mut r1, r);
861 let s2 = fp_sub(&s0, &fp_mul(&q, &s1, p), p);
862 s0 = std::mem::replace(&mut s1, s2);
863 let t2 = fp_sub(&t0, &fp_mul(&q, &t1, p), p);
864 t0 = std::mem::replace(&mut t1, t2);
865 }
866 let inv = fp_inv(*r0.last().expect("gcd of nonzero polynomials is nonzero"), p);
867 (fp_scale(&r0, inv, p), fp_scale(&s0, inv, p), fp_scale(&t0, inv, p))
868}
869
870fn fp_derivative(a: &[u64], p: u64) -> Vec<u64> {
871 fp_trim(
872 a.iter()
873 .enumerate()
874 .skip(1)
875 .map(|(i, &x)| x * (i as u64 % p) % p)
876 .collect(),
877 )
878}
879
880fn fp_powmod(base: &[u64], mut e: u64, f: &[u64], p: u64) -> Vec<u64> {
882 let mut acc = vec![1u64];
883 let mut b = fp_divrem(base, f, p).1;
884 while e > 0 {
885 if e & 1 == 1 {
886 acc = fp_divrem(&fp_mul(&acc, &b, p), f, p).1;
887 }
888 e >>= 1;
889 if e > 0 {
890 b = fp_divrem(&fp_mul(&b, &b, p), f, p).1;
891 }
892 }
893 acc
894}
895
896fn nullspace_mod_p(mut a: Vec<Vec<u64>>, p: u64) -> Vec<Vec<u64>> {
898 let rows = a.len();
899 let cols = a[0].len();
900 let mut pivot_cols = Vec::new();
901 let mut r = 0;
902 for c in 0..cols {
903 if r == rows {
904 break;
905 }
906 let Some(pr) = (r..rows).find(|&i| a[i][c] != 0) else {
907 continue;
908 };
909 a.swap(r, pr);
910 let inv = fp_inv(a[r][c], p);
911 for x in a[r].iter_mut() {
912 *x = *x * inv % p;
913 }
914 for i in 0..rows {
915 if i != r && a[i][c] != 0 {
916 let f = a[i][c];
917 let pivot = a[r].clone();
918 for (x, y) in a[i].iter_mut().zip(&pivot) {
919 *x = (*x + p - f * y % p) % p;
920 }
921 }
922 }
923 pivot_cols.push(c);
924 r += 1;
925 }
926 (0..cols)
927 .filter(|c| !pivot_cols.contains(c))
928 .map(|fc| {
929 let mut v = vec![0u64; cols];
930 v[fc] = 1;
931 for (ri, &pc) in pivot_cols.iter().enumerate() {
932 v[pc] = (p - a[ri][fc]) % p;
933 }
934 v
935 })
936 .collect()
937}
938
939fn berlekamp(f: &[u64], p: u64) -> Vec<Vec<u64>> {
943 let n = f.len() - 1;
944 let xp = fp_powmod(&[0, 1], p, f, p);
945 let mut rows = Vec::with_capacity(n);
946 let mut cur = vec![1u64];
947 for _ in 0..n {
948 let mut row = cur.clone();
949 row.resize(n, 0);
950 rows.push(row);
951 cur = fp_divrem(&fp_mul(&cur, &xp, p), f, p).1;
952 }
953 let mut a = vec![vec![0u64; n]; n];
954 for (i, row) in rows.iter().enumerate() {
955 for (j, &q) in row.iter().enumerate() {
956 a[j][i] = if i == j { (q + p - 1) % p } else { q };
957 }
958 }
959 let basis = nullspace_mod_p(a, p);
960 let r = basis.len();
961 let mut factors = vec![f.to_vec()];
962 'split: for v in &basis {
963 let v = fp_trim(v.clone());
964 if v.len() <= 1 {
965 continue;
966 }
967 for s in 0..p {
968 let mut shifted = v.clone();
969 shifted[0] = (shifted[0] + p - s) % p;
970 let shifted = fp_trim(shifted);
971 let mut next = Vec::with_capacity(factors.len() + 1);
972 for g in factors.drain(..) {
973 if g.len() <= 2 {
974 next.push(g);
975 continue;
976 }
977 let h = fp_gcd(&g, &shifted, p);
978 if h.len() > 1 && h.len() < g.len() {
979 let rest = fp_monic(&fp_divrem(&g, &h, p).0, p);
980 next.push(h);
981 next.push(rest);
982 } else {
983 next.push(g);
984 }
985 }
986 factors = next;
987 if factors.len() == r {
988 break 'split;
989 }
990 }
991 }
992 factors.sort();
993 factors
994}
995
996fn hensel_pair(
1000 f: &[BigInt],
1001 g0: &[u64],
1002 h0: &[u64],
1003 p: u64,
1004 k: u32,
1005) -> (Vec<BigInt>, Vec<BigInt>) {
1006 let (_, s, t) = fp_ext_gcd(g0, h0, p);
1007 let pb = BigInt::from_u64(p);
1008 let mut m = pb.clone();
1009 let mut g = from_fp(g0);
1010 let mut h = from_fp(h0);
1011 for _ in 1..k {
1012 let diff = z_sub(f, &z_mul(&g, &h));
1013 let e = fp_trim(
1014 diff.iter()
1015 .map(|c| {
1016 let (q, r) = c.div_rem(&m).expect("m is nonzero");
1017 debug_assert!(r.is_zero(), "f ≡ g·h (mod m) makes the error divisible by m");
1018 modp(&q, p)
1019 })
1020 .collect(),
1021 );
1022 let te = fp_mul(&t, &e, p);
1023 let (q, dg) = fp_divrem(&te, g0, p);
1024 let dh = fp_add(&fp_mul(&s, &e, p), &fp_mul(&q, h0, p), p);
1025 let mp = m.mul(&pb);
1026 g = zm_reduce(&z_add(&g, &z_scale(&from_fp(&dg), &m)), &mp);
1027 h = zm_reduce(&z_add(&h, &z_scale(&from_fp(&dh), &m)), &mp);
1028 m = mp;
1029 }
1030 (g, h)
1031}
1032
1033fn hensel_multi(f: &[BigInt], mods: &[Vec<u64>], p: u64, k: u32, pk: &BigInt) -> Vec<Vec<BigInt>> {
1036 let mut out = Vec::with_capacity(mods.len());
1037 let mut target = f.to_vec();
1038 for g0 in &mods[..mods.len() - 1] {
1039 let (h0, rem) = fp_divrem(&to_fp(&target, p), g0, p);
1040 debug_assert!(rem.is_empty(), "every modular factor divides the target");
1041 let (g, h) = hensel_pair(&target, g0, &h0, p, k);
1042 out.push(g);
1043 target = h;
1044 }
1045 let inv = bigint_mod_inverse(target.last().expect("the cofactor is nonzero"), pk);
1046 out.push(zm_reduce(&z_scale(&target, &inv), pk));
1047 out
1048}
1049
1050fn next_combination(idx: &mut [usize], n: usize) -> bool {
1053 let s = idx.len();
1054 for i in (0..s).rev() {
1055 if idx[i] < n - s + i {
1056 idx[i] += 1;
1057 for j in i + 1..s {
1058 idx[j] = idx[j - 1] + 1;
1059 }
1060 return true;
1061 }
1062 }
1063 false
1064}
1065
1066fn recombine(mut f: Vec<BigInt>, mut lifted: Vec<Vec<BigInt>>, pk: &BigInt) -> Vec<Vec<BigInt>> {
1070 let mut found = Vec::new();
1071 let mut s = 1;
1072 while 2 * s <= lifted.len() {
1073 let n = lifted.len();
1074 let mut idx: Vec<usize> = (0..s).collect();
1075 let mut hit = None;
1076 loop {
1077 let lc = f.last().expect("f has positive degree").clone();
1078 let mut cand = vec![lc];
1079 for &i in &idx {
1080 cand = zm_reduce(&z_mul(&cand, &lifted[i]), pk);
1081 }
1082 let cand = z_primitive(&z_trim(cand.iter().map(|c| symmetric(c, pk)).collect()));
1083 if let Some(q) = z_div_exact(&f, &cand) {
1084 hit = Some((cand, q));
1085 break;
1086 }
1087 if !next_combination(&mut idx, n) {
1088 break;
1089 }
1090 }
1091 match hit {
1092 Some((g, q)) => {
1093 found.push(g);
1094 f = z_primitive(&q);
1095 for &i in idx.iter().rev() {
1096 lifted.remove(i);
1097 }
1098 }
1099 None => s += 1,
1100 }
1101 }
1102 if f.len() > 1 {
1103 found.push(z_primitive(&f));
1104 }
1105 found
1106}
1107
1108fn is_prime(n: u64) -> bool {
1109 n >= 2 && (2..).take_while(|d| d * d <= n).all(|d| !n.is_multiple_of(d))
1110}
1111
1112fn factor_squarefree_primitive(f: Vec<BigInt>) -> Vec<Vec<BigInt>> {
1114 let n = f.len() - 1;
1115 if n <= 1 {
1116 return vec![f];
1117 }
1118 let lc = f.last().expect("positive degree").clone();
1119 let mut best: Option<(u64, Vec<Vec<u64>>)> = None;
1120 let mut admissible = 0;
1121 for p in (3u64..).step_by(2).filter(|&p| is_prime(p)) {
1122 if modp(&lc, p) == 0 {
1123 continue;
1124 }
1125 let fm = fp_monic(&to_fp(&f, p), p);
1126 if fp_gcd(&fm, &fp_derivative(&fm, p), p).len() != 1 {
1127 continue;
1128 }
1129 let factors = berlekamp(&fm, p);
1130 if factors.len() == 1 {
1131 return vec![f];
1132 }
1133 if best.as_ref().is_none_or(|(_, b)| factors.len() < b.len()) {
1134 best = Some((p, factors));
1135 }
1136 admissible += 1;
1137 if admissible == 5 {
1138 break;
1139 }
1140 }
1141 let (p, mods) = best.expect("a squarefree polynomial has admissible primes");
1142 let l1 = f.iter().fold(BigInt::zero(), |a, c| a.add(&c.abs()));
1143 let bound = lc.abs().mul(&l1).shl_bits(n as u64 + 1);
1144 let pb = BigInt::from_u64(p);
1145 let mut pk = pb.clone();
1146 let mut k = 1u32;
1147 while pk <= bound {
1148 pk = pk.mul(&pb);
1149 k += 1;
1150 }
1151 let lifted = hensel_multi(&f, &mods, p, k, &pk);
1152 recombine(f, lifted, &pk)
1153}
1154
1155impl crate::exact_codec::ExactCodec for Poly {
1156 fn write(&self, out: &mut Vec<u8>) {
1158 crate::exact_codec::write_list(&self.c, out);
1159 }
1160
1161 fn read(buf: &[u8], pos: &mut usize) -> Option<Self> {
1162 Some(Poly::from_coeffs(crate::exact_codec::read_list(buf, pos)?))
1163 }
1164}
1165
1166#[cfg(test)]
1167#[path = "../../logicaffeine_tests/unit/logicaffeine_base/poly/tests.rs"]
1168mod tests;