gamma.js (4531B)
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 original C code, long comment, copyright, license, and constants are from [Cephes]{@link http://www.netlib.org/cephes}. The implementation follows the original, but has been modified for JavaScript. 22 * 23 * ```text 24 * Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier 25 * 26 * Some software in this archive may be from the book _Methods and Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster International, 1989) or from the Cephes Mathematical Library, a commercial product. In either event, it is copyrighted by the author. What you see here may be used freely but it comes with no support or guarantee. 27 * 28 * Stephen L. Moshier 29 * moshier@na-net.ornl.gov 30 * ``` 31 */ 32 33 'use strict'; 34 35 // MODULES // 36 37 var isnan = require( './../../../../base/assert/is-nan' ); 38 var isInteger = require( './../../../../base/assert/is-integer' ); 39 var isNegativeZero = require( './../../../../base/assert/is-negative-zero' ); 40 var abs = require( './../../../../base/special/abs' ); 41 var floor = require( './../../../../base/special/floor' ); 42 var sin = require( './../../../../base/special/sin' ); 43 var PINF = require( '@stdlib/constants/float64/pinf' ); 44 var NINF = require( '@stdlib/constants/float64/ninf' ); 45 var PI = require( '@stdlib/constants/float64/pi' ); 46 var stirlingApprox = require( './stirling_approximation.js' ); 47 var smallApprox = require( './small_approximation.js' ); 48 var rateval = require( './rational_pq.js' ); 49 50 51 // MAIN // 52 53 /** 54 * Evaluates the gamma function. 55 * 56 * ## Method 57 * 58 * 1. Arguments \\(|x| \leq 34\\) are reduced by recurrence and the function approximated by a rational function of degree \\(6/7\\) in the interval \\((2,3)\\). 59 * 2. Large negative arguments are made positive using a reflection formula. 60 * 3. Large arguments are handled by Stirling's formula. 61 * 62 * 63 * ## Notes 64 * 65 * - Relative error: 66 * 67 * | arithmetic | domain | # trials | peak | rms | 68 * |:----------:|:---------:|:--------:|:-------:|:-------:| 69 * | DEC | -34,34 | 10000 | 1.3e-16 | 2.5e-17 | 70 * | IEEE | -170,-33 | 20000 | 2.3e-15 | 3.3e-16 | 71 * | IEEE | -33, 33 | 20000 | 9.4e-16 | 2.2e-16 | 72 * | IEEE | 33, 171.6 | 20000 | 2.3e-15 | 3.2e-16 | 73 * 74 * - Error for arguments outside the test range will be larger owing to error amplification by the exponential function. 75 * 76 * 77 * @param {number} x - input value 78 * @returns {number} function value 79 * 80 * @example 81 * var v = gamma( 4.0 ); 82 * // returns 6.0 83 * 84 * @example 85 * var v = gamma( -1.5 ); 86 * // returns ~2.363 87 * 88 * @example 89 * var v = gamma( -0.5 ); 90 * // returns ~-3.545 91 * 92 * @example 93 * var v = gamma( 0.5 ); 94 * // returns ~1.772 95 * 96 * @example 97 * var v = gamma( 0.0 ); 98 * // returns Infinity 99 * 100 * @example 101 * var v = gamma( -0.0 ); 102 * // returns -Infinity 103 * 104 * @example 105 * var v = gamma( NaN ); 106 * // returns NaN 107 */ 108 function gamma( x ) { 109 var sign; 110 var q; 111 var p; 112 var z; 113 if ( 114 (isInteger( x ) && x < 0) || 115 x === NINF || 116 isnan( x ) 117 ) { 118 return NaN; 119 } 120 if ( x === 0.0 ) { 121 if ( isNegativeZero( x ) ) { 122 return NINF; 123 } 124 return PINF; 125 } 126 if ( x > 171.61447887182298 ) { 127 return PINF; 128 } 129 if ( x < -170.5674972726612 ) { 130 return 0.0; 131 } 132 q = abs( x ); 133 if ( q > 33.0 ) { 134 if ( x >= 0.0 ) { 135 return stirlingApprox( x ); 136 } 137 p = floor( q ); 138 139 // Check whether `x` is even... 140 if ( (p&1) === 0 ) { 141 sign = -1.0; 142 } else { 143 sign = 1.0; 144 } 145 z = q - p; 146 if ( z > 0.5 ) { 147 p += 1.0; 148 z = q - p; 149 } 150 z = q * sin( PI * z ); 151 return sign * PI / ( abs(z)*stirlingApprox(q) ); 152 } 153 // Reduce `x`... 154 z = 1.0; 155 while ( x >= 3.0 ) { 156 x -= 1.0; 157 z *= x; 158 } 159 while ( x < 0.0 ) { 160 if ( x > -1.0e-9 ) { 161 return smallApprox( x, z ); 162 } 163 z /= x; 164 x += 1.0; 165 } 166 while ( x < 2.0 ) { 167 if ( x < 1.0e-9 ) { 168 return smallApprox( x, z ); 169 } 170 z /= x; 171 x += 1.0; 172 } 173 if ( x === 2.0 ) { 174 return z; 175 } 176 x -= 2.0; 177 return z * rateval( x ); 178 } 179 180 181 // EXPORTS // 182 183 module.exports = gamma;