time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

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;