algebraeon_rings/isolated_algebraic/real/
mod.rs

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>, //a primitive irreducible polynomial of degree >= 2 with a unique real root between a and b
18    //an arbitrarily small interval containing the root. May be mutated
19    tight_a: Rational, //tight lower bound
20    tight_b: Rational, //tight upper bound
21    //a heuristically large interval containing the root. Should not shrink
22    wide_a: LowerBound, //wide lower bound. None means -inf
23    wide_b: UpperBound, //wide upper bound. None means +inf
24    //false : decreasing i.e. poly(a) > poly(b), true : increasing i.e. poly(a) < poly(b)
25    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            //quadratic
51            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); //because we are a real number
67            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            // write!(f, "±");
112            // write!(f, "{}", decimal_string_approx(self.accuracy() / Rational::TWO));
113        }
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; //polys should be irreducible primitive fav-assoc so this is valid
205        loop {
206            //test for equality: If the tight bounds on one are within the wide bounds of the other
207            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            //test for inequality: If the tight bounds are disjoint
217            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            //refine
225            self.refine();
226            other.refine();
227        }
228    }
229
230    pub fn cmp_rat_mut(&mut self, other: &Rational) -> std::cmp::Ordering {
231        loop {
232            // println!("cmp_rat_mut {:?}", self);
233
234            //test for inequality: other is outside the tight bounds
235            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            // println!("refine");
243
244            //refine
245            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                    // eg: c + bx + ax^2 = c + x(b + x(a))
295                    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        // println!("add {:?} {:?}", alg1, alg2);
463
464        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            //compose with x - n/d = dx - n
481            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            //example where f(g(x)) is not primitive even though f and g are
927            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        // (x-a)(x-b) = x^2 - 100/7
961        // so ab=-100/7
962        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}