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