/** * @license * Copyright 2019 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 { ENGINE } from '../../engine'; import { customGrad } from '../../gradients'; import { _FusedMatMul } from '../../kernel_names'; import { makeTypesMatch } from '../../tensor_util'; import { convertToTensor } from '../../tensor_util_env'; import * as util from '../../util'; import { add } from '../add'; import * as broadcast_util from '../broadcast_util'; import { applyActivation, getFusedBiasGradient, getFusedDyActivation, shouldFuse } from '../fused_util'; import { matMul as unfusedMatMul } from '../mat_mul'; import { op } from '../operation'; import { reshape } from '../reshape'; /** * Computes the dot product of two matrices with optional activation and bias. * * ```js * const a = tf.tensor2d([-1, -2], [1, 2]); * const b = tf.tensor2d([1, 2, 3, 4], [2, 2]); * const bias = tf.tensor2d([1, 2], [1, 2]); * * tf.fused.matMul({a, b, bias, activation: 'relu'}).print(); * ``` * * @param obj An object with the following properties: * - `a` First matrix in dot product operation. * - `b` Second matrix in dot product operation. * - `transposeA` If true, `a` is transposed before multiplication. * - `transposeB` If true, `b` is transposed before multiplication. * - `bias` Matrix to be added to the result. * - `activation` Name of activation kernel (defaults to `linear`). * - `preluActivationWeights` Tensor of prelu weights. * - `leakyreluAlpha` Alpha of leakyrelu. */ function fusedMatMul_({ a, b, transposeA = false, transposeB = false, bias, activation = 'linear', preluActivationWeights, leakyreluAlpha = 0.2, }) { if (shouldFuse(ENGINE.state.gradientDepth, activation) === false) { let result = unfusedMatMul(a, b, transposeA, transposeB); if (bias != null) { result = add(result, bias); } return applyActivation(result, activation, preluActivationWeights, leakyreluAlpha); } let $a = convertToTensor(a, 'a', 'fused matMul'); let $b = convertToTensor(b, 'b', 'fused matMul'); [$a, $b] = makeTypesMatch($a, $b); const innerShapeA = transposeA ? $a.shape[$a.rank - 2] : $a.shape[$a.rank - 1]; const innerShapeB = transposeB ? $b.shape[$b.rank - 1] : $b.shape[$b.rank - 2]; const outerShapeA = transposeA ? $a.shape[$a.rank - 1] : $a.shape[$a.rank - 2]; const outerShapeB = transposeB ? $b.shape[$b.rank - 2] : $b.shape[$b.rank - 1]; const outerDimsA = $a.shape.slice(0, -2); const outerDimsB = $b.shape.slice(0, -2); const batchDimA = util.sizeFromShape(outerDimsA); const batchDimB = util.sizeFromShape(outerDimsB); util.assert(innerShapeA === innerShapeB, () => `Error in fused matMul: inner shapes (${innerShapeA}) and (` + `${innerShapeB}) of Tensors with shapes ${$a.shape} and ` + `${$b.shape} and transposeA=${transposeA}` + ` and transposeB=${transposeB} must match.`); const outShapeOuterDims = broadcast_util.assertAndGetBroadcastShape($a.shape.slice(0, -2), $b.shape.slice(0, -2)); const outShape = outShapeOuterDims.concat([outerShapeA, outerShapeB]); const a3D = transposeA ? reshape($a, [batchDimA, innerShapeA, outerShapeA]) : reshape($a, [batchDimA, outerShapeA, innerShapeA]); const b3D = transposeB ? reshape($b, [batchDimB, outerShapeB, innerShapeB]) : reshape($b, [batchDimB, innerShapeB, outerShapeB]); let $bias; if (bias != null) { $bias = convertToTensor(bias, 'bias', 'fused matMul'); [$bias] = makeTypesMatch($bias, $a); broadcast_util.assertAndGetBroadcastShape(outShape, $bias.shape); } let $preluActivationWeights; if (preluActivationWeights != null) { $preluActivationWeights = convertToTensor(preluActivationWeights, 'prelu weights', 'fused matMul'); } const grad = (dy, saved) => { const [a3D, b3D, y, $bias] = saved; // we reshape dy because the result of the forward is not // necessarily going to be a 3d tensor due to a reshape done at the end of // the customOp. const dyActivation = getFusedDyActivation(reshape(dy, y.shape), y, activation); let aDer; let bDer; if (!transposeA && !transposeB) { aDer = unfusedMatMul(dyActivation, b3D, false, true); bDer = unfusedMatMul(a3D, dyActivation, true, false); } else if (!transposeA && transposeB) { aDer = unfusedMatMul(dyActivation, b3D, false, false); bDer = unfusedMatMul(dyActivation, a3D, true, false); } else if (transposeA && !transposeB) { aDer = unfusedMatMul(b3D, dyActivation, false, true); bDer = unfusedMatMul(a3D, dyActivation, false, false); } else { aDer = unfusedMatMul(b3D, dyActivation, true, true); bDer = unfusedMatMul(dyActivation, a3D, true, true); } if (bias != null) { const biasDer = getFusedBiasGradient($bias, dyActivation); return [aDer, bDer, biasDer]; } else { return [aDer, bDer]; } }; const inputs = { a: a3D, b: b3D, bias: $bias, preluActivationWeights: $preluActivationWeights }; const attrs = { transposeA, transposeB, activation, leakyreluAlpha }; // Depending on the the params passed in we will have different number of // inputs and thus a a different number of elements in the gradient. if (bias == null) { const customOp = customGrad((a3D, b3D, save) => { const res = // tslint:disable-next-line: no-unnecessary-type-assertion ENGINE.runKernel(_FusedMatMul, inputs, attrs); save([a3D, b3D, res]); return { value: reshape(res, outShape), gradFunc: grad }; }); return customOp(a3D, b3D); } else { const customOpWithBias = customGrad((a3D, b3D, $bias, save) => { const res = // tslint:disable-next-line: no-unnecessary-type-assertion ENGINE.runKernel(_FusedMatMul, inputs, attrs); save([a3D, b3D, res, $bias]); return { value: reshape(res, outShape), gradFunc: grad }; }); return customOpWithBias(a3D, b3D, $bias); } } export const matMul = /* @__PURE__ */ op({ fusedMatMul_ }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWF0X211bC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3RmanMtY29yZS9zcmMvb3BzL2Z1c2VkL21hdF9tdWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBRUgsT0FBTyxFQUFDLE1BQU0sRUFBQyxNQUFNLGNBQWMsQ0FBQztBQUNwQyxPQUFPLEVBQUMsVUFBVSxFQUFDLE1BQU0saUJBQWlCLENBQUM7QUFDM0MsT0FBTyxFQUFDLFlBQVksRUFBd0MsTUFBTSxvQkFBb0IsQ0FBQztBQUl2RixPQUFPLEVBQUMsY0FBYyxFQUFDLE1BQU0sbUJBQW1CLENBQUM7QUFDakQsT0FBTyxFQUFDLGVBQWUsRUFBQyxNQUFNLHVCQUF1QixDQUFDO0FBRXRELE9BQU8sS0FBSyxJQUFJLE1BQU0sWUFBWSxDQUFDO0FBRW5DLE9BQU8sRUFBQyxHQUFHLEVBQUMsTUFBTSxRQUFRLENBQUM7QUFDM0IsT0FBTyxLQUFLLGNBQWMsTUFBTSxtQkFBbUIsQ0FBQztBQUVwRCxPQUFPLEVBQUMsZUFBZSxFQUFFLG9CQUFvQixFQUFFLG9CQUFvQixFQUFFLFVBQVUsRUFBQyxNQUFNLGVBQWUsQ0FBQztBQUN0RyxPQUFPLEVBQUMsTUFBTSxJQUFJLGFBQWEsRUFBQyxNQUFNLFlBQVksQ0FBQztBQUNuRCxPQUFPLEVBQUMsRUFBRSxFQUFDLE1BQU0sY0FBYyxDQUFDO0FBQ2hDLE9BQU8sRUFBQyxPQUFPLEVBQUMsTUFBTSxZQUFZLENBQUM7QUFFbkM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0JHO0FBQ0gsU0FBUyxZQUFZLENBQUMsRUFDcEIsQ0FBQyxFQUNELENBQUMsRUFDRCxVQUFVLEdBQUcsS0FBSyxFQUNsQixVQUFVLEdBQUcsS0FBSyxFQUNsQixJQUFJLEVBQ0osVUFBVSxHQUFHLFFBQVEsRUFDckIsc0JBQXNCLEVBQ3RCLGNBQWMsR0FBRyxHQUFHLEdBVXJCO0lBQ0csSUFBSSxVQUFV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QUFFLEdBQWEsRUFBRSxLQUFhLEVBQUUsSUFBa0IsRUFBRSxFQUFFO1lBQ2xFLE1BQU0sR0FBRztZQUNMLDBEQUEwRDtZQUMxRCxNQUFNLENBQUMsU0FBUyxDQUNaLFlBQVksRUFBRSxNQUFtQyxFQUNqRCxLQUFnQyxDQUFXLENBQUM7WUFFcEQsSUFBSSxDQUFDLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztZQUU3QixPQUFPLEVBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxHQUFHLEVBQUUsUUFBUSxDQUFDLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBQyxDQUFDO1FBQ3pELENBQUMsQ0FBQyxDQUFDO1FBRVAsT0FBTyxnQkFBZ0IsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO0tBQzFDO0FBQ0gsQ0FBQztBQUVELE1BQU0sQ0FBQyxNQUFNLE1BQU0sR0FBRyxlQUFlLENBQUMsRUFBRSxDQUFDLEVBQUMsWUFBWSxFQUFDLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCAyMDE5IEdvb2dsZSBMTEMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKi9cblxuaW1wb3J0IHtFTkdJTkV9IGZyb20gJy4uLy4uL2VuZ2luZSc7XG5pbXBvcnQge2N1c3RvbUdyYWR9IGZyb20gJy4uLy4uL2dyYWRpZW50cyc7XG5pbXBvcnQge19GdXNlZE1hdE11bCwgX0Z1c2VkTWF0TXVsQXR0cnMsIF9GdXNlZE1hdE11bElucHV0c30gZnJvbSAnLi4vLi4va2VybmVsX25hbWVzJztcbmltcG9ydCB7TmFtZWRBdHRyTWFwfSBmcm9tICcuLi8uLi9rZXJuZWxfcmVnaXN0cnknO1xuaW1wb3J0IHtUZW5zb3IsIFRlbnNvcjNEfSBmcm9tICcuLi8uLi90ZW5zb3InO1xuaW1wb3J0IHtHcmFkU2F2ZUZ1bmMsIE5hbWVkVGVuc29yTWFwfSBmcm9tICcuLi8uLi90ZW5zb3JfdHlwZXMnO1xuaW1wb3J0IHttYWtlVHlwZXNNYXRjaH0gZnJvbSAnLi4vLi4vdGVuc29yX3V0aWwnO1xuaW1wb3J0IHtjb252ZXJ0VG9UZW5zb3J9IGZyb20gJy4uLy4uL3RlbnNvcl91dGlsX2Vudic7XG5pbXBvcnQge1RlbnNvckxpa2V9IGZyb20gJy4uLy4uL3R5cGVzJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vLi4vdXRpbCc7XG5cbmltcG9ydCB7YWRkfSBmcm9tICcuLi9hZGQnO1xuaW1wb3J0ICogYXMgYnJvYWRjYXN0X3V0aWwgZnJvbSAnLi4vYnJvYWRjYXN0X3V0aWwnO1xuaW1wb3J0IHtBY3RpdmF0aW9ufSBmcm9tICcuLi9mdXNlZF90eXBlcyc7XG5pbXBvcnQge2FwcGx5QWN0aXZhdGlvbiwgZ2V0RnVzZWRCaWFzR3JhZGllbnQsIGdldEZ1c2VkRHlBY3RpdmF0aW9uLCBzaG91bGRGdXNlfSBmcm9tICcuLi9mdXNlZF91dGlsJztcbmltcG9ydCB7bWF0TXVsIGFzIHVuZnVzZWRNYXRNdWx9IGZyb20gJy4uL21hdF9tdWwnO1xuaW1wb3J0IHtvcH0gZnJvbSAnLi4vb3BlcmF0aW9uJztcbmltcG9ydCB7cmVzaGFwZX0gZnJvbSAnLi4vcmVzaGFwZSc7XG5cbi8qKlxuICogQ29tcHV0ZXMgdGhlIGRvdCBwcm9kdWN0IG9mIHR3byBtYXRyaWNlcyB3aXRoIG9wdGlvbmFsIGFjdGl2YXRpb24gYW5kIGJpYXMuXG4gKlxuICogYGBganNcbiAqIGNvbnN0IGEgPSB0Zi50ZW5zb3IyZChbLTEsIC0yXSwgWzEsIDJdKTtcbiAqIGNvbnN0IGIgPSB0Zi50ZW5zb3IyZChbMSwgMiwgMywgNF0sIFsyLCAyXSk7XG4gKiBjb25zdCBiaWFzID0gdGYudGVuc29yMmQoWzEsIDJdLCBbMSwgMl0pO1xuICpcbiAqIHRmLmZ1c2VkLm1hdE11bCh7YSwgYiwgYmlhcywgYWN0aXZhdGlvbjogJ3JlbHUnfSkucHJpbnQoKTtcbiAqIGBgYFxuICpcbiAqIEBwYXJhbSBvYmogQW4gb2JqZWN0IHdpdGggdGhlIGZvbGxvd2luZyBwcm9wZXJ0aWVzOlxuICogLSBgYWAgRmlyc3QgbWF0cml4IGluIGRvdCBwcm9kdWN0IG9wZXJhdGlvbi5cbiAqIC0gYGJgIFNlY29uZCBtYXRyaXggaW4gZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICogLSBgdHJhbnNwb3NlQWAgSWYgdHJ1ZSwgYGFgIGlzIHRyYW5zcG9zZWQgYmVmb3JlIG11bHRpcGxpY2F0aW9uLlxuICogLSBgdHJhbnNwb3NlQmAgSWYgdHJ1ZSwgYGJgIGlzIHRyYW5zcG9zZWQgYmVmb3JlIG11bHRpcGxpY2F0aW9uLlxuICogLSBgYmlhc2AgTWF0cml4IHRvIGJlIGFkZGVkIHRvIHRoZSByZXN1bHQuXG4gKiAtIGBhY3RpdmF0aW9uYCBOYW1lIG9mIGFjdGl2YXRpb24ga2VybmVsIChkZWZhdWx0cyB0byBgbGluZWFyYCkuXG4gKiAtIGBwcmVsdUFjdGl2YXRpb25XZWlnaHRzYCBUZW5zb3Igb2YgcHJlbHUgd2VpZ2h0cy5cbiAqIC0gYGxlYWt5cmVsdUFscGhhYCBBbHBoYSBvZiBsZWFreXJlbHUuXG4gKi9cbmZ1bmN0aW9uIGZ1c2VkTWF0TXVsXyh7XG4gIGEsXG4gIGIsXG4gIHRyYW5zcG9zZUEgPSBmYWxzZSxcbiAgdHJhbnNwb3NlQiA9IGZhbHNlLFxuICBiaWFzLFxuICBhY3RpdmF0aW9uID0gJ2xpbmVhcicsXG4gIHByZWx1QWN0aXZhdGlvbldlaWdodHMsXG4gIGxlYWt5cmVsdUFscGhhID0gMC4yLFxufToge1xuICBhOiBUZW5zb3J8VGVuc29yTGlrZSxcbiAgYjogVGVuc29yfFRlbnNvckxpa2UsXG4gIHRyYW5zcG9zZUE/OiBib29sZWFuLFxuICB0cmFuc3Bvc2VCPzogYm9vbGVhbixcbiAgYmlhcz86IFRlbnNvcnxUZW5zb3JMaWtlLFxuICBhY3RpdmF0aW9uPzogQWN0aXZhdGlvbixcbiAgcHJlbHVBY3RpdmF0aW9uV2VpZ2h0cz86IFRlbnNvclxuICBsZWFreXJlbHVBbHBoYT86IG51bWJlclxufSk6IFRlbnNvciB7XG4gICAgaWYgKHNob3VsZEZ1c2UoRU5HSU5FLnN0YXRlLmdyYWRpZW50RGVwdGgsIGFjdGl2YXRpb24pID09PSBmYWxzZSkge1xuICAgICAgbGV0IHJlc3VsdCA9IHVuZnVzZWRNYXRNdWwoYSwgYiwgdHJhbnNwb3NlQSwgdHJhbnNwb3NlQik7XG4gICAgICBpZiAoYmlhcyAhPSBudWxsKSB7XG4gICAgICAgIHJlc3VsdCA9IGFkZChyZXN1bHQsIGJpYXMpO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gYXBwbHlBY3RpdmF0aW9uKFxuICAgICAgICAgICAgICAgICByZXN1bHQsIGFjdGl2YXRpb24sIHByZWx1QWN0aXZhdGlvbldlaWdodHMsIGxlYWt5cmVsdUFscGhhKTtcbiAgICB9XG5cbiAgICBsZXQgJGEgPSBjb252ZXJ0VG9UZW5zb3IoYSwgJ2EnLCAnZnVzZWQgbWF0TXVsJyk7XG4gICAgbGV0ICRiID0gY29udmVydFRvVGVuc29yKGIsICdiJywgJ2Z1c2VkIG1hdE11bCcpO1xuICAgIFskYSwgJGJdID0gbWFrZVR5cGVzTWF0Y2goJGEsICRiKTtcblxuICAgIGNvbnN0IGlubmVyU2hhcGVBID1cbiAgICAgICAgdHJhbnNwb3NlQSA/ICRhLnNoYXBlWyRhLnJhbmsgLSAyXSA6ICRhLnNoYXBlWyRhLnJhbmsgLSAxXTtcbiAgICBjb25zdCBpbm5lclNoYXBlQiA9XG4gICAgICAgIHRyYW5zcG9zZUIgPyAkYi5zaGFwZVskYi5yYW5rIC0gMV0gOiAkYi5zaGFwZVskYi5yYW5rIC0gMl07XG5cbiAgICBjb25zdCBvdXRlclNoYXBlQSA9XG4gICAgICAgIHRyYW5zcG9zZUEgPyAkYS5zaGFwZVskYS5yYW5rIC0gMV0gOiAkYS5zaGFwZVskYS5yYW5rIC0gMl07XG4gICAgY29uc3Qgb3V0ZXJTaGFwZUIgPVxuICAgICAgICB0cmFuc3Bvc2VCID8gJGIuc2hhcGVbJGIucmFuayAtIDJdIDogJGIuc2hhcGVbJGIucmFuayAtIDFdO1xuXG4gICAgY29uc3Qgb3V0ZXJEaW1zQSA9ICRhLnNoYXBlLnNsaWNlKDAsIC0yKTtcbiAgICBjb25zdCBvdXRlckRpbXNCID0gJGIuc2hhcGUuc2xpY2UoMCwgLTIpO1xuICAgIGNvbnN0IGJhdGNoRGltQSA9IHV0aWwuc2l6ZUZyb21TaGFwZShvdXRlckRpbXNBKTtcbiAgICBjb25zdCBiYXRjaERpbUIgPSB1dGlsLnNpemVGcm9tU2hhcGUob3V0ZXJEaW1zQik7XG5cbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgaW5uZXJTaGFwZUEgPT09IGlubmVyU2hhcGVCLFxuICAgICAgICAoKSA9PiBgRXJyb3IgaW4gZnVzZWQgbWF0TXVsOiBpbm5lciBzaGFwZXMgKCR7aW5uZXJTaGFwZUF9KSBhbmQgKGAgK1xuICAgICAgICAgICAgYCR7aW5uZXJTaGFwZUJ9KSBvZiBUZW5zb3JzIHdpdGggc2hhcGVzICR7JGEuc2hhcGV9IGFuZCBgICtcbiAgICAgICAgICAgIGAkeyRiLnNoYXBlfSBhbmQgdHJhbnNwb3NlQT0ke3RyYW5zcG9zZUF9YCArXG4gICAgICAgICAgICBgIGFuZCB0cmFuc3Bvc2VCPSR7dHJhbnNwb3NlQn0gbXVzdCBtYXRjaC5gKTtcblxuICAgIGNvbnN0IG91dFNoYXBlT3V0ZXJEaW1zID0gYnJvYWRjYXN0X3V0aWwuYXNzZXJ0QW5kR2V0QnJvYWRjYXN0U2hhcGUoXG4gICAgICAgICRhLnNoYXBlLnNsaWNlKDAsIC0yKSwgJGIuc2hhcGUuc2xpY2UoMCwgLTIpKTtcbiAgICBjb25zdCBvdXRTaGFwZSA9IG91dFNoYXBlT3V0ZXJEaW1zLmNvbmNhdChbb3V0ZXJTaGFwZUEsIG91dGVyU2hhcGVCXSk7XG5cbiAgICBjb25zdCBhM0Q6IFRlbnNvcjNEID0gdHJhbnNwb3NlQSA/XG4gICAgICAgIHJlc2hhcGUoJGEsIFtiYXRjaERpbUEsIGlubmVyU2hhcGVBLCBvdXRlclNoYXBlQV0pIDpcbiAgICAgICAgcmVzaGFwZSgkYSwgW2JhdGNoRGltQSwgb3V0ZXJTaGFwZUEsIGlubmVyU2hhcGVBXSk7XG4gICAgY29uc3QgYjNEOiBUZW5zb3IzRCA9IHRyYW5zcG9zZUIgP1xuICAgICAgICByZXNoYXBlKCRiLCBbYmF0Y2hEaW1CLCBvdXRlclNoYXBlQiwgaW5uZXJTaGFwZUJdKSA6XG4gICAgICAgIHJlc2hhcGUoJGIsIFtiYXRjaERpbUIsIGlubmVyU2hhcGVCLCBvdXRlclNoYXBlQl0pO1xuXG4gICAgbGV0ICRiaWFzOiBUZW5zb3I7XG4gICAgaWYgKGJpYXMgIT0gbnVsbCkge1xuICAgICAgJGJpYXMgPSBjb252ZXJ0VG9UZW5zb3IoYmlhcywgJ2JpYXMnLCAnZnVzZWQgbWF0TXVsJyk7XG4gICAgICBbJGJpYXNdID0gbWFrZVR5cGVzTWF0Y2goJGJpYXMsICRhKTtcblxuICAgICAgYnJvYWRjYXN0X3V0aWwuYXNzZXJ0QW5kR2V0QnJvYWRjYXN0U2hhcGUob3V0U2hhcGUsICRiaWFzLnNoYXBlKTtcbiAgICB9XG5cbiAgICBsZXQgJHByZWx1QWN0aXZhdGlvbldlaWdodHM6IFRlbnNvcjtcbiAgICBpZiAocHJlbHVBY3RpdmF0aW9uV2VpZ2h0cyAhPSBudWxsKSB7XG4gICAgICAkcHJlbHVBY3RpdmF0aW9uV2VpZ2h0cyA9IGNvbnZlcnRUb1RlbnNvcihcbiAgICAgICAgICBwcmVsdUFjdGl2YXRpb25XZWlnaHRzLCAncHJlbHUgd2VpZ2h0cycsICdmdXNlZCBtYXRNdWwnKTtcbiAgICB9XG5cbiAgICBjb25zdCBncmFkID0gKGR5OiBUZW5zb3IzRCwgc2F2ZWQ6IFRlbnNvcltdKSA9PiB7XG4gICAgICBjb25zdCBbYTNELCBiM0QsIHksICRiaWFzXSA9IHNhdmVkO1xuICAgICAgLy8gd2UgcmVzaGFwZSBkeSBiZWNhdXNlIHRoZSByZXN1bHQgb2YgdGhlIGZvcndhcmQgaXMgbm90XG4gICAgICAvLyBuZWNlc3NhcmlseSBnb2luZyB0byBiZSBhIDNkIHRlbnNvciBkdWUgdG8gYSByZXNoYXBlIGRvbmUgYXQgdGhlIGVuZCBvZlxuICAgICAgLy8gdGhlIGN1c3RvbU9wLlxuICAgICAgY29uc3QgZHlBY3RpdmF0aW9uID1cbiAgICAgICAgICBnZXRGdXNlZER5QWN0aXZhdGlvbihyZXNoYXBlKGR5LCB5LnNoYXBlKSwgeSwgYWN0aXZhdGlvbik7XG4gICAgICBsZXQgYURlcjogVGVuc29yO1xuICAgICAgbGV0IGJEZXI6IFRlbnNvcjtcblxuICAgICAgaWYgKCF0cmFuc3Bvc2VBICYmICF0cmFuc3Bvc2VCKSB7XG4gICAgICAgIGFEZXIgPSB1bmZ1c2VkTWF0TXVsKGR5QWN0aXZhdGlvbiwgYjNELCBmYWxzZSwgdHJ1ZSk7XG4gICAgICAgIGJEZXIgPSB1bmZ1c2VkTWF0TXVsKGEzRCwgZHlBY3RpdmF0aW9uLCB0cnVlLCBmYWxzZSk7XG4gICAgICB9IGVsc2UgaWYgKCF0cmFuc3Bvc2VBICYmIHRyYW5zcG9zZUIpIHtcbiAgICAgICAgYURlciA9IHVuZnVzZWRNYXRNdWwoZHlBY3RpdmF0aW9uLCBiM0QsIGZhbHNlLCBmYWxzZSk7XG4gICAgICAgIGJEZXIgPSB1bmZ1c2VkTWF0TXVsKGR5QWN0aXZhdGlvbiwgYTNELCB0cnVlLCBmYWxzZSk7XG4gICAgICB9IGVsc2UgaWYgKHRyYW5zcG9zZUEgJiYgIXRyYW5zcG9zZUIpIHtcbiAgICAgICAgYURlciA9IHVuZnVzZWRNYXRNdWwoYjNELCBkeUFjdGl2YXRpb24sIGZhbHNlLCB0cnVlKTtcbiAgICAgICAgYkRlciA9IHVuZnVzZWRNYXRNdWwoYTNELCBkeUFjdGl2YXRpb24sIGZhbHNlLCBmYWxzZSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBhRGVyID0gdW5mdXNlZE1hdE11bChiM0QsIGR5QWN0aXZhdGlvbiwgdHJ1ZSwgdHJ1ZSk7XG4gICAgICAgIGJEZXIgPSB1bmZ1c2VkTWF0TXVsKGR5QWN0aXZhdGlvbiwgYTNELCB0cnVlLCB0cnVlKTtcbiAgICAgIH1cblxuICAgICAgaWYgKGJpYXMgIT0gbnVsbCkge1xuICAgICAgICBjb25zdCBiaWFzRGVyID0gZ2V0RnVzZWRCaWFzR3JhZGllbnQoJGJpYXMsIGR5QWN0aXZhdGlvbik7XG4gICAgICAgIHJldHVybiBbYURlciwgYkRlciwgYmlhc0Rlcl07XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gW2FEZXIsIGJEZXJdO1xuICAgICAgfVxuICAgIH07XG5cbiAgICBjb25zdCBpbnB1dHM6IF9GdXNlZE1hdE11bElucHV0cyA9IHtcbiAgICAgIGE6IGEzRCxcbiAgICAgIGI6IGIzRCxcbiAgICAgIGJpYXM6ICRiaWFzLFxuICAgICAgcHJlbHVBY3RpdmF0aW9uV2VpZ2h0czogJHByZWx1QWN0aXZhdGlvbldlaWdodHNcbiAgICB9O1xuICAgIGNvbnN0IGF0dHJzOiBfRnVzZWRNYXRNdWxBdHRycyA9XG4gICAgICAgIHt0cmFuc3Bvc2VBLCB0cmFuc3Bvc2VCLCBhY3RpdmF0aW9uLCBsZWFreXJlbHVBbHBoYX07XG5cbiAgICAvLyBEZXBlbmRpbmcgb24gdGhlIHRoZSBwYXJhbXMgcGFzc2VkIGluIHdlIHdpbGwgaGF2ZSBkaWZmZXJlbnQgbnVtYmVyIG9mXG4gICAgLy8gaW5wdXRzIGFuZCB0aHVzIGEgYSBkaWZmZXJlbnQgbnVtYmVyIG9mIGVsZW1lbnRzIGluIHRoZSBncmFkaWVudC5cbiAgICBpZiAoYmlhcyA9PSBudWxsKSB7XG4gICAgICBjb25zdCBjdXN0b21PcCA9XG4gICAgICAgICAgY3VzdG9tR3JhZCgoYTNEOiBUZW5zb3IzRCwgYjNEOiBUZW5zb3IzRCwgc2F2ZTogR3JhZFNhdmVGdW5jKSA9PiB7XG4gICAgICAgICAgICBjb25zdCByZXMgPVxuICAgICAgICAgICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTogbm8tdW5uZWNlc3NhcnktdHlwZS1hc3NlcnRpb25cbiAgICAgICAgICAgICAgICBFTkdJTkUucnVuS2VybmVsKFxuICAgICAgICAgICAgICAgICAgICBfRnVzZWRNYXRNdWwsIGlucHV0cyBhcyB1bmtub3duIGFzIE5hbWVkVGVuc29yTWFwLFxuICAgICAgICAgICAgICAgICAgICBhdHRycyBhcyB1bmtub3duIGFzIE5hbWVkQXR0ck1hcCkgYXMgVGVuc29yO1xuXG4gICAgICAgICAgICBzYXZlKFthM0QsIGIzRCwgcmVzXSk7XG5cbiAgICAgICAgICAgIHJldHVybiB7dmFsdWU6IHJlc2hhcGUocmVzLCBvdXRTaGFwZSksIGdyYWRGdW5jOiBncmFkfTtcbiAgICAgICAgICB9KTtcbiAgICAgIHJldHVybiBjdXN0b21PcChhM0QsIGIzRCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGN1c3RvbU9wV2l0aEJpYXMgPSBjdXN0b21HcmFkKFxuICAgICAgICAgIChhM0Q6IFRlbnNvcjNELCBiM0Q6IFRlbnNvcjNELCAkYmlhczogVGVuc29yLCBzYXZlOiBHcmFkU2F2ZUZ1bmMpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHJlcyA9XG4gICAgICAgICAgICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOiBuby11bm5lY2Vzc2FyeS10eXBlLWFzc2VydGlvblxuICAgICAgICAgICAgICAgIEVOR0lORS5ydW5LZXJuZWwoXG4gICAgICAgICAgICAgICAgICAgIF9GdXNlZE1hdE11bCwgaW5wdXRzIGFzIHVua25vd24gYXMgTmFtZWRUZW5zb3JNYXAsXG4gICAgICAgICAgICAgICAgICAgIGF0dHJzIGFzIHVua25vd24gYXMgTmFtZWRBdHRyTWFwKSBhcyBUZW5zb3I7XG5cbiAgICAgICAgICAgIHNhdmUoW2EzRCwgYjNELCByZXMsICRiaWFzXSk7XG5cbiAgICAgICAgICAgIHJldHVybiB7dmFsdWU6IHJlc2hhcGUocmVzLCBvdXRTaGFwZSksIGdyYWRGdW5jOiBncmFkfTtcbiAgICAgICAgICB9KTtcblxuICAgICAgcmV0dXJuIGN1c3RvbU9wV2l0aEJpYXMoYTNELCBiM0QsICRiaWFzKTtcbiAgICB9XG4gIH1cblxuICBleHBvcnQgY29uc3QgbWF0TXVsID0gLyogQF9fUFVSRV9fICovIG9wKHtmdXNlZE1hdE11bF99KTtcbiJdfQ==