#include "fmpz_mod_mpoly.h"
typedef struct
{
slong a;
slong b;
} pair_t;
typedef struct
{
pair_t * pairs;
slong length;
slong alloc;
}
pairs_struct;
typedef pairs_struct pairs_t[1];
static void
pairs_init(pairs_t vec)
{
vec->pairs = NULL;
vec->length = 0;
vec->alloc = 0;
}
static void
pairs_fit_length(pairs_t vec, slong len)
{
if (len > vec->alloc)
{
if (len < 2 * vec->alloc)
len = 2 * vec->alloc;
vec->pairs = flint_realloc(vec->pairs, len * sizeof(pair_t));
vec->alloc = len;
}
}
static void
pairs_clear(pairs_t vec)
{
flint_free(vec->pairs);
}
static void
pairs_append(pairs_t vec, slong i, slong j)
{
pairs_fit_length(vec, vec->length + 1);
vec->pairs[vec->length].a = i;
vec->pairs[vec->length].b = j;
vec->length++;
}
#define BUCHBERGER_DEGREE_SELECTION 1
static pair_t
fmpz_mod_mpoly_select_pop_pair(pairs_t pairs, const fmpz_mod_mpoly_vec_t G,
const fmpz_mod_mpoly_ctx_t ctx)
{
slong len, choice, nvars;
pair_t result;
nvars = ctx->minfo->nvars;
len = pairs->length;
choice = 0;
if (len > 1)
{
slong i, j, a, b;
ulong * exp;
ulong * lcm;
ulong * best_lcm;
ulong l, total;
int best;
exp = flint_malloc(sizeof(ulong) * G->length * nvars);
lcm = flint_malloc(sizeof(ulong) * (nvars + 1));
best_lcm = flint_malloc(sizeof(ulong) * (nvars + 1));
for (i = 0; i <= nvars; i++)
best_lcm[i] = UWORD_MAX;
for (i = 0; i < G->length; i++)
fmpz_mod_mpoly_get_term_exp_ui(exp + i * nvars, G->p + i, 0, ctx);
for (i = 0; i < len; i++)
{
a = pairs->pairs[i].a;
b = pairs->pairs[i].b;
total = 0;
best = 1;
for (j = 0; j < nvars; j++)
{
l = FLINT_MAX(exp[a * nvars + j], exp[b * nvars + j]);
lcm[j] = l;
total += l;
}
#if BUCHBERGER_DEGREE_SELECTION
if (total > best_lcm[nvars])
{
best = 0;
}
else if (total == best_lcm[nvars] && ctx->minfo->ord == ORD_LEX)
{
for (j = 0; j < nvars; j++)
{
if (lcm[j] > best_lcm[j]) { best = 0; break; }
if (lcm[j] < best_lcm[j]) break;
}
}
else if (total == best_lcm[nvars])
{
best = 0;
}
#else
if (ctx->minfo->ord == ORD_LEX)
{
for (j = 0; j < nvars; j++)
{
if (lcm[j] > best_lcm[j]) { best = 0; break; }
if (lcm[j] < best_lcm[j]) break;
}
}
else
{
if (total > best_lcm[nvars])
best = 0;
else if (total == best_lcm[nvars])
best = 0;
}
#endif
if (best)
{
for (j = 0; j < nvars; j++)
best_lcm[j] = lcm[j];
best_lcm[nvars] = total;
choice = i;
}
}
flint_free(exp);
flint_free(lcm);
flint_free(best_lcm);
}
result = pairs->pairs[choice];
pairs->pairs[choice] = pairs->pairs[pairs->length - 1];
pairs->length--;
return result;
}
static int
within_limits(const fmpz_mod_mpoly_t poly, slong poly_len_limit,
const fmpz_mod_mpoly_ctx_t ctx)
{
if (fmpz_mod_mpoly_length(poly, ctx) > poly_len_limit)
return 0;
return 1;
}
static int
monomial_divides(const ulong * a, const ulong * b, slong nvars)
{
slong i;
for (i = 0; i < nvars; i++)
if (a[i] > b[i])
return 0;
return 1;
}
static int
monomial_disjoint(const ulong * a, const ulong * b, slong nvars)
{
slong i;
for (i = 0; i < nvars; i++)
if (a[i] && b[i])
return 0;
return 1;
}
static void
monomial_lcm(ulong * out, const ulong * a, const ulong * b, slong nvars)
{
slong i;
for (i = 0; i < nvars; i++)
out[i] = FLINT_MAX(a[i], b[i]);
}
static int
monomial_equal(const ulong * a, const ulong * b, slong nvars)
{
slong i;
for (i = 0; i < nvars; i++)
if (a[i] != b[i])
return 0;
return 1;
}
static void
filter_redundant(slong * G_active, slong * G_active_len,
slong ih, const ulong * exp, slong nvars)
{
const ulong * mh;
slong i, new_active_len;
mh = exp + ih * nvars;
new_active_len = 0;
for (i = 0; i < *G_active_len; i++)
{
slong ig = G_active[i];
if (!monomial_divides(mh, exp + ig * nvars, nvars))
G_active[new_active_len++] = ig;
}
*G_active_len = new_active_len;
}
static void
update_pairs(const slong * G_active, slong G_active_len,
pairs_t B, slong ih, const ulong * exp, slong nvars)
{
const ulong * mh;
ulong * lcm_hg;
ulong * lcm_tmp;
slong * D_ig;
slong * E_ig;
slong D_alloc, D_len, E_len;
slong i, k;
int dominated;
mh = exp + ih * nvars;
lcm_hg = flint_malloc(nvars * sizeof(ulong));
lcm_tmp = flint_malloc(nvars * sizeof(ulong));
D_alloc = G_active_len + 1;
D_len = 0;
D_ig = flint_malloc(D_alloc * sizeof(slong));
for (i = 0; i < G_active_len; i++)
{
slong ig = G_active[i];
const ulong * mg = exp + ig * nvars;
if (!monomial_disjoint(mh, mg, nvars))
{
monomial_lcm(lcm_hg, mh, mg, nvars);
dominated = 0;
for (k = 0; k < D_len && !dominated; k++)
{
monomial_lcm(lcm_tmp, mh, exp + D_ig[k] * nvars, nvars);
if (monomial_divides(lcm_tmp, lcm_hg, nvars))
dominated = 1;
}
for (k = i + 1; k < G_active_len && !dominated; k++)
{
monomial_lcm(lcm_tmp, mh, exp + G_active[k] * nvars, nvars);
if (monomial_divides(lcm_tmp, lcm_hg, nvars))
dominated = 1;
}
if (dominated)
continue;
}
if (D_len == D_alloc)
{
D_alloc *= 2;
D_ig = flint_realloc(D_ig, D_alloc * sizeof(slong));
}
D_ig[D_len++] = ig;
}
E_ig = flint_malloc((D_len + 1) * sizeof(slong));
E_len = 0;
for (i = 0; i < D_len; i++)
{
slong ig = D_ig[i];
if (!monomial_disjoint(mh, exp + ig * nvars, nvars))
E_ig[E_len++] = ig;
}
flint_free(D_ig);
{
slong new_len = 0;
for (k = 0; k < B->length; k++)
{
slong ig1 = B->pairs[k].a;
slong ig2 = B->pairs[k].b;
const ulong * mg1 = exp + ig1 * nvars;
const ulong * mg2 = exp + ig2 * nvars;
monomial_lcm(lcm_hg, mg1, mg2, nvars);
if (!monomial_divides(mh, lcm_hg, nvars))
goto keep;
monomial_lcm(lcm_tmp, mg1, mh, nvars);
if (monomial_equal(lcm_tmp, lcm_hg, nvars))
goto keep;
monomial_lcm(lcm_tmp, mg2, mh, nvars);
if (monomial_equal(lcm_tmp, lcm_hg, nvars))
goto keep;
continue;
keep:
B->pairs[new_len++] = B->pairs[k];
}
B->length = new_len;
}
for (i = 0; i < E_len; i++)
pairs_append(B, ih, E_ig[i]);
flint_free(E_ig);
flint_free(lcm_hg);
flint_free(lcm_tmp);
}
int
fmpz_mod_mpoly_buchberger_naive_with_limits(fmpz_mod_mpoly_vec_t G,
const fmpz_mod_mpoly_vec_t F,
slong ideal_len_limit, slong poly_len_limit,
const fmpz_mod_mpoly_ctx_t ctx)
{
pairs_t B;
fmpz_mod_mpoly_t h;
slong * G_active;
ulong * exp;
slong i, ih, G_active_len, G_active_alloc, exp_alloc, nvars;
pair_t pair;
int success;
fmpz_mod_mpoly_vec_set_monic_unique(G, F, ctx);
if (G->length <= 1)
return 1;
if (G->length >= ideal_len_limit)
return 0;
for (i = 0; i < G->length; i++)
if (!within_limits(fmpz_mod_mpoly_vec_entry(G, i), poly_len_limit, ctx))
return 0;
nvars = ctx->minfo->nvars;
exp_alloc = G->length + 16;
exp = flint_malloc(exp_alloc * nvars * sizeof(ulong));
for (i = 0; i < G->length; i++)
fmpz_mod_mpoly_get_term_exp_ui(exp + i * nvars,
fmpz_mod_mpoly_vec_entry(G, i), 0, ctx);
G_active_alloc = exp_alloc;
G_active = flint_malloc(G_active_alloc * sizeof(slong));
G_active_len = 0;
pairs_init(B);
fmpz_mod_mpoly_init(h, ctx);
G_active[G_active_len++] = 0;
for (i = 1; i < G->length; i++)
{
update_pairs(G_active, G_active_len, B, i, exp, nvars);
filter_redundant(G_active, &G_active_len, i, exp, nvars);
G_active[G_active_len++] = i;
}
success = 1;
while (B->length != 0)
{
pair = fmpz_mod_mpoly_select_pop_pair(B, G, ctx);
fmpz_mod_mpoly_spoly(h, fmpz_mod_mpoly_vec_entry(G, pair.a),
fmpz_mod_mpoly_vec_entry(G, pair.b), ctx);
fmpz_mod_mpoly_reduction_monic_part(h, h, G, ctx);
if (!fmpz_mod_mpoly_is_zero(h, ctx))
{
if (G->length >= ideal_len_limit ||
!within_limits(h, poly_len_limit, ctx))
{
success = 0;
break;
}
ih = G->length;
fmpz_mod_mpoly_vec_append(G, h, ctx);
if (ih >= exp_alloc)
{
exp_alloc = FLINT_MAX(exp_alloc * 2, ih + 1);
exp = flint_realloc(exp, exp_alloc * nvars * sizeof(ulong));
}
fmpz_mod_mpoly_get_term_exp_ui(exp + ih * nvars,
fmpz_mod_mpoly_vec_entry(G, ih), 0,
ctx);
if (G_active_len + 1 > G_active_alloc)
{
G_active_alloc = FLINT_MAX(G_active_alloc * 2,
G_active_len + 2);
G_active = flint_realloc(G_active,
G_active_alloc * sizeof(slong));
}
update_pairs(G_active, G_active_len, B, ih, exp, nvars);
filter_redundant(G_active, &G_active_len, ih, exp, nvars);
G_active[G_active_len++] = ih;
}
}
for (i = 0; i < G_active_len; i++)
{
FLINT_ASSERT(G_active[i] >= i);
fmpz_mod_mpoly_swap(fmpz_mod_mpoly_vec_entry(G, i),
fmpz_mod_mpoly_vec_entry(G, G_active[i]), ctx);
}
fmpz_mod_mpoly_clear(h, ctx);
pairs_clear(B);
flint_free(G_active);
flint_free(exp);
return success;
}
void
fmpz_mod_mpoly_buchberger_naive(fmpz_mod_mpoly_vec_t G,
const fmpz_mod_mpoly_vec_t F, const fmpz_mod_mpoly_ctx_t ctx)
{
fmpz_mod_mpoly_buchberger_naive_with_limits(G, F, WORD_MAX, WORD_MAX, ctx);
}