#include "fmpz.h"
#include "mpoly.h"
#include "long_extras.h"
slong _mpoly_compress_exps(
slong * V,
slong * D,
slong * deg,
slong * S,
slong n,
slong l)
{
slong mind, maxd, tot;
slong * minp = FLINT_ARRAY_ALLOC(n, slong);
slong * maxp = FLINT_ARRAY_ALLOC(n, slong);
slong * minn = FLINT_ARRAY_ALLOC(n, slong);
slong * maxn = FLINT_ARRAY_ALLOC(n, slong);
slong * perm = FLINT_ARRAY_ALLOC(n, slong);
slong * tmp = maxn;
slong i, j, k, m;
slong best_loc_i, best_loc_j, best_min, best_deg;
fmpz_t this_prod, best_prod;
fmpz_init(this_prod);
fmpz_init(best_prod);
FLINT_ASSERT(n > 0);
FLINT_ASSERT(l > 1);
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
V[i*n + j] = (i == j);
for (j = 0; j < n; j++)
minp[j] = maxp[j] = S[0*n + j];
for (i = 1; i < l; i++)
for (j = 0; j < n; j++)
{
minp[j] = FLINT_MIN(minp[j], S[i*n + j]);
maxp[j] = FLINT_MAX(maxp[j], S[i*n + j]);
}
for (j = 0; j < n; j++)
{
D[j] = minp[j];
deg[j] = 1 + maxp[j] - minp[j];
}
for (i = 0; i < l; i++)
for (j = 0; j < n; j++)
S[i*n + j] -= D[j];
tot = 0;
for (j = 0; j < n; j++)
{
if (z_add_checked(&tot, tot, deg[j]))
goto done;
}
again:
fmpz_one(best_prod);
for (j = 0; j < n; j++)
fmpz_mul_si(best_prod, best_prod, deg[j]);
best_loc_i = -1;
best_loc_j = -1;
best_min = 0;
for (i = 0; i < n; i++)
{
slong this_best_j, this_best_min = WORD_MAX, this_best_deg;
mind = WORD_MAX;
maxd = WORD_MIN;
for (j = 0; j < n; j++)
{
minp[j] = minn[j] = WORD_MAX;
maxp[j] = maxn[j] = WORD_MIN;
}
for (k = 0; k < l; k++)
{
slong * Sk = S + k*n;
tot = 0;
for (j = 0; j < n; j++)
{
tot += Sk[j];
minp[j] = FLINT_MIN(minp[j], Sk[i] + Sk[j]);
maxp[j] = FLINT_MAX(maxp[j], Sk[i] + Sk[j]);
minn[j] = FLINT_MIN(minn[j], Sk[i] - Sk[j]);
maxn[j] = FLINT_MAX(maxn[j], Sk[i] - Sk[j]);
}
mind = FLINT_MIN(mind, tot);
maxd = FLINT_MAX(maxd, tot);
}
this_best_deg = deg[i];
this_best_j = n + 1;
if (1 + maxd - mind < this_best_deg)
{
this_best_j = 0;
this_best_min = mind;
this_best_deg = 1 + maxd - mind;
}
for (j = 0; j < n; j++)
{
if (j == i)
continue;
if (1 + maxp[j] - minp[j] < this_best_deg)
{
this_best_j = 1 + j;
this_best_min = minp[j];
this_best_deg = 1 + maxp[j] - minp[j];
}
if (1 + maxn[j] - minn[j] < this_best_deg)
{
this_best_j = -1 - j;
this_best_min = minn[j];
this_best_deg = 1 + maxn[j] - minn[j];
}
}
if (this_best_j > n)
continue;
fmpz_set_si(this_prod, this_best_deg);
for (j = 0; j < n; j++)
if (j != i)
fmpz_mul_si(this_prod, this_prod, deg[j]);
if (fmpz_cmp(this_prod, best_prod) < 0)
{
fmpz_swap(best_prod, this_prod);
best_loc_i = i;
best_loc_j = this_best_j;
best_min = this_best_min;
best_deg = this_best_deg;
}
}
if (best_loc_i >= 0)
{
i = best_loc_i;
j = best_loc_j;
deg[i] = best_deg;
if (j < 0)
{
j = -j - 1;
for (k = 0; k < l; k++)
S[k*n + i] += -S[k*n + j] - best_min;
for (k = 0; k < n; k++)
{
D[k] += best_min*V[k*n + i];
V[k*n + j] += V[k*n + i];
}
}
else if (j > 0)
{
j = j - 1;
for (k = 0; k < l; k++)
S[k*n + i] += S[k*n + j] - best_min;
for (k = 0; k < n; k++)
{
D[k] += best_min*V[k*n + i];
V[k*n + j] += -V[k*n + i];
}
}
else
{
for (k = 0; k < l; k++)
{
slong tot = 0;
for (j = 0; j < n; j++)
tot += S[k*n + j];
S[k*n + i] = tot - best_min;
}
for (k = 0; k < n; k++)
{
D[k] += best_min*V[k*n + i];
for (j = 0; j < n; j++)
if (j != i)
V[k*n + j] += -V[k*n + i];
}
}
goto again;
}
done:
for (i = 0; i < n; i++)
tmp[i] = i;
for (i = 1; i < n; i++)
for (j = i; j > 0 && deg[tmp[j]] < deg[tmp[j - 1]]; j--)
FLINT_SWAP(slong, tmp[j], tmp[j - 1]);
m = 1;
while (m < n && deg[tmp[n - (m + 1)]] > 1)
m++;
for (i = 0; i < n; i++)
perm[i] = (i < m) ? tmp[n - m + i] : tmp[i - m];
for (i = 0; i < n; i++)
tmp[i] = deg[perm[i]] - 1;
for (i = 0; i < n; i++)
deg[i] = tmp[i];
for (k = 0; k < l; k++)
{
slong * Sk = S + k*n;
for (i = 0; i < n; i++)
tmp[i] = Sk[perm[i]];
for (i = 0; i < n; i++)
Sk[i] = tmp[i];
}
for (k = 0; k < n; k++)
{
slong * Vk = V + k*n;
for (i = 0; i < n; i++)
tmp[i] = Vk[perm[i]];
for (i = 0; i < n; i++)
Vk[i] = tmp[i];
}
flint_free(minp);
flint_free(maxp);
flint_free(minn);
flint_free(maxn);
flint_free(perm);
fmpz_clear(this_prod);
fmpz_clear(best_prod);
return m;
}
void mpoly_compression_init(mpoly_compression_t M)
{
M->mvars = 0;
M->nvars = 0;
M->exps = NULL;
M->exps_alloc = 0;
M->rest = NULL;
M->rest_alloc = 0;
M->umat = NULL;
M->deltas = NULL;
M->degs = NULL;
M->is_trivial = 0;
M->is_perm = 0;
M->is_irred = 0;
}
void mpoly_compression_clear(mpoly_compression_t M)
{
flint_free(M->exps);
flint_free(M->rest);
}
void mpoly_compression_set(
mpoly_compression_t M,
const ulong * Aexps,
flint_bitcnt_t Abits,
slong Alen,
const mpoly_ctx_t mctx)
{
slong i, j, one_total;
slong N = mpoly_words_per_exp_sp(Abits, mctx);
slong nvars = mctx->nvars;
ulong * Mexps;
int overflowed;
slong sum_deg, tries;
flint_rand_t state;
M->nvars = nvars;
_slong_array_fit_length(&M->rest, &M->rest_alloc, (nvars + 2)*nvars);
M->umat = M->rest;
M->deltas = M->umat + nvars*nvars;
M->degs = M->deltas + nvars;
_slong_array_fit_length(&M->exps, &M->exps_alloc, Alen*nvars);
Mexps = (ulong *) M->exps;
for (i = 0; i < Alen; i++)
mpoly_get_monomial_ui_sp(Mexps + nvars*i, Aexps + N*i, Abits, mctx);
M->mvars = _mpoly_compress_exps(M->umat, M->deltas, M->degs, M->exps, nvars, Alen);
FLINT_ASSERT(M->mvars > 0);
M->is_trivial = (M->mvars == nvars) && (mctx->ord == ORD_LEX);
M->is_perm = 1;
one_total = 0;
for (i = 0; i < nvars; i++)
for (j = 0; j < nvars; j++)
{
if (M->umat[i*nvars + j] == 1)
{
one_total++;
if (i != j)
M->is_trivial = 0;
}
else if (M->umat[i*nvars + j] == 0)
{
if (i == j)
M->is_trivial = 0;
}
else
{
M->is_trivial = 0;
M->is_perm = 0;
}
}
if (one_total != M->nvars)
M->is_perm = 0;
flint_rand_init(state);
sum_deg = 1;
overflowed = 0;
for (j = 0; j < M->mvars; j++)
{
if (z_add_checked(&sum_deg, sum_deg, M->degs[j]))
{
overflowed = 1;
break;
}
}
tries = 12;
if (!overflowed)
tries -= Alen/sum_deg/2;
M->is_irred = _mpoly_test_irreducible(M->exps, nvars, Alen,
M->mvars, state, tries);
flint_rand_clear(state);
}