time-to-botec

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

main.js (5784B)


      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 join = require( 'path' ).join;
     24 var readFile = require( '@stdlib/fs/read-file' ).sync;
     25 var replace = require( '@stdlib/string/replace' );
     26 var isInteger = require( '@stdlib/assert/is-integer' ).isPrimitive;
     27 
     28 
     29 // VARIABLES //
     30 
     31 var opts = {
     32 	'encoding': 'utf8'
     33 };
     34 var dir = join( __dirname, 'templates' );
     35 
     36 // Templates:
     37 var COEFFICIENT_RATIO_TEMPLATE = readFile( join( dir, 'coefficient_ratio.js.txt' ), opts ); // eslint-disable-line id-length
     38 var EVALRATIONAL_TEMPLATE = readFile( join( dir, 'evalrational.js.txt' ), opts );
     39 var LOOP_TEMPLATE = readFile( join( dir, 'loop.js.txt' ), opts );
     40 var NAN_TEMPLATE = readFile( join( dir, 'nan.js.txt' ), opts );
     41 var MAX_CHARS = 66; // max-len (80) - chars already in line ('2x tab': 8, 's1 = ': 5, ';': 1)
     42 
     43 
     44 // FUNCTIONS //
     45 
     46 /**
     47 * Serializes a single value to a string.
     48 *
     49 * @private
     50 * @param {number} x - value to serialize
     51 * @returns {string} serialized value
     52 */
     53 function value2string( x ) {
     54 	var str = x.toString();
     55 	if ( isInteger( x ) ) {
     56 		str += '.0';
     57 	}
     58 	return str;
     59 }
     60 
     61 /**
     62 * Serializes an array of numbers to an indented newline separated list.
     63 *
     64 * @private
     65 * @param {NumericArray} x - array of numbers
     66 * @returns {string} serialized value
     67 */
     68 function array2list( x ) {
     69 	var str;
     70 	var n;
     71 	var m;
     72 	var i;
     73 
     74 	n = x.length;
     75 	m = n - 1;
     76 	str = '';
     77 	for ( i = 0; i < n; i++ ) {
     78 		str += '\t' + x[ i ].toString();
     79 		if ( isInteger( x[ i ] ) ) {
     80 			str += '.0';
     81 		}
     82 		if ( i < m ) {
     83 			str += ',\n';
     84 		}
     85 	}
     86 	return str;
     87 }
     88 
     89 /**
     90 * Serializes an array of coefficients to a string implementing Horner's method.
     91 *
     92 * @private
     93 * @param {NumericArray} x - coefficients sorted in ascending degree
     94 * @returns {string} output string
     95 */
     96 function hornerAscending( x ) {
     97 	var str;
     98 	var n;
     99 	var m;
    100 	var i;
    101 
    102 	n = x.length;
    103 	m = n - 1;
    104 	str = x[ 0 ].toString();
    105 	if ( isInteger( x[ 0 ] ) ) {
    106 		str += '.0';
    107 	}
    108 	for ( i = 1; i < n; i++ ) {
    109 		str += ' + (x * ';
    110 		if ( i < m ) {
    111 			str += '(';
    112 		}
    113 		str += x[ i ].toString();
    114 		if ( isInteger( x[ i ] ) ) {
    115 			str += '.0';
    116 		}
    117 	}
    118 	// Close all the parentheses...
    119 	for ( i = 0; i < (2*m)-1; i++ ) {
    120 		str += ')';
    121 	}
    122 	return str;
    123 }
    124 
    125 /**
    126 * Serializes an array of coefficients to a string implementing Horner's method.
    127 *
    128 * @private
    129 * @param {NumericArray} x - coefficients sorted in descending degree
    130 * @returns {string} output string
    131 */
    132 function hornerDescending( x ) {
    133 	var str;
    134 	var m;
    135 	var i;
    136 
    137 	m = x.length - 1;
    138 	str = x[ m ].toString();
    139 	if ( isInteger( x[ m ] ) ) {
    140 		str += '.0';
    141 	}
    142 	for ( i = m-1; i >= 0; i-- ) {
    143 		str += ' + (x * ';
    144 		if ( i > 0 ) {
    145 			str += '(';
    146 		}
    147 		str += x[ i ].toString();
    148 		if ( isInteger( x[ i ] ) ) {
    149 			str += '.0';
    150 		}
    151 	}
    152 	// Close all the parentheses...
    153 	for ( i = 0; i < (2*m)-1; i++ ) {
    154 		str += ')';
    155 	}
    156 	return str;
    157 }
    158 
    159 /**
    160 * Replaces the specified tag in a source string by the chosen replacement and adds directive to disable the maximum line length lint rule if necessary.
    161 *
    162 * @private
    163 * @param {string} src - source string
    164 * @param {string} target - target tag
    165 * @param {string} str - replacement
    166 * @returns {string} output string
    167 */
    168 function replaceString( src, target, str ) {
    169 	var out = replace( src, '{{'+target+'}}', str );
    170 	if ( str.length > MAX_CHARS ) {
    171 		out = replace( out, '{{'+target+'_ESLINT}}', ' // eslint-disable-line max-len' );
    172 	} else {
    173 		out = replace( out, '{{'+target+'_ESLINT}}', '' );
    174 	}
    175 	return out;
    176 }
    177 
    178 
    179 // MAIN //
    180 
    181 /**
    182 * Compiles a module string which exports a function for evaluating a rational function.
    183 *
    184 * @param {NumericArray} P - numerator polynomial coefficients sorted in ascending degree
    185 * @param {NumericArray} Q - denominator polynomial coefficients sorted in ascending degree
    186 * @returns {string} module string exporting a function for evaluating a rational function
    187 *
    188 * @example
    189 * var P = [ -6.0, -5.0 ];
    190 * var Q = [ 3.0, 0.5 ];
    191 *
    192 * var str = compile( P, Q );
    193 * // returns <string>
    194 */
    195 function compile( P, Q ) {
    196 	var str;
    197 	var n;
    198 
    199 	n = P.length;
    200 
    201 	// If no coefficients, the function always returns NaN...
    202 	if ( n === 0 ) {
    203 		return NAN_TEMPLATE;
    204 	}
    205 	// If P and Q have different lengths, the function always returns NaN...
    206 	if ( n !== Q.length ) {
    207 		return NAN_TEMPLATE;
    208 	}
    209 	// If P and Q only have one coefficient, the function always returns the ratio of those coefficients...
    210 	if ( n === 1 ) {
    211 		return replace( COEFFICIENT_RATIO_TEMPLATE, '{{ratio}}', value2string( P[0] / Q[0] ) );
    212 	}
    213 	// Avoid exceeding the maximum stack size on V8 by using a simple loop :(. Note that the choice of `500` was empirically determined...
    214 	if ( n > 500 ) {
    215 		str = replace( LOOP_TEMPLATE, '{{P}}', array2list( P ) );
    216 		str = replace( str, '{{Q}}', array2list( Q ) );
    217 		return replace( str, '{{ratio}}', value2string( P[0]/Q[0] ) );
    218 	}
    219 	// If more than one coefficient, apply Horner's method...
    220 	str = replaceString( EVALRATIONAL_TEMPLATE, 'P_ASCENDING', hornerAscending( P ) );
    221 	str = replaceString( str, 'Q_ASCENDING', hornerAscending( Q ) );
    222 	str = replaceString( str, 'P_DESCENDING', hornerDescending( P ) );
    223 	str = replaceString( str, 'Q_DESCENDING', hornerDescending( Q ) );
    224 	return replace( str, '{{ratio}}', value2string( P[0]/Q[0] ) );
    225 }
    226 
    227 
    228 // EXPORTS //
    229 
    230 module.exports = compile;