gammaln.js (10225B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2018 The Stdlib Authors. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 * 19 * ## Notice 20 * 21 * The following copyright, license, and long comment were part of the original implementation available as part of [FreeBSD]{@link https://svnweb.freebsd.org/base/release/9.3.0/lib/msun/src/e_lgamma_r.c}. The implementation follows the original, but has been modified for JavaScript. 22 * 23 * ```text 24 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 25 * 26 * Developed at SunPro, a Sun Microsystems, Inc. business. 27 * Permission to use, copy, modify, and distribute this 28 * software is freely granted, provided that this notice 29 * is preserved. 30 * ``` 31 */ 32 33 'use strict'; 34 35 // MODULES // 36 37 var isnan = require( './../../../../base/assert/is-nan' ); 38 var isInfinite = require( './../../../../base/assert/is-infinite' ); 39 var abs = require( './../../../../base/special/abs' ); 40 var ln = require( './../../../../base/special/ln' ); 41 var trunc = require( './../../../../base/special/trunc' ); 42 var sinpi = require( './../../../../base/special/sinpi' ); 43 var PI = require( '@stdlib/constants/float64/pi' ); 44 var PINF = require( '@stdlib/constants/float64/pinf' ); 45 var polyvalA1 = require( './polyval_a1.js' ); 46 var polyvalA2 = require( './polyval_a2.js' ); 47 var polyvalR = require( './polyval_r.js' ); 48 var polyvalS = require( './polyval_s.js' ); 49 var polyvalT1 = require( './polyval_t1.js' ); 50 var polyvalT2 = require( './polyval_t2.js' ); 51 var polyvalT3 = require( './polyval_t3.js' ); 52 var polyvalU = require( './polyval_u.js' ); 53 var polyvalV = require( './polyval_v.js' ); 54 var polyvalW = require( './polyval_w.js' ); 55 56 57 // VARIABLES // 58 59 var A1C = 7.72156649015328655494e-02; // 0x3FB3C467E37DB0C8 60 var A2C = 3.22467033424113591611e-01; // 0x3FD4A34CC4A60FAD 61 var RC = 1.0; 62 var SC = -7.72156649015328655494e-02; // 0xBFB3C467E37DB0C8 63 var T1C = 4.83836122723810047042e-01; // 0x3FDEF72BC8EE38A2 64 var T2C = -1.47587722994593911752e-01; // 0xBFC2E4278DC6C509 65 var T3C = 6.46249402391333854778e-02; // 0x3FB08B4294D5419B 66 var UC = -7.72156649015328655494e-02; // 0xBFB3C467E37DB0C8 67 var VC = 1.0; 68 var WC = 4.18938533204672725052e-01; // 0x3FDACFE390C97D69 69 var YMIN = 1.461632144968362245; 70 var TWO52 = 4503599627370496; // 2**52 71 var TWO58 = 288230376151711744; // 2**58 72 var TINY = 8.470329472543003e-22; 73 var TC = 1.46163214496836224576e+00; // 0x3FF762D86356BE3F 74 var TF = -1.21486290535849611461e-01; // 0xBFBF19B9BCC38A42 75 var TT = -3.63867699703950536541e-18; // 0xBC50C7CAA48A971F => TT = -(tail of TF) 76 77 78 // MAIN // 79 80 /** 81 * Evaluates the natural logarithm of the gamma function. 82 * 83 * ## Method 84 * 85 * 1. Argument reduction for \\(0 < x \leq 8\\). Since \\(\Gamma(1+s) = s \Gamma(s)\\), for \\(x \in \[0,8]\\), we may reduce \\(x\\) to a number in \\(\[1.5,2.5]\\) by 86 * 87 * ```tex 88 * \operatorname{lgamma}(1+s) = \ln(s) + \operatorname{lgamma}(s) 89 * ``` 90 * 91 * For example, 92 * 93 * ```tex 94 * \begin{align*} 95 * \operatorname{lgamma}(7.3) &= \ln(6.3) + \operatorname{lgamma}(6.3) \\ 96 * &= \ln(6.3 \cdot 5.3) + \operatorname{lgamma}(5.3) \\ 97 * &= \ln(6.3 \cdot 5.3 \cdot 4.3 \cdot 3.3 \cdot2.3) + \operatorname{lgamma}(2.3) 98 * \end{align*} 99 * ``` 100 * 101 * 2. Compute a polynomial approximation of \\(\mathrm{lgamma}\\) around its minimum (\\(\mathrm{ymin} = 1.461632144968362245\\)) to maintain monotonicity. On the interval \\(\[\mathrm{ymin} - 0.23, \mathrm{ymin} + 0.27]\\) (i.e., \\(\[1.23164,1.73163]\\)), we let \\(z = x - \mathrm{ymin}\\) and use 102 * 103 * ```tex 104 * \operatorname{lgamma}(x) = -1.214862905358496078218 + z^2 \cdot \operatorname{poly}(z) 105 * ``` 106 * 107 * where \\(\operatorname{poly}(z)\\) is a \\(14\\) degree polynomial. 108 * 109 * 3. Compute a rational approximation in the primary interval \\(\[2,3]\\). Let \\( s = x - 2.0 \\). We can thus use the approximation 110 * 111 * ```tex 112 * \operatorname{lgamma}(x) = \frac{s}{2} + s\frac{\operatorname{P}(s)}{\operatorname{Q}(s)} 113 * ``` 114 * 115 * with accuracy 116 * 117 * ```tex 118 * \biggl|\frac{\mathrm{P}}{\mathrm{Q}} - \biggr(\operatorname{lgamma}(x)-\frac{s}{2}\biggl)\biggl| < 2^{-61.71} 119 * ``` 120 * 121 * The algorithms are based on the observation 122 * 123 * ```tex 124 * \operatorname{lgamma}(2+s) = s(1 - \gamma) + \frac{\zeta(2) - 1}{2} s^2 - \frac{\zeta(3) - 1}{3} s^3 + \ldots 125 * ``` 126 * 127 * where \\(\zeta\\) is the zeta function and \\(\gamma = 0.5772156649...\\) is the Euler-Mascheroni constant, which is very close to \\(0.5\\). 128 * 129 * 4. For \\(x \geq 8\\), 130 * 131 * ```tex 132 * \operatorname{lgamma}(x) \approx \biggl(x-\frac{1}{2}\biggr) \ln(x) - x + \frac{\ln(2\pi)}{2} + \frac{1}{12x} - \frac{1}{360x^3} + \ldots 133 * ``` 134 * 135 * which can be expressed 136 * 137 * ```tex 138 * \operatorname{lgamma}(x) \approx \biggl(x-\frac{1}{2}\biggr)(\ln(x)-1)-\frac{\ln(2\pi)-1}{2} + \ldots 139 * ``` 140 * 141 * Let \\(z = \frac{1}{x}\\). We can then use the approximation 142 * 143 * ```tex 144 * f(z) = \operatorname{lgamma}(x) - \biggl(x-\frac{1}{2}\biggr)(\ln(x)-1) 145 * ``` 146 * 147 * by 148 * 149 * ```tex 150 * w = w_0 + w_1 z + w_2 z^3 + w_3 z^5 + \ldots + w_6 z^{11} 151 * ``` 152 * 153 * where 154 * 155 * ```tex 156 * |w - f(z)| < 2^{-58.74} 157 * ``` 158 * 159 * 5. For negative \\(x\\), since 160 * 161 * ```tex 162 * -x \Gamma(-x) \Gamma(x) = \frac{\pi}{\sin(\pi x)} 163 * ``` 164 * 165 * where \\(\Gamma\\) is the gamma function, we have 166 * 167 * ```tex 168 * \Gamma(x) = \frac{\pi}{\sin(\pi x)(-x)\Gamma(-x)} 169 * ``` 170 * 171 * Since \\(\Gamma(-x)\\) is positive, 172 * 173 * ```tex 174 * \operatorname{sign}(\Gamma(x)) = \operatorname{sign}(\sin(\pi x)) 175 * ``` 176 * 177 * for \\(x < 0\\). Hence, for \\(x < 0\\), 178 * 179 * ```tex 180 * \mathrm{signgam} = \operatorname{sign}(\sin(\pi x)) 181 * ``` 182 * 183 * and 184 * 185 * ```tex 186 * \begin{align*} 187 * \operatorname{lgamma}(x) &= \ln(|\Gamma(x)|) \\ 188 * &= \ln\biggl(\frac{\pi}{|x \sin(\pi x)|}\biggr) - \operatorname{lgamma}(-x) 189 * \end{align*} 190 * ``` 191 * 192 * <!-- <note> --> 193 * 194 * Note that one should avoid computing \\(\pi (-x)\\) directly in the computation of \\(\sin(\pi (-x))\\). 195 * 196 * <!-- </note> --> 197 * 198 * 199 * ## Special Cases 200 * 201 * ```tex 202 * \begin{align*} 203 * \operatorname{lgamma}(2+s) &\approx s (1-\gamma) & \mathrm{for\ tiny\ s} \\ 204 * \operatorname{lgamma}(x) &\approx -\ln(x) & \mathrm{for\ tiny\ x} \\ 205 * \operatorname{lgamma}(1) &= 0 & \\ 206 * \operatorname{lgamma}(2) &= 0 & \\ 207 * \operatorname{lgamma}(0) &= \infty & \\ 208 * \operatorname{lgamma}(\infty) &= \infty & \\ 209 * \operatorname{lgamma}(-\mathrm{integer}) &= \pm \infty 210 * \end{align*} 211 * ``` 212 * 213 * 214 * @param {number} x - input value 215 * @returns {number} function value 216 * 217 * @example 218 * var v = gammaln( 1.0 ); 219 * // returns 0.0 220 * 221 * @example 222 * var v = gammaln( 2.0 ); 223 * // returns 0.0 224 * 225 * @example 226 * var v = gammaln( 4.0 ); 227 * // returns ~1.792 228 * 229 * @example 230 * var v = gammaln( -0.5 ); 231 * // returns ~1.266 232 * 233 * @example 234 * var v = gammaln( 0.5 ); 235 * // returns ~0.572 236 * 237 * @example 238 * var v = gammaln( 0.0 ); 239 * // returns Infinity 240 * 241 * @example 242 * var v = gammaln( NaN ); 243 * // returns NaN 244 */ 245 function gammaln( x ) { 246 var isNegative; 247 var nadj; 248 var flg; 249 var p3; 250 var p2; 251 var p1; 252 var p; 253 var q; 254 var t; 255 var w; 256 var y; 257 var z; 258 var r; 259 260 // Special cases: NaN, +-infinity 261 if ( isnan( x ) || isInfinite( x ) ) { 262 return x; 263 } 264 // Special case: 0 265 if ( x === 0.0 ) { 266 return PINF; 267 } 268 if ( x < 0.0 ) { 269 isNegative = true; 270 x = -x; 271 } else { 272 isNegative = false; 273 } 274 // If |x| < 2**-70, return -ln(|x|) 275 if ( x < TINY ) { 276 return -ln( x ); 277 } 278 if ( isNegative ) { 279 // If |x| >= 2**52, must be -integer 280 if ( x >= TWO52 ) { 281 return PINF; 282 } 283 t = sinpi( x ); 284 if ( t === 0.0 ) { 285 return PINF; 286 } 287 nadj = ln( PI / abs( t*x ) ); 288 } 289 // If x equals 1 or 2, return 0 290 if ( x === 1.0 || x === 2.0 ) { 291 return 0.0; 292 } 293 // If x < 2, use lgamma(x) = lgamma(x+1) - log(x) 294 if ( x < 2.0 ) { 295 if ( x <= 0.9 ) { 296 r = -ln( x ); 297 298 // 0.7316 <= x <= 0.9 299 if ( x >= ( YMIN - 1.0 + 0.27 ) ) { 300 y = 1.0 - x; 301 flg = 0; 302 } 303 // 0.2316 <= x < 0.7316 304 else if ( x >= (YMIN - 1.0 - 0.27) ) { 305 y = x - (TC - 1.0); 306 flg = 1; 307 } 308 // 0 < x < 0.2316 309 else { 310 y = x; 311 flg = 2; 312 } 313 } else { 314 r = 0.0; 315 316 // 1.7316 <= x < 2 317 if ( x >= (YMIN + 0.27) ) { 318 y = 2.0 - x; 319 flg = 0; 320 } 321 // 1.2316 <= x < 1.7316 322 else if ( x >= (YMIN - 0.27) ) { 323 y = x - TC; 324 flg = 1; 325 } 326 // 0.9 < x < 1.2316 327 else { 328 y = x - 1.0; 329 flg = 2; 330 } 331 } 332 switch ( flg ) { // eslint-disable-line default-case 333 case 0: 334 z = y * y; 335 p1 = A1C + (z*polyvalA1( z )); 336 p2 = z * (A2C + (z*polyvalA2( z ))); 337 p = (y*p1) + p2; 338 r += ( p - (0.5*y) ); 339 break; 340 case 1: 341 z = y * y; 342 w = z * y; 343 p1 = T1C + (w*polyvalT1( w )); 344 p2 = T2C + (w*polyvalT2( w )); 345 p3 = T3C + (w*polyvalT3( w )); 346 p = (z*p1) - (TT - (w*(p2+(y*p3)))); 347 r += ( TF + p ); 348 break; 349 case 2: 350 p1 = y * (UC + (y*polyvalU( y ))); 351 p2 = VC + (y*polyvalV( y )); 352 r += (-0.5*y) + (p1/p2); 353 break; 354 } 355 } 356 // 2 <= x < 8 357 else if ( x < 8.0 ) { 358 flg = trunc( x ); 359 y = x - flg; 360 p = y * (SC + (y*polyvalS( y ))); 361 q = RC + (y*polyvalR( y )); 362 r = (0.5*y) + (p/q); 363 z = 1.0; // gammaln(1+s) = ln(s) + gammaln(s) 364 switch ( flg ) { // eslint-disable-line default-case 365 case 7: 366 z *= y + 6.0; 367 368 /* falls through */ 369 case 6: 370 z *= y + 5.0; 371 372 /* falls through */ 373 case 5: 374 z *= y + 4.0; 375 376 /* falls through */ 377 case 4: 378 z *= y + 3.0; 379 380 /* falls through */ 381 case 3: 382 z *= y + 2.0; 383 r += ln( z ); 384 } 385 } 386 // 8 <= x < 2**58 387 else if ( x < TWO58 ) { 388 t = ln( x ); 389 z = 1.0 / x; 390 y = z * z; 391 w = WC + (z*polyvalW( y )); 392 r = ((x-0.5)*(t-1.0)) + w; 393 } 394 // 2**58 <= x <= Inf 395 else { 396 r = x * ( ln(x)-1.0 ); 397 } 398 if ( isNegative ) { 399 r = nadj - r; 400 } 401 return r; 402 } 403 404 405 // EXPORTS // 406 407 module.exports = gammaln;