#include "long_extras.h"
#include "fmpz_mod_poly.h"
#include "mpoly.h"
#include "fmpz_mod_mpoly.h"
static int _from_dense(
fmpz_mod_mpoly_t A,
flint_bitcnt_t Abits,
slong * Adeg_bounds,
slong * expect_deg,
fmpz_mod_poly_t D,
const fmpz_mod_mpoly_ctx_t ctx)
{
int ret;
slong off, j, k, N;
flint_bitcnt_t bits;
slong nvars = ctx->minfo->nvars;
slong Alen;
ulong topmask, outrange;
ulong * exps, * pcurexp, * pexps, * rangemask;
TMP_INIT;
FLINT_ASSERT(nvars <= FLINT_BITS);
TMP_START;
exps = (ulong *) TMP_ALLOC(nvars*sizeof(ulong));
off = 1;
for (j = 0; j < nvars; j++)
{
FLINT_ASSERT(expect_deg[j] >= 0);
off *= Adeg_bounds[j];
exps[j] = expect_deg[j];
}
bits = mpoly_exp_bits_required_ui(exps, ctx->minfo);
bits = FLINT_MAX(Abits, bits);
bits = mpoly_fix_bits(bits, ctx->minfo);
N = mpoly_words_per_exp(bits, ctx->minfo);
fmpz_mod_mpoly_fit_length_reset_bits(A, 0, bits, ctx);
Alen = 0;
pexps = (ulong *) TMP_ALLOC(N*nvars*sizeof(ulong));
for (k = 0; k < nvars; k++)
mpoly_gen_monomial_sp(pexps + k*N, k, bits, ctx->minfo);
off--;
pcurexp = (ulong *) TMP_ALLOC(N*sizeof(ulong));
rangemask = (ulong *) TMP_ALLOC(nvars*sizeof(ulong));
outrange = 0;
mpoly_monomial_zero(pcurexp, N);
k = off;
for (j = nvars - 1; j >= 0; j--)
{
exps[j] = k % Adeg_bounds[j];
rangemask[j] = UWORD(1) << j;
outrange ^= (outrange ^ FLINT_SIGN_EXT(expect_deg[j] - exps[j])) & rangemask[j];
k = k / Adeg_bounds[j];
mpoly_monomial_madd_inplace_mp(pcurexp, exps[j], pexps + N*j, N);
}
topmask = 0;
for (; off >= 0; off--)
{
if (off < D->length && !fmpz_is_zero(D->coeffs + off))
{
if (outrange)
{
_fmpz_mod_mpoly_set_length(A, 0, ctx);
ret = 0;
goto cleanup;
}
_fmpz_mod_mpoly_fit_length(&A->coeffs, &A->coeffs_alloc,
&A->exps, &A->exps_alloc, N, Alen + 1);
fmpz_swap(A->coeffs + Alen, D->coeffs + off);
mpoly_monomial_set(A->exps + N*Alen, pcurexp, N);
topmask |= (A->exps + N*Alen)[N - 1];
Alen++;
}
j = nvars - 1;
do {
--exps[j];
outrange ^= (outrange ^ FLINT_SIGN_EXT(expect_deg[j] - exps[j])) & rangemask[j];
if (FLINT_SIGN_EXT(exps[j]) != 0)
{
FLINT_ASSERT(off == 0 || j > 0);
FLINT_ASSERT(exps[j] == -UWORD(1));
exps[j] = Adeg_bounds[j] - 1;
outrange ^= (outrange ^ FLINT_SIGN_EXT(expect_deg[j] - exps[j])) & rangemask[j];
mpoly_monomial_madd_inplace_mp(pcurexp, exps[j], pexps + N*j, N);
}
else
{
mpoly_monomial_sub_mp(pcurexp, pcurexp, pexps + N*j, N);
break;
}
} while (--j >= 0);
}
_fmpz_mod_mpoly_set_length(A, Alen, ctx);
if (ctx->minfo->ord != ORD_LEX)
{
flint_bitcnt_t pos;
mpoly_get_cmpmask(pcurexp, N, bits, ctx->minfo);
pos = FLINT_BIT_COUNT(topmask);
if (N == 1)
_fmpz_mod_mpoly_radix_sort1(A->coeffs, A->exps, 0, A->length,
pos, pcurexp[0], topmask);
else
_fmpz_mod_mpoly_radix_sort(A->coeffs, A->exps, 0, A->length,
(N - 1)*FLINT_BITS + pos, N, pcurexp);
}
ret = 1;
cleanup:
TMP_END;
return ret;
}
int _fmpz_mod_mpoly_divides_dense_maxfields(
fmpz_mod_mpoly_t Q,
const fmpz_mod_mpoly_t A, fmpz * maxAfields,
const fmpz_mod_mpoly_t B, fmpz * maxBfields,
const fmpz_mod_mpoly_ctx_t ctx)
{
int success;
slong i;
slong nvars = ctx->minfo->nvars;
fmpz_mod_poly_t Ad, Bd, Qd, Rd;
slong * Abounds, * Bbounds, * Qbounds, * Edegs;
slong prod_deg;
TMP_INIT;
FLINT_ASSERT(A->length > 0);
FLINT_ASSERT(B->length > 0);
FLINT_ASSERT(A->bits <= FLINT_BITS);
FLINT_ASSERT(B->bits <= FLINT_BITS);
FLINT_ASSERT(0 < ctx->minfo->nvars);
FLINT_ASSERT(ctx->minfo->nvars <= FLINT_BITS);
TMP_START;
Abounds = TMP_ARRAY_ALLOC(4*nvars, slong);
Bbounds = Abounds + nvars;
Qbounds = Bbounds + nvars;
Edegs = Qbounds + nvars;
mpoly_get_monomial_ui_unpacked_ffmpz((ulong *)Abounds, maxAfields, ctx->minfo);
mpoly_get_monomial_ui_unpacked_ffmpz((ulong *)Bbounds, maxBfields, ctx->minfo);
prod_deg = 1;
for (i = 0; i < ctx->minfo->nvars; i++)
{
Edegs[i] = Abounds[i] - Bbounds[i];
if (Abounds[i] < Bbounds[i])
{
success = 0;
fmpz_mod_mpoly_zero(Q, ctx);
goto cleanup;
}
if (i != 0)
{
Qbounds[i] = Abounds[i] + 1;
Bbounds[i] = Abounds[i] + 1;
}
else
{
Qbounds[i] = Abounds[i] - Bbounds[i] + 1;
Bbounds[i] = Bbounds[i] + 1;
}
if (z_add_checked(&Abounds[i], Abounds[i], 1) ||
z_mul_checked(&prod_deg, prod_deg, Abounds[i]))
{
success = -1;
fmpz_mod_mpoly_zero(Q, ctx);
goto cleanup;
}
}
_fmpz_mod_mpoly_init_dense_mock(Ad, A, Abounds, ctx);
_fmpz_mod_mpoly_init_dense_mock(Bd, B, Bbounds, ctx);
fmpz_mod_poly_init(Qd, ctx->ffinfo);
fmpz_mod_poly_init(Rd, ctx->ffinfo);
fmpz_mod_poly_divrem(Qd, Rd, Ad, Bd, ctx->ffinfo);
success = fmpz_mod_poly_is_zero(Rd, ctx->ffinfo) &&
_from_dense(Q, A->bits, Qbounds, Edegs, Qd, ctx);
fmpz_mod_poly_clear(Qd, ctx->ffinfo);
fmpz_mod_poly_clear(Rd, ctx->ffinfo);
_fmpz_mod_mpoly_clear_dense_mock(Ad);
_fmpz_mod_mpoly_clear_dense_mock(Bd);
cleanup:
TMP_END;
return success;
}
int fmpz_mod_mpoly_divides_dense(
fmpz_mod_mpoly_t Q,
const fmpz_mod_mpoly_t A,
const fmpz_mod_mpoly_t B,
const fmpz_mod_mpoly_ctx_t ctx)
{
int success;
slong i;
fmpz * maxAfields, * maxBfields;
TMP_INIT;
if (fmpz_mod_mpoly_is_zero(B, ctx))
{
if (!fmpz_mod_mpoly_is_zero(A, ctx) &&
!fmpz_is_one(fmpz_mod_mpoly_ctx_modulus(ctx)))
{
flint_throw(FLINT_DIVZERO, "fmpz_mod_mpoly_divides_dense: divide by zero");
}
fmpz_mod_mpoly_zero(Q, ctx);
return 1;
}
if (fmpz_mod_mpoly_is_zero(A, ctx))
{
fmpz_mod_mpoly_zero(Q, ctx);
return 1;
}
if (A->bits > FLINT_BITS || B->bits > FLINT_BITS ||
ctx->minfo->nvars > FLINT_BITS ||
ctx->minfo->nvars < 1)
{
return -1;
}
TMP_START;
maxAfields = TMP_ARRAY_ALLOC(2*ctx->minfo->nfields, fmpz);
maxBfields = maxAfields + ctx->minfo->nfields;
for (i = 0; i < 2*ctx->minfo->nfields; i++)
fmpz_init(maxAfields + i);
mpoly_max_fields_fmpz(maxAfields, A->exps, A->length, A->bits, ctx->minfo);
mpoly_max_fields_fmpz(maxBfields, B->exps, B->length, B->bits, ctx->minfo);
success = _fmpz_mod_mpoly_divides_dense_maxfields(Q,
A, maxAfields, B, maxBfields, ctx);
for (i = 0; i < 2*ctx->minfo->nfields; i++)
fmpz_clear(maxAfields + i);
TMP_END;
return success;
}