main.js (5620B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2019 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 setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); 24 var isFunction = require( '@stdlib/assert/is-function' ); 25 var isNumber = require( '@stdlib/assert/is-number' ).isPrimitive; 26 var isIteratorLike = require( '@stdlib/assert/is-iterator-like' ); 27 var hasOwnProp = require( '@stdlib/assert/has-own-property' ); 28 var iteratorSymbol = require( '@stdlib/symbol/iterator' ); 29 30 31 // MAIN // 32 33 /** 34 * Returns an iterator which performs element-wise division of two or more iterators. 35 * 36 * ## Notes 37 * 38 * - If provided a numeric value as an iterator argument, the value is broadcast as an **infinite** iterator which **always** returns the provided value. 39 * - If an iterated value is non-numeric (including `NaN`), the returned iterator returns `NaN`. If non-numeric iterated values are possible, you are advised to provide an iterator which type checks and handles non-numeric values accordingly. 40 * - The length of the returned iterator is equal to the length of the shortest provided iterator. In other words, the returned iterator ends once **one** of the provided iterators ends. 41 * - If an environment supports `Symbol.iterator` and all provided iterators are iterable, the returned iterator is iterable. 42 * 43 * @param {Iterator} iter0 - first input iterator 44 * @param {...(Iterator|number)} iterator - subsequent iterators 45 * @throws {Error} must provide two or more iterators 46 * @throws {TypeError} must provide iterator protocol-compliant objects 47 * @returns {Iterator} iterator 48 * 49 * @example 50 * var array2iterator = require( '@stdlib/array/to-iterator' ); 51 * 52 * var it1 = array2iterator( [ 3.0, 2.0 ] ); 53 * var it2 = array2iterator( [ 1.0, 4.0 ] ); 54 * 55 * var iter = iterDivide( it1, it2 ); 56 * 57 * var v = iter.next().value; 58 * // returns 3.0 59 * 60 * v = iter.next().value; 61 * // returns 0.5 62 * 63 * var bool = iter.next().done; 64 * // returns true 65 */ 66 function iterDivide() { 67 var iterators; 68 var types; 69 var niter; 70 var iter; 71 var FLG; 72 var i; 73 74 niter = arguments.length; 75 if ( niter < 2 ) { 76 throw new Error( 'insufficient input arguments. Must provide two or more iterators.' ); 77 } 78 iterators = []; 79 types = []; 80 for ( i = 0; i < niter; i++ ) { 81 iterators.push( arguments[ i ] ); 82 if ( isIteratorLike( arguments[ i ] ) ) { 83 types.push( 1 ); 84 } else if ( isNumber( arguments[ i ] ) ) { 85 types.push( 0 ); 86 } else { 87 throw new TypeError( 'invalid argument. Must provide an iterator protocol-compliant object or a number. Argument: `' + i + '`. Value: `' + arguments[ i ] + '`.' ); 88 } 89 } 90 // Create an iterator protocol-compliant object: 91 iter = {}; 92 setReadOnly( iter, 'next', next ); 93 setReadOnly( iter, 'return', end ); 94 95 // If an environment supports `Symbol.iterator` and all provided iterators are iterable, make the iterator iterable: 96 if ( iteratorSymbol ) { 97 for ( i = 0; i < niter; i++ ) { 98 if ( types[ i ] && !isFunction( iterators[ i ][ iteratorSymbol ] ) ) { // eslint-disable-line max-len 99 FLG = true; 100 break; 101 } 102 } 103 if ( !FLG ) { 104 setReadOnly( iter, iteratorSymbol, factory ); 105 } 106 } 107 FLG = 0; 108 i = 0; 109 return iter; 110 111 /** 112 * Returns an iterator protocol-compliant object containing the next iterated value. 113 * 114 * @private 115 * @returns {Object} iterator protocol-compliant object 116 */ 117 function next() { 118 var s; 119 var v; 120 var i; 121 if ( FLG ) { 122 return { 123 'done': true 124 }; 125 } 126 FLG = 0; 127 if ( types[ 0 ] ) { 128 v = iterators[ 0 ].next(); 129 if ( v.done ) { 130 FLG += 1; 131 if ( hasOwnProp( v, 'value' ) ) { 132 if ( typeof v.value === 'number' ) { 133 s = v.value; 134 } else { 135 s = NaN; 136 } 137 } else { 138 return { 139 'done': true 140 }; 141 } 142 } else if ( typeof v.value === 'number' ) { 143 s = v.value; 144 } else { 145 s = NaN; 146 } 147 } else { 148 s = iterators[ 0 ]; 149 } 150 for ( i = 1; i < niter; i++ ) { 151 if ( types[ i ] ) { 152 v = iterators[ i ].next(); 153 if ( v.done ) { 154 FLG += 1; 155 if ( hasOwnProp( v, 'value' ) ) { 156 if ( typeof v.value === 'number' ) { 157 s /= v.value; 158 } else { 159 s = NaN; 160 } 161 continue; 162 } 163 return { 164 'done': true 165 }; 166 } 167 if ( typeof v.value === 'number' ) { 168 s /= v.value; 169 } else { 170 s = NaN; 171 } 172 } else { 173 s /= iterators[ i ]; 174 } 175 } 176 return { 177 'value': s, 178 'done': false 179 }; 180 } 181 182 /** 183 * Finishes an iterator. 184 * 185 * @private 186 * @param {*} [value] - value to return 187 * @returns {Object} iterator protocol-compliant object 188 */ 189 function end( value ) { 190 FLG = 1; 191 if ( arguments.length ) { 192 return { 193 'value': value, 194 'done': true 195 }; 196 } 197 return { 198 'done': true 199 }; 200 } 201 202 /** 203 * Returns a new iterator. 204 * 205 * @private 206 * @returns {Iterator} iterator 207 */ 208 function factory() { 209 var args; 210 var i; 211 212 args = []; 213 for ( i = 0; i < niter; i++ ) { 214 if ( types[ i ] ) { 215 args.push( iterators[ i ][ iteratorSymbol ]() ); 216 } else { 217 args.push( iterators[ i ] ); 218 } 219 } 220 return iterDivide.apply( null, args ); 221 } 222 } 223 224 225 // EXPORTS // 226 227 module.exports = iterDivide;