time-to-botec

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

factory.js (5164B)


      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 evalrational = require( './evalrational.js' );
     24 
     25 
     26 // MAIN //
     27 
     28 /**
     29 * Generates a function for evaluating a rational function.
     30 *
     31 * ## Notes
     32 *
     33 * -   The compiled function uses [Horner's rule][horners-method] for efficient computation.
     34 *
     35 * [horners-method]: http://en.wikipedia.org/wiki/Horner%27s_method
     36 *
     37 *
     38 * @param {NumericArray} P - numerator polynomial coefficients sorted in ascending degree
     39 * @param {NumericArray} Q - denominator polynomial coefficients sorted in ascending degree
     40 * @returns {Function} function for evaluating a rational function
     41 *
     42 * @example
     43 * var P = [ 20.0, 8.0, 3.0 ];
     44 * var Q = [ 10.0, 9.0, 1.0 ];
     45 *
     46 * var rational = factory( P, Q );
     47 *
     48 * var v = rational( 10.0 ); // => (20*10^0 + 8*10^1 + 3*10^2) / (10*10^0 + 9*10^1 + 1*10^2) = (20+80+300)/(10+90+100)
     49 * // returns 2.0
     50 *
     51 * v = rational( 2.0 ); // => (20*2^0 + 8*2^1 + 3*2^2) / (10*2^0 + 9*2^1 + 1*2^2) = (20+16+12)/(10+18+4)
     52 * // returns 1.5
     53 */
     54 function factory( P, Q ) {
     55 	var f;
     56 	var r;
     57 	var n;
     58 	var m;
     59 	var i;
     60 
     61 	// Avoid exceeding maximum stack size on V8 :(. Note that the value of `500` was empirically determined...
     62 	if ( P.length > 500 ) {
     63 		return rational;
     64 	}
     65 	// Code generation. Start with the function definition...
     66 	f = 'return function evalrational(x){';
     67 
     68 	// Create the function body...
     69 	n = P.length;
     70 
     71 	// Declare variables...
     72 	f += 'var ax,s1,s2;';
     73 
     74 	// If no coefficients, the function always returns NaN...
     75 	if ( n === 0 ) {
     76 		f += 'return NaN;';
     77 	}
     78 	// If P and Q have different lengths, the function always returns NaN...
     79 	else if ( n !== Q.length ) {
     80 		f += 'return NaN;';
     81 	}
     82 	// If P and Q have only one coefficient, the function always returns the ratio of the first coefficients...
     83 	else if ( n === 1 ) {
     84 		r = P[ 0 ] / Q[ 0 ];
     85 		f += 'return ' + r + ';';
     86 	}
     87 	// If more than one coefficient, apply Horner's method to both the numerator and denominator...
     88 	else {
     89 		// If `x == 0`, return the ratio of the first coefficients...
     90 		r = P[ 0 ] / Q[ 0 ];
     91 		f += 'if(x===0.0){return ' + r + ';}';
     92 
     93 		// Compute the absolute value of `x`...
     94 		f += 'if(x<0.0){ax=-x;}else{ax=x;}';
     95 
     96 		// If `abs(x) <= 1`, evaluate the numerator and denominator of the rational function using Horner's method...
     97 		f += 'if(ax<=1.0){';
     98 		f += 's1 = ' + P[ 0 ];
     99 		m = n - 1;
    100 		for ( i = 1; i < n; i++ ) {
    101 			f += '+x*';
    102 			if ( i < m ) {
    103 				f += '(';
    104 			}
    105 			f += P[ i ];
    106 		}
    107 		// Close all the parentheses...
    108 		for ( i = 0; i < m-1; i++ ) {
    109 			f += ')';
    110 		}
    111 		f += ';';
    112 		f += 's2 = ' + Q[ 0 ];
    113 		m = n - 1;
    114 		for ( i = 1; i < n; i++ ) {
    115 			f += '+x*';
    116 			if ( i < m ) {
    117 				f += '(';
    118 			}
    119 			f += Q[ i ];
    120 		}
    121 		// Close all the parentheses...
    122 		for ( i = 0; i < m-1; i++ ) {
    123 			f += ')';
    124 		}
    125 		f += ';';
    126 
    127 		// Close the if statement...
    128 		f += '}else{';
    129 
    130 		// If `abs(x) > 1`, evaluate the numerator and denominator via the inverse to avoid overflow...
    131 		f += 'x = 1.0/x;';
    132 		m = n - 1;
    133 		f += 's1 = ' + P[ m ];
    134 		for ( i = m - 1; i >= 0; i-- ) {
    135 			f += '+x*';
    136 			if ( i > 0 ) {
    137 				f += '(';
    138 			}
    139 			f += P[ i ];
    140 		}
    141 		// Close all the parentheses...
    142 		for ( i = 0; i < m-1; i++ ) {
    143 			f += ')';
    144 		}
    145 		f += ';';
    146 
    147 		m = n - 1;
    148 		f += 's2 = ' + Q[ m ];
    149 		for ( i = m - 1; i >= 0; i-- ) {
    150 			f += '+x*';
    151 			if ( i > 0 ) {
    152 				f += '(';
    153 			}
    154 			f += Q[ i ];
    155 		}
    156 		// Close all the parentheses...
    157 		for ( i = 0; i < m-1; i++ ) {
    158 			f += ')';
    159 		}
    160 		f += ';';
    161 
    162 		// Close the else statement...
    163 		f += '}';
    164 
    165 		// Return the ratio of the two sums...
    166 		f += 'return s1/s2;';
    167 	}
    168 	// Close the function:
    169 	f += '}';
    170 
    171 	// Add a source directive for debugging:
    172 	f += '//# sourceURL=evalrational.factory.js';
    173 
    174 	// Create the function in the global scope:
    175 	return ( new Function( f ) )(); // eslint-disable-line no-new-func
    176 
    177 	/*
    178 	*	function evalrational( x ) {
    179 	*		var ax, s1, s2;
    180 	*		if ( x === 0.0 ) {
    181 	*			return P[0] / Q[0];
    182 	*		}
    183 	*		if ( x < 0.0 ) {
    184 	*			ax = -x;
    185 	*		} else {
    186 	*			ax = x;
    187 	*		}
    188 	*		if ( ax <= 1.0 ) {
    189 	*			s1 = P[0]+x*(P[1]+x*(P[2]+x*(P[3]+...+x*(P[n-2]+x*P[n-1]))));
    190 	*			s2 = Q[0]+x*(Q[1]+x*(Q[2]+x*(Q[3]+...+x*(Q[n-2]+x*Q[n-1]))));
    191 	*		} else {
    192 	*			x = 1.0/x;
    193 	*			s1 = P[n-1]+x*(P[n-2]+x*(P[n-3]+x*(P[n-4]+...+x*(P[1]+x*P[0]))));
    194 	*			s2 = Q[n-1]+x*(Q[n-2]+x*(Q[n-3]+x*(Q[n-4]+...+x*(Q[1]+x*Q[0]))));
    195 	*		}
    196 	*		return s1 / s2;
    197 	*	}
    198 	*/
    199 
    200 	/**
    201 	* Evaluates a rational function.
    202 	*
    203 	* @private
    204 	* @param {number} x - value at which to evaluate a rational function
    205 	* @returns {number} evaluated rational function
    206 	*/
    207 	function rational( x ) {
    208 		return evalrational( P, Q, x );
    209 	}
    210 }
    211 
    212 
    213 // EXPORTS //
    214 
    215 module.exports = factory;