@idlekit/core 0.2.1 → 0.3.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 (82) hide show
  1. package/dist/index.d.ts +12 -0
  2. package/dist/index.js +12 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/scenario/compile.d.ts +25 -0
  5. package/dist/scenario/compile.js +60 -0
  6. package/dist/scenario/compile.js.map +1 -1
  7. package/dist/scenario/scenarioContract.d.ts +152 -0
  8. package/dist/scenario/scenarioContract.js +2815 -0
  9. package/dist/scenario/scenarioContract.js.map +1 -0
  10. package/dist/sim/analysis/analyzers.d.ts +245 -0
  11. package/dist/sim/analysis/analyzers.js +1076 -0
  12. package/dist/sim/analysis/analyzers.js.map +1 -0
  13. package/dist/sim/analysis/contracts.d.ts +147 -0
  14. package/dist/sim/analysis/contracts.js +1230 -0
  15. package/dist/sim/analysis/contracts.js.map +1 -0
  16. package/dist/sim/analysis/distribution.d.ts +199 -0
  17. package/dist/sim/analysis/distribution.js +1246 -0
  18. package/dist/sim/analysis/distribution.js.map +1 -0
  19. package/dist/sim/analysis/eta.d.ts +118 -0
  20. package/dist/sim/analysis/eta.js +968 -70
  21. package/dist/sim/analysis/eta.js.map +1 -1
  22. package/dist/sim/analysis/experiment.d.ts +300 -0
  23. package/dist/sim/analysis/experiment.js +2206 -0
  24. package/dist/sim/analysis/experiment.js.map +1 -0
  25. package/dist/sim/analysis/growth.d.ts +2 -0
  26. package/dist/sim/analysis/growth.js +5 -1
  27. package/dist/sim/analysis/growth.js.map +1 -1
  28. package/dist/sim/analysis/growthFit.d.ts +183 -0
  29. package/dist/sim/analysis/growthFit.js +1798 -0
  30. package/dist/sim/analysis/growthFit.js.map +1 -0
  31. package/dist/sim/analysis/inspect.d.ts +236 -0
  32. package/dist/sim/analysis/inspect.js +388 -0
  33. package/dist/sim/analysis/inspect.js.map +1 -0
  34. package/dist/sim/analysis/metrics.d.ts +91 -0
  35. package/dist/sim/analysis/metrics.js +266 -0
  36. package/dist/sim/analysis/metrics.js.map +1 -0
  37. package/dist/sim/analysis/prestigeCycle.d.ts +6 -0
  38. package/dist/sim/analysis/prestigeCycle.js +10 -1
  39. package/dist/sim/analysis/prestigeCycle.js.map +1 -1
  40. package/dist/sim/analysis/registry.d.ts +41 -0
  41. package/dist/sim/analysis/registry.js +54 -0
  42. package/dist/sim/analysis/registry.js.map +1 -0
  43. package/dist/sim/analysis/resetActions.d.ts +7 -0
  44. package/dist/sim/analysis/resetActions.js +45 -0
  45. package/dist/sim/analysis/resetActions.js.map +1 -0
  46. package/dist/sim/analysis/resetCycle.d.ts +175 -0
  47. package/dist/sim/analysis/resetCycle.js +1009 -0
  48. package/dist/sim/analysis/resetCycle.js.map +1 -0
  49. package/dist/sim/analysis/resetOptimization.d.ts +198 -0
  50. package/dist/sim/analysis/resetOptimization.js +2187 -0
  51. package/dist/sim/analysis/resetOptimization.js.map +1 -0
  52. package/dist/sim/analysis/sensitivity.d.ts +73 -0
  53. package/dist/sim/analysis/sensitivity.js +160 -0
  54. package/dist/sim/analysis/sensitivity.js.map +1 -0
  55. package/dist/sim/monteCarlo.d.ts +12 -0
  56. package/dist/sim/monteCarlo.js +33 -4
  57. package/dist/sim/monteCarlo.js.map +1 -1
  58. package/dist/sim/observation.d.ts +22 -0
  59. package/dist/sim/observation.js +33 -0
  60. package/dist/sim/observation.js.map +1 -1
  61. package/dist/sim/offline.js +9 -2
  62. package/dist/sim/offline.js.map +1 -1
  63. package/dist/sim/random.d.ts +15 -0
  64. package/dist/sim/random.js +28 -0
  65. package/dist/sim/random.js.map +1 -1
  66. package/dist/sim/session.d.ts +7 -0
  67. package/dist/sim/session.js +13 -0
  68. package/dist/sim/session.js.map +1 -1
  69. package/dist/sim/simulator.js +7 -1
  70. package/dist/sim/simulator.js.map +1 -1
  71. package/dist/sim/step.d.ts +67 -1
  72. package/dist/sim/step.js +100 -18
  73. package/dist/sim/step.js.map +1 -1
  74. package/dist/sim/strategy/opt/freshRun.d.ts +3 -0
  75. package/dist/sim/strategy/opt/freshRun.js +6 -0
  76. package/dist/sim/strategy/opt/freshRun.js.map +1 -0
  77. package/dist/sim/strategy/opt/runner.js +2 -2
  78. package/dist/sim/strategy/opt/runner.js.map +1 -1
  79. package/dist/sim/strategy/planner.d.ts +26 -0
  80. package/dist/sim/strategy/planner.js +30 -0
  81. package/dist/sim/strategy/planner.js.map +1 -1
  82. package/package.json +2 -2
@@ -0,0 +1,1246 @@
1
+ import * as _validateReport_1 from "typia/lib/internal/_validateReport";
2
+ import * as _createStandardSchema_1 from "typia/lib/internal/_createStandardSchema";
3
+ import typia, {} from "typia";
4
+ import { mulberry32, logicalDrawId, logicalDrawSeed } from "../random";
5
+ /**
6
+ * Distribution summary v1.
7
+ * Completion-conditioned quantiles stay labeled. They are not the population quantile.
8
+ * `mean-of-log10` is not an arithmetic mean.
9
+ */
10
+ const DISTRIBUTION_ALGORITHM_VERSION = "distribution-v1";
11
+ const DISTRIBUTION_SCHEMA_ID = "https://idlekit.dev/schemas/distribution.v1.schema.json";
12
+ const QUANTILE_METHOD = "inclusive-floor-index";
13
+ const QUANTILE_METHOD_VERSION = 1;
14
+ /**
15
+ * @evidence docs/requirements/active/simulation-distribution.md#req-pr13-sample-accounting Each sample is observed, censored, failed, or unsupported. A censored tail is not an observed p95.
16
+ * @evidenceReview docs/requirements/active/simulation-distribution.md#req-pr13-sample-accounting #6e4e90e Re-read the section: 90 of 100 complete, the population p95 stays null, and an all-censored run is not mean 0.
17
+ */
18
+ export const distributionSampleAccounting = "observed-censored-failed-unsupported";
19
+ /**
20
+ * @evidence docs/requirements/active/simulation-distribution.md#req-pr13-quantile-convention The default quantile is inclusive floor of (n-1)*q. Keys are canonical, not rounded percents.
21
+ * @evidenceReview docs/requirements/active/simulation-distribution.md#req-pr13-quantile-convention #2413847 Re-read the section: the five-point array hits 1, 3, 4, and 5, an empty sample stays null, and q0.946 does not share a key with q0.954.
22
+ */
23
+ export const distributionQuantileConvention = "inclusive-floor-index-v1";
24
+ /**
25
+ * @evidence docs/requirements/active/simulation-distribution.md#req-pr13-logical-draws Logical draw id and seed ignore worker count and call order. Common random numbers need the execution stream.
26
+ * @evidenceReview docs/requirements/active/simulation-distribution.md#req-pr13-logical-draws #6e3688a Re-read the section: reversed calls keep draw 2, and an unsupported stream refuses pairing.
27
+ */
28
+ export const distributionLogicalDraws = "logical-index-not-worker-slot";
29
+ /**
30
+ * @evidence docs/requirements/active/simulation-distribution.md#req-pr13-empirical-drop A seeded 1% Bernoulli draw is checked inside a fixed tolerance. A deterministic model may report zero variance.
31
+ * @evidenceReview docs/requirements/active/simulation-distribution.md#req-pr13-empirical-drop #466e88b Re-read the section: the rate is not required to equal 0.01 exactly, and identical draws stay zero variance.
32
+ */
33
+ export const distributionEmpiricalDrop = "seeded-bernoulli-tolerance";
34
+ /**
35
+ * @evidence docs/requirements/active/simulation-distribution.md#req-pr13-mean-identity Arithmetic mean, mean of log10, and log10 of the mean are different slots. A missing first visible change is censored.
36
+ * @evidenceReview docs/requirements/active/simulation-distribution.md#req-pr13-mean-identity #5fa6bcc Re-read the section: mean of log10 of 10 and 1000 is 2, and an absent first visible change does not become active time or 0.
37
+ */
38
+ export const distributionMeanIdentity = "arithmetic-mean-log-and-log-mean";
39
+ export function distributionSchemaId() {
40
+ return DISTRIBUTION_SCHEMA_ID;
41
+ }
42
+ export function distributionAlgorithmVersion() {
43
+ return DISTRIBUTION_ALGORITHM_VERSION;
44
+ }
45
+ export class DistributionRejected extends Error {
46
+ constructor(message) {
47
+ super(message);
48
+ this.name = "DistributionRejected";
49
+ }
50
+ }
51
+ /*
52
+ * typia runs on first call, not at module load. A plugin that the bundled CLI loads
53
+ * imports core source without the transform, and a module-load call would throw there.
54
+ */
55
+ let validateDistributionArtifactValidator;
56
+ /**
57
+ * @evidence docs/requirements/active/simulation-distribution.md#req-pr13-quantile-convention The generated validator accepts this artifact.
58
+ * @evidenceReview docs/requirements/active/simulation-distribution.md#req-pr13-quantile-convention #2413847 Re-read the section: the wire object is distribution v1 and the method is inclusive-floor-index.
59
+ */
60
+ export const validateDistributionArtifact = (input) => (validateDistributionArtifactValidator ??= (() => {
61
+ const _ip0 = input => "string" === typeof input["key"];
62
+ const _ip1 = input => "number" === typeof input["q"];
63
+ const _ip2 = input => null === input["value"] || "string" === typeof input["value"];
64
+ const _ve0 = "Readonly<{ schemaVersion: 1; algorithmVersion: \"distribution-v1\"; method: Readonly<{ id: \"inclusive-floor-index\"; version: 1; }>; status: \"ok\" | \"unavailable\" | \"unsupported\"; horizonSec: number | null; ... 7 more ...; warnings: readonly string[]; }>";
65
+ const _ve1 = "Readonly<{ draws: number; observed: number; rightCensored: number; failed: number; unsupported: number; completionRate: number | null; }>";
66
+ const _ve2 = "Readonly<{ key: string; q: number; conditioning: \"all-eligible-draws\"; estimator: \"common-horizon-order-statistic\" | \"not-estimated\"; status: \"observed\" | \"outside-observed-range\" | \"unavailable\"; value: string | null; }>";
67
+ const _ve3 = "ReadonlyArray<Readonly<{ key: string; q: number; conditioning: \"completed-only\"; status: \"observed\" | \"unavailable\"; value: string | null; }>>";
68
+ const _ve4 = "Readonly<{ arithmetic: Readonly<{ label: \"arithmetic-mean\" | \"log10-of-arithmetic-mean\" | \"mean-of-log10\"; status: \"observed\" | \"unavailable\" | \"unsupported\"; value: string | null; }>; meanLog10: Readonly<...>; log10OfMean: Readonly<...>; }>";
69
+ const _vp0 = (input, _path, _exceptionable = true) => "string" === typeof input["key"] || _report(_exceptionable, {
70
+ path: _path + ".key",
71
+ expected: "string",
72
+ value: input["key"]
73
+ });
74
+ const _vp1 = (input, _path, _exceptionable = true) => "number" === typeof input["q"] || _report(_exceptionable, {
75
+ path: _path + ".q",
76
+ expected: "number",
77
+ value: input["q"]
78
+ });
79
+ const _vp2 = (input, _path, _exceptionable = true) => null === input["value"] || "string" === typeof input["value"] || _report(_exceptionable, {
80
+ path: _path + ".value",
81
+ expected: "(null | string)",
82
+ value: input["value"]
83
+ });
84
+ const _ve5 = "Readonly<{ label: \"arithmetic-mean\" | \"log10-of-arithmetic-mean\" | \"mean-of-log10\"; status: \"observed\" | \"unavailable\" | \"unsupported\"; value: string | null; }>";
85
+ const _io0 = input => 1 === input.schemaVersion && "distribution-v1" === input.algorithmVersion && ("object" === typeof input.method && null !== input.method && _io1(input.method)) && ("ok" === input.status || "unavailable" === input.status || "unsupported" === input.status) && (null === input.horizonSec || "number" === typeof input.horizonSec) && ("object" === typeof input.accounting && null !== input.accounting && _io2(input.accounting)) && (Array.isArray(input.seeds) && input.seeds.every(elem => "object" === typeof elem && null !== elem && _io3(elem))) && ("object" === typeof input.uncertainty && null !== input.uncertainty && _io4(input.uncertainty)) && ("object" === typeof input.population && null !== input.population && _io5(input.population)) && (Array.isArray(input.conditional) && input.conditional.every(elem => "object" === typeof elem && null !== elem && _io6(elem))) && ("object" === typeof input.means && null !== input.means && _io7(input.means)) && ("object" === typeof input.reduction && null !== input.reduction && _io9(input.reduction)) && (Array.isArray(input.warnings) && input.warnings.every(elem => "string" === typeof elem));
86
+ const _io1 = input => "inclusive-floor-index" === input.id && 1 === input.version;
87
+ const _io2 = input => "number" === typeof input.draws && "number" === typeof input.observed && "number" === typeof input.rightCensored && "number" === typeof input.failed && "number" === typeof input.unsupported && (null === input.completionRate || "number" === typeof input.completionRate);
88
+ const _io3 = input => "string" === typeof input.drawId && "number" === typeof input.drawIndex && "number" === typeof input.seed;
89
+ const _io4 = input => ("mixed" === input.source || "model-strategy-session" === input.source || "monetization" === input.source) && "boolean" === typeof input.zeroVariance && (null === input.variance || "number" === typeof input.variance);
90
+ const _io5 = input => _ip0(input) && _ip1(input) && "all-eligible-draws" === input.conditioning && ("common-horizon-order-statistic" === input.estimator || "not-estimated" === input.estimator) && ("observed" === input.status || "outside-observed-range" === input.status || "unavailable" === input.status) && _ip2(input);
91
+ const _io6 = input => _ip0(input) && _ip1(input) && "completed-only" === input.conditioning && ("observed" === input.status || "unavailable" === input.status) && _ip2(input);
92
+ const _io7 = input => "object" === typeof input.arithmetic && null !== input.arithmetic && _io8(input.arithmetic) && ("object" === typeof input.meanLog10 && null !== input.meanLog10 && _io8(input.meanLog10)) && ("object" === typeof input.log10OfMean && null !== input.log10OfMean && _io8(input.log10OfMean));
93
+ const _io8 = input => ("arithmetic-mean" === input.label || "log10-of-arithmetic-mean" === input.label || "mean-of-log10" === input.label) && ("observed" === input.status || "unavailable" === input.status || "unsupported" === input.status) && _ip2(input);
94
+ const _io9 = input => "logical-draw-index" === input.order && true === input.workerCountIndependent;
95
+ const _vo0 = (input, _path, _exceptionable = true) => [1 === input.schemaVersion || _report(_exceptionable, {
96
+ path: _path + ".schemaVersion",
97
+ expected: "1",
98
+ value: input.schemaVersion
99
+ }), "distribution-v1" === input.algorithmVersion || _report(_exceptionable, {
100
+ path: _path + ".algorithmVersion",
101
+ expected: "\"distribution-v1\"",
102
+ value: input.algorithmVersion
103
+ }), ("object" === typeof input.method && null !== input.method || _report(_exceptionable, {
104
+ path: _path + ".method",
105
+ expected: "Readonly<{ id: \"inclusive-floor-index\"; version: 1; }>",
106
+ value: input.method
107
+ })) && _vo1(input.method, _path + ".method", true && _exceptionable) || _report(_exceptionable, {
108
+ path: _path + ".method",
109
+ expected: "Readonly<{ id: \"inclusive-floor-index\"; version: 1; }>",
110
+ value: input.method
111
+ }), "ok" === input.status || "unavailable" === input.status || "unsupported" === input.status || _report(_exceptionable, {
112
+ path: _path + ".status",
113
+ expected: "(\"ok\" | \"unavailable\" | \"unsupported\")",
114
+ value: input.status
115
+ }), null === input.horizonSec || "number" === typeof input.horizonSec || _report(_exceptionable, {
116
+ path: _path + ".horizonSec",
117
+ expected: "(null | number)",
118
+ value: input.horizonSec
119
+ }), ("object" === typeof input.accounting && null !== input.accounting || _report(_exceptionable, {
120
+ path: _path + ".accounting",
121
+ expected: _ve1,
122
+ value: input.accounting
123
+ })) && _vo2(input.accounting, _path + ".accounting", true && _exceptionable) || _report(_exceptionable, {
124
+ path: _path + ".accounting",
125
+ expected: _ve1,
126
+ value: input.accounting
127
+ }), (Array.isArray(input.seeds) || _report(_exceptionable, {
128
+ path: _path + ".seeds",
129
+ expected: "ReadonlyArray<Readonly<{ drawId: string; drawIndex: number; seed: number; }>>",
130
+ value: input.seeds
131
+ })) && input.seeds.map((elem, _index4) => ("object" === typeof elem && null !== elem || _report(_exceptionable, {
132
+ path: _path + ".seeds[" + _index4 + "]",
133
+ expected: "Readonly<{ drawId: string; drawIndex: number; seed: number; }>",
134
+ value: elem
135
+ })) && _vo3(elem, _path + ".seeds[" + _index4 + "]", true && _exceptionable) || _report(_exceptionable, {
136
+ path: _path + ".seeds[" + _index4 + "]",
137
+ expected: "Readonly<{ drawId: string; drawIndex: number; seed: number; }>",
138
+ value: elem
139
+ })).every(flag => flag) || _report(_exceptionable, {
140
+ path: _path + ".seeds",
141
+ expected: "ReadonlyArray<Readonly<{ drawId: string; drawIndex: number; seed: number; }>>",
142
+ value: input.seeds
143
+ }), ("object" === typeof input.uncertainty && null !== input.uncertainty || _report(_exceptionable, {
144
+ path: _path + ".uncertainty",
145
+ expected: "Readonly<{ source: DistributionUncertaintySource; zeroVariance: boolean; variance: number | null; }>",
146
+ value: input.uncertainty
147
+ })) && _vo4(input.uncertainty, _path + ".uncertainty", true && _exceptionable) || _report(_exceptionable, {
148
+ path: _path + ".uncertainty",
149
+ expected: "Readonly<{ source: DistributionUncertaintySource; zeroVariance: boolean; variance: number | null; }>",
150
+ value: input.uncertainty
151
+ }), ("object" === typeof input.population && null !== input.population || _report(_exceptionable, {
152
+ path: _path + ".population",
153
+ expected: _ve2,
154
+ value: input.population
155
+ })) && _vo5(input.population, _path + ".population", true && _exceptionable) || _report(_exceptionable, {
156
+ path: _path + ".population",
157
+ expected: _ve2,
158
+ value: input.population
159
+ }), (Array.isArray(input.conditional) || _report(_exceptionable, {
160
+ path: _path + ".conditional",
161
+ expected: _ve3,
162
+ value: input.conditional
163
+ })) && input.conditional.map((elem, _index5) => ("object" === typeof elem && null !== elem || _report(_exceptionable, {
164
+ path: _path + ".conditional[" + _index5 + "]",
165
+ expected: "Readonly<{ key: string; q: number; conditioning: \"completed-only\"; status: \"observed\" | \"unavailable\"; value: string | null; }>",
166
+ value: elem
167
+ })) && _vo6(elem, _path + ".conditional[" + _index5 + "]", true && _exceptionable) || _report(_exceptionable, {
168
+ path: _path + ".conditional[" + _index5 + "]",
169
+ expected: "Readonly<{ key: string; q: number; conditioning: \"completed-only\"; status: \"observed\" | \"unavailable\"; value: string | null; }>",
170
+ value: elem
171
+ })).every(flag => flag) || _report(_exceptionable, {
172
+ path: _path + ".conditional",
173
+ expected: _ve3,
174
+ value: input.conditional
175
+ }), ("object" === typeof input.means && null !== input.means || _report(_exceptionable, {
176
+ path: _path + ".means",
177
+ expected: _ve4,
178
+ value: input.means
179
+ })) && _vo7(input.means, _path + ".means", true && _exceptionable) || _report(_exceptionable, {
180
+ path: _path + ".means",
181
+ expected: _ve4,
182
+ value: input.means
183
+ }), ("object" === typeof input.reduction && null !== input.reduction || _report(_exceptionable, {
184
+ path: _path + ".reduction",
185
+ expected: "Readonly<{ order: \"logical-draw-index\"; workerCountIndependent: true; }>",
186
+ value: input.reduction
187
+ })) && _vo9(input.reduction, _path + ".reduction", true && _exceptionable) || _report(_exceptionable, {
188
+ path: _path + ".reduction",
189
+ expected: "Readonly<{ order: \"logical-draw-index\"; workerCountIndependent: true; }>",
190
+ value: input.reduction
191
+ }), (Array.isArray(input.warnings) || _report(_exceptionable, {
192
+ path: _path + ".warnings",
193
+ expected: "ReadonlyArray<string>",
194
+ value: input.warnings
195
+ })) && input.warnings.map((elem, _index6) => "string" === typeof elem || _report(_exceptionable, {
196
+ path: _path + ".warnings[" + _index6 + "]",
197
+ expected: "string",
198
+ value: elem
199
+ })).every(flag => flag) || _report(_exceptionable, {
200
+ path: _path + ".warnings",
201
+ expected: "ReadonlyArray<string>",
202
+ value: input.warnings
203
+ })].every(flag => flag);
204
+ const _vo1 = (input, _path, _exceptionable = true) => ["inclusive-floor-index" === input.id || _report(_exceptionable, {
205
+ path: _path + ".id",
206
+ expected: "\"inclusive-floor-index\"",
207
+ value: input.id
208
+ }), 1 === input.version || _report(_exceptionable, {
209
+ path: _path + ".version",
210
+ expected: "1",
211
+ value: input.version
212
+ })].every(flag => flag);
213
+ const _vo2 = (input, _path, _exceptionable = true) => ["number" === typeof input.draws || _report(_exceptionable, {
214
+ path: _path + ".draws",
215
+ expected: "number",
216
+ value: input.draws
217
+ }), "number" === typeof input.observed || _report(_exceptionable, {
218
+ path: _path + ".observed",
219
+ expected: "number",
220
+ value: input.observed
221
+ }), "number" === typeof input.rightCensored || _report(_exceptionable, {
222
+ path: _path + ".rightCensored",
223
+ expected: "number",
224
+ value: input.rightCensored
225
+ }), "number" === typeof input.failed || _report(_exceptionable, {
226
+ path: _path + ".failed",
227
+ expected: "number",
228
+ value: input.failed
229
+ }), "number" === typeof input.unsupported || _report(_exceptionable, {
230
+ path: _path + ".unsupported",
231
+ expected: "number",
232
+ value: input.unsupported
233
+ }), null === input.completionRate || "number" === typeof input.completionRate || _report(_exceptionable, {
234
+ path: _path + ".completionRate",
235
+ expected: "(null | number)",
236
+ value: input.completionRate
237
+ })].every(flag => flag);
238
+ const _vo3 = (input, _path, _exceptionable = true) => ["string" === typeof input.drawId || _report(_exceptionable, {
239
+ path: _path + ".drawId",
240
+ expected: "string",
241
+ value: input.drawId
242
+ }), "number" === typeof input.drawIndex || _report(_exceptionable, {
243
+ path: _path + ".drawIndex",
244
+ expected: "number",
245
+ value: input.drawIndex
246
+ }), "number" === typeof input.seed || _report(_exceptionable, {
247
+ path: _path + ".seed",
248
+ expected: "number",
249
+ value: input.seed
250
+ })].every(flag => flag);
251
+ const _vo4 = (input, _path, _exceptionable = true) => ["mixed" === input.source || "model-strategy-session" === input.source || "monetization" === input.source || _report(_exceptionable, {
252
+ path: _path + ".source",
253
+ expected: "(\"mixed\" | \"model-strategy-session\" | \"monetization\")",
254
+ value: input.source
255
+ }), "boolean" === typeof input.zeroVariance || _report(_exceptionable, {
256
+ path: _path + ".zeroVariance",
257
+ expected: "boolean",
258
+ value: input.zeroVariance
259
+ }), null === input.variance || "number" === typeof input.variance || _report(_exceptionable, {
260
+ path: _path + ".variance",
261
+ expected: "(null | number)",
262
+ value: input.variance
263
+ })].every(flag => flag);
264
+ const _vo5 = (input, _path, _exceptionable = true) => [_vp0(input, _path, true && _exceptionable), _vp1(input, _path, true && _exceptionable), "all-eligible-draws" === input.conditioning || _report(_exceptionable, {
265
+ path: _path + ".conditioning",
266
+ expected: "\"all-eligible-draws\"",
267
+ value: input.conditioning
268
+ }), "common-horizon-order-statistic" === input.estimator || "not-estimated" === input.estimator || _report(_exceptionable, {
269
+ path: _path + ".estimator",
270
+ expected: "(\"common-horizon-order-statistic\" | \"not-estimated\")",
271
+ value: input.estimator
272
+ }), "observed" === input.status || "outside-observed-range" === input.status || "unavailable" === input.status || _report(_exceptionable, {
273
+ path: _path + ".status",
274
+ expected: "(\"observed\" | \"outside-observed-range\" | \"unavailable\")",
275
+ value: input.status
276
+ }), _vp2(input, _path, true && _exceptionable)].every(flag => flag);
277
+ const _vo6 = (input, _path, _exceptionable = true) => [_vp0(input, _path, true && _exceptionable), _vp1(input, _path, true && _exceptionable), "completed-only" === input.conditioning || _report(_exceptionable, {
278
+ path: _path + ".conditioning",
279
+ expected: "\"completed-only\"",
280
+ value: input.conditioning
281
+ }), "observed" === input.status || "unavailable" === input.status || _report(_exceptionable, {
282
+ path: _path + ".status",
283
+ expected: "(\"observed\" | \"unavailable\")",
284
+ value: input.status
285
+ }), _vp2(input, _path, true && _exceptionable)].every(flag => flag);
286
+ const _vo7 = (input, _path, _exceptionable = true) => [("object" === typeof input.arithmetic && null !== input.arithmetic || _report(_exceptionable, {
287
+ path: _path + ".arithmetic",
288
+ expected: _ve5,
289
+ value: input.arithmetic
290
+ })) && _vo8(input.arithmetic, _path + ".arithmetic", true && _exceptionable) || _report(_exceptionable, {
291
+ path: _path + ".arithmetic",
292
+ expected: _ve5,
293
+ value: input.arithmetic
294
+ }), ("object" === typeof input.meanLog10 && null !== input.meanLog10 || _report(_exceptionable, {
295
+ path: _path + ".meanLog10",
296
+ expected: _ve5,
297
+ value: input.meanLog10
298
+ })) && _vo8(input.meanLog10, _path + ".meanLog10", true && _exceptionable) || _report(_exceptionable, {
299
+ path: _path + ".meanLog10",
300
+ expected: _ve5,
301
+ value: input.meanLog10
302
+ }), ("object" === typeof input.log10OfMean && null !== input.log10OfMean || _report(_exceptionable, {
303
+ path: _path + ".log10OfMean",
304
+ expected: _ve5,
305
+ value: input.log10OfMean
306
+ })) && _vo8(input.log10OfMean, _path + ".log10OfMean", true && _exceptionable) || _report(_exceptionable, {
307
+ path: _path + ".log10OfMean",
308
+ expected: _ve5,
309
+ value: input.log10OfMean
310
+ })].every(flag => flag);
311
+ const _vo8 = (input, _path, _exceptionable = true) => ["arithmetic-mean" === input.label || "log10-of-arithmetic-mean" === input.label || "mean-of-log10" === input.label || _report(_exceptionable, {
312
+ path: _path + ".label",
313
+ expected: "(\"arithmetic-mean\" | \"log10-of-arithmetic-mean\" | \"mean-of-log10\")",
314
+ value: input.label
315
+ }), "observed" === input.status || "unavailable" === input.status || "unsupported" === input.status || _report(_exceptionable, {
316
+ path: _path + ".status",
317
+ expected: "(\"observed\" | \"unavailable\" | \"unsupported\")",
318
+ value: input.status
319
+ }), _vp2(input, _path, true && _exceptionable)].every(flag => flag);
320
+ const _vo9 = (input, _path, _exceptionable = true) => ["logical-draw-index" === input.order || _report(_exceptionable, {
321
+ path: _path + ".order",
322
+ expected: "\"logical-draw-index\"",
323
+ value: input.order
324
+ }), true === input.workerCountIndependent || _report(_exceptionable, {
325
+ path: _path + ".workerCountIndependent",
326
+ expected: "true",
327
+ value: input.workerCountIndependent
328
+ })].every(flag => flag);
329
+ const __is = input => "object" === typeof input && null !== input && _io0(input);
330
+ let errors;
331
+ let _report;
332
+ return _createStandardSchema_1._createStandardSchema(input => {
333
+ if (false === __is(input)) {
334
+ errors = [];
335
+ _report = _validateReport_1._validateReport(errors);
336
+ ((input, _path, _exceptionable = true) => ("object" === typeof input && null !== input || _report(true, {
337
+ path: _path + "",
338
+ expected: _ve0,
339
+ value: input
340
+ })) && _vo0(input, _path + "", true) || _report(true, {
341
+ path: _path + "",
342
+ expected: _ve0,
343
+ value: input
344
+ }))(input, "$input", true);
345
+ const success = 0 === errors.length;
346
+ return success ? {
347
+ success,
348
+ data: input
349
+ } : {
350
+ success,
351
+ errors,
352
+ data: input
353
+ };
354
+ }
355
+ return {
356
+ success: true,
357
+ data: input
358
+ };
359
+ });
360
+ })())(input);
361
+ function buildDistributionJsonSchema() {
362
+ return {
363
+ components: {
364
+ schemas: {
365
+ "ReadonlyschemaVersion1algorithmVersiondistribution-v1methodReadonlyidinclusive-floor-indexversion1statusokunavailableunsupportedhorizonSecnumbernull---7more---warningsreadonlystring": {
366
+ type: "object",
367
+ properties: {
368
+ schemaVersion: {
369
+ "const": 1
370
+ },
371
+ algorithmVersion: {
372
+ "const": "distribution-v1"
373
+ },
374
+ method: {
375
+ $ref: "#/components/schemas/Readonlyidinclusive-floor-indexversion1"
376
+ },
377
+ status: {
378
+ oneOf: [
379
+ {
380
+ "const": "ok"
381
+ },
382
+ {
383
+ "const": "unavailable"
384
+ },
385
+ {
386
+ "const": "unsupported"
387
+ }
388
+ ]
389
+ },
390
+ horizonSec: {
391
+ oneOf: [
392
+ {
393
+ type: "null"
394
+ },
395
+ {
396
+ type: "number"
397
+ }
398
+ ]
399
+ },
400
+ accounting: {
401
+ $ref: "#/components/schemas/ReadonlydrawsnumberobservednumberrightCensorednumberfailednumberunsupportednumbercompletionRatenumbernull"
402
+ },
403
+ seeds: {
404
+ type: "array",
405
+ items: {
406
+ $ref: "#/components/schemas/ReadonlydrawIdstringdrawIndexnumberseednumber"
407
+ }
408
+ },
409
+ uncertainty: {
410
+ $ref: "#/components/schemas/ReadonlysourceDistributionUncertaintySourcezeroVariancebooleanvariancenumbernull"
411
+ },
412
+ population: {
413
+ $ref: "#/components/schemas/Readonlykeystringqnumberconditioningall-eligible-drawsestimatorcommon-horizon-order-statisticnot-estimatedstatusobservedoutside-observed-rangeunavailablevaluestringnull"
414
+ },
415
+ conditional: {
416
+ type: "array",
417
+ items: {
418
+ $ref: "#/components/schemas/Readonlykeystringqnumberconditioningcompleted-onlystatusobservedunavailablevaluestringnull"
419
+ }
420
+ },
421
+ means: {
422
+ $ref: "#/components/schemas/ReadonlyarithmeticReadonlylabelarithmetic-meanlog10-of-arithmetic-meanmean-of-log10statusobservedunavailableunsupportedvaluestringnullmeanLog10Readonly---log10OfMeanReadonly---"
423
+ },
424
+ reduction: {
425
+ $ref: "#/components/schemas/Readonlyorderlogical-draw-indexworkerCountIndependenttrue"
426
+ },
427
+ warnings: {
428
+ type: "array",
429
+ items: {
430
+ type: "string"
431
+ }
432
+ }
433
+ },
434
+ required: [
435
+ "schemaVersion",
436
+ "algorithmVersion",
437
+ "method",
438
+ "status",
439
+ "horizonSec",
440
+ "accounting",
441
+ "seeds",
442
+ "uncertainty",
443
+ "population",
444
+ "conditional",
445
+ "means",
446
+ "reduction",
447
+ "warnings"
448
+ ],
449
+ additionalProperties: false
450
+ },
451
+ "Readonlyidinclusive-floor-indexversion1": {
452
+ type: "object",
453
+ properties: {
454
+ id: {
455
+ "const": "inclusive-floor-index"
456
+ },
457
+ version: {
458
+ "const": 1
459
+ }
460
+ },
461
+ required: [
462
+ "id",
463
+ "version"
464
+ ],
465
+ additionalProperties: false
466
+ },
467
+ ReadonlydrawsnumberobservednumberrightCensorednumberfailednumberunsupportednumbercompletionRatenumbernull: {
468
+ type: "object",
469
+ properties: {
470
+ draws: {
471
+ type: "number"
472
+ },
473
+ observed: {
474
+ type: "number"
475
+ },
476
+ rightCensored: {
477
+ type: "number"
478
+ },
479
+ failed: {
480
+ type: "number"
481
+ },
482
+ unsupported: {
483
+ type: "number"
484
+ },
485
+ completionRate: {
486
+ oneOf: [
487
+ {
488
+ type: "null"
489
+ },
490
+ {
491
+ type: "number"
492
+ }
493
+ ]
494
+ }
495
+ },
496
+ required: [
497
+ "draws",
498
+ "observed",
499
+ "rightCensored",
500
+ "failed",
501
+ "unsupported",
502
+ "completionRate"
503
+ ],
504
+ additionalProperties: false
505
+ },
506
+ ReadonlydrawIdstringdrawIndexnumberseednumber: {
507
+ type: "object",
508
+ properties: {
509
+ drawId: {
510
+ type: "string"
511
+ },
512
+ drawIndex: {
513
+ type: "number"
514
+ },
515
+ seed: {
516
+ type: "number"
517
+ }
518
+ },
519
+ required: [
520
+ "drawId",
521
+ "drawIndex",
522
+ "seed"
523
+ ],
524
+ additionalProperties: false
525
+ },
526
+ ReadonlysourceDistributionUncertaintySourcezeroVariancebooleanvariancenumbernull: {
527
+ type: "object",
528
+ properties: {
529
+ source: {
530
+ $ref: "#/components/schemas/DistributionUncertaintySource"
531
+ },
532
+ zeroVariance: {
533
+ type: "boolean"
534
+ },
535
+ variance: {
536
+ oneOf: [
537
+ {
538
+ type: "null"
539
+ },
540
+ {
541
+ type: "number"
542
+ }
543
+ ]
544
+ }
545
+ },
546
+ required: [
547
+ "source",
548
+ "zeroVariance",
549
+ "variance"
550
+ ],
551
+ additionalProperties: false
552
+ },
553
+ DistributionUncertaintySource: {
554
+ oneOf: [
555
+ {
556
+ "const": "mixed"
557
+ },
558
+ {
559
+ "const": "model-strategy-session"
560
+ },
561
+ {
562
+ "const": "monetization"
563
+ }
564
+ ]
565
+ },
566
+ "Readonlykeystringqnumberconditioningall-eligible-drawsestimatorcommon-horizon-order-statisticnot-estimatedstatusobservedoutside-observed-rangeunavailablevaluestringnull": {
567
+ type: "object",
568
+ properties: {
569
+ key: {
570
+ type: "string"
571
+ },
572
+ q: {
573
+ type: "number"
574
+ },
575
+ conditioning: {
576
+ "const": "all-eligible-draws"
577
+ },
578
+ estimator: {
579
+ oneOf: [
580
+ {
581
+ "const": "common-horizon-order-statistic"
582
+ },
583
+ {
584
+ "const": "not-estimated"
585
+ }
586
+ ]
587
+ },
588
+ status: {
589
+ oneOf: [
590
+ {
591
+ "const": "observed"
592
+ },
593
+ {
594
+ "const": "outside-observed-range"
595
+ },
596
+ {
597
+ "const": "unavailable"
598
+ }
599
+ ]
600
+ },
601
+ value: {
602
+ oneOf: [
603
+ {
604
+ type: "null"
605
+ },
606
+ {
607
+ type: "string"
608
+ }
609
+ ]
610
+ }
611
+ },
612
+ required: [
613
+ "key",
614
+ "q",
615
+ "conditioning",
616
+ "estimator",
617
+ "status",
618
+ "value"
619
+ ],
620
+ additionalProperties: false
621
+ },
622
+ "Readonlykeystringqnumberconditioningcompleted-onlystatusobservedunavailablevaluestringnull": {
623
+ type: "object",
624
+ properties: {
625
+ key: {
626
+ type: "string"
627
+ },
628
+ q: {
629
+ type: "number"
630
+ },
631
+ conditioning: {
632
+ "const": "completed-only"
633
+ },
634
+ status: {
635
+ oneOf: [
636
+ {
637
+ "const": "observed"
638
+ },
639
+ {
640
+ "const": "unavailable"
641
+ }
642
+ ]
643
+ },
644
+ value: {
645
+ oneOf: [
646
+ {
647
+ type: "null"
648
+ },
649
+ {
650
+ type: "string"
651
+ }
652
+ ]
653
+ }
654
+ },
655
+ required: [
656
+ "key",
657
+ "q",
658
+ "conditioning",
659
+ "status",
660
+ "value"
661
+ ],
662
+ additionalProperties: false
663
+ },
664
+ "ReadonlyarithmeticReadonlylabelarithmetic-meanlog10-of-arithmetic-meanmean-of-log10statusobservedunavailableunsupportedvaluestringnullmeanLog10Readonly---log10OfMeanReadonly---": {
665
+ type: "object",
666
+ properties: {
667
+ arithmetic: {
668
+ $ref: "#/components/schemas/Readonlylabelarithmetic-meanlog10-of-arithmetic-meanmean-of-log10statusobservedunavailableunsupportedvaluestringnull"
669
+ },
670
+ meanLog10: {
671
+ $ref: "#/components/schemas/Readonlylabelarithmetic-meanlog10-of-arithmetic-meanmean-of-log10statusobservedunavailableunsupportedvaluestringnull"
672
+ },
673
+ log10OfMean: {
674
+ $ref: "#/components/schemas/Readonlylabelarithmetic-meanlog10-of-arithmetic-meanmean-of-log10statusobservedunavailableunsupportedvaluestringnull"
675
+ }
676
+ },
677
+ required: [
678
+ "arithmetic",
679
+ "meanLog10",
680
+ "log10OfMean"
681
+ ],
682
+ additionalProperties: false
683
+ },
684
+ "Readonlylabelarithmetic-meanlog10-of-arithmetic-meanmean-of-log10statusobservedunavailableunsupportedvaluestringnull": {
685
+ type: "object",
686
+ properties: {
687
+ label: {
688
+ oneOf: [
689
+ {
690
+ "const": "arithmetic-mean"
691
+ },
692
+ {
693
+ "const": "log10-of-arithmetic-mean"
694
+ },
695
+ {
696
+ "const": "mean-of-log10"
697
+ }
698
+ ]
699
+ },
700
+ status: {
701
+ oneOf: [
702
+ {
703
+ "const": "observed"
704
+ },
705
+ {
706
+ "const": "unavailable"
707
+ },
708
+ {
709
+ "const": "unsupported"
710
+ }
711
+ ]
712
+ },
713
+ value: {
714
+ oneOf: [
715
+ {
716
+ type: "null"
717
+ },
718
+ {
719
+ type: "string"
720
+ }
721
+ ]
722
+ }
723
+ },
724
+ required: [
725
+ "label",
726
+ "status",
727
+ "value"
728
+ ],
729
+ additionalProperties: false
730
+ },
731
+ "Readonlyorderlogical-draw-indexworkerCountIndependenttrue": {
732
+ type: "object",
733
+ properties: {
734
+ order: {
735
+ "const": "logical-draw-index"
736
+ },
737
+ workerCountIndependent: {
738
+ "const": true
739
+ }
740
+ },
741
+ required: [
742
+ "order",
743
+ "workerCountIndependent"
744
+ ],
745
+ additionalProperties: false
746
+ }
747
+ }
748
+ },
749
+ schema: {
750
+ type: "object",
751
+ properties: {
752
+ schemaVersion: {
753
+ "const": 1
754
+ },
755
+ algorithmVersion: {
756
+ "const": "distribution-v1"
757
+ },
758
+ method: {
759
+ $ref: "#/components/schemas/Readonlyidinclusive-floor-indexversion1"
760
+ },
761
+ status: {
762
+ oneOf: [
763
+ {
764
+ "const": "ok"
765
+ },
766
+ {
767
+ "const": "unavailable"
768
+ },
769
+ {
770
+ "const": "unsupported"
771
+ }
772
+ ]
773
+ },
774
+ horizonSec: {
775
+ oneOf: [
776
+ {
777
+ type: "null"
778
+ },
779
+ {
780
+ type: "number"
781
+ }
782
+ ]
783
+ },
784
+ accounting: {
785
+ $ref: "#/components/schemas/ReadonlydrawsnumberobservednumberrightCensorednumberfailednumberunsupportednumbercompletionRatenumbernull"
786
+ },
787
+ seeds: {
788
+ type: "array",
789
+ items: {
790
+ $ref: "#/components/schemas/ReadonlydrawIdstringdrawIndexnumberseednumber"
791
+ }
792
+ },
793
+ uncertainty: {
794
+ $ref: "#/components/schemas/ReadonlysourceDistributionUncertaintySourcezeroVariancebooleanvariancenumbernull"
795
+ },
796
+ population: {
797
+ $ref: "#/components/schemas/Readonlykeystringqnumberconditioningall-eligible-drawsestimatorcommon-horizon-order-statisticnot-estimatedstatusobservedoutside-observed-rangeunavailablevaluestringnull"
798
+ },
799
+ conditional: {
800
+ type: "array",
801
+ items: {
802
+ $ref: "#/components/schemas/Readonlykeystringqnumberconditioningcompleted-onlystatusobservedunavailablevaluestringnull"
803
+ }
804
+ },
805
+ means: {
806
+ $ref: "#/components/schemas/ReadonlyarithmeticReadonlylabelarithmetic-meanlog10-of-arithmetic-meanmean-of-log10statusobservedunavailableunsupportedvaluestringnullmeanLog10Readonly---log10OfMeanReadonly---"
807
+ },
808
+ reduction: {
809
+ $ref: "#/components/schemas/Readonlyorderlogical-draw-indexworkerCountIndependenttrue"
810
+ },
811
+ warnings: {
812
+ type: "array",
813
+ items: {
814
+ type: "string"
815
+ }
816
+ }
817
+ },
818
+ required: [
819
+ "schemaVersion",
820
+ "algorithmVersion",
821
+ "method",
822
+ "status",
823
+ "horizonSec",
824
+ "accounting",
825
+ "seeds",
826
+ "uncertainty",
827
+ "population",
828
+ "conditional",
829
+ "means",
830
+ "reduction",
831
+ "warnings"
832
+ ],
833
+ additionalProperties: false
834
+ },
835
+ version: "3.1"
836
+ };
837
+ }
838
+ let distributionJsonSchemaUnit;
839
+ /**
840
+ * @evidence docs/requirements/active/simulation-distribution.md#req-pr13-quantile-convention The structural schema is OpenAPI 3.1 from typia.
841
+ * @evidenceReview docs/requirements/active/simulation-distribution.md#req-pr13-quantile-convention #2413847 Re-read the section: schema version is 3.1.
842
+ */
843
+ export function distributionJsonSchema() {
844
+ distributionJsonSchemaUnit ??= buildDistributionJsonSchema();
845
+ return distributionJsonSchemaUnit;
846
+ }
847
+ const WARNINGS = [
848
+ "a censored sample is not a zero-second completion",
849
+ "a failed or unsupported sample is not an observed improvement",
850
+ "completion-conditioned quantiles are not the population quantile",
851
+ "mean of log10 is not the arithmetic mean, and log10 of the mean is a third metric",
852
+ "this estimator is not a survival model for varying censoring times",
853
+ "logical draw seeds ignore worker count and call order",
854
+ "zero variance is a valid result when the model consumes no randomness",
855
+ ];
856
+ export function canonicalQuantileKey(q) {
857
+ if (typeof q !== "number" || !Number.isFinite(q) || q < 0 || q > 1) {
858
+ throw new DistributionRejected("distribution quantile must be a finite number from 0 to 1");
859
+ }
860
+ return `q${Object.is(q, -0) ? "0" : String(q)}`;
861
+ }
862
+ /**
863
+ * Inclusive floor index on the ordered finite values.
864
+ * Empty input is null, not 0. This is method `inclusive-floor-index` version 1.
865
+ */
866
+ export function quantileInclusiveFloor(values, q) {
867
+ canonicalQuantileKey(q);
868
+ const sorted = values.filter((value) => Number.isFinite(value)).sort((left, right) => left - right);
869
+ if (sorted.length === 0)
870
+ return null;
871
+ const index = Math.max(0, Math.min(sorted.length - 1, Math.floor((sorted.length - 1) * q)));
872
+ return sorted[index] ?? null;
873
+ }
874
+ export function assertDistributionRun(args) {
875
+ if (typeof args.draws !== "number" || !Number.isFinite(args.draws) || args.draws < 1) {
876
+ throw new DistributionRejected("distribution draws must be a finite number >= 1");
877
+ }
878
+ const count = Math.floor(args.draws);
879
+ if (count < 1)
880
+ throw new DistributionRejected("distribution draws must be a finite number >= 1");
881
+ if (typeof args.seed !== "number" || !Number.isFinite(args.seed)) {
882
+ throw new DistributionRejected("distribution seed must be finite");
883
+ }
884
+ if (args.budget !== undefined && (typeof args.budget !== "number" || !Number.isFinite(args.budget) || args.budget < count)) {
885
+ throw new DistributionRejected("distribution budget must be a finite number >= draws");
886
+ }
887
+ quantileKeys(args.quantiles);
888
+ if (args.coupling?.commonRandomNumbers && args.coupling.executionStream !== true) {
889
+ throw new DistributionRejected("common random numbers require the execution stream");
890
+ }
891
+ return count;
892
+ }
893
+ /** One uniform from the logical draw seed. Worker count is not an argument. */
894
+ export function independentBernoulli(seed, drawIndex, probability) {
895
+ if (typeof probability !== "number" || !Number.isFinite(probability) || probability < 0 || probability > 1) {
896
+ throw new DistributionRejected("distribution probability must be a finite number from 0 to 1");
897
+ }
898
+ return mulberry32(logicalDrawSeed(seed, drawIndex))() < probability;
899
+ }
900
+ /**
901
+ * A missing first visible change is censored.
902
+ * It is not replaced with active time or with 0.
903
+ */
904
+ export function firstVisibleDistributionSample(args) {
905
+ if (!Number.isInteger(args.drawIndex) || args.drawIndex < 0) {
906
+ throw new DistributionRejected("distribution drawIndex must be an integer >= 0");
907
+ }
908
+ if (typeof args.seed !== "number" || !Number.isFinite(args.seed)) {
909
+ throw new DistributionRejected("distribution seed must be finite");
910
+ }
911
+ if (args.firstVisibleChangeSec === undefined) {
912
+ return {
913
+ drawIndex: args.drawIndex,
914
+ seed: args.seed,
915
+ status: "censored",
916
+ value: null,
917
+ source: "model-strategy-session",
918
+ };
919
+ }
920
+ // Seconds since the session start. A non-finite or negative time is not a completion.
921
+ if (!Number.isFinite(args.firstVisibleChangeSec) || args.firstVisibleChangeSec < 0) {
922
+ return {
923
+ drawIndex: args.drawIndex,
924
+ seed: args.seed,
925
+ status: "failed",
926
+ value: null,
927
+ source: "model-strategy-session",
928
+ };
929
+ }
930
+ return {
931
+ drawIndex: args.drawIndex,
932
+ seed: args.seed,
933
+ status: "observed",
934
+ value: String(args.firstVisibleChangeSec),
935
+ source: "model-strategy-session",
936
+ };
937
+ }
938
+ function quantileKeys(quantiles) {
939
+ if (quantiles.length === 0)
940
+ throw new DistributionRejected("distribution quantiles must be non-empty");
941
+ const keys = [];
942
+ const seen = new Set();
943
+ for (const q of quantiles) {
944
+ const key = canonicalQuantileKey(q);
945
+ if (seen.has(key))
946
+ throw new DistributionRejected(`distribution quantile key ${key} is duplicated`);
947
+ seen.add(key);
948
+ keys.push(key);
949
+ }
950
+ return keys;
951
+ }
952
+ function formatFinite(value) {
953
+ if (Object.is(value, -0))
954
+ return "0";
955
+ return String(value);
956
+ }
957
+ const DECIMAL_WIRE = /^([+-]?)(\d*)(?:\.(\d*))?(?:e([+-]?\d+))?$/i;
958
+ /** Sign, significant digits, and exponent of a decimal spelling, so two spellings of one value compare equal. */
959
+ function decimalKey(text) {
960
+ const match = DECIMAL_WIRE.exec(text.trim());
961
+ if (!match)
962
+ return null;
963
+ const whole = match[2] ?? "";
964
+ const fraction = match[3] ?? "";
965
+ if (whole === "" && fraction === "")
966
+ return null;
967
+ // Index scans, not /0+$/: a regex anchored at the end rescans a long run of zeros from every start.
968
+ const raw = `${whole}${fraction}`;
969
+ let first = 0;
970
+ while (first < raw.length && raw[first] === "0")
971
+ first += 1;
972
+ let last = raw.length;
973
+ while (last > first && raw[last - 1] === "0")
974
+ last -= 1;
975
+ if (first === last)
976
+ return "0";
977
+ const exponent = Number(match[4] ?? "0") - fraction.length + (raw.length - last);
978
+ return `${match[1] === "-" ? "-" : ""}${raw.slice(first, last)}e${exponent}`;
979
+ }
980
+ /**
981
+ * The observed value as a number, only when the number is the value the caller wrote.
982
+ * A spelling that rounds, such as 9007199254740993, is rejected rather than reported as a neighbour.
983
+ */
984
+ function parseObserved(value) {
985
+ if (value === null)
986
+ return null;
987
+ const parsed = Number(value);
988
+ if (!Number.isFinite(parsed))
989
+ return null;
990
+ const written = decimalKey(value);
991
+ if (written === null)
992
+ return null;
993
+ if (written !== decimalKey(String(parsed)) && !(written === "0" && parsed === 0)) {
994
+ throw new DistributionRejected(`distribution observed value ${value} is not exactly representable`);
995
+ }
996
+ return parsed;
997
+ }
998
+ /** Neumaier compensated sum of value / scale. A large value that cancels does not erase a small residual. */
999
+ function compensatedSum(values, scale) {
1000
+ let sum = 0;
1001
+ let compensation = 0;
1002
+ for (const value of values) {
1003
+ const term = value / scale;
1004
+ const next = sum + term;
1005
+ compensation += Math.abs(sum) >= Math.abs(term) ? sum - next + term : term - next + sum;
1006
+ sum = next;
1007
+ }
1008
+ return sum + compensation;
1009
+ }
1010
+ /** Mean that stays finite when the values are finite and keeps small residuals when large values cancel. */
1011
+ function finiteMean(values) {
1012
+ let smallest = Number.POSITIVE_INFINITY;
1013
+ let largest = Number.NEGATIVE_INFINITY;
1014
+ for (const value of values) {
1015
+ smallest = Math.min(smallest, value);
1016
+ largest = Math.max(largest, value);
1017
+ }
1018
+ // The unscaled sum keeps subnormal terms. Only when it overflows are the terms divided by a power of two large
1019
+ // enough that n of them cannot overflow; that division is exact for every term that is not itself subnormal.
1020
+ const unscaled = compensatedSum(values, 1);
1021
+ const mean = Number.isFinite(unscaled)
1022
+ ? unscaled / values.length
1023
+ : (() => {
1024
+ // Scale only the terms of at least 1, which cannot underflow when halved a few times. Smaller terms keep
1025
+ // their own unscaled sum, so a subnormal residual survives large values that overflow and cancel.
1026
+ const scale = 2 ** (Math.ceil(Math.log2(values.length)) + 1);
1027
+ const large = values.filter((value) => Math.abs(value) >= 1);
1028
+ const small = values.filter((value) => Math.abs(value) < 1);
1029
+ return (compensatedSum(large, scale) / values.length) * scale + compensatedSum(small, 1) / values.length;
1030
+ })();
1031
+ // Every mean lies inside [min, max], so rounding at the number limit cannot step past it.
1032
+ return Math.min(largest, Math.max(smallest, mean));
1033
+ }
1034
+ /** Population variance, rescaled when the squares overflow. Null when the variance itself is not a finite number. */
1035
+ function finiteVariance(values, mean) {
1036
+ const plain = values.reduce((sum, value) => sum + (value - mean) ** 2, 0) / values.length;
1037
+ if (Number.isFinite(plain))
1038
+ return plain;
1039
+ let scale = 0;
1040
+ for (const value of values)
1041
+ scale = Math.max(scale, Math.abs(value - mean));
1042
+ if (!Number.isFinite(scale) || scale === 0)
1043
+ return null;
1044
+ const scaled = values.reduce((sum, value) => sum + ((value - mean) / scale) ** 2, 0) / values.length;
1045
+ const variance = scaled * scale * scale;
1046
+ return Number.isFinite(variance) ? variance : null;
1047
+ }
1048
+ function meanSlot(label, status, value) {
1049
+ return {
1050
+ label,
1051
+ status,
1052
+ value: status === "observed" && value !== null && Number.isFinite(value) ? formatFinite(value) : null,
1053
+ };
1054
+ }
1055
+ function populationAt(observed, censored, probability, horizonSec) {
1056
+ const key = canonicalQuantileKey(probability);
1057
+ if (horizonSec === null) {
1058
+ return {
1059
+ key,
1060
+ q: probability,
1061
+ conditioning: "all-eligible-draws",
1062
+ estimator: "not-estimated",
1063
+ status: "unavailable",
1064
+ value: null,
1065
+ };
1066
+ }
1067
+ const eligible = observed.length + censored;
1068
+ if (eligible === 0 || observed.length === 0) {
1069
+ return {
1070
+ key,
1071
+ q: probability,
1072
+ conditioning: "all-eligible-draws",
1073
+ estimator: "common-horizon-order-statistic",
1074
+ status: "unavailable",
1075
+ value: null,
1076
+ };
1077
+ }
1078
+ const sorted = [...observed].sort((left, right) => left - right);
1079
+ const index = Math.floor((eligible - 1) * probability);
1080
+ if (index >= sorted.length) {
1081
+ return {
1082
+ key,
1083
+ q: probability,
1084
+ conditioning: "all-eligible-draws",
1085
+ estimator: "common-horizon-order-statistic",
1086
+ status: "outside-observed-range",
1087
+ value: null,
1088
+ };
1089
+ }
1090
+ const value = sorted[index];
1091
+ if (value === undefined) {
1092
+ return {
1093
+ key,
1094
+ q: probability,
1095
+ conditioning: "all-eligible-draws",
1096
+ estimator: "common-horizon-order-statistic",
1097
+ status: "unavailable",
1098
+ value: null,
1099
+ };
1100
+ }
1101
+ return {
1102
+ key,
1103
+ q: probability,
1104
+ conditioning: "all-eligible-draws",
1105
+ estimator: "common-horizon-order-statistic",
1106
+ status: "observed",
1107
+ value: formatFinite(value),
1108
+ };
1109
+ }
1110
+ /**
1111
+ * Summarise fixed samples. Empty observed values stay unavailable.
1112
+ * The population p95 uses a common horizon only. It does not copy the conditional quantile.
1113
+ */
1114
+ export function analyzeDistribution(request) {
1115
+ if (request.coupling?.commonRandomNumbers && request.coupling.executionStream !== true) {
1116
+ throw new DistributionRejected("common random numbers require the execution stream");
1117
+ }
1118
+ const keys = quantileKeys(request.quantiles);
1119
+ const probability = request.populationProbability ?? 0.95;
1120
+ canonicalQuantileKey(probability);
1121
+ const horizonSec = request.horizonSec === undefined ? null : request.horizonSec;
1122
+ if (horizonSec !== null && (typeof horizonSec !== "number" || !Number.isFinite(horizonSec) || horizonSec < 0)) {
1123
+ throw new DistributionRejected("distribution horizonSec must be a finite number >= 0 or null");
1124
+ }
1125
+ const ordered = [...request.samples].sort((left, right) => left.drawIndex - right.drawIndex);
1126
+ const seen = new Set();
1127
+ const observed = [];
1128
+ let rightCensored = 0;
1129
+ let failed = 0;
1130
+ let unsupported = 0;
1131
+ const sources = new Set();
1132
+ for (const sample of ordered) {
1133
+ if (!Number.isInteger(sample.drawIndex) || sample.drawIndex < 0) {
1134
+ throw new DistributionRejected("distribution drawIndex must be an integer >= 0");
1135
+ }
1136
+ if (seen.has(sample.drawIndex))
1137
+ throw new DistributionRejected("distribution drawIndex must be unique");
1138
+ seen.add(sample.drawIndex);
1139
+ if (typeof sample.seed !== "number" || !Number.isFinite(sample.seed)) {
1140
+ throw new DistributionRejected("distribution seed must be finite");
1141
+ }
1142
+ // Untyped input can carry any source. The artifact names only the two the contract declares.
1143
+ if (sample.source !== "model-strategy-session" && sample.source !== "monetization") {
1144
+ throw new DistributionRejected("distribution sample source must be model-strategy-session or monetization");
1145
+ }
1146
+ sources.add(sample.source);
1147
+ if (sample.status === "censored") {
1148
+ rightCensored += 1;
1149
+ continue;
1150
+ }
1151
+ if (sample.status === "failed") {
1152
+ failed += 1;
1153
+ continue;
1154
+ }
1155
+ if (sample.status === "unsupported") {
1156
+ unsupported += 1;
1157
+ continue;
1158
+ }
1159
+ // Untyped input can carry any status. Only "observed" is counted as a completion.
1160
+ if (sample.status !== "observed") {
1161
+ throw new DistributionRejected("distribution sample status must be observed, censored, failed, or unsupported");
1162
+ }
1163
+ const parsed = parseObserved(sample.value);
1164
+ if (parsed === null) {
1165
+ failed += 1;
1166
+ continue;
1167
+ }
1168
+ // The common-horizon estimator ranks every censored draw after every completion. A completion past the
1169
+ // horizon would make that rank unknowable.
1170
+ if (horizonSec !== null && parsed > horizonSec) {
1171
+ throw new DistributionRejected("distribution observed value exceeds the common horizonSec");
1172
+ }
1173
+ observed.push(parsed);
1174
+ }
1175
+ const eligible = observed.length + rightCensored;
1176
+ const completionRate = eligible > 0 ? observed.length / eligible : null;
1177
+ const arithmetic = observed.length > 0 ? finiteMean(observed) : null;
1178
+ const identical = observed.every((value) => value === observed[0]);
1179
+ const rawVariance = arithmetic === null ? null : finiteVariance(observed, arithmetic);
1180
+ // Distinct observations whose squared deviations underflow are not zero variance. That variance is reported as
1181
+ // absent, and zeroVariance stays tied to identical draws.
1182
+ const variance = rawVariance === 0 && !identical ? null : rawVariance;
1183
+ const positive = observed.every((value) => value > 0);
1184
+ // The log values are summed with compensation too, so a small one survives large ones that cancel.
1185
+ const meanLog = arithmetic !== null && positive ? compensatedSum(observed.map((value) => Math.log10(value)), 1) / observed.length : null;
1186
+ const logMean = arithmetic !== null && arithmetic > 0 ? Math.log10(arithmetic) : null;
1187
+ const source = sources.size > 1 ? "mixed" : ([...sources][0] ?? "model-strategy-session");
1188
+ const population = populationAt(observed, rightCensored, probability, horizonSec);
1189
+ const conditional = request.quantiles.map((q, index) => {
1190
+ const value = quantileInclusiveFloor(observed, q);
1191
+ return {
1192
+ key: keys[index] ?? canonicalQuantileKey(q),
1193
+ q,
1194
+ conditioning: "completed-only",
1195
+ status: value === null ? "unavailable" : "observed",
1196
+ value: value === null ? null : formatFinite(value),
1197
+ };
1198
+ });
1199
+ const status = ordered.length > 0 && unsupported === ordered.length
1200
+ ? "unsupported"
1201
+ : observed.length === 0
1202
+ ? "unavailable"
1203
+ : "ok";
1204
+ return {
1205
+ schemaVersion: 1,
1206
+ algorithmVersion: DISTRIBUTION_ALGORITHM_VERSION,
1207
+ method: { id: QUANTILE_METHOD, version: QUANTILE_METHOD_VERSION },
1208
+ status,
1209
+ horizonSec,
1210
+ accounting: {
1211
+ draws: ordered.length,
1212
+ observed: observed.length,
1213
+ rightCensored,
1214
+ failed,
1215
+ unsupported,
1216
+ completionRate,
1217
+ },
1218
+ seeds: ordered.map((sample) => ({
1219
+ drawId: logicalDrawId(sample.drawIndex),
1220
+ drawIndex: sample.drawIndex,
1221
+ seed: sample.seed,
1222
+ })),
1223
+ uncertainty: {
1224
+ source,
1225
+ zeroVariance: arithmetic !== null && identical,
1226
+ variance,
1227
+ },
1228
+ population,
1229
+ conditional,
1230
+ means: {
1231
+ arithmetic: meanSlot("arithmetic-mean", arithmetic === null ? "unavailable" : "observed", arithmetic),
1232
+ meanLog10: meanSlot("mean-of-log10", arithmetic === null ? "unavailable" : positive ? "observed" : "unsupported", meanLog),
1233
+ log10OfMean: meanSlot("log10-of-arithmetic-mean", arithmetic === null ? "unavailable" : arithmetic > 0 ? "observed" : "unsupported", logMean),
1234
+ },
1235
+ reduction: { order: "logical-draw-index", workerCountIndependent: true },
1236
+ warnings: arithmetic !== null && variance === null
1237
+ ? [
1238
+ ...WARNINGS,
1239
+ rawVariance === 0
1240
+ ? "variance is below the number range and is not reported"
1241
+ : "variance exceeds the number range and is not reported",
1242
+ ]
1243
+ : WARNINGS,
1244
+ };
1245
+ }
1246
+ //# sourceMappingURL=distribution.js.map