main.js (6823B)
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 setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); 24 var setReadOnlyAccessor = require( '@stdlib/utils/define-nonenumerable-read-only-accessor' ); 25 var setReadWriteAccessor = require( '@stdlib/utils/define-nonenumerable-read-write-accessor' ); 26 var constantFunction = require( '@stdlib/utils/constant-function' ); 27 var noop = require( '@stdlib/utils/noop' ); 28 var copy = require( '@stdlib/utils/copy' ); 29 var isInteger = require( '@stdlib/assert/is-number' ).isPrimitive; 30 var isObject = require( '@stdlib/assert/is-plain-object' ); 31 var isNonNegativeInteger = require( '@stdlib/assert/is-nonnegative-integer' ).isPrimitive; 32 var hasOwnProp = require( '@stdlib/assert/has-own-property' ); 33 var MAX_VALUE = require( '@stdlib/constants/float64/max' ); 34 var runif = require( './../../../base/discrete-uniform' ).factory; 35 var iteratorSymbol = require( '@stdlib/symbol/iterator' ); 36 37 38 // MAIN // 39 40 /** 41 * Returns an iterator for generating pseudorandom numbers drawn from a discrete uniform distribution. 42 * 43 * @param {integer} a - minimum support 44 * @param {integer} b - maximum support 45 * @param {Options} [options] - function options 46 * @param {PRNG} [options.prng] - pseudorandom number generator which generates uniformly distributed pseudorandom numbers 47 * @param {PRNGSeedMT19937} [options.seed] - pseudorandom number generator seed 48 * @param {PRNGStateMT19937} [options.state] - pseudorandom number generator state 49 * @param {boolean} [options.copy=true] - boolean indicating whether to copy a provided pseudorandom number generator state 50 * @param {NonNegativeInteger} [options.iter] - number of iterations 51 * @throws {TypeError} `a` must be an integer 52 * @throws {TypeError} `b` must be an integer 53 * @throws {RangeError} `a` must be less than `b` 54 * @throws {TypeError} options argument must be an object 55 * @throws {TypeError} must provide valid options 56 * @throws {Error} must provide a valid state 57 * @returns {Iterator} iterator 58 * 59 * @example 60 * var iter = iterator( 2, 6 ); 61 * 62 * var r = iter.next().value; 63 * // returns <number> 64 * 65 * r = iter.next().value; 66 * // returns <number> 67 * 68 * r = iter.next().value; 69 * // returns <number> 70 * 71 * // ... 72 */ 73 function iterator( a, b, options ) { 74 var opts; 75 var iter; 76 var rand; 77 var FLG; 78 var i; 79 if ( !isInteger( a ) ) { 80 throw new TypeError( 'invalid argument. First argument must be an integer. Value: `'+a+'`.' ); 81 } 82 if ( !isInteger( b ) ) { 83 throw new TypeError( 'invalid argument. Second argument must be an integer. Value: `'+b+'`.' ); 84 } 85 if ( a > b ) { 86 throw new RangeError( 'invalid argument. Minimum support `a` must be less than or equal to maximum support `b`. Value: `['+a+','+b+']`.'); 87 } 88 if ( arguments.length > 2 ) { 89 if ( !isObject( options ) ) { 90 throw new TypeError( 'invalid argument. Options argument must be an object. Value: `'+options+'`.' ); 91 } 92 opts = copy( options, 1 ); 93 if ( hasOwnProp( opts, 'iter' ) ) { 94 if ( !isNonNegativeInteger( opts.iter ) ) { 95 throw new TypeError( 'invalid option. `iter` option must be a nonnegative integer. Option: `'+opts.iter+'`.' ); 96 } 97 } else { 98 opts.iter = MAX_VALUE; 99 } 100 rand = runif( a, b, opts ); 101 if ( opts.prng === void 0 && opts.copy !== false ) { 102 opts.state = rand.state; // cache a copy of the PRNG state 103 } 104 } else { 105 rand = runif( a, b ); 106 opts = { 107 'iter': MAX_VALUE, 108 'state': rand.state // cache a copy of the PRNG state 109 }; 110 } 111 i = 0; 112 113 // Create an iterator protocol-compliant object: 114 iter = {}; 115 setReadOnly( iter, 'next', next ); 116 setReadOnly( iter, 'return', end ); 117 118 if ( opts && opts.prng ) { 119 setReadOnly( iter, 'seed', null ); 120 setReadOnly( iter, 'seedLength', null ); 121 setReadWriteAccessor( iter, 'state', constantFunction( null ), noop ); 122 setReadOnly( iter, 'stateLength', null ); 123 setReadOnly( iter, 'byteLength', null ); 124 } else { 125 setReadOnlyAccessor( iter, 'seed', getSeed ); 126 setReadOnlyAccessor( iter, 'seedLength', getSeedLength ); 127 setReadWriteAccessor( iter, 'state', getState, setState ); 128 setReadOnlyAccessor( iter, 'stateLength', getStateLength ); 129 setReadOnlyAccessor( iter, 'byteLength', getStateSize ); 130 } 131 setReadOnly( iter, 'PRNG', rand.PRNG ); 132 133 // If an environment supports `Symbol.iterator`, make the iterator iterable: 134 if ( iteratorSymbol ) { 135 setReadOnly( iter, iteratorSymbol, factory ); 136 } 137 return iter; 138 139 /** 140 * Returns an iterator protocol-compliant object containing the next iterated value. 141 * 142 * @private 143 * @returns {Object} iterator protocol-compliant object 144 */ 145 function next() { 146 i += 1; 147 if ( FLG || i > opts.iter ) { 148 return { 149 'done': true 150 }; 151 } 152 return { 153 'value': rand(), 154 'done': false 155 }; 156 } 157 158 /** 159 * Finishes an iterator. 160 * 161 * @private 162 * @param {*} [value] - value to return 163 * @returns {Object} iterator protocol-compliant object 164 */ 165 function end( value ) { 166 FLG = true; 167 if ( arguments.length ) { 168 return { 169 'value': value, 170 'done': true 171 }; 172 } 173 return { 174 'done': true 175 }; 176 } 177 178 /** 179 * Returns a new iterator. 180 * 181 * @private 182 * @returns {Iterator} iterator 183 */ 184 function factory() { 185 return iterator( a, b, opts ); 186 } 187 188 /** 189 * Returns the PRNG seed. 190 * 191 * @private 192 * @returns {PRNGSeedMT19937} seed 193 */ 194 function getSeed() { 195 return rand.PRNG.seed; 196 } 197 198 /** 199 * Returns the PRNG seed length. 200 * 201 * @private 202 * @returns {PositiveInteger} seed length 203 */ 204 function getSeedLength() { 205 return rand.PRNG.seedLength; 206 } 207 208 /** 209 * Returns the PRNG state length. 210 * 211 * @private 212 * @returns {PositiveInteger} state length 213 */ 214 function getStateLength() { 215 return rand.PRNG.stateLength; 216 } 217 218 /** 219 * Returns the PRNG state size (in bytes). 220 * 221 * @private 222 * @returns {PositiveInteger} state size (in bytes) 223 */ 224 function getStateSize() { 225 return rand.PRNG.byteLength; 226 } 227 228 /** 229 * Returns the current pseudorandom number generator state. 230 * 231 * @private 232 * @returns {PRNGStateMT19937} current state 233 */ 234 function getState() { 235 return rand.PRNG.state; 236 } 237 238 /** 239 * Sets the pseudorandom number generator state. 240 * 241 * @private 242 * @param {PRNGStateMT19937} s - generator state 243 * @throws {Error} must provide a valid state 244 */ 245 function setState( s ) { 246 rand.PRNG.state = s; 247 } 248 } 249 250 251 // EXPORTS // 252 253 module.exports = iterator;