@tensorflow/tfjs-layers
Version:
TensorFlow layers API in JavaScript
401 lines • 69.4 kB
JavaScript
/**
* @license
* Copyright 2018 Google LLC
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
* =============================================================================
*/
/**
* Normalization layers.
*/
import * as tfc from '@tensorflow/tfjs-core';
import { moments, reshape, serialization, tidy, util } from '@tensorflow/tfjs-core';
import { getConstraint, serializeConstraint } from '../constraints';
import { InputSpec, Layer } from '../engine/topology';
import { NotImplementedError, ValueError } from '../errors';
import { getInitializer, serializeInitializer } from '../initializers';
import { getRegularizer, serializeRegularizer } from '../regularizers';
import * as generic_utils from '../utils/generic_utils';
import * as math_utils from '../utils/math_utils';
import { getExactlyOneShape, getExactlyOneTensor } from '../utils/types_utils';
/**
* Applies batch normalization on x given mean, var, beta and gamma.
*
* I.e. returns:
* `output = (x - mean) / (sqrt(var) + epsilon) * gamma + beta`
*
* @param x Input tensor.
* @param mean Mean of batch.
* @param variance Variance of batch.
* @param beta Tensor with which to center the input.
* @param gamma Tensor by which to scale the input.
* @param epsilon Fuzz factor.
* @returns The result of the batch normalization.
*/
export function batchNormalization(x, mean, variance, beta, gamma, epsilon = 1e-3) {
let out;
if (x.rank === 2) {
out = tfc.batchNorm2d(x, mean, variance, beta, gamma, epsilon);
}
else if (x.rank === 3) {
// TODO(cais): Check rank; give proper error message.
out = tfc.batchNorm3d(x, mean, variance, beta, gamma, epsilon);
}
else if (x.rank === 4) {
out = tfc.batchNorm4d(x, mean, variance, beta, gamma, epsilon);
}
else {
throw new NotImplementedError(`batchNormalization is not implemented for array of rank ${x.rank} ` +
`yet`);
}
return out;
}
/**
* Non-broadcasting batch normalization for use in training (not inference).
*
* The input is normalized to zero mean and unit variance along the
* `reductionAxes`, followed by scaling with `gamma` and shifted by `beta`.
* The result of that is returned as the first element
* of the returned `Array`. The other two elements are the mean and variance,
* respectively.
*
* @param x Input tensor to be normalized.
* @param gamma Tensor by which to scale the input.
* @param beta Tensor by which to center the input.
* @param reductionAxes Axes over which to normalize.
* @param epsilon Fuzz factor.
* @returns An `Array` of three `Tensors`:
* [normalized tensor, mean of input, variance of input].
*/
function regularNormalizeBatchInTraining(x, gamma, beta, reductionAxes, epsilon = 1e-3) {
return tidy(() => {
const meanAndVariance = tfc.moments(x, reductionAxes);
const mean = meanAndVariance.mean;
const variance = meanAndVariance.variance;
const normed = batchNormalization(x, mean, variance, beta, gamma, epsilon);
return [normed, mean, variance];
});
}
/**
* Broadcasting batch normalization for use in training (not inference).
*
* The input is normalized to zero mean and unit variance along the
* `reductionAxes`, followed by scaling with `gamma` and shifted by `beta`.
* The result of that is returned as the first element
* of the returned `Array`. The other two elements are the mean and variance,
* respectively.
*
* @param x Input tensor to be normalized.
* @param gamma Tensor by which to scale the input.
* @param beta Tensor by which to center the input.
* @param reductionAxes Axes over which to normalize.
* @param epsilon Fuzz factor.
* @returns An `Array` of three `Tensors`:
* [normalized tensor, mean of input, variance of input].
*/
function broadcastNormalizeBatchInTraining(x, gamma, beta, reductionAxes, epsilon = 1e-3) {
return tidy(() => {
const meanAndVariance = tfc.moments(x, reductionAxes);
const mean = meanAndVariance.mean;
const variance = meanAndVariance.variance;
const targetShape = [];
for (const axis of math_utils.range(0, x.rank)) {
if (reductionAxes.indexOf(axis) !== -1) {
targetShape.push(1);
}
else {
targetShape.push(x.shape[axis]);
}
}
const broadcastMean = reshape(mean, targetShape);
const broadcastVariance = reshape(variance, targetShape);
const broadcastGamma = gamma == null ? null : reshape(gamma, targetShape);
const broadcastBeta = beta == null ? null : reshape(beta, targetShape);
const normed = batchNormalization(x, broadcastMean, broadcastVariance, broadcastBeta, broadcastGamma, epsilon);
return [normed, mean, variance];
});
}
/**
* Batch normalization for use in training (not inference).
*
* @param x Input tensor to be normalized.
* @param gamma Tensor by which to scale the input.
* @param beta Tensor by which to center the input.
* @param reductionAxes Axes over which to normalize.
* @param epsilon Fuzz factor.
* @returns An `Array` of three `Tensors`:
* [normalized tensor, mean of input, variance of input].
*/
export function normalizeBatchInTraining(x, gamma, beta, reductionAxes, epsilon = 1e-3) {
if (util.arraysEqual(reductionAxes.slice().sort(), math_utils.range(0, x.rank - 1))) {
return regularNormalizeBatchInTraining(x, gamma, beta, reductionAxes, epsilon);
}
else {
return broadcastNormalizeBatchInTraining(x, gamma, beta, reductionAxes, epsilon);
}
}
class BatchNormalization extends Layer {
constructor(args) {
if (args == null) {
args = {};
}
super(args);
this.supportsMasking = true;
this.axis = args.axis == null ? -1 : args.axis;
this.momentum = args.momentum == null ? 0.99 : args.momentum;
this.epsilon = args.epsilon == null ? 1e-3 : args.epsilon;
this.center = args.center == null ? true : args.center;
this.scale = args.scale == null ? true : args.scale;
this.betaInitializer = getInitializer(args.betaInitializer || 'zeros');
this.gammaInitializer = getInitializer(args.gammaInitializer || 'ones');
this.movingMeanInitializer =
getInitializer(args.movingMeanInitializer || 'zeros');
this.movingVarianceInitializer =
getInitializer(args.movingVarianceInitializer || 'ones');
this.betaConstraint = getConstraint(args.betaConstraint);
this.gammaConstraint = getConstraint(args.gammaConstraint);
this.betaRegularizer = getRegularizer(args.betaRegularizer);
this.gammaRegularizer = getRegularizer(args.gammaRegularizer);
}
build(inputShape) {
inputShape = getExactlyOneShape(inputShape);
const axis = this.axis >= 0 ? this.axis : (this.axis + inputShape.length);
const dim = inputShape[axis];
if (dim == null) {
throw new ValueError(`Axis ${axis} of input tensor should have a defined dimension but ` +
`the layer received an input with shape ` +
`${JSON.stringify(inputShape)}.`);
}
this.inputSpec =
[new InputSpec({ ndim: inputShape.length, axes: { [axis]: dim } })];
const shape = [dim];
if (this.scale) {
this.gamma = this.addWeight('gamma', shape, null, this.gammaInitializer, this.gammaRegularizer, true, this.gammaConstraint);
}
if (this.center) {
this.beta = this.addWeight('beta', shape, null, this.betaInitializer, this.betaRegularizer, true, this.betaConstraint);
}
this.movingMean = this.addWeight('moving_mean', shape, null, this.movingMeanInitializer, null, false);
this.movingVariance = this.addWeight('moving_variance', shape, null, this.movingVarianceInitializer, null, false);
this.built = true;
}
call(inputs, kwargs) {
return tidy(() => {
const training = kwargs['training'] == null ? false : kwargs['training'];
const input = getExactlyOneTensor(inputs);
const inputShape = input.shape;
const ndim = inputShape.length;
const reductionAxes = math_utils.range(0, ndim);
const axis = this.axis >= 0 ? this.axis : (this.axis + ndim);
reductionAxes.splice(axis, 1);
const broadcastShape = generic_utils.pyListRepeat(1, ndim);
broadcastShape[axis] = inputShape[axis];
const sortedReductionAxes = reductionAxes.slice();
sortedReductionAxes.sort();
const needsBroadcasting = !util.arraysEqual(sortedReductionAxes, math_utils.range(0, ndim).slice(0, ndim - 1));
const normalizeInference = () => {
if (needsBroadcasting) {
const broadcastMovingMean = reshape(this.movingMean.read(), broadcastShape);
const broadcastMovingVariance = reshape(this.movingVariance.read(), broadcastShape);
const broadcastBeta = this.center ? reshape(this.beta.read(), broadcastShape) : null;
const broadcastGamma = this.scale ? reshape(this.gamma.read(), broadcastShape) : null;
return batchNormalization(input, broadcastMovingMean, broadcastMovingVariance, broadcastBeta, broadcastGamma, this.epsilon);
}
else {
return batchNormalization(input, this.movingMean.read(), this.movingVariance.read(), this.beta == null ? null : this.beta.read(), this.gamma == null ? null : this.gamma.read(), this.epsilon);
}
};
if (!training) {
return normalizeInference();
}
const [normedTraining, mean, variance] = normalizeBatchInTraining(input, this.gamma.read(), this.beta.read(), reductionAxes, this.epsilon);
const doMovingAverage = (variable, value, momentum) => {
tfc.tidy(() => {
const decay = 1 - momentum;
const origValue = variable.read();
const updateDelta = tfc.mul(tfc.sub(origValue, value), decay);
variable.write(tfc.sub(origValue, updateDelta));
});
};
// Perform updates to moving mean and moving variance for training.
// Porting Note: In PyKeras, these updates to `movingMean` and
// `movingAverage` are done as a deferred Graph, added to the `Layer`'s
// `update`s using the `add_update()` method. Here we do it imperatively
// and encapsulate the updates in a function that is invoked
// immediately.
const updateMovingMeanAndVariance = () => {
doMovingAverage(this.movingMean, mean, this.momentum);
doMovingAverage(this.movingVariance, variance, this.momentum);
};
updateMovingMeanAndVariance();
return normedTraining;
});
}
getConfig() {
const config = {
axis: this.axis,
momentum: this.momentum,
epsilon: this.epsilon,
center: this.center,
scale: this.scale,
betaInitializer: serializeInitializer(this.betaInitializer),
gammaInitializer: serializeInitializer(this.gammaInitializer),
movingMeanInitializer: serializeInitializer(this.movingMeanInitializer),
movingVarianceInitializer: serializeInitializer(this.movingVarianceInitializer),
betaRegularizer: serializeRegularizer(this.betaRegularizer),
gammaRegularizer: serializeRegularizer(this.gammaRegularizer),
betaConstraint: serializeConstraint(this.betaConstraint),
gammaConstraint: serializeConstraint(this.gammaConstraint)
};
const baseConfig = super.getConfig();
Object.assign(config, baseConfig);
return config;
}
}
/** @nocollapse */
BatchNormalization.className = 'BatchNormalization';
export { BatchNormalization };
serialization.registerClass(BatchNormalization);
class LayerNormalization extends Layer {
constructor(args) {
if (args == null) {
args = {};
}
super(args);
this.axis = args.axis == null ? -1 : args.axis;
if (typeof this.axis === 'number') {
if (!Number.isInteger(this.axis)) {
throw new Error(`Expected axis to be an integer, but received ${this.axis}`);
}
}
else if (Array.isArray(this.axis)) {
for (const axis of this.axis) {
if (!Number.isInteger(axis)) {
throw new Error(`Expected axis to be an array of integers, ` +
`but received ${JSON.stringify(this.axis)}`);
}
}
}
else {
throw new Error(`Expected axis to be an integer or an array of integers, ` +
`but received ${JSON.stringify(this.axis)}`);
}
this.epsilon = args.epsilon == null ? 1e-3 : args.epsilon;
this.center = args.center == null ? true : args.center;
this.scale = args.scale == null ? true : args.scale;
this.betaInitializer = getInitializer(args.betaInitializer || 'zeros');
this.gammaInitializer = getInitializer(args.gammaInitializer || 'ones');
this.betaRegularizer = getRegularizer(args.betaRegularizer);
this.gammaRegularizer = getRegularizer(args.gammaRegularizer);
this.supportsMasking = true;
}
build(inputShape) {
inputShape = getExactlyOneShape(inputShape);
const nDims = inputShape.length;
// Convert axis to array and resolve negatives.
if (typeof this.axis === 'number') {
this.axis = [this.axis];
}
for (let i = 0; i < this.axis.length; ++i) {
if (this.axis[i] < 0) {
this.axis[i] += nDims;
}
}
// Further validate axes.
for (const axis of this.axis) {
if (axis < 0 || axis >= nDims) {
throw new Error(`Invalid axis: ${axis}`);
}
}
if (this.axis.length !== generic_utils.unique(this.axis).length) {
throw new Error(`Found duplicate axes in: ${this.axis}`);
}
const paramShape = this.axis.map(axis => inputShape[axis]);
const trainable = true;
if (this.scale) {
this.gamma = this.addWeight('gamma', paramShape, 'float32', this.gammaInitializer, this.gammaRegularizer, trainable);
}
else {
this.gamma = null;
}
if (this.center) {
this.beta = this.addWeight('beta', paramShape, 'float32', this.betaInitializer, this.betaRegularizer, trainable);
}
else {
this.beta = null;
}
this.built = true;
}
call(inputs, kwargs) {
const input = getExactlyOneTensor(inputs);
const inputShape = input.shape;
const nDims = inputShape.length;
return tidy(() => {
const keepDims = true;
let { mean, variance } = moments(input, this.axis, keepDims);
const broadcastShape = generic_utils.pyListRepeat(1, nDims);
for (const dim of this.axis) {
broadcastShape[dim] = inputShape[dim];
}
const broadcast = (v) => {
if (v != null && v.shape.length !== nDims) {
return tfc.reshape(v, broadcastShape);
}
else {
return v;
}
};
let scale = this.scale ? broadcast(this.gamma.read()) : null;
let offset = this.center ? broadcast(this.beta.read()) : null;
// TODO(https://github.com/tensorflow/tfjs/issues/2120): The tiling below
// is a workaround for the limitation of core's batchNormalization?d don't
// support broadcasting in their gradients. In addition, the tiling is
// necessary to ensure correctness on the browser CPU backend regardless
// of forward or backward computation. Remove this workaround once the
// limitation is addressed. See .
const momentsTiling = [];
const scaleOffsetTiling = [];
for (let i = 0; i < nDims; ++i) {
if (this.axis.indexOf(i) !== -1) {
momentsTiling.push(inputShape[i]);
scaleOffsetTiling.push(1);
}
else {
momentsTiling.push(1);
scaleOffsetTiling.push(inputShape[i]);
}
}
mean = tfc.tile(mean, momentsTiling);
variance = tfc.tile(variance, momentsTiling);
if (scale != null) {
scale = tfc.tile(scale, scaleOffsetTiling);
}
if (offset != null) {
offset = tfc.tile(offset, scaleOffsetTiling);
}
return batchNormalization(input, mean, variance, offset, scale, this.epsilon);
});
}
getConfig() {
const config = {
axis: this.axis,
epsilon: this.epsilon,
center: this.center,
scale: this.scale,
betaInitializer: serializeInitializer(this.betaInitializer),
gammaInitializer: serializeInitializer(this.gammaInitializer),
betaRegularizer: serializeRegularizer(this.betaRegularizer),
gammaRegularizer: serializeRegularizer(this.gammaRegularizer)
};
const baseConfig = super.getConfig();
Object.assign(config, baseConfig);
return config;
}
}
/** @nocollapse */
LayerNormalization.className = 'LayerNormalization';
export { LayerNormalization };
serialization.registerClass(LayerNormalization);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9ybWFsaXphdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3RmanMtbGF5ZXJzL3NyYy9sYXllcnMvbm9ybWFsaXphdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7R0FRRztBQUVIOztHQUVHO0FBRUgsT0FBTyxLQUFLLEdBQUcsTUFBTSx1QkFBdUIsQ0FBQztBQUM3QyxPQUFPLEVBQUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxhQUFhLEVBQWtELElBQUksRUFBRSxJQUFJLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUVsSSxPQUFPLEVBQW1DLGFBQWEsRUFBRSxtQkFBbUIsRUFBQyxNQUFNLGdCQUFnQixDQUFDO0FBQ3BHLE9BQU8sRUFBQyxTQUFTLEVBQUUsS0FBSyxFQUFZLE1BQU0sb0JBQW9CLENBQUM7QUFDL0QsT0FBTyxFQUFDLG1CQUFtQixFQUFFLFVBQVUsRUFBQyxNQUFNLFdBQVcsQ0FBQztBQUMxRCxPQUFPLEVBQUMsY0FBYyxFQUFzQyxvQkFBb0IsRUFBQyxNQUFNLGlCQUFpQixDQUFDO0FBRXpHLE9BQU8sRUFBQyxjQUFjLEVBQXNDLG9CQUFvQixFQUFDLE1BQU0saUJBQWlCLENBQUM7QUFFekcsT0FBTyxLQUFLLGFBQWEsTUFBTSx3QkFBd0IsQ0FBQztBQUN4RCxPQUFPLEtBQUssVUFBVSxNQUFNLHFCQUFxQixDQUFDO0FBQ2xELE9BQU8sRUFBQyxrQkFBa0IsRUFBRSxtQkFBbUIsRUFBQyxNQUFNLHNCQUFzQixDQUFDO0FBRzdFOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSCxNQUFNLFVBQVUsa0JBQWtCLENBQzlCLENBQVMsRUFBRSxJQUFZLEVBQUUsUUFBZ0IsRUFBRSxJQUFhLEVBQUUsS0FBYyxFQUN4RSxPQUFPLEdBQUcsSUFBSTtJQUNoQixJQUFJLEdBQVcsQ0FBQztJQUNoQixJQUFJLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUFFO1FBQ2hCLEdBQUcsR0FBRyxHQUFHLENBQUMsV0FBVyxDQUNqQixDQUFhLEVBQUUsSUFBMkIsRUFDMUMsUUFBK0IsRUFBRSxJQUEyQixFQUM1RCxLQUE0QixFQUFFLE9BQU8sQ0FBQyxDQUFDO0tBQzVDO1NBQU0sSUFBSSxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsRUFBRTtRQUN2QixxREFBcUQ7UUFDckQsR0FBRyxHQUFHLEdBQUcsQ0FBQyxXQUFXLENBQ2pCLENBQWEsRUFBRSxJQUEyQixFQUMxQyxRQUErQixFQUFFLElBQTJCLEVBQzVELEtBQTRCLEVBQUUsT0FBTyxDQUFDLENBQUM7S0FDNUM7U0FBTSxJQUFJLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUFFO1FBQ3ZCLEdBQUcsR0FBRyxHQUFHLENBQUMsV0FBVyxDQUNqQixDQUFhLEVBQUUsSUFBMkIsRUFDMUMsUUFBK0IsRUFBRSxJQUEyQixFQUM1RCxLQUE0QixFQUFFLE9BQU8sQ0FBQyxDQUFDO0tBQzVDO1NBQU07UUFDTCxNQUFNLElBQUksbUJBQW1CLENBQ3pCLDJEQUEyRCxDQUFDLENBQUMsSUFBSSxHQUFHO1lBQ3BFLEtBQUssQ0FBQyxDQUFDO0tBQ1o7SUFDRCxPQUFPLEdBQUcsQ0FBQztBQUNiLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILFNBQVMsK0JBQStCLENBQ3BDLENBQVMsRUFBRSxLQUFhLEVBQUUsSUFBWSxFQUFFLGFBQXVCLEVBQy9ELE9BQU8sR0FBRyxJQUFJO0lBQ2hCLE9BQU8sSUFBSSxDQUFDLEdBQUcsRUFBRTtRQUNSLE1BQU0sZUFBZSxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxFQUFFLGFBQWEsQ0FBQyxDQUFDO1FBQ3RELE1BQU0sSUFBSSxHQUFHLGVBQWUsQ0FBQyxJQUFJLENBQUM7UUFDbEMsTUFBTSxRQUFRLEdBQUcsZUFBZSxDQUFDLFFBQVEsQ0FBQztRQUMxQyxNQUFNLE1BQU0sR0FDUixrQkFBa0IsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ2hFLE9BQU8sQ0FBQyxNQUFNLEVBQUUsSUFBSSxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQ2xDLENBQUMsQ0FBNkIsQ0FBQztBQUN4QyxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQkc7QUFDSCxTQUFTLGlDQUFpQyxDQUN0QyxDQUFTLEVBQUUsS0FBYSxFQUFFLElBQVksRUFBRSxhQUF1QixFQUMvRCxPQUFPLEdBQUcsSUFBSTtJQUNoQixPQUFPLElBQUksQ0FBQyxHQUFHLEVBQUU7UUFDUixNQUFNLGVBQWUsR0FBRyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUMsRUFBRSxhQUFhLENBQUMsQ0FBQztRQUN0RCxNQUFNLElBQUksR0FBRyxlQUFlLENBQUMsSUFBSSxDQUFDO1FBQ2xDLE1BQU0sUUFBUSxHQUFHLGVBQWUsQ0FBQyxRQUFRLENBQUM7UUFDMUMsTUFBTSxXQUFXLEdBQWEsRUFBRSxDQUFDO1FBQ2pDLEtBQUssTUFBTSxJQUFJLElBQUksVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFO1lBQzlDLElBQUksYUFBYSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRTtnQkFDdEMsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQzthQUNyQjtpQkFBTTtnQkFDTCxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQzthQUNqQztTQUNGO1FBQ0QsTUFBTSxhQUFhLEdBQUcsT0FBTyxDQUFDLElBQUksRUFBRSxXQUFXLENBQUMsQ0FBQztRQUNqRCxNQUFNLGlCQUFpQixHQUFHLE9BQU8sQ0FBQyxRQUFRLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDekQsTUFBTSxjQUFjLEdBQ2hCLEtBQUssSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxXQUFXLENBQUMsQ0FBQztRQUN2RCxNQUFNLGFBQWEsR0FDZixJQUFJLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDckQsTUFBTSxNQUFNLEdBQUcsa0JBQWtCLENBQzdCLENBQUMsRUFBRSxhQUFhLEVBQUUsaUJBQWlCLEVBQUUsYUFBYSxFQUNsRCxjQUFjLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDN0IsT0FBTyxDQUFDLE1BQU0sRUFBRSxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDbEMsQ0FBQyxDQUE2QixDQUFDO0FBQ3hDLENBQUM7QUFFRDs7Ozs7Ozs7OztHQVVHO0FBQ0gsTUFBTSxVQUFVLHdCQUF3QixDQUNwQyxDQUFTLEVBQUUsS0FBYSxFQUFFLElBQVksRUFBRSxhQUF1QixFQUMvRCxPQUFPLEdBQUcsSUFBSTtJQUNoQixJQUFJLElBQUksQ0FBQyxXQUFXLENBQ1osYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDLElBQUksRUFBRSxFQUFFLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRTtRQUN0RSxPQUFPLCtCQUErQixDQUNsQyxDQUFDLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxhQUFhLEVBQUUsT0FBTyxDQUFDLENBQUM7S0FDN0M7U0FBTTtRQUNMLE9BQU8saUNBQWlDLENBQ3BDLENBQUMsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLGFBQWEsRUFBRSxPQUFPLENBQUMsQ0FBQztLQUM3QztBQUNILENBQUM7QUFvRkQsTUFBYSxrQkFBbUIsU0FBUSxLQUFLO0lBcUIzQyxZQUFZLElBQWtDO1FBQzVDLElBQUksSUFBSSxJQUFJLElBQUksRUFBRTtZQUNoQixJQUFJLEdBQUcsRUFBRSxDQUFDO1NBQ1g7UUFDRCxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFFWixJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQztRQUM1QixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQztRQUMvQyxJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUM7UUFDN0QsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDO1FBQzFELElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLE1BQU0sSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUN2RCxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUM7UUFDcEQsSUFBSSxDQUFDLGVBQWUsR0FBRyxjQUFjLENBQUMsSUFBSSxDQUFDLGVBQWUsSUFBSSxPQUFPLENBQUMsQ0FBQztRQUN2RSxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsY0FBYyxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsSUFBSSxNQUFNLENBQUMsQ0FBQztRQUN4RSxJQUFJLENBQUMscUJBQXFCO1lBQ3RCLGNBQWMsQ0FBQyxJQUFJLENBQUMscUJBQXFCLElBQUksT0FBTyxDQUFDLENBQUM7UUFDMUQsSUFBSSxDQUFDLHlCQUF5QjtZQUMxQixjQUFjLENBQUMsSUFBSSxDQUFDLHlCQUF5QixJQUFJLE1BQU0sQ0FBQyxDQUFDO1FBQzdELElBQUksQ0FBQyxjQUFjLEdBQUcsYUFBYSxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUN6RCxJQUFJLENBQUMsZUFBZSxHQUFHLGFBQWEsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDM0QsSUFBSSxDQUFDLGVBQWUsR0FBRyxjQUFjLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxDQUFDO1FBQzVELElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxjQUFjLENBQUMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUM7SUFDaEUsQ0FBQztJQUVlLEtBQUssQ0FBQyxVQUF5QjtRQUM3QyxVQUFVLEdBQUcsa0JBQWtCLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDNUMsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksR0FBRyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDMUUsTUFBTSxHQUFHLEdBQUcsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzdCLElBQUksR0FBRyxJQUFJLElBQUksRUFBRTtZQUNmLE1BQU0sSUFBSSxVQUFVLENBQ2hCLFFBQVEsSUFBSSx1REFBdUQ7Z0JBQ25FLHlDQUF5QztnQkFDekMsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQztTQUN2QztRQUNELElBQUksQ0FBQyxTQUFTO1lBQ1YsQ0FBQyxJQUFJLFNBQVMsQ0FBQyxFQUFDLElBQUksRUFBRSxVQUFVLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxFQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsR0FBRyxFQUFDLEVBQUMsQ0FBQyxDQUFDLENBQUM7UUFDcEUsTUFBTSxLQUFLLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNwQixJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUU7WUFDZCxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQ3ZCLE9BQU8sRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxJQUFJLENBQUMsZ0JBQWdCLEVBQ2xFLElBQUksRUFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7U0FDakM7UUFDRCxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUU7WUFDZixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQ3RCLE1BQU0sRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxlQUFlLEVBQUUsSUFBSSxDQUFDLGVBQWUsRUFBRSxJQUFJLEVBQ3JFLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQztTQUMxQjtRQUNELElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FDNUIsYUFBYSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLHFCQUFxQixFQUFFLElBQUksRUFBRSxLQUFLLENBQUMsQ0FBQztRQUN6RSxJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQ2hDLGlCQUFpQixFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLHlCQUF5QixFQUFFLElBQUksRUFDcEUsS0FBSyxDQUFDLENBQUM7UUFDWCxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQztJQUNwQixDQUFDO0lBRVEsSUFBSSxDQUFDLE1BQXVCLEVBQUUsTUFBYztRQUNuRCxPQUFPLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDZixNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsVUFBVSxDQUFDLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUN6RSxNQUFNLEtBQUssR0FBRyxtQkFBbUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUMxQyxNQUFNLFVBQVUsR0FBRyxLQUFLLENBQUMsS0FBSyxDQUFDO1lBQy9CLE1BQU0sSUFBSSxHQUFHLFVBQVUsQ0FBQyxNQUFNLENBQUM7WUFDL0IsTUFBTSxhQUFhLEdBQUcsVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7WUFDaEQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsQ0FBQztZQUM3RCxhQUFhLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztZQUM5QixNQUFNLGNBQWMsR0FBRyxhQUFhLENBQUMsWUFBWSxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztZQUMzRCxjQUFjLENBQUMsSUFBSSxDQUFDLEdBQUcsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBRXhDLE1BQU0sbUJBQW1CLEdBQUcsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ2xELG1CQUFtQixDQUFDLElBQUksRUFBRSxDQUFDO1lBQzNCLE1BQU0saUJBQWlCLEdBQUcsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUN2QyxtQkFBbUIsRUFBRSxVQUFVLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLElBQUksR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBRXZFLE1BQU0sa0JBQWtCLEdBQWlCLEdBQUcsRUFBRTtnQkFDNUMsSUFBSSxpQkFBaUIsRUFBRTtvQkFDckIsTUFBTSxtQkFBbUIsR0FDckIsT0FBTyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxFQUFFLEVBQUUsY0FBYyxDQUFDLENBQUM7b0JBQ3BELE1BQU0sdUJBQXVCLEdBQ3pCLE9BQU8sQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLElBQUksRUFBRSxFQUFFLGNBQWMsQ0FBQyxDQUFDO29CQUN4RCxNQUFNLGFBQWEsR0FDZixJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsRUFBRSxjQUFjLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO29CQUNuRSxNQUFNLGNBQWMsR0FDaEIsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLEVBQUUsY0FBYyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztvQkFDbkUsT0FBTyxrQkFBa0IsQ0FDckIsS0FBSyxFQUFFLG1CQUFtQixFQUFFLHVCQUF1QixFQUNuRCxhQUFhLEVBQUUsY0FBYyxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztpQkFDbEQ7cUJBQU07b0JBQ0wsT0FBTyxrQkFBa0IsQ0FDckIsS0FBSyxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxFQUFFLEVBQUUsSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLEVBQUUsRUFDekQsSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsRUFDM0MsSUFBSSxDQUFDLEtBQUssSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7aUJBQ2xFO1lBQ0gsQ0FBQyxDQUFDO1lBRUYsSUFBSSxDQUFDLFFBQVEsRUFBRTtnQkFDYixPQUFPLGtCQUFrQixFQUFFLENBQUM7YUFDN0I7WUFFRCxNQUFNLENBQUMsY0FBYyxFQUFFLElBQUksRUFBRSxRQUFRLENBQUMsR0FBRyx3QkFBd0IsQ0FDN0QsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsRUFBRSxhQUFhLEVBQ3pELElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUVsQixNQUFNLGVBQWUsR0FDakIsQ0FBQyxRQUF1QixFQUFFLEtBQWEsRUFBRSxRQUFnQixFQUFRLEVBQUU7Z0JBQ2pFLEdBQUcsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFO29CQUNaLE1BQU0sS0FBSyxHQUFHLENBQUMsR0FBRyxRQUFRLENBQUM7b0JBQzNCLE1BQU0sU0FBUyxHQUFHLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztvQkFDbEMsTUFBTSxXQUFXLEdBQUcsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLFNBQVMsRUFBRSxLQUFLLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztvQkFDOUQsUUFBUSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLFNBQVMsRUFBRSxXQUFXLENBQUMsQ0FBQyxDQUFDO2dCQUNsRCxDQUFDLENBQUMsQ0FBQztZQUNMLENBQUMsQ0FBQztZQUVOLG1FQUFtRTtZQUNuRSw4REFBOEQ7WUFDOUQseUVBQXlFO1lBQ3pFLDBFQUEwRTtZQUMxRSw4REFBOEQ7WUFDOUQsaUJBQWlCO1lBQ2pCLE1BQU0sMkJBQTJCLEdBQUcsR0FBRyxFQUFFO2dCQUN2QyxlQUFlLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO2dCQUN0RCxlQUFlLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQ2hFLENBQUMsQ0FBQztZQUNGLDJCQUEyQixFQUFFLENBQUM7WUFFOUIsT0FBTyxjQUFjLENBQUM7UUFDeEIsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRVEsU0FBUztRQUNoQixNQUFNLE1BQU0sR0FBNkI7WUFDdkMsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJO1lBQ2YsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFRO1lBQ3ZCLE9BQU8sRUFBRSxJQUFJLENBQUMsT0FBTztZQUNyQixNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU07WUFDbkIsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLO1lBQ2pCLGVBQWUsRUFBRSxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDO1lBQzNELGdCQUFnQixFQUFFLG9CQUFvQixDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQztZQUM3RCxxQkFBcUIsRUFBRSxvQkFBb0IsQ0FBQyxJQUFJLENBQUMscUJBQXFCLENBQUM7WUFDdkUseUJBQXlCLEVBQ3JCLG9CQUFvQixDQUFDLElBQUksQ0FBQyx5QkFBeUIsQ0FBQztZQUN4RCxlQUFlLEVBQUUsb0JBQW9CLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQztZQUMzRCxnQkFBZ0IsRUFBRSxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUM7WUFDN0QsY0FBYyxFQUFFLG1CQUFtQixDQUFDLElBQUksQ0FBQyxjQUFjLENBQUM7WUFDeEQsZUFBZSxFQUFFLG1CQUFtQixDQUFDLElBQUksQ0FBQyxlQUFlLENBQUM7U0FDM0QsQ0FBQztRQUNGLE1BQU0sVUFBVSxHQUFHLEtBQUssQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUNyQyxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUMsQ0FBQztRQUNsQyxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDOztBQXZLRCxrQkFBa0I7QUFDWCw0QkFBUyxHQUFHLG9CQUFvQixDQUFDO1NBRjdCLGtCQUFrQjtBQTBLL0IsYUFBYSxDQUFDLGFBQWEsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO0FBa0RoRCxNQUFhLGtCQUFtQixTQUFRLEtBQUs7SUFnQjNDLFlBQVksSUFBa0M7UUFDNUMsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFO1lBQ2hCLElBQUksR0FBRyxFQUFFLENBQUM7U0FDWDtRQUNELEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUVaLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDO1FBQy9DLElBQUksT0FBTyxJQUFJLENBQUMsSUFBSSxLQUFLLFFBQVEsRUFBRTtZQUNqQyxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUU7Z0JBQ2hDLE1BQU0sSUFBSSxLQUFLLENBQ1gsZ0RBQWdELElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO2FBQ2xFO1NBQ0Y7YUFBTSxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFO1lBQ25DLEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxDQUFDLElBQUksRUFBRTtnQkFDNUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLEVBQUU7b0JBQzNCLE1BQU0sSUFBSSxLQUFLLENBQ1gsNENBQTRDO3dCQUM1QyxnQkFBZ0IsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO2lCQUNsRDthQUNGO1NBQ0Y7YUFBTTtZQUNMLE1BQU0sSUFBSSxLQUFLLENBQ1gsMERBQTBEO2dCQUMxRCxnQkFBZ0IsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1NBQ2xEO1FBRUQsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDO1FBQzFELElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLE1BQU0sSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUN2RCxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUM7UUFDcEQsSUFBSSxDQUFDLGVBQWUsR0FBRyxjQUFjLENBQUMsSUFBSSxDQUFDLGVBQWUsSUFBSSxPQUFPLENBQUMsQ0FBQztRQUN2RSxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsY0FBYyxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsSUFBSSxNQUFNLENBQUMsQ0FBQztRQUN4RSxJQUFJLENBQUMsZUFBZSxHQUFHLGNBQWMsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDNUQsSUFBSSxDQUFDLGdCQUFnQixHQUFHLGNBQWMsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUU5RCxJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQztJQUM5QixDQUFDO0lBRWUsS0FBSyxDQUFDLFVBQXlCO1FBQzdDLFVBQVUsR0FBRyxrQkFBa0IsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUM1QyxNQUFNLEtBQUssR0FBRyxVQUFVLENBQUMsTUFBTSxDQUFDO1FBRWhDLCtDQUErQztRQUMvQyxJQUFJLE9BQU8sSUFBSSxDQUFDLElBQUksS0FBSyxRQUFRLEVBQUU7WUFDakMsSUFBSSxDQUFDLElBQUksR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN6QjtRQUNELEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsRUFBRTtZQUN6QyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFO2dCQUNwQixJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQzthQUN2QjtTQUNGO1FBRUQseUJBQXlCO1FBQ3pCLEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxDQUFDLElBQUksRUFBRTtZQUM1QixJQUFJLElBQUksR0FBRyxDQUFDLElBQUksSUFBSSxJQUFJLEtBQUssRUFBRTtnQkFDN0IsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsSUFBSSxFQUFFLENBQUMsQ0FBQzthQUMxQztTQUNGO1FBQ0QsSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sS0FBSyxhQUFhLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLEVBQUU7WUFDL0QsTUFBTSxJQUFJLEtBQUssQ0FBQyw0QkFBNEIsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7U0FDMUQ7UUFFRCxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBYSxDQUFDO1FBRXZFLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQztRQUN2QixJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUU7WUFDZCxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQ3ZCLE9BQU8sRUFBRSxVQUFVLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxnQkFBZ0IsRUFDckQsSUFBSSxDQUFDLGdCQUFnQixFQUFFLFNBQVMsQ0FBQyxDQUFDO1NBQ3ZDO2FBQU07WUFDTCxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQztTQUNuQjtRQUNELElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRTtZQUNmLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FDdEIsTUFBTSxFQUFFLFVBQVUsRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLGVBQWUsRUFDbkQsSUFBSSxDQUFDLGVBQWUsRUFBRSxTQUFTLENBQUMsQ0FBQztTQUN0QzthQUFNO1lBQ0wsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7U0FDbEI7UUFFRCxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQztJQUNwQixDQUFDO0lBRVEsSUFBSSxDQUFDLE1BQXVCLEVBQUUsTUFBYztRQUNuRCxNQUFNLEtBQUssR0FBRyxtQkFBbUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMxQyxNQUFNLFVBQVUsR0FBRyxLQUFLLENBQUMsS0FBSyxDQUFDO1FBQy9CLE1BQU0sS0FBSyxHQUFHLFVBQVUsQ0FBQyxNQUFNLENBQUM7UUFFaEMsT0FBTyxJQUFJLENBQUMsR0FBRyxFQUFFO1lBQ2YsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDO1lBQ3RCLElBQUksRUFBQyxJQUFJLEVBQUUsUUFBUSxFQUFDLEdBQUcsT0FBTyxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQzNELE1BQU0sY0FBYyxHQUFHLGFBQWEsQ0FBQyxZQUFZLENBQUMsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQzVELEtBQUssTUFBTSxHQUFHLElBQUksSUFBSSxDQUFDLElBQWdCLEVBQUU7Z0JBQ3ZDLGNBQWMsQ0FBQyxHQUFHLENBQUMsR0FBRyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUM7YUFDdkM7WUFFRCxNQUFNLFNBQVMsR0FBRyxDQUFDLENBQVMsRUFBRSxFQUFFO2dCQUM5QixJQUFJLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxDQUFDLEtBQUssQ0FBQyxNQUFNLEtBQUssS0FBSyxFQUFFO29CQUN6QyxPQUFPLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxFQUFFLGNBQWMsQ0FBQyxDQUFDO2lCQUN2QztxQkFBTTtvQkFDTCxPQUFPLENBQUMsQ0FBQztpQkFDVjtZQUNILENBQUMsQ0FBQztZQUVGLElBQUksS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztZQUM3RCxJQUFJLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7WUFFOUQseUVBQXlFO1lBQ3pFLDBFQUEwRTtZQUMxRSxzRUFBc0U7WUFDdEUsd0VBQXdFO1lBQ3hFLHNFQUFzRTtZQUN0RSxpQ0FBaUM7WUFDakMsTUFBTSxhQUFhLEdBQWEsRUFBRSxDQUFDO1lBQ25DLE1BQU0saUJBQWlCLEdBQWEsRUFBRSxDQUFDO1lBQ3ZDLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxLQUFLLEVBQUUsRUFBRSxDQUFDLEVBQUU7Z0JBQzlCLElBQUssSUFBSSxDQUFDLElBQWlCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFO29CQUM3QyxhQUFhLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO29CQUNsQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7aUJBQzNCO3FCQUFNO29CQUNMLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7b0JBQ3RCLGlCQUFpQixDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztpQkFDdkM7YUFDRjtZQUNELElBQUksR0FBRyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxhQUFhLENBQUMsQ0FBQztZQUNyQyxRQUFRLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsYUFBYSxDQUFDLENBQUM7WUFDN0MsSUFBSSxLQUFLLElBQUksSUFBSSxFQUFFO2dCQUNqQixLQUFLLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsaUJBQWlCLENBQUMsQ0FBQzthQUM1QztZQUNELElBQUksTUFBTSxJQUFJLElBQUksRUFBRTtnQkFDbEIsTUFBTSxHQUFHLEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLGlCQUFpQixDQUFDLENBQUM7YUFDOUM7WUFFRCxPQUFPLGtCQUFrQixDQUNyQixLQUFLLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMxRCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFUSxTQUFTO1FBQ2hCLE1BQU0sTUFBTSxHQUE2QjtZQUN2QyxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUk7WUFDZixPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU87WUFDckIsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNO1lBQ25CLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSztZQUNqQixlQUFlLEVBQUUsb0JBQW9CLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQztZQUMzRCxnQkFBZ0IsRUFBRSxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUM7WUFDN0QsZUFBZSxFQUFFLG9CQUFvQixDQUFDLElBQUksQ0FBQyxlQUFlLENBQUM7WUFDM0QsZ0JBQWdCLEVBQUUsb0JBQW9CLENBQUMsSUFBSSxDQUFDLGdCQUFnQixDQUFDO1NBQzlELENBQUM7UUFDRixNQUFNLFVBQVUsR0FBRyxLQUFLLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDckMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDbEMsT0FBTyxNQUFNLENBQUM7SUFDaEIsQ0FBQzs7QUF0S0Qsa0JBQWtCO0FBQ1gsNEJBQVMsR0FBRyxvQkFBb0IsQ0FBQztTQUY3QixrQkFBa0I7QUF5Sy9CLGFBQWEsQ0FBQyxhQUFhLENBQUMsa0JBQWtCLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCAyMDE4IEdvb2dsZSBMTENcbiAqXG4gKiBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhbiBNSVQtc3R5bGVcbiAqIGxpY2Vuc2UgdGhhdCBjYW4gYmUgZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBvciBhdFxuICogaHR0cHM6Ly9vcGVuc291cmNlLm9yZy9saWNlbnNlcy9NSVQuXG4gKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICovXG5cbi8qKlxuICogTm9ybWFsaXphdGlvbiBsYXllcnMuXG4gKi9cblxuaW1wb3J0ICogYXMgdGZjIGZyb20gJ0B0ZW5zb3JmbG93L3RmanMtY29yZSc7XG5pbXBvcnQge21vbWVudHMsIHJlc2hhcGUsIHNlcmlhbGl6YXRpb24sIFRlbnNvciwgVGVuc29yMUQsIFRlbnNvcjJELCBUZW5zb3IzRCwgVGVuc29yNEQsIHRpZHksIHV0aWx9IGZyb20gJ0B0ZW5zb3JmbG93L3RmanMtY29yZSc7XG5cbmltcG9ydCB7Q29uc3RyYWludCwgQ29uc3RyYWludElkZW50aWZpZXIsIGdldENvbnN0cmFpbnQsIHNlcmlhbGl6ZUNvbnN0cmFpbnR9IGZyb20gJy4uL2NvbnN0cmFpbnRzJztcbmltcG9ydCB7SW5wdXRTcGVjLCBMYXllciwgTGF5ZXJBcmdzfSBmcm9tICcuLi9lbmdpbmUvdG9wb2xvZ3knO1xuaW1wb3J0IHtOb3RJbXBsZW1lbnRlZEVycm9yLCBWYWx1ZUVycm9yfSBmcm9tICcuLi9lcnJvcnMnO1xuaW1wb3J0IHtnZXRJbml0aWFsaXplciwgSW5pdGlhbGl6ZXIsIEluaXRpYWxpemVySWRlbnRpZmllciwgc2VyaWFsaXplSW5pdGlhbGl6ZXJ9IGZyb20gJy4uL2luaXRpYWxpemVycyc7XG5pbXBvcnQge1NoYXBlfSBmcm9tICcuLi9rZXJhc19mb3JtYXQvY29tbW9uJztcbmltcG9ydCB7Z2V0UmVndWxhcml6ZXIsIFJlZ3VsYXJpemVyLCBSZWd1bGFyaXplcklkZW50aWZpZXIsIHNlcmlhbGl6ZVJlZ3VsYXJpemVyfSBmcm9tICcuLi9yZWd1bGFyaXplcnMnO1xuaW1wb3J0IHtLd2FyZ3N9IGZyb20gJy4uL3R5cGVzJztcbmltcG9ydCAqIGFzIGdlbmVyaWNfdXRpbHMgZnJvbSAnLi4vdXRpbHMvZ2VuZXJpY191dGlscyc7XG5pbXBvcnQgKiBhcyBtYXRoX3V0aWxzIGZyb20gJy4uL3V0aWxzL21hdGhfdXRpbHMnO1xuaW1wb3J0IHtnZXRFeGFjdGx5T25lU2hhcGUsIGdldEV4YWN0bHlPbmVUZW5zb3J9IGZyb20gJy4uL3V0aWxzL3R5cGVzX3V0aWxzJztcbmltcG9ydCB7TGF5ZXJWYXJpYWJsZX0gZnJvbSAnLi4vdmFyaWFibGVzJztcblxuLyoqXG4gKiBBcHBsaWVzIGJhdGNoIG5vcm1hbGl6YXRpb24gb24geCBnaXZlbiBtZWFuLCB2YXIsIGJldGEgYW5kIGdhbW1hLlxuICpcbiAqIEkuZS4gcmV0dXJuczpcbiAqICAgYG91dHB1dCA9ICh4IC0gbWVhbikgLyAoc3FydCh2YXIpICsgZXBzaWxvbikgKiBnYW1tYSArIGJldGFgXG4gKlxuICogQHBhcmFtIHggSW5wdXQgdGVuc29yLlxuICogQHBhcmFtIG1lYW4gTWVhbiBvZiBiYXRjaC5cbiAqIEBwYXJhbSB2YXJpYW5jZSBWYXJpYW5jZSBvZiBiYXRjaC5cbiAqIEBwYXJhbSBiZXRhIFRlbnNvciB3aXRoIHdoaWNoIHRvIGNlbnRlciB0aGUgaW5wdXQuXG4gKiBAcGFyYW0gZ2FtbWEgVGVuc29yIGJ5IHdoaWNoIHRvIHNjYWxlIHRoZSBpbnB1dC5cbiAqIEBwYXJhbSBlcHNpbG9uIEZ1enogZmFjdG9yLlxuICogQHJldHVybnMgVGhlIHJlc3VsdCBvZiB0aGUgYmF0Y2ggbm9ybWFsaXphdGlvbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJhdGNoTm9ybWFsaXphdGlvbihcbiAgICB4OiBUZW5zb3IsIG1lYW46IFRlbnNvciwgdmFyaWFuY2U6IFRlbnNvciwgYmV0YT86IFRlbnNvciwgZ2FtbWE/OiBUZW5zb3IsXG4gICAgZXBzaWxvbiA9IDFlLTMpOiBUZW5zb3Ige1xuICBsZXQgb3V0OiBUZW5zb3I7XG4gIGlmICh4LnJhbmsgPT09IDIpIHtcbiAgICBvdXQgPSB0ZmMuYmF0Y2hOb3JtMmQoXG4gICAgICAgIHggYXMgVGVuc29yMkQsIG1lYW4gYXMgVGVuc29yMkQgfCBUZW5zb3IxRCxcbiAgICAgICAgdmFyaWFuY2UgYXMgVGVuc29yMkQgfCBUZW5zb3IxRCwgYmV0YSBhcyBUZW5zb3IyRCB8IFRlbnNvcjFELFxuICAgICAgICBnYW1tYSBhcyBUZW5zb3IyRCB8IFRlbnNvcjFELCBlcHNpbG9uKTtcbiAgfSBlbHNlIGlmICh4LnJhbmsgPT09IDMpIHtcbiAgICAvLyBUT0RPKGNhaXMpOiBDaGVjayByYW5rOyBnaXZlIHByb3BlciBlcnJvciBtZXNzYWdlLlxuICAgIG91dCA9IHRmYy5iYXRjaE5vcm0zZChcbiAgICAgICAgeCBhcyBUZW5zb3IzRCwgbWVhbiBhcyBUZW5zb3IzRCB8IFRlbnNvcjFELFxuICAgICAgICB2YXJpYW5jZSBhcyBUZW5zb3IzRCB8IFRlbnNvcjFELCBiZXRhIGFzIFRlbnNvcjNEIHwgVGVuc29yMUQsXG4gICAgICAgIGdhbW1hIGFzIFRlbnNvcjNEIHwgVGVuc29yMUQsIGVwc2lsb24pO1xuICB9IGVsc2UgaWYgKHgucmFuayA9PT0gNCkge1xuICAgIG91dCA9IHRmYy5iYXRjaE5vcm00ZChcbiAgICAgICAgeCBhcyBUZW5zb3I0RCwgbWVhbiBhcyBUZW5zb3I0RCB8IFRlbnNvcjFELFxuICAgICAgICB2YXJpYW5jZSBhcyBUZW5zb3I0RCB8IFRlbnNvcjFELCBiZXRhIGFzIFRlbnNvcjREIHwgVGVuc29yMUQsXG4gICAgICAgIGdhbW1hIGFzIFRlbnNvcjREIHwgVGVuc29yMUQsIGVwc2lsb24pO1xuICB9IGVsc2Uge1xuICAgIHRocm93IG5ldyBOb3RJbXBsZW1lbnRlZEVycm9yKFxuICAgICAgICBgYmF0Y2hOb3JtYWxpemF0aW9uIGlzIG5vdCBpbXBsZW1lbnRlZCBmb3IgYXJyYXkgb2YgcmFuayAke3gucmFua30gYCArXG4gICAgICAgIGB5ZXRgKTtcbiAgfVxuICByZXR1cm4gb3V0O1xufVxuXG4vKipcbiAqIE5vbi1icm9hZGNhc3RpbmcgYmF0Y2ggbm9ybWFsaXphdGlvbiBmb3IgdXNlIGluIHRyYWluaW5nIChub3QgaW5mZXJlbmNlKS5cbiAqXG4gKiBUaGUgaW5wdXQgaXMgbm9ybWFsaXplZCB0byB6ZXJvIG1lYW4gYW5kIHVuaXQgdmFyaWFuY2UgYWxvbmcgdGhlXG4gKiBgcmVkdWN0aW9uQXhlc2AsIGZvbGxvd2VkIGJ5IHNjYWxpbmcgd2l0aCBgZ2FtbWFgIGFuZCBzaGlmdGVkIGJ5IGBiZXRhYC5cbiAqIFRoZSByZXN1bHQgb2YgdGhhdCBpcyByZXR1cm5lZCBhcyB0aGUgZmlyc3QgZWxlbWVudFxuICogb2YgdGhlIHJldHVybmVkIGBBcnJheWAuIFRoZSBvdGhlciB0d28gZWxlbWVudHMgYXJlIHRoZSBtZWFuIGFuZCB2YXJpYW5jZSxcbiAqIHJlc3BlY3RpdmVseS5cbiAqXG4gKiBAcGFyYW0geCBJbnB1dCB0ZW5zb3IgdG8gYmUgbm9ybWFsaXplZC5cbiAqIEBwYXJhbSBnYW1tYSBUZW5zb3IgYnkgd2hpY2ggdG8gc2NhbGUgdGhlIGlucHV0LlxuICogQHBhcmFtIGJldGEgVGVuc29yIGJ5IHdoaWNoIHRvIGNlbnRlciB0aGUgaW5wdXQuXG4gKiBAcGFyYW0gcmVkdWN0aW9uQXhlcyBBeGVzIG92ZXIgd2hpY2ggdG8gbm9ybWFsaXplLlxuICogQHBhcmFtIGVwc2lsb24gRnV6eiBmYWN0b3IuXG4gKiBAcmV0dXJucyBBbiBgQXJyYXlgIG9mIHRocmVlIGBUZW5zb3JzYDpcbiAqICAgW25vcm1hbGl6ZWQgdGVuc29yLCBtZWFuIG9mIGlucHV0LCB2YXJpYW5jZSBvZiBpbnB1dF0uXG4gKi9cbmZ1bmN0aW9uIHJlZ3VsYXJOb3JtYWxpemVCYXRjaEluVHJhaW5pbmcoXG4gICAgeDogVGVuc29yLCBnYW1tYTogVGVuc29yLCBiZXRhOiBUZW5zb3IsIHJlZHVjdGlvbkF4ZXM6IG51bWJlcltdLFxuICAgIGVwc2lsb24gPSAxZS0zKTogW1RlbnNvciwgVGVuc29yLCBUZW5zb3JdIHtcbiAgcmV0dXJuIHRpZHkoKCkgPT4ge1xuICAgICAgICAgICBjb25zdCBtZWFuQW5kVmFyaWFuY2UgPSB0ZmMubW9tZW50cyh4LCByZWR1Y3Rpb25BeGVzKTtcbiAgICAgICAgICAgY29uc3QgbWVhbiA9IG1lYW5BbmRWYXJpYW5jZS5tZWFuO1xuICAgICAgICAgICBjb25zdCB2YXJpYW5jZSA9IG1lYW5BbmRWYXJpYW5jZS52YXJpYW5jZTtcbiAgICAgICAgICAgY29uc3Qgbm9ybWVkID1cbiAgICAgICAgICAgICAgIGJhdGNoTm9ybWFsaXphdGlvbih4LCBtZWFuLCB2YXJpYW5jZSwgYmV0YSwgZ2FtbWEsIGVwc2lsb24pO1xuICAgICAgICAgICByZXR1cm4gW25vcm1lZCwgbWVhbiwgdmFyaWFuY2VdO1xuICAgICAgICAgfSkgYXMgW1RlbnNvciwgVGVuc29yLCBUZW5zb3JdO1xufVxuXG4vKipcbiAqIEJyb2FkY2FzdGluZyBiYXRjaCBub3JtYWxpemF0aW9uIGZvciB1c2UgaW4gdHJhaW5pbmcgKG5vdCBpbmZlcmVuY2UpLlxuICpcbiAqIFRoZSBpbnB1dCBpcyBub3JtYWxpemVkIHRvIHplcm8gbWVhbiBhbmQgdW5pdCB2YXJpYW5jZSBhbG9uZyB0aGVcbiAqIGByZWR1Y3Rpb25BeGVzYCwgZm9sbG93ZWQgYnkgc2NhbGluZyB3aXRoIGBnYW1tYWAgYW5kIHNoaWZ0ZWQgYnkgYGJldGFgLlxuICogVGhlIHJlc3VsdCBvZiB0aGF0IGlzIHJldHVybmVkIGFzIHRoZSBmaXJzdCBlbGVtZW50XG4gKiBvZiB0aGUgcmV0dXJuZWQgYEFycmF5YC4gVGhlIG90aGVyIHR3byBlbGVtZW50cyBhcmUgdGhlIG1lYW4gYW5kIHZhcmlhbmNlLFxuICogcmVzcGVjdGl2ZWx5LlxuICpcbiAqIEBwYXJhbSB4IElucHV0IHRlbnNvciB0byBiZSBub3JtYWxpemVkLlxuICogQHBhcmFtIGdhbW1hIFRlbnNvciBieSB3aGljaCB0byBzY2FsZSB0aGUgaW5wdXQuXG4gKiBAcGFyYW0gYmV0YSBUZW5zb3IgYnkgd2hpY2ggdG8gY2VudGVyIHRoZSBpbnB1dC5cbiAqIEBwYXJhbSByZWR1Y3Rpb25BeGVzIEF4ZXMgb3ZlciB3aGljaCB0byBub3JtYWxpemUuXG4gKiBAcGFyYW0gZXBzaWxvbiBGdXp6IGZhY3Rvci5cbiAqIEByZXR1cm5zIEFuIGBBcnJheWAgb2YgdGhyZWUgYFRlbnNvcnNgOlxuICogICBbbm9ybWFsaXplZCB0ZW5zb3IsIG1lYW4gb2YgaW5wdXQsIHZhcmlhbmNlIG9mIGlucHV0XS5cbiAqL1xuZnVuY3Rpb24gYnJvYWRjYXN0Tm9ybWFsaXplQmF0Y2hJblRyYWluaW5nKFxuICAgIHg6IFRlbnNvciwgZ2FtbWE6IFRlbnNvciwgYmV0YTogVGVuc29yLCByZWR1Y3Rpb25BeGVzOiBudW1iZXJbXSxcbiAgICBlcHNpbG9uID0gMWUtMyk6IFtUZW5zb3IsIFRlbnNvciwgVGVuc29yXSB7XG4gIHJldHVybiB0aWR5KCgpID0+IHtcbiAgICAgICAgICAgY29uc3QgbWVhbkFuZFZhcmlhbmNlID0gdGZjLm1vbWVudHMoeCwgcmVkdWN0aW9uQXhlcyk7XG4gICAgICAgICAgIGNvbnN0IG1lYW4gPSBtZWFuQW5kVmFyaWFuY2UubWVhbjtcbiAgICAgICAgICAgY29uc3QgdmFyaWFuY2UgPSBtZWFuQW5kVmFyaWFuY2UudmFyaWFuY2U7XG4gICAgICAgICAgIGNvbnN0IHRhcmdldFNoYXBlOiBudW1iZXJbXSA9IFtdO1xuICAgICAgICAgICBmb3IgKGNvbnN0IGF4aXMgb2YgbWF0aF91dGlscy5yYW5nZSgwLCB4LnJhbmspKSB7XG4gICAgICAgICAgICAgaWYgKHJlZHVjdGlvbkF4ZXMuaW5kZXhPZihheGlzKSAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgIHRhcmdldFNoYXBlLnB1c2goMSk7XG4gICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgIHRhcmdldFNoYXBlLnB1c2goeC5zaGFwZVtheGlzXSk7XG4gICAgICAgICAgICAgfVxuICAgICAgICAgICB9XG4gICAgICAgICAgIGNvbnN0IGJyb2FkY2FzdE1lYW4gPSByZXNoYXBlKG1lYW4sIHRhcmdldFNoYXBlKTtcbiAgICAgICAgICAgY29uc3QgYnJvYWRjYXN0VmFyaWFuY2UgPSByZXNoYXBlKHZhcmlhbmNlLCB0YXJnZXRTaGFwZSk7XG4gICAgICAgICAgIGNvbnN0IGJyb2FkY2FzdEdhbW1hID1cbiAgICAgICAgICAgICAgIGdhbW1hID09IG51bGwgPyBudWxsIDogcmVzaGFwZShnYW1tYSwgdGFyZ2V0U2hhcGUpO1xuICAgICAgICAgICBjb25zdCBicm9hZGNhc3RCZXRhID1cbiAgICAgICAgICAgICAgIGJldGEgPT0gbnVsbCA/IG51bGwgOiByZXNoYXBlKGJldGEsIHRhcmdldFNoYXBlKTtcbiAgICAgICAgICAgY29uc3Qgbm9ybWVkID0gYmF0Y2hOb3JtYWxpemF0aW9uKFxuICAgICAgICAgICAgICAgeCwgYnJvYWRjYXN0TWVhbiwgYnJvYWRjYXN0VmFyaWFuY2UsIGJyb2FkY2FzdEJldGEsXG4gICAgICAgICAgICAgICBicm9hZGNhc3RHYW1tYSwgZXBzaWxvbik7XG4gICAgICAgICAgIHJldHVybiBbbm9ybWVkLCBtZWFuLCB2YXJpYW5jZV07XG4gICAgICAgICB9KSBhcyBbVGVuc29yLCBUZW5zb3IsIFRlbnNvcl07XG59XG5cbi8qKlxuICogQmF0Y2ggbm9ybWFsaXphdGlvbiBmb3IgdXNlIGluIHRyYWluaW5nIChub3QgaW5mZXJlbmNlKS5cbiAqXG4gKiBAcGFyYW0geCBJbnB1dCB0ZW5zb3IgdG8gYmUgbm9ybWFsaXplZC5cbiAqIEBwYXJhbSBnYW1tYSBUZW5zb3IgYnkgd2hpY2ggdG8gc2NhbGUgdGhlIGlucHV0LlxuICogQHBhcmFtIGJldGEgVGVuc29yIGJ5IHdoaWNoIHRvIGNlbnRlciB0aGUgaW5wdXQuXG4gKiBAcGFyYW0gcmVkdWN0aW9uQXhlcyBBeGVzIG92ZXIgd2hpY2ggdG8gbm9ybWFsaXplLlxuICogQHBhcmFtIGVwc2lsb24gRnV6eiBmYWN0b3IuXG4gKiBAcmV0dXJucyBBbiBgQXJyYXlgIG9mIHRocmVlIGBUZW5zb3JzYDpcbiAqICAgW25vcm1hbGl6ZWQgdGVuc29yLCBtZWFuIG9mIGlucHV0LCB2YXJpYW5jZSBvZiBpbnB1dF0uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBub3JtYWxpemVCYXRjaEluVHJhaW5pbmcoXG4gICAgeDogVGVuc29yLCBnYW1tYTogVGVuc29yLCBiZXRhOiBUZW5zb3IsIHJlZHVjdGlvbkF4ZXM6IG51bWJlcltdLFxuICAgIGVwc2lsb24gPSAxZS0zKTogW1RlbnNvciwgVGVuc29yLCBUZW5zb3JdIHtcbiAgaWYgKHV0aWwuYXJyYXlzRXF1YWwoXG4gICAgICAgICAgcmVkdWN0aW9uQXhlcy5zbGljZSgpLnNvcnQoKSwgbWF0aF91dGlscy5yYW5nZSgwLCB4LnJhbmsgLSAxKSkpIHtcbiAgICByZXR1cm4gcmVndWxhck5vcm1hbGl6ZUJhdGNoSW5UcmFpbmluZyhcbiAgICAgICAgeCwgZ2FtbWEsIGJldGEsIHJlZHVjdGlvbkF4ZXMsIGVwc2lsb24pO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiBicm9hZGNhc3ROb3JtYWxpemVCYXRjaEluVHJhaW5pbmcoXG4gICAgICAgIHgsIGdhbW1hLCBiZXRhLCByZWR1Y3Rpb25BeGVzLCBlcHNpbG9uKTtcbiAgfVxufVxuXG5leHBvcnQgZGVjbGFyZSBpbnRlcmZhY2UgQmF0Y2hOb3JtYWxpemF0aW9uTGF5ZXJBcmdzIGV4dGVuZHMgTGF5ZXJBcmdzIHtcbiAgLyoqXG4gICAqIFRoZSBpbnRlZ2VyIGF4aXMgdGhhdCBzaG91bGQgYmUgbm9ybWFsaXplZCAodHlwaWNhbGx5IHRoZSBmZWF0dXJlcyBheGlzKS5cbiAgICogRGVmYXVsdHMgdG8gLTEuXG4gICAqXG4gICAqIEZvciBpbnN0YW5jZSwgYWZ0ZXIgYSBgQ29udjJEYCBsYXllciB3aXRoIGBkYXRhX2Zvcm1hdD1cImNoYW5uZWxzX2ZpcnN0XCJgLFxuICAgKiBzZXQgYGF4aXM9MWAgaW4gYGJhdGNoTm9ybWFsaXphdGlvbmAuXG4gICAqL1xuICBheGlzPzogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiBNb21lbnR1bSBvZiB0aGUgbW92aW5nIGF2ZXJhZ2UuIERlZmF1bHRzIHRvIDAuOTkuXG4gICAqL1xuICBtb21lbnR1bT86IG51bWJlcjtcblxuICAvKipcbiAgICogU21hbGwgZmxvYXQgYWRkZWQgdG8gdGhlIHZhcmlhbmNlIHRvIGF2b2lkIGRpdmlkaW5nIGJ5IHplcm8uIERlZmF1bHRzIHRvXG4gICAqIDFlLTMuXG4gICAqL1xuICBlcHNpbG9uPzogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiBJZiBgdHJ1ZWAsIGFkZCBvZmZzZXQgb2YgYGJldGFgIHRvIG5vcm1hbGl6ZWQgdGVuc29yLlxuICAgKiBJZiBgZmFsc2VgLCBgYmV0YWAgaXMgaWdub3JlZC5cbiAgICogRGVmYXVsdHMgdG8gYHRydWVgLlxuICAgKi9cbiAgY2VudGVyPzogYm9vbGVhbjtcblxuICAvKipcbiAgICogSWYgYHRydWVgLCBtdWx0aXBseSBieSBgZ2FtbWFgLlxuICAgKiBJZiBgZmFsc2VgLCBgZ2FtbWFgIGlzIG5vdCB1c2VkLlxuICAgKiBXaGVuIHRoZSBuZXh0IGxheWVyIGlzIGxpbmVhciAoYWxzbyBlLmcuIGBubi5yZWx1YCksXG4gICAqIHRoaXMgY2FuIGJlIGRpc2FibGVkIHNpbmNlIHRoZSBzY2FsaW5nIHdpbGwgYmUgZG9uZSBieSB0aGUgbmV4dCBsYXllci5cbiAgICogRGVmYXVsdHMgdG8gYHRydWVgLlxuICAgKi9cbiAgc2NhbGU/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBJbml0aWFsaXplciBmb3IgdGhlIGJldGEgd2VpZ2h0LlxuICAgKiAgRGVmYXVsdHMgdG8gJ3plcm9zJy5cbiAgICovXG4gIGJldGFJbml0aWFsaXplcj86IEluaXRpYWxpemVySWRlbnRpZmllcnxJbml0aWFsaXplcjtcblxuICAvKipcbiAgICogSW5pdGlhbGl6ZXIgZm9yIHRoZSBnYW1tYSB3ZWlnaHQuXG4gICAqICBEZWZhdWx0cyB0byBgb25lc2AuXG4gICAqL1xuICBnYW1tYUluaXRpYWxpemVyPzogSW5pdGlhbGl6ZXJJZGVudGlmaWVyfEluaXRpYWxpemVyO1xuXG4gIC8qKlxuICAgKiBJbml0aWFsaXplciBmb3IgdGhlIG1vdmluZyBtZWFuLlxuICAgKiBEZWZhdWx0cyB0byBgemVyb3NgXG4gICAqL1xuICBtb3ZpbmdNZWFuSW5pdGlhbGl6ZXI/OiBJbml0aWFsaXplcklkZW50aWZpZXJ8SW5pdGlhbGl6ZXI7XG5cbiAgLyoqXG4gICAqIEluaXRpYWxpemVyIGZvciB0aGUgbW92aW5nIHZhcmlhbmNlLlxuICAgKiAgRGVmYXVsdHMgdG8gJ09uZXMnLlxuICAgKi9cbiAgbW92aW5nVmFyaWFuY2VJbml0aWFsaXplcj86IEluaXRpYWxpemVySWRlbnRpZmllcnxJbml0aWFsaXplcjtcblxuICAvKipcbiAgICogQ29uc3RyYWludCBmb3IgdGhlIGJldGEgd2VpZ2h0LlxuICAgKi9cbiAgYmV0YUNvbnN0cmFpbnQ/OiBDb25zdHJhaW50SWRlbnRpZmllcnxDb25zdHJhaW50O1xuXG4gIC8qKlxuICAgKiBDb25zdHJhaW50IGZvciBnYW1tYSB3ZWlnaHQuXG4gICAqL1xuICBnYW1tYUNvbnN0cmFpbnQ/OiBDb25zdHJhaW50SWRlbnRpZmllcnxDb25zdHJhaW50O1xuXG4gIC8qKlxuICAgKiBSZWd1bGFyaXplciBmb3IgdGhlIGJldGEgd2VpZ2h0LlxuICAgKi9cbiAgYmV0YVJlZ3VsYXJpemVyPzogUmVndWxhcml6ZXJJZGVudGlmaWVyfFJlZ3VsYXJpemVyO1xuXG4gIC8qKlxuICAgKiBSZWd1b