1use num_bigint::BigInt;
15use num_integer::Integer;
16use num_traits::{One, Signed, ToPrimitive, Zero};
17
18use crate::basis::Basis;
19use crate::primes::{lcm as u64_lcm, mod_inverse};
20use crate::rational::RnsRational;
21use crate::rns::crt_balanced;
22
23#[derive(Clone, Debug)]
28pub struct Polynomial {
29 pub coeffs: Vec<RnsRational>,
30 pub channels: Basis,
31}
32
33impl Polynomial {
34 pub fn new(coeffs: Vec<RnsRational>, channels: Basis) -> Self {
36 let mut p = Polynomial { coeffs, channels };
37 p.trim();
38 p
39 }
40
41 pub fn from_int_coeffs(coeffs: &[i64], channels: Basis) -> Self {
43 let c = coeffs
44 .iter()
45 .map(|&v| RnsRational::from_int(v, channels.clone()))
46 .collect();
47 Self::new(c, channels)
48 }
49
50 fn r_zero(&self) -> RnsRational {
51 RnsRational::zero(self.channels.clone())
52 }
53 fn r_one(&self) -> RnsRational {
54 RnsRational::from_int(1, self.channels.clone())
55 }
56
57 fn trim(&mut self) {
58 while self.coeffs.len() > 0 && self.coeffs.last().unwrap().is_zero() {
59 self.coeffs.pop();
60 }
61 }
62
63 pub fn zero(channels: Basis) -> Self {
65 Polynomial { coeffs: vec![], channels }
66 }
67
68 pub fn one(channels: Basis) -> Self {
70 Self::from_int_coeffs(&[1], channels)
71 }
72
73 pub fn constant(c: RnsRational) -> Self {
75 let ch = c.channels.clone();
76 Self::new(vec![c], ch)
77 }
78
79 pub fn is_zero(&self) -> bool {
81 self.coeffs.is_empty()
82 }
83
84 pub fn degree(&self) -> usize {
87 self.coeffs.len().saturating_sub(1)
88 }
89
90 pub fn leading(&self) -> RnsRational {
92 self.coeffs.last().cloned().unwrap_or_else(|| self.r_zero())
93 }
94
95 pub fn eval(&self, x: &RnsRational) -> RnsRational {
97 let mut acc = self.r_zero();
98 for c in self.coeffs.iter().rev() {
99 acc = acc.mul(x).add(c);
100 }
101 acc
102 }
103
104 pub fn sign_at(&self, x: &RnsRational) -> i32 {
106 self.eval(x).signum()
107 }
108
109 pub fn derivative(&self) -> Self {
111 if self.coeffs.len() <= 1 {
112 return Self::zero(self.channels.clone());
113 }
114 let coeffs = self
115 .coeffs
116 .iter()
117 .enumerate()
118 .skip(1)
119 .map(|(i, c)| c.mul(&RnsRational::from_int(i as i64, self.channels.clone())))
120 .collect();
121 Self::new(coeffs, self.channels.clone())
122 }
123
124 pub fn scalar_mul(&self, s: &RnsRational) -> Self {
126 Self::new(
127 self.coeffs.iter().map(|c| c.mul(s)).collect(),
128 self.channels.clone(),
129 )
130 }
131
132 pub fn add(&self, other: &Self) -> Self {
134 let n = self.coeffs.len().max(other.coeffs.len());
135 let mut out = Vec::with_capacity(n);
136 for i in 0..n {
137 let a = self.coeffs.get(i).cloned().unwrap_or_else(|| self.r_zero());
138 let b = other.coeffs.get(i).cloned().unwrap_or_else(|| self.r_zero());
139 out.push(a.add(&b));
140 }
141 Self::new(out, self.channels.clone())
142 }
143
144 pub fn sub(&self, other: &Self) -> Self {
146 self.add(&other.scalar_mul(&RnsRational::from_int(-1, self.channels.clone())))
147 }
148
149 pub fn mul(&self, other: &Self) -> Self {
151 if self.is_zero() || other.is_zero() {
152 return Self::zero(self.channels.clone());
153 }
154 let mut out = vec![self.r_zero(); self.coeffs.len() + other.coeffs.len() - 1];
155 for (i, a) in self.coeffs.iter().enumerate() {
156 for (j, b) in other.coeffs.iter().enumerate() {
157 out[i + j] = out[i + j].add(&a.mul(b));
158 }
159 }
160 Self::new(out, self.channels.clone())
161 }
162
163 pub fn divmod(&self, divisor: &Self) -> (Self, Self) {
166 assert!(!divisor.is_zero(), "polynomial division by zero");
167 let mut rem = self.clone();
168 let d_deg = divisor.degree();
169 let d_lead = divisor.leading();
170 if self.is_zero() || self.degree() < d_deg {
171 return (Self::zero(self.channels.clone()), rem);
172 }
173 let mut quot = vec![self.r_zero(); self.degree() - d_deg + 1];
174 while !rem.is_zero() && rem.degree() >= d_deg {
175 let shift = rem.degree() - d_deg;
176 let factor = rem.leading().div(&d_lead);
177 quot[shift] = factor.clone();
178 let mut sub_coeffs = vec![self.r_zero(); shift];
180 for c in &divisor.coeffs {
181 sub_coeffs.push(c.mul(&factor));
182 }
183 let sub = Self::new(sub_coeffs, self.channels.clone());
184 rem = rem.sub(&sub);
185 }
186 (Self::new(quot, self.channels.clone()), rem)
187 }
188
189 pub fn rem(&self, divisor: &Self) -> Self {
191 self.divmod(divisor).1
192 }
193
194 pub fn div_exact(&self, divisor: &Self) -> Self {
196 let (q, r) = self.divmod(divisor);
197 debug_assert!(r.is_zero(), "div_exact: non-zero remainder");
198 q
199 }
200
201 pub fn monic(&self) -> Self {
203 if self.is_zero() {
204 return self.clone();
205 }
206 let inv = self.r_one().div(&self.leading());
207 self.scalar_mul(&inv)
208 }
209
210 pub fn gcd(a: &Self, b: &Self) -> Self {
212 let mut x = a.clone();
213 let mut y = b.clone();
214 while !y.is_zero() {
215 let r = x.rem(&y);
216 x = y;
217 y = r;
218 }
219 x.monic()
220 }
221
222 pub fn squarefree(&self) -> Self {
224 if self.is_zero() || self.degree() == 0 {
225 return self.monic();
226 }
227 let g = Self::gcd(self, &self.derivative());
228 self.div_exact(&g).monic()
229 }
230
231 pub fn sturm_sequence(&self) -> Vec<Self> {
233 let mut seq = vec![self.clone(), self.derivative()];
234 while !seq.last().unwrap().is_zero() {
235 let n = seq.len();
236 let r = seq[n - 2].rem(&seq[n - 1]);
237 if r.is_zero() {
238 break;
239 }
240 seq.push(r.scalar_mul(&RnsRational::from_int(-1, self.channels.clone())));
241 }
242 seq
243 }
244
245 pub fn sign_changes(seq: &[Self], x: &RnsRational) -> usize {
247 let mut last = 0i32;
248 let mut changes = 0usize;
249 for s in seq {
250 let sign = s.sign_at(x);
251 if sign != 0 {
252 if last != 0 && sign != last {
253 changes += 1;
254 }
255 last = sign;
256 }
257 }
258 changes
259 }
260
261 pub fn sturm_root_count(&self, a: &RnsRational, b: &RnsRational) -> usize {
263 let seq = self.sturm_sequence();
264 let va = Self::sign_changes(&seq, a);
265 let vb = Self::sign_changes(&seq, b);
266 va.saturating_sub(vb)
267 }
268
269 pub fn root_bound(&self) -> RnsRational {
271 if self.is_zero() || self.degree() == 0 {
272 return self.r_one();
273 }
274 let lead = self.leading();
275 let mut max_ratio = self.r_zero();
276 for c in &self.coeffs[..self.coeffs.len() - 1] {
277 let ratio = c.div(&lead).abs();
278 if ratio > max_ratio {
279 max_ratio = ratio;
280 }
281 }
282 self.r_one().add(&max_ratio)
283 }
284
285 pub fn isolate_real_roots(&self) -> Vec<(RnsRational, RnsRational)> {
288 let sf = self.squarefree();
289 if sf.degree() == 0 {
290 return Vec::new();
291 }
292 let seq = sf.sturm_sequence();
293 let b = sf.root_bound();
294 let lo = b.neg();
295 let hi = b;
296 let mut out = Vec::new();
297 let min_width = RnsRational::new(BigInt::one(), BigInt::one() << 80, sf.channels.clone());
299 Self::isolate_rec(&seq, &lo, &hi, &min_width, &mut out);
300 out.sort_by(|x, y| x.0.cmp(&y.0));
301 out
302 }
303
304 fn isolate_rec(
305 seq: &[Self],
306 lo: &RnsRational,
307 hi: &RnsRational,
308 min_width: &RnsRational,
309 out: &mut Vec<(RnsRational, RnsRational)>,
310 ) {
311 let cnt = Self::sign_changes(seq, lo).saturating_sub(Self::sign_changes(seq, hi));
312 if cnt == 0 {
313 return;
314 }
315 if cnt == 1 {
316 out.push((lo.clone(), hi.clone()));
317 return;
318 }
319 let width = hi.sub(lo);
320 if width < *min_width {
321 out.push((lo.clone(), hi.clone()));
322 return;
323 }
324 let mid = lo.midpoint(hi);
325 Self::isolate_rec(seq, lo, &mid, min_width, out);
326 Self::isolate_rec(seq, &mid, hi, min_width, out);
327 }
328
329 fn primitive_int_coeffs(&self) -> Vec<BigInt> {
334 if self.is_zero() {
335 return vec![BigInt::zero()];
336 }
337 let mut denom_lcm = 1u64;
339 let mut pairs = Vec::new();
340 for c in &self.coeffs {
341 let (p, q) = c.to_pair();
342 let qu = q.to_u64().unwrap_or(1);
343 denom_lcm = u64_lcm(denom_lcm, qu);
344 pairs.push((p, q));
345 }
346 let big_lcm = BigInt::from(denom_lcm);
347 let mut ints: Vec<BigInt> = pairs
348 .iter()
349 .map(|(p, q)| p * (&big_lcm / q))
350 .collect();
351 let mut content = BigInt::zero();
353 for v in &ints {
354 content = content.gcd(v);
355 }
356 if !content.is_zero() && content != BigInt::one() {
357 for v in &mut ints {
358 *v /= &content;
359 }
360 }
361 ints
362 }
363
364 pub fn find_rational_root(&self) -> Option<RnsRational> {
366 let ints = self.primitive_int_coeffs();
367 if ints.len() <= 1 {
368 return None;
369 }
370 let a0 = ints.first().unwrap().clone();
371 let an = ints.last().unwrap().clone();
372 if a0.is_zero() {
374 return Some(self.r_zero());
375 }
376 let p_divs = divisors(&a0.abs());
377 let q_divs = divisors(&an.abs());
378 for p in &p_divs {
379 for q in &q_divs {
380 for sign in [1i64, -1] {
381 let cand = RnsRational::new(
382 BigInt::from(sign) * p,
383 q.clone(),
384 self.channels.clone(),
385 );
386 if self.eval(&cand).is_zero() {
387 return Some(cand);
388 }
389 }
390 }
391 }
392 None
393 }
394
395 pub fn factor_over_q(&self) -> Vec<Self> {
399 let mut work = self.squarefree();
400 let mut factors = Vec::new();
401 loop {
402 if work.degree() == 0 {
403 break;
404 }
405 match work.find_rational_root() {
406 Some(r) => {
407 let lin = Self::new(
409 vec![r.neg(), work.r_one()],
410 self.channels.clone(),
411 );
412 work = work.div_exact(&lin);
413 factors.push(lin.monic());
414 }
415 None => break,
416 }
417 }
418 if work.degree() >= 1 {
419 factors.push(work.monic());
420 }
421 factors
422 }
423}
424
425impl PartialEq for Polynomial {
426 fn eq(&self, other: &Self) -> bool {
427 self.coeffs == other.coeffs
428 }
429}
430impl Eq for Polynomial {}
431
432fn divisors(n: &BigInt) -> Vec<BigInt> {
434 let mut out = vec![BigInt::one()];
435 let n_u = match n.to_u128() {
436 Some(v) if v > 0 => v,
437 _ => return out,
438 };
439 let mut divs = Vec::new();
440 let mut d = 1u128;
441 while d * d <= n_u {
442 if n_u % d == 0 {
443 divs.push(d);
444 if d != n_u / d {
445 divs.push(n_u / d);
446 }
447 }
448 d += 1;
449 }
450 out.clear();
451 for v in divs {
452 out.push(BigInt::from(v));
453 }
454 out
455}
456
457type BiPoly = Vec<Polynomial>;
462
463fn bi_degree(p: &BiPoly) -> usize {
464 let mut d = 0;
465 for (i, c) in p.iter().enumerate() {
466 if !c.is_zero() {
467 d = i;
468 }
469 }
470 d
471}
472
473fn resultant_y(a: &BiPoly, b: &BiPoly, channels: &Basis) -> Polynomial {
488 let m = bi_degree(a);
489 let n = bi_degree(b);
490 let size = m + n;
491 if size == 0 {
492 return Polynomial::one(channels.clone());
493 }
494 let zero = Polynomial::zero(channels.clone());
495 let mut mat = vec![vec![zero.clone(); size]; size];
496
497 for i in 0..n {
499 for j in 0..=m {
500 mat[i][i + j] = a[m - j].clone();
501 }
502 }
503 for i in 0..m {
505 for j in 0..=n {
506 mat[n + i][i + j] = b[n - j].clone();
507 }
508 }
509
510 let int_mat = clear_denominators(&mat);
511 let det = multimodular_det(&int_mat);
512 int_poly_to_polynomial(&det, channels)
513}
514
515type IntPoly = Vec<BigInt>;
517
518fn clear_denominators(mat: &[Vec<Polynomial>]) -> Vec<Vec<IntPoly>> {
522 mat.iter()
523 .map(|row| {
524 let mut l = BigInt::one();
525 for entry in row {
526 for c in &entry.coeffs {
527 let (_, q) = c.to_pair();
528 l = l.lcm(&q);
529 }
530 }
531 row.iter()
532 .map(|entry| {
533 entry
534 .coeffs
535 .iter()
536 .map(|c| {
537 let (p, q) = c.to_pair();
538 p * (&l / &q)
539 })
540 .collect::<IntPoly>()
541 })
542 .collect()
543 })
544 .collect()
545}
546
547fn multimodular_det(mat: &[Vec<IntPoly>]) -> IntPoly {
549 let n = mat.len();
550 if n == 0 {
551 return vec![BigInt::one()];
552 }
553
554 let deg_x: usize = mat
557 .iter()
558 .map(|row| row.iter().map(|e| e.len().saturating_sub(1)).max().unwrap_or(0))
559 .sum();
560 let slots = deg_x + 1;
561
562 let mut bits: u64 = 2; for row in mat {
567 let row_sum: BigInt = row
568 .iter()
569 .map(|e| e.iter().map(|c| c.abs()).sum::<BigInt>())
570 .sum();
571 bits += row_sum.bits().max(1);
572 }
573 let basis = Basis::with_bits(bits);
574 let moduli = basis.moduli().to_vec();
575
576 use rayon::prelude::*;
578 let per_prime: Vec<Vec<u32>> = moduli
579 .par_iter()
580 .map(|&p| det_poly_mod_p(mat, deg_x, p as u64))
581 .collect();
582
583 (0..slots)
585 .map(|s| {
586 let residues: Vec<u32> = per_prime.iter().map(|poly| poly[s]).collect();
587 crt_balanced(&residues, &moduli)
588 })
589 .collect()
590}
591
592fn det_poly_mod_p(mat: &[Vec<IntPoly>], deg_x: usize, p: u64) -> Vec<u32> {
595 let n = mat.len();
596 let num_points = deg_x + 1;
597 let mut ys = Vec::with_capacity(num_points);
598 for t in 0..num_points {
599 let tt = (t as u64) % p;
600 let mut a = vec![vec![0u64; n]; n];
601 for (i, row) in mat.iter().enumerate() {
602 for (j, entry) in row.iter().enumerate() {
603 a[i][j] = eval_int_poly_mod(entry, tt, p);
604 }
605 }
606 ys.push(det_scalar_mod_p(a, p));
607 }
608 interpolate_mod_p(&ys, p).into_iter().map(|c| c as u32).collect()
609}
610
611fn eval_int_poly_mod(poly: &[BigInt], t: u64, p: u64) -> u64 {
613 let pb = BigInt::from(p);
614 let mut acc = 0u64;
615 for c in poly.iter().rev() {
616 let cm = c.mod_floor(&pb).to_u64().unwrap_or(0);
617 acc = (acc * (t % p) + cm) % p;
618 }
619 acc
620}
621
622fn det_scalar_mod_p(mut a: Vec<Vec<u64>>, p: u64) -> u64 {
625 let n = a.len();
626 let mut det = 1u64;
627 for k in 0..n {
628 let pivot = (k..n).find(|&i| a[i][k] % p != 0);
629 let piv = match pivot {
630 Some(i) => i,
631 None => return 0, };
633 if piv != k {
634 a.swap(piv, k);
635 det = (p - det) % p; }
637 let inv = mod_inverse(a[k][k], p).expect("nonzero element of a field is invertible");
638 det = det * (a[k][k] % p) % p;
639 let pivot_row = a[k].clone();
640 for row in a.iter_mut().skip(k + 1) {
641 let factor = row[k] * inv % p;
642 if factor == 0 {
643 continue;
644 }
645 for (j, &pj) in pivot_row.iter().enumerate().skip(k) {
646 let sub = factor * (pj % p) % p;
647 row[j] = (row[j] + p - sub) % p;
648 }
649 }
650 }
651 det % p
652}
653
654fn interpolate_mod_p(ys: &[u64], p: u64) -> Vec<u64> {
657 let np = ys.len();
658 let mut coeffs = vec![0u64; np];
659 for (s, &ys_s) in ys.iter().enumerate() {
660 let mut num = vec![1u64];
662 let mut denom = 1u64;
663 for t in 0..np {
664 if t == s {
665 continue;
666 }
667 num = poly_mul_linear(&num, t as u64, p);
668 let diff = (s as i64 - t as i64).rem_euclid(p as i64) as u64;
669 denom = denom * diff % p;
670 }
671 let scale = ys_s % p * mod_inverse(denom, p).expect("distinct nodes are invertible") % p;
672 for (k, &nc) in num.iter().enumerate() {
673 coeffs[k] = (coeffs[k] + scale * nc) % p;
674 }
675 }
676 coeffs
677}
678
679fn poly_mul_linear(c: &[u64], t: u64, p: u64) -> Vec<u64> {
682 let mut r = vec![0u64; c.len() + 1];
683 for (k, &ck) in c.iter().enumerate() {
684 r[k + 1] = (r[k + 1] + ck) % p; let neg = (p - (t * ck % p)) % p; r[k] = (r[k] + neg) % p;
687 }
688 r
689}
690
691fn int_poly_to_polynomial(coeffs: &[BigInt], channels: &Basis) -> Polynomial {
693 let rcoeffs = coeffs
694 .iter()
695 .map(|c| RnsRational::new(c.clone(), BigInt::one(), channels.clone()))
696 .collect();
697 Polynomial::new(rcoeffs, channels.clone())
698}
699
700fn lift_const(p: &Polynomial) -> BiPoly {
702 p.coeffs.iter().map(|c| Polynomial::constant(c.clone())).collect()
703}
704
705fn shift_sub(q: &Polynomial, channels: &Basis) -> BiPoly {
707 let x_poly = Polynomial::from_int_coeffs(&[0, 1], channels.clone());
709 let neg_one = Polynomial::from_int_coeffs(&[-1], channels.clone());
710 let base: BiPoly = vec![x_poly, neg_one];
711
712 let mut acc: BiPoly = vec![Polynomial::zero(channels.clone())];
713 let mut power: BiPoly = vec![Polynomial::one(channels.clone())]; for (j, c) in q.coeffs.iter().enumerate() {
715 if j > 0 {
716 power = bi_mul(&power, &base, channels);
717 }
718 let term = bi_scalar(&power, c);
719 acc = bi_add(&acc, &term, channels);
720 }
721 acc
722}
723
724fn invert_scale(q: &Polynomial, channels: &Basis) -> BiPoly {
726 let d = q.degree();
727 let mut out: BiPoly = vec![Polynomial::zero(channels.clone()); d + 1];
728 for (j, c) in q.coeffs.iter().enumerate() {
729 let mut xj = vec![0i64; j + 1];
731 xj[j] = 1;
732 let x_pow = Polynomial::from_int_coeffs(&xj, channels.clone());
733 out[d - j] = x_pow.scalar_mul(c);
734 }
735 out
736}
737
738fn bi_add(a: &BiPoly, b: &BiPoly, channels: &Basis) -> BiPoly {
739 let n = a.len().max(b.len());
740 (0..n)
741 .map(|i| {
742 let za = a.get(i).cloned().unwrap_or_else(|| Polynomial::zero(channels.clone()));
743 let zb = b.get(i).cloned().unwrap_or_else(|| Polynomial::zero(channels.clone()));
744 za.add(&zb)
745 })
746 .collect()
747}
748
749fn bi_scalar(a: &BiPoly, s: &RnsRational) -> BiPoly {
750 a.iter().map(|c| c.scalar_mul(s)).collect()
751}
752
753fn bi_mul(a: &BiPoly, b: &BiPoly, channels: &Basis) -> BiPoly {
754 if a.is_empty() || b.is_empty() {
755 return vec![Polynomial::zero(channels.clone())];
756 }
757 let mut out = vec![Polynomial::zero(channels.clone()); a.len() + b.len() - 1];
758 for (i, ca) in a.iter().enumerate() {
759 for (j, cb) in b.iter().enumerate() {
760 out[i + j] = out[i + j].add(&ca.mul(cb));
761 }
762 }
763 out
764}
765
766#[derive(Clone, Debug)]
768pub struct AlgebraicNumber {
769 pub annihilating_poly: Polynomial,
776 pub interval: (RnsRational, RnsRational),
778 pub channels: Basis,
779}
780
781impl AlgebraicNumber {
782 pub fn sqrt(n: u64, channels: Basis) -> Self {
784 let annihilating_poly = Polynomial::from_int_coeffs(&[-(n as i64), 0, 1], channels.clone());
786 let lo = RnsRational::from_int(0, channels.clone());
787 let hi = RnsRational::from_int(n as i64 + 1, channels.clone());
788 Self::from_annihilating_poly_interval(annihilating_poly, lo, hi, channels)
789 }
790
791 pub fn cbrt(n: u64, channels: Basis) -> Self {
793 let annihilating_poly = Polynomial::from_int_coeffs(&[-(n as i64), 0, 0, 1], channels.clone());
794 let lo = RnsRational::from_int(0, channels.clone());
795 let hi = RnsRational::from_int(n as i64 + 1, channels.clone());
796 Self::from_annihilating_poly_interval(annihilating_poly, lo, hi, channels)
797 }
798
799 pub fn from_rational(r: RnsRational) -> Self {
801 let channels = r.channels.clone();
802 let annihilating_poly = Polynomial::new(
803 vec![r.neg(), RnsRational::from_int(1, channels.clone())],
804 channels.clone(),
805 );
806 let lo = r.sub(&RnsRational::from_int(1, channels.clone()));
807 let hi = r.add(&RnsRational::from_int(1, channels.clone()));
808 AlgebraicNumber { annihilating_poly, interval: (lo, hi), channels }
809 }
810
811 pub fn from_poly_root(p: Polynomial, root_index: usize, channels: Basis) -> Self {
813 let roots = p.isolate_real_roots();
814 let (lo, hi) = roots
815 .get(root_index)
816 .cloned()
817 .expect("root_index out of range");
818 let factors = p.factor_over_q();
820 let annihilating_poly = Self::factor_for_interval(&factors, &lo, &hi).unwrap_or(p);
821 AlgebraicNumber { annihilating_poly, interval: (lo, hi), channels }
822 }
823
824 fn from_annihilating_poly_interval(
825 annihilating_poly: Polynomial,
826 lo: RnsRational,
827 hi: RnsRational,
828 channels: Basis,
829 ) -> Self {
830 let mut a = AlgebraicNumber { annihilating_poly, interval: (lo, hi), channels };
831 let target = RnsRational::new(BigInt::one(), BigInt::from(1_000_000), a.channels.clone());
833 a.refine_interval(&target);
834 a
835 }
836
837 pub fn degree(&self) -> usize {
839 self.annihilating_poly.degree()
840 }
841
842 pub fn try_minimize(&self) -> AlgebraicNumber {
847 let factors = self.annihilating_poly.squarefree().factor_over_q();
848 let approx = self.to_f64();
849 let mut best: Option<(Polynomial, f64)> = None;
850 for f in &factors {
851 for (lo, hi) in f.isolate_real_roots() {
852 let mut cand = AlgebraicNumber {
853 annihilating_poly: f.clone(),
854 interval: (lo, hi),
855 channels: self.channels.clone(),
856 };
857 let target = RnsRational::new(BigInt::one(), BigInt::from(1_000_000), self.channels.clone());
858 cand.refine_interval(&target);
859 let dist = (cand.to_f64() - approx).abs();
860 if best.as_ref().map(|(_, d)| dist < *d).unwrap_or(true) {
861 best = Some((f.clone(), dist));
862 }
863 }
864 }
865 match best {
866 Some((f, _)) => AlgebraicNumber {
867 annihilating_poly: f,
868 interval: self.interval.clone(),
869 channels: self.channels.clone(),
870 },
871 None => self.clone(),
872 }
873 }
874
875 pub fn to_rational(&self) -> Option<RnsRational> {
877 if self.annihilating_poly.degree() == 1 {
878 let c0 = self.annihilating_poly.coeffs[0].clone();
880 let c1 = self.annihilating_poly.coeffs[1].clone();
881 Some(c0.neg().div(&c1))
882 } else {
883 None
884 }
885 }
886
887 pub fn refine_interval(&mut self, target_width: &RnsRational) {
889 let (mut lo, mut hi) = self.interval.clone();
890 let sign_lo = self.annihilating_poly.sign_at(&lo);
891 if sign_lo == 0 {
893 self.interval = (lo.clone(), lo);
894 return;
895 }
896 if self.annihilating_poly.sign_at(&hi) == 0 {
897 self.interval = (hi.clone(), hi);
898 return;
899 }
900 while hi.sub(&lo) >= *target_width {
901 let mid = lo.midpoint(&hi);
902 let sm = self.annihilating_poly.sign_at(&mid);
903 if sm == 0 {
904 lo = mid.clone();
905 hi = mid;
906 break;
907 } else if sm == sign_lo {
908 lo = mid;
909 } else {
910 hi = mid;
911 }
912 }
913 self.interval = (lo, hi);
914 }
915
916 pub fn to_f64(&self) -> f64 {
918 let mut clone = self.clone();
919 let target = RnsRational::new(BigInt::one(), BigInt::one() << 60, self.channels.clone());
920 clone.refine_interval(&target);
921 clone.interval.0.midpoint(&clone.interval.1).to_f64()
922 }
923
924 pub fn sign(&self) -> i32 {
926 if self.annihilating_poly.degree() == 1 && self.annihilating_poly.coeffs[0].is_zero() {
929 return 0;
930 }
931 let mut clone = self.clone();
932 let zero = RnsRational::zero(self.channels.clone());
933 let mut target = RnsRational::new(BigInt::one(), BigInt::from(1024), self.channels.clone());
935 for _ in 0..200 {
936 if clone.interval.0 > zero {
937 return 1;
938 }
939 if clone.interval.1 < zero {
940 return -1;
941 }
942 clone.refine_interval(&target);
943 target = target.mul(&RnsRational::from_fraction(1, 2, self.channels.clone()));
944 }
945 clone.interval.0.midpoint(&clone.interval.1).signum()
946 }
947
948 pub fn neg(&self) -> Self {
950 let coeffs = self
951 .annihilating_poly
952 .coeffs
953 .iter()
954 .enumerate()
955 .map(|(i, c)| if i % 2 == 1 { c.neg() } else { c.clone() })
956 .collect();
957 let annihilating_poly = Polynomial::new(coeffs, self.channels.clone()).monic();
958 AlgebraicNumber {
959 annihilating_poly,
960 interval: (self.interval.1.neg(), self.interval.0.neg()),
961 channels: self.channels.clone(),
962 }
963 }
964
965 pub fn recip(&self) -> Self {
967 let mut coeffs = self.annihilating_poly.coeffs.clone();
968 coeffs.reverse();
969 let annihilating_poly = Polynomial::new(coeffs, self.channels.clone()).monic();
970 let v = self.to_f64();
971 Self::reconstruct(annihilating_poly, 1.0 / v, self.channels.clone())
972 }
973
974 pub fn add(&self, other: &Self) -> Self {
976 let channels = self.channels.clone();
977 let a = lift_const(&self.annihilating_poly);
978 let b = shift_sub(&other.annihilating_poly, &channels);
979 let res = resultant_y(&a, &b, &channels);
980 let value = self.to_f64() + other.to_f64();
981 Self::reconstruct(res, value, channels)
982 }
983
984 pub fn mul(&self, other: &Self) -> Self {
986 let channels = self.channels.clone();
987 let a = lift_const(&self.annihilating_poly);
988 let b = invert_scale(&other.annihilating_poly, &channels);
989 let res = resultant_y(&a, &b, &channels);
990 let value = self.to_f64() * other.to_f64();
991 Self::reconstruct(res, value, channels)
992 }
993
994 fn reconstruct(res: Polynomial, approx: f64, channels: Basis) -> Self {
997 let factors = res.factor_over_q();
998 let mut best: Option<(AlgebraicNumber, f64)> = None;
1000 for f in &factors {
1001 for (lo, hi) in f.isolate_real_roots() {
1002 let mut cand = AlgebraicNumber {
1003 annihilating_poly: f.clone(),
1004 interval: (lo, hi),
1005 channels: channels.clone(),
1006 };
1007 let target = RnsRational::new(BigInt::one(), BigInt::from(1_000_000), channels.clone());
1008 cand.refine_interval(&target);
1009 let dist = (cand.to_f64() - approx).abs();
1010 if best.as_ref().map(|(_, d)| dist < *d).unwrap_or(true) {
1011 best = Some((cand, dist));
1012 }
1013 }
1014 }
1015 best.map(|(a, _)| a).expect("resultant had no real root near target")
1016 }
1017
1018 fn factor_for_interval(
1019 factors: &[Polynomial],
1020 lo: &RnsRational,
1021 hi: &RnsRational,
1022 ) -> Option<Polynomial> {
1023 for f in factors {
1024 if f.sturm_root_count(lo, hi) >= 1 {
1025 return Some(f.monic());
1026 }
1027 }
1028 None
1029 }
1030}
1031
1032#[cfg(test)]
1033mod tests {
1034 use super::*;
1035
1036 fn ch() -> Basis {
1037 Basis::standard()
1038 }
1039
1040 #[test]
1041 fn sqrt2_annihilating_poly() {
1042 let s = AlgebraicNumber::sqrt(2, ch());
1043 assert_eq!(s.degree(), 2);
1044 assert_eq!(
1046 s.annihilating_poly.sign_at(&RnsRational::from_int(1, ch())),
1047 -1
1048 );
1049 assert!(s.interval.0 < s.interval.1);
1050 assert!(s.to_rational().is_none());
1051 assert!((s.to_f64() - 2f64.sqrt()).abs() < 1e-9);
1052 }
1053
1054 #[test]
1055 fn from_rational_roundtrip() {
1056 let r = RnsRational::from_fraction(3, 5, ch());
1057 let a = AlgebraicNumber::from_rational(r.clone());
1058 assert_eq!(a.to_rational(), Some(r));
1059 }
1060
1061 #[test]
1062 fn sturm_counts() {
1063 let p = Polynomial::from_int_coeffs(&[-2, 0, 1], ch());
1065 let r = |a: i64, b: i64| {
1066 p.sturm_root_count(
1067 &RnsRational::from_int(a, ch()),
1068 &RnsRational::from_int(b, ch()),
1069 )
1070 };
1071 assert_eq!(r(-2, -1), 1);
1072 assert_eq!(r(1, 2), 1);
1073 assert_eq!(r(-2, 2), 2);
1074 }
1075
1076 #[test]
1077 fn sqrt2_times_sqrt2_is_two() {
1078 let s = AlgebraicNumber::sqrt(2, ch());
1079 let p = s.mul(&s);
1080 assert_eq!(p.degree(), 1);
1082 assert_eq!(p.to_rational(), Some(RnsRational::from_int(2, ch())));
1083 }
1084
1085 #[test]
1086 fn sqrt2_times_sqrt3_is_sqrt6() {
1087 let s2 = AlgebraicNumber::sqrt(2, ch());
1088 let s3 = AlgebraicNumber::sqrt(3, ch());
1089 let p = s2.mul(&s3);
1090 assert_eq!(p.degree(), 2);
1091 let expected = Polynomial::from_int_coeffs(&[-6, 0, 1], ch()).monic();
1093 assert_eq!(p.annihilating_poly, expected);
1094 }
1095
1096 #[test]
1097 fn sqrt2_plus_sqrt2_is_2sqrt2() {
1098 let s = AlgebraicNumber::sqrt(2, ch());
1099 let p = s.add(&s);
1100 assert_eq!(p.degree(), 2);
1102 let expected = Polynomial::from_int_coeffs(&[-8, 0, 1], ch()).monic();
1103 assert_eq!(p.annihilating_poly, expected);
1104 }
1105
1106 #[test]
1107 fn refine_narrows() {
1108 let mut s = AlgebraicNumber::sqrt(2, ch());
1109 let target = RnsRational::new(BigInt::one(), BigInt::from(10).pow(20), ch());
1110 s.refine_interval(&target);
1111 assert!(s.interval.1.sub(&s.interval.0) < target);
1112 }
1113
1114 fn det_bigint(mut m: Vec<Vec<BigInt>>) -> BigInt {
1118 let n = m.len();
1119 if n == 0 {
1120 return BigInt::one();
1121 }
1122 let mut sign = 1i32;
1123 let mut prev = BigInt::one();
1124 for k in 0..n - 1 {
1125 if m[k][k].is_zero() {
1126 let swap = (k + 1..n).find(|&i| !m[i][k].is_zero());
1127 match swap {
1128 Some(i) => {
1129 m.swap(k, i);
1130 sign = -sign;
1131 }
1132 None => return BigInt::zero(),
1133 }
1134 }
1135 for i in k + 1..n {
1136 for j in k + 1..n {
1137 let num = &m[i][j] * &m[k][k] - &m[i][k] * &m[k][j];
1138 m[i][j] = num / &prev;
1139 }
1140 m[i][k] = BigInt::zero();
1141 }
1142 prev = m[k][k].clone();
1143 }
1144 let det = m[n - 1][n - 1].clone();
1145 if sign < 0 {
1146 -det
1147 } else {
1148 det
1149 }
1150 }
1151
1152 fn eval_int_poly(poly: &[BigInt], x: &BigInt) -> BigInt {
1153 let mut acc = BigInt::zero();
1154 for c in poly.iter().rev() {
1155 acc = acc * x + c;
1156 }
1157 acc
1158 }
1159
1160 struct Lcg(u64);
1162 impl Lcg {
1163 fn next(&mut self) -> u64 {
1164 self.0 = self.0.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
1165 self.0 >> 33
1166 }
1167 fn coeff(&mut self, range: i64) -> i64 {
1169 (self.next() as i64).rem_euclid(2 * range + 1) - range
1170 }
1171 }
1172
1173 #[test]
1174 fn multimodular_det_matches_bigint_scalar() {
1175 let mut rng = Lcg(0x1234_5678_9abc_def0);
1176 for n in 1..=6usize {
1177 for _ in 0..20 {
1178 let scalar: Vec<Vec<BigInt>> = (0..n)
1180 .map(|_| (0..n).map(|_| BigInt::from(rng.coeff(50))).collect())
1181 .collect();
1182 let int_mat: Vec<Vec<IntPoly>> = scalar
1183 .iter()
1184 .map(|row| row.iter().map(|c| vec![c.clone()]).collect())
1185 .collect();
1186
1187 let expected = det_bigint(scalar);
1188 let got = multimodular_det(&int_mat);
1189 let got0 = got.first().cloned().unwrap_or_else(BigInt::zero);
1191 assert_eq!(got0, expected, "n={n}");
1192 assert!(got.iter().skip(1).all(|c| c.is_zero()));
1193 }
1194 }
1195 }
1196
1197 #[test]
1198 fn multimodular_det_matches_bigint_polynomial() {
1199 let mut rng = Lcg(0xfeed_face_cafe_d00d);
1200 for n in 1..=5usize {
1201 for _ in 0..20 {
1202 let int_mat: Vec<Vec<IntPoly>> = (0..n)
1204 .map(|_| {
1205 (0..n)
1206 .map(|_| {
1207 vec![
1208 BigInt::from(rng.coeff(20)),
1209 BigInt::from(rng.coeff(20)),
1210 ]
1211 })
1212 .collect()
1213 })
1214 .collect();
1215
1216 let det_poly = multimodular_det(&int_mat);
1217
1218 for x in [-3i64, -1, 0, 2, 5, 11] {
1221 let xb = BigInt::from(x);
1222 let scalar: Vec<Vec<BigInt>> = int_mat
1223 .iter()
1224 .map(|row| row.iter().map(|e| eval_int_poly(e, &xb)).collect())
1225 .collect();
1226 let expected = det_bigint(scalar);
1227 let got = eval_int_poly(&det_poly, &xb);
1228 assert_eq!(got, expected, "n={n}, x={x}");
1229 }
1230 }
1231 }
1232 }
1233}