UNPKG

82.2 kBJavaScriptView Raw
1/**
2 * @license
3 * Copyright 2018 Google LLC. All Rights Reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 * =============================================================================
16 */
17import { tidy, util } from '@tensorflow/tfjs-core';
18import { getNodeNameAndIndex, getParamValue, getTensor, getTensorsForCurrentContenxt, parseNodeName } from '../operations/executors/utils';
19import { executeOp } from '../operations/operation_executor';
20import { ExecutionContext } from './execution_context';
21import { getExecutionSubgraph, getNodesInTopologicalOrder, isControlFlow } from './model_analysis';
22export class GraphExecutor {
23 /**
24 *
25 * @param graph Graph the model or function graph to be executed.
26 * @param parent When building function exector you need to set the parent
27 * executor. Since the weights and function executor maps are set at parant
28 * level, that function executor can access the function maps and weight maps
29 * through the parent.
30 */
31 constructor(graph, parent) {
32 this.graph = graph;
33 this.parent = parent;
34 this.compiledMap = new Map();
35 this._weightMap = {};
36 this.SEPERATOR = ',';
37 this._functions = {};
38 this._functionExecutorMap = {};
39 this._outputs = graph.outputs;
40 this._inputs = graph.inputs;
41 this._initNodes = graph.initNodes;
42 this._signature = graph.signature;
43 this._functions = graph.functions;
44 // create sub-graph executors
45 if (graph.functions != null) {
46 Object.keys(graph.functions).forEach(name => {
47 this._functionExecutorMap[name] =
48 new GraphExecutor(graph.functions[name], this);
49 });
50 }
51 }
52 get weightIds() {
53 return this.parent ? this.parent.weightIds : this._weightIds;
54 }
55 get functionExecutorMap() {
56 return this.parent ? this.parent.functionExecutorMap :
57 this._functionExecutorMap;
58 }
59 get weightMap() {
60 return this.parent ? this.parent.weightMap : this._weightMap;
61 }
62 set weightMap(weightMap) {
63 const weightIds = Object.keys(weightMap).map(key => weightMap[key].map(tensor => tensor.id));
64 this._weightIds = [].concat(...weightIds);
65 this._weightMap = weightMap;
66 }
67 /**
68 * Set `ResourceManager` shared by executors of a model.
69 * @param resourceManager: `ResourceManager` of the `GraphModel`.
70 */
71 set resourceManager(resourceManager) {
72 this._resourceManager = resourceManager;
73 }
74 get inputs() {
75 return this._inputs.map(node => {
76 return {
77 name: node.name,
78 shape: node.attrParams['shape'] ?
79 node.attrParams['shape'].value :
80 undefined,
81 dtype: node.attrParams['dtype'] ?
82 node.attrParams['dtype'].value :
83 undefined
84 };
85 });
86 }
87 get outputs() {
88 return this._outputs.map(node => {
89 return {
90 name: node.name,
91 shape: node.attrParams['shape'] ?
92 node.attrParams['shape'].value :
93 undefined,
94 dtype: node.attrParams['dtype'] ?
95 node.attrParams['dtype'].value :
96 undefined
97 };
98 });
99 }
100 get inputNodes() {
101 return this._inputs.map(node => node.signatureKey || node.name);
102 }
103 get outputNodes() {
104 return this._outputs.map((node) => {
105 const name = node.signatureKey || node.name;
106 return node.defaultOutput ? (`${name}:${node.defaultOutput}`) : name;
107 });
108 }
109 get functions() {
110 return Object.keys(this._functions).reduce((map, key) => {
111 map[key] = this._functions[key].signature;
112 return map;
113 }, {});
114 }
115 getCompilationKey(inputs, outputs) {
116 const sortedInputs = inputs.map(node => node.name).sort();
117 const sortedOutputs = outputs.map(node => node.name).sort();
118 return sortedInputs.join(this.SEPERATOR) + '--' +
119 sortedOutputs.join(this.SEPERATOR);
120 }
121 /**
122 * Compiles the inference graph and returns the minimal set of nodes that are
123 * required for execution, in the correct execution order.
124 */
125 compile(inputs, outputs) {
126 const executionInfo = getExecutionSubgraph(inputs, outputs, this.weightMap, this._initNodes);
127 const { missingInputs, dynamicNode, syncInputs } = executionInfo;
128 if (dynamicNode != null) {
129 throw new Error(`This execution contains the node '${dynamicNode.name}', which has ` +
130 `the dynamic op '${dynamicNode.op}'. Please use ` +
131 `model.executeAsync() instead. Alternatively, to avoid the ` +
132 `dynamic ops, specify the inputs [${syncInputs}]`);
133 }
134 if (missingInputs.length > 0) {
135 const outNames = outputs.map(n => n.name);
136 const inNames = Object.keys(inputs);
137 throw new Error(`Cannot compute the outputs [${outNames}] from the provided inputs ` +
138 `[${inNames}]. Missing the following inputs: [${missingInputs}]`);
139 }
140 return getNodesInTopologicalOrder(this.graph, this.weightMap, executionInfo);
141 }
142 /**
143 * Executes the inference for given input tensors.
144 * @param inputs Tensor map for the model inputs, keyed by the input node
145 * names.
146 * @param outputs Optional. output node name from the Tensorflow model, if
147 * no outputs are specified, the default outputs of the model would be used.
148 * You can inspect intermediate nodes of the model by adding them to the
149 * outputs array.
150 */
151 execute(inputs, outputs) {
152 inputs = this.mapInputs(inputs);
153 const names = Object.keys(inputs).sort();
154 this.checkInputs(inputs);
155 this.checkInputShapeAndType(inputs);
156 outputs = this.mapOutputs(outputs);
157 this.checkOutputs(outputs);
158 const inputNodes = names.map(name => this.graph.nodes[parseNodeName(name)[0]]);
159 const outputNodeNames = outputs.map(name => parseNodeName(name)[0]);
160 let outputNodes = outputNodeNames.map(name => this.graph.nodes[name]);
161 // If no outputs are specified, then use the default outputs of the model.
162 if (outputNodes.length === 0) {
163 outputNodes = this._outputs;
164 }
165 const compilationKey = this.getCompilationKey(inputNodes, outputNodes);
166 // Do nothing if the compiled graph cache contains the input.
167 let orderedNodes = this.compiledMap.get(compilationKey);
168 if (orderedNodes == null) {
169 orderedNodes = this.compile(inputs, outputNodes);
170 this.compiledMap.set(compilationKey, orderedNodes);
171 }
172 const tensorArrayMap = {};
173 const tensorListMap = {};
174 return tidy(() => {
175 const context = new ExecutionContext(this.weightMap, tensorArrayMap, tensorListMap, this.functionExecutorMap);
176 const tensorsMap = Object.assign({}, this.weightMap);
177 Object.keys(inputs).forEach(name => {
178 const [nodeName, index] = parseNodeName(name);
179 const tensors = [];
180 tensors[index] = inputs[name];
181 tensorsMap[nodeName] = tensors;
182 });
183 const tensorsToKeep = this.getFrozenTensorIds(tensorsMap);
184 const intermediateTensorConsumerCount = {};
185 for (let i = 0; i < orderedNodes.length; i++) {
186 const node = orderedNodes[i];
187 if (!tensorsMap[node.name]) {
188 const tensors = executeOp(node, tensorsMap, context, this._resourceManager);
189 if (util.isPromise(tensors)) {
190 throw new Error(`The execution of the op '${node.op}' returned a promise. ` +
191 `Please use model.executeAsync() instead.`);
192 }
193 tensorsMap[node.name] = tensors;
194 this.checkTensorForDisposal(node.name, node, tensorsMap, context, tensorsToKeep, outputNodeNames, intermediateTensorConsumerCount);
195 }
196 }
197 // dispose the context for the root executor
198 if (this.parent == null) {
199 context.dispose(tensorsToKeep);
200 }
201 return outputs.map(name => getTensor(name, tensorsMap, context));
202 });
203 }
204 getFrozenTensorIds(tensorMap) {
205 const ids = [].concat.apply([], Object.keys(tensorMap)
206 .map(key => tensorMap[key])
207 .map(tensors => tensors.map(tensor => tensor.id)));
208 return new Set(ids);
209 }
210 checkTensorForDisposal(nodeName, node, tensorMap, context, tensorsToKeep, outputNames, intermediateTensorConsumerCount) {
211 // Skip output nodes and any control flow nodes, since its dependency is
212 // tricky to track correctly.
213 if (node.category === 'control' || outputNames.indexOf(nodeName) !== -1) {
214 return;
215 }
216 tensorMap[nodeName].forEach(tensor => {
217 if (tensor != null) {
218 intermediateTensorConsumerCount[tensor.id] =
219 (intermediateTensorConsumerCount[tensor.id] || 0) +
220 node.children.length;
221 }
222 });
223 node.inputs.forEach(input => {
224 // Skip any control flow nodes, since its dependency is tricky to track
225 // correctly.
226 if (input.category !== 'control') {
227 const tensors = getTensorsForCurrentContenxt(input.name, tensorMap, context);
228 if (tensors != null) {
229 tensors.forEach(tensor => {
230 if (tensor && !tensor.kept && !tensorsToKeep.has(tensor.id)) {
231 const count = intermediateTensorConsumerCount[tensor.id];
232 if (count === 1) {
233 tensor.dispose();
234 delete intermediateTensorConsumerCount[tensor.id];
235 }
236 else if (count != null) {
237 // only intermediate nodes has count set, inputs and weights are
238 // not.
239 intermediateTensorConsumerCount[tensor.id]--;
240 }
241 }
242 });
243 }
244 }
245 });
246 }
247 /**
248 * Executes the inference for given input tensors in Async fashion.
249 * @param inputs Tensor map for the model inputs, keyed by the input node
250 * names.
251 * @param outputs output node name from the Tensorflow model, if no outputs
252 * are specified, the default outputs of the model would be used. You can
253 * inspect intermediate nodes of the model by adding them to the outputs
254 * array.
255 */
256 async executeAsync(inputs, outputs) {
257 return this._executeAsync(inputs, outputs);
258 }
259 /**
260 * Executes the inference for given input tensors in Async fashion.
261 * @param inputs Tensor map for the model inputs, keyed by the input node
262 * names.
263 * @param outputs Optional. output node name from the Tensorflow model,
264 * if no outputs are specified, the default outputs of the model would be
265 * used. You can inspect intermediate nodes of the model by adding them to the
266 * outputs array.
267 * @param isFunctionExecution Optional. Flag for executing a function.
268 * @param tensorArrayMap Optional, global TensorArray map by id. Used for
269 * function execution.
270 * @param tensorArrayMap Optinal global TensorList map by id. Used for
271 * function execution.
272 */
273 async _executeAsync(inputs, outputs, isFunctionExecution = false, tensorArrayMap = {}, tensorListMap = {}) {
274 if (!isFunctionExecution) {
275 inputs = this.mapInputs(inputs);
276 this.checkInputs(inputs);
277 this.checkInputShapeAndType(inputs);
278 outputs = this.mapOutputs(outputs);
279 this.checkOutputs(outputs);
280 }
281 const context = new ExecutionContext(this.weightMap, tensorArrayMap, tensorListMap, this.functionExecutorMap);
282 // Graph with control flow op requires runtime evaluation of the execution
283 // order, while without control flow the execution order is pre-determined
284 // in the compile method.
285 const tensorMap = await this.executeWithControlFlow(inputs, context, outputs, isFunctionExecution);
286 const results = outputs.map(name => getTensor(name, tensorMap, context));
287 // dispose all the intermediate tensors
288 const outputIds = results.map(t => t.id);
289 const inputIds = Object.keys(inputs).map(name => inputs[name].id);
290 const keepIds = new Set([...outputIds, ...inputIds, ...this.weightIds]);
291 Object.keys(tensorMap).forEach(key => {
292 const tensorArray = tensorMap[key];
293 tensorArray.forEach(tensor => {
294 if (tensor && !tensor.kept && !tensor.isDisposed &&
295 !keepIds.has(tensor.id)) {
296 tensor.dispose();
297 }
298 });
299 });
300 // dispose the context for the root executor
301 if (this.parent == null) {
302 context.dispose(keepIds);
303 }
304 return results;
305 }
306 async executeFunctionAsync(inputs, tensorArrayMap, tensorListMap) {
307 const mappedInputs = inputs.reduce((map, tensor, index) => {
308 map[this.inputs[index].name] = tensor;
309 return map;
310 }, {});
311 return this._executeAsync(mappedInputs, this.outputNodes, true, tensorArrayMap, tensorListMap);
312 }
313 /**
314 * When there are control flow nodes in the graph, the graph execution use
315 * ExecutionContext to keep track of the frames and loop iterators.
316 * @param inputs placeholder tensors for the graph.
317 * @param context the execution context object for current execution.
318 * @param outputNames Optional. output node name from the Tensorflow model,
319 * if no outputs are specified, the default outputs of the model would be
320 * used. You can inspect intermediate nodes of the model by adding them to the
321 * outputs array.
322 * @param isFunctionExecution Flag for executing a function.
323 */
324 async executeWithControlFlow(inputs, context, outputNames, isFunctionExecution) {
325 const names = Object.keys(inputs);
326 const inputNodes = names.map(name => this.graph.nodes[parseNodeName(name)[0]]);
327 const outputNodeNames = outputNames.map(name => parseNodeName(name)[0]);
328 let outputNodes = outputNodeNames.map(name => this.graph.nodes[name]);
329 // If no outputs are specified, then use the default outputs of the model.
330 if (outputNodes.length === 0) {
331 outputNodes = this._outputs;
332 }
333 const { usedNodes, missingInputs, dynamicNode, syncInputs } = getExecutionSubgraph(inputs, outputNodes, this.weightMap, this._initNodes);
334 // First nodes to execute include inputNodes, weights, and initNodes.
335 const stack = [
336 ...inputNodes, ...this.graph.weights, ...(this._initNodes || [])
337 ].map(node => {
338 return { node, contexts: context.currentContext };
339 });
340 const tensorsMap = Object.assign({}, this.weightMap);
341 Object.keys(inputs).forEach(name => {
342 const [nodeName, index] = parseNodeName(name);
343 const tensors = [];
344 tensors[index] = inputs[name];
345 tensorsMap[nodeName] = tensors;
346 });
347 const intermediateTensorConsumerCount = {};
348 const tensorsToKeep = this.getFrozenTensorIds(tensorsMap);
349 const added = {};
350 while (stack.length > 0) {
351 const promises = this.processStack(inputNodes, stack, context, tensorsMap, added, tensorsToKeep, outputNodeNames, intermediateTensorConsumerCount, usedNodes);
352 await Promise.all(promises);
353 }
354 if (dynamicNode == null && !isFunctionExecution) {
355 console.warn(`This model execution did not contain any nodes with control flow ` +
356 `or dynamic output shapes. You can use model.execute() instead.`);
357 }
358 const missingOutputs = outputNodes
359 .filter(node => !isControlFlow(node) &&
360 !getTensor(node.name, tensorsMap, context))
361 .map(node => node.name);
362 if (missingOutputs.length > 0) {
363 let alternativeMsg = '';
364 if (dynamicNode != null) {
365 alternativeMsg =
366 `Alternatively, to avoid the dynamic ops, use model.execute() ` +
367 `and specify the inputs [${syncInputs}]`;
368 }
369 throw new Error(`Cannot compute the outputs [${missingOutputs}] from the provided ` +
370 `inputs [${names}]. Consider providing the following inputs: ` +
371 `[${missingInputs}]. ${alternativeMsg}`);
372 }
373 return tensorsMap;
374 }
375 processStack(inputNodes, stack, context, tensorMap, added, tensorsToKeep, outputNames, intermediateTensorConsumerCount, usedNodes) {
376 const promises = [];
377 while (stack.length > 0) {
378 const item = stack.pop();
379 context.currentContext = item.contexts;
380 let nodeName = '';
381 // The tensor of the Enter op with isConstant set should be set
382 // in the parent scope, so it will be available as constant for the
383 // whole loop.
384 if (item.node.op === 'Enter' &&
385 getParamValue('isConstant', item.node, tensorMap, context)) {
386 [nodeName] = getNodeNameAndIndex(item.node.name, context);
387 }
388 // only process nodes that are not in the tensorMap yet, this include
389 // inputNodes and internal initNodes.
390 if (tensorMap[item.node.name] == null) {
391 const tensors = executeOp(item.node, tensorMap, context, this._resourceManager);
392 if (!nodeName) {
393 [nodeName] = getNodeNameAndIndex(item.node.name, context);
394 }
395 const currentContext = context.currentContext;
396 if (util.isPromise(tensors)) {
397 promises.push(tensors.then(t => {
398 tensorMap[nodeName] = t;
399 context.currentContext = currentContext;
400 this.checkTensorForDisposal(nodeName, item.node, tensorMap, context, tensorsToKeep, outputNames, intermediateTensorConsumerCount);
401 this.processChildNodes(item.node, stack, context, tensorMap, added, usedNodes);
402 return t;
403 }));
404 }
405 else {
406 tensorMap[nodeName] = tensors;
407 this.checkTensorForDisposal(nodeName, item.node, tensorMap, context, tensorsToKeep, outputNames, intermediateTensorConsumerCount);
408 this.processChildNodes(item.node, stack, context, tensorMap, added, usedNodes);
409 }
410 }
411 else {
412 this.processChildNodes(item.node, stack, context, tensorMap, added, usedNodes);
413 }
414 }
415 return promises;
416 }
417 processChildNodes(node, stack, context, tensorMap, added, usedNodes) {
418 node.children.forEach((childNode) => {
419 const [nodeName,] = getNodeNameAndIndex(childNode.name, context);
420 if (added[nodeName] || !usedNodes.has(childNode.name)) {
421 return;
422 }
423 // Merge op can be pushed if any of its inputs has value.
424 if (childNode.op === 'Merge') {
425 if (childNode.inputNames.some(name => {
426 return !!getTensor(name, tensorMap, context);
427 })) {
428 added[nodeName] = true;
429 stack.push({ contexts: context.currentContext, node: childNode });
430 }
431 }
432 else // Otherwise all inputs must to have value.
433 if (childNode.inputNames.every(name => {
434 return !!getTensor(name, tensorMap, context);
435 })) {
436 added[nodeName] = true;
437 stack.push({ contexts: context.currentContext, node: childNode });
438 }
439 });
440 }
441 /**
442 * Releases the memory used by the weight tensors.
443 */
444 dispose() {
445 Object.keys(this.weightMap)
446 .forEach(key => this.weightMap[key].forEach(tensor => tensor.dispose()));
447 }
448 checkInputShapeAndType(inputs) {
449 Object.keys(inputs).forEach(name => {
450 const input = inputs[name];
451 const [nodeName,] = parseNodeName(name);
452 const node = this.graph.nodes[nodeName];
453 if (node.attrParams['shape'] && node.attrParams['shape'].value) {
454 const shape = node.attrParams['shape'].value;
455 const match = shape.length === input.shape.length &&
456 input.shape.every((dim, index) => shape[index] === -1 || shape[index] === dim);
457 util.assert(match, () => `The shape of dict['${node.name}'] provided in ` +
458 `model.execute(dict) must be [${shape}], but was ` +
459 `[${input.shape}]`);
460 }
461 if (node.attrParams['dtype'] && node.attrParams['dtype'].value) {
462 util.assert(input.dtype === node.attrParams['dtype'].value, () => `The dtype of dict['${node.name}'] provided in ` +
463 `model.execute(dict) must be ` +
464 `${node.attrParams['dtype'].value}, but was ${input.dtype}`);
465 }
466 });
467 }
468 mapInputs(inputs) {
469 const result = {};
470 for (const inputName in inputs) {
471 if (this._signature != null && this._signature.inputs != null &&
472 this._signature.inputs[inputName] != null) {
473 const tensor = this._signature.inputs[inputName];
474 result[tensor.name] = inputs[inputName];
475 }
476 else {
477 result[inputName] = inputs[inputName];
478 }
479 }
480 return result;
481 }
482 checkInputs(inputs) {
483 const notInGraph = Object.keys(inputs).filter(name => {
484 const [nodeName] = parseNodeName(name);
485 return this.graph.nodes[nodeName] == null;
486 });
487 if (notInGraph.length > 0) {
488 throw new Error(`The dict provided in model.execute(dict) has ` +
489 `keys: [${notInGraph}] that are not part of graph`);
490 }
491 }
492 mapOutputs(outputs) {
493 return outputs.map(name => {
494 if (this._signature != null && this._signature.outputs != null &&
495 this._signature.outputs[name] != null) {
496 const tensor = this._signature.outputs[name];
497 return tensor.name;
498 }
499 return name;
500 }, {});
501 }
502 checkOutputs(outputs) {
503 outputs.forEach(name => {
504 const [normalizedName] = parseNodeName(name);
505 if (!this.graph.nodes[normalizedName]) {
506 throw new Error(`The output '${name}' is not found in the graph`);
507 }
508 });
509 }
510}
511//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JhcGhfZXhlY3V0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi90ZmpzLWNvbnZlcnRlci9zcmMvZXhlY3V0b3IvZ3JhcGhfZXhlY3V0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBRUgsT0FBTyxFQUFtQyxJQUFJLEVBQUUsSUFBSSxFQUFDLE1BQU0sdUJBQXVCLENBQUM7QUFJbkYsT0FBTyxFQUFDLG1CQUFtQixFQUFFLGFBQWEsRUFBRSxTQUFTLEVBQUUsNEJBQTRCLEVBQUUsYUFBYSxFQUFDLE1BQU0sK0JBQStCLENBQUM7QUFDekksT0FBTyxFQUFDLFNBQVMsRUFBQyxNQUFNLGtDQUFrQyxDQUFDO0FBRzNELE9BQU8sRUFBQyxnQkFBZ0IsRUFBdUIsTUFBTSxxQkFBcUIsQ0FBQztBQUMzRSxPQUFPLEVBQUMsb0JBQW9CLEVBQUUsMEJBQTBCLEVBQUUsYUFBYSxFQUFDLE1BQU0sa0JBQWtCLENBQUM7QUFTakcsTUFBTSxPQUFPLGFBQWE7SUF1RnhCOzs7Ozs7O09BT0c7SUFDSCxZQUFvQixLQUFZLEVBQVUsTUFBc0I7UUFBNUMsVUFBSyxHQUFMLEtBQUssQ0FBTztRQUFVLFdBQU0sR0FBTixNQUFNLENBQWdCO1FBOUZ4RCxnQkFBVyxHQUF3QixJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQzdDLGVBQVUsR0FBb0IsRUFBRSxDQUFDO1FBTWpDLGNBQVMsR0FBRyxHQUFHLENBQUM7UUFDaEIsZUFBVSxHQUEyQixFQUFFLENBQUM7UUFDeEMseUJBQW9CLEdBQXNDLEVBQUUsQ0FBQztRQXNGbkUsSUFBSSxDQUFDLFFBQVEsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDO1FBQzlCLElBQUksQ0FBQyxPQUFPLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQztRQUM1QixJQUFJLENBQUMsVUFBVSxHQUFHLEtBQUssQ0FBQyxTQUFTLENBQUM7UUFDbEMsSUFBSSxDQUFDLFVB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QUE4QixDQUFDLENBQUM7U0FDekQ7SUFDSCxDQUFDO0lBRU8sVUFBVSxDQUFDLE9BQWlCO1FBQ2xDLE9BQU8sT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRTtZQUN4QixJQUFJLElBQUksQ0FBQyxVQUFVLElBQUksSUFBSSxJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxJQUFJLElBQUk7Z0JBQzFELElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLElBQUksRUFBRTtnQkFDekMsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQzdDLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQzthQUNwQjtZQUNELE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ1QsQ0FBQztJQUVPLFlBQVksQ0FBQyxPQUFpQjtRQUNwQyxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFO1lBQ3JCLE1BQU0sQ0FBQyxjQUFjLENBQUMsR0FBRyxhQUFhLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDN0MsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLGNBQWMsQ0FBQyxFQUFFO2dCQUNyQyxNQUFNLElBQUksS0FBSyxDQUFDLGVBQWUsSUFBSSw2QkFBNkIsQ0FBQyxDQUFDO2FBQ25FO1FBQ0gsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgMjAxOCBHb29nbGUgTExDLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICovXG5cbmltcG9ydCB7RGF0YVR5cGUsIE5hbWVkVGVuc29yTWFwLCBUZW5zb3IsIHRpZHksIHV0aWx9IGZyb20gJ0B0ZW5zb3JmbG93L3RmanMtY29yZSc7XG5cbmltcG9ydCB7SVNpZ25hdHVyZURlZn0gZnJvbSAnLi4vZGF0YS9jb21waWxlZF9hcGknO1xuaW1wb3J0IHtOYW1lZFRlbnNvcnNNYXAsIFRlbnNvckFycmF5TWFwLCBUZW5zb3JJbmZvLCBUZW5zb3JMaXN0TWFwfSBmcm9tICcuLi9kYXRhL3R5cGVzJztcbmltcG9ydCB7Z2V0Tm9kZU5hbWVBbmRJbmRleCwgZ2V0UGFyYW1WYWx1ZSwgZ2V0VGVuc29yLCBnZXRUZW5zb3JzRm9yQ3VycmVudENvbnRlbnh0LCBwYXJzZU5vZGVOYW1lfSBmcm9tICcuLi9vcGVyYXRpb25zL2V4ZWN1dG9ycy91dGlscyc7XG5pbXBvcnQge2V4ZWN1dGVPcH0gZnJvbSAnLi4vb3BlcmF0aW9ucy9vcGVyYXRpb25fZXhlY3V0b3InO1xuaW1wb3J0IHtHcmFwaCwgTm9kZX0gZnJvbSAnLi4vb3BlcmF0aW9ucy90eXBlcyc7XG5cbmltcG9ydCB7RXhlY3V0aW9uQ29udGV4dCwgRXhlY3V0aW9uQ29udGV4dEluZm99IGZyb20gJy4vZXhlY3V0aW9uX2NvbnRleHQnO1xuaW1wb3J0IHtnZXRFeGVjdXRpb25TdWJncmFwaCwgZ2V0Tm9kZXNJblRvcG9sb2dpY2FsT3JkZXIsIGlzQ29udHJvbEZsb3d9IGZyb20gJy4vbW9kZWxfYW5hbHlzaXMnO1xuaW1wb3J0IHtSZXNvdXJjZU1hbmFnZXJ9IGZyb20gJy4vcmVzb3VyY2VfbWFuYWdlcic7XG5pbXBvcnQge0Z1bmN0aW9uRXhlY3V0b3J9IGZyb20gJy4vdHlwZXMnO1xuXG5pbnRlcmZhY2UgTm9kZVdpdGhDb250ZXh0cyB7XG4gIGNvbnRleHRzOiBFeGVjdXRpb25Db250ZXh0SW5mb1tdO1xuICBub2RlOiBOb2RlO1xufVxuXG5leHBvcnQgY2xhc3MgR3JhcGhFeGVjdXRvciBpbXBsZW1lbnRzIEZ1bmN0aW9uRXhlY3V0b3Ige1xuICBwcml2YXRlIGNvbXBpbGVkTWFwOiBNYXA8c3RyaW5nLCBOb2RlW10+ID0gbmV3IE1hcCgpO1xuICBwcml2YXRlIF93ZWlnaHRNYXA6IE5hbWVkVGVuc29yc01hcCA9IHt9O1xuICBwcml2YXRlIF93ZWlnaHRJZHM6IG51bWJlcltdO1xuICBwcml2YXRlIF9zaWduYXR1cmU6IElTaWduYXR1cmVEZWY7XG4gIHByaXZhdGUgX2lucHV0czogTm9kZVtdO1xuICBwcml2YXRlIF9vdXRwdXRzOiBOb2RlW107XG4gIHByaXZhdGUgX2luaXROb2RlczogTm9kZVtdOyAgLy8gSW50ZXJuYWwgaW5pdCBub2RlcyB0byBzdGFydCBpbml0aWFsaXphdGlvbi5cbiAgcHJpdmF0ZSBTRVBFUkFUT1IgPSAnLCc7XG4gIHByaXZhdGUgX2Z1bmN0aW9uczoge1trZXk6IHN0cmluZ106IEdyYXBofSA9IHt9O1xuICBwcml2YXRlIF9mdW5jdGlvbkV4ZWN1dG9yTWFwOiB7W2tleTogc3RyaW5nXTogRnVuY3Rpb25FeGVjdXRvcn0gPSB7fTtcbiAgcHJpdmF0ZSBfcmVzb3VyY2VNYW5hZ2VyOiBSZXNvdXJjZU1hbmFnZXI7XG5cbiAgZ2V0IHdlaWdodElkcygpOiBudW1iZXJbXSB7XG4gICAgcmV0dXJuIHRoaXMucGFyZW50ID8gdGhpcy5wYXJlbnQud2VpZ2h0SWRzIDogdGhpcy5fd2VpZ2h0SWRzO1xuICB9XG5cbiAgZ2V0IGZ1bmN0aW9uRXhlY3V0b3JNYXAoKToge1trZXk6IHN0cmluZ106IEZ1bmN0aW9uRXhlY3V0b3J9IHtcbiAgICByZXR1cm4gdGhpcy5wYXJlbnQgPyB0aGlzLnBhcmVudC5mdW5jdGlvbkV4ZWN1dG9yTWFwIDpcbiAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9mdW5jdGlvbkV4ZWN1dG9yTWFwO1xuICB9XG5cbiAgZ2V0IHdlaWdodE1hcCgpOiBOYW1lZFRlbnNvcnNNYXAge1xuICAgIHJldHVybiB0aGlzLnBhcmVudCA/IHRoaXMucGFyZW50LndlaWdodE1hcCA6IHRoaXMuX3dlaWdodE1hcDtcbiAgfVxuXG4gIHNldCB3ZWlnaHRNYXAod2VpZ2h0TWFwOiBOYW1lZFRlbnNvcnNNYXApIHtcbiAgICBjb25zdCB3ZWlnaHRJZHMgPSBPYmplY3Qua2V5cyh3ZWlnaHRNYXApLm1hcChcbiAgICAgICAga2V5ID0+IHdlaWdodE1hcFtrZXldLm1hcCh0ZW5zb3IgPT4gdGVuc29yLmlkKSk7XG4gICAgdGhpcy5fd2VpZ2h0SWRzID0gW10uY29uY2F0KC4uLndlaWdodElkcyk7XG4gICAgdGhpcy5fd2VpZ2h0TWFwID0gd2VpZ2h0TWFwO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldCBgUmVzb3VyY2VNYW5hZ2VyYCBzaGFyZWQgYnkgZXhlY3V0b3JzIG9mIGEgbW9kZWwuXG4gICAqIEBwYXJhbSByZXNvdXJjZU1hbmFnZXI6IGBSZXNvdXJjZU1hbmFnZXJgIG9mIHRoZSBgR3JhcGhNb2RlbGAuXG4gICAqL1xuICBzZXQgcmVzb3VyY2VNYW5hZ2VyKHJlc291cmNlTWFuYWdlcjogUmVzb3VyY2VNYW5hZ2VyKSB7XG4gICAgdGhpcy5fcmVzb3VyY2VNYW5hZ2VyID0gcmVzb3VyY2VNYW5hZ2VyO1xuICB9XG5cbiAgZ2V0IGlucHV0cygpOiBUZW5zb3JJbmZvW10ge1xuICAgIHJldHVybiB0aGlzLl9pbnB1dHMubWFwKG5vZGUgPT4ge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgbmFtZTogbm9kZS5uYW1lLFxuICAgICAgICBzaGFwZTogbm9kZS5hdHRyUGFyYW1zWydzaGFwZSddID9cbiAgICAgICAgICAgIG5vZGUuYXR0clBhcmFtc1snc2hhcGUnXS52YWx1ZSBhcyBudW1iZXJbXSA6XG4gICAgICAgICAgICB1bmRlZmluZWQsXG4gICAgICAgIGR0eXBlOiBub2RlLmF0dHJQYXJhbXNbJ2R0eXBlJ10gP1xuICAgICAgICAgICAgbm9kZS5hdHRyUGFyYW1zWydkdHlwZSddLnZhbHVlIGFzIERhdGFUeXBlIDpcbiAgICAgICAgICAgIHVuZGVmaW5lZFxuICAgICAgfTtcbiAgICB9KTtcbiAgfVxuXG4gIGdldCBvdXRwdXRzKCk6IFRlbnNvckluZm9bXSB7XG4gICAgcmV0dXJuIHRoaXMuX291dHB1dHMubWFwKG5vZGUgPT4ge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgbmFtZTogbm9kZS5uYW1lLFxuICAgICAgICBzaGFwZTogbm9kZS5hdHRyUGFyYW1zWydzaGFwZSddID9cbiAgICAgICAgICAgIG5vZGUuYXR0clBhcmFtc1snc2hhcGUnXS52YWx1ZSBhcyBudW1iZXJbXSA6XG4gICAgICAgICAgICB1bmRlZmluZWQsXG4gICAgICAgIGR0eXBlOiBub2RlLmF0dHJQYXJhbXNbJ2R0eXBlJ10gP1xuICAgICAgICAgICAgbm9kZS5hdHRyUGFyYW1zWydkdHlwZSddLnZhbHVlIGFzIERhdGFUeXBlIDpcbiAgICAgICAgICAgIHVuZGVmaW5lZFxuICAgICAgfTtcbiAgICB9KTtcbiAgfVxuXG4gIGdldCBpbnB1dE5vZGVzKCk6IHN0cmluZ1tdIHtcbiAgICByZXR1cm4gdGhpcy5faW5wdXRzLm1hcChub2RlID0+IG5vZGUuc2lnbmF0dXJlS2V5IHx8IG5vZGUubmFtZSk7XG4gIH1cblxuICBnZXQgb3V0cHV0Tm9kZXMoKTogc3RyaW5nW10ge1xuICAgIHJldHVybiB0aGlzLl9vdXRwdXRzLm1hcCgobm9kZSkgPT4ge1xuICAgICAgY29uc3QgbmFtZSA9IG5vZGUuc2lnbmF0dXJlS2V5IHx8IG5vZGUubmFtZTtcbiAgICAgIHJldHVybiBub2RlLmRlZmF1bHRPdXRwdXQgPyAoYCR7bmFtZX06JHtub2RlLmRlZmF1bHRPdXRwdXR9YCkgOiBuYW1lO1xuICAgIH0pO1xuICB9XG5cbiAgZ2V0IGZ1bmN0aW9ucygpOiB7W2tleTogc3RyaW5nXTogSVNpZ25hdHVyZURlZn0ge1xuICAgIHJldHVybiBPYmplY3Qua2V5cyh0aGlzLl9mdW5jdGlvbnMpLnJlZHVjZSgobWFwLCBrZXkpID0+IHtcbiAgICAgIG1hcFtrZXldID0gdGhpcy5fZnVuY3Rpb25zW2tleV0uc2lnbmF0dXJlO1xuICAgICAgcmV0dXJuIG1hcDtcbiAgICB9LCB7fSBhcyB7W2tleTogc3RyaW5nXTogSVNpZ25hdHVyZURlZn0pO1xuICB9XG5cbiAgLyoqXG4gICAqXG4gICAqIEBwYXJhbSBncmFwaCBHcmFwaCB0aGUgbW9kZWwgb3IgZnVuY3Rpb24gZ3JhcGggdG8gYmUgZXhlY3V0ZWQuXG4gICAqIEBwYXJhbSBwYXJlbnQgV2hlbiBidWlsZGluZyBmdW5jdGlvbiBleGVjdG9yIHlvdSBuZWVkIHRvIHNldCB0aGUgcGFyZW50XG4gICAqIGV4ZWN1dG9yLiBTaW5jZSB0aGUgd2VpZ2h0cyBhbmQgZnVuY3Rpb24gZXhlY3V0b3IgbWFwcyBhcmUgc2V0IGF0IHBhcmFudFxuICAgKiBsZXZlbCwgdGhhdCBmdW5jdGlvbiBleGVjdXRvciBjYW4gYWNjZXNzIHRoZSBmdW5jdGlvbiBtYXBzIGFuZCB3ZWlnaHQgbWFwc1xuICAgKiB0aHJvdWdoIHRoZSBwYXJlbnQuXG4gICAqL1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSBwYXJlbnQ/OiBHcmFwaEV4ZWN1dG9yKSB7XG4gICAgdGhpcy5fb3V0cHV0cyA9IGdyYXBoLm91dHB1dHM7XG4gICAgdGhpcy5faW5wdXRzID0gZ3JhcGguaW5wdXRzO1xuICAgIHRoaXMuX2luaXROb2RlcyA9IGdyYXBoLmluaXROb2RlcztcbiAgICB0aGlzLl9zaWduYXR1cmUgPSBncmFwaC5zaWduYXR1cmU7XG4gICAgdGhpcy5fZnVuY3Rpb25zID0gZ3JhcGguZnVuY3Rpb25zO1xuICAgIC8vIGNyZWF0ZSBzdWItZ3JhcGggZXhlY3V0b3JzXG4gICAgaWYgKGdyYXBoLmZ1bmN0aW9ucyAhPSBudWxsKSB7XG4gICAgICBPYmplY3Qua2V5cyhncmFwaC5mdW5jdGlvbnMpLmZvckVhY2gobmFtZSA9PiB7XG4gICAgICAgIHRoaXMuX2Z1bmN0aW9uRXhlY3V0b3JNYXBbbmFtZV0gPVxuICAgICAgICAgICAgbmV3IEdyYXBoRXhlY3V0b3IoZ3JhcGguZnVuY3Rpb25zW25hbWVdLCB0aGlzKTtcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgZ2V0Q29tcGlsYXRpb25LZXkoaW5wdXRzOiBOb2RlW10sIG91dHB1dHM6IE5vZGVbXSk6IHN0cmluZyB7XG4gICAgY29uc3Qgc29ydGVkSW5wdXRzID0gaW5wdXRzLm1hcChub2RlID0+IG5vZGUubmFtZSkuc29ydCgpO1xuICAgIGNvbnN0IHNvcnRlZE91dHB1dHMgPSBvdXRwdXRzLm1hcChub2RlID0+IG5vZGUubmFtZSkuc29ydCgpO1xuICAgIHJldHVybiBzb3J0ZWRJbnB1dHMuam9pbih0aGlzLlNFUEVSQVRPUikgKyAnLS0nICtcbiAgICAgICAgc29ydGVkT3V0cHV0cy5qb2luKHRoaXMuU0VQRVJBVE9SKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21waWxlcyB0aGUgaW5mZXJlbmNlIGdyYXBoIGFuZCByZXR1cm5zIHRoZSBtaW5pbWFsIHNldCBvZiBub2RlcyB0aGF0IGFyZVxuICAgKiByZXF1aXJlZCBmb3IgZXhlY3V0aW9uLCBpbiB0aGUgY29ycmVjdCBleGVjdXRpb24gb3JkZXIuXG4gICAqL1xuICBwcml2YXRlIGNvbXBpbGUoaW5wdXRzOiBOYW1lZFRlbnNvck1hcCwgb3V0cHV0czogTm9kZVtdKTogTm9kZVtdIHtcbiAgICBjb25zdCBleGVjdXRpb25JbmZvID1cbiAgICAgICAgZ2V0RXhlY3V0aW9uU3ViZ3JhcGgoaW5wdXRzLCBvdXRwdXRzLCB0aGlzLndlaWdodE1hcCwgdGhpcy5faW5pdE5vZGVzKTtcbiAgICBjb25zdCB7bWlzc2luZ0lucHV0cywgZHluYW1pY05vZGUsIHN5bmNJbnB1dHN9ID0gZXhlY3V0aW9uSW5mbztcbiAgICBpZiAoZHluYW1pY05vZGUgIT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGBUaGlzIGV4ZWN1dGlvbiBjb250YWlucyB0aGUgbm9kZSAnJHtkeW5hbWljTm9kZS5uYW1lfScsIHdoaWNoIGhhcyBgICtcbiAgICAgICAgICBgdGhlIGR5bmFtaWMgb3AgJyR7ZHluYW1pY05vZGUub3B9Jy4gUGxlYXNlIHVzZSBgICtcbiAgICAgICAgICBgbW9kZWwuZXhlY3V0ZUFzeW5jKCkgaW5zdGVhZC4gQWx0ZXJuYXRpdmVseSwgdG8gYXZvaWQgdGhlIGAgK1xuICAgICAgICAgIGBkeW5hbWljIG9wcywgc3BlY2lmeSB0aGUgaW5wdXRzIFske3N5bmNJbnB1dHN9XWApO1xuICAgIH1cblxuICAgIGlmIChtaXNzaW5nSW5wdXRzLmxlbmd0aCA+IDApIHtcbiAgICAgIGNvbnN0IG91dE5hbWVzID0gb3V0cHV0cy5tYXAobiA9PiBuLm5hbWUpO1xuICAgICAgY29uc3QgaW5OYW1lcyA9IE9iamVjdC5rZXlzKGlucHV0cyk7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYENhbm5vdCBjb21wdXRlIHRoZSBvdXRwdXRzIFske291dE5hbWVzfV0gZnJvbSB0aGUgcHJvdmlkZWQgaW5wdXRzIGAgK1xuICAgICAgICAgIGBbJHtpbk5hbWVzfV0uIE1pc3NpbmcgdGhlIGZvbGxvd2luZyBpbnB1dHM6IFske21pc3NpbmdJbnB1dHN9XWApO1xuICAgIH1cblxuICAgIHJldHVybiBnZXROb2Rlc0luVG9wb2xvZ2ljYWxPcmRlcihcbiAgICAgICAgdGhpcy5ncmFwaCwgdGhpcy53ZWlnaHRNYXAsIGV4ZWN1dGlvbkluZm8pO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4ZWN1dGVzIHRoZSBpbmZlcmVuY2UgZm9yIGdpdmVuIGlucHV0IHRlbnNvcnMuXG4gICAqIEBwYXJhbSBpbnB1dHMgVGVuc29yIG1hcCBmb3IgdGhlIG1vZGVsIGlucHV0cywga2V5ZWQgYnkgdGhlIGlucHV0IG5vZGVcbiAgICogbmFtZXMuXG4gICAqIEBwYXJhbSBvdXRwdXRzIE9wdGlvbmFsLiBvdXRwdXQgbm9kZSBuYW1lIGZyb20gdGhlIFRlbnNvcmZsb3cgbW9kZWwsIGlmXG4gICAqIG5vIG91dHB1dHMgYXJlIHNwZWNpZmllZCwgdGhlIGRlZmF1bHQgb3V0cHV0cyBvZiB0aGUgbW9kZWwgd291bGQgYmUgdXNlZC5cbiAgICogWW91IGNhbiBpbnNwZWN0IGludGVybWVkaWF0ZSBub2RlcyBvZiB0aGUgbW9kZWwgYnkgYWRkaW5nIHRoZW0gdG8gdGhlXG4gICAqIG91dHB1dHMgYXJyYXkuXG4gICAqL1xuICBleGVjdXRlKGlucHV0czogTmFtZWRUZW5zb3JNYXAsIG91dHB1dHM/OiBzdHJpbmdbXSk6IFRlbnNvcltdIHtcbiAgICBpbnB1dHMgPSB0aGlzLm1hcElucHV0cyhpbnB1dHMpO1xuICAgIGNvbnN0IG5hbWVzID0gT2JqZWN0LmtleXMoaW5wdXRzKS5zb3J0KCk7XG4gICAgdGhpcy5jaGVja0lucHV0cyhpbnB1dHMpO1xuICAgIHRoaXMuY2hlY2tJbnB1dFNoYXBlQW5kVHlwZShpbnB1dHMpO1xuICAgIG91dHB1dHMgPSB0aGlzLm1hcE91dHB1dHMob3V0cHV0cyk7XG4gICAgdGhpcy5jaGVja091dHB1dHMob3V0cHV0cyk7XG4gICAgY29uc3QgaW5wdXROb2RlcyA9XG4gICAgICAgIG5hbWVzLm1hcChuYW1lID0+IHRoaXMuZ3JhcGgubm9kZXNbcGFyc2VOb2RlTmFtZShuYW1lKVswXV0pO1xuICAgIGNvbnN0IG91dHB1dE5vZGVOYW1lcyA9IG91dHB1dHMubWFwKG5hbWUgPT4gcGFyc2VOb2RlTmFtZShuYW1lKVswXSk7XG4gICAgbGV0IG91dHB1dE5vZGVzID0gb3V0cHV0Tm9kZU5hbWVzLm1hcChuYW1lID0+IHRoaXMuZ3JhcGgubm9kZXNbbmFtZV0pO1xuXG4gICAgLy8gSWYgbm8gb3V0cHV0cyBhcmUgc3BlY2lmaWVkLCB0aGVuIHVzZSB0aGUgZGVmYXVsdCBvdXRwdXRzIG9mIHRoZSBtb2RlbC5cbiAgICBpZiAob3V0cHV0Tm9kZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICBvdXRwdXROb2RlcyA9IHRoaXMuX291dHB1dHM7XG4gICAgfVxuXG4gICAgY29uc3QgY29tcGlsYXRpb25LZXkgPSB0aGlzLmdldENvbXBpbGF0aW9uS2V5KGlucHV0Tm9kZXMsIG91dHB1dE5vZGVzKTtcblxuICAgIC8vIERvIG5vdGhpbmcgaWYgdGhlIGNvbXBpbGVkIGdyYXBoIGNhY2hlIGNvbnRhaW5zIHRoZSBpbnB1dC5cbiAgICBsZXQgb3JkZXJlZE5vZGVzID0gdGhpcy5jb21waWxlZE1hcC5nZXQoY29tcGlsYXRpb25LZXkpO1xuICAgIGlmIChvcmRlcmVkTm9kZXMgPT0gbnVsbCkge1xuICAgICAgb3JkZXJlZE5vZGVzID0gdGhpcy5jb21waWxlKGlucHV0cywgb3V0cHV0Tm9kZXMpO1xuICAgICAgdGhpcy5jb21waWxlZE1hcC5zZXQoY29tcGlsYXRpb25LZXksIG9yZGVyZWROb2Rlcyk7XG4gICAgfVxuXG4gICAgY29uc3QgdGVuc29yQXJyYXlNYXA6IFRlbnNvckFycmF5TWFwID0ge307XG4gICAgY29uc3QgdGVuc29yTGlzdE1hcDogVGVuc29yTGlzdE1hcCA9IHt9O1xuXG4gICAgcmV0dXJuIHRpZHkoKCkgPT4ge1xuICAgICAgY29uc3QgY29udGV4dCA9IG5ldyBFeGVjdXRpb25Db250ZXh0KFxuICAgICAgICAgIHRoaXMud2VpZ2h0TWFwLCB0ZW5zb3JBcnJheU1hcCwgdGVuc29yTGlzdE1hcCxcbiAgICAgICAgICB0aGlzLmZ1bmN0aW9uRXhlY3V0b3JNYXApO1xuICAgICAgY29uc3QgdGVuc29yc01hcDogTmFtZWRUZW5zb3JzTWFwID0gey4uLnRoaXMud2VpZ2h0TWFwfTtcblxuICAgICAgT2JqZWN0LmtleXMoaW5wdXRzKS5mb3JFYWNoKG5hbWUgPT4ge1xuICAgICAgICBjb25zdCBbbm9kZU5hbWUsIGluZGV4XSA9IHBhcnNlTm9kZU5hbWUobmFtZSk7XG4gICAgICAgIGNvbnN0IHRlbnNvcnM6IFRlbnNvcltdID0gW107XG4gICAgICAgIHRlbnNvcnNbaW5kZXhdID0gaW5wdXRzW25hbWVdO1xuICAgICAgICB0ZW5zb3JzTWFwW25vZGVOYW1lXSA9IHRlbnNvcnM7XG4gICAgICB9KTtcblxuICAgICAgY29uc3QgdGVuc29yc1RvS2VlcCA9IHRoaXMuZ2V0RnJvemVuVGVuc29ySWRzKHRlbnNvcnNNYXApO1xuICAgICAgY29uc3QgaW50ZXJtZWRpYXRlVGVuc29yQ29uc3VtZXJDb3VudDoge1trZXk6IG51bWJlcl06IG51bWJlcn0gPSB7fTtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgb3JkZXJlZE5vZGVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNvbnN0IG5vZGUgPSBvcmRlcmVkTm9kZXNbaV07XG4gICAgICAgIGlmICghdGVuc29yc01hcFtub2RlLm5hbWVdKSB7XG4gICAgICAgICAgY29uc3QgdGVuc29ycyA9XG4gICAgICAgICAgICAgIGV4ZWN1dGVPcChub2RlLCB0ZW5zb3JzTWFwLCBjb250ZXh0LCB0aGlzLl9yZXNvdXJjZU1hbmFnZXIpIGFzXG4gICAgICAgICAgICAgIFRlbnNvcltdO1xuICAgICAgICAgIGlmICh1dGlsLmlzUHJvbWlzZSh0ZW5zb3JzKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgICAgIGBUaGUgZXhlY3V0aW9uIG9mIHRoZSBvcCAnJHtub2RlLm9wfScgcmV0dXJuZWQgYSBwcm9taXNlLiBgICtcbiAgICAgICAgICAgICAgICBgUGxlYXNlIHVzZSBtb2RlbC5leGVjdXRlQXN5bmMoKSBpbnN0ZWFkLmApO1xuICAgICAgICAgIH1cbiAgICAgICAgICB0ZW5zb3JzTWFwW25vZGUubmFtZV0gPSB0ZW5zb3JzO1xuICAgICAgICAgIHRoaXMuY2hlY2tUZW5zb3JGb3JEaXNwb3NhbChcbiAgICAgICAgICAgICAgbm9kZS5uYW1lLCBub2RlLCB0ZW5zb3JzTWFwLCBjb250ZXh0LCB0ZW5zb3JzVG9LZWVwLFxuICAgICAgICAgICAgICBvdXRwdXROb2RlTmFtZXMsIGludGVybWVkaWF0ZVRlbnNvckNvbnN1bWVyQ291bnQpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICAvLyBkaXNwb3NlIHRoZSBjb250ZXh0IGZvciB0aGUgcm9vdCBleGVjdXRvclxuICAgICAgaWYgKHRoaXMucGFyZW50ID09IG51bGwpIHtcbiAgICAgICAgY29udGV4dC5kaXNwb3NlKHRlbnNvcnNUb0tlZXApO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG91dHB1dHMubWFwKG5hbWUgPT4gZ2V0VGVuc29yKG5hbWUsIHRlbnNvcnNNYXAsIGNvbnRleHQpKTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0RnJvemVuVGVuc29ySWRzKHRlbnNvck1hcDogTmFtZWRUZW5zb3JzTWFwKTogU2V0PG51bWJlcj4ge1xuICAgIGNvbnN0IGlkcyA9IFtdLmNvbmNhdC5hcHBseShcbiAgICAgICAgW10sXG4gICAgICAgIE9iamVjdC5rZXlzKHRlbnNvck1hcClcbiAgICAgICAgICAgIC5tYXAoa2V5ID0+IHRlbnNvck1hcFtrZXldKVxuICAgICAgICAgICAgLm1hcCh0ZW5zb3JzID0+IHRlbnNvcnMubWFwKHRlbnNvciA9PiB0ZW5zb3IuaWQpKSk7XG4gICAgcmV0dXJuIG5ldyBTZXQoaWRzKTtcbiAgfVxuICBwcml2YXRlIGNoZWNrVGVuc29yRm9yRGlzcG9zYWwoXG4gICAgICBub2RlTmFtZTogc3RyaW5nLCBub2RlOiBOb2RlLCB0ZW5zb3JNYXA6IE5hbWVkVGVuc29yc01hcCxcbiAgICAgIGNvbnRleHQ6IEV4ZWN1dGlvbkNvbnRleHQsIHRlbnNvcnNUb0tlZXA6IFNldDxudW1iZXI+LFxuICAgICAgb3V0cHV0TmFtZXM6IHN0cmluZ1tdLFxuICAgICAgaW50ZXJtZWRpYXRlVGVuc29yQ29uc3VtZXJDb3VudDoge1trZXk6IHN0cmluZ106IG51bWJlcn0pIHtcbiAgICAvLyBTa2lwIG91dHB1dCBub2RlcyBhbmQgYW55IGNvbnRyb2wgZmxvdyBub2Rlcywgc2luY2UgaXRzIGRlcGVuZGVuY3kgaXNcbiAgICAvLyB0cmlja3kgdG8gdHJhY2sgY29ycmVjdGx5LlxuICAgIGlmIChub2RlLmNhdGVnb3J5ID09PSAnY29udHJvbCcgfHwgb3V0cHV0TmFtZXMuaW5kZXhPZihub2RlTmFtZSkgIT09IC0xKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGVuc29yTWFwW25vZGVOYW1lXS5mb3JFYWNoKHRlbnNvciA9PiB7XG4gICAgICBpZiAodGVuc29yICE9IG51bGwpIHtcbiAgICAgICAgaW50ZXJtZWRpYXRlVGVuc29yQ29uc3VtZXJDb3VudFt0ZW5zb3IuaWRdID1cbiAgICAgICAgICAgIChpbnRlcm1lZGlhdGVUZW5zb3JDb25zdW1lckNvdW50W3RlbnNvci5pZF0gfHwgMCkgK1xuICAgICAgICAgICAgbm9kZS5jaGlsZHJlbi5sZW5ndGg7XG4gICAgICB9XG4gICAgfSk7XG4gICAgbm9kZS5pbnB1dHMuZm9yRWFjaChpbnB1dCA9PiB7XG4gICAgICAvLyBTa2lwIGFueSBjb250cm9sIGZsb3cgbm9kZXMsIHNpbmNlIGl0cyBkZXBlbmRlbmN5IGlzIHRyaWNreSB0byB0cmFja1xuICAgICAgLy8gY29ycmVjdGx5LlxuICAgICAgaWYgKGlucHV0LmNhdGVnb3J5ICE9PSAnY29udHJvbCcpIHtcbiAgICAgICAgY29uc3QgdGVuc29ycyA9XG4gICAgICAgICAgICBnZXRUZW5zb3JzRm9yQ3VycmVudENvbnRlbnh0KGlucHV0Lm5hbWUsIHRlbnNvck1hcCwgY29udGV4dCk7XG4gICAgICAgIGlmICh0ZW5zb3JzICE9IG51bGwpIHtcbiAgICAgICAgICB0ZW5zb3JzLmZvckVhY2godGVuc29yID0+IHtcbiAgICAgICAgICAgIGlmICh0ZW5zb3IgJiYgIXRlbnNvci5rZXB0ICYmICF0ZW5zb3JzVG9LZWVwLmhhcyh0ZW5zb3IuaWQpKSB7XG4gICAgICAgICAgICAgIGNvbnN0IGNvdW50ID0gaW50ZXJtZWRpYXRlVGVuc29yQ29uc3VtZXJDb3VudFt0ZW5zb3IuaWRdO1xuICAgICAgICAgICAgICBpZiAoY291bnQgPT09IDEpIHtcbiAgICAgICAgICAgICAgICB0ZW5zb3IuZGlzcG9zZSgpO1xuICAgICAgICAgICAgICAgIGRlbGV0ZSBpbnRlcm1lZGlhdGVUZW5zb3JDb25zdW1lckNvdW50W3RlbnNvci5pZF07XG4gICAgICAgICAgICAgIH0gZWxzZSBpZiAoY291bnQgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIC8vIG9ubHkgaW50ZXJtZWRpYXRlIG5vZGVzIGhhcyBjb3VudCBzZXQsIGlucHV0cyBhbmQgd2VpZ2h0cyBhcmVcbiAgICAgICAgICAgICAgICAvLyBub3QuXG4gICAgICAgICAgICAgICAgaW50ZXJtZWRpYXRlVGVuc29yQ29uc3VtZXJDb3VudFt0ZW5zb3IuaWRdLS07XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4ZWN1dGVzIHRoZSBpbmZlcmVuY2UgZm9yIGdpdmVuIGlucHV0IHRlbnNvcnMgaW4gQXN5bmMgZmFzaGlvbi5cbiAgICogQHBhcmFtIGlucHV0cyBUZW5zb3IgbWFwIGZvciB0aGUgbW9kZWwgaW5wdXRzLCBrZXllZCBieSB0aGUgaW5wdXQgbm9kZVxuICAgKiBuYW1lcy5cbiAgICogQHBhcmFtIG91dHB1dHMgb3V0cHV0IG5vZGUgbmFtZSBmcm9tIHRoZSBUZW5zb3JmbG93IG1vZGVsLCBpZiBubyBvdXRwdXRzXG4gICAqIGFyZSBzcGVjaWZpZWQsIHRoZSBkZWZhdWx0IG91dHB1dHMgb2YgdGhlIG1vZGVsIHdvdWxkIGJlIHVzZWQuIFlvdSBjYW5cbiAgICogaW5zcGVjdCBpbnRlcm1lZGlhdGUgbm9kZXMgb2YgdGhlIG1vZGVsIGJ5IGFkZGluZyB0aGVtIHRvIHRoZSBvdXRwdXRzXG4gICAqIGFycmF5LlxuICAgKi9cbiAgYXN5bmMgZXhlY3V0ZUFzeW5jKGlucHV0czogTmFtZWRUZW5zb3JNYXAsIG91dHB1dHM/OiBzdHJpbmdbXSk6XG4gICAgICBQcm9taXNlPFRlbnNvcltdPiB7XG4gICAgcmV0dXJuIHRoaXMuX2V4ZWN1dGVBc3luYyhpbnB1dHMsIG91dHB1dHMpO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4ZWN1dGVzIHRoZSBpbmZlcmVuY2UgZm9yIGdpdmVuIGlucHV0IHRlbnNvcnMgaW4gQXN5bmMgZmFzaGlvbi5cbiAgICogQHBhcmFtIGlucHV0cyBUZW5zb3IgbWFwIGZvciB0aGUgbW9kZWwgaW5wdXRzLCBrZXllZCBieSB0aGUgaW5wdXQgbm9kZVxuICAgKiBuYW1lcy5cbiAgICogQHBhcmFtIG91dHB1dHMgT3B0aW9uYWwuIG91dHB1dCBub2RlIG5hbWUgZnJvbSB0aGUgVGVuc29yZmxvdyBtb2RlbCxcbiAgICogaWYgbm8gb3V0cHV0cyBhcmUgc3BlY2lmaWVkLCB0aGUgZGVmYXVsdCBvdXRwdXRzIG9mIHRoZSBtb2RlbCB3b3VsZCBiZVxuICAgKiB1c2VkLiBZb3UgY2FuIGluc3BlY3QgaW50ZXJtZWRpYXRlIG5vZGVzIG9mIHRoZSBtb2RlbCBieSBhZGRpbmcgdGhlbSB0byB0aGVcbiAgICogb3V0cHV0cyBhcnJheS5cbiAgICogQHBhcmFtIGlzRnVuY3Rpb25FeGVjdXRpb24gT3B0aW9uYWwuIEZsYWcgZm9yIGV4ZWN1dGluZyBhIGZ1bmN0aW9uLlxuICAgKiBAcGFyYW0gdGVuc29yQXJyYXlNYXAgT3B0aW9uYWwsIGdsb2JhbCBUZW5zb3JBcnJheSBtYXAgYnkgaWQuIFVzZWQgZm9yXG4gICAqIGZ1bmN0aW9uIGV4ZWN1dGlvbi5cbiAgICogQHBhcmFtIHRlbnNvckFycmF5TWFwIE9wdGluYWwgZ2xvYmFsIFRlbnNvckxpc3QgbWFwIGJ5IGlkLiBVc2VkIGZvclxuICAgKiBmdW5jdGlvbiBleGVjdXRpb24uXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIF9leGVjdXRlQXN5bmMoXG4gICAgICBpbnB1dHM6IE5hbWVkVGVuc29yTWFwLCBvdXRwdXRzPzogc3RyaW5nW10sIGlzRnVuY3Rpb25FeGVjdXRpb24gPSBmYWxzZSxcbiAgICAgIHRlbnNvckFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCA9IHt9LFxuICAgICAgdGVuc29yTGlzdE1hcDogVGVuc29yTGlzdE1hcCA9IHt9KTogUHJvbWlzZTxUZW5zb3JbXT4ge1xuICAgIGlmICghaXNGdW5jdGlvbkV4ZWN1dGlvbikge1xuICAgICAgaW5wdXRzID0gdGhpcy5tYXBJbnB1dHMoaW5wdXRzKTtcbiAgICAgIHRoaXMuY2hlY2tJbnB1dHMoaW5wdXRzKTtcbiAgICAgIHRoaXMuY2hlY2tJbnB1dFNoYXBlQW5kVHlwZShpbnB1dHMpO1xuICAgICAgb3V0cHV0cyA9IHRoaXMubWFwT3V0cHV0cyhvdXRwdXRzKTtcbiAgICAgIHRoaXMuY2hlY2tPdXRwdXRzKG91dHB1dHMpO1xuICAgIH1cblxuICAgIGNvbnN0IGNvbnRleHQgPSBuZXcgRXhlY3V0aW9uQ29udGV4dChcbiAgICAgICAgdGhpcy53ZWlnaHRNYXAsIHRlbnNvckFycmF5TWFwLCB0ZW5zb3JMaXN0TWFwLFxuICAgICAgICB0aGlzLmZ1bmN0aW9uRXhlY3V0b3JNYXApO1xuXG4gICAgLy8gR3JhcGggd2l0aCBjb250cm9sIGZsb3cgb3AgcmVxdWlyZXMgcnVudGltZSBldmFsdWF0aW9uIG9mIHRoZSBleGVjdXRpb25cbiAgICAvLyBvcmRlciwgd2hpbGUgd2l0aG91dCBjb250cm9sIGZsb3cgdGhlIGV4ZWN1dGlvbiBvcmRlciBpcyBwcmUtZGV0ZXJtaW5lZFxuICAgIC8vIGluIHRoZSBjb21waWxlIG1ldGhvZC5cbiAgICBjb25zdCB0ZW5zb3JNYXAgPSBhd2FpdCB0aGlzLmV4ZWN1dGVXaXRoQ29udHJvbEZsb3coXG4gICAgICAgIGlucHV0cywgY29udGV4dCwgb3V0cHV0cywgaXNGdW5jdGlvbkV4ZWN1dGlvbik7XG4gICAgY29uc3QgcmVzdWx0cyA9IG91dHB1dHMubWFwKG5hbWUgPT4gZ2V0VGVuc29yKG5hbWUsIHRlbnNvck1hcCwgY29udGV4dCkpO1xuXG4gICAgLy8gZGlzcG9zZSBhbGwgdGhlIGludGVybWVkaWF0ZSB0ZW5zb3JzXG4gICAgY29uc3Qgb3V0cHV0SWRzID0gcmVzdWx0cy5tYXAodCA9PiB0LmlkKTtcbiAgICBjb25zdCBpbnB1dElkcyA9IE9iamVjdC5rZXlzKGlucHV0cykubWFwKG5hbWUgPT4gaW5wdXRzW25hbWVdLmlkKTtcbiAgICBjb25zdCBrZWVwSWRzID1cbiAgICAgICAgbmV3IFNldDxudW1iZXI+KFsuLi5vdXRwdXRJZHMsIC4uLmlucHV0SWRzLCAuLi50aGlzLndlaWdodElkc10pO1xuICAgIE9iamVjdC5rZXlzKHRlbnNvck1hcCkuZm9yRWFjaChrZXkgPT4ge1xuICAgICAgY29uc3QgdGVuc29yQXJyYXkgPSB0ZW5zb3JNYXBba2V5XTtcbiAgICAgIHRlbnNvckFycmF5LmZvckVhY2godGVuc29yID0+IHtcbiAgICAgICAgaWYgKHRlbnNvciAmJiAhdGVuc29yLmtlcHQgJiYgIXRlbnNvci5pc0Rpc3Bvc2VkICYmXG4gICAgICAgICAgICAha2VlcElkcy5oYXModGVuc29yLmlkKSkge1xuICAgICAgICAgIHRlbnNvci5kaXNwb3NlKCk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH0pO1xuICAgIC8vIGRpc3Bvc2UgdGhlIGNvbnRleHQgZm9yIHRoZSByb290IGV4ZWN1dG9yXG4gICAgaWYgKHRoaXMucGFyZW50ID09IG51bGwpIHtcbiAgICAgIGNvbnRleHQuZGlzcG9zZShrZWVwSWRzKTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0cztcbiAgfVxuXG4gIGFzeW5jIGV4ZWN1dGVGdW5jdGlvbkFzeW5jKFxuICAgICAgaW5wdXRzOiBUZW5zb3JbXSwgdGVuc29yQXJyYXlNYXA6IFRlbnNvckFycmF5TWFwLFxuICAgICAgdGVuc29yTGlzdE1hcDogVGVuc29yTGlzdE1hcCk6IFByb21pc2U8VGVuc29yW10+IHtcbiAgICBjb25zdCBtYXBwZWRJbnB1dHMgPSBpbnB1dHMucmVkdWNlKChtYXAsIHRlbnNvciwgaW5kZXgpID0+IHtcbiAgICAgIG1hcFt0aGlzLmlucHV0c1tpbmRleF0ubmFtZV0gPSB0ZW5zb3I7XG4gICAgICByZXR1cm4gbWFwO1xuICAgIH0sIHt9IGFzIE5hbWVkVGVuc29yTWFwKTtcblxuICAgIHJldHVybiB0aGlzLl9leGVjdXRlQXN5bmMoXG4gICAgICAgIG1hcHBlZElucHV0cywgdGhpcy5vdXRwdXROb2RlcywgdHJ1ZSwgdGVuc29yQXJyYXlNYXAsIHRlbnNvckxpc3RNYXApO1xuICB9XG4gIC8qKlxuICAgKiBXaGVuIHRoZXJlIGFyZSBjb250cm9sIGZsb3cgbm9kZXMgaW4gdGhlIGdyYXBoLCB0aGUgZ3JhcGggZXhlY3V0aW9uIHVzZVxuICAgKiBFeGVjdXRpb25Db250ZXh0IHRvIGtlZXAgdHJhY2sgb2YgdGhlIGZyYW1lcyBhbmQgbG9vcCBpdGVyYXRvcnMuXG4gICAqIEBwYXJhbSBpbnB1dHMgcGxhY2Vob2xkZXIgdGVuc29ycyBmb3IgdGhlIGdyYXBoLlxuICAgKiBAcGFyYW0gY29udGV4dCB0aGUgZXhlY3V0aW9uIGNvbnRleHQgb2JqZWN0IGZvciBjdXJyZW50IGV4ZWN1dGlvbi5cbiAgICogQHBhcmFtIG91dHB1dE5hbWVzIE9wdGlvbmFsLiBvdXRwdXQgbm9kZSBuYW1lIGZyb20gdGhlIFRlbnNvcmZsb3cgbW9kZWwsXG4gICAqIGlmIG5vIG91dHB1dHMgYXJlIHNwZWNpZmllZCwgdGhlIGRlZmF1bHQgb3V0cHV0cyBvZiB0aGUgbW9kZWwgd291bGQgYmVcbiAgICogdXNlZC4gWW91IGNhbiBpbnNwZWN0IGludGVybWVkaWF0ZSBub2RlcyBvZiB0aGUgbW9kZWwgYnkgYWRkaW5nIHRoZW0gdG8gdGhlXG4gICAqIG91dHB1dHMgYXJyYXkuXG4gICAqIEBwYXJhbSBpc0Z1bmN0aW9uRXhlY3V0aW9uIEZsYWcgZm9yIGV4ZWN1dGluZyBhIGZ1bmN0aW9uLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBleGVjdXRlV2l0aENvbnRyb2xGbG93KFxuICAgICAgaW5wdXRzOiBOYW1lZFRlbnNvck1hcCwgY29udGV4dDogRXhlY3V0aW9uQ29udGV4dCwgb3V0cHV0TmFtZXM/OiBzdHJpbmdbXSxcbiAgICAgIGlzRnVuY3Rpb25FeGVjdXRpb24/OiBib29sZWFuKTogUHJvbWlzZTxOYW1lZFRlbnNvcnNNYXA+IHtcbiAgICBjb25zdCBuYW1lcyA9IE9iamVjdC5rZXlzKGlucHV0cyk7XG4gICAgY29uc3QgaW5wdXROb2RlcyA9XG4gICAgICAgIG5hbWVzLm1hcChuYW1lID0+IHRoaXMuZ3JhcGgubm9kZXNbcGFyc2VOb2RlTmFtZShuYW1lKVswXV0pO1xuICAgIGNvbnN0IG91dHB1dE5vZGVOYW1lcyA9IG91dHB1dE5hbWVzLm1hcChuYW1lID0+IHBhcnNlTm9kZU5hbWUobmFtZSlbMF0pO1xuICAgIGxldCBvdXRwdXROb2RlcyA9IG91dHB1dE5vZGVOYW1lcy5tYXAobmFtZSA9PiB0aGlzLmdyYXBoLm5vZGVzW25hbWVdKTtcblxuICAgIC8vIElmIG5vIG91dHB1dHMgYXJlIHNwZWNpZmllZCwgdGhlbiB1c2UgdGhlIGRlZmF1bHQgb3V0cHV0cyBvZiB0aGUgbW9kZWwuXG4gICAgaWYgKG91dHB1dE5vZGVzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgb3V0cHV0Tm9kZXMgPSB0aGlzLl9vdXRwdXRzO1xuICAgIH1cblxuICAgIGNvbnN0IHt1c2VkTm9kZXMsIG1pc3NpbmdJbnB1dHMsIGR5bmFtaWNOb2RlLCBzeW5jSW5wdXRzfSA9XG4gICAgICAgIGdldEV4ZWN1dGlvblN1YmdyYXBoKFxuICAgICAgICAgICAgaW5wdXRzLCBvdXRwdXROb2RlcywgdGhpcy53ZWlnaHRNYXAsIHRoaXMuX2luaXROb2Rlcyk7XG5cbiAgICAvLyBGaXJzdCBub2RlcyB0byBleGVjdXRlIGluY2x1ZGUgaW5wdXROb2Rlcywgd2VpZ2h0cywgYW5kIGluaXROb2Rlcy5cbiAgICBjb25zdCBzdGFjazogTm9kZVdpdGhDb250ZXh0c1tdID0gW1xuICAgICAgLi4uaW5wdXROb2RlcywgLi4udGhpcy5ncmFwaC53ZWlnaHRzLCAuLi4odGhpcy5faW5pdE5vZGVzIHx8IFtdKVxuICAgIF0ubWFwKG5vZGUgPT4ge1xuICAgICAgcmV0dXJuIHtub2RlLCBjb250ZXh0czogY29udGV4dC5jdXJyZW50Q29udGV4dH07XG4gICAgfSk7XG4gICAgY29uc3QgdGVuc29yc01hcDogTmFtZWRUZW5zb3JzTWFwID0gey4uLnRoaXMud2VpZ2h0TWFwfTtcbiAgICBPYmplY3Qua2V5cyhpbnB1dHMpLmZvckVhY2gobmFtZSA9PiB7XG4gICAgICBjb25zdCBbbm9kZU5hbWUsIGluZGV4XSA9IHBhcnNlTm9kZU5hbWUobmFtZSk7XG4gICAgICBjb25zdCB0ZW5zb3JzOiBUZW5zb3JbXSA9IFtdO1xuICAgICAgdGVuc29yc1tpbmRleF0gPSBpbnB1dHNbbmFtZV07XG4gICAgICB0ZW5zb3JzTWFwW25vZGVOYW1lXSA9IHRlbnNvcnM7XG4gICAgfSk7XG4gICAgY29uc3QgaW50ZXJtZWRpYXRlVGVuc29yQ29uc3VtZXJDb3VudDoge1trZXk6IG51bWJlcl06IG51bWJlcn0gPSB7fTtcbiAgICBjb25zdCB0ZW5zb3JzVG9LZWVwID0gdGhpcy5nZXRGcm96ZW5UZW5zb3JJZHModGVuc29yc01hcCk7XG4gICAgY29uc3QgYWRkZWQ6IHtba2V5OiBzdHJpbmddOiBib29sZWFufSA9IHt9O1xuICAgIHdoaWxlIChzdGFjay5sZW5ndGggPiAwKSB7XG4gICAgICBjb25zdCBwcm9taXNlcyA9IHRoaXMucHJvY2Vzc1N0YWNrKFxuICAgICAgICAgIGlucHV0Tm9kZXMsIHN0YWNrLCBjb250ZXh0LCB0ZW5zb3JzTWFwLCBhZGRlZCwgdGVuc29yc1RvS2VlcCxcbiAgICAgICAgICBvdXRwdXROb2RlTmFtZXMsIGludGVybWVkaWF0ZVRlbnNvckNvbnN1bWVyQ291bnQsIHVzZWROb2Rlcyk7XG4gICAgICBhd2FpdCBQcm9taXNlLmFsbChwcm9taXNlcyk7XG4gICAgfVxuICAgIGlmIChkeW5hbWljTm9kZSA9PSBudWxsICYmICFpc0Z1bmN0aW9uRXhlY3V0aW9uKSB7XG4gICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgYFRoaXMgbW9kZWwgZXhlY3V0aW9uIGRpZCBub3QgY29udGFpbiBhbnkgbm9kZXMgd2l0aCBjb250cm9sIGZsb3cgYCArXG4gICAgICAgICAgYG9yIGR5bmFtaWMgb3V0cHV0IHNoYXBlcy4gWW91IGNhbiB1c2UgbW9kZWwuZXhlY3V0ZSgpIGluc3RlYWQuYCk7XG4gICAgfVxuICAgIGNvbnN0IG1pc3NpbmdPdXRwdXRzID1cbiAgICAgICAgb3V0cHV0Tm9kZXNcbiAgICAgICAgICAgIC5maWx0ZXIoXG4gICAgICAgICAgICAgICAgbm9kZSA9PiAhaXNDb250cm9sRmxvdyhub2RlKSAmJlxuICAgICAgICAgICAgICAgICAgICAhZ2V0VGVuc29yKG5vZGUubmFtZSwgdGVuc29yc01hcCwgY29udGV4dCkpXG4gICAgICAgICAgICAubWFwKG5vZGUgPT4gbm9kZS5uYW1lKTtcbiAgICBpZiAobWlzc2luZ091dHB1dHMubGVuZ3RoID4gMCkge1xuICAgICAgbGV0IGFsdGVybmF0aXZlTXNnID0gJyc7XG4gICAgICBpZiAoZHluYW1pY05vZGUgIT0gbnVsbCkge1xuICAgICAgICBhbHRlcm5hdGl2ZU1zZyA9XG4gICAgICAgICAgICBgQWx0ZXJuYXRpdmVseSwgdG8gYXZvaWQgdGhlIGR5bmFtaWMgb3BzLCB1c2UgbW9kZWwuZXhlY3V0ZSgpIGAgK1xuICAgICAgICAgICAgYGFuZCBzcGVjaWZ5IHRoZSBpbnB1dHMgWyR7c3luY0lucHV0c31dYDtcbiAgICAgIH1cbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICBgQ2Fubm90IGNvbXB1dGUgdGhlIG91dHB1dHMgWyR7bWlzc2luZ091dHB1dHN9XSBmcm9tIHRoZSBwcm92aWRlZCBgICtcbiAgICAgICAgICBgaW5wdXRzIFske25hbWVzfV0uIENvbnNpZGVyIHByb3ZpZGluZyB0aGUgZm9sbG93aW5nIGlucHV0czogYCArXG4gICAgICAgICAgYFske21pc3NpbmdJbnB1dHN9XS4gJHthbHRlcm5hdGl2ZU1zZ31gKTtcbiAgICB9XG4gICAgcmV0dXJuIHRlbnNvcnNNYXA7XG4gIH1cblxuICBwcml2YXRlIHByb2Nlc3NTdGFjayhcbiAgICAgIGlucHV0Tm9kZXM6IE5vZGVbXSwgc3RhY2s6IE5vZGVXaXRoQ29udGV4dHNbXSwgY29udGV4dDogRXhlY3V0aW9uQ29udGV4dCxcbiAgICAgIHRlbnNvck1hcDogTmFtZWRUZW5zb3JzTWFwLCBhZGRlZDoge1trZXk6IHN0cmluZ106IGJvb2xlYW59LFxuICAgICAgdGVuc29yc1RvS2VlcDogU2V0PG51bWJlcj4sIG91dHB1dE5hbWVzOiBzdHJpbmdbXSxcbiAgICAgIGludGVybWVkaWF0ZVRlbnNvckNvbnN1bWVyQ291bnQ6IHtba2V5OiBudW1iZXJdOiBudW1iZXJ9LFxuICAgICAgdXNlZE5vZGVzOiBTZXQ8c3RyaW5nPikge1xuICAgIGNvbnN0IHByb21pc2VzOiBBcnJheTxQcm9taXNlPFRlbnNvcltdPj4gPSBbXTtcbiAgICB3aGlsZSAoc3RhY2subGVuZ3RoID4gMCkge1xuICAgICAgY29uc3QgaXRlbSA9IHN0YWNrLnBvcCgpO1xuICAgICAgY29udGV4dC5jdXJyZW50Q29udGV4dCA9IGl0ZW0uY29udGV4dHM7XG4gICAgICBsZXQgbm9kZU5hbWUgPSAnJztcbiAgICAgIC8vIFRoZSB0ZW5zb3Igb2YgdGhlIEVudGVyIG9wIHdpdGggaXNDb25zdGFudCBzZXQgc2hvdWxkIGJlIHNldFxuICAgICAgLy8gaW4gdGhlIHBhcmVudCBzY29wZSwgc28gaXQgd2lsbCBiZSBhdmFpbGFibGUgYXMgY29uc3RhbnQgZm9yIHRoZVxuICAgICAgLy8gd2hvbGUgbG9vcC5cbiAgICAgIGlmIChpdGVtLm5vZGUub3AgPT09ICdFbnRlcicgJiZcbiAgICAgICAgICBnZXRQYXJhbVZhbHVlKCdpc0NvbnN0YW50JywgaXRlbS5ub2RlLCB0ZW5zb3JNYXAsIGNvbnRleHQpKSB7XG4gICAgICAgIFtub2RlTmFtZV0gPSBnZXROb2RlTmFtZUFuZEluZGV4KGl0ZW0ubm9kZS5uYW1lLCBjb250ZXh0KTtcbiAgICAgIH1cblxuICAgICAgLy8gb25seSBwcm9jZXNzIG5vZGVzIHRoYXQgYXJlIG5vdCBpbiB0aGUgdGVuc29yTWFwIHlldCwgdGhpcyBpbmNsdWRlXG4gICAgICAvLyBpbnB1dE5vZGVzIGFuZCBpbnRlcm5hbCBpbml0Tm9kZXMuXG4gICAgICBpZiAodGVuc29yTWFwW2l0ZW0ubm9kZS5uYW1lXSA9PSBudWxsKSB7XG4gICAgICAgIGNvbnN0IHRlbnNvcnMgPVxuICAgICAgICAgICAgZXhlY3V0ZU9wKGl0ZW0ubm9kZSwgdGVuc29yTWFwLCBjb250ZXh0LCB0aGlzLl9yZXNvdXJjZU1hbmFnZXIpO1xuICAgICAgICBpZiAoIW5vZGVOYW1lKSB7XG4gICAgICAgICAgW25vZGVOYW1lXSA9IGdldE5vZGVOYW1lQW5kSW5kZXgoaXRlbS5ub2RlLm5hbWUsIGNvbnRleHQpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGN1cnJlbnRDb250ZXh0ID0gY29udGV4dC5jdXJyZW50Q29udGV4dDtcbiAgICAgICAgaWYgKHV0aWwuaXNQcm9taXNlKHRlbnNvcnMpKSB7XG4gICAgICAgICAgcHJvbWlzZXMucHVzaCh0ZW5zb3JzLnRoZW4odCA9PiB7XG4gICAgICAgICAgICB0ZW5zb3JNYXBbbm9kZU5hbWVdID0gdDtcbiAgICAgICAgICAgIGNvbnRleHQuY3VycmVudENvbnRleHQgPSBjdXJyZW50Q29udGV4dDtcbiAgICAgICAgICAgIHRoaXMuY2hlY2tUZW5zb3JGb3JEaXNwb3NhbChcbiAgICAgICAgICAgICAgICBub2RlTmFtZSwgaXRlbS5ub2RlLCB0ZW5zb3JNYXAsIGNvbnRleHQsIHRlbnNvcnNUb0tlZXAsXG4gICAgICAgICAgICAgICAgb3V0cHV0TmFtZXMsIGludGVybWVkaWF0ZVRlbnNvckNvbnN1bWVyQ291bnQpO1xuICAgICAgICAgICAgdGhpcy5wcm9jZXNzQ2hpbGROb2RlcyhcbiAgICAgICAgICAgICAgICBpdGVtLm5vZGUsIHN0YWNrLCBjb250ZXh0LCB0ZW5zb3JNYXAsIGFkZGVkLCB1c2VkTm9kZXMpO1xuICAgICAgICAgICAgcmV0dXJuIHQ7XG4gICAgICAgICAgfSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRlbnNvck1hcFtub2RlTmFtZV0gPSB0ZW5zb3JzO1xuICAgICAgICAgIHRoaXMuY2hlY2tUZW5zb3JGb3JEaXNwb3NhbChcbiAgICAgICAgICAgICAgbm9kZU5hbWUsIGl0ZW0ubm9kZSwgdGVuc29yTWFwLCBjb250ZXh0LCB0ZW5zb3JzVG9LZWVwLFxuICAgICAgICAgICAgICBvdXRwdXROYW1lcywgaW50ZXJtZWRpYXRlVGVuc29yQ29uc3VtZXJDb3VudCk7XG4gICAgICAgICAgdGhpcy5wcm9jZXNzQ2hpbGROb2RlcyhcbiAgICAgICAgICAgICAgaXRlbS5ub2RlLCBzdGFjaywgY29udGV4dCwgdGVuc29yTWFwLCBhZGRlZCwgdXNlZE5vZGVzKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5wcm9jZXNzQ2hpbGROb2RlcyhcbiAgICAgICAgICAgIGl0ZW0ubm9kZSwgc3RhY2ssIGNvbnRleHQsIHRlbnNvck1hcCwgYWRkZWQsIHVzZWROb2Rlcyk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBwcm9taXNlcztcbiAgfVxuXG4gIHByaXZhdGUgcHJvY2Vzc0NoaWxkTm9kZXMoXG4gICAgICBub2RlOiBOb2RlLCBzdGFjazogTm9kZVdpdGhDb250ZXh0c1tdLCBjb250ZXh0OiBFeGVjdXRpb25Db250ZXh0LFxuICAgICAgdGVuc29yTWFwOiBOYW1lZFRlbnNvcnNNYXAsIGFkZGVkOiB7W2tleTogc3RyaW5nXTogYm9vbGVhbn0sXG4gICAgICB1c2VkTm9kZXM6IFNldDxzdHJpbmc+KSB7XG4gICAgbm9kZS5jaGlsZHJlbi5mb3JFYWNoKChjaGlsZE5vZGUpID0+IHtcbiAgICAgIGNvbnN0IFtub2RlTmFtZSwgXSA9IGdldE5vZGVOYW1lQW5kSW5kZXgoY2hpbGROb2RlLm5hbWUsIGNvbnRleHQpO1xuICAgICAgaWYgKGFkZGVkW25vZGVOYW1lXSB8fCAhdXNlZE5vZGVzLmhhcyhjaGlsZE5vZGUubmFtZSkpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgLy8gTWVyZ2Ugb3AgY2FuIGJlIHB1c2hlZCBpZiBhbnkgb2YgaXRzIGlucHV0cyBoYXMgdmFsdWUuXG4gICAgICBpZiAoY2hpbGROb2RlLm9wID09PSAnTWVyZ2UnKSB7XG4gICAgICAgIGlmIChjaGlsZE5vZGUuaW5wdXROYW1lcy5zb21lKG5hbWUgPT4ge1xuICAgICAgICAgICAgICByZXR1cm4gISFnZXRUZW5zb3IobmFtZSwgdGVuc29yTWFwLCBjb250ZXh0KTtcbiAgICAgICAgICAgIH0pKSB7XG4gICAgICAgICAgYWRkZWRbbm9kZU5hbWVdID0gdHJ1ZTtcbiAgICAgICAgICBzdGFjay5wdXNoKHtjb250ZXh0czogY29udGV4dC5jdXJyZW50Q29udGV4dCwgbm9kZTogY2hpbGROb2RlfSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSAgLy8gT3RoZXJ3aXNlIGFsbCBpbnB1dHMgbXVzdCB0byBoYXZlIHZhbHVlLlxuICAgICAgICAgIGlmIChjaGlsZE5vZGUuaW5wdXROYW1lcy5ldmVyeShuYW1lID0+IHtcbiAgICAgICAgICAgICAgICByZXR1cm4gISFnZXRUZW5zb3IobmFtZSwgdGVuc29yTWFwLCBjb250ZXh0KTtcbiAgICAgICAgICAgICAgfSkpIHtcbiAgICAgICAgYWRkZWRbbm9kZU5hbWVdID0gdHJ1ZTtcbiAgICAgICAgc3RhY2sucHVzaCh7Y29udGV4dHM6IGNvbnRleHQuY3VycmVudENvbnRleHQsIG5vZGU6IGNoaWxkTm9kZX0pO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbGVhc2VzIHRoZSBtZW1vcnkgdXNlZCBieSB0aGUgd2VpZ2h0IHRlbnNvcnMuXG4gICAqL1xuICBkaXNwb3NlKCkge1xuICAgIE9iamVjdC5rZXlzKHRoaXMud2VpZ2h0TWFwKVxuICAgICAgICAuZm9yRWFjaChcbiAgICAgICAgICAgIGtleSA9PiB0aGlzLndlaWdodE1hcFtrZXldLmZvckVhY2godGVuc29yID0+IHRlbnNvci5kaXNwb3NlKCkpKTtcbiAgfVxuXG4gIHByaXZhdGUgY2hlY2tJbnB1dFNoYXBlQW5kVHlwZShpbnB1dHM6IE5hbWVkVGVuc29yTWFwKSB7XG4gICAgT2JqZWN0LmtleXMoaW5wdXRzKS5mb3JFYWNoKG5hbWUgPT4ge1xuICAgICAgY29uc3QgaW5wdXQgPSBpbnB1dHNbbmFtZV07XG4gICAgICBjb25zdCBbbm9kZU5hbWUsIF0gPSBwYXJzZU5vZGVOYW1lKG5hbWUpO1xuICAgICAgY29uc3Qgbm9kZSA9IHRoaXMuZ3JhcGgubm9kZXNbbm9kZU5hbWVdO1xuICAgICAgaWYgKG5vZGUuYXR0clBhcmFtc1snc2hhcGUnXSAmJiBub2RlLmF0dHJQYXJhbXNbJ3NoYXBlJ10udmFsdWUpIHtcbiAgICAgICAgY29uc3Qgc2hhcGUgPSBub2RlLmF0dHJQYXJhbXNbJ3NoYXBlJ10udmFsdWUgYXMgbnVtYmVyW107XG4gICAgICAgIGNvbnN0IG1hdGNoID0gc2hhcGUubGVuZ3RoID09PSBpbnB1dC5zaGFwZS5sZW5ndGggJiZcbiAgICAgICAgICAgIGlucHV0LnNoYXBlLmV2ZXJ5KFxuICAgICAgICAgICAgICAgIChkaW0sIGluZGV4KSA9PiBzaGFwZVtpbmRleF0gPT09IC0xIHx8IHNoYXBlW2luZGV4XSA9PT0gZGltKTtcbiAgICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgICBtYXRjaCxcbiAgICAgICAgICAgICgpID0+IGBUaGUgc2hhcGUgb2YgZGljdFsnJHtub2RlLm5hbWV9J10gcHJvdmlkZWQgaW4gYCArXG4gICAgICAgICAgICAgICAgYG1vZGVsLmV4ZWN1dGUoZGljdCkgbXVzdCBiZSBbJHtzaGFwZX1dLCBidXQgd2FzIGAgK1xuICAgICAgICAgICAgICAgIGBbJHtpbnB1dC5zaGFwZX1dYCk7XG4gICAgICB9XG4gICAgICBpZiAobm9kZS5hdHRyUGFyYW1zWydkdHlwZSddICYmIG5vZGUuYXR0clBhcmFtc1snZHR5cGUnXS52YWx1ZSkge1xuICAgICAgICB1dGlsLmFzc2VydChcbiAgICAgICAgICAgIGlucHV0LmR0eXBlID09PSBub2RlLmF0dHJQYXJhbXNbJ2R0eXBlJ10udmFsdWUgYXMgc3RyaW5nLFxuICAgICAgICAgICAgKCkgPT4gYFRoZSBkdHlwZSBvZiBkaWN0Wycke25vZGUubmFtZX0nXSBwcm92aWRlZCBpbiBgICtcbiAgICAgICAgICAgICAgICBgbW9kZWwuZXhlY3V0ZShkaWN0KSBtdXN0IGJlIGAgK1xuICAgICAgICAgICAgICAgIGAke25vZGUuYXR0clBhcmFtc1snZHR5cGUnXS52YWx1ZX0sIGJ1dCB3YXMgJHtpbnB1dC5kdHlwZX1gKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgbWFwSW5wdXRzKGlucHV0czogTmFtZWRUZW5zb3JNYXApIHtcbiAgICBjb25zdCByZXN1bHQ6IE5hbWVkVGVuc29yTWFwID0ge307XG4gICAgZm9yIChjb25zdCBpbnB1dE5hbWUgaW4gaW5wdXRzKSB7XG4gICAgICBpZiAodGhpcy5fc2lnbmF0dXJlICE9IG51bGwgJiYgdGhpcy5fc2lnbmF0dXJlLmlucHV0cyAhPSBudWxsICYmXG4gICAgICAgICAgdGhpcy5fc2lnbmF0dXJlLmlucHV0c1tpbnB1dE5hbWVdICE9IG51bGwpIHtcbiAgICAgICAgY29uc3QgdGVuc29yID0gdGhpcy5fc2lnbmF0dXJlLmlucHV0c1tpbnB1dE5hbWVdO1xuICAgICAgICByZXN1bHRbdGVuc29yLm5hbWVdID0gaW5wdXRzW2lucHV0TmFtZV07XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXN1bHRbaW5wdXROYW1lXSA9IGlucHV0c1tpbnB1dE5hbWVdO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgcHJpdmF0ZSBjaGVja0lucHV0cyhpbnB1dHM6IE5hbWVkVGVuc29yTWFwKSB7XG4gICAgY29uc3Qgbm90SW5HcmFwaCA9IE9iamVjdC5rZXlzKGlucHV0cykuZmlsdGVyKG5hbWUgPT4ge1xuICAgICAgY29uc3QgW25vZGVOYW1lXSA9IHBhcnNlTm9kZU5hbWUobmFtZSk7XG4gICAgICByZXR1cm4gdGhpcy5ncmFwaC5ub2Rlc1tub2RlTmFtZV0gPT0gbnVsbDtcbiAgICB9KTtcbiAgICBpZiAobm90SW5HcmFwaC5sZW5ndGggPiAwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYFRoZSBkaWN0IHByb3ZpZGVkIGluIG1vZGVsLmV4ZWN1dGUoZGljdCkgaGFzIGAgK1xuICAgICAgICAgIGBrZXlzOiBbJHtub3RJbkdyYXBofV0gdGhhdCBhcmUgbm90IHBhcnQgb2YgZ3JhcGhgKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIG1hcE91dHB1dHMob3V0cHV0czogc3RyaW5nW10pIHtcbiAgICByZXR1cm4gb3V0cHV0cy5tYXAobmFtZSA9PiB7XG4gICAgICBpZiAodGhpcy5fc2lnbmF0dXJlICE9IG51bGwgJiYgdGhpcy5fc2lnbmF0dXJlLm91dHB1dHMgIT0gbnVsbCAmJlxuICAgICAgICAgIHRoaXMuX3NpZ25hdHVyZS5vdXRwdXRzW25hbWVdICE9IG51bGwpIHtcbiAgICAgICAgY29uc3QgdGVuc29yID0gdGhpcy5fc2lnbmF0dXJlLm91dHB1dHNbbmFtZV07XG4gICAgICAgIHJldHVybiB0ZW5zb3IubmFtZTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBuYW1lO1xuICAgIH0sIHt9KTtcbiAgfVxuXG4gIHByaXZhdGUgY2hlY2tPdXRwdXRzKG91dHB1dHM6IHN0cmluZ1tdKTogdm9pZCB7XG4gICAgb3V0cHV0cy5mb3JFYWNoKG5hbWUgPT4ge1xuICAgICAgY29uc3QgW25vcm1hbGl6ZWROYW1lXSA9IHBhcnNlTm9kZU5hbWUobmFtZSk7XG4gICAgICBpZiAoIXRoaXMuZ3JhcGgubm9kZXNbbm9ybWFsaXplZE5hbWVdKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgVGhlIG91dHB1dCAnJHtuYW1lfScgaXMgbm90IGZvdW5kIGluIHRoZSBncmFwaGApO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG59XG4iXX0=
\No newline at end of file