gx
chenyc
2025-06-12 7b72ac13a83764a662159d4a49b7fffb90476ecb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
/**
 * @license
 * Copyright 2023 Google LLC.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =============================================================================
 */
/**
 *  Tokenizer layers.
 */
/* Original source: keras-nlp/tokenizer.py */
import { serialization, tensor, tidy } from '@tensorflow/tfjs-core';
import { Layer } from '../../engine/topology';
import { NotImplementedError, ValueError } from '../../errors';
import { BytePairTokenizerCache, bytesToUnicode, createStaticHashtable, removeStringsFromInputs, splitStringsForBpe } from './tokenizers_utils';
import { tensorToArr, tensorArrTo2DArr } from './utils';
/**
 * Base class for Tokenizers.
 *
 *  Tokenizers in the tfjs library should all subclass this layer.
 *  The class provides two core methods `tokenize()` and `detokenize()` for
 *  going from plain text to sequences and back. A tokenizer is a subclass of
 *  `Layer` and can be combined with other layers in a `tf.sequential` model.
 *
 *  Subclassers should always implement the `tokenize()` method, which will also
 *  be the default when calling the layer directly on inputs.
 *
 *  Subclassers can optionally implement the `detokenize()` method if the
 *  tokenization is reversible. Otherwise, this can be skipped.
 *
 *  Subclassers should implement `get_vocabulary()`, `vocabulary_size()`,
 *  `token_to_id()` and `id_to_token()` if applicable. For some simple
 *  "vocab free" tokenizers, such as a whitespace splitter shown below, these
 *  methods do not apply and can be skipped.
 *
 *  Example:
 *
 *  ```js
 *  class WhitespaceSplitterTokenizer extends Tokenizer {
 *    tokenize(inputs: Tensor): Tensor[] {
 *      const stringInputs = inputs.dataSync() as unknown as string[];
 *      return stringInputs.map(input => Tensor(input.split(' ')));
 *    }
 *
 *    override detokenize(inputs: Tensor[]): Tensor {
 *      const stringInputs = inputs.map(
 *        input => input.dataSync() as unknown as string[]);
 *      return Tensor(stringInputs.map(str => str.join(' ')));
 *    }
 *  }
 *
 * const tokenizer = new WhitespaceSplitterTokenizer();
 *
 * tokenizer.tokenize(tensor(['this is a test']))[0].print();
 *
 * tokenizer.detokenize([tensor(['this', 'is', 'a', 'test'])]).print();
 * ```
 */
export class Tokenizer extends Layer {
    /**
     * Transform tokens back into strings.
     *
     * @param inputs Input tensor.
     * @param kwargs Additional keyword arguments.
     */
    detokenize(inputs) {
        throw new NotImplementedError(`No implementation of 'detokenize()' was found for
      ${this.constructor.name}.`);
    }
    /**
     * Get the tokenizer vocabulary as a list of strings terms.
     */
    get vocabulary() {
        throw new NotImplementedError(`No implementation of 'vocabulary()' was found for
      ${this.constructor.name}.`);
    }
    /**
     * Returns the total size of the token id space.
     */
    get vocabularySize() {
        throw new NotImplementedError(`No implementation of 'vocabularySize()' was found for
      ${this.constructor.name}.`);
    }
    /**
     * Convert an integer id to a string token.
     */
    idToToken(id) {
        throw new NotImplementedError(`No implementation of 'idToToken()' was found for
      ${this.constructor.name}.`);
    }
    /**
     * Convert an integer id to a string token.
     */
    tokenToId(token) {
        throw new NotImplementedError(`No implementation of 'tokenToId()' was found for
      ${this.constructor.name}.`);
    }
    call(inputs, { mode = 'tokenize' } = {}) {
        if (mode === 'tokenize') {
            if (inputs instanceof Array) {
                throw new ValueError(`tokenize expects Tensor, not Tensor[].`);
            }
            return this.tokenize(inputs);
        }
        if (mode === 'detokenize') {
            if (!(inputs instanceof Array)) {
                throw new ValueError(`detokenize expects Tensor[], not Tensor.`);
            }
            return this.detokenize(inputs);
        }
        throw new ValueError(`Input mode=${mode} is not supported.`);
    }
}
/**
 * Byte-pair encoding tokenizer layer.
 *
 * This BPE tokenizer provides the same functionality as the official GPT-2
 * tokenizer. Given the same `vocabulary` which maps tokens to ids, and `merges`
 * which describes BPE merge rules, it should provide the same output as OpenAI
 * implementation (https://github.com/openai/gpt-2/blob/master/src/encoder.py).
 *
 * If input is a batch of strings (rank > 0):
 * By default, the layer will output a `Tensor[]`.
 * If `sequenceLength` is set, the layer will output a `Tensor[]` where all
 * inputs have been padded or truncated to `sequenceLength`.
 *
 * Examples:
 *
 * Tokenize
 * ```js
 * const vocabulary = new Map([['butter', 1], ['fly', 2]]);
 * const merges = ['b u', 't t', 'e r', 'bu tt', 'butt er', 'f l', 'fl y'];
 * const tokenizer = new BytePairTokenizer({vocabulary, merges});
 *
 * tokenizer.tokenize(tensor(['butterfly']))[0].print();
 * tokenizer.tokenize(tensor(['butterfly, butter']))[1].print();
 * ```
 *
 * Detokenize
 * ```js
 * const vocabulary = new Map([['butter', 1], ['fly', 2]]);
 * const merges = ['b u', 't t', 'e r', 'bu tt', 'butt er', 'f l', 'fl y'];
 * const tokenizer = new BytePairTokenizer({vocabulary, merges});
 *
 * tokenizer.detokenize([[1, 2]]).print();
 * ```
 */
class BytePairTokenizer extends Tokenizer {
    constructor(args) {
        super(args);
        this.cache = new BytePairTokenizerCache();
        this._vocabulary = new Map(args.vocabulary);
        this.merges = [...args.merges];
        this.sequenceLength = args.sequenceLength || null;
        this.addPrefixSpace = args.addPrefixSpace || false;
        this.unsplittableTokens = args.unsplittableTokens || null;
        // Create byte <=> unicode mapping. This is useful for handling
        // whitespace tokens.
        const [byteList, unicodeList] = bytesToUnicode();
        this.byte2Unicode = createStaticHashtable(Array.from(byteList), unicodeList, '');
        if (this.unsplittableTokens) {
            // Put unsplittable tokens into cache, so it won't be further split and
            // merged.
            this.cache.insert(this.unsplittableTokens, this.unsplittableTokens);
        }
        // Create mapping between string tokens to int ids, and vice versa.
        const bytePairs = [...this._vocabulary.keys()];
        const bytePairEncodingIndicies = [...this._vocabulary.values()];
        this.tokenToIdMap = createStaticHashtable(bytePairs, bytePairEncodingIndicies, -1);
        this.idToTokenMap = createStaticHashtable(bytePairEncodingIndicies, bytePairs, '');
        // Create ranking of merge rules, this is the same as order of merge pairs
        // in `this.merges`.
        this.mergeRanksLookupDefault = this.merges.length + 1;
        this.mergeRanks = createStaticHashtable(this.merges, [...Array(this.merges.length).keys()], this.mergeRanksLookupDefault);
    }
    /**
     * Get the tokenizer vocabulary as a list of string tokens.
     */
    get vocabulary() {
        return [...this._vocabulary.keys()];
    }
    /**
     * Get the size of the tokenizer vocabulary.
     */
    get vocabularySize() {
        return this._vocabulary.size;
    }
    /**
     * Convert an integer id to a string token.
     */
    idToToken(id) {
        // This will be slow, but keep memory usage down compared to building a
        // dict. Assuming the main use case is looking up a few special tokens
        // early in the vocab, this should be fine.
        const keys = this.vocabulary;
        for (const token of keys) {
            if (this._vocabulary.get(token) === id) {
                return token;
            }
        }
        return undefined;
    }
    /**
     * Convert a string token to an integer id.
     */
    tokenToId(token) {
        return this._vocabulary.get(token);
    }
    getConfig() {
        const config = {
            vocabulary: Array.from(this._vocabulary.entries()),
            merges: this.merges,
            sequenceLength: this.sequenceLength,
            addPrefixSpace: this.addPrefixSpace,
            unsplittableTokens: this.unsplittableTokens,
        };
        const baseConfig = super.getConfig();
        Object.assign(config, baseConfig);
        return config;
    }
    /**
     * Perform one step of byte-pair merge.
     */
    bpeMergeOneStep(words, mask) {
        const wordsStr = tensorArrTo2DArr(words);
        // Get all word pairs.
        const first = wordsStr.map(arr => arr.slice(0, -1));
        const second = wordsStr.map(arr => arr.slice(1, arr.length));
        // Mask empty.
        const nonEmptyMask = second.map(arr => arr.length > 0);
        mask = mask.map((a, idx) => a && nonEmptyMask[idx]);
        if (!mask.some(e => e)) {
            return [words, mask];
        }
        const nonEmptyIndices = mask
            .map((bool, idx) => bool ? idx : -1)
            .filter(e => e !== -1);
        const filteredFirst = nonEmptyIndices.map(idx => first[idx]);
        const filteredSecond = nonEmptyIndices.map(idx => second[idx]);
        // Get byte pair ranking in merge rules.
        const pairs = filteredFirst.map((firstSubArr, idx) => {
            const secondSubArr = filteredSecond[idx];
            return firstSubArr.map((char, idx) => `${char} ${secondSubArr[idx]}`);
        });
        const pairRanksTensor = this.mergeRanks.lookup(pairs.map(arr => tensor(arr)));
        const pairRanks = tensorArrTo2DArr(pairRanksTensor);
        // Get BPE pair ranks.
        const minPairRank = pairRanks.map(arr => arr.reduce((a, b) => Math.min(a, b), Infinity));
        const pairFoundMask = minPairRank.map(rank => rank !== this.mergeRanksLookupDefault);
        // Tokens that cannot be further merged are marked as finished.
        for (const [idx, index] of nonEmptyIndices.entries()) {
            const update = pairFoundMask[idx];
            mask[index] = update;
        }
        if (!mask.some(e => e)) {
            return [words, mask];
        }
        function argMin(arr) {
            return arr.indexOf(arr.reduce((a, b) => Math.min(a, b), Infinity));
        }
        const maskedPairRanks = pairRanks.filter((_, idx) => pairFoundMask[idx]);
        const minPairRankIndices = maskedPairRanks.map(arr => argMin(arr));
        // Get words and pairs to process.
        const unfinishedWords = wordsStr.filter((_, idx) => mask[idx]);
        const pairLeft = unfinishedWords.map((word, idx) => word[minPairRankIndices[idx]]);
        const pairRight = unfinishedWords.map((word, idx) => word[minPairRankIndices[idx] + 1]);
        const mergedPairs = pairLeft.map((left, idx) => {
            const right = pairRight[idx];
            return `${left}${right}`;
        });
        const unfinishedWordsIndices = mask
            .map((_, idx) => idx)
            .filter((_, idx) => mask[idx]);
        const mergedPairIndices = unfinishedWordsIndices.map((index, idx) => [index, minPairRankIndices[idx]]);
        const emptyStringIndices = unfinishedWordsIndices.map((index, idx) => [index, minPairRankIndices[idx] + 1]);
        for (const [idx, indices] of mergedPairIndices.entries()) {
            const [wordIdx, charIdx] = indices;
            const mergedPair = mergedPairs[idx];
            wordsStr[wordIdx][charIdx] = mergedPair;
        }
        for (const indices of emptyStringIndices) {
            const [wordIdx, charIdx] = indices;
            wordsStr[wordIdx][charIdx] = '';
        }
        words = wordsStr.map(word => tensor(word));
        words = removeStringsFromInputs(words, '');
        return [words, mask];
    }
    /**
     * Perform byte-pair merge for each word in the inputs.
     */
    bpeMerge(words) {
        const numWords = words.length;
        // Merge bytes.
        function loopCondition(mask) {
            return mask.some(e => e);
        }
        const initialMask = Array(numWords).fill(true);
        let mergedWords = words;
        let mask = initialMask;
        while (loopCondition(mask)) {
            [mergedWords, mask] = this.bpeMergeOneStep(mergedWords, mask);
        }
        return mergedWords;
    }
    /**
     * Map token bytes to unicode using `byte2unicode`.
     */
    transformBytes(tokens) {
        const tokensStr = tensorToArr(tokens);
        const splitBytes = tokensStr.map(token => tensor(token.split('').map(c => c.charCodeAt(0))));
        const splitUnicode = this.byte2Unicode.lookup(splitBytes);
        return splitUnicode;
    }
    /**
     * Process unseen tokens and add to cache.
     */
    bpeMergeAndUpdateCache(tokens) {
        const words = this.transformBytes(tokens);
        const tokenizedWordsTensor = this.bpeMerge(words);
        const tokenizedWords = tensorArrTo2DArr(tokenizedWordsTensor);
        // For each word, join all its token by a whitespace,
        // e.g., ["dragon", "fly"] => "dragon fly" for hash purpose.
        const joinedTokens = tokenizedWords.map(word => word.join(' '));
        this.cache.insert(tokens, joinedTokens);
    }
    tokenize(inputs) {
        return tidy(() => {
            if (this.addPrefixSpace) {
                const strInputs = tensorToArr(inputs);
                inputs = tensor(strInputs.map(word => ' ' + word));
            }
            const rawTokensTensor = splitStringsForBpe(inputs, this.unsplittableTokens);
            const rawTokens = tensorArrTo2DArr(rawTokensTensor);
            const tokenRowSplits = [0];
            for (const [idx, token] of rawTokens.entries()) {
                tokenRowSplits.push(tokenRowSplits[idx] + token.length);
            }
            const flatTokens = rawTokens.reduce((acc, e) => acc.concat(e), []);
            // Check cache.
            const cacheLookup = this.cache.lookup(flatTokens);
            const cacheMask = cacheLookup.map(e => e === '');
            const hasUnseenWords = cacheMask.some((bool, idx) => bool && flatTokens[idx] !== '');
            const processUnseenTokens = () => {
                const unseenTokens = flatTokens.filter((_, idx) => cacheMask[idx]);
                this.bpeMergeAndUpdateCache(tensor(unseenTokens));
                return this.cache.lookup(flatTokens);
            };
            // If `has_unseen_words == True`, it means not all tokens are in cache,
            // we will process the unseen tokens. Otherwise return the cache lookup.
            const tokenizedWords = hasUnseenWords ? processUnseenTokens() : cacheLookup;
            const tokensTensor = this.tokenToIdMap.lookup(tokenizedWords.map(word => tensor(word.split(' '))));
            const tokens = tokensTensor.map(t => Array.from(t.dataSync()));
            // Unflatten to match input.
            const newTokenRowSplits = [0];
            for (const [idx, token] of tokens.entries()) {
                newTokenRowSplits.push(newTokenRowSplits[idx] + token.length);
            }
            const newFlatTokens = tokens.reduce((acc, e) => acc.concat(e), []);
            const gatheredIndices = tokenRowSplits.map(index => newTokenRowSplits[index]);
            let tokens2D = [];
            for (let i = 0; i < gatheredIndices.length - 1; i++) {
                const [start, end] = [gatheredIndices[i], gatheredIndices[i + 1]];
                const row = newFlatTokens.slice(start, end);
                tokens2D.push(tensor(row));
            }
            // Convert to a dense output if `sequenceLength` is set.
            if (this.sequenceLength) {
                // pad or truncate
                tokens2D = tokens2D.map(t => {
                    if (t.size === this.sequenceLength) {
                        return t;
                    }
                    else if (t.size > this.sequenceLength) {
                        return t.slice(0, this.sequenceLength);
                    }
                    else {
                        return t.pad([[0, this.sequenceLength - t.size]]);
                    }
                });
            }
            return tokens2D;
        });
    }
    detokenize(inputs) {
        const unicodeText = this.idToTokenMap.lookup(inputs)
            .map(t => tensorToArr(t).join(''));
        return tensor(unicodeText);
    }
}
/** @nocollapse */
BytePairTokenizer.className = 'BytePairTokenizer';
export { BytePairTokenizer };
serialization.registerClass(BytePairTokenizer);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9rZW5pemVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3RmanMtbGF5ZXJzL3NyYy9sYXllcnMvbmxwL3Rva2VuaXplcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBRUg7O0dBRUc7QUFFSCw2Q0FBNkM7QUFDN0MsT0FBTyxFQUFVLGFBQWEsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFDLE1BQU0sdUJBQXVCLENBQUM7QUFFM0UsT0FBTyxFQUFFLEtBQUssRUFBYSxNQUFNLHVCQUF1QixDQUFDO0FBQ3pELE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxVQUFVLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDL0QsT0FBTyxFQUFFLHNCQUFzQixFQUFtQixjQUFjLEVBQUUscUJBQXFCLEVBQUUsdUJBQXVCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNqSyxPQUFPLEVBQUUsV0FBVyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sU0FBUyxDQUFDO0FBTXhEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXlDRztBQUNILE1BQU0sT0FBZ0IsU0FBVSxTQUFRLEtBQUs7SUFTM0M7Ozs7O09BS0c7SUFDSCxVQUFVLENBQUMsTUFBZ0I7UUFDekIsTUFBTSxJQUFJLG1CQUFtQixDQUMzQjtRQUNFLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxHQUFHLENBQzNCLENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDSCxJQUFJLFVBQVU7UUFDWixNQUFNLElBQUksbUJBQW1CLENBQzNCO1FBQ0UsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEdBQUcsQ0FDM0IsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNILElBQUksY0FBYztRQUNoQixNQUFNLElBQUksbUJBQW1CLENBQzNCO1FBQ0UsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJ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QyxDQUFDLElBQUksS0FBSyxJQUFJLENBQUMsY0FBYyxFQUFFO3dCQUNsQyxPQUFPLENBQUMsQ0FBQztxQkFDVjt5QkFBTSxJQUFJLENBQUMsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLGNBQWMsRUFBRTt3QkFDdkMsT0FBTyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUM7cUJBQ3hDO3lCQUFNO3dCQUNMLE9BQU8sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxjQUFjLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztxQkFDbkQ7Z0JBQ0gsQ0FBQyxDQUFDLENBQUM7YUFDSjtZQUVELE9BQU8sUUFBUSxDQUFDO1FBQ2xCLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVRLFVBQVUsQ0FBQyxNQUFnQjtRQUNsQyxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUM7YUFDakQsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUUsV0FBVyxDQUFDLENBQUMsQ0FBYyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBRW5ELE9BQU8sTUFBTSxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQzdCLENBQUM7O0FBaFZELGtCQUFrQjtBQUNGLDJCQUFTLEdBQUcsbUJBQW1CLEFBQXRCLENBQXVCO1NBRnJDLGlCQUFpQjtBQW1WOUIsYUFBYSxDQUFDLGFBQWEsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IDIwMjMgR29vZ2xlIExMQy5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqL1xuXG4vKipcbiAqICBUb2tlbml6ZXIgbGF5ZXJzLlxuICovXG5cbi8qIE9yaWdpbmFsIHNvdXJjZToga2VyYXMtbmxwL3Rva2VuaXplci5weSAqL1xuaW1wb3J0IHsgVGVuc29yLCBzZXJpYWxpemF0aW9uLCB0ZW5zb3IsIHRpZHl9IGZyb20gJ0B0ZW5zb3JmbG93L3RmanMtY29yZSc7XG5cbmltcG9ydCB7IExheWVyLCBMYXllckFyZ3MgfSBmcm9tICcuLi8uLi9lbmdpbmUvdG9wb2xvZ3knO1xuaW1wb3J0IHsgTm90SW1wbGVtZW50ZWRFcnJvciwgVmFsdWVFcnJvciB9IGZyb20gJy4uLy4uL2Vycm9ycyc7XG5pbXBvcnQgeyBCeXRlUGFpclRva2VuaXplckNhY2hlLCBTdGF0aWNIYXNoVGFibGUsIGJ5dGVzVG9Vbmljb2RlLCBjcmVhdGVTdGF0aWNIYXNodGFibGUsIHJlbW92ZVN0cmluZ3NGcm9tSW5wdXRzLCBzcGxpdFN0cmluZ3NGb3JCcGUgfSBmcm9tICcuL3Rva2VuaXplcnNfdXRpbHMnO1xuaW1wb3J0IHsgdGVuc29yVG9BcnIsIHRlbnNvckFyclRvMkRBcnIgfSBmcm9tICcuL3V0aWxzJztcblxuZXhwb3J0IGRlY2xhcmUgaW50ZXJmYWNlIFRva2VuaXplck9wdGlvbnMge1xuICBtb2RlPzogJ3Rva2VuaXplJyB8ICdkZXRva2VuaXplJztcbn1cblxuLyoqXG4gKiBCYXNlIGNsYXNzIGZvciBUb2tlbml6ZXJzLlxuICpcbiAqICBUb2tlbml6ZXJzIGluIHRoZSB0ZmpzIGxpYnJhcnkgc2hvdWxkIGFsbCBzdWJjbGFzcyB0aGlzIGxheWVyLlxuICogIFRoZSBjbGFzcyBwcm92aWRlcyB0d28gY29yZSBtZXRob2RzIGB0b2tlbml6ZSgpYCBhbmQgYGRldG9rZW5pemUoKWAgZm9yXG4gKiAgZ29pbmcgZnJvbSBwbGFpbiB0ZXh0IHRvIHNlcXVlbmNlcyBhbmQgYmFjay4gQSB0b2tlbml6ZXIgaXMgYSBzdWJjbGFzcyBvZlxuICogIGBMYXllcmAgYW5kIGNhbiBiZSBjb21iaW5lZCB3aXRoIG90aGVyIGxheWVycyBpbiBhIGB0Zi5zZXF1ZW50aWFsYCBtb2RlbC5cbiAqXG4gKiAgU3ViY2xhc3NlcnMgc2hvdWxkIGFsd2F5cyBpbXBsZW1lbnQgdGhlIGB0b2tlbml6ZSgpYCBtZXRob2QsIHdoaWNoIHdpbGwgYWxzb1xuICogIGJlIHRoZSBkZWZhdWx0IHdoZW4gY2FsbGluZyB0aGUgbGF5ZXIgZGlyZWN0bHkgb24gaW5wdXRzLlxuICpcbiAqICBTdWJjbGFzc2VycyBjYW4gb3B0aW9uYWxseSBpbXBsZW1lbnQgdGhlIGBkZXRva2VuaXplKClgIG1ldGhvZCBpZiB0aGVcbiAqICB0b2tlbml6YXRpb24gaXMgcmV2ZXJzaWJsZS4gT3RoZXJ3aXNlLCB0aGlzIGNhbiBiZSBza2lwcGVkLlxuICpcbiAqICBTdWJjbGFzc2VycyBzaG91bGQgaW1wbGVtZW50IGBnZXRfdm9jYWJ1bGFyeSgpYCwgYHZvY2FidWxhcnlfc2l6ZSgpYCxcbiAqICBgdG9rZW5fdG9faWQoKWAgYW5kIGBpZF90b190b2tlbigpYCBpZiBhcHBsaWNhYmxlLiBGb3Igc29tZSBzaW1wbGVcbiAqICBcInZvY2FiIGZyZWVcIiB0b2tlbml6ZXJzLCBzdWNoIGFzIGEgd2hpdGVzcGFjZSBzcGxpdHRlciBzaG93biBiZWxvdywgdGhlc2VcbiAqICBtZXRob2RzIGRvIG5vdCBhcHBseSBhbmQgY2FuIGJlIHNraXBwZWQuXG4gKlxuICogIEV4YW1wbGU6XG4gKlxuICogIGBgYGpzXG4gKiAgY2xhc3MgV2hpdGVzcGFjZVNwbGl0dGVyVG9rZW5pemVyIGV4dGVuZHMgVG9rZW5pemVyIHtcbiAqICAgIHRva2VuaXplKGlucHV0czogVGVuc29yKTogVGVuc29yW10ge1xuICogICAgICBjb25zdCBzdHJpbmdJbnB1dHMgPSBpbnB1dHMuZGF0YVN5bmMoKSBhcyB1bmtub3duIGFzIHN0cmluZ1tdO1xuICogICAgICByZXR1cm4gc3RyaW5nSW5wdXRzLm1hcChpbnB1dCA9PiBUZW5zb3IoaW5wdXQuc3BsaXQoJyAnKSkpO1xuICogICAgfVxuICpcbiAqICAgIG92ZXJyaWRlIGRldG9rZW5pemUoaW5wdXRzOiBUZW5zb3JbXSk6IFRlbnNvciB7XG4gKiAgICAgIGNvbnN0IHN0cmluZ0lucHV0cyA9IGlucHV0cy5tYXAoXG4gKiAgICAgICAgaW5wdXQgPT4gaW5wdXQuZGF0YVN5bmMoKSBhcyB1bmtub3duIGFzIHN0cmluZ1tdKTtcbiAqICAgICAgcmV0dXJuIFRlbnNvcihzdHJpbmdJbnB1dHMubWFwKHN0ciA9PiBzdHIuam9pbignICcpKSk7XG4gKiAgICB9XG4gKiAgfVxuICpcbiAqIGNvbnN0IHRva2VuaXplciA9IG5ldyBXaGl0ZXNwYWNlU3BsaXR0ZXJUb2tlbml6ZXIoKTtcbiAqXG4gKiB0b2tlbml6ZXIudG9rZW5pemUodGVuc29yKFsndGhpcyBpcyBhIHRlc3QnXSkpWzBdLnByaW50KCk7XG4gKlxuICogdG9rZW5pemVyLmRldG9rZW5pemUoW3RlbnNvcihbJ3RoaXMnLCAnaXMnLCAnYScsICd0ZXN0J10pXSkucHJpbnQoKTtcbiAqIGBgYFxuICovXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgVG9rZW5pemVyIGV4dGVuZHMgTGF5ZXIge1xuICAvKipcbiAgICogVHJhbnNmb3JtIGlucHV0IHRlbnNvcnMgb2Ygc3RyaW5ncyBpbnRvIG91dHB1dCB0b2tlbnMuXG4gICAqXG4gICAqIEBwYXJhbSBpbnB1dHMgSW5wdXQgdGVuc29yLlxuICAgKiBAcGFyYW0ga3dhcmdzIEFkZGl0aW9uYWwga2V5d29yZCBhcmd1bWVudHMuXG4gICAqL1xuICBhYnN0cmFjdCB0b2tlbml6ZShpbnB1dHM6IFRlbnNvcik6IFRlbnNvcltdO1xuXG4gIC8qKlxuICAgKiBUcmFuc2Zvcm0gdG9rZW5zIGJhY2sgaW50byBzdHJpbmdzLlxuICAgKlxuICAgKiBAcGFyYW0gaW5wdXRzIElucHV0IHRlbnNvci5cbiAgICogQHBhcmFtIGt3YXJncyBBZGRpdGlvbmFsIGtleXdvcmQgYXJndW1lbnRzLlxuICAgKi9cbiAgZGV0b2tlbml6ZShpbnB1dHM6IFRlbnNvcltdKTogVGVuc29yIHtcbiAgICB0aHJvdyBuZXcgTm90SW1wbGVtZW50ZWRFcnJvcihcbiAgICAgIGBObyBpbXBsZW1lbnRhdGlvbiBvZiAnZGV0b2tlbml6ZSgpJyB3YXMgZm91bmQgZm9yXG4gICAgICAke3RoaXMuY29uc3RydWN0b3IubmFtZX0uYFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSB0b2tlbml6ZXIgdm9jYWJ1bGFyeSBhcyBhIGxpc3Qgb2Ygc3RyaW5ncyB0ZXJtcy5cbiAgICovXG4gIGdldCB2b2NhYnVsYXJ5KCk6IHN0cmluZ1tdIHtcbiAgICB0aHJvdyBuZXcgTm90SW1wbGVtZW50ZWRFcnJvcihcbiAgICAgIGBObyBpbXBsZW1lbnRhdGlvbiBvZiAndm9jYWJ1bGFyeSgpJyB3YXMgZm91bmQgZm9yXG4gICAgICAke3RoaXMuY29uc3RydWN0b3IubmFtZX0uYFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgdG90YWwgc2l6ZSBvZiB0aGUgdG9rZW4gaWQgc3BhY2UuXG4gICAqL1xuICBnZXQgdm9jYWJ1bGFyeVNpemUoKTogbnVtYmVyIHtcbiAgICB0aHJvdyBuZXcgTm90SW1wbGVtZW50ZWRFcnJvcihcbiAgICAgIGBObyBpbXBsZW1lbnRhdGlvbiBvZiAndm9jYWJ1bGFyeVNpemUoKScgd2FzIGZvdW5kIGZvclxuICAgICAgJHt0aGlzLmNvbnN0cnVjdG9yLm5hbWV9LmBcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbnZlcnQgYW4gaW50ZWdlciBpZCB0byBhIHN0cmluZyB0b2tlbi5cbiAgICovXG4gIGlkVG9Ub2tlbihpZDogbnVtYmVyKTogc3RyaW5nIHtcbiAgICB0aHJvdyBuZXcgTm90SW1wbGVtZW50ZWRFcnJvcihcbiAgICAgIGBObyBpbXBsZW1lbnRhdGlvbiBvZiAnaWRUb1Rva2VuKCknIHdhcyBmb3VuZCBmb3JcbiAgICAgICR7dGhpcy5jb25zdHJ1Y3Rvci5uYW1lfS5gXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb252ZXJ0IGFuIGludGVnZXIgaWQgdG8gYSBzdHJpbmcgdG9rZW4uXG4gICAqL1xuICB0b2tlblRvSWQodG9rZW46IHN0cmluZyk6IG51bWJlciB7XG4gICAgdGhyb3cgbmV3IE5vdEltcGxlbWVudGVkRXJyb3IoXG4gICAgICBgTm8gaW1wbGVtZW50YXRpb24gb2YgJ3Rva2VuVG9JZCgpJyB3YXMgZm91bmQgZm9yXG4gICAgICAke3RoaXMuY29uc3RydWN0b3IubmFtZX0uYFxuICAgICk7XG4gIH1cblxuICBvdmVycmlkZSBjYWxsKFxuICAgIGlucHV0czogVGVuc29yfFRlbnNvcltdLFxuICAgIHttb2RlID0gJ3Rva2VuaXplJ306IFRva2VuaXplck9wdGlvbnM9e31cbiAgKTogVGVuc29yfFRlbnNvcltdIHtcblxuICAgIGlmIChtb2RlID09PSAndG9rZW5pemUnKSB7XG4gICAgICBpZiAoaW5wdXRzIGluc3RhbmNlb2YgQXJyYXkpIHtcbiAgICAgICAgdGhyb3cgbmV3IFZhbHVlRXJyb3IoYHRva2VuaXplIGV4cGVjdHMgVGVuc29yLCBub3QgVGVuc29yW10uYCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy50b2tlbml6ZShpbnB1dHMpO1xuICAgIH1cblxuICAgIGlmIChtb2RlID09PSAnZGV0b2tlbml6ZScpIHtcbiAgICAgIGlmICghKGlucHV0cyBpbnN0YW5jZW9mIEFycmF5KSkge1xuICAgICAgICB0aHJvdyBuZXcgVmFsdWVFcnJvcihgZGV0b2tlbml6ZSBleHBlY3RzIFRlbnNvcltdLCBub3QgVGVuc29yLmApO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuZGV0b2tlbml6ZShpbnB1dHMpO1xuICAgIH1cblxuICAgIHRocm93IG5ldyBWYWx1ZUVycm9yKGBJbnB1dCBtb2RlPSR7bW9kZX0gaXMgbm90IHN1cHBvcnRlZC5gKTtcbiAgfVxufVxuXG4vKiBPcmlnaW5hbCBzb3VyY2U6IGtlcmFzLW5scC9ieXRlX3BhaXJfdG9rZW5pemVyLnB5ICovXG4vLyBUT0RPKHBmb3JkZXJpcXVlKTogU3VwcG9ydCBmaWxlbmFtZSBzdHJpbmcgaW5wdXRzIGZvciB2b2NhYnVsYXJ5IGFuZCBtZXJnZXMuXG5leHBvcnQgZGVjbGFyZSBpbnRlcmZhY2UgQnl0ZVBhaXJUb2tlbml6ZXJBcmdzIGV4dGVuZHMgTGF5ZXJBcmdzIHtcbiAgLyoqXG4gICAqIE1hcHMgdG9rZW4gdG8gaW50ZWdlciBpZHNcbiAgICovXG4gIHZvY2FidWxhcnk6IE1hcDxzdHJpbmcsIG51bWJlcj47XG5cbiAgLyoqXG4gICAqIEFycmF5LiBDb250YWlucyB0aGUgbWVyZ2UgcnVsZS5cbiAgICovXG4gIG1lcmdlczogc3RyaW5nW107XG5cbiAgLyoqXG4gICAqIEludGVnZXIuIElmIHNldCwgdGhlIG91dHB1dCB3aWxsIGJlIHBhZGRlZCBvciB0cnVuY2F0ZWQgdG8gdGhlXG4gICAqIGBzZXF1ZW5jZUxlbmd0aGAuIERlZmF1bHRzIHRvIGBudWxsYC5cbiAgICovXG4gIHNlcXVlbmNlTGVuZ3RoPzogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiBCb29sZWFuLiBXaGV0aGVyIHRvIGFkZCBhbiBpbml0aWFsIHNwYWNlIHRvIHRoZSBpbnB1dC4gVGhpcyB0b2tlbml6ZXIgaXNcbiAgICogd2hpdGVzcGFjZSBhd2FyZSwgYW5kIHdpbGwgdG9rZW5pemUgYSB3b3JkIHdpdGggYSBsZWFkaW5nIHNwYWNlXG4gICAqIGRpZmZlcmVudGx5LiBBZGRpbmcgYSBwcmVmaXggc3BhY2UgdG8gdGhlIGZpcnN0IHdvcmQgd2lsbCBjYXVzZSBpdCB0byBiZVxuICAgKiB0b2tlbml6ZWQgZXF1aXZhbGVudGx5IHRvIGFsbCBzdWJzZXF1ZW50IHdvcmRzIGluIHRoZSBzZXF1ZW5jZS5cbiAgICogRGVmYXVsdHMgdG8gYGZhbHNlYC5cbiAgICovXG4gIGFkZFByZWZpeFNwYWNlPzogYm9vbGVhbjtcblxuICAvKipcbiAgICogQXJyYXkuIEEgbGlzdCBvZiBzdHJpbmdzIHRoYXQgd2lsbCBuZXZlciBiZSBzcGxpdCBkdXJpbmcgdGhlIHdvcmQtbGV2ZWxcbiAgICogc3BsaXR0aW5nIGFwcGxpZWQgYmVmb3JlIHRoZSBieXRlLXBhaXIgZW5jb2RpbmcuIFRoaXMgY2FuIGJlIHVzZWQgdG8gZW5zdXJlXG4gICAqIHNwZWNpYWwgdG9rZW5zIG1hcCB0byB1bmlxdWUgaW5kaWNlcyBpbiB0aGUgdm9jYWJ1bGFyeSwgZXZlbiBpZiB0aGVzZVxuICAgKiBzcGVjaWFsIHRva2VucyBjb250YWluIHNwbGl0dGFibGUgY2hhcmFjdGVycyBzdWNoIGFzIHB1bmN0dWF0aW9uLiBTcGVjaWFsXG4gICAqIHRva2VucyBtdXN0IHN0aWxsIGJlIGluY2x1ZGVkIGluIGB2b2NhYnVsYXJ5YC4gRGVmYXVsdHMgdG8gYE5vbmVgLlxuICAgKi9cbiAgdW5zcGxpdHRhYmxlVG9rZW5zPzogc3RyaW5nW107XG59XG5cbi8qKlxuICogQnl0ZS1wYWlyIGVuY29kaW5nIHRva2VuaXplciBsYXllci5cbiAqXG4gKiBUaGlzIEJQRSB0b2tlbml6ZXIgcHJvdmlkZXMgdGhlIHNhbWUgZnVuY3Rpb25hbGl0eSBhcyB0aGUgb2ZmaWNpYWwgR1BULTJcbiAqIHRva2VuaXplci4gR2l2ZW4gdGhlIHNhbWUgYHZvY2FidWxhcnlgIHdoaWNoIG1hcHMgdG9rZW5zIHRvIGlkcywgYW5kIGBtZXJnZXNgXG4gKiB3aGljaCBkZXNjcmliZXMgQlBFIG1lcmdlIHJ1bGVzLCBpdCBzaG91bGQgcHJvdmlkZSB0aGUgc2FtZSBvdXRwdXQgYXMgT3BlbkFJXG4gKiBpbXBsZW1lbnRhdGlvbiAoaHR0cHM6Ly9naXRodWIuY29tL29wZW5haS9ncHQtMi9ibG9iL21hc3Rlci9zcmMvZW5jb2Rlci5weSkuXG4gKlxuICogSWYgaW5wdXQgaXMgYSBiYXRjaCBvZiBzdHJpbmdzIChyYW5rID4gMCk6XG4gKiBCeSBkZWZhdWx0LCB0aGUgbGF5ZXIgd2lsbCBvdXRwdXQgYSBgVGVuc29yW11gLlxuICogSWYgYHNlcXVlbmNlTGVuZ3RoYCBpcyBzZXQsIHRoZSBsYXllciB3aWxsIG91dHB1dCBhIGBUZW5zb3JbXWAgd2hlcmUgYWxsXG4gKiBpbnB1dHMgaGF2ZSBiZWVuIHBhZGRlZCBvciB0cnVuY2F0ZWQgdG8gYHNlcXVlbmNlTGVuZ3RoYC5cbiAqXG4gKiBFeGFtcGxlczpcbiAqXG4gKiBUb2tlbml6ZVxuICogYGBganNcbiAqIGNvbnN0IHZvY2FidWxhcnkgPSBuZXcgTWFwKFtbJ2J1dHRlcicsIDFdLCBbJ2ZseScsIDJdXSk7XG4gKiBjb25zdCBtZXJnZXMgPSBbJ2IgdScsICd0IHQnLCAnZSByJywgJ2J1IHR0JywgJ2J1dHQgZXInLCAnZiBsJywgJ2ZsIHknXTtcbiAqIGNvbnN0IHRva2VuaXplciA9IG5ldyBCeXRlUGFpclRva2VuaXplcih7dm9jYWJ1bGFyeSwgbWVyZ2VzfSk7XG4gKlxuICogdG9rZW5pemVyLnRva2VuaXplKHRlbnNvcihbJ2J1dHRlcmZseSddKSlbMF0ucHJpbnQoKTtcbiAqIHRva2VuaXplci50b2tlbml6ZSh0ZW5zb3IoWydidXR0ZXJmbHksIGJ1dHRlciddKSlbMV0ucHJpbnQoKTtcbiAqIGBgYFxuICpcbiAqIERldG9rZW5pemVcbiAqIGBgYGpzXG4gKiBjb25zdCB2b2NhYnVsYXJ5ID0gbmV3IE1hcChbWydidXR0ZXInLCAxXSwgWydmbHknLCAyXV0pO1xuICogY29uc3QgbWVyZ2VzID0gWydiIHUnLCAndCB0JywgJ2UgcicsICdidSB0dCcsICdidXR0IGVyJywgJ2YgbCcsICdmbCB5J107XG4gKiBjb25zdCB0b2tlbml6ZXIgPSBuZXcgQnl0ZVBhaXJUb2tlbml6ZXIoe3ZvY2FidWxhcnksIG1lcmdlc30pO1xuICpcbiAqIHRva2VuaXplci5kZXRva2VuaXplKFtbMSwgMl1dKS5wcmludCgpO1xuICogYGBgXG4gKi9cbmV4cG9ydCBjbGFzcyBCeXRlUGFpclRva2VuaXplciBleHRlbmRzIFRva2VuaXplciB7XG4gIC8qKiBAbm9jb2xsYXBzZSAqL1xuICBzdGF0aWMgcmVhZG9ubHkgY2xhc3NOYW1lID0gJ0J5dGVQYWlyVG9rZW5pemVyJztcblxuICBwcml2YXRlIF92b2NhYnVsYXJ5OiBNYXA8c3RyaW5nLCBudW1iZXI+O1xuICBwcml2YXRlIG1lcmdlczogc3RyaW5nW107XG5cbiAgcHJpdmF0ZSByZWFkb25seSBzZXF1ZW5jZUxlbmd0aDogbnVtYmVyO1xuICBwcml2YXRlIHJlYWRvbmx5IGFkZFByZWZpeFNwYWNlOiBib29sZWFuO1xuICBwcml2YXRlIHJlYWRvbmx5IHVuc3BsaXR0YWJsZVRva2Vuczogc3RyaW5nW107XG5cbiAgcHJpdmF0ZSByZWFkb25seSBieXRlMlVuaWNvZGU6IFN0YXRpY0hhc2hUYWJsZTxudW1iZXIsIHN0cmluZz47XG4gIHByaXZhdGUgcmVhZG9ubHkgY2FjaGUgPSBuZXcgQnl0ZVBhaXJUb2tlbml6ZXJDYWNoZSgpO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgdG9rZW5Ub0lkTWFwOiBTdGF0aWNIYXNoVGFibGU8c3RyaW5nLCBudW1iZXI+O1xuICBwcml2YXRlIHJlYWRvbmx5IGlkVG9Ub2tlbk1hcDogU3RhdGljSGFzaFRhYmxlPG51bWJlciwgc3RyaW5nPjtcblxuICBwcml2YXRlIHJlYWRvbmx5IG1lcmdlUmFua3NMb29rdXBEZWZhdWx0OiBudW1iZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgbWVyZ2VSYW5rczogU3RhdGljSGFzaFRhYmxlPHN0cmluZywgbnVtYmVyPjtcblxuICBjb25zdHJ1Y3RvcihhcmdzOiBCeXRlUGFpclRva2VuaXplckFyZ3MpIHtcbiAgICBzdXBlcihhcmdzKTtcblxuICAgIHRoaXMuX3ZvY2FidWxhcnkgPSBuZXcgTWFwKGFyZ3Mudm9jYWJ1bGFyeSk7XG4gICAgdGhpcy5tZXJnZXMgPSBbLi4uYXJncy5tZXJnZXNdO1xuXG4gICAgdGhpcy5zZXF1ZW5jZUxlbmd0aCA9IGFyZ3Muc2VxdWVuY2VMZW5ndGggfHwgbnVsbDtcbiAgICB0aGlzLmFkZFByZWZpeFNwYWNlID0gYXJncy5hZGRQcmVmaXhTcGFjZSB8fCBmYWxzZTtcbiAgICB0aGlzLnVuc3BsaXR0YWJsZVRva2VucyA9IGFyZ3MudW5zcGxpdHRhYmxlVG9rZW5zIHx8IG51bGw7XG5cbiAgICAvLyBDcmVhdGUgYnl0ZSA8PT4gdW5pY29kZSBtYXBwaW5nLiBUaGlzIGlzIHVzZWZ1bCBmb3IgaGFuZGxpbmdcbiAgICAvLyB3aGl0ZXNwYWNlIHRva2Vucy5cbiAgICBjb25zdCBbYnl0ZUxpc3QsIHVuaWNvZGVMaXN0XSA9IGJ5dGVzVG9Vbmljb2RlKCk7XG4gICAgdGhpcy5ieXRlMlVuaWNvZGUgPSBjcmVhdGVTdGF0aWNIYXNodGFibGUoXG4gICAgICBBcnJheS5mcm9tKGJ5dGVMaXN0KSwgdW5pY29kZUxpc3QsICcnKTtcblxuICAgIGlmICh0aGlzLnVuc3BsaXR0YWJsZVRva2Vucykge1xuICAgICAgLy8gUHV0IHVuc3BsaXR0YWJsZSB0b2tlbnMgaW50byBjYWNoZSwgc28gaXQgd29uJ3QgYmUgZnVydGhlciBzcGxpdCBhbmRcbiAgICAgIC8vIG1lcmdlZC5cbiAgICAgIHRoaXMuY2FjaGUuaW5zZXJ0KHRoaXMudW5zcGxpdHRhYmxlVG9rZW5zLCB0aGlzLnVuc3BsaXR0YWJsZVRva2Vucyk7XG4gICAgfVxuXG4gICAgLy8gQ3JlYXRlIG1hcHBpbmcgYmV0d2VlbiBzdHJpbmcgdG9rZW5zIHRvIGludCBpZHMsIGFuZCB2aWNlIHZlcnNhLlxuICAgIGNvbnN0IGJ5dGVQYWlycyA9IFsuLi50aGlzLl92b2NhYnVsYXJ5LmtleXMoKV07XG4gICAgY29uc3QgYnl0ZVBhaXJFbmNvZGluZ0luZGljaWVzID0gWy4uLnRoaXMuX3ZvY2FidWxhcnkudmFsdWVzKCldO1xuXG4gICAgdGhpcy50b2tlblRvSWRNYXAgPSBjcmVhdGVTdGF0aWNIYXNodGFibGUoXG4gICAgICBieXRlUGFpcnMsIGJ5dGVQYWlyRW5jb2RpbmdJbmRpY2llcywgLTEpO1xuXG4gICAgdGhpcy5pZFRvVG9rZW5NYXAgPSBjcmVhdGVTdGF0aWNIYXNodGFibGUoXG4gICAgICBieXRlUGFpckVuY29kaW5nSW5kaWNpZXMsIGJ5dGVQYWlycywgJycpO1xuXG4gICAgLy8gQ3JlYXRlIHJhbmtpbmcgb2YgbWVyZ2UgcnVsZXMsIHRoaXMgaXMgdGhlIHNhbWUgYXMgb3JkZXIgb2YgbWVyZ2UgcGFpcnNcbiAgICAvLyBpbiBgdGhpcy5tZXJnZXNgLlxuICAgIHRoaXMubWVyZ2VSYW5rc0xvb2t1cERlZmF1bHQgPSB0aGlzLm1lcmdlcy5sZW5ndGggKyAxO1xuICAgIHRoaXMubWVyZ2VSYW5rcyA9IGNyZWF0ZVN0YXRpY0hhc2h0YWJsZShcbiAgICAgIHRoaXMubWVyZ2VzLFxuICAgICAgWy4uLkFycmF5KHRoaXMubWVyZ2VzLmxlbmd0aCkua2V5cygpXSxcbiAgICAgIHRoaXMubWVyZ2VSYW5rc0xvb2t1cERlZmF1bHRcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgdG9rZW5pemVyIHZvY2FidWxhcnkgYXMgYSBsaXN0IG9mIHN0cmluZyB0b2tlbnMuXG4gICAqL1xuICBvdmVycmlkZSBnZXQgdm9jYWJ1bGFyeSgpOiBzdHJpbmdbXSB7XG4gICAgcmV0dXJuIFsuLi50aGlzLl92b2NhYnVsYXJ5LmtleXMoKV07XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBzaXplIG9mIHRoZSB0b2tlbml6ZXIgdm9jYWJ1bGFyeS5cbiAgICovXG4gIG92ZXJyaWRlIGdldCB2b2NhYnVsYXJ5U2l6ZSgpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLl92b2NhYnVsYXJ5LnNpemU7XG4gIH1cblxuICAvKipcbiAgICogQ29udmVydCBhbiBpbnRlZ2VyIGlkIHRvIGEgc3RyaW5nIHRva2VuLlxuICAgKi9cbiAgb3ZlcnJpZGUgaWRUb1Rva2VuKGlkOiBudW1iZXIpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICAgIC8vIFRoaXMgd2lsbCBiZSBzbG93LCBidXQga2VlcCBtZW1vcnkgdXNhZ2UgZG93biBjb21wYXJlZCB0byBidWlsZGluZyBhXG4gICAgLy8gZGljdC4gQXNzdW1pbmcgdGhlIG1haW4gdXNlIGNhc2UgaXMgbG9va2luZyB1cCBhIGZldyBzcGVjaWFsIHRva2Vuc1xuICAgIC8vIGVhcmx5IGluIHRoZSB2b2NhYiwgdGhpcyBzaG91bGQgYmUgZmluZS5cbiAgICBjb25zdCBrZXlzID0gdGhpcy52b2NhYnVsYXJ5O1xuICAgIGZvciAoY29uc3QgdG9rZW4gb2Yga2V5cykge1xuICAgICAgaWYgKHRoaXMuX3ZvY2FidWxhcnkuZ2V0KHRva2VuKSA9PT0gaWQpIHtcbiAgICAgICAgcmV0dXJuIHRva2VuO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbnZlcnQgYSBzdHJpbmcgdG9rZW4gdG8gYW4gaW50ZWdlciBpZC5cbiAgICovXG4gIG92ZXJyaWRlIHRva2VuVG9JZCh0b2tlbjogc3RyaW5nKTogbnVtYmVyIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gdGhpcy5fdm9jYWJ1bGFyeS5nZXQodG9rZW4pO1xuICB9XG5cbiAgb3ZlcnJpZGUgZ2V0Q29uZmlnKCk6IHNlcmlhbGl6YXRpb24uQ29uZmlnRGljdCB7XG4gICAgY29uc3QgY29uZmlnID0ge1xuICAgICAgdm9jYWJ1bGFyeTogQXJyYXkuZnJvbSh0aGlzLl92b2NhYnVsYXJ5LmVudHJpZXMoKSksXG4gICAgICBtZXJnZXM6IHRoaXMubWVyZ2VzLFxuICAgICAgc2VxdWVuY2VMZW5ndGg6IHRoaXMuc2VxdWVuY2VMZW5ndGgsXG4gICAgICBhZGRQcmVmaXhTcGFjZTogdGhpcy5hZGRQcmVmaXhTcGFjZSxcbiAgICAgIHVuc3BsaXR0YWJsZVRva2VuczogdGhpcy51bnNwbGl0dGFibGVUb2tlbnMsXG4gICAgfTtcbiAgICBjb25zdCBiYXNlQ29uZmlnID0gc3VwZXIuZ2V0Q29uZmlnKCk7XG4gICAgT2JqZWN0LmFzc2lnbihjb25maWcsIGJhc2VDb25maWcpO1xuICAgIHJldHVybiBjb25maWc7XG4gIH1cblxuICAvKipcbiAgICogUGVyZm9ybSBvbmUgc3RlcCBvZiBieXRlLXBhaXIgbWVyZ2UuXG4gICAqL1xuICBwcml2YXRlIGJwZU1lcmdlT25lU3RlcChcbiAgICB3b3JkczogVGVuc29yW10sIG1hc2s6IGJvb2xlYW5bXSk6IFtUZW5zb3JbXSwgYm9vbGVhbltdXSB7XG5cbiAgICBjb25zdCB3b3Jkc1N0ciA9IHRlbnNvckFyclRvMkRBcnIod29yZHMpIGFzIHN0cmluZ1tdW107XG5cbiAgICAvLyBHZXQgYWxsIHdvcmQgcGFpcnMuXG4gICAgY29uc3QgZmlyc3QgPSB3b3Jkc1N0ci5tYXAoYXJyID0+IGFyci5zbGljZSgwLCAtMSkpO1xuICAgIGNvbnN0IHNlY29uZCA9IHdvcmRzU3RyLm1hcChhcnIgPT4gYXJyLnNsaWNlKDEsIGFyci5sZW5ndGgpKTtcblxuICAgIC8vIE1hc2sgZW1wdHkuXG4gICAgY29uc3Qgbm9uRW1wdHlNYXNrID0gc2Vjb25kLm1hcChhcnIgPT4gYXJyLmxlbmd0aCA+IDApO1xuICAgIG1hc2sgPSBtYXNrLm1hcCgoYSwgaWR4KSA9PiBhICYmIG5vbkVtcHR5TWFza1tpZHhdKTtcbiAgICBpZiAoIW1hc2suc29tZShlID0+IGUpKSB7XG4gICAgICByZXR1cm4gW3dvcmRzLCBtYXNrXTtcbiAgICB9XG4gICAgY29uc3Qgbm9uRW1wdHlJbmRpY2VzID0gbWFza1xuICAgICAgLm1hcCgoYm9vbCwgaWR4KSA9PiBib29sID8gaWR4IDogLTEpXG4gICAgICAuZmlsdGVyKGUgPT4gZSAhPT0gLTEpO1xuXG4gICAgY29uc3QgZmlsdGVyZWRGaXJzdCA9IG5vbkVtcHR5SW5kaWNlcy5tYXAoaWR4ID0+IGZpcnN0W2lkeF0pO1xuICAgIGNvbnN0IGZpbHRlcmVkU2Vjb25kID0gbm9uRW1wdHlJbmRpY2VzLm1hcChpZHggPT4gc2Vjb25kW2lkeF0pO1xuXG4gICAgLy8gR2V0IGJ5dGUgcGFpciByYW5raW5nIGluIG1lcmdlIHJ1bGVzLlxuICAgIGNvbnN0IHBhaXJzOiBzdHJpbmdbXVtdID0gZmlsdGVyZWRGaXJzdC5tYXAoKGZpcnN0U3ViQXJyLCBpZHgpID0+IHtcbiAgICAgIGNvbnN0IHNlY29uZFN1YkFyciA9IGZpbHRlcmVkU2Vjb25kW2lkeF07XG5cbiAgICAgIHJldHVybiBmaXJzdFN1YkFyci5tYXAoKGNoYXIsIGlkeCkgPT4gYCR7Y2hhcn0gJHtzZWNvbmRTdWJBcnJbaWR4XX1gKTtcbiAgICB9KTtcbiAgICBjb25zdCBwYWlyUmFua3NUZW5zb3IgPSB0aGlzLm1lcmdlUmFua3MubG9va3VwKFxuICAgICAgcGFpcnMubWFwKGFyciA9PiB0ZW5zb3IoYXJyKSkpO1xuICAgIGNvbnN0IHBhaXJSYW5rcyA9IHRlbnNvckFyclRvMkRBcnIocGFpclJhbmtzVGVuc29yKSBhcyBudW1iZXJbXVtdO1xuXG4gICAgLy8gR2V0IEJQRSBwYWlyIHJhbmtzLlxuICAgIGNvbnN0IG1pblBhaXJSYW5rID0gcGFpclJhbmtzLm1hcChcbiAgICAgIGFyciA9PiBhcnIucmVkdWNlKChhLCBiKSA9PiBNYXRoLm1pbihhLCBiKSwgSW5maW5pdHkpKTtcbiAgICBjb25zdCBwYWlyRm91bmRNYXNrID0gbWluUGFpclJhbmsubWFwKFxuICAgICAgcmFuayA9PiByYW5rICE9PSB0aGlzLm1lcmdlUmFua3NMb29rdXBEZWZhdWx0KTtcblxuICAgIC8vIFRva2VucyB0aGF0IGNhbm5vdCBiZSBmdXJ0aGVyIG1lcmdlZCBhcmUgbWFya2VkIGFzIGZpbmlzaGVkLlxuICAgIGZvciAoY29uc3QgW2lkeCwgaW5kZXhdIG9mIG5vbkVtcHR5SW5kaWNlcy5lbnRyaWVzKCkpIHtcbiAgICAgIGNvbnN0IHVwZGF0ZSA9IHBhaXJGb3VuZE1hc2tbaWR4XTtcbiAgICAgIG1hc2tbaW5kZXhdID0gdXBkYXRlO1xuICAgIH1cbiAgICBpZiAoIW1hc2suc29tZShlID0+IGUpKSB7XG4gICAgICByZXR1cm4gW3dvcmRzLCBtYXNrXTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBhcmdNaW4oYXJyOiBudW1iZXJbXSk6IG51bWJlciB7XG4gICAgICByZXR1cm4gYXJyLmluZGV4T2YoYXJyLnJlZHVjZSgoYSwgYikgPT4gTWF0aC5taW4oYSwgYiksIEluZmluaXR5KSk7XG4gICAgfVxuXG4gICAgY29uc3QgbWFza2VkUGFpclJhbmtzID0gcGFpclJhbmtzLmZpbHRlcigoXywgaWR4KSA9PiBwYWlyRm91bmRNYXNrW2lkeF0pO1xuICAgIGNvbnN0IG1pblBhaXJSYW5rSW5kaWNlcyA9IG1hc2tlZFBhaXJSYW5rcy5tYXAoYXJyID0+IGFyZ01pbihhcnIpKTtcblxuICAgIC8vIEdldCB3b3JkcyBhbmQgcGFpcnMgdG8gcHJvY2Vzcy5cbiAgICBjb25zdCB1bmZpbmlzaGVkV29yZHMgPSB3b3Jkc1N0ci5maWx0ZXIoKF8sIGlkeCkgPT4gbWFza1tpZHhdKTtcblxuICAgIGNvbnN0IHBhaXJMZWZ0ID0gdW5maW5pc2hlZFdvcmRzLm1hcChcbiAgICAgICh3b3JkLCBpZHgpID0+IHdvcmRbbWluUGFpclJhbmtJbmRpY2VzW2lkeF1dKTtcblxuICAgIGNvbnN0IHBhaXJSaWdodCA9IHVuZmluaXNoZWRXb3Jkcy5tYXAoXG4gICAgICAod29yZCwgaWR4KSA9PiB3b3JkW21pblBhaXJSYW5rSW5kaWNlc1tpZHhdICsgMV0pO1xuXG4gICAgY29uc3QgbWVyZ2VkUGFpcnMgPSBwYWlyTGVmdC5tYXAoKGxlZnQsIGlkeCkgPT4ge1xuICAgICAgY29uc3QgcmlnaHQgPSBwYWlyUmlnaHRbaWR4XTtcbiAgICAgIHJldHVybiBgJHtsZWZ0fSR7cmlnaHR9YDtcbiAgICB9KTtcbiAgICBjb25zdCB1bmZpbmlzaGVkV29yZHNJbmRpY2VzID0gbWFza1xuICAgICAgLm1hcCgoXywgaWR4KSA9PiBpZHgpXG4gICAgICAuZmlsdGVyKChfLCBpZHgpID0+IG1hc2tbaWR4XSk7XG5cbiAgICBjb25zdCBtZXJnZWRQYWlySW5kaWNlcyA9IHVuZmluaXNoZWRXb3Jkc0luZGljZXMubWFwKFxuICAgICAgKGluZGV4LCBpZHgpID0+IFtpbmRleCwgbWluUGFpclJhbmtJbmRpY2VzW2lkeF1dKTtcbiAgICBjb25zdCBlbXB0eVN0cmluZ0luZGljZXMgPSB1bmZpbmlzaGVkV29yZHNJbmRpY2VzLm1hcChcbiAgICAgIChpbmRleCwgaWR4KSA9PiBbaW5kZXgsIG1pblBhaXJSYW5rSW5kaWNlc1tpZHhdICsgMV0pO1xuXG4gICAgZm9yIChjb25zdCBbaWR4LCBpbmRpY2VzXSBvZiBtZXJnZWRQYWlySW5kaWNlcy5lbnRyaWVzKCkpIHtcbiAgICAgIGNvbnN0IFt3b3JkSWR4LCBjaGFySWR4XSA9IGluZGljZXM7XG4gICAgICBjb25zdCBtZXJnZWRQYWlyID0gbWVyZ2VkUGFpcnNbaWR4XTtcbiAgICAgIHdvcmRzU3RyW3dvcmRJZHhdW2NoYXJJZHhdID0gbWVyZ2VkUGFpcjtcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IGluZGljZXMgb2YgZW1wdHlTdHJpbmdJbmRpY2VzKSB7XG4gICAgICBjb25zdCBbd29yZElkeCwgY2hhcklkeF0gPSBpbmRpY2VzO1xuICAgICAgd29yZHNTdHJbd29yZElkeF1bY2hhcklkeF0gPSAnJztcbiAgICB9XG5cbiAgICB3b3JkcyA9IHdvcmRzU3RyLm1hcCh3b3JkID0+IHRlbnNvcih3b3JkKSk7XG4gICAgd29yZHMgPSByZW1vdmVTdHJpbmdzRnJvbUlucHV0cyh3b3JkcywgJycpO1xuXG4gICAgcmV0dXJuIFt3b3JkcywgbWFza107XG4gIH1cblxuICAvKipcbiAgICogUGVyZm9ybSBieXRlLXBhaXIgbWVyZ2UgZm9yIGVhY2ggd29yZCBpbiB0aGUgaW5wdXRzLlxuICAgKi9cbiAgcHJpdmF0ZSBicGVNZXJnZSh3b3JkczogVGVuc29yW10pOiBUZW5zb3JbXSB7XG4gICAgY29uc3QgbnVtV29yZHMgPSB3b3Jkcy5sZW5ndGg7XG5cbiAgICAvLyBNZXJnZSBieXRlcy5cbiAgICBmdW5jdGlvbiBsb29wQ29uZGl0aW9uKG1hc2s6IGJvb2xlYW5bXSk6IGJvb2xlYW4ge1xuICAgICAgcmV0dXJuIG1hc2suc29tZShlID0+IGUpO1xuICAgIH1cblxuICAgIGNvbnN0IGluaXRpYWxNYXNrOiBib29sZWFuW10gPSBBcnJheShudW1Xb3JkcykuZmlsbCh0cnVlKTtcblxuICAgIGxldCBtZXJnZWRXb3JkcyA9IHdvcmRzO1xuICAgIGxldCBtYXNrID0gaW5pdGlhbE1hc2s7XG4gICAgd2hpbGUgKGxvb3BDb25kaXRpb24obWFzaykpIHtcbiAgICAgIFttZXJnZWRXb3JkcywgbWFza10gPSB0aGlzLmJwZU1lcmdlT25lU3RlcChtZXJnZWRXb3JkcywgbWFzayk7XG4gICAgfVxuXG4gICAgcmV0dXJuIG1lcmdlZFdvcmRzO1xuICB9XG5cbiAgLyoqXG4gICAqIE1hcCB0b2tlbiBieXRlcyB0byB1bmljb2RlIHVzaW5nIGBieXRlMnVuaWNvZGVgLlxuICAgKi9cbiAgcHJpdmF0ZSB0cmFuc2Zvcm1CeXRlcyh0b2tlbnM6IFRlbnNvcik6IFRlbnNvcltdIHtcbiAgICBjb25zdCB0b2tlbnNTdHIgPSB0ZW5zb3JUb0Fycih0b2tlbnMpIGFzIHN0cmluZ1tdO1xuXG4gICAgY29uc3Qgc3BsaXRCeXRlcyA9IHRva2Vuc1N0ci5tYXAoXG4gICAgICB0b2tlbiA9PiB0ZW5zb3IodG9rZW4uc3BsaXQoJycpLm1hcChjID0+IGMuY2hhckNvZGVBdCgwKSkpKTtcbiAgICBjb25zdCBzcGxpdFVuaWNvZGUgPSB0aGlzLmJ5dGUyVW5pY29kZS5sb29rdXAoc3BsaXRCeXRlcyk7XG5cbiAgICByZXR1cm4gc3BsaXRVbmljb2RlO1xuICB9XG5cbiAgLyoqXG4gICAqIFByb2Nlc3MgdW5zZWVuIHRva2VucyBhbmQgYWRkIHRvIGNhY2hlLlxuICAgKi9cbiAgcHJpdmF0ZSBicGVNZXJnZUFuZFVwZGF0ZUNhY2hlKHRva2VuczogVGVuc29yKSB7XG4gICAgY29uc3Qgd29yZHMgPSB0aGlzLnRyYW5zZm9ybUJ5dGVzKHRva2Vucyk7XG4gICAgY29uc3QgdG9rZW5pemVkV29yZHNUZW5zb3IgPSB0aGlzLmJwZU1lcmdlKHdvcmRzKTtcbiAgICBjb25zdCB0b2tlbml6ZWRXb3JkcyA9IHRlbnNvckFyclRvMkRBcnIodG9rZW5pemVkV29yZHNUZW5zb3IpIGFzIHN0cmluZ1tdW107XG5cbiAgICAvLyBGb3IgZWFjaCB3b3JkLCBqb2luIGFsbCBpdHMgdG9rZW4gYnkgYSB3aGl0ZXNwYWNlLFxuICAgIC8vIGUuZy4sIFtcImRyYWdvblwiLCBcImZseVwiXSA9PiBcImRyYWdvbiBmbHlcIiBmb3IgaGFzaCBwdXJwb3NlLlxuICAgIGNvbnN0IGpvaW5lZFRva2VucyA9IHRva2VuaXplZFdvcmRzLm1hcCh3b3JkID0+IHdvcmQuam9pbignICcpKTtcblxuICAgIHRoaXMuY2FjaGUuaW5zZXJ0KHRva2Vucywgam9pbmVkVG9rZW5zKTtcbiAgfVxuXG4gIHRva2VuaXplKGlucHV0czogVGVuc29yKTogVGVuc29yW10ge1xuICAgIHJldHVybiB0aWR5KCgpID0+IHtcbiAgICAgIGlmICh0aGlzLmFkZFByZWZpeFNwYWNlKSB7XG4gICAgICAgIGNvbnN0IHN0cklucHV0cyA9IHRlbnNvclRvQXJyKGlucHV0cykgYXMgc3RyaW5nW107XG4gICAgICAgIGlucHV0cyA9IHRlbnNvcihzdHJJbnB1dHMubWFwKHdvcmQgPT4gJyAnICsgd29yZCkpO1xuICAgICAgfVxuXG4gICAgICBjb25zdCByYXdUb2tlbnNUZW5zb3IgPVxuICAgICAgICBzcGxpdFN0cmluZ3NGb3JCcGUoaW5wdXRzLCB0aGlzLnVuc3BsaXR0YWJsZVRva2Vucyk7XG4gICAgICBjb25zdCByYXdUb2tlbnMgPSB0ZW5zb3JBcnJUbzJEQXJyKHJhd1Rva2Vuc1RlbnNvcikgYXMgc3RyaW5nW11bXTtcblxuICAgICAgY29uc3QgdG9rZW5Sb3dTcGxpdHMgPSBbMF07XG4gICAgICBmb3IgKGNvbnN0IFtpZHgsIHRva2VuXSBvZiByYXdUb2tlbnMuZW50cmllcygpKSB7XG4gICAgICAgIHRva2VuUm93U3BsaXRzLnB1c2godG9rZW5Sb3dTcGxpdHNbaWR4XSArIHRva2VuLmxlbmd0aCk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGZsYXRUb2tlbnMgPSByYXdUb2tlbnMucmVkdWNlKChhY2MsIGUpID0+IGFjYy5jb25jYXQoZSksIFtdKTtcblxuICAgICAgLy8gQ2hlY2sgY2FjaGUuXG4gICAgICBjb25zdCBjYWNoZUxvb2t1cCA9IHRoaXMuY2FjaGUubG9va3VwKGZsYXRUb2tlbnMpO1xuICAgICAgY29uc3QgY2FjaGVNYXNrID0gY2FjaGVMb29rdXAubWFwKGUgPT4gZSA9PT0gJycpO1xuXG4gICAgICBjb25zdCBoYXNVbnNlZW5Xb3JkcyA9IGNhY2hlTWFzay5zb21lKFxuICAgICAgICAoYm9vbCwgaWR4KSA9PiBib29sICYmIGZsYXRUb2tlbnNbaWR4XSAhPT0gJycpO1xuXG4gICAgICBjb25zdCBwcm9jZXNzVW5zZWVuVG9rZW5zID0gKCk6IHN0cmluZ1tdICA9PiB7XG4gICAgICAgIGNvbnN0IHVuc2VlblRva2VucyA9IGZsYXRUb2tlbnMuZmlsdGVyKChfLCBpZHgpID0+IGNhY2hlTWFza1tpZHhdKTtcbiAgICAgICAgdGhpcy5icGVNZXJnZUFuZFVwZGF0ZUNhY2hlKHRlbnNvcih1bnNlZW5Ub2tlbnMpKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuY2FjaGUubG9va3VwKGZsYXRUb2tlbnMpO1xuICAgICAgfTtcblxuICAgICAgLy8gSWYgYGhhc191bnNlZW5fd29yZHMgPT0gVHJ1ZWAsIGl0IG1lYW5zIG5vdCBhbGwgdG9rZW5zIGFyZSBpbiBjYWNoZSxcbiAgICAgIC8vIHdlIHdpbGwgcHJvY2VzcyB0aGUgdW5zZWVuIHRva2Vucy4gT3RoZXJ3aXNlIHJldHVybiB0aGUgY2FjaGUgbG9va3VwLlxuICAgICAgY29uc3QgdG9rZW5pemVkV29yZHMgPVxuICAgICAgICBoYXNVbnNlZW5Xb3JkcyA/IHByb2Nlc3NVbnNlZW5Ub2tlbnMoKSA6IGNhY2hlTG9va3VwO1xuXG4gICAgICBjb25zdCB0b2tlbnNUZW5zb3IgPSB0aGlzLnRva2VuVG9JZE1hcC5sb29rdXAoXG4gICAgICAgIHRva2VuaXplZFdvcmRzLm1hcCh3b3JkID0+IHRlbnNvcih3b3JkLnNwbGl0KCcgJykpKSk7XG4gICAgICBjb25zdCB0b2tlbnMgPSB0b2tlbnNUZW5zb3IubWFwKHQgPT4gQXJyYXkuZnJvbSh0LmRhdGFTeW5jKCkpKTtcblxuICAgICAgLy8gVW5mbGF0dGVuIHRvIG1hdGNoIGlucHV0LlxuICAgICAgY29uc3QgbmV3VG9rZW5Sb3dTcGxpdHMgPSBbMF07XG4gICAgICBmb3IgKGNvbnN0IFtpZHgsIHRva2VuXSBvZiB0b2tlbnMuZW50cmllcygpKSB7XG4gICAgICAgIG5ld1Rva2VuUm93U3BsaXRzLnB1c2gobmV3VG9rZW5Sb3dTcGxpdHNbaWR4XSArIHRva2VuLmxlbmd0aCk7XG4gICAgICB9XG4gICAgICBjb25zdCBuZXdGbGF0VG9rZW5zID0gdG9rZW5zLnJlZHVjZSgoYWNjLCBlKSA9PiBhY2MuY29uY2F0KGUpLCBbXSk7XG4gICAgICBjb25zdCBnYXRoZXJlZEluZGljZXMgPVxuICAgICAgICB0b2tlblJvd1NwbGl0cy5tYXAoaW5kZXggPT4gbmV3VG9rZW5Sb3dTcGxpdHNbaW5kZXhdKTtcblxuICAgICAgbGV0IHRva2VuczJEOiBUZW5zb3JbXSA9IFtdO1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBnYXRoZXJlZEluZGljZXMubGVuZ3RoIC0gMTsgaSsrKSB7XG4gICAgICAgIGNvbnN0IFtzdGFydCwgZW5kXSA9IFtnYXRoZXJlZEluZGljZXNbaV0sIGdhdGhlcmVkSW5kaWNlc1tpKzFdXTtcbiAgICAgICAgY29uc3Qgcm93ID0gbmV3RmxhdFRva2Vucy5zbGljZShzdGFydCwgZW5kKTtcbiAgICAgICAgdG9rZW5zMkQucHVzaCh0ZW5zb3Iocm93KSk7XG4gICAgICB9XG5cbiAgICAgIC8vIENvbnZlcnQgdG8gYSBkZW5zZSBvdXRwdXQgaWYgYHNlcXVlbmNlTGVuZ3RoYCBpcyBzZXQuXG4gICAgICBpZiAodGhpcy5zZXF1ZW5jZUxlbmd0aCkge1xuICAgICAgICAvLyBwYWQgb3IgdHJ1bmNhdGVcbiAgICAgICAgdG9rZW5zMkQgPSB0b2tlbnMyRC5tYXAodCA9PiB7XG4gICAgICAgICAgaWYgKHQuc2l6ZSA9PT0gdGhpcy5zZXF1ZW5jZUxlbmd0aCkge1xuICAgICAgICAgICAgcmV0dXJuIHQ7XG4gICAgICAgICAgfSBlbHNlIGlmICh0LnNpemUgPiB0aGlzLnNlcXVlbmNlTGVuZ3RoKSB7XG4gICAgICAgICAgICByZXR1cm4gdC5zbGljZSgwLCB0aGlzLnNlcXVlbmNlTGVuZ3RoKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIHQucGFkKFtbMCwgdGhpcy5zZXF1ZW5jZUxlbmd0aCAtIHQuc2l6ZV1dKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gdG9rZW5zMkQ7XG4gICAgfSk7XG4gIH1cblxuICBvdmVycmlkZSBkZXRva2VuaXplKGlucHV0czogVGVuc29yW10pOiBUZW5zb3Ige1xuICAgIGNvbnN0IHVuaWNvZGVUZXh0ID0gdGhpcy5pZFRvVG9rZW5NYXAubG9va3VwKGlucHV0cylcbiAgICAgIC5tYXAodCA9PiAodGVuc29yVG9BcnIodCkgYXMgc3RyaW5nW10pLmpvaW4oJycpKTtcblxuICAgIHJldHVybiB0ZW5zb3IodW5pY29kZVRleHQpO1xuICB9XG59XG5zZXJpYWxpemF0aW9uLnJlZ2lzdGVyQ2xhc3MoQnl0ZVBhaXJUb2tlbml6ZXIpO1xuIl19