/**
|
* @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';
|
describeWithFlags('RMSPropOptimizer', ALL_ENVS, () => {
|
it('basic', async () => {
|
const initialTensors = tf.memory().numTensors;
|
const learningRate = 0.1;
|
const moment = 0.1;
|
const rho = 0.95;
|
const optimizer = tf.train.rmsprop(learningRate, rho, moment);
|
const x = tf.tensor1d([1, 2]).variable();
|
const f = () => x.square().sum();
|
let numTensors = tf.memory().numTensors;
|
let cost = optimizer.minimize(f, /* returnCost */ true);
|
// Cost & 2 accumulators should be the only additional arrays.
|
expect(tf.memory().numTensors).toBe(numTensors + 3);
|
// epsilon = 1e-8
|
// newAccumulatedMeanSquare =
|
// rho * accumulatedMeanSquare + (1 - rho) * grad ^ 2 = (0.2)
|
// newAccumulatedMoments = momentum * accumulatedMoments +
|
// learning_rate * gradient / sqrt(newAccumulatedMeanSquare +
|
// epsilon) = 0.1 * 0 + ((0.1 * 2) / sqrt(0.2 + 1e-8)) = 0.44721
|
// x -= learningRate * newAccumulatedMoments
|
//
|
// de/dx = [2, 4]
|
// accumulatedMeanSquare = [0, 0]
|
// newAccumulatedMeanSquare = [.2, .8]
|
// accumulatedMoments = [0, 0]
|
// newAccumulatedMoments = [0.44721, 0.44721]
|
// x = [0.55279, 1.55279]
|
expectArraysClose(await x.data(), [0.55279, 1.55279]);
|
cost.dispose();
|
numTensors = tf.memory().numTensors;
|
cost = optimizer.minimize(f, /* returnCost */ false);
|
// x = [0.55279, 1.55279]
|
// de/dx = [1.10558, 3.10558]
|
// accumulatedMeanSquare = [0.2, 0.8]
|
// newAccumulatedMeanSquare = [0.25105125, 1.242231]
|
// accumulatedMoments = [0.44721, 0.44721]
|
// newAccumulatedMoments = [0.26534, 0.32336]
|
// x = [0.28745, 1.22943]
|
// TODO: Fix numerical precision.
|
expectArraysClose(await x.data(), [0.28745, 1.222943], 1e-2);
|
// There should be no new additional Tensors.
|
expect(tf.memory().numTensors).toBe(numTensors);
|
expect(cost).toBe(null);
|
x.dispose();
|
optimizer.dispose();
|
// The only additional tensor remaining is the argument to variable().
|
expect(tf.memory().numTensors).toBe(initialTensors + 1);
|
});
|
it('gradient with centered momentum', async () => {
|
const initialTensors = tf.memory().numTensors;
|
const learningRate = 0.1;
|
const moment = 0.1;
|
const rho = 0.95;
|
const eps = 1e-8;
|
const optimizer = tf.train.rmsprop(learningRate, rho, moment, eps, true);
|
const x = tf.tensor1d([1, 2]).variable();
|
const f = () => x.square().sum();
|
let numTensors = tf.memory().numTensors;
|
let cost = optimizer.minimize(f, /* returnCost */ true);
|
// Cost & 3 accumulators should be the only additional arrays.
|
expect(tf.memory().numTensors).toBe(numTensors + 4);
|
// epsilon = 1e-8
|
// newAccumulatedMeanSquare =
|
// rho * accumulatedMeanSquare + (1 - rho) * grad ^ 2 = [.2, .8]
|
// newAccumulatedMeanGrad =
|
// rho * accumulatedMeanGrad + (1 - rho) * grad = [0.1, 0.2]
|
// newAccumulatedMoments = momentum * accumulatedMoments +
|
// learning_rate * gradient / sqrt(newAccumulatedMeanSquare
|
// - newAccumulatedMeanGrad * 2 +
|
// epsilon) = 0.1 * 0 + ((0.1 * 2)
|
// / sqrt(0.2 - 0.01 + 1e-8)) = 0.458831
|
// x -= learningRate * newAccumulatedMoments
|
//
|
// de/dx = [2, 4]
|
// accumulatedMeanSquare = [0, 0]
|
// newAccumulatedMeanSquare = [.2, .8]
|
// newAccumulatedMeanGrad = [.1, .2]
|
// accumulatedMoments = [0, 0]
|
// newAccumulatedMoments = [0.45883, 0.458831]
|
// x = [0.54117, 1.541169]
|
expectArraysClose(await x.data(), [0.54117, 1.541169]);
|
cost.dispose();
|
numTensors = tf.memory().numTensors;
|
cost = optimizer.minimize(f, /* returnCost */ false);
|
// x = [0.54117, 1.541169]
|
// de/dx = [1.08234, 3.082338]
|
// accumulatedMeanSquare = [0.2, 0.8]
|
// accumulatedMeanGrad = [.1, .2]
|
// newAccumulatedMeanSquare = [0.248572, 1.235040]
|
// newAccumulatedMeanGrad = [0.149117, 0.3441169]
|
// accumulatedMoments = [0.45883, 0.458831]
|
// newAccumulatedMoments = [0.273385, 0.3375766]
|
// x = [0.267785, 1.2035924]
|
// TODO: Fix numerical precision.
|
expectArraysClose(await x.data(), [0.267785, 1.2035924], 1e-2);
|
// There should be no new additional Tensors.
|
expect(tf.memory().numTensors).toBe(numTensors);
|
expect(cost).toBe(null);
|
x.dispose();
|
optimizer.dispose();
|
// The only additional tensor remaining is the argument to variable().
|
expect(tf.memory().numTensors).toBe(initialTensors + 1);
|
});
|
it('Save and load weights: centered = false', async () => {
|
const learningRate = 0.1;
|
const moment = 0.1;
|
const rho = 0.95;
|
const optimizer1 = tf.train.rmsprop(learningRate, rho, moment);
|
const x = tf.tensor1d([1, 2]).variable();
|
const f = () => x.square().sum();
|
let cost = optimizer1.minimize(f, /* returnCost */ true);
|
expectArraysClose(await cost.data(), 5);
|
expectArraysClose(await x.data(), [0.5527865, 1.5527864]);
|
const weights = await optimizer1.getWeights();
|
// An iteration variable and two optimizer state variables.
|
expect(weights.length).toEqual(3);
|
const optimizer2 = tf.train.rmsprop(learningRate, rho, moment);
|
await optimizer2.setWeights(weights);
|
cost = optimizer2.minimize(f, /* returnCost */ true);
|
expectArraysClose(await cost.data(), 2.7167187);
|
expectArraysClose(await x.data(), [0.2874418, 1.2294267]);
|
expect(optimizer2.iterations).toEqual(2);
|
});
|
it('Save, load weights and continue training: centered = true', async () => {
|
const learningRate = 0.1;
|
const moment = 0.1;
|
const rho = 0.95;
|
const epsilon = undefined;
|
const centered = true;
|
const optimizer1 = tf.train.rmsprop(learningRate, rho, moment, epsilon, centered);
|
const x = tf.tensor1d([1, 2]).variable();
|
const f = () => x.square().sum();
|
let cost = optimizer1.minimize(f, /* returnCost */ true);
|
expectArraysClose(await cost.data(), 5);
|
expectArraysClose(await x.data(), [0.5411684, 1.5411685]);
|
const weights = await optimizer1.getWeights();
|
// An iteration variable and three optimizer state variables.
|
expect(weights.length).toEqual(4);
|
const optimizer2 = tf.train.rmsprop(learningRate, rho, moment, epsilon, centered);
|
await optimizer2.setWeights(weights);
|
cost = optimizer2.minimize(f, /* returnCost */ true);
|
expectArraysClose(await cost.data(), 2.668063);
|
expectArraysClose(await x.data(), [0.2677834, 1.2035918]);
|
expect(optimizer2.iterations).toEqual(2);
|
const optimizer3 = tf.train.rmsprop(learningRate, rho, moment, epsilon, centered);
|
await optimizer3.setWeights(await optimizer2.getWeights());
|
cost = optimizer3.minimize(f, /* returnCost */ true);
|
expectArraysClose(await cost.data(), 1.520341);
|
expect(optimizer3.iterations).toEqual(3);
|
});
|
it('serialization round-trip', () => {
|
const originalOpt = tf.train.rmsprop(0.1, 0.5, 0.1, 1e-7, true);
|
const reserialized = tf.RMSPropOptimizer.fromConfig(tf.RMSPropOptimizer, originalOpt.getConfig());
|
expect(reserialized.getConfig()).toEqual(originalOpt.getConfig());
|
});
|
it('must define learning rate', () => {
|
const learningRate = undefined;
|
expect(() => tf.train.rmsprop(learningRate))
|
.toThrowError(/learningRate for RMSPropOptimizer must be defined./);
|
});
|
});
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm1zcHJvcF9vcHRpbWl6ZXJfdGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3RmanMtY29yZS9zcmMvb3B0aW1pemVycy9ybXNwcm9wX29wdGltaXplcl90ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7R0FlRztBQUVILE9BQU8sS0FBSyxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQy9CLE9BQU8sRUFBQyxRQUFRLEVBQUUsaUJBQWlCLEVBQUMsTUFBTSxpQkFBaUIsQ0FBQztBQUM1RCxPQUFPLEVBQUMsaUJBQWlCLEVBQUMsTUFBTSxjQUFjLENBQUM7QUFFL0MsaUJBQWlCLENBQUMsa0JBQWtCLEVBQUUsUUFBUSxFQUFFLEdBQUcsRUFBRTtJQUNuRCxFQUFFLENBQUMsT0FBTyxFQUFFLEtBQUssSUFBSSxFQUFFO1FBQ3JCLE1BQU0sY0FBYyxHQUFHLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxVQUFVLENBQUM7UUFDOUMsTUFBTSxZQUFZLEdBQUcsR0FBRyxDQUFDO1FBQ3pCLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQztRQUNuQixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUM7UUFDakIsTUFBTSxTQUFTLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsWUFBWSxFQUFFLEdBQUcsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUU5RCxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxFQUFFLENBQUM7UUFFekMsTUFBTSxDQUFDLEdBQUcsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBRWpDLElBQUksVUFBVSxHQUFHLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxVQUFVLENBQUM7UUFFeEMsSUFBSSxJQUFJLEdBQUcsU0FB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c291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IDIwMTggR29vZ2xlIExMQy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqL1xuXG5pbXBvcnQgKiBhcyB0ZiBmcm9tICcuLi9pbmRleCc7XG5pbXBvcnQge0FMTF9FTlZTLCBkZXNjcmliZVdpdGhGbGFnc30gZnJvbSAnLi4vamFzbWluZV91dGlsJztcbmltcG9ydCB7ZXhwZWN0QXJyYXlzQ2xvc2V9IGZyb20gJy4uL3Rlc3RfdXRpbCc7XG5cbmRlc2NyaWJlV2l0aEZsYWdzKCdSTVNQcm9wT3B0aW1pemVyJywgQUxMX0VOVlMsICgpID0+IHtcbiAgaXQoJ2Jhc2ljJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGluaXRpYWxUZW5zb3JzID0gdGYubWVtb3J5KCkubnVtVGVuc29ycztcbiAgICBjb25zdCBsZWFybmluZ1JhdGUgPSAwLjE7XG4gICAgY29uc3QgbW9tZW50ID0gMC4xO1xuICAgIGNvbnN0IHJobyA9IDAuOTU7XG4gICAgY29uc3Qgb3B0aW1pemVyID0gdGYudHJhaW4ucm1zcHJvcChsZWFybmluZ1JhdGUsIHJobywgbW9tZW50KTtcblxuICAgIGNvbnN0IHggPSB0Zi50ZW5zb3IxZChbMSwgMl0pLnZhcmlhYmxlKCk7XG5cbiAgICBjb25zdCBmID0gKCkgPT4geC5zcXVhcmUoKS5zdW0oKTtcblxuICAgIGxldCBudW1UZW5zb3JzID0gdGYubWVtb3J5KCkubnVtVGVuc29ycztcblxuICAgIGxldCBjb3N0ID0gb3B0aW1pemVyLm1pbmltaXplKGYgYXMgKCkgPT4gdGYuU2NhbGFyLCAvKiByZXR1cm5Db3N0ICovIHRydWUpO1xuXG4gICAgLy8gQ29zdCAmIDIgYWNjdW11bGF0b3JzIHNob3VsZCBiZSB0aGUgb25seSBhZGRpdGlvbmFsIGFycmF5cy5cbiAgICBleHBlY3QodGYubWVtb3J5KCkubnVtVGVuc29ycykudG9CZShudW1UZW5zb3JzICsgMyk7XG5cbiAgICAvLyBlcHNpbG9uID0gMWUtOFxuICAgIC8vIG5ld0FjY3VtdWxhdGVkTWVhblNxdWFyZSA9XG4gICAgLy8gICAgICAgICAgcmhvICogYWNjdW11bGF0ZWRNZWFuU3F1YXJlICsgKDEgLSByaG8pICogZ3JhZCBeIDIgPSAoMC4yKVxuICAgIC8vIG5ld0FjY3VtdWxhdGVkTW9tZW50cyA9IG1vbWVudHVtICogYWNjdW11bGF0ZWRNb21lbnRzICtcbiAgICAvLyAgICAgICAgICBsZWFybmluZ19yYXRlICogZ3JhZGllbnQgLyBzcXJ0KG5ld0FjY3VtdWxhdGVkTWVhblNxdWFyZSArXG4gICAgLy8gICAgICAgICAgZXBzaWxvbikgPSAwLjEgKiAwICsgKCgwLjEgKiAyKSAvIHNxcnQoMC4yICsgMWUtOCkpID0gMC40NDcyMVxuICAgIC8vIHggLT0gbGVhcm5pbmdSYXRlICogbmV3QWNjdW11bGF0ZWRNb21lbnRzXG4gICAgLy9cbiAgICAvLyBkZS9keCA9IFsyLCA0XVxuICAgIC8vIGFjY3VtdWxhdGVkTWVhblNxdWFyZSA9IFswLCAwXVxuICAgIC8vIG5ld0FjY3VtdWxhdGVkTWVhblNxdWFyZSA9IFsuMiwgLjhdXG4gICAgLy8gYWNjdW11bGF0ZWRNb21lbnRzID0gWzAsIDBdXG4gICAgLy8gbmV3QWNjdW11bGF0ZWRNb21lbnRzID0gWzAuNDQ3MjEsIDAuNDQ3MjFdXG4gICAgLy8geCA9IFswLjU1Mjc5LCAxLjU1Mjc5XVxuICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IHguZGF0YSgpLCBbMC41NTI3OSwgMS41NTI3OV0pO1xuXG4gICAgY29zdC5kaXNwb3NlKCk7XG4gICAgbnVtVGVuc29ycyA9IHRmLm1lbW9yeSgpLm51bVRlbnNvcnM7XG5cbiAgICBjb3N0ID0gb3B0aW1pemVyLm1pbmltaXplKGYgYXMgKCkgPT4gdGYuU2NhbGFyLCAvKiByZXR1cm5Db3N0ICovIGZhbHNlKTtcblxuICAgIC8vIHggPSBbMC41NTI3OSwgMS41NTI3OV1cbiAgICAvLyBkZS9keCA9IFsxLjEwNTU4LCAzLjEwNTU4XVxuICAgIC8vIGFjY3VtdWxhdGVkTWVhblNxdWFyZSA9IFswLjIsIDAuOF1cbiAgICAvLyBuZXdBY2N1bXVsYXRlZE1lYW5TcXVhcmUgPSBbMC4yNTEwNTEyNSwgMS4yNDIyMzFdXG4gICAgLy8gYWNjdW11bGF0ZWRNb21lbnRzID0gWzAuNDQ3MjEsIDAuNDQ3MjFdXG4gICAgLy8gbmV3QWNjdW11bGF0ZWRNb21lbnRzID0gWzAuMjY1MzQsIDAuMzIzMzZdXG4gICAgLy8geCA9IFswLjI4NzQ1LCAxLjIyOTQzXVxuXG4gICAgLy8gVE9ETzogRml4IG51bWVyaWNhbCBwcmVjaXNpb24uXG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgeC5kYXRhKCksIFswLjI4NzQ1LCAxLjIyMjk0M10sIDFlLTIpO1xuXG4gICAgLy8gVGhlcmUgc2hvdWxkIGJlIG5vIG5ldyBhZGRpdGlvbmFsIFRlbnNvcnMuXG4gICAgZXhwZWN0KHRmLm1lbW9yeSgpLm51bVRlbnNvcnMpLnRvQmUobnVtVGVuc29ycyk7XG5cbiAgICBleHBlY3QoY29zdCkudG9CZShudWxsKTtcblxuICAgIHguZGlzcG9zZSgpO1xuICAgIG9wdGltaXplci5kaXNwb3NlKCk7XG4gICAgLy8gVGhlIG9ubHkgYWRkaXRpb25hbCB0ZW5zb3IgcmVtYWluaW5nIGlzIHRoZSBhcmd1bWVudCB0byB2YXJpYWJsZSgpLlxuICAgIGV4cGVjdCh0Zi5tZW1vcnkoKS5udW1UZW5zb3JzKS50b0JlKGluaXRpYWxUZW5zb3JzICsgMSk7XG4gIH0pO1xuXG4gIGl0KCdncmFkaWVudCB3aXRoIGNlbnRlcmVkIG1vbWVudHVtJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGluaXRpYWxUZW5zb3JzID0gdGYubWVtb3J5KCkubnVtVGVuc29ycztcbiAgICBjb25zdCBsZWFybmluZ1JhdGUgPSAwLjE7XG4gICAgY29uc3QgbW9tZW50ID0gMC4xO1xuICAgIGNvbnN0IHJobyA9IDAuOTU7XG4gICAgY29uc3QgZXBzID0gMWUtODtcbiAgICBjb25zdCBvcHRpbWl6ZXIgPSB0Zi50cmFpbi5ybXNwcm9wKGxlYXJuaW5nUmF0ZSwgcmhvLCBtb21lbnQsIGVwcywgdHJ1ZSk7XG5cbiAgICBjb25zdCB4ID0gdGYudGVuc29yMWQoWzEsIDJdKS52YXJpYWJsZSgpO1xuXG4gICAgY29uc3QgZiA9ICgpID0+IHguc3F1YXJlKCkuc3VtKCk7XG5cbiAgICBsZXQgbnVtVGVuc29ycyA9IHRmLm1lbW9yeSgpLm51bVRlbnNvcnM7XG5cbiAgICBsZXQgY29zdCA9IG9wdGltaXplci5taW5pbWl6ZShmIGFzICgpID0+IHRmLlNjYWxhciwgLyogcmV0dXJuQ29zdCAqLyB0cnVlKTtcblxuICAgIC8vIENvc3QgJiAzIGFjY3VtdWxhdG9ycyBzaG91bGQgYmUgdGhlIG9ubHkgYWRkaXRpb25hbCBhcnJheXMuXG4gICAgZXhwZWN0KHRmLm1lbW9yeSgpLm51bVRlbnNvcnMpLnRvQmUobnVtVGVuc29ycyArIDQpO1xuXG4gICAgLy8gZXBzaWxvbiA9IDFlLThcbiAgICAvLyBuZXdBY2N1bXVsYXRlZE1lYW5TcXVhcmUgPVxuICAgIC8vICAgICAgICAgIHJobyAqIGFjY3VtdWxhdGVkTWVhblNxdWFyZSArICgxIC0gcmhvKSAqIGdyYWQgXiAyID0gWy4yLCAuOF1cbiAgICAvLyBuZXdBY2N1bXVsYXRlZE1lYW5HcmFkID1cbiAgICAvLyAgICAgICAgICByaG8gKiBhY2N1bXVsYXRlZE1lYW5HcmFkICsgKDEgLSByaG8pICogZ3JhZCA9IFswLjEsIDAuMl1cbiAgICAvLyBuZXdBY2N1bXVsYXRlZE1vbWVudHMgPSBtb21lbnR1bSAqIGFjY3VtdWxhdGVkTW9tZW50cyArXG4gICAgLy8gICAgICAgICAgbGVhcm5pbmdfcmF0ZSAqIGdyYWRpZW50IC8gc3FydChuZXdBY2N1bXVsYXRlZE1lYW5TcXVhcmVcbiAgICAvLyAgICAgICAgICAgIC0gbmV3QWNjdW11bGF0ZWRNZWFuR3JhZCAqIDIgK1xuICAgIC8vICAgICAgICAgICAgICBlcHNpbG9uKSA9IDAuMSAqIDAgKyAoKDAuMSAqIDIpXG4gICAgLy8gICAgICAgICAgICAgICAgLyBzcXJ0KDAuMiAtIDAuMDEgKyAxZS04KSkgPSAwLjQ1ODgzMVxuICAgIC8vIHggLT0gbGVhcm5pbmdSYXRlICogbmV3QWNjdW11bGF0ZWRNb21lbnRzXG4gICAgLy9cbiAgICAvLyBkZS9keCA9IFsyLCA0XVxuICAgIC8vIGFjY3VtdWxhdGVkTWVhblNxdWFyZSA9IFswLCAwXVxuICAgIC8vIG5ld0FjY3VtdWxhdGVkTWVhblNxdWFyZSA9IFsuMiwgLjhdXG4gICAgLy8gbmV3QWNjdW11bGF0ZWRNZWFuR3JhZCA9IFsuMSwgLjJdXG4gICAgLy8gYWNjdW11bGF0ZWRNb21lbnRzID0gWzAsIDBdXG4gICAgLy8gbmV3QWNjdW11bGF0ZWRNb21lbnRzID0gWzAuNDU4ODMsIDAuNDU4ODMxXVxuICAgIC8vIHggPSBbMC41NDExNywgMS41NDExNjldXG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgeC5kYXRhKCksIFswLjU0MTE3LCAxLjU0MTE2OV0pO1xuXG4gICAgY29zdC5kaXNwb3NlKCk7XG4gICAgbnVtVGVuc29ycyA9IHRmLm1lbW9yeSgpLm51bVRlbnNvcnM7XG5cbiAgICBjb3N0ID0gb3B0aW1pemVyLm1pbmltaXplKGYgYXMgKCkgPT4gdGYuU2NhbGFyLCAvKiByZXR1cm5Db3N0ICovIGZhbHNlKTtcblxuICAgIC8vIHggPSBbMC41NDExNywgMS41NDExNjldXG4gICAgLy8gZGUvZHggPSBbMS4wODIzNCwgMy4wODIzMzhdXG4gICAgLy8gYWNjdW11bGF0ZWRNZWFuU3F1YXJlID0gWzAuMiwgMC44XVxuICAgIC8vIGFjY3VtdWxhdGVkTWVhbkdyYWQgPSBbLjEsIC4yXVxuICAgIC8vIG5ld0FjY3VtdWxhdGVkTWVhblNxdWFyZSA9IFswLjI0ODU3MiwgMS4yMzUwNDBdXG4gICAgLy8gbmV3QWNjdW11bGF0ZWRNZWFuR3JhZCA9IFswLjE0OTExNywgMC4zNDQxMTY5XVxuICAgIC8vIGFjY3VtdWxhdGVkTW9tZW50cyA9IFswLjQ1ODgzLCAwLjQ1ODgzMV1cbiAgICAvLyBuZXdBY2N1bXVsYXRlZE1vbWVudHMgPSBbMC4yNzMzODUsIDAuMzM3NTc2Nl1cbiAgICAvLyB4ID0gWzAuMjY3Nzg1LCAxLjIwMzU5MjRdXG5cbiAgICAvLyBUT0RPOiBGaXggbnVtZXJpY2FsIHByZWNpc2lvbi5cbiAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCB4LmRhdGEoKSwgWzAuMjY3Nzg1LCAxLjIwMzU5MjRdLCAxZS0yKTtcblxuICAgIC8vIFRoZXJlIHNob3VsZCBiZSBubyBuZXcgYWRkaXRpb25hbCBUZW5zb3JzLlxuICAgIGV4cGVjdCh0Zi5tZW1vcnkoKS5udW1UZW5zb3JzKS50b0JlKG51bVRlbnNvcnMpO1xuXG4gICAgZXhwZWN0KGNvc3QpLnRvQmUobnVsbCk7XG5cbiAgICB4LmRpc3Bvc2UoKTtcbiAgICBvcHRpbWl6ZXIuZGlzcG9zZSgpO1xuICAgIC8vIFRoZSBvbmx5IGFkZGl0aW9uYWwgdGVuc29yIHJlbWFpbmluZyBpcyB0aGUgYXJndW1lbnQgdG8gdmFyaWFibGUoKS5cbiAgICBleHBlY3QodGYubWVtb3J5KCkubnVtVGVuc29ycykudG9CZShpbml0aWFsVGVuc29ycyArIDEpO1xuICB9KTtcblxuICBpdCgnU2F2ZSBhbmQgbG9hZCB3ZWlnaHRzOiBjZW50ZXJlZCA9IGZhbHNlJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGxlYXJuaW5nUmF0ZSA9IDAuMTtcbiAgICBjb25zdCBtb21lbnQgPSAwLjE7XG4gICAgY29uc3QgcmhvID0gMC45NTtcbiAgICBjb25zdCBvcHRpbWl6ZXIxID0gdGYudHJhaW4ucm1zcHJvcChsZWFybmluZ1JhdGUsIHJobywgbW9tZW50KTtcblxuICAgIGNvbnN0IHggPSB0Zi50ZW5zb3IxZChbMSwgMl0pLnZhcmlhYmxlKCk7XG4gICAgY29uc3QgZjogKCkgPT4gdGYuU2NhbGFyID0gKCkgPT4geC5zcXVhcmUoKS5zdW0oKTtcblxuICAgIGxldCBjb3N0ID0gb3B0aW1pemVyMS5taW5pbWl6ZShmLCAvKiByZXR1cm5Db3N0ICovIHRydWUpO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IGNvc3QuZGF0YSgpLCA1KTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCB4LmRhdGEoKSwgWzAuNTUyNzg2NSwgMS41NTI3ODY0XSk7XG5cbiAgICBjb25zdCB3ZWlnaHRzID0gYXdhaXQgb3B0aW1pemVyMS5nZXRXZWlnaHRzKCk7XG4gICAgLy8gQW4gaXRlcmF0aW9uIHZhcmlhYmxlIGFuZCB0d28gb3B0aW1pemVyIHN0YXRlIHZhcmlhYmxlcy5cbiAgICBleHBlY3Qod2VpZ2h0cy5sZW5ndGgpLnRvRXF1YWwoMyk7XG5cbiAgICBjb25zdCBvcHRpbWl6ZXIyID0gdGYudHJhaW4ucm1zcHJvcChsZWFybmluZ1JhdGUsIHJobywgbW9tZW50KTtcbiAgICBhd2FpdCBvcHRpbWl6ZXIyLnNldFdlaWdodHMod2VpZ2h0cyk7XG5cbiAgICBjb3N0ID0gb3B0aW1pemVyMi5taW5pbWl6ZShmLCAvKiByZXR1cm5Db3N0ICovIHRydWUpO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IGNvc3QuZGF0YSgpLCAyLjcxNjcxODcpO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IHguZGF0YSgpLCBbMC4yODc0NDE4LCAxLjIyOTQyNjddKTtcbiAgICBleHBlY3Qob3B0aW1pemVyMi5pdGVyYXRpb25zKS50b0VxdWFsKDIpO1xuICB9KTtcblxuICBpdCgnU2F2ZSwgbG9hZCB3ZWlnaHRzIGFuZCBjb250aW51ZSB0cmFpbmluZzogY2VudGVyZWQgPSB0cnVlJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGxlYXJuaW5nUmF0ZSA9IDAuMTtcbiAgICBjb25zdCBtb21lbnQgPSAwLjE7XG4gICAgY29uc3QgcmhvID0gMC45NTtcbiAgICBjb25zdCBlcHNpbG9uOiBudW1iZXIgPSB1bmRlZmluZWQ7XG4gICAgY29uc3QgY2VudGVyZWQgPSB0cnVlO1xuICAgIGNvbnN0IG9wdGltaXplcjEgPVxuICAgICAgICB0Zi50cmFpbi5ybXNwcm9wKGxlYXJuaW5nUmF0ZSwgcmhvLCBtb21lbnQsIGVwc2lsb24sIGNlbnRlcmVkKTtcblxuICAgIGNvbnN0IHggPSB0Zi50ZW5zb3IxZChbMSwgMl0pLnZhcmlhYmxlKCk7XG4gICAgY29uc3QgZiA9ICgpID0+IHguc3F1YXJlKCkuc3VtKCk7XG5cbiAgICBsZXQgY29zdCA9IG9wdGltaXplcjEubWluaW1pemUoZiBhcyAoKSA9PiB0Zi5TY2FsYXIsIC8qIHJldHVybkNvc3QgKi8gdHJ1ZSk7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgY29zdC5kYXRhKCksIDUpO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IHguZGF0YSgpLCBbMC41NDExNjg0LCAxLjU0MTE2ODVdKTtcblxuICAgIGNvbnN0IHdlaWdodHMgPSBhd2FpdCBvcHRpbWl6ZXIxLmdldFdlaWdodHMoKTtcbiAgICAvLyBBbiBpdGVyYXRpb24gdmFyaWFibGUgYW5kIHRocmVlIG9wdGltaXplciBzdGF0ZSB2YXJpYWJsZXMuXG4gICAgZXhwZWN0KHdlaWdodHMubGVuZ3RoKS50b0VxdWFsKDQpO1xuXG4gICAgY29uc3Qgb3B0aW1pemVyMiA9XG4gICAgICAgIHRmLnRyYWluLnJtc3Byb3AobGVhcm5pbmdSYXRlLCByaG8sIG1vbWVudCwgZXBzaWxvbiwgY2VudGVyZWQpO1xuICAgIGF3YWl0IG9wdGltaXplcjIuc2V0V2VpZ2h0cyh3ZWlnaHRzKTtcblxuICAgIGNvc3QgPSBvcHRpbWl6ZXIyLm1pbmltaXplKGYgYXMgKCkgPT4gdGYuU2NhbGFyLCAvKiByZXR1cm5Db3N0ICovIHRydWUpO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKGF3YWl0IGNvc3QuZGF0YSgpLCAyLjY2ODA2Myk7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgeC5kYXRhKCksIFswLjI2Nzc4MzQsIDEuMjAzNTkxOF0pO1xuICAgIGV4cGVjdChvcHRpbWl6ZXIyLml0ZXJhdGlvbnMpLnRvRXF1YWwoMik7XG5cbiAgICBjb25zdCBvcHRpbWl6ZXIzID1cbiAgICAgICAgdGYudHJhaW4ucm1zcHJvcChsZWFybmluZ1JhdGUsIHJobywgbW9tZW50LCBlcHNpbG9uLCBjZW50ZXJlZCk7XG4gICAgYXdhaXQgb3B0aW1pemVyMy5zZXRXZWlnaHRzKGF3YWl0IG9wdGltaXplcjIuZ2V0V2VpZ2h0cygpKTtcbiAgICBjb3N0ID0gb3B0aW1pemVyMy5taW5pbWl6ZShmIGFzICgpID0+IHRmLlNjYWxhciwgLyogcmV0dXJuQ29zdCAqLyB0cnVlKTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShhd2FpdCBjb3N0LmRhdGEoKSwgMS41MjAzNDEpO1xuICAgIGV4cGVjdChvcHRpbWl6ZXIzLml0ZXJhdGlvbnMpLnRvRXF1YWwoMyk7XG4gIH0pO1xuXG4gIGl0KCdzZXJpYWxpemF0aW9uIHJvdW5kLXRyaXAnLCAoKSA9PiB7XG4gICAgY29uc3Qgb3JpZ2luYWxPcHQgPSB0Zi50cmFpbi5ybXNwcm9wKDAuMSwgMC41LCAwLjEsIDFlLTcsIHRydWUpO1xuICAgIGNvbnN0IHJlc2VyaWFsaXplZCA9IHRmLlJNU1Byb3BPcHRpbWl6ZXIuZnJvbUNvbmZpZyhcbiAgICAgICAgdGYuUk1TUHJvcE9wdGltaXplciwgb3JpZ2luYWxPcHQuZ2V0Q29uZmlnKCkpO1xuICAgIGV4cGVjdChyZXNlcmlhbGl6ZWQuZ2V0Q29uZmlnKCkpLnRvRXF1YWwob3JpZ2luYWxPcHQuZ2V0Q29uZmlnKCkpO1xuICB9KTtcblxuICBpdCgnbXVzdCBkZWZpbmUgbGVhcm5pbmcgcmF0ZScsICgpID0+IHtcbiAgICBjb25zdCBsZWFybmluZ1JhdGU6IG51bWJlciA9IHVuZGVmaW5lZDtcbiAgICBleHBlY3QoKCkgPT4gdGYudHJhaW4ucm1zcHJvcChsZWFybmluZ1JhdGUpKVxuICAgICAgICAudG9UaHJvd0Vycm9yKC9sZWFybmluZ1JhdGUgZm9yIFJNU1Byb3BPcHRpbWl6ZXIgbXVzdCBiZSBkZWZpbmVkLi8pO1xuICB9KTtcbn0pO1xuIl19
|