From bb92ef35663ebf1962d88100c025c1eececa6864 Mon Sep 17 00:00:00 2001 From: stdlib-bot Date: Mon, 25 Mar 2024 01:43:47 +0000 Subject: [PATCH] Auto-generated commit --- .../evalpoly/benchmark/benchmark.factory.js | 29 ++-- .../benchmark/benchmark.factory.length.js | 17 +- base/tools/evalpoly/benchmark/benchmark.js | 19 ++- .../evalpoly/benchmark/benchmark.length.js | 15 +- base/tools/evalpoly/docs/repl.txt | 6 +- base/tools/evalpoly/docs/types/index.d.ts | 12 +- base/tools/evalpoly/docs/types/test.ts | 24 +-- base/tools/evalpoly/lib/factory.js | 2 +- base/tools/evalpoly/lib/index.js | 4 +- base/tools/evalpoly/lib/main.js | 2 +- base/tools/evalpolyf/README.md | 154 ++++++++++++++++++ .../evalpolyf/benchmark/benchmark.factory.js | 87 ++++++++++ .../benchmark/benchmark.factory.length.js | 104 ++++++++++++ base/tools/evalpolyf/benchmark/benchmark.js | 62 +++++++ .../evalpolyf/benchmark/benchmark.length.js | 104 ++++++++++++ .../docs/img/equation_polynomial.svg | 75 +++++++++ base/tools/evalpolyf/docs/repl.txt | 56 +++++++ base/tools/evalpolyf/docs/types/index.d.ts | 122 ++++++++++++++ base/tools/evalpolyf/docs/types/test.ts | 97 +++++++++++ base/tools/evalpolyf/examples/index.js | 69 ++++++++ base/tools/evalpolyf/lib/factory.js | 135 +++++++++++++++ base/tools/evalpolyf/lib/index.js | 60 +++++++ base/tools/evalpolyf/lib/main.js | 71 ++++++++ base/tools/evalpolyf/package.json | 65 ++++++++ base/tools/evalpolyf/test/test.factory.js | 117 +++++++++++++ base/tools/evalpolyf/test/test.js | 38 +++++ base/tools/evalpolyf/test/test.main.js | 71 ++++++++ base/tools/lib/index.js | 9 + 28 files changed, 1553 insertions(+), 73 deletions(-) create mode 100644 base/tools/evalpolyf/README.md create mode 100644 base/tools/evalpolyf/benchmark/benchmark.factory.js create mode 100644 base/tools/evalpolyf/benchmark/benchmark.factory.length.js create mode 100644 base/tools/evalpolyf/benchmark/benchmark.js create mode 100644 base/tools/evalpolyf/benchmark/benchmark.length.js create mode 100644 base/tools/evalpolyf/docs/img/equation_polynomial.svg create mode 100644 base/tools/evalpolyf/docs/repl.txt create mode 100644 base/tools/evalpolyf/docs/types/index.d.ts create mode 100644 base/tools/evalpolyf/docs/types/test.ts create mode 100644 base/tools/evalpolyf/examples/index.js create mode 100644 base/tools/evalpolyf/lib/factory.js create mode 100644 base/tools/evalpolyf/lib/index.js create mode 100644 base/tools/evalpolyf/lib/main.js create mode 100644 base/tools/evalpolyf/package.json create mode 100644 base/tools/evalpolyf/test/test.factory.js create mode 100644 base/tools/evalpolyf/test/test.js create mode 100644 base/tools/evalpolyf/test/test.main.js diff --git a/base/tools/evalpoly/benchmark/benchmark.factory.js b/base/tools/evalpoly/benchmark/benchmark.factory.js index a1849c6da..6f2410910 100644 --- a/base/tools/evalpoly/benchmark/benchmark.factory.js +++ b/base/tools/evalpoly/benchmark/benchmark.factory.js @@ -21,7 +21,7 @@ // MODULES // var bench = require( '@stdlib/bench' ); -var randu = require( '@stdlib/random/base/randu' ); +var uniform = require( '@stdlib/random/array/uniform' ); var isnan = require( './../../../../base/assert/is-nan' ); var pkg = require( './../package.json' ).name; var factory = require( './../lib/factory.js' ); @@ -30,18 +30,18 @@ var factory = require( './../lib/factory.js' ); // MAIN // bench( pkg+'::create:factory', function benchmark( b ) { - var c; + var values; var f; var i; - c = []; - for ( i = 0; i < 10; i++ ) { - c.push( randu() ); - } + values = [ + uniform( 10, 0.0, 100.0 ), + uniform( 10, 0.0, 100.0 ), + uniform( 10, 0.0, 100.0 ) + ]; b.tic(); for ( i = 0; i < b.iterations; i++ ) { - c[ 0 ] = randu(); - f = factory( c ); + f = factory( values[ i%values.length ] ); if ( typeof f !== 'function' ) { b.fail( 'should return a function' ); } @@ -55,22 +55,17 @@ bench( pkg+'::create:factory', function benchmark( b ) { }); bench( pkg+'::evaluate:factory', function benchmark( b ) { - var x; + var values; var v; var f; - var c; var i; - c = []; - for ( i = 0; i < 10; i++ ) { - c.push( randu() ); - } - f = factory( c ); + f = factory( uniform( 10, 0.0, 100.0 ) ); + values = uniform( 10, 0.0, 100.0 ); b.tic(); for ( i = 0; i < b.iterations; i++ ) { - x = randu() * 100.0; - v = f( x ); + v = f( values[ i%values.length ] ); if ( isnan( v ) ) { b.fail( 'should not return NaN' ); } diff --git a/base/tools/evalpoly/benchmark/benchmark.factory.length.js b/base/tools/evalpoly/benchmark/benchmark.factory.length.js index 4914c73b7..894d0b823 100644 --- a/base/tools/evalpoly/benchmark/benchmark.factory.length.js +++ b/base/tools/evalpoly/benchmark/benchmark.factory.length.js @@ -22,7 +22,7 @@ var bench = require( '@stdlib/bench' ); var pow = require( './../../../../base/special/pow' ); -var randu = require( '@stdlib/random/base/randu' ); +var uniform = require( '@stdlib/random/array/uniform' ); var isnan = require( './../../../../base/assert/is-nan' ); var pkg = require( './../package.json' ).name; var factory = require( './../lib/factory.js' ); @@ -38,15 +38,7 @@ var factory = require( './../lib/factory.js' ); * @returns {Function} benchmark function */ function createBenchmark( len ) { - var polyval; - var c; - var i; - - c = []; - for ( i = 0; i < len; i++ ) { - c.push( randu() ); - } - polyval = factory( c ); + var polyval = factory( uniform( len, 0.0, 100.0 ) ); return benchmark; /** @@ -60,10 +52,11 @@ function createBenchmark( len ) { var v; var i; + x = uniform( 10, 0.0, 100.0 ); + b.tic(); for ( i = 0; i < b.iterations; i++ ) { - x = randu() * 100.0; - v = polyval( x ); + v = polyval( x[ i%x.length ] ); if ( isnan( v ) ) { b.fail( 'should not return NaN' ); } diff --git a/base/tools/evalpoly/benchmark/benchmark.js b/base/tools/evalpoly/benchmark/benchmark.js index d451467c6..9062d31ef 100644 --- a/base/tools/evalpoly/benchmark/benchmark.js +++ b/base/tools/evalpoly/benchmark/benchmark.js @@ -21,7 +21,7 @@ // MODULES // var bench = require( '@stdlib/bench' ); -var randu = require( '@stdlib/random/base/randu' ); +var uniform = require( '@stdlib/random/array/uniform' ); var isnan = require( './../../../../base/assert/is-nan' ); var pkg = require( './../package.json' ).name; var evalpoly = require( './../lib' ); @@ -30,20 +30,21 @@ var evalpoly = require( './../lib' ); // MAIN // bench( pkg, function benchmark( b ) { - var c; + var coefs; var x; var v; var i; - c = []; - for ( i = 0; i < 10; i++ ) { - c.push( randu() ); - } + coefs = [ + uniform( 10, 0.0, 100.0 ), + uniform( 10, 0.0, 100.0 ), + uniform( 10, 0.0, 100.0 ) + ]; + x = uniform( 10, 0.0, 100.0 ); + b.tic(); for ( i = 0; i < b.iterations; i++ ) { - c[ 0 ] = randu(); - x = randu() * 100.0; - v = evalpoly( c, x ); + v = evalpoly( coefs[ i%coefs.length ], x[ i%x.length ] ); if ( isnan( v ) ) { b.fail( 'should not return NaN' ); } diff --git a/base/tools/evalpoly/benchmark/benchmark.length.js b/base/tools/evalpoly/benchmark/benchmark.length.js index 30170d780..72eb5a167 100644 --- a/base/tools/evalpoly/benchmark/benchmark.length.js +++ b/base/tools/evalpoly/benchmark/benchmark.length.js @@ -22,7 +22,7 @@ var bench = require( '@stdlib/bench' ); var pow = require( './../../../../base/special/pow' ); -var randu = require( '@stdlib/random/base/randu' ); +var uniform = require( '@stdlib/random/array/uniform' ); var isnan = require( './../../../../base/assert/is-nan' ); var pkg = require( './../package.json' ).name; var evalpoly = require( './../lib' ); @@ -38,13 +38,7 @@ var evalpoly = require( './../lib' ); * @returns {Function} benchmark function */ function createBenchmark( len ) { - var c; - var i; - - c = []; - for ( i = 0; i < len; i++ ) { - c.push( randu() ); - } + var c = uniform( len, 0.0, 100.0 ); return benchmark; /** @@ -58,10 +52,11 @@ function createBenchmark( len ) { var v; var i; + x = uniform( 10, 0.0, 100.0 ); + b.tic(); for ( i = 0; i < b.iterations; i++ ) { - x = randu() * 100.0; - v = evalpoly( c, x ); + v = evalpoly( c, x[ i%x.length ] ); if ( isnan( v ) ) { b.fail( 'should not return NaN' ); } diff --git a/base/tools/evalpoly/docs/repl.txt b/base/tools/evalpoly/docs/repl.txt index 76708f8cb..05749135b 100644 --- a/base/tools/evalpoly/docs/repl.txt +++ b/base/tools/evalpoly/docs/repl.txt @@ -40,14 +40,14 @@ Examples -------- - > var polyval = {{alias}}.factory( [ 3.0, 2.0, 1.0 ] ); + > var f = {{alias}}.factory( [ 3.0, 2.0, 1.0 ] ); // 3*10^0 + 2*10^1 + 1*10^2 - > var v = polyval( 10.0 ) + > var v = f( 10.0 ) 123.0 // 3*5^0 + 2*5^1 + 1*5^2 - > v = polyval( 5.0 ) + > v = f( 5.0 ) 38.0 See Also diff --git a/base/tools/evalpoly/docs/types/index.d.ts b/base/tools/evalpoly/docs/types/index.d.ts index 36fc34c2f..17a300d4d 100644 --- a/base/tools/evalpoly/docs/types/index.d.ts +++ b/base/tools/evalpoly/docs/types/index.d.ts @@ -28,7 +28,7 @@ import { Collection } from '@stdlib/types/array'; * @param x - value at which to evaluate a polynomial * @returns evaluated polynomial */ -type EvaluationFunction = ( x: number ) => number; +type PolynomialFunction = ( x: number ) => number; /** * Interface for evaluating polynomials. @@ -49,7 +49,7 @@ interface EvalPoly { * @returns evaluated polynomial * * @example - * var v = evalpoly( [3.0,2.0,1.0], 10.0 ); // 3*10^0 + 2*10^1 + 1*10^2 + * var v = evalpoly( [ 3.0, 2.0, 1.0 ], 10.0 ); // 3*10^0 + 2*10^1 + 1*10^2 * // returns 123.0 */ ( c: Collection, x: number ): number; @@ -68,7 +68,7 @@ interface EvalPoly { * @returns function for evaluating a polynomial * * @example - * var polyval = evalpoly.factory( [3.0,2.0,1.0] ); + * var polyval = evalpoly.factory( [ 3.0, 2.0, 1.0 ] ); * * var v = polyval( 10.0 ); // => 3*10^0 + 2*10^1 + 1*10^2 * // returns 123.0 @@ -76,7 +76,7 @@ interface EvalPoly { * v = polyval( 5.0 ); // => 3*5^0 + 2*5^1 + 1*5^2 * // returns 38.0 */ - factory( c: Collection ): EvaluationFunction; + factory( c: Collection ): PolynomialFunction; } /** @@ -94,11 +94,11 @@ interface EvalPoly { * @returns evaluated polynomial * * @example -* var v = evalpoly( [3.0,2.0,1.0], 10.0 ); // 3*10^0 + 2*10^1 + 1*10^2 +* var v = evalpoly( [ 3.0, 2.0, 1.0 ], 10.0 ); // 3*10^0 + 2*10^1 + 1*10^2 * // returns 123.0 * * @example -* var polyval = evalpoly.factory( [3.0,2.0,1.0] ); +* var polyval = evalpoly.factory( [ 3.0, 2.0, 1.0 ] ); * * var v = polyval( 10.0 ); // => 3*10^0 + 2*10^1 + 1*10^2 * // returns 123.0 diff --git a/base/tools/evalpoly/docs/types/test.ts b/base/tools/evalpoly/docs/types/test.ts index 7169dffbb..50c01b5fb 100644 --- a/base/tools/evalpoly/docs/types/test.ts +++ b/base/tools/evalpoly/docs/types/test.ts @@ -57,7 +57,17 @@ import evalpoly = require( './index' ); // Attached to main export is a `factory` method which returns a function... { const c = [ 3.0, 2.0, 1.0 ]; - evalpoly.factory( c ); // $ExpectType EvaluationFunction + evalpoly.factory( c ); // $ExpectType PolynomialFunction +} + +// The compiler throws an error if the `factory` method is provided a first argument which is not an array of numbers... +{ + evalpoly.factory( true ); // $ExpectError + evalpoly.factory( false ); // $ExpectError + evalpoly.factory( 'abc' ); // $ExpectError + evalpoly.factory( 123 ); // $ExpectError + evalpoly.factory( {} ); // $ExpectError + evalpoly.factory( ( x: number ): number => x ); // $ExpectError } // The compiler throws an error if the `factory` method is provided an unsupported number of arguments... @@ -74,7 +84,7 @@ import evalpoly = require( './index' ); polyval( 1.0 ); // $ExpectType number } -// The `factory` method returns a function which does not compile if provided a first argument which is not a number... +// The compiler throws an error if the function returned by the `factory` method is provided a first argument which is not a number... { const c = [ 3.0, 2.0, 1.0 ]; const polyval = evalpoly.factory( c ); @@ -85,13 +95,3 @@ import evalpoly = require( './index' ); polyval( {} ); // $ExpectError polyval( ( x: number ): number => x ); // $ExpectError } - -// The compiler throws an error if the `factory` method is provided a first argument which is not an array of numbers... -{ - evalpoly.factory( true ); // $ExpectError - evalpoly.factory( false ); // $ExpectError - evalpoly.factory( 'abc' ); // $ExpectError - evalpoly.factory( 123 ); // $ExpectError - evalpoly.factory( {} ); // $ExpectError - evalpoly.factory( ( x: number ): number => x ); // $ExpectError -} diff --git a/base/tools/evalpoly/lib/factory.js b/base/tools/evalpoly/lib/factory.js index 9962692e3..447d22fa3 100644 --- a/base/tools/evalpoly/lib/factory.js +++ b/base/tools/evalpoly/lib/factory.js @@ -39,7 +39,7 @@ var evalpoly = require( './main.js' ); * @returns {Function} function for evaluating a polynomial * * @example -* var polyval = factory( [3.0,2.0,1.0] ); +* var polyval = factory( [ 3.0, 2.0, 1.0 ] ); * * var v = polyval( 10.0 ); // => 3*10^0 + 2*10^1 + 1*10^2 * // returns 123.0 diff --git a/base/tools/evalpoly/lib/index.js b/base/tools/evalpoly/lib/index.js index 9963243f1..feea42a18 100644 --- a/base/tools/evalpoly/lib/index.js +++ b/base/tools/evalpoly/lib/index.js @@ -26,13 +26,13 @@ * @example * var evalpoly = require( '@stdlib/math/base/tools/evalpoly' ); * -* var v = evalpoly( [3.0,2.0,1.0], 10.0 ); // 3*10^0 + 2*10^1 + 1*10^2 +* var v = evalpoly( [ 3.0, 2.0, 1.0 ], 10.0 ); // 3*10^0 + 2*10^1 + 1*10^2 * // returns 123.0 * * @example * var evalpoly = require( '@stdlib/math/base/tools/evalpoly' ); * -* var polyval = evalpoly.factory( [3.0,2.0,1.0] ); +* var polyval = evalpoly.factory( [ 3.0, 2.0, 1.0 ] ); * * var v = polyval( 10.0 ); // => 3*10^0 + 2*10^1 + 1*10^2 * // returns 123.0 diff --git a/base/tools/evalpoly/lib/main.js b/base/tools/evalpoly/lib/main.js index 9a70a2b32..b114301f6 100644 --- a/base/tools/evalpoly/lib/main.js +++ b/base/tools/evalpoly/lib/main.js @@ -34,7 +34,7 @@ * @returns {number} evaluated polynomial * * @example -* var v = evalpoly( [3.0,2.0,1.0], 10.0 ); // 3*10^0 + 2*10^1 + 1*10^2 +* var v = evalpoly( [ 3.0, 2.0, 1.0 ], 10.0 ); // 3*10^0 + 2*10^1 + 1*10^2 * // returns 123.0 */ function evalpoly( c, x ) { diff --git a/base/tools/evalpolyf/README.md b/base/tools/evalpolyf/README.md new file mode 100644 index 000000000..d9213f55a --- /dev/null +++ b/base/tools/evalpolyf/README.md @@ -0,0 +1,154 @@ + + +# evalpolyf + +> Evaluate a [polynomial][polynomial] using single-precision floating-point arithmetic. + +
+ +A [polynomial][polynomial] in a variable `x` can be expressed as + + + +```math +c_nx^n + c_{n-1}x^{n-1} + \ldots + c_1x^1 + c_0 = \sum_{i=0}^{n} c_ix^i +``` + + + + + +where `c_n, c_{n-1}, ..., c_0` are constants. + +
+ + + +
+ +## Usage + +```javascript +var evalpolyf = require( '@stdlib/math/base/tools/evalpolyf' ); +``` + +#### evalpolyf( c, x ) + +Evaluates a [polynomial][polynomial] having coefficients `c` and degree `n` at a value `x`, where `n = c.length-1`. + +```javascript +var Float32Array = require( '@stdlib/array/float32' ); + +var v = evalpolyf( new Float32Array( [ 3.0, 2.0, 1.0 ] ), 10 ); // => 3*10^0 + 2*10^1 + 1*10^2 +// returns 123.0 +``` + +The coefficients should be ordered in **ascending** degree, thus matching summation notation. + +#### evalpolyf.factory( c ) + +Uses code generation to in-line coefficients and return a function for evaluating a [polynomial][polynomial] using single-precision floating-point arithmetic. + +```javascript +var Float32Array = require( '@stdlib/array/float32' ); + +var polyval = evalpolyf.factory( new Float32Array( [ 3.0, 2.0, 1.0 ] ) ); + +var v = polyval( 10.0 ); // => 3*10^0 + 2*10^1 + 1*10^2 +// returns 123.0 + +v = polyval( 5.0 ); // => 3*5^0 + 2*5^1 + 1*5^2 +// returns 38.0 +``` + +
+ + + +
+ +## Notes + +- For hot code paths in which coefficients are invariant, a compiled function will be more performant than `evalpolyf()`. +- While code generation can boost performance, its use may be problematic in browser contexts enforcing a strict [content security policy][mdn-csp] (CSP). If running in or targeting an environment with a CSP, avoid using code generation. + +
+ + + +
+ +## Examples + + + +```javascript +var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +var uniform = require( '@stdlib/random/base/uniform' ); +var evalpolyf = require( '@stdlib/math/base/tools/evalpolyf' ); + +// Create an array of random coefficients: +var coef = discreteUniform( 10, -100, 100, { + 'dtype': 'float32' +}); + +// Evaluate the polynomial at random values: +var v; +var i; +for ( i = 0; i < 100; i++ ) { + v = uniform( 0.0, 100.0 ); + console.log( 'f(%d) = %d', v, evalpolyf( coef, v ) ); +} + +// Generate an `evalpolyf` function: +var polyval = evalpolyf.factory( coef ); +for ( i = 0; i < 100; i++ ) { + v = uniform( -50.0, 50.0 ); + console.log( 'f(%d) = %d', v, polyval( v ) ); +} +``` + +
+ + + + + + + + + + + + + + diff --git a/base/tools/evalpolyf/benchmark/benchmark.factory.js b/base/tools/evalpolyf/benchmark/benchmark.factory.js new file mode 100644 index 000000000..67859b948 --- /dev/null +++ b/base/tools/evalpolyf/benchmark/benchmark.factory.js @@ -0,0 +1,87 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 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 bench = require( '@stdlib/bench' ); +var uniform = require( '@stdlib/random/array/uniform' ); +var isnan = require( './../../../../base/assert/is-nan' ); +var pkg = require( './../package.json' ).name; +var factory = require( './../lib/factory.js' ); + + +// MAIN // + +bench( pkg+'::create:factory', function benchmark( b ) { + var values; + var opts; + var f; + var i; + + opts = { + 'dtype': 'float32' + }; + values = [ + uniform( 10, 0.0, 100.0, opts ), + uniform( 10, 0.0, 100.0, opts ), + uniform( 10, 0.0, 100.0, opts ) + ]; + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + f = factory( values[ i%values.length ] ); + if ( typeof f !== 'function' ) { + b.fail( 'should return a function' ); + } + } + b.toc(); + if ( typeof f !== 'function' ) { + b.fail( 'should return a function' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+'::evaluate:factory', function benchmark( b ) { + var values; + var opts; + var v; + var f; + var i; + + opts = { + 'dtype': 'float32' + }; + f = factory( uniform( 10, 0.0, 100.0, opts ) ); + values = uniform( 10, 0.0, 100.0, opts ); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = f( values[ i%values.length ] ); + if ( isnan( v ) ) { + b.fail( 'should not return NaN' ); + } + } + b.toc(); + if ( isnan( v ) ) { + b.fail( 'should not return NaN' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/base/tools/evalpolyf/benchmark/benchmark.factory.length.js b/base/tools/evalpolyf/benchmark/benchmark.factory.length.js new file mode 100644 index 000000000..f5deafcce --- /dev/null +++ b/base/tools/evalpolyf/benchmark/benchmark.factory.length.js @@ -0,0 +1,104 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 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 bench = require( '@stdlib/bench' ); +var pow = require( './../../../../base/special/pow' ); +var uniform = require( '@stdlib/random/array/uniform' ); +var isnan = require( './../../../../base/assert/is-nan' ); +var pkg = require( './../package.json' ).name; +var factory = require( './../lib/factory.js' ); + + +// FUNCTIONS // + +/** +* Creates a benchmark function. +* +* @private +* @param {PositiveInteger} len - array length +* @returns {Function} benchmark function +*/ +function createBenchmark( len ) { + var polyval; + var opts; + + opts = { + 'dtype': 'float32' + }; + polyval = factory( uniform( len, 0.0, 100.0, opts ) ); + return benchmark; + + /** + * Benchmark function. + * + * @private + * @param {Benchmark} b - benchmark instance + */ + function benchmark( b ) { + var x; + var v; + var i; + + x = uniform( 10, 0.0, 100.0, opts ); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = polyval( x[ i%x.length ] ); + if ( isnan( v ) ) { + b.fail( 'should not return NaN' ); + } + } + b.toc(); + if ( isnan( v ) ) { + b.fail( 'should not return NaN' ); + } + b.pass( 'benchmark finished' ); + b.end(); + } +} + + +// MAIN // + +/** +* Main execution sequence. +* +* @private +*/ +function main() { + var len; + var min; + var max; + var f; + var i; + + min = 1; // 10^min + max = 6; // 10^max + + for ( i = min; i <= max; i++ ) { + len = pow( 10, i ); + f = createBenchmark( len ); + bench( pkg+':factory:len='+len, f ); + } +} + +main(); diff --git a/base/tools/evalpolyf/benchmark/benchmark.js b/base/tools/evalpolyf/benchmark/benchmark.js new file mode 100644 index 000000000..4a01709a8 --- /dev/null +++ b/base/tools/evalpolyf/benchmark/benchmark.js @@ -0,0 +1,62 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 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 bench = require( '@stdlib/bench' ); +var uniform = require( '@stdlib/random/array/uniform' ); +var isnan = require( './../../../../base/assert/is-nan' ); +var pkg = require( './../package.json' ).name; +var evalpolyf = require( './../lib' ); + + +// MAIN // + +bench( pkg, function benchmark( b ) { + var coefs; + var opts; + var x; + var v; + var i; + + opts = { + 'dtype': 'float32' + }; + coefs = [ + uniform( 10, 0.0, 100.0, opts ), + uniform( 10, 0.0, 100.0, opts ), + uniform( 10, 0.0, 100.0, opts ) + ]; + x = uniform( 10, 0.0, 100.0, opts ); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = evalpolyf( coefs[ i%coefs.length ], x[ i%x.length ] ); + if ( isnan( v ) ) { + b.fail( 'should not return NaN' ); + } + } + b.toc(); + if ( isnan( v ) ) { + b.fail( 'should not return NaN' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/base/tools/evalpolyf/benchmark/benchmark.length.js b/base/tools/evalpolyf/benchmark/benchmark.length.js new file mode 100644 index 000000000..c08150141 --- /dev/null +++ b/base/tools/evalpolyf/benchmark/benchmark.length.js @@ -0,0 +1,104 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 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 bench = require( '@stdlib/bench' ); +var pow = require( './../../../../base/special/pow' ); +var uniform = require( '@stdlib/random/array/uniform' ); +var isnan = require( './../../../../base/assert/is-nan' ); +var pkg = require( './../package.json' ).name; +var evalpolyf = require( './../lib' ); + + +// FUNCTIONS // + +/** +* Creates a benchmark function. +* +* @private +* @param {PositiveInteger} len - array length +* @returns {Function} benchmark function +*/ +function createBenchmark( len ) { + var opts; + var c; + + opts = { + 'dtype': 'float32' + }; + c = uniform( len, 0.0, 100.0, opts ); + return benchmark; + + /** + * Benchmark function. + * + * @private + * @param {Benchmark} b - benchmark instance + */ + function benchmark( b ) { + var x; + var v; + var i; + + x = uniform( 10, 0.0, 100.0, opts ); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = evalpolyf( c, x[ i%x.length ] ); + if ( isnan( v ) ) { + b.fail( 'should not return NaN' ); + } + } + b.toc(); + if ( isnan( v ) ) { + b.fail( 'should not return NaN' ); + } + b.pass( 'benchmark finished' ); + b.end(); + } +} + + +// MAIN // + +/** +* Main execution sequence. +* +* @private +*/ +function main() { + var len; + var min; + var max; + var f; + var i; + + min = 1; // 10^min + max = 6; // 10^max + + for ( i = min; i <= max; i++ ) { + len = pow( 10, i ); + f = createBenchmark( len ); + bench( pkg+':len='+len, f ); + } +} + +main(); diff --git a/base/tools/evalpolyf/docs/img/equation_polynomial.svg b/base/tools/evalpolyf/docs/img/equation_polynomial.svg new file mode 100644 index 000000000..e82bb0160 --- /dev/null +++ b/base/tools/evalpolyf/docs/img/equation_polynomial.svg @@ -0,0 +1,75 @@ + +c Subscript n Baseline x Superscript n Baseline plus c Subscript n minus 1 Baseline x Superscript n minus 1 Baseline plus ellipsis plus c 1 x Superscript 1 Baseline plus c 0 equals sigma-summation Underscript i equals 0 Overscript n Endscripts c Subscript i Baseline x Superscript i + + + \ No newline at end of file diff --git a/base/tools/evalpolyf/docs/repl.txt b/base/tools/evalpolyf/docs/repl.txt new file mode 100644 index 000000000..90911675b --- /dev/null +++ b/base/tools/evalpolyf/docs/repl.txt @@ -0,0 +1,56 @@ + +{{alias}}( c, x ) + Evaluates a polynomial using single-precision floating-point arithmetic. + + Parameters + ---------- + c: Array + Polynomial coefficients sorted in ascending degree. + + x: number + Value at which to evaluate the polynomial. + + Returns + ------- + out: number + Evaluated polynomial. + + Examples + -------- + > var arr = new {{alias:@stdlib/array/float32}}( [ 3.0, 2.0, 1.0 ] ); + + // 3*10^0 + 2*10^1 + 1*10^2 + > var v = {{alias}}( arr, 10.0 ) + 123.0 + + +{{alias}}.factory( c ) + Returns a function for evaluating a polynomial using single-precision + floating-point arithmetic. + + Parameters + ---------- + c: Array + Polynomial coefficients sorted in ascending degree. + + Returns + ------- + fcn: Function + Function for evaluating a polynomial. + + Examples + -------- + > var c = new {{alias:@stdlib/array/float32}}( [ 3.0, 2.0, 1.0 ] ); + > var f = {{alias}}.factory( c ); + + // 3*10^0 + 2*10^1 + 1*10^2 + > var v = f( 10.0 ) + 123.0 + + // 3*5^0 + 2*5^1 + 1*5^2 + > v = f( 5.0 ) + 38.0 + + See Also + -------- + diff --git a/base/tools/evalpolyf/docs/types/index.d.ts b/base/tools/evalpolyf/docs/types/index.d.ts new file mode 100644 index 000000000..4058fd247 --- /dev/null +++ b/base/tools/evalpolyf/docs/types/index.d.ts @@ -0,0 +1,122 @@ +/* +* @license Apache-2.0 +* +* Copyright (c) 2024 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. +*/ + +// TypeScript Version: 4.1 + +/// + +import { Collection } from '@stdlib/types/array'; + +/** +* Evaluates a polynomial using single-precision floating-point arithmetic. +* +* @param x - value at which to evaluate a polynomial +* @returns evaluated polynomial +*/ +type PolynomialFunction = ( x: number ) => number; + +/** +* Interface for evaluating polynomials. +*/ +interface EvalPoly { + /** + * Evaluates a polynomial using single-precision floating-point arithmetic. + * + * ## Notes + * + * - The implementation uses [Horner's rule][horners-method] for efficient computation. + * + * [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method + * + * + * @param c - polynomial coefficients sorted in ascending degree + * @param x - value at which to evaluate the polynomial + * @returns evaluated polynomial + * + * @example + * var Float32Array = require( '@stdlib/array/float32' ); + * + * var v = evalpolyf( new Float32Array( [ 3.0, 2.0, 1.0 ] ), 10.0 ); // 3*10^0 + 2*10^1 + 1*10^2 + * // returns 123.0 + */ + ( c: Collection, x: number ): number; + + /** + * Generates a function for evaluating a polynomial using single-precision floating-point arithmetic. + * + * ## Notes + * + * - The compiled function uses [Horner's rule][horners-method] for efficient computation. + * + * [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method + * + * + * @param c - polynomial coefficients sorted in ascending degree + * @returns function for evaluating a polynomial + * + * @example + * var Float32Array = require( '@stdlib/array/float32' ); + * + * var polyval = evalpolyf.factory( new Float32Array( [ 3.0, 2.0, 1.0 ] ) ); + * + * var v = polyval( 10.0 ); // => 3*10^0 + 2*10^1 + 1*10^2 + * // returns 123.0 + * + * v = polyval( 5.0 ); // => 3*5^0 + 2*5^1 + 1*5^2 + * // returns 38.0 + */ + factory( c: Collection ): PolynomialFunction; +} + +/** +* Evaluates a polynomial using single-precision floating-point arithmetic. +* +* ## Notes +* +* - The implementation uses [Horner's rule][horners-method] for efficient computation. +* +* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method +* +* +* @param c - polynomial coefficients sorted in ascending degree +* @param x - value at which to evaluate the polynomial +* @returns evaluated polynomial +* +* @example +* var Float32Array = require( '@stdlib/array/float32' ); +* +* var v = evalpolyf( new Float32Array( [ 3.0, 2.0, 1.0 ] ), 10.0 ); // 3*10^0 + 2*10^1 + 1*10^2 +* // returns 123.0 +* +* @example +* var Float32Array = require( '@stdlib/array/float32' ); +* +* var polyval = evalpolyf.factory( new Float32Array( [ 3.0, 2.0, 1.0 ] ) ); +* +* var v = polyval( 10.0 ); // => 3*10^0 + 2*10^1 + 1*10^2 +* // returns 123.0 +* +* v = polyval( 5.0 ); // => 3*5^0 + 2*5^1 + 1*5^2 +* // returns 38.0 +*/ +declare var evalpolyf: EvalPoly; + + +// EXPORTS // + +export = evalpolyf; diff --git a/base/tools/evalpolyf/docs/types/test.ts b/base/tools/evalpolyf/docs/types/test.ts new file mode 100644 index 000000000..2a8919cd9 --- /dev/null +++ b/base/tools/evalpolyf/docs/types/test.ts @@ -0,0 +1,97 @@ +/* +* @license Apache-2.0 +* +* Copyright (c) 2024 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. +*/ + +import evalpolyf = require( './index' ); + + +// TESTS // + +// The function returns a number... +{ + evalpolyf( [ 3.0, 2.0, 1.0 ], 10.0 ); // $ExpectType number +} + +// The compiler throws an error if the function is provided a first argument which is not an array of numbers... +{ + evalpolyf( true, 10.0 ); // $ExpectError + evalpolyf( false, 10.0 ); // $ExpectError + evalpolyf( 'abc', 10.0 ); // $ExpectError + evalpolyf( 123, 10.0 ); // $ExpectError + evalpolyf( {}, 10.0 ); // $ExpectError + evalpolyf( ( x: number ): number => x, 10.0 ); // $ExpectError +} + +// The compiler throws an error if the function is provided a second argument which is not a number... +{ + const c = [ 3.0, 2.0, 1.0 ]; + evalpolyf( c, true ); // $ExpectError + evalpolyf( c, false ); // $ExpectError + evalpolyf( c, 'abc' ); // $ExpectError + evalpolyf( c, [] ); // $ExpectError + evalpolyf( c, {} ); // $ExpectError + evalpolyf( c, ( x: number ): number => x ); // $ExpectError +} + +// The compiler throws an error if the function is provided an insufficient number of arguments... +{ + const c = [ 3.0, 2.0, 1.0 ]; + evalpolyf(); // $ExpectError + evalpolyf( c ); // $ExpectError +} + +// Attached to main export is a `factory` method which returns a function... +{ + const c = [ 3.0, 2.0, 1.0 ]; + evalpolyf.factory( c ); // $ExpectType PolynomialFunction +} + +// The compiler throws an error if the `factory` method is provided a first argument which is not an array of numbers... +{ + evalpolyf.factory( true ); // $ExpectError + evalpolyf.factory( false ); // $ExpectError + evalpolyf.factory( 'abc' ); // $ExpectError + evalpolyf.factory( 123 ); // $ExpectError + evalpolyf.factory( {} ); // $ExpectError + evalpolyf.factory( ( x: number ): number => x ); // $ExpectError +} + +// The compiler throws an error if the `factory` method is provided an unsupported number of arguments... +{ + const c = [ 3.0, 2.0, 1.0 ]; + evalpolyf.factory(); // $ExpectError + evalpolyf.factory( c, 2.0 ); // $ExpectError +} + +// The `factory` method returns a function which returns a number... +{ + const c = [ 3.0, 2.0, 1.0 ]; + const polyval = evalpolyf.factory( c ); + polyval( 1.0 ); // $ExpectType number +} + +// The compiler throws an error if the function returned by the `factory` method is provided a first argument which is not a number... +{ + const c = [ 3.0, 2.0, 1.0 ]; + const polyval = evalpolyf.factory( c ); + polyval( true ); // $ExpectError + polyval( false ); // $ExpectError + polyval( 'abc' ); // $ExpectError + polyval( [] ); // $ExpectError + polyval( {} ); // $ExpectError + polyval( ( x: number ): number => x ); // $ExpectError +} diff --git a/base/tools/evalpolyf/examples/index.js b/base/tools/evalpolyf/examples/index.js new file mode 100644 index 000000000..ae459bde9 --- /dev/null +++ b/base/tools/evalpolyf/examples/index.js @@ -0,0 +1,69 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 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'; + +var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +var uniform = require( '@stdlib/random/base/uniform' ); +var evalpolyf = require( './../lib' ); + +function toStr( coef ) { + var str; + var c; + var n; + var i; + + n = coef.length; + str = coef[ n-1 ] + 'x^' + n; + for ( i = n-2; i >= 0; i-- ) { + c = coef[ i ]; + if ( c < 0 ) { + c = -c; + str += ' - '; + } else { + str += ' + '; + } + str += c + 'x^' + i; + } + return str; +} + +// Create an array of random coefficients: +var coef = discreteUniform( 10, -100, 100, { + 'dtype': 'float32' +}); + +// Generate a polynomial equation: +var eqn = toStr( coef ); +console.log( 'f(x) = %s', eqn ); + +// Evaluate the polynomial at random values: +var v; +var i; +for ( i = 0; i < 100; i++ ) { + v = uniform( 0.0, 100.0 ); + console.log( 'f(%d) = %d', v, evalpolyf( coef, v ) ); +} + +// Generate an `evalpolyf` function: +var polyval = evalpolyf.factory( coef ); +console.log( '\nf(x) = %s', eqn ); +for ( i = 0; i < 100; i++ ) { + v = uniform( -50.0, 50.0 ); + console.log( 'f(%d) = %d', v, polyval( v ) ); +} diff --git a/base/tools/evalpolyf/lib/factory.js b/base/tools/evalpolyf/lib/factory.js new file mode 100644 index 000000000..500c692be --- /dev/null +++ b/base/tools/evalpolyf/lib/factory.js @@ -0,0 +1,135 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 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 float64ToFloat32 = require( '@stdlib/number/float64/base/to-float32' ); +var Float32Array = require( '@stdlib/array/float32' ); +var Fcn = require( '@stdlib/function/ctor' ); +var evalpolyf = require( './main.js' ); + + +// MAIN // + +/** +* Generates a function for evaluating a polynomial using single-precision floating-point arithmetic. +* +* ## Notes +* +* - The compiled function uses [Horner's rule][horners-method] for efficient computation. +* +* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method +* +* @param {NumericArray} c - polynomial coefficients sorted in ascending degree +* @returns {Function} function for evaluating a polynomial +* +* @example +* var Float32Array = require( '@stdlib/array/float32' ); +* +* var polyval = factory( new Float32Array( [ 3.0, 2.0, 1.0 ] ) ); +* +* var v = polyval( 10.0 ); // => 3*10^0 + 2*10^1 + 1*10^2 +* // returns 123.0 +* +* v = polyval( 5.0 ); // => 3*5^0 + 2*5^1 + 1*5^2 +* // returns 38.0 +*/ +function factory( c ) { + var f; + var n; + var m; + var i; + + // Explicitly copy in order to ensure single-precision: + c = new Float32Array( c ); + + // Avoid exceeding the maximum stack size on V8 :(. Note that the choice of `500` was empirically determined... + if ( c.length > 500 ) { + return polyval; + } + // Code generation. Start with the function definition... + f = 'return function evalpolyf(x){'; + + // Create the function body... + n = c.length; + + // If no coefficients, the function always returns 0... + if ( n === 0 ) { + f += 'return 0.0;'; + } + // If only one coefficient, the function always returns that coefficient... + else if ( n === 1 ) { + f += 'return ' + c[ 0 ] + ';'; + } + // If more than one coefficient, apply Horner's method... + else { + // If `x == 0`, return the first coefficient... + f += 'if(x===0.0){return ' + c[ 0 ] + ';}'; + + // Otherwise, evaluate the polynomial... + f += 'return f64_to_f32(' + c[ 0 ]; + m = n - 1; + for ( i = 1; i < n; i++ ) { + f += '+f64_to_f32(x*'; + if ( i < m ) { + f += 'f64_to_f32('; + } + f += c[ i ]; + } + // Close all the parentheses... + for ( i = 0; i < 2*m; i++ ) { + f += ')'; + } + f += ';'; + } + // Close the function: + f += '}'; + + // Add a source directive for debugging: + f += '//# sourceURL=evalpolyf.factory.js'; + + // Create the function in the global scope: + return ( new Fcn( 'f64_to_f32', f ) )( float64ToFloat32 ); + + /* + * function evalpolyf( x ) { + * if ( x === 0.0 ) { + * return c[ 0 ]; + * } + * return f64_to_f32(c[0]+f64_to_f32(x*f64_to_f32(c[1]+f64_to_f32(x*f64_to_f32(c[2]+f64_to_f32(x*f64_to_f32(c[3]+...+f64_to_f32(x*f64_to_f32(c[n-2]+f64_to_f32(x*c[n-1])))))))))); + * } + */ + + /** + * Evaluates a polynomial. + * + * @private + * @param {number} x - value at which to evaluate a polynomial + * @returns {number} evaluated polynomial + */ + function polyval( x ) { + return evalpolyf( c, x ); + } +} + + +// EXPORTS // + +module.exports = factory; diff --git a/base/tools/evalpolyf/lib/index.js b/base/tools/evalpolyf/lib/index.js new file mode 100644 index 000000000..4df7c4fb5 --- /dev/null +++ b/base/tools/evalpolyf/lib/index.js @@ -0,0 +1,60 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 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'; + +/** +* Evaluate a polynomial using single-precision floating-point arithmetic. +* +* @module @stdlib/math/base/tools/evalpolyf +* +* @example +* var Float32Array = require( '@stdlib/array/float32' ); +* var evalpolyf = require( '@stdlib/math/base/tools/evalpolyf' ); +* +* var v = evalpolyf( new Float32Array( [ 3.0, 2.0, 1.0 ] ), 10.0 ); // 3*10^0 + 2*10^1 + 1*10^2 +* // returns 123.0 +* +* @example +* var Float32Array = require( '@stdlib/array/float32' ); +* var evalpolyf = require( '@stdlib/math/base/tools/evalpolyf' ); +* +* var polyval = evalpolyf.factory( new Float32Array( [ 3.0, 2.0, 1.0 ] ) ); +* +* var v = polyval( 10.0 ); // => 3*10^0 + 2*10^1 + 1*10^2 +* // returns 123.0 +* +* v = polyval( 5.0 ); // => 3*5^0 + 2*5^1 + 1*5^2 +* // returns 38.0 +*/ + +// MODULES // + +var setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); +var main = require( './main.js' ); +var factory = require( './factory.js' ); + + +// MAIN // + +setReadOnly( main, 'factory', factory ); + + +// EXPORTS // + +module.exports = main; diff --git a/base/tools/evalpolyf/lib/main.js b/base/tools/evalpolyf/lib/main.js new file mode 100644 index 000000000..8759f9bb3 --- /dev/null +++ b/base/tools/evalpolyf/lib/main.js @@ -0,0 +1,71 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 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 float64ToFloat32 = require( '@stdlib/number/float64/base/to-float32' ); + + +// MAIN // + +/** +* Evaluates a polynomial using single-precision floating-point arithmetic. +* +* ## Notes +* +* - The implementation uses [Horner's rule][horners-method] for efficient computation. +* +* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method +* +* @param {NumericArray} c - polynomial coefficients sorted in ascending degree +* @param {number} x - value at which to evaluate the polynomial +* @returns {number} evaluated polynomial +* +* @example +* var Float32Array = require( '@stdlib/array/float32' ); +* +* var v = evalpolyf( new Float32Array( [ 3.0, 2.0, 1.0 ] ), 10.0 ); // 3*10^0 + 2*10^1 + 1*10^2 +* // returns 123.0 +*/ +function evalpolyf( c, x ) { + var p; + var i; + + i = c.length; + if ( i < 2 || x === 0.0 ) { + if ( i === 0 ) { + return 0.0; + } + return c[ 0 ]; + } + i -= 1; + p = float64ToFloat32( float64ToFloat32( c[ i ] * x ) + c[ i-1 ] ); + i -= 2; + while ( i >= 0 ) { + p = float64ToFloat32( float64ToFloat32( p * x ) + c[ i ] ); + i -= 1; + } + return p; +} + + +// EXPORTS // + +module.exports = evalpolyf; diff --git a/base/tools/evalpolyf/package.json b/base/tools/evalpolyf/package.json new file mode 100644 index 000000000..78405e902 --- /dev/null +++ b/base/tools/evalpolyf/package.json @@ -0,0 +1,65 @@ +{ + "name": "@stdlib/math/base/tools/evalpolyf", + "version": "0.0.0", + "description": "Evaluate a polynomial using single-precision floating-point arithmetic.", + "license": "Apache-2.0", + "author": { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + }, + "contributors": [ + { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + } + ], + "main": "./lib", + "directories": { + "benchmark": "./benchmark", + "doc": "./docs", + "example": "./examples", + "lib": "./lib", + "test": "./test" + }, + "types": "./docs/types", + "scripts": {}, + "homepage": "https://github.com/stdlib-js/stdlib", + "repository": { + "type": "git", + "url": "git://github.com/stdlib-js/stdlib.git" + }, + "bugs": { + "url": "https://github.com/stdlib-js/stdlib/issues" + }, + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": ">=0.10.0", + "npm": ">2.7.0" + }, + "os": [ + "aix", + "darwin", + "freebsd", + "linux", + "macos", + "openbsd", + "sunos", + "win32", + "windows" + ], + "keywords": [ + "stdlib", + "stdmath", + "mathematics", + "math", + "polynomial", + "eval", + "evaluate", + "poly", + "polyval", + "evalpoly", + "horner", + "number" + ] +} diff --git a/base/tools/evalpolyf/test/test.factory.js b/base/tools/evalpolyf/test/test.factory.js new file mode 100644 index 000000000..084582c65 --- /dev/null +++ b/base/tools/evalpolyf/test/test.factory.js @@ -0,0 +1,117 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 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 tape = require( 'tape' ); +var Float32Array = require( '@stdlib/array/float32' ); +var factory = require( './../lib/factory.js' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof factory, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function returns a function', function test( t ) { + var f = factory( [ 1.0, 2.0, 3.0 ] ); + t.strictEqual( typeof f, 'function', 'returns expected value' ); + t.end(); +}); + +tape( 'if provided an empty coefficient array, the function returns a function which always returns `0`', function test( t ) { + var f; + var v; + var i; + + f = factory( new Float32Array( [] ) ); + for ( i = 0; i < 100; i++ ) { + v = f( i ); + t.strictEqual( v, 0.0, 'returns expected value' ); + } + t.end(); +}); + +tape( 'if provided only 1 coefficient, the function returns a function which always returns that coefficient', function test( t ) { + var f; + var v; + var i; + + f = factory( new Float32Array( [ 2.0 ] ) ); + for ( i = 0; i < 100; i++ ) { + v = f( i ); + t.strictEqual( v, 2.0, 'returns expected value' ); + } + t.end(); +}); + +tape( 'if the value at which to evaluate a polynomial is `0`, the function returns a function which returns the first coefficient', function test( t ) { + var f; + var v; + + f = factory( new Float32Array( [ 3.0, 2.0, 1.0 ] ) ); + v = f( 0.0 ); + t.strictEqual( v, 3.0, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function returns a function which evaluates a polynomial', function test( t ) { + var f; + var v; + + f = factory( new Float32Array( [ 4.0, 5.0 ] ) ); + v = f( 6.0 ); + t.strictEqual( v, 34.0, 'returns expected value' ); + + f = factory( new Float32Array( [ -4.0, -5.0 ] ) ); + v = f( 6.0 ); + t.strictEqual( v, -34.0, 'returns expected value' ); + + f = factory( new Float32Array( [ -19.0, 7.0, -4.0, 6.0 ] ) ); + v = f( 3.0 ); + t.strictEqual( v, 128.0, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function returns a function which evaluates a polynomial (large number of coefficients)', function test( t ) { + var sum; + var f; + var c; + var v; + var i; + + c = []; + sum = 0; + for ( i = 0; i < 1000; i++ ) { + c.push( i ); + sum += i; + } + f = factory( new Float32Array( c ) ); + + v = f( 1.0 ); + t.strictEqual( v, sum, 'returns expected value' ); + + t.end(); +}); diff --git a/base/tools/evalpolyf/test/test.js b/base/tools/evalpolyf/test/test.js new file mode 100644 index 000000000..1f79e3ae1 --- /dev/null +++ b/base/tools/evalpolyf/test/test.js @@ -0,0 +1,38 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 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 tape = require( 'tape' ); +var evalpolyf = require( './../lib' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof evalpolyf, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'attached to the main export is a `factory` method', function test( t ) { + t.strictEqual( typeof evalpolyf.factory, 'function', 'has method' ); + t.end(); +}); diff --git a/base/tools/evalpolyf/test/test.main.js b/base/tools/evalpolyf/test/test.main.js new file mode 100644 index 000000000..ffebcefce --- /dev/null +++ b/base/tools/evalpolyf/test/test.main.js @@ -0,0 +1,71 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 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 tape = require( 'tape' ); +var Float32Array = require( '@stdlib/array/float32' ); +var evalpolyf = require( './../lib' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.true( typeof evalpolyf, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'if provided an empty coefficient array, the function returns `0`', function test( t ) { + var v = evalpolyf( new Float32Array( [] ), 10.0 ); + t.strictEqual( v, 0.0, 'returns expected value' ); + t.end(); +}); + +tape( 'if provided only 1 coefficient, the function returns that coefficient', function test( t ) { + var v = evalpolyf( new Float32Array( [ 1.0 ] ), 10.0 ); + t.strictEqual( v, 1.0, 'returns expected value' ); + t.end(); +}); + +tape( 'if the value at which to evaluate a polynomial is `0`, the function returns the first coefficient', function test( t ) { + var v = evalpolyf( new Float32Array( [ 3.0, 2.0, 1.0 ] ), 0.0 ); + t.strictEqual( v, 3.0, 'returns expected value' ); + t.end(); +}); + +tape( 'the function evaluates a polynomial', function test( t ) { + var c; + var v; + + c = new Float32Array( [ 4.0, 5.0 ] ); + v = evalpolyf( c, 6.0 ); + t.strictEqual( v, 34.0, 'returns expected value' ); + + c = new Float32Array( [ -4.0, -5.0 ] ); + v = evalpolyf( c, 6.0 ); + t.strictEqual( v, -34.0, 'returns expected value' ); + + c = new Float32Array( [ -19.0, 7.0, -4.0, 6.0 ] ); + v = evalpolyf( c, 3.0 ); + t.strictEqual( v, 128.0, 'returns expected value' ); + + t.end(); +}); diff --git a/base/tools/lib/index.js b/base/tools/lib/index.js index 3c78744db..970ead8b2 100644 --- a/base/tools/lib/index.js +++ b/base/tools/lib/index.js @@ -58,6 +58,15 @@ setReadOnly( ns, 'continuedFraction', require( './../../../base/tools/continued- */ setReadOnly( ns, 'evalpoly', require( './../../../base/tools/evalpoly' ) ); +/** +* @name evalpolyf +* @memberof tools +* @readonly +* @type {Function} +* @see {@link module:@stdlib/math/base/tools/evalpolyf} +*/ +setReadOnly( ns, 'evalpolyf', require( './../../../base/tools/evalpolyf' ) ); + /** * @name evalrational * @memberof ns