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