diff --git a/math/base/tools/evalrational-compile-c/coverage.ndjson b/math/base/tools/evalrational-compile-c/coverage.ndjson new file mode 100644 index 000000000..bd306ccf4 --- /dev/null +++ b/math/base/tools/evalrational-compile-c/coverage.ndjson @@ -0,0 +1 @@ +[300,300,100,38,38,100,5,5,100,300,300,100,"f36b80d61e0e3b083672453d7e783d5e69551540","2024-03-20 13:01:07 -0700"] diff --git a/math/base/tools/evalrational-compile-c/index.html b/math/base/tools/evalrational-compile-c/index.html new file mode 100644 index 000000000..8fed8a032 --- /dev/null +++ b/math/base/tools/evalrational-compile-c/index.html @@ -0,0 +1,131 @@ + + + + +
++ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ ++ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 | 1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x + | /** +* @license Apache-2.0 +* +* Copyright (c) 2022 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +/** +* Compile a C function for evaluating a rational function. +* +* @module @stdlib/math/base/tools/evalrational-compile-c +* +* @example +* var compile = require( '@stdlib/math/base/tools/evalrational-compile-c' ); +* +* var P = [ -6.0, -5.0 ]; +* var Q = [ 3.0, 0.5 ]; +* +* var str = compile( P, Q ); // ( -6*6^0 - 5*6^1 ) / ( 3*6^0 + 0.5*6^1 ) +* // returns <string> +*/ + +// MODULES // + +var main = require( './main.js' ); + + +// EXPORTS // + +module.exports = main; + |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 | 1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +26x +26x +26x +3x +3x +26x +3x +3x +20x +26x +4x +4x +20x +20x +26x +1x +1x +1x +1x +1x +1x +1x +1x +14x +14x +14x +14x +14x +14x +14x +14x +14x +14x +7014x +7014x +6012x +6012x +7014x +7014x +7000x +7000x +7014x +14x +14x +1x +1x +1x +1x +1x +1x +1x +1x +16x +16x +16x +16x +16x +16x +16x +16x +16x +16x +14x +14x +16x +16x +44x +44x +28x +28x +44x +44x +26x +26x +44x +44x +16x +16x +72x +72x +16x +16x +1x +1x +1x +1x +1x +1x +1x +1x +16x +16x +16x +16x +16x +16x +16x +16x +11x +11x +16x +16x +44x +44x +28x +28x +44x +44x +29x +29x +44x +44x +16x +16x +72x +72x +16x +16x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +33x +33x +33x +33x +33x +33x +33x +33x +33x +33x +33x +16x +16x +16x +33x +11x +11x +33x +33x +33x +33x +6x +6x +6x +6x +27x +33x +1x +1x +1x +1x +26x +33x +11x +11x +11x +11x +11x +15x +33x +7x +7x +7x +7x +7x +7x +7x +7x +7x +8x +8x +8x +8x +8x +8x +8x +8x +8x +33x +1x +1x +1x +1x +1x + | /** +* @license Apache-2.0 +* +* Copyright (c) 2022 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var join = require( 'path' ).join; +var readFile = require( '@stdlib/fs/read-file' ).sync; +var replace = require( '@stdlib/string/replace' ); +var isInteger = require( '@stdlib/assert/is-integer' ).isPrimitive; +var uppercase = require( '@stdlib/string/base/uppercase' ); +var PINF = require( '@stdlib/constants/float64/pinf' ); +var NINF = require( '@stdlib/constants/float64/ninf' ); + + +// VARIABLES // + +var opts = { + 'encoding': 'utf8' +}; +var dir = join( __dirname, 'templates' ); + +// Templates: +var COEFFICIENT_RATIO_TEMPLATE = readFile( join( dir, 'coefficient_ratio.c.txt' ), opts ); // eslint-disable-line id-length +var EVALRATIONAL_TEMPLATE = readFile( join( dir, 'evalrational.c.txt' ), opts ); +var LOOP_TEMPLATE = readFile( join( dir, 'loop.c.txt' ), opts ); +var NAN_TEMPLATE = readFile( join( dir, 'nan.c.txt' ), opts ); + + +// FUNCTIONS // + +/** +* Serializes a single value to a string. +* +* @private +* @param {number} x - value to serialize +* @returns {string} serialized value +*/ +function value2string( x ) { + var str; + if ( x === PINF ) { + return '1.0{{dtype_suffix}} / 0.0{{dtype_suffix}}'; + } + if ( x === NINF ) { + return '-1.0{{dtype_suffix}} / 0.0{{dtype_suffix}}'; + } + str = x.toString(); + if ( isInteger( x ) ) { + str += '.0'; + } + str += '{{dtype_suffix}}'; + return str; +} + +/** +* Serializes an array of numbers to an indented newline separated list. +* +* @private +* @param {NumericArray} x - array of numbers +* @returns {string} serialized value +*/ +function array2list( x ) { + var str; + var n; + var m; + var i; + + n = x.length; + m = n - 1; + str = ''; + for ( i = 0; i < n; i++ ) { + str += '\t' + x[ i ].toString(); + if ( isInteger( x[ i ] ) ) { + str += '.0'; + } + str += '{{dtype_suffix}}'; + if ( i < m ) { + str += ',\n'; + } + } + return str; +} + +/** +* Serializes an array of coefficients to a string implementing Horner's method. +* +* @private +* @param {NumericArray} x - coefficients sorted in ascending degree +* @returns {string} output string +*/ +function hornerAscending( x ) { + var str; + var n; + var m; + var i; + + n = x.length; + m = n - 1; + str = x[ 0 ].toString(); + if ( isInteger( x[ 0 ] ) ) { + str += '.0'; + } + str += '{{dtype_suffix}}'; + for ( i = 1; i < n; i++ ) { + str += ' + (x * '; + if ( i < m ) { + str += '('; + } + str += x[ i ].toString(); + if ( isInteger( x[ i ] ) ) { + str += '.0'; + } + str += '{{dtype_suffix}}'; + } + // Close all the parentheses... + for ( i = 0; i < (2*m)-1; i++ ) { + str += ')'; + } + return str; +} + +/** +* Serializes an array of coefficients to a string implementing Horner's method. +* +* @private +* @param {NumericArray} x - coefficients sorted in descending degree +* @returns {string} output string +*/ +function hornerDescending( x ) { + var str; + var m; + var i; + + m = x.length - 1; + str = x[ m ].toString(); + if ( isInteger( x[ m ] ) ) { + str += '.0'; + } + str += '{{dtype_suffix}}'; + for ( i = m-1; i >= 0; i-- ) { + str += ' + (ix * '; + if ( i > 0 ) { + str += '('; + } + str += x[ i ].toString(); + if ( isInteger( x[ i ] ) ) { + str += '.0'; + } + str += '{{dtype_suffix}}'; + } + // Close all the parentheses... + for ( i = 0; i < (2*m)-1; i++ ) { + str += ')'; + } + return str; +} + + +// MAIN // + +/** +* Compiles a C function string for evaluating a rational function. +* +* @param {NumericArray} P - numerator polynomial coefficients sorted in ascending degree +* @param {NumericArray} Q - denominator polynomial coefficients sorted in ascending degree +* @param {Options} [options] - function options +* @param {string} [options.dtype='double'] - input value floating-point data type +* @param {string} [options.name='evalrational'] - function name +* @returns {string} module string exporting a function for evaluating a rational function +* +* @example +* var P = [ -6.0, -5.0 ]; +* var Q = [ 3.0, 0.5 ]; +* +* var str = compile( P, Q ); +* // returns <string> +*/ +function compile( P, Q, options ) { + var opts; + var str; + var n; + + opts = { + 'dtype': 'double', + 'name': 'evalrational', + 'suffix': '' + }; + if ( arguments.length > 2 ) { + opts.dtype = options.dtype || opts.dtype; + opts.name = options.name || opts.name; + } + if ( opts.dtype === 'float' ) { + opts.suffix = 'f'; + } + n = P.length; + + // If no coefficients, the function always returns NaN... + if ( n === 0 ) { + str = replace( NAN_TEMPLATE, '{{dtype}}', opts.dtype ); + str = replace( str, '{{dtype_suffix}}', opts.suffix ); + return replace( str, '{{fname}}', opts.name ); + } + // If P and Q have different lengths, the function always returns NaN... + if ( n !== Q.length ) { + str = replace( NAN_TEMPLATE, '{{dtype}}', opts.dtype ); + str = replace( str, '{{dtype_suffix}}', opts.suffix ); + return replace( str, '{{fname}}', opts.name ); + } + // If P and Q only have one coefficient, the function always returns the ratio of those coefficients... + if ( n === 1 ) { + str = replace( COEFFICIENT_RATIO_TEMPLATE, '{{ratio}}', value2string( P[0] / Q[0] ) ); + str = replace( str, '{{dtype}}', opts.dtype ); + str = replace( str, '{{dtype_suffix}}', opts.suffix ); + return replace( str, '{{fname}}', opts.name ); + } + // Avoid exceeding the maximum stack size on V8 by using a simple loop :(. Note that the choice of `500` was empirically determined... + if ( n > 500 ) { + str = replace( LOOP_TEMPLATE, '{{P}}', array2list( P ) ); + str = replace( str, '{{Q}}', array2list( Q ) ); + str = replace( str, '{{ratio}}', value2string( P[0] / Q[0] ) ); + str = replace( str, '{{num_coefficients}}', n.toString() ); + str = replace( str, '{{dtype}}', opts.dtype ); + str = replace( str, '{{dtype_suffix}}', opts.suffix ); + str = replace( str, '{{fname}}', opts.name ); + return replace( str, '{{FNAME}}', uppercase( opts.name ) ); + } + // If more than one coefficient, apply Horner's method... + str = replace( EVALRATIONAL_TEMPLATE, '{{P_ASCENDING}}', hornerAscending( P ) ); + str = replace( str, '{{Q_ASCENDING}}', hornerAscending( Q ) ); + str = replace( str, '{{P_DESCENDING}}', hornerDescending( P ) ); + str = replace( str, '{{Q_DESCENDING}}', hornerDescending( Q ) ); + str = replace( str, '{{ratio}}', value2string( P[0] / Q[0] ) ); + str = replace( str, '{{dtype}}', opts.dtype ); + str = replace( str, '{{dtype_suffix}}', opts.suffix ); + return replace( str, '{{fname}}', opts.name ); +} + + +// EXPORTS // + +module.exports = compile; + |