arb/acb_hypgeom/dilog_zero_taylor.c
2017-02-24 21:58:23 +01:00

236 lines
4.8 KiB
C

/*
Copyright (C) 2017 Fredrik Johansson
This file is part of Arb.
Arb is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License (LGPL) as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version. See <http://www.gnu.org/licenses/>.
*/
#include "acb_hypgeom.h"
static void
bsplit_zero(acb_t P, acb_t R, acb_t Q, const acb_t z,
slong a, slong b, slong prec)
{
if (b - a == 1)
{
acb_mul_ui(P, z, a * a, prec);
acb_set_ui(R, (a + 1) * (a + 1));
acb_set(Q, R);
}
else
{
acb_t P2, R2, Q2;
slong m;
acb_init(P2);
acb_init(R2);
acb_init(Q2);
m = a + (b - a) / 2;
bsplit_zero(P, R, Q, z, a, m, prec);
bsplit_zero(P2, R2, Q2, z, m, b, prec);
acb_mul(R, R, Q2, prec);
acb_addmul(R, P, R2, prec);
acb_mul(P, P, P2, prec);
acb_mul(Q, Q, Q2, prec);
acb_clear(P2);
acb_clear(R2);
acb_clear(Q2);
}
}
#if FLINT_BITS == 64
#define DIVLIM 1625 /* just a tuning value */
#define DIVLIM2 1000000000 /* avoid overflow */
#else
#define DIVLIM 40
#define DIVLIM2 30000
#endif
void
acb_hypgeom_dilog_taylor_sum(acb_t res, const acb_t z, slong n, slong prec)
{
slong k, qk, m, power;
ulong q;
acb_t s, t, u;
acb_ptr zpow;
int real;
if (n <= 3)
{
if (n <= 1)
acb_zero(res);
else if (n == 2)
acb_set_round(res, z, prec);
else
{
acb_init(t);
acb_mul(t, z, z, prec);
acb_mul_2exp_si(t, t, -2);
acb_add(res, z, t, prec);
acb_clear(t);
}
return;
}
/* use binary splitting */
if (prec > 4000 && acb_bits(z) < prec * 0.02)
{
acb_init(s);
acb_init(t);
acb_init(u);
bsplit_zero(s, t, u, z, 1, n, prec);
acb_add(s, s, t, prec);
acb_mul(s, s, z, prec);
acb_div(res, s, u, prec);
acb_clear(s);
acb_clear(t);
acb_clear(u);
return;
}
real = acb_is_real(z);
k = n - 1;
m = n_sqrt(n);
acb_init(s);
acb_init(t);
zpow = _acb_vec_init(m + 1);
_acb_vec_set_powers(zpow, z, m + 1, prec);
power = (n - 1) % m;
while (k >= DIVLIM)
{
if (k < DIVLIM2) /* todo: write a div_uiui function? */
{
acb_div_ui(t, zpow + power, k * k, prec);
}
else
{
acb_div_ui(t, zpow + power, k, prec);
acb_div_ui(t, t, k, prec);
}
acb_add(s, s, t, prec);
if (power == 0)
{
acb_mul(s, s, zpow + m, prec);
power = m - 1;
}
else
{
power--;
}
k--;
}
qk = k; /* k at which to change denominator */
q = 1;
while (k >= 2)
{
/* find next qk such that the consecutive denominators can be
collected in a single word */
if (k == qk)
{
if (q != 1)
acb_div_ui(s, s, q, prec);
q = qk * qk;
qk--;
while (qk > 1)
{
ulong hi, lo;
umul_ppmm(hi, lo, q, qk * qk);
if (hi != 0)
break;
q *= qk * qk;
qk--;
}
acb_mul_ui(s, s, q, prec);
}
if (power == 0)
{
acb_add_ui(s, s, q / (k * k), prec);
acb_mul(s, s, zpow + m, prec);
power = m - 1;
}
else
{
if (real) /* minor optimization */
arb_addmul_ui(acb_realref(s), acb_realref(zpow + power), q / (k * k), prec);
else
acb_addmul_ui(s, zpow + power, q / (k * k), prec);
power--;
}
k--;
}
if (q != 1)
acb_div_ui(s, s, q, prec);
acb_add(s, s, z, prec);
acb_swap(res, s);
_acb_vec_clear(zpow, m + 1);
acb_clear(s);
acb_clear(t);
}
void
acb_hypgeom_dilog_zero_taylor(acb_t res, const acb_t z, slong prec)
{
mag_t m;
slong n;
double x;
int real;
mag_init(m);
acb_get_mag(m, z);
real = acb_is_real(z);
x = -mag_get_d_log2_approx(m);
n = 2;
if (x > 0.01)
{
n = prec / x + 1;
n += (x > 2.0); /* relative error for very small |z| */
}
n = FLINT_MAX(n, 2);
mag_geom_series(m, m, n);
mag_div_ui(m, m, n);
mag_div_ui(m, m, n);
if (mag_is_finite(m))
{
acb_hypgeom_dilog_taylor_sum(res, z, n, prec);
if (real)
arb_add_error_mag(acb_realref(res), m);
else
acb_add_error_mag(res, m);
}
else
{
acb_indeterminate(res);
}
mag_clear(m);
}