#include "gr_vec.h"
#include "gr_poly.h"
#include "gr_poly/impl.h"
static int
_gr_poly_div_divconquer_recursive(gr_ptr Q, gr_ptr W,
gr_srcptr A, gr_srcptr B, slong lenB, gr_srcptr invB, slong cutoff, gr_ctx_t ctx)
{
slong sz = ctx->sizeof_elem;
int status = GR_SUCCESS;
if (lenB < FLINT_MAX(2, cutoff))
{
if (invB == NULL)
return _gr_poly_div_basecase_noinv(Q, A, 2 * lenB - 1, B, lenB, ctx);
else
return _gr_poly_div_basecase_preinv1(Q, A, 2 * lenB - 1, B, lenB, invB, ctx);
}
else
{
const slong n2 = lenB / 2;
const slong n1 = lenB - n2;
gr_ptr W1 = W;
gr_ptr W2 = GR_ENTRY(W, lenB, sz);
gr_srcptr p1 = GR_ENTRY(A, 2 * n2, sz);
gr_srcptr p2;
gr_srcptr d1 = GR_ENTRY(B, n2, sz);
gr_srcptr d2 = B;
gr_srcptr d3 = GR_ENTRY(B, n1, sz);
gr_ptr q1 = GR_ENTRY(Q, n2, sz);
gr_ptr q2 = Q;
gr_ptr d1q1 = W2;
gr_ptr d2q1, t;
status |= _gr_poly_divrem_divconquer_recursive(q1, d1q1, W1, p1, d1, n1, invB, cutoff, ctx);
d2q1 = W1;
status |= _gr_poly_mullow(d2q1, q1, n1, d2, n2, n1 + n2 - 1, ctx);
if (n2 > n1 - 1)
status |= gr_set(W1, GR_ENTRY(d2q1, n1 - 1, sz), ctx);
status |= _gr_poly_add(GR_ENTRY(W1, n2 - (n1 - 1), sz), d1q1, n1 - 1, GR_ENTRY(d2q1, n2, sz), n1 - 1, ctx);
t = W1;
status |= _gr_poly_sub(t, GR_ENTRY(A, n2 + (n1 - 1), sz), n2, t, n2, ctx);
p2 = GR_ENTRY(t, - (n2 - 1), sz);
status |= _gr_poly_div_divconquer_recursive(q2, W2, p2, d3, n2, invB, cutoff, ctx);
return status;
}
}
static int
__gr_poly_div_divconquer(gr_ptr Q, gr_srcptr A, slong lenA,
gr_srcptr B, slong lenB, gr_srcptr invB, slong cutoff, gr_ctx_t ctx)
{
slong sz = ctx->sizeof_elem;
int status = GR_SUCCESS;
if (lenA < 2 * lenB - 1)
{
const slong n1 = lenA - lenB + 1;
const slong n2 = lenB - n1;
gr_srcptr p1 = GR_ENTRY(A, n2, sz);
gr_srcptr d1 = GR_ENTRY(B, n2, sz);
gr_ptr W;
GR_TMP_INIT_VEC(W, 2 * lenB, ctx);
status |= _gr_poly_div_divconquer_recursive(Q, W, p1, d1, n1, invB, cutoff, ctx);
GR_TMP_CLEAR_VEC(W, 2 * lenB, ctx);
}
else
{
gr_ptr W;
GR_TMP_INIT_VEC(W, 2 * lenB, ctx);
status |= _gr_poly_div_divconquer_recursive(Q, W, A, B, lenB, invB, cutoff, ctx);
GR_TMP_CLEAR_VEC(W, 2 * lenB, ctx);
}
return status;
}
static int
_gr_vec_sub_dec(gr_ptr a, gr_srcptr b, gr_srcptr c, slong n, gr_ctx_t ctx)
{
slong sz = ctx->sizeof_elem;
int status = GR_SUCCESS;
slong i;
for (i = n - 1; i >= 0; i--)
status |= gr_sub(GR_ENTRY(a, i, sz), GR_ENTRY(b, i, sz), GR_ENTRY(c, i, sz), ctx);
return status;
}
int
_gr_poly_div_divconquer_preinv1(gr_ptr Q, gr_srcptr A, slong lenA,
gr_srcptr B, slong lenB, gr_srcptr invB, slong cutoff, gr_ctx_t ctx)
{
if (lenA <= 2 * lenB - 1)
{
return __gr_poly_div_divconquer(Q, A, lenA, B, lenB, invB, cutoff, ctx);
}
else
{
gr_ptr S, T, R;
slong shift, next, n = 2 * lenB - 1;
slong sz = ctx->sizeof_elem;
int status = GR_SUCCESS;
GR_TMP_INIT_VEC(S, 3 * n, ctx);
T = GR_ENTRY(S, n, sz);
R = GR_ENTRY(T, n, sz);
shift = lenA - n;
status |= _gr_vec_set(S, GR_ENTRY(A, shift, sz), n, ctx);
while (lenA >= n)
{
shift = lenA - n;
status |= _gr_poly_divrem_divconquer_recursive(GR_ENTRY(Q, shift, sz), T, R, S, B, lenB, invB, cutoff, ctx);
next = FLINT_MIN(lenB, shift);
status |= _gr_vec_sub_dec(GR_ENTRY(S, next, sz), S, T, lenB - 1, ctx);
status |= _gr_vec_set(S, GR_ENTRY(A, shift - next, sz), next, ctx);
lenA -= lenB;
}
if (lenA >= lenB)
status |= __gr_poly_div_divconquer(Q, S, lenA, B, lenB, invB, cutoff, ctx);
GR_TMP_CLEAR_VEC(S, 3 * n, ctx);
return status;
}
}
int
_gr_poly_div_divconquer_noinv(gr_ptr Q, gr_srcptr A, slong lenA,
gr_srcptr B, slong lenB, slong cutoff, gr_ctx_t ctx)
{
return _gr_poly_div_divconquer_preinv1(Q, A, lenA, B, lenB, NULL, cutoff, ctx);
}
int
_gr_poly_div_divconquer(gr_ptr Q, gr_srcptr A, slong lenA, gr_srcptr B, slong lenB, slong cutoff, gr_ctx_t ctx)
{
slong sz = ctx->sizeof_elem;
int status = GR_SUCCESS;
gr_ptr invB;
GR_TMP_INIT(invB, ctx);
status = gr_inv(invB, GR_ENTRY(B, lenB - 1, sz), ctx);
if (status == GR_SUCCESS)
status = _gr_poly_div_divconquer_preinv1(Q, A, lenA, B, lenB, invB, cutoff, ctx);
else
status = _gr_poly_div_divconquer_noinv(Q, A, lenA, B, lenB, cutoff, ctx);
GR_TMP_CLEAR(invB, ctx);
return status;
}
int
gr_poly_div_divconquer(gr_poly_t Q, const gr_poly_t A, const gr_poly_t B, slong cutoff, gr_ctx_t ctx)
{
slong Alen, Blen, Qlen;
int status = GR_SUCCESS;
slong sz = ctx->sizeof_elem;
Alen = A->length;
Blen = B->length;
if (Blen == 0)
return GR_DOMAIN;
if (gr_is_zero(GR_ENTRY(B->coeffs, Blen - 1, sz), ctx) != T_FALSE)
return GR_UNABLE;
if (Alen < Blen)
return gr_poly_zero(Q, ctx);
Qlen = Alen - Blen + 1;
if (Q == A || Q == B)
{
gr_poly_t t;
gr_poly_init2(t, Qlen, ctx);
status = _gr_poly_div_divconquer(t->coeffs, A->coeffs, A->length, B->coeffs, B->length, cutoff, ctx);
gr_poly_swap(Q, t, ctx);
gr_poly_clear(t, ctx);
}
else
{
gr_poly_fit_length(Q, Qlen, ctx);
status = _gr_poly_div_divconquer(Q->coeffs, A->coeffs, A->length, B->coeffs, B->length, cutoff, ctx);
}
_gr_poly_set_length(Q, Qlen, ctx);
_gr_poly_normalise(Q, ctx);
return status;
}