/* Copyright (C) 2011, 2016, 2021 Fredrik Johansson This file is part of FLINT. FLINT is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. See . */ #include #include "flint.h" #include "ulong_extras.h" #include "nmod_vec.h" #include "nmod_poly.h" #define NMOD_POLY_NEWTON_EXP_CUTOFF 4000 /* c_k x^k -> c_k x^k / (m+k) */ void _nmod_poly_integral_offset(mp_ptr res, mp_srcptr poly, slong len, slong m, nmod_t mod) { slong k; mp_limb_t t; t = 1; for (k = len - 1; k >= 0; k--) { res[k] = n_mulmod2_preinv(poly[k], t, mod.n, mod.ninv); t = n_mulmod2_preinv(t, m + k, mod.n, mod.ninv); } if (t >= mod.n) t = n_mod2_preinv(t, mod.n, mod.ninv); t = n_invmod(t, mod.n); for (k = 0; k < len; k++) { res[k] = n_mulmod2_preinv(res[k], t, mod.n, mod.ninv); t = n_mulmod2_preinv(t, m + k, mod.n, mod.ninv); } } void _nmod_poly_exp_series_newton(mp_ptr f, mp_ptr g, mp_srcptr h, slong hlen, slong n, nmod_t mod) { slong a[FLINT_BITS]; slong i, m, l, r; mp_ptr t, hprime; int inverse; /* If g is provided, we compute g = exp(-h), and we can use g as scratch space. Otherwise, we still need to compute exp(-h) to length (n+1)/2 for intermediate use, and we still need n coefficients of scratch space. */ inverse = (g != NULL); if (!inverse) g = _nmod_vec_init(n); hlen = FLINT_MIN(hlen, n); t = _nmod_vec_init(n); hprime = _nmod_vec_init(hlen - 1); _nmod_poly_derivative(hprime, h, hlen, mod); for (i = 1; (WORD(1) << i) < n; i++); a[i = 0] = n; while (n >= NMOD_POLY_NEWTON_EXP_CUTOFF || i == 0) a[++i] = (n = (n + 1) / 2); /* f := exp(h) + O(x^n), g := exp(-h) + O(x^n) */ _nmod_poly_exp_series_basecase(f, h, hlen, n, mod); _nmod_poly_inv_series(g, f, n, n, mod); for (i--; i >= 0; i--) { m = n; /* previous length */ n = a[i]; /* new length */ l = FLINT_MIN(hlen, n) - 1; r = FLINT_MIN(l + m - 1, n - 1); if (l >= m) _nmod_poly_mullow(t, hprime, l, f, m, r, mod); else _nmod_poly_mullow(t, f, m, hprime, l, r, mod); _nmod_poly_mullow(g + m, g, n - m, t + m - 1, r + 1 - m, n - m, mod); _nmod_poly_integral_offset(g + m, g + m, n - m, m, mod); _nmod_poly_mullow(f + m, f, n - m, g + m, n - m, n - m, mod); /* g := exp(-h) + O(x^n); not needed if we only want exp(x) */ if (i != 0 || inverse) { _nmod_poly_mullow(t, f, n, g, m, n, mod); _nmod_poly_mullow(g + m, g, m, t + m, n - m, n - m, mod); _nmod_vec_neg(g + m, g + m, n - m, mod); } } _nmod_vec_clear(hprime); _nmod_vec_clear(t); if (!inverse) _nmod_vec_clear(g); } void _nmod_poly_exp_series(mp_ptr f, mp_srcptr h, slong hlen, slong n, nmod_t mod) { hlen = FLINT_MIN(hlen, n); if (hlen >= 2 && n > 2 && _nmod_vec_is_zero(h + 1, hlen - 2)) _nmod_poly_exp_series_monomial_ui(f, h[hlen - 1], hlen - 1, n, mod); else if (hlen < NMOD_POLY_NEWTON_EXP_CUTOFF) _nmod_poly_exp_series_basecase(f, h, hlen, n, mod); else _nmod_poly_exp_series_newton(f, NULL, h, hlen, n, mod); } void _nmod_poly_exp_expinv_series(mp_ptr f, mp_ptr g, mp_srcptr h, slong hlen, slong n, nmod_t mod) { hlen = FLINT_MIN(hlen, n); if (hlen < NMOD_POLY_NEWTON_EXP_CUTOFF) { _nmod_poly_exp_series_basecase(f, h, hlen, n, mod); _nmod_poly_inv_series(g, f, n, n, mod); } else { _nmod_poly_exp_series_newton(f, g, h, hlen, n, mod); } } void nmod_poly_exp_series(nmod_poly_t f, const nmod_poly_t h, slong n) { slong hlen = h->length; if (hlen > 0 && h->coeffs[0] != UWORD(0)) { flint_printf("Exception (nmod_poly_exp_series). Constant term != 0.\n"); flint_abort(); } if (n <= 1 || hlen <= 1) { if (n == 0) nmod_poly_zero(f); else nmod_poly_one(f); return; } nmod_poly_fit_length(f, n); _nmod_poly_exp_series(f->coeffs, h->coeffs, hlen, n, f->mod); f->length = n; _nmod_poly_normalise(f); }