UNPKG

@tensorflow/tfjs-layers

Version:

TensorFlow layers API in JavaScript

178 lines 26.9 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. * ============================================================================= */ /** * Interfaces and methods for training models using tf.Tensor objects. */ import * as tfc from '@tensorflow/tfjs-core'; import { Tensor } from '@tensorflow/tfjs-core'; import { expandDims, gather, sliceAlongFirstAxis } from '../backend/tfjs_backend'; export function checkBatchSize(batchSize) { tfc.util.assert(batchSize > 0 && Number.isInteger(batchSize), () => `batchSize is required to be a positive integer, but got ${batchSize}`); } /** * Slice a Tensor or an Array of Tensors, by start and stop indices. * * Porting Note: The `_slice_arrays` function in PyKeras is covered by this * function and `sliceArraysByIndices()` together. * * @param arrays: the input. * @param start: the starting index (inclusive). * @param stop: the stopping index (exclusive). * @returns The result of the slicing. If `arrays` is an `Array` of * `tf.Tensor`s, the slicing will be applied to all elements of the `Array` * in the same way. */ export function sliceArrays(arrays, start, stop) { if (arrays == null) { return [null]; } else if (Array.isArray(arrays)) { return arrays.map(array => sliceAlongFirstAxis(array, start, stop - start)); } else { // Tensor. return sliceAlongFirstAxis(arrays, start, stop - start); } } /** * Slice a Tensor or an Array of Tensors, by random-order indices. * * Porting Note: The `_slice_arrays` function in PyKeras is covered by this * function and `sliceArrays()` together. * * @param arrays The input `tf.Tensor` or `Array` of `tf.Tensor`s to slice. * If an `Array` of `tf.Tensor`s, all `tf.Tensor`s will be sliced in the * same fashion. * @param indices The indices to use for slicing along the first (batch) * dimension. * @returns Result(s) of the slicing. */ export function sliceArraysByIndices(arrays, indices) { return tfc.tidy(() => { if (arrays == null) { return null; } else if (Array.isArray(arrays)) { return arrays.map(array => sliceArraysByIndices(array, indices)); } else { // TODO(cais): indices should be a pre-constructed Tensor1D to avoid // tensor1d() calls. return gather(arrays, indices.dtype === 'int32' ? indices : tfc.cast(indices, 'int32')); } }); } /** * Returns a list of batch indices (tuples of indices). * @param size: Integer, total size of the data to slice into batches. * @param batchSize: Integer, batch size. * @returns An Array of [batchStart, batchEnd] tuples. batchStart is * inclusive; batchEnd is exclusive. I.e., each batch consists of indices x * that satisfy batchStart <= x < batchEnd. */ export function makeBatches(size, batchSize) { const output = []; let batchStart = 0; let batchEnd = null; while (batchStart < size) { batchEnd = batchStart + batchSize; if (batchEnd >= size) { batchEnd = size; } output.push([batchStart, batchEnd]); batchStart = batchEnd; } return output; } /** * Ensure tensors all have a rank of at least 2. * * If a tensor has a rank of 1, it is dimension-expanded to rank 2. * If any tensor has a rank of 0 (i.e., is a scalar), an error will be thrown. */ export function ensureTensorsRank2OrHigher(tensors) { const outs = []; if (tensors instanceof Tensor) { tensors = [tensors]; } // Make Tensors at least 2D. for (let i = 0; i < tensors.length; ++i) { const tensor = tensors[i]; if (tensor.rank === 1) { outs.push(expandDims(tensor, 1)); } else if (tensor.rank === 0) { throw new Error('Expected tensor to be at least 1D, but received a 0D tensor ' + '(scalar).'); } else { outs.push(tensor); } } return outs; } /** * Compare a set of tensors with a reference (old) set, discard the ones * in the new set that are not present in the reference set. * * This method is used for memory clenaup during calls such as * LayersModel.fit(). * * @param tensors New set which may contain Tensors not present in * `refTensors`. * @param refTensors Reference Tensor set. */ // TODO(cais, kangyizhang): Deduplicate with tfjs-data. export function disposeNewTensors(tensors, refTensors) { if (tensors == null) { return; } const oldTensorIds = []; if (refTensors instanceof Tensor) { oldTensorIds.push(refTensors.id); } else if (Array.isArray(refTensors)) { refTensors.forEach(t => oldTensorIds.push(t.id)); } else if (refTensors != null) { // `oldTensors` is a map from string name to Tensor. for (const name in refTensors) { const oldTensor = refTensors[name]; oldTensorIds.push(oldTensor.id); } } const tensorsToDispose = []; if (tensors instanceof Tensor) { if (oldTensorIds.indexOf(tensors.id) === -1) { tensorsToDispose.push(tensors); } } else if (Array.isArray(tensors)) { tensors.forEach(t => { if (oldTensorIds.indexOf(t.id) === -1) { tensorsToDispose.push(t); } }); } else if (tensors != null) { // `oldTensors` is a map from string name to Tensor. for (const name in tensors) { const tensor = tensors[name]; if (oldTensorIds.indexOf(tensor.id) === -1) { tensorsToDispose.push(tensor); } } } tensorsToDispose.forEach(t => { if (!t.isDisposed) { t.dispose(); } }); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhaW5pbmdfdGVuc29ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3RmanMtbGF5ZXJzL3NyYy9lbmdpbmUvdHJhaW5pbmdfdGVuc29ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7R0FRRztBQUVIOztHQUVHO0FBRUgsT0FBTyxLQUFLLEdBQUcsTUFBTSx1QkFBdUIsQ0FBQztBQUM3QyxPQUFPLEVBQUMsTUFBTSxFQUFXLE1BQU0sdUJBQXVCLENBQUM7QUFDdkQsT0FBTyxFQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUUsbUJBQW1CLEVBQUMsTUFBTSx5QkFBeUIsQ0FBQztBQTZJaEYsTUFBTSxVQUFVLGNBQWMsQ0FBQyxTQUFpQjtJQUM5QyxHQUFHLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FDWCxTQUFTLEdBQUcsQ0FBQyxJQUFJLE1BQU0sQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLEVBQzVDLEdBQUcsRUFBRSxDQUFDLDJEQUNGLFNBQVMsRUFBRSxDQUFDLENBQUM7QUFDdkIsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7R0FZRztBQUNILE1BQU0sVUFBVSxXQUFXLENBQ3ZCLE1BQXVCLEVBQUUsS0FBYSxFQUFFLElBQVk7SUFDdEQsSUFBSSxNQUFNLElBQUksSUFBSSxFQUFFO1FBQ2xCLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztLQUNmO1NBQU0sSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxFQUFFO1FBQ2hDLE9BQU8sTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLG1CQUFtQixDQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsSUFBSSxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUM7S0FDN0U7U0FBTSxFQUFHLFVBQVU7UUFDbEIsT0FBTyxtQkFBbUIsQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLElBQUksR0FBRyxLQUFLLENBQUMsQ0FBQztLQUN6RDtB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QyxDQUFDLEVBQUU7Z0JBQzFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQzthQUMvQjtTQUNGO0tBQ0Y7SUFFRCxnQkFBZ0IsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUU7UUFDM0IsSUFBSSxDQUFDLENBQUMsQ0FBQyxVQUFVLEVBQUU7WUFDakIsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO1NBQ2I7SUFDSCxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgMjAxOCBHb29nbGUgTExDXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlXG4gKiBsaWNlbnNlIHRoYXQgY2FuIGJlIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgb3IgYXRcbiAqIGh0dHBzOi8vb3BlbnNvdXJjZS5vcmcvbGljZW5zZXMvTUlULlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqL1xuXG4vKipcbiAqIEludGVyZmFjZXMgYW5kIG1ldGhvZHMgZm9yIHRyYWluaW5nIG1vZGVscyB1c2luZyB0Zi5UZW5zb3Igb2JqZWN0cy5cbiAqL1xuXG5pbXBvcnQgKiBhcyB0ZmMgZnJvbSAnQHRlbnNvcmZsb3cvdGZqcy1jb3JlJztcbmltcG9ydCB7VGVuc29yLCBUZW5zb3IxRH0gZnJvbSAnQHRlbnNvcmZsb3cvdGZqcy1jb3JlJztcbmltcG9ydCB7ZXhwYW5kRGltcywgZ2F0aGVyLCBzbGljZUFsb25nRmlyc3RBeGlzfSBmcm9tICcuLi9iYWNrZW5kL3RmanNfYmFja2VuZCc7XG5pbXBvcnQge0Jhc2VDYWxsYmFjaywgQ3VzdG9tQ2FsbGJhY2tBcmdzLCBNb2RlbExvZ2dpbmdWZXJib3NpdHksIFlpZWxkRXZlcnlPcHRpb25zfSBmcm9tICcuLi9iYXNlX2NhbGxiYWNrcyc7XG5pbXBvcnQge0NsYXNzV2VpZ2h0LCBDbGFzc1dlaWdodE1hcH0gZnJvbSAnLi90cmFpbmluZ191dGlscyc7XG5cbi8qKlxuICogSW50ZXJmYWNlIGNvbmZpZ3VyYXRpb24gbW9kZWwgdHJhaW5pbmcgYmFzZWQgb24gZGF0YSBhcyBgdGYuVGVuc29yYHMuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTW9kZWxGaXRBcmdzIHtcbiAgLyoqXG4gICAqIE51bWJlciBvZiBzYW1wbGVzIHBlciBncmFkaWVudCB1cGRhdGUuIElmIHVuc3BlY2lmaWVkLCBpdFxuICAgKiB3aWxsIGRlZmF1bHQgdG8gMzIuXG4gICAqL1xuICBiYXRjaFNpemU/OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIEludGVnZXIgbnVtYmVyIG9mIHRpbWVzIHRvIGl0ZXJhdGUgb3ZlciB0aGUgdHJhaW5pbmcgZGF0YSBhcnJheXMuXG4gICAqL1xuICBlcG9jaHM/OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIFZlcmJvc2l0eSBsZXZlbC5cbiAgICpcbiAgICogRXhwZWN0ZWQgdG8gYmUgMCwgMSwgb3IgMi4gRGVmYXVsdDogMS5cbiAgICpcbiAgICogMCAtIE5vIHByaW50ZWQgbWVzc2FnZSBkdXJpbmcgZml0KCkgY2FsbC5cbiAgICogMSAtIEluIE5vZGUuanMgKHRmanMtbm9kZSksIHByaW50cyB0aGUgcHJvZ3Jlc3MgYmFyLCB0b2dldGhlciB3aXRoXG4gICAqICAgICByZWFsLXRpbWUgdXBkYXRlcyBvZiBsb3NzIGFuZCBtZXRyaWMgdmFsdWVzIGFuZCB0cmFpbmluZyBzcGVlZC5cbiAgICogICAgIEluIHRoZSBicm93c2VyOiBubyBhY3Rpb24uIFRoaXMgaXMgdGhlIGRlZmF1bHQuXG4gICAqIDIgLSBOb3QgaW1wbGVtZW50ZWQgeWV0LlxuICAgKi9cbiAgdmVyYm9zZT86IE1vZGVsTG9nZ2luZ1ZlcmJvc2l0eSB8IDI7XG5cbiAgLyoqXG4gICAqIExpc3Qgb2YgY2FsbGJhY2tzIHRvIGJlIGNhbGxlZCBkdXJpbmcgdHJhaW5pbmcuXG4gICAqIENhbiBoYXZlIG9uZSBvciBtb3JlIG9mIHRoZSBmb2xsb3dpbmcgY2FsbGJhY2tzOlxuICAgKiAgIC0gYG9uVHJhaW5CZWdpbihsb2dzKWA6IGNhbGxlZCB3aGVuIHRyYWluaW5nIHN0YXJ0cy5cbiAgICogICAtIGBvblRyYWluRW5kKGxvZ3MpYDogY2FsbGVkIHdoZW4gdHJhaW5pbmcgZW5kcy5cbiAgICogICAtIGBvbkVwb2NoQmVnaW4oZXBvY2gsIGxvZ3MpYDogY2FsbGVkIGF0IHRoZSBzdGFydCBvZiBldmVyeSBlcG9jaC5cbiAgICogICAtIGBvbkVwb2NoRW5kKGVwb2NoLCBsb2dzKWA6IGNhbGxlZCBhdCB0aGUgZW5kIG9mIGV2ZXJ5IGVwb2NoLlxuICAgKiAgIC0gYG9uQmF0Y2hCZWdpbihiYXRjaCwgbG9ncylgOiBjYWxsZWQgYXQgdGhlIHN0YXJ0IG9mIGV2ZXJ5IGJhdGNoLlxuICAgKiAgIC0gYG9uQmF0Y2hFbmQoYmF0Y2gsIGxvZ3MpYDogY2FsbGVkIGF0IHRoZSBlbmQgb2YgZXZlcnkgYmF0Y2guXG4gICAqICAgLSBgb25ZaWVsZChlcG9jaCwgYmF0Y2gsIGxvZ3MpYDogY2FsbGVkIGV2ZXJ5IGB5aWVsZEV2ZXJ5YCBtaWxsaXNlY29uZHNcbiAgICogICAgICB3aXRoIHRoZSBjdXJyZW50IGVwb2NoLCBiYXRjaCBhbmQgbG9ncy4gVGhlIGxvZ3MgYXJlIHRoZSBzYW1lXG4gICAqICAgICAgYXMgaW4gYG9uQmF0Y2hFbmQoKWAuIE5vdGUgdGhhdCBgb25ZaWVsZGAgY2FuIHNraXAgYmF0Y2hlcyBvclxuICAgKiAgICAgIGVwb2Nocy4gU2VlIGFsc28gZG9jcyBmb3IgYHlpZWxkRXZlcnlgIGJlbG93LlxuICAgKi9cbiAgY2FsbGJhY2tzPzogQmFzZUNhbGxiYWNrW118Q3VzdG9tQ2FsbGJhY2tBcmdzfEN1c3RvbUNhbGxiYWNrQXJnc1tdO1xuXG4gIC8qKlxuICAgKiBGbG9hdCBiZXR3ZWVuIDAgYW5kIDE6IGZyYWN0aW9uIG9mIHRoZSB0cmFpbmluZyBkYXRhXG4gICAqIHRvIGJlIHVzZWQgYXMgdmFsaWRhdGlvbiBkYXRhLiBUaGUgbW9kZWwgd2lsbCBzZXQgYXBhcnQgdGhpcyBmcmFjdGlvbiBvZlxuICAgKiB0aGUgdHJhaW5pbmcgZGF0YSwgd2lsbCBub3QgdHJhaW4gb24gaXQsIGFuZCB3aWxsIGV2YWx1YXRlIHRoZSBsb3NzIGFuZFxuICAgKiBhbnkgbW9kZWwgbWV0cmljcyBvbiB0aGlzIGRhdGEgYXQgdGhlIGVuZCBvZiBlYWNoIGVwb2NoLlxuICAgKiBUaGUgdmFsaWRhdGlvbiBkYXRhIGlzIHNlbGVjdGVkIGZyb20gdGhlIGxhc3Qgc2FtcGxlcyBpbiB0aGUgYHhgIGFuZCBgeWBcbiAgICogZGF0YSBwcm92aWRlZCwgYmVmb3JlIHNodWZmbGluZy5cbiAgICovXG4gIHZhbGlkYXRpb25TcGxpdD86IG51bWJlcjtcblxuICAvKipcbiAgICogRGF0YSBvbiB3aGljaCB0byBldmFsdWF0ZSB0aGUgbG9zcyBhbmQgYW55IG1vZGVsXG4gICAqIG1ldHJpY3MgYXQgdGhlIGVuZCBvZiBlYWNoIGVwb2NoLiBUaGUgbW9kZWwgd2lsbCBub3QgYmUgdHJhaW5lZCBvbiB0aGlzXG4gICAqIGRhdGEuIFRoaXMgY291bGQgYmUgYSB0dXBsZSBbeFZhbCwgeVZhbF0gb3IgYSB0dXBsZSBbeFZhbCwgeVZhbCxcbiAgICogdmFsU2FtcGxlV2VpZ2h0c10uIFRoZSBtb2RlbCB3aWxsIG5vdCBiZSB0cmFpbmVkIG9uIHRoaXMgZGF0YS5cbiAgICogYHZhbGlkYXRpb25EYXRhYCB3aWxsIG92ZXJyaWRlIGB2YWxpZGF0aW9uU3BsaXRgLlxuICAgKi9cbiAgdmFsaWRhdGlvbkRhdGE/OiBbXG4gICAgVGVuc29yfFRlbnNvcltdLCBUZW5zb3J8VGVuc29yW11cbiAgXXxbVGVuc29yIHwgVGVuc29yW10sIFRlbnNvcnxUZW5zb3JbXSwgVGVuc29yfFRlbnNvcltdXTtcblxuICAvKipcbiAgICogV2hldGhlciB0byBzaHVmZmxlIHRoZSB0cmFpbmluZyBkYXRhIGJlZm9yZSBlYWNoIGVwb2NoLiBIYXNcbiAgICogbm8gZWZmZWN0IHdoZW4gYHN0ZXBzUGVyRXBvY2hgIGlzIG5vdCBgbnVsbGAuXG4gICAqL1xuICBzaHVmZmxlPzogYm9vbGVhbjtcblxuICAvKipcbiAgICogT3B0aW9uYWwgb2JqZWN0IG1hcHBpbmcgY2xhc3MgaW5kaWNlcyAoaW50ZWdlcnMpIHRvXG4gICAqIGEgd2VpZ2h0IChmbG9hdCkgdG8gYXBwbHkgdG8gdGhlIG1vZGVsJ3MgbG9zcyBmb3IgdGhlIHNhbXBsZXMgZnJvbSB0aGlzXG4gICAqIGNsYXNzIGR1cmluZyB0cmFpbmluZy4gVGhpcyBjYW4gYmUgdXNlZnVsIHRvIHRlbGwgdGhlIG1vZGVsIHRvIFwicGF5IG1vcmVcbiAgICogYXR0ZW50aW9uXCIgdG8gc2FtcGxlcyBmcm9tIGFuIHVuZGVyLXJlcHJlc2VudGVkIGNsYXNzLlxuICAgKlxuICAgKiBJZiB0aGUgbW9kZWwgaGFzIG11bHRpcGxlIG91dHB1dHMsIGEgY2xhc3Mgd2VpZ2h0IGNhbiBiZSBzcGVjaWZpZWQgZm9yXG4gICAqIGVhY2ggb2YgdGhlIG91dHB1dHMgYnkgc2V0dGluZyB0aGlzIGZpZWxkIGFuIGFycmF5IG9mIHdlaWdodCBvYmplY3RcbiAgICogb3IgYW4gb2JqZWN0IHRoYXQgbWFwcyBtb2RlbCBvdXRwdXQgbmFtZXMgKGUuZy4sIGBtb2RlbC5vdXRwdXROYW1lc1swXWApXG4gICAqIHRvIHdlaWdodCBvYmplY3RzLlxuICAgKi9cbiAgY2xhc3NXZWlnaHQ/OiBDbGFzc1dlaWdodHxDbGFzc1dlaWdodFtdfENsYXNzV2VpZ2h0TWFwO1xuXG4gIC8qKlxuICAgKiBPcHRpb25hbCBhcnJheSBvZiB0aGUgc2FtZSBsZW5ndGggYXMgeCwgY29udGFpbmluZ1xuICAgKiB3ZWlnaHRzIHRvIGFwcGx5IHRvIHRoZSBtb2RlbCdzIGxvc3MgZm9yIGVhY2ggc2FtcGxlLiBJbiB0aGUgY2FzZSBvZlxuICAgKiB0ZW1wb3JhbCBkYXRhLCB5b3UgY2FuIHBhc3MgYSAyRCBhcnJheSB3aXRoIHNoYXBlIChzYW1wbGVzLFxuICAgKiBzZXF1ZW5jZUxlbmd0aCksIHRvIGFwcGx5IGEgZGlmZmVyZW50IHdlaWdodCB0byBldmVyeSB0aW1lc3RlcCBvZiBldmVyeVxuICAgKiBzYW1wbGUuIEluIHRoaXMgY2FzZSB5b3Ugc2hvdWxkIG1ha2Ugc3VyZSB0byBzcGVjaWZ5XG4gICAqIHNhbXBsZVdlaWdodE1vZGU9XCJ0ZW1wb3JhbFwiIGluIGNvbXBpbGUoKS5cbiAgICovXG4gIHNhbXBsZVdlaWdodD86IFRlbnNvcjtcblxuICAvKipcbiAgICogRXBvY2ggYXQgd2hpY2ggdG8gc3RhcnQgdHJhaW5pbmcgKHVzZWZ1bCBmb3IgcmVzdW1pbmcgYSBwcmV2aW91cyB0cmFpbmluZ1xuICAgKiBydW4pLiBXaGVuIHRoaXMgaXMgdXNlZCwgYGVwb2Noc2AgaXMgdGhlIGluZGV4IG9mIHRoZSBcImZpbmFsIGVwb2NoXCIuXG4gICAqIFRoZSBtb2RlbCBpcyBub3QgdHJhaW5lZCBmb3IgYSBudW1iZXIgb2YgaXRlcmF0aW9ucyBnaXZlbiBieSBgZXBvY2hzYCxcbiAgICogYnV0IG1lcmVseSB1bnRpbCB0aGUgZXBvY2ggb2YgaW5kZXggYGVwb2Noc2AgaXMgcmVhY2hlZC5cbiAgICovXG4gIGluaXRpYWxFcG9jaD86IG51bWJlcjtcblxuICAvKipcbiAgICogVG90YWwgbnVtYmVyIG9mIHN0ZXBzIChiYXRjaGVzIG9mIHNhbXBsZXMpIGJlZm9yZVxuICAgKiBkZWNsYXJpbmcgb25lIGVwb2NoIGZpbmlzaGVkIGFuZCBzdGFydGluZyB0aGUgbmV4dCBlcG9jaC4gV2hlbiB0cmFpbmluZ1xuICAgKiB3aXRoIElucHV0IFRlbnNvcnMgc3VjaCBhcyBUZW5zb3JGbG93IGRhdGEgdGVuc29ycywgdGhlIGRlZmF1bHQgYG51bGxgIGlzXG4gICAqIGVxdWFsIHRvIHRoZSBudW1iZXIgb2YgdW5pcXVlIHNhbXBsZXMgaW4geW91ciBkYXRhc2V0IGRpdmlkZWQgYnkgdGhlXG4gICAqIGJhdGNoIHNpemUsIG9yIDEgaWYgdGhhdCBjYW5ub3QgYmUgZGV0ZXJtaW5lZC5cbiAgICovXG4gIHN0ZXBzUGVyRXBvY2g/OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIE9ubHkgcmVsZXZhbnQgaWYgYHN0ZXBzUGVyRXBvY2hgIGlzIHNwZWNpZmllZC4gVG90YWwgbnVtYmVyIG9mIHN0ZXBzXG4gICAqIChiYXRjaGVzIG9mIHNhbXBsZXMpIHRvIHZhbGlkYXRlIGJlZm9yZSBzdG9wcGluZy5cbiAgICovXG4gIHZhbGlkYXRpb25TdGVwcz86IG51bWJlcjtcblxuICAvKipcbiAgICogQ29uZmlndXJlcyB0aGUgZnJlcXVlbmN5IG9mIHlpZWxkaW5nIHRoZSBtYWluIHRocmVhZCB0byBvdGhlciB0YXNrcy5cbiAgICpcbiAgICogSW4gdGhlIGJyb3dzZXIgZW52aXJvbm1lbnQsIHlpZWxkaW5nIHRoZSBtYWluIHRocmVhZCBjYW4gaW1wcm92ZSB0aGVcbiAgICogcmVzcG9uc2l2ZW5lc3Mgb2YgdGhlIHBhZ2UgZHVyaW5nIHRyYWluaW5nLiBJbiB0aGUgTm9kZS5qcyBlbnZpcm9ubWVudCxcbiAgICogaXQgY2FuIGVuc3VyZSB0YXNrcyBxdWV1ZWQgaW4gdGhlIGV2ZW50IGxvb3AgY2FuIGJlIGhhbmRsZWQgaW4gYSB0aW1lbHlcbiAgICogbWFubmVyLlxuICAgKlxuICAgKiBUaGUgdmFsdWUgY2FuIGJlIG9uZSBvZiB0aGUgZm9sbG93aW5nOlxuICAgKiAgIC0gYCdhdXRvJ2A6IFRoZSB5aWVsZGluZyBoYXBwZW5zIGF0IGEgY2VydGFpbiBmcmFtZSByYXRlIChjdXJyZW50bHkgc2V0XG4gICAqICAgICAgICAgICAgICAgYXQgMTI1bXMpLiBUaGlzIGlzIHRoZSBkZWZhdWx0LlxuICAgKiAgIC0gYCdiYXRjaCdgOiB5aWVsZCBldmVyeSBiYXRjaC5cbiAgICogICAtIGAnZXBvY2gnYDogeWllbGQgZXZlcnkgZXBvY2guXG4gICAqICAgLSBhbnkgYG51bWJlcmA6IHlpZWxkIGV2ZXJ5IGBudW1iZXJgIG1pbGxpc2Vjb25kcy5cbiAgICogICAtIGAnbmV2ZXInYDogbmV2ZXIgeWllbGQuICh5aWVsZGluZyBjYW4gc3RpbGwgaGFwcGVuIHRocm91Z2ggYGF3YWl0XG4gICAqICAgICAgbmV4dEZyYW1lKClgIGNhbGxzIGluIGN1c3RvbSBjYWxsYmFja3MuKVxuICAgKi9cbiAgeWllbGRFdmVyeT86IFlpZWxkRXZlcnlPcHRpb25zO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY2hlY2tCYXRjaFNpemUoYmF0Y2hTaXplOiBudW1iZXIpIHtcbiAgdGZjLnV0aWwuYXNzZXJ0KFxuICAgICAgYmF0Y2hTaXplID4gMCAmJiBOdW1iZXIuaXNJbnRlZ2VyKGJhdGNoU2l6ZSksXG4gICAgICAoKSA9PiBgYmF0Y2hTaXplIGlzIHJlcXVpcmVkIHRvIGJlIGEgcG9zaXRpdmUgaW50ZWdlciwgYnV0IGdvdCAke1xuICAgICAgICAgIGJhdGNoU2l6ZX1gKTtcbn1cblxuLyoqXG4gKiBTbGljZSBhIFRlbnNvciBvciBhbiBBcnJheSBvZiBUZW5zb3JzLCBieSBzdGFydCBhbmQgc3RvcCBpbmRpY2VzLlxuICpcbiAqIFBvcnRpbmcgTm90ZTogVGhlIGBfc2xpY2VfYXJyYXlzYCBmdW5jdGlvbiBpbiBQeUtlcmFzIGlzIGNvdmVyZWQgYnkgdGhpc1xuICogICBmdW5jdGlvbiBhbmQgYHNsaWNlQXJyYXlzQnlJbmRpY2VzKClgIHRvZ2V0aGVyLlxuICpcbiAqIEBwYXJhbSBhcnJheXM6IHRoZSBpbnB1dC5cbiAqIEBwYXJhbSBzdGFydDogdGhlIHN0YXJ0aW5nIGluZGV4IChpbmNsdXNpdmUpLlxuICogQHBhcmFtIHN0b3A6IHRoZSBzdG9wcGluZyBpbmRleCAoZXhjbHVzaXZlKS5cbiAqIEByZXR1cm5zIFRoZSByZXN1bHQgb2YgdGhlIHNsaWNpbmcuIElmIGBhcnJheXNgIGlzIGFuIGBBcnJheWAgb2ZcbiAqICAgYHRmLlRlbnNvcmBzLCB0aGUgc2xpY2luZyB3aWxsIGJlIGFwcGxpZWQgdG8gYWxsIGVsZW1lbnRzIG9mIHRoZSBgQXJyYXlgXG4gKiAgIGluIHRoZSBzYW1lIHdheS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNsaWNlQXJyYXlzKFxuICAgIGFycmF5czogVGVuc29yfFRlbnNvcltdLCBzdGFydDogbnVtYmVyLCBzdG9wOiBudW1iZXIpOiBUZW5zb3J8VGVuc29yW10ge1xuICBpZiAoYXJyYXlzID09IG51bGwpIHtcbiAgICByZXR1cm4gW251bGxdO1xuICB9IGVsc2UgaWYgKEFycmF5LmlzQXJyYXkoYXJyYXlzKSkge1xuICAgIHJldHVybiBhcnJheXMubWFwKGFycmF5ID0+IHNsaWNlQWxvbmdGaXJzdEF4aXMoYXJyYXksIHN0YXJ0LCBzdG9wIC0gc3RhcnQpKTtcbiAgfSBlbHNlIHsgIC8vIFRlbnNvci5cbiAgICByZXR1cm4gc2xpY2VBbG9uZ0ZpcnN0QXhpcyhhcnJheXMsIHN0YXJ0LCBzdG9wIC0gc3RhcnQpO1xuICB9XG59XG5cbi8qKlxuICogU2xpY2UgYSBUZW5zb3Igb3IgYW4gQXJyYXkgb2YgVGVuc29ycywgYnkgcmFuZG9tLW9yZGVyIGluZGljZXMuXG4gKlxuICogUG9ydGluZyBOb3RlOiBUaGUgYF9zbGljZV9hcnJheXNgIGZ1bmN0aW9uIGluIFB5S2VyYXMgaXMgY292ZXJlZCBieSB0aGlzXG4gKiAgIGZ1bmN0aW9uIGFuZCBgc2xpY2VBcnJheXMoKWAgdG9nZXRoZXIuXG4gKlxuICogQHBhcmFtIGFycmF5cyBUaGUgaW5wdXQgYHRmLlRlbnNvcmAgb3IgYEFycmF5YCBvZiBgdGYuVGVuc29yYHMgdG8gc2xpY2UuXG4gKiAgIElmIGFuIGBBcnJheWAgb2YgYHRmLlRlbnNvcmBzLCBhbGwgYHRmLlRlbnNvcmBzIHdpbGwgYmUgc2xpY2VkIGluIHRoZVxuICogICBzYW1lIGZhc2hpb24uXG4gKiBAcGFyYW0gaW5kaWNlcyBUaGUgaW5kaWNlcyB0byB1c2UgZm9yIHNsaWNpbmcgYWxvbmcgdGhlIGZpcnN0IChiYXRjaClcbiAqICAgZGltZW5zaW9uLlxuICogQHJldHVybnMgUmVzdWx0KHMpIG9mIHRoZSBzbGljaW5nLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2xpY2VBcnJheXNCeUluZGljZXMoXG4gICAgYXJyYXlzOiBUZW5zb3J8VGVuc29yW10sIGluZGljZXM6IFRlbnNvcjFEKTogVGVuc29yfFRlbnNvcltdIHtcbiAgcmV0dXJuIHRmYy50aWR5KCgpID0+IHtcbiAgICBpZiAoYXJyYXlzID09IG51bGwpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH0gZWxzZSBpZiAoQXJyYXkuaXNBcnJheShhcnJheXMpKSB7XG4gICAgICByZXR1cm4gYXJyYXlzLm1hcChcbiAgICAgICAgICBhcnJheSA9PiAoc2xpY2VBcnJheXNCeUluZGljZXMoYXJyYXksIGluZGljZXMpIGFzIFRlbnNvcikpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBUT0RPKGNhaXMpOiBpbmRpY2VzIHNob3VsZCBiZSBhIHByZS1jb25zdHJ1Y3RlZCBUZW5zb3IxRCB0byBhdm9pZFxuICAgICAgLy8gICB0ZW5zb3IxZCgpIGNhbGxzLlxuICAgICAgcmV0dXJuIGdhdGhlcihcbiAgICAgICAgICBhcnJheXMsXG4gICAgICAgICAgaW5kaWNlcy5kdHlwZSA9PT0gJ2ludDMyJyA/IGluZGljZXMgOiB0ZmMuY2FzdChpbmRpY2VzLCAnaW50MzInKSk7XG4gICAgfVxuICB9KTtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIGEgbGlzdCBvZiBiYXRjaCBpbmRpY2VzICh0dXBsZXMgb2YgaW5kaWNlcykuXG4gKiBAcGFyYW0gc2l6ZTogSW50ZWdlciwgdG90YWwgc2l6ZSBvZiB0aGUgZGF0YSB0byBzbGljZSBpbnRvIGJhdGNoZXMuXG4gKiBAcGFyYW0gYmF0Y2hTaXplOiBJbnRlZ2VyLCBiYXRjaCBzaXplLlxuICogQHJldHVybnMgQW4gQXJyYXkgb2YgW2JhdGNoU3RhcnQsIGJhdGNoRW5kXSB0dXBsZXMuIGJhdGNoU3RhcnQgaXNcbiAqICAgaW5jbHVzaXZlOyBiYXRjaEVuZCBpcyBleGNsdXNpdmUuIEkuZS4sIGVhY2ggYmF0Y2ggY29uc2lzdHMgb2YgaW5kaWNlcyB4XG4gKiAgIHRoYXQgc2F0aXNmeSBiYXRjaFN0YXJ0IDw9IHggPCBiYXRjaEVuZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG1ha2VCYXRjaGVzKFxuICAgIHNpemU6IG51bWJlciwgYmF0Y2hTaXplOiBudW1iZXIpOiBBcnJheTxbbnVtYmVyLCBudW1iZXJdPiB7XG4gIGNvbnN0IG91dHB1dDogQXJyYXk8W251bWJlciwgbnVtYmVyXT4gPSBbXTtcbiAgbGV0IGJhdGNoU3RhcnQgPSAwO1xuICBsZXQgYmF0Y2hFbmQ6IG51bWJlciA9IG51bGw7XG4gIHdoaWxlIChiYXRjaFN0YXJ0IDwgc2l6ZSkge1xuICAgIGJhdGNoRW5kID0gYmF0Y2hTdGFydCArIGJhdGNoU2l6ZTtcbiAgICBpZiAoYmF0Y2hFbmQgPj0gc2l6ZSkge1xuICAgICAgYmF0Y2hFbmQgPSBzaXplO1xuICAgIH1cbiAgICBvdXRwdXQucHVzaChbYmF0Y2hTdGFydCwgYmF0Y2hFbmRdKTtcbiAgICBiYXRjaFN0YXJ0ID0gYmF0Y2hFbmQ7XG4gIH1cbiAgcmV0dXJuIG91dHB1dDtcbn1cblxuLyoqXG4gKiBFbnN1cmUgdGVuc29ycyBhbGwgaGF2ZSBhIHJhbmsgb2YgYXQgbGVhc3QgMi5cbiAqXG4gKiBJZiBhIHRlbnNvciBoYXMgYSByYW5rIG9mIDEsIGl0IGlzIGRpbWVuc2lvbi1leHBhbmRlZCB0byByYW5rIDIuXG4gKiBJZiBhbnkgdGVuc29yIGhhcyBhIHJhbmsgb2YgMCAoaS5lLiwgaXMgYSBzY2FsYXIpLCBhbiBlcnJvciB3aWxsIGJlIHRocm93bi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGVuc3VyZVRlbnNvcnNSYW5rMk9ySGlnaGVyKHRlbnNvcnM6IFRlbnNvcnxUZW5zb3JbXSk6IFRlbnNvcltdIHtcbiAgY29uc3Qgb3V0czogVGVuc29yW10gPSBbXTtcbiAgaWYgKHRlbnNvcnMgaW5zdGFuY2VvZiBUZW5zb3IpIHtcbiAgICB0ZW5zb3JzID0gW3RlbnNvcnNdO1xuICB9XG5cbiAgLy8gTWFrZSBUZW5zb3JzIGF0IGxlYXN0IDJELlxuICBmb3IgKGxldCBpID0gMDsgaSA8IHRlbnNvcnMubGVuZ3RoOyArK2kpIHtcbiAgICBjb25zdCB0ZW5zb3IgPSB0ZW5zb3JzW2ldO1xuICAgIGlmICh0ZW5zb3IucmFuayA9PT0gMSkge1xuICAgICAgb3V0cy5wdXNoKGV4cGFuZERpbXModGVuc29yLCAxKSk7XG4gICAgfSBlbHNlIGlmICh0ZW5zb3IucmFuayA9PT0gMCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdFeHBlY3RlZCB0ZW5zb3IgdG8gYmUgYXQgbGVhc3QgMUQsIGJ1dCByZWNlaXZlZCBhIDBEIHRlbnNvciAnICtcbiAgICAgICAgICAnKHNjYWxhcikuJyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIG91dHMucHVzaCh0ZW5zb3IpO1xuICAgIH1cbiAgfVxuICByZXR1cm4gb3V0cztcbn1cblxuLyoqXG4gKiBDb21wYXJlIGEgc2V0IG9mIHRlbnNvcnMgd2l0aCBhIHJlZmVyZW5jZSAob2xkKSBzZXQsIGRpc2NhcmQgdGhlIG9uZXNcbiAqIGluIHRoZSBuZXcgc2V0IHRoYXQgYXJlIG5vdCBwcmVzZW50IGluIHRoZSByZWZlcmVuY2Ugc2V0LlxuICpcbiAqIFRoaXMgbWV0aG9kIGlzIHVzZWQgZm9yIG1lbW9yeSBjbGVuYXVwIGR1cmluZyBjYWxscyBzdWNoIGFzXG4gKiBMYXllcnNNb2RlbC5maXQoKS5cbiAqXG4gKiBAcGFyYW0gdGVuc29ycyBOZXcgc2V0IHdoaWNoIG1heSBjb250YWluIFRlbnNvcnMgbm90IHByZXNlbnQgaW5cbiAqICAgYHJlZlRlbnNvcnNgLlxuICogQHBhcmFtIHJlZlRlbnNvcnMgUmVmZXJlbmNlIFRlbnNvciBzZXQuXG4gKi9cbi8vIFRPRE8oY2Fpcywga2FuZ3lpemhhbmcpOiBEZWR1cGxpY2F0ZSB3aXRoIHRmanMtZGF0YS5cbmV4cG9ydCBmdW5jdGlvbiBkaXNwb3NlTmV3VGVuc29ycyhcbiAgICB0ZW5zb3JzOiBUZW5zb3J8VGVuc29yW118e1tpbnB1dE5hbWU6IHN0cmluZ106IFRlbnNvcn0sXG4gICAgcmVmVGVuc29yczogVGVuc29yfFRlbnNvcltdfHtbaW5wdXROYW1lOiBzdHJpbmddOiBUZW5zb3J9KTogdm9pZCB7XG4gIGlmICh0ZW5zb3JzID09IG51bGwpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgY29uc3Qgb2xkVGVuc29ySWRzOiBudW1iZXJbXSA9IFtdO1xuICBpZiAocmVmVGVuc29ycyBpbnN0YW5jZW9mIFRlbnNvcikge1xuICAgIG9sZFRlbnNvcklkcy5wdXNoKHJlZlRlbnNvcnMuaWQpO1xuICB9IGVsc2UgaWYgKEFycmF5LmlzQXJyYXkocmVmVGVuc29ycykpIHtcbiAgICByZWZUZW5zb3JzLmZvckVhY2godCA9PiBvbGRUZW5zb3JJZHMucHVzaCh0LmlkKSk7XG4gIH0gZWxzZSBpZiAocmVmVGVuc29ycyAhPSBudWxsKSB7XG4gICAgLy8gYG9sZFRlbnNvcnNgIGlzIGEgbWFwIGZyb20gc3RyaW5nIG5hbWUgdG8gVGVuc29yLlxuICAgIGZvciAoY29uc3QgbmFtZSBpbiByZWZUZW5zb3JzKSB7XG4gICAgICBjb25zdCBvbGRUZW5zb3IgPSByZWZUZW5zb3JzW25hbWVdO1xuICAgICAgb2xkVGVuc29ySWRzLnB1c2gob2xkVGVuc29yLmlkKTtcbiAgICB9XG4gIH1cblxuICBjb25zdCB0ZW5zb3JzVG9EaXNwb3NlOiBUZW5zb3JbXSA9IFtdO1xuICBpZiAodGVuc29ycyBpbnN0YW5jZW9mIFRlbnNvcikge1xuICAgIGlmIChvbGRUZW5zb3JJZHMuaW5kZXhPZih0ZW5zb3JzLmlkKSA9PT0gLTEpIHtcbiAgICAgIHRlbnNvcnNUb0Rpc3Bvc2UucHVzaCh0ZW5zb3JzKTtcbiAgICB9XG4gIH0gZWxzZSBpZiAoQXJyYXkuaXNBcnJheSh0ZW5zb3JzKSkge1xuICAgIHRlbnNvcnMuZm9yRWFjaCh0ID0+IHtcbiAgICAgIGlmIChvbGRUZW5zb3JJZHMuaW5kZXhPZih0LmlkKSA9PT0gLTEpIHtcbiAgICAgICAgdGVuc29yc1RvRGlzcG9zZS5wdXNoKHQpO1xuICAgICAgfVxuICAgIH0pO1xuICB9IGVsc2UgaWYgKHRlbnNvcnMgIT0gbnVsbCkge1xuICAgIC8vIGBvbGRUZW5zb3JzYCBpcyBhIG1hcCBmcm9tIHN0cmluZyBuYW1lIHRvIFRlbnNvci5cbiAgICBmb3IgKGNvbnN0IG5hbWUgaW4gdGVuc29ycykge1xuICAgICAgY29uc3QgdGVuc29yID0gdGVuc29yc1tuYW1lXTtcbiAgICAgIGlmIChvbGRUZW5zb3JJZHMuaW5kZXhPZih0ZW5zb3IuaWQpID09PSAtMSkge1xuICAgICAgICB0ZW5zb3JzVG9EaXNwb3NlLnB1c2godGVuc29yKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICB0ZW5zb3JzVG9EaXNwb3NlLmZvckVhY2godCA9PiB7XG4gICAgaWYgKCF0LmlzRGlzcG9zZWQpIHtcbiAgICAgIHQuZGlzcG9zZSgpO1xuICAgIH1cbiAgfSk7XG59XG4iXX0=