#include "gr_vec.h"
#include "gr_poly.h"
#include "gr_poly/impl.h"
int
_gr_poly_divrem_divconquer_recursive(gr_ptr Q, gr_ptr BQ, gr_ptr W,
gr_srcptr A, gr_srcptr B, slong lenB, gr_srcptr invB, slong cutoff, gr_ctx_t ctx)
{
int status = GR_SUCCESS;
slong sz = ctx->sizeof_elem;
if (lenB < FLINT_MAX(2, cutoff))
{
status |= _gr_vec_zero(BQ, lenB - 1, ctx);
status |= _gr_vec_set(GR_ENTRY(BQ, lenB - 1, sz), GR_ENTRY(A, lenB - 1, sz), lenB, ctx);
if (invB == NULL)
status |= _gr_poly_divrem_basecase_noinv(Q, BQ, BQ, 2 * lenB - 1, B, lenB, ctx);
else
status |= _gr_poly_divrem_basecase_preinv1(Q, BQ, BQ, 2 * lenB - 1, B, lenB, invB, ctx);
status |= _gr_vec_neg(BQ, BQ, lenB - 1, ctx);
status |= _gr_vec_set(GR_ENTRY(BQ, lenB - 1, sz), GR_ENTRY(A, lenB - 1, sz), lenB, 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_srcptr d4 = B;
gr_ptr q1 = GR_ENTRY(Q, n2, sz);
gr_ptr q2 = Q;
gr_ptr dq1 = GR_ENTRY(BQ, n2, sz);
gr_ptr d1q1 = GR_ENTRY(BQ, 2 * n2, sz);
gr_ptr d2q1, d3q2, d4q2, t;
status |= _gr_poly_divrem_divconquer_recursive(q1, d1q1, W1, p1, d1, n1, invB, cutoff, ctx);
d2q1 = W1;
status |= _gr_poly_mul(d2q1, q1, n1, d2, n2, ctx);
_gr_vec_swap(dq1, d2q1, n2, ctx);
status |= _gr_poly_add(GR_ENTRY(dq1, n2, sz), GR_ENTRY(dq1, n2, sz), n1 - 1, GR_ENTRY(d2q1, n2, sz), n1 - 1, ctx);
t = BQ;
status |= _gr_poly_sub(t, GR_ENTRY(A, n2 + (n1 - 1), sz), n2, GR_ENTRY(dq1, (n1 - 1), sz), n2, ctx);
p2 = GR_ENTRY(t, - (n2 - 1), sz);
d3q2 = W1;
status |= _gr_poly_divrem_divconquer_recursive(q2, d3q2, W2, p2, d3, n2, invB, cutoff, ctx);
d4q2 = W2;
status |= _gr_poly_mul(d4q2, d4, n1, q2, n2, ctx);
_gr_vec_swap(BQ, d4q2, n2, ctx);
status |= _gr_poly_add(GR_ENTRY(BQ, n2, sz), GR_ENTRY(BQ, n2, sz), n1 - 1, GR_ENTRY(d4q2, n2, sz), n1 - 1, ctx);
status |= _gr_poly_add(GR_ENTRY(BQ, n1, sz), GR_ENTRY(BQ, n1, sz), 2 * n2 - 1, d3q2, 2 * n2 - 1, ctx);
}
return status;
}
static int
__gr_poly_divrem_divconquer(gr_ptr Q, gr_ptr R, gr_srcptr A, slong lenA, gr_srcptr B, slong lenB, gr_srcptr invB, slong cutoff, gr_ctx_t ctx)
{
int status = GR_SUCCESS;
slong sz = ctx->sizeof_elem;
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_srcptr d2 = B;
gr_ptr W, d1q1, d2q1;
GR_TMP_INIT_VEC(W, (2 * n1 - 1) + lenB - 1, ctx);
d1q1 = GR_ENTRY(R, n2, sz);
d2q1 = GR_ENTRY(W, (2 * n1 - 1), sz);
status |= _gr_poly_divrem_divconquer_recursive(Q, d1q1, W, p1, d1, n1, invB, cutoff, ctx);
if (n1 >= n2)
status |= _gr_poly_mul(d2q1, Q, n1, d2, n2, ctx);
else
status |= _gr_poly_mul(d2q1, d2, n2, Q, n1, ctx);
_gr_vec_swap(R, d2q1, n2, ctx);
status |= _gr_poly_add(GR_ENTRY(R, n2, sz), GR_ENTRY(R, n2, sz), n1 - 1, GR_ENTRY(d2q1, n2, sz), n1 - 1, ctx);
status |= _gr_poly_sub(R, A, lenA, R, lenA, ctx);
GR_TMP_CLEAR_VEC(W, (2 * n1 - 1) + lenB - 1, ctx);
}
else
{
gr_ptr W;
GR_TMP_INIT_VEC(W, lenA, ctx);
status |= _gr_poly_divrem_divconquer_recursive(Q, R, W, A, B, lenB, invB, cutoff, ctx);
status |= _gr_poly_sub(R, A, lenB - 1, R, lenB - 1, ctx);
GR_TMP_CLEAR_VEC(W, lenA, ctx);
}
return status;
}
int
_gr_poly_divrem_divconquer_preinv1(gr_ptr Q, gr_ptr R, gr_srcptr A, slong lenA, gr_srcptr B, slong lenB, gr_srcptr invB, slong cutoff, gr_ctx_t ctx)
{
int status = GR_SUCCESS;
slong sz = ctx->sizeof_elem;
if (lenA <= 2 * lenB - 1)
{
gr_ptr W;
GR_TMP_INIT_VEC(W, lenA, ctx);
status |= __gr_poly_divrem_divconquer(Q, W, A, lenA, B, lenB, invB, cutoff, ctx);
_gr_vec_swap(R, W, lenB - 1, ctx);
GR_TMP_CLEAR_VEC(W, lenA, ctx);
}
else
{
slong shift, n = 2 * lenB - 1, len1;
gr_ptr QB, W, S;
len1 = 2 * n + lenA;
GR_TMP_INIT_VEC(W, len1, ctx);
S = GR_ENTRY(W, 2 * n, sz);
status |= _gr_vec_set(S, A, lenA, ctx);
QB = GR_ENTRY(W, n, sz);
while (lenA >= n)
{
shift = lenA - n;
status |= _gr_poly_divrem_divconquer_recursive(GR_ENTRY(Q, shift, sz), QB, W, GR_ENTRY(S, shift, sz), B, lenB, invB, cutoff, ctx);
status |= _gr_poly_sub(GR_ENTRY(S, shift, sz), GR_ENTRY(S, shift, sz), n, QB, n, ctx);
lenA -= lenB;
}
if (lenA >= lenB)
{
status |= __gr_poly_divrem_divconquer(Q, W, S, lenA, B, lenB, invB, cutoff, ctx);
_gr_vec_swap(W, S, lenA, ctx);
}
_gr_vec_swap(R, S, lenB - 1, ctx);
GR_TMP_CLEAR_VEC(W, len1, ctx);
}
return status;
}
int
_gr_poly_divrem_divconquer_noinv(gr_ptr Q, gr_ptr R, gr_srcptr A, slong lenA, gr_srcptr B, slong lenB, slong cutoff, gr_ctx_t ctx)
{
return _gr_poly_divrem_divconquer_preinv1(Q, R, A, lenA, B, lenB, NULL, cutoff, ctx);
}
int
_gr_poly_divrem_divconquer(gr_ptr Q, gr_ptr R, 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_divrem_divconquer_preinv1(Q, R, A, lenA, B, lenB, invB, cutoff, ctx);
else
status = _gr_poly_divrem_divconquer_noinv(Q, R, A, lenA, B, lenB, cutoff, ctx);
GR_TMP_CLEAR(invB, ctx);
return status;
}
int
gr_poly_divrem_divconquer(gr_poly_t Q, gr_poly_t R,
const gr_poly_t A, const gr_poly_t B, slong cutoff, gr_ctx_t ctx)
{
slong lenA = A->length, lenB = B->length, lenQ = lenA - lenB + 1;
slong sz = ctx->sizeof_elem;
gr_poly_t tQ, tR;
gr_ptr q, r;
int status = GR_SUCCESS;
if (lenB == 0)
return GR_DOMAIN;
if (gr_is_zero(GR_ENTRY(B->coeffs, lenB - 1, sz), ctx) != T_FALSE)
return GR_UNABLE;
if (lenA < lenB)
{
status |= gr_poly_set(R, A, ctx);
status |= gr_poly_zero(Q, ctx);
return status;
}
if (Q == A || Q == B)
{
gr_poly_init2(tQ, lenQ, ctx);
q = tQ->coeffs;
}
else
{
gr_poly_fit_length(Q, lenQ, ctx);
q = Q->coeffs;
}
if (R == B)
{
gr_poly_init2(tR, lenB - 1, ctx);
r = tR->coeffs;
}
else
{
gr_poly_fit_length(R, lenB - 1, ctx);
r = R->coeffs;
}
status |= _gr_poly_divrem_divconquer(q, r, A->coeffs, lenA, B->coeffs, lenB, cutoff, ctx);
if (Q == A || Q == B)
{
gr_poly_swap(tQ, Q, ctx);
gr_poly_clear(tQ, ctx);
}
if (R == B)
{
gr_poly_swap(tR, R, ctx);
gr_poly_clear(tR, ctx);
}
_gr_poly_set_length(Q, lenQ, ctx);
_gr_poly_set_length(R, lenB - 1, ctx);
_gr_poly_normalise(Q, ctx);
_gr_poly_normalise(R, ctx);
return status;
}