Created
September 18, 2023 09:38
-
-
Save SalvatorePreviti/55cd69800ef2e4be9135324c1bc899f1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe('newPRNG', () => { | |
test('should generate seeded random values', () => { | |
expect(newPRNG(123)(new Array(5))).toEqual([-220828386, -1556571610, 1450074142, -2011800852, 1359997245]); | |
expect(Buffer.from(newPRNG(123)(new Int8Array(20))).toString('hex')).toBe( | |
'1ef2d66d26a3389a1e566e60ec88165a3d510fe9', | |
); | |
expect(Buffer.from(newPRNG(123)(new Int8Array(20))).toString('hex')).toBe( | |
'1ef2d66d26a3389a1e566e60ec88165a3d510fe9', | |
); | |
expect(Buffer.from(newPRNG(123)(new Uint8ClampedArray(10))).toString('hex')).toBe('1ef2d66d26a3389a1e56'); | |
expect(Buffer.from(newPRNG(123)(new Int16Array(10))).toString('hex')).toBe( | |
'1ef2d66d26a3389a1e566e60ec88165a3d510fe9', | |
); | |
expect(Buffer.from(newPRNG(123)(new Uint16Array(10))).toString('hex')).toBe( | |
'1ef2d66d26a3389a1e566e60ec88165a3d510fe9', | |
); | |
expect(Buffer.from(newPRNG(123)(new Uint32Array(5))).toString('hex')).toBe( | |
'1ef2d66d26a3389a1e566e60ec88165a3d510fe9', | |
); | |
expect(Buffer.from(newPRNG(123)(new Int32Array(5))).toString('hex')).toBe( | |
'1ef2d66d26a3389a1e566e60ec88165a3d510fe9', | |
); | |
expect(Buffer.from(newPRNG(123)(new BigInt64Array(3))).toString('hex')).toBe( | |
'1ef2d66d26a3389a1e566e60ec88165a3d510fe945489fa0', | |
); | |
expect(Buffer.from(newPRNG(123)(new BigUint64Array(3))).toString('hex')).toBe( | |
'1ef2d66d26a3389a1e566e60ec88165a3d510fe945489fa0', | |
); | |
expect(Buffer.from(newPRNG(123)(new DataView(new ArrayBuffer(20))).buffer).toString('hex')).toBe( | |
'1ef2d66d26a3389a1e566e60ec88165a3d510fe9', | |
); | |
}); | |
test('should call randomizer if provided', () => { | |
let x = 0; | |
expect(Buffer.from(newPRNG(1, () => ++x)(new Uint8Array(200))).toString('hex')).toBe( | |
'db12adaf757fe47e954775b89bd6afd5be384f6981f9929c463a143e7dc208ddbf2e660be05c67303854fe1215e68c469b45f13a11ca31' + | |
'da7ef1c8f9930bd7ef983605d0c72afcd33bdcec65b9bab1204f571141e33a296167428f0cf80d76d787987e608e541f9d287da3d021d' + | |
'908a9be8bbb75d4c1e8fb195f5c7ac7379375ee31bfa3d2ae6867c288c59fd2205ab7d71768fbc1b43a1b00b28e30f71cf0be140b6a10' + | |
'827ce3d012296aaea8100089794abb717a940393fc03f0bcdf51cab7cbe4b45676e54c33', | |
); | |
expect(x).toBe(10); | |
}); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** A pseudo random number generator */ | |
export interface PRNG { | |
/** Returns a pseudorandom 32 bit unsigned integer */ | |
(): number; | |
/** Fills the given typed array with pseudorandom values */ | |
<T extends IntegerTypedArray | DataView | ArrayBuffer | number[] | null>(typedArray?: T): T; | |
} | |
/** | |
* Returns a new pseudo random generator function that can be used to fill typed arrays with random values. | |
* Based on xoshiro algorithm, see http://prng.di.unimi.it/ | |
* Extremely compact when minified, ~445 bytes. | |
* Useful for generating testing data or to obtain a repeatable sequence of pseudorandom numbers. | |
* @param seed The seed to use, a 53 bit integer | |
* @param randomizer An optional function that can returns a random integer to be mixed. | |
* @example | |
* const prng = newPRNG(123); | |
* const randomUint32: number = prng(); | |
* const bytes: Uint8Array = prng(new Uint8Array(10)); | |
* const random32bitInts: number[] = prng(new Array(12)) | |
*/ | |
export const newPRNG = /*@__PURE__*/ (seed: number, randomizer?: () => number): PRNG => { | |
let q!: number; | |
let n!: number; | |
let ab!: number; // accumulator for bytes | |
let an = 0; // number of bits available in acc | |
let state = 0x3a522fb3; // initialization and randomization | |
let s0 = 0x54e241; // xoshiro state 0 | |
let s1 = state * 97; // xoshiro state 1 | |
let s2 = s0 * 13; // xoshiro state 2 | |
let s3 = seed; // xoshiro state 3 | |
const prng = (out?: UnsafeAny) => { | |
if (out !== undefined) { | |
if (out) { | |
n = out.BYTES_PER_ELEMENT * 8; | |
if (!n || n > 4) { | |
n = out.byteLength; | |
out = n ? new Uint8Array(out.buffer || out, out.byteOffset | 0, n) : out; | |
n = n ? 8 : 32; | |
} | |
for (let i = 0, m = -1 >>> (32 - n); i < out.length; ++i) { | |
if (an < n) { | |
an = 32; | |
ab = prng(); | |
} | |
out[i] = m & (ab >>> an); | |
an -= n; | |
} | |
} | |
return out; | |
} | |
if (state > 23) { | |
state >>= 2; | |
seed = prng() - seed / 7; | |
s1 ^= (seed * 17) ^ (state & 1 && randomizer! && randomizer()); | |
} | |
if (!--state) { | |
state = 16 ^ (s2 & 7); | |
seed ^= state ^ (randomizer! && randomizer()); | |
} | |
q = s1; | |
s2 ^= s0; | |
s3 ^= s1; | |
s1 ^= s2; | |
s0 ^= s3; | |
s2 ^= q << 9; | |
s3 = (s3 << 11) ^ (s3 >> 21); | |
q *= 5; | |
return (seed ^ (((q << 7) ^ (q >> 25)) * 9)) >>> 0; | |
}; | |
return prng; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment