#include "arb.h"
#include "arb_mat.h"
#include "acb_theta.h"
#define ACB_THETA_ELD_MAX_PTS 10000000
#define ACB_THETA_ELD_MAX_ERR 100
static void
slong_vec_max(slong * r, slong * v1, slong * v2, slong d)
{
slong k;
for (k = 0; k < d; k++)
{
r[k] = FLINT_MAX(v1[k], v2[k]);
}
}
static int
arf_get_si_safe(slong * m, const arf_t x, arf_rnd_t rnd)
{
if (!arf_is_finite(x))
{
return 0;
}
else if (arf_cmpabs_2exp_si(x, FLINT_BITS - 4) > 0)
{
return 0;
}
else
{
*m = arf_get_si(x, rnd);
return 1;
}
}
static int
acb_theta_eld_interval(slong * min, slong * mid, slong * max, const arb_t ctr, const arf_t rad)
{
slong lp = ACB_THETA_LOW_PREC;
slong e;
arb_t y;
arf_t b;
int res;
arb_init(y);
arf_init(b);
arf_set_mag(b, arb_radref(ctr));
res = arf_get_si_safe(&e, b, ARF_RND_NEAR);
if (res)
{
res = (e <= ACB_THETA_ELD_MAX_ERR);
}
res = res && arf_get_si_safe(mid, arb_midref(ctr), ARF_RND_NEAR);
arb_set_arf(y, rad);
arb_add(y, ctr, y, lp);
arb_get_ubound_arf(b, y, lp);
res = res && arf_get_si_safe(max, b, ARF_RND_FLOOR);
arb_set_arf(y, rad);
arb_sub(y, ctr, y, lp);
arb_get_lbound_arf(b, y, lp);
res = res && arf_get_si_safe(min, b, ARF_RND_CEIL);
arb_clear(y);
arf_clear(b);
return res;
}
static void
acb_theta_eld_next_R2(arf_t next_R2, const arf_t R2, const arb_t gamma, const arb_t v, slong k)
{
slong lp = ACB_THETA_LOW_PREC;
arb_t x;
arf_t sub;
arb_init(x);
arf_init(sub);
arb_mul_si(x, gamma, k, lp);
arb_add(x, x, v, lp);
arb_sqr(x, x, lp);
arb_get_lbound_arf(sub, x, lp);
arf_sub(next_R2, R2, sub, lp, ARF_RND_CEIL);
arb_clear(x);
arf_clear(sub);
}
static void
acb_theta_eld_init_children(acb_theta_eld_t E, slong nr, slong nl)
{
slong d = E->dim;
slong g = E->ambient_dim;
slong k;
if (nr > 0)
{
E->rchildren = flint_malloc(nr * sizeof(struct acb_theta_eld_struct));
E->nr = nr;
for (k = 0; k < nr; k++)
{
acb_theta_eld_init(&E->rchildren[k], d - 1, g);
}
}
if (nl > 0)
{
E->lchildren = flint_malloc(nl * sizeof(struct acb_theta_eld_struct));
E->nl = nl;
for (k = 0; k < nl; k++)
{
acb_theta_eld_init(&E->lchildren[k], d - 1, g);
}
}
}
static int
acb_theta_eld_init_interval(acb_theta_eld_t E, const arb_mat_t C,
const arf_t R2, arb_srcptr v, slong * last_coords)
{
slong min, mid, max;
slong d = E->dim;
slong g = E->ambient_dim;
slong lp = ACB_THETA_LOW_PREC;
slong k;
arb_t x, ctr;
arf_t rad;
int res;
arb_init(x);
arb_init(ctr);
arf_init(rad);
for (k = 0; k < g - d; k++)
{
E->last_coords[k] = last_coords[k];
}
if (arf_cmp_si(R2, 0) < 0)
{
arf_zero(rad);
}
else
{
arb_set_arf(x, R2);
arb_sqrt(x, x, lp);
arb_div(x, x, arb_mat_entry(C, d - 1, d - 1), lp);
arb_get_ubound_arf(rad, x, lp);
}
arb_div(ctr, &v[d - 1], arb_mat_entry(C, d - 1, d - 1), lp);
arb_neg(ctr, ctr);
res = acb_theta_eld_interval(&min, &mid, &max, ctr, rad);
if (res)
{
E->min = min;
E->mid = mid;
E->max = max;
}
arb_clear(x);
arb_clear(ctr);
arf_clear(rad);
return res;
}
static int
acb_theta_eld_set_rec(acb_theta_eld_t E, const arb_mat_t C,
const arf_t R2, arb_srcptr v, slong * last_coords)
{
slong d = E->dim;
slong g = E->ambient_dim;
slong min, mid, max;
slong lp = ACB_THETA_LOW_PREC;
arf_t next_R2;
slong *next_coords;
arb_ptr v_diff;
arb_ptr v_mid;
arb_ptr next_v;
slong c, nr, nl, k;
int res;
res = acb_theta_eld_init_interval(E, C, R2, v, last_coords);
if (!res)
{
return 0;
}
min = E->min;
mid = E->mid;
max = E->max;
if (min > max)
{
E->nb_pts = 0;
if (d == 1)
{
E->nb_border = 2;
}
else
{
E->nb_border = 0;
}
for (k = 0; k < d; k++)
{
E->box[k] = 0;
}
return 1;
}
else if (d == 1)
{
E->nb_pts = max - min + 1;
E->nb_border = 2;
E->box[0] = FLINT_MAX(max, -min);
return (E->nb_pts <= ACB_THETA_ELD_MAX_PTS);
}
arf_init(next_R2);
next_coords = flint_malloc((g - d + 1) * sizeof(slong));
v_diff = _arb_vec_init(d - 1);
v_mid = _arb_vec_init(d - 1);
next_v = _arb_vec_init(d - 1);
nr = max - mid + 1;
nl = mid - min;
acb_theta_eld_init_children(E, nr, nl);
for (k = 0; k < d - 1; k++)
{
arb_set(&v_diff[k], arb_mat_entry(C, k, d - 1));
arb_mul_si(&v_mid[k], &v_diff[k], mid, lp);
}
_arb_vec_add(v_mid, v_mid, v, d - 1, lp);
for (k = 0; k < g - d; k++)
{
next_coords[k + 1] = last_coords[k];
}
E->nb_pts = 0;
E->nb_border = 0;
E->box[d - 1] = FLINT_MAX(max, -min);
for (k = 0; k < d - 1; k++)
{
E->box[k] = 0;
}
_arb_vec_set(next_v, v_mid, d - 1);
for (k = 0; (k < nr) && res; k++)
{
if (k > 0)
{
_arb_vec_add(next_v, next_v, v_diff, d - 1, lp);
}
c = mid + k;
acb_theta_eld_next_R2(next_R2, R2, arb_mat_entry(C, d - 1, d - 1), &v[d - 1], c);
next_coords[0] = c;
res = acb_theta_eld_set_rec(&E->rchildren[k], C, next_R2, next_v, next_coords);
if (res)
{
E->nb_pts += (&E->rchildren[k])->nb_pts;
E->nb_border += (&E->rchildren[k])->nb_border;
slong_vec_max(E->box, E->box, (&E->rchildren[k])->box, d - 1);
res = ((E->nb_pts) <= ACB_THETA_ELD_MAX_PTS);
}
}
_arb_vec_set(next_v, v_mid, d - 1);
for (k = 0; (k < nl) && res; k++)
{
_arb_vec_sub(next_v, next_v, v_diff, d - 1, lp);
c = mid - (k + 1);
acb_theta_eld_next_R2(next_R2, R2, arb_mat_entry(C, d - 1, d - 1), &v[d - 1], c);
next_coords[0] = c;
res = acb_theta_eld_set_rec(&E->lchildren[k], C, next_R2, next_v, next_coords);
if (res)
{
E->nb_pts += (&E->lchildren[k])->nb_pts;
E->nb_border += (&E->lchildren[k])->nb_border;
slong_vec_max(E->box, E->box, (&E->lchildren[k])->box, d - 1);
res = ((E->nb_pts) <= ACB_THETA_ELD_MAX_PTS);
}
}
arf_clear(next_R2);
flint_free(next_coords);
_arb_vec_clear(v_diff, d - 1);
_arb_vec_clear(v_mid, d - 1);
_arb_vec_clear(next_v, d - 1);
return res;
}
int
acb_theta_eld_set(acb_theta_eld_t E, const arb_mat_t C, const arf_t R2, arb_srcptr v)
{
slong d = E->dim;
slong g = E->ambient_dim;
acb_theta_eld_clear(E);
acb_theta_eld_init(E, d, g);
return acb_theta_eld_set_rec(E, C, R2, v, NULL);
}