638 lines
17 KiB
JavaScript
638 lines
17 KiB
JavaScript
import seedrandom from 'seedrandom';
|
|
|
|
function _defineProperties(target, props) {
|
|
for (var i = 0; i < props.length; i++) {
|
|
var descriptor = props[i];
|
|
descriptor.enumerable = descriptor.enumerable || false;
|
|
descriptor.configurable = true;
|
|
if ("value" in descriptor) descriptor.writable = true;
|
|
Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
|
|
}
|
|
}
|
|
function _createClass(Constructor, protoProps, staticProps) {
|
|
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
Object.defineProperty(Constructor, "prototype", {
|
|
writable: false
|
|
});
|
|
return Constructor;
|
|
}
|
|
function _inheritsLoose(subClass, superClass) {
|
|
subClass.prototype = Object.create(superClass.prototype);
|
|
subClass.prototype.constructor = subClass;
|
|
_setPrototypeOf(subClass, superClass);
|
|
}
|
|
function _setPrototypeOf(o, p) {
|
|
_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {
|
|
o.__proto__ = p;
|
|
return o;
|
|
};
|
|
return _setPrototypeOf(o, p);
|
|
}
|
|
function _toPrimitive(input, hint) {
|
|
if (typeof input !== "object" || input === null) return input;
|
|
var prim = input[Symbol.toPrimitive];
|
|
if (prim !== undefined) {
|
|
var res = prim.call(input, hint || "default");
|
|
if (typeof res !== "object") return res;
|
|
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
}
|
|
return (hint === "string" ? String : Number)(input);
|
|
}
|
|
function _toPropertyKey(arg) {
|
|
var key = _toPrimitive(arg, "string");
|
|
return typeof key === "symbol" ? key : String(key);
|
|
}
|
|
|
|
var RNG = /*#__PURE__*/function () {
|
|
function RNG() {}
|
|
var _proto = RNG.prototype;
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
_proto._seed = function _seed(seed, _opts) {
|
|
// TODO: add entropy and stuff
|
|
if (seed === (seed || 0)) {
|
|
return seed;
|
|
} else {
|
|
var strSeed = '' + seed;
|
|
var s = 0;
|
|
for (var k = 0; k < strSeed.length; ++k) {
|
|
s ^= strSeed.charCodeAt(k) | 0;
|
|
}
|
|
return s;
|
|
}
|
|
};
|
|
return RNG;
|
|
}();
|
|
|
|
var RNGFunction = /*#__PURE__*/function (_RNG) {
|
|
_inheritsLoose(RNGFunction, _RNG);
|
|
function RNGFunction(thunk, opts) {
|
|
var _this;
|
|
_this = _RNG.call(this) || this;
|
|
_this._rng = void 0;
|
|
_this.seed(thunk, opts);
|
|
return _this;
|
|
}
|
|
var _proto = RNGFunction.prototype;
|
|
_proto.next = function next() {
|
|
return this._rng();
|
|
}
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
;
|
|
_proto.seed = function seed(thunk, _opts) {
|
|
this._rng = thunk;
|
|
};
|
|
_proto.clone = function clone(_, opts) {
|
|
return new RNGFunction(this._rng, opts);
|
|
};
|
|
_createClass(RNGFunction, [{
|
|
key: "name",
|
|
get: function get() {
|
|
return 'function';
|
|
}
|
|
}]);
|
|
return RNGFunction;
|
|
}(RNG);
|
|
|
|
/**
|
|
* Construct an RNG with variable inputs. Used in calls to Random constructor
|
|
* @param {...*} args - Distribution-specific arguments
|
|
* @return RNG
|
|
*
|
|
* @example
|
|
* new Random(RNGFactory(...args))
|
|
*/
|
|
var RNGFactory = (function () {
|
|
var args = [].slice.call(arguments);
|
|
var _args = args,
|
|
_args$ = _args[0],
|
|
arg0 = _args$ === void 0 ? 'default' : _args$;
|
|
switch (typeof arg0) {
|
|
case 'object':
|
|
if (arg0 instanceof RNG) {
|
|
return arg0;
|
|
}
|
|
break;
|
|
case 'function':
|
|
return new RNGFunction(arg0);
|
|
case 'number':
|
|
case 'string':
|
|
default:
|
|
return new RNGFunction(seedrandom.apply(void 0, args));
|
|
}
|
|
throw new Error("invalid RNG \"" + arg0 + "\"");
|
|
});
|
|
|
|
var uniform = (function (random, min, max) {
|
|
if (min === void 0) {
|
|
min = 0;
|
|
}
|
|
if (max === void 0) {
|
|
max = 1;
|
|
}
|
|
return function () {
|
|
return random.next() * (max - min) + min;
|
|
};
|
|
});
|
|
|
|
function numberValidator(num) {
|
|
return new NumberValidator(num);
|
|
}
|
|
var NumberValidator = function NumberValidator(num) {
|
|
var _this = this;
|
|
this.n = void 0;
|
|
this.isInt = function () {
|
|
if (Number.isInteger(_this.n)) {
|
|
return _this;
|
|
}
|
|
throw new Error("Expected number to be an integer, got " + _this.n);
|
|
};
|
|
this.isPositive = function () {
|
|
if (_this.n > 0) {
|
|
return _this;
|
|
}
|
|
throw new Error("Expected number to be positive, got " + _this.n);
|
|
};
|
|
this.lessThan = function (v) {
|
|
if (_this.n < v) {
|
|
return _this;
|
|
}
|
|
throw new Error("Expected number to be less than " + v + ", got " + _this.n);
|
|
};
|
|
this.greaterThanOrEqual = function (v) {
|
|
if (_this.n >= v) {
|
|
return _this;
|
|
}
|
|
throw new Error("Expected number to be greater than or equal to " + v + ", got " + _this.n);
|
|
};
|
|
this.greaterThan = function (v) {
|
|
if (_this.n > v) {
|
|
return _this;
|
|
}
|
|
throw new Error("Expected number to be greater than " + v + ", got " + _this.n);
|
|
};
|
|
this.n = num;
|
|
};
|
|
|
|
var uniformInt = (function (random, min, max) {
|
|
if (min === void 0) {
|
|
min = 0;
|
|
}
|
|
if (max === void 0) {
|
|
max = 1;
|
|
}
|
|
if (max === undefined) {
|
|
max = min === undefined ? 1 : min;
|
|
min = 0;
|
|
}
|
|
numberValidator(min).isInt();
|
|
numberValidator(max).isInt();
|
|
return function () {
|
|
return Math.floor(random.next() * (max - min + 1) + min);
|
|
};
|
|
});
|
|
|
|
var uniformBoolean = (function (random) {
|
|
return function () {
|
|
return random.next() >= 0.5;
|
|
};
|
|
});
|
|
|
|
var normal = (function (random, mu, sigma) {
|
|
if (mu === void 0) {
|
|
mu = 0;
|
|
}
|
|
if (sigma === void 0) {
|
|
sigma = 1;
|
|
}
|
|
return function () {
|
|
var x, y, r;
|
|
do {
|
|
x = random.next() * 2 - 1;
|
|
y = random.next() * 2 - 1;
|
|
r = x * x + y * y;
|
|
} while (!r || r > 1);
|
|
return mu + sigma * y * Math.sqrt(-2 * Math.log(r) / r);
|
|
};
|
|
});
|
|
|
|
var logNormal = (function (random, mu, sigma) {
|
|
if (mu === void 0) {
|
|
mu = 0;
|
|
}
|
|
if (sigma === void 0) {
|
|
sigma = 1;
|
|
}
|
|
var normal = random.normal(mu, sigma);
|
|
return function () {
|
|
return Math.exp(normal());
|
|
};
|
|
});
|
|
|
|
var bernoulli = (function (random, p) {
|
|
if (p === void 0) {
|
|
p = 0.5;
|
|
}
|
|
numberValidator(p).greaterThanOrEqual(0).lessThan(1);
|
|
return function () {
|
|
return Math.floor(random.next() + p);
|
|
};
|
|
});
|
|
|
|
var binomial = (function (random, n, p) {
|
|
if (n === void 0) {
|
|
n = 1;
|
|
}
|
|
if (p === void 0) {
|
|
p = 0.5;
|
|
}
|
|
numberValidator(n).isInt().isPositive();
|
|
numberValidator(p).greaterThanOrEqual(0).lessThan(1);
|
|
return function () {
|
|
var i = 0;
|
|
var x = 0;
|
|
while (i++ < n) {
|
|
if (random.next() < p) {
|
|
x++;
|
|
}
|
|
}
|
|
return x;
|
|
};
|
|
});
|
|
|
|
var geometric = (function (random, p) {
|
|
if (p === void 0) {
|
|
p = 0.5;
|
|
}
|
|
numberValidator(p).greaterThan(0).lessThan(1);
|
|
var invLogP = 1.0 / Math.log(1.0 - p);
|
|
return function () {
|
|
return Math.floor(1 + Math.log(random.next()) * invLogP);
|
|
};
|
|
});
|
|
|
|
var logFactorialTable = [0.0, 0.0, 0.69314718055994529, 1.791759469228055, 3.1780538303479458, 4.7874917427820458, 6.5792512120101012, 8.5251613610654147, 10.604602902745251, 12.801827480081469];
|
|
var logFactorial = function logFactorial(k) {
|
|
return logFactorialTable[k];
|
|
};
|
|
var logSqrt2PI = 0.91893853320467267;
|
|
var poisson = (function (random, lambda) {
|
|
if (lambda === void 0) {
|
|
lambda = 1;
|
|
}
|
|
numberValidator(lambda).isPositive();
|
|
if (lambda < 10) {
|
|
// inversion method
|
|
var expMean = Math.exp(-lambda);
|
|
return function () {
|
|
var p = expMean;
|
|
var x = 0;
|
|
var u = random.next();
|
|
while (u > p) {
|
|
u = u - p;
|
|
p = lambda * p / ++x;
|
|
}
|
|
return x;
|
|
};
|
|
} else {
|
|
// generative method
|
|
var smu = Math.sqrt(lambda);
|
|
var b = 0.931 + 2.53 * smu;
|
|
var a = -0.059 + 0.02483 * b;
|
|
var invAlpha = 1.1239 + 1.1328 / (b - 3.4);
|
|
var vR = 0.9277 - 3.6224 / (b - 2);
|
|
return function () {
|
|
while (true) {
|
|
var u = void 0;
|
|
var v = random.next();
|
|
if (v <= 0.86 * vR) {
|
|
u = v / vR - 0.43;
|
|
return Math.floor((2 * a / (0.5 - Math.abs(u)) + b) * u + lambda + 0.445);
|
|
}
|
|
if (v >= vR) {
|
|
u = random.next() - 0.5;
|
|
} else {
|
|
u = v / vR - 0.93;
|
|
u = (u < 0 ? -0.5 : 0.5) - u;
|
|
v = random.next() * vR;
|
|
}
|
|
var us = 0.5 - Math.abs(u);
|
|
if (us < 0.013 && v > us) {
|
|
continue;
|
|
}
|
|
var k = Math.floor((2 * a / us + b) * u + lambda + 0.445);
|
|
v = v * invAlpha / (a / (us * us) + b);
|
|
if (k >= 10) {
|
|
var t = (k + 0.5) * Math.log(lambda / k) - lambda - logSqrt2PI + k - (1 / 12.0 - (1 / 360.0 - 1 / (1260.0 * k * k)) / (k * k)) / k;
|
|
if (Math.log(v * smu) <= t) {
|
|
return k;
|
|
}
|
|
} else if (k >= 0) {
|
|
var _logFactorial;
|
|
var f = (_logFactorial = logFactorial(k)) != null ? _logFactorial : 0;
|
|
if (Math.log(v) <= k * Math.log(lambda) - lambda - f) {
|
|
return k;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
var exponential = (function (random, lambda) {
|
|
if (lambda === void 0) {
|
|
lambda = 1;
|
|
}
|
|
numberValidator(lambda).isPositive();
|
|
return function () {
|
|
return -Math.log(1 - random.next()) / lambda;
|
|
};
|
|
});
|
|
|
|
var irwinHall = (function (random, n) {
|
|
if (n === void 0) {
|
|
n = 1;
|
|
}
|
|
numberValidator(n).isInt().greaterThanOrEqual(0);
|
|
return function () {
|
|
var sum = 0;
|
|
for (var i = 0; i < n; ++i) {
|
|
sum += random.next();
|
|
}
|
|
return sum;
|
|
};
|
|
});
|
|
|
|
var bates = (function (random, n) {
|
|
if (n === void 0) {
|
|
n = 1;
|
|
}
|
|
numberValidator(n).isInt().isPositive();
|
|
var irwinHall = random.irwinHall(n);
|
|
return function () {
|
|
return irwinHall() / n;
|
|
};
|
|
});
|
|
|
|
var pareto = (function (random, alpha) {
|
|
if (alpha === void 0) {
|
|
alpha = 1;
|
|
}
|
|
numberValidator(alpha).greaterThanOrEqual(0);
|
|
var invAlpha = 1.0 / alpha;
|
|
return function () {
|
|
return 1.0 / Math.pow(1.0 - random.next(), invAlpha);
|
|
};
|
|
});
|
|
|
|
var RNGMathRandom = /*#__PURE__*/function (_RNG) {
|
|
_inheritsLoose(RNGMathRandom, _RNG);
|
|
function RNGMathRandom() {
|
|
return _RNG.apply(this, arguments) || this;
|
|
}
|
|
var _proto = RNGMathRandom.prototype;
|
|
_proto.next = function next() {
|
|
return Math.random();
|
|
}
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
;
|
|
_proto.seed = function seed(_seed, _opts) {
|
|
// intentionally empty
|
|
};
|
|
_proto.clone = function clone() {
|
|
return new RNGMathRandom();
|
|
};
|
|
_createClass(RNGMathRandom, [{
|
|
key: "name",
|
|
get: function get() {
|
|
return 'default';
|
|
}
|
|
}]);
|
|
return RNGMathRandom;
|
|
}(RNG);
|
|
|
|
/**
|
|
* Seedable random number generator supporting many common distributions.
|
|
*
|
|
* Defaults to Math.random as its underlying pseudorandom number generator.
|
|
*
|
|
* @name Random
|
|
* @class
|
|
*
|
|
* @param {RNG|function} [rng=Math.random] - Underlying pseudorandom number generator.
|
|
*/
|
|
var Random = /*#__PURE__*/function () {
|
|
function Random(rng) {
|
|
var _this = this;
|
|
this._rng = void 0;
|
|
this._patch = void 0;
|
|
this._cache = {};
|
|
this.next = function () {
|
|
return _this._rng.next();
|
|
};
|
|
this["float"] = function (min, max) {
|
|
return _this.uniform(min, max)();
|
|
};
|
|
this["int"] = function (min, max) {
|
|
return _this.uniformInt(min, max)();
|
|
};
|
|
this.integer = function (min, max) {
|
|
return _this.uniformInt(min, max)();
|
|
};
|
|
this.bool = function () {
|
|
return _this.uniformBoolean()();
|
|
};
|
|
this["boolean"] = function () {
|
|
return _this.uniformBoolean()();
|
|
};
|
|
this.uniform = function (min, max) {
|
|
return _this._memoize('uniform', uniform, min, max);
|
|
};
|
|
this.uniformInt = function (min, max) {
|
|
return _this._memoize('uniformInt', uniformInt, min, max);
|
|
};
|
|
this.uniformBoolean = function () {
|
|
return _this._memoize('uniformBoolean', uniformBoolean);
|
|
};
|
|
this.normal = function (mu, sigma) {
|
|
return normal(_this, mu, sigma);
|
|
};
|
|
this.logNormal = function (mu, sigma) {
|
|
return logNormal(_this, mu, sigma);
|
|
};
|
|
this.bernoulli = function (p) {
|
|
return bernoulli(_this, p);
|
|
};
|
|
this.binomial = function (n, p) {
|
|
return binomial(_this, n, p);
|
|
};
|
|
this.geometric = function (p) {
|
|
return geometric(_this, p);
|
|
};
|
|
this.poisson = function (lambda) {
|
|
return poisson(_this, lambda);
|
|
};
|
|
this.exponential = function (lambda) {
|
|
return exponential(_this, lambda);
|
|
};
|
|
this.irwinHall = function (n) {
|
|
return irwinHall(_this, n);
|
|
};
|
|
this.bates = function (n) {
|
|
return bates(_this, n);
|
|
};
|
|
this.pareto = function (alpha) {
|
|
return pareto(_this, alpha);
|
|
};
|
|
if (rng && rng instanceof RNG) {
|
|
this.use(rng);
|
|
} else {
|
|
this.use(new RNGMathRandom());
|
|
}
|
|
this._cache = {};
|
|
}
|
|
/**
|
|
* @member {RNG} Underlying pseudo-random number generator
|
|
*/
|
|
var _proto = Random.prototype;
|
|
/**
|
|
* Creates a new `Random` instance, optionally specifying parameters to
|
|
* set a new seed.
|
|
*
|
|
* @see RNG.clone
|
|
*
|
|
* @param {string} [seed] - Optional seed for new RNG.
|
|
* @param {object} [opts] - Optional config for new RNG options.
|
|
* @return {Random}
|
|
*/
|
|
_proto.clone = function clone() {
|
|
var args = [].slice.call(arguments);
|
|
if (args.length) {
|
|
return new Random(RNGFactory.apply(void 0, args));
|
|
} else {
|
|
return new Random(this.rng.clone());
|
|
}
|
|
}
|
|
/**
|
|
* Sets the underlying pseudorandom number generator used via
|
|
* either an instance of `seedrandom`, a custom instance of RNG
|
|
* (for PRNG plugins), or a string specifying the PRNG to use
|
|
* along with an optional `seed` and `opts` to initialize the
|
|
* RNG.
|
|
*
|
|
* @example
|
|
* import random from 'random'
|
|
*
|
|
* random.use('example_seedrandom_string')
|
|
* // or
|
|
* random.use(seedrandom('kittens'))
|
|
* // or
|
|
* random.use(Math.random)
|
|
*
|
|
* @param {...*} args
|
|
*/;
|
|
_proto.use = function use() {
|
|
this._rng = RNGFactory.apply(void 0, [].slice.call(arguments));
|
|
}
|
|
/**
|
|
* Patches `Math.random` with this Random instance's PRNG.
|
|
*/;
|
|
_proto.patch = function patch() {
|
|
if (this._patch) {
|
|
throw new Error('Math.random already patched');
|
|
}
|
|
this._patch = Math.random;
|
|
Math.random = this.uniform();
|
|
}
|
|
/**
|
|
* Restores a previously patched `Math.random` to its original value.
|
|
*/;
|
|
_proto.unpatch = function unpatch() {
|
|
if (this._patch) {
|
|
Math.random = this._patch;
|
|
delete this._patch;
|
|
}
|
|
}
|
|
// --------------------------------------------------------------------------
|
|
// Uniform utility functions
|
|
// --------------------------------------------------------------------------
|
|
/**
|
|
* Convenience wrapper around `this.rng.next()`
|
|
*
|
|
* Returns a floating point number in [0, 1).
|
|
*
|
|
* @return {number}
|
|
*/;
|
|
/**
|
|
* Returns an item chosen uniformly at trandom from the given array.
|
|
*
|
|
* Convence wrapper around `random.uniformInt()`
|
|
*
|
|
* @param {Array<T>} [array] - Lower bound (integer, inclusive)
|
|
* @return {T | undefined}
|
|
*/
|
|
_proto.choice = function choice(array) {
|
|
if (!Array.isArray(array)) {
|
|
throw new Error("Random.choice expected input to be an array, got " + typeof array);
|
|
}
|
|
var length = array == null ? void 0 : array.length;
|
|
if (length > 0) {
|
|
var index = this.uniformInt(0, length - 1)();
|
|
return array[index];
|
|
} else {
|
|
return undefined;
|
|
}
|
|
}
|
|
// --------------------------------------------------------------------------
|
|
// Uniform distributions
|
|
// --------------------------------------------------------------------------
|
|
/**
|
|
* Generates a [Continuous uniform distribution](https://en.wikipedia.org/wiki/Uniform_distribution_(continuous)).
|
|
*
|
|
* @param {number} [min=0] - Lower bound (float, inclusive)
|
|
* @param {number} [max=1] - Upper bound (float, exclusive)
|
|
* @return {function}
|
|
*/;
|
|
// --------------------------------------------------------------------------
|
|
// Internal
|
|
// --------------------------------------------------------------------------
|
|
/**
|
|
* Memoizes distributions to ensure they're only created when necessary.
|
|
*
|
|
* Returns a thunk which that returns independent, identically distributed
|
|
* samples from the specified distribution.
|
|
*
|
|
* @private
|
|
*
|
|
* @param {string} label - Name of distribution
|
|
* @param {function} getter - Function which generates a new distribution
|
|
* @param {...*} args - Distribution-specific arguments
|
|
*
|
|
* @return {function}
|
|
*/
|
|
_proto._memoize = function _memoize(label, getter) {
|
|
var args = [].slice.call(arguments, 2);
|
|
var key = "" + args.join(';');
|
|
var value = this._cache[label];
|
|
if (value === undefined || value.key !== key) {
|
|
value = {
|
|
key: key,
|
|
distribution: getter.apply(void 0, [this].concat(args))
|
|
};
|
|
this._cache[label] = value;
|
|
}
|
|
return value.distribution;
|
|
};
|
|
_createClass(Random, [{
|
|
key: "rng",
|
|
get: function get() {
|
|
return this._rng;
|
|
}
|
|
}]);
|
|
return Random;
|
|
}();
|
|
// defaults to Math.random as its RNG
|
|
var random = new Random();
|
|
|
|
export { RNG, RNGFactory, Random, random as default };
|
|
//# sourceMappingURL=random.module.js.map
|