2021-11-25 15:20:28 +01:00
|
|
|
/*
|
|
|
|
Copyright (C) 2021 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 "arb_hypgeom.h"
|
|
|
|
|
|
|
|
static void
|
|
|
|
mag_div_fmpq(mag_t res, const mag_t x, const fmpq_t a)
|
|
|
|
{
|
|
|
|
mag_t t;
|
|
|
|
mag_init(t);
|
|
|
|
mag_set_fmpz_lower(t, fmpq_numref(a));
|
|
|
|
mag_div(res, x, t);
|
|
|
|
mag_set_fmpz(t, fmpq_denref(a));
|
|
|
|
mag_mul(res, res, t);
|
|
|
|
mag_clear(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
mag_pow_fmpq_fast(mag_t res, const mag_t x, const fmpq_t e)
|
|
|
|
{
|
|
|
|
fmpz_t b;
|
|
|
|
fmpz_init(b);
|
|
|
|
|
|
|
|
if (mag_cmp_2exp_si(x, 0) >= 0)
|
|
|
|
{
|
|
|
|
fmpz_cdiv_q(b, fmpq_numref(e), fmpq_denref(e));
|
|
|
|
mag_pow_fmpz(res, x, b);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fmpz_fdiv_q(b, fmpq_numref(e), fmpq_denref(e));
|
|
|
|
mag_pow_fmpz(res, x, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
fmpz_clear(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
slong
|
2022-01-13 11:12:26 +01:00
|
|
|
_arb_hypgeom_gamma_upper_fmpq_inf_choose_N_1(mag_t err, const fmpq_t a, const arb_t z, int prefactor, const mag_t abs_tol)
|
2021-11-25 15:20:28 +01:00
|
|
|
{
|
|
|
|
slong N, aa, ab;
|
|
|
|
fmpz_t az1, az2;
|
|
|
|
fmpq_t a1;
|
|
|
|
mag_t t, u;
|
|
|
|
|
|
|
|
fmpz_init(az1);
|
|
|
|
fmpz_init(az2);
|
|
|
|
fmpq_init(a1);
|
|
|
|
mag_init(t);
|
|
|
|
mag_init(u);
|
|
|
|
|
|
|
|
fmpz_fdiv_q(az1, fmpq_numref(a), fmpq_denref(a));
|
|
|
|
fmpz_cdiv_q(az2, fmpq_numref(a), fmpq_denref(a));
|
|
|
|
|
|
|
|
if (!fmpz_fits_si(az1) || !fmpz_fits_si(az2))
|
|
|
|
{
|
|
|
|
mag_inf(err);
|
|
|
|
N = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aa = fmpz_get_si(az1);
|
|
|
|
ab = fmpz_get_si(az2);
|
|
|
|
|
|
|
|
/* prefactor z^(a-1) * exp(-z) */
|
2022-01-13 11:12:26 +01:00
|
|
|
if (prefactor)
|
|
|
|
{
|
|
|
|
arb_get_mag_lower(t, z);
|
|
|
|
mag_expinv(t, t);
|
|
|
|
fmpq_sub_ui(a1, a, 1);
|
|
|
|
arb_get_mag(u, z);
|
|
|
|
mag_pow_fmpq_fast(u, u, a1);
|
|
|
|
mag_mul(err, t, u);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mag_one(err);
|
|
|
|
}
|
2021-11-25 15:20:28 +01:00
|
|
|
|
2021-12-02 18:19:40 +01:00
|
|
|
if (mag_is_inf(err))
|
2021-11-25 15:20:28 +01:00
|
|
|
{
|
2021-12-02 18:19:40 +01:00
|
|
|
N = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
arb_get_mag_lower(t, z);
|
|
|
|
mag_inv(t, t);
|
2021-11-25 15:20:28 +01:00
|
|
|
|
2021-12-02 18:19:40 +01:00
|
|
|
for (N = 1; ; N++)
|
2021-11-25 15:20:28 +01:00
|
|
|
{
|
2021-12-02 18:19:40 +01:00
|
|
|
mag_mul_ui(u, err, FLINT_MAX(FLINT_ABS(aa - N), FLINT_ABS(ab - N)));
|
|
|
|
mag_mul(u, u, t);
|
|
|
|
|
|
|
|
if (N >= ab - 1 && mag_cmp(u, abs_tol) < 0)
|
|
|
|
{
|
|
|
|
mag_swap(err, u);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop if terms are increasing, unless a is a positive integer in
|
|
|
|
which case the series will terminate eventually. */
|
|
|
|
if (mag_cmp(u, err) > 0 && !(aa == ab && aa >= 1))
|
|
|
|
{
|
|
|
|
mag_inf(err);
|
|
|
|
N = -1;
|
|
|
|
break;
|
|
|
|
}
|
2021-11-25 15:20:28 +01:00
|
|
|
|
2021-12-02 18:19:40 +01:00
|
|
|
mag_swap(err, u);
|
2021-11-25 15:20:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fmpz_clear(az1);
|
|
|
|
fmpz_clear(az2);
|
|
|
|
mag_clear(t);
|
|
|
|
mag_clear(u);
|
|
|
|
fmpq_clear(a1);
|
|
|
|
|
|
|
|
return N;
|
|
|
|
}
|
|
|
|
|
2022-01-13 11:12:26 +01:00
|
|
|
slong
|
|
|
|
_arb_hypgeom_gamma_upper_fmpq_inf_choose_N(mag_t err, const fmpq_t a, const arb_t z, const mag_t abs_tol)
|
|
|
|
{
|
|
|
|
return _arb_hypgeom_gamma_upper_fmpq_inf_choose_N_1(err, a, z, 1, abs_tol);
|
|
|
|
}
|
|
|
|
|
|
|
|
slong
|
|
|
|
_arb_hypgeom_gamma_upper_fmpq_inf_choose_N_rel(mag_t err, const fmpq_t a, const arb_t z, slong prec)
|
|
|
|
{
|
|
|
|
mag_t tol;
|
|
|
|
slong N;
|
|
|
|
mag_init(tol);
|
|
|
|
mag_set_ui_2exp_si(tol, 1, -prec);
|
|
|
|
N = _arb_hypgeom_gamma_upper_fmpq_inf_choose_N_1(err, a, z, 0, tol);
|
|
|
|
mag_clear(tol);
|
|
|
|
return N;
|
|
|
|
}
|
|
|
|
|
2021-11-25 15:20:28 +01:00
|
|
|
static void
|
|
|
|
upper_bsplit(arb_t M, arb_t S, arb_t Q, const fmpz_t ap, const fmpz_t aq, const arb_t z, slong na, slong nb, int cont, slong prec)
|
|
|
|
{
|
|
|
|
if (nb - na == 0)
|
|
|
|
{
|
|
|
|
arb_zero(M);
|
|
|
|
arb_zero(S);
|
|
|
|
arb_one(Q);
|
|
|
|
}
|
|
|
|
else if (nb - na == 1)
|
|
|
|
{
|
|
|
|
fmpz_t t;
|
|
|
|
fmpz_init_set(t, ap);
|
|
|
|
fmpz_submul_ui(t, aq, na + 1);
|
|
|
|
fmpz_neg(t, t);
|
|
|
|
arb_set_fmpz(M, t);
|
|
|
|
arb_mul_fmpz(S, z, aq, prec);
|
|
|
|
arb_neg(S, S);
|
|
|
|
arb_set(Q, S);
|
|
|
|
fmpz_clear(t);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slong m;
|
|
|
|
arb_t M2, S2, Q2;
|
|
|
|
|
|
|
|
m = na + (nb - na) / 2;
|
|
|
|
|
|
|
|
arb_init(M2);
|
|
|
|
arb_init(S2);
|
|
|
|
arb_init(Q2);
|
|
|
|
|
|
|
|
upper_bsplit(M, S, Q, ap, aq, z, na, m, 1, prec);
|
|
|
|
upper_bsplit(M2, S2, Q2, ap, aq, z, m, nb, cont, prec);
|
|
|
|
|
|
|
|
/* todo: squaring opt; power table */
|
|
|
|
arb_mul(S, S, Q2, prec);
|
|
|
|
arb_addmul(S, M, S2, prec);
|
|
|
|
|
|
|
|
if (cont)
|
|
|
|
arb_mul(M, M, M2, prec);
|
|
|
|
|
|
|
|
arb_mul(Q, Q, Q2, prec);
|
|
|
|
|
|
|
|
arb_clear(M2);
|
|
|
|
arb_clear(S2);
|
|
|
|
arb_clear(Q2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_arb_hypgeom_gamma_upper_fmpq_inf_bsplit(arb_t res, const fmpq_t a, const arb_t z, slong N, slong prec)
|
|
|
|
{
|
|
|
|
arb_t M, S, Q;
|
|
|
|
fmpq_t a1;
|
|
|
|
|
|
|
|
arb_init(M);
|
|
|
|
arb_init(S);
|
|
|
|
arb_init(Q);
|
|
|
|
fmpq_init(a1);
|
|
|
|
|
|
|
|
N = FLINT_MAX(N, 0);
|
|
|
|
|
|
|
|
upper_bsplit(M, S, Q, fmpq_numref(a), fmpq_denref(a), z, 0, N, 0, prec);
|
|
|
|
arb_div(S, S, Q, prec);
|
|
|
|
|
|
|
|
fmpq_sub_ui(a1, a, 1);
|
|
|
|
arb_pow_fmpq(M, z, a1, prec);
|
|
|
|
arb_mul(S, S, M, prec);
|
|
|
|
|
|
|
|
arb_neg(M, z);
|
|
|
|
arb_exp(M, M, prec);
|
|
|
|
arb_mul(res, S, M, prec);
|
|
|
|
|
|
|
|
arb_clear(M);
|
|
|
|
arb_clear(S);
|
|
|
|
arb_clear(Q);
|
|
|
|
fmpq_clear(a1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* select N and bound error for z**a * exp(-z) / a * sum(z**n / rf(a+1, n)) */
|
|
|
|
slong
|
|
|
|
_arb_hypgeom_gamma_lower_fmpq_0_choose_N(mag_t err, const fmpq_t a, const arb_t z, const mag_t abs_tol)
|
|
|
|
{
|
|
|
|
slong N, aa, ab, c;
|
|
|
|
fmpz_t az1, az2;
|
|
|
|
mag_t t, u;
|
|
|
|
|
|
|
|
fmpz_init(az1);
|
|
|
|
fmpz_init(az2);
|
|
|
|
mag_init(t);
|
|
|
|
mag_init(u);
|
|
|
|
|
|
|
|
fmpz_fdiv_q(az1, fmpq_numref(a), fmpq_denref(a));
|
|
|
|
fmpz_cdiv_q(az2, fmpq_numref(a), fmpq_denref(a));
|
|
|
|
|
|
|
|
if (!fmpz_fits_si(az1) || !fmpz_fits_si(az2))
|
|
|
|
{
|
|
|
|
mag_inf(err);
|
|
|
|
N = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aa = fmpz_get_si(az1);
|
|
|
|
ab = fmpz_get_si(az2);
|
|
|
|
|
|
|
|
/* prefactor z^a * exp(-z) / a */
|
|
|
|
arb_get_mag_lower(t, z);
|
|
|
|
mag_expinv(t, t);
|
|
|
|
arb_get_mag(u, z);
|
|
|
|
mag_pow_fmpq_fast(u, u, a);
|
|
|
|
mag_mul(err, t, u);
|
|
|
|
mag_div_fmpq(err, err, a);
|
|
|
|
|
|
|
|
arb_get_mag(t, z);
|
|
|
|
|
|
|
|
for (N = 1; ; N++)
|
|
|
|
{
|
|
|
|
c = FLINT_MIN(FLINT_ABS(aa + N), FLINT_ABS(ab + N));
|
|
|
|
|
|
|
|
if (c == 0)
|
|
|
|
{
|
|
|
|
fmpq_t q;
|
|
|
|
fmpq_init(q);
|
|
|
|
fmpq_add_ui(q, a, N);
|
|
|
|
mag_div_fmpq(err, err, q);
|
|
|
|
fmpq_clear(q);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mag_div_ui(err, err, c);
|
|
|
|
}
|
|
|
|
|
|
|
|
mag_mul(err, err, t);
|
|
|
|
|
|
|
|
/* todo: condition can be relaxed */
|
|
|
|
/* todo: faster check (compare t) */
|
|
|
|
if ((aa + N) > 0 && mag_cmp(err, abs_tol) < 0)
|
|
|
|
{
|
|
|
|
mag_div_ui(u, t, aa + N);
|
|
|
|
mag_geom_series(u, u, 0);
|
|
|
|
|
|
|
|
mag_mul(u, err, u);
|
|
|
|
|
|
|
|
if (mag_cmp(u, abs_tol) < 0)
|
|
|
|
{
|
|
|
|
mag_swap(err, u);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fmpz_clear(az1);
|
|
|
|
fmpz_clear(az2);
|
|
|
|
mag_clear(t);
|
|
|
|
mag_clear(u);
|
|
|
|
|
|
|
|
return N;
|
|
|
|
}
|
|
|
|
|
2022-01-13 11:12:26 +01:00
|
|
|
|
2021-11-25 15:20:28 +01:00
|
|
|
/* todo: squaring opt; power table */
|
|
|
|
static void
|
|
|
|
lower_bsplit(arb_t M, arb_t S, arb_t Q, const fmpz_t ap, const fmpz_t aq, const arb_t z, slong na, slong nb, int cont, slong prec)
|
|
|
|
{
|
|
|
|
if (nb - na == 0)
|
|
|
|
{
|
|
|
|
arb_zero(M);
|
|
|
|
arb_zero(S);
|
|
|
|
arb_one(Q);
|
|
|
|
}
|
|
|
|
else if (nb - na == 1)
|
|
|
|
{
|
|
|
|
fmpz_t t;
|
|
|
|
fmpz_init_set(t, ap);
|
|
|
|
fmpz_addmul_ui(t, aq, na + 1);
|
|
|
|
arb_set_fmpz(S, t);
|
|
|
|
arb_set(Q, S);
|
|
|
|
arb_mul_fmpz(M, z, aq, prec);
|
|
|
|
fmpz_clear(t);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slong m;
|
|
|
|
arb_t M2, S2, Q2;
|
|
|
|
|
|
|
|
m = na + (nb - na) / 2;
|
|
|
|
|
|
|
|
arb_init(M2);
|
|
|
|
arb_init(S2);
|
|
|
|
arb_init(Q2);
|
|
|
|
|
|
|
|
lower_bsplit(M, S, Q, ap, aq, z, na, m, 1, prec);
|
|
|
|
lower_bsplit(M2, S2, Q2, ap, aq, z, m, nb, cont, prec);
|
|
|
|
|
|
|
|
arb_mul(S, S, Q2, prec);
|
|
|
|
arb_addmul(S, M, S2, prec);
|
|
|
|
|
|
|
|
if (cont)
|
|
|
|
arb_mul(M, M, M2, prec);
|
|
|
|
|
|
|
|
arb_mul(Q, Q, Q2, prec);
|
|
|
|
|
|
|
|
arb_clear(M2);
|
|
|
|
arb_clear(S2);
|
|
|
|
arb_clear(Q2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_arb_hypgeom_gamma_lower_fmpq_0_bsplit(arb_t res, const fmpq_t a, const arb_t z, slong N, slong prec)
|
|
|
|
{
|
|
|
|
arb_t M, S, Q;
|
|
|
|
|
|
|
|
arb_init(M);
|
|
|
|
arb_init(S);
|
|
|
|
arb_init(Q);
|
|
|
|
|
|
|
|
N = FLINT_MAX(N, 0);
|
|
|
|
|
|
|
|
lower_bsplit(M, S, Q, fmpq_numref(a), fmpq_denref(a), z, 0, N, 0, prec);
|
|
|
|
arb_div(S, S, Q, prec);
|
|
|
|
|
|
|
|
arb_pow_fmpq(M, z, a, prec);
|
|
|
|
arb_mul(S, S, M, prec);
|
|
|
|
|
|
|
|
arb_neg(M, z);
|
|
|
|
arb_exp(M, M, prec);
|
|
|
|
arb_mul(S, S, M, prec);
|
|
|
|
|
|
|
|
arb_div_fmpz(S, S, fmpq_numref(a), prec);
|
|
|
|
arb_mul_fmpz(res, S, fmpq_denref(a), prec);
|
|
|
|
|
|
|
|
arb_clear(M);
|
|
|
|
arb_clear(S);
|
|
|
|
arb_clear(Q);
|
|
|
|
}
|
2021-11-27 13:29:53 +01:00
|
|
|
|
|
|
|
/* bounded by 1/z^n * sum z^k / k! */
|
|
|
|
slong
|
|
|
|
_arb_hypgeom_gamma_upper_singular_si_choose_N(mag_t err, slong n, const arb_t z, const mag_t abs_tol)
|
|
|
|
{
|
|
|
|
slong k;
|
|
|
|
mag_t t, u, zm;
|
|
|
|
|
|
|
|
mag_init(t);
|
|
|
|
mag_init(u);
|
|
|
|
mag_init(zm);
|
|
|
|
|
|
|
|
arb_get_mag(zm, z);
|
|
|
|
|
|
|
|
arb_get_mag_lower(t, z);
|
|
|
|
mag_inv(t, t);
|
|
|
|
mag_pow_ui(t, t, n);
|
|
|
|
|
|
|
|
for (k = 1; ; k++)
|
|
|
|
{
|
|
|
|
mag_mul(t, t, zm);
|
|
|
|
mag_div_ui(t, t, k);
|
|
|
|
|
|
|
|
if (mag_cmp(t, abs_tol) < 0)
|
|
|
|
{
|
|
|
|
mag_div_ui(u, zm, k);
|
|
|
|
mag_geom_series(u, u, 0);
|
|
|
|
mag_mul(u, t, u);
|
|
|
|
|
|
|
|
if (mag_cmp(u, abs_tol) < 0)
|
|
|
|
{
|
|
|
|
mag_swap(err, t);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mag_clear(t);
|
|
|
|
mag_clear(u);
|
|
|
|
mag_clear(zm);
|
|
|
|
|
|
|
|
return k;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
singular_bsplit(arb_t M, arb_t S, arb_t Q, slong n, const arb_t z, slong na, slong nb, int cont, slong prec)
|
|
|
|
{
|
|
|
|
if (nb - na == 0)
|
|
|
|
{
|
|
|
|
arb_zero(M);
|
|
|
|
arb_zero(S);
|
|
|
|
arb_one(Q);
|
|
|
|
}
|
|
|
|
else if (nb - na == 1)
|
|
|
|
{
|
|
|
|
fmpz_t t;
|
|
|
|
slong k;
|
|
|
|
|
|
|
|
k = na;
|
|
|
|
|
|
|
|
if (k == n)
|
|
|
|
arb_neg(M, z);
|
|
|
|
else
|
|
|
|
arb_mul_si(M, z, n - k, prec);
|
|
|
|
|
|
|
|
arb_set_si(S, (k != n) ? (k + 1) : 0);
|
|
|
|
|
|
|
|
fmpz_init_set_si(t, k + 1);
|
|
|
|
if (k != n)
|
|
|
|
fmpz_mul_si(t, t, k - n);
|
|
|
|
arb_set_fmpz(Q, t);
|
|
|
|
fmpz_clear(t);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slong m;
|
|
|
|
arb_t M2, S2, Q2;
|
|
|
|
|
|
|
|
m = na + (nb - na) / 2;
|
|
|
|
|
|
|
|
arb_init(M2);
|
|
|
|
arb_init(S2);
|
|
|
|
arb_init(Q2);
|
|
|
|
|
|
|
|
singular_bsplit(M, S, Q, n, z, na, m, 1, prec);
|
|
|
|
singular_bsplit(M2, S2, Q2, n, z, m, nb, cont, prec);
|
|
|
|
|
|
|
|
arb_mul(S, S, Q2, prec);
|
|
|
|
arb_addmul(S, M, S2, prec);
|
|
|
|
|
|
|
|
if (cont)
|
|
|
|
arb_mul(M, M, M2, prec);
|
|
|
|
|
|
|
|
arb_mul(Q, Q, Q2, prec);
|
|
|
|
|
|
|
|
arb_clear(M2);
|
|
|
|
arb_clear(S2);
|
|
|
|
arb_clear(Q2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_arb_hypgeom_gamma_upper_singular_si_bsplit(arb_t res, slong n, const arb_t z, slong N, slong prec)
|
|
|
|
{
|
|
|
|
arb_t M, S, Q;
|
|
|
|
|
|
|
|
arb_init(M);
|
|
|
|
arb_init(S);
|
|
|
|
arb_init(Q);
|
|
|
|
|
|
|
|
N = FLINT_MAX(N, 0);
|
|
|
|
|
|
|
|
singular_bsplit(M, S, Q, n, z, 0, N, 0, prec);
|
|
|
|
|
|
|
|
/* (-1)**n/fac(n) * (digamma(n+1) - ln(z)) - (S/Q)/z**n */
|
|
|
|
arb_pow_ui(M, z, n, prec);
|
|
|
|
arb_mul(Q, Q, M, prec);
|
|
|
|
arb_div(S, S, Q, prec);
|
|
|
|
|
|
|
|
arb_set_ui(M, n + 1);
|
|
|
|
arb_digamma(M, M, prec);
|
|
|
|
arb_log(Q, z, prec);
|
|
|
|
arb_sub(M, M, Q, prec);
|
|
|
|
arb_fac_ui(Q, n, prec);
|
|
|
|
arb_div(M, M, Q, prec);
|
|
|
|
if (n & 1)
|
|
|
|
arb_neg(M, M);
|
|
|
|
|
|
|
|
arb_sub(res, M, S, prec);
|
|
|
|
|
|
|
|
arb_clear(M);
|
|
|
|
arb_clear(S);
|
|
|
|
arb_clear(Q);
|
|
|
|
}
|