class RandomState {
constructor(seed) {
this.source = new alea(seed);
for(const name in d3random) {
if(!name.startsWith('random')) continue;
const distribution = name[6].toLowerCase() + name.slice(7);
this[distribution] = (...args) => {
const generator = d3random[name].source(this.source)(...args);
generator.array = (size, {dtype=Float64Array}={}) => dtype.from({length:size}, () => generator());
return generator;
}
}
}
sample(func, lo, hi, {ngrid=128}={}) {
const dx = (hi - lo) / ngrid;
const x = Float64Array.from({length: ngrid+1}, (_,i) => lo + dx * i);
const fx = x.map(x => func(x));
let cdf = fx.reduce(
(out, _, i, fx) => {
out.push((i==0? 0: out[i-1] + 0.5 * (fx[i-1] + fx[i])));
return out
}, []);
cdf = cdf.map(x => x / cdf[ngrid]);
const generator = () => interp(this.uniform(0,1)(), cdf, x);
generator.array = (size, {dtype=Float64Array}={}) => interp(
this.uniform(0,1).array(size, {dtype:dtype}), cdf, x);
return generator;
}
onsphere(radius=1) {
const twopi = 2 * Math.PI;
const generator = () => {
const phi = this.uniform(0, twopi)();
const vz = this.uniform(-radius, radius)();
const vr = Math.sqrt(radius * radius - vz * vz);
const vx = vr * Math.cos(phi);
const vy = vr * Math.sin(phi);
return [vx, vy, vz];
}
generator.array = (size, {dtype=Float64Array}={}) => {
const phi = this.uniform(0, twopi).array(size, {dtype:dtype});
const vz = this.uniform(-radius, radius).array(size, {dtype:dtype});
const rsq = radius * radius;
const vr = vz.map(vz => Math.sqrt(rsq - vz * vz));
const vx = vr.map((vr,i) => vr * Math.cos(phi[i]));
const vy = vr.map((vr,i) => vr * Math.sin(phi[i]));
return [vx, vy, vz];
};
return generator;
}
shuffle(array) {
const u = this.uniform(0,1).array(array.length, {dtype:Float64Array});
const swap = Int32Array.from({length:u.length}, (_,i)=>Math.floor(u[i]*i));
for(let i=array.length-1; i>0; i--) {
const j = swap[i];
const temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
}