time-to-botec

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

main.js (6112B)


      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 isPositiveInteger = require( '@stdlib/assert/is-positive-integer' ).isPrimitive;
     24 var isArrayLike = require( '@stdlib/assert/is-array-like-object' );
     25 var isPositiveZero = require( '@stdlib/math/base/assert/is-positive-zero' );
     26 var isNegativeZero = require( '@stdlib/math/base/assert/is-negative-zero' );
     27 var isnan = require( '@stdlib/math/base/assert/is-nan' );
     28 var PINF = require( '@stdlib/constants/float64/pinf' );
     29 var NINF = require( '@stdlib/constants/float64/ninf' );
     30 var Float64Array = require( '@stdlib/array/float64' );
     31 
     32 
     33 // MAIN //
     34 
     35 /**
     36 * Returns an accumulator function which incrementally computes moving minimum and maximum values.
     37 *
     38 * @param {Collection} [out] - output array
     39 * @param {PositiveInteger} window - window size
     40 * @throws {TypeError} output argument must be array-like
     41 * @throws {TypeError} window size must be a positive integer
     42 * @returns {Function} accumulator function
     43 *
     44 * @example
     45 * var accumulator = incrmminmax( 3 );
     46 *
     47 * var mm = accumulator();
     48 * // returns null
     49 *
     50 * mm = accumulator( 2.0 );
     51 * // returns [ 2.0, 2.0 ]
     52 *
     53 * mm = accumulator( -5.0 );
     54 * // returns [ -5.0, 2.0 ]
     55 *
     56 * mm = accumulator( 3.0 );
     57 * // returns [ -5.0, 3.0 ]
     58 *
     59 * mm = accumulator( 5.0 );
     60 * // returns [ -5.0, 5.0 ]
     61 *
     62 * mm = accumulator();
     63 * // returns [ -5.0, 5.0 ]
     64 */
     65 function incrmminmax( out, window ) {
     66 	var minmax;
     67 	var buf;
     68 	var min;
     69 	var max;
     70 	var W;
     71 	var N;
     72 	var i;
     73 	if ( arguments.length === 1 ) {
     74 		minmax = [ 0.0, 0.0 ];
     75 		W = out;
     76 	} else {
     77 		if ( !isArrayLike( out ) ) {
     78 			throw new TypeError( 'invalid argument. Output argument must be an array-like object. Value: `' + out + '`.' );
     79 		}
     80 		minmax = out;
     81 		W = window;
     82 	}
     83 	if ( !isPositiveInteger( W ) ) {
     84 		throw new TypeError( 'invalid argument. Window size must be a positive integer. Value: `' + W + '`.' );
     85 	}
     86 	buf = new Float64Array( W );
     87 	min = PINF;
     88 	max = NINF;
     89 	i = -1;
     90 	N = 0;
     91 
     92 	return accumulator;
     93 
     94 	/**
     95 	* If provided a value, the accumulator function returns updated minimum and maximum values. If not provided a value, the accumulator function returns the current minimum and maximum values.
     96 	*
     97 	* @private
     98 	* @param {number} [x] - input value
     99 	* @returns {(ArrayLikeObject|null)} output array or null
    100 	*/
    101 	function accumulator( x ) {
    102 		var sgn;
    103 		var v;
    104 		var k;
    105 		if ( arguments.length === 0 ) {
    106 			if ( N === 0 ) {
    107 				return null;
    108 			}
    109 			return minmax;
    110 		}
    111 		// Update the index for managing the circular buffer:
    112 		i = (i+1) % W;
    113 
    114 		// Case: incoming value is NaN...
    115 		if ( isnan( x ) ) {
    116 			N = W; // explicitly set to avoid `N < W` branch
    117 			min = x;
    118 			max = x;
    119 		}
    120 		// Case: initial window...
    121 		else if ( N < W ) {
    122 			N += 1;
    123 			if ( x < min || ( x === min && isNegativeZero( x ) ) ) {
    124 				min = x;
    125 			}
    126 			if ( x > max || ( x === max && isPositiveZero( x ) ) ) {
    127 				max = x;
    128 			}
    129 		}
    130 		// Case: outgoing value is the current minimum or maximum and the new value is either greater than the minimum or less than the maximum, and, thus, we need to find new accumulated values among the current buffer values...
    131 		else if (
    132 			( buf[ i ] === min && x > min ) ||
    133 			( buf[ i ] === max && x < max ) ||
    134 			isnan( buf[ i ] )
    135 		) {
    136 			min = x;
    137 			max = x;
    138 			for ( k = 0; k < W; k++ ) {
    139 				if ( k !== i ) {
    140 					v = buf[ k ];
    141 					if ( isnan( v ) ) {
    142 						min = v;
    143 						max = v;
    144 						break; // no need to continue searching
    145 					}
    146 					if ( v < min || ( v === min && isNegativeZero( v ) ) ) {
    147 						min = v;
    148 					}
    149 					if ( v > max || ( v === max && isPositiveZero( v ) ) ) {
    150 						max = v;
    151 					}
    152 				}
    153 			}
    154 		}
    155 		// Case: incoming value is less than current minimum value...
    156 		else if ( x < min ) {
    157 			min = x;
    158 		}
    159 		// Case: incoming value is greater than current maximum value...
    160 		else if ( x > max ) {
    161 			max = x;
    162 		}
    163 		// Case: incoming value is zero, which means we need to be careful and correctly handle signed zeros...
    164 		else if ( x === 0.0 ) {
    165 			sgn = isNegativeZero( x );
    166 			if ( x === min ) {
    167 				// Case: outgoing value is the current minimum...
    168 				if (
    169 					buf[ i ] === min &&
    170 					isNegativeZero( buf[ i ] ) &&
    171 					sgn === false
    172 				) {
    173 					// Because the outgoing and incoming are different signs (-,+), we need to search the buffer to see if it contains a negative zero. If so, the minimum value remains negative zero; otherwise, the minimum value is incoming value...
    174 					min = x;
    175 					for ( k = 0; k < W; k++ ) {
    176 						if ( k !== i && isNegativeZero( buf[ k ] ) ) {
    177 							min = buf[ k ];
    178 							break;
    179 						}
    180 					}
    181 				} else if ( sgn ) {
    182 					// Ensure minimum value has the correct sign:
    183 					min = x;
    184 				}
    185 			}
    186 			if ( x === max ) {
    187 				// Case: outgoing value is the current maximum...
    188 				if (
    189 					buf[ i ] === max &&
    190 					isPositiveZero( buf[ i ] ) &&
    191 					sgn
    192 				) {
    193 					// Because the outgoing and incoming are different signs (+,-), we need to search the buffer to see if it contains a positive zero. If so, the maximum value remains positive zero; otherwise, the maximum value is incoming value...
    194 					max = x;
    195 					for ( k = 0; k < W; k++ ) {
    196 						if ( k !== i && isPositiveZero( buf[ k ] ) ) {
    197 							max = buf[ k ];
    198 							break;
    199 						}
    200 					}
    201 				} else if ( sgn === false ) {
    202 					// Ensure maximum value has the correct sign:
    203 					max = x;
    204 				}
    205 			}
    206 		}
    207 		// Case: updating existing window; however, the minimum and maximum values do not change so nothing to do but update our buffer...
    208 		buf[ i ] = x;
    209 
    210 		minmax[ 0 ] = min;
    211 		minmax[ 1 ] = max;
    212 		return minmax;
    213 	}
    214 }
    215 
    216 
    217 // EXPORTS //
    218 
    219 module.exports = incrmminmax;