#include "thread_support.h"
#include "acb.h"
#include "acb_dirichlet.h"
#include "arb_calc.h"
static const slong GRAMS_LAW_MAX = 126;
static const slong ROSSERS_RULE_MAX = 13999526;
typedef struct _zz_node_struct
{
arf_struct t;
arb_struct v;
fmpz *gram;
slong prec;
struct _zz_node_struct *prev;
struct _zz_node_struct *next;
}
zz_node_struct;
typedef zz_node_struct zz_node_t[1];
typedef zz_node_struct * zz_node_ptr;
typedef const zz_node_struct * zz_node_srcptr;
static int
zz_node_is_gram_node(const zz_node_t p)
{
return p->gram != NULL;
}
static int
zz_node_sgn(const zz_node_t p)
{
int s = arb_sgn_nonzero(&p->v);
if (!s)
{
flint_throw(FLINT_ERROR, "unexpectedly imprecise evaluation of Z(t)\n");
}
return s;
}
static int
zz_node_is_good_gram_node(const zz_node_t p)
{
if (zz_node_is_gram_node(p))
{
int s = zz_node_sgn(p);
if ((s > 0 && fmpz_is_even(p->gram)) ||
(s < 0 && fmpz_is_odd(p->gram)))
{
return 1;
}
}
return 0;
}
static void
zz_node_init(zz_node_t p)
{
arf_init(&p->t);
arb_init(&p->v);
arb_indeterminate(&p->v);
p->prec = 0;
p->gram = NULL;
p->prev = NULL;
p->next = NULL;
}
static void
zz_node_clear(zz_node_t p)
{
arf_clear(&p->t);
arb_clear(&p->v);
if (p->gram)
{
fmpz_clear(p->gram);
flint_free(p->gram);
}
p->prec = 0;
p->gram = NULL;
p->prev = NULL;
p->next = NULL;
}
static int
zz_node_refine(zz_node_t p)
{
slong default_prec = arf_bits(&p->t) + 8;
if (p->prec < default_prec)
{
p->prec = default_prec;
}
else
{
p->prec *= 2;
}
return _acb_dirichlet_definite_hardy_z(&p->v, &p->t, &p->prec);
}
static zz_node_ptr
create_non_gram_node(const arf_t t)
{
zz_node_ptr p = flint_malloc(sizeof(zz_node_struct));
zz_node_init(p);
arf_set(&p->t, t);
zz_node_refine(p);
return p;
}
static zz_node_ptr
create_gram_node(const fmpz_t n)
{
zz_node_ptr p;
arb_t t, v;
acb_t z;
slong prec = fmpz_bits(n) + 8;
arb_init(t);
arb_init(v);
acb_init(z);
while (1)
{
acb_dirichlet_gram_point(t, n, NULL, NULL, prec + fmpz_bits(n));
acb_set_arb(z, t);
acb_dirichlet_hardy_z(z, z, NULL, NULL, 1, prec);
acb_get_real(v, z);
if (!arb_contains_zero(v))
{
break;
}
prec *= 2;
}
p = flint_malloc(sizeof(zz_node_struct));
zz_node_init(p);
p->gram = flint_malloc(sizeof(fmpz));
fmpz_init(p->gram);
fmpz_set(p->gram, n);
arf_set(&p->t, arb_midref(t));
arb_set(&p->v, v);
p->prec = prec;
arb_clear(t);
arb_clear(v);
acb_clear(z);
return p;
}
static slong
count_gram_intervals(zz_node_srcptr a, zz_node_srcptr b)
{
slong out = 0;
if (!a || !b)
{
flint_throw(FLINT_ERROR, "a and b must be non-NULL\n");
}
if (!zz_node_is_good_gram_node(a) || !zz_node_is_good_gram_node(b))
{
flint_throw(FLINT_ERROR, "both nodes must be good Gram points\n");
}
else
{
fmpz_t m;
fmpz_init(m);
fmpz_sub(m, b->gram, a->gram);
out = fmpz_get_si(m);
fmpz_clear(m);
}
return out;
}
static slong
count_sign_changes(zz_node_srcptr a, zz_node_srcptr b)
{
zz_node_srcptr p, q;
slong n = 0;
if (!a || !b)
{
flint_throw(FLINT_ERROR, "a and b must be non-NULL\n");
}
p = a;
q = a->next;
while (p != b)
{
if (!q)
{
flint_throw(FLINT_ERROR, "prematurely reached end of list\n");
}
if (zz_node_sgn(p) != zz_node_sgn(q))
{
n++;
}
p = q;
q = q->next;
}
return n;
}
static zz_node_ptr
extend_to_next_good_gram_node(zz_node_t p)
{
fmpz_t n;
zz_node_ptr q, r;
fmpz_init(n);
if (!zz_node_is_gram_node(p))
{
flint_throw(FLINT_ERROR, "expected to begin at a gram point\n");
}
if (p->next)
{
flint_throw(FLINT_ERROR, "expected to extend from the end of a list\n");
}
fmpz_set(n, p->gram);
q = p;
while (1)
{
fmpz_add_ui(n, n, 1);
r = create_gram_node(n);
q->next = r;
r->prev = q;
q = r;
r = NULL;
if (zz_node_is_good_gram_node(q))
{
break;
}
}
fmpz_clear(n);
return q;
}
static zz_node_ptr
extend_to_prev_good_gram_node(zz_node_t p)
{
fmpz_t n;
zz_node_ptr q, r;
fmpz_init(n);
if (!zz_node_is_gram_node(p))
{
flint_throw(FLINT_ERROR, "expected to begin at a gram point\n");
}
if (p->prev)
{
flint_throw(FLINT_ERROR, "expected to extend from the start of a list\n");
}
fmpz_set(n, p->gram);
q = p;
while (1)
{
fmpz_sub_ui(n, n, 1);
r = create_gram_node(n);
q->prev = r;
r->next = q;
q = r;
r = NULL;
if (zz_node_is_good_gram_node(q))
{
break;
}
}
fmpz_clear(n);
return q;
}
static void
_arb_get_lbound_arf_nonnegative(arf_t res, const arb_t x, slong prec)
{
arb_get_lbound_arf(res, x, prec);
if (arf_cmp_si(res, 0) < 0)
{
arf_zero(res);
}
}
static void
_weighted_arithmetic_mean(arb_t res, const arf_t x1, const arf_t x2,
const arb_t w1, const arb_t w2, slong prec)
{
if (!arb_is_nonnegative(w1) || !arb_is_nonnegative(w2))
{
arb_indeterminate(res);
}
else if (arb_is_zero(w1) && arb_is_zero(w2))
{
arb_set_interval_arf(res, x1, x2, prec);
}
else if (arb_is_zero(w1))
{
arb_set_arf(res, x2);
}
else if (arb_is_zero(w2))
{
arb_set_arf(res, x1);
}
else if (arb_is_exact(w1) && arb_is_exact(w2))
{
arb_t a, b;
arb_init(a);
arb_init(b);
arb_mul_arf(a, w1, x1, prec);
arb_addmul_arf(a, w2, x2, prec);
arb_add(b, w1, w2, prec);
arb_div(res, a, b, prec);
arb_clear(a);
arb_clear(b);
}
else
{
arb_t a, b, r1, r2;
arb_init(a);
arb_init(b);
arb_init(r1);
arb_init(r2);
arb_zero(a);
arb_zero(b);
_arb_get_lbound_arf_nonnegative(arb_midref(a), w1, prec);
arb_get_ubound_arf(arb_midref(b), w2, prec);
_weighted_arithmetic_mean(r1, x1, x2, a, b, prec);
arb_zero(a);
arb_zero(b);
arb_get_ubound_arf(arb_midref(a), w1, prec);
_arb_get_lbound_arf_nonnegative(arb_midref(b), w2, prec);
_weighted_arithmetic_mean(r2, x1, x2, a, b, prec);
arb_union(res, r1, r2, prec);
arb_clear(a);
arb_clear(b);
arb_clear(r1);
arb_clear(r2);
}
}
static void
split_interval(arb_t out,
const arf_t t1, const arb_t v1, slong sign1,
const arf_t t2, const arb_t v2, slong sign2, slong prec)
{
if (sign1 == sign2)
{
arb_t w1, w2;
arb_init(w1);
arb_init(w2);
arb_abs(w1, v2);
arb_sqrt(w1, w1, prec);
arb_abs(w2, v1);
arb_sqrt(w2, w2, prec);
_weighted_arithmetic_mean(out, t1, t2, w1, w2, prec);
arb_clear(w1);
arb_clear(w2);
}
else
{
arb_set_arf(out, t1);
arb_add_arf(out, out, t2, prec);
arb_mul_2exp_si(out, out, -1);
}
}
static void
intercalate(zz_node_t a, zz_node_t b)
{
arb_t t;
zz_node_ptr q, r, mid_node;
if (a == NULL || b == NULL)
{
flint_throw(FLINT_ERROR, "a and b must be non-NULL\n");
}
if (!zz_node_is_good_gram_node(a) || !zz_node_is_good_gram_node(b))
{
flint_throw(FLINT_ERROR, "a and b must represent good Gram points\n");
}
if (a == b) return;
arb_init(t);
q = a;
r = a->next;
while (q != b)
{
if (!r)
{
flint_throw(FLINT_ERROR, "prematurely reached end of list\n");
}
while (1)
{
split_interval(t,
&q->t, &q->v, zz_node_sgn(q),
&r->t, &r->v, zz_node_sgn(r),
FLINT_MIN(q->prec, r->prec));
if (!arb_contains_arf(t, &q->t) && !arb_contains_arf(t, &r->t))
{
break;
}
zz_node_refine((q->prec < r->prec) ? q : r);
}
mid_node = create_non_gram_node(arb_midref(t));
q->next = mid_node;
mid_node->prev = q;
mid_node->next = r;
r->prev = mid_node;
q = r;
r = r->next;
}
arb_clear(t);
}
static void
trim(zz_node_ptr *A, zz_node_ptr *B,
zz_node_ptr a, zz_node_ptr b, slong k)
{
slong n;
for (n = 0; n < k; n++)
{
a = a->next;
while (!zz_node_is_good_gram_node(a))
{
a = a->next;
}
b = b->prev;
while (!zz_node_is_good_gram_node(b))
{
b = b->prev;
}
}
*A = a;
*B = b;
}
static slong
count_up_separated_zeros(arf_interval_ptr res,
zz_node_srcptr U, zz_node_srcptr V, const fmpz_t n, slong len)
{
if (len <= 0)
{
return 0;
}
else if (fmpz_sgn(n) < 1)
{
flint_throw(FLINT_ERROR, "nonpositive indices of zeros are not supported\n");
}
else if (U == NULL || V == NULL)
{
flint_throw(FLINT_ERROR, "U and V must not be NULL\n");
}
if (!zz_node_is_good_gram_node(U) || !zz_node_is_good_gram_node(V))
{
flint_throw(FLINT_ERROR, "U and V must be good Gram points\n");
}
else
{
slong i = 0;
zz_node_srcptr p = U;
fmpz_t N, k;
fmpz_init(N);
fmpz_init(k);
fmpz_add_ui(N, p->gram, 1);
fmpz_set(k, n);
while (p != V)
{
if (!p->next)
{
flint_throw(FLINT_ERROR, "prematurely reached end of list\n");
}
if (zz_node_sgn(p) != zz_node_sgn(p->next))
{
fmpz_add_ui(N, N, 1);
if (fmpz_equal(N, k))
{
arf_set(&res[i].a, &p->t);
arf_set(&res[i].b, &p->next->t);
fmpz_add_ui(k, k, 1);
i++;
if (i == len)
break;
}
}
p = p->next;
}
fmpz_clear(k);
return i;
}
return 0;
}
static void
turing_search_near(zz_node_ptr *pu, zz_node_ptr *pv, slong *psb, const fmpz_t n)
{
zz_node_ptr u, v;
slong i;
slong zn;
slong sb;
slong cgb;
const slong loopcount = 4;
fmpz_t k;
fmpz_init(k);
fmpz_sub_ui(k, n, 2);
u = create_gram_node(k);
fmpz_sub_ui(k, n, 1);
v = create_gram_node(k);
u->next = v;
v->prev = u;
if (!zz_node_is_good_gram_node(u))
u = extend_to_prev_good_gram_node(u);
if (!zz_node_is_good_gram_node(v))
v = extend_to_next_good_gram_node(v);
sb = 0;
cgb = 0;
while (1)
{
zz_node_ptr nv;
nv = extend_to_next_good_gram_node(v);
zn = count_gram_intervals(v, nv);
for (i = 0; i < loopcount && count_sign_changes(v, nv) < zn; i++)
{
intercalate(v, nv);
}
if (count_sign_changes(v, nv) >= zn)
{
cgb++;
if (cgb > sb)
{
sb = cgb;
if (acb_dirichlet_turing_method_bound(nv->gram) <= sb)
{
v = nv;
break;
}
}
}
else
{
cgb = 0;
}
v = nv;
}
cgb = 0;
while (1)
{
zz_node_ptr pu;
pu = extend_to_prev_good_gram_node(u);
zn = count_gram_intervals(pu, u);
for (i = 0; i < loopcount && count_sign_changes(pu, u) < zn; i++)
{
intercalate(pu, u);
}
if (count_sign_changes(pu, u) >= zn)
{
cgb++;
if (cgb == sb)
{
u = pu;
break;
}
}
else
{
cgb = 0;
}
u = pu;
}
*pu = u;
*pv = v;
*psb = sb;
fmpz_clear(k);
}
static void
turing_search_far(zz_node_ptr *pu, zz_node_ptr *pv, slong *psb,
zz_node_ptr u, zz_node_ptr v, slong initial_cgb)
{
slong i;
slong zn;
slong sb;
slong cgb;
const slong loopcount = 4;
sb = 0;
cgb = initial_cgb;
while (1)
{
zz_node_ptr nv;
nv = extend_to_next_good_gram_node(v);
zn = count_gram_intervals(v, nv);
for (i = 0; i < loopcount && count_sign_changes(v, nv) < zn; i++)
{
intercalate(v, nv);
}
if (count_sign_changes(v, nv) >= zn)
{
cgb++;
if (cgb % 2 == 0 && sb < cgb / 2)
{
sb = cgb / 2;
if (acb_dirichlet_turing_method_bound(nv->gram) <= sb)
{
v = nv;
break;
}
}
}
else
{
cgb = 0;
}
v = nv;
}
cgb = initial_cgb;
while (1)
{
zz_node_ptr pu;
pu = extend_to_prev_good_gram_node(u);
zn = count_gram_intervals(pu, u);
for (i = 0; i < loopcount && count_sign_changes(pu, u) < zn; i++)
{
intercalate(pu, u);
}
if (count_sign_changes(pu, u) >= zn)
{
cgb++;
if (cgb == sb*2)
{
u = pu;
break;
}
}
else
{
cgb = 0;
}
u = pu;
}
*pu = u;
*pv = v;
*psb = sb;
}
static void
_separated_turing_list(zz_node_ptr *pU, zz_node_ptr *pV,
zz_node_ptr *pu, zz_node_ptr *pv, const fmpz_t n)
{
zz_node_ptr U, V, u, v;
slong i;
slong sb_near;
slong sb_far;
slong zn;
slong variations;
const slong loopcount = 4;
if (fmpz_cmp_si(n, 2) < 0)
{
flint_throw(FLINT_ERROR, "invalid n: %s\n", fmpz_get_str(NULL, 10, n));
}
turing_search_near(&u, &v, &sb_near, n);
trim(&U, &V, u, v, sb_near);
zn = count_gram_intervals(U, V);
for (i = 0; i < loopcount && count_sign_changes(U, V) < zn; i++)
{
intercalate(U, V);
}
variations = count_sign_changes(U, V);
if (variations > zn)
{
flint_throw(FLINT_ERROR, "unexpected number of sign changes\n");
}
else if (variations < zn)
{
zz_node_ptr r = U;
zz_node_ptr s = V;
turing_search_far(&u, &v, &sb_far, u, v, sb_near);
trim(&U, &V, u, v, 2*sb_far);
zn = count_gram_intervals(U, V);
for (i = 0; i < loopcount && count_sign_changes(U, V) < zn; i++)
{
intercalate(U, r);
intercalate(s, V);
}
variations = count_sign_changes(U, V);
if (variations > zn)
{
flint_throw(FLINT_ERROR, "unexpected number of sign changes\n");
}
else if (variations < zn)
{
trim(&U, &V, u, v, sb_far);
zn = count_gram_intervals(U, V);
while (count_sign_changes(U, V) < zn)
{
intercalate(U, V);
}
if (count_sign_changes(U, V) != zn)
{
flint_throw(FLINT_ERROR, "unexpected number of sign changes\n");
}
}
}
*pu = u;
*pv = v;
*pU = U;
*pV = V;
}
static void
_separated_rosser_list(zz_node_ptr *pu, zz_node_ptr *pv, const fmpz_t n)
{
fmpz_t k;
zz_node_ptr u, v;
if (fmpz_cmp_si(n, 1) < 0 || fmpz_cmp_si(n, ROSSERS_RULE_MAX) > 0)
{
flint_throw(FLINT_ERROR, "invalid n: %s\n", fmpz_get_str(NULL, 10, n));
}
fmpz_init(k);
fmpz_sub_ui(k, n, 2);
u = create_gram_node(k);
fmpz_sub_ui(k, n, 1);
v = create_gram_node(k);
u->next = v;
v->prev = u;
if (!zz_node_is_good_gram_node(u))
u = extend_to_prev_good_gram_node(u);
if (!zz_node_is_good_gram_node(v))
v = extend_to_next_good_gram_node(v);
while (count_sign_changes(u, v) != count_gram_intervals(u, v))
{
intercalate(u, v);
}
*pu = u;
*pv = v;
fmpz_clear(k);
}
static void
_separated_gram_list(zz_node_ptr *pu, zz_node_ptr *pv, const fmpz_t n)
{
fmpz_t k;
zz_node_ptr u, v;
if (fmpz_cmp_si(n, 1) < 0 || fmpz_cmp_si(n, GRAMS_LAW_MAX) > 0)
{
flint_throw(FLINT_ERROR, "invalid n: %s\n", fmpz_get_str(NULL, 10, n));
}
fmpz_init(k);
fmpz_sub_ui(k, n, 2);
u = create_gram_node(k);
fmpz_sub_ui(k, n, 1);
v = create_gram_node(k);
u->next = v;
v->prev = u;
*pu = u;
*pv = v;
fmpz_clear(k);
}
static void
_separated_list(zz_node_ptr *pU, zz_node_ptr *pV,
zz_node_ptr *pu, zz_node_ptr *pv, const fmpz_t n)
{
zz_node_ptr U, V, u, v;
if (fmpz_cmp_si(n, GRAMS_LAW_MAX) <= 0)
{
_separated_gram_list(&u, &v, n);
U = u;
V = v;
}
else if (fmpz_cmp_si(n, ROSSERS_RULE_MAX) <= 0)
{
_separated_rosser_list(&u, &v, n);
U = u;
V = v;
}
else
{
_separated_turing_list(&U, &V, &u, &v, n);
}
if (U == NULL || V == NULL)
{
flint_throw(FLINT_ERROR, "U and V must not be NULL\n");
}
if (!zz_node_is_good_gram_node(U) || !zz_node_is_good_gram_node(V))
{
flint_throw(FLINT_ERROR, "U and V must be good Gram points\n");
}
if (U == V)
{
flint_throw(FLINT_ERROR, "the list must include at least one interval\n");
}
*pU = U;
*pV = V;
*pu = u;
*pv = v;
}
static slong
_isolate_hardy_z_zeros(arf_interval_ptr res, const fmpz_t n, slong len)
{
zz_node_ptr U, V, u, v;
slong count;
_separated_list(&U, &V, &u, &v, n);
count = count_up_separated_zeros(res, U, V, n, len);
while (u)
{
v = u;
u = u->next;
zz_node_clear(v);
flint_free(v);
}
return count;
}
static void
acb_dirichlet_isolate_hardy_z_zeros(arf_interval_ptr res, const fmpz_t n, slong len)
{
if (len <= 0)
{
return;
}
else if (fmpz_sgn(n) < 1)
{
flint_throw(FLINT_ERROR, "nonpositive indices of zeros are not supported\n");
}
else
{
slong c = 0;
fmpz_t k;
fmpz_init(k);
while (c < len)
{
fmpz_add_si(k, n, c);
c += _isolate_hardy_z_zeros(res + c, k, len - c);
}
fmpz_clear(k);
}
}
void
acb_dirichlet_isolate_hardy_z_zero(arf_t a, arf_t b, const fmpz_t n)
{
if (fmpz_sgn(n) < 1)
{
flint_throw(FLINT_ERROR, "nonpositive indices of zeros are not supported\n");
}
else
{
arf_interval_t r;
arf_interval_init(r);
_isolate_hardy_z_zeros(r, n, 1);
arf_set(a, &r->a);
arf_set(b, &r->b);
arf_interval_clear(r);
}
}
static void
count_up(arf_t a, arf_t b, zz_node_srcptr U, zz_node_srcptr V, const fmpz_t n)
{
arf_interval_t r;
arf_interval_init(r);
count_up_separated_zeros(r, U, V, n, 1);
arf_set(a, &r->a);
arf_set(b, &r->b);
arf_interval_clear(r);
}
void
_acb_dirichlet_isolate_turing_hardy_z_zero(arf_t a, arf_t b, const fmpz_t n)
{
zz_node_ptr U, V, u, v;
_separated_turing_list(&U, &V, &u, &v, n);
count_up(a, b, U, V, n);
while (u)
{
v = u;
u = u->next;
zz_node_clear(v);
flint_free(v);
}
}
void
_acb_dirichlet_isolate_rosser_hardy_z_zero(arf_t a, arf_t b, const fmpz_t n)
{
zz_node_ptr u, v;
_separated_rosser_list(&u, &v, n);
count_up(a, b, u, v, n);
while (u)
{
v = u;
u = u->next;
zz_node_clear(v);
flint_free(v);
}
}
void
_acb_dirichlet_isolate_gram_hardy_z_zero(arf_t a, arf_t b, const fmpz_t n)
{
zz_node_ptr u, v;
_separated_gram_list(&u, &v, n);
count_up(a, b, u, v, n);
while (u)
{
v = u;
u = u->next;
zz_node_clear(v);
flint_free(v);
}
}
static void
_acb_set_arf(acb_t res, const arf_t t)
{
acb_zero(res);
arb_set_arf(acb_realref(res), t);
}
static void
gram_index(fmpz_t res, const arf_t t)
{
if (arf_cmp_si(t, 10) < 0)
{
flint_throw(FLINT_ERROR, "gram_index requires t > 10\n");
}
else
{
acb_t z;
slong prec = arf_abs_bound_lt_2exp_si(t) + 8;
acb_init(z);
while (1)
{
_acb_set_arf(z, t);
acb_dirichlet_hardy_theta(z, z, NULL, NULL, 1, prec);
arb_const_pi(acb_imagref(z), prec);
arb_div(acb_realref(z), acb_realref(z), acb_imagref(z), prec);
arb_floor(acb_realref(z), acb_realref(z), prec);
if (arb_get_unique_fmpz(res, acb_realref(z)))
{
break;
}
prec *= 2;
}
acb_clear(z);
}
}
static slong
_exact_zeta_multi_nzeros(fmpz *res, arf_srcptr points, slong len)
{
zz_node_ptr U, V, u, v, p;
arb_t x;
fmpz_t n, N;
slong i;
arf_srcptr t;
int s;
slong prec;
arb_init(x);
fmpz_init(n);
fmpz_init(N);
gram_index(n, points);
fmpz_add_ui(n, n, 2);
_separated_list(&U, &V, &u, &v, n);
p = U;
fmpz_add_ui(N, U->gram, 1);
for (i = 0, t = points; i < len && arf_cmp(t, &p->t) <= 0; i++, t++)
{
fmpz_set(res + i, N);
}
while (i < len && p != V)
{
if (!p->next)
{
flint_throw(FLINT_ERROR, "prematurely reached the end of the list\n");
}
if (zz_node_sgn(p) != zz_node_sgn(p->next))
{
while (i < len && arf_cmp(t, &p->next->t) <= 0)
{
prec = arf_abs_bound_lt_2exp_si(t) + 8;
s = _acb_dirichlet_definite_hardy_z(x, t, &prec);
if (zz_node_sgn(p->next) == s)
{
fmpz_add_ui(res + i, N, 1);
}
else
{
fmpz_set(res + i, N);
}
t++;
i++;
}
fmpz_add_ui(N, N, 1);
}
p = p->next;
}
if (i == 0)
{
fmpz_set(res, N);
i++;
}
while (u)
{
v = u;
u = u->next;
zz_node_clear(v);
flint_free(v);
}
arb_clear(x);
fmpz_clear(n);
fmpz_clear(N);
return i;
}
static void
exact_zeta_multi_nzeros(fmpz *res, arf_srcptr p, slong len)
{
slong i, c;
arf_srcptr q;
if (len <= 0)
{
return;
}
for (i = 0, q = p; i < len - 1; i++, q++)
{
if (arf_cmp(q, p) > 0)
{
flint_throw(FLINT_ERROR, "p must be in increasing order\n");
}
}
c = 0;
while (c < len)
{
if (arf_cmp_si(p + c, 14) < 0)
{
fmpz_zero(res + c);
c++;
}
else
{
c += _exact_zeta_multi_nzeros(res + c, p + c, len - c);
}
}
}
void
_acb_dirichlet_exact_zeta_nzeros(fmpz_t res, const arf_t t)
{
exact_zeta_multi_nzeros(res, t, 1);
}
typedef struct
{
arb_ptr res;
arf_interval_ptr p;
slong prec;
}
work_t;
static void
refinement_worker(slong i, work_t * work)
{
_acb_dirichlet_refine_hardy_z_zero(work->res + i, &(work->p[i].a), &(work->p[i].b), work->prec);
}
void
acb_dirichlet_hardy_z_zeros(arb_ptr res, const fmpz_t n, slong len, slong prec)
{
if (len <= 0)
{
return;
}
else if (fmpz_sgn(n) < 1)
{
flint_throw(FLINT_ERROR, "nonpositive indices of zeros are not supported\n");
}
else
{
work_t work;
arf_interval_ptr p = _arf_interval_vec_init(len);
acb_dirichlet_isolate_hardy_z_zeros(p, n, len);
work.res = res;
work.p = p;
work.prec = prec;
flint_parallel_do((do_func_t) refinement_worker, &work, len, -1, FLINT_PARALLEL_STRIDED);
_arf_interval_vec_clear(p, len);
}
}
static void
_arb_set_interval_fmpz(arb_t res, const fmpz_t a, const fmpz_t b)
{
fmpz_t n;
fmpz_init(n);
fmpz_add(n, a, b);
arf_set_fmpz(arb_midref(res), n);
fmpz_sub(n, b, a);
mag_set_fmpz(arb_radref(res), n);
arb_mul_2exp_si(res, res, -1);
fmpz_clear(n);
}
void
acb_dirichlet_zeta_nzeros(arb_t res, const arb_t t, slong prec)
{
if (arb_is_exact(t))
{
fmpz_t n;
fmpz_init(n);
_acb_dirichlet_exact_zeta_nzeros(n, arb_midref(t));
arb_set_fmpz(res, n);
fmpz_clear(n);
}
else
{
arf_struct b[2];
fmpz n[2];
arf_init(b);
arf_init(b + 1);
fmpz_init(n);
fmpz_init(n + 1);
arb_get_lbound_arf(b, t, prec);
arb_get_ubound_arf(b + 1, t, prec);
exact_zeta_multi_nzeros(n, b, 2);
_arb_set_interval_fmpz(res, n, n + 1);
arf_clear(b);
arf_clear(b + 1);
fmpz_clear(n);
fmpz_clear(n + 1);
}
arb_set_round(res, res, prec);
}
void
acb_dirichlet_zeta_nzeros_gram(fmpz_t res, const fmpz_t n)
{
zz_node_ptr U, V, u, v, p;
fmpz_t k, N;
if (fmpz_cmp_si(n, -1) < 0)
{
flint_throw(FLINT_ERROR, "n must be >= -1\n");
}
fmpz_init(k);
fmpz_init(N);
fmpz_add_ui(k, n, 2);
_separated_list(&U, &V, &u, &v, k);
p = U;
fmpz_add_ui(N, U->gram, 1);
fmpz_set_si(res, -1);
while (1)
{
if (p == NULL)
{
flint_throw(FLINT_ERROR, "prematurely reached the end of the list\n");
}
if (zz_node_is_gram_node(p) && fmpz_equal(n, p->gram))
{
fmpz_set(res, N);
break;
}
if (zz_node_sgn(p) != zz_node_sgn(p->next))
{
fmpz_add_ui(N, N, 1);
}
if (p == V)
{
break;
}
p = p->next;
}
if (fmpz_sgn(res) < 0)
{
flint_throw(FLINT_ERROR, "failed to find the gram point\n");
}
while (u)
{
v = u;
u = u->next;
zz_node_clear(v);
flint_free(v);
}
fmpz_clear(k);
fmpz_clear(N);
}