diff options
Diffstat (limited to 'gsl-1.9/specfunc/poch.c')
-rw-r--r-- | gsl-1.9/specfunc/poch.c | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/gsl-1.9/specfunc/poch.c b/gsl-1.9/specfunc/poch.c new file mode 100644 index 0000000..782900e --- /dev/null +++ b/gsl-1.9/specfunc/poch.c @@ -0,0 +1,435 @@ +/* specfunc/poch.c + * + * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* Author: G. Jungman */ + +#include <config.h> +#include <gsl/gsl_math.h> +#include <gsl/gsl_errno.h> +#include <gsl/gsl_sf_exp.h> +#include <gsl/gsl_sf_log.h> +#include <gsl/gsl_sf_pow_int.h> +#include <gsl/gsl_sf_psi.h> +#include <gsl/gsl_sf_gamma.h> + +#include "error.h" + +static const double bern[21] = { + 0.0 /* no element 0 */, + +0.833333333333333333333333333333333e-01, + -0.138888888888888888888888888888888e-02, + +0.330687830687830687830687830687830e-04, + -0.826719576719576719576719576719576e-06, + +0.208767569878680989792100903212014e-07, + -0.528419013868749318484768220217955e-09, + +0.133825365306846788328269809751291e-10, + -0.338968029632258286683019539124944e-12, + +0.858606205627784456413590545042562e-14, + -0.217486869855806187304151642386591e-15, + +0.550900282836022951520265260890225e-17, + -0.139544646858125233407076862640635e-18, + +0.353470703962946747169322997780379e-20, + -0.895351742703754685040261131811274e-22, + +0.226795245233768306031095073886816e-23, + -0.574472439520264523834847971943400e-24, + +0.145517247561486490186626486727132e-26, + -0.368599494066531017818178247990866e-28, + +0.933673425709504467203255515278562e-30, + -0.236502241570062993455963519636983e-31 +}; + + +/* ((a)_x - 1)/x in the "small x" region where + * cancellation must be controlled. + * + * Based on SLATEC DPOCH1(). + */ +/* +C When ABS(X) is so small that substantial cancellation will occur if +C the straightforward formula is used, we use an expansion due +C to Fields and discussed by Y. L. Luke, The Special Functions and Their +C Approximations, Vol. 1, Academic Press, 1969, page 34. +C +C The ratio POCH(A,X) = GAMMA(A+X)/GAMMA(A) is written by Luke as +C (A+(X-1)/2)**X * polynomial in (A+(X-1)/2)**(-2) . +C In order to maintain significance in POCH1, we write for positive a +C (A+(X-1)/2)**X = EXP(X*LOG(A+(X-1)/2)) = EXP(Q) +C = 1.0 + Q*EXPREL(Q) . +C Likewise the polynomial is written +C POLY = 1.0 + X*POLY1(A,X) . +C Thus, +C POCH1(A,X) = (POCH(A,X) - 1) / X +C = EXPREL(Q)*(Q/X + Q*POLY1(A,X)) + POLY1(A,X) +C +*/ +static +int +pochrel_smallx(const double a, const double x, gsl_sf_result * result) +{ + /* + SQTBIG = 1.0D0/SQRT(24.0D0*D1MACH(1)) + ALNEPS = LOG(D1MACH(3)) + */ + const double SQTBIG = 1.0/(2.0*M_SQRT2*M_SQRT3*GSL_SQRT_DBL_MIN); + const double ALNEPS = GSL_LOG_DBL_EPSILON - M_LN2; + + if(x == 0.0) { + return gsl_sf_psi_e(a, result); + } + else { + const double bp = ( (a < -0.5) ? 1.0-a-x : a ); + const int incr = ( (bp < 10.0) ? 11.0-bp : 0 ); + const double b = bp + incr; + double dpoch1; + gsl_sf_result dexprl; + int stat_dexprl; + int i; + + double var = b + 0.5*(x-1.0); + double alnvar = log(var); + double q = x*alnvar; + + double poly1 = 0.0; + + if(var < SQTBIG) { + const int nterms = (int)(-0.5*ALNEPS/alnvar + 1.0); + const double var2 = (1.0/var)/var; + const double rho = 0.5 * (x + 1.0); + double term = var2; + double gbern[24]; + int k, j; + + gbern[1] = 1.0; + gbern[2] = -rho/12.0; + poly1 = gbern[2] * term; + + if(nterms > 20) { + /* NTERMS IS TOO BIG, MAYBE D1MACH(3) IS BAD */ + /* nterms = 20; */ + result->val = 0.0; + result->err = 0.0; + GSL_ERROR ("error", GSL_ESANITY); + } + + for(k=2; k<=nterms; k++) { + double gbk = 0.0; + for(j=1; j<=k; j++) { + gbk += bern[k-j+1]*gbern[j]; + } + gbern[k+1] = -rho*gbk/k; + + term *= (2*k-2-x)*(2*k-1-x)*var2; + poly1 += gbern[k+1]*term; + } + } + + stat_dexprl = gsl_sf_expm1_e(q, &dexprl); + if(stat_dexprl != GSL_SUCCESS) { + result->val = 0.0; + result->err = 0.0; + return stat_dexprl; + } + dexprl.val = dexprl.val/q; + poly1 *= (x - 1.0); + dpoch1 = dexprl.val * (alnvar + q * poly1) + poly1; + + for(i=incr-1; i >= 0; i--) { + /* + C WE HAVE DPOCH1(B,X), BUT BP IS SMALL, SO WE USE BACKWARDS RECURSION + C TO OBTAIN DPOCH1(BP,X). + */ + double binv = 1.0/(bp+i); + dpoch1 = (dpoch1 - binv) / (1.0 + x*binv); + } + + if(bp == a) { + result->val = dpoch1; + result->err = 2.0 * GSL_DBL_EPSILON * (fabs(incr) + 1.0) * fabs(result->val); + return GSL_SUCCESS; + } + else { + /* + C WE HAVE DPOCH1(BP,X), BUT A IS LT -0.5. WE THEREFORE USE A + C REFLECTION FORMULA TO OBTAIN DPOCH1(A,X). + */ + double sinpxx = sin(M_PI*x)/x; + double sinpx2 = sin(0.5*M_PI*x); + double t1 = sinpxx/tan(M_PI*b); + double t2 = 2.0*sinpx2*(sinpx2/x); + double trig = t1 - t2; + result->val = dpoch1 * (1.0 + x*trig) + trig; + result->err = (fabs(dpoch1*x) + 1.0) * GSL_DBL_EPSILON * (fabs(t1) + fabs(t2)); + result->err += 2.0 * GSL_DBL_EPSILON * (fabs(incr) + 1.0) * fabs(result->val); + return GSL_SUCCESS; + } + } +} + + +/* Assumes a>0 and a+x>0. + */ +static +int +lnpoch_pos(const double a, const double x, gsl_sf_result * result) +{ + double absx = fabs(x); + + if(absx > 0.1*a || absx*log(GSL_MAX_DBL(a,2.0)) > 0.1) { + if(a < GSL_SF_GAMMA_XMAX && a+x < GSL_SF_GAMMA_XMAX) { + /* If we can do it by calculating the gamma functions + * directly, then that will be more accurate than + * doing the subtraction of the logs. + */ + gsl_sf_result g1; + gsl_sf_result g2; + gsl_sf_gammainv_e(a, &g1); + gsl_sf_gammainv_e(a+x, &g2); + result->val = -log(g2.val/g1.val); + result->err = g1.err/fabs(g1.val) + g2.err/fabs(g2.val); + result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val); + return GSL_SUCCESS; + } + else { + /* Otherwise we must do the subtraction. + */ + gsl_sf_result lg1; + gsl_sf_result lg2; + int stat_1 = gsl_sf_lngamma_e(a, &lg1); + int stat_2 = gsl_sf_lngamma_e(a+x, &lg2); + result->val = lg2.val - lg1.val; + result->err = lg2.err + lg1.err; + result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val); + return GSL_ERROR_SELECT_2(stat_1, stat_2); + } + } + else if(absx < 0.1*a && a > 15.0) { + /* Be careful about the implied subtraction. + * Note that both a+x and and a must be + * large here since a is not small + * and x is not relatively large. + * So we calculate using Stirling for Log[Gamma(z)]. + * + * Log[Gamma(a+x)/Gamma(a)] = x(Log[a]-1) + (x+a-1/2)Log[1+x/a] + * + (1/(1+eps) - 1) / (12 a) + * - (1/(1+eps)^3 - 1) / (360 a^3) + * + (1/(1+eps)^5 - 1) / (1260 a^5) + * - (1/(1+eps)^7 - 1) / (1680 a^7) + * + ... + */ + const double eps = x/a; + const double den = 1.0 + eps; + const double d3 = den*den*den; + const double d5 = d3*den*den; + const double d7 = d5*den*den; + const double c1 = -eps/den; + const double c3 = -eps*(3.0+eps*(3.0+eps))/d3; + const double c5 = -eps*(5.0+eps*(10.0+eps*(10.0+eps*(5.0+eps))))/d5; + const double c7 = -eps*(7.0+eps*(21.0+eps*(35.0+eps*(35.0+eps*(21.0+eps*(7.0+eps))))))/d7; + const double p8 = gsl_sf_pow_int(1.0+eps,8); + const double c8 = 1.0/p8 - 1.0; /* these need not */ + const double c9 = 1.0/(p8*(1.0+eps)) - 1.0; /* be very accurate */ + const double a4 = a*a*a*a; + const double a6 = a4*a*a; + const double ser_1 = c1 + c3/(30.0*a*a) + c5/(105.0*a4) + c7/(140.0*a6); + const double ser_2 = c8/(99.0*a6*a*a) - 691.0/360360.0 * c9/(a6*a4); + const double ser = (ser_1 + ser_2)/ (12.0*a); + + double term1 = x * log(a/M_E); + double term2; + gsl_sf_result ln_1peps; + gsl_sf_log_1plusx_e(eps, &ln_1peps); /* log(1 + x/a) */ + term2 = (x + a - 0.5) * ln_1peps.val; + + result->val = term1 + term2 + ser; + result->err = GSL_DBL_EPSILON*fabs(term1); + result->err += fabs((x + a - 0.5)*ln_1peps.err); + result->err += fabs(ln_1peps.val) * GSL_DBL_EPSILON * (fabs(x) + fabs(a) + 0.5); + result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val); + return GSL_SUCCESS; + } + else { + gsl_sf_result poch_rel; + int stat_p = pochrel_smallx(a, x, &poch_rel); + double eps = x*poch_rel.val; + int stat_e = gsl_sf_log_1plusx_e(eps, result); + result->err = 2.0 * fabs(x * poch_rel.err / (1.0 + eps)); + result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val); + return GSL_ERROR_SELECT_2(stat_e, stat_p); + } +} + + +/*-*-*-*-*-*-*-*-*-*-*-* Functions with Error Codes *-*-*-*-*-*-*-*-*-*-*-*/ + +int +gsl_sf_lnpoch_e(const double a, const double x, gsl_sf_result * result) +{ + /* CHECK_POINTER(result) */ + + if(a <= 0.0 || a+x <= 0.0) { + DOMAIN_ERROR(result); + } + else if(x == 0.0) { + result->val = 0.0; + result->err = 0.0; + return GSL_SUCCESS; + } + else { + return lnpoch_pos(a, x, result); + } +} + + +int +gsl_sf_lnpoch_sgn_e(const double a, const double x, + gsl_sf_result * result, double * sgn) +{ + if(a == 0.0 || a+x == 0.0) { + *sgn = 0.0; + DOMAIN_ERROR(result); + } + else if(x == 0.0) { + *sgn = 1.0; + result->val = 0.0; + result->err = 0.0; + return GSL_SUCCESS; + } + else if(a > 0.0 && a+x > 0.0) { + *sgn = 1.0; + return lnpoch_pos(a, x, result); + } + else if(a < 0.0 && a+x < 0.0) { + /* Reduce to positive case using reflection. + */ + double sin_1 = sin(M_PI * (1.0 - a)); + double sin_2 = sin(M_PI * (1.0 - a - x)); + if(sin_1 == 0.0 || sin_2 == 0.0) { + *sgn = 0.0; + DOMAIN_ERROR(result); + } + else { + gsl_sf_result lnp_pos; + int stat_pp = lnpoch_pos(1.0-a, -x, &lnp_pos); + double lnterm = log(fabs(sin_1/sin_2)); + result->val = lnterm - lnp_pos.val; + result->err = lnp_pos.err; + result->err += 2.0 * GSL_DBL_EPSILON * (fabs(1.0-a) + fabs(1.0-a-x)) * fabs(lnterm); + result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val); + *sgn = GSL_SIGN(sin_1*sin_2); + return stat_pp; + } + } + else { + /* Evaluate gamma ratio directly. + */ + gsl_sf_result lg_apn; + gsl_sf_result lg_a; + double s_apn, s_a; + int stat_apn = gsl_sf_lngamma_sgn_e(a+x, &lg_apn, &s_apn); + int stat_a = gsl_sf_lngamma_sgn_e(a, &lg_a, &s_a); + if(stat_apn == GSL_SUCCESS && stat_a == GSL_SUCCESS) { + result->val = lg_apn.val - lg_a.val; + result->err = lg_apn.err + lg_a.err; + result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val); + *sgn = s_a * s_apn; + return GSL_SUCCESS; + } + else if(stat_apn == GSL_EDOM || stat_a == GSL_EDOM){ + *sgn = 0.0; + DOMAIN_ERROR(result); + } + else { + result->val = 0.0; + result->err = 0.0; + *sgn = 0.0; + return GSL_FAILURE; + } + } +} + + +int +gsl_sf_poch_e(const double a, const double x, gsl_sf_result * result) +{ + /* CHECK_POINTER(result) */ + + if(x == 0.0) { + result->val = 1.0; + result->err = 0.0; + return GSL_SUCCESS; + } + else { + gsl_sf_result lnpoch; + double sgn; + int stat_lnpoch = gsl_sf_lnpoch_sgn_e(a, x, &lnpoch, &sgn); + int stat_exp = gsl_sf_exp_err_e(lnpoch.val, lnpoch.err, result); + result->val *= sgn; + result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val); + return GSL_ERROR_SELECT_2(stat_exp, stat_lnpoch); + } +} + + +int +gsl_sf_pochrel_e(const double a, const double x, gsl_sf_result * result) +{ + const double absx = fabs(x); + const double absa = fabs(a); + + /* CHECK_POINTER(result) */ + + if(absx > 0.1*absa || absx*log(GSL_MAX(absa,2.0)) > 0.1) { + gsl_sf_result lnpoch; + double sgn; + int stat_poch = gsl_sf_lnpoch_sgn_e(a, x, &lnpoch, &sgn); + if(lnpoch.val > GSL_LOG_DBL_MAX) { + OVERFLOW_ERROR(result); + } + else { + const double el = exp(lnpoch.val); + result->val = (sgn*el - 1.0)/x; + result->err = fabs(result->val) * (lnpoch.err + 2.0 * GSL_DBL_EPSILON); + result->err += 2.0 * GSL_DBL_EPSILON * (fabs(sgn*el) + 1.0) / fabs(x); + return stat_poch; + } + } + else { + return pochrel_smallx(a, x, result); + } +} + + +/*-*-*-*-*-*-*-*-*-* Functions w/ Natural Prototypes *-*-*-*-*-*-*-*-*-*-*/ + +#include "eval.h" + +double gsl_sf_lnpoch(const double a, const double x) +{ + EVAL_RESULT(gsl_sf_lnpoch_e(a, x, &result)); +} + +double gsl_sf_poch(const double a, const double x) +{ + EVAL_RESULT(gsl_sf_poch_e(a, x, &result)); +} + +double gsl_sf_pochrel(const double a, const double x) +{ + EVAL_RESULT(gsl_sf_pochrel_e(a, x, &result)); +} |