#include "mpn_extras.h"
#include "nmod.h"
#include "fq_nmod.h"
#include "fq_nmod_poly.h"
#include "n_poly.h"
int n_fq_poly_is_canonical(const n_poly_t A, const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
slong i;
if (A->length < 0)
return 0;
if (d*A->length > A->alloc)
return 0;
for (i = 0; i < A->length; i++)
{
if (!n_fq_is_canonical(A->coeffs + d*i, ctx))
return 0;
if (i + 1 == A->length && _n_fq_is_zero(A->coeffs + d*i, d))
return 0;
}
return 1;
}
void n_fq_poly_init2(
n_fq_poly_t A,
slong alloc,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
if (alloc > 0)
{
A->alloc = d*alloc;
A->coeffs = flint_malloc(A->alloc*sizeof(ulong));
}
else
{
A->alloc = 0;
A->coeffs = NULL;
}
A->length = 0;
}
void n_fq_poly_randtest(
n_poly_t A,
flint_rand_t state,
slong len,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
slong i;
if (len < 1)
{
A->length = 0;
return;
}
n_poly_fit_length(A, d*len);
for (i = 0; i < d*len; i++)
A->coeffs[i] = n_randint(state, ctx->mod.n);
A->length = len;
_n_fq_poly_normalise(A, d);
}
void _n_fq_poly_one(n_poly_t A, slong d)
{
n_poly_fit_length(A, d);
A->length = 1;
_n_fq_one(A->coeffs + d*0, d);
}
int n_fq_poly_is_one(n_poly_t A, const fq_nmod_ctx_t ctx)
{
return A->length == 1 && _n_fq_is_one(A->coeffs, fq_nmod_ctx_degree(ctx));
}
void n_fq_poly_get_coeff_n_fq(
ulong * c,
const n_fq_poly_t A,
slong e,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
if (e >= A->length)
_n_fq_zero(c, d);
else
_n_fq_set(c, A->coeffs + d*e, d);
}
void n_fq_poly_get_coeff_fq_nmod(
fq_nmod_t c,
const n_fq_poly_t A,
slong e,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
if (e >= A->length)
fq_nmod_zero(c, ctx);
else
n_fq_get_fq_nmod(c, A->coeffs + d*e, ctx);
}
void n_fq_poly_set_coeff_n_fq(
n_fq_poly_t A,
slong j,
const ulong * c,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
FLINT_ASSERT(n_fq_is_canonical(c, ctx));
FLINT_ASSERT(n_fq_poly_is_canonical(A, ctx));
n_poly_fit_length(A, d*(j + 1));
if (j + 1 <= A->length)
{
_n_fq_set(A->coeffs + d*j, c, d);
if (j + 1 == A->length)
_n_fq_poly_normalise(A, d);
}
else if (!_n_fq_is_zero(c, d))
{
flint_mpn_zero(A->coeffs + d*A->length, d*(j - A->length));
_n_fq_set(A->coeffs + d*j, c, d);
A->length = j + 1;
}
FLINT_ASSERT(n_fq_poly_is_canonical(A, ctx));
}
void n_fq_poly_set_coeff_fq_nmod(
n_poly_t A,
slong j,
const fq_nmod_t c,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
FLINT_ASSERT(n_fq_poly_is_canonical(A, ctx));
n_poly_fit_length(A, d*(j + 1));
if (j + 1 <= A->length)
{
n_fq_set_fq_nmod(A->coeffs + d*j, c, ctx);
if (j + 1 == A->length)
_n_fq_poly_normalise(A, d);
}
else if (!fq_nmod_is_zero(c, ctx))
{
flint_mpn_zero(A->coeffs + d*A->length, d*(j - A->length));
n_fq_set_fq_nmod(A->coeffs + d*j, c, ctx);
A->length = j + 1;
}
FLINT_ASSERT(n_fq_poly_is_canonical(A, ctx));
}
void n_fq_poly_scalar_mul_ui(
n_poly_t A,
const n_poly_t B,
ulong c,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
if (c >= ctx->mod.n)
{
NMOD_RED(c, c, ctx->mod);
}
if (B->length < 1 || c == 0)
{
n_poly_zero(A);
return;
}
n_poly_fit_length(A, d*B->length);
_nmod_vec_scalar_mul_nmod(A->coeffs, B->coeffs, d*B->length, c, ctx->mod);
A->length = B->length;
_n_fq_poly_normalise(A, d);
}
int n_fq_poly_equal(
const n_poly_t A,
const n_poly_t B,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
slong i;
if (A->length != B->length)
return 0;
for (i = 0; i < d*B->length; i++)
if (A->coeffs[i] != B->coeffs[i])
return 0;
return 1;
}
void n_fq_poly_set(
n_poly_t A,
const n_poly_t B,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
if (A == B)
return;
n_poly_fit_length(A, d*B->length);
_nmod_vec_set(A->coeffs, B->coeffs, d*B->length);
A->length = B->length;
}
void n_fq_poly_set_n_fq(
n_poly_t A,
const ulong * c,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
n_poly_fit_length(A, d);
_nmod_vec_set(A->coeffs, c, d);
A->length = 1;
_n_fq_poly_normalise(A, d);
}
void n_fq_poly_set_fq_nmod(
n_poly_t A,
const fq_nmod_t c,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
n_poly_fit_length(A, d);
n_fq_set_fq_nmod(A->coeffs, c, ctx);
A->length = 1;
_n_fq_poly_normalise(A, d);
}
void n_fq_poly_shift_right(
n_poly_t A,
const n_poly_t B,
slong n,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
if (n < 1)
{
n_fq_poly_set(A, B, ctx);
return;
}
else if (B->length <= n)
{
n_poly_zero(A);
return;
}
else
{
n_poly_fit_length(A, d*(B->length - n));
flint_mpn_copyi(A->coeffs, B->coeffs + d*n, d*(B->length - n));
A->length = B->length - n;
}
}
void n_fq_poly_shift_left(
n_poly_t A,
const n_poly_t B,
slong n,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
if (n < 1)
{
n_fq_poly_set(A, B, ctx);
}
else if (n_fq_poly_is_zero(B))
{
n_fq_poly_zero(A);
}
else
{
n_poly_fit_length(A, d*(B->length + n));
flint_mpn_copyd(A->coeffs + d*n, B->coeffs, d*B->length);
flint_mpn_zero(A->coeffs, d*n);
A->length = B->length + n;
}
}
void n_fq_poly_truncate(n_poly_t A, slong len, const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
if (A->length > len)
{
A->length = len;
_n_fq_poly_normalise(A, d);
}
}
void n_fq_poly_evaluate_fq_nmod(
fq_nmod_t e,
const n_poly_t A,
const fq_nmod_t c,
const fq_nmod_ctx_t ctx)
{
fq_nmod_poly_t AA;
fq_nmod_poly_init(AA, ctx);
n_fq_poly_get_fq_nmod_poly(AA, A, ctx);
fq_nmod_poly_evaluate_fq_nmod(e, AA, c, ctx);
fq_nmod_poly_clear(AA, ctx);
}
void n_fq_poly_evaluate_n_fq(
ulong * e,
const n_poly_t A,
const ulong * c,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
slong i;
ulong * u = FLINT_ARRAY_ALLOC(d, ulong);
ulong * t = FLINT_ARRAY_ALLOC(d, ulong);
_n_fq_zero(t, d);
for (i = 0; i < A->length; i++)
{
n_fq_pow_ui(u, c, i, ctx);
n_fq_mul(u, u, A->coeffs + d*i, ctx);
n_fq_add(t, t, u, ctx);
}
_n_fq_set(e, t, d);
flint_free(u);
flint_free(t);
}
void n_fq_poly_eval_pow(
ulong * ev,
const n_fq_poly_t P,
n_fq_poly_t alphapow,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
const ulong * Pcoeffs = P->coeffs;
slong i, Plen = P->length;
ulong * alpha_powers = alphapow->coeffs;
ulong * t;
slong k;
TMP_INIT;
TMP_START;
t = TMP_ALLOC(d*FLINT_MAX(N_FQ_MUL_ITCH, N_FQ_LAZY_ITCH)*sizeof(ulong));
if (Plen > alphapow->length)
{
slong oldlength = alphapow->length;
FLINT_ASSERT(2 <= oldlength);
n_poly_fit_length(alphapow, d*Plen);
alphapow->length = Plen;
alpha_powers = alphapow->coeffs;
for (k = oldlength; k < Plen; k++)
{
_n_fq_mul(alpha_powers + d*k, alpha_powers + d*(k - 1),
alpha_powers + d*1, ctx, t);
}
}
_nmod_vec_zero(t, 6*d);
switch (_n_fq_dot_lazy_size(Plen, ctx))
{
#define lazycase(n) \
case n: \
for (i = 0; i < Plen; i++) \
_n_fq_madd2_lazy##n(t, Pcoeffs + d*i, alpha_powers + d*i, d); \
_n_fq_reduce2_lazy##n(t, d, ctx->mod); \
break;
lazycase(1)
lazycase(2)
lazycase(3)
default:
for (i = 0; i < Plen; i++)
_n_fq_madd2(t, Pcoeffs + d*i, alpha_powers + d*i, ctx, t + 2*d);
break;
}
_n_fq_reduce2(ev, t, ctx, t + 2*d);
TMP_END;
}
void n_fq_poly_set_fq_nmod_poly(
n_poly_t A,
const fq_nmod_poly_t B,
const fq_nmod_ctx_t ctx)
{
slong i;
slong d = fq_nmod_ctx_degree(ctx);
n_poly_fit_length(A, d*B->length);
for (i = 0; i < B->length; i++)
n_fq_set_fq_nmod(A->coeffs + d*i, B->coeffs + i, ctx);
A->length = B->length;
}
void n_fq_poly_get_fq_nmod_poly(
fq_nmod_poly_t A,
const n_poly_t B,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
slong i;
FLINT_ASSERT(B->alloc >= d*B->length);
fq_nmod_poly_fit_length(A, B->length, ctx);
for (i = 0; i < B->length; i++)
n_fq_get_fq_nmod(A->coeffs + i, B->coeffs + d*i, ctx);
A->length = B->length;
}
void n_fq_poly_scalar_mul_n_fq(
n_poly_t A,
const n_poly_t B,
const ulong * c,
const fq_nmod_ctx_t ctx)
{
slong i, d = fq_nmod_ctx_degree(ctx);
n_poly_fit_length(A, d*B->length);
for (i = 0; i < B->length; i++)
n_fq_mul(A->coeffs + d*i, B->coeffs + d*i, c, ctx);
A->length = B->length;
_n_fq_poly_normalise(A, d);
}
void n_fq_poly_make_monic(
n_poly_t A,
const n_poly_t B,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
slong itch = FLINT_MAX(N_FQ_MUL_ITCH, N_FQ_INV_ITCH);
ulong * tmp, * inv;
slong i, Blen = B->length;
if (Blen < 1)
{
n_poly_zero(A);
return;
}
n_poly_fit_length(A, d*Blen);
tmp = FLINT_ARRAY_ALLOC(d*(itch + 1), ulong);
inv = tmp + d*itch;
_n_fq_inv(inv, B->coeffs + d*(Blen - 1), ctx, tmp);
for (i = 0; i < Blen - 1; i++)
_n_fq_mul(A->coeffs + d*i, B->coeffs + d*i, inv, ctx, tmp);
_n_fq_one(A->coeffs + d*(Blen - 1), d);
A->length = Blen;
flint_free(tmp);
}
void n_fq_poly_scalar_addmul_n_fq(
n_fq_poly_t A,
const n_fq_poly_t B,
const n_fq_poly_t C,
const ulong * s,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
slong i;
ulong * Acoeffs;
ulong * Bcoeffs;
ulong * Ccoeffs;
slong Blen = B->length;
slong Clen = C->length;
ulong * t;
TMP_INIT;
n_poly_fit_length(A, d*FLINT_MAX(Blen, Clen));
Bcoeffs = B->coeffs;
Ccoeffs = C->coeffs;
TMP_START;
t = TMP_ALLOC(d*N_FQ_MUL_ITCH*sizeof(ulong));
if (Blen > Clen)
{
n_poly_fit_length(A, d*Blen);
Acoeffs = A->coeffs;
for (i = 0; i < Clen; i++)
_n_fq_addmul(Acoeffs + d*i, Bcoeffs + d*i, Ccoeffs + d*i, s, ctx, t);
if (A != B)
_nmod_vec_set(Acoeffs + d*Clen, Bcoeffs + d*Clen, d*(Blen - Clen));
A->length = Blen;
}
else if (Blen < Clen)
{
n_poly_fit_length(A, d*Clen);
Acoeffs = A->coeffs;
for (i = 0; i < Blen; i++)
_n_fq_addmul(Acoeffs + d*i, Bcoeffs + d*i, Ccoeffs + d*i, s, ctx, t);
for ( ; i < Clen; i++)
_n_fq_mul(Acoeffs + d*i, Ccoeffs + d*i, s, ctx, t);
A->length = Clen;
}
else
{
n_poly_fit_length(A, d*Blen);
Acoeffs = A->coeffs;
for (i = 0; i < Blen; i++)
_n_fq_addmul(Acoeffs + d*i, Bcoeffs + d*i, Ccoeffs + d*i, s, ctx, t);
A->length = Blen;
_n_fq_poly_normalise(A, d);
}
TMP_END;
}
void n_fq_poly_shift_left_scalar_submul(
n_poly_t A,
slong k,
const ulong * c,
const fq_nmod_ctx_t ctx)
{
slong d = fq_nmod_ctx_degree(ctx);
ulong * Acoeffs;
slong i;
slong Alen = A->length;
ulong * u = FLINT_ARRAY_ALLOC(d, ulong);
n_poly_fit_length(A, d*(Alen + k));
Acoeffs = A->coeffs;
flint_mpn_copyd(Acoeffs + d*k, Acoeffs, d*Alen);
flint_mpn_zero(Acoeffs, d*k);
for (i = 0; i < A->length; i++)
{
n_fq_mul(u, c, Acoeffs + d*(i + k), ctx);
_n_fq_sub(Acoeffs + d*i, Acoeffs + d*i, u, d, fq_nmod_ctx_mod(ctx));
}
A->length = Alen + k;
flint_free(u);
}
ulong n_fq_poly_remove(
n_poly_t f,
const n_poly_t g,
const fq_nmod_ctx_t ctx)
{
n_poly_t q, r;
ulong i = 0;
n_poly_init(q);
n_poly_init(r);
while (1)
{
if (f->length < g->length)
break;
n_fq_poly_divrem(q, r, f, g, ctx);
if (r->length == 0)
n_poly_swap(q, f);
else
break;
i++;
}
n_poly_clear(q);
n_poly_clear(r);
return i;
}