#include "nmod.h"
#include "nmod_vec.h"
#include "nmod_poly.h"
static
void _nmod_geometric_progression_evaluate_init_nonfullword(nmod_geometric_progression_t G,
ulong r, slong len, nmod_t mod,
ulong q, ulong q_pr_quo, ulong q_pr_rem,
ulong inv_r,
ulong inv_q, ulong inv_q_pr_quo, ulong inv_q_pr_rem)
{
nmod_poly_init2_preinv(G->ev_f, mod.n, mod.ninv, 2*len - 1);
G->ev_s = _nmod_vec_init(len);
G->ev_f->length = 2*len - 1;
ulong r_pow_i2 = r;
ulong r_pr_quo = n_mulmod_precomp_shoup(r_pow_i2, mod.n);
G->ev_f->coeffs[0] = 1;
for (slong i = 1; i < 2*len - 1; i++)
{
G->ev_f->coeffs[i] = n_mulmod_shoup(r_pow_i2, G->ev_f->coeffs[i - 1], r_pr_quo, mod.n);
n_mulmod_and_precomp_shoup(&r_pow_i2, &r_pr_quo, q, r_pow_i2, q_pr_quo, q_pr_rem, r_pr_quo, mod.n);
}
r_pr_quo = n_mulmod_precomp_shoup(inv_r, mod.n);
G->ev_s[0] = 1;
for (slong i = 1; i < len; i++)
{
G->ev_s[i] = n_mulmod_shoup(inv_r, G->ev_s[i - 1], r_pr_quo, mod.n);
n_mulmod_and_precomp_shoup(&inv_r, &r_pr_quo, inv_q, inv_r, inv_q_pr_quo, inv_q_pr_rem, r_pr_quo, mod.n);
}
}
static
void _nmod_geometric_progression_evaluate_init(nmod_geometric_progression_t G,
ulong r, slong len, nmod_t mod,
ulong q, ulong inv_r, ulong inv_q)
{
nmod_poly_init2_preinv(G->ev_f, mod.n, mod.ninv, 2*len - 1);
G->ev_s = _nmod_vec_init(len);
G->ev_f->length = 2*len - 1;
G->ev_f->coeffs[0] = 1;
for (slong i = 1; i < 2*len - 1; i++)
{
G->ev_f->coeffs[i] = nmod_mul(G->ev_f->coeffs[i - 1], r, mod);
r = nmod_mul(r, q, mod);
}
G->ev_s[0] = 1;
for (slong i = 1; i < len; i++)
{
G->ev_s[i] = nmod_mul(G->ev_s[i - 1], inv_r, mod);
inv_r = nmod_mul(inv_r, inv_q, mod);
}
}
static
void _nmod_geometric_progression_interpolate_init_nonfullword(nmod_geometric_progression_t G,
slong len, nmod_t mod,
ulong q, ulong q_pr_quo, ulong q_pr_rem,
ulong inv_q, ulong inv_q_pr_quo, ulong inv_q_pr_rem)
{
nmod_poly_init2_preinv(G->int_f, mod.n, mod.ninv, len);
G->int_f->length = len;
G->int_s1 = _nmod_vec_init(len);
G->int_s2 = _nmod_vec_init(len);
G->int_f->coeffs[0] = 1;
G->int_s2[0] = 1;
const ulong one_precomp = n_mulmod_precomp_shoup(UWORD(1), mod.n);
ulong q_pow_i = 1;
ulong q_pow_i_pr = one_precomp;
ulong inv_q_pow_i = 1;
ulong inv_q_pow_i_pr = one_precomp;
ulong inv_q_i = 1;
ulong inv_q_i_pr = one_precomp;
ulong prod_diff = 1;
for (slong i = 1; i < len; i++)
{
ulong inv_q_pow_i_pr_rem = n_mulmod_precomp_shoup_rem_from_quo(inv_q_pow_i_pr, mod.n);
n_mulmod_and_precomp_shoup(&inv_q_i, &inv_q_i_pr, inv_q_pow_i, inv_q_i, inv_q_pow_i_pr, inv_q_pow_i_pr_rem, inv_q_i_pr, mod.n);
n_mulmod_and_precomp_shoup(&inv_q_pow_i, &inv_q_pow_i_pr, inv_q, inv_q_pow_i, inv_q_pr_quo, inv_q_pr_rem, inv_q_pow_i_pr, mod.n);
G->int_f->coeffs[i] = n_mulmod_shoup(q_pow_i, G->int_f->coeffs[i-1], q_pow_i_pr, mod.n);
n_mulmod_and_precomp_shoup(&q_pow_i, &q_pow_i_pr, q, q_pow_i, q_pr_quo, q_pr_rem, q_pow_i_pr, mod.n);
G->int_s1[i-1] = q_pow_i - 1;
prod_diff = nmod_mul(q_pow_i - 1, prod_diff, mod);
G->int_s2[i] = n_mulmod_shoup(inv_q_i, prod_diff, inv_q_i_pr, mod.n);
}
G->int_s1[len-1] = nmod_inv(prod_diff, mod);
for (slong i = len - 1; i > 0; i--)
{
ulong w_i = G->int_s1[i];
if (i % 2)
G->int_f->coeffs[i] = mod.n - nmod_mul(G->int_f->coeffs[i], w_i, mod);
else
G->int_f->coeffs[i] = nmod_mul(G->int_f->coeffs[i], w_i, mod);
G->int_s1[i-1] = nmod_mul(G->int_s1[i-1], w_i, mod);
}
}
static
void _nmod_geometric_progression_interpolate_init(nmod_geometric_progression_t G,
slong len, nmod_t mod,
ulong q, ulong inv_q)
{
nmod_poly_init2_preinv(G->int_f, mod.n, mod.ninv, len);
G->int_f->length = len;
G->int_s1 = _nmod_vec_init(len);
G->int_s2 = _nmod_vec_init(len);
G->int_f->coeffs[0] = 1;
G->int_s2[0] = 1;
ulong q_pow_i = 1;
ulong inv_q_pow_i = 1;
ulong inv_q_i = 1;
ulong prod_diff = 1;
for (slong i = 1; i < len; i++)
{
inv_q_i = nmod_mul(inv_q_i, inv_q_pow_i, mod);
inv_q_pow_i = nmod_mul(inv_q_pow_i, inv_q, mod);
G->int_f->coeffs[i] = nmod_mul(G->int_f->coeffs[i-1], q_pow_i, mod);
q_pow_i = nmod_mul(q_pow_i, q, mod);
G->int_s1[i-1] = q_pow_i - 1;
prod_diff = nmod_mul(q_pow_i - 1, prod_diff, mod);
G->int_s2[i] = nmod_mul(prod_diff, inv_q_i, mod);
}
G->int_s1[len-1] = nmod_inv(prod_diff, mod);
for (slong i = len - 1; i > 0; i--)
{
ulong w_i = G->int_s1[i];
if (i % 2)
G->int_f->coeffs[i] = mod.n - nmod_mul(G->int_f->coeffs[i], w_i, mod);
else
G->int_f->coeffs[i] = nmod_mul(G->int_f->coeffs[i], w_i, mod);
G->int_s1[i-1] = nmod_mul(G->int_s1[i-1], w_i, mod);
}
}
static
void _nmod_geometric_progression_extrapolate_init(nmod_geometric_progression_t G,
slong len, nmod_t mod,
ulong q, ulong inv_q)
{
nmod_poly_init2_preinv(G->ext_ff, mod.n, mod.ninv, len-1);
nmod_poly_init2_preinv(G->ext_fb, mod.n, mod.ninv, len-1);
G->ext_ff->length = len-1;
G->ext_fb->length = len-1;
G->ext_s1f = _nmod_vec_init(len);
G->ext_s1b = _nmod_vec_init(len);
G->ext_s2 = _nmod_vec_init(len);
G->ext_s3 = _nmod_vec_init(len);
G->ext_s1f[0] = 1;
G->ext_s1b[0] = 1;
ulong q_pow_i = q;
ulong inv_q_pow_i = inv_q;
for (slong i = 1; i < len; i++)
{
G->ext_ff->coeffs[i-1] = q_pow_i - 1;
G->ext_fb->coeffs[i-1] = inv_q_pow_i - 1;
G->ext_s1f[i] = nmod_mul(G->ext_s1f[i-1], q_pow_i - 1, mod);
G->ext_s1b[i] = nmod_mul(G->ext_s1b[i-1], inv_q_pow_i - 1, mod);
q_pow_i = nmod_mul(q_pow_i, q, mod);
inv_q_pow_i = nmod_mul(inv_q_pow_i, inv_q, mod);
}
G->ext_s2[len-1] = nmod_inv(G->ext_s1f[len-1], mod);
G->ext_s3[len-1] = nmod_inv(G->ext_s1b[len-1], mod);
for (slong i = len-1; i > 0; i--)
{
G->ext_s2[i-1] = nmod_mul(G->ext_s2[i], G->ext_ff->coeffs[i-1], mod);
G->ext_s3[i-1] = nmod_mul(G->ext_s3[i], G->ext_fb->coeffs[i-1], mod);
G->ext_ff->coeffs[i-1] = nmod_mul(G->ext_s2[i], G->ext_s1f[i-1], mod);
G->ext_fb->coeffs[i-1] = nmod_mul(G->ext_s3[i], G->ext_s1b[i-1], mod);
}
}
static
void _nmod_geometric_progression_interpolate_extrapolate_init(nmod_geometric_progression_t G,
slong len, nmod_t mod,
ulong q, ulong inv_q)
{
nmod_poly_init2_preinv(G->ext_ff, mod.n, mod.ninv, len-1);
nmod_poly_init2_preinv(G->ext_fb, mod.n, mod.ninv, len-1);
G->ext_ff->length = len-1;
G->ext_fb->length = len-1;
G->ext_s1f = _nmod_vec_init(len);
G->ext_s1b = _nmod_vec_init(len);
G->ext_s2 = _nmod_vec_init(len);
G->ext_s3 = _nmod_vec_init(len);
nmod_poly_init2_preinv(G->int_f, mod.n, mod.ninv, len);
G->int_f->length = len;
G->int_s1 = G->ext_s2;
G->int_s2 = _nmod_vec_init(len);
G->ext_s1f[0] = 1;
G->ext_s1b[0] = 1;
G->int_f->coeffs[0] = 1;
G->int_s2[0] = 1;
ulong q_pow_i = 1;
ulong inv_q_pow_i = 1;
for (slong i = 1; i < len; i++)
{
G->int_f->coeffs[i] = nmod_mul(G->int_f->coeffs[i-1],
mod.n - q_pow_i, mod);
G->int_s2[i] = nmod_mul(G->int_s2[i-1], inv_q_pow_i, mod);
G->int_s2[i-1] = nmod_mul(G->int_s2[i-1], G->ext_s1f[i-1], mod);
q_pow_i = nmod_mul(q_pow_i, q, mod);
inv_q_pow_i = nmod_mul(inv_q_pow_i, inv_q, mod);
G->ext_ff->coeffs[i-1] = q_pow_i - 1;
G->ext_fb->coeffs[i-1] = inv_q_pow_i - 1;
G->ext_s1f[i] = nmod_mul(G->ext_s1f[i-1], q_pow_i - 1, mod);
G->ext_s1b[i] = nmod_mul(G->ext_s1b[i-1], inv_q_pow_i - 1, mod);
}
G->int_s2[len-1] = nmod_mul(G->int_s2[len-1], G->ext_s1f[len-1], mod);
G->ext_s2[len-1] = nmod_inv(G->ext_s1f[len-1], mod);
G->ext_s3[len-1] = nmod_inv(G->ext_s1b[len-1], mod);
for (slong i = len-1; i > 0; i--)
{
G->ext_s2[i-1] = nmod_mul(G->ext_s2[i], G->ext_ff->coeffs[i-1], mod);
G->int_f->coeffs[i] = nmod_mul(G->ext_s2[i], G->int_f->coeffs[i], mod);
G->ext_s3[i-1] = nmod_mul(G->ext_s3[i], G->ext_fb->coeffs[i-1], mod);
G->ext_ff->coeffs[i-1] = nmod_mul(G->ext_s2[i], G->ext_s1f[i-1], mod);
G->ext_fb->coeffs[i-1] = nmod_mul(G->ext_s3[i], G->ext_s1b[i-1], mod);
}
}
void _nmod_geometric_progression_init_function(nmod_geometric_progression_t G,
ulong r, slong len, nmod_t mod,
ulong function)
{
G->len = len;
G->mod = mod;
G->function = function;
if (NMOD_CAN_USE_SHOUP(mod))
{
const ulong q = nmod_mul(r, r, mod);
const ulong inv_r = nmod_inv(r, mod);
const ulong inv_q = nmod_mul(inv_r, inv_r, mod);
ulong q_pr_rem;
ulong q_pr_quo;
ulong inv_q_pr_rem;
ulong inv_q_pr_quo;
n_mulmod_precomp_shoup_quo_rem(&q_pr_quo, &q_pr_rem, q, mod.n);
n_mulmod_precomp_shoup_quo_rem(&inv_q_pr_quo, &inv_q_pr_rem, inv_q, mod.n);
if (function & UWORD(1))
_nmod_geometric_progression_evaluate_init_nonfullword(G, r, len, mod, q, q_pr_quo, q_pr_rem, inv_r, inv_q, inv_q_pr_quo, inv_q_pr_rem);
if ((function & UWORD(6)) == UWORD(6))
_nmod_geometric_progression_interpolate_extrapolate_init(G, len, mod, q, inv_q);
else if ((function & UWORD(2)) == UWORD(2))
_nmod_geometric_progression_interpolate_init_nonfullword(G, len, mod, q, q_pr_quo, q_pr_rem, inv_q, inv_q_pr_quo, inv_q_pr_rem);
else if ((function & UWORD(4)) == UWORD(4))
_nmod_geometric_progression_extrapolate_init(G, len, mod, q, inv_q);
}
else
{
const ulong q = nmod_mul(r, r, mod);
const ulong inv_r = nmod_inv(r, mod);
const ulong inv_q = nmod_mul(inv_r, inv_r, mod);
if (function & UWORD(1))
_nmod_geometric_progression_evaluate_init(G, r, len, mod, q, inv_r, inv_q);
if ((function & UWORD(6)) == UWORD(6))
_nmod_geometric_progression_interpolate_extrapolate_init(G, len, mod, q, inv_q);
else if ((function & UWORD(2)) == UWORD(2))
_nmod_geometric_progression_interpolate_init(G, len, mod, q, inv_q);
else if ((function & UWORD(4)) == UWORD(4))
_nmod_geometric_progression_extrapolate_init(G, len, mod, q, inv_q);
}
}
static
void _nmod_geometric_progression_clear_function(nmod_geometric_progression_t G, ulong function)
{
if (function & UWORD(1))
{
_nmod_vec_clear(G->ev_s);
nmod_poly_clear(G->ev_f);
}
if ((function & UWORD(6)) == UWORD(6))
{
_nmod_vec_clear(G->int_s2);
nmod_poly_clear(G->int_f);
_nmod_vec_clear(G->ext_s1f);
_nmod_vec_clear(G->ext_s1b);
_nmod_vec_clear(G->ext_s2);
_nmod_vec_clear(G->ext_s3);
nmod_poly_clear(G->ext_ff);
nmod_poly_clear(G->ext_fb);
}
else if ((function & UWORD(2)) == UWORD(2))
{
_nmod_vec_clear(G->int_s1);
_nmod_vec_clear(G->int_s2);
nmod_poly_clear(G->int_f);
}
else if ((function & UWORD(4)) == UWORD(4))
{
_nmod_vec_clear(G->ext_s1f);
_nmod_vec_clear(G->ext_s1b);
_nmod_vec_clear(G->ext_s2);
_nmod_vec_clear(G->ext_s3);
nmod_poly_clear(G->ext_ff);
nmod_poly_clear(G->ext_fb);
}
}
void nmod_geometric_progression_init(nmod_geometric_progression_t G,
ulong r, slong len, nmod_t mod)
{
_nmod_geometric_progression_init_function(G, r, len, mod, UWORD(7));
}
void nmod_geometric_progression_clear(nmod_geometric_progression_t G)
{
_nmod_geometric_progression_clear_function(G, G->function);
}