| 1 |
/** |
|---|
| 2 |
* Implementation of the gamma and beta functions, and their integrals. |
|---|
| 3 |
* |
|---|
| 4 |
* License: BSD style: $(LICENSE) |
|---|
| 5 |
* Copyright: Based on the CEPHES math library, which is |
|---|
| 6 |
* Copyright (C) 1994 Stephen L. Moshier (moshier@world.std.com). |
|---|
| 7 |
* Authors: Stephen L. Moshier (original C code). Conversion to D by Don Clugston |
|---|
| 8 |
* |
|---|
| 9 |
* |
|---|
| 10 |
Macros: |
|---|
| 11 |
* TABLE_SV = <table border=1 cellpadding=4 cellspacing=0> |
|---|
| 12 |
* <caption>Special Values</caption> |
|---|
| 13 |
* $0</table> |
|---|
| 14 |
* SVH = $(TR $(TH $1) $(TH $2)) |
|---|
| 15 |
* SV = $(TR $(TD $1) $(TD $2)) |
|---|
| 16 |
* GAMMA = Γ |
|---|
| 17 |
* INTEGRATE = $(BIG ∫<sub>$(SMALL $1)</sub><sup>$2</sup>) |
|---|
| 18 |
* POWER = $1<sup>$2</sup> |
|---|
| 19 |
* NAN = $(RED NAN) |
|---|
| 20 |
*/ |
|---|
| 21 |
module tango.math.GammaFunction; |
|---|
| 22 |
private import tango.math.Math; |
|---|
| 23 |
private import tango.math.IEEE; |
|---|
| 24 |
private import tango.math.ErrorFunction; |
|---|
| 25 |
|
|---|
| 26 |
version(Windows) { // Some tests only pass on DMD Windows |
|---|
| 27 |
version(DigitalMars) { |
|---|
| 28 |
version = FailsOnLinux; |
|---|
| 29 |
} |
|---|
| 30 |
} |
|---|
| 31 |
|
|---|
| 32 |
//------------------------------------------------------------------ |
|---|
| 33 |
|
|---|
| 34 |
/// The maximum value of x for which gamma(x) < real.infinity. |
|---|
| 35 |
const real MAXGAMMA = 1755.5483429L; |
|---|
| 36 |
|
|---|
| 37 |
private { |
|---|
| 38 |
|
|---|
| 39 |
const real SQRT2PI = 2.50662827463100050242E0L; // sqrt(2pi) |
|---|
| 40 |
|
|---|
| 41 |
// Polynomial approximations for gamma and loggamma. |
|---|
| 42 |
|
|---|
| 43 |
const real GammaNumeratorCoeffs[] = [ 1.0, |
|---|
| 44 |
0x1.acf42d903366539ep-1, 0x1.73a991c8475f1aeap-2, 0x1.c7e918751d6b2a92p-4, |
|---|
| 45 |
0x1.86d162cca32cfe86p-6, 0x1.0c378e2e6eaf7cd8p-8, 0x1.dc5c66b7d05feb54p-12, |
|---|
| 46 |
0x1.616457b47e448694p-15 |
|---|
| 47 |
]; |
|---|
| 48 |
|
|---|
| 49 |
const real GammaDenominatorCoeffs[] = [ 1.0, |
|---|
| 50 |
0x1.a8f9faae5d8fc8bp-2, -0x1.cb7895a6756eebdep-3, -0x1.7b9bab006d30652ap-5, |
|---|
| 51 |
0x1.c671af78f312082ep-6, -0x1.a11ebbfaf96252dcp-11, -0x1.447b4d2230a77ddap-10, |
|---|
| 52 |
0x1.ec1d45bb85e06696p-13,-0x1.d4ce24d05bd0a8e6p-17 |
|---|
| 53 |
]; |
|---|
| 54 |
|
|---|
| 55 |
const real GammaSmallCoeffs[] = [ 1.0, |
|---|
| 56 |
0x1.2788cfc6fb618f52p-1, -0x1.4fcf4026afa2f7ecp-1, -0x1.5815e8fa24d7e306p-5, |
|---|
| 57 |
0x1.5512320aea2ad71ap-3, -0x1.59af0fb9d82e216p-5, -0x1.3b4b61d3bfdf244ap-7, |
|---|
| 58 |
0x1.d9358e9d9d69fd34p-8, -0x1.38fc4bcbada775d6p-10 |
|---|
| 59 |
]; |
|---|
| 60 |
|
|---|
| 61 |
const real GammaSmallNegCoeffs[] = [ -1.0, |
|---|
| 62 |
0x1.2788cfc6fb618f54p-1, 0x1.4fcf4026afa2bc4cp-1, -0x1.5815e8fa2468fec8p-5, |
|---|
| 63 |
-0x1.5512320baedaf4b6p-3, -0x1.59af0fa283baf07ep-5, 0x1.3b4a70de31e05942p-7, |
|---|
| 64 |
0x1.d9398be3bad13136p-8, 0x1.291b73ee05bcbba2p-10 |
|---|
| 65 |
]; |
|---|
| 66 |
|
|---|
| 67 |
const real logGammaStirlingCoeffs[] = [ |
|---|
| 68 |
0x1.5555555555553f98p-4, -0x1.6c16c16c07509b1p-9, 0x1.a01a012461cbf1e4p-11, |
|---|
| 69 |
-0x1.3813089d3f9d164p-11, 0x1.b911a92555a277b8p-11, -0x1.ed0a7b4206087b22p-10, |
|---|
| 70 |
0x1.402523859811b308p-8 |
|---|
| 71 |
]; |
|---|
| 72 |
|
|---|
| 73 |
const real logGammaNumerator[] = [ |
|---|
| 74 |
-0x1.0edd25913aaa40a2p+23, -0x1.31c6ce2e58842d1ep+24, -0x1.f015814039477c3p+23, |
|---|
| 75 |
-0x1.74ffe40c4b184b34p+22, -0x1.0d9c6d08f9eab55p+20, -0x1.54c6b71935f1fc88p+16, |
|---|
| 76 |
-0x1.0e761b42932b2aaep+11 |
|---|
| 77 |
]; |
|---|
| 78 |
|
|---|
| 79 |
const real logGammaDenominator[] = [ |
|---|
| 80 |
-0x1.4055572d75d08c56p+24, -0x1.deeb6013998e4d76p+24, -0x1.106f7cded5dcc79ep+24, |
|---|
| 81 |
-0x1.25e17184848c66d2p+22, -0x1.301303b99a614a0ap+19, -0x1.09e76ab41ae965p+15, |
|---|
| 82 |
-0x1.00f95ced9e5f54eep+9, 1.0 |
|---|
| 83 |
]; |
|---|
| 84 |
|
|---|
| 85 |
/* |
|---|
| 86 |
* Helper function: Gamma function computed by Stirling's formula. |
|---|
| 87 |
* |
|---|
| 88 |
* Stirling's formula for the gamma function is: |
|---|
| 89 |
* |
|---|
| 90 |
* $(GAMMA)(x) = sqrt(2 π) x<sup>x-0.5</sup> exp(-x) (1 + 1/x P(1/x)) |
|---|
| 91 |
* |
|---|
| 92 |
*/ |
|---|
| 93 |
real gammaStirling(real x) |
|---|
| 94 |
{ |
|---|
| 95 |
// CEPHES code Copyright 1994 by Stephen L. Moshier |
|---|
| 96 |
|
|---|
| 97 |
const real SmallStirlingCoeffs[] = [ |
|---|
| 98 |
0x1.55555555555543aap-4, 0x1.c71c71c720dd8792p-9, -0x1.5f7268f0b5907438p-9, |
|---|
| 99 |
-0x1.e13cd410e0477de6p-13, 0x1.9b0f31643442616ep-11, 0x1.2527623a3472ae08p-14, |
|---|
| 100 |
-0x1.37f6bc8ef8b374dep-11,-0x1.8c968886052b872ap-16, 0x1.76baa9c6d3eeddbcp-11 |
|---|
| 101 |
]; |
|---|
| 102 |
|
|---|
| 103 |
const real LargeStirlingCoeffs[] = [ 1.0L, |
|---|
| 104 |
8.33333333333333333333E-2L, 3.47222222222222222222E-3L, |
|---|
| 105 |
-2.68132716049382716049E-3L, -2.29472093621399176955E-4L, |
|---|
| 106 |
7.84039221720066627474E-4L, 6.97281375836585777429E-5L |
|---|
| 107 |
]; |
|---|
| 108 |
|
|---|
| 109 |
real w = 1.0L/x; |
|---|
| 110 |
real y = exp(x); |
|---|
| 111 |
if ( x > 1024.0L ) { |
|---|
| 112 |
// For large x, use rational coefficients from the analytical expansion. |
|---|
| 113 |
w = poly(w, LargeStirlingCoeffs); |
|---|
| 114 |
// Avoid overflow in pow() |
|---|
| 115 |
real v = pow( x, 0.5L * x - 0.25L ); |
|---|
| 116 |
y = v * (v / y); |
|---|
| 117 |
} |
|---|
| 118 |
else { |
|---|
| 119 |
w = 1.0L + w * poly( w, SmallStirlingCoeffs); |
|---|
| 120 |
y = pow( x, x - 0.5L ) / y; |
|---|
| 121 |
} |
|---|
| 122 |
y = SQRT2PI * y * w; |
|---|
| 123 |
return y; |
|---|
| 124 |
} |
|---|
| 125 |
|
|---|
| 126 |
} // private |
|---|
| 127 |
|
|---|
| 128 |
/**************** |
|---|
| 129 |
* The sign of $(GAMMA)(x). |
|---|
| 130 |
* |
|---|
| 131 |
* Returns -1 if $(GAMMA)(x) < 0, +1 if $(GAMMA)(x) > 0, |
|---|
| 132 |
* $(NAN) if sign is indeterminate. |
|---|
| 133 |
*/ |
|---|
| 134 |
real sgnGamma(real x) |
|---|
| 135 |
{ |
|---|
| 136 |
/* Author: Don Clugston. */ |
|---|
| 137 |
if (isNaN(x)) return x; |
|---|
| 138 |
if (x > 0) return 1.0; |
|---|
| 139 |
if (x < -1/real.epsilon) { |
|---|
| 140 |
// Large negatives lose all precision |
|---|
| 141 |
return NaN(TANGO_NAN.SGNGAMMA); |
|---|
| 142 |
} |
|---|
| 143 |
// if (remquo(x, -1.0, n) == 0) { |
|---|
| 144 |
long n = rndlong(x); |
|---|
| 145 |
if (x == n) { |
|---|
| 146 |
return x == 0 ? copysign(1, x) : NaN(TANGO_NAN.SGNGAMMA); |
|---|
| 147 |
} |
|---|
| 148 |
return n & 1 ? 1.0 : -1.0; |
|---|
| 149 |
} |
|---|
| 150 |
|
|---|
| 151 |
debug(UnitTest) { |
|---|
| 152 |
unittest { |
|---|
| 153 |
assert(sgnGamma(5.0) == 1.0); |
|---|
| 154 |
assert(isNaN(sgnGamma(-3.0))); |
|---|
| 155 |
assert(sgnGamma(-0.1) == -1.0); |
|---|
| 156 |
assert(sgnGamma(-55.1) == 1.0); |
|---|
| 157 |
assert(isNaN(sgnGamma(-real.infinity))); |
|---|
| 158 |
assert(isIdentical(sgnGamma(NaN(0xABC)), NaN(0xABC))); |
|---|
| 159 |
} |
|---|
| 160 |
} |
|---|
| 161 |
|
|---|
| 162 |
/***************************************************** |
|---|
| 163 |
* The Gamma function, $(GAMMA)(x) |
|---|
| 164 |
* |
|---|
| 165 |
* $(GAMMA)(x) is a generalisation of the factorial function |
|---|
| 166 |
* to real and complex numbers. |
|---|
| 167 |
* Like x!, $(GAMMA)(x+1) = x*$(GAMMA)(x). |
|---|
| 168 |
* |
|---|
| 169 |
* Mathematically, if z.re > 0 then |
|---|
| 170 |
* $(GAMMA)(z) = $(INTEGRATE 0, ∞) $(POWER t, z-1)$(POWER e, -t) dt |
|---|
| 171 |
* |
|---|
| 172 |
* $(TABLE_SV |
|---|
| 173 |
* $(SVH x, $(GAMMA)(x) ) |
|---|
| 174 |
* $(SV $(NAN), $(NAN) ) |
|---|
| 175 |
* $(SV ±0.0, ±∞) |
|---|
| 176 |
* $(SV integer > 0, (x-1)! ) |
|---|
| 177 |
* $(SV integer < 0, $(NAN) ) |
|---|
| 178 |
* $(SV +∞, +∞ ) |
|---|
| 179 |
* $(SV -∞, $(NAN) ) |
|---|
| 180 |
* ) |
|---|
| 181 |
*/ |
|---|
| 182 |
real gamma(real x) |
|---|
| 183 |
{ |
|---|
| 184 |
/* Based on code from the CEPHES library. |
|---|
| 185 |
* CEPHES code Copyright 1994 by Stephen L. Moshier |
|---|
| 186 |
* |
|---|
| 187 |
* Arguments |x| <= 13 are reduced by recurrence and the function |
|---|
| 188 |
* approximated by a rational function of degree 7/8 in the |
|---|
| 189 |
* interval (2,3). Large arguments are handled by Stirling's |
|---|
| 190 |
* formula. Large negative arguments are made positive using |
|---|
| 191 |
* a reflection formula. |
|---|
| 192 |
*/ |
|---|
| 193 |
|
|---|
| 194 |
real q, z; |
|---|
| 195 |
if (isNaN(x)) return x; |
|---|
| 196 |
if (x == -x.infinity) return NaN(TANGO_NAN.GAMMA_DOMAIN); |
|---|
| 197 |
if ( fabs(x) > MAXGAMMA ) return real.infinity; |
|---|
| 198 |
if (x==0) return 1.0/x; // +- infinity depending on sign of x, create an exception. |
|---|
| 199 |
|
|---|
| 200 |
q = fabs(x); |
|---|
| 201 |
|
|---|
| 202 |
if ( q > 13.0L ) { |
|---|
| 203 |
// Large arguments are handled by Stirling's |
|---|
| 204 |
// formula. Large negative arguments are made positive using |
|---|
| 205 |
// the reflection formula. |
|---|
| 206 |
|
|---|
| 207 |
if ( x < 0.0L ) { |
|---|
| 208 |
int sgngam = 1; // sign of gamma. |
|---|
| 209 |
real p = floor(q); |
|---|
| 210 |
if (p == q) |
|---|
| 211 |
return NaN(TANGO_NAN.GAMMA_DOMAIN); // poles for all integers <0. |
|---|
| 212 |
int intpart = cast(int)(p); |
|---|
| 213 |
if ( (intpart & 1) == 0 ) |
|---|
| 214 |
sgngam = -1; |
|---|
| 215 |
z = q - p; |
|---|
| 216 |
if ( z > 0.5L ) { |
|---|
| 217 |
p += 1.0L; |
|---|
| 218 |
z = q - p; |
|---|
| 219 |
} |
|---|
| 220 |
z = q * sin( PI * z ); |
|---|
| 221 |
z = fabs(z) * gammaStirling(q); |
|---|
| 222 |
if ( z <= PI/real.max ) return sgngam * real.infinity; |
|---|
| 223 |
return sgngam * PI/z; |
|---|
| 224 |
} else { |
|---|
| 225 |
return gammaStirling(x); |
|---|
| 226 |
} |
|---|
| 227 |
} |
|---|
| 228 |
|
|---|
| 229 |
// Arguments |x| <= 13 are reduced by recurrence and the function |
|---|
| 230 |
// approximated by a rational function of degree 7/8 in the |
|---|
| 231 |
// interval (2,3). |
|---|
| 232 |
|
|---|
| 233 |
z = 1.0L; |
|---|
| 234 |
while ( x >= 3.0L ) { |
|---|
| 235 |
x -= 1.0L; |
|---|
| 236 |
z *= x; |
|---|
| 237 |
} |
|---|
| 238 |
|
|---|
| 239 |
while ( x < -0.03125L ) { |
|---|
| 240 |
z /= x; |
|---|
| 241 |
x += 1.0L; |
|---|
| 242 |
} |
|---|
| 243 |
|
|---|
| 244 |
if ( x <= 0.03125L ) { |
|---|
| 245 |
if ( x == 0.0L ) |
|---|
| 246 |
return NaN(TANGO_NAN.GAMMA_POLE); |
|---|
| 247 |
else { |
|---|
| 248 |
if ( x < 0.0L ) { |
|---|
| 249 |
x = -x; |
|---|
| 250 |
return z / (x * poly( x, GammaSmallNegCoeffs )); |
|---|
| 251 |
} else { |
|---|
| 252 |
return z / (x * poly( x, GammaSmallCoeffs )); |
|---|
| 253 |
} |
|---|
| 254 |
} |
|---|
| 255 |
} |
|---|
| 256 |
|
|---|
| 257 |
while ( x < 2.0L ) { |
|---|
| 258 |
z /= x; |
|---|
| 259 |
x += 1.0L; |
|---|
| 260 |
} |
|---|
| 261 |
if ( x == 2.0L ) return z; |
|---|
| 262 |
|
|---|
| 263 |
x -= 2.0L; |
|---|
| 264 |
return z * poly( x, GammaNumeratorCoeffs ) / poly( x, GammaDenominatorCoeffs ); |
|---|
| 265 |
} |
|---|
| 266 |
|
|---|
| 267 |
debug(UnitTest) { |
|---|
| 268 |
unittest { |
|---|
| 269 |
// gamma(n) = factorial(n-1) if n is an integer. |
|---|
| 270 |
real fact = 1.0L; |
|---|
| 271 |
for (int i=1; fact<real.max; ++i) { |
|---|
| 272 |
// Require exact equality for small factorials |
|---|
| 273 |
if (i<14) assert(gamma(i*1.0L) == fact); |
|---|
| 274 |
version(FailsOnLinux) assert(feqrel(gamma(i*1.0L), fact) > real.mant_dig-15); |
|---|
| 275 |
fact *= (i*1.0L); |
|---|
| 276 |
} |
|---|
| 277 |
assert(gamma(0.0) == real.infinity); |
|---|
| 278 |
assert(gamma(-0.0) == -real.infinity); |
|---|
| 279 |
assert(isNaN(gamma(-1.0))); |
|---|
| 280 |
assert(isNaN(gamma(-15.0))); |
|---|
| 281 |
assert(isIdentical(gamma(NaN(0xABC)), NaN(0xABC))); |
|---|
| 282 |
assert(gamma(real.infinity) == real.infinity); |
|---|
| 283 |
assert(gamma(real.max) == real.infinity); |
|---|
| 284 |
assert(isNaN(gamma(-real.infinity))); |
|---|
| 285 |
assert(gamma(real.min*real.epsilon) == real.infinity); |
|---|
| 286 |
assert(gamma(MAXGAMMA)< real.infinity); |
|---|
| 287 |
assert(gamma(MAXGAMMA*2) == real.infinity); |
|---|
| 288 |
|
|---|
| 289 |
// Test some high-precision values (50 decimal digits) |
|---|
| 290 |
const real SQRT_PI = 1.77245385090551602729816748334114518279754945612238L; |
|---|
| 291 |
|
|---|
| 292 |
version(FailsOnLinux) assert(feqrel(gamma(0.5L), SQRT_PI) == real.mant_dig); |
|---|
| 293 |
|
|---|
| 294 |
assert(feqrel(gamma(1.0/3.L), 2.67893853470774763365569294097467764412868937795730L) >= real.mant_dig-2); |
|---|
| 295 |
assert(feqrel(gamma(0.25L), |
|---|
| 296 |
3.62560990822190831193068515586767200299516768288006L) >= real.mant_dig-1); |
|---|
| 297 |
assert(feqrel(gamma(1.0/5.0L), |
|---|
| 298 |
4.59084371199880305320475827592915200343410999829340L) >= real.mant_dig-1); |
|---|
| 299 |
} |
|---|
| 300 |
} |
|---|
| 301 |
|
|---|
| 302 |
/***************************************************** |
|---|
| 303 |
* Natural logarithm of gamma function. |
|---|
| 304 |
* |
|---|
| 305 |
* Returns the base e (2.718...) logarithm of the absolute |
|---|
| 306 |
* value of the gamma function of the argument. |
|---|
| 307 |
* |
|---|
| 308 |
* For reals, logGamma is equivalent to log(fabs(gamma(x))). |
|---|
| 309 |
* |
|---|
| 310 |
* $(TABLE_SV |
|---|
| 311 |
* $(SVH x, logGamma(x) ) |
|---|
| 312 |
* $(SV $(NAN), $(NAN) ) |
|---|
| 313 |
* $(SV integer <= 0, +∞ ) |
|---|
| 314 |
* $(SV ±∞, +∞ ) |
|---|
| 315 |
* ) |
|---|
| 316 |
*/ |
|---|
| 317 |
real logGamma(real x) |
|---|
| 318 |
{ |
|---|
| 319 |
/* Based on code from the CEPHES library. |
|---|
| 320 |
* CEPHES code Copyright 1994 by Stephen L. Moshier |
|---|
| 321 |
* |
|---|
| 322 |
* For arguments greater than 33, the logarithm of the gamma |
|---|
| 323 |
* function is approximated by the logarithmic version of |
|---|
| 324 |
* Stirling's formula using a polynomial approximation of |
|---|
| 325 |
* degree 4. Arguments between -33 and +33 are reduced by |
|---|
| 326 |
* recurrence to the interval [2,3] of a rational approximation. |
|---|
| 327 |
* The cosecant reflection formula is employed for arguments |
|---|
| 328 |
* less than -33. |
|---|
| 329 |
*/ |
|---|
| 330 |
real q, w, z, f, nx; |
|---|
| 331 |
|
|---|
| 332 |
if (isNaN(x)) return x; |
|---|
| 333 |
if (fabs(x) == x.infinity) return x.infinity; |
|---|
| 334 |
|
|---|
| 335 |
if( x < -34.0L ) { |
|---|
| 336 |
q = -x; |
|---|
| 337 |
w = logGamma(q); |
|---|
| 338 |
real p = floor(q); |
|---|
| 339 |
if ( p == q ) return real.infinity; |
|---|
| 340 |
int intpart = cast(int)(p); |
|---|
| 341 |
real sgngam = 1; |
|---|
| 342 |
if ( (intpart & 1) == 0 ) |
|---|
| 343 |
sgngam = -1; |
|---|
| 344 |
z = q - p; |
|---|
| 345 |
if ( z > 0.5L ) { |
|---|
| 346 |
p += 1.0L; |
|---|
| 347 |
z = p - q; |
|---|
| 348 |
} |
|---|
| 349 |
z = q * sin( PI * z ); |
|---|
| 350 |
if ( z == 0.0L ) return sgngam * real.infinity; |
|---|
| 351 |
/* z = LOGPI - logl( z ) - w; */ |
|---|
| 352 |
z = log( PI/z ) - w; |
|---|
| 353 |
return z; |
|---|
| 354 |
} |
|---|
| 355 |
|
|---|
| 356 |
if( x < 13.0L ) { |
|---|
| 357 |
z = 1.0L; |
|---|
| 358 |
nx = floor( x + 0.5L ); |
|---|
| 359 |
f = x - nx; |
|---|
| 360 |
while ( x >= 3.0L ) { |
|---|
| 361 |
nx -= 1.0L; |
|---|
| 362 |
x = nx + f; |
|---|
| 363 |
z *= x; |
|---|
| 364 |
} |
|---|
| 365 |
while ( x < 2.0L ) { |
|---|
| 366 |
if( fabs(x) <= 0.03125 ) { |
|---|
| 367 |
if ( x == 0.0L ) return real.infinity; |
|---|
| 368 |
if ( x < 0.0L ) { |
|---|
| 369 |
x = -x; |
|---|
| 370 |
q = z / (x * poly( x, GammaSmallNegCoeffs)); |
|---|
| 371 |
} else |
|---|
| 372 |
q = z / (x * poly( x, GammaSmallCoeffs)); |
|---|
| 373 |
return log( fabs(q) ); |
|---|
| 374 |
} |
|---|
| 375 |
z /= nx + f; |
|---|
| 376 |
nx += 1.0L; |
|---|
| 377 |
x = nx + f; |
|---|
| 378 |
} |
|---|
| 379 |
z = fabs(z); |
|---|
| 380 |
if ( x == 2.0L ) |
|---|
| 381 |
return log(z); |
|---|
| 382 |
x = (nx - 2.0L) + f; |
|---|
| 383 |
real p = x * rationalPoly( x, logGammaNumerator, logGammaDenominator); |
|---|
| 384 |
return log(z) + p; |
|---|
| 385 |
} |
|---|
| 386 |
|
|---|
| 387 |
// const real MAXLGM = 1.04848146839019521116e+4928L; |
|---|
| 388 |
// if( x > MAXLGM ) return sgngaml * real.infinity; |
|---|
| 389 |
|
|---|
| 390 |
const real LOGSQRT2PI = 0.91893853320467274178L; // log( sqrt( 2*pi ) ) |
|---|
| 391 |
|
|---|
| 392 |
q = ( x - 0.5L ) * log(x) - x + LOGSQRT2PI; |
|---|
| 393 |
if (x > 1.0e10L) return q; |
|---|
| 394 |
real p = 1.0L / (x*x); |
|---|
| 395 |
q += poly( p, logGammaStirlingCoeffs ) / x; |
|---|
| 396 |
return q ; |
|---|
| 397 |
} |
|---|
| 398 |
|
|---|
| 399 |
debug(UnitTest) { |
|---|
| 400 |
unittest { |
|---|
| 401 |
assert(isIdentical(logGamma(NaN(0xDEF)), NaN(0xDEF))); |
|---|
| 402 |
assert(logGamma(real.infinity) == real.infinity); |
|---|
| 403 |
assert(logGamma(-1.0) == real.infinity); |
|---|
| 404 |
assert(logGamma(0.0) == real.infinity); |
|---|
| 405 |
assert(logGamma(-50.0) == real.infinity); |
|---|
| 406 |
assert(isIdentical(0.0L, logGamma(1.0L))); |
|---|
| 407 |
assert(isIdentical(0.0L, logGamma(2.0L))); |
|---|
| 408 |
assert(logGamma(real.min*real.epsilon) == real.infinity); |
|---|
| 409 |
assert(logGamma(-real.min*real.epsilon) == real.infinity); |
|---|
| 410 |
|
|---|
| 411 |
// x, correct loggamma(x), correct d/dx loggamma(x). |
|---|
| 412 |
static real[] testpoints = [ |
|---|
| 413 |
8.0L, 8.525146484375L + 1.48766904143001655310E-5, 2.01564147795560999654E0L, |
|---|
| 414 |
8.99993896484375e-1L, 6.6375732421875e-2L + 5.11505711292524166220E-6L, -7.54938684259372234258E-1, |
|---|
| 415 |
7.31597900390625e-1L, 2.2369384765625e-1 + 5.21506341809849792422E-6L, -1.13355566660398608343E0L, |
|---|
| 416 |
2.31639862060546875e-1L, 1.3686676025390625L + 1.12609441752996145670E-5L, -4.56670961813812679012E0, |
|---|
| 417 |
1.73162841796875L, -8.88214111328125e-2L + 3.36207740803753034508E-6L, 2.33339034686200586920E-1L, |
|---|
| 418 |
1.23162841796875L, -9.3902587890625e-2L + 1.28765089229009648104E-5L, -2.49677345775751390414E-1L, |
|---|
| 419 |
7.3786976294838206464e19L, 3.301798506038663053312e21L - 1.656137564136932662487046269677E5L, |
|---|
| 420 |
4.57477139169563904215E1L, |
|---|
| 421 |
1.08420217248550443401E-19L, 4.36682586669921875e1L + 1.37082843669932230418E-5L, |
|---|
| 422 |
-9.22337203685477580858E18L, |
|---|
| 423 |
1.0L, 0.0L, -5.77215664901532860607E-1L, |
|---|
| 424 |
2.0L, 0.0L, 4.22784335098467139393E-1L, |
|---|
| 425 |
-0.5L, 1.2655029296875L + 9.19379714539648894580E-6L, 3.64899739785765205590E-2L, |
|---|
| 426 |
-1.5L, 8.6004638671875e-1L + 6.28657731014510932682E-7L, 7.03156640645243187226E-1L, |
|---|
| 427 |
-2.5L, -5.6243896484375E-2L + 1.79986700949327405470E-7, 1.10315664064524318723E0L, |
|---|
| 428 |
-3.5L, -1.30902099609375L + 1.43111007079536392848E-5L, 1.38887092635952890151E0L |
|---|
| 429 |
]; |
|---|
| 430 |
// TODO: test derivatives as well. |
|---|
| 431 |
for (int i=0; i<testpoints.length; i+=3) { |
|---|
| 432 |
assert( feqrel(logGamma(testpoints[i]), testpoints[i+1]) > real.mant_dig-5); |
|---|
| 433 |
if (testpoints[i]<MAXGAMMA) { |
|---|
| 434 |
assert( feqrel(log(fabs(gamma(testpoints[i]))), testpoints[i+1]) > real.mant_dig-5); |
|---|
| 435 |
} |
|---|
| 436 |
} |
|---|
| 437 |
assert(logGamma(-50.2) == log(fabs(gamma(-50.2)))); |
|---|
| 438 |
assert(logGamma(-0.008) == log(fabs(gamma(-0.008)))); |
|---|
| 439 |
assert(feqrel(logGamma(-38.8),log(fabs(gamma(-38.8)))) > real.mant_dig-4); |
|---|
| 440 |
assert(feqrel(logGamma(1500.0L),log(gamma(1500.0L))) > real.mant_dig-2); |
|---|
| 441 |
} |
|---|
| 442 |
} |
|---|
| 443 |
|
|---|
| 444 |
private { |
|---|
| 445 |
const real MAXLOG = 0x1.62e42fefa39ef358p+13L; // log(real.max) |
|---|
| 446 |
const real MINLOG = -0x1.6436716d5406e6d8p+13L; // log(real.min*real.epsilon) = log(smallest denormal) |
|---|
| 447 |
const real BETA_BIG = 9.223372036854775808e18L; |
|---|
| 448 |
const real BETA_BIGINV = 1.084202172485504434007e-19L; |
|---|
| 449 |
} |
|---|
| 450 |
|
|---|
| 451 |
/** Beta function |
|---|
| 452 |
* |
|---|
| 453 |
* The beta function is defined as |
|---|
| 454 |
* |
|---|
| 455 |
* beta(x, y) = (Γ(x) Γ(y))/Γ(x + y) |
|---|
| 456 |
*/ |
|---|
| 457 |
real beta(real x, real y) |
|---|
| 458 |
{ |
|---|
| 459 |
if ((x+y)> MAXGAMMA) { |
|---|
| 460 |
return exp(logGamma(x) + logGamma(y) - logGamma(x+y)); |
|---|
| 461 |
} else return gamma(x)*gamma(y)/gamma(x+y); |
|---|
| 462 |
} |
|---|
| 463 |
|
|---|
| 464 |
debug(UnitTest) { |
|---|
| 465 |
unittest { |
|---|
| 466 |
assert(isIdentical(beta(NaN(0xABC), 4), NaN(0xABC))); |
|---|
| 467 |
assert(isIdentical(beta(2, NaN(0xABC)), NaN(0xABC))); |
|---|
| 468 |
} |
|---|
| 469 |
} |
|---|
| 470 |
|
|---|
| 471 |
/** Incomplete beta integral |
|---|
| 472 |
* |
|---|
| 473 |
* Returns incomplete beta integral of the arguments, evaluated |
|---|
| 474 |
* from zero to x. The regularized incomplete beta function is defined as |
|---|
| 475 |
* |
|---|
| 476 |
* betaIncomplete(a, b, x) = Γ(a+b)/(Γ(a) Γ(b)) * |
|---|
| 477 |
* $(INTEGRATE 0, x) $(POWER t, a-1)$(POWER (1-t),b-1) dt |
|---|
| 478 |
* |
|---|
| 479 |
* and is the same as the the cumulative distribution function. |
|---|
| 480 |
* |
|---|
| 481 |
* The domain of definition is 0 <= x <= 1. In this |
|---|
| 482 |
* implementation a and b are restricted to positive values. |
|---|
| 483 |
* The integral from x to 1 may be obtained by the symmetry |
|---|
| 484 |
* relation |
|---|
| 485 |
* |
|---|
| 486 |
* betaIncompleteCompl(a, b, x ) = betaIncomplete( b, a, 1-x ) |
|---|
| 487 |
* |
|---|
| 488 |
* The integral is evaluated by a continued fraction expansion |
|---|
| 489 |
* or, when b*x is small, by a power series. |
|---|
| 490 |
*/ |
|---|
| 491 |
real betaIncomplete(real aa, real bb, real xx ) |
|---|
| 492 |
{ |
|---|
| 493 |
if (!(aa>0 && bb>0)) { |
|---|
| 494 |
if (isNaN(aa)) return aa; |
|---|
| 495 |
if (isNaN(bb)) return bb; |
|---|
| 496 |
return NaN(TANGO_NAN.BETA_DOMAIN); // domain error |
|---|
| 497 |
} |
|---|
| 498 |
if (!(xx>0 && xx<1.0)) { |
|---|
| 499 |
if (isNaN(xx)) return xx; |
|---|
| 500 |
if ( xx == 0.0L ) return 0.0; |
|---|
| 501 |
if ( xx == 1.0L ) return 1.0; |
|---|
| 502 |
return NaN(TANGO_NAN.BETA_DOMAIN); // domain error |
|---|
| 503 |
} |
|---|
| 504 |
if ( (bb * xx) <= 1.0L && xx <= 0.95L) { |
|---|
| 505 |
return betaDistPowerSeries(aa, bb, xx); |
|---|
| 506 |
} |
|---|
| 507 |
real x; |
|---|
| 508 |
real xc; // = 1 - x |
|---|
| 509 |
|
|---|
| 510 |
real a, b; |
|---|
| 511 |
int flag = 0; |
|---|
| 512 |
|
|---|
| 513 |
/* Reverse a and b if x is greater than the mean. */ |
|---|
| 514 |
if( xx > (aa/(aa+bb)) ) { |
|---|
| 515 |
// here x > aa/(aa+bb) and (bb*x>1 or x>0.95) |
|---|
| 516 |
flag = 1; |
|---|
| 517 |
a = bb; |
|---|
| 518 |
b = aa; |
|---|
| 519 |
xc = xx; |
|---|
| 520 |
x = 1.0L - xx; |
|---|
| 521 |
} else { |
|---|
| 522 |
a = aa; |
|---|
| 523 |
b = bb; |
|---|
| 524 |
xc = 1.0L - xx; |
|---|
| 525 |
x = xx; |
|---|
| 526 |
} |
|---|
| 527 |
|
|---|
| 528 |
if( flag == 1 && (b * x) <= 1.0L && x <= 0.95L) { |
|---|
| 529 |
// here xx > aa/(aa+bb) and ((bb*xx>1) or xx>0.95) and (aa*(1-xx)<=1) and xx > 0.05 |
|---|
| 530 |
return 1.0 - betaDistPowerSeries(a, b, x); // note loss of precision |
|---|
| 531 |
} |
|---|
| 532 |
|
|---|
| 533 |
real w; |
|---|
| 534 |
// Choose expansion for optimal convergence |
|---|
| 535 |
// One is for x * (a+b+2) < (a+1), |
|---|
| 536 |
// the other is for x * (a+b+2) > (a+1). |
|---|
| 537 |
real y = x * (a+b-2.0L) - (a-1.0L); |
|---|
| 538 |
if( y < 0.0L ) { |
|---|
| 539 |
w = betaDistExpansion1( a, b, x ); |
|---|
| 540 |
} else { |
|---|
| 541 |
w = betaDistExpansion2( a, b, x ) / xc; |
|---|
| 542 |
} |
|---|
| 543 |
|
|---|
| 544 |
/* Multiply w by the factor |
|---|
| 545 |
a b |
|---|
| 546 |
x (1-x) Gamma(a+b) / ( a Gamma(a) Gamma(b) ) . */ |
|---|
| 547 |
|
|---|
| 548 |
y = a * log(x); |
|---|
| 549 |
real t = b * log(xc); |
|---|
| 550 |
if ( (a+b) < MAXGAMMA && fabs(y) < MAXLOG && fabs(t) < MAXLOG ) { |
|---|
| 551 |
t = pow(xc,b); |
|---|
| 552 |
t *= pow(x,a); |
|---|
| 553 |
t /= a; |
|---|
| 554 |
t *= w; |
|---|
| 555 |
t *= gamma(a+b) / (gamma(a) * gamma(b)); |
|---|
| 556 |
} else { |
|---|
| 557 |
/* Resort to logarithms. */ |
|---|
| 558 |
y += t + logGamma(a+b) - logGamma(a) - logGamma(b); |
|---|
| 559 |
y += log(w/a); |
|---|
| 560 |
|
|---|
| 561 |
t = exp(y); |
|---|
| 562 |
/+ |
|---|
| 563 |
// There seems to be a bug in Cephes at this point. |
|---|
| 564 |
// Problems occur for y > MAXLOG, not y < MINLOG. |
|---|
| 565 |
if( y < MINLOG ) { |
|---|
| 566 |
t = 0.0L; |
|---|
| 567 |
} else { |
|---|
| 568 |
t = exp(y); |
|---|
| 569 |
} |
|---|
| 570 |
+/ |
|---|
| 571 |
} |
|---|
| 572 |
if( flag == 1 ) { |
|---|
| 573 |
/+ // CEPHES includes this code, but I think it is erroneous. |
|---|
| 574 |
if( t <= real.epsilon ) { |
|---|
| 575 |
t = 1.0L - real.epsilon; |
|---|
| 576 |
} else |
|---|
| 577 |
+/ |
|---|
| 578 |
t = 1.0L - t; |
|---|
| 579 |
} |
|---|
| 580 |
return t; |
|---|
| 581 |
} |
|---|
| 582 |
|
|---|
| 583 |
/** Inverse of incomplete beta integral |
|---|
| 584 |
* |
|---|
| 585 |
* Given y, the function finds x such that |
|---|
| 586 |
* |
|---|
| 587 |
* betaIncomplete(a, b, x) == y |
|---|
| 588 |
* |
|---|
| 589 |
* Newton iterations or interval halving is used. |
|---|
| 590 |
*/ |
|---|
| 591 |
real betaIncompleteInv(real aa, real bb, real yy0 ) |
|---|
| 592 |
{ |
|---|
| 593 |
real a, b, y0, d, y, x, x0, x1, lgm, yp, di, dithresh, yl, yh, xt; |
|---|
| 594 |
int i, rflg, dir, nflg; |
|---|
| 595 |
|
|---|
| 596 |
if (isNaN(yy0)) return yy0; |
|---|
| 597 |
if (isNaN(aa)) return aa; |
|---|
| 598 |
if (isNaN(bb)) return bb; |
|---|
| 599 |
if( yy0 <= 0.0L ) |
|---|
| 600 |
return 0.0L; |
|---|
| 601 |
if( yy0 >= 1.0L ) |
|---|
| 602 |
return 1.0L; |
|---|
| 603 |
x0 = 0.0L; |
|---|
| 604 |
yl = 0.0L; |
|---|
| 605 |
x1 = 1.0L; |
|---|
| 606 |
yh = 1.0L; |
|---|
| 607 |
if( aa <= 1.0L || bb <= 1.0L ) { |
|---|
| 608 |
dithresh = 1.0e-7L; |
|---|
| 609 |
rflg = 0; |
|---|
| 610 |
a = aa; |
|---|
| 611 |
b = bb; |
|---|
| 612 |
y0 = yy0; |
|---|
| 613 |
x = a/(a+b); |
|---|
| 614 |
y = betaIncomplete( a, b, x ); |
|---|
| 615 |
nflg = 0; |
|---|
| 616 |
goto ihalve; |
|---|
| 617 |
} else { |
|---|
| 618 |
nflg = 0; |
|---|
| 619 |
dithresh = 1.0e-4L; |
|---|
| 620 |
} |
|---|
| 621 |
|
|---|
| 622 |
/* approximation to inverse function */ |
|---|
| 623 |
|
|---|
| 624 |
yp = -normalDistributionInvImpl( yy0 ); |
|---|
| 625 |
|
|---|
| 626 |
if( yy0 > 0.5L ) { |
|---|
| 627 |
rflg = 1; |
|---|
| 628 |
a = bb; |
|---|
| 629 |
b = aa; |
|---|
| 630 |
y0 = 1.0L - yy0; |
|---|
| 631 |
yp = -yp; |
|---|
| 632 |
} else { |
|---|
| 633 |
rflg = 0; |
|---|
| 634 |
a = aa; |
|---|
| 635 |
b = bb; |
|---|
| 636 |
y0 = yy0; |
|---|
| 637 |
} |
|---|
| 638 |
|
|---|
| 639 |
lgm = (yp * yp - 3.0L)/6.0L; |
|---|
| 640 |
x = 2.0L/( 1.0L/(2.0L * a-1.0L) + 1.0L/(2.0L * b - 1.0L) ); |
|---|
| 641 |
d = yp * sqrt( x + lgm ) / x |
|---|
| 642 |
- ( 1.0L/(2.0L * b - 1.0L) - 1.0L/(2.0L * a - 1.0L) ) |
|---|
| 643 |
* (lgm + (5.0L/6.0L) - 2.0L/(3.0L * x)); |
|---|
| 644 |
d = 2.0L * d; |
|---|
| 645 |
if( d < MINLOG ) { |
|---|
| 646 |
x = 1.0L; |
|---|
| 647 |
goto under; |
|---|
| 648 |
} |
|---|
| 649 |
x = a/( a + b * exp(d) ); |
|---|
| 650 |
y = betaIncomplete( a, b, x ); |
|---|
| 651 |
yp = (y - y0)/y0; |
|---|
| 652 |
if( fabs(yp) < 0.2 ) |
|---|
| 653 |
goto newt; |
|---|
| 654 |
|
|---|
| 655 |
/* Resort to interval halving if not close enough. */ |
|---|
| 656 |
ihalve: |
|---|
| 657 |
|
|---|
| 658 |
dir = 0; |
|---|
| 659 |
di = 0.5L; |
|---|
| 660 |
for( i=0; i<400; i++ ) { |
|---|
| 661 |
if( i != 0 ) { |
|---|
| 662 |
x = x0 + di * (x1 - x0); |
|---|
| 663 |
if( x == 1.0L ) { |
|---|
| 664 |
x = 1.0L - real.epsilon; |
|---|
| 665 |
} |
|---|
| 666 |
if( x == 0.0L ) { |
|---|
| 667 |
di = 0.5; |
|---|
| 668 |
x = x0 + di * (x1 - x0); |
|---|
| 669 |
if( x == 0.0 ) |
|---|
| 670 |
goto under; |
|---|
| 671 |
} |
|---|
| 672 |
y = betaIncomplete( a, b, x ); |
|---|
| 673 |
yp = (x1 - x0)/(x1 + x0); |
|---|
| 674 |
if( fabs(yp) < dithresh ) |
|---|
| 675 |
goto newt; |
|---|
| 676 |
yp = (y-y0)/y0; |
|---|
| 677 |
if( fabs(yp) < dithresh ) |
|---|
| 678 |
goto newt; |
|---|
| 679 |
} |
|---|
| 680 |
if( y < y0 ) { |
|---|
| 681 |
x0 = x; |
|---|
| 682 |
yl = y; |
|---|
| 683 |
if( dir < 0 ) { |
|---|
| 684 |
dir = 0; |
|---|
| 685 |
di = 0.5L; |
|---|
| 686 |
} else if( dir > 3 ) |
|---|
| 687 |
di = 1.0L - (1.0L - di) * (1.0L - di); |
|---|
| 688 |
else if( dir > 1 ) |
|---|
| 689 |
di = 0.5L * di + 0.5L; |
|---|
| 690 |
else |
|---|
| 691 |
di = (y0 - y)/(yh - yl); |
|---|
| 692 |
dir += 1; |
|---|
| 693 |
if( x0 > 0.95L ) { |
|---|
| 694 |
if( rflg == 1 ) { |
|---|
| 695 |
rflg = 0; |
|---|
| 696 |
a = aa; |
|---|
| 697 |
b = bb; |
|---|
| 698 |
y0 = yy0; |
|---|
| 699 |
} else { |
|---|
| 700 |
rflg = 1; |
|---|
| 701 |
a = bb; |
|---|
| 702 |
b = aa; |
|---|
| 703 |
y0 = 1.0 - yy0; |
|---|
| 704 |
} |
|---|
| 705 |
x = 1.0L - x; |
|---|
| 706 |
y = betaIncomplete( a, b, x ); |
|---|
| 707 |
x0 = 0.0; |
|---|
| 708 |
yl = 0.0; |
|---|
| 709 |
x1 = 1.0; |
|---|
| 710 |
yh = 1.0; |
|---|
| 711 |
goto ihalve; |
|---|
| 712 |
} |
|---|
| 713 |
} else { |
|---|
| 714 |
x1 = x; |
|---|
| 715 |
if( rflg == 1 && x1 < real.epsilon ) { |
|---|
| 716 |
x = 0.0L; |
|---|
| 717 |
goto done; |
|---|
| 718 |
} |
|---|
| 719 |
yh = y; |
|---|
| 720 |
if( dir > 0 ) { |
|---|
| 721 |
dir = 0; |
|---|
| 722 |
di = 0.5L; |
|---|
| 723 |
} |
|---|
| 724 |
else if( dir < -3 ) |
|---|
| 725 |
di = di * di; |
|---|
| 726 |
else if( dir < -1 ) |
|---|
| 727 |
di = 0.5L * di; |
|---|
| 728 |
else |
|---|
| 729 |
di = (y - y0)/(yh - yl); |
|---|
| 730 |
dir -= 1; |
|---|
| 731 |
} |
|---|
| 732 |
} |
|---|
| 733 |
// loss of precision has occurred |
|---|
| 734 |
|
|---|
| 735 |
//mtherr( "incbil", PLOSS ); |
|---|
| 736 |
if( x0 >= 1.0L ) { |
|---|
| 737 |
x = 1.0L - real.epsilon; |
|---|
| 738 |
goto done; |
|---|
| 739 |
} |
|---|
| 740 |
if( x <= 0.0L ) { |
|---|
| 741 |
under: |
|---|
| 742 |
// underflow has occurred |
|---|
| 743 |
//mtherr( "incbil", UNDERFLOW ); |
|---|
| 744 |
x = 0.0L; |
|---|
| 745 |
goto done; |
|---|
| 746 |
} |
|---|
| 747 |
|
|---|
| 748 |
newt: |
|---|
| 749 |
|
|---|
| 750 |
if ( nflg ) { |
|---|
| 751 |
goto done; |
|---|
| 752 |
} |
|---|
| 753 |
nflg = 1; |
|---|
| 754 |
lgm = logGamma(a+b) - logGamma(a) - logGamma(b); |
|---|
| 755 |
|
|---|
| 756 |
for( i=0; i<15; i++ ) { |
|---|
| 757 |
/* Compute the function at this point. */ |
|---|
| 758 |
if ( i != 0 ) |
|---|
| 759 |
y = betaIncomplete(a,b,x); |
|---|
| 760 |
if ( y < yl ) { |
|---|
| 761 |
x = x0; |
|---|
| 762 |
y = yl; |
|---|
| 763 |
} else if( y > yh ) { |
|---|
| 764 |
x = x1; |
|---|
| 765 |
y = yh; |
|---|
| 766 |
} else if( y < y0 ) { |
|---|
| 767 |
x0 = x; |
|---|
| 768 |
yl = y; |
|---|
| 769 |
} else { |
|---|
| 770 |
x1 = x; |
|---|
| 771 |
yh = y; |
|---|
| 772 |
} |
|---|
| 773 |
if( x == 1.0L || x == 0.0L ) |
|---|
| 774 |
break; |
|---|
| 775 |
/* Compute the derivative of the function at this point. */ |
|---|
| 776 |
d = (a - 1.0L) * log(x) + (b - 1.0L) * log(1.0L - x) + lgm; |
|---|
| 777 |
if ( d < MINLOG ) { |
|---|
| 778 |
goto done; |
|---|
| 779 |
} |
|---|
| 780 |
if ( d > MAXLOG ) { |
|---|
| 781 |
break; |
|---|
| 782 |
} |
|---|
| 783 |
d = exp(d); |
|---|
| 784 |
/* Compute the step to the next approximation of x. */ |
|---|
| 785 |
d = (y - y0)/d; |
|---|
| 786 |
xt = x - d; |
|---|
| 787 |
if ( xt <= x0 ) { |
|---|
| 788 |
y = (x - x0) / (x1 - x0); |
|---|
| 789 |
xt = x0 + 0.5L * y * (x - x0); |
|---|
| 790 |
if( xt <= 0.0L ) |
|---|
| 791 |
break; |
|---|
| 792 |
} |
|---|
| 793 |
if ( xt >= x1 ) { |
|---|
| 794 |
y = (x1 - x) / (x1 - x0); |
|---|
| 795 |
xt = x1 - 0.5L * y * (x1 - x); |
|---|
| 796 |
if ( xt >= 1.0L ) |
|---|
| 797 |
break; |
|---|
| 798 |
} |
|---|
| 799 |
x = xt; |
|---|
| 800 |
if ( fabs(d/x) < (128.0L * real.epsilon) ) |
|---|
| 801 |
goto done; |
|---|
| 802 |
} |
|---|
| 803 |
/* Did not converge. */ |
|---|
| 804 |
dithresh = 256.0L * real.epsilon; |
|---|
| 805 |
goto ihalve; |
|---|
| 806 |
|
|---|
| 807 |
done: |
|---|
| 808 |
if ( rflg ) { |
|---|
| 809 |
if( x <= real.epsilon ) |
|---|
| 810 |
x = 1.0L - real.epsilon; |
|---|
| 811 |
else |
|---|
| 812 |
x = 1.0L - x; |
|---|
| 813 |
} |
|---|
| 814 |
return x; |
|---|
| 815 |
} |
|---|
| 816 |
|
|---|
| 817 |
debug(UnitTest) { |
|---|
| 818 |
unittest { // also tested by the normal distribution |
|---|
| 819 |
// check NaN propagation |
|---|
| 820 |
assert(isIdentical(betaIncomplete(NaN(0xABC),2,3), NaN(0xABC))); |
|---|
| 821 |
assert(isIdentical(betaIncomplete(7,NaN(0xABC),3), NaN(0xABC))); |
|---|
| 822 |
assert(isIdentical(betaIncomplete(7,15,NaN(0xABC)), NaN(0xABC))); |
|---|
| 823 |
assert(isIdentical(betaIncompleteInv(NaN(0xABC),1,17), NaN(0xABC))); |
|---|
| 824 |
assert(isIdentical(betaIncompleteInv(2,NaN(0xABC),8), NaN(0xABC))); |
|---|
| 825 |
assert(isIdentical(betaIncompleteInv(2,3, NaN(0xABC)), NaN(0xABC))); |
|---|
| 826 |
|
|---|
| 827 |
assert(isNaN(betaIncomplete(-1, 2, 3))); |
|---|
| 828 |
|
|---|
| 829 |
assert(betaIncomplete(1, 2, 0)==0); |
|---|
| 830 |
assert(betaIncomplete(1, 2, 1)==1); |
|---|
| 831 |
assert(isNaN(betaIncomplete(1, 2, 3))); |
|---|
| 832 |
assert(betaIncompleteInv(1, 1, 0)==0); |
|---|
| 833 |
assert(betaIncompleteInv(1, 1, 1)==1); |
|---|
| 834 |
|
|---|
| 835 |
// Test some values against Microsoft Excel 2003. |
|---|
| 836 |
|
|---|
| 837 |
assert(fabs(betaIncomplete(8, 10, 0.2) - 0.010_934_315_236_957_2L) < 0.000_000_000_5); |
|---|
| 838 |
assert(fabs(betaIncomplete(2, 2.5, 0.9) - 0.989_722_597_604_107L) < 0.000_000_000_000_5); |
|---|
| 839 |
assert(fabs(betaIncomplete(1000, 800, 0.5) - 1.17914088832798E-06L) < 0.000_000_05e-6); |
|---|
| 840 |
|
|---|
| 841 |
assert(fabs(betaIncomplete(0.0001, 10000, 0.0001) - 0.999978059369989L) < 0.000_000_000_05); |
|---|
| 842 |
|
|---|
| 843 |
assert(fabs(betaIncompleteInv(5, 10, 0.2) - 0.229121208190918L) < 0.000_000_5L); |
|---|
| 844 |
assert(fabs(betaIncompleteInv(4, 7, 0.8) - 0.483657360076904L) < 0.000_000_5L); |
|---|
| 845 |
|
|---|
| 846 |
// Coverage tests. I don't have correct values for these tests, but |
|---|
| 847 |
// these values cover most of the code, so they are useful for |
|---|
| 848 |
// regression testing. |
|---|
| 849 |
// Extensive testing failed to increase the coverage. It seems likely that about |
|---|
| 850 |
// half the code in this function is unnecessary; there is potential for |
|---|
| 851 |
// significant improvement over the original CEPHES code. |
|---|
| 852 |
|
|---|
| 853 |
// Excel 2003 gives clearly erroneous results (betadist>1) when a and x are tiny and b is huge. |
|---|
| 854 |
// The correct results are for these next tests are unknown. |
|---|
| 855 |
|
|---|
| 856 |
// real testpoint1 = betaIncomplete(1e-10, 5e20, 8e-21); |
|---|
| 857 |
// assert(testpoint1 == 0x1.ffff_ffff_c906_404cp-1L); |
|---|
| 858 |
|
|---|
| 859 |
assert(betaIncomplete(0.01, 327726.7, 0.545113) == 1.0); |
|---|
| 860 |
assert(betaIncompleteInv(0.01, 8e-48, 5.45464e-20)==1-real.epsilon); |
|---|
| 861 |
assert(betaIncompleteInv(0.01, 8e-48, 9e-26)==1-real.epsilon); |
|---|
| 862 |
|
|---|
| 863 |
assert(betaIncomplete(0.01, 498.437, 0.0121433) == 0x1.ffff_8f72_19197402p-1); |
|---|
| 864 |
assert(1- betaIncomplete(0.01, 328222, 4.0375e-5) == 0x1.5f62926b4p-30); |
|---|
| 865 |
version(FailsOnLinux) assert(betaIncompleteInv(0x1.b3d151fbba0eb18p+1, 1.2265e-19, 2.44859e-18)==0x1.c0110c8531d0952cp-1); |
|---|
| 866 |
version(FailsOnLinux) assert(betaIncompleteInv(0x1.ff1275ae5b939bcap-41, 4.6713e18, 0.0813601)==0x1.f97749d90c7adba8p-63); |
|---|
| 867 |
real a1; |
|---|
| 868 |
a1 = 3.40483; |
|---|
| 869 |
version(FailsOnLinux) assert(betaIncompleteInv(a1, 4.0640301659679627772e19L, 0.545113)== 0x1.ba8c08108aaf5d14p-109); |
|---|
| 870 |
real b1; |
|---|
| 871 |
b1= 2.82847e-25; |
|---|
| 872 |
version(FailsOnLinux) assert(betaIncompleteInv(0.01, b1, 9e-26) == 0x1.549696104490aa9p-830); |
|---|
| 873 |
|
|---|
| 874 |
// --- Problematic cases --- |
|---|
| 875 |
// This is a situation where the series expansion fails to converge |
|---|
| 876 |
assert( isNaN(betaIncompleteInv(0.12167, 4.0640301659679627772e19L, 0.0813601))); |
|---|
| 877 |
// This next result is almost certainly erroneous. |
|---|
| 878 |
assert(betaIncomplete(1.16251e20, 2.18e39, 5.45e-20)==-real.infinity); |
|---|
| 879 |
} |
|---|
| 880 |
} |
|---|
| 881 |
|
|---|
| 882 |
private { |
|---|
| 883 |
// Implementation functions |
|---|
| 884 |
|
|---|
| 885 |
// Continued fraction expansion #1 for incomplete beta integral |
|---|
| 886 |
// Use when x < (a+1)/(a+b+2) |
|---|
| 887 |
real betaDistExpansion1(real a, real b, real x ) |
|---|
| 888 |
{ |
|---|
| 889 |
real xk, pk, pkm1, pkm2, qk, qkm1, qkm2; |
|---|
| 890 |
real k1, k2, k3, k4, k5, k6, k7, k8; |
|---|
| 891 |
real r, t, ans; |
|---|
| 892 |
int n; |
|---|
| 893 |
|
|---|
| 894 |
k1 = a; |
|---|
| 895 |
k2 = a + b; |
|---|
| 896 |
k3 = a; |
|---|
| 897 |
k4 = a + 1.0L; |
|---|
| 898 |
k5 = 1.0L; |
|---|
| 899 |
k6 = b - 1.0L; |
|---|
| 900 |
k7 = k4; |
|---|
| 901 |
k8 = a + 2.0L; |
|---|
| 902 |
|
|---|
| 903 |
pkm2 = 0.0L; |
|---|
| 904 |
qkm2 = 1.0L; |
|---|
| 905 |
pkm1 = 1.0L; |
|---|
| 906 |
qkm1 = 1.0L; |
|---|
| 907 |
ans = 1.0L; |
|---|
| 908 |
r = 1.0L; |
|---|
| 909 |
n = 0; |
|---|
| 910 |
const real thresh = 3.0L * real.epsilon; |
|---|
| 911 |
do { |
|---|
| 912 |
xk = -( x * k1 * k2 )/( k3 * k4 ); |
|---|
| 913 |
pk = pkm1 + pkm2 * xk; |
|---|
| 914 |
qk = qkm1 + qkm2 * xk; |
|---|
| 915 |
pkm2 = pkm1; |
|---|
| 916 |
pkm1 = pk; |
|---|
| 917 |
qkm2 = qkm1; |
|---|
| 918 |
qkm1 = qk; |
|---|
| 919 |
|
|---|
| 920 |
xk = ( x * k5 * k6 )/( k7 * k8 ); |
|---|
| 921 |
pk = pkm1 + pkm2 * xk; |
|---|
| 922 |
qk = qkm1 + qkm2 * xk; |
|---|
| 923 |
pkm2 = pkm1; |
|---|
| 924 |
pkm1 = pk; |
|---|
| 925 |
qkm2 = qkm1; |
|---|
| 926 |
qkm1 = qk; |
|---|
| 927 |
|
|---|
| 928 |
if( qk != 0.0L ) |
|---|
| 929 |
r = pk/qk; |
|---|
| 930 |
if( r != 0.0L ) { |
|---|
| 931 |
t = fabs( (ans - r)/r ); |
|---|
| 932 |
ans = r; |
|---|
| 933 |
} else { |
|---|
| 934 |
t = 1.0L; |
|---|
| 935 |
} |
|---|
| 936 |
|
|---|
| 937 |
if( t < thresh ) |
|---|
| 938 |
return ans; |
|---|
| 939 |
|
|---|
| 940 |
k1 += 1.0L; |
|---|
| 941 |
k2 += 1.0L; |
|---|
| 942 |
k3 += 2.0L; |
|---|
| 943 |
k4 += 2.0L; |
|---|
| 944 |
k5 += 1.0L; |
|---|
| 945 |
k6 -= 1.0L; |
|---|
| 946 |
k7 += 2.0L; |
|---|
| 947 |
k8 += 2.0L; |
|---|
| 948 |
|
|---|
| 949 |
if( (fabs(qk) + fabs(pk)) > BETA_BIG ) { |
|---|
| 950 |
pkm2 *= BETA_BIGINV; |
|---|
| 951 |
pkm1 *= BETA_BIGINV; |
|---|
| 952 |
qkm2 *= BETA_BIGINV; |
|---|
| 953 |
qkm1 *= BETA_BIGINV; |
|---|
| 954 |
} |
|---|
| 955 |
if( (fabs(qk) < BETA_BIGINV) || (fabs(pk) < BETA_BIGINV) ) { |
|---|
| 956 |
pkm2 *= BETA_BIG; |
|---|
| 957 |
pkm1 *= BETA_BIG; |
|---|
| 958 |
qkm2 *= BETA_BIG; |
|---|
| 959 |
qkm1 *= BETA_BIG; |
|---|
| 960 |
} |
|---|
| 961 |
} |
|---|
| 962 |
while( ++n < 400 ); |
|---|
| 963 |
// loss of precision has occurred |
|---|
| 964 |
// mtherr( "incbetl", PLOSS ); |
|---|
| 965 |
return ans; |
|---|
| 966 |
} |
|---|
| 967 |
|
|---|
| 968 |
// Continued fraction expansion #2 for incomplete beta integral |
|---|
| 969 |
// Use when x > (a+1)/(a+b+2) |
|---|
| 970 |
real betaDistExpansion2(real a, real b, real x ) |
|---|
| 971 |
{ |
|---|
| 972 |
real xk, pk, pkm1, pkm2, qk, qkm1, qkm2; |
|---|
| 973 |
real k1, k2, k3, k4, k5, k6, k7, k8; |
|---|
| 974 |
real r, t, ans, z; |
|---|
| 975 |
|
|---|
| 976 |
k1 = a; |
|---|
| 977 |
k2 = b - 1.0L; |
|---|
| 978 |
k3 = a; |
|---|
| 979 |
k4 = a + 1.0L; |
|---|
| 980 |
k5 = 1.0L; |
|---|
| 981 |
k6 = a + b; |
|---|
| 982 |
k7 = a + 1.0L; |
|---|
| 983 |
k8 = a + 2.0L; |
|---|
| 984 |
|
|---|
| 985 |
pkm2 = 0.0L; |
|---|
| 986 |
qkm2 = 1.0L; |
|---|
| 987 |
pkm1 = 1.0L; |
|---|
| 988 |
qkm1 = 1.0L; |
|---|
| 989 |
z = x / (1.0L-x); |
|---|
| 990 |
ans = 1.0L; |
|---|
| 991 |
r = 1.0L; |
|---|
| 992 |
int n = 0; |
|---|
| 993 |
const real thresh = 3.0L * real.epsilon; |
|---|
| 994 |
do { |
|---|
| 995 |
|
|---|
| 996 |
xk = -( z * k1 * k2 )/( k3 * k4 ); |
|---|
| 997 |
pk = pkm1 + pkm2 * xk; |
|---|
| 998 |
qk = qkm1 + qkm2 * xk; |
|---|
| 999 |
pkm2 = pkm1; |
|---|
| 1000 |
pkm1 = pk; |
|---|
| 1001 |
qkm2 = qkm1; |
|---|
| 1002 |
qkm1 = qk; |
|---|
| 1003 |
|
|---|
| 1004 |
xk = ( z * k5 * k6 )/( k7 * k8 ); |
|---|
| 1005 |
pk = pkm1 + pkm2 * xk; |
|---|
| 1006 |
qk = qkm1 + qkm2 * xk; |
|---|
| 1007 |
pkm2 = pkm1; |
|---|
| 1008 |
pkm1 = pk; |
|---|
| 1009 |
qkm2 = qkm1; |
|---|
| 1010 |
qkm1 = qk; |
|---|
| 1011 |
|
|---|
| 1012 |
if( qk != 0.0L ) |
|---|
| 1013 |
r = pk/qk; |
|---|
| 1014 |
if( r != 0.0L ) { |
|---|
| 1015 |
t = fabs( (ans - r)/r ); |
|---|
| 1016 |
ans = r; |
|---|
| 1017 |
} else |
|---|
| 1018 |
t = 1.0L; |
|---|
| 1019 |
|
|---|
| 1020 |
if( t < thresh ) |
|---|
| 1021 |
return ans; |
|---|
| 1022 |
k1 += 1.0L; |
|---|
| 1023 |
k2 -= 1.0L; |
|---|
| 1024 |
k3 += 2.0L; |
|---|
| 1025 |
k4 += 2.0L; |
|---|
| 1026 |
k5 += 1.0L; |
|---|
| 1027 |
k6 += 1.0L; |
|---|
| 1028 |
k7 += 2.0L; |
|---|
| 1029 |
k8 += 2.0L; |
|---|
| 1030 |
|
|---|
| 1031 |
if( (fabs(qk) + fabs(pk)) > BETA_BIG ) { |
|---|
| 1032 |
pkm2 *= BETA_BIGINV; |
|---|
| 1033 |
pkm1 *= BETA_BIGINV; |
|---|
| 1034 |
qkm2 *= BETA_BIGINV; |
|---|
| 1035 |
qkm1 *= BETA_BIGINV; |
|---|
| 1036 |
} |
|---|
| 1037 |
if( (fabs(qk) < BETA_BIGINV) || (fabs(pk) < BETA_BIGINV) ) { |
|---|
| 1038 |
pkm2 *= BETA_BIG; |
|---|
| 1039 |
pkm1 *= BETA_BIG; |
|---|
| 1040 |
qkm2 *= BETA_BIG; |
|---|
| 1041 |
qkm1 *= BETA_BIG; |
|---|
| 1042 |
} |
|---|
| 1043 |
} while( ++n < 400 ); |
|---|
| 1044 |
// loss of precision has occurred |
|---|
| 1045 |
//mtherr( "incbetl", PLOSS ); |
|---|
| 1046 |
return ans; |
|---|
| 1047 |
} |
|---|
| 1048 |
|
|---|
| 1049 |
/* Power series for incomplete gamma integral. |
|---|
| 1050 |
Use when b*x is small. */ |
|---|
| 1051 |
real betaDistPowerSeries(real a, real b, real x ) |
|---|
| 1052 |
{ |
|---|
| 1053 |
real ai = 1.0L / a; |
|---|
| 1054 |
real u = (1.0L - b) * x; |
|---|
| 1055 |
real v = u / (a + 1.0L); |
|---|
| 1056 |
real t1 = v; |
|---|
| 1057 |
real t = u; |
|---|
| 1058 |
real n = 2.0L; |
|---|
| 1059 |
real s = 0.0L; |
|---|
| 1060 |
real z = real.epsilon * ai; |
|---|
| 1061 |
while( fabs(v) > z ) { |
|---|
| 1062 |
u = (n - b) * x / n; |
|---|
| 1063 |
t *= u; |
|---|
| 1064 |
v = t / (a + n); |
|---|
| 1065 |
s += v; |
|---|
| 1066 |
n += 1.0L; |
|---|
| 1067 |
} |
|---|
| 1068 |
s += t1; |
|---|
| 1069 |
s += ai; |
|---|
| 1070 |
|
|---|
| 1071 |
u = a * log(x); |
|---|
| 1072 |
if ( (a+b) < MAXGAMMA && fabs(u) < MAXLOG ) { |
|---|
| 1073 |
t = gamma(a+b)/(gamma(a)*gamma(b)); |
|---|
| 1074 |
s = s * t * pow(x,a); |
|---|
| 1075 |
} else { |
|---|
| 1076 |
t = logGamma(a+b) - logGamma(a) - logGamma(b) + u + log(s); |
|---|
| 1077 |
|
|---|
| 1078 |
if( t < MINLOG ) { |
|---|
| 1079 |
s = 0.0L; |
|---|
| 1080 |
} else |
|---|
| 1081 |
s = exp(t); |
|---|
| 1082 |
} |
|---|
| 1083 |
return s; |
|---|
| 1084 |
} |
|---|
| 1085 |
|
|---|
| 1086 |
} |
|---|
| 1087 |
|
|---|
| 1088 |
/*************************************** |
|---|
| 1089 |
* Incomplete gamma integral and its complement |
|---|
| 1090 |
* |
|---|
| 1091 |
* These functions are defined by |
|---|
| 1092 |
* |
|---|
| 1093 |
* gammaIncomplete = ( $(INTEGRATE 0, x) $(POWER e, -t) $(POWER t, a-1) dt )/ $(GAMMA)(a) |
|---|
| 1094 |
* |
|---|
| 1095 |
* gammaIncompleteCompl(a,x) = 1 - gammaIncomplete(a,x) |
|---|
| 1096 |
* = ($(INTEGRATE x, ∞) $(POWER e, -t) $(POWER t, a-1) dt )/ $(GAMMA)(a) |
|---|
| 1097 |
* |
|---|
| 1098 |
* In this implementation both arguments must be positive. |
|---|
| 1099 |
* The integral is evaluated by either a power series or |
|---|
| 1100 |
* continued fraction expansion, depending on the relative |
|---|
| 1101 |
* values of a and x. |
|---|
| 1102 |
*/ |
|---|
| 1103 |
real gammaIncomplete(real a, real x ) |
|---|
| 1104 |
in { |
|---|
| 1105 |
assert(x >= 0); |
|---|
| 1106 |
assert(a > 0); |
|---|
| 1107 |
} |
|---|
| 1108 |
body { |
|---|
| 1109 |
/* left tail of incomplete gamma function: |
|---|
| 1110 |
* |
|---|
| 1111 |
* inf. k |
|---|
| 1112 |
* a -x - x |
|---|
| 1113 |
* x e > ---------- |
|---|
| 1114 |
* - - |
|---|
| 1115 |
* k=0 | (a+k+1) |
|---|
| 1116 |
* |
|---|
| 1117 |
*/ |
|---|
| 1118 |
if (x==0) |
|---|
| 1119 |
return 0.0L; |
|---|
| 1120 |
|
|---|
| 1121 |
if ( (x > 1.0L) && (x > a ) ) |
|---|
| 1122 |
return 1.0L - gammaIncompleteCompl(a,x); |
|---|
| 1123 |
|
|---|
| 1124 |
real ax = a * log(x) - x - logGamma(a); |
|---|
| 1125 |
/+ |
|---|
| 1126 |
if( ax < MINLOGL ) return 0; // underflow |
|---|
| 1127 |
// { mtherr( "igaml", UNDERFLOW ); return( 0.0L ); } |
|---|
| 1128 |
+/ |
|---|
| 1129 |
ax = exp(ax); |
|---|
| 1130 |
|
|---|
| 1131 |
/* power series */ |
|---|
| 1132 |
real r = a; |
|---|
| 1133 |
real c = 1.0L; |
|---|
| 1134 |
real ans = 1.0L; |
|---|
| 1135 |
|
|---|
| 1136 |
do { |
|---|
| 1137 |
r += 1.0L; |
|---|
| 1138 |
c *= x/r; |
|---|
| 1139 |
ans += c; |
|---|
| 1140 |
} while( c/ans > real.epsilon ); |
|---|
| 1141 |
|
|---|
| 1142 |
return ans * ax/a; |
|---|
| 1143 |
} |
|---|
| 1144 |
|
|---|
| 1145 |
/** ditto */ |
|---|
| 1146 |
real gammaIncompleteCompl(real a, real x ) |
|---|
| 1147 |
in { |
|---|
| 1148 |
assert(x >= 0); |
|---|
| 1149 |
assert(a > 0); |
|---|
| 1150 |
} |
|---|
| 1151 |
body { |
|---|
| 1152 |
if (x==0) |
|---|
| 1153 |
return 1.0L; |
|---|
| 1154 |
if ( (x < 1.0L) || (x < a) ) |
|---|
| 1155 |
return 1.0L - gammaIncomplete(a,x); |
|---|
| 1156 |
|
|---|
| 1157 |
// DAC (Cephes bug fix): This is necessary to avoid |
|---|
| 1158 |
// spurious nans, eg |
|---|
| 1159 |
// log(x)-x = NaN when x = real.infinity |
|---|
| 1160 |
const real MAXLOGL = 1.1356523406294143949492E4L; |
|---|
| 1161 |
if (x > MAXLOGL) return 0; // underflow |
|---|
| 1162 |
|
|---|
| 1163 |
real ax = a * log(x) - x - logGamma(a); |
|---|
| 1164 |
//const real MINLOGL = -1.1355137111933024058873E4L; |
|---|
| 1165 |
// if ( ax < MINLOGL ) return 0; // underflow; |
|---|
| 1166 |
ax = exp(ax); |
|---|
| 1167 |
|
|---|
| 1168 |
|
|---|
| 1169 |
/* continued fraction */ |
|---|
| 1170 |
real y = 1.0L - a; |
|---|
| 1171 |
real z = x + y + 1.0L; |
|---|
| 1172 |
real c = 0.0L; |
|---|
| 1173 |
|
|---|
| 1174 |
real pk, qk, t; |
|---|
| 1175 |
|
|---|
| 1176 |
real pkm2 = 1.0L; |
|---|
| 1177 |
real qkm2 = x; |
|---|
| 1178 |
real pkm1 = x + 1.0L; |
|---|
| 1179 |
real qkm1 = z * x; |
|---|
| 1180 |
real ans = pkm1/qkm1; |
|---|
| 1181 |
|
|---|
| 1182 |
do { |
|---|
| 1183 |
c += 1.0L; |
|---|
| 1184 |
y += 1.0L; |
|---|
| 1185 |
z += 2.0L; |
|---|
| 1186 |
real yc = y * c; |
|---|
| 1187 |
pk = pkm1 * z - pkm2 * yc; |
|---|
| 1188 |
qk = qkm1 * z - qkm2 * yc; |
|---|
| 1189 |
if( qk != 0.0L ) { |
|---|
| 1190 |
real r = pk/qk; |
|---|
| 1191 |
t = fabs( (ans - r)/r ); |
|---|
| 1192 |
ans = r; |
|---|
| 1193 |
} else { |
|---|
| 1194 |
t = 1.0L; |
|---|
| 1195 |
} |
|---|
| 1196 |
pkm2 = pkm1; |
|---|
| 1197 |
pkm1 = pk; |
|---|
| 1198 |
qkm2 = qkm1; |
|---|
| 1199 |
qkm1 = qk; |
|---|
| 1200 |
|
|---|
| 1201 |
const real BIG = 9.223372036854775808e18L; |
|---|
| 1202 |
|
|---|
| 1203 |
if ( fabs(pk) > BIG ) { |
|---|
| 1204 |
pkm2 /= BIG; |
|---|
| 1205 |
pkm1 /= BIG; |
|---|
| 1206 |
qkm2 /= BIG; |
|---|
| 1207 |
qkm1 /= BIG; |
|---|
| 1208 |
} |
|---|
| 1209 |
} while ( t > real.epsilon ); |
|---|
| 1210 |
|
|---|
| 1211 |
return ans * ax; |
|---|
| 1212 |
} |
|---|
| 1213 |
|
|---|
| 1214 |
/** Inverse of complemented incomplete gamma integral |
|---|
| 1215 |
* |
|---|
| 1216 |
* Given a and y, the function finds x such that |
|---|
| 1217 |
* |
|---|
| 1218 |
* gammaIncompleteCompl( a, x ) = p. |
|---|
| 1219 |
* |
|---|
| 1220 |
* Starting with the approximate value x = a $(POWER t, 3), where |
|---|
| 1221 |
* t = 1 - d - normalDistributionInv(p) sqrt(d), |
|---|
| 1222 |
* and d = 1/9a, |
|---|
| 1223 |
* the routine performs up to 10 Newton iterations to find the |
|---|
| 1224 |
* root of incompleteGammaCompl(a,x) - p = 0. |
|---|
| 1225 |
*/ |
|---|
| 1226 |
real gammaIncompleteComplInv(real a, real p) |
|---|
| 1227 |
in { |
|---|
| 1228 |
assert(p>=0 && p<= 1); |
|---|
| 1229 |
assert(a>0); |
|---|
| 1230 |
} |
|---|
| 1231 |
body { |
|---|
| 1232 |
if (p==0) return real.infinity; |
|---|
| 1233 |
|
|---|
| 1234 |
real y0 = p; |
|---|
| 1235 |
const real MAXLOGL = 1.1356523406294143949492E4L; |
|---|
| 1236 |
real x0, x1, x, yl, yh, y, d, lgm, dithresh; |
|---|
| 1237 |
int i, dir; |
|---|
| 1238 |
|
|---|
| 1239 |
/* bound the solution */ |
|---|
| 1240 |
x0 = real.max; |
|---|
| 1241 |
yl = 0.0L; |
|---|
| 1242 |
x1 = 0.0L; |
|---|
| 1243 |
yh = 1.0L; |
|---|
| 1244 |
dithresh = 4.0 * real.epsilon; |
|---|
| 1245 |
|
|---|
| 1246 |
/* approximation to inverse function */ |
|---|
| 1247 |
d = 1.0L/(9.0L*a); |
|---|
| 1248 |
y = 1.0L - d - normalDistributionInvImpl(y0) * sqrt(d); |
|---|
| 1249 |
x = a * y * y * y; |
|---|
| 1250 |
|
|---|
| 1251 |
lgm = logGamma(a); |
|---|
| 1252 |
|
|---|
| 1253 |
for( i=0; i<10; i++ ) { |
|---|
| 1254 |
if( x > x0 || x < x1 ) |
|---|
| 1255 |
goto ihalve; |
|---|
| 1256 |
y = gammaIncompleteCompl(a,x); |
|---|
| 1257 |
if ( y < yl || y > yh ) |
|---|
| 1258 |
goto ihalve; |
|---|
| 1259 |
if ( y < y0 ) { |
|---|
| 1260 |
x0 = x; |
|---|
| 1261 |
yl = y; |
|---|
| 1262 |
} else { |
|---|
| 1263 |
x1 = x; |
|---|
| 1264 |
yh = y; |
|---|
| 1265 |
} |
|---|
| 1266 |
/* compute the derivative of the function at this point */ |
|---|
| 1267 |
d = (a - 1.0L) * log(x0) - x0 - lgm; |
|---|
| 1268 |
if ( d < -MAXLOGL ) |
|---|
| 1269 |
goto ihalve; |
|---|
| 1270 |
d = -exp(d); |
|---|
| 1271 |
/* compute the step to the next approximation of x */ |
|---|
| 1272 |
d = (y - y0)/d; |
|---|
| 1273 |
x = x - d; |
|---|
| 1274 |
if ( i < 3 ) continue; |
|---|
| 1275 |
if ( fabs(d/x) < dithresh ) return x; |
|---|
| 1276 |
} |
|---|
| 1277 |
|
|---|
| 1278 |
/* Resort to interval halving if Newton iteration did not converge. */ |
|---|
| 1279 |
ihalve: |
|---|
| 1280 |
d = 0.0625L; |
|---|
| 1281 |
if ( x0 == real.max ) { |
|---|
| 1282 |
if( x <= 0.0L ) |
|---|
| 1283 |
x = 1.0L; |
|---|
| 1284 |
while( x0 == real.max ) { |
|---|
| 1285 |
x = (1.0L + d) * x; |
|---|
| 1286 |
y = gammaIncompleteCompl( a, x ); |
|---|
| 1287 |
if ( y < y0 ) { |
|---|
| 1288 |
x0 = x; |
|---|
| 1289 |
yl = y; |
|---|
| 1290 |
break; |
|---|
| 1291 |
} |
|---|
| 1292 |
d = d + d; |
|---|
| 1293 |
} |
|---|
| 1294 |
} |
|---|
| 1295 |
d = 0.5L; |
|---|
| 1296 |
dir = 0; |
|---|
| 1297 |
|
|---|
| 1298 |
for( i=0; i<400; i++ ) { |
|---|
| 1299 |
x = x1 + d * (x0 - x1); |
|---|
| 1300 |
y = gammaIncompleteCompl( a, x ); |
|---|
| 1301 |
lgm = (x0 - x1)/(x1 + x0); |
|---|
| 1302 |
if ( fabs(lgm) < dithresh ) |
|---|
| 1303 |
break; |
|---|
| 1304 |
lgm = (y - y0)/y0; |
|---|
| 1305 |
if ( fabs(lgm) < dithresh ) |
|---|
| 1306 |
break; |
|---|
| 1307 |
if ( x <= 0.0L ) |
|---|
| 1308 |
break; |
|---|
| 1309 |
if ( y > y0 ) { |
|---|
| 1310 |
x1 = x; |
|---|
| 1311 |
yh = y; |
|---|
| 1312 |
if ( dir < 0 ) { |
|---|
| 1313 |
dir = 0; |
|---|
| 1314 |
d = 0.5L; |
|---|
| 1315 |
} else if ( dir > 1 ) |
|---|
| 1316 |
d = 0.5L * d + 0.5L; |
|---|
| 1317 |
else |
|---|
| 1318 |
d = (y0 - yl)/(yh - yl); |
|---|
| 1319 |
dir += 1; |
|---|
| 1320 |
} else { |
|---|
| 1321 |
x0 = x; |
|---|
| 1322 |
yl = y; |
|---|
| 1323 |
if ( dir > 0 ) { |
|---|
| 1324 |
dir = 0; |
|---|
| 1325 |
d = 0.5L; |
|---|
| 1326 |
} else if ( dir < -1 ) |
|---|
| 1327 |
d = 0.5L * d; |
|---|
| 1328 |
else |
|---|
| 1329 |
d = (y0 - yl)/(yh - yl); |
|---|
| 1330 |
dir -= 1; |
|---|
| 1331 |
} |
|---|
| 1332 |
} |
|---|
| 1333 |
/+ |
|---|
| 1334 |
if( x == 0.0L ) |
|---|
| 1335 |
mtherr( "igamil", UNDERFLOW ); |
|---|
| 1336 |
+/ |
|---|
| 1337 |
return x; |
|---|
| 1338 |
} |
|---|
| 1339 |
|
|---|
| 1340 |
debug(UnitTest) { |
|---|
| 1341 |
unittest { |
|---|
| 1342 |
//Values from Excel's GammaInv(1-p, x, 1) |
|---|
| 1343 |
assert(fabs(gammaIncompleteComplInv(1, 0.5) - 0.693147188044814) < 0.00000005); |
|---|
| 1344 |
assert(fabs(gammaIncompleteComplInv(12, 0.99) - 5.42818075054289) < 0.00000005); |
|---|
| 1345 |
assert(fabs(gammaIncompleteComplInv(100, 0.8) - 91.5013985848288L) < 0.000005); |
|---|
| 1346 |
|
|---|
| 1347 |
assert(gammaIncomplete(1, 0)==0); |
|---|
| 1348 |
assert(gammaIncompleteCompl(1, 0)==1); |
|---|
| 1349 |
assert(gammaIncomplete(4545, real.infinity)==1); |
|---|
| 1350 |
|
|---|
| 1351 |
// Values from Excel's (1-GammaDist(x, alpha, 1, TRUE)) |
|---|
| 1352 |
|
|---|
| 1353 |
assert(fabs(1.0L-gammaIncompleteCompl(0.5, 2) - 0.954499729507309L) < 0.00000005); |
|---|
| 1354 |
assert(fabs(gammaIncomplete(0.5, 2) - 0.954499729507309L) < 0.00000005); |
|---|
| 1355 |
// Fixed Cephes bug: |
|---|
| 1356 |
assert(gammaIncompleteCompl(384, real.infinity)==0); |
|---|
| 1357 |
assert(gammaIncompleteComplInv(3, 0)==real.infinity); |
|---|
| 1358 |
} |
|---|
| 1359 |
} |
|---|
| 1360 |
|
|---|
| 1361 |
/** Digamma function |
|---|
| 1362 |
* |
|---|
| 1363 |
* The digamma function is the logarithmic derivative of the gamma function. |
|---|
| 1364 |
* |
|---|
| 1365 |
* digamma(x) = d/dx logGamma(x) |
|---|
| 1366 |
* |
|---|
| 1367 |
*/ |
|---|
| 1368 |
real digamma(real x) |
|---|
| 1369 |
{ |
|---|
| 1370 |
// Based on CEPHES, Stephen L. Moshier. |
|---|
| 1371 |
|
|---|
| 1372 |
// DAC: These values are Bn / n for n=2,4,6,8,10,12,14. |
|---|
| 1373 |
const real [] Bn_n = [ |
|---|
| 1374 |
1.0L/(6*2), -1.0L/(30*4), 1.0L/(42*6), -1.0L/(30*8), |
|---|
| 1375 |
5.0L/(66*10), -691.0L/(2730*12), 7.0L/(6*14) ]; |
|---|
| 1376 |
|
|---|
| 1377 |
real p, q, nz, s, w, y, z; |
|---|
| 1378 |
int i, n, negative; |
|---|
| 1379 |
|
|---|
| 1380 |
negative = 0; |
|---|
| 1381 |
nz = 0.0; |
|---|
| 1382 |
|
|---|
| 1383 |
if ( x <= 0.0 ) { |
|---|
| 1384 |
negative = 1; |
|---|
| 1385 |
q = x; |
|---|
| 1386 |
p = floor(q); |
|---|
| 1387 |
if( p == q ) { |
|---|
| 1388 |
return NaN(TANGO_NAN.GAMMA_POLE); // singularity. |
|---|
| 1389 |
} |
|---|
| 1390 |
/* Remove the zeros of tan(PI x) |
|---|
| 1391 |
* by subtracting the nearest integer from x |
|---|
| 1392 |
*/ |
|---|
| 1393 |
nz = q - p; |
|---|
| 1394 |
if ( nz != 0.5 ) { |
|---|
| 1395 |
if ( nz > 0.5 ) { |
|---|
| 1396 |
p += 1.0; |
|---|
| 1397 |
nz = q - p; |
|---|
| 1398 |
} |
|---|
| 1399 |
nz = PI/tan(PI*nz); |
|---|
| 1400 |
} else { |
|---|
| 1401 |
nz = 0.0; |
|---|
| 1402 |
} |
|---|
| 1403 |
x = 1.0 - x; |
|---|
| 1404 |
} |
|---|
| 1405 |
|
|---|
| 1406 |
// check for small positive integer |
|---|
| 1407 |
if ((x <= 13.0) && (x == floor(x)) ) { |
|---|
| 1408 |
y = 0.0; |
|---|
| 1409 |
n = rndint(x); |
|---|
| 1410 |
// DAC: CEPHES bugfix. Cephes did this in reverse order, which |
|---|
| 1411 |
// created a larger roundoff error. |
|---|
| 1412 |
for (i=n-1; i>0; --i) { |
|---|
| 1413 |
y+=1.0L/i; |
|---|
| 1414 |
} |
|---|
| 1415 |
y -= EULERGAMMA; |
|---|
| 1416 |
goto done; |
|---|
| 1417 |
} |
|---|
| 1418 |
|
|---|
| 1419 |
s = x; |
|---|
| 1420 |
w = 0.0; |
|---|
| 1421 |
while ( s < 10.0 ) { |
|---|
| 1422 |
w += 1.0/s; |
|---|
| 1423 |
s += 1.0; |
|---|
| 1424 |
} |
|---|
| 1425 |
|
|---|
| 1426 |
if ( s < 1.0e17 ) { |
|---|
| 1427 |
z = 1.0/(s * s); |
|---|
| 1428 |
y = z * poly(z, Bn_n); |
|---|
| 1429 |
} else |
|---|
| 1430 |
y = 0.0; |
|---|
| 1431 |
|
|---|
| 1432 |
y = log(s) - 0.5L/s - y - w; |
|---|
| 1433 |
|
|---|
| 1434 |
done: |
|---|
| 1435 |
if ( negative ) { |
|---|
| 1436 |
y -= nz; |
|---|
| 1437 |
} |
|---|
| 1438 |
return y; |
|---|
| 1439 |
} |
|---|
| 1440 |
|
|---|
| 1441 |
import tango.stdc.stdio; |
|---|
| 1442 |
debug(UnitTest) { |
|---|
| 1443 |
unittest { |
|---|
| 1444 |
// Exact values |
|---|
| 1445 |
assert(digamma(1)== -EULERGAMMA); |
|---|
| 1446 |
assert(feqrel(digamma(0.25), -PI/2 - 3* LN2 - EULERGAMMA)>=real.mant_dig-7); |
|---|
| 1447 |
assert(feqrel(digamma(1.0L/6), -PI/2 *sqrt(3.0L) - 2* LN2 -1.5*log(3.0L) - EULERGAMMA)>=real.mant_dig-7); |
|---|
| 1448 |
assert(digamma(-5)!<>0); |
|---|
| 1449 |
assert(feqrel(digamma(2.5), -EULERGAMMA - 2*LN2 + 2.0 + 2.0L/3)>=real.mant_dig-9); |
|---|
| 1450 |
assert(isIdentical(digamma(NaN(0xABC)), NaN(0xABC))); |
|---|
| 1451 |
|
|---|
| 1452 |
for (int k=1; k<40; ++k) { |
|---|
| 1453 |
real y=0; |
|---|
| 1454 |
for (int u=k; u>=1; --u) { |
|---|
| 1455 |
y+= 1.0L/u; |
|---|
| 1456 |
} |
|---|
| 1457 |
assert(feqrel(digamma(k+1),-EULERGAMMA + y) >=real.mant_dig-2); |
|---|
| 1458 |
} |
|---|
| 1459 |
|
|---|
| 1460 |
// printf("%d %La %La %d %d\n", k+1, digamma(k+1), -EULERGAMMA + x, feqrel(digamma(k+1),-EULERGAMMA + y), feqrel(digamma(k+1), -EULERGAMMA + x)); |
|---|
| 1461 |
} |
|---|
| 1462 |
} |
|---|