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