UNPKG

fast-prng-wasm

Version:

A collection of fast, SIMD-enabled, pseudo random number generators in WebAssembly. Simple to use from JavaScript (node or browser), and AssemblyScript.

165 lines (136 loc) 4.36 kB
import { int32Number, int53Number, number, coord, coordSquared, JUMP_256 } from './common'; let s0: u64 = 0; let s1: u64 = 0; let s2: u64 = 0; let s3: u64 = 0; export const SEED_COUNT: i32 = 4; export function setSeed(a: u64, b: u64, c: u64, d: u64): void { s0 = a; s1 = b; s2 = c; s3 = d; nextInt64(); }; /** * Advances the state by 2^128 steps every call. Can be used to generate 2^128 * non-overlapping subsequences (with the same seed) for parallel computations. */ export function jump(): void { let jump_s0: u64 = 0; let jump_s1: u64 = 0; let jump_s2: u64 = 0; let jump_s3: u64 = 0; // loop through each 64-bit value in the jump array for (let i: i32 = 0; i < JUMP_256.length; i++) { // loop through each bit of the jump value, and if bit is 1, compute a new jump state for (let b: i32 = 0; b < 64; b++) { if ((JUMP_256[i] & (1 << b)) != 0) { jump_s0 ^= s0; jump_s1 ^= s1; jump_s2 ^= s2; jump_s3 ^= s3; } nextInt64(); } } // Set the new state s0 = jump_s0; s1 = jump_s1; s2 = jump_s2; s3 = jump_s3; } @inline export function nextInt64(): u64 { const result: u64 = s0 + s3; // Shift const t: u64 = s1 << 17; // XOR s2 ^= s0; s3 ^= s1; s1 ^= s2; s0 ^= s3; s2 ^= t; // Rotate: rotl(45) -> (sl 45 | sr (64-45)) s3 = (s3 << 45) | (s3 >> 19); return result; }; // No runtime function call penalty is incurred here because // we inline and optimize the build at compile time @inline export function nextInt53Number(): f64 { return int53Number(nextInt64()); } @inline export function nextInt32Number(): f64 { return int32Number(nextInt64()); } @inline export function nextNumber(): f64 { return number(nextInt64()); } @inline export function nextCoord(): f64 { return coord(nextInt64()); } @inline export function nextCoordSquared(): f64 { return coordSquared(nextInt64()); } // Expose array management functions from this module export { allocUint64Array, allocFloat64Array, freeArray } from './common'; /* * If we extract the following mostly-repeated functions to shared logic, * define a type for the function, and pass the generator function as a * parameter, it runs somewhat slower because of runtime function call overhead * (At least I think, and so it can't be avoided using @inline). * * So in the interest of speed over cleanliness, we repeat this logic in each * generator type. * * The same speed caveat applies when wrapping the generators in a class: * Everything slows down. So we opt instead for static functions and speed. */ /** Monte Carlo test: Count how many random points fall inside a unit circle */ export function batchTestUnitCirclePoints(count: i32): i32 { let inCircle: i32 = 0; let xSquared: f64; let ySquared: f64; for (let i: i32 = 0; i < count; i++) { xSquared = nextCoordSquared(); ySquared = nextCoordSquared(); if (xSquared + ySquared <= 1.0) { inCircle++; } } return inCircle; } export function fillUint64Array_Int64(arr: Uint64Array): void { for (let i: i32 = 0; i < arr.length; i++) { unchecked(arr[i] = nextInt64()); } } export function fillFloat64Array_Int53Numbers(arr: Float64Array): void { for (let i: i32 = 0; i < arr.length; i++) { unchecked(arr[i] = nextInt53Number()); } } export function fillFloat64Array_Int32Numbers(arr: Float64Array): void { for (let i: i32 = 0; i < arr.length; i++) { unchecked(arr[i] = nextInt32Number()); } } export function fillFloat64Array_Numbers(arr: Float64Array): void { for (let i: i32 = 0; i < arr.length; i++) { unchecked(arr[i] = nextNumber()); } } export function fillFloat64Array_Coords(arr: Float64Array): void { for (let i: i32 = 0; i < arr.length; i++) { unchecked(arr[i] = nextCoord()); } } export function fillFloat64Array_CoordsSquared(arr: Float64Array): void { for (let i: i32 = 0; i < arr.length; i++) { unchecked(arr[i] = nextCoordSquared()); } }