#include "mpn_extras.h"
#include "nmod_poly.h"
#include "fq_nmod.h"
#include "fq_nmod_vec.h"
#include "fq_nmod_poly.h"
static void
_fq_nmod_poly_mul_univariate_no_pad (fq_nmod_struct * rop,
const fq_nmod_struct * op1, slong len1,
const fq_nmod_struct * op2, slong len2,
const fq_nmod_ctx_t ctx)
{
const slong fqlen = ctx->modulus->length - 1;
const slong pfqlen = 2*fqlen - 1;
const nmod_t mod = ctx->mod;
const slong rlen = len1 + len2 - 1;
const slong llen1 = (op1 + (len1 - 1))->length;
const slong llen2 = (op2 + (len2 - 1))->length;
const slong clen1 = pfqlen*(len1-1) + llen1;
const slong clen2 = pfqlen*(len2-1) + llen2;
const slong crlen = clen1 + clen2 - 1;
const slong lrlen = llen1 + llen2 - 1;
slong i;
slong len;
nn_ptr cop1, cop2, crop;
cop1 = (ulong *) flint_malloc(clen1*sizeof(ulong));
for (i = 0; i < len1 - 1; i++)
{
flint_mpn_copyi(cop1 + pfqlen*i, (op1 + i)->coeffs, (op1 + i)->length);
flint_mpn_zero(cop1 + pfqlen*i + (op1 + i)->length, pfqlen - (op1 + i)->length);
}
{
flint_mpn_copyi(cop1 + pfqlen*i, (op1 + i)->coeffs, (op1 + i)->length);
}
if (op2 != op1)
{
cop2 = (ulong *) flint_malloc(clen2*sizeof(ulong));
for (i = 0; i < len2 - 1; i++)
{
flint_mpn_copyi(cop2 + pfqlen*i, (op2 + i)->coeffs,(op2 + i)->length);
flint_mpn_zero(cop2 + pfqlen*i + (op2 + i)->length, pfqlen - (op2 + i)->length);
}
{
flint_mpn_copyi(cop2 + pfqlen*i, (op2 + i)->coeffs, (op2 + i)->length);
}
}
else
{
cop2 = cop1;
}
crop = (ulong *) flint_malloc(crlen*sizeof(ulong));
if (clen1 >= clen2)
_nmod_poly_mul(crop, cop1, clen1, cop2, clen2, mod);
else
_nmod_poly_mul(crop, cop2, clen2, cop1, clen1, mod);
for (i = 0; i < rlen - 1; i++)
{
_fq_nmod_reduce(crop + pfqlen*i, pfqlen, ctx);
len = fqlen;
while (len && (*(crop + pfqlen*i + len - 1) == UWORD(0))) len--;
nmod_poly_fit_length(rop + i, len);
(rop + i)->length = len;
flint_mpn_copyi((rop + i)->coeffs, crop + pfqlen*i, len);
}
{
len = lrlen;
if (len > fqlen)
{
_fq_nmod_reduce(crop + pfqlen*i, len, ctx);
len = fqlen;
while (len && (*(crop + pfqlen*i + len - 1) == UWORD(0))) len--;
}
nmod_poly_fit_length(rop + i, len);
(rop + i)->length = len;
flint_mpn_copyi((rop + i)->coeffs, crop + pfqlen*i, len);
}
flint_free(cop1);
if (op2 != op1)
{
flint_free(cop2);
}
flint_free(crop);
}
void
_fq_nmod_poly_mul_univariate (fq_nmod_struct * rop, const fq_nmod_struct * op1, slong len1, const fq_nmod_struct * op2, slong len2, const fq_nmod_ctx_t ctx)
{
slong len1n = len1, len2n = len2;
_fq_nmod_poly_normalise2(op1, &len1n, ctx);
_fq_nmod_poly_normalise2(op2, &len2n, ctx);
if (len1n == 0)
_fq_nmod_vec_zero(rop, len1 + len2 - 1, ctx);
else if (len2n == 0)
_fq_nmod_vec_zero(rop, len1 + len2 - 1, ctx);
else
{
_fq_nmod_poly_mul_univariate_no_pad(rop, op1, len1n, op2, len2n, ctx);
_fq_nmod_vec_zero(rop + len1n + len2n - 1,
len1 + len2 - len1n - len2n, ctx);
}
}
void
fq_nmod_poly_mul_univariate (fq_nmod_poly_t rop,
const fq_nmod_poly_t op1,
const fq_nmod_poly_t op2,
const fq_nmod_ctx_t ctx)
{
const slong len1 = op1->length;
const slong len2 = op2->length;
const slong rlen = op1->length + op2->length - 1;
if (len1 == 0 || len2 == 0)
{
fq_nmod_poly_zero(rop, ctx);
return;
}
else
{
fq_nmod_poly_fit_length(rop, rlen, ctx);
_fq_nmod_poly_mul_univariate(rop->coeffs, op1->coeffs, len1,
op2->coeffs, len2, ctx);
_fq_nmod_poly_set_length(rop, rlen, ctx);
}
}