1use super::{bisection_gen::RationalSimpleBetweenGenerator, poly_tools::*};
2use crate::{polynomial::*, structure::*};
3use algebraeon_nzq::*;
4use algebraeon_sets::structure::*;
5use bounds::*;
6use interval::*;
7use polynomial::*;
8use std::fmt::Display;
9use std::hash::Hash;
10
11mod bounds;
12mod interval;
13pub mod polynomial;
14
15#[derive(Debug, Clone)]
16pub struct RealAlgebraicRoot {
17 poly: Polynomial<Integer>, tight_a: Rational, tight_b: Rational, wide_a: LowerBound, wide_b: UpperBound, dir: bool,
26}
27
28impl std::hash::Hash for RealAlgebraicRoot {
29 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
30 debug_assert!(self.poly.leading_coeff().unwrap() > Integer::ZERO);
31 self.poly.hash(state);
32 }
33}
34
35impl RealAlgebraicRoot {
36 pub fn poly(&self) -> &Polynomial<Integer> {
37 &self.poly
38 }
39 pub fn tight_a(&self) -> &Rational {
40 &self.tight_a
41 }
42 pub fn tight_b(&self) -> &Rational {
43 &self.tight_b
44 }
45}
46
47impl Display for RealAlgebraicRoot {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 if self.poly.num_coeffs() == 3 {
50 let a = self.poly.coeff(2);
52 let b = self.poly.coeff(1);
53 let c = self.poly.coeff(0);
54 debug_assert!(a.as_ref() > &Integer::ZERO);
55
56 let d = b.as_ref() * b.as_ref() - Integer::from(4) * a.as_ref() * c.as_ref();
57 let mut d_sq = Integer::ONE;
58 let mut d_sqfreee = Integer::ONE;
59 let (d_sign, d_factors) = d.factor().into_unit_and_powers().unwrap();
60 for (d_factor, k) in d_factors {
61 d_sq *= d_factor.nat_pow(&(&k / Natural::TWO));
62 if k % Natural::TWO == Natural::ONE {
63 d_sqfreee *= d_factor;
64 }
65 }
66 debug_assert_eq!(d_sign, Integer::ONE); debug_assert_eq!(d, &d_sqfreee * &d_sq * &d_sq);
68
69 let two_a = Integer::TWO * a.as_ref();
70
71 let x = Rational::from_integers(-b.as_ref(), two_a.clone());
72 let y = Rational::from_integers(d_sq, two_a);
73 debug_assert!(y > Rational::ZERO);
74 let r = d_sqfreee;
75
76 let mut tight_a_abs = self.tight_a.clone();
77 if tight_a_abs < Rational::ZERO {
78 tight_a_abs = -tight_a_abs;
79 }
80
81 let mut tight_b_abs = self.tight_b.clone();
82 if tight_b_abs < Rational::ZERO {
83 tight_b_abs = -tight_b_abs;
84 }
85
86 let sign = tight_a_abs < tight_b_abs;
87 match (x, y) {
88 (Rational::ZERO, Rational::ONE) => {
89 write!(f, "{}√{}", if sign { "" } else { "-" }, r)?;
90 }
91 (Rational::ZERO, y) => {
92 write!(f, "{}{}√{}", if sign { "" } else { "-" }, y, r)?;
93 }
94 (x, Rational::ONE) => {
95 write!(f, "{}{}√{}", x, if sign { "+" } else { "-" }, r)?;
96 }
97 (x, y) => {
98 write!(f, "{}{}{}√{}", x, if sign { "+" } else { "-" }, y, r)?;
99 }
100 }
101 } else {
102 let mut root = self.clone();
103 root.refine_to_accuracy(&Rational::from_integers(
104 Integer::from(1),
105 Integer::from(100),
106 ));
107 let m = (&root.tight_a + &root.tight_b) / Rational::TWO;
108
109 write!(f, "≈")?;
110 write!(f, "{}", m.decimal_string_approx())?;
111 }
114 Ok(())
115 }
116}
117
118impl RealAlgebraicRoot {
119 #[allow(clippy::op_ref)]
120 pub fn check_invariants(&self) -> Result<(), &'static str> {
121 if self.tight_a >= self.tight_b {
122 return Err("tight a should be strictly less than b");
123 }
124 if self.wide_a.clone() >= self.wide_b.clone() {
125 return Err("wide a should be strictly less than b");
126 }
127 if self.poly
128 != self
129 .poly
130 .clone()
131 .primitive_squarefree_part()
132 .factor_fav_assoc()
133 .1
134 {
135 return Err("poly should be primitive and favoriate associate");
136 }
137 if !self.poly.is_irreducible() {
138 return Err("poly should be irreducible");
139 }
140 if self.poly.degree().unwrap() < 2 {
141 return Err("poly should have degree at least 2");
142 }
143 let at_a = self.evaluate(&self.tight_a);
144 let at_b = self.evaluate(&self.tight_b);
145 assert_ne!(at_a, Rational::from(0));
146 assert_ne!(at_b, Rational::from(0));
147 let sign_a = &at_a > &Rational::from(0);
148 let sign_b = &at_b > &Rational::from(0);
149 if sign_a == sign_b {
150 return Err("sign at a and b should be different");
151 }
152 if self.dir == sign_a {
153 return Err("dir is incorrect");
154 }
155 Ok(())
156 }
157
158 #[allow(unused)]
159 fn new_wide_bounds(poly: Polynomial<Integer>, wide_a: Rational, wide_b: Rational) -> Self {
160 let dir = poly.apply_map(|x| Rational::from(x)).evaluate(&wide_a) < Rational::from(0);
161 let x = Self {
162 poly,
163 tight_a: wide_a.clone(),
164 tight_b: wide_b.clone(),
165 wide_a: LowerBound::Finite(wide_a),
166 wide_b: UpperBound::Finite(wide_b),
167 dir,
168 };
169 debug_assert!(x.check_invariants().is_ok());
170 x
171 }
172
173 fn evaluate(&self, val: &Rational) -> Rational {
174 evaluate_at_rational(&self.poly, val)
175 }
176
177 pub fn accuracy(&self) -> Rational {
178 &self.tight_b - &self.tight_a
179 }
180
181 pub fn refine(&mut self) {
182 let m = RationalSimpleBetweenGenerator::new_between_middle(
183 &self.tight_a,
184 &self.tight_b,
185 &Rational::from_integers(Integer::from(1), Integer::from(3)),
186 )
187 .next()
188 .unwrap();
189 let m_sign = self.evaluate(&m) > Rational::from(0);
190 if self.dir == m_sign {
191 self.tight_b = m;
192 } else {
193 self.tight_a = m;
194 }
195 }
196
197 pub fn refine_to_accuracy(&mut self, accuracy: &Rational) {
198 while &self.accuracy() > accuracy {
199 self.refine();
200 }
201 }
202
203 pub fn cmp_mut(&mut self, other: &mut Self) -> std::cmp::Ordering {
204 let polys_are_eq = self.poly == other.poly; loop {
206 if polys_are_eq {
208 if other.wide_a <= self.tight_a && self.tight_b <= other.wide_b {
209 return std::cmp::Ordering::Equal;
210 }
211 if self.wide_a <= other.tight_a && other.tight_b <= self.wide_b {
212 return std::cmp::Ordering::Equal;
213 }
214 }
215
216 if self.tight_b <= other.tight_a {
218 return std::cmp::Ordering::Less;
219 }
220 if other.tight_b <= self.tight_a {
221 return std::cmp::Ordering::Greater;
222 }
223
224 self.refine();
226 other.refine();
227 }
228 }
229
230 pub fn cmp_rat_mut(&mut self, other: &Rational) -> std::cmp::Ordering {
231 loop {
232 if &self.tight_b <= other {
236 return std::cmp::Ordering::Less;
237 }
238 if other <= &self.tight_a {
239 return std::cmp::Ordering::Greater;
240 }
241
242 self.refine();
246 }
247 }
248
249 fn neg_mut(&mut self) {
250 let (unit, fav_assoc) = Polynomial::compose(
251 &self.poly,
252 &Polynomial::from_coeffs(vec![Integer::from(0), Integer::from(-1)]),
253 )
254 .factor_fav_assoc();
255 if unit == Polynomial::one() {
256 self.poly = fav_assoc;
257 self.dir = !self.dir;
258 } else if unit == Polynomial::neg(&Polynomial::one()) {
259 self.poly = fav_assoc;
260 } else {
261 panic!();
262 }
263 (self.tight_a, self.tight_b) = (-self.tight_b.clone(), -self.tight_a.clone());
264 (self.wide_a, self.wide_b) = (self.wide_b.clone().neg(), self.wide_a.clone().neg());
265 }
266
267 fn neg(mut self) -> Self {
268 self.neg_mut();
269 self
270 }
271
272 pub fn min_poly(&self) -> Polynomial<Rational> {
273 self.poly.apply_map(|c| Rational::from(c)).fav_assoc()
274 }
275
276 pub fn apply_poly(&mut self, poly: &Polynomial<Rational>) -> RealAlgebraic {
277 let poly = Polynomial::rem(poly, &self.min_poly());
278 if let Some(rat) = poly.as_constant() {
279 RealAlgebraic::Rational(rat)
280 } else {
281 let ans_poly = self
282 .min_poly()
283 .algebraic_number_field_unchecked()
284 .min_poly(&poly)
285 .primitive_part_fof();
286
287 identify_real_root(
288 ans_poly,
289 (0..).map(|i| {
290 if i != 0 {
291 self.refine();
292 }
293
294 let mut coeffs = poly.coeffs().collect::<Vec<_>>().into_iter().rev();
296 let lc = coeffs.next().unwrap();
297 let mut ans = mul_interval_rat((&self.tight_a, &self.tight_b), lc);
298 for (i, c) in coeffs.enumerate() {
299 if i != 0 {
300 ans = mul_intervals((&ans.0, &ans.1), (&self.tight_a, &self.tight_b));
301 }
302 ans = add_interval_rat((&ans.0, &ans.1), c);
303 }
304
305 ans
306 }),
307 )
308 }
309 }
310}
311
312#[derive(Debug, Clone, CanonicalStructure)]
313#[canonical_structure(eq, partial_ord, ord)]
314pub enum RealAlgebraic {
315 Rational(Rational),
316 Real(RealAlgebraicRoot),
317}
318
319impl RealAlgebraic {
320 pub fn check_invariants(&self) -> Result<(), &'static str> {
321 match self {
322 RealAlgebraic::Rational(_x) => {}
323 RealAlgebraic::Real(x) => match x.check_invariants() {
324 Ok(()) => {}
325 Err(e) => {
326 return Err(e);
327 }
328 },
329 }
330 Ok(())
331 }
332
333 pub fn cmp_mut(&mut self, other: &mut Self) -> std::cmp::Ordering {
334 {
335 match self {
336 RealAlgebraic::Rational(self_rep) => match other {
337 RealAlgebraic::Rational(other_rep) => self_rep.cmp(&other_rep),
338 RealAlgebraic::Real(other_rep) => other_rep.cmp_rat_mut(self_rep).reverse(),
339 },
340 RealAlgebraic::Real(self_rep) => match other {
341 RealAlgebraic::Rational(other_rep) => self_rep.cmp_rat_mut(other_rep),
342 RealAlgebraic::Real(other_rep) => self_rep.cmp_mut(other_rep),
343 },
344 }
345 }
346 }
347
348 pub fn min_poly(&self) -> Polynomial<Rational> {
349 match self {
350 RealAlgebraic::Rational(rat) => Polynomial::from_coeffs(vec![-rat, Rational::ONE]),
351 RealAlgebraic::Real(real_root) => real_root.min_poly(),
352 }
353 }
354
355 pub fn apply_poly(&mut self, poly: &Polynomial<Rational>) -> Self {
356 match self {
357 RealAlgebraic::Rational(rat) => RealAlgebraic::Rational(poly.evaluate(rat)),
358 RealAlgebraic::Real(real_root) => real_root.apply_poly(poly),
359 }
360 }
361
362 pub fn degree(&self) -> usize {
363 self.min_poly().degree().unwrap()
364 }
365}
366
367pub enum RealIsolatingRegion<'a> {
368 Rational(&'a Rational),
369 Interval(&'a Rational, &'a Rational),
370}
371
372impl RealAlgebraic {
373 pub fn refine(&mut self) {
374 match self {
375 RealAlgebraic::Rational(..) => {}
376 RealAlgebraic::Real(x) => {
377 x.refine();
378 }
379 }
380 }
381
382 pub fn isolate<'a>(&'a self) -> RealIsolatingRegion<'a> {
383 match self {
384 RealAlgebraic::Rational(rational) => RealIsolatingRegion::Rational(rational),
385 RealAlgebraic::Real(real_algebraic_root) => RealIsolatingRegion::Interval(
386 &real_algebraic_root.tight_a,
387 &real_algebraic_root.tight_b,
388 ),
389 }
390 }
391}
392
393impl PositiveRealNthRootSignature for RealAlgebraicCanonicalStructure {
394 fn nth_root(&self, x: &Self::Set, n: usize) -> Result<Self::Set, ()> {
395 nth_root(x, n)
396 }
397}
398
399#[allow(clippy::derived_hash_with_manual_eq)]
400impl PartialEq for RealAlgebraic {
401 fn eq(&self, other: &Self) -> bool {
402 self.cmp(other).is_eq()
403 }
404}
405
406impl Eq for RealAlgebraic {}
407
408impl Hash for RealAlgebraic {
409 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
410 core::mem::discriminant(self).hash(state);
411 }
412}
413
414#[allow(clippy::non_canonical_partial_ord_impl)]
415impl PartialOrd for RealAlgebraic {
416 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
417 Some(self.clone().cmp_mut(&mut other.clone()))
418 }
419}
420
421impl Ord for RealAlgebraic {
422 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
423 self.partial_cmp(other).unwrap()
424 }
425}
426
427impl Display for RealAlgebraic {
428 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
429 match self {
430 RealAlgebraic::Rational(a) => write!(f, "{}", a),
431 RealAlgebraic::Real(a) => write!(f, "{}", a),
432 }
433 }
434}
435
436impl ToStringSignature for RealAlgebraicCanonicalStructure {
437 fn to_string(&self, elem: &Self::Set) -> String {
438 format!("{}", elem)
439 }
440}
441
442impl RinglikeSpecializationSignature for RealAlgebraicCanonicalStructure {
443 fn try_ring_restructure(&self) -> Option<impl EqSignature<Set = Self::Set> + RingSignature> {
444 Some(self.clone())
445 }
446
447 fn try_char_zero_ring_restructure(
448 &self,
449 ) -> Option<impl EqSignature<Set = Self::Set> + CharZeroRingSignature> {
450 Some(self.clone())
451 }
452}
453
454impl ZeroSignature for RealAlgebraicCanonicalStructure {
455 fn zero(&self) -> Self::Set {
456 RealAlgebraic::Rational(Rational::from(0))
457 }
458}
459
460impl AdditionSignature for RealAlgebraicCanonicalStructure {
461 fn add(&self, alg1: &Self::Set, alg2: &Self::Set) -> Self::Set {
462 fn add_rat(mut elem: RealAlgebraicRoot, rat: &Rational) -> RealAlgebraicRoot {
465 elem.tight_a += rat;
466 elem.tight_b += rat;
467 match &elem.wide_a {
468 LowerBound::Inf => {}
469 LowerBound::Finite(a) => {
470 elem.wide_a = LowerBound::Finite(a + rat);
471 }
472 }
473 match &elem.wide_b {
474 UpperBound::Inf => {}
475 UpperBound::Finite(b) => {
476 elem.wide_b = UpperBound::Finite(b + rat);
477 }
478 }
479
480 elem.poly = Polynomial::compose(
482 &elem.poly.apply_map(|c| Rational::from(c)),
483 &Polynomial::from_coeffs(vec![-rat, Rational::ONE]),
484 )
485 .primitive_part_fof();
486
487 #[cfg(debug_assertions)]
488 elem.check_invariants().unwrap();
489 elem
490 }
491
492 match (alg1, alg2) {
493 (RealAlgebraic::Rational(rat1), RealAlgebraic::Rational(rat2)) => {
494 RealAlgebraic::Rational(rat1 + rat2)
495 }
496 (RealAlgebraic::Rational(rat1), RealAlgebraic::Real(alg2)) => {
497 RealAlgebraic::Real(add_rat(alg2.clone(), rat1))
498 }
499 (RealAlgebraic::Real(alg1), RealAlgebraic::Rational(rat2)) => {
500 RealAlgebraic::Real(add_rat(alg1.clone(), rat2))
501 }
502 (RealAlgebraic::Real(alg1), RealAlgebraic::Real(alg2)) => {
503 let mut alg1 = alg1.clone();
504 let mut alg2 = alg2.clone();
505
506 identify_real_root(
507 root_sum_poly(&alg1.poly, &alg2.poly),
508 (0..).map(|i| {
509 if i != 0 {
510 alg1.refine();
511 alg2.refine();
512 }
513
514 add_intervals(
515 (&alg1.tight_a, &alg1.tight_b),
516 (&alg2.tight_a, &alg2.tight_b),
517 )
518 }),
519 )
520 }
521 }
522 }
523}
524
525impl CancellativeAdditionSignature for RealAlgebraicCanonicalStructure {
526 fn try_sub(&self, a: &Self::Set, b: &Self::Set) -> Option<Self::Set> {
527 Some(self.sub(a, b))
528 }
529}
530
531impl TryNegateSignature for RealAlgebraicCanonicalStructure {
532 fn try_neg(&self, a: &Self::Set) -> Option<Self::Set> {
533 Some(self.neg(a))
534 }
535}
536
537impl AdditiveMonoidSignature for RealAlgebraicCanonicalStructure {}
538
539impl AdditiveGroupSignature for RealAlgebraicCanonicalStructure {
540 fn neg(&self, a: &Self::Set) -> Self::Set {
541 match a {
542 RealAlgebraic::Rational(a) => RealAlgebraic::Rational(-a),
543 RealAlgebraic::Real(root) => RealAlgebraic::Real(root.clone().neg()),
544 }
545 }
546}
547
548impl OneSignature for RealAlgebraicCanonicalStructure {
549 fn one(&self) -> Self::Set {
550 RealAlgebraic::Rational(Rational::from(1))
551 }
552}
553
554impl MultiplicationSignature for RealAlgebraicCanonicalStructure {
555 fn mul(&self, elem1: &Self::Set, elem2: &Self::Set) -> Self::Set {
556 match elem1.cmp(&self.zero()) {
557 std::cmp::Ordering::Less => {
558 return self.neg(&self.mul(&self.neg(elem1), elem2));
559 }
560 std::cmp::Ordering::Equal => {
561 return self.zero();
562 }
563 std::cmp::Ordering::Greater => {}
564 }
565
566 match elem2.cmp(&self.zero()) {
567 std::cmp::Ordering::Less => {
568 return self.neg(&self.mul(elem1, &self.neg(elem2)));
569 }
570 std::cmp::Ordering::Equal => {
571 return self.zero();
572 }
573 std::cmp::Ordering::Greater => {}
574 }
575
576 debug_assert!(elem1 > &self.zero());
577 debug_assert!(elem2 > &self.zero());
578
579 #[allow(clippy::items_after_statements)]
580 fn mul_pos_rat(mut elem: RealAlgebraicRoot, rat: &Rational) -> RealAlgebraicRoot {
581 debug_assert!(rat > &Rational::from(0));
582 elem.tight_a *= rat;
583 elem.tight_b *= rat;
584 match &elem.wide_a {
585 LowerBound::Inf => {}
586 LowerBound::Finite(a) => {
587 elem.wide_a = LowerBound::Finite(a * rat);
588 }
589 }
590 match &elem.wide_b {
591 UpperBound::Inf => {}
592 UpperBound::Finite(b) => {
593 elem.wide_b = UpperBound::Finite(b * rat);
594 }
595 }
596 elem.poly = root_rat_mul_poly(elem.poly, rat);
597 #[cfg(debug_assertions)]
598 elem.check_invariants().unwrap();
599 elem
600 }
601
602 match (elem1, elem2) {
603 (RealAlgebraic::Rational(rat1), RealAlgebraic::Rational(rat2)) => {
604 RealAlgebraic::Rational(rat1 * rat2)
605 }
606 (RealAlgebraic::Rational(rat1), RealAlgebraic::Real(alg2)) => {
607 RealAlgebraic::Real(mul_pos_rat(alg2.clone(), rat1))
608 }
609 (RealAlgebraic::Real(alg1), RealAlgebraic::Rational(rat2)) => {
610 RealAlgebraic::Real(mul_pos_rat(alg1.clone(), rat2))
611 }
612 (RealAlgebraic::Real(alg1), RealAlgebraic::Real(alg2)) => {
613 let mut alg1 = alg1.clone();
614 let mut alg2 = alg2.clone();
615
616 identify_real_root(
617 root_product_poly(&alg1.poly, &alg2.poly),
618 (0..).map(|i| {
619 if i != 0 {
620 alg1.refine();
621 alg2.refine();
622 }
623 let ans_tight_a = &alg1.tight_a * &alg2.tight_a;
624 let ans_tight_b = &alg1.tight_b * &alg2.tight_b;
625 (ans_tight_a, ans_tight_b)
626 }),
627 )
628 }
629 }
630 }
631}
632
633impl CommutativeMultiplicationSignature for RealAlgebraicCanonicalStructure {}
634
635impl MultiplicativeMonoidSignature for RealAlgebraicCanonicalStructure {}
636
637impl MultiplicativeAbsorptionMonoidSignature for RealAlgebraicCanonicalStructure {}
638
639impl LeftDistributiveMultiplicationOverAddition for RealAlgebraicCanonicalStructure {}
640
641impl RightDistributiveMultiplicationOverAddition for RealAlgebraicCanonicalStructure {}
642
643impl SemiRingSignature for RealAlgebraicCanonicalStructure {}
644
645impl RingSignature for RealAlgebraicCanonicalStructure {
646 fn is_reduced(&self) -> Result<bool, String> {
647 Ok(true)
648 }
649}
650
651impl CharacteristicSignature for RealAlgebraicCanonicalStructure {
652 fn characteristic(&self) -> Natural {
653 Natural::ZERO
654 }
655}
656
657impl TryReciprocalSignature for RealAlgebraicCanonicalStructure {
658 fn try_reciprocal(&self, a: &Self::Set) -> Option<Self::Set> {
659 let mut a = a.clone();
660 match RealAlgebraic::cmp_mut(&mut a, &mut self.zero()) {
661 std::cmp::Ordering::Less => Some(self.neg(&self.try_reciprocal(&self.neg(&a))?)),
662 std::cmp::Ordering::Equal => None,
663 std::cmp::Ordering::Greater => match a {
664 RealAlgebraic::Rational(x) => Some(RealAlgebraic::Rational(
665 Rational::try_reciprocal(&x).unwrap(),
666 )),
667 RealAlgebraic::Real(mut root) => {
668 debug_assert!(root.tight_a >= Rational::from(0));
669 while root.tight_a == Rational::from(0) {
670 root.refine();
671 }
672 debug_assert!(Rational::from(0) < root.tight_a);
673 (root.tight_a, root.tight_b) = (
674 Rational::try_reciprocal(&root.tight_b).unwrap(),
675 Rational::try_reciprocal(&root.tight_a).unwrap(),
676 );
677 (root.wide_a, root.wide_b) = (
678 {
679 match root.wide_b {
680 UpperBound::Inf => LowerBound::Finite(Rational::from(0)),
681 UpperBound::Finite(x) => {
682 #[cfg(debug_assertions)]
683 if x.is_zero() {
684 panic!(
685 "wide upper bound of strictly positive root should be strictly positive i.e. non-zero"
686 );
687 }
688 match Rational::try_reciprocal(&x) {
689 Some(x_inv) => LowerBound::Finite(x_inv),
690 None => panic!(),
691 }
692 }
693 }
694 },
695 {
696 match root.wide_a {
697 LowerBound::Inf => UpperBound::Inf,
698 LowerBound::Finite(x) => match x.cmp(&Rational::from(0)) {
699 std::cmp::Ordering::Less | std::cmp::Ordering::Equal => {
700 UpperBound::Inf
701 }
702 std::cmp::Ordering::Greater => {
703 UpperBound::Finite(Rational::try_reciprocal(&x).unwrap())
704 }
705 },
706 }
707 },
708 );
709 let (unit, fav_assoc) = Polynomial::from_coeffs(
710 root.poly
711 .coeffs()
712 .collect::<Vec<_>>()
713 .into_iter()
714 .rev()
715 .collect(),
716 )
717 .factor_fav_assoc();
718 if unit == Polynomial::one() {
719 root.poly = fav_assoc;
720 root.dir = !root.dir;
721 } else if unit == Polynomial::neg(&Polynomial::one()) {
722 root.poly = fav_assoc;
723 } else {
724 panic!();
725 }
726 let ans = RealAlgebraic::Real(root);
727 #[cfg(debug_assertions)]
728 ans.check_invariants().unwrap();
729 Some(ans)
730 }
731 },
732 }
733 }
734}
735
736impl CancellativeMultiplicationSignature for RealAlgebraicCanonicalStructure {
737 fn try_divide(&self, a: &Self::Set, b: &Self::Set) -> Option<Self::Set> {
738 Some(self.mul(a, &self.try_reciprocal(b)?))
739 }
740}
741
742impl MultiplicativeIntegralMonoidSignature for RealAlgebraicCanonicalStructure {}
743
744impl IntegralDomainSignature for RealAlgebraicCanonicalStructure {}
745
746impl FieldSignature for RealAlgebraicCanonicalStructure {}
747
748impl CharZeroRingSignature for RealAlgebraicCanonicalStructure {
749 fn try_to_int(&self, alg: &Self::Set) -> Option<Integer> {
750 match alg {
751 RealAlgebraic::Rational(rat) => rat.try_to_int(),
752 RealAlgebraic::Real(_) => None,
753 }
754 }
755}
756
757impl CharZeroFieldSignature for RealAlgebraicCanonicalStructure {
758 fn try_to_rat(&self, x: &Self::Set) -> Option<Rational> {
759 match x {
760 RealAlgebraic::Rational(rational) => Some(rational.clone()),
761 RealAlgebraic::Real(_) => None,
762 }
763 }
764}
765
766impl ComplexSubsetSignature for RealAlgebraicCanonicalStructure {
767 fn as_f32_real_and_imaginary_parts(&self, z: &Self::Set) -> (f32, f32) {
768 (z.as_f32(), 0.0)
769 }
770
771 fn as_f64_real_and_imaginary_parts(&self, z: &Self::Set) -> (f64, f64) {
772 (z.as_f64(), 0.0)
773 }
774}
775
776impl RealSubsetSignature for RealAlgebraicCanonicalStructure {
777 fn as_f64(&self, x: &Self::Set) -> f64 {
778 match x {
779 RealAlgebraic::Rational(x) => x.as_f64(),
780 RealAlgebraic::Real(x) => {
781 let mut x = x.clone();
782 x.refine_to_accuracy(&Rational::from_integers(
783 Integer::from(1),
784 Integer::from(1_000_000_000_000_000i64),
785 ));
786 ((x.tight_a + x.tight_b) / Rational::from(2)).as_f64()
787 }
788 }
789 }
790
791 fn as_f32(&self, x: &Self::Set) -> f32 {
792 self.as_f64(x) as f32
793 }
794}
795
796impl OrderedRingSignature for RealAlgebraicCanonicalStructure {
797 fn ring_cmp(&self, a: &Self::Set, b: &Self::Set) -> std::cmp::Ordering {
798 a.cmp(b)
799 }
800}
801
802impl RealRoundingSignature for RealAlgebraicCanonicalStructure {
803 fn floor(&self, x: &Self::Set) -> Integer {
804 let mut x = x.clone();
805 loop {
806 match x.isolate() {
807 RealIsolatingRegion::Rational(v) => {
808 return v.floor();
809 }
810 RealIsolatingRegion::Interval(a, b) => {
811 let a_floor = a.floor();
812 let b_floor = b.floor();
813 if a_floor == b_floor {
814 return a_floor;
815 } else {
816 x.refine();
817 }
818 }
819 }
820 }
821 }
822 fn ceil(&self, x: &Self::Set) -> Integer {
823 let mut x = x.clone();
824 loop {
825 match x.isolate() {
826 RealIsolatingRegion::Rational(v) => {
827 return v.ceil();
828 }
829 RealIsolatingRegion::Interval(a, b) => {
830 let a_floor = a.ceil();
831 let b_floor = b.ceil();
832 if a_floor == b_floor {
833 return a_floor;
834 } else {
835 x.refine();
836 }
837 }
838 }
839 }
840 }
841 fn round(&self, x: &Self::Set) -> Integer {
842 let mut x = x.clone();
843 loop {
844 match x.isolate() {
845 RealIsolatingRegion::Rational(v) => {
846 return v.round();
847 }
848 RealIsolatingRegion::Interval(a, b) => {
849 let a_floor = a.round();
850 let b_floor = b.round();
851 if a_floor == b_floor {
852 return a_floor;
853 } else {
854 x.refine();
855 }
856 }
857 }
858 }
859 }
860}
861
862impl<B: BorrowedStructure<RealAlgebraicCanonicalStructure>>
863 IntegralDomainExtensionAllPolynomialRoots<
864 IntegerCanonicalStructure,
865 RealAlgebraicCanonicalStructure,
866 > for PrincipalIntegerMap<RealAlgebraicCanonicalStructure, B>
867{
868 fn all_roots(&self, polynomial: &Polynomial<Integer>) -> Vec<RealAlgebraic> {
869 polynomial.all_real_roots()
870 }
871}
872
873impl<B: BorrowedStructure<RealAlgebraicCanonicalStructure>>
874 IntegralDomainExtensionAllPolynomialRoots<
875 RationalCanonicalStructure,
876 RealAlgebraicCanonicalStructure,
877 > for PrincipalRationalMap<RealAlgebraicCanonicalStructure, B>
878{
879 fn all_roots(&self, polynomial: &Polynomial<Rational>) -> Vec<RealAlgebraic> {
880 polynomial.all_real_roots()
881 }
882}
883
884#[cfg(test)]
885mod tests {
886
887 use crate::structure::IntoErgonomic;
888
889 use super::*;
890
891 #[test]
892 fn test_real_neg() {
893 {
894 let f = Polynomial::<Integer>::from_coeffs(vec![-2, 0, 1]);
895 let roots = f.all_real_roots();
896
897 assert_eq!(roots.len(), 2);
898 let a = &roots[0];
899 let b = &roots[1];
900
901 let a_neg = RealAlgebraic::neg(a);
902 let b_neg = RealAlgebraic::neg(b);
903
904 a_neg.check_invariants().unwrap();
905 b_neg.check_invariants().unwrap();
906
907 println!("a = {}", a);
908 println!("b = {}", b);
909 println!("a_neg = {}", a_neg);
910 println!("b_neg = {}", b_neg);
911
912 assert_ne!(a, b);
913 assert_eq!(a, &b_neg);
914 assert_eq!(b, &a_neg);
915 }
916 {
917 let f = Polynomial::<Integer>::from_coeffs(vec![-1, 0, 0, 0, 0, 0, 3, 1]);
918 let roots = f.all_real_roots();
919
920 assert_eq!(roots.len(), 3);
921 for root in roots {
922 RealAlgebraic::neg(&root).check_invariants().unwrap();
923 }
924 }
925 {
926 let f = Polynomial::<Integer>::from_coeffs(vec![-4, -1, 1]);
928 let roots = f.all_real_roots();
929 for root in roots {
930 let root2 = RealAlgebraic::add(
931 &root,
932 &RealAlgebraic::try_from_rat(&Rational::from_integers(1, 2)).unwrap(),
933 );
934 root2.check_invariants().unwrap();
935 }
936 }
937 }
938
939 #[test]
940 fn test_real_add() {
941 let f = Polynomial::<Integer>::from_coeffs(vec![-2, 0, 3]);
942 let roots = f.all_real_roots();
943 let a = RealAlgebraic::sum(roots.iter().collect());
944 assert_eq!(a, RealAlgebraic::zero());
945
946 let f = Polynomial::<Integer>::from_coeffs(vec![-7, 0, 100]);
947 let roots = f.all_real_roots();
948 let a = RealAlgebraic::sum(roots.iter().collect());
949 assert_eq!(a, RealAlgebraic::zero());
950
951 let f = Polynomial::<Integer>::from_coeffs(vec![-100, 0, 7]);
952 let roots = f.all_real_roots();
953 let a = RealAlgebraic::sum(roots.iter().collect());
954 assert_eq!(a, RealAlgebraic::zero());
955 }
956
957 #[test]
958 fn test_real_mul() {
959 let f = Polynomial::<Integer>::from_coeffs(vec![-100, 0, 7]);
960 let roots = f.all_real_roots();
963 let a = RealAlgebraic::product(roots.iter().collect());
964 assert_eq!(
965 a,
966 RealAlgebraic::try_from_rat(&Rational::from_integers(-100, 7)).unwrap()
967 );
968 }
969
970 #[test]
971 fn test_real_nth_root() {
972 let x = &Polynomial::<Integer>::var().into_ergonomic();
973 let f = ((4 * x.pow(5) - 12 * x.pow(3) + 8 * x + 1)
974 * (x + 1)
975 * (x)
976 * (x - 1)
977 * (x - 2)
978 * (x - 3)
979 * (x - 4)
980 * (x - 5)
981 * (x - 144)
982 * (x.pow(2) - 3))
983 .into_verbose();
984 let n = 2;
985
986 for root in f.all_real_roots() {
987 println!();
988 println!("root = {}", root);
989 if let Ok(nth_root) = root.nth_root(n) {
990 println!("YES {}-root = {}", n, nth_root);
991 debug_assert!(RealAlgebraic::zero() <= root);
992 debug_assert!(RealAlgebraic::zero() <= nth_root);
993 debug_assert_eq!(nth_root.nat_pow(&Natural::from(n)), root);
994 } else {
995 println!("NO {}-root", n);
996 debug_assert!(RealAlgebraic::zero() > root);
997 }
998 }
999 }
1000
1001 #[test]
1002 fn test_real_algebraic_ordering() {
1003 let mut all_roots = vec![];
1004 for f in [
1005 Polynomial::from_coeffs(vec![
1006 Integer::from(-2),
1007 Integer::from(-4),
1008 Integer::from(-2),
1009 ]),
1010 Polynomial::from_coeffs(vec![Integer::from(6), Integer::from(0), Integer::from(-3)]),
1011 Polynomial::from_coeffs(vec![Integer::from(1), Integer::from(-3), Integer::from(1)]),
1012 Polynomial::from_coeffs(vec![
1013 Integer::from(2),
1014 Integer::from(-3),
1015 Integer::from(0),
1016 Integer::from(0),
1017 Integer::from(0),
1018 Integer::from(1),
1019 ]),
1020 Polynomial::from_coeffs(vec![
1021 Integer::from(1),
1022 Integer::from(-3),
1023 Integer::from(0),
1024 Integer::from(0),
1025 Integer::from(0),
1026 Integer::from(1),
1027 ]),
1028 Polynomial::from_coeffs(vec![
1029 Integer::from(-1),
1030 Integer::from(12),
1031 Integer::from(-4),
1032 Integer::from(-15),
1033 Integer::from(5),
1034 Integer::from(3),
1035 Integer::from(-1),
1036 ]),
1037 ] {
1038 for root in Polynomial::real_roots(&f, None, None, false, false) {
1039 all_roots.push(root.clone());
1040 }
1041 }
1042
1043 all_roots.sort();
1044
1045 for mut root in &mut all_roots {
1046 root.check_invariants().unwrap();
1047 match &mut root {
1048 RealAlgebraic::Rational(_a) => {}
1049 RealAlgebraic::Real(a) => {
1050 a.refine_to_accuracy(&Rational::from_integers(1, i64::MAX));
1051 }
1052 }
1053 println!(" {} {:?}", root, root);
1054 }
1055
1056 let mut all_roots_sorted_by_lower_tight_bound = all_roots.clone();
1057 all_roots_sorted_by_lower_tight_bound.sort_by_key(|root| match root {
1058 RealAlgebraic::Rational(a) => a.clone(),
1059 RealAlgebraic::Real(r) => r.tight_a.clone(),
1060 });
1061 assert_eq!(all_roots, all_roots_sorted_by_lower_tight_bound);
1062 }
1063}