@cloudglides/nox 1.1.0
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/.github/workflows/build-on-tag.yml +67 -0
- package/IMPROVEMENTS.md +58 -0
- package/LICENSE +15 -0
- package/README.md +119 -0
- package/docs/API.md +70 -0
- package/package.json +25 -0
- package/src/core.js +8 -0
- package/src/generators/index.js +7 -0
- package/src/generators/logistic.js +19 -0
- package/src/generators/mixer.js +20 -0
- package/src/generators/mt19937.js +74 -0
- package/src/generators/pcg64.js +44 -0
- package/src/generators/splitmix64.js +32 -0
- package/src/generators/tent.js +24 -0
- package/src/generators/xorshift64.js +38 -0
- package/src/index.d.ts +114 -0
- package/src/index.js +31 -0
- package/src/presets.js +15 -0
- package/src/rng.js +141 -0
- package/src/utils/bits.js +25 -0
- package/src/utils/categorical.js +57 -0
- package/src/utils/combinatorics.js +85 -0
- package/src/utils/distributions-extra.js +32 -0
- package/src/utils/distributions-special.js +21 -0
- package/src/utils/distributions.js +69 -0
- package/src/utils/entropy.js +57 -0
- package/src/utils/index.js +30 -0
- package/src/utils/noise.js +81 -0
- package/src/utils/sampling.js +106 -0
- package/src/utils/seed.js +14 -0
- package/src/utils/seeding.js +37 -0
- package/src/utils/sequence.js +53 -0
- package/src/utils/state.js +39 -0
- package/src/utils/statistics.js +127 -0
- package/src/utils/stochastic.js +32 -0
- package/test/advanced.js +71 -0
- package/test/basic.js +13 -0
- package/test/benchmark.js +35 -0
- package/test/comprehensive.js +100 -0
- package/test/profile.js +73 -0
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export interface IGenerator {
|
|
2
|
+
next(): number | bigint;
|
|
3
|
+
nextInt(max?: number): number;
|
|
4
|
+
nextFloat(): number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type GeneratorConstructor = new (seed?: number | bigint) => IGenerator;
|
|
8
|
+
|
|
9
|
+
export class RNG {
|
|
10
|
+
gen: IGenerator;
|
|
11
|
+
constructor(generator?: GeneratorConstructor | IGenerator, seed?: number | bigint);
|
|
12
|
+
next(): any;
|
|
13
|
+
nextInt(max?: number): number;
|
|
14
|
+
nextFloat(): number;
|
|
15
|
+
int(min: number, max: number): number;
|
|
16
|
+
bool(probability?: number): boolean;
|
|
17
|
+
range(min: number, max: number, step?: number): number;
|
|
18
|
+
choice<T>(arr: T[]): T;
|
|
19
|
+
batch<T>(count: number, fn: (rng: RNG, index: number) => T): T[];
|
|
20
|
+
floats(count: number): number[];
|
|
21
|
+
ints(count: number, max?: number): number[];
|
|
22
|
+
bools(count: number, probability?: number): boolean[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function rng(): RNG;
|
|
26
|
+
export function deterministic(seed: number | bigint): RNG;
|
|
27
|
+
|
|
28
|
+
export function normal(rng: RNG | IGenerator, mean?: number, stddev?: number): number;
|
|
29
|
+
export function exponential(rng: RNG | IGenerator, lambda?: number): number;
|
|
30
|
+
export function uniform(rng: RNG | IGenerator, min: number, max: number): number;
|
|
31
|
+
export function poisson(rng: RNG | IGenerator, lambda: number): number;
|
|
32
|
+
|
|
33
|
+
export function shuffle<T>(arr: T[], rng: RNG | IGenerator, inPlace?: boolean): T[];
|
|
34
|
+
export function pick<T>(arr: T[], rng: RNG | IGenerator): T;
|
|
35
|
+
export function sample<T>(arr: T[], count: number, rng: RNG | IGenerator): T[];
|
|
36
|
+
|
|
37
|
+
export function saveState(rng: RNG): any;
|
|
38
|
+
export function restoreState(rng: RNG, snapshot: any): void;
|
|
39
|
+
export function cloneGenerator(rng: RNG): RNG;
|
|
40
|
+
|
|
41
|
+
export function weightedPick<T>(arr: T[], weights: number[], rng: RNG | IGenerator): T;
|
|
42
|
+
export function weightedSample<T>(arr: T[], weights: number[], count: number, rng: RNG | IGenerator): T[];
|
|
43
|
+
export function reservoirSample<T>(stream: T[], k: number, rng: RNG | IGenerator): T[];
|
|
44
|
+
|
|
45
|
+
export interface TestResult {
|
|
46
|
+
mean?: number;
|
|
47
|
+
variance?: number;
|
|
48
|
+
stdDev?: number;
|
|
49
|
+
tStatistic?: number;
|
|
50
|
+
expectedMean?: number;
|
|
51
|
+
expectedVariance?: number;
|
|
52
|
+
chi2Statistic?: number;
|
|
53
|
+
degreesOfFreedom?: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface KSTestResult {
|
|
57
|
+
statistic: number;
|
|
58
|
+
pass_0_10: boolean;
|
|
59
|
+
pass_0_05: boolean;
|
|
60
|
+
pass_0_01: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function meanTest(data: number[], expectedMean?: number): TestResult;
|
|
64
|
+
export function varianceTest(data: number[], expectedVariance?: number): TestResult;
|
|
65
|
+
export function kolmogorovSmirnovTest(data: number[]): KSTestResult;
|
|
66
|
+
|
|
67
|
+
export class Xorshift64 {
|
|
68
|
+
constructor(seed?: number | bigint);
|
|
69
|
+
next(): bigint;
|
|
70
|
+
nextInt(max?: number): number;
|
|
71
|
+
nextFloat(): number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export class Splitmix64 {
|
|
75
|
+
constructor(seed?: number | bigint);
|
|
76
|
+
next(): bigint;
|
|
77
|
+
nextInt(max?: number): number;
|
|
78
|
+
nextFloat(): number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export class PCG64 {
|
|
82
|
+
constructor(seed?: bigint, inc?: bigint);
|
|
83
|
+
next(): bigint;
|
|
84
|
+
nextInt(max?: number): number;
|
|
85
|
+
nextFloat(): number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export class MT19937 {
|
|
89
|
+
constructor(seed?: number);
|
|
90
|
+
next(): number;
|
|
91
|
+
nextInt(max?: number): number;
|
|
92
|
+
nextFloat(): number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export class Logistic {
|
|
96
|
+
constructor(seed?: number, r?: number);
|
|
97
|
+
next(): number;
|
|
98
|
+
nextInt(max?: number): number;
|
|
99
|
+
nextFloat(): number;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class Tent {
|
|
103
|
+
constructor(seed?: number, mu?: number);
|
|
104
|
+
next(): number;
|
|
105
|
+
nextInt(max?: number): number;
|
|
106
|
+
nextFloat(): number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export class Mixer {
|
|
110
|
+
constructor(rng1: any, rng2: any);
|
|
111
|
+
next(): bigint;
|
|
112
|
+
nextInt(max?: number): number;
|
|
113
|
+
nextFloat(): number;
|
|
114
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export {
|
|
2
|
+
rng,
|
|
3
|
+
RNG,
|
|
4
|
+
deterministic,
|
|
5
|
+
normal,
|
|
6
|
+
exponential,
|
|
7
|
+
uniform,
|
|
8
|
+
poisson,
|
|
9
|
+
shuffle,
|
|
10
|
+
pick,
|
|
11
|
+
sample,
|
|
12
|
+
saveState,
|
|
13
|
+
restoreState,
|
|
14
|
+
cloneGenerator,
|
|
15
|
+
weightedPick,
|
|
16
|
+
weightedSample,
|
|
17
|
+
reservoirSample,
|
|
18
|
+
meanTest,
|
|
19
|
+
varianceTest,
|
|
20
|
+
kolmogorovSmirnovTest
|
|
21
|
+
} from './core.js';
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
Xorshift64,
|
|
25
|
+
Splitmix64,
|
|
26
|
+
PCG64,
|
|
27
|
+
MT19937,
|
|
28
|
+
Logistic,
|
|
29
|
+
Tent,
|
|
30
|
+
Mixer
|
|
31
|
+
} from './generators/index.js';
|
package/src/presets.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { RNG } from './rng.js';
|
|
2
|
+
import { PCG64 } from './generators/index.js';
|
|
3
|
+
|
|
4
|
+
export const deterministic = (seed) => {
|
|
5
|
+
if (seed === null || seed === undefined) {
|
|
6
|
+
throw new TypeError('Deterministic mode requires an explicit seed');
|
|
7
|
+
}
|
|
8
|
+
if (typeof seed !== 'number' && typeof seed !== 'bigint') {
|
|
9
|
+
throw new TypeError('Seed must be a number or bigint');
|
|
10
|
+
}
|
|
11
|
+
if (typeof seed === 'number' && !Number.isFinite(seed)) {
|
|
12
|
+
throw new Error('Seed must be a finite number');
|
|
13
|
+
}
|
|
14
|
+
return new RNG(PCG64, seed);
|
|
15
|
+
};
|
package/src/rng.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { PCG64 } from './generators/index.js';
|
|
2
|
+
import { combined } from './utils/entropy.js';
|
|
3
|
+
|
|
4
|
+
export class RNG {
|
|
5
|
+
constructor(generator = null, seed = null) {
|
|
6
|
+
if (generator === null) {
|
|
7
|
+
seed = seed || combined();
|
|
8
|
+
this.gen = new PCG64(seed);
|
|
9
|
+
} else if (typeof generator === 'function') {
|
|
10
|
+
this.gen = new generator(seed || combined());
|
|
11
|
+
} else if (typeof generator === 'object' && generator !== null) {
|
|
12
|
+
this.gen = generator;
|
|
13
|
+
} else {
|
|
14
|
+
throw new TypeError('Generator must be a constructor function or instance');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
next() {
|
|
19
|
+
return this.gen.next();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
nextInt(max = 2147483647) {
|
|
23
|
+
return this.gen.nextInt(max);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
nextFloat() {
|
|
27
|
+
return this.gen.nextFloat();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
int(min, max) {
|
|
31
|
+
if (typeof min !== 'number' || typeof max !== 'number') {
|
|
32
|
+
throw new TypeError('int() requires two number arguments');
|
|
33
|
+
}
|
|
34
|
+
if (!Number.isInteger(min) || !Number.isInteger(max)) {
|
|
35
|
+
throw new TypeError('int() arguments must be integers');
|
|
36
|
+
}
|
|
37
|
+
if (min > max) [min, max] = [max, min];
|
|
38
|
+
return Math.floor(this.nextFloat() * (max - min + 1)) + min;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
bool(probability = 0.5) {
|
|
42
|
+
if (typeof probability !== 'number') {
|
|
43
|
+
throw new TypeError('probability must be a number');
|
|
44
|
+
}
|
|
45
|
+
if (probability < 0 || probability > 1) {
|
|
46
|
+
throw new RangeError('probability must be between 0 and 1');
|
|
47
|
+
}
|
|
48
|
+
return this.nextFloat() < probability;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
range(min, max, step = 1) {
|
|
52
|
+
if (typeof min !== 'number' || typeof max !== 'number' || typeof step !== 'number') {
|
|
53
|
+
throw new TypeError('min, max, and step must be numbers');
|
|
54
|
+
}
|
|
55
|
+
if (!Number.isInteger(min) || !Number.isInteger(max) || !Number.isInteger(step)) {
|
|
56
|
+
throw new Error('min, max, and step must be integers');
|
|
57
|
+
}
|
|
58
|
+
if (step <= 0) {
|
|
59
|
+
throw new Error('step must be positive');
|
|
60
|
+
}
|
|
61
|
+
if (min > max) {
|
|
62
|
+
throw new Error('min must be less than or equal to max');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const count = Math.floor((max - min) / step) + 1;
|
|
66
|
+
const idx = this.nextInt(count);
|
|
67
|
+
return min + idx * step;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
choice(arr) {
|
|
71
|
+
if (!Array.isArray(arr) || arr.length === 0) {
|
|
72
|
+
throw new TypeError('choice() requires non-empty array');
|
|
73
|
+
}
|
|
74
|
+
return arr[this.nextInt(arr.length)];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
batch(count, fn) {
|
|
78
|
+
if (typeof count !== 'number' || !Number.isInteger(count)) {
|
|
79
|
+
throw new TypeError('count must be an integer');
|
|
80
|
+
}
|
|
81
|
+
if (count < 0) {
|
|
82
|
+
throw new RangeError('count must be non-negative');
|
|
83
|
+
}
|
|
84
|
+
if (typeof fn !== 'function') {
|
|
85
|
+
throw new TypeError('fn must be a function');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const result = [];
|
|
89
|
+
for (let i = 0; i < count; i++) {
|
|
90
|
+
result.push(fn(this, i));
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
floats(count) {
|
|
96
|
+
if (typeof count !== 'number' || !Number.isInteger(count)) {
|
|
97
|
+
throw new TypeError('count must be an integer');
|
|
98
|
+
}
|
|
99
|
+
if (count < 0) {
|
|
100
|
+
throw new RangeError('count must be non-negative');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const result = new Array(count);
|
|
104
|
+
for (let i = 0; i < count; i++) {
|
|
105
|
+
result[i] = this.nextFloat();
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
ints(count, max = 2147483647) {
|
|
111
|
+
if (typeof count !== 'number' || !Number.isInteger(count)) {
|
|
112
|
+
throw new TypeError('count must be an integer');
|
|
113
|
+
}
|
|
114
|
+
if (count < 0) {
|
|
115
|
+
throw new RangeError('count must be non-negative');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const result = new Array(count);
|
|
119
|
+
for (let i = 0; i < count; i++) {
|
|
120
|
+
result[i] = this.nextInt(max);
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
bools(count, probability = 0.5) {
|
|
126
|
+
if (typeof count !== 'number' || !Number.isInteger(count)) {
|
|
127
|
+
throw new TypeError('count must be an integer');
|
|
128
|
+
}
|
|
129
|
+
if (count < 0) {
|
|
130
|
+
throw new RangeError('count must be non-negative');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const result = new Array(count);
|
|
134
|
+
for (let i = 0; i < count; i++) {
|
|
135
|
+
result[i] = this.bool(probability);
|
|
136
|
+
}
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export const rng = () => new RNG();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const rotateBits = (val, shift) => {
|
|
2
|
+
const mask = BigInt(64);
|
|
3
|
+
shift = BigInt(shift) % mask;
|
|
4
|
+
return (val << shift) | (val >> (mask - shift));
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const extractBits = (val, start, length) => {
|
|
8
|
+
const mask = (BigInt(1) << BigInt(length)) - BigInt(1);
|
|
9
|
+
return (val >> BigInt(start)) & mask;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const hammingWeight = (val) => {
|
|
13
|
+
let count = 0;
|
|
14
|
+
while (val > 0n) {
|
|
15
|
+
count += val & 1n ? 1 : 0;
|
|
16
|
+
val >>= 1n;
|
|
17
|
+
}
|
|
18
|
+
return count;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const bitRange = (val, min, max) => {
|
|
22
|
+
const shift = BigInt(min);
|
|
23
|
+
const mask = (BigInt(1) << BigInt(max - min)) - BigInt(1);
|
|
24
|
+
return (val >> shift) & mask;
|
|
25
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export const categorical = (rng, categories, probabilities) => {
|
|
2
|
+
if (!categories || !probabilities) throw new Error('Categories and probabilities required');
|
|
3
|
+
if (categories.length === 0) throw new Error('Categories cannot be empty');
|
|
4
|
+
if (probabilities.length !== categories.length) {
|
|
5
|
+
throw new Error('Probabilities and categories must have same length');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const total = probabilities.reduce((a, b) => a + b, 0);
|
|
9
|
+
if (total <= 0) throw new Error('Probabilities must sum to positive value');
|
|
10
|
+
|
|
11
|
+
const normalized = probabilities.map(p => p / total);
|
|
12
|
+
|
|
13
|
+
let cumsum = 0;
|
|
14
|
+
const cumulative = normalized.map(p => (cumsum += p));
|
|
15
|
+
|
|
16
|
+
const rand = rng.nextFloat();
|
|
17
|
+
for (let i = 0; i < cumulative.length; i++) {
|
|
18
|
+
if (rand <= cumulative[i]) {
|
|
19
|
+
return categories[i];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return categories[categories.length - 1];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const multinomial = (rng, n, categories, probabilities) => {
|
|
27
|
+
const result = {};
|
|
28
|
+
for (const cat of categories) {
|
|
29
|
+
result[cat] = 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (let i = 0; i < n; i++) {
|
|
33
|
+
const pick = categorical(rng, categories, probabilities);
|
|
34
|
+
result[pick]++;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const categorical2D = (rng, matrix) => {
|
|
41
|
+
const rows = matrix.length;
|
|
42
|
+
const cols = matrix[0].length;
|
|
43
|
+
|
|
44
|
+
const total = matrix.flat().reduce((a, b) => a + b, 0);
|
|
45
|
+
let rand = rng.nextFloat() * total;
|
|
46
|
+
|
|
47
|
+
for (let i = 0; i < rows; i++) {
|
|
48
|
+
for (let j = 0; j < cols; j++) {
|
|
49
|
+
rand -= matrix[i][j];
|
|
50
|
+
if (rand <= 0) {
|
|
51
|
+
return [i, j];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return [rows - 1, cols - 1];
|
|
57
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export const combinations = (arr, k) => {
|
|
2
|
+
if (k > arr.length) return [];
|
|
3
|
+
if (k === 1) return arr.map(x => [x]);
|
|
4
|
+
if (k === arr.length) return [arr];
|
|
5
|
+
|
|
6
|
+
const result = [];
|
|
7
|
+
const combine = (start, current) => {
|
|
8
|
+
if (current.length === k) {
|
|
9
|
+
result.push([...current]);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
for (let i = start; i < arr.length; i++) {
|
|
13
|
+
current.push(arr[i]);
|
|
14
|
+
combine(i + 1, current);
|
|
15
|
+
current.pop();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
combine(0, []);
|
|
20
|
+
return result;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const permutations = (arr) => {
|
|
24
|
+
if (arr.length <= 1) return [arr];
|
|
25
|
+
|
|
26
|
+
const result = [];
|
|
27
|
+
const permute = (arr, start) => {
|
|
28
|
+
if (start === arr.length - 1) {
|
|
29
|
+
result.push([...arr]);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
for (let i = start; i < arr.length; i++) {
|
|
33
|
+
[arr[start], arr[i]] = [arr[i], arr[start]];
|
|
34
|
+
permute(arr, start + 1);
|
|
35
|
+
[arr[start], arr[i]] = [arr[i], arr[start]];
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
permute([...arr], 0);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const kPermutations = (arr, k) => {
|
|
44
|
+
if (k > arr.length) return [];
|
|
45
|
+
if (k === 1) return arr.map(x => [x]);
|
|
46
|
+
|
|
47
|
+
const result = [];
|
|
48
|
+
const perm = (available, current) => {
|
|
49
|
+
if (current.length === k) {
|
|
50
|
+
result.push([...current]);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
for (let i = 0; i < available.length; i++) {
|
|
54
|
+
const item = available[i];
|
|
55
|
+
const remaining = available.slice(0, i).concat(available.slice(i + 1));
|
|
56
|
+
perm(remaining, [...current, item]);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
perm(arr, []);
|
|
61
|
+
return result;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const randomCombination = (arr, k, rng) => {
|
|
65
|
+
const indices = [];
|
|
66
|
+
const n = arr.length;
|
|
67
|
+
|
|
68
|
+
while (indices.length < k) {
|
|
69
|
+
const idx = rng.nextInt(n);
|
|
70
|
+
if (!indices.includes(idx)) {
|
|
71
|
+
indices.push(idx);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return indices.sort((a, b) => a - b).map(i => arr[i]);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const randomPermutation = (arr, rng) => {
|
|
79
|
+
const copy = [...arr];
|
|
80
|
+
for (let i = copy.length - 1; i > 0; i--) {
|
|
81
|
+
const j = rng.nextInt(i + 1);
|
|
82
|
+
[copy[i], copy[j]] = [copy[j], copy[i]];
|
|
83
|
+
}
|
|
84
|
+
return copy;
|
|
85
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const beta = (rng, alpha, beta) => {
|
|
2
|
+
const u = -Math.log(rng.nextFloat()) / alpha;
|
|
3
|
+
const v = -Math.log(rng.nextFloat()) / beta;
|
|
4
|
+
return u / (u + v);
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const gamma = (rng, shape, scale = 1) => {
|
|
8
|
+
if (shape < 1) {
|
|
9
|
+
return gamma(rng, shape + 1, scale) * Math.pow(rng.nextFloat(), 1 / shape);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const d = shape - 1 / 3;
|
|
13
|
+
const c = 1 / Math.sqrt(9 * d);
|
|
14
|
+
|
|
15
|
+
let z, v, u;
|
|
16
|
+
do {
|
|
17
|
+
do {
|
|
18
|
+
const x = (rng.nextFloat() * 2 - 1);
|
|
19
|
+
v = 1 + c * x;
|
|
20
|
+
} while (v <= 0);
|
|
21
|
+
|
|
22
|
+
v = v * v * v;
|
|
23
|
+
u = rng.nextFloat();
|
|
24
|
+
z = -Math.log(u);
|
|
25
|
+
} while (u > 1 - 0.0331 * (x * x) * (x * x) && Math.log(z + Math.log(d)) > Math.log(d * (Math.log(v) + 1)));
|
|
26
|
+
|
|
27
|
+
return d * v * scale;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const chi2 = (rng, k) => {
|
|
31
|
+
return gamma(rng, k / 2, 2);
|
|
32
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const weibull = (rng, shape, scale = 1) => {
|
|
2
|
+
const u = rng.nextFloat();
|
|
3
|
+
return scale * Math.pow(-Math.log(1 - u), 1 / shape);
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export const lognormal = (rng, mu, sigma) => {
|
|
7
|
+
const u1 = rng.nextFloat();
|
|
8
|
+
const u2 = rng.nextFloat();
|
|
9
|
+
const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
|
10
|
+
return Math.exp(mu + sigma * z);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const rayleigh = (rng, sigma = 1) => {
|
|
14
|
+
const u = rng.nextFloat();
|
|
15
|
+
return sigma * Math.sqrt(-2 * Math.log(u));
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const cauchy = (rng, median = 0, scale = 1) => {
|
|
19
|
+
const u = rng.nextFloat();
|
|
20
|
+
return median + scale * Math.tan(Math.PI * (u - 0.5));
|
|
21
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export const normal = (rng, mean = 0, stddev = 1) => {
|
|
2
|
+
if (!rng || typeof rng.nextFloat !== 'function') {
|
|
3
|
+
throw new TypeError('First argument must be RNG instance');
|
|
4
|
+
}
|
|
5
|
+
if (typeof mean !== 'number' || typeof stddev !== 'number') {
|
|
6
|
+
throw new TypeError('mean and stddev must be numbers');
|
|
7
|
+
}
|
|
8
|
+
if (stddev <= 0) {
|
|
9
|
+
throw new Error('stddev must be positive');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let u1, u2;
|
|
13
|
+
do {
|
|
14
|
+
u1 = rng.nextFloat();
|
|
15
|
+
u2 = rng.nextFloat();
|
|
16
|
+
} while (u1 <= 0 || u2 <= 0);
|
|
17
|
+
|
|
18
|
+
const z0 = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
|
19
|
+
return z0 * stddev + mean;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const exponential = (rng, lambda = 1) => {
|
|
23
|
+
if (!rng || typeof rng.nextFloat !== 'function') {
|
|
24
|
+
throw new TypeError('First argument must be RNG instance');
|
|
25
|
+
}
|
|
26
|
+
if (typeof lambda !== 'number' || lambda <= 0) {
|
|
27
|
+
throw new Error('lambda must be a positive number');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let u = rng.nextFloat();
|
|
31
|
+
while (u === 0 || u === 1) {
|
|
32
|
+
u = rng.nextFloat();
|
|
33
|
+
}
|
|
34
|
+
return -Math.log(1 - u) / lambda;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const poisson = (rng, lambda) => {
|
|
38
|
+
if (!rng || typeof rng.nextFloat !== 'function') {
|
|
39
|
+
throw new TypeError('First argument must be RNG instance');
|
|
40
|
+
}
|
|
41
|
+
if (typeof lambda !== 'number' || lambda <= 0) {
|
|
42
|
+
throw new Error('lambda must be a positive number');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const L = Math.exp(-lambda);
|
|
46
|
+
let k = 0;
|
|
47
|
+
let p = 1;
|
|
48
|
+
|
|
49
|
+
do {
|
|
50
|
+
k++;
|
|
51
|
+
p *= rng.nextFloat();
|
|
52
|
+
} while (p > L);
|
|
53
|
+
|
|
54
|
+
return k - 1;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const uniform = (rng, min, max) => {
|
|
58
|
+
if (!rng || typeof rng.nextFloat !== 'function') {
|
|
59
|
+
throw new TypeError('First argument must be RNG instance');
|
|
60
|
+
}
|
|
61
|
+
if (typeof min !== 'number' || typeof max !== 'number') {
|
|
62
|
+
throw new TypeError('min and max must be numbers');
|
|
63
|
+
}
|
|
64
|
+
if (min >= max) {
|
|
65
|
+
throw new Error('min must be less than max');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return min + rng.nextFloat() * (max - min);
|
|
69
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { performance } from 'perf_hooks';
|
|
2
|
+
import { randomBytes } from 'crypto';
|
|
3
|
+
|
|
4
|
+
let cryptoCache = null;
|
|
5
|
+
let cryptoCacheTime = 0;
|
|
6
|
+
|
|
7
|
+
export const fromPerformance = () => {
|
|
8
|
+
const t = performance.now();
|
|
9
|
+
return BigInt(Math.floor(t * 1000)) ^ BigInt(Math.floor(t * 1000000) % 1000000);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const fromMemory = () => {
|
|
13
|
+
if (typeof process !== 'undefined' && process.memoryUsage) {
|
|
14
|
+
const mem = process.memoryUsage();
|
|
15
|
+
const total = mem.heapUsed + mem.external + mem.heapTotal;
|
|
16
|
+
return BigInt(Math.floor(total)) ^ BigInt(Date.now());
|
|
17
|
+
}
|
|
18
|
+
return BigInt(Date.now());
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const fromCrypto = (bytes = 8) => {
|
|
22
|
+
try {
|
|
23
|
+
const now = Date.now();
|
|
24
|
+
if (cryptoCache && (now - cryptoCacheTime) < 100) {
|
|
25
|
+
return cryptoCache;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const buf = randomBytes(Math.max(bytes, 8));
|
|
29
|
+
let val = 0n;
|
|
30
|
+
for (let i = 0; i < buf.length; i++) {
|
|
31
|
+
val = (val << 8n) | BigInt(buf[i]);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
cryptoCache = val;
|
|
35
|
+
cryptoCacheTime = now;
|
|
36
|
+
return val;
|
|
37
|
+
} catch {
|
|
38
|
+
return BigInt(Math.random() * Number.MAX_SAFE_INTEGER);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const combined = () => {
|
|
43
|
+
const perf = fromPerformance();
|
|
44
|
+
const mem = fromMemory();
|
|
45
|
+
const crypto = fromCrypto();
|
|
46
|
+
|
|
47
|
+
let mix = perf ^ mem ^ crypto;
|
|
48
|
+
mix = mix ^ (mix >> 33n);
|
|
49
|
+
mix = (mix * 0xff51afd7ed558ccdn) & ((1n << 64n) - 1n);
|
|
50
|
+
|
|
51
|
+
return mix !== 0n ? mix : 1n;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const clearCryptoCache = () => {
|
|
55
|
+
cryptoCache = null;
|
|
56
|
+
cryptoCacheTime = 0;
|
|
57
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export { seedFromTime, seedFromEntropy } from './seed.js';
|
|
2
|
+
export { normal, exponential, poisson, uniform } from './distributions.js';
|
|
3
|
+
export { beta, gamma, chi2 } from './distributions-extra.js';
|
|
4
|
+
export { weibull, lognormal, rayleigh, cauchy } from './distributions-special.js';
|
|
5
|
+
export { shuffle, pick, sample } from './sequence.js';
|
|
6
|
+
export { fromPerformance, fromMemory, fromCrypto, combined, clearCryptoCache } from './entropy.js';
|
|
7
|
+
export { rotateBits, extractBits, hammingWeight, bitRange } from './bits.js';
|
|
8
|
+
export { brownianMotion, ornsteinUhlenbeck, geometricBrownian } from './stochastic.js';
|
|
9
|
+
export { chiSquareTest, entropy, autocorrelation, runTest, kolmogorovSmirnovTest, meanTest, varianceTest } from './statistics.js';
|
|
10
|
+
export { perlin2D, valueNoise } from './noise.js';
|
|
11
|
+
export { weightedPick, weightedSample, reservoirSample } from './sampling.js';
|
|
12
|
+
export { saveState, restoreState, cloneGenerator } from './state.js';
|
|
13
|
+
export { SeedSequence, seedMultiple } from './seeding.js';
|
|
14
|
+
export { combinations, permutations, kPermutations, randomCombination, randomPermutation } from './combinatorics.js';
|
|
15
|
+
export { categorical, multinomial, categorical2D } from './categorical.js';
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|