@cloudglides/nox 1.1.5 → 2.0.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/README.md +9 -9
- package/example/src/App.css +89 -84
- package/example/src/App.jsx +375 -151
- package/package.json +7 -6
- package/src/core.browser.js +10 -2
- package/src/core.js +32 -4
- package/src/generators/logistic.js +30 -25
- package/src/generators/mixer.js +7 -7
- package/src/generators/mt19937.js +10 -7
- package/src/generators/pcg64.js +23 -12
- package/src/generators/splitmix64.js +12 -6
- package/src/generators/tent.js +12 -7
- package/src/generators/xorshift64.js +6 -3
- package/src/index.d.ts +68 -4
- package/src/index.js +154 -2
- package/src/rng.browser.js +21 -10
- package/src/rng.js +95 -82
- package/src/utils/arrays.js +149 -0
- package/src/utils/bits.js +146 -21
- package/src/utils/categorical.js +68 -31
- package/src/utils/combinatorics.js +113 -69
- package/src/utils/confidence.js +145 -0
- package/src/utils/decomposition.js +204 -0
- package/src/utils/distributions-advanced.js +122 -0
- package/src/utils/distributions-extra.js +102 -11
- package/src/utils/distributions-special.js +77 -20
- package/src/utils/distributions.js +99 -35
- package/src/utils/effects.js +172 -0
- package/src/utils/entropy.browser.js +29 -26
- package/src/utils/entropy.js +18 -8
- package/src/utils/helpers.js +64 -0
- package/src/utils/hypothesis.js +167 -0
- package/src/utils/integration.js +137 -0
- package/src/utils/interpolation.js +221 -0
- package/src/utils/matrix.js +242 -0
- package/src/utils/noise.js +36 -22
- package/src/utils/odesolvers.js +176 -0
- package/src/utils/optimization.js +215 -0
- package/src/utils/precomputed.js +166 -0
- package/src/utils/probability.js +199 -0
- package/src/utils/regression.js +170 -0
- package/src/utils/resampling.js +112 -0
- package/src/utils/rootfinding.js +158 -0
- package/src/utils/sampling.js +86 -77
- package/src/utils/seed.js +10 -4
- package/src/utils/seeding.js +24 -12
- package/src/utils/sequence.js +116 -32
- package/src/utils/state.js +48 -36
- package/src/utils/statistics.js +64 -2
- package/src/utils/stochastic.js +91 -31
- package/src/utils/stratified.js +108 -0
- package/src/utils/timeseries.js +166 -0
- package/src/utils/transforms.js +146 -0
- package/test/comprehensive-new.js +126 -0
- package/test/comprehensive.js +4 -3
- package/test/error-handling.js +49 -0
- package/test/new-features.js +52 -0
- package/IMPROVEMENTS.md +0 -58
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
export function bisection(f, a, b, tol = 1e-6, maxIter = 100) {
|
|
2
|
+
if (typeof f !== 'function') {
|
|
3
|
+
throw new TypeError('f must be function');
|
|
4
|
+
}
|
|
5
|
+
if (typeof a !== 'number' || typeof b !== 'number') {
|
|
6
|
+
throw new TypeError('a and b must be numbers');
|
|
7
|
+
}
|
|
8
|
+
if (a >= b) {
|
|
9
|
+
throw new RangeError('a must be less than b');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const fa = f(a);
|
|
13
|
+
const fb = f(b);
|
|
14
|
+
|
|
15
|
+
if (fa * fb > 0) {
|
|
16
|
+
throw new RangeError('f(a) and f(b) must have opposite signs');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let c, fc;
|
|
20
|
+
for (let i = 0; i < maxIter; i++) {
|
|
21
|
+
c = (a + b) / 2;
|
|
22
|
+
fc = f(c);
|
|
23
|
+
|
|
24
|
+
if (Math.abs(fc) < tol || Math.abs(b - a) / 2 < tol) {
|
|
25
|
+
return { root: c, iterations: i + 1, residual: Math.abs(fc) };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (fa * fc < 0) {
|
|
29
|
+
b = c;
|
|
30
|
+
fb = fc;
|
|
31
|
+
} else {
|
|
32
|
+
a = c;
|
|
33
|
+
fa = fc;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return { root: c, iterations: maxIter, residual: Math.abs(fc) };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function newtonRaphson(f, df, x0, tol = 1e-6, maxIter = 100) {
|
|
41
|
+
if (typeof f !== 'function' || typeof df !== 'function') {
|
|
42
|
+
throw new TypeError('f and df must be functions');
|
|
43
|
+
}
|
|
44
|
+
if (typeof x0 !== 'number') {
|
|
45
|
+
throw new TypeError('x0 must be number');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let x = x0;
|
|
49
|
+
for (let i = 0; i < maxIter; i++) {
|
|
50
|
+
const fx = f(x);
|
|
51
|
+
const dfx = df(x);
|
|
52
|
+
|
|
53
|
+
if (Math.abs(dfx) < 1e-14) {
|
|
54
|
+
throw new RangeError('derivative too close to zero');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const xNew = x - fx / dfx;
|
|
58
|
+
|
|
59
|
+
if (Math.abs(xNew - x) < tol) {
|
|
60
|
+
return { root: xNew, iterations: i + 1, residual: Math.abs(f(xNew)) };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
x = xNew;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { root: x, iterations: maxIter, residual: Math.abs(f(x)) };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function secant(f, x0, x1, tol = 1e-6, maxIter = 100) {
|
|
70
|
+
if (typeof f !== 'function') {
|
|
71
|
+
throw new TypeError('f must be function');
|
|
72
|
+
}
|
|
73
|
+
if (typeof x0 !== 'number' || typeof x1 !== 'number') {
|
|
74
|
+
throw new TypeError('x0 and x1 must be numbers');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let xPrev = x0;
|
|
78
|
+
let xCurr = x1;
|
|
79
|
+
let fPrev = f(xPrev);
|
|
80
|
+
let fCurr = f(xCurr);
|
|
81
|
+
|
|
82
|
+
for (let i = 0; i < maxIter; i++) {
|
|
83
|
+
if (Math.abs(fCurr - fPrev) < 1e-14) {
|
|
84
|
+
throw new RangeError('denominator too close to zero');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const xNext = xCurr - fCurr * (xCurr - xPrev) / (fCurr - fPrev);
|
|
88
|
+
|
|
89
|
+
if (Math.abs(xNext - xCurr) < tol) {
|
|
90
|
+
return { root: xNext, iterations: i + 1, residual: Math.abs(f(xNext)) };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
xPrev = xCurr;
|
|
94
|
+
xCurr = xNext;
|
|
95
|
+
fPrev = fCurr;
|
|
96
|
+
fCurr = f(xCurr);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return { root: xCurr, iterations: maxIter, residual: Math.abs(fCurr) };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function falsePosition(f, a, b, tol = 1e-6, maxIter = 100) {
|
|
103
|
+
if (typeof f !== 'function') {
|
|
104
|
+
throw new TypeError('f must be function');
|
|
105
|
+
}
|
|
106
|
+
if (typeof a !== 'number' || typeof b !== 'number') {
|
|
107
|
+
throw new TypeError('a and b must be numbers');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let fa = f(a);
|
|
111
|
+
let fb = f(b);
|
|
112
|
+
|
|
113
|
+
if (fa * fb > 0) {
|
|
114
|
+
throw new RangeError('f(a) and f(b) must have opposite signs');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
for (let i = 0; i < maxIter; i++) {
|
|
118
|
+
const c = a - (fa * (b - a)) / (fb - fa);
|
|
119
|
+
const fc = f(c);
|
|
120
|
+
|
|
121
|
+
if (Math.abs(fc) < tol) {
|
|
122
|
+
return { root: c, iterations: i + 1, residual: Math.abs(fc) };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (fa * fc < 0) {
|
|
126
|
+
b = c;
|
|
127
|
+
fb = fc;
|
|
128
|
+
} else {
|
|
129
|
+
a = c;
|
|
130
|
+
fa = fc;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const c = a - (fa * (b - a)) / (fb - fa);
|
|
135
|
+
return { root: c, iterations: maxIter, residual: Math.abs(f(c)) };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function fixedPoint(g, x0, tol = 1e-6, maxIter = 100) {
|
|
139
|
+
if (typeof g !== 'function') {
|
|
140
|
+
throw new TypeError('g must be function');
|
|
141
|
+
}
|
|
142
|
+
if (typeof x0 !== 'number') {
|
|
143
|
+
throw new TypeError('x0 must be number');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let x = x0;
|
|
147
|
+
for (let i = 0; i < maxIter; i++) {
|
|
148
|
+
const xNew = g(x);
|
|
149
|
+
|
|
150
|
+
if (Math.abs(xNew - x) < tol) {
|
|
151
|
+
return { root: xNew, iterations: i + 1, residual: Math.abs(xNew - x) };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
x = xNew;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return { root: x, iterations: maxIter, residual: Math.abs(g(x) - x) };
|
|
158
|
+
}
|
package/src/utils/sampling.js
CHANGED
|
@@ -1,88 +1,97 @@
|
|
|
1
1
|
export const weightedPick = (arr, weights, rng) => {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
2
|
+
if (!Array.isArray(arr) || arr.length === 0) {
|
|
3
|
+
throw new RangeError('arr must be a non-empty array');
|
|
4
|
+
}
|
|
5
|
+
if (!Array.isArray(weights) || weights.length === 0) {
|
|
6
|
+
throw new RangeError('weights must be a non-empty array');
|
|
7
|
+
}
|
|
8
|
+
if (arr.length !== weights.length) {
|
|
9
|
+
throw new RangeError('arr and weights must have same length');
|
|
10
|
+
}
|
|
11
|
+
if (!rng || typeof rng.nextFloat !== 'function') {
|
|
12
|
+
throw new TypeError('rng must be an RNG instance');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let total = 0;
|
|
16
|
+
for (let i = 0; i < weights.length; i++) {
|
|
17
|
+
if (typeof weights[i] !== 'number') {
|
|
18
|
+
throw new TypeError('All weights must be numbers');
|
|
19
|
+
}
|
|
20
|
+
total += weights[i];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (total <= 0) {
|
|
24
|
+
throw new RangeError('Weights must sum to positive value');
|
|
18
25
|
}
|
|
19
|
-
return a + b;
|
|
20
|
-
}, 0);
|
|
21
|
-
|
|
22
|
-
if (total <= 0) {
|
|
23
|
-
throw new Error('Weights must sum to positive value');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
let rand = rng.nextFloat() * total;
|
|
27
|
-
|
|
28
|
-
for (let i = 0; i < arr.length; i++) {
|
|
29
|
-
rand -= weights[i];
|
|
30
|
-
if (rand <= 0) return arr[i];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return arr[arr.length - 1];
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export const weightedSample = (arr, weights, count, rng) => {
|
|
37
|
-
if (!Array.isArray(arr) || arr.length === 0) {
|
|
38
|
-
throw new TypeError('arr must be a non-empty array');
|
|
39
|
-
}
|
|
40
|
-
if (!Array.isArray(weights) || weights.length === 0) {
|
|
41
|
-
throw new TypeError('weights must be a non-empty array');
|
|
42
|
-
}
|
|
43
|
-
if (arr.length !== weights.length) {
|
|
44
|
-
throw new Error('arr and weights must have same length');
|
|
45
|
-
}
|
|
46
|
-
if (typeof count !== 'number' || !Number.isInteger(count)) {
|
|
47
|
-
throw new TypeError('count must be an integer');
|
|
48
|
-
}
|
|
49
|
-
if (count <= 0) {
|
|
50
|
-
return [];
|
|
51
|
-
}
|
|
52
|
-
if (!rng || typeof rng.nextFloat !== 'function') {
|
|
53
|
-
throw new TypeError('rng must be an RNG instance');
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const result = [];
|
|
57
|
-
const len = arr.length;
|
|
58
|
-
if (count >= len) {
|
|
59
|
-
return [...arr];
|
|
60
|
-
}
|
|
61
26
|
|
|
62
|
-
|
|
63
|
-
|
|
27
|
+
let rand = rng.nextFloat() * total;
|
|
28
|
+
let cumsum = 0;
|
|
64
29
|
|
|
65
|
-
for (let i = 0; i <
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
remaining.splice(idx, 1);
|
|
69
|
-
remainingWeights.splice(idx, 1);
|
|
30
|
+
for (let i = 0; i < arr.length; i++) {
|
|
31
|
+
cumsum += weights[i];
|
|
32
|
+
if (rand < cumsum) return arr[i];
|
|
70
33
|
}
|
|
71
34
|
|
|
72
|
-
return
|
|
35
|
+
return arr[arr.length - 1];
|
|
73
36
|
};
|
|
74
37
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
38
|
+
export const weightedSample = (arr, weights, count, rng) => {
|
|
39
|
+
if (!Array.isArray(arr) || arr.length === 0) {
|
|
40
|
+
throw new RangeError('arr must be a non-empty array');
|
|
41
|
+
}
|
|
42
|
+
if (!Array.isArray(weights) || weights.length === 0) {
|
|
43
|
+
throw new RangeError('weights must be a non-empty array');
|
|
44
|
+
}
|
|
45
|
+
if (arr.length !== weights.length) {
|
|
46
|
+
throw new RangeError('arr and weights must have same length');
|
|
47
|
+
}
|
|
48
|
+
if (typeof count !== 'number' || !Number.isInteger(count)) {
|
|
49
|
+
throw new TypeError('count must be an integer');
|
|
50
|
+
}
|
|
51
|
+
if (count <= 0) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
if (!rng || typeof rng.nextFloat !== 'function') {
|
|
55
|
+
throw new TypeError('rng must be an RNG instance');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const len = arr.length;
|
|
59
|
+
if (count >= len) {
|
|
60
|
+
return [...arr];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const remaining = [...arr];
|
|
64
|
+
const remainingWeights = [...weights];
|
|
65
|
+
const result = [];
|
|
66
|
+
|
|
67
|
+
for (let i = 0; i < count; i++) {
|
|
68
|
+
const idx = weightedPickIndexRange(remainingWeights, i, rng);
|
|
69
|
+
result.push(remaining[i + idx]);
|
|
70
|
+
|
|
71
|
+
const temp = remaining[i];
|
|
72
|
+
remaining[i] = remaining[i + idx];
|
|
73
|
+
remaining[i + idx] = temp;
|
|
74
|
+
|
|
75
|
+
const tempW = remainingWeights[i];
|
|
76
|
+
remainingWeights[i] = remainingWeights[i + idx];
|
|
77
|
+
remainingWeights[i + idx] = tempW;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return result;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const weightedPickIndexRange = (weights, start, rng) => {
|
|
84
|
+
let total = 0;
|
|
85
|
+
for (let i = start; i < weights.length; i++) {
|
|
86
|
+
total += weights[i];
|
|
87
|
+
}
|
|
88
|
+
let rand = rng.nextFloat() * total;
|
|
89
|
+
for (let i = start; i < weights.length; i++) {
|
|
90
|
+
rand -= weights[i];
|
|
91
|
+
if (rand <= 0) return i - start;
|
|
92
|
+
}
|
|
93
|
+
return weights.length - start - 1;
|
|
94
|
+
};
|
|
86
95
|
|
|
87
96
|
export const reservoirSample = (stream, k, rng) => {
|
|
88
97
|
if (!Array.isArray(stream) || stream.length === 0) {
|
package/src/utils/seed.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
export const seedFromTime = () => {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
const time = BigInt(Date.now());
|
|
3
|
+
const rand = BigInt(Math.floor(Math.random() * 1000000));
|
|
4
|
+
return (time << 20n) | rand;
|
|
5
|
+
};
|
|
4
6
|
|
|
5
7
|
export const seedFromEntropy = (entropy) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
if (typeof entropy !== 'string') {
|
|
9
|
+
throw new TypeError('entropy must be a string');
|
|
10
|
+
}
|
|
11
|
+
if (entropy.length === 0) {
|
|
12
|
+
throw new RangeError('entropy cannot be empty');
|
|
13
|
+
}
|
|
8
14
|
|
|
9
15
|
let hash = 5381n;
|
|
10
16
|
for (let i = 0; i < entropy.length; i++) {
|
package/src/utils/seeding.js
CHANGED
|
@@ -22,16 +22,28 @@ export class SeedSequence {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
spawn(n = 1) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
if (typeof n !== 'number' || !Number.isInteger(n)) {
|
|
26
|
+
throw new TypeError('n must be an integer');
|
|
27
|
+
}
|
|
28
|
+
if (n <= 0) {
|
|
29
|
+
throw new RangeError('n must be positive');
|
|
30
|
+
}
|
|
31
|
+
const seeds = [];
|
|
32
|
+
for (let i = 0; i < n; i++) {
|
|
33
|
+
seeds.push(this.next());
|
|
34
|
+
}
|
|
35
|
+
return seeds;
|
|
36
|
+
}
|
|
30
37
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
|
|
39
|
+
export const seedMultiple = (rngClasses, entropy = null) => {
|
|
40
|
+
if (!Array.isArray(rngClasses)) {
|
|
41
|
+
throw new TypeError('rngClasses must be an array');
|
|
42
|
+
}
|
|
43
|
+
if (rngClasses.length === 0) {
|
|
44
|
+
throw new RangeError('rngClasses cannot be empty');
|
|
45
|
+
}
|
|
46
|
+
const seq = new SeedSequence(entropy);
|
|
47
|
+
const seeds = seq.spawn(rngClasses.length);
|
|
48
|
+
return rngClasses.map((RngClass, i) => new RngClass(seeds[i]));
|
|
49
|
+
};
|
package/src/utils/sequence.js
CHANGED
|
@@ -3,7 +3,7 @@ export const shuffle = (arr, rng, inPlace = false) => {
|
|
|
3
3
|
throw new TypeError('First argument must be array');
|
|
4
4
|
}
|
|
5
5
|
if (!rng || typeof rng.nextInt !== 'function') {
|
|
6
|
-
throw new TypeError('RNG instance
|
|
6
|
+
throw new TypeError('Second argument must be RNG instance');
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const target = inPlace ? arr : [...arr];
|
|
@@ -20,43 +20,127 @@ export const shuffle = (arr, rng, inPlace = false) => {
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
export const pick = (arr, rng) => {
|
|
23
|
+
if (!Array.isArray(arr)) {
|
|
24
|
+
throw new TypeError('First argument must be array');
|
|
25
|
+
}
|
|
26
|
+
if (arr.length === 0) {
|
|
27
|
+
throw new RangeError('Array cannot be empty');
|
|
28
|
+
}
|
|
29
|
+
if (!rng || typeof rng.nextInt !== 'function') {
|
|
30
|
+
throw new TypeError('Second argument must be RNG instance');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return arr[rng.nextInt(arr.length)];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const sample = (arr, count, rng) => {
|
|
37
|
+
if (!Array.isArray(arr)) {
|
|
38
|
+
throw new TypeError('First argument must be array');
|
|
39
|
+
}
|
|
40
|
+
if (typeof count !== 'number' || !Number.isInteger(count)) {
|
|
41
|
+
throw new TypeError('Sample count must be an integer');
|
|
42
|
+
}
|
|
43
|
+
if (count <= 0) {
|
|
44
|
+
throw new RangeError('Sample count must be positive');
|
|
45
|
+
}
|
|
46
|
+
if (count > arr.length) {
|
|
47
|
+
throw new RangeError('Sample count exceeds array length');
|
|
48
|
+
}
|
|
49
|
+
if (!rng || typeof rng.nextInt !== 'function') {
|
|
50
|
+
throw new TypeError('Second argument must be RNG instance');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const copy = [...arr];
|
|
54
|
+
const len = copy.length;
|
|
55
|
+
for (let i = len - 1; i > len - count - 1; i--) {
|
|
56
|
+
const j = rng.nextInt(i + 1);
|
|
57
|
+
const temp = copy[i];
|
|
58
|
+
copy[i] = copy[j];
|
|
59
|
+
copy[j] = temp;
|
|
60
|
+
}
|
|
61
|
+
return copy.slice(len - count);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const sampleWithReplacement = (arr, count, rng) => {
|
|
23
65
|
if (!Array.isArray(arr)) {
|
|
24
66
|
throw new TypeError('First argument must be array');
|
|
25
67
|
}
|
|
26
68
|
if (arr.length === 0) {
|
|
27
|
-
throw new
|
|
69
|
+
throw new RangeError('Array cannot be empty');
|
|
70
|
+
}
|
|
71
|
+
if (typeof count !== 'number' || !Number.isInteger(count)) {
|
|
72
|
+
throw new TypeError('Sample count must be an integer');
|
|
73
|
+
}
|
|
74
|
+
if (count <= 0) {
|
|
75
|
+
throw new RangeError('Sample count must be positive');
|
|
28
76
|
}
|
|
29
77
|
if (!rng || typeof rng.nextInt !== 'function') {
|
|
30
|
-
throw new TypeError('RNG instance
|
|
78
|
+
throw new TypeError('rng must be an RNG instance');
|
|
31
79
|
}
|
|
32
|
-
|
|
33
|
-
|
|
80
|
+
|
|
81
|
+
const result = new Array(count);
|
|
82
|
+
const len = arr.length;
|
|
83
|
+
for (let i = 0; i < count; i++) {
|
|
84
|
+
result[i] = arr[rng.nextInt(len)];
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
34
87
|
};
|
|
35
88
|
|
|
36
|
-
export const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
89
|
+
export const permute = (arr, rng) => {
|
|
90
|
+
if (!Array.isArray(arr)) {
|
|
91
|
+
throw new TypeError('First argument must be array');
|
|
92
|
+
}
|
|
93
|
+
if (!rng || typeof rng.nextInt !== 'function') {
|
|
94
|
+
throw new TypeError('Second argument must be RNG instance');
|
|
95
|
+
}
|
|
96
|
+
return shuffle(arr, rng, false);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const range = (start, end, step = 1) => {
|
|
100
|
+
if (typeof start !== 'number' || !Number.isInteger(start)) {
|
|
101
|
+
throw new TypeError('start must be an integer');
|
|
102
|
+
}
|
|
103
|
+
if (typeof end !== 'number' || !Number.isInteger(end)) {
|
|
104
|
+
throw new TypeError('end must be an integer');
|
|
105
|
+
}
|
|
106
|
+
if (typeof step !== 'number' || !Number.isInteger(step)) {
|
|
107
|
+
throw new TypeError('step must be an integer');
|
|
108
|
+
}
|
|
109
|
+
if (step === 0) {
|
|
110
|
+
throw new RangeError('step cannot be zero');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const result = [];
|
|
114
|
+
if (step > 0) {
|
|
115
|
+
for (let i = start; i < end; i += step) {
|
|
116
|
+
result.push(i);
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
for (let i = start; i > end; i += step) {
|
|
120
|
+
result.push(i);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const cycle = (arr, count) => {
|
|
127
|
+
if (!Array.isArray(arr)) {
|
|
128
|
+
throw new TypeError('First argument must be array');
|
|
129
|
+
}
|
|
130
|
+
if (arr.length === 0) {
|
|
131
|
+
throw new RangeError('Array cannot be empty');
|
|
132
|
+
}
|
|
133
|
+
if (typeof count !== 'number' || !Number.isInteger(count)) {
|
|
134
|
+
throw new TypeError('count must be an integer');
|
|
135
|
+
}
|
|
136
|
+
if (count <= 0) {
|
|
137
|
+
throw new RangeError('count must be positive');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const result = new Array(count);
|
|
141
|
+
const len = arr.length;
|
|
142
|
+
for (let i = 0; i < count; i++) {
|
|
143
|
+
result[i] = arr[i % len];
|
|
144
|
+
}
|
|
145
|
+
return result;
|
|
146
|
+
};
|
package/src/utils/state.js
CHANGED
|
@@ -1,39 +1,51 @@
|
|
|
1
1
|
export const saveState = (rng) => {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
2
|
+
const gen = rng.gen || rng;
|
|
3
|
+
if (gen.state === undefined) {
|
|
4
|
+
throw new Error('Generator does not support state snapshots');
|
|
5
|
+
}
|
|
6
|
+
const state = gen.state;
|
|
7
|
+
return {
|
|
8
|
+
state: typeof state === 'bigint' ? state.toString() : state,
|
|
9
|
+
isBigInt: typeof state === 'bigint'
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const restoreState = (rng, snapshot) => {
|
|
14
|
+
const gen = rng.gen || rng;
|
|
15
|
+
if (gen.state === undefined) {
|
|
16
|
+
throw new Error('Generator does not support state snapshots');
|
|
17
|
+
}
|
|
18
|
+
gen.state = snapshot.isBigInt ? BigInt(snapshot.state) : snapshot.state;
|
|
19
|
+
};
|
|
16
20
|
|
|
17
21
|
export const cloneGenerator = (rng) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
22
|
+
const isRNGWrapper = rng.gen !== undefined;
|
|
23
|
+
const gen = isRNGWrapper ? rng.gen : rng;
|
|
24
|
+
const Constructor = gen.constructor;
|
|
25
|
+
|
|
26
|
+
let clonedGen = Object.create(Constructor.prototype);
|
|
27
|
+
|
|
28
|
+
if (Constructor.name === 'PCG64') {
|
|
29
|
+
clonedGen.state = gen.state;
|
|
30
|
+
clonedGen.inc = gen.inc;
|
|
31
|
+
} else if (Constructor.name === 'Logistic') {
|
|
32
|
+
clonedGen.x = gen.x;
|
|
33
|
+
clonedGen.r = gen.r;
|
|
34
|
+
} else if (Constructor.name === 'Tent') {
|
|
35
|
+
clonedGen.x = gen.x;
|
|
36
|
+
clonedGen.mu = gen.mu;
|
|
37
|
+
} else if (Constructor.name === 'Xorshift64' || Constructor.name === 'Splitmix64' || Constructor.name === 'MT19937') {
|
|
38
|
+
clonedGen.state = gen.state;
|
|
39
|
+
if (Constructor.name === 'MT19937') {
|
|
40
|
+
clonedGen.mt = [...gen.mt];
|
|
41
|
+
clonedGen.mti = gen.mti;
|
|
42
|
+
}
|
|
43
|
+
} else if (Constructor.name === 'Mixer') {
|
|
44
|
+
clonedGen.rng1 = cloneGenerator(gen.rng1);
|
|
45
|
+
clonedGen.rng2 = cloneGenerator(gen.rng2);
|
|
46
|
+
} else {
|
|
47
|
+
clonedGen.state = gen.state;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return isRNGWrapper ? new (rng.constructor)(clonedGen) : clonedGen;
|
|
51
|
+
};
|