main.js (6479B)
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 isProbability = require( '@stdlib/assert/is-probability' ).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 rbern = require( './../../../base/bernoulli' ).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 Bernoulli distribution. 42 * 43 * @param {Probability} p - success probability 44 * @param {Options} [options] - function options 45 * @param {*} [options.seed] - pseudorandom number generator seed 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} first argument must be a probability 52 * @throws {TypeError} options argument must be an object 53 * @throws {TypeError} must provide valid options 54 * @throws {Error} must provide a valid state 55 * @returns {Iterator} iterator 56 * 57 * @example 58 * var iter = iterator( 0.3 ); 59 * 60 * var r = iter.next().value; 61 * // returns <number> 62 * 63 * r = iter.next().value; 64 * // returns <number> 65 * 66 * r = iter.next().value; 67 * // returns <number> 68 * 69 * // ... 70 */ 71 function iterator( p, options ) { 72 var opts; 73 var iter; 74 var rand; 75 var FLG; 76 var i; 77 if ( !isProbability( p ) ) { 78 throw new TypeError( 'invalid argument. First argument must be a probability. Value: `'+p+'`.' ); 79 } 80 if ( arguments.length > 1 ) { 81 if ( !isObject( options ) ) { 82 throw new TypeError( 'invalid argument. Options argument must be an object. Value: `'+options+'`.' ); 83 } 84 opts = copy( options, 1 ); 85 if ( hasOwnProp( opts, 'iter' ) ) { 86 if ( !isNonNegativeInteger( opts.iter ) ) { 87 throw new TypeError( 'invalid option. `iter` option must be a nonnegative integer. Option: `'+opts.iter+'`.' ); 88 } 89 } else { 90 opts.iter = MAX_VALUE; 91 } 92 rand = rbern( p, opts ); 93 if ( opts.prng === void 0 && opts.copy !== false ) { 94 opts.state = rand.state; // cache a copy of the PRNG state 95 } 96 } else { 97 rand = rbern( p ); 98 opts = { 99 'iter': MAX_VALUE, 100 'state': rand.state // cache a copy of the PRNG state 101 }; 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 110 if ( opts && opts.prng ) { 111 setReadOnly( iter, 'seed', null ); 112 setReadOnly( iter, 'seedLength', null ); 113 setReadWriteAccessor( iter, 'state', constantFunction( null ), noop ); 114 setReadOnly( iter, 'stateLength', null ); 115 setReadOnly( iter, 'byteLength', null ); 116 } else { 117 setReadOnlyAccessor( iter, 'seed', getSeed ); 118 setReadOnlyAccessor( iter, 'seedLength', getSeedLength ); 119 setReadWriteAccessor( iter, 'state', getState, setState ); 120 setReadOnlyAccessor( iter, 'stateLength', getStateLength ); 121 setReadOnlyAccessor( iter, 'byteLength', getStateSize ); 122 } 123 setReadOnly( iter, 'PRNG', rand.PRNG ); 124 125 // If an environment supports `Symbol.iterator`, make the iterator iterable: 126 if ( iteratorSymbol ) { 127 setReadOnly( iter, iteratorSymbol, factory ); 128 } 129 return iter; 130 131 /** 132 * Returns an iterator protocol-compliant object containing the next iterated value. 133 * 134 * @private 135 * @returns {Object} iterator protocol-compliant object 136 */ 137 function next() { 138 i += 1; 139 if ( FLG || i > opts.iter ) { 140 return { 141 'done': true 142 }; 143 } 144 return { 145 'value': rand(), 146 'done': false 147 }; 148 } 149 150 /** 151 * Finishes an iterator. 152 * 153 * @private 154 * @param {*} [value] - value to return 155 * @returns {Object} iterator protocol-compliant object 156 */ 157 function end( value ) { 158 FLG = true; 159 if ( arguments.length ) { 160 return { 161 'value': value, 162 'done': true 163 }; 164 } 165 return { 166 'done': true 167 }; 168 } 169 170 /** 171 * Returns a new iterator. 172 * 173 * @private 174 * @returns {Iterator} iterator 175 */ 176 function factory() { 177 return iterator( p, opts ); 178 } 179 180 /** 181 * Returns the PRNG seed. 182 * 183 * @private 184 * @returns {PRNGSeedMT19937} seed 185 */ 186 function getSeed() { 187 return rand.PRNG.seed; 188 } 189 190 /** 191 * Returns the PRNG seed length. 192 * 193 * @private 194 * @returns {PositiveInteger} seed length 195 */ 196 function getSeedLength() { 197 return rand.PRNG.seedLength; 198 } 199 200 /** 201 * Returns the PRNG state length. 202 * 203 * @private 204 * @returns {PositiveInteger} state length 205 */ 206 function getStateLength() { 207 return rand.PRNG.stateLength; 208 } 209 210 /** 211 * Returns the PRNG state size (in bytes). 212 * 213 * @private 214 * @returns {PositiveInteger} state size (in bytes) 215 */ 216 function getStateSize() { 217 return rand.PRNG.byteLength; 218 } 219 220 /** 221 * Returns the current pseudorandom number generator state. 222 * 223 * @private 224 * @returns {PRNGStateMT19937} current state 225 */ 226 function getState() { 227 return rand.PRNG.state; 228 } 229 230 /** 231 * Sets the pseudorandom number generator state. 232 * 233 * @private 234 * @param {PRNGStateMT19937} s - generator state 235 * @throws {Error} must provide a valid state 236 */ 237 function setState( s ) { 238 rand.PRNG.state = s; 239 } 240 } 241 242 243 // EXPORTS // 244 245 module.exports = iterator;