#include <math.h>
#include "fmpz.h"
#include "fmpz_mat.h"
#include "fmpz_poly.h"
#include "fmpz_poly_factor.h"
slong _fmpz_poly_factor_CLD_mat(fmpz_mat_t res, const fmpz_poly_t f,
fmpz_poly_factor_t lifted_fac, fmpz_t P, ulong k)
{
ulong i, lo_n, hi_n, bound;
slong zeroes, r = lifted_fac->num;
slong bit_r = FLINT_MAX(r, 20);
fmpz_poly_t gd, gcld, temp;
fmpz_poly_t trunc_f, trunc_fac;
fmpz_t t;
for (i = 0; i < k; i++)
{
fmpz_poly_CLD_bound(fmpz_mat_entry(res, r, i), f, i);
fmpz_poly_CLD_bound(fmpz_mat_entry(res, r, 2*k - i - 1), f, f->length - i - 2);
}
fmpz_init(t);
bound = fmpz_bits(P) - bit_r - bit_r/2;
for (lo_n = 0; lo_n < k; lo_n++)
{
fmpz_mul_ui(t, fmpz_mat_entry(res, r, lo_n), (slong) sqrt(f->length));
if (fmpz_bits(t) > bound)
break;
}
fmpz_zero(t);
for (hi_n = 0; hi_n < k; hi_n++)
{
fmpz_mul_ui(t, fmpz_mat_entry(res, r, 2*k - hi_n - 1), (slong) sqrt(f->length));
if (fmpz_bits(t) > bound)
break;
}
fmpz_clear(t);
fmpz_poly_init(gd);
fmpz_poly_init(gcld);
if (lo_n > 0)
{
for (i = 0; (slong) i < r; i++)
{
zeroes = 0;
while (fmpz_is_zero(lifted_fac->p[i].coeffs + zeroes))
zeroes++;
fmpz_poly_attach_truncate(trunc_fac, lifted_fac->p + i, lo_n + zeroes + 1);
fmpz_poly_derivative(gd, trunc_fac);
fmpz_poly_mullow(gcld, f, gd, lo_n + zeroes);
fmpz_poly_divlow_smodp(fmpz_mat_entry(res, i, 0), gcld, trunc_fac, P, lo_n);
}
}
if (hi_n > 0)
{
fmpz_poly_init(temp);
fmpz_poly_attach_shift(trunc_f, f, f->length - hi_n);
for (i = 0; (slong) i < r; i++)
{
slong len = lifted_fac->p[i].length - hi_n - 1;
if (len < 0)
{
fmpz_poly_shift_left(temp, lifted_fac->p + i, -len);
fmpz_poly_derivative(gd, temp);
fmpz_poly_mulhigh_n(gcld, trunc_f, gd, hi_n);
fmpz_poly_divhigh_smodp(fmpz_mat_entry(res, i, lo_n), gcld, temp, P, hi_n);
} else
{
fmpz_poly_attach_shift(trunc_fac, lifted_fac->p + i, len);
fmpz_poly_derivative(gd, trunc_fac);
fmpz_poly_mulhigh_n(gcld, trunc_f, gd, hi_n);
fmpz_poly_divhigh_smodp(fmpz_mat_entry(res, i, lo_n), gcld, trunc_fac, P, hi_n);
}
}
fmpz_poly_clear(temp);
}
if (hi_n > 0)
{
for (i = 0; i < hi_n; i++)
fmpz_set(fmpz_mat_entry(res, r, lo_n + i), fmpz_mat_entry(res, r, 2*k - hi_n + i));
}
fmpz_poly_clear(gd);
fmpz_poly_clear(gcld);
return lo_n + hi_n;
}