UNPKG

@tensorflow/tfjs-layers

Version:

TensorFlow layers API in JavaScript

236 lines 34.2 kB
/** * @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. * ============================================================================= */ /* Original Source: losses.py */ import * as tfc from '@tensorflow/tfjs-core'; import { tidy, util } from '@tensorflow/tfjs-core'; import { epsilon } from './backend/common'; import * as K from './backend/tfjs_backend'; import { ValueError } from './errors'; /** * Normalizes a tensor wrt the L2 norm alongside the specified axis. * @param x * @param axis Axis along which to perform normalization. */ export function l2Normalize(x, axis) { return tidy(() => { if (x.dtype !== 'float32') { x = tfc.cast(x, 'float32'); } const squareSum = tfc.sum(K.square(x), axis, true); const epsilonTensor = tfc.fill(squareSum.shape, epsilon()); const norm = tfc.sqrt(tfc.maximum(squareSum, epsilonTensor)); return tfc.div(x, norm); }); } export function meanSquaredError(yTrue, yPred) { return tidy(() => tfc.mean(K.square(tfc.sub(yPred, yTrue)), -1)); } export function meanAbsoluteError(yTrue, yPred) { return tidy(() => tfc.mean(tfc.abs(tfc.sub(yPred, yTrue)), -1)); } export function meanAbsolutePercentageError(yTrue, yPred) { return tidy(() => { const diff = tfc.sub(yTrue, yPred); const clippedTrue = tfc.clipByValue(tfc.abs(yTrue), epsilon(), Number.MAX_VALUE); const absResult = tfc.abs(tfc.div(diff, clippedTrue)); return tfc.mul(100, tfc.mean(absResult, -1)); }); } export function meanSquaredLogarithmicError(yTrue, yPred) { return tidy(() => { const clippedPred = tfc.clipByValue(yPred, epsilon(), Number.MAX_VALUE); const firstLog = tfc.log(tfc.add(1, clippedPred)); const clippedTrue = tfc.clipByValue(yTrue, epsilon(), Number.MAX_VALUE); const secondLog = tfc.log(tfc.add(1, clippedTrue)); return tfc.mean(K.square(tfc.sub(firstLog, secondLog)), -1); }); } export function squaredHinge(yTrue, yPred) { return tidy(() => { const maxResult = tfc.maximum(0, tfc.sub(1, tfc.mul(yTrue, yPred))); return tfc.mean(K.square(maxResult), -1); }); } export function hinge(yTrue, yPred) { return tidy(() => { const maxResult = tfc.maximum(0, tfc.sub(1, tfc.mul(yTrue, yPred))); return tfc.mean(maxResult, -1); }); } export function categoricalHinge(yTrue, yPred) { return tidy(() => { const pos = tfc.sum(tfc.mul(yTrue, yPred), -1); const neg = tfc.max(tfc.mul(tfc.sub(1, yTrue), yPred), -1); return tfc.maximum(0, tfc.add(1, tfc.sub(neg, pos))); }); } /** * Logarithm of the hyperbolic cosine of the prediction error. * * `log(cosh(x))` is approximately equal to `(x ** 2) / 2` for small `x` and * to `abs(x) - log(2)` for large `x`. This means that 'logcosh' works mostly * like the mean squared error, but will not be so strongly affected by the * occasional wildly incorrect prediction. */ export function logcosh(yTrue, yPred) { return tidy(() => { const log2 = Math.log(2); const predictionDiff = tfc.sub(yPred, yTrue); const logcoshResult = tfc.sub(tfc.add(predictionDiff, tfc.softplus(tfc.mul(-2, predictionDiff))), log2); return tfc.mean(logcoshResult, -1); }); } export function categoricalCrossentropy(target, output, fromLogits = false) { return tidy(() => { if (fromLogits) { output = tfc.softmax(output); } else { // scale preds so that the class probabilities of each sample sum to 1. const outputSum = tfc.sum(output, output.shape.length - 1, true); output = tfc.div(output, outputSum); } output = tfc.clipByValue(output, epsilon(), 1 - epsilon()); return tfc.neg(tfc.sum(tfc.mul(tfc.cast(target, 'float32'), tfc.log(output)), output.shape.length - 1)); }); } /** * Categorical crossentropy with integer targets. * * @param target An integer tensor. * @param output A tensor resulting from a softmax (unless `fromLogits` is * `true`, in which case `output` is expected to be the logits). * @param fromLogits Boolean, whether `output` is the result of a softmax, or is * a tensor of logits. */ export function sparseCategoricalCrossentropy(target, output, fromLogits = false) { return tidy(() => { const flatTarget = tfc.cast(tfc.floor(K.flatten(target)), 'int32'); output = tfc.clipByValue(output, epsilon(), 1 - epsilon()); const outputShape = output.shape; const oneHotTarget = tfc.reshape(tfc.oneHot(flatTarget, outputShape[outputShape.length - 1]), outputShape); return categoricalCrossentropy(oneHotTarget, output, fromLogits); }); } /** * From TensorFlow's implementation in nn_impl.py: * * For brevity, let `x = logits`, `z = labels`. The logistic loss is * z * -log(sigmoid(x)) + (1 - z) * -log(1 - sigmoid(x)) * = z * -log(1 / (1 + exp(-x))) + (1 - z) * -log(exp(-x) / (1 + exp(-x))) * = z * log(1 + exp(-x)) + (1 - z) * (-log(exp(-x)) + log(1 + exp(-x))) * = z * log(1 + exp(-x)) + (1 - z) * (x + log(1 + exp(-x)) * = (1 - z) * x + log(1 + exp(-x)) * = x - x * z + log(1 + exp(-x)) * For x < 0, to avoid overflow in exp(-x), we reformulate the above * x - x * z + log(1 + exp(-x)) * = log(exp(x)) - x * z + log(1 + exp(-x)) * = - x * z + log(1 + exp(x)) * Hence, to ensure stability and avoid overflow, the implementation uses this * equivalent formulation * max(x, 0) - x * z + log(1 + exp(-abs(x))) * * @param labels The labels. * @param logits The logits. */ export function sigmoidCrossEntropyWithLogits(labels, logits) { if (!util.arraysEqual(labels.shape, logits.shape)) { throw new ValueError(`logits and labels must have the same shape, but got shapes ` + `${JSON.stringify(labels.shape)} and ${JSON.stringify(logits.shape)}`); } return tidy(() => { // The logistic loss formula from above is // x - x * z + log(1 + exp(-x)) // For x < 0, a more numerically stable formula is // -x * z + log(1 + exp(x)) // Note that these two expressions can be combined into the following: // max(x, 0) - x * z + log(1 + exp(-abs(x))) const reluLogits = tfc.relu(logits); const negAbsLogits = tfc.neg(tfc.abs(logits)); return tfc.add(tfc.sub(reluLogits, tfc.mul(logits, labels)), tfc.log1p(tfc.exp(negAbsLogits))); }); } export function binaryCrossentropy(yTrue, yPred) { return tidy(() => { let y; y = tfc.clipByValue(yPred, epsilon(), 1 - epsilon()); y = tfc.log(tfc.div(y, tfc.sub(1, y))); return tfc.mean(sigmoidCrossEntropyWithLogits(yTrue, y), -1); }); } export function kullbackLeiblerDivergence(yTrue, yPred) { return tidy(() => { const clippedTrue = tfc.clipByValue(yTrue, epsilon(), 1); const clippedPred = tfc.clipByValue(yPred, epsilon(), 1); return tfc.sum(tfc.mul(yTrue, tfc.log(tfc.div(clippedTrue, clippedPred))), -1); }); } export function poisson(yTrue, yPred) { return tidy(() => { const logPred = tfc.log(tfc.add(epsilon(), yPred)); return tfc.mean(tfc.sub(yPred, tfc.mul(yTrue, logPred)), -1); }); } export function cosineProximity(yTrue, yPred) { return tidy(() => { const trueNormalized = l2Normalize(yTrue, -1); const predNormalized = l2Normalize(yPred, -1); const trueXPred = tfc.mul(trueNormalized, predNormalized); return tfc.neg(tfc.sum(trueXPred, -1)); }); } export const mse = meanSquaredError; export const MSE = meanSquaredError; export const mae = meanAbsoluteError; export const MAE = meanAbsoluteError; export const mape = meanAbsolutePercentageError; export const MAPE = meanAbsolutePercentageError; export const msle = meanSquaredLogarithmicError; export const MSLE = meanSquaredLogarithmicError; export const kld = kullbackLeiblerDivergence; export const KLD = kullbackLeiblerDivergence; export const cosine = cosineProximity; // TODO(michaelterry): Add deserialize() function. export const lossesMap = { meanSquaredError, meanAbsoluteError, meanAbsolutePercentageError, meanSquaredLogarithmicError, squaredHinge, hinge, categoricalHinge, logcosh, categoricalCrossentropy, sparseCategoricalCrossentropy, binaryCrossentropy, kullbackLeiblerDivergence, poisson, cosineProximity }; // Porting note: This diverges from the PyKeras implementation and may need to // change based on (de)serialization requirements. export function get(identifierOrFn) { if (typeof identifierOrFn === 'string') { if (identifierOrFn in lossesMap) { return lossesMap[identifierOrFn]; } let errMsg = `Unknown loss ${identifierOrFn}`; if (identifierOrFn.toLowerCase().includes('softmaxcrossentropy')) { errMsg = `Unknown loss ${identifierOrFn}. ` + 'Use "categoricalCrossentropy" as the string name for ' + 'tf.losses.softmaxCrossEntropy'; } throw new ValueError(errMsg); } else { return identifierOrFn; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9zc2VzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vdGZqcy1sYXllcnMvc3JjL2xvc3Nlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7R0FRRztBQUVILGdDQUFnQztBQUNoQyxPQUFPLEtBQUssR0FBRyxNQUFNLHVCQUF1QixDQUFDO0FBQzdDLE9BQU8sRUFBbUIsSUFBSSxFQUFFLElBQUksRUFBQyxNQUFNLHVCQUF1QixDQUFDO0FBRW5FLE9BQU8sRUFBQyxPQUFPLEVBQUMsTUFBTSxrQkFBa0IsQ0FBQztBQUN6QyxPQUFPLEtBQUssQ0FBQyxNQUFNLHdCQUF3QixDQUFDO0FBQzVDLE9BQU8sRUFBQyxVQUFVLEVBQUMsTUFBTSxVQUFVLENBQUM7QUFHcEM7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSxXQUFXLENBQUMsQ0FBUyxFQUFFLElBQWE7SUFDbEQsT0FBTyxJQUFJLENBQUMsR0FBRyxFQUFFO1FBQ2YsSUFBSSxDQUFDLENBQUMsS0FBSyxLQUFLLFNBQVMsRUFBRTtZQUN6QixDQUFDLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLEVBQUUsU0FBUyxDQUFDLENBQUM7U0FDNUI7UUFDRCxNQUFNLFNBQVMsR0FBRyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQ25ELE1BQU0sYUFBYSxHQUFHLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQzNELE1BQU0sSUFBSSxHQUFHLEdBQUcsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsYUFBYSxDQUFDLENBQUMsQ0FBQztRQUM3RCxPQUFPLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQzFCLENBQUMs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SUFDM0IsWUFBWTtJQUNaLEtBQUs7SUFDTCxnQkFBZ0I7SUFDaEIsT0FBTztJQUNQLHVCQUF1QjtJQUN2Qiw2QkFBNkI7SUFDN0Isa0JBQWtCO0lBQ2xCLHlCQUF5QjtJQUN6QixPQUFPO0lBQ1AsZUFBZTtDQUNoQixDQUFDO0FBRUYsOEVBQThFO0FBQzlFLGtEQUFrRDtBQUNsRCxNQUFNLFVBQVUsR0FBRyxDQUFDLGNBQXFDO0lBQ3ZELElBQUksT0FBTyxjQUFjLEtBQUssUUFBUSxFQUFFO1FBQ3RDLElBQUksY0FBYyxJQUFJLFNBQVMsRUFBRTtZQUMvQixPQUFPLFNBQVMsQ0FBQyxjQUFjLENBQUMsQ0FBQztTQUNsQztRQUNELElBQUksTUFBTSxHQUFHLGdCQUFnQixjQUFjLEVBQUUsQ0FBQztRQUM5QyxJQUFJLGNBQWMsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxRQUFRLENBQUMscUJBQXFCLENBQUMsRUFBRTtZQUNoRSxNQUFNLEdBQUcsZ0JBQWdCLGNBQWMsSUFBSTtnQkFDdkMsdURBQXVEO2dCQUN2RCwrQkFBK0IsQ0FBQztTQUNyQztRQUNELE1BQU0sSUFBSSxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUM7S0FDOUI7U0FBTTtRQUNMLE9BQU8sY0FBYyxDQUFDO0tBQ3ZCO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCAyMDE4IEdvb2dsZSBMTENcbiAqXG4gKiBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhbiBNSVQtc3R5bGVcbiAqIGxpY2Vuc2UgdGhhdCBjYW4gYmUgZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBvciBhdFxuICogaHR0cHM6Ly9vcGVuc291cmNlLm9yZy9saWNlbnNlcy9NSVQuXG4gKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICovXG5cbi8qIE9yaWdpbmFsIFNvdXJjZTogbG9zc2VzLnB5ICovXG5pbXBvcnQgKiBhcyB0ZmMgZnJvbSAnQHRlbnNvcmZsb3cvdGZqcy1jb3JlJztcbmltcG9ydCB7VGVuc29yLCBUZW5zb3IxRCwgdGlkeSwgdXRpbH0gZnJvbSAnQHRlbnNvcmZsb3cvdGZqcy1jb3JlJztcblxuaW1wb3J0IHtlcHNpbG9ufSBmcm9tICcuL2JhY2tlbmQvY29tbW9uJztcbmltcG9ydCAqIGFzIEsgZnJvbSAnLi9iYWNrZW5kL3RmanNfYmFja2VuZCc7XG5pbXBvcnQge1ZhbHVlRXJyb3J9IGZyb20gJy4vZXJyb3JzJztcbmltcG9ydCB7TG9zc09yTWV0cmljRm59IGZyb20gJy4vdHlwZXMnO1xuXG4vKipcbiAqIE5vcm1hbGl6ZXMgYSB0ZW5zb3Igd3J0IHRoZSBMMiBub3JtIGFsb25nc2lkZSB0aGUgc3BlY2lmaWVkIGF4aXMuXG4gKiBAcGFyYW0geFxuICogQHBhcmFtIGF4aXMgQXhpcyBhbG9uZyB3aGljaCB0byBwZXJmb3JtIG5vcm1hbGl6YXRpb24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsMk5vcm1hbGl6ZSh4OiBUZW5zb3IsIGF4aXM/OiBudW1iZXIpOiBUZW5zb3Ige1xuICByZXR1cm4gdGlkeSgoKSA9PiB7XG4gICAgaWYgKHguZHR5cGUgIT09ICdmbG9hdDMyJykge1xuICAgICAgeCA9IHRmYy5jYXN0KHgsICdmbG9hdDMyJyk7XG4gICAgfVxuICAgIGNvbnN0IHNxdWFyZVN1bSA9IHRmYy5zdW0oSy5zcXVhcmUoeCksIGF4aXMsIHRydWUpO1xuICAgIGNvbnN0IGVwc2lsb25UZW5zb3IgPSB0ZmMuZmlsbChzcXVhcmVTdW0uc2hhcGUsIGVwc2lsb24oKSk7XG4gICAgY29uc3Qgbm9ybSA9IHRmYy5zcXJ0KHRmYy5tYXhpbXVtKHNxdWFyZVN1bSwgZXBzaWxvblRlbnNvcikpO1xuICAgIHJldHVybiB0ZmMuZGl2KHgsIG5vcm0pO1xuICB9KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1lYW5TcXVhcmVkRXJyb3IoeVRydWU6IFRlbnNvciwgeVByZWQ6IFRlbnNvcik6IFRlbnNvciB7XG4gIHJldHVybiB0aWR5KCgpID0+IHRmYy5tZWFuKEsuc3F1YXJlKHRmYy5zdWIoeVByZWQsIHlUcnVlKSksIC0xKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtZWFuQWJzb2x1dGVFcnJvcih5VHJ1ZTogVGVuc29yLCB5UHJlZDogVGVuc29yKTogVGVuc29yIHtcbiAgcmV0dXJuIHRpZHkoKCkgPT4gdGZjLm1lYW4odGZjLmFicyh0ZmMuc3ViKHlQcmVkLCB5VHJ1ZSkpLCAtMSkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbWVhbkFic29sdXRlUGVyY2VudGFnZUVycm9yKFxuICAgIHlUcnVlOiBUZW5zb3IsIHlQcmVkOiBUZW5zb3IpOiBUZW5zb3Ige1xuICByZXR1cm4gdGlkeSgoKSA9PiB7XG4gICAgY29uc3QgZGlmZiA9IHRmYy5zdWIoeVRydWUsIHlQcmVkKTtcbiAgICBjb25zdCBjbGlwcGVkVHJ1ZSA9XG4gICAgICAgIHRmYy5jbGlwQnlWYWx1ZSh0ZmMuYWJzKHlUcnVlKSwgZXBzaWxvbigpLCBOdW1iZXIuTUFYX1ZBTFVFKTtcbiAgICBjb25zdCBhYnNSZXN1bHQgPSB0ZmMuYWJzKHRmYy5kaXYoZGlmZiwgY2xpcHBlZFRydWUpKTtcbiAgICByZXR1cm4gdGZjLm11bCgxMDAsIHRmYy5tZWFuKGFic1Jlc3VsdCwgLTEpKTtcbiAgfSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtZWFuU3F1YXJlZExvZ2FyaXRobWljRXJyb3IoXG4gICAgeVRydWU6IFRlbnNvciwgeVByZWQ6IFRlbnNvcik6IFRlbnNvciB7XG4gIHJldHVybiB0aWR5KCgpID0+IHtcbiAgICBjb25zdCBjbGlwcGVkUHJlZCA9IHRmYy5jbGlwQnlWYWx1ZSh5UHJlZCwgZXBzaWxvbigpLCBOdW1iZXIuTUFYX1ZBTFVFKTtcbiAgICBjb25zdCBmaXJzdExvZyA9IHRmYy5sb2codGZjLmFkZCgxLCBjbGlwcGVkUHJlZCkpO1xuXG4gICAgY29uc3QgY2xpcHBlZFRydWUgPSB0ZmMuY2xpcEJ5VmFsdWUoeVRydWUsIGVwc2lsb24oKSwgTnVtYmVyLk1BWF9WQUxVRSk7XG4gICAgY29uc3Qgc2Vjb25kTG9nID0gdGZjLmxvZyh0ZmMuYWRkKDEsIGNsaXBwZWRUcnVlKSk7XG5cbiAgICByZXR1cm4gdGZjLm1lYW4oSy5zcXVhcmUodGZjLnN1YihmaXJzdExvZywgc2Vjb25kTG9nKSksIC0xKTtcbiAgfSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzcXVhcmVkSGluZ2UoeVRydWU6IFRlbnNvciwgeVByZWQ6IFRlbnNvcik6IFRlbnNvciB7XG4gIHJldHVybiB0aWR5KCgpID0+IHtcbiAgICBjb25zdCBtYXhSZXN1bHQgPSB0ZmMubWF4aW11bSgwLCB0ZmMuc3ViKDEsIHRmYy5tdWwoeVRydWUsIHlQcmVkKSkpO1xuICAgIHJldHVybiB0ZmMubWVhbihLLnNxdWFyZShtYXhSZXN1bHQpLCAtMSk7XG4gIH0pO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaGluZ2UoeVRydWU6IFRlbnNvciwgeVByZWQ6IFRlbnNvcik6IFRlbnNvciB7XG4gIHJldHVybiB0aWR5KCgpID0+IHtcbiAgICBjb25zdCBtYXhSZXN1bHQgPSB0ZmMubWF4aW11bSgwLCB0ZmMuc3ViKDEsIHRmYy5tdWwoeVRydWUsIHlQcmVkKSkpO1xuICAgIHJldHVybiB0ZmMubWVhbihtYXhSZXN1bHQsIC0xKTtcbiAgfSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjYXRlZ29yaWNhbEhpbmdlKHlUcnVlOiBUZW5zb3IsIHlQcmVkOiBUZW5zb3IpOiBUZW5zb3Ige1xuICByZXR1cm4gdGlkeSgoKSA9PiB7XG4gICAgY29uc3QgcG9zID0gdGZjLnN1bSh0ZmMubXVsKHlUcnVlLCB5UHJlZCksIC0xKTtcbiAgICBjb25zdCBuZWcgPSB0ZmMubWF4KHRmYy5tdWwodGZjLnN1YigxLCB5VHJ1ZSksIHlQcmVkKSwgLTEpO1xuICAgIHJldHVybiB0ZmMubWF4aW11bSgwLCB0ZmMuYWRkKDEsIHRmYy5zdWIobmVnLCBwb3MpKSk7XG4gIH0pO1xufVxuXG4vKipcbiAqIExvZ2FyaXRobSBvZiB0aGUgaHlwZXJib2xpYyBjb3NpbmUgb2YgdGhlIHByZWRpY3Rpb24gZXJyb3IuXG4gKlxuICogYGxvZyhjb3NoKHgpKWAgaXMgYXBwcm94aW1hdGVseSBlcXVhbCB0byBgKHggKiogMikgLyAyYCBmb3Igc21hbGwgYHhgIGFuZFxuICogdG8gYGFicyh4KSAtIGxvZygyKWAgZm9yIGxhcmdlIGB4YC4gVGhpcyBtZWFucyB0aGF0ICdsb2djb3NoJyB3b3JrcyBtb3N0bHlcbiAqIGxpa2UgdGhlIG1lYW4gc3F1YXJlZCBlcnJvciwgYnV0IHdpbGwgbm90IGJlIHNvIHN0cm9uZ2x5IGFmZmVjdGVkIGJ5IHRoZVxuICogb2NjYXNpb25hbCB3aWxkbHkgaW5jb3JyZWN0IHByZWRpY3Rpb24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsb2djb3NoKHlUcnVlOiBUZW5zb3IsIHlQcmVkOiBUZW5zb3IpOiBUZW5zb3Ige1xuICByZXR1cm4gdGlkeSgoKSA9PiB7XG4gICAgY29uc3QgbG9nMiA9IE1hdGgubG9nKDIpO1xuICAgIGNvbnN0IHByZWRpY3Rpb25EaWZmID0gdGZjLnN1Yih5UHJlZCwgeVRydWUpO1xuICAgIGNvbnN0IGxvZ2Nvc2hSZXN1bHQgPSB0ZmMuc3ViKFxuICAgICAgICB0ZmMuYWRkKHByZWRpY3Rpb25EaWZmLCB0ZmMuc29mdHBsdXModGZjLm11bCgtMiwgcHJlZGljdGlvbkRpZmYpKSksXG4gICAgICAgIGxvZzIpO1xuICAgIHJldHVybiB0ZmMubWVhbihsb2djb3NoUmVzdWx0LCAtMSk7XG4gIH0pO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY2F0ZWdvcmljYWxDcm9zc2VudHJvcHkoXG4gICAgdGFyZ2V0OiBUZW5zb3IsIG91dHB1dDogVGVuc29yLCBmcm9tTG9naXRzID0gZmFsc2UpOiBUZW5zb3Ige1xuICByZXR1cm4gdGlkeSgoKSA9PiB7XG4gICAgaWYgKGZyb21Mb2dpdHMpIHtcbiAgICAgIG91dHB1dCA9IHRmYy5zb2Z0bWF4KG91dHB1dCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIHNjYWxlIHByZWRzIHNvIHRoYXQgdGhlIGNsYXNzIHByb2JhYmlsaXRpZXMgb2YgZWFjaCBzYW1wbGUgc3VtIHRvIDEuXG4gICAgICBjb25zdCBvdXRwdXRTdW0gPSB0ZmMuc3VtKG91dHB1dCwgb3V0cHV0LnNoYXBlLmxlbmd0aCAtIDEsIHRydWUpO1xuICAgICAgb3V0cHV0ID0gdGZjLmRpdihvdXRwdXQsIG91dHB1dFN1bSk7XG4gICAgfVxuICAgIG91dHB1dCA9IHRmYy5jbGlwQnlWYWx1ZShvdXRwdXQsIGVwc2lsb24oKSwgMSAtIGVwc2lsb24oKSk7XG4gICAgcmV0dXJuIHRmYy5uZWcodGZjLnN1bShcbiAgICAgICAgdGZjLm11bCh0ZmMuY2FzdCh0YXJnZXQsICdmbG9hdDMyJyksIHRmYy5sb2cob3V0cHV0KSksXG4gICAgICAgIG91dHB1dC5zaGFwZS5sZW5ndGggLSAxKSk7XG4gIH0pO1xufVxuXG4vKipcbiAqIENhdGVnb3JpY2FsIGNyb3NzZW50cm9weSB3aXRoIGludGVnZXIgdGFyZ2V0cy5cbiAqXG4gKiBAcGFyYW0gdGFyZ2V0IEFuIGludGVnZXIgdGVuc29yLlxuICogQHBhcmFtIG91dHB1dCBBIHRlbnNvciByZXN1bHRpbmcgZnJvbSBhIHNvZnRtYXggKHVubGVzcyBgZnJvbUxvZ2l0c2AgaXNcbiAqICBgdHJ1ZWAsIGluIHdoaWNoIGNhc2UgYG91dHB1dGAgaXMgZXhwZWN0ZWQgdG8gYmUgdGhlIGxvZ2l0cykuXG4gKiBAcGFyYW0gZnJvbUxvZ2l0cyBCb29sZWFuLCB3aGV0aGVyIGBvdXRwdXRgIGlzIHRoZSByZXN1bHQgb2YgYSBzb2Z0bWF4LCBvciBpc1xuICogICBhIHRlbnNvciBvZiBsb2dpdHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzcGFyc2VDYXRlZ29yaWNhbENyb3NzZW50cm9weShcbiAgICB0YXJnZXQ6IFRlbnNvciwgb3V0cHV0OiBUZW5zb3IsIGZyb21Mb2dpdHMgPSBmYWxzZSk6IFRlbnNvciB7XG4gIHJldHVybiB0aWR5KCgpID0+IHtcbiAgICBjb25zdCBmbGF0VGFyZ2V0ID1cbiAgICAgICAgdGZjLmNhc3QodGZjLmZsb29yKEsuZmxhdHRlbih0YXJnZXQpKSwgJ2ludDMyJykgYXMgVGVuc29yMUQ7XG4gICAgb3V0cHV0ID0gdGZjLmNsaXBCeVZhbHVlKG91dHB1dCwgZXBzaWxvbigpLCAxIC0gZXBzaWxvbigpKTtcbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9IG91dHB1dC5zaGFwZTtcbiAgICBjb25zdCBvbmVIb3RUYXJnZXQgPSB0ZmMucmVzaGFwZShcbiAgICAgICAgdGZjLm9uZUhvdChmbGF0VGFyZ2V0LCBvdXRwdXRTaGFwZVtvdXRwdXRTaGFwZS5sZW5ndGggLSAxXSksXG4gICAgICAgIG91dHB1dFNoYXBlKTtcbiAgICByZXR1cm4gY2F0ZWdvcmljYWxDcm9zc2VudHJvcHkob25lSG90VGFyZ2V0LCBvdXRwdXQsIGZyb21Mb2dpdHMpO1xuICB9KTtcbn1cblxuLyoqXG4gKiBGcm9tIFRlbnNvckZsb3cncyBpbXBsZW1lbnRhdGlvbiBpbiBubl9pbXBsLnB5OlxuICpcbiAqIEZvciBicmV2aXR5LCBsZXQgYHggPSBsb2dpdHNgLCBgeiA9IGxhYmVsc2AuICBUaGUgbG9naXN0aWMgbG9zcyBpc1xuICogICAgICB6ICogLWxvZyhzaWdtb2lkKHgpKSArICgxIC0geikgKiAtbG9nKDEgLSBzaWdtb2lkKHgpKVxuICogICAgPSB6ICogLWxvZygxIC8gKDEgKyBleHAoLXgpKSkgKyAoMSAtIHopICogLWxvZyhleHAoLXgpIC8gKDEgKyBleHAoLXgpKSlcbiAqICAgID0geiAqIGxvZygxICsgZXhwKC14KSkgKyAoMSAtIHopICogKC1sb2coZXhwKC14KSkgKyBsb2coMSArIGV4cCgteCkpKVxuICogICAgPSB6ICogbG9nKDEgKyBleHAoLXgpKSArICgxIC0geikgKiAoeCArIGxvZygxICsgZXhwKC14KSlcbiAqICAgID0gKDEgLSB6KSAqIHggKyBsb2coMSArIGV4cCgteCkpXG4gKiAgICA9IHggLSB4ICogeiArIGxvZygxICsgZXhwKC14KSlcbiAqIEZvciB4IDwgMCwgdG8gYXZvaWQgb3ZlcmZsb3cgaW4gZXhwKC14KSwgd2UgcmVmb3JtdWxhdGUgdGhlIGFib3ZlXG4gKiAgICAgIHggLSB4ICogeiArIGxvZygxICsgZXhwKC14KSlcbiAqICAgID0gbG9nKGV4cCh4KSkgLSB4ICogeiArIGxvZygxICsgZXhwKC14KSlcbiAqICAgID0gLSB4ICogeiArIGxvZygxICsgZXhwKHgpKVxuICogSGVuY2UsIHRvIGVuc3VyZSBzdGFiaWxpdHkgYW5kIGF2b2lkIG92ZXJmbG93LCB0aGUgaW1wbGVtZW50YXRpb24gdXNlcyB0aGlzXG4gKiBlcXVpdmFsZW50IGZvcm11bGF0aW9uXG4gKiAgICBtYXgoeCwgMCkgLSB4ICogeiArIGxvZygxICsgZXhwKC1hYnMoeCkpKVxuICpcbiAqIEBwYXJhbSBsYWJlbHMgVGhlIGxhYmVscy5cbiAqIEBwYXJhbSBsb2dpdHMgVGhlIGxvZ2l0cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNpZ21vaWRDcm9zc0VudHJvcHlXaXRoTG9naXRzKFxuICAgIGxhYmVsczogVGVuc29yLCBsb2dpdHM6IFRlbnNvcik6IFRlbnNvciB7XG4gIGlmICghdXRpbC5hcnJheXNFcXVhbChsYWJlbHMuc2hhcGUsIGxvZ2l0cy5zaGFwZSkpIHtcbiAgICB0aHJvdyBuZXcgVmFsdWVFcnJvcihcbiAgICAgICAgYGxvZ2l0cyBhbmQgbGFiZWxzIG11c3QgaGF2ZSB0aGUgc2FtZSBzaGFwZSwgYnV0IGdvdCBzaGFwZXMgYCArXG4gICAgICAgIGAke0pTT04uc3RyaW5naWZ5KGxhYmVscy5zaGFwZSl9IGFuZCAke0pTT04uc3RyaW5naWZ5KGxvZ2l0cy5zaGFwZSl9YCk7XG4gIH1cbiAgcmV0dXJuIHRpZHkoKCkgPT4ge1xuICAgIC8vIFRoZSBsb2dpc3RpYyBsb3NzIGZvcm11bGEgZnJvbSBhYm92ZSBpc1xuICAgIC8vICAgeCAtIHggKiB6ICsgbG9nKDEgKyBleHAoLXgpKVxuICAgIC8vIEZvciB4IDwgMCwgYSBtb3JlIG51bWVyaWNhbGx5IHN0YWJsZSBmb3JtdWxhIGlzXG4gICAgLy8gICAteCAqIHogKyBsb2coMSArIGV4cCh4KSlcbiAgICAvLyBOb3RlIHRoYXQgdGhlc2UgdHdvIGV4cHJlc3Npb25zIGNhbiBiZSBjb21iaW5lZCBpbnRvIHRoZSBmb2xsb3dpbmc6XG4gICAgLy8gICBtYXgoeCwgMCkgLSB4ICogeiArIGxvZygxICsgZXhwKC1hYnMoeCkpKVxuICAgIGNvbnN0IHJlbHVMb2dpdHMgPSB0ZmMucmVsdShsb2dpdHMpO1xuICAgIGNvbnN0IG5lZ0Fic0xvZ2l0cyA9IHRmYy5uZWcodGZjLmFicyhsb2dpdHMpKTtcbiAgICByZXR1cm4gdGZjLmFkZChcbiAgICAgICAgdGZjLnN1YihyZWx1TG9naXRzLCB0ZmMubXVsKGxvZ2l0cywgbGFiZWxzKSksXG4gICAgICAgIHRmYy5sb2cxcCh0ZmMuZXhwKG5lZ0Fic0xvZ2l0cykpKTtcbiAgfSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBiaW5hcnlDcm9zc2VudHJvcHkoeVRydWU6IFRlbnNvciwgeVByZWQ6IFRlbnNvcik6IFRlbnNvciB7XG4gIHJldHVybiB0aWR5KCgpID0+IHtcbiAgICBsZXQgeTogVGVuc29yO1xuICAgIHkgPSB0ZmMuY2xpcEJ5VmFsdWUoeVByZWQsIGVwc2lsb24oKSwgMSAtIGVwc2lsb24oKSk7XG4gICAgeSA9IHRmYy5sb2codGZjLmRpdih5LCB0ZmMuc3ViKDEsIHkpKSk7XG4gICAgcmV0dXJuIHRmYy5tZWFuKHNpZ21vaWRDcm9zc0VudHJvcHlXaXRoTG9naXRzKHlUcnVlLCB5KSwgLTEpO1xuICB9KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGt1bGxiYWNrTGVpYmxlckRpdmVyZ2VuY2UoXG4gICAgeVRydWU6IFRlbnNvciwgeVByZWQ6IFRlbnNvcik6IFRlbnNvciB7XG4gIHJldHVybiB0aWR5KCgpID0+IHtcbiAgICBjb25zdCBjbGlwcGVkVHJ1ZSA9IHRmYy5jbGlwQnlWYWx1ZSh5VHJ1ZSwgZXBzaWxvbigpLCAxKTtcbiAgICBjb25zdCBjbGlwcGVkUHJlZCA9IHRmYy5jbGlwQnlWYWx1ZSh5UHJlZCwgZXBzaWxvbigpLCAxKTtcbiAgICByZXR1cm4gdGZjLnN1bShcbiAgICAgICAgdGZjLm11bCh5VHJ1ZSwgdGZjLmxvZyh0ZmMuZGl2KGNsaXBwZWRUcnVlLCBjbGlwcGVkUHJlZCkpKSwgLTEpO1xuICB9KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBvaXNzb24oeVRydWU6IFRlbnNvciwgeVByZWQ6IFRlbnNvcik6IFRlbnNvciB7XG4gIHJldHVybiB0aWR5KCgpID0+IHtcbiAgICBjb25zdCBsb2dQcmVkID0gdGZjLmxvZyh0ZmMuYWRkKGVwc2lsb24oKSwgeVByZWQpKTtcbiAgICByZXR1cm4gdGZjLm1lYW4odGZjLnN1Yih5UHJlZCwgdGZjLm11bCh5VHJ1ZSwgbG9nUHJlZCkpLCAtMSk7XG4gIH0pO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29zaW5lUHJveGltaXR5KHlUcnVlOiBUZW5zb3IsIHlQcmVkOiBUZW5zb3IpOiBUZW5zb3Ige1xuICByZXR1cm4gdGlkeSgoKSA9PiB7XG4gICAgY29uc3QgdHJ1ZU5vcm1hbGl6ZWQgPSBsMk5vcm1hbGl6ZSh5VHJ1ZSwgLTEpO1xuICAgIGNvbnN0IHByZWROb3JtYWxpemVkID0gbDJOb3JtYWxpemUoeVByZWQsIC0xKTtcbiAgICBjb25zdCB0cnVlWFByZWQgPSB0ZmMubXVsKHRydWVOb3JtYWxpemVkLCBwcmVkTm9ybWFsaXplZCk7XG4gICAgcmV0dXJuIHRmYy5uZWcodGZjLnN1bSh0cnVlWFByZWQsIC0xKSk7XG4gIH0pO1xufVxuXG5leHBvcnQgY29uc3QgbXNlID0gbWVhblNxdWFyZWRFcnJvcjtcbmV4cG9ydCBjb25zdCBNU0UgPSBtZWFuU3F1YXJlZEVycm9yO1xuZXhwb3J0IGNvbnN0IG1hZSA9IG1lYW5BYnNvbHV0ZUVycm9yO1xuZXhwb3J0IGNvbnN0IE1BRSA9IG1lYW5BYnNvbHV0ZUVycm9yO1xuZXhwb3J0IGNvbnN0IG1hcGUgPSBtZWFuQWJzb2x1dGVQZXJjZW50YWdlRXJyb3I7XG5leHBvcnQgY29uc3QgTUFQRSA9IG1lYW5BYnNvbHV0ZVBlcmNlbnRhZ2VFcnJvcjtcbmV4cG9ydCBjb25zdCBtc2xlID0gbWVhblNxdWFyZWRMb2dhcml0aG1pY0Vycm9yO1xuZXhwb3J0IGNvbnN0IE1TTEUgPSBtZWFuU3F1YXJlZExvZ2FyaXRobWljRXJyb3I7XG5leHBvcnQgY29uc3Qga2xkID0ga3VsbGJhY2tMZWlibGVyRGl2ZXJnZW5jZTtcbmV4cG9ydCBjb25zdCBLTEQgPSBrdWxsYmFja0xlaWJsZXJEaXZlcmdlbmNlO1xuZXhwb3J0IGNvbnN0IGNvc2luZSA9IGNvc2luZVByb3hpbWl0eTtcblxuLy8gVE9ETyhtaWNoYWVsdGVycnkpOiBBZGQgZGVzZXJpYWxpemUoKSBmdW5jdGlvbi5cblxuZXhwb3J0IGNvbnN0IGxvc3Nlc01hcDoge1tmdW5jdGlvbk5hbWU6IHN0cmluZ106IExvc3NPck1ldHJpY0ZufSA9IHtcbiAgbWVhblNxdWFyZWRFcnJvcixcbiAgbWVhbkFic29sdXRlRXJyb3IsXG4gIG1lYW5BYnNvbHV0ZVBlcmNlbnRhZ2VFcnJvcixcbiAgbWVhblNxdWFyZWRMb2dhcml0aG1pY0Vycm9yLFxuICBzcXVhcmVkSGluZ2UsXG4gIGhpbmdlLFxuICBjYXRlZ29yaWNhbEhpbmdlLFxuICBsb2djb3NoLFxuICBjYXRlZ29yaWNhbENyb3NzZW50cm9weSxcbiAgc3BhcnNlQ2F0ZWdvcmljYWxDcm9zc2VudHJvcHksXG4gIGJpbmFyeUNyb3NzZW50cm9weSxcbiAga3VsbGJhY2tMZWlibGVyRGl2ZXJnZW5jZSxcbiAgcG9pc3NvbixcbiAgY29zaW5lUHJveGltaXR5XG59O1xuXG4vLyBQb3J0aW5nIG5vdGU6IFRoaXMgZGl2ZXJnZXMgZnJvbSB0aGUgUHlLZXJhcyBpbXBsZW1lbnRhdGlvbiBhbmQgbWF5IG5lZWQgdG9cbi8vIGNoYW5nZSBiYXNlZCBvbiAoZGUpc2VyaWFsaXphdGlvbiByZXF1aXJlbWVudHMuXG5leHBvcnQgZnVuY3Rpb24gZ2V0KGlkZW50aWZpZXJPckZuOiBzdHJpbmd8TG9zc09yTWV0cmljRm4pOiBMb3NzT3JNZXRyaWNGbiB7XG4gIGlmICh0eXBlb2YgaWRlbnRpZmllck9yRm4gPT09ICdzdHJpbmcnKSB7XG4gICAgaWYgKGlkZW50aWZpZXJPckZuIGluIGxvc3Nlc01hcCkge1xuICAgICAgcmV0dXJuIGxvc3Nlc01hcFtpZGVudGlmaWVyT3JGbl07XG4gICAgfVxuICAgIGxldCBlcnJNc2cgPSBgVW5rbm93biBsb3NzICR7aWRlbnRpZmllck9yRm59YDtcbiAgICBpZiAoaWRlbnRpZmllck9yRm4udG9Mb3dlckNhc2UoKS5pbmNsdWRlcygnc29mdG1heGNyb3NzZW50cm9weScpKSB7XG4gICAgICBlcnJNc2cgPSBgVW5rbm93biBsb3NzICR7aWRlbnRpZmllck9yRm59LiBgICtcbiAgICAgICAgICAnVXNlIFwiY2F0ZWdvcmljYWxDcm9zc2VudHJvcHlcIiBhcyB0aGUgc3RyaW5nIG5hbWUgZm9yICcgK1xuICAgICAgICAgICd0Zi5sb3NzZXMuc29mdG1heENyb3NzRW50cm9weSc7XG4gICAgfVxuICAgIHRocm93IG5ldyBWYWx1ZUVycm9yKGVyck1zZyk7XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIGlkZW50aWZpZXJPckZuO1xuICB9XG59XG4iXX0=