UNPKG

@freeword/meta

Version:

Meta package for Freeword: exports all core types, constants, and utilities from the src/ directory.

448 lines 20.6 kB
import _ from 'lodash'; import { prng_alea as SeededRNGFactory } from 'esm-seedrandom'; import { throwable } from "./Outcome.js"; import * as Consts from "../UtilityConsts.js"; import { MAX_UINT32 } from "../UtilityConsts.js"; export const CHARCODE_0 = '0'.charCodeAt(0); // = 48 export const CHARCODE_9 = '9'.charCodeAt(0); // = 57 export const CHARCODE_A = 'A'.charCodeAt(0); // = 65 export const CHARCODE_Z = 'Z'.charCodeAt(0); // = 90 export const CHARCODE_a = 'a'.charCodeAt(0); // = 97 export const CHARCODE_z = 'z'.charCodeAt(0); // = 122 const CharCodeA52m1 = 52 + CHARCODE_A - 1; // 26 upper and 26 lower const CharCodeA62m1 = 62 + CHARCODE_A - 1; // 26 upper, 26 lower, and 10 digits // we baseline on 'A': 65-90 -> A-Z, 91-117 -> 97-122 (a-z), 117-126 -> 48-57 (0-9) const CharOffsetA52toa = 1 + CHARCODE_Z - CHARCODE_a; // if we are at 'Z+1', we want to be at 'a': Z+1-(Z+1-a) -> a const CharOffsetA62to0 = 27 + CHARCODE_Z - CHARCODE_0; // if we are at 'Z+27', we want to be at '0': Z+27-(Z+27-0) -> 0 const SINT32_TO_UINT32 = 2 ** 31; export class BaseRandomFactory { // == [Construction] constructor(rng) { Object.defineProperty(this, 'rng', { value: rng, configurable: true, enumerable: false }); } static make(rngspec) { return new this(this.rngFor(rngspec)); } static rngFor(rngspec = Math.random) { if (typeof rngspec === 'function') { return rngspec; } if (rngspec && (typeof rngspec === 'string')) { return this._seededRNG(rngspec); } throw throwable('RNG must be a non-blank string, a function, or undefined', 'badRNG', { given: rngspec }); } static _seededRNG(seed) { return SeededRNGFactory(seed).double; } // -- // == [One-shot numeric methods] /** Random float, `0 <= x < 1` */ rand01() { return this.rng(); } /** Random float, `lo <= x < hi` */ rand({ lo = 0, hi: hiIn = lo + 1 } = {}) { const range = _.max([lo, hiIn]) - lo; return lo + (this.rng() * range); } /** Random integer, `lo <= x <= hi` */ int(opts = {}) { const { lo = 0, hi = lo + 9 } = opts; return Math.floor(this.rand({ lo, hi: hi + 1 })); } /** Random integer, `0 <= x <= 2^32 - 1`. On some factories this is more efficient than `int({ hi: MAX_UINT32 })`. */ uint32() { return Math.floor(this.rand01() * MAX_UINT32); } /** Random integer, `0 <= x <= 2^32 - 1`. On some factories this is more efficient than `int({ hi: MAX_UINT32 })`. */ sint32() { return this.uint32() - SINT32_TO_UINT32; } /** Random boolean */ bool() { return (this.rand01() < 0.5); } // -- // == [One-shot character methods] /** Random character `loCharCode <= x < hiCharCode` */ char({ lo = CHARCODE_a, hi = CHARCODE_z } = {}) { return String.fromCharCode(this.int({ lo, hi })); } /** Random character `loChar <= x < hiChar` */ charBetween({ lo: loChar = 'a', hi: hiChar = 'z' } = {}) { const lo = loChar.charCodeAt(0); const hi = hiChar.charCodeAt(0); return this.char({ lo, hi }); } /** Random lower-case letter `'a'…'z'` */ lower() { return this.charBetween({ lo: 'a', hi: 'z' }); } /** Random upper-case letter `'A'…'Z'` */ upper() { return this.charBetween({ lo: 'A', hi: 'Z' }); } /** Random letter `'A'…'Z'` */ azAZ() { const charCode = this.int({ lo: CHARCODE_A, hi: CharCodeA52m1 }); if (charCode <= CHARCODE_Z) { return String.fromCharCode(charCode); } return String.fromCharCode(charCode - CharOffsetA52toa); } /** Random character `'0'..'9'…'A'..'Z'…'a'…'z'` */ azAZ09() { const charCode = this.int({ lo: CHARCODE_A, hi: CharCodeA62m1 }); if (charCode <= CHARCODE_Z) { return String.fromCharCode(charCode); } if (charCode <= CharCodeA52m1) { return String.fromCharCode(charCode - CharOffsetA52toa); } return String.fromCharCode(charCode - CharOffsetA62to0); } /** Random numeral `'0'…'9'` */ numeral() { return this.charBetween({ lo: '0', hi: '9' }); } // -- // == [Streming character methods] /** Stream of `count` random characters, `lo <= x <= hi` */ *charsStar(opts) { for (const charCode of this.intsStar(opts)) { yield String.fromCharCode(charCode); } } /** Stream of `count` random characters, `lo <= char <= hi` */ *charsBetweenStar({ lo = 'a', hi = 'z', ...opts }) { const loCharCode = lo.charCodeAt(0); const hiCharCode = hi.charCodeAt(0); for (const charCode of this.intsStar({ ...opts, lo: loCharCode, hi: hiCharCode })) { yield String.fromCharCode(charCode); } } *lowersStar(count) { yield* this.charsStar({ lo: CHARCODE_a, hi: CHARCODE_z, count }); } *uppersStar(count) { yield* this.charsStar({ lo: CHARCODE_A, hi: CHARCODE_Z, count }); } *numeralsStar(count) { yield* this.charsStar({ lo: CHARCODE_0, hi: CHARCODE_9, count }); } *azAZStar(count) { for (const charCode of this.intsStar({ lo: CHARCODE_A, hi: CharCodeA52m1, count })) { if (charCode <= CHARCODE_Z) { yield String.fromCharCode(charCode); continue; } yield String.fromCharCode(charCode - CharOffsetA52toa); } } *azAZ09Star(count) { for (const charCode of this.intsStar({ lo: CHARCODE_A, hi: CharCodeA62m1, count })) { if (charCode <= CHARCODE_Z) { yield String.fromCharCode(charCode); continue; } if (charCode <= CharCodeA52m1) { yield String.fromCharCode(charCode - CharOffsetA52toa); continue; } yield String.fromCharCode(charCode - CharOffsetA62to0); } } // -- /** Random float, `0 <= x < 1`. @note: this makes a new factory for each call. But if you're making enough numbers to matter, you should choose one of the subclasses anduse the stream methods. */ static rand01(rngspec = Math.random) { return this.rngFor(rngspec)(); } /** Random float, `lo <= x < hi`. @note: this makes a new factory for each call. But if you're making enough numbers to matter, you should choose one of the subclasses anduse the stream methods. */ static rand(opts = {}, rngspec = Math.random) { const { lo = 0, hi: hiIn = lo + 1 } = opts; const hi = _.max([lo, hiIn]); return lo + ((hi - lo) * this.rand01(rngspec)); } /** Random integer, `lo <= x <= hi`. @note: this makes a new factory for each call. But if you're making enough numbers to matter, you should choose one of the subclasses anduse the stream methods. */ static int(opts = {}, rngspec = Math.random) { const { lo = 0, hi = lo + 9 } = opts; return Math.floor(this.rand({ ...opts, lo, hi: hi + 1 }, rngspec)); } /** Random integer, `0 <= x <= 2^32 - 1`. Some subclasses can do this more efficiently. @note: this makes a new factory for each call. But if you're making enough numbers to matter, you should choose one of the subclasses anduse the stream methods. */ static uint32(rngspec = Math.random) { return Math.floor(this.rand01(rngspec) * MAX_UINT32); } /** Random integer, `-2^31 <= x <= 2^31 - 1`. Some subclasses can do this more efficiently. @note: this makes a new factory for each call. But if you're making enough numbers to matter, you should choose one of the subclasses anduse the stream methods. */ static sint32(rngspec = Math.random) { return Math.floor(this.rand01(rngspec) * SINT32_TO_UINT32); } /** Random float, `lo <= x < hi`, with only 32 bits of randomness guaranteed. Some subclasses can do this more efficiently.@note: this makes a new factory for each call. But if you're making enough numbers to matter, you should choose one of the subclasses anduse the stream methods. */ static loose01(rngspec = Math.random) { return this.rand01(rngspec); } // -- // == [Fundamental Stream methods] -- these do the work, other stuff is sugar /** Stream of `count` random numbers, `0 <= x < 1` */ *rand01sStar(count) { for (let seq = 0; seq < count; seq++) { yield this.rng(); } } /** Stream of `count` random numbers, `lo <= x < hi` (0 and lo + 1 by default) */ *randsStar(opts) { const { lo = 0, hi: hiIn = lo + 1, count } = opts; const hi = _.max([lo, hiIn]); if (hi === 1) { // don't have to multiply if hi is 1 if (lo === 0) { yield* this.rand01sStar(count); return; } // don't have to multiply or add for (const num01 of this.rand01sStar(count)) { yield lo + num01; } ; return; } if (lo === 0) { // don't have to add if lo is 0 for (const num01 of this.rand01sStar(count)) { yield num01 * hi; } ; return; } const range = hi - lo; for (const num01 of this.rand01sStar(count)) { yield lo + (num01 * range); } } /** Stream of `count` random integers, `lo <= x <= hi` -- (by default, `lo = 0` and `hi = lo + 9`) */ *intsStar(opts) { const { lo = 0, hi = lo + 9, count } = opts; for (const float of this.randsStar({ lo, hi: hi + 1, count })) { yield Math.floor(float); } } /** Stream of `count` random integers, `0 <= x <= 2^32 - 1`. (Some subclasses can be more efficient at this than `IntsStar`.) */ *uint32sStar(count) { for (const float of this.rand01sStar(count)) { yield Math.floor(float * MAX_UINT32); } } /** Stream of `count` random integers, `0 <= x <= 2^32 - 1`. (Some subclasses can be more efficient at this than `IntsStar`.) */ *sint32sStar(count) { for (const uint of this.uint32sStar(count)) { yield uint - SINT32_TO_UINT32; } } /** Stream of `count` random numbers with **32 bits of randomness**, `0 <= x < 1`. (Some subclasses can be more efficient if only 32 bits of randomness are needed.) */ *loose01sStar(count) { yield* this.rand01sStar(count); } /** Stream of `count` random numbers with **32 bits of randomness**, `lo <= x < hi`. (Some subclasses can be more efficient if only 32 bits of randomness are needed.) * (Some subclasses can be more efficient if only 32 bits of randomness are needed. */ *loosesStar(opts) { const { lo = 0, hi = lo + 1, count } = opts; if (lo === 0 && hi === 1) { yield* this.loose01sStar(count); return; } const range = hi - lo; for (const num01 of this.loose01sStar(count)) { yield lo + (num01 * range); } } // -- // == [Array methods] /** Array of `count` random numbers, `0 <= x < 1` */ rand01s(count) { return [...this.rand01sStar(count)]; } /** Array of `count` random numbers, `lo <= x < hi` */ rands(opts) { return [...this.randsStar(opts)]; } /** Array of `count` random integers, `lo <= x <= hi` */ randInts(opts) { return [...this.intsStar(opts)]; } /** Array of `count` random integers, `0 <= x <= 2^32 - 1` */ randUint32s(count) { return [...this.uint32sStar(count)]; } // -- // == [Typed Array methods] /** Uint32Array of `count` random integers, `0 <= x < 2^32 - 1` */ randUint32Array(count) { const array = new Uint32Array(count); let index = 0; for (const value of this.uint32sStar(count)) { array[index++] = value; } return array; } /** Float32Array of `count` random numbers, `0 <= x < 1` */ randFloat32Array01(count) { const array = new Float32Array(count); let index = 0; for (const value of this.loose01sStar(count)) { array[index++] = value; } return array; } /** Float32Array of `count` random numbers, `0 <= x < 1` */ randFloat32Array(opts) { const array = new Float32Array(opts.count); let index = 0; for (const value of this.loosesStar(opts)) { array[index++] = value; } return array; } /** Float64Array of `count` random numbers with 56 bits of randomness, `0 <= x < 1`. **NOTE: that's 56 bits of randomness, not 64 bits**. */ randFloat64Array01(count) { const array = new Float64Array(count); let index = 0; for (const value of this.rand01sStar(count)) { array[index++] = value; } return array; } /** Float64Array of `count` random numbers with 56 bits of randomness, `0 <= x < 1`. **NOTE: that's 56 bits of randomness, not 64 bits**. */ randFloat64Array(opts) { const array = new Float64Array(opts.count); let index = 0; for (const value of this.randsStar(opts)) { array[index++] = value; } return array; } // -- // == [static methods for rand01s] -- stream of `count` numbers, `0 <= x < 1`] /** Stream of `count` random numbers, `0 <= x < 1` */ static *rand01sStar(count, rngspec) { if (!_.isNumber(count)) { throw throwable('count must be a number', 'absentVal', count); } if (count < 0) { return; } const factory = this.make(rngspec); yield* factory.rand01sStar(count); } /** Array of `count` random numbers, `0 <= x < 1` */ static rand01s(count, rngspec) { return [...this.rand01sStar(count, rngspec)]; } /** Float32Array of `count` random numbers, `0 <= x < 1` */ static randFloat32Array01(opts, rngspec) { const factory = this.make(rngspec); return factory.randFloat32Array01(opts.count); } /** Float64Array of `count` random numbers with 56 bits of randomness, `0 <= x < 1`. **NOTE: that's 56 bits of randomness, not 64 bits**. */ static randFloat64Array01(opts, rngspec) { const factory = this.make(rngspec); return factory.randFloat64Array01(opts.count); } // -- // == [static methods for rands] -- stream of `count` numbers, `lo <= x < hi`] /** Stream of `count` random numbers, `lo <= x < hi` */ static *randsStar(opts, rngspec) { const factory = this.make(rngspec); yield* factory.randsStar(opts); } /** Array of `count` random numbers, `lo <= x < hi` */ static rands(opts, rngspec) { const factory = this.make(rngspec); return factory.rands(opts); } /** Float32Array of `count` random numbers, `lo <= x < hi` */ static randFloat32Array(opts, rngspec) { const factory = this.make(rngspec); return factory.randFloat32Array(opts); } /** Float64Array of `count` random numbers with 56 bits of randomness, `lo <= x < hi`. **NOTE: that's 56 bits of randomness, not 64 bits**. */ static randFloat64Array(opts, rngspec) { const factory = this.make(rngspec); return factory.randFloat64Array(opts); } // -- // == [static methods for randInts] -- stream of `count` integers, `lo <= x <= hi`] /** Stream of `count` random integers, `lo <= x <= hi` */ static *intsStar(opts, rngspec) { const factory = this.make(rngspec); yield* factory.intsStar(opts); } /** Array of `count` random integers, `lo <= x <= hi` */ static randInts(opts, rngspec) { const factory = this.make(rngspec); return factory.randInts(opts); } // -- // == [static methods for randUint32s] -- stream of `count` integers, `0 <= x < 2^32 - 1`] /** Stream of `count` random integers, `0 <= x <= 2^32 - 1` */ static *uint32sStar(count, rngspec) { const factory = this.make(rngspec); yield* factory.uint32sStar(count); } /** Array of `count` random integers, `0 <= x <= 2^32 - 1` */ static randUint32s(count, rngspec) { const factory = this.make(rngspec); return factory.randUint32s(count); } /** Uint32Array of `count` random integers, `0 <= x < 2^32 - 1` */ static randUint32Array(count, rngspec) { const factory = this.make(rngspec); return factory.randUint32Array(count); } // -- // == [Random Selection methods] -- *RandIndicesStar(items, opts) { const { count } = opts; const hi = (typeof items === 'number') ? items : items.length; for (const float of this.rand01sStar(count)) { yield Math.floor(hi * float); } } *RandChoicesStar(items, opts) { for (const index of this.RandIndicesStar(items, opts)) { yield items[index]; } } /** Naive sampling function storing the already picked values in a Set. * Performance of this function will decrease dramatically when `k` is a * high proportion of `n`. */ naiveSample(items, opts) { const { count } = opts; if (count >= items.length) { return Array.from(items); } const seen = new Set(); const result = []; for (const index of this.RandIndicesStar(items, { count: Infinity })) { if (seen.has(index)) { continue; } seen.add(index); result.push(items[index]); if (result.length >= count) { break; } } return result; } *TuplesStar(items, opts) { const { count, itemLen } = opts; const generator = this.intsStar({ lo: 0, hi: items.length, count: count * itemLen }); for (let seq = 0; seq < count; seq++) { const tuple = []; for (let pos = 0; pos < itemLen; pos++) { tuple.push(items[generator.next().value]); } yield tuple; } } /** Stream of `count` random sequences, each having `lo <= len <= hi` elements from `items`; by default, `lo = 1` and `hi = lo + 9`. Note: lo and hi are **inclusive**. */ *SequencesStar(items, opts) { const tupleLens = this.intsStar({ ...opts, count: Infinity }); const elements = this.RandChoicesStar(items, { count: Infinity }); const { count } = opts; for (let seq = 0; seq < count; seq++) { const tuple = []; const len = tupleLens.next().value; for (let pos = 0; pos < len; pos++) { tuple.push(elements.next().value); } yield tuple; } } // -- // == [Accessories for randStrings] -- string built of `count` independently chosen entries from `dictionary`] *RandStrsStar(substrings = Consts.CharsAZ09Bar, opts) { for (const segs of this.TuplesStar(substrings, opts)) { yield segs.join(''); } } *RandLenStrsStar(substrings = Consts.CharsAZ09Bar, opts) { for (const segs of this.SequencesStar(substrings, opts)) { yield segs.join(''); } } } export class RandomFactory extends BaseRandomFactory { } export class SeededRandomFactory extends RandomFactory { constructor(seed) { const rng = SeededRNGFactory(seed); super(rng.double); Object.defineProperty(this, 'rng', { value: rng, configurable: true, enumerable: false }); } static make(seed) { return new this(seed); } static rngFor(seed) { return SeededRNGFactory(seed).double; } /** Random float, `0 <= x < 1`. @note: this makes a new factory for each call */ static loose01(seed) { return SeededRNGFactory(seed).quick(); } /** Random integer, `0 <= x <= 2^32 - 1`. @note: this makes a new factory for each call */ static uint32(seed) { return SeededRNGFactory(seed).int32() + SINT32_TO_UINT32; } /** Random integer, `0 <= x <= 2^32 - 1`. @note: this makes a new factory for each call */ static sint32(seed) { return SeededRNGFactory(seed).int32(); } *loose01sStar(count) { for (let index = 0; index < count; index++) { yield this.rng.quick(); } } *loosesStar(opts) { const { lo = 0, hi = lo + 1, count } = opts; if (lo === 0 && hi === 1) { yield* this.loose01sStar(count); return; } const range = hi - lo; for (const num01 of this.loose01sStar(count)) { yield lo + (num01 * range); } } *uint32sStar(count) { for (let index = 0; index < count; index++) { yield this.rng.int32(); } } } //# sourceMappingURL=Random.js.map