@yipe/dice 0.1.1 → 0.1.3

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/dist/index.js CHANGED
@@ -45,20 +45,19 @@ var LRUCache = class {
45
45
  };
46
46
 
47
47
  // src/types.ts
48
- var COMPUTATIONAL_EPS = 1e-40;
49
- var TEST_EPS = 1e-10;
48
+ var EPS = 1e-12;
50
49
 
51
50
  // src/query.ts
52
51
  var _DiceQuery = class _DiceQuery {
53
- constructor(singles, combined) {
52
+ constructor(singles, combined, eps = EPS) {
54
53
  __publicField(this, "singles");
55
54
  __publicField(this, "combined");
56
55
  this.singles = Array.isArray(singles) ? singles : [singles];
57
- const c = combined ?? PMF.convolveMany(this.singles);
58
- this.combined = Math.abs(c.mass() - 1) <= 1e-12 ? c : c.normalize();
59
56
  if (this.singles.some((s) => s === void 0)) {
60
57
  throw new Error("DiceQuery contains undefined singles");
61
58
  }
59
+ const c = combined ?? PMF.convolveMany(this.singles);
60
+ this.combined = Math.abs(c.mass() - 1) <= eps ? c : c.normalize();
62
61
  }
63
62
  /**
64
63
  * Returns the expected damage across all possible outcomes.
@@ -356,23 +355,16 @@ var _DiceQuery = class _DiceQuery {
356
355
  * - "How much damage comes from miss effects (like save-for-half spells)?"
357
356
  */
358
357
  expectedDamageFrom(labels) {
359
- if (typeof labels === "string") {
360
- return this._expectedDamageFromSingle(labels);
361
- }
362
- let totalExpectedDamage = 0;
363
- for (const [, probabilityBin] of this.combined) {
364
- for (const label of labels) {
365
- totalExpectedDamage += probabilityBin.attr?.[label] || 0;
358
+ const wanted = Array.isArray(labels) ? labels : [labels];
359
+ let total = 0;
360
+ for (const single of this.singles) {
361
+ for (const [dmg, bin] of single) {
362
+ let p = 0;
363
+ for (const label of wanted) p += bin.count[label] ?? 0;
364
+ total += dmg * p;
366
365
  }
367
366
  }
368
- return totalExpectedDamage;
369
- }
370
- _expectedDamageFromSingle(label) {
371
- let totalExpectedDamage = 0;
372
- for (const [, probabilityBin] of this.combined) {
373
- totalExpectedDamage += probabilityBin.attr?.[label] || 0;
374
- }
375
- return totalExpectedDamage;
367
+ return total;
376
368
  }
377
369
  /**
378
370
  * Returns damage statistics for scenarios where AT LEAST ONE attack results in
@@ -595,7 +587,7 @@ var _DiceQuery = class _DiceQuery {
595
587
  * query.toStackedChartData(['hit', 'crit'])
596
588
  * // → {labels: [0, 6, 12, ...], datasets: [{label: 'hit', data: [0, 0.03, ...]}, ...]}
597
589
  */
598
- toStackedChartData(labels = [], epsilon = 1e-12) {
590
+ toStackedChartData(labels = [], epsilon = EPS) {
599
591
  const damageValues = this.combined.support();
600
592
  damageValues.map((dmg) => {
601
593
  const bin = this.combined.map.get(dmg);
@@ -1227,7 +1219,7 @@ var _DiceQuery = class _DiceQuery {
1227
1219
  *
1228
1220
  * Returns tuple: [pFirstNonSubset, pFirstSubset, pAnySuccess, pNone]
1229
1221
  */
1230
- firstSuccessSplit(successOutcome, subsetOutcome, eps = COMPUTATIONAL_EPS) {
1222
+ firstSuccessSplit(successOutcome, subsetOutcome, eps = EPS) {
1231
1223
  const pmfs = this.singles;
1232
1224
  if (!pmfs.length) {
1233
1225
  throw new Error("firstSuccessSplitFromPMFs: pmfs must be non-empty");
@@ -1276,15 +1268,14 @@ __publicField(_DiceQuery, "DEFAULT_OUTCOMES", [
1276
1268
  "missNone"
1277
1269
  ]);
1278
1270
  var DiceQuery = _DiceQuery;
1279
- var EPSILON = COMPUTATIONAL_EPS;
1280
1271
  var pmfCache = new LRUCache(1e3);
1281
1272
  var _PMF = class _PMF {
1282
- constructor(map = /* @__PURE__ */ new Map(), epsilon = EPSILON, normalized = false, identifier = `anon#${_PMF.__anonIdCounter++}`, _preservedProvidence = true) {
1273
+ constructor(map = /* @__PURE__ */ new Map(), epsilon = EPS, normalized = false, identifier = `anon#${_PMF.__anonIdCounter++}`, _preservedProvenance = true) {
1283
1274
  this.map = map;
1284
1275
  this.epsilon = epsilon;
1285
1276
  this.normalized = normalized;
1286
1277
  this.identifier = identifier;
1287
- this._preservedProvidence = _preservedProvidence;
1278
+ this._preservedProvenance = _preservedProvenance;
1288
1279
  // Cached computed values
1289
1280
  __publicField(this, "_support");
1290
1281
  __publicField(this, "_min");
@@ -1294,15 +1285,18 @@ var _PMF = class _PMF {
1294
1285
  __publicField(this, "_variance");
1295
1286
  __publicField(this, "_stdev");
1296
1287
  }
1297
- static empty(epsilon = EPSILON, identifier = "empty") {
1288
+ static empty(epsilon = EPS, identifier = "empty") {
1298
1289
  return new _PMF(/* @__PURE__ */ new Map(), epsilon, false, identifier);
1299
1290
  }
1300
1291
  // This has a single bin at value 0, mass of 1
1301
- static zero(epsilon = EPSILON) {
1292
+ static zero(epsilon = EPS) {
1302
1293
  const m = /* @__PURE__ */ new Map();
1303
1294
  m.set(0, { p: 1, count: { miss: 1 }, attr: {} });
1304
1295
  return new _PMF(m, epsilon, false, "zero");
1305
1296
  }
1297
+ static delta(value, epsilon = EPS) {
1298
+ return _PMF.fromMap(/* @__PURE__ */ new Map([[value, 1]]), epsilon);
1299
+ }
1306
1300
  // This creates a single bin at value 0, but with weight 0.
1307
1301
  static emptyMass() {
1308
1302
  return _PMF.zero().scaleMass(0);
@@ -1405,7 +1399,7 @@ var _PMF = class _PMF {
1405
1399
  * @param eps Optional tolerance for floating point rounding
1406
1400
  * @returns A single PMF representing the exclusive mixture of all options.
1407
1401
  */
1408
- static exclusive(options, eps = 1e-12) {
1402
+ static exclusive(options, eps = EPS) {
1409
1403
  const items = options.map(
1410
1404
  (o) => Array.isArray(o) ? { pmf: o[0], weight: o[1] } : o
1411
1405
  );
@@ -1435,8 +1429,7 @@ var _PMF = class _PMF {
1435
1429
  * [pCrit, critPMF],
1436
1430
  * ]);
1437
1431
  */
1438
- static mixN(weights) {
1439
- const eps = 1e-12;
1432
+ static mixN(weights, eps = EPS) {
1440
1433
  const filtered = weights.filter(([w]) => w > eps);
1441
1434
  if (filtered.length === 0) {
1442
1435
  return _PMF.emptyMass();
@@ -1458,17 +1451,23 @@ var _PMF = class _PMF {
1458
1451
  // This is a convenience method for when we use power
1459
1452
  // TODO: It can be smarter in the future, and we can also add it to query
1460
1453
  // That way statistics operations on invalid PMFs can throw an error
1461
- // TODO… how can we detect if manually merging two queries' combined PMFs, as that loses providence?
1462
- setPreservedProvidence(preserved) {
1463
- if (!this._preservedProvidence && preserved) {
1454
+ // TODO… how can we detect if manually merging two queries' combined PMFs, as that loses provenance?
1455
+ setPreservedProvenance(preserved) {
1456
+ if (!this._preservedProvenance && preserved) {
1464
1457
  throw new Error(
1465
- "Preserved providence is already set to false, cannot fix that"
1458
+ "Preserved provenance is already set to false, cannot fix that"
1466
1459
  );
1467
1460
  }
1468
- this._preservedProvidence = preserved;
1461
+ this._preservedProvenance = preserved;
1469
1462
  }
1470
- preservedProvidence() {
1471
- return this._preservedProvidence;
1463
+ preservedProvenance() {
1464
+ return this._preservedProvenance;
1465
+ }
1466
+ getPowerCacheKey(n, eps) {
1467
+ const id = this.identifier;
1468
+ let key = `${id}`;
1469
+ for (let i = 1; i < n; i++) key += `+${id}`;
1470
+ return `${key}@${eps}`;
1472
1471
  }
1473
1472
  /**
1474
1473
  * Efficiently computes this PMF convolved with itself `n` times.
@@ -1486,7 +1485,7 @@ var _PMF = class _PMF {
1486
1485
  }
1487
1486
  if (n === 1) return this;
1488
1487
  const epsilon = eps ?? this.epsilon;
1489
- const key = getPowerCacheKey(this, n, epsilon);
1488
+ const key = this.getPowerCacheKey(n, epsilon);
1490
1489
  {
1491
1490
  const cached = pmfCache?.get(key);
1492
1491
  if (cached) return cached;
@@ -1503,7 +1502,7 @@ var _PMF = class _PMF {
1503
1502
  base = base.convolve(base, epsilon);
1504
1503
  }
1505
1504
  }
1506
- result.setPreservedProvidence(false);
1505
+ result.setPreservedProvenance(false);
1507
1506
  {
1508
1507
  pmfCache?.set(key, result);
1509
1508
  }
@@ -1532,7 +1531,7 @@ var _PMF = class _PMF {
1532
1531
  outcomeMass(outcome) {
1533
1532
  let totalProbabilityMass = 0;
1534
1533
  for (const { p, count } of this.map.values()) {
1535
- totalProbabilityMass += p * count[outcome];
1534
+ totalProbabilityMass += p * (count[outcome] ?? 0);
1536
1535
  }
1537
1536
  return totalProbabilityMass;
1538
1537
  }
@@ -1795,7 +1794,7 @@ var _PMF = class _PMF {
1795
1794
  }
1796
1795
  convolve(other, eps, raw = false) {
1797
1796
  const epsilon = eps ?? this.epsilon;
1798
- const norm = (x) => raw ? x : Math.abs(x.mass() - 1) <= 1e-12 ? x : x.normalize();
1797
+ const norm = (x) => raw ? x : Math.abs(x.mass() - 1) <= epsilon ? x : x.normalize();
1799
1798
  const A0 = norm(this);
1800
1799
  const B0 = norm(other);
1801
1800
  const [A, B] = A0.identifier <= B0.identifier ? [A0, B0] : [B0, A0];
@@ -1830,14 +1829,13 @@ var _PMF = class _PMF {
1830
1829
  epsilon,
1831
1830
  !raw,
1832
1831
  `${A.identifier}${raw ? "*" : "+"}${B.identifier}`
1833
- // ← canonical order
1834
1832
  );
1835
1833
  const mExp = (raw ? A.mass() : 1) * (raw ? B.mass() : 1);
1836
1834
  const mGot = result.mass();
1837
- if (mExp !== 0 && Math.abs(mGot - mExp) > 1e-12) {
1835
+ if (mExp !== 0 && Math.abs(mGot - mExp) > epsilon) {
1838
1836
  result = result.scaleMass(mExp / mGot);
1839
1837
  }
1840
- if (!raw && Math.abs(result.mass() - 1) > 1e-12)
1838
+ if (!raw && Math.abs(result.mass() - 1) > epsilon)
1841
1839
  result = result.normalize();
1842
1840
  pmfCache?.set(cacheKey, result);
1843
1841
  return result;
@@ -1885,7 +1883,7 @@ var _PMF = class _PMF {
1885
1883
  * - Similar PMF lists share common prefixes (A+B, (A+B)+C, etc.)
1886
1884
  * - Order-independent cache keys work better with consistent build patterns
1887
1885
  */
1888
- static convolveMany(pmfList, eps = EPSILON) {
1886
+ static convolveMany(pmfList, eps = EPS) {
1889
1887
  if (pmfList.length === 0) return _PMF.empty(eps);
1890
1888
  if (pmfList.length === 1) return pmfList[0];
1891
1889
  return _PMF.reduceConvolveLeft(pmfList, eps);
@@ -1900,7 +1898,7 @@ var _PMF = class _PMF {
1900
1898
  static fromJSON(jsonData) {
1901
1899
  return new _PMF(
1902
1900
  new Map(jsonData.bins),
1903
- EPSILON,
1901
+ EPS,
1904
1902
  !!jsonData.normalized,
1905
1903
  jsonData.identifier || "fromJSON"
1906
1904
  );
@@ -2046,6 +2044,21 @@ var _PMF = class _PMF {
2046
2044
  }
2047
2045
  return false;
2048
2046
  }
2047
+ tailProbGE(t) {
2048
+ let s = 0;
2049
+ for (const [x, rec] of this) {
2050
+ const p = typeof rec === "number" ? rec : rec.p;
2051
+ if (p > 0 && x >= t) s += p;
2052
+ }
2053
+ return s;
2054
+ }
2055
+ tailProbGT(t) {
2056
+ let s = 0;
2057
+ for (const [x, rec] of this) {
2058
+ if (x > t) s += rec.p;
2059
+ }
2060
+ return s;
2061
+ }
2049
2062
  /**
2050
2063
  * Returns a new PMF containing only bins where the specified outcome has non-zero probability.
2051
2064
  * This creates a marginal distribution for the given outcome type, with probabilities
@@ -2105,6 +2118,66 @@ var _PMF = class _PMF {
2105
2118
  const pNone = 1 - pSpecificSuccess - pGeneralSuccess;
2106
2119
  return { pSpecificSuccess, pGeneralSuccess, pNone, pAny };
2107
2120
  }
2121
+ mapValues(f, eps = EPS, opts) {
2122
+ const rounding = opts?.rounding ?? "none";
2123
+ const preserveCounts = opts?.preserveCounts ?? true;
2124
+ const round = (x) => rounding === "floor" ? Math.floor(x) : rounding === "ceil" ? Math.ceil(x) : rounding === "round" ? Math.round(x) : x;
2125
+ const probs = /* @__PURE__ */ new Map();
2126
+ const counts = /* @__PURE__ */ new Map();
2127
+ for (const [v, rec] of this) {
2128
+ if (Math.abs(rec.p) < eps) continue;
2129
+ const u = round(f(v));
2130
+ probs.set(u, (probs.get(u) ?? 0) + rec.p);
2131
+ if (preserveCounts) {
2132
+ const rec2 = this.map.get(v);
2133
+ const src = typeof rec2 === "number" ? void 0 : rec2?.count;
2134
+ if (src) {
2135
+ const dest = counts.get(u) ?? {};
2136
+ for (const k in src) {
2137
+ dest[k] = (dest[k] ?? 0) + src[k];
2138
+ }
2139
+ counts.set(u, dest);
2140
+ }
2141
+ }
2142
+ }
2143
+ const internal = /* @__PURE__ */ new Map();
2144
+ for (const [u, p] of probs) {
2145
+ internal.set(u, { p, count: counts.get(u) ?? {} });
2146
+ }
2147
+ return _PMF.fromMap(
2148
+ new Map(Array.from(internal, ([u, b]) => [u, b.p])),
2149
+ eps
2150
+ );
2151
+ }
2152
+ static fromMap(m, eps = EPS, { requireIntegerValues = true } = {}) {
2153
+ const filtered = [];
2154
+ for (const [v, p] of m) {
2155
+ if (!Number.isFinite(v) || !Number.isFinite(p)) continue;
2156
+ if (p <= 0 || Math.abs(p) < eps) continue;
2157
+ if (requireIntegerValues && !Number.isInteger(v)) {
2158
+ throw new Error(`fromMap: non-integer outcome ${v}`);
2159
+ }
2160
+ filtered.push([v, p]);
2161
+ }
2162
+ if (filtered.length === 0) {
2163
+ throw new Error("fromMap: empty or invalid input map");
2164
+ }
2165
+ let sum = 0;
2166
+ let c = 0;
2167
+ for (const [, p] of filtered) {
2168
+ const y = p - c;
2169
+ const t = sum + y;
2170
+ c = t - sum - y;
2171
+ sum = t;
2172
+ }
2173
+ if (sum <= 0) throw new Error("pmfFromMap: probabilities sum to 0");
2174
+ filtered.sort((a, b) => a[0] - b[0]);
2175
+ const internal = /* @__PURE__ */ new Map();
2176
+ for (const [v, p] of filtered) {
2177
+ internal.set(v, { p: p / sum, count: {} });
2178
+ }
2179
+ return new _PMF(internal, eps);
2180
+ }
2108
2181
  query() {
2109
2182
  return new DiceQuery(this);
2110
2183
  }
@@ -2112,12 +2185,127 @@ var _PMF = class _PMF {
2112
2185
  // Unique ID generator for anonymous PMFs to avoid cache key collisions
2113
2186
  __publicField(_PMF, "__anonIdCounter", 1);
2114
2187
  var PMF = _PMF;
2115
- function getPowerCacheKey(base, n, eps) {
2116
- const id = base.identifier;
2117
- let key = `${id}`;
2118
- for (let i = 1; i < n; i++) key += `+${id}`;
2119
- return `${key}@${eps}`;
2120
- }
2188
+
2189
+ // src/mixture.ts
2190
+ var Mixture = class _Mixture {
2191
+ constructor(eps = EPS) {
2192
+ __publicField(this, "totals", /* @__PURE__ */ new Map());
2193
+ // raw mass per outcome (pre-normalization)
2194
+ __publicField(this, "labelMass", /* @__PURE__ */ new Map());
2195
+ // raw mass per outcome per label
2196
+ __publicField(this, "eps");
2197
+ this.eps = Number.isFinite(eps) ? eps : EPS;
2198
+ }
2199
+ /** Remove all accumulated state. */
2200
+ clear() {
2201
+ this.totals.clear();
2202
+ this.labelMass.clear();
2203
+ return this;
2204
+ }
2205
+ /** Number of distinct outcome values currently accumulated. */
2206
+ size() {
2207
+ return this.totals.size;
2208
+ }
2209
+ /** Whether a label was ever added. */
2210
+ hasLabel(label) {
2211
+ for (const bag of this.labelMass.values()) if (bag[label]) return true;
2212
+ return false;
2213
+ }
2214
+ /**
2215
+ * Add a labeled component with a mixture weight.
2216
+ * Weight can be any positive finite number. Very small contributions are pruned by eps.
2217
+ */
2218
+ add(label, pmf, weight = 1) {
2219
+ if (!Number.isFinite(weight) || weight <= 0) return this;
2220
+ for (const [v, binOrNumber] of pmf) {
2221
+ const isNumber = typeof binOrNumber === "number";
2222
+ const p = isNumber ? binOrNumber : binOrNumber?.p ?? 0;
2223
+ if (p <= 0) continue;
2224
+ const add = weight * p;
2225
+ if (!Number.isFinite(add) || Math.abs(add) < this.eps) continue;
2226
+ this.totals.set(v, (this.totals.get(v) ?? 0) + add);
2227
+ const bag = this.labelMass.get(v) ?? {};
2228
+ bag[label] = (bag[label] ?? 0) + add;
2229
+ this.labelMass.set(v, bag);
2230
+ }
2231
+ return this;
2232
+ }
2233
+ buildPMF(eps = EPS) {
2234
+ let grand = 0;
2235
+ let c = 0;
2236
+ for (const m of this.totals.values()) {
2237
+ const y = m - c;
2238
+ const t = grand + y;
2239
+ c = t - grand - y;
2240
+ grand = t;
2241
+ }
2242
+ if (!(grand > 0)) throw new Error("LabeledMixture: zero total mass");
2243
+ const internal = /* @__PURE__ */ new Map();
2244
+ for (const [v, m] of this.totals) {
2245
+ if (m <= 0 || Math.abs(m) < this.eps) continue;
2246
+ const count = this.labelMass.get(v) ?? {};
2247
+ internal.set(v, { p: m / grand, count });
2248
+ }
2249
+ return new PMF(internal, eps);
2250
+ }
2251
+ /**
2252
+ * Produce normalized *per-label* PMFs (labels independent).
2253
+ * These are unlabeled PMFs built from the raw mass of that label alone.
2254
+ */
2255
+ byOutcome() {
2256
+ const labels = /* @__PURE__ */ new Set();
2257
+ for (const bag of this.labelMass.values()) {
2258
+ for (const k of Object.keys(bag)) labels.add(k);
2259
+ }
2260
+ const out = {};
2261
+ for (const label of labels) {
2262
+ const m = /* @__PURE__ */ new Map();
2263
+ for (const [v, bag] of this.labelMass) {
2264
+ const w = bag[label];
2265
+ if (w && Math.abs(w) >= this.eps) m.set(v, w);
2266
+ }
2267
+ if (m.size > 0) out[label] = PMF.fromMap(m, this.eps);
2268
+ }
2269
+ return out;
2270
+ }
2271
+ /**
2272
+ * Mixture weights per label, normalized to sum to 1 over labels that appeared.
2273
+ * Uses raw mass before per-outcome normalization.
2274
+ */
2275
+ weights() {
2276
+ const res = {};
2277
+ for (const [, bag] of this.labelMass) {
2278
+ for (const [lab, w] of Object.entries(bag)) {
2279
+ if (!Number.isFinite(w) || w <= 0) continue;
2280
+ res[lab] = (res[lab] ?? 0) + w;
2281
+ }
2282
+ }
2283
+ let total = 0;
2284
+ let c = 0;
2285
+ for (const v of Object.values(res)) {
2286
+ const y = v - c;
2287
+ const t = total + y;
2288
+ c = t - total - y;
2289
+ total = t;
2290
+ }
2291
+ if (total > 0) {
2292
+ for (const k in res) res[k] = res[k] / total;
2293
+ }
2294
+ return res;
2295
+ }
2296
+ toJSON() {
2297
+ return {
2298
+ totals: Array.from(this.totals.entries()).sort((a, b) => a[0] - b[0]),
2299
+ labels: Array.from(this.labelMass.entries()).sort((a, b) => a[0] - b[0]),
2300
+ eps: this.eps
2301
+ };
2302
+ }
2303
+ static mix(items, eps = EPS) {
2304
+ const mix = new _Mixture(eps);
2305
+ for (const [lab, pmf, w] of items) mix.add(lab, pmf, w);
2306
+ return mix.buildPMF();
2307
+ }
2308
+ };
2121
2309
 
2122
2310
  // src/dice.ts
2123
2311
  var Dice = class _Dice {
@@ -2433,7 +2621,7 @@ var Dice = class _Dice {
2433
2621
  * Convert dice to PMF using OutcomeType labels directly from damage distribution.
2434
2622
  * This is much cleaner than the original complex distribution conversion.
2435
2623
  */
2436
- toPMF(numEpsilon = COMPUTATIONAL_EPS) {
2624
+ toPMF(numEpsilon = EPS) {
2437
2625
  const total = this.total();
2438
2626
  if (total === 0) return PMF.empty(numEpsilon);
2439
2627
  this.ensureHitDistribution();
@@ -2612,7 +2800,7 @@ function parseExpression(arr, n) {
2612
2800
  }
2613
2801
  critNorm = crit.total();
2614
2802
  crit = op.call(crit, parseBinaryArgument(arg, arr, n));
2615
- critNorm = critNorm ? crit.total() / critNorm : 1;
2803
+ critNorm = crit && critNorm ? crit.total() / critNorm : 1;
2616
2804
  }
2617
2805
  let save;
2618
2806
  let saveNorm = 1;
@@ -2627,7 +2815,7 @@ function parseExpression(arr, n) {
2627
2815
  saveNorm = save.total();
2628
2816
  finalResult = finalResult.deleteFace(min);
2629
2817
  save = op.call(save, parseBinaryArgument(arg, arr, n));
2630
- saveNorm = saveNorm ? save.total() / saveNorm : 1;
2818
+ saveNorm = save && saveNorm ? save.total() / saveNorm : 1;
2631
2819
  }
2632
2820
  let pc;
2633
2821
  let pcNorm = 1;
@@ -2640,7 +2828,7 @@ function parseExpression(arr, n) {
2640
2828
  const missBefore = pc.total();
2641
2829
  finalResult = finalResult.deleteFace(min);
2642
2830
  pc = op.call(pc, parseBinaryArgument(arg, arr, n)).divideRoundDown(2);
2643
- const missAfter = pc.total();
2831
+ const missAfter = pc ? pc.total() : 0;
2644
2832
  pcNorm = missBefore ? missAfter / missBefore : 1;
2645
2833
  }
2646
2834
  let miss;
@@ -2656,7 +2844,7 @@ function parseExpression(arr, n) {
2656
2844
  missNorm = miss.total();
2657
2845
  finalResult = finalResult.deleteFace(min);
2658
2846
  miss = op.call(miss, parseBinaryArgument(arg, arr, n));
2659
- missNorm = missNorm ? miss.total() / missNorm : 1;
2847
+ missNorm = miss && missNorm ? miss.total() / missNorm : 1;
2660
2848
  }
2661
2849
  let norm = finalResult.total();
2662
2850
  finalResult = op.call(finalResult, arg);
@@ -2946,6 +3134,6 @@ function parseOperation(s) {
2946
3134
  return;
2947
3135
  }
2948
3136
 
2949
- export { COMPUTATIONAL_EPS, Dice, DiceQuery, LRUCache, PMF, TEST_EPS, clearParserCache, getCachingEnabled, getPowerCacheKey, parse, setCachingEnabled };
3137
+ export { DiceQuery, EPS, LRUCache, Mixture, PMF, clearParserCache, getCachingEnabled, parse, pmfCache, setCachingEnabled };
2950
3138
  //# sourceMappingURL=index.js.map
2951
3139
  //# sourceMappingURL=index.js.map