mirror of
https://github.com/vale981/arb
synced 2025-03-05 09:21:38 -05:00
236 lines
4.8 KiB
C
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);
|
|
}
|
|
|