Skip to content

Commit 71787ad

Browse files
committed
feat: added missing prng algorithms
1 parent 91c2f3d commit 71787ad

13 files changed

+593
-75
lines changed

README.md

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
# ts-seedrandom
22

3-
Seeded random number generators for JavaScript, ported to TypeScript. Forked from https://github.com/davidbau/seedrandom.
4-
5-
> [!WARNING]
6-
> Not all functionality from original package has been ported so far. Currently, only Alea and ARC4 algorithms are available.
3+
Seeded random number generators for JavaScript, ported to TypeScript. Based on https://github.com/shanewholloway/js-esm-seedrandom.
74

85
## Installation
96

@@ -42,3 +39,17 @@ const state = aleaGenerator.state();
4239
const secondAleaGenerator = prngAlea('seed', state);
4340
```
4441

42+
## Available Algorithms
43+
44+
The following PRNG algorithms are available:
45+
46+
1. `prngAlea`: Alea algorithm
47+
2. `prngArc4`: ARC4 algorithm
48+
3. `prngTychei`: Tyche-i algorithm
49+
4. `prngXor128`: XorShift128 algorithm
50+
5. `prngXor4096`: XorShift4096 algorithm
51+
6. `prngXorshift7`: XorShift7 algorithm
52+
7. `prngXorwow`: Xorwow algorithm
53+
54+
You can import and use any of these algorithms in the same way as demonstrated in the usage examples above.
55+

package-lock.json

+10-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"name": "ts-seedrandom",
3-
"version": "0.1.0",
3+
"version": "1.0.0",
44
"type": "module",
55
"description": "Seeded random number generators for JavaScript, ported to TypeScript",
66
"author": "Jure Rotar <[email protected]>",
7-
"contributors": ["David Bau"],
7+
"contributors": ["David Bau", "Shane Holloway"],
88
"license": "MIT",
99
"homepage": "https://github.com/jurerotar/ts-seedrandom#README",
1010
"repository": {
@@ -41,7 +41,7 @@
4141
},
4242
"devDependencies": {
4343
"@biomejs/biome": "1.9.3",
44-
"seedrandom": "3.0.5",
44+
"esm-seedrandom": "^3.0.5",
4545
"typescript": "5.6.2",
4646
"vite": "5.4.8",
4747
"vitest": "2.1.2"

src/index.ts

+34-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
11
import { alea } from 'src/prng-algorithms/alea';
22
import { arc4 } from 'src/prng-algorithms/arc-4';
3-
import type { AleaGeneratorState, Arc4GeneratorState, PRNGAlgorithm, PRNGFunction } from 'src/types';
3+
import { tychei } from 'src/prng-algorithms/tychei';
4+
import { xor128 } from 'src/prng-algorithms/xor-128';
5+
import { xor4096 } from 'src/prng-algorithms/xor-4096';
6+
import { xorShift7 } from 'src/prng-algorithms/xor-shift-7';
7+
import { xorWow } from 'src/prng-algorithms/xor-wow';
8+
import type {
9+
AleaGeneratorState,
10+
Arc4GeneratorState,
11+
PRNGAlgorithm,
12+
PRNGFunction,
13+
TycheiGeneratorState,
14+
Xor128GeneratorState,
15+
Xor4096GeneratorState,
16+
XorShift7GeneratorState,
17+
XorwowGeneratorState,
18+
} from 'src/types';
419

5-
export { alea as prngAlea, arc4 as prngArc4, type PRNGAlgorithm, type PRNGFunction, type AleaGeneratorState, type Arc4GeneratorState };
20+
export {
21+
alea as prngAlea,
22+
arc4 as prngArc4,
23+
tychei as prngTychei,
24+
xor128 as prngXor128,
25+
xor4096 as prngXor4096,
26+
xorShift7 as prngXorShift7,
27+
xorWow as prngXorWow,
28+
type PRNGAlgorithm,
29+
type PRNGFunction,
30+
type AleaGeneratorState,
31+
type Arc4GeneratorState,
32+
type TycheiGeneratorState,
33+
type Xor128GeneratorState,
34+
type Xor4096GeneratorState,
35+
type XorShift7GeneratorState,
36+
type XorwowGeneratorState,
37+
};

src/prng-algorithms/__tests__/prng-algorithms.test.ts

+31-32
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,44 @@
11
// @ts-ignore
2-
import seedrandom from 'seedrandom';
3-
import { prngAlea, prngArc4 } from 'src/index';
2+
import { prng_alea, prng_arc4, prng_tychei, prng_xor128, prng_xor4096, prng_xorshift7, prng_xorwow } from 'esm-seedrandom';
3+
import { prngAlea, prngArc4, prngTychei, prngXor128, prngXor4096, prngXorShift7, prngXorWow } from 'src/index';
44
import { describe, expect, test } from 'vitest';
55

6-
// TODO: Uncomment additional tests as more algorithms are ported over
76
const prngPairs = [
87
{
98
name: 'alea',
10-
originalPrng: seedrandom.alea,
9+
originalPrng: prng_alea,
1110
portedPrng: prngAlea,
1211
},
1312
{
1413
name: 'arc4',
15-
originalPrng: seedrandom,
14+
originalPrng: prng_arc4,
1615
portedPrng: prngArc4,
1716
},
18-
// {
19-
// name: 'tychei',
20-
// originalPrng: seedrandom.tychei,
21-
// portedPrng: prngAlea,
22-
// },
23-
// {
24-
// name: 'xor128',
25-
// originalPrng: seedrandom.xor128,
26-
// portedPrng: prngAlea,
27-
// },
28-
// {
29-
// name: 'xor4096',
30-
// originalPrng: seedrandom.xor4096,
31-
// portedPrng: prngAlea,
32-
// },
33-
// {
34-
// name: 'xorShift7',
35-
// originalPrng: seedrandom.xorShift7,
36-
// portedPrng: prngAlea,
37-
// },
38-
// {
39-
// name: 'xorWow',
40-
// originalPrng: seedrandom.xorWow,
41-
// portedPrng: prngAlea,
42-
// },
17+
{
18+
name: 'tychei',
19+
originalPrng: prng_tychei,
20+
portedPrng: prngTychei,
21+
},
22+
{
23+
name: 'xor128',
24+
originalPrng: prng_xor128,
25+
portedPrng: prngXor128,
26+
},
27+
{
28+
name: 'xor4096',
29+
originalPrng: prng_xor4096,
30+
portedPrng: prngXor4096,
31+
},
32+
{
33+
name: 'xorShift7',
34+
originalPrng: prng_xorshift7,
35+
portedPrng: prngXorShift7,
36+
},
37+
{
38+
name: 'xorWow',
39+
originalPrng: prng_xorwow,
40+
portedPrng: prngXorWow,
41+
},
4342
];
4443

4544
describe('prng algorithms', () => {
@@ -49,11 +48,11 @@ describe('prng algorithms', () => {
4948
const originalPrngInstance = originalPrng('seed');
5049
const portedPrngInstance = portedPrng('seed');
5150

52-
for (let i = 0; i <= 100; i += 1) {
51+
for (let i = 0; i <= 10000; i += 1) {
5352
const originalPrngNextValue = originalPrngInstance();
5453
const portedPrngNextValue = portedPrngInstance();
5554

56-
expect(originalPrngNextValue).toEqual(portedPrngNextValue);
55+
expect(originalPrngNextValue, `Failed on iteration ${i}`).toEqual(portedPrngNextValue);
5756
}
5857
});
5958

src/prng-algorithms/alea.ts

+8-21
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { AleaGeneratorState, GeneratorInterface, PRNGAlgorithm } from 'src/types';
2+
import { mash } from 'src/utils';
23

34
class AleaGenerator implements GeneratorInterface<AleaGeneratorState> {
45
c = 1;
@@ -7,37 +8,23 @@ class AleaGenerator implements GeneratorInterface<AleaGeneratorState> {
78
s2;
89

910
constructor(seed: string | number = Date.now()) {
10-
let n = 0xefc8249d;
11-
12-
const mash = (seed: string): number => {
13-
for (let i = 0; i < seed.length; i++) {
14-
n += seed.charCodeAt(i);
15-
let h = 0.02519603282416938 * n;
16-
n = h >>> 0;
17-
h -= n;
18-
h *= n;
19-
n = h >>> 0;
20-
h -= n;
21-
n += h * 0x100000000; // 2^32
22-
}
23-
return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
24-
};
11+
const m = mash();
2512

2613
const stringifiedSeed = seed.toString();
2714

28-
this.s0 = mash(' ');
29-
this.s1 = mash(' ');
30-
this.s2 = mash(' ');
15+
this.s0 = m(' ');
16+
this.s1 = m(' ');
17+
this.s2 = m(' ');
3118

32-
this.s0 -= mash(stringifiedSeed);
19+
this.s0 -= m(stringifiedSeed);
3320
if (this.s0 < 0) {
3421
this.s0 += 1;
3522
}
36-
this.s1 -= mash(stringifiedSeed);
23+
this.s1 -= m(stringifiedSeed);
3724
if (this.s1 < 0) {
3825
this.s1 += 1;
3926
}
40-
this.s2 -= mash(stringifiedSeed);
27+
this.s2 -= m(stringifiedSeed);
4128
if (this.s2 < 0) {
4229
this.s2 += 1;
4330
}

src/prng-algorithms/tychei.ts

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import type { GeneratorInterface, PRNGAlgorithm, TycheiGeneratorState } from 'src/types';
2+
import { xorDouble } from 'src/utils';
3+
4+
const createTycheiGenerator = (seed: string | number = Date.now()): GeneratorInterface<TycheiGeneratorState> => {
5+
let a = 0;
6+
let b = 0;
7+
let c = 2654435769 | 0;
8+
let d = 1367130551;
9+
10+
const next = (): number => {
11+
b = (b << 25) ^ (b >>> 7) ^ c;
12+
c = (c - d) | 0;
13+
d = (d << 24) ^ (d >>> 8) ^ a;
14+
a = (a - b) | 0;
15+
b = (b << 20) ^ (b >>> 12) ^ c;
16+
c = (c - d) | 0;
17+
d = (d << 16) ^ (c >>> 16) ^ a;
18+
a = (a - b) | 0;
19+
20+
return a;
21+
};
22+
23+
const getState = (): TycheiGeneratorState => ({
24+
a,
25+
b,
26+
c,
27+
d,
28+
});
29+
30+
const setState = (state: TycheiGeneratorState): void => {
31+
a = state.a;
32+
b = state.b;
33+
c = state.c;
34+
d = state.d;
35+
};
36+
37+
if (Number.isInteger(seed)) {
38+
const integerSeed = seed as number;
39+
a = (integerSeed / 0x100000000) | 0;
40+
b = integerSeed | 0;
41+
}
42+
43+
const stringifiedSeed = seed.toString();
44+
45+
for (let k = 0; k < stringifiedSeed.length + 20; k++) {
46+
b ^= stringifiedSeed.charCodeAt(k) | 0;
47+
next();
48+
}
49+
50+
return {
51+
next,
52+
state: getState,
53+
setState,
54+
};
55+
};
56+
57+
export const tychei: PRNGAlgorithm<TycheiGeneratorState> = (seed, state) => {
58+
const tycheiGenerator = createTycheiGenerator(seed);
59+
60+
const prng = () => (tycheiGenerator.next() >>> 0) / 0x100000000;
61+
prng.quick = () => prng();
62+
prng.double = () => xorDouble(tycheiGenerator);
63+
prng.int32 = () => tycheiGenerator.next() | 0;
64+
prng.state = () => tycheiGenerator.state();
65+
66+
if (typeof state !== 'undefined') {
67+
tycheiGenerator.setState(state);
68+
}
69+
70+
return prng;
71+
};

0 commit comments

Comments
 (0)