@jax-data-science/sci-image-visualizer 0.3.3 → 0.5.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.
- package/README.md +268 -4
- package/esm2022/index.mjs +21 -1
- package/esm2022/lib/contracts/display-types.mjs +20 -2
- package/esm2022/lib/contracts/image.contract.mjs +1 -1
- package/esm2022/lib/contracts/plot-type-contribution.contract.mjs +66 -0
- package/esm2022/lib/contracts/plot-type.mjs +41 -1
- package/esm2022/lib/contracts/ports/spatial-data.port.mjs +3 -0
- package/esm2022/lib/contracts/spatial-dataset.contract.mjs +45 -0
- package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
- package/esm2022/lib/implementations/napari-js/napari-axes-labels.mjs +10 -19
- package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +71 -7
- package/esm2022/lib/implementations/napari-js/napari-scale-bar.mjs +1 -1
- package/esm2022/lib/implementations/napari-js/napari-spatial-tooltip.mjs +96 -0
- package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +1934 -55
- package/esm2022/lib/implementations/napari-js/napari-zoom.mjs +18 -0
- package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +73 -8
- package/esm2022/lib/implementations/osd/osd-coordinate-transform.mjs +8 -1
- package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +3 -4
- package/esm2022/lib/implementations/osd/osd-zoom.mjs +20 -0
- package/esm2022/lib/implementations/plotly/omics-trace-builders.mjs +555 -0
- package/esm2022/lib/implementations/plotly/plotly.service.mjs +13 -7
- package/esm2022/lib/implementations/spatial-data-http/spatial-data-http.service.mjs +304 -0
- package/esm2022/lib/implementations/spatial-data-http/spatial-wire.mjs +179 -0
- package/esm2022/lib/plot-mode/plot-mode-controller.mjs +329 -0
- package/esm2022/lib/routing-visualizer.service.mjs +228 -12
- package/esm2022/lib/spatial/embedding-compute.mjs +142 -0
- package/esm2022/lib/spatial/gene-search.mjs +77 -0
- package/esm2022/lib/spatial/spatial-density.mjs +224 -0
- package/esm2022/lib/spatial/spatial-encoding.mjs +232 -0
- package/esm2022/lib/spatial/spatial-expression.mjs +257 -0
- package/esm2022/lib/spatial/spatial-framing.mjs +60 -0
- package/esm2022/lib/spatial/spatial-heatmap.mjs +185 -0
- package/esm2022/lib/spatial/spatial-hover.mjs +79 -0
- package/esm2022/lib/spatial/spatial-sections.mjs +96 -0
- package/esm2022/lib/spatial/spatial-selection.mjs +264 -0
- package/esm2022/lib/spatial/spatial-volume-image.mjs +163 -0
- package/esm2022/lib/spatial/tsne-worker.mjs +10 -0
- package/esm2022/lib/spatial/world-grid.mjs +77 -0
- package/esm2022/lib/spatial-controls/spatial-charts/spatial-charts.component.mjs +1278 -0
- package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +621 -0
- package/esm2022/lib/store/spatial-selection.service.mjs +47 -0
- package/esm2022/lib/store/visualizer-store.service.mjs +23 -1
- package/esm2022/lib/toolbar/toolbar.component.mjs +61 -21
- package/esm2022/lib/visualization.module.mjs +23 -5
- package/esm2022/lib/visualizer.component.mjs +527 -44
- 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
- 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
- package/fesm2022/jax-data-science-sci-image-visualizer-tsne-worker-B_nXDGf0.mjs +12 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-tsne-worker-B_nXDGf0.mjs.map +1 -0
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +15971 -7808
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
- package/fesm2022/tsne.worker.js +19091 -0
- package/index.d.ts +16 -0
- package/lib/contracts/display-types.d.ts +131 -0
- package/lib/contracts/image.contract.d.ts +7 -0
- package/lib/contracts/plot-type-contribution.contract.d.ts +172 -0
- package/lib/contracts/plot-type.d.ts +52 -2
- package/lib/contracts/ports/spatial-data.port.d.ts +90 -0
- package/lib/contracts/spatial-dataset.contract.d.ts +281 -0
- package/lib/contracts/visualizer.contract.d.ts +104 -1
- package/lib/implementations/napari-js/napari-axes-labels.d.ts +9 -7
- package/lib/implementations/napari-js/napari-region-overlay.d.ts +38 -4
- package/lib/implementations/napari-js/napari-scale-bar.d.ts +7 -3
- package/lib/implementations/napari-js/napari-spatial-tooltip.d.ts +32 -0
- package/lib/implementations/napari-js/napari-visualizer.service.d.ts +497 -16
- package/lib/implementations/napari-js/napari-zoom.d.ts +15 -0
- package/lib/implementations/osd/openseadragon-visualizer.service.d.ts +21 -0
- package/lib/implementations/osd/osd-coordinate-transform.d.ts +6 -0
- package/lib/implementations/osd/osd-zoom.d.ts +19 -0
- package/lib/implementations/plotly/omics-trace-builders.d.ts +193 -0
- package/lib/implementations/plotly/plotly.service.d.ts +9 -0
- package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +123 -0
- package/lib/implementations/spatial-data-http/spatial-wire.d.ts +115 -0
- package/lib/plot-mode/plot-mode-controller.d.ts +130 -0
- package/lib/routing-visualizer.service.d.ts +49 -6
- package/lib/spatial/embedding-compute.d.ts +73 -0
- package/lib/spatial/gene-search.d.ts +48 -0
- package/lib/spatial/spatial-density.d.ts +73 -0
- package/lib/spatial/spatial-encoding.d.ts +155 -0
- package/lib/spatial/spatial-expression.d.ts +144 -0
- package/lib/spatial/spatial-framing.d.ts +37 -0
- package/lib/spatial/spatial-heatmap.d.ts +111 -0
- package/lib/spatial/spatial-hover.d.ts +49 -0
- package/lib/spatial/spatial-sections.d.ts +59 -0
- package/lib/spatial/spatial-selection.d.ts +113 -0
- package/lib/spatial/spatial-volume-image.d.ts +67 -0
- package/lib/spatial/tsne-worker.d.ts +7 -0
- package/lib/spatial/world-grid.d.ts +58 -0
- package/lib/spatial-controls/spatial-charts/spatial-charts.component.d.ts +439 -0
- package/lib/spatial-controls/spatial-controls.component.d.ts +257 -0
- package/lib/store/spatial-selection.service.d.ts +27 -0
- package/lib/store/visualizer-store.service.d.ts +13 -0
- package/lib/toolbar/toolbar.component.d.ts +36 -8
- package/lib/visualization.module.d.ts +28 -24
- package/lib/visualizer.component.d.ts +139 -7
- package/package.json +6 -2
- package/src/lib/assets/spatial-omics-3d.svg +31 -0
- 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib21pY3MtdHJhY2UtYnVpbGRlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL2ltcGxlbWVudGF0aW9ucy9wbG90bHkvb21pY3MtdHJhY2UtYnVpbGRlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBDQUEwQyxDQUFDO0FBd0N2RSxNQUFNLGFBQWEsR0FBRyxTQUFTLENBQUM7QUFDaEM7d0VBQ3dFO0FBQ3hFLE1BQU0sb0JBQW9CLEdBQUcsTUFBTSxDQUFDO0FBRXBDLCtFQUErRTtBQUMvRSxTQUFTLE9BQU8sQ0FBQyxLQUFzQixFQUFFLFFBQTJCO0lBQ2xFLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLEdBQUcsS0FBSyxDQUFDO0lBQzlCLE1BQU0sR0FBRyxHQUFhLEVBQUUsQ0FBQztJQUN6QixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ3ZDLElBQUksUUFBUSxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztZQUFFLFNBQVM7UUFDdkMsTUFBTSxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3BCLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztZQUFFLFNBQVM7UUFDbEMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDakQsQ0FBQztJQUNELE9BQU8sR0FBRyxDQUFDO0FBQ2IsQ0FBQztBQUVELHlFQUF5RTtBQUN6RSxTQUFTLElBQUksQ0FBQyxNQUFnQixFQUFFLEdBQUcsR0FB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QUEwQjtTQUMxRSxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQsTUFBTSxVQUFVLGFBQWEsQ0FBQyxLQUF3QjtJQUNwRCxPQUFPO1FBQ0wsMEVBQTBFO1FBQzFFLE1BQU0sRUFBRSxFQUFFLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUU7UUFDckMsUUFBUSxFQUFFLElBQUk7UUFDZCxTQUFTLEVBQUUsU0FBUztRQUNwQixLQUFLLEVBQUUsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDLEVBQUUsRUFBRSxRQUFRLEVBQUUsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFO1lBQy9ELEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxLQUFLLENBQUMsVUFBVSxJQUFJLEVBQUUsRUFBRSxFQUFFO1FBQzNDLEtBQUssRUFBRSxFQUFFLFVBQVUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxFQUFFO1FBQ25ELDRFQUE0RTtRQUM1RSxNQUFNLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsRUFBRSxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxHQUFHLEdBQUcsQ0FBQztLQUNwRCxDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IE5PX0NBVEVHT1JZIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL3NwYXRpYWwtZGF0YXNldC5jb250cmFjdCc7XG5cbi8qKlxuICogUGxvdGx5IHRyYWNlcyBmb3IgdGhlIDEtRCBkaXN0cmlidXRpb24gY2hhcnRzIG92ZXIgc3BhdGlhbC1vbWljcyB2YWx1ZXM6XG4gKiBoaXN0b2dyYW0sIHZpb2xpbiBhbmQgYm94LlxuICpcbiAqIFB1cmUsIGxpa2UgYHBsb3RseS10cmFjZS1idWlsZGVycy50c2Ag4oCUIGFuZCBkZWxpYmVyYXRlbHkgU0VQQVJBVEUgZnJvbSBpdCxcbiAqIGJlY2F1c2UgdGhhdCBtb2R1bGUncyBgVHJhY2VCdWlsZElucHV0YCBpcyBpbWFnZS1tYXRyaXggc2hhcGVkIChgZnJhbWVzYCxcbiAqIGB3aWR0aGAsIGByYXRpb3NgLCBgdHJ1ZUltYWdlU2l6ZWApIGFuZCBjYW5ub3QgZXhwcmVzcyBcIm9uZSB2YWx1ZSBwZXJcbiAqIG9ic2VydmF0aW9uXCIuIEZvcmNpbmcgdGhlIHR3byB0b2dldGhlciB3b3VsZCBtYWtlIGJvdGggd29yc2UuXG4gKlxuICogVmlvbGluIGFuZCBib3ggbmVlZCBubyBidW5kbGluZyB3b3JrOiBgcGxvdGx5LmpzLWRpc3QtbWluYCBpcyB0aGUgZnVsbFxuICogZGlzdHJpYnV0aW9uIGFuZCBhbHJlYWR5IGNhcnJpZXMgYm90aCB0cmFjZSB0eXBlcy5cbiAqL1xuXG5leHBvcnQgdHlwZSBPbWljc0NoYXJ0S2luZCA9XG4gIHwgJ2hpc3RvZ3JhbScgfCAndmlvbGluJyB8ICdib3gnIHwgJ2NvdW50cycgfCAnaGVhdG1hcCcgfCAnZW1iZWRkaW5nJztcblxuLyoqIFBlci1vYnNlcnZhdGlvbiBncm91cGluZyBmb3IgYSB2aW9saW4vYm94LCBvciBhbiBvdmVybGFpZCBoaXN0b2dyYW0uICovXG5leHBvcnQgaW50ZXJmYWNlIE9taWNzR3JvdXBpbmcge1xuICBjb2RlczogVWludDE2QXJyYXk7XG4gIGNhdGVnb3JpZXM6IHN0cmluZ1tdO1xuICAvKiogYCNycmdnYmJgIHBlciBjYXRlZ29yeSwgaW5kZXgtYWxpZ25lZCDigJQgdGhlIHNhbWUgY29sb3VycyB0aGUgbWFwIHVzZXMuICovXG4gIGNvbG9yczogc3RyaW5nW107XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgT21pY3NUcmFjZUlucHV0IHtcbiAgLyoqIFRoZSB2YWx1ZXMgYmVpbmcgY2hhcnRlZCAoYW4gYW5ub3RhdGlvbiBjb2x1bW4gb3IgYSBnZW5lIHZlY3RvcikuICovXG4gIHZhbHVlczogRmxvYXQzMkFycmF5O1xuICAvKiogQXhpcyBsYWJlbCBmb3IgdGhlIHZhbHVlIOKAlCB0aGUgY29sdW1uIG9yIGdlbmUgbmFtZS4gKi9cbiAgbmFtZTogc3RyaW5nO1xuICBncm91cD86IE9taWNzR3JvdXBpbmcgfCBudWxsO1xuICAvKiogYDFgIG1hcmtzIGEgc2VsZWN0ZWQgb2JzZXJ2YXRpb24uIFdoZW4gcHJlc2VudCwgdGhlIGNoYXJ0cyBuYXJyb3cgdG8gaXQuICovXG4gIHNlbGVjdGlvbj86IFVpbnQ4QXJyYXkgfCBudWxsO1xuICAvKiogUGxvdCBgbG9nMXAodmFsdWUpYCBpbnN0ZWFkIG9mIHRoZSByYXcgdmFsdWUuICovXG4gIGxvZz86IGJvb2xlYW47XG4gIC8qKiBDb2xvdXIgZm9yIHRoZSB1bmdyb3VwZWQgdHJhY2UuICovXG4gIGNvbG9yPzogc3RyaW5nO1xufVxuXG5jb25zdCBERUZBVUxUX0NPTE9SID0gJyMwMDcyQjInO1xuLyoqIFBsb3RseSByZW5kZXJzIGV2ZXJ5IHBvaW50IG9mIGEgdmlvbGluL2JveDsgcGFzdCB0aGlzIHdlIHRoaW4gdGhlIGlucHV0LlxuICogIDg0ayBjZWxscyB4IGEgdmlvbGluIHBlciBjYXRlZ29yeSBpcyBvdGhlcndpc2Ugc2Vjb25kcyBvZiBsYXlvdXQuICovXG5jb25zdCBNQVhfUE9JTlRTX1BFUl9UUkFDRSA9IDIwXzAwMDtcblxuLyoqIEZpbml0ZSB2YWx1ZXMgb25seSwgb3B0aW9uYWxseSBsb2ctc2NhbGVkIGFuZCByZXN0cmljdGVkIHRvIGEgc2VsZWN0aW9uLiAqL1xuZnVuY3Rpb24gcHJlcGFyZShpbnB1dDogT21pY3NUcmFjZUlucHV0LCByZXN0cmljdDogVWludDhBcnJheSB8IG51bGwpOiBudW1iZXJbXSB7XG4gIGNvbnN0IHsgdmFsdWVzLCBsb2cgfSA9IGlucHV0O1xuICBjb25zdCBvdXQ6IG51bWJlcltdID0gW107XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgaWYgKHJlc3RyaWN0ICYmICFyZXN0cmljdFtpXSkgY29udGludWU7XG4gICAgY29uc3QgdiA9IHZhbHVlc1tpXTtcbiAgICBpZiAoIU51bWJlci5pc0Zpbml0ZSh2KSkgY29udGludWU7XG4gICAgb3V0LnB1c2gobG9nID8gTWF0aC5sb2cxcChNYXRoLm1heCgwLCB2KSkgOiB2KTtcbiAgfVxuICByZXR1cm4gb3V0O1xufVxuXG4vKiogRXZlbmx5IHRoaW4gYSBzYW1wbGUgdG8gYG1heGAgcG9pbnRzLCBwcmVzZXJ2aW5nIGl0cyBkaXN0cmlidXRpb24uICovXG5mdW5jdGlvbiB0aGluKHZhbHVlczogbnVtYmVyW10sIG1heCA9IE1BWF9QT0lOVFNfUEVSX1RSQUNFKTogbnVtYmVyW10ge1xuICBpZiAodmFsdWVzLmxlbmd0aCA8PSBtYXgpIHJldHVybiB2YWx1ZXM7XG4gIGNvbnN0IHN0ZXAgPSB2YWx1ZXMubGVuZ3RoIC8gbWF4O1xuICBjb25zdCBvdXQ6IG51bWJlcltdID0gW107XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbWF4OyBpKyspIG91dC5wdXNoKHZhbHVlc1tNYXRoLmZsb29yKGkgKiBzdGVwKV0pO1xuICByZXR1cm4gb3V0O1xufVxuXG4vKiogVmFsdWVzIHBlciBjYXRlZ29yeSwgZmluaXRlIGFuZCBvcHRpb25hbGx5IHNlbGVjdGlvbi1yZXN0cmljdGVkLiAqL1xuZnVuY3Rpb24gYnlDYXRlZ29yeShcbiAgaW5wdXQ6IE9taWNzVHJhY2VJbnB1dCwgZ3JvdXA6IE9taWNzR3JvdXBpbmcsIHJlc3RyaWN0OiBVaW50OEFycmF5IHwgbnVsbCxcbik6IG51bWJlcltdW10ge1xuICBjb25zdCBidWNrZXRzOiBudW1iZXJbXVtdID0gZ3JvdXAuY2F0ZWdvcmllcy5tYXAoKCkgPT4gW10pO1xuICBjb25zdCB7IHZhbHVlcywgbG9nIH0gPSBpbnB1dDtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyBpKyspIHtcbiAgICBpZiAocmVzdHJpY3QgJiYgIXJlc3RyaWN0W2ldKSBjb250aW51ZTtcbiAgICBjb25zdCBjb2RlID0gZ3JvdXAuY29kZXNbaV07XG4gICAgLy8gTk9fQ0FURUdPUlkgaGFzIG5vIGJ1Y2tldCBieSBkZWZpbml0aW9uOyBhbiBvdXQtb2YtcmFuZ2UgY29kZSB3b3VsZFxuICAgIC8vIG90aGVyd2lzZSBzaWxlbnRseSBsYW5kIGluIHRoZSB3cm9uZyBvbmUuXG4gICAgaWYgKGNvZGUgPT09IE5PX0NBVEVHT1JZIHx8IGNvZGUgPj0gYnVja2V0cy5sZW5ndGgpIGNvbnRpbnVlO1xuICAgIGNvbnN0IHYgPSB2YWx1ZXNbaV07XG4gICAgaWYgKCFOdW1iZXIuaXNGaW5pdGUodikpIGNvbnRpbnVlO1xuICAgIGJ1Y2tldHNbY29kZV0ucHVzaChsb2cgPyBNYXRoLmxvZzFwKE1hdGgubWF4KDAsIHYpKSA6IHYpO1xuICB9XG4gIHJldHVybiBidWNrZXRzO1xufVxuXG4vKiogVHJ1ZSB3aGVuIHRoZSBzZWxlY3Rpb24gYWN0dWFsbHkgcmVzdHJpY3RzIGFueXRoaW5nLiAqL1xuZnVuY3Rpb24gYWN0aXZlU2VsZWN0aW9uKHNlbGVjdGlvbjogVWludDhBcnJheSB8IG51bGwgfCB1bmRlZmluZWQpOiBVaW50OEFycmF5IHwgbnVsbCB7XG4gIGlmICghc2VsZWN0aW9uKSByZXR1cm4gbnVsbDtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBzZWxlY3Rpb24ubGVuZ3RoOyBpKyspIGlmIChzZWxlY3Rpb25baV0pIHJldHVybiBzZWxlY3Rpb247XG4gIHJldHVybiBudWxsO1xufVxuXG4vKipcbiAqIFRyYWNlcyBmb3Igb25lIGNoYXJ0IGtpbmQuXG4gKlxuICogLSAqKmhpc3RvZ3JhbSoqIOKAlCB0aGUgZnVsbCBkaXN0cmlidXRpb24sIHBsdXMgYW4gb3ZlcmxhaWQgKlNlbGVjdGVkKiB0cmFjZVxuICogICB3aGVuIGEgc2VsZWN0aW9uIGV4aXN0cywgc28gdGhlIHR3byBhcmUgZGlyZWN0bHkgY29tcGFyYWJsZS4gVGhhdFxuICogICBjb21wYXJpc29uIGlzIHRoZSB3aG9sZSBwb2ludCBvZiBsaW5raW5nIHRoZSBjaGFydCB0byB0aGUgbWFwLlxuICogLSAqKnZpb2xpbiAvIGJveCoqIOKAlCBvbmUgcGVyIGNhdGVnb3J5IHdoZW4gZ3JvdXBlZCwgb3RoZXJ3aXNlIGEgc2luZ2xlIHRyYWNlLlxuICogICBUaGVzZSBuYXJyb3cgVE8gdGhlIHNlbGVjdGlvbiByYXRoZXIgdGhhbiBvdmVybGF5aW5nIGl0OiBhIHZpb2xpbiBwZXJcbiAqICAgY2F0ZWdvcnkgcGVyIHNlbGVjdGlvbiBzdGF0ZSBpcyB1bnJlYWRhYmxlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gYnVpbGRPbWljc1RyYWNlcyhraW5kOiBPbWljc0NoYXJ0S2luZCwgaW5wdXQ6IE9taWNzVHJhY2VJbnB1dCk6IHVua25vd25bXSB7XG4gIGNvbnN0IHNlbGVjdGlvbiA9IGFjdGl2ZVNlbGVjdGlvbihpbnB1dC5zZWxlY3Rpb24pO1xuICBjb25zdCBjb2xvciA9IGlucHV0LmNvbG9yID8/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==
|