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
/**
 * @license
 * Copyright 2018 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 * as tf from '../index';
import { ALL_ENVS, describeWithFlags } from '../jasmine_util';
import { expectArraysClose } from '../test_util';
import { Optimizer } from './optimizer';
import { SGDOptimizer } from './sgd_optimizer';
describeWithFlags('optimizer', ALL_ENVS, () => {
    it('basic', async () => {
        const initialTensors = tf.memory().numTensors;
        const learningRate = .1;
        const optimizer = tf.train.sgd(learningRate);
        const x = tf.scalar(4).variable();
        const bias = tf.scalar(1).variable();
        const strayVariable = tf.scalar(-1).variable();
        let numTensors = tf.memory().numTensors;
        // tslint:disable-next-line: no-unnecessary-type-assertion
        const f = () => x.square().add(bias);
        let cost = optimizer.minimize(f, /* returnCost */ true);
        // Cost should be the only additional arrays.
        expect(tf.memory().numTensors).toBe(numTensors + 1);
        // de/dx = 2x
        const expectedX1 = -2 * 4 * learningRate + 4;
        // de/db = 1
        const expectedBias1 = -1 * learningRate + 1;
        expectArraysClose(await x.data(), [expectedX1]);
        expectArraysClose(await bias.data(), [expectedBias1]);
        expectArraysClose(await cost.data(), [Math.pow(4, 2) + 1]);
        // The stray variable should remain unchanged.
        expectArraysClose(await strayVariable.data(), [-1]);
        cost.dispose();
        numTensors = tf.memory().numTensors;
        cost = optimizer.minimize(f, /* returnCost */ false);
        // There should be no new additional Tensors.
        expect(tf.memory().numTensors).toBe(numTensors);
        const expectedX2 = -2 * expectedX1 * learningRate + expectedX1;
        const expectedBias2 = -learningRate + expectedBias1;
        expectArraysClose(await x.data(), [expectedX2]);
        expectArraysClose(await bias.data(), [expectedBias2]);
        expect(cost).toBe(null);
        // The stray variable should remain unchanged.
        expectArraysClose(await strayVariable.data(), [-1]);
        optimizer.dispose();
        x.dispose();
        bias.dispose();
        strayVariable.dispose();
        // The only additional tensors remaining are the arguments to variable().
        expect(tf.memory().numTensors).toBe(initialTensors + 3);
    });
    it('varList array of all variables', async () => {
        const learningRate = .1;
        const optimizer = new SGDOptimizer(learningRate);
        const x = tf.scalar(4).variable();
        const bias = tf.scalar(1).variable();
        const strayVariable = tf.scalar(-1).variable();
        const varList = [x, bias];
        // tslint:disable-next-line: no-unnecessary-type-assertion
        const f = () => x.square().add(bias);
        let cost = optimizer.minimize(f, /* returnCost */ true, varList);
        // de/dx = 2x
        const expectedX1 = -2 * 4 * learningRate + 4;
        // de/db = 1
        const expectedBias1 = -1 * learningRate + 1;
        expectArraysClose(await x.data(), [expectedX1]);
        expectArraysClose(await bias.data(), [expectedBias1]);
        expectArraysClose(await cost.data(), [Math.pow(4, 2) + 1]);
        // The stray variable should remain unchanged.
        expectArraysClose(await strayVariable.data(), [-1]);
        cost = optimizer.minimize(f, /* returnCost */ false, varList);
        const expectedX2 = -2 * expectedX1 * learningRate + expectedX1;
        const expectedBias2 = -learningRate + expectedBias1;
        expectArraysClose(await x.data(), [expectedX2]);
        expectArraysClose(await bias.data(), [expectedBias2]);
        // The stray variable should remain unchanged.
        expectArraysClose(await strayVariable.data(), [-1]);
        expect(cost).toBe(null);
    });
    it('varList empty array of variables throws error', () => {
        const learningRate = .1;
        const optimizer = new SGDOptimizer(learningRate);
        const x = tf.scalar(4).variable();
        const bias = tf.scalar(1).variable();
        // Stray variable.
        tf.scalar(-1).variable();
        const varList = [];
        // tslint:disable-next-line: no-unnecessary-type-assertion
        const f = () => x.square().add(bias);
        expect(() => optimizer.minimize(f, /* returnCost */ true, varList))
            .toThrowError();
    });
    it('varList subset of variables update', async () => {
        const learningRate = .1;
        const optimizer = new SGDOptimizer(learningRate);
        const x = tf.scalar(4).variable();
        const bias = tf.scalar(1).variable();
        const strayVariable = tf.scalar(-1).variable();
        const varList = [x];
        // tslint:disable-next-line: no-unnecessary-type-assertion
        const f = () => x.square().add(bias);
        let cost = optimizer.minimize(f, /* returnCost */ true, varList);
        // de/dx = 2x
        const expectedValue1 = -2 * 4 * learningRate + 4;
        expectArraysClose(await x.data(), [expectedValue1]);
        // bias should remain unchanged.
        expectArraysClose(await bias.data(), [1]);
        expectArraysClose(await cost.data(), [Math.pow(4, 2) + 1]);
        // The stray variable should remain unchanged.
        expectArraysClose(await strayVariable.data(), [-1]);
        cost = optimizer.minimize(f, /* returnCost */ false, varList);
        const expectedValue2 = -2 * expectedValue1 * learningRate + expectedValue1;
        expectArraysClose(await x.data(), [expectedValue2]);
        // Bias still should remain unchanged.
        expectArraysClose(await bias.data(), [1]);
        expect(cost).toBe(null);
        // The stray variable should remain unchanged.
        expectArraysClose(await strayVariable.data(), [-1]);
    });
    it('only bias trainable', async () => {
        const learningRate = .1;
        const optimizer = new SGDOptimizer(learningRate);
        const trainable = false;
        const x = tf.scalar(4).variable(trainable);
        const bias = tf.scalar(1).variable();
        const strayVariable = tf.scalar(-1).variable();
        // tslint:disable-next-line: no-unnecessary-type-assertion
        const f = () => x.square().add(bias);
        let cost = optimizer.minimize(f, /* returnCost */ true);
        // x should not have been updated.
        expectArraysClose(await x.data(), [4]);
        // de/db = 1
        const expectedBias1 = -1 * learningRate + 1;
        expectArraysClose(await bias.data(), [expectedBias1]);
        expectArraysClose(await cost.data(), [Math.pow(4, 2) + 1]);
        // The stray variable should remain unchanged.
        expectArraysClose(await strayVariable.data(), [-1]);
        cost = optimizer.minimize(f, /* returnCost */ false);
        // x should not have been updated.
        expectArraysClose(await x.data(), [4]);
        const expectedBias2 = -learningRate + expectedBias1;
        expectArraysClose(await bias.data(), [expectedBias2]);
        expect(cost).toBe(null);
        // The stray variable should remain unchanged.
        expectArraysClose(await strayVariable.data(), [-1]);
    });
    it('only bias trainable, only x in varList throws error', () => {
        const learningRate = .1;
        const optimizer = new SGDOptimizer(learningRate);
        const trainable = false;
        const x = tf.scalar(4).variable(trainable);
        const bias = tf.scalar(1).variable();
        // stray variable.
        tf.scalar(-1).variable();
        const varList = [x];
        // tslint:disable-next-line: no-unnecessary-type-assertion
        const f = () => x.square().add(bias);
        expect(() => optimizer.minimize(f, /* returnCost */ true, varList))
            .toThrowError();
    });
    it('instanceof Optimizer', () => {
        const learningRate = .1;
        const optimizer = new SGDOptimizer(learningRate);
        expect(optimizer instanceof Optimizer).toBe(true);
    });
    it('throws error when f returns a non-scalar', () => {
        const learningRate = .1;
        const optimizer = new SGDOptimizer(learningRate);
        const x = tf.tensor1d([1, 2]).variable();
        const f = () => x.square();
        // tslint:disable-next-line:no-any
        expect(() => optimizer.minimize(f)).toThrowError();
    });
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3B0aW1pemVyX3Rlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi90ZmpzLWNvcmUvc3JjL29wdGltaXplcnMvb3B0aW1pemVyX3Rlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBRUgsT0FBTyxLQUFLLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDL0IsT0FBTyxFQUFDLFFBQVEsRUFBRSxpQkFBaUIsRUFBQyxNQUFNLGlCQUFpQixDQUFDO0FBRTVELE9BQU8sRUFBQyxpQkFBaUIsRUFBQyxNQUFNLGNBQWMsQ0FBQztBQUUvQyxPQUFPLEVBQUMsU0FBUyxFQUFDLE1BQU0sYUFBYSxDQUFDO0FBQ3RDLE9BQU8sRUFBQyxZQUFZLEVBQUMsTUFBTSxpQkFBaUIsQ0FBQztBQUU3QyxpQkFBaUIsQ0FBQyxXQUFXLEVBQUUsUUFBUSxFQUFFLEdBQUcsRUFBRTtJQUM1QyxFQUFFLENBQUMsT0FBTyxFQUFFLEtBQUssSUFBSSxFQUFFO1FBQ3JCLE1BQU0sY0FBYyxHQUFHLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxVQUFVLENBQUM7UUFDOUMsTUFBTSxZQUFZLEdBQUcsRUFBRSxDQUFDO1FBQ3hCLE1BQU0sU0FBUyxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBRTdDLE1BQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDbEMsTUFBTSxJQUFJLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNyQyxNQUFNLGFBQWEsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxFQUFFLENBQUM7UUFFL0MsSUFBSSxVQUFVLEdBQUcsRUFBRSxDQUFDLE1BQU0sRUFBRSxDQUFD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OlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IDIwMTggR29vZ2xlIExMQy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqL1xuXG5pbXBvcnQgKiBhcyB0ZiBmcm9tICcuLi9pbmRleCc7XG5pbXBvcnQge0FMTF9FTlZTLCBkZXNjcmliZVdpdGhGbGFnc30gZnJvbSAnLi4vamFzbWluZV91dGlsJztcbmltcG9ydCB7VmFyaWFibGV9IGZyb20gJy4uL3RlbnNvcic7XG5pbXBvcnQge2V4cGVjdEFycmF5c0Nsb3NlfSBmcm9tICcuLi90ZXN0X3V0aWwnO1xuXG5pbXBvcnQge09wdGltaXplcn0gZnJvbSAnLi9vcHRpbWl6ZXInO1xuaW1wb3J0IHtTR0RPcHRpbWl6ZXJ9IGZyb20gJy4vc2dkX29wdGltaXplcic7XG5cbmRlc2NyaWJlV2l0aEZsYWdzKCdvcHRpbWl6ZXInLCBBTExfRU5WUywgKCkgPT4ge1xuICBpdCgnYmFzaWMnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgaW5pdGlhbFRlbnNvcnMgPSB0Zi5tZW1vcnkoKS5udW1UZW5zb3JzO1xuICAgIGNvbnN0IGxlYXJuaW5nUmF0ZSA9IC4xO1xuICAgIGNvbnN0IG9wdGltaXplciA9IHRmLnRyYWluLnNnZChsZWFybmluZ1JhdGUpO1xuXG4gICAgY29uc3QgeCA9IHRmLnNjYWxhcig0KS52YXJpYWJsZSgpO1xuICAgIGNvbnN0IGJpYXMgPSB0Zi5zY2FsYXIoMSkudmFyaWFibGUoKTtcbiAgICBjb25zdCBzdHJheVZhcmlhYmxlID0gdGYuc2NhbGFyKC0xKS52YXJpYWJsZSgpO1xuXG4gICAgbGV0IG51bVRlbnNvcnMgPSB0Zi5tZW1vcnkoKS5udW1UZW5zb3JzO1xuXG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOiBuby11bm5lY2Vzc2FyeS10eXBlLWFzc2VydGlvblxuICAgIGNvbnN0IGYgPSAoKSA9PiB4LnNxdWFyZSgpLmFkZChiaWFzKSBhcyB0Zi5TY2FsYXI7XG5cbiAgICBsZXQgY29zdCA9IG9wdGltaXplci5taW5pbWl6ZShmLCAvKiByZXR1cm5Db3N0ICovIHRydWUpO1xuXG4gICAgLy8gQ29zdCBzaG91bGQgYmUgdGhlIG9ubHkgYWRkaXRpb25hbCBhcnJheXMuXG4gICAgZXhwZWN0KHRmLm1lbW9yeSgpLm51bVRlbnNvcnMpLnRvQmUobnVtVGVuc29ycyArIDEpO1xuXG4gICAgLy8gZGUvZHggPSAyeFxuICAgIGNvbnN0IGV4cGVjdGVkWDEgPSAtMiAqIDQgKiBsZWFybmluZ1JhdGUgKyA0O1xuICAgIC8vIGRlL2RiID0gMVxuICAgIGNvbnN0IGV4cGVjdGVkQmlhczEgPSAtMSAqIGxlYXJuaW5nUmF0ZSArIDE7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgeC5kYXRhKCksIFtleHBlY3RlZFgxXSk7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgYmlhcy5kYXRhKCksIFtleHBlY3RlZEJpYXMxXSk7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgY29zdC5kYXRhKCksIFtNYXRoLnBvdyg0LCAyKSArIDFdKTtcbiAgICAvLyBUaGUgc3RyYXkgdmFyaWFibGUgc2hvdWxkIHJlbWFpbiB1bmNoYW5nZWQuXG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgc3RyYXlWYXJpYWJsZS5kYXRhKCksIFstMV0pO1xuXG4gICAgY29zdC5kaXNwb3NlKCk7XG4gICAgbnVtVGVuc29ycyA9IHRmLm1lbW9yeSgpLm51bVRlbnNvcnM7XG5cbiAgICBjb3N0ID0gb3B0aW1pemVyLm1pbmltaXplKGYsIC8qIHJldHVybkNvc3QgKi8gZmFsc2UpO1xuICAgIC8vIFRoZXJlIHNob3VsZCBiZSBubyBuZXcgYWRkaXRpb25hbCBUZW5zb3JzLlxuICAgIGV4cGVjdCh0Zi5tZW1vcnkoKS5udW1UZW5zb3JzKS50b0JlKG51bVRlbnNvcnMpO1xuXG4gICAgY29uc3QgZXhwZWN0ZWRYMiA9IC0yICogZXhwZWN0ZWRYMSAqIGxlYXJuaW5nUmF0ZSArIGV4cGVjdGVkWDE7XG4gICAgY29uc3QgZXhwZWN0ZWRCaWFzMiA9IC1sZWFybmluZ1JhdGUgKyBleHBlY3RlZEJpYXMxO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IHguZGF0YSgpLCBbZXhwZWN0ZWRYMl0pO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IGJpYXMuZGF0YSgpLCBbZXhwZWN0ZWRCaWFzMl0pO1xuICAgIGV4cGVjdChjb3N0KS50b0JlKG51bGwpO1xuICAgIC8vIFRoZSBzdHJheSB2YXJpYWJsZSBzaG91bGQgcmVtYWluIHVuY2hhbmdlZC5cbiAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCBzdHJheVZhcmlhYmxlLmRhdGEoKSwgWy0xXSk7XG5cbiAgICBvcHRpbWl6ZXIuZGlzcG9zZSgpO1xuICAgIHguZGlzcG9zZSgpO1xuICAgIGJpYXMuZGlzcG9zZSgpO1xuICAgIHN0cmF5VmFyaWFibGUuZGlzcG9zZSgpO1xuICAgIC8vIFRoZSBvbmx5IGFkZGl0aW9uYWwgdGVuc29ycyByZW1haW5pbmcgYXJlIHRoZSBhcmd1bWVudHMgdG8gdmFyaWFibGUoKS5cbiAgICBleHBlY3QodGYubWVtb3J5KCkubnVtVGVuc29ycykudG9CZShpbml0aWFsVGVuc29ycyArIDMpO1xuICB9KTtcblxuICBpdCgndmFyTGlzdCBhcnJheSBvZiBhbGwgdmFyaWFibGVzJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGxlYXJuaW5nUmF0ZSA9IC4xO1xuICAgIGNvbnN0IG9wdGltaXplciA9IG5ldyBTR0RPcHRpbWl6ZXIobGVhcm5pbmdSYXRlKTtcblxuICAgIGNvbnN0IHggPSB0Zi5zY2FsYXIoNCkudmFyaWFibGUoKTtcbiAgICBjb25zdCBiaWFzID0gdGYuc2NhbGFyKDEpLnZhcmlhYmxlKCk7XG4gICAgY29uc3Qgc3RyYXlWYXJpYWJsZSA9IHRmLnNjYWxhcigtMSkudmFyaWFibGUoKTtcbiAgICBjb25zdCB2YXJMaXN0ID0gW3gsIGJpYXNdO1xuXG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOiBuby11bm5lY2Vzc2FyeS10eXBlLWFzc2VydGlvblxuICAgIGNvbnN0IGYgPSAoKSA9PiB4LnNxdWFyZSgpLmFkZChiaWFzKSBhcyB0Zi5TY2FsYXI7XG5cbiAgICBsZXQgY29zdCA9IG9wdGltaXplci5taW5pbWl6ZShmLCAvKiByZXR1cm5Db3N0ICovIHRydWUsIHZhckxpc3QpO1xuXG4gICAgLy8gZGUvZHggPSAyeFxuICAgIGNvbnN0IGV4cGVjdGVkWDEgPSAtMiAqIDQgKiBsZWFybmluZ1JhdGUgKyA0O1xuICAgIC8vIGRlL2RiID0gMVxuICAgIGNvbnN0IGV4cGVjdGVkQmlhczEgPSAtMSAqIGxlYXJuaW5nUmF0ZSArIDE7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgeC5kYXRhKCksIFtleHBlY3RlZFgxXSk7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgYmlhcy5kYXRhKCksIFtleHBlY3RlZEJpYXMxXSk7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgY29zdC5kYXRhKCksIFtNYXRoLnBvdyg0LCAyKSArIDFdKTtcbiAgICAvLyBUaGUgc3RyYXkgdmFyaWFibGUgc2hvdWxkIHJlbWFpbiB1bmNoYW5nZWQuXG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgc3RyYXlWYXJpYWJsZS5kYXRhKCksIFstMV0pO1xuXG4gICAgY29zdCA9IG9wdGltaXplci5taW5pbWl6ZShmLCAvKiByZXR1cm5Db3N0ICovIGZhbHNlLCB2YXJMaXN0KTtcblxuICAgIGNvbnN0IGV4cGVjdGVkWDIgPSAtMiAqIGV4cGVjdGVkWDEgKiBsZWFybmluZ1JhdGUgKyBleHBlY3RlZFgxO1xuICAgIGNvbnN0IGV4cGVjdGVkQmlhczIgPSAtbGVhcm5pbmdSYXRlICsgZXhwZWN0ZWRCaWFzMTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCB4LmRhdGEoKSwgW2V4cGVjdGVkWDJdKTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCBiaWFzLmRhdGEoKSwgW2V4cGVjdGVkQmlhczJdKTtcbiAgICAvLyBUaGUgc3RyYXkgdmFyaWFibGUgc2hvdWxkIHJlbWFpbiB1bmNoYW5nZWQuXG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgc3RyYXlWYXJpYWJsZS5kYXRhKCksIFstMV0pO1xuICAgIGV4cGVjdChjb3N0KS50b0JlKG51bGwpO1xuICB9KTtcblxuICBpdCgndmFyTGlzdCBlbXB0eSBhcnJheSBvZiB2YXJpYWJsZXMgdGhyb3dzIGVycm9yJywgKCkgPT4ge1xuICAgIGNvbnN0IGxlYXJuaW5nUmF0ZSA9IC4xO1xuICAgIGNvbnN0IG9wdGltaXplciA9IG5ldyBTR0RPcHRpbWl6ZXIobGVhcm5pbmdSYXRlKTtcblxuICAgIGNvbnN0IHggPSB0Zi5zY2FsYXIoNCkudmFyaWFibGUoKTtcbiAgICBjb25zdCBiaWFzID0gdGYuc2NhbGFyKDEpLnZhcmlhYmxlKCk7XG4gICAgLy8gU3RyYXkgdmFyaWFibGUuXG4gICAgdGYuc2NhbGFyKC0xKS52YXJpYWJsZSgpO1xuICAgIGNvbnN0IHZhckxpc3Q6IFZhcmlhYmxlW10gPSBbXTtcblxuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTogbm8tdW5uZWNlc3NhcnktdHlwZS1hc3NlcnRpb25cbiAgICBjb25zdCBmID0gKCkgPT4geC5zcXVhcmUoKS5hZGQoYmlhcykgYXMgdGYuU2NhbGFyO1xuXG4gICAgZXhwZWN0KCgpID0+IG9wdGltaXplci5taW5pbWl6ZShmLCAvKiByZXR1cm5Db3N0ICovIHRydWUsIHZhckxpc3QpKVxuICAgICAgICAudG9UaHJvd0Vycm9yKCk7XG4gIH0pO1xuXG4gIGl0KCd2YXJMaXN0IHN1YnNldCBvZiB2YXJpYWJsZXMgdXBkYXRlJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGxlYXJuaW5nUmF0ZSA9IC4xO1xuICAgIGNvbnN0IG9wdGltaXplciA9IG5ldyBTR0RPcHRpbWl6ZXIobGVhcm5pbmdSYXRlKTtcblxuICAgIGNvbnN0IHggPSB0Zi5zY2FsYXIoNCkudmFyaWFibGUoKTtcbiAgICBjb25zdCBiaWFzID0gdGYuc2NhbGFyKDEpLnZhcmlhYmxlKCk7XG4gICAgY29uc3Qgc3RyYXlWYXJpYWJsZSA9IHRmLnNjYWxhcigtMSkudmFyaWFibGUoKTtcbiAgICBjb25zdCB2YXJMaXN0ID0gW3hdO1xuXG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOiBuby11bm5lY2Vzc2FyeS10eXBlLWFzc2VydGlvblxuICAgIGNvbnN0IGYgPSAoKSA9PiB4LnNxdWFyZSgpLmFkZChiaWFzKSBhcyB0Zi5TY2FsYXI7XG5cbiAgICBsZXQgY29zdCA9IG9wdGltaXplci5taW5pbWl6ZShmLCAvKiByZXR1cm5Db3N0ICovIHRydWUsIHZhckxpc3QpO1xuXG4gICAgLy8gZGUvZHggPSAyeFxuICAgIGNvbnN0IGV4cGVjdGVkVmFsdWUxID0gLTIgKiA0ICogbGVhcm5pbmdSYXRlICsgNDtcbiAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCB4LmRhdGEoKSwgW2V4cGVjdGVkVmFsdWUxXSk7XG4gICAgLy8gYmlhcyBzaG91bGQgcmVtYWluIHVuY2hhbmdlZC5cbiAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCBiaWFzLmRhdGEoKSwgWzFdKTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCBjb3N0LmRhdGEoKSwgW01hdGgucG93KDQsIDIpICsgMV0pO1xuICAgIC8vIFRoZSBzdHJheSB2YXJpYWJsZSBzaG91bGQgcmVtYWluIHVuY2hhbmdlZC5cbiAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCBzdHJheVZhcmlhYmxlLmRhdGEoKSwgWy0xXSk7XG5cbiAgICBjb3N0ID0gb3B0aW1pemVyLm1pbmltaXplKGYsIC8qIHJldHVybkNvc3QgKi8gZmFsc2UsIHZhckxpc3QpO1xuXG4gICAgY29uc3QgZXhwZWN0ZWRWYWx1ZTIgPSAtMiAqIGV4cGVjdGVkVmFsdWUxICogbGVhcm5pbmdSYXRlICsgZXhwZWN0ZWRWYWx1ZTE7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgeC5kYXRhKCksIFtleHBlY3RlZFZhbHVlMl0pO1xuICAgIC8vIEJpYXMgc3RpbGwgc2hvdWxkIHJlbWFpbiB1bmNoYW5nZWQuXG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgYmlhcy5kYXRhKCksIFsxXSk7XG4gICAgZXhwZWN0KGNvc3QpLnRvQmUobnVsbCk7XG4gICAgLy8gVGhlIHN0cmF5IHZhcmlhYmxlIHNob3VsZCByZW1haW4gdW5jaGFuZ2VkLlxuICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IHN0cmF5VmFyaWFibGUuZGF0YSgpLCBbLTFdKTtcbiAgfSk7XG5cbiAgaXQoJ29ubHkgYmlhcyB0cmFpbmFibGUnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgbGVhcm5pbmdSYXRlID0gLjE7XG4gICAgY29uc3Qgb3B0aW1pemVyID0gbmV3IFNHRE9wdGltaXplcihsZWFybmluZ1JhdGUpO1xuXG4gICAgY29uc3QgdHJhaW5hYmxlID0gZmFsc2U7XG4gICAgY29uc3QgeCA9IHRmLnNjYWxhcig0KS52YXJpYWJsZSh0cmFpbmFibGUpO1xuICAgIGNvbnN0IGJpYXMgPSB0Zi5zY2FsYXIoMSkudmFyaWFibGUoKTtcbiAgICBjb25zdCBzdHJheVZhcmlhYmxlID0gdGYuc2NhbGFyKC0xKS52YXJpYWJsZSgpO1xuXG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOiBuby11bm5lY2Vzc2FyeS10eXBlLWFzc2VydGlvblxuICAgIGNvbnN0IGYgPSAoKSA9PiB4LnNxdWFyZSgpLmFkZChiaWFzKSBhcyB0Zi5TY2FsYXI7XG5cbiAgICBsZXQgY29zdCA9IG9wdGltaXplci5taW5pbWl6ZShmLCAvKiByZXR1cm5Db3N0ICovIHRydWUpO1xuXG4gICAgLy8geCBzaG91bGQgbm90IGhhdmUgYmVlbiB1cGRhdGVkLlxuICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IHguZGF0YSgpLCBbNF0pO1xuICAgIC8vIGRlL2RiID0gMVxuICAgIGNvbnN0IGV4cGVjdGVkQmlhczEgPSAtMSAqIGxlYXJuaW5nUmF0ZSArIDE7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgYmlhcy5kYXRhKCksIFtleHBlY3RlZEJpYXMxXSk7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgY29zdC5kYXRhKCksIFtNYXRoLnBvdyg0LCAyKSArIDFdKTtcbiAgICAvLyBUaGUgc3RyYXkgdmFyaWFibGUgc2hvdWxkIHJlbWFpbiB1bmNoYW5nZWQuXG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgc3RyYXlWYXJpYWJsZS5kYXRhKCksIFstMV0pO1xuXG4gICAgY29zdCA9IG9wdGltaXplci5taW5pbWl6ZShmLCAvKiByZXR1cm5Db3N0ICovIGZhbHNlKTtcblxuICAgIC8vIHggc2hvdWxkIG5vdCBoYXZlIGJlZW4gdXBkYXRlZC5cbiAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCB4LmRhdGEoKSwgWzRdKTtcbiAgICBjb25zdCBleHBlY3RlZEJpYXMyID0gLWxlYXJuaW5nUmF0ZSArIGV4cGVjdGVkQmlhczE7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgYmlhcy5kYXRhKCksIFtleHBlY3RlZEJpYXMyXSk7XG4gICAgZXhwZWN0KGNvc3QpLnRvQmUobnVsbCk7XG4gICAgLy8gVGhlIHN0cmF5IHZhcmlhYmxlIHNob3VsZCByZW1haW4gdW5jaGFuZ2VkLlxuICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IHN0cmF5VmFyaWFibGUuZGF0YSgpLCBbLTFdKTtcbiAgfSk7XG5cbiAgaXQoJ29ubHkgYmlhcyB0cmFpbmFibGUsIG9ubHkgeCBpbiB2YXJMaXN0IHRocm93cyBlcnJvcicsICgpID0+IHtcbiAgICBjb25zdCBsZWFybmluZ1JhdGUgPSAuMTtcbiAgICBjb25zdCBvcHRpbWl6ZXIgPSBuZXcgU0dET3B0aW1pemVyKGxlYXJuaW5nUmF0ZSk7XG5cbiAgICBjb25zdCB0cmFpbmFibGUgPSBmYWxzZTtcbiAgICBjb25zdCB4ID0gdGYuc2NhbGFyKDQpLnZhcmlhYmxlKHRyYWluYWJsZSk7XG4gICAgY29uc3QgYmlhcyA9IHRmLnNjYWxhcigxKS52YXJpYWJsZSgpO1xuICAgIC8vIHN0cmF5IHZhcmlhYmxlLlxuICAgIHRmLnNjYWxhcigtMSkudmFyaWFibGUoKTtcbiAgICBjb25zdCB2YXJMaXN0ID0gW3hdO1xuXG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOiBuby11bm5lY2Vzc2FyeS10eXBlLWFzc2VydGlvblxuICAgIGNvbnN0IGYgPSAoKSA9PiB4LnNxdWFyZSgpLmFkZChiaWFzKSBhcyB0Zi5TY2FsYXI7XG5cbiAgICBleHBlY3QoKCkgPT4gb3B0aW1pemVyLm1pbmltaXplKGYsIC8qIHJldHVybkNvc3QgKi8gdHJ1ZSwgdmFyTGlzdCkpXG4gICAgICAgIC50b1Rocm93RXJyb3IoKTtcbiAgfSk7XG5cbiAgaXQoJ2luc3RhbmNlb2YgT3B0aW1pemVyJywgKCkgPT4ge1xuICAgIGNvbnN0IGxlYXJuaW5nUmF0ZSA9IC4xO1xuICAgIGNvbnN0IG9wdGltaXplciA9IG5ldyBTR0RPcHRpbWl6ZXIobGVhcm5pbmdSYXRlKTtcblxuICAgIGV4cGVjdChvcHRpbWl6ZXIgaW5zdGFuY2VvZiBPcHRpbWl6ZXIpLnRvQmUodHJ1ZSk7XG4gIH0pO1xuXG4gIGl0KCd0aHJvd3MgZXJyb3Igd2hlbiBmIHJldHVybnMgYSBub24tc2NhbGFyJywgKCkgPT4ge1xuICAgIGNvbnN0IGxlYXJuaW5nUmF0ZSA9IC4xO1xuICAgIGNvbnN0IG9wdGltaXplciA9IG5ldyBTR0RPcHRpbWl6ZXIobGVhcm5pbmdSYXRlKTtcblxuICAgIGNvbnN0IHggPSB0Zi50ZW5zb3IxZChbMSwgMl0pLnZhcmlhYmxlKCk7XG4gICAgY29uc3QgZiA9ICgpID0+IHguc3F1YXJlKCk7XG5cbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgZXhwZWN0KCgpID0+IG9wdGltaXplci5taW5pbWl6ZShmIGFzIGFueSkpLnRvVGhyb3dFcnJvcigpO1xuICB9KTtcbn0pO1xuIl19