time-to-botec

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

polygamma.js (4637B)


      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 and copyright notice are from the [Boost library]{@link http://www.boost.org/doc/libs/1_65_0/boost/math/special_functions/detail/polygamma.hpp}. The implementation follows the original but has been modified for JavaScript.
     22 *
     23 * ```text
     24 * (C) Copyright Nikhar Agrawal 2013.
     25 * (C) Copyright Christopher Kormanyos 2013.
     26 * (C) Copyright John Maddock 2014.
     27 * (C) Copyright Paul Bristow 2013.
     28 *
     29 * Use, modification and distribution are subject to the
     30 * Boost Software License, Version 1.0. (See accompanying file
     31 * LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
     32 * ```
     33 */
     34 
     35 'use strict';
     36 
     37 // MODULES //
     38 
     39 var logger = require( 'debug' );
     40 var isNonNegativeInteger = require( './../../../../base/assert/is-nonnegative-integer' );
     41 var factorial = require( './../../../../base/special/factorial' );
     42 var trigamma = require( './../../../../base/special/trigamma' );
     43 var digamma = require( './../../../../base/special/digamma' );
     44 var signum = require( './../../../../base/special/signum' );
     45 var ldexp = require( './../../../../base/special/ldexp' );
     46 var floor = require( './../../../../base/special/floor' );
     47 var trunc = require( './../../../../base/special/trunc' );
     48 var zeta = require( './../../../../base/special/riemann-zeta' );
     49 var abs = require( './../../../../base/special/abs' );
     50 var min = require( './../../../../base/special/min' );
     51 var PINF = require( '@stdlib/constants/float64/pinf' );
     52 var NINF = require( '@stdlib/constants/float64/ninf' );
     53 var MAX = require( '@stdlib/constants/float64/max' );
     54 var PI = require( '@stdlib/constants/float64/pi' );
     55 var attransitionplus = require( './attransitionplus.js' );
     56 var atinfinityplus = require( './atinfinityplus.js' );
     57 var polycotpi = require( './polycotpi.js' );
     58 var nearzero = require( './nearzero.js' );
     59 
     60 
     61 // VARIABLES //
     62 
     63 var debug = logger( 'polygamma' );
     64 var DIGITS_BASE10 = 19;
     65 
     66 
     67 // MAIN //
     68 
     69 /**
     70 * Evaluates the polygamma function.
     71 *
     72 * @param {NonNegativeInteger} n - order of derivative
     73 * @param {number} x - input value
     74 * @returns {number} (n+1)'th derivative
     75 *
     76 * @example
     77 * var v = polygamma( 3, 1.2 );
     78 * // returns ~3.245
     79 *
     80 * @example
     81 * var v = polygamma( 5, 1.2 );
     82 * // returns ~41.39
     83 *
     84 * @example
     85 * var v = polygamma( 3, -4.9 );
     86 * // returns ~60014.239
     87 *
     88 * @example
     89 * var v = polygamma( 2.5, -1.2 );
     90 * // returns NaN
     91 *
     92 * @example
     93 * var v = polygamma( -1, 5.3 );
     94 * // returns NaN
     95 *
     96 * @example
     97 * var v = polygamma( 2, -2.0 );
     98 * // returns NaN
     99 *
    100 * @example
    101 * var v = polygamma( NaN, 2.1 );
    102 * // returns NaN
    103 *
    104 * @example
    105 * var v = polygamma( 1, NaN );
    106 * // returns NaN
    107 *
    108 * @example
    109 * var v = polygamma( NaN, NaN );
    110 * // returns NaN
    111 */
    112 function polygamma( n, x ) {
    113 	var xSmallLimit;
    114 	var result;
    115 	var z;
    116 
    117 	if ( !isNonNegativeInteger( n ) ) {
    118 		return NaN;
    119 	}
    120 	if ( n === 0 ) {
    121 		return digamma( x );
    122 	}
    123 	if ( n === 1 ) {
    124 		return trigamma( x );
    125 	}
    126 	if ( x < 0.0 ) {
    127 		if ( floor(x) === x ) {
    128 			// Result is infinity if `x` is odd, and a pole error if `x` is even.
    129 			if ( trunc( x ) & 1 ) {
    130 				return PINF;
    131 			}
    132 			debug( 'Evaluation at negative integer: %d.', x );
    133 			return NaN;
    134 		}
    135 		z = 1.0 - x;
    136 		result = polygamma( n, z ) + ( PI * polycotpi( n, z, x ) );
    137 		return ( n & 1 ) ? -result : result;
    138 	}
    139 	// Limit for use of small-x series is chosen so that the series doesn't go too divergent in the first few terms. Ordinarily, this would mean setting the limit to `~1/n`, but we can tolerate a small amount of divergence:
    140 	xSmallLimit = min( 5.0/n, 0.25 );
    141 	if ( x < xSmallLimit ) {
    142 		return nearzero( n, x );
    143 	}
    144 	if ( x > ( 0.4 * DIGITS_BASE10 ) + ( 4*n ) ) {
    145 		return atinfinityplus( n, x );
    146 	}
    147 	if ( x === 1.0 ) {
    148 		return ( ( n & 1 ) ? 1.0 : -1.0 ) * factorial( n ) * zeta( n+1 );
    149 	}
    150 	if ( x === 0.5 ) {
    151 		result = ( ( n & 1 ) ? 1.0 : -1.0 ) * factorial( n ) * zeta( n+1 );
    152 		if ( abs( result ) >= ldexp( MAX, -n-1 ) ) {
    153 			return ( signum( result ) === 1 ) ? PINF : NINF;
    154 		}
    155 		result *= ldexp( 1.0, n+1 ) - 1.0;
    156 		return result;
    157 	}
    158 	return attransitionplus( n, x );
    159 }
    160 
    161 
    162 // EXPORTS //
    163 
    164 module.exports = polygamma;