1use crate::numeric::{rational_from_f64_exact, rational_to_f64_nearest, BigInt, Rational};
25use crate::poly::{Poly, RootInterval};
26use std::cmp::Ordering;
27use std::fmt;
28use std::hash::{Hash, Hasher};
29
30#[derive(Clone, Debug)]
32pub struct Algebraic {
33 min: Poly,
35 index: usize,
37 iv: RootInterval,
40}
41
42fn isqrt(n: &BigInt) -> BigInt {
44 if n.is_zero() {
45 return BigInt::zero();
46 }
47 let mut x = BigInt::from_i64(1).shl_bits(n.bit_len() / 2 + 1);
48 loop {
49 let y = x.add(&n.div_rem(&x).expect("x > 0").0).shr_bits(1).expect("nonnegative");
50 if y >= x {
51 return x;
52 }
53 x = y;
54 }
55}
56
57struct Quadratic {
59 a: Rational,
60 b: Rational,
61 d: BigInt,
62}
63
64fn half() -> Rational {
65 Rational::from_ratio_i64(1, 2).expect("2 is nonzero")
66}
67
68fn pow2_recip(bits: u32) -> Rational {
69 Rational::new(BigInt::from_i64(1), BigInt::from_i64(1).shl_bits(bits as u64))
70 .expect("a power of two is nonzero")
71}
72
73fn integer_root_floor(n: &BigInt, k: u32) -> BigInt {
75 if n.is_zero() {
76 return BigInt::zero();
77 }
78 let top = n.bit_len() / k as u64 + 1;
79 let mut s = BigInt::zero();
80 for bit in (0..=top).rev() {
81 let t = s.add(&BigInt::from_i64(1).shl_bits(bit));
82 if t.pow(k) <= *n {
83 s = t;
84 }
85 }
86 s
87}
88
89fn rational_root_bounds(x: &Rational, k: u32, bits: u32) -> (Rational, Rational) {
91 let scale = BigInt::from_i64(1).shl_bits(k as u64 * bits as u64);
92 let scaled = x.mul(&Rational::from_bigint(scale));
93 let lo_int = integer_root_floor(&scaled.floor(), k);
94 let ceil = scaled.ceil();
95 let mut hi_int = integer_root_floor(&ceil, k);
96 if hi_int.pow(k) < ceil {
97 hi_int = hi_int.add(&BigInt::from_i64(1));
98 }
99 let den = BigInt::from_i64(1).shl_bits(bits as u64);
100 (
101 Rational::new(lo_int, den.clone()).expect("nonzero"),
102 Rational::new(hi_int, den).expect("nonzero"),
103 )
104}
105
106fn interval_pow(lo: &Rational, hi: &Rational, e: u32) -> (Rational, Rational) {
108 let pl = lo.pow(e as i32).expect("nonnegative powers exist");
109 let ph = hi.pow(e as i32).expect("nonnegative powers exist");
110 if e % 2 == 1 || !lo.is_negative() {
111 (pl, ph)
112 } else if !hi.is_positive() {
113 (ph, pl)
114 } else {
115 (Rational::zero(), pl.max(ph))
116 }
117}
118
119fn interval_mul(a: &(Rational, Rational), b: &(Rational, Rational)) -> (Rational, Rational) {
120 let products = [a.0.mul(&b.0), a.0.mul(&b.1), a.1.mul(&b.0), a.1.mul(&b.1)];
121 let lo = products.iter().min().expect("four products").clone();
122 let hi = products.iter().max().expect("four products").clone();
123 (lo, hi)
124}
125
126pub(crate) fn resultant_in_y(p: &Poly, q_at: impl Fn(&Rational) -> Poly, degree: usize) -> Poly {
129 let xs: Vec<Rational> = (0..=degree as i64).map(Rational::from_i64).collect();
130 let mut d: Vec<Rational> = xs.iter().map(|x| p.resultant(&q_at(x))).collect();
131 for j in 1..=degree {
132 for i in (j..=degree).rev() {
133 let num = d[i].sub(&d[i - 1]);
134 let den = xs[i].sub(&xs[i - j]);
135 d[i] = num.div(&den).expect("interpolation points are distinct");
136 }
137 }
138 let mut poly = Poly::constant(d[degree].clone());
139 for j in (0..degree).rev() {
140 let factor = Poly::from_coeffs(vec![xs[j].negated(), Rational::one()]);
141 poly = poly.mul(&factor).add(&Poly::constant(d[j].clone()));
142 }
143 poly
144}
145
146fn split_square(n: &BigInt) -> (BigInt, BigInt) {
149 let mut rest = n.clone();
150 let mut k = BigInt::from_i64(1);
151 let mut d = BigInt::from_i64(1);
152 let mut p = 2i64;
153 while p < 10_000 {
154 let pb = BigInt::from_i64(p);
155 if pb.mul(&pb) > rest {
156 break;
157 }
158 let mut e = 0u32;
159 loop {
160 let (q, r) = rest.div_rem(&pb).expect("p is nonzero");
161 if !r.is_zero() {
162 break;
163 }
164 rest = q;
165 e += 1;
166 }
167 k = k.mul(&pb.pow(e / 2));
168 if e % 2 == 1 {
169 d = d.mul(&pb);
170 }
171 p += if p == 2 { 1 } else { 2 };
172 }
173 let s = integer_root_floor(&rest, 2);
174 if s.mul(&s) == rest {
175 k = k.mul(&s);
176 } else {
177 d = d.mul(&rest);
178 }
179 (k, d)
180}
181
182impl Algebraic {
183 pub fn from_i64(n: i64) -> Algebraic {
184 Self::from_rational(Rational::from_i64(n))
185 }
186
187 pub fn from_rational(r: Rational) -> Algebraic {
188 let min = Poly::from_bigints(&[r.numerator().negated(), r.denominator().clone()]);
189 Algebraic {
190 min,
191 index: 1,
192 iv: RootInterval {
193 lo: r.clone(),
194 hi: r,
195 },
196 }
197 }
198
199 pub fn from_f64(f: f64) -> Option<Algebraic> {
201 rational_from_f64_exact(f).map(Self::from_rational)
202 }
203
204 pub fn root_of(p: &Poly, k: usize) -> Option<Algebraic> {
206 if k == 0 || p.degree().unwrap_or(0) == 0 {
207 return None;
208 }
209 let roots = p.isolate_real_roots();
210 let iv = roots.get(k - 1)?;
211 if iv.lo == iv.hi {
212 return Some(Self::from_rational(iv.lo.clone()));
213 }
214 let factors: Vec<Poly> = p.factor_over_q().into_iter().map(|(g, _)| g).collect();
215 let g = factors
216 .into_iter()
217 .find(|g| g.count_real_roots_in(&iv.lo, &iv.hi) == 1)
218 .expect("the root lies on exactly one irreducible factor");
219 Some(Self::from_irreducible(g, iv.lo.clone(), iv.hi.clone()))
220 }
221
222 fn from_irreducible(g: Poly, lo: Rational, hi: Rational) -> Algebraic {
225 if g.degree() == Some(1) {
226 let r = g.coeff(0).negated().div(&g.coeff(1)).expect("degree 1");
227 return Self::from_rational(r);
228 }
229 let bound = g.root_bound();
230 let index = g.count_real_roots_in(&bound.negated(), &lo) + 1;
231 Algebraic {
232 min: g,
233 index,
234 iv: RootInterval { lo, hi },
235 }
236 }
237
238 fn select(candidate: &Poly, enclose: impl Fn(u32) -> (Rational, Rational)) -> Algebraic {
242 let factors: Vec<Poly> = candidate.factor_over_q().into_iter().map(|(g, _)| g).collect();
243 let mut bits = 4u32;
244 loop {
245 let (lo, hi) = enclose(bits);
246 if lo == hi {
247 return Self::from_rational(lo);
248 }
249 let mut hits = factors.iter().filter_map(|g| {
250 let n = g.count_real_roots_in(&lo, &hi) + usize::from(g.eval(&lo).is_zero());
251 (n > 0).then_some((g, n))
252 });
253 if let (Some((g, 1)), None) = (hits.next(), hits.next()) {
254 if g.degree() == Some(1) {
255 let r = g.coeff(0).negated().div(&g.coeff(1)).expect("degree 1");
256 return Self::from_rational(r);
257 }
258 return Self::from_irreducible(g.clone(), lo, hi);
259 }
260 bits = bits.saturating_mul(2);
261 }
262 }
263
264 pub fn degree(&self) -> usize {
265 self.min.degree().expect("a minimal polynomial is nonzero")
266 }
267
268 pub fn minimal_polynomial(&self) -> &Poly {
269 &self.min
270 }
271
272 pub fn root_index(&self) -> usize {
274 self.index
275 }
276
277 pub fn to_rational(&self) -> Option<Rational> {
278 (self.degree() == 1).then(|| self.iv.lo.clone())
279 }
280
281 pub fn is_zero(&self) -> bool {
282 self.degree() == 1 && self.iv.lo.is_zero()
283 }
284
285 pub fn signum(&self) -> i32 {
286 if let Some(r) = self.to_rational() {
287 return if r.is_zero() {
288 0
289 } else if r.is_negative() {
290 -1
291 } else {
292 1
293 };
294 }
295 let mut iv = self.iv.clone();
296 loop {
297 if !iv.lo.is_negative() {
298 return 1;
299 }
300 if !iv.hi.is_positive() {
301 return -1;
302 }
303 iv = self.bisect(&iv);
304 }
305 }
306
307 fn bisect(&self, iv: &RootInterval) -> RootInterval {
309 if iv.lo == iv.hi {
310 return iv.clone();
311 }
312 let mid = iv.lo.add(&iv.hi).mul(&half());
313 let at_mid = self.min.eval(&mid);
314 if at_mid.is_zero() {
315 return RootInterval {
316 lo: mid.clone(),
317 hi: mid,
318 };
319 }
320 if at_mid.is_negative() == self.min.eval(&iv.lo).is_negative() {
321 RootInterval {
322 lo: mid,
323 hi: iv.hi.clone(),
324 }
325 } else {
326 RootInterval {
327 lo: iv.lo.clone(),
328 hi: mid,
329 }
330 }
331 }
332
333 pub fn enclosure(&self, bits: u32) -> (Rational, Rational) {
336 let eps = pow2_recip(bits);
337 let mut iv = self.iv.clone();
338 while iv.hi.sub(&iv.lo) > eps {
339 iv = self.bisect(&iv);
340 }
341 (iv.lo, iv.hi)
342 }
343
344 pub fn neg(&self) -> Algebraic {
345 if let Some(r) = self.to_rational() {
346 return Self::from_rational(r.negated());
347 }
348 let min = Poly::from_bigints(&self.min.neg_var().primitive_int());
349 let real_roots = min.count_real_roots();
350 Algebraic {
351 index: real_roots + 1 - self.index,
352 iv: RootInterval {
353 lo: self.iv.hi.negated(),
354 hi: self.iv.lo.negated(),
355 },
356 min,
357 }
358 }
359
360 fn add_rational(&self, r: &Rational) -> Algebraic {
361 if let Some(s) = self.to_rational() {
362 return Self::from_rational(s.add(r));
363 }
364 let shifted = self
365 .min
366 .compose(&Poly::from_coeffs(vec![r.negated(), Rational::one()]));
367 Algebraic {
368 min: Poly::from_bigints(&shifted.primitive_int()),
369 index: self.index,
370 iv: RootInterval {
371 lo: self.iv.lo.add(r),
372 hi: self.iv.hi.add(r),
373 },
374 }
375 }
376
377 fn mul_rational(&self, r: &Rational) -> Algebraic {
378 if r.is_zero() {
379 return Self::from_i64(0);
380 }
381 if let Some(s) = self.to_rational() {
382 return Self::from_rational(s.mul(r));
383 }
384 let scaled = self.min.compose(&Poly::from_coeffs(vec![
385 Rational::zero(),
386 r.recip().expect("r is nonzero"),
387 ]));
388 let min = Poly::from_bigints(&scaled.primitive_int());
389 let (lo, hi) = (self.iv.lo.mul(r), self.iv.hi.mul(r));
390 if r.is_positive() {
391 Algebraic {
392 min,
393 index: self.index,
394 iv: RootInterval { lo, hi },
395 }
396 } else {
397 let real_roots = min.count_real_roots();
398 Algebraic {
399 min,
400 index: real_roots + 1 - self.index,
401 iv: RootInterval { lo: hi, hi: lo },
402 }
403 }
404 }
405
406 pub fn add(&self, o: &Algebraic) -> Algebraic {
407 if let Some(r) = o.to_rational() {
408 return self.add_rational(&r);
409 }
410 if let Some(r) = self.to_rational() {
411 return o.add_rational(&r);
412 }
413 if let Some((x, y)) = self.one_quadratic_field(o) {
414 return Self::from_quadratic(x.a.add(&y.a), x.b.add(&y.b), &x.d);
415 }
416 let mb = o.min.clone();
417 let candidate = resultant_in_y(
418 &self.min,
419 |x0| mb.compose(&Poly::from_coeffs(vec![x0.clone(), Rational::from_i64(-1)])),
420 self.degree() * o.degree(),
421 );
422 Self::select(&candidate, |bits| {
423 let (alo, ahi) = self.enclosure(bits + 1);
424 let (blo, bhi) = o.enclosure(bits + 1);
425 (alo.add(&blo), ahi.add(&bhi))
426 })
427 }
428
429 pub fn sub(&self, o: &Algebraic) -> Algebraic {
430 self.add(&o.neg())
431 }
432
433 pub fn mul(&self, o: &Algebraic) -> Algebraic {
434 if let Some(r) = o.to_rational() {
435 return self.mul_rational(&r);
436 }
437 if let Some(r) = self.to_rational() {
438 return o.mul_rational(&r);
439 }
440 if let Some((x, y)) = self.one_quadratic_field(o) {
441 let d = Rational::from_bigint(x.d.clone());
442 let a = x.a.mul(&y.a).add(&x.b.mul(&y.b).mul(&d));
443 let b = x.a.mul(&y.b).add(&y.a.mul(&x.b));
444 return Self::from_quadratic(a, b, &x.d);
445 }
446 let db = o.degree();
447 let mb = o.min.clone();
448 let candidate = resultant_in_y(
449 &self.min,
450 |x0| {
451 let mut c = vec![Rational::zero(); db + 1];
452 let mut xp = Rational::one();
453 for j in 0..=db {
454 c[db - j] = mb.coeff(j).mul(&xp);
455 xp = xp.mul(x0);
456 }
457 Poly::from_coeffs(c)
458 },
459 self.degree() * db,
460 );
461 Self::select(&candidate, |bits| {
462 let a = self.enclosure(bits + 4);
463 let b = o.enclosure(bits + 4);
464 interval_mul(&a, &b)
465 })
466 }
467
468 pub fn recip(&self) -> Option<Algebraic> {
469 if self.is_zero() {
470 return None;
471 }
472 if let Some(r) = self.to_rational() {
473 return Some(Self::from_rational(r.recip().expect("nonzero")));
474 }
475 let candidate = Poly::from_bigints(&self.min.reverse().primitive_int());
476 Some(Self::select(&candidate, |bits| {
477 let mut b = bits;
478 loop {
479 let (lo, hi) = self.enclosure(b);
480 if lo.is_positive() || hi.is_negative() {
481 return (
482 hi.recip().expect("0 is outside the enclosure"),
483 lo.recip().expect("0 is outside the enclosure"),
484 );
485 }
486 b += 4;
487 }
488 }))
489 }
490
491 pub fn div(&self, o: &Algebraic) -> Option<Algebraic> {
492 Some(self.mul(&o.recip()?))
493 }
494
495 pub fn pow(&self, e: i32) -> Option<Algebraic> {
497 if e < 0 {
498 return self.recip()?.pow(-e);
499 }
500 if e == 0 {
501 return Some(Self::from_i64(1));
502 }
503 if let Some(r) = self.to_rational() {
504 return Some(Self::from_rational(r.pow(e).expect("nonnegative power")));
505 }
506 let e = e as u32;
507 let candidate = resultant_in_y(
508 &self.min,
509 |x0| {
510 let mut c = vec![Rational::zero(); e as usize + 1];
511 c[0] = x0.clone();
512 c[e as usize] = Rational::from_i64(-1);
513 Poly::from_coeffs(c)
514 },
515 self.degree(),
516 );
517 Some(Self::select(&candidate, |bits| {
518 let (lo, hi) = self.enclosure(bits + 2 * e + 8);
519 interval_pow(&lo, &hi, e)
520 }))
521 }
522
523 pub fn sqrt(&self) -> Option<Algebraic> {
525 self.nth_root(2)
526 }
527
528 pub fn nth_root(&self, k: u32) -> Option<Algebraic> {
531 if k == 0 {
532 return None;
533 }
534 if k == 1 || self.is_zero() {
535 return Some(self.clone());
536 }
537 let negative = self.signum() < 0;
538 if negative {
539 if k.is_multiple_of(2) {
540 return None;
541 }
542 return Some(self.neg().nth_root(k)?.neg());
543 }
544 let mut c = vec![Rational::zero(); self.degree() * k as usize + 1];
545 for (i, a) in self.min.coeffs().iter().enumerate() {
546 c[i * k as usize] = a.clone();
547 }
548 let candidate = Poly::from_coeffs(c);
549 Some(Self::select(&candidate, |bits| {
550 let (lo, hi) = self.enclosure(bits * k + 4);
551 let lo = if lo.is_negative() { Rational::zero() } else { lo };
552 let (l, _) = rational_root_bounds(&lo, k, bits + 2);
553 let (_, u) = rational_root_bounds(&hi, k, bits + 2);
554 (l, u)
555 }))
556 }
557
558 pub fn real_roots(coeffs: &[Algebraic]) -> Vec<(Algebraic, usize)> {
567 let mut c = coeffs.to_vec();
568 while c.last().is_some_and(Algebraic::is_zero) {
569 c.pop();
570 }
571 if c.len() <= 1 {
572 return Vec::new();
573 }
574 let rational: Option<Vec<Rational>> = c.iter().map(Algebraic::to_rational).collect();
575 let norm = match &rational {
576 Some(r) => Poly::from_coeffs(r.clone()),
577 None => norm_over_q(&c),
578 };
579 let mut out = Vec::new();
580 for (g, _) in norm.factor_over_q() {
581 for k in 1..=g.count_real_roots() {
582 let r = Self::root_of(&g, k).expect("an index within the real roots");
583 if rational.is_none() && !eval_at(&c, &r).is_zero() {
584 continue;
585 }
586 let mut multiplicity = 1;
587 let mut d = derivative(&c);
588 while !d.is_empty() && eval_at(&d, &r).is_zero() {
589 multiplicity += 1;
590 d = derivative(&d);
591 }
592 out.push((r, multiplicity));
593 }
594 }
595 out.sort_by(|a, b| a.0.cmp(&b.0));
596 out
597 }
598
599 pub fn floor(&self) -> BigInt {
600 if let Some(r) = self.to_rational() {
601 return r.floor();
602 }
603 let mut iv = self.iv.clone();
604 loop {
605 let (fl, fh) = (iv.lo.floor(), iv.hi.floor());
606 if fl == fh {
607 return fl;
608 }
609 iv = self.bisect(&iv);
610 }
611 }
612
613 pub fn ceil(&self) -> BigInt {
614 if let Some(r) = self.to_rational() {
615 return r.ceil();
616 }
617 let mut iv = self.iv.clone();
618 loop {
619 let (cl, ch) = (iv.lo.ceil(), iv.hi.ceil());
620 if cl == ch {
621 return cl;
622 }
623 iv = self.bisect(&iv);
624 }
625 }
626
627 pub fn to_f64(&self) -> f64 {
629 if let Some(r) = self.to_rational() {
630 return rational_to_f64_nearest(&r);
631 }
632 let mut iv = self.iv.clone();
633 loop {
634 let (fl, fh) = (rational_to_f64_nearest(&iv.lo), rational_to_f64_nearest(&iv.hi));
635 if fl.to_bits() == fh.to_bits() {
636 return fl;
637 }
638 iv = self.bisect(&iv);
639 }
640 }
641
642 fn quadratic(&self) -> Option<Quadratic> {
647 if self.degree() != 2 {
648 return None;
649 }
650 let c = self.min.primitive_int();
651 let (c0, c1, c2) = (&c[0], &c[1], &c[2]);
652 let d = c1.mul(c1).sub(&BigInt::from_i64(4).mul(c2).mul(c0));
653 let two_c2 = Rational::from_bigint(c2.add(c2));
654 let a = Rational::from_bigint(c1.negated()).div(&two_c2).expect("c₂ is nonzero");
655 let b = Rational::one().div(&two_c2).expect("c₂ is nonzero");
656 let b = if self.index == 2 { b } else { b.negated() };
657 Some(Quadratic { a, b, d })
658 }
659
660 fn one_quadratic_field(&self, o: &Algebraic) -> Option<(Quadratic, Quadratic)> {
664 let (x, y) = (self.quadratic()?, o.quadratic()?);
665 if x.d == y.d {
666 return Some((x, y));
667 }
668 let product = x.d.mul(&y.d);
669 let r = isqrt(&product);
670 if r.mul(&r) != product {
671 return None;
672 }
673 let (keep, other) = if x.d <= y.d { (x, y) } else { (y, x) };
674 let scale = Rational::from_bigint(r).div(&Rational::from_bigint(keep.d.clone())).expect("d is positive");
675 let moved = Quadratic {
676 a: other.a,
677 b: other.b.mul(&scale),
678 d: keep.d.clone(),
679 };
680 Some((keep, moved))
681 }
682
683 fn from_quadratic(a: Rational, b: Rational, d: &BigInt) -> Algebraic {
688 if b.is_zero() {
689 return Self::from_rational(a);
690 }
691 let c0 = a.mul(&a).sub(&b.mul(&b).mul(&Rational::from_bigint(d.clone())));
692 let c1 = a.add(&a).negated();
693 let p = Poly::from_coeffs(vec![c0, c1, Rational::one()]);
694 let min = Poly::from_bigints(&p.primitive_int());
695 let larger = b.is_positive();
696 let m = if larger { b } else { b.negated() };
697 let s = Rational::from_bigint(isqrt(d));
698 let (near, far) = (m.mul(&s), m.mul(&s.add(&Rational::one())));
699 let (lo, hi) = if larger { (a.add(&near), a.add(&far)) } else { (a.sub(&far), a.sub(&near)) };
700 Algebraic {
701 min,
702 index: if larger { 2 } else { 1 },
703 iv: RootInterval { lo, hi },
704 }
705 }
706
707 fn fmt_quadratic(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
708 let c = self.min.primitive_int();
709 let (cc, b, a) = (&c[0], &c[1], &c[2]);
710 let four = BigInt::from_i64(4);
711 let disc = b.mul(b).sub(&four.mul(a).mul(cc));
712 let (k, d) = split_square(&disc);
713 let two_a = a.add(a);
714 let g = b.abs().gcd(&k).gcd(&two_a);
715 let r = b.negated().div_rem(&g).expect("g divides b").0;
716 let s = k.div_rem(&g).expect("g divides k").0;
717 let t = two_a.div_rem(&g).expect("g divides 2a").0;
718 let plus = self.index == 2;
719 let one = BigInt::from_i64(1);
720 let radical = if s == one {
721 format!("√{d}")
722 } else {
723 format!("{s}√{d}")
724 };
725 if r.is_zero() {
726 let sign = if plus { "" } else { "-" };
727 if t == one {
728 write!(f, "{sign}{radical}")
729 } else {
730 write!(f, "{sign}{radical}/{t}")
731 }
732 } else {
733 let op = if plus { "+" } else { "-" };
734 if t == one {
735 write!(f, "{r} {op} {radical}")
736 } else {
737 write!(f, "({r} {op} {radical})/{t}")
738 }
739 }
740 }
741}
742
743fn eval_at(c: &[Algebraic], x: &Algebraic) -> Algebraic {
745 c.iter().rev().fold(Algebraic::from_i64(0), |acc, a| acc.mul(x).add(a))
746}
747
748fn derivative(c: &[Algebraic]) -> Vec<Algebraic> {
749 c.iter()
750 .enumerate()
751 .skip(1)
752 .map(|(i, a)| a.mul_rational(&Rational::from_i64(i as i64)))
753 .collect()
754}
755
756fn norm_over_q(c: &[Algebraic]) -> Poly {
759 use crate::elimination::Polynomial;
760 let mut symbols: Vec<&Algebraic> = Vec::new();
761 for a in c {
762 if a.to_rational().is_none() && !symbols.contains(&a) {
763 symbols.push(a);
764 }
765 }
766 let mut f = Polynomial::zero();
767 for (i, a) in c.iter().enumerate() {
768 let coefficient = match a.to_rational() {
769 Some(r) => Polynomial::constant(r),
770 None => Polynomial::var(1 + symbols.iter().position(|s| *s == a).expect("collected")),
771 };
772 f = f.add(&coefficient.mul(&Polynomial::var(0).pow(i as u32)));
773 }
774 for (j, a) in symbols.iter().enumerate() {
775 let minimal = Polynomial::from_univariate(a.minimal_polynomial(), j + 1);
776 f = minimal.resultant(&f, j + 1);
777 }
778 f.to_univariate(0).expect("every coefficient eliminated")
779}
780
781impl PartialEq for Algebraic {
782 fn eq(&self, o: &Self) -> bool {
783 self.index == o.index && self.min == o.min
784 }
785}
786
787impl Eq for Algebraic {}
788
789impl Hash for Algebraic {
790 fn hash<H: Hasher>(&self, state: &mut H) {
791 self.min.hash(state);
792 self.index.hash(state);
793 }
794}
795
796impl Ord for Algebraic {
797 fn cmp(&self, o: &Self) -> Ordering {
798 if self == o {
799 return Ordering::Equal;
800 }
801 if let (Some(a), Some(b)) = (self.to_rational(), o.to_rational()) {
802 return a.cmp(&b);
803 }
804 let (mut a, mut b) = (self.iv.clone(), o.iv.clone());
805 loop {
806 if a.hi <= b.lo {
807 return Ordering::Less;
808 }
809 if b.hi <= a.lo {
810 return Ordering::Greater;
811 }
812 if a.hi.sub(&a.lo) >= b.hi.sub(&b.lo) {
813 a = self.bisect(&a);
814 } else {
815 b = o.bisect(&b);
816 }
817 }
818 }
819}
820
821impl PartialOrd for Algebraic {
822 fn partial_cmp(&self, o: &Self) -> Option<Ordering> {
823 Some(self.cmp(o))
824 }
825}
826
827impl fmt::Display for Algebraic {
828 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
831 match self.degree() {
832 1 => write!(f, "{}", self.iv.lo),
833 2 => self.fmt_quadratic(f),
834 _ => write!(f, "root {} of {}", self.index, self.min),
835 }
836 }
837}
838
839impl crate::exact_codec::ExactCodec for Algebraic {
840 fn write(&self, out: &mut Vec<u8>) {
842 self.min.write(out);
843 (self.index as u64).write(out);
844 }
845
846 fn read(buf: &[u8], pos: &mut usize) -> Option<Self> {
849 let min = Poly::read(buf, pos)?;
850 let index = usize::try_from(u64::read(buf, pos)?).ok()?;
851 let degree = min.degree()?;
852 if degree == 0 || degree > crate::exact_codec::MAX_WIRE_DEGREE || index == 0 {
853 return None;
854 }
855 if Poly::from_bigints(&min.primitive_int()) != min || !min.is_irreducible() {
856 return None;
857 }
858 let value = Algebraic::root_of(&min, index)?;
859 (value.min == min && value.index == index).then_some(value)
860 }
861}
862
863pub(crate) fn negative_root(n: u32) -> String {
865 if n == 2 {
866 "square root of a negative number".to_string()
867 } else {
868 format!("an even root ({n}th) of a negative number")
869 }
870}
871
872#[cfg(test)]
873#[path = "../../logicaffeine_tests/unit/logicaffeine_base/algebraic/tests.rs"]
874mod tests;