From fb6e85669cc76104aae26db530c03f0bffab9ae7 Mon Sep 17 00:00:00 2001 From: Fredrik Johansson Date: Fri, 22 Mar 2013 23:48:18 +0100 Subject: [PATCH] fast gamma for rational and integer arguments --- doc/source/fmprb.rst | 2 ++ fmprb.h | 1 + fmprb/gamma.c | 42 ++++++++++++++++++++++++++ fmprb/gamma_fmpq.c | 70 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+) create mode 100644 fmprb/gamma_fmpq.c diff --git a/doc/source/fmprb.rst b/doc/source/fmprb.rst index acd19c54..fa8161e7 100644 --- a/doc/source/fmprb.rst +++ b/doc/source/fmprb.rst @@ -698,6 +698,8 @@ Gamma function .. function:: void fmprb_gamma(fmprb_t y, const fmprb_t x, long prec) +.. function:: void fmprb_gamma_fmpq(fmprb_t y, const fmpq_t x, long prec) + Sets `y = \Gamma(x)`, the gamma function. .. function:: void fmprb_rgamma(fmprb_t y, const fmprb_t x, long prec) diff --git a/fmprb.h b/fmprb.h index fbf3f164..ff1c5e72 100644 --- a/fmprb.h +++ b/fmprb.h @@ -321,6 +321,7 @@ void fmprb_agm(fmprb_t z, const fmprb_t x, const fmprb_t y, long prec); void fmprb_lgamma(fmprb_t y, const fmprb_t x, long prec); void fmprb_rgamma(fmprb_t y, const fmprb_t x, long prec); void fmprb_gamma(fmprb_t y, const fmprb_t x, long prec); +void fmprb_gamma_fmpq(fmprb_t y, const fmpq_t x, long prec); void fmprb_digamma(fmprb_t y, const fmprb_t x, long prec); diff --git a/fmprb/gamma.c b/fmprb/gamma.c index 4a24e418..27527a1a 100644 --- a/fmprb/gamma.c +++ b/fmprb/gamma.c @@ -33,6 +33,48 @@ _fmprb_gamma(fmprb_t y, const fmprb_t x, long prec, int inverse) long r, n, wp; fmprb_t t, u, v; + if (fmprb_is_exact(x)) + { + const fmpr_struct * mid = fmprb_midref(x); + + if (fmpr_is_special(mid)) + { + if (!inverse && fmpr_is_pos_inf(mid)) + { + fmprb_set(y, x); + } + else if (fmpr_is_nan(mid) || fmpr_is_neg_inf(mid) || !inverse) + { + fmpr_nan(fmprb_midref(y)); + fmpr_pos_inf(fmprb_radref(y)); + } + else + { + fmprb_zero(y); + } + return; + } + else + { + const fmpz exp = *fmpr_expref(mid); + const fmpz man = *fmpr_manref(mid); + + /* fast gamma(n), gamma(n/2) or gamma(n/4) */ + if (!COEFF_IS_MPZ(exp) && (exp >= -2) && + ((double) fmpz_bits(&man) + exp < prec)) + { + fmpq_t a; + fmpq_init(a); + fmpr_get_fmpq(a, mid); + fmprb_gamma_fmpq(y, a, prec + 2 * inverse); + if (inverse) + fmprb_ui_div(y, 1, y, prec); + fmpq_clear(a); + return; + } + } + } + wp = prec + FLINT_BIT_COUNT(prec); gamma_stirling_choose_param_fmprb(&reflect, &r, &n, x, 1, 0, wp); diff --git a/fmprb/gamma_fmpq.c b/fmprb/gamma_fmpq.c new file mode 100644 index 00000000..286d05ca --- /dev/null +++ b/fmprb/gamma_fmpq.c @@ -0,0 +1,70 @@ +/*============================================================================= + + This file is part of ARB. + + ARB is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + ARB is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ARB; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +=============================================================================*/ +/****************************************************************************** + + Copyright (C) 2013 Fredrik Johansson + +******************************************************************************/ + +#include "fmprb.h" +#include "gamma.h" + +void +fmprb_gamma_fmpq(fmprb_t y, const fmpq_t x, long prec) +{ + fmpz p, q; + + p = *fmpq_numref(x); + q = *fmpq_denref(x); + + if ((q == 1 || q == 2 || q == 3 || q == 4 || q == 6) && !COEFF_IS_MPZ(p)) + { + if (q == 1) + { + if (p <= 0) + { + fmpr_nan(fmprb_midref(y)); + fmpr_pos_inf(fmprb_radref(y)); + return; + } + + if (p < 1200 || 1.44265 * (p*log(p) - p) < 15.0 * prec) + { + fmpz_t t; + fmpz_init(t); + fmpz_fac_ui(t, p - 1); + fmprb_set_round_fmpz(y, t, prec); + fmpz_clear(t); + return; + } + } + + p = FLINT_ABS(p); + + if (p < q * 500.0 || p < q * (500.0 + 0.1 * prec * sqrt(prec))) + { + gamma_fmpq_outward(y, x, prec); + return; + } + } + + gamma_fmpq_stirling(y, x, prec); +} +