time-to-botec

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

generators.js (5818B)


      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 'use strict';
     20 
     21 // MODULES //
     22 
     23 var abs = require( './../../../../base/special/abs' );
     24 var FLOAT32_SMALLEST_NORMAL = require( '@stdlib/constants/float32/smallest-normal' );
     25 var EPS = require( '@stdlib/constants/float64/eps' );
     26 
     27 
     28 // VARIABLES //
     29 
     30 var MAX_ITER = 1000000;
     31 
     32 
     33 // FUNCTIONS //
     34 
     35 /**
     36 * Evaluates a continued fraction expansion.
     37 *
     38 * ```text
     39 *           a1
     40 *      ---------------
     41 *      b1 +     a2
     42 *           ----------
     43 *            b2 +   a3
     44 *                -----
     45 *                b3 + ...
     46 * ```
     47 *
     48 * @private
     49 * @param {Function} gen - function giving terms of continued fraction expansion
     50 * @param {PositiveNumber} factor - further terms are only added as long as factor*result is smaller than the next term
     51 * @param {PositiveInteger} maxIter - maximum number of iterations
     52 * @returns {number} evaluated expansion
     53 */
     54 function continuedFractionA( gen, factor, maxIter ) {
     55 	var isgenerator;
     56 	var delta;
     57 	var a0;
     58 	var f;
     59 	var C;
     60 	var D;
     61 	var v;
     62 
     63 	isgenerator = typeof gen.next === 'function';
     64 	v = ( isgenerator ) ? gen.next().value : gen();
     65 	f = v[ 1 ];
     66 	a0 = v[ 0 ];
     67 	if ( f === 0.0 ) {
     68 		f = FLOAT32_SMALLEST_NORMAL;
     69 	}
     70 	C = f;
     71 	D = 0;
     72 	if ( isgenerator === true ) {
     73 		do {
     74 			v = gen.next().value;
     75 			if ( v ) {
     76 				D = v[ 1 ] + ( v[ 0 ] * D );
     77 				if ( D === 0.0 ) {
     78 					D = FLOAT32_SMALLEST_NORMAL;
     79 				}
     80 				C = v[ 1 ] + ( v[ 0 ] / C );
     81 				if ( C === 0.0 ) {
     82 					C = FLOAT32_SMALLEST_NORMAL;
     83 				}
     84 				D = 1.0 / D;
     85 				delta = C * D;
     86 				f *= delta;
     87 			}
     88 		} while ( ( abs( delta - 1.0 ) > factor ) && --maxIter ); // eslint-disable-line no-plusplus
     89 	} else {
     90 		do {
     91 			v = gen();
     92 			if ( v ) {
     93 				D = v[ 1 ] + ( v[ 0 ] * D );
     94 				if ( D === 0.0 ) {
     95 					D = FLOAT32_SMALLEST_NORMAL;
     96 				}
     97 				C = v[ 1 ] + ( v[ 0 ] / C );
     98 				if ( C === 0.0 ) {
     99 					C = FLOAT32_SMALLEST_NORMAL;
    100 				}
    101 				D = 1.0 / D;
    102 				delta = C * D;
    103 				f *= delta;
    104 			}
    105 		} while ( v && ( abs( delta - 1.0 ) > factor ) && --maxIter ); // eslint-disable-line no-plusplus
    106 	}
    107 	return a0 / f;
    108 }
    109 
    110 /**
    111 * Evaluates a continued fraction expansion.
    112 *
    113 * ```text
    114 *      b0 +    a1
    115 *      ---------------
    116 *      b1 +     a2
    117 *           ----------
    118 *           b2 +   a3
    119 *                -----
    120 *                b3 + ...
    121 * ```
    122 *
    123 * @private
    124 * @param {Function} gen - function giving terms of continued fraction expansion
    125 * @param {PositiveNumber} factor - further terms are only added as long as factor*result is smaller than the next term
    126 * @param {PositiveInteger} maxIter - maximum number of iterations
    127 * @returns {number} evaluated expansion
    128 */
    129 function continuedFractionB( gen, factor, maxIter ) {
    130 	var isgenerator;
    131 	var delta;
    132 	var f;
    133 	var C;
    134 	var D;
    135 	var v;
    136 
    137 	isgenerator = typeof gen.next === 'function';
    138 	v = ( isgenerator ) ? gen.next().value : gen();
    139 	f = v[ 1 ];
    140 	if ( f === 0.0 ) {
    141 		f = FLOAT32_SMALLEST_NORMAL;
    142 	}
    143 	C = f;
    144 	D = 0.0;
    145 	if ( isgenerator === true ) {
    146 		do {
    147 			v = gen.next().value;
    148 			if ( v ) {
    149 				D = v[ 1 ] + ( v[ 0 ] * D );
    150 				if ( D === 0.0 ) {
    151 					D = FLOAT32_SMALLEST_NORMAL;
    152 				}
    153 				C = v[ 1 ] + ( v[ 0 ] / C );
    154 				if ( C === 0.0 ) {
    155 					C = FLOAT32_SMALLEST_NORMAL;
    156 				}
    157 				D = 1.0 / D;
    158 				delta = C * D;
    159 				f *= delta;
    160 			}
    161 		} while ( v && ( abs( delta - 1.0 ) > factor ) && --maxIter ); // eslint-disable-line no-plusplus
    162 	} else {
    163 		do {
    164 			v = gen();
    165 			if ( v ) {
    166 				D = v[ 1 ] + ( v[ 0 ] * D );
    167 				if ( D === 0.0 ) {
    168 					D = FLOAT32_SMALLEST_NORMAL;
    169 				}
    170 				C = v[ 1 ] + ( v[ 0 ] / C );
    171 				if ( C === 0.0 ) {
    172 					C = FLOAT32_SMALLEST_NORMAL;
    173 				}
    174 				D = 1.0 / D;
    175 				delta = C * D;
    176 				f *= delta;
    177 			}
    178 		} while ( v && ( abs( delta - 1.0 ) > factor ) && --maxIter ); // eslint-disable-line no-plusplus
    179 	}
    180 	return f;
    181 }
    182 
    183 
    184 // MAIN //
    185 
    186 /**
    187 * Evaluates the continued fraction approximation for the supplied series generator using the modified Lentz algorithm.
    188 *
    189 * ## References
    190 *
    191 * -   Lentz, William J. 1976. "Generating bessel functions in Mie scattering calculations using continued fractions." _Applied Optics_ 15 (3): 668–71. doi:[10.1364/AO.15.000668](https://doi.org/10.1364/AO.15.000668).
    192 *
    193 * @param {Function} generator - function returning terms of continued fraction expansion
    194 * @param {Object} [options] - function options
    195 * @param {PositiveInteger} [options.maxIter=1000] - maximum number of iterations
    196 * @param {PositiveNumber} [options.tolerance=2.22e-16] - further terms are only added as long as the next term is greater than current term times the tolerance
    197 * @param {boolean} [options.keep=false] - whether to keep the leading b term
    198 * @returns {number} value of continued fraction
    199 *
    200 * @example
    201 * // Continued fraction for (e-1)^(-1):
    202 * var gen = generator();
    203 * var out = continuedFraction( gen );
    204 * // returns ~0.582
    205 *
    206 * function* generator() {
    207 *    var i = 0;
    208 *    while ( true ) {
    209 *        i++;
    210 *        yield [ i, i ];
    211 *    }
    212 * }
    213 */
    214 function continuedFraction( generator, options ) {
    215 	var maxIter;
    216 	var opts;
    217 	var eps;
    218 
    219 	opts = {};
    220 	if ( arguments.length > 1 ) {
    221 		opts = options;
    222 	}
    223 	maxIter = opts.maxIter || MAX_ITER;
    224 	eps = opts.tolerance || EPS;
    225 
    226 	if ( opts.keep ) {
    227 		return continuedFractionB( generator, eps, maxIter );
    228 	}
    229 	return continuedFractionA( generator, eps, maxIter );
    230 }
    231 
    232 
    233 // EXPORTS //
    234 
    235 module.exports = continuedFraction;