minmax.js (5050B)
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 isPositiveZero = require( '@stdlib/math/base/assert/is-positive-zero' ); 24 var isNegativeZero = require( '@stdlib/math/base/assert/is-negative-zero' ); 25 var isnan = require( '@stdlib/math/base/assert/is-nan' ); 26 var PINF = require( '@stdlib/constants/float64/pinf' ); 27 var NINF = require( '@stdlib/constants/float64/ninf' ); 28 29 30 // MAIN // 31 32 /** 33 * Returns an accumulator function which incrementally computes moving minimum and maximum values. 34 * 35 * @private 36 * @param {Collection} out - output array 37 * @param {PositiveInteger} W - window size 38 * @param {Collection} buf - data buffer 39 * @returns {Function} accumulator function 40 * 41 * @example 42 * var buf = [ 0.0, 0.0, 0.0 ]; 43 * 44 * var accumulator = incrmminmax( [ 0.0, 0.0 ], 3, buf ); 45 * 46 * var mm = accumulator( 2.0, 0 ); 47 * // returns [ 2.0, 2.0 ] 48 * 49 * buf[ 0 ] = 2.0; 50 * 51 * mm = accumulator( -5.0, 1 ); 52 * // returns [ -5.0, 2.0 ] 53 * 54 * buf[ 1 ] = -5.0; 55 * 56 * mm = accumulator( 3.0, 2 ); 57 * // returns [ -5.0, 3.0 ] 58 * 59 * buf[ 2 ] = 3.0; 60 * 61 * mm = accumulator( 5.0, 0 ); 62 * // returns [ -5.0, 5.0 ] 63 * 64 * buf[ 0 ] = 5.0; 65 */ 66 function incrmminmax( out, W, buf ) { 67 var min; 68 var max; 69 var N; 70 71 min = PINF; 72 max = NINF; 73 N = 0; 74 75 return accumulator; 76 77 /** 78 * Updates accumulator state. 79 * 80 * @private 81 * @param {number} x - input value 82 * @param {NonNegativeInteger} i - buffer index 83 * @returns {Collection} output array 84 */ 85 function accumulator( x, i ) { 86 var sgn; 87 var v; 88 var k; 89 90 // Case: incoming value is NaN... 91 if ( isnan( x ) ) { 92 N = W; // explicitly set to avoid `N < W` branch 93 min = x; 94 max = x; 95 } 96 // Case: initial window... 97 else if ( N < W ) { 98 N += 1; 99 if ( x < min || ( x === min && isNegativeZero( x ) ) ) { 100 min = x; 101 } 102 if ( x > max || ( x === max && isPositiveZero( x ) ) ) { 103 max = x; 104 } 105 } 106 // 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... 107 else if ( 108 ( buf[ i ] === min && x > min ) || 109 ( buf[ i ] === max && x < max ) || 110 isnan( buf[ i ] ) 111 ) { 112 min = x; 113 max = x; 114 for ( k = 0; k < W; k++ ) { 115 if ( k !== i ) { 116 v = buf[ k ]; 117 if ( isnan( v ) ) { 118 min = v; 119 max = v; 120 break; // no need to continue searching 121 } 122 if ( v < min || ( v === min && isNegativeZero( v ) ) ) { 123 min = v; 124 } 125 if ( v > max || ( v === max && isPositiveZero( v ) ) ) { 126 max = v; 127 } 128 } 129 } 130 } 131 // Case: incoming value is less than current minimum value... 132 else if ( x < min ) { 133 min = x; 134 } 135 // Case: incoming value is greater than current maximum value... 136 else if ( x > max ) { 137 max = x; 138 } 139 // Case: incoming value is zero, which means we need to be careful and correctly handle signed zeros... 140 else if ( x === 0.0 ) { 141 sgn = isNegativeZero( x ); 142 if ( x === min ) { 143 // Case: outgoing value is the current minimum... 144 if ( 145 buf[ i ] === min && 146 isNegativeZero( buf[ i ] ) && 147 sgn === false 148 ) { 149 // 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... 150 min = x; 151 for ( k = 0; k < W; k++ ) { 152 if ( k !== i && isNegativeZero( buf[ k ] ) ) { 153 min = buf[ k ]; 154 break; 155 } 156 } 157 } else if ( sgn ) { 158 // Ensure minimum value has the correct sign: 159 min = x; 160 } 161 } 162 if ( x === max ) { 163 // Case: outgoing value is the current maximum... 164 if ( 165 buf[ i ] === max && 166 isPositiveZero( buf[ i ] ) && 167 sgn 168 ) { 169 // 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... 170 max = x; 171 for ( k = 0; k < W; k++ ) { 172 if ( k !== i && isPositiveZero( buf[ k ] ) ) { 173 max = buf[ k ]; 174 break; 175 } 176 } 177 } else if ( sgn === false ) { 178 // Ensure maximum value has the correct sign: 179 max = x; 180 } 181 } 182 } 183 // Case: updating existing window; however, the minimum and maximum values do not change so nothing to do... 184 185 out[ 0 ] = min; 186 out[ 1 ] = max; 187 return out; 188 } 189 } 190 191 192 // EXPORTS // 193 194 module.exports = incrmminmax;