#include "longlong.h"
#include "gr_vec.h"
#include "gr_poly.h"
gr_ptr * _gr_poly_tree_alloc(slong len, gr_ctx_t ctx)
{
gr_ptr * tree = NULL;
if (len)
{
slong i, height = FLINT_CLOG2(len);
tree = flint_malloc(sizeof(gr_ptr) * (height + 1));
for (i = 0; i <= height; i++)
{
slong n = len + (len >> i) + 1;
tree[i] = flint_malloc(n * ctx->sizeof_elem);
_gr_vec_init(tree[i], n, ctx);
}
}
return tree;
}
void _gr_poly_tree_free(gr_ptr * tree, slong len, gr_ctx_t ctx)
{
if (len)
{
slong i, height = FLINT_CLOG2(len);
for (i = 0; i <= height; i++)
{
slong n = len + (len >> i) + 1;
_gr_vec_clear(tree[i], n, ctx);
flint_free(tree[i]);
}
flint_free(tree);
}
}
static int
_gr_poly_mul_monic(gr_ptr res, gr_srcptr poly1, slong len1, gr_srcptr poly2, slong len2, gr_ctx_t ctx)
{
int status = GR_SUCCESS;
if (len1 + len2 - 2 > 0)
status |= _gr_poly_mullow(res, poly1, len1, poly2, len2, len1 + len2 - 2, ctx);
status |= gr_one(GR_ENTRY(res, len1 + len2 - 2, ctx->sizeof_elem), ctx);
return status;
}
int
_gr_poly_tree_build(gr_ptr * tree, gr_srcptr roots, slong len, gr_ctx_t ctx)
{
slong height, pow, left, i;
gr_ptr pa, pb;
gr_srcptr a, b;
slong sz = ctx->sizeof_elem;
int status = GR_SUCCESS;
if (len == 0)
return GR_SUCCESS;
height = FLINT_CLOG2(len);
for (i = 0; i < len; i++)
{
status |= gr_one(GR_ENTRY(tree[0], 2 * i + 1, sz), ctx);
status |= gr_neg(GR_ENTRY(tree[0], 2 * i, sz), GR_ENTRY(roots, i, sz), ctx);
}
if (height > 1)
{
pa = tree[1];
for (i = 0; i < len / 2; i++)
{
a = GR_ENTRY(roots, 2 * i, sz);
b = GR_ENTRY(roots, 2 * i + 1, sz);
status |= gr_mul(GR_ENTRY(pa, (3 * i), sz), a, b, ctx);
status |= gr_add(GR_ENTRY(pa, (3 * i + 1), sz), a, b, ctx);
status |= gr_neg(GR_ENTRY(pa, (3 * i + 1), sz), GR_ENTRY(pa, (3 * i + 1), sz), ctx);
status |= gr_one(GR_ENTRY(pa, (3 * i + 2), sz), ctx);
}
if (len & 1)
{
status |= gr_neg(GR_ENTRY(pa, 3 * (len / 2), sz), GR_ENTRY(roots, len - 1, sz), ctx);
status |= gr_one(GR_ENTRY(pa, 3 * (len / 2) + 1, sz), ctx);
}
}
for (i = 1; i < height - 1; i++)
{
left = len;
pow = WORD(1) << i;
pa = tree[i];
pb = tree[i + 1];
while (left >= 2 * pow)
{
status |= _gr_poly_mul_monic(pb, pa, pow + 1, GR_ENTRY(pa, pow + 1, sz), pow + 1, ctx);
left -= 2 * pow;
pa = GR_ENTRY(pa, 2 * pow + 2, sz);
pb = GR_ENTRY(pb, 2 * pow + 1, sz);
}
if (left > pow)
{
status |= _gr_poly_mul_monic(pb, pa, pow + 1, GR_ENTRY(pa, pow + 1, sz), left - pow + 1, ctx);
}
else if (left > 0)
{
status |= _gr_vec_set(pb, pa, left + 1, ctx);
}
}
return status;
}
static inline int
_gr_poly_rem_2(gr_ptr q, gr_ptr r, gr_srcptr a, slong al,
gr_srcptr b, slong bl, gr_ctx_t ctx)
{
slong sz = ctx->sizeof_elem;
int status = GR_SUCCESS;
if (al == 2)
{
status |= gr_mul(r, GR_ENTRY(a, 1, sz), b, ctx);
status |= gr_sub(r, a, r, ctx);
}
else
{
status |= _gr_poly_divrem(q, r, a, al, b, bl, ctx);
}
return status;
}
int
_gr_poly_evaluate_vec_fast_precomp(gr_ptr vs, gr_srcptr poly,
slong plen, const gr_ptr * tree, slong len, gr_ctx_t ctx)
{
slong height, i, j, pow, left;
slong tree_height;
slong tlen, alloc, qalloc;
gr_ptr tmp, q, t, u, swap, pa, pb, pc;
slong sz = ctx->sizeof_elem;
int status = GR_SUCCESS;
if (len < 2 || plen < 2)
{
if (len == 1)
{
gr_ptr tmp;
GR_TMP_INIT(tmp, ctx);
status |= gr_neg(tmp, tree[0], ctx);
status |= _gr_poly_evaluate(vs, poly, plen, tmp, ctx);
GR_TMP_CLEAR(tmp, ctx);
}
else if (len != 0 && plen == 0)
{
status |= _gr_vec_zero(vs, len, ctx);
}
else if (len != 0 && plen == 1)
{
for (i = 0; i < len; i++)
status |= gr_set(GR_ENTRY(vs, i, sz), poly, ctx);
}
return status;
}
left = len;
height = FLINT_BIT_COUNT(plen - 1) - 1;
tree_height = FLINT_CLOG2(len);
while (height >= tree_height)
height--;
pow = WORD(1) << height;
qalloc = pow;
for (i = j = 0; i < len; i += pow, j += (pow + 1))
{
tlen = ((i + pow) <= len) ? pow : len % pow;
qalloc = FLINT_MAX(qalloc, plen - tlen);
}
alloc = 2 * len + qalloc;
GR_TMP_INIT_VEC(tmp, alloc, ctx);
t = tmp;
u = GR_ENTRY(t, len, sz);
q = GR_ENTRY(u, len, sz);
for (i = j = 0; i < len; i += pow, j += (pow + 1))
{
tlen = ((i + pow) <= len) ? pow : len % pow;
status |= _gr_poly_divrem(q, GR_ENTRY(t, i, sz), poly, plen, GR_ENTRY(tree[height], j, sz), tlen + 1, ctx);
}
for (i = height - 1; i >= 0; i--)
{
pow = WORD(1) << i;
left = len;
pa = tree[i];
pb = t;
pc = u;
while (left >= 2 * pow)
{
status |= _gr_poly_rem_2(q, pc, pb, 2 * pow, pa, pow + 1, ctx);
status |= _gr_poly_rem_2(q, GR_ENTRY(pc, pow, sz), pb, 2 * pow, GR_ENTRY(pa, pow + 1, sz), pow + 1, ctx);
pa = GR_ENTRY(pa, 2 * pow + 2, sz);
pb = GR_ENTRY(pb, 2 * pow, sz);
pc = GR_ENTRY(pc, 2 * pow, sz);
left -= 2 * pow;
}
if (left > pow)
{
status |= _gr_poly_divrem(q, pc, pb, left, pa, pow + 1, ctx);
status |= _gr_poly_divrem(q, GR_ENTRY(pc, pow, sz), pb, left, GR_ENTRY(pa, pow + 1, sz), left - pow + 1, ctx);
}
else if (left > 0)
status |= _gr_vec_set(pc, pb, left, ctx);
swap = t;
t = u;
u = swap;
}
_gr_vec_swap(vs, t, len, ctx);
GR_TMP_CLEAR_VEC(tmp, alloc, ctx);
return status;
}
int _gr_poly_evaluate_vec_fast(gr_ptr ys, gr_srcptr poly, slong plen, gr_srcptr xs, slong n, gr_ctx_t ctx)
{
gr_ptr * tree;
int status = GR_SUCCESS;
tree = _gr_poly_tree_alloc(n, ctx);
status |= _gr_poly_tree_build(tree, xs, n, ctx);
status |= _gr_poly_evaluate_vec_fast_precomp(ys, poly, plen, tree, n, ctx);
_gr_poly_tree_free(tree, n, ctx);
return status;
}
int
gr_poly_evaluate_vec_fast(gr_vec_t ys, const gr_poly_t poly, const gr_vec_t xs, gr_ctx_t ctx)
{
gr_vec_set_length(ys, xs->length, ctx);
return _gr_poly_evaluate_vec_fast(ys->entries, poly->coeffs, poly->length, xs->entries, xs->length, ctx);
}