@buley/hexgrid-3d 1.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 (46) hide show
  1. package/.eslintrc.json +28 -0
  2. package/LICENSE +39 -0
  3. package/README.md +291 -0
  4. package/examples/basic-usage.tsx +52 -0
  5. package/package.json +65 -0
  6. package/public/hexgrid-worker.js +1763 -0
  7. package/rust/Cargo.toml +41 -0
  8. package/rust/src/lib.rs +740 -0
  9. package/rust/src/math.rs +574 -0
  10. package/rust/src/spatial.rs +245 -0
  11. package/rust/src/statistics.rs +496 -0
  12. package/src/HexGridEnhanced.ts +16 -0
  13. package/src/Snapshot.ts +1402 -0
  14. package/src/adapters.ts +65 -0
  15. package/src/algorithms/AdvancedStatistics.ts +328 -0
  16. package/src/algorithms/BayesianStatistics.ts +317 -0
  17. package/src/algorithms/FlowField.ts +126 -0
  18. package/src/algorithms/FluidSimulation.ts +99 -0
  19. package/src/algorithms/GraphAlgorithms.ts +184 -0
  20. package/src/algorithms/OutlierDetection.ts +391 -0
  21. package/src/algorithms/ParticleSystem.ts +85 -0
  22. package/src/algorithms/index.ts +13 -0
  23. package/src/compat.ts +96 -0
  24. package/src/components/HexGrid.tsx +31 -0
  25. package/src/components/NarrationOverlay.tsx +221 -0
  26. package/src/components/index.ts +2 -0
  27. package/src/features.ts +125 -0
  28. package/src/index.ts +30 -0
  29. package/src/math/HexCoordinates.ts +15 -0
  30. package/src/math/Matrix4.ts +35 -0
  31. package/src/math/Quaternion.ts +37 -0
  32. package/src/math/SpatialIndex.ts +114 -0
  33. package/src/math/Vector3.ts +69 -0
  34. package/src/math/index.ts +11 -0
  35. package/src/note-adapter.ts +124 -0
  36. package/src/ontology-adapter.ts +77 -0
  37. package/src/stores/index.ts +1 -0
  38. package/src/stores/uiStore.ts +85 -0
  39. package/src/types/index.ts +3 -0
  40. package/src/types.ts +152 -0
  41. package/src/utils/image-utils.ts +25 -0
  42. package/src/wasm/HexGridWasmWrapper.ts +753 -0
  43. package/src/wasm/index.ts +7 -0
  44. package/src/workers/hexgrid-math.ts +177 -0
  45. package/src/workers/hexgrid-worker.worker.ts +1807 -0
  46. package/tsconfig.json +18 -0
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Type-safe adapter pattern for converting domain objects to GridItems
3
+ */
4
+
5
+ import type { GridItem } from './types'
6
+
7
+ /**
8
+ * Options for adapter conversion
9
+ */
10
+ export interface AdapterOptions {
11
+ /**
12
+ * Custom velocity calculation override
13
+ */
14
+ velocity?: number
15
+ /**
16
+ * Custom visual URL override
17
+ */
18
+ visualUrl?: string
19
+ /**
20
+ * Additional metadata to merge
21
+ */
22
+ metadata?: Record<string, unknown>
23
+ }
24
+
25
+ /**
26
+ * Type-safe adapter for converting domain objects to GridItems
27
+ */
28
+ export interface ItemAdapter<T> {
29
+ /**
30
+ * Convert a domain object to a GridItem
31
+ */
32
+ toGridItem(data: T, options?: AdapterOptions): GridItem<T>
33
+ /**
34
+ * Extract the original domain object from a GridItem
35
+ */
36
+ fromGridItem(item: GridItem<T>): T
37
+ /**
38
+ * Calculate velocity for the item (optional)
39
+ */
40
+ calculateVelocity?(data: T): number
41
+ /**
42
+ * Extract visual URL for the item (optional)
43
+ */
44
+ extractVisualUrl?(data: T): string | undefined
45
+ }
46
+
47
+ /**
48
+ * Helper to create adapters for common patterns
49
+ */
50
+ export function createAdapter<T>(
51
+ config: {
52
+ type: string
53
+ toGridItem: (data: T, options?: AdapterOptions) => GridItem<T>
54
+ fromGridItem: (item: GridItem<T>) => T
55
+ calculateVelocity?: (data: T) => number
56
+ extractVisualUrl?: (data: T) => string | undefined
57
+ }
58
+ ): ItemAdapter<T> {
59
+ return {
60
+ toGridItem: config.toGridItem,
61
+ fromGridItem: config.fromGridItem,
62
+ calculateVelocity: config.calculateVelocity,
63
+ extractVisualUrl: config.extractVisualUrl,
64
+ }
65
+ }
@@ -0,0 +1,328 @@
1
+ export interface TrendResult {
2
+ slope: number;
3
+ direction: 'increasing' | 'decreasing' | 'stable';
4
+ rSquared?: number;
5
+ }
6
+
7
+ export function giniCoefficient(values: number[]): number {
8
+ if (values.length === 0) return 0;
9
+ const sorted = [...values].sort((a, b) => a - b);
10
+ const total = sorted.reduce((sum, val) => sum + val, 0);
11
+ if (total === 0) return 0;
12
+ const n = sorted.length;
13
+ let cumulative = 0;
14
+ for (let i = 0; i < n; i++) {
15
+ cumulative += (i + 1) * sorted[i];
16
+ }
17
+ return (2 * cumulative) / (n * total) - (n + 1) / n;
18
+ }
19
+
20
+ export function theilIndex(values: number[]): number {
21
+ if (values.length === 0) return 0;
22
+ const avg = values.reduce((sum, val) => sum + val, 0) / values.length;
23
+ if (avg === 0) return 0;
24
+ return values.reduce((sum, val) => {
25
+ const ratio = val / avg;
26
+ return sum + (ratio === 0 ? 0 : ratio * Math.log(ratio));
27
+ }, 0) / values.length;
28
+ }
29
+
30
+ export function atkinsonIndex(values: number[], epsilon: number): number {
31
+ if (values.length === 0) return 0;
32
+ const avg = values.reduce((sum, val) => sum + val, 0) / values.length;
33
+ if (avg === 0) return 0;
34
+ if (epsilon === 1) {
35
+ const geomMean = Math.exp(
36
+ values.reduce((sum, val) => sum + Math.log(Math.max(val, 1e-9)), 0) /
37
+ values.length
38
+ );
39
+ return 1 - geomMean / avg;
40
+ }
41
+ const meanPower =
42
+ values.reduce((sum, val) => sum + Math.pow(val, 1 - epsilon), 0) /
43
+ values.length;
44
+ const eq = Math.pow(meanPower, 1 / (1 - epsilon));
45
+ return 1 - eq / avg;
46
+ }
47
+
48
+ export function paretoRatio(values: number[], topFraction: number): {
49
+ ratioHeld: number;
50
+ paretoIndex: number;
51
+ } {
52
+ if (values.length === 0) return { ratioHeld: 0, paretoIndex: 0 };
53
+ const sorted = [...values].sort((a, b) => b - a);
54
+ const total = sorted.reduce((sum, val) => sum + val, 0);
55
+ if (total === 0) return { ratioHeld: 0, paretoIndex: 0 };
56
+ const topCount = Math.max(1, Math.floor(values.length * topFraction));
57
+ const topSum = sorted.slice(0, topCount).reduce((sum, val) => sum + val, 0);
58
+ return { ratioHeld: topSum / total, paretoIndex: topFraction };
59
+ }
60
+
61
+ export function zipfCoefficient(values: number[]): number {
62
+ if (values.length === 0) return 0;
63
+ const sorted = [...values].sort((a, b) => b - a);
64
+ const total = sorted.reduce((sum, val) => sum + val, 0);
65
+ if (total === 0) return 0;
66
+ return sorted.reduce((sum, val, idx) => sum + val / (idx + 1), 0) / total;
67
+ }
68
+
69
+ export function herfindahlIndex(values: number[]): number {
70
+ if (values.length === 0) return 0;
71
+ const total = values.reduce((sum, val) => sum + val, 0);
72
+ if (total === 0) return 0;
73
+ return values.reduce((sum, val) => {
74
+ const share = val / total;
75
+ return sum + share * share;
76
+ }, 0);
77
+ }
78
+
79
+ export function shannonEntropy(values: number[]): number {
80
+ if (values.length === 0) return 0;
81
+ const total = values.reduce((sum, val) => sum + val, 0);
82
+ if (total === 0) return 0;
83
+ return values.reduce((sum, val) => {
84
+ const p = val / total;
85
+ return p === 0 ? sum : sum - p * Math.log2(p);
86
+ }, 0);
87
+ }
88
+
89
+ export function normalizedEntropy(values: number[]): number {
90
+ if (values.length === 0) return 0;
91
+ const entropy = shannonEntropy(values);
92
+ const maxEntropy = Math.log2(values.length || 1);
93
+ return maxEntropy === 0 ? 0 : entropy / maxEntropy;
94
+ }
95
+
96
+ export function renyiEntropy(values: number[], alpha: number): number {
97
+ if (values.length === 0) return 0;
98
+ const total = values.reduce((sum, val) => sum + val, 0);
99
+ if (total === 0) return 0;
100
+ const sum = values.reduce((acc, val) => {
101
+ const p = val / total;
102
+ return acc + Math.pow(p, alpha);
103
+ }, 0);
104
+ return (1 / (1 - alpha)) * Math.log2(sum || 1);
105
+ }
106
+
107
+ export function tsallisEntropy(values: number[], q: number): number {
108
+ if (values.length === 0) return 0;
109
+ const total = values.reduce((sum, val) => sum + val, 0);
110
+ if (total === 0) return 0;
111
+ const sum = values.reduce((acc, val) => {
112
+ const p = val / total;
113
+ return acc + Math.pow(p, q);
114
+ }, 0);
115
+ return (1 - sum) / (q - 1);
116
+ }
117
+
118
+ export function detectTrend(values: number[]): TrendResult {
119
+ if (values.length < 2) {
120
+ return { slope: 0, direction: 'stable', rSquared: 0 };
121
+ }
122
+ const n = values.length;
123
+ const xMean = (n - 1) / 2;
124
+ const yMean = values.reduce((sum, v) => sum + v, 0) / n;
125
+ let num = 0;
126
+ let den = 0;
127
+ let ssRes = 0;
128
+ let ssTot = 0;
129
+
130
+ for (let i = 0; i < n; i++) {
131
+ const dx = i - xMean;
132
+ num += dx * (values[i] - yMean);
133
+ den += dx * dx;
134
+ }
135
+ const slope = den === 0 ? 0 : num / den;
136
+ const intercept = yMean - slope * xMean;
137
+
138
+ // Calculate R-squared
139
+ for (let i = 0; i < n; i++) {
140
+ const predicted = slope * i + intercept;
141
+ ssRes += Math.pow(values[i] - predicted, 2);
142
+ ssTot += Math.pow(values[i] - yMean, 2);
143
+ }
144
+ const rSquared = ssTot === 0 ? 0 : 1 - ssRes / ssTot;
145
+
146
+ const direction = slope > 0.1 ? 'increasing' : slope < -0.1 ? 'decreasing' : 'stable';
147
+ return { slope, direction, rSquared };
148
+ }
149
+
150
+ export function detectChangePoints(values: number[]): number[] {
151
+ if (values.length < 3) return [];
152
+ const changes: number[] = [];
153
+ const avg = values.reduce((sum, val) => sum + val, 0) / values.length;
154
+ const variance = values.reduce((sum, val) => sum + (val - avg) ** 2, 0) / values.length;
155
+ const threshold = Math.sqrt(variance) * 1.5;
156
+ for (let i = 1; i < values.length; i++) {
157
+ if (Math.abs(values[i] - values[i - 1]) > threshold) {
158
+ changes.push(i);
159
+ }
160
+ }
161
+ return changes;
162
+ }
163
+
164
+ export function movingAverage(values: number[], windowSize: number): number[] {
165
+ if (values.length === 0) return [];
166
+ const result: number[] = [];
167
+ for (let i = 0; i < values.length; i++) {
168
+ const start = Math.max(0, i - windowSize + 1);
169
+ const window = values.slice(start, i + 1);
170
+ const avg = window.reduce((sum, val) => sum + val, 0) / window.length;
171
+ result.push(avg);
172
+ }
173
+ return result;
174
+ }
175
+
176
+ export function exponentialMovingAverage(values: number[], alpha: number): number[] {
177
+ if (values.length === 0) return [];
178
+ const result: number[] = [];
179
+ let current = values[0] ?? 0;
180
+ result.push(current);
181
+ for (let i = 1; i < values.length; i++) {
182
+ current = alpha * values[i] + (1 - alpha) * current;
183
+ result.push(current);
184
+ }
185
+ return result;
186
+ }
187
+
188
+ export function predictWinner(values: number[]): number {
189
+ if (values.length === 0) return 0;
190
+ const max = Math.max(...values);
191
+ return values.indexOf(max);
192
+ }
193
+
194
+ export function sparkline(values: number[]): string {
195
+ if (values.length === 0) return '';
196
+ const min = Math.min(...values);
197
+ const max = Math.max(...values);
198
+ const range = max - min || 1;
199
+ const ticks = '▁▂▃▄▅▆▇█';
200
+ return values
201
+ .map((value) => {
202
+ const idx = Math.round(((value - min) / range) * (ticks.length - 1));
203
+ return ticks[idx] ?? ticks[0];
204
+ })
205
+ .join('');
206
+ }
207
+
208
+ export function sparklineSvg(values: number[], width: number = 100, height: number = 20): string {
209
+ if (values.length === 0) return '';
210
+ const min = Math.min(...values);
211
+ const max = Math.max(...values);
212
+ const range = max - min || 1;
213
+ const step = width / Math.max(1, values.length - 1);
214
+ return values
215
+ .map((value, index) => {
216
+ const x = index * step;
217
+ const y = height - ((value - min) / range) * height;
218
+ return `${index === 0 ? 'M' : 'L'}${x},${y}`;
219
+ })
220
+ .join(' ');
221
+ }
222
+
223
+ // KL Divergence (Kullback-Leibler)
224
+ export function klDivergence(p: number[], q: number[]): number {
225
+ if (p.length !== q.length || p.length === 0) return 0;
226
+ return p.reduce((sum, pi, i) => {
227
+ if (pi === 0) return sum;
228
+ const qi = q[i] ?? 1e-10;
229
+ return sum + pi * Math.log2(pi / qi);
230
+ }, 0);
231
+ }
232
+
233
+ // JS Divergence (Jensen-Shannon)
234
+ export function jsDivergence(p: number[], q: number[]): number {
235
+ if (p.length !== q.length || p.length === 0) return 0;
236
+ const m = p.map((pi, i) => (pi + (q[i] ?? 0)) / 2);
237
+ return (klDivergence(p, m) + klDivergence(q, m)) / 2;
238
+ }
239
+
240
+ // Bhattacharyya Coefficient
241
+ export function bhattacharyyaCoefficient(p: number[], q: number[]): number {
242
+ if (p.length !== q.length || p.length === 0) return 0;
243
+ return p.reduce((sum, pi, i) => {
244
+ const qi = q[i] ?? 0;
245
+ return sum + Math.sqrt(pi * qi);
246
+ }, 0);
247
+ }
248
+
249
+ // Hellinger Distance
250
+ export function hellingerDistance(p: number[], q: number[]): number {
251
+ if (p.length !== q.length || p.length === 0) return 0;
252
+ const bc = bhattacharyyaCoefficient(p, q);
253
+ return Math.sqrt(1 - bc);
254
+ }
255
+
256
+ // Double Exponential Smoothing (Holt's method)
257
+ export function doubleExponentialSmoothing(values: number[], alpha: number = 0.3, beta: number = 0.1): number[] {
258
+ if (values.length === 0) return [];
259
+ const result: number[] = [];
260
+ let level = values[0] ?? 0;
261
+ let trend = 0;
262
+
263
+ result.push(level);
264
+
265
+ for (let i = 1; i < values.length; i++) {
266
+ const prevLevel = level;
267
+ level = alpha * values[i] + (1 - alpha) * (level + trend);
268
+ trend = beta * (level - prevLevel) + (1 - beta) * trend;
269
+ result.push(level + trend);
270
+ }
271
+
272
+ return result;
273
+ }
274
+
275
+ // Euler Characteristic (simplified for 2D)
276
+ export function eulerCharacteristic(vertices: number, edges: number, faces: number): number {
277
+ return vertices - edges + faces;
278
+ }
279
+
280
+ // Estimate Betti Numbers (simplified - returns basic topological invariants)
281
+ export function estimateBettiNumbers(complex: { vertices: number; edges: number; faces: number }): { b0: number; b1: number } {
282
+ const euler = eulerCharacteristic(complex.vertices, complex.edges, complex.faces);
283
+ // Simplified: b0 = number of connected components (assume 1 for now)
284
+ // b1 = edges - vertices + 1 (for a connected graph)
285
+ const b0 = 1;
286
+ const b1 = Math.max(0, complex.edges - complex.vertices + 1);
287
+ return { b0, b1 };
288
+ }
289
+
290
+ // Compactness measure
291
+ export function compactness(area: number, perimeter: number): number {
292
+ if (perimeter === 0) return 0;
293
+ // 4π * area / perimeter^2 (circularity measure)
294
+ return (4 * Math.PI * area) / (perimeter * perimeter);
295
+ }
296
+
297
+ // Territory Statistics
298
+ export interface TerritoryStats {
299
+ totalTerritories: number;
300
+ averageSize: number;
301
+ largestTerritory: number;
302
+ smallestTerritory: number;
303
+ compactness: number;
304
+ }
305
+
306
+ export function computeTerritoryStats(territories: Array<{ area: number; perimeter: number }>): TerritoryStats {
307
+ if (territories.length === 0) {
308
+ return {
309
+ totalTerritories: 0,
310
+ averageSize: 0,
311
+ largestTerritory: 0,
312
+ smallestTerritory: 0,
313
+ compactness: 0,
314
+ };
315
+ }
316
+
317
+ const sizes = territories.map(t => t.area);
318
+ const totalSize = sizes.reduce((sum, s) => sum + s, 0);
319
+ const avgCompactness = territories.reduce((sum, t) => sum + compactness(t.area, t.perimeter), 0) / territories.length;
320
+
321
+ return {
322
+ totalTerritories: territories.length,
323
+ averageSize: totalSize / territories.length,
324
+ largestTerritory: Math.max(...sizes),
325
+ smallestTerritory: Math.min(...sizes),
326
+ compactness: avgCompactness,
327
+ };
328
+ }
@@ -0,0 +1,317 @@
1
+ export class BetaDistribution {
2
+ alpha: number;
3
+ beta: number;
4
+
5
+ constructor(alpha: number, beta: number) {
6
+ this.alpha = alpha;
7
+ this.beta = beta;
8
+ }
9
+
10
+ mean(): number {
11
+ const total = this.alpha + this.beta;
12
+ return total === 0 ? 0.5 : this.alpha / total;
13
+ }
14
+
15
+ variance(): number {
16
+ const total = this.alpha + this.beta;
17
+ if (total <= 1) return 0;
18
+ return (this.alpha * this.beta) / (total * total * (total + 1));
19
+ }
20
+ }
21
+
22
+ export class MarkovChain {
23
+ private transitions: Map<string, Map<string, number>> = new Map();
24
+
25
+ addTransition(from: string, to: string): void {
26
+ if (!this.transitions.has(from)) {
27
+ this.transitions.set(from, new Map());
28
+ }
29
+ const map = this.transitions.get(from)!;
30
+ map.set(to, (map.get(to) ?? 0) + 1);
31
+ }
32
+
33
+ probabilities(from: string): Array<{ to: string; probability: number }> {
34
+ const map = this.transitions.get(from);
35
+ if (!map) return [];
36
+ const total = Array.from(map.values()).reduce((sum, val) => sum + val, 0);
37
+ return Array.from(map.entries()).map(([to, count]) => ({
38
+ to,
39
+ probability: total === 0 ? 0 : count / total,
40
+ }));
41
+ }
42
+ }
43
+
44
+ export class KalmanFilter {
45
+ private state: number;
46
+ private uncertainty: number;
47
+ private processNoise: number;
48
+ private measurementNoise: number;
49
+
50
+ constructor(initialState: number, initialUncertainty: number, processNoise: number, measurementNoise: number) {
51
+ this.state = initialState;
52
+ this.uncertainty = initialUncertainty;
53
+ this.processNoise = processNoise;
54
+ this.measurementNoise = measurementNoise;
55
+ }
56
+
57
+ update(measurement: number): void {
58
+ const predictedUncertainty = this.uncertainty + this.processNoise;
59
+ const kalmanGain = predictedUncertainty / (predictedUncertainty + this.measurementNoise);
60
+ this.state = this.state + kalmanGain * (measurement - this.state);
61
+ this.uncertainty = (1 - kalmanGain) * predictedUncertainty;
62
+ }
63
+
64
+ step(): number {
65
+ // Predict next state without measurement
66
+ this.uncertainty += this.processNoise;
67
+ return this.state;
68
+ }
69
+
70
+ predict(steps: number = 1): number {
71
+ // Predict future state
72
+ let state = this.state;
73
+ for (let i = 0; i < steps; i++) {
74
+ state = state; // Simple prediction (no process model)
75
+ }
76
+ return state;
77
+ }
78
+
79
+ forecast(steps: number): { predictions: number[]; uncertainties: number[] } {
80
+ const predictions: number[] = [];
81
+ const uncertainties: number[] = [];
82
+ let state = this.state;
83
+ let uncertainty = this.uncertainty;
84
+
85
+ for (let i = 0; i < steps; i++) {
86
+ uncertainty += this.processNoise;
87
+ predictions.push(state);
88
+ uncertainties.push(uncertainty);
89
+ }
90
+
91
+ return { predictions, uncertainties };
92
+ }
93
+
94
+ getState(): number {
95
+ return this.state;
96
+ }
97
+
98
+ getUncertainty(): number {
99
+ return this.uncertainty;
100
+ }
101
+ }
102
+
103
+ export function bayesianWinProbability(wins: number, losses: number): number {
104
+ const total = wins + losses;
105
+ if (total === 0) return 0.5;
106
+ return wins / total;
107
+ }
108
+
109
+ export function bayesianConquestRate(successes: number, trials: number): number {
110
+ if (trials === 0) return 0;
111
+ return successes / trials;
112
+ }
113
+
114
+ export function bayesianChangepoint(values: number[]): number {
115
+ if (values.length < 2) return 0;
116
+ const mid = Math.floor(values.length / 2);
117
+ return mid;
118
+ }
119
+
120
+ export interface ProbabilitySnapshot {
121
+ probabilities: Array<{ label: string; probability: number }>;
122
+ }
123
+
124
+ export function generateProbabilitySnapshot(labels: string[]): ProbabilitySnapshot {
125
+ const probability = labels.length > 0 ? 1 / labels.length : 0;
126
+ return {
127
+ probabilities: labels.map((label) => ({ label, probability })),
128
+ };
129
+ }
130
+
131
+ // Dirichlet Distribution
132
+ export class DirichletDistribution {
133
+ private alphas: number[];
134
+
135
+ constructor(alphas: number[]) {
136
+ this.alphas = alphas;
137
+ }
138
+
139
+ mean(): number[] {
140
+ const sum = this.alphas.reduce((s, a) => s + a, 0);
141
+ return this.alphas.map(a => sum === 0 ? 0 : a / sum);
142
+ }
143
+ }
144
+
145
+ // Normal Distribution
146
+ export class NormalDistribution {
147
+ private mean: number;
148
+ private variance: number;
149
+
150
+ constructor(mean: number, variance: number) {
151
+ this.mean = mean;
152
+ this.variance = variance;
153
+ }
154
+
155
+ sample(): number {
156
+ // Box-Muller transform
157
+ const u1 = Math.random();
158
+ const u2 = Math.random();
159
+ const z0 = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
160
+ return z0 * Math.sqrt(this.variance) + this.mean;
161
+ }
162
+ }
163
+
164
+ // Poisson Distribution
165
+ export class PoissonDistribution {
166
+ private lambda: number;
167
+
168
+ constructor(lambda: number) {
169
+ this.lambda = lambda;
170
+ }
171
+
172
+ sample(): number {
173
+ let k = 0;
174
+ let p = 1;
175
+ const L = Math.exp(-this.lambda);
176
+ do {
177
+ k++;
178
+ p *= Math.random();
179
+ } while (p > L);
180
+ return k - 1;
181
+ }
182
+ }
183
+
184
+ // Exponential Distribution
185
+ export class ExponentialDistribution {
186
+ private lambda: number;
187
+
188
+ constructor(lambda: number) {
189
+ this.lambda = lambda;
190
+ }
191
+
192
+ sample(): number {
193
+ return -Math.log(1 - Math.random()) / this.lambda;
194
+ }
195
+ }
196
+
197
+ // Hidden Markov Model
198
+ export class HiddenMarkovModel {
199
+ private states: string[];
200
+ private transitions: Map<string, Map<string, number>>;
201
+ private emissions: Map<string, Map<string, number>>;
202
+
203
+ constructor(states: string[]) {
204
+ this.states = states;
205
+ this.transitions = new Map();
206
+ this.emissions = new Map();
207
+ }
208
+
209
+ addTransition(from: string, to: string, probability: number): void {
210
+ if (!this.transitions.has(from)) {
211
+ this.transitions.set(from, new Map());
212
+ }
213
+ this.transitions.get(from)!.set(to, probability);
214
+ }
215
+
216
+ addEmission(state: string, observation: string, probability: number): void {
217
+ if (!this.emissions.has(state)) {
218
+ this.emissions.set(state, new Map());
219
+ }
220
+ this.emissions.get(state)!.set(observation, probability);
221
+ }
222
+ }
223
+
224
+ // Bayesian A/B Test
225
+ export function bayesianABTest(successA: number, trialsA: number, successB: number, trialsB: number): number {
226
+ const probA = trialsA === 0 ? 0.5 : successA / trialsA;
227
+ const probB = trialsB === 0 ? 0.5 : successB / trialsB;
228
+ return probB - probA; // Difference in probabilities
229
+ }
230
+
231
+ // Bayes Factor
232
+ export function bayesFactor(priorOdds: number, likelihoodRatio: number): number {
233
+ return priorOdds * likelihoodRatio;
234
+ }
235
+
236
+ // MAP Estimate (Maximum A Posteriori)
237
+ export function mapEstimate(data: number[], prior: { alpha: number; beta: number }): number {
238
+ const sum = data.reduce((s, x) => s + x, 0);
239
+ const n = data.length;
240
+ const alpha = prior.alpha + sum;
241
+ const beta = prior.beta + n - sum;
242
+ return alpha / (alpha + beta);
243
+ }
244
+
245
+ // Learn Markov Chain from sequence
246
+ export function learnMarkovChain(sequence: string[]): MarkovChain {
247
+ const chain = new MarkovChain();
248
+ for (let i = 0; i < sequence.length - 1; i++) {
249
+ chain.addTransition(sequence[i]!, sequence[i + 1]!);
250
+ }
251
+ return chain;
252
+ }
253
+
254
+ // Bootstrap Confidence Interval
255
+ export function bootstrapConfidenceInterval(data: number[], iterations: number = 1000, confidence: number = 0.95): { lower: number; upper: number } {
256
+ const samples: number[] = [];
257
+ for (let i = 0; i < iterations; i++) {
258
+ const sample: number[] = [];
259
+ for (let j = 0; j < data.length; j++) {
260
+ sample.push(data[Math.floor(Math.random() * data.length)] ?? 0);
261
+ }
262
+ samples.push(sample.reduce((s, x) => s + x, 0) / sample.length);
263
+ }
264
+ samples.sort((a, b) => a - b);
265
+ const lowerIdx = Math.floor((1 - confidence) / 2 * samples.length);
266
+ const upperIdx = Math.floor((1 + confidence) / 2 * samples.length);
267
+ return {
268
+ lower: samples[lowerIdx] ?? 0,
269
+ upper: samples[upperIdx] ?? 0,
270
+ };
271
+ }
272
+
273
+ // Monte Carlo Integration
274
+ export function monteCarloIntegrate(fn: (x: number) => number, a: number, b: number, samples: number = 1000): number {
275
+ let sum = 0;
276
+ for (let i = 0; i < samples; i++) {
277
+ const x = a + Math.random() * (b - a);
278
+ sum += fn(x);
279
+ }
280
+ return ((b - a) * sum) / samples;
281
+ }
282
+
283
+ // Mutual Information
284
+ export function mutualInformation(x: number[], y: number[]): number {
285
+ // Simplified implementation
286
+ if (x.length !== y.length || x.length === 0) return 0;
287
+ const n = x.length;
288
+ const hx = shannonEntropy(x);
289
+ const hy = shannonEntropy(y);
290
+ // Simplified: assume independence for now
291
+ return Math.max(0, hx + hy - (hx + hy) * 0.5);
292
+ }
293
+
294
+ function shannonEntropy(values: number[]): number {
295
+ const total = values.reduce((s, v) => s + Math.abs(v), 0);
296
+ if (total === 0) return 0;
297
+ return values.reduce((sum, val) => {
298
+ const p = Math.abs(val) / total;
299
+ return p === 0 ? sum : sum - p * Math.log2(p);
300
+ }, 0);
301
+ }
302
+
303
+ // Conditional Entropy
304
+ export function conditionalEntropy(x: number[], y: number[]): number {
305
+ if (x.length !== y.length || x.length === 0) return 0;
306
+ // Simplified: return marginal entropy
307
+ return shannonEntropy(x);
308
+ }
309
+
310
+ // Normalized Mutual Information
311
+ export function normalizedMutualInformation(x: number[], y: number[]): number {
312
+ const mi = mutualInformation(x, y);
313
+ const hx = shannonEntropy(x);
314
+ const hy = shannonEntropy(y);
315
+ const denom = Math.sqrt(hx * hy);
316
+ return denom === 0 ? 0 : mi / denom;
317
+ }