Skip to main content

logicaffeine_base/
exact_real.rs

1//! Exact real numbers: every computable real, to any precision, with equality decided wherever a
2//! theorem decides it.
3//!
4//! An [`ExactReal`] is one of four forms. Three are **exact forms** whose equality is decidable:
5//!
6//! - an [`Algebraic`] number (rationals included);
7//! - an **exponential-algebraic sum** `Σ aₖ·e^{βₖ}` with complex algebraic `aₖ`, `βₖ` — what
8//!   `exp`, `sin` and `cos` of algebraic arguments produce, and what linear differential equations
9//!   integrate to. By the Lindemann–Weierstrass theorem the `e^{βₖ}` for distinct algebraic `βₖ`
10//!   are linearly independent over the algebraic numbers, so a sum is zero exactly when every
11//!   coefficient is zero after like exponents are merged: equality is a finite symbolic check;
12//! - a **Laurent polynomial in π** `Σ aₖ·πᵏ` with algebraic `aₖ` — zero exactly when every
13//!   coefficient is, because π is transcendental.
14//!
15//! Everything else — a mix of π with exponentials, a logarithm, an exponential of a
16//! transcendental, a number a program defines by a function ([`ExactReal::defined`]) — is a
17//! **general computable real**: an expression DAG that encloses its value in a dyadic interval at
18//! any requested working precision. Equality of general reals is
19//! undecidable (it contains the halting problem), so `compare` refines until the values separate
20//! and reports a typed [`RealError::Inseparable`] when a budget runs out first; `compare_within`
21//! is total. There is deliberately no `PartialEq`: numeric equality is `compare`, which answers
22//! `Equal` only when an exact form proves it.
23//!
24//! Evaluation is fixed-point dyadic arithmetic (`m·2^e`, no gcds): π by Machin's formula (cached at
25//! the highest precision computed), `ln 2 = 2·atanh(1/3)`, `exp` by argument halving, Taylor and
26//! repeated squaring, `log` by `atanh`, `sin`/`cos` by reduction modulo 2π, Taylor and the
27//! double-angle formulas, square roots by integer Newton. Every primitive returns a rigorous
28//! enclosure with its truncation errors counted, so every printed digit is certain.
29
30use crate::algebraic::{negative_root, Algebraic};
31use crate::interval::{ceil_div, floor_div, one, Dyadic, Iv};
32use crate::numeric::{rational_from_f64_exact, BigInt, Rational};
33use crate::ode::{Field, FlowError};
34use std::cmp::Ordering;
35use std::fmt;
36use std::sync::{Arc, Mutex, OnceLock};
37
38// ── errors ────────────────────────────────────────────────────────────────────────────────────
39
40/// Why an exact real could not answer.
41#[derive(Clone, Debug, PartialEq, Eq)]
42pub enum RealError {
43    /// Two quantities agreed to `2^-bits` and no theorem decided whether they are equal.
44    Inseparable { what: String, bits: u32 },
45    /// The operation is undefined at an exactly known argument.
46    Domain(String),
47}
48
49impl fmt::Display for RealError {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        match self {
52            RealError::Inseparable { what, bits } => write!(
53                f,
54                "cannot separate {what} within 2^-{bits}; they may be equal"
55            ),
56            RealError::Domain(why) => write!(f, "{why}"),
57        }
58    }
59}
60
61/// The answer of a comparison within a tolerance: which side, or within the tolerance.
62#[derive(Clone, Copy, Debug, PartialEq, Eq)]
63pub enum Closeness {
64    Less,
65    Greater,
66    Close,
67}
68
69// ── primitive enclosures ──────────────────────────────────────────────────────────────────────
70
71/// `atan(1/n)·2^w`, as integer bounds, from the alternating series with every truncation counted.
72fn atan_recip_scaled(n: i64, w: i64) -> (BigInt, BigInt) {
73    let nb = BigInt::from_i64(n);
74    let n2 = nb.mul(&nb);
75    let mut power = one().shl_bits(w as u64).div_rem(&nb).expect("n > 0").0;
76    let mut sum = BigInt::zero();
77    let mut k = 0i64;
78    while !power.is_zero() {
79        let term = power.div_rem(&BigInt::from_i64(2 * k + 1)).expect("odd").0;
80        sum = if k % 2 == 0 { sum.add(&term) } else { sum.sub(&term) };
81        power = power.div_rem(&n2).expect("n² > 0").0;
82        k += 1;
83    }
84    let err = BigInt::from_i64(3 * k + 3);
85    (sum.sub(&err), sum.add(&err))
86}
87
88fn pi_cache() -> &'static Mutex<Option<(i64, BigInt, BigInt)>> {
89    static CACHE: OnceLock<Mutex<Option<(i64, BigInt, BigInt)>>> = OnceLock::new();
90    CACHE.get_or_init(|| Mutex::new(None))
91}
92
93/// π enclosed on the `2^-p` grid, by Machin's `π = 16·atan(1/5) − 4·atan(1/239)`.
94fn pi_iv(p: i64) -> Iv {
95    let p = p.max(8);
96    {
97        let cache = pi_cache().lock().expect("the π cache is never poisoned");
98        if let Some((w, lo, hi)) = cache.as_ref() {
99            if *w >= p {
100                let iv = Iv {
101                    lo: Dyadic::new(lo.clone(), -w),
102                    hi: Dyadic::new(hi.clone(), -w),
103                };
104                return iv.round_out(p);
105            }
106        }
107    }
108    let w = p + 24;
109    let (a_lo, a_hi) = atan_recip_scaled(5, w);
110    let (b_lo, b_hi) = atan_recip_scaled(239, w);
111    let sixteen = BigInt::from_i64(16);
112    let four = BigInt::from_i64(4);
113    let lo = sixteen.mul(&a_lo).sub(&four.mul(&b_hi));
114    let hi = sixteen.mul(&a_hi).sub(&four.mul(&b_lo));
115    *pi_cache().lock().expect("the π cache is never poisoned") = Some((w, lo.clone(), hi.clone()));
116    Iv {
117        lo: Dyadic::new(lo, -w),
118        hi: Dyadic::new(hi, -w),
119    }
120    .round_out(p)
121}
122
123/// `atanh(z)·2^w` for a rational `0 ≤ z ≤ 1/3`, as integer bounds: the positive series
124/// `Σ z^{2j+1}/(2j+1)` truncated where its terms vanish, every division counted.
125fn atanh_scaled(z_num: &BigInt, z_den: &BigInt, w: i64) -> (BigInt, BigInt) {
126    let z2_num = z_num.mul(z_num);
127    let z2_den = z_den.mul(z_den);
128    let mut power = floor_div(&z_num.shl_bits(w as u64), z_den);
129    let mut sum = BigInt::zero();
130    let mut k = 0i64;
131    while !power.is_zero() {
132        sum = sum.add(&power.div_rem(&BigInt::from_i64(2 * k + 1)).expect("odd").0);
133        power = floor_div(&power.mul(&z2_num), &z2_den);
134        k += 1;
135    }
136    (sum.clone(), sum.add(&BigInt::from_i64(2 * k + 4)))
137}
138
139fn ln2_cache() -> &'static Mutex<Option<(i64, BigInt, BigInt)>> {
140    static CACHE: OnceLock<Mutex<Option<(i64, BigInt, BigInt)>>> = OnceLock::new();
141    CACHE.get_or_init(|| Mutex::new(None))
142}
143
144/// `ln 2 = 2·atanh(1/3)` enclosed on the `2^-p` grid.
145fn ln2_iv(p: i64) -> Iv {
146    let p = p.max(8);
147    {
148        let cache = ln2_cache().lock().expect("the ln 2 cache is never poisoned");
149        if let Some((w, lo, hi)) = cache.as_ref() {
150            if *w >= p {
151                return Iv {
152                    lo: Dyadic::new(lo.clone(), -w),
153                    hi: Dyadic::new(hi.clone(), -w),
154                }
155                .round_out(p);
156            }
157        }
158    }
159    let w = p + 16;
160    let (lo, hi) = atanh_scaled(&one(), &BigInt::from_i64(3), w);
161    let (lo, hi) = (lo.shl_bits(1), hi.shl_bits(1));
162    *ln2_cache().lock().expect("the ln 2 cache is never poisoned") =
163        Some((w, lo.clone(), hi.clone()));
164    Iv {
165        lo: Dyadic::new(lo, -w),
166        hi: Dyadic::new(hi, -w),
167    }
168    .round_out(p)
169}
170
171/// `e^x` for a dyadic point, on the `2^-p` grid: the raw enclosure at a working precision that
172/// covers the argument halvings and the size of the result, rounded outward.
173fn exp_point(x: &Dyadic, p: i64) -> Iv {
174    let s = (x.mag_bits() + 2).max(0);
175    let growth = if x.is_positive() {
176        (x.ceil_scaled(0).to_i64().unwrap_or(i64::MAX / 4) * 3 / 2).max(0)
177    } else {
178        0
179    };
180    exp_raw(x, p + s + growth + 16).round_out(p)
181}
182
183/// `e^x` enclosed at working precision `w`: halve until `|y| ≤ 1/4`, sum the Taylor series in
184/// fixed point at `2^-w` with every truncation counted (each step loses under one unit and the
185/// tail after the last nonzero term is under one unit), then square back with outward rounding.
186fn exp_raw(x: &Dyadic, w: i64) -> Iv {
187    let s = (x.mag_bits() + 2).max(0);
188    let y = x.shift(-s);
189    let unit = one().shl_bits(w as u64);
190    let yw = y.floor_scaled(w);
191    let mut term = unit.clone();
192    let mut sum = unit;
193    let mut k = 1i64;
194    loop {
195        term = floor_div(&term.mul(&yw), &BigInt::from_i64(k).shl_bits(w as u64));
196        if term.is_zero() {
197            break;
198        }
199        sum = sum.add(&term);
200        k += 1;
201    }
202    let err = BigInt::from_i64(2 * k + 6);
203    let mut iv = Iv {
204        lo: Dyadic::new(sum.sub(&err), -w),
205        hi: Dyadic::new(sum.add(&err), -w),
206    };
207    for _ in 0..s {
208        iv = iv.sq().round_out(w);
209    }
210    iv
211}
212
213/// `ln x` for a positive dyadic point: `x = r·2^k` with `r ∈ [1, 2)`, `ln r = 2·atanh((r−1)/(r+1))`.
214fn log_point(x: &Dyadic, p: i64) -> Iv {
215    let k = x.m.bit_len() as i64 + x.e - 1;
216    let r = x.shift(-k);
217    let r_num = r.floor_scaled(p + 64);
218    let den = one().shl_bits((p + 64) as u64);
219    let w = p + 24;
220    let (z_lo_num, z_hi_num) = (r_num.sub(&den), r_num.add(&one()).sub(&den));
221    let (lo_scaled, _) = atanh_scaled(&z_lo_num, &r_num.add(&den), w);
222    let (_, hi_scaled) = atanh_scaled(&z_hi_num, &r_num.add(&one()).add(&den), w);
223    let log_r = Iv {
224        lo: Dyadic::new(lo_scaled.shl_bits(1), -w),
225        hi: Dyadic::new(hi_scaled.shl_bits(1), -w),
226    };
227    let kb = Iv::point(Dyadic::int(k));
228    let extra = (k.unsigned_abs() as f64).log2().ceil() as i64 + 4;
229    kb.mul(&ln2_iv(w + extra)).add(&log_r).round_out(p)
230}
231
232/// `(sin x, cos x)` for a dyadic point with `|x| < 8`, on the `2^-p` grid.
233fn sin_cos_small(x: &Dyadic, p: i64) -> (Iv, Iv) {
234    let (s, c) = sin_cos_raw(x, p + 32);
235    (s.round_out(p), c.round_out(p))
236}
237
238/// `(sin x, cos x)` for `|x| < 8` at working precision `w`: divide by 16, sum both Taylor series
239/// in fixed point with every truncation counted, then four double-angle steps in interval
240/// arithmetic.
241fn sin_cos_raw(x: &Dyadic, w: i64) -> (Iv, Iv) {
242    let s = 4i64;
243    let y = x.shift(-s);
244    let yw = y.floor_scaled(w);
245    let scale = one().shl_bits(w as u64);
246    let mut term = yw.clone();
247    let mut sin_sum = yw.clone();
248    let mut cos_sum = scale.clone();
249    let mut cos_term = scale;
250    let mut k = 1i64;
251    loop {
252        cos_term = floor_div(
253            &cos_term.mul(&yw).mul(&yw).negated(),
254            &BigInt::from_i64((2 * k - 1) * (2 * k)).shl_bits(2 * w as u64),
255        );
256        term = floor_div(
257            &term.mul(&yw).mul(&yw).negated(),
258            &BigInt::from_i64((2 * k) * (2 * k + 1)).shl_bits(2 * w as u64),
259        );
260        if term.is_zero() && cos_term.is_zero() {
261            break;
262        }
263        sin_sum = sin_sum.add(&term);
264        cos_sum = cos_sum.add(&cos_term);
265        k += 1;
266    }
267    let err = Dyadic::new(BigInt::from_i64(2 * k + 6), -w);
268    let mut sn = Iv::point(Dyadic::new(sin_sum, -w)).widen(&err);
269    let mut cs = Iv::point(Dyadic::new(cos_sum, -w)).widen(&err);
270    let two = Iv::point(Dyadic::int(2));
271    let unit = Iv::point(Dyadic::int(1));
272    for _ in 0..s {
273        let s2 = two.mul(&sn.mul(&cs)).round_out(w);
274        let c2 = unit.add(&two.mul(&sn.sq()).neg()).round_out(w);
275        sn = s2.clamp_unit();
276        cs = c2.clamp_unit();
277    }
278    (sn, cs)
279}
280
281/// `(sin x, cos x)` for any dyadic point: subtract a whole number of turns `⌊x/2π⌋·2π` (exact
282/// periodicity, computed with an enclosure of π), then enclose at the reduced interval's midpoint
283/// and widen by its radius — both functions are 1-Lipschitz.
284fn sin_cos_point(x: &Dyadic, p: i64) -> (Iv, Iv) {
285    if x.mag_bits() <= 2 {
286        return sin_cos_small(x, p);
287    }
288    let guard = x.mag_bits().max(0) + 16;
289    let two_pi = pi_iv(p + guard).mul(&Iv::point(Dyadic::int(2)));
290    let k_num = x.floor_scaled(64);
291    let approx_turns = floor_div(&k_num, &two_pi.lo.floor_scaled(64).max(BigInt::from_i64(1)));
292    let k = Iv::point(Dyadic::new(approx_turns, 0));
293    let reduced = Iv::point(x.clone()).add(&k.mul(&two_pi).neg());
294    let mid = reduced.lo.add(&reduced.hi).shift(-1);
295    let radius = reduced.width().shift(-1);
296    let (s, c) = sin_cos_small(&mid, p + 4);
297    (
298        s.widen(&radius).clamp_unit().round_out(p),
299        c.widen(&radius).clamp_unit().round_out(p),
300    )
301}
302
303/// `⌊√n⌋` for `n ≥ 0` by integer Newton.
304fn isqrt(n: &BigInt) -> BigInt {
305    if n.is_zero() {
306        return BigInt::zero();
307    }
308    let mut x = one().shl_bits(n.bit_len() / 2 + 1);
309    loop {
310        let y = x.add(&n.div_rem(&x).expect("x > 0").0).shr_bits(1).expect("positive");
311        if y >= x {
312            return x;
313        }
314        x = y;
315    }
316}
317
318fn sqrt_floor(d: &Dyadic, p: i64) -> Dyadic {
319    Dyadic::new(isqrt(&d.floor_scaled(2 * p).max(BigInt::zero())), -p)
320}
321
322fn sqrt_ceil(d: &Dyadic, p: i64) -> Dyadic {
323    let n = d.ceil_scaled(2 * p).max(BigInt::zero());
324    let s = isqrt(&n);
325    let s = if s.mul(&s) < n { s.add(&one()) } else { s };
326    Dyadic::new(s, -p)
327}
328
329/// `1/[lo, hi]` for an interval excluding zero, rounded outward to `2^-p`.
330fn recip_iv(x: &Iv, p: i64) -> Iv {
331    let num = one();
332    let bound = |d: &Dyadic, up: bool| -> Dyadic {
333        let shift = p - d.e;
334        let (n, dd) = if shift >= 0 {
335            (num.shl_bits(shift as u64), d.m.clone())
336        } else {
337            (num.clone(), d.m.shl_bits((-shift) as u64))
338        };
339        let (n, dd) = if dd.is_negative() {
340            (n.negated(), dd.negated())
341        } else {
342            (n, dd)
343        };
344        let q = if up { ceil_div(&n, &dd) } else { floor_div(&n, &dd) };
345        Dyadic::new(q, -p)
346    };
347    Iv {
348        lo: bound(&x.hi, false),
349        hi: bound(&x.lo, true),
350    }
351}
352
353// ── complex algebraic numbers and the exact forms ─────────────────────────────────────────────
354
355use crate::complex_algebraic::ComplexAlgebraic as Calg;
356
357/// `Σ coefficient·e^{exponent}`, conjugate-symmetric (so real-valued), each exponent once, no zero
358/// coefficient, sorted by exponent descending.
359#[derive(Clone, Debug, PartialEq, Eq, Hash)]
360struct ExpSum {
361    terms: Vec<(Calg, Calg)>,
362}
363
364impl ExpSum {
365    fn from_terms(raw: Vec<(Calg, Calg)>) -> ExpSum {
366        let mut merged: Vec<(Calg, Calg)> = Vec::new();
367        let mut raw = raw;
368        raw.sort_by(|a, b| b.0.cmp(&a.0));
369        for (e, c) in raw {
370            match merged.last_mut() {
371                Some((le, lc)) if *le == e => *lc = lc.add(&c),
372                _ => merged.push((e, c)),
373            }
374        }
375        merged.retain(|(_, c)| !c.is_zero());
376        ExpSum { terms: merged }
377    }
378
379    fn constant(a: &Algebraic) -> ExpSum {
380        ExpSum::from_terms(vec![(Calg::zero(), Calg::real(a.clone()))])
381    }
382
383    fn add(&self, o: &ExpSum) -> ExpSum {
384        ExpSum::from_terms(self.terms.iter().chain(o.terms.iter()).cloned().collect())
385    }
386
387    fn neg(&self) -> ExpSum {
388        ExpSum {
389            terms: self.terms.iter().map(|(e, c)| (e.clone(), c.neg())).collect(),
390        }
391    }
392
393    fn mul(&self, o: &ExpSum) -> ExpSum {
394        let mut raw = Vec::with_capacity(self.terms.len() * o.terms.len());
395        for (e1, c1) in &self.terms {
396            for (e2, c2) in &o.terms {
397                raw.push((e1.add(e2), c1.mul(c2)));
398            }
399        }
400        ExpSum::from_terms(raw)
401    }
402
403    /// The reciprocal of a single term `c·e^β` is `c⁻¹·e^{−β}`; a longer sum has no exact
404    /// reciprocal in this ring.
405    fn recip(&self) -> Option<ExpSum> {
406        match self.terms.as_slice() {
407            [(e, c)] => Some(ExpSum {
408                terms: vec![(e.neg(), c.recip()?)],
409            }),
410            _ => None,
411        }
412    }
413
414    /// The constant, when the sum has no exponential term.
415    fn as_constant(&self) -> Option<Algebraic> {
416        match self.terms.as_slice() {
417            [] => Some(Algebraic::from_i64(0)),
418            [(e, c)] if e.is_zero() => Some(c.re.clone()),
419            _ => None,
420        }
421    }
422}
423
424/// `Σ coefficient·π^power` (powers may be negative), no zero coefficient, powers descending.
425#[derive(Clone, Debug, PartialEq, Eq, Hash)]
426struct PiPoly {
427    terms: Vec<(i32, Algebraic)>,
428}
429
430impl PiPoly {
431    fn from_terms(raw: Vec<(i32, Algebraic)>) -> PiPoly {
432        let mut raw = raw;
433        raw.sort_by_key(|t| std::cmp::Reverse(t.0));
434        let mut merged: Vec<(i32, Algebraic)> = Vec::new();
435        for (k, c) in raw {
436            match merged.last_mut() {
437                Some((lk, lc)) if *lk == k => *lc = lc.add(&c),
438                _ => merged.push((k, c)),
439            }
440        }
441        merged.retain(|(_, c)| !c.is_zero());
442        PiPoly { terms: merged }
443    }
444
445    fn constant(a: &Algebraic) -> PiPoly {
446        PiPoly::from_terms(vec![(0, a.clone())])
447    }
448
449    fn add(&self, o: &PiPoly) -> PiPoly {
450        PiPoly::from_terms(self.terms.iter().chain(o.terms.iter()).cloned().collect())
451    }
452
453    fn neg(&self) -> PiPoly {
454        PiPoly {
455            terms: self.terms.iter().map(|(k, c)| (*k, c.neg())).collect(),
456        }
457    }
458
459    fn mul(&self, o: &PiPoly) -> PiPoly {
460        let mut raw = Vec::new();
461        for (k1, c1) in &self.terms {
462            for (k2, c2) in &o.terms {
463                raw.push((k1 + k2, c1.mul(c2)));
464            }
465        }
466        PiPoly::from_terms(raw)
467    }
468
469    fn recip(&self) -> Option<PiPoly> {
470        match self.terms.as_slice() {
471            [(k, c)] => Some(PiPoly {
472                terms: vec![(-k, c.recip()?)],
473            }),
474            _ => None,
475        }
476    }
477
478    fn as_constant(&self) -> Option<Algebraic> {
479        match self.terms.as_slice() {
480            [] => Some(Algebraic::from_i64(0)),
481            [(0, c)] => Some(c.clone()),
482            _ => None,
483        }
484    }
485}
486
487// ── the expression DAG ────────────────────────────────────────────────────────────────────────
488
489/// An algebraic leaf keeps the narrowest dyadic isolating interval found so far.
490struct AlgLeaf {
491    value: Algebraic,
492    cache: Mutex<Option<Iv>>,
493}
494
495enum Node {
496    Alg(AlgLeaf),
497    Pi,
498    Add(ExactReal, ExactReal),
499    Mul(ExactReal, ExactReal),
500    Neg(ExactReal),
501    Recip(ExactReal),
502    Exp(ExactReal),
503    Log(ExactReal),
504    Sin(ExactReal),
505    Cos(ExactReal),
506    Sqrt(ExactReal),
507    /// One component of the solution of a polynomial differential equation.
508    Flow(Arc<FlowSolution>, usize),
509    /// A number a program's function defines.
510    Defined(Arc<Defined>),
511}
512
513// ── real numbers a program defines ────────────────────────────────────────────────────────────
514
515/// A program's function a real number is defined by, as the tier running the program calls it from
516/// inside a refinement. The tier evaluates the function in a context of its own, so a call made in
517/// the middle of another computation changes nothing the program can see.
518pub trait Approximant: Send + Sync {
519    /// The function's answer at `n`, as the rational it is; `Err` is the function's own failure, or
520    /// an answer that is no rational.
521    fn call(&self, n: u64) -> Result<Rational, String>;
522}
523
524/// How a program's function gives its real number.
525#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
526pub enum Definition {
527    /// `f(i)` is the digit in place `i` of the number written in base `base`: the number is
528    /// `f(0).f(1)f(2)…`, digit `i` worth `base^-i`.
529    Digits { base: u32 },
530    /// `f(n)` is within `2^-n` of the number.
531    Approximations,
532}
533
534impl Definition {
535    /// The definition `kind` names — `digits` in `base`, or `approximations` — refused in the words
536    /// every tier uses when it is neither, or a base below 2.
537    pub fn named(kind: &str, base: i64) -> Result<Definition, String> {
538        match kind {
539            "digits" => u32::try_from(base)
540                .ok()
541                .filter(|b| *b >= 2)
542                .map(|base| Definition::Digits { base })
543                .ok_or_else(|| format!("a real number's digits are in a base from 2 up, got {base}")),
544            "approximations" => Ok(Definition::Approximations),
545            other => Err(format!(
546                "a real number is defined by its digits or its approximations, got {other}"
547            )),
548        }
549    }
550}
551
552/// What a real number built from a function a program defines is, where a task, a channel or the
553/// wire refuses it: it stays with the program that defines it.
554pub const STAYS_WITH_ITS_PROGRAM: &str = "a real number a function of the program defines";
555
556/// The sentence a program stops with when it hands a task, or sends through a channel, a value that
557/// holds a real number a function of the program defines.
558pub fn not_sendable() -> String {
559    format!("cannot send value through a channel: NotSendable({STAYS_WITH_ITS_PROGRAM:?})")
560}
561
562/// The refusal of a function's answer at `n` that is a value of `type_name`, not a rational.
563pub fn not_a_rational_answer(name: &str, n: u64, type_name: &str) -> String {
564    format!("{name}({n}) is {type_name}, and a real number is defined by whole or rational answers")
565}
566
567/// What a defined real has read of its function so far.
568enum Known {
569    /// The digits from place `0` on.
570    Digits(Vec<u32>),
571    /// The approximations made, by the precision each was asked at.
572    Approximations(Vec<(u32, Rational)>),
573}
574
575/// A real number a program's function defines, with what it has read of the function.
576pub(crate) struct Defined {
577    /// The function, as the program spells it.
578    name: String,
579    definition: Definition,
580    source: Arc<dyn Approximant>,
581    known: Mutex<Known>,
582}
583
584/// `2^-n` as a rational.
585fn two_to_minus(n: u32) -> Rational {
586    Rational::new(one(), one().shl_bits(u64::from(n))).expect("a power of two is nonzero")
587}
588
589impl Defined {
590    fn describe(&self) -> String {
591        match self.definition {
592            Definition::Digits { base } => {
593                format!("the real number whose digits in base {base} are {}", self.name)
594            }
595            Definition::Approximations => format!("the real number approximated by {}", self.name),
596        }
597    }
598
599    /// An enclosure at most `2^-(p+1)` wide, from as many digits, or as close an approximation, as
600    /// that takes. The function is called without the record of what it answered held, so an answer
601    /// that reads this number again finds it as it was.
602    fn enclose(&self, p: i64) -> Result<Iv, RealError> {
603        match self.definition {
604            Definition::Digits { base } => self.enclose_digits(base, p),
605            Definition::Approximations => self.enclose_approximations(p),
606        }
607    }
608
609    /// The first `count` digits, read from the function where they are not yet known.
610    fn digits(&self, base: u32, count: usize) -> Result<Vec<u32>, RealError> {
611        loop {
612            let next = match &*self.known.lock().expect("unpoisoned") {
613                Known::Digits(d) if d.len() >= count => return Ok(d[..count].to_vec()),
614                Known::Digits(d) => d.len(),
615                Known::Approximations(_) => unreachable!("a real defined by digits reads digits"),
616            };
617            let answer = self.source.call(next as u64).map_err(RealError::Domain)?;
618            let digit = answer
619                .to_bigint()
620                .filter(|_| answer.is_integer())
621                .and_then(|d| d.to_u64())
622                .filter(|d| *d < u64::from(base))
623                .ok_or_else(|| {
624                    RealError::Domain(format!(
625                        "digit {next} of {} is {answer}: a digit in base {base} is a whole number from 0 to {}",
626                        self.describe(),
627                        base - 1
628                    ))
629                })?;
630            if let Known::Digits(d) = &mut *self.known.lock().expect("unpoisoned") {
631                if d.len() == next {
632                    d.push(digit as u32);
633                }
634            }
635        }
636    }
637
638    /// `[S, S + base^-k]` for the sum `S` of the digits in places `0..=k`, with `base^-k ≤ 2^-(p+2)`.
639    fn enclose_digits(&self, base: u32, p: i64) -> Result<Iv, RealError> {
640        let b = Rational::from_i64(i64::from(base));
641        let mut places = 0usize;
642        let mut tail = Rational::from_i64(1);
643        let bound = two_to_minus(u32::try_from((p + 2).max(0)).unwrap_or(u32::MAX));
644        while tail > bound {
645            tail = tail.div(&b).expect("a base is positive");
646            places += 1;
647        }
648        let digits = self.digits(base, places + 1)?;
649        let mut sum = Rational::zero();
650        let mut weight = Rational::from_i64(1);
651        for d in &digits {
652            sum = sum.add(&weight.mul(&Rational::from_i64(i64::from(*d))));
653            weight = weight.div(&b).expect("a base is positive");
654        }
655        Ok(Iv {
656            lo: Dyadic::from_rational_floor(&sum, p + 2),
657            hi: Dyadic::from_rational_ceil(&sum.add(&tail), p + 2),
658        })
659    }
660
661    /// `[q − 2^-n, q + 2^-n]` for the approximation `q` at precision `n = p + 2`, every
662    /// approximation checked against those made before: two that lie further apart than their
663    /// precisions allow approximate no one number.
664    fn enclose_approximations(&self, p: i64) -> Result<Iv, RealError> {
665        let n = u32::try_from((p + 2).max(0)).unwrap_or(u32::MAX);
666        let known = match &*self.known.lock().expect("unpoisoned") {
667            Known::Approximations(made) => made.iter().find(|(m, _)| *m == n).map(|(_, q)| q.clone()),
668            Known::Digits(_) => unreachable!("a real defined by approximations reads approximations"),
669        };
670        let q = match known {
671            Some(q) => q,
672            None => {
673                let q = self.source.call(u64::from(n)).map_err(RealError::Domain)?;
674                if let Known::Approximations(made) = &mut *self.known.lock().expect("unpoisoned") {
675                    for (m, r) in made.iter() {
676                        if q.sub(r).abs() > two_to_minus(n).add(&two_to_minus(*m)) {
677                            return Err(RealError::Domain(format!(
678                                "{name}({n}) is {q} and {name}({m}) is {r}, further apart than 2^-{n} + 2^-{m}: \
679                                 {name} approximates no one real number",
680                                name = self.name
681                            )));
682                        }
683                    }
684                    if !made.iter().any(|(m, _)| *m == n) {
685                        made.push((n, q.clone()));
686                    }
687                }
688                q
689            }
690        };
691        let eps = two_to_minus(n);
692        Ok(Iv {
693            lo: Dyadic::from_rational_floor(&q.sub(&eps), p + 2),
694            hi: Dyadic::from_rational_ceil(&q.add(&eps), p + 2),
695        })
696    }
697}
698
699/// The solution of `x' = f(x)` from `start` at time `t`, every component enclosed together.
700pub(crate) struct FlowSolution {
701    field: Field,
702    start: Vec<ExactReal>,
703    t: Rational,
704    /// The working precision of the tightest enclosure computed so far, and that enclosure.
705    cache: Mutex<Option<(i64, Vec<Iv>)>>,
706}
707
708impl fmt::Debug for FlowSolution {
709    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
710        write!(f, "FlowSolution(t = {}, {} components)", self.t, self.start.len())
711    }
712}
713
714impl FlowSolution {
715    /// Component `i` within `2^-(p+2)`, the walk's working precision doubling until it is.
716    fn enclose(&self, i: usize, p: i64, cap: i64) -> Result<Iv, RealError> {
717        let within = |iv: &Iv| iv.width().mag_bits() <= -(p + 2);
718        if let Some((_, ivs)) = self.cache.lock().expect("unpoisoned").as_ref() {
719            if within(&ivs[i]) {
720                return Ok(ivs[i].round_out(p + 2));
721            }
722        }
723        let mut q = (p + 32).max(64);
724        loop {
725            let start = self
726                .start
727                .iter()
728                .map(|x| x.enclosed(q + 8, cap))
729                .collect::<Result<Vec<Iv>, RealError>>()?;
730            match crate::ode::flow(&self.field, &start, &self.t, q, (q / 2).max(40)) {
731                Ok(ivs) => {
732                    let done = within(&ivs[i]);
733                    let answer = ivs[i].round_out(p + 2);
734                    *self.cache.lock().expect("unpoisoned") = Some((q, ivs));
735                    if done {
736                        return Ok(answer);
737                    }
738                }
739                Err(FlowError::Escapes) => {
740                    return Err(RealError::Domain(
741                        "the solution of the flow leaves every bound before its time".to_string(),
742                    ))
743                }
744                Err(FlowError::Shape) => {
745                    return Err(RealError::Domain(
746                        "a flow's field and its start disagree in shape".to_string(),
747                    ))
748                }
749            }
750            if q >= cap {
751                return Err(RealError::Inseparable {
752                    what: "the solution of the flow".to_string(),
753                    bits: cap as u32,
754                });
755            }
756            q = (q * 2).min(cap);
757        }
758    }
759}
760
761/// The sign of the integer polynomial `min` at the dyadic `m·2^-k`, scaled by `2^{k·deg}`.
762fn poly_sign_at(min: &[BigInt], d: &Dyadic) -> Ordering {
763    let k = (-d.e).max(0);
764    let m = if d.e > 0 { d.m.shl_bits(d.e as u64) } else { d.m.clone() };
765    let deg = min.len() - 1;
766    let mut acc = BigInt::zero();
767    let mut mp = one();
768    for (i, c) in min.iter().enumerate() {
769        acc = acc.add(&c.mul(&mp).shl_bits((k as u64) * (deg - i) as u64));
770        mp = mp.mul(&m);
771    }
772    acc.cmp(&BigInt::zero())
773}
774
775impl AlgLeaf {
776    /// The root's dyadic isolating interval refined to width `2^-p`. The first call seeds it by
777    /// rounding the algebraic number's own isolating interval INWARD onto a dyadic grid fine enough
778    /// that the root stays between the rounded ends (checked by the sign of the minimal polynomial,
779    /// which changes only at the root inside); later calls continue from the cached interval.
780    fn enclose(&self, p: i64) -> Iv {
781        if let Some(r) = self.value.to_rational() {
782            return Iv {
783                lo: Dyadic::from_rational_floor(&r, p),
784                hi: Dyadic::from_rational_ceil(&r, p),
785            };
786        }
787        let min_poly = self.value.minimal_polynomial();
788        let min = min_poly.primitive_int();
789        let mut cache = self.cache.lock().expect("an algebraic leaf cache is never poisoned");
790        let mut iv = match cache.take() {
791            Some(iv) => iv,
792            None => {
793                let (lo, hi) = self.value.enclosure(8);
794                let lo_sign = min_poly.eval(&lo).cmp(&Rational::zero());
795                let hi_sign = min_poly.eval(&hi).cmp(&Rational::zero());
796                let mut k = 16i64;
797                loop {
798                    let dlo = Dyadic::from_rational_ceil(&lo, k);
799                    let dhi = Dyadic::from_rational_floor(&hi, k);
800                    if dlo.cmp(&dhi) == Ordering::Less
801                        && poly_sign_at(&min, &dlo) == lo_sign
802                        && poly_sign_at(&min, &dhi) == hi_sign
803                    {
804                        break Iv { lo: dlo, hi: dhi };
805                    }
806                    k *= 2;
807                }
808            }
809        };
810        let eps = Dyadic::new(one(), -p);
811        let lo_sign = poly_sign_at(&min, &iv.lo);
812        while iv.width().cmp(&eps) == Ordering::Greater {
813            let mid = iv.lo.add(&iv.hi).shift(-1);
814            let s = poly_sign_at(&min, &mid);
815            if s == Ordering::Equal {
816                iv = Iv::point(mid);
817                break;
818            }
819            if s == lo_sign {
820                iv.lo = mid;
821            } else {
822                iv.hi = mid;
823            }
824        }
825        *cache = Some(iv.clone());
826        iv
827    }
828}
829
830impl Node {
831    /// An enclosure computed with working precision `p`; `cap` bounds every precision search.
832    fn enclose(&self, p: i64, cap: i64) -> Result<Iv, RealError> {
833        match self {
834            Node::Alg(leaf) => Ok(leaf.enclose(p + 2)),
835            Node::Pi => Ok(pi_iv(p + 2)),
836            Node::Add(a, b) => {
837                Ok(a.enclosed(p + 2, cap)?.add(&b.enclosed(p + 2, cap)?).round_out(p + 1))
838            }
839            Node::Neg(a) => Ok(a.enclosed(p, cap)?.neg()),
840            Node::Mul(a, b) => {
841                let ma = a.enclosed(4, cap)?.mag_bits().max(0);
842                let mb = b.enclosed(4, cap)?.mag_bits().max(0);
843                let x = a.enclosed(p + mb + 4, cap)?;
844                let y = b.enclosed(p + ma + 4, cap)?;
845                Ok(x.mul(&y).round_out(p + 2))
846            }
847            Node::Recip(a) => {
848                let mut q = p.max(8);
849                loop {
850                    let x = a.enclosed(q, cap)?;
851                    if !x.contains_zero() {
852                        let nearest = if x.lo.is_positive() { x.lo.clone() } else { x.hi.neg() };
853                        let lower = 1 - nearest.mag_bits();
854                        let need = p + 2 * lower.max(0) + 6;
855                        if q >= need {
856                            return Ok(recip_iv(&x, p + 2));
857                        }
858                        q = need;
859                        continue;
860                    }
861                    if q >= cap {
862                        return Err(RealError::Inseparable {
863                            what: "the divisor from zero".to_string(),
864                            bits: cap as u32,
865                        });
866                    }
867                    q = (q * 2).min(cap);
868                }
869            }
870            Node::Exp(a) => {
871                let rough = a.enclosed(4, cap)?;
872                let growth = if rough.hi.is_positive() {
873                    rough.hi.ceil_scaled(0).to_i64().unwrap_or(i64::MAX / 8) * 3 / 2 + 2
874                } else {
875                    2
876                };
877                let x = a.enclosed(p + growth, cap)?;
878                Ok(Iv {
879                    lo: exp_point(&x.lo, p + 4).lo,
880                    hi: exp_point(&x.hi, p + 4).hi,
881                })
882            }
883            Node::Log(a) => {
884                let mut q = p.max(8);
885                loop {
886                    let x = a.enclosed(q, cap)?;
887                    if x.hi.is_negative() || (x.hi.m.is_zero() && x.lo.m.is_zero()) {
888                        return Err(RealError::Domain(
889                            "logarithm of a nonpositive number".to_string(),
890                        ));
891                    }
892                    if x.lo.is_positive() {
893                        let lower = 1 - x.lo.mag_bits();
894                        let need = p + lower.max(0) + 6;
895                        if q >= need {
896                            return Ok(Iv {
897                                lo: log_point(&x.lo, p + 4).lo,
898                                hi: log_point(&x.hi, p + 4).hi,
899                            });
900                        }
901                        q = need;
902                        continue;
903                    }
904                    if q >= cap {
905                        return Err(RealError::Inseparable {
906                            what: "the logarithm's argument from zero".to_string(),
907                            bits: cap as u32,
908                        });
909                    }
910                    q = (q * 2).min(cap);
911                }
912            }
913            Node::Sqrt(a) => {
914                let mut q = p.max(8);
915                loop {
916                    let x = a.enclosed(q, cap)?;
917                    if x.hi.is_negative() {
918                        return Err(RealError::Domain(
919                            "square root of a negative number".to_string(),
920                        ));
921                    }
922                    if x.lo.is_positive() {
923                        let lower = 1 - x.lo.mag_bits();
924                        let need = p + lower.max(0) + 6;
925                        if q >= need {
926                            return Ok(Iv {
927                                lo: sqrt_floor(&x.lo, p + 2),
928                                hi: sqrt_ceil(&x.hi, p + 2),
929                            });
930                        }
931                        q = need;
932                        continue;
933                    }
934                    if q >= cap {
935                        return Err(RealError::Inseparable {
936                            what: "the square root's argument from zero".to_string(),
937                            bits: cap as u32,
938                        });
939                    }
940                    q = (q * 2).min(cap);
941                }
942            }
943            Node::Flow(solution, i) => solution.enclose(*i, p, cap),
944            Node::Defined(d) => d.enclose(p),
945            Node::Sin(a) | Node::Cos(a) => {
946                let x = a.enclosed(p + 2, cap)?;
947                let mid = x.lo.add(&x.hi).shift(-1);
948                let radius = x.width().shift(-1);
949                let (s, c) = sin_cos_point(&mid, p + 4);
950                let v = if matches!(self, Node::Sin(_)) { s } else { c };
951                Ok(v.widen(&radius).clamp_unit().round_out(p + 2))
952            }
953        }
954    }
955}
956
957// ── the public type ───────────────────────────────────────────────────────────────────────────
958
959#[derive(Clone)]
960enum Form {
961    Alg(Algebraic),
962    Exp(ExpSum),
963    Pi(PiPoly),
964    General(Arc<Node>),
965}
966
967/// A computable real number. See the module documentation.
968#[derive(Clone)]
969pub struct ExactReal {
970    form: Form,
971    tree: Arc<OnceLock<Arc<Node>>>,
972    /// The tightest enclosure computed so far, with the working precision it was computed at; every
973    /// clone shares it, so a node a deep expression reaches many times is refined once per
974    /// precision rather than once per path to it.
975    best: Arc<Mutex<Option<(i64, Iv)>>>,
976}
977
978/// Significant digits a general computable real prints with.
979pub const DISPLAY_DIGITS: u32 = 30;
980
981/// The precision budget, in bits, of a comparison whose budget the caller does not state.
982pub const DEFAULT_BUDGET: u32 = 4096;
983
984fn half_real() -> ExactReal {
985    ExactReal::from_rational(Rational::from_ratio_i64(1, 2).expect("2 ≠ 0"))
986}
987
988impl ExactReal {
989    fn of(form: Form) -> ExactReal {
990        let form = match form {
991            Form::Exp(s) => match s.as_constant() {
992                Some(a) => Form::Alg(a),
993                None => Form::Exp(s),
994            },
995            Form::Pi(p) => match p.as_constant() {
996                Some(a) => Form::Alg(a),
997                None => Form::Pi(p),
998            },
999            other => other,
1000        };
1001        ExactReal {
1002            form,
1003            tree: Arc::new(OnceLock::new()),
1004            best: Arc::new(Mutex::new(None)),
1005        }
1006    }
1007
1008    /// An enclosure computed with working precision at least `p`: the tightest one already known
1009    /// when it was computed at `p` or above, else a fresh one, kept when it is the tightest.
1010    fn enclosed(&self, p: i64, cap: i64) -> Result<Iv, RealError> {
1011        if let Some((q, iv)) = self.best.lock().expect("unpoisoned").as_ref() {
1012            if *q >= p {
1013                return Ok(iv.clone());
1014            }
1015        }
1016        let iv = self.node().enclose(p, cap)?;
1017        let mut best = self.best.lock().expect("unpoisoned");
1018        if best.as_ref().is_none_or(|(q, _)| *q < p) {
1019            *best = Some((p, iv.clone()));
1020        }
1021        Ok(iv)
1022    }
1023
1024    fn general(node: Node) -> ExactReal {
1025        ExactReal::of(Form::General(Arc::new(node)))
1026    }
1027
1028    pub fn from_i64(n: i64) -> ExactReal {
1029        Self::from_algebraic(Algebraic::from_i64(n))
1030    }
1031
1032    pub fn from_rational(r: Rational) -> ExactReal {
1033        Self::from_algebraic(Algebraic::from_rational(r))
1034    }
1035
1036    pub fn from_algebraic(a: Algebraic) -> ExactReal {
1037        Self::of(Form::Alg(a))
1038    }
1039
1040    /// The exact value of a finite float; `None` for NaN and the infinities.
1041    pub fn from_f64(f: f64) -> Option<ExactReal> {
1042        rational_from_f64_exact(f).map(Self::from_rational)
1043    }
1044
1045    pub fn pi() -> ExactReal {
1046        Self::of(Form::Pi(PiPoly {
1047            terms: vec![(1, Algebraic::from_i64(1))],
1048        }))
1049    }
1050
1051    pub fn e() -> ExactReal {
1052        Self::from_i64(1).exp()
1053    }
1054
1055    /// The value as an algebraic number, when it is one exactly.
1056    pub fn exact_algebraic(&self) -> Option<Algebraic> {
1057        match &self.form {
1058            Form::Alg(a) => Some(a.clone()),
1059            _ => None,
1060        }
1061    }
1062
1063    /// True only when an exact form proves the value is zero.
1064    pub fn is_exactly_zero(&self) -> bool {
1065        matches!(&self.form, Form::Alg(a) if a.is_zero())
1066    }
1067
1068    /// Held in a form whose equality is decidable.
1069    pub fn is_exact_form(&self) -> bool {
1070        !matches!(&self.form, Form::General(_))
1071    }
1072
1073    fn exp_sum(&self) -> Option<ExpSum> {
1074        match &self.form {
1075            Form::Alg(a) => Some(ExpSum::constant(a)),
1076            Form::Exp(s) => Some(s.clone()),
1077            _ => None,
1078        }
1079    }
1080
1081    fn pi_poly(&self) -> Option<PiPoly> {
1082        match &self.form {
1083            Form::Alg(a) => Some(PiPoly::constant(a)),
1084            Form::Pi(p) => Some(p.clone()),
1085            _ => None,
1086        }
1087    }
1088
1089    fn sum_of(terms: Vec<ExactReal>) -> ExactReal {
1090        terms
1091            .into_iter()
1092            .reduce(|acc, t| ExactReal::general(Node::Add(acc, t)))
1093            .unwrap_or_else(|| ExactReal::from_i64(0))
1094    }
1095
1096    fn product_of(factors: Vec<ExactReal>) -> ExactReal {
1097        factors
1098            .into_iter()
1099            .reduce(|acc, t| ExactReal::general(Node::Mul(acc, t)))
1100            .unwrap_or_else(|| ExactReal::from_i64(1))
1101    }
1102
1103    /// The expression DAG that evaluates this value; built once and shared by every clone.
1104    fn node(&self) -> Arc<Node> {
1105        self.tree
1106            .get_or_init(|| match &self.form {
1107                Form::General(n) => n.clone(),
1108                Form::Alg(a) => Arc::new(Node::Alg(AlgLeaf {
1109                    value: a.clone(),
1110                    cache: Mutex::new(None),
1111                })),
1112                Form::Pi(p) => {
1113                    let terms = p
1114                        .terms
1115                        .iter()
1116                        .map(|(k, c)| {
1117                            let base = if *k >= 0 {
1118                                ExactReal::general(Node::Pi)
1119                            } else {
1120                                ExactReal::general(Node::Recip(ExactReal::general(Node::Pi)))
1121                            };
1122                            let mut factors = vec![ExactReal::from_algebraic(c.clone())];
1123                            factors.extend((0..k.unsigned_abs()).map(|_| base.clone()));
1124                            ExactReal::product_of(factors)
1125                        })
1126                        .collect();
1127                    ExactReal::sum_of(terms).node()
1128                }
1129                Form::Exp(s) => {
1130                    let terms = s
1131                        .terms
1132                        .iter()
1133                        .map(|(e, c)| {
1134                            let mut factors = Vec::new();
1135                            if !e.re.is_zero() {
1136                                factors.push(ExactReal::general(Node::Exp(
1137                                    ExactReal::from_algebraic(e.re.clone()),
1138                                )));
1139                            }
1140                            if e.im.is_zero() {
1141                                factors.push(ExactReal::from_algebraic(c.re.clone()));
1142                            } else {
1143                                let v = ExactReal::from_algebraic(e.im.clone());
1144                                let mut parts = Vec::new();
1145                                if !c.re.is_zero() {
1146                                    parts.push(ExactReal::product_of(vec![
1147                                        ExactReal::from_algebraic(c.re.clone()),
1148                                        ExactReal::general(Node::Cos(v.clone())),
1149                                    ]));
1150                                }
1151                                if !c.im.is_zero() {
1152                                    parts.push(ExactReal::product_of(vec![
1153                                        ExactReal::from_algebraic(c.im.neg()),
1154                                        ExactReal::general(Node::Sin(v)),
1155                                    ]));
1156                                }
1157                                factors.push(ExactReal::sum_of(parts));
1158                            }
1159                            ExactReal::product_of(factors)
1160                        })
1161                        .collect();
1162                    ExactReal::sum_of(terms).node()
1163                }
1164            })
1165            .clone()
1166    }
1167
1168    pub fn add(&self, o: &ExactReal) -> ExactReal {
1169        if let (Form::Alg(a), Form::Alg(b)) = (&self.form, &o.form) {
1170            return Self::from_algebraic(a.add(b));
1171        }
1172        if let (Some(a), Some(b)) = (self.exp_sum(), o.exp_sum()) {
1173            return Self::of(Form::Exp(a.add(&b)));
1174        }
1175        if let (Some(a), Some(b)) = (self.pi_poly(), o.pi_poly()) {
1176            return Self::of(Form::Pi(a.add(&b)));
1177        }
1178        Self::general(Node::Add(self.clone(), o.clone()))
1179    }
1180
1181    pub fn neg(&self) -> ExactReal {
1182        match &self.form {
1183            Form::Alg(a) => Self::from_algebraic(a.neg()),
1184            Form::Exp(s) => Self::of(Form::Exp(s.neg())),
1185            Form::Pi(p) => Self::of(Form::Pi(p.neg())),
1186            Form::General(_) => Self::general(Node::Neg(self.clone())),
1187        }
1188    }
1189
1190    pub fn sub(&self, o: &ExactReal) -> ExactReal {
1191        self.add(&o.neg())
1192    }
1193
1194    pub fn mul(&self, o: &ExactReal) -> ExactReal {
1195        if let (Form::Alg(a), Form::Alg(b)) = (&self.form, &o.form) {
1196            return Self::from_algebraic(a.mul(b));
1197        }
1198        if let (Some(a), Some(b)) = (self.exp_sum(), o.exp_sum()) {
1199            return Self::of(Form::Exp(a.mul(&b)));
1200        }
1201        if let (Some(a), Some(b)) = (self.pi_poly(), o.pi_poly()) {
1202            return Self::of(Form::Pi(a.mul(&b)));
1203        }
1204        Self::general(Node::Mul(self.clone(), o.clone()))
1205    }
1206
1207    /// `1/self`: refused when the value is exactly zero; a general value that turns out to be zero
1208    /// fails when it is evaluated.
1209    pub fn recip(&self) -> Result<ExactReal, RealError> {
1210        if self.is_exactly_zero() {
1211            return Err(RealError::Domain("division by zero".to_string()));
1212        }
1213        Ok(match &self.form {
1214            Form::Alg(a) => Self::from_algebraic(a.recip().expect("nonzero")),
1215            Form::Exp(s) => match s.recip() {
1216                Some(r) => Self::of(Form::Exp(r)),
1217                None => Self::general(Node::Recip(self.clone())),
1218            },
1219            Form::Pi(p) => match p.recip() {
1220                Some(r) => Self::of(Form::Pi(r)),
1221                None => Self::general(Node::Recip(self.clone())),
1222            },
1223            Form::General(_) => Self::general(Node::Recip(self.clone())),
1224        })
1225    }
1226
1227    pub fn div(&self, o: &ExactReal) -> Result<ExactReal, RealError> {
1228        Ok(self.mul(&o.recip()?))
1229    }
1230
1231    /// `self^k`; `None` only for zero to a negative power.
1232    pub fn pow(&self, k: i32) -> Option<ExactReal> {
1233        if k < 0 {
1234            return self.recip().ok()?.pow(-k);
1235        }
1236        if let Form::Alg(a) = &self.form {
1237            return a.pow(k).map(Self::from_algebraic);
1238        }
1239        let mut acc = Self::from_i64(1);
1240        let mut base = self.clone();
1241        let mut e = k as u32;
1242        while e > 0 {
1243            if e & 1 == 1 {
1244                acc = acc.mul(&base);
1245            }
1246            e >>= 1;
1247            if e > 0 {
1248                base = base.mul(&base);
1249            }
1250        }
1251        Some(acc)
1252    }
1253
1254    /// The real number `source` defines (see [`Definition`]), `name` being how the program spells the
1255    /// function. Its digits or approximations are read from the function only as a refinement needs
1256    /// them, and each is read once.
1257    pub fn defined(definition: Definition, name: &str, source: Arc<dyn Approximant>) -> ExactReal {
1258        let known = match definition {
1259            Definition::Digits { .. } => Known::Digits(Vec::new()),
1260            Definition::Approximations => Known::Approximations(Vec::new()),
1261        };
1262        ExactReal::general(Node::Defined(Arc::new(Defined {
1263            name: name.to_string(),
1264            definition,
1265            source,
1266            known: Mutex::new(known),
1267        })))
1268    }
1269
1270    /// Whether the number can travel as data: one built from a function a program defines cannot, its
1271    /// function staying with the program.
1272    pub fn is_transmissible(&self) -> bool {
1273        match &self.form {
1274            Form::General(n) => n.is_transmissible(),
1275            _ => true,
1276        }
1277    }
1278
1279    /// The solution of `x' = f(x)` from `start` at time `t ≥ 0`, one general real per component,
1280    /// enclosed by validated Taylor integration ([`crate::ode`]); `None` when the field's shape
1281    /// does not match the start or the time is negative. The components share one integration.
1282    pub fn flow(field: &Field, start: &[ExactReal], t: &Rational) -> Option<Vec<ExactReal>> {
1283        if start.is_empty()
1284            || start.len() != field.dimension()
1285            || field.components.iter().any(|c| c.iter().any(|m| m.powers.len() != start.len()))
1286            || t.is_negative()
1287        {
1288            return None;
1289        }
1290        let solution = Arc::new(FlowSolution {
1291            field: field.clone(),
1292            start: start.to_vec(),
1293            t: t.clone(),
1294            cache: Mutex::new(None),
1295        });
1296        Some(
1297            (0..start.len())
1298                .map(|i| ExactReal::general(Node::Flow(solution.clone(), i)))
1299                .collect(),
1300        )
1301    }
1302
1303    /// The nonnegative square root; exact where [`ExactReal::nth_root`] is.
1304    pub fn sqrt(&self) -> Result<ExactReal, RealError> {
1305        self.nth_root(2)
1306    }
1307
1308    /// The real `n`-th root (`n ≥ 1`), the nonnegative one for even `n`. Exact on algebraic numbers
1309    /// and on a single real exponential term, `ⁿ√(c·e^β) = ⁿ√c·e^(β/n)`; an even root of a value
1310    /// held exactly as negative is refused. Any other value's square root is evaluated as one, and
1311    /// its higher roots are `e^(ln|x|/n)` with the sign of `x`, which must then be told from zero.
1312    pub fn nth_root(&self, n: u32) -> Result<ExactReal, RealError> {
1313        assert!(n >= 1, "a root has a positive index");
1314        if n == 1 {
1315            return Ok(self.clone());
1316        }
1317        let refused = || RealError::Domain(negative_root(n));
1318        match &self.form {
1319            Form::Alg(a) => return a.nth_root(n).map(Self::from_algebraic).ok_or_else(refused),
1320            Form::Exp(s) => {
1321                if let [(e, c)] = s.terms.as_slice() {
1322                    if e.is_real() && c.is_real() {
1323                        let root = c.re.nth_root(n).ok_or_else(refused)?;
1324                        let beta = e.re.div(&Algebraic::from_i64(i64::from(n))).expect("n ≥ 1");
1325                        return Ok(Self::of(Form::Exp(ExpSum::from_terms(vec![(
1326                            Calg::real(beta),
1327                            Calg::real(root),
1328                        )]))));
1329                    }
1330                }
1331            }
1332            _ => {}
1333        }
1334        if n == 2 {
1335            return Ok(Self::general(Node::Sqrt(self.clone())));
1336        }
1337        let index = Self::from_rational(Rational::from_ratio_i64(1, i64::from(n)).expect("n ≥ 1"));
1338        match self.compare(&Self::from_i64(0), DEFAULT_BUDGET)? {
1339            Ordering::Equal => Ok(Self::from_i64(0)),
1340            Ordering::Greater => Ok(self.log()?.mul(&index).exp()),
1341            Ordering::Less if n % 2 == 0 => Err(refused()),
1342            Ordering::Less => Ok(self.neg().log()?.mul(&index).exp().neg()),
1343        }
1344    }
1345
1346    pub fn exp(&self) -> ExactReal {
1347        match &self.form {
1348            Form::Alg(a) if a.is_zero() => Self::from_i64(1),
1349            Form::Alg(a) => Self::of(Form::Exp(ExpSum::from_terms(vec![(
1350                Calg::real(a.clone()),
1351                Calg::real(Algebraic::from_i64(1)),
1352            )]))),
1353            Form::General(n) => match n.as_ref() {
1354                Node::Log(inner) => inner.clone(),
1355                _ => Self::general(Node::Exp(self.clone())),
1356            },
1357            _ => Self::general(Node::Exp(self.clone())),
1358        }
1359    }
1360
1361    /// The natural logarithm: refused at an exactly known nonpositive value; exact on a single real
1362    /// exponential term with a positive algebraic coefficient (`log(c·e^β) = β + log c`).
1363    pub fn log(&self) -> Result<ExactReal, RealError> {
1364        match &self.form {
1365            Form::Alg(a) if a.signum() <= 0 => Err(RealError::Domain(
1366                "logarithm of a nonpositive number".to_string(),
1367            )),
1368            Form::Alg(a) if *a == Algebraic::from_i64(1) => Ok(Self::from_i64(0)),
1369            Form::Exp(s) => match s.terms.as_slice() {
1370                [(e, c)] if e.is_real() && c.is_real() && c.re.signum() > 0 => {
1371                    let beta = Self::from_algebraic(e.re.clone());
1372                    if c.re == Algebraic::from_i64(1) {
1373                        Ok(beta)
1374                    } else {
1375                        Ok(beta.add(&Self::from_algebraic(c.re.clone()).log()?))
1376                    }
1377                }
1378                _ => Ok(Self::general(Node::Log(self.clone()))),
1379            },
1380            Form::General(n) => match n.as_ref() {
1381                Node::Exp(inner) => Ok(inner.clone()),
1382                _ => Ok(Self::general(Node::Log(self.clone()))),
1383            },
1384            _ => Ok(Self::general(Node::Log(self.clone()))),
1385        }
1386    }
1387
1388    fn trig(&self, sine: bool) -> ExactReal {
1389        match &self.form {
1390            Form::Alg(a) if a.is_zero() => Self::from_i64(if sine { 0 } else { 1 }),
1391            Form::Alg(a) => {
1392                let half = Algebraic::from_rational(Rational::from_ratio_i64(1, 2).expect("2 ≠ 0"));
1393                let plus = Calg::imag(a.clone());
1394                let minus = Calg::imag(a.neg());
1395                let terms = if sine {
1396                    vec![(plus, Calg::imag(half.neg())), (minus, Calg::imag(half))]
1397                } else {
1398                    vec![(plus, Calg::real(half.clone())), (minus, Calg::real(half))]
1399                };
1400                Self::of(Form::Exp(ExpSum::from_terms(terms)))
1401            }
1402            _ => Self::general(if sine {
1403                Node::Sin(self.clone())
1404            } else {
1405                Node::Cos(self.clone())
1406            }),
1407        }
1408    }
1409
1410    pub fn sin(&self) -> ExactReal {
1411        self.trig(true)
1412    }
1413
1414    pub fn cos(&self) -> ExactReal {
1415        self.trig(false)
1416    }
1417
1418    pub fn cosh(&self) -> ExactReal {
1419        self.exp().add(&self.neg().exp()).mul(&half_real())
1420    }
1421
1422    pub fn sinh(&self) -> ExactReal {
1423        self.exp().sub(&self.neg().exp()).mul(&half_real())
1424    }
1425
1426    /// A closed interval holding the value, at most `2^-bits` wide.
1427    pub fn enclosure(&self, bits: u32) -> Result<(Rational, Rational), RealError> {
1428        let iv = self.enclose_width(bits as i64, Self::cap_for(bits))?;
1429        Ok((iv.lo.to_rational(), iv.hi.to_rational()))
1430    }
1431
1432    fn cap_for(bits: u32) -> i64 {
1433        4 * bits as i64 + DEFAULT_BUDGET as i64
1434    }
1435
1436    /// An enclosure at most `2^-bits` wide, raising the working precision as needed up to `cap`.
1437    fn enclose_width(&self, bits: i64, cap: i64) -> Result<Iv, RealError> {
1438        if let Form::Alg(a) = &self.form {
1439            if let Some(r) = a.to_rational() {
1440                return Ok(Iv {
1441                    lo: Dyadic::from_rational_floor(&r, bits + 2),
1442                    hi: Dyadic::from_rational_ceil(&r, bits + 2),
1443                });
1444            }
1445        }
1446        let eps = Dyadic::new(one(), -bits);
1447        let mut p = bits + 8;
1448        loop {
1449            let iv = self.enclosed(p, cap)?;
1450            if iv.width().cmp(&eps) != Ordering::Greater {
1451                return Ok(iv);
1452            }
1453            if p >= cap {
1454                return Err(RealError::Inseparable {
1455                    what: format!("an enclosure of {} narrower than 2^-{bits}", self.describe()),
1456                    bits: cap as u32,
1457                });
1458            }
1459            p = (p + p / 2 + 16).min(cap);
1460        }
1461    }
1462
1463    /// The order of two values. When their difference has an exact form the answer is exact (a
1464    /// nonzero exact difference is refined until its sign shows, which always terminates);
1465    /// otherwise the difference is refined to `2^-budget` and the comparison fails loudly if the
1466    /// values have not separated.
1467    pub fn compare(&self, o: &ExactReal, budget: u32) -> Result<Ordering, RealError> {
1468        let d = self.sub(o);
1469        if d.is_exactly_zero() {
1470            return Ok(Ordering::Equal);
1471        }
1472        let cap = if d.is_exact_form() { i64::MAX / 4 } else { budget as i64 };
1473        let mut p = 8i64;
1474        loop {
1475            let iv = d.enclosed(p, cap.max(p))?;
1476            if iv.lo.is_positive() {
1477                return Ok(Ordering::Greater);
1478            }
1479            if iv.hi.is_negative() {
1480                return Ok(Ordering::Less);
1481            }
1482            if p >= cap {
1483                return Err(RealError::Inseparable {
1484                    what: format!("{} and {}", self.describe(), o.describe()),
1485                    bits: budget,
1486                });
1487            }
1488            p = (p * 2).min(cap);
1489        }
1490    }
1491
1492    /// Total comparison against a tolerance: `Close` when the difference is certainly within
1493    /// `tolerance`, otherwise the certain side — decided from one enclosure of the difference
1494    /// narrower than a quarter of the tolerance. Fails only when the difference itself cannot be
1495    /// evaluated (a division by a quantity inseparable from zero, a logarithm of a nonpositive
1496    /// number).
1497    pub fn compare_within(&self, o: &ExactReal, tolerance: &Rational) -> Result<Closeness, RealError> {
1498        let d = self.sub(o);
1499        if d.is_exactly_zero() {
1500            return Ok(Closeness::Close);
1501        }
1502        let quarter = tolerance.mul(&Rational::from_ratio_i64(1, 4).expect("4 ≠ 0"));
1503        let mut bits = 1i64;
1504        while Rational::new(one(), one().shl_bits(bits as u64)).expect("nonzero") > quarter {
1505            bits += 1;
1506        }
1507        let tol = Dyadic::from_rational_floor(tolerance, bits + 4);
1508        let iv = d.enclose_width(bits, Self::cap_for(bits as u32))?;
1509        Ok(
1510            if iv.lo.cmp(&tol.neg()) != Ordering::Less && iv.hi.cmp(&tol) != Ordering::Greater {
1511                Closeness::Close
1512            } else if iv.lo.is_positive() {
1513                Closeness::Greater
1514            } else {
1515                Closeness::Less
1516            },
1517        )
1518    }
1519
1520    /// `⌊self⌋`: exact on algebraic numbers; otherwise refined until the enclosure lies within one
1521    /// unit interval, failing at `2^-budget`.
1522    pub fn floor(&self, budget: u32) -> Result<BigInt, RealError> {
1523        if let Form::Alg(a) = &self.form {
1524            return Ok(a.floor());
1525        }
1526        let mut p = 8i64;
1527        loop {
1528            let iv = self.enclosed(p, (budget as i64).max(p))?;
1529            let (fl, fh) = (iv.lo.floor_scaled(0), iv.hi.floor_scaled(0));
1530            if fl == fh {
1531                return Ok(fl);
1532            }
1533            if p >= budget as i64 {
1534                return Err(RealError::Inseparable {
1535                    what: format!("{} from the integer {fh}", self.describe()),
1536                    bits: budget,
1537                });
1538            }
1539            p = (p * 2).min(budget as i64);
1540        }
1541    }
1542
1543    /// `round(|self|·10^k)`, half away from zero: exact on rationals, otherwise refined until the
1544    /// enclosure rounds to one integer.
1545    fn scaled_round(&self, k: i64) -> Result<BigInt, RealError> {
1546        let ten_k = Rational::from_bigint(BigInt::from_i64(10).pow(k.unsigned_abs() as u32));
1547        let scale = |r: Rational| -> Rational {
1548            if k >= 0 {
1549                r.mul(&ten_k)
1550            } else {
1551                r.div(&ten_k).expect("10^k ≠ 0")
1552            }
1553        };
1554        if let Some(r) = self.exact_algebraic().and_then(|a| a.to_rational()) {
1555            return Ok(scale(r.abs()).round());
1556        }
1557        let half = Rational::from_ratio_i64(1, 2).expect("2 ≠ 0");
1558        let digits_bits = (k.max(0) as f64 * std::f64::consts::LOG2_10).ceil() as i64;
1559        let cap = Self::cap_for(digits_bits as u32 + 64);
1560        let mut p = 16 + digits_bits;
1561        loop {
1562            let iv = self.enclosed(p, cap.max(p))?;
1563            let (lo, hi) = if !iv.hi.is_positive() {
1564                (iv.hi.neg(), iv.lo.neg())
1565            } else {
1566                (iv.lo.clone(), iv.hi.clone())
1567            };
1568            if !lo.is_negative() {
1569                let a = scale(lo.to_rational()).add(&half).floor();
1570                let b = scale(hi.to_rational()).add(&half).floor();
1571                if a == b {
1572                    return Ok(a);
1573                }
1574            }
1575            if p >= cap {
1576                return Err(RealError::Inseparable {
1577                    what: format!("{} from a rounding boundary", self.describe()),
1578                    bits: cap as u32,
1579                });
1580            }
1581            p = (p + p / 2 + 16).min(cap);
1582        }
1583    }
1584
1585    /// `sig` significant digits, correctly rounded (half away from zero on an exact tie), in plain
1586    /// positional notation; a value that cannot be printed shows its error in angle brackets.
1587    pub fn to_decimal(&self, sig: u32) -> String {
1588        self.try_to_decimal(sig).unwrap_or_else(|e| format!("⟨{e}⟩"))
1589    }
1590
1591    /// [`ExactReal::to_decimal`], with the failure (a value inseparable from zero or from a
1592    /// rounding boundary within the budget) returned rather than printed.
1593    pub fn try_to_decimal(&self, sig: u32) -> Result<String, RealError> {
1594        if self.is_exactly_zero() {
1595            return Ok("0".to_string());
1596        }
1597        let sig = sig.max(1) as i64;
1598        let zero = Self::from_i64(0);
1599        let negative = self.compare(&zero, DEFAULT_BUDGET)? == Ordering::Less;
1600        let abs = if negative { self.neg() } else { self.clone() };
1601        let mut exp10 = abs.decimal_exponent()?;
1602        let digits = loop {
1603            let text = abs.scaled_round(sig - 1 - exp10)?.to_string();
1604            if text.len() as i64 > sig {
1605                exp10 += 1;
1606                continue;
1607            }
1608            break text;
1609        };
1610        let body = if exp10 >= sig - 1 {
1611            format!("{digits}{}", "0".repeat((exp10 - sig + 1) as usize))
1612        } else if exp10 >= 0 {
1613            let (a, b) = digits.split_at((exp10 + 1) as usize);
1614            format!("{a}.{b}")
1615        } else {
1616            format!("0.{}{digits}", "0".repeat((-exp10 - 1) as usize))
1617        };
1618        Ok(if negative { format!("-{body}") } else { body })
1619    }
1620
1621    /// `⌊log₁₀ self⌋` for a positive value.
1622    fn decimal_exponent(&self) -> Result<i64, RealError> {
1623        let ten = Self::from_i64(10);
1624        let mut power = Self::from_i64(1);
1625        let mut e = 0i64;
1626        if self.compare(&power, DEFAULT_BUDGET)? != Ordering::Less {
1627            loop {
1628                let next = power.mul(&ten);
1629                if self.compare(&next, DEFAULT_BUDGET)? == Ordering::Less {
1630                    return Ok(e);
1631                }
1632                power = next;
1633                e += 1;
1634            }
1635        }
1636        let tenth = Self::from_rational(Rational::from_ratio_i64(1, 10).expect("10 ≠ 0"));
1637        loop {
1638            power = power.mul(&tenth);
1639            e -= 1;
1640            if self.compare(&power, DEFAULT_BUDGET)? != Ordering::Less {
1641                return Ok(e);
1642            }
1643        }
1644    }
1645
1646    /// The integer part and exactly `frac` digits after the point, truncated toward zero.
1647    pub fn truncated_decimal(&self, frac: u32) -> Result<String, RealError> {
1648        let negative = self.compare(&Self::from_i64(0), DEFAULT_BUDGET)? == Ordering::Less;
1649        let abs = if negative { self.neg() } else { self.clone() };
1650        let ten_k = Self::from_rational(Rational::from_bigint(BigInt::from_i64(10).pow(frac)));
1651        let digits_bits = (frac as f64 * std::f64::consts::LOG2_10).ceil() as u32;
1652        let n = abs.mul(&ten_k).floor(digits_bits + DEFAULT_BUDGET)?;
1653        let text = format!("{:0>width$}", n.to_string(), width = frac as usize + 1);
1654        let (int, fr) = text.split_at(text.len() - frac as usize);
1655        let sign = if negative { "-" } else { "" };
1656        Ok(if frac == 0 {
1657            format!("{sign}{int}")
1658        } else {
1659            format!("{sign}{int}.{fr}")
1660        })
1661    }
1662
1663    /// A structural description: exact forms symbolically, general reals as the formula that
1664    /// built them (`π + e`, `log(2)`, `1/(π - e)`), never by evaluating them.
1665    pub fn describe(&self) -> String {
1666        match &self.form {
1667            Form::General(n) => n.describe(),
1668            _ => self.to_string(),
1669        }
1670    }
1671}
1672
1673impl Node {
1674    fn is_transmissible(&self) -> bool {
1675        match self {
1676            Node::Alg(_) | Node::Pi => true,
1677            Node::Add(a, b) | Node::Mul(a, b) => a.is_transmissible() && b.is_transmissible(),
1678            Node::Neg(a)
1679            | Node::Recip(a)
1680            | Node::Exp(a)
1681            | Node::Log(a)
1682            | Node::Sin(a)
1683            | Node::Cos(a)
1684            | Node::Sqrt(a) => a.is_transmissible(),
1685            Node::Flow(solution, _) => solution.start.iter().all(ExactReal::is_transmissible),
1686            Node::Defined(_) => false,
1687        }
1688    }
1689
1690    fn describe(&self) -> String {
1691        let wrap = |x: &ExactReal| {
1692            let d = x.describe();
1693            if d.contains(' ') {
1694                format!("({d})")
1695            } else {
1696                d
1697            }
1698        };
1699        match self {
1700            Node::Alg(leaf) => leaf.value.to_string(),
1701            Node::Pi => "π".to_string(),
1702            Node::Add(a, b) => {
1703                if let Form::General(n) = &b.form {
1704                    if let Node::Neg(inner) = n.as_ref() {
1705                        return format!("{} - {}", a.describe(), wrap(inner));
1706                    }
1707                }
1708                let bd = b.describe();
1709                match bd.strip_prefix('-') {
1710                    Some(rest) if !rest.contains(' ') => format!("{} - {rest}", a.describe()),
1711                    _ if bd.contains(' ') => format!("{} + ({bd})", a.describe()),
1712                    _ => format!("{} + {bd}", a.describe()),
1713                }
1714            }
1715            Node::Mul(a, b) => format!("{}·{}", wrap(a), wrap(b)),
1716            Node::Neg(a) => format!("-{}", wrap(a)),
1717            Node::Recip(a) => format!("1/{}", wrap(a)),
1718            Node::Exp(a) => format!("exp({})", a.describe()),
1719            Node::Log(a) => format!("log({})", a.describe()),
1720            Node::Sin(a) => format!("sin({})", a.describe()),
1721            Node::Cos(a) => format!("cos({})", a.describe()),
1722            Node::Sqrt(a) => format!("√({})", a.describe()),
1723            Node::Flow(solution, i) => format!("x{} of a flow at time {}", i + 1, solution.t),
1724            Node::Defined(d) => d.describe(),
1725        }
1726    }
1727}
1728
1729// ── display ───────────────────────────────────────────────────────────────────────────────────
1730
1731/// A coefficient in front of a product: bare when it is a plain integer or radical, in
1732/// parentheses when it is a sum or a fraction.
1733fn coefficient_text(a: &Algebraic) -> String {
1734    let s = a.to_string();
1735    if s.contains(' ') || s.contains('/') {
1736        format!("({s})")
1737    } else {
1738        s
1739    }
1740}
1741
1742fn exponent_text(a: &Algebraic, base: &str) -> String {
1743    if *a == Algebraic::from_i64(1) {
1744        return base.to_string();
1745    }
1746    match a.to_rational() {
1747        Some(r) if r.is_integer() && !r.is_negative() => format!("{base}^{r}"),
1748        _ => format!("{base}^({a})"),
1749    }
1750}
1751
1752/// Write `Σ sign·factors` with the first sign attached and the rest spaced.
1753fn write_sum(f: &mut fmt::Formatter<'_>, terms: Vec<(bool, String)>) -> fmt::Result {
1754    for (i, (negative, body)) in terms.iter().enumerate() {
1755        match (i, negative) {
1756            (0, true) => write!(f, "-{body}")?,
1757            (0, false) => write!(f, "{body}")?,
1758            (_, true) => write!(f, " - {body}")?,
1759            (_, false) => write!(f, " + {body}")?,
1760        }
1761    }
1762    Ok(())
1763}
1764
1765/// `coefficient·factor·…`, with a unit coefficient dropped (the sign is carried separately).
1766fn product_text(coefficient: &Algebraic, factors: &[String]) -> (bool, String) {
1767    let negative = coefficient.signum() < 0;
1768    let magnitude = if negative { coefficient.neg() } else { coefficient.clone() };
1769    let mut parts: Vec<String> = Vec::new();
1770    if factors.is_empty() {
1771        return (negative, magnitude.to_string());
1772    }
1773    if magnitude != Algebraic::from_i64(1) {
1774        parts.push(coefficient_text(&magnitude));
1775    }
1776    parts.extend(factors.iter().cloned());
1777    (negative, parts.join("·"))
1778}
1779
1780impl fmt::Display for ExpSum {
1781    /// Exponential terms by decreasing growth: a real exponent prints as `c·e^u`; a conjugate pair
1782    /// `a·e^{u+iv} + ā·e^{u−iv}` prints as its real form `2Re(a)·e^u·cos(v) − 2Im(a)·e^u·sin(v)`.
1783    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1784        let two = Algebraic::from_i64(2);
1785        let mut terms = Vec::new();
1786        for (e, c) in &self.terms {
1787            if e.im.signum() < 0 {
1788                continue;
1789            }
1790            let mut factors = Vec::new();
1791            if !e.re.is_zero() {
1792                factors.push(exponent_text(&e.re, "e"));
1793            }
1794            if e.im.is_zero() {
1795                terms.push(product_text(&c.re, &factors));
1796                continue;
1797            }
1798            let cos_coef = c.re.mul(&two);
1799            let sin_coef = c.im.mul(&two).neg();
1800            if !cos_coef.is_zero() {
1801                let mut fs = factors.clone();
1802                fs.push(format!("cos({})", e.im));
1803                terms.push(product_text(&cos_coef, &fs));
1804            }
1805            if !sin_coef.is_zero() {
1806                let mut fs = factors;
1807                fs.push(format!("sin({})", e.im));
1808                terms.push(product_text(&sin_coef, &fs));
1809            }
1810        }
1811        write_sum(f, terms)
1812    }
1813}
1814
1815impl fmt::Display for PiPoly {
1816    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1817        let terms = self
1818            .terms
1819            .iter()
1820            .map(|(k, c)| {
1821                let factors = match k {
1822                    0 => Vec::new(),
1823                    1 => vec!["π".to_string()],
1824                    k if *k > 1 => vec![format!("π^{k}")],
1825                    k => vec![format!("π^({k})")],
1826                };
1827                product_text(c, &factors)
1828            })
1829            .collect();
1830        write_sum(f, terms)
1831    }
1832}
1833
1834impl fmt::Display for ExactReal {
1835    /// An exact form symbolically (`√2`, `e^2 + 1`, `cos(1)`, `π^2 + 1`); a general real as
1836    /// [`DISPLAY_DIGITS`] correctly rounded significant digits.
1837    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1838        match &self.form {
1839            Form::Alg(a) => write!(f, "{a}"),
1840            Form::Exp(s) => write!(f, "{s}"),
1841            Form::Pi(p) => write!(f, "{p}"),
1842            Form::General(_) => write!(f, "{}", self.to_decimal(DISPLAY_DIGITS)),
1843        }
1844    }
1845}
1846
1847impl fmt::Debug for ExactReal {
1848    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1849        write!(f, "ExactReal({self})")
1850    }
1851}
1852
1853// ── the canonical encoding ────────────────────────────────────────────────────────────────────
1854
1855impl crate::exact_codec::ExactCodec for ExactReal {
1856    /// A form tag, then the form: `0` an algebraic number; `1` an exponential sum (each term's
1857    /// exponent and coefficient as real and imaginary algebraic parts); `2` a polynomial in π
1858    /// (each power and coefficient); `3` a general real as the operation that built it (`0` add,
1859    /// `1` multiply, `2` negate, `3` reciprocal, `4` exp, `5` log, `6` sin, `7` cos, `8` square
1860    /// root) and its operands.
1861    fn write(&self, out: &mut Vec<u8>) {
1862        use crate::exact_codec::write_len;
1863        match &self.form {
1864            Form::Alg(a) => {
1865                out.push(0);
1866                a.write(out);
1867            }
1868            Form::Exp(sum) => {
1869                out.push(1);
1870                write_len(sum.terms.len(), out);
1871                for (e, c) in &sum.terms {
1872                    e.re.write(out);
1873                    e.im.write(out);
1874                    c.re.write(out);
1875                    c.im.write(out);
1876                }
1877            }
1878            Form::Pi(poly) => {
1879                out.push(2);
1880                write_len(poly.terms.len(), out);
1881                for (k, c) in &poly.terms {
1882                    (*k as i64).write(out);
1883                    c.write(out);
1884                }
1885            }
1886            Form::General(node) => {
1887                out.push(3);
1888                let (tag, operands): (u8, Vec<&ExactReal>) = match node.as_ref() {
1889                    Node::Add(a, b) => (0, vec![a, b]),
1890                    Node::Mul(a, b) => (1, vec![a, b]),
1891                    Node::Neg(a) => (2, vec![a]),
1892                    Node::Recip(a) => (3, vec![a]),
1893                    Node::Exp(a) => (4, vec![a]),
1894                    Node::Log(a) => (5, vec![a]),
1895                    Node::Sin(a) => (6, vec![a]),
1896                    Node::Cos(a) => (7, vec![a]),
1897                    Node::Sqrt(a) => (8, vec![a]),
1898                    Node::Flow(solution, i) => {
1899                        out.push(9);
1900                        write_len(solution.field.components.len(), out);
1901                        for monomials in &solution.field.components {
1902                            write_len(monomials.len(), out);
1903                            for m in monomials {
1904                                m.coefficient.write(out);
1905                                for e in &m.powers {
1906                                    u64::from(*e).write(out);
1907                                }
1908                            }
1909                        }
1910                        for x in &solution.start {
1911                            x.write(out);
1912                        }
1913                        solution.t.write(out);
1914                        write_len(*i, out);
1915                        return;
1916                    }
1917                    Node::Defined(d) => {
1918                        out.push(10);
1919                        let description = d.describe();
1920                        write_len(description.len(), out);
1921                        out.extend_from_slice(description.as_bytes());
1922                        return;
1923                    }
1924                    Node::Alg(_) | Node::Pi => {
1925                        unreachable!("an algebraic or π leaf is an exact form, never a general real")
1926                    }
1927                };
1928                out.push(tag);
1929                for x in operands {
1930                    x.write(out);
1931                }
1932            }
1933        }
1934    }
1935
1936    fn read(buf: &[u8], pos: &mut usize) -> Option<Self> {
1937        read_real(buf, pos, 0)
1938    }
1939}
1940
1941/// A real at nesting `depth`, rebuilt through the operations that make it so that only canonical
1942/// encodings survive the round trip; an exponential sum must be conjugate-symmetric (real).
1943fn read_real(buf: &[u8], pos: &mut usize, depth: usize) -> Option<ExactReal> {
1944    use crate::exact_codec::{read_byte, read_len, ExactCodec, MAX_WIRE_DEPTH, MAX_WIRE_LEN};
1945    if depth > MAX_WIRE_DEPTH {
1946        return None;
1947    }
1948    match read_byte(buf, pos)? {
1949        0 => Some(ExactReal::from_algebraic(Algebraic::read(buf, pos)?)),
1950        1 => {
1951            let n = read_len(buf, pos, MAX_WIRE_LEN)?;
1952            let mut raw = Vec::with_capacity(n.min(64));
1953            for _ in 0..n {
1954                let e = Calg {
1955                    re: Algebraic::read(buf, pos)?,
1956                    im: Algebraic::read(buf, pos)?,
1957                };
1958                let c = Calg {
1959                    re: Algebraic::read(buf, pos)?,
1960                    im: Algebraic::read(buf, pos)?,
1961                };
1962                raw.push((e, c));
1963            }
1964            let sum = ExpSum::from_terms(raw);
1965            let conjugate = |z: &Calg| Calg {
1966                re: z.re.clone(),
1967                im: z.im.neg(),
1968            };
1969            let real = sum
1970                .terms
1971                .iter()
1972                .all(|(e, c)| sum.terms.iter().any(|(f, d)| *f == conjugate(e) && *d == conjugate(c)));
1973            real.then(|| ExactReal::of(Form::Exp(sum)))
1974        }
1975        2 => {
1976            let n = read_len(buf, pos, MAX_WIRE_LEN)?;
1977            let mut raw = Vec::with_capacity(n.min(64));
1978            for _ in 0..n {
1979                let k = i32::try_from(i64::read(buf, pos)?).ok()?;
1980                raw.push((k, Algebraic::read(buf, pos)?));
1981            }
1982            Some(ExactReal::of(Form::Pi(PiPoly::from_terms(raw))))
1983        }
1984        3 => {
1985            let tag = read_byte(buf, pos)?;
1986            if tag == 9 {
1987                return read_flow(buf, pos, depth);
1988            }
1989            let a = read_real(buf, pos, depth + 1)?;
1990            match tag {
1991                0 => Some(a.add(&read_real(buf, pos, depth + 1)?)),
1992                1 => Some(a.mul(&read_real(buf, pos, depth + 1)?)),
1993                2 => Some(a.neg()),
1994                3 => a.recip().ok(),
1995                4 => Some(a.exp()),
1996                5 => a.log().ok(),
1997                6 => Some(a.sin()),
1998                7 => Some(a.cos()),
1999                8 => a.sqrt().ok(),
2000                _ => None,
2001            }
2002        }
2003        _ => None,
2004    }
2005}
2006
2007/// A flow's component: its field, its start and its time, rebuilt through [`ExactReal::flow`].
2008fn read_flow(buf: &[u8], pos: &mut usize, depth: usize) -> Option<ExactReal> {
2009    use crate::exact_codec::{read_len, ExactCodec, MAX_WIRE_LEN};
2010    use crate::ode::Monomial;
2011    let n = read_len(buf, pos, MAX_WIRE_LEN)?;
2012    let mut components = Vec::with_capacity(n.min(64));
2013    for _ in 0..n {
2014        let count = read_len(buf, pos, MAX_WIRE_LEN)?;
2015        let mut monomials = Vec::with_capacity(count.min(64));
2016        for _ in 0..count {
2017            let coefficient = Rational::read(buf, pos)?;
2018            let mut powers = Vec::with_capacity(n);
2019            for _ in 0..n {
2020                powers.push(u32::try_from(u64::read(buf, pos)?).ok()?);
2021            }
2022            monomials.push(Monomial { coefficient, powers });
2023        }
2024        components.push(monomials);
2025    }
2026    let mut start = Vec::with_capacity(n);
2027    for _ in 0..n {
2028        start.push(read_real(buf, pos, depth + 1)?);
2029    }
2030    let t = Rational::read(buf, pos)?;
2031    let i = read_len(buf, pos, n.saturating_sub(1))?;
2032    ExactReal::flow(&Field { components }, &start, &t)?.into_iter().nth(i)
2033}
2034
2035#[cfg(test)]
2036#[path = "../../logicaffeine_tests/unit/logicaffeine_base/exact_real/tests.rs"]
2037mod tests;