@jax-data-science/sci-image-visualizer 0.3.2 → 0.4.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 (93) hide show
  1. package/README.md +215 -11
  2. package/esm2022/index.mjs +20 -1
  3. package/esm2022/lib/contracts/display-types.mjs +20 -2
  4. package/esm2022/lib/contracts/image.contract.mjs +1 -1
  5. package/esm2022/lib/contracts/plot-type.mjs +36 -1
  6. package/esm2022/lib/contracts/ports/spatial-data.port.mjs +3 -0
  7. package/esm2022/lib/contracts/spatial-dataset.contract.mjs +45 -0
  8. package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
  9. package/esm2022/lib/implementations/napari-js/napari-axes-labels.mjs +10 -19
  10. package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +71 -7
  11. package/esm2022/lib/implementations/napari-js/napari-scale-bar.mjs +1 -1
  12. package/esm2022/lib/implementations/napari-js/napari-spatial-tooltip.mjs +96 -0
  13. package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +1934 -55
  14. package/esm2022/lib/implementations/napari-js/napari-zoom.mjs +18 -0
  15. package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +5 -3
  16. package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +3 -4
  17. package/esm2022/lib/implementations/osd/osd-zoom.mjs +20 -0
  18. package/esm2022/lib/implementations/plotly/omics-trace-builders.mjs +555 -0
  19. package/esm2022/lib/implementations/plotly/plotly.service.mjs +13 -7
  20. package/esm2022/lib/implementations/spatial-data-http/spatial-data-http.service.mjs +304 -0
  21. package/esm2022/lib/implementations/spatial-data-http/spatial-wire.mjs +179 -0
  22. package/esm2022/lib/routing-visualizer.service.mjs +223 -12
  23. package/esm2022/lib/spatial/embedding-compute.mjs +142 -0
  24. package/esm2022/lib/spatial/gene-search.mjs +77 -0
  25. package/esm2022/lib/spatial/spatial-density.mjs +224 -0
  26. package/esm2022/lib/spatial/spatial-encoding.mjs +232 -0
  27. package/esm2022/lib/spatial/spatial-expression.mjs +257 -0
  28. package/esm2022/lib/spatial/spatial-framing.mjs +60 -0
  29. package/esm2022/lib/spatial/spatial-heatmap.mjs +185 -0
  30. package/esm2022/lib/spatial/spatial-hover.mjs +79 -0
  31. package/esm2022/lib/spatial/spatial-sections.mjs +96 -0
  32. package/esm2022/lib/spatial/spatial-selection.mjs +264 -0
  33. package/esm2022/lib/spatial/spatial-volume-image.mjs +163 -0
  34. package/esm2022/lib/spatial/tsne-worker.mjs +10 -0
  35. package/esm2022/lib/spatial/world-grid.mjs +77 -0
  36. package/esm2022/lib/spatial-controls/spatial-charts/spatial-charts.component.mjs +1278 -0
  37. package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +621 -0
  38. package/esm2022/lib/store/spatial-selection.service.mjs +47 -0
  39. package/esm2022/lib/store/visualizer-store.service.mjs +23 -1
  40. package/esm2022/lib/toolbar/segmentation/cellpose-segmenter.service.mjs +2 -2
  41. package/esm2022/lib/toolbar/segmentation/sam-model-registry.mjs +9 -9
  42. package/esm2022/lib/toolbar/toolbar.component.mjs +30 -7
  43. package/esm2022/lib/visualization.module.mjs +23 -5
  44. package/esm2022/lib/visualizer.component.mjs +283 -22
  45. package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs → jax-data-science-sci-image-visualizer-onnx-sam-session-BwP-A11m.mjs} +6 -4
  46. package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs.map → jax-data-science-sci-image-visualizer-onnx-sam-session-BwP-A11m.mjs.map} +1 -1
  47. package/fesm2022/jax-data-science-sci-image-visualizer-tsne-worker-B_nXDGf0.mjs +12 -0
  48. package/fesm2022/jax-data-science-sci-image-visualizer-tsne-worker-B_nXDGf0.mjs.map +1 -0
  49. package/fesm2022/jax-data-science-sci-image-visualizer.mjs +15282 -7829
  50. package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
  51. package/fesm2022/tsne.worker.js +19091 -0
  52. package/index.d.ts +15 -0
  53. package/lib/contracts/display-types.d.ts +131 -0
  54. package/lib/contracts/image.contract.d.ts +7 -0
  55. package/lib/contracts/plot-type.d.ts +42 -2
  56. package/lib/contracts/ports/spatial-data.port.d.ts +90 -0
  57. package/lib/contracts/spatial-dataset.contract.d.ts +281 -0
  58. package/lib/contracts/visualizer.contract.d.ts +99 -1
  59. package/lib/implementations/napari-js/napari-axes-labels.d.ts +9 -7
  60. package/lib/implementations/napari-js/napari-region-overlay.d.ts +38 -4
  61. package/lib/implementations/napari-js/napari-scale-bar.d.ts +7 -3
  62. package/lib/implementations/napari-js/napari-spatial-tooltip.d.ts +32 -0
  63. package/lib/implementations/napari-js/napari-visualizer.service.d.ts +497 -16
  64. package/lib/implementations/napari-js/napari-zoom.d.ts +15 -0
  65. package/lib/implementations/osd/osd-zoom.d.ts +19 -0
  66. package/lib/implementations/plotly/omics-trace-builders.d.ts +193 -0
  67. package/lib/implementations/plotly/plotly.service.d.ts +9 -0
  68. package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +123 -0
  69. package/lib/implementations/spatial-data-http/spatial-wire.d.ts +115 -0
  70. package/lib/routing-visualizer.service.d.ts +45 -6
  71. package/lib/spatial/embedding-compute.d.ts +73 -0
  72. package/lib/spatial/gene-search.d.ts +48 -0
  73. package/lib/spatial/spatial-density.d.ts +73 -0
  74. package/lib/spatial/spatial-encoding.d.ts +155 -0
  75. package/lib/spatial/spatial-expression.d.ts +144 -0
  76. package/lib/spatial/spatial-framing.d.ts +37 -0
  77. package/lib/spatial/spatial-heatmap.d.ts +111 -0
  78. package/lib/spatial/spatial-hover.d.ts +49 -0
  79. package/lib/spatial/spatial-sections.d.ts +59 -0
  80. package/lib/spatial/spatial-selection.d.ts +113 -0
  81. package/lib/spatial/spatial-volume-image.d.ts +67 -0
  82. package/lib/spatial/tsne-worker.d.ts +7 -0
  83. package/lib/spatial/world-grid.d.ts +58 -0
  84. package/lib/spatial-controls/spatial-charts/spatial-charts.component.d.ts +439 -0
  85. package/lib/spatial-controls/spatial-controls.component.d.ts +257 -0
  86. package/lib/store/spatial-selection.service.d.ts +27 -0
  87. package/lib/store/visualizer-store.service.d.ts +13 -0
  88. package/lib/toolbar/toolbar.component.d.ts +19 -3
  89. package/lib/visualization.module.d.ts +28 -24
  90. package/lib/visualizer.component.d.ts +73 -2
  91. package/package.json +7 -3
  92. package/src/lib/assets/spatial-omics-3d.svg +31 -0
  93. package/src/lib/assets/spatial-omics.svg +27 -0
@@ -0,0 +1,555 @@
1
+ import { NO_CATEGORY } from '../../contracts/spatial-dataset.contract';
2
+ const DEFAULT_COLOR = '#0072B2';
3
+ /** Plotly renders every point of a violin/box; past this we thin the input.
4
+ * 84k cells x a violin per category is otherwise seconds of layout. */
5
+ const MAX_POINTS_PER_TRACE = 20_000;
6
+ /** Finite values only, optionally log-scaled and restricted to a selection. */
7
+ function prepare(input, restrict) {
8
+ const { values, log } = input;
9
+ const out = [];
10
+ for (let i = 0; i < values.length; i++) {
11
+ if (restrict && !restrict[i])
12
+ continue;
13
+ const v = values[i];
14
+ if (!Number.isFinite(v))
15
+ continue;
16
+ out.push(log ? Math.log1p(Math.max(0, v)) : v);
17
+ }
18
+ return out;
19
+ }
20
+ /** Evenly thin a sample to `max` points, preserving its distribution. */
21
+ function thin(values, max = MAX_POINTS_PER_TRACE) {
22
+ if (values.length <= max)
23
+ return values;
24
+ const step = values.length / max;
25
+ const out = [];
26
+ for (let i = 0; i < max; i++)
27
+ out.push(values[Math.floor(i * step)]);
28
+ return out;
29
+ }
30
+ /** Values per category, finite and optionally selection-restricted. */
31
+ function byCategory(input, group, restrict) {
32
+ const buckets = group.categories.map(() => []);
33
+ const { values, log } = input;
34
+ for (let i = 0; i < values.length; i++) {
35
+ if (restrict && !restrict[i])
36
+ continue;
37
+ const code = group.codes[i];
38
+ // NO_CATEGORY has no bucket by definition; an out-of-range code would
39
+ // otherwise silently land in the wrong one.
40
+ if (code === NO_CATEGORY || code >= buckets.length)
41
+ continue;
42
+ const v = values[i];
43
+ if (!Number.isFinite(v))
44
+ continue;
45
+ buckets[code].push(log ? Math.log1p(Math.max(0, v)) : v);
46
+ }
47
+ return buckets;
48
+ }
49
+ /** True when the selection actually restricts anything. */
50
+ function activeSelection(selection) {
51
+ if (!selection)
52
+ return null;
53
+ for (let i = 0; i < selection.length; i++)
54
+ if (selection[i])
55
+ return selection;
56
+ return null;
57
+ }
58
+ /**
59
+ * Traces for one chart kind.
60
+ *
61
+ * - **histogram** — the full distribution, plus an overlaid *Selected* trace
62
+ * when a selection exists, so the two are directly comparable. That
63
+ * comparison is the whole point of linking the chart to the map.
64
+ * - **violin / box** — one per category when grouped, otherwise a single trace.
65
+ * These narrow TO the selection rather than overlaying it: a violin per
66
+ * category per selection state is unreadable.
67
+ */
68
+ export function buildOmicsTraces(kind, input) {
69
+ const selection = activeSelection(input.selection);
70
+ const color = input.color ?? DEFAULT_COLOR;
71
+ if (kind === 'histogram') {
72
+ const traces = [{
73
+ type: 'histogram',
74
+ x: prepare(input, null),
75
+ name: 'All',
76
+ marker: { color, line: { width: 0 } },
77
+ opacity: selection ? 0.55 : 1,
78
+ hovertemplate: '%{y} obs<br>%{x}<extra>All</extra>',
79
+ }];
80
+ if (selection) {
81
+ traces.push({
82
+ type: 'histogram',
83
+ x: prepare(input, selection),
84
+ name: 'Selected',
85
+ marker: { color: '#D55E00', line: { width: 0 } },
86
+ opacity: 0.85,
87
+ hovertemplate: '%{y} obs<br>%{x}<extra>Selected</extra>',
88
+ });
89
+ }
90
+ return traces;
91
+ }
92
+ const type = kind; // 'violin' | 'box'
93
+ const shared = {
94
+ type,
95
+ ...(type === 'violin'
96
+ ? { box: { visible: true }, meanline: { visible: true }, points: false }
97
+ : { boxpoints: false }),
98
+ };
99
+ if (input.group) {
100
+ const buckets = byCategory(input, input.group, selection);
101
+ return buckets
102
+ // Drop empty categories: an empty violin renders as a stray tick with a
103
+ // label, which reads as data.
104
+ .map((values, i) => ({ values, i }))
105
+ .filter((b) => b.values.length > 0)
106
+ .map((b) => ({
107
+ ...shared,
108
+ y: thin(b.values),
109
+ name: input.group.categories[b.i],
110
+ marker: { color: input.group.colors[b.i] ?? color },
111
+ line: { color: input.group.colors[b.i] ?? color },
112
+ }));
113
+ }
114
+ const values = thin(prepare(input, selection));
115
+ if (values.length === 0)
116
+ return [];
117
+ return [{
118
+ ...shared,
119
+ y: values,
120
+ name: selection ? 'Selected' : 'All',
121
+ marker: { color },
122
+ line: { color },
123
+ }];
124
+ }
125
+ /** Layout for a chart kind — axis titles reflect the log toggle. */
126
+ export function omicsLayout(kind, input) {
127
+ const label = input.log ? `log1p(${input.name})` : input.name;
128
+ const base = {
129
+ margin: { t: 10, r: 10, b: 40, l: 52 },
130
+ autosize: true,
131
+ showlegend: kind === 'histogram' && !!activeSelection(input.selection),
132
+ legend: { orientation: 'h', y: 1.12, x: 0 },
133
+ bargap: 0.02,
134
+ hovermode: 'closest',
135
+ };
136
+ if (kind === 'histogram') {
137
+ return {
138
+ ...base,
139
+ barmode: 'overlay',
140
+ xaxis: { title: { text: label } },
141
+ yaxis: { title: { text: 'observations' } },
142
+ };
143
+ }
144
+ return {
145
+ ...base,
146
+ xaxis: { automargin: true },
147
+ yaxis: { title: { text: label }, zeroline: false },
148
+ };
149
+ }
150
+ /** Whether a chart kind needs a categorical grouping to be worth showing. */
151
+ export function benefitsFromGrouping(kind) {
152
+ return kind === 'violin' || kind === 'box';
153
+ }
154
+ /** Categories shown as bars before the tail is folded into one "other". */
155
+ const MAX_COUNT_BARS = 25;
156
+ /** Per-category counts, biggest first, with the tail folded into one bar. */
157
+ export function countByCategory(input) {
158
+ const { codes, categories, colors } = input.group;
159
+ const selection = activeSelection(input.selection);
160
+ const totals = new Float64Array(categories.length);
161
+ const selected = new Float64Array(categories.length);
162
+ for (let i = 0; i < codes.length; i++) {
163
+ const c = codes[i];
164
+ if (c === NO_CATEGORY || c >= categories.length)
165
+ continue;
166
+ totals[c]++;
167
+ if (selection && selection[i])
168
+ selected[c]++;
169
+ }
170
+ const ranked = Array.from(totals, (n, c) => ({ c, n }))
171
+ .filter((r) => r.n > 0)
172
+ .sort((a, b) => b.n - a.n);
173
+ const max = input.max ?? MAX_COUNT_BARS;
174
+ const head = ranked.slice(0, max);
175
+ const tail = ranked.slice(max);
176
+ const out = {
177
+ labels: head.map((r) => categories[r.c]),
178
+ colors: head.map((r) => colors[r.c] ?? DEFAULT_COLOR),
179
+ totals: head.map((r) => r.n),
180
+ selected: head.map((r) => selected[r.c]),
181
+ };
182
+ // The tail is aggregated rather than dropped: 338 subclasses do not fit a
183
+ // readable axis, but silently showing 25 of them would misstate the whole.
184
+ if (tail.length) {
185
+ out.labels.push(`other (${tail.length} categories)`);
186
+ out.colors.push('#9e9e9e');
187
+ out.totals.push(tail.reduce((a, r) => a + r.n, 0));
188
+ out.selected.push(tail.reduce((a, r) => a + selected[r.c], 0));
189
+ }
190
+ return out;
191
+ }
192
+ /**
193
+ * Bars of per-category cell counts — the distribution a CATEGORICAL colour
194
+ * source has.
195
+ *
196
+ * A histogram of a category code would be meaningless (the codes are labels, not
197
+ * magnitudes), but "how many cells per class" is a real question, and the same one
198
+ * the legend implies without answering. Horizontal, because taxonomy labels are
199
+ * long, and sorted, because rank is what the chart is read for.
200
+ */
201
+ export function buildCountTraces(input) {
202
+ const { labels, colors, totals, selected } = countByCategory(input);
203
+ const selection = activeSelection(input.selection);
204
+ // Plotly draws the first category at the BOTTOM of a horizontal axis, so
205
+ // reverse to put the biggest bar at the top where the eye starts.
206
+ const flip = (a) => a.slice().reverse();
207
+ const traces = [{
208
+ type: 'bar',
209
+ orientation: 'h',
210
+ x: flip(totals),
211
+ y: flip(labels),
212
+ name: selection ? 'All' : 'Cells',
213
+ marker: { color: flip(colors) },
214
+ opacity: selection ? 0.45 : 1,
215
+ hovertemplate: '%{x} obs<extra>%{y}</extra>',
216
+ }];
217
+ if (selection) {
218
+ traces.push({
219
+ type: 'bar',
220
+ orientation: 'h',
221
+ x: flip(selected),
222
+ y: flip(labels),
223
+ name: 'Selected',
224
+ marker: { color: flip(colors) },
225
+ hovertemplate: '%{x} obs<extra>%{y} · selected</extra>',
226
+ });
227
+ }
228
+ return traces;
229
+ }
230
+ export function countsLayout(input) {
231
+ const { labels } = countByCategory(input);
232
+ return {
233
+ // Room for the labels, and a height that grows with the bar count so 25
234
+ // categories are not crushed into the same box as 3.
235
+ margin: { t: 10, r: 10, b: 40, l: 8 },
236
+ autosize: true,
237
+ // Overlaid, not stacked: the selected bar reads against the total it came
238
+ // from, which is the comparison being made.
239
+ barmode: 'overlay',
240
+ showlegend: !!activeSelection(input.selection),
241
+ legend: { orientation: 'h', y: 1.12, x: 0 },
242
+ hovermode: 'closest',
243
+ xaxis: { title: { text: 'observations' } },
244
+ yaxis: { automargin: true, ticklabelposition: 'inside', tickfont: { size: 10 } },
245
+ height: Math.max(180, 22 * labels.length + 60),
246
+ };
247
+ }
248
+ /**
249
+ * Past this many categories, one trace each stops being worth it.
250
+ *
251
+ * A trace per category buys a legend whose entries toggle — click one to isolate a population,
252
+ * which is how these plots are actually read. But Plotly holds per-trace state, and a legend of
253
+ * hundreds of entries is unreadable anyway (the ABC atlas has 338 subclasses), so beyond the cap
254
+ * everything goes into ONE trace with a per-point colour: no legend, but it still draws.
255
+ */
256
+ export const EMBEDDING_MAX_LEGEND_CATEGORIES = 40;
257
+ /**
258
+ * Categories past which the legend is not DRAWN, though the traces stay split.
259
+ *
260
+ * A legend is only worth its space if it fits. seqFISH's 22 cell types stack into one tall
261
+ * column in a docked panel, overlapping the axis title and running off the bottom — and
262
+ * the colours already match the map, whose own panel lists them, while hover names the
263
+ * category under the cursor. So past this the plot takes the whole panel and the legend
264
+ * is dropped rather than shown badly.
265
+ */
266
+ export const EMBEDDING_MAX_LEGEND_SHOWN = 10;
267
+ /** Dimming applied to unselected points, matching the map's muted opacity. */
268
+ const EMBEDDING_MUTED_OPACITY = 0.15;
269
+ /**
270
+ * Mix a hex colour toward white by `amount`.
271
+ *
272
+ * How unselected points are dimmed in 3D. `scatter3d` accepts `marker.opacity` only as a
273
+ * SCALAR — a per-point array is silently collapsed to the first value, verified against
274
+ * the bundled Plotly — so the per-point opacity that dims the 2D plot does nothing there,
275
+ * and a selection appeared to highlight nothing in 3D. Colour arrays ARE honoured, so
276
+ * the same effect is produced by mixing toward the paper instead.
277
+ */
278
+ function towardPaper(hex, amount) {
279
+ const clean = hex.replace('#', '');
280
+ const full = clean.length === 3
281
+ ? clean.split('').map((c) => c + c).join('')
282
+ : clean;
283
+ const n = Number.parseInt(full, 16);
284
+ if (!Number.isFinite(n) || full.length !== 6)
285
+ return hex;
286
+ const mix = (c) => Math.round(c + (255 - c) * amount);
287
+ const r = mix((n >> 16) & 0xff);
288
+ const g = mix((n >> 8) & 0xff);
289
+ const b = mix(n & 0xff);
290
+ return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;
291
+ }
292
+ const DEFAULT_EMBEDDING_POINT_SIZE = 4;
293
+ export function buildEmbeddingTraces(input) {
294
+ const { x, y, categories, selection } = input;
295
+ const n = Math.min(x.length, y.length);
296
+ if (n === 0)
297
+ return [];
298
+ const z = input.z;
299
+ // `scattergl` for the plane — 10^4-10^6 points, which the SVG renderer would not
300
+ // survive. `scatter3d` for the cloud: WebGL too, but a different trace type with its
301
+ // own scene, which is why a third dimension cannot just be added to the former. It
302
+ // costs more per point, so it suits this scale and not millions.
303
+ const type = z ? 'scatter3d' : 'scattergl';
304
+ const markerSize = z ? Math.max(1, (input.size ?? DEFAULT_EMBEDDING_POINT_SIZE) - 2)
305
+ : (input.size ?? DEFAULT_EMBEDDING_POINT_SIZE);
306
+ const opacityFor = (i) => !selection || selection[i] ? 1 : EMBEDDING_MUTED_OPACITY;
307
+ const isSelected = (i) => !selection || selection[i] === 1;
308
+ // In 3D the dimming has to be in the COLOUR, since a per-point opacity array is
309
+ // ignored there. Cached per input colour: 19k lookups of the same few categories.
310
+ const dimCache = new Map();
311
+ const shade = (hex, i) => {
312
+ if (!z || isSelected(i))
313
+ return hex;
314
+ const hit = dimCache.get(hex);
315
+ if (hit)
316
+ return hit;
317
+ const dimmed = towardPaper(hex, 1 - EMBEDDING_MUTED_OPACITY);
318
+ dimCache.set(hex, dimmed);
319
+ return dimmed;
320
+ };
321
+ /** Per-point opacity, which only the 2D trace types honour. */
322
+ const opacityArray = (indices) => selection && !z ? indices.map(opacityFor) : undefined;
323
+ if (!categories || categories.names.length === 0) {
324
+ return [{
325
+ type,
326
+ mode: 'markers',
327
+ x: Array.from(x.subarray(0, n)),
328
+ y: Array.from(y.subarray(0, n)),
329
+ ...(z ? { z: Array.from(z.subarray(0, n)) } : {}),
330
+ // The OBSERVATION index per point, carried through so a lasso can be turned back
331
+ // into a selection. Plotly reports a selected point by its position within its
332
+ // trace, which is not the observation index once the points are split by category.
333
+ customdata: Array.from({ length: n }, (_, i) => i),
334
+ marker: {
335
+ size: markerSize,
336
+ // A flat colour normally; per point once a selection has to dim some of them in
337
+ // 3D, where opacity arrays are ignored.
338
+ color: selection && z
339
+ ? Array.from({ length: n }, (_, i) => shade('#4c72b0', i))
340
+ : '#4c72b0',
341
+ ...(opacityArray(Array.from({ length: n }, (_, i) => i))
342
+ ? { opacity: Array.from({ length: n }, (_, i) => opacityFor(i)) }
343
+ : {}),
344
+ },
345
+ hoverinfo: 'none',
346
+ showlegend: false,
347
+ }];
348
+ }
349
+ const { codes, names, colors } = categories;
350
+ if (names.length > EMBEDDING_MAX_LEGEND_CATEGORIES) {
351
+ // One trace, per-point colour. Hover still names the category, which is the part that
352
+ // matters; the legend is what is lost.
353
+ return [{
354
+ type,
355
+ mode: 'markers',
356
+ x: Array.from(x.subarray(0, n)),
357
+ y: Array.from(y.subarray(0, n)),
358
+ ...(z ? { z: Array.from(z.subarray(0, n)) } : {}),
359
+ marker: {
360
+ size: markerSize,
361
+ color: Array.from({ length: n }, (_, i) => shade(colors[codes[i]] ?? '#999999', i)),
362
+ ...(opacityArray(Array.from({ length: n }, (_, i) => i))
363
+ ? { opacity: Array.from({ length: n }, (_, i) => opacityFor(i)) }
364
+ : {}),
365
+ },
366
+ customdata: Array.from({ length: n }, (_, i) => i),
367
+ text: Array.from({ length: n }, (_, i) => names[codes[i]] ?? 'unassigned'),
368
+ hovertemplate: '%{text}<extra></extra>',
369
+ showlegend: false,
370
+ }];
371
+ }
372
+ // One trace per category: the legend entries toggle, so a population can be isolated by
373
+ // clicking rather than by re-colouring the whole plot.
374
+ const buckets = names.map(() => []);
375
+ for (let i = 0; i < n; i++) {
376
+ const c = codes[i];
377
+ // NO_CATEGORY, or a code past the list, is unassigned — dropped rather than drawn in a
378
+ // colour that would read as a population of its own.
379
+ if (c < buckets.length)
380
+ buckets[c].push(i);
381
+ }
382
+ return buckets
383
+ .map((idx, c) => ({ idx, c }))
384
+ .filter(({ idx }) => idx.length > 0)
385
+ .map(({ idx, c }) => ({
386
+ type,
387
+ mode: 'markers',
388
+ name: names[c],
389
+ x: idx.map((i) => x[i]),
390
+ y: idx.map((i) => y[i]),
391
+ ...(z ? { z: idx.map((i) => z[i]) } : {}),
392
+ // This trace's points ARE a subset, so the observation index has to travel with
393
+ // them; `pointIndex` alone would address the wrong cell.
394
+ customdata: idx,
395
+ marker: {
396
+ size: markerSize,
397
+ color: selection && z
398
+ ? idx.map((i) => shade(colors[c] ?? '#999999', i))
399
+ : (colors[c] ?? '#999999'),
400
+ ...(opacityArray(idx) ? { opacity: idx.map(opacityFor) } : {}),
401
+ },
402
+ // Split per category regardless, so hover names it and a future isolate control
403
+ // has something to toggle; only the legend's VISIBILITY depends on the count.
404
+ showlegend: names.length <= EMBEDDING_MAX_LEGEND_SHOWN,
405
+ hovertemplate: `${names[c]}<extra></extra>`,
406
+ }));
407
+ }
408
+ /**
409
+ * Axis title: the embedding's name, the axis number, and its variance share when there is
410
+ * one to report.
411
+ */
412
+ function axisTitle(stem, axis, varianceRatio) {
413
+ const share = varianceRatio?.[axis - 1];
414
+ if (share === undefined || !Number.isFinite(share))
415
+ return `${stem} ${axis}`;
416
+ // One decimal: the difference between 23.4% and 23% is worth seeing when comparing
417
+ // components, and more digits than that is noise from the estimate.
418
+ return `${stem} ${axis} (${(share * 100).toFixed(1)}%)`;
419
+ }
420
+ export function embeddingLayout(input) {
421
+ const stem = input.label || 'Embedding';
422
+ const vr = input.varianceRatio;
423
+ // No annotation for a derived embedding. That a recomputed UMAP is not the published
424
+ // picture does need saying — but the panel's caption already says it, and repeating it
425
+ // over the plot cost a strip of the panel's height and, in the 3D scene, sat on top of
426
+ // the cloud. One statement, in the caption.
427
+ if (input.z) {
428
+ return {
429
+ autosize: true,
430
+ // A 3D scene has no margins to speak of; the axes live inside it.
431
+ margin: { l: 0, r: 0, t: 0, b: 0 },
432
+ scene: {
433
+ xaxis: { title: { text: axisTitle(stem, 1, vr) } },
434
+ yaxis: { title: { text: axisTitle(stem, 2, vr) } },
435
+ zaxis: { title: { text: axisTitle(stem, 3, vr) } },
436
+ // Carried across the redraw, or every recolour snaps the cloud back to its
437
+ // default orientation.
438
+ ...(input.view?.camera ? { camera: input.view.camera } : {}),
439
+ // Equal aspect for the same reason as the plane: the axes carry no units, so
440
+ // distances only compare if all three are scaled alike. `data` keeps the cloud's
441
+ // own proportions instead of stretching it into the cube.
442
+ aspectmode: 'data',
443
+ },
444
+ showlegend: false,
445
+ hovermode: 'closest',
446
+ };
447
+ }
448
+ return {
449
+ autosize: true,
450
+ // Bottom margin carries the legend: laid out BELOW the plot rather than beside it.
451
+ // A vertical legend of 22 cell types took half a docked panel's width and squeezed
452
+ // the embedding into a corner — and the shapes of the clusters are the thing being
453
+ // read here, while the labels are a lookup.
454
+ margin: {
455
+ l: 44,
456
+ r: 8,
457
+ t: 8,
458
+ // Room for the legend only when one is drawn; otherwise the axis title alone.
459
+ b: (input.categories?.names.length ?? 0) > 0
460
+ && (input.categories?.names.length ?? 0) <= EMBEDDING_MAX_LEGEND_SHOWN ? 64 : 40,
461
+ },
462
+ // Equal aspect: an embedding's axes carry no units, so distances are only comparable if the
463
+ // two are scaled alike. Stretching one axis to fill the panel invents structure.
464
+ xaxis: {
465
+ title: { text: axisTitle(stem, 1, vr) }, zeroline: false, ticks: 'outside',
466
+ ...(input.view?.ranges ? { range: input.view.ranges.x, autorange: false } : {}),
467
+ },
468
+ yaxis: {
469
+ title: { text: axisTitle(stem, 2, vr) }, zeroline: false, ticks: 'outside',
470
+ scaleanchor: 'x', scaleratio: 1,
471
+ ...(input.view?.ranges ? { range: input.view.ranges.y, autorange: false } : {}),
472
+ },
473
+ legend: {
474
+ orientation: 'h',
475
+ itemsizing: 'constant',
476
+ font: { size: 9 },
477
+ yanchor: 'top', y: -0.16, xanchor: 'left', x: 0,
478
+ },
479
+ hovermode: 'closest',
480
+ };
481
+ }
482
+ /** Diverging, centred: after z-scoring, the sign carries the meaning. */
483
+ const HEATMAP_DIVERGING = [
484
+ [0, '#2166AC'],
485
+ [0.5, '#F7F7F7'],
486
+ [1, '#B2182B'],
487
+ ];
488
+ /** Sequential, for raw means where zero is the bottom rather than the middle. */
489
+ const HEATMAP_SEQUENTIAL = [
490
+ [0, '#F7FBFF'],
491
+ [0.5, '#6BAED6'],
492
+ [1, '#08306B'],
493
+ ];
494
+ export function buildHeatmapTraces(input) {
495
+ const { rows, cols, values, counts } = input;
496
+ if (rows.length === 0 || cols.length === 0)
497
+ return [];
498
+ // Plotly wants z as an array of rows, bottom-up; NaN renders as a gap.
499
+ const z = [];
500
+ for (let r = rows.length - 1; r >= 0; r--) {
501
+ const line = [];
502
+ for (let c = 0; c < cols.length; c++) {
503
+ const v = values[r * cols.length + c];
504
+ line.push(Number.isFinite(v) ? v : null);
505
+ }
506
+ z.push(line);
507
+ }
508
+ const y = rows.slice().reverse();
509
+ const unit = input.zScored ? 'z' : 'mean';
510
+ // Symmetric about zero when z-scored, so the same magnitude reads the same
511
+ // whichever side of the gene's average it falls on.
512
+ let zmin;
513
+ let zmax;
514
+ if (input.zScored) {
515
+ let peak = 0;
516
+ for (let i = 0; i < values.length; i++) {
517
+ const v = Math.abs(values[i]);
518
+ if (Number.isFinite(v) && v > peak)
519
+ peak = v;
520
+ }
521
+ zmax = peak > 0 ? peak : 1;
522
+ zmin = -zmax;
523
+ }
524
+ const hover = counts
525
+ ? cols.map((label, c) => `${label} · ${counts[c]} cells`)
526
+ : cols;
527
+ return [{
528
+ type: 'heatmap',
529
+ z,
530
+ x: cols,
531
+ y,
532
+ customdata: z.map(() => hover),
533
+ colorscale: input.zScored ? HEATMAP_DIVERGING : HEATMAP_SEQUENTIAL,
534
+ ...(zmin !== undefined ? { zmin, zmax } : {}),
535
+ // A gap has no colour rather than the scale's bottom: nothing was measured
536
+ // there, which is not the same as a low mean.
537
+ hoverongaps: false,
538
+ colorbar: { title: { text: unit, side: 'right' }, thickness: 10, len: 0.9 },
539
+ hovertemplate: `%{y}<br>%{customdata}<br>${unit} %{z:.2f}<extra></extra>`,
540
+ }];
541
+ }
542
+ export function heatmapLayout(input) {
543
+ return {
544
+ // Left margin grows with the longest gene name; the labels are the point.
545
+ margin: { t: 10, r: 10, b: 90, l: 8 },
546
+ autosize: true,
547
+ hovermode: 'closest',
548
+ xaxis: { automargin: true, tickangle: -45, tickfont: { size: 10 },
549
+ title: { text: input.groupLabel ?? '' } },
550
+ yaxis: { automargin: true, tickfont: { size: 10 } },
551
+ // Grows with the gene count so 3 rows are not stretched to 20 rows' height.
552
+ height: Math.max(180, 24 * input.rows.length + 110),
553
+ };
554
+ }
555
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib21pY3MtdHJhY2UtYnVpbGRlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL2ltcGxlbWVudGF0aW9ucy9wbG90bHkvb21pY3MtdHJhY2UtYnVpbGRlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBDQUEwQyxDQUFDO0FBd0N2RSxNQUFNLGFBQWEsR0FBRyxTQUFTLENBQUM7QUFDaEM7d0VBQ3dFO0FBQ3hFLE1BQU0sb0JBQW9CLEdBQUcsTUFBTSxDQUFDO0FBRXBDLCtFQUErRTtBQUMvRSxTQUFTLE9BQU8sQ0FBQyxLQUFzQixFQUFFLFFBQTJCO0lBQ2xFLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLEdBQUcsS0FBSyxDQUFDO0lBQzlCLE1BQU0sR0FBRyxHQUFhLEVBQUUsQ0FBQztJQUN6QixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ3ZDLElBQUksUUFBUSxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztZQUFFLFNBQVM7UUFDdkMsTUFBTSxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3BCLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztZQUFFLFNBQVM7UUFDbEMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDakQsQ0FBQztJQUNELE9BQU8sR0FBRyxDQUFDO0FBQ2IsQ0FBQztBQUVELHlFQUF5RTtBQUN6RSxTQUFTLElBQUksQ0FBQyxNQUFnQixFQUFFLEdBQUcsR0FBRyxvQkFBb0I7SUFDeEQsSUFBSSxNQUFNLENBQUMsTUFBTSxJQUFJLEdBQUc7UUFBRSxPQUFPLE1BQU0sQ0FBQztJQUN4QyxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsTUFBTSxHQUFHLEdBQUcsQ0FBQztJQUNqQyxNQUFNLEdBQUcsR0FBYSxFQUFFLENBQUM7SUFDekIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEdBQUcsRUFBRSxDQUFDLEVBQUU7UUFBRSxHQUFHLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDckUsT0FBTyxHQUFHLENBQUM7QUFDYixDQUFDO0FBRUQsdUVBQXVFO0FBQ3ZFLFNBQVMsVUFBVSxDQUNqQixLQUFzQixFQUFFLEtBQW9CLEVBQUUsUUFBMkI7SUFFekUsTUFBTSxPQUFPLEdBQWUsS0FBSyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDM0QsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsR0FBRyxLQUFLLENBQUM7SUFDOUIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUN2QyxJQUFJLFFBQVEsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7WUFBRSxTQUFTO1FBQ3ZDLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDNUIsc0VBQXNFO1FBQ3RFLDRDQUE0QztRQUM1QyxJQUFJLElBQUksS0FBSyxXQUFXLElBQUksSUFBSSxJQUFJLE9BQU8sQ0FBQyxNQUFNO1lBQUUsU0FBUztRQUM3RCxNQUFNLENBQUMsR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDcEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO1lBQUUsU0FBUztRQUNsQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMzRCxDQUFDO0lBQ0QsT0FBTyxPQUFPLENBQUM7QUFDakIsQ0FBQztBQUVELDJEQUEyRDtBQUMzRCxTQUFTLGVBQWUsQ0FBQyxTQUF3QztJQUMvRCxJQUFJLENBQUMsU0FBUztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQzVCLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxTQUFTLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRTtRQUFFLElBQUksU0FBUyxDQUFDLENBQUMsQ0FBQztZQUFFLE9BQU8sU0FBUyxDQUFDO0lBQzlFLE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztBQUVEOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sVUFBVSxnQkFBZ0IsQ0FBQyxJQUFvQixFQUFFLEtBQXNCO0lBQzNFLE1BQU0sU0FBUyxHQUFHLGVBQWUsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDbkQsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLEtBQUssSUFBSSxhQUFhLENBQUM7SUFFM0MsSUFBSSxJQUFJLEtBQUssV0FBVyxFQUFFLENBQUM7UUFDekIsTUFBTSxNQUFNLEdBQWMsQ0FBQztnQkFDekIsSUFBSSxFQUFFLFdBQVc7Z0JBQ2pCLENBQUMsRUFBRSxPQUFPLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQztnQkFDdkIsSUFBSSxFQUFFLEtBQUs7Z0JBQ1gsTUFBTSxFQUFFLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsRUFBRTtnQkFDckMsT0FBTyxFQUFFLFNBQVMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUM3QixhQUFhLEVBQUUsb0NBQW9DO2FBQ3BELENBQUMsQ0FBQztRQUNILElBQUksU0FBUyxFQUFFLENBQUM7WUFDZCxNQUFNLENBQUMsSUFBSSxDQUFDO2dCQUNWLElBQUksRUFBRSxXQUFXO2dCQUNqQixDQUFDLEVBQUUsT0FBTyxDQUFDLEtBQUssRUFBRSxTQUFTLENBQUM7Z0JBQzVCLElBQUksRUFBRSxVQUFVO2dCQUNoQixNQUFNLEVBQUUsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsRUFBRTtnQkFDaEQsT0FBTyxFQUFFLElBQUk7Z0JBQ2IsYUFBYSxFQUFFLHlDQUF5QzthQUN6RCxDQUFDLENBQUM7UUFDTCxDQUFDO1FBQ0QsT0FBTyxNQUFNLENBQUM7SUFDaEIsQ0FBQztJQUVELE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxDQUFDLG1CQUFtQjtJQUN0QyxNQUFNLE1BQU0sR0FBRztRQUNiLElBQUk7UUFDSixHQUFHLENBQUMsSUFBSSxLQUFLLFFBQVE7WUFDbkIsQ0FBQyxDQUFDLEVBQUUsR0FBRyxFQUFFLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxFQUFFLFFBQVEsRUFBRSxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsRUFBRSxNQUFNLEVBQUUsS0FBYyxFQUFFO1lBQ2pGLENBQUMsQ0FBQyxFQUFFLFNBQVMsRUFBRSxLQUFjLEVBQUUsQ0FBQztLQUNuQyxDQUFDO0lBRUYsSUFBSSxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDaEIsTUFBTSxPQUFPLEdBQUcsVUFBVSxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsS0FBSyxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBQzFELE9BQU8sT0FBTztZQUNaLHdFQUF3RTtZQUN4RSw4QkFBOEI7YUFDN0IsR0FBRyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDO2FBQ25DLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO2FBQ2xDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztZQUNYLEdBQUcsTUFBTTtZQUNULENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQztZQUNqQixJQUFJLEVBQUUsS0FBSyxDQUFDLEtBQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNsQyxNQUFNLEVBQUUsRUFBRSxLQUFLLEVBQUUsS0FBSyxDQUFDLEtBQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssRUFBRTtZQUNwRCxJQUFJLEVBQUUsRUFBRSxLQUFLLEVBQUUsS0FBSyxDQUFDLEtBQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssRUFBRTtTQUNuRCxDQUFDLENBQUMsQ0FBQztJQUNSLENBQUM7SUFFRCxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDO0lBQy9DLElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxDQUFDO1FBQUUsT0FBTyxFQUFFLENBQUM7SUFDbkMsT0FBTyxDQUFDO1lBQ04sR0FBRyxNQUFNO1lBQ1QsQ0FBQyxFQUFFLE1BQU07WUFDVCxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLEtBQUs7WUFDcEMsTUFBTSxFQUFFLEVBQUUsS0FBSyxFQUFFO1lBQ2pCLElBQUksRUFBRSxFQUFFLEtBQUssRUFBRTtTQUNoQixDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQsb0VBQW9FO0FBQ3BFLE1BQU0sVUFBVSxXQUFXLENBQUMsSUFBb0IsRUFBRSxLQUFzQjtJQUN0RSxNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxTQUFTLEtBQUssQ0FBQyxJQUFJLEdBQUcsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQztJQUM5RCxNQUFNLElBQUksR0FBRztRQUNYLE1BQU0sRUFBRSxFQUFFLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsRUFBRSxFQUFFLEVBQUU7UUFDdEMsUUFBUSxFQUFFLElBQUk7UUFDZCxVQUFVLEVBQUUsSUFBSSxLQUFLLFdBQVcsSUFBSSxDQUFDLENBQUMsZUFBZSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUM7UUFDdEUsTUFBTSxFQUFFLEVBQUUsV0FBVyxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUU7UUFDM0MsTUFBTSxFQUFFLElBQUk7UUFDWixTQUFTLEVBQUUsU0FBUztLQUNyQixDQUFDO0lBQ0YsSUFBSSxJQUFJLEtBQUssV0FBVyxFQUFFLENBQUM7UUFDekIsT0FBTztZQUNMLEdBQUcsSUFBSTtZQUNQLE9BQU8sRUFBRSxTQUFTO1lBQ2xCLEtBQUssRUFBRSxFQUFFLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsRUFBRTtZQUNqQyxLQUFLLEVBQUUsRUFBRSxLQUFLLEVBQUUsRUFBRSxJQUFJLEVBQUUsY0FBYyxFQUFFLEVBQUU7U0FDM0MsQ0FBQztJQUNKLENBQUM7SUFDRCxPQUFPO1FBQ0wsR0FBRyxJQUFJO1FBQ1AsS0FBSyxFQUFFLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRTtRQUMzQixLQUFLLEVBQUUsRUFBRSxLQUFLLEVBQUUsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRTtLQUNuRCxDQUFDO0FBQ0osQ0FBQztBQUVELDZFQUE2RTtBQUM3RSxNQUFNLFVBQVUsb0JBQW9CLENBQUMsSUFBb0I7SUFDdkQsT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLElBQUksS0FBSyxLQUFLLENBQUM7QUFDN0MsQ0FBQztBQUVELDJFQUEyRTtBQUMzRSxNQUFNLGNBQWMsR0FBRyxFQUFFLENBQUM7QUFZMUIsNkVBQTZFO0FBQzdFLE1BQU0sVUFBVSxlQUFlLENBQzdCLEtBQXNCO0lBRXRCLE1BQU0sRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUM7SUFDbEQsTUFBTSxTQUFTLEdBQUcsZUFBZSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUNuRCxNQUFNLE1BQU0sR0FBRyxJQUFJLFlBQVksQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDbkQsTUFBTSxRQUFRLEdBQUcsSUFBSSxZQUFZLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3JELEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDdEMsTUFBTSxDQUFDLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ25CLElBQUksQ0FBQyxLQUFLLFdBQVcsSUFBSSxDQUFDLElBQUksVUFBVSxDQUFDLE1BQU07WUFBRSxTQUFTO1FBQzFELE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ1osSUFBSSxTQUFTLElBQUksU0FBUyxDQUFDLENBQUMsQ0FBQztZQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBQy9DLENBQUM7SUFFRCxNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztTQUNwRCxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1NBQ3RCLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzdCLE1BQU0sR0FBRyxHQUFHLEtBQUssQ0FBQyxHQUFHLElBQUksY0FBYyxDQUFDO0lBQ3hDLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ2xDLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7SUFFL0IsTUFBTSxHQUFHLEdBQUc7UUFDVixNQUFNLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN4QyxNQUFNLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxhQUFhLENBQUM7UUFDckQsTUFBTSxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDNUIsUUFBUSxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7S0FDekMsQ0FBQztJQUNGLDBFQUEwRTtJQUMxRSwyRUFBMkU7SUFDM0UsSUFBSSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDaEIsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxJQUFJLENBQUMsTUFBTSxjQUFjLENBQUMsQ0FBQztRQUNyRCxHQUFHLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUMzQixHQUFHLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNuRCxHQUFHLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNqRSxDQUFDO0lBQ0QsT0FBTyxHQUFHLENBQUM7QUFDYixDQUFDO0FBRUQ7Ozs7Ozs7O0dBUUc7QUFDSCxNQUFNLFVBQVUsZ0JBQWdCLENBQUMsS0FBc0I7SUFDckQsTUFBTSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLGVBQWUsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNwRSxNQUFNLFNBQVMsR0FBRyxlQUFlLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ25ELHlFQUF5RTtJQUN6RSxrRUFBa0U7SUFDbEUsTUFBTSxJQUFJLEdBQUcsQ0FBSSxDQUFNLEVBQU8sRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUNyRCxNQUFNLE1BQU0sR0FBYyxDQUFDO1lBQ3pCLElBQUksRUFBRSxLQUFLO1lBQ1gsV0FBVyxFQUFFLEdBQUc7WUFDaEIsQ0FBQyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUM7WUFDZixDQUFDLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQztZQUNmLElBQUksRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsT0FBTztZQUNqQyxNQUFNLEVBQUUsRUFBRSxLQUFLLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFO1lBQy9CLE9BQU8sRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUM3QixhQUFhLEVBQUUsNkJBQTZCO1NBQzdDLENBQUMsQ0FBQztJQUNILElBQUksU0FBUyxFQUFFLENBQUM7UUFDZCxNQUFNLENBQUMsSUFBSSxDQUFDO1lBQ1YsSUFBSSxFQUFFLEtBQUs7WUFDWCxXQUFXLEVBQUUsR0FBRztZQUNoQixDQUFDLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQztZQUNqQixDQUFDLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQztZQUNmLElBQUksRUFBRSxVQUFVO1lBQ2hCLE1BQU0sRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUU7WUFDL0IsYUFBYSxFQUFFLHdDQUF3QztTQUN4RCxDQUFDLENBQUM7SUFDTCxDQUFDO0lBQ0QsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQztBQUVELE1BQU0sVUFBVSxZQUFZLENBQUMsS0FBeUM7SUFDcEUsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLGVBQWUsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMxQyxPQUFPO1FBQ0wsd0VBQXdFO1FBQ3hFLHFEQUFxRDtRQUNyRCxNQUFNLEVBQUUsRUFBRSxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFO1FBQ3JDLFFBQVEsRUFBRSxJQUFJO1FBQ2QsMEVBQTBFO1FBQzFFLDRDQUE0QztRQUM1QyxPQUFPLEVBQUUsU0FBUztRQUNsQixVQUFVLEVBQUUsQ0FBQyxDQUFDLGVBQWUsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDO1FBQzlDLE1BQU0sRUFBRSxFQUFFLFdBQVcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFO1FBQzNDLFNBQVMsRUFBRSxTQUFTO1FBQ3BCLEtBQUssRUFBRSxFQUFFLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxjQUFjLEVBQUUsRUFBRTtRQUMxQyxLQUFLLEVBQUUsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLGlCQUFpQixFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLEVBQUU7UUFDaEYsTUFBTSxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEVBQUUsR0FBRyxNQUFNLENBQUMsTUFBTSxHQUFHLEVBQUUsQ0FBQztLQUMvQyxDQUFDO0FBQ0osQ0FBQztBQWdGRDs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sK0JBQStCLEdBQUcsRUFBRSxDQUFDO0FBRWxEOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sMEJBQTBCLEdBQUcsRUFBRSxDQUFDO0FBRTdDLDhFQUE4RTtBQUM5RSxNQUFNLHVCQUF1QixHQUFHLElBQUksQ0FBQztBQUVyQzs7Ozs7Ozs7R0FRRztBQUNILFNBQVMsV0FBVyxDQUFDLEdBQVcsRUFBRSxNQUFjO0lBQzlDLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ25DLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxNQUFNLEtBQUssQ0FBQztRQUM3QixDQUFDLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1FBQzVDLENBQUMsQ0FBQyxLQUFLLENBQUM7SUFDVixNQUFNLENBQUMsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNwQyxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUM7UUFBRSxPQUFPLEdBQUcsQ0FBQztJQUN6RCxNQUFNLEdBQUcsR0FBRyxDQUFDLENBQVMsRUFBVSxFQUFFLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUM7SUFDdEUsTUFBTSxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDO0lBQ2hDLE1BQU0sQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQztJQUMvQixNQUFNLENBQUMsR0FBRyxHQUFHLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDO0lBQ3hCLE9BQU8sSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxFQUFFLENBQUM7QUFDeEUsQ0FBQztBQUVELE1BQU0sNEJBQTRCLEdBQUcsQ0FBQyxDQUFDO0FBRXZDLE1BQU0sVUFBVSxvQkFBb0IsQ0FBQyxLQUEwQjtJQUM3RCxNQUFNLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxVQUFVLEVBQUUsU0FBUyxFQUFFLEdBQUcsS0FBSyxDQUFDO0lBQzlDLE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdkMsSUFBSSxDQUFDLEtBQUssQ0FBQztRQUFFLE9BQU8sRUFBRSxDQUFDO0lBQ3ZCLE1BQU0sQ0FBQyxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUM7SUFDbEIsaUZBQWlGO0lBQ2pGLHFGQUFxRjtJQUNyRixtRkFBbUY7SUFDbkYsaUVBQWlFO0lBQ2pFLE1BQU0sSUFBSSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxXQUFXLENBQUM7SUFDM0MsTUFBTSxVQUFVLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxJQUFJLElBQUksNEJBQTRCLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDbEYsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksSUFBSSw0QkFBNEIsQ0FBQyxDQUFDO0lBQ2pELE1BQU0sVUFBVSxHQUFHLENBQUMsQ0FBUyxFQUFVLEVBQUUsQ0FDdkMsQ0FBQyxTQUFTLElBQUksU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLHVCQUF1QixDQUFDO0lBQzNELE1BQU0sVUFBVSxHQUFHLENBQUMsQ0FBUyxFQUFXLEVBQUUsQ0FBQyxDQUFDLFNBQVMsSUFBSSxTQUFTLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzVFLGdGQUFnRjtJQUNoRixrRkFBa0Y7SUFDbEYsTUFBTSxRQUFRLEdBQUcsSUFBSSxHQUFHLEVBQWtCLENBQUM7SUFDM0MsTUFBTSxLQUFLLEdBQUcsQ0FBQyxHQUFXLEVBQUUsQ0FBUyxFQUFVLEVBQUU7UUFDL0MsSUFBSSxDQUFDLENBQUMsSUFBSSxVQUFVLENBQUMsQ0FBQyxDQUFDO1lBQUUsT0FBTyxHQUFHLENBQUM7UUFDcEMsTUFBTSxHQUFHLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUM5QixJQUFJLEdBQUc7WUFBRSxPQUFPLEdBQUcsQ0FBQztRQUNwQixNQUFNLE1BQU0sR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUMsR0FBRyx1QkFBdUIsQ0FBQyxDQUFDO1FBQzdELFFBQVEsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQzFCLE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUMsQ0FBQztJQUNGLCtEQUErRDtJQUMvRCxNQUFNLFlBQVksR0FBRyxDQUFDLE9BQTBCLEVBQXdCLEVBQUUsQ0FDeEUsU0FBUyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFFeEQsSUFBSSxDQUFDLFVBQVUsSUFBSSxVQUFVLENBQUMsS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUNqRCxPQUFPLENBQUM7Z0JBQ04sSUFBSTtnQkFDSixJQUFJLEVBQUUsU0FBUztnQkFDZixDQUFDLEVBQUUsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztnQkFDL0IsQ0FBQyxFQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQy9CLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7Z0JBQ2pELGlGQUFpRjtnQkFDakYsK0VBQStFO2dCQUMvRSxtRkFBbUY7Z0JBQ25GLFVBQVUsRUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO2dCQUNsRCxNQUFNLEVBQUU7b0JBQ04sSUFBSSxFQUFFLFVBQVU7b0JBQ2hCLGdGQUFnRjtvQkFDaEYsd0NBQXdDO29CQUN4QyxLQUFLLEVBQUUsU0FBUyxJQUFJLENBQUM7d0JBQ25CLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsS0FBSyxDQUFDLFNBQVMsRUFBRSxDQUFDLENBQUMsQ0FBQzt3QkFDMUQsQ0FBQyxDQUFDLFNBQVM7b0JBQ2IsR0FBRyxDQUFDLFlBQVksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7d0JBQ3RELENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUU7d0JBQ2pFLENBQUMsQ0FBQyxFQUFFLENBQUM7aUJBQ1I7Z0JBQ0QsU0FBUyxFQUFFLE1BQU07Z0JBQ2pCLFVBQVUsRUFBRSxLQUFLO2FBQ2xCLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxNQUFNLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxVQUFVLENBQUM7SUFDNUMsSUFBSSxLQUFLLENBQUMsTUFBTSxHQUFHLCtCQUErQixFQUFFLENBQUM7UUFDbkQsc0ZBQXNGO1FBQ3RGLHVDQUF1QztRQUN2QyxPQUFPLENBQUM7Z0JBQ04sSUFBSTtnQkFDSixJQUFJLEVBQUUsU0FBUztnQkFDZixDQUFDLEVBQUUsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztnQkFDL0IsQ0FBQyxFQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQy9CLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7Z0JBQ2pELE1BQU0sRUFBRTtvQkFDTixJQUFJLEVBQUUsVUFBVTtvQkFDaEIsS0FBSyxFQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLFNBQVMsRUFBRSxDQUFDLENBQUMsQ0FBQztvQkFDbkYsR0FBRyxDQUFDLFlBQVksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7d0JBQ3RELENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUU7d0JBQ2pFLENBQUMsQ0FBQyxFQUFFLENBQUM7aUJBQ1I7Z0JBQ0QsVUFBVSxFQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQ2xELElBQUksRUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLFlBQVksQ0FBQztnQkFDMUUsYUFBYSxFQUFFLHdCQUF3QjtnQkFDdkMsVUFBVSxFQUFFLEtBQUs7YUFDbEIsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELHdGQUF3RjtJQUN4Rix1REFBdUQ7SUFDdkQsTUFBTSxPQUFPLEdBQWUsS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUNoRCxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDM0IsTUFBTSxDQUFDLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ25CLHVGQUF1RjtRQUN2RixxREFBcUQ7UUFDckQsSUFBSSxDQUFDLEdBQUcsT0FBTyxDQUFDLE1BQU07WUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzdDLENBQUM7SUFDRCxPQUFPLE9BQU87U0FDWCxHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7U0FDN0IsTUFBTSxDQUFDLENBQUMsRUFBRSxHQUFHLEVBQUUsRUFBRSxFQUFFLENBQUMsR0FBRyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7U0FDbkMsR0FBRyxDQUFDLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDcEIsSUFBSTtRQUNKLElBQUksRUFBRSxTQUFTO1FBQ2YsSUFBSSxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDZCxDQUFDLEVBQUUsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3ZCLENBQUMsRUFBRSxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDdkIsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ3pDLGdGQUFnRjtRQUNoRix5REFBeUQ7UUFDekQsVUFBVSxFQUFFLEdBQUc7UUFDZixNQUFNLEVBQUU7WUFDTixJQUFJLEVBQUUsVUFBVTtZQUNoQixLQUFLLEVBQUUsU0FBUyxJQUFJLENBQUM7Z0JBQ25CLENBQUMsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLFNBQVMsRUFBRSxDQUFDLENBQUMsQ0FBQztnQkFDbEQsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLFNBQVMsQ0FBQztZQUM1QixHQUFHLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxHQUFHLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztTQUMvRDtRQUNELGdGQUFnRjtRQUNoRiw4RUFBOEU7UUFDOUUsVUFBVSxFQUFFLEtBQUssQ0FBQyxNQUFNLElBQUksMEJBQTBCO1FBQ3RELGFBQWEsRUFBRSxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsaUJBQWlCO0tBQzVDLENBQUMsQ0FBQyxDQUFDO0FBQ1IsQ0FBQztBQUVEOzs7R0FHRztBQUNILFNBQVMsU0FBUyxDQUFDLElBQVksRUFBRSxJQUFZLEVBQUUsYUFBd0I7SUFDckUsTUFBTSxLQUFLLEdBQUcsYUFBYSxFQUFFLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ3hDLElBQUksS0FBSyxLQUFLLFNBQVMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDO1FBQUUsT0FBTyxHQUFHLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQztJQUM3RSxtRkFBbUY7SUFDbkYsb0VBQW9FO0lBQ3BFLE9BQU8sR0FBRyxJQUFJLElBQUksSUFBSSxLQUFLLENBQUMsS0FBSyxHQUFHLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO0FBQzFELENBQUM7QUFFRCxNQUFNLFVBQVUsZUFBZSxDQUFDLEtBQTBCO0lBQ3hELE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxLQUFLLElBQUksV0FBVyxDQUFDO0lBQ3hDLE1BQU0sRUFBRSxHQUFHLEtBQUssQ0FBQyxhQUFhLENBQUM7SUFDL0IscUZBQXFGO0lBQ3JGLHVGQUF1RjtJQUN2Rix1RkFBdUY7SUFDdkYsNENBQTRDO0lBRTVDLElBQUksS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ1osT0FBTztZQUNMLFFBQVEsRUFBRSxJQUFJO1lBQ2Qsa0VBQWtFO1lBQ2xFLE1BQU0sRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUU7WUFDbEMsS0FBSyxFQUFFO2dCQUNMLEtBQUssRUFBRSxFQUFFLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsRUFBRSxFQUFFO2dCQUNsRCxLQUFLLEVBQUUsRUFBRSxLQUFLLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxDQUFDLElBQUksRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLEVBQUUsRUFBRTtnQkFDbEQsS0FBSyxFQUFFLEVBQUUsS0FBSyxFQUFFLEVBQUUsSUFBSSxFQUFFLFNBQVMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxFQUFFLEVBQUU7Z0JBQ2xELDJFQUEyRTtnQkFDM0UsdUJBQXVCO2dCQUN2QixHQUFHLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztnQkFDNUQsNkVBQTZFO2dCQUM3RSxpRkFBaUY7Z0JBQ2pGLDBEQUEwRDtnQkFDMUQsVUFBVSxFQUFFLE1BQU07YUFDbkI7WUFDRCxVQUFVLEVBQUUsS0FBSztZQUNqQixTQUFTLEVBQUUsU0FBUztTQUNyQixDQUFDO0lBQ0osQ0FBQztJQUVELE9BQU87UUFDTCxRQUFRLEVBQUUsSUFBSTtRQUNkLG1GQUFtRjtRQUNuRixtRkFBbUY7UUFDbkYsbUZBQW1GO1FBQ25GLDRDQUE0QztRQUM1QyxNQUFNLEVBQUU7WUFDTixDQUFDLEVBQUUsRUFBRTtZQUNMLENBQUMsRUFBRSxDQUFDO1lBQ0osQ0FBQyxFQUFFLENBQUM7WUFDSiw4RUFBOEU7WUFDOUUsQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLFVBQVUsRUFBRSxLQUFLLENBQUMsTUFBTSxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUM7bUJBQ3ZDLENBQUMsS0FBSyxDQUFDLFVBQVUsRUFBRSxLQUFLLENBQUMsTUFBTSxJQUFJLENBQUMsQ0FBQyxJQUFJLDBCQUEwQixDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUU7U0FDbkY7UUFDRCw0RkFBNEY7UUFDNUYsaUZBQWlGO1FBQ2pGLEtBQUssRUFBRTtZQUNMLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsRUFBRSxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLFNBQVM7WUFDMUUsR0FBRyxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7U0FDaEY7UUFDRCxLQUFLLEVBQUU7WUFDTCxLQUFLLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxDQUFDLElBQUksRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLEVBQUUsRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxTQUFTO1lBQzFFLFdBQVcsRUFBRSxHQUFHLEVBQUUsVUFBVSxFQUFFLENBQUM7WUFDL0IsR0FBRyxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7U0FDaEY7UUFDRCxNQUFNLEVBQUU7WUFDTixXQUFXLEVBQUUsR0FBRztZQUNoQixVQUFVLEVBQUUsVUFBVTtZQUN0QixJQUFJLEVBQUUsRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFO1lBQ2pCLE9BQU8sRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsQ0FBQyxFQUFFLENBQUM7U0FDaEQ7UUFDRCxTQUFTLEVBQUUsU0FBUztLQUNyQixDQUFDO0FBQ0osQ0FBQztBQWVELHlFQUF5RTtBQUN6RSxNQUFNLGlCQUFpQixHQUF1QjtJQUM1QyxDQUFDLENBQUMsRUFBRSxTQUFTLENBQUM7SUFDZCxDQUFDLEdBQUcsRUFBRSxTQUFTLENBQUM7SUFDaEIsQ0FBQyxDQUFDLEVBQUUsU0FBUyxDQUFDO0NBQ2YsQ0FBQztBQUNGLGlGQUFpRjtBQUNqRixNQUFNLGtCQUFrQixHQUF1QjtJQUM3QyxDQUFDLENBQUMsRUFBRSxTQUFTLENBQUM7SUFDZCxDQUFDLEdBQUcsRUFBRSxTQUFTLENBQUM7SUFDaEIsQ0FBQyxDQUFDLEVBQUUsU0FBUyxDQUFDO0NBQ2YsQ0FBQztBQUVGLE1BQU0sVUFBVSxrQkFBa0IsQ0FBQyxLQUF3QjtJQUN6RCxNQUFNLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsS0FBSyxDQUFDO0lBQzdDLElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxDQUFDO1FBQUUsT0FBTyxFQUFFLENBQUM7SUFDdEQsdUVBQXVFO0lBQ3ZFLE1BQU0sQ0FBQyxHQUF3QixFQUFFLENBQUM7SUFDbEMsS0FBSyxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDMUMsTUFBTSxJQUFJLEdBQXNCLEVBQUUsQ0FBQztRQUNuQyxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQ3JDLE1BQU0sQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQztZQUN0QyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDM0MsQ0FBQztRQUNELENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDZixDQUFDO0lBQ0QsTUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ2pDLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBQzFDLDJFQUEyRTtJQUMzRSxvREFBb0Q7SUFDcEQsSUFBSSxJQUF3QixDQUFDO0lBQzdCLElBQUksSUFBd0IsQ0FBQztJQUM3QixJQUFJLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNsQixJQUFJLElBQUksR0FBRyxDQUFDLENBQUM7UUFDYixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQ3ZDLE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDOUIsSUFBSSxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxJQUFJO2dCQUFFLElBQUksR0FBRyxDQUFDLENBQUM7UUFDL0MsQ0FBQztRQUNELElBQUksR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUMzQixJQUFJLEdBQUcsQ0FBQyxJQUFJLENBQUM7SUFDZixDQUFDO0lBQ0QsTUFBTSxLQUFLLEdBQUcsTUFBTTtRQUNsQixDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLEdBQUcsS0FBSyxNQUFNLE1BQU0sQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDO1FBQ3pELENBQUMsQ0FBQyxJQUFJLENBQUM7SUFDVCxPQUFPLENBQUM7WUFDTixJQUFJLEVBQUUsU0FBUztZQUNmLENBQUM7WUFDRCxDQUFDLEVBQUUsSUFBSTtZQUNQLENBQUM7WUFDRCxVQUFVLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUM7WUFDOUIsVUFBVSxFQUFFLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxrQkFBa0I7WUFDbEUsR0FBRyxDQUFDLElBQUksS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7WUFDN0MsMkVBQTJFO1lBQzNFLDhDQUE4QztZQUM5QyxXQUFXLEVBQUUsS0FBSztZQUNsQixRQUFRLEVBQUUsRUFBRSxLQUFLLEVBQUUsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUU7WUFDM0UsYUFBYSxFQUFFLDRCQUE0QixJQUFJLDBCQUEwQjtTQUMxRSxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQsTUFBTSxVQUFVLGFBQWEsQ0FBQyxLQUF3QjtJQUNwRCxPQUFPO1FBQ0wsMEVBQTBFO1FBQzFFLE1BQU0sRUFBRSxFQUFFLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUU7UUFDckMsUUFBUSxFQUFFLElBQUk7UUFDZCxTQUFTLEVBQUUsU0FBUztRQUNwQixLQUFLLEVBQUUsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDLEVBQUUsRUFBRSxRQUFRLEVBQUUsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFO1lBQy9ELEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxLQUFLLENBQUMsVUFBVSxJQUFJLEVBQUUsRUFBRSxFQUFFO1FBQzNDLEtBQUssRUFBRSxFQUFFLFVBQVUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxFQUFFO1FBQ25ELDRFQUE0RTtRQUM1RSxNQUFNLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsRUFBRSxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxHQUFHLEdBQUcsQ0FBQztLQUNwRCxDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IE5PX0NBVEVHT1JZIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL3NwYXRpYWwtZGF0YXNldC5jb250cmFjdCc7XG5cbi8qKlxuICogUGxvdGx5IHRyYWNlcyBmb3IgdGhlIDEtRCBkaXN0cmlidXRpb24gY2hhcnRzIG92ZXIgc3BhdGlhbC1vbWljcyB2YWx1ZXM6XG4gKiBoaXN0b2dyYW0sIHZpb2xpbiBhbmQgYm94LlxuICpcbiAqIFB1cmUsIGxpa2UgYHBsb3RseS10cmFjZS1idWlsZGVycy50c2Ag4oCUIGFuZCBkZWxpYmVyYXRlbHkgU0VQQVJBVEUgZnJvbSBpdCxcbiAqIGJlY2F1c2UgdGhhdCBtb2R1bGUncyBgVHJhY2VCdWlsZElucHV0YCBpcyBpbWFnZS1tYXRyaXggc2hhcGVkIChgZnJhbWVzYCxcbiAqIGB3aWR0aGAsIGByYXRpb3NgLCBgdHJ1ZUltYWdlU2l6ZWApIGFuZCBjYW5ub3QgZXhwcmVzcyBcIm9uZSB2YWx1ZSBwZXJcbiAqIG9ic2VydmF0aW9uXCIuIEZvcmNpbmcgdGhlIHR3byB0b2dldGhlciB3b3VsZCBtYWtlIGJvdGggd29yc2UuXG4gKlxuICogVmlvbGluIGFuZCBib3ggbmVlZCBubyBidW5kbGluZyB3b3JrOiBgcGxvdGx5LmpzLWRpc3QtbWluYCBpcyB0aGUgZnVsbFxuICogZGlzdHJpYnV0aW9uIGFuZCBhbHJlYWR5IGNhcnJpZXMgYm90aCB0cmFjZSB0eXBlcy5cbiAqL1xuXG5leHBvcnQgdHlwZSBPbWljc0NoYXJ0S2luZCA9XG4gIHwgJ2hpc3RvZ3JhbScgfCAndmlvbGluJyB8ICdib3gnIHwgJ2NvdW50cycgfCAnaGVhdG1hcCcgfCAnZW1iZWRkaW5nJztcblxuLyoqIFBlci1vYnNlcnZhdGlvbiBncm91cGluZyBmb3IgYSB2aW9saW4vYm94LCBvciBhbiBvdmVybGFpZCBoaXN0b2dyYW0uICovXG5leHBvcnQgaW50ZXJmYWNlIE9taWNzR3JvdXBpbmcge1xuICBjb2RlczogVWludDE2QXJyYXk7XG4gIGNhdGVnb3JpZXM6IHN0cmluZ1tdO1xuICAvKiogYCNycmdnYmJgIHBlciBjYXRlZ29yeSwgaW5kZXgtYWxpZ25lZCDigJQgdGhlIHNhbWUgY29sb3VycyB0aGUgbWFwIHVzZXMuICovXG4gIGNvbG9yczogc3RyaW5nW107XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgT21pY3NUcmFjZUlucHV0IHtcbiAgLyoqIFRoZSB2YWx1ZXMgYmVpbmcgY2hhcnRlZCAoYW4gYW5ub3RhdGlvbiBjb2x1bW4gb3IgYSBnZW5lIHZlY3RvcikuICovXG4gIHZhbHVlczogRmxvYXQzMkFycmF5O1xuICAvKiogQXhpcyBsYWJlbCBmb3IgdGhlIHZhbHVlIOKAlCB0aGUgY29sdW1uIG9yIGdlbmUgbmFtZS4gKi9cbiAgbmFtZTogc3RyaW5nO1xuICBncm91cD86IE9taWNzR3JvdXBpbmcgfCBudWxsO1xuICAvKiogYDFgIG1hcmtzIGEgc2VsZWN0ZWQgb2JzZXJ2YXRpb24uIFdoZW4gcHJlc2VudCwgdGhlIGNoYXJ0cyBuYXJyb3cgdG8gaXQuICovXG4gIHNlbGVjdGlvbj86IFVpbnQ4QXJyYXkgfCBudWxsO1xuICAvKiogUGxvdCBgbG9nMXAodmFsdWUpYCBpbnN0ZWFkIG9mIHRoZSByYXcgdmFsdWUuICovXG4gIGxvZz86IGJvb2xlYW47XG4gIC8qKiBDb2xvdXIgZm9yIHRoZSB1bmdyb3VwZWQgdHJhY2UuICovXG4gIGNvbG9yPzogc3RyaW5nO1xufVxuXG5jb25zdCBERUZBVUxUX0NPTE9SID0gJyMwMDcyQjInO1xuLyoqIFBsb3RseSByZW5kZXJzIGV2ZXJ5IHBvaW50IG9mIGEgdmlvbGluL2JveDsgcGFzdCB0aGlzIHdlIHRoaW4gdGhlIGlucHV0LlxuICogIDg0ayBjZWxscyB4IGEgdmlvbGluIHBlciBjYXRlZ29yeSBpcyBvdGhlcndpc2Ugc2Vjb25kcyBvZiBsYXlvdXQuICovXG5jb25zdCBNQVhfUE9JTlRTX1BFUl9UUkFDRSA9IDIwXzAwMDtcblxuLyoqIEZpbml0ZSB2YWx1ZXMgb25seSwgb3B0aW9uYWxseSBsb2ctc2NhbGVkIGFuZCByZXN0cmljdGVkIHRvIGEgc2VsZWN0aW9uLiAqL1xuZnVuY3Rpb24gcHJlcGFyZShpbnB1dDogT21pY3NUcmFjZUlucHV0LCByZXN0cmljdDogVWludDhBcnJheSB8IG51bGwpOiBudW1iZXJbXSB7XG4gIGNvbnN0IHsgdmFsdWVzLCBsb2cgfSA9IGlucHV0O1xuICBjb25zdCBvdXQ6IG51bWJlcltdID0gW107XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgaWYgKHJlc3RyaWN0ICYmICFyZXN0cmljdFtpXSkgY29udGludWU7XG4gICAgY29uc3QgdiA9IHZhbHVlc1tpXTtcbiAgICBpZiAoIU51bWJlci5pc0Zpbml0ZSh2KSkgY29udGludWU7XG4gICAgb3V0LnB1c2gobG9nID8gTWF0aC5sb2cxcChNYXRoLm1heCgwLCB2KSkgOiB2KTtcbiAgfVxuICByZXR1cm4gb3V0O1xufVxuXG4vKiogRXZlbmx5IHRoaW4gYSBzYW1wbGUgdG8gYG1heGAgcG9pbnRzLCBwcmVzZXJ2aW5nIGl0cyBkaXN0cmlidXRpb24uICovXG5mdW5jdGlvbiB0aGluKHZhbHVlczogbnVtYmVyW10sIG1heCA9IE1BWF9QT0lOVFNfUEVSX1RSQUNFKTogbnVtYmVyW10ge1xuICBpZiAodmFsdWVzLmxlbmd0aCA8PSBtYXgpIHJldHVybiB2YWx1ZXM7XG4gIGNvbnN0IHN0ZXAgPSB2YWx1ZXMubGVuZ3RoIC8gbWF4O1xuICBjb25zdCBvdXQ6IG51bWJlcltdID0gW107XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbWF4OyBpKyspIG91dC5wdXNoKHZhbHVlc1tNYXRoLmZsb29yKGkgKiBzdGVwKV0pO1xuICByZXR1cm4gb3V0O1xufVxuXG4vKiogVmFsdWVzIHBlciBjYXRlZ29yeSwgZmluaXRlIGFuZCBvcHRpb25hbGx5IHNlbGVjdGlvbi1yZXN0cmljdGVkLiAqL1xuZnVuY3Rpb24gYnlDYXRlZ29yeShcbiAgaW5wdXQ6IE9taWNzVHJhY2VJbnB1dCwgZ3JvdXA6IE9taWNzR3JvdXBpbmcsIHJlc3RyaWN0OiBVaW50OEFycmF5IHwgbnVsbCxcbik6IG51bWJlcltdW10ge1xuICBjb25zdCBidWNrZXRzOiBudW1iZXJbXVtdID0gZ3JvdXAuY2F0ZWdvcmllcy5tYXAoKCkgPT4gW10pO1xuICBjb25zdCB7IHZhbHVlcywgbG9nIH0gPSBpbnB1dDtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyBpKyspIHtcbiAgICBpZiAocmVzdHJpY3QgJiYgIXJlc3RyaWN0W2ldKSBjb250aW51ZTtcbiAgICBjb25zdCBjb2RlID0gZ3JvdXAuY29kZXNbaV07XG4gICAgLy8gTk9fQ0FURUdPUlkgaGFzIG5vIGJ1Y2tldCBieSBkZWZpbml0aW9uOyBhbiBvdXQtb2YtcmFuZ2UgY29kZSB3b3VsZFxuICAgIC8vIG90aGVyd2lzZSBzaWxlbnRseSBsYW5kIGluIHRoZSB3cm9uZyBvbmUuXG4gICAgaWYgKGNvZGUgPT09IE5PX0NBVEVHT1JZIHx8IGNvZGUgPj0gYnVja2V0cy5sZW5ndGgpIGNvbnRpbnVlO1xuICAgIGNvbnN0IHYgPSB2YWx1ZXNbaV07XG4gICAgaWYgKCFOdW1iZXIuaXNGaW5pdGUodikpIGNvbnRpbnVlO1xuICAgIGJ1Y2tldHNbY29kZV0ucHVzaChsb2cgPyBNYXRoLmxvZzFwKE1hdGgubWF4KDAsIHYpKSA6IHYpO1xuICB9XG4gIHJldHVybiBidWNrZXRzO1xufVxuXG4vKiogVHJ1ZSB3aGVuIHRoZSBzZWxlY3Rpb24gYWN0dWFsbHkgcmVzdHJpY3RzIGFueXRoaW5nLiAqL1xuZnVuY3Rpb24gYWN0aXZlU2VsZWN0aW9uKHNlbGVjdGlvbjogVWludDhBcnJheSB8IG51bGwgfCB1bmRlZmluZWQpOiBVaW50OEFycmF5IHwgbnVsbCB7XG4gIGlmICghc2VsZWN0aW9uKSByZXR1cm4gbnVsbDtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBzZWxlY3Rpb24ubGVuZ3RoOyBpKyspIGlmIChzZWxlY3Rpb25baV0pIHJldHVybiBzZWxlY3Rpb247XG4gIHJldHVybiBudWxsO1xufVxuXG4vKipcbiAqIFRyYWNlcyBmb3Igb25lIGNoYXJ0IGtpbmQuXG4gKlxuICogLSAqKmhpc3RvZ3JhbSoqIOKAlCB0aGUgZnVsbCBkaXN0cmlidXRpb24sIHBsdXMgYW4gb3ZlcmxhaWQgKlNlbGVjdGVkKiB0cmFjZVxuICogICB3aGVuIGEgc2VsZWN0aW9uIGV4aXN0cywgc28gdGhlIHR3byBhcmUgZGlyZWN0bHkgY29tcGFyYWJsZS4gVGhhdFxuICogICBjb21wYXJpc29uIGlzIHRoZSB3aG9sZSBwb2ludCBvZiBsaW5raW5nIHRoZSBjaGFydCB0byB0aGUgbWFwLlxuICogLSAqKnZpb2xpbiAvIGJveCoqIOKAlCBvbmUgcGVyIGNhdGVnb3J5IHdoZW4gZ3JvdXBlZCwgb3RoZXJ3aXNlIGEgc2luZ2xlIHRyYWNlLlxuICogICBUaGVzZSBuYXJyb3cgVE8gdGhlIHNlbGVjdGlvbiByYXRoZXIgdGhhbiBvdmVybGF5aW5nIGl0OiBhIHZpb2xpbiBwZXJcbiAqICAgY2F0ZWdvcnkgcGVyIHNlbGVjdGlvbiBzdGF0ZSBpcyB1bnJlYWRhYmxlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gYnVpbGRPbWljc1RyYWNlcyhraW5kOiBPbWljc0NoYXJ0S2luZCwgaW5wdXQ6IE9taWNzVHJhY2VJbnB1dCk6IHVua25vd25bXSB7XG4gIGNvbnN0IHNlbGVjdGlvbiA9IGFjdGl2ZVNlbGVjdGlvbihpbnB1dC5zZWxlY3Rpb24pO1xuICBjb25zdCBjb2xvciA9IGlucHV0LmNvbG9yID8/IERFRkFVTFRfQ09MT1I7XG5cbiAgaWYgKGtpbmQgPT09ICdoaXN0b2dyYW0nKSB7XG4gICAgY29uc3QgdHJhY2VzOiB1bmtub3duW10gPSBbe1xuICAgICAgdHlwZTogJ2hpc3RvZ3JhbScsXG4gICAgICB4OiBwcmVwYXJlKGlucHV0LCBudWxsKSxcbiAgICAgIG5hbWU6ICdBbGwnLFxuICAgICAgbWFya2VyOiB7IGNvbG9yLCBsaW5lOiB7IHdpZHRoOiAwIH0gfSxcbiAgICAgIG9wYWNpdHk6IHNlbGVjdGlvbiA/IDAuNTUgOiAxLFxuICAgICAgaG92ZXJ0ZW1wbGF0ZTogJyV7eX0gb2JzPGJyPiV7eH08ZXh0cmE+QWxsPC9leHRyYT4nLFxuICAgIH1dO1xuICAgIGlmIChzZWxlY3Rpb24pIHtcbiAgICAgIHRyYWNlcy5wdXNoKHtcbiAgICAgICAgdHlwZTogJ2hpc3RvZ3JhbScsXG4gICAgICAgIHg6IHByZXBhcmUoaW5wdXQsIHNlbGVjdGlvbiksXG4gICAgICAgIG5hbWU6ICdTZWxlY3RlZCcsXG4gICAgICAgIG1hcmtlcjogeyBjb2xvcjogJyNENTVFMDAnLCBsaW5lOiB7IHdpZHRoOiAwIH0gfSxcbiAgICAgICAgb3BhY2l0eTogMC44NSxcbiAgICAgICAgaG92ZXJ0ZW1wbGF0ZTogJyV7eX0gb2JzPGJyPiV7eH08ZXh0cmE+U2VsZWN0ZWQ8L2V4dHJhPicsXG4gICAgICB9KTtcbiAgICB9XG4gICAgcmV0dXJuIHRyYWNlcztcbiAgfVxuXG4gIGNvbnN0IHR5cGUgPSBraW5kOyAvLyAndmlvbGluJyB8ICdib3gnXG4gIGNvbnN0IHNoYXJlZCA9IHtcbiAgICB0eXBlLFxuICAgIC4uLih0eXBlID09PSAndmlvbGluJ1xuICAgICAgPyB7IGJveDogeyB2aXNpYmxlOiB0cnVlIH0sIG1lYW5saW5lOiB7IHZpc2libGU6IHRydWUgfSwgcG9pbnRzOiBmYWxzZSBhcyBjb25zdCB9XG4gICAgICA6IHsgYm94cG9pbnRzOiBmYWxzZSBhcyBjb25zdCB9KSxcbiAgfTtcblxuICBpZiAoaW5wdXQuZ3JvdXApIHtcbiAgICBjb25zdCBidWNrZXRzID0gYnlDYXRlZ29yeShpbnB1dCwgaW5wdXQuZ3JvdXAsIHNlbGVjdGlvbik7XG4gICAgcmV0dXJuIGJ1Y2tldHNcbiAgICAgIC8vIERyb3AgZW1wdHkgY2F0ZWdvcmllczogYW4gZW1wdHkgdmlvbGluIHJlbmRlcnMgYXMgYSBzdHJheSB0aWNrIHdpdGggYVxuICAgICAgLy8gbGFiZWwsIHdoaWNoIHJlYWRzIGFzIGRhdGEuXG4gICAgICAubWFwKCh2YWx1ZXMsIGkpID0+ICh7IHZhbHVlcywgaSB9KSlcbiAgICAgIC5maWx0ZXIoKGIpID0+IGIudmFsdWVzLmxlbmd0aCA+IDApXG4gICAgICAubWFwKChiKSA9PiAoe1xuICAgICAgICAuLi5zaGFyZWQsXG4gICAgICAgIHk6IHRoaW4oYi52YWx1ZXMpLFxuICAgICAgICBuYW1lOiBpbnB1dC5ncm91cCEuY2F0ZWdvcmllc1tiLmldLFxuICAgICAgICBtYXJrZXI6IHsgY29sb3I6IGlucHV0Lmdyb3VwIS5jb2xvcnNbYi5pXSA/PyBjb2xvciB9LFxuICAgICAgICBsaW5lOiB7IGNvbG9yOiBpbnB1dC5ncm91cCEuY29sb3JzW2IuaV0gPz8gY29sb3IgfSxcbiAgICAgIH0pKTtcbiAgfVxuXG4gIGNvbnN0IHZhbHVlcyA9IHRoaW4ocHJlcGFyZShpbnB1dCwgc2VsZWN0aW9uKSk7XG4gIGlmICh2YWx1ZXMubGVuZ3RoID09PSAwKSByZXR1cm4gW107XG4gIHJldHVybiBbe1xuICAgIC4uLnNoYXJlZCxcbiAgICB5OiB2YWx1ZXMsXG4gICAgbmFtZTogc2VsZWN0aW9uID8gJ1NlbGVjdGVkJyA6ICdBbGwnLFxuICAgIG1hcmtlcjogeyBjb2xvciB9LFxuICAgIGxpbmU6IHsgY29sb3IgfSxcbiAgfV07XG59XG5cbi8qKiBMYXlvdXQgZm9yIGEgY2hhcnQga2luZCDigJQgYXhpcyB0aXRsZXMgcmVmbGVjdCB0aGUgbG9nIHRvZ2dsZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBvbWljc0xheW91dChraW5kOiBPbWljc0NoYXJ0S2luZCwgaW5wdXQ6IE9taWNzVHJhY2VJbnB1dCk6IHVua25vd24ge1xuICBjb25zdCBsYWJlbCA9IGlucHV0LmxvZyA/IGBsb2cxcCgke2lucHV0Lm5hbWV9KWAgOiBpbnB1dC5uYW1lO1xuICBjb25zdCBiYXNlID0ge1xuICAgIG1hcmdpbjogeyB0OiAxMCwgcjogMTAsIGI6IDQwLCBsOiA1MiB9LFxuICAgIGF1dG9zaXplOiB0cnVlLFxuICAgIHNob3dsZWdlbmQ6IGtpbmQgPT09ICdoaXN0b2dyYW0nICYmICEhYWN0aXZlU2VsZWN0aW9uKGlucHV0LnNlbGVjdGlvbiksXG4gICAgbGVnZW5kOiB7IG9yaWVudGF0aW9uOiAnaCcsIHk6IDEuMTIsIHg6IDAgfSxcbiAgICBiYXJnYXA6IDAuMDIsXG4gICAgaG92ZXJtb2RlOiAnY2xvc2VzdCcsXG4gIH07XG4gIGlmIChraW5kID09PSAnaGlzdG9ncmFtJykge1xuICAgIHJldHVybiB7XG4gICAgICAuLi5iYXNlLFxuICAgICAgYmFybW9kZTogJ292ZXJsYXknLFxuICAgICAgeGF4aXM6IHsgdGl0bGU6IHsgdGV4dDogbGFiZWwgfSB9LFxuICAgICAgeWF4aXM6IHsgdGl0bGU6IHsgdGV4dDogJ29ic2VydmF0aW9ucycgfSB9LFxuICAgIH07XG4gIH1cbiAgcmV0dXJuIHtcbiAgICAuLi5iYXNlLFxuICAgIHhheGlzOiB7IGF1dG9tYXJnaW46IHRydWUgfSxcbiAgICB5YXhpczogeyB0aXRsZTogeyB0ZXh0OiBsYWJlbCB9LCB6ZXJvbGluZTogZmFsc2UgfSxcbiAgfTtcbn1cblxuLyoqIFdoZXRoZXIgYSBjaGFydCBraW5kIG5lZWRzIGEgY2F0ZWdvcmljYWwgZ3JvdXBpbmcgdG8gYmUgd29ydGggc2hvd2luZy4gKi9cbmV4cG9ydCBmdW5jdGlvbiBiZW5lZml0c0Zyb21Hcm91cGluZyhraW5kOiBPbWljc0NoYXJ0S2luZCk6IGJvb2xlYW4ge1xuICByZXR1cm4ga2luZCA9PT0gJ3Zpb2xpbicgfHwga2luZCA9PT0gJ2JveCc7XG59XG5cbi8qKiBDYXRlZ29yaWVzIHNob3duIGFzIGJhcnMgYmVmb3JlIHRoZSB0YWlsIGlzIGZvbGRlZCBpbnRvIG9uZSBcIm90aGVyXCIuICovXG5jb25zdCBNQVhfQ09VTlRfQkFSUyA9IDI1O1xuXG5leHBvcnQgaW50ZXJmYWNlIE9taWNzQ291bnRJbnB1dCB7XG4gIC8qKiBUaGUgY2F0ZWdvcmljYWwgYmVpbmcgY291bnRlZCwgd2l0aCB0aGUgbWFwJ3Mgb3duIGNvbG91cnMuICovXG4gIGdyb3VwOiBPbWljc0dyb3VwaW5nO1xuICAvKiogYDFgIG1hcmtzIGEgc2VsZWN0ZWQgb2JzZXJ2YXRpb247IHdoZW4gcHJlc2VudCB0aGUgYmFycyBzaG93IHRoZSBzZWxlY3Rpb25cbiAgICogIGFnYWluc3QgdGhlIHRvdGFsLiAqL1xuICBzZWxlY3Rpb24/OiBVaW50OEFycmF5IHwgbnVsbDtcbiAgLyoqIEJhcnMgYmVmb3JlIHRoZSB0YWlsIGlzIGFnZ3JlZ2F0ZWQuICovXG4gIG1heD86IG51bWJlcjtcbn1cblxuLyoqIFBlci1jYXRlZ29yeSBjb3VudHMsIGJpZ2dlc3QgZmlyc3QsIHdpdGggdGhlIHRhaWwgZm9sZGVkIGludG8gb25lIGJhci4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb3VudEJ5Q2F0ZWdvcnkoXG4gIGlucHV0OiBPbWljc0NvdW50SW5wdXQsXG4pOiB7IGxhYmVsczogc3RyaW5nW107IGNvbG9yczogc3RyaW5nW107IHRvdGFsczogbnVtYmVyW107IHNlbGVjdGVkOiBudW1iZXJbXSB9IHtcbiAgY29uc3QgeyBjb2RlcywgY2F0ZWdvcmllcywgY29sb3JzIH0gPSBpbnB1dC5ncm91cDtcbiAgY29uc3Qgc2VsZWN0aW9uID0gYWN0aXZlU2VsZWN0aW9uKGlucHV0LnNlbGVjdGlvbik7XG4gIGNvbnN0IHRvdGFscyA9IG5ldyBGbG9hdDY0QXJyYXkoY2F0ZWdvcmllcy5sZW5ndGgpO1xuICBjb25zdCBzZWxlY3RlZCA9IG5ldyBGbG9hdDY0QXJyYXkoY2F0ZWdvcmllcy5sZW5ndGgpO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGNvZGVzLmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgYyA9IGNvZGVzW2ldO1xuICAgIGlmIChjID09PSBOT19DQVRFR09SWSB8fCBjID49IGNhdGVnb3JpZXMubGVuZ3RoKSBjb250aW51ZTtcbiAgICB0b3RhbHNbY10rKztcbiAgICBpZiAoc2VsZWN0aW9uICYmIHNlbGVjdGlvbltpXSkgc2VsZWN0ZWRbY10rKztcbiAgfVxuXG4gIGNvbnN0IHJhbmtlZCA9IEFycmF5LmZyb20odG90YWxzLCAobiwgYykgPT4gKHsgYywgbiB9KSlcbiAgICAuZmlsdGVyKChyKSA9PiByLm4gPiAwKVxuICAgIC5zb3J0KChhLCBiKSA9PiBiLm4gLSBhLm4pO1xuICBjb25zdCBtYXggPSBpbnB1dC5tYXggPz8gTUFYX0NPVU5UX0JBUlM7XG4gIGNvbnN0IGhlYWQgPSByYW5rZWQuc2xpY2UoMCwgbWF4KTtcbiAgY29uc3QgdGFpbCA9IHJhbmtlZC5zbGljZShtYXgpO1xuXG4gIGNvbnN0IG91dCA9IHtcbiAgICBsYWJlbHM6IGhlYWQubWFwKChyKSA9PiBjYXRlZ29yaWVzW3IuY10pLFxuICAgIGNvbG9yczogaGVhZC5tYXAoKHIpID0+IGNvbG9yc1tyLmNdID8/IERFRkFVTFRfQ09MT1IpLFxuICAgIHRvdGFsczogaGVhZC5tYXAoKHIpID0+IHIubiksXG4gICAgc2VsZWN0ZWQ6IGhlYWQubWFwKChyKSA9PiBzZWxlY3RlZFtyLmNdKSxcbiAgfTtcbiAgLy8gVGhlIHRhaWwgaXMgYWdncmVnYXRlZCByYXRoZXIgdGhhbiBkcm9wcGVkOiAzMzggc3ViY2xhc3NlcyBkbyBub3QgZml0IGFcbiAgLy8gcmVhZGFibGUgYXhpcywgYnV0IHNpbGVudGx5IHNob3dpbmcgMjUgb2YgdGhlbSB3b3VsZCBtaXNzdGF0ZSB0aGUgd2hvbGUuXG4gIGlmICh0YWlsLmxlbmd0aCkge1xuICAgIG91dC5sYWJlbHMucHVzaChgb3RoZXIgKCR7dGFpbC5sZW5ndGh9IGNhdGVnb3JpZXMpYCk7XG4gICAgb3V0LmNvbG9ycy5wdXNoKCcjOWU5ZTllJyk7XG4gICAgb3V0LnRvdGFscy5wdXNoKHRhaWwucmVkdWNlKChhLCByKSA9PiBhICsgci5uLCAwKSk7XG4gICAgb3V0LnNlbGVjdGVkLnB1c2godGFpbC5yZWR1Y2UoKGEsIHIpID0+IGEgKyBzZWxlY3RlZFtyLmNdLCAwKSk7XG4gIH1cbiAgcmV0dXJuIG91dDtcbn1cblxuLyoqXG4gKiBCYXJzIG9mIHBlci1jYXRlZ29yeSBjZWxsIGNvdW50cyDigJQgdGhlIGRpc3RyaWJ1dGlvbiBhIENBVEVHT1JJQ0FMIGNvbG91clxuICogc291cmNlIGhhcy5cbiAqXG4gKiBBIGhpc3RvZ3JhbSBvZiBhIGNhdGVnb3J5IGNvZGUgd291bGQgYmUgbWVhbmluZ2xlc3MgKHRoZSBjb2RlcyBhcmUgbGFiZWxzLCBub3RcbiAqIG1hZ25pdHVkZXMpLCBidXQgXCJob3cgbWFueSBjZWxscyBwZXIgY2xhc3NcIiBpcyBhIHJlYWwgcXVlc3Rpb24sIGFuZCB0aGUgc2FtZSBvbmVcbiAqIHRoZSBsZWdlbmQgaW1wbGllcyB3aXRob3V0IGFuc3dlcmluZy4gSG9yaXpvbnRhbCwgYmVjYXVzZSB0YXhvbm9teSBsYWJlbHMgYXJlXG4gKiBsb25nLCBhbmQgc29ydGVkLCBiZWNhdXNlIHJhbmsgaXMgd2hhdCB0aGUgY2hhcnQgaXMgcmVhZCBmb3IuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBidWlsZENvdW50VHJhY2VzKGlucHV0OiBPbWljc0NvdW50SW5wdXQpOiB1bmtub3duW10ge1xuICBjb25zdCB7IGxhYmVscywgY29sb3JzLCB0b3RhbHMsIHNlbGVjdGVkIH0gPSBjb3VudEJ5Q2F0ZWdvcnkoaW5wdXQpO1xuICBjb25zdCBzZWxlY3Rpb24gPSBhY3RpdmVTZWxlY3Rpb24oaW5wdXQuc2VsZWN0aW9uKTtcbiAgLy8gUGxvdGx5IGRyYXdzIHRoZSBmaXJzdCBjYXRlZ29yeSBhdCB0aGUgQk9UVE9NIG9mIGEgaG9yaXpvbnRhbCBheGlzLCBzb1xuICAvLyByZXZlcnNlIHRvIHB1dCB0aGUgYmlnZ2VzdCBiYXIgYXQgdGhlIHRvcCB3aGVyZSB0aGUgZXllIHN0YXJ0cy5cbiAgY29uc3QgZmxpcCA9IDxUPihhOiBUW10pOiBUW10gPT4gYS5zbGljZSgpLnJldmVyc2UoKTtcbiAgY29uc3QgdHJhY2VzOiB1bmtub3duW10gPSBbe1xuICAgIHR5cGU6ICdiYXInLFxuICAgIG9yaWVudGF0aW9uOiAnaCcsXG4gICAgeDogZmxpcCh0b3RhbHMpLFxuICAgIHk6IGZsaXAobGFiZWxzKSxcbiAgICBuYW1lOiBzZWxlY3Rpb24gPyAnQWxsJyA6ICdDZWxscycsXG4gICAgbWFya2VyOiB7IGNvbG9yOiBmbGlwKGNvbG9ycykgfSxcbiAgICBvcGFjaXR5OiBzZWxlY3Rpb24gPyAwLjQ1IDogMSxcbiAgICBob3ZlcnRlbXBsYXRlOiAnJXt4fSBvYnM8ZXh0cmE+JXt5fTwvZXh0cmE+JyxcbiAgfV07XG4gIGlmIChzZWxlY3Rpb24pIHtcbiAgICB0cmFjZXMucHVzaCh7XG4gICAgICB0eXBlOiAnYmFyJyxcbiAgICAgIG9yaWVudGF0aW9uOiAnaCcsXG4gICAgICB4OiBmbGlwKHNlbGVjdGVkKSxcbiAgICAgIHk6IGZsaXAobGFiZWxzKSxcbiAgICAgIG5hbWU6ICdTZWxlY3RlZCcsXG4gICAgICBtYXJrZXI6IHsgY29sb3I6IGZsaXAoY29sb3JzKSB9LFxuICAgICAgaG92ZXJ0ZW1wbGF0ZTogJyV7eH0gb2JzPGV4dHJhPiV7eX0gwrcgc2VsZWN0ZWQ8L2V4dHJhPicsXG4gICAgfSk7XG4gIH1cbiAgcmV0dXJuIHRyYWNlcztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvdW50c0xheW91dChpbnB1dDogT21pY3NDb3VudElucHV0ICYgeyBuYW1lOiBzdHJpbmcgfSk6IHVua25vd24ge1xuICBjb25zdCB7IGxhYmVscyB9ID0gY291bnRCeUNhdGVnb3J5KGlucHV0KTtcbiAgcmV0dXJuIHtcbiAgICAvLyBSb29tIGZvciB0aGUgbGFiZWxzLCBhbmQgYSBoZWlnaHQgdGhhdCBncm93cyB3aXRoIHRoZSBiYXIgY291bnQgc28gMjVcbiAgICAvLyBjYXRlZ29yaWVzIGFyZSBub3QgY3J1c2hlZCBpbnRvIHRoZSBzYW1lIGJveCBhcyAzLlxuICAgIG1hcmdpbjogeyB0OiAxMCwgcjogMTAsIGI6IDQwLCBsOiA4IH0sXG4gICAgYXV0b3NpemU6IHRydWUsXG4gICAgLy8gT3ZlcmxhaWQsIG5vdCBzdGFja2VkOiB0aGUgc2VsZWN0ZWQgYmFyIHJlYWRzIGFnYWluc3QgdGhlIHRvdGFsIGl0IGNhbWVcbiAgICAvLyBmcm9tLCB3aGljaCBpcyB0aGUgY29tcGFyaXNvbiBiZWluZyBtYWRlLlxuICAgIGJhcm1vZGU6ICdvdmVybGF5JyxcbiAgICBzaG93bGVnZW5kOiAhIWFjdGl2ZVNlbGVjdGlvbihpbnB1dC5zZWxlY3Rpb24pLFxuICAgIGxlZ2VuZDogeyBvcmllbnRhdGlvbjogJ2gnLCB5OiAxLjEyLCB4OiAwIH0sXG4gICAgaG92ZXJtb2RlOiAnY2xvc2VzdCcsXG4gICAgeGF4aXM6IHsgdGl0bGU6IHsgdGV4dDogJ29ic2VydmF0aW9ucycgfSB9LFxuICAgIHlheGlzOiB7IGF1dG9tYXJnaW46IHRydWUsIHRpY2tsYWJlbHBvc2l0aW9uOiAnaW5zaWRlJywgdGlja2ZvbnQ6IHsgc2l6ZTogMTAgfSB9LFxuICAgIGhlaWdodDogTWF0aC5tYXgoMTgwLCAyMiAqIGxhYmVscy5sZW5ndGggKyA2MCksXG4gIH07XG59XG5cbi8vIOKUgOKUgCBoZWF0bWFwIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4vKipcbiAqIEdlbmVzIMOXIGdyb3VwcyBtZWFuIGV4cHJlc3Npb24sIGFzIGEgUGxvdGx5IGhlYXRtYXAuXG4gKlxuICogVGhlIG1hdHJpeCBpdHNlbGYgaXMgY29tcHV0ZWQgYnkgdGhlIHB1cmUgYGhlYXRtYXBNYXRyaXhgIGluXG4gKiBgc3BhdGlhbC9zcGF0aWFsLWhlYXRtYXAudHNgOyB0aGlzIG9ubHkgc2hhcGVzIGl0IGZvciBQbG90bHkuIEtlcHQgaW4gdGhlXG4gKiBtYXRyaXgncyBvd24gcm93LW1ham9yIG9yZGVyIGFuZCBmbGlwcGVkIG9uY2UgaGVyZSwgYmVjYXVzZSBQbG90bHkgZHJhd3NcbiAqIGB6WzBdYCBhdCB0aGUgQk9UVE9NIG9mIHRoZSB5IGF4aXMgd2hpbGUgdGhlIGNhbGxlciBsaXN0cyBnZW5lcyB0b3AtZG93bi5cbiAqXG4gKiBUaGUgY29sb3VyIHNjYWxlIGlzIGl0cyBvd24sIE5PVCB0aGUgbWFwJ3MgYGNvbnRpbnVvdXNDb2xvcm1hcGAuIFRoaXMgaXMgYVxuICogZGlmZmVyZW50IHF1YW50aXR5IOKAlCBhIHotc2NvcmVkIG1lYW4gcGVyIGdyb3VwLCBub3QgYSBwZXItY2VsbCB2YWx1ZSDigJQgc29cbiAqIHNoYXJpbmcgb25lIHNjYWxlIHdvdWxkIGludml0ZSByZWFkaW5nIGEgaGVhdG1hcCBjZWxsIGFzIGlmIGl0IHdlcmUgYSBtYXJrZXJcbiAqIGNvbG91ci4gRGl2ZXJnaW5nIGFuZCBjZW50cmVkIG9uIHplcm8sIGJlY2F1c2UgYWZ0ZXIgei1zY29yaW5nIHRoZSBzaWduIGlzXG4gKiB0aGUgcmVhZGluZzogYWJvdmUgb3IgYmVsb3cgdGhpcyBnZW5lJ3MgYXZlcmFnZSBhY3Jvc3MgdGhlIGdyb3Vwcy5cbiAqL1xuLyoqXG4gKiBBbiBlbWJlZGRpbmcgc2NhdHRlciDigJQgYSBVTUFQLCB0LVNORSBvciBQQ0EgcGxhbmUgb3ZlciB0aGUgc2FtZSBvYnNlcnZhdGlvbnMuXG4gKlxuICogQW5zd2VycyBhIGRpZmZlcmVudCBxdWVzdGlvbiBmcm9tIHRoZSBtYXA6IHRoZSBtYXAgc2F5cyBXSEVSRSBhIHBvcHVsYXRpb24gc2l0cyBpbiB0aGUgdGlzc3VlLFxuICogdGhlIGVtYmVkZGluZyBzYXlzIHdoaWNoIHBvcHVsYXRpb25zIHRoZXJlIGFyZSBhbmQgaG93IGNsb3NlIHRoZXkgYXJlIGluIGV4cHJlc3Npb24uIFJlYWQgc2lkZVxuICogYnkgc2lkZSwgYW5kIHNlbGVjdGluZyBpbiBvbmUgaGlnaGxpZ2h0aW5nIHRoZSBvdGhlciwgaXMgdGhlIHdob2xlIHBvaW50IG9mIGhhdmluZyBib3RoLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIE9taWNzRW1iZWRkaW5nSW5wdXQge1xuICAvKiogRW1iZWRkaW5nIGNvb3JkaW5hdGVzLCBpbmRleC1hbGlnbmVkIHdpdGggdGhlIG9ic2VydmF0aW9ucy4gKi9cbiAgeDogRmxvYXQzMkFycmF5O1xuICB5OiBGbG9hdDMyQXJyYXk7XG4gIC8qKlxuICAgKiBUaGlyZCBkaW1lbnNpb24sIHdoZW4gdGhlIGVtYmVkZGluZyBoYXMgb25lLlxuICAgKlxuICAgKiBJdHMgcHJlc2VuY2UgaXMgd2hhdCBzd2l0Y2hlcyB0aGUgcGxvdCB0byBhIHJvdGF0YWJsZSAzRCBzY2F0dGVyLiBBIDNEIGVtYmVkZGluZyBpc1xuICAgKiBhbHdheXMgQ09NUFVURUQg4oCUIGV2ZXJ5IGVtYmVkZGluZyBwdWJsaXNoZWQgd2l0aCBhIHNwYXRpYWwgZGF0YXNldCBpcyAyRCwgYmVjYXVzZSBhXG4gICAqIFVNQVAgZXhpc3RzIHRvIGJlIGxvb2tlZCBhdCDigJQgc28gYGRlcml2ZWRgIGlzIGV4cGVjdGVkIGFsb25nc2lkZSBpdC5cbiAgICovXG4gIHo/OiBGbG9hdDMyQXJyYXk7XG4gIC8qKiBBeGlzIGxhYmVsIHN0ZW0sIGUuZy4gXCJVTUFQXCIg4oCUIHRoZSBheGVzIGJlY29tZSBcIlVNQVAgMVwiIC8gXCJVTUFQIDJcIi4gKi9cbiAgbGFiZWw6IHN0cmluZztcbiAgLyoqIENvbG91ciBieSBhIGNhdGVnb3JpY2FsIGNvbHVtbjogcGVyLW9ic2VydmF0aW9uIGNvZGVzIHBsdXMgdGhlIGNhdGVnb3J5IG5hbWVzL2NvbG91cnMuICovXG4gIGNhdGVnb3JpZXM/OiB7XG4gICAgY29kZXM6IFVpbnQxNkFycmF5O1xuICAgIG5hbWVzOiByZWFkb25seSBzdHJpbmdbXTtcbiAgICBjb2xvcnM6IHJlYWRvbmx5IHN0cmluZ1tdO1xuICB9O1xuICAvKiogTWFya2VyIGRpYW1ldGVyIGluIHB4LiAqL1xuICBzaXplPzogbnVtYmVyO1xuICAvKipcbiAgICogU2VsZWN0aW9uIG1hc2sgb3ZlciB0aGUgb2JzZXJ2YXRpb25zLiBXaGVuIHByZXNlbnQsIHVuc2VsZWN0ZWQgcG9pbnRzIGFyZSBkaW1tZWQgcmF0aGVyIHRoYW5cbiAgICogZHJvcHBlZDogdGhlIHNoYXBlIG9mIHRoZSB3aG9sZSBlbWJlZGRpbmcgaXMgdGhlIGNvbnRleHQgdGhhdCBtYWtlcyBhIHNlbGVjdGlvbiBsZWdpYmxlLCBhbmRcbiAgICogcmVtb3ZpbmcgaXQgd291bGQgbGVhdmUgYSBoYW5kZnVsIG9mIGRvdHMgZmxvYXRpbmcgaW4gYW4gZW1wdHkgcGxhbmUuXG4gICAqL1xuICBzZWxlY3Rpb24/OiBVaW50OEFycmF5O1xuICAvKiogVHJ1ZSB3aGVuIHRoZSBjb29yZGluYXRlcyB3ZXJlIGNvbXB1dGVkIGhlcmUgcmF0aGVyIHRoYW4gcHVibGlzaGVkIHdpdGggdGhlIGRhdGFzZXQuICovXG4gIGRlcml2ZWQ/OiBib29sZWFuO1xuICAvKipcbiAgICogRnJhY3Rpb24gb2YgdmFyaWFuY2UgcGVyIGF4aXMsIGZvciBhIExJTkVBUiBlbWJlZGRpbmcuXG4gICAqXG4gICAqIFByZXNlbnQgZm9yIGEgUENBIGFuZCBhYnNlbnQgZm9yIGEgVU1BUCwgYW5kIHRoYXQgZGlmZmVyZW5jZSBpcyB0aGUgcG9pbnQgb2Ygc2hvd2luZ1xuICAgKiBhIFBDQSBhdCBhbGw6IGl0cyBheGVzIGFyZSBvcmRlcmVkIGFuZCBlYWNoIGV4cGxhaW5zIGEgbWVhc3VyYWJsZSBzaGFyZSBvZiB0aGVcbiAgICogdmFyaWFuY2UsIHNvIFwiUEMxICgyMyUpXCIgc2F5cyBzb21ldGhpbmcgYSByZWFkZXIgY2FuIGFjdCBvbi4gQSBVTUFQJ3MgY29vcmRpbmF0ZXMgYXJlXG4gICAqIGFuIGFyYml0cmFyeSBvdXRwdXQgb2YgYW4gb3B0aW1pc2F0aW9uIOKAlCB1bm9yZGVyZWQsIHVuaXRsZXNzLCBhbmQgcmVwcm9kdWNpYmxlIG9ubHlcbiAgICogdXAgdG8gYSByb3RhdGlvbiDigJQgc28gdGhlcmUgaXMgbm90aGluZyB0byBsYWJlbCB0aGVtIHdpdGguXG4gICAqL1xuICB2YXJpYW5jZVJhdGlvPzogbnVtYmVyW107XG4gIC8qKlxuICAgKiBUaGUgdmlldyB0byBrZWVwIGFjcm9zcyBhIHJlZHJhdy5cbiAgICpcbiAgICogYFBsb3RseS5yZWFjdGAgcmVzZXRzIHRoZSAzRCBzY2VuZSBjYW1lcmEgYW5kIGFueSB6b29tZWQgMkQgYXhpcyByYW5nZSB1bmxlc3MgdGhlXG4gICAqIGxheW91dCBjYXJyaWVzIHRoZW0sIHNvIHJlY29sb3VyaW5nIG9yIHNlbGVjdGluZyB3b3VsZCB0aHJvdyBhd2F5IGFuIG9yaWVudGF0aW9uIHRoZVxuICAgKiB1c2VyIGhhZCBzZXQgdXAuIFJvdGF0aW5nIGEgY2xvdWQgdG8gc2VlIGEgc3RydWN0dXJlIGFuZCBsb3NpbmcgaXQgb24gdGhlIG5leHQgY2xpY2tcbiAgICogbWFrZXMgdGhlIHBsb3QgdW51c2FibGUgZm9yIHRoZSB0aGluZyBpdCBpcyBmb3IuXG4gICAqXG4gICAqIGBjYW1lcmFgIGlzIGEgM0Qgc2NlbmUgY2FtZXJhOyBgcmFuZ2VzYCBhcmUgMkQgYXhpcyByYW5nZXMsIGFuZCBhcmUgb25seSBwYXNzZWQgd2hlblxuICAgKiB0aGUgdXNlciBoYXMgYWN0dWFsbHkgem9vbWVkIOKAlCBmcmVlemluZyBhbiBhdXRvcmFuZ2VkIGF4aXMgd291bGQgc3RvcCB0aGUgcGxvdFxuICAgKiByZS1maXR0aW5nIHdoZW4gdGhlIGRhdGEgY2hhbmdlcy5cbiAgICovXG4gIHZpZXc/OiB7IGNhbWVyYT86IHVua25vd247IHJhbmdlcz86IHsgeDogdW5rbm93bjsgeTogdW5rbm93biB9IH07XG59XG5cbi8qKlxuICogUGFzdCB0aGlzIG1hbnkgY2F0ZWdvcmllcywgb25lIHRyYWNlIGVhY2ggc3RvcHMgYmVpbmcgd29ydGggaXQuXG4gKlxuICogQSB0cmFjZSBwZXIgY2F0ZWdvcnkgYnV5cyBhIGxlZ2VuZCB3aG9zZSBlbnRyaWVzIHRvZ2dsZSDigJQgY2xpY2sgb25lIHRvIGlzb2xhdGUgYSBwb3B1bGF0aW9uLFxuICogd2hpY2ggaXMgaG93IHRoZXNlIHBsb3RzIGFyZSBhY3R1YWxseSByZWFkLiBCdXQgUGxvdGx5IGhvbGRzIHBlci10cmFjZSBzdGF0ZSwgYW5kIGEgbGVnZW5kIG9mXG4gKiBodW5kcmVkcyBvZiBlbnRyaWVzIGlzIHVucmVhZGFibGUgYW55d2F5ICh0aGUgQUJDIGF0bGFzIGhhcyAzMzggc3ViY2xhc3NlcyksIHNvIGJleW9uZCB0aGUgY2FwXG4gKiBldmVyeXRoaW5nIGdvZXMgaW50byBPTkUgdHJhY2Ugd2l0aCBhIHBlci1wb2ludCBjb2xvdXI6IG5vIGxlZ2VuZCwgYnV0IGl0IHN0aWxsIGRyYXdzLlxuICovXG5leHBvcnQgY29uc3QgRU1CRURESU5HX01BWF9MRUdFTkRfQ0FURUdPUklFUyA9IDQwO1xuXG4vKipcbiAqIENhdGVnb3JpZXMgcGFzdCB3aGljaCB0aGUgbGVnZW5kIGlzIG5vdCBEUkFXTiwgdGhvdWdoIHRoZSB0cmFjZXMgc3RheSBzcGxpdC5cbiAqXG4gKiBBIGxlZ2VuZCBpcyBvbmx5IHdvcnRoIGl0cyBzcGFjZSBpZiBpdCBmaXRzLiBzZXFGSVNIJ3MgMjIgY2VsbCB0eXBlcyBzdGFjayBpbnRvIG9uZSB0YWxsXG4gKiBjb2x1bW4gaW4gYSBkb2NrZWQgcGFuZWwsIG92ZXJsYXBwaW5nIHRoZSBheGlzIHRpdGxlIGFuZCBydW5uaW5nIG9mZiB0aGUgYm90dG9tIOKAlCBhbmRcbiAqIHRoZSBjb2xvdXJzIGFscmVhZHkgbWF0Y2ggdGhlIG1hcCwgd2hvc2Ugb3duIHBhbmVsIGxpc3RzIHRoZW0sIHdoaWxlIGhvdmVyIG5hbWVzIHRoZVxuICogY2F0ZWdvcnkgdW5kZXIgdGhlIGN1cnNvci4gU28gcGFzdCB0aGlzIHRoZSBwbG90IHRha2VzIHRoZSB3aG9sZSBwYW5lbCBhbmQgdGhlIGxlZ2VuZFxuICogaXMgZHJvcHBlZCByYXRoZXIgdGhhbiBzaG93biBiYWRseS5cbiAqL1xuZXhwb3J0IGNvbnN0IEVNQkVERElOR19NQVhfTEVHRU5EX1NIT1dOID0gMTA7XG5cbi8qKiBEaW1taW5nIGFwcGxpZWQgdG8gdW5zZWxlY3RlZCBwb2ludHMsIG1hdGNoaW5nIHRoZSBtYXAncyBtdXRlZCBvcGFjaXR5LiAqL1xuY29uc3QgRU1CRURESU5HX01VVEVEX09QQUNJVFkgPSAwLjE1O1xuXG4vKipcbiAqIE1peCBhIGhleCBjb2xvdXIgdG93YXJkIHdoaXRlIGJ5IGBhbW91bnRgLlxuICpcbiAqIEhvdyB1bnNlbGVjdGVkIHBvaW50cyBhcmUgZGltbWVkIGluIDNELiBgc2NhdHRlcjNkYCBhY2NlcHRzIGBtYXJrZXIub3BhY2l0eWAgb25seSBhcyBhXG4gKiBTQ0FMQVIg4oCUIGEgcGVyLXBvaW50IGFycmF5IGlzIHNpbGVudGx5IGNvbGxhcHNlZCB0byB0aGUgZmlyc3QgdmFsdWUsIHZlcmlmaWVkIGFnYWluc3RcbiAqIHRoZSBidW5kbGVkIFBsb3RseSDigJQgc28gdGhlIHBlci1wb2ludCBvcGFjaXR5IHRoYXQgZGltcyB0aGUgMkQgcGxvdCBkb2VzIG5vdGhpbmcgdGhlcmUsXG4gKiBhbmQgYSBzZWxlY3Rpb24gYXBwZWFyZWQgdG8gaGlnaGxpZ2h0IG5vdGhpbmcgaW4gM0QuIENvbG91ciBhcnJheXMgQVJFIGhvbm91cmVkLCBzb1xuICogdGhlIHNhbWUgZWZmZWN0IGlzIHByb2R1Y2VkIGJ5IG1peGluZyB0b3dhcmQgdGhlIHBhcGVyIGluc3RlYWQuXG4gKi9cbmZ1bmN0aW9uIHRvd2FyZFBhcGVyKGhleDogc3RyaW5nLCBhbW91bnQ6IG51bWJlcik6IHN0cmluZyB7XG4gIGNvbnN0IGNsZWFuID0gaGV4LnJlcGxhY2UoJyMnLCAnJyk7XG4gIGNvbnN0IGZ1bGwgPSBjbGVhbi5sZW5ndGggPT09IDNcbiAgICA/IGNsZWFuLnNwbGl0KCcnKS5tYXAoKGMpID0+IGMgKyBjKS5qb2luKCcnKVxuICAgIDogY2xlYW47XG4gIGNvbnN0IG4gPSBOdW1iZXIucGFyc2VJbnQoZnVsbCwgMTYpO1xuICBpZiAoIU51bWJlci5pc0Zpbml0ZShuKSB8fCBmdWxsLmxlbmd0aCAhPT0gNikgcmV0dXJuIGhleDtcbiAgY29uc3QgbWl4ID0gKGM6IG51bWJlcik6IG51bWJlciA9PiBNYXRoLnJvdW5kKGMgKyAoMjU1IC0gYykgKiBhbW91bnQpO1xuICBjb25zdCByID0gbWl4KChuID4+IDE2KSAmIDB4ZmYpO1xuICBjb25zdCBnID0gbWl4KChuID4+IDgpICYgMHhmZik7XG4gIGNvbnN0IGIgPSBtaXgobiAmIDB4ZmYpO1xuICByZXR1cm4gYCMkeygociA8PCAxNikgfCAoZyA8PCA4KSB8IGIpLnRvU3RyaW5nKDE2KS5wYWRTdGFydCg2LCAnMCcpfWA7XG59XG5cbmNvbnN0IERFRkFVTFRfRU1CRURESU5HX1BPSU5UX1NJWkUgPSA0O1xuXG5leHBvcnQgZnVuY3Rpb24gYnVpbGRFbWJlZGRpbmdUcmFjZXMoaW5wdXQ6IE9taWNzRW1iZWRkaW5nSW5wdXQpOiB1bmtub3duW10ge1xuICBjb25zdCB7IHgsIHksIGNhdGVnb3JpZXMsIHNlbGVjdGlvbiB9ID0gaW5wdXQ7XG4gIGNvbnN0IG4gPSBNYXRoLm1pbih4Lmxlbmd0aCwgeS5sZW5ndGgpO1xuICBpZiAobiA9PT0gMCkgcmV0dXJuIFtdO1xuICBjb25zdCB6ID0gaW5wdXQuejtcbiAgLy8gYHNjYXR0ZXJnbGAgZm9yIHRoZSBwbGFuZSDigJQgMTBeNC0xMF42IHBvaW50cywgd2hpY2ggdGhlIFNWRyByZW5kZXJlciB3b3VsZCBub3RcbiAgLy8gc3Vydml2ZS4gYHNjYXR0ZXIzZGAgZm9yIHRoZSBjbG91ZDogV2ViR0wgdG9vLCBidXQgYSBkaWZmZXJlbnQgdHJhY2UgdHlwZSB3aXRoIGl0c1xuICAvLyBvd24gc2NlbmUsIHdoaWNoIGlzIHdoeSBhIHRoaXJkIGRpbWVuc2lvbiBjYW5ub3QganVzdCBiZSBhZGRlZCB0byB0aGUgZm9ybWVyLiBJdFxuICAvLyBjb3N0cyBtb3JlIHBlciBwb2ludCwgc28gaXQgc3VpdHMgdGhpcyBzY2FsZSBhbmQgbm90IG1pbGxpb25zLlxuICBjb25zdCB0eXBlID0geiA/ICdzY2F0dGVyM2QnIDogJ3NjYXR0ZXJnbCc7XG4gIGNvbnN0IG1hcmtlclNpemUgPSB6ID8gTWF0aC5tYXgoMSwgKGlucHV0LnNpemUgPz8gREVGQVVMVF9FTUJFRERJTkdfUE9JTlRfU0laRSkgLSAyKVxuICAgIDogKGlucHV0LnNpemUgPz8gREVGQVVMVF9FTUJFRERJTkdfUE9JTlRfU0laRSk7XG4gIGNvbnN0IG9wYWNpdHlGb3IgPSAoaTogbnVtYmVyKTogbnVtYmVyID0+XG4gICAgIXNlbGVjdGlvbiB8fCBzZWxlY3Rpb25baV0gPyAxIDogRU1CRURESU5HX01VVEVEX09QQUNJVFk7XG4gIGNvbnN0IGlzU2VsZWN0ZWQgPSAoaTogbnVtYmVyKTogYm9vbGVhbiA9PiAhc2VsZWN0aW9uIHx8IHNlbGVjdGlvbltpXSA9PT0gMTtcbiAgLy8gSW4gM0QgdGhlIGRpbW1pbmcgaGFzIHRvIGJlIGluIHRoZSBDT0xPVVIsIHNpbmNlIGEgcGVyLXBvaW50IG9wYWNpdHkgYXJyYXkgaXNcbiAgLy8gaWdub3JlZCB0aGVyZS4gQ2FjaGVkIHBlciBpbnB1dCBjb2xvdXI6IDE5ayBsb29rdXBzIG9mIHRoZSBzYW1lIGZldyBjYXRlZ29yaWVzLlxuICBjb25zdCBkaW1DYWNoZSA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmc+KCk7XG4gIGNvbnN0IHNoYWRlID0gKGhleDogc3RyaW5nLCBpOiBudW1iZXIpOiBzdHJpbmcgPT4ge1xuICAgIGlmICgheiB8fCBpc1NlbGVjdGVkKGkpKSByZXR1cm4gaGV4O1xuICAgIGNvbnN0IGhpdCA9IGRpbUNhY2hlLmdldChoZXgpO1xuICAgIGlmIChoaXQpIHJldHVybiBoaXQ7XG4gICAgY29uc3QgZGltbWVkID0gdG93YXJkUGFwZXIoaGV4LCAxIC0gRU1CRURESU5HX01VVEVEX09QQUNJVFkpO1xuICAgIGRpbUNhY2hlLnNldChoZXgsIGRpbW1lZCk7XG4gICAgcmV0dXJuIGRpbW1lZDtcbiAgfTtcbiAgLyoqIFBlci1wb2ludCBvcGFjaXR5LCB3aGljaCBvbmx5IHRoZSAyRCB0cmFjZSB0eXBlcyBob25vdXIuICovXG4gIGNvbnN0IG9wYWNpdHlBcnJheSA9IChpbmRpY2VzOiByZWFkb25seSBudW1iZXJbXSk6IG51bWJlcltdIHwgdW5kZWZpbmVkID0+XG4gICAgc2VsZWN0aW9uICYmICF6ID8gaW5kaWNlcy5tYXAob3BhY2l0eUZvcikgOiB1bmRlZmluZWQ7XG5cbiAgaWYgKCFjYXRlZ29yaWVzIHx8IGNhdGVnb3JpZXMubmFtZXMubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIFt7XG4gICAgICB0eXBlLFxuICAgICAgbW9kZTogJ21hcmtlcnMnLFxuICAgICAgeDogQXJyYXkuZnJvbSh4LnN1YmFycmF5KDAsIG4pKSxcbiAgICAgIHk6IEFycmF5LmZyb20oeS5zdWJhcnJheSgwLCBuKSksXG4gICAgICAuLi4oeiA/IHsgejogQXJyYXkuZnJvbSh6LnN1YmFycmF5KDAsIG4pKSB9IDoge30pLFxuICAgICAgLy8gVGhlIE9CU0VSVkFUSU9OIGluZGV4IHBlciBwb2ludCwgY2FycmllZCB0aHJvdWdoIHNvIGEgbGFzc28gY2FuIGJlIHR1cm5lZCBiYWNrXG4gICAgICAvLyBpbnRvIGEgc2VsZWN0aW9uLiBQbG90bHkgcmVwb3J0cyBhIHNlbGVjdGVkIHBvaW50IGJ5IGl0cyBwb3NpdGlvbiB3aXRoaW4gaXRzXG4gICAgICAvLyB0cmFjZSwgd2hpY2ggaXMgbm90IHRoZSBvYnNlcnZhdGlvbiBpbmRleCBvbmNlIHRoZSBwb2ludHMgYXJlIHNwbGl0IGJ5IGNhdGVnb3J5LlxuICAgICAgY3VzdG9tZGF0YTogQXJyYXkuZnJvbSh7IGxlbmd0aDogbiB9LCAoXywgaSkgPT4gaSksXG4gICAgICBtYXJrZXI6IHtcbiAgICAgICAgc2l6ZTogbWFya2VyU2l6ZSxcbiAgICAgICAgLy8gQSBmbGF0IGNvbG91ciBub3JtYWxseTsgcGVyIHBvaW50IG9uY2UgYSBzZWxlY3Rpb24gaGFzIHRvIGRpbSBzb21lIG9mIHRoZW0gaW5cbiAgICAgICAgLy8gM0QsIHdoZXJlIG9wYWNpdHkgYXJyYXlzIGFyZSBpZ25vcmVkLlxuICAgICAgICBjb2xvcjogc2VsZWN0aW9uICYmIHpcbiAgICAgICAgICA/IEFycmF5LmZyb20oeyBsZW5ndGg6IG4gfSwgKF8sIGkpID0+IHNoYWRlKCcjNGM3MmIwJywgaSkpXG4gICAgICAgICAgOiAnIzRjNzJiMCcsXG4gICAgICAgIC4uLihvcGFjaXR5QXJyYXkoQXJyYXkuZnJvbSh7IGxlbmd0aDogbiB9LCAoXywgaSkgPT4gaSkpXG4gICAgICAgICAgPyB7IG9wYWNpdHk6IEFycmF5LmZyb20oeyBsZW5ndGg6IG4gfSwgKF8sIGkpID0+IG9wYWNpdHlGb3IoaSkpIH1cbiAgICAgICAgICA6IHt9KSxcbiAgICAgIH0sXG4gICAgICBob3ZlcmluZm86ICdub25lJyxcbiAgICAgIHNob3dsZWdlbmQ6IGZhbHNlLFxuICAgIH1dO1xuICB9XG5cbiAgY29uc3QgeyBjb2RlcywgbmFtZXMsIGNvbG9ycyB9ID0gY2F0ZWdvcmllcztcbiAgaWYgKG5hbWVzLmxlbmd0aCA+IEVNQkVERElOR19NQVhfTEVHRU5EX0NBVEVHT1JJRVMpIHtcbiAgICAvLyBPbmUgdHJhY2UsIHBlci1wb2ludCBjb2xvdXIuIEhvdmVyIHN0aWxsIG5hbWVzIHRoZSBjYXRlZ29yeSwgd2hpY2ggaXMgdGhlIHBhcnQgdGhhdFxuICAgIC8vIG1hdHRlcnM7IHRoZSBsZWdlbmQgaXMgd2hhdCBpcyBsb3N0LlxuICAgIHJldHVybiBbe1xuICAgICAgdHlwZSxcbiAgICAgIG1vZGU6ICdtYXJrZXJzJyxcbiAgICAgIHg6IEFycmF5LmZyb20oeC5zdWJhcnJheSgwLCBuKSksXG4gICAgICB5OiBBcnJheS5mcm9tKHkuc3ViYXJyYXkoMCwgbikpLFxuICAgICAgLi4uKHogPyB7IHo6IEFycmF5LmZyb20oei5zdWJhcnJheSgwLCBuKSkgfSA6IHt9KSxcbiAgICAgIG1hcmtlcjoge1xuICAgICAgICBzaXplOiBtYXJrZXJTaXplLFxuICAgICAgICBjb2xvcjogQXJyYXkuZnJvbSh7IGxlbmd0aDogbiB9LCAoXywgaSkgPT4gc2hhZGUoY29sb3JzW2NvZGVzW2ldXSA/PyAnIzk5OTk5OScsIGkpKSxcbiAgICAgICAgLi4uKG9wYWNpdHlBcnJheShBcnJheS5mcm9tKHsgbGVuZ3RoOiBuIH0sIChfLCBpKSA9PiBpKSlcbiAgICAgICAgICA/IHsgb3BhY2l0eTogQXJyYXkuZnJvbSh7IGxlbmd0aDogbiB9LCAoXywgaSkgPT4gb3BhY2l0eUZvcihpKSkgfVxuICAgICAgICAgIDoge30pLFxuICAgICAgfSxcbiAgICAgIGN1c3RvbWRhdGE6IEFycmF5LmZyb20oeyBsZW5ndGg6IG4gfSwgKF8sIGkpID0+IGkpLFxuICAgICAgdGV4dDogQXJyYXkuZnJvbSh7IGxlbmd0aDogbiB9LCAoXywgaSkgPT4gbmFtZXNbY29kZXNbaV1dID8/ICd1bmFzc2lnbmVkJyksXG4gICAgICBob3ZlcnRlbXBsYXRlOiAnJXt0ZXh0fTxleHRyYT48L2V4dHJhPicsXG4gICAgICBzaG93bGVnZW5kOiBmYWxzZSxcbiAgICB9XTtcbiAgfVxuXG4gIC8vIE9uZSB0cmFjZSBwZXIgY2F0ZWdvcnk6IHRoZSBsZWdlbmQgZW50cmllcyB0b2dnbGUsIHNvIGEgcG9wdWxhdGlvbiBjYW4gYmUgaXNvbGF0ZWQgYnlcbiAgLy8gY2xpY2tpbmcgcmF0aGVyIHRoYW4gYnkgcmUtY29sb3VyaW5nIHRoZSB3aG9sZSBwbG90LlxuICBjb25zdCBidWNrZXRzOiBudW1iZXJbXVtdID0gbmFtZXMubWFwKCgpID0+IFtdKTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBuOyBpKyspIHtcbiAgICBjb25zdCBjID0gY29kZXNbaV07XG4gICAgLy8gTk9fQ0FURUdPUlksIG9yIGEgY29kZSBwYXN0IHRoZSBsaXN0LCBpcyB1bmFzc2lnbmVkIOKAlCBkcm9wcGVkIHJhdGhlciB0aGFuIGRyYXduIGluIGFcbiAgICAvLyBjb2xvdXIgdGhhdCB3b3VsZCByZWFkIGFzIGEgcG9wdWxhdGlvbiBvZiBpdHMgb3duLlxuICAgIGlmIChjIDwgYnVja2V0cy5sZW5ndGgpIGJ1Y2tldHNbY10ucHVzaChpKTtcbiAgfVxuICByZXR1cm4gYnVja2V0c1xuICAgIC5tYXAoKGlkeCwgYykgPT4gKHsgaWR4LCBjIH0pKVxuICAgIC5maWx0ZXIoKHsgaWR4IH0pID0+IGlkeC5sZW5ndGggPiAwKVxuICAgIC5tYXAoKHsgaWR4LCBjIH0pID0+ICh7XG4gICAgICB0eXBlLFxuICAgICAgbW9kZTogJ21hcmtlcnMnLFxuICAgICAgbmFtZTogbmFtZXNbY10sXG4gICAgICB4OiBpZHgubWFwKChpKSA9PiB4W2ldKSxcbiAgICAgIHk6IGlkeC5tYXAoKGkpID0+IHlbaV0pLFxuICAgICAgLi4uKHogPyB7IHo6IGlkeC5tYXAoKGkpID0+IHpbaV0pIH0gOiB7fSksXG4gICAgICAvLyBUaGlzIHRyYWNlJ3MgcG9pbnRzIEFSRSBhIHN1YnNldCwgc28gdGhlIG9ic2VydmF0aW9uIGluZGV4IGhhcyB0byB0cmF2ZWwgd2l0aFxuICAgICAgLy8gdGhlbTsgYHBvaW50SW5kZXhgIGFsb25lIHdvdWxkIGFkZHJlc3MgdGhlIHdyb25nIGNlbGwuXG4gICAgICBjdXN0b21kYXRhOiBpZHgsXG4gICAgICBtYXJrZXI6IHtcbiAgICAgICAgc2l6ZTogbWFya2VyU2l6ZSxcbiAgICAgICAgY29sb3I6IHNlbGVjdGlvbiAmJiB6XG4gICAgICAgICAgPyBpZHgubWFwKChpKSA9PiBzaGFkZShjb2xvcnNbY10gPz8gJyM5OTk5OTknLCBpKSlcbiAgICAgICAgICA6IChjb2xvcnNbY10gPz8gJyM5OTk5OTknKSxcbiAgICAgICAgLi4uKG9wYWNpdHlBcnJheShpZHgpID8geyBvcGFjaXR5OiBpZHgubWFwKG9wYWNpdHlGb3IpIH0gOiB7fSksXG4gICAgICB9LFxuICAgICAgLy8gU3BsaXQgcGVyIGNhdGVnb3J5IHJlZ2FyZGxlc3MsIHNvIGhvdmVyIG5hbWVzIGl0IGFuZCBhIGZ1dHVyZSBpc29sYXRlIGNvbnRyb2xcbiAgICAgIC8vIGhhcyBzb21ldGhpbmcgdG8gdG9nZ2xlOyBvbmx5IHRoZSBsZWdlbmQncyBWSVNJQklMSVRZIGRlcGVuZHMgb24gdGhlIGNvdW50LlxuICAgICAgc2hvd2xlZ2VuZDogbmFtZXMubGVuZ3RoIDw9IEVNQkVERElOR19NQVhfTEVHRU5EX1NIT1dOLFxuICAgICAgaG92ZXJ0ZW1wbGF0ZTogYCR7bmFtZXNbY119PGV4dHJhPjwvZXh0cmE+YCxcbiAgICB9KSk7XG59XG5cbi8qKlxuICogQXhpcyB0aXRsZTogdGhlIGVtYmVkZGluZydzIG5hbWUsIHRoZSBheGlzIG51bWJlciwgYW5kIGl0cyB2YXJpYW5jZSBzaGFyZSB3aGVuIHRoZXJlIGlzXG4gKiBvbmUgdG8gcmVwb3J0LlxuICovXG5mdW5jdGlvbiBheGlzVGl0bGUoc3RlbTogc3RyaW5nLCBheGlzOiBudW1iZXIsIHZhcmlhbmNlUmF0aW8/OiBudW1iZXJbXSk6IHN0cmluZyB7XG4gIGNvbnN0IHNoYXJlID0gdmFyaWFuY2VSYXRpbz8uW2F4aXMgLSAxXTtcbiAgaWYgKHNoYXJlID09PSB1bmRlZmluZWQgfHwgIU51bWJlci5pc0Zpbml0ZShzaGFyZSkpIHJldHVybiBgJHtzdGVtfSAke2F4aXN9YDtcbiAgLy8gT25lIGRlY2ltYWw6IHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gMjMuNCUgYW5kIDIzJSBpcyB3b3J0aCBzZWVpbmcgd2hlbiBjb21wYXJpbmdcbiAgLy8gY29tcG9uZW50cywgYW5kIG1vcmUgZGlnaXRzIHRoYW4gdGhhdCBpcyBub2lzZSBmcm9tIHRoZSBlc3RpbWF0ZS5cbiAgcmV0dXJuIGAke3N0ZW19ICR7YXhpc30gKCR7KHNoYXJlICogMTAwKS50b0ZpeGVkKDEpfSUpYDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGVtYmVkZGluZ0xheW91dChpbnB1dDogT21pY3NFbWJlZGRpbmdJbnB1dCk6IHVua25vd24ge1xuICBjb25zdCBzdGVtID0gaW5wdXQubGFiZWwgfHwgJ0VtYmVkZGluZyc7XG4gIGNvbnN0IHZyID0gaW5wdXQudmFyaWFuY2VSYXRpbztcbiAgLy8gTm8gYW5ub3RhdGlvbiBmb3IgYSBkZXJpdmVkIGVtYmVkZGluZy4gVGhhdCBhIHJlY29tcHV0ZWQgVU1BUCBpcyBub3QgdGhlIHB1Ymxpc2hlZFxuICAvLyBwaWN0dXJlIGRvZXMgbmVlZCBzYXlpbmcg4oCUIGJ1dCB0aGUgcGFuZWwncyBjYXB0aW9uIGFscmVhZHkgc2F5cyBpdCwgYW5kIHJlcGVhdGluZyBpdFxuICAvLyBvdmVyIHRoZSBwbG90IGNvc3QgYSBzdHJpcCBvZiB0aGUgcGFuZWwncyBoZWlnaHQgYW5kLCBpbiB0aGUgM0Qgc2NlbmUsIHNhdCBvbiB0b3Agb2ZcbiAgLy8gdGhlIGNsb3VkLiBPbmUgc3RhdGVtZW50LCBpbiB0aGUgY2FwdGlvbi5cblxuICBpZiAoaW5wdXQueikge1xuICAgIHJldHVybiB7XG4gICAgICBhdXRvc2l6ZTogdHJ1ZSxcbiAgICAgIC8vIEEgM0Qgc2NlbmUgaGFzIG5vIG1hcmdpbnMgdG8gc3BlYWsgb2Y7IHRoZSBheGVzIGxpdmUgaW5zaWRlIGl0LlxuICAgICAgbWFyZ2luOiB7IGw6IDAsIHI6IDAsIHQ6IDAsIGI6IDAgfSxcbiAgICAgIHNjZW5lOiB7XG4gICAgICAgIHhheGlzOiB7IHRpdGxlOiB7IHRleHQ6IGF4aXNUaXRsZShzdGVtLCAxLCB2cikgfSB9LFxuICAgICAgICB5YXhpczogeyB0aXRsZTogeyB0ZXh0OiBheGlzVGl0bGUoc3RlbSwgMiwgdnIpIH0gfSxcbiAgICAgICAgemF4aXM6IHsgdGl0bGU6IHsgdGV4dDogYXhpc1RpdGxlKHN0ZW0sIDMsIHZyKSB9IH0sXG4gICAgICAgIC8vIENhcnJpZWQgYWNyb3NzIHRoZSByZWRyYXcsIG9yIGV2ZXJ5IHJlY29sb3VyIHNuYXBzIHRoZSBjbG91ZCBiYWNrIHRvIGl0c1xuICAgICAgICAvLyBkZWZhdWx0IG9yaWVudGF0aW9uLlxuICAgICAgICAuLi4oaW5wdXQudmlldz8uY2FtZXJhID8geyBjYW1lcmE6IGlucHV0LnZpZXcuY2FtZXJhIH0gOiB7fSksXG4gICAgICAgIC8vIEVxdWFsIGFzcGVjdCBmb3IgdGhlIHNhbWUgcmVhc29uIGFzIHRoZSBwbGFuZTogdGhlIGF4ZXMgY2Fycnkgbm8gdW5pdHMsIHNvXG4gICAgICAgIC8vIGRpc3RhbmNlcyBvbmx5IGNvbXBhcmUgaWYgYWxsIHRocmVlIGFyZSBzY2FsZWQgYWxpa2UuIGBkYXRhYCBrZWVwcyB0aGUgY2xvdWQnc1xuICAgICAgICAvLyBvd24gcHJvcG9ydGlvbnMgaW5zdGVhZCBvZiBzdHJldGNoaW5nIGl0IGludG8gdGhlIGN1YmUuXG4gICAgICAgIGFzcGVjdG1vZGU6ICdkYXRhJyxcbiAgICAgIH0sXG4gICAgICBzaG93bGVnZW5kOiBmYWxzZSxcbiAgICAgIGhvdmVybW9kZTogJ2Nsb3Nlc3QnLFxuICAgIH07XG4gIH1cblxuICByZXR1cm4ge1xuICAgIGF1dG9zaXplOiB0cnVlLFxuICAgIC8vIEJvdHRvbSBtYXJnaW4gY2FycmllcyB0aGUgbGVnZW5kOiBsYWlkIG91dCBCRUxPVyB0aGUgcGxvdCByYXRoZXIgdGhhbiBiZXNpZGUgaXQuXG4gICAgLy8gQSB2ZXJ0aWNhbCBsZWdlbmQgb2YgMjIgY2VsbCB0eXBlcyB0b29rIGhhbGYgYSBkb2NrZWQgcGFuZWwncyB3aWR0aCBhbmQgc3F1ZWV6ZWRcbiAgICAvLyB0aGUgZW1iZWRkaW5nIGludG8gYSBjb3JuZXIg4oCUIGFuZCB0aGUgc2hhcGVzIG9mIHRoZSBjbHVzdGVycyBhcmUgdGhlIHRoaW5nIGJlaW5nXG4gICAgLy8gcmVhZCBoZXJlLCB3aGlsZSB0aGUgbGFiZWxzIGFyZSBhIGxvb2t1cC5cbiAgICBtYXJnaW46IHtcbiAgICAgIGw6IDQ0LFxuICAgICAgcjogOCxcbiAgICAgIHQ6IDgsXG4gICAgICAvLyBSb29tIGZvciB0aGUgbGVnZW5kIG9ubHkgd2hlbiBvbmUgaXMgZHJhd247IG90aGVyd2lzZSB0aGUgYXhpcyB0aXRsZSBhbG9uZS5cbiAgICAgIGI6IChpbnB1dC5jYXRlZ29yaWVzPy5uYW1lcy5sZW5ndGggPz8gMCkgPiAwXG4gICAgICAgICYmIChpbnB1dC5jYXRlZ29yaWVzPy5uYW1lcy5sZW5ndGggPz8gMCkgPD0gRU1CRURESU5HX01BWF9MRUdFTkRfU0hPV04gPyA2NCA6IDQwLFxuICAgIH0sXG4gICAgLy8gRXF1YWwgYXNwZWN0OiBhbiBlbWJlZGRpbmcncyBheGVzIGNhcnJ5IG5vIHVuaXRzLCBzbyBkaXN0YW5jZXMgYXJlIG9ubHkgY29tcGFyYWJsZSBpZiB0aGVcbiAgICAvLyB0d28gYXJlIHNjYWxlZCBhbGlrZS4gU3RyZXRjaGluZyBvbmUgYXhpcyB0byBmaWxsIHRoZSBwYW5lbCBpbnZlbnRzIHN0cnVjdHVyZS5cbiAgICB4YXhpczoge1xuICAgICAgdGl0bGU6IHsgdGV4dDogYXhpc1RpdGxlKHN0ZW0sIDEsIHZyKSB9LCB6ZXJvbGluZTogZmFsc2UsIHRpY2tzOiAnb3V0c2lkZScsXG4gICAgICAuLi4oaW5wdXQudmlldz8ucmFuZ2VzID8geyByYW5nZTogaW5wdXQudmlldy5yYW5nZXMueCwgYXV0b3JhbmdlOiBmYWxzZSB9IDoge30pLFxuICAgIH0sXG4gICAgeWF4aXM6IHtcbiAgICAgIHRpdGxlOiB7IHRleHQ6IGF4aXNUaXRsZShzdGVtLCAyLCB2cikgfSwgemVyb2xpbmU6IGZhbHNlLCB0aWNrczogJ291dHNpZGUnLFxuICAgICAgc2NhbGVhbmNob3I6ICd4Jywgc2NhbGVyYXRpbzogMSxcbiAgICAgIC4uLihpbnB1dC52aWV3Py5yYW5nZXMgPyB7IHJhbmdlOiBpbnB1dC52aWV3LnJhbmdlcy55LCBhdXRvcmFuZ2U6IGZhbHNlIH0gOiB7fSksXG4gICAgfSxcbiAgICBsZWdlbmQ6IHtcbiAgICAgIG9yaWVudGF0aW9uOiAnaCcsXG4gICAgICBpdGVtc2l6aW5nOiAnY29uc3RhbnQnLFxuICAgICAgZm9udDogeyBzaXplOiA5IH0sXG4gICAgICB5YW5jaG9yOiAndG9wJywgeTogLTAuMTYsIHhhbmNob3I6ICdsZWZ0JywgeDogMCxcbiAgICB9LFxuICAgIGhvdmVybW9kZTogJ2Nsb3Nlc3QnLFxuICB9O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE9taWNzSGVhdG1hcElucHV0IHtcbiAgcm93czogc3RyaW5nW107XG4gIGNvbHM6IHN0cmluZ1tdO1xuICAvKiogYHJvd3MubGVuZ3RoIMOXIGNvbHMubGVuZ3RoYCwgcm93LW1ham9yOyBgTmFOYCB3aGVyZSBub3RoaW5nIHdhcyBtZWFzdXJlZC4gKi9cbiAgdmFsdWVzOiBGbG9hdDMyQXJyYXk7XG4gIC8qKiBDZWxscyBiZWhpbmQgZWFjaCBjb2x1bW4sIGZvciB0aGUgaG92ZXIuICovXG4gIGNvdW50cz86IFVpbnQzMkFycmF5O1xuICAvKiogVHJ1ZSB3aGVuIGB2YWx1ZXNgIGFyZSB6LXNjb3Jlcywgd2hpY2ggc2V0cyB0aGUgc2NhbGUgYW5kIHRoZSBsYWJlbHMuICovXG4gIHpTY29yZWQ/OiBib29sZWFuO1xuICAvKiogQ29sdW1uIGF4aXMgdGl0bGUg4oCUIHRoZSBncm91cGluZyBjb2x1bW4ncyBuYW1lLCBvciBcImNlbGxcIi4gKi9cbiAgZ3JvdXBMYWJlbD86IHN0cmluZztcbn1cblxuLyoqIERpdmVyZ2luZywgY2VudHJlZDogYWZ0ZXIgei1zY29yaW5nLCB0aGUgc2lnbiBjYXJyaWVzIHRoZSBtZWFuaW5nLiAqL1xuY29uc3QgSEVBVE1BUF9ESVZFUkdJTkc6IFtudW1iZXIsIHN0cmluZ11bXSA9IFtcbiAgWzAsICcjMjE2NkFDJ10sXG4gIFswLjUsICcjRjdGN0Y3J10sXG4gIFsxLCAnI0IyMTgyQiddLFxuXTtcbi8qKiBTZXF1ZW50aWFsLCBmb3IgcmF3IG1lYW5zIHdoZXJlIHplcm8gaXMgdGhlIGJvdHRvbSByYXRoZXIgdGhhbiB0aGUgbWlkZGxlLiAqL1xuY29uc3QgSEVBVE1BUF9TRVFVRU5USUFMOiBbbnVtYmVyLCBzdHJpbmddW10gPSBbXG4gIFswLCAnI0Y3RkJGRiddLFxuICBbMC41LCAnIzZCQUVENiddLFxuICBbMSwgJyMwODMwNkInXSxcbl07XG5cbmV4cG9ydCBmdW5jdGlvbiBidWlsZEhlYXRtYXBUcmFjZXMoaW5wdXQ6IE9taWNzSGVhdG1hcElucHV0KTogdW5rbm93bltdIHtcbiAgY29uc3QgeyByb3dzLCBjb2xzLCB2YWx1ZXMsIGNvdW50cyB9ID0gaW5wdXQ7XG4gIGlmIChyb3dzLmxlbmd0aCA9PT0gMCB8fCBjb2xzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIFtdO1xuICAvLyBQbG90bHkgd2FudHMgeiBhcyBhbiBhcnJheSBvZiByb3dzLCBib3R0b20tdXA7IE5hTiByZW5kZXJzIGFzIGEgZ2FwLlxuICBjb25zdCB6OiAobnVtYmVyIHwgbnVsbClbXVtdID0gW107XG4gIGZvciAobGV0IHIgPSByb3dzLmxlbmd0aCAtIDE7IHIgPj0gMDsgci0tKSB7XG4gICAgY29uc3QgbGluZTogKG51bWJlciB8IG51bGwpW10gPSBbXTtcbiAgICBmb3IgKGxldCBjID0gMDsgYyA8IGNvbHMubGVuZ3RoOyBjKyspIHtcbiAgICAgIGNvbnN0IHYgPSB2YWx1ZXNbciAqIGNvbHMubGVuZ3RoICsgY107XG4gICAgICBsaW5lLnB1c2goTnVtYmVyLmlzRmluaXRlKHYpID8gdiA6IG51bGwpO1xuICAgIH1cbiAgICB6LnB1c2gobGluZSk7XG4gIH1cbiAgY29uc3QgeSA9IHJvd3Muc2xpY2UoKS5yZXZlcnNlKCk7XG4gIGNvbnN0IHVuaXQgPSBpbnB1dC56U2NvcmVkID8gJ3onIDogJ21lYW4nO1xuICAvLyBTeW1tZXRyaWMgYWJvdXQgemVybyB3aGVuIHotc2NvcmVkLCBzbyB0aGUgc2FtZSBtYWduaXR1ZGUgcmVhZHMgdGhlIHNhbWVcbiAgLy8gd2hpY2hldmVyIHNpZGUgb2YgdGhlIGdlbmUncyBhdmVyYWdlIGl0IGZhbGxzIG9uLlxuICBsZXQgem1pbjogbnVtYmVyIHwgdW5kZWZpbmVkO1xuICBsZXQgem1heDogbnVtYmVyIHwgdW5kZWZpbmVkO1xuICBpZiAoaW5wdXQuelNjb3JlZCkge1xuICAgIGxldCBwZWFrID0gMDtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlcy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgdiA9IE1hdGguYWJzKHZhbHVlc1tpXSk7XG4gICAgICBpZiAoTnVtYmVyLmlzRmluaXRlKHYpICYmIHYgPiBwZWFrKSBwZWFrID0gdjtcbiAgICB9XG4gICAgem1heCA9IHBlYWsgPiAwID8gcGVhayA6IDE7XG4gICAgem1pbiA9IC16bWF4O1xuICB9XG4gIGNvbnN0IGhvdmVyID0gY291bnRzXG4gICAgPyBjb2xzLm1hcCgobGFiZWwsIGMpID0+IGAke2xhYmVsfSDCtyAke2NvdW50c1tjXX0gY2VsbHNgKVxuICAgIDogY29scztcbiAgcmV0dXJuIFt7XG4gICAgdHlwZTogJ2hlYXRtYXAnLFxuICAgIHosXG4gICAgeDogY29scyxcbiAgICB5LFxuICAgIGN1c3RvbWRhdGE6IHoubWFwKCgpID0+IGhvdmVyKSxcbiAgICBjb2xvcnNjYWxlOiBpbnB1dC56U2NvcmVkID8gSEVBVE1BUF9ESVZFUkdJTkcgOiBIRUFUTUFQX1NFUVVFTlRJQUwsXG4gICAgLi4uKHptaW4gIT09IHVuZGVmaW5lZCA/IHsgem1pbiwgem1heCB9IDoge30pLFxuICAgIC8vIEEgZ2FwIGhhcyBubyBjb2xvdXIgcmF0aGVyIHRoYW4gdGhlIHNjYWxlJ3MgYm90dG9tOiBub3RoaW5nIHdhcyBtZWFzdXJlZFxuICAgIC8vIHRoZXJlLCB3aGljaCBpcyBub3QgdGhlIHNhbWUgYXMgYSBsb3cgbWVhbi5cbiAgICBob3Zlcm9uZ2FwczogZmFsc2UsXG4gICAgY29sb3JiYXI6IHsgdGl0bGU6IHsgdGV4dDogdW5pdCwgc2lkZTogJ3JpZ2h0JyB9LCB0aGlja25lc3M6IDEwLCBsZW46IDAuOSB9LFxuICAgIGhvdmVydGVtcGxhdGU6IGAle3l9PGJyPiV7Y3VzdG9tZGF0YX08YnI+JHt1bml0fSAle3o6LjJmfTxleHRyYT48L2V4dHJhPmAsXG4gIH1dO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaGVhdG1hcExheW91dChpbnB1dDogT21pY3NIZWF0bWFwSW5wdXQpOiB1bmtub3duIHtcbiAgcmV0dXJuIHtcbiAgICAvLyBMZWZ0IG1hcmdpbiBncm93cyB3aXRoIHRoZSBsb25nZXN0IGdlbmUgbmFtZTsgdGhlIGxhYmVscyBhcmUgdGhlIHBvaW50LlxuICAgIG1hcmdpbjogeyB0OiAxMCwgcjogMTAsIGI6IDkwLCBsOiA4IH0sXG4gICAgYXV0b3NpemU6IHRydWUsXG4gICAgaG92ZXJtb2RlOiAnY2xvc2VzdCcsXG4gICAgeGF4aXM6IHsgYXV0b21hcmdpbjogdHJ1ZSwgdGlja2FuZ2xlOiAtNDUsIHRpY2tmb250OiB7IHNpemU6IDEwIH0sXG4gICAgICB0aXRsZTogeyB0ZXh0OiBpbnB1dC5ncm91cExhYmVsID8/ICcnIH0gfSxcbiAgICB5YXhpczogeyBhdXRvbWFyZ2luOiB0cnVlLCB0aWNrZm9udDogeyBzaXplOiAxMCB9IH0sXG4gICAgLy8gR3Jvd3Mgd2l0aCB0aGUgZ2VuZSBjb3VudCBzbyAzIHJvd3MgYXJlIG5vdCBzdHJldGNoZWQgdG8gMjAgcm93cycgaGVpZ2h0LlxuICAgIGhlaWdodDogTWF0aC5tYXgoMTgwLCAyNCAqIGlucHV0LnJvd3MubGVuZ3RoICsgMTEwKSxcbiAgfTtcbn1cbiJdfQ==