/** * @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. * ============================================================================= */ import { InputLayer } from './engine/input_layer'; import { Layer } from './engine/topology'; import { input } from './exports'; import { ELU, LeakyReLU, PReLU, ReLU, Softmax, ThresholdedReLU } from './layers/advanced_activations'; import { Conv1D, Conv2D, Conv2DTranspose, Conv3D, Cropping2D, SeparableConv2D, UpSampling2D, Conv3DTranspose } from './layers/convolutional'; import { DepthwiseConv2D } from './layers/convolutional_depthwise'; import { ConvLSTM2D, ConvLSTM2DCell } from './layers/convolutional_recurrent'; import { Activation, Dense, Dropout, Flatten, Masking, Permute, RepeatVector, Reshape, SpatialDropout1D } from './layers/core'; import { Embedding } from './layers/embeddings'; import { Add, Average, Concatenate, Dot, Maximum, Minimum, Multiply } from './layers/merge'; import { AlphaDropout, GaussianDropout, GaussianNoise } from './layers/noise'; import { BatchNormalization, LayerNormalization } from './layers/normalization'; import { ZeroPadding2D } from './layers/padding'; import { AveragePooling1D, AveragePooling2D, AveragePooling3D, GlobalAveragePooling1D, GlobalAveragePooling2D, GlobalMaxPooling1D, GlobalMaxPooling2D, MaxPooling1D, MaxPooling2D, MaxPooling3D } from './layers/pooling'; import { GRU, GRUCell, LSTM, LSTMCell, RNN, RNNCell, SimpleRNN, SimpleRNNCell, StackedRNNCells } from './layers/recurrent'; import { Bidirectional, TimeDistributed } from './layers/wrappers'; import { Rescaling } from './layers/preprocessing/image_preprocessing'; import { CenterCrop } from './layers/preprocessing/center_crop'; import { CategoryEncoding } from './layers/preprocessing/category_encoding'; import { Resizing } from './layers/preprocessing/image_resizing'; import { RandomWidth } from './layers/preprocessing/random_width'; // TODO(cais): Add doc string to all the public static functions in this // class; include exectuable JavaScript code snippets where applicable // (b/74074458). // Input Layer. /** * An input layer is an entry point into a `tf.LayersModel`. * * `InputLayer` is generated automatically for `tf.Sequential` models by * specifying the `inputshape` or `batchInputShape` for the first layer. It * should not be specified explicitly. However, it can be useful sometimes, * e.g., when constructing a sequential model from a subset of another * sequential model's layers. Like the code snippet below shows. * * ```js * // Define a model which simply adds two inputs. * const model1 = tf.sequential(); * model1.add(tf.layers.dense({inputShape: [4], units: 3, activation: 'relu'})); * model1.add(tf.layers.dense({units: 1, activation: 'sigmoid'})); * model1.summary(); * model1.predict(tf.zeros([1, 4])).print(); * * // Construct another model, reusing the second layer of `model1` while * // not using the first layer of `model1`. Note that you cannot add the second * // layer of `model` directly as the first layer of the new sequential model, * // because doing so will lead to an error related to the fact that the layer * // is not an input layer. Instead, you need to create an `inputLayer` and add * // it to the new sequential model before adding the reused layer. * const model2 = tf.sequential(); * // Use an inputShape that matches the input shape of `model1`'s second * // layer. * model2.add(tf.layers.inputLayer({inputShape: [3]})); * model2.add(model1.layers[1]); * model2.summary(); * model2.predict(tf.zeros([1, 3])).print(); * ``` * * @doc {heading: 'Layers', subheading: 'Inputs', namespace: 'layers'} */ export function inputLayer(args) { return new InputLayer(args); } // Advanced Activation Layers. /** * Exponential Linear Unit (ELU). * * It follows: * `f(x) = alpha * (exp(x) - 1.) for x < 0`, * `f(x) = x for x >= 0`. * * Input shape: * Arbitrary. Use the configuration `inputShape` when using this layer as the * first layer in a model. * * Output shape: * Same shape as the input. * * References: * - [Fast and Accurate Deep Network Learning by Exponential Linear Units * (ELUs)](https://arxiv.org/abs/1511.07289v1) * * @doc { * heading: 'Layers', * subheading: 'Advanced Activation', * namespace: 'layers' * } */ export function elu(args) { return new ELU(args); } /** * Rectified Linear Unit activation function. * * Input shape: * Arbitrary. Use the config field `inputShape` (Array of integers, does * not include the sample axis) when using this layer as the first layer * in a model. * * Output shape: * Same shape as the input. * * @doc { * heading: 'Layers', * subheading: 'Advanced Activation', * namespace: 'layers' * } */ export function reLU(args) { return new ReLU(args); } /** * Leaky version of a rectified linear unit. * * It allows a small gradient when the unit is not active: * `f(x) = alpha * x for x < 0.` * `f(x) = x for x >= 0.` * * Input shape: * Arbitrary. Use the configuration `inputShape` when using this layer as the * first layer in a model. * * Output shape: * Same shape as the input. * * @doc { * heading: 'Layers', * subheading: 'Advanced Activation', * namespace: 'layers' * } */ export function leakyReLU(args) { return new LeakyReLU(args); } /** * Parameterized version of a leaky rectified linear unit. * * It follows * `f(x) = alpha * x for x < 0.` * `f(x) = x for x >= 0.` * wherein `alpha` is a trainable weight. * * Input shape: * Arbitrary. Use the configuration `inputShape` when using this layer as the * first layer in a model. * * Output shape: * Same shape as the input. * * @doc { * heading: 'Layers', * subheading: 'Advanced Activation', * namespace: 'layers' * } */ export function prelu(args) { return new PReLU(args); } /** * Softmax activation layer. * * Input shape: * Arbitrary. Use the configuration `inputShape` when using this layer as the * first layer in a model. * * Output shape: * Same shape as the input. * * @doc { * heading: 'Layers', * subheading: 'Advanced Activation', * namespace: 'layers' * } */ export function softmax(args) { return new Softmax(args); } /** * Thresholded Rectified Linear Unit. * * It follows: * `f(x) = x for x > theta`, * `f(x) = 0 otherwise`. * * Input shape: * Arbitrary. Use the configuration `inputShape` when using this layer as the * first layer in a model. * * Output shape: * Same shape as the input. * * References: * - [Zero-Bias Autoencoders and the Benefits of Co-Adapting * Features](http://arxiv.org/abs/1402.3337) * * @doc { * heading: 'Layers', * subheading: 'Advanced Activation', * namespace: 'layers' * } */ export function thresholdedReLU(args) { return new ThresholdedReLU(args); } // Convolutional Layers. /** * 1D convolution layer (e.g., temporal convolution). * * This layer creates a convolution kernel that is convolved * with the layer input over a single spatial (or temporal) dimension * to produce a tensor of outputs. * * If `use_bias` is True, a bias vector is created and added to the outputs. * * If `activation` is not `null`, it is applied to the outputs as well. * * When using this layer as the first layer in a model, provide an * `inputShape` argument `Array` or `null`. * * For example, `inputShape` would be: * - `[10, 128]` for sequences of 10 vectors of 128-dimensional vectors * - `[null, 128]` for variable-length sequences of 128-dimensional vectors. * * @doc {heading: 'Layers', subheading: 'Convolutional', namespace: 'layers'} */ export function conv1d(args) { return new Conv1D(args); } /** * 2D convolution layer (e.g. spatial convolution over images). * * This layer creates a convolution kernel that is convolved * with the layer input to produce a tensor of outputs. * * If `useBias` is True, a bias vector is created and added to the outputs. * * If `activation` is not `null`, it is applied to the outputs as well. * * When using this layer as the first layer in a model, * provide the keyword argument `inputShape` * (Array of integers, does not include the sample axis), * e.g. `inputShape=[128, 128, 3]` for 128x128 RGB pictures * in `dataFormat='channelsLast'`. * * @doc {heading: 'Layers', subheading: 'Convolutional', namespace: 'layers'} */ export function conv2d(args) { return new Conv2D(args); } /** * Transposed convolutional layer (sometimes called Deconvolution). * * The need for transposed convolutions generally arises * from the desire to use a transformation going in the opposite direction of * a normal convolution, i.e., from something that has the shape of the output * of some convolution to something that has the shape of its input while * maintaining a connectivity pattern that is compatible with said * convolution. * * When using this layer as the first layer in a model, provide the * configuration `inputShape` (`Array` of integers, does not include the * sample axis), e.g., `inputShape: [128, 128, 3]` for 128x128 RGB pictures in * `dataFormat: 'channelsLast'`. * * Input shape: * 4D tensor with shape: * `[batch, channels, rows, cols]` if `dataFormat` is `'channelsFirst'`. * or 4D tensor with shape * `[batch, rows, cols, channels]` if `dataFormat` is `'channelsLast'`. * * Output shape: * 4D tensor with shape: * `[batch, filters, newRows, newCols]` if `dataFormat` is * `'channelsFirst'`. or 4D tensor with shape: * `[batch, newRows, newCols, filters]` if `dataFormat` is `'channelsLast'`. * * References: * - [A guide to convolution arithmetic for deep * learning](https://arxiv.org/abs/1603.07285v1) * - [Deconvolutional * Networks](http://www.matthewzeiler.com/pubs/cvpr2010/cvpr2010.pdf) * * @doc {heading: 'Layers', subheading: 'Convolutional', namespace: 'layers'} */ export function conv2dTranspose(args) { return new Conv2DTranspose(args); } /** * 3D convolution layer (e.g. spatial convolution over volumes). * * This layer creates a convolution kernel that is convolved * with the layer input to produce a tensor of outputs. * * If `useBias` is True, a bias vector is created and added to the outputs. * * If `activation` is not `null`, it is applied to the outputs as well. * * When using this layer as the first layer in a model, * provide the keyword argument `inputShape` * (Array of integers, does not include the sample axis), * e.g. `inputShape=[128, 128, 128, 1]` for 128x128x128 grayscale volumes * in `dataFormat='channelsLast'`. * * @doc {heading: 'Layers', subheading: 'Convolutional', namespace: 'layers'} */ export function conv3d(args) { return new Conv3D(args); } export function conv3dTranspose(args) { return new Conv3DTranspose(args); } /** * Depthwise separable 2D convolution. * * Separable convolution consists of first performing * a depthwise spatial convolution * (which acts on each input channel separately) * followed by a pointwise convolution which mixes together the resulting * output channels. The `depthMultiplier` argument controls how many * output channels are generated per input channel in the depthwise step. * * Intuitively, separable convolutions can be understood as * a way to factorize a convolution kernel into two smaller kernels, * or as an extreme version of an Inception block. * * Input shape: * 4D tensor with shape: * `[batch, channels, rows, cols]` if data_format='channelsFirst' * or 4D tensor with shape: * `[batch, rows, cols, channels]` if data_format='channelsLast'. * * Output shape: * 4D tensor with shape: * `[batch, filters, newRows, newCols]` if data_format='channelsFirst' * or 4D tensor with shape: * `[batch, newRows, newCols, filters]` if data_format='channelsLast'. * `rows` and `cols` values might have changed due to padding. * * @doc {heading: 'Layers', subheading: 'Convolutional', namespace: 'layers'} */ export function separableConv2d(args) { return new SeparableConv2D(args); } /** * Cropping layer for 2D input (e.g., image). * * This layer can crop an input * at the top, bottom, left and right side of an image tensor. * * Input shape: * 4D tensor with shape: * - If `dataFormat` is `"channelsLast"`: * `[batch, rows, cols, channels]` * - If `data_format` is `"channels_first"`: * `[batch, channels, rows, cols]`. * * Output shape: * 4D with shape: * - If `dataFormat` is `"channelsLast"`: * `[batch, croppedRows, croppedCols, channels]` * - If `dataFormat` is `"channelsFirst"`: * `[batch, channels, croppedRows, croppedCols]`. * * Examples * ```js * * const model = tf.sequential(); * model.add(tf.layers.cropping2D({cropping:[[2, 2], [2, 2]], * inputShape: [128, 128, 3]})); * //now output shape is [batch, 124, 124, 3] * ``` * * @doc {heading: 'Layers', subheading: 'Convolutional', namespace: 'layers'} */ export function cropping2D(args) { return new Cropping2D(args); } /** * Upsampling layer for 2D inputs. * * Repeats the rows and columns of the data * by size[0] and size[1] respectively. * * * Input shape: * 4D tensor with shape: * - If `dataFormat` is `"channelsLast"`: * `[batch, rows, cols, channels]` * - If `dataFormat` is `"channelsFirst"`: * `[batch, channels, rows, cols]` * * Output shape: * 4D tensor with shape: * - If `dataFormat` is `"channelsLast"`: * `[batch, upsampledRows, upsampledCols, channels]` * - If `dataFormat` is `"channelsFirst"`: * `[batch, channels, upsampledRows, upsampledCols]` * * * @doc {heading: 'Layers', subheading: 'Convolutional', namespace: 'layers'} */ export function upSampling2d(args) { return new UpSampling2D(args); } // Convolutional(depthwise) Layers. /** * Depthwise separable 2D convolution. * * Depthwise Separable convolutions consists in performing just the first step * in a depthwise spatial convolution (which acts on each input channel * separately). The `depthMultiplier` argument controls how many output channels * are generated per input channel in the depthwise step. * * @doc {heading: 'Layers', subheading: 'Convolutional', namespace: 'layers'} */ export function depthwiseConv2d(args) { return new DepthwiseConv2D(args); } // Basic Layers. /** * Applies an activation function to an output. * * This layer applies element-wise activation function. Other layers, notably * `dense` can also apply activation functions. Use this isolated activation * function to extract the values before and after the * activation. For instance: * * ```js * const input = tf.input({shape: [5]}); * const denseLayer = tf.layers.dense({units: 1}); * const activationLayer = tf.layers.activation({activation: 'relu6'}); * * // Obtain the output symbolic tensors by applying the layers in order. * const denseOutput = denseLayer.apply(input); * const activationOutput = activationLayer.apply(denseOutput); * * // Create the model based on the inputs. * const model = tf.model({ * inputs: input, * outputs: [denseOutput, activationOutput] * }); * * // Collect both outputs and print separately. * const [denseOut, activationOut] = model.predict(tf.randomNormal([6, 5])); * denseOut.print(); * activationOut.print(); * ``` * * @doc {heading: 'Layers', subheading: 'Basic', namespace: 'layers'} */ export function activation(args) { return new Activation(args); } /** * Creates a dense (fully connected) layer. * * This layer implements the operation: * `output = activation(dot(input, kernel) + bias)` * * `activation` is the element-wise activation function * passed as the `activation` argument. * * `kernel` is a weights matrix created by the layer. * * `bias` is a bias vector created by the layer (only applicable if `useBias` * is `true`). * * **Input shape:** * * nD `tf.Tensor` with shape: `(batchSize, ..., inputDim)`. * * The most common situation would be * a 2D input with shape `(batchSize, inputDim)`. * * **Output shape:** * * nD tensor with shape: `(batchSize, ..., units)`. * * For instance, for a 2D input with shape `(batchSize, inputDim)`, * the output would have shape `(batchSize, units)`. * * Note: if the input to the layer has a rank greater than 2, then it is * flattened prior to the initial dot product with the kernel. * * @doc {heading: 'Layers', subheading: 'Basic', namespace: 'layers'} */ export function dense(args) { return new Dense(args); } /** * Applies * [dropout](http://www.cs.toronto.edu/~rsalakhu/papers/srivastava14a.pdf) to * the input. * * Dropout consists in randomly setting a fraction `rate` of input units to 0 at * each update during training time, which helps prevent overfitting. * * @doc {heading: 'Layers', subheading: 'Basic', namespace: 'layers'} */ export function dropout(args) { return new Dropout(args); } /** * Spatial 1D version of Dropout. * * This Layer type performs the same function as the Dropout layer, but it drops * entire 1D feature maps instead of individual elements. For example, if an * input example consists of 3 timesteps and the feature map for each timestep * has a size of 4, a `spatialDropout1d` layer may zero out the feature maps * of the 1st timesteps and 2nd timesteps completely while sparing all feature * elements of the 3rd timestep. * * If adjacent frames (timesteps) are strongly correlated (as is normally the * case in early convolution layers), regular dropout will not regularize the * activation and will otherwise just result in merely an effective learning * rate decrease. In this case, `spatialDropout1d` will help promote * independence among feature maps and should be used instead. * * **Arguments:** * rate: A floating-point number >=0 and <=1. Fraction of the input elements * to drop. * * **Input shape:** * 3D tensor with shape `(samples, timesteps, channels)`. * * **Output shape:** * Same as the input shape. * * References: * - [Efficient Object Localization Using Convolutional * Networks](https://arxiv.org/abs/1411.4280) * * @doc {heading: 'Layers', subheading: 'Basic', namespace: 'layers'} */ export function spatialDropout1d(args) { return new SpatialDropout1D(args); } /** * Flattens the input. Does not affect the batch size. * * A `Flatten` layer flattens each batch in its inputs to 1D (making the output * 2D). * * For example: * * ```js * const input = tf.input({shape: [4, 3]}); * const flattenLayer = tf.layers.flatten(); * // Inspect the inferred output shape of the flatten layer, which * // equals `[null, 12]`. The 2nd dimension is 4 * 3, i.e., the result of the * // flattening. (The 1st dimension is the undermined batch size.) * console.log(JSON.stringify(flattenLayer.apply(input).shape)); * ``` * * @doc {heading: 'Layers', subheading: 'Basic', namespace: 'layers'} */ export function flatten(args) { return new Flatten(args); } /** * Repeats the input n times in a new dimension. * * ```js * const model = tf.sequential(); * model.add(tf.layers.repeatVector({n: 4, inputShape: [2]})); * const x = tf.tensor2d([[10, 20]]); * // Use the model to do inference on a data point the model hasn't seen * model.predict(x).print(); * // output shape is now [batch, 2, 4] * ``` * * @doc {heading: 'Layers', subheading: 'Basic', namespace: 'layers'} */ export function repeatVector(args) { return new RepeatVector(args); } /** * Reshapes an input to a certain shape. * * ```js * const input = tf.input({shape: [4, 3]}); * const reshapeLayer = tf.layers.reshape({targetShape: [2, 6]}); * // Inspect the inferred output shape of the Reshape layer, which * // equals `[null, 2, 6]`. (The 1st dimension is the undermined batch size.) * console.log(JSON.stringify(reshapeLayer.apply(input).shape)); * ``` * * Input shape: * Arbitrary, although all dimensions in the input shape must be fixed. * Use the configuration `inputShape` when using this layer as the * first layer in a model. * * * Output shape: * [batchSize, targetShape[0], targetShape[1], ..., * targetShape[targetShape.length - 1]]. * * @doc {heading: 'Layers', subheading: 'Basic', namespace: 'layers'} */ export function reshape(args) { return new Reshape(args); } /** * Permutes the dimensions of the input according to a given pattern. * * Useful for, e.g., connecting RNNs and convnets together. * * Example: * * ```js * const model = tf.sequential(); * model.add(tf.layers.permute({ * dims: [2, 1], * inputShape: [10, 64] * })); * console.log(model.outputShape); * // Now model's output shape is [null, 64, 10], where null is the * // unpermuted sample (batch) dimension. * ``` * * Input shape: * Arbitrary. Use the configuration field `inputShape` when using this * layer as the first layer in a model. * * Output shape: * Same rank as the input shape, but with the dimensions re-ordered (i.e., * permuted) according to the `dims` configuration of this layer. * * @doc {heading: 'Layers', subheading: 'Basic', namespace: 'layers'} */ export function permute(args) { return new Permute(args); } /** * Maps positive integers (indices) into dense vectors of fixed size. * E.g. [[4], [20]] -> [[0.25, 0.1], [0.6, -0.2]] * * **Input shape:** 2D tensor with shape: `[batchSize, sequenceLength]`. * * **Output shape:** 3D tensor with shape: `[batchSize, sequenceLength, * outputDim]`. * * @doc {heading: 'Layers', subheading: 'Basic', namespace: 'layers'} */ export function embedding(args) { return new Embedding(args); } // Merge Layers. /** * Layer that performs element-wise addition on an `Array` of inputs. * * It takes as input a list of tensors, all of the same shape, and returns a * single tensor (also of the same shape). The inputs are specified as an * `Array` when the `apply` method of the `Add` layer instance is called. For * example: * * ```js * const input1 = tf.input({shape: [2, 2]}); * const input2 = tf.input({shape: [2, 2]}); * const addLayer = tf.layers.add(); * const sum = addLayer.apply([input1, input2]); * console.log(JSON.stringify(sum.shape)); * // You get [null, 2, 2], with the first dimension as the undetermined batch * // dimension. * ``` * * @doc {heading: 'Layers', subheading: 'Merge', namespace: 'layers'} */ export function add(args) { return new Add(args); } /** * Layer that performs element-wise averaging on an `Array` of inputs. * * It takes as input a list of tensors, all of the same shape, and returns a * single tensor (also of the same shape). For example: * * ```js * const input1 = tf.input({shape: [2, 2]}); * const input2 = tf.input({shape: [2, 2]}); * const averageLayer = tf.layers.average(); * const average = averageLayer.apply([input1, input2]); * console.log(JSON.stringify(average.shape)); * // You get [null, 2, 2], with the first dimension as the undetermined batch * // dimension. * ``` * * @doc {heading: 'Layers', subheading: 'Merge', namespace: 'layers'} */ export function average(args) { return new Average(args); } /** * Layer that concatenates an `Array` of inputs. * * It takes a list of tensors, all of the same shape except for the * concatenation axis, and returns a single tensor, the concatenation * of all inputs. For example: * * ```js * const input1 = tf.input({shape: [2, 2]}); * const input2 = tf.input({shape: [2, 3]}); * const concatLayer = tf.layers.concatenate(); * const output = concatLayer.apply([input1, input2]); * console.log(JSON.stringify(output.shape)); * // You get [null, 2, 5], with the first dimension as the undetermined batch * // dimension. The last dimension (5) is the result of concatenating the * // last dimensions of the inputs (2 and 3). * ``` * * @doc {heading: 'Layers', subheading: 'Merge', namespace: 'layers'} */ export function concatenate(args) { return new Concatenate(args); } /** * Layer that computes the element-wise maximum of an `Array` of inputs. * * It takes as input a list of tensors, all of the same shape, and returns a * single tensor (also of the same shape). For example: * * ```js * const input1 = tf.input({shape: [2, 2]}); * const input2 = tf.input({shape: [2, 2]}); * const maxLayer = tf.layers.maximum(); * const max = maxLayer.apply([input1, input2]); * console.log(JSON.stringify(max.shape)); * // You get [null, 2, 2], with the first dimension as the undetermined batch * // dimension. * ``` * * @doc {heading: 'Layers', subheading: 'Merge', namespace: 'layers'} */ export function maximum(args) { return new Maximum(args); } /** * Layer that computes the element-wise minimum of an `Array` of inputs. * * It takes as input a list of tensors, all of the same shape, and returns a * single tensor (also of the same shape). For example: * * ```js * const input1 = tf.input({shape: [2, 2]}); * const input2 = tf.input({shape: [2, 2]}); * const minLayer = tf.layers.minimum(); * const min = minLayer.apply([input1, input2]); * console.log(JSON.stringify(min.shape)); * // You get [null, 2, 2], with the first dimension as the undetermined batch * // dimension. * ``` * * @doc {heading: 'Layers', subheading: 'Merge', namespace: 'layers'} */ export function minimum(args) { return new Minimum(args); } /** * Layer that multiplies (element-wise) an `Array` of inputs. * * It takes as input an Array of tensors, all of the same * shape, and returns a single tensor (also of the same shape). * For example: * * ```js * const input1 = tf.input({shape: [2, 2]}); * const input2 = tf.input({shape: [2, 2]}); * const input3 = tf.input({shape: [2, 2]}); * const multiplyLayer = tf.layers.multiply(); * const product = multiplyLayer.apply([input1, input2, input3]); * console.log(product.shape); * // You get [null, 2, 2], with the first dimension as the undetermined batch * // dimension. * * @doc {heading: 'Layers', subheading: 'Merge', namespace: 'layers'} */ export function multiply(args) { return new Multiply(args); } /** * Layer that computes a dot product between samples in two tensors. * * E.g., if applied to a list of two tensors `a` and `b` both of shape * `[batchSize, n]`, the output will be a tensor of shape `[batchSize, 1]`, * where each entry at index `[i, 0]` will be the dot product between * `a[i, :]` and `b[i, :]`. * * Example: * * ```js * const dotLayer = tf.layers.dot({axes: -1}); * const x1 = tf.tensor2d([[10, 20], [30, 40]]); * const x2 = tf.tensor2d([[-1, -2], [-3, -4]]); * * // Invoke the layer's apply() method in eager (imperative) mode. * const y = dotLayer.apply([x1, x2]); * y.print(); * ``` * * @doc {heading: 'Layers', subheading: 'Merge', namespace: 'layers'} */ export function dot(args) { return new Dot(args); } // Normalization Layers. /** * Batch normalization layer (Ioffe and Szegedy, 2014). * * Normalize the activations of the previous layer at each batch, * i.e. applies a transformation that maintains the mean activation * close to 0 and the activation standard deviation close to 1. * * Input shape: * Arbitrary. Use the keyword argument `inputShape` (Array of integers, does * not include the sample axis) when calling the constructor of this class, * if this layer is used as a first layer in a model. * * Output shape: * Same shape as input. * * References: * - [Batch Normalization: Accelerating Deep Network Training by Reducing * Internal Covariate Shift](https://arxiv.org/abs/1502.03167) * * @doc {heading: 'Layers', subheading: 'Normalization', namespace: 'layers'} */ export function batchNormalization(args) { return new BatchNormalization(args); } /** * Layer-normalization layer (Ba et al., 2016). * * Normalizes the activations of the previous layer for each given example in a * batch independently, instead of across a batch like in `batchNormalization`. * In other words, this layer applies a transformation that maintains the mean * activation within each example close to 0 and activation variance close to 1. * * Input shape: * Arbitrary. Use the argument `inputShape` when using this layer as the first * layer in a model. * * Output shape: * Same as input. * * References: * - [Layer Normalization](https://arxiv.org/abs/1607.06450) * * @doc {heading: 'Layers', subheading: 'Normalization', namespace: 'layers'} */ export function layerNormalization(args) { return new LayerNormalization(args); } // Padding Layers. /** * Zero-padding layer for 2D input (e.g., image). * * This layer can add rows and columns of zeros * at the top, bottom, left and right side of an image tensor. * * Input shape: * 4D tensor with shape: * - If `dataFormat` is `"channelsLast"`: * `[batch, rows, cols, channels]` * - If `data_format` is `"channels_first"`: * `[batch, channels, rows, cols]`. * * Output shape: * 4D with shape: * - If `dataFormat` is `"channelsLast"`: * `[batch, paddedRows, paddedCols, channels]` * - If `dataFormat` is `"channelsFirst"`: * `[batch, channels, paddedRows, paddedCols]`. * * @doc {heading: 'Layers', subheading: 'Padding', namespace: 'layers'} */ export function zeroPadding2d(args) { return new ZeroPadding2D(args); } // Pooling Layers. /** * Average pooling operation for spatial data. * * Input shape: `[batchSize, inLength, channels]` * * Output shape: `[batchSize, pooledLength, channels]` * * `tf.avgPool1d` is an alias. * * @doc {heading: 'Layers', subheading: 'Pooling', namespace: 'layers'} */ export function averagePooling1d(args) { return new AveragePooling1D(args); } export function avgPool1d(args) { return averagePooling1d(args); } // For backwards compatibility. // See https://github.com/tensorflow/tfjs/issues/152 export function avgPooling1d(args) { return averagePooling1d(args); } /** * Average pooling operation for spatial data. * * Input shape: * - If `dataFormat === CHANNEL_LAST`: * 4D tensor with shape: * `[batchSize, rows, cols, channels]` * - If `dataFormat === CHANNEL_FIRST`: * 4D tensor with shape: * `[batchSize, channels, rows, cols]` * * Output shape * - If `dataFormat === CHANNEL_LAST`: * 4D tensor with shape: * `[batchSize, pooledRows, pooledCols, channels]` * - If `dataFormat === CHANNEL_FIRST`: * 4D tensor with shape: * `[batchSize, channels, pooledRows, pooledCols]` * * `tf.avgPool2d` is an alias. * * @doc {heading: 'Layers', subheading: 'Pooling', namespace: 'layers'} */ export function averagePooling2d(args) { return new AveragePooling2D(args); } export function avgPool2d(args) { return averagePooling2d(args); } // For backwards compatibility. // See https://github.com/tensorflow/tfjs/issues/152 export function avgPooling2d(args) { return averagePooling2d(args); } /** * Average pooling operation for 3D data. * * Input shape * - If `dataFormat === channelsLast`: * 5D tensor with shape: * `[batchSize, depths, rows, cols, channels]` * - If `dataFormat === channelsFirst`: * 4D tensor with shape: * `[batchSize, channels, depths, rows, cols]` * * Output shape * - If `dataFormat=channelsLast`: * 5D tensor with shape: * `[batchSize, pooledDepths, pooledRows, pooledCols, channels]` * - If `dataFormat=channelsFirst`: * 5D tensor with shape: * `[batchSize, channels, pooledDepths, pooledRows, pooledCols]` * * @doc {heading: 'Layers', subheading: 'Pooling', namespace: 'layers'} */ export function averagePooling3d(args) { return new AveragePooling3D(args); } export function avgPool3d(args) { return averagePooling3d(args); } // For backwards compatibility. // See https://github.com/tensorflow/tfjs/issues/152 export function avgPooling3d(args) { return averagePooling3d(args); } /** * Global average pooling operation for temporal data. * * Input Shape: 3D tensor with shape: `[batchSize, steps, features]`. * * Output Shape: 2D tensor with shape: `[batchSize, features]`. * * @doc {heading: 'Layers', subheading: 'Pooling', namespace: 'layers'} */ export function globalAveragePooling1d(args) { return new GlobalAveragePooling1D(args); } /** * Global average pooling operation for spatial data. * * Input shape: * - If `dataFormat` is `CHANNEL_LAST`: * 4D tensor with shape: `[batchSize, rows, cols, channels]`. * - If `dataFormat` is `CHANNEL_FIRST`: * 4D tensor with shape: `[batchSize, channels, rows, cols]`. * * Output shape: * 2D tensor with shape: `[batchSize, channels]`. * * @doc {heading: 'Layers', subheading: 'Pooling', namespace: 'layers'} */ export function globalAveragePooling2d(args) { return new GlobalAveragePooling2D(args); } /** * Global max pooling operation for temporal data. * * Input Shape: 3D tensor with shape: `[batchSize, steps, features]`. * * Output Shape: 2D tensor with shape: `[batchSize, features]`. * * @doc {heading: 'Layers', subheading: 'Pooling', namespace: 'layers'} */ export function globalMaxPooling1d(args) { return new GlobalMaxPooling1D(args); } /** * Global max pooling operation for spatial data. * * Input shape: * - If `dataFormat` is `CHANNEL_LAST`: * 4D tensor with shape: `[batchSize, rows, cols, channels]`. * - If `dataFormat` is `CHANNEL_FIRST`: * 4D tensor with shape: `[batchSize, channels, rows, cols]`. * * Output shape: * 2D tensor with shape: `[batchSize, channels]`. * * @doc {heading: 'Layers', subheading: 'Pooling', namespace: 'layers'} */ export function globalMaxPooling2d(args) { return new GlobalMaxPooling2D(args); } /** * Max pooling operation for temporal data. * * Input shape: `[batchSize, inLength, channels]` * * Output shape: `[batchSize, pooledLength, channels]` * * @doc {heading: 'Layers', subheading: 'Pooling', namespace: 'layers'} */ export function maxPooling1d(args) { return new MaxPooling1D(args); } /** * Max pooling operation for spatial data. * * Input shape * - If `dataFormat === CHANNEL_LAST`: * 4D tensor with shape: * `[batchSize, rows, cols, channels]` * - If `dataFormat === CHANNEL_FIRST`: * 4D tensor with shape: * `[batchSize, channels, rows, cols]` * * Output shape * - If `dataFormat=CHANNEL_LAST`: * 4D tensor with shape: * `[batchSize, pooledRows, pooledCols, channels]` * - If `dataFormat=CHANNEL_FIRST`: * 4D tensor with shape: * `[batchSize, channels, pooledRows, pooledCols]` * * @doc {heading: 'Layers', subheading: 'Pooling', namespace: 'layers'} */ export function maxPooling2d(args) { return new MaxPooling2D(args); } /** * Max pooling operation for 3D data. * * Input shape * - If `dataFormat === channelsLast`: * 5D tensor with shape: * `[batchSize, depths, rows, cols, channels]` * - If `dataFormat === channelsFirst`: * 5D tensor with shape: * `[batchSize, channels, depths, rows, cols]` * * Output shape * - If `dataFormat=channelsLast`: * 5D tensor with shape: * `[batchSize, pooledDepths, pooledRows, pooledCols, channels]` * - If `dataFormat=channelsFirst`: * 5D tensor with shape: * `[batchSize, channels, pooledDepths, pooledRows, pooledCols]` * * @doc {heading: 'Layers', subheading: 'Pooling', namespace: 'layers'} */ export function maxPooling3d(args) { return new MaxPooling3D(args); } // Recurrent Layers. /** * Gated Recurrent Unit - Cho et al. 2014. * * This is an `RNN` layer consisting of one `GRUCell`. However, unlike * the underlying `GRUCell`, the `apply` method of `SimpleRNN` operates * on a sequence of inputs. The shape of the input (not including the first, * batch dimension) needs to be at least 2-D, with the first dimension being * time steps. For example: * * ```js * const rnn = tf.layers.gru({units: 8, returnSequences: true}); * * // Create an input with 10 time steps. * const input = tf.input({shape: [10, 20]}); * const output = rnn.apply(input); * * console.log(JSON.stringify(output.shape)); * // [null, 10, 8]: 1st dimension is unknown batch size; 2nd dimension is the * // same as the sequence length of `input`, due to `returnSequences`: `true`; * // 3rd dimension is the `GRUCell`'s number of units. * * @doc {heading: 'Layers', subheading: 'Recurrent', namespace: 'layers'} */ export function gru(args) { return new GRU(args); } /** * Cell class for `GRU`. * * `GRUCell` is distinct from the `RNN` subclass `GRU` in that its * `apply` method takes the input data of only a single time step and returns * the cell's output at the time step, while `GRU` takes the input data * over a number of time steps. For example: * * ```js * const cell = tf.layers.gruCell({units: 2}); * const input = tf.input({shape: [10]}); * const output = cell.apply(input); * * console.log(JSON.stringify(output.shape)); * // [null, 10]: This is the cell's output at a single time step. The 1st * // dimension is the unknown batch size. * ``` * * Instance(s) of `GRUCell` can be used to construct `RNN` layers. The * most typical use of this workflow is to combine a number of cells into a * stacked RNN cell (i.e., `StackedRNNCell` internally) and use it to create an * RNN. For example: * * ```js * const cells = [ * tf.layers.gruCell({units: 4}), * tf.layers.gruCell({units: 8}), * ]; * const rnn = tf.layers.rnn({cell: cells, returnSequences: true}); * * // Create an input with 10 time steps and a length-20 vector at each step. * const input = tf.input({shape: [10, 20]}); * const output = rnn.apply(input); * * console.log(JSON.stringify(output.shape)); * // [null, 10, 8]: 1st dimension is unknown batch size; 2nd dimension is the * // same as the sequence length of `input`, due to `returnSequences`: `true`; * // 3rd dimension is the last `gruCell`'s number of units. * ``` * * To create an `RNN` consisting of only *one* `GRUCell`, use the * `tf.layers.gru`. * * @doc {heading: 'Layers', subheading: 'Recurrent', namespace: 'layers'} */ export function gruCell(args) { return new GRUCell(args); } /** * Long-Short Term Memory layer - Hochreiter 1997. * * This is an `RNN` layer consisting of one `LSTMCell`. However, unlike * the underlying `LSTMCell`, the `apply` method of `LSTM` operates * on a sequence of inputs. The shape of the input (not including the first, * batch dimension) needs to be at least 2-D, with the first dimension being * time steps. For example: * * ```js * const lstm = tf.layers.lstm({units: 8, returnSequences: true}); * * // Create an input with 10 time steps. * const input = tf.input({shape: [10, 20]}); * const output = lstm.apply(input); * * console.log(JSON.stringify(output.shape)); * // [null, 10, 8]: 1st dimension is unknown batch size; 2nd dimension is the * // same as the sequence length of `input`, due to `returnSequences`: `true`; * // 3rd dimension is the `LSTMCell`'s number of units. * * @doc {heading: 'Layers', subheading: 'Recurrent', namespace: 'layers'} */ export function lstm(args) { return new LSTM(args); } /** * Cell class for `LSTM`. * * `LSTMCell` is distinct from the `RNN` subclass `LSTM` in that its * `apply` method takes the input data of only a single time step and returns * the cell's output at the time step, while `LSTM` takes the input data * over a number of time steps. For example: * * ```js * const cell = tf.layers.lstmCell({units: 2}); * const input = tf.input({shape: [10]}); * const output = cell.apply(input); * * console.log(JSON.stringify(output.shape)); * // [null, 10]: This is the cell's output at a single time step. The 1st * // dimension is the unknown batch size. * ``` * * Instance(s) of `LSTMCell` can be used to construct `RNN` layers. The * most typical use of this workflow is to combine a number of cells into a * stacked RNN cell (i.e., `StackedRNNCell` internally) and use it to create an * RNN. For example: * * ```js * const cells = [ * tf.layers.lstmCell({units: 4}), * tf.layers.lstmCell({units: 8}), * ]; * const rnn = tf.layers.rnn({cell: cells, returnSequences: true}); * * // Create an input with 10 time steps and a length-20 vector at each step. * const input = tf.input({shape: [10, 20]}); * const output = rnn.apply(input); * * console.log(JSON.stringify(output.shape)); * // [null, 10, 8]: 1st dimension is unknown batch size; 2nd dimension is the * // same as the sequence length of `input`, due to `returnSequences`: `true`; * // 3rd dimension is the last `lstmCell`'s number of units. * ``` * * To create an `RNN` consisting of only *one* `LSTMCell`, use the * `tf.layers.lstm`. * * @doc {heading: 'Layers', subheading: 'Recurrent', namespace: 'layers'} */ export function lstmCell(args) { return new LSTMCell(args); } /** * Fully-connected RNN where the output is to be fed back to input. * * This is an `RNN` layer consisting of one `SimpleRNNCell`. However, unlike * the underlying `SimpleRNNCell`, the `apply` method of `SimpleRNN` operates * on a sequence of inputs. The shape of the input (not including the first, * batch dimension) needs to be at least 2-D, with the first dimension being * time steps. For example: * * ```js * const rnn = tf.layers.simpleRNN({units: 8, returnSequences: true}); * * // Create an input with 10 time steps. * const input = tf.input({shape: [10, 20]}); * const output = rnn.apply(input); * * console.log(JSON.stringify(output.shape)); * // [null, 10, 8]: 1st dimension is unknown batch size; 2nd dimension is the * // same as the sequence length of `input`, due to `returnSequences`: `true`; * // 3rd dimension is the `SimpleRNNCell`'s number of units. * ``` * * @doc {heading: 'Layers', subheading: 'Recurrent', namespace: 'layers'} */ export function simpleRNN(args) { return new SimpleRNN(args); } /** * Cell class for `SimpleRNN`. * * `SimpleRNNCell` is distinct from the `RNN` subclass `SimpleRNN` in that its * `apply` method takes the input data of only a single time step and returns * the cell's output at the time step, while `SimpleRNN` takes the input data * over a number of time steps. For example: * * ```js * const cell = tf.layers.simpleRNNCell({units: 2}); * const input = tf.input({shape: [10]}); * const output = cell.apply(input); * * console.log(JSON.stringify(output.shape)); * // [null, 10]: This is the cell's output at a single time step. The 1st * // dimension is the unknown batch size. * ``` * * Instance(s) of `SimpleRNNCell` can be used to construct `RNN` layers. The * most typical use of this workflow is to combine a number of cells into a * stacked RNN cell (i.e., `StackedRNNCell` internally) and use it to create an * RNN. For example: * * ```js * const cells = [ * tf.layers.simpleRNNCell({units: 4}), * tf.layers.simpleRNNCell({units: 8}), * ]; * const rnn = tf.layers.rnn({cell: cells, returnSequences: true}); * * // Create an input with 10 time steps and a length-20 vector at each step. * const input = tf.input({shape: [10, 20]}); * const output = rnn.apply(input); * * console.log(JSON.stringify(output.shape)); * // [null, 10, 8]: 1st dimension is unknown batch size; 2nd dimension is the * // same as the sequence length of `input`, due to `returnSequences`: `true`; * // 3rd dimension is the last `SimpleRNNCell`'s number of units. * ``` * * To create an `RNN` consisting of only *one* `SimpleRNNCell`, use the * `tf.layers.simpleRNN`. * * @doc {heading: 'Layers', subheading: 'Recurrent', namespace: 'layers'} */ export function simpleRNNCell(args) { return new SimpleRNNCell(args); } /** * Convolutional LSTM layer - Xingjian Shi 2015. * * This is a `ConvRNN2D` layer consisting of one `ConvLSTM2DCell`. However, * unlike the underlying `ConvLSTM2DCell`, the `apply` method of `ConvLSTM2D` * operates on a sequence of inputs. The shape of the input (not including the * first, batch dimension) needs to be 4-D, with the first dimension being time * steps. For example: * * ```js * const filters = 3; * const kernelSize = 3; * * const batchSize = 4; * const sequenceLength = 2; * const size = 5; * const channels = 3; * * const inputShape = [batchSize, sequenceLength, size, size, channels]; * const input = tf.ones(inputShape); * * const layer = tf.layers.convLstm2d({filters, kernelSize}); * * const output = layer.apply(input); * ``` */ /** @doc {heading: 'Layers', subheading: 'Recurrent', namespace: 'layers'} */ export function convLstm2d(args) { return new ConvLSTM2D(args); } /** * Cell class for `ConvLSTM2D`. * * `ConvLSTM2DCell` is distinct from the `ConvRNN2D` subclass `ConvLSTM2D` in * that its `call` method takes the input data of only a single time step and * returns the cell's output at the time step, while `ConvLSTM2D` takes the * input data over a number of time steps. For example: * * ```js * const filters = 3; * const kernelSize = 3; * * const sequenceLength = 1; * const size = 5; * const channels = 3; * * const inputShape = [sequenceLength, size, size, channels]; * const input = tf.ones(inputShape); * * const cell = tf.layers.convLstm2dCell({filters, kernelSize}); * * cell.build(input.shape); * * const outputSize = size - kernelSize + 1; * const outShape = [sequenceLength, outputSize, outputSize, filters]; * * const initialH = tf.zeros(outShape); * const initialC = tf.zeros(outShape); * * const [o, h, c] = cell.call([input, initialH, initialC], {}); * ``` */ /** @doc {heading: 'Layers', subheading: 'Recurrent', namespace: 'layers'} */ export function convLstm2dCell(args) { return new ConvLSTM2DCell(args); } /** * Base class for recurrent layers. * * Input shape: * 3D tensor with shape `[batchSize, timeSteps, inputDim]`. * * Output shape: * - if `returnState`, an Array of tensors (i.e., `tf.Tensor`s). The first * tensor is the output. The remaining tensors are the states at the * last time step, each with shape `[batchSize, units]`. * - if `returnSequences`, the output will have shape * `[batchSize, timeSteps, units]`. * - else, the output will have shape `[batchSize, units]`. * * Masking: * This layer supports masking for input data with a variable number * of timesteps. To introduce masks to your data, * use an embedding layer with the `mask_zero` parameter * set to `True`. * * Notes on using statefulness in RNNs: * You can set RNN layers to be 'stateful', which means that the states * computed for the samples in one batch will be reused as initial states * for the samples in the next batch. This assumes a one-to-one mapping * between samples in different successive batches. * * To enable statefulness: * - specify `stateful: true` in the layer constructor. * - specify a fixed batch size for your model, by passing * if sequential model: * `batchInputShape=[...]` to the first layer in your model. * else for functional model with 1 or more Input layers: * `batchShape=[...]` to all the first layers in your model. * This is the expected shape of your inputs *including the batch size*. * It should be a tuple of integers, e.g. `(32, 10, 100)`. * - specify `shuffle=False` when calling fit(). * * To reset the states of your model, call `.resetStates()` on either * a specific layer, or on your entire model. * * Note on specifying the initial state of RNNs * You can specify the initial state of RNN layers symbolically by * calling them with the option `initialState`. The value of * `initialState` should be a tensor or list of tensors representing * the initial state of the RNN layer. * * You can specify the initial state of RNN layers numerically by * calling `resetStates` with the keyword argument `states`. The value of * `states` should be a numpy array or list of numpy arrays representing * the initial state of the RNN layer. * * Note on passing external constants to RNNs * You can pass "external" constants to the cell using the `constants` * keyword argument of `RNN.call` method. This requires that the `cell.call` * method accepts the same keyword argument `constants`. Such constants * can be used to condition the cell transformation on additional static * inputs (not changing over time), a.k.a. an attention mechanism. * * @doc {heading: 'Layers', subheading: 'Recurrent', namespace: 'layers'} */ export function rnn(args) { return new RNN(args); } /** * Wrapper allowing a stack of RNN cells to behave as a single cell. * * Used to implement efficient stacked RNNs. * * @doc {heading: 'Layers', subheading: 'Recurrent', namespace: 'layers'} */ export function stackedRNNCells(args) { return new StackedRNNCells(args); } // Wrapper Layers. /** @doc {heading: 'Layers', subheading: 'Wrapper', namespace: 'layers'} */ export function bidirectional(args) { return new Bidirectional(args); } /** * This wrapper applies a layer to every temporal slice of an input. * * The input should be at least 3D, and the dimension of the index `1` will be * considered to be the temporal dimension. * * Consider a batch of 32 samples, where each sample is a sequence of 10 vectors * of 16 dimensions. The batch input shape of the layer is then `[32, 10, * 16]`, and the `inputShape`, not including the sample dimension, is * `[10, 16]`. * * You can then use `TimeDistributed` to apply a `Dense` layer to each of the 10 * timesteps, independently: * * ```js * const model = tf.sequential(); * model.add(tf.layers.timeDistributed({ * layer: tf.layers.dense({units: 8}), * inputShape: [10, 16], * })); * * // Now model.outputShape = [null, 10, 8]. * // The output will then have shape `[32, 10, 8]`. * * // In subsequent layers, there is no need for `inputShape`: * model.add(tf.layers.timeDistributed({layer: tf.layers.dense({units: 32})})); * console.log(JSON.stringify(model.outputs[0].shape)); * // Now model.outputShape = [null, 10, 32]. * ``` * * The output will then have shape `[32, 10, 32]`. * * `TimeDistributed` can be used with arbitrary layers, not just `Dense`, for * instance a `Conv2D` layer. * * ```js * const model = tf.sequential(); * model.add(tf.layers.timeDistributed({ * layer: tf.layers.conv2d({filters: 64, kernelSize: [3, 3]}), * inputShape: [10, 299, 299, 3], * })); * console.log(JSON.stringify(model.outputs[0].shape)); * ``` * * @doc {heading: 'Layers', subheading: 'Wrapper', namespace: 'layers'} */ export function timeDistributed(args) { return new TimeDistributed(args); } // Aliases for pooling. export const globalMaxPool1d = globalMaxPooling1d; export const globalMaxPool2d = globalMaxPooling2d; export const maxPool1d = maxPooling1d; export const maxPool2d = maxPooling2d; export { Layer, RNN, RNNCell, input /* alias for tf.input */ }; /** * Apply additive zero-centered Gaussian noise. * * As it is a regularization layer, it is only active at training time. * * This is useful to mitigate overfitting * (you could see it as a form of random data augmentation). * Gaussian Noise (GS) is a natural choice as corruption process * for real valued inputs. * * # Arguments * stddev: float, standard deviation of the noise distribution. * * # Input shape * Arbitrary. Use the keyword argument `input_shape` * (tuple of integers, does not include the samples axis) * when using this layer as the first layer in a model. * * # Output shape * Same shape as input. * * @doc {heading: 'Layers', subheading: 'Noise', namespace: 'layers'} */ export function gaussianNoise(args) { return new GaussianNoise(args); } /** * Apply multiplicative 1-centered Gaussian noise. * * As it is a regularization layer, it is only active at training time. * * Arguments: * - `rate`: float, drop probability (as with `Dropout`). * The multiplicative noise will have * standard deviation `sqrt(rate / (1 - rate))`. * * Input shape: * Arbitrary. Use the keyword argument `inputShape` * (tuple of integers, does not include the samples axis) * when using this layer as the first layer in a model. * * Output shape: * Same shape as input. * * References: * - [Dropout: A Simple Way to Prevent Neural Networks from Overfitting]( * http://www.cs.toronto.edu/~rsalakhu/papers/srivastava14a.pdf) * * @doc {heading: 'Layers', subheading: 'Noise', namespace: 'layers'} */ export function gaussianDropout(args) { return new GaussianDropout(args); } /** * Applies Alpha Dropout to the input. * * As it is a regularization layer, it is only active at training time. * * Alpha Dropout is a `Dropout` that keeps mean and variance of inputs * to their original values, in order to ensure the self-normalizing property * even after this dropout. * Alpha Dropout fits well to Scaled Exponential Linear Units * by randomly setting activations to the negative saturation value. * * Arguments: * - `rate`: float, drop probability (as with `Dropout`). * The multiplicative noise will have * standard deviation `sqrt(rate / (1 - rate))`. * - `noise_shape`: A 1-D `Tensor` of type `int32`, representing the * shape for randomly generated keep/drop flags. * * Input shape: * Arbitrary. Use the keyword argument `inputShape` * (tuple of integers, does not include the samples axis) * when using this layer as the first layer in a model. * * Output shape: * Same shape as input. * * References: * - [Self-Normalizing Neural Networks](https://arxiv.org/abs/1706.02515) * * @doc {heading: 'Layers', subheading: 'Noise', namespace: 'layers'} */ export function alphaDropout(args) { return new AlphaDropout(args); } /** * Masks a sequence by using a mask value to skip timesteps. * * If all features for a given sample timestep are equal to `mask_value`, * then the sample timestep will be masked (skipped) in all downstream layers * (as long as they support masking). * * If any downstream layer does not support masking yet receives such * an input mask, an exception will be raised. * * Arguments: * - `maskValue`: Either None or mask value to skip. * * Input shape: * Arbitrary. Use the keyword argument `inputShape` * (tuple of integers, does not include the samples axis) * when using this layer as the first layer in a model. * * Output shape: * Same shape as input. * * @doc {heading: 'Layers', subheading: 'Mask', namespace: 'layers'} */ export function masking(args) { return new Masking(args); } /** * A preprocessing layer which rescales input values to a new range. * * This layer rescales every value of an input (often an image) by multiplying * by `scale` and adding `offset`. * * For instance: * 1. To rescale an input in the ``[0, 255]`` range * to be in the `[0, 1]` range, you would pass `scale=1/255`. * 2. To rescale an input in the ``[0, 255]`` range to be in the `[-1, 1]` * range, you would pass `scale=1./127.5, offset=-1`. * The rescaling is applied both during training and inference. Inputs can be * of integer or floating point dtype, and by default the layer will output * floats. * * Arguments: * - `scale`: Float, the scale to apply to the inputs. * - `offset`: Float, the offset to apply to the inputs. * * Input shape: * Arbitrary. * * Output shape: * Same as input. * * @doc {heading: 'Layers', subheading: 'Rescaling', namespace: 'layers'} */ export function rescaling(args) { return new Rescaling(args); } /** * A preprocessing layer which center crops images. * * This layers crops the central portion of the images to a target size. If an * image is smaller than the target size, it will be resized and cropped so as * to return the largest possible window in the image that matches the target * aspect ratio. * * Input pixel values can be of any range (e.g. `[0., 1.)` or `[0, 255]`) and * of integer or floating point dtype. * * If the input height/width is even and the target height/width is odd (or * inversely), the input image is left-padded by 1 pixel. * * Arguments: * `height`: Integer, the height of the output shape. * `width`: Integer, the width of the output shape. * * Input shape: * 3D (unbatched) or 4D (batched) tensor with shape: * `(..., height, width, channels)`, in `channelsLast` format. * * Output shape: * 3D (unbatched) or 4D (batched) tensor with shape: * `(..., targetHeight, targetWidth, channels)`. * * * @doc {heading: 'Layers', subheading: 'CenterCrop', namespace: 'layers'} */ export function centerCrop(args) { return new CenterCrop(args); } /** * A preprocessing layer which resizes images. * This layer resizes an image input to a target height and width. The input * should be a 4D (batched) or 3D (unbatched) tensor in `"channels_last"` * format. Input pixel values can be of any range (e.g. `[0., 1.)` or `[0, * 255]`) and of interger or floating point dtype. By default, the layer will * output floats. * * Arguments: * - `height`: number, the height for the output tensor. * - `width`: number, the width for the output tensor. * - `interpolation`: string, the method for image resizing interpolation. * - `cropToAspectRatio`: boolean, whether to keep image aspect ratio. * * Input shape: * Arbitrary. * * Output shape: * height, width, num channels. * * @doc {heading: 'Layers', subheading: 'Resizing', namespace: 'layers'} */ export function resizing(args) { return new Resizing(args); } /** * A preprocessing layer which encodes integer features. * * This layer provides options for condensing data into a categorical encoding * when the total number of tokens are known in advance. It accepts integer * values as inputs, and it outputs a dense representation of those * inputs. * * Arguments: * * numTokens: The total number of tokens the layer should support. All * inputs to the layer must integers in the range `0 <= value < * numTokens`, or an error will be thrown. * * outputMode: Specification for the output of the layer. * Defaults to `multiHot`. Values can be `oneHot`, `multiHot` or * `count`, configuring the layer as follows: * * oneHot: Encodes each individual element in the input into an * array of `numTokens` size, containing a 1 at the element index. If * the last dimension is size 1, will encode on that dimension. If the * last dimension is not size 1, will append a new dimension for the * encoded output. * * multiHot: Encodes each sample in the input into a single array * of `numTokens` size, containing a 1 for each vocabulary term * present in the sample. Treats the last dimension as the sample * dimension, if input shape is `(..., sampleLength)`, output shape * will be `(..., numTokens)`. * * count: Like `multiHot`, but the int array contains a count of * the number of times the token at that index appeared in the sample. * * For all output modes, currently only output up to rank 2 is supported. * Call arguments: * inputs: A 1D or 2D tensor of integer inputs. * countWeights: A tensor in the same shape as `inputs` indicating the * weight for each sample value when summing up in `count` mode. Not used * in `multiHot` or `oneHot` modes. * * * @doc {heading: 'Layers', subheading: 'CategoryEncoding', namespace: 'layers'} */ export function categoryEncoding(args) { return new CategoryEncoding(args); } /** * A preprocessing layer which randomly varies image width during training. * * This layer will randomly adjusts the width of a batch of images of a batch * of images by a random factor. * * The input should be a 3D (unbatched) or 4D (batched) tensor in * the `"channels_last"` image data format. Input pixel values can be of any * range (e.g. `[0., 1.)` or `[0, 255]`) and of integer or floating point * dtype. By default, the layer will output floats. By default, this layer is * inactive during inference. For an overview and full list of preprocessing * layers, see the preprocessing [guide] * (https://www.tensorflow.org/guide/keras/preprocessing_layers). * * Arguments: * * factor: * A positive float (fraction of original width), or a tuple of size 2 * representing lower and upper bound for resizing vertically. * When represented as a single float, this value is used for both the upper * and lower bound. For instance, `factor=(0.2, 0.3)` results in an output * with width changed by a random amount in the range `[20%, 30%]`. * `factor=(-0.2, 0.3)` results in an output with width changed by a random * amount in the range `[-20%, +30%]`. `factor=0.2` results in an output * with width changed by a random amount in the range `[-20%, +20%]`. * interpolation: * String, the interpolation method. * Defaults to `bilinear`. * Supports `"bilinear"`, `"nearest"`. * The tf methods `"bicubic"`, `"area"`, `"lanczos3"`, `"lanczos5"`, * `"gaussian"`, `"mitchellcubic"` are unimplemented in tfjs. * seed: * Integer. Used to create a random seed. * * Input shape: * 3D (unbatched) or 4D (batched) tensor with shape: * `(..., height, width, channels)`, in `"channels_last"` format. * Output shape: * 3D (unbatched) or 4D (batched) tensor with shape: * `(..., height, random_width, channels)`. * * * @doc {heading: 'Layers', subheading: 'RandomWidth', namespace: 'layers'} */ export function randomWidth(args) { return new RandomWidth(args); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhwb3J0c19sYXllcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi90ZmpzLWxheWVycy9zcmMvZXhwb3J0c19sYXllcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7O0dBUUc7QUFFSCxPQUFPLEVBQUMsVUFBVSxFQUFpQixNQUFNLHNCQUFzQixDQUFDO0FBQ2hFLE9BQU8sRUFBQyxLQUFLLEVBQVksTUFBTSxtQkFBbUIsQ0FBQztBQUNuRCxPQUFPLEVBQUMsS0FBSyxFQUFDLE1BQU0sV0FBVyxDQUFDO0FBQ2hDLE9BQU8sRUFBQyxHQUFHLEVBQWdCLFNBQVMsRUFBc0IsS0FBSyxFQUFrQixJQUFJLEVBQWlCLE9BQU8sRUFBb0IsZUFBZSxFQUEyQixNQUFNLCtCQUErQixDQUFDO0FBQ2pOLE9BQU8sRUFBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLGVBQWUsRUFBRSxNQUFNLEVBQWlCLFVBQVUsRUFBdUIsZUFBZSxFQUEwQixZQUFZLEVBQXlCLGVBQWUsRUFBQyxNQUFNLHdCQUF3QixDQUFDO0FBQzlOLE9BQU8sRUFBQyxlQUFlLEVBQTJCLE1BQU0sa0NBQWtDLENBQUM7QUFDM0YsT0FBTyxFQUFDLFVBQVUsRUFBa0IsY0FBYyxFQUFxQixNQUFNLGtDQUFrQyxDQUFDO0FBQ2hILE9BQU8sRUFBQyxVQUFVLEVBQXVCLEtBQUssRUFBa0IsT0FBTyxFQUFvQixPQUFPLEVBQW9CLE9BQU8sRUFBZSxPQUFPLEVBQW9CLFlBQVksRUFBeUIsT0FBTyxFQUFvQixnQkFBZ0IsRUFBOEIsTUFBTSxlQUFlLENBQUM7QUFDM1MsT0FBTyxFQUFDLFNBQVMsRUFBcUIsTUFBTSxxQkFBcUIsQ0FBQztBQUNsRSxPQUFPLEVBQUMsR0FBRyxFQUFFLE9BQU8sRUFBRSxXQUFXLEVBQXdCLEdBQUcsRUFBZ0IsT0FBTyxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUMsTUFBTSxnQkFBZ0IsQ0FBQztBQUM5SCxPQUFPLEVBQUMsWUFB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LE1BQU0sVUFBVSxRQUFRLENBQUMsSUFBbUI7SUFDMUMsT0FBTyxJQUFJLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUM1QixDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTBDRztBQUNILE1BQU0sVUFBVSxnQkFBZ0IsQ0FBQyxJQUEwQjtJQUN6RCxPQUFPLElBQUksZ0JBQWdCLENBQUMsSUFBSSxDQUFDLENBQUM7QUFDcEMsQ0FBQztBQUVBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBMkNHO0FBQ0YsTUFBTSxVQUFVLFdBQVcsQ0FBQyxJQUFxQjtJQUMvQyxPQUFPLElBQUksV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQy9CLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgMjAxOCBHb29nbGUgTExDXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlXG4gKiBsaWNlbnNlIHRoYXQgY2FuIGJlIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgb3IgYXRcbiAqIGh0dHBzOi8vb3BlbnNvdXJjZS5vcmcvbGljZW5zZXMvTUlULlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqL1xuXG5pbXBvcnQge0lucHV0TGF5ZXIsIElucHV0TGF5ZXJBcmdzfSBmcm9tICcuL2VuZ2luZS9pbnB1dF9sYXllcic7XG5pbXBvcnQge0xheWVyLCBMYXllckFyZ3N9IGZyb20gJy4vZW5naW5lL3RvcG9sb2d5JztcbmltcG9ydCB7aW5wdXR9IGZyb20gJy4vZXhwb3J0cyc7XG5pbXBvcnQge0VMVSwgRUxVTGF5ZXJBcmdzLCBMZWFreVJlTFUsIExlYWt5UmVMVUxheWVyQXJncywgUFJlTFUsIFBSZUxVTGF5ZXJBcmdzLCBSZUxVLCBSZUxVTGF5ZXJBcmdzLCBTb2Z0bWF4LCBTb2Z0bWF4TGF5ZXJBcmdzLCBUaHJlc2hvbGRlZFJlTFUsIFRocmVzaG9sZGVkUmVMVUxheWVyQXJnc30gZnJvbSAnLi9sYXllcnMvYWR2YW5jZWRfYWN0aXZhdGlvbnMnO1xuaW1wb3J0IHtDb252MUQsIENvbnYyRCwgQ29udjJEVHJhbnNwb3NlLCBDb252M0QsIENvbnZMYXllckFyZ3MsIENyb3BwaW5nMkQsIENyb3BwaW5nMkRMYXllckFyZ3MsIFNlcGFyYWJsZUNvbnYyRCwgU2VwYXJhYmxlQ29udkxheWVyQXJncywgVXBTYW1wbGluZzJELCBVcFNhbXBsaW5nMkRMYXllckFyZ3MsIENvbnYzRFRyYW5zcG9zZX0gZnJvbSAnLi9sYXllcnMvY29udm9sdXRpb25hbCc7XG5pbXBvcnQge0RlcHRod2lzZUNvbnYyRCwgRGVwdGh3aXNlQ29udjJETGF5ZXJBcmdzfSBmcm9tICcuL2xheWVycy9jb252b2x1dGlvbmFsX2RlcHRod2lzZSc7XG5pbXBvcnQge0NvbnZMU1RNMkQsIENvbnZMU1RNMkRBcmdzLCBDb252TFNUTTJEQ2VsbCwgQ29udkxTVE0yRENlbGxBcmdzfSBmcm9tICcuL2xheWVycy9jb252b2x1dGlvbmFsX3JlY3VycmVudCc7XG5pbXBvcnQge0FjdGl2YXRpb24sIEFjdGl2YXRpb25MYXllckFyZ3MsIERlbnNlLCBEZW5zZUxheWVyQXJncywgRHJvcG91dCwgRHJvcG91dExheWVyQXJncywgRmxhdHRlbiwgRmxhdHRlbkxheWVyQXJncywgTWFza2luZywgTWFza2luZ0FyZ3MsIFBlcm11dGUsIFBlcm11dGVMYXllckFyZ3MsIFJlcGVhdFZlY3RvciwgUmVwZWF0VmVjdG9yTGF5ZXJBcmdzLCBSZXNoYXBlLCBSZXNoYXBlTGF5ZXJBcmdzLCBTcGF0aWFsRHJvcG91dDFELCBTcGF0aWFsRHJvcG91dDFETGF5ZXJDb25maWd9IGZyb20gJy4vbGF5ZXJzL2NvcmUnO1xuaW1wb3J0IHtFbWJlZGRpbmcsIEVtYmVkZGluZ0xheWVyQXJnc30gZnJvbSAnLi9sYXllcnMvZW1iZWRkaW5ncyc7XG5pbXBvcnQge0FkZCwgQXZlcmFnZSwgQ29uY2F0ZW5hdGUsIENvbmNhdGVuYXRlTGF5ZXJBcmdzLCBEb3QsIERvdExheWVyQXJncywgTWF4aW11bSwgTWluaW11bSwgTXVsdGlwbHl9IGZyb20gJy4vbGF5ZXJzL21lcmdlJztcbmltcG9ydCB7QWxwaGFEcm9wb3V0LCBBbHBoYURyb3BvdXRBcmdzLCBHYXVzc2lhbkRyb3BvdXQsIEdhdXNzaWFuRHJvcG91dEFyZ3MsIEdhdXNzaWFuTm9pc2UsIEdhdXNzaWFuTm9pc2VBcmdzfSBmcm9tICcuL2xheWVycy9ub2lzZSc7XG5pbXBvcnQge0JhdGNoTm9ybWFsaXphdGlvbiwgQmF0Y2hOb3JtYWxpemF0aW9uTGF5ZXJBcmdzLCBMYXllck5vcm1hbGl6YXRpb24sIExheWVyTm9ybWFsaXphdGlvbkxheWVyQXJnc30gZnJvbSAnLi9sYXllcnMvbm9ybWFsaXphdGlvbic7XG5pbXBvcnQge1plcm9QYWRkaW5nMkQsIFplcm9QYWRkaW5nMkRMYXllckFyZ3N9IGZyb20gJy4vbGF5ZXJzL3BhZGRpbmcnO1xuaW1wb3J0IHtBdmVyYWdlUG9vbGluZzFELCBBdmVyYWdlUG9vbGluZzJELCBBdmVyYWdlUG9vbGluZzNELCBHbG9iYWxBdmVyYWdlUG9vbGluZzFELCBHbG9iYWxBdmVyYWdlUG9vbGluZzJELCBHbG9iYWxNYXhQb29saW5nMUQsIEdsb2JhbE1heFBvb2xpbmcyRCwgR2xvYmFsUG9vbGluZzJETGF5ZXJBcmdzLCBNYXhQb29saW5nMUQsIE1heFBvb2xpbmcyRCwgTWF4UG9vbGluZzNELCBQb29saW5nMURMYXllckFyZ3MsIFBvb2xpbmcyRExheWVyQXJncywgUG9vbGluZzNETGF5ZXJBcmdzfSBmcm9tICcuL2xheWVycy9wb29saW5nJztcbmltcG9ydCB7R1JVLCBHUlVDZWxsLCBHUlVDZWxsTGF5ZXJBcmdzLCBHUlVMYXllckFyZ3MsIExTVE0sIExTVE1DZWxsLCBMU1RNQ2VsbExheWVyQXJncywgTFNUTUxheWVyQXJncywgUk5OLCBSTk5DZWxsLCBSTk5MYXllckFyZ3MsIFNpbXBsZVJOTiwgU2ltcGxlUk5OQ2VsbCwgU2ltcGxlUk5OQ2VsbExheWVyQXJncywgU2ltcGxlUk5OTGF5ZXJBcmdzLCBTdGFja2VkUk5OQ2VsbHMsIFN0YWNrZWRSTk5DZWxsc0FyZ3N9IGZyb20gJy4vbGF5ZXJzL3JlY3VycmVudCc7XG5pbXBvcnQge0JpZGlyZWN0aW9uYWwsIEJpZGlyZWN0aW9uYWxMYXllckFyZ3MsIFRpbWVEaXN0cmlidXRlZCwgV3JhcHBlckxheWVyQXJnc30gZnJvbSAnLi9sYXllcnMvd3JhcHBlcnMnO1xuaW1wb3J0IHtSZXNjYWxpbmcsIFJlc2NhbGluZ0FyZ3N9IGZyb20gJy4vbGF5ZXJzL3ByZXByb2Nlc3NpbmcvaW1hZ2VfcHJlcHJvY2Vzc2luZyc7XG5pbXBvcnQge0NlbnRlckNyb3AsIENlbnRlckNyb3BBcmdzfSBmcm9tICcuL2xheWVycy9wcmVwcm9jZXNzaW5nL2NlbnRlcl9jcm9wJztcbmltcG9ydCB7Q2F0ZWdvcnlFbmNvZGluZywgQ2F0ZWdvcnlFbmNvZGluZ0FyZ3N9IGZyb20gJy4vbGF5ZXJzL3ByZXByb2Nlc3NpbmcvY2F0ZWdvcnlfZW5jb2RpbmcnO1xuaW1wb3J0IHtSZXNpemluZywgUmVzaXppbmdBcmdzfSBmcm9tICcuL2xheWVycy9wcmVwcm9jZXNzaW5nL2ltYWdlX3Jlc2l6aW5nJztcbmltcG9ydCB7UmFuZG9tV2lkdGgsIFJhbmRvbVdpZHRoQXJnc30gZnJvbSAnLi9sYXllcnMvcHJlcHJvY2Vzc2luZy9yYW5kb21fd2lkdGgnO1xuXG4vLyBUT0RPKGNhaXMpOiBBZGQgZG9jIHN0cmluZyB0byBhbGwgdGhlIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb25zIGluIHRoaXNcbi8vICAgY2xhc3M7IGluY2x1ZGUgZXhlY3R1YWJsZSBKYXZhU2NyaXB0IGNvZGUgc25pcHBldHMgd2hlcmUgYXBwbGljYWJsZVxuLy8gICAoYi83NDA3NDQ1OCkuXG5cbi8vIElucHV0IExheWVyLlxuLyoqXG4gKiBBbiBpbnB1dCBsYXllciBpcyBhbiBlbnRyeSBwb2ludCBpbnRvIGEgYHRmLkxheWVyc01vZGVsYC5cbiAqXG4gKiBgSW5wdXRMYXllcmAgaXMgZ2VuZXJhdGVkIGF1dG9tYXRpY2FsbHkgZm9yIGB0Zi5TZXF1ZW50aWFsYCBtb2RlbHMgYnlcbiAqIHNwZWNpZnlpbmcgdGhlIGBpbnB1dHNoYXBlYCBvciBgYmF0Y2hJbnB1dFNoYXBlYCBmb3IgdGhlIGZpcnN0IGxheWVyLiAgSXRcbiAqIHNob3VsZCBub3QgYmUgc3BlY2lmaWVkIGV4cGxpY2l0bHkuIEhvd2V2ZXIsIGl0IGNhbiBiZSB1c2VmdWwgc29tZXRpbWVzLFxuICogZS5nLiwgd2hlbiBjb25zdHJ1Y3RpbmcgYSBzZXF1ZW50aWFsIG1vZGVsIGZyb20gYSBzdWJzZXQgb2YgYW5vdGhlclxuICogc2VxdWVudGlhbCBtb2RlbCdzIGxheWVycy4gTGlrZSB0aGUgY29kZSBzbmlwcGV0IGJlbG93IHNob3dzLlxuICpcbiAqIGBgYGpzXG4gKiAvLyBEZWZpbmUgYSBtb2RlbCB3aGljaCBzaW1wbHkgYWRkcyB0d28gaW5wdXRzLlxuICogY29uc3QgbW9kZWwxID0gdGYuc2VxdWVudGlhbCgpO1xuICogbW9kZWwxLmFkZCh0Zi5sYXllcnMuZGVuc2Uoe2lucHV0U2hhcGU6IFs0XSwgdW5pdHM6IDMsIGFjdGl2YXRpb246ICdyZWx1J30pKTtcbiAqIG1vZGVsMS5hZGQodGYubGF5ZXJzLmRlbnNlKHt1bml0czogMSwgYWN0aXZhdGlvbjogJ3NpZ21vaWQnfSkpO1xuICogbW9kZWwxLnN1bW1hcnkoKTtcbiAqIG1vZGVsMS5wcmVkaWN0KHRmLnplcm9zKFsxLCA0XSkpLnByaW50KCk7XG4gKlxuICogLy8gQ29uc3RydWN0IGFub3RoZXIgbW9kZWwsIHJldXNpbmcgdGhlIHNlY29uZCBsYXllciBvZiBgbW9kZWwxYCB3aGlsZVxuICogLy8gbm90IHVzaW5nIHRoZSBmaXJzdCBsYXllciBvZiBgbW9kZWwxYC4gTm90ZSB0aGF0IHlvdSBjYW5ub3QgYWRkIHRoZSBzZWNvbmRcbiAqIC8vIGxheWVyIG9mIGBtb2RlbGAgZGlyZWN0bHkgYXMgdGhlIGZpcnN0IGxheWVyIG9mIHRoZSBuZXcgc2VxdWVudGlhbCBtb2RlbCxcbiAqIC8vIGJlY2F1c2UgZG9pbmcgc28gd2lsbCBsZWFkIHRvIGFuIGVycm9yIHJlbGF0ZWQgdG8gdGhlIGZhY3QgdGhhdCB0aGUgbGF5ZXJcbiAqIC8vIGlzIG5vdCBhbiBpbnB1dCBsYXllci4gSW5zdGVhZCwgeW91IG5lZWQgdG8gY3JlYXRlIGFuIGBpbnB1dExheWVyYCBhbmQgYWRkXG4gKiAvLyBpdCB0byB0aGUgbmV3IHNlcXVlbnRpYWwgbW9kZWwgYmVmb3JlIGFkZGluZyB0aGUgcmV1c2VkIGxheWVyLlxuICogY29uc3QgbW9kZWwyID0gdGYuc2VxdWVudGlhbCgpO1xuICogLy8gVXNlIGFuIGlucHV0U2hhcGUgdGhhdCBtYXRjaGVzIHRoZSBpbnB1dCBzaGFwZSBvZiBgbW9kZWwxYCdzIHNlY29uZFxuICogLy8gbGF5ZXIuXG4gKiBtb2RlbDIuYWRkKHRmLmxheWVycy5pbnB1dExheWVyKHtpbnB1dFNoYXBlOiBbM119KSk7XG4gKiBtb2RlbDIuYWRkKG1vZGVsMS5sYXllcnNbMV0pO1xuICogbW9kZWwyLnN1bW1hcnkoKTtcbiAqIG1vZGVsMi5wcmVkaWN0KHRmLnplcm9zKFsxLCAzXSkpLnByaW50KCk7XG4gKiBgYGBcbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ0lucHV0cycsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbnB1dExheWVyKGFyZ3M6IElucHV0TGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgSW5wdXRMYXllcihhcmdzKTtcbn1cblxuLy8gQWR2YW5jZWQgQWN0aXZhdGlvbiBMYXllcnMuXG5cbi8qKlxuICogRXhwb25lbnRpYWwgTGluZWFyIFVuaXQgKEVMVSkuXG4gKlxuICogSXQgZm9sbG93czpcbiAqIGBmKHgpID0gIGFscGhhICogKGV4cCh4KSAtIDEuKSBmb3IgeCA8IDBgLFxuICogYGYoeCkgPSB4IGZvciB4ID49IDBgLlxuICpcbiAqIElucHV0IHNoYXBlOlxuICogICBBcmJpdHJhcnkuIFVzZSB0aGUgY29uZmlndXJhdGlvbiBgaW5wdXRTaGFwZWAgd2hlbiB1c2luZyB0aGlzIGxheWVyIGFzIHRoZVxuICogICBmaXJzdCBsYXllciBpbiBhIG1vZGVsLlxuICpcbiAqIE91dHB1dCBzaGFwZTpcbiAqICAgU2FtZSBzaGFwZSBhcyB0aGUgaW5wdXQuXG4gKlxuICogUmVmZXJlbmNlczpcbiAqICAgLSBbRmFzdCBhbmQgQWNjdXJhdGUgRGVlcCBOZXR3b3JrIExlYXJuaW5nIGJ5IEV4cG9uZW50aWFsIExpbmVhciBVbml0c1xuICogKEVMVXMpXShodHRwczovL2FyeGl2Lm9yZy9hYnMvMTUxMS4wNzI4OXYxKVxuICpcbiAqIEBkb2Mge1xuICogICBoZWFkaW5nOiAnTGF5ZXJzJyxcbiAqICAgc3ViaGVhZGluZzogJ0FkdmFuY2VkIEFjdGl2YXRpb24nLFxuICogICBuYW1lc3BhY2U6ICdsYXllcnMnXG4gKiB9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBlbHUoYXJncz86IEVMVUxheWVyQXJncykge1xuICByZXR1cm4gbmV3IEVMVShhcmdzKTtcbn1cblxuLyoqXG4gKiBSZWN0aWZpZWQgTGluZWFyIFVuaXQgYWN0aXZhdGlvbiBmdW5jdGlvbi5cbiAqXG4gKiBJbnB1dCBzaGFwZTpcbiAqICAgQXJiaXRyYXJ5LiBVc2UgdGhlIGNvbmZpZyBmaWVsZCBgaW5wdXRTaGFwZWAgKEFycmF5IG9mIGludGVnZXJzLCBkb2VzXG4gKiAgIG5vdCBpbmNsdWRlIHRoZSBzYW1wbGUgYXhpcykgd2hlbiB1c2luZyB0aGlzIGxheWVyIGFzIHRoZSBmaXJzdCBsYXllclxuICogICBpbiBhIG1vZGVsLlxuICpcbiAqIE91dHB1dCBzaGFwZTpcbiAqICAgU2FtZSBzaGFwZSBhcyB0aGUgaW5wdXQuXG4gKlxuICogQGRvYyB7XG4gKiAgIGhlYWRpbmc6ICdMYXllcnMnLFxuICogICBzdWJoZWFkaW5nOiAnQWR2YW5jZWQgQWN0aXZhdGlvbicsXG4gKiAgIG5hbWVzcGFjZTogJ2xheWVycydcbiAqIH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlTFUoYXJncz86IFJlTFVMYXllckFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBSZUxVKGFyZ3MpO1xufVxuXG4vKipcbiAqIExlYWt5IHZlcnNpb24gb2YgYSByZWN0aWZpZWQgbGluZWFyIHVuaXQuXG4gKlxuICogSXQgYWxsb3dzIGEgc21hbGwgZ3JhZGllbnQgd2hlbiB0aGUgdW5pdCBpcyBub3QgYWN0aXZlOlxuICogYGYoeCkgPSBhbHBoYSAqIHggZm9yIHggPCAwLmBcbiAqIGBmKHgpID0geCBmb3IgeCA+PSAwLmBcbiAqXG4gKiBJbnB1dCBzaGFwZTpcbiAqICAgQXJiaXRyYXJ5LiBVc2UgdGhlIGNvbmZpZ3VyYXRpb24gYGlucHV0U2hhcGVgIHdoZW4gdXNpbmcgdGhpcyBsYXllciBhcyB0aGVcbiAqICAgZmlyc3QgbGF5ZXIgaW4gYSBtb2RlbC5cbiAqXG4gKiBPdXRwdXQgc2hhcGU6XG4gKiAgIFNhbWUgc2hhcGUgYXMgdGhlIGlucHV0LlxuICpcbiAqIEBkb2Mge1xuICogICBoZWFkaW5nOiAnTGF5ZXJzJyxcbiAqICAgc3ViaGVhZGluZzogJ0FkdmFuY2VkIEFjdGl2YXRpb24nLFxuICogICBuYW1lc3BhY2U6ICdsYXllcnMnXG4gKiB9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsZWFreVJlTFUoYXJncz86IExlYWt5UmVMVUxheWVyQXJncykge1xuICByZXR1cm4gbmV3IExlYWt5UmVMVShhcmdzKTtcbn1cblxuLyoqXG4gKiBQYXJhbWV0ZXJpemVkIHZlcnNpb24gb2YgYSBsZWFreSByZWN0aWZpZWQgbGluZWFyIHVuaXQuXG4gKlxuICogSXQgZm9sbG93c1xuICogYGYoeCkgPSBhbHBoYSAqIHggZm9yIHggPCAwLmBcbiAqIGBmKHgpID0geCBmb3IgeCA+PSAwLmBcbiAqIHdoZXJlaW4gYGFscGhhYCBpcyBhIHRyYWluYWJsZSB3ZWlnaHQuXG4gKlxuICogSW5wdXQgc2hhcGU6XG4gKiAgIEFyYml0cmFyeS4gVXNlIHRoZSBjb25maWd1cmF0aW9uIGBpbnB1dFNoYXBlYCB3aGVuIHVzaW5nIHRoaXMgbGF5ZXIgYXMgdGhlXG4gKiAgIGZpcnN0IGxheWVyIGluIGEgbW9kZWwuXG4gKlxuICogT3V0cHV0IHNoYXBlOlxuICogICBTYW1lIHNoYXBlIGFzIHRoZSBpbnB1dC5cbiAqXG4gKiBAZG9jIHtcbiAqICAgaGVhZGluZzogJ0xheWVycycsXG4gKiAgIHN1YmhlYWRpbmc6ICdBZHZhbmNlZCBBY3RpdmF0aW9uJyxcbiAqICAgbmFtZXNwYWNlOiAnbGF5ZXJzJ1xuICogfVxuICovXG5leHBvcnQgZnVuY3Rpb24gcHJlbHUoYXJncz86IFBSZUxVTGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgUFJlTFUoYXJncyk7XG59XG5cbi8qKlxuICogU29mdG1heCBhY3RpdmF0aW9uIGxheWVyLlxuICpcbiAqIElucHV0IHNoYXBlOlxuICogICBBcmJpdHJhcnkuIFVzZSB0aGUgY29uZmlndXJhdGlvbiBgaW5wdXRTaGFwZWAgd2hlbiB1c2luZyB0aGlzIGxheWVyIGFzIHRoZVxuICogICBmaXJzdCBsYXllciBpbiBhIG1vZGVsLlxuICpcbiAqIE91dHB1dCBzaGFwZTpcbiAqICAgU2FtZSBzaGFwZSBhcyB0aGUgaW5wdXQuXG4gKlxuICogQGRvYyB7XG4gKiAgIGhlYWRpbmc6ICdMYXllcnMnLFxuICogICBzdWJoZWFkaW5nOiAnQWR2YW5jZWQgQWN0aXZhdGlvbicsXG4gKiAgIG5hbWVzcGFjZTogJ2xheWVycydcbiAqIH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNvZnRtYXgoYXJncz86IFNvZnRtYXhMYXllckFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBTb2Z0bWF4KGFyZ3MpO1xufVxuXG4vKipcbiAqIFRocmVzaG9sZGVkIFJlY3RpZmllZCBMaW5lYXIgVW5pdC5cbiAqXG4gKiBJdCBmb2xsb3dzOlxuICogYGYoeCkgPSB4IGZvciB4ID4gdGhldGFgLFxuICogYGYoeCkgPSAwIG90aGVyd2lzZWAuXG4gKlxuICogSW5wdXQgc2hhcGU6XG4gKiAgIEFyYml0cmFyeS4gVXNlIHRoZSBjb25maWd1cmF0aW9uIGBpbnB1dFNoYXBlYCB3aGVuIHVzaW5nIHRoaXMgbGF5ZXIgYXMgdGhlXG4gKiAgIGZpcnN0IGxheWVyIGluIGEgbW9kZWwuXG4gKlxuICogT3V0cHV0IHNoYXBlOlxuICogICBTYW1lIHNoYXBlIGFzIHRoZSBpbnB1dC5cbiAqXG4gKiBSZWZlcmVuY2VzOlxuICogICAtIFtaZXJvLUJpYXMgQXV0b2VuY29kZXJzIGFuZCB0aGUgQmVuZWZpdHMgb2YgQ28tQWRhcHRpbmdcbiAqIEZlYXR1cmVzXShodHRwOi8vYXJ4aXYub3JnL2Ficy8xNDAyLjMzMzcpXG4gKlxuICogQGRvYyB7XG4gKiAgIGhlYWRpbmc6ICdMYXllcnMnLFxuICogICBzdWJoZWFkaW5nOiAnQWR2YW5jZWQgQWN0aXZhdGlvbicsXG4gKiAgIG5hbWVzcGFjZTogJ2xheWVycydcbiAqIH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHRocmVzaG9sZGVkUmVMVShhcmdzPzogVGhyZXNob2xkZWRSZUxVTGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgVGhyZXNob2xkZWRSZUxVKGFyZ3MpO1xufVxuXG4vLyBDb252b2x1dGlvbmFsIExheWVycy5cblxuLyoqXG4gKiAxRCBjb252b2x1dGlvbiBsYXllciAoZS5nLiwgdGVtcG9yYWwgY29udm9sdXRpb24pLlxuICpcbiAqIFRoaXMgbGF5ZXIgY3JlYXRlcyBhIGNvbnZvbHV0aW9uIGtlcm5lbCB0aGF0IGlzIGNvbnZvbHZlZFxuICogd2l0aCB0aGUgbGF5ZXIgaW5wdXQgb3ZlciBhIHNpbmdsZSBzcGF0aWFsIChvciB0ZW1wb3JhbCkgZGltZW5zaW9uXG4gKiB0byBwcm9kdWNlIGEgdGVuc29yIG9mIG91dHB1dHMuXG4gKlxuICogSWYgYHVzZV9iaWFzYCBpcyBUcnVlLCBhIGJpYXMgdmVjdG9yIGlzIGNyZWF0ZWQgYW5kIGFkZGVkIHRvIHRoZSBvdXRwdXRzLlxuICpcbiAqIElmIGBhY3RpdmF0aW9uYCBpcyBub3QgYG51bGxgLCBpdCBpcyBhcHBsaWVkIHRvIHRoZSBvdXRwdXRzIGFzIHdlbGwuXG4gKlxuICogV2hlbiB1c2luZyB0aGlzIGxheWVyIGFzIHRoZSBmaXJzdCBsYXllciBpbiBhIG1vZGVsLCBwcm92aWRlIGFuXG4gKiBgaW5wdXRTaGFwZWAgYXJndW1lbnQgYEFycmF5YCBvciBgbnVsbGAuXG4gKlxuICogRm9yIGV4YW1wbGUsIGBpbnB1dFNoYXBlYCB3b3VsZCBiZTpcbiAqIC0gYFsxMCwgMTI4XWAgZm9yIHNlcXVlbmNlcyBvZiAxMCB2ZWN0b3JzIG9mIDEyOC1kaW1lbnNpb25hbCB2ZWN0b3JzXG4gKiAtIGBbbnVsbCwgMTI4XWAgZm9yIHZhcmlhYmxlLWxlbmd0aCBzZXF1ZW5jZXMgb2YgMTI4LWRpbWVuc2lvbmFsIHZlY3RvcnMuXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdDb252b2x1dGlvbmFsJywgIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb252MWQoYXJnczogQ29udkxheWVyQXJncykge1xuICByZXR1cm4gbmV3IENvbnYxRChhcmdzKTtcbn1cblxuLyoqXG4gKiAyRCBjb252b2x1dGlvbiBsYXllciAoZS5nLiBzcGF0aWFsIGNvbnZvbHV0aW9uIG92ZXIgaW1hZ2VzKS5cbiAqXG4gKiBUaGlzIGxheWVyIGNyZWF0ZXMgYSBjb252b2x1dGlvbiBrZXJuZWwgdGhhdCBpcyBjb252b2x2ZWRcbiAqIHdpdGggdGhlIGxheWVyIGlucHV0IHRvIHByb2R1Y2UgYSB0ZW5zb3Igb2Ygb3V0cHV0cy5cbiAqXG4gKiBJZiBgdXNlQmlhc2AgaXMgVHJ1ZSwgYSBiaWFzIHZlY3RvciBpcyBjcmVhdGVkIGFuZCBhZGRlZCB0byB0aGUgb3V0cHV0cy5cbiAqXG4gKiBJZiBgYWN0aXZhdGlvbmAgaXMgbm90IGBudWxsYCwgaXQgaXMgYXBwbGllZCB0byB0aGUgb3V0cHV0cyBhcyB3ZWxsLlxuICpcbiAqIFdoZW4gdXNpbmcgdGhpcyBsYXllciBhcyB0aGUgZmlyc3QgbGF5ZXIgaW4gYSBtb2RlbCxcbiAqIHByb3ZpZGUgdGhlIGtleXdvcmQgYXJndW1lbnQgYGlucHV0U2hhcGVgXG4gKiAoQXJyYXkgb2YgaW50ZWdlcnMsIGRvZXMgbm90IGluY2x1ZGUgdGhlIHNhbXBsZSBheGlzKSxcbiAqIGUuZy4gYGlucHV0U2hhcGU9WzEyOCwgMTI4LCAzXWAgZm9yIDEyOHgxMjggUkdCIHBpY3R1cmVzXG4gKiBpbiBgZGF0YUZvcm1hdD0nY2hhbm5lbHNMYXN0J2AuXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdDb252b2x1dGlvbmFsJywgbmFtZXNwYWNlOiAnbGF5ZXJzJ31cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNvbnYyZChhcmdzOiBDb252TGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgQ29udjJEKGFyZ3MpO1xufVxuXG4vKipcbiAqIFRyYW5zcG9zZWQgY29udm9sdXRpb25hbCBsYXllciAoc29tZXRpbWVzIGNhbGxlZCBEZWNvbnZvbHV0aW9uKS5cbiAqXG4gKiBUaGUgbmVlZCBmb3IgdHJhbnNwb3NlZCBjb252b2x1dGlvbnMgZ2VuZXJhbGx5IGFyaXNlc1xuICogZnJvbSB0aGUgZGVzaXJlIHRvIHVzZSBhIHRyYW5zZm9ybWF0aW9uIGdvaW5nIGluIHRoZSBvcHBvc2l0ZSBkaXJlY3Rpb24gb2ZcbiAqIGEgbm9ybWFsIGNvbnZvbHV0aW9uLCBpLmUuLCBmcm9tIHNvbWV0aGluZyB0aGF0IGhhcyB0aGUgc2hhcGUgb2YgdGhlIG91dHB1dFxuICogb2Ygc29tZSBjb252b2x1dGlvbiB0byBzb21ldGhpbmcgdGhhdCBoYXMgdGhlIHNoYXBlIG9mIGl0cyBpbnB1dCB3aGlsZVxuICogbWFpbnRhaW5pbmcgYSBjb25uZWN0aXZpdHkgcGF0dGVybiB0aGF0IGlzIGNvbXBhdGlibGUgd2l0aCBzYWlkXG4gKiBjb252b2x1dGlvbi5cbiAqXG4gKiBXaGVuIHVzaW5nIHRoaXMgbGF5ZXIgYXMgdGhlIGZpcnN0IGxheWVyIGluIGEgbW9kZWwsIHByb3ZpZGUgdGhlXG4gKiBjb25maWd1cmF0aW9uIGBpbnB1dFNoYXBlYCAoYEFycmF5YCBvZiBpbnRlZ2VycywgZG9lcyBub3QgaW5jbHVkZSB0aGVcbiAqIHNhbXBsZSBheGlzKSwgZS5nLiwgYGlucHV0U2hhcGU6IFsxMjgsIDEyOCwgM11gIGZvciAxMjh4MTI4IFJHQiBwaWN0dXJlcyBpblxuICogYGRhdGFGb3JtYXQ6ICdjaGFubmVsc0xhc3QnYC5cbiAqXG4gKiBJbnB1dCBzaGFwZTpcbiAqICAgNEQgdGVuc29yIHdpdGggc2hhcGU6XG4gKiAgIGBbYmF0Y2gsIGNoYW5uZWxzLCByb3dzLCBjb2xzXWAgaWYgYGRhdGFGb3JtYXRgIGlzIGAnY2hhbm5lbHNGaXJzdCdgLlxuICogICBvciA0RCB0ZW5zb3Igd2l0aCBzaGFwZVxuICogICBgW2JhdGNoLCByb3dzLCBjb2xzLCBjaGFubmVsc11gIGlmIGBkYXRhRm9ybWF0YCBpcyBgJ2NoYW5uZWxzTGFzdCdgLlxuICpcbiAqIE91dHB1dCBzaGFwZTpcbiAqICAgNEQgdGVuc29yIHdpdGggc2hhcGU6XG4gKiAgIGBbYmF0Y2gsIGZpbHRlcnMsIG5ld1Jvd3MsIG5ld0NvbHNdYCBpZiBgZGF0YUZvcm1hdGAgaXNcbiAqIGAnY2hhbm5lbHNGaXJzdCdgLiBvciA0RCB0ZW5zb3Igd2l0aCBzaGFwZTpcbiAqICAgYFtiYXRjaCwgbmV3Um93cywgbmV3Q29scywgZmlsdGVyc11gIGlmIGBkYXRhRm9ybWF0YCBpcyBgJ2NoYW5uZWxzTGFzdCdgLlxuICpcbiAqIFJlZmVyZW5jZXM6XG4gKiAgIC0gW0EgZ3VpZGUgdG8gY29udm9sdXRpb24gYXJpdGhtZXRpYyBmb3IgZGVlcFxuICogbGVhcm5pbmddKGh0dHBzOi8vYXJ4aXYub3JnL2Ficy8xNjAzLjA3Mjg1djEpXG4gKiAgIC0gW0RlY29udm9sdXRpb25hbFxuICogTmV0d29ya3NdKGh0dHA6Ly93d3cubWF0dGhld3plaWxlci5jb20vcHVicy9jdnByMjAxMC9jdnByMjAxMC5wZGYpXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdDb252b2x1dGlvbmFsJywgbmFtZXNwYWNlOiAnbGF5ZXJzJ31cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNvbnYyZFRyYW5zcG9zZShhcmdzOiBDb252TGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgQ29udjJEVHJhbnNwb3NlKGFyZ3MpO1xufVxuXG4vKipcbiAqIDNEIGNvbnZvbHV0aW9uIGxheWVyIChlLmcuIHNwYXRpYWwgY29udm9sdXRpb24gb3ZlciB2b2x1bWVzKS5cbiAqXG4gKiBUaGlzIGxheWVyIGNyZWF0ZXMgYSBjb252b2x1dGlvbiBrZXJuZWwgdGhhdCBpcyBjb252b2x2ZWRcbiAqIHdpdGggdGhlIGxheWVyIGlucHV0IHRvIHByb2R1Y2UgYSB0ZW5zb3Igb2Ygb3V0cHV0cy5cbiAqXG4gKiBJZiBgdXNlQmlhc2AgaXMgVHJ1ZSwgYSBiaWFzIHZlY3RvciBpcyBjcmVhdGVkIGFuZCBhZGRlZCB0byB0aGUgb3V0cHV0cy5cbiAqXG4gKiBJZiBgYWN0aXZhdGlvbmAgaXMgbm90IGBudWxsYCwgaXQgaXMgYXBwbGllZCB0byB0aGUgb3V0cHV0cyBhcyB3ZWxsLlxuICpcbiAqIFdoZW4gdXNpbmcgdGhpcyBsYXllciBhcyB0aGUgZmlyc3QgbGF5ZXIgaW4gYSBtb2RlbCxcbiAqIHByb3ZpZGUgdGhlIGtleXdvcmQgYXJndW1lbnQgYGlucHV0U2hhcGVgXG4gKiAoQXJyYXkgb2YgaW50ZWdlcnMsIGRvZXMgbm90IGluY2x1ZGUgdGhlIHNhbXBsZSBheGlzKSxcbiAqIGUuZy4gYGlucHV0U2hhcGU9WzEyOCwgMTI4LCAxMjgsIDFdYCBmb3IgMTI4eDEyOHgxMjggZ3JheXNjYWxlIHZvbHVtZXNcbiAqIGluIGBkYXRhRm9ybWF0PSdjaGFubmVsc0xhc3QnYC5cbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ0NvbnZvbHV0aW9uYWwnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gY29udjNkKGFyZ3M6IENvbnZMYXllckFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBDb252M0QoYXJncyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjb252M2RUcmFuc3Bvc2UoYXJnczogQ29udkxheWVyQXJncyk6IExheWVyIHtcbiAgcmV0dXJuIG5ldyBDb252M0RUcmFuc3Bvc2UoYXJncyk7XG59XG5cbi8qKlxuICogRGVwdGh3aXNlIHNlcGFyYWJsZSAyRCBjb252b2x1dGlvbi5cbiAqXG4gKiBTZXBhcmFibGUgY29udm9sdXRpb24gY29uc2lzdHMgb2YgZmlyc3QgcGVyZm9ybWluZ1xuICogYSBkZXB0aHdpc2Ugc3BhdGlhbCBjb252b2x1dGlvblxuICogKHdoaWNoIGFjdHMgb24gZWFjaCBpbnB1dCBjaGFubmVsIHNlcGFyYXRlbHkpXG4gKiBmb2xsb3dlZCBieSBhIHBvaW50d2lzZSBjb252b2x1dGlvbiB3aGljaCBtaXhlcyB0b2dldGhlciB0aGUgcmVzdWx0aW5nXG4gKiBvdXRwdXQgY2hhbm5lbHMuIFRoZSBgZGVwdGhNdWx0aXBsaWVyYCBhcmd1bWVudCBjb250cm9scyBob3cgbWFueVxuICogb3V0cHV0IGNoYW5uZWxzIGFyZSBnZW5lcmF0ZWQgcGVyIGlucHV0IGNoYW5uZWwgaW4gdGhlIGRlcHRod2lzZSBzdGVwLlxuICpcbiAqIEludHVpdGl2ZWx5LCBzZXBhcmFibGUgY29udm9sdXRpb25zIGNhbiBiZSB1bmRlcnN0b29kIGFzXG4gKiBhIHdheSB0byBmYWN0b3JpemUgYSBjb252b2x1dGlvbiBrZXJuZWwgaW50byB0d28gc21hbGxlciBrZXJuZWxzLFxuICogb3IgYXMgYW4gZXh0cmVtZSB2ZXJzaW9uIG9mIGFuIEluY2VwdGlvbiBibG9jay5cbiAqXG4gKiBJbnB1dCBzaGFwZTpcbiAqICAgNEQgdGVuc29yIHdpdGggc2hhcGU6XG4gKiAgICAgYFtiYXRjaCwgY2hhbm5lbHMsIHJvd3MsIGNvbHNdYCBpZiBkYXRhX2Zvcm1hdD0nY2hhbm5lbHNGaXJzdCdcbiAqICAgb3IgNEQgdGVuc29yIHdpdGggc2hhcGU6XG4gKiAgICAgYFtiYXRjaCwgcm93cywgY29scywgY2hhbm5lbHNdYCBpZiBkYXRhX2Zvcm1hdD0nY2hhbm5lbHNMYXN0Jy5cbiAqXG4gKiBPdXRwdXQgc2hhcGU6XG4gKiAgIDREIHRlbnNvciB3aXRoIHNoYXBlOlxuICogICAgIGBbYmF0Y2gsIGZpbHRlcnMsIG5ld1Jvd3MsIG5ld0NvbHNdYCBpZiBkYXRhX2Zvcm1hdD0nY2hhbm5lbHNGaXJzdCdcbiAqICAgb3IgNEQgdGVuc29yIHdpdGggc2hhcGU6XG4gKiAgICAgYFtiYXRjaCwgbmV3Um93cywgbmV3Q29scywgZmlsdGVyc11gIGlmIGRhdGFfZm9ybWF0PSdjaGFubmVsc0xhc3QnLlxuICogICAgIGByb3dzYCBhbmQgYGNvbHNgIHZhbHVlcyBtaWdodCBoYXZlIGNoYW5nZWQgZHVlIHRvIHBhZGRpbmcuXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdDb252b2x1dGlvbmFsJywgbmFtZXNwYWNlOiAnbGF5ZXJzJ31cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNlcGFyYWJsZUNvbnYyZChhcmdzOiBTZXBhcmFibGVDb252TGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgU2VwYXJhYmxlQ29udjJEKGFyZ3MpO1xufVxuXG4vKipcbiAqIENyb3BwaW5nIGxheWVyIGZvciAyRCBpbnB1dCAoZS5nLiwgaW1hZ2UpLlxuICpcbiAqIFRoaXMgbGF5ZXIgY2FuIGNyb3AgYW4gaW5wdXRcbiAqIGF0IHRoZSB0b3AsIGJvdHRvbSwgbGVmdCBhbmQgcmlnaHQgc2lkZSBvZiBhbiBpbWFnZSB0ZW5zb3IuXG4gKlxuICogSW5wdXQgc2hhcGU6XG4gKiAgIDREIHRlbnNvciB3aXRoIHNoYXBlOlxuICogICAtIElmIGBkYXRhRm9ybWF0YCBpcyBgXCJjaGFubmVsc0xhc3RcImA6XG4gKiAgICAgYFtiYXRjaCwgcm93cywgY29scywgY2hhbm5lbHNdYFxuICogICAtIElmIGBkYXRhX2Zvcm1hdGAgaXMgYFwiY2hhbm5lbHNfZmlyc3RcImA6XG4gKiAgICAgYFtiYXRjaCwgY2hhbm5lbHMsIHJvd3MsIGNvbHNdYC5cbiAqXG4gKiBPdXRwdXQgc2hhcGU6XG4gKiAgIDREIHdpdGggc2hhcGU6XG4gKiAgIC0gSWYgYGRhdGFGb3JtYXRgIGlzIGBcImNoYW5uZWxzTGFzdFwiYDpcbiAqICAgICBgW2JhdGNoLCBjcm9wcGVkUm93cywgY3JvcHBlZENvbHMsIGNoYW5uZWxzXWBcbiAqICAgIC0gSWYgYGRhdGFGb3JtYXRgIGlzIGBcImNoYW5uZWxzRmlyc3RcImA6XG4gKiAgICAgYFtiYXRjaCwgY2hhbm5lbHMsIGNyb3BwZWRSb3dzLCBjcm9wcGVkQ29sc11gLlxuICpcbiAqIEV4YW1wbGVzXG4gKiBgYGBqc1xuICpcbiAqIGNvbnN0IG1vZGVsID0gdGYuc2VxdWVudGlhbCgpO1xuICogbW9kZWwuYWRkKHRmLmxheWVycy5jcm9wcGluZzJEKHtjcm9wcGluZzpbWzIsIDJdLCBbMiwgMl1dLFxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlucHV0U2hhcGU6IFsxMjgsIDEyOCwgM119KSk7XG4gKiAvL25vdyBvdXRwdXQgc2hhcGUgaXMgW2JhdGNoLCAxMjQsIDEyNCwgM11cbiAqIGBgYFxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdMYXllcnMnLCBzdWJoZWFkaW5nOiAnQ29udm9sdXRpb25hbCcsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcm9wcGluZzJEKGFyZ3M6IENyb3BwaW5nMkRMYXllckFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBDcm9wcGluZzJEKGFyZ3MpO1xufVxuXG4vKipcbiAqIFVwc2FtcGxpbmcgbGF5ZXIgZm9yIDJEIGlucHV0cy5cbiAqXG4gKiBSZXBlYXRzIHRoZSByb3dzIGFuZCBjb2x1bW5zIG9mIHRoZSBkYXRhXG4gKiBieSBzaXplWzBdIGFuZCBzaXplWzFdIHJlc3BlY3RpdmVseS5cbiAqXG4gKlxuICogSW5wdXQgc2hhcGU6XG4gKiAgICA0RCB0ZW5zb3Igd2l0aCBzaGFwZTpcbiAqICAgICAtIElmIGBkYXRhRm9ybWF0YCBpcyBgXCJjaGFubmVsc0xhc3RcImA6XG4gKiAgICAgICAgIGBbYmF0Y2gsIHJvd3MsIGNvbHMsIGNoYW5uZWxzXWBcbiAqICAgICAtIElmIGBkYXRhRm9ybWF0YCBpcyBgXCJjaGFubmVsc0ZpcnN0XCJgOlxuICogICAgICAgIGBbYmF0Y2gsIGNoYW5uZWxzLCByb3dzLCBjb2xzXWBcbiAqXG4gKiBPdXRwdXQgc2hhcGU6XG4gKiAgICAgNEQgdGVuc29yIHdpdGggc2hhcGU6XG4gKiAgICAgLSBJZiBgZGF0YUZvcm1hdGAgaXMgYFwiY2hhbm5lbHNMYXN0XCJgOlxuICogICAgICAgIGBbYmF0Y2gsIHVwc2FtcGxlZFJvd3MsIHVwc2FtcGxlZENvbHMsIGNoYW5uZWxzXWBcbiAqICAgICAtIElmIGBkYXRhRm9ybWF0YCBpcyBgXCJjaGFubmVsc0ZpcnN0XCJgOlxuICogICAgICAgICBgW2JhdGNoLCBjaGFubmVscywgdXBzYW1wbGVkUm93cywgdXBzYW1wbGVkQ29sc11gXG4gKlxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdMYXllcnMnLCBzdWJoZWFkaW5nOiAnQ29udm9sdXRpb25hbCcsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB1cFNhbXBsaW5nMmQoYXJnczogVXBTYW1wbGluZzJETGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgVXBTYW1wbGluZzJEKGFyZ3MpO1xufVxuXG4vLyBDb252b2x1dGlvbmFsKGRlcHRod2lzZSkgTGF5ZXJzLlxuXG4vKipcbiAqIERlcHRod2lzZSBzZXBhcmFibGUgMkQgY29udm9sdXRpb24uXG4gKlxuICogRGVwdGh3aXNlIFNlcGFyYWJsZSBjb252b2x1dGlvbnMgY29uc2lzdHMgaW4gcGVyZm9ybWluZyBqdXN0IHRoZSBmaXJzdCBzdGVwXG4gKiBpbiBhIGRlcHRod2lzZSBzcGF0aWFsIGNvbnZvbHV0aW9uICh3aGljaCBhY3RzIG9uIGVhY2ggaW5wdXQgY2hhbm5lbFxuICogc2VwYXJhdGVseSkuIFRoZSBgZGVwdGhNdWx0aXBsaWVyYCBhcmd1bWVudCBjb250cm9scyBob3cgbWFueSBvdXRwdXQgY2hhbm5lbHNcbiAqIGFyZSBnZW5lcmF0ZWQgcGVyIGlucHV0IGNoYW5uZWwgaW4gdGhlIGRlcHRod2lzZSBzdGVwLlxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdMYXllcnMnLCBzdWJoZWFkaW5nOiAnQ29udm9sdXRpb25hbCcsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkZXB0aHdpc2VDb252MmQoYXJnczogRGVwdGh3aXNlQ29udjJETGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgRGVwdGh3aXNlQ29udjJEKGFyZ3MpO1xufVxuXG4vLyBCYXNpYyBMYXllcnMuXG5cbi8qKlxuICogQXBwbGllcyBhbiBhY3RpdmF0aW9uIGZ1bmN0aW9uIHRvIGFuIG91dHB1dC5cbiAqXG4gKiBUaGlzIGxheWVyIGFwcGxpZXMgZWxlbWVudC13aXNlIGFjdGl2YXRpb24gZnVuY3Rpb24uICBPdGhlciBsYXllcnMsIG5vdGFibHlcbiAqIGBkZW5zZWAgY2FuIGFsc28gYXBwbHkgYWN0aXZhdGlvbiBmdW5jdGlvbnMuICBVc2UgdGhpcyBpc29sYXRlZCBhY3RpdmF0aW9uXG4gKiBmdW5jdGlvbiB0byBleHRyYWN0IHRoZSB2YWx1ZXMgYmVmb3JlIGFuZCBhZnRlciB0aGVcbiAqIGFjdGl2YXRpb24uIEZvciBpbnN0YW5jZTpcbiAqXG4gKiBgYGBqc1xuICogY29uc3QgaW5wdXQgPSB0Zi5pbnB1dCh7c2hhcGU6IFs1XX0pO1xuICogY29uc3QgZGVuc2VMYXllciA9IHRmLmxheWVycy5kZW5zZSh7dW5pdHM6IDF9KTtcbiAqIGNvbnN0IGFjdGl2YXRpb25MYXllciA9IHRmLmxheWVycy5hY3RpdmF0aW9uKHthY3RpdmF0aW9uOiAncmVsdTYnfSk7XG4gKlxuICogLy8gT2J0YWluIHRoZSBvdXRwdXQgc3ltYm9saWMgdGVuc29ycyBieSBhcHBseWluZyB0aGUgbGF5ZXJzIGluIG9yZGVyLlxuICogY29uc3QgZGVuc2VPdXRwdXQgPSBkZW5zZUxheWVyLmFwcGx5KGlucHV0KTtcbiAqIGNvbnN0IGFjdGl2YXRpb25PdXRwdXQgPSBhY3RpdmF0aW9uTGF5ZXIuYXBwbHkoZGVuc2VPdXRwdXQpO1xuICpcbiAqIC8vIENyZWF0ZSB0aGUgbW9kZWwgYmFzZWQgb24gdGhlIGlucHV0cy5cbiAqIGNvbnN0IG1vZGVsID0gdGYubW9kZWwoe1xuICogICAgIGlucHV0czogaW5wdXQsXG4gKiAgICAgb3V0cHV0czogW2RlbnNlT3V0cHV0LCBhY3RpdmF0aW9uT3V0cHV0XVxuICogfSk7XG4gKlxuICogLy8gQ29sbGVjdCBib3RoIG91dHB1dHMgYW5kIHByaW50IHNlcGFyYXRlbHkuXG4gKiBjb25zdCBbZGVuc2VPdXQsIGFjdGl2YXRpb25PdXRdID0gbW9kZWwucHJlZGljdCh0Zi5yYW5kb21Ob3JtYWwoWzYsIDVdKSk7XG4gKiBkZW5zZU91dC5wcmludCgpO1xuICogYWN0aXZhdGlvbk91dC5wcmludCgpO1xuICogYGBgXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdCYXNpYycsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhY3RpdmF0aW9uKGFyZ3M6IEFjdGl2YXRpb25MYXllckFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBBY3RpdmF0aW9uKGFyZ3MpO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYSBkZW5zZSAoZnVsbHkgY29ubmVjdGVkKSBsYXllci5cbiAqXG4gKiBUaGlzIGxheWVyIGltcGxlbWVudHMgdGhlIG9wZXJhdGlvbjpcbiAqICAgYG91dHB1dCA9IGFjdGl2YXRpb24oZG90KGlucHV0LCBrZXJuZWwpICsgYmlhcylgXG4gKlxuICogYGFjdGl2YXRpb25gIGlzIHRoZSBlbGVtZW50LXdpc2UgYWN0aXZhdGlvbiBmdW5jdGlvblxuICogICBwYXNzZWQgYXMgdGhlIGBhY3RpdmF0aW9uYCBhcmd1bWVudC5cbiAqXG4gKiBga2VybmVsYCBpcyBhIHdlaWdodHMgbWF0cml4IGNyZWF0ZWQgYnkgdGhlIGxheWVyLlxuICpcbiAqIGBiaWFzYCBpcyBhIGJpYXMgdmVjdG9yIGNyZWF0ZWQgYnkgdGhlIGxheWVyIChvbmx5IGFwcGxpY2FibGUgaWYgYHVzZUJpYXNgXG4gKiBpcyBgdHJ1ZWApLlxuICpcbiAqICoqSW5wdXQgc2hhcGU6KipcbiAqXG4gKiAgIG5EIGB0Zi5UZW5zb3JgIHdpdGggc2hhcGU6IGAoYmF0Y2hTaXplLCAuLi4sIGlucHV0RGltKWAuXG4gKlxuICogICBUaGUgbW9zdCBjb21tb24gc2l0dWF0aW9uIHdvdWxkIGJlXG4gKiAgIGEgMkQgaW5wdXQgd2l0aCBzaGFwZSBgKGJhdGNoU2l6ZSwgaW5wdXREaW0pYC5cbiAqXG4gKiAqKk91dHB1dCBzaGFwZToqKlxuICpcbiAqICAgbkQgdGVuc29yIHdpdGggc2hhcGU6IGAoYmF0Y2hTaXplLCAuLi4sIHVuaXRzKWAuXG4gKlxuICogICBGb3IgaW5zdGFuY2UsIGZvciBhIDJEIGlucHV0IHdpdGggc2hhcGUgYChiYXRjaFNpemUsIGlucHV0RGltKWAsXG4gKiAgIHRoZSBvdXRwdXQgd291bGQgaGF2ZSBzaGFwZSBgKGJhdGNoU2l6ZSwgdW5pdHMpYC5cbiAqXG4gKiBOb3RlOiBpZiB0aGUgaW5wdXQgdG8gdGhlIGxheWVyIGhhcyBhIHJhbmsgZ3JlYXRlciB0aGFuIDIsIHRoZW4gaXQgaXNcbiAqIGZsYXR0ZW5lZCBwcmlvciB0byB0aGUgaW5pdGlhbCBkb3QgcHJvZHVjdCB3aXRoIHRoZSBrZXJuZWwuXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdCYXNpYycsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkZW5zZShhcmdzOiBEZW5zZUxheWVyQXJncykge1xuICByZXR1cm4gbmV3IERlbnNlKGFyZ3MpO1xufVxuXG4vKipcbiAqIEFwcGxpZXNcbiAqIFtkcm9wb3V0XShodHRwOi8vd3d3LmNzLnRvcm9udG8uZWR1L35yc2FsYWtodS9wYXBlcnMvc3JpdmFzdGF2YTE0YS5wZGYpIHRvXG4gKiB0aGUgaW5wdXQuXG4gKlxuICogRHJvcG91dCBjb25zaXN0cyBpbiByYW5kb21seSBzZXR0aW5nIGEgZnJhY3Rpb24gYHJhdGVgIG9mIGlucHV0IHVuaXRzIHRvIDAgYXRcbiAqIGVhY2ggdXBkYXRlIGR1cmluZyB0cmFpbmluZyB0aW1lLCB3aGljaCBoZWxwcyBwcmV2ZW50IG92ZXJmaXR0aW5nLlxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdMYXllcnMnLCBzdWJoZWFkaW5nOiAnQmFzaWMnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gZHJvcG91dChhcmdzOiBEcm9wb3V0TGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgRHJvcG91dChhcmdzKTtcbn1cblxuLyoqXG4gKiBTcGF0aWFsIDFEIHZlcnNpb24gb2YgRHJvcG91dC5cbiAqXG4gKiBUaGlzIExheWVyIHR5cGUgcGVyZm9ybXMgdGhlIHNhbWUgZnVuY3Rpb24gYXMgdGhlIERyb3BvdXQgbGF5ZXIsIGJ1dCBpdCBkcm9wc1xuICogZW50aXJlIDFEIGZlYXR1cmUgbWFwcyBpbnN0ZWFkIG9mIGluZGl2aWR1YWwgZWxlbWVudHMuIEZvciBleGFtcGxlLCBpZiBhblxuICogaW5wdXQgZXhhbXBsZSBjb25zaXN0cyBvZiAzIHRpbWVzdGVwcyBhbmQgdGhlIGZlYXR1cmUgbWFwIGZvciBlYWNoIHRpbWVzdGVwXG4gKiBoYXMgYSBzaXplIG9mIDQsIGEgYHNwYXRpYWxEcm9wb3V0MWRgIGxheWVyIG1heSB6ZXJvIG91dCB0aGUgZmVhdHVyZSBtYXBzXG4gKiBvZiB0aGUgMXN0IHRpbWVzdGVwcyBhbmQgMm5kIHRpbWVzdGVwcyBjb21wbGV0ZWx5IHdoaWxlIHNwYXJpbmcgYWxsIGZlYXR1cmVcbiAqIGVsZW1lbnRzIG9mIHRoZSAzcmQgdGltZXN0ZXAuXG4gKlxuICogSWYgYWRqYWNlbnQgZnJhbWVzICh0aW1lc3RlcHMpIGFyZSBzdHJvbmdseSBjb3JyZWxhdGVkIChhcyBpcyBub3JtYWxseSB0aGVcbiAqIGNhc2UgaW4gZWFybHkgY29udm9sdXRpb24gbGF5ZXJzKSwgcmVndWxhciBkcm9wb3V0IHdpbGwgbm90IHJlZ3VsYXJpemUgdGhlXG4gKiBhY3RpdmF0aW9uIGFuZCB3aWxsIG90aGVyd2lzZSBqdXN0IHJlc3VsdCBpbiBtZXJlbHkgYW4gZWZmZWN0aXZlIGxlYXJuaW5nXG4gKiByYXRlIGRlY3JlYXNlLiBJbiB0aGlzIGNhc2UsIGBzcGF0aWFsRHJvcG91dDFkYCB3aWxsIGhlbHAgcHJvbW90ZVxuICogaW5kZXBlbmRlbmNlIGFtb25nIGZlYXR1cmUgbWFwcyBhbmQgc2hvdWxkIGJlIHVzZWQgaW5zdGVhZC5cbiAqXG4gKiAqKkFyZ3VtZW50czoqKlxuICogICByYXRlOiBBIGZsb2F0aW5nLXBvaW50IG51bWJlciA+PTAgYW5kIDw9MS4gRnJhY3Rpb24gb2YgdGhlIGlucHV0IGVsZW1lbnRzXG4gKiAgICAgdG8gZHJvcC5cbiAqXG4gKiAqKklucHV0IHNoYXBlOioqXG4gKiAgIDNEIHRlbnNvciB3aXRoIHNoYXBlIGAoc2FtcGxlcywgdGltZXN0ZXBzLCBjaGFubmVscylgLlxuICpcbiAqICoqT3V0cHV0IHNoYXBlOioqXG4gKiAgIFNhbWUgYXMgdGhlIGlucHV0IHNoYXBlLlxuICpcbiAqIFJlZmVyZW5jZXM6XG4gKiAgIC0gW0VmZmljaWVudCBPYmplY3QgTG9jYWxpemF0aW9uIFVzaW5nIENvbnZvbHV0aW9uYWxcbiAqICAgICAgTmV0d29ya3NdKGh0dHBzOi8vYXJ4aXYub3JnL2Ficy8xNDExLjQyODApXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdCYXNpYycsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzcGF0aWFsRHJvcG91dDFkKGFyZ3M6IFNwYXRpYWxEcm9wb3V0MURMYXllckNvbmZpZykge1xuICByZXR1cm4gbmV3IFNwYXRpYWxEcm9wb3V0MUQoYXJncyk7XG59XG5cbi8qKlxuICogRmxhdHRlbnMgdGhlIGlucHV0LiBEb2VzIG5vdCBhZmZlY3QgdGhlIGJhdGNoIHNpemUuXG4gKlxuICogQSBgRmxhdHRlbmAgbGF5ZXIgZmxhdHRlbnMgZWFjaCBiYXRjaCBpbiBpdHMgaW5wdXRzIHRvIDFEIChtYWtpbmcgdGhlIG91dHB1dFxuICogMkQpLlxuICpcbiAqIEZvciBleGFtcGxlOlxuICpcbiAqIGBgYGpzXG4gKiBjb25zdCBpbnB1dCA9IHRmLmlucHV0KHtzaGFwZTogWzQsIDNdfSk7XG4gKiBjb25zdCBmbGF0dGVuTGF5ZXIgPSB0Zi5sYXllcnMuZmxhdHRlbigpO1xuICogLy8gSW5zcGVjdCB0aGUgaW5mZXJyZWQgb3V0cHV0IHNoYXBlIG9mIHRoZSBmbGF0dGVuIGxheWVyLCB3aGljaFxuICogLy8gZXF1YWxzIGBbbnVsbCwgMTJdYC4gVGhlIDJuZCBkaW1lbnNpb24gaXMgNCAqIDMsIGkuZS4sIHRoZSByZXN1bHQgb2YgdGhlXG4gKiAvLyBmbGF0dGVuaW5nLiAoVGhlIDFzdCBkaW1lbnNpb24gaXMgdGhlIHVuZGVybWluZWQgYmF0Y2ggc2l6ZS4pXG4gKiBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeShmbGF0dGVuTGF5ZXIuYXBwbHkoaW5wdXQpLnNoYXBlKSk7XG4gKiBgYGBcbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ0Jhc2ljJywgbmFtZXNwYWNlOiAnbGF5ZXJzJ31cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZsYXR0ZW4oYXJncz86IEZsYXR0ZW5MYXllckFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBGbGF0dGVuKGFyZ3MpO1xufVxuXG4vKipcbiAqIFJlcGVhdHMgdGhlIGlucHV0IG4gdGltZXMgaW4gYSBuZXcgZGltZW5zaW9uLlxuICpcbiAqIGBgYGpzXG4gKiAgY29uc3QgbW9kZWwgPSB0Zi5zZXF1ZW50aWFsKCk7XG4gKiAgbW9kZWwuYWRkKHRmLmxheWVycy5yZXBlYXRWZWN0b3Ioe246IDQsIGlucHV0U2hhcGU6IFsyXX0pKTtcbiAqICBjb25zdCB4ID0gdGYudGVuc29yMmQoW1sxMCwgMjBdXSk7XG4gKiAgLy8gVXNlIHRoZSBtb2RlbCB0byBkbyBpbmZlcmVuY2Ugb24gYSBkYXRhIHBvaW50IHRoZSBtb2RlbCBoYXNuJ3Qgc2VlblxuICogIG1vZGVsLnByZWRpY3QoeCkucHJpbnQoKTtcbiAqICAvLyBvdXRwdXQgc2hhcGUgaXMgbm93IFtiYXRjaCwgMiwgNF1cbiAqIGBgYFxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdMYXllcnMnLCBzdWJoZWFkaW5nOiAnQmFzaWMnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVwZWF0VmVjdG9yKGFyZ3M6IFJlcGVhdFZlY3RvckxheWVyQXJncykge1xuICByZXR1cm4gbmV3IFJlcGVhdFZlY3RvcihhcmdzKTtcbn1cblxuLyoqXG4gKiBSZXNoYXBlcyBhbiBpbnB1dCB0byBhIGNlcnRhaW4gc2hhcGUuXG4gKlxuICogYGBganNcbiAqIGNvbnN0IGlucHV0ID0gdGYuaW5wdXQoe3NoYXBlOiBbNCwgM119KTtcbiAqIGNvbnN0IHJlc2hhcGVMYXllciA9IHRmLmxheWVycy5yZXNoYXBlKHt0YXJnZXRTaGFwZTogWzIsIDZdfSk7XG4gKiAvLyBJbnNwZWN0IHRoZSBpbmZlcnJlZCBvdXRwdXQgc2hhcGUgb2YgdGhlIFJlc2hhcGUgbGF5ZXIsIHdoaWNoXG4gKiAvLyBlcXVhbHMgYFtudWxsLCAyLCA2XWAuIChUaGUgMXN0IGRpbWVuc2lvbiBpcyB0aGUgdW5kZXJtaW5lZCBiYXRjaCBzaXplLilcbiAqIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KHJlc2hhcGVMYXllci5hcHBseShpbnB1dCkuc2hhcGUpKTtcbiAqIGBgYFxuICpcbiAqIElucHV0IHNoYXBlOlxuICogICBBcmJpdHJhcnksIGFsdGhvdWdoIGFsbCBkaW1lbnNpb25zIGluIHRoZSBpbnB1dCBzaGFwZSBtdXN0IGJlIGZpeGVkLlxuICogICBVc2UgdGhlIGNvbmZpZ3VyYXRpb24gYGlucHV0U2hhcGVgIHdoZW4gdXNpbmcgdGhpcyBsYXllciBhcyB0aGVcbiAqICAgZmlyc3QgbGF5ZXIgaW4gYSBtb2RlbC5cbiAqXG4gKlxuICogT3V0cHV0IHNoYXBlOlxuICogICBbYmF0Y2hTaXplLCB0YXJnZXRTaGFwZVswXSwgdGFyZ2V0U2hhcGVbMV0sIC4uLixcbiAqICAgIHRhcmdldFNoYXBlW3RhcmdldFNoYXBlLmxlbmd0aCAtIDFdXS5cbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ0Jhc2ljJywgbmFtZXNwYWNlOiAnbGF5ZXJzJ31cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc2hhcGUoYXJnczogUmVzaGFwZUxheWVyQXJncykge1xuICByZXR1cm4gbmV3IFJlc2hhcGUoYXJncyk7XG59XG5cbi8qKlxuICogUGVybXV0ZXMgdGhlIGRpbWVuc2lvbnMgb2YgdGhlIGlucHV0IGFjY29yZGluZyB0byBhIGdpdmVuIHBhdHRlcm4uXG4gKlxuICogVXNlZnVsIGZvciwgZS5nLiwgY29ubmVjdGluZyBSTk5zIGFuZCBjb252bmV0cyB0b2dldGhlci5cbiAqXG4gKiBFeGFtcGxlOlxuICpcbiAqIGBgYGpzXG4gKiBjb25zdCBtb2RlbCA9IHRmLnNlcXVlbnRpYWwoKTtcbiAqIG1vZGVsLmFkZCh0Zi5sYXllcnMucGVybXV0ZSh7XG4gKiAgIGRpbXM6IFsyLCAxXSxcbiAqICAgaW5wdXRTaGFwZTogWzEwLCA2NF1cbiAqIH0pKTtcbiAqIGNvbnNvbGUubG9nKG1vZGVsLm91dHB1dFNoYXBlKTtcbiAqIC8vIE5vdyBtb2RlbCdzIG91dHB1dCBzaGFwZSBpcyBbbnVsbCwgNjQsIDEwXSwgd2hlcmUgbnVsbCBpcyB0aGVcbiAqIC8vIHVucGVybXV0ZWQgc2FtcGxlIChiYXRjaCkgZGltZW5zaW9uLlxuICogYGBgXG4gKlxuICogSW5wdXQgc2hhcGU6XG4gKiAgIEFyYml0cmFyeS4gVXNlIHRoZSBjb25maWd1cmF0aW9uIGZpZWxkIGBpbnB1dFNoYXBlYCB3aGVuIHVzaW5nIHRoaXNcbiAqICAgbGF5ZXIgYXMgdGhlIGZpcnN0IGxheWVyIGluIGEgbW9kZWwuXG4gKlxuICogT3V0cHV0IHNoYXBlOlxuICogICBTYW1lIHJhbmsgYXMgdGhlIGlucHV0IHNoYXBlLCBidXQgd2l0aCB0aGUgZGltZW5zaW9ucyByZS1vcmRlcmVkIChpLmUuLFxuICogICBwZXJtdXRlZCkgYWNjb3JkaW5nIHRvIHRoZSBgZGltc2AgY29uZmlndXJhdGlvbiBvZiB0aGlzIGxheWVyLlxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdMYXllcnMnLCBzdWJoZWFkaW5nOiAnQmFzaWMnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gcGVybXV0ZShhcmdzOiBQZXJtdXRlTGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgUGVybXV0ZShhcmdzKTtcbn1cblxuLyoqXG4gKiBNYXBzIHBvc2l0aXZlIGludGVnZXJzIChpbmRpY2VzKSBpbnRvIGRlbnNlIHZlY3RvcnMgb2YgZml4ZWQgc2l6ZS5cbiAqIEUuZy4gW1s0XSwgWzIwXV0gLT4gW1swLjI1LCAwLjFdLCBbMC42LCAtMC4yXV1cbiAqXG4gKiAqKklucHV0IHNoYXBlOioqIDJEIHRlbnNvciB3aXRoIHNoYXBlOiBgW2JhdGNoU2l6ZSwgc2VxdWVuY2VMZW5ndGhdYC5cbiAqXG4gKiAqKk91dHB1dCBzaGFwZToqKiAzRCB0ZW5zb3Igd2l0aCBzaGFwZTogYFtiYXRjaFNpemUsIHNlcXVlbmNlTGVuZ3RoLFxuICogb3V0cHV0RGltXWAuXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdCYXNpYycsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBlbWJlZGRpbmcoYXJnczogRW1iZWRkaW5nTGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgRW1iZWRkaW5nKGFyZ3MpO1xufVxuXG4vLyBNZXJnZSBMYXllcnMuXG5cbi8qKlxuICogTGF5ZXIgdGhhdCBwZXJmb3JtcyBlbGVtZW50LXdpc2UgYWRkaXRpb24gb24gYW4gYEFycmF5YCBvZiBpbnB1dHMuXG4gKlxuICogSXQgdGFrZXMgYXMgaW5wdXQgYSBsaXN0IG9mIHRlbnNvcnMsIGFsbCBvZiB0aGUgc2FtZSBzaGFwZSwgYW5kIHJldHVybnMgYVxuICogc2luZ2xlIHRlbnNvciAoYWxzbyBvZiB0aGUgc2FtZSBzaGFwZSkuIFRoZSBpbnB1dHMgYXJlIHNwZWNpZmllZCBhcyBhblxuICogYEFycmF5YCB3aGVuIHRoZSBgYXBwbHlgIG1ldGhvZCBvZiB0aGUgYEFkZGAgbGF5ZXIgaW5zdGFuY2UgaXMgY2FsbGVkLiBGb3JcbiAqIGV4YW1wbGU6XG4gKlxuICogYGBganNcbiAqIGNvbnN0IGlucHV0MSA9IHRmLmlucHV0KHtzaGFwZTogWzIsIDJdfSk7XG4gKiBjb25zdCBpbnB1dDIgPSB0Zi5pbnB1dCh7c2hhcGU6IFsyLCAyXX0pO1xuICogY29uc3QgYWRkTGF5ZXIgPSB0Zi5sYXllcnMuYWRkKCk7XG4gKiBjb25zdCBzdW0gPSBhZGRMYXllci5hcHBseShbaW5wdXQxLCBpbnB1dDJdKTtcbiAqIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KHN1bS5zaGFwZSkpO1xuICogLy8gWW91IGdldCBbbnVsbCwgMiwgMl0sIHdpdGggdGhlIGZpcnN0IGRpbWVuc2lvbiBhcyB0aGUgdW5kZXRlcm1pbmVkIGJhdGNoXG4gKiAvLyBkaW1lbnNpb24uXG4gKiBgYGBcbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ01lcmdlJywgbmFtZXNwYWNlOiAnbGF5ZXJzJ31cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGFkZChhcmdzPzogTGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgQWRkKGFyZ3MpO1xufVxuXG4vKipcbiAqIExheWVyIHRoYXQgcGVyZm9ybXMgZWxlbWVudC13aXNlIGF2ZXJhZ2luZyBvbiBhbiBgQXJyYXlgIG9mIGlucHV0cy5cbiAqXG4gKiBJdCB0YWtlcyBhcyBpbnB1dCBhIGxpc3Qgb2YgdGVuc29ycywgYWxsIG9mIHRoZSBzYW1lIHNoYXBlLCBhbmQgcmV0dXJucyBhXG4gKiBzaW5nbGUgdGVuc29yIChhbHNvIG9mIHRoZSBzYW1lIHNoYXBlKS4gRm9yIGV4YW1wbGU6XG4gKlxuICogYGBganNcbiAqIGNvbnN0IGlucHV0MSA9IHRmLmlucHV0KHtzaGFwZTogWzIsIDJdfSk7XG4gKiBjb25zdCBpbnB1dDIgPSB0Zi5pbnB1dCh7c2hhcGU6IFsyLCAyXX0pO1xuICogY29uc3QgYXZlcmFnZUxheWVyID0gdGYubGF5ZXJzLmF2ZXJhZ2UoKTtcbiAqIGNvbnN0IGF2ZXJhZ2UgPSBhdmVyYWdlTGF5ZXIuYXBwbHkoW2lucHV0MSwgaW5wdXQyXSk7XG4gKiBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeShhdmVyYWdlLnNoYXBlKSk7XG4gKiAvLyBZb3UgZ2V0IFtudWxsLCAyLCAyXSwgd2l0aCB0aGUgZmlyc3QgZGltZW5zaW9uIGFzIHRoZSB1bmRldGVybWluZWQgYmF0Y2hcbiAqIC8vIGRpbWVuc2lvbi5cbiAqIGBgYFxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdMYXllcnMnLCBzdWJoZWFkaW5nOiAnTWVyZ2UnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gYXZlcmFnZShhcmdzPzogTGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgQXZlcmFnZShhcmdzKTtcbn1cblxuLyoqXG4gKiBMYXllciB0aGF0IGNvbmNhdGVuYXRlcyBhbiBgQXJyYXlgIG9mIGlucHV0cy5cbiAqXG4gKiBJdCB0YWtlcyBhIGxpc3Qgb2YgdGVuc29ycywgYWxsIG9mIHRoZSBzYW1lIHNoYXBlIGV4Y2VwdCBmb3IgdGhlXG4gKiBjb25jYXRlbmF0aW9uIGF4aXMsIGFuZCByZXR1cm5zIGEgc2luZ2xlIHRlbnNvciwgdGhlIGNvbmNhdGVuYXRpb25cbiAqIG9mIGFsbCBpbnB1dHMuIEZvciBleGFtcGxlOlxuICpcbiAqIGBgYGpzXG4gKiBjb25zdCBpbnB1dDEgPSB0Zi5pbnB1dCh7c2hhcGU6IFsyLCAyXX0pO1xuICogY29uc3QgaW5wdXQyID0gdGYuaW5wdXQoe3NoYXBlOiBbMiwgM119KTtcbiAqIGNvbnN0IGNvbmNhdExheWVyID0gdGYubGF5ZXJzLmNvbmNhdGVuYXRlKCk7XG4gKiBjb25zdCBvdXRwdXQgPSBjb25jYXRMYXllci5hcHBseShbaW5wdXQxLCBpbnB1dDJdKTtcbiAqIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KG91dHB1dC5zaGFwZSkpO1xuICogLy8gWW91IGdldCBbbnVsbCwgMiwgNV0sIHdpdGggdGhlIGZpcnN0IGRpbWVuc2lvbiBhcyB0aGUgdW5kZXRlcm1pbmVkIGJhdGNoXG4gKiAvLyBkaW1lbnNpb24uIFRoZSBsYXN0IGRpbWVuc2lvbiAoNSkgaXMgdGhlIHJlc3VsdCBvZiBjb25jYXRlbmF0aW5nIHRoZVxuICogLy8gbGFzdCBkaW1lbnNpb25zIG9mIHRoZSBpbnB1dHMgKDIgYW5kIDMpLlxuICogYGBgXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdNZXJnZScsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb25jYXRlbmF0ZShhcmdzPzogQ29uY2F0ZW5hdGVMYXllckFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBDb25jYXRlbmF0ZShhcmdzKTtcbn1cblxuLyoqXG4gKiBMYXllciB0aGF0IGNvbXB1dGVzIHRoZSBlbGVtZW50LXdpc2UgbWF4aW11bSBvZiBhbiBgQXJyYXlgIG9mIGlucHV0cy5cbiAqXG4gKiBJdCB0YWtlcyBhcyBpbnB1dCBhIGxpc3Qgb2YgdGVuc29ycywgYWxsIG9mIHRoZSBzYW1lIHNoYXBlLCBhbmQgcmV0dXJucyBhXG4gKiBzaW5nbGUgdGVuc29yIChhbHNvIG9mIHRoZSBzYW1lIHNoYXBlKS4gRm9yIGV4YW1wbGU6XG4gKlxuICogYGBganNcbiAqIGNvbnN0IGlucHV0MSA9IHRmLmlucHV0KHtzaGFwZTogWzIsIDJdfSk7XG4gKiBjb25zdCBpbnB1dDIgPSB0Zi5pbnB1dCh7c2hhcGU6IFsyLCAyXX0pO1xuICogY29uc3QgbWF4TGF5ZXIgPSB0Zi5sYXllcnMubWF4aW11bSgpO1xuICogY29uc3QgbWF4ID0gbWF4TGF5ZXIuYXBwbHkoW2lucHV0MSwgaW5wdXQyXSk7XG4gKiBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeShtYXguc2hhcGUpKTtcbiAqIC8vIFlvdSBnZXQgW251bGwsIDIsIDJdLCB3aXRoIHRoZSBmaXJzdCBkaW1lbnNpb24gYXMgdGhlIHVuZGV0ZXJtaW5lZCBiYXRjaFxuICogLy8gZGltZW5zaW9uLlxuICogYGBgXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdNZXJnZScsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtYXhpbXVtKGFyZ3M/OiBMYXllckFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBNYXhpbXVtKGFyZ3MpO1xufVxuXG4vKipcbiAqIExheWVyIHRoYXQgY29tcHV0ZXMgdGhlIGVsZW1lbnQtd2lzZSBtaW5pbXVtIG9mIGFuIGBBcnJheWAgb2YgaW5wdXRzLlxuICpcbiAqIEl0IHRha2VzIGFzIGlucHV0IGEgbGlzdCBvZiB0ZW5zb3JzLCBhbGwgb2YgdGhlIHNhbWUgc2hhcGUsIGFuZCByZXR1cm5zIGFcbiAqIHNpbmdsZSB0ZW5zb3IgKGFsc28gb2YgdGhlIHNhbWUgc2hhcGUpLiBGb3IgZXhhbXBsZTpcbiAqXG4gKiBgYGBqc1xuICogY29uc3QgaW5wdXQxID0gdGYuaW5wdXQoe3NoYXBlOiBbMiwgMl19KTtcbiAqIGNvbnN0IGlucHV0MiA9IHRmLmlucHV0KHtzaGFwZTogWzIsIDJdfSk7XG4gKiBjb25zdCBtaW5MYXllciA9IHRmLmxheWVycy5taW5pbXVtKCk7XG4gKiBjb25zdCBtaW4gPSBtaW5MYXllci5hcHBseShbaW5wdXQxLCBpbnB1dDJdKTtcbiAqIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KG1pbi5zaGFwZSkpO1xuICogLy8gWW91IGdldCBbbnVsbCwgMiwgMl0sIHdpdGggdGhlIGZpcnN0IGRpbWVuc2lvbiBhcyB0aGUgdW5kZXRlcm1pbmVkIGJhdGNoXG4gKiAvLyBkaW1lbnNpb24uXG4gKiBgYGBcbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ01lcmdlJywgbmFtZXNwYWNlOiAnbGF5ZXJzJ31cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG1pbmltdW0oYXJncz86IExheWVyQXJncykge1xuICByZXR1cm4gbmV3IE1pbmltdW0oYXJncyk7XG59XG5cbi8qKlxuICogTGF5ZXIgdGhhdCBtdWx0aXBsaWVzIChlbGVtZW50LXdpc2UpIGFuIGBBcnJheWAgb2YgaW5wdXRzLlxuICpcbiAqIEl0IHRha2VzIGFzIGlucHV0IGFuIEFycmF5IG9mIHRlbnNvcnMsIGFsbCBvZiB0aGUgc2FtZVxuICogc2hhcGUsIGFuZCByZXR1cm5zIGEgc2luZ2xlIHRlbnNvciAoYWxzbyBvZiB0aGUgc2FtZSBzaGFwZSkuXG4gKiBGb3IgZXhhbXBsZTpcbiAqXG4gKiBgYGBqc1xuICogY29uc3QgaW5wdXQxID0gdGYuaW5wdXQoe3NoYXBlOiBbMiwgMl19KTtcbiAqIGNvbnN0IGlucHV0MiA9IHRmLmlucHV0KHtzaGFwZTogWzIsIDJdfSk7XG4gKiBjb25zdCBpbnB1dDMgPSB0Zi5pbnB1dCh7c2hhcGU6IFsyLCAyXX0pO1xuICogY29uc3QgbXVsdGlwbHlMYXllciA9IHRmLmxheWVycy5tdWx0aXBseSgpO1xuICogY29uc3QgcHJvZHVjdCA9IG11bHRpcGx5TGF5ZXIuYXBwbHkoW2lucHV0MSwgaW5wdXQyLCBpbnB1dDNdKTtcbiAqIGNvbnNvbGUubG9nKHByb2R1Y3Quc2hhcGUpO1xuICogLy8gWW91IGdldCBbbnVsbCwgMiwgMl0sIHdpdGggdGhlIGZpcnN0IGRpbWVuc2lvbiBhcyB0aGUgdW5kZXRlcm1pbmVkIGJhdGNoXG4gKiAvLyBkaW1lbnNpb24uXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdNZXJnZScsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtdWx0aXBseShhcmdzPzogTGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgTXVsdGlwbHkoYXJncyk7XG59XG5cbi8qKlxuICogTGF5ZXIgdGhhdCBjb21wdXRlcyBhIGRvdCBwcm9kdWN0IGJldHdlZW4gc2FtcGxlcyBpbiB0d28gdGVuc29ycy5cbiAqXG4gKiBFLmcuLCBpZiBhcHBsaWVkIHRvIGEgbGlzdCBvZiB0d28gdGVuc29ycyBgYWAgYW5kIGBiYCBib3RoIG9mIHNoYXBlXG4gKiBgW2JhdGNoU2l6ZSwgbl1gLCB0aGUgb3V0cHV0IHdpbGwgYmUgYSB0ZW5zb3Igb2Ygc2hhcGUgYFtiYXRjaFNpemUsIDFdYCxcbiAqIHdoZXJlIGVhY2ggZW50cnkgYXQgaW5kZXggYFtpLCAwXWAgd2lsbCBiZSB0aGUgZG90IHByb2R1Y3QgYmV0d2VlblxuICogYGFbaSwgOl1gIGFuZCBgYltpLCA6XWAuXG4gKlxuICogRXhhbXBsZTpcbiAqXG4gKiBgYGBqc1xuICogY29uc3QgZG90TGF5ZXIgPSB0Zi5sYXllcnMuZG90KHtheGVzOiAtMX0pO1xuICogY29uc3QgeDEgPSB0Zi50ZW5zb3IyZChbWzEwLCAyMF0sIFszMCwgNDBdXSk7XG4gKiBjb25zdCB4MiA9IHRmLnRlbnNvcjJkKFtbLTEsIC0yXSwgWy0zLCAtNF1dKTtcbiAqXG4gKiAvLyBJbnZva2UgdGhlIGxheWVyJ3MgYXBwbHkoKSBtZXRob2QgaW4gZWFnZXIgKGltcGVyYXRpdmUpIG1vZGUuXG4gKiBjb25zdCB5ID0gZG90TGF5ZXIuYXBwbHkoW3gxLCB4Ml0pO1xuICogeS5wcmludCgpO1xuICogYGBgXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdNZXJnZScsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkb3QoYXJnczogRG90TGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgRG90KGFyZ3MpO1xufVxuXG4vLyBOb3JtYWxpemF0aW9uIExheWVycy5cblxuLyoqXG4gKiBCYXRjaCBub3JtYWxpemF0aW9uIGxheWVyIChJb2ZmZSBhbmQgU3plZ2VkeSwgMjAxNCkuXG4gKlxuICogTm9ybWFsaXplIHRoZSBhY3RpdmF0aW9ucyBvZiB0aGUgcHJldmlvdXMgbGF5ZXIgYXQgZWFjaCBiYXRjaCxcbiAqIGkuZS4gYXBwbGllcyBhIHRyYW5zZm9ybWF0aW9uIHRoYXQgbWFpbnRhaW5zIHRoZSBtZWFuIGFjdGl2YXRpb25cbiAqIGNsb3NlIHRvIDAgYW5kIHRoZSBhY3RpdmF0aW9uIHN0YW5kYXJkIGRldmlhdGlvbiBjbG9zZSB0byAxLlxuICpcbiAqIElucHV0IHNoYXBlOlxuICogICBBcmJpdHJhcnkuIFVzZSB0aGUga2V5d29yZCBhcmd1bWVudCBgaW5wdXRTaGFwZWAgKEFycmF5IG9mIGludGVnZXJzLCBkb2VzXG4gKiAgIG5vdCBpbmNsdWRlIHRoZSBzYW1wbGUgYXhpcykgd2hlbiBjYWxsaW5nIHRoZSBjb25zdHJ1Y3RvciBvZiB0aGlzIGNsYXNzLFxuICogICBpZiB0aGlzIGxheWVyIGlzIHVzZWQgYXMgYSBmaXJzdCBsYXllciBpbiBhIG1vZGVsLlxuICpcbiAqIE91dHB1dCBzaGFwZTpcbiAqICAgU2FtZSBzaGFwZSBhcyBpbnB1dC5cbiAqXG4gKiBSZWZlcmVuY2VzOlxuICogICAtIFtCYXRjaCBOb3JtYWxpemF0aW9uOiBBY2NlbGVyYXRpbmcgRGVlcCBOZXR3b3JrIFRyYWluaW5nIGJ5IFJlZHVjaW5nXG4gKiBJbnRlcm5hbCBDb3ZhcmlhdGUgU2hpZnRdKGh0dHBzOi8vYXJ4aXYub3JnL2Ficy8xNTAyLjAzMTY3KVxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdMYXllcnMnLCBzdWJoZWFkaW5nOiAnTm9ybWFsaXphdGlvbicsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBiYXRjaE5vcm1hbGl6YXRpb24oYXJncz86IEJhdGNoTm9ybWFsaXphdGlvbkxheWVyQXJncykge1xuICByZXR1cm4gbmV3IEJhdGNoTm9ybWFsaXphdGlvbihhcmdzKTtcbn1cblxuLyoqXG4gKiBMYXllci1ub3JtYWxpemF0aW9uIGxheWVyIChCYSBldCBhbC4sIDIwMTYpLlxuICpcbiAqIE5vcm1hbGl6ZXMgdGhlIGFjdGl2YXRpb25zIG9mIHRoZSBwcmV2aW91cyBsYXllciBmb3IgZWFjaCBnaXZlbiBleGFtcGxlIGluIGFcbiAqIGJhdGNoIGluZGVwZW5kZW50bHksIGluc3RlYWQgb2YgYWNyb3NzIGEgYmF0Y2ggbGlrZSBpbiBgYmF0Y2hOb3JtYWxpemF0aW9uYC5cbiAqIEluIG90aGVyIHdvcmRzLCB0aGlzIGxheWVyIGFwcGxpZXMgYSB0cmFuc2Zvcm1hdGlvbiB0aGF0IG1haW50YWlucyB0aGUgbWVhblxuICogYWN0aXZhdGlvbiB3aXRoaW4gZWFjaCBleGFtcGxlIGNsb3NlIHRvIDAgYW5kIGFjdGl2YXRpb24gdmFyaWFuY2UgY2xvc2UgdG8gMS5cbiAqXG4gKiBJbnB1dCBzaGFwZTpcbiAqICAgQXJiaXRyYXJ5LiBVc2UgdGhlIGFyZ3VtZW50IGBpbnB1dFNoYXBlYCB3aGVuIHVzaW5nIHRoaXMgbGF5ZXIgYXMgdGhlIGZpcnN0XG4gKiAgIGxheWVyIGluIGEgbW9kZWwuXG4gKlxuICogT3V0cHV0IHNoYXBlOlxuICogICBTYW1lIGFzIGlucHV0LlxuICpcbiAqIFJlZmVyZW5jZXM6XG4gKiAgIC0gW0xheWVyIE5vcm1hbGl6YXRpb25dKGh0dHBzOi8vYXJ4aXYub3JnL2Ficy8xNjA3LjA2NDUwKVxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdMYXllcnMnLCBzdWJoZWFkaW5nOiAnTm9ybWFsaXphdGlvbicsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsYXllck5vcm1hbGl6YXRpb24oYXJncz86IExheWVyTm9ybWFsaXphdGlvbkxheWVyQXJncykge1xuICByZXR1cm4gbmV3IExheWVyTm9ybWFsaXphdGlvbihhcmdzKTtcbn1cblxuLy8gUGFkZGluZyBMYXllcnMuXG5cbi8qKlxuICogWmVyby1wYWRkaW5nIGxheWVyIGZvciAyRCBpbnB1dCAoZS5nLiwgaW1hZ2UpLlxuICpcbiAqIFRoaXMgbGF5ZXIgY2FuIGFkZCByb3dzIGFuZCBjb2x1bW5zIG9mIHplcm9zXG4gKiBhdCB0aGUgdG9wLCBib3R0b20sIGxlZnQgYW5kIHJpZ2h0IHNpZGUgb2YgYW4gaW1hZ2UgdGVuc29yLlxuICpcbiAqIElucHV0IHNoYXBlOlxuICogICA0RCB0ZW5zb3Igd2l0aCBzaGFwZTpcbiAqICAgLSBJZiBgZGF0YUZvcm1hdGAgaXMgYFwiY2hhbm5lbHNMYXN0XCJgOlxuICogICAgIGBbYmF0Y2gsIHJvd3MsIGNvbHMsIGNoYW5uZWxzXWBcbiAqICAgLSBJZiBgZGF0YV9mb3JtYXRgIGlzIGBcImNoYW5uZWxzX2ZpcnN0XCJgOlxuICogICAgIGBbYmF0Y2gsIGNoYW5uZWxzLCByb3dzLCBjb2xzXWAuXG4gKlxuICogT3V0cHV0IHNoYXBlOlxuICogICA0RCB3aXRoIHNoYXBlOlxuICogICAtIElmIGBkYXRhRm9ybWF0YCBpcyBgXCJjaGFubmVsc0xhc3RcImA6XG4gKiAgICAgYFtiYXRjaCwgcGFkZGVkUm93cywgcGFkZGVkQ29scywgY2hhbm5lbHNdYFxuICogICAgLSBJZiBgZGF0YUZvcm1hdGAgaXMgYFwiY2hhbm5lbHNGaXJzdFwiYDpcbiAqICAgICBgW2JhdGNoLCBjaGFubmVscywgcGFkZGVkUm93cywgcGFkZGVkQ29sc11gLlxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdMYXllcnMnLCBzdWJoZWFkaW5nOiAnUGFkZGluZycsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB6ZXJvUGFkZGluZzJkKGFyZ3M/OiBaZXJvUGFkZGluZzJETGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgWmVyb1BhZGRpbmcyRChhcmdzKTtcbn1cblxuLy8gUG9vbGluZyBMYXllcnMuXG5cbi8qKlxuICogQXZlcmFnZSBwb29saW5nIG9wZXJhdGlvbiBmb3Igc3BhdGlhbCBkYXRhLlxuICpcbiAqIElucHV0IHNoYXBlOiBgW2JhdGNoU2l6ZSwgaW5MZW5ndGgsIGNoYW5uZWxzXWBcbiAqXG4gKiBPdXRwdXQgc2hhcGU6IGBbYmF0Y2hTaXplLCBwb29sZWRMZW5ndGgsIGNoYW5uZWxzXWBcbiAqXG4gKiBgdGYuYXZnUG9vbDFkYCBpcyBhbiBhbGlhcy5cbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ1Bvb2xpbmcnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gYXZlcmFnZVBvb2xpbmcxZChhcmdzOiBQb29saW5nMURMYXllckFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBBdmVyYWdlUG9vbGluZzFEKGFyZ3MpO1xufVxuZXhwb3J0IGZ1bmN0aW9uIGF2Z1Bvb2wxZChhcmdzOiBQb29saW5nMURMYXllckFyZ3MpIHtcbiAgcmV0dXJuIGF2ZXJhZ2VQb29saW5nMWQoYXJncyk7XG59XG4vLyBGb3IgYmFja3dhcmRzIGNvbXBhdGliaWxpdHkuXG4vLyBTZWUgaHR0cHM6Ly9naXRodWIuY29tL3RlbnNvcmZsb3cvdGZqcy9pc3N1ZXMvMTUyXG5leHBvcnQgZnVuY3Rpb24gYXZnUG9vbGluZzFkKGFyZ3M6IFBvb2xpbmcxRExheWVyQXJncykge1xuICByZXR1cm4gYXZlcmFnZVBvb2xpbmcxZChhcmdzKTtcbn1cblxuLyoqXG4gKiBBdmVyYWdlIHBvb2xpbmcgb3BlcmF0aW9uIGZvciBzcGF0aWFsIGRhdGEuXG4gKlxuICogSW5wdXQgc2hhcGU6XG4gKiAgLSBJZiBgZGF0YUZvcm1hdCA9PT0gQ0hBTk5FTF9MQVNUYDpcbiAqICAgICAgNEQgdGVuc29yIHdpdGggc2hhcGU6XG4gKiAgICAgIGBbYmF0Y2hTaXplLCByb3dzLCBjb2xzLCBjaGFubmVsc11gXG4gKiAgLSBJZiBgZGF0YUZvcm1hdCA9PT0gQ0hBTk5FTF9GSVJTVGA6XG4gKiAgICAgIDREIHRlbnNvciB3aXRoIHNoYXBlOlxuICogICAgICBgW2JhdGNoU2l6ZSwgY2hhbm5lbHMsIHJvd3MsIGNvbHNdYFxuICpcbiAqIE91dHB1dCBzaGFwZVxuICogIC0gSWYgYGRhdGFGb3JtYXQgPT09IENIQU5ORUxfTEFTVGA6XG4gKiAgICAgIDREIHRlbnNvciB3aXRoIHNoYXBlOlxuICogICAgICBgW2JhdGNoU2l6ZSwgcG9vbGVkUm93cywgcG9vbGVkQ29scywgY2hhbm5lbHNdYFxuICogIC0gSWYgYGRhdGFGb3JtYXQgPT09IENIQU5ORUxfRklSU1RgOlxuICogICAgICA0RCB0ZW5zb3Igd2l0aCBzaGFwZTpcbiAqICAgICAgYFtiYXRjaFNpemUsIGNoYW5uZWxzLCBwb29sZWRSb3dzLCBwb29sZWRDb2xzXWBcbiAqXG4gKiBgdGYuYXZnUG9vbDJkYCBpcyBhbiBhbGlhcy5cbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ1Bvb2xpbmcnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gYXZlcmFnZVBvb2xpbmcyZChhcmdzOiBQb29saW5nMkRMYXllckFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBBdmVyYWdlUG9vbGluZzJEKGFyZ3MpO1xufVxuZXhwb3J0IGZ1bmN0aW9uIGF2Z1Bvb2wyZChhcmdzOiBQb29saW5nMkRMYXllckFyZ3MpIHtcbiAgcmV0dXJuIGF2ZXJhZ2VQb29saW5nMmQoYXJncyk7XG59XG4vLyBGb3IgYmFja3dhcmRzIGNvbXBhdGliaWxpdHkuXG4vLyBTZWUgaHR0cHM6Ly9naXRodWIuY29tL3RlbnNvcmZsb3cvdGZqcy9pc3N1ZXMvMTUyXG5leHBvcnQgZnVuY3Rpb24gYXZnUG9vbGluZzJkKGFyZ3M6IFBvb2xpbmcyRExheWVyQXJncykge1xuICByZXR1cm4gYXZlcmFnZVBvb2xpbmcyZChhcmdzKTtcbn1cblxuLyoqXG4gKiBBdmVyYWdlIHBvb2xpbmcgb3BlcmF0aW9uIGZvciAzRCBkYXRhLlxuICpcbiAqIElucHV0IHNoYXBlXG4gKiAgIC0gSWYgYGRhdGFGb3JtYXQgPT09IGNoYW5uZWxzTGFzdGA6XG4gKiAgICAgICA1RCB0ZW5zb3Igd2l0aCBzaGFwZTpcbiAqICAgICAgIGBbYmF0Y2hTaXplLCBkZXB0aHMsIHJvd3MsIGNvbHMsIGNoYW5uZWxzXWBcbiAqICAgLSBJZiBgZGF0YUZvcm1hdCA9PT0gY2hhbm5lbHNGaXJzdGA6XG4gKiAgICAgIDREIHRlbnNvciB3aXRoIHNoYXBlOlxuICogICAgICAgYFtiYXRjaFNpemUsIGNoYW5uZWxzLCBkZXB0aHMsIHJvd3MsIGNvbHNdYFxuICpcbiAqIE91dHB1dCBzaGFwZVxuICogICAtIElmIGBkYXRhRm9ybWF0PWNoYW5uZWxzTGFzdGA6XG4gKiAgICAgICA1RCB0ZW5zb3Igd2l0aCBzaGFwZTpcbiAqICAgICAgIGBbYmF0Y2hTaXplLCBwb29sZWREZXB0aHMsIHBvb2xlZFJvd3MsIHBvb2xlZENvbHMsIGNoYW5uZWxzXWBcbiAqICAgLSBJZiBgZGF0YUZvcm1hdD1jaGFubmVsc0ZpcnN0YDpcbiAqICAgICAgIDVEIHRlbnNvciB3aXRoIHNoYXBlOlxuICogICAgICAgYFtiYXRjaFNpemUsIGNoYW5uZWxzLCBwb29sZWREZXB0aHMsIHBvb2xlZFJvd3MsIHBvb2xlZENvbHNdYFxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdMYXllcnMnLCBzdWJoZWFkaW5nOiAnUG9vbGluZycsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhdmVyYWdlUG9vbGluZzNkKGFyZ3M6IFBvb2xpbmczRExheWVyQXJncykge1xuICByZXR1cm4gbmV3IEF2ZXJhZ2VQb29saW5nM0QoYXJncyk7XG59XG5leHBvcnQgZnVuY3Rpb24gYXZnUG9vbDNkKGFyZ3M6IFBvb2xpbmczRExheWVyQXJncykge1xuICByZXR1cm4gYXZlcmFnZVBvb2xpbmczZChhcmdzKTtcbn1cbi8vIEZvciBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eS5cbi8vIFNlZSBodHRwczovL2dpdGh1Yi5jb20vdGVuc29yZmxvdy90ZmpzL2lzc3Vlcy8xNTJcbmV4cG9ydCBmdW5jdGlvbiBhdmdQb29saW5nM2QoYXJnczogUG9vbGluZzNETGF5ZXJBcmdzKSB7XG4gIHJldHVybiBhdmVyYWdlUG9vbGluZzNkKGFyZ3MpO1xufVxuXG4vKipcbiAqIEdsb2JhbCBhdmVyYWdlIHBvb2xpbmcgb3BlcmF0aW9uIGZvciB0ZW1wb3JhbCBkYXRhLlxuICpcbiAqIElucHV0IFNoYXBlOiAzRCB0ZW5zb3Igd2l0aCBzaGFwZTogYFtiYXRjaFNpemUsIHN0ZXBzLCBmZWF0dXJlc11gLlxuICpcbiAqIE91dHB1dCBTaGFwZTogMkQgdGVuc29yIHdpdGggc2hhcGU6IGBbYmF0Y2hTaXplLCBmZWF0dXJlc11gLlxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdMYXllcnMnLCBzdWJoZWFkaW5nOiAnUG9vbGluZycsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnbG9iYWxBdmVyYWdlUG9vbGluZzFkKGFyZ3M/OiBMYXllckFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBHbG9iYWxBdmVyYWdlUG9vbGluZzFEKGFyZ3MpO1xufVxuXG4vKipcbiAqIEdsb2JhbCBhdmVyYWdlIHBvb2xpbmcgb3BlcmF0aW9uIGZvciBzcGF0aWFsIGRhdGEuXG4gKlxuICogSW5wdXQgc2hhcGU6XG4gKiAgIC0gSWYgYGRhdGFGb3JtYXRgIGlzIGBDSEFOTkVMX0xBU1RgOlxuICogICAgICAgNEQgdGVuc29yIHdpdGggc2hhcGU6IGBbYmF0Y2hTaXplLCByb3dzLCBjb2xzLCBjaGFubmVsc11gLlxuICogICAtIElmIGBkYXRhRm9ybWF0YCBpcyBgQ0hBTk5FTF9GSVJTVGA6XG4gKiAgICAgICA0RCB0ZW5zb3Igd2l0aCBzaGFwZTogYFtiYXRjaFNpemUsIGNoYW5uZWxzLCByb3dzLCBjb2xzXWAuXG4gKlxuICogT3V0cHV0IHNoYXBlOlxuICogICAyRCB0ZW5zb3Igd2l0aCBzaGFwZTogYFtiYXRjaFNpemUsIGNoYW5uZWxzXWAuXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdQb29saW5nJywgbmFtZXNwYWNlOiAnbGF5ZXJzJ31cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdsb2JhbEF2ZXJhZ2VQb29saW5nMmQoYXJnczogR2xvYmFsUG9vbGluZzJETGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgR2xvYmFsQXZlcmFnZVBvb2xpbmcyRChhcmdzKTtcbn1cblxuLyoqXG4gKiBHbG9iYWwgbWF4IHBvb2xpbmcgb3BlcmF0aW9uIGZvciB0ZW1wb3JhbCBkYXRhLlxuICpcbiAqIElucHV0IFNoYXBlOiAzRCB0ZW5zb3Igd2l0aCBzaGFwZTogYFtiYXRjaFNpemUsIHN0ZXBzLCBmZWF0dXJlc11gLlxuICpcbiAqIE91dHB1dCBTaGFwZTogMkQgdGVuc29yIHdpdGggc2hhcGU6IGBbYmF0Y2hTaXplLCBmZWF0dXJlc11gLlxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdMYXllcnMnLCBzdWJoZWFkaW5nOiAnUG9vbGluZycsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnbG9iYWxNYXhQb29saW5nMWQoYXJncz86IExheWVyQXJncykge1xuICByZXR1cm4gbmV3IEdsb2JhbE1heFBvb2xpbmcxRChhcmdzKTtcbn1cblxuLyoqXG4gKiBHbG9iYWwgbWF4IHBvb2xpbmcgb3BlcmF0aW9uIGZvciBzcGF0aWFsIGRhdGEuXG4gKlxuICogSW5wdXQgc2hhcGU6XG4gKiAgIC0gSWYgYGRhdGFGb3JtYXRgIGlzIGBDSEFOTkVMX0xBU1RgOlxuICogICAgICAgNEQgdGVuc29yIHdpdGggc2hhcGU6IGBbYmF0Y2hTaXplLCByb3dzLCBjb2xzLCBjaGFubmVsc11gLlxuICogICAtIElmIGBkYXRhRm9ybWF0YCBpcyBgQ0hBTk5FTF9GSVJTVGA6XG4gKiAgICAgICA0RCB0ZW5zb3Igd2l0aCBzaGFwZTogYFtiYXRjaFNpemUsIGNoYW5uZWxzLCByb3dzLCBjb2xzXWAuXG4gKlxuICogT3V0cHV0IHNoYXBlOlxuICogICAyRCB0ZW5zb3Igd2l0aCBzaGFwZTogYFtiYXRjaFNpemUsIGNoYW5uZWxzXWAuXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdQb29saW5nJywgbmFtZXNwYWNlOiAnbGF5ZXJzJ31cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdsb2JhbE1heFBvb2xpbmcyZChhcmdzOiBHbG9iYWxQb29saW5nMkRMYXllckFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBHbG9iYWxNYXhQb29saW5nMkQoYXJncyk7XG59XG5cbi8qKlxuICogTWF4IHBvb2xpbmcgb3BlcmF0aW9uIGZvciB0ZW1wb3JhbCBkYXRhLlxuICpcbiAqIElucHV0IHNoYXBlOiAgYFtiYXRjaFNpemUsIGluTGVuZ3RoLCBjaGFubmVsc11gXG4gKlxuICogT3V0cHV0IHNoYXBlOiBgW2JhdGNoU2l6ZSwgcG9vbGVkTGVuZ3RoLCBjaGFubmVsc11gXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdQb29saW5nJywgbmFtZXNwYWNlOiAnbGF5ZXJzJ31cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG1heFBvb2xpbmcxZChhcmdzOiBQb29saW5nMURMYXllckFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBNYXhQb29saW5nMUQoYXJncyk7XG59XG5cbi8qKlxuICogTWF4IHBvb2xpbmcgb3BlcmF0aW9uIGZvciBzcGF0aWFsIGRhdGEuXG4gKlxuICogSW5wdXQgc2hhcGVcbiAqICAgLSBJZiBgZGF0YUZvcm1hdCA9PT0gQ0hBTk5FTF9MQVNUYDpcbiAqICAgICAgIDREIHRlbnNvciB3aXRoIHNoYXBlOlxuICogICAgICAgYFtiYXRjaFNpemUsIHJvd3MsIGNvbHMsIGNoYW5uZWxzXWBcbiAqICAgLSBJZiBgZGF0YUZvcm1hdCA9PT0gQ0hBTk5FTF9GSVJTVGA6XG4gKiAgICAgIDREIHRlbnNvciB3aXRoIHNoYXBlOlxuICogICAgICAgYFtiYXRjaFNpemUsIGNoYW5uZWxzLCByb3dzLCBjb2xzXWBcbiAqXG4gKiBPdXRwdXQgc2hhcGVcbiAqICAgLSBJZiBgZGF0YUZvcm1hdD1DSEFOTkVMX0xBU1RgOlxuICogICAgICAgNEQgdGVuc29yIHdpdGggc2hhcGU6XG4gKiAgICAgICBgW2JhdGNoU2l6ZSwgcG9vbGVkUm93cywgcG9vbGVkQ29scywgY2hhbm5lbHNdYFxuICogICAtIElmIGBkYXRhRm9ybWF0PUNIQU5ORUxfRklSU1RgOlxuICogICAgICAgNEQgdGVuc29yIHdpdGggc2hhcGU6XG4gKiAgICAgICBgW2JhdGNoU2l6ZSwgY2hhbm5lbHMsIHBvb2xlZFJvd3MsIHBvb2xlZENvbHNdYFxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdMYXllcnMnLCBzdWJoZWFkaW5nOiAnUG9vbGluZycsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtYXhQb29saW5nMmQoYXJnczogUG9vbGluZzJETGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgTWF4UG9vbGluZzJEKGFyZ3MpO1xufVxuXG4vKipcbiAqIE1heCBwb29saW5nIG9wZXJhdGlvbiBmb3IgM0QgZGF0YS5cbiAqXG4gKiBJbnB1dCBzaGFwZVxuICogICAtIElmIGBkYXRhRm9ybWF0ID09PSBjaGFubmVsc0xhc3RgOlxuICogICAgICAgNUQgdGVuc29yIHdpdGggc2hhcGU6XG4gKiAgICAgICBgW2JhdGNoU2l6ZSwgZGVwdGhzLCByb3dzLCBjb2xzLCBjaGFubmVsc11gXG4gKiAgIC0gSWYgYGRhdGFGb3JtYXQgPT09IGNoYW5uZWxzRmlyc3RgOlxuICogICAgICA1RCB0ZW5zb3Igd2l0aCBzaGFwZTpcbiAqICAgICAgIGBbYmF0Y2hTaXplLCBjaGFubmVscywgZGVwdGhzLCByb3dzLCBjb2xzXWBcbiAqXG4gKiBPdXRwdXQgc2hhcGVcbiAqICAgLSBJZiBgZGF0YUZvcm1hdD1jaGFubmVsc0xhc3RgOlxuICogICAgICAgNUQgdGVuc29yIHdpdGggc2hhcGU6XG4gKiAgICAgICBgW2JhdGNoU2l6ZSwgcG9vbGVkRGVwdGhzLCBwb29sZWRSb3dzLCBwb29sZWRDb2xzLCBjaGFubmVsc11gXG4gKiAgIC0gSWYgYGRhdGFGb3JtYXQ9Y2hhbm5lbHNGaXJzdGA6XG4gKiAgICAgICA1RCB0ZW5zb3Igd2l0aCBzaGFwZTpcbiAqICAgICAgIGBbYmF0Y2hTaXplLCBjaGFubmVscywgcG9vbGVkRGVwdGhzLCBwb29sZWRSb3dzLCBwb29sZWRDb2xzXWBcbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ1Bvb2xpbmcnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gbWF4UG9vbGluZzNkKGFyZ3M6IFBvb2xpbmczRExheWVyQXJncykge1xuICByZXR1cm4gbmV3IE1heFBvb2xpbmczRChhcmdzKTtcbn1cblxuLy8gUmVjdXJyZW50IExheWVycy5cblxuLyoqXG4gKiBHYXRlZCBSZWN1cnJlbnQgVW5pdCAtIENobyBldCBhbC4gMjAxNC5cbiAqXG4gKiBUaGlzIGlzIGFuIGBSTk5gIGxheWVyIGNvbnNpc3Rpbmcgb2Ygb25lIGBHUlVDZWxsYC4gSG93ZXZlciwgdW5saWtlXG4gKiB0aGUgdW5kZXJseWluZyBgR1JVQ2VsbGAsIHRoZSBgYXBwbHlgIG1ldGhvZCBvZiBgU2ltcGxlUk5OYCBvcGVyYXRlc1xuICogb24gYSBzZXF1ZW5jZSBvZiBpbnB1dHMuIFRoZSBzaGFwZSBvZiB0aGUgaW5wdXQgKG5vdCBpbmNsdWRpbmcgdGhlIGZpcnN0LFxuICogYmF0Y2ggZGltZW5zaW9uKSBuZWVkcyB0byBiZSBhdCBsZWFzdCAyLUQsIHdpdGggdGhlIGZpcnN0IGRpbWVuc2lvbiBiZWluZ1xuICogdGltZSBzdGVwcy4gRm9yIGV4YW1wbGU6XG4gKlxuICogYGBganNcbiAqIGNvbnN0IHJubiA9IHRmLmxheWVycy5ncnUoe3VuaXRzOiA4LCByZXR1cm5TZXF1ZW5jZXM6IHRydWV9KTtcbiAqXG4gKiAvLyBDcmVhdGUgYW4gaW5wdXQgd2l0aCAxMCB0aW1lIHN0ZXBzLlxuICogY29uc3QgaW5wdXQgPSB0Zi5pbnB1dCh7c2hhcGU6IFsxMCwgMjBdfSk7XG4gKiBjb25zdCBvdXRwdXQgPSBybm4uYXBwbHkoaW5wdXQpO1xuICpcbiAqIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KG91dHB1dC5zaGFwZSkpO1xuICogLy8gW251bGwsIDEwLCA4XTogMXN0IGRpbWVuc2lvbiBpcyB1bmtub3duIGJhdGNoIHNpemU7IDJuZCBkaW1lbnNpb24gaXMgdGhlXG4gKiAvLyBzYW1lIGFzIHRoZSBzZXF1ZW5jZSBsZW5ndGggb2YgYGlucHV0YCwgZHVlIHRvIGByZXR1cm5TZXF1ZW5jZXNgOiBgdHJ1ZWA7XG4gKiAvLyAzcmQgZGltZW5zaW9uIGlzIHRoZSBgR1JVQ2VsbGAncyBudW1iZXIgb2YgdW5pdHMuXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdSZWN1cnJlbnQnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ3J1KGFyZ3M6IEdSVUxheWVyQXJncykge1xuICByZXR1cm4gbmV3IEdSVShhcmdzKTtcbn1cblxuLyoqXG4gKiBDZWxsIGNsYXNzIGZvciBgR1JVYC5cbiAqXG4gKiBgR1JVQ2VsbGAgaXMgZGlzdGluY3QgZnJvbSB0aGUgYFJOTmAgc3ViY2xhc3MgYEdSVWAgaW4gdGhhdCBpdHNcbiAqIGBhcHBseWAgbWV0aG9kIHRha2VzIHRoZSBpbnB1dCBkYXRhIG9mIG9ubHkgYSBzaW5nbGUgdGltZSBzdGVwIGFuZCByZXR1cm5zXG4gKiB0aGUgY2VsbCdzIG91dHB1dCBhdCB0aGUgdGltZSBzdGVwLCB3aGlsZSBgR1JVYCB0YWtlcyB0aGUgaW5wdXQgZGF0YVxuICogb3ZlciBhIG51bWJlciBvZiB0aW1lIHN0ZXBzLiBGb3IgZXhhbXBsZTpcbiAqXG4gKiBgYGBqc1xuICogY29uc3QgY2VsbCA9IHRmLmxheWVycy5ncnVDZWxsKHt1bml0czogMn0pO1xuICogY29uc3QgaW5wdXQgPSB0Zi5pbnB1dCh7c2hhcGU6IFsxMF19KTtcbiAqIGNvbnN0IG91dHB1dCA9IGNlbGwuYXBwbHkoaW5wdXQpO1xuICpcbiAqIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KG91dHB1dC5zaGFwZSkpO1xuICogLy8gW251bGwsIDEwXTogVGhpcyBpcyB0aGUgY2VsbCdzIG91dHB1dCBhdCBhIHNpbmdsZSB0aW1lIHN0ZXAuIFRoZSAxc3RcbiAqIC8vIGRpbWVuc2lvbiBpcyB0aGUgdW5rbm93biBiYXRjaCBzaXplLlxuICogYGBgXG4gKlxuICogSW5zdGFuY2Uocykgb2YgYEdSVUNlbGxgIGNhbiBiZSB1c2VkIHRvIGNvbnN0cnVjdCBgUk5OYCBsYXllcnMuIFRoZVxuICogbW9zdCB0eXBpY2FsIHVzZSBvZiB0aGlzIHdvcmtmbG93IGlzIHRvIGNvbWJpbmUgYSBudW1iZXIgb2YgY2VsbHMgaW50byBhXG4gKiBzdGFja2VkIFJOTiBjZWxsIChpLmUuLCBgU3RhY2tlZFJOTkNlbGxgIGludGVybmFsbHkpIGFuZCB1c2UgaXQgdG8gY3JlYXRlIGFuXG4gKiBSTk4uIEZvciBleGFtcGxlOlxuICpcbiAqIGBgYGpzXG4gKiBjb25zdCBjZWxscyA9IFtcbiAqICAgdGYubGF5ZXJzLmdydUNlbGwoe3VuaXRzOiA0fSksXG4gKiAgIHRmLmxheWVycy5ncnVDZWxsKHt1bml0czogOH0pLFxuICogXTtcbiAqIGNvbnN0IHJubiA9IHRmLmxheWVycy5ybm4oe2NlbGw6IGNlbGxzLCByZXR1cm5TZXF1ZW5jZXM6IHRydWV9KTtcbiAqXG4gKiAvLyBDcmVhdGUgYW4gaW5wdXQgd2l0aCAxMCB0aW1lIHN0ZXBzIGFuZCBhIGxlbmd0aC0yMCB2ZWN0b3IgYXQgZWFjaCBzdGVwLlxuICogY29uc3QgaW5wdXQgPSB0Zi5pbnB1dCh7c2hhcGU6IFsxMCwgMjBdfSk7XG4gKiBjb25zdCBvdXRwdXQgPSBybm4uYXBwbHkoaW5wdXQpO1xuICpcbiAqIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KG91dHB1dC5zaGFwZSkpO1xuICogLy8gW251bGwsIDEwLCA4XTogMXN0IGRpbWVuc2lvbiBpcyB1bmtub3duIGJhdGNoIHNpemU7IDJuZCBkaW1lbnNpb24gaXMgdGhlXG4gKiAvLyBzYW1lIGFzIHRoZSBzZXF1ZW5jZSBsZW5ndGggb2YgYGlucHV0YCwgZHVlIHRvIGByZXR1cm5TZXF1ZW5jZXNgOiBgdHJ1ZWA7XG4gKiAvLyAzcmQgZGltZW5zaW9uIGlzIHRoZSBsYXN0IGBncnVDZWxsYCdzIG51bWJlciBvZiB1bml0cy5cbiAqIGBgYFxuICpcbiAqIFRvIGNyZWF0ZSBhbiBgUk5OYCBjb25zaXN0aW5nIG9mIG9ubHkgKm9uZSogYEdSVUNlbGxgLCB1c2UgdGhlXG4gKiBgdGYubGF5ZXJzLmdydWAuXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdSZWN1cnJlbnQnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ3J1Q2VsbChhcmdzOiBHUlVDZWxsTGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgR1JVQ2VsbChhcmdzKTtcbn1cblxuLyoqXG4gKiBMb25nLVNob3J0IFRlcm0gTWVtb3J5IGxheWVyIC0gSG9jaHJlaXRlciAxOTk3LlxuICpcbiAqIFRoaXMgaXMgYW4gYFJOTmAgbGF5ZXIgY29uc2lzdGluZyBvZiBvbmUgYExTVE1DZWxsYC4gSG93ZXZlciwgdW5saWtlXG4gKiB0aGUgdW5kZXJseWluZyBgTFNUTUNlbGxgLCB0aGUgYGFwcGx5YCBtZXRob2Qgb2YgYExTVE1gIG9wZXJhdGVzXG4gKiBvbiBhIHNlcXVlbmNlIG9mIGlucHV0cy4gVGhlIHNoYXBlIG9mIHRoZSBpbnB1dCAobm90IGluY2x1ZGluZyB0aGUgZmlyc3QsXG4gKiBiYXRjaCBkaW1lbnNpb24pIG5lZWRzIHRvIGJlIGF0IGxlYXN0IDItRCwgd2l0aCB0aGUgZmlyc3QgZGltZW5zaW9uIGJlaW5nXG4gKiB0aW1lIHN0ZXBzLiBGb3IgZXhhbXBsZTpcbiAqXG4gKiBgYGBqc1xuICogY29uc3QgbHN0bSA9IHRmLmxheWVycy5sc3RtKHt1bml0czogOCwgcmV0dXJuU2VxdWVuY2VzOiB0cnVlfSk7XG4gKlxuICogLy8gQ3JlYXRlIGFuIGlucHV0IHdpdGggMTAgdGltZSBzdGVwcy5cbiAqIGNvbnN0IGlucHV0ID0gdGYuaW5wdXQoe3NoYXBlOiBbMTAsIDIwXX0pO1xuICogY29uc3Qgb3V0cHV0ID0gbHN0bS5hcHBseShpbnB1dCk7XG4gKlxuICogY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkob3V0cHV0LnNoYXBlKSk7XG4gKiAvLyBbbnVsbCwgMTAsIDhdOiAxc3QgZGltZW5zaW9uIGlzIHVua25vd24gYmF0Y2ggc2l6ZTsgMm5kIGRpbWVuc2lvbiBpcyB0aGVcbiAqIC8vIHNhbWUgYXMgdGhlIHNlcXVlbmNlIGxlbmd0aCBvZiBgaW5wdXRgLCBkdWUgdG8gYHJldHVyblNlcXVlbmNlc2A6IGB0cnVlYDtcbiAqIC8vIDNyZCBkaW1lbnNpb24gaXMgdGhlIGBMU1RNQ2VsbGAncyBudW1iZXIgb2YgdW5pdHMuXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdSZWN1cnJlbnQnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gbHN0bShhcmdzOiBMU1RNTGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgTFNUTShhcmdzKTtcbn1cblxuLyoqXG4gKiBDZWxsIGNsYXNzIGZvciBgTFNUTWAuXG4gKlxuICogYExTVE1DZWxsYCBpcyBkaXN0aW5jdCBmcm9tIHRoZSBgUk5OYCBzdWJjbGFzcyBgTFNUTWAgaW4gdGhhdCBpdHNcbiAqIGBhcHBseWAgbWV0aG9kIHRha2VzIHRoZSBpbnB1dCBkYXRhIG9mIG9ubHkgYSBzaW5nbGUgdGltZSBzdGVwIGFuZCByZXR1cm5zXG4gKiB0aGUgY2VsbCdzIG91dHB1dCBhdCB0aGUgdGltZSBzdGVwLCB3aGlsZSBgTFNUTWAgdGFrZXMgdGhlIGlucHV0IGRhdGFcbiAqIG92ZXIgYSBudW1iZXIgb2YgdGltZSBzdGVwcy4gRm9yIGV4YW1wbGU6XG4gKlxuICogYGBganNcbiAqIGNvbnN0IGNlbGwgPSB0Zi5sYXllcnMubHN0bUNlbGwoe3VuaXRzOiAyfSk7XG4gKiBjb25zdCBpbnB1dCA9IHRmLmlucHV0KHtzaGFwZTogWzEwXX0pO1xuICogY29uc3Qgb3V0cHV0ID0gY2VsbC5hcHBseShpbnB1dCk7XG4gKlxuICogY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkob3V0cHV0LnNoYXBlKSk7XG4gKiAvLyBbbnVsbCwgMTBdOiBUaGlzIGlzIHRoZSBjZWxsJ3Mgb3V0cHV0IGF0IGEgc2luZ2xlIHRpbWUgc3RlcC4gVGhlIDFzdFxuICogLy8gZGltZW5zaW9uIGlzIHRoZSB1bmtub3duIGJhdGNoIHNpemUuXG4gKiBgYGBcbiAqXG4gKiBJbnN0YW5jZShzKSBvZiBgTFNUTUNlbGxgIGNhbiBiZSB1c2VkIHRvIGNvbnN0cnVjdCBgUk5OYCBsYXllcnMuIFRoZVxuICogbW9zdCB0eXBpY2FsIHVzZSBvZiB0aGlzIHdvcmtmbG93IGlzIHRvIGNvbWJpbmUgYSBudW1iZXIgb2YgY2VsbHMgaW50byBhXG4gKiBzdGFja2VkIFJOTiBjZWxsIChpLmUuLCBgU3RhY2tlZFJOTkNlbGxgIGludGVybmFsbHkpIGFuZCB1c2UgaXQgdG8gY3JlYXRlIGFuXG4gKiBSTk4uIEZvciBleGFtcGxlOlxuICpcbiAqIGBgYGpzXG4gKiBjb25zdCBjZWxscyA9IFtcbiAqICAgdGYubGF5ZXJzLmxzdG1DZWxsKHt1bml0czogNH0pLFxuICogICB0Zi5sYXllcnMubHN0bUNlbGwoe3VuaXRzOiA4fSksXG4gKiBdO1xuICogY29uc3Qgcm5uID0gdGYubGF5ZXJzLnJubih7Y2VsbDogY2VsbHMsIHJldHVyblNlcXVlbmNlczogdHJ1ZX0pO1xuICpcbiAqIC8vIENyZWF0ZSBhbiBpbnB1dCB3aXRoIDEwIHRpbWUgc3RlcHMgYW5kIGEgbGVuZ3RoLTIwIHZlY3RvciBhdCBlYWNoIHN0ZXAuXG4gKiBjb25zdCBpbnB1dCA9IHRmLmlucHV0KHtzaGFwZTogWzEwLCAyMF19KTtcbiAqIGNvbnN0IG91dHB1dCA9IHJubi5hcHBseShpbnB1dCk7XG4gKlxuICogY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkob3V0cHV0LnNoYXBlKSk7XG4gKiAvLyBbbnVsbCwgMTAsIDhdOiAxc3QgZGltZW5zaW9uIGlzIHVua25vd24gYmF0Y2ggc2l6ZTsgMm5kIGRpbWVuc2lvbiBpcyB0aGVcbiAqIC8vIHNhbWUgYXMgdGhlIHNlcXVlbmNlIGxlbmd0aCBvZiBgaW5wdXRgLCBkdWUgdG8gYHJldHVyblNlcXVlbmNlc2A6IGB0cnVlYDtcbiAqIC8vIDNyZCBkaW1lbnNpb24gaXMgdGhlIGxhc3QgYGxzdG1DZWxsYCdzIG51bWJlciBvZiB1bml0cy5cbiAqIGBgYFxuICpcbiAqIFRvIGNyZWF0ZSBhbiBgUk5OYCBjb25zaXN0aW5nIG9mIG9ubHkgKm9uZSogYExTVE1DZWxsYCwgdXNlIHRoZVxuICogYHRmLmxheWVycy5sc3RtYC5cbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ1JlY3VycmVudCcsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsc3RtQ2VsbChhcmdzOiBMU1RNQ2VsbExheWVyQXJncykge1xuICByZXR1cm4gbmV3IExTVE1DZWxsKGFyZ3MpO1xufVxuXG4vKipcbiAqIEZ1bGx5LWNvbm5lY3RlZCBSTk4gd2hlcmUgdGhlIG91dHB1dCBpcyB0byBiZSBmZWQgYmFjayB0byBpbnB1dC5cbiAqXG4gKiBUaGlzIGlzIGFuIGBSTk5gIGxheWVyIGNvbnNpc3Rpbmcgb2Ygb25lIGBTaW1wbGVSTk5DZWxsYC4gSG93ZXZlciwgdW5saWtlXG4gKiB0aGUgdW5kZXJseWluZyBgU2ltcGxlUk5OQ2VsbGAsIHRoZSBgYXBwbHlgIG1ldGhvZCBvZiBgU2ltcGxlUk5OYCBvcGVyYXRlc1xuICogb24gYSBzZXF1ZW5jZSBvZiBpbnB1dHMuIFRoZSBzaGFwZSBvZiB0aGUgaW5wdXQgKG5vdCBpbmNsdWRpbmcgdGhlIGZpcnN0LFxuICogYmF0Y2ggZGltZW5zaW9uKSBuZWVkcyB0byBiZSBhdCBsZWFzdCAyLUQsIHdpdGggdGhlIGZpcnN0IGRpbWVuc2lvbiBiZWluZ1xuICogdGltZSBzdGVwcy4gRm9yIGV4YW1wbGU6XG4gKlxuICogYGBganNcbiAqIGNvbnN0IHJubiA9IHRmLmxheWVycy5zaW1wbGVSTk4oe3VuaXRzOiA4LCByZXR1cm5TZXF1ZW5jZXM6IHRydWV9KTtcbiAqXG4gKiAvLyBDcmVhdGUgYW4gaW5wdXQgd2l0aCAxMCB0aW1lIHN0ZXBzLlxuICogY29uc3QgaW5wdXQgPSB0Zi5pbnB1dCh7c2hhcGU6IFsxMCwgMjBdfSk7XG4gKiBjb25zdCBvdXRwdXQgPSBybm4uYXBwbHkoaW5wdXQpO1xuICpcbiAqIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KG91dHB1dC5zaGFwZSkpO1xuICogLy8gW251bGwsIDEwLCA4XTogMXN0IGRpbWVuc2lvbiBpcyB1bmtub3duIGJhdGNoIHNpemU7IDJuZCBkaW1lbnNpb24gaXMgdGhlXG4gKiAvLyBzYW1lIGFzIHRoZSBzZXF1ZW5jZSBsZW5ndGggb2YgYGlucHV0YCwgZHVlIHRvIGByZXR1cm5TZXF1ZW5jZXNgOiBgdHJ1ZWA7XG4gKiAvLyAzcmQgZGltZW5zaW9uIGlzIHRoZSBgU2ltcGxlUk5OQ2VsbGAncyBudW1iZXIgb2YgdW5pdHMuXG4gKiBgYGBcbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ1JlY3VycmVudCcsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzaW1wbGVSTk4oYXJnczogU2ltcGxlUk5OTGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgU2ltcGxlUk5OKGFyZ3MpO1xufVxuXG4vKipcbiAqIENlbGwgY2xhc3MgZm9yIGBTaW1wbGVSTk5gLlxuICpcbiAqIGBTaW1wbGVSTk5DZWxsYCBpcyBkaXN0aW5jdCBmcm9tIHRoZSBgUk5OYCBzdWJjbGFzcyBgU2ltcGxlUk5OYCBpbiB0aGF0IGl0c1xuICogYGFwcGx5YCBtZXRob2QgdGFrZXMgdGhlIGlucHV0IGRhdGEgb2Ygb25seSBhIHNpbmdsZSB0aW1lIHN0ZXAgYW5kIHJldHVybnNcbiAqIHRoZSBjZWxsJ3Mgb3V0cHV0IGF0IHRoZSB0aW1lIHN0ZXAsIHdoaWxlIGBTaW1wbGVSTk5gIHRha2VzIHRoZSBpbnB1dCBkYXRhXG4gKiBvdmVyIGEgbnVtYmVyIG9mIHRpbWUgc3RlcHMuIEZvciBleGFtcGxlOlxuICpcbiAqIGBgYGpzXG4gKiBjb25zdCBjZWxsID0gdGYubGF5ZXJzLnNpbXBsZVJOTkNlbGwoe3VuaXRzOiAyfSk7XG4gKiBjb25zdCBpbnB1dCA9IHRmLmlucHV0KHtzaGFwZTogWzEwXX0pO1xuICogY29uc3Qgb3V0cHV0ID0gY2VsbC5hcHBseShpbnB1dCk7XG4gKlxuICogY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkob3V0cHV0LnNoYXBlKSk7XG4gKiAvLyBbbnVsbCwgMTBdOiBUaGlzIGlzIHRoZSBjZWxsJ3Mgb3V0cHV0IGF0IGEgc2luZ2xlIHRpbWUgc3RlcC4gVGhlIDFzdFxuICogLy8gZGltZW5zaW9uIGlzIHRoZSB1bmtub3duIGJhdGNoIHNpemUuXG4gKiBgYGBcbiAqXG4gKiBJbnN0YW5jZShzKSBvZiBgU2ltcGxlUk5OQ2VsbGAgY2FuIGJlIHVzZWQgdG8gY29uc3RydWN0IGBSTk5gIGxheWVycy4gVGhlXG4gKiBtb3N0IHR5cGljYWwgdXNlIG9mIHRoaXMgd29ya2Zsb3cgaXMgdG8gY29tYmluZSBhIG51bWJlciBvZiBjZWxscyBpbnRvIGFcbiAqIHN0YWNrZWQgUk5OIGNlbGwgKGkuZS4sIGBTdGFja2VkUk5OQ2VsbGAgaW50ZXJuYWxseSkgYW5kIHVzZSBpdCB0byBjcmVhdGUgYW5cbiAqIFJOTi4gRm9yIGV4YW1wbGU6XG4gKlxuICogYGBganNcbiAqIGNvbnN0IGNlbGxzID0gW1xuICogICB0Zi5sYXllcnMuc2ltcGxlUk5OQ2VsbCh7dW5pdHM6IDR9KSxcbiAqICAgdGYubGF5ZXJzLnNpbXBsZVJOTkNlbGwoe3VuaXRzOiA4fSksXG4gKiBdO1xuICogY29uc3Qgcm5uID0gdGYubGF5ZXJzLnJubih7Y2VsbDogY2VsbHMsIHJldHVyblNlcXVlbmNlczogdHJ1ZX0pO1xuICpcbiAqIC8vIENyZWF0ZSBhbiBpbnB1dCB3aXRoIDEwIHRpbWUgc3RlcHMgYW5kIGEgbGVuZ3RoLTIwIHZlY3RvciBhdCBlYWNoIHN0ZXAuXG4gKiBjb25zdCBpbnB1dCA9IHRmLmlucHV0KHtzaGFwZTogWzEwLCAyMF19KTtcbiAqIGNvbnN0IG91dHB1dCA9IHJubi5hcHBseShpbnB1dCk7XG4gKlxuICogY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkob3V0cHV0LnNoYXBlKSk7XG4gKiAvLyBbbnVsbCwgMTAsIDhdOiAxc3QgZGltZW5zaW9uIGlzIHVua25vd24gYmF0Y2ggc2l6ZTsgMm5kIGRpbWVuc2lvbiBpcyB0aGVcbiAqIC8vIHNhbWUgYXMgdGhlIHNlcXVlbmNlIGxlbmd0aCBvZiBgaW5wdXRgLCBkdWUgdG8gYHJldHVyblNlcXVlbmNlc2A6IGB0cnVlYDtcbiAqIC8vIDNyZCBkaW1lbnNpb24gaXMgdGhlIGxhc3QgYFNpbXBsZVJOTkNlbGxgJ3MgbnVtYmVyIG9mIHVuaXRzLlxuICogYGBgXG4gKlxuICogVG8gY3JlYXRlIGFuIGBSTk5gIGNvbnNpc3Rpbmcgb2Ygb25seSAqb25lKiBgU2ltcGxlUk5OQ2VsbGAsIHVzZSB0aGVcbiAqIGB0Zi5sYXllcnMuc2ltcGxlUk5OYC5cbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ1JlY3VycmVudCcsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzaW1wbGVSTk5DZWxsKGFyZ3M6IFNpbXBsZVJOTkNlbGxMYXllckFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBTaW1wbGVSTk5DZWxsKGFyZ3MpO1xufVxuXG4vKipcbiAqIENvbnZvbHV0aW9uYWwgTFNUTSBsYXllciAtIFhpbmdqaWFuIFNoaSAyMDE1LlxuICpcbiAqIFRoaXMgaXMgYSBgQ29udlJOTjJEYCBsYXllciBjb25zaXN0aW5nIG9mIG9uZSBgQ29udkxTVE0yRENlbGxgLiBIb3dldmVyLFxuICogdW5saWtlIHRoZSB1bmRlcmx5aW5nIGBDb252TFNUTTJEQ2VsbGAsIHRoZSBgYXBwbHlgIG1ldGhvZCBvZiBgQ29udkxTVE0yRGBcbiAqIG9wZXJhdGVzIG9uIGEgc2VxdWVuY2Ugb2YgaW5wdXRzLiBUaGUgc2hhcGUgb2YgdGhlIGlucHV0IChub3QgaW5jbHVkaW5nIHRoZVxuICogZmlyc3QsIGJhdGNoIGRpbWVuc2lvbikgbmVlZHMgdG8gYmUgNC1ELCB3aXRoIHRoZSBmaXJzdCBkaW1lbnNpb24gYmVpbmcgdGltZVxuICogc3RlcHMuIEZvciBleGFtcGxlOlxuICpcbiAqIGBgYGpzXG4gKiBjb25zdCBmaWx0ZXJzID0gMztcbiAqIGNvbnN0IGtlcm5lbFNpemUgPSAzO1xuICpcbiAqIGNvbnN0IGJhdGNoU2l6ZSA9IDQ7XG4gKiBjb25zdCBzZXF1ZW5jZUxlbmd0aCA9IDI7XG4gKiBjb25zdCBzaXplID0gNTtcbiAqIGNvbnN0IGNoYW5uZWxzID0gMztcbiAqXG4gKiBjb25zdCBpbnB1dFNoYXBlID0gW2JhdGNoU2l6ZSwgc2VxdWVuY2VMZW5ndGgsIHNpemUsIHNpemUsIGNoYW5uZWxzXTtcbiAqIGNvbnN0IGlucHV0ID0gdGYub25lcyhpbnB1dFNoYXBlKTtcbiAqXG4gKiBjb25zdCBsYXllciA9IHRmLmxheWVycy5jb252THN0bTJkKHtmaWx0ZXJzLCBrZXJuZWxTaXplfSk7XG4gKlxuICogY29uc3Qgb3V0cHV0ID0gbGF5ZXIuYXBwbHkoaW5wdXQpO1xuICogYGBgXG4gKi9cbi8qKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ1JlY3VycmVudCcsIG5hbWVzcGFjZTogJ2xheWVycyd9ICovXG5leHBvcnQgZnVuY3Rpb24gY29udkxzdG0yZChhcmdzOiBDb252TFNUTTJEQXJncykge1xuICByZXR1cm4gbmV3IENvbnZMU1RNMkQoYXJncyk7XG59XG5cbi8qKlxuICogQ2VsbCBjbGFzcyBmb3IgYENvbnZMU1RNMkRgLlxuICpcbiAqIGBDb252TFNUTTJEQ2VsbGAgaXMgZGlzdGluY3QgZnJvbSB0aGUgYENvbnZSTk4yRGAgc3ViY2xhc3MgYENvbnZMU1RNMkRgIGluXG4gKiB0aGF0IGl0cyBgY2FsbGAgbWV0aG9kIHRha2VzIHRoZSBpbnB1dCBkYXRhIG9mIG9ubHkgYSBzaW5nbGUgdGltZSBzdGVwIGFuZFxuICogcmV0dXJucyB0aGUgY2VsbCdzIG91dHB1dCBhdCB0aGUgdGltZSBzdGVwLCB3aGlsZSBgQ29udkxTVE0yRGAgdGFrZXMgdGhlXG4gKiBpbnB1dCBkYXRhIG92ZXIgYSBudW1iZXIgb2YgdGltZSBzdGVwcy4gRm9yIGV4YW1wbGU6XG4gKlxuICogYGBganNcbiAqIGNvbnN0IGZpbHRlcnMgPSAzO1xuICogY29uc3Qga2VybmVsU2l6ZSA9IDM7XG4gKlxuICogY29uc3Qgc2VxdWVuY2VMZW5ndGggPSAxO1xuICogY29uc3Qgc2l6ZSA9IDU7XG4gKiBjb25zdCBjaGFubmVscyA9IDM7XG4gKlxuICogY29uc3QgaW5wdXRTaGFwZSA9IFtzZXF1ZW5jZUxlbmd0aCwgc2l6ZSwgc2l6ZSwgY2hhbm5lbHNdO1xuICogY29uc3QgaW5wdXQgPSB0Zi5vbmVzKGlucHV0U2hhcGUpO1xuICpcbiAqIGNvbnN0IGNlbGwgPSB0Zi5sYXllcnMuY29udkxzdG0yZENlbGwoe2ZpbHRlcnMsIGtlcm5lbFNpemV9KTtcbiAqXG4gKiBjZWxsLmJ1aWxkKGlucHV0LnNoYXBlKTtcbiAqXG4gKiBjb25zdCBvdXRwdXRTaXplID0gc2l6ZSAtIGtlcm5lbFNpemUgKyAxO1xuICogY29uc3Qgb3V0U2hhcGUgPSBbc2VxdWVuY2VMZW5ndGgsIG91dHB1dFNpemUsIG91dHB1dFNpemUsIGZpbHRlcnNdO1xuICpcbiAqIGNvbnN0IGluaXRpYWxIID0gdGYuemVyb3Mob3V0U2hhcGUpO1xuICogY29uc3QgaW5pdGlhbEMgPSB0Zi56ZXJvcyhvdXRTaGFwZSk7XG4gKlxuICogY29uc3QgW28sIGgsIGNdID0gY2VsbC5jYWxsKFtpbnB1dCwgaW5pdGlhbEgsIGluaXRpYWxDXSwge30pO1xuICogYGBgXG4gKi9cbi8qKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ1JlY3VycmVudCcsIG5hbWVzcGFjZTogJ2xheWVycyd9ICovXG5leHBvcnQgZnVuY3Rpb24gY29udkxzdG0yZENlbGwoYXJnczogQ29udkxTVE0yRENlbGxBcmdzKSB7XG4gIHJldHVybiBuZXcgQ29udkxTVE0yRENlbGwoYXJncyk7XG59XG5cbi8qKlxuICogQmFzZSBjbGFzcyBmb3IgcmVjdXJyZW50IGxheWVycy5cbiAqXG4gKiBJbnB1dCBzaGFwZTpcbiAqICAgM0QgdGVuc29yIHdpdGggc2hhcGUgYFtiYXRjaFNpemUsIHRpbWVTdGVwcywgaW5wdXREaW1dYC5cbiAqXG4gKiBPdXRwdXQgc2hhcGU6XG4gKiAgIC0gaWYgYHJldHVyblN0YXRlYCwgYW4gQXJyYXkgb2YgdGVuc29ycyAoaS5lLiwgYHRmLlRlbnNvcmBzKS4gVGhlIGZpcnN0XG4gKiAgICAgdGVuc29yIGlzIHRoZSBvdXRwdXQuIFRoZSByZW1haW5pbmcgdGVuc29ycyBhcmUgdGhlIHN0YXRlcyBhdCB0aGVcbiAqICAgICBsYXN0IHRpbWUgc3RlcCwgZWFjaCB3aXRoIHNoYXBlIGBbYmF0Y2hTaXplLCB1bml0c11gLlxuICogICAtIGlmIGByZXR1cm5TZXF1ZW5jZXNgLCB0aGUgb3V0cHV0IHdpbGwgaGF2ZSBzaGFwZVxuICogICAgIGBbYmF0Y2hTaXplLCB0aW1lU3RlcHMsIHVuaXRzXWAuXG4gKiAgIC0gZWxzZSwgdGhlIG91dHB1dCB3aWxsIGhhdmUgc2hhcGUgYFtiYXRjaFNpemUsIHVuaXRzXWAuXG4gKlxuICogTWFza2luZzpcbiAqICAgVGhpcyBsYXllciBzdXBwb3J0cyBtYXNraW5nIGZvciBpbnB1dCBkYXRhIHdpdGggYSB2YXJpYWJsZSBudW1iZXJcbiAqICAgb2YgdGltZXN0ZXBzLiBUbyBpbnRyb2R1Y2UgbWFza3MgdG8geW91ciBkYXRhLFxuICogICB1c2UgYW4gZW1iZWRkaW5nIGxheWVyIHdpdGggdGhlIGBtYXNrX3plcm9gIHBhcmFtZXRlclxuICogICBzZXQgdG8gYFRydWVgLlxuICpcbiAqIE5vdGVzIG9uIHVzaW5nIHN0YXRlZnVsbmVzcyBpbiBSTk5zOlxuICogICBZb3UgY2FuIHNldCBSTk4gbGF5ZXJzIHRvIGJlICdzdGF0ZWZ1bCcsIHdoaWNoIG1lYW5zIHRoYXQgdGhlIHN0YXRlc1xuICogICBjb21wdXRlZCBmb3IgdGhlIHNhbXBsZXMgaW4gb25lIGJhdGNoIHdpbGwgYmUgcmV1c2VkIGFzIGluaXRpYWwgc3RhdGVzXG4gKiAgIGZvciB0aGUgc2FtcGxlcyBpbiB0aGUgbmV4dCBiYXRjaC4gVGhpcyBhc3N1bWVzIGEgb25lLXRvLW9uZSBtYXBwaW5nXG4gKiAgIGJldHdlZW4gc2FtcGxlcyBpbiBkaWZmZXJlbnQgc3VjY2Vzc2l2ZSBiYXRjaGVzLlxuICpcbiAqICAgVG8gZW5hYmxlIHN0YXRlZnVsbmVzczpcbiAqICAgICAtIHNwZWNpZnkgYHN0YXRlZnVsOiB0cnVlYCBpbiB0aGUgbGF5ZXIgY29uc3RydWN0b3IuXG4gKiAgICAgLSBzcGVjaWZ5IGEgZml4ZWQgYmF0Y2ggc2l6ZSBmb3IgeW91ciBtb2RlbCwgYnkgcGFzc2luZ1xuICogICAgICAgaWYgc2VxdWVudGlhbCBtb2RlbDpcbiAqICAgICAgICAgYGJhdGNoSW5wdXRTaGFwZT1bLi4uXWAgdG8gdGhlIGZpcnN0IGxheWVyIGluIHlvdXIgbW9kZWwuXG4gKiAgICAgICBlbHNlIGZvciBmdW5jdGlvbmFsIG1vZGVsIHdpdGggMSBvciBtb3JlIElucHV0IGxheWVyczpcbiAqICAgICAgICAgYGJhdGNoU2hhcGU9Wy4uLl1gIHRvIGFsbCB0aGUgZmlyc3QgbGF5ZXJzIGluIHlvdXIgbW9kZWwuXG4gKiAgICAgICBUaGlzIGlzIHRoZSBleHBlY3RlZCBzaGFwZSBvZiB5b3VyIGlucHV0cyAqaW5jbHVkaW5nIHRoZSBiYXRjaCBzaXplKi5cbiAqICAgICAgIEl0IHNob3VsZCBiZSBhIHR1cGxlIG9mIGludGVnZXJzLCBlLmcuIGAoMzIsIDEwLCAxMDApYC5cbiAqICAgICAtIHNwZWNpZnkgYHNodWZmbGU9RmFsc2VgIHdoZW4gY2FsbGluZyBmaXQoKS5cbiAqXG4gKiAgIFRvIHJlc2V0IHRoZSBzdGF0ZXMgb2YgeW91ciBtb2RlbCwgY2FsbCBgLnJlc2V0U3RhdGVzKClgIG9uIGVpdGhlclxuICogICBhIHNwZWNpZmljIGxheWVyLCBvciBvbiB5b3VyIGVudGlyZSBtb2RlbC5cbiAqXG4gKiBOb3RlIG9uIHNwZWNpZnlpbmcgdGhlIGluaXRpYWwgc3RhdGUgb2YgUk5Oc1xuICogICBZb3UgY2FuIHNwZWNpZnkgdGhlIGluaXRpYWwgc3RhdGUgb2YgUk5OIGxheWVycyBzeW1ib2xpY2FsbHkgYnlcbiAqICAgY2FsbGluZyB0aGVtIHdpdGggdGhlIG9wdGlvbiBgaW5pdGlhbFN0YXRlYC4gVGhlIHZhbHVlIG9mXG4gKiAgIGBpbml0aWFsU3RhdGVgIHNob3VsZCBiZSBhIHRlbnNvciBvciBsaXN0IG9mIHRlbnNvcnMgcmVwcmVzZW50aW5nXG4gKiAgIHRoZSBpbml0aWFsIHN0YXRlIG9mIHRoZSBSTk4gbGF5ZXIuXG4gKlxuICogICBZb3UgY2FuIHNwZWNpZnkgdGhlIGluaXRpYWwgc3RhdGUgb2YgUk5OIGxheWVycyBudW1lcmljYWxseSBieVxuICogICBjYWxsaW5nIGByZXNldFN0YXRlc2Agd2l0aCB0aGUga2V5d29yZCBhcmd1bWVudCBgc3RhdGVzYC4gVGhlIHZhbHVlIG9mXG4gKiAgIGBzdGF0ZXNgIHNob3VsZCBiZSBhIG51bXB5IGFycmF5IG9yIGxpc3Qgb2YgbnVtcHkgYXJyYXlzIHJlcHJlc2VudGluZ1xuICogICB0aGUgaW5pdGlhbCBzdGF0ZSBvZiB0aGUgUk5OIGxheWVyLlxuICpcbiAqIE5vdGUgb24gcGFzc2luZyBleHRlcm5hbCBjb25zdGFudHMgdG8gUk5Oc1xuICogICBZb3UgY2FuIHBhc3MgXCJleHRlcm5hbFwiIGNvbnN0YW50cyB0byB0aGUgY2VsbCB1c2luZyB0aGUgYGNvbnN0YW50c2BcbiAqICAga2V5d29yZCBhcmd1bWVudCBvZiBgUk5OLmNhbGxgIG1ldGhvZC4gVGhpcyByZXF1aXJlcyB0aGF0IHRoZSBgY2VsbC5jYWxsYFxuICogICBtZXRob2QgYWNjZXB0cyB0aGUgc2FtZSBrZXl3b3JkIGFyZ3VtZW50IGBjb25zdGFudHNgLiBTdWNoIGNvbnN0YW50c1xuICogICBjYW4gYmUgdXNlZCB0byBjb25kaXRpb24gdGhlIGNlbGwgdHJhbnNmb3JtYXRpb24gb24gYWRkaXRpb25hbCBzdGF0aWNcbiAqICAgaW5wdXRzIChub3QgY2hhbmdpbmcgb3ZlciB0aW1lKSwgYS5rLmEuIGFuIGF0dGVudGlvbiBtZWNoYW5pc20uXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdSZWN1cnJlbnQnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gcm5uKGFyZ3M6IFJOTkxheWVyQXJncykge1xuICByZXR1cm4gbmV3IFJOTihhcmdzKTtcbn1cblxuLyoqXG4gKiBXcmFwcGVyIGFsbG93aW5nIGEgc3RhY2sgb2YgUk5OIGNlbGxzIHRvIGJlaGF2ZSBhcyBhIHNpbmdsZSBjZWxsLlxuICpcbiAqIFVzZWQgdG8gaW1wbGVtZW50IGVmZmljaWVudCBzdGFja2VkIFJOTnMuXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdSZWN1cnJlbnQnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gc3RhY2tlZFJOTkNlbGxzKGFyZ3M6IFN0YWNrZWRSTk5DZWxsc0FyZ3Mpe1xuICByZXR1cm4gbmV3IFN0YWNrZWRSTk5DZWxscyhhcmdzKTtcbn1cblxuLy8gV3JhcHBlciBMYXllcnMuXG5cbi8qKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ1dyYXBwZXInLCBuYW1lc3BhY2U6ICdsYXllcnMnfSAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJpZGlyZWN0aW9uYWwoYXJnczogQmlkaXJlY3Rpb25hbExheWVyQXJncykge1xuICByZXR1cm4gbmV3IEJpZGlyZWN0aW9uYWwoYXJncyk7XG59XG5cbi8qKlxuICogVGhpcyB3cmFwcGVyIGFwcGxpZXMgYSBsYXllciB0byBldmVyeSB0ZW1wb3JhbCBzbGljZSBvZiBhbiBpbnB1dC5cbiAqXG4gKiBUaGUgaW5wdXQgc2hvdWxkIGJlIGF0IGxlYXN0IDNELCAgYW5kIHRoZSBkaW1lbnNpb24gb2YgdGhlIGluZGV4IGAxYCB3aWxsIGJlXG4gKiBjb25zaWRlcmVkIHRvIGJlIHRoZSB0ZW1wb3JhbCBkaW1lbnNpb24uXG4gKlxuICogQ29uc2lkZXIgYSBiYXRjaCBvZiAzMiBzYW1wbGVzLCB3aGVyZSBlYWNoIHNhbXBsZSBpcyBhIHNlcXVlbmNlIG9mIDEwIHZlY3RvcnNcbiAqIG9mIDE2IGRpbWVuc2lvbnMuIFRoZSBiYXRjaCBpbnB1dCBzaGFwZSBvZiB0aGUgbGF5ZXIgaXMgdGhlbiBgWzMyLCAgMTAsXG4gKiAxNl1gLCBhbmQgdGhlIGBpbnB1dFNoYXBlYCwgbm90IGluY2x1ZGluZyB0aGUgc2FtcGxlIGRpbWVuc2lvbiwgaXNcbiAqIGBbMTAsIDE2XWAuXG4gKlxuICogWW91IGNhbiB0aGVuIHVzZSBgVGltZURpc3RyaWJ1dGVkYCB0byBhcHBseSBhIGBEZW5zZWAgbGF5ZXIgdG8gZWFjaCBvZiB0aGUgMTBcbiAqIHRpbWVzdGVwcywgaW5kZXBlbmRlbnRseTpcbiAqXG4gKiBgYGBqc1xuICogY29uc3QgbW9kZWwgPSB0Zi5zZXF1ZW50aWFsKCk7XG4gKiBtb2RlbC5hZGQodGYubGF5ZXJzLnRpbWVEaXN0cmlidXRlZCh7XG4gKiAgIGxheWVyOiB0Zi5sYXllcnMuZGVuc2Uoe3VuaXRzOiA4fSksXG4gKiAgIGlucHV0U2hhcGU6IFsxMCwgMTZdLFxuICogfSkpO1xuICpcbiAqIC8vIE5vdyBtb2RlbC5vdXRwdXRTaGFwZSA9IFtudWxsLCAxMCwgOF0uXG4gKiAvLyBUaGUgb3V0cHV0IHdpbGwgdGhlbiBoYXZlIHNoYXBlIGBbMzIsIDEwLCA4XWAuXG4gKlxuICogLy8gSW4gc3Vic2VxdWVudCBsYXllcnMsIHRoZXJlIGlzIG5vIG5lZWQgZm9yIGBpbnB1dFNoYXBlYDpcbiAqIG1vZGVsLmFkZCh0Zi5sYXllcnMudGltZURpc3RyaWJ1dGVkKHtsYXllcjogdGYubGF5ZXJzLmRlbnNlKHt1bml0czogMzJ9KX0pKTtcbiAqIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KG1vZGVsLm91dHB1dHNbMF0uc2hhcGUpKTtcbiAqIC8vIE5vdyBtb2RlbC5vdXRwdXRTaGFwZSA9IFtudWxsLCAxMCwgMzJdLlxuICogYGBgXG4gKlxuICogVGhlIG91dHB1dCB3aWxsIHRoZW4gaGF2ZSBzaGFwZSBgWzMyLCAxMCwgMzJdYC5cbiAqXG4gKiBgVGltZURpc3RyaWJ1dGVkYCBjYW4gYmUgdXNlZCB3aXRoIGFyYml0cmFyeSBsYXllcnMsIG5vdCBqdXN0IGBEZW5zZWAsIGZvclxuICogaW5zdGFuY2UgYSBgQ29udjJEYCBsYXllci5cbiAqXG4gKiBgYGBqc1xuICogY29uc3QgbW9kZWwgPSB0Zi5zZXF1ZW50aWFsKCk7XG4gKiBtb2RlbC5hZGQodGYubGF5ZXJzLnRpbWVEaXN0cmlidXRlZCh7XG4gKiAgIGxheWVyOiB0Zi5sYXllcnMuY29udjJkKHtmaWx0ZXJzOiA2NCwga2VybmVsU2l6ZTogWzMsIDNdfSksXG4gKiAgIGlucHV0U2hhcGU6IFsxMCwgMjk5LCAyOTksIDNdLFxuICogfSkpO1xuICogY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkobW9kZWwub3V0cHV0c1swXS5zaGFwZSkpO1xuICogYGBgXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdXcmFwcGVyJywgbmFtZXNwYWNlOiAnbGF5ZXJzJ31cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHRpbWVEaXN0cmlidXRlZChhcmdzOiBXcmFwcGVyTGF5ZXJBcmdzKSB7XG4gIHJldHVybiBuZXcgVGltZURpc3RyaWJ1dGVkKGFyZ3MpO1xufVxuXG4vLyBBbGlhc2VzIGZvciBwb29saW5nLlxuZXhwb3J0IGNvbnN0IGdsb2JhbE1heFBvb2wxZCA9IGdsb2JhbE1heFBvb2xpbmcxZDtcbmV4cG9ydCBjb25zdCBnbG9iYWxNYXhQb29sMmQgPSBnbG9iYWxNYXhQb29saW5nMmQ7XG5leHBvcnQgY29uc3QgbWF4UG9vbDFkID0gbWF4UG9vbGluZzFkO1xuZXhwb3J0IGNvbnN0IG1heFBvb2wyZCA9IG1heFBvb2xpbmcyZDtcblxuZXhwb3J0IHtMYXllciwgUk5OLCBSTk5DZWxsLCBpbnB1dCAvKiBhbGlhcyBmb3IgdGYuaW5wdXQgKi99O1xuXG4vKipcbiAqIEFwcGx5IGFkZGl0aXZlIHplcm8tY2VudGVyZWQgR2F1c3NpYW4gbm9pc2UuXG4gKlxuICogQXMgaXQgaXMgYSByZWd1bGFyaXphdGlvbiBsYXllciwgaXQgaXMgb25seSBhY3RpdmUgYXQgdHJhaW5pbmcgdGltZS5cbiAqXG4gKiBUaGlzIGlzIHVzZWZ1bCB0byBtaXRpZ2F0ZSBvdmVyZml0dGluZ1xuICogKHlvdSBjb3VsZCBzZWUgaXQgYXMgYSBmb3JtIG9mIHJhbmRvbSBkYXRhIGF1Z21lbnRhdGlvbikuXG4gKiBHYXVzc2lhbiBOb2lzZSAoR1MpIGlzIGEgbmF0dXJhbCBjaG9pY2UgYXMgY29ycnVwdGlvbiBwcm9jZXNzXG4gKiBmb3IgcmVhbCB2YWx1ZWQgaW5wdXRzLlxuICpcbiAqICMgQXJndW1lbnRzXG4gKiBzdGRkZXY6IGZsb2F0LCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIG5vaXNlIGRpc3RyaWJ1dGlvbi5cbiAqXG4gKiAjIElucHV0IHNoYXBlXG4gKiBBcmJpdHJhcnkuIFVzZSB0aGUga2V5d29yZCBhcmd1bWVudCBgaW5wdXRfc2hhcGVgXG4gKiAodHVwbGUgb2YgaW50ZWdlcnMsIGRvZXMgbm90IGluY2x1ZGUgdGhlIHNhbXBsZXMgYXhpcylcbiAqIHdoZW4gdXNpbmcgdGhpcyBsYXllciBhcyB0aGUgZmlyc3QgbGF5ZXIgaW4gYSBtb2RlbC5cbiAqXG4gKiAjIE91dHB1dCBzaGFwZVxuICogU2FtZSBzaGFwZSBhcyBpbnB1dC5cbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ05vaXNlJywgbmFtZXNwYWNlOiAnbGF5ZXJzJ31cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdhdXNzaWFuTm9pc2UoYXJnczogR2F1c3NpYW5Ob2lzZUFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBHYXVzc2lhbk5vaXNlKGFyZ3MpO1xufVxuXG4vKipcbiAqIEFwcGx5IG11bHRpcGxpY2F0aXZlIDEtY2VudGVyZWQgR2F1c3NpYW4gbm9pc2UuXG4gKlxuICogQXMgaXQgaXMgYSByZWd1bGFyaXphdGlvbiBsYXllciwgaXQgaXMgb25seSBhY3RpdmUgYXQgdHJhaW5pbmcgdGltZS5cbiAqXG4gKiBBcmd1bWVudHM6XG4gKiAgIC0gYHJhdGVgOiBmbG9hdCwgZHJvcCBwcm9iYWJpbGl0eSAoYXMgd2l0aCBgRHJvcG91dGApLlxuICogICAgIFRoZSBtdWx0aXBsaWNhdGl2ZSBub2lzZSB3aWxsIGhhdmVcbiAqICAgICBzdGFuZGFyZCBkZXZpYXRpb24gYHNxcnQocmF0ZSAvICgxIC0gcmF0ZSkpYC5cbiAqXG4gKiBJbnB1dCBzaGFwZTpcbiAqICAgQXJiaXRyYXJ5LiBVc2UgdGhlIGtleXdvcmQgYXJndW1lbnQgYGlucHV0U2hhcGVgXG4gKiAgICh0dXBsZSBvZiBpbnRlZ2VycywgZG9lcyBub3QgaW5jbHVkZSB0aGUgc2FtcGxlcyBheGlzKVxuICogICB3aGVuIHVzaW5nIHRoaXMgbGF5ZXIgYXMgdGhlIGZpcnN0IGxheWVyIGluIGEgbW9kZWwuXG4gKlxuICogT3V0cHV0IHNoYXBlOlxuICogICBTYW1lIHNoYXBlIGFzIGlucHV0LlxuICpcbiAqIFJlZmVyZW5jZXM6XG4gKiAgIC0gW0Ryb3BvdXQ6IEEgU2ltcGxlIFdheSB0byBQcmV2ZW50IE5ldXJhbCBOZXR3b3JrcyBmcm9tIE92ZXJmaXR0aW5nXShcbiAqICAgICAgaHR0cDovL3d3dy5jcy50b3JvbnRvLmVkdS9+cnNhbGFraHUvcGFwZXJzL3NyaXZhc3RhdmExNGEucGRmKVxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdMYXllcnMnLCBzdWJoZWFkaW5nOiAnTm9pc2UnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2F1c3NpYW5Ecm9wb3V0KGFyZ3M6IEdhdXNzaWFuRHJvcG91dEFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBHYXVzc2lhbkRyb3BvdXQoYXJncyk7XG59XG5cbi8qKlxuICogQXBwbGllcyBBbHBoYSBEcm9wb3V0IHRvIHRoZSBpbnB1dC5cbiAqXG4gKiBBcyBpdCBpcyBhIHJlZ3VsYXJpemF0aW9uIGxheWVyLCBpdCBpcyBvbmx5IGFjdGl2ZSBhdCB0cmFpbmluZyB0aW1lLlxuICpcbiAqIEFscGhhIERyb3BvdXQgaXMgYSBgRHJvcG91dGAgdGhhdCBrZWVwcyBtZWFuIGFuZCB2YXJpYW5jZSBvZiBpbnB1dHNcbiAqIHRvIHRoZWlyIG9yaWdpbmFsIHZhbHVlcywgaW4gb3JkZXIgdG8gZW5zdXJlIHRoZSBzZWxmLW5vcm1hbGl6aW5nIHByb3BlcnR5XG4gKiBldmVuIGFmdGVyIHRoaXMgZHJvcG91dC5cbiAqIEFscGhhIERyb3BvdXQgZml0cyB3ZWxsIHRvIFNjYWxlZCBFeHBvbmVudGlhbCBMaW5lYXIgVW5pdHNcbiAqIGJ5IHJhbmRvbWx5IHNldHRpbmcgYWN0aXZhdGlvbnMgdG8gdGhlIG5lZ2F0aXZlIHNhdHVyYXRpb24gdmFsdWUuXG4gKlxuICogQXJndW1lbnRzOlxuICogICAtIGByYXRlYDogZmxvYXQsIGRyb3AgcHJvYmFiaWxpdHkgKGFzIHdpdGggYERyb3BvdXRgKS5cbiAqICAgICBUaGUgbXVsdGlwbGljYXRpdmUgbm9pc2Ugd2lsbCBoYXZlXG4gKiAgICAgc3RhbmRhcmQgZGV2aWF0aW9uIGBzcXJ0KHJhdGUgLyAoMSAtIHJhdGUpKWAuXG4gKiAgIC0gYG5vaXNlX3NoYXBlYDogQSAxLUQgYFRlbnNvcmAgb2YgdHlwZSBgaW50MzJgLCByZXByZXNlbnRpbmcgdGhlXG4gKiAgICAgc2hhcGUgZm9yIHJhbmRvbWx5IGdlbmVyYXRlZCBrZWVwL2Ryb3AgZmxhZ3MuXG4gKlxuICogSW5wdXQgc2hhcGU6XG4gKiAgIEFyYml0cmFyeS4gVXNlIHRoZSBrZXl3b3JkIGFyZ3VtZW50IGBpbnB1dFNoYXBlYFxuICogICAodHVwbGUgb2YgaW50ZWdlcnMsIGRvZXMgbm90IGluY2x1ZGUgdGhlIHNhbXBsZXMgYXhpcylcbiAqICAgd2hlbiB1c2luZyB0aGlzIGxheWVyIGFzIHRoZSBmaXJzdCBsYXllciBpbiBhIG1vZGVsLlxuICpcbiAqIE91dHB1dCBzaGFwZTpcbiAqICAgU2FtZSBzaGFwZSBhcyBpbnB1dC5cbiAqXG4gKiBSZWZlcmVuY2VzOlxuICogICAtIFtTZWxmLU5vcm1hbGl6aW5nIE5ldXJhbCBOZXR3b3Jrc10oaHR0cHM6Ly9hcnhpdi5vcmcvYWJzLzE3MDYuMDI1MTUpXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdOb2lzZScsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhbHBoYURyb3BvdXQoYXJnczogQWxwaGFEcm9wb3V0QXJncykge1xuICByZXR1cm4gbmV3IEFscGhhRHJvcG91dChhcmdzKTtcbn1cblxuLyoqXG4gKiBNYXNrcyBhIHNlcXVlbmNlIGJ5IHVzaW5nIGEgbWFzayB2YWx1ZSB0byBza2lwIHRpbWVzdGVwcy5cbiAqXG4gKiBJZiBhbGwgZmVhdHVyZXMgZm9yIGEgZ2l2ZW4gc2FtcGxlIHRpbWVzdGVwIGFyZSBlcXVhbCB0byBgbWFza192YWx1ZWAsXG4gKiB0aGVuIHRoZSBzYW1wbGUgdGltZXN0ZXAgd2lsbCBiZSBtYXNrZWQgKHNraXBwZWQpIGluIGFsbCBkb3duc3RyZWFtIGxheWVyc1xuICogKGFzIGxvbmcgYXMgdGhleSBzdXBwb3J0IG1hc2tpbmcpLlxuICpcbiAqIElmIGFueSBkb3duc3RyZWFtIGxheWVyIGRvZXMgbm90IHN1cHBvcnQgbWFza2luZyB5ZXQgcmVjZWl2ZXMgc3VjaFxuICogYW4gaW5wdXQgbWFzaywgYW4gZXhjZXB0aW9uIHdpbGwgYmUgcmFpc2VkLlxuICpcbiAqIEFyZ3VtZW50czpcbiAqICAgLSBgbWFza1ZhbHVlYDogRWl0aGVyIE5vbmUgb3IgbWFzayB2YWx1ZSB0byBza2lwLlxuICpcbiAqIElucHV0IHNoYXBlOlxuICogICBBcmJpdHJhcnkuIFVzZSB0aGUga2V5d29yZCBhcmd1bWVudCBgaW5wdXRTaGFwZWBcbiAqICAgKHR1cGxlIG9mIGludGVnZXJzLCBkb2VzIG5vdCBpbmNsdWRlIHRoZSBzYW1wbGVzIGF4aXMpXG4gKiAgIHdoZW4gdXNpbmcgdGhpcyBsYXllciBhcyB0aGUgZmlyc3QgbGF5ZXIgaW4gYSBtb2RlbC5cbiAqXG4gKiBPdXRwdXQgc2hhcGU6XG4gKiAgIFNhbWUgc2hhcGUgYXMgaW5wdXQuXG4gKlxuICogQGRvYyB7aGVhZGluZzogJ0xheWVycycsIHN1YmhlYWRpbmc6ICdNYXNrJywgbmFtZXNwYWNlOiAnbGF5ZXJzJ31cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG1hc2tpbmcoYXJncz86IE1hc2tpbmdBcmdzKSB7XG4gIHJldHVybiBuZXcgTWFza2luZyhhcmdzKTtcbn1cblxuLyoqXG4gKiBBIHByZXByb2Nlc3NpbmcgbGF5ZXIgd2hpY2ggcmVzY2FsZXMgaW5wdXQgdmFsdWVzIHRvIGEgbmV3IHJhbmdlLlxuICpcbiAqIFRoaXMgbGF5ZXIgcmVzY2FsZXMgZXZlcnkgdmFsdWUgb2YgYW4gaW5wdXQgKG9mdGVuIGFuIGltYWdlKSBieSBtdWx0aXBseWluZ1xuICogYnkgYHNjYWxlYCBhbmQgYWRkaW5nIGBvZmZzZXRgLlxuICpcbiAqIEZvciBpbnN0YW5jZTpcbiAqIDEuIFRvIHJlc2NhbGUgYW4gaW5wdXQgaW4gdGhlIGBgWzAsIDI1NV1gYCByYW5nZVxuICogdG8gYmUgaW4gdGhlIGBbMCwgMV1gIHJhbmdlLCB5b3Ugd291bGQgcGFzcyBgc2NhbGU9MS8yNTVgLlxuICogMi4gVG8gcmVzY2FsZSBhbiBpbnB1dCBpbiB0aGUgYGBbMCwgMjU1XWBgIHJhbmdlIHRvIGJlIGluIHRoZSBgWy0xLCAxXWBcbiAqIHJhbmdlLCB5b3Ugd291bGQgcGFzcyBgc2NhbGU9MS4vMTI3LjUsIG9mZnNldD0tMWAuXG4gKiBUaGUgcmVzY2FsaW5nIGlzIGFwcGxpZWQgYm90aCBkdXJpbmcgdHJhaW5pbmcgYW5kIGluZmVyZW5jZS4gSW5wdXRzIGNhbiBiZVxuICogb2YgaW50ZWdlciBvciBmbG9hdGluZyBwb2ludCBkdHlwZSwgYW5kIGJ5IGRlZmF1bHQgdGhlIGxheWVyIHdpbGwgb3V0cHV0XG4gKiBmbG9hdHMuXG4gKlxuICogQXJndW1lbnRzOlxuICogICAtIGBzY2FsZWA6IEZsb2F0LCB0aGUgc2NhbGUgdG8gYXBwbHkgdG8gdGhlIGlucHV0cy5cbiAqICAgLSBgb2Zmc2V0YDogRmxvYXQsIHRoZSBvZmZzZXQgdG8gYXBwbHkgdG8gdGhlIGlucHV0cy5cbiAqXG4gKiBJbnB1dCBzaGFwZTpcbiAqICAgQXJiaXRyYXJ5LlxuICpcbiAqIE91dHB1dCBzaGFwZTpcbiAqICAgU2FtZSBhcyBpbnB1dC5cbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ1Jlc2NhbGluZycsIG5hbWVzcGFjZTogJ2xheWVycyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNjYWxpbmcoYXJncz86IFJlc2NhbGluZ0FyZ3MpIHtcbiAgcmV0dXJuIG5ldyBSZXNjYWxpbmcoYXJncyk7XG59XG5cbi8qKlxuICogIEEgcHJlcHJvY2Vzc2luZyBsYXllciB3aGljaCBjZW50ZXIgY3JvcHMgaW1hZ2VzLlxuICpcbiAqICAgVGhpcyBsYXllcnMgY3JvcHMgdGhlIGNlbnRyYWwgcG9ydGlvbiBvZiB0aGUgaW1hZ2VzIHRvIGEgdGFyZ2V0IHNpemUuIElmIGFuXG4gKiAgIGltYWdlIGlzIHNtYWxsZXIgdGhhbiB0aGUgdGFyZ2V0IHNpemUsIGl0IHdpbGwgYmUgcmVzaXplZCBhbmQgY3JvcHBlZCBzbyBhc1xuICogICB0byByZXR1cm4gdGhlIGxhcmdlc3QgcG9zc2libGUgd2luZG93IGluIHRoZSBpbWFnZSB0aGF0IG1hdGNoZXMgdGhlIHRhcmdldFxuICogICBhc3BlY3QgcmF0aW8uXG4gKlxuICogICBJbnB1dCBwaXhlbCB2YWx1ZXMgY2FuIGJlIG9mIGFueSByYW5nZSAoZS5nLiBgWzAuLCAxLilgIG9yIGBbMCwgMjU1XWApIGFuZFxuICogICBvZiBpbnRlZ2VyIG9yIGZsb2F0aW5nIHBvaW50IGR0eXBlLlxuICpcbiAqICAgSWYgdGhlIGlucHV0IGhlaWdodC93aWR0aCBpcyBldmVuIGFuZCB0aGUgdGFyZ2V0IGhlaWdodC93aWR0aCBpcyBvZGQgKG9yXG4gKiAgIGludmVyc2VseSksIHRoZSBpbnB1dCBpbWFnZSBpcyBsZWZ0LXBhZGRlZCBieSAxIHBpeGVsLlxuICpcbiAqICAgQXJndW1lbnRzOlxuICogICAgIGBoZWlnaHRgOiBJbnRlZ2VyLCB0aGUgaGVpZ2h0IG9mIHRoZSBvdXRwdXQgc2hhcGUuXG4gKiAgICAgYHdpZHRoYDogSW50ZWdlciwgdGhlIHdpZHRoIG9mIHRoZSBvdXRwdXQgc2hhcGUuXG4gKlxuICogICBJbnB1dCBzaGFwZTpcbiAqICAgICAzRCAodW5iYXRjaGVkKSBvciA0RCAoYmF0Y2hlZCkgdGVuc29yIHdpdGggc2hhcGU6XG4gKiAgICAgYCguLi4sIGhlaWdodCwgd2lkdGgsIGNoYW5uZWxzKWAsIGluIGBjaGFubmVsc0xhc3RgIGZvcm1hdC5cbiAqXG4gKiAgIE91dHB1dCBzaGFwZTpcbiAqICAgICAzRCAodW5iYXRjaGVkKSBvciA0RCAoYmF0Y2hlZCkgdGVuc29yIHdpdGggc2hhcGU6XG4gKiAgICAgYCguLi4sIHRhcmdldEhlaWdodCwgdGFyZ2V0V2lkdGgsIGNoYW5uZWxzKWAuXG4gKlxuICpcbiAqICBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ0NlbnRlckNyb3AnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gY2VudGVyQ3JvcChhcmdzPzogQ2VudGVyQ3JvcEFyZ3MpIHtcbiAgIHJldHVybiBuZXcgQ2VudGVyQ3JvcChhcmdzKTtcbiAgfVxuXG4vKipcbiAqIEEgcHJlcHJvY2Vzc2luZyBsYXllciB3aGljaCByZXNpemVzIGltYWdlcy5cbiAqIFRoaXMgbGF5ZXIgcmVzaXplcyBhbiBpbWFnZSBpbnB1dCB0byBhIHRhcmdldCBoZWlnaHQgYW5kIHdpZHRoLiBUaGUgaW5wdXRcbiAqIHNob3VsZCBiZSBhIDREIChiYXRjaGVkKSBvciAzRCAodW5iYXRjaGVkKSB0ZW5zb3IgaW4gYFwiY2hhbm5lbHNfbGFzdFwiYFxuICogZm9ybWF0LiAgSW5wdXQgcGl4ZWwgdmFsdWVzIGNhbiBiZSBvZiBhbnkgcmFuZ2UgKGUuZy4gYFswLiwgMS4pYCBvciBgWzAsXG4gKiAyNTVdYCkgYW5kIG9mIGludGVyZ2VyIG9yIGZsb2F0aW5nIHBvaW50IGR0eXBlLiBCeSBkZWZhdWx0LCB0aGUgbGF5ZXIgd2lsbFxuICogb3V0cHV0IGZsb2F0cy5cbiAqXG4gKiBBcmd1bWVudHM6XG4gKiAgIC0gYGhlaWdodGA6IG51bWJlciwgdGhlIGhlaWdodCBmb3IgdGhlIG91dHB1dCB0ZW5zb3IuXG4gKiAgIC0gYHdpZHRoYDogbnVtYmVyLCB0aGUgd2lkdGggZm9yIHRoZSBvdXRwdXQgdGVuc29yLlxuICogICAtIGBpbnRlcnBvbGF0aW9uYDogc3RyaW5nLCB0aGUgbWV0aG9kIGZvciBpbWFnZSByZXNpemluZyBpbnRlcnBvbGF0aW9uLlxuICogICAtIGBjcm9wVG9Bc3BlY3RSYXRpb2A6IGJvb2xlYW4sIHdoZXRoZXIgdG8ga2VlcCBpbWFnZSBhc3BlY3QgcmF0aW8uXG4gKlxuICogSW5wdXQgc2hhcGU6XG4gKiAgIEFyYml0cmFyeS5cbiAqXG4gKiBPdXRwdXQgc2hhcGU6XG4gKiAgIGhlaWdodCwgd2lkdGgsIG51bSBjaGFubmVscy5cbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ1Jlc2l6aW5nJywgbmFtZXNwYWNlOiAnbGF5ZXJzJ31cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc2l6aW5nKGFyZ3M/OiBSZXNpemluZ0FyZ3MpIHtcbiAgcmV0dXJuIG5ldyBSZXNpemluZyhhcmdzKTtcbn1cblxuLyoqXG4gKiBBIHByZXByb2Nlc3NpbmcgbGF5ZXIgd2hpY2ggZW5jb2RlcyBpbnRlZ2VyIGZlYXR1cmVzLlxuICpcbiAqIFRoaXMgbGF5ZXIgcHJvdmlkZXMgb3B0aW9ucyBmb3IgY29uZGVuc2luZyBkYXRhIGludG8gYSBjYXRlZ29yaWNhbCBlbmNvZGluZ1xuICogd2hlbiB0aGUgdG90YWwgbnVtYmVyIG9mIHRva2VucyBhcmUga25vd24gaW4gYWR2YW5jZS4gSXQgYWNjZXB0cyBpbnRlZ2VyXG4gKiB2YWx1ZXMgYXMgaW5wdXRzLCBhbmQgaXQgb3V0cHV0cyBhIGRlbnNlIHJlcHJlc2VudGF0aW9uIG9mIHRob3NlXG4gKiBpbnB1dHMuXG4gKlxuICogQXJndW1lbnRzOlxuICpcbiAqIG51bVRva2VuczogVGhlIHRvdGFsIG51bWJlciBvZiB0b2tlbnMgdGhlIGxheWVyIHNob3VsZCBzdXBwb3J0LiBBbGxcbiAqICBpbnB1dHMgdG8gdGhlIGxheWVyIG11c3QgaW50ZWdlcnMgaW4gdGhlIHJhbmdlIGAwIDw9IHZhbHVlIDxcbiAqICBudW1Ub2tlbnNgLCBvciBhbiBlcnJvciB3aWxsIGJlIHRocm93bi5cbiAqXG4gKiBvdXRwdXRNb2RlOiBTcGVjaWZpY2F0aW9uIGZvciB0aGUgb3V0cHV0IG9mIHRoZSBsYXllci5cbiAqICBEZWZhdWx0cyB0byBgbXVsdGlIb3RgLiBWYWx1ZXMgY2FuIGJlIGBvbmVIb3RgLCBgbXVsdGlIb3RgIG9yXG4gKiAgYGNvdW50YCwgY29uZmlndXJpbmcgdGhlIGxheWVyIGFzIGZvbGxvd3M6XG4gKlxuICogICAgb25lSG90OiBFbmNvZGVzIGVhY2ggaW5kaXZpZHVhbCBlbGVtZW50IGluIHRoZSBpbnB1dCBpbnRvIGFuXG4gKiAgICAgIGFycmF5IG9mIGBudW1Ub2tlbnNgIHNpemUsIGNvbnRhaW5pbmcgYSAxIGF0IHRoZSBlbGVtZW50IGluZGV4LiBJZlxuICogICAgICB0aGUgbGFzdCBkaW1lbnNpb24gaXMgc2l6ZSAxLCB3aWxsIGVuY29kZSBvbiB0aGF0IGRpbWVuc2lvbi4gSWYgdGhlXG4gKiAgICAgIGxhc3QgZGltZW5zaW9uIGlzIG5vdCBzaXplIDEsIHdpbGwgYXBwZW5kIGEgbmV3IGRpbWVuc2lvbiBmb3IgdGhlXG4gKiAgICAgIGVuY29kZWQgb3V0cHV0LlxuICpcbiAqICAgIG11bHRpSG90OiBFbmNvZGVzIGVhY2ggc2FtcGxlIGluIHRoZSBpbnB1dCBpbnRvIGEgc2luZ2xlIGFycmF5XG4gKiAgICAgb2YgYG51bVRva2Vuc2Agc2l6ZSwgY29udGFpbmluZyBhIDEgZm9yIGVhY2ggdm9jYWJ1bGFyeSB0ZXJtXG4gKiAgICAgcHJlc2VudCBpbiB0aGUgc2FtcGxlLiBUcmVhdHMgdGhlIGxhc3QgZGltZW5zaW9uIGFzIHRoZSBzYW1wbGVcbiAqICAgICBkaW1lbnNpb24sIGlmIGlucHV0IHNoYXBlIGlzIGAoLi4uLCBzYW1wbGVMZW5ndGgpYCwgb3V0cHV0IHNoYXBlXG4gKiAgICAgd2lsbCBiZSBgKC4uLiwgbnVtVG9rZW5zKWAuXG4gKlxuICogICAgY291bnQ6IExpa2UgYG11bHRpSG90YCwgYnV0IHRoZSBpbnQgYXJyYXkgY29udGFpbnMgYSBjb3VudCBvZlxuICogICAgIHRoZSBudW1iZXIgb2YgdGltZXMgdGhlIHRva2VuIGF0IHRoYXQgaW5kZXggYXBwZWFyZWQgaW4gdGhlIHNhbXBsZS5cbiAqXG4gKiAgRm9yIGFsbCBvdXRwdXQgbW9kZXMsIGN1cnJlbnRseSBvbmx5IG91dHB1dCB1cCB0byByYW5rIDIgaXMgc3VwcG9ydGVkLlxuICogICBDYWxsIGFyZ3VtZW50czpcbiAqICAgIGlucHV0czogQSAxRCBvciAyRCB0ZW5zb3Igb2YgaW50ZWdlciBpbnB1dHMuXG4gKiAgICBjb3VudFdlaWdodHM6IEEgdGVuc29yIGluIHRoZSBzYW1lIHNoYXBlIGFzIGBpbnB1dHNgIGluZGljYXRpbmcgdGhlXG4gKiAgICB3ZWlnaHQgZm9yIGVhY2ggc2FtcGxlIHZhbHVlIHdoZW4gc3VtbWluZyB1cCBpbiBgY291bnRgIG1vZGUuIE5vdCB1c2VkXG4gKiAgICBpbiBgbXVsdGlIb3RgIG9yIGBvbmVIb3RgIG1vZGVzLlxuICpcbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ0NhdGVnb3J5RW5jb2RpbmcnLCBuYW1lc3BhY2U6ICdsYXllcnMnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gY2F0ZWdvcnlFbmNvZGluZyhhcmdzOiBDYXRlZ29yeUVuY29kaW5nQXJncykge1xuICByZXR1cm4gbmV3IENhdGVnb3J5RW5jb2RpbmcoYXJncyk7XG59XG5cbiAvKipcbiAgKiBBIHByZXByb2Nlc3NpbmcgbGF5ZXIgd2hpY2ggcmFuZG9tbHkgdmFyaWVzIGltYWdlIHdpZHRoIGR1cmluZyB0cmFpbmluZy5cbiAgKlxuICAqIFRoaXMgbGF5ZXIgd2lsbCByYW5kb21seSBhZGp1c3RzIHRoZSB3aWR0aCBvZiBhIGJhdGNoIG9mIGltYWdlcyBvZiBhIGJhdGNoXG4gICogb2YgaW1hZ2VzIGJ5IGEgcmFuZG9tIGZhY3Rvci5cbiAgKlxuICAqIFRoZSBpbnB1dCBzaG91bGQgYmUgYSAzRCAodW5iYXRjaGVkKSBvciA0RCAoYmF0Y2hlZCkgdGVuc29yIGluXG4gICogdGhlIGBcImNoYW5uZWxzX2xhc3RcImAgaW1hZ2UgZGF0YSBmb3JtYXQuIElucHV0IHBpeGVsIHZhbHVlcyBjYW4gYmUgb2YgYW55XG4gICogcmFuZ2UgKGUuZy4gYFswLiwgMS4pYCBvciBgWzAsIDI1NV1gKSBhbmQgb2YgaW50ZWdlciBvciBmbG9hdGluZyBwb2ludFxuICAqIGR0eXBlLiBCeSBkZWZhdWx0LCB0aGUgbGF5ZXIgd2lsbCBvdXRwdXQgZmxvYXRzLiBCeSBkZWZhdWx0LCB0aGlzIGxheWVyIGlzXG4gICogaW5hY3RpdmUgZHVyaW5nIGluZmVyZW5jZS4gRm9yIGFuIG92ZXJ2aWV3IGFuZCBmdWxsIGxpc3Qgb2YgcHJlcHJvY2Vzc2luZ1xuICAqIGxheWVycywgc2VlIHRoZSBwcmVwcm9jZXNzaW5nIFtndWlkZV1cbiAgKiAoaHR0cHM6Ly93d3cudGVuc29yZmxvdy5vcmcvZ3VpZGUva2VyYXMvcHJlcHJvY2Vzc2luZ19sYXllcnMpLlxuICAqXG4gICogQXJndW1lbnRzOlxuICAqXG4gICogZmFjdG9yOlxuICAqICAgQSBwb3NpdGl2ZSBmbG9hdCAoZnJhY3Rpb24gb2Ygb3JpZ2luYWwgd2lkdGgpLCBvciBhIHR1cGxlIG9mIHNpemUgMlxuICAqICAgcmVwcmVzZW50aW5nIGxvd2VyIGFuZCB1cHBlciBib3VuZCBmb3IgcmVzaXppbmcgdmVydGljYWxseS5cbiAgKiAgIFdoZW4gcmVwcmVzZW50ZWQgYXMgYSBzaW5nbGUgZmxvYXQsIHRoaXMgdmFsdWUgaXMgdXNlZCBmb3IgYm90aCB0aGUgdXBwZXJcbiAgKiAgIGFuZCBsb3dlciBib3VuZC4gRm9yIGluc3RhbmNlLCBgZmFjdG9yPSgwLjIsIDAuMylgIHJlc3VsdHMgaW4gYW4gb3V0cHV0XG4gICogICB3aXRoIHdpZHRoIGNoYW5nZWQgYnkgYSByYW5kb20gYW1vdW50IGluIHRoZSByYW5nZSBgWzIwJSwgMzAlXWAuXG4gICogICBgZmFjdG9yPSgtMC4yLCAwLjMpYCByZXN1bHRzIGluIGFuIG91dHB1dCB3aXRoIHdpZHRoIGNoYW5nZWQgYnkgYSByYW5kb21cbiAgKiAgIGFtb3VudCBpbiB0aGUgcmFuZ2UgYFstMjAlLCArMzAlXWAuIGBmYWN0b3I9MC4yYCByZXN1bHRzIGluIGFuIG91dHB1dFxuICAqICAgd2l0aCB3aWR0aCBjaGFuZ2VkIGJ5IGEgcmFuZG9tIGFtb3VudCBpbiB0aGUgcmFuZ2UgYFstMjAlLCArMjAlXWAuXG4gICogaW50ZXJwb2xhdGlvbjpcbiAgKiAgIFN0cmluZywgdGhlIGludGVycG9sYXRpb24gbWV0aG9kLlxuICAqICAgRGVmYXVsdHMgdG8gYGJpbGluZWFyYC5cbiAgKiAgIFN1cHBvcnRzIGBcImJpbGluZWFyXCJgLCBgXCJuZWFyZXN0XCJgLlxuICAqICAgVGhlIHRmIG1ldGhvZHMgYFwiYmljdWJpY1wiYCwgYFwiYXJlYVwiYCwgYFwibGFuY3pvczNcImAsIGBcImxhbmN6b3M1XCJgLFxuICAqICAgYFwiZ2F1c3NpYW5cImAsIGBcIm1pdGNoZWxsY3ViaWNcImAgYXJlIHVuaW1wbGVtZW50ZWQgaW4gdGZqcy5cbiAgKiBzZWVkOlxuICAqICAgSW50ZWdlci4gVXNlZCB0byBjcmVhdGUgYSByYW5kb20gc2VlZC5cbiAgKlxuICAqIElucHV0IHNoYXBlOlxuICAqICAgICAzRCAodW5iYXRjaGVkKSBvciA0RCAoYmF0Y2hlZCkgdGVuc29yIHdpdGggc2hhcGU6XG4gICogICAgIGAoLi4uLCBoZWlnaHQsIHdpZHRoLCBjaGFubmVscylgLCBpbiBgXCJjaGFubmVsc19sYXN0XCJgIGZvcm1hdC5cbiAgKiBPdXRwdXQgc2hhcGU6XG4gICogICAgIDNEICh1bmJhdGNoZWQpIG9yIDREIChiYXRjaGVkKSB0ZW5zb3Igd2l0aCBzaGFwZTpcbiAgKiAgICAgYCguLi4sIGhlaWdodCwgcmFuZG9tX3dpZHRoLCBjaGFubmVscylgLlxuICAqXG4gICpcbiAgKiBAZG9jIHtoZWFkaW5nOiAnTGF5ZXJzJywgc3ViaGVhZGluZzogJ1JhbmRvbVdpZHRoJywgbmFtZXNwYWNlOiAnbGF5ZXJzJ31cbiAgKi9cbiAgZXhwb3J0IGZ1bmN0aW9uIHJhbmRvbVdpZHRoKGFyZ3M6IFJhbmRvbVdpZHRoQXJncykge1xuICAgIHJldHVybiBuZXcgUmFuZG9tV2lkdGgoYXJncyk7XG4gIH1cbiJdfQ==