Skip to main content

logicaffeine_base/
multi.rs

1//! Values of several ordered infinitesimals: `dt`, `dx` declared much smaller than `dt`, and so on.
2//!
3//! An infinitesimal declared much smaller than another is smaller than every positive number made from
4//! it ([`Scale`]), so the values a program makes from the two are the rational functions of the smaller
5//! over the values of the larger, `R(dt)(dx)`, ordered by the transfer principle one infinitesimal at a
6//! time, the smallest first: a rational function of `dx` has, for every small enough `dx` beside a fixed
7//! `dt`, the sign of its lowest-order coefficient in `dx`, and that coefficient's sign is decided among
8//! the values of `dt`. So `dx < dt^n` for every standard `n`, and `dx/dt^1000` is infinitesimal.
9//!
10//! A [`Multi`] holds a value at the smallest infinitesimal it involves, as a rational function of a
11//! root `dx^(1/q)` whose coefficients involve only larger infinitesimals: reduced, its denominator
12//! comonic, on the least root that holds it. A value of one infinitesimal is held as its real closure's
13//! own ([`Closed`]) and a standard number as an algebraic number, so a value has one form whatever route
14//! made it.
15//!
16//! A value prints as an exact fraction of polynomials in the infinitesimals, in ascending powers of the
17//! smallest and each coefficient in the larger ones: `dt + dx`, `dx/dt`, `1/(dt + dx)`, `(1 + dt)·dx`.
18
19use std::cmp::Ordering;
20use std::hash::{Hash, Hasher};
21use std::sync::Arc;
22
23use crate::algebraic::{negative_root, Algebraic};
24use crate::closure::{AlgValue, Recipe};
25use crate::exact_codec::{read_byte, read_len, write_len, ExactCodec, MAX_WIRE_DEPTH, MAX_WIRE_LEN};
26use crate::hyperreal::{Hyperreal, PowerError, MAX_POWER_DEGREE};
27use crate::numeric::{BigInt, Rational};
28use crate::ordered_field::{OrderedField, PolyOver};
29use crate::puiseux::{power_text, top_level, Closed, Puiseux};
30use crate::tower::Scale;
31
32/// A value of several ordered infinitesimals. See the module documentation.
33#[derive(Clone, Debug)]
34pub enum Multi {
35    /// A standard number.
36    Standard(Algebraic),
37    /// A value of one infinitesimal, not standard.
38    One(Closed, Arc<Scale>),
39    /// `num/den` in `scale^(1/q)`, every coefficient a value of larger infinitesimals and at least one
40    /// not standard.
41    Over {
42        scale: Arc<Scale>,
43        q: u32,
44        num: PolyOver<Multi>,
45        den: PolyOver<Multi>,
46    },
47    /// A value of the real closure at its smallest infinitesimal that is no rational function of a
48    /// root of it ([`crate::closure`]).
49    Alg(Arc<AlgValue>),
50}
51
52/// Why a power of a value of several infinitesimals is refused, with the infinitesimal and the root
53/// of it its degree is counted in.
54#[derive(Clone, Debug, PartialEq, Eq)]
55pub struct MultiPowerError {
56    pub error: PowerError,
57    pub name: String,
58    pub q: u32,
59}
60
61impl MultiPowerError {
62    /// The refusal: `the power would have degree 5000 in dx; …`.
63    pub fn describe(&self) -> String {
64        self.error.describe(&power_text(&self.name, &ratio(1, self.q)))
65    }
66}
67
68fn ratio(k: usize, q: u32) -> Rational {
69    Rational::from_ratio_i64(k as i64, i64::from(q)).expect("q is positive")
70}
71
72fn gcd_u64(a: u64, b: u64) -> u64 {
73    if b == 0 {
74        a
75    } else {
76        gcd_u64(b, a % b)
77    }
78}
79
80fn lcm_u32(a: u32, b: u32) -> u32 {
81    let g = gcd_u64(u64::from(a), u64::from(b));
82    u32::try_from(u64::from(a) / g * u64::from(b)).expect("a root of an infinitesimal of moderate index")
83}
84
85/// The smaller of two infinitesimals a value combines; the tower has ordered every pair first.
86fn smaller<'a>(a: &'a Arc<Scale>, b: &'a Arc<Scale>) -> &'a Arc<Scale> {
87    match a.order(b).expect("every infinitesimal a value combines is ordered with the others") {
88        Ordering::Greater => b,
89        Ordering::Less | Ordering::Equal => a,
90    }
91}
92
93/// The smallest infinitesimal either value involves; when both involve it, as both values know it — a
94/// value read on a residue class of its reciprocal, with the facts its class adds, combined with any
95/// other is read on that class ([`met`]).
96fn smallest(a: Option<&Arc<Scale>>, b: Option<&Arc<Scale>>) -> Option<Arc<Scale>> {
97    match (a, b) {
98        (Some(x), Some(y)) if x.same_infinitesimal(y) => Some(met(x, y)),
99        (Some(x), Some(y)) => Some(smaller(x, y).clone()),
100        (x, None) => x.cloned(),
101        (None, y) => y.cloned(),
102    }
103}
104
105/// One infinitesimal as two values know it: with the facts about its reciprocal both state. Each value
106/// holds on the classes its facts admit, so their combination holds where both do.
107fn met(a: &Arc<Scale>, b: &Arc<Scale>) -> Arc<Scale> {
108    match a.residues.and(&b.residues) {
109        Ok(both) if both == a.residues => a.clone(),
110        Ok(both) if both == b.residues => b.clone(),
111        Ok(both) => Arc::new(a.with_residues(both)),
112        Err(_) => a.clone(),
113    }
114}
115
116/// `s` among the infinitesimals `out` lists, one copy each: an infinitesimal listed already is held with
117/// the facts both copies state ([`met`]), for a value lives where all of its parts do.
118fn push_scale(out: &mut Vec<Arc<Scale>>, s: &Arc<Scale>) {
119    match out.iter_mut().find(|t| t.same_infinitesimal(s)) {
120        Some(t) => *t = met(t, s),
121        None => out.push(s.clone()),
122    }
123}
124
125impl Multi {
126    /// The infinitesimal itself.
127    pub fn generator(scale: &Arc<Scale>) -> Multi {
128        Multi::One(Closed::from_hyperreal(Hyperreal::dt()), scale.clone())
129    }
130
131    /// A value of one infinitesimal, in its one form.
132    pub fn of_closed(c: Closed, scale: &Arc<Scale>) -> Multi {
133        match c.as_standard() {
134            Some(a) => Multi::Standard(a),
135            None => Multi::One(c, scale.clone()),
136        }
137    }
138
139    /// The smallest infinitesimal the value involves; `None` for a standard number.
140    pub fn top(&self) -> Option<&Arc<Scale>> {
141        match self {
142            Multi::Standard(_) => None,
143            Multi::One(_, s) | Multi::Over { scale: s, .. } => Some(s),
144            Multi::Alg(a) => Some(&a.scale),
145        }
146    }
147
148    /// Every infinitesimal the value involves, each once, with the facts all of its parts state.
149    pub fn scales(&self) -> Vec<Arc<Scale>> {
150        let mut out = Vec::new();
151        self.collect_scales(&mut out);
152        out
153    }
154
155    fn collect_scales(&self, out: &mut Vec<Arc<Scale>>) {
156        match self {
157            Multi::Standard(_) => {}
158            Multi::One(_, s) => push_scale(out, s),
159            Multi::Over { scale, num, den, .. } => {
160                push_scale(out, scale);
161                for c in num.c.iter().chain(den.c.iter()) {
162                    c.collect_scales(out);
163                }
164            }
165            Multi::Alg(a) => {
166                push_scale(out, &a.scale);
167                recipe_scales(&a.recipe, out);
168            }
169        }
170    }
171
172    /// The value as `(q, num, den)`, a fraction of polynomials in `scale^(1/q)`, for `scale` its own
173    /// smallest infinitesimal or one smaller, and the value no root that is none.
174    pub(crate) fn parts_at(&self, scale: &Arc<Scale>) -> (u32, PolyOver<Multi>, PolyOver<Multi>) {
175        match self {
176            Multi::Alg(a) if a.scale.same_infinitesimal(scale) => {
177                unreachable!("a root that is no rational function of a root of its infinitesimal has no fraction there")
178            }
179            Multi::Over { scale: s, q, num, den } if s.same_infinitesimal(scale) => (*q, num.clone(), den.clone()),
180            Multi::One(c, s) if s.same_infinitesimal(scale) => {
181                let (h, q) = c
182                    .field()
183                    .expect("a root that is no rational function of a root of its infinitesimal meets no larger one");
184                let (num, den) = h.parts();
185                let lift = |p: &PolyOver<Algebraic>| PolyOver::from(p.c.iter().cloned().map(Multi::Standard).collect());
186                (q, lift(num), lift(den))
187            }
188            _ => (1, PolyOver::constant(self.clone()), PolyOver::one()),
189        }
190    }
191
192    /// Both values over the smallest infinitesimal either involves, on the least root of it both lie
193    /// on: that infinitesimal, the root's `q`, and each value's numerator and denominator.
194    #[allow(clippy::type_complexity)]
195    fn over_one(
196        &self,
197        o: &Multi,
198    ) -> (Arc<Scale>, u32, (PolyOver<Multi>, PolyOver<Multi>), (PolyOver<Multi>, PolyOver<Multi>)) {
199        let scale = smallest(self.top(), o.top()).expect("a value of an infinitesimal is on one side");
200        let (qa, na, da) = self.parts_at(&scale);
201        let (qb, nb, db) = o.parts_at(&scale);
202        let q = lcm_u32(qa, qb);
203        let (ma, mb) = ((q / qa) as usize, (q / qb) as usize);
204        (scale, q, (na.spread(ma), da.spread(ma)), (nb.spread(mb), db.spread(mb)))
205    }
206
207    /// Both values as values of one infinitesimal's real closure, when neither involves another.
208    fn closed_pair(&self, o: &Multi) -> Option<(Closed, Closed, Arc<Scale>)> {
209        let closed = |m: &Multi| match m {
210            Multi::Standard(a) => Some(Closed::from_hyperreal(Hyperreal::from_algebraic(a.clone()))),
211            Multi::One(c, _) => Some(c.clone()),
212            Multi::Over { .. } | Multi::Alg(_) => None,
213        };
214        let scale = match (self.top(), o.top()) {
215            (Some(a), Some(b)) if a.same_infinitesimal(b) => met(a, b),
216            (Some(a), None) | (None, Some(a)) => a.clone(),
217            _ => return None,
218        };
219        Some((closed(self)?, closed(o)?, scale))
220    }
221
222    /// Both values in the real closure at the smallest infinitesimal either involves, when one of them
223    /// is a root there that is no rational function of a root of it.
224    fn closure_pair(&self, o: &Multi) -> Option<(Arc<AlgValue>, Arc<AlgValue>)> {
225        let scale = &smallest(self.top(), o.top())?;
226        let root_there = |m: &Multi| match m {
227            Multi::Alg(a) => a.scale.same_infinitesimal(scale),
228            Multi::One(c, s) => s.same_infinitesimal(scale) && c.field().is_none(),
229            _ => false,
230        };
231        if !(root_there(self) || root_there(o)) {
232            return None;
233        }
234        Some((Arc::new(AlgValue::at(self, scale)), Arc::new(AlgValue::at(o, scale))))
235    }
236
237    /// `num/den` in `scale^(1/q)` in its one form: reduced, the denominator comonic, on the least root
238    /// of `scale` that holds it; a value with no power of `scale` left is its coefficient, and one whose
239    /// every coefficient is standard is a value of `scale` alone. A denominator that is one power of the
240    /// root shares with the numerator only the power both hold, so it reduces without a gcd.
241    pub(crate) fn make(scale: &Arc<Scale>, q: u32, num: PolyOver<Multi>, den: PolyOver<Multi>) -> Multi {
242        if num.is_zero() {
243            return Multi::zero();
244        }
245        let monomial = den.order().filter(|&d| den.c.iter().enumerate().all(|(i, c)| i == d || c.is_zero()));
246        let (num, den) = if den.degree() == Some(0) {
247            let inv = den.coeff(0).recip().expect("the denominator is nonzero");
248            (num.scale(&inv), PolyOver::one())
249        } else if let Some(d) = monomial {
250            let shift = d.min(num.order().expect("the numerator is nonzero"));
251            let inv = den.coeff(d).recip().expect("the denominator is nonzero");
252            let mut power = vec![Multi::zero(); d - shift + 1];
253            power[d - shift] = Multi::Standard(Algebraic::from_i64(1));
254            (PolyOver::from(num.c[shift..].to_vec()).scale(&inv), PolyOver::from(power))
255        } else {
256            let g = num.gcd(&den);
257            let (num, den) = (num.div_rem(&g).0, den.div_rem(&g).0);
258            let inv = den.coeff(den.order().expect("the denominator is nonzero")).recip().expect("nonzero");
259            (num.scale(&inv), den.scale(&inv))
260        };
261        let (num, den, q) = least_root(num, den, q);
262        if num.degree() == Some(0) && den.degree() == Some(0) {
263            return num.coeff(0);
264        }
265        let standard = |p: &PolyOver<Multi>| -> Option<PolyOver<Algebraic>> {
266            p.c.iter()
267                .map(|c| match c {
268                    Multi::Standard(a) => Some(a.clone()),
269                    _ => None,
270                })
271                .collect::<Option<Vec<_>>>()
272                .map(PolyOver::from)
273        };
274        if let (Some(n), Some(d)) = (standard(&num), standard(&den)) {
275            return Multi::of_closed(Closed::of_field(Hyperreal::from_parts(n, d), q), scale);
276        }
277        Multi::Over {
278            scale: scale.clone(),
279            q,
280            num,
281            den,
282        }
283    }
284
285    /// The exponent of the leading term in the value's smallest infinitesimal and its coefficient, a
286    /// value of the larger ones; `None` for a standard number.
287    pub(crate) fn leading(&self) -> Option<(Rational, Multi)> {
288        match self {
289            Multi::Standard(_) => None,
290            Multi::Alg(a) => Some((a.order(), a.leading().1)),
291            Multi::One(c, _) => c.leading().map(|(v, a)| (v, Multi::Standard(a))),
292            Multi::Over { q, num, den, .. } => {
293                let n = num.order().expect("a value is nonzero");
294                let d = den.order().expect("the denominator is nonzero");
295                let v = Rational::from_ratio_i64(n as i64 - d as i64, i64::from(*q)).expect("q is positive");
296                Some((v, num.coeff(n)))
297            }
298        }
299    }
300
301    /// What the value is infinitely close to among the values of `at` and the infinitesimals larger than
302    /// it, when it is limited from `at`'s standpoint: every infinitesimal smaller than `at` read at zero,
303    /// the inner limit with `at` held fixed. With no `at`, the standard part. `None` for a value infinite
304    /// from that standpoint. Every infinitesimal of the value is ordered with `at`.
305    pub fn standard_part_at(&self, at: Option<&Arc<Scale>>) -> Option<Multi> {
306        let Some(top) = self.top() else {
307            return Some(self.clone());
308        };
309        if let Some(at) = at {
310            if top.order(at).expect("the standpoint is ordered with the value's infinitesimals") != Ordering::Less {
311                return Some(self.clone());
312            }
313        }
314        let (v, c) = self.leading().expect("a value of an infinitesimal is nonzero");
315        match v.cmp(&Rational::zero()) {
316            Ordering::Greater => Some(Multi::zero()),
317            Ordering::Less => None,
318            Ordering::Equal => c.standard_part_at(at),
319        }
320    }
321
322    /// The standard real infinitely close to a limited value; `None` for an infinite one.
323    pub fn standard_part(&self) -> Option<Algebraic> {
324        match self.standard_part_at(None)? {
325            Multi::Standard(a) => Some(a),
326            _ => unreachable!("the standard part of a value is standard"),
327        }
328    }
329
330    /// Zero, or smaller than every positive value of `at` and the infinitesimals larger than it (every
331    /// positive standard number, with no `at`).
332    pub fn is_infinitesimal_at(&self, at: Option<&Arc<Scale>>) -> bool {
333        self.standard_part_at(at).is_some_and(|s| s.is_zero())
334    }
335
336    /// Bounded by a value of `at` and the infinitesimals larger than it (a standard number, with no
337    /// `at`).
338    pub fn is_limited_at(&self, at: Option<&Arc<Scale>>) -> bool {
339        self.standard_part_at(at).is_some()
340    }
341
342    pub fn is_infinitesimal(&self) -> bool {
343        self.is_infinitesimal_at(None)
344    }
345
346    pub fn is_limited(&self) -> bool {
347        self.is_limited_at(None)
348    }
349
350    /// The largest degree any of the value's infinitesimals reaches in it, with that infinitesimal and
351    /// the root of it the degree counts.
352    fn widest(&self) -> Option<(usize, Arc<Scale>, u32)> {
353        match self {
354            Multi::Standard(_) | Multi::Alg(_) => None,
355            Multi::One(c, s) => c.field().map(|(h, q)| (h.degree(), s.clone(), q)),
356            Multi::Over { scale, q, num, den } => {
357                let here = (num.degree().unwrap_or(0).max(den.degree().unwrap_or(0)), scale.clone(), *q);
358                num.c
359                    .iter()
360                    .chain(den.c.iter())
361                    .filter_map(Multi::widest)
362                    .chain(std::iter::once(here))
363                    .max_by_key(|(d, _, _)| *d)
364            }
365        }
366    }
367
368    /// `self^k`; refused for zero to a negative power and for a degree past
369    /// [`crate::hyperreal::MAX_POWER_DEGREE`] in any of the value's infinitesimals.
370    pub fn pow(&self, k: i32) -> Result<Multi, MultiPowerError> {
371        let zero_to_negative = |name: &str| MultiPowerError {
372            error: PowerError::ZeroToNegative,
373            name: name.to_string(),
374            q: 1,
375        };
376        match self {
377            Multi::Standard(a) => a.pow(k).map(Multi::Standard).ok_or_else(|| zero_to_negative("")),
378            Multi::One(c, s) => c.pow(k).map(|r| Multi::of_closed(r, s)).map_err(|e| MultiPowerError {
379                error: e.error,
380                name: s.name.clone(),
381                q: e.q,
382            }),
383            Multi::Alg(a) => {
384                let reach = u128::from(k.unsigned_abs());
385                if reach > u128::from(MAX_POWER_DEGREE) {
386                    return Err(MultiPowerError {
387                        error: PowerError::DegreeTooHigh { degree: reach },
388                        name: a.scale.name.clone(),
389                        q: a.expansion.ramification(),
390                    });
391                }
392                Ok(match k.cmp(&0) {
393                    Ordering::Equal => Multi::one(),
394                    Ordering::Greater => AlgValue::pow(a, k.unsigned_abs()),
395                    Ordering::Less => match AlgValue::recip(a) {
396                        Multi::Alg(r) => AlgValue::pow(&r, k.unsigned_abs()),
397                        r => r.pow(k.saturating_neg())?,
398                    },
399                })
400            }
401            Multi::Over { .. } => {
402                if let Some((degree, scale, q)) = self.widest() {
403                    let reach = degree as u128 * u128::from(k.unsigned_abs());
404                    if reach > u128::from(MAX_POWER_DEGREE) {
405                        return Err(MultiPowerError {
406                            error: PowerError::DegreeTooHigh { degree: reach },
407                            name: scale.name.clone(),
408                            q,
409                        });
410                    }
411                }
412                let mut base = if k < 0 {
413                    self.recip().expect("a value of several infinitesimals is nonzero")
414                } else {
415                    self.clone()
416                };
417                let mut acc = Multi::one();
418                let mut e = k.unsigned_abs();
419                while e > 0 {
420                    if e & 1 == 1 {
421                        acc = acc.mul(&base);
422                    }
423                    e >>= 1;
424                    if e > 0 {
425                        base = base.mul(&base);
426                    }
427                }
428                Ok(acc)
429            }
430        }
431    }
432
433    /// The real `n`-th root, the nonnegative one for an even `n`: of a value of one infinitesimal in its
434    /// real closure, and of a product of a power of the smallest infinitesimal and a value of the larger
435    /// ones as the product of their roots.
436    pub fn root(&self, n: u32) -> Result<Multi, String> {
437        if n == 1 || self.is_zero() {
438            return Ok(self.clone());
439        }
440        match self {
441            Multi::Standard(a) => a.nth_root(n).map(Multi::Standard).ok_or_else(|| negative_root(n)),
442            Multi::One(c, s) => Ok(Multi::of_closed(c.root(n)?, s)),
443            Multi::Alg(a) => AlgValue::root(a, n),
444            Multi::Over { scale, q, num, den } => {
445                let single = |p: &PolyOver<Multi>| -> Option<usize> {
446                    let mut terms = p.c.iter().enumerate().filter(|(_, c)| !c.is_zero());
447                    let (i, _) = terms.next()?;
448                    terms.next().is_none().then_some(i)
449                };
450                let (Some(a), Some(b)) = (single(num), single(den)) else {
451                    return AlgValue::root(&Arc::new(AlgValue::field(self, scale)), n);
452                };
453                let root = num.coeff(a).root(n)?;
454                let e = a as i64 - b as i64;
455                let mut power = vec![Multi::zero(); e.unsigned_abs() as usize + 1];
456                power[e.unsigned_abs() as usize] = Multi::one();
457                let power = PolyOver::from(power);
458                let (top, bottom) = if e >= 0 {
459                    (power.scale(&root), PolyOver::one())
460                } else {
461                    (PolyOver::constant(root), power)
462                };
463                Ok(Multi::make(scale, q * n, top, bottom))
464            }
465        }
466    }
467}
468
469/// `num/den` on the least root of their infinitesimal: `t = s^(1/q)` replaced by `t^m` for the largest
470/// `m` dividing `q` and every power the two hold.
471fn least_root(num: PolyOver<Multi>, den: PolyOver<Multi>, q: u32) -> (PolyOver<Multi>, PolyOver<Multi>, u32) {
472    let powers = num
473        .c
474        .iter()
475        .enumerate()
476        .chain(den.c.iter().enumerate())
477        .filter(|(_, c)| !c.is_zero())
478        .map(|(i, _)| i as u64);
479    let m = powers.fold(u64::from(q), gcd_u64) as usize;
480    if m <= 1 {
481        return (num, den, q);
482    }
483    let gather = |p: &PolyOver<Multi>| PolyOver::from(p.c.iter().step_by(m).cloned().collect());
484    (gather(&num), gather(&den), q / m as u32)
485}
486
487impl OrderedField for Multi {
488    fn zero() -> Self {
489        Multi::Standard(Algebraic::from_i64(0))
490    }
491    fn one() -> Self {
492        Multi::Standard(Algebraic::from_i64(1))
493    }
494    fn from_i64(n: i64) -> Self {
495        Multi::Standard(Algebraic::from_i64(n))
496    }
497    fn is_zero(&self) -> bool {
498        matches!(self, Multi::Standard(a) if a.is_zero())
499    }
500    fn add(&self, o: &Self) -> Self {
501        if let (Multi::Standard(a), Multi::Standard(b)) = (self, o) {
502            return Multi::Standard(a.add(b));
503        }
504        if let Some((a, b, s)) = self.closed_pair(o) {
505            return Multi::of_closed(a.add(&b), &s);
506        }
507        if let Some((a, b)) = self.closure_pair(o) {
508            return AlgValue::add(&a, &b);
509        }
510        let (scale, q, (na, da), (nb, db)) = self.over_one(o);
511        if da == db {
512            return Multi::make(&scale, q, na.add(&nb), da);
513        }
514        Multi::make(&scale, q, na.mul(&db).add(&nb.mul(&da)), da.mul(&db))
515    }
516    fn neg(&self) -> Self {
517        match self {
518            Multi::Standard(a) => Multi::Standard(a.neg()),
519            Multi::One(c, s) => Multi::One(c.neg(), s.clone()),
520            Multi::Over { scale, q, num, den } => Multi::Over {
521                scale: scale.clone(),
522                q: *q,
523                num: num.neg(),
524                den: den.clone(),
525            },
526            Multi::Alg(a) => AlgValue::neg(a),
527        }
528    }
529    fn mul(&self, o: &Self) -> Self {
530        if let (Multi::Standard(a), Multi::Standard(b)) = (self, o) {
531            return Multi::Standard(a.mul(b));
532        }
533        if self.is_zero() || o.is_zero() {
534            return Multi::zero();
535        }
536        if let Some((a, b, s)) = self.closed_pair(o) {
537            return Multi::of_closed(a.mul(&b), &s);
538        }
539        if let Some((a, b)) = self.closure_pair(o) {
540            return AlgValue::mul(&a, &b);
541        }
542        let (scale, q, (na, da), (nb, db)) = self.over_one(o);
543        Multi::make(&scale, q, na.mul(&nb), da.mul(&db))
544    }
545    fn recip(&self) -> Option<Self> {
546        match self {
547            Multi::Standard(a) => a.recip().map(Multi::Standard),
548            Multi::One(c, s) => c.recip().map(|r| Multi::of_closed(r, s)),
549            Multi::Over { scale, q, num, den } => Some(Multi::make(scale, *q, den.clone(), num.clone())),
550            Multi::Alg(a) => Some(AlgValue::recip(a)),
551        }
552    }
553    fn signum(&self) -> i32 {
554        match self {
555            Multi::Standard(a) => a.signum(),
556            Multi::One(c, _) => c.signum(),
557            Multi::Over { num, .. } => num.coeff(num.order().expect("a value is nonzero")).signum(),
558            Multi::Alg(a) => a.sign(),
559        }
560    }
561}
562
563impl PartialEq for Multi {
564    fn eq(&self, o: &Self) -> bool {
565        match (self, o) {
566            (Multi::Standard(a), Multi::Standard(b)) => a == b,
567            (Multi::One(a, s), Multi::One(b, t)) => s.same_infinitesimal(t) && a == b,
568            (
569                Multi::Over { scale: s, q: qa, num: na, den: da },
570                Multi::Over { scale: t, q: qb, num: nb, den: db },
571            ) => s.same_infinitesimal(t) && qa == qb && na == nb && da == db,
572            (Multi::Alg(a), Multi::Alg(b)) => AlgValue::equals(a, b),
573            _ => false,
574        }
575    }
576}
577
578impl Eq for Multi {}
579
580impl Hash for Multi {
581    /// Equal values have one form, and one infinitesimal one name.
582    fn hash<H: Hasher>(&self, state: &mut H) {
583        match self {
584            Multi::Standard(a) => {
585                0u8.hash(state);
586                a.hash(state);
587            }
588            Multi::One(c, s) => {
589                1u8.hash(state);
590                s.name.hash(state);
591                c.hash(state);
592            }
593            Multi::Over { scale, q, num, den } => {
594                2u8.hash(state);
595                scale.name.hash(state);
596                q.hash(state);
597                num.hash(state);
598                den.hash(state);
599            }
600            Multi::Alg(a) => {
601                3u8.hash(state);
602                a.scale.name.hash(state);
603                let (order, lead) = a.leading();
604                order.hash(state);
605                a.expansion.ramification().hash(state);
606                lead.hash(state);
607            }
608        }
609    }
610}
611
612// ── printing ──────────────────────────────────────────────────────────────────────────────────
613
614impl Multi {
615    /// The value as an exact fraction of polynomials in its infinitesimals, as a hyperreal prints: a
616    /// polynomial with its coefficients as they are, `(1/2)·dt + dx`, and any other value as a fraction
617    /// of polynomials with coprime integer coefficients where every coefficient is rational,
618    /// `dx/(2·dt)`. An infinite integer prints as one of one infinitesimal does, a sum of reciprocals
619    /// (`Multi::render_reciprocals`), wherever it is printed — alone, under a radical, as a
620    /// coefficient.
621    pub fn render(&self) -> String {
622        if let Multi::Alg(a) = self {
623            return AlgValue::render(a);
624        }
625        if self.is_integral() {
626            if let Some(text) = self.render_reciprocals() {
627                return text;
628            }
629        }
630        let (n, d) = self.fraction();
631        if d.top().is_none() {
632            return render_polynomial(self);
633        }
634        let numerator = render_polynomial(&n);
635        let denominator = render_polynomial(&d);
636        let numerator = if top_level(&numerator, &[' ']) { format!("({numerator})") } else { numerator };
637        let denominator = if top_level(&denominator, &[' ', '·']) { format!("({denominator})") } else { denominator };
638        format!("{numerator}/{denominator}")
639    }
640
641    /// Whether the value is an integer on every residue class its infinitesimals' facts admit — the facts
642    /// its scales carry, a class's own among them, an infinitesimal its terms know apart read with the
643    /// facts all of them state ([`met`]): a polynomial in their reciprocals with rational
644    /// coefficients whose fraction vanishes on each. Modulo the coefficients' common denominator `D` the
645    /// fraction depends only on each reciprocal's class, so it is read on one member of each admitted
646    /// class; past [`INTEGRAL_CLASSES`] joint classes — counted before any is read — the value is not
647    /// called one, for no program holds a residue that fine.
648    pub(crate) fn is_integral(&self) -> bool {
649        let mut terms = Vec::new();
650        if reciprocal_terms(self, &mut Vec::new(), &mut terms).is_none() {
651            return false;
652        }
653        let denominator = terms.iter().fold(BigInt::from_i64(1), |d, (_, c)| {
654            let g = d.gcd(c.denominator());
655            d.div_rem(&g).expect("a nonzero divisor").0.mul(c.denominator())
656        });
657        let Some(d) = denominator.to_i64().and_then(|d| u64::try_from(d).ok()) else {
658            return false;
659        };
660        if d == 1 {
661            return true;
662        }
663        let mut scales: Vec<Arc<Scale>> = Vec::new();
664        for (monomial, _) in &terms {
665            for (s, _) in monomial {
666                match scales.iter_mut().find(|t| t.same_infinitesimal(s)) {
667                    Some(t) => *t = met(t, s),
668                    None => scales.push(s.clone()),
669                }
670            }
671        }
672        let mut moduli: Vec<u64> = Vec::with_capacity(scales.len());
673        let mut joint: u64 = 1;
674        for s in &scales {
675            let ty = &s.residues;
676            let g = gcd_u64(d, ty.modulus());
677            let Some(m) = (d / g).checked_mul(ty.modulus()) else {
678                return false;
679            };
680            let per_period = (0..ty.modulus()).filter(|h| ty.admits(*h)).count() as u64;
681            joint = match joint.checked_mul((m / ty.modulus()).saturating_mul(per_period)) {
682                Some(j) if j <= INTEGRAL_CLASSES => j,
683                _ => return false,
684            };
685            moduli.push(m);
686        }
687        let members: Vec<Vec<u64>> = scales
688            .iter()
689            .zip(&moduli)
690            .map(|(s, m)| (0..*m).filter(|h| s.residues.admits(*h)).collect())
691            .collect();
692        let mut at = vec![0usize; scales.len()];
693        loop {
694            let value = terms.iter().fold(Rational::zero(), |acc, (monomial, c)| {
695                let product = monomial.iter().fold(c.clone(), |p, (s, k)| {
696                    let i = scales.iter().position(|t| t.same_infinitesimal(s)).expect("every scale is listed");
697                    let h = Rational::from_i64(members[i][at[i]] as i64);
698                    p.mul(&h.pow(*k as i32).expect("a natural power"))
699                });
700                acc.add(&product)
701            });
702            if !value.is_integer() {
703                return false;
704            }
705            let mut i = 0;
706            loop {
707                if i == at.len() {
708                    return true;
709                }
710                at[i] += 1;
711                if at[i] < members[i].len() {
712                    break;
713                }
714                at[i] = 0;
715                i += 1;
716            }
717        }
718    }
719
720    /// The value as a sum of terms `c/(dt^i·dx^j)`, the infinitesimals of each term written larger
721    /// first and the terms most infinite in the smallest infinitesimal first, when it is a polynomial in
722    /// the reciprocals of its infinitesimals with rational coefficients: the form an infinite integer of
723    /// several prints in, as one of one infinitesimal does (`1/(3·dt^3) - 1/(2·dt^2) + 1/(6·dt)`).
724    /// `None` for any other value.
725    pub(crate) fn render_reciprocals(&self) -> Option<String> {
726        let mut terms = Vec::new();
727        reciprocal_terms(self, &mut Vec::new(), &mut terms)?;
728        let mut scales: Vec<Arc<Scale>> = Vec::new();
729        for (monomial, _) in &terms {
730            for (s, _) in monomial {
731                if !scales.iter().any(|t| t.same_infinitesimal(s)) {
732                    scales.push(s.clone());
733                }
734            }
735        }
736        scales.sort_by(|a, b| a.order(b).unwrap_or(Ordering::Equal));
737        let powers = |monomial: &[(Arc<Scale>, u32)]| -> Vec<u32> {
738            scales
739                .iter()
740                .map(|s| monomial.iter().find(|(t, _)| t.same_infinitesimal(s)).map_or(0, |(_, k)| *k))
741                .collect()
742        };
743        terms.sort_by(|(a, _), (b, _)| powers(b).cmp(&powers(a)));
744        let mut out = String::new();
745        for (i, (monomial, c)) in terms.iter().enumerate() {
746            let negative = c.is_negative();
747            let m = c.abs();
748            let body = if monomial.is_empty() {
749                m.to_string()
750            } else {
751                let factors: Vec<String> = scales
752                    .iter()
753                    .rev()
754                    .filter_map(|s| {
755                        let k = monomial.iter().find(|(t, _)| t.same_infinitesimal(s))?.1;
756                        Some(if k == 1 { s.name.clone() } else { format!("{}^{k}", s.name) })
757                    })
758                    .collect();
759                let product = factors.join("·");
760                let (num, den) = (m.numerator().clone(), m.denominator().clone());
761                if den == BigInt::from_i64(1) && factors.len() == 1 {
762                    format!("{num}/{product}")
763                } else if den == BigInt::from_i64(1) {
764                    format!("{num}/({product})")
765                } else {
766                    format!("{num}/({den}·{product})")
767                }
768            };
769            out.push_str(&match (i, negative) {
770                (0, true) => format!("-{body}"),
771                (0, false) => body,
772                (_, true) => format!(" - {body}"),
773                (_, false) => format!(" + {body}"),
774            });
775        }
776        Some(if out.is_empty() { "0".to_string() } else { out })
777    }
778
779    /// The numerator and denominator the value prints as. See [`Multi::fraction`].
780    pub(crate) fn fraction_parts(&self) -> (Multi, Multi) {
781        self.fraction()
782    }
783
784    /// `self = n/d` for polynomials `n` and `d` in the infinitesimals with no common factor, `d`'s
785    /// leading term positive, and integer coefficients when every coefficient is rational.
786    fn fraction(&self) -> (Multi, Multi) {
787        let (n, d) = self.raw_fraction();
788        if d.signum() < 0 {
789            (n.neg(), d.neg())
790        } else {
791            (n, d)
792        }
793    }
794
795    fn raw_fraction(&self) -> (Multi, Multi) {
796        match self {
797            Multi::Standard(a) => match a.to_rational() {
798                Some(r) => (
799                    Multi::Standard(Algebraic::from_rational(Rational::from_bigint(r.numerator().clone()))),
800                    Multi::Standard(Algebraic::from_rational(Rational::from_bigint(r.denominator().clone()))),
801                ),
802                None => (self.clone(), Multi::one()),
803            },
804            Multi::One(c, s) => match c.field() {
805                Some((h, q)) => {
806                    let (n, d) = h.display_fraction();
807                    let poly = |p: PolyOver<Algebraic>| {
808                        Multi::of_closed(Closed::of_field(Hyperreal::from_parts(p, PolyOver::one()), q), s)
809                    };
810                    (poly(n), poly(d))
811                }
812                None => (self.clone(), Multi::one()),
813            },
814            Multi::Alg(_) => (self.clone(), Multi::one()),
815            Multi::Over { scale, q, num, den } => {
816                let l = num
817                    .c
818                    .iter()
819                    .chain(den.c.iter())
820                    .filter(|c| !c.is_zero())
821                    .fold(Multi::one(), |acc, c| polynomial_lcm(&acc, &c.raw_fraction().1));
822                let lift = |p: &PolyOver<Multi>| PolyOver::from(p.c.iter().map(|c| c.mul(&l)).collect());
823                let (n, d) = (lift(num), lift(den));
824                let g = polynomial_gcd(&content(&n), &content(&d));
825                let g_inv = g.recip().expect("a gcd of nonzero polynomials is nonzero");
826                let reduce = |p: PolyOver<Multi>| Multi::make(scale, *q, p.scale(&g_inv), PolyOver::one());
827                (reduce(n), reduce(d))
828            }
829        }
830    }
831}
832
833/// The greatest common divisor of the coefficients of `p`, polynomials in larger infinitesimals.
834fn content(p: &PolyOver<Multi>) -> Multi {
835    p.c.iter().filter(|c| !c.is_zero()).fold(Multi::zero(), |acc, c| polynomial_gcd(&acc, c))
836}
837
838fn polynomial_lcm(a: &Multi, b: &Multi) -> Multi {
839    let g = polynomial_gcd(a, b);
840    a.mul(b).mul(&g.recip().expect("a gcd of nonzero polynomials is nonzero"))
841}
842
843/// Whether the value is a root that is no rational function of a root of its infinitesimal: a
844/// polynomial's coefficient that no denominator is cleared from and no factor shared with.
845fn is_root_atom(m: &Multi) -> bool {
846    matches!(m, Multi::One(c, _) if c.field().is_none()) || matches!(m, Multi::Alg(_))
847}
848
849/// The smallest infinitesimal a polynomial is a polynomial in; a root atom is a coefficient, in none.
850fn polynomial_top(m: &Multi) -> Option<&Arc<Scale>> {
851    if is_root_atom(m) {
852        None
853    } else {
854        m.top()
855    }
856}
857
858/// A polynomial as `(q, coefficients)` in `scale^(1/q)`, a root atom a constant.
859fn polynomial_at(m: &Multi, scale: &Arc<Scale>) -> (u32, PolyOver<Multi>) {
860    if is_root_atom(m) {
861        return (1, PolyOver::constant(m.clone()));
862    }
863    let (q, p, _) = m.parts_at(scale);
864    (q, p)
865}
866
867/// The greatest common divisor of two polynomials in the infinitesimals, its leading term positive:
868/// the gcd of their contents in the smallest infinitesimal either involves, times the primitive gcd
869/// of what is left, found over the field of the larger infinitesimals' values and cleared of its
870/// denominators. Two standard integers meet in their integer gcd, any other two constants — standard
871/// numbers and roots alike — in 1.
872fn polynomial_gcd(a: &Multi, b: &Multi) -> Multi {
873    let positive = |m: &Multi| if m.signum() < 0 { m.neg() } else { m.clone() };
874    if a.is_zero() {
875        return positive(b);
876    }
877    if b.is_zero() {
878        return positive(a);
879    }
880    let Some(scale) = smallest(polynomial_top(a), polynomial_top(b)) else {
881        let integer = |m: &Multi| match m {
882            Multi::Standard(x) => x.to_rational().filter(Rational::is_integer).and_then(|r| r.to_bigint()),
883            _ => None,
884        };
885        return match (integer(a), integer(b)) {
886            (Some(x), Some(y)) => Multi::Standard(Algebraic::from_rational(Rational::from_bigint(x.gcd(&y)))),
887            _ => Multi::one(),
888        };
889    };
890    let (qa, pa) = polynomial_at(a, &scale);
891    let (qb, pb) = polynomial_at(b, &scale);
892    let q = lcm_u32(qa, qb);
893    let (pa, pb) = (pa.spread((q / qa) as usize), pb.spread((q / qb) as usize));
894    let (ca, cb) = (content(&pa), content(&pb));
895    let c = polynomial_gcd(&ca, &cb);
896    let primitive = |p: &PolyOver<Multi>, k: &Multi| p.scale(&k.recip().expect("a content is nonzero"));
897    let (pa, pb) = (primitive(&pa, &ca), primitive(&pb, &cb));
898    if pa.degree() == Some(0) || pb.degree() == Some(0) {
899        return c;
900    }
901    let g = Multi::make(&scale, q, pa.gcd(&pb), PolyOver::one());
902    let (g, _) = g.raw_fraction();
903    let g_content = match polynomial_top(&g) {
904        Some(t) if t.same_infinitesimal(&scale) => content(&polynomial_at(&g, &scale).1),
905        _ => g.clone(),
906    };
907    let g = g.mul(&g_content.recip().expect("a content is nonzero"));
908    c.mul(&positive(&g))
909}
910
911/// A polynomial in the infinitesimals, in ascending powers of its smallest, each coefficient written in
912/// the larger ones: the constant term with its own signs, every other term's sign between the terms.
913fn render_polynomial(p: &Multi) -> String {
914    match p {
915        Multi::Standard(a) => a.to_string(),
916        Multi::One(c, s) => c.render(&s.name),
917        Multi::Alg(a) => AlgValue::render(a),
918        Multi::Over { scale, q, num, .. } => {
919            let mut out = String::new();
920            for (k, c) in num.c.iter().enumerate() {
921                if c.is_zero() {
922                    continue;
923                }
924                if k == 0 {
925                    out.push_str(&render_polynomial(c));
926                    continue;
927                }
928                let power = power_text(&scale.name, &ratio(k, *q));
929                let negative = c.signum() < 0;
930                let magnitude = render_polynomial(&if negative { c.neg() } else { c.clone() });
931                let body = if magnitude == "1" {
932                    power
933                } else if top_level(&magnitude, &[' ', '/']) {
934                    format!("({magnitude})·{power}")
935                } else {
936                    format!("{magnitude}·{power}")
937                };
938                match (out.is_empty(), negative) {
939                    (true, true) => out.push_str(&format!("-{body}")),
940                    (true, false) => out.push_str(&body),
941                    (false, true) => out.push_str(&format!(" - {body}")),
942                    (false, false) => out.push_str(&format!(" + {body}")),
943                }
944            }
945            out
946        }
947    }
948}
949
950// ── bytes ─────────────────────────────────────────────────────────────────────────────────────
951
952impl Multi {
953    /// The value's canonical bytes: each infinitesimal it involves once, by its name, its declaration
954    /// ([`Scale::to_bytes`]) and the facts about its reciprocal the value holds it with — a residue
955    /// class's, on which an infinite integer of several is one — in the order a walk of the value first
956    /// meets them; then the value, each infinitesimal written by its place among them.
957    pub fn to_bytes(&self) -> Vec<u8> {
958        let table = self.scales();
959        let mut out = Vec::new();
960        write_len(table.len(), &mut out);
961        for s in &table {
962            write_text(&s.name, &mut out);
963            let bytes = s.to_bytes();
964            write_len(bytes.len(), &mut out);
965            out.extend_from_slice(&bytes);
966            s.residues.write(&mut out);
967        }
968        self.write_value(&table, &mut out);
969        out
970    }
971
972    fn write_value(&self, table: &[Arc<Scale>], out: &mut Vec<u8>) {
973        let place = |s: &Arc<Scale>| {
974            table.iter().position(|t| t.same_infinitesimal(s)).expect("the table holds every infinitesimal") as u64
975        };
976        match self {
977            Multi::Standard(a) => {
978                out.push(0);
979                a.write(out);
980            }
981            Multi::One(c, s) => {
982                out.push(1);
983                place(s).write(out);
984                c.encode(out);
985            }
986            Multi::Over { scale, q, num, den } => {
987                out.push(2);
988                place(scale).write(out);
989                u64::from(*q).write(out);
990                for p in [num, den] {
991                    write_len(p.c.len(), out);
992                    for c in &p.c {
993                        c.write_value(table, out);
994                    }
995                }
996            }
997            Multi::Alg(a) => {
998                out.push(3);
999                place(&a.scale).write(out);
1000                write_recipe(&a.recipe, table, out);
1001            }
1002        }
1003    }
1004
1005    /// The value [`Multi::to_bytes`] wrote; `None` for a forged or non-canonical encoding.
1006    pub fn from_bytes(buf: &[u8]) -> Option<Multi> {
1007        let mut pos = 0;
1008        let count = read_len(buf, &mut pos, MAX_WIRE_LEN)?;
1009        let mut table = Vec::with_capacity(count.min(64));
1010        for _ in 0..count {
1011            let name = read_text(buf, &mut pos)?;
1012            let n = read_len(buf, &mut pos, MAX_WIRE_LEN)?;
1013            let bytes = buf.get(pos..pos.checked_add(n)?)?;
1014            pos += n;
1015            let declared = Scale::from_bytes(&name, bytes)?;
1016            let residues = crate::limit_value::ResidueType::read(buf, &mut pos)?;
1017            table.push(if residues == declared.residues {
1018                declared
1019            } else if declared.residues.and(&residues).ok()? == residues {
1020                Arc::new(declared.with_residues(residues))
1021            } else {
1022                return None;
1023            });
1024        }
1025        for (i, a) in table.iter().enumerate() {
1026            if table[i + 1..].iter().any(|b| a.order(b).is_none_or(|o| o == Ordering::Equal)) {
1027                return None;
1028            }
1029        }
1030        let value = Multi::read_value(buf, &mut pos, &table, 0)?;
1031        (pos == buf.len() && value.to_bytes() == buf).then_some(value)
1032    }
1033
1034    fn read_value(buf: &[u8], pos: &mut usize, table: &[Arc<Scale>], depth: usize) -> Option<Multi> {
1035        if depth > MAX_WIRE_DEPTH {
1036            return None;
1037        }
1038        Some(match read_byte(buf, pos)? {
1039            0 => Multi::Standard(Algebraic::read(buf, pos)?),
1040            1 => {
1041                let s = table.get(usize::try_from(u64::read(buf, pos)?).ok()?)?;
1042                Multi::of_closed(Closed::decode(buf, pos)?, s)
1043            }
1044            2 => {
1045                let s = table.get(usize::try_from(u64::read(buf, pos)?).ok()?)?.clone();
1046                let q = u32::try_from(u64::read(buf, pos)?).ok().filter(|q| *q > 0)?;
1047                let mut polys = Vec::with_capacity(2);
1048                for _ in 0..2 {
1049                    let n = read_len(buf, pos, MAX_WIRE_LEN)?;
1050                    let mut c = Vec::with_capacity(n.min(64));
1051                    for _ in 0..n {
1052                        c.push(Multi::read_value(buf, pos, table, depth + 1)?);
1053                    }
1054                    polys.push(PolyOver::from(c));
1055                }
1056                let den = polys.pop().expect("two polynomials");
1057                let num = polys.pop().expect("two polynomials");
1058                if den.is_zero() {
1059                    return None;
1060                }
1061                let larger = num
1062                    .c
1063                    .iter()
1064                    .chain(den.c.iter())
1065                    .all(|c| c.scales().iter().all(|t| t.order(&s) == Some(Ordering::Greater)));
1066                if !larger {
1067                    return None;
1068                }
1069                Multi::make(&s, q, num, den)
1070            }
1071            3 => {
1072                let s = table.get(usize::try_from(u64::read(buf, pos)?).ok()?)?.clone();
1073                read_recipe(buf, pos, table, &s, depth + 1)?
1074            }
1075            _ => return None,
1076        })
1077    }
1078}
1079
1080/// Every infinitesimal the leaves of a recipe involve.
1081fn recipe_scales(recipe: &Recipe, out: &mut Vec<Arc<Scale>>) {
1082    match recipe {
1083        Recipe::Field(x) => x.collect_scales(out),
1084        Recipe::Closed(_) => {}
1085        Recipe::Add(a, b) | Recipe::Mul(a, b) => {
1086            recipe_scales(&a.recipe, out);
1087            recipe_scales(&b.recipe, out);
1088        }
1089        Recipe::Neg(a) | Recipe::Recip(a) | Recipe::Root(a, _) | Recipe::Pow(a, _) => recipe_scales(&a.recipe, out),
1090    }
1091}
1092
1093/// A recipe: a tag, then its parts — `0` a rational function and `1` a root of one infinitesimal at
1094/// the leaves, `2` a sum, `3` a negation, `4` a product, `5` a reciprocal, `6` a root and `7` a power,
1095/// the index or exponent after the value.
1096fn write_recipe(recipe: &Recipe, table: &[Arc<Scale>], out: &mut Vec<u8>) {
1097    match recipe {
1098        Recipe::Field(x) => {
1099            out.push(0);
1100            x.write_value(table, out);
1101        }
1102        Recipe::Closed(p) => {
1103            out.push(1);
1104            p.write(out);
1105        }
1106        Recipe::Add(a, b) => {
1107            out.push(2);
1108            write_recipe(&a.recipe, table, out);
1109            write_recipe(&b.recipe, table, out);
1110        }
1111        Recipe::Neg(a) => {
1112            out.push(3);
1113            write_recipe(&a.recipe, table, out);
1114        }
1115        Recipe::Mul(a, b) => {
1116            out.push(4);
1117            write_recipe(&a.recipe, table, out);
1118            write_recipe(&b.recipe, table, out);
1119        }
1120        Recipe::Recip(a) => {
1121            out.push(5);
1122            write_recipe(&a.recipe, table, out);
1123        }
1124        Recipe::Root(a, n) => {
1125            out.push(6);
1126            write_recipe(&a.recipe, table, out);
1127            u64::from(*n).write(out);
1128        }
1129        Recipe::Pow(a, k) => {
1130            out.push(7);
1131            write_recipe(&a.recipe, table, out);
1132            u64::from(*k).write(out);
1133        }
1134    }
1135}
1136
1137/// The value a recipe [`write_recipe`] wrote builds, at the infinitesimal `scale`.
1138fn read_recipe(buf: &[u8], pos: &mut usize, table: &[Arc<Scale>], scale: &Arc<Scale>, depth: usize) -> Option<Multi> {
1139    use crate::exact_codec::MAX_WIRE_DEGREE;
1140    if depth > MAX_WIRE_DEPTH {
1141        return None;
1142    }
1143    let operand = |m: Multi| Arc::new(AlgValue::at(&m, scale));
1144    Some(match read_byte(buf, pos)? {
1145        0 => Multi::read_value(buf, pos, table, depth + 1)?,
1146        1 => Multi::of_closed(Puiseux::read(buf, pos)?.canonical(), scale),
1147        2 => {
1148            let a = operand(read_recipe(buf, pos, table, scale, depth + 1)?);
1149            let b = operand(read_recipe(buf, pos, table, scale, depth + 1)?);
1150            AlgValue::add(&a, &b)
1151        }
1152        3 => AlgValue::neg(&operand(read_recipe(buf, pos, table, scale, depth + 1)?)),
1153        4 => {
1154            let a = read_recipe(buf, pos, table, scale, depth + 1)?;
1155            let b = read_recipe(buf, pos, table, scale, depth + 1)?;
1156            if a.is_zero() || b.is_zero() {
1157                return None;
1158            }
1159            AlgValue::mul(&operand(a), &operand(b))
1160        }
1161        5 => {
1162            let a = read_recipe(buf, pos, table, scale, depth + 1)?;
1163            if a.is_zero() {
1164                return None;
1165            }
1166            AlgValue::recip(&operand(a))
1167        }
1168        6 => {
1169            let a = read_recipe(buf, pos, table, scale, depth + 1)?;
1170            let n = u32::try_from(u64::read(buf, pos)?).ok().filter(|n| (2..=MAX_WIRE_DEGREE as u32).contains(n))?;
1171            if a.is_zero() {
1172                return None;
1173            }
1174            AlgValue::root(&operand(a), n).ok()?
1175        }
1176        7 => {
1177            let a = read_recipe(buf, pos, table, scale, depth + 1)?;
1178            let k = u32::try_from(u64::read(buf, pos)?).ok().filter(|k| (2..=MAX_POWER_DEGREE as u32).contains(k))?;
1179            if a.is_zero() {
1180                return None;
1181            }
1182            AlgValue::pow(&operand(a), k)
1183        }
1184        _ => return None,
1185    })
1186}
1187
1188fn write_text(text: &str, out: &mut Vec<u8>) {
1189    write_len(text.len(), out);
1190    out.extend_from_slice(text.as_bytes());
1191}
1192
1193fn read_text(buf: &[u8], pos: &mut usize) -> Option<String> {
1194    let n = read_len(buf, pos, MAX_WIRE_LEN)?;
1195    let bytes = buf.get(*pos..pos.checked_add(n)?)?;
1196    *pos += n;
1197    String::from_utf8(bytes.to_vec()).ok()
1198}
1199
1200#[cfg(test)]
1201#[path = "../../logicaffeine_tests/unit/logicaffeine_base/multi/tests.rs"]
1202mod tests;
1203
1204/// The terms of a polynomial in the reciprocals of infinitesimals with rational coefficients, each its
1205/// monomial — the infinitesimals and the powers of their reciprocals, `monomial` the part already
1206/// read — and its coefficient; `None` for a value that is no such polynomial.
1207fn reciprocal_terms(m: &Multi, monomial: &mut Vec<(Arc<Scale>, u32)>, out: &mut Vec<(Vec<(Arc<Scale>, u32)>, Rational)>) -> Option<()> {
1208    match m {
1209        Multi::Standard(a) => {
1210            let r = a.to_rational()?;
1211            if !r.is_zero() {
1212                out.push((monomial.clone(), r));
1213            }
1214        }
1215        Multi::One(c, s) => {
1216            let (h, q) = c.field()?;
1217            if q != 1 {
1218                return None;
1219            }
1220            for (j, r) in h.as_reciprocal_powers()?.into_iter().enumerate() {
1221                if r.is_zero() {
1222                    continue;
1223                }
1224                let mut term = monomial.clone();
1225                if j > 0 {
1226                    term.push((s.clone(), j as u32));
1227                }
1228                out.push((term, r));
1229            }
1230        }
1231        Multi::Over { scale, q, num, den } => {
1232            let d = den.order()?;
1233            if den.c.iter().enumerate().any(|(i, c)| i != d && !c.is_zero()) {
1234                return None;
1235            }
1236            for (i, c) in num.c.iter().enumerate() {
1237                if c.is_zero() {
1238                    continue;
1239                }
1240                if i > d || (d - i) % *q as usize != 0 {
1241                    return None;
1242                }
1243                let j = ((d - i) / *q as usize) as u32;
1244                if j > 0 {
1245                    monomial.push((scale.clone(), j));
1246                }
1247                reciprocal_terms(&c.mul(&den.coeff(d).recip()?), monomial, out)?;
1248                if j > 0 {
1249                    monomial.pop();
1250                }
1251            }
1252        }
1253        Multi::Alg(_) => return None,
1254    }
1255    Some(())
1256}
1257
1258/// The most joint residue classes [`Multi::is_integral`] reads.
1259const INTEGRAL_CLASSES: u64 = 1 << 16;