1use num_bigint::BigInt;
15use num_integer::Integer;
16use num_traits::{One, Signed, ToPrimitive, Zero};
17
18use crate::primes::lcm as u64_lcm;
19use crate::rational::RnsRational;
20use crate::rns::Channels;
21
22#[derive(Clone, Debug)]
27pub struct Polynomial {
28 pub coeffs: Vec<RnsRational>,
29 pub channels: Channels,
30}
31
32impl Polynomial {
33 pub fn new(coeffs: Vec<RnsRational>, channels: Channels) -> Self {
35 let mut p = Polynomial { coeffs, channels };
36 p.trim();
37 p
38 }
39
40 pub fn from_int_coeffs(coeffs: &[i64], channels: Channels) -> Self {
42 let c = coeffs
43 .iter()
44 .map(|&v| RnsRational::from_int(v, channels.clone()))
45 .collect();
46 Self::new(c, channels)
47 }
48
49 fn r_zero(&self) -> RnsRational {
50 RnsRational::zero(self.channels.clone())
51 }
52 fn r_one(&self) -> RnsRational {
53 RnsRational::from_int(1, self.channels.clone())
54 }
55
56 fn trim(&mut self) {
57 while self.coeffs.len() > 0 && self.coeffs.last().unwrap().is_zero() {
58 self.coeffs.pop();
59 }
60 }
61
62 pub fn zero(channels: Channels) -> Self {
64 Polynomial { coeffs: vec![], channels }
65 }
66
67 pub fn one(channels: Channels) -> Self {
69 Self::from_int_coeffs(&[1], channels)
70 }
71
72 pub fn constant(c: RnsRational) -> Self {
74 let ch = c.channels.clone();
75 Self::new(vec![c], ch)
76 }
77
78 pub fn is_zero(&self) -> bool {
80 self.coeffs.is_empty()
81 }
82
83 pub fn degree(&self) -> usize {
86 self.coeffs.len().saturating_sub(1)
87 }
88
89 pub fn leading(&self) -> RnsRational {
91 self.coeffs.last().cloned().unwrap_or_else(|| self.r_zero())
92 }
93
94 pub fn eval(&self, x: &RnsRational) -> RnsRational {
96 let mut acc = self.r_zero();
97 for c in self.coeffs.iter().rev() {
98 acc = acc.mul(x).add(c);
99 }
100 acc
101 }
102
103 pub fn sign_at(&self, x: &RnsRational) -> i32 {
105 self.eval(x).signum()
106 }
107
108 pub fn derivative(&self) -> Self {
110 if self.coeffs.len() <= 1 {
111 return Self::zero(self.channels.clone());
112 }
113 let coeffs = self
114 .coeffs
115 .iter()
116 .enumerate()
117 .skip(1)
118 .map(|(i, c)| c.mul(&RnsRational::from_int(i as i64, self.channels.clone())))
119 .collect();
120 Self::new(coeffs, self.channels.clone())
121 }
122
123 pub fn scalar_mul(&self, s: &RnsRational) -> Self {
125 Self::new(
126 self.coeffs.iter().map(|c| c.mul(s)).collect(),
127 self.channels.clone(),
128 )
129 }
130
131 pub fn add(&self, other: &Self) -> Self {
133 let n = self.coeffs.len().max(other.coeffs.len());
134 let mut out = Vec::with_capacity(n);
135 for i in 0..n {
136 let a = self.coeffs.get(i).cloned().unwrap_or_else(|| self.r_zero());
137 let b = other.coeffs.get(i).cloned().unwrap_or_else(|| self.r_zero());
138 out.push(a.add(&b));
139 }
140 Self::new(out, self.channels.clone())
141 }
142
143 pub fn sub(&self, other: &Self) -> Self {
145 self.add(&other.scalar_mul(&RnsRational::from_int(-1, self.channels.clone())))
146 }
147
148 pub fn mul(&self, other: &Self) -> Self {
150 if self.is_zero() || other.is_zero() {
151 return Self::zero(self.channels.clone());
152 }
153 let mut out = vec![self.r_zero(); self.coeffs.len() + other.coeffs.len() - 1];
154 for (i, a) in self.coeffs.iter().enumerate() {
155 for (j, b) in other.coeffs.iter().enumerate() {
156 out[i + j] = out[i + j].add(&a.mul(b));
157 }
158 }
159 Self::new(out, self.channels.clone())
160 }
161
162 pub fn divmod(&self, divisor: &Self) -> (Self, Self) {
165 assert!(!divisor.is_zero(), "polynomial division by zero");
166 let mut rem = self.clone();
167 let d_deg = divisor.degree();
168 let d_lead = divisor.leading();
169 if self.is_zero() || self.degree() < d_deg {
170 return (Self::zero(self.channels.clone()), rem);
171 }
172 let mut quot = vec![self.r_zero(); self.degree() - d_deg + 1];
173 while !rem.is_zero() && rem.degree() >= d_deg {
174 let shift = rem.degree() - d_deg;
175 let factor = rem.leading().div(&d_lead);
176 quot[shift] = factor.clone();
177 let mut sub_coeffs = vec![self.r_zero(); shift];
179 for c in &divisor.coeffs {
180 sub_coeffs.push(c.mul(&factor));
181 }
182 let sub = Self::new(sub_coeffs, self.channels.clone());
183 rem = rem.sub(&sub);
184 }
185 (Self::new(quot, self.channels.clone()), rem)
186 }
187
188 pub fn rem(&self, divisor: &Self) -> Self {
190 self.divmod(divisor).1
191 }
192
193 pub fn div_exact(&self, divisor: &Self) -> Self {
195 let (q, r) = self.divmod(divisor);
196 debug_assert!(r.is_zero(), "div_exact: non-zero remainder");
197 q
198 }
199
200 pub fn monic(&self) -> Self {
202 if self.is_zero() {
203 return self.clone();
204 }
205 let inv = self.r_one().div(&self.leading());
206 self.scalar_mul(&inv)
207 }
208
209 pub fn gcd(a: &Self, b: &Self) -> Self {
211 let mut x = a.clone();
212 let mut y = b.clone();
213 while !y.is_zero() {
214 let r = x.rem(&y);
215 x = y;
216 y = r;
217 }
218 x.monic()
219 }
220
221 pub fn squarefree(&self) -> Self {
223 if self.is_zero() || self.degree() == 0 {
224 return self.monic();
225 }
226 let g = Self::gcd(self, &self.derivative());
227 self.div_exact(&g).monic()
228 }
229
230 pub fn sturm_sequence(&self) -> Vec<Self> {
232 let mut seq = vec![self.clone(), self.derivative()];
233 while !seq.last().unwrap().is_zero() {
234 let n = seq.len();
235 let r = seq[n - 2].rem(&seq[n - 1]);
236 if r.is_zero() {
237 break;
238 }
239 seq.push(r.scalar_mul(&RnsRational::from_int(-1, self.channels.clone())));
240 }
241 seq
242 }
243
244 pub fn sign_changes(seq: &[Self], x: &RnsRational) -> usize {
246 let mut last = 0i32;
247 let mut changes = 0usize;
248 for s in seq {
249 let sign = s.sign_at(x);
250 if sign != 0 {
251 if last != 0 && sign != last {
252 changes += 1;
253 }
254 last = sign;
255 }
256 }
257 changes
258 }
259
260 pub fn sturm_root_count(&self, a: &RnsRational, b: &RnsRational) -> usize {
262 let seq = self.sturm_sequence();
263 let va = Self::sign_changes(&seq, a);
264 let vb = Self::sign_changes(&seq, b);
265 va.saturating_sub(vb)
266 }
267
268 pub fn root_bound(&self) -> RnsRational {
270 if self.is_zero() || self.degree() == 0 {
271 return self.r_one();
272 }
273 let lead = self.leading();
274 let mut max_ratio = self.r_zero();
275 for c in &self.coeffs[..self.coeffs.len() - 1] {
276 let ratio = c.div(&lead).abs();
277 if ratio > max_ratio {
278 max_ratio = ratio;
279 }
280 }
281 self.r_one().add(&max_ratio)
282 }
283
284 pub fn isolate_real_roots(&self) -> Vec<(RnsRational, RnsRational)> {
287 let sf = self.squarefree();
288 if sf.degree() == 0 {
289 return Vec::new();
290 }
291 let seq = sf.sturm_sequence();
292 let b = sf.root_bound();
293 let lo = b.neg();
294 let hi = b;
295 let mut out = Vec::new();
296 let min_width = RnsRational::new(BigInt::one(), BigInt::one() << 80, sf.channels.clone());
298 Self::isolate_rec(&seq, &lo, &hi, &min_width, &mut out);
299 out.sort_by(|x, y| x.0.cmp(&y.0));
300 out
301 }
302
303 fn isolate_rec(
304 seq: &[Self],
305 lo: &RnsRational,
306 hi: &RnsRational,
307 min_width: &RnsRational,
308 out: &mut Vec<(RnsRational, RnsRational)>,
309 ) {
310 let cnt = Self::sign_changes(seq, lo).saturating_sub(Self::sign_changes(seq, hi));
311 if cnt == 0 {
312 return;
313 }
314 if cnt == 1 {
315 out.push((lo.clone(), hi.clone()));
316 return;
317 }
318 let width = hi.sub(lo);
319 if width < *min_width {
320 out.push((lo.clone(), hi.clone()));
321 return;
322 }
323 let mid = lo.midpoint(hi);
324 Self::isolate_rec(seq, lo, &mid, min_width, out);
325 Self::isolate_rec(seq, &mid, hi, min_width, out);
326 }
327
328 fn primitive_int_coeffs(&self) -> Vec<BigInt> {
333 if self.is_zero() {
334 return vec![BigInt::zero()];
335 }
336 let mut denom_lcm = 1u64;
338 let mut pairs = Vec::new();
339 for c in &self.coeffs {
340 let (p, q) = c.to_pair();
341 let qu = q.to_u64().unwrap_or(1);
342 denom_lcm = u64_lcm(denom_lcm, qu);
343 pairs.push((p, q));
344 }
345 let big_lcm = BigInt::from(denom_lcm);
346 let mut ints: Vec<BigInt> = pairs
347 .iter()
348 .map(|(p, q)| p * (&big_lcm / q))
349 .collect();
350 let mut content = BigInt::zero();
352 for v in &ints {
353 content = content.gcd(v);
354 }
355 if !content.is_zero() && content != BigInt::one() {
356 for v in &mut ints {
357 *v /= &content;
358 }
359 }
360 ints
361 }
362
363 pub fn find_rational_root(&self) -> Option<RnsRational> {
365 let ints = self.primitive_int_coeffs();
366 if ints.len() <= 1 {
367 return None;
368 }
369 let a0 = ints.first().unwrap().clone();
370 let an = ints.last().unwrap().clone();
371 if a0.is_zero() {
373 return Some(self.r_zero());
374 }
375 let p_divs = divisors(&a0.abs());
376 let q_divs = divisors(&an.abs());
377 for p in &p_divs {
378 for q in &q_divs {
379 for sign in [1i64, -1] {
380 let cand = RnsRational::new(
381 BigInt::from(sign) * p,
382 q.clone(),
383 self.channels.clone(),
384 );
385 if self.eval(&cand).is_zero() {
386 return Some(cand);
387 }
388 }
389 }
390 }
391 None
392 }
393
394 pub fn factor_over_q(&self) -> Vec<Self> {
398 let mut work = self.squarefree();
399 let mut factors = Vec::new();
400 loop {
401 if work.degree() == 0 {
402 break;
403 }
404 match work.find_rational_root() {
405 Some(r) => {
406 let lin = Self::new(
408 vec![r.neg(), work.r_one()],
409 self.channels.clone(),
410 );
411 work = work.div_exact(&lin);
412 factors.push(lin.monic());
413 }
414 None => break,
415 }
416 }
417 if work.degree() >= 1 {
418 factors.push(work.monic());
419 }
420 factors
421 }
422}
423
424impl PartialEq for Polynomial {
425 fn eq(&self, other: &Self) -> bool {
426 self.coeffs == other.coeffs
427 }
428}
429impl Eq for Polynomial {}
430
431fn divisors(n: &BigInt) -> Vec<BigInt> {
433 let mut out = vec![BigInt::one()];
434 let n_u = match n.to_u128() {
435 Some(v) if v > 0 => v,
436 _ => return out,
437 };
438 let mut divs = Vec::new();
439 let mut d = 1u128;
440 while d * d <= n_u {
441 if n_u % d == 0 {
442 divs.push(d);
443 if d != n_u / d {
444 divs.push(n_u / d);
445 }
446 }
447 d += 1;
448 }
449 out.clear();
450 for v in divs {
451 out.push(BigInt::from(v));
452 }
453 out
454}
455
456type BiPoly = Vec<Polynomial>;
461
462fn bi_degree(p: &BiPoly) -> usize {
463 let mut d = 0;
464 for (i, c) in p.iter().enumerate() {
465 if !c.is_zero() {
466 d = i;
467 }
468 }
469 d
470}
471
472fn resultant_y(a: &BiPoly, b: &BiPoly, channels: &Channels) -> Polynomial {
475 let m = bi_degree(a);
476 let n = bi_degree(b);
477 let size = m + n;
478 if size == 0 {
479 return Polynomial::one(channels.clone());
480 }
481 let zero = Polynomial::zero(channels.clone());
482 let mut mat = vec![vec![zero.clone(); size]; size];
483
484 for i in 0..n {
486 for j in 0..=m {
487 mat[i][i + j] = a[m - j].clone();
488 }
489 }
490 for i in 0..m {
492 for j in 0..=n {
493 mat[n + i][i + j] = b[n - j].clone();
494 }
495 }
496
497 bareiss_det(mat, channels)
498}
499
500fn bareiss_det(mut m: Vec<Vec<Polynomial>>, channels: &Channels) -> Polynomial {
502 let n = m.len();
503 if n == 0 {
504 return Polynomial::one(channels.clone());
505 }
506 let mut sign = 1i32;
507 let mut prev = Polynomial::one(channels.clone());
508 for k in 0..n - 1 {
509 if m[k][k].is_zero() {
510 let mut swap = None;
512 for i in (k + 1)..n {
513 if !m[i][k].is_zero() {
514 swap = Some(i);
515 break;
516 }
517 }
518 match swap {
519 Some(i) => {
520 m.swap(k, i);
521 sign = -sign;
522 }
523 None => return Polynomial::zero(channels.clone()),
524 }
525 }
526 for i in (k + 1)..n {
527 for j in (k + 1)..n {
528 let term1 = m[i][j].mul(&m[k][k]);
529 let term2 = m[i][k].mul(&m[k][j]);
530 let numer = term1.sub(&term2);
531 m[i][j] = numer.div_exact(&prev);
532 }
533 m[i][k] = Polynomial::zero(channels.clone());
534 }
535 prev = m[k][k].clone();
536 }
537 let det = m[n - 1][n - 1].clone();
538 if sign < 0 {
539 det.scalar_mul(&RnsRational::from_int(-1, channels.clone()))
540 } else {
541 det
542 }
543}
544
545fn lift_const(p: &Polynomial) -> BiPoly {
547 p.coeffs.iter().map(|c| Polynomial::constant(c.clone())).collect()
548}
549
550fn shift_sub(q: &Polynomial, channels: &Channels) -> BiPoly {
552 let x_poly = Polynomial::from_int_coeffs(&[0, 1], channels.clone());
554 let neg_one = Polynomial::from_int_coeffs(&[-1], channels.clone());
555 let base: BiPoly = vec![x_poly, neg_one];
556
557 let mut acc: BiPoly = vec![Polynomial::zero(channels.clone())];
558 let mut power: BiPoly = vec![Polynomial::one(channels.clone())]; for (j, c) in q.coeffs.iter().enumerate() {
560 if j > 0 {
561 power = bi_mul(&power, &base, channels);
562 }
563 let term = bi_scalar(&power, c);
564 acc = bi_add(&acc, &term, channels);
565 }
566 acc
567}
568
569fn invert_scale(q: &Polynomial, channels: &Channels) -> BiPoly {
571 let d = q.degree();
572 let mut out: BiPoly = vec![Polynomial::zero(channels.clone()); d + 1];
573 for (j, c) in q.coeffs.iter().enumerate() {
574 let mut xj = vec![0i64; j + 1];
576 xj[j] = 1;
577 let x_pow = Polynomial::from_int_coeffs(&xj, channels.clone());
578 out[d - j] = x_pow.scalar_mul(c);
579 }
580 out
581}
582
583fn bi_add(a: &BiPoly, b: &BiPoly, channels: &Channels) -> BiPoly {
584 let n = a.len().max(b.len());
585 (0..n)
586 .map(|i| {
587 let za = a.get(i).cloned().unwrap_or_else(|| Polynomial::zero(channels.clone()));
588 let zb = b.get(i).cloned().unwrap_or_else(|| Polynomial::zero(channels.clone()));
589 za.add(&zb)
590 })
591 .collect()
592}
593
594fn bi_scalar(a: &BiPoly, s: &RnsRational) -> BiPoly {
595 a.iter().map(|c| c.scalar_mul(s)).collect()
596}
597
598fn bi_mul(a: &BiPoly, b: &BiPoly, channels: &Channels) -> BiPoly {
599 if a.is_empty() || b.is_empty() {
600 return vec![Polynomial::zero(channels.clone())];
601 }
602 let mut out = vec![Polynomial::zero(channels.clone()); a.len() + b.len() - 1];
603 for (i, ca) in a.iter().enumerate() {
604 for (j, cb) in b.iter().enumerate() {
605 out[i + j] = out[i + j].add(&ca.mul(cb));
606 }
607 }
608 out
609}
610
611#[derive(Clone, Debug)]
613pub struct AlgebraicNumber {
614 pub min_poly: Polynomial,
616 pub interval: (RnsRational, RnsRational),
618 pub channels: Channels,
619}
620
621impl AlgebraicNumber {
622 pub fn sqrt(n: u64, channels: Channels) -> Self {
624 let min_poly = Polynomial::from_int_coeffs(&[-(n as i64), 0, 1], channels.clone());
626 let lo = RnsRational::from_int(0, channels.clone());
627 let hi = RnsRational::from_int(n as i64 + 1, channels.clone());
628 Self::from_min_poly_interval(min_poly, lo, hi, channels)
629 }
630
631 pub fn cbrt(n: u64, channels: Channels) -> Self {
633 let min_poly = Polynomial::from_int_coeffs(&[-(n as i64), 0, 0, 1], channels.clone());
634 let lo = RnsRational::from_int(0, channels.clone());
635 let hi = RnsRational::from_int(n as i64 + 1, channels.clone());
636 Self::from_min_poly_interval(min_poly, lo, hi, channels)
637 }
638
639 pub fn from_rational(r: RnsRational) -> Self {
641 let channels = r.channels.clone();
642 let min_poly = Polynomial::new(
643 vec![r.neg(), RnsRational::from_int(1, channels.clone())],
644 channels.clone(),
645 );
646 let lo = r.sub(&RnsRational::from_int(1, channels.clone()));
647 let hi = r.add(&RnsRational::from_int(1, channels.clone()));
648 AlgebraicNumber { min_poly, interval: (lo, hi), channels }
649 }
650
651 pub fn from_poly_root(p: Polynomial, root_index: usize, channels: Channels) -> Self {
653 let roots = p.isolate_real_roots();
654 let (lo, hi) = roots
655 .get(root_index)
656 .cloned()
657 .expect("root_index out of range");
658 let factors = p.factor_over_q();
660 let min_poly = Self::factor_for_interval(&factors, &lo, &hi).unwrap_or(p);
661 AlgebraicNumber { min_poly, interval: (lo, hi), channels }
662 }
663
664 fn from_min_poly_interval(
665 min_poly: Polynomial,
666 lo: RnsRational,
667 hi: RnsRational,
668 channels: Channels,
669 ) -> Self {
670 let mut a = AlgebraicNumber { min_poly, interval: (lo, hi), channels };
671 let target = RnsRational::new(BigInt::one(), BigInt::from(1_000_000), a.channels.clone());
673 a.refine_interval(&target);
674 a
675 }
676
677 pub fn degree(&self) -> usize {
679 self.min_poly.degree()
680 }
681
682 pub fn to_rational(&self) -> Option<RnsRational> {
684 if self.min_poly.degree() == 1 {
685 let c0 = self.min_poly.coeffs[0].clone();
687 let c1 = self.min_poly.coeffs[1].clone();
688 Some(c0.neg().div(&c1))
689 } else {
690 None
691 }
692 }
693
694 pub fn refine_interval(&mut self, target_width: &RnsRational) {
696 let (mut lo, mut hi) = self.interval.clone();
697 let sign_lo = self.min_poly.sign_at(&lo);
698 if sign_lo == 0 {
700 self.interval = (lo.clone(), lo);
701 return;
702 }
703 if self.min_poly.sign_at(&hi) == 0 {
704 self.interval = (hi.clone(), hi);
705 return;
706 }
707 while hi.sub(&lo) >= *target_width {
708 let mid = lo.midpoint(&hi);
709 let sm = self.min_poly.sign_at(&mid);
710 if sm == 0 {
711 lo = mid.clone();
712 hi = mid;
713 break;
714 } else if sm == sign_lo {
715 lo = mid;
716 } else {
717 hi = mid;
718 }
719 }
720 self.interval = (lo, hi);
721 }
722
723 pub fn to_f64(&self) -> f64 {
725 let mut clone = self.clone();
726 let target = RnsRational::new(BigInt::one(), BigInt::one() << 60, self.channels.clone());
727 clone.refine_interval(&target);
728 clone.interval.0.midpoint(&clone.interval.1).to_f64()
729 }
730
731 pub fn sign(&self) -> i32 {
733 if self.min_poly.degree() == 1 && self.min_poly.coeffs[0].is_zero() {
736 return 0;
737 }
738 let mut clone = self.clone();
739 let zero = RnsRational::zero(self.channels.clone());
740 let mut target = RnsRational::new(BigInt::one(), BigInt::from(1024), self.channels.clone());
742 for _ in 0..200 {
743 if clone.interval.0 > zero {
744 return 1;
745 }
746 if clone.interval.1 < zero {
747 return -1;
748 }
749 clone.refine_interval(&target);
750 target = target.mul(&RnsRational::from_fraction(1, 2, self.channels.clone()));
751 }
752 clone.interval.0.midpoint(&clone.interval.1).signum()
753 }
754
755 pub fn neg(&self) -> Self {
757 let coeffs = self
758 .min_poly
759 .coeffs
760 .iter()
761 .enumerate()
762 .map(|(i, c)| if i % 2 == 1 { c.neg() } else { c.clone() })
763 .collect();
764 let min_poly = Polynomial::new(coeffs, self.channels.clone()).monic();
765 AlgebraicNumber {
766 min_poly,
767 interval: (self.interval.1.neg(), self.interval.0.neg()),
768 channels: self.channels.clone(),
769 }
770 }
771
772 pub fn recip(&self) -> Self {
774 let mut coeffs = self.min_poly.coeffs.clone();
775 coeffs.reverse();
776 let min_poly = Polynomial::new(coeffs, self.channels.clone()).monic();
777 let v = self.to_f64();
778 Self::reconstruct(min_poly, 1.0 / v, self.channels.clone())
779 }
780
781 pub fn add(&self, other: &Self) -> Self {
783 let channels = self.channels.clone();
784 let a = lift_const(&self.min_poly);
785 let b = shift_sub(&other.min_poly, &channels);
786 let res = resultant_y(&a, &b, &channels);
787 let value = self.to_f64() + other.to_f64();
788 Self::reconstruct(res, value, channels)
789 }
790
791 pub fn mul(&self, other: &Self) -> Self {
793 let channels = self.channels.clone();
794 let a = lift_const(&self.min_poly);
795 let b = invert_scale(&other.min_poly, &channels);
796 let res = resultant_y(&a, &b, &channels);
797 let value = self.to_f64() * other.to_f64();
798 Self::reconstruct(res, value, channels)
799 }
800
801 fn reconstruct(res: Polynomial, approx: f64, channels: Channels) -> Self {
804 let factors = res.factor_over_q();
805 let mut best: Option<(AlgebraicNumber, f64)> = None;
807 for f in &factors {
808 for (lo, hi) in f.isolate_real_roots() {
809 let mut cand = AlgebraicNumber {
810 min_poly: f.clone(),
811 interval: (lo, hi),
812 channels: channels.clone(),
813 };
814 let target = RnsRational::new(BigInt::one(), BigInt::from(1_000_000), channels.clone());
815 cand.refine_interval(&target);
816 let dist = (cand.to_f64() - approx).abs();
817 if best.as_ref().map(|(_, d)| dist < *d).unwrap_or(true) {
818 best = Some((cand, dist));
819 }
820 }
821 }
822 best.map(|(a, _)| a).expect("resultant had no real root near target")
823 }
824
825 fn factor_for_interval(
826 factors: &[Polynomial],
827 lo: &RnsRational,
828 hi: &RnsRational,
829 ) -> Option<Polynomial> {
830 for f in factors {
831 if f.sturm_root_count(lo, hi) >= 1 {
832 return Some(f.monic());
833 }
834 }
835 None
836 }
837}
838
839#[cfg(test)]
840mod tests {
841 use super::*;
842
843 fn ch() -> Channels {
844 Channels::standard(32)
845 }
846
847 #[test]
848 fn sqrt2_min_poly() {
849 let s = AlgebraicNumber::sqrt(2, ch());
850 assert_eq!(s.degree(), 2);
851 assert_eq!(
853 s.min_poly.sign_at(&RnsRational::from_int(1, ch())),
854 -1
855 );
856 assert!(s.interval.0 < s.interval.1);
857 assert!(s.to_rational().is_none());
858 assert!((s.to_f64() - 2f64.sqrt()).abs() < 1e-9);
859 }
860
861 #[test]
862 fn from_rational_roundtrip() {
863 let r = RnsRational::from_fraction(3, 5, ch());
864 let a = AlgebraicNumber::from_rational(r.clone());
865 assert_eq!(a.to_rational(), Some(r));
866 }
867
868 #[test]
869 fn sturm_counts() {
870 let p = Polynomial::from_int_coeffs(&[-2, 0, 1], ch());
872 let r = |a: i64, b: i64| {
873 p.sturm_root_count(
874 &RnsRational::from_int(a, ch()),
875 &RnsRational::from_int(b, ch()),
876 )
877 };
878 assert_eq!(r(-2, -1), 1);
879 assert_eq!(r(1, 2), 1);
880 assert_eq!(r(-2, 2), 2);
881 }
882
883 #[test]
884 fn sqrt2_times_sqrt2_is_two() {
885 let s = AlgebraicNumber::sqrt(2, ch());
886 let p = s.mul(&s);
887 assert_eq!(p.degree(), 1);
889 assert_eq!(p.to_rational(), Some(RnsRational::from_int(2, ch())));
890 }
891
892 #[test]
893 fn sqrt2_times_sqrt3_is_sqrt6() {
894 let s2 = AlgebraicNumber::sqrt(2, ch());
895 let s3 = AlgebraicNumber::sqrt(3, ch());
896 let p = s2.mul(&s3);
897 assert_eq!(p.degree(), 2);
898 let expected = Polynomial::from_int_coeffs(&[-6, 0, 1], ch()).monic();
900 assert_eq!(p.min_poly, expected);
901 }
902
903 #[test]
904 fn sqrt2_plus_sqrt2_is_2sqrt2() {
905 let s = AlgebraicNumber::sqrt(2, ch());
906 let p = s.add(&s);
907 assert_eq!(p.degree(), 2);
909 let expected = Polynomial::from_int_coeffs(&[-8, 0, 1], ch()).monic();
910 assert_eq!(p.min_poly, expected);
911 }
912
913 #[test]
914 fn refine_narrows() {
915 let mut s = AlgebraicNumber::sqrt(2, ch());
916 let target = RnsRational::new(BigInt::one(), BigInt::from(10).pow(20), ch());
917 s.refine_interval(&target);
918 assert!(s.interval.1.sub(&s.interval.0) < target);
919 }
920}