gx
chenyc
2025-06-12 7b72ac13a83764a662159d4a49b7fffb90476ecb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
/**
 * @license
 * Copyright 2017 Google LLC. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =============================================================================
 */
import { add } from './engine';
import * as tf from './index';
import { ALL_ENVS, describeWithFlags } from './jasmine_util';
import { zerosLike } from './ops/ops';
import { backpropagateGradients, getFilteredNodesXToY } from './tape';
import { expectArraysClose } from './test_util';
describeWithFlags('getFilteredNodesXToY', ALL_ENVS, () => {
    it('no paths from x to y', () => {
        const x = tf.scalar(1);
        const intermediate1 = tf.scalar(0);
        const intermediate2 = tf.scalar(0);
        const y = tf.scalar(2);
        const tape = [
            {
                id: 0,
                kernelName: 'node0',
                inputs: { x },
                outputs: [intermediate1],
                gradient: null
            },
            {
                id: 1,
                kernelName: 'node1',
                inputs: { intermediate2 },
                outputs: [y],
                gradient: null
            }
        ];
        const filteredTapeNodes = getFilteredNodesXToY(tape, [x], y);
        expect(filteredTapeNodes.length).toBe(0);
        expect(filteredTapeNodes).toEqual([]);
    });
    it('one operation x => y', () => {
        const x = tf.scalar(1);
        const y = tf.scalar(2);
        const tape = [
            { id: 0, kernelName: 'node0', inputs: { x }, outputs: [y], gradient: null }
        ];
        const filteredTapeNodes = getFilteredNodesXToY(tape, [x], y);
        expect(filteredTapeNodes.length).toBe(1);
        expect(filteredTapeNodes).toEqual(tape);
    });
    it('1 operation [x0, x1] => y, all input paths', () => {
        const x0 = tf.scalar(0);
        const x1 = tf.scalar(1);
        const y = tf.scalar(2);
        const tape = [{
                id: 0,
                kernelName: 'node0',
                inputs: { x0, x1 },
                outputs: [y],
                gradient: null
            }];
        const filteredTapeNodes = getFilteredNodesXToY(tape, [x0, x1], y);
        expect(filteredTapeNodes.length).toBe(1);
        expect(filteredTapeNodes).toEqual(tape);
    });
    it('one operation [x0, x1] => y, one input paths', () => {
        const x0 = tf.scalar(0);
        const x1 = tf.scalar(1);
        const y = tf.scalar(2);
        const tape = [{
                id: 0,
                kernelName: 'node0',
                inputs: { x0, x1 },
                outputs: [y],
                gradient: null
            }];
        const filteredTapeNodes = getFilteredNodesXToY(tape, [x0], y);
        expect(filteredTapeNodes.length).toBe(1);
        // x1 input should be pruned, we don't ask for the gradient of x1.
        expect(filteredTapeNodes[0]).toEqual({
            id: 0,
            kernelName: 'node0',
            inputs: { x0 },
            outputs: [y],
            gradient: null
        });
    });
    it('two operations x => intermediate => y', () => {
        const x = tf.scalar(1);
        const intermediate = tf.scalar(0);
        const y = tf.scalar(2);
        const tape = [
            {
                id: 0,
                kernelName: 'node0',
                inputs: { x },
                outputs: [intermediate],
                gradient: null
            },
            {
                id: 1,
                kernelName: 'node1',
                inputs: { intermediate },
                outputs: [y],
                gradient: null
            }
        ];
        const filteredTapeNodes = getFilteredNodesXToY(tape, [x], y);
        expect(filteredTapeNodes.length).toBe(2);
        expect(filteredTapeNodes).toEqual(tape);
    });
    it('two operations [x0, x1], [x2] => ' +
        'intermediate => y', () => {
        const x0 = tf.scalar(1);
        const x1 = tf.scalar(2);
        const x2 = tf.scalar(3);
        const intermediate = tf.scalar(4);
        const y = tf.scalar(2);
        const tape = [
            {
                id: 0,
                kernelName: 'node0',
                inputs: { x0, x1 },
                outputs: [intermediate],
                gradient: null
            },
            {
                id: 1,
                kernelName: 'node1',
                inputs: { x2, intermediate },
                outputs: [y],
                gradient: null
            }
        ];
        const filteredTapeNodes = getFilteredNodesXToY(tape, [x0, x1, x2], y);
        expect(filteredTapeNodes.length).toBe(2);
        expect(filteredTapeNodes).toEqual(tape);
    });
    it('x => y and x => orphan', () => {
        const x = tf.scalar(1);
        const orphan = tf.scalar(0);
        const y = tf.scalar(2);
        const tape = [
            {
                id: 0,
                kernelName: 'node0',
                inputs: { x },
                outputs: [orphan],
                gradient: null
            },
            { id: 1, kernelName: 'node1', inputs: { x }, outputs: [y], gradient: null }
        ];
        const filteredTapeNodes = getFilteredNodesXToY(tape, [x], y);
        expect(filteredTapeNodes.length).toBe(1);
        // The orphan should be removed.
        expect(filteredTapeNodes[0]).toEqual(tape[1]);
    });
    it('x => y and orphan => y', () => {
        const x = tf.scalar(1);
        const orphan = tf.scalar(0);
        const y = tf.scalar(2);
        const tape = [{
                id: 0,
                kernelName: 'node0',
                inputs: { x, orphan },
                outputs: [y],
                gradient: null
            }];
        const filteredTapeNodes = getFilteredNodesXToY(tape, [x], y);
        expect(filteredTapeNodes.length).toBe(1);
        // The orphan should be pruned from the node's input.
        expect(filteredTapeNodes[0]).toEqual({
            id: 0,
            kernelName: 'node0',
            inputs: { x },
            outputs: [y],
            gradient: null
        });
    });
    it('1 op with 3 outputs x => y1, y2, y3', () => {
        const x = tf.scalar(1);
        const y1 = tf.scalar(2);
        const y2 = tf.scalar(2);
        const y3 = tf.scalar(2);
        const tape = [{
                id: 0,
                kernelName: 'node0',
                inputs: { x },
                outputs: [y1, y2, y3],
                gradient: null
            }];
        const filteredNodes1 = getFilteredNodesXToY(tape, [x], y1);
        expect(filteredNodes1.length).toBe(1);
        expect(filteredNodes1).toEqual(tape);
        const filteredNodes2 = getFilteredNodesXToY(tape, [x], y2);
        expect(filteredNodes2.length).toBe(1);
        expect(filteredNodes2).toEqual(tape);
        const filteredNodes3 = getFilteredNodesXToY(tape, [x], y3);
        expect(filteredNodes3.length).toBe(1);
        expect(filteredNodes3).toEqual(tape);
    });
});
describeWithFlags('backpropagateGradients', ALL_ENVS, () => {
    it('Throws if gradient is not defined', () => {
        const x = tf.scalar(0);
        const y = tf.scalar(1);
        const dy = tf.scalar(1);
        const accumulatedGradientsMap = {};
        accumulatedGradientsMap[y.id] = dy;
        const tape = [
            { id: 0, kernelName: 'node0', inputs: { x }, outputs: [y], gradient: null }
        ];
        expect(() => backpropagateGradients(accumulatedGradientsMap, tape, f => tf.tidy(f), add))
            .toThrowError();
    });
    it('basic backprop with 1 node', async () => {
        const x = tf.scalar(0);
        const y = tf.scalar(1);
        const dy = tf.scalar(1);
        const accumulatedGradientsMap = {};
        accumulatedGradientsMap[y.id] = dy;
        const tape = [{
                id: 0,
                kernelName: 'node0',
                inputs: { x },
                outputs: [y],
                gradient: (dys) => {
                    return { x: () => dys[0].add(tf.scalar(1)) };
                }
            }];
        backpropagateGradients(accumulatedGradientsMap, tape, f => tf.tidy(f), add);
        expectArraysClose(await accumulatedGradientsMap[x.id].data(), [2]);
    });
    it('basic backprop with 2 nodes', async () => {
        const x = tf.scalar(0);
        const intermediate = tf.scalar(1);
        const y = tf.scalar(2);
        const dy = tf.scalar(1);
        const accumulatedGradientsMap = {};
        accumulatedGradientsMap[y.id] = dy;
        const tape = [
            {
                id: 0,
                kernelName: 'node0',
                inputs: { x },
                outputs: [intermediate],
                gradient: (dys) => {
                    return { x: () => dys[0].add(tf.scalar(1)) };
                }
            },
            {
                id: 1,
                kernelName: 'node1',
                inputs: { intermediate },
                outputs: [y],
                gradient: (dys) => {
                    return { intermediate: () => dys[0].add(tf.scalar(1)) };
                }
            }
        ];
        backpropagateGradients(accumulatedGradientsMap, tape, f => tf.tidy(f), add);
        // dx = dy + 1 + 1
        expectArraysClose(await accumulatedGradientsMap[x.id].data(), [3]);
    });
    it('basic backprop with a split node accumulates gradients', async () => {
        const x = tf.scalar(0);
        const intermediate1 = tf.scalar(1);
        const intermediate2 = tf.scalar(2);
        const y = tf.scalar(3);
        const dy = tf.scalar(1);
        const accumulatedGradientsMap = {};
        accumulatedGradientsMap[y.id] = dy;
        const tape = [
            {
                id: 0,
                kernelName: 'node0',
                inputs: { x },
                outputs: [intermediate1],
                gradient: (dys) => {
                    return { x: () => dys[0].add(tf.scalar(1)) };
                }
            },
            {
                id: 1,
                kernelName: 'node1',
                inputs: { x },
                outputs: [intermediate2],
                gradient: (dys) => {
                    return { x: () => dys[0].add(tf.scalar(1)) };
                }
            },
            {
                id: 2,
                kernelName: 'node2',
                inputs: { intermediate1, intermediate2 },
                outputs: [y],
                gradient: (dys) => {
                    return {
                        intermediate1: () => dys[0].add(tf.scalar(1)),
                        intermediate2: () => dys[0].add(tf.scalar(1))
                    };
                }
            }
        ];
        backpropagateGradients(accumulatedGradientsMap, tape, f => tf.tidy(f), add);
        // dx = dy + 1 + 1 + 1 + 1 + 1
        expectArraysClose(await accumulatedGradientsMap[x.id].data(), [(await dy.data())[0] + 5]);
    });
    it('backprop over 1 node with 3 outputs, w.r.t to the 2nd output', async () => {
        const x = tf.tensor1d([1, 1, 1]);
        const y1 = tf.scalar(1);
        const y2 = tf.scalar(1);
        const y3 = tf.scalar(1);
        const accumulatedGradientsMap = {};
        // Backproping through the 2nd output.
        const dy2 = tf.scalar(5);
        accumulatedGradientsMap[y2.id] = dy2;
        let dys;
        const tape = [{
                id: 0,
                kernelName: 'node0',
                inputs: { x },
                outputs: [y1, y2, y3],
                gradient: (dys_) => {
                    dys = dys_.map(dy => dy || zerosLike(y1));
                    return { x: () => tf.stack(dys) };
                }
            }];
        backpropagateGradients(accumulatedGradientsMap, tape, f => tf.tidy(f), add);
        expectArraysClose(await accumulatedGradientsMap[x.id].data(), [0, 5, 0]);
        expectArraysClose(await dys[0].data(), [0]);
        expectArraysClose(await dys[1].data(), [5]);
        expectArraysClose(await dys[2].data(), [0]);
    });
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFwZV90ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vdGZqcy1jb3JlL3NyYy90YXBlX3Rlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBRUgsT0FBTyxFQUFDLEdBQUcsRUFBVSxNQUFNLFVBQVUsQ0FBQztBQUN0QyxPQUFPLEtBQUssRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUM5QixPQUFPLEVBQUMsUUFBUSxFQUFFLGlCQUFpQixFQUFDLE1BQU0sZ0JBQWdCLENBQUM7QUFDM0QsT0FBTyxFQUFDLFNBQVMsRUFBQyxNQUFNLFdBQVcsQ0FBQztBQUNwQyxPQUFPLEVBQUMsc0JBQXNCLEVBQUUsb0JBQW9CLEVBQVcsTUFBTSxRQUFRLENBQUM7QUFDOUUsT0FBTyxFQUFDLGlCQUFpQixFQUFDLE1BQU0sYUFBYSxDQUFDO0FBRTlDLGlCQUFpQixDQUFDLHNCQUFzQixFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUU7SUFDdkQsRUFBRSxDQUFDLHNCQUFzQixFQUFFLEdBQUcsRUFBRTtRQUM5QixNQUFNLENBQUMsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3ZCLE1BQU0sYUFBYSxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFbkMsTUFBTSxhQUFhLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNuQyxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBRXZCLE1BQU0sSUFBSSxHQUFlO1lBQ3ZCO2dCQUNFLEVBQUUsRUFBRSxDQUFDO2dCQUNMLFVBQVUsRUFBRSxPQUFPO2dCQUNuQixNQUFNLEVBQUUsRUFBQyxDQUFDLEVBQUM7Z0JBQ1gsT0FBTyxFQUFFLENBQUMsYUFBYSxDQUFDO2dCQUN4QixRQUFRLEVBQUUsSUFBSTthQUNmO1lBQ0Q7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RUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLElBQUksU0FBUyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7b0JBQzFDLE9BQU8sRUFBQyxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsRUFBQyxDQUFDO2dCQUNsQyxDQUFDO2FBQ0YsQ0FBQyxDQUFDO1FBRUgsc0JBQXNCLENBQ2xCLHVCQUF1QixFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBdUIsQ0FBQyxFQUNwRSxHQUFHLENBQUMsQ0FBQztRQUNULGlCQUFpQixDQUFDLE1BQU0sdUJBQXVCLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3pFLGlCQUFpQixDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM1QyxpQkFBaUIsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDNUMsaUJBQWlCLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzlDLENBQUMsQ0FBQyxDQUFDO0FBQ1IsQ0FBQyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgTExDLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICovXG5cbmltcG9ydCB7YWRkLCBTY29wZUZufSBmcm9tICcuL2VuZ2luZSc7XG5pbXBvcnQgKiBhcyB0ZiBmcm9tICcuL2luZGV4JztcbmltcG9ydCB7QUxMX0VOVlMsIGRlc2NyaWJlV2l0aEZsYWdzfSBmcm9tICcuL2phc21pbmVfdXRpbCc7XG5pbXBvcnQge3plcm9zTGlrZX0gZnJvbSAnLi9vcHMvb3BzJztcbmltcG9ydCB7YmFja3Byb3BhZ2F0ZUdyYWRpZW50cywgZ2V0RmlsdGVyZWROb2Rlc1hUb1ksIFRhcGVOb2RlfSBmcm9tICcuL3RhcGUnO1xuaW1wb3J0IHtleHBlY3RBcnJheXNDbG9zZX0gZnJvbSAnLi90ZXN0X3V0aWwnO1xuXG5kZXNjcmliZVdpdGhGbGFncygnZ2V0RmlsdGVyZWROb2Rlc1hUb1knLCBBTExfRU5WUywgKCkgPT4ge1xuICBpdCgnbm8gcGF0aHMgZnJvbSB4IHRvIHknLCAoKSA9PiB7XG4gICAgY29uc3QgeCA9IHRmLnNjYWxhcigxKTtcbiAgICBjb25zdCBpbnRlcm1lZGlhdGUxID0gdGYuc2NhbGFyKDApO1xuXG4gICAgY29uc3QgaW50ZXJtZWRpYXRlMiA9IHRmLnNjYWxhcigwKTtcbiAgICBjb25zdCB5ID0gdGYuc2NhbGFyKDIpO1xuXG4gICAgY29uc3QgdGFwZTogVGFwZU5vZGVbXSA9IFtcbiAgICAgIHtcbiAgICAgICAgaWQ6IDAsXG4gICAgICAgIGtlcm5lbE5hbWU6ICdub2RlMCcsXG4gICAgICAgIGlucHV0czoge3h9LFxuICAgICAgICBvdXRwdXRzOiBbaW50ZXJtZWRpYXRlMV0sXG4gICAgICAgIGdyYWRpZW50OiBudWxsXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICBpZDogMSxcbiAgICAgICAga2VybmVsTmFtZTogJ25vZGUxJyxcbiAgICAgICAgaW5wdXRzOiB7aW50ZXJtZWRpYXRlMn0sXG4gICAgICAgIG91dHB1dHM6IFt5XSxcbiAgICAgICAgZ3JhZGllbnQ6IG51bGxcbiAgICAgIH1cbiAgICBdO1xuXG4gICAgY29uc3QgZmlsdGVyZWRUYXBlTm9kZXMgPSBnZXRGaWx0ZXJlZE5vZGVzWFRvWSh0YXBlLCBbeF0sIHkpO1xuXG4gICAgZXhwZWN0KGZpbHRlcmVkVGFwZU5vZGVzLmxlbmd0aCkudG9CZSgwKTtcbiAgICBleHBlY3QoZmlsdGVyZWRUYXBlTm9kZXMpLnRvRXF1YWwoW10pO1xuICB9KTtcblxuICBpdCgnb25lIG9wZXJhdGlvbiB4ID0+IHknLCAoKSA9PiB7XG4gICAgY29uc3QgeCA9IHRmLnNjYWxhcigxKTtcbiAgICBjb25zdCB5ID0gdGYuc2NhbGFyKDIpO1xuXG4gICAgY29uc3QgdGFwZTogVGFwZU5vZGVbXSA9IFtcbiAgICAgIHtpZDogMCwga2VybmVsTmFtZTogJ25vZGUwJywgaW5wdXRzOiB7eH0sIG91dHB1dHM6IFt5XSwgZ3JhZGllbnQ6IG51bGx9XG4gICAgXTtcblxuICAgIGNvbnN0IGZpbHRlcmVkVGFwZU5vZGVzID0gZ2V0RmlsdGVyZWROb2Rlc1hUb1kodGFwZSwgW3hdLCB5KTtcblxuICAgIGV4cGVjdChmaWx0ZXJlZFRhcGVOb2Rlcy5sZW5ndGgpLnRvQmUoMSk7XG4gICAgZXhwZWN0KGZpbHRlcmVkVGFwZU5vZGVzKS50b0VxdWFsKHRhcGUpO1xuICB9KTtcblxuICBpdCgnMSBvcGVyYXRpb24gW3gwLCB4MV0gPT4geSwgYWxsIGlucHV0IHBhdGhzJywgKCkgPT4ge1xuICAgIGNvbnN0IHgwID0gdGYuc2NhbGFyKDApO1xuICAgIGNvbnN0IHgxID0gdGYuc2NhbGFyKDEpO1xuICAgIGNvbnN0IHkgPSB0Zi5zY2FsYXIoMik7XG5cbiAgICBjb25zdCB0YXBlOiBUYXBlTm9kZVtdID0gW3tcbiAgICAgIGlkOiAwLFxuICAgICAga2VybmVsTmFtZTogJ25vZGUwJyxcbiAgICAgIGlucHV0czoge3gwLCB4MX0sXG4gICAgICBvdXRwdXRzOiBbeV0sXG4gICAgICBncmFkaWVudDogbnVsbFxuICAgIH1dO1xuXG4gICAgY29uc3QgZmlsdGVyZWRUYXBlTm9kZXMgPSBnZXRGaWx0ZXJlZE5vZGVzWFRvWSh0YXBlLCBbeDAsIHgxXSwgeSk7XG5cbiAgICBleHBlY3QoZmlsdGVyZWRUYXBlTm9kZXMubGVuZ3RoKS50b0JlKDEpO1xuICAgIGV4cGVjdChmaWx0ZXJlZFRhcGVOb2RlcykudG9FcXVhbCh0YXBlKTtcbiAgfSk7XG5cbiAgaXQoJ29uZSBvcGVyYXRpb24gW3gwLCB4MV0gPT4geSwgb25lIGlucHV0IHBhdGhzJywgKCkgPT4ge1xuICAgIGNvbnN0IHgwID0gdGYuc2NhbGFyKDApO1xuICAgIGNvbnN0IHgxID0gdGYuc2NhbGFyKDEpO1xuICAgIGNvbnN0IHkgPSB0Zi5zY2FsYXIoMik7XG5cbiAgICBjb25zdCB0YXBlOiBUYXBlTm9kZVtdID0gW3tcbiAgICAgIGlkOiAwLFxuICAgICAga2VybmVsTmFtZTogJ25vZGUwJyxcbiAgICAgIGlucHV0czoge3gwLCB4MX0sXG4gICAgICBvdXRwdXRzOiBbeV0sXG4gICAgICBncmFkaWVudDogbnVsbFxuICAgIH1dO1xuXG4gICAgY29uc3QgZmlsdGVyZWRUYXBlTm9kZXMgPSBnZXRGaWx0ZXJlZE5vZGVzWFRvWSh0YXBlLCBbeDBdLCB5KTtcblxuICAgIGV4cGVjdChmaWx0ZXJlZFRhcGVOb2Rlcy5sZW5ndGgpLnRvQmUoMSk7XG4gICAgLy8geDEgaW5wdXQgc2hvdWxkIGJlIHBydW5lZCwgd2UgZG9uJ3QgYXNrIGZvciB0aGUgZ3JhZGllbnQgb2YgeDEuXG4gICAgZXhwZWN0KGZpbHRlcmVkVGFwZU5vZGVzWzBdKS50b0VxdWFsKHtcbiAgICAgIGlkOiAwLFxuICAgICAga2VybmVsTmFtZTogJ25vZGUwJyxcbiAgICAgIGlucHV0czoge3gwfSxcbiAgICAgIG91dHB1dHM6IFt5XSxcbiAgICAgIGdyYWRpZW50OiBudWxsXG4gICAgfSk7XG4gIH0pO1xuXG4gIGl0KCd0d28gb3BlcmF0aW9ucyB4ID0+IGludGVybWVkaWF0ZSA9PiB5JywgKCkgPT4ge1xuICAgIGNvbnN0IHggPSB0Zi5zY2FsYXIoMSk7XG4gICAgY29uc3QgaW50ZXJtZWRpYXRlID0gdGYuc2NhbGFyKDApO1xuICAgIGNvbnN0IHkgPSB0Zi5zY2FsYXIoMik7XG5cbiAgICBjb25zdCB0YXBlOiBUYXBlTm9kZVtdID0gW1xuICAgICAge1xuICAgICAgICBpZDogMCxcbiAgICAgICAga2VybmVsTmFtZTogJ25vZGUwJyxcbiAgICAgICAgaW5wdXRzOiB7eH0sXG4gICAgICAgIG91dHB1dHM6IFtpbnRlcm1lZGlhdGVdLFxuICAgICAgICBncmFkaWVudDogbnVsbFxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgaWQ6IDEsXG4gICAgICAgIGtlcm5lbE5hbWU6ICdub2RlMScsXG4gICAgICAgIGlucHV0czoge2ludGVybWVkaWF0ZX0sXG4gICAgICAgIG91dHB1dHM6IFt5XSxcbiAgICAgICAgZ3JhZGllbnQ6IG51bGxcbiAgICAgIH1cbiAgICBdO1xuXG4gICAgY29uc3QgZmlsdGVyZWRUYXBlTm9kZXMgPSBnZXRGaWx0ZXJlZE5vZGVzWFRvWSh0YXBlLCBbeF0sIHkpO1xuXG4gICAgZXhwZWN0KGZpbHRlcmVkVGFwZU5vZGVzLmxlbmd0aCkudG9CZSgyKTtcbiAgICBleHBlY3QoZmlsdGVyZWRUYXBlTm9kZXMpLnRvRXF1YWwodGFwZSk7XG4gIH0pO1xuXG4gIGl0KCd0d28gb3BlcmF0aW9ucyBbeDAsIHgxXSwgW3gyXSA9PiAnICtcbiAgICAgICAgICdpbnRlcm1lZGlhdGUgPT4geScsXG4gICAgICgpID0+IHtcbiAgICAgICBjb25zdCB4MCA9IHRmLnNjYWxhcigxKTtcbiAgICAgICBjb25zdCB4MSA9IHRmLnNjYWxhcigyKTtcbiAgICAgICBjb25zdCB4MiA9IHRmLnNjYWxhcigzKTtcbiAgICAgICBjb25zdCBpbnRlcm1lZGlhdGUgPSB0Zi5zY2FsYXIoNCk7XG4gICAgICAgY29uc3QgeSA9IHRmLnNjYWxhcigyKTtcblxuICAgICAgIGNvbnN0IHRhcGU6IFRhcGVOb2RlW10gPSBbXG4gICAgICAgICB7XG4gICAgICAgICAgIGlkOiAwLFxuICAgICAgICAgICBrZXJuZWxOYW1lOiAnbm9kZTAnLFxuICAgICAgICAgICBpbnB1dHM6IHt4MCwgeDF9LFxuICAgICAgICAgICBvdXRwdXRzOiBbaW50ZXJtZWRpYXRlXSxcbiAgICAgICAgICAgZ3JhZGllbnQ6IG51bGxcbiAgICAgICAgIH0sXG4gICAgICAgICB7XG4gICAgICAgICAgIGlkOiAxLFxuICAgICAgICAgICBrZXJuZWxOYW1lOiAnbm9kZTEnLFxuICAgICAgICAgICBpbnB1dHM6IHt4MiwgaW50ZXJtZWRpYXRlfSxcbiAgICAgICAgICAgb3V0cHV0czogW3ldLFxuICAgICAgICAgICBncmFkaWVudDogbnVsbFxuICAgICAgICAgfVxuICAgICAgIF07XG5cbiAgICAgICBjb25zdCBmaWx0ZXJlZFRhcGVOb2RlcyA9IGdldEZpbHRlcmVkTm9kZXNYVG9ZKHRhcGUsIFt4MCwgeDEsIHgyXSwgeSk7XG5cbiAgICAgICBleHBlY3QoZmlsdGVyZWRUYXBlTm9kZXMubGVuZ3RoKS50b0JlKDIpO1xuICAgICAgIGV4cGVjdChmaWx0ZXJlZFRhcGVOb2RlcykudG9FcXVhbCh0YXBlKTtcbiAgICAgfSk7XG5cbiAgaXQoJ3ggPT4geSBhbmQgeCA9PiBvcnBoYW4nLCAoKSA9PiB7XG4gICAgY29uc3QgeCA9IHRmLnNjYWxhcigxKTtcbiAgICBjb25zdCBvcnBoYW4gPSB0Zi5zY2FsYXIoMCk7XG4gICAgY29uc3QgeSA9IHRmLnNjYWxhcigyKTtcblxuICAgIGNvbnN0IHRhcGU6IFRhcGVOb2RlW10gPSBbXG4gICAgICB7XG4gICAgICAgIGlkOiAwLFxuICAgICAgICBrZXJuZWxOYW1lOiAnbm9kZTAnLFxuICAgICAgICBpbnB1dHM6IHt4fSxcbiAgICAgICAgb3V0cHV0czogW29ycGhhbl0sXG4gICAgICAgIGdyYWRpZW50OiBudWxsXG4gICAgICB9LFxuICAgICAge2lkOiAxLCBrZXJuZWxOYW1lOiAnbm9kZTEnLCBpbnB1dHM6IHt4fSwgb3V0cHV0czogW3ldLCBncmFkaWVudDogbnVsbH1cbiAgICBdO1xuXG4gICAgY29uc3QgZmlsdGVyZWRUYXBlTm9kZXMgPSBnZXRGaWx0ZXJlZE5vZGVzWFRvWSh0YXBlLCBbeF0sIHkpO1xuXG4gICAgZXhwZWN0KGZpbHRlcmVkVGFwZU5vZGVzLmxlbmd0aCkudG9CZSgxKTtcbiAgICAvLyBUaGUgb3JwaGFuIHNob3VsZCBiZSByZW1vdmVkLlxuICAgIGV4cGVjdChmaWx0ZXJlZFRhcGVOb2Rlc1swXSkudG9FcXVhbCh0YXBlWzFdKTtcbiAgfSk7XG5cbiAgaXQoJ3ggPT4geSBhbmQgb3JwaGFuID0+IHknLCAoKSA9PiB7XG4gICAgY29uc3QgeCA9IHRmLnNjYWxhcigxKTtcbiAgICBjb25zdCBvcnBoYW4gPSB0Zi5zY2FsYXIoMCk7XG4gICAgY29uc3QgeSA9IHRmLnNjYWxhcigyKTtcblxuICAgIGNvbnN0IHRhcGU6IFRhcGVOb2RlW10gPSBbe1xuICAgICAgaWQ6IDAsXG4gICAgICBrZXJuZWxOYW1lOiAnbm9kZTAnLFxuICAgICAgaW5wdXRzOiB7eCwgb3JwaGFufSxcbiAgICAgIG91dHB1dHM6IFt5XSxcbiAgICAgIGdyYWRpZW50OiBudWxsXG4gICAgfV07XG5cbiAgICBjb25zdCBmaWx0ZXJlZFRhcGVOb2RlcyA9IGdldEZpbHRlcmVkTm9kZXNYVG9ZKHRhcGUsIFt4XSwgeSk7XG5cbiAgICBleHBlY3QoZmlsdGVyZWRUYXBlTm9kZXMubGVuZ3RoKS50b0JlKDEpO1xuICAgIC8vIFRoZSBvcnBoYW4gc2hvdWxkIGJlIHBydW5lZCBmcm9tIHRoZSBub2RlJ3MgaW5wdXQuXG4gICAgZXhwZWN0KGZpbHRlcmVkVGFwZU5vZGVzWzBdKS50b0VxdWFsKHtcbiAgICAgIGlkOiAwLFxuICAgICAga2VybmVsTmFtZTogJ25vZGUwJyxcbiAgICAgIGlucHV0czoge3h9LFxuICAgICAgb3V0cHV0czogW3ldLFxuICAgICAgZ3JhZGllbnQ6IG51bGxcbiAgICB9KTtcbiAgfSk7XG5cbiAgaXQoJzEgb3Agd2l0aCAzIG91dHB1dHMgeCA9PiB5MSwgeTIsIHkzJywgKCkgPT4ge1xuICAgIGNvbnN0IHggPSB0Zi5zY2FsYXIoMSk7XG4gICAgY29uc3QgeTEgPSB0Zi5zY2FsYXIoMik7XG4gICAgY29uc3QgeTIgPSB0Zi5zY2FsYXIoMik7XG4gICAgY29uc3QgeTMgPSB0Zi5zY2FsYXIoMik7XG5cbiAgICBjb25zdCB0YXBlOiBUYXBlTm9kZVtdID0gW3tcbiAgICAgIGlkOiAwLFxuICAgICAga2VybmVsTmFtZTogJ25vZGUwJyxcbiAgICAgIGlucHV0czoge3h9LFxuICAgICAgb3V0cHV0czogW3kxLCB5MiwgeTNdLFxuICAgICAgZ3JhZGllbnQ6IG51bGxcbiAgICB9XTtcblxuICAgIGNvbnN0IGZpbHRlcmVkTm9kZXMxID0gZ2V0RmlsdGVyZWROb2Rlc1hUb1kodGFwZSwgW3hdLCB5MSk7XG4gICAgZXhwZWN0KGZpbHRlcmVkTm9kZXMxLmxlbmd0aCkudG9CZSgxKTtcbiAgICBleHBlY3QoZmlsdGVyZWROb2RlczEpLnRvRXF1YWwodGFwZSk7XG5cbiAgICBjb25zdCBmaWx0ZXJlZE5vZGVzMiA9IGdldEZpbHRlcmVkTm9kZXNYVG9ZKHRhcGUsIFt4XSwgeTIpO1xuICAgIGV4cGVjdChmaWx0ZXJlZE5vZGVzMi5sZW5ndGgpLnRvQmUoMSk7XG4gICAgZXhwZWN0KGZpbHRlcmVkTm9kZXMyKS50b0VxdWFsKHRhcGUpO1xuXG4gICAgY29uc3QgZmlsdGVyZWROb2RlczMgPSBnZXRGaWx0ZXJlZE5vZGVzWFRvWSh0YXBlLCBbeF0sIHkzKTtcbiAgICBleHBlY3QoZmlsdGVyZWROb2RlczMubGVuZ3RoKS50b0JlKDEpO1xuICAgIGV4cGVjdChmaWx0ZXJlZE5vZGVzMykudG9FcXVhbCh0YXBlKTtcbiAgfSk7XG59KTtcblxuZGVzY3JpYmVXaXRoRmxhZ3MoJ2JhY2twcm9wYWdhdGVHcmFkaWVudHMnLCBBTExfRU5WUywgKCkgPT4ge1xuICBpdCgnVGhyb3dzIGlmIGdyYWRpZW50IGlzIG5vdCBkZWZpbmVkJywgKCkgPT4ge1xuICAgIGNvbnN0IHggPSB0Zi5zY2FsYXIoMCk7XG4gICAgY29uc3QgeSA9IHRmLnNjYWxhcigxKTtcblxuICAgIGNvbnN0IGR5ID0gdGYuc2NhbGFyKDEpO1xuXG4gICAgY29uc3QgYWNjdW11bGF0ZWRHcmFkaWVudHNNYXA6IHtbdGVuc29ySWQ6IG51bWJlcl06IHRmLlRlbnNvcn0gPSB7fTtcbiAgICBhY2N1bXVsYXRlZEdyYWRpZW50c01hcFt5LmlkXSA9IGR5O1xuXG4gICAgY29uc3QgdGFwZTogVGFwZU5vZGVbXSA9IFtcbiAgICAgIHtpZDogMCwga2VybmVsTmFtZTogJ25vZGUwJywgaW5wdXRzOiB7eH0sIG91dHB1dHM6IFt5XSwgZ3JhZGllbnQ6IG51bGx9XG4gICAgXTtcblxuICAgIGV4cGVjdChcbiAgICAgICAgKCkgPT4gYmFja3Byb3BhZ2F0ZUdyYWRpZW50cyhcbiAgICAgICAgICAgIGFjY3VtdWxhdGVkR3JhZGllbnRzTWFwLCB0YXBlLFxuICAgICAgICAgICAgZiA9PiB0Zi50aWR5KGYgYXMgU2NvcGVGbjx0Zi5UZW5zb3I+KSwgYWRkKSlcbiAgICAgICAgLnRvVGhyb3dFcnJvcigpO1xuICB9KTtcblxuICBpdCgnYmFzaWMgYmFja3Byb3Agd2l0aCAxIG5vZGUnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgeCA9IHRmLnNjYWxhcigwKTtcbiAgICBjb25zdCB5ID0gdGYuc2NhbGFyKDEpO1xuXG4gICAgY29uc3QgZHkgPSB0Zi5zY2FsYXIoMSk7XG5cbiAgICBjb25zdCBhY2N1bXVsYXRlZEdyYWRpZW50c01hcDoge1t0ZW5zb3JJZDogbnVtYmVyXTogdGYuVGVuc29yfSA9IHt9O1xuICAgIGFjY3VtdWxhdGVkR3JhZGllbnRzTWFwW3kuaWRdID0gZHk7XG5cbiAgICBjb25zdCB0YXBlOiBUYXBlTm9kZVtdID0gW3tcbiAgICAgIGlkOiAwLFxuICAgICAga2VybmVsTmFtZTogJ25vZGUwJyxcbiAgICAgIGlucHV0czoge3h9LFxuICAgICAgb3V0cHV0czogW3ldLFxuICAgICAgZ3JhZGllbnQ6IChkeXM6IHRmLlRlbnNvcltdKSA9PiB7XG4gICAgICAgIHJldHVybiB7eDogKCkgPT4gZHlzWzBdLmFkZCh0Zi5zY2FsYXIoMSkpfTtcbiAgICAgIH1cbiAgICB9XTtcblxuICAgIGJhY2twcm9wYWdhdGVHcmFkaWVudHMoXG4gICAgICAgIGFjY3VtdWxhdGVkR3JhZGllbnRzTWFwLCB0YXBlLCBmID0+IHRmLnRpZHkoZiBhcyBTY29wZUZuPHRmLlRlbnNvcj4pLFxuICAgICAgICBhZGQpO1xuXG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgYWNjdW11bGF0ZWRHcmFkaWVudHNNYXBbeC5pZF0uZGF0YSgpLCBbMl0pO1xuICB9KTtcblxuICBpdCgnYmFzaWMgYmFja3Byb3Agd2l0aCAyIG5vZGVzJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IHggPSB0Zi5zY2FsYXIoMCk7XG4gICAgY29uc3QgaW50ZXJtZWRpYXRlID0gdGYuc2NhbGFyKDEpO1xuICAgIGNvbnN0IHkgPSB0Zi5zY2FsYXIoMik7XG5cbiAgICBjb25zdCBkeSA9IHRmLnNjYWxhcigxKTtcblxuICAgIGNvbnN0IGFjY3VtdWxhdGVkR3JhZGllbnRzTWFwOiB7W3RlbnNvcklkOiBudW1iZXJdOiB0Zi5UZW5zb3J9ID0ge307XG4gICAgYWNjdW11bGF0ZWRHcmFkaWVudHNNYXBbeS5pZF0gPSBkeTtcblxuICAgIGNvbnN0IHRhcGU6IFRhcGVOb2RlW10gPSBbXG4gICAgICB7XG4gICAgICAgIGlkOiAwLFxuICAgICAgICBrZXJuZWxOYW1lOiAnbm9kZTAnLFxuICAgICAgICBpbnB1dHM6IHt4fSxcbiAgICAgICAgb3V0cHV0czogW2ludGVybWVkaWF0ZV0sXG4gICAgICAgIGdyYWRpZW50OiAoZHlzOiB0Zi5UZW5zb3JbXSkgPT4ge1xuICAgICAgICAgIHJldHVybiB7eDogKCkgPT4gZHlzWzBdLmFkZCh0Zi5zY2FsYXIoMSkpfTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgaWQ6IDEsXG4gICAgICAgIGtlcm5lbE5hbWU6ICdub2RlMScsXG4gICAgICAgIGlucHV0czoge2ludGVybWVkaWF0ZX0sXG4gICAgICAgIG91dHB1dHM6IFt5XSxcbiAgICAgICAgZ3JhZGllbnQ6IChkeXM6IHRmLlRlbnNvcltdKSA9PiB7XG4gICAgICAgICAgcmV0dXJuIHtpbnRlcm1lZGlhdGU6ICgpID0+IGR5c1swXS5hZGQodGYuc2NhbGFyKDEpKX07XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICBdO1xuXG4gICAgYmFja3Byb3BhZ2F0ZUdyYWRpZW50cyhcbiAgICAgICAgYWNjdW11bGF0ZWRHcmFkaWVudHNNYXAsIHRhcGUsIGYgPT4gdGYudGlkeShmIGFzIFNjb3BlRm48dGYuVGVuc29yPiksXG4gICAgICAgIGFkZCk7XG5cbiAgICAvLyBkeCA9IGR5ICsgMSArIDFcbiAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCBhY2N1bXVsYXRlZEdyYWRpZW50c01hcFt4LmlkXS5kYXRhKCksIFszXSk7XG4gIH0pO1xuXG4gIGl0KCdiYXNpYyBiYWNrcHJvcCB3aXRoIGEgc3BsaXQgbm9kZSBhY2N1bXVsYXRlcyBncmFkaWVudHMnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgeCA9IHRmLnNjYWxhcigwKTtcbiAgICBjb25zdCBpbnRlcm1lZGlhdGUxID0gdGYuc2NhbGFyKDEpO1xuICAgIGNvbnN0IGludGVybWVkaWF0ZTIgPSB0Zi5zY2FsYXIoMik7XG4gICAgY29uc3QgeSA9IHRmLnNjYWxhcigzKTtcblxuICAgIGNvbnN0IGR5ID0gdGYuc2NhbGFyKDEpO1xuXG4gICAgY29uc3QgYWNjdW11bGF0ZWRHcmFkaWVudHNNYXA6IHtbdGVuc29ySWQ6IG51bWJlcl06IHRmLlRlbnNvcn0gPSB7fTtcbiAgICBhY2N1bXVsYXRlZEdyYWRpZW50c01hcFt5LmlkXSA9IGR5O1xuXG4gICAgY29uc3QgdGFwZTogVGFwZU5vZGVbXSA9IFtcbiAgICAgIHtcbiAgICAgICAgaWQ6IDAsXG4gICAgICAgIGtlcm5lbE5hbWU6ICdub2RlMCcsXG4gICAgICAgIGlucHV0czoge3h9LFxuICAgICAgICBvdXRwdXRzOiBbaW50ZXJtZWRpYXRlMV0sXG4gICAgICAgIGdyYWRpZW50OiAoZHlzOiB0Zi5UZW5zb3JbXSkgPT4ge1xuICAgICAgICAgIHJldHVybiB7eDogKCkgPT4gZHlzWzBdLmFkZCh0Zi5zY2FsYXIoMSkpfTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgaWQ6IDEsXG4gICAgICAgIGtlcm5lbE5hbWU6ICdub2RlMScsXG4gICAgICAgIGlucHV0czoge3h9LFxuICAgICAgICBvdXRwdXRzOiBbaW50ZXJtZWRpYXRlMl0sXG4gICAgICAgIGdyYWRpZW50OiAoZHlzOiB0Zi5UZW5zb3JbXSkgPT4ge1xuICAgICAgICAgIHJldHVybiB7eDogKCkgPT4gZHlzWzBdLmFkZCh0Zi5zY2FsYXIoMSkpfTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgaWQ6IDIsXG4gICAgICAgIGtlcm5lbE5hbWU6ICdub2RlMicsXG4gICAgICAgIGlucHV0czoge2ludGVybWVkaWF0ZTEsIGludGVybWVkaWF0ZTJ9LFxuICAgICAgICBvdXRwdXRzOiBbeV0sXG4gICAgICAgIGdyYWRpZW50OiAoZHlzOiB0Zi5UZW5zb3JbXSkgPT4ge1xuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBpbnRlcm1lZGlhdGUxOiAoKSA9PiBkeXNbMF0uYWRkKHRmLnNjYWxhcigxKSksXG4gICAgICAgICAgICBpbnRlcm1lZGlhdGUyOiAoKSA9PiBkeXNbMF0uYWRkKHRmLnNjYWxhcigxKSlcbiAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICB9XG4gICAgXTtcblxuICAgIGJhY2twcm9wYWdhdGVHcmFkaWVudHMoXG4gICAgICAgIGFjY3VtdWxhdGVkR3JhZGllbnRzTWFwLCB0YXBlLCBmID0+IHRmLnRpZHkoZiBhcyBTY29wZUZuPHRmLlRlbnNvcj4pLFxuICAgICAgICBhZGQpO1xuXG4gICAgLy8gZHggPSBkeSArIDEgKyAxICsgMSArIDEgKyAxXG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoXG4gICAgICAgIGF3YWl0IGFjY3VtdWxhdGVkR3JhZGllbnRzTWFwW3guaWRdLmRhdGEoKSwgWyhhd2FpdCBkeS5kYXRhKCkpWzBdICsgNV0pO1xuICB9KTtcblxuICBpdCgnYmFja3Byb3Agb3ZlciAxIG5vZGUgd2l0aCAzIG91dHB1dHMsIHcuci50IHRvIHRoZSAybmQgb3V0cHV0JyxcbiAgICAgYXN5bmMgKCkgPT4ge1xuICAgICAgIGNvbnN0IHggPSB0Zi50ZW5zb3IxZChbMSwgMSwgMV0pO1xuICAgICAgIGNvbnN0IHkxID0gdGYuc2NhbGFyKDEpO1xuICAgICAgIGNvbnN0IHkyID0gdGYuc2NhbGFyKDEpO1xuICAgICAgIGNvbnN0IHkzID0gdGYuc2NhbGFyKDEpO1xuXG4gICAgICAgY29uc3QgYWNjdW11bGF0ZWRHcmFkaWVudHNNYXA6IHtbdGVuc29ySWQ6IG51bWJlcl06IHRmLlRlbnNvcn0gPSB7fTtcbiAgICAgICAvLyBCYWNrcHJvcGluZyB0aHJvdWdoIHRoZSAybmQgb3V0cHV0LlxuICAgICAgIGNvbnN0IGR5MiA9IHRmLnNjYWxhcig1KTtcbiAgICAgICBhY2N1bXVsYXRlZEdyYWRpZW50c01hcFt5Mi5pZF0gPSBkeTI7XG5cbiAgICAgICBsZXQgZHlzOiB0Zi5TY2FsYXJbXTtcbiAgICAgICBjb25zdCB0YXBlOiBUYXBlTm9kZVtdID0gW3tcbiAgICAgICAgIGlkOiAwLFxuICAgICAgICAga2VybmVsTmFtZTogJ25vZGUwJyxcbiAgICAgICAgIGlucHV0czoge3h9LFxuICAgICAgICAgb3V0cHV0czogW3kxLCB5MiwgeTNdLFxuICAgICAgICAgZ3JhZGllbnQ6IChkeXNfOiB0Zi5TY2FsYXJbXSkgPT4ge1xuICAgICAgICAgICBkeXMgPSBkeXNfLm1hcChkeSA9PiBkeSB8fCB6ZXJvc0xpa2UoeTEpKTtcbiAgICAgICAgICAgcmV0dXJuIHt4OiAoKSA9PiB0Zi5zdGFjayhkeXMpfTtcbiAgICAgICAgIH1cbiAgICAgICB9XTtcblxuICAgICAgIGJhY2twcm9wYWdhdGVHcmFkaWVudHMoXG4gICAgICAgICAgIGFjY3VtdWxhdGVkR3JhZGllbnRzTWFwLCB0YXBlLCBmID0+IHRmLnRpZHkoZiBhcyBTY29wZUZuPHRmLlRlbnNvcj4pLFxuICAgICAgICAgICBhZGQpO1xuICAgICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IGFjY3VtdWxhdGVkR3JhZGllbnRzTWFwW3guaWRdLmRhdGEoKSwgWzAsIDUsIDBdKTtcbiAgICAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCBkeXNbMF0uZGF0YSgpLCBbMF0pO1xuICAgICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IGR5c1sxXS5kYXRhKCksIFs1XSk7XG4gICAgICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgZHlzWzJdLmRhdGEoKSwgWzBdKTtcbiAgICAgfSk7XG59KTtcbiJdfQ==