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;