/**
|
* @license
|
* Copyright 2018 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 { complex } from '../ops/complex';
|
import { tensor } from '../ops/tensor';
|
import { sizeFromShape } from '../util';
|
import { DTYPE_VALUE_SIZE_MAP } from './types';
|
import { CompositeArrayBuffer } from './composite_array_buffer';
|
import { backend } from '../globals';
|
import { env } from '../environment';
|
import { getBackend } from '../globals';
|
/** Number of bytes reserved for the length of the string. (32bit integer). */
|
const NUM_BYTES_STRING_LENGTH = 4;
|
/**
|
* Encode a map from names to weight values as an ArrayBuffer, along with an
|
* `Array` of `WeightsManifestEntry` as specification of the encoded weights.
|
*
|
* This function does not perform sharding.
|
*
|
* This function is the reverse of `decodeWeights`.
|
*
|
* @param tensors A map ("dict") from names to tensors.
|
* @param group Group to which the weights belong (optional).
|
* @returns A `Promise` of
|
* - A flat `ArrayBuffer` with all the binary values of the `Tensor`s
|
* concatenated.
|
* - An `Array` of `WeightManifestEntry`s, carrying information including
|
* tensor names, `dtype`s and shapes.
|
* @throws Error: on unsupported tensor `dtype`.
|
*/
|
export async function encodeWeights(tensors, group) {
|
// TODO(adarob, cais): Support quantization.
|
const specs = [];
|
const dataPromises = [];
|
const names = Array.isArray(tensors) ?
|
tensors.map(tensor => tensor.name) :
|
Object.keys(tensors);
|
for (let i = 0; i < names.length; ++i) {
|
const name = names[i];
|
const t = Array.isArray(tensors) ? tensors[i].tensor : tensors[name];
|
if (t.dtype !== 'float32' && t.dtype !== 'int32' && t.dtype !== 'bool' &&
|
t.dtype !== 'string' && t.dtype !== 'complex64') {
|
throw new Error(`Unsupported dtype in weight '${name}': ${t.dtype}`);
|
}
|
const spec = { name, shape: t.shape, dtype: t.dtype };
|
if (t.dtype === 'string') {
|
const utf8bytes = new Promise(async (resolve) => {
|
const vals = await t.bytes();
|
const totalNumBytes = vals.reduce((p, c) => p + c.length, 0) +
|
NUM_BYTES_STRING_LENGTH * vals.length;
|
const bytes = new Uint8Array(totalNumBytes);
|
let offset = 0;
|
for (let i = 0; i < vals.length; i++) {
|
const val = vals[i];
|
const bytesOfLength = new Uint8Array(new Uint32Array([val.length]).buffer);
|
bytes.set(bytesOfLength, offset);
|
offset += NUM_BYTES_STRING_LENGTH;
|
bytes.set(val, offset);
|
offset += val.length;
|
}
|
resolve(bytes);
|
});
|
dataPromises.push(utf8bytes);
|
}
|
else {
|
dataPromises.push(t.data());
|
}
|
if (group != null) {
|
spec.group = group;
|
}
|
specs.push(spec);
|
}
|
const tensorValues = await Promise.all(dataPromises);
|
return { data: concatenateTypedArrays(tensorValues), specs };
|
}
|
/**
|
* Decode flat ArrayBuffer as weights.
|
*
|
* This function does not handle sharding.
|
*
|
* This function is the reverse of `encodeWeights`.
|
*
|
* @param weightData A flat ArrayBuffer or an array of ArrayBuffers carrying the
|
* binary values of the tensors concatenated in the order specified in
|
* `specs`.
|
* @param specs Specifications of the names, dtypes and shapes of the tensors
|
* whose value are encoded by `buffer`.
|
* @return A map from tensor name to tensor value, with the names corresponding
|
* to names in `specs`.
|
* @throws Error, if any of the tensors has unsupported dtype.
|
*/
|
export function decodeWeights(weightData, specs) {
|
// TODO(adarob, cais): Support quantization.
|
const compositeBuffer = new CompositeArrayBuffer(weightData);
|
const out = {};
|
let offset = 0;
|
for (const spec of specs) {
|
const byteLength = getWeightBytelength(spec, (start, end) => {
|
return compositeBuffer.slice(offset + start, offset + end);
|
});
|
out[spec.name] = decodeWeight(spec, compositeBuffer
|
.slice(offset, offset + byteLength));
|
offset += byteLength;
|
}
|
return out;
|
}
|
function getWeightBytelength(spec, slice) {
|
const size = sizeFromShape(spec.shape);
|
let bytesPerValue;
|
if ('quantization' in spec) {
|
const quantization = spec.quantization;
|
bytesPerValue = DTYPE_VALUE_SIZE_MAP[quantization.dtype];
|
}
|
else if (spec.dtype === 'string') {
|
// Can not statically determine string length.
|
let byteLength = 0;
|
for (let i = 0; i < size; i++) {
|
byteLength += NUM_BYTES_STRING_LENGTH + new Uint32Array(slice(byteLength, byteLength + NUM_BYTES_STRING_LENGTH))[0];
|
}
|
return byteLength;
|
}
|
else {
|
bytesPerValue = DTYPE_VALUE_SIZE_MAP[spec.dtype];
|
}
|
return size * bytesPerValue;
|
}
|
async function getWeightBytelengthAsync(spec, slice) {
|
const size = sizeFromShape(spec.shape);
|
let bytesPerValue;
|
if ('quantization' in spec) {
|
const quantization = spec.quantization;
|
bytesPerValue = DTYPE_VALUE_SIZE_MAP[quantization.dtype];
|
}
|
else if (spec.dtype === 'string') {
|
// Can not statically determine string length.
|
let byteLength = 0;
|
for (let i = 0; i < size; i++) {
|
byteLength += NUM_BYTES_STRING_LENGTH + new Uint32Array(await slice(byteLength, byteLength + NUM_BYTES_STRING_LENGTH))[0];
|
}
|
return byteLength;
|
}
|
else {
|
bytesPerValue = DTYPE_VALUE_SIZE_MAP[spec.dtype];
|
}
|
return size * bytesPerValue;
|
}
|
function decodeWeight(spec, byteBuffer) {
|
const name = spec.name;
|
const dtype = spec.dtype;
|
const shape = spec.shape;
|
const size = sizeFromShape(shape);
|
let values;
|
let offset = 0;
|
if ('quantization' in spec) {
|
const quantization = spec.quantization;
|
if (quantization.dtype === 'uint8' || quantization.dtype === 'uint16') {
|
if (!('min' in quantization && 'scale' in quantization)) {
|
throw new Error(`Weight ${spec.name} with quantization ${quantization.dtype} ` +
|
`doesn't have corresponding metadata min and scale.`);
|
}
|
}
|
else if (quantization.dtype === 'float16') {
|
if (dtype !== 'float32') {
|
throw new Error(`Weight ${spec.name} is quantized with ${quantization.dtype} ` +
|
`which only supports weights of type float32 not ${dtype}.`);
|
}
|
}
|
else {
|
throw new Error(`Weight ${spec.name} has unknown ` +
|
`quantization dtype ${quantization.dtype}. ` +
|
`Supported quantization dtypes are: ` +
|
`'uint8', 'uint16', and 'float16'.`);
|
}
|
const quantizationSizeFactor = DTYPE_VALUE_SIZE_MAP[quantization.dtype];
|
const quantizedArray = (quantization.dtype === 'uint8') ?
|
new Uint8Array(byteBuffer) :
|
new Uint16Array(byteBuffer);
|
if (dtype === 'float32') {
|
if (quantization.dtype === 'uint8' || quantization.dtype === 'uint16') {
|
values = new Float32Array(quantizedArray.length);
|
for (let i = 0; i < quantizedArray.length; i++) {
|
const v = quantizedArray[i];
|
values[i] = v * quantization.scale + quantization.min;
|
}
|
}
|
else if (quantization.dtype === 'float16') {
|
// TODO: This is inefficient. Make getFloat16Decoder efficient.
|
const float16Decode = getFloat16Decoder();
|
values = float16Decode(quantizedArray);
|
}
|
else {
|
throw new Error(`Unsupported quantization type ${quantization.dtype} ` +
|
`for weight type float32.`);
|
}
|
}
|
else if (dtype === 'int32') {
|
if (quantization.dtype !== 'uint8' && quantization.dtype !== 'uint16') {
|
throw new Error(`Unsupported quantization type ${quantization.dtype} ` +
|
`for weight type int32.`);
|
}
|
values = new Int32Array(quantizedArray.length);
|
for (let i = 0; i < quantizedArray.length; i++) {
|
const v = quantizedArray[i];
|
values[i] = Math.round(v * quantization.scale + quantization.min);
|
}
|
}
|
else {
|
throw new Error(`Unsupported dtype in weight '${name}': ${dtype}`);
|
}
|
offset += size * quantizationSizeFactor;
|
}
|
else if (dtype === 'string') {
|
const size = sizeFromShape(spec.shape);
|
values = [];
|
for (let i = 0; i < size; i++) {
|
const byteLength = new Uint32Array(byteBuffer.slice(offset, offset + NUM_BYTES_STRING_LENGTH))[0];
|
offset += NUM_BYTES_STRING_LENGTH;
|
const bytes = new Uint8Array(byteBuffer.slice(offset, offset + byteLength));
|
values.push(bytes);
|
offset += byteLength;
|
}
|
}
|
else {
|
const dtypeFactor = DTYPE_VALUE_SIZE_MAP[dtype];
|
if (dtype === 'float32') {
|
values = new Float32Array(byteBuffer);
|
}
|
else if (dtype === 'int32') {
|
values = new Int32Array(byteBuffer);
|
}
|
else if (dtype === 'bool') {
|
values = new Uint8Array(byteBuffer);
|
}
|
else if (dtype === 'complex64') {
|
values = new Float32Array(byteBuffer);
|
const real = new Float32Array(values.length / 2);
|
const image = new Float32Array(values.length / 2);
|
for (let i = 0; i < real.length; i++) {
|
real[i] = values[i * 2];
|
image[i] = values[i * 2 + 1];
|
}
|
const realTensor = tensor(real, shape, 'float32');
|
const imageTensor = tensor(image, shape, 'float32');
|
const complexTensor = complex(realTensor, imageTensor);
|
realTensor.dispose();
|
imageTensor.dispose();
|
return complexTensor;
|
}
|
else {
|
throw new Error(`Unsupported dtype in weight '${name}': ${dtype}`);
|
}
|
offset += size * dtypeFactor;
|
}
|
return tensor(values, shape, dtype);
|
}
|
async function readToLength(reader, initialData, length) {
|
let data = new Uint8Array(initialData);
|
while (data.byteLength < length) {
|
const { done, value } = await reader.read();
|
if (done && value == null) {
|
const missing = length - data.byteLength;
|
throw new Error(`Reader is done but ${missing} bytes are still expected`);
|
}
|
// TODO: Don't create a new array every loop.
|
const newData = new Uint8Array(data.length + value.byteLength);
|
newData.set(data, 0);
|
newData.set(new Uint8Array(value), data.length);
|
data = newData;
|
}
|
return data.buffer;
|
}
|
export async function decodeWeightsStream(weightStream, specs) {
|
const tensors = {};
|
const reader = weightStream.getReader();
|
let data = new ArrayBuffer(0);
|
for (const spec of specs) {
|
const byteLength = await getWeightBytelengthAsync(spec, async (start, end) => {
|
data = await readToLength(reader, data, end);
|
return data.slice(start, end);
|
});
|
data = await readToLength(reader, data, byteLength);
|
// Slice the tensor out
|
const tensorData = data.slice(0, byteLength);
|
data = data.slice(byteLength);
|
const weightTensor = decodeWeight(spec, tensorData);
|
tensors[spec.name] = weightTensor;
|
// TODO(mattsoulanille): Better way to call uploadToGPU.
|
// TODO(mattsoulanille): Make this work for webgl too.
|
if (getBackend() === 'webgpu') {
|
const b = backend();
|
if ('uploadToGPU' in b &&
|
sizeFromShape(weightTensor.shape) >= env()
|
.get('WEBGPU_CPU_HANDOFF_SIZE_THRESHOLD')) {
|
b.uploadToGPU(weightTensor.dataId);
|
}
|
}
|
}
|
return tensors;
|
}
|
/**
|
* Concatenate TypedArrays into an ArrayBuffer.
|
*/
|
export function concatenateTypedArrays(xs) {
|
// TODO(adarob, cais): Support quantization.
|
if (xs === null) {
|
throw new Error(`Invalid input value: ${JSON.stringify(xs)}`);
|
}
|
let totalByteLength = 0;
|
// `normalizedXs` is here for this reason: a `TypedArray`'s `buffer'
|
// can have a different byte length from that of the `TypedArray` itself,
|
// for example, when the `TypedArray` is created from an offset in an
|
// `ArrayBuffer`. `normliazedXs` holds `TypedArray`s whose `buffer`s match
|
// the `TypedArray` in byte length. If an element of `xs` does not show
|
// this property, a new `TypedArray` that satisfy this property will be
|
// constructed and pushed into `normalizedXs`.
|
const normalizedXs = [];
|
xs.forEach((x) => {
|
totalByteLength += x.byteLength;
|
// tslint:disable:no-any
|
normalizedXs.push(x.byteLength === x.buffer.byteLength ? x :
|
new x.constructor(x));
|
if (!(x instanceof Float32Array || x instanceof Int32Array ||
|
x instanceof Uint8Array)) {
|
throw new Error(`Unsupported TypedArray subtype: ${x.constructor.name}`);
|
}
|
// tslint:enable:no-any
|
});
|
const y = new Uint8Array(totalByteLength);
|
let offset = 0;
|
normalizedXs.forEach((x) => {
|
y.set(new Uint8Array(x.buffer), offset);
|
offset += x.byteLength;
|
});
|
return y.buffer;
|
}
|
// Use Buffer on Node.js instead of Blob/atob/btoa
|
const useNodeBuffer = typeof Buffer !== 'undefined' &&
|
(typeof Blob === 'undefined' || typeof atob === 'undefined' ||
|
typeof btoa === 'undefined');
|
/**
|
* Calculate the byte length of a JavaScript string.
|
*
|
* Note that a JavaScript string can contain wide characters, therefore the
|
* length of the string is not necessarily equal to the byte length.
|
*
|
* @param str Input string.
|
* @returns Byte length.
|
*/
|
export function stringByteLength(str) {
|
if (useNodeBuffer) {
|
return Buffer.byteLength(str, 'utf8');
|
}
|
return new Blob([str]).size;
|
}
|
/**
|
* Encode an ArrayBuffer as a base64 encoded string.
|
*
|
* @param buffer `ArrayBuffer` to be converted.
|
* @returns A string that base64-encodes `buffer`.
|
*/
|
export function arrayBufferToBase64String(buffer) {
|
if (useNodeBuffer) {
|
return Buffer.from(buffer).toString('base64');
|
}
|
const buf = new Uint8Array(buffer);
|
let s = '';
|
for (let i = 0, l = buf.length; i < l; i++) {
|
s += String.fromCharCode(buf[i]);
|
}
|
return btoa(s);
|
}
|
/**
|
* Decode a base64 string as an ArrayBuffer.
|
*
|
* @param str Base64 string.
|
* @returns Decoded `ArrayBuffer`.
|
*/
|
export function base64StringToArrayBuffer(str) {
|
if (useNodeBuffer) {
|
const buf = Buffer.from(str, 'base64');
|
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
}
|
const s = atob(str);
|
const buffer = new Uint8Array(s.length);
|
for (let i = 0; i < s.length; ++i) {
|
buffer.set([s.charCodeAt(i)], i);
|
}
|
return buffer.buffer;
|
}
|
/**
|
* Concatenate a number of ArrayBuffers into one.
|
*
|
* @param buffers An array of ArrayBuffers to concatenate, or a single
|
* ArrayBuffer.
|
* @returns Result of concatenating `buffers` in order.
|
*
|
* @deprecated Use tf.io.CompositeArrayBuffer.join() instead.
|
*/
|
export function concatenateArrayBuffers(buffers) {
|
return CompositeArrayBuffer.join(buffers);
|
}
|
/**
|
* Get the basename of a path.
|
*
|
* Behaves in a way analogous to Linux's basename command.
|
*
|
* @param path
|
*/
|
export function basename(path) {
|
const SEPARATOR = '/';
|
path = path.trim();
|
while (path.endsWith(SEPARATOR)) {
|
path = path.slice(0, path.length - 1);
|
}
|
const items = path.split(SEPARATOR);
|
return items[items.length - 1];
|
}
|
/**
|
* Create `ModelJSON` from `ModelArtifacts`.
|
*
|
* @param artifacts Model artifacts, describing the model and its weights.
|
* @param manifest Weight manifest, describing where the weights of the
|
* `ModelArtifacts` are stored, and some metadata about them.
|
* @returns Object representing the `model.json` file describing the model
|
* artifacts and weights
|
*/
|
export function getModelJSONForModelArtifacts(artifacts, manifest) {
|
const result = {
|
modelTopology: artifacts.modelTopology,
|
format: artifacts.format,
|
generatedBy: artifacts.generatedBy,
|
convertedBy: artifacts.convertedBy,
|
weightsManifest: manifest
|
};
|
if (artifacts.signature != null) {
|
result.signature = artifacts.signature;
|
}
|
if (artifacts.userDefinedMetadata != null) {
|
result.userDefinedMetadata = artifacts.userDefinedMetadata;
|
}
|
if (artifacts.modelInitializer != null) {
|
result.modelInitializer = artifacts.modelInitializer;
|
}
|
if (artifacts.initializerSignature != null) {
|
result.initializerSignature = artifacts.initializerSignature;
|
}
|
if (artifacts.trainingConfig != null) {
|
result.trainingConfig = artifacts.trainingConfig;
|
}
|
return result;
|
}
|
/**
|
* Create `ModelArtifacts` from a JSON file and weights.
|
*
|
* @param modelJSON Object containing the parsed JSON of `model.json`
|
* @param weightSpecs The list of WeightsManifestEntry for the model. Must be
|
* passed if the modelJSON has a weightsManifest.
|
* @param weightData An ArrayBuffer or array of ArrayBuffers of weight data for
|
* the model corresponding to the weights in weightSpecs. Must be passed if
|
* the modelJSON has a weightsManifest.
|
* @returns A Promise of the `ModelArtifacts`, as described by the JSON file.
|
*/
|
export function getModelArtifactsForJSONSync(modelJSON, weightSpecs, weightData) {
|
const modelArtifacts = {
|
modelTopology: modelJSON.modelTopology,
|
format: modelJSON.format,
|
generatedBy: modelJSON.generatedBy,
|
convertedBy: modelJSON.convertedBy
|
};
|
if (modelJSON.trainingConfig != null) {
|
modelArtifacts.trainingConfig = modelJSON.trainingConfig;
|
}
|
if (modelJSON.weightsManifest != null) {
|
if (!weightSpecs) {
|
throw new Error('modelJSON has weightsManifest but weightSpecs is null');
|
}
|
if (!weightData) {
|
throw new Error('modelJSON has weightsManifest but weightData is null');
|
}
|
modelArtifacts.weightSpecs = weightSpecs;
|
modelArtifacts.weightData = weightData;
|
}
|
if (modelJSON.signature != null) {
|
modelArtifacts.signature = modelJSON.signature;
|
}
|
if (modelJSON.userDefinedMetadata != null) {
|
modelArtifacts.userDefinedMetadata = modelJSON.userDefinedMetadata;
|
}
|
if (modelJSON.modelInitializer != null) {
|
modelArtifacts.modelInitializer = modelJSON.modelInitializer;
|
}
|
if (modelJSON.initializerSignature != null) {
|
modelArtifacts.initializerSignature = modelJSON.initializerSignature;
|
}
|
return modelArtifacts;
|
}
|
/**
|
* Create `ModelArtifacts` from a JSON file.
|
*
|
* @param modelJSON Object containing the parsed JSON of `model.json`
|
* @param loadWeights Function that takes the JSON file's weights manifest,
|
* reads weights from the listed path(s), and returns a Promise of the
|
* weight manifest entries along with the weights data.
|
* @returns A Promise of the `ModelArtifacts`, as described by the JSON file.
|
*/
|
export async function getModelArtifactsForJSON(modelJSON, loadWeights) {
|
let weightSpecs;
|
let weightData;
|
if (modelJSON.weightsManifest != null) {
|
[weightSpecs, weightData] = await loadWeights(modelJSON.weightsManifest);
|
}
|
return getModelArtifactsForJSONSync(modelJSON, weightSpecs, weightData);
|
}
|
/**
|
* Populate ModelArtifactsInfo fields for a model with JSON topology.
|
* @param modelArtifacts
|
* @returns A ModelArtifactsInfo object.
|
*/
|
export function getModelArtifactsInfoForJSON(modelArtifacts) {
|
if (modelArtifacts.modelTopology instanceof ArrayBuffer) {
|
throw new Error('Expected JSON model topology, received ArrayBuffer.');
|
}
|
return {
|
dateSaved: new Date(),
|
modelTopologyType: 'JSON',
|
modelTopologyBytes: modelArtifacts.modelTopology == null ?
|
0 :
|
stringByteLength(JSON.stringify(modelArtifacts.modelTopology)),
|
weightSpecsBytes: modelArtifacts.weightSpecs == null ?
|
0 :
|
stringByteLength(JSON.stringify(modelArtifacts.weightSpecs)),
|
weightDataBytes: modelArtifacts.weightData == null ?
|
0 :
|
new CompositeArrayBuffer(modelArtifacts.weightData).byteLength,
|
};
|
}
|
/**
|
* Concatenate the weights stored in a WeightsManifestConfig into a list of
|
* WeightsManifestEntry
|
*
|
* @param weightsManifest The WeightsManifestConfig to extract weights from.
|
* @returns A list of WeightsManifestEntry of the weights in the weightsManifest
|
*/
|
export function getWeightSpecs(weightsManifest) {
|
const weightSpecs = [];
|
for (const entry of weightsManifest) {
|
weightSpecs.push(...entry.weights);
|
}
|
return weightSpecs;
|
}
|
/**
|
* Computes mantisa table for casting Float16 to Float32
|
* See http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf
|
*
|
* @returns Uint32Array, 2048 mantissa lookup values.
|
*/
|
function computeFloat16MantisaTable() {
|
const convertMantissa = (i) => {
|
let m = i << 13;
|
let e = 0;
|
while ((m & 0x00800000) === 0) {
|
e -= 0x00800000;
|
m <<= 1;
|
}
|
m &= ~0x00800000;
|
e += 0x38800000;
|
return m | e;
|
};
|
const mantisaTable = new Uint32Array(2048);
|
mantisaTable[0] = 0;
|
for (let i = 1; i < 1024; i++) {
|
mantisaTable[i] = convertMantissa(i);
|
}
|
for (let i = 1024; i < 2048; i++) {
|
mantisaTable[i] = 0x38000000 + ((i - 1024) << 13);
|
}
|
return mantisaTable;
|
}
|
/**
|
* Computes exponent table for casting Float16 to Float32
|
* See http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf
|
*
|
* @returns Uint32Array, 64 exponent lookup values.
|
*/
|
function computeFloat16ExponentTable() {
|
const exponentTable = new Uint32Array(64);
|
exponentTable[0] = 0;
|
exponentTable[31] = 0x47800000;
|
exponentTable[32] = 0x80000000;
|
exponentTable[63] = 0xc7800000;
|
for (let i = 1; i < 31; i++) {
|
exponentTable[i] = i << 23;
|
}
|
for (let i = 33; i < 63; i++) {
|
exponentTable[i] = 0x80000000 + ((i - 32) << 23);
|
}
|
return exponentTable;
|
}
|
/**
|
* Computes offset table for casting Float16 to Float32
|
* See http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf
|
*
|
* @returns Uint32Array, 6d offset values.
|
*/
|
function computeFloat16OffsetTable() {
|
const offsetTable = new Uint32Array(64);
|
for (let i = 0; i < 64; i++) {
|
offsetTable[i] = 1024;
|
}
|
offsetTable[0] = offsetTable[32] = 0;
|
return offsetTable;
|
}
|
/**
|
* Retrieve a Float16 decoder which will decode a ByteArray of Float16 values
|
* to a Float32Array.
|
*
|
* @returns Function (buffer: Uint16Array) => Float32Array which decodes
|
* the Uint16Array of Float16 bytes to a Float32Array.
|
*/
|
export function getFloat16Decoder() {
|
// Algorithm is based off of
|
// http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf
|
// Cache lookup tables
|
const mantisaTable = computeFloat16MantisaTable();
|
const exponentTable = computeFloat16ExponentTable();
|
const offsetTable = computeFloat16OffsetTable();
|
return (quantizedArray) => {
|
const buffer = new ArrayBuffer(4 * quantizedArray.length);
|
const bufferUint32View = new Uint32Array(buffer);
|
for (let index = 0; index < quantizedArray.length; index++) {
|
const float16Bits = quantizedArray[index];
|
const float32Bits = mantisaTable[offsetTable[float16Bits >> 10] + (float16Bits & 0x3ff)] +
|
exponentTable[float16Bits >> 10];
|
bufferUint32View[index] = float32Bits;
|
}
|
return new Float32Array(buffer);
|
};
|
}
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW9fdXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi90ZmpzLWNvcmUvc3JjL2lvL2lvX3V0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7R0FlRztBQUVILE9BQU8sRUFBQyxPQUFPLEVBQUMsTUFBTSxnQkFBZ0IsQ0FBQztBQUN2QyxPQUFPLEVBQUMsTUFBTSxFQUFDLE1BQU0sZUFBZSxDQUFDO0FBR3JDLE9BQU8sRUFBQyxhQUFhLEVBQUMsTUFBTSxTQUFTLENBQUM7QUFFdEMsT0FBTyxFQUFDLG9CQUFvQixFQUFzSCxNQUFNLFNBQVMsQ0FBQztBQUNsSyxPQUFPLEVBQUMsb0JBQW9CLEVBQUMsTUFBTSwwQkFBMEIsQ0FBQztBQUU5RCxPQUFPLEVBQUMsT0FBTyxFQUFDLE1BQU0sWUFBWSxDQUFDO0FBRW5DLE9BQU8sRUFBQyxHQUFHLEVBQUMsTUFBTSxnQkFBZ0IsQ0FBQztBQUNuQyxPQUFPLEVBQUMsVUFBVSxFQUFDLE1BQU0sWUFBWSxDQUFDO0FBRXRDLDhFQUE4RTtBQUM5RSxNQUFNLHVCQUF1QixHQUFHLENBQUMsQ0FBQztBQUVsQzs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsYUFBYSxDQUMvQixPQUFxQyxFQUFFLEtBQW1CO0lBRTVELDRDQUE0QztJQUM1QyxNQUFNLEtBQUssR0FBMkIsRUFBRSxDQUFDO0lBQ3pDLE1BQU0sWUFBWSxHQUErQixFQUFFLENBQUM7SUFFcEQsTUFBTSxLQUFLLEdBQWEsS0FBSyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQzVDLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUNwQyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBRXpCLEtBQUssSUFBSSxDQUFDLEdB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LENBQUMsY0FBMkIsRUFBRSxFQUFFO1FBQ3JDLE1BQU0sTUFBTSxHQUFHLElBQUksV0FBVyxDQUFDLENBQUMsR0FBRyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDMUQsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLFdBQVcsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNqRCxLQUFLLElBQUksS0FBSyxHQUFHLENBQUMsRUFBRSxLQUFLLEdBQUcsY0FBYyxDQUFDLE1BQU0sRUFBRSxLQUFLLEVBQUUsRUFBRTtZQUMxRCxNQUFNLFdBQVcsR0FBRyxjQUFjLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDMUMsTUFBTSxXQUFXLEdBQ2IsWUFBWSxDQUFDLFdBQVcsQ0FBQyxXQUFXLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEdBQUcsS0FBSyxDQUFDLENBQUM7Z0JBQ3BFLGFBQWEsQ0FBQyxXQUFXLElBQUksRUFBRSxDQUFDLENBQUM7WUFDckMsZ0JBQWdCLENBQUMsS0FBSyxDQUFDLEdBQUcsV0FBVyxDQUFDO1NBQ3ZDO1FBQ0QsT0FBTyxJQUFJLFlBQVksQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNsQyxDQUFDLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IDIwMTggR29vZ2xlIExMQy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqL1xuXG5pbXBvcnQge2NvbXBsZXh9IGZyb20gJy4uL29wcy9jb21wbGV4JztcbmltcG9ydCB7dGVuc29yfSBmcm9tICcuLi9vcHMvdGVuc29yJztcbmltcG9ydCB7TmFtZWRUZW5zb3IsIE5hbWVkVGVuc29yTWFwfSBmcm9tICcuLi90ZW5zb3JfdHlwZXMnO1xuaW1wb3J0IHtUeXBlZEFycmF5fSBmcm9tICcuLi90eXBlcyc7XG5pbXBvcnQge3NpemVGcm9tU2hhcGV9IGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge0RUWVBFX1ZBTFVFX1NJWkVfTUFQLCBNb2RlbEFydGlmYWN0cywgTW9kZWxBcnRpZmFjdHNJbmZvLCBNb2RlbEpTT04sIFdlaWdodERhdGEsIFdlaWdodEdyb3VwLCBXZWlnaHRzTWFuaWZlc3RDb25maWcsIFdlaWdodHNNYW5pZmVzdEVudHJ5fSBmcm9tICcuL3R5cGVzJztcbmltcG9ydCB7Q29tcG9zaXRlQXJyYXlCdWZmZXJ9IGZyb20gJy4vY29tcG9zaXRlX2FycmF5X2J1ZmZlcic7XG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vdGVuc29yJztcbmltcG9ydCB7YmFja2VuZH0gZnJvbSAnLi4vZ2xvYmFscyc7XG5pbXBvcnQge0RhdGFJZH0gZnJvbSAnLi4vdGVuc29yX2luZm8nO1xuaW1wb3J0IHtlbnZ9IGZyb20gJy4uL2Vudmlyb25tZW50JztcbmltcG9ydCB7Z2V0QmFja2VuZH0gZnJvbSAnLi4vZ2xvYmFscyc7XG5cbi8qKiBOdW1iZXIgb2YgYnl0ZXMgcmVzZXJ2ZWQgZm9yIHRoZSBsZW5ndGggb2YgdGhlIHN0cmluZy4gKDMyYml0IGludGVnZXIpLiAqL1xuY29uc3QgTlVNX0JZVEVTX1NUUklOR19MRU5HVEggPSA0O1xuXG4vKipcbiAqIEVuY29kZSBhIG1hcCBmcm9tIG5hbWVzIHRvIHdlaWdodCB2YWx1ZXMgYXMgYW4gQXJyYXlCdWZmZXIsIGFsb25nIHdpdGggYW5cbiAqIGBBcnJheWAgb2YgYFdlaWdodHNNYW5pZmVzdEVudHJ5YCBhcyBzcGVjaWZpY2F0aW9uIG9mIHRoZSBlbmNvZGVkIHdlaWdodHMuXG4gKlxuICogVGhpcyBmdW5jdGlvbiBkb2VzIG5vdCBwZXJmb3JtIHNoYXJkaW5nLlxuICpcbiAqIFRoaXMgZnVuY3Rpb24gaXMgdGhlIHJldmVyc2Ugb2YgYGRlY29kZVdlaWdodHNgLlxuICpcbiAqIEBwYXJhbSB0ZW5zb3JzIEEgbWFwIChcImRpY3RcIikgZnJvbSBuYW1lcyB0byB0ZW5zb3JzLlxuICogQHBhcmFtIGdyb3VwIEdyb3VwIHRvIHdoaWNoIHRoZSB3ZWlnaHRzIGJlbG9uZyAob3B0aW9uYWwpLlxuICogQHJldHVybnMgQSBgUHJvbWlzZWAgb2ZcbiAqICAgLSBBIGZsYXQgYEFycmF5QnVmZmVyYCB3aXRoIGFsbCB0aGUgYmluYXJ5IHZhbHVlcyBvZiB0aGUgYFRlbnNvcmBzXG4gKiAgICAgY29uY2F0ZW5hdGVkLlxuICogICAtIEFuIGBBcnJheWAgb2YgYFdlaWdodE1hbmlmZXN0RW50cnlgcywgY2FycnlpbmcgaW5mb3JtYXRpb24gaW5jbHVkaW5nXG4gKiAgICAgdGVuc29yIG5hbWVzLCBgZHR5cGVgcyBhbmQgc2hhcGVzLlxuICogQHRocm93cyBFcnJvcjogb24gdW5zdXBwb3J0ZWQgdGVuc29yIGBkdHlwZWAuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBlbmNvZGVXZWlnaHRzKFxuICAgIHRlbnNvcnM6IE5hbWVkVGVuc29yTWFwfE5hbWVkVGVuc29yW10sIGdyb3VwPzogV2VpZ2h0R3JvdXApOlxuICAgIFByb21pc2U8e2RhdGE6IEFycmF5QnVmZmVyLCBzcGVjczogV2VpZ2h0c01hbmlmZXN0RW50cnlbXX0+IHtcbiAgLy8gVE9ETyhhZGFyb2IsIGNhaXMpOiBTdXBwb3J0IHF1YW50aXphdGlvbi5cbiAgY29uc3Qgc3BlY3M6IFdlaWdodHNNYW5pZmVzdEVudHJ5W10gPSBbXTtcbiAgY29uc3QgZGF0YVByb21pc2VzOiBBcnJheTxQcm9taXNlPFR5cGVkQXJyYXk+PiA9IFtdO1xuXG4gIGNvbnN0IG5hbWVzOiBzdHJpbmdbXSA9IEFycmF5LmlzQXJyYXkodGVuc29ycykgP1xuICAgICAgdGVuc29ycy5tYXAodGVuc29yID0+IHRlbnNvci5uYW1lKSA6XG4gICAgICBPYmplY3Qua2V5cyh0ZW5zb3JzKTtcblxuICBmb3IgKGxldCBpID0gMDsgaSA8IG5hbWVzLmxlbmd0aDsgKytpKSB7XG4gICAgY29uc3QgbmFtZSA9IG5hbWVzW2ldO1xuICAgIGNvbnN0IHQgPSBBcnJheS5pc0FycmF5KHRlbnNvcnMpID8gdGVuc29yc1tpXS50ZW5zb3IgOiB0ZW5zb3JzW25hbWVdO1xuICAgIGlmICh0LmR0eXBlICE9PSAnZmxvYXQzMicgJiYgdC5kdHlwZSAhPT0gJ2ludDMyJyAmJiB0LmR0eXBlICE9PSAnYm9vbCcgJiZcbiAgICAgICAgdC5kdHlwZSAhPT0gJ3N0cmluZycgJiYgdC5kdHlwZSAhPT0gJ2NvbXBsZXg2NCcpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgVW5zdXBwb3J0ZWQgZHR5cGUgaW4gd2VpZ2h0ICcke25hbWV9JzogJHt0LmR0eXBlfWApO1xuICAgIH1cbiAgICBjb25zdCBzcGVjOiBXZWlnaHRzTWFuaWZlc3RFbnRyeSA9IHtuYW1lLCBzaGFwZTogdC5zaGFwZSwgZHR5cGU6IHQuZHR5cGV9O1xuICAgIGlmICh0LmR0eXBlID09PSAnc3RyaW5nJykge1xuICAgICAgY29uc3QgdXRmOGJ5dGVzID0gbmV3IFByb21pc2U8VHlwZWRBcnJheT4oYXN5bmMgcmVzb2x2ZSA9PiB7XG4gICAgICAgIGNvbnN0IHZhbHMgPSBhd2FpdCB0LmJ5dGVzKCkgYXMgVWludDhBcnJheVtdO1xuICAgICAgICBjb25zdCB0b3RhbE51bUJ5dGVzID0gdmFscy5yZWR1Y2UoKHAsIGMpID0+IHAgKyBjLmxlbmd0aCwgMCkgK1xuICAgICAgICAgICAgTlVNX0JZVEVTX1NUUklOR19MRU5HVEggKiB2YWxzLmxlbmd0aDtcbiAgICAgICAgY29uc3QgYnl0ZXMgPSBuZXcgVWludDhBcnJheSh0b3RhbE51bUJ5dGVzKTtcbiAgICAgICAgbGV0IG9mZnNldCA9IDA7XG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFscy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgIGNvbnN0IHZhbCA9IHZhbHNbaV07XG4gICAgICAgICAgY29uc3QgYnl0ZXNPZkxlbmd0aCA9XG4gICAgICAgICAgICAgIG5ldyBVaW50OEFycmF5KG5ldyBVaW50MzJBcnJheShbdmFsLmxlbmd0aF0pLmJ1ZmZlcik7XG4gICAgICAgICAgYnl0ZXMuc2V0KGJ5dGVzT2ZMZW5ndGgsIG9mZnNldCk7XG4gICAgICAgICAgb2Zmc2V0ICs9IE5VTV9CWVRFU19TVFJJTkdfTEVOR1RIO1xuICAgICAgICAgIGJ5dGVzLnNldCh2YWwsIG9mZnNldCk7XG4gICAgICAgICAgb2Zmc2V0ICs9IHZhbC5sZW5ndGg7XG4gICAgICAgIH1cbiAgICAgICAgcmVzb2x2ZShieXRlcyk7XG4gICAgICB9KTtcbiAgICAgIGRhdGFQcm9taXNlcy5wdXNoKHV0ZjhieXRlcyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGRhdGFQcm9taXNlcy5wdXNoKHQuZGF0YSgpKTtcbiAgICB9XG4gICAgaWYgKGdyb3VwICE9IG51bGwpIHtcbiAgICAgIHNwZWMuZ3JvdXAgPSBncm91cDtcbiAgICB9XG4gICAgc3BlY3MucHVzaChzcGVjKTtcbiAgfVxuXG4gIGNvbnN0IHRlbnNvclZhbHVlcyA9IGF3YWl0IFByb21pc2UuYWxsKGRhdGFQcm9taXNlcyk7XG4gIHJldHVybiB7ZGF0YTogY29uY2F0ZW5hdGVUeXBlZEFycmF5cyh0ZW5zb3JWYWx1ZXMpLCBzcGVjc307XG59XG5cbi8qKlxuICogRGVjb2RlIGZsYXQgQXJyYXlCdWZmZXIgYXMgd2VpZ2h0cy5cbiAqXG4gKiBUaGlzIGZ1bmN0aW9uIGRvZXMgbm90IGhhbmRsZSBzaGFyZGluZy5cbiAqXG4gKiBUaGlzIGZ1bmN0aW9uIGlzIHRoZSByZXZlcnNlIG9mIGBlbmNvZGVXZWlnaHRzYC5cbiAqXG4gKiBAcGFyYW0gd2VpZ2h0RGF0YSBBIGZsYXQgQXJyYXlCdWZmZXIgb3IgYW4gYXJyYXkgb2YgQXJyYXlCdWZmZXJzIGNhcnJ5aW5nIHRoZVxuICogICBiaW5hcnkgdmFsdWVzIG9mIHRoZSB0ZW5zb3JzIGNvbmNhdGVuYXRlZCBpbiB0aGUgb3JkZXIgc3BlY2lmaWVkIGluXG4gKiAgIGBzcGVjc2AuXG4gKiBAcGFyYW0gc3BlY3MgU3BlY2lmaWNhdGlvbnMgb2YgdGhlIG5hbWVzLCBkdHlwZXMgYW5kIHNoYXBlcyBvZiB0aGUgdGVuc29yc1xuICogICB3aG9zZSB2YWx1ZSBhcmUgZW5jb2RlZCBieSBgYnVmZmVyYC5cbiAqIEByZXR1cm4gQSBtYXAgZnJvbSB0ZW5zb3IgbmFtZSB0byB0ZW5zb3IgdmFsdWUsIHdpdGggdGhlIG5hbWVzIGNvcnJlc3BvbmRpbmdcbiAqICAgdG8gbmFtZXMgaW4gYHNwZWNzYC5cbiAqIEB0aHJvd3MgRXJyb3IsIGlmIGFueSBvZiB0aGUgdGVuc29ycyBoYXMgdW5zdXBwb3J0ZWQgZHR5cGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkZWNvZGVXZWlnaHRzKFxuICAgIHdlaWdodERhdGE6IFdlaWdodERhdGEsXG4gICAgc3BlY3M6IFdlaWdodHNNYW5pZmVzdEVudHJ5W10pOiBOYW1lZFRlbnNvck1hcCB7XG4gIC8vIFRPRE8oYWRhcm9iLCBjYWlzKTogU3VwcG9ydCBxdWFudGl6YXRpb24uXG4gIGNvbnN0IGNvbXBvc2l0ZUJ1ZmZlciA9IG5ldyBDb21wb3NpdGVBcnJheUJ1ZmZlcih3ZWlnaHREYXRhKTtcbiAgY29uc3Qgb3V0OiBOYW1lZFRlbnNvck1hcCA9IHt9O1xuICBsZXQgb2Zmc2V0ID0gMDtcbiAgZm9yIChjb25zdCBzcGVjIG9mIHNwZWNzKSB7XG4gICAgY29uc3QgYnl0ZUxlbmd0aCA9IGdldFdlaWdodEJ5dGVsZW5ndGgoc3BlYywgKHN0YXJ0LCBlbmQpID0+IHtcbiAgICAgIHJldHVybiBjb21wb3NpdGVCdWZmZXIuc2xpY2Uob2Zmc2V0ICsgc3RhcnQsIG9mZnNldCArIGVuZCk7XG4gICAgfSk7XG4gICAgb3V0W3NwZWMubmFtZV0gPSBkZWNvZGVXZWlnaHQoc3BlYywgY29tcG9zaXRlQnVmZmVyXG4gICAgICAuc2xpY2Uob2Zmc2V0LCBvZmZzZXQgKyBieXRlTGVuZ3RoKSk7XG4gICAgb2Zmc2V0ICs9IGJ5dGVMZW5ndGg7XG4gIH1cbiAgcmV0dXJuIG91dDtcbn1cblxuZnVuY3Rpb24gZ2V0V2VpZ2h0Qnl0ZWxlbmd0aChzcGVjOiBXZWlnaHRzTWFuaWZlc3RFbnRyeSxcbiAgc2xpY2U6IChzdGFydDogbnVtYmVyLCBlbmQ6IG51bWJlcikgPT4gQXJyYXlCdWZmZXIpOiBudW1iZXIge1xuXG4gIGNvbnN0IHNpemUgPSBzaXplRnJvbVNoYXBlKHNwZWMuc2hhcGUpO1xuICBsZXQgYnl0ZXNQZXJWYWx1ZTogbnVtYmVyO1xuICBpZiAoJ3F1YW50aXphdGlvbicgaW4gc3BlYykge1xuICAgIGNvbnN0IHF1YW50aXphdGlvbiA9IHNwZWMucXVhbnRpemF0aW9uO1xuICAgIGJ5dGVzUGVyVmFsdWUgPSBEVFlQRV9WQUxVRV9TSVpFX01BUFtxdWFudGl6YXRpb24uZHR5cGVdO1xuICB9IGVsc2UgaWYgKHNwZWMuZHR5cGUgPT09ICdzdHJpbmcnKSB7XG4gICAgLy8gQ2FuIG5vdCBzdGF0aWNhbGx5IGRldGVybWluZSBzdHJpbmcgbGVuZ3RoLlxuICAgIGxldCBieXRlTGVuZ3RoID0gMDtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHNpemU7IGkrKykge1xuICAgICAgYnl0ZUxlbmd0aCArPSBOVU1fQllURVNfU1RSSU5HX0xFTkdUSCArIG5ldyBVaW50MzJBcnJheShcbiAgICAgICAgc2xpY2UoYnl0ZUxlbmd0aCwgYnl0ZUxlbmd0aCArIE5VTV9CWVRFU19TVFJJTkdfTEVOR1RIKSlbMF07XG4gICAgfVxuICAgIHJldHVybiBieXRlTGVuZ3RoO1xuICB9IGVsc2Uge1xuICAgIGJ5dGVzUGVyVmFsdWUgPSBEVFlQRV9WQUxVRV9TSVpFX01BUFtzcGVjLmR0eXBlXTtcbiAgfVxuXG4gIHJldHVybiBzaXplICogYnl0ZXNQZXJWYWx1ZTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZ2V0V2VpZ2h0Qnl0ZWxlbmd0aEFzeW5jKFxuICBzcGVjOiBXZWlnaHRzTWFuaWZlc3RFbnRyeSxcbiAgc2xpY2U6IChzdGFydDogbnVtYmVyLCBlbmQ6IG51bWJlcikgPT4gUHJvbWlzZTxBcnJheUJ1ZmZlcj5cbik6IFByb21pc2U8bnVtYmVyPiB7XG5cbiAgY29uc3Qgc2l6ZSA9IHNpemVGcm9tU2hhcGUoc3BlYy5zaGFwZSk7XG4gIGxldCBieXRlc1BlclZhbHVlOiBudW1iZXI7XG4gIGlmICgncXVhbnRpemF0aW9uJyBpbiBzcGVjKSB7XG4gICAgY29uc3QgcXVhbnRpemF0aW9uID0gc3BlYy5xdWFudGl6YXRpb247XG4gICAgYnl0ZXNQZXJWYWx1ZSA9IERUWVBFX1ZBTFVFX1NJWkVfTUFQW3F1YW50aXphdGlvbi5kdHlwZV07XG4gIH0gZWxzZSBpZiAoc3BlYy5kdHlwZSA9PT0gJ3N0cmluZycpIHtcbiAgICAvLyBDYW4gbm90IHN0YXRpY2FsbHkgZGV0ZXJtaW5lIHN0cmluZyBsZW5ndGguXG4gICAgbGV0IGJ5dGVMZW5ndGggPSAwO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc2l6ZTsgaSsrKSB7XG4gICAgICBieXRlTGVuZ3RoICs9IE5VTV9CWVRFU19TVFJJTkdfTEVOR1RIICsgbmV3IFVpbnQzMkFycmF5KFxuICAgICAgICBhd2FpdCBzbGljZShieXRlTGVuZ3RoLCBieXRlTGVuZ3RoICsgTlVNX0JZVEVTX1NUUklOR19MRU5HVEgpKVswXTtcbiAgICB9XG4gICAgcmV0dXJuIGJ5dGVMZW5ndGg7XG4gIH0gZWxzZSB7XG4gICAgYnl0ZXNQZXJWYWx1ZSA9IERUWVBFX1ZBTFVFX1NJWkVfTUFQW3NwZWMuZHR5cGVdO1xuICB9XG5cbiAgcmV0dXJuIHNpemUgKiBieXRlc1BlclZhbHVlO1xufVxuXG5mdW5jdGlvbiBkZWNvZGVXZWlnaHQoXG4gIHNwZWM6IFdlaWdodHNNYW5pZmVzdEVudHJ5LFxuICBieXRlQnVmZmVyOiBBcnJheUJ1ZmZlcik6IFRlbnNvciB7XG5cbiAgY29uc3QgbmFtZSA9IHNwZWMubmFtZTtcbiAgY29uc3QgZHR5cGUgPSBzcGVjLmR0eXBlO1xuICBjb25zdCBzaGFwZSA9IHNwZWMuc2hhcGU7XG4gIGNvbnN0IHNpemUgPSBzaXplRnJvbVNoYXBlKHNoYXBlKTtcbiAgbGV0IHZhbHVlczogVHlwZWRBcnJheSB8IHN0cmluZ1tdIHwgVWludDhBcnJheVtdO1xuICBsZXQgb2Zmc2V0ID0gMDtcblxuICBpZiAoJ3F1YW50aXphdGlvbicgaW4gc3BlYykge1xuICAgIGNvbnN0IHF1YW50aXphdGlvbiA9IHNwZWMucXVhbnRpemF0aW9uO1xuICAgIGlmIChxdWFudGl6YXRpb24uZHR5cGUgPT09ICd1aW50OCcgfHwgcXVhbnRpemF0aW9uLmR0eXBlID09PSAndWludDE2Jykge1xuICAgICAgaWYgKCEoJ21pbicgaW4gcXVhbnRpemF0aW9uICYmICdzY2FsZScgaW4gcXVhbnRpemF0aW9uKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgV2VpZ2h0ICR7c3BlYy5uYW1lfSB3aXRoIHF1YW50aXphdGlvbiAke3F1YW50aXphdGlvbi5kdHlwZX0gYCArXG4gICAgICAgICAgICBgZG9lc24ndCBoYXZlIGNvcnJlc3BvbmRpbmcgbWV0YWRhdGEgbWluIGFuZCBzY2FsZS5gKTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKHF1YW50aXphdGlvbi5kdHlwZSA9PT0gJ2Zsb2F0MTYnKSB7XG4gICAgICBpZiAoZHR5cGUgIT09ICdmbG9hdDMyJykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgV2VpZ2h0ICR7c3BlYy5uYW1lfSBpcyBxdWFudGl6ZWQgd2l0aCAke3F1YW50aXphdGlvbi5kdHlwZX0gYCArXG4gICAgICAgICAgICBgd2hpY2ggb25seSBzdXBwb3J0cyB3ZWlnaHRzIG9mIHR5cGUgZmxvYXQzMiBub3QgJHtkdHlwZX0uYCk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICBgV2VpZ2h0ICR7c3BlYy5uYW1lfSBoYXMgdW5rbm93biBgICtcbiAgICAgICAgICBgcXVhbnRpemF0aW9uIGR0eXBlICR7cXVhbnRpemF0aW9uLmR0eXBlfS4gYCArXG4gICAgICAgICAgYFN1cHBvcnRlZCBxdWFudGl6YXRpb24gZHR5cGVzIGFyZTogYCArXG4gICAgICAgICAgYCd1aW50OCcsICd1aW50MTYnLCBhbmQgJ2Zsb2F0MTYnLmApO1xuICAgIH1cbiAgICBjb25zdCBxdWFudGl6YXRpb25TaXplRmFjdG9yID0gRFRZUEVfVkFMVUVfU0laRV9NQVBbcXVhbnRpemF0aW9uLmR0eXBlXTtcbiAgICBjb25zdCBxdWFudGl6ZWRBcnJheSA9IChxdWFudGl6YXRpb24uZHR5cGUgPT09ICd1aW50OCcpID9cbiAgICAgIG5ldyBVaW50OEFycmF5KGJ5dGVCdWZmZXIpIDpcbiAgICAgIG5ldyBVaW50MTZBcnJheShieXRlQnVmZmVyKTtcbiAgICBpZiAoZHR5cGUgPT09ICdmbG9hdDMyJykge1xuICAgICAgaWYgKHF1YW50aXphdGlvbi5kdHlwZSA9PT0gJ3VpbnQ4JyB8fCBxdWFudGl6YXRpb24uZHR5cGUgPT09ICd1aW50MTYnKSB7XG4gICAgICAgIHZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkocXVhbnRpemVkQXJyYXkubGVuZ3RoKTtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBxdWFudGl6ZWRBcnJheS5sZW5ndGg7IGkrKykge1xuICAgICAgICAgIGNvbnN0IHYgPSBxdWFudGl6ZWRBcnJheVtpXTtcbiAgICAgICAgICB2YWx1ZXNbaV0gPSB2ICogcXVhbnRpemF0aW9uLnNjYWxlICsgcXVhbnRpemF0aW9uLm1pbjtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChxdWFudGl6YXRpb24uZHR5cGUgPT09ICdmbG9hdDE2Jykge1xuICAgICAgICAvLyBUT0RPOiBUaGlzIGlzIGluZWZmaWNpZW50LiBNYWtlIGdldEZsb2F0MTZEZWNvZGVyIGVmZmljaWVudC5cbiAgICAgICAgY29uc3QgZmxvYXQxNkRlY29kZSA9IGdldEZsb2F0MTZEZWNvZGVyKCk7XG4gICAgICAgIHZhbHVlcyA9IGZsb2F0MTZEZWNvZGUocXVhbnRpemVkQXJyYXkgYXMgVWludDE2QXJyYXkpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGBVbnN1cHBvcnRlZCBxdWFudGl6YXRpb24gdHlwZSAke3F1YW50aXphdGlvbi5kdHlwZX0gYCArXG4gICAgICAgICAgYGZvciB3ZWlnaHQgdHlwZSBmbG9hdDMyLmApO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAoZHR5cGUgPT09ICdpbnQzMicpIHtcbiAgICAgIGlmIChxdWFudGl6YXRpb24uZHR5cGUgIT09ICd1aW50OCcgJiYgcXVhbnRpemF0aW9uLmR0eXBlICE9PSAndWludDE2Jykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYFVuc3VwcG9ydGVkIHF1YW50aXphdGlvbiB0eXBlICR7cXVhbnRpemF0aW9uLmR0eXBlfSBgICtcbiAgICAgICAgICBgZm9yIHdlaWdodCB0eXBlIGludDMyLmApO1xuICAgICAgfVxuICAgICAgdmFsdWVzID0gbmV3IEludDMyQXJyYXkocXVhbnRpemVkQXJyYXkubGVuZ3RoKTtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcXVhbnRpemVkQXJyYXkubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgY29uc3QgdiA9IHF1YW50aXplZEFycmF5W2ldO1xuICAgICAgICB2YWx1ZXNbaV0gPSBNYXRoLnJvdW5kKHYgKiBxdWFudGl6YXRpb24uc2NhbGUgKyBxdWFudGl6YXRpb24ubWluKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbnN1cHBvcnRlZCBkdHlwZSBpbiB3ZWlnaHQgJyR7bmFtZX0nOiAke2R0eXBlfWApO1xuICAgIH1cbiAgICBvZmZzZXQgKz0gc2l6ZSAqIHF1YW50aXphdGlvblNpemVGYWN0b3I7XG4gIH0gZWxzZSBpZiAoZHR5cGUgPT09ICdzdHJpbmcnKSB7XG4gICAgY29uc3Qgc2l6ZSA9IHNpemVGcm9tU2hhcGUoc3BlYy5zaGFwZSk7XG4gICAgdmFsdWVzID0gW107XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzaXplOyBpKyspIHtcbiAgICAgIGNvbnN0IGJ5dGVMZW5ndGggPSBuZXcgVWludDMyQXJyYXkoXG4gICAgICAgIGJ5dGVCdWZmZXIuc2xpY2Uob2Zmc2V0LCBvZmZzZXQgKyBOVU1fQllURVNfU1RSSU5HX0xFTkdUSCkpWzBdO1xuICAgICAgb2Zmc2V0ICs9IE5VTV9CWVRFU19TVFJJTkdfTEVOR1RIO1xuICAgICAgY29uc3QgYnl0ZXMgPSBuZXcgVWludDhBcnJheShcbiAgICAgICAgYnl0ZUJ1ZmZlci5zbGljZShvZmZzZXQsIG9mZnNldCArIGJ5dGVMZW5ndGgpKTtcbiAgICAgICh2YWx1ZXMgYXMgVWludDhBcnJheVtdKS5wdXNoKGJ5dGVzKTtcbiAgICAgIG9mZnNldCArPSBieXRlTGVuZ3RoO1xuICAgIH1cbiAgfSBlbHNlIHtcbiAgICBjb25zdCBkdHlwZUZhY3RvciA9IERUWVBFX1ZBTFVFX1NJWkVfTUFQW2R0eXBlXTtcbiAgICBpZiAoZHR5cGUgPT09ICdmbG9hdDMyJykge1xuICAgICAgdmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShieXRlQnVmZmVyKTtcbiAgICB9IGVsc2UgaWYgKGR0eXBlID09PSAnaW50MzInKSB7XG4gICAgICB2YWx1ZXMgPSBuZXcgSW50MzJBcnJheShieXRlQnVmZmVyKTtcbiAgICB9IGVsc2UgaWYgKGR0eXBlID09PSAnYm9vbCcpIHtcbiAgICAgIHZhbHVlcyA9IG5ldyBVaW50OEFycmF5KGJ5dGVCdWZmZXIpO1xuICAgIH0gZWxzZSBpZiAoZHR5cGUgPT09ICdjb21wbGV4NjQnKSB7XG4gICAgICB2YWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGJ5dGVCdWZmZXIpO1xuICAgICAgY29uc3QgcmVhbCA9IG5ldyBGbG9hdDMyQXJyYXkodmFsdWVzLmxlbmd0aCAvIDIpO1xuICAgICAgY29uc3QgaW1hZ2UgPSBuZXcgRmxvYXQzMkFycmF5KHZhbHVlcy5sZW5ndGggLyAyKTtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcmVhbC5sZW5ndGg7IGkrKykge1xuICAgICAgICByZWFsW2ldID0gdmFsdWVzW2kgKiAyXTtcbiAgICAgICAgaW1hZ2VbaV0gPSB2YWx1ZXNbaSAqIDIgKyAxXTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHJlYWxUZW5zb3IgPSB0ZW5zb3IocmVhbCwgc2hhcGUsICdmbG9hdDMyJyk7XG4gICAgICBjb25zdCBpbWFnZVRlbnNvciA9IHRlbnNvcihpbWFnZSwgc2hhcGUsICdmbG9hdDMyJyk7XG4gICAgICBjb25zdCBjb21wbGV4VGVuc29yID0gY29tcGxleChyZWFsVGVuc29yLCBpbWFnZVRlbnNvcik7XG4gICAgICByZWFsVGVuc29yLmRpc3Bvc2UoKTtcbiAgICAgIGltYWdlVGVuc29yLmRpc3Bvc2UoKTtcbiAgICAgIHJldHVybiBjb21wbGV4VGVuc29yO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFVuc3VwcG9ydGVkIGR0eXBlIGluIHdlaWdodCAnJHtuYW1lfSc6ICR7ZHR5cGV9YCk7XG4gICAgfVxuICAgIG9mZnNldCArPSBzaXplICogZHR5cGVGYWN0b3I7XG4gIH1cbiAgcmV0dXJuIHRlbnNvcih2YWx1ZXMsIHNoYXBlLCBkdHlwZSk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHJlYWRUb0xlbmd0aChyZWFkZXI6IFJlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlcjxBcnJheUJ1ZmZlcj4sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5pdGlhbERhdGE6IEFycmF5QnVmZmVyLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aDogbnVtYmVyKTogUHJvbWlzZTxBcnJheUJ1ZmZlcj4ge1xuICBsZXQgZGF0YSA9IG5ldyBVaW50OEFycmF5KGluaXRpYWxEYXRhKTtcblxuICB3aGlsZSAoZGF0YS5ieXRlTGVuZ3RoIDwgbGVuZ3RoKSB7XG4gICAgY29uc3Qge2RvbmUsIHZhbHVlfSA9IGF3YWl0IHJlYWRlci5yZWFkKCk7XG4gICAgaWYgKGRvbmUgJiYgdmFsdWUgPT0gbnVsbCkge1xuICAgICAgY29uc3QgbWlzc2luZyAgPSBsZW5ndGggLSBkYXRhLmJ5dGVMZW5ndGg7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFJlYWRlciBpcyBkb25lIGJ1dCAke21pc3Npbmd9IGJ5dGVzIGFyZSBzdGlsbCBleHBlY3RlZGApO1xuICAgIH1cblxuICAgIC8vIFRPRE86IERvbid0IGNyZWF0ZSBhIG5ldyBhcnJheSBldmVyeSBsb29wLlxuICAgIGNvbnN0IG5ld0RhdGEgPSBuZXcgVWludDhBcnJheShkYXRhLmxlbmd0aCArIHZhbHVlLmJ5dGVMZW5ndGgpO1xuICAgIG5ld0RhdGEuc2V0KGRhdGEsIDApO1xuICAgIG5ld0RhdGEuc2V0KG5ldyBVaW50OEFycmF5KHZhbHVlKSwgZGF0YS5sZW5ndGgpO1xuICAgIGRhdGEgPSBuZXdEYXRhO1xuICB9XG5cbiAgcmV0dXJuIGRhdGEuYnVmZmVyO1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZGVjb2RlV2VpZ2h0c1N0cmVhbShcbiAgd2VpZ2h0U3RyZWFtOiBSZWFkYWJsZVN0cmVhbTxBcnJheUJ1ZmZlcj4sXG4gIHNwZWNzOiBXZWlnaHRzTWFuaWZlc3RFbnRyeVtdKTogUHJvbWlzZTxOYW1lZFRlbnNvck1hcD4ge1xuXG4gIGNvbnN0IHRlbnNvcnM6IE5hbWVkVGVuc29yTWFwID0ge307XG4gIGNvbnN0IHJlYWRlciA9IHdlaWdodFN0cmVhbS5nZXRSZWFkZXIoKTtcbiAgbGV0IGRhdGEgPSBuZXcgQXJyYXlCdWZmZXIoMCk7XG5cbiAgZm9yIChjb25zdCBzcGVjIG9mIHNwZWNzKSB7XG4gICAgY29uc3QgYnl0ZUxlbmd0aCA9IGF3YWl0IGdldFdlaWdodEJ5dGVsZW5ndGhBc3luYyhzcGVjLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXN5bmMgKHN0YXJ0LCBlbmQpID0+IHtcbiAgICAgIGRhdGEgPSBhd2FpdCByZWFkVG9MZW5ndGgocmVhZGVyLCBkYXRhLCBlbmQpO1xuICAgICAgcmV0dXJuIGRhdGEuc2xpY2Uoc3RhcnQsIGVuZCk7XG4gICAgfSk7XG4gICAgZGF0YSA9IGF3YWl0IHJlYWRUb0xlbmd0aChyZWFkZXIsIGRhdGEsIGJ5dGVMZW5ndGgpO1xuXG4gICAgLy8gU2xpY2UgdGhlIHRlbnNvciBvdXRcbiAgICBjb25zdCB0ZW5zb3JEYXRhID0gZGF0YS5zbGljZSgwLCBieXRlTGVuZ3RoKTtcbiAgICBkYXRhID0gZGF0YS5zbGljZShieXRlTGVuZ3RoKTtcblxuICAgIGNvbnN0IHdlaWdodFRlbnNvciA9IGRlY29kZVdlaWdodChzcGVjLCB0ZW5zb3JEYXRhKTtcbiAgICB0ZW5zb3JzW3NwZWMubmFtZV0gPSB3ZWlnaHRUZW5zb3I7XG5cbiAgICAvLyBUT0RPKG1hdHRzb3VsYW5pbGxlKTogQmV0dGVyIHdheSB0byBjYWxsIHVwbG9hZFRvR1BVLlxuICAgIC8vIFRPRE8obWF0dHNvdWxhbmlsbGUpOiBNYWtlIHRoaXMgd29yayBmb3Igd2ViZ2wgdG9vLlxuICAgIGlmIChnZXRCYWNrZW5kKCkgPT09ICd3ZWJncHUnKSB7XG4gICAgICBjb25zdCBiID0gYmFja2VuZCgpO1xuXG4gICAgICBpZiAoJ3VwbG9hZFRvR1BVJyBpbiBiICYmXG4gICAgICAgIHNpemVGcm9tU2hhcGUod2VpZ2h0VGVuc29yLnNoYXBlKSA+PSAoZW52KClcbiAgICAgICAgICAuZ2V0KCdXRUJHUFVfQ1BVX0hBTkRPRkZfU0laRV9USFJFU0hPTEQnKSBhcyBudW1iZXIpKSB7XG4gICAgICAgIChiLnVwbG9hZFRvR1BVIGFzIChkYXRhSWQ6IERhdGFJZCkgPT4gdm9pZCkod2VpZ2h0VGVuc29yLmRhdGFJZCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHRlbnNvcnM7XG59XG5cbi8qKlxuICogQ29uY2F0ZW5hdGUgVHlwZWRBcnJheXMgaW50byBhbiBBcnJheUJ1ZmZlci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNvbmNhdGVuYXRlVHlwZWRBcnJheXMoeHM6IFR5cGVkQXJyYXlbXSk6IEFycmF5QnVmZmVyIHtcbiAgLy8gVE9ETyhhZGFyb2IsIGNhaXMpOiBTdXBwb3J0IHF1YW50aXphdGlvbi5cbiAgaWYgKHhzID09PSBudWxsKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGlucHV0IHZhbHVlOiAke0pTT04uc3RyaW5naWZ5KHhzKX1gKTtcbiAgfVxuXG4gIGxldCB0b3RhbEJ5dGVMZW5ndGggPSAwO1xuXG4gIC8vIGBub3JtYWxpemVkWHNgIGlzIGhlcmUgZm9yIHRoaXMgcmVhc29uOiBhIGBUeXBlZEFycmF5YCdzIGBidWZmZXInXG4gIC8vIGNhbiBoYXZlIGEgZGlmZmVyZW50IGJ5dGUgbGVuZ3RoIGZyb20gdGhhdCBvZiB0aGUgYFR5cGVkQXJyYXlgIGl0c2VsZixcbiAgLy8gZm9yIGV4YW1wbGUsIHdoZW4gdGhlIGBUeXBlZEFycmF5YCBpcyBjcmVhdGVkIGZyb20gYW4gb2Zmc2V0IGluIGFuXG4gIC8vIGBBcnJheUJ1ZmZlcmAuIGBub3JtbGlhemVkWHNgIGhvbGRzIGBUeXBlZEFycmF5YHMgd2hvc2UgYGJ1ZmZlcmBzIG1hdGNoXG4gIC8vIHRoZSBgVHlwZWRBcnJheWAgaW4gYnl0ZSBsZW5ndGguIElmIGFuIGVsZW1lbnQgb2YgYHhzYCBkb2VzIG5vdCBzaG93XG4gIC8vIHRoaXMgcHJvcGVydHksIGEgbmV3IGBUeXBlZEFycmF5YCB0aGF0IHNhdGlzZnkgdGhpcyBwcm9wZXJ0eSB3aWxsIGJlXG4gIC8vIGNvbnN0cnVjdGVkIGFuZCBwdXNoZWQgaW50byBgbm9ybWFsaXplZFhzYC5cbiAgY29uc3Qgbm9ybWFsaXplZFhzOiBUeXBlZEFycmF5W10gPSBbXTtcbiAgeHMuZm9yRWFjaCgoeDogVHlwZWRBcnJheSkgPT4ge1xuICAgIHRvdGFsQnl0ZUxlbmd0aCArPSB4LmJ5dGVMZW5ndGg7XG4gICAgLy8gdHNsaW50OmRpc2FibGU6bm8tYW55XG4gICAgbm9ybWFsaXplZFhzLnB1c2goXG4gICAgICAgIHguYnl0ZUxlbmd0aCA9PT0geC5idWZmZXIuYnl0ZUxlbmd0aCA/IHggOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcgKHguY29uc3RydWN0b3IgYXMgYW55KSh4KSk7XG4gICAgaWYgKCEoeCBhcyBhbnkgaW5zdGFuY2VvZiBGbG9hdDMyQXJyYXkgfHwgeCBhcyBhbnkgaW5zdGFuY2VvZiBJbnQzMkFycmF5IHx8XG4gICAgICAgICAgeCBhcyBhbnkgaW5zdGFuY2VvZiBVaW50OEFycmF5KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbnN1cHBvcnRlZCBUeXBlZEFycmF5IHN1YnR5cGU6ICR7eC5jb25zdHJ1Y3Rvci5uYW1lfWApO1xuICAgIH1cbiAgICAvLyB0c2xpbnQ6ZW5hYmxlOm5vLWFueVxuICB9KTtcblxuICBjb25zdCB5ID0gbmV3IFVpbnQ4QXJyYXkodG90YWxCeXRlTGVuZ3RoKTtcbiAgbGV0IG9mZnNldCA9IDA7XG4gIG5vcm1hbGl6ZWRYcy5mb3JFYWNoKCh4OiBUeXBlZEFycmF5KSA9PiB7XG4gICAgeS5zZXQobmV3IFVpbnQ4QXJyYXkoeC5idWZmZXIpLCBvZmZzZXQpO1xuICAgIG9mZnNldCArPSB4LmJ5dGVMZW5ndGg7XG4gIH0pO1xuXG4gIHJldHVybiB5LmJ1ZmZlcjtcbn1cblxuLy8gVXNlIEJ1ZmZlciBvbiBOb2RlLmpzIGluc3RlYWQgb2YgQmxvYi9hdG9iL2J0b2FcbmNvbnN0IHVzZU5vZGVCdWZmZXIgPSB0eXBlb2YgQnVmZmVyICE9PSAndW5kZWZpbmVkJyAmJlxuICAgICh0eXBlb2YgQmxvYiA9PT0gJ3VuZGVmaW5lZCcgfHwgdHlwZW9mIGF0b2IgPT09ICd1bmRlZmluZWQnIHx8XG4gICAgIHR5cGVvZiBidG9hID09PSAndW5kZWZpbmVkJyk7XG5cbi8qKlxuICogQ2FsY3VsYXRlIHRoZSBieXRlIGxlbmd0aCBvZiBhIEphdmFTY3JpcHQgc3RyaW5nLlxuICpcbiAqIE5vdGUgdGhhdCBhIEphdmFTY3JpcHQgc3RyaW5nIGNhbiBjb250YWluIHdpZGUgY2hhcmFjdGVycywgdGhlcmVmb3JlIHRoZVxuICogbGVuZ3RoIG9mIHRoZSBzdHJpbmcgaXMgbm90IG5lY2Vzc2FyaWx5IGVxdWFsIHRvIHRoZSBieXRlIGxlbmd0aC5cbiAqXG4gKiBAcGFyYW0gc3RyIElucHV0IHN0cmluZy5cbiAqIEByZXR1cm5zIEJ5dGUgbGVuZ3RoLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc3RyaW5nQnl0ZUxlbmd0aChzdHI6IHN0cmluZyk6IG51bWJlciB7XG4gIGlmICh1c2VOb2RlQnVmZmVyKSB7XG4gICAgcmV0dXJuIEJ1ZmZlci5ieXRlTGVuZ3RoKHN0ciwgJ3V0ZjgnKTtcbiAgfVxuICByZXR1cm4gbmV3IEJsb2IoW3N0cl0pLnNpemU7XG59XG5cbi8qKlxuICogRW5jb2RlIGFuIEFycmF5QnVmZmVyIGFzIGEgYmFzZTY0IGVuY29kZWQgc3RyaW5nLlxuICpcbiAqIEBwYXJhbSBidWZmZXIgYEFycmF5QnVmZmVyYCB0byBiZSBjb252ZXJ0ZWQuXG4gKiBAcmV0dXJucyBBIHN0cmluZyB0aGF0IGJhc2U2NC1lbmNvZGVzIGBidWZmZXJgLlxuICovXG5leHBvcnQgZnVuY3Rpb24gYXJyYXlCdWZmZXJUb0Jhc2U2NFN0cmluZyhidWZmZXI6IEFycmF5QnVmZmVyKTogc3RyaW5nIHtcbiAgaWYgKHVzZU5vZGVCdWZmZXIpIHtcbiAgICByZXR1cm4gQnVmZmVyLmZyb20oYnVmZmVyKS50b1N0cmluZygnYmFzZTY0Jyk7XG4gIH1cbiAgY29uc3QgYnVmID0gbmV3IFVpbnQ4QXJyYXkoYnVmZmVyKTtcbiAgbGV0IHMgPSAnJztcbiAgZm9yIChsZXQgaSA9IDAsIGwgPSBidWYubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgcyArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKGJ1ZltpXSk7XG4gIH1cbiAgcmV0dXJuIGJ0b2Eocyk7XG59XG5cbi8qKlxuICogRGVjb2RlIGEgYmFzZTY0IHN0cmluZyBhcyBhbiBBcnJheUJ1ZmZlci5cbiAqXG4gKiBAcGFyYW0gc3RyIEJhc2U2NCBzdHJpbmcuXG4gKiBAcmV0dXJucyBEZWNvZGVkIGBBcnJheUJ1ZmZlcmAuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBiYXNlNjRTdHJpbmdUb0FycmF5QnVmZmVyKHN0cjogc3RyaW5nKTogQXJyYXlCdWZmZXIge1xuICBpZiAodXNlTm9kZUJ1ZmZlcikge1xuICAgIGNvbnN0IGJ1ZiA9IEJ1ZmZlci5mcm9tKHN0ciwgJ2Jhc2U2NCcpO1xuICAgIHJldHVybiBidWYuYnVmZmVyLnNsaWNlKGJ1Zi5ieXRlT2Zmc2V0LCBidWYuYnl0ZU9mZnNldCArIGJ1Zi5ieXRlTGVuZ3RoKTtcbiAgfVxuICBjb25zdCBzID0gYXRvYihzdHIpO1xuICBjb25zdCBidWZmZXIgPSBuZXcgVWludDhBcnJheShzLmxlbmd0aCk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgcy5sZW5ndGg7ICsraSkge1xuICAgIGJ1ZmZlci5zZXQoW3MuY2hhckNvZGVBdChpKV0sIGkpO1xuICB9XG4gIHJldHVybiBidWZmZXIuYnVmZmVyO1xufVxuXG4vKipcbiAqIENvbmNhdGVuYXRlIGEgbnVtYmVyIG9mIEFycmF5QnVmZmVycyBpbnRvIG9uZS5cbiAqXG4gKiBAcGFyYW0gYnVmZmVycyBBbiBhcnJheSBvZiBBcnJheUJ1ZmZlcnMgdG8gY29uY2F0ZW5hdGUsIG9yIGEgc2luZ2xlXG4gKiAgICAgQXJyYXlCdWZmZXIuXG4gKiBAcmV0dXJucyBSZXN1bHQgb2YgY29uY2F0ZW5hdGluZyBgYnVmZmVyc2AgaW4gb3JkZXIuXG4gKlxuICogQGRlcHJlY2F0ZWQgVXNlIHRmLmlvLkNvbXBvc2l0ZUFycmF5QnVmZmVyLmpvaW4oKSBpbnN0ZWFkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY29uY2F0ZW5hdGVBcnJheUJ1ZmZlcnMoYnVmZmVyczogQXJyYXlCdWZmZXJbXVxuICAgICAgfCBBcnJheUJ1ZmZlcik6IEFycmF5QnVmZmVyIHtcbiAgcmV0dXJuIENvbXBvc2l0ZUFycmF5QnVmZmVyLmpvaW4oYnVmZmVycyk7XG59XG5cbi8qKlxuICogR2V0IHRoZSBiYXNlbmFtZSBvZiBhIHBhdGguXG4gKlxuICogQmVoYXZlcyBpbiBhIHdheSBhbmFsb2dvdXMgdG8gTGludXgncyBiYXNlbmFtZSBjb21tYW5kLlxuICpcbiAqIEBwYXJhbSBwYXRoXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBiYXNlbmFtZShwYXRoOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBTRVBBUkFUT1IgPSAnLyc7XG4gIHBhdGggPSBwYXRoLnRyaW0oKTtcbiAgd2hpbGUgKHBhdGguZW5kc1dpdGgoU0VQQVJBVE9SKSkge1xuICAgIHBhdGggPSBwYXRoLnNsaWNlKDAsIHBhdGgubGVuZ3RoIC0gMSk7XG4gIH1cbiAgY29uc3QgaXRlbXMgPSBwYXRoLnNwbGl0KFNFUEFSQVRPUik7XG4gIHJldHVybiBpdGVtc1tpdGVtcy5sZW5ndGggLSAxXTtcbn1cblxuLyoqXG4gKiBDcmVhdGUgYE1vZGVsSlNPTmAgZnJvbSBgTW9kZWxBcnRpZmFjdHNgLlxuICpcbiAqIEBwYXJhbSBhcnRpZmFjdHMgTW9kZWwgYXJ0aWZhY3RzLCBkZXNjcmliaW5nIHRoZSBtb2RlbCBhbmQgaXRzIHdlaWdodHMuXG4gKiBAcGFyYW0gbWFuaWZlc3QgV2VpZ2h0IG1hbmlmZXN0LCBkZXNjcmliaW5nIHdoZXJlIHRoZSB3ZWlnaHRzIG9mIHRoZVxuICogICAgIGBNb2RlbEFydGlmYWN0c2AgYXJlIHN0b3JlZCwgYW5kIHNvbWUgbWV0YWRhdGEgYWJvdXQgdGhlbS5cbiAqIEByZXR1cm5zIE9iamVjdCByZXByZXNlbnRpbmcgdGhlIGBtb2RlbC5qc29uYCBmaWxlIGRlc2NyaWJpbmcgdGhlIG1vZGVsXG4gKiAgICAgYXJ0aWZhY3RzIGFuZCB3ZWlnaHRzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRNb2RlbEpTT05Gb3JNb2RlbEFydGlmYWN0cyhcbiAgICBhcnRpZmFjdHM6IE1vZGVsQXJ0aWZhY3RzLCBtYW5pZmVzdDogV2VpZ2h0c01hbmlmZXN0Q29uZmlnKTogTW9kZWxKU09OIHtcbiAgY29uc3QgcmVzdWx0OiBNb2RlbEpTT04gPSB7XG4gICAgbW9kZWxUb3BvbG9neTogYXJ0aWZhY3RzLm1vZGVsVG9wb2xvZ3ksXG4gICAgZm9ybWF0OiBhcnRpZmFjdHMuZm9ybWF0LFxuICAgIGdlbmVyYXRlZEJ5OiBhcnRpZmFjdHMuZ2VuZXJhdGVkQnksXG4gICAgY29udmVydGVkQnk6IGFydGlmYWN0cy5jb252ZXJ0ZWRCeSxcbiAgICB3ZWlnaHRzTWFuaWZlc3Q6IG1hbmlmZXN0XG4gIH07XG4gIGlmIChhcnRpZmFjdHMuc2lnbmF0dXJlICE9IG51bGwpIHtcbiAgICByZXN1bHQuc2lnbmF0dXJlID0gYXJ0aWZhY3RzLnNpZ25hdHVyZTtcbiAgfVxuICBpZiAoYXJ0aWZhY3RzLnVzZXJEZWZpbmVkTWV0YWRhdGEgIT0gbnVsbCkge1xuICAgIHJlc3VsdC51c2VyRGVmaW5lZE1ldGFkYXRhID0gYXJ0aWZhY3RzLnVzZXJEZWZpbmVkTWV0YWRhdGE7XG4gIH1cbiAgaWYgKGFydGlmYWN0cy5tb2RlbEluaXRpYWxpemVyICE9IG51bGwpIHtcbiAgICByZXN1bHQubW9kZWxJbml0aWFsaXplciA9IGFydGlmYWN0cy5tb2RlbEluaXRpYWxpemVyO1xuICB9XG4gIGlmIChhcnRpZmFjdHMuaW5pdGlhbGl6ZXJTaWduYXR1cmUgIT0gbnVsbCkge1xuICAgIHJlc3VsdC5pbml0aWFsaXplclNpZ25hdHVyZSA9IGFydGlmYWN0cy5pbml0aWFsaXplclNpZ25hdHVyZTtcbiAgfVxuICBpZiAoYXJ0aWZhY3RzLnRyYWluaW5nQ29uZmlnICE9IG51bGwpIHtcbiAgICByZXN1bHQudHJhaW5pbmdDb25maWcgPSBhcnRpZmFjdHMudHJhaW5pbmdDb25maWc7XG4gIH1cbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxuLyoqXG4gKiBDcmVhdGUgYE1vZGVsQXJ0aWZhY3RzYCBmcm9tIGEgSlNPTiBmaWxlIGFuZCB3ZWlnaHRzLlxuICpcbiAqIEBwYXJhbSBtb2RlbEpTT04gT2JqZWN0IGNvbnRhaW5pbmcgdGhlIHBhcnNlZCBKU09OIG9mIGBtb2RlbC5qc29uYFxuICogQHBhcmFtIHdlaWdodFNwZWNzIFRoZSBsaXN0IG9mIFdlaWdodHNNYW5pZmVzdEVudHJ5IGZvciB0aGUgbW9kZWwuIE11c3QgYmVcbiAqICAgICBwYXNzZWQgaWYgdGhlIG1vZGVsSlNPTiBoYXMgYSB3ZWlnaHRzTWFuaWZlc3QuXG4gKiBAcGFyYW0gd2VpZ2h0RGF0YSBBbiBBcnJheUJ1ZmZlciBvciBhcnJheSBvZiBBcnJheUJ1ZmZlcnMgb2Ygd2VpZ2h0IGRhdGEgZm9yXG4gKiAgICAgdGhlIG1vZGVsIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHdlaWdodHMgaW4gd2VpZ2h0U3BlY3MuIE11c3QgYmUgcGFzc2VkIGlmXG4gKiAgICAgdGhlIG1vZGVsSlNPTiBoYXMgYSB3ZWlnaHRzTWFuaWZlc3QuXG4gKiBAcmV0dXJucyBBIFByb21pc2Ugb2YgdGhlIGBNb2RlbEFydGlmYWN0c2AsIGFzIGRlc2NyaWJlZCBieSB0aGUgSlNPTiBmaWxlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TW9kZWxBcnRpZmFjdHNGb3JKU09OU3luYyhcbiAgICBtb2RlbEpTT046IE1vZGVsSlNPTiwgd2VpZ2h0U3BlY3M/OiBXZWlnaHRzTWFuaWZlc3RFbnRyeVtdLFxuICAgIHdlaWdodERhdGE/OiBXZWlnaHREYXRhKTogTW9kZWxBcnRpZmFjdHMge1xuXG4gIGNvbnN0IG1vZGVsQXJ0aWZhY3RzOiBNb2RlbEFydGlmYWN0cyA9IHtcbiAgICBtb2RlbFRvcG9sb2d5OiBtb2RlbEpTT04ubW9kZWxUb3BvbG9neSxcbiAgICBmb3JtYXQ6IG1vZGVsSlNPTi5mb3JtYXQsXG4gICAgZ2VuZXJhdGVkQnk6IG1vZGVsSlNPTi5nZW5lcmF0ZWRCeSxcbiAgICBjb252ZXJ0ZWRCeTogbW9kZWxKU09OLmNvbnZlcnRlZEJ5XG4gIH07XG5cbiAgaWYgKG1vZGVsSlNPTi50cmFpbmluZ0NvbmZpZyAhPSBudWxsKSB7XG4gICAgbW9kZWxBcnRpZmFjdHMudHJhaW5pbmdDb25maWcgPSBtb2RlbEpTT04udHJhaW5pbmdDb25maWc7XG4gIH1cbiAgaWYgKG1vZGVsSlNPTi53ZWlnaHRzTWFuaWZlc3QgIT0gbnVsbCkge1xuICAgIGlmICghd2VpZ2h0U3BlY3MpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbW9kZWxKU09OIGhhcyB3ZWlnaHRzTWFuaWZlc3QgYnV0IHdlaWdodFNwZWNzIGlzIG51bGwnKTtcbiAgICB9XG4gICAgaWYgKCF3ZWlnaHREYXRhKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ21vZGVsSlNPTiBoYXMgd2VpZ2h0c01hbmlmZXN0IGJ1dCB3ZWlnaHREYXRhIGlzIG51bGwnKTtcbiAgICB9XG4gICAgbW9kZWxBcnRpZmFjdHMud2VpZ2h0U3BlY3MgPSB3ZWlnaHRTcGVjcztcbiAgICBtb2RlbEFydGlmYWN0cy53ZWlnaHREYXRhID0gd2VpZ2h0RGF0YTtcbiAgfVxuICBpZiAobW9kZWxKU09OLnNpZ25hdHVyZSAhPSBudWxsKSB7XG4gICAgbW9kZWxBcnRpZmFjdHMuc2lnbmF0dXJlID0gbW9kZWxKU09OLnNpZ25hdHVyZTtcbiAgfVxuICBpZiAobW9kZWxKU09OLnVzZXJEZWZpbmVkTWV0YWRhdGEgIT0gbnVsbCkge1xuICAgIG1vZGVsQXJ0aWZhY3RzLnVzZXJEZWZpbmVkTWV0YWRhdGEgPSBtb2RlbEpTT04udXNlckRlZmluZWRNZXRhZGF0YTtcbiAgfVxuICBpZiAobW9kZWxKU09OLm1vZGVsSW5pdGlhbGl6ZXIgIT0gbnVsbCkge1xuICAgIG1vZGVsQXJ0aWZhY3RzLm1vZGVsSW5pdGlhbGl6ZXIgPSBtb2RlbEpTT04ubW9kZWxJbml0aWFsaXplcjtcbiAgfVxuICBpZiAobW9kZWxKU09OLmluaXRpYWxpemVyU2lnbmF0dXJlICE9IG51bGwpIHtcbiAgICBtb2RlbEFydGlmYWN0cy5pbml0aWFsaXplclNpZ25hdHVyZSA9IG1vZGVsSlNPTi5pbml0aWFsaXplclNpZ25hdHVyZTtcbiAgfVxuXG4gIHJldHVybiBtb2RlbEFydGlmYWN0cztcbn1cblxuLyoqXG4gKiBDcmVhdGUgYE1vZGVsQXJ0aWZhY3RzYCBmcm9tIGEgSlNPTiBmaWxlLlxuICpcbiAqIEBwYXJhbSBtb2RlbEpTT04gT2JqZWN0IGNvbnRhaW5pbmcgdGhlIHBhcnNlZCBKU09OIG9mIGBtb2RlbC5qc29uYFxuICogQHBhcmFtIGxvYWRXZWlnaHRzIEZ1bmN0aW9uIHRoYXQgdGFrZXMgdGhlIEpTT04gZmlsZSdzIHdlaWdodHMgbWFuaWZlc3QsXG4gKiAgICAgcmVhZHMgd2VpZ2h0cyBmcm9tIHRoZSBsaXN0ZWQgcGF0aChzKSwgYW5kIHJldHVybnMgYSBQcm9taXNlIG9mIHRoZVxuICogICAgIHdlaWdodCBtYW5pZmVzdCBlbnRyaWVzIGFsb25nIHdpdGggdGhlIHdlaWdodHMgZGF0YS5cbiAqIEByZXR1cm5zIEEgUHJvbWlzZSBvZiB0aGUgYE1vZGVsQXJ0aWZhY3RzYCwgYXMgZGVzY3JpYmVkIGJ5IHRoZSBKU09OIGZpbGUuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRNb2RlbEFydGlmYWN0c0ZvckpTT04oXG4gICAgbW9kZWxKU09OOiBNb2RlbEpTT04sXG4gICAgbG9hZFdlaWdodHM6ICh3ZWlnaHRzTWFuaWZlc3Q6IFdlaWdodHNNYW5pZmVzdENvbmZpZykgPT4gUHJvbWlzZTxbXG4gICAgICAvKiB3ZWlnaHRTcGVjcyAqLyBXZWlnaHRzTWFuaWZlc3RFbnRyeVtdLCBXZWlnaHREYXRhLFxuICAgIF0+KTogUHJvbWlzZTxNb2RlbEFydGlmYWN0cz4ge1xuICBsZXQgd2VpZ2h0U3BlY3M6IFdlaWdodHNNYW5pZmVzdEVudHJ5W10gfCB1bmRlZmluZWQ7XG4gIGxldCB3ZWlnaHREYXRhOiBXZWlnaHREYXRhIHwgdW5kZWZpbmVkO1xuXG4gIGlmIChtb2RlbEpTT04ud2VpZ2h0c01hbmlmZXN0ICE9IG51bGwpIHtcbiAgICBbd2VpZ2h0U3BlY3MsIHdlaWdodERhdGFdID0gYXdhaXQgbG9hZFdlaWdodHMobW9kZWxKU09OLndlaWdodHNNYW5pZmVzdCk7XG4gIH1cblxuICByZXR1cm4gZ2V0TW9kZWxBcnRpZmFjdHNGb3JKU09OU3luYyhtb2RlbEpTT04sIHdlaWdodFNwZWNzLCB3ZWlnaHREYXRhKTtcbn1cblxuLyoqXG4gKiBQb3B1bGF0ZSBNb2RlbEFydGlmYWN0c0luZm8gZmllbGRzIGZvciBhIG1vZGVsIHdpdGggSlNPTiB0b3BvbG9neS5cbiAqIEBwYXJhbSBtb2RlbEFydGlmYWN0c1xuICogQHJldHVybnMgQSBNb2RlbEFydGlmYWN0c0luZm8gb2JqZWN0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TW9kZWxBcnRpZmFjdHNJbmZvRm9ySlNPTihtb2RlbEFydGlmYWN0czogTW9kZWxBcnRpZmFjdHMpOlxuICAgIE1vZGVsQXJ0aWZhY3RzSW5mbyB7XG4gIGlmIChtb2RlbEFydGlmYWN0cy5tb2RlbFRvcG9sb2d5IGluc3RhbmNlb2YgQXJyYXlCdWZmZXIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0V4cGVjdGVkIEpTT04gbW9kZWwgdG9wb2xvZ3ksIHJlY2VpdmVkIEFycmF5QnVmZmVyLicpO1xuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBkYXRlU2F2ZWQ6IG5ldyBEYXRlKCksXG4gICAgbW9kZWxUb3BvbG9neVR5cGU6ICdKU09OJyxcbiAgICBtb2RlbFRvcG9sb2d5Qnl0ZXM6IG1vZGVsQXJ0aWZhY3RzLm1vZGVsVG9wb2xvZ3kgPT0gbnVsbCA/XG4gICAgICAgIDAgOlxuICAgICAgICBzdHJpbmdCeXRlTGVuZ3RoKEpTT04uc3RyaW5naWZ5KG1vZGVsQXJ0aWZhY3RzLm1vZGVsVG9wb2xvZ3kpKSxcbiAgICB3ZWlnaHRTcGVjc0J5dGVzOiBtb2RlbEFydGlmYWN0cy53ZWlnaHRTcGVjcyA9PSBudWxsID9cbiAgICAgICAgMCA6XG4gICAgICAgIHN0cmluZ0J5dGVMZW5ndGgoSlNPTi5zdHJpbmdpZnkobW9kZWxBcnRpZmFjdHMud2VpZ2h0U3BlY3MpKSxcbiAgICB3ZWlnaHREYXRhQnl0ZXM6IG1vZGVsQXJ0aWZhY3RzLndlaWdodERhdGEgPT0gbnVsbCA/XG4gICAgICAgIDAgOlxuICAgICAgICBuZXcgQ29tcG9zaXRlQXJyYXlCdWZmZXIobW9kZWxBcnRpZmFjdHMud2VpZ2h0RGF0YSkuYnl0ZUxlbmd0aCxcbiAgfTtcbn1cblxuLyoqXG4gKiBDb25jYXRlbmF0ZSB0aGUgd2VpZ2h0cyBzdG9yZWQgaW4gYSBXZWlnaHRzTWFuaWZlc3RDb25maWcgaW50byBhIGxpc3Qgb2ZcbiAqIFdlaWdodHNNYW5pZmVzdEVudHJ5XG4gKlxuICogQHBhcmFtIHdlaWdodHNNYW5pZmVzdCBUaGUgV2VpZ2h0c01hbmlmZXN0Q29uZmlnIHRvIGV4dHJhY3Qgd2VpZ2h0cyBmcm9tLlxuICogQHJldHVybnMgQSBsaXN0IG9mIFdlaWdodHNNYW5pZmVzdEVudHJ5IG9mIHRoZSB3ZWlnaHRzIGluIHRoZSB3ZWlnaHRzTWFuaWZlc3RcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFdlaWdodFNwZWNzKHdlaWdodHNNYW5pZmVzdDogV2VpZ2h0c01hbmlmZXN0Q29uZmlnKTpcbiAgICBXZWlnaHRzTWFuaWZlc3RFbnRyeVtdIHtcbiAgY29uc3Qgd2VpZ2h0U3BlY3M6IFdlaWdodHNNYW5pZmVzdEVudHJ5W10gPSBbXTtcbiAgZm9yIChjb25zdCBlbnRyeSBvZiB3ZWlnaHRzTWFuaWZlc3QpIHtcbiAgICB3ZWlnaHRTcGVjcy5wdXNoKC4uLmVudHJ5LndlaWdodHMpO1xuICB9XG4gIHJldHVybiB3ZWlnaHRTcGVjcztcbn1cblxuLyoqXG4gKiBDb21wdXRlcyBtYW50aXNhIHRhYmxlIGZvciBjYXN0aW5nIEZsb2F0MTYgdG8gRmxvYXQzMlxuICogU2VlIGh0dHA6Ly93d3cuZm94LXRvb2xraXQub3JnL2Z0cC9mYXN0aGFsZmZsb2F0Y29udmVyc2lvbi5wZGZcbiAqXG4gKiBAcmV0dXJucyBVaW50MzJBcnJheSwgMjA0OCBtYW50aXNzYSBsb29rdXAgdmFsdWVzLlxuICovXG5mdW5jdGlvbiBjb21wdXRlRmxvYXQxNk1hbnRpc2FUYWJsZSgpOiBVaW50MzJBcnJheSB7XG4gIGNvbnN0IGNvbnZlcnRNYW50aXNzYSA9IChpOiBudW1iZXIpOiBudW1iZXIgPT4ge1xuICAgIGxldCBtID0gaSA8PCAxMztcbiAgICBsZXQgZSA9IDA7XG5cbiAgICB3aGlsZSAoKG0gJiAweDAwODAwMDAwKSA9PT0gMCkge1xuICAgICAgZSAtPSAweDAwODAwMDAwO1xuICAgICAgbSA8PD0gMTtcbiAgICB9XG4gICAgbSAmPSB+MHgwMDgwMDAwMDtcbiAgICBlICs9IDB4Mzg4MDAwMDA7XG5cbiAgICByZXR1cm4gbSB8IGU7XG4gIH07XG5cbiAgY29uc3QgbWFudGlzYVRhYmxlID0gbmV3IFVpbnQzMkFycmF5KDIwNDgpO1xuXG4gIG1hbnRpc2FUYWJsZVswXSA9IDA7XG4gIGZvciAobGV0IGkgPSAxOyBpIDwgMTAyNDsgaSsrKSB7XG4gICAgbWFudGlzYVRhYmxlW2ldID0gY29udmVydE1hbnRpc3NhKGkpO1xuICB9XG4gIGZvciAobGV0IGkgPSAxMDI0OyBpIDwgMjA0ODsgaSsrKSB7XG4gICAgbWFudGlzYVRhYmxlW2ldID0gMHgzODAwMDAwMCArICgoaSAtIDEwMjQpIDw8IDEzKTtcbiAgfVxuXG4gIHJldHVybiBtYW50aXNhVGFibGU7XG59XG5cbi8qKlxuICogQ29tcHV0ZXMgZXhwb25lbnQgdGFibGUgZm9yIGNhc3RpbmcgRmxvYXQxNiB0byBGbG9hdDMyXG4gKiBTZWUgaHR0cDovL3d3dy5mb3gtdG9vbGtpdC5vcmcvZnRwL2Zhc3RoYWxmZmxvYXRjb252ZXJzaW9uLnBkZlxuICpcbiAqIEByZXR1cm5zIFVpbnQzMkFycmF5LCA2NCBleHBvbmVudCBsb29rdXAgdmFsdWVzLlxuICovXG5mdW5jdGlvbiBjb21wdXRlRmxvYXQxNkV4cG9uZW50VGFibGUoKTogVWludDMyQXJyYXkge1xuICBjb25zdCBleHBvbmVudFRhYmxlID0gbmV3IFVpbnQzMkFycmF5KDY0KTtcblxuICBleHBvbmVudFRhYmxlWzBdID0gMDtcbiAgZXhwb25lbnRUYWJsZVszMV0gPSAweDQ3ODAwMDAwO1xuICBleHBvbmVudFRhYmxlWzMyXSA9IDB4ODAwMDAwMDA7XG4gIGV4cG9uZW50VGFibGVbNjNdID0gMHhjNzgwMDAwMDtcbiAgZm9yIChsZXQgaSA9IDE7IGkgPCAzMTsgaSsrKSB7XG4gICAgZXhwb25lbnRUYWJsZVtpXSA9IGkgPDwgMjM7XG4gIH1cbiAgZm9yIChsZXQgaSA9IDMzOyBpIDwgNjM7IGkrKykge1xuICAgIGV4cG9uZW50VGFibGVbaV0gPSAweDgwMDAwMDAwICsgKChpIC0gMzIpIDw8IDIzKTtcbiAgfVxuXG4gIHJldHVybiBleHBvbmVudFRhYmxlO1xufVxuXG4vKipcbiAqIENvbXB1dGVzIG9mZnNldCB0YWJsZSBmb3IgY2FzdGluZyBGbG9hdDE2IHRvIEZsb2F0MzJcbiAqIFNlZSBodHRwOi8vd3d3LmZveC10b29sa2l0Lm9yZy9mdHAvZmFzdGhhbGZmbG9hdGNvbnZlcnNpb24ucGRmXG4gKlxuICogQHJldHVybnMgVWludDMyQXJyYXksIDZkIG9mZnNldCB2YWx1ZXMuXG4gKi9cbmZ1bmN0aW9uIGNvbXB1dGVGbG9hdDE2T2Zmc2V0VGFibGUoKTogVWludDMyQXJyYXkge1xuICBjb25zdCBvZmZzZXRUYWJsZSA9IG5ldyBVaW50MzJBcnJheSg2NCk7XG5cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCA2NDsgaSsrKSB7XG4gICAgb2Zmc2V0VGFibGVbaV0gPSAxMDI0O1xuICB9XG4gIG9mZnNldFRhYmxlWzBdID0gb2Zmc2V0VGFibGVbMzJdID0gMDtcblxuICByZXR1cm4gb2Zmc2V0VGFibGU7XG59XG5cbi8qKlxuICogUmV0cmlldmUgYSBGbG9hdDE2IGRlY29kZXIgd2hpY2ggd2lsbCBkZWNvZGUgYSBCeXRlQXJyYXkgb2YgRmxvYXQxNiB2YWx1ZXNcbiAqIHRvIGEgRmxvYXQzMkFycmF5LlxuICpcbiAqIEByZXR1cm5zIEZ1bmN0aW9uIChidWZmZXI6IFVpbnQxNkFycmF5KSA9PiBGbG9hdDMyQXJyYXkgd2hpY2ggZGVjb2Rlc1xuICogICAgICAgICAgdGhlIFVpbnQxNkFycmF5IG9mIEZsb2F0MTYgYnl0ZXMgdG8gYSBGbG9hdDMyQXJyYXkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRGbG9hdDE2RGVjb2RlcigpOiAoYnVmZmVyOiBVaW50MTZBcnJheSkgPT4gRmxvYXQzMkFycmF5IHtcbiAgLy8gQWxnb3JpdGhtIGlzIGJhc2VkIG9mZiBvZlxuICAvLyBodHRwOi8vd3d3LmZveC10b29sa2l0Lm9yZy9mdHAvZmFzdGhhbGZmbG9hdGNvbnZlcnNpb24ucGRmXG5cbiAgLy8gQ2FjaGUgbG9va3VwIHRhYmxlc1xuICBjb25zdCBtYW50aXNhVGFibGUgPSBjb21wdXRlRmxvYXQxNk1hbnRpc2FUYWJsZSgpO1xuICBjb25zdCBleHBvbmVudFRhYmxlID0gY29tcHV0ZUZsb2F0MTZFeHBvbmVudFRhYmxlKCk7XG4gIGNvbnN0IG9mZnNldFRhYmxlID0gY29tcHV0ZUZsb2F0MTZPZmZzZXRUYWJsZSgpO1xuXG4gIHJldHVybiAocXVhbnRpemVkQXJyYXk6IFVpbnQxNkFycmF5KSA9PiB7XG4gICAgY29uc3QgYnVmZmVyID0gbmV3IEFycmF5QnVmZmVyKDQgKiBxdWFudGl6ZWRBcnJheS5sZW5ndGgpO1xuICAgIGNvbnN0IGJ1ZmZlclVpbnQzMlZpZXcgPSBuZXcgVWludDMyQXJyYXkoYnVmZmVyKTtcbiAgICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgcXVhbnRpemVkQXJyYXkubGVuZ3RoOyBpbmRleCsrKSB7XG4gICAgICBjb25zdCBmbG9hdDE2Qml0cyA9IHF1YW50aXplZEFycmF5W2luZGV4XTtcbiAgICAgIGNvbnN0IGZsb2F0MzJCaXRzID1cbiAgICAgICAgICBtYW50aXNhVGFibGVbb2Zmc2V0VGFibGVbZmxvYXQxNkJpdHMgPj4gMTBdICsgKGZsb2F0MTZCaXRzICYgMHgzZmYpXSArXG4gICAgICAgICAgZXhwb25lbnRUYWJsZVtmbG9hdDE2Qml0cyA+PiAxMF07XG4gICAgICBidWZmZXJVaW50MzJWaWV3W2luZGV4XSA9IGZsb2F0MzJCaXRzO1xuICAgIH1cbiAgICByZXR1cm4gbmV3IEZsb2F0MzJBcnJheShidWZmZXIpO1xuICB9O1xufVxuIl19
|