@xtia/alea-rc 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # Alea
2
+
3
+ ### Tame the Chaos
4
+
5
+ Alea is a utility wrapper for turning random numbers into useful values. Give it any source of randomness, get a toolkit for dice, samples, strings, and more.
6
+
7
+ * Fully typed
8
+ * Crypto-safe and seeded algorithms out-of-the-box
9
+ * No dependencies
10
+ * ~2.7kb minified
11
+ * Ranged int, array shuffling, dice roll, weighted sampling, phrase generation, uuid, bytes and many more
12
+
13
+ ## Brief:
14
+
15
+ `npm i @xtia/alea` (pending release; use `@xtia/alea-rc` to preview)
16
+
17
+ ```ts
18
+ import { alea, cryptoAlea } from "@xtia/alea";
19
+
20
+ // generate values (driven by Math.random())
21
+ const damage = alea.roll(2, 6); // 2d6
22
+ const duration = alea.between(1000, 1500);
23
+ const loot = alea.chance(0.125) ? "epic" : "common";
24
+ const id = alea.string(5);
25
+ const npcName = alea.sample(["Alice", "Bob", "Charlie"]);
26
+
27
+ // secure source (driven by environment's crypto)
28
+ const key = cryptoAlea.string(16);
29
+ ```
30
+
31
+ ## Custom sources
32
+
33
+ Use any provider as RNG source:
34
+
35
+ ```ts
36
+ import {
37
+ createAleaFromFunc,
38
+ createAleaFromSeed,
39
+ createAleaFromByteSource,
40
+ } from "@xtia/alea";
41
+
42
+ const deterministic = createAleaFromSeed("abc123");
43
+ const xkcdRng = createAleaFromFunc(() => 4/6); // https://xkcd.com/221/
44
+ const secure = createAleaFromByteSource(
45
+ buf => hardwareRNG.fillRandomBytes(buf)
46
+ );
47
+
48
+ // or use provided PRNG algorithms:
49
+ import {
50
+ mulberry32,
51
+ sfc32,
52
+ xoshiro128pp,
53
+ } from "@xtia/alea/prng";
54
+
55
+ // each returns an Alea instance:
56
+ const fast = mulberry32("my-seed");
57
+ const varied = sfc32(1, 2, 3, 4);
58
+ const strong = xoshiro128pp(5, 6, 7, 8);
59
+
60
+ const reproducibleId = varied.string(5);
61
+ ```
package/browser.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./common";
2
+ export declare const cryptoAlea: import("./common").Alea;
package/browser.js ADDED
@@ -0,0 +1,3 @@
1
+ import { createAleaFromByteSource } from "./internal/factories";
2
+ export * from "./common";
3
+ export const cryptoAlea = createAleaFromByteSource(arr => globalThis.crypto.getRandomValues(arr));
package/common.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { Alea } from "./internal/alea";
2
+ export type { Alea };
3
+ export { createAleaFromByteSource, createAleaFromSeed, createAleaFromFunc, } from "./internal/factories";
4
+ export { charsets } from "./internal/util";
5
+ export declare const alea: Alea;
package/common.js ADDED
@@ -0,0 +1,4 @@
1
+ import { Alea } from "./internal/alea";
2
+ export { createAleaFromByteSource, createAleaFromSeed, createAleaFromFunc, } from "./internal/factories";
3
+ export { charsets } from "./internal/util";
4
+ export const alea = new Alea(Math.random);
@@ -0,0 +1,130 @@
1
+ type RandomFunction = () => number;
2
+ type PhraseFunc = (parse: (template: string) => string) => string;
3
+ export declare class Alea {
4
+ /**
5
+ * Generate a float between 0 and 1 (exclusive)
6
+ */
7
+ readonly next: RandomFunction;
8
+ /**
9
+ * @param next Source RNG - a function that returns a value >= 0 and < 1
10
+ */
11
+ constructor(next: RandomFunction);
12
+ /**
13
+ * Generate a series of normalised random values
14
+ * @param count
15
+ * @returns Random values
16
+ */
17
+ batch(count: number): number[];
18
+ /**
19
+ * Pick a random item from an array
20
+ * @param items
21
+ * @returns Random item from an array
22
+ */
23
+ sample<T>(items: ArrayLike<T>): T;
24
+ /**
25
+ * Pick a number of unique random items from an array
26
+ * @param items
27
+ * @param count
28
+ * @returns Random items from an array
29
+ */
30
+ sample<T>(items: ArrayLike<T>, count: number): T[];
31
+ /**
32
+ * Get a boolean value with a (`probability` in 1) chance of being `true`
33
+ * @param probability
34
+ * @returns Random boolean
35
+ */
36
+ chance(probability: number): boolean;
37
+ /**
38
+ * Get a shuffled copy of an array
39
+ * @param items
40
+ * @returns Shuffled copy of an array
41
+ */
42
+ shuffle<T>(items: ArrayLike<T>): T[];
43
+ /**
44
+ * Get a value between `min` and `max`
45
+ * @param min Minimum value, *inclusive*
46
+ * @param max Maximum value, *exclusive*
47
+ * @returns Random value in range
48
+ */
49
+ between(min: number, max: number): number;
50
+ /**
51
+ * Generate a random string, drawing a given character set
52
+ * @param length
53
+ * @param charset
54
+ * @returns Generated string
55
+ */
56
+ string(length: number, charset?: string): string;
57
+ /**
58
+ * Generates a phrase from a table and a root string
59
+ * @example
60
+ * ```ts
61
+ * const message = alea.phrase({
62
+ * greeting: ["hello", "hi", "{int} blessings"],
63
+ * addressee: ["world", "planet", "{adjective} @xtia user"],
64
+ * adjective: ["beautiful", "wonderful"],
65
+ * int: () => alea.int(0, 9).toString(),
66
+ * }, "{greeting}, {addressee}!")
67
+ * ```
68
+ * @param table
69
+ * @param root
70
+ * @returns Generated phrase
71
+ */
72
+ phrase(table: Record<string, ArrayLike<string> | string | PhraseFunc>, root: string): string;
73
+ /**
74
+ * Create a factory to pick random items from a biased list
75
+ * @param table Candidate table, as an array of `[value, weight]` tuples
76
+ * @returns Random item factory
77
+ */
78
+ createWeightedSampler<T>(table: [value: T, weight: number][]): {
79
+ sample: () => T;
80
+ };
81
+ /**
82
+ * Generate a sequence of bytes
83
+ * @param count
84
+ * @returns Random byte array
85
+ */
86
+ bytes(count: number): Uint8Array<ArrayBuffer>;
87
+ /**
88
+ * Round a value up or down, according to probability defined by its non-integral part
89
+ * @example
90
+ * ```ts
91
+ * const rawDamage = weapon.damage / armour.protection;
92
+ * hp -= alea.round(rawDamage);
93
+ * // HP remains integer while law of averages applies fractional damage
94
+ * ```
95
+ * @param n
96
+ * @returns Randomly rounded value
97
+ */
98
+ round(n: number): number;
99
+ /**
100
+ * Get a random gaussian normal value
101
+ * @param mean
102
+ * @param deviation
103
+ * @returns Random gaussian normal
104
+ */
105
+ normal(mean: number, deviation: number): number;
106
+ /**
107
+ * Get a random integer value between `min` and `max`, inclusive.
108
+ * @param min Minimum value, **inclusive**
109
+ * @param max Maximum value, **inclusive**
110
+ * @returns Random int value
111
+ */
112
+ int(min: number, max: number): number;
113
+ /**
114
+ * Roll dice
115
+ * @param count Number of dice to roll (default 1)
116
+ * @param sides Number of sides per die (default 6)
117
+ * @returns Dice result
118
+ */
119
+ roll(count?: number, sides?: number): number;
120
+ /**
121
+ * Generate a random UUID (version 4)
122
+ *
123
+ * **Security note**: output is only as cryptographically secure as
124
+ * an instance's PRNG source, which, by default, is not.
125
+ * @see {@link Alea.crypto}
126
+ * @returns Random UUID string
127
+ */
128
+ uuid(): string;
129
+ }
130
+ export {};
@@ -0,0 +1,232 @@
1
+ import { charsets } from "./util";
2
+ export class Alea {
3
+ /**
4
+ * @param next Source RNG - a function that returns a value >= 0 and < 1
5
+ */
6
+ constructor(next) {
7
+ this.next = next;
8
+ }
9
+ /**
10
+ * Generate a series of normalised random values
11
+ * @param count
12
+ * @returns Random values
13
+ */
14
+ batch(count) {
15
+ if (!Number.isInteger(count) || count < 0) {
16
+ throw new RangeError("count must be a non-negative integer");
17
+ }
18
+ return Array.from({ length: count }, () => this.next());
19
+ }
20
+ sample(items, count) {
21
+ if (count === undefined) {
22
+ if (items.length === 0)
23
+ throw new RangeError("Empty sample source");
24
+ return items[Math.floor(this.next() * items.length)];
25
+ }
26
+ if (!Number.isInteger(count) || count < 0) {
27
+ throw new RangeError("count must be a non-negative integer");
28
+ }
29
+ if (count > items.length) {
30
+ throw new RangeError(`Cannot sample ${count} items from array of length ${items.length}`);
31
+ }
32
+ const result = Array.from({ length: count }, (_, index) => items[index]);
33
+ for (let i = count; i < items.length; i++) {
34
+ const replaceIndex = Math.floor(this.next() * (i + 1));
35
+ if (replaceIndex < count) {
36
+ result[replaceIndex] = items[i];
37
+ }
38
+ }
39
+ return result;
40
+ }
41
+ /**
42
+ * Get a boolean value with a (`probability` in 1) chance of being `true`
43
+ * @param probability
44
+ * @returns Random boolean
45
+ */
46
+ chance(probability) {
47
+ return this.next() < probability;
48
+ }
49
+ /**
50
+ * Get a shuffled copy of an array
51
+ * @param items
52
+ * @returns Shuffled copy of an array
53
+ */
54
+ shuffle(items) {
55
+ const shuffled = Array.from(items);
56
+ for (let i = shuffled.length - 1; i > 0; i--) {
57
+ const j = Math.floor(this.next() * (i + 1));
58
+ [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
59
+ }
60
+ return shuffled;
61
+ }
62
+ /**
63
+ * Get a value between `min` and `max`
64
+ * @param min Minimum value, *inclusive*
65
+ * @param max Maximum value, *exclusive*
66
+ * @returns Random value in range
67
+ */
68
+ between(min, max) {
69
+ const range = max - min;
70
+ return min + range * this.next();
71
+ }
72
+ /**
73
+ * Generate a random string, drawing a given character set
74
+ * @param length
75
+ * @param charset
76
+ * @returns Generated string
77
+ */
78
+ string(length, charset = charsets.alphanumericMixedCase) {
79
+ if (!Number.isInteger(length) || length < 0)
80
+ throw new RangeError("length must be a non-negative integer");
81
+ if (!charset || charset.length === 0)
82
+ throw new RangeError("charset must not be empty");
83
+ const chars = Array.from({ length }, () => charset[Math.floor(this.next() * charset.length)]);
84
+ return chars.join('');
85
+ }
86
+ /**
87
+ * Generates a phrase from a table and a root string
88
+ * @example
89
+ * ```ts
90
+ * const message = alea.phrase({
91
+ * greeting: ["hello", "hi", "{int} blessings"],
92
+ * addressee: ["world", "planet", "{adjective} @xtia user"],
93
+ * adjective: ["beautiful", "wonderful"],
94
+ * int: () => alea.int(0, 9).toString(),
95
+ * }, "{greeting}, {addressee}!")
96
+ * ```
97
+ * @param table
98
+ * @param root
99
+ * @returns Generated phrase
100
+ */
101
+ phrase(table, root) {
102
+ return root.replace(/\{([^}]+)\}/g, ((_, key) => {
103
+ if (table[key] === undefined)
104
+ return `{${key}}`;
105
+ if (typeof table[key] == "function") {
106
+ return table[key](template => this.phrase(table, template));
107
+ }
108
+ const source = table[key];
109
+ if (typeof source == "string")
110
+ return this.phrase(table, source);
111
+ return this.phrase(table, this.sample(source));
112
+ }));
113
+ }
114
+ /**
115
+ * Create a factory to pick random items from a biased list
116
+ * @param table Candidate table, as an array of `[value, weight]` tuples
117
+ * @returns Random item factory
118
+ */
119
+ createWeightedSampler(table) {
120
+ const filtered = table.filter(v => Number.isFinite(v[1]) && v[1] > 0);
121
+ if (filtered.length === 0) {
122
+ throw new Error("Weighted source has no viable candidates");
123
+ }
124
+ const cumulative = new Array(filtered.length);
125
+ let total = 0;
126
+ for (let i = 0; i < filtered.length; i++) {
127
+ total += filtered[i][1];
128
+ cumulative[i] = total;
129
+ }
130
+ return {
131
+ sample: () => {
132
+ const r = this.between(0, total);
133
+ let lo = 0;
134
+ let hi = cumulative.length - 1;
135
+ while (lo < hi) {
136
+ const mid = (lo + hi) >>> 1;
137
+ if (r < cumulative[mid])
138
+ hi = mid;
139
+ else
140
+ lo = mid + 1;
141
+ }
142
+ return filtered[lo][0];
143
+ },
144
+ };
145
+ }
146
+ /**
147
+ * Generate a sequence of bytes
148
+ * @param count
149
+ * @returns Random byte array
150
+ */
151
+ bytes(count) {
152
+ const arr = new Uint8Array(count);
153
+ for (let i = 0; i < count; i++)
154
+ arr[i] = this.int(0, 255);
155
+ return arr;
156
+ }
157
+ /**
158
+ * Round a value up or down, according to probability defined by its non-integral part
159
+ * @example
160
+ * ```ts
161
+ * const rawDamage = weapon.damage / armour.protection;
162
+ * hp -= alea.round(rawDamage);
163
+ * // HP remains integer while law of averages applies fractional damage
164
+ * ```
165
+ * @param n
166
+ * @returns Randomly rounded value
167
+ */
168
+ round(n) {
169
+ const floor = Math.floor(n);
170
+ return this.chance(n - floor) ? floor + 1 : floor;
171
+ }
172
+ /**
173
+ * Get a random gaussian normal value
174
+ * @param mean
175
+ * @param deviation
176
+ * @returns Random gaussian normal
177
+ */
178
+ normal(mean, deviation) {
179
+ let u1 = this.next();
180
+ while (u1 <= Number.EPSILON) {
181
+ u1 = this.next();
182
+ }
183
+ const u2 = this.next();
184
+ const mag = Math.sqrt(-2 * Math.log(u1));
185
+ const angle = 2 * Math.PI * u2;
186
+ return mean + mag * Math.cos(angle) * deviation;
187
+ }
188
+ /**
189
+ * Get a random integer value between `min` and `max`, inclusive.
190
+ * @param min Minimum value, **inclusive**
191
+ * @param max Maximum value, **inclusive**
192
+ * @returns Random int value
193
+ */
194
+ int(min, max) {
195
+ return Math.floor(this.between(min, max + 1));
196
+ }
197
+ /**
198
+ * Roll dice
199
+ * @param count Number of dice to roll (default 1)
200
+ * @param sides Number of sides per die (default 6)
201
+ * @returns Dice result
202
+ */
203
+ roll(count = 1, sides = 6) {
204
+ let total = 0;
205
+ for (let i = 0; i < count; i++) {
206
+ total += this.int(1, sides);
207
+ }
208
+ return total;
209
+ }
210
+ /**
211
+ * Generate a random UUID (version 4)
212
+ *
213
+ * **Security note**: output is only as cryptographically secure as
214
+ * an instance's PRNG source, which, by default, is not.
215
+ * @see {@link Alea.crypto}
216
+ * @returns Random UUID string
217
+ */
218
+ uuid() {
219
+ // xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
220
+ const bytes = this.bytes(16);
221
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
222
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
223
+ const hex = Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
224
+ return [
225
+ hex.slice(0, 8),
226
+ hex.slice(8, 12),
227
+ hex.slice(12, 16),
228
+ hex.slice(16, 20),
229
+ hex.slice(20, 32)
230
+ ].join('-');
231
+ }
232
+ }
@@ -0,0 +1,34 @@
1
+ import { Alea } from "./alea";
2
+ /**
3
+ * Create an Alea instance using a byte generator, such as `crypto`,
4
+ * as a RNG source
5
+ * @example
6
+ * ```ts
7
+ * const cryptoAlea = createAleaFromByteSource(crypto.getRandomValues);
8
+ * const hwAlea = createAleaFromByteSource(hardwareRng.fillBytes);
9
+ * ```
10
+ * @param applyBytes A callback that fills a Uint8Array with random bytes
11
+ * @returns A byte generator-sourced Alea instance
12
+ */
13
+ export declare function createAleaFromByteSource(applyBytes: (buffer: Uint8Array) => void): Alea;
14
+ /**
15
+ * Create an Alea instance using a Mulberry32 source
16
+ *
17
+ * Fast, with decent statistical quality
18
+ *
19
+ * For applications requiring higher statistical quality or different characteristics, see the specialized PRNGs in @xtia/Alea/prng
20
+ * @param seed
21
+ * @returns Alea instance using Mulberry32
22
+ */
23
+ export declare function createAleaFromSeed(seed: number | string): Alea;
24
+ /**
25
+ * Create an Alea instance using a custom function as an RNG source
26
+ * @example
27
+ * ```ts
28
+ * const basicAlea = createAleaFromFunc(Math.random);
29
+ * const lcgAlea = createAleaFromFunc(customRng.next);
30
+ * ```
31
+ * @param fn Source RNG; a function that returns a value >= 0 and < 1
32
+ * @returns Custom function-sourced Alea instance
33
+ */
34
+ export declare function createAleaFromFunc(fn: () => number): Alea;
@@ -0,0 +1,48 @@
1
+ import { mulberry32 } from "../prng";
2
+ import { Alea } from "./alea";
3
+ /**
4
+ * Create an Alea instance using a byte generator, such as `crypto`,
5
+ * as a RNG source
6
+ * @example
7
+ * ```ts
8
+ * const cryptoAlea = createAleaFromByteSource(crypto.getRandomValues);
9
+ * const hwAlea = createAleaFromByteSource(hardwareRng.fillBytes);
10
+ * ```
11
+ * @param applyBytes A callback that fills a Uint8Array with random bytes
12
+ * @returns A byte generator-sourced Alea instance
13
+ */
14
+ export function createAleaFromByteSource(applyBytes) {
15
+ const buffer = new ArrayBuffer(4);
16
+ const view = new Uint8Array(buffer);
17
+ const uint32View = new Uint32Array(buffer);
18
+ return new Alea(() => {
19
+ applyBytes(view);
20
+ return uint32View[0] / 4294967296;
21
+ });
22
+ }
23
+ /**
24
+ * Create an Alea instance using a Mulberry32 source
25
+ *
26
+ * Fast, with decent statistical quality
27
+ *
28
+ * For applications requiring higher statistical quality or different characteristics, see the specialized PRNGs in @xtia/Alea/prng
29
+ * @param seed
30
+ * @returns Alea instance using Mulberry32
31
+ */
32
+ export function createAleaFromSeed(seed) {
33
+ return mulberry32(seed);
34
+ }
35
+ /**
36
+ * Create an Alea instance using a custom function as an RNG source
37
+ * @example
38
+ * ```ts
39
+ * const basicAlea = createAleaFromFunc(Math.random);
40
+ * const lcgAlea = createAleaFromFunc(customRng.next);
41
+ * ```
42
+ * @param fn Source RNG; a function that returns a value >= 0 and < 1
43
+ * @returns Custom function-sourced Alea instance
44
+ */
45
+ export function createAleaFromFunc(fn) {
46
+ return new Alea(fn);
47
+ }
48
+ // const xkcdAlea = createAleaFromFunc(() => 4/6); // decided by die roll
@@ -0,0 +1,15 @@
1
+ export declare function murmur3_32(key: string, seed?: number): number;
2
+ export declare const charsets: {
3
+ lowercase: string;
4
+ uppercase: string;
5
+ numbers: string;
6
+ hexadecimalUppercase: string;
7
+ hexadecimalLowercase: string;
8
+ alphanumericUppercase: string;
9
+ alphanumericLowercase: string;
10
+ alphanumericMixedCase: string;
11
+ urlSafe: string;
12
+ wide: string;
13
+ exclude: (charset: string, excluded: string) => string;
14
+ unique: (charset: string) => string;
15
+ };
@@ -0,0 +1,63 @@
1
+ export function murmur3_32(key, seed = 0) {
2
+ let remainder = key.length & 3;
3
+ let bytes = key.length - remainder;
4
+ let h1 = seed >>> 0;
5
+ const c1 = 0xcc9e2d51;
6
+ const c2 = 0x1b873593;
7
+ let i = 0;
8
+ while (i < bytes) {
9
+ let k1 = (key.charCodeAt(i) & 0xff)
10
+ | ((key.charCodeAt(i + 1) & 0xff) << 8)
11
+ | ((key.charCodeAt(i + 2) & 0xff) << 16)
12
+ | ((key.charCodeAt(i + 3) & 0xff) << 24);
13
+ i += 4;
14
+ k1 = Math.imul(k1, c1);
15
+ k1 = (k1 << 15) | (k1 >>> 17);
16
+ k1 = Math.imul(k1, c2);
17
+ h1 ^= k1;
18
+ h1 = (h1 << 13) | (h1 >>> 19);
19
+ h1 = (Math.imul(h1, 5) + 0xe6546b64) | 0;
20
+ }
21
+ let k1 = 0;
22
+ switch (remainder) {
23
+ case 3:
24
+ k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
25
+ case 2:
26
+ k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
27
+ case 1:
28
+ k1 ^= (key.charCodeAt(i) & 0xff);
29
+ k1 = Math.imul(k1, c1);
30
+ k1 = (k1 << 15) | (k1 >>> 17);
31
+ k1 = Math.imul(k1, c2);
32
+ h1 ^= k1;
33
+ }
34
+ h1 ^= key.length;
35
+ h1 ^= h1 >>> 16;
36
+ h1 = Math.imul(h1, 0x85ebca6b);
37
+ h1 ^= h1 >>> 13;
38
+ h1 = Math.imul(h1, 0xc2b2ae35);
39
+ h1 ^= h1 >>> 16;
40
+ return h1 >>> 0;
41
+ }
42
+ const lowercase = "abcdefghijklmnopqrstuvwxyz";
43
+ const uppercase = lowercase.toUpperCase();
44
+ const numbers = "0123456789";
45
+ export const charsets = {
46
+ lowercase,
47
+ uppercase,
48
+ numbers,
49
+ hexadecimalUppercase: numbers + "ABCDEF",
50
+ hexadecimalLowercase: numbers + "abcdef",
51
+ alphanumericUppercase: uppercase + numbers,
52
+ alphanumericLowercase: lowercase + numbers,
53
+ alphanumericMixedCase: lowercase + uppercase + numbers,
54
+ urlSafe: uppercase + lowercase + numbers + "_-.~",
55
+ wide: uppercase + lowercase + numbers + "_-+=[]{};#:@~,./<>?!$%^&*()",
56
+ exclude: (charset, excluded) => {
57
+ const excludedSet = new Set([...excluded]);
58
+ return [...charset].filter(c => !excludedSet.has(c)).join("");
59
+ },
60
+ unique: (charset) => {
61
+ return [...new Set([...charset])].join("");
62
+ },
63
+ };
package/node.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./common";
2
+ export declare const cryptoAlea: import("./common").Alea;
package/node.js ADDED
@@ -0,0 +1,6 @@
1
+ import { createAleaFromByteSource } from "./internal/factories";
2
+ import { randomBytes } from 'node:crypto';
3
+ export * from "./common";
4
+ export const cryptoAlea = createAleaFromByteSource(arr => {
5
+ randomBytes(arr.length).copy(arr);
6
+ });
package/other.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { Alea } from "./internal/alea";
2
+ export * from "./common";
3
+ /**
4
+ * An Alea instance that uses the local environment's `crypto` provider
5
+ */
6
+ export declare const cryptoAlea: Alea;
package/other.js ADDED
@@ -0,0 +1,8 @@
1
+ import { Alea } from "./internal/alea";
2
+ export * from "./common";
3
+ /**
4
+ * An Alea instance that uses the local environment's `crypto` provider
5
+ */
6
+ export const cryptoAlea = new Alea(() => {
7
+ throw new Error("cryptoAlea is not available in this environment. Consider using createAleaFromByteSource() with your environment's crypto API.");
8
+ });
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@xtia/alea-rc",
3
+ "version": "0.0.1",
4
+ "description": "RNG utilities",
5
+ "repository": {
6
+ "url": "https://github.com/tiadrop/alea",
7
+ "type": "github"
8
+ },
9
+ "sideEffects": false,
10
+ "types": "./index.d.ts",
11
+ "main": "./index.js",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./other.d.ts",
15
+ "browser": "./browser.js",
16
+ "workerd": "./browser.js",
17
+ "deno": "./browser.js",
18
+ "node": "./node.js",
19
+ "bun": "./node.js",
20
+ "default": "./other.js"
21
+ },
22
+ "./prng": {
23
+ "types": "./prng/index.d.ts",
24
+ "default": "./prng/index.js"
25
+ },
26
+ "./internal/*": null
27
+ },
28
+ "scripts": {
29
+ "prepublishOnly": "cp ../README.md .",
30
+ "postpublish": "rm README.md"
31
+ },
32
+ "keywords": [
33
+ "random",
34
+ "rng"
35
+ ],
36
+ "author": "Aleta Lovelace",
37
+ "license": "MIT"
38
+ }
@@ -0,0 +1,3 @@
1
+ export { mulberry32 } from "./mulberry32";
2
+ export { sfc32 } from "./sfc32";
3
+ export { xoshiro128pp } from "./xoshiro128pp";
package/prng/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { mulberry32 } from "./mulberry32";
2
+ export { sfc32 } from "./sfc32";
3
+ export { xoshiro128pp } from "./xoshiro128pp";
@@ -0,0 +1,9 @@
1
+ import { Alea } from "../internal/alea";
2
+ /**
3
+ * Create an Alea instance using a Mulberry32 source
4
+ *
5
+ * Fast, with decent statistical quality
6
+ * @param seed
7
+ * @returns Alea instance using Mulberry32
8
+ */
9
+ export declare const mulberry32: (seed: number | string) => Alea;
@@ -0,0 +1,19 @@
1
+ import { Alea } from "../internal/alea";
2
+ import { murmur3_32 } from "../internal/util";
3
+ /**
4
+ * Create an Alea instance using a Mulberry32 source
5
+ *
6
+ * Fast, with decent statistical quality
7
+ * @param seed
8
+ * @returns Alea instance using Mulberry32
9
+ */
10
+ export const mulberry32 = (seed) => {
11
+ let nseed = typeof seed == "string" ? murmur3_32(seed) : (seed >>> 0);
12
+ return new Alea(() => {
13
+ nseed |= 0;
14
+ nseed = nseed + 0x6D2B79F5 | 0;
15
+ let t = Math.imul(nseed ^ nseed >>> 15, 1 | nseed);
16
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
17
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
18
+ });
19
+ };
@@ -0,0 +1,12 @@
1
+ import { Alea } from "../internal/alea";
2
+ /**
3
+ * Create an Alea instance using a Small Fast Counter (SFC32) source
4
+ *
5
+ * Fairly fast, with high statistical quality
6
+ * @param a Seed A
7
+ * @param b Seed B
8
+ * @param c Seed C
9
+ * @param d Seed D
10
+ * @returns Alea isntance using SFC32
11
+ */
12
+ export declare function sfc32(a: number | string, b: number | string, c: number | string, d: number | string): Alea;
package/prng/sfc32.js ADDED
@@ -0,0 +1,31 @@
1
+ import { Alea } from "../internal/alea";
2
+ import { murmur3_32 } from "../internal/util";
3
+ /**
4
+ * Create an Alea instance using a Small Fast Counter (SFC32) source
5
+ *
6
+ * Fairly fast, with high statistical quality
7
+ * @param a Seed A
8
+ * @param b Seed B
9
+ * @param c Seed C
10
+ * @param d Seed D
11
+ * @returns Alea isntance using SFC32
12
+ */
13
+ export function sfc32(a, b, c, d) {
14
+ const toWord = (v) => typeof v === "number" ? (v >>> 0) : murmur3_32(String(v));
15
+ let s0 = toWord(a) | 0;
16
+ let s1 = toWord(b) | 0;
17
+ let s2 = toWord(c) | 0;
18
+ let s3 = toWord(d) | 0;
19
+ return new Alea(() => {
20
+ s0 |= 0;
21
+ s1 |= 0;
22
+ s2 |= 0;
23
+ s3 |= 0;
24
+ const t = (s0 + s1 | 0) + s3 | 0;
25
+ s3 = s3 + 1 | 0;
26
+ s0 = s1 ^ s1 >>> 9;
27
+ s1 = s2 + (s2 << 3) | 0;
28
+ s2 = (s2 << 21 | s2 >>> 11) + t | 0;
29
+ return (t >>> 0) / 4294967296;
30
+ });
31
+ }
@@ -0,0 +1,12 @@
1
+ import { Alea } from "../internal/alea";
2
+ /**
3
+ * Create an Alea instance using a Xoshiro128++ source
4
+ *
5
+ * Very high statistical quality
6
+ * @param a Seed A
7
+ * @param b Seed B
8
+ * @param c Seed C
9
+ * @param d Seed D
10
+ * @returns Alea instance using Xoshiro128++
11
+ */
12
+ export declare function xoshiro128pp(a: number | string, b: number | string, c: number | string, d: number | string): Alea;
@@ -0,0 +1,41 @@
1
+ import { Alea } from "../internal/alea";
2
+ import { murmur3_32 } from "../internal/util";
3
+ function rotl(x, k) {
4
+ return (x << k) | (x >>> (32 - k));
5
+ }
6
+ /**
7
+ * Create an Alea instance using a Xoshiro128++ source
8
+ *
9
+ * Very high statistical quality
10
+ * @param a Seed A
11
+ * @param b Seed B
12
+ * @param c Seed C
13
+ * @param d Seed D
14
+ * @returns Alea instance using Xoshiro128++
15
+ */
16
+ export function xoshiro128pp(a, b, c, d) {
17
+ const toWord = (v) => typeof v === "number" ? v >>> 0 : murmur3_32(String(v));
18
+ let s0 = toWord(a) | 0;
19
+ let s1 = toWord(b) | 0;
20
+ let s2 = toWord(c) | 0;
21
+ let s3 = toWord(d) | 0;
22
+ // requires at least one non-zero value
23
+ if (s0 === 0 && s1 === 0 && s2 === 0 && s3 === 0) {
24
+ s0 = 1;
25
+ }
26
+ return new Alea(() => {
27
+ s0 |= 0;
28
+ s1 |= 0;
29
+ s2 |= 0;
30
+ s3 |= 0;
31
+ const result = (rotl(s0 + s3, 7) + s0) >>> 0;
32
+ const t = s1 << 9;
33
+ s2 ^= s0;
34
+ s3 ^= s1;
35
+ s1 ^= s2;
36
+ s0 ^= s3;
37
+ s2 ^= t;
38
+ s3 = rotl(s3, 11);
39
+ return result / 4294967296;
40
+ });
41
+ }