UNPKG

13.9 kBJavaScriptView Raw
1/**
2 * @license
3 * Copyright 2021 Google LLC. All Rights Reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 * =============================================================================
16 */
17import { ENGINE } from '../../engine';
18import { SparseFillEmptyRows } from '../../kernel_names';
19import { convertToTensor } from '../../tensor_util_env';
20import { op } from '../operation';
21/**
22 * The input SparseTensor is represented via the map of inputs {`indices`,
23 * `values`, `denseShape`}. The output SparseTensor has the same `denseShape`
24 * but with indices `outputIndices` and values `outputValues`. This op inserts a
25 * single entry for every row that doesn't have any values. The index is created
26 * as `[row, 0, ..., 0]` and the inserted value is `defaultValue`.
27 *
28 * For example, suppose `spInput` has shape [5, 6] and non-empty values:
29 * [0, 1]: a
30 * [0, 3]: b
31 * [2, 0]: c
32 * [3, 1]: d
33 *
34 * Rows 1 and 4 are empty, so the output will be of shape [5, 6] with values:
35 * [0, 1]: a
36 * [0, 3]: b
37 * [1, 0]: `defaultValue`
38 * [2, 0]: c
39 * [3, 1]: d
40 * [4, 0]: `defaultValue`
41 *
42 * The output SparseTensor will be in row-major order and will have the same
43 * shape as the input.
44 *
45 * This op also returns an indicator vector shaped [dense_shape[0]] such that
46 * emptyRowIndicator[i] = True iff row i was an empty row.
47 *
48 * And a reverse index map vector shaped [indices.shape[0]] that is used during
49 * backpropagation, reverseIndexMap[i] = outi s.t. indices[i, j] ==
50 * outputIndices[outi, j] for all j
51 *
52 * ```js
53 * const result = tf.sparse.sparseFillEmptyRows(
54 * [[0, 0], [1, 0], [1, 3], [1, 4], [3, 2], [3, 3]],
55 * [0, 10, 13, 14, 32, 33], [5, 6], -1);
56 * console.log(result);
57 * result['outputIndices'].print(); // [[0, 0], [1, 0], [1, 3], [1, 4],
58 * // [2, 0], [3, 2], [3, 3], [4, 0]]
59 * result['outputValues'].print(); // [0, 10, 13, 14,-1, 32, 33, -1]
60 * result['emptyRowIndicator'].print(); // [false, false, true, false, true]
61 * result['reverseIndexMap'].print(); // [0, 1, 2, 3, 5, 6]
62 * ```
63 * @param indices: 2-D. the indices of the sparse tensor.
64 * @param values: 1-D. the values of the sparse tensor.
65 * @param denseShape: 1-D. the shape of the sparse tensor.
66 * @param defaultValue: 0-D. default value to insert into location [row, 0, ...,
67 * 0] for rows missing from the input sparse tensor.
68 * @return A map with the following properties:
69 * - outputIndices
70 * - outputValues: 1-D. the values of the filled sparse tensor.
71 * - emptyRowIndicator: 1-D. whether the dense row was missing in the input
72 * sparse tensor.
73 * - reverseIndexMap: 1-D. a map from the input indices to the output
74 * indices.
75 * @doc {heading: 'Operations', subheading: 'Sparse'}
76 */
77function sparseFillEmptyRows_(indices, values, denseShape, defaultValue) {
78 const $indices = convertToTensor(indices, 'indices', 'sparseFillEmptyRows', 'int32');
79 const $values = convertToTensor(values, 'values', 'sparseFillEmptyRows');
80 const $denseShape = convertToTensor(denseShape, 'denseShape', 'sparseFillEmptyRows', 'int32');
81 const $defaultValue = convertToTensor(defaultValue, 'defaultValue', 'sparseFillEmptyRows', $values.dtype);
82 if ($indices.rank !== 2) {
83 throw new Error(`Indices should be Tensor2D but received shape
84 ${$indices.shape}`);
85 }
86 if ($values.rank !== 1) {
87 throw new Error(`Values should be Tensor1D but received shape ${$values.shape}`);
88 }
89 if ($denseShape.rank !== 1) {
90 throw new Error(`Dense shape should be Tensor1D but received shape ${$denseShape.shape}`);
91 }
92 if ($defaultValue.rank !== 0) {
93 throw new Error(`Default value should be a scalar but received shape ${$defaultValue.shape}`);
94 }
95 const inputs = {
96 indices: $indices,
97 values: $values,
98 denseShape: $denseShape,
99 defaultValue: $defaultValue
100 };
101 const result = ENGINE.runKernel(SparseFillEmptyRows, inputs);
102 return {
103 outputIndices: result[0],
104 outputValues: result[1],
105 emptyRowIndicator: result[2],
106 reverseIndexMap: result[3]
107 };
108}
109export const sparseFillEmptyRows = op({ sparseFillEmptyRows_ });
110//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BhcnNlX2ZpbGxfZW1wdHlfcm93cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3RmanMtY29yZS9zcmMvb3BzL3NwYXJzZS9zcGFyc2VfZmlsbF9lbXB0eV9yb3dzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7R0FlRztBQUVILE9BQU8sRUFBQyxNQUFNLEVBQUMsTUFBTSxjQUFjLENBQUM7QUFDcEMsT0FBTyxFQUFDLG1CQUFtQixFQUE0QixNQUFNLG9CQUFvQixDQUFDO0FBR2xGLE9BQU8sRUFBQyxlQUFlLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUV0RCxPQUFPLEVBQUMsRUFBRSxFQUFDLE1BQU0sY0FBYyxDQUFDO0FBRWhDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBdURHO0FBQ0gsU0FBUyxvQkFBb0IsQ0FDekIsT0FBNEIsRUFBRSxNQUEyQixFQUN6RCxVQUErQixFQUMvQixZQUErQjtJQUNqQyxNQUFNLFFBQVEsR0FDVixlQUFlLENBQUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxxQkFBcUIsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUN4RSxNQUFNLE9BQU8sR0FBRyxlQUFlLENBQUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxxQkFBcUIsQ0FBQyxDQUFDO0lBQ3pFLE1BQU0sV0FBVyxHQUNiLGVBQWUsQ0FBQyxVQUFVLEVBQUUsWUFBWSxFQUFFLHFCQUFxQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQzlFLE1BQU0sYUFBYSxHQUFHLGVBQWUsQ0FDakMsWUFBWSxFQUFFLGNBQWMsRUFBRSxxQkFBcUIsRUFBRSxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7SUFFeEUsSUFBSSxRQUFRLENBQUMsSUFBSSxLQUFLLENBQUMsRUFBRTtRQUN2QixNQUFNLElBQUksS0FBSyxDQUFDO1VBQ1YsUUFBUSxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7S0FDekI7SUFDRCxJQUFJLE9BQU8sQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUFFO1FBQ3RCLE1BQU0sSUFBSSxLQUFLLENBQ1gsZ0RBQWdELE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO0tBQ3RFO0lBQ0QsSUFBSSxXQUFXLENBQUMsSUFBSSxLQUFLLENBQUMsRUFBRTtRQUMxQixNQUFNLElBQUksS0FBSyxDQUFDLHFEQUNaLFdBQVcsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO0tBQzFCO0lBQ0QsSUFBSSxhQUFhLENBQUMsSUFBSSxLQUFLLENBQUMsRUFBRTtRQUM1QixNQUFNLElBQUksS0FBSyxDQUFDLHVEQUNaLGFBQWEsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO0tBQzVCO0lBRUQsTUFBTSxNQUFNLEdBQThCO1FBQ3hDLE9BQU8sRUFBRSxRQUFRO1FBQ2pCLE1BQU0sRUFBRSxPQUFPO1FBQ2YsVUFBVSxFQUFFLFdBQVc7UUFDdkIsWUFBWSxFQUFFLGFBQWE7S0FDNUIsQ0FBQztJQUVGLE1BQU0sTUFBTSxHQUFhLE1BQU0sQ0FBQyxTQUFTLENBQUMsbUJBQW1CLEVBQUUsTUFBWSxDQUFDLENBQUM7SUFDN0UsT0FBTztRQUNMLGFBQWEsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQ3hCLFlBQVksRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQ3ZCLGlCQUFpQixFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7UUFDNUIsZUFBZSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7S0FDM0IsQ0FBQztBQUNKLENBQUM7QUFFRCxNQUFNLENBQUMsTUFBTSxtQkFBbUIsR0FBRyxFQUFFLENBQUMsRUFBQyxvQkFBb0IsRUFBQyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgMjAyMSBHb29nbGUgTExDLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICovXG5cbmltcG9ydCB7RU5HSU5FfSBmcm9tICcuLi8uLi9lbmdpbmUnO1xuaW1wb3J0IHtTcGFyc2VGaWxsRW1wdHlSb3dzLCBTcGFyc2VGaWxsRW1wdHlSb3dzSW5wdXRzfSBmcm9tICcuLi8uLi9rZXJuZWxfbmFtZXMnO1xuaW1wb3J0IHtTY2FsYXIsIFRlbnNvciwgVGVuc29yMUQsIFRlbnNvcjJEfSBmcm9tICcuLi8uLi90ZW5zb3InO1xuaW1wb3J0IHtOYW1lZFRlbnNvck1hcH0gZnJvbSAnLi4vLi4vdGVuc29yX3R5cGVzJztcbmltcG9ydCB7Y29udmVydFRvVGVuc29yfSBmcm9tICcuLi8uLi90ZW5zb3JfdXRpbF9lbnYnO1xuaW1wb3J0IHtTY2FsYXJMaWtlLCBUZW5zb3JMaWtlfSBmcm9tICcuLi8uLi90eXBlcyc7XG5pbXBvcnQge29wfSBmcm9tICcuLi9vcGVyYXRpb24nO1xuXG4vKipcbiAqIFRoZSBpbnB1dCBTcGFyc2VUZW5zb3IgaXMgcmVwcmVzZW50ZWQgdmlhIHRoZSBtYXAgb2YgaW5wdXRzIHtgaW5kaWNlc2AsXG4gKiBgdmFsdWVzYCwgYGRlbnNlU2hhcGVgfS4gVGhlIG91dHB1dCBTcGFyc2VUZW5zb3IgaGFzIHRoZSBzYW1lIGBkZW5zZVNoYXBlYFxuICogYnV0IHdpdGggaW5kaWNlcyBgb3V0cHV0SW5kaWNlc2AgYW5kIHZhbHVlcyBgb3V0cHV0VmFsdWVzYC4gVGhpcyBvcCBpbnNlcnRzIGFcbiAqIHNpbmdsZSBlbnRyeSBmb3IgZXZlcnkgcm93IHRoYXQgZG9lc24ndCBoYXZlIGFueSB2YWx1ZXMuIFRoZSBpbmRleCBpcyBjcmVhdGVkXG4gKiBhcyBgW3JvdywgMCwgLi4uLCAwXWAgYW5kIHRoZSBpbnNlcnRlZCB2YWx1ZSBpcyBgZGVmYXVsdFZhbHVlYC5cbiAqXG4gKiBGb3IgZXhhbXBsZSwgc3VwcG9zZSBgc3BJbnB1dGAgaGFzIHNoYXBlIFs1LCA2XSBhbmQgbm9uLWVtcHR5IHZhbHVlczpcbiAqIFswLCAxXTogYVxuICogWzAsIDNdOiBiXG4gKiBbMiwgMF06IGNcbiAqIFszLCAxXTogZFxuICpcbiAqIFJvd3MgMSBhbmQgNCBhcmUgZW1wdHksIHNvIHRoZSBvdXRwdXQgd2lsbCBiZSBvZiBzaGFwZSBbNSwgNl0gd2l0aCB2YWx1ZXM6XG4gKiBbMCwgMV06IGFcbiAqIFswLCAzXTogYlxuICogWzEsIDBdOiBgZGVmYXVsdFZhbHVlYFxuICogWzIsIDBdOiBjXG4gKiBbMywgMV06IGRcbiAqIFs0LCAwXTogYGRlZmF1bHRWYWx1ZWBcbiAqXG4gKiBUaGUgb3V0cHV0IFNwYXJzZVRlbnNvciB3aWxsIGJlIGluIHJvdy1tYWpvciBvcmRlciBhbmQgd2lsbCBoYXZlIHRoZSBzYW1lXG4gKiBzaGFwZSBhcyB0aGUgaW5wdXQuXG4gKlxuICogVGhpcyBvcCBhbHNvIHJldHVybnMgYW4gaW5kaWNhdG9yIHZlY3RvciBzaGFwZWQgW2RlbnNlX3NoYXBlWzBdXSBzdWNoIHRoYXRcbiAqIGVtcHR5Um93SW5kaWNhdG9yW2ldID0gVHJ1ZSBpZmYgcm93IGkgd2FzIGFuIGVtcHR5IHJvdy5cbiAqXG4gKiBBbmQgYSByZXZlcnNlIGluZGV4IG1hcCB2ZWN0b3Igc2hhcGVkIFtpbmRpY2VzLnNoYXBlWzBdXSB0aGF0IGlzIHVzZWQgZHVyaW5nXG4gKiBiYWNrcHJvcGFnYXRpb24sIHJldmVyc2VJbmRleE1hcFtpXSA9IG91dGkgcy50LiBpbmRpY2VzW2ksIGpdID09XG4gKiBvdXRwdXRJbmRpY2VzW291dGksIGpdIGZvciBhbGwgalxuICpcbiAqIGBgYGpzXG4gKiBjb25zdCByZXN1bHQgPSB0Zi5zcGFyc2Uuc3BhcnNlRmlsbEVtcHR5Um93cyhcbiAqICAgW1swLCAwXSwgWzEsIDBdLCBbMSwgM10sIFsxLCA0XSwgWzMsIDJdLCBbMywgM11dLFxuICogICBbMCwgMTAsIDEzLCAxNCwgMzIsIDMzXSwgWzUsIDZdLCAtMSk7XG4gKiBjb25zb2xlLmxvZyhyZXN1bHQpO1xuICogcmVzdWx0WydvdXRwdXRJbmRpY2VzJ10ucHJpbnQoKTsgLy8gW1swLCAwXSwgWzEsIDBdLCBbMSwgM10sIFsxLCA0XSxcbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vICBbMiwgMF0sIFszLCAyXSwgWzMsIDNdLCBbNCwgMF1dXG4gKiByZXN1bHRbJ291dHB1dFZhbHVlcyddLnByaW50KCk7IC8vIFswLCAxMCwgMTMsIDE0LC0xLCAzMiwgMzMsIC0xXVxuICogcmVzdWx0WydlbXB0eVJvd0luZGljYXRvciddLnByaW50KCk7IC8vIFtmYWxzZSwgZmFsc2UsIHRydWUsIGZhbHNlLCB0cnVlXVxuICogcmVzdWx0WydyZXZlcnNlSW5kZXhNYXAnXS5wcmludCgpOyAvLyBbMCwgMSwgMiwgMywgNSwgNl1cbiAqIGBgYFxuICogQHBhcmFtIGluZGljZXM6IDItRC4gdGhlIGluZGljZXMgb2YgdGhlIHNwYXJzZSB0ZW5zb3IuXG4gKiBAcGFyYW0gdmFsdWVzOiAxLUQuIHRoZSB2YWx1ZXMgb2YgdGhlIHNwYXJzZSB0ZW5zb3IuXG4gKiBAcGFyYW0gZGVuc2VTaGFwZTogMS1ELiB0aGUgc2hhcGUgb2YgdGhlIHNwYXJzZSB0ZW5zb3IuXG4gKiBAcGFyYW0gZGVmYXVsdFZhbHVlOiAwLUQuIGRlZmF1bHQgdmFsdWUgdG8gaW5zZXJ0IGludG8gbG9jYXRpb24gW3JvdywgMCwgLi4uLFxuICogICAgIDBdIGZvciByb3dzIG1pc3NpbmcgZnJvbSB0aGUgaW5wdXQgc3BhcnNlIHRlbnNvci5cbiAqIEByZXR1cm4gQSBtYXAgd2l0aCB0aGUgZm9sbG93aW5nIHByb3BlcnRpZXM6XG4gKiAgICAgLSBvdXRwdXRJbmRpY2VzXG4gKiAgICAgLSBvdXRwdXRWYWx1ZXM6IDEtRC4gdGhlIHZhbHVlcyBvZiB0aGUgZmlsbGVkIHNwYXJzZSB0ZW5zb3IuXG4gKiAgICAgLSBlbXB0eVJvd0luZGljYXRvcjogMS1ELiB3aGV0aGVyIHRoZSBkZW5zZSByb3cgd2FzIG1pc3NpbmcgaW4gdGhlIGlucHV0XG4gKiBzcGFyc2UgdGVuc29yLlxuICogICAgIC0gcmV2ZXJzZUluZGV4TWFwOiAxLUQuIGEgbWFwIGZyb20gdGhlIGlucHV0IGluZGljZXMgdG8gdGhlIG91dHB1dFxuICogaW5kaWNlcy5cbiAqIEBkb2Mge2hlYWRpbmc6ICdPcGVyYXRpb25zJywgc3ViaGVhZGluZzogJ1NwYXJzZSd9XG4gKi9cbmZ1bmN0aW9uIHNwYXJzZUZpbGxFbXB0eVJvd3NfKFxuICAgIGluZGljZXM6IFRlbnNvcjJEfFRlbnNvckxpa2UsIHZhbHVlczogVGVuc29yMUR8VGVuc29yTGlrZSxcbiAgICBkZW5zZVNoYXBlOiBUZW5zb3IxRHxUZW5zb3JMaWtlLFxuICAgIGRlZmF1bHRWYWx1ZTogU2NhbGFyfFNjYWxhckxpa2UpOiBOYW1lZFRlbnNvck1hcCB7XG4gIGNvbnN0ICRpbmRpY2VzID1cbiAgICAgIGNvbnZlcnRUb1RlbnNvcihpbmRpY2VzLCAnaW5kaWNlcycsICdzcGFyc2VGaWxsRW1wdHlSb3dzJywgJ2ludDMyJyk7XG4gIGNvbnN0ICR2YWx1ZXMgPSBjb252ZXJ0VG9UZW5zb3IodmFsdWVzLCAndmFsdWVzJywgJ3NwYXJzZUZpbGxFbXB0eVJvd3MnKTtcbiAgY29uc3QgJGRlbnNlU2hhcGUgPVxuICAgICAgY29udmVydFRvVGVuc29yKGRlbnNlU2hhcGUsICdkZW5zZVNoYXBlJywgJ3NwYXJzZUZpbGxFbXB0eVJvd3MnLCAnaW50MzInKTtcbiAgY29uc3QgJGRlZmF1bHRWYWx1ZSA9IGNvbnZlcnRUb1RlbnNvcihcbiAgICAgIGRlZmF1bHRWYWx1ZSwgJ2RlZmF1bHRWYWx1ZScsICdzcGFyc2VGaWxsRW1wdHlSb3dzJywgJHZhbHVlcy5kdHlwZSk7XG5cbiAgaWYgKCRpbmRpY2VzLnJhbmsgIT09IDIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEluZGljZXMgc2hvdWxkIGJlIFRlbnNvcjJEIGJ1dCByZWNlaXZlZCBzaGFwZVxuICAgICAgICAkeyRpbmRpY2VzLnNoYXBlfWApO1xuICB9XG4gIGlmICgkdmFsdWVzLnJhbmsgIT09IDEpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBWYWx1ZXMgc2hvdWxkIGJlIFRlbnNvcjFEIGJ1dCByZWNlaXZlZCBzaGFwZSAkeyR2YWx1ZXMuc2hhcGV9YCk7XG4gIH1cbiAgaWYgKCRkZW5zZVNoYXBlLnJhbmsgIT09IDEpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYERlbnNlIHNoYXBlIHNob3VsZCBiZSBUZW5zb3IxRCBidXQgcmVjZWl2ZWQgc2hhcGUgJHtcbiAgICAgICAgJGRlbnNlU2hhcGUuc2hhcGV9YCk7XG4gIH1cbiAgaWYgKCRkZWZhdWx0VmFsdWUucmFuayAhPT0gMCkge1xuICAgIHRocm93IG5ldyBFcnJvcihgRGVmYXVsdCB2YWx1ZSBzaG91bGQgYmUgYSBzY2FsYXIgYnV0IHJlY2VpdmVkIHNoYXBlICR7XG4gICAgICAgICRkZWZhdWx0VmFsdWUuc2hhcGV9YCk7XG4gIH1cblxuICBjb25zdCBpbnB1dHM6IFNwYXJzZUZpbGxFbXB0eVJvd3NJbnB1dHMgPSB7XG4gICAgaW5kaWNlczogJGluZGljZXMsXG4gICAgdmFsdWVzOiAkdmFsdWVzLFxuICAgIGRlbnNlU2hhcGU6ICRkZW5zZVNoYXBlLFxuICAgIGRlZmF1bHRWYWx1ZTogJGRlZmF1bHRWYWx1ZVxuICB9O1xuXG4gIGNvbnN0IHJlc3VsdDogVGVuc29yW10gPSBFTkdJTkUucnVuS2VybmVsKFNwYXJzZUZpbGxFbXB0eVJvd3MsIGlucHV0cyBhcyB7fSk7XG4gIHJldHVybiB7XG4gICAgb3V0cHV0SW5kaWNlczogcmVzdWx0WzBdLFxuICAgIG91dHB1dFZhbHVlczogcmVzdWx0WzFdLFxuICAgIGVtcHR5Um93SW5kaWNhdG9yOiByZXN1bHRbMl0sXG4gICAgcmV2ZXJzZUluZGV4TWFwOiByZXN1bHRbM11cbiAgfTtcbn1cblxuZXhwb3J0IGNvbnN0IHNwYXJzZUZpbGxFbXB0eVJvd3MgPSBvcCh7c3BhcnNlRmlsbEVtcHR5Um93c199KTtcbiJdfQ==
\No newline at end of file