@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,232 @@
|
|
|
1
|
+
import { buildColormapLut } from '../contracts/colormap-lut';
|
|
2
|
+
import { fallbackColorFor } from '../store/class-color.util';
|
|
3
|
+
import { NO_CATEGORY, } from '../contracts/spatial-dataset.contract';
|
|
4
|
+
/** Neutral grey for observations with no value (NaN, or {@link NO_CATEGORY}). */
|
|
5
|
+
export const MISSING_COLOR = [150, 150, 150];
|
|
6
|
+
/** Default alpha for muted (background / unselected) points — CosMx-style context. */
|
|
7
|
+
export const DEFAULT_MUTED_OPACITY = 0.15;
|
|
8
|
+
/**
|
|
9
|
+
* Largest number of CATEGORIES whose colours survive the 3D points layer's LUT
|
|
10
|
+
* intact.
|
|
11
|
+
*
|
|
12
|
+
* That layer has no per-point RGBA — it maps a per-point SCALAR through a
|
|
13
|
+
* 256-entry colormap LUT — so a categorical palette is encoded as one contiguous
|
|
14
|
+
* block of LUT entries per value. Measured against napari-js, every K in 2..96
|
|
15
|
+
* blocks round-trips exactly and K=97 is the first that does not: 256 texels
|
|
16
|
+
* cannot keep more values apart than that.
|
|
17
|
+
*
|
|
18
|
+
* One of those 96 blocks is spent on {@link NO_CATEGORY}, which every column can
|
|
19
|
+
* contain, so what fits is **95 categories** — the number published here. It was
|
|
20
|
+
* 96 while the encoder rejected at 96, which meant a 96-category column drew flat
|
|
21
|
+
* with the panel showing no warning: the one failure this ceiling exists to
|
|
22
|
+
* prevent, arrived at from the other direction.
|
|
23
|
+
*
|
|
24
|
+
* Above it the cloud draws FLAT rather than draw a lie, because subtly wrong
|
|
25
|
+
* category colours under a confident legend is the failure nobody catches.
|
|
26
|
+
*
|
|
27
|
+
* A limit of that one layer, and of nothing else: the 2D markers carry per-point
|
|
28
|
+
* RGBA, and a density volume is a scalar field per cluster. Shared here so the
|
|
29
|
+
* renderer that enforces it and the panel that explains it cannot drift apart.
|
|
30
|
+
*/
|
|
31
|
+
export const SPATIAL_3D_MAX_CATEGORIES = 95;
|
|
32
|
+
/** LUT blocks the 3D layer can keep apart — {@link SPATIAL_3D_MAX_CATEGORIES}
|
|
33
|
+
* plus the one reserved for a missing value. */
|
|
34
|
+
export const SPATIAL_3D_LUT_BLOCKS = SPATIAL_3D_MAX_CATEGORIES + 1;
|
|
35
|
+
/**
|
|
36
|
+
* Fallback categorical palette. Colour-blind-safe and stable, so a category
|
|
37
|
+
* without an authored colour still lands somewhere sensible and lands there
|
|
38
|
+
* every run (`fallbackColorFor` hashes the name — same name, same colour).
|
|
39
|
+
* Okabe–Ito, plus two greys for long tails.
|
|
40
|
+
*/
|
|
41
|
+
export const DEFAULT_CATEGORICAL_PALETTE = [
|
|
42
|
+
'#0072B2', '#E69F00', '#009E73', '#CC79A7', '#56B4E9',
|
|
43
|
+
'#D55E00', '#F0E442', '#000000', '#7F7F7F', '#BFBFBF',
|
|
44
|
+
];
|
|
45
|
+
/** Parse `#rgb` / `#rrggbb` to 0–255 RGB; unparseable input falls back to grey.
|
|
46
|
+
* Exported so the 3D LUT path parses palettes exactly as the 2D path does. */
|
|
47
|
+
export function parseHex(hex) {
|
|
48
|
+
const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec((hex ?? '').trim());
|
|
49
|
+
if (!m)
|
|
50
|
+
return MISSING_COLOR;
|
|
51
|
+
let h = m[1];
|
|
52
|
+
if (h.length === 3)
|
|
53
|
+
h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
|
|
54
|
+
return [
|
|
55
|
+
parseInt(h.slice(0, 2), 16),
|
|
56
|
+
parseInt(h.slice(2, 4), 16),
|
|
57
|
+
parseInt(h.slice(4, 6), 16),
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
function alphaAt(i, opts) {
|
|
61
|
+
const on = opts.opacity ?? 1;
|
|
62
|
+
const off = opts.mutedOpacity ?? DEFAULT_MUTED_OPACITY;
|
|
63
|
+
return opts.muted && opts.muted[i] ? off : on;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Per-category colours: the column's authored `colors` where present (so the
|
|
67
|
+
* viewer matches the figures the same analysis produced elsewhere), otherwise
|
|
68
|
+
* the palette by position, wrapping through a name-hash past its end so a
|
|
69
|
+
* 40-cluster column still gets stable, distinct-ish colours.
|
|
70
|
+
*/
|
|
71
|
+
export function resolveCategoryColors(meta, palette = DEFAULT_CATEGORICAL_PALETTE) {
|
|
72
|
+
const authored = meta.colors;
|
|
73
|
+
return meta.categories.map((name, i) => {
|
|
74
|
+
const given = authored?.[i];
|
|
75
|
+
if (given)
|
|
76
|
+
return given;
|
|
77
|
+
if (i < palette.length)
|
|
78
|
+
return palette[i];
|
|
79
|
+
return fallbackColorFor(name, palette);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/** Categorical column → flat per-point RGBA (length `4 * codes.length`). */
|
|
83
|
+
export function encodeCategorical(codes, opts) {
|
|
84
|
+
const n = codes.length;
|
|
85
|
+
const out = new Float32Array(n * 4);
|
|
86
|
+
// Resolve each category once — parsing a hex string per point would dominate.
|
|
87
|
+
const rgb = opts.colors.map(parseHex);
|
|
88
|
+
const missing = opts.missingColor ?? MISSING_COLOR;
|
|
89
|
+
for (let i = 0; i < n; i++) {
|
|
90
|
+
const code = codes[i];
|
|
91
|
+
const c = code === NO_CATEGORY || code >= rgb.length ? missing : rgb[code];
|
|
92
|
+
const o = i * 4;
|
|
93
|
+
out[o] = c[0] / 255;
|
|
94
|
+
out[o + 1] = c[1] / 255;
|
|
95
|
+
out[o + 2] = c[2] / 255;
|
|
96
|
+
// A point with no category is background by definition — never emphasised.
|
|
97
|
+
out[o + 3] = code === NO_CATEGORY
|
|
98
|
+
? Math.min(alphaAt(i, opts), opts.mutedOpacity ?? DEFAULT_MUTED_OPACITY)
|
|
99
|
+
: alphaAt(i, opts);
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
/** Continuous column / gene vector → flat per-point RGBA. */
|
|
104
|
+
export function encodeContinuous(values, opts) {
|
|
105
|
+
const n = values.length;
|
|
106
|
+
const out = new Float32Array(n * 4);
|
|
107
|
+
const missing = opts.missingColor ?? MISSING_COLOR;
|
|
108
|
+
const lut = opts.lut;
|
|
109
|
+
const lo = opts.log ? Math.log1p(Math.max(0, opts.min)) : opts.min;
|
|
110
|
+
const hi = opts.log ? Math.log1p(Math.max(0, opts.max)) : opts.max;
|
|
111
|
+
// A degenerate window (all values equal) maps everything to the LUT midpoint
|
|
112
|
+
// rather than dividing by zero.
|
|
113
|
+
const span = hi - lo;
|
|
114
|
+
for (let i = 0; i < n; i++) {
|
|
115
|
+
const raw = values[i];
|
|
116
|
+
const o = i * 4;
|
|
117
|
+
if (!Number.isFinite(raw)) {
|
|
118
|
+
out[o] = missing[0] / 255;
|
|
119
|
+
out[o + 1] = missing[1] / 255;
|
|
120
|
+
out[o + 2] = missing[2] / 255;
|
|
121
|
+
out[o + 3] = opts.mutedOpacity ?? DEFAULT_MUTED_OPACITY;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const v = opts.log ? Math.log1p(Math.max(0, raw)) : raw;
|
|
125
|
+
const t = span > 0 ? (v - lo) / span : 0.5;
|
|
126
|
+
const idx = Math.max(0, Math.min(255, Math.round(t * 255)));
|
|
127
|
+
const c = lut[idx] ?? missing;
|
|
128
|
+
out[o] = c[0] / 255;
|
|
129
|
+
out[o + 1] = c[1] / 255;
|
|
130
|
+
out[o + 2] = c[2] / 255;
|
|
131
|
+
out[o + 3] = alphaAt(i, opts);
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Contrast window from percentiles of the finite values — the CosMx
|
|
137
|
+
* "cap outliers at percentiles to improve contrast" rule. `lo`/`hi` are
|
|
138
|
+
* fractions (0.01 / 0.99 clips the extreme 1% at each end).
|
|
139
|
+
*
|
|
140
|
+
* Returns `[0, 1]` for an empty or all-missing vector so callers always get a
|
|
141
|
+
* usable window.
|
|
142
|
+
*/
|
|
143
|
+
export function contrastWindow(values, lo = 0.01, hi = 0.99) {
|
|
144
|
+
const finite = [];
|
|
145
|
+
for (let i = 0; i < values.length; i++) {
|
|
146
|
+
if (Number.isFinite(values[i]))
|
|
147
|
+
finite.push(values[i]);
|
|
148
|
+
}
|
|
149
|
+
if (finite.length === 0)
|
|
150
|
+
return [0, 1];
|
|
151
|
+
finite.sort((a, b) => a - b);
|
|
152
|
+
const at = (f) => {
|
|
153
|
+
const k = Math.max(0, Math.min(finite.length - 1, Math.round(f * (finite.length - 1))));
|
|
154
|
+
return finite[k];
|
|
155
|
+
};
|
|
156
|
+
const min = at(Math.max(0, Math.min(1, lo)));
|
|
157
|
+
const max = at(Math.max(0, Math.min(1, hi)));
|
|
158
|
+
// A flat or inverted window would divide by zero downstream; widen it.
|
|
159
|
+
return max > min ? [min, max] : [min, min + 1];
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Marker sizes for the points layer. napari-js `PointsLayer.size` is a
|
|
163
|
+
* **diameter** in data units while `SpatialObservations.radius` is a radius —
|
|
164
|
+
* the doubling lives here so no call site has to remember it.
|
|
165
|
+
*
|
|
166
|
+
* `fallbackRadius` covers a dataset that declares no radius (segmented cells
|
|
167
|
+
* often don't); it is in image pixels, like everything else in this space.
|
|
168
|
+
*/
|
|
169
|
+
export function markerDiameters(observations, fallbackRadius = 4) {
|
|
170
|
+
const r = observations.radius;
|
|
171
|
+
if (typeof r === 'number')
|
|
172
|
+
return r * 2;
|
|
173
|
+
if (r instanceof Float32Array) {
|
|
174
|
+
const out = new Float32Array(r.length);
|
|
175
|
+
for (let i = 0; i < r.length; i++)
|
|
176
|
+
out[i] = r[i] * 2;
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
return fallbackRadius * 2;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Adapt a flat RGBA buffer to the `RGBA[]` shape napari-js 0.12.0's
|
|
183
|
+
* `PointsLayer.faceColor` accepts. One 4-element array per point — the reason
|
|
184
|
+
* this is a named boundary rather than inlined: it is the one place the model
|
|
185
|
+
* stops being typed-array shaped, and it is what a future napari-js taking a
|
|
186
|
+
* `Float32Array` would delete.
|
|
187
|
+
*/
|
|
188
|
+
export function toRgbaTuples(flat) {
|
|
189
|
+
const n = flat.length / 4;
|
|
190
|
+
const out = new Array(n);
|
|
191
|
+
for (let i = 0; i < n; i++) {
|
|
192
|
+
const o = i * 4;
|
|
193
|
+
out[i] = [flat[o], flat[o + 1], flat[o + 2], flat[o + 3]];
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
/** Convenience: a 256-entry LUT for a colormap value, falling back to Viridis. */
|
|
198
|
+
export function lutFor(colormapValue, reverse = false) {
|
|
199
|
+
return buildColormapLut(colormapValue, reverse)
|
|
200
|
+
?? buildColormapLut('Viridis', reverse);
|
|
201
|
+
}
|
|
202
|
+
/** True when every entry of a LUT is a neutral grey. Tested on the VALUES rather
|
|
203
|
+
* than the colormap's name, so a reversed or renamed grey ramp still counts. */
|
|
204
|
+
export function isGrayscaleLut(lut) {
|
|
205
|
+
return lut.every(([r, g, b]) => r === g && g === b);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* LUT for a spatial CONTINUOUS encoding — a gene, a numeric column.
|
|
209
|
+
*
|
|
210
|
+
* `override` is the caller's explicit choice and wins outright, grey included: a
|
|
211
|
+
* grey ramp picked FOR the data is a decision, not the image's colormap leaking
|
|
212
|
+
* in.
|
|
213
|
+
*
|
|
214
|
+
* Without one, the display colormap — unless that colormap is grayscale. The
|
|
215
|
+
* tissue underneath is grayscale, so a grey overlay of a measurement is
|
|
216
|
+
* indistinguishable from the anatomy it is drawn over, and the reader cannot tell
|
|
217
|
+
* which channel a bright pixel belongs to. Viridis is then the fallback, which is
|
|
218
|
+
* what the CosMx guidance prescribes for continuous values anyway.
|
|
219
|
+
*
|
|
220
|
+
* Shared by the markers, both gene maps and the panel's colour bar, so none of
|
|
221
|
+
* them can disagree about what a colour means, whichever way the choice falls.
|
|
222
|
+
*/
|
|
223
|
+
export function spatialContinuousLut(colormapValue, reverse = false, override) {
|
|
224
|
+
// An inline scale is an array, so emptiness has to be checked as well as
|
|
225
|
+
// presence: `[]` is truthy and would resolve to the fallback LUT silently.
|
|
226
|
+
const chosen = Array.isArray(override) ? (override.length ? override : null) : override;
|
|
227
|
+
if (chosen)
|
|
228
|
+
return lutFor(chosen, reverse);
|
|
229
|
+
const lut = lutFor(colormapValue, reverse);
|
|
230
|
+
return isGrayscaleLut(lut) ? buildColormapLut('Viridis', reverse) : lut;
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BhdGlhbC1lbmNvZGluZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvc3BhdGlhbC9zcGF0aWFsLWVuY29kaW5nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBTyxnQkFBZ0IsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ2xFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQzdELE9BQU8sRUFDa0IsV0FBVyxHQUNuQyxNQUFNLHVDQUF1QyxDQUFDO0FBK0IvQyxpRkFBaUY7QUFDakYsTUFBTSxDQUFDLE1BQU0sYUFBYSxHQUFRLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQztBQUVsRCxzRkFBc0Y7QUFDdEYsTUFBTSxDQUFDLE1BQU0scUJBQXFCLEdBQUcsSUFBSSxDQUFDO0FBRTFDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBc0JHO0FBQ0gsTUFBTSxDQUFDLE1BQU0seUJBQXlCLEdBQUcsRUFBRSxDQUFDO0FBRTVDO2lEQUNpRDtBQUNqRCxNQUFNLENBQUMsTUFBTSxxQkFBcUIsR0FBRyx5QkFBeUIsR0FBRyxDQUFDLENBQUM7QUFFbkU7Ozs7O0dBS0c7QUFDSCxNQUFNLENBQUMsTUFBTSwyQkFBMkIsR0FBYTtJQUNuRCxTQUFTLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsU0FBUztJQUNyRCxTQUFTLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsU0FBUztDQUN0RCxDQUFDO0FBOEJGOytFQUMrRTtBQUMvRSxNQUFNLFVBQVUsUUFBUSxDQUFDLEdBQVc7SUFDbEMsTUFBTSxDQUFDLEdBQUcsZ0NBQWdDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7SUFDcEUsSUFBSSxDQUFDLENBQUM7UUFBRSxPQUFPLGFBQWEsQ0FBQztJ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TUFBTSxDQUFDLGFBQXNCLEVBQUUsT0FBTyxHQUFHLEtBQUs7SUFDNUQsT0FBTyxnQkFBZ0IsQ0FBQyxhQUFhLEVBQUUsT0FBTyxDQUFDO1dBQzFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxPQUFPLENBQUUsQ0FBQztBQUM3QyxDQUFDO0FBRUQ7aUZBQ2lGO0FBQ2pGLE1BQU0sVUFBVSxjQUFjLENBQUMsR0FBbUI7SUFDaEQsT0FBTyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztBQUN0RCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBQ0gsTUFBTSxVQUFVLG9CQUFvQixDQUNsQyxhQUFzQixFQUFFLE9BQU8sR0FBRyxLQUFLLEVBQUUsUUFBK0I7SUFFeEUseUVBQXlFO0lBQ3pFLDJFQUEyRTtJQUMzRSxNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQztJQUN4RixJQUFJLE1BQU07UUFBRSxPQUFPLE1BQU0sQ0FBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDM0MsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLGFBQWEsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUMzQyxPQUFPLGNBQWMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxFQUFFLE9BQU8sQ0FBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUM7QUFDM0UsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFJnYiwgYnVpbGRDb2xvcm1hcEx1dCB9IGZyb20gJy4uL2NvbnRyYWN0cy9jb2xvcm1hcC1sdXQnO1xuaW1wb3J0IHsgZmFsbGJhY2tDb2xvckZvciB9IGZyb20gJy4uL3N0b3JlL2NsYXNzLWNvbG9yLnV0aWwnO1xuaW1wb3J0IHtcbiAgQ2F0ZWdvcmljYWxDb2x1bW5NZXRhLCBOT19DQVRFR09SWSwgU3BhdGlhbE9ic2VydmF0aW9ucyxcbn0gZnJvbSAnLi4vY29udHJhY3RzL3NwYXRpYWwtZGF0YXNldC5jb250cmFjdCc7XG5pbXBvcnQgeyBDb2xvcm1hcFZhbHVlIH0gZnJvbSAnLi4vY29udHJhY3RzL2Rpc3BsYXktdHlwZXMnO1xuXG4vKipcbiAqIFR1cm5pbmcgc3BhdGlhbC1vbWljcyBjb2x1bW5zIGludG8gcGVyLXBvaW50IHZpc3VhbCBhdHRyaWJ1dGVzLlxuICpcbiAqIFB1cmUg4oCUIG5vIEFuZ3VsYXIsIG5vIFJ4SlMsIG5vIHJlbmRlcmVyIOKAlCBsaWtlIGBwbG90bHktdHJhY2UtYnVpbGRlcnMudHNgLCBzb1xuICogdGhlIGVuY29kaW5ncyBhcmUgdGVzdGFibGUgb24gdGhlaXIgb3duIGFuZCBzaGFyZWQgYnkgYW55IGJhY2tlbmQgdGhhdCBncm93c1xuICogYSBzcGF0aWFsIG1vZGUuXG4gKlxuICogVGhlIHJ1bGVzIGhlcmUgYXJlIHRoZSBvbmVzIHRoZSBDb3NNeCBhbmFseXNpcyBndWlkYW5jZSBwcmVzY3JpYmVzLCBtYWRlXG4gKiBleHBsaWNpdCByYXRoZXIgdGhhbiBsZWZ0IHRvIGVhY2ggY2FsbCBzaXRlOlxuICogIC0gY291bnQtbGlrZSBjb2x1bW5zIHJlYWQgb24gYSAqKmxvZyoqIHNjYWxlIChsaW5lYXIgY29sbGFwc2VzIHRoZW0gYWdhaW5zdCBhXG4gKiAgICBmZXcgYnJpZ2h0IG91dGxpZXJzKTtcbiAqICAtIGNvbnRyYXN0IHdpbmRvd3MgY29tZSBmcm9tICoqcGVyY2VudGlsZXMqKiwgbm90IG1pbi9tYXgsIHNvIGEgc2luZ2xlXG4gKiAgICBzYXR1cmF0ZWQgc3BvdCBkb2Vzbid0IGZsYXR0ZW4gZXZlcnl0aGluZyBlbHNlO1xuICogIC0gY29udGV4dCBpcyBrZXB0IGJ5ICoqbXV0aW5nKiogcmF0aGVyIHRoYW4gaGlkaW5nIOKAlCBiYWNrZ3JvdW5kIHBvaW50cyBzdGF5XG4gKiAgICB2aXNpYmxlIGF0IGxvdyBhbHBoYSBzbyB0aGUgdGlzc3VlIHNoYXBlIHN1cnZpdmVzLlxuICpcbiAqIENPTE9VUiBSRVBSRVNFTlRBVElPTlxuICogLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiBJbnRlcm5hbGx5IGV2ZXJ5dGhpbmcgaXMgYSBmbGF0IGBGbG9hdDMyQXJyYXlgIG9mIGBbcixnLGIsYSwg4oCmXWAgaW4gMC4uMSDigJRcbiAqIG9uZSBhbGxvY2F0aW9uIGZvciBOIHBvaW50cywgR1BVLXVwbG9hZCBzaGFwZWQuIG5hcGFyaS1qcyAwLjEyLjAnc1xuICogYFBvaW50c0xheWVyLmZhY2VDb2xvcmAgdGFrZXMgYFJHQkFbXWAgKGFuIGFycmF5IG9mIDQtdHVwbGVzKSBpbnN0ZWFkLCBzb1xuICoge0BsaW5rIHRvUmdiYVR1cGxlc30gYWRhcHRzIGF0IHRoZSBib3VuZGFyeTsgd2hlbiB0aGUgbGF5ZXIgYWNjZXB0cyBhIHR5cGVkXG4gKiBhcnJheSB0aGUgYWRhcHRlciBnb2VzIGF3YXkgYW5kIHRoZSBmbGF0IGJ1ZmZlciBpcyBwYXNzZWQgc3RyYWlnaHQgdGhyb3VnaC5cbiAqL1xuXG4vKiogbmFwYXJpLWpzIHBlci1wb2ludCBjb2xvdXIgdHVwbGU6IHIsIGcsIGIsIGEgaW4gMC4uMS4gKi9cbmV4cG9ydCB0eXBlIFJHQkEgPSBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXTtcblxuLyoqIE5ldXRyYWwgZ3JleSBmb3Igb2JzZXJ2YXRpb25zIHdpdGggbm8gdmFsdWUgKE5hTiwgb3Ige0BsaW5rIE5PX0NBVEVHT1JZfSkuICovXG5leHBvcnQgY29uc3QgTUlTU0lOR19DT0xPUjogUmdiID0gWzE1MCwgMTUwLCAxNTBdO1xuXG4vKiogRGVmYXVsdCBhbHBoYSBmb3IgbXV0ZWQgKGJhY2tncm91bmQgLyB1bnNlbGVjdGVkKSBwb2ludHMg4oCUIENvc014LXN0eWxlIGNvbnRleHQuICovXG5leHBvcnQgY29uc3QgREVGQVVMVF9NVVRFRF9PUEFDSVRZID0gMC4xNTtcblxuLyoqXG4gKiBMYXJnZXN0IG51bWJlciBvZiBDQVRFR09SSUVTIHdob3NlIGNvbG91cnMgc3Vydml2ZSB0aGUgM0QgcG9pbnRzIGxheWVyJ3MgTFVUXG4gKiBpbnRhY3QuXG4gKlxuICogVGhhdCBsYXllciBoYXMgbm8gcGVyLXBvaW50IFJHQkEg4oCUIGl0IG1hcHMgYSBwZXItcG9pbnQgU0NBTEFSIHRocm91Z2ggYVxuICogMjU2LWVudHJ5IGNvbG9ybWFwIExVVCDigJQgc28gYSBjYXRlZ29yaWNhbCBwYWxldHRlIGlzIGVuY29kZWQgYXMgb25lIGNvbnRpZ3VvdXNcbiAqIGJsb2NrIG9mIExVVCBlbnRyaWVzIHBlciB2YWx1ZS4gTWVhc3VyZWQgYWdhaW5zdCBuYXBhcmktanMsIGV2ZXJ5IEsgaW4gMi4uOTZcbiAqIGJsb2NrcyByb3VuZC10cmlwcyBleGFjdGx5IGFuZCBLPTk3IGlzIHRoZSBmaXJzdCB0aGF0IGRvZXMgbm90OiAyNTYgdGV4ZWxzXG4gKiBjYW5ub3Qga2VlcCBtb3JlIHZhbHVlcyBhcGFydCB0aGFuIHRoYXQuXG4gKlxuICogT25lIG9mIHRob3NlIDk2IGJsb2NrcyBpcyBzcGVudCBvbiB7QGxpbmsgTk9fQ0FURUdPUll9LCB3aGljaCBldmVyeSBjb2x1bW4gY2FuXG4gKiBjb250YWluLCBzbyB3aGF0IGZpdHMgaXMgKio5NSBjYXRlZ29yaWVzKiog4oCUIHRoZSBudW1iZXIgcHVibGlzaGVkIGhlcmUuIEl0IHdhc1xuICogOTYgd2hpbGUgdGhlIGVuY29kZXIgcmVqZWN0ZWQgYXQgOTYsIHdoaWNoIG1lYW50IGEgOTYtY2F0ZWdvcnkgY29sdW1uIGRyZXcgZmxhdFxuICogd2l0aCB0aGUgcGFuZWwgc2hvd2luZyBubyB3YXJuaW5nOiB0aGUgb25lIGZhaWx1cmUgdGhpcyBjZWlsaW5nIGV4aXN0cyB0b1xuICogcHJldmVudCwgYXJyaXZlZCBhdCBmcm9tIHRoZSBvdGhlciBkaXJlY3Rpb24uXG4gKlxuICogQWJvdmUgaXQgdGhlIGNsb3VkIGRyYXdzIEZMQVQgcmF0aGVyIHRoYW4gZHJhdyBhIGxpZSwgYmVjYXVzZSBzdWJ0bHkgd3JvbmdcbiAqIGNhdGVnb3J5IGNvbG91cnMgdW5kZXIgYSBjb25maWRlbnQgbGVnZW5kIGlzIHRoZSBmYWlsdXJlIG5vYm9keSBjYXRjaGVzLlxuICpcbiAqIEEgbGltaXQgb2YgdGhhdCBvbmUgbGF5ZXIsIGFuZCBvZiBub3RoaW5nIGVsc2U6IHRoZSAyRCBtYXJrZXJzIGNhcnJ5IHBlci1wb2ludFxuICogUkdCQSwgYW5kIGEgZGVuc2l0eSB2b2x1bWUgaXMgYSBzY2FsYXIgZmllbGQgcGVyIGNsdXN0ZXIuIFNoYXJlZCBoZXJlIHNvIHRoZVxuICogcmVuZGVyZXIgdGhhdCBlbmZvcmNlcyBpdCBhbmQgdGhlIHBhbmVsIHRoYXQgZXhwbGFpbnMgaXQgY2Fubm90IGRyaWZ0IGFwYXJ0LlxuICovXG5leHBvcnQgY29uc3QgU1BBVElBTF8zRF9NQVhfQ0FURUdPUklFUyA9IDk1O1xuXG4vKiogTFVUIGJsb2NrcyB0aGUgM0QgbGF5ZXIgY2FuIGtlZXAgYXBhcnQg4oCUIHtAbGluayBTUEFUSUFMXzNEX01BWF9DQVRFR09SSUVTfVxuICogIHBsdXMgdGhlIG9uZSByZXNlcnZlZCBmb3IgYSBtaXNzaW5nIHZhbHVlLiAqL1xuZXhwb3J0IGNvbnN0IFNQQVRJQUxfM0RfTFVUX0JMT0NLUyA9IFNQQVRJQUxfM0RfTUFYX0NBVEVHT1JJRVMgKyAxO1xuXG4vKipcbiAqIEZhbGxiYWNrIGNhdGVnb3JpY2FsIHBhbGV0dGUuIENvbG91ci1ibGluZC1zYWZlIGFuZCBzdGFibGUsIHNvIGEgY2F0ZWdvcnlcbiAqIHdpdGhvdXQgYW4gYXV0aG9yZWQgY29sb3VyIHN0aWxsIGxhbmRzIHNvbWV3aGVyZSBzZW5zaWJsZSBhbmQgbGFuZHMgdGhlcmVcbiAqIGV2ZXJ5IHJ1biAoYGZhbGxiYWNrQ29sb3JGb3JgIGhhc2hlcyB0aGUgbmFtZSDigJQgc2FtZSBuYW1lLCBzYW1lIGNvbG91cikuXG4gKiBPa2FiZeKAk0l0bywgcGx1cyB0d28gZ3JleXMgZm9yIGxvbmcgdGFpbHMuXG4gKi9cbmV4cG9ydCBjb25zdCBERUZBVUxUX0NBVEVHT1JJQ0FMX1BBTEVUVEU6IHN0cmluZ1tdID0gW1xuICAnIzAwNzJCMicsICcjRTY5RjAwJywgJyMwMDlFNzMnLCAnI0NDNzlBNycsICcjNTZCNEU5JyxcbiAgJyNENTVFMDAnLCAnI0YwRTQ0MicsICcjMDAwMDAwJywgJyM3RjdGN0YnLCAnI0JGQkZCRicsXG5dO1xuXG5pbnRlcmZhY2UgTXV0ZU9wdGlvbnMge1xuICAvKiogQWxwaGEgZm9yIHBvaW50cyB0aGF0IGFyZSBub3QgbXV0ZWQuIERlZmF1bHQgMS4gKi9cbiAgb3BhY2l0eT86IG51bWJlcjtcbiAgLyoqIEFscGhhIGZvciBtdXRlZCBwb2ludHMuIERlZmF1bHQge0BsaW5rIERFRkFVTFRfTVVURURfT1BBQ0lUWX0uICovXG4gIG11dGVkT3BhY2l0eT86IG51bWJlcjtcbiAgLyoqIGAxYCBhdCBpbmRleCBpIG11dGVzIG9ic2VydmF0aW9uIGkuIEFic2VudCDih5Igbm90aGluZyBpcyBtdXRlZC4gKi9cbiAgbXV0ZWQ/OiBVaW50OEFycmF5IHwgbnVsbDtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDYXRlZ29yaWNhbEVuY29kaW5nT3B0aW9ucyBleHRlbmRzIE11dGVPcHRpb25zIHtcbiAgLyoqIFBlci1jYXRlZ29yeSBgI3JyZ2diYmAsIGluZGV4LWFsaWduZWQgd2l0aCB0aGUgY29sdW1uJ3MgY2F0ZWdvcmllcy4gKi9cbiAgY29sb3JzOiBzdHJpbmdbXTtcbiAgLyoqIENvbG91ciBmb3Ige0BsaW5rIE5PX0NBVEVHT1JZfS4gRGVmYXVsdCB7QGxpbmsgTUlTU0lOR19DT0xPUn0uICovXG4gIG1pc3NpbmdDb2xvcj86IFJnYjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDb250aW51b3VzRW5jb2RpbmdPcHRpb25zIGV4dGVuZHMgTXV0ZU9wdGlvbnMge1xuICAvKiogMjU2LWVudHJ5IFJHQiB0YWJsZSBmcm9tIHtAbGluayBidWlsZENvbG9ybWFwTHV0fS4gKi9cbiAgbHV0OiBSZ2JbXTtcbiAgLyoqIENvbnRyYXN0IHdpbmRvdyBpbiBWQUxVRSB1bml0cyAocHJlLWxvZykuICovXG4gIG1pbjogbnVtYmVyO1xuICBtYXg6IG51bWJlcjtcbiAgLyoqIEFwcGx5IGBsb2cxcGAgdG8gdGhlIHZhbHVlIGFuZCB0aGUgd2luZG93IGJlZm9yZSBub3JtYWxpc2luZy4gKi9cbiAgbG9nPzogYm9vbGVhbjtcbiAgLyoqIENvbG91ciBmb3IgYE5hTmAuIERlZmF1bHQge0BsaW5rIE1JU1NJTkdfQ09MT1J9LiAqL1xuICBtaXNzaW5nQ29sb3I/OiBSZ2I7XG59XG5cbi8qKiBQYXJzZSBgI3JnYmAgLyBgI3JyZ2diYmAgdG8gMOKAkzI1NSBSR0I7IHVucGFyc2VhYmxlIGlucHV0IGZhbGxzIGJhY2sgdG8gZ3JleS5cbiAqICBFeHBvcnRlZCBzbyB0aGUgM0QgTFVUIHBhdGggcGFyc2VzIHBhbGV0dGVzIGV4YWN0bHkgYXMgdGhlIDJEIHBhdGggZG9lcy4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZUhleChoZXg6IHN0cmluZyk6IFJnYiB7XG4gIGNvbnN0IG0gPSAvXiM/KFswLTlhLWZdezN9fFswLTlhLWZdezZ9KSQvaS5leGVjKChoZXggPz8gJycpLnRyaW0oKSk7XG4gIGlmICghbSkgcmV0dXJuIE1JU1NJTkdfQ09MT1I7XG4gIGxldCBoID0gbVsxXTtcbiAgaWYgKGgubGVuZ3RoID09PSAzKSBoID0gaFswXSArIGhbMF0gKyBoWzFdICsgaFsxXSArIGhbMl0gKyBoWzJdO1xuICByZXR1cm4gW1xuICAgIHBhcnNlSW50KGguc2xpY2UoMCwgMiksIDE2KSxcbiAgICBwYXJzZUludChoLnNsaWNlKDIsIDQpLCAxNiksXG4gICAgcGFyc2VJbnQoaC5zbGljZSg0LCA2KSwgMTYpLFxuICBdO1xufVxuXG5mdW5jdGlvbiBhbHBoYUF0KGk6IG51bWJlciwgb3B0czogTXV0ZU9wdGlvbnMpOiBudW1iZXIge1xuICBjb25zdCBvbiA9IG9wdHMub3BhY2l0eSA/PyAxO1xuICBjb25zdCBvZmYgPSBvcHRzLm11dGVkT3BhY2l0eSA/PyBERUZBVUxUX01VVEVEX09QQUNJVFk7XG4gIHJldHVybiBvcHRzLm11dGVkICYmIG9wdHMubXV0ZWRbaV0gPyBvZmYgOiBvbjtcbn1cblxuLyoqXG4gKiBQZXItY2F0ZWdvcnkgY29sb3VyczogdGhlIGNvbHVtbidzIGF1dGhvcmVkIGBjb2xvcnNgIHdoZXJlIHByZXNlbnQgKHNvIHRoZVxuICogdmlld2VyIG1hdGNoZXMgdGhlIGZpZ3VyZXMgdGhlIHNhbWUgYW5hbHlzaXMgcHJvZHVjZWQgZWxzZXdoZXJlKSwgb3RoZXJ3aXNlXG4gKiB0aGUgcGFsZXR0ZSBieSBwb3NpdGlvbiwgd3JhcHBpbmcgdGhyb3VnaCBhIG5hbWUtaGFzaCBwYXN0IGl0cyBlbmQgc28gYVxuICogNDAtY2x1c3RlciBjb2x1bW4gc3RpbGwgZ2V0cyBzdGFibGUsIGRpc3RpbmN0LWlzaCBjb2xvdXJzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZUNhdGVnb3J5Q29sb3JzKFxuICBtZXRhOiBDYXRlZ29yaWNhbENvbHVtbk1ldGEsIHBhbGV0dGU6IHN0cmluZ1tdID0gREVGQVVMVF9DQVRFR09SSUNBTF9QQUxFVFRFLFxuKTogc3RyaW5nW10ge1xuICBjb25zdCBhdXRob3JlZCA9IG1ldGEuY29sb3JzO1xuICByZXR1cm4gbWV0YS5jYXRlZ29yaWVzLm1hcCgobmFtZSwgaSkgPT4ge1xuICAgIGNvbnN0IGdpdmVuID0gYXV0aG9yZWQ/LltpXTtcbiAgICBpZiAoZ2l2ZW4pIHJldHVybiBnaXZlbjtcbiAgICBpZiAoaSA8IHBhbGV0dGUubGVuZ3RoKSByZXR1cm4gcGFsZXR0ZVtpXTtcbiAgICByZXR1cm4gZmFsbGJhY2tDb2xvckZvcihuYW1lLCBwYWxldHRlKTtcbiAgfSk7XG59XG5cbi8qKiBDYXRlZ29yaWNhbCBjb2x1bW4g4oaSIGZsYXQgcGVyLXBvaW50IFJHQkEgKGxlbmd0aCBgNCAqIGNvZGVzLmxlbmd0aGApLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGVuY29kZUNhdGVnb3JpY2FsKFxuICBjb2RlczogVWludDE2QXJyYXksIG9wdHM6IENhdGVnb3JpY2FsRW5jb2RpbmdPcHRpb25zLFxuKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3QgbiA9IGNvZGVzLmxlbmd0aDtcbiAgY29uc3Qgb3V0ID0gbmV3IEZsb2F0MzJBcnJheShuICogNCk7XG4gIC8vIFJlc29sdmUgZWFjaCBjYXRlZ29yeSBvbmNlIOKAlCBwYXJzaW5nIGEgaGV4IHN0cmluZyBwZXIgcG9pbnQgd291bGQgZG9taW5hdGUuXG4gIGNvbnN0IHJnYiA9IG9wdHMuY29sb3JzLm1hcChwYXJzZUhleCk7XG4gIGNvbnN0IG1pc3NpbmcgPSBvcHRzLm1pc3NpbmdDb2xvciA/PyBNSVNTSU5HX0NPTE9SO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IG47IGkrKykge1xuICAgIGNvbnN0IGNvZGUgPSBjb2Rlc1tpXTtcbiAgICBjb25zdCBjID0gY29kZSA9PT0gTk9fQ0FURUdPUlkgfHwgY29kZSA+PSByZ2IubGVuZ3RoID8gbWlzc2luZyA6IHJnYltjb2RlXTtcbiAgICBjb25zdCBvID0gaSAqIDQ7XG4gICAgb3V0W29dID0gY1swXSAvIDI1NTtcbiAgICBvdXRbbyArIDFdID0gY1sxXSAvIDI1NTtcbiAgICBvdXRbbyArIDJdID0gY1syXSAvIDI1NTtcbiAgICAvLyBBIHBvaW50IHdpdGggbm8gY2F0ZWdvcnkgaXMgYmFja2dyb3VuZCBieSBkZWZpbml0aW9uIOKAlCBuZXZlciBlbXBoYXNpc2VkLlxuICAgIG91dFtvICsgM10gPSBjb2RlID09PSBOT19DQVRFR09SWVxuICAgICAgPyBNYXRoLm1pbihhbHBoYUF0KGksIG9wdHMpLCBvcHRzLm11dGVkT3BhY2l0eSA/PyBERUZBVUxUX01VVEVEX09QQUNJVFkpXG4gICAgICA6IGFscGhhQXQoaSwgb3B0cyk7XG4gIH1cbiAgcmV0dXJuIG91dDtcbn1cblxuLyoqIENvbnRpbnVvdXMgY29sdW1uIC8gZ2VuZSB2ZWN0b3Ig4oaSIGZsYXQgcGVyLXBvaW50IFJHQkEuICovXG5leHBvcnQgZnVuY3Rpb24gZW5jb2RlQ29udGludW91cyhcbiAgdmFsdWVzOiBGbG9hdDMyQXJyYXksIG9wdHM6IENvbnRpbnVvdXNFbmNvZGluZ09wdGlvbnMsXG4pOiBGbG9hdDMyQXJyYXkge1xuICBjb25zdCBuID0gdmFsdWVzLmxlbmd0aDtcbiAgY29uc3Qgb3V0ID0gbmV3IEZsb2F0MzJBcnJheShuICogNCk7XG4gIGNvbnN0IG1pc3NpbmcgPSBvcHRzLm1pc3NpbmdDb2xvciA/PyBNSVNTSU5HX0NPTE9SO1xuICBjb25zdCBsdXQgPSBvcHRzLmx1dDtcbiAgY29uc3QgbG8gPSBvcHRzLmxvZyA/IE1hdGgubG9nMXAoTWF0aC5tYXgoMCwgb3B0cy5taW4pKSA6IG9wdHMubWluO1xuICBjb25zdCBoaSA9IG9wdHMubG9nID8gTWF0aC5sb2cxcChNYXRoLm1heCgwLCBvcHRzLm1heCkpIDogb3B0cy5tYXg7XG4gIC8vIEEgZGVnZW5lcmF0ZSB3aW5kb3cgKGFsbCB2YWx1ZXMgZXF1YWwpIG1hcHMgZXZlcnl0aGluZyB0byB0aGUgTFVUIG1pZHBvaW50XG4gIC8vIHJhdGhlciB0aGFuIGRpdmlkaW5nIGJ5IHplcm8uXG4gIGNvbnN0IHNwYW4gPSBoaSAtIGxvO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IG47IGkrKykge1xuICAgIGNvbnN0IHJhdyA9IHZhbHVlc1tpXTtcbiAgICBjb25zdCBvID0gaSAqIDQ7XG4gICAgaWYgKCFOdW1iZXIuaXNGaW5pdGUocmF3KSkge1xuICAgICAgb3V0W29dID0gbWlzc2luZ1swXSAvIDI1NTtcbiAgICAgIG91dFtvICsgMV0gPSBtaXNzaW5nWzFdIC8gMjU1O1xuICAgICAgb3V0W28gKyAyXSA9IG1pc3NpbmdbMl0gLyAyNTU7XG4gICAgICBvdXRbbyArIDNdID0gb3B0cy5tdXRlZE9wYWNpdHkgPz8gREVGQVVMVF9NVVRFRF9PUEFDSVRZO1xuICAgICAgY29udGludWU7XG4gICAgfVxuICAgIGNvbnN0IHYgPSBvcHRzLmxvZyA/IE1hdGgubG9nMXAoTWF0aC5tYXgoMCwgcmF3KSkgOiByYXc7XG4gICAgY29uc3QgdCA9IHNwYW4gPiAwID8gKHYgLSBsbykgLyBzcGFuIDogMC41O1xuICAgIGNvbnN0IGlkeCA9IE1hdGgubWF4KDAsIE1hdGgubWluKDI1NSwgTWF0aC5yb3VuZCh0ICogMjU1KSkpO1xuICAgIGNvbnN0IGMgPSBsdXRbaWR4XSA/PyBtaXNzaW5nO1xuICAgIG91dFtvXSA9IGNbMF0gLyAyNTU7XG4gICAgb3V0W28gKyAxXSA9IGNbMV0gLyAyNTU7XG4gICAgb3V0W28gKyAyXSA9IGNbMl0gLyAyNTU7XG4gICAgb3V0W28gKyAzXSA9IGFscGhhQXQoaSwgb3B0cyk7XG4gIH1cbiAgcmV0dXJuIG91dDtcbn1cblxuLyoqXG4gKiBDb250cmFzdCB3aW5kb3cgZnJvbSBwZXJjZW50aWxlcyBvZiB0aGUgZmluaXRlIHZhbHVlcyDigJQgdGhlIENvc014XG4gKiBcImNhcCBvdXRsaWVycyBhdCBwZXJjZW50aWxlcyB0byBpbXByb3ZlIGNvbnRyYXN0XCIgcnVsZS4gYGxvYC9gaGlgIGFyZVxuICogZnJhY3Rpb25zICgwLjAxIC8gMC45OSBjbGlwcyB0aGUgZXh0cmVtZSAxJSBhdCBlYWNoIGVuZCkuXG4gKlxuICogUmV0dXJucyBgWzAsIDFdYCBmb3IgYW4gZW1wdHkgb3IgYWxsLW1pc3NpbmcgdmVjdG9yIHNvIGNhbGxlcnMgYWx3YXlzIGdldCBhXG4gKiB1c2FibGUgd2luZG93LlxuICovXG5leHBvcnQgZnVuY3Rpb24gY29udHJhc3RXaW5kb3coXG4gIHZhbHVlczogRmxvYXQzMkFycmF5LCBsbyA9IDAuMDEsIGhpID0gMC45OSxcbik6IFtudW1iZXIsIG51bWJlcl0ge1xuICBjb25zdCBmaW5pdGU6IG51bWJlcltdID0gW107XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgaWYgKE51bWJlci5pc0Zpbml0ZSh2YWx1ZXNbaV0pKSBmaW5pdGUucHVzaCh2YWx1ZXNbaV0pO1xuICB9XG4gIGlmIChmaW5pdGUubGVuZ3RoID09PSAwKSByZXR1cm4gWzAsIDFdO1xuICBmaW5pdGUuc29ydCgoYSwgYikgPT4gYSAtIGIpO1xuICBjb25zdCBhdCA9IChmOiBudW1iZXIpID0+IHtcbiAgICBjb25zdCBrID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oZmluaXRlLmxlbmd0aCAtIDEsIE1hdGgucm91bmQoZiAqIChmaW5pdGUubGVuZ3RoIC0gMSkpKSk7XG4gICAgcmV0dXJuIGZpbml0ZVtrXTtcbiAgfTtcbiAgY29uc3QgbWluID0gYXQoTWF0aC5tYXgoMCwgTWF0aC5taW4oMSwgbG8pKSk7XG4gIGNvbnN0IG1heCA9IGF0KE1hdGgubWF4KDAsIE1hdGgubWluKDEsIGhpKSkpO1xuICAvLyBBIGZsYXQgb3IgaW52ZXJ0ZWQgd2luZG93IHdvdWxkIGRpdmlkZSBieSB6ZXJvIGRvd25zdHJlYW07IHdpZGVuIGl0LlxuICByZXR1cm4gbWF4ID4gbWluID8gW21pbiwgbWF4XSA6IFttaW4sIG1pbiArIDFdO1xufVxuXG4vKipcbiAqIE1hcmtlciBzaXplcyBmb3IgdGhlIHBvaW50cyBsYXllci4gbmFwYXJpLWpzIGBQb2ludHNMYXllci5zaXplYCBpcyBhXG4gKiAqKmRpYW1ldGVyKiogaW4gZGF0YSB1bml0cyB3aGlsZSBgU3BhdGlhbE9ic2VydmF0aW9ucy5yYWRpdXNgIGlzIGEgcmFkaXVzIOKAlFxuICogdGhlIGRvdWJsaW5nIGxpdmVzIGhlcmUgc28gbm8gY2FsbCBzaXRlIGhhcyB0byByZW1lbWJlciBpdC5cbiAqXG4gKiBgZmFsbGJhY2tSYWRpdXNgIGNvdmVycyBhIGRhdGFzZXQgdGhhdCBkZWNsYXJlcyBubyByYWRpdXMgKHNlZ21lbnRlZCBjZWxsc1xuICogb2Z0ZW4gZG9uJ3QpOyBpdCBpcyBpbiBpbWFnZSBwaXhlbHMsIGxpa2UgZXZlcnl0aGluZyBlbHNlIGluIHRoaXMgc3BhY2UuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtYXJrZXJEaWFtZXRlcnMoXG4gIG9ic2VydmF0aW9uczogU3BhdGlhbE9ic2VydmF0aW9ucywgZmFsbGJhY2tSYWRpdXMgPSA0LFxuKTogbnVtYmVyIHwgRmxvYXQzMkFycmF5IHtcbiAgY29uc3QgciA9IG9ic2VydmF0aW9ucy5yYWRpdXM7XG4gIGlmICh0eXBlb2YgciA9PT0gJ251bWJlcicpIHJldHVybiByICogMjtcbiAgaWYgKHIgaW5zdGFuY2VvZiBGbG9hdDMyQXJyYXkpIHtcbiAgICBjb25zdCBvdXQgPSBuZXcgRmxvYXQzMkFycmF5KHIubGVuZ3RoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHIubGVuZ3RoOyBpKyspIG91dFtpXSA9IHJbaV0gKiAyO1xuICAgIHJldHVybiBvdXQ7XG4gIH1cbiAgcmV0dXJuIGZhbGxiYWNrUmFkaXVzICogMjtcbn1cblxuLyoqXG4gKiBBZGFwdCBhIGZsYXQgUkdCQSBidWZmZXIgdG8gdGhlIGBSR0JBW11gIHNoYXBlIG5hcGFyaS1qcyAwLjEyLjAnc1xuICogYFBvaW50c0xheWVyLmZhY2VDb2xvcmAgYWNjZXB0cy4gT25lIDQtZWxlbWVudCBhcnJheSBwZXIgcG9pbnQg4oCUIHRoZSByZWFzb25cbiAqIHRoaXMgaXMgYSBuYW1lZCBib3VuZGFyeSByYXRoZXIgdGhhbiBpbmxpbmVkOiBpdCBpcyB0aGUgb25lIHBsYWNlIHRoZSBtb2RlbFxuICogc3RvcHMgYmVpbmcgdHlwZWQtYXJyYXkgc2hhcGVkLCBhbmQgaXQgaXMgd2hhdCBhIGZ1dHVyZSBuYXBhcmktanMgdGFraW5nIGFcbiAqIGBGbG9hdDMyQXJyYXlgIHdvdWxkIGRlbGV0ZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHRvUmdiYVR1cGxlcyhmbGF0OiBGbG9hdDMyQXJyYXkpOiBSR0JBW10ge1xuICBjb25zdCBuID0gZmxhdC5sZW5ndGggLyA0O1xuICBjb25zdCBvdXQ6IFJHQkFbXSA9IG5ldyBBcnJheShuKTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBuOyBpKyspIHtcbiAgICBjb25zdCBvID0gaSAqIDQ7XG4gICAgb3V0W2ldID0gW2ZsYXRbb10sIGZsYXRbbyArIDFdLCBmbGF0W28gKyAyXSwgZmxhdFtvICsgM11dO1xuICB9XG4gIHJldHVybiBvdXQ7XG59XG5cbi8qKiBDb252ZW5pZW5jZTogYSAyNTYtZW50cnkgTFVUIGZvciBhIGNvbG9ybWFwIHZhbHVlLCBmYWxsaW5nIGJhY2sgdG8gVmlyaWRpcy4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsdXRGb3IoY29sb3JtYXBWYWx1ZTogdW5rbm93biwgcmV2ZXJzZSA9IGZhbHNlKTogUmdiW10ge1xuICByZXR1cm4gYnVpbGRDb2xvcm1hcEx1dChjb2xvcm1hcFZhbHVlLCByZXZlcnNlKVxuICAgID8/IGJ1aWxkQ29sb3JtYXBMdXQoJ1ZpcmlkaXMnLCByZXZlcnNlKSE7XG59XG5cbi8qKiBUcnVlIHdoZW4gZXZlcnkgZW50cnkgb2YgYSBMVVQgaXMgYSBuZXV0cmFsIGdyZXkuIFRlc3RlZCBvbiB0aGUgVkFMVUVTIHJhdGhlclxuICogIHRoYW4gdGhlIGNvbG9ybWFwJ3MgbmFtZSwgc28gYSByZXZlcnNlZCBvciByZW5hbWVkIGdyZXkgcmFtcCBzdGlsbCBjb3VudHMuICovXG5leHBvcnQgZnVuY3Rpb24gaXNHcmF5c2NhbGVMdXQobHV0OiByZWFkb25seSBSZ2JbXSk6IGJvb2xlYW4ge1xuICByZXR1cm4gbHV0LmV2ZXJ5KChbciwgZywgYl0pID0+IHIgPT09IGcgJiYgZyA9PT0gYik7XG59XG5cbi8qKlxuICogTFVUIGZvciBhIHNwYXRpYWwgQ09OVElOVU9VUyBlbmNvZGluZyDigJQgYSBnZW5lLCBhIG51bWVyaWMgY29sdW1uLlxuICpcbiAqIGBvdmVycmlkZWAgaXMgdGhlIGNhbGxlcidzIGV4cGxpY2l0IGNob2ljZSBhbmQgd2lucyBvdXRyaWdodCwgZ3JleSBpbmNsdWRlZDogYVxuICogZ3JleSByYW1wIHBpY2tlZCBGT1IgdGhlIGRhdGEgaXMgYSBkZWNpc2lvbiwgbm90IHRoZSBpbWFnZSdzIGNvbG9ybWFwIGxlYWtpbmdcbiAqIGluLlxuICpcbiAqIFdpdGhvdXQgb25lLCB0aGUgZGlzcGxheSBjb2xvcm1hcCDigJQgdW5sZXNzIHRoYXQgY29sb3JtYXAgaXMgZ3JheXNjYWxlLiBUaGVcbiAqIHRpc3N1ZSB1bmRlcm5lYXRoIGlzIGdyYXlzY2FsZSwgc28gYSBncmV5IG92ZXJsYXkgb2YgYSBtZWFzdXJlbWVudCBpc1xuICogaW5kaXN0aW5ndWlzaGFibGUgZnJvbSB0aGUgYW5hdG9teSBpdCBpcyBkcmF3biBvdmVyLCBhbmQgdGhlIHJlYWRlciBjYW5ub3QgdGVsbFxuICogd2hpY2ggY2hhbm5lbCBhIGJyaWdodCBwaXhlbCBiZWxvbmdzIHRvLiBWaXJpZGlzIGlzIHRoZW4gdGhlIGZhbGxiYWNrLCB3aGljaCBpc1xuICogd2hhdCB0aGUgQ29zTXggZ3VpZGFuY2UgcHJlc2NyaWJlcyBmb3IgY29udGludW91cyB2YWx1ZXMgYW55d2F5LlxuICpcbiAqIFNoYXJlZCBieSB0aGUgbWFya2VycywgYm90aCBnZW5lIG1hcHMgYW5kIHRoZSBwYW5lbCdzIGNvbG91ciBiYXIsIHNvIG5vbmUgb2ZcbiAqIHRoZW0gY2FuIGRpc2FncmVlIGFib3V0IHdoYXQgYSBjb2xvdXIgbWVhbnMsIHdoaWNoZXZlciB3YXkgdGhlIGNob2ljZSBmYWxscy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNwYXRpYWxDb250aW51b3VzTHV0KFxuICBjb2xvcm1hcFZhbHVlOiB1bmtub3duLCByZXZlcnNlID0gZmFsc2UsIG92ZXJyaWRlPzogQ29sb3JtYXBWYWx1ZSB8IG51bGwsXG4pOiBSZ2JbXSB7XG4gIC8vIEFuIGlubGluZSBzY2FsZSBpcyBhbiBhcnJheSwgc28gZW1wdGluZXNzIGhhcyB0byBiZSBjaGVja2VkIGFzIHdlbGwgYXNcbiAgLy8gcHJlc2VuY2U6IGBbXWAgaXMgdHJ1dGh5IGFuZCB3b3VsZCByZXNvbHZlIHRvIHRoZSBmYWxsYmFjayBMVVQgc2lsZW50bHkuXG4gIGNvbnN0IGNob3NlbiA9IEFycmF5LmlzQXJyYXkob3ZlcnJpZGUpID8gKG92ZXJyaWRlLmxlbmd0aCA/IG92ZXJyaWRlIDogbnVsbCkgOiBvdmVycmlkZTtcbiAgaWYgKGNob3NlbikgcmV0dXJuIGx1dEZvcihjaG9zZW4sIHJldmVyc2UpO1xuICBjb25zdCBsdXQgPSBsdXRGb3IoY29sb3JtYXBWYWx1ZSwgcmV2ZXJzZSk7XG4gIHJldHVybiBpc0dyYXlzY2FsZUx1dChsdXQpID8gYnVpbGRDb2xvcm1hcEx1dCgnVmlyaWRpcycsIHJldmVyc2UpISA6IGx1dDtcbn1cbiJdfQ==
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { blurVolumeAxis } from './spatial-density';
|
|
2
|
+
/** In-place separable Gaussian along one axis of a `w × h` field. */
|
|
3
|
+
function blurAxis(field, w, h, axis, sigma) {
|
|
4
|
+
if (!(sigma > 0.01))
|
|
5
|
+
return;
|
|
6
|
+
const radius = Math.max(1, Math.ceil(sigma * 3));
|
|
7
|
+
const kernel = new Float32Array(radius * 2 + 1);
|
|
8
|
+
let sum = 0;
|
|
9
|
+
for (let i = -radius; i <= radius; i++) {
|
|
10
|
+
const v = Math.exp(-(i * i) / (2 * sigma * sigma));
|
|
11
|
+
kernel[i + radius] = v;
|
|
12
|
+
sum += v;
|
|
13
|
+
}
|
|
14
|
+
for (let i = 0; i < kernel.length; i++)
|
|
15
|
+
kernel[i] /= sum;
|
|
16
|
+
const len = axis === 0 ? w : h;
|
|
17
|
+
const stride = axis === 0 ? 1 : w;
|
|
18
|
+
const lines = axis === 0 ? h : w;
|
|
19
|
+
const line = new Float32Array(len);
|
|
20
|
+
for (let l = 0; l < lines; l++) {
|
|
21
|
+
const base = axis === 0 ? l * w : l;
|
|
22
|
+
for (let i = 0; i < len; i++)
|
|
23
|
+
line[i] = field[base + i * stride];
|
|
24
|
+
for (let i = 0; i < len; i++) {
|
|
25
|
+
let acc = 0;
|
|
26
|
+
for (let k = -radius; k <= radius; k++) {
|
|
27
|
+
// Clamped edges: zero-padding would darken the specimen's own boundary,
|
|
28
|
+
// and the support channel already says where there is no data at all.
|
|
29
|
+
const j = Math.min(len - 1, Math.max(0, i + k));
|
|
30
|
+
acc += line[j] * kernel[k + radius];
|
|
31
|
+
}
|
|
32
|
+
field[base + i * stride] = acc;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Estimate a gene's expression field over the image the observations sit in.
|
|
38
|
+
*
|
|
39
|
+
* Null when nothing lands on the raster — the caller then draws no layer, rather
|
|
40
|
+
* than an empty one the reader has to interpret.
|
|
41
|
+
*/
|
|
42
|
+
export function expressionField(obs, opts) {
|
|
43
|
+
const { width: w, height: h, step, sigma, values, indices } = opts;
|
|
44
|
+
if (w <= 0 || h <= 0)
|
|
45
|
+
return null;
|
|
46
|
+
const [sx, sy] = opts.ref?.scale ?? [1, 1];
|
|
47
|
+
const [tx, ty] = opts.ref?.translate ?? [0, 0];
|
|
48
|
+
const num = new Float32Array(w * h);
|
|
49
|
+
const den = new Float32Array(w * h);
|
|
50
|
+
const n = indices ? indices.length : obs.count;
|
|
51
|
+
let placed = 0;
|
|
52
|
+
for (let k = 0; k < n; k++) {
|
|
53
|
+
const i = indices ? indices[k] : k;
|
|
54
|
+
const e = values[i];
|
|
55
|
+
// A NaN is "not measured for this cell", which must not become a zero: it
|
|
56
|
+
// would pull the local mean down as if the gene were absent there.
|
|
57
|
+
if (!Number.isFinite(e))
|
|
58
|
+
continue;
|
|
59
|
+
const px = Math.floor((obs.x[i] * sx + tx) / step);
|
|
60
|
+
const py = Math.floor((obs.y[i] * sy + ty) / step);
|
|
61
|
+
if (px < 0 || px >= w || py < 0 || py >= h)
|
|
62
|
+
continue;
|
|
63
|
+
const at = py * w + px;
|
|
64
|
+
num[at] += e;
|
|
65
|
+
den[at] += 1;
|
|
66
|
+
placed++;
|
|
67
|
+
}
|
|
68
|
+
if (!placed)
|
|
69
|
+
return null;
|
|
70
|
+
for (const axis of [0, 1]) {
|
|
71
|
+
blurAxis(num, w, h, axis, sigma);
|
|
72
|
+
blurAxis(den, w, h, axis, sigma);
|
|
73
|
+
}
|
|
74
|
+
const mean = new Float32Array(w * h);
|
|
75
|
+
let lo = Infinity;
|
|
76
|
+
let hi = -Infinity;
|
|
77
|
+
const positives = [];
|
|
78
|
+
for (let i = 0; i < mean.length; i++) {
|
|
79
|
+
const d = den[i];
|
|
80
|
+
if (d <= 0)
|
|
81
|
+
continue;
|
|
82
|
+
const m = num[i] / d;
|
|
83
|
+
mean[i] = m;
|
|
84
|
+
positives.push(d);
|
|
85
|
+
if (m < lo)
|
|
86
|
+
lo = m;
|
|
87
|
+
if (m > hi)
|
|
88
|
+
hi = m;
|
|
89
|
+
}
|
|
90
|
+
if (!Number.isFinite(lo))
|
|
91
|
+
return null;
|
|
92
|
+
positives.sort((a, b) => a - b);
|
|
93
|
+
const supportScale = positives[positives.length >> 1] || 0;
|
|
94
|
+
return {
|
|
95
|
+
width: w,
|
|
96
|
+
height: h,
|
|
97
|
+
mean,
|
|
98
|
+
support: den,
|
|
99
|
+
supportScale,
|
|
100
|
+
range: hi > lo ? [lo, hi] : [lo, lo + 1],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Colour a field into RGBA8 for a `channels: 4` image source.
|
|
105
|
+
*
|
|
106
|
+
* Alpha comes from the SUPPORT, ramped to the field's own median support rather
|
|
107
|
+
* than a constant: a pixel backed by one distant cell is faint, a properly sampled
|
|
108
|
+
* one is solid, and unmeasured tissue is fully transparent — so the map never
|
|
109
|
+
* asserts a measurement where there was none. `opacity` scales the whole layer on
|
|
110
|
+
* top of that.
|
|
111
|
+
*/
|
|
112
|
+
export function colorExpressionField(field, lut, window, opts = {}) {
|
|
113
|
+
const { mean, support, supportScale } = field;
|
|
114
|
+
const rgba = new Uint8Array(mean.length * 4);
|
|
115
|
+
const log = !!opts.log;
|
|
116
|
+
const opacity = opts.opacity ?? 1;
|
|
117
|
+
const norm = (v) => (log ? Math.log1p(Math.max(0, v)) : v);
|
|
118
|
+
const lo = norm(window[0]);
|
|
119
|
+
const hi = norm(window[1]);
|
|
120
|
+
const span = hi > lo ? hi - lo : 1;
|
|
121
|
+
const last = lut.length - 1;
|
|
122
|
+
const sat = supportScale > 0 ? supportScale : 1;
|
|
123
|
+
for (let i = 0; i < mean.length; i++) {
|
|
124
|
+
const d = support[i];
|
|
125
|
+
if (d <= 0)
|
|
126
|
+
continue; // unmeasured — leave it transparent
|
|
127
|
+
const t = Math.min(1, Math.max(0, (norm(mean[i]) - lo) / span));
|
|
128
|
+
const c = lut[Math.round(t * last)] ?? lut[last];
|
|
129
|
+
const a = Math.min(1, d / sat) * opacity;
|
|
130
|
+
const o = i * 4;
|
|
131
|
+
// Premultiplied, matching the canvas alpha mode the renderer configures.
|
|
132
|
+
rgba[o] = Math.round(c[0] * a);
|
|
133
|
+
rgba[o + 1] = Math.round(c[1] * a);
|
|
134
|
+
rgba[o + 2] = Math.round(c[2] * a);
|
|
135
|
+
rgba[o + 3] = Math.round(a * 255);
|
|
136
|
+
}
|
|
137
|
+
return rgba;
|
|
138
|
+
}
|
|
139
|
+
export function expressionVolume(obs, grid, opts) {
|
|
140
|
+
const { width: w, height: h, depth: d, voxelSize } = grid;
|
|
141
|
+
if (w <= 0 || h <= 0 || d <= 0)
|
|
142
|
+
return null;
|
|
143
|
+
const num = new Float32Array(w * h * d);
|
|
144
|
+
const den = new Float32Array(w * h * d);
|
|
145
|
+
const idx = opts.indices;
|
|
146
|
+
const n = idx ? idx.length : obs.count;
|
|
147
|
+
const z = obs.z;
|
|
148
|
+
let placed = 0;
|
|
149
|
+
// The imaged range, taken from the observations that were actually included:
|
|
150
|
+
// the estimate may not reach past the outermost section it was built from.
|
|
151
|
+
let first = d;
|
|
152
|
+
let last = -1;
|
|
153
|
+
for (let k = 0; k < n; k++) {
|
|
154
|
+
const i = idx ? idx[k] : k;
|
|
155
|
+
const e = opts.values[i];
|
|
156
|
+
// Not measured for this cell — must not be read as "the gene is absent here".
|
|
157
|
+
if (!Number.isFinite(e))
|
|
158
|
+
continue;
|
|
159
|
+
const vx = Math.floor(obs.x[i] / voxelSize[0]);
|
|
160
|
+
const vy = Math.floor(obs.y[i] / voxelSize[1]);
|
|
161
|
+
const vz = z ? Math.floor(z[i] / voxelSize[2]) : 0;
|
|
162
|
+
if (vx < 0 || vx >= w || vy < 0 || vy >= h || vz < 0 || vz >= d)
|
|
163
|
+
continue;
|
|
164
|
+
const at = (vz * h + vy) * w + vx;
|
|
165
|
+
num[at] += e;
|
|
166
|
+
den[at] += 1;
|
|
167
|
+
placed++;
|
|
168
|
+
if (vz < first)
|
|
169
|
+
first = vz;
|
|
170
|
+
if (vz > last)
|
|
171
|
+
last = vz;
|
|
172
|
+
}
|
|
173
|
+
if (!placed)
|
|
174
|
+
return null;
|
|
175
|
+
const sigmaVox = [
|
|
176
|
+
opts.sigma[0] / voxelSize[0], opts.sigma[1] / voxelSize[1], opts.sigma[2] / voxelSize[2],
|
|
177
|
+
];
|
|
178
|
+
// In-plane always: a section's own gene map is a 2D field, whichever mode this is.
|
|
179
|
+
for (const axis of [0, 1]) {
|
|
180
|
+
blurVolumeAxis(num, w, h, d, axis, sigmaVox[axis]);
|
|
181
|
+
blurVolumeAxis(den, w, h, d, axis, sigmaVox[axis]);
|
|
182
|
+
}
|
|
183
|
+
if (opts.interpolate) {
|
|
184
|
+
blurVolumeAxis(num, w, h, d, 2, sigmaVox[2]);
|
|
185
|
+
blurVolumeAxis(den, w, h, d, 2, sigmaVox[2]);
|
|
186
|
+
const plane = w * h;
|
|
187
|
+
for (let k = 0; k < d; k++) {
|
|
188
|
+
if (k >= first && k <= last)
|
|
189
|
+
continue;
|
|
190
|
+
// The blur leaves a tail past the outermost sections; keeping it would draw
|
|
191
|
+
// expression where the specimen was never sectioned.
|
|
192
|
+
num.fill(0, k * plane, (k + 1) * plane);
|
|
193
|
+
den.fill(0, k * plane, (k + 1) * plane);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const mean = new Float32Array(num.length);
|
|
197
|
+
let lo = Infinity;
|
|
198
|
+
let hi = -Infinity;
|
|
199
|
+
for (let i = 0; i < mean.length; i++) {
|
|
200
|
+
const support = den[i];
|
|
201
|
+
if (support <= 0)
|
|
202
|
+
continue;
|
|
203
|
+
const m = num[i] / support;
|
|
204
|
+
mean[i] = m;
|
|
205
|
+
if (m < lo)
|
|
206
|
+
lo = m;
|
|
207
|
+
if (m > hi)
|
|
208
|
+
hi = m;
|
|
209
|
+
}
|
|
210
|
+
if (!Number.isFinite(lo))
|
|
211
|
+
return null;
|
|
212
|
+
return {
|
|
213
|
+
width: w,
|
|
214
|
+
height: h,
|
|
215
|
+
depth: d,
|
|
216
|
+
mean,
|
|
217
|
+
support: den,
|
|
218
|
+
range: hi > lo ? [lo, hi] : [lo, lo + 1],
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Encode a field into the uint8 scalar a `VolumeLayer` renders, windowed the same
|
|
223
|
+
* way the 2D map and the markers are so the three cannot disagree.
|
|
224
|
+
*
|
|
225
|
+
* `floor` lifts every MEASURED voxel to at least this fraction of full scale, so
|
|
226
|
+
* that "the gene is not here" can be told from "nobody sectioned here" — the
|
|
227
|
+
* distinction the 2D map keeps with its separate support channel, which a scalar
|
|
228
|
+
* volume cannot express because its raymarch takes alpha from the same number it
|
|
229
|
+
* takes colour from.
|
|
230
|
+
*
|
|
231
|
+
* It defaults to OFF, because in a volume that trade does not pay. A ray crosses
|
|
232
|
+
* tens of measured planes and the DVR composites every one of them, so a floor
|
|
233
|
+
* large enough to see on a single plane saturates to an opaque shell over the
|
|
234
|
+
* whole specimen and buries the signal it was meant to contextualise, while one
|
|
235
|
+
* small enough to stay faint is already invisible per plane. The measured envelope
|
|
236
|
+
* is better read from the anatomy layer and the observations, which are drawn in
|
|
237
|
+
* the same scene — and the surrounding density volumes take the same no-floor
|
|
238
|
+
* approach, so the scene stays internally consistent.
|
|
239
|
+
*/
|
|
240
|
+
export function encodeExpressionVolume(field, window, opts = {}) {
|
|
241
|
+
const { mean, support } = field;
|
|
242
|
+
const out = new Uint8Array(mean.length);
|
|
243
|
+
const log = !!opts.log;
|
|
244
|
+
const floor = Math.min(1, Math.max(0, opts.floor ?? 0));
|
|
245
|
+
const norm = (v) => (log ? Math.log1p(Math.max(0, v)) : v);
|
|
246
|
+
const lo = norm(window[0]);
|
|
247
|
+
const hi = norm(window[1]);
|
|
248
|
+
const span = hi > lo ? hi - lo : 1;
|
|
249
|
+
for (let i = 0; i < mean.length; i++) {
|
|
250
|
+
if (support[i] <= 0)
|
|
251
|
+
continue; // unmeasured — zero, so the raymarch skips it
|
|
252
|
+
const t = Math.min(1, Math.max(0, (norm(mean[i]) - lo) / span));
|
|
253
|
+
out[i] = Math.round((floor + (1 - floor) * t) * 255);
|
|
254
|
+
}
|
|
255
|
+
return out;
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BhdGlhbC1leHByZXNzaW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9zcGF0aWFsL3NwYXRpYWwtZXhwcmVzc2lvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsY0FBYyxFQUFlLE1BQU0sbUJBQW1CLENBQUM7QUEwRGhFLHFFQUFxRTtBQUNyRSxTQUFTLFFBQVEsQ0FBQyxLQUFtQixFQUFFLENBQVMsRUFBRSxDQUFTLEVBQUUsSUFBVyxFQUFFLEtBQWE7SUFDckYsSUFBSSxDQUFDLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQztRQUFFLE9BQU87SUFDNUIsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNqRCxNQUFNLE1BQU0sR0FBRyxJQUFJLFlBQVksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ2hELElBQUksR0FBRyxHQUFHLENBQUMsQ0FBQztJQUNaLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxJQUFJLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ3ZDLE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxLQUFLLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUNuRCxNQUFNLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN2QixHQUFHLElBQUksQ0FBQyxDQUFDO0lBQ1gsQ0FBQztJQUNELEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRTtRQUFFLE1BQU0sQ0FB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LENBQVUsRUFBRSxDQUFDO1FBQ25DLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxJQUFJLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDakMsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBRSxLQUFLLENBQUMsQ0FBQztJQUNuQyxDQUFDO0lBRUQsTUFBTSxJQUFJLEdBQUcsSUFBSSxZQUFZLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ3JDLElBQUksRUFBRSxHQUFHLFFBQVEsQ0FBQztJQUNsQixJQUFJLEVBQUUsR0FBRyxDQUFDLFFBQVEsQ0FBQztJQUNuQixNQUFNLFNBQVMsR0FBYSxFQUFFLENBQUM7SUFDL0IsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUNyQyxNQUFNLENBQUMsR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDakIsSUFBSSxDQUFDLElBQUksQ0FBQztZQUFFLFNBQVM7UUFDckIsTUFBTSxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNyQixJQUFJLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ1osU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNsQixJQUFJLENBQUMsR0FBRyxFQUFFO1lBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUNuQixJQUFJLENBQUMsR0FBRyxFQUFFO1lBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUNyQixDQUFDO0lBQ0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFFdEMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUNo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RSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzNFLE1BQU0sRUFBRSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMzQixNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDM0IsTUFBTSxJQUFJLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ25DLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDckMsSUFBSSxPQUFPLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztZQUFFLFNBQVMsQ0FBQyw4Q0FBOEM7UUFDN0UsTUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUNoRSxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsQ0FBQztJQUN2RCxDQUFDO0lBQ0QsT0FBTyxHQUFHLENBQUM7QUFDYixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgU3BhdGlhbEltYWdlUmVmLCBTcGF0aWFsT2JzZXJ2YXRpb25zIH0gZnJvbSAnLi4vY29udHJhY3RzL3NwYXRpYWwtZGF0YXNldC5jb250cmFjdCc7XG5pbXBvcnQgeyBibHVyVm9sdW1lQXhpcywgRGVuc2l0eUdyaWQgfSBmcm9tICcuL3NwYXRpYWwtZGVuc2l0eSc7XG5cbi8qKlxuICogVHVybmluZyBwZXItY2VsbCBleHByZXNzaW9uIGludG8gYSAqKmdlbmUgbWFwKiog4oCUIGEgY29udGludW91cyBmaWVsZCBvdmVyIHRoZVxuICogdGlzc3VlLCBkcmF3biB1bmRlciB0aGUgY2VsbHMgcmF0aGVyIHRoYW4gaW5zdGVhZCBvZiB0aGVtLlxuICpcbiAqIEEgc2NhdHRlciBjb2xvdXJlZCBieSBhIGdlbmUgYW5zd2VycyBcIndoaWNoIGNlbGxzIGV4cHJlc3MgaXRcIjsgaXQgZG9lcyBub3QgYW5zd2VyXG4gKiBcIndoZXJlIGlzIGl0IGV4cHJlc3NlZFwiLCBiZWNhdXNlIHRoZSBleWUgY2Fubm90IGludGVncmF0ZSB0aG91c2FuZHMgb2Ygc21hbGwgZG90c1xuICogaW50byBhIHRlcnJpdG9yeS4gQSBmaWVsZCBjYW4gYmUgZXN0aW1hdGVkIGJldHdlZW4gdGhlIGNlbGxzIGFuZCByZWFkIGFzIGFcbiAqIHJlZ2lvbiwgd2hpY2ggaXMgd2hhdCB0aGUgQ29zTXggZ3VpZGFuY2UgYW5kIFNwYXRpYWwtTGl2ZSdzIGhlYXQgbGF5ZXIgYm90aCBkby5cbiAqXG4gKiBUaGUgcXVhbnRpdHkgaXMgdGhlIGtlcm5lbC13ZWlnaHRlZCAqKk1FQU4gcGVyIGNlbGwqKiwgbm90IGEgc3VtOlxuICpcbiAqIGBgYFxuICogICBtZWFuKHApID0gzqPhtaIgdyhwLCB44bWiKcK3ZeG1oiAgLyAgzqPhtaIgdyhwLCB44bWiKVxuICogYGBgXG4gKlxuICogQSBzdW0gd291bGQgY29uZmxhdGUgXCJtYW55IGNlbGxzIGhlcmVcIiB3aXRoIFwiaGlnaCBleHByZXNzaW9uIGhlcmVcIiDigJQgYSBkZW5zZVxuICogcmVnaW9uIHdvdWxkIGdsb3cgd2hhdGV2ZXIgaXRzIGNlbGxzIHdlcmUgZG9pbmcsIHdoaWNoIGlzIHRoZSBzaW5nbGUgZWFzaWVzdCB3YXlcbiAqIHRvIHJlYWQgYSBnZW5lIG1hcCB3cm9uZ2x5LiBUaGUgZGVub21pbmF0b3IgaXMgYWxzbyB3aGF0IG1ha2VzIGVtcHRpbmVzc1xuICogZXhwcmVzc2libGU6IHdoZXJlIG5vIGNlbGwgd2FzIG1lYXN1cmVkIHRoZSBtZWFuIGlzICp1bmRlZmluZWQqLCBub3QgemVybywgc28gdGhlXG4gKiBjYWxsZXIgZ2V0cyBhIGBzdXBwb3J0YCBjaGFubmVsIGFuZCBjYW4gZHJhdyBub3RoaW5nIHRoZXJlIGluc3RlYWQgb2YgZHJhd2luZyB0aGVcbiAqIGNvbG9ybWFwJ3MgbG93IGVuZCBvdmVyIHVubWVhc3VyZWQgdGlzc3VlLlxuICpcbiAqIFB1cmUg4oCUIG5vIEFuZ3VsYXIsIG5vIEdQVSwgbm8gY29sb3VyIOKAlCBsaWtlIGBzcGF0aWFsLWRlbnNpdHkudHNgLCBhbmQgdGVzdGVkIHRoZVxuICogc2FtZSB3YXkuXG4gKi9cblxuZXhwb3J0IGludGVyZmFjZSBFeHByZXNzaW9uRmllbGRPcHRpb25zIHtcbiAgLyoqIERhdGHihpJpbWFnZSBhZmZpbmUsIHNvIHRoZSBmaWVsZCBsYW5kcyBpbiB0aGUgZGlzcGxheWVkIGltYWdlJ3MgcGl4ZWxzLiAqL1xuICByZWY/OiBTcGF0aWFsSW1hZ2VSZWY7XG4gIC8qKiBSYXN0ZXIgc2l6ZSBpbiBmaWVsZCBwaXhlbHMuICovXG4gIHdpZHRoOiBudW1iZXI7XG4gIGhlaWdodDogbnVtYmVyO1xuICAvKiogSW1hZ2UgcGl4ZWxzIHBlciBmaWVsZCBwaXhlbCDigJQgdGhlIGZpZWxkIGlzIGNvYXJzZXIgdGhhbiB0aGUgc2xpZGUgaXQgY292ZXJzLiAqL1xuICBzdGVwOiBudW1iZXI7XG4gIC8qKiBLZXJuZWwgz4MgaW4gRklFTEQgcGl4ZWxzLiAqL1xuICBzaWdtYTogbnVtYmVyO1xuICAvKiogT25lIGV4cHJlc3Npb24gdmFsdWUgcGVyIG9ic2VydmF0aW9uLiAqL1xuICB2YWx1ZXM6IEZsb2F0MzJBcnJheTtcbiAgLyoqIE9ic2VydmF0aW9ucyB0byBpbmNsdWRlOyBldmVyeSBvbmUgd2hlbiBhYnNlbnQgKGEgcGxhbmUsIGEgc2VsZWN0aW9uKS4gKi9cbiAgaW5kaWNlcz86IFVpbnQzMkFycmF5O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEV4cHJlc3Npb25GaWVsZCB7XG4gIHdpZHRoOiBudW1iZXI7XG4gIGhlaWdodDogbnVtYmVyO1xuICAvKiogS2VybmVsLXdlaWdodGVkIG1lYW4gZXhwcmVzc2lvbiwgdmFsaWQgb25seSB3aGVyZSBgc3VwcG9ydCA+IDBgLiAqL1xuICBtZWFuOiBGbG9hdDMyQXJyYXk7XG4gIC8qKiBLZXJuZWwtd2VpZ2h0ZWQgY2VsbCBjb3VudCBwZXIgZmllbGQgcGl4ZWwg4oCUIDAgd2hlcmUgbm90aGluZyB3YXMgbWVhc3VyZWQuICovXG4gIHN1cHBvcnQ6IEZsb2F0MzJBcnJheTtcbiAgLyoqIE1lZGlhbiBwb3NpdGl2ZSBzdXBwb3J0OiB0aGUgc2NhbGUgYXQgd2hpY2ggYSBwaXhlbCBpcyBcInByb3Blcmx5IHNhbXBsZWRcIixcbiAgICogIGFuZCBhIGRhdGEtZHJpdmVuIHNhdHVyYXRpb24gcG9pbnQgZm9yIGFuIG9wYWNpdHkgcmFtcC4gKi9cbiAgc3VwcG9ydFNjYWxlOiBudW1iZXI7XG4gIC8qKiBGaW5pdGUgcmFuZ2Ugb2YgYG1lYW5gIG92ZXIgdGhlIHN1cHBvcnRlZCBwaXhlbHMsIGZvciBhIGNvbnRyYXN0IHdpbmRvdy4gKi9cbiAgcmFuZ2U6IFtudW1iZXIsIG51bWJlcl07XG59XG5cbi8qKiBJbi1wbGFjZSBzZXBhcmFibGUgR2F1c3NpYW4gYWxvbmcgb25lIGF4aXMgb2YgYSBgdyDDlyBoYCBmaWVsZC4gKi9cbmZ1bmN0aW9uIGJsdXJBeGlzKGZpZWxkOiBGbG9hdDMyQXJyYXksIHc6IG51bWJlciwgaDogbnVtYmVyLCBheGlzOiAwIHwgMSwgc2lnbWE6IG51bWJlcik6IHZvaWQge1xuICBpZiAoIShzaWdtYSA+IDAuMDEpKSByZXR1cm47XG4gIGNvbnN0IHJhZGl1cyA9IE1hdGgubWF4KDEsIE1hdGguY2VpbChzaWdtYSAqIDMpKTtcbiAgY29uc3Qga2VybmVsID0gbmV3IEZsb2F0MzJBcnJheShyYWRpdXMgKiAyICsgMSk7XG4gIGxldCBzdW0gPSAwO1xuICBmb3IgKGxldCBpID0gLXJhZGl1czsgaSA8PSByYWRpdXM7IGkrKykge1xuICAgIGNvbnN0IHYgPSBNYXRoLmV4cCgtKGkgKiBpKSAvICgyICogc2lnbWEgKiBzaWdtYSkpO1xuICAgIGtlcm5lbFtpICsgcmFkaXVzXSA9IHY7XG4gICAgc3VtICs9IHY7XG4gIH1cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBrZXJuZWwubGVuZ3RoOyBpKyspIGtlcm5lbFtpXSAvPSBzdW07XG5cbiAgY29uc3QgbGVuID0gYXhpcyA9PT0gMCA/IHcgOiBoO1xuICBjb25zdCBzdHJpZGUgPSBheGlzID09PSAwID8gMSA6IHc7XG4gIGNvbnN0IGxpbmVzID0gYXhpcyA9PT0gMCA/IGggOiB3O1xuICBjb25zdCBsaW5lID0gbmV3IEZsb2F0MzJBcnJheShsZW4pO1xuICBmb3IgKGxldCBsID0gMDsgbCA8IGxpbmVzOyBsKyspIHtcbiAgICBjb25zdCBiYXNlID0gYXhpcyA9PT0gMCA/IGwgKiB3IDogbDtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxlbjsgaSsrKSBsaW5lW2ldID0gZmllbGRbYmFzZSArIGkgKiBzdHJpZGVdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgIGxldCBhY2MgPSAwO1xuICAgICAgZm9yIChsZXQgayA9IC1yYWRpdXM7IGsgPD0gcmFkaXVzOyBrKyspIHtcbiAgICAgICAgLy8gQ2xhbXBlZCBlZGdlczogemVyby1wYWRkaW5nIHdvdWxkIGRhcmtlbiB0aGUgc3BlY2ltZW4ncyBvd24gYm91bmRhcnksXG4gICAgICAgIC8vIGFuZCB0aGUgc3VwcG9ydCBjaGFubmVsIGFscmVhZHkgc2F5cyB3aGVyZSB0aGVyZSBpcyBubyBkYXRhIGF0IGFsbC5cbiAgICAgICAgY29uc3QgaiA9IE1hdGgubWluKGxlbiAtIDEsIE1hdGgubWF4KDAsIGkgKyBrKSk7XG4gICAgICAgIGFjYyArPSBsaW5lW2pdICoga2VybmVsW2sgKyByYWRpdXNdO1xuICAgICAgfVxuICAgICAgZmllbGRbYmFzZSArIGkgKiBzdHJpZGVdID0gYWNjO1xuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIEVzdGltYXRlIGEgZ2VuZSdzIGV4cHJlc3Npb24gZmllbGQgb3ZlciB0aGUgaW1hZ2UgdGhlIG9ic2VydmF0aW9ucyBzaXQgaW4uXG4gKlxuICogTnVsbCB3aGVuIG5vdGhpbmcgbGFuZHMgb24gdGhlIHJhc3RlciDigJQgdGhlIGNhbGxlciB0aGVuIGRyYXdzIG5vIGxheWVyLCByYXRoZXJcbiAqIHRoYW4gYW4gZW1wdHkgb25lIHRoZSByZWFkZXIgaGFzIHRvIGludGVycHJldC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGV4cHJlc3Npb25GaWVsZChcbiAgb2JzOiBTcGF0aWFsT2JzZXJ2YXRpb25zLFxuICBvcHRzOiBFeHByZXNzaW9uRmllbGRPcHRpb25zLFxuKTogRXhwcmVzc2lvbkZpZWxkIHwgbnVsbCB7XG4gIGNvbnN0IHsgd2lkdGg6IHcsIGhlaWdodDogaCwgc3RlcCwgc2lnbWEsIHZhbHVlcywgaW5kaWNlcyB9ID0gb3B0cztcbiAgaWYgKHcgPD0gMCB8fCBoIDw9IDApIHJldHVybiBudWxsO1xuICBjb25zdCBbc3gsIHN5XSA9IG9wdHMucmVmPy5zY2FsZSA/PyBbMSwgMV07XG4gIGNvbnN0IFt0eCwgdHldID0gb3B0cy5yZWY/LnRyYW5zbGF0ZSA/PyBbMCwgMF07XG5cbiAgY29uc3QgbnVtID0gbmV3IEZsb2F0MzJBcnJheSh3ICogaCk7XG4gIGNvbnN0IGRlbiA9IG5ldyBGbG9hdDMyQXJyYXkodyAqIGgpO1xuICBjb25zdCBuID0gaW5kaWNlcyA/IGluZGljZXMubGVuZ3RoIDogb2JzLmNvdW50O1xuICBsZXQgcGxhY2VkID0gMDtcbiAgZm9yIChsZXQgayA9IDA7IGsgPCBuOyBrKyspIHtcbiAgICBjb25zdCBpID0gaW5kaWNlcyA/IGluZGljZXNba10gOiBrO1xuICAgIGNvbnN0IGUgPSB2YWx1ZXNbaV07XG4gICAgLy8gQSBOYU4gaXMgXCJub3QgbWVhc3VyZWQgZm9yIHRoaXMgY2VsbFwiLCB3aGljaCBtdXN0IG5vdCBiZWNvbWUgYSB6ZXJvOiBpdFxuICAgIC8vIHdvdWxkIHB1bGwgdGhlIGxvY2FsIG1lYW4gZG93biBhcyBpZiB0aGUgZ2VuZSB3ZXJlIGFic2VudCB0aGVyZS5cbiAgICBpZiAoIU51bWJlci5pc0Zpbml0ZShlKSkgY29udGludWU7XG4gICAgY29uc3QgcHggPSBNYXRoLmZsb29yKChvYnMueFtpXSAqIHN4ICsgdHgpIC8gc3RlcCk7XG4gICAgY29uc3QgcHkgPSBNYXRoLmZsb29yKChvYnMueVtpXSAqIHN5ICsgdHkpIC8gc3RlcCk7XG4gICAgaWYgKHB4IDwgMCB8fCBweCA+PSB3IHx8IHB5IDwgMCB8fCBweSA+PSBoKSBjb250aW51ZTtcbiAgICBjb25zdCBhdCA9IHB5ICogdyArIHB4O1xuICAgIG51bVthdF0gKz0gZTtcbiAgICBkZW5bYXRdICs9IDE7XG4gICAgcGxhY2VkKys7XG4gIH1cbiAgaWYgKCFwbGFjZWQpIHJldHVybiBudWxsO1xuXG4gIGZvciAoY29uc3QgYXhpcyBvZiBbMCwgMV0gYXMgY29uc3QpIHtcbiAgICBibHVyQXhpcyhudW0sIHcsIGgsIGF4aXMsIHNpZ21hKTtcbiAgICBibHVyQXhpcyhkZW4sIHcsIGgsIGF4aXMsIHNpZ21hKTtcbiAgfVxuXG4gIGNvbnN0IG1lYW4gPSBuZXcgRmxvYXQzMkFycmF5KHcgKiBoKTtcbiAgbGV0IGxvID0gSW5maW5pdHk7XG4gIGxldCBoaSA9IC1JbmZpbml0eTtcbiAgY29uc3QgcG9zaXRpdmVzOiBudW1iZXJbXSA9IFtdO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IG1lYW4ubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBkID0gZGVuW2ldO1xuICAgIGlmIChkIDw9IDApIGNvbnRpbnVlO1xuICAgIGNvbnN0IG0gPSBudW1baV0gLyBkO1xuICAgIG1lYW5baV0gPSBtO1xuICAgIHBvc2l0aXZlcy5wdXNoKGQpO1xuICAgIGlmIChtIDwgbG8pIGxvID0gbTtcbiAgICBpZiAobSA+IGhpKSBoaSA9IG07XG4gIH1cbiAgaWYgKCFOdW1iZXIuaXNGaW5pdGUobG8pKSByZXR1cm4gbnVsbDtcblxuICBwb3NpdGl2ZXMuc29ydCgoYSwgYikgPT4gYSAtIGIpO1xuICBjb25zdCBzdXBwb3J0U2NhbGUgPSBwb3NpdGl2ZXNbcG9zaXRpdmVzLmxlbmd0aCA+PiAxXSB8fCAwO1xuICByZXR1cm4ge1xuICAgIHdpZHRoOiB3LFxuICAgIGhlaWdodDogaCxcbiAgICBtZWFuLFxuICAgIHN1cHBvcnQ6IGRlbixcbiAgICBzdXBwb3J0U2NhbGUsXG4gICAgcmFuZ2U6IGhpID4gbG8gPyBbbG8sIGhpXSA6IFtsbywgbG8gKyAxXSxcbiAgfTtcbn1cblxuLyoqXG4gKiBDb2xvdXIgYSBmaWVsZCBpbnRvIFJHQkE4IGZvciBhIGBjaGFubmVsczogNGAgaW1hZ2Ugc291cmNlLlxuICpcbiAqIEFscGhhIGNvbWVzIGZyb20gdGhlIFNVUFBPUlQsIHJhbXBlZCB0byB0aGUgZmllbGQncyBvd24gbWVkaWFuIHN1cHBvcnQgcmF0aGVyXG4gKiB0aGFuIGEgY29uc3RhbnQ6IGEgcGl4ZWwgYmFja2VkIGJ5IG9uZSBkaXN0YW50IGNlbGwgaXMgZmFpbnQsIGEgcHJvcGVybHkgc2FtcGxlZFxuICogb25lIGlzIHNvbGlkLCBhbmQgdW5tZWFzdXJlZCB0aXNzdWUgaXMgZnVsbHkgdHJhbnNwYXJlbnQg4oCUIHNvIHRoZSBtYXAgbmV2ZXJcbiAqIGFzc2VydHMgYSBtZWFzdXJlbWVudCB3aGVyZSB0aGVyZSB3YXMgbm9uZS4gYG9wYWNpdHlgIHNjYWxlcyB0aGUgd2hvbGUgbGF5ZXIgb25cbiAqIHRvcCBvZiB0aGF0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gY29sb3JFeHByZXNzaW9uRmllbGQoXG4gIGZpZWxkOiBFeHByZXNzaW9uRmllbGQsXG4gIGx1dDogcmVhZG9ubHkgKHJlYWRvbmx5IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSlbXSxcbiAgd2luZG93OiBbbnVtYmVyLCBudW1iZXJdLFxuICBvcHRzOiB7IGxvZz86IGJvb2xlYW47IG9wYWNpdHk/OiBudW1iZXIgfSA9IHt9LFxuKTogVWludDhBcnJheSB7XG4gIGNvbnN0IHsgbWVhbiwgc3VwcG9ydCwgc3VwcG9ydFNjYWxlIH0gPSBmaWVsZDtcbiAgY29uc3QgcmdiYSA9IG5ldyBVaW50OEFycmF5KG1lYW4ubGVuZ3RoICogNCk7XG4gIGNvbnN0IGxvZyA9ICEhb3B0cy5sb2c7XG4gIGNvbnN0IG9wYWNpdHkgPSBvcHRzLm9wYWNpdHkgPz8gMTtcbiAgY29uc3Qgbm9ybSA9ICh2OiBudW1iZXIpOiBudW1iZXIgPT4gKGxvZyA/IE1hdGgubG9nMXAoTWF0aC5tYXgoMCwgdikpIDogdik7XG4gIGNvbnN0IGxvID0gbm9ybSh3aW5kb3dbMF0pO1xuICBjb25zdCBoaSA9IG5vcm0od2luZG93WzFdKTtcbiAgY29uc3Qgc3BhbiA9IGhpID4gbG8gPyBoaSAtIGxvIDogMTtcbiAgY29uc3QgbGFzdCA9IGx1dC5sZW5ndGggLSAxO1xuICBjb25zdCBzYXQgPSBzdXBwb3J0U2NhbGUgPiAwID8gc3VwcG9ydFNjYWxlIDogMTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBtZWFuLmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgZCA9IHN1cHBvcnRbaV07XG4gICAgaWYgKGQgPD0gMCkgY29udGludWU7IC8vIHVubWVhc3VyZWQg4oCUIGxlYXZlIGl0IHRyYW5zcGFyZW50XG4gICAgY29uc3QgdCA9IE1hdGgubWluKDEsIE1hdGgubWF4KDAsIChub3JtKG1lYW5baV0pIC0gbG8pIC8gc3BhbikpO1xuICAgIGNvbnN0IGMgPSBsdXRbTWF0aC5yb3VuZCh0ICogbGFzdCldID8/IGx1dFtsYXN0XTtcbiAgICBjb25zdCBhID0gTWF0aC5taW4oMSwgZCAvIHNhdCkgKiBvcGFjaXR5O1xuICAgIGNvbnN0IG8gPSBpICogNDtcbiAgICAvLyBQcmVtdWx0aXBsaWVkLCBtYXRjaGluZyB0aGUgY2FudmFzIGFscGhhIG1vZGUgdGhlIHJlbmRlcmVyIGNvbmZpZ3VyZXMuXG4gICAgcmdiYVtvXSA9IE1hdGgucm91bmQoY1swXSAqIGEpO1xuICAgIHJnYmFbbyArIDFdID0gTWF0aC5yb3VuZChjWzFdICogYSk7XG4gICAgcmdiYVtvICsgMl0gPSBNYXRoLnJvdW5kKGNbMl0gKiBhKTtcbiAgICByZ2JhW28gKyAzXSA9IE1hdGgucm91bmQoYSAqIDI1NSk7XG4gIH1cbiAgcmV0dXJuIHJnYmE7XG59XG5cbi8qKlxuICogVGhlIHNhbWUgZXN0aW1hdG9yIGluIDNEOiBhIGdlbmUncyBleHByZXNzaW9uIG92ZXIgYSBzZXJpYWxseSBzZWN0aW9uZWRcbiAqIHNwZWNpbWVuLCBvbiBhIHtAbGluayBEZW5zaXR5R3JpZH0gbGF0dGljZS5cbiAqXG4gKiBUd28gbW9kZXMsIGFuZCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZW0gaXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhXG4gKiBtZWFzdXJlbWVudCBhbmQgYW4gZXN0aW1hdGU6XG4gKlxuICogIC0gKipzaGVldHMqKiAoYGludGVycG9sYXRlYCB1bnNldCkgYmx1cnMgaW4tcGxhbmUgb25seSwgc28gZXhhY3RseSB0aGUgeiBwbGFuZXNcbiAqICAgIHRoYXQgaG9sZCBjZWxscyBhcmUgZmlsbGVkIGFuZCB0aGUgZ2FwcyBiZXR3ZWVuIHNlY3Rpb25zIHN0YXkgZW1wdHkuIFRoaXMgaXNcbiAqICAgIHRoZSBtZWFzdXJlZCBzZWN0aW9ucywgc3RhY2tlZCDigJQgdGhlIDJEIGdlbmUgbWFwIG9mIGVhY2ggc2xpZGUsIGF0IGl0cyBvd24gei5cbiAqICAtICoqdm9sdW1lKiogKGBpbnRlcnBvbGF0ZWAgc2V0KSBhbHNvIHNtb290aHMgYWxvbmcgeiwgc28gdGhlIHBsYW5lcyBiZXR3ZWVuXG4gKiAgICB0aGUgc2VjdGlvbnMgY2FycnkgYW4gaW50ZXJwb2xhdGVkIHZhbHVlLiBTbW9vdGhpbmcgdGhlIG51bWVyYXRvciBhbmQgdGhlXG4gKiAgICBkZW5vbWluYXRvciB0b2dldGhlciBpcyB3aGF0IG1ha2VzIHRoYXQgaG9uZXN0OiBpdCBpcyBOYWRhcmF5YS1XYXRzb24sIHNvIGFuXG4gKiAgICB1bmltYWdlZCBwbGFuZSBpbmhlcml0cyBpdHMgbmVpZ2hib3VycycgTUVBTiByYXRoZXIgdGhhbiB0aGVpciBzdW0gdGhpbm5lZFxuICogICAgb3ZlciBhIGdhcCBub2JvZHkgc2FtcGxlZC4gT3V0c2lkZSB0aGUgc2FtcGxlZCByYW5nZSB0aGUgZmllbGQgaXMgemVyb2VkIOKAlFxuICogICAgXCJzbW9vdGhlZCBiZXR3ZWVuIHRoZSBpbWFnZWQgc2VjdGlvbnNcIiBtdXN0IG5vdCBwdXQgZXhwcmVzc2lvbiBpbiBmcm9udCBvZlxuICogICAgdGhlIGZpcnN0IHNlY3Rpb24gb3IgYmVoaW5kIHRoZSBsYXN0LlxuICpcbiAqIEluZGl2aWR1YWwgY2VsbHMgYXJlIHN0aWxsIG5ldmVyIGludGVycG9sYXRlZDogY29uc2VjdXRpdmUgc2VjdGlvbnMgc2FtcGxlXG4gKiBkaWZmZXJlbnQgY2VsbHMsIHNvIHRoZXJlIGlzIG5vdGhpbmcgdG8gaW50ZXJwb2xhdGUgYWxvbmcuIFdoYXQgaXMgaW50ZXJwb2xhdGVkXG4gKiBpcyBhIGZpZWxkLCB3aGljaCBpcyBsZWdpdGltYXRlbHkgZGVmaW5lZCBiZXR3ZWVuIHRoZSBwbGFuZXMuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRXhwcmVzc2lvblZvbHVtZU9wdGlvbnMge1xuICAvKiogS2VybmVsIM+DIHBlciBheGlzLCBpbiB0aGUgb2JzZXJ2YXRpb25zJyBvd24gdW5pdHMuIFRoZSB6IHRlcm0gaXMgdXNlZCBvbmx5XG4gICAqICB3aGVuIHtAbGluayBpbnRlcnBvbGF0ZX0gaXMgc2V0LiAqL1xuICBzaWdtYTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdO1xuICAvKiogT25lIGV4cHJlc3Npb24gdmFsdWUgcGVyIG9ic2VydmF0aW9uLiAqL1xuICB2YWx1ZXM6IEZsb2F0MzJBcnJheTtcbiAgLyoqIE9ic2VydmF0aW9ucyB0byBpbmNsdWRlOyBldmVyeSBvbmUgd2hlbiBhYnNlbnQg4oCUIG9uZSBzZWN0aW9uLCBvciBhIHNlbGVjdGlvbi4gKi9cbiAgaW5kaWNlcz86IFVpbnQzMkFycmF5O1xuICAvKiogU21vb3RoIGFsb25nIHogaW50byBhIGNvbnRpbnVvdXMgZXN0aW1hdGUgcmF0aGVyIHRoYW4gbGVhdmluZyB0aGUgbWVhc3VyZWRcbiAgICogIHNoZWV0cyBpc29sYXRlZCBhdCB0aGVpciBvd24gcGxhbmVzLiAqL1xuICBpbnRlcnBvbGF0ZT86IGJvb2xlYW47XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRXhwcmVzc2lvblZvbHVtZUZpZWxkIHtcbiAgd2lkdGg6IG51bWJlcjtcbiAgaGVpZ2h0OiBudW1iZXI7XG4gIGRlcHRoOiBudW1iZXI7XG4gIC8qKiBLZXJuZWwtd2VpZ2h0ZWQgbWVhbiBleHByZXNzaW9uLCB2YWxpZCBvbmx5IHdoZXJlIGBzdXBwb3J0ID4gMGAuICovXG4gIG1lYW46IEZsb2F0MzJBcnJheTtcbiAgLyoqIEtlcm5lbC13ZWlnaHRlZCBjZWxsIGNvdW50IHBlciB2b3hlbCDigJQgMCB3aGVyZSBub3RoaW5nIHdhcyBtZWFzdXJlZC4gKi9cbiAgc3VwcG9ydDogRmxvYXQzMkFycmF5O1xuICAvKiogRmluaXRlIHJhbmdlIG9mIGBtZWFuYCBvdmVyIHRoZSBzdXBwb3J0ZWQgdm94ZWxzLCBmb3IgYSBjb250cmFzdCB3aW5kb3cuICovXG4gIHJhbmdlOiBbbnVtYmVyLCBudW1iZXJdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZXhwcmVzc2lvblZvbHVtZShcbiAgb2JzOiBTcGF0aWFsT2JzZXJ2YXRpb25zLFxuICBncmlkOiBEZW5zaXR5R3JpZCxcbiAgb3B0czogRXhwcmVzc2lvblZvbHVtZU9wdGlvbnMsXG4pOiBFeHByZXNzaW9uVm9sdW1lRmllbGQgfCBudWxsIHtcbiAgY29uc3QgeyB3aWR0aDogdywgaGVpZ2h0OiBoLCBkZXB0aDogZCwgdm94ZWxTaXplIH0gPSBncmlkO1xuICBpZiAodyA8PSAwIHx8IGggPD0gMCB8fCBkIDw9IDApIHJldHVybiBudWxsO1xuICBjb25zdCBudW0gPSBuZXcgRmxvYXQzMkFycmF5KHcgKiBoICogZCk7XG4gIGNvbnN0IGRlbiA9IG5ldyBGbG9hdDMyQXJyYXkodyAqIGggKiBkKTtcbiAgY29uc3QgaWR4ID0gb3B0cy5pbmRpY2VzO1xuICBjb25zdCBuID0gaWR4ID8gaWR4Lmxlbmd0aCA6IG9icy5jb3VudDtcbiAgY29uc3QgeiA9IG9icy56O1xuICBsZXQgcGxhY2VkID0gMDtcbiAgLy8gVGhlIGltYWdlZCByYW5nZSwgdGFrZW4gZnJvbSB0aGUgb2JzZXJ2YXRpb25zIHRoYXQgd2VyZSBhY3R1YWxseSBpbmNsdWRlZDpcbiAgLy8gdGhlIGVzdGltYXRlIG1heSBub3QgcmVhY2ggcGFzdCB0aGUgb3V0ZXJtb3N0IHNlY3Rpb24gaXQgd2FzIGJ1aWx0IGZyb20uXG4gIGxldCBmaXJzdCA9IGQ7XG4gIGxldCBsYXN0ID0gLTE7XG5cbiAgZm9yIChsZXQgayA9IDA7IGsgPCBuOyBrKyspIHtcbiAgICBjb25zdCBpID0gaWR4ID8gaWR4W2tdIDogaztcbiAgICBjb25zdCBlID0gb3B0cy52YWx1ZXNbaV07XG4gICAgLy8gTm90IG1lYXN1cmVkIGZvciB0aGlzIGNlbGwg4oCUIG11c3Qgbm90IGJlIHJlYWQgYXMgXCJ0aGUgZ2VuZSBpcyBhYnNlbnQgaGVyZVwiLlxuICAgIGlmICghTnVtYmVyLmlzRmluaXRlKGUpKSBjb250aW51ZTtcbiAgICBjb25zdCB2eCA9IE1hdGguZmxvb3Iob2JzLnhbaV0gLyB2b3hlbFNpemVbMF0pO1xuICAgIGNvbnN0IHZ5ID0gTWF0aC5mbG9vcihvYnMueVtpXSAvIHZveGVsU2l6ZVsxXSk7XG4gICAgY29uc3QgdnogPSB6ID8gTWF0aC5mbG9vcih6W2ldIC8gdm94ZWxTaXplWzJdKSA6IDA7XG4gICAgaWYgKHZ4IDwgMCB8fCB2eCA+PSB3IHx8IHZ5IDwgMCB8fCB2eSA+PSBoIHx8IHZ6IDwgMCB8fCB2eiA+PSBkKSBjb250aW51ZTtcbiAgICBjb25zdCBhdCA9ICh2eiAqIGggKyB2eSkgKiB3ICsgdng7XG4gICAgbnVtW2F0XSArPSBlO1xuICAgIGRlblthdF0gKz0gMTtcbiAgICBwbGFjZWQrKztcbiAgICBpZiAodnogPCBmaXJzdCkgZmlyc3QgPSB2ejtcbiAgICBpZiAodnogPiBsYXN0KSBsYXN0ID0gdno7XG4gIH1cbiAgaWYgKCFwbGFjZWQpIHJldHVybiBudWxsO1xuXG4gIGNvbnN0IHNpZ21hVm94OiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0gPSBbXG4gICAgb3B0cy5zaWdtYVswXSAvIHZveGVsU2l6ZVswXSwgb3B0cy5zaWdtYVsxXSAvIHZveGVsU2l6ZVsxXSwgb3B0cy5zaWdtYVsyXSAvIHZveGVsU2l6ZVsyXSxcbiAgXTtcbiAgLy8gSW4tcGxhbmUgYWx3YXlzOiBhIHNlY3Rpb24ncyBvd24gZ2VuZSBtYXAgaXMgYSAyRCBmaWVsZCwgd2hpY2hldmVyIG1vZGUgdGhpcyBpcy5cbiAgZm9yIChjb25zdCBheGlzIG9mIFswLCAxXSBhcyBjb25zdCkge1xuICAgIGJsdXJWb2x1bWVBeGlzKG51bSwgdywgaCwgZCwgYXhpcywgc2lnbWFWb3hbYXhpc10pO1xuICAgIGJsdXJWb2x1bWVBeGlzKGRlbiwgdywgaCwgZCwgYXhpcywgc2lnbWFWb3hbYXhpc10pO1xuICB9XG4gIGlmIChvcHRzLmludGVycG9sYXRlKSB7XG4gICAgYmx1clZvbHVtZUF4aXMobnVtLCB3LCBoLCBkLCAyLCBzaWdtYVZveFsyXSk7XG4gICAgYmx1clZvbHVtZUF4aXMoZGVuLCB3LCBoLCBkLCAyLCBzaWdtYVZveFsyXSk7XG4gICAgY29uc3QgcGxhbmUgPSB3ICogaDtcbiAgICBmb3IgKGxldCBrID0gMDsgayA8IGQ7IGsrKykge1xuICAgICAgaWYgKGsgPj0gZmlyc3QgJiYgayA8PSBsYXN0KSBjb250aW51ZTtcbiAgICAgIC8vIFRoZSBibHVyIGxlYXZlcyBhIHRhaWwgcGFzdCB0aGUgb3V0ZXJtb3N0IHNlY3Rpb25zOyBrZWVwaW5nIGl0IHdvdWxkIGRyYXdcbiAgICAgIC8vIGV4cHJlc3Npb24gd2hlcmUgdGhlIHNwZWNpbWVuIHdhcyBuZXZlciBzZWN0aW9uZWQuXG4gICAgICBudW0uZmlsbCgwLCBrICogcGxhbmUsIChrICsgMSkgKiBwbGFuZSk7XG4gICAgICBkZW4uZmlsbCgwLCBrICogcGxhbmUsIChrICsgMSkgKiBwbGFuZSk7XG4gICAgfVxuICB9XG5cbiAgY29uc3QgbWVhbiA9IG5ldyBGbG9hdDMyQXJyYXkobnVtLmxlbmd0aCk7XG4gIGxldCBsbyA9IEluZmluaXR5O1xuICBsZXQgaGkgPSAtSW5maW5pdHk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbWVhbi5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IHN1cHBvcnQgPSBkZW5baV07XG4gICAgaWYgKHN1cHBvcnQgPD0gMCkgY29udGludWU7XG4gICAgY29uc3QgbSA9IG51bVtpXSAvIHN1cHBvcnQ7XG4gICAgbWVhbltpXSA9IG07XG4gICAgaWYgKG0gPCBsbykgbG8gPSBtO1xuICAgIGlmIChtID4gaGkpIGhpID0gbTtcbiAgfVxuICBpZiAoIU51bWJlci5pc0Zpbml0ZShsbykpIHJldHVybiBudWxsO1xuXG4gIHJldHVybiB7XG4gICAgd2lkdGg6IHcsXG4gICAgaGVpZ2h0OiBoLFxuICAgIGRlcHRoOiBkLFxuICAgIG1lYW4sXG4gICAgc3VwcG9ydDogZGVuLFxuICAgIHJhbmdlOiBoaSA+IGxvID8gW2xvLCBoaV0gOiBbbG8sIGxvICsgMV0sXG4gIH07XG59XG5cbi8qKlxuICogRW5jb2RlIGEgZmllbGQgaW50byB0aGUgdWludDggc2NhbGFyIGEgYFZvbHVtZUxheWVyYCByZW5kZXJzLCB3aW5kb3dlZCB0aGUgc2FtZVxuICogd2F5IHRoZSAyRCBtYXAgYW5kIHRoZSBtYXJrZXJzIGFyZSBzbyB0aGUgdGhyZWUgY2Fubm90IGRpc2FncmVlLlxuICpcbiAqIGBmbG9vcmAgbGlmdHMgZXZlcnkgTUVBU1VSRUQgdm94ZWwgdG8gYXQgbGVhc3QgdGhpcyBmcmFjdGlvbiBvZiBmdWxsIHNjYWxlLCBzb1xuICogdGhhdCBcInRoZSBnZW5lIGlzIG5vdCBoZXJlXCIgY2FuIGJlIHRvbGQgZnJvbSBcIm5vYm9keSBzZWN0aW9uZWQgaGVyZVwiIOKAlCB0aGVcbiAqIGRpc3RpbmN0aW9uIHRoZSAyRCBtYXAga2VlcHMgd2l0aCBpdHMgc2VwYXJhdGUgc3VwcG9ydCBjaGFubmVsLCB3aGljaCBhIHNjYWxhclxuICogdm9sdW1lIGNhbm5vdCBleHByZXNzIGJlY2F1c2UgaXRzIHJheW1hcmNoIHRha2VzIGFscGhhIGZyb20gdGhlIHNhbWUgbnVtYmVyIGl0XG4gKiB0YWtlcyBjb2xvdXIgZnJvbS5cbiAqXG4gKiBJdCBkZWZhdWx0cyB0byBPRkYsIGJlY2F1c2UgaW4gYSB2b2x1bWUgdGhhdCB0cmFkZSBkb2VzIG5vdCBwYXkuIEEgcmF5IGNyb3NzZXNcbiAqIHRlbnMgb2YgbWVhc3VyZWQgcGxhbmVzIGFuZCB0aGUgRFZSIGNvbXBvc2l0ZXMgZXZlcnkgb25lIG9mIHRoZW0sIHNvIGEgZmxvb3JcbiAqIGxhcmdlIGVub3VnaCB0byBzZWUgb24gYSBzaW5nbGUgcGxhbmUgc2F0dXJhdGVzIHRvIGFuIG9wYXF1ZSBzaGVsbCBvdmVyIHRoZVxuICogd2hvbGUgc3BlY2ltZW4gYW5kIGJ1cmllcyB0aGUgc2lnbmFsIGl0IHdhcyBtZWFudCB0byBjb250ZXh0dWFsaXNlLCB3aGlsZSBvbmVcbiAqIHNtYWxsIGVub3VnaCB0byBzdGF5IGZhaW50IGlzIGFscmVhZHkgaW52aXNpYmxlIHBlciBwbGFuZS4gVGhlIG1lYXN1cmVkIGVudmVsb3BlXG4gKiBpcyBiZXR0ZXIgcmVhZCBmcm9tIHRoZSBhbmF0b215IGxheWVyIGFuZCB0aGUgb2JzZXJ2YXRpb25zLCB3aGljaCBhcmUgZHJhd24gaW5cbiAqIHRoZSBzYW1lIHNjZW5lIOKAlCBhbmQgdGhlIHN1cnJvdW5kaW5nIGRlbnNpdHkgdm9sdW1lcyB0YWtlIHRoZSBzYW1lIG5vLWZsb29yXG4gKiBhcHByb2FjaCwgc28gdGhlIHNjZW5lIHN0YXlzIGludGVybmFsbHkgY29uc2lzdGVudC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGVuY29kZUV4cHJlc3Npb25Wb2x1bWUoXG4gIGZpZWxkOiBFeHByZXNzaW9uVm9sdW1lRmllbGQsXG4gIHdpbmRvdzogW251bWJlciwgbnVtYmVyXSxcbiAgb3B0czogeyBsb2c/OiBib29sZWFuOyBmbG9vcj86IG51bWJlciB9ID0ge30sXG4pOiBVaW50OEFycmF5IHtcbiAgY29uc3QgeyBtZWFuLCBzdXBwb3J0IH0gPSBmaWVsZDtcbiAgY29uc3Qgb3V0ID0gbmV3IFVpbnQ4QXJyYXkobWVhbi5sZW5ndGgpO1xuICBjb25zdCBsb2cgPSAhIW9wdHMubG9nO1xuICBjb25zdCBmbG9vciA9IE1hdGgubWluKDEsIE1hdGgubWF4KDAsIG9wdHMuZmxvb3IgPz8gMCkpO1xuICBjb25zdCBub3JtID0gKHY6IG51bWJlcik6IG51bWJlciA9PiAobG9nID8gTWF0aC5sb2cxcChNYXRoLm1heCgwLCB2KSkgOiB2KTtcbiAgY29uc3QgbG8gPSBub3JtKHdpbmRvd1swXSk7XG4gIGNvbnN0IGhpID0gbm9ybSh3aW5kb3dbMV0pO1xuICBjb25zdCBzcGFuID0gaGkgPiBsbyA/IGhpIC0gbG8gOiAxO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IG1lYW4ubGVuZ3RoOyBpKyspIHtcbiAgICBpZiAoc3VwcG9ydFtpXSA8PSAwKSBjb250aW51ZTsgLy8gdW5tZWFzdXJlZCDigJQgemVybywgc28gdGhlIHJheW1hcmNoIHNraXBzIGl0XG4gICAgY29uc3QgdCA9IE1hdGgubWluKDEsIE1hdGgubWF4KDAsIChub3JtKG1lYW5baV0pIC0gbG8pIC8gc3BhbikpO1xuICAgIG91dFtpXSA9IE1hdGgucm91bmQoKGZsb29yICsgKDEgLSBmbG9vcikgKiB0KSAqIDI1NSk7XG4gIH1cbiAgcmV0dXJuIG91dDtcbn1cbiJdfQ==
|