Skip to main content

logicaffeine_base/
ordered_field.rs

1//! Polynomials over an ordered field with exact arithmetic, the coefficient rings of the hyperreals.
2//!
3//! The hyperreals a program's infinitesimal generates are rational functions of it over the real
4//! algebraic numbers; a second infinitesimal smaller than every power of the first generates rational
5//! functions of itself over those, and so on. Each level is one variable over the field below, so the
6//! arithmetic every level needs (sums, products, Euclid's division and gcd, Yun's square-free
7//! decomposition) is written once here, over any [`OrderedField`].
8
9use std::fmt::Debug;
10use std::hash::Hash;
11
12use crate::algebraic::Algebraic;
13
14/// A field with exact arithmetic and a decided order, read from the sign of an element.
15pub trait OrderedField: Clone + PartialEq + Eq + Hash + Debug {
16    fn zero() -> Self;
17    fn one() -> Self;
18    fn from_i64(n: i64) -> Self;
19    fn is_zero(&self) -> bool;
20    fn add(&self, o: &Self) -> Self;
21    fn neg(&self) -> Self;
22    fn mul(&self, o: &Self) -> Self;
23    /// `None` for zero.
24    fn recip(&self) -> Option<Self>;
25    /// `−1`, `0` or `1`.
26    fn signum(&self) -> i32;
27
28    fn sub(&self, o: &Self) -> Self {
29        self.add(&o.neg())
30    }
31}
32
33impl OrderedField for Algebraic {
34    fn zero() -> Self {
35        Algebraic::from_i64(0)
36    }
37    fn one() -> Self {
38        Algebraic::from_i64(1)
39    }
40    fn from_i64(n: i64) -> Self {
41        Algebraic::from_i64(n)
42    }
43    fn is_zero(&self) -> bool {
44        Algebraic::is_zero(self)
45    }
46    fn add(&self, o: &Self) -> Self {
47        Algebraic::add(self, o)
48    }
49    fn neg(&self) -> Self {
50        Algebraic::neg(self)
51    }
52    fn mul(&self, o: &Self) -> Self {
53        Algebraic::mul(self, o)
54    }
55    fn recip(&self) -> Option<Self> {
56        Algebraic::recip(self)
57    }
58    fn signum(&self) -> i32 {
59        Algebraic::signum(self)
60    }
61    fn sub(&self, o: &Self) -> Self {
62        Algebraic::sub(self, o)
63    }
64}
65
66/// A polynomial in one variable over `K`, little-endian, no trailing zero.
67#[derive(Clone, Debug, PartialEq, Eq, Hash)]
68pub struct PolyOver<K> {
69    pub(crate) c: Vec<K>,
70}
71
72impl<K: OrderedField> PolyOver<K> {
73    pub fn from(c: Vec<K>) -> PolyOver<K> {
74        let mut c = c;
75        while c.last().is_some_and(K::is_zero) {
76            c.pop();
77        }
78        PolyOver { c }
79    }
80
81    pub fn constant(a: K) -> PolyOver<K> {
82        PolyOver::from(vec![a])
83    }
84
85    pub fn zero() -> PolyOver<K> {
86        PolyOver { c: Vec::new() }
87    }
88
89    pub fn one() -> PolyOver<K> {
90        PolyOver::constant(K::one())
91    }
92
93    /// The variable itself.
94    pub fn variable() -> PolyOver<K> {
95        PolyOver::from(vec![K::zero(), K::one()])
96    }
97
98    pub fn is_zero(&self) -> bool {
99        self.c.is_empty()
100    }
101
102    pub fn degree(&self) -> Option<usize> {
103        self.c.len().checked_sub(1)
104    }
105
106    /// The index of the lowest-order nonzero coefficient.
107    pub fn order(&self) -> Option<usize> {
108        self.c.iter().position(|a| !a.is_zero())
109    }
110
111    pub fn coeff(&self, i: usize) -> K {
112        self.c.get(i).cloned().unwrap_or_else(K::zero)
113    }
114
115    pub fn add(&self, o: &PolyOver<K>) -> PolyOver<K> {
116        let n = self.c.len().max(o.c.len());
117        PolyOver::from((0..n).map(|i| self.coeff(i).add(&o.coeff(i))).collect())
118    }
119
120    pub fn neg(&self) -> PolyOver<K> {
121        PolyOver {
122            c: self.c.iter().map(K::neg).collect(),
123        }
124    }
125
126    pub fn mul(&self, o: &PolyOver<K>) -> PolyOver<K> {
127        if self.is_zero() || o.is_zero() {
128            return PolyOver::zero();
129        }
130        let mut out = vec![K::zero(); self.c.len() + o.c.len() - 1];
131        for (i, a) in self.c.iter().enumerate() {
132            if a.is_zero() {
133                continue;
134            }
135            for (j, b) in o.c.iter().enumerate() {
136                if b.is_zero() {
137                    continue;
138                }
139                out[i + j] = out[i + j].add(&a.mul(b));
140            }
141        }
142        PolyOver::from(out)
143    }
144
145    pub fn scale(&self, k: &K) -> PolyOver<K> {
146        PolyOver::from(self.c.iter().map(|a| a.mul(k)).collect())
147    }
148
149    /// Euclid's division by a nonzero `d`: `(q, r)` with `self = q·d + r` and `deg r < deg d`.
150    pub fn div_rem(&self, d: &PolyOver<K>) -> (PolyOver<K>, PolyOver<K>) {
151        let dd = d.degree().expect("the divisor is nonzero");
152        let inv = d.c[dd].recip().expect("the leading coefficient is nonzero");
153        if self.c.len() <= dd {
154            return (PolyOver::zero(), self.clone());
155        }
156        let mut r = self.c.clone();
157        let mut q = vec![K::zero(); r.len() - dd];
158        for k in (0..q.len()).rev() {
159            let coef = r[k + dd].mul(&inv);
160            if !coef.is_zero() {
161                for (j, dj) in d.c.iter().enumerate() {
162                    r[k + j] = r[k + j].sub(&coef.mul(dj));
163                }
164            }
165            q[k] = coef;
166        }
167        r.truncate(dd);
168        (PolyOver::from(q), PolyOver::from(r))
169    }
170
171    pub fn monic(&self) -> PolyOver<K> {
172        match self.c.last() {
173            None => PolyOver::zero(),
174            Some(lc) => self.scale(&lc.recip().expect("nonzero")),
175        }
176    }
177
178    /// The monic greatest common divisor.
179    pub fn gcd(&self, o: &PolyOver<K>) -> PolyOver<K> {
180        let (mut a, mut b) = (self.monic(), o.monic());
181        while !b.is_zero() {
182            let r = a.div_rem(&b).1;
183            a = b;
184            b = r.monic();
185        }
186        a
187    }
188
189    pub fn derivative(&self) -> PolyOver<K> {
190        PolyOver::from(
191            self.c
192                .iter()
193                .enumerate()
194                .skip(1)
195                .map(|(i, a)| a.mul(&K::from_i64(i as i64)))
196                .collect(),
197        )
198    }
199
200    /// `lc · Π aᵢ^i` with each `aᵢ` monic and squarefree and the `aᵢ` coprime, by Yun's algorithm:
201    /// the leading coefficient and each `(aᵢ, i)` of positive degree.
202    pub fn squarefree_parts(&self) -> (K, Vec<(PolyOver<K>, usize)>) {
203        let lc = self.c.last().cloned().unwrap_or_else(K::zero);
204        if self.degree().unwrap_or(0) == 0 {
205            return (lc, Vec::new());
206        }
207        let f = self.monic();
208        let g = f.gcd(&f.derivative());
209        let mut b = f.div_rem(&g).0;
210        let c = f.derivative().div_rem(&g).0;
211        let mut d = c.add(&b.derivative().neg());
212        let mut parts = Vec::new();
213        let mut i = 1;
214        while b.degree().unwrap_or(0) > 0 {
215            let a = b.gcd(&d);
216            b = b.div_rem(&a).0;
217            let c = d.div_rem(&a).0;
218            d = c.add(&b.derivative().neg());
219            if a.degree().unwrap_or(0) > 0 {
220                parts.push((a, i));
221            }
222            i += 1;
223        }
224        (lc, parts)
225    }
226
227    pub fn eval(&self, t: &K) -> K {
228        self.c.iter().rev().fold(K::zero(), |acc, a| acc.mul(t).add(a))
229    }
230
231    /// `self(x^m)`.
232    pub fn spread(&self, m: usize) -> PolyOver<K> {
233        let mut c = vec![K::zero(); self.c.len().saturating_sub(1) * m + 1];
234        for (i, a) in self.c.iter().enumerate() {
235            c[i * m] = a.clone();
236        }
237        PolyOver::from(c)
238    }
239
240    /// The squarefree part, monic: `self / gcd(self, self′)`.
241    pub fn squarefree(&self) -> PolyOver<K> {
242        if self.degree().unwrap_or(0) == 0 {
243            return self.monic();
244        }
245        let g = self.gcd(&self.derivative());
246        self.div_rem(&g).0.monic()
247    }
248
249    /// `self(x + a)`, by Horner's rule.
250    pub fn shift(&self, a: &K) -> PolyOver<K> {
251        let linear = PolyOver::from(vec![a.clone(), K::one()]);
252        self.c
253            .iter()
254            .rev()
255            .fold(PolyOver::zero(), |acc, c| acc.mul(&linear).add(&PolyOver::constant(c.clone())))
256    }
257
258    /// `self(−x)`.
259    pub fn neg_var(&self) -> PolyOver<K> {
260        PolyOver::from(
261            self.c
262                .iter()
263                .enumerate()
264                .map(|(i, a)| if i % 2 == 1 { a.neg() } else { a.clone() })
265                .collect(),
266        )
267    }
268
269    /// `self / x^k` for the largest `k` that divides it: the polynomial without its root `0`.
270    pub fn without_zero_root(&self) -> PolyOver<K> {
271        match self.order() {
272            Some(k) => PolyOver::from(self.c[k..].to_vec()),
273            None => PolyOver::zero(),
274        }
275    }
276
277    /// `x^n·self(1/x)` for `n` the degree: the polynomial whose roots are the reciprocals of the
278    /// nonzero roots.
279    pub fn reversed(&self) -> PolyOver<K> {
280        let mut c = self.without_zero_root().c;
281        c.reverse();
282        PolyOver::from(c)
283    }
284
285    /// The resultant `lc^{deg o}·Π o(α)` over the roots `α` of `self`, by Euclid's algorithm.
286    pub fn resultant(&self, o: &PolyOver<K>) -> K {
287        let (Some(m), Some(n)) = (self.degree(), o.degree()) else {
288            return K::zero();
289        };
290        let power = |h: &K, k: usize| (0..k).fold(K::one(), |acc, _| acc.mul(h));
291        if n == 0 {
292            return power(&o.c[0], m);
293        }
294        if m == 0 {
295            return power(&self.c[0], n);
296        }
297        let r = self.div_rem(o).1;
298        let Some(dr) = r.degree() else {
299            return K::zero();
300        };
301        let v = power(&o.c[n], m - dr).mul(&o.resultant(&r));
302        if (m * n) % 2 == 1 {
303            v.neg()
304        } else {
305            v
306        }
307    }
308
309    /// The polynomial of degree at most `degree` whose value at each integer `j ≤ degree` is
310    /// `at(j)`, by Newton's divided differences.
311    pub fn interpolate(degree: usize, at: impl Fn(i64) -> K) -> PolyOver<K> {
312        let nodes: Vec<i64> = (0..=degree as i64).collect();
313        let mut values: Vec<K> = nodes.iter().map(|j| at(*j)).collect();
314        let n = nodes.len();
315        for j in 1..n {
316            for i in (j..n).rev() {
317                let gap = K::from_i64(nodes[i] - nodes[i - j]);
318                values[i] = values[i]
319                    .sub(&values[i - 1])
320                    .mul(&gap.recip().expect("the nodes are distinct"));
321            }
322        }
323        let mut out = PolyOver::constant(values[n - 1].clone());
324        for i in (0..n - 1).rev() {
325            let factor = PolyOver::from(vec![K::from_i64(-nodes[i]), K::one()]);
326            out = out.mul(&factor).add(&PolyOver::constant(values[i].clone()));
327        }
328        out
329    }
330
331    /// The monic polynomial whose roots are the sums `rᵢ + sⱼ` of a root of each, every pair once —
332    /// `Res_z(P(z), Q(y − z))` up to its leading coefficient — through power sums: the sums' `m`-th power
333    /// sum is `Σ_k C(m, k)·p_k(P)·p_(m−k)(Q)`.
334    pub fn sum_poly(p: &PolyOver<K>, q: &PolyOver<K>) -> PolyOver<K> {
335        let (Some(dp), Some(dq)) = (p.degree(), q.degree()) else {
336            return PolyOver::one();
337        };
338        let degree = dp * dq;
339        let (sp, sq) = (p.power_sums(degree), q.power_sums(degree));
340        let mut sums = vec![K::zero(); degree + 1];
341        for (m, sum) in sums.iter_mut().enumerate().skip(1) {
342            let mut choose = K::one();
343            for k in 0..=m {
344                if k > 0 {
345                    choose = choose
346                        .mul(&K::from_i64((m - k + 1) as i64))
347                        .mul(&K::from_i64(k as i64).recip().expect("a positive integer is nonzero"));
348                }
349                *sum = sum.add(&choose.mul(&sp[k]).mul(&sq[m - k]));
350            }
351        }
352        PolyOver::from_power_sums(&sums, degree)
353    }
354
355    /// The monic polynomial whose roots are the products `rᵢ·sⱼ` of a root of each, every pair once —
356    /// `Res_z(P(z), z^n·Q(y/z))` up to its leading coefficient — through power sums: the products'
357    /// `m`-th power sum is `p_m(P)·p_m(Q)`.
358    pub fn product_poly(p: &PolyOver<K>, q: &PolyOver<K>) -> PolyOver<K> {
359        let (Some(dp), Some(dq)) = (p.degree(), q.degree()) else {
360            return PolyOver::one();
361        };
362        let degree = dp * dq;
363        let (sp, sq) = (p.power_sums(degree), q.power_sums(degree));
364        let sums: Vec<K> = (0..=degree).map(|m| sp[m].mul(&sq[m])).collect();
365        PolyOver::from_power_sums(&sums, degree)
366    }
367
368    /// The power sums `s_0 … s_n` of the roots of the polynomial, `s_0` its degree, by Newton's
369    /// identities from its coefficients.
370    fn power_sums(&self, n: usize) -> Vec<K> {
371        let d = self.degree().unwrap_or(0);
372        let mut sums = vec![K::zero(); n + 1];
373        sums[0] = K::from_i64(d as i64);
374        if d == 0 {
375            return sums;
376        }
377        let lead = self.coeff(d).recip().expect("a leading coefficient is nonzero");
378        let a: Vec<K> = (0..=d).map(|i| if i == 0 { K::one() } else { self.coeff(d - i).mul(&lead) }).collect();
379        for m in 1..=n {
380            let mut acc = if m <= d { a[m].mul(&K::from_i64(m as i64)) } else { K::zero() };
381            for i in 1..m.min(d + 1) {
382                acc = acc.add(&a[i].mul(&sums[m - i]));
383            }
384            sums[m] = acc.neg();
385        }
386        sums
387    }
388
389    /// The monic polynomial of degree `d` whose roots have the power sums `sums[1] … sums[d]`.
390    fn from_power_sums(sums: &[K], d: usize) -> PolyOver<K> {
391        let mut b = vec![K::one(); d + 1];
392        for j in 1..=d {
393            let mut acc = sums[j].clone();
394            for i in 1..j {
395                acc = acc.add(&b[i].mul(&sums[j - i]));
396            }
397            b[j] = acc.mul(&K::from_i64(j as i64).recip().expect("a positive integer is nonzero")).neg();
398        }
399        PolyOver::from((0..=d).map(|i| b[d - i].clone()).collect())
400    }
401
402    /// The rational function `num/den` of numerator degree at most `l` and denominator degree at most
403    /// `m`, `den(0) ≠ 0`, whose expansion at 0 begins with `series` (its first `l + m + 1`
404    /// coefficients), by the extended Euclidean algorithm on `x^(l+m+1)` and the truncated series;
405    /// `None` when no such function exists.
406    pub fn pade(series: &[K], l: usize, m: usize) -> Option<(PolyOver<K>, PolyOver<K>)> {
407        let n = l + m + 1;
408        let mut top = vec![K::zero(); n + 1];
409        top[n] = K::one();
410        let truncated: Vec<K> = (0..n).map(|i| series.get(i).cloned().unwrap_or_else(K::zero)).collect();
411        let (mut r0, mut r1) = (PolyOver::from(top), PolyOver::from(truncated));
412        let (mut t0, mut t1) = (PolyOver::zero(), PolyOver::one());
413        while r1.degree().is_some_and(|d| d > l) {
414            let (q, r) = r0.div_rem(&r1);
415            let t = t0.add(&q.mul(&t1).neg());
416            r0 = std::mem::replace(&mut r1, r);
417            t0 = std::mem::replace(&mut t1, t);
418        }
419        if t1.degree().unwrap_or(0) > m || t1.coeff(0).is_zero() {
420            return None;
421        }
422        Some((r1, t1))
423    }
424
425    /// The monic polynomial whose roots are the `k`-th powers of the roots of `P`, each as often as in
426    /// `P` — `Res_z(P(z), y − z^k)` up to its leading coefficient. A binomial `P = a·zⁿ + b`, whose roots
427    /// are the `n`-th roots of `c = −b/a`, has their `k`-th powers the roots of `y^(n/g) − c^(k/g)` for
428    /// `g = gcd(n, k)`, read off at once. Any other is read through the power sums of its roots: by
429    /// Newton's identities the coefficients give `s₁ … s_{dk}`, the `k`-th powers have the power sums
430    /// `s_k, s_{2k}, …, s_{dk}`, and the same identities give their coefficients back — `O(d²k)` field
431    /// operations, no determinant.
432    pub fn power_poly(p: &PolyOver<K>, k: u32) -> PolyOver<K> {
433        let Some(d) = p.degree().filter(|d| *d >= 1) else {
434            return p.clone();
435        };
436        if (1..d).all(|i| p.coeff(i).is_zero()) {
437            let c = p.coeff(0).neg().mul(&p.coeff(d).recip().expect("a leading coefficient is nonzero"));
438            let (mut a, mut b) = (d as u64, u64::from(k));
439            while b != 0 {
440                (a, b) = (b, a % b);
441            }
442            let (degree, exponent) = (d / a as usize, u64::from(k) / a);
443            let power = (0..exponent).fold(K::one(), |acc, _| acc.mul(&c));
444            let mut out = vec![K::zero(); degree + 1];
445            out[0] = power.neg();
446            out[degree] = K::one();
447            return PolyOver::from(out);
448        }
449        let k = k as usize;
450        let sums = p.power_sums(d * k);
451        let powers: Vec<K> = (0..=d).map(|j| sums[j * k].clone()).collect();
452        PolyOver::from_power_sums(&powers, d)
453    }
454}
455
456#[cfg(test)]
457#[path = "../../logicaffeine_tests/unit/logicaffeine_base/ordered_field/tests.rs"]
458mod tests;