@cloudglides/nox 1.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.
Files changed (49) hide show
  1. package/.github/workflows/build-on-tag.yml +67 -0
  2. package/IMPROVEMENTS.md +58 -0
  3. package/LICENSE +15 -0
  4. package/README.md +119 -0
  5. package/docs/API.md +70 -0
  6. package/example/README.md +26 -0
  7. package/example/index.html +13 -0
  8. package/example/package-lock.json +1636 -0
  9. package/example/package.json +20 -0
  10. package/example/src/App.css +161 -0
  11. package/example/src/App.jsx +176 -0
  12. package/example/src/index.css +55 -0
  13. package/example/src/main.jsx +10 -0
  14. package/example/vite.config.js +11 -0
  15. package/package.json +25 -0
  16. package/src/core.js +8 -0
  17. package/src/generators/index.js +7 -0
  18. package/src/generators/logistic.js +19 -0
  19. package/src/generators/mixer.js +20 -0
  20. package/src/generators/mt19937.js +74 -0
  21. package/src/generators/pcg64.js +44 -0
  22. package/src/generators/splitmix64.js +32 -0
  23. package/src/generators/tent.js +24 -0
  24. package/src/generators/xorshift64.js +38 -0
  25. package/src/index.d.ts +114 -0
  26. package/src/index.js +32 -0
  27. package/src/presets.js +15 -0
  28. package/src/rng.js +142 -0
  29. package/src/utils/bits.js +25 -0
  30. package/src/utils/categorical.js +57 -0
  31. package/src/utils/combinatorics.js +85 -0
  32. package/src/utils/distributions-extra.js +32 -0
  33. package/src/utils/distributions-special.js +21 -0
  34. package/src/utils/distributions.js +69 -0
  35. package/src/utils/entropy.js +95 -0
  36. package/src/utils/index.js +30 -0
  37. package/src/utils/noise.js +81 -0
  38. package/src/utils/sampling.js +106 -0
  39. package/src/utils/seed.js +14 -0
  40. package/src/utils/seeding.js +37 -0
  41. package/src/utils/sequence.js +53 -0
  42. package/src/utils/state.js +39 -0
  43. package/src/utils/statistics.js +127 -0
  44. package/src/utils/stochastic.js +32 -0
  45. package/test/advanced.js +71 -0
  46. package/test/basic.js +13 -0
  47. package/test/benchmark.js +35 -0
  48. package/test/comprehensive.js +100 -0
  49. package/test/profile.js +73 -0
@@ -0,0 +1,95 @@
1
+ let cryptoCache = null;
2
+ let cryptoCacheTime = 0;
3
+
4
+ const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
5
+ const isBrowser = typeof window !== 'undefined';
6
+
7
+ let performanceImpl = typeof performance !== 'undefined' ? performance : null;
8
+ let randomBytesImpl = null;
9
+
10
+ if (isNode && typeof require !== 'undefined') {
11
+ try {
12
+ const perf_hooks = require('perf_hooks');
13
+ performanceImpl = perf_hooks.performance;
14
+ } catch {}
15
+
16
+ try {
17
+ const crypto_mod = require('crypto');
18
+ randomBytesImpl = crypto_mod.randomBytes;
19
+ } catch {}
20
+ }
21
+
22
+ export const fromPerformance = () => {
23
+ try {
24
+ if (performanceImpl && performanceImpl.now) {
25
+ const t = performanceImpl.now();
26
+ return BigInt(Math.floor(t * 1000)) ^ BigInt(Math.floor(t * 1000000) % 1000000);
27
+ }
28
+ } catch {
29
+ // fallback
30
+ }
31
+ return BigInt(Date.now());
32
+ };
33
+
34
+ export const fromMemory = () => {
35
+ try {
36
+ if (isNode && typeof process !== 'undefined' && process.memoryUsage) {
37
+ const mem = process.memoryUsage();
38
+ const total = mem.heapUsed + mem.external + mem.heapTotal;
39
+ return BigInt(Math.floor(total)) ^ BigInt(Date.now());
40
+ }
41
+ } catch {
42
+ // fallback
43
+ }
44
+ return BigInt(Date.now());
45
+ };
46
+
47
+ export const fromCrypto = (bytes = 8) => {
48
+ try {
49
+ const now = Date.now();
50
+
51
+ if (cryptoCache && (now - cryptoCacheTime) < 100) {
52
+ return cryptoCache;
53
+ }
54
+
55
+ let val = 0n;
56
+
57
+ if (randomBytesImpl) {
58
+ const buf = randomBytesImpl(Math.max(bytes, 8));
59
+ for (let i = 0; i < buf.length; i++) {
60
+ val = (val << 8n) | BigInt(buf[i]);
61
+ }
62
+ } else if (isBrowser && typeof crypto !== 'undefined' && crypto.getRandomValues) {
63
+ const arr = new Uint8Array(Math.max(bytes, 8));
64
+ crypto.getRandomValues(arr);
65
+ for (let i = 0; i < arr.length; i++) {
66
+ val = (val << 8n) | BigInt(arr[i]);
67
+ }
68
+ } else {
69
+ return BigInt(Math.random() * Number.MAX_SAFE_INTEGER);
70
+ }
71
+
72
+ cryptoCache = val;
73
+ cryptoCacheTime = now;
74
+ return val;
75
+ } catch {
76
+ return BigInt(Math.random() * Number.MAX_SAFE_INTEGER);
77
+ }
78
+ };
79
+
80
+ export const combined = () => {
81
+ const perf = fromPerformance();
82
+ const mem = fromMemory();
83
+ const crypto = fromCrypto();
84
+
85
+ let mix = perf ^ mem ^ crypto;
86
+ mix = mix ^ (mix >> 33n);
87
+ mix = (mix * 0xff51afd7ed558ccdn) & ((1n << 64n) - 1n);
88
+
89
+ return mix !== 0n ? mix : 1n;
90
+ };
91
+
92
+ export const clearCryptoCache = () => {
93
+ cryptoCache = null;
94
+ cryptoCacheTime = 0;
95
+ };
@@ -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
+
@@ -0,0 +1,81 @@
1
+ const permutation = [151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,
2
+ 140,36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,
3
+ 247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,
4
+ 57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,
5
+ 74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,
6
+ 60,211,133,230,206,39,323,234,35,158,234,144,12,234,165,
7
+ 35,38,112,106,43,23,147,126,65,189,235,64,211,205,230,108,
8
+ 248,191,227,21,205,45,94,2,32,284,239,210,246,24,97,78,
9
+ 101,40,71,224,255,326,167,21,213,2,223,166,205,57,130,180,
10
+ 51,108,203,190,57,74,76,88,207,208,239,170,251,67,77,51,
11
+ 133,69,249,2,127,80,60,159,168,81,163,64,143,146,157,41,
12
+ 244,8,51,153,78,130,83,142,46,168,134,55,63,160,166,56,
13
+ 172,85,24,254,192,221,100,495,32,163,244,236,37,46,28,141,
14
+ 149,34,69,193,70,199,154,159,147,12,130,199,10,154,27,112];
15
+
16
+ const fade = (t) => t * t * t * (t * (t * 6 - 15) + 10);
17
+ const lerp = (t, a, b) => a + t * (b - a);
18
+ const grad = (hash, x, y) => {
19
+ const h = hash & 15;
20
+ const u = h < 8 ? x : y;
21
+ const v = h < 8 ? y : x;
22
+ return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
23
+ };
24
+
25
+ export const perlin2D = (rng, x, y, octaves = 1) => {
26
+ let value = 0;
27
+ let amplitude = 1;
28
+ let frequency = 1;
29
+ let maxValue = 0;
30
+
31
+ for (let i = 0; i < octaves; i++) {
32
+ const xi = Math.floor((x * frequency) % 256);
33
+ const yi = Math.floor((y * frequency) % 256);
34
+
35
+ const xf = (x * frequency) - Math.floor(x * frequency);
36
+ const yf = (y * frequency) - Math.floor(y * frequency);
37
+
38
+ const u = fade(xf);
39
+ const v = fade(yf);
40
+
41
+ const p0 = permutation[xi];
42
+ const p1 = permutation[(xi + 1) & 255];
43
+
44
+ const g00 = grad(permutation[(p0 + yi) & 255], xf, yf);
45
+ const g10 = grad(permutation[(p1 + yi) & 255], xf - 1, yf);
46
+ const g01 = grad(permutation[(p0 + yi + 1) & 255], xf, yf - 1);
47
+ const g11 = grad(permutation[(p1 + yi + 1) & 255], xf - 1, yf - 1);
48
+
49
+ const nx0 = lerp(u, g00, g10);
50
+ const nx1 = lerp(u, g01, g11);
51
+ const nxy = lerp(v, nx0, nx1);
52
+
53
+ value += nxy * amplitude;
54
+ maxValue += amplitude;
55
+ amplitude *= 0.5;
56
+ frequency *= 2;
57
+ }
58
+
59
+ return value / maxValue;
60
+ };
61
+
62
+ export const valueNoise = (rng, x, y, scale = 1) => {
63
+ const sx = Math.floor(x * scale);
64
+ const sy = Math.floor(y * scale);
65
+
66
+ const n00 = Math.sin(sx * 12.9898 + sy * 78.233) * 43758.5453;
67
+ const n10 = Math.sin((sx + 1) * 12.9898 + sy * 78.233) * 43758.5453;
68
+ const n01 = Math.sin(sx * 12.9898 + (sy + 1) * 78.233) * 43758.5453;
69
+ const n11 = Math.sin((sx + 1) * 12.9898 + (sy + 1) * 78.233) * 43758.5453;
70
+
71
+ const fx = x * scale - sx;
72
+ const fy = y * scale - sy;
73
+
74
+ const u = fade(fx);
75
+ const v = fade(fy);
76
+
77
+ const nx0 = lerp(u, n00 - Math.floor(n00), n10 - Math.floor(n10));
78
+ const nx1 = lerp(u, n01 - Math.floor(n01), n11 - Math.floor(n11));
79
+
80
+ return lerp(v, nx0, nx1);
81
+ };
@@ -0,0 +1,106 @@
1
+ export const weightedPick = (arr, weights, rng) => {
2
+ if (!Array.isArray(arr) || arr.length === 0) {
3
+ throw new TypeError('arr must be a non-empty array');
4
+ }
5
+ if (!Array.isArray(weights) || weights.length === 0) {
6
+ throw new TypeError('weights must be a non-empty array');
7
+ }
8
+ if (arr.length !== weights.length) {
9
+ throw new Error('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
+ const total = weights.reduce((a, b) => {
16
+ if (typeof a !== 'number' || typeof b !== 'number') {
17
+ throw new TypeError('All weights must be numbers');
18
+ }
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
+ throw new RangeError('count must be non-negative');
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 remaining = [...arr];
58
+ const remainingWeights = [...weights];
59
+
60
+ for (let i = 0; i < count && remaining.length > 0; i++) {
61
+ const idx = weightedPickIndex(remainingWeights, rng);
62
+ result.push(remaining[idx]);
63
+ remaining.splice(idx, 1);
64
+ remainingWeights.splice(idx, 1);
65
+ }
66
+
67
+ return result;
68
+ };
69
+
70
+ const weightedPickIndex = (weights, rng) => {
71
+ const total = weights.reduce((a, b) => a + b, 0);
72
+ let rand = rng.nextFloat() * total;
73
+
74
+ for (let i = 0; i < weights.length; i++) {
75
+ rand -= weights[i];
76
+ if (rand <= 0) return i;
77
+ }
78
+
79
+ return weights.length - 1;
80
+ };
81
+
82
+ export const reservoirSample = (stream, k, rng) => {
83
+ if (!Array.isArray(stream) || stream.length === 0) {
84
+ throw new TypeError('stream must be a non-empty array');
85
+ }
86
+ if (typeof k !== 'number' || !Number.isInteger(k)) {
87
+ throw new TypeError('k must be an integer');
88
+ }
89
+ if (k <= 0) {
90
+ throw new RangeError('k must be positive');
91
+ }
92
+ if (!rng || typeof rng.nextInt !== 'function') {
93
+ throw new TypeError('rng must be an RNG instance');
94
+ }
95
+
96
+ const reservoir = stream.slice(0, Math.min(k, stream.length));
97
+
98
+ for (let i = k; i < stream.length; i++) {
99
+ const j = rng.nextInt(i + 1);
100
+ if (j < k) {
101
+ reservoir[j] = stream[i];
102
+ }
103
+ }
104
+
105
+ return reservoir;
106
+ };
@@ -0,0 +1,14 @@
1
+ export const seedFromTime = () => {
2
+ return BigInt(Date.now() + Math.random() * 1000000);
3
+ };
4
+
5
+ export const seedFromEntropy = (entropy) => {
6
+ if (typeof entropy !== 'string') throw new TypeError('Entropy must be string');
7
+ if (entropy.length === 0) throw new Error('Entropy cannot be empty');
8
+
9
+ let hash = 5381n;
10
+ for (let i = 0; i < entropy.length; i++) {
11
+ hash = ((hash << 5n) + hash) ^ BigInt(entropy.charCodeAt(i));
12
+ }
13
+ return hash % (1n << 32n);
14
+ };
@@ -0,0 +1,37 @@
1
+ import { seedFromTime, seedFromEntropy } from './seed.js';
2
+ import { fromPerformance, fromMemory, fromCrypto, combined } from './entropy.js';
3
+
4
+ export class SeedSequence {
5
+ constructor(entropy = null) {
6
+ if (entropy === null) {
7
+ this.seed = combined();
8
+ } else if (typeof entropy === 'string') {
9
+ this.seed = seedFromEntropy(entropy);
10
+ } else if (typeof entropy === 'bigint' || typeof entropy === 'number') {
11
+ this.seed = BigInt(entropy);
12
+ } else {
13
+ this.seed = entropy;
14
+ }
15
+ this.counter = 0n;
16
+ }
17
+
18
+ next() {
19
+ const val = this.seed ^ BigInt(this.counter);
20
+ this.counter += 1n;
21
+ return val;
22
+ }
23
+
24
+ spawn(n = 1) {
25
+ const seeds = [];
26
+ for (let i = 0; i < n; i++) {
27
+ seeds.push(this.next());
28
+ }
29
+ return seeds;
30
+ }
31
+ }
32
+
33
+ export const seedMultiple = (rngClasses, entropy = null) => {
34
+ const seq = new SeedSequence(entropy);
35
+ const seeds = seq.spawn(rngClasses.length);
36
+ return rngClasses.map((RngClass, i) => new RngClass(seeds[i]));
37
+ };
@@ -0,0 +1,53 @@
1
+ export const shuffle = (arr, rng, inPlace = false) => {
2
+ if (!Array.isArray(arr)) {
3
+ throw new TypeError('First argument must be array');
4
+ }
5
+ if (!rng || typeof rng.nextInt !== 'function') {
6
+ throw new TypeError('RNG instance required');
7
+ }
8
+ if (typeof inPlace !== 'boolean') {
9
+ throw new TypeError('inPlace must be boolean');
10
+ }
11
+
12
+ const target = inPlace ? arr : [...arr];
13
+ for (let i = target.length - 1; i > 0; i--) {
14
+ const j = rng.nextInt(i + 1);
15
+ [target[i], target[j]] = [target[j], target[i]];
16
+ }
17
+ return target;
18
+ };
19
+
20
+ export const pick = (arr, rng) => {
21
+ if (!Array.isArray(arr)) {
22
+ throw new TypeError('First argument must be array');
23
+ }
24
+ if (arr.length === 0) {
25
+ throw new Error('Array cannot be empty');
26
+ }
27
+ if (!rng || typeof rng.nextInt !== 'function') {
28
+ throw new TypeError('RNG instance required');
29
+ }
30
+
31
+ return arr[rng.nextInt(arr.length)];
32
+ };
33
+
34
+ export const sample = (arr, count, rng) => {
35
+ if (!Array.isArray(arr)) {
36
+ throw new TypeError('First argument must be array');
37
+ }
38
+ if (typeof count !== 'number' || !Number.isInteger(count)) {
39
+ throw new TypeError('Sample count must be an integer');
40
+ }
41
+ if (count <= 0) {
42
+ throw new Error('Sample count must be positive');
43
+ }
44
+ if (count > arr.length) {
45
+ throw new Error('Sample count exceeds array length');
46
+ }
47
+ if (!rng || typeof rng.nextInt !== 'function') {
48
+ throw new TypeError('RNG instance required');
49
+ }
50
+
51
+ const copy = shuffle(arr, rng);
52
+ return copy.slice(0, count);
53
+ };
@@ -0,0 +1,39 @@
1
+ export const saveState = (rng) => {
2
+ const gen = rng.gen || rng;
3
+ if (gen.state === undefined) {
4
+ throw new Error('Generator does not support state snapshots');
5
+ }
6
+ return { state: gen.state };
7
+ };
8
+
9
+ export const restoreState = (rng, snapshot) => {
10
+ const gen = rng.gen || rng;
11
+ if (gen.state === undefined) {
12
+ throw new Error('Generator does not support state snapshots');
13
+ }
14
+ gen.state = snapshot.state;
15
+ };
16
+
17
+ export const cloneGenerator = (rng) => {
18
+ const isRNGWrapper = rng.gen !== undefined;
19
+ const gen = isRNGWrapper ? rng.gen : rng;
20
+ const Constructor = gen.constructor;
21
+
22
+ let clonedGen;
23
+
24
+ if (Constructor.name === 'PCG64') {
25
+ clonedGen = new Constructor(gen.state, gen.inc);
26
+ } else if (Constructor.name === 'Logistic') {
27
+ clonedGen = new Constructor(gen.x, gen.r);
28
+ } else if (Constructor.name === 'Tent') {
29
+ clonedGen = new Constructor(gen.x, gen.mu);
30
+ } else if (Constructor.name === 'Mixer') {
31
+ const rng1Clone = cloneGenerator(gen.rng1);
32
+ const rng2Clone = cloneGenerator(gen.rng2);
33
+ clonedGen = new Constructor(rng1Clone, rng2Clone);
34
+ } else {
35
+ clonedGen = new Constructor(gen.state);
36
+ }
37
+
38
+ return isRNGWrapper ? new (rng.constructor)(Constructor, gen.state) : clonedGen;
39
+ };
@@ -0,0 +1,127 @@
1
+ export const chiSquareTest = (data, bins = 10) => {
2
+ const histogram = new Array(bins).fill(0);
3
+ for (const val of data) {
4
+ const idx = Math.floor(val * bins);
5
+ histogram[Math.min(idx, bins - 1)]++;
6
+ }
7
+
8
+ const expected = data.length / bins;
9
+ let chi2 = 0;
10
+ for (const count of histogram) {
11
+ chi2 += ((count - expected) ** 2) / expected;
12
+ }
13
+
14
+ return { chi2, expected, histogram };
15
+ };
16
+
17
+ export const entropy = (data, bins = 10) => {
18
+ const histogram = new Array(bins).fill(0);
19
+ for (const val of data) {
20
+ const idx = Math.floor(val * bins);
21
+ histogram[Math.min(idx, bins - 1)]++;
22
+ }
23
+
24
+ let ent = 0;
25
+ const p = 1 / data.length;
26
+ for (const count of histogram) {
27
+ if (count > 0) {
28
+ const pi = count / data.length;
29
+ ent -= pi * Math.log2(pi);
30
+ }
31
+ }
32
+ return ent;
33
+ };
34
+
35
+ export const autocorrelation = (data, lag) => {
36
+ if (lag < 0 || lag >= data.length) throw new Error('Lag out of range');
37
+ if (data.length < 2) throw new Error('Need at least 2 data points');
38
+
39
+ const mean = data.reduce((a, b) => a + b, 0) / data.length;
40
+ let c0 = 0;
41
+ let cl = 0;
42
+
43
+ for (let i = 0; i < data.length; i++) {
44
+ c0 += (data[i] - mean) ** 2;
45
+ }
46
+
47
+ if (c0 === 0) return 0;
48
+
49
+ for (let i = 0; i < data.length - lag; i++) {
50
+ cl += (data[i] - mean) * (data[i + lag] - mean);
51
+ }
52
+
53
+ return cl / c0;
54
+ };
55
+
56
+ export const runTest = (data, threshold = 0.5) => {
57
+ const binary = data.map(v => v > threshold ? 1 : 0);
58
+ let runs = 1;
59
+ for (let i = 1; i < binary.length; i++) {
60
+ if (binary[i] !== binary[i - 1]) runs++;
61
+ }
62
+ return runs;
63
+ };
64
+
65
+ export const kolmogorovSmirnovTest = (data) => {
66
+ if (!Array.isArray(data) || data.length === 0) {
67
+ throw new Error('Data must be non-empty array');
68
+ }
69
+
70
+ const sorted = [...data].sort((a, b) => a - b);
71
+ let maxD = 0;
72
+
73
+ for (let i = 0; i < sorted.length; i++) {
74
+ const empirical = (i + 1) / sorted.length;
75
+ const theoretical = sorted[i];
76
+ maxD = Math.max(maxD, Math.abs(empirical - theoretical));
77
+ }
78
+
79
+ const criticalValues = {
80
+ 0.10: 1.224 / Math.sqrt(data.length),
81
+ 0.05: 1.358 / Math.sqrt(data.length),
82
+ 0.01: 1.627 / Math.sqrt(data.length)
83
+ };
84
+
85
+ return {
86
+ statistic: maxD,
87
+ pass_0_10: maxD < criticalValues[0.10],
88
+ pass_0_05: maxD < criticalValues[0.05],
89
+ pass_0_01: maxD < criticalValues[0.01]
90
+ };
91
+ };
92
+
93
+ export const meanTest = (data, expectedMean = 0.5) => {
94
+ if (!Array.isArray(data) || data.length === 0) {
95
+ throw new Error('Data must be non-empty array');
96
+ }
97
+
98
+ const mean = data.reduce((a, b) => a + b, 0) / data.length;
99
+ const variance = data.reduce((a, v) => a + (v - mean) ** 2, 0) / data.length;
100
+ const stdDev = Math.sqrt(variance);
101
+ const tStat = (mean - expectedMean) / (stdDev / Math.sqrt(data.length));
102
+
103
+ return {
104
+ mean,
105
+ variance,
106
+ stdDev,
107
+ tStatistic: tStat,
108
+ expectedMean
109
+ };
110
+ };
111
+
112
+ export const varianceTest = (data, expectedVariance = 1 / 12) => {
113
+ if (!Array.isArray(data) || data.length === 0) {
114
+ throw new Error('Data must be non-empty array');
115
+ }
116
+
117
+ const mean = data.reduce((a, b) => a + b, 0) / data.length;
118
+ const variance = data.reduce((a, v) => a + (v - mean) ** 2, 0) / data.length;
119
+ const chi2 = (data.length - 1) * variance / expectedVariance;
120
+
121
+ return {
122
+ variance,
123
+ expectedVariance,
124
+ chi2Statistic: chi2,
125
+ degreesOfFreedom: data.length - 1
126
+ };
127
+ };
@@ -0,0 +1,32 @@
1
+ export const brownianMotion = (rng, steps, dt = 1) => {
2
+ const path = [0];
3
+ for (let i = 0; i < steps; i++) {
4
+ const drift = -0.5 * dt;
5
+ const diffusion = Math.sqrt(dt) * (rng.nextFloat() * 2 - 1);
6
+ const nextVal = path[i] + drift + diffusion;
7
+ path.push(nextVal);
8
+ }
9
+ return path;
10
+ };
11
+
12
+ export const ornsteinUhlenbeck = (rng, steps, theta = 0.1, mu = 0, sigma = 1) => {
13
+ const path = [mu];
14
+ for (let i = 0; i < steps; i++) {
15
+ const drift = theta * (mu - path[i]);
16
+ const diffusion = sigma * (rng.nextFloat() * 2 - 1);
17
+ const nextVal = path[i] + drift + diffusion;
18
+ path.push(nextVal);
19
+ }
20
+ return path;
21
+ };
22
+
23
+ export const geometricBrownian = (rng, steps, mu = 0.05, sigma = 0.2, dt = 0.01) => {
24
+ const path = [1];
25
+ for (let i = 0; i < steps; i++) {
26
+ const drift = (mu - 0.5 * sigma * sigma) * dt;
27
+ const diffusion = sigma * Math.sqrt(dt) * (rng.nextFloat() * 2 - 1);
28
+ const nextVal = path[i] * Math.exp(drift + diffusion);
29
+ path.push(nextVal);
30
+ }
31
+ return path;
32
+ };
@@ -0,0 +1,71 @@
1
+ import { rng, deterministic } from '../src/index.js';
2
+ import {
3
+ weightedPick,
4
+ reservoirSample,
5
+ meanTest,
6
+ varianceTest,
7
+ kolmogorovSmirnovTest
8
+ } from '../src/index.js';
9
+
10
+ console.log('=== Batch Operations ===');
11
+ const r = rng();
12
+
13
+ const floats = r.floats(5);
14
+ console.log('Batch floats:', floats);
15
+
16
+ const ints = r.ints(5, 100);
17
+ console.log('Batch ints:', ints);
18
+
19
+ const bools = r.bools(5, 0.3);
20
+ console.log('Batch bools:', bools);
21
+
22
+ const custom = r.batch(3, (rng, i) => ({ id: i, val: rng.nextFloat() }));
23
+ console.log('Custom batch:', custom);
24
+
25
+ console.log('\n=== Utility Methods ===');
26
+ console.log('Range (10-20, step 2):', r.range(10, 20, 2));
27
+ console.log('Choice:', r.choice(['apple', 'banana', 'cherry']));
28
+
29
+ console.log('\n=== Weighted Sampling ===');
30
+ const items = ['A', 'B', 'C'];
31
+ const weights = [0.5, 0.3, 0.2];
32
+ console.log('Weighted pick:', weightedPick(items, weights, r));
33
+
34
+ console.log('\n=== Reservoir Sampling ===');
35
+ const stream = Array.from({ length: 100 }, (_, i) => i);
36
+ const sample = reservoirSample(stream, 5, r);
37
+ console.log('Reservoir sample:', sample);
38
+
39
+ console.log('\n=== Statistical Tests ===');
40
+ const testData = r.floats(1000);
41
+
42
+ const mean = meanTest(testData);
43
+ console.log('Mean test:', {
44
+ computed: mean.mean.toFixed(4),
45
+ expected: mean.expectedMean.toFixed(4),
46
+ tStat: mean.tStatistic.toFixed(4)
47
+ });
48
+
49
+ const variance = varianceTest(testData);
50
+ console.log('Variance test:', {
51
+ computed: variance.variance.toFixed(6),
52
+ expected: variance.expectedVariance.toFixed(6),
53
+ chi2: variance.chi2Statistic.toFixed(4)
54
+ });
55
+
56
+ const ks = kolmogorovSmirnovTest(testData);
57
+ console.log('KS test:', {
58
+ statistic: ks.statistic.toFixed(6),
59
+ pass_0_05: ks.pass_0_05
60
+ });
61
+
62
+ console.log('\n=== Deterministic Reproducibility ===');
63
+ const d1 = deterministic(12345);
64
+ const seq1 = d1.floats(5);
65
+
66
+ const d2 = deterministic(12345);
67
+ const seq2 = d2.floats(5);
68
+
69
+ console.log('Sequence 1:', seq1);
70
+ console.log('Sequence 2:', seq2);
71
+ console.log('Identical:', JSON.stringify(seq1) === JSON.stringify(seq2));