Skip to main content

logicaffeine_base/
supertask.rs

1//! The run-time half of the supertask engine: the exact operations the statements it writes call,
2//! generic over a value model ([`ValueAlgebra`]) so the interpreter and a compiled program run one
3//! implementation.
4//!
5//! The engine resolves a supertask by rewriting the loop into ordinary statements; what those
6//! statements cannot express in arithmetic alone lives here, each as a builtin every tier runs
7//! the same way.
8//!
9//! - [`trip_count`]: how many times a loop stepped by a clock runs — an infinite natural when the
10//!   step is infinitesimal.
11//! - [`affine_power`]: `Aᴺ·x` for an exact matrix whose eigenvalues are roots of unity, at an
12//!   infinite `N`. With `A^z·(A^p − I)^m = 0` (read off the characteristic polynomial, then
13//!   checked by multiplying it out), `q ↦ A^(z + r + p·q)·x` is a polynomial of degree below `m` in
14//!   `q` for each residue `r`, so `m` samples and their forward differences give
15//!   `Aᴺ·x = Σ_j C(q, j)·Δʲ` exactly, read on each residue class of `N`.
16//! - [`polynomial_flow`]: `N` Euler steps of a polynomial differential equation, ending on the
17//!   validated solution's germ.
18//! - [`hybrid_flow`]: `N` steps of a loop whose state flows by a polynomial Euler step between
19//!   jumps: the values of the ideal hybrid system it follows (see [`crate::hybrid`]), Zeno tails and
20//!   continuous firing included, each a germ known by its standard part or, where the loop's own
21//!   value is exactly it, that value.
22//! - [`cycle_at`]: the state at step `N` of a recorded run that has entered a cycle.
23//! - [`first_exit`]: how many passes a loop makes whose guard compares terms linear in a counter
24//!   it steps — `While not (n <= x and x < n + 1):`, the floor of Imamura's Lemma 1 — read where
25//!   the guard's truth can change, and on each residue class of the generator where those points
26//!   depend on it.
27//! - [`search_candidates`]: the naturals at which a search over every natural must look. A
28//!   predicate built from linear terms `a·n + b` and remainders modulo `m` is constant between the
29//!   integers where some `a·n + b` changes sign and periodic with the period `lcm(m)` there, so its
30//!   first witness, if any, lies at a sign change or within one period after one.
31
32use crate::algebraic::Algebraic;
33use crate::exact_real::{ExactReal, RealError};
34use crate::hybrid;
35use crate::germ::{Flow, Germ};
36use crate::hypernatural::Hypernatural;
37use crate::hyperreal::Hyperreal;
38use crate::limit_value::{bounded_lcm, Depends, ResidueError, ResidueType, MAX_MODULUS};
39use crate::number::Number;
40use crate::numeric::{BigInt, Rational};
41use crate::ode::{escape_time_bound, Field as OdeField, Monomial};
42use crate::poly::Poly;
43use crate::tower::{self, Branch, ExactRef, HyperForm, HyperValue, NaturalValue, Rel, Scale, Shape, TowerValue, ValueAlgebra};
44use std::cmp::Ordering;
45use std::sync::Arc;
46
47/// A square matrix of values, row by row.
48pub type Matrix<V> = Vec<Vec<V>>;
49
50fn int<V: TowerValue>(n: i64) -> V {
51    V::from_bigint(BigInt::from_i64(n))
52}
53
54fn items<V: ValueAlgebra>(v: &V, what: &str) -> Result<Vec<V>, String> {
55    match v.shape() {
56        Shape::List(values) => Ok(values),
57        _ => Err(format!("{what} is a Seq, got {}", v.type_name())),
58    }
59}
60
61// ── trip counts ───────────────────────────────────────────────────────────────────────────────
62
63/// A number of the tower, standard or not.
64fn is_number<V: TowerValue>(v: &V) -> bool {
65    v.is_standard_number() || matches!(v.exact(), Some(e) if !matches!(e, ExactRef::Ordinal(_)))
66}
67
68/// A number with no infinitesimal or infinite part and no dependence on the residue.
69fn is_standard<V: TowerValue>(v: &V) -> bool {
70    v.is_standard_number() || matches!(v.exact(), Some(ExactRef::Algebraic(_)) | Some(ExactRef::Real(_)))
71}
72
73/// How many times `While t <relation> bound:` runs when each step adds `step` to `t`, starting
74/// from `t0`: `⌈(bound − t0)/step⌉` for a strict bound, `⌊…⌋ + 1` for an inclusive one, `0` when
75/// the guard is false at the start. `−1` — not counted, so the loop runs as written — when the
76/// clock is not a number, or when a standard step walks away from its bound; a refusal when an
77/// infinitesimal step does, since that loop could never end.
78pub fn trip_count<V: ValueAlgebra>(t0: &V, step: &V, bound: &V, relation: &str) -> Result<V, String> {
79    let (guard, rising) = match relation {
80        "<" => (Rel::Lt, true),
81        "<=" => (Rel::LtEq, true),
82        ">" => (Rel::Gt, false),
83        ">=" => (Rel::GtEq, false),
84        other => {
85            return Err(format!(
86                "a clock's guard is a relation <, <=, > or >=, got `{other}`"
87            ))
88        }
89    };
90    if !(is_number(t0) && is_number(step) && is_number(bound)) {
91        return Ok(int(-1));
92    }
93    if !V::holds(guard, t0, bound)? {
94        return Ok(int(0));
95    }
96    let toward = if rising { Rel::Gt } else { Rel::Lt };
97    if !V::holds(toward, step, &int(0))? {
98        if is_standard(step) {
99            return Ok(int(-1));
100        }
101        return Err(format!(
102            "the clock never reaches its bound: it starts at {}, steps by {} and runs while it is {relation} {}",
103            t0.display_string(),
104            step.display_string(),
105            bound.display_string()
106        ));
107    }
108    let distance = V::subtract(bound.clone(), t0.clone())?;
109    let steps = V::exact_divide(distance, step.clone())?;
110    match guard {
111        Rel::Lt | Rel::Gt => V::ceil(steps),
112        _ => V::add(V::floor(steps)?, int(1)),
113    }
114}
115
116// ── affine powers ─────────────────────────────────────────────────────────────────────────────
117
118fn matrix_of<V: ValueAlgebra>(v: &V) -> Result<Matrix<V>, String> {
119    let rows = items(v, "a supertask's update matrix")?;
120    let rows: Matrix<V> = rows
121        .iter()
122        .map(|r| items(r, "each row of a supertask's update matrix"))
123        .collect::<Result<_, _>>()?;
124    if rows.is_empty() || rows.iter().any(|r| r.len() != rows.len()) {
125        return Err("a supertask's update matrix is square and not empty".to_string());
126    }
127    Ok(rows)
128}
129
130fn identity<V: ValueAlgebra>(n: usize) -> Matrix<V> {
131    (0..n)
132        .map(|i| (0..n).map(|j| int(i64::from(i == j))).collect())
133        .collect()
134}
135
136fn mat_mul<V: ValueAlgebra>(a: &Matrix<V>, b: &Matrix<V>) -> Result<Matrix<V>, String> {
137    let n = a.len();
138    let mut out = Vec::with_capacity(n);
139    for row in a {
140        let mut out_row = Vec::with_capacity(n);
141        for j in 0..n {
142            let mut acc = int(0);
143            for (k, entry) in row.iter().enumerate() {
144                acc = V::add(acc, V::multiply(entry.clone(), b[k][j].clone())?)?;
145            }
146            out_row.push(acc);
147        }
148        out.push(out_row);
149    }
150    Ok(out)
151}
152
153fn mat_vec<V: ValueAlgebra>(a: &Matrix<V>, x: &[V]) -> Result<Vec<V>, String> {
154    a.iter()
155        .map(|row| {
156            row.iter().zip(x).try_fold(int(0), |acc, (e, v)| {
157                V::add(acc, V::multiply(e.clone(), v.clone())?)
158            })
159        })
160        .collect()
161}
162
163fn mat_sub<V: ValueAlgebra>(a: &Matrix<V>, b: &Matrix<V>) -> Result<Matrix<V>, String> {
164    a.iter()
165        .zip(b)
166        .map(|(ra, rb)| {
167            ra.iter()
168                .zip(rb)
169                .map(|(x, y)| V::subtract(x.clone(), y.clone()))
170                .collect()
171        })
172        .collect()
173}
174
175fn mat_pow<V: ValueAlgebra>(a: &Matrix<V>, mut e: BigInt) -> Result<Matrix<V>, String> {
176    let two = BigInt::from_u64(2);
177    let mut acc = identity(a.len());
178    let mut base = a.clone();
179    while e.is_positive() {
180        let (q, r) = e.div_rem(&two).expect("two is nonzero");
181        if !r.is_zero() {
182            acc = mat_mul(&acc, &base)?;
183        }
184        e = q;
185        if e.is_positive() {
186            base = mat_mul(&base, &base)?;
187        }
188    }
189    Ok(acc)
190}
191
192fn is_zero<V: ValueAlgebra>(v: &V) -> Result<bool, String> {
193    V::holds(Rel::Eq, v, &int(0))
194}
195
196/// A standard rational entry of a characteristic polynomial: an integer, a rational, a float as the
197/// rational it denotes, or a rational algebraic number; `None` for anything else.
198fn as_rational<V: ValueAlgebra>(v: &V) -> Option<Rational> {
199    match v.shape() {
200        Shape::Int(n) => Some(Rational::from_i64(n)),
201        Shape::BigInt(b) => Some(Rational::from_bigint(b)),
202        Shape::Rational(r) => Some(r),
203        Shape::Float => v.standard_rational(),
204        _ => match v.exact()? {
205            ExactRef::Algebraic(a) => a.to_rational(),
206            _ => None,
207        },
208    }
209}
210
211/// `det(xI − A)` by Faddeev–LeVerrier: `M₀ = 0`, `Mₖ = A·Mₖ₋₁ + c_{n−k+1}·I`,
212/// `c_{n−k} = −tr(A·Mₖ)/k`.
213fn characteristic_polynomial<V: ValueAlgebra>(a: &Matrix<V>) -> Result<Vec<V>, String> {
214    let n = a.len();
215    let mut c: Vec<V> = vec![int(0); n + 1];
216    c[n] = int(1);
217    let mut m: Matrix<V> = vec![vec![int(0); n]; n];
218    for k in 1..=n {
219        let am = mat_mul(a, &m)?;
220        m = am;
221        for (i, row) in m.iter_mut().enumerate() {
222            row[i] = V::add(row[i].clone(), c[n - k + 1].clone())?;
223        }
224        let am = mat_mul(a, &m)?;
225        let trace = (0..n).try_fold(int(0), |acc, i| V::add(acc, am[i][i].clone()))?;
226        let neg = V::subtract(int(0), trace)?;
227        c[n - k] = V::exact_divide(neg, int(k as i64))?;
228    }
229    Ok(c)
230}
231
232fn totient(mut n: u64) -> u64 {
233    let mut result = n;
234    let mut p = 2;
235    while p * p <= n {
236        if n.is_multiple_of(p) {
237            while n.is_multiple_of(p) {
238                n /= p;
239            }
240            result -= result / p;
241        }
242        p += 1;
243    }
244    if n > 1 {
245        result -= result / n;
246    }
247    result
248}
249
250fn mobius(mut n: u64) -> i32 {
251    let mut sign = 1;
252    let mut p = 2;
253    while p * p <= n {
254        if n.is_multiple_of(p) {
255            n /= p;
256            if n.is_multiple_of(p) {
257                return 0;
258            }
259            sign = -sign;
260        }
261        p += 1;
262    }
263    if n > 1 {
264        sign = -sign;
265    }
266    sign
267}
268
269/// `Φ_d = ∏_{e | d} (xᵉ − 1)^μ(d/e)`.
270fn cyclotomic(d: u64) -> Poly {
271    let mut num = Poly::one();
272    let mut den = Poly::one();
273    for e in (1..=d).filter(|e| d.is_multiple_of(*e)) {
274        let mut c = vec![Rational::zero(); e as usize + 1];
275        c[0] = Rational::from_i64(-1);
276        c[e as usize] = Rational::from_i64(1);
277        let factor = Poly::from_coeffs(c);
278        match mobius(d / e) {
279            1 => num = num.mul(&factor),
280            -1 => den = den.mul(&factor),
281            _ => {}
282        }
283    }
284    num.div_rem(&den).expect("a nonzero divisor").0
285}
286
287/// The order `d` with `Φ_d = f` for a monic irreducible `f`; `None` when `f` is not cyclotomic.
288/// `φ(d) ≥ √(d/2)`, so every `d` with `φ(d) = deg f` is below `2·deg²`.
289fn cyclotomic_order(f: &Poly) -> Option<u64> {
290    let degree = f.degree()? as u64;
291    (1..=2 * degree * degree + 2)
292        .filter(|d| totient(*d) == degree)
293        .find(|d| cyclotomic(*d) == *f)
294}
295
296/// `(z, p, m)` with `A^z·(A^p − I)^m = 0`, read off the characteristic polynomial: `z` the
297/// multiplicity of the eigenvalue 0, `p` the lcm of the orders of the roots of unity, `m` the
298/// largest multiplicity among them.
299fn unit_root_structure<V: ValueAlgebra>(a: &Matrix<V>) -> Result<(u64, u64, u64), String> {
300    let coefficients = characteristic_polynomial(a)?;
301    let rational: Vec<Rational> = coefficients
302        .iter()
303        .map(|c| {
304            as_rational(c).ok_or_else(|| {
305                format!(
306                    "the update's characteristic polynomial has the coefficient {}; a supertask's closed \
307                     form is exact here only for an eigenvalue that is a standard root of unity",
308                    c.display_string()
309                )
310            })
311        })
312        .collect::<Result<_, _>>()?;
313    let chi = Poly::from_coeffs(rational);
314    let (mut z, mut p, mut m) = (0u64, 1u64, 0u64);
315    for (factor, multiplicity) in chi.factor_over_q() {
316        let factor = factor.monic();
317        if factor == Poly::x() {
318            z = u64::from(multiplicity);
319            continue;
320        }
321        let d = cyclotomic_order(&factor).ok_or_else(|| {
322            format!(
323                "an eigenvalue of the update is not a root of unity (a root of {factor}); the state \
324                 grows, shrinks or turns by an irrational angle at every step, and a supertask over \
325                 such an update has no exact closed form here"
326            )
327        })?;
328        p = p / gcd(p, d) * d;
329        m = m.max(u64::from(multiplicity));
330    }
331    Ok((z, p, m))
332}
333
334fn gcd(a: u64, b: u64) -> u64 {
335    if b == 0 {
336        a
337    } else {
338        gcd(b, a % b)
339    }
340}
341
342/// The infinite natural a step count is, or the refusal of anything but a natural number.
343fn infinite_count<V: TowerValue>(n: &V) -> Result<NaturalValue, String> {
344    match n.exact() {
345        Some(ExactRef::Natural(nv)) => Ok(nv.clone()),
346        _ if several_count(n) => Err(format!(
347            "this supertask's step count, {}, is infinite in more than one infinitesimal, which only an affine \
348             closed form or a cycle is read over; a flow is integrated over a count of one",
349            n.display_string()
350        )),
351        _ => Err(format!(
352            "a supertask's step count is a natural number, got {}",
353            n.display_string()
354        )),
355    }
356}
357
358/// Whether a step count is an infinite integer of several infinitesimals ([`HyperForm::Integer`]).
359fn several_count<V: TowerValue>(n: &V) -> bool {
360    matches!(n.exact(), Some(ExactRef::Hyper(h)) if matches!(h.value, HyperForm::Integer(_)))
361}
362
363/// `f` of a residue `r` — a natural number, or one that depends on the residues of the generators — read
364/// on each class it is a natural number on, the classes' answers joined as one value.
365fn on_residue<V: ValueAlgebra>(r: &V, f: &dyn Fn(u64) -> Result<V, String>) -> Result<V, String> {
366    if let Some(k) = r.integer() {
367        return f(k.to_u64().ok_or_else(|| format!("a residue is a natural number, got {}", r.display_string()))?);
368    }
369    match r.exact() {
370        Some(ExactRef::Depends(d)) => tower::map_depends(d, |b| on_residue(&b, f)),
371        _ => Err(format!("a residue is a natural number, got {}", r.display_string())),
372    }
373}
374
375/// `Aᴺ·x` for a step count infinite in several infinitesimals, one dependency closure at a time, as
376/// [`affine_power`] reads one of one: an update whose characteristic polynomial is standard and whose
377/// eigenvalues are roots of unity by the finite differences of its powers, read on the classes of
378/// `N` modulo their period; any other by `exp(N·log A)` in the smallest infinitesimal the update, its
379/// state and the count hold ([`crate::nested_flow::power`]).
380fn several_count_power<V: ValueAlgebra>(a: &Matrix<V>, x: &[V], n: &V) -> Result<Vec<V>, String> {
381    let width = a.len();
382    let mut values: Vec<Option<V>> = vec![None; width];
383    for i in 0..width {
384        if values[i].is_some() {
385            continue;
386        }
387        let closure = dependency_closure(a, i)?;
388        let sub_a: Matrix<V> = closure
389            .iter()
390            .map(|&r| closure.iter().map(|&c| a[r][c].clone()).collect())
391            .collect();
392        let sub_x: Vec<V> = closure.iter().map(|&k| x[k].clone()).collect();
393        let standard = characteristic_polynomial(&sub_a)?
394            .iter()
395            .all(|c| as_rational(c).is_some());
396        let results = if standard {
397            let (z, p, m) = unit_root_structure(&sub_a)?;
398            check_annihilator(&sub_a, z, p, m)?;
399            let differences = root_differences(&sub_a, &sub_x, z, p, m)?;
400            evaluate_over_count(n, z, p, &differences, sub_x.len())?
401        } else {
402            let mut found = Vec::new();
403            for v in sub_a.iter().flatten().chain(sub_x.iter()).chain(std::iter::once(n)) {
404                infinitesimals_in(v, &mut found);
405            }
406            for (k, s) in found.iter().enumerate() {
407                if let Some(t) = found[k + 1..].iter().find(|t| s.order(t).is_none()) {
408                    return Err(s.unordered(t));
409                }
410            }
411            let finest = found
412                .iter()
413                .find(|s| found.iter().all(|t| s.order(t) != Some(Ordering::Greater)))
414                .expect("a count infinite in several infinitesimals holds one")
415                .clone();
416            let matrix: crate::nested_flow::NMatrix = sub_a.iter().map(|row| row.iter().map(tower::as_number).collect()).collect();
417            let start: Vec<Number> = sub_x.iter().map(tower::as_number).collect();
418            crate::nested_flow::power(&finest, &matrix, tower::as_number(n), &start)?
419                .into_iter()
420                .map(tower::from_number)
421                .collect()
422        };
423        for (pos, &k) in closure.iter().enumerate() {
424            values[k].get_or_insert_with(|| results[pos].clone());
425        }
426    }
427    Ok(values.into_iter().map(|v| v.expect("every component is resolved")).collect())
428}
429
430/// `Aᴺ·x` read from the finite differences of `A^(z + r + p·j)·x` ([`root_differences`]) for a count
431/// `N` of any kind: `N − z ≡ r (mod p)` on each class, and `Σ_k C((N − z − r)/p, k)·Δᵏ` there.
432fn evaluate_over_count<V: ValueAlgebra>(
433    n: &V,
434    z: u64,
435    p: u64,
436    differences: &[Vec<Vec<V>>],
437    width: usize,
438) -> Result<Vec<V>, String> {
439    let shifted = V::subtract(n.clone(), int(z as i64))?;
440    let residue = V::modulo(shifted.clone(), int(p as i64))?;
441    (0..width)
442        .map(|i| {
443            on_residue(&residue, &|r| {
444                let q = V::exact_divide(V::subtract(shifted.clone(), int(r as i64))?, int(p as i64))?;
445                let mut binomial: V = int(1);
446                let mut total: V = int(0);
447                for (j, delta) in differences[r as usize].iter().enumerate() {
448                    if j > 0 {
449                        let factor = V::subtract(q.clone(), int(j as i64 - 1))?;
450                        binomial = V::exact_divide(V::multiply(binomial, factor)?, int(j as i64))?;
451                    }
452                    total = V::add(total, V::multiply(binomial.clone(), delta[i].clone())?)?;
453                }
454                Ok(total)
455            })
456        })
457        .collect()
458}
459
460/// `Aᴺ·x`. See the module documentation.
461pub fn affine_power<V: ValueAlgebra>(matrix: &V, vector: &V, n: &V) -> Result<V, String> {
462    if let Some(on_classes) = on_each_class(&[matrix, vector, n], |args| affine_power(&args[0], &args[1], &args[2])) {
463        return on_classes;
464    }
465    let a = matrix_of(matrix)?;
466    let x = items(vector, "a supertask's state")?;
467    if x.len() != a.len() {
468        return Err(format!(
469            "a supertask's state has {} values for a {}-by-{} update",
470            x.len(),
471            a.len(),
472            a.len()
473        ));
474    }
475    let natural = match n.shape() {
476        Shape::Int(k) if k >= 0 => {
477            return Ok(V::list(mat_vec(&mat_pow(&a, BigInt::from_i64(k))?, &x)?));
478        }
479        Shape::BigInt(k) if k.is_positive() => {
480            return Ok(V::list(mat_vec(&mat_pow(&a, k)?, &x)?));
481        }
482        _ if several_count(n) => return Ok(V::list(several_count_power(&a, &x, n)?)),
483        _ => infinite_count(n)?,
484    };
485    let width = a.len();
486    let mut values: Vec<Option<V>> = vec![None; width];
487    for i in 0..width {
488        if values[i].is_some() {
489            continue;
490        }
491        let closure = dependency_closure(&a, i)?;
492        let sub_a: Matrix<V> = closure
493            .iter()
494            .map(|&r| closure.iter().map(|&c| a[r][c].clone()).collect())
495            .collect();
496        let sub_x: Vec<V> = closure.iter().map(|&k| x[k].clone()).collect();
497        let standard = characteristic_polynomial(&sub_a)?
498            .iter()
499            .all(|c| as_rational(c).is_some());
500        let results = if standard {
501            root_of_unity_power(&sub_a, &sub_x, &natural)?
502        } else {
503            flow_power(&sub_a, &sub_x, &natural)?
504        };
505        for (pos, &k) in closure.iter().enumerate() {
506            values[k].get_or_insert_with(|| results[pos].clone());
507        }
508    }
509    Ok(V::list(
510        values
511            .into_iter()
512            .map(|v| v.expect("every component is resolved"))
513            .collect(),
514    ))
515}
516
517/// Every component the state's component `i` reads, at any distance, `i` included, ascending.
518fn dependency_closure<V: ValueAlgebra>(a: &Matrix<V>, i: usize) -> Result<Vec<usize>, String> {
519    let mut seen = vec![false; a.len()];
520    let mut stack = vec![i];
521    seen[i] = true;
522    while let Some(r) = stack.pop() {
523        for (c, entry) in a[r].iter().enumerate() {
524            if !seen[c] && !is_zero(entry)? {
525                seen[c] = true;
526                stack.push(c);
527            }
528        }
529    }
530    Ok((0..a.len()).filter(|&k| seen[k]).collect())
531}
532
533/// The certificate the closed form rests on, checked by multiplying it out: `A^z·(A^p − I)^m = 0`.
534pub fn check_annihilator<V: ValueAlgebra>(a: &Matrix<V>, z: u64, p: u64, m: u64) -> Result<(), String> {
535    let checked = mat_mul(
536        &mat_pow(a, BigInt::from_u64(z))?,
537        &mat_pow(
538            &mat_sub(&mat_pow(a, BigInt::from_u64(p))?, &identity(a.len()))?,
539            BigInt::from_u64(m),
540        )?,
541    )?;
542    for entry in checked.iter().flatten() {
543        if !is_zero(entry)? {
544            return Err(format!(
545                "the supertask engine's certificate A^{z}·(A^{p} − I)^{m} = 0 failed to check; no closed form is used"
546            ));
547        }
548    }
549    Ok(())
550}
551
552/// `Aᴺ·x` for an update whose characteristic polynomial is standard and whose eigenvalues are
553/// roots of unity — its entries may still carry the infinitesimal, as a clock's step does.
554fn root_of_unity_power<V: ValueAlgebra>(
555    a: &Matrix<V>,
556    x: &[V],
557    natural: &NaturalValue,
558) -> Result<Vec<V>, String> {
559    let (z, p, m) = unit_root_structure(a)?;
560    check_annihilator(a, z, p, m)?;
561    let differences = root_differences(a, x, z, p, m)?;
562    evaluate_by_class(natural, z, p, &differences, x.len())
563}
564
565/// The finite differences, through order `m`, of `A^(z + r + p·j)·x` in `j`, for each `r < p`: the
566/// pieces `Aᴺ·x` is read from on each class of `N − z` modulo `p`.
567fn root_differences<V: ValueAlgebra>(a: &Matrix<V>, x: &[V], z: u64, p: u64, m: u64) -> Result<Vec<Vec<Vec<V>>>, String> {
568    let samples = m.max(1);
569    let mut differences: Vec<Vec<Vec<V>>> = Vec::with_capacity(p as usize);
570    for r in 0..p {
571        let mut points = Vec::with_capacity(samples as usize);
572        for j in 0..samples {
573            let power = mat_pow(a, BigInt::from_u64(z + r + p * j))?;
574            points.push(mat_vec(&power, x)?);
575        }
576        let mut deltas = Vec::with_capacity(samples as usize);
577        for _ in 0..samples {
578            deltas.push(points[0].clone());
579            points = points
580                .windows(2)
581                .map(|w| {
582                    w[1].iter()
583                        .zip(&w[0])
584                        .map(|(b, a)| V::subtract(b.clone(), a.clone()))
585                        .collect::<Result<Vec<_>, _>>()
586                })
587                .collect::<Result<_, _>>()?;
588        }
589        differences.push(deltas);
590    }
591    Ok(differences)
592}
593
594/// A polynomial in a root of the infinitesimal `scale` with rational coefficients: the root's `q` and
595/// the coefficients of `dt^(k/q)`, lowest power first. `1 + 2·dt + dt^(3/2)` is `(2, [1, 0, 2, 1])`.
596fn dt_polynomial<V: ValueAlgebra>(v: &V, scale: &Scale) -> Option<(u32, Vec<Rational>)> {
597    let (h, q) = match v.exact() {
598        Some(ExactRef::Hyper(h)) if !h.scale.same_infinitesimal(scale) => return None,
599        Some(ExactRef::Hyper(h)) => match &h.value {
600            HyperForm::Rational(r) => (r.clone(), 1),
601            HyperForm::Puiseux(p) => p.rational_function()?,
602            HyperForm::Germ(_) | HyperForm::Multi(_) | HyperForm::Integer(_) | HyperForm::Nested(_) => return None,
603        },
604        _ => return Some((1, vec![as_rational(v)?])),
605    };
606    let coefficients = h.as_dt_polynomial()?.iter().map(|c| c.to_rational()).collect::<Option<_>>()?;
607    Some((q, coefficients))
608}
609
610/// Whether a polynomial in `dt^(1/q)` has a term between `dt⁰` and `dt¹`, which no step of a flow has.
611fn below_dt(q: u32, coefficients: &[Rational]) -> bool {
612    coefficients.iter().take(q as usize).skip(1).any(|c| !c.is_zero())
613}
614
615/// The value on the residue class `c`, for a value that depends on the residue.
616fn at_class<V: TowerValue>(v: &V, c: u64) -> V {
617    match v.exact() {
618        Some(ExactRef::Depends(d)) => d.value.at(c).map(|b| b.0.clone()).unwrap_or_else(|| v.clone()),
619        Some(ExactRef::Natural(n)) => match n.value.depends().at(c) {
620            Some(p) => tower::from_hyper(Hyperreal::from_reciprocal_powers(p.coeffs()), &n.scale),
621            None => v.clone(),
622        },
623        _ => v.clone(),
624    }
625}
626
627/// The moduli the residue-dependent values of a state are split by.
628fn state_moduli<V: TowerValue>(x: &[V]) -> Vec<u64> {
629    x.iter()
630        .filter_map(|v| match v.exact() {
631            Some(ExactRef::Depends(d)) => Some(d.value.modulus()),
632            Some(ExactRef::Natural(h)) => Some(h.value.depends().modulus()),
633            _ => None,
634        })
635        .collect()
636}
637
638fn classes(moduli: impl IntoIterator<Item = u64>) -> Option<u64> {
639    moduli.into_iter().try_fold(1u64, |acc, m| {
640        let l = u128::from(acc / gcd(acc, m)) * u128::from(m);
641        (l <= u128::from(MAX_MODULUS)).then_some(l as u64)
642    })
643}
644
645/// `N` steps of `x ↦ A·x` for `A = I + Σ_{k≥1} dt^(k/q)·A_k` with rational `A_k`, none below `dt`, and
646/// `N = T/dt + δ` on each residue class: the state as germs (see `crate::germ`).
647fn flow_power<V: ValueAlgebra>(a: &Matrix<V>, x: &[V], natural: &NaturalValue) -> Result<Vec<V>, String> {
648    let n = a.len();
649    let clock = &natural.scale;
650    let mut found = Vec::new();
651    for v in a.iter().flatten().chain(x.iter()) {
652        infinitesimals_in(v, &mut found);
653    }
654    for s in &found {
655        if s.order(clock).is_none() {
656            return Err(clock.unordered(s));
657        }
658    }
659    let finer = found
660        .iter()
661        .filter(|s| s.order(clock) == Some(Ordering::Less))
662        .fold(None::<Arc<Scale>>, |least, s| match least {
663            Some(t) if t.order(s) == Some(Ordering::Less) => Some(t),
664            _ => Some(s.clone()),
665        });
666    if let Some(finer) = finer {
667        return several_on_classes(a, x, natural, |matrix, start, count| {
668            crate::nested_flow::power(&finer, matrix, count.clone(), start)
669        });
670    }
671    if found.iter().any(|s| s.order(clock) == Some(Ordering::Greater)) {
672        return several_on_classes(a, x, natural, |matrix, start, count| {
673            let (t, delta) = linear_count(count)?;
674            crate::nested_flow::flow(clock, matrix, t, delta, start)
675        });
676    }
677    let mut entries = Vec::with_capacity(n * n);
678    for (i, row) in a.iter().enumerate() {
679        for (j, entry) in row.iter().enumerate() {
680            let (q, coefficients) = dt_polynomial(entry, &natural.scale).ok_or_else(|| {
681                format!(
682                    "the update's entry {} is not a polynomial in the infinitesimal, or in a root of it, with rational coefficients, so this supertask is not a flow the engine integrates",
683                    entry.display_string()
684                )
685            })?;
686            if below_dt(q, &coefficients) {
687                return Err(format!(
688                    "the update's entry {} has a term of a fractional order of the infinitesimal below its first, so this supertask is not a flow",
689                    entry.display_string()
690                ));
691            }
692            entries.push((i, j, q, coefficients));
693        }
694    }
695    let grid = entries.iter().fold(1u64, |l, (_, _, q, _)| l / gcd(l, u64::from(*q)) * u64::from(*q));
696    let grid = u32::try_from(grid).map_err(|_| "the update's entries lie on too fine a root of the infinitesimal".to_string())?;
697    let mut orders: Vec<Vec<Vec<Rational>>> = Vec::new();
698    for (i, j, q, coefficients) in entries {
699        let spread = (grid / q) as usize;
700        for (k, c) in coefficients.into_iter().enumerate() {
701            while orders.len() <= k * spread {
702                orders.push(vec![vec![Rational::zero(); n]; n]);
703            }
704            orders[k * spread][i][j] = c;
705        }
706    }
707    let identity: Vec<Vec<Rational>> = (0..n)
708        .map(|i| (0..n).map(|j| if i == j { Rational::one() } else { Rational::zero() }).collect())
709        .collect();
710    if orders.first() != Some(&identity) {
711        return Err(
712            "the update changes its state by a standard amount at every step as well as an infinitesimal one, so this supertask is neither a cycle nor a flow"
713                .to_string(),
714        );
715    }
716    let terms: Vec<Vec<Vec<Rational>>> = orders[1..].to_vec();
717    if terms.is_empty() {
718        return Ok(x.to_vec());
719    }
720    let scale = &natural.scale;
721    let ty = scale.residues.clone();
722    let counts = natural.value.depends();
723    let l = classes([counts.modulus(), ty.modulus()].into_iter().chain(state_moduli(x)))
724        .ok_or_else(|| "a flow's step count and state split over more residue classes than one value holds".to_string())?;
725    let mut columns: Vec<Vec<Option<Branch<V>>>> = vec![Vec::with_capacity(l as usize); n];
726    for c in 0..l {
727        let Some(count) = counts.at(c) else {
728            columns.iter_mut().for_each(|col| col.push(None));
729            continue;
730        };
731        if count.degree().unwrap_or(0) != 1 {
732            return Err(
733                "a flow's step count is a multiple of 1/dt; one that grows faster or slower has no standard end state here"
734                    .to_string(),
735            );
736        }
737        let (t, delta) = (count.coeff(1), count.coeff(0));
738        if !t.is_positive() {
739            return Err("a flow runs forward a positive time".to_string());
740        }
741        let start: Vec<Germ> = x
742            .iter()
743            .map(|v| {
744                let at = at_class(v, c);
745                tower::germ_of(&at).ok_or_else(|| {
746                    format!("a flow's state is numbers, not {}", at.display_string())
747                })
748            })
749            .collect::<Result<_, _>>()?;
750        let flow = Flow::on_grid(grid, terms.clone(), t, delta, start)
751            .ok_or_else(|| "a flow's update and state disagree in shape".to_string())?;
752        for (i, column) in columns.iter_mut().enumerate() {
753            column.push(Some(Branch(tower::from_germ(Germ::flow(&flow, i), scale))));
754        }
755    }
756    Ok(columns
757        .into_iter()
758        .map(|raw| {
759            let d = Depends::from_branches(ty.clone(), l, raw).expect("the classes are the type's own");
760            tower::from_depends(d, scale)
761        })
762        .collect())
763}
764
765/// `N` steps of `x ↦ A·x` for an update that holds infinitesimals other than the clock's, on each
766/// residue class of the step count: `end` gives the state from the update, the start and the count
767/// on the class, each a [`Number`].
768fn several_on_classes<V: ValueAlgebra>(
769    a: &Matrix<V>,
770    x: &[V],
771    natural: &NaturalValue,
772    end: impl Fn(&crate::nested_flow::NMatrix, &[Number], &Number) -> Result<Vec<Number>, String>,
773) -> Result<Vec<V>, String> {
774    let n = a.len();
775    let scale = &natural.scale;
776    let ty = scale.residues.clone();
777    let counts = natural.value.depends();
778    let l = classes([counts.modulus(), ty.modulus()].into_iter().chain(state_moduli(x)))
779        .ok_or_else(|| "a flow's step count and state split over more residue classes than one value holds".to_string())?;
780    let matrix: crate::nested_flow::NMatrix = a.iter().map(|row| row.iter().map(tower::as_number).collect()).collect();
781    let mut columns: Vec<Vec<Option<Branch<V>>>> = vec![Vec::with_capacity(l as usize); n];
782    for c in 0..l {
783        let Some(count) = counts.at(c) else {
784            columns.iter_mut().for_each(|col| col.push(None));
785            continue;
786        };
787        let count = tower::as_number::<V>(&tower::from_hyper(Hyperreal::from_reciprocal_powers(count.coeffs()), scale));
788        let start: Vec<Number> = x.iter().map(|v| tower::as_number(&at_class(v, c))).collect();
789        let state = end(&matrix, &start, &count)?;
790        for (column, value) in columns.iter_mut().zip(state) {
791            column.push(Some(Branch(tower::from_number(value))));
792        }
793    }
794    Ok(columns
795        .into_iter()
796        .map(|raw| {
797            let d = Depends::from_branches(ty.clone(), l, raw).expect("the classes are the type's own");
798            tower::from_depends(d, scale)
799        })
800        .collect())
801}
802
803/// `N = T/dt + δ`, a step count read as the time it covers and its offset; refused for a count that
804/// grows faster or slower than `1/dt`.
805fn linear_count(count: &Number) -> Result<(Rational, Rational), String> {
806    let refuse = || {
807        "a flow's step count is a multiple of 1/dt; one that grows faster or slower has no standard end state here".to_string()
808    };
809    let hyper = match count.exact() {
810        Some(ExactRef::Hyper(h)) => h.value.rational().cloned().ok_or_else(refuse)?,
811        Some(ExactRef::Natural(n)) => n.value.to_hyperreal().single().cloned().ok_or_else(refuse)?,
812        _ => return Err(refuse()),
813    };
814    let powers = hyper.as_reciprocal_powers().ok_or_else(refuse)?;
815    if powers.len() != 2 || !powers[1].is_positive() {
816        return Err(if powers.len() == 2 { "a flow runs forward a positive time".to_string() } else { refuse() });
817    }
818    Ok((powers[1].clone(), powers[0].clone()))
819}
820
821/// One monomial of a supertask's polynomial update: its coefficient and its exponents.
822struct Term<V> {
823    coefficient: V,
824    exponents: Vec<u32>,
825}
826
827/// A polynomial over a state of `width` values, encoded as a list of `[coefficient, e₁, …, eₖ]`
828/// entries.
829fn poly_terms<V: ValueAlgebra>(poly: &V, width: usize) -> Result<Vec<Term<V>>, String> {
830    items(poly, "a polynomial update's component")?
831        .iter()
832        .map(|entry| {
833            let entry = items(entry, "a polynomial update's term")?;
834            if entry.len() != width + 1 {
835                return Err("a polynomial update's term names an exponent for every state variable".to_string());
836            }
837            let exponents = entry[1..]
838                .iter()
839                .map(|e| match e.shape() {
840                    Shape::Int(k) => u32::try_from(k).map_err(|_| "an exponent is a small natural number".to_string()),
841                    _ => Err("an exponent is a small natural number".to_string()),
842                })
843                .collect::<Result<Vec<u32>, String>>()?;
844            Ok(Term {
845                coefficient: entry[0].clone(),
846                exponents,
847            })
848        })
849        .collect()
850}
851
852/// The update `field` encodes, one list of `[coefficient, e₁, …, eₖ]` entries per state variable.
853fn update_terms<V: ValueAlgebra>(field: &V, width: usize) -> Result<Vec<Vec<Term<V>>>, String> {
854    let components = items(field, "a polynomial update")?;
855    if components.len() != width {
856        return Err(format!(
857            "a polynomial update has {} components for a state of {width}",
858            components.len()
859        ));
860    }
861    components.iter().map(|component| poly_terms(component, width)).collect()
862}
863
864/// A polynomial's value at `x`, in the value model's own arithmetic.
865fn eval_terms<V: ValueAlgebra>(terms: &[Term<V>], x: &[V]) -> Result<V, String> {
866    let mut total = int(0);
867    for term in terms {
868        let mut value = term.coefficient.clone();
869        for (v, e) in x.iter().zip(&term.exponents) {
870            for _ in 0..*e {
871                value = V::multiply(value, v.clone())?;
872            }
873        }
874        total = V::add(total, value)?;
875    }
876    Ok(total)
877}
878
879/// One pass of a polynomial update.
880fn apply_update<V: ValueAlgebra>(terms: &[Vec<Term<V>>], x: &[V]) -> Result<Vec<V>, String> {
881    terms.iter().map(|component| eval_terms(component, x)).collect()
882}
883
884/// `N` passes of `x ← x + dt·f(x) + O(dt²)`, `f` polynomial with rational coefficients. A finite
885/// `N` runs the update itself. An infinite one, `N = T/dt + δ` on each residue class, ends
886/// infinitely close to the solution of `x' = f(x)` from the start's standard part at time `T`, a
887/// germ known by its standard part; a scalar solution certified to leave every bound before `T`
888/// leaves an unlimited germ.
889pub fn polynomial_flow<V: ValueAlgebra>(field: &V, vector: &V, n: &V) -> Result<V, String> {
890    let x = items(vector, "a supertask's state")?;
891    let terms = update_terms(field, x.len())?;
892    let natural = match n.shape() {
893        Shape::Int(k) if k >= 0 => {
894            let mut state = x;
895            for _ in 0..k {
896                state = apply_update(&terms, &state)?;
897            }
898            return Ok(V::list(state));
899        }
900        _ => infinite_count(n)?,
901    };
902    let width = x.len();
903    on_one_infinitesimal(terms.iter().flatten().map(|t| &t.coefficient), &natural.scale, "update", "a nonlinear flow is integrated over rates that are standard numbers")?;
904    on_one_infinitesimal(x.iter(), &natural.scale, "state", "a nonlinear flow is integrated from a start whose standard part is a number")?;
905    let mut components = Vec::with_capacity(width);
906    for (i, component) in terms.iter().enumerate() {
907        let mut monomials = Vec::new();
908        let mut identity = false;
909        for term in component {
910            let (q, series) = dt_polynomial(&term.coefficient, &natural.scale).ok_or_else(|| {
911                format!(
912                    "the update's coefficient {} is not a polynomial in the infinitesimal, or in a root of it, with rational coefficients, so this supertask is not a flow the engine integrates",
913                    term.coefficient.display_string()
914                )
915            })?;
916            if below_dt(q, &series) {
917                return Err(format!(
918                    "the update's coefficient {} has a term of a fractional order of the infinitesimal below its first, so this supertask is not a flow",
919                    term.coefficient.display_string()
920                ));
921            }
922            let at_zero = series.first().cloned().unwrap_or_else(Rational::zero);
923            let own = term.exponents.iter().enumerate().all(|(j, e)| *e == u32::from(j == i));
924            if own && at_zero == Rational::one() {
925                identity = true;
926            } else if !at_zero.is_zero() {
927                return Err(
928                    "the update changes its state by a standard amount at every step as well as an infinitesimal one, so this supertask is neither a cycle nor a flow"
929                        .to_string(),
930                );
931            }
932            let rate = series.get(q as usize).cloned().unwrap_or_else(Rational::zero);
933            if !rate.is_zero() {
934                monomials.push(Monomial {
935                    coefficient: rate,
936                    powers: term.exponents.clone(),
937                });
938            }
939        }
940        if !identity {
941            return Err(
942                "the update does not keep its state when the infinitesimal is zero, so this supertask is not a flow"
943                    .to_string(),
944            );
945        }
946        components.push(monomials);
947    }
948    let ode = OdeField { components };
949    let scale = &natural.scale;
950    let ty = scale.residues.clone();
951    let counts = natural.value.depends();
952    let l = classes([counts.modulus(), ty.modulus()].into_iter().chain(state_moduli(&x)))
953        .ok_or_else(|| "a flow's step count and state split over more residue classes than one value holds".to_string())?;
954    let mut columns: Vec<Vec<Option<Branch<V>>>> = vec![Vec::with_capacity(l as usize); width];
955    for c in 0..l {
956        let Some(count) = counts.at(c) else {
957            columns.iter_mut().for_each(|col| col.push(None));
958            continue;
959        };
960        if count.degree().unwrap_or(0) != 1 {
961            return Err(
962                "a flow's step count is a multiple of 1/dt; one that grows faster or slower has no standard end state here"
963                    .to_string(),
964            );
965        }
966        let t = count.coeff(1);
967        if !t.is_positive() {
968            return Err("a flow runs forward a positive time".to_string());
969        }
970        let start = x
971            .iter()
972            .map(|v| {
973                let at = at_class(v, c);
974                tower::germ_of(&at)
975                    .and_then(|g| g.standard_part().ok().flatten())
976                    .ok_or_else(|| {
977                        format!("a flow starts from limited numbers, not {}", at.display_string())
978                    })
979            })
980            .collect::<Result<Vec<ExactReal>, String>>()?;
981        let ends = flow_end(&ode, &start, &t)?;
982        for (column, end) in columns.iter_mut().zip(ends) {
983            column.push(Some(Branch(tower::from_germ(end, scale))));
984        }
985    }
986    Ok(V::list(
987        columns
988            .into_iter()
989            .map(|raw| {
990                let d = Depends::from_branches(ty.clone(), l, raw).expect("the classes are the type's own");
991                tower::from_depends(d, scale)
992            })
993            .collect(),
994    ))
995}
996
997/// The germs a flow of `x' = f(x)` from `start` leaves at time `t`: each known by its standard
998/// part, or, for a scalar solution certified to blow up before `t`, unlimited with its sign.
999fn flow_end(ode: &OdeField, start: &[ExactReal], t: &Rational) -> Result<Vec<Germ>, String> {
1000    let ends = ExactReal::flow(ode, start, t)
1001        .ok_or_else(|| "a flow's update and state disagree in shape".to_string())?;
1002    match ends[0].enclosure(8) {
1003        Ok(_) => Ok(ends.iter().map(Germ::standard).collect()),
1004        Err(RealError::Domain(_)) => {
1005            let escape = |p: &Poly, x0: &Rational| {
1006                escape_time_bound(p, x0, 24).is_some_and(|bound| bound < *t)
1007            };
1008            let scalar = (ode.components.len() == 1).then(|| {
1009                let mut coefficients = Vec::new();
1010                for m in &ode.components[0] {
1011                    let k = m.powers[0] as usize;
1012                    if coefficients.len() <= k {
1013                        coefficients.resize(k + 1, Rational::zero());
1014                    }
1015                    coefficients[k] = coefficients[k].add(&m.coefficient);
1016                }
1017                Poly::from_coeffs(coefficients)
1018            });
1019            let x0 = start[0].exact_algebraic().and_then(|a| a.to_rational());
1020            match (scalar, x0) {
1021                (Some(p), Some(x0)) if escape(&p, &x0) => Ok(vec![Germ::unlimited(1)]),
1022                (Some(p), Some(x0)) if escape(&p.neg_var().neg(), &x0.negated()) => {
1023                    Ok(vec![Germ::unlimited(-1)])
1024                }
1025                _ => Err(format!(
1026                    "the solution of the flow leaves every bound before time {t}, and no certificate says it blows up there"
1027                )),
1028            }
1029        }
1030        Err(e) => Err(e.to_string()),
1031    }
1032}
1033
1034/// `Σ_j C(q, j)·Δʲ_r` on every residue class of the step count `N`, with `r = (N − z) mod p` and
1035/// `q = (N − z − r)/p` there.
1036fn evaluate_by_class<V: ValueAlgebra>(
1037    natural: &NaturalValue,
1038    z: u64,
1039    p: u64,
1040    differences: &[Vec<Vec<V>>],
1041    width: usize,
1042) -> Result<Vec<V>, String> {
1043    let scale = &natural.scale;
1044    let refuse = |e: ResidueError| e.describe(&scale.generator_text());
1045    let ty = scale.residues.clone();
1046    let shifted = natural
1047        .value
1048        .sub(&Hypernatural::from_integer(BigInt::from_u64(z), &ty))
1049        .map_err(refuse)?;
1050    let residues = shifted.rem(&BigInt::from_u64(p)).map_err(refuse)?;
1051    let l = classes([residues.modulus(), shifted.depends().modulus(), ty.modulus()])
1052        .ok_or_else(|| "a supertask's step count splits over more residue classes than one value holds".to_string())?;
1053    let mut columns: Vec<Vec<Option<Branch<V>>>> = vec![Vec::with_capacity(l as usize); width];
1054    for c in 0..l {
1055        let (Some(r), Some(poly)) = (residues.at(c), shifted.depends().at(c)) else {
1056            for column in &mut columns {
1057                column.push(None);
1058            }
1059            continue;
1060        };
1061        let r_value = r.to_u64().expect("a residue below p");
1062        let q = poly
1063            .sub(&Poly::constant(Rational::from_bigint(r.clone())))
1064            .scale(&Rational::new(BigInt::from_u64(1), BigInt::from_u64(p)).expect("p > 0"));
1065        let q: V = tower::from_hyper(Hyperreal::from_reciprocal_powers(q.coeffs()), scale);
1066        let deltas = &differences[r_value as usize];
1067        let mut binomial: V = int(1);
1068        let mut totals: Vec<V> = vec![int(0); width];
1069        for (j, delta) in deltas.iter().enumerate() {
1070            if j > 0 {
1071                let factor = V::subtract(q.clone(), int(j as i64 - 1))?;
1072                binomial = V::exact_divide(V::multiply(binomial, factor)?, int(j as i64))?;
1073            }
1074            for (total, d) in totals.iter_mut().zip(delta) {
1075                *total = V::add(total.clone(), V::multiply(binomial.clone(), d.clone())?)?;
1076            }
1077        }
1078        for (column, total) in columns.iter_mut().zip(totals) {
1079            column.push(Some(Branch(total)));
1080        }
1081    }
1082    Ok(columns
1083        .into_iter()
1084        .map(|raw| {
1085            let d = Depends::from_branches(ty.clone(), l, raw).expect("the classes are the type's own");
1086            tower::from_depends(d, scale)
1087        })
1088        .collect())
1089}
1090
1091// ── hybrid flows ─────────────────────────────────────────────────────────────────────────────
1092
1093/// A condition of a hybrid loop as the engine encodes it, its coefficients still the program's
1094/// values.
1095enum Encoded<V> {
1096    All(Vec<Encoded<V>>),
1097    Any(Vec<Encoded<V>>),
1098    Not(Box<Encoded<V>>),
1099    Compare(hybrid::Rel, Vec<Term<V>>),
1100    Infinitesimal(Vec<Term<V>>),
1101    Limited(Vec<Term<V>>),
1102    Truth(usize),
1103    Constant(bool),
1104}
1105
1106enum EncodedStmt<V> {
1107    Set(usize, Vec<Term<V>>),
1108    Put(usize, V),
1109    If(Encoded<V>, Vec<EncodedStmt<V>>, Vec<EncodedStmt<V>>),
1110}
1111
1112struct EncodedJump<V> {
1113    guard: Encoded<V>,
1114    block: Vec<EncodedStmt<V>>,
1115}
1116
1117/// A part of a hybrid loop's body: a run of flow statements as one polynomial update, or the
1118/// check of a jump.
1119enum EncodedItem<V> {
1120    Flow(Vec<Vec<Term<V>>>),
1121    Check(usize),
1122}
1123
1124/// A list's kind, named by the Text it starts with, and the rest of its items.
1125fn tagged<V: ValueAlgebra>(v: &V, what: &str) -> Result<(String, Vec<V>), String> {
1126    let mut parts = items(v, what)?;
1127    if parts.is_empty() {
1128        return Err(format!("{what} is empty"));
1129    }
1130    let head = parts.remove(0);
1131    let tag = head
1132        .text()
1133        .ok_or_else(|| format!("{what} starts with its kind, named by Text"))?
1134        .to_string();
1135    Ok((tag, parts))
1136}
1137
1138fn slot_of<V: ValueAlgebra>(v: &V, width: usize) -> Result<usize, String> {
1139    match v.shape() {
1140        Shape::Int(k) if k >= 0 && (k as usize) < width => Ok(k as usize),
1141        _ => Err(format!(
1142            "a hybrid loop names a value by its place among its {width}, got {}",
1143            v.display_string()
1144        )),
1145    }
1146}
1147
1148fn encoded_cond<V: ValueAlgebra>(v: &V, width: usize) -> Result<Encoded<V>, String> {
1149    let (tag, parts) = tagged(v, "a hybrid loop's condition")?;
1150    let many = |parts: &[V]| -> Result<Vec<Encoded<V>>, String> { parts.iter().map(|p| encoded_cond(p, width)).collect() };
1151    fn one<'p, V>(parts: &'p [V], tag: &str) -> Result<&'p V, String> {
1152        match parts {
1153            [p] => Ok(p),
1154            _ => Err(format!("a hybrid loop's `{tag}` condition has one operand")),
1155        }
1156    }
1157    Ok(match tag.as_str() {
1158        "all" => Encoded::All(many(&parts)?),
1159        "any" => Encoded::Any(many(&parts)?),
1160        "not" => Encoded::Not(Box::new(encoded_cond(one(&parts, &tag)?, width)?)),
1161        "compare" => match parts.as_slice() {
1162            [rel, poly] => {
1163                let rel = rel
1164                    .text()
1165                    .and_then(hybrid::Rel::named)
1166                    .ok_or_else(|| "a hybrid loop's comparison names its relation".to_string())?;
1167                Encoded::Compare(rel, poly_terms(poly, width)?)
1168            }
1169            _ => return Err("a hybrid loop's comparison is a relation and a polynomial".to_string()),
1170        },
1171        "infinitesimal" => Encoded::Infinitesimal(poly_terms(one(&parts, &tag)?, width)?),
1172        "limited" => Encoded::Limited(poly_terms(one(&parts, &tag)?, width)?),
1173        "truth" => Encoded::Truth(slot_of(one(&parts, &tag)?, width)?),
1174        "constant" => Encoded::Constant(truth_of(one(&parts, &tag)?)?),
1175        other => return Err(format!("a hybrid loop has no condition of kind `{other}`")),
1176    })
1177}
1178
1179fn encoded_block<V: ValueAlgebra>(v: &V, width: usize) -> Result<Vec<EncodedStmt<V>>, String> {
1180    items(v, "a hybrid loop's jump")?
1181        .iter()
1182        .map(|stmt| {
1183            let (tag, parts) = tagged(stmt, "a hybrid loop's statement")?;
1184            Ok(match (tag.as_str(), parts.as_slice()) {
1185                ("set", [slot, poly]) => EncodedStmt::Set(slot_of(slot, width)?, poly_terms(poly, width)?),
1186                ("put", [slot, value]) => EncodedStmt::Put(slot_of(slot, width)?, value.clone()),
1187                ("if", [cond, then, otherwise]) => EncodedStmt::If(
1188                    encoded_cond(cond, width)?,
1189                    encoded_block(then, width)?,
1190                    encoded_block(otherwise, width)?,
1191                ),
1192                _ => return Err(format!("a hybrid loop has no statement of kind `{tag}` with {} parts", parts.len())),
1193            })
1194        })
1195        .collect()
1196}
1197
1198fn encoded_jumps<V: ValueAlgebra>(v: &V, width: usize) -> Result<Vec<EncodedJump<V>>, String> {
1199    items(v, "a hybrid loop's jumps")?
1200        .iter()
1201        .map(|jump| match items(jump, "a hybrid loop's jump")?.as_slice() {
1202            [guard, block] => Ok(EncodedJump {
1203                guard: encoded_cond(guard, width)?,
1204                block: encoded_block(block, width)?,
1205            }),
1206            _ => Err("a hybrid loop's jump is a guard and a block".to_string()),
1207        })
1208        .collect()
1209}
1210
1211fn encoded_schedule<V: ValueAlgebra>(v: &V, width: usize, jumps: usize) -> Result<Vec<EncodedItem<V>>, String> {
1212    let schedule = items(v, "a hybrid loop's body")?
1213        .iter()
1214        .map(|item| {
1215            let (tag, parts) = tagged(item, "a part of a hybrid loop's body")?;
1216            match (tag.as_str(), parts.as_slice()) {
1217                ("flow", [field]) => Ok(EncodedItem::Flow(update_terms(field, width)?)),
1218                ("check", [k]) => match k.shape() {
1219                    Shape::Int(k) if k >= 0 && (k as usize) < jumps => Ok(EncodedItem::Check(k as usize)),
1220                    _ => Err(format!("a hybrid loop checks one of its {jumps} jumps, got {}", k.display_string())),
1221                },
1222                _ => Err(format!("a part of a hybrid loop's body is a `flow` or a `check`, not `{tag}`")),
1223            }
1224        })
1225        .collect::<Result<Vec<_>, String>>()?;
1226    for k in 0..jumps {
1227        if schedule.iter().filter(|item| matches!(item, EncodedItem::Check(j) if *j == k)).count() != 1 {
1228            return Err("a hybrid loop's body checks each of its jumps once".to_string());
1229        }
1230    }
1231    Ok(schedule)
1232}
1233
1234fn truth_of<V: ValueAlgebra>(v: &V) -> Result<bool, String> {
1235    match v.shape() {
1236        Shape::Bool => Ok(v.values_equal(&V::boolean(true))),
1237        _ => Err(format!("a hybrid loop's condition reads a truth value, got {}", v.type_name())),
1238    }
1239}
1240
1241fn tower_rel(rel: hybrid::Rel) -> Rel {
1242    match rel {
1243        hybrid::Rel::Lt => Rel::Lt,
1244        hybrid::Rel::Le => Rel::LtEq,
1245        hybrid::Rel::Gt => Rel::Gt,
1246        hybrid::Rel::Ge => Rel::GtEq,
1247        hybrid::Rel::Eq => Rel::Eq,
1248        hybrid::Rel::Ne => Rel::NotEq,
1249    }
1250}
1251
1252/// One run of flow statements, a component that only keeps its own value keeping it as it is —
1253/// a truth value or text among the state is never multiplied by one.
1254fn literal_update<V: ValueAlgebra>(field: &[Vec<Term<V>>], x: &[V]) -> Result<Vec<V>, String> {
1255    field
1256        .iter()
1257        .enumerate()
1258        .map(|(i, component)| match component.as_slice() {
1259            [only]
1260                if only.exponents.iter().enumerate().all(|(j, e)| *e == u32::from(j == i))
1261                    && only.coefficient.integer().is_some_and(|n| n == BigInt::from_i64(1)) =>
1262            {
1263                Ok(x[i].clone())
1264            }
1265            _ => eval_terms(component, x),
1266        })
1267        .collect()
1268}
1269
1270fn literal_cond<V: ValueAlgebra>(c: &Encoded<V>, x: &[V]) -> Result<bool, String> {
1271    Ok(match c {
1272        Encoded::All(cs) => {
1273            for c in cs {
1274                if !literal_cond(c, x)? {
1275                    return Ok(false);
1276                }
1277            }
1278            true
1279        }
1280        Encoded::Any(cs) => {
1281            for c in cs {
1282                if literal_cond(c, x)? {
1283                    return Ok(true);
1284                }
1285            }
1286            false
1287        }
1288        Encoded::Not(c) => !literal_cond(c, x)?,
1289        Encoded::Compare(rel, p) => V::holds(tower_rel(*rel), &eval_terms(p, x)?, &int(0))?,
1290        Encoded::Infinitesimal(p) => truth_of(&tower::is_infinitesimal(&eval_terms(p, x)?)?)?,
1291        Encoded::Limited(p) => truth_of(&tower::is_limited(&eval_terms(p, x)?)?)?,
1292        Encoded::Truth(slot) => truth_of(&x[*slot])?,
1293        Encoded::Constant(b) => *b,
1294    })
1295}
1296
1297fn literal_exec<V: ValueAlgebra>(stmts: &[EncodedStmt<V>], x: &mut [V]) -> Result<(), String> {
1298    for s in stmts {
1299        match s {
1300            EncodedStmt::Set(slot, p) => x[*slot] = eval_terms(p, x)?,
1301            EncodedStmt::Put(slot, v) => x[*slot] = v.clone(),
1302            EncodedStmt::If(c, then, otherwise) => {
1303                if literal_cond(c, x)? {
1304                    literal_exec(then, x)?;
1305                } else {
1306                    literal_exec(otherwise, x)?;
1307                }
1308            }
1309        }
1310    }
1311    Ok(())
1312}
1313
1314/// The standard part of a coefficient or a value as an algebraic number, and whether it is that
1315/// number exactly.
1316fn standard_value<V: ValueAlgebra>(v: &V) -> Result<(Algebraic, bool), String> {
1317    if let Some(r) = v.standard_rational() {
1318        return Ok((Algebraic::from_rational(r), true));
1319    }
1320    if let Some(ExactRef::Algebraic(a)) = v.exact() {
1321        return Ok((a.clone(), true));
1322    }
1323    let germ = tower::germ_of(v)
1324        .ok_or_else(|| format!("a hybrid loop computes with numbers, not {}", v.type_name()))?;
1325    let standard = germ
1326        .standard_part()
1327        .map_err(|e| e.to_string())?
1328        .ok_or_else(|| format!("a hybrid loop starts from limited numbers, not {}", v.display_string()))?;
1329    let a = standard.exact_algebraic().ok_or_else(|| {
1330        format!(
1331            "{} has no algebraic standard part, so the times a hybrid loop's jumps fire at are not found exactly",
1332            v.display_string()
1333        )
1334    })?;
1335    let exact = is_standard(v) || germ.as_hyperreal().is_some_and(|h| h.as_standard().is_some());
1336    Ok((a, exact))
1337}
1338
1339/// The coefficients of `dt⁰` and `dt¹` in a coefficient of an Euler step, which has no term of a
1340/// fractional order between them.
1341fn step_coefficients<V: ValueAlgebra>(v: &V) -> Result<(Algebraic, Algebraic), String> {
1342    if let Some(r) = v.standard_rational() {
1343        return Ok((Algebraic::from_rational(r), Algebraic::from_i64(0)));
1344    }
1345    let germ = tower::germ_of(v)
1346        .ok_or_else(|| format!("a hybrid loop's flow computes with numbers, not {}", v.type_name()))?;
1347    let order = |k: i64| -> Result<Algebraic, String> {
1348        germ.coefficient(k)
1349            .map_err(|e| e.to_string())?
1350            .exact_algebraic()
1351            .ok_or_else(|| format!("the flow's coefficient {} is not algebraic", v.display_string()))
1352    };
1353    if germ.floor() < 0 && (germ.floor()..0).any(|k| order(k).map_or(true, |c| !c.is_zero())) {
1354        return Err(format!("the flow's coefficient {} is not limited", v.display_string()));
1355    }
1356    let dt = i64::from(germ.ramification());
1357    if (1..dt).any(|k| order(k).map_or(true, |c| !c.is_zero())) {
1358        return Err(format!(
1359            "the flow's coefficient {} has a term of a fractional order of the infinitesimal below its first, so the loop is not an Euler step",
1360            v.display_string()
1361        ));
1362    }
1363    Ok((order(0)?, order(dt)?))
1364}
1365
1366fn ideal_poly<V: ValueAlgebra>(terms: &[Term<V>], width: usize, class: u64) -> Result<hybrid::Term, String> {
1367    let mut out = Vec::with_capacity(terms.len());
1368    let mut exact = true;
1369    for t in terms {
1370        let (a, is_exact) = standard_value(&at_class(&t.coefficient, class))?;
1371        exact &= is_exact;
1372        out.push((t.exponents.clone(), a));
1373    }
1374    Ok(hybrid::Term {
1375        poly: hybrid::MPoly::from_terms(width, out),
1376        exact,
1377    })
1378}
1379
1380fn ideal_cond<V: ValueAlgebra>(c: &Encoded<V>, width: usize, class: u64) -> Result<hybrid::Cond, String> {
1381    Ok(match c {
1382        Encoded::All(cs) => hybrid::Cond::All(cs.iter().map(|c| ideal_cond(c, width, class)).collect::<Result<_, _>>()?),
1383        Encoded::Any(cs) => hybrid::Cond::Any(cs.iter().map(|c| ideal_cond(c, width, class)).collect::<Result<_, _>>()?),
1384        Encoded::Not(c) => hybrid::Cond::Not(Box::new(ideal_cond(c, width, class)?)),
1385        Encoded::Compare(rel, p) => hybrid::Cond::Compare(*rel, ideal_poly(p, width, class)?),
1386        Encoded::Infinitesimal(p) => hybrid::Cond::Infinitesimal(ideal_poly(p, width, class)?),
1387        Encoded::Limited(p) => hybrid::Cond::Limited(ideal_poly(p, width, class)?),
1388        Encoded::Truth(slot) => hybrid::Cond::Truth(*slot),
1389        Encoded::Constant(b) => hybrid::Cond::Constant(*b),
1390    })
1391}
1392
1393/// A value a slot holds, as the ideal run holds it; a value no polynomial reads is kept in
1394/// `others` and named by its place there.
1395fn ideal_cell<V: ValueAlgebra>(v: &V, others: &mut Vec<V>) -> Result<hybrid::Cell, String> {
1396    if matches!(v.shape(), Shape::Bool) {
1397        return Ok(hybrid::Cell::Bool(truth_of(v)?));
1398    }
1399    if is_number(v) {
1400        let (value, exact) = standard_value(v)?;
1401        return Ok(hybrid::Cell::Num { value, exact });
1402    }
1403    others.push(v.clone());
1404    Ok(hybrid::Cell::Other(others.len() - 1))
1405}
1406
1407fn ideal_block<V: ValueAlgebra>(
1408    stmts: &[EncodedStmt<V>],
1409    width: usize,
1410    class: u64,
1411    others: &mut Vec<V>,
1412) -> Result<Vec<hybrid::Stmt>, String> {
1413    stmts
1414        .iter()
1415        .map(|s| {
1416            Ok(match s {
1417                EncodedStmt::Set(slot, p) => hybrid::Stmt::Set {
1418                    slot: *slot,
1419                    value: hybrid::Value::Poly(ideal_poly(p, width, class)?),
1420                },
1421                EncodedStmt::Put(slot, v) => hybrid::Stmt::Set {
1422                    slot: *slot,
1423                    value: match ideal_cell(&at_class(v, class), others)? {
1424                        hybrid::Cell::Num { value, exact } => hybrid::Value::Poly(hybrid::Term {
1425                            poly: hybrid::MPoly::constant(width, value),
1426                            exact,
1427                        }),
1428                        cell => hybrid::Value::Fixed(cell),
1429                    },
1430                },
1431                EncodedStmt::If(c, then, otherwise) => hybrid::Stmt::If {
1432                    cond: ideal_cond(c, width, class)?,
1433                    then: ideal_block(then, width, class, others)?,
1434                    otherwise: ideal_block(otherwise, width, class, others)?,
1435                },
1436            })
1437        })
1438        .collect()
1439}
1440
1441/// The rates and the exactly unchanged slots of a run of flow statements, read on residue class
1442/// `class`: each component keeps its own value at `dt = 0`, and moves by the coefficient of `dt`.
1443fn segment<V: ValueAlgebra>(field: &[Vec<Term<V>>], class: u64) -> Result<(Vec<hybrid::MPoly>, Vec<bool>), String> {
1444    let width = field.len();
1445    let mut rates = Vec::with_capacity(width);
1446    let mut preserved = Vec::with_capacity(width);
1447    for (i, component) in field.iter().enumerate() {
1448        let own = |t: &Term<V>| t.exponents.iter().enumerate().all(|(j, e)| *e == u32::from(j == i));
1449        let mut terms = Vec::new();
1450        let mut identity = false;
1451        for term in component {
1452            let (at_zero, rate) = step_coefficients(&at_class(&term.coefficient, class))?;
1453            if own(term) && at_zero == Algebraic::from_i64(1) {
1454                identity = true;
1455            } else if !at_zero.is_zero() {
1456                return Err(
1457                    "a hybrid loop's flow changes a value by a standard amount at every step, so it is not an Euler step"
1458                        .to_string(),
1459                );
1460            }
1461            if !rate.is_zero() {
1462                terms.push((term.exponents.clone(), rate));
1463            }
1464        }
1465        if !identity {
1466            return Err("a hybrid loop's flow does not keep its state when the infinitesimal is zero".to_string());
1467        }
1468        preserved.push(match component.as_slice() {
1469            [only] => {
1470                let c = at_class(&only.coefficient, class);
1471                own(only) && is_standard(&c) && as_rational(&c) == Some(Rational::one())
1472            }
1473            _ => false,
1474        });
1475        rates.push(hybrid::MPoly::from_terms(width, terms));
1476    }
1477    Ok((rates, preserved))
1478}
1479
1480/// The slots a floating-point value reaches: those that start as one, and those a statement sets
1481/// from a floating-point coefficient or from a slot it reaches.
1482fn float_reach<V: ValueAlgebra>(jumps: &[EncodedJump<V>], start: &[V]) -> Vec<bool> {
1483    fn walk<V: ValueAlgebra>(stmts: &[EncodedStmt<V>], reach: &mut [bool]) -> bool {
1484        let mut grown = false;
1485        for s in stmts {
1486            match s {
1487                EncodedStmt::Set(slot, p) => {
1488                    let floats = p.iter().any(|t| {
1489                        matches!(t.coefficient.shape(), Shape::Float)
1490                            || t.exponents.iter().enumerate().any(|(i, e)| *e > 0 && reach[i])
1491                    });
1492                    if floats && !reach[*slot] {
1493                        reach[*slot] = true;
1494                        grown = true;
1495                    }
1496                }
1497                EncodedStmt::Put(slot, v) => {
1498                    if matches!(v.shape(), Shape::Float) && !reach[*slot] {
1499                        reach[*slot] = true;
1500                        grown = true;
1501                    }
1502                }
1503                EncodedStmt::If(_, then, otherwise) => {
1504                    grown |= walk(then, reach);
1505                    grown |= walk(otherwise, reach);
1506                }
1507            }
1508        }
1509        grown
1510    }
1511    let mut reach: Vec<bool> = start.iter().map(|v| matches!(v.shape(), Shape::Float)).collect();
1512    while jumps.iter().any(|j| walk(&j.block, &mut reach)) {}
1513    reach
1514}
1515
1516/// `N` steps of a loop whose body is polynomial Euler steps and jumps, as `model` encodes them:
1517/// `[body, jumps]`, the body its parts in order — `["flow", field]` a run of flow statements as one
1518/// polynomial update per slot, as for [`polynomial_flow`], `["check", k]` the check of jump `k` —
1519/// and each jump `[guard, block]`. A finite `N` runs the steps themselves. An infinite one,
1520/// `N = T/dt + δ` on each residue class, ends on the values of the ideal hybrid system the loop
1521/// follows for time `T` (see [`crate::hybrid`]): a value the flow moves is a germ known by its
1522/// standard part, a value the loop holds exactly is that value.
1523pub fn hybrid_flow<V: ValueAlgebra>(model: &V, vector: &V, n: &V) -> Result<V, String> {
1524    let x = items(vector, "a hybrid loop's state")?;
1525    let width = x.len();
1526    let parts = items(model, "a hybrid loop's model")?;
1527    let [body, jumps] = parts.as_slice() else {
1528        return Err("a hybrid loop's model is its body and its jumps".to_string());
1529    };
1530    let jumps = encoded_jumps(jumps, width)?;
1531    let schedule = encoded_schedule(body, width, jumps.len())?;
1532    let natural = match n.shape() {
1533        Shape::Int(k) if k >= 0 => {
1534            let mut state = x;
1535            for _ in 0..k {
1536                for item in &schedule {
1537                    match item {
1538                        EncodedItem::Flow(field) => state = literal_update(field, &state)?,
1539                        EncodedItem::Check(j) => {
1540                            if literal_cond(&jumps[*j].guard, &state)? {
1541                                literal_exec(&jumps[*j].block, &mut state)?;
1542                            }
1543                        }
1544                    }
1545                }
1546            }
1547            return Ok(V::list(state));
1548        }
1549        _ => infinite_count(n)?,
1550    };
1551    on_one_infinitesimal([model, vector].into_iter(), &natural.scale, "loop", "a hybrid loop's rates and jumps are standard numbers")?;
1552    let floats = float_reach(&jumps, &x);
1553    let scale = &natural.scale;
1554    let ty = scale.residues.clone();
1555    let counts = natural.value.depends();
1556    let coefficient_moduli: Vec<V> = schedule
1557        .iter()
1558        .flat_map(|item| match item {
1559            EncodedItem::Flow(field) => field.iter().flatten().map(|t| t.coefficient.clone()).collect(),
1560            EncodedItem::Check(_) => Vec::new(),
1561        })
1562        .collect();
1563    let l = classes(
1564        [counts.modulus(), ty.modulus()]
1565            .into_iter()
1566            .chain(state_moduli(&x))
1567            .chain(state_moduli(&coefficient_moduli)),
1568    )
1569    .ok_or_else(|| "a hybrid loop's step count and state split over more residue classes than one value holds".to_string())?;
1570    let mut columns: Vec<Vec<Option<Branch<V>>>> = vec![Vec::with_capacity(l as usize); width];
1571    for c in 0..l {
1572        let Some(count) = counts.at(c) else {
1573            columns.iter_mut().for_each(|col| col.push(None));
1574            continue;
1575        };
1576        if count.degree().unwrap_or(0) != 1 {
1577            return Err(
1578                "a hybrid loop's step count is a multiple of 1/dt; one that grows faster or slower has no standard end state here"
1579                    .to_string(),
1580            );
1581        }
1582        let span = count.coeff(1);
1583        if !span.is_positive() {
1584            return Err("a hybrid loop runs forward a positive time".to_string());
1585        }
1586        let items = schedule
1587            .iter()
1588            .map(|item| {
1589                Ok(match item {
1590                    EncodedItem::Flow(field) => {
1591                        let (rates, preserved) = segment(field, c)?;
1592                        hybrid::Item::Flow { rates, preserved }
1593                    }
1594                    EncodedItem::Check(k) => hybrid::Item::Check(*k),
1595                })
1596            })
1597            .collect::<Result<Vec<_>, String>>()?;
1598        let mut others = Vec::new();
1599        let start = x
1600            .iter()
1601            .map(|v| ideal_cell(&at_class(v, c), &mut others))
1602            .collect::<Result<Vec<_>, String>>()?;
1603        let ideal_jumps = jumps
1604            .iter()
1605            .map(|j| {
1606                Ok(hybrid::Jump {
1607                    guard: ideal_cond(&j.guard, width, c)?,
1608                    block: ideal_block(&j.block, width, c, &mut others)?,
1609                })
1610            })
1611            .collect::<Result<Vec<_>, String>>()?;
1612        let model = hybrid::Model::new(width, items, ideal_jumps);
1613        let end = hybrid::run(&model, start.clone(), &Algebraic::from_rational(span))?;
1614        for (i, (column, cell)) in columns.iter_mut().zip(end).enumerate() {
1615            let value = match cell {
1616                hybrid::Cell::Num { value, exact: true } => {
1617                    if floats[i] {
1618                        match &start[i] {
1619                            hybrid::Cell::Num { value: was, .. } if *was == value => at_class(&x[i], c),
1620                            _ => {
1621                                return Err(
1622                                    "a hybrid loop computes exactly, and a value its jumps set is a floating-point number, whose arithmetic rounds; declare it Rational or Real"
1623                                        .to_string(),
1624                                )
1625                            }
1626                        }
1627                    } else {
1628                        tower::from_algebraic(value)
1629                    }
1630                }
1631                hybrid::Cell::Num { value, exact: false } => {
1632                    tower::from_germ(Germ::standard(&ExactReal::from_algebraic(value)), scale)
1633                }
1634                hybrid::Cell::Bool(b) => V::boolean(b),
1635                hybrid::Cell::Other(k) => others[k].clone(),
1636            };
1637            column.push(Some(Branch(value)));
1638        }
1639    }
1640    Ok(V::list(
1641        columns
1642            .into_iter()
1643            .map(|raw| {
1644                let d = Depends::from_branches(ty.clone(), l, raw).expect("the classes are the type's own");
1645                tower::from_depends(d, scale)
1646            })
1647            .collect(),
1648    ))
1649}
1650
1651// ── cycles ────────────────────────────────────────────────────────────────────────────────────
1652
1653/// The state at step `n` of a run recorded as `states` (the state before each step) up to the step
1654/// whose state, `repeated`, had been seen before: the run is eventually periodic from the first
1655/// occurrence of `repeated`.
1656pub fn cycle_at<V: ValueAlgebra>(states: &V, repeated: &V, n: &V) -> Result<V, String> {
1657    let states = items(states, "a supertask's recorded states")?;
1658    let mu = states
1659        .iter()
1660        .position(|s| s.values_equal(repeated))
1661        .ok_or_else(|| "the supertask engine's repeated state is not among the recorded ones".to_string())?;
1662    let lambda = (states.len() - mu) as u64;
1663    let state_at = |i: usize| items(&states[i], "a recorded state");
1664    if let Shape::Int(k) = n.shape() {
1665        if k >= 0 {
1666            let k = k as u64;
1667            let i = if k < states.len() as u64 {
1668                k
1669            } else {
1670                mu as u64 + (k - mu as u64) % lambda
1671            };
1672            return Ok(V::list(state_at(i as usize)?));
1673        }
1674    }
1675    if several_count(n) {
1676        let residue = V::modulo(V::subtract(n.clone(), int(mu as i64))?, int(lambda as i64))?;
1677        let width = state_at(mu)?.len();
1678        return Ok(V::list(
1679            (0..width)
1680                .map(|i| on_residue(&residue, &|r| Ok(state_at(mu + r as usize)?[i].clone())))
1681                .collect::<Result<_, _>>()?,
1682        ));
1683    }
1684    let nv = infinite_count(n)?;
1685    let scale = &nv.scale;
1686    let refuse = |e: ResidueError| e.describe(&scale.generator_text());
1687    let ty = scale.residues.clone();
1688    let shifted = nv
1689        .value
1690        .sub(&Hypernatural::from_integer(BigInt::from_u64(mu as u64), &ty))
1691        .map_err(refuse)?;
1692    let residues = shifted.rem(&BigInt::from_u64(lambda)).map_err(refuse)?;
1693    let width = state_at(mu)?.len();
1694    let mut columns: Vec<Vec<Option<Branch<V>>>> = vec![Vec::new(); width];
1695    for c in 0..residues.modulus() {
1696        match residues.at(c) {
1697            None => columns.iter_mut().for_each(|col| col.push(None)),
1698            Some(r) => {
1699                let state = state_at(mu + r.to_u64().expect("a residue below λ") as usize)?;
1700                for (column, value) in columns.iter_mut().zip(state) {
1701                    column.push(Some(Branch(value)));
1702                }
1703            }
1704        }
1705    }
1706    Ok(V::list(
1707        columns
1708            .into_iter()
1709            .map(|raw| {
1710                let d = Depends::from_branches(ty.clone(), residues.modulus(), raw)
1711                    .expect("the classes are the type's own");
1712                tower::from_depends(d, scale)
1713            })
1714            .collect(),
1715    ))
1716}
1717
1718// ── state sizes ───────────────────────────────────────────────────────────────────────────────
1719
1720/// The bits a state occupies — a number by its magnitude, text by its length, a list or tuple by
1721/// its items, an exact value by its canonical encoding — the measure the cycle search stops a
1722/// growing state by.
1723pub fn state_size<V: ValueAlgebra>(v: &V) -> u64 {
1724    match v.shape() {
1725        Shape::Bool => 1,
1726        Shape::Int(_) | Shape::Float => 64,
1727        Shape::BigInt(b) => b.bit_len().max(64),
1728        Shape::Rational(r) => r.numerator().bit_len() + r.denominator().bit_len(),
1729        Shape::Text(len) => 8 * len as u64,
1730        Shape::List(values) | Shape::Tuple(values) => values.iter().map(state_size).sum(),
1731        Shape::Decimal | Shape::Other => match tower::exact_bytes(v) {
1732            Some((_, bytes)) => 8 * bytes.len() as u64,
1733            None => 8 * v.display_string().len() as u64,
1734        },
1735    }
1736}
1737
1738// ── first exits ───────────────────────────────────────────────────────────────────────────────
1739
1740/// A loop's guard as the supertask engine writes it: `and`, `or` and `not` over atoms linear in the
1741/// loop's counter `t`.
1742enum Guard<V> {
1743    /// `a·t + b <rel> 0`.
1744    Linear { rel: Rel, a: V, b: V },
1745    /// `(a·t + b) % m <rel> c`.
1746    Remainder { rel: Rel, a: V, b: V, m: V, c: V },
1747    And(Box<Guard<V>>, Box<Guard<V>>),
1748    Or(Box<Guard<V>>, Box<Guard<V>>),
1749    Not(Box<Guard<V>>),
1750}
1751
1752/// A guard's answer at one value of its counter: whether it holds, or the finer residue classes a
1753/// remainder needs before it can say.
1754enum Truth {
1755    Holds(bool),
1756    Finer(u64),
1757}
1758
1759fn relation_named(name: &str) -> Option<Rel> {
1760    Some(match name {
1761        "<" => Rel::Lt,
1762        "<=" => Rel::LtEq,
1763        ">" => Rel::Gt,
1764        ">=" => Rel::GtEq,
1765        "=" => Rel::Eq,
1766        "!=" => Rel::NotEq,
1767        _ => return None,
1768    })
1769}
1770
1771impl<V: ValueAlgebra> Guard<V> {
1772    /// The formula `[tag, …]` the engine writes: `["and", f, g]`, `["or", f, g]`, `["not", f]`,
1773    /// `[relation, a, b]` and `["%", relation, a, b, m, c]`.
1774    fn read(v: &V) -> Result<Guard<V>, String> {
1775        let malformed = || format!("a loop's guard is a formula the supertask engine writes, got {}", v.display_string());
1776        let parts = items(v, "a loop's guard")?;
1777        let tag = parts.first().and_then(|t| t.text().map(str::to_string));
1778        let sub = |i: usize| match parts.get(i) {
1779            Some(p) => Guard::read(p).map(Box::new),
1780            None => Err(malformed()),
1781        };
1782        let at = |i: usize| parts.get(i).cloned().ok_or_else(malformed);
1783        let relation = |v: V| v.text().and_then(relation_named).ok_or_else(malformed);
1784        match (tag.as_deref(), parts.len()) {
1785            (Some("and"), 3) => Ok(Guard::And(sub(1)?, sub(2)?)),
1786            (Some("or"), 3) => Ok(Guard::Or(sub(1)?, sub(2)?)),
1787            (Some("not"), 2) => Ok(Guard::Not(sub(1)?)),
1788            (Some("%"), 6) => Ok(Guard::Remainder {
1789                rel: relation(at(1)?)?,
1790                a: at(2)?,
1791                b: at(3)?,
1792                m: at(4)?,
1793                c: at(5)?,
1794            }),
1795            (Some(_), 3) => Ok(Guard::Linear {
1796                rel: relation(at(0)?)?,
1797                a: at(1)?,
1798                b: at(2)?,
1799            }),
1800            _ => Err(malformed()),
1801        }
1802    }
1803
1804    /// Every number the guard reads.
1805    fn numbers<'g>(&'g self, out: &mut Vec<&'g V>) {
1806        match self {
1807            Guard::Linear { a, b, .. } => out.extend([a, b]),
1808            Guard::Remainder { a, b, m, c, .. } => out.extend([a, b, m, c]),
1809            Guard::And(f, g) | Guard::Or(f, g) => {
1810                f.numbers(out);
1811                g.numbers(out);
1812            }
1813            Guard::Not(f) => f.numbers(out),
1814        }
1815    }
1816
1817    /// The linear terms and remainders the guard is built from.
1818    fn atoms<'g>(&'g self, out: &mut Vec<&'g Guard<V>>) {
1819        match self {
1820            Guard::Linear { .. } | Guard::Remainder { .. } => out.push(self),
1821            Guard::And(f, g) | Guard::Or(f, g) => {
1822                f.atoms(out);
1823                g.atoms(out);
1824            }
1825            Guard::Not(f) => f.atoms(out),
1826        }
1827    }
1828
1829    /// The same guard with every number passed through `f`.
1830    fn map(&self, f: &dyn Fn(&V) -> V) -> Guard<V> {
1831        match self {
1832            Guard::Linear { rel, a, b } => Guard::Linear {
1833                rel: *rel,
1834                a: f(a),
1835                b: f(b),
1836            },
1837            Guard::Remainder { rel, a, b, m, c } => Guard::Remainder {
1838                rel: *rel,
1839                a: f(a),
1840                b: f(b),
1841                m: f(m),
1842                c: f(c),
1843            },
1844            Guard::And(x, y) => Guard::And(Box::new(x.map(f)), Box::new(y.map(f))),
1845            Guard::Or(x, y) => Guard::Or(Box::new(x.map(f)), Box::new(y.map(f))),
1846            Guard::Not(x) => Guard::Not(Box::new(x.map(f))),
1847        }
1848    }
1849
1850    /// The guard at the counter `t`.
1851    fn at(&self, t: &V) -> Result<Truth, String> {
1852        let term = |a: &V, b: &V| V::add(V::multiply(a.clone(), t.clone())?, b.clone());
1853        let decided = |value: V, rel: Rel, against: &V| -> Result<Truth, String> {
1854            match dependence(&value) {
1855                1 => Ok(Truth::Holds(V::holds(rel, &value, against)?)),
1856                finer => Ok(Truth::Finer(finer)),
1857            }
1858        };
1859        let both = |f: &Guard<V>, g: &Guard<V>, and: bool| -> Result<Truth, String> {
1860            match f.at(t)? {
1861                Truth::Holds(x) if x != and => Ok(Truth::Holds(x)),
1862                Truth::Holds(_) => g.at(t),
1863                finer => Ok(finer),
1864            }
1865        };
1866        match self {
1867            Guard::Linear { rel, a, b } => decided(term(a, b)?, *rel, &int(0)),
1868            Guard::Remainder { rel, a, b, m, c } => decided(V::modulo(term(a, b)?, m.clone())?, *rel, c),
1869            Guard::And(f, g) => both(f, g, true),
1870            Guard::Or(f, g) => both(f, g, false),
1871            Guard::Not(f) => Ok(match f.at(t)? {
1872                Truth::Holds(x) => Truth::Holds(!x),
1873                finer => finer,
1874            }),
1875        }
1876    }
1877}
1878
1879/// The modulus a value's dependence on the residue of the generator needs; `1` for a value the
1880/// same on every class.
1881fn dependence<V: TowerValue>(v: &V) -> u64 {
1882    match v.exact() {
1883        Some(ExactRef::Natural(n)) if n.value.depends().single().is_none() => n.value.depends().modulus(),
1884        Some(ExactRef::Depends(d)) if d.value.single().is_none() => d.value.modulus(),
1885        _ => 1,
1886    }
1887}
1888
1889// ── residue classes ───────────────────────────────────────────────────────────────────────────
1890
1891/// Every infinitesimal `v` involves, its lists' items, a dependence's branches and a provisional
1892/// answer's value included, each once.
1893fn infinitesimals_in<V: ValueAlgebra>(v: &V, out: &mut Vec<Arc<Scale>>) {
1894    let push = |s: &Arc<Scale>, out: &mut Vec<Arc<Scale>>| {
1895        if !out.iter().any(|t| t.same_infinitesimal(s)) {
1896            out.push(s.clone());
1897        }
1898    };
1899    if let Shape::List(items) | Shape::Tuple(items) = v.shape() {
1900        for item in &items {
1901            infinitesimals_in(item, out);
1902        }
1903        return;
1904    }
1905    match v.exact() {
1906        Some(ExactRef::Hyper(h)) => match &h.value {
1907            HyperForm::Multi(m) | HyperForm::Integer(m) => m.scales().iter().for_each(|s| push(s, out)),
1908            HyperForm::Nested(g) => g.scales().iter().for_each(|s| push(s, out)),
1909            _ => push(&h.scale, out),
1910        },
1911        Some(ExactRef::Natural(n)) => push(&n.scale, out),
1912        Some(ExactRef::Depends(d)) => {
1913            push(&d.scale, out);
1914            for (_, b) in d.value.branches() {
1915                infinitesimals_in(&b.0, out);
1916            }
1917        }
1918        Some(ExactRef::Converging(c)) => infinitesimals_in(c.value(), out),
1919        _ => {}
1920    }
1921}
1922
1923/// The refusal of a supertask stepped by the infinitesimal `clock` whose `what` holds another, for
1924/// the reason `why`.
1925fn on_one_infinitesimal<'v, V: ValueAlgebra + 'v>(
1926    values: impl Iterator<Item = &'v V>,
1927    clock: &Scale,
1928    what: &str,
1929    why: &str,
1930) -> Result<(), String> {
1931    let mut found = Vec::new();
1932    for v in values {
1933        infinitesimals_in(v, &mut found);
1934    }
1935    match found.iter().find(|s| !s.same_infinitesimal(clock)) {
1936        Some(other) => Err(format!(
1937            "this supertask steps by {} and its {what} holds {}: {why}",
1938            clock.name, other.name
1939        )),
1940        None => Ok(()),
1941    }
1942}
1943
1944/// The one infinitesimal `values` involve, or the refusal when they involve two.
1945fn sole_infinitesimal<V: ValueAlgebra>(values: &[&V]) -> Result<Option<Arc<Scale>>, String> {
1946    let mut found = Vec::new();
1947    for v in values {
1948        infinitesimals_in(*v, &mut found);
1949    }
1950    match found.as_slice() {
1951        [] => Ok(None),
1952        [one] => Ok(Some(one.clone())),
1953        [a, b, ..] => Err(format!(
1954            "this supertask's values hold both {} and {}; a supertask over one infinitesimal whose values hold another is not resolved",
1955            a.name, b.name
1956        )),
1957    }
1958}
1959
1960/// Every generator whose residue `v` reads — a residue-dependent value's and an infinite integer's that
1961/// depends on the residue — its lists' items, a dependence's branches and a provisional answer's value
1962/// included, each once.
1963fn residue_generators<V: ValueAlgebra>(v: &V, out: &mut Vec<Arc<Scale>>) {
1964    let mut push = |s: &Arc<Scale>| {
1965        if !out.iter().any(|t| t.same_infinitesimal(s)) {
1966            out.push(s.clone());
1967        }
1968    };
1969    if let Shape::List(items) | Shape::Tuple(items) = v.shape() {
1970        for item in &items {
1971            residue_generators(item, out);
1972        }
1973        return;
1974    }
1975    match v.exact() {
1976        Some(ExactRef::Natural(n)) if n.value.depends().single().is_none() => push(&n.scale),
1977        Some(ExactRef::Depends(d)) => {
1978            push(&d.scale);
1979            for (_, b) in d.value.branches() {
1980                residue_generators(&b.0, out);
1981            }
1982        }
1983        Some(ExactRef::Converging(c)) => residue_generators(c.value(), out),
1984        _ => {}
1985    }
1986}
1987
1988/// The moduli of the residues of `scale`'s generator that `v` reads, its lists' items and a
1989/// dependence's branches included.
1990fn moduli_on<V: ValueAlgebra>(v: &V, scale: &Scale, out: &mut Vec<u64>) {
1991    if let Shape::List(items) | Shape::Tuple(items) = v.shape() {
1992        for item in &items {
1993            moduli_on(item, scale, out);
1994        }
1995        return;
1996    }
1997    match v.exact() {
1998        Some(ExactRef::Natural(n)) if n.scale.same_infinitesimal(scale) => out.push(dependence(v)),
1999        Some(ExactRef::Depends(d)) => {
2000            if d.scale.same_infinitesimal(scale) {
2001                out.push(dependence(v));
2002            }
2003            for (_, b) in d.value.branches() {
2004                moduli_on(&b.0, scale, out);
2005            }
2006        }
2007        Some(ExactRef::Converging(c)) => moduli_on(c.value(), scale, out),
2008        _ => {}
2009    }
2010}
2011
2012/// The residue-dependent values anywhere in `v`, its lists' items included.
2013fn dependences<V: ValueAlgebra>(v: &V, out: &mut Vec<V>) {
2014    match v.shape() {
2015        Shape::List(items) | Shape::Tuple(items) => {
2016            for item in &items {
2017                dependences(item, out);
2018            }
2019        }
2020        _ => {
2021            if matches!(v.exact(), Some(ExactRef::Depends(_))) {
2022                out.push(v.clone());
2023            }
2024        }
2025    }
2026}
2027
2028/// `v` on the class `h` of the generator, its lists' items included ([`on_class`]).
2029fn pinned<V: ValueAlgebra>(v: &V, h: u64, class: &Arc<Scale>) -> V {
2030    match v.shape() {
2031        Shape::List(items) => V::list(items.iter().map(|item| pinned(item, h, class)).collect()),
2032        _ => on_class(v, h, class),
2033    }
2034}
2035
2036/// An answer read on one class, back on the program's own scale: a hyperreal is a function of the
2037/// infinitesimal whatever its residue. An infinite integer stays the polynomial it is on its class,
2038/// which the classes' answers join into one ([`tower::from_depends`]).
2039fn rescaled<V: ValueAlgebra>(v: &V, scale: &Arc<Scale>) -> V {
2040    if let Shape::List(items) = v.shape() {
2041        return V::list(items.iter().map(|item| rescaled(item, scale)).collect());
2042    }
2043    match v.exact() {
2044        Some(ExactRef::Hyper(x)) if x.scale.same_infinitesimal(scale) && !matches!(x.value, HyperForm::Multi(_) | HyperForm::Integer(_) | HyperForm::Nested(_)) => V::hyper(HyperValue {
2045            value: x.value.clone(),
2046            scale: scale.clone(),
2047        }),
2048        _ => v.clone(),
2049    }
2050}
2051
2052/// The answers of the classes modulo `modulus` as one value: lists of one width item by item, any
2053/// other answer the residue-dependent value its classes make (one value where they agree).
2054fn joined<V: ValueAlgebra>(scale: &Arc<Scale>, modulus: u64, answers: Vec<Option<V>>) -> V {
2055    let lists: Option<Vec<Option<Vec<V>>>> = answers
2056        .iter()
2057        .map(|a| match a {
2058            None => Some(None),
2059            Some(v) => match v.shape() {
2060                Shape::List(items) => Some(Some(items)),
2061                _ => None,
2062            },
2063        })
2064        .collect();
2065    if let Some(lists) = lists {
2066        let mut widths = lists.iter().flatten().map(Vec::len);
2067        if let Some(width) = widths.next().filter(|w| widths.all(|x| x == *w)) {
2068            return V::list(
2069                (0..width)
2070                    .map(|i| joined(scale, modulus, lists.iter().map(|l| l.as_ref().map(|l| l[i].clone())).collect()))
2071                    .collect(),
2072            );
2073        }
2074    }
2075    let branches = answers.into_iter().map(|a| a.map(Branch)).collect();
2076    let value = Depends::from_branches(scale.residues.clone(), modulus, branches).expect("the classes are the type's own");
2077    tower::from_depends(value, scale)
2078}
2079
2080/// `f` over `args` on each residue class of the generator that the residue-dependent values among
2081/// them read — every value pinned to its class — joined into one answer, which depends on the residue
2082/// only where the classes' answers differ; `None` when no argument holds a residue-dependent value.
2083/// A supertask whose every pass adds `0 when ⌈1/dt⌉ is even, 1 when it is odd` ends at `0` on the
2084/// even classes and `1/dt` on the odd ones.
2085///
2086/// Values that read the residues of several generators are read on the classes of one of them, an
2087/// infinite integer's own among them, and `f` reads the others within each class the same way; the
2088/// classes' answers join in the one form [`tower::from_depends`] gives them, a dependence on the
2089/// coarsest — the generator of the largest infinitesimal — whose branches depend on the finer.
2090fn on_each_class<V: ValueAlgebra>(args: &[&V], f: impl Fn(&[V]) -> Result<V, String>) -> Option<Result<V, String>> {
2091    let mut found = Vec::new();
2092    for arg in args {
2093        dependences(*arg, &mut found);
2094    }
2095    let mut generators = Vec::new();
2096    for arg in args {
2097        residue_generators(*arg, &mut generators);
2098    }
2099    for (i, x) in generators.iter().enumerate() {
2100        if let Some(y) = generators[i + 1..].iter().find(|y| x.order(y).is_none()) {
2101            return Some(Err(x.unordered(y)));
2102        }
2103    }
2104    let scale = match generators.as_slice() {
2105        [first, _, ..] => first.clone(),
2106        _ => scale_among(&found.iter().collect::<Vec<_>>())?,
2107    };
2108    let refuse = |e: ResidueError| e.describe(&scale.generator_text());
2109    let mut modulus = scale.residues.modulus();
2110    let mut moduli = Vec::new();
2111    for arg in args {
2112        moduli_on(*arg, &scale, &mut moduli);
2113    }
2114    for m in moduli {
2115        modulus = match bounded_lcm(modulus, m) {
2116            Ok(m) => m,
2117            Err(e) => return Some(Err(refuse(e))),
2118        };
2119    }
2120    let mut answers: Vec<Option<V>> = Vec::with_capacity(modulus as usize);
2121    for h in 0..modulus {
2122        if !scale.residues.admits(h) {
2123            answers.push(None);
2124            continue;
2125        }
2126        let class = Arc::new(scale.with_residues(
2127            scale
2128                .residues
2129                .and(&ResidueType::congruent(h, modulus))
2130                .expect("an admitted class is consistent with its type"),
2131        ));
2132        let on_class: Vec<V> = args.iter().map(|arg| pinned(*arg, h, &class)).collect();
2133        match f(&on_class) {
2134            Ok(answer) => answers.push(Some(rescaled(&answer, &scale))),
2135            Err(e) => return Some(Err(e)),
2136        }
2137    }
2138    Some(Ok(joined(&scale, modulus, answers)))
2139}
2140
2141/// The program's infinitesimal, as the first value on its scale carries it.
2142fn scale_among<V: TowerValue>(values: &[&V]) -> Option<Arc<Scale>> {
2143    values.iter().find_map(|v| match v.exact()? {
2144        ExactRef::Hyper(h) => Some(h.scale.clone()),
2145        ExactRef::Natural(n) => Some(n.scale.clone()),
2146        ExactRef::Depends(d) => Some(d.scale.clone()),
2147        _ => None,
2148    })
2149}
2150
2151/// `v` on the class of the generator `class` describes, `H ≡ h`: an infinite integer by its
2152/// polynomial there, a residue-dependent value by its branch there, and a hyperreal on the
2153/// class's own scale.
2154fn on_class<V: TowerValue>(v: &V, h: u64, class: &Arc<Scale>) -> V {
2155    match v.exact() {
2156        Some(ExactRef::Hyper(x)) if x.scale.same_infinitesimal(class) && !matches!(x.value, HyperForm::Multi(_) | HyperForm::Integer(_) | HyperForm::Nested(_)) => V::hyper(HyperValue {
2157            value: x.value.clone(),
2158            scale: class.clone(),
2159        }),
2160        Some(ExactRef::Natural(n)) if n.scale.same_infinitesimal(class) => {
2161            let p = n.value.depends().at(h).expect("an admitted class has a branch").clone();
2162            let value = Hypernatural::from_polynomial(p, &class.residues).expect("a branch is an integer on its class");
2163            tower::from_natural(value, class)
2164        }
2165        Some(ExactRef::Depends(d)) if d.scale.same_infinitesimal(class) => {
2166            on_class(&d.value.at(h).expect("an admitted class has a branch").0, h, class)
2167        }
2168        _ => v.clone(),
2169    }
2170}
2171
2172/// The polynomial in the generator an exit is on its own class.
2173fn class_polynomial<V: TowerValue>(v: &V) -> Option<Poly> {
2174    if let Some(n) = v.integer() {
2175        return Some(Poly::constant(Rational::from_bigint(n)));
2176    }
2177    match v.exact()? {
2178        ExactRef::Natural(n) => n.value.depends().single().cloned(),
2179        _ => None,
2180    }
2181}
2182
2183/// Where a guard first fails on one residue class of the generator.
2184enum Exit<V> {
2185    At(V),
2186    Never,
2187    /// A value the class does not pin down: the classes modulo this must be read apart first.
2188    Finer(u64),
2189}
2190
2191/// How many passes `While <guard>:` makes when each pass adds `step` to its counter, from `t0`: the
2192/// first natural `k` at which the guard fails at `t0 + k·step`. The guard compares terms linear in
2193/// the counter (the formula `Guard::read` takes), so its truth can change only where one of them
2194/// crosses zero and otherwise repeats with the period of its remainders; its first failure lies at
2195/// a crossing or within one period after one, and is found by evaluating the guard there alone.
2196/// Where those points depend on the residue of the generator — `⌊1/(2·dt)⌋` — each class is read on
2197/// its own scale and the count is the infinite integer the classes' answers make.
2198///
2199/// `−1` — not counted, so the loop runs as written — when every number is standard (the loop then
2200/// ends after finitely many passes, or never, exactly as it is written), when one is not a number,
2201/// and when a standard step never fails the guard; a refusal when an infinitesimal one never does.
2202pub fn first_exit<V: ValueAlgebra>(t0: &V, step: &V, guard: &V) -> Result<V, String> {
2203    let guard = Guard::read(guard)?;
2204    let mut numbers = vec![t0, step];
2205    guard.numbers(&mut numbers);
2206    let countable = |v: &&V| {
2207        v.is_standard_number()
2208            || matches!(
2209                v.exact(),
2210                Some(
2211                    ExactRef::Algebraic(_)
2212                        | ExactRef::Real(_)
2213                        | ExactRef::Hyper(_)
2214                        | ExactRef::Natural(_)
2215                        | ExactRef::Depends(_)
2216                )
2217            )
2218    };
2219    if !numbers.iter().all(countable) || numbers.iter().all(|v| is_standard(*v)) {
2220        return Ok(int(-1));
2221    }
2222    sole_infinitesimal(&numbers)?;
2223    let scale = scale_among(&numbers).expect("a value that is not standard has a scale");
2224    let refuse = |e: ResidueError| e.describe(&scale.generator_text());
2225    let mut modulus = scale.residues.modulus();
2226    for v in &numbers {
2227        modulus = bounded_lcm(modulus, dependence(*v)).map_err(refuse)?;
2228    }
2229    'refine: loop {
2230        let mut exits: Vec<Option<Exit<V>>> = Vec::with_capacity(modulus as usize);
2231        for h in 0..modulus {
2232            if !scale.residues.admits(h) {
2233                exits.push(None);
2234                continue;
2235            }
2236            let class = Arc::new(scale.with_residues(
2237                scale
2238                    .residues
2239                    .and(&ResidueType::congruent(h, modulus))
2240                    .expect("an admitted class is consistent with its type"),
2241            ));
2242            let pin = |v: &V| on_class(v, h, &class);
2243            let (t0, step) = (pin(t0), pin(step));
2244            match exit_on_class(&t0, &step, &guard.map(&pin))? {
2245                Exit::Finer(m) => {
2246                    let finer = bounded_lcm(modulus, m).map_err(refuse)?;
2247                    if finer == modulus {
2248                        return Err(format!(
2249                            "this loop's guard depends on the residue of {} past the classes that separate it",
2250                            scale.generator_text()
2251                        ));
2252                    }
2253                    modulus = finer;
2254                    continue 'refine;
2255                }
2256                exit => exits.push(Some(exit)),
2257            }
2258        }
2259        if exits.iter().flatten().any(|e| matches!(e, Exit::Never)) {
2260            let finite = exits.iter().flatten().all(|e| match e {
2261                Exit::At(k) => is_standard(k),
2262                _ => true,
2263            });
2264            if is_standard(step) && finite {
2265                return Ok(int(-1));
2266            }
2267            return Err(format!(
2268                "this loop never ends: its counter starts at {}, steps by {}, and its guard holds at every step",
2269                t0.display_string(),
2270                step.display_string()
2271            ));
2272        }
2273        let branches: Vec<Option<Poly>> = exits
2274            .iter()
2275            .map(|e| match e {
2276                Some(Exit::At(k)) => Some(class_polynomial(k).expect("an exit is an integer on its class")),
2277                _ => None,
2278            })
2279            .collect();
2280        let value = Depends::from_branches(scale.residues.clone(), modulus, branches)
2281            .expect("the classes are the type's own");
2282        let count = Hypernatural::from_branches(value).expect("each branch is an integer on its class");
2283        return Ok(tower::from_natural(count, &scale));
2284    }
2285}
2286
2287/// The first failure of `guard` on one class, every value pinned to it.
2288fn exit_on_class<V: ValueAlgebra>(t0: &V, step: &V, guard: &Guard<V>) -> Result<Exit<V>, String> {
2289    let mut starts: Vec<V> = vec![int(0)];
2290    let mut period: u64 = 1;
2291    let mut atoms = Vec::new();
2292    guard.atoms(&mut atoms);
2293    for atom in atoms {
2294        let (Guard::Linear { a, b, .. } | Guard::Remainder { a, b, .. }) = atom else {
2295            unreachable!("an atom is a linear term or a remainder");
2296        };
2297        let rate = V::multiply(a.clone(), step.clone())?;
2298        if let Guard::Remainder { m, .. } = atom {
2299            let entry = V::list(vec![rate.clone(), m.clone()]);
2300            let p = remainder_period(&entry)?.ok_or_else(|| {
2301                format!(
2302                    "a loop's guard takes a remainder by a whole number of a term that steps by a \
2303                     rational amount; here the term steps by {} and the remainder is by {}",
2304                    rate.display_string(),
2305                    m.display_string()
2306                )
2307            })?;
2308            period = bounded_lcm(period, p).map_err(|_| "a loop's guard repeats with too long a period".to_string())?;
2309            continue;
2310        }
2311        if V::holds(Rel::Eq, &rate, &int(0))? {
2312            continue;
2313        }
2314        let start = V::add(V::multiply(a.clone(), t0.clone())?, b.clone())?;
2315        let crossing = V::floor(V::exact_divide(V::subtract(int(0), start)?, rate)?)?;
2316        match dependence(&crossing) {
2317            1 => starts.push(crossing),
2318            finer => return Ok(Exit::Finer(finer)),
2319        }
2320    }
2321    let mut ordered: Vec<V> = Vec::with_capacity(starts.len());
2322    for s in starts {
2323        if !V::holds(Rel::GtEq, &s, &int(0))? {
2324            continue;
2325        }
2326        let mut at = ordered.len();
2327        for (i, o) in ordered.iter().enumerate() {
2328            if V::holds(Rel::LtEq, &s, o)? {
2329                at = i;
2330                break;
2331            }
2332        }
2333        if at == ordered.len() || !V::holds(Rel::Eq, &s, &ordered[at])? {
2334            ordered.insert(at, s);
2335        }
2336    }
2337    let mut examined: u64 = 0;
2338    let mut next: Option<V> = None;
2339    for s in ordered {
2340        let mut k = match next {
2341            Some(n) if V::holds(Rel::Gt, &n, &s)? => n,
2342            _ => s.clone(),
2343        };
2344        let last = V::add(s, int(period as i64))?;
2345        while V::holds(Rel::LtEq, &k, &last)? {
2346            examined += 1;
2347            if examined > MAX_CANDIDATES {
2348                return Err("a loop's guard changes at too many points to read where it first fails".to_string());
2349            }
2350            let t = V::add(t0.clone(), V::multiply(k.clone(), step.clone())?)?;
2351            match guard.at(&t)? {
2352                Truth::Holds(true) => {}
2353                Truth::Holds(false) => return Ok(Exit::At(k)),
2354                Truth::Finer(m) => return Ok(Exit::Finer(m)),
2355            }
2356            k = V::add(k, int(1))?;
2357        }
2358        next = Some(k);
2359    }
2360    Ok(Exit::Never)
2361}
2362
2363// ── search candidates ─────────────────────────────────────────────────────────────────────────
2364
2365/// The most candidates one search examines; past it the search is not decided this way.
2366const MAX_CANDIDATES: u64 = 1 << 20;
2367
2368/// A standard exact coefficient; `None` for a float (whose rounding the linear reading would not
2369/// see) or anything nonstandard.
2370fn exact_standard<V: ValueAlgebra>(v: &V) -> Option<V> {
2371    match v.shape() {
2372        Shape::Int(_) | Shape::BigInt(_) | Shape::Rational(_) => Some(v.clone()),
2373        _ => matches!(v.exact(), Some(ExactRef::Algebraic(_))).then(|| v.clone()),
2374    }
2375}
2376
2377/// The period in `n` of a remainder term: `m` for `n % m`, written as the modulus alone, and
2378/// `m·q` for `(a·n + b) % m`, written `[a, m]`, with `a = p/q` in lowest terms — `a·(n + m·q)`
2379/// differs from `a·n` by `p·m`. `None` when it is not a positive whole period.
2380fn remainder_period<V: ValueAlgebra>(entry: &V) -> Result<Option<u64>, String> {
2381    let (a, m) = match entry.shape() {
2382        Shape::List(_) => {
2383            let pair = items(entry, "a remainder term")?;
2384            match pair.as_slice() {
2385                [a, m] => (a.clone(), m.clone()),
2386                _ => return Ok(None),
2387            }
2388        }
2389        _ => (int(1), entry.clone()),
2390    };
2391    let m = match m.shape() {
2392        Shape::Int(k) if k > 0 => k as u64,
2393        _ => return Ok(None),
2394    };
2395    let denominator = match a.shape() {
2396        Shape::Int(_) | Shape::BigInt(_) => 1,
2397        Shape::Rational(r) => match r.denominator().to_u64() {
2398            Some(q) => q,
2399            None => return Ok(None),
2400        },
2401        _ => return Ok(None),
2402    };
2403    Ok(m.checked_mul(denominator))
2404}
2405
2406/// The naturals a search over every natural must examine, in increasing order; empty when the
2407/// predicate's coefficients or periods are not ones this reading decides. See the module
2408/// documentation.
2409pub fn search_candidates<V: ValueAlgebra>(atoms: &V, moduli: &V) -> Result<V, String> {
2410    let undecided = || Ok(V::list(Vec::new()));
2411    let mut period: u64 = 1;
2412    for entry in items(moduli, "a search's periods")? {
2413        let Some(m) = remainder_period(&entry)? else {
2414            return undecided();
2415        };
2416        let l = u128::from(period / gcd(period, m)) * u128::from(m);
2417        if l > u128::from(MAX_CANDIDATES) {
2418            return undecided();
2419        }
2420        period = l as u64;
2421    }
2422    let mut starts: Vec<u64> = vec![0];
2423    for atom in items(atoms, "a search's linear terms")? {
2424        let pair = items(&atom, "a linear term")?;
2425        let (Some(a), Some(b)) = (
2426            pair.first().and_then(exact_standard),
2427            pair.get(1).and_then(exact_standard),
2428        ) else {
2429            return undecided();
2430        };
2431        if V::holds(Rel::Eq, &a, &int(0))? {
2432            continue;
2433        }
2434        let root = V::exact_divide(V::subtract(int(0), b)?, a)?;
2435        let sides: [fn(V) -> Result<V, String>; 2] = [V::floor, V::ceil];
2436        for side in sides {
2437            match side(root.clone())?.shape() {
2438                Shape::Int(t) if t >= 0 => starts.push(t as u64),
2439                Shape::Int(_) => {}
2440                Shape::BigInt(t) if t.is_negative() => {}
2441                _ => return undecided(),
2442            }
2443        }
2444    }
2445    starts.sort_unstable();
2446    starts.dedup();
2447    let mut out: Vec<u64> = Vec::new();
2448    for s in starts {
2449        let from = out.last().map_or(s, |last| s.max(last + 1));
2450        let to = s.saturating_add(period);
2451        if to >= from {
2452            if out.len() as u64 + (to - from + 1) > MAX_CANDIDATES {
2453                return undecided();
2454            }
2455            out.extend(from..=to);
2456        }
2457    }
2458    Ok(V::list(out.into_iter().map(|n| int(n as i64)).collect()))
2459}
2460
2461#[cfg(test)]
2462#[path = "../../logicaffeine_tests/unit/logicaffeine_base/supertask/tests.rs"]
2463mod tests;