#include "fmpz_mod.h"
#include "fmpz_mod_mpoly_factor.h"
int fmpz_mod_bpoly_hlift2(
fmpz_mod_bpoly_t A,
fmpz_mod_bpoly_t B0,
fmpz_mod_bpoly_t B1,
const fmpz_t alpha,
slong degree_inner,
const fmpz_mod_ctx_t ctx,
fmpz_mod_poly_bpoly_stack_t St)
{
int success;
slong i, j;
fmpz_t malpha;
fmpz_mod_poly_struct * c, * s, * t, * u, * v;
FLINT_ASSERT(fmpz_mod_bpoly_is_canonical(A, ctx));
FLINT_ASSERT(fmpz_mod_bpoly_is_canonical(B0, ctx));
FLINT_ASSERT(fmpz_mod_bpoly_is_canonical(B1, ctx));
if (A->length < 1 || B0->length < 1 || B1->length < 1)
return -1;
fmpz_init(malpha);
fmpz_mod_poly_stack_fit_request(St->poly_stack, 5);
c = fmpz_mod_poly_stack_take_top(St->poly_stack);
s = fmpz_mod_poly_stack_take_top(St->poly_stack);
t = fmpz_mod_poly_stack_take_top(St->poly_stack);
u = fmpz_mod_poly_stack_take_top(St->poly_stack);
v = fmpz_mod_poly_stack_take_top(St->poly_stack);
fmpz_mod_bpoly_taylor_shift_gen0(A, alpha, ctx);
fmpz_mod_bpoly_taylor_shift_gen0(B0, alpha, ctx);
fmpz_mod_bpoly_taylor_shift_gen0(B1, alpha, ctx);
FLINT_ASSERT(fmpz_mod_poly_degree(A->coeffs + 0, ctx) ==
fmpz_mod_poly_degree(B0->coeffs + 0, ctx) +
fmpz_mod_poly_degree(B1->coeffs + 0, ctx));
if (fmpz_mod_poly_degree(A->coeffs + 0, ctx) != degree_inner)
{
success = -1;
goto cleanup;
}
FLINT_ASSERT(fmpz_mod_bpoly_degree1(A, ctx) ==
fmpz_mod_poly_degree(A->coeffs + 0, ctx));
if (!fmpz_mod_poly_invmod(s, B1->coeffs + 0, B0->coeffs + 0, ctx))
{
success = -2;
goto cleanup;
}
fmpz_mod_bpoly_fit_length(B0, A->length, ctx);
fmpz_mod_bpoly_fit_length(B1, A->length, ctx);
for (j = 1; j < A->length; j++)
{
fmpz_mod_poly_set(c, A->coeffs + j, ctx);
for (i = 0; i <= j; i++)
{
if (i < B0->length && j - i < B1->length)
{
fmpz_mod_poly_mul(t, B0->coeffs + i, B1->coeffs + j - i, ctx);
fmpz_mod_poly_sub(c, c, t, ctx);
}
}
if (fmpz_mod_poly_is_zero(c, ctx))
continue;
fmpz_mod_poly_mul(t, s, c, ctx);
fmpz_mod_poly_rem(u, t, B0->coeffs + 0, ctx);
fmpz_mod_poly_mul(t, u, B1->coeffs + 0, ctx);
fmpz_mod_poly_sub(c, c, t, ctx);
fmpz_mod_poly_div(v, c, B0->coeffs + 0, ctx);
if (j < B0->length)
fmpz_mod_poly_add(B0->coeffs + j, B0->coeffs + j, u, ctx);
else
fmpz_mod_poly_set(B0->coeffs + j, u, ctx);
if (j < B1->length)
fmpz_mod_poly_add(B1->coeffs + j, B1->coeffs + j, v, ctx);
else
fmpz_mod_poly_set(B1->coeffs + j, v, ctx);
if (!fmpz_mod_poly_is_zero(B0->coeffs + j, ctx))
B0->length = FLINT_MAX(B0->length, j + 1);
if (!fmpz_mod_poly_is_zero(B1->coeffs + j, ctx))
B1->length = FLINT_MAX(B1->length, j + 1);
if (B0->length - 1 + B1->length - 1 > A->length - 1)
{
success = 0;
goto cleanup;
}
}
fmpz_mod_neg(malpha, alpha, ctx);
fmpz_mod_bpoly_taylor_shift_gen0(B0, malpha, ctx);
fmpz_mod_bpoly_taylor_shift_gen0(B1, malpha, ctx);
success = 1;
cleanup:
#if FLINT_WANT_ASSERT
if (success > 0)
{
fmpz_mod_bpoly_t tp1, tp2;
fmpz_mod_bpoly_init(tp1, ctx);
fmpz_mod_bpoly_init(tp2, ctx);
fmpz_mod_neg(malpha, alpha, ctx);
fmpz_mod_bpoly_taylor_shift_gen0(A, malpha, ctx);
fmpz_mod_bpoly_mul(tp1, B0, B1, ctx);
FLINT_ASSERT(fmpz_mod_bpoly_equal(tp1, A, ctx));
fmpz_mod_bpoly_clear(tp1, ctx);
fmpz_mod_bpoly_clear(tp2, ctx);
}
#endif
fmpz_clear(malpha);
fmpz_mod_poly_stack_give_back(St->poly_stack, 5);
return success;
}
int fmpz_mod_bpoly_hlift(
slong r,
fmpz_mod_bpoly_t A,
fmpz_mod_bpoly_struct * B,
const fmpz_t alpha,
slong degree_inner,
const fmpz_mod_ctx_t ctx,
fmpz_mod_poly_bpoly_stack_t St)
{
int success;
slong i, j, k, tdeg;
fmpz_t malpha;
fmpz_mod_poly_struct * c, * t, * u;
fmpz_mod_poly_struct ** s, ** v, ** Binv;
fmpz_mod_bpoly_struct ** U;
TMP_INIT;
FLINT_ASSERT(r >= 2);
if (r < 3)
return fmpz_mod_bpoly_hlift2(A, B + 0, B + 1, alpha, degree_inner, ctx, St);
FLINT_ASSERT(fmpz_mod_bpoly_is_canonical(A, ctx));
if (A->length < 1)
return -1;
for (i = 0; i < r; i++)
{
FLINT_ASSERT(fmpz_mod_bpoly_is_canonical(B + i, ctx));
if (B[i].length < 1)
return -1;
}
TMP_START;
fmpz_mod_bpoly_stack_fit_request(St->bpoly_stack, r);
U = TMP_ARRAY_ALLOC(r, fmpz_mod_bpoly_struct *);
for (i = 0; i < r; i++)
{
U[i] = fmpz_mod_bpoly_stack_take_top(St->bpoly_stack);
fmpz_mod_bpoly_fit_length(U[i], A->length, ctx);
for (j = 0; j < A->length; j++)
fmpz_mod_poly_zero(U[i]->coeffs + j, ctx);
U[i]->length = A->length;
fmpz_mod_bpoly_fit_length(B + i, A->length, ctx);
}
fmpz_mod_poly_stack_fit_request(St->poly_stack, 3*r + 3);
s = TMP_ARRAY_ALLOC(3*r, fmpz_mod_poly_struct *);
v = s + r;
Binv = v + r;
for (i = 0; i < r; i++)
{
s[i] = fmpz_mod_poly_stack_take_top(St->poly_stack);
v[i] = fmpz_mod_poly_stack_take_top(St->poly_stack);
Binv[i] = fmpz_mod_poly_stack_take_top(St->poly_stack);
}
fmpz_init(malpha);
c = fmpz_mod_poly_stack_take_top(St->poly_stack);
t = fmpz_mod_poly_stack_take_top(St->poly_stack);
u = fmpz_mod_poly_stack_take_top(St->poly_stack);
fmpz_mod_bpoly_taylor_shift_gen0(A, alpha, ctx);
for (i = 0; i < r; i++)
fmpz_mod_bpoly_taylor_shift_gen0(B + i, alpha, ctx);
j = 0;
for (i = 0; i < r; i++)
j += fmpz_mod_poly_degree(B[i].coeffs + 0, ctx);
FLINT_ASSERT(j == fmpz_mod_poly_degree(A->coeffs + 0, ctx));
if (fmpz_mod_poly_degree(A->coeffs + 0, ctx) != degree_inner)
{
success = -1;
goto cleanup;
}
FLINT_ASSERT(fmpz_mod_bpoly_degree1(A, ctx) ==
fmpz_mod_poly_degree(A->coeffs + 0, ctx));
for (i = 0; i < r; i++)
{
fmpz_mod_poly_one(t, ctx);
for (j = 0; j < r; j++)
{
if (j != i)
fmpz_mod_poly_mul(t, t, B[j].coeffs + 0, ctx);
}
if (!fmpz_mod_poly_invmod(s[i], t, B[i].coeffs + 0, ctx))
{
success = -1;
goto cleanup;
}
fmpz_mod_poly_reverse(t, B[i].coeffs + 0, B[i].coeffs[0].length, ctx);
fmpz_mod_poly_inv_series(Binv[i], t, B[i].coeffs[0].length, ctx);
}
k = r - 2;
fmpz_mod_poly_mul(U[k]->coeffs + 0, B[k].coeffs + 0, B[k + 1].coeffs + 0, ctx);
for (k--; k > 0; k--)
fmpz_mod_poly_mul(U[k]->coeffs + 0, B[k].coeffs + 0, U[k + 1]->coeffs + 0, ctx);
for (j = 1; j < A->length; j++)
{
for (k = 0; k < r; k++)
fmpz_mod_poly_zero(U[k]->coeffs + j, ctx);
k = r - 2;
fmpz_mod_poly_zero(U[k]->coeffs + j, ctx);
for (i = 0; i <= j; i++)
{
if (i < B[k].length && j - i < B[k + 1].length)
{
fmpz_mod_poly_mul(t, B[k].coeffs + i, B[k + 1].coeffs + j - i, ctx);
fmpz_mod_poly_add(U[k]->coeffs + j, U[k]->coeffs + j, t, ctx);
}
}
for (k--; k > 0; k--)
{
fmpz_mod_poly_zero(U[k]->coeffs + j, ctx);
for (i = 0; i <= j; i++)
{
if (i < B[k].length)
{
fmpz_mod_poly_mul(t, B[k].coeffs + i, U[k + 1]->coeffs + j - i, ctx);
fmpz_mod_poly_add(U[k]->coeffs + j, U[k]->coeffs + j, t, ctx);
}
}
}
fmpz_mod_poly_set(c, A->coeffs + j, ctx);
for (i = 0; i <= j; i++)
{
if (i < B[0].length)
{
fmpz_mod_poly_mul(t, B[0].coeffs + i, U[1]->coeffs + j - i, ctx);
fmpz_mod_poly_sub(c, c, t, ctx);
}
}
if (fmpz_mod_poly_is_zero(c, ctx))
continue;
tdeg = 0;
for (i = 0; i < r; i++)
{
fmpz_mod_poly_rem(t, c, B[i].coeffs + 0, ctx);
fmpz_mod_poly_mulmod_preinv(v[i], s[i], t, B[i].coeffs + 0, Binv[i], ctx);
while (j >= B[i].length)
{
fmpz_mod_poly_zero(B[i].coeffs + B[i].length, ctx);
B[i].length++;
}
fmpz_mod_poly_add(B[i].coeffs + j, B[i].coeffs + j, v[i], ctx);
fmpz_mod_bpoly_normalise(B + i, ctx);
tdeg += B[i].length - 1;
}
if (tdeg >= A->length)
{
success = 0;
goto cleanup;
}
k = r - 2;
fmpz_mod_poly_mul(t, B[k].coeffs + 0, v[k + 1], ctx);
fmpz_mod_poly_mul(u, B[k + 1].coeffs + 0, v[k], ctx);
fmpz_mod_poly_add(t, t, u, ctx);
fmpz_mod_poly_add(U[k]->coeffs + j, U[k]->coeffs + j, t, ctx);
for (k--; k > 0; k--)
{
fmpz_mod_poly_mul(u, B[k].coeffs + 0, t, ctx);
fmpz_mod_poly_mul(t, U[k + 1]->coeffs + 0, v[k], ctx);
fmpz_mod_poly_add(t, t, u, ctx);
fmpz_mod_poly_add(U[k]->coeffs + j, U[k]->coeffs + j, t, ctx);
}
}
fmpz_mod_neg(malpha, alpha, ctx);
for (i = 0; i < r; i++)
fmpz_mod_bpoly_taylor_shift_gen0(B + i, malpha, ctx);
success = 1;
cleanup:
#if FLINT_WANT_ASSERT
if (success > 0)
{
fmpz_mod_bpoly_t tp1, tp2;
fmpz_mod_bpoly_init(tp1, ctx);
fmpz_mod_bpoly_init(tp2, ctx);
fmpz_mod_neg(malpha, alpha, ctx);
fmpz_mod_bpoly_taylor_shift_gen0(A, malpha, ctx);
fmpz_mod_bpoly_mul(tp1, B + 0, B + 1, ctx);
for (i = 2; i < r; i++)
{
fmpz_mod_bpoly_mul(tp2, tp1, B + i, ctx);
fmpz_mod_bpoly_swap(tp1, tp2, ctx);
}
FLINT_ASSERT(fmpz_mod_bpoly_equal(tp1, A, ctx));
fmpz_mod_bpoly_clear(tp1, ctx);
fmpz_mod_bpoly_clear(tp2, ctx);
}
#endif
fmpz_clear(malpha);
fmpz_mod_bpoly_stack_give_back(St->bpoly_stack, r);
fmpz_mod_poly_stack_give_back(St->poly_stack, 3*r + 3);
TMP_END;
return success;
}