@cloudglides/nox 1.1.6 → 3.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.
Files changed (66) hide show
  1. package/README.md +9 -9
  2. package/example/package.json +1 -1
  3. package/example/src/App.css +38 -7
  4. package/example/src/App.jsx +328 -25
  5. package/package.json +7 -6
  6. package/pnpm-workspace.yaml +3 -0
  7. package/src/core.browser.js +37 -4
  8. package/src/core.js +37 -4
  9. package/src/generators/logistic.js +30 -25
  10. package/src/generators/mixer.js +7 -7
  11. package/src/generators/mt19937.js +10 -7
  12. package/src/generators/pcg64.js +23 -12
  13. package/src/generators/splitmix64.js +12 -6
  14. package/src/generators/tent.js +12 -7
  15. package/src/generators/xorshift64.js +6 -3
  16. package/src/index.browser.js +168 -1
  17. package/src/index.d.ts +68 -4
  18. package/src/index.js +169 -1
  19. package/src/rng.browser.js +5 -6
  20. package/src/rng.js +95 -94
  21. package/src/utils/arrays.js +149 -0
  22. package/src/utils/bits.js +146 -21
  23. package/src/utils/categorical.js +68 -31
  24. package/src/utils/clustering.js +143 -0
  25. package/src/utils/combinatorics.js +113 -69
  26. package/src/utils/confidence.js +145 -0
  27. package/src/utils/decomposition.js +204 -0
  28. package/src/utils/diagnostics.js +309 -0
  29. package/src/utils/distributions-advanced.js +122 -0
  30. package/src/utils/distributions-extra.js +102 -11
  31. package/src/utils/distributions-special.js +77 -20
  32. package/src/utils/distributions.js +99 -35
  33. package/src/utils/effects.js +172 -0
  34. package/src/utils/entropy.browser.js +29 -26
  35. package/src/utils/entropy.js +18 -8
  36. package/src/utils/helpers.js +64 -0
  37. package/src/utils/hypothesis.js +167 -0
  38. package/src/utils/integration.js +137 -0
  39. package/src/utils/interpolation.js +221 -0
  40. package/src/utils/logistic.js +91 -0
  41. package/src/utils/matrix.js +242 -0
  42. package/src/utils/noise.js +36 -22
  43. package/src/utils/odesolvers.js +176 -0
  44. package/src/utils/optimization.js +215 -0
  45. package/src/utils/polynomial.js +136 -0
  46. package/src/utils/precomputed.js +166 -0
  47. package/src/utils/probability.js +199 -0
  48. package/src/utils/pvalues.js +142 -0
  49. package/src/utils/regression.js +170 -0
  50. package/src/utils/resampling.js +112 -0
  51. package/src/utils/rootfinding.js +158 -0
  52. package/src/utils/sampling.js +86 -77
  53. package/src/utils/seed.js +10 -4
  54. package/src/utils/seeding.js +24 -12
  55. package/src/utils/sequence.js +116 -32
  56. package/src/utils/state.js +48 -36
  57. package/src/utils/statistics.js +64 -2
  58. package/src/utils/stochastic.js +91 -31
  59. package/src/utils/stratified.js +108 -0
  60. package/src/utils/timeseries.js +166 -0
  61. package/src/utils/transforms.js +146 -0
  62. package/test/comprehensive.js +4 -3
  63. package/test/new-features.js +52 -0
  64. package/IMPROVEMENTS.md +0 -58
  65. package/PERFORMANCE.md +0 -69
  66. package/example/pnpm-lock.yaml +0 -1006
@@ -1,32 +1,92 @@
1
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
- };
2
+ if (!rng || typeof rng.nextFloat !== 'function') {
3
+ throw new TypeError('First argument must be RNG instance');
4
+ }
5
+ if (typeof steps !== 'number' || !Number.isInteger(steps)) {
6
+ throw new TypeError('steps must be an integer');
7
+ }
8
+ if (steps <= 0) {
9
+ throw new RangeError('steps must be positive');
10
+ }
11
+ if (typeof dt !== 'number') {
12
+ throw new TypeError('dt must be a number');
13
+ }
14
+ if (dt <= 0) {
15
+ throw new RangeError('dt must be positive');
16
+ }
17
+ const path = [0];
18
+ for (let i = 0; i < steps; i++) {
19
+ const drift = -0.5 * dt;
20
+ const diffusion = Math.sqrt(dt) * (rng.nextFloat() * 2 - 1);
21
+ const nextVal = path[i] + drift + diffusion;
22
+ path.push(nextVal);
23
+ }
24
+ return path;
25
+ };
26
+
27
+ export const ornsteinUhlenbeck = (rng, steps, theta = 0.1, mu = 0, sigma = 1) => {
28
+ if (!rng || typeof rng.nextFloat !== 'function') {
29
+ throw new TypeError('First argument must be RNG instance');
30
+ }
31
+ if (typeof steps !== 'number' || !Number.isInteger(steps)) {
32
+ throw new TypeError('steps must be an integer');
33
+ }
34
+ if (steps <= 0) {
35
+ throw new RangeError('steps must be positive');
36
+ }
37
+ if (typeof theta !== 'number') {
38
+ throw new TypeError('theta must be a number');
39
+ }
40
+ if (typeof mu !== 'number') {
41
+ throw new TypeError('mu must be a number');
42
+ }
43
+ if (typeof sigma !== 'number') {
44
+ throw new TypeError('sigma must be a number');
45
+ }
46
+ if (sigma <= 0) {
47
+ throw new RangeError('sigma must be positive');
48
+ }
49
+ const path = [mu];
50
+ for (let i = 0; i < steps; i++) {
51
+ const drift = theta * (mu - path[i]);
52
+ const diffusion = sigma * (rng.nextFloat() * 2 - 1);
53
+ const nextVal = path[i] + drift + diffusion;
54
+ path.push(nextVal);
55
+ }
56
+ return path;
57
+ };
58
+
59
+ export const geometricBrownian = (rng, steps, mu = 0.05, sigma = 0.2, dt = 0.01) => {
60
+ if (!rng || typeof rng.nextFloat !== 'function') {
61
+ throw new TypeError('First argument must be RNG instance');
62
+ }
63
+ if (typeof steps !== 'number' || !Number.isInteger(steps)) {
64
+ throw new TypeError('steps must be an integer');
65
+ }
66
+ if (steps <= 0) {
67
+ throw new RangeError('steps must be positive');
68
+ }
69
+ if (typeof mu !== 'number') {
70
+ throw new TypeError('mu must be a number');
71
+ }
72
+ if (typeof sigma !== 'number') {
73
+ throw new TypeError('sigma must be a number');
74
+ }
75
+ if (sigma <= 0) {
76
+ throw new RangeError('sigma must be positive');
77
+ }
78
+ if (typeof dt !== 'number') {
79
+ throw new TypeError('dt must be a number');
80
+ }
81
+ if (dt <= 0) {
82
+ throw new RangeError('dt must be positive');
83
+ }
84
+ const path = [1];
85
+ for (let i = 0; i < steps; i++) {
86
+ const drift = (mu - 0.5 * sigma * sigma) * dt;
87
+ const diffusion = sigma * Math.sqrt(dt) * (rng.nextFloat() * 2 - 1);
88
+ const nextVal = path[i] * Math.exp(drift + diffusion);
89
+ path.push(nextVal);
90
+ }
91
+ return path;
92
+ };
@@ -0,0 +1,108 @@
1
+ export function stratifiedSample(r, data, strata, n) {
2
+ if (!Array.isArray(data) || data.length === 0) {
3
+ throw new TypeError('data must be a non-empty array');
4
+ }
5
+ if (typeof strata !== 'function') {
6
+ throw new TypeError('strata must be a function that returns stratum key');
7
+ }
8
+ if (typeof n !== 'number' || n < 1) {
9
+ throw new RangeError('n must be a positive integer');
10
+ }
11
+
12
+ const groups = new Map();
13
+
14
+ for (let i = 0; i < data.length; i++) {
15
+ const key = strata(data[i]);
16
+ if (!groups.has(key)) {
17
+ groups.set(key, []);
18
+ }
19
+ groups.get(key).push(data[i]);
20
+ }
21
+
22
+ const result = [];
23
+ const groupCount = groups.size;
24
+ const samplesPerGroup = Math.floor(n / groupCount);
25
+ let remaining = n - samplesPerGroup * groupCount;
26
+
27
+ for (const [key, group] of groups) {
28
+ const k = samplesPerGroup + (remaining > 0 ? 1 : 0);
29
+ if (remaining > 0) remaining--;
30
+
31
+ if (k >= group.length) {
32
+ result.push(...group);
33
+ } else {
34
+ for (let i = 0; i < k; i++) {
35
+ const idx = r.nextInt(group.length);
36
+ result.push(group[idx]);
37
+ group[idx] = group[group.length - 1];
38
+ group.pop();
39
+ }
40
+ }
41
+ }
42
+
43
+ return result;
44
+ }
45
+
46
+ export function stratifiedSampleProportional(r, data, strata, n) {
47
+ if (!Array.isArray(data) || data.length === 0) {
48
+ throw new TypeError('data must be a non-empty array');
49
+ }
50
+ if (typeof strata !== 'function') {
51
+ throw new TypeError('strata must be a function');
52
+ }
53
+ if (typeof n !== 'number' || n < 1) {
54
+ throw new RangeError('n must be a positive integer');
55
+ }
56
+
57
+ const groups = new Map();
58
+
59
+ for (let i = 0; i < data.length; i++) {
60
+ const key = strata(data[i]);
61
+ if (!groups.has(key)) {
62
+ groups.set(key, []);
63
+ }
64
+ groups.get(key).push(data[i]);
65
+ }
66
+
67
+ const result = [];
68
+ const total = data.length;
69
+
70
+ for (const [key, group] of groups) {
71
+ const k = Math.round(n * group.length / total);
72
+
73
+ if (k >= group.length) {
74
+ result.push(...group);
75
+ } else if (k > 0) {
76
+ const copy = [...group];
77
+ for (let i = 0; i < k; i++) {
78
+ const idx = r.nextInt(copy.length);
79
+ result.push(copy[idx]);
80
+ copy[idx] = copy[copy.length - 1];
81
+ copy.pop();
82
+ }
83
+ }
84
+ }
85
+
86
+ return result;
87
+ }
88
+
89
+ export function stratify(data, strata) {
90
+ if (!Array.isArray(data) || data.length === 0) {
91
+ throw new TypeError('data must be a non-empty array');
92
+ }
93
+ if (typeof strata !== 'function') {
94
+ throw new TypeError('strata must be a function');
95
+ }
96
+
97
+ const groups = new Map();
98
+
99
+ for (let i = 0; i < data.length; i++) {
100
+ const key = strata(data[i]);
101
+ if (!groups.has(key)) {
102
+ groups.set(key, []);
103
+ }
104
+ groups.get(key).push(data[i]);
105
+ }
106
+
107
+ return Object.fromEntries(groups);
108
+ }
@@ -0,0 +1,166 @@
1
+ export function diff(data, order = 1) {
2
+ if (!Array.isArray(data) || data.length === 0) {
3
+ throw new TypeError('data must be non-empty array');
4
+ }
5
+ if (typeof order !== 'number' || order < 1) {
6
+ throw new RangeError('order must be positive integer');
7
+ }
8
+
9
+ let result = [...data];
10
+
11
+ for (let o = 0; o < order; o++) {
12
+ const prev = result;
13
+ result = new Array(prev.length - 1);
14
+ for (let i = 0; i < prev.length - 1; i++) {
15
+ result[i] = prev[i + 1] - prev[i];
16
+ }
17
+ }
18
+
19
+ return result;
20
+ }
21
+
22
+ export function lag(data, lags = 1) {
23
+ if (!Array.isArray(data) || data.length === 0) {
24
+ throw new TypeError('data must be non-empty array');
25
+ }
26
+ if (typeof lags !== 'number' || lags < 1) {
27
+ throw new RangeError('lags must be positive integer');
28
+ }
29
+
30
+ const result = new Array(data.length);
31
+
32
+ for (let i = 0; i < data.length; i++) {
33
+ result[i] = i < lags ? null : data[i - lags];
34
+ }
35
+
36
+ return result;
37
+ }
38
+
39
+ export function shift(data, periods = 1) {
40
+ if (!Array.isArray(data) || data.length === 0) {
41
+ throw new TypeError('data must be non-empty array');
42
+ }
43
+ if (typeof periods !== 'number') {
44
+ throw new RangeError('periods must be integer');
45
+ }
46
+
47
+ const result = new Array(data.length);
48
+
49
+ if (periods > 0) {
50
+ for (let i = 0; i < data.length; i++) {
51
+ result[i] = i < periods ? null : data[i - periods];
52
+ }
53
+ } else if (periods < 0) {
54
+ for (let i = 0; i < data.length; i++) {
55
+ result[i] = i < -periods ? null : data[i - periods];
56
+ }
57
+ } else {
58
+ return [...data];
59
+ }
60
+
61
+ return result;
62
+ }
63
+
64
+ export function sma(data, window) {
65
+ if (!Array.isArray(data) || data.length === 0) {
66
+ throw new TypeError('data must be non-empty array');
67
+ }
68
+ if (typeof window !== 'number' || window < 1) {
69
+ throw new RangeError('window must be positive integer');
70
+ }
71
+
72
+ const result = new Array(data.length);
73
+
74
+ for (let i = 0; i < data.length; i++) {
75
+ if (i < window - 1) {
76
+ result[i] = null;
77
+ } else {
78
+ let sum = 0;
79
+ for (let j = i - window + 1; j <= i; j++) {
80
+ sum += data[j];
81
+ }
82
+ result[i] = sum / window;
83
+ }
84
+ }
85
+
86
+ return result;
87
+ }
88
+
89
+ export function ema(data, window) {
90
+ if (!Array.isArray(data) || data.length === 0) {
91
+ throw new TypeError('data must be non-empty array');
92
+ }
93
+ if (typeof window !== 'number' || window < 1) {
94
+ throw new RangeError('window must be positive integer');
95
+ }
96
+
97
+ const alpha = 2 / (window + 1);
98
+ const result = new Array(data.length);
99
+
100
+ result[0] = data[0];
101
+
102
+ for (let i = 1; i < data.length; i++) {
103
+ result[i] = alpha * data[i] + (1 - alpha) * result[i - 1];
104
+ }
105
+
106
+ return result;
107
+ }
108
+
109
+ export function acf(data, maxLag = 40) {
110
+ if (!Array.isArray(data) || data.length === 0) {
111
+ throw new TypeError('data must be non-empty array');
112
+ }
113
+ if (typeof maxLag !== 'number' || maxLag < 1) {
114
+ throw new RangeError('maxLag must be positive integer');
115
+ }
116
+
117
+ const mean = data.reduce((a, b) => a + b, 0) / data.length;
118
+ const centered = data.map(x => x - mean);
119
+
120
+ let c0 = 0;
121
+ for (let i = 0; i < data.length; i++) {
122
+ c0 += centered[i] * centered[i];
123
+ }
124
+
125
+ const result = new Array(Math.min(maxLag + 1, data.length));
126
+ result[0] = 1;
127
+
128
+ for (let k = 1; k < result.length; k++) {
129
+ let ck = 0;
130
+ for (let i = 0; i < data.length - k; i++) {
131
+ ck += centered[i] * centered[i + k];
132
+ }
133
+ result[k] = ck / c0;
134
+ }
135
+
136
+ return result;
137
+ }
138
+
139
+ export function pacf(data, maxLag = 40) {
140
+ if (!Array.isArray(data) || data.length === 0) {
141
+ throw new TypeError('data must be non-empty array');
142
+ }
143
+ if (typeof maxLag !== 'number' || maxLag < 1) {
144
+ throw new RangeError('maxLag must be positive integer');
145
+ }
146
+
147
+ const correlations = acf(data, maxLag);
148
+ const result = [1];
149
+
150
+ for (let k = 1; k < correlations.length; k++) {
151
+ let numerator = correlations[k];
152
+
153
+ for (let j = 1; j < k; j++) {
154
+ numerator -= result[j] * correlations[k - j];
155
+ }
156
+
157
+ let denominator = 1;
158
+ for (let j = 1; j < k; j++) {
159
+ denominator -= result[j] * correlations[j];
160
+ }
161
+
162
+ result.push(numerator / denominator);
163
+ }
164
+
165
+ return result;
166
+ }
@@ -0,0 +1,146 @@
1
+ export function zscore(data) {
2
+ if (!Array.isArray(data) || data.length === 0) {
3
+ throw new TypeError('data must be non-empty array');
4
+ }
5
+
6
+ const mean = data.reduce((a, b) => a + b, 0) / data.length;
7
+ let variance = 0;
8
+
9
+ for (let i = 0; i < data.length; i++) {
10
+ variance += (data[i] - mean) ** 2;
11
+ }
12
+
13
+ const stddev = Math.sqrt(variance / data.length);
14
+
15
+ if (stddev === 0) {
16
+ throw new RangeError('cannot z-score data with zero variance');
17
+ }
18
+
19
+ return data.map(x => (x - mean) / stddev);
20
+ }
21
+
22
+ export function standardize(data) {
23
+ if (!Array.isArray(data) || data.length === 0) {
24
+ throw new TypeError('data must be non-empty array');
25
+ }
26
+
27
+ const mean = data.reduce((a, b) => a + b, 0) / data.length;
28
+ return data.map(x => x - mean);
29
+ }
30
+
31
+ export function minMaxScale(data, min = 0, max = 1) {
32
+ if (!Array.isArray(data) || data.length === 0) {
33
+ throw new TypeError('data must be non-empty array');
34
+ }
35
+ if (typeof min !== 'number' || typeof max !== 'number') {
36
+ throw new TypeError('min and max must be numbers');
37
+ }
38
+
39
+ const dataMin = Math.min(...data);
40
+ const dataMax = Math.max(...data);
41
+ const range = dataMax - dataMin;
42
+
43
+ if (range === 0) {
44
+ throw new RangeError('cannot scale data with zero range');
45
+ }
46
+
47
+ return data.map(x => ((x - dataMin) / range) * (max - min) + min);
48
+ }
49
+
50
+ export function logTransform(data, base = Math.E) {
51
+ if (!Array.isArray(data) || data.length === 0) {
52
+ throw new TypeError('data must be non-empty array');
53
+ }
54
+ if (typeof base !== 'number' || base <= 0 || base === 1) {
55
+ throw new RangeError('base must be positive number != 1');
56
+ }
57
+
58
+ for (let i = 0; i < data.length; i++) {
59
+ if (data[i] <= 0) {
60
+ throw new RangeError('all values must be positive');
61
+ }
62
+ }
63
+
64
+ const logBase = Math.log(base);
65
+ return data.map(x => Math.log(x) / logBase);
66
+ }
67
+
68
+ export function sqrtTransform(data) {
69
+ if (!Array.isArray(data) || data.length === 0) {
70
+ throw new TypeError('data must be non-empty array');
71
+ }
72
+
73
+ for (let i = 0; i < data.length; i++) {
74
+ if (data[i] < 0) {
75
+ throw new RangeError('all values must be non-negative');
76
+ }
77
+ }
78
+
79
+ return data.map(x => Math.sqrt(x));
80
+ }
81
+
82
+ export function rank(data) {
83
+ if (!Array.isArray(data) || data.length === 0) {
84
+ throw new TypeError('data must be non-empty array');
85
+ }
86
+
87
+ const indexed = data.map((val, i) => [val, i]);
88
+ indexed.sort((a, b) => a[0] - b[0]);
89
+
90
+ const ranks = new Array(data.length);
91
+ let i = 0;
92
+
93
+ while (i < indexed.length) {
94
+ let j = i;
95
+ while (j < indexed.length && indexed[j][0] === indexed[i][0]) {
96
+ j++;
97
+ }
98
+
99
+ const rank = (i + 1 + j) / 2;
100
+ for (let k = i; k < j; k++) {
101
+ ranks[indexed[k][1]] = rank;
102
+ }
103
+
104
+ i = j;
105
+ }
106
+
107
+ return ranks;
108
+ }
109
+
110
+ export function robustScale(data, center = 'median', scale = 'iqr') {
111
+ if (!Array.isArray(data) || data.length === 0) {
112
+ throw new TypeError('data must be non-empty array');
113
+ }
114
+
115
+ let centerVal;
116
+ if (center === 'median') {
117
+ const sorted = [...data].sort((a, b) => a - b);
118
+ centerVal = sorted.length % 2 === 0
119
+ ? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2
120
+ : sorted[Math.floor(sorted.length / 2)];
121
+ } else if (center === 'mean') {
122
+ centerVal = data.reduce((a, b) => a + b, 0) / data.length;
123
+ } else {
124
+ throw new RangeError('center must be "median" or "mean"');
125
+ }
126
+
127
+ let scaleVal;
128
+ if (scale === 'iqr') {
129
+ const sorted = [...data].sort((a, b) => a - b);
130
+ const q1 = sorted[Math.floor(sorted.length * 0.25)];
131
+ const q3 = sorted[Math.floor(sorted.length * 0.75)];
132
+ scaleVal = q3 - q1;
133
+ } else if (scale === 'mad') {
134
+ const centered = data.map(x => Math.abs(x - centerVal));
135
+ const sorted = [...centered].sort((a, b) => a - b);
136
+ scaleVal = sorted[Math.floor(sorted.length / 2)];
137
+ } else {
138
+ throw new RangeError('scale must be "iqr" or "mad"');
139
+ }
140
+
141
+ if (scaleVal === 0) {
142
+ throw new RangeError('scale value is zero');
143
+ }
144
+
145
+ return data.map(x => (x - centerVal) / scaleVal);
146
+ }
@@ -61,10 +61,11 @@ console.log(` State restored correctly: ${val2 === val2_restored}`);
61
61
 
62
62
  console.log('\n7. Generator Cloning');
63
63
  const orig = deterministic(12345);
64
- const origSeq = orig.floats(3);
64
+ orig.floats(3);
65
65
  const cloned = cloneGenerator(orig);
66
- const clonedSeq = cloned.floats(3);
67
- console.log(` Clone has same sequence: ${JSON.stringify(origSeq) === JSON.stringify(clonedSeq)}`);
66
+ const origNext = orig.floats(3);
67
+ const clonedNext = cloned.floats(3);
68
+ console.log(` Clone has same sequence: ${JSON.stringify(origNext) === JSON.stringify(clonedNext)}`);
68
69
 
69
70
  console.log('\n8. Weighted Sampling');
70
71
  const items = ['A', 'B', 'C'];
@@ -0,0 +1,52 @@
1
+ import { rng, binomial, geometric, normals, exponentials, sampleWithReplacement, permute, range, cycle, clz, ctz, popcountNum, reverseBits, setBit, clearBit, toggleBit } from '../src/index.js';
2
+
3
+ console.log('=== NEW FEATURES TEST ===\n');
4
+
5
+ const r = rng();
6
+
7
+ console.log('1. Binomial Distribution');
8
+ const binom = binomial(r, 20, 0.5);
9
+ console.log(` binomial(20, 0.5): ${binom} (between 0-20)`);
10
+
11
+ console.log('\n2. Geometric Distribution');
12
+ const geom = geometric(r, 0.3);
13
+ console.log(` geometric(0.3): ${geom} (number of trials)`);
14
+
15
+ console.log('\n3. Batch Normal Distribution (with caching)');
16
+ const normals_arr = normals(r, 5);
17
+ console.log(` normals(5): [${normals_arr.map(n => n.toFixed(2)).join(', ')}]`);
18
+
19
+ console.log('\n4. Batch Exponential Distribution');
20
+ const exps = exponentials(r, 4, 1);
21
+ console.log(` exponentials(4): [${exps.map(e => e.toFixed(2)).join(', ')}]`);
22
+
23
+ console.log('\n5. Sampling with Replacement');
24
+ const sampled = sampleWithReplacement(['A', 'B', 'C'], 8, r);
25
+ console.log(` sampleWithReplacement([A,B,C], 8): [${sampled.join(', ')}]`);
26
+
27
+ console.log('\n6. Permutation (alias for shuffle)');
28
+ const perm = permute([1, 2, 3, 4], r);
29
+ console.log(` permute([1,2,3,4]): [${perm.join(', ')}]`);
30
+
31
+ console.log('\n7. Range Generation');
32
+ const r1 = range(0, 5);
33
+ const r2 = range(10, 5, -1);
34
+ console.log(` range(0, 5): [${r1.join(', ')}]`);
35
+ console.log(` range(10, 5, -1): [${r2.join(', ')}]`);
36
+
37
+ console.log('\n8. Cycle (repeat array)');
38
+ const cyc = cycle(['X', 'Y'], 6);
39
+ console.log(` cycle([X, Y], 6): [${cyc.join(', ')}]`);
40
+
41
+ console.log('\n9. Bit Operations');
42
+ const val = 0b11010110n;
43
+ console.log(` Value: 0b11010110`);
44
+ console.log(` clz(): ${clz(val)}`);
45
+ console.log(` ctz(): ${ctz(val)}`);
46
+ console.log(` popcountNum(214): ${popcountNum(214)}`);
47
+ console.log(` reverseBits() [8-bit]: 0b${reverseBits(val, 8).toString(2).padStart(8, '0')}`);
48
+ console.log(` setBit(0b11010110, 0): 0b${setBit(val, 0).toString(2).padStart(8, '0')}`);
49
+ console.log(` clearBit(0b11010110, 1): 0b${clearBit(val, 1).toString(2).padStart(8, '0')}`);
50
+ console.log(` toggleBit(0b11010110, 4): 0b${toggleBit(val, 4).toString(2).padStart(8, '0')}`);
51
+
52
+ console.log('\n✓ All new features working correctly');
package/IMPROVEMENTS.md DELETED
@@ -1,58 +0,0 @@
1
- # Improvements Made (Hour 2)
2
-
3
- ## Batch Operations
4
- - Added `batch(count, fn)` - Execute custom function n times
5
- - Added `floats(count)` - Generate array of floats
6
- - Added `ints(count, max)` - Generate array of integers
7
- - Added `bools(count, probability)` - Generate array of booleans
8
-
9
- ## RNG API Enhancements
10
- - Added `range(min, max, step)` - Pick random value from stepped range
11
- - Added `choice(arr)` - Pick random element from array
12
- - Better error messages with TypeError vs RangeError distinctions
13
-
14
- ## Statistical Improvements
15
- - Added `kolmogorovSmirnovTest(data)` - KS goodness-of-fit test
16
- - Added `meanTest(data, expected)` - Test mean of distribution
17
- - Added `varianceTest(data, expected)` - Test variance of distribution
18
- - All tests return detailed result objects with pass/fail indicators
19
-
20
- ## Sampling Enhancements
21
- - Improved `weightedPick()` with comprehensive validation
22
- - Improved `weightedSample()` with type checking
23
- - Improved `reservoirSample()` with better error handling
24
-
25
- ## Generator Optimizations
26
- - Removed modulo bias in `nextInt()` - uses rejection sampling instead
27
- - Applied to: PCG64, Xorshift64, Splitmix64, MT19937
28
- - Ensures uniform distribution across all ranges
29
-
30
- ## Performance & Caching
31
- - Added crypto cache in entropy source for performance
32
- - Added `clearCryptoCache()` export for testing
33
- - Better entropy mixing with proper BigInt operations
34
-
35
- ## Type Safety
36
- - Added `IGenerator` interface for type consistency
37
- - Added `GeneratorConstructor` type definition
38
- - Added generic types to shuffle, pick, sample functions
39
- - Added test result interfaces: `TestResult`, `KSTestResult`
40
- - Complete TypeScript definitions for all new APIs
41
-
42
- ## Documentation
43
- - Updated README with batch operations examples
44
- - Added weighted sampling section
45
- - Added statistical tests section
46
- - Enhanced generator selection documentation
47
-
48
- ## Testing
49
- - Added comprehensive test suite (test/comprehensive.js)
50
- - Added advanced features test (test/advanced.js)
51
- - All tests pass successfully
52
-
53
- ## Code Quality
54
- - Total lines: 1107 → 1318 (+211)
55
- - Files modified: 10
56
- - Lines added/changed: 286
57
- - Consistent error handling patterns
58
- - Full backward compatibility maintained