UNPKG

spica

Version:

Supervisor, Coroutine, Channel, select, AtomicPromise, Cancellation, Cache, List, Queue, Stack, and some utils.

207 lines (196 loc) 6.06 kB
const bases = Object.freeze([...Array(7)].map((_, i) => 1 << i)); assert.deepStrictEqual(bases, [1, 2, 4, 8, 16, 32, 64]); const masks = Object.freeze(bases.map(radix => radix - 1)); const dict0S = [ ...[...Array(36)].map((_, i) => i.toString(36)), ...[...Array(36)].map((_, i) => i.toString(36).toUpperCase()).slice(-26), '+', '/', ].join(''); assert(dict0S.length === 64); const dict0_ = [ ...[...Array(36)].map((_, i) => i.toString(36)), ...[...Array(36)].map((_, i) => i.toString(36).toUpperCase()).slice(-26), '-', '_', ].join(''); assert(dict0_.length === 64); const dictAz = [ ...[...Array(36)].map((_, i) => i.toString(36).toUpperCase()).slice(-26), ...[...Array(36)].map((_, i) => i.toString(36)).slice(-26), ].join(''); assert(dictAz.length === 52); export const rnd10 = cons(10); export const rnd16 = cons(16); export const rnd32 = cons(32); export const rnd36 = cons(36); export const rnd62 = cons(62); export const rnd64 = cons(64); export const rnd09 = conv(rnd10, dict0_); export const rnd0f = conv(rnd16, dict0_); export const rnd0v = conv(rnd32, dict0_); export const rnd0z = conv(rnd36, dict0_); export const rnd0Z = conv(rnd62, dict0_); export const rnd0S = conv(rnd64, dict0S); export const rnd0_ = conv(rnd64, dict0_); export const rndAP = conv(rnd16, dictAz); export const rndAf = conv(rnd32, dictAz); export function unique(rng: (len: number) => string, len: number = 1, mem?: Set<string>): () => string { const independence = !mem; mem ??= new Set(); const trials = 3; let prefixes: Set<string>; let prefix = ''; return function random(): string { assert(mem = mem!); for (let i = 0; i < trials; ++i) { const r = rng(len); if (mem.has(r)) continue; try { mem.add(r); } catch (reason) { // ベンチマーク程度でもSetがパンクする場合がある。 if (!independence) throw reason; prefixes ??= new Set(); prefix ||= '?'; assert(prefix.length > 0); for (let i = 0; i < trials; ++i) { prefix = rng(prefix.length); if (prefixes.has(prefix)) continue; prefixes.add(prefix); mem.clear(); return random(); } prefixes = new Set(); prefix += '?'; return random(); } return prefix + r; } ++len; independence && mem.clear(); return random(); }; } function cons(size: number): () => number { const len = bases.findIndex(radix => radix >= size) as 1; assert(len > 0); return function rng(): number { const r = random(len); assert(r < bases[len]); return r < size ? r : rng(); }; } function conv($rng: () => number, dict: string): (len?: number, rng?: () => number) => string { return (len = 1, rng = $rng) => { let acc = ''; while (len--) { acc += dict[rng()]; } return acc; }; } const buffer = new Uint16Array(512); const digit = 16; let index = 0; let buf = 0; let offset = 0; function random(len: 1 | 2 | 3 | 4 | 5 | 6): number { assert(1 <= len && len <= 6); assert(offset < digit); if (offset < len) { if (index === 0) { crypto.getRandomValues(buffer); index = buffer.length - 1; buf = buffer[index]; offset = digit; } else { assert(digit < 32); buf = buf << digit | buffer[--index]; offset += digit; assert(offset < 32); assert(1 << offset > 0); } } assert(offset >= len); offset -= len; return buf >> offset & masks[len]; } export function xorshift(seed: number = xorshift.seed()): () => number { assert(seed >>> 0 === seed); assert(seed >= 1); assert(seed <= ~0 >>> 0); return () => { let x = seed; x ^= x << 13; x ^= x >>> 17; x ^= x << 15; return seed = x >>> 0; }; } export namespace xorshift { const max = ~0 >>> 0; assert(max + 1 >>> 0 === 0); export function seed(): number { return Math.random() * max + 1 >>> 0; } assert(max + 1 > max); assert(max / (max + 1) < 1); export function random(seed?: number): () => number { const rng = xorshift(seed); return () => rng() / (max + 1); } } const uint32n = (n: bigint): bigint => n & 2n ** 32n - 1n; const uint64n = (n: bigint): bigint => n & 2n ** 64n - 1n; // https://www.pcg-random.org/download.html // https://github.com/imneme/pcg-c/blob/master/include/pcg_variants.h export function pcg32(seed: [bigint, bigint] = pcg32.seed()): () => number { return () => pcg32.next(seed); } export namespace pcg32 { type Seed = [bigint, bigint]; const MULT = 6364136223846793005n; export function random(seed?: [bigint, bigint]): () => number { const rng = pcg32(seed); return () => rng() / 2 ** 32; } export function seed( state: bigint = BigInt(xorshift.seed()) << 32n | BigInt(xorshift.seed()), inc: bigint = BigInt(xorshift.seed()) << 32n | BigInt(xorshift.seed()), ): Seed { const seed: Seed = [0n, uint64n(inc << 1n | 1n)]; seed[0] = uint64n(seed[0] * MULT + seed[1]); seed[0] = uint64n(seed[0] + state); seed[0] = uint64n(seed[0] * MULT + seed[1]); return seed; } export function next(seed: Seed): number { assert(seed[0] !== 0n || seed[1] !== 0n); const oldstate = seed[0]; seed[0] = uint64n(oldstate * MULT + seed[1]); const xorshifted = uint32n(((oldstate >> 18n) ^ oldstate) >> 27n); const rot = oldstate >> 59n; return Number(uint32n((xorshifted >> rot) | (xorshifted << (-rot & 31n)))); } export function advance(seed: Seed, delta: bigint): Seed { delta = uint64n(delta); let acc_mult = 1n; let acc_plus = 0n; let cur_mult = MULT; let cur_plus = seed[1]; while (delta > 0) { if (delta & 1n) { acc_mult = uint64n(acc_mult * cur_mult); acc_plus = uint64n(acc_plus * cur_mult + cur_plus); } cur_plus = uint64n((cur_mult + 1n) * cur_plus); cur_mult = uint64n(cur_mult * cur_mult); delta >>= 1n; } seed[0] = uint64n(acc_mult * seed[0] + acc_plus); return seed; } }