Power series inversion and division
#include <flint/nmod_poly.h>void nmod_poly_inv_series(nmod_poly_t Qinv, const nmod_poly_t Q, slong n)sourceSet Qinv to the inverse of the power series modulo , that is, . The underscore version requires and , writes coefficients and does not allow aliasing; may be longer than . The non-underscore version throws FLINT_DIVZERO if or .
void _nmod_poly_inv_series_basecase(nn_ptr Qinv, nn_srcptr Q, slong Qlen, slong n, nmod_t mod)sourcevoid nmod_poly_inv_series_basecase(nmod_poly_t Qinv, const nmod_poly_t Q, slong n)sourceThe same, by the recurrence , , with each sum computed as a dot product. A binomial is inverted directly as a geometric series.
void nmod_poly_inv_series_newton(nmod_poly_t Qinv, const nmod_poly_t Q, slong n)sourceThe same as _nmod_poly_inv_series and nmod_poly_inv_series, of which they are aliases; they use Newton iteration only when the length makes it faster.
void _nmod_poly_div_series(nn_ptr Q, nn_srcptr A, slong Alen, nn_srcptr B, slong Blen, slong n, nmod_t mod)sourcevoid nmod_poly_div_series(nmod_poly_t Q, const nmod_poly_t A, const nmod_poly_t B, slong n)sourceSet to , so that . The underscore version requires , writes coefficients and does not allow aliasing. The non-underscore version throws FLINT_DIVZERO if , or .
void _nmod_poly_div_series_basecase(nn_ptr Q, nn_srcptr A, slong Alen, nn_srcptr B, slong Blen, slong n, nmod_t mod)sourcevoid nmod_poly_div_series_basecase(nmod_poly_t Q, const nmod_poly_t A, const nmod_poly_t B, slong n)sourceThe same by the recurrence .
A power series over is invertible if and only if is a unit, and then its inverse modulo is a polynomial of length . These functions compute and modulo . Inversion is the basic step of division with remainder (applied to the reversed divisor, see Computing modulo a fixed polynomial) and of most power series functions, and it costs only a small constant number of multiplications.
AlgorithmNewton iteration for power series and divisionInverse, quotient, square root, reciprocal square root, exponential and logarithm of power series by Newton iteration with middle products and the Karp–Markstein trick, and polynomial division with remainder via reversed polynomials, as implemented fornmod_poly.
Algorithms#
The basecase algorithm solves coefficient by coefficient:
with each sum computed as one dot product (_nmod_vec_dot_rev, see Computing dot products) and a single reduction. It costs operations. A binomial is inverted as a geometric series. Division uses the analogous recurrence .
Newton iteration doubles the number of correct coefficients in each step: if , then
The product is known to be in its low coefficients, so only its middle coefficients are needed, which is a middle product, and the correction is a truncated product of length . Summed over all steps the cost is a small multiple of one multiplication of length . Division is computed in a similar way, folding the multiplication by into the last Newton step instead of computing first, as proposed by Karp and Markstein [KM1997].
nmod_poly_inv_series uses the basecase algorithm for , and otherwise for below a cutoff tabulated by the bit size of (with fft_small: 36 to 50 for moduli up to 7 bits, 81 to 292 for 8 to 21 bits, 388 to 407 for 22 to 28 bits and 231 to 448 above) or for short , and Newton iteration otherwise. nmod_poly_div_series uses the basecase for and analogous tables (about 80 to 600). nmod_poly_inv_series_newton is the same function as nmod_poly_inv_series.
Examples#
The generating function of the Fibonacci numbers is . One series division gives modulo , and here for , checked against the matrix-free identity :
#include <flint/flint.h>
#include <flint/nmod.h>
#include <flint/nmod_poly.h>
int main(void)
{
nmod_poly_t num, den, f;
slong m = 100001;
ulong a, b;
nmod_t mod;
nmod_init(&mod, 1000000007);
nmod_poly_init_mod(num, mod);
nmod_poly_init_mod(den, mod);
nmod_poly_init_mod(f, mod);
nmod_poly_set_coeff_ui(num, 1, 1); /* x */
nmod_poly_set_coeff_ui(den, 0, 1); /* 1 - x - x^2 */
nmod_poly_set_coeff_ui(den, 1, mod.n - 1);
nmod_poly_set_coeff_ui(den, 2, mod.n - 1);
nmod_poly_div_series(f, num, den, m);
flint_printf("F_0..F_10:");
for (slong i = 0; i <= 10; i++)
flint_printf(" %wu", nmod_poly_get_coeff_ui(f, i));
flint_printf("\n");
a = nmod_poly_get_coeff_ui(f, 50000);
b = nmod_poly_get_coeff_ui(f, 50001);
flint_printf("F_100000 = %wu\n", nmod_poly_get_coeff_ui(f, 100000));
flint_printf("identity = %wu\n",
nmod_mul(a, nmod_sub(nmod_add(b, b, mod), a, mod), mod));
nmod_poly_clear(num);
nmod_poly_clear(den);
nmod_poly_clear(f);
return 0;
}F_0..F_10: 0 1 1 2 3 5 8 13 21 34 55 F_100000 = 911435502 identity = 911435502
The Bernoulli numbers are defined by . Inverting the series modulo a prime larger than the length gives ; the program prints , , , and , which are :
#include <flint/flint.h>
#include <flint/nmod.h>
#include <flint/nmod_poly.h>
int main(void)
{
nmod_t mod;
nmod_poly_t h, b;
slong k, m = 12;
ulong fac = 1, den[6] = { 0, 6, 30, 42, 30, 66 };
nmod_init(&mod, 1000003);
nmod_poly_init_mod(h, mod);
nmod_poly_init_mod(b, mod);
/* h = sum x^k / (k+1)! */
for (k = 0; k < m; k++)
{
fac = nmod_mul(fac, k + 1, mod);
nmod_poly_set_coeff_ui(h, k, nmod_inv(fac, mod));
}
nmod_poly_inv_series(b, h, m);
/* B_k = k! * b_k */
fac = 1;
for (k = 1; k < m; k++)
{
fac = nmod_mul(fac, k, mod);
if (k % 2 == 0)
{
ulong B = nmod_mul(fac, nmod_poly_get_coeff_ui(b, k), mod);
ulong t = nmod_mul(B, den[k / 2], mod);
flint_printf("%wu * B_%wd = %wd\n", den[k / 2], k,
(t > mod.n / 2) ? (slong) t - (slong) mod.n : (slong) t);
}
}
nmod_poly_clear(h);
nmod_poly_clear(b);
return 0;
}6 * B_2 = 1 30 * B_4 = -1 42 * B_6 = 1 30 * B_8 = -1 66 * B_10 = 5
Guides#
References#
- [KM1997] A. H. Karp and P. Markstein, "High-precision division and square root", ACM Transactions on Mathematical Software 23:4 (1997), 561–589
The FLINT team, “Power series inversion and division (nmod_poly.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/nmod_poly/inv_series.html (generated 2026-11-02).
@misc{flint-doc-nmod-poly-inv-series,
author = {{The FLINT team}},
title = {{Power series inversion and division (nmod\_poly.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/nmod_poly/inv_series.html},
note = {Generated 2026-11-02}
}