Skip to main content

malachite_base/unsigned_polynomial/arithmetic/
evaluate.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::num::arithmetic::mod_mul::{
10    mod_mul_precompute_shoup, mod_mul_shoup, mod_mul_shoup_lazy,
11};
12use crate::num::arithmetic::traits::{ModIsReduced, ModPowerOf2IsReduced};
13use crate::num::basic::integers::PrimitiveInt;
14use crate::num::basic::unsigneds::PrimitiveUnsigned;
15use crate::num::conversion::traits::ExactFrom;
16use crate::polynomial::{ModEvaluate, ModEvaluateGeometric, ModEvaluateMany, ModPowerOf2Evaluate};
17use crate::unsigned_polynomial::UnsignedPolynomial;
18use crate::unsigned_polynomial::arithmetic::mod_mul::ModData;
19use crate::unsigned_polynomial::arithmetic::mod_mul_middle::mod_mul_middle_karatsuba;
20use crate::unsigned_polynomial::arithmetic::mod_mul_truncated::mod_mul_truncated_to_out;
21use alloc::vec;
22use alloc::vec::Vec;
23use core::cmp::max;
24
25// Evaluates a polynomial at x modulo 2^pow, after checking that pow fits in `T` and that the
26// coefficients and x are reduced.
27fn mod_power_of_2_evaluate<T: PrimitiveUnsigned>(p: &UnsignedPolynomial<T>, x: T, pow: u64) -> T {
28    assert!(pow <= T::WIDTH);
29    assert!(
30        p.mod_power_of_2_is_reduced(pow),
31        "self must be reduced mod 2^pow, but {p} has a coefficient >= 2^{pow}"
32    );
33    assert!(
34        x.significant_bits() <= pow,
35        "x must be reduced mod 2^pow, but {x} >= 2^{pow}"
36    );
37    let mut value = T::ZERO;
38    for &c in p.coefficients.iter().rev() {
39        value = value.wrapping_mul(x).wrapping_add(c);
40    }
41    value.mod_power_of_2(pow)
42}
43
44impl<T: PrimitiveUnsigned> ModPowerOf2Evaluate<T> for &UnsignedPolynomial<T> {
45    type Output = T;
46
47    /// Evaluates an [`UnsignedPolynomial`] at a value of its coefficient type, modulo $2^k$, taking
48    /// the polynomial by reference. The coefficients and the value must already be reduced modulo
49    /// $2^k$, and $k$ may be at most the width of the type.
50    ///
51    /// $$
52    /// f(p, x, k) = \sum_{i=0}^{n-1} c_i x^i \bmod 2^k,
53    /// $$
54    ///
55    /// where $c_i$ is the coefficient of $x^i$ in $p$ and $n$ is its length. The zero polynomial
56    /// evaluates to 0 everywhere.
57    ///
58    /// Reducing modulo $2^k$ commutes with wrapping arithmetic, which works modulo $2^w$ for the
59    /// type's width $w \geq k$, so Horner's rule is carried out with wrapping multiplications and
60    /// additions and the value is reduced once, at the end.
61    ///
62    /// # Worst-case complexity
63    /// $T(n) = O(n)$
64    ///
65    /// $M(n) = O(1)$
66    ///
67    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.len()`.
68    ///
69    /// # Panics
70    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `x` is
71    /// greater than or equal to $2^k$.
72    ///
73    /// # Examples
74    /// ```
75    /// use core::str::FromStr;
76    /// use malachite_base::polynomial::ModPowerOf2Evaluate;
77    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
78    ///
79    /// let p = UnsignedPolynomial::<u8>::from_str("5*x^2+3*x+7").unwrap();
80    /// // 5 * 36 + 3 * 6 + 7 = 205, which is 13 mod 16.
81    /// assert_eq!((&p).mod_power_of_2_evaluate(6, 4), 13);
82    /// assert_eq!((&p).mod_power_of_2_evaluate(0, 4), 7);
83    /// // All 8 bits of a u8: 205 itself.
84    /// assert_eq!((&p).mod_power_of_2_evaluate(6, 8), 205);
85    /// ```
86    ///
87    /// This is equivalent to `nmod_poly_evaluate_nmod` from `nmod_poly/evaluate_nmod.c`, FLINT
88    /// 3.6.0, with the modulus $2^k$, except that the value must be reduced.
89    #[inline]
90    fn mod_power_of_2_evaluate(self, x: T, pow: u64) -> T {
91        mod_power_of_2_evaluate(self, x, pow)
92    }
93}
94
95impl<T: PrimitiveUnsigned> ModPowerOf2Evaluate<T> for UnsignedPolynomial<T> {
96    type Output = T;
97
98    /// Evaluates an [`UnsignedPolynomial`] at a value of its coefficient type, modulo $2^k$, taking
99    /// the polynomial by value. The coefficients and the value must already be reduced modulo
100    /// $2^k$, and $k$ may be at most the width of the type.
101    ///
102    /// $$
103    /// f(p, x, k) = \sum_{i=0}^{n-1} c_i x^i \bmod 2^k,
104    /// $$
105    ///
106    /// where $c_i$ is the coefficient of $x^i$ in $p$ and $n$ is its length. The zero polynomial
107    /// evaluates to 0 everywhere.
108    ///
109    /// Reducing modulo $2^k$ commutes with wrapping arithmetic, which works modulo $2^w$ for the
110    /// type's width $w \geq k$, so Horner's rule is carried out with wrapping multiplications and
111    /// additions and the value is reduced once, at the end.
112    ///
113    /// # Worst-case complexity
114    /// $T(n) = O(n)$
115    ///
116    /// $M(n) = O(1)$
117    ///
118    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.len()`.
119    ///
120    /// # Panics
121    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `x` is
122    /// greater than or equal to $2^k$.
123    ///
124    /// # Examples
125    /// ```
126    /// use core::str::FromStr;
127    /// use malachite_base::polynomial::ModPowerOf2Evaluate;
128    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
129    ///
130    /// let p = UnsignedPolynomial::<u8>::from_str("5*x^2+3*x+7").unwrap();
131    /// // 5 * 36 + 3 * 6 + 7 = 205, which is 13 mod 16.
132    /// assert_eq!(p.clone().mod_power_of_2_evaluate(6, 4), 13);
133    /// assert_eq!(p.clone().mod_power_of_2_evaluate(0, 4), 7);
134    /// // All 8 bits of a u8: 205 itself.
135    /// assert_eq!(p.mod_power_of_2_evaluate(6, 8), 205);
136    /// ```
137    ///
138    /// This is equivalent to `nmod_poly_evaluate_nmod` from `nmod_poly/evaluate_nmod.c`, FLINT
139    /// 3.6.0, with the modulus $2^k$, except that the value must be reduced.
140    #[inline]
141    fn mod_power_of_2_evaluate(self, x: T, pow: u64) -> T {
142        mod_power_of_2_evaluate(&self, x, pow)
143    }
144}
145
146// The shortest polynomial evaluated with Shoup's method, when `T` is wider than 32 bits: Shoup's
147// precomputation is a two-by-one division, which is only paid back from this length on. For
148// narrower types every polynomial of length 2 or more uses it. Tuned on Apple M-series for `u64`
149// and `u128`; FLINT's `FLINT_MULMOD_SHOUP_THRESHOLD` is 10.
150const MOD_EVALUATE_SHOUP_THRESHOLD: usize = 3;
151
152// Evaluates a polynomial at `x` modulo `m` with Horner's rule, reducing after every step.
153// `coefficients` must be nonempty.
154//
155// This is equivalent to `_nmod_poly_evaluate_nmod_horner` from `nmod_poly/evaluate_nmod.c`, FLINT
156// 3.6.0.
157crate_test_fn! {mod_evaluate_horner<T: PrimitiveUnsigned>(coefficients: &[T], x: T, m: T) -> T {
158    let data = T::precompute_mod_mul_data(&m);
159    let (&last, rest) = coefficients.split_last().unwrap();
160    let mut value = last;
161    for &c in rest.iter().rev() {
162        value.mod_mul_precomputed_assign(x, m, &data);
163        value.mod_add_assign(c, m);
164    }
165    value
166}}
167
168// Evaluates a polynomial at `x` modulo `m` with Horner's rule, multiplying by `x` with Shoup's
169// method. `coefficients` must be nonempty, `x_precomp` must be `mod_mul_precompute_shoup(x, m)`,
170// and the top bit of `m` must be clear.
171//
172// This is equivalent to `_nmod_poly_evaluate_nmod_precomp` from `nmod_poly/evaluate_nmod.c`, FLINT
173// 3.6.0.
174crate_test_fn! {mod_evaluate_shoup<T: PrimitiveUnsigned>(
175    coefficients: &[T],
176    x: T,
177    x_precomp: T,
178    m: T,
179) -> T {
180    let (&last, rest) = coefficients.split_last().unwrap();
181    let mut value = last;
182    for &c in rest.iter().rev() {
183        value = mod_mul_shoup(x, value, x_precomp, m);
184        value.mod_add_assign(c, m);
185    }
186    value
187}}
188
189// Evaluates a polynomial at `x` modulo `m` like `mod_evaluate_shoup`, but reduces only partially:
190// the result is congruent to the polynomial's value and less than $3m - 1$. `coefficients` must be
191// nonempty, `x_precomp` must be `mod_mul_precompute_shoup(x, m)`, and `m` must be at most `T::MAX /
192// 3`, so that $3m - 1$ values fit.
193//
194// This is equivalent to `_nmod_poly_evaluate_nmod_precomp_lazy` from `nmod_poly/evaluate_nmod.c`,
195// FLINT 3.6.0.
196crate_test_fn! {mod_evaluate_shoup_lazy<T: PrimitiveUnsigned>(
197    coefficients: &[T],
198    x: T,
199    x_precomp: T,
200    m: T,
201) -> T {
202    let (&last, rest) = coefficients.split_last().unwrap();
203    let mut value = last;
204    for &c in rest.iter().rev() {
205        // value is x * value mod m, or that plus m
206        value = mod_mul_shoup_lazy(x, value, x_precomp, m);
207        // value is now less than 3m - 1, since c < m
208        value.wrapping_add_assign(c);
209    }
210    value
211}}
212
213// Evaluates the polynomial with the given coefficients at `x` modulo `m`. The coefficients and `x`
214// must be less than `m`; this is not checked. It is public, but hidden, because malachite-nz
215// evaluates an `IntegerPolynomial` modulo a word with it, after reducing the coefficients.
216//
217// This is equivalent to `_nmod_poly_evaluate_nmod` from `nmod_poly/evaluate_nmod.c`, FLINT 3.6.0,
218// except that rectangular splitting is not used.
219#[doc(hidden)]
220pub fn mod_evaluate_slice<T: PrimitiveUnsigned>(coefficients: &[T], x: T, m: T) -> T {
221    let len = coefficients.len();
222    if len == 0 {
223        return T::ZERO;
224    }
225    if len == 1 || x == T::ZERO {
226        return coefficients[0];
227    }
228    // Shoup's method needs the top bit of m clear
229    if m.get_highest_bit() || (T::WIDTH > u32::WIDTH && len < MOD_EVALUATE_SHOUP_THRESHOLD) {
230        return mod_evaluate_horner(coefficients, x, m);
231    }
232    let x_precomp = mod_mul_precompute_shoup(x, m);
233    // The lazy loop's values are less than 3m - 1, so it is used when those fit: when m <= (2^W +
234    // 1) / 3, which is T::MAX / 3 since every width W is even. FLINT calls this bound LAZY_MAX.
235    if m <= T::MAX / T::from(3u8) {
236        let mut value = mod_evaluate_shoup_lazy(coefficients, x, x_precomp, m);
237        // correct the excess
238        let two_m = m << 1;
239        if value >= two_m {
240            value -= two_m;
241        } else if value >= m {
242            value -= m;
243        }
244        value
245    } else {
246        mod_evaluate_shoup(coefficients, x, x_precomp, m)
247    }
248}
249
250fn mod_evaluate<T: PrimitiveUnsigned>(p: &UnsignedPolynomial<T>, x: T, m: T) -> T {
251    assert!(
252        p.mod_is_reduced(&m),
253        "self must be reduced mod m, but {p} has a coefficient >= {m}"
254    );
255    assert!(x < m, "x must be reduced mod m, but {x} >= {m}");
256    mod_evaluate_slice(&p.coefficients, x, m)
257}
258
259impl<T: PrimitiveUnsigned> ModEvaluate<T> for &UnsignedPolynomial<T> {
260    type Output = T;
261
262    /// Evaluates an [`UnsignedPolynomial`] at a value of its coefficient type, modulo a value of
263    /// that type, taking the polynomial by reference. The coefficients and the value must already
264    /// be reduced modulo `m`.
265    ///
266    /// $$
267    /// f(p, x, m) = \sum_{i=0}^{n-1} c_i x^i \bmod m,
268    /// $$
269    ///
270    /// where $c_i$ is the coefficient of $x^i$ in $p$ and $n$ is its length. The zero polynomial
271    /// evaluates to 0 everywhere.
272    ///
273    /// The value is found with Horner's rule, reducing after every step. When the polynomial is
274    /// long enough and the top bit of `m` is clear, every multiplication is by the same `x`, so
275    /// Shoup's method is used: $\lfloor x 2^W / m \rfloor$, where $W$ is the width of `T`, is
276    /// computed once, and each product is then reduced with a multiplication in place of a
277    /// division. When $m \leq (2^W - 1) / 3$ the reductions are also lazy, leaving the value below
278    /// $3m - 1$ until the end.
279    ///
280    /// # Worst-case complexity
281    /// $T(n) = O(n)$
282    ///
283    /// $M(n) = O(1)$
284    ///
285    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.len()`.
286    ///
287    /// # Panics
288    /// Panics if `m` is 0, or if any coefficient of `self` or `x` is greater than or equal to `m`.
289    ///
290    /// # Examples
291    /// ```
292    /// use core::str::FromStr;
293    /// use malachite_base::polynomial::ModEvaluate;
294    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
295    ///
296    /// let p = UnsignedPolynomial::<u8>::from_str("5*x^2+3*x+7").unwrap();
297    /// // 5 * 36 + 3 * 6 + 7 = 205, which is 10 mod 13.
298    /// assert_eq!((&p).mod_evaluate(6, 13), 10);
299    /// assert_eq!((&p).mod_evaluate(0, 13), 7);
300    /// // 205 itself, modulo a larger modulus.
301    /// assert_eq!((&p).mod_evaluate(6, 211), 205);
302    /// ```
303    ///
304    /// This is equivalent to `nmod_poly_evaluate_nmod` from `nmod_poly/evaluate_nmod.c`, FLINT
305    /// 3.6.0, except that the value must be reduced.
306    #[inline]
307    fn mod_evaluate(self, x: T, m: T) -> T {
308        mod_evaluate(self, x, m)
309    }
310}
311
312impl<T: PrimitiveUnsigned> ModEvaluate<T> for UnsignedPolynomial<T> {
313    type Output = T;
314
315    /// Evaluates an [`UnsignedPolynomial`] at a value of its coefficient type, modulo a value of
316    /// that type, taking the polynomial by value. The coefficients and the value must already be
317    /// reduced modulo `m`.
318    ///
319    /// $$
320    /// f(p, x, m) = \sum_{i=0}^{n-1} c_i x^i \bmod m,
321    /// $$
322    ///
323    /// where $c_i$ is the coefficient of $x^i$ in $p$ and $n$ is its length. The zero polynomial
324    /// evaluates to 0 everywhere.
325    ///
326    /// The value is found with Horner's rule, reducing after every step. When the polynomial is
327    /// long enough and the top bit of `m` is clear, every multiplication is by the same `x`, so
328    /// Shoup's method is used: $\lfloor x 2^W / m \rfloor$, where $W$ is the width of `T`, is
329    /// computed once, and each product is then reduced with a multiplication in place of a
330    /// division. When $m \leq (2^W - 1) / 3$ the reductions are also lazy, leaving the value below
331    /// $3m - 1$ until the end.
332    ///
333    /// # Worst-case complexity
334    /// $T(n) = O(n)$
335    ///
336    /// $M(n) = O(1)$
337    ///
338    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.len()`.
339    ///
340    /// # Panics
341    /// Panics if `m` is 0, or if any coefficient of `self` or `x` is greater than or equal to `m`.
342    ///
343    /// # Examples
344    /// ```
345    /// use core::str::FromStr;
346    /// use malachite_base::polynomial::ModEvaluate;
347    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
348    ///
349    /// let p = UnsignedPolynomial::<u8>::from_str("5*x^2+3*x+7").unwrap();
350    /// // 5 * 36 + 3 * 6 + 7 = 205, which is 10 mod 13.
351    /// assert_eq!(p.clone().mod_evaluate(6, 13), 10);
352    /// assert_eq!(p.clone().mod_evaluate(0, 13), 7);
353    /// // 205 itself, modulo a larger modulus.
354    /// assert_eq!(p.mod_evaluate(6, 211), 205);
355    /// ```
356    ///
357    /// This is equivalent to `nmod_poly_evaluate_nmod` from `nmod_poly/evaluate_nmod.c`, FLINT
358    /// 3.6.0, except that the value must be reduced.
359    #[inline]
360    fn mod_evaluate(self, x: T, m: T) -> T {
361        mod_evaluate(&self, x, m)
362    }
363}
364
365// The numbers of points evaluated together by `mod_evaluate_many_in_place`, for types at most 32
366// bits wide and for wider types. Horner's rule is a chain of dependent multiplications, so running
367// several points through one pass over the coefficients keeps more multiplications in flight. Tuned
368// on Apple M-series: for `u64` a block of 8 is 3.4 to 4.8 times as fast as one point at a time at
369// length 256, and for `u32` a block of 4 is best.
370const MOD_EVALUATE_MANY_NARROW_BLOCK: usize = 4;
371const MOD_EVALUATE_MANY_WIDE_BLOCK: usize = 8;
372
373// Replaces each of the `N` points in `xs` with the polynomial's value there modulo `m`, with
374// Horner's rule for all of them in one pass over the coefficients. `coefficients` must be nonempty,
375// and the points and coefficients reduced.
376crate_test_fn! {mod_evaluate_horner_block<T: PrimitiveUnsigned, const N: usize>(
377    coefficients: &[T],
378    xs: &mut [T; N],
379    m: T,
380) {
381    let data = T::precompute_mod_mul_data(&m);
382    let (&last, rest) = coefficients.split_last().unwrap();
383    let points = *xs;
384    let mut values = [last; N];
385    for &c in rest.iter().rev() {
386        for (value, &x) in values.iter_mut().zip(points.iter()) {
387            value.mod_mul_precomputed_assign(x, m, &data);
388            value.mod_add_assign(c, m);
389        }
390    }
391    *xs = values;
392}}
393
394// Like `mod_evaluate_horner_block`, multiplying by each point with Shoup's method. The top bit of
395// `m` must be clear.
396crate_test_fn! {mod_evaluate_shoup_block<T: PrimitiveUnsigned, const N: usize>(
397    coefficients: &[T],
398    xs: &mut [T; N],
399    m: T,
400) {
401    let points = *xs;
402    let precomps = points.map(|x| mod_mul_precompute_shoup(x, m));
403    let (&last, rest) = coefficients.split_last().unwrap();
404    let mut values = [last; N];
405    for &c in rest.iter().rev() {
406        for ((value, &x), &x_precomp) in values.iter_mut().zip(points.iter()).zip(precomps.iter()) {
407            *value = mod_mul_shoup(x, *value, x_precomp, m);
408            value.mod_add_assign(c, m);
409        }
410    }
411    *xs = values;
412}}
413
414// Like `mod_evaluate_shoup_block`, with lazy reduction as in `mod_evaluate_shoup_lazy`. `m` must be
415// at most `T::MAX / 3`. The values are fully reduced at the end.
416crate_test_fn! {mod_evaluate_shoup_lazy_block<T: PrimitiveUnsigned, const N: usize>(
417    coefficients: &[T],
418    xs: &mut [T; N],
419    m: T,
420) {
421    let points = *xs;
422    let precomps = points.map(|x| mod_mul_precompute_shoup(x, m));
423    let (&last, rest) = coefficients.split_last().unwrap();
424    let mut values = [last; N];
425    for &c in rest.iter().rev() {
426        for ((value, &x), &x_precomp) in values.iter_mut().zip(points.iter()).zip(precomps.iter()) {
427            // value is x * value mod m, or that plus m, and then less than 3m - 1
428            *value = mod_mul_shoup_lazy(x, *value, x_precomp, m);
429            value.wrapping_add_assign(c);
430        }
431    }
432    let two_m = m << 1;
433    for value in &mut values {
434        if *value >= two_m {
435            *value -= two_m;
436        } else if *value >= m {
437            *value -= m;
438        }
439    }
440    *xs = values;
441}}
442
443// Replaces each point in `xs` with the polynomial's value there modulo `m`. The points and the
444// coefficients must be reduced; this is not checked. Blocks of points are evaluated together, with
445// the method `mod_evaluate_slice` would choose for one point. Evaluates the points in `xs` in
446// blocks of `N`, with the method chosen by `mod_evaluate_many_in_place`, and returns the leftover
447// points, fewer than `N` of them.
448fn mod_evaluate_many_blocks<'a, T: PrimitiveUnsigned, const N: usize>(
449    coefficients: &[T],
450    xs: &'a mut [T],
451    m: T,
452    shoup: bool,
453    lazy: bool,
454) -> &'a mut [T] {
455    let (blocks, remainder) = xs.as_chunks_mut::<N>();
456    for block in blocks {
457        if !shoup {
458            mod_evaluate_horner_block(coefficients, block, m);
459        } else if lazy {
460            mod_evaluate_shoup_lazy_block(coefficients, block, m);
461        } else {
462            mod_evaluate_shoup_block(coefficients, block, m);
463        }
464    }
465    remainder
466}
467
468// Replaces each point in `xs` with the polynomial's value there modulo `m`. The points and the
469// coefficients must be reduced; this is not checked. Blocks of points are evaluated together, with
470// the method `mod_evaluate_slice` would choose for one point.
471crate_test_fn! {mod_evaluate_many_in_place<T: PrimitiveUnsigned>(
472    coefficients: &[T],
473    xs: &mut [T],
474    m: T,
475) {
476    let len = coefficients.len();
477    match len {
478        0 => xs.fill(T::ZERO),
479        1 => xs.fill(coefficients[0]),
480        _ => {
481            // As in mod_evaluate_slice: Shoup's method needs the top bit of m clear, and is used
482            // for short polynomials only when T is at most 32 bits wide
483            let shoup = !m.get_highest_bit()
484                && (T::WIDTH <= u32::WIDTH || len >= MOD_EVALUATE_SHOUP_THRESHOLD);
485            let lazy = m <= T::MAX / T::from(3u8);
486            // Wider types take blocks of the wide size first; the leftover points go through blocks
487            // of the narrow size, and then one at a time
488            let xs = if T::WIDTH <= u32::WIDTH {
489                xs
490            } else {
491                mod_evaluate_many_blocks::<T, MOD_EVALUATE_MANY_WIDE_BLOCK>(
492                    coefficients,
493                    xs,
494                    m,
495                    shoup,
496                    lazy,
497                )
498            };
499            let remainder = mod_evaluate_many_blocks::<T, MOD_EVALUATE_MANY_NARROW_BLOCK>(
500                coefficients,
501                xs,
502                m,
503                shoup,
504                lazy,
505            );
506            for x in remainder {
507                *x = mod_evaluate_slice(coefficients, *x, m);
508            }
509        }
510    }
511}}
512
513impl<T: PrimitiveUnsigned> ModEvaluateMany<T> for &UnsignedPolynomial<T> {
514    type Output = T;
515
516    /// Evaluates an [`UnsignedPolynomial`] at each of several values of its coefficient type,
517    /// modulo a value of that type. The coefficients and the values must already be reduced modulo
518    /// `m`.
519    ///
520    /// $$
521    /// f(p, (x_j)_{j=0}^{k-1}, m) = \left ( \sum_{i=0}^{n-1} c_i x_j^i \bmod m
522    /// \right )_{j=0}^{k-1},
523    /// $$
524    ///
525    /// where $c_i$ is the coefficient of $x^i$ in $p$ and $n$ is its length.
526    ///
527    /// The result is the same as calling
528    /// [`mod_evaluate`](crate::polynomial::ModEvaluate::mod_evaluate) at each value, with the same
529    /// choice between Horner's rule and Shoup's method, but the polynomial is checked once, and
530    /// several values are evaluated together in each pass over the coefficients. Horner's rule is a
531    /// chain of dependent multiplications, so interleaving independent chains keeps the processor's
532    /// multipliers busy.
533    ///
534    /// # Worst-case complexity
535    /// $T(n, k) = O(nk)$
536    ///
537    /// $M(k) = O(k)$
538    ///
539    /// where $T$ is time, $M$ is additional memory, $n$ is `self.len()`, and $k$ is `xs.len()`.
540    ///
541    /// # Panics
542    /// Panics if `m` is 0, or if any coefficient of `self` or any value in `xs` is greater than or
543    /// equal to `m`.
544    ///
545    /// # Examples
546    /// ```
547    /// use core::str::FromStr;
548    /// use malachite_base::polynomial::ModEvaluateMany;
549    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
550    ///
551    /// let p = UnsignedPolynomial::<u8>::from_str("5*x^2+3*x+7").unwrap();
552    /// // 7, 15, 33, 61, 99, and 147, mod 13
553    /// assert_eq!(
554    ///     (&p).mod_evaluate_many(&[0, 1, 2, 3, 4, 5], 13),
555    ///     &[7, 2, 7, 9, 8, 4]
556    /// );
557    /// ```
558    ///
559    /// This is equivalent to `nmod_poly_evaluate_nmod_vec_iter` from
560    /// `nmod_poly/evaluate_nmod_vec.c`, FLINT 3.6.0, except that the values must be reduced.
561    fn mod_evaluate_many(self, xs: &[T], m: T) -> Vec<T> {
562        assert!(
563            self.mod_is_reduced(&m),
564            "self must be reduced mod m, but {self} has a coefficient >= {m}"
565        );
566        for &x in xs {
567            assert!(x < m, "x must be reduced mod m, but {x} >= {m}");
568        }
569        let mut values = xs.to_vec();
570        mod_evaluate_many_in_place(&self.coefficients, &mut values, m);
571        values
572    }
573}
574
575// Whether geometric evaluation of a polynomial of length `n` at `k` points modulo `m` uses
576// Bluestein's trick rather than evaluating at each power of q: when the polynomial and the number
577// of points are both long enough. Evaluating at each power is fast with Shoup's multiplication, and
578// fastest with its lazy form, which needs the top two bits of `m` clear; when the top bit is set,
579// Shoup's multiplication is unavailable and Bluestein's trick wins much sooner. The middle product
580// accumulates in three words once `m` has more than about W - 8 bits, where W is `T::WIDTH`, which
581// slows it. Measured on an Apple M-series machine, 2026-10, for `u64` with moduli of 20, 30, 40,
582// 50, 62, 63, and 64 bits; the boundaries for other types are scaled by their width without being
583// measured.
584fn mod_evaluate_geometric_fast_preferred<T: PrimitiveUnsigned>(n: usize, k: usize, m: T) -> bool {
585    let bits = m.significant_bits();
586    let width = T::WIDTH;
587    let (min_n, min_k) = if bits == width {
588        (32, 32)
589    } else if bits == width - 1 {
590        (256, 256)
591    } else if bits + 8 > width {
592        (512, 1024)
593    } else if bits << 4 > width * 5 {
594        (256, 256)
595    } else {
596        (128, 128)
597    };
598    n >= min_n && k >= min_k
599}
600
601// Evaluates the polynomial with the given coefficients, reduced modulo `m`, at $1, q, q^2, \ldots,
602// q^{k-1}$ modulo `m`, generating the powers of q and then evaluating at them all, several points
603// at a time.
604//
605// This is equivalent to `_nmod_poly_evaluate_geometric_nmod_vec_iter` from
606// `nmod_poly/evaluate_geometric_nmod_vec.c`, FLINT 3.6.0, with the ratio q in place of FLINT's
607// $r^2$.
608crate_test_fn! {mod_evaluate_geometric_iter<T: PrimitiveUnsigned>(
609    coefficients: &[T],
610    q: T,
611    k: usize,
612    m: T,
613) -> Vec<T> {
614    let mut values = Vec::with_capacity(k);
615    if k != 0 {
616        let mut power = T::ONE % m;
617        values.push(power);
618        if m.get_highest_bit() {
619            let data = T::precompute_mod_mul_data(&m);
620            for _ in 1..k {
621                power.mod_mul_precomputed_assign(q, m, &data);
622                values.push(power);
623            }
624        } else {
625            let q_precomp = mod_mul_precompute_shoup(q, m);
626            for _ in 1..k {
627                power = mod_mul_shoup(q, power, q_precomp, m);
628                values.push(power);
629            }
630        }
631    }
632    mod_evaluate_many_in_place(coefficients, &mut values, m);
633    values
634}}
635
636// Evaluates the polynomial with the given coefficients, reduced modulo `m`, at $1, q, q^2, \ldots,
637// q^{k-1}$ modulo `m` with Bluestein's trick, where `q_inverse` is the inverse of q modulo `m`.
638//
639// With $\binom{t}{2} = t(t-1)/2$, every product $ij$ is $\binom{i+j}{2} - \binom{i}{2} -
640// \binom{j}{2}$, so
641// $$
642// \sum_i c_i q^{ij} = q^{-\binom{j}{2}} \sum_i \left ( c_i q^{-\binom{i}{2}} \right )
643// q^{\binom{i+j}{2}}.
644// $$
645// The sum is coefficient $n - 1 + j$ of the product of the reverse of $(c_i q^{-\binom{i}{2}})_i$
646// and $(q^{\binom{t}{2}})_t$, so all $k$ values are one middle product, the $k$ coefficients of
647// that product from coefficient $n - 1$ on. Leading zero coefficients of the polynomial are
648// skipped, shortening $n$.
649//
650// This is equivalent to `_nmod_poly_evaluate_geometric_nmod_vec_fast` from
651// `nmod_poly/evaluate_geometric_nmod_vec.c`, FLINT 3.6.0, with exponents $\binom{t}{2}$ in place of
652// FLINT's $t^2/2$, so that no square root of q is needed.
653crate_test_fn! {mod_evaluate_geometric_fast<T: PrimitiveUnsigned>(
654    coefficients: &[T],
655    q: T,
656    q_inverse: T,
657    k: usize,
658    m: T,
659) -> Vec<T> {
660    if k == 0 {
661        return Vec::new();
662    }
663    let Some(start) = coefficients.iter().position(|&c| c != T::ZERO) else {
664        return vec![T::ZERO; k];
665    };
666    let n = coefficients.len();
667    let a_len = n - start;
668    let data = T::precompute_mod_mul_data(&m);
669    // binomial_powers[t] = q^C(t, 2), for t < n + k - 1.
670    let b_len = n + k - 1;
671    let mut binomial_powers = Vec::with_capacity(b_len);
672    let mut power = T::ONE % m;
673    let mut step = T::ONE % m;
674    for _ in 0..b_len {
675        binomial_powers.push(power);
676        power.mod_mul_precomputed_assign(step, m, &data);
677        step.mod_mul_precomputed_assign(q, m, &data);
678    }
679    // inverse_powers[t] = q^-C(t, 2), for t < max(n, k).
680    let w_len = max(n, k);
681    let mut inverse_powers = Vec::with_capacity(w_len);
682    let mut power = T::ONE % m;
683    let mut step = T::ONE % m;
684    for _ in 0..w_len {
685        inverse_powers.push(power);
686        power.mod_mul_precomputed_assign(step, m, &data);
687        step.mod_mul_precomputed_assign(q_inverse, m, &data);
688    }
689    // The scaled coefficients, from the first nonzero one on, reversed.
690    let scaled: Vec<T> = coefficients[start..]
691        .iter()
692        .zip(&inverse_powers[start..])
693        .rev()
694        .map(|(&c, &w)| c.mod_mul_precomputed(w, m, &data))
695        .collect();
696    let mut product = vec![T::ZERO; k];
697    mod_mul_middle_karatsuba(
698        &mut product,
699        &scaled,
700        &binomial_powers[start..start + a_len + k - 1],
701        &ModData::new(m, a_len),
702    );
703    product
704        .iter()
705        .zip(&inverse_powers)
706        .map(|(&z, &w)| z.mod_mul_precomputed(w, m, &data))
707        .collect()
708}}
709
710// Evaluates like `mod_evaluate_geometric_fast`, but computes the sums with a truncated product of
711// length $n + k - 1$ and keeps its last $k$ coefficients, discarding the first $n - 1$, rather than
712// with a middle product. It is kept to measure what the middle product saves.
713crate_test_fn! {
714#[allow(dead_code)]
715mod_evaluate_geometric_fast_truncated<T: PrimitiveUnsigned>(
716    coefficients: &[T],
717    q: T,
718    q_inverse: T,
719    k: usize,
720    m: T,
721) -> Vec<T> {
722    if k == 0 {
723        return Vec::new();
724    }
725    let Some(start) = coefficients.iter().position(|&c| c != T::ZERO) else {
726        return vec![T::ZERO; k];
727    };
728    let n = coefficients.len();
729    let a_len = n - start;
730    let data = T::precompute_mod_mul_data(&m);
731    // binomial_powers[t] = q^C(t, 2), for t < n + k - 1.
732    let b_len = n + k - 1;
733    let mut binomial_powers = Vec::with_capacity(b_len);
734    let mut power = T::ONE % m;
735    let mut step = T::ONE % m;
736    for _ in 0..b_len {
737        binomial_powers.push(power);
738        power.mod_mul_precomputed_assign(step, m, &data);
739        step.mod_mul_precomputed_assign(q, m, &data);
740    }
741    // inverse_powers[t] = q^-C(t, 2), for t < max(n, k).
742    let w_len = max(n, k);
743    let mut inverse_powers = Vec::with_capacity(w_len);
744    let mut power = T::ONE % m;
745    let mut step = T::ONE % m;
746    for _ in 0..w_len {
747        inverse_powers.push(power);
748        power.mod_mul_precomputed_assign(step, m, &data);
749        step.mod_mul_precomputed_assign(q_inverse, m, &data);
750    }
751    // The scaled coefficients, from the first nonzero one on, reversed.
752    let scaled: Vec<T> = coefficients[start..]
753        .iter()
754        .zip(&inverse_powers[start..])
755        .rev()
756        .map(|(&c, &w)| c.mod_mul_precomputed(w, m, &data))
757        .collect();
758    let mut product = vec![T::ZERO; a_len + k - 1];
759    mod_mul_truncated_to_out(
760        &mut product,
761        &scaled,
762        &binomial_powers[start..start + a_len + k - 1],
763        m,
764    );
765    product[a_len - 1..]
766        .iter()
767        .zip(&inverse_powers)
768        .map(|(&z, &w)| z.mod_mul_precomputed(w, m, &data))
769        .collect()
770}}
771
772impl<T: PrimitiveUnsigned> ModEvaluateGeometric<T> for &UnsignedPolynomial<T> {
773    type Output = T;
774
775    /// Evaluates an [`UnsignedPolynomial`] at $1, q, q^2, \ldots, q^{k-1}$, modulo a value of its
776    /// coefficient type. The coefficients and `q` must already be reduced modulo `m`.
777    ///
778    /// $$
779    /// f(p, q, k, m) = \left ( \sum_{i=0}^{n-1} c_i q^{ij} \bmod m \right )_{j=0}^{k-1},
780    /// $$
781    ///
782    /// where $c_i$ is the coefficient of $x^i$ in $p$ and $n$ is its length.
783    ///
784    /// The powers of `q` are computed with Shoup's method when the top bit of `m` is clear, since
785    /// every multiplication is by `q`, and are then evaluated as by
786    /// [`mod_evaluate_many`](crate::polynomial::ModEvaluateMany::mod_evaluate_many), in place.
787    ///
788    /// # Worst-case complexity
789    /// $T(n, k) = O(nk)$
790    ///
791    /// $M(k) = O(k)$
792    ///
793    /// where $T$ is time, $M$ is additional memory, $n$ is `self.len()`, and $k$ is `k`.
794    ///
795    /// # Panics
796    /// Panics if `m` is 0, or if any coefficient of `self` or `q` is greater than or equal to `m`.
797    ///
798    /// # Examples
799    /// ```
800    /// use core::str::FromStr;
801    /// use malachite_base::polynomial::ModEvaluateGeometric;
802    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
803    ///
804    /// let p = UnsignedPolynomial::<u8>::from_str("5*x^2+3*x+7").unwrap();
805    /// // At 1, 2, 4, and 8: 15, 33, 99, and 351, mod 13
806    /// assert_eq!((&p).mod_evaluate_geometric(2, 4, 13), &[2, 7, 8, 0]);
807    /// ```
808    ///
809    /// This is equivalent to `nmod_poly_evaluate_geometric_nmod_vec_iter` from
810    /// `nmod_poly/evaluate_geometric_nmod_vec.c`, FLINT 3.6.0, with `q` in place of FLINT's $r^2$:
811    /// FLINT evaluates at the powers of the square of its argument.
812    fn mod_evaluate_geometric(self, q: T, k: u64, m: T) -> Vec<T> {
813        assert!(
814            self.mod_is_reduced(&m),
815            "self must be reduced mod m, but {self} has a coefficient >= {m}"
816        );
817        assert!(q < m, "q must be reduced mod m, but {q} >= {m}");
818        let k = usize::exact_from(k);
819        let n = self.coefficients.len();
820        if mod_evaluate_geometric_fast_preferred(n, k, m)
821            && q != T::ZERO
822            && let Some(q_inverse) = q.mod_inverse(m)
823        {
824            mod_evaluate_geometric_fast(&self.coefficients, q, q_inverse, k, m)
825        } else {
826            mod_evaluate_geometric_iter(&self.coefficients, q, k, m)
827        }
828    }
829}