mirror of
https://github.com/vale981/arb
synced 2025-03-05 09:21:38 -05:00
571 lines
13 KiB
C
571 lines
13 KiB
C
/*
|
|
Copyright (C) 2015 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"
|
|
#include "acb_hypgeom.h"
|
|
|
|
int
|
|
acb_hypgeom_u_asymp_determine_region(const mag_t r,
|
|
const mag_t zlo, const acb_t z);
|
|
|
|
void
|
|
acb_hypgeom_gamma_upper_asymp(acb_t res, const acb_t s,
|
|
const acb_t z, int regularized, slong prec)
|
|
{
|
|
acb_t t, u;
|
|
|
|
acb_init(t);
|
|
acb_init(u);
|
|
|
|
acb_sub_ui(t, s, 1, prec);
|
|
acb_neg(t, t);
|
|
|
|
acb_hypgeom_u_asymp(u, t, t, z, -1, prec);
|
|
|
|
if (regularized == 2)
|
|
{
|
|
acb_div(u, u, z, prec);
|
|
}
|
|
else
|
|
{
|
|
acb_neg(t, t);
|
|
acb_pow(t, z, t, prec);
|
|
acb_mul(u, u, t, prec);
|
|
|
|
if (regularized == 1)
|
|
{
|
|
acb_rgamma(t, s, prec);
|
|
acb_mul(u, u, t, prec);
|
|
}
|
|
}
|
|
|
|
acb_neg(t, z);
|
|
acb_exp(t, t, prec);
|
|
acb_mul(res, t, u, prec);
|
|
|
|
acb_clear(t);
|
|
acb_clear(u);
|
|
}
|
|
|
|
void
|
|
acb_hypgeom_gamma_upper_1f1a(acb_t res, const acb_t s,
|
|
const acb_t z, int regularized, slong prec)
|
|
{
|
|
acb_t a, t, w;
|
|
acb_struct b[2];
|
|
|
|
acb_init(a);
|
|
acb_init(b);
|
|
acb_init(b + 1);
|
|
acb_init(t);
|
|
acb_init(w);
|
|
|
|
acb_set(a, s);
|
|
acb_add_ui(b, s, 1, prec);
|
|
acb_one(b + 1);
|
|
acb_neg(w, z);
|
|
|
|
/* t = 1F1(s, s+1, -z) / s */
|
|
acb_hypgeom_pfq_direct(t, a, 1, b, 2, w, -1, prec);
|
|
acb_div(t, t, s, prec);
|
|
|
|
if (regularized == 2)
|
|
{
|
|
acb_neg(a, s);
|
|
acb_pow(a, z, a, prec);
|
|
acb_gamma(b, s, prec);
|
|
acb_mul(b, b, a, prec);
|
|
acb_sub(res, b, t, prec);
|
|
}
|
|
else
|
|
{
|
|
acb_pow(a, z, s, prec);
|
|
acb_mul(t, t, a, prec);
|
|
|
|
if (regularized == 1)
|
|
{
|
|
acb_rgamma(a, s, prec);
|
|
acb_mul(t, t, a, prec);
|
|
acb_sub_ui(res, t, 1, prec);
|
|
acb_neg(res, res);
|
|
}
|
|
else
|
|
{
|
|
acb_gamma(a, s, prec);
|
|
acb_sub(res, a, t, prec);
|
|
}
|
|
}
|
|
|
|
acb_clear(a);
|
|
acb_clear(b);
|
|
acb_clear(b + 1);
|
|
acb_clear(t);
|
|
acb_clear(w);
|
|
}
|
|
|
|
void
|
|
acb_hypgeom_gamma_upper_1f1b(acb_t res, const acb_t s,
|
|
const acb_t z, int regularized, slong prec)
|
|
{
|
|
acb_t a, b, t;
|
|
|
|
acb_init(a);
|
|
acb_init(b);
|
|
acb_init(t);
|
|
|
|
acb_add_ui(b, s, 1, prec);
|
|
acb_hypgeom_pfq_direct(t, NULL, 0, b, 1, z, -1, prec);
|
|
acb_div(t, t, s, prec);
|
|
|
|
acb_neg(a, z);
|
|
acb_exp(a, a, prec);
|
|
acb_mul(t, t, a, prec);
|
|
|
|
if (regularized == 2)
|
|
{
|
|
acb_neg(a, s);
|
|
acb_pow(a, z, a, prec);
|
|
acb_gamma(b, s, prec);
|
|
acb_mul(b, b, a, prec);
|
|
acb_sub(res, b, t, prec);
|
|
}
|
|
else
|
|
{
|
|
acb_pow(a, z, s, prec);
|
|
acb_mul(t, t, a, prec);
|
|
|
|
if (regularized == 1)
|
|
{
|
|
acb_rgamma(a, s, prec);
|
|
acb_mul(t, t, a, prec);
|
|
acb_sub_ui(res, t, 1, prec);
|
|
acb_neg(res, res);
|
|
}
|
|
else
|
|
{
|
|
acb_gamma(a, s, prec);
|
|
acb_sub(res, a, t, prec);
|
|
}
|
|
}
|
|
|
|
acb_clear(a);
|
|
acb_clear(b);
|
|
acb_clear(t);
|
|
}
|
|
|
|
/* requires n <= 0 */
|
|
void
|
|
acb_hypgeom_gamma_upper_singular(acb_t res, slong s, const acb_t z, int regularized, slong prec)
|
|
{
|
|
acb_t A, B, C, t, u;
|
|
acb_struct a[2];
|
|
acb_struct b[2];
|
|
arb_t f;
|
|
slong n;
|
|
|
|
if (regularized == 1)
|
|
{
|
|
acb_zero(res);
|
|
return;
|
|
}
|
|
|
|
n = -s;
|
|
|
|
acb_init(A);
|
|
acb_init(B);
|
|
acb_init(C);
|
|
acb_init(t);
|
|
acb_init(u);
|
|
arb_init(f);
|
|
acb_init(a + 0);
|
|
acb_init(a + 1);
|
|
acb_init(b + 0);
|
|
acb_init(b + 1);
|
|
|
|
arb_fac_ui(f, n, prec);
|
|
|
|
/* (-1)^n (psi(n+1) - log(z)) / n! */
|
|
acb_set_ui(A, n + 1);
|
|
acb_digamma(A, A, prec);
|
|
|
|
acb_log(t, z, prec);
|
|
acb_sub(A, A, t, prec);
|
|
acb_div_arb(A, A, f, prec);
|
|
if (n % 2)
|
|
acb_neg(A, A);
|
|
|
|
/* (-1)^n z 2F2(1,1;2,2+n;-z) / (n+1)! */
|
|
acb_set_si(a, 1);
|
|
acb_set_si(b, 2);
|
|
acb_set_si(b + 1, 2 + n);
|
|
acb_neg(t, z);
|
|
acb_hypgeom_pfq_direct(B, a, 1, b, 2, t, -1, prec);
|
|
acb_mul(B, B, z, prec);
|
|
arb_mul_ui(f, f, n + 1, prec);
|
|
acb_div_arb(B, B, f, prec);
|
|
if (n % 2)
|
|
acb_neg(B, B);
|
|
|
|
/* -sum((-z)^k / ((k-n) k!), k=0...n-1) */
|
|
acb_set_si(a, -n);
|
|
acb_set_si(b, 1 - n);
|
|
acb_set_si(b + 1, 1);
|
|
acb_neg(t, z);
|
|
|
|
if (n == 0)
|
|
{
|
|
acb_zero(C);
|
|
}
|
|
else
|
|
{
|
|
acb_hypgeom_pfq_sum(C, u, a, 1, b, 2, t, n, prec);
|
|
acb_div_ui(C, C, n, prec);
|
|
}
|
|
|
|
if (regularized == 2)
|
|
{
|
|
acb_pow_si(t, z, n, prec);
|
|
acb_mul(A, A, t, prec);
|
|
acb_mul(B, B, t, prec);
|
|
}
|
|
else
|
|
{
|
|
acb_pow_si(t, z, -n, prec);
|
|
acb_mul(C, C, t, prec);
|
|
}
|
|
|
|
acb_add(res, A, B, prec);
|
|
acb_add(res, res, C, prec);
|
|
|
|
acb_clear(A);
|
|
acb_clear(B);
|
|
acb_clear(C);
|
|
acb_clear(t);
|
|
acb_clear(u);
|
|
arb_clear(f);
|
|
acb_clear(a + 0);
|
|
acb_clear(a + 1);
|
|
acb_clear(b + 0);
|
|
acb_clear(b + 1);
|
|
}
|
|
|
|
static int
|
|
_determine_region(const acb_t s, const acb_t z)
|
|
{
|
|
int R;
|
|
mag_t r, zlo;
|
|
acb_t t;
|
|
|
|
mag_init(r);
|
|
mag_init(zlo);
|
|
acb_init(t);
|
|
|
|
/* lower bound for |z| */
|
|
acb_get_mag_lower(zlo, z);
|
|
|
|
/* upper bound for r = |s - 1| */
|
|
acb_sub_ui(t, s, 1, MAG_BITS);
|
|
acb_get_mag(r, t);
|
|
|
|
R = acb_hypgeom_u_asymp_determine_region(r, zlo, z);
|
|
|
|
mag_clear(r);
|
|
mag_clear(zlo);
|
|
acb_clear(t);
|
|
|
|
return R;
|
|
}
|
|
|
|
/* Returns 1 if it can be determined that a^n > b^n + c^n.
|
|
* Requires 1 <= n <= WORD_MAX.
|
|
* If n == WORD_MAX, the infinity norm a > max(b, c) is compared instead. */
|
|
int
|
|
_mag_gt_norm_ui(const mag_t a, const mag_t b, const mag_t c, ulong n)
|
|
{
|
|
int result;
|
|
result = 0;
|
|
if (n < 1)
|
|
{
|
|
flint_abort();
|
|
}
|
|
else if (mag_is_zero(a))
|
|
{
|
|
result = 0;
|
|
}
|
|
else if (mag_is_zero(b))
|
|
{
|
|
result = mag_cmp(a, c) > 0;
|
|
}
|
|
else if (mag_is_zero(c))
|
|
{
|
|
result = mag_cmp(a, b) > 0;
|
|
}
|
|
else if (n == WORD_MAX)
|
|
{
|
|
result = mag_cmp(a, b) > 0 && mag_cmp(a, c) > 0;
|
|
}
|
|
else if (n == 1)
|
|
{
|
|
mag_t sum;
|
|
mag_init(sum);
|
|
mag_add(sum, b, c);
|
|
result = mag_cmp(a, sum) > 0;
|
|
mag_clear(sum);
|
|
}
|
|
else if (_mag_gt_norm_ui(a, b, c, 1))
|
|
{
|
|
result = 1;
|
|
}
|
|
else if (!_mag_gt_norm_ui(a, b, c, WORD_MAX))
|
|
{
|
|
result = 0;
|
|
}
|
|
else
|
|
{
|
|
mag_t u, v, w, sum;
|
|
mag_init(u);
|
|
mag_init(v);
|
|
mag_init(w);
|
|
mag_init(sum);
|
|
mag_pow_ui_lower(u, a, n);
|
|
mag_pow_ui(v, b, n);
|
|
mag_pow_ui(w, c, n);
|
|
mag_add(sum, v, w);
|
|
result = mag_cmp(u, sum) > 0;
|
|
mag_clear(u);
|
|
mag_clear(v);
|
|
mag_clear(w);
|
|
mag_clear(sum);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* Given nonnegative real s, x, and prec,
|
|
* returns 1 if the asymptotic expansion of the
|
|
* hypergeometric U function should be used for upper incomplete gamma.
|
|
* It returns 1 if x > 4 and (x-c)^8 > (a*s)^8 + (b*prec)^8.
|
|
* Careful mag rounding is not used because this is just a heuristic. */
|
|
static int
|
|
_nonnegative_real_use_asymp(const mag_t s, const mag_t x, slong prec)
|
|
{
|
|
int result;
|
|
result = 0;
|
|
if (mag_cmp_2exp_si(x, 2) > 0)
|
|
{
|
|
mag_t a, b, c, u, v, w;
|
|
mag_init(a);
|
|
mag_init(b);
|
|
mag_init(c);
|
|
mag_init(u);
|
|
mag_init(v);
|
|
mag_init(w);
|
|
mag_set_d(a, 1.029287542);
|
|
mag_set_d(b, 0.3319411658);
|
|
mag_set_d(c, 2.391097143);
|
|
mag_sub(u, x, c);
|
|
mag_mul(v, a, s);
|
|
mag_mul_ui(w, b, FLINT_MAX(0, prec));
|
|
result = _mag_gt_norm_ui(u, v, w, 8);
|
|
mag_clear(a);
|
|
mag_clear(b);
|
|
mag_clear(c);
|
|
mag_clear(u);
|
|
mag_clear(v);
|
|
mag_clear(w);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
_acb_is_nonnegative_real(const acb_t z)
|
|
{
|
|
return arb_is_zero(acb_imagref(z)) && arb_is_nonnegative(acb_realref(z));
|
|
}
|
|
|
|
void
|
|
acb_hypgeom_gamma_upper_nointegration(acb_t res, const acb_t s, const acb_t z, int regularized, slong prec)
|
|
{
|
|
if (!acb_is_finite(s) || !acb_is_finite(z))
|
|
{
|
|
acb_indeterminate(res);
|
|
return;
|
|
}
|
|
|
|
if (acb_is_zero(z))
|
|
{
|
|
if (regularized == 2)
|
|
{
|
|
if (arb_is_negative(acb_realref(s)))
|
|
{
|
|
acb_inv(res, s, prec);
|
|
acb_neg(res, res);
|
|
}
|
|
else
|
|
{
|
|
acb_indeterminate(res);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (arb_is_positive(acb_realref(s)))
|
|
{
|
|
if (regularized == 1)
|
|
acb_one(res);
|
|
else
|
|
acb_gamma(res, s, prec);
|
|
}
|
|
else
|
|
{
|
|
acb_indeterminate(res);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
slong n = WORD_MAX;
|
|
|
|
if (acb_is_int(s))
|
|
{
|
|
if (regularized == 1 && arf_sgn(arb_midref(acb_realref(s))) <= 0)
|
|
{
|
|
acb_zero(res);
|
|
return;
|
|
}
|
|
|
|
if (arf_cmpabs_2exp_si(arb_midref(acb_realref(s)), 30) < 0)
|
|
{
|
|
n = arf_get_si(arb_midref(acb_realref(s)), ARF_RND_DOWN);
|
|
}
|
|
}
|
|
|
|
if (n >= 1 && n <= 3)
|
|
{
|
|
acb_t t, u;
|
|
|
|
acb_init(t);
|
|
acb_init(u);
|
|
|
|
if (regularized == 2)
|
|
acb_pow_si(u, z, n, prec);
|
|
|
|
if (n == 1)
|
|
{
|
|
acb_neg(res, z);
|
|
acb_exp(res, res, prec);
|
|
}
|
|
else if (n == 2)
|
|
{
|
|
acb_add_ui(t, z, 1, prec);
|
|
acb_neg(res, z);
|
|
acb_exp(res, res, prec);
|
|
acb_mul(res, res, t, prec);
|
|
}
|
|
else if (n == 3)
|
|
{
|
|
acb_add_ui(t, z, 2, prec);
|
|
acb_mul(t, t, z, prec);
|
|
acb_add_ui(t, t, 2, prec);
|
|
acb_neg(res, z);
|
|
acb_exp(res, res, prec);
|
|
acb_mul(res, res, t, prec);
|
|
}
|
|
|
|
if (regularized == 2)
|
|
acb_div(res, res, u, prec);
|
|
else if (regularized == 1 && n == 3)
|
|
acb_mul_2exp_si(res, res, -1);
|
|
|
|
acb_clear(t);
|
|
acb_clear(u);
|
|
|
|
return;
|
|
}
|
|
|
|
if (_acb_is_nonnegative_real(s) && _acb_is_nonnegative_real(z))
|
|
{
|
|
int result;
|
|
mag_t ms, mx;
|
|
mag_init(ms);
|
|
mag_init(mx);
|
|
arb_get_mag(ms, acb_realref(s));
|
|
arb_get_mag(mx, acb_realref(z));
|
|
result = _nonnegative_real_use_asymp(ms, mx, prec);
|
|
mag_clear(ms);
|
|
mag_clear(mx);
|
|
if (result)
|
|
{
|
|
acb_hypgeom_gamma_upper_asymp(res, s, z, regularized, prec);
|
|
return;
|
|
}
|
|
}
|
|
else if (acb_hypgeom_u_use_asymp(z, prec) &&
|
|
((0 < n && n < WORD_MAX) || _determine_region(s, z)))
|
|
{
|
|
acb_hypgeom_gamma_upper_asymp(res, s, z, regularized, prec);
|
|
return;
|
|
}
|
|
|
|
if (n <= 0 && n > -10 * prec)
|
|
{
|
|
acb_hypgeom_gamma_upper_singular(res, n, z, regularized, prec);
|
|
return;
|
|
}
|
|
|
|
if (arf_sgn(arb_midref(acb_realref(z))) > 0)
|
|
acb_hypgeom_gamma_upper_1f1b(res, s, z, regularized, prec);
|
|
else
|
|
acb_hypgeom_gamma_upper_1f1a(res, s, z, regularized, prec);
|
|
}
|
|
}
|
|
|
|
void
|
|
acb_hypgeom_gamma_upper(acb_t res, const acb_t s, const acb_t z, int regularized, slong prec)
|
|
{
|
|
acb_t res2;
|
|
slong acc, max, t;
|
|
|
|
acb_init(res2);
|
|
|
|
acb_hypgeom_gamma_upper_nointegration(res2, s, z, regularized, prec);
|
|
|
|
acc = acb_rel_accuracy_bits(res2);
|
|
|
|
if (acc < 0.5 * prec)
|
|
{
|
|
max = prec;
|
|
t = acb_rel_accuracy_bits(z);
|
|
max = FLINT_MIN(max, t);
|
|
t = acb_rel_accuracy_bits(s);
|
|
max = FLINT_MIN(max, t);
|
|
|
|
if (max > 2 && acc < 0.5 * max)
|
|
{
|
|
if (acb_is_real(s) && acb_is_real(z) &&
|
|
arf_cmp_2exp_si(arb_midref(acb_realref(z)), -16) > 0 &&
|
|
arf_cmpabs_2exp_si(arb_midref(acb_realref(s)), 60) < 0 &&
|
|
arf_cmpabs_2exp_si(arb_midref(acb_realref(z)), 60) < 0)
|
|
{
|
|
arb_hypgeom_gamma_upper_integration(acb_realref(res),
|
|
acb_realref(s), acb_realref(z), regularized, prec);
|
|
arb_zero(acb_imagref(res));
|
|
|
|
if (acb_rel_accuracy_bits(res) > acb_rel_accuracy_bits(res2) ||
|
|
(acb_is_finite(res) && !acb_is_finite(res2)))
|
|
{
|
|
acb_swap(res, res2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
acb_swap(res, res2);
|
|
acb_clear(res2);
|
|
}
|