spica
Version:
Supervisor, Coroutine, Channel, select, AtomicPromise, Cancellation, Cache, List, Queue, Stack, and some utils.
207 lines (196 loc) • 6.06 kB
text/typescript
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;
}
}