time-to-botec

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

zeta.js (8087B)


      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_60_0/boost/math/special_functions/zeta.hpp}. The implementation follows the original, but has been modified for JavaScript.
     22 *
     23 * ```text
     24 * (C) Copyright John Maddock 2006.
     25 *
     26 * Use, modification and distribution are subject to the
     27 * Boost Software License, Version 1.0. (See accompanying file
     28 * LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
     29 * ```
     30 */
     31 
     32 'use strict';
     33 
     34 // MODULES //
     35 
     36 var isnan = require( './../../../../base/assert/is-nan' );
     37 var isInteger = require( './../../../../base/assert/is-integer' );
     38 var abs = require( './../../../../base/special/abs' );
     39 var exp = require( './../../../../base/special/exp' );
     40 var floor = require( './../../../../base/special/floor' );
     41 var gamma = require( './../../../../base/special/gamma' );
     42 var gammaln = require( './../../../../base/special/gammaln' );
     43 var sinpi = require( './../../../../base/special/sinpi' );
     44 var pow = require( './../../../../base/special/pow' );
     45 var ln = require( './../../../../base/special/ln' );
     46 var PINF = require( '@stdlib/constants/float64/pinf' );
     47 var NINF = require( '@stdlib/constants/float64/ninf' );
     48 var TWO_PI = require( '@stdlib/constants/float64/two-pi' );
     49 var SQRT_EPSILON = require( '@stdlib/constants/float64/sqrt-eps' );
     50 var LN_SQRT_TWO_PI = require( '@stdlib/constants/float64/ln-sqrt-two-pi' );
     51 var ODD_POSITIVE_INTEGERS = require( './odd_positive_integers.json' );
     52 var EVEN_NONNEGATIVE_INTEGERS = require( './even_nonnegative_integers.json' );
     53 var BERNOULLI = require( './bernoulli.json' );
     54 var rateval1 = require( './rational_p1q1.js' );
     55 var rateval2 = require( './rational_p2q2.js' );
     56 var rateval3 = require( './rational_p3q3.js' );
     57 var rateval4 = require( './rational_p4q4.js' );
     58 var rateval5 = require( './rational_p5q5.js' );
     59 var rateval6 = require( './rational_p6q6.js' );
     60 
     61 
     62 // VARIABLES //
     63 
     64 var MAX_BERNOULLI_2N = 129;
     65 var MAX_FACTORIAL = 170; // TODO: consider making external constant
     66 var MAX_LN = 709; // TODO: consider making external constant
     67 var Y1 = 1.2433929443359375;
     68 var Y3 = 0.6986598968505859375;
     69 
     70 
     71 // MAIN //
     72 
     73 /**
     74 * Evaluates the Riemann zeta function.
     75 *
     76 * ## Method
     77 *
     78 * 1.  First, we use the reflection formula
     79 *
     80 *     ```tex
     81 *     \zeta(1-s) = 2 \sin\biggl(\frac{\pi(1-s)}{2}\biggr)(2\pi^{-s})\Gamma(s)\zeta(s)
     82 *     ```
     83 *
     84 *     to make \\(s\\) positive.
     85 *
     86 * 2.  For \\(s \in (0,1)\\), we use the approximation
     87 *
     88 *     ```tex
     89 *     \zeta(s) = \frac{C + \operatorname{R}(1-s) - s}{1-s}
     90 *     ```
     91 *
     92 *     with rational approximation \\(\operatorname{R}(1-z)\\) and constant \\(C\\).
     93 *
     94 * 3.  For \\(s \in (1,4)\\), we use the approximation
     95 *
     96 *     ```tex
     97 *     \zeta(s) = C + \operatorname{R}(s-n) + \frac{1}{s-1}
     98 *     ```
     99 *
    100 *     with rational approximation \\(\operatorname{R}(z-n)\\), constant \\(C\\), and integer \\(n\\).
    101 *
    102 * 4.  For \\(s > 4\\), we use the approximation
    103 *
    104 *     ```tex
    105 *     \zeta(s) = 1 + e^{\operatorname{R}(z-n)}
    106 *     ```
    107 *
    108 *     with rational approximation \\(\operatorname{R}(z-n)\\) and integer \\(n\\).
    109 *
    110 * 5.  For negative odd integers, we use the closed form
    111 *
    112 *     ```tex
    113 *     \zeta(-n) = \frac{(-1)^n}{n+1} B_{n+1}
    114 *     ```
    115 *
    116 *     where \\(B_{n+1}\\) is a Bernoulli number.
    117 *
    118 * 6.  For negative even integers, we use the closed form
    119 *
    120 *     ```tex
    121 *     \zeta(-2n) = 0
    122 *     ```
    123 *
    124 * 7.  For nonnegative even integers, we could use the closed form
    125 *
    126 *     ```tex
    127 *     \zeta(2n) = \frac{(-1)^{n-1}2^{2n-1}\pi^{2n}}{(2n)!} B_{2n}
    128 *     ```
    129 *
    130 *     where \\(B_{2n}\\) is a Bernoulli number. However, to speed computation, we use precomputed values (Wolfram Alpha).
    131 *
    132 * 8.  For positive negative integers, we use precomputed values (Wolfram Alpha), as these values are useful for certain infinite series calculations.
    133 *
    134 *
    135 * ## Notes
    136 *
    137 * -   \\(\[\approx 1.5\mbox{e-}8, 1)\\)
    138 *
    139 *     -   max deviation: \\(2.020\mbox{e-}18\\)
    140 *     -   expected error: \\(-2.020\mbox{e-}18\\)
    141 *     -   max error found (double): \\(3.994987\mbox{e-}17\\)
    142 *
    143 * -   \\(\[1,2\]\\)
    144 *
    145 *     -   max deviation: \\(9.007\mbox{e-}20\\)
    146 *     -   expected error: \\(9.007\mbox{e-}20\\)
    147 *
    148 * -   \\((2,4\]\\)
    149 *
    150 *     -   max deviation: \\(5.946\mbox{e-}22\\)
    151 *     -   expected error: \\(-5.946\mbox{e-}22\\)
    152 *
    153 * -   \\((4,7\]\\)
    154 *
    155 *     -   max deviation: \\(2.955\mbox{e-}17\\)
    156 *     -   expected error: \\(2.955\mbox{e-}17\\)
    157 *     -   max error found (double): \\(2.009135\mbox{e-}16\\)
    158 *
    159 * -   \\((7,15)\\)
    160 *
    161 *     -   max deviation: \\(7.117\mbox{e-}16\\)
    162 *     -   expected error: \\(7.117\mbox{e-}16\\)
    163 *     -   max error found (double): \\(9.387771\mbox{e-}16\\)
    164 *
    165 * -   \\(\[15,36)\\)
    166 *
    167 *     -   max error (in interpolated form): \\(1.668\mbox{e-}17\\)
    168 *     -   max error found (long double): \\(1.669714\mbox{e-}17\\)
    169 *
    170 *
    171 * @param {number} s - input value
    172 * @returns {number} function value
    173 *
    174 * @example
    175 * var v = zeta( 1.1 );
    176 * // returns ~10.584
    177 *
    178 * @example
    179 * var v = zeta( -4.0 );
    180 * // returns 0.0
    181 *
    182 * @example
    183 * var v = zeta( 70.0 );
    184 * // returns 1.0
    185 *
    186 * @example
    187 * var v = zeta( 0.5 );
    188 * // returns ~-1.46
    189 *
    190 * @example
    191 * var v = zeta( 1.0 ); // pole
    192 * // returns NaN
    193 *
    194 * @example
    195 * var v = zeta( NaN );
    196 * // returns NaN
    197 */
    198 function zeta( s ) {
    199 	var tmp;
    200 	var sc;
    201 	var as;
    202 	var is;
    203 	var r;
    204 	var n;
    205 
    206 	// Check for `NaN`:
    207 	if ( isnan( s ) ) {
    208 		return NaN;
    209 	}
    210 	// Check for a pole:
    211 	if ( s === 1.0 ) {
    212 		return NaN;
    213 	}
    214 	// Check for large value:
    215 	if ( s >= 56.0 ) {
    216 		return 1.0;
    217 	}
    218 	// Check for a closed form (integers):
    219 	if ( isInteger( s ) ) {
    220 		// Cast `s` to a 32-bit signed integer:
    221 		is = s|0; // asm type annotation
    222 
    223 		// Check that `s` does not exceed MAX_INT32:
    224 		if ( is === s ) {
    225 			if ( is < 0 ) {
    226 				as = (-is)|0; // asm type annotation
    227 
    228 				// Check if even negative integer:
    229 				if ( (as&1) === 0 ) {
    230 					return 0.0;
    231 				}
    232 				n = ( (as+1) / 2 )|0; // asm type annotation
    233 
    234 				// Check if less than max Bernoulli number:
    235 				if ( n <= MAX_BERNOULLI_2N ) {
    236 					return -BERNOULLI[ n ] / (as+1.0);
    237 				}
    238 				// fall through...
    239 			}
    240 			// Check if even nonnegative integer:
    241 			else if ( (is&1) === 0 ) {
    242 				return EVEN_NONNEGATIVE_INTEGERS[ is/2 ];
    243 			}
    244 			// Must be a odd positive integer:
    245 			else {
    246 				return ODD_POSITIVE_INTEGERS[ (is-3)/2 ];
    247 			}
    248 		}
    249 		// fall through...
    250 	}
    251 	if ( abs(s) < SQRT_EPSILON ) {
    252 		return -0.5 - (LN_SQRT_TWO_PI * s);
    253 	}
    254 	sc = 1.0 - s;
    255 	if ( s < 0.0 ) {
    256 		// Check if even negative integer:
    257 		if ( floor(s/2.0) === s/2.0 ) {
    258 			return 0.0;
    259 		}
    260 		// Swap `s` and `sc`:
    261 		tmp = s;
    262 		s = sc;
    263 		sc = tmp;
    264 
    265 		// Determine if computation will overflow:
    266 		if ( s > MAX_FACTORIAL ) {
    267 			tmp = sinpi( 0.5*sc ) * 2.0 * zeta( s );
    268 			r = gammaln( s );
    269 			r -= s * ln( TWO_PI );
    270 			if ( r > MAX_LN ) {
    271 				return ( tmp < 0.0 ) ? NINF : PINF;
    272 			}
    273 			return tmp * exp( r );
    274 		}
    275 		return sinpi( 0.5*sc ) * 2.0 * pow( TWO_PI, -s ) * gamma( s ) * zeta( s ); // eslint-disable-line max-len
    276 	}
    277 	if ( s < 1.0 ) {
    278 		tmp = rateval1( sc );
    279 		tmp -= Y1;
    280 		tmp += sc;
    281 		tmp /= sc;
    282 		return tmp;
    283 	}
    284 	if ( s <= 2.0 ) {
    285 		sc = -sc;
    286 		tmp = 1.0 / sc;
    287 		return tmp + rateval2( sc );
    288 	}
    289 	if ( s <= 4.0 ) {
    290 		tmp = Y3 + ( 1.0 / (-sc) );
    291 		return tmp + rateval3( s-2.0 );
    292 	}
    293 	if ( s <= 7.0 ) {
    294 		tmp = rateval4( s-4.0 );
    295 		return 1.0 + exp( tmp );
    296 	}
    297 	if ( s < 15.0 ) {
    298 		tmp = rateval5( s-7.0 );
    299 		return 1.0 + exp( tmp );
    300 	}
    301 	if ( s < 36.0 ) {
    302 		tmp = rateval6( s-15.0 );
    303 		return 1.0 + exp( tmp );
    304 	}
    305 	// s < 56
    306 	return 1.0 + pow( 2.0, -s );
    307 }
    308 
    309 
    310 // EXPORTS //
    311 
    312 module.exports = zeta;