#include <stdlib.h>
#include "ulong_extras.h"
#include "long_extras.h"
#include "fmpz.h"
#include "mpoly.h"
typedef struct {
slong x;
slong y;
} point2d;
static int _point2d_cmp(slong x1, slong y1, slong x2, slong y2)
{
if (x1 < x2)
return -1;
if (x1 > x2)
return 1;
if (y1 < y2)
return -1;
if (y1 > y2)
return 1;
return 0;
}
static int point2d_cmp(const point2d * p, const point2d * q)
{
return _point2d_cmp(p->x, p->y, q->x, q->y);
}
static int _is_ccw(point2d O, point2d A, point2d B)
{
return z_mat22_det_is_negative(A.y - O.y, A.x - O.x,
B.y - O.y, B.x - O.x);
}
typedef struct {
point2d * points;
slong length;
slong alloc;
} point2d_set_struct;
typedef point2d_set_struct point2d_set_t[1];
#if FLINT_WANT_ASSERT
static int point2d_set_is_canonical(const point2d_set_t A)
{
slong i;
for (i = 1; i < A->length; i++)
{
if (point2d_cmp(A->points + i, A->points + i - 1) <= 0)
return 0;
}
return 1;
}
#endif
static void point2d_set_init(point2d_set_t A)
{
A->points = NULL;
A->length = 0;
A->alloc = 0;
}
static void point2d_set_clear(point2d_set_t A)
{
if (A->alloc > 0)
flint_free(A->points);
}
static void point2d_set_swap(point2d_set_t A, point2d_set_t B)
{
point2d_set_struct t = *B;
*B = *A;
*A = t;
}
static void point2d_set_fit_length(point2d_set_t A, slong len)
{
if (len > A->alloc)
{
len = FLINT_MAX(len, 2*A->alloc);
A->points = (point2d *) flint_realloc(A->points, len*sizeof(point2d));
A->alloc = len;
}
}
static void point2d_set_sort(point2d_set_t A)
{
qsort(A->points, A->length, sizeof(point2d),
(int(*)(const void*,const void*))point2d_cmp);
}
static slong convex_hull_ccw(slong * idxs, const point2d * P, slong nP)
{
slong i, j, k = 0;
if (nP < 3)
{
for (i = 0; i < nP; i++)
idxs[i] = i;
return nP;
}
for (i = 0; i < nP; i++)
{
while (k >= 2 && !_is_ccw(P[idxs[k - 2]], P[idxs[k - 1]], P[i]))
k--;
idxs[k++] = i;
}
for (i = nP - 1, j = k + 1; i > 0; i--)
{
while (k >= j && !_is_ccw(P[idxs[k - 2]], P[idxs[k - 1]], P[i - 1]))
k--;
idxs[k++] = i - 1;
}
return k - 1;
}
static int _is_in_polygon(
point2d * V,
slong nV,
point2d p)
{
slong i, a, b, c;
#if FLINT_WANT_ASSERT
int check;
i = nV - 1;
check = !_is_ccw(V[0], V[i], p);
for (i = nV - 2; i >= 0; i--)
check &= !_is_ccw(V[i + 1], V[i], p);
#endif
again:
FLINT_ASSERT(nV >= 3);
if (nV < 8)
{
i = nV - 1;
if (_is_ccw(V[0], V[i], p))
{
FLINT_ASSERT(check == 0);
return 0;
}
for (i = nV - 2; i >= 0; i--)
{
if (_is_ccw(V[i + 1], V[i], p))
{
FLINT_ASSERT(check == 0);
return 0;
}
}
FLINT_ASSERT(check == 1);
return 1;
}
a = nV/4;
b = nV/2;
c = nV - nV/4;
if (_is_ccw(V[a], V[0], p))
{
V += 0;
nV = 1 + a;
goto again;
}
if (_is_ccw(V[b], V[a], p))
{
V += a;
nV = 1 + b - a;
goto again;
}
if (_is_ccw(V[c], V[b], p))
{
V += b;
nV = 1 + c - b;
goto again;
}
if (!_is_ccw(V[0], V[c], p))
{
FLINT_ASSERT(check == 1);
return 1;
}
if (!_is_ccw(V[nV - 1], V[c], p))
{
FLINT_ASSERT(check == !_is_ccw(V[0], V[nV - 1], p));
return !_is_ccw(V[0], V[nV - 1], p);
}
V += c;
nV = nV - c;
if (nV >= 3)
goto again;
FLINT_ASSERT(nV == 2);
FLINT_ASSERT(check == 0);
return 0;
}
static void point2d_set_merge_shift(
point2d_set_t T,
const point2d_set_t A,
const point2d_set_t B,
slong sx, slong sy,
point2d * V, slong nV)
{
point2d * Apoints, * Bpoints, * Tpoints;
slong Alen = A->length;
slong Blen = B->length;
slong i, j, k;
FLINT_ASSERT(T != A);
FLINT_ASSERT(T != B);
point2d_set_fit_length(T, Alen + Blen);
Apoints = A->points;
Bpoints = B->points;
Tpoints = T->points;
i = j = k = 0;
while (i < Alen && j < Blen)
{
slong Bsx = Bpoints[j].x + sx;
slong Bsy = Bpoints[j].y + sy;
int cmp = _point2d_cmp(Apoints[i].x, Apoints[i].y, Bsx, Bsy);
if (cmp < 0)
{
Tpoints[k] = Apoints[i];
i++;
k++;
}
else if (cmp == 0)
{
Tpoints[k] = Apoints[i];
i++;
j++;
k++;
}
else
{
Tpoints[k].x = Bsx;
Tpoints[k].y = Bsy;
j++;
k += _is_in_polygon(V, nV, Tpoints[k]);
}
}
while (i < Alen)
{
Tpoints[k] = Apoints[i];
i++;
k++;
}
while (j < Blen)
{
Tpoints[k].x = Bpoints[j].x + sx;
Tpoints[k].y = Bpoints[j].y + sy;
j++;
k += _is_in_polygon(V, nV, Tpoints[k]);
}
T->length = k;
FLINT_ASSERT(point2d_set_is_canonical(T));
}
static void point2d_set_merge(
point2d_set_t T,
const point2d_set_t A,
const point2d_set_t B)
{
point2d * Apoints, * Bpoints, * Tpoints;
slong Alen = A->length;
slong Blen = B->length;
slong i, j, k;
FLINT_ASSERT(T != A);
FLINT_ASSERT(T != B);
point2d_set_fit_length(T, Alen + Blen);
Apoints = A->points;
Bpoints = B->points;
Tpoints = T->points;
i = j = k = 0;
while (i < Alen && j < Blen)
{
int cmp = _point2d_cmp(Apoints[i].x, Apoints[i].y,
Bpoints[j].x, Bpoints[j].y);
if (cmp < 0)
{
Tpoints[k] = Apoints[i];
i++;
k++;
}
else if (cmp == 0)
{
Tpoints[k] = Apoints[i];
i++;
j++;
k++;
}
else
{
Tpoints[k] = Bpoints[j];
j++;
k += 1;
}
}
while (i < Alen)
{
Tpoints[k] = Apoints[i];
i++;
k++;
}
while (j < Blen)
{
Tpoints[k] = Bpoints[j];
j++;
k += 1;
}
T->length = k;
FLINT_ASSERT(point2d_set_is_canonical(T));
}
#if FLINT_WANT_ASSERT
static int point2d_set_contains(const point2d_set_t A, slong x, slong y)
{
slong lo = 0;
slong mid;
slong hi = A->length;
const point2d * Apoints = A->points;
int cmp;
again:
if (hi - lo < 8)
{
for ( ; lo < hi; lo++)
{
if (Apoints[lo].x == x && Apoints[lo].y == y)
return 1;
}
return 0;
}
mid = lo + (hi - lo)/2;
cmp = _point2d_cmp(Apoints[mid].x, Apoints[mid].y, x, y);
if (cmp == 0)
return 1;
if (cmp < 0)
lo = mid;
else
hi = mid;
goto again;
}
#endif
static int point2d_set_disjoint(
const point2d_set_t A,
const point2d_set_t B)
{
point2d * Apoints = A->points;
point2d * Bpoints = B->points;
slong Alen = A->length;
slong Blen = B->length;
slong lo, mid, hi;
int cmp;
#if FLINT_WANT_ASSERT
int check = 1;
for (lo = 0; lo < Blen; lo++)
check &= !point2d_set_contains(A, Bpoints[lo].x, Bpoints[lo].y);
#endif
again:
if (Alen < 1 || Blen < 1)
{
FLINT_ASSERT(check == 1);
return 1;
}
if (Alen < Blen)
{
FLINT_SWAP(slong, Alen, Blen);
{
point2d * T = Apoints;
Apoints = Bpoints;
Bpoints = T;
}
}
cmp = point2d_cmp(&Bpoints[0], &Apoints[0]);
if (cmp == 0)
{
FLINT_ASSERT(check == 0);
return 0;
}
if (cmp < 0)
{
Bpoints += 1;
Blen -= 1;
goto again;
}
lo = 0;
hi = Alen - 1;
cmp = point2d_cmp(&Bpoints[0], &Apoints[hi]);
if (cmp >= 0)
{
FLINT_ASSERT(cmp == check);
return cmp;
}
search:
FLINT_ASSERT(point2d_cmp(&Apoints[lo], &Bpoints[0]) < 0);
FLINT_ASSERT(point2d_cmp(&Bpoints[0], &Apoints[hi]) < 0);
if (hi - lo < 8)
{
for (lo++ ; lo < hi; lo++)
{
cmp = point2d_cmp(&Bpoints[0], &Apoints[lo]);
if (cmp == 0)
{
FLINT_ASSERT(check == 0);
return 0;
}
if (cmp < 0)
break;
}
Apoints += lo;
Alen -= lo;
Bpoints += 1;
Blen -= 1;
goto again;
}
mid = lo + (hi - lo)/2;
cmp = point2d_cmp(&Apoints[mid], &Bpoints[0]);
if (cmp == 0)
{
FLINT_ASSERT(check == 0);
return 0;
}
if (cmp < 0)
lo = mid;
else
hi = mid;
goto search;
}
static int convex_hull_is_indecomposable(
point2d * V,
slong nV,
ulong bound,
point2d_set_t Ai,
point2d_set_t Aim1,
point2d_set_t T,
point2d_set_t S,
point2d * E,
slong * Egcd)
{
slong i, j, k, g, prevx, prevy;
ulong prod_hi, prod_lo;
FLINT_ASSERT(nV >= 3);
if (nV == 3)
{
ulong g = FLINT_ABS(V[2].x - V[0].x);
g = n_gcd(g, FLINT_ABS(V[2].y - V[0].y));
g = n_gcd(g, FLINT_ABS(V[1].x - V[0].x));
g = n_gcd(g, FLINT_ABS(V[1].y - V[0].y));
return g == 1;
}
prevx = V[0].x;
prevy = V[0].y;
g = 0;
prod_hi = 0; prod_lo = 1;
for (i = nV - 1; i >= 0; i--)
{
E[i].x = prevx - V[i].x;
E[i].y = prevy - V[i].y;
prevx = V[i].x;
prevy = V[i].y;
Egcd[i] = n_gcd(FLINT_ABS(E[i].x), FLINT_ABS(E[i].y));
E[i].x /= Egcd[i];
E[i].y /= Egcd[i];
g = n_gcd(g, Egcd[i]);
umul_ppmm(prod_hi, prod_lo, prod_lo, Egcd[i]);
if (prod_hi != 0)
return -1;
}
if (g > 1)
return 0;
if (prod_lo > bound)
return -1;
point2d_set_fit_length(S, Egcd[nV - 1]);
for (j = 0; j < Egcd[nV - 1]; j++)
{
S->points[j].x = V[0].x - j*E[nV - 1].x;
S->points[j].y = V[0].y - j*E[nV - 1].y;
}
S->length = Egcd[nV - 1];
point2d_set_sort(S);
Aim1->length = 0;
for (i = 0; i < nV - 1; i++)
{
point2d_set_fit_length(Ai, Egcd[i]);
k = 0;
for (j = 1; j <= Egcd[i]; j++)
{
Ai->points[k].x = V[0].x + j*E[i].x;
Ai->points[k].y = V[0].y + j*E[i].y;
if (!_is_in_polygon(V, nV, Ai->points[k]))
break;
k++;
}
Ai->length = k;
point2d_set_sort(Ai);
if (Aim1->length > 0)
{
point2d_set_merge(T, Ai, Aim1);
point2d_set_swap(Ai, T);
for (j = 1; j <= Egcd[i]; j++)
{
point2d_set_merge_shift(T, Ai, Aim1, j*E[i].x, j*E[i].y, V, nV);
point2d_set_swap(Ai, T);
if (!point2d_set_disjoint(Ai, S))
return 0;
}
}
else
{
if (!point2d_set_disjoint(Ai, S))
return 0;
}
point2d_set_swap(Aim1, Ai);
}
return 1;
}
static void z_rand_vec_primitive(
slong * v, slong len,
flint_rand_t state,
ulong bound)
{
slong i, g;
again:
g = 0;
for (i = 0; i < len; i++)
{
v[i] = z_randint(state, bound);
g = n_gcd(g, FLINT_ABS(v[i]));
}
if (g == 0)
goto again;
if (g == 1)
return;
for (i = 0; i < len; i++)
v[i] /= g;
}
static int _test_indecomposable2(
slong * a,
slong * b,
slong n)
{
slong i;
ulong g = 0;
for (i = 0; i < n; i++)
g = n_gcd(g, FLINT_ABS(a[i] - b[i]));
return g == 1;
}
static int _test_colinear(
slong * a,
slong * b,
slong * c,
slong n)
{
slong i;
int result;
fmpz_t tn, td, sn, sd, g;
fmpz_init(tn);
fmpz_init(td);
fmpz_init(sn);
fmpz_init(sd);
fmpz_init(g);
for (i = 0; i < n; i++)
{
fmpz_set_si(sn, a[i]);
fmpz_sub_si(sn, sn, c[i]);
fmpz_set_si(sd, a[i]);
fmpz_sub_si(sd, sd, b[i]);
fmpz_gcd(g, sn, sd);
if (fmpz_is_zero(g))
continue;
if (fmpz_sgn(sd) < 0)
fmpz_neg(g, g);
fmpz_divexact(sn, sn, g);
fmpz_divexact(sd, sd, g);
if (fmpz_is_zero(td))
{
fmpz_swap(td, sd);
fmpz_swap(tn, sn);
}
else if (!fmpz_equal(sd, td) || !fmpz_equal(sn, tn))
{
result = 4;
goto cleanup;
}
}
if (fmpz_is_zero(td))
result = 0;
else if (fmpz_sgn(tn) < 0)
result = 1;
else if (fmpz_cmp(tn, td) > 0)
result = 2;
else
result = 3;
cleanup:
fmpz_clear(tn);
fmpz_clear(td);
fmpz_clear(sn);
fmpz_clear(sd);
fmpz_clear(g);
return result;
}
static int _test_indecomposable3(
slong * a,
slong * b,
slong * c,
slong n)
{
slong i;
ulong g;
switch (_test_colinear(a, b, c, n))
{
case 0:
return 0;
case 1:
return _test_indecomposable2(c, b, n);
case 2:
return _test_indecomposable2(a, c, n);
case 3:
return _test_indecomposable2(a, b, n);
default:
break;
}
g = 0;
for (i = 0; i < n; i++)
{
g = n_gcd(g, FLINT_ABS(a[i] - b[i]));
g = n_gcd(g, FLINT_ABS(a[i] - c[i]));
}
return g == 1;
}
int _mpoly_test_irreducible(
slong * Aexps, slong stride, slong Alen,
slong nvars,
flint_rand_t state,
slong tries_left)
{
int success;
slong i, j, newlen, hull_len;
ulong matrix_bound = 2;
ulong memory_bound = 1000;
ulong max_memory_bound = UWORD(1) << (20 + FLINT_BITS/8);
slong * rowx;
slong * rowy;
point2d * points;
slong * dups;
slong * hull_idxs;
slong hull_idxs_alloc;
point2d * hull_points;
slong hull_points_alloc;
point2d_set_t T1, T2, T3, T4;
if (Alen < 2 || nvars < 2)
return 0;
if (Alen == 2)
return _test_indecomposable2(Aexps + 0*stride, Aexps + 1*stride, nvars);
if (Alen == 3)
return _test_indecomposable3(Aexps + 0*stride, Aexps + 1*stride,
Aexps + 2*stride, nvars);
if (tries_left <= 0)
return 0;
rowx = FLINT_ARRAY_ALLOC(2*nvars, slong);
rowy = rowx + nvars;
points = FLINT_ARRAY_ALLOC(Alen, point2d);
dups = FLINT_ARRAY_ALLOC(Alen, slong);
hull_idxs_alloc = 10;
hull_idxs = FLINT_ARRAY_ALLOC(hull_idxs_alloc, slong);
hull_points_alloc = 4;
hull_points = FLINT_ARRAY_ALLOC(hull_points_alloc, point2d);
point2d_set_init(T1);
point2d_set_init(T2);
point2d_set_init(T3);
point2d_set_init(T4);
again:
if (--tries_left < 0)
{
success = 0;
goto cleanup;
}
memory_bound = FLINT_MIN(max_memory_bound, memory_bound/8*9);
matrix_bound += 1;
if (nvars == 2)
{
tries_left = 0;
memory_bound = max_memory_bound;
for (i = 0; i < Alen; i++)
{
slong lox = Aexps[i*stride + 0];
slong loy = Aexps[i*stride + 1];
if (FLINT_MIN(lox, loy) <= -(WORD(1) << (FLINT_BITS - 3)) ||
FLINT_MAX(lox, loy) >= WORD(1) << (FLINT_BITS - 3))
{
success = 0;
goto cleanup;
}
points[i].x = lox;
points[i].y = loy;
}
}
else
{
z_rand_vec_primitive(rowx, nvars, state, matrix_bound);
z_rand_vec_primitive(rowy, nvars, state, matrix_bound);
for (i = 0; i < Alen; i++)
{
ulong x2, x1, x0, y2, y1, y0, p2, p1, p0;
x2 = x1 = x0 = y2 = y1 = y0 = 0;
for (j = 0; j < nvars; j++)
{
smul_ppmm(p1, p0, Aexps[i*stride + j], rowx[j]);
p2 = FLINT_SIGN_EXT(p1);
add_sssaaaaaa(x2, x1, x0, x2, x1, x0, p2, p1, p0);
smul_ppmm(p1, p0, Aexps[i*stride + j], rowy[j]);
p2 = FLINT_SIGN_EXT(p1);
add_sssaaaaaa(y2, y1, y0, y2, y1, y0, p2, p1, p0);
}
if (x2 != FLINT_SIGN_EXT(x0) || x1 != FLINT_SIGN_EXT(x0) ||
y2 != FLINT_SIGN_EXT(y0) || y1 != FLINT_SIGN_EXT(y0))
{
goto again;
}
points[i].x = x0;
points[i].y = y0;
if (points[i].x <= -(WORD(1) << (FLINT_BITS - 3)) ||
points[i].y <= -(WORD(1) << (FLINT_BITS - 3)) ||
points[i].x >= WORD(1) << (FLINT_BITS - 3) ||
points[i].y >= WORD(1) << (FLINT_BITS - 3))
{
goto again;
}
}
}
qsort(points, Alen, sizeof(point2d),
(int(*)(const void*,const void*))point2d_cmp);
dups[0] = 0;
newlen = 1;
for (i = 1; i < Alen; i++)
{
if (point2d_cmp(&points[newlen - 1], &points[i]) == 0)
{
dups[newlen - 1] = 1;
}
else
{
dups[newlen] = 0;
points[newlen] = points[i];
newlen++;
}
}
if (newlen + 1 > hull_idxs_alloc)
{
hull_idxs_alloc = newlen + 1;
hull_idxs = (slong *) flint_realloc(hull_idxs,
hull_idxs_alloc*sizeof(slong));
}
hull_len = convex_hull_ccw(hull_idxs, points, newlen);
if (hull_len < 3)
goto again;
if (hull_len + 1 > hull_points_alloc)
{
hull_points_alloc = hull_len + 1;
hull_points = (point2d *) flint_realloc(hull_points,
hull_points_alloc*sizeof(point2d));
}
for (i = 0; i < hull_len; i++)
{
hull_points[i] = points[hull_idxs[i]];
if (dups[hull_idxs[i]] != 0)
goto again;
}
success = convex_hull_is_indecomposable(hull_points, hull_len,
memory_bound, T1, T2, T3, T4, points, dups);
if (success < 1)
{
if (success < 0)
memory_bound = FLINT_MIN(max_memory_bound, memory_bound/8*9);
goto again;
}
success = 1;
cleanup:
point2d_set_clear(T1);
point2d_set_clear(T2);
point2d_set_clear(T3);
point2d_set_clear(T4);
flint_free(rowx);
flint_free(points);
flint_free(dups);
flint_free(hull_idxs);
flint_free(hull_points);
return success;
}
int mpoly_test_irreducible(
ulong * Aexps, flint_bitcnt_t Abits, slong Alen,
const mpoly_ctx_t ctx)
{
int result, overflowed;
slong n = ctx->nvars;
slong i, j, N, sum_deg, tries;
slong * uexps, * max_exps;
flint_rand_t state;
if (Abits > FLINT_BITS || Alen < 2)
return 0;
flint_rand_init(state);
uexps = FLINT_ARRAY_ALLOC(n*Alen, slong);
max_exps = FLINT_ARRAY_ALLOC(n, slong);
for (j = 0; j < n; j++)
max_exps[j] = 0;
N = mpoly_words_per_exp(Abits, ctx);
for (i = 0; i < Alen; i++)
{
mpoly_get_monomial_ui_sp((ulong *)uexps + n*i, Aexps + N*i, Abits, ctx);
for (j = 0; j < n; j++)
max_exps[j] = FLINT_MAX(max_exps[j], uexps[n*i + j]);
}
sum_deg = 1;
overflowed = 0;
for (j = 0; j < n; j++)
{
if (z_add_checked(&sum_deg, sum_deg, max_exps[j]))
{
overflowed = 1;
break;
}
}
tries = 12;
if (!overflowed)
tries -= Alen/sum_deg/2;
result = _mpoly_test_irreducible(uexps, n, Alen, n, state, tries);
flint_rand_clear(state);
flint_free(max_exps);
flint_free(uexps);
return result;
}