/* * Mathlib : A C Library of Special Functions * Copyright (C) 1998 Ross Ihaka and the R Development Core Team * Copyright (C) 2000-2007 The R Development Core Team * based on AS243 (C) 1989 Royal Statistical Society * * 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, a copy is available at * http://www.r-project.org/Licenses/ */ /* Algorithm AS 243 Lenth,R.V. (1989). Appl. Statist., Vol.38, 185-189. * ---------------- * Cumulative probability at t of the non-central t-distribution * with df degrees of freedom (may be fractional) and non-centrality * parameter delta. * * NOTE * * Requires the following auxiliary routines: * * lgammafn(x) - log gamma function * pbeta(x, a, b) - incomplete beta function * pnorm(x) - normal distribution function * * CONSTANTS * * M_SQRT_2dPI = 1/ {gamma(1.5) * sqrt(2)} = sqrt(2 / pi) * M_LN_SQRT_PI = ln(sqrt(pi)) = ln(pi)/2 */ #include "nmath.h" #include "dpq.h" /*----------- DEBUGGING ------------- * * make CFLAGS='-DDEBUG_pnt -g -I/usr/local/include -I../include' * -- Feb.3, 1999; M.Maechler: - For 't > delta > 20' (or so) the result is completely WRONG! */ double pnt(double t, double df, double delta, int lower_tail, int log_p) { double albeta, b, del, errbd, lambda, rxb, tt, x; LDOUBLE a, geven, godd, p, q, s, tnc, xeven, xodd; int it, negdel; /* note - itrmax and errmax may be changed to suit one's needs. */ const int itrmax = 1000; const static double errmax = 1.e-12; if (df <= 0.0) ML_ERR_return_NAN; if(delta == 0.0) return pt(t, df, lower_tail, log_p); if(!R_FINITE(t)) return (t < 0) ? R_DT_0 : R_DT_1; if (t >= 0.) { negdel = FALSE; tt = t; del = delta; } else { negdel = TRUE; tt = -t; del = -delta; } if (df > 4e5 || del*del > 2*M_LN2*(-(DBL_MIN_EXP))) { /*-- 2nd part: if del > 37.62, then p=0 below FIXME: test should depend on `df', `tt' AND `del' ! */ /* Approx. from Abramowitz & Stegun 26.7.10 (p.949) */ s = 1./(4.*df); return pnorm(tt*(1. - s), del, sqrt(1. + tt*tt*2.*s), lower_tail != negdel, log_p); } /* initialize twin series */ /* Guenther, J. (1978). Statist. Computn. Simuln. vol.6, 199. */ x = t * t; x = x / (x + df);/* in [0,1) */ #ifdef DEBUG_pnt REprintf("pnt(t=%7g, df=%7g, delta=%7g) ==> x= %10g:",t,df,delta, x); #endif if (x > 0.) {/* <==> t != 0 */ lambda = del * del; p = .5 * exp(-.5 * lambda); #ifdef DEBUG_pnt REprintf("\t p=%10g\n",p); #endif if(p == 0.) { /* underflow! */ /*========== really use an other algorithm for this case !!! */ ML_ERROR(ME_UNDERFLOW, "pnt"); ML_ERROR(ME_RANGE, "pnt"); /* |delta| too large */ return R_DT_0; } #ifdef DEBUG_pnt REprintf("it 1e5*(godd, geven) p q s " /* 1.3 1..4..7.9 1..4..7.9 1..4..7.9 1..4..7.9 1..4..7.9_ */ " pnt(*) errbd\n"); /* 1..4..7..0..3..6 1..4..7.9*/ #endif q = M_SQRT_2dPI * p * del; s = .5 - p; a = .5; b = .5 * df; rxb = pow(1. - x, b); /* ~ 1 - b*x for tiny x */ albeta = M_LN_SQRT_PI + lgammafn(b) - lgammafn(.5 + b); xodd = pbeta(x, a, b, /*lower*/TRUE, /*log_p*/FALSE); godd = 2. * rxb * exp(a * log(x) - albeta); tnc = b * x; xeven = (tnc < DBL_EPSILON) ? tnc : 1. - rxb; geven = tnc * rxb; tnc = p * xodd + q * xeven; /* repeat until convergence or iteration limit */ for(it = 1; it <= itrmax; it++) { a += 1.; xodd -= godd; xeven -= geven; godd *= x * (a + b - 1.) / a; geven *= x * (a + b - .5) / (a + .5); p *= lambda / (2 * it); q *= lambda / (2 * it + 1); tnc += p * xodd + q * xeven; s -= p; /* R 2.4.0 added test for rounding error here. */ if(s < -1.e-10) { /* happens e.g. for (t,df,delta)=(40,10,38.5), after 799 it.*/ ML_ERROR(ME_PRECISION, "pnt"); #ifdef DEBUG_pnt REprintf("s = %#14.7g < 0 !!! ---> non-convergence!!\n", s); #endif goto finis; } if(s <= 0) goto finis; errbd = 2. * s * (xodd - godd); #ifdef DEBUG_pnt REprintf("%3d %#9.4g %#9.4g %#9.4g %#9.4g %#9.4g %#14.10g %#9.4g\n", it, 1e5*godd, 1e5*geven, p,q, s, tnc, errbd); #endif if(errbd < errmax) goto finis;/*convergence*/ } /* non-convergence:*/ ML_ERROR(ME_NOCONV, "pnt"); } else { /* x = t = 0 */ tnc = 0.; } finis: tnc += pnorm(- del, 0., 1., /*lower*/TRUE, /*log_p*/FALSE); lower_tail = lower_tail != negdel; /* xor */ /* return R_DT_val(tnc); We want to warn about cancellation here */ if(lower_tail) return log_p ? log(tnc) : tnc; else { if(tnc > 1 - 1e-10) ML_ERROR(ME_PRECISION, "pnt"); tnc = fmin2(tnc, 1.0); /* Precaution */ return log_p ? log1p(-tnc) : (0.5 - tnc + 0.5); } }