/** * @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==