You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

639 lines
17 KiB

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