Composition and Taylor shifts
#include <flint/fmpz_poly.h>void _fmpz_poly_compose(fmpz * res, const fmpz * poly1, slong len1, const fmpz * poly2, slong len2)sourcevoid fmpz_poly_compose(fmpz_poly_t res, const fmpz_poly_t poly1, const fmpz_poly_t poly2)sourceSet res to the composition of = poly1 and = poly2. Inner polynomials of the form use a Taylor shift and a scaling, short outer polynomials Horner's rule, and others the divide-and-conquer algorithm. The underscore version requires positive lengths, writes coefficients and does not allow aliasing. See Composition and Taylor shifts.
void _fmpz_poly_compose_horner(fmpz * res, const fmpz * poly1, slong len1, const fmpz * poly2, slong len2)sourcevoid fmpz_poly_compose_horner(fmpz_poly_t res, const fmpz_poly_t poly1, const fmpz_poly_t poly2)sourcevoid _fmpz_poly_compose_divconquer(fmpz * res, const fmpz * poly1, slong len1, const fmpz * poly2, slong len2)sourcevoid fmpz_poly_compose_divconquer(fmpz_poly_t res, const fmpz_poly_t poly1, const fmpz_poly_t poly2)sourceComposition by Horner's rule, respectively by the divide-and-conquer algorithm that splits into blocks evaluated at and combines them with the powers . The underscore versions have the conventions of _fmpz_poly_compose.
void fmpz_poly_taylor_shift(fmpz_poly_t g, const fmpz_poly_t f, const fmpz_t c)sourceSet to ; the underscore version shifts (poly, n) in place. It uses Horner's scheme for length less than 64 and the divide-and-conquer algorithm otherwise.
void fmpz_poly_taylor_shift_horner(fmpz_poly_t g, const fmpz_poly_t f, const fmpz_t c)sourceTaylor shift by Horner's scheme, additions and multiplications by (only additions for ).
void fmpz_poly_taylor_shift_divconquer(fmpz_poly_t g, const fmpz_poly_t f, const fmpz_t c)sourceTaylor shift by divide and conquer, with the powers of computed by the binomial theorem; the two halves are shifted in parallel when threads are available. Short inputs use Horner's scheme, with a cutoff that grows with the size of the coefficients.
void fmpz_poly_taylor_shift_multi_mod(fmpz_poly_t g, const fmpz_poly_t f, const fmpz_t c)sourceTaylor shift by a multimodular algorithm: the shift is computed modulo enough word-size primes for a bound on the result and combined by the Chinese remainder theorem, using flint_get_num_threads threads.
fmpz_poly_compose computes , a polynomial of degree . The Taylor shift is the special case , which occurs often enough (for example in root isolation) to have its own functions. For composition of power series, see fmpz_poly_compose_series.
nmod_poly.
Algorithms#
Horner's rule performs products of increasingly unbalanced size. The divide-and-conquer algorithm groups the coefficients of in pairs, , then combines pairs with , quadruples with and so on, so that all products are balanced; with fast multiplication its cost is for , against for Horner's rule. fmpz_poly_compose uses Horner's rule for and divide and conquer otherwise, and reduces inner polynomials of the form to a Taylor shift, a scaling and an inflation.
For the Taylor shift, Horner's scheme costs additions and multiplications by (only additions for ), on coefficients that grow to bits. The divide-and-conquer algorithm writes , shifts the halves recursively and computes by the binomial theorem, so that the work is in a few large products [GG1997]. fmpz_poly_taylor_shift uses Horner's scheme below length 64 and divide and conquer otherwise; the divide-and-conquer function itself returns to Horner's scheme below a length between 100 and 1000 that grows with the coefficient size, and runs the two recursive halves in parallel when threads are available. fmpz_poly_taylor_shift_multi_mod computes the shift modulo many word-size primes in parallel and reconstructs the result, which is competitive for long polynomials with small coefficients and many threads.
Examples#
Composition with and a Taylor shift by ; the shift of a polynomial by and then by returns the original:
#include <flint/flint.h>
#include <flint/fmpz.h>
#include <flint/fmpz_poly.h>
int main(void)
{
fmpz_poly_t g, h, r, s;
fmpz_t c;
fmpz_poly_init(g);
fmpz_poly_init(h);
fmpz_poly_init(r);
fmpz_poly_init(s);
fmpz_init(c);
fmpz_poly_set_str(g, "3 1 -3 2"); /* 2x^2 - 3x + 1 */
fmpz_poly_set_str(h, "3 1 0 1"); /* x^2 + 1 */
fmpz_poly_compose(r, g, h);
flint_printf("g(h(x)) = "); fmpz_poly_print_pretty(r, "x"); flint_printf("\n");
fmpz_set_si(c, -1);
fmpz_poly_taylor_shift(r, g, c);
flint_printf("g(x - 1) = "); fmpz_poly_print_pretty(r, "x"); flint_printf("\n");
/* the shift agrees with composition by x + c */
fmpz_poly_chebyshev_t(g, 100);
fmpz_set_si(c, 7);
fmpz_poly_taylor_shift(r, g, c);
fmpz_poly_set_str(h, "2 7 1");
fmpz_poly_compose(s, g, h);
flint_printf("shift == compose: %d\n", fmpz_poly_equal(r, s));
fmpz_neg(c, c);
fmpz_poly_taylor_shift(r, r, c);
flint_printf("shift back: %d\n", fmpz_poly_equal(r, g));
fmpz_poly_clear(g);
fmpz_poly_clear(h);
fmpz_poly_clear(r);
fmpz_poly_clear(s);
fmpz_clear(c);
return 0;
}g(h(x)) = 2*x^4+x^2 g(x - 1) = 2*x^2-7*x+6 shift == compose: 1 shift back: 1
References#
- [GG1997] J. von zur Gathen and J. Gerhard. Fast algorithms for Taylor shifts and certain difference equations. ISSAC 1997, 40–47.
The FLINT team, “Composition and Taylor shifts (fmpz_poly.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/fmpz_poly/compose.html (generated 2026-11-02).
@misc{flint-doc-fmpz-poly-compose,
author = {{The FLINT team}},
title = {{Composition and Taylor shifts (fmpz\_poly.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/fmpz_poly/compose.html},
note = {Generated 2026-11-02}
}