Skip to main content

logicaffeine_base/
germ.rs

1//! Germs at `dt = 0`: the hyperreals a loop of infinitely many infinitesimal steps leaves, known by
2//! the series of their expansion in `dt` with exact computable-real coefficients.
3//!
4//! A [`Hyperreal`] is a rational function of `dt`; `(1 + dt)^(1/dt)`, the state after `⌈1/dt⌉`
5//! Euler steps of `x′ = x`, is not. It is the germ of an analytic function at `dt = 0`, whose series
6//! `e − (e/2)·dt + (11e/24)·dt² − …` has computable coefficients, and its standard part is the
7//! first of them. A [`Germ`] holds such a value as the operations that built it — the series of a
8//! rational function, a flow, sums, products, reciprocals — and computes coefficients on demand,
9//! each once.
10//!
11//! **Flows.** `N` steps of an affine update `x ↦ A(dt)·x` with `A(0) = I`, `A(dt) = I + Σ_{k≥1}
12//! dt^k·A_k`, and `N = T/dt + δ`, leave `exp(N·log A(dt))·x₀`. Writing `L(dt) = log A(dt) = Σ_{k≥1}
13//! dt^k·L_k`, the exponent is `C_0 + Σ_{j≥1} dt^j·C_j` with `C_j = T·L_{j+1} + δ·L_j`, and `C_0 =
14//! T·A_1`. The standard part is `e^{T·A_1}·x₀` always. When the `A_k` commute, so do the `C_j`,
15//! and `exp(Σ_{j≥1} dt^j·C_j) = Σ_m dt^m·E_m` with `E_0 = I` and `E_m = (1/m)·Σ_{k=1}^m k·C_k·E_{m−k}`;
16//! every coefficient is then exact. When they do not, the coefficients past the standard part are
17//! refused by name rather than approximated.
18//!
19//! **Nonlinear flows.** A flow of a polynomial differential equation is known to its standard part,
20//! the equation's solution at `T` enclosed by validated integration; its higher orders are refused
21//! by name ([`Germ::standard`]). A solution that leaves every bound before `T` leaves a germ that is
22//! not limited and has only a sign ([`Germ::unlimited`]); the sum of it and a limited germ, and its
23//! product with one whose standard part is nonzero, are unlimited too.
24//!
25//! **Fractional orders.** A germ's orders are the multiples of `1/q` for its ramification `q`: order
26//! `k` is the coefficient of `dt^(k/q)`. A value of the real closure ([`Puiseux`]) is read from its
27//! branch on that branch's own grid; two germs meet on the least grid both lie on, each refined to it.
28//!
29//! **Functions.** `exp`, `ln` and the real `n`-th roots are germs of their arguments, each coefficient
30//! from the ones before it: with `g = Σ g_k·t^k`, `t = dt^(1/q)`,
31//!
32//! - `e = exp(g)` for a limited `g`: `e_0 = e^{g_0}` and `k·e_k = Σ_{j=1..k} j·g_j·e_{k−j}`;
33//! - `l = ln(g)` for an appreciable positive `g`: `l_0 = ln(g_0)` and
34//!   `l_k = (g_k − (1/k)·Σ_{j=1..k−1} j·l_j·g_{k−j}) / g_0`;
35//! - `w = ⁿ√g` for `g = c·t^v·(1 + …)`, refined until `n` divides `v`, `c > 0` for an even `n`: `w`
36//!   starts at `ⁿ√c·t^(v/n)` and `w_{v/n+j} = (1/(j·c))·Σ_{i=1..j} ((1/n + 1)·i − j)·g_{v+i}·w_{v/n+j−i}`
37//!   (Miller's recurrence for a power series raised to `1/n`).
38//!
39//! The exponential of an infinite value lies beyond every power of `dt`, and the logarithm of an
40//! infinitesimal or an infinite one carries `ln(dt)`; neither is a germ, and both are refused by name.
41//!
42//! **Signs.** A germ's sign is the sign of its first nonzero coefficient. Coefficients compare
43//! exactly on the exact forms; a general one that cannot be told from zero within the budget, or a
44//! germ whose coefficients through `dt^`[`SIGN_ORDERS`] all vanish, is a loud error, never a guess.
45
46use std::fmt;
47use std::sync::{Arc, Mutex};
48
49use crate::algebraic::negative_root;
50use crate::exact_real::{ExactReal, RealError, DEFAULT_BUDGET};
51use crate::hyperreal::Hyperreal;
52use crate::matrix_exp::exp_matrix;
53use crate::numeric::Rational;
54use crate::puiseux::{expansion_term, remainder, Puiseux};
55
56/// How many orders of `dt` a sign or a standard part looks through before it is refused.
57pub const SIGN_ORDERS: i64 = 64;
58
59/// Why a coefficient or a sign is not given.
60#[derive(Clone, Debug, PartialEq, Eq)]
61pub enum GermError {
62    /// The flow's update matrices do not commute, so its coefficients past the standard part are
63    /// not computed exactly. The order is the exponent of `dt`.
64    NotComputed { order: Rational },
65    /// The value is known by its standard part alone, as a nonlinear flow is. The order is the
66    /// exponent of `dt`.
67    StandardOnly { order: Rational },
68    /// A coefficient could not be told from zero.
69    Inseparable(String),
70    /// The coefficients through `dt^`[`SIGN_ORDERS`] vanish.
71    Flat,
72    /// The reciprocal of a germ that may be zero.
73    DivisionByZero,
74    /// An even root of a negative value.
75    NegativeRoot(u32),
76    /// The logarithm of a negative value.
77    Nonpositive,
78    /// The exponential of an infinite value, beyond every power of `dt`.
79    BeyondEveryPower,
80    /// The logarithm of a positive infinitesimal, which carries `ln(dt)`, or of an infinite value,
81    /// which carries `ln(1/dt)`.
82    CarriesLog { infinite: bool },
83}
84
85impl fmt::Display for GermError {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        match self {
88            GermError::NotComputed { order } => write!(
89                f,
90                "the {} term of this flow is not computed exactly: its update matrices do not commute",
91                order_text(order)
92            ),
93            GermError::StandardOnly { order } => write!(
94                f,
95                "the {} term of this value is not known: a nonlinear flow is known by its standard part",
96                order_text(order)
97            ),
98            GermError::Inseparable(why) => f.write_str(why),
99            GermError::Flat => write!(
100                f,
101                "cannot tell this value from zero within its first {SIGN_ORDERS} orders in the infinitesimal"
102            ),
103            GermError::DivisionByZero => f.write_str("Division by zero"),
104            GermError::NegativeRoot(n) => f.write_str(&negative_root(*n)),
105            GermError::Nonpositive => f.write_str("logarithm of a nonpositive number"),
106            GermError::BeyondEveryPower => {
107                f.write_str("the exponential of this value is beyond every power of dt")
108            }
109            GermError::CarriesLog { infinite: false } => f.write_str("the logarithm of this value carries ln(dt)"),
110            GermError::CarriesLog { infinite: true } => f.write_str("the logarithm of this value carries ln(1/dt)"),
111        }
112    }
113}
114
115impl From<RealError> for GermError {
116    fn from(e: RealError) -> GermError {
117        GermError::Inseparable(e.to_string())
118    }
119}
120
121/// The term `dt^order` is named by: `dt^1`, `dt^2`, `dt^(3/2)`.
122fn order_text(order: &Rational) -> String {
123    if order.is_integer() {
124        format!("dt^{order}")
125    } else {
126        format!("dt^({order})")
127    }
128}
129
130/// `k/q` as an exponent of `dt`.
131fn exponent(k: i64, q: u32) -> Rational {
132    Rational::from_ratio_i64(k, i64::from(q)).expect("q is positive")
133}
134
135fn gcd(a: u64, b: u64) -> u64 {
136    if b == 0 {
137        a
138    } else {
139        gcd(b, a % b)
140    }
141}
142
143fn lcm(a: u32, b: u32) -> u32 {
144    let g = gcd(u64::from(a), u64::from(b));
145    u32::try_from(u64::from(a) / g * u64::from(b)).expect("a germ's ramification fits")
146}
147
148/// `N` steps of an affine update, the shared part of each component's germ.
149#[derive(Debug)]
150pub struct Flow {
151    /// The root of `dt` the update is a polynomial in: `A = I + Σ_{k≥1} u^k·A_k`, `u = dt^(1/grid)`.
152    grid: u32,
153    /// `A_k` for `k ≥ 1`, zero below `k = grid`, the term of `dt` itself.
154    terms: Vec<Vec<Vec<Rational>>>,
155    t: Rational,
156    delta: Rational,
157    start: Vec<Germ>,
158    commuting: bool,
159    exp_t: Vec<Vec<ExactReal>>,
160    /// `E_m`, the coefficient of `u^m`, computed in order.
161    expansion: Mutex<Vec<Vec<Vec<Rational>>>>,
162    /// `C_j` for `j ≥ 1`, the coefficient of `u^j`, computed in order.
163    exponent: Mutex<Vec<Vec<Vec<Rational>>>>,
164    /// The least grid the update and the start lie on, every start refined to it: the state is a
165    /// series in `dt^(1/q)`.
166    q: u32,
167    /// The lowest order of the start, and so of the state, in units of `dt^(1/q)`.
168    floor: i64,
169    /// The state's coefficients `s_m` computed so far.
170    state: Mutex<std::collections::BTreeMap<i64, Vec<ExactReal>>>,
171}
172
173type Matrix = Vec<Vec<Rational>>;
174
175fn zeros(n: usize) -> Matrix {
176    vec![vec![Rational::zero(); n]; n]
177}
178
179fn identity(n: usize) -> Matrix {
180    (0..n)
181        .map(|i| (0..n).map(|j| if i == j { Rational::one() } else { Rational::zero() }).collect())
182        .collect()
183}
184
185fn mat_mul(a: &Matrix, b: &Matrix) -> Matrix {
186    let n = a.len();
187    (0..n)
188        .map(|i| {
189            (0..n)
190                .map(|j| (0..n).fold(Rational::zero(), |acc, k| acc.add(&a[i][k].mul(&b[k][j]))))
191                .collect()
192        })
193        .collect()
194}
195
196fn mat_add(a: &Matrix, b: &Matrix) -> Matrix {
197    a.iter()
198        .zip(b)
199        .map(|(x, y)| x.iter().zip(y).map(|(p, q)| p.add(q)).collect())
200        .collect()
201}
202
203fn mat_scale(a: &Matrix, s: &Rational) -> Matrix {
204    a.iter().map(|row| row.iter().map(|v| v.mul(s)).collect()).collect()
205}
206
207fn is_zero_matrix(a: &Matrix) -> bool {
208    a.iter().all(|row| row.iter().all(Rational::is_zero))
209}
210
211impl Flow {
212    /// The flow of `N = T/dt + δ` steps of `A(dt) = I + Σ_{k≥1} dt^k·terms[k − 1]` from `start`;
213    /// `None` when the shapes disagree or there is no first-order term.
214    pub fn new(terms: Vec<Matrix>, t: Rational, delta: Rational, start: Vec<Germ>) -> Option<Arc<Flow>> {
215        Flow::on_grid(1, terms, t, delta, start)
216    }
217
218    /// The flow of `N = T/dt + δ` steps of `A = I + Σ_{k≥1} dt^(k/grid)·terms[k − 1]` from `start`;
219    /// `None` when the shapes disagree, a term below `dt` does not vanish, or there is no term of
220    /// `dt`.
221    pub fn on_grid(grid: u32, terms: Vec<Matrix>, t: Rational, delta: Rational, start: Vec<Germ>) -> Option<Arc<Flow>> {
222        let n = start.len();
223        let g = grid as usize;
224        if n == 0
225            || grid == 0
226            || terms.len() < g
227            || terms.iter().any(|m| m.len() != n || m.iter().any(|r| r.len() != n))
228            || !terms[..g - 1].iter().all(is_zero_matrix)
229        {
230            return None;
231        }
232        let commuting = terms.iter().enumerate().all(|(i, a)| {
233            terms[i + 1..].iter().all(|b| mat_mul(a, b) == mat_mul(b, a))
234        });
235        let exp_t = exp_matrix(&terms[g - 1], &t)?;
236        let q = start.iter().map(Germ::ramification).fold(grid, lcm);
237        let start: Vec<Germ> = start.iter().map(|g| g.refined(q / g.q)).collect();
238        let floor = start.iter().map(Germ::floor).min().unwrap_or(0);
239        Some(Arc::new(Flow {
240            grid,
241            terms,
242            t,
243            delta,
244            start,
245            commuting,
246            exp_t,
247            expansion: Mutex::new(Vec::new()),
248            exponent: Mutex::new(Vec::new()),
249            q,
250            floor,
251            state: Mutex::new(std::collections::BTreeMap::new()),
252        }))
253    }
254
255    fn width(&self) -> usize {
256        self.start.len()
257    }
258
259    fn term(&self, k: usize) -> Matrix {
260        self.terms.get(k - 1).cloned().unwrap_or_else(|| zeros(self.width()))
261    }
262
263    /// `L_k`, the coefficient of `u^k` in `log A = Σ_{n≥1} (−1)^{n+1}·X^n/n`, `X = Σ u^k·A_k`.
264    fn log_term(&self, k: usize) -> Matrix {
265        let n = self.width();
266        let mut total = zeros(n);
267        let mut power: Vec<Matrix> = (0..=k).map(|j| if j == 0 { identity(n) } else { zeros(n) }).collect();
268        for count in 1..=k {
269            let mut next: Vec<Matrix> = vec![zeros(n); k + 1];
270            for (have, p) in power.iter().enumerate() {
271                if p.iter().all(|r| r.iter().all(Rational::is_zero)) {
272                    continue;
273                }
274                for step in 1..=(k - have) {
275                    next[have + step] = mat_add(&next[have + step], &mat_mul(p, &self.term(step)));
276                }
277            }
278            power = next;
279            let sign = if count % 2 == 1 { 1 } else { -1 };
280            let c = Rational::from_ratio_i64(sign, count as i64).expect("count ≥ 1");
281            total = mat_add(&total, &mat_scale(&power[k], &c));
282        }
283        total
284    }
285
286    /// `C_j = T·L_{grid+j} + δ·L_j`: `N·log A = (T·u^(−grid) + δ)·Σ u^k·L_k` past its constant `T·A_grid`.
287    fn exponent(&self, j: usize) -> Matrix {
288        let mut cache = self.exponent.lock().expect("unpoisoned");
289        while cache.len() < j {
290            let i = cache.len() + 1;
291            let c = mat_add(
292                &mat_scale(&self.log_term(i + self.grid as usize), &self.t),
293                &mat_scale(&self.log_term(i), &self.delta),
294            );
295            cache.push(c);
296        }
297        cache[j - 1].clone()
298    }
299
300    fn expansion(&self, m: usize) -> Matrix {
301        let mut cache = self.expansion.lock().expect("unpoisoned");
302        if cache.is_empty() {
303            cache.push(identity(self.width()));
304        }
305        while cache.len() <= m {
306            let mm = cache.len();
307            let mut total = zeros(self.width());
308            for k in 1..=mm {
309                let c = self.exponent(k);
310                if is_zero_matrix(&c) {
311                    continue;
312                }
313                let term = mat_mul(&c, &cache[mm - k]);
314                total = mat_add(&total, &mat_scale(&term, &Rational::from_i64(k as i64)));
315            }
316            cache.push(mat_scale(&total, &Rational::from_ratio_i64(1, mm as i64).expect("mm ≥ 1")));
317        }
318        cache[m].clone()
319    }
320
321    /// `s_m = e^{T·A_grid}·Σ_{a·r+b=m} E_a·x₀_b`, in units of `dt^(1/q)`, `u = dt^(r/q)`. Without
322    /// commuting terms `e^{C_0 + Σ u^j·C_j}` is no product of exponentials, and only the orders below
323    /// the first nonzero `C_j` are known, which need `E_0 = I` alone.
324    fn state(&self, m: i64) -> Result<Vec<ExactReal>, GermError> {
325        let r = i64::from(self.q / self.grid);
326        let reach = (m - self.floor).max(0) / r;
327        if !self.commuting && (1..=reach as usize).any(|j| !is_zero_matrix(&self.exponent(j))) {
328            return Err(GermError::NotComputed { order: exponent(m, self.q) });
329        }
330        if let Some(s) = self.state.lock().expect("unpoisoned").get(&m) {
331            return Ok(s.clone());
332        }
333        let n = self.width();
334        let mut inner = vec![ExactReal::from_i64(0); n];
335        for a in 0..=reach as usize {
336            let e = self.expansion(a);
337            if is_zero_matrix(&e) {
338                continue;
339            }
340            let start: Vec<ExactReal> = self
341                .start
342                .iter()
343                .map(|g| g.coefficient(m - a as i64 * r))
344                .collect::<Result<_, _>>()?;
345            for (i, slot) in inner.iter_mut().enumerate() {
346                for (j, x) in start.iter().enumerate() {
347                    if !e[i][j].is_zero() {
348                        *slot = slot.add(&ExactReal::from_rational(e[i][j].clone()).mul(x));
349                    }
350                }
351            }
352        }
353        let s: Vec<ExactReal> = (0..n)
354            .map(|i| (0..n).fold(ExactReal::from_i64(0), |acc, j| acc.add(&self.exp_t[i][j].mul(&inner[j]))))
355            .collect();
356        self.state.lock().expect("unpoisoned").insert(m, s.clone());
357        Ok(s)
358    }
359}
360
361#[derive(Debug)]
362enum Node {
363    /// The series of a rational function of `dt`.
364    Series(Hyperreal),
365    /// A computable real, constant in `dt`.
366    Constant(ExactReal),
367    Add(Germ, Germ),
368    Mul(Germ, Germ),
369    Neg(Germ),
370    /// `1/g` for a germ whose order and leading coefficient are known.
371    Recip(Germ, i64, ExactReal),
372    /// One component of a flow.
373    Flow(Arc<Flow>, usize),
374    /// A limited value whose standard part alone is known.
375    Standard(ExactReal),
376    /// A value beyond every bound, positive or negative, of which only the sign is known.
377    Unlimited(i32),
378    /// `g` on a grid `m` times finer: order `k` is `g`'s order `k/m`, and zero off `g`'s grid. Made
379    /// only where two grids meet, and written as `g`, its reader meeting them again.
380    Refine(Germ, u32),
381    /// A value of the real closure, read from its branch.
382    Puiseux(Puiseux),
383    /// `exp(g)` for a limited `g`.
384    Exp(Germ),
385    /// `ln(g)` for `g` with a positive standard part, here `g_0`.
386    Ln(Germ, ExactReal),
387    /// The real `n`-th root of `g = c·t^v·(1 + …)`, `n` dividing `v`, with `ⁿ√c`.
388    Root { g: Germ, n: u32, v: i64, c: ExactReal, lead: ExactReal },
389}
390
391/// A germ at `dt = 0`. See the module documentation.
392#[derive(Clone, Debug)]
393pub struct Germ {
394    node: Arc<Node>,
395    /// The lowest order that may be nonzero.
396    floor: i64,
397    /// The ramification: order `k` is the coefficient of `dt^(k/q)`.
398    q: u32,
399    cache: Arc<Mutex<Vec<ExactReal>>>,
400}
401
402impl Germ {
403    /// Whether the germ can travel as data: one holding a real built from a function a program
404    /// defines cannot, that function staying with the program.
405    pub fn is_transmissible(&self) -> bool {
406        match self.node.as_ref() {
407            Node::Series(_) | Node::Unlimited(_) => true,
408            Node::Constant(x) | Node::Standard(x) => x.is_transmissible(),
409            Node::Add(a, b) | Node::Mul(a, b) => a.is_transmissible() && b.is_transmissible(),
410            Node::Neg(a) | Node::Recip(a, _, _) | Node::Refine(a, _) | Node::Exp(a) | Node::Ln(a, _) => {
411                a.is_transmissible()
412            }
413            Node::Root { g, .. } => g.is_transmissible(),
414            Node::Puiseux(_) => true,
415            Node::Flow(flow, _) => flow.start.iter().all(Germ::is_transmissible),
416        }
417    }
418
419    fn of(node: Node, floor: i64, q: u32) -> Germ {
420        Germ {
421            node: Arc::new(node),
422            floor,
423            q,
424            cache: Arc::new(Mutex::new(Vec::new())),
425        }
426    }
427
428    /// The series of a hyperreal.
429    pub fn from_hyperreal(h: &Hyperreal) -> Germ {
430        let floor = h.valuation().unwrap_or(0).min(0);
431        Germ::of(Node::Series(h.clone()), floor, 1)
432    }
433
434    /// The expansion of a value of the real closure, on its branch's grid.
435    pub fn from_puiseux(p: &Puiseux) -> Germ {
436        Germ::of(Node::Puiseux(p.clone()), p.order(), p.ramification())
437    }
438
439    pub fn from_i64(n: i64) -> Germ {
440        Germ::from_hyperreal(&Hyperreal::from_i64(n))
441    }
442
443    /// A computable real, constant in `dt`.
444    pub fn constant(x: &ExactReal) -> Germ {
445        Germ::of(Node::Constant(x.clone()), 0, 1)
446    }
447
448    /// The rational function this germ is the series of, when it was built as one.
449    pub fn as_hyperreal(&self) -> Option<&Hyperreal> {
450        match self.node.as_ref() {
451            Node::Series(h) => Some(h),
452            _ => None,
453        }
454    }
455
456    /// Component `i` of a flow's state.
457    pub fn flow(flow: &Arc<Flow>, i: usize) -> Germ {
458        Germ::of(Node::Flow(flow.clone(), i), flow.floor, flow.q)
459    }
460
461    /// A limited value known by its standard part alone; every higher order is
462    /// [`GermError::StandardOnly`].
463    pub fn standard(x: &ExactReal) -> Germ {
464        Germ::of(Node::Standard(x.clone()), 0, 1)
465    }
466
467    /// A value beyond every bound with the sign of `sign`.
468    pub fn unlimited(sign: i32) -> Germ {
469        Germ::of(Node::Unlimited(sign.signum()), 0, 1)
470    }
471
472    /// The ramification `q`: order `k` is the coefficient of `dt^(k/q)`.
473    pub fn ramification(&self) -> u32 {
474        self.q
475    }
476
477    /// The same germ on a grid `m` times finer.
478    fn refined(&self, m: u32) -> Germ {
479        if m == 1 {
480            return self.clone();
481        }
482        Germ::of(Node::Refine(self.clone(), m), self.floor * i64::from(m), self.q * m)
483    }
484
485    /// Both germs on the least grid both lie on.
486    fn met(&self, o: &Germ) -> (Germ, Germ, u32) {
487        let q = lcm(self.q, o.q);
488        (self.refined(q / self.q), o.refined(q / o.q), q)
489    }
490
491    /// The orders a sign looks through: those of `dt` through [`SIGN_ORDERS`].
492    fn sign_orders(&self) -> i64 {
493        SIGN_ORDERS * i64::from(self.q)
494    }
495
496    /// The sign of an unlimited germ; `None` for a limited one and for one whose order is not known.
497    pub fn unlimited_sign(&self) -> Option<i32> {
498        match self.node.as_ref() {
499            Node::Unlimited(sign) => Some(*sign),
500            _ => None,
501        }
502    }
503
504    /// Whether every coefficient below order zero is known to vanish: a limited value.
505    fn limited_by_construction(&self) -> bool {
506        self.floor >= 0 && self.unlimited_sign().is_none()
507    }
508
509    /// The lowest order whose coefficient may be nonzero, in units of `dt^(1/q)`.
510    pub fn floor(&self) -> i64 {
511        self.floor
512    }
513
514    /// The coefficient of `dt^(k/q)`, `q` the [`Germ::ramification`].
515    pub fn coefficient(&self, k: i64) -> Result<ExactReal, GermError> {
516        if k < self.floor {
517            return Ok(ExactReal::from_i64(0));
518        }
519        let at = (k - self.floor) as usize;
520        if let Some(c) = self.cache.lock().expect("unpoisoned").get(at) {
521            return Ok(c.clone());
522        }
523        let c = match self.node.as_ref() {
524            Node::Series(h) => ExactReal::from_algebraic(h.laurent_coefficient(k)),
525            Node::Constant(x) => {
526                if k == 0 {
527                    x.clone()
528                } else {
529                    ExactReal::from_i64(0)
530                }
531            }
532            Node::Add(a, b) => a.coefficient(k)?.add(&b.coefficient(k)?),
533            Node::Neg(a) => a.coefficient(k)?.neg(),
534            Node::Mul(a, b) => {
535                let mut total = ExactReal::from_i64(0);
536                for i in a.floor..=(k - b.floor) {
537                    total = total.add(&a.coefficient(i)?.mul(&b.coefficient(k - i)?));
538                }
539                total
540            }
541            Node::Recip(g, order, lead) => {
542                if k == -order {
543                    lead.recip()?
544                } else {
545                    let mut total = ExactReal::from_i64(0);
546                    for i in (-order)..k {
547                        total = total.add(&self.coefficient(i)?.mul(&g.coefficient(order + k - i)?));
548                    }
549                    total.neg().div(lead)?
550                }
551            }
552            Node::Flow(flow, i) => flow.state(k)?[*i].clone(),
553            Node::Standard(x) => {
554                if k == 0 {
555                    x.clone()
556                } else {
557                    return Err(GermError::StandardOnly { order: Rational::from_i64(k) });
558                }
559            }
560            Node::Unlimited(_) => return Err(GermError::StandardOnly { order: Rational::from_i64(k) }),
561            Node::Refine(g, m) => {
562                let m = i64::from(*m);
563                if k.rem_euclid(m) == 0 {
564                    g.coefficient(k.div_euclid(m))?
565                } else {
566                    ExactReal::from_i64(0)
567                }
568            }
569            Node::Puiseux(p) => ExactReal::from_algebraic(p.coefficient(k)),
570            Node::Exp(g) => {
571                if k == 0 {
572                    g.coefficient(0)?.exp()
573                } else {
574                    let mut total = ExactReal::from_i64(0);
575                    for j in 1..=k {
576                        let gj = g.coefficient(j)?;
577                        if !gj.is_exactly_zero() {
578                            total = total.add(&ExactReal::from_i64(j).mul(&gj).mul(&self.coefficient(k - j)?));
579                        }
580                    }
581                    total.mul(&ExactReal::from_rational(Rational::from_ratio_i64(1, k).expect("k ≥ 1")))
582                }
583            }
584            Node::Ln(g, g0) => {
585                if k == 0 {
586                    g0.log()?
587                } else {
588                    let mut inner = ExactReal::from_i64(0);
589                    for j in 1..k {
590                        let gk = g.coefficient(k - j)?;
591                        if !gk.is_exactly_zero() {
592                            inner = inner.add(&ExactReal::from_i64(j).mul(&self.coefficient(j)?).mul(&gk));
593                        }
594                    }
595                    let inner = inner.mul(&ExactReal::from_rational(Rational::from_ratio_i64(1, k).expect("k ≥ 1")));
596                    g.coefficient(k)?.sub(&inner).div(g0)?
597                }
598            }
599            Node::Root { g, n, v, c, lead } => {
600                let j = k - self.floor;
601                if j == 0 {
602                    lead.clone()
603                } else {
604                    let alpha = Rational::from_ratio_i64(1, i64::from(*n)).expect("n ≥ 1").add(&Rational::one());
605                    let mut total = ExactReal::from_i64(0);
606                    for i in 1..=j {
607                        let gi = g.coefficient(v + i)?;
608                        if !gi.is_exactly_zero() {
609                            let weight = alpha.mul(&Rational::from_i64(i)).sub(&Rational::from_i64(j));
610                            total = total.add(&ExactReal::from_rational(weight).mul(&gi).mul(&self.coefficient(k - i)?));
611                        }
612                    }
613                    total.div(&ExactReal::from_i64(j).mul(c))?
614                }
615            }
616        };
617        let mut cache = self.cache.lock().expect("unpoisoned");
618        if cache.len() == at {
619            cache.push(c.clone());
620        }
621        Ok(c)
622    }
623
624    pub fn add(&self, o: &Germ) -> Germ {
625        match (self.unlimited_sign(), o.unlimited_sign()) {
626            (Some(s), None) if o.limited_by_construction() => return Germ::unlimited(s),
627            (None, Some(s)) if self.limited_by_construction() => return Germ::unlimited(s),
628            (Some(s), Some(t)) if s == t => return Germ::unlimited(s),
629            _ => {}
630        }
631        let (a, b, q) = self.met(o);
632        let floor = a.floor.min(b.floor);
633        Germ::of(Node::Add(a, b), floor, q)
634    }
635
636    pub fn neg(&self) -> Germ {
637        if let Some(s) = self.unlimited_sign() {
638            return Germ::unlimited(-s);
639        }
640        Germ::of(Node::Neg(self.clone()), self.floor, self.q)
641    }
642
643    pub fn sub(&self, o: &Germ) -> Germ {
644        self.add(&o.neg())
645    }
646
647    pub fn mul(&self, o: &Germ) -> Germ {
648        let known = |g: &Germ| match g.unlimited_sign() {
649            Some(s) => Some(s),
650            None if g.limited_by_construction() => g
651                .standard_part()
652                .ok()
653                .flatten()
654                .and_then(|x| x.compare(&ExactReal::from_i64(0), DEFAULT_BUDGET).ok())
655                .map(|o| o as i32),
656            None => None,
657        };
658        if self.unlimited_sign().is_some() || o.unlimited_sign().is_some() {
659            if let (Some(s), Some(t)) = (known(self), known(o)) {
660                if s != 0 && t != 0 {
661                    return Germ::unlimited(s * t);
662                }
663            }
664        }
665        let (a, b, q) = self.met(o);
666        let floor = a.floor + b.floor;
667        Germ::of(Node::Mul(a, b), floor, q)
668    }
669
670    /// The order, in units of `dt^(1/q)`, and the coefficient of the first nonzero term.
671    pub fn leading(&self) -> Result<(i64, ExactReal), GermError> {
672        for k in self.floor..self.floor + self.sign_orders() {
673            let c = self.coefficient(k)?;
674            if !c.is_exactly_zero() {
675                let sign = c.compare(&ExactReal::from_i64(0), DEFAULT_BUDGET)?;
676                if sign != std::cmp::Ordering::Equal {
677                    return Ok((k, c));
678                }
679            }
680        }
681        Err(GermError::Flat)
682    }
683
684    pub fn recip(&self) -> Result<Germ, GermError> {
685        let (order, lead) = match self.leading() {
686            Ok(l) => l,
687            Err(GermError::Flat) => return Err(GermError::DivisionByZero),
688            Err(e) => return Err(e),
689        };
690        Ok(Germ::of(Node::Recip(self.clone(), order, lead), -order, self.q))
691    }
692
693    /// The real `n`-th root (`n ≥ 1`), the nonnegative one for an even `n`; refused for a negative
694    /// germ and an even `n`.
695    pub fn root(&self, n: u32) -> Result<Germ, GermError> {
696        assert!(n >= 1, "a root has a positive index");
697        if n == 1 {
698            return Ok(self.clone());
699        }
700        if let Some(sign) = self.unlimited_sign() {
701            return if sign < 0 && n % 2 == 0 {
702                Err(GermError::NegativeRoot(n))
703            } else {
704                Ok(Germ::unlimited(sign))
705            };
706        }
707        let (v, c) = self.leading()?;
708        if n % 2 == 0 && c.compare(&ExactReal::from_i64(0), DEFAULT_BUDGET)? == std::cmp::Ordering::Less {
709            return Err(GermError::NegativeRoot(n));
710        }
711        let m = (u64::from(n) / gcd(u64::from(n), v.unsigned_abs())) as u32;
712        let g = self.refined(m);
713        let v = v * i64::from(m);
714        let lead = c.nth_root(n)?;
715        let floor = v / i64::from(n);
716        let q = g.q;
717        Ok(Germ::of(Node::Root { g, n, v, c, lead }, floor, q))
718    }
719
720    /// `exp(self)`, refused for an infinite value, whose exponential lies beyond every power of `dt`.
721    pub fn exp(&self) -> Result<Germ, GermError> {
722        if self.unlimited_sign().is_some() || self.standard_part()?.is_none() {
723            return Err(GermError::BeyondEveryPower);
724        }
725        Ok(Germ::of(Node::Exp(self.clone()), 0, self.q))
726    }
727
728    /// The natural logarithm of a value whose standard part is positive; one that is negative has
729    /// none, and one that is a positive infinitesimal or infinite carries the logarithm of `dt`.
730    pub fn ln(&self) -> Result<Germ, GermError> {
731        if let Some(sign) = self.unlimited_sign() {
732            return Err(if sign > 0 { GermError::CarriesLog { infinite: true } } else { GermError::Nonpositive });
733        }
734        let (order, lead) = self.leading()?;
735        if lead.compare(&ExactReal::from_i64(0), DEFAULT_BUDGET)? == std::cmp::Ordering::Less {
736            return Err(GermError::Nonpositive);
737        }
738        match order.cmp(&0) {
739            std::cmp::Ordering::Less => Err(GermError::CarriesLog { infinite: true }),
740            std::cmp::Ordering::Greater => Err(GermError::CarriesLog { infinite: false }),
741            std::cmp::Ordering::Equal => Ok(Germ::of(Node::Ln(self.clone(), lead), 0, self.q)),
742        }
743    }
744
745    pub fn div(&self, o: &Germ) -> Result<Germ, GermError> {
746        Ok(self.mul(&o.recip()?))
747    }
748
749    /// `−1`, `0` (only for a germ that is exactly a zero rational function) or `1`.
750    pub fn signum(&self) -> Result<i32, GermError> {
751        match self.node.as_ref() {
752            Node::Series(h) => return Ok(h.signum()),
753            Node::Unlimited(sign) => return Ok(*sign),
754            _ => {}
755        }
756        let (_, lead) = self.leading()?;
757        Ok(match lead.compare(&ExactReal::from_i64(0), DEFAULT_BUDGET)? {
758            std::cmp::Ordering::Less => -1,
759            std::cmp::Ordering::Equal => 0,
760            std::cmp::Ordering::Greater => 1,
761        })
762    }
763
764    /// The standard part of a limited germ; `None` for an infinite one.
765    pub fn standard_part(&self) -> Result<Option<ExactReal>, GermError> {
766        if self.unlimited_sign().is_some() {
767            return Ok(None);
768        }
769        for k in self.floor..0 {
770            let c = self.coefficient(k)?;
771            if !c.is_exactly_zero() && c.compare(&ExactReal::from_i64(0), DEFAULT_BUDGET)? != std::cmp::Ordering::Equal {
772                return Ok(None);
773            }
774        }
775        Ok(Some(self.coefficient(0)?))
776    }
777
778    /// The two leading orders and, from the order after the last one shown on the germ's grid, what
779    /// is left, the infinitesimal written `name`: `e - (1/2)·e·dt + O(dt^2)`, `1 + √dt + O(dt)`.
780    pub fn render(&self, name: &str) -> String {
781        if let Some(sign) = self.unlimited_sign() {
782            return if sign < 0 { "-unlimited".to_string() } else { "unlimited".to_string() };
783        }
784        let mut terms: Vec<String> = Vec::new();
785        let mut k = self.floor;
786        let mut last_shown = None;
787        while terms.len() < 2 && k < self.floor + self.sign_orders() {
788            match self.coefficient(k) {
789                Ok(c) if c.is_exactly_zero() => {}
790                Ok(c) => {
791                    terms.push(expansion_term(name, &c.to_string(), &exponent(k, self.q)));
792                    last_shown = Some(k);
793                }
794                Err(_) => break,
795            }
796            k += 1;
797        }
798        let k = last_shown.map_or(k, |order| order + 1);
799        let rest = remainder(name, &exponent(k, self.q));
800        let mut out = String::new();
801        for (i, t) in terms.iter().enumerate() {
802            match (i, t.strip_prefix('-')) {
803                (0, _) => out.push_str(t),
804                (_, Some(rest)) => {
805                    out.push_str(" - ");
806                    out.push_str(rest);
807                }
808                (_, None) => {
809                    out.push_str(" + ");
810                    out.push_str(t);
811                }
812            }
813        }
814        if out.is_empty() {
815            rest
816        } else {
817            format!("{out} + {rest}")
818        }
819    }
820}
821
822impl crate::exact_codec::ExactCodec for Germ {
823    /// A node tag, then the node: `0` the series of a hyperreal; `1` a sum and `2` a product of two
824    /// germs; `3` a negation; `4` a reciprocal, whose leading term is found again on reading; `5` a
825    /// component of a flow — its width `n`, the number of update terms and each as `n × n`
826    /// rationals, `T`, `δ`, the `n` start germs and the component; `6` a computable real constant
827    /// in `dt`; `7` a value known by its standard part; `8` an unlimited value by its sign; `10` a
828    /// value of the real closure; `11` an exponential, `12` a logarithm and `13` an `n`-th root, `n`
829    /// after the germ; `14` a component of a flow whose update is a polynomial in a root of `dt`, its
830    /// grid and then the fields of `5`. A germ refined to a finer grid is written as itself: every node that meets two
831    /// grids meets them again as it is read.
832    fn write(&self, out: &mut Vec<u8>) {
833        use crate::exact_codec::write_len;
834        match self.node.as_ref() {
835            Node::Refine(g, _) => g.write(out),
836            Node::Puiseux(p) => {
837                out.push(10);
838                p.write(out);
839            }
840            Node::Exp(g) => {
841                out.push(11);
842                g.write(out);
843            }
844            Node::Ln(g, _) => {
845                out.push(12);
846                g.write(out);
847            }
848            Node::Root { g, n, .. } => {
849                out.push(13);
850                g.write(out);
851                write_len(*n as usize, out);
852            }
853            Node::Series(h) => {
854                out.push(0);
855                h.write(out);
856            }
857            Node::Constant(x) => {
858                out.push(6);
859                x.write(out);
860            }
861            Node::Standard(x) => {
862                out.push(7);
863                x.write(out);
864            }
865            Node::Unlimited(sign) => {
866                out.push(8);
867                out.push(u8::from(*sign > 0));
868            }
869            Node::Add(a, b) => {
870                out.push(1);
871                a.write(out);
872                b.write(out);
873            }
874            Node::Mul(a, b) => {
875                out.push(2);
876                a.write(out);
877                b.write(out);
878            }
879            Node::Neg(a) => {
880                out.push(3);
881                a.write(out);
882            }
883            Node::Recip(a, _, _) => {
884                out.push(4);
885                a.write(out);
886            }
887            Node::Flow(flow, i) => {
888                if flow.grid == 1 {
889                    out.push(5);
890                } else {
891                    out.push(14);
892                    write_len(flow.grid as usize, out);
893                }
894                let n = flow.width();
895                write_len(n, out);
896                write_len(flow.terms.len(), out);
897                for m in &flow.terms {
898                    for row in m {
899                        for v in row {
900                            v.write(out);
901                        }
902                    }
903                }
904                flow.t.write(out);
905                flow.delta.write(out);
906                for g in &flow.start {
907                    g.write(out);
908                }
909                (*i as u64).write(out);
910            }
911        }
912    }
913
914    fn read(buf: &[u8], pos: &mut usize) -> Option<Self> {
915        read_germ(buf, pos, 0)
916    }
917}
918
919fn read_germ(buf: &[u8], pos: &mut usize, depth: usize) -> Option<Germ> {
920    use crate::exact_codec::{read_byte, read_len, ExactCodec, MAX_WIRE_DEGREE, MAX_WIRE_DEPTH};
921    if depth > MAX_WIRE_DEPTH {
922        return None;
923    }
924    Some(match read_byte(buf, pos)? {
925        0 => Germ::from_hyperreal(&Hyperreal::read(buf, pos)?),
926        6 => Germ::constant(&ExactReal::read(buf, pos)?),
927        7 => Germ::standard(&ExactReal::read(buf, pos)?),
928        8 => match read_byte(buf, pos)? {
929            0 => Germ::unlimited(-1),
930            1 => Germ::unlimited(1),
931            _ => return None,
932        },
933        1 => {
934            let a = read_germ(buf, pos, depth + 1)?;
935            a.add(&read_germ(buf, pos, depth + 1)?)
936        }
937        2 => {
938            let a = read_germ(buf, pos, depth + 1)?;
939            a.mul(&read_germ(buf, pos, depth + 1)?)
940        }
941        3 => read_germ(buf, pos, depth + 1)?.neg(),
942        4 => read_germ(buf, pos, depth + 1)?.recip().ok()?,
943        10 => Germ::from_puiseux(&Puiseux::read(buf, pos)?),
944        11 => read_germ(buf, pos, depth + 1)?.exp().ok()?,
945        12 => read_germ(buf, pos, depth + 1)?.ln().ok()?,
946        13 => {
947            let g = read_germ(buf, pos, depth + 1)?;
948            let n = read_len(buf, pos, MAX_WIRE_DEGREE).filter(|n| *n >= 2)?;
949            let n = u32::try_from(n).ok()?;
950            g.root(n).ok()?
951        }
952        tag @ (5 | 14) => {
953            let grid = if tag == 5 {
954                1
955            } else {
956                u32::try_from(read_len(buf, pos, MAX_WIRE_DEGREE).filter(|g| *g >= 2)?).ok()?
957            };
958            let n = read_len(buf, pos, MAX_WIRE_DEGREE)?;
959            let count = read_len(buf, pos, MAX_WIRE_DEGREE)?;
960            let mut terms = Vec::with_capacity(count);
961            for _ in 0..count {
962                let mut m = Vec::with_capacity(n);
963                for _ in 0..n {
964                    let mut row = Vec::with_capacity(n);
965                    for _ in 0..n {
966                        row.push(Rational::read(buf, pos)?);
967                    }
968                    m.push(row);
969                }
970                terms.push(m);
971            }
972            let t = Rational::read(buf, pos)?;
973            let delta = Rational::read(buf, pos)?;
974            let mut start = Vec::with_capacity(n);
975            for _ in 0..n {
976                start.push(read_germ(buf, pos, depth + 1)?);
977            }
978            let i = u64::read(buf, pos)? as usize;
979            if i >= n {
980                return None;
981            }
982            Germ::flow(&Flow::on_grid(grid, terms, t, delta, start)?, i)
983        }
984        _ => return None,
985    })
986}
987
988#[cfg(test)]
989#[path = "../../logicaffeine_tests/unit/logicaffeine_base/germ/tests.rs"]
990mod tests;