#include "fmpz.h"
#include "fmpz_poly.h"
#include "mpoly.h"
#include "fmpz_mpoly.h"
static slong fmpz_mpolyd_length(const fmpz_mpolyd_t A)
{
slong i, j;
slong degb_prod;
degb_prod = WORD(1);
for (j = 0; j < A->nvars; j++)
{
degb_prod *= A->deg_bounds[j];
}
for (i = degb_prod; i > 0; i--)
{
if (!fmpz_is_zero(A->coeffs + i - 1))
{
break;
}
}
return i;
}
static int fmpz_mpolyd_set_degbounds(fmpz_mpolyd_t A, slong * bounds)
{
slong i;
int success = 0;
slong degb_prod;
degb_prod = 1;
for (i = 0; i < A->nvars; i++)
{
ulong hi;
A->deg_bounds[i] = bounds[i];
umul_ppmm(hi, degb_prod, degb_prod, A->deg_bounds[i]);
if (hi != WORD(0) || degb_prod < 0)
{
goto done;
}
}
success = 1;
fmpz_mpolyd_fit_length(A, degb_prod);
done:
return success;
}
static void fmpz_mpoly_convert_to_fmpz_mpolyd_degbound(fmpz_mpolyd_t poly1,
const fmpz_mpoly_t poly2, const fmpz_mpoly_ctx_t ctx)
{
slong degb_prod;
slong i, j, N;
ulong * exps;
slong nvars = ctx->minfo->nvars;
TMP_INIT;
FLINT_ASSERT(poly1->nvars == nvars);
FLINT_ASSERT(poly2->bits <= FLINT_BITS);
degb_prod = WORD(1);
for (i = 0; i < nvars; i++)
{
degb_prod *= poly1->deg_bounds[i];
}
for (i = 0; i < degb_prod; i++)
{
fmpz_zero(poly1->coeffs + i);
}
if (poly2->length == 0)
{
return;
}
TMP_START;
exps = (ulong *) TMP_ALLOC(nvars*sizeof(ulong));
N = mpoly_words_per_exp(poly2->bits, ctx->minfo);
for (i = 0; i < poly2->length; i++)
{
slong off;
mpoly_get_monomial_ui(exps, poly2->exps + N*i, poly2->bits, ctx->minfo);
off = 0;
for (j = 0; j < nvars; j++)
{
off = exps[j] + poly1->deg_bounds[j]*off;
}
fmpz_set(poly1->coeffs + off, poly2->coeffs + i);
}
TMP_END;
}
static void fmpz_mpoly_consume_fmpz_mpolyd_clear(fmpz_mpoly_t A, fmpz_mpolyd_t B,
const fmpz_mpoly_ctx_t ctx)
{
slong i, j, k, N;
slong bits, nvars = ctx->minfo->nvars;
slong Alen;
ulong diff;
ulong topmask;
ulong * exps, * ptempexp, * plastexp;
TMP_INIT;
FLINT_ASSERT(nvars == B->nvars);
TMP_START;
exps = (ulong *) TMP_ALLOC(nvars*sizeof(ulong));
for (i = B->coeff_alloc - 1; i >= B->length; i--)
fmpz_clear(B->coeffs + i);
for (j = 0; j < nvars; j++)
{
exps[j] = B->deg_bounds[j] - 1;
}
bits = mpoly_exp_bits_required_ui(exps, ctx->minfo);
bits = mpoly_fix_bits(bits, ctx->minfo);
N = mpoly_words_per_exp(bits, ctx->minfo);
Alen = 0;
fmpz_mpoly_zero(A, ctx);
fmpz_mpoly_fit_length_reset_bits(A, 0, bits, ctx);
plastexp = (ulong *) TMP_ALLOC(N*sizeof(ulong));
for (j = 0; j < nvars; j++)
exps[j] = (j == nvars - 1);
mpoly_set_monomial_ui(plastexp, exps, bits, ctx->minfo);
ptempexp = (ulong *) TMP_ALLOC(N*sizeof(ulong));
k = i;
for (j = nvars - 1; j >= 0; j--)
{
exps[j] = k % B->deg_bounds[j];
k = k / B->deg_bounds[j];
}
mpoly_set_monomial_ui(ptempexp, exps, bits, ctx->minfo);
diff = 0;
topmask = 0;
for (; i >= 0; i--)
{
if (!fmpz_is_zero(B->coeffs + i))
{
_fmpz_mpoly_fit_length(&A->coeffs, &A->exps, &A->alloc, Alen + 1, N);
fmpz_swap(A->coeffs + Alen, B->coeffs + i);
mpoly_monomial_msub_mp(A->exps + N*Alen, ptempexp, diff, plastexp, N);
topmask |= (A->exps + N*Alen)[N - 1];
Alen++;
}
fmpz_clear(B->coeffs + i);
diff++;
--exps[nvars - 1];
if ((slong)(exps[nvars - 1]) < WORD(0))
{
exps[nvars - 1] = B->deg_bounds[nvars - 1] - 1;
for (j = nvars - 2; j >= 0; j--)
{
--exps[j];
if ((slong)(exps[j]) < WORD(0))
{
FLINT_ASSERT(i == 0 || j > 0);
exps[j] = B->deg_bounds[j] - 1;
} else
{
break;
}
}
mpoly_set_monomial_ui(ptempexp, exps, bits, ctx->minfo);
diff = 0;
}
}
_fmpz_mpoly_set_length(A, Alen, ctx);
if (ctx->minfo->ord != ORD_LEX)
{
slong msb;
mpoly_get_cmpmask(ptempexp, N, bits, ctx->minfo);
if (topmask != WORD(0))
{
msb = flint_clz(topmask);
msb = (FLINT_BITS - 1)^msb;
} else
{
msb = -WORD(1);
}
if (N == 1) {
if (msb >= WORD(0))
{
_fmpz_mpoly_radix_sort1(A, 0, A->length,
msb, ptempexp[0], topmask);
}
} else {
_fmpz_mpoly_radix_sort(A, 0, A->length,
(N - 1)*FLINT_BITS + msb, N, ptempexp);
}
}
flint_free(B->deg_bounds);
flint_free(B->coeffs);
B->deg_bounds = NULL;
B->coeffs = NULL;
TMP_END;
}
int _fmpz_mpoly_mul_dense(fmpz_mpoly_t P,
const fmpz_mpoly_t A, fmpz * maxAfields,
const fmpz_mpoly_t B, fmpz * maxBfields,
const fmpz_mpoly_ctx_t ctx)
{
int success, P_is_stolen;
slong i;
slong nvars = ctx->minfo->nvars;
fmpz_mpolyd_t Ad, Bd, Pd;
fmpz_poly_t Au, Bu, Pu;
slong * Abounds, * Bbounds, * Pbounds;
TMP_INIT;
FLINT_ASSERT(A->length != 0);
FLINT_ASSERT(B->length != 0);
FLINT_ASSERT(nvars > 0);
FLINT_ASSERT(A->bits <= FLINT_BITS);
FLINT_ASSERT(B->bits <= FLINT_BITS);
TMP_START;
Abounds = (slong *) TMP_ALLOC(ctx->minfo->nvars*sizeof(slong));
Bbounds = (slong *) TMP_ALLOC(ctx->minfo->nvars*sizeof(slong));
Pbounds = (slong *) TMP_ALLOC(ctx->minfo->nvars*sizeof(slong));
mpoly_get_monomial_ui_unpacked_ffmpz((ulong *)Abounds, maxAfields, ctx->minfo);
mpoly_get_monomial_ui_unpacked_ffmpz((ulong *)Bbounds, maxBfields, ctx->minfo);
for (i = 0; i < ctx->minfo->nvars; i++)
{
Abounds[i] = Abounds[i] + 1;
Bbounds[i] = Bbounds[i] + 1;
Pbounds[i] = Abounds[i] + Bbounds[i] - 1;
if ((Abounds[i] | Bbounds[i] | Pbounds[i]) < 0)
{
goto failed_stage1;
}
if (i > 0)
{
Abounds[i] = Pbounds[i];
Bbounds[i] = Pbounds[i];
}
}
fmpz_mpolyd_init(Ad, nvars);
fmpz_mpolyd_init(Bd, nvars);
P_is_stolen = 0;
if (P != A && P != B && P->alloc > 0)
{
Pd->nvars = nvars;
Pd->degb_alloc = nvars;
Pd->deg_bounds = (slong *) flint_malloc(Pd->degb_alloc*sizeof(slong));
for (i = 0; i < nvars; i++)
{
Pd->deg_bounds[i] = WORD(1);
}
Pd->coeffs = P->coeffs;
Pd->coeff_alloc = P->alloc;
P->coeffs = (fmpz *) flint_calloc(P->alloc, sizeof(fmpz));
P_is_stolen = 1;
}
else
{
fmpz_mpolyd_init(Pd, ctx->minfo->nvars);
}
success = 1;
success = success && fmpz_mpolyd_set_degbounds(Ad, Abounds);
success = success && fmpz_mpolyd_set_degbounds(Bd, Bbounds);
success = success && fmpz_mpolyd_set_degbounds(Pd, Pbounds);
if (!success)
{
goto failed_stage2;
}
fmpz_mpoly_convert_to_fmpz_mpolyd_degbound(Ad, A, ctx);
fmpz_mpoly_convert_to_fmpz_mpolyd_degbound(Bd, B, ctx);
Au->alloc = Ad->coeff_alloc;
Au->coeffs = Ad->coeffs;
Au->length = fmpz_mpolyd_length(Ad);
Bu->alloc = Bd->coeff_alloc;
Bu->coeffs = Bd->coeffs;
Bu->length = fmpz_mpolyd_length(Bd);
Pu->alloc = Pd->coeff_alloc;
Pu->coeffs = Pd->coeffs;
Pu->length = 0;
fmpz_poly_mul(Pu, Au, Bu);
Pd->coeff_alloc = Pu->alloc;
Pd->coeffs = Pu->coeffs;
Pd->length = Pu->length;
fmpz_mpolyd_clear(Bd);
fmpz_mpolyd_clear(Ad);
fmpz_mpoly_consume_fmpz_mpolyd_clear(P, Pd, ctx);
done:
TMP_END;
return success;
failed_stage2:
fmpz_mpolyd_clear(Ad);
fmpz_mpolyd_clear(Bd);
if (P_is_stolen)
{
fmpz * t = Pd->coeffs;
Pd->coeffs = P->coeffs;
P->coeffs = t;
}
fmpz_mpolyd_clear(Pd);
failed_stage1:
success = 0;
goto done;
}
int fmpz_mpoly_mul_dense(fmpz_mpoly_t A, const fmpz_mpoly_t B,
const fmpz_mpoly_t C, const fmpz_mpoly_ctx_t ctx)
{
slong i;
int success;
fmpz * maxBfields, * maxCfields;
TMP_INIT;
if (B->length == 0 || C->length == 0)
{
fmpz_mpoly_zero(A, ctx);
return 1;
}
if (B->bits > FLINT_BITS || C->bits > FLINT_BITS ||
ctx->minfo->nvars < 1)
{
return 0;
}
TMP_START;
maxBfields = (fmpz *) TMP_ALLOC(ctx->minfo->nfields*sizeof(fmpz));
maxCfields = (fmpz *) TMP_ALLOC(ctx->minfo->nfields*sizeof(fmpz));
for (i = 0; i < ctx->minfo->nfields; i++)
{
fmpz_init(maxBfields + i);
fmpz_init(maxCfields + i);
}
mpoly_max_fields_fmpz(maxBfields, B->exps, B->length, B->bits, ctx->minfo);
mpoly_max_fields_fmpz(maxCfields, C->exps, C->length, C->bits, ctx->minfo);
success = _fmpz_mpoly_mul_dense(A, B, maxBfields, C, maxCfields, ctx);
for (i = 0; i < ctx->minfo->nfields; i++)
{
fmpz_clear(maxBfields + i);
fmpz_clear(maxCfields + i);
}
TMP_END;
return success;
}