@yipe/dice 0.6.0 → 0.8.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.
@@ -14,6 +14,15 @@ export declare class RollBuilder {
14
14
  protected get lastConfig(): RollConfig;
15
15
  hasHiddenState(): boolean;
16
16
  getSubRollConfigs(): readonly RollConfig[];
17
+ /**
18
+ * A cheap, stable string that FULLY identifies this builder's PMF — used by {@link AttackBuilder.toPMF}
19
+ * to cache resolved attack PMFs across rebuilds without walking the AST via {@link toExpression}. A plain
20
+ * roll is fully determined by its {@link RollConfig} array (count/sides/modifier/reroll/explode/minimum/
21
+ * bestOf/keep/rollType/isSubtraction), so serializing that is sound. Subclasses whose PMF depends on
22
+ * hidden state NOT captured by `subRollConfigs` (half/scale/max/parsed/pooled/composite transforms) return
23
+ * `null` to opt OUT of caching — a conservative miss is always safe; a wrong key would corrupt DPR.
24
+ */
25
+ cacheKey(): string | null;
17
26
  static fromConfig(config: Partial<RollConfig>): RollBuilder;
18
27
  static fromConfigs(configs: Partial<RollConfig>[]): RollBuilder;
19
28
  static fromArgs(...args: any[]): RollBuilder;
@@ -80,6 +89,7 @@ export declare class HalfRollBuilder extends RollBuilder {
80
89
  private readonly innerRoll;
81
90
  constructor(innerRoll: RollBuilder);
82
91
  hasHiddenState(): boolean;
92
+ cacheKey(): string | null;
83
93
  get lastConfig(): RollConfig;
84
94
  getSubRollConfigs(): readonly RollConfig[];
85
95
  toExpression(): string;
@@ -99,6 +109,7 @@ export declare class ScaleRollBuilder extends RollBuilder {
99
109
  private readonly rounding;
100
110
  constructor(innerRoll: RollBuilder, numerator: number, denominator?: number, rounding?: "floor" | "round" | "ceil");
101
111
  hasHiddenState(): boolean;
112
+ cacheKey(): string | null;
102
113
  get lastConfig(): RollConfig;
103
114
  getSubRollConfigs(): readonly RollConfig[];
104
115
  toExpression(): string;
@@ -113,6 +124,7 @@ export declare class MaxOfRollBuilder extends RollBuilder {
113
124
  private readonly diceSides?;
114
125
  constructor(innerRoll: RollBuilder, count: number, diceCount?: number | undefined, diceSides?: number | undefined);
115
126
  hasHiddenState(): boolean;
127
+ cacheKey(): string | null;
116
128
  get lastConfig(): RollConfig;
117
129
  getSubRollConfigs(): readonly RollConfig[];
118
130
  toExpression(): string;
@@ -132,6 +144,7 @@ export declare class AlwaysHitBuilder extends RollBuilder {
132
144
  onHit(count: number, die: RollBuilder, modifier: number): AttackBuilder;
133
145
  onHit(count: number, sides: number, modifier: number): AttackBuilder;
134
146
  get critThreshold(): number;
147
+ cacheKey(): string | null;
135
148
  critOn(critThreshold: number): AlwaysHitBuilder;
136
149
  alwaysCrits(): AlwaysCritBuilder;
137
150
  toExpression(): string;
@@ -155,6 +168,7 @@ export declare class AlwaysCritBuilder extends RollBuilder {
155
168
  onHit(count: number, die: RollBuilder, modifier: number): AttackBuilder;
156
169
  onHit(count: number, sides: number, modifier: number): AttackBuilder;
157
170
  get critThreshold(): number;
171
+ cacheKey(): string | null;
158
172
  critOn(critThreshold: number): AlwaysCritBuilder;
159
173
  toExpression(): string;
160
174
  toPMF(): PMF;
@@ -165,6 +179,7 @@ export declare class ParsedRollBuilder extends RollBuilder {
165
179
  private readonly originalExpression;
166
180
  constructor(expression: string);
167
181
  hasHiddenState(): boolean;
182
+ cacheKey(): string | null;
168
183
  protected create(configs: readonly RollConfig[]): RollBuilder;
169
184
  toPMF(_eps?: number): PMF;
170
185
  toExpression(): string;
@@ -178,6 +193,7 @@ export declare class PooledRollBuilder extends RollBuilder {
178
193
  constructor(baseAST: ExpressionNode, baseExpression: string, configs?: readonly RollConfig[]);
179
194
  protected create(configs: readonly RollConfig[]): PooledRollBuilder;
180
195
  hasHiddenState(): boolean;
196
+ cacheKey(): string | null;
181
197
  d(_sides: number | undefined): RollBuilder;
182
198
  reroll(_value: number): RollBuilder;
183
199
  explode(_count?: number | undefined): RollBuilder;
@@ -1 +1 @@
1
- {"version":3,"file":"roll.d.ts","sourceRoot":"","sources":["../../src/builder/roll.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"roll.d.ts","sourceRoot":"","sources":["../../src/builder/roll.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -203,7 +203,7 @@ var _DiceQuery = class _DiceQuery {
203
203
  * const attack = d20.plus(5).ac(15).onHit(d(2,6).plus(3)).onCrit(d(2,6))
204
204
  * const query = attack.toQuery()
205
205
  * const pmf = query.combinedWithAttribution()
206
- * // Now pmf can be used with toDamageAttributionChartSeries()
206
+ * // Now pmf can be used with attributionByValue() / damageAttributionChartModel()
207
207
  */
208
208
  combinedWithAttribution() {
209
209
  if (this._combinedWithAttr) {
@@ -229,6 +229,14 @@ var _DiceQuery = class _DiceQuery {
229
229
  attributionByValue() {
230
230
  return this.combinedWithAttribution().attributionByValue();
231
231
  }
232
+ /**
233
+ * Full numeric model for the stacked damage-attribution chart. Convenience for
234
+ * `combinedWithAttribution().damageAttributionChartModel(options)`; see
235
+ * {@link PMF.damageAttributionChartModel}.
236
+ */
237
+ damageAttributionChartModel(options) {
238
+ return this.combinedWithAttribution().damageAttributionChartModel(options);
239
+ }
232
240
  /**
233
241
  * How many of the independent single PMFs can produce the given outcome
234
242
  * label. Useful for "all of them succeeded" style probabilities where the
@@ -833,290 +841,6 @@ var _DiceQuery = class _DiceQuery {
833
841
  }));
834
842
  return { labels: damageValues, datasets };
835
843
  }
836
- /**
837
- * Returns pure mathematical data for attribution charts showing outcome contributions.
838
- *
839
- * Automatically discovers all outcome types present in the PMF, applies filtering rules,
840
- * and returns proportional data suitable for stacked visualization.
841
- *
842
- * @param options Configuration options
843
- * @param options.stackOrder Preferred order for outcome types (unknowns placed at end)
844
- * @param options.filterRules Function to determine if outcome should be included for a given damage value
845
- * @param options.asPercentages Whether to return percentages (0-100) or probabilities (0-1)
846
- * @returns Pure data structure with support, outcomes, and proportional data
847
- *
848
- * @example
849
- * query.toAttributionChartSeries()
850
- * // → {support: [0, 6, 12], outcomes: ['hit', 'crit'], data: {hit: [5.2, 8.1, ...], crit: [0, 2.3, ...]}}
851
- */
852
- toAttributionChartSeries(options = {}) {
853
- const {
854
- stackOrder = [
855
- "missNone",
856
- "missDamage",
857
- "saveFail",
858
- "saveHalf",
859
- "pc",
860
- "hit",
861
- "crit"
862
- ],
863
- filterRules = (outcome, damage) => !(outcome === "missNone" && damage !== 0),
864
- asPercentages = true
865
- } = options;
866
- const originalSupport = this.combined.support();
867
- if (originalSupport.length === 0) {
868
- return { support: [], outcomes: [], data: {} };
869
- }
870
- const minDamage = Math.min(...originalSupport);
871
- const maxDamage = Math.max(...originalSupport);
872
- const support = Array.from(
873
- { length: maxDamage - minDamage + 1 },
874
- (_, i) => minDamage + i
875
- );
876
- const allOutcomeTypes = /* @__PURE__ */ new Set();
877
- for (const [, bin] of this.combined.map) {
878
- for (const outcomeType in bin.count) {
879
- if (bin.count[outcomeType] && bin.count[outcomeType] > 0) {
880
- allOutcomeTypes.add(outcomeType);
881
- }
882
- }
883
- }
884
- const existingOutcomes = Array.from(allOutcomeTypes).sort((a, b) => {
885
- const indexA = stackOrder.indexOf(a);
886
- const indexB = stackOrder.indexOf(b);
887
- if (indexA >= 0 && indexB >= 0) return indexA - indexB;
888
- if (indexA >= 0) return -1;
889
- if (indexB >= 0) return 1;
890
- return a.localeCompare(b);
891
- });
892
- if (existingOutcomes.length === 0) {
893
- return { support, outcomes: [], data: {} };
894
- }
895
- const data = {};
896
- for (const outcome of existingOutcomes) {
897
- data[outcome] = support.map((damage) => {
898
- const bin = this.combined.map.get(damage);
899
- if (!bin) return 0;
900
- if (!filterRules(outcome, damage)) {
901
- return 0;
902
- }
903
- const outcomeCount = bin.count[outcome] || 0;
904
- let totalChartableCount = 0;
905
- for (const [outcomeName, count] of Object.entries(bin.count)) {
906
- if (filterRules(outcomeName, damage)) {
907
- totalChartableCount += count || 0;
908
- }
909
- }
910
- if (totalChartableCount === 0) return 0;
911
- const outcomeFraction = outcomeCount / totalChartableCount;
912
- const outcomeProbability = bin.p * outcomeFraction;
913
- return asPercentages ? outcomeProbability * 100 : outcomeProbability;
914
- });
915
- }
916
- return {
917
- support,
918
- outcomes: existingOutcomes,
919
- data
920
- };
921
- }
922
- /**
923
- * Returns pure mathematical data for damage attribution charts showing damage contribution
924
- * from each outcome type at each damage value.
925
- *
926
- * Similar to toAttributionChartSeries() but uses bin.attr (damage attribution) instead of
927
- * bin.count (probability attribution).
928
- *
929
- * @param options Configuration options
930
- * @param options.stackOrder Preferred order for outcome types (unknowns placed at end)
931
- * @param options.filterRules Function to determine if outcome should be included for a given damage value
932
- * @param options.asPercentages Whether to return percentages (0-100) or raw damage values (0+)
933
- * @returns Pure data structure with support, outcomes, and damage attribution data
934
- *
935
- * @example
936
- * query.toDamageAttributionChartSeries()
937
- * // → {support: [0, 6, 12], outcomes: ['hit', 'crit'], data: {hit: [3.2, 5.1, ...], crit: [0, 1.8, ...]}}
938
- */
939
- toDamageAttributionChartSeries(options = {}) {
940
- const {
941
- stackOrder = [
942
- "missNone",
943
- "missDamage",
944
- "saveFail",
945
- "saveHalf",
946
- "pc",
947
- "hit",
948
- "crit"
949
- ],
950
- filterRules = (outcome, damage) => !(outcome === "missNone" && damage !== 0),
951
- asPercentages = true
952
- } = options;
953
- const originalSupport = this.combined.support();
954
- if (originalSupport.length === 0) {
955
- return { support: [], outcomes: [], data: {} };
956
- }
957
- const minDamage = Math.min(...originalSupport);
958
- const maxDamage = Math.max(...originalSupport);
959
- const support = Array.from(
960
- { length: maxDamage - minDamage + 1 },
961
- (_, i) => minDamage + i
962
- );
963
- const allOutcomeTypes = /* @__PURE__ */ new Set();
964
- for (const [, bin] of this.combined.map) {
965
- if (bin.attr) {
966
- for (const outcomeType in bin.attr) {
967
- if (bin.attr[outcomeType] && bin.attr[outcomeType] > 0) {
968
- allOutcomeTypes.add(outcomeType);
969
- }
970
- }
971
- }
972
- }
973
- const existingOutcomes = Array.from(allOutcomeTypes).sort((a, b) => {
974
- const indexA = stackOrder.indexOf(a);
975
- const indexB = stackOrder.indexOf(b);
976
- if (indexA >= 0 && indexB >= 0) return indexA - indexB;
977
- if (indexA >= 0) return -1;
978
- if (indexB >= 0) return 1;
979
- return a.localeCompare(b);
980
- });
981
- if (existingOutcomes.length === 0) {
982
- return { support, outcomes: [], data: {} };
983
- }
984
- const data = {};
985
- for (const outcome of existingOutcomes) {
986
- data[outcome] = support.map((damage) => {
987
- const bin = this.combined.map.get(damage);
988
- if (!bin || !bin.attr) return 0;
989
- if (!filterRules(outcome, damage)) {
990
- return 0;
991
- }
992
- const outcomeDamageAttribution = bin.attr[outcome] || 0;
993
- if (asPercentages) {
994
- let totalDamageAttribution = 0;
995
- for (const [outcomeName, damageAttr] of Object.entries(bin.attr)) {
996
- if (filterRules(outcomeName, damage)) {
997
- totalDamageAttribution += damageAttr || 0;
998
- }
999
- }
1000
- if (totalDamageAttribution === 0) return 0;
1001
- const damagePercentage = outcomeDamageAttribution / totalDamageAttribution * 100;
1002
- return damagePercentage * bin.p * 100;
1003
- } else {
1004
- return outcomeDamageAttribution;
1005
- }
1006
- });
1007
- }
1008
- return {
1009
- support,
1010
- outcomes: existingOutcomes,
1011
- data
1012
- };
1013
- }
1014
- /**
1015
- * Returns pure mathematical data for outcome attribution charts showing which
1016
- * attack outcome combinations can produce each damage value.
1017
- *
1018
- * Unlike toDamageAttributionChartSeries() which tracks damage sources, this tracks
1019
- * outcome combinations - answering "what attack outcomes produced this damage?"
1020
- *
1021
- * @param options Configuration options
1022
- * @param options.stackOrder Preferred order for outcome types (unknowns placed at end)
1023
- * @param options.filterRules Function to determine if outcome should be included for a given damage value
1024
- * @param options.asPercentages Whether to return percentages (0-100) or probabilities (0-1)
1025
- * @returns Pure data structure with support, outcomes, and outcome combination probabilities
1026
- *
1027
- * @example
1028
- * query.toOutcomeAttributionChartSeries()
1029
- * // → {support: [0, 6, 12], outcomes: ['all_miss', 'mixed', 'all_hit'], data: {all_miss: [15, 0, 0], mixed: [60, 80, 20], all_hit: [25, 20, 80]}}
1030
- */
1031
- toOutcomeAttributionChartSeries(options = {}) {
1032
- const {
1033
- stackOrder = [
1034
- "missNone",
1035
- "missDamage",
1036
- "saveFail",
1037
- "saveHalf",
1038
- "pc",
1039
- "hit",
1040
- "crit"
1041
- ],
1042
- filterRules = (outcome, damage) => !(outcome === "missNone" && damage !== 0),
1043
- asPercentages = true
1044
- } = options;
1045
- const originalSupport = this.combined.support();
1046
- if (originalSupport.length === 0) {
1047
- return { support: [], outcomes: [], data: {} };
1048
- }
1049
- const minDamage = Math.min(...originalSupport);
1050
- const maxDamage = Math.max(...originalSupport);
1051
- const support = Array.from(
1052
- { length: maxDamage - minDamage + 1 },
1053
- (_, i) => minDamage + i
1054
- );
1055
- const allOutcomeTypes = /* @__PURE__ */ new Set();
1056
- for (const [, bin] of this.combined.map) {
1057
- for (const outcomeType in bin.count) {
1058
- if (bin.count[outcomeType] && bin.count[outcomeType] > 0) {
1059
- allOutcomeTypes.add(outcomeType);
1060
- }
1061
- }
1062
- }
1063
- const existingOutcomes = Array.from(allOutcomeTypes).sort((a, b) => {
1064
- const indexA = stackOrder.indexOf(a);
1065
- const indexB = stackOrder.indexOf(b);
1066
- if (indexA >= 0 && indexB >= 0) return indexA - indexB;
1067
- if (indexA >= 0) return -1;
1068
- if (indexB >= 0) return 1;
1069
- return a.localeCompare(b);
1070
- });
1071
- if (existingOutcomes.length === 0) {
1072
- return { support, outcomes: [], data: {} };
1073
- }
1074
- const data = {};
1075
- for (const outcome of existingOutcomes) {
1076
- data[outcome] = support.map((damage) => {
1077
- const bin = this.combined.map.get(damage);
1078
- if (!bin) return 0;
1079
- if (!filterRules(outcome, damage)) {
1080
- return 0;
1081
- }
1082
- if (outcome === "missNone") {
1083
- const outcomeCount = bin.count[outcome] || 0;
1084
- if (outcomeCount === 0) return 0;
1085
- if (asPercentages) {
1086
- let totalChartableCount = 0;
1087
- for (const [outcomeName, count] of Object.entries(bin.count)) {
1088
- if (filterRules(outcomeName, damage)) {
1089
- totalChartableCount += count || 0;
1090
- }
1091
- }
1092
- if (totalChartableCount === 0) return 0;
1093
- const outcomeFraction = outcomeCount / totalChartableCount;
1094
- return outcomeFraction * bin.p * 100;
1095
- } else {
1096
- return outcomeCount;
1097
- }
1098
- }
1099
- if (!bin.attr) return 0;
1100
- const outcomeDamageContribution = bin.attr[outcome] || 0;
1101
- if (asPercentages) {
1102
- let totalDamageAttribution = 0;
1103
- for (const [, damageAttr] of Object.entries(bin.attr)) {
1104
- totalDamageAttribution += damageAttr || 0;
1105
- }
1106
- if (totalDamageAttribution === 0) return 0;
1107
- const outcomeFraction = outcomeDamageContribution / totalDamageAttribution;
1108
- return outcomeFraction * bin.p * 100;
1109
- } else {
1110
- return outcomeDamageContribution;
1111
- }
1112
- });
1113
- }
1114
- return {
1115
- support,
1116
- outcomes: existingOutcomes,
1117
- data
1118
- };
1119
- }
1120
844
  /**
1121
845
  * Returns pure mathematical data for cumulative distribution function (CDF).
1122
846
  * Shows P(X ≤ x) - the probability of getting at most x damage.
@@ -2488,6 +2212,149 @@ var _PMF = class _PMF {
2488
2212
  }
2489
2213
  return result;
2490
2214
  }
2215
+ /**
2216
+ * Reversed-convention CCDF percentile markers used by the attribution chart:
2217
+ * for each target probability t, the largest damage x still reached with
2218
+ * P(X ≥ x) > t%, falling back to the smallest/largest support value at the
2219
+ * edges. Ported verbatim from the app so `p80/p50/p20` keep their intentional
2220
+ * reversed meaning (p80 is the low-damage end). Computed on the full, un-binned
2221
+ * support. Assumes a non-empty map.
2222
+ */
2223
+ attributionPercentiles() {
2224
+ const sortedKeys = [...this.map.keys()].sort((a, b) => a - b);
2225
+ const sparseCCDF = [];
2226
+ let cumulativeP = 0;
2227
+ for (let i = sortedKeys.length - 1; i >= 0; i--) {
2228
+ const key = sortedKeys[i];
2229
+ const bin = this.map.get(key);
2230
+ if (!bin) continue;
2231
+ cumulativeP += bin.p;
2232
+ sparseCCDF.unshift({ x: key, y: cumulativeP * 100 });
2233
+ }
2234
+ const findDamageAtProbability = (targetProb) => {
2235
+ for (let i = 0; i < sparseCCDF.length; i++) {
2236
+ if (sparseCCDF[i].y <= targetProb) {
2237
+ return i > 0 ? sparseCCDF[i - 1].x : sparseCCDF[i].x;
2238
+ }
2239
+ }
2240
+ return sparseCCDF[sparseCCDF.length - 1].x;
2241
+ };
2242
+ return {
2243
+ p80: findDamageAtProbability(80),
2244
+ p50: findDamageAtProbability(50),
2245
+ p20: findDamageAtProbability(20)
2246
+ };
2247
+ }
2248
+ /**
2249
+ * Full numeric model for the stacked damage-attribution chart — bar-height
2250
+ * masses, tooltip shares, bucket labels/ranges, percentile markers, and the
2251
+ * mean. The caller only maps these into a rendering format (colors, labels,
2252
+ * axis units); all of the dice-and-probability logic lives here.
2253
+ *
2254
+ * Built split-first-then-bin: the attribution split ({@link attributionByValue})
2255
+ * runs on the un-binned distribution, then the resulting series are coarsened.
2256
+ * {@link rebin} is deliberately *not* used — rebinning first would fold any
2257
+ * sub-`binSize` damage into the damage-0 bucket, which the split then mistakes
2258
+ * for a clean miss and drops.
2259
+ *
2260
+ * @param options.maxBuckets Coarsen to at most this many equal-width buckets
2261
+ * when the integer support is wider (`range > maxBuckets`); omit for a dense,
2262
+ * per-integer model.
2263
+ * @param options.stackOrder Preferred outcome order (defaults to
2264
+ * {@link ALL_OUTCOME_TYPES}); labels outside it sort alphabetically after.
2265
+ * @param options.epsilon Bucket-total floor below which a `shares` entry is 0
2266
+ * (divide-by-~0 guard). Defaults to 1e-9.
2267
+ */
2268
+ damageAttributionChartModel(options = {}) {
2269
+ const { maxBuckets, stackOrder = ALL_OUTCOME_TYPES, epsilon = 1e-9 } = options;
2270
+ const empty = {
2271
+ labels: [],
2272
+ outcomes: [],
2273
+ series: /* @__PURE__ */ new Map(),
2274
+ shares: /* @__PURE__ */ new Map(),
2275
+ totals: [],
2276
+ percentiles: { p80: 0, p50: 0, p20: 0 },
2277
+ mean: 0
2278
+ };
2279
+ if (this.map.size === 0) return empty;
2280
+ const split = this.attributionByValue();
2281
+ const discovered = /* @__PURE__ */ new Set();
2282
+ for (const [, bin] of this.map) {
2283
+ for (const k in bin.count) discovered.add(k);
2284
+ if (bin.attr) for (const k in bin.attr) discovered.add(k);
2285
+ }
2286
+ const outcomes = sortOutcomes([...discovered], stackOrder);
2287
+ const hasAttribution = outcomes.length > 0;
2288
+ let min = Infinity;
2289
+ let max = -Infinity;
2290
+ const widen = (d) => {
2291
+ if (d < min) min = d;
2292
+ if (d > max) max = d;
2293
+ };
2294
+ if (hasAttribution) {
2295
+ for (const s of split.values())
2296
+ for (const [d, m] of s) if (m > 0) widen(d);
2297
+ } else {
2298
+ for (const [d, bin] of this.map) if ((bin.p || 0) > 0) widen(d);
2299
+ }
2300
+ if (max < min) return empty;
2301
+ const range = max - min;
2302
+ const binned = maxBuckets !== void 0 && maxBuckets > 0 && range > maxBuckets;
2303
+ const binSize = binned ? Math.ceil((range + 1) / maxBuckets) : 1;
2304
+ const numBins = binned ? Math.ceil((range + 1) / binSize) : range + 1;
2305
+ const bucketOf = (d) => Math.floor((d - min) / binSize);
2306
+ const labels = [];
2307
+ const binRanges = binned ? [] : void 0;
2308
+ for (let i = 0; i < numBins; i++) {
2309
+ const start = min + i * binSize;
2310
+ labels.push(start);
2311
+ if (binRanges)
2312
+ binRanges.push({ start, end: Math.min(start + binSize - 1, max) });
2313
+ }
2314
+ const series = /* @__PURE__ */ new Map();
2315
+ for (const outcome of outcomes) {
2316
+ const arr = new Array(numBins).fill(0);
2317
+ const s = split.get(outcome);
2318
+ if (s) {
2319
+ for (const [d, m] of s) {
2320
+ const b = bucketOf(d);
2321
+ if (b >= 0 && b < numBins) arr[b] += m;
2322
+ }
2323
+ }
2324
+ series.set(outcome, arr);
2325
+ }
2326
+ const totals = new Array(numBins).fill(0);
2327
+ if (hasAttribution) {
2328
+ for (const arr of series.values())
2329
+ for (let i = 0; i < numBins; i++) totals[i] += arr[i];
2330
+ } else {
2331
+ for (const [d, bin] of this.map) {
2332
+ const p = bin.p || 0;
2333
+ if (p <= 0) continue;
2334
+ const b = bucketOf(d);
2335
+ if (b >= 0 && b < numBins) totals[b] += p;
2336
+ }
2337
+ }
2338
+ const shares = /* @__PURE__ */ new Map();
2339
+ for (const outcome of outcomes) {
2340
+ const arr = series.get(outcome);
2341
+ const sh = new Array(numBins).fill(0);
2342
+ for (let i = 0; i < numBins; i++) {
2343
+ sh[i] = totals[i] > epsilon ? arr[i] / totals[i] : 0;
2344
+ }
2345
+ shares.set(outcome, sh);
2346
+ }
2347
+ return {
2348
+ labels,
2349
+ binRanges,
2350
+ outcomes,
2351
+ series,
2352
+ shares,
2353
+ totals,
2354
+ percentiles: this.attributionPercentiles(),
2355
+ mean: this.mean()
2356
+ };
2357
+ }
2491
2358
  tailProbGE(t) {
2492
2359
  let s = 0;
2493
2360
  for (const [x, bin] of this) {