UNPKG

87.9 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.CostAnalyzer = exports.CostSnapshot = exports.CostMetric = void 0;
4const Listr = require("listr");
5const util_1 = require("util");
6const index_1 = require("../index");
7const shared_1 = require("./shared");
8const throttle_1 = require("./throttle");
9/**
10 * A line item in the cost estimate, including the resource usage metric
11 * measured and its pricing.
12 * @public
13 */
14class CostMetric {
15 /** @internal */
16 constructor(arg) {
17 this.name = arg.name;
18 this.pricing = arg.pricing;
19 this.unit = arg.unit;
20 this.measured = arg.measured;
21 this.unitPlural = arg.unitPlural;
22 this.comment = arg.comment;
23 this.informationalOnly = arg.informationalOnly;
24 }
25 /**
26 * The cost contribution of this cost metric. Equal to
27 * {@link CostMetric.pricing} * {@link CostMetric.measured}.
28 */
29 cost() {
30 return this.pricing * this.measured;
31 }
32 /**
33 * Return a string with the cost estimate for this metric, omitting
34 * comments.
35 */
36 describeCostOnly() {
37 const p = (n, precision = 8) => Number.isInteger(n) ? String(n) : n.toFixed(precision);
38 const getUnit = (n) => {
39 if (n > 1) {
40 return (this.unitPlural ||
41 (!this.unit.match(/[A-Z]$/) ? this.unit + "s" : this.unit));
42 }
43 else {
44 return this.unit;
45 }
46 };
47 const cost = `$${p(this.cost())}`;
48 const pricing = `$${p(this.pricing)}/${this.unit}`;
49 const metric = p(this.measured, this.unit === "second" ? 1 : 8);
50 const unit = getUnit(this.measured);
51 return `${this.name.padEnd(21)} ${pricing.padEnd(20)} ${metric.padStart(12)} ${unit.padEnd(10)} ${cost.padEnd(14)}`;
52 }
53 /** Describe this cost metric, including comments. */
54 toString() {
55 return `${this.describeCostOnly()}${(this.comment && `// ${this.comment}`) || ""}`;
56 }
57}
58exports.CostMetric = CostMetric;
59/**
60 * A summary of the costs incurred by a faast.js module at a point in time.
61 * Output of {@link FaastModule.costSnapshot}.
62 * @remarks
63 * Cost information provided by faast.js is an estimate. It is derived from
64 * internal faast.js measurements and not by consulting data provided by your
65 * cloud provider.
66 *
67 * **Faast.js does not guarantee the accuracy of cost estimates.**
68 *
69 * **Use at your own risk.**
70 *
71 * Example using AWS:
72 * ```typescript
73 * const faastModule = await faast("aws", m);
74 * try {
75 * // Invoke faastModule.functions.*
76 * } finally {
77 * await faastModule.cleanup();
78 * console.log(`Cost estimate:`);
79 * console.log(`${await faastModule.costSnapshot()}`);
80 * }
81 * ```
82 *
83 * AWS example output:
84 * ```text
85 * Cost estimate:
86 * functionCallDuration $0.00002813/second 0.6 second $0.00001688 68.4% [1]
87 * sqs $0.00000040/request 9 requests $0.00000360 14.6% [2]
88 * sns $0.00000050/request 5 requests $0.00000250 10.1% [3]
89 * functionCallRequests $0.00000020/request 5 requests $0.00000100 4.1% [4]
90 * outboundDataTransfer $0.09000000/GB 0.00000769 GB $0.00000069 2.8% [5]
91 * logIngestion $0.50000000/GB 0 GB $0 0.0% [6]
92 * ---------------------------------------------------------------------------------------
93 * $0.00002467 (USD)
94 *
95 * * Estimated using highest pricing tier for each service. Limitations apply.
96 * ** Does not account for free tier.
97 * [1]: https://aws.amazon.com/lambda/pricing (rate = 0.00001667/(GB*second) * 1.6875 GB = 0.00002813/second)
98 * [2]: https://aws.amazon.com/sqs/pricing
99 * [3]: https://aws.amazon.com/sns/pricing
100 * [4]: https://aws.amazon.com/lambda/pricing
101 * [5]: https://aws.amazon.com/ec2/pricing/on-demand/#Data_Transfer
102 * [6]: https://aws.amazon.com/cloudwatch/pricing/ - Log ingestion costs not currently included.
103 * ```
104 *
105 * A cost snapshot contains several {@link CostMetric} values. Each `CostMetric`
106 * summarizes one component of the overall cost of executing the functions so
107 * far. Some cost metrics are common to all faast providers, and other metrics
108 * are provider-specific. The common metrics are:
109 *
110 * - `functionCallDuration`: the estimated billed CPU time (rounded to the next
111 * 100ms) consumed by completed cloud function calls. This is the metric that
112 * usually dominates cost.
113 *
114 * - `functionCallRequests`: the number of invocation requests made. Most
115 * providers charge for each invocation.
116 *
117 * Provider-specific metrics vary. For example, AWS has the following additional
118 * metrics:
119 *
120 * - `sqs`: AWS Simple Queueing Service. This metric captures the number of
121 * queue requests made to insert and retrieve queued results (each 64kb chunk
122 * is counted as an additional request). SQS is used even if
123 * {@link CommonOptions.mode} is not set to `"queue"`, because it is necessary
124 * for monitoring cloud function invocations.
125 *
126 * - `sns`: AWS Simple Notification Service. SNS is used to invoke Lambda
127 * functions when {@link CommonOptions.mode} is `"queue"`.
128 *
129 * - `outboundDataTransfer`: an estimate of the network data transferred out
130 * from the cloud provider for this faast.js module. This estimate only counts
131 * data returned from cloud function invocations and infrastructure that
132 * faast.js sets up. It does not count any outbound data sent by your cloud
133 * functions that are not known to faast.js. Note that if you run faast.js on
134 * EC2 in the same region (see {@link AwsOptions.region}), then the data
135 * transfer costs will be zero (however, the cost snapshot will not include
136 * EC2 costs). Also note that if your cloud function transfers data from/to S3
137 * buckets in the same region, there is no cost as long as that data is not
138 * returned from the function.
139 *
140 * - `logIngestion`: this cost metric is always zero for AWS. It is present to
141 * remind the user that AWS charges for log data ingested by CloudWatch Logs
142 * that are not measured by faast.js. Log entries may arrive significantly
143 * after function execution completes, and there is no way for faast.js to
144 * know exactly how long to wait, therefore it does not attempt to measure
145 * this cost. In practice, if your cloud functions do not perform extensive
146 * logging on all invocations, log ingestion costs from faast.js are likely to
147 * be low or fall within the free tier.
148 *
149 * For Google, extra metrics include `outboundDataTransfer` similar to AWS, and
150 * `pubsub`, which combines costs that are split into `sns` and `sqs` on AWS.
151 *
152 * The Local provider has no extra metrics.
153 *
154 * Prices are retrieved dynamically from AWS and Google and cached locally.
155 * Cached prices expire after 24h. For each cost metric, faast.js uses the
156 * highest price tier to compute estimated pricing.
157 *
158 * Cost estimates do not take free tiers into account.
159 * @public
160 */
161class CostSnapshot {
162 /** @internal */
163 constructor(
164 /** The {@link Provider}, e.g. "aws" or "google" */
165 provider,
166 /**
167 * The options used to initialize the faast.js module where this cost
168 * snapshot was generated.
169 */
170 options, stats, costMetrics = []) {
171 this.provider = provider;
172 this.options = options;
173 /**
174 * The cost metric components for this cost snapshot. See
175 * {@link CostMetric}.
176 */
177 this.costMetrics = [];
178 this.stats = stats.clone();
179 this.costMetrics = [...costMetrics];
180 }
181 /** Sum of cost metrics. */
182 total() {
183 return (0, shared_1.sum)(this.costMetrics.map(metric => metric.cost()));
184 }
185 /** A summary of all cost metrics and prices in this cost snapshot. */
186 toString() {
187 let rv = "";
188 this.costMetrics.sort((a, b) => b.cost() - a.cost());
189 const total = this.total();
190 const comments = [];
191 const percent = (entry) => ((entry.cost() / total) * 100).toFixed(1).padStart(5) + "% ";
192 for (const entry of this.costMetrics) {
193 let commentIndex = "";
194 if (entry.comment) {
195 comments.push(entry.comment);
196 commentIndex = ` [${comments.length}]`;
197 }
198 rv += `${entry.describeCostOnly()}${percent(entry)}${commentIndex}\n`;
199 }
200 rv +=
201 "---------------------------------------------------------------------------------------\n";
202 rv += `$${this.total().toFixed(8)}`.padStart(78) + " (USD)\n\n";
203 rv += ` * Estimated using highest pricing tier for each service. Limitations apply.\n`;
204 rv += ` ** Does not account for free tier.\n`;
205 rv += comments.map((c, i) => `[${i + 1}]: ${c}`).join("\n");
206 return rv;
207 }
208 /**
209 * Comma separated value output for a cost snapshot.
210 * @remarks
211 * The format is "metric,unit,pricing,measured,cost,percentage,comment".
212 *
213 * Example output:
214 * ```text
215 * metric,unit,pricing,measured,cost,percentage,comment
216 * functionCallDuration,second,0.00002813,0.60000000,0.00001688,64.1% ,"https://aws.amazon.com/lambda/pricing (rate = 0.00001667/(GB*second) * 1.6875 GB = 0.00002813/second)"
217 * functionCallRequests,request,0.00000020,5,0.00000100,3.8% ,"https://aws.amazon.com/lambda/pricing"
218 * outboundDataTransfer,GB,0.09000000,0.00000844,0.00000076,2.9% ,"https://aws.amazon.com/ec2/pricing/on-demand/#Data_Transfer"
219 * sqs,request,0.00000040,13,0.00000520,19.7% ,"https://aws.amazon.com/sqs/pricing"
220 * sns,request,0.00000050,5,0.00000250,9.5% ,"https://aws.amazon.com/sns/pricing"
221 * logIngestion,GB,0.50000000,0,0,0.0% ,"https://aws.amazon.com/cloudwatch/pricing/ - Log ingestion costs not currently included."
222 * ```
223 */
224 csv() {
225 let rv = "";
226 rv += "metric,unit,pricing,measured,cost,percentage,comment\n";
227 const total = this.total();
228 const p = (n) => (Number.isInteger(n) ? n : n.toFixed(8));
229 const percent = (entry) => ((entry.cost() / total) * 100).toFixed(1) + "% ";
230 for (const entry of this.costMetrics) {
231 rv += `${entry.name},${entry.unit},${p(entry.pricing)},${p(entry.measured)},${p(entry.cost())},${percent(entry)},"${(entry.comment || "").replace('"', '""')}"\n`;
232 }
233 return rv;
234 }
235 /** @internal */
236 push(metric) {
237 this.costMetrics.push(metric);
238 }
239 /**
240 * Find a specific cost metric by name.
241 * @returns a {@link CostMetric} if found, otherwise `undefined`.
242 */
243 find(name) {
244 return this.costMetrics.find(m => m.name === name);
245 }
246}
247exports.CostSnapshot = CostSnapshot;
248/**
249 * Analyze the cost of a workload across many provider configurations.
250 * @public
251 */
252var CostAnalyzer;
253(function (CostAnalyzer) {
254 /**
255 * Default AWS cost analyzer configurations include all memory sizes for AWS
256 * Lambda.
257 * @remarks
258 * The default AWS cost analyzer configurations include every memory size
259 * from 128MB to 3008MB in 64MB increments. Each configuration has the
260 * following settings:
261 *
262 * ```typescript
263 * {
264 * provider: "aws",
265 * options: {
266 * mode: "https",
267 * memorySize,
268 * timeout: 300,
269 * gc: "off",
270 * childProcess: true
271 * }
272 * }
273 * ```
274 *
275 * Use `Array.map` to change or `Array.filter` to remove some of these
276 * configurations. For example:
277 *
278 * ```typescript
279 * const configsWithAtLeast1GB = awsConfigurations.filter(c => c.memorySize > 1024)
280 * const shorterTimeout = awsConfigurations.map(c => ({...c, timeout: 60 }));
281 * ```
282 * @public
283 */
284 CostAnalyzer.awsConfigurations = (() => {
285 const rv = [];
286 for (let memorySize = 128; memorySize <= 3008; memorySize += 64) {
287 rv.push({
288 provider: "aws",
289 options: {
290 mode: "https",
291 memorySize,
292 timeout: 300,
293 gc: "off",
294 childProcess: true
295 }
296 });
297 }
298 return rv;
299 })();
300 /**
301 * Default Google Cloud Functions cost analyzer configurations include all
302 * available memory sizes.
303 * @remarks
304 * Each google cost analyzer configuration follows this template:
305 *
306 * ```typescript
307 * {
308 * provider: "google",
309 * options: {
310 * mode: "https",
311 * memorySize,
312 * timeout: 300,
313 * gc: "off",
314 * childProcess: true
315 * }
316 * }
317 * ```
318 *
319 * where `memorySize` is in `[128, 256, 512, 1024, 2048]`.
320 * @public
321 */
322 CostAnalyzer.googleConfigurations = (() => {
323 const rv = [];
324 for (const memorySize of [128, 256, 512, 1024, 2048]) {
325 rv.push({
326 provider: "google",
327 options: {
328 mode: "https",
329 memorySize,
330 timeout: 300,
331 gc: "off",
332 childProcess: true
333 }
334 });
335 }
336 return rv;
337 })();
338 const workloadDefaults = {
339 configurations: CostAnalyzer.awsConfigurations,
340 summarize: summarizeMean,
341 format: defaultFormat,
342 formatCSV: defaultFormatCSV,
343 silent: false,
344 repetitions: 10,
345 concurrency: 64
346 };
347 function defaultFormat(attr, value) {
348 return `${attr}:${(0, shared_1.f1)(value)}`;
349 }
350 function defaultFormatCSV(_, value) {
351 return (0, shared_1.f1)(value);
352 }
353 const ps = (n) => (n / 1000).toFixed(3);
354 function summarizeMean(attributes) {
355 const stats = {};
356 attributes.forEach(a => (0, shared_1.keysOf)(a).forEach(attr => {
357 if (!(attr in stats)) {
358 stats[attr] = new shared_1.Statistics();
359 }
360 stats[attr].update(a[attr]);
361 }));
362 const result = {};
363 (0, shared_1.keysOf)(stats).forEach(attr => {
364 result[attr] = stats[attr].mean;
365 });
366 return result;
367 }
368 async function estimate(workload, config) {
369 const { provider, options } = config;
370 const faastModule = await (0, index_1.faast)(provider, workload.funcs, options);
371 const { repetitions, concurrency: repetitionConcurrency } = workload;
372 const doWork = (0, throttle_1.throttle)({ concurrency: repetitionConcurrency }, workload.work);
373 const results = [];
374 for (let i = 0; i < repetitions; i++) {
375 results.push(doWork(faastModule).catch(_ => { }));
376 }
377 const rv = (await Promise.all(results)).filter(r => r);
378 await faastModule.cleanup();
379 const costSnapshot = await faastModule.costSnapshot();
380 const extraMetrics = workload.summarize(rv);
381 return { costSnapshot, config, extraMetrics };
382 }
383 /**
384 * Estimate the cost of a workload using multiple configurations and
385 * providers.
386 * @param userWorkload - a {@link CostAnalyzer.Workload} object specifying
387 * the workload to run and additional parameters.
388 * @returns A promise for a {@link CostAnalyzer.Result}
389 * @public
390 * @remarks
391 * It can be deceptively difficult to set optimal parameters for AWS Lambda
392 * and similar services. On the surface there appears to be only one
393 * parameter: memory size. Choosing more memory also gives more CPU
394 * performance, but it's unclear how much. It's also unclear where single
395 * core performance stops getting better. The workload cost analyzer solves
396 * these problems by making it easy to run cost experiments.
397 * ```text
398 * (AWS)
399 * ┌───────┐
400 * ┌────▶│ 128MB │
401 * │ └───────┘
402 * │ ┌───────┐
403 * ┌─────────────────┐ ├────▶│ 256MB │
404 * ┌──────────────┐ │ │ │ └───────┘
405 * │ workload │───▶│ │ │ ...
406 * └──────────────┘ │ │ │ ┌───────┐
407 * │ cost analyzer │─────┼────▶│3008MB │
408 * ┌──────────────┐ │ │ │ └───────┘
409 * │configurations│───▶│ │ │
410 * └──────────────┘ │ │ │ (Google)
411 * └─────────────────┘ │ ┌───────┐
412 * ├────▶│ 128MB │
413 * │ └───────┘
414 * │ ┌───────┐
415 * └────▶│ 256MB │
416 * └───────┘
417 * ```
418 * `costAnalyzer` is the entry point. It automatically runs this workload
419 * against multiple configurations in parallel. Then it uses faast.js' cost
420 * snapshot mechanism to automatically determine the price of running the
421 * workload with each configuration.
422 *
423 * Example:
424 *
425 * ```typescript
426 * // functions.ts
427 * export function randomNumbers(n: number) {
428 * let sum = 0;
429 * for (let i = 0; i < n; i++) {
430 * sum += Math.random();
431 * }
432 * return sum;
433 * }
434 *
435 * // cost-analyzer-example.ts
436 * import { writeFileSync } from "fs";
437 * import { CostAnalyzer, FaastModule } from "faastjs";
438 * import * as funcs from "./functions";
439 *
440 * async function work(faastModule: FaastModule<typeof funcs>) {
441 * await faastModule.functions.randomNumbers(100000000);
442 * }
443 *
444 * async function main() {
445 * const results = await CostAnalyzer.analyze({ funcs, work });
446 * writeFileSync("cost.csv", results.csv());
447 * }
448 *
449 * main();
450 * ```
451 *
452 * Example output (this is printed to `console.log` unless the
453 * {@link CostAnalyzer.Workload.silent} is `true`):
454 * ```text
455 * ✔ aws 128MB queue 15.385s 0.274σ $0.00003921
456 * ✔ aws 192MB queue 10.024s 0.230σ $0.00003576
457 * ✔ aws 256MB queue 8.077s 0.204σ $0.00003779
458 * ▲ ▲ ▲ ▲ ▲ ▲
459 * │ │ │ │ │ │
460 * provider │ mode │ stdev average
461 * │ │ execution estimated
462 * memory │ time cost
463 * size │
464 * average cloud
465 * execution time
466 * ```
467 *
468 * The output lists the provider, memory size, ({@link CommonOptions.mode}),
469 * average time of a single execution of the workload, the standard
470 * deviation (in seconds) of the execution time, and average estimated cost
471 * for a single run of the workload.
472 *
473 * The "execution time" referenced here is not wall clock time, but rather
474 * execution time in the cloud function. The execution time does not include
475 * any time the workload spends waiting locally. If the workload invokes
476 * multiple cloud functions, their execution times will be summed even if
477 * they happen concurrently. This ensures the execution time and cost are
478 * aligned.
479 */
480 async function analyze(userWorkload) {
481 const scheduleEstimate = (0, throttle_1.throttle)({
482 concurrency: 8,
483 rate: 4,
484 burst: 1,
485 retry: 3
486 }, estimate);
487 const { concurrency = workloadDefaults.concurrency } = userWorkload;
488 const workload = {
489 ...workloadDefaults,
490 ...userWorkload,
491 work: (0, throttle_1.throttle)({ concurrency }, userWorkload.work)
492 };
493 const { configurations } = workload;
494 const promises = configurations.map(config => scheduleEstimate(workload, config));
495 const format = workload.format || defaultFormat;
496 const renderer = workload.silent ? "silent" : "default";
497 const list = new Listr(promises.map((promise, i) => {
498 const { provider, options } = configurations[i];
499 const { memorySize, mode } = options;
500 return {
501 title: `${provider} ${memorySize}MB ${mode}`,
502 task: async (_, task) => {
503 const { costSnapshot, extraMetrics } = await promise;
504 const total = (costSnapshot.total() / workload.repetitions).toFixed(8);
505 const { errors } = costSnapshot.stats;
506 const { executionTime } = costSnapshot.stats;
507 const message = `${ps(executionTime.mean)}s ${ps(executionTime.stdev)}σ $${total}`;
508 const errMessage = errors > 0 ? ` (${errors} errors)` : "";
509 const extra = (0, shared_1.keysOf)(extraMetrics)
510 .map(k => format(k, extraMetrics[k]))
511 .join(" ");
512 task.title = `${task.title} ${message}${errMessage} ${extra}`;
513 }
514 };
515 }), { concurrent: 8, nonTTYRenderer: renderer, renderer });
516 await list.run();
517 const results = await Promise.all(promises);
518 results.sort((a, b) => a.costSnapshot.options.memorySize - b.costSnapshot.options.memorySize);
519 return new Result(workload, results);
520 }
521 CostAnalyzer.analyze = analyze;
522 /**
523 * Cost analyzer results for each workload and configuration.
524 * @remarks
525 * The `estimates` property has the cost estimates for each configuration.
526 * See {@link CostAnalyzer.Estimate}.
527 * @public
528 */
529 class Result {
530 /** @internal */
531 constructor(
532 /** The workload analyzed. */
533 workload,
534 /**
535 * Cost estimates for each configuration of the workload. See
536 * {@link CostAnalyzer.Estimate}.
537 */
538 estimates) {
539 this.workload = workload;
540 this.estimates = estimates;
541 }
542 /**
543 * Comma-separated output of cost analyzer. One line per cost analyzer
544 * configuration.
545 * @remarks
546 * The columns are:
547 *
548 * - `memory`: The memory size allocated.
549 *
550 * - `cloud`: The cloud provider.
551 *
552 * - `mode`: See {@link CommonOptions.mode}.
553 *
554 * - `options`: A string summarizing other faast.js options applied to the
555 * `workload`. See {@link CommonOptions}.
556 *
557 * - `completed`: Number of repetitions that successfully completed.
558 *
559 * - `errors`: Number of invocations that failed.
560 *
561 * - `retries`: Number of retries that were attempted.
562 *
563 * - `cost`: The average cost of executing the workload once.
564 *
565 * - `executionTime`: the aggregate time spent executing on the provider for
566 * all cloud function invocations in the workload. This is averaged across
567 * repetitions.
568 *
569 * - `executionTimeStdev`: The standard deviation of `executionTime`.
570 *
571 * - `billedTime`: the same as `exectionTime`, except rounded up to the next
572 * 100ms for each invocation. Usually very close to `executionTime`.
573 */
574 csv() {
575 const attributes = new Set();
576 this.estimates.forEach(est => (0, shared_1.keysOf)(est.extraMetrics).forEach(key => attributes.add(key)));
577 const columns = [
578 "memory",
579 "cloud",
580 "mode",
581 "options",
582 "completed",
583 "errors",
584 "retries",
585 "cost",
586 "executionTime",
587 "executionTimeStdev",
588 "billedTime",
589 ...attributes
590 ];
591 let rv = columns.join(",") + "\n";
592 this.estimates.forEach(({ costSnapshot, extraMetrics }) => {
593 const { memorySize, mode, ...rest } = costSnapshot.options;
594 const options = `"${(0, util_1.inspect)(rest).replace('"', '""')}"`;
595 const { completed, errors, retries, executionTime, estimatedBilledTime } = costSnapshot.stats;
596 const cost = (costSnapshot.total() / this.workload.repetitions).toFixed(8);
597 const formatter = this.workload.formatCSV || defaultFormatCSV;
598 const metrics = {};
599 for (const attr of attributes) {
600 metrics[attr] = formatter(attr, extraMetrics[attr]);
601 }
602 const row = {
603 memory: memorySize,
604 cloud: costSnapshot.provider,
605 mode,
606 options,
607 completed,
608 errors,
609 retries,
610 cost: `$${cost}`,
611 executionTime: ps(executionTime.mean),
612 executionTimeStdev: ps(executionTime.stdev),
613 billedTime: ps(estimatedBilledTime.mean),
614 ...metrics
615 };
616 rv += (0, shared_1.keysOf)(row)
617 .map(k => String(row[k]))
618 .join(",");
619 rv += "\n";
620 });
621 return rv;
622 }
623 }
624 CostAnalyzer.Result = Result;
625})(CostAnalyzer = exports.CostAnalyzer || (exports.CostAnalyzer = {}));
626//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29zdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb3N0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLCtCQUErQjtBQUMvQiwrQkFBK0I7QUFDL0Isb0NBQThDO0FBSTlDLHFDQUF1RDtBQUN2RCx5Q0FBc0M7QUFHdEM7Ozs7R0FJRztBQUNILE1BQWEsVUFBVTtJQTBCbkIsZ0JBQWdCO0lBQ2hCLFlBQVksR0FBOEM7UUFDdEQsSUFBSSxDQUFDLElBQUksR0FBRyxHQUFHLENBQUMsSUFBSSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxPQUFPLEdBQUcsR0FBRyxDQUFDLE9BQU8sQ0FBQztRQUMzQixJQUFJLENBQUMsSUFBSSxHQUFHLEdBQUcsQ0FBQyxJQUFJLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsR0FBRyxHQUFHLENBQUMsUUFBUSxDQUFDO1FBQzdCLElBQUksQ0FBQyxVQUFVLEdBQUcsR0FBRyxDQUFDLFVBQVUsQ0FBQztRQUNqQyxJQUFJLENBQUMsT0FBTyxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUM7UUFDM0IsSUFBSSxDQUFDLGlCQUFpQixHQUFHLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBQztJQUNuRCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsSUFBSTtRQUNBLE9BQU8sSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDO0lBQ3hDLENBQUM7SUFFRDs7O09BR0c7SUFDSCxnQkFBZ0I7UUFDWixNQUFNLENBQUMsR0FBRyxDQUFDLENBQVMsRUFBRSxTQUFTLEdBQUcsQ0FBQyxFQUFFLEVBQUUsQ0FDbkMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzNELE1BQU0sT0FBTyxHQUFHLENBQUMsQ0FBUyxFQUFFLEVBQUU7WUFDMUIs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QUFRLENBQUMsU0FBUyxJQUFJLGdCQUFnQixDQUFDO2dCQUU5RCxNQUFNLE9BQU8sR0FBaUMsRUFBRSxDQUFDO2dCQUNqRCxLQUFLLE1BQU0sSUFBSSxJQUFJLFVBQVUsRUFBRTtvQkFDM0IsT0FBTyxDQUFDLElBQUksQ0FBQyxHQUFHLFNBQVMsQ0FBQyxJQUFJLEVBQUUsWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7aUJBQ3ZEO2dCQUNELE1BQU0sR0FBRyxHQUFHO29CQUNSLE1BQU0sRUFBRSxVQUFVO29CQUNsQixLQUFLLEVBQUUsWUFBWSxDQUFDLFFBQVE7b0JBQzVCLElBQUk7b0JBQ0osT0FBTztvQkFDUCxTQUFTO29CQUNULE1BQU07b0JBQ04sT0FBTztvQkFDUCxJQUFJLEVBQUUsSUFBSSxJQUFJLEVBQUU7b0JBQ2hCLGFBQWEsRUFBRSxFQUFFLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQztvQkFDckMsa0JBQWtCLEVBQUUsRUFBRSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUM7b0JBQzNDLFVBQVUsRUFBRSxFQUFFLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDO29CQUN4QyxHQUFHLE9BQU87aUJBQ2IsQ0FBQztnQkFFRixFQUFFLElBQUksSUFBQSxlQUFNLEVBQUMsR0FBRyxDQUFDO3FCQUNaLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztxQkFDeEIsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUNmLEVBQUUsSUFBSSxJQUFJLENBQUM7WUFDZixDQUFDLENBQUMsQ0FBQztZQUNILE9BQU8sRUFBRSxDQUFDO1FBQ2QsQ0FBQztLQUNKO0lBckdZLG1CQUFNLFNBcUdsQixDQUFBO0FBQ0wsQ0FBQyxFQW5oQmdCLFlBQVksR0FBWixvQkFBWSxLQUFaLG9CQUFZLFFBbWhCNUIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBMaXN0ciBmcm9tIFwibGlzdHJcIjtcbmltcG9ydCB7IGluc3BlY3QgfSBmcm9tIFwidXRpbFwiO1xuaW1wb3J0IHsgZmFhc3QsIEZhYXN0TW9kdWxlIH0gZnJvbSBcIi4uL2luZGV4XCI7XG5pbXBvcnQgeyBBd3NPcHRpb25zIH0gZnJvbSBcIi4vYXdzL2F3cy1mYWFzdFwiO1xuaW1wb3J0IHsgR29vZ2xlT3B0aW9ucyB9IGZyb20gXCIuL2dvb2dsZS9nb29nbGUtZmFhc3RcIjtcbmltcG9ydCB7IEZ1bmN0aW9uU3RhdHMsIENvbW1vbk9wdGlvbnMgfSBmcm9tIFwiLi9wcm92aWRlclwiO1xuaW1wb3J0IHsgZjEsIGtleXNPZiwgU3RhdGlzdGljcywgc3VtIH0gZnJvbSBcIi4vc2hhcmVkXCI7XG5pbXBvcnQgeyB0aHJvdHRsZSB9IGZyb20gXCIuL3Rocm90dGxlXCI7XG5pbXBvcnQgeyBQcm9wZXJ0aWVzRXhjZXB0LCBBbnlGdW5jdGlvbiB9IGZyb20gXCIuL3R5cGVzXCI7XG5cbi8qKlxuICogQSBsaW5lIGl0ZW0gaW4gdGhlIGNvc3QgZXN0aW1hdGUsIGluY2x1ZGluZyB0aGUgcmVzb3VyY2UgdXNhZ2UgbWV0cmljXG4gKiBtZWFzdXJlZCBhbmQgaXRzIHByaWNpbmcuXG4gKiBAcHVibGljXG4gKi9cbmV4cG9ydCBjbGFzcyBDb3N0TWV0cmljIHtcbiAgICAvKiogVGhlIG5hbWUgb2YgdGhlIGNvc3QgbWV0cmljLCBlLmcuIGBmdW5jdGlvbkNhbGxEdXJhdGlvbmAgKi9cbiAgICByZWFkb25seSBuYW1lOiBzdHJpbmc7XG4gICAgLyoqIFRoZSBwcmljZSBpbiBVU0QgcGVyIHVuaXQgbWVhc3VyZWQuICovXG4gICAgcmVhZG9ubHkgcHJpY2luZzogbnVtYmVyO1xuICAgIC8qKiBUaGUgbmFtZSBvZiB0aGUgdW5pdHMgdGhhdCBwcmljaW5nIGlzIG1lYXN1cmVkIGluIGZvciB0aGlzIG1ldHJpYy4gKi9cbiAgICByZWFkb25seSB1bml0OiBzdHJpbmc7XG4gICAgLyoqIFRoZSBtZWFzdXJlZCB2YWx1ZSBvZiB0aGUgY29zdCBtZXRyaWMsIGluIHVuaXRzLiAqL1xuICAgIHJlYWRvbmx5IG1lYXN1cmVkOiBudW1iZXI7XG4gICAgLyoqXG4gICAgICogVGhlIHBsdXJhbCBmb3JtIG9mIHRoZSB1bml0IG5hbWUuIEJ5IGRlZmF1bHQgdGhlIHBsdXJhbCBmb3JtIHdpbGwgYmUgdGhlXG4gICAgICogbmFtZSBvZiB0aGUgdW5pdCB3aXRoIFwic1wiIGFwcGVuZGVkIGF0IHRoZSBlbmQsIHVubGVzcyB0aGUgbGFzdCBsZXR0ZXIgaXNcbiAgICAgKiBjYXBpdGFsaXplZCwgaW4gd2hpY2ggY2FzZSB0aGVyZSBpcyBubyBwbHVyYWwgZm9ybSAoZS5nLiBcIkdCXCIpLlxuICAgICAqL1xuICAgIHJlYWRvbmx5IHVuaXRQbHVyYWw/OiBzdHJpbmc7XG4gICAgLyoqXG4gICAgICogQW4gb3B0aW9uYWwgY29tbWVudCwgdXN1YWxseSBwcm92aWRpbmcgYSBsaW5rIHRvIHRoZSBwcm92aWRlcidzIHByaWNpbmdcbiAgICAgKiBwYWdlIGFuZCBvdGhlciBkYXRhLlxuICAgICAqL1xuICAgIHJlYWRvbmx5IGNvbW1lbnQ/OiBzdHJpbmc7XG4gICAgLyoqXG4gICAgICogVHJ1ZSBpZiB0aGlzIGNvc3QgbWV0cmljIGlzIG9ubHkgZm9yIGluZm9ybWF0aW9uYWwgcHVycG9zZXMgKGUuZy4gQVdTJ3NcbiAgICAgKiBgbG9nSW5nZXN0aW9uYCkgYW5kIGRvZXMgbm90IGNvbnRyaWJ1dGUgY29zdC5cbiAgICAgKi9cbiAgICByZWFkb25seSBpbmZvcm1hdGlvbmFsT25seT86IGJvb2xlYW47XG5cbiAgICAvKiogQGludGVybmFsICovXG4gICAgY29uc3RydWN0b3IoYXJnOiBQcm9wZXJ0aWVzRXhjZXB0PENvc3RNZXRyaWMsIEFueUZ1bmN0aW9uPikge1xuICAgICAgICB0aGlzLm5hbWUgPSBhcmcubmFtZTtcbiAgICAgICAgdGhpcy5wcmljaW5nID0gYXJnLnByaWNpbmc7XG4gICAgICAgIHRoaXMudW5pdCA9IGFyZy51bml0O1xuICAgICAgICB0aGlzLm1lYXN1cmVkID0gYXJnLm1lYXN1cmVkO1xuICAgICAgICB0aGlzLnVuaXRQbHVyYWwgPSBhcmcudW5pdFBsdXJhbDtcbiAgICAgICAgdGhpcy5jb21tZW50ID0gYXJnLmNvbW1lbnQ7XG4gICAgICAgIHRoaXMuaW5mb3JtYXRpb25hbE9ubHkgPSBhcmcuaW5mb3JtYXRpb25hbE9ubHk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVGhlIGNvc3QgY29udHJpYnV0aW9uIG9mIHRoaXMgY29zdCBtZXRyaWMuIEVxdWFsIHRvXG4gICAgICoge0BsaW5rIENvc3RNZXRyaWMucHJpY2luZ30gKiB7QGxpbmsgQ29zdE1ldHJpYy5tZWFzdXJlZH0uXG4gICAgICovXG4gICAgY29zdCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucHJpY2luZyAqIHRoaXMubWVhc3VyZWQ7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJuIGEgc3RyaW5nIHdpdGggdGhlIGNvc3QgZXN0aW1hdGUgZm9yIHRoaXMgbWV0cmljLCBvbWl0dGluZ1xuICAgICAqIGNvbW1lbnRzLlxuICAgICAqL1xuICAgIGRlc2NyaWJlQ29zdE9ubHkoKSB7XG4gICAgICAgIGNvbnN0IHAgPSAobjogbnVtYmVyLCBwcmVjaXNpb24gPSA4KSA9PlxuICAgICAgICAgICAgTnVtYmVyLmlzSW50ZWdlcihuKSA/IFN0cmluZyhuKSA6IG4udG9GaXhlZChwcmVjaXNpb24pO1xuICAgICAgICBjb25zdCBnZXRVbml0ID0gKG46IG51bWJlcikgPT4ge1xuICAgICAgICAgICAgaWYgKG4gPiAxKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgICAgICAgICAgdGhpcy51bml0UGx1cmFsIHx8XG4gICAgICAgICAgICAgICAgICAgICghdGhpcy51bml0Lm1hdGNoKC9bQS1aXSQvKSA/IHRoaXMudW5pdCArIFwic1wiIDogdGhpcy51bml0KVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnVuaXQ7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG5cbiAgICAgICAgY29uc3QgY29zdCA9IGAkJHtwKHRoaXMuY29zdCgpKX1gO1xuICAgICAgICBjb25zdCBwcmljaW5nID0gYCQke3AodGhpcy5wcmljaW5nKX0vJHt0aGlzLnVuaXR9YDtcbiAgICAgICAgY29uc3QgbWV0cmljID0gcCh0aGlzLm1lYXN1cmVkLCB0aGlzLnVuaXQgPT09IFwic2Vjb25kXCIgPyAxIDogOCk7XG4gICAgICAgIGNvbnN0IHVuaXQgPSBnZXRVbml0KHRoaXMubWVhc3VyZWQpO1xuXG4gICAgICAgIHJldHVybiBgJHt0aGlzLm5hbWUucGFkRW5kKDIxKX0gJHtwcmljaW5nLnBhZEVuZCgyMCl9ICR7bWV0cmljLnBhZFN0YXJ0KFxuICAgICAgICAgICAgMTJcbiAgICAgICAgKX0gJHt1bml0LnBhZEVuZCgxMCl9ICR7Y29zdC5wYWRFbmQoMTQpfWA7XG4gICAgfVxuXG4gICAgLyoqIERlc2NyaWJlIHRoaXMgY29zdCBtZXRyaWMsIGluY2x1ZGluZyBjb21tZW50cy4gKi9cbiAgICB0b1N0cmluZygpIHtcbiAgICAgICAgcmV0dXJuIGAke3RoaXMuZGVzY3JpYmVDb3N0T25seSgpfSR7XG4gICAgICAgICAgICAodGhpcy5jb21tZW50ICYmIGAvLyAke3RoaXMuY29tbWVudH1gKSB8fCBcIlwiXG4gICAgICAgIH1gO1xuICAgIH1cbn1cblxuLyoqXG4gKiBBIHN1bW1hcnkgb2YgdGhlIGNvc3RzIGluY3VycmVkIGJ5IGEgZmFhc3QuanMgbW9kdWxlIGF0IGEgcG9pbnQgaW4gdGltZS5cbiAqIE91dHB1dCBvZiB7QGxpbmsgRmFhc3RNb2R1bGUuY29zdFNuYXBzaG90fS5cbiAqIEByZW1hcmtzXG4gKiBDb3N0IGluZm9ybWF0aW9uIHByb3ZpZGVkIGJ5IGZhYXN0LmpzIGlzIGFuIGVzdGltYXRlLiBJdCBpcyBkZXJpdmVkIGZyb21cbiAqIGludGVybmFsIGZhYXN0LmpzIG1lYXN1cmVtZW50cyBhbmQgbm90IGJ5IGNvbnN1bHRpbmcgZGF0YSBwcm92aWRlZCBieSB5b3VyXG4gKiBjbG91ZCBwcm92aWRlci5cbiAqXG4gKiAqKkZhYXN0LmpzIGRvZXMgbm90IGd1YXJhbnRlZSB0aGUgYWNjdXJhY3kgb2YgY29zdCBlc3RpbWF0ZXMuKipcbiAqXG4gKiAqKlVzZSBhdCB5b3VyIG93biByaXNrLioqXG4gKlxuICogRXhhbXBsZSB1c2luZyBBV1M6XG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBjb25zdCBmYWFzdE1vZHVsZSA9IGF3YWl0IGZhYXN0KFwiYXdzXCIsIG0pO1xuICogdHJ5IHtcbiAqICAgICAvLyBJbnZva2UgZmFhc3RNb2R1bGUuZnVuY3Rpb25zLipcbiAqIH0gZmluYWxseSB7XG4gKiAgICAgYXdhaXQgZmFhc3RNb2R1bGUuY2xlYW51cCgpO1xuICogICAgIGNvbnNvbGUubG9nKGBDb3N0IGVzdGltYXRlOmApO1xuICogICAgIGNvbnNvbGUubG9nKGAke2F3YWl0IGZhYXN0TW9kdWxlLmNvc3RTbmFwc2hvdCgpfWApO1xuICogfVxuICogYGBgXG4gKlxuICogQVdTIGV4YW1wbGUgb3V0cHV0OlxuICogYGBgdGV4dFxuICogQ29zdCBlc3RpbWF0ZTpcbiAqIGZ1bmN0aW9uQ2FsbER1cmF0aW9uICAkMC4wMDAwMjgxMy9zZWNvbmQgICAgICAgICAgICAwLjYgc2Vjb25kICAgICAkMC4wMDAwMTY4OCAgICA2OC40JSAgWzFdXG4gKiBzcXMgICAgICAgICAgICAgICAgICAgJDAuMDAwMDAwNDAvcmVxdWVzdCAgICAgICAgICAgICA5IHJlcXVlc3RzICAgJDAuMDAwMDAzNjAgICAgMTQuNiUgIFsyXVxuICogc25zICAgICAgICAgICAgICAgICAgICQwLjAwMDAwMDUwL3JlcXVlc3QgICAgICAgICAgICAgNSByZXF1ZXN0cyAgICQwLjAwMDAwMjUwICAgIDEwLjElICBbM11cbiAqIGZ1bmN0aW9uQ2FsbFJlcXVlc3RzICAkMC4wMDAwMDAyMC9yZXF1ZXN0ICAgICAgICAgICAgIDUgcmVxdWVzdHMgICAkMC4wMDAwMDEwMCAgICAgNC4xJSAgWzRdXG4gKiBvdXRib3VuZERhdGFUcmFuc2ZlciAgJDAuMDkwMDAwMDAvR0IgICAgICAgICAwLjAwMDAwNzY5IEdCICAgICAgICAgJDAuMDAwMDAwNjkgICAgIDIuOCUgIFs1XVxuICogbG9nSW5nZXN0aW9uICAgICAgICAgICQwLjUwMDAwMDAwL0dCICAgICAgICAgICAgICAgICAgMCBHQiAgICAgICAgICQwICAgICAgICAgICAgICAwLjAlICBbNl1cbiAqIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICQwLjAwMDAyNDY3IChVU0QpXG4gKlxuICogICAqIEVzdGltYXRlZCB1c2luZyBoaWdoZXN0IHByaWNpbmcgdGllciBmb3IgZWFjaCBzZXJ2aWNlLiBMaW1pdGF0aW9ucyBhcHBseS5cbiAqICAqKiBEb2VzIG5vdCBhY2NvdW50IGZvciBmcmVlIHRpZXIuXG4gKiBbMV06IGh0dHBzOi8vYXdzLmFtYXpvbi5jb20vbGFtYmRhL3ByaWNpbmcgKHJhdGUgPSAwLjAwMDAxNjY3LyhHQipzZWNvbmQpICogMS42ODc1IEdCID0gMC4wMDAwMjgxMy9zZWNvbmQpXG4gKiBbMl06IGh0dHBzOi8vYXdzLmFtYXpvbi5jb20vc3FzL3ByaWNpbmdcbiAqIFszXTogaHR0cHM6Ly9hd3MuYW1hem9uLmNvbS9zbnMvcHJpY2luZ1xuICogWzRdOiBodHRwczovL2F3cy5hbWF6b24uY29tL2xhbWJkYS9wcmljaW5nXG4gKiBbNV06IGh0dHBzOi8vYXdzLmFtYXpvbi5jb20vZWMyL3ByaWNpbmcvb24tZGVtYW5kLyNEYXRhX1RyYW5zZmVyXG4gKiBbNl06IGh0dHBzOi8vYXdzLmFtYXpvbi5jb20vY2xvdWR3YXRjaC9wcmljaW5nLyAtIExvZyBpbmdlc3Rpb24gY29zdHMgbm90IGN1cnJlbnRseSBpbmNsdWRlZC5cbiAqIGBgYFxuICpcbiAqIEEgY29zdCBzbmFwc2hvdCBjb250YWlucyBzZXZlcmFsIHtAbGluayBDb3N0TWV0cmljfSB2YWx1ZXMuIEVhY2ggYENvc3RNZXRyaWNgXG4gKiBzdW1tYXJpemVzIG9uZSBjb21wb25lbnQgb2YgdGhlIG92ZXJhbGwgY29zdCBvZiBleGVjdXRpbmcgdGhlIGZ1bmN0aW9ucyBzb1xuICogZmFyLiBTb21lIGNvc3QgbWV0cmljcyBhcmUgY29tbW9uIHRvIGFsbCBmYWFzdCBwcm92aWRlcnMsIGFuZCBvdGhlciBtZXRyaWNzXG4gKiBhcmUgcHJvdmlkZXItc3BlY2lmaWMuIFRoZSBjb21tb24gbWV0cmljcyBhcmU6XG4gKlxuICogLSBgZnVuY3Rpb25DYWxsRHVyYXRpb25gOiB0aGUgZXN0aW1hdGVkIGJpbGxlZCBDUFUgdGltZSAocm91bmRlZCB0byB0aGUgbmV4dFxuICogICAxMDBtcykgY29uc3VtZWQgYnkgY29tcGxldGVkIGNsb3VkIGZ1bmN0aW9uIGNhbGxzLiBUaGlzIGlzIHRoZSBtZXRyaWMgdGhhdFxuICogICB1c3VhbGx5IGRvbWluYXRlcyBjb3N0LlxuICpcbiAqIC0gYGZ1bmN0aW9uQ2FsbFJlcXVlc3RzYDogdGhlIG51bWJlciBvZiBpbnZvY2F0aW9uIHJlcXVlc3RzIG1hZGUuIE1vc3RcbiAqICAgcHJvdmlkZXJzIGNoYXJnZSBmb3IgZWFjaCBpbnZvY2F0aW9uLlxuICpcbiAqIFByb3ZpZGVyLXNwZWNpZmljIG1ldHJpY3MgdmFyeS4gRm9yIGV4YW1wbGUsIEFXUyBoYXMgdGhlIGZvbGxvd2luZyBhZGRpdGlvbmFsXG4gKiBtZXRyaWNzOlxuICpcbiAqIC0gYHNxc2A6IEFXUyBTaW1wbGUgUXVldWVpbmcgU2VydmljZS4gVGhpcyBtZXRyaWMgY2FwdHVyZXMgdGhlIG51bWJlciBvZlxuICogICBxdWV1ZSByZXF1ZXN0cyBtYWRlIHRvIGluc2VydCBhbmQgcmV0cmlldmUgcXVldWVkIHJlc3VsdHMgKGVhY2ggNjRrYiBjaHVua1xuICogICBpcyBjb3VudGVkIGFzIGFuIGFkZGl0aW9uYWwgcmVxdWVzdCkuIFNRUyBpcyB1c2VkIGV2ZW4gaWZcbiAqICAge0BsaW5rIENvbW1vbk9wdGlvbnMubW9kZX0gaXMgbm90IHNldCB0byBgXCJxdWV1ZVwiYCwgYmVjYXVzZSBpdCBpcyBuZWNlc3NhcnlcbiAqICAgZm9yIG1vbml0b3JpbmcgY2xvdWQgZnVuY3Rpb24gaW52b2NhdGlvbnMuXG4gKlxuICogLSBgc25zYDogQVdTIFNpbXBsZSBOb3RpZmljYXRpb24gU2VydmljZS4gU05TIGlzIHVzZWQgdG8gaW52b2tlIExhbWJkYVxuICogICBmdW5jdGlvbnMgd2hlbiB7QGxpbmsgQ29tbW9uT3B0aW9ucy5tb2RlfSBpcyBgXCJxdWV1ZVwiYC5cbiAqXG4gKiAtIGBvdXRib3VuZERhdGFUcmFuc2ZlcmA6IGFuIGVzdGltYXRlIG9mIHRoZSBuZXR3b3JrIGRhdGEgdHJhbnNmZXJyZWQgb3V0XG4gKiAgIGZyb20gdGhlIGNsb3VkIHByb3ZpZGVyIGZvciB0aGlzIGZhYXN0LmpzIG1vZHVsZS4gVGhpcyBlc3RpbWF0ZSBvbmx5IGNvdW50c1xuICogICBkYXRhIHJldHVybmVkIGZyb20gY2xvdWQgZnVuY3Rpb24gaW52b2NhdGlvbnMgYW5kIGluZnJhc3RydWN0dXJlIHRoYXRcbiAqICAgZmFhc3QuanMgc2V0cyB1cC4gSXQgZG9lcyBub3QgY291bnQgYW55IG91dGJvdW5kIGRhdGEgc2VudCBieSB5b3VyIGNsb3VkXG4gKiAgIGZ1bmN0aW9ucyB0aGF0IGFyZSBub3Qga25vd24gdG8gZmFhc3QuanMuIE5vdGUgdGhhdCBpZiB5b3UgcnVuIGZhYXN0LmpzIG9uXG4gKiAgIEVDMiBpbiB0aGUgc2FtZSByZWdpb24gKHNlZSB7QGxpbmsgQXdzT3B0aW9ucy5yZWdpb259KSwgdGhlbiB0aGUgZGF0YVxuICogICB0cmFuc2ZlciBjb3N0cyB3aWxsIGJlIHplcm8gKGhvd2V2ZXIsIHRoZSBjb3N0IHNuYXBzaG90IHdpbGwgbm90IGluY2x1ZGVcbiAqICAgRUMyIGNvc3RzKS4gQWxzbyBub3RlIHRoYXQgaWYgeW91ciBjbG91ZCBmdW5jdGlvbiB0cmFuc2ZlcnMgZGF0YSBmcm9tL3RvIFMzXG4gKiAgIGJ1Y2tldHMgaW4gdGhlIHNhbWUgcmVnaW9uLCB0aGVyZSBpcyBubyBjb3N0IGFzIGxvbmcgYXMgdGhhdCBkYXRhIGlzIG5vdFxuICogICByZXR1cm5lZCBmcm9tIHRoZSBmdW5jdGlvbi5cbiAqXG4gKiAtIGBsb2dJbmdlc3Rpb25gOiB0aGlzIGNvc3QgbWV0cmljIGlzIGFsd2F5cyB6ZXJvIGZvciBBV1MuIEl0IGlzIHByZXNlbnQgdG9cbiAqICAgcmVtaW5kIHRoZSB1c2VyIHRoYXQgQVdTIGNoYXJnZXMgZm9yIGxvZyBkYXRhIGluZ2VzdGVkIGJ5IENsb3VkV2F0Y2ggTG9nc1xuICogICB0aGF0IGFyZSBub3QgbWVhc3VyZWQgYnkgZmFhc3QuanMuIExvZyBlbnRyaWVzIG1heSBhcnJpdmUgc2lnbmlmaWNhbnRseVxuICogICBhZnRlciBmdW5jdGlvbiBleGVjdXRpb24gY29tcGxldGVzLCBhbmQgdGhlcmUgaXMgbm8gd2F5IGZvciBmYWFzdC5qcyB0b1xuICogICBrbm93IGV4YWN0bHkgaG93IGxvbmcgdG8gd2FpdCwgdGhlcmVmb3JlIGl0IGRvZXMgbm90IGF0dGVtcHQgdG8gbWVhc3VyZVxuICogICB0aGlzIGNvc3QuIEluIHByYWN0aWNlLCBpZiB5b3VyIGNsb3VkIGZ1bmN0aW9ucyBkbyBub3QgcGVyZm9ybSBleHRlbnNpdmVcbiAqICAgbG9nZ2luZyBvbiBhbGwgaW52b2NhdGlvbnMsIGxvZyBpbmdlc3Rpb24gY29zdHMgZnJvbSBmYWFzdC5qcyBhcmUgbGlrZWx5IHRvXG4gKiAgIGJlIGxvdyBvciBmYWxsIHdpdGhpbiB0aGUgZnJlZSB0aWVyLlxuICpcbiAqIEZvciBHb29nbGUsIGV4dHJhIG1ldHJpY3MgaW5jbHVkZSBgb3V0Ym91bmREYXRhVHJhbnNmZXJgIHNpbWlsYXIgdG8gQVdTLCBhbmRcbiAqIGBwdWJzdWJgLCB3aGljaCBjb21iaW5lcyBjb3N0cyB0aGF0IGFyZSBzcGxpdCBpbnRvIGBzbnNgIGFuZCBgc3FzYCBvbiBBV1MuXG4gKlxuICogVGhlIExvY2FsIHByb3ZpZGVyIGhhcyBubyBleHRyYSBtZXRyaWNzLlxuICpcbiAqIFByaWNlcyBhcmUgcmV0cmlldmVkIGR5bmFtaWNhbGx5IGZyb20gQVdTIGFuZCBHb29nbGUgYW5kIGNhY2hlZCBsb2NhbGx5LlxuICogQ2FjaGVkIHByaWNlcyBleHBpcmUgYWZ0ZXIgMjRoLiBGb3IgZWFjaCBjb3N0IG1ldHJpYywgZmFhc3QuanMgdXNlcyB0aGVcbiAqIGhpZ2hlc3QgcHJpY2UgdGllciB0byBjb21wdXRlIGVzdGltYXRlZCBwcmljaW5nLlxuICpcbiAqIENvc3QgZXN0aW1hdGVzIGRvIG5vdCB0YWtlIGZyZWUgdGllcnMgaW50byBhY2NvdW50LlxuICogQHB1YmxpY1xuICovXG5leHBvcnQgY2xhc3MgQ29zdFNuYXBzaG90IHtcbiAgICAvKiogVGhlIGZ1bmN0aW9uIHN0YXRpc3RpY3MgdGhhdCB3ZXJlIHVzZWQgdG8gY29tcHV0ZSBwcmljZXMuICovXG4gICAgcmVhZG9ubHkgc3RhdHM6IEZ1bmN0aW9uU3RhdHM7XG4gICAgLyoqXG4gICAgICogVGhlIGNvc3QgbWV0cmljIGNvbXBvbmVudHMgZm9yIHRoaXMgY29zdCBzbmFwc2hvdC4gU2VlXG4gICAgICoge0BsaW5rIENvc3RNZXRyaWN9LlxuICAgICAqL1xuICAgIHJlYWRvbmx5IGNvc3RNZXRyaWNzOiBDb3N0TWV0cmljW10gPSBbXTtcbiAgICAvKiogQGludGVybmFsICovXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIC8qKiBUaGUge0BsaW5rIFByb3ZpZGVyfSwgZS5nLiBcImF3c1wiIG9yIFwiZ29vZ2xlXCIgKi9cbiAgICAgICAgcmVhZG9ubHkgcHJvdmlkZXI6IHN0cmluZyxcbiAgICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBvcHRpb25zIHVzZWQgdG8gaW5pdGlhbGl6ZSB0aGUgZmFhc3QuanMgbW9kdWxlIHdoZXJlIHRoaXMgY29zdFxuICAgICAgICAgKiBzbmFwc2hvdCB3YXMgZ2VuZXJhdGVkLlxuICAgICAgICAgKi9cbiAgICAgICAgcmVhZG9ubHkgb3B0aW9uczogQ29tbW9uT3B0aW9ucyB8IEF3c09wdGlvbnMgfCBHb29nbGVPcHRpb25zLFxuICAgICAgICBzdGF0czogRnVuY3Rpb25TdGF0cyxcbiAgICAgICAgY29zdE1ldHJpY3M6IENvc3RNZXRyaWNbXSA9IFtdXG4gICAgKSB7XG4gICAgICAgIHRoaXMuc3RhdHMgPSBzdGF0cy5jbG9uZSgpO1xuICAgICAgICB0aGlzLmNvc3RNZXRyaWNzID0gWy4uLmNvc3RNZXRyaWNzXTtcbiAgICB9XG5cbiAgICAvKiogU3VtIG9mIGNvc3QgbWV0cmljcy4gKi9cbiAgICB0b3RhbCgpIHtcbiAgICAgICAgcmV0dXJuIHN1bSh0aGlzLmNvc3RNZXRyaWNzLm1hcChtZXRyaWMgPT4gbWV0cmljLmNvc3QoKSkpO1xuICAgIH1cblxuICAgIC8qKiBBIHN1bW1hcnkgb2YgYWxsIGNvc3QgbWV0cmljcyBhbmQgcHJpY2VzIGluIHRoaXMgY29zdCBzbmFwc2hvdC4gKi9cbiAgICB0b1N0cmluZygpIHtcbiAgICAgICAgbGV0IHJ2ID0gXCJcIjtcbiAgICAgICAgdGhpcy5jb3N0TWV0cmljcy5zb3J0KChhLCBiKSA9PiBiLmNvc3QoKSAtIGEuY29zdCgpKTtcbiAgICAgICAgY29uc3QgdG90YWwgPSB0aGlzLnRvdGFsKCk7XG4gICAgICAgIGNvbnN0IGNvbW1lbnRzID0gW107XG4gICAgICAgIGNvbnN0IHBlcmNlbnQgPSAoZW50cnk6IENvc3RNZXRyaWMpID0+XG4gICAgICAgICAgICAoKGVudHJ5LmNvc3QoKSAvIHRvdGFsKSAqIDEwMCkudG9GaXhlZCgxKS5wYWRTdGFydCg1KSArIFwiJSBcIjtcbiAgICAgICAgZm9yIChjb25zdCBlbnRyeSBvZiB0aGlzLmNvc3RNZXRyaWNzKSB7XG4gICAgICAgICAgICBsZXQgY29tbWVudEluZGV4ID0gXCJcIjtcbiAgICAgICAgICAgIGlmIChlbnRyeS5jb21tZW50KSB7XG4gICAgICAgICAgICAgICAgY29tbWVudHMucHVzaChlbnRyeS5jb21tZW50KTtcbiAgICAgICAgICAgICAgICBjb21tZW50SW5kZXggPSBgIFske2NvbW1lbnRzLmxlbmd0aH1dYDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJ2ICs9IGAke2VudHJ5LmRlc2NyaWJlQ29zdE9ubHkoKX0ke3BlcmNlbnQoZW50cnkpfSR7Y29tbWVudEluZGV4fVxcbmA7XG4gICAgICAgIH1cbiAgICAgICAgcnYgKz1cbiAgICAgICAgICAgIFwiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuXCI7XG4gICAgICAgIHJ2ICs9IGAkJHt0aGlzLnRvdGFsKCkudG9GaXhlZCg4KX1gLnBhZFN0YXJ0KDc4KSArIFwiIChVU0QpXFxuXFxuXCI7XG4gICAgICAgIHJ2ICs9IGAgICogRXN0aW1hdGVkIHVzaW5nIGhpZ2hlc3QgcHJpY2luZyB0aWVyIGZvciBlYWNoIHNlcnZpY2UuIExpbWl0YXRpb25zIGFwcGx5LlxcbmA7XG4gICAgICAgIHJ2ICs9IGAgKiogRG9lcyBub3QgYWNjb3VudCBmb3IgZnJlZSB0aWVyLlxcbmA7XG4gICAgICAgIHJ2ICs9IGNvbW1lbnRzLm1hcCgoYywgaSkgPT4gYFske2kgKyAxfV06ICR7Y31gKS5qb2luKFwiXFxuXCIpO1xuICAgICAgICByZXR1cm4gcnY7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ29tbWEgc2VwYXJhdGVkIHZhbHVlIG91dHB1dCBmb3IgYSBjb3N0IHNuYXBzaG90LlxuICAgICAqIEByZW1hcmtzXG4gICAgICogVGhlIGZvcm1hdCBpcyBcIm1ldHJpYyx1bml0LHByaWNpbmcsbWVhc3VyZWQsY29zdCxwZXJjZW50YWdlLGNvbW1lbnRcIi5cbiAgICAgKlxuICAgICAqIEV4YW1wbGUgb3V0cHV0OlxuICAgICAqIGBgYHRleHRcbiAgICAgKiBtZXRyaWMsdW5pdCxwcmljaW5nLG1lYXN1cmVkLGNvc3QscGVyY2VudGFnZSxjb21tZW50XG4gICAgICogZnVuY3Rpb25DYWxsRHVyYXRpb24sc2Vjb25kLDAuMDAwMDI4MTMsMC42MDAwMDAwMCwwLjAwMDAxNjg4LDY0LjElICxcImh0dHBzOi8vYXdzLmFtYXpvbi5jb20vbGFtYmRhL3ByaWNpbmcgKHJhdGUgPSAwLjAwMDAxNjY3LyhHQipzZWNvbmQpICogMS42ODc1IEdCID0gMC4wMDAwMjgxMy9zZWNvbmQpXCJcbiAgICAgKiBmdW5jdGlvbkNhbGxSZXF1ZXN0cyxyZXF1ZXN0LDAuMDAwMDAwMjAsNSwwLjAwMDAwMTAwLDMuOCUgLFwiaHR0cHM6Ly9hd3MuYW1hem9uLmNvbS9sYW1iZGEvcHJpY2luZ1wiXG4gICAgICogb3V0Ym91bmREYXRhVHJhbnNmZXIsR0IsMC4wOTAwMDAwMCwwLjAwMDAwODQ0LDAuMDAwMDAwNzYsMi45JSAsXCJodHRwczovL2F3cy5hbWF6b24uY29tL2VjMi9wcmljaW5nL29uLWRlbWFuZC8jRGF0YV9UcmFuc2ZlclwiXG4gICAgICogc3FzLHJlcXVlc3QsMC4wMDAwMDA0MCwxMywwLjAwMDAwNTIwLDE5LjclICxcImh0dHBzOi8vYXdzLmFtYXpvbi5jb20vc3FzL3ByaWNpbmdcIlxuICAgICAqIHNucyxyZXF1ZXN0LDAuMDAwMDAwNTAsNSwwLjAwMDAwMjUwLDkuNSUgLFwiaHR0cHM6Ly9hd3MuYW1hem9uLmNvbS9zbnMvcHJpY2luZ1wiXG4gICAgICogbG9nSW5nZXN0aW9uLEdCLDAuNTAwMDAwMDAsMCwwLDAuMCUgLFwiaHR0cHM6Ly9hd3MuYW1hem9uLmNvbS9jbG91ZHdhdGNoL3ByaWNpbmcvIC0gTG9nIGluZ2VzdGlvbiBjb3N0cyBub3QgY3VycmVudGx5IGluY2x1ZGVkLlwiXG4gICAgICogYGBgXG4gICAgICovXG4gICAgY3N2KCkge1xuICAgICAgICBsZXQgcnYgPSBcIlwiO1xuICAgICAgICBydiArPSBcIm1ldHJpYyx1bml0LHByaWNpbmcsbWVhc3VyZWQsY29zdCxwZXJjZW50YWdlLGNvbW1lbnRcXG5cIjtcbiAgICAgICAgY29uc3QgdG90YWwgPSB0aGlzLnRvdGFsKCk7XG4gICAgICAgIGNvbnN0IHAgPSAobjogbnVtYmVyKSA9PiAoTnVtYmVyLmlzSW50ZWdlcihuKSA/IG4gOiBuLnRvRml4ZWQoOCkpO1xuICAgICAgICBjb25zdCBwZXJjZW50ID0gKGVudHJ5OiBDb3N0TWV0cmljKSA9PlxuICAgICAgICAgICAgKChlbnRyeS5jb3N0KCkgLyB0b3RhbCkgKiAxMDApLnRvRml4ZWQoMSkgKyBcIiUgXCI7XG4gICAgICAgIGZvciAoY29uc3QgZW50cnkgb2YgdGhpcy5jb3N0TWV0cmljcykge1xuICAgICAgICAgICAgcnYgKz0gYCR7ZW50cnkubmFtZX0sJHtlbnRyeS51bml0fSwke3AoZW50cnkucHJpY2luZyl9LCR7cChcbiAgICAgICAgICAgICAgICBlbnRyeS5tZWFzdXJlZFxuICAgICAgICAgICAgKX0sJHtwKGVudHJ5LmNvc3QoKSl9LCR7cGVyY2VudChlbnRyeSl9LFwiJHsoZW50cnkuY29tbWVudCB8fCBcIlwiKS5yZXBsYWNlKFxuICAgICAgICAgICAgICAgICdcIicsXG4gICAgICAgICAgICAgICAgJ1wiXCInXG4gICAgICAgICAgICApfVwiXFxuYDtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcnY7XG4gICAgfVxuXG4gICAgLyoqIEBpbnRlcm5hbCAqL1xuICAgIHB1c2gobWV0cmljOiBDb3N0TWV0cmljKSB7XG4gICAgICAgIHRoaXMuY29zdE1ldHJpY3MucHVzaChtZXRyaWMpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZpbmQgYSBzcGVjaWZpYyBjb3N0IG1ldHJpYyBieSBuYW1lLlxuICAgICAqIEByZXR1cm5zIGEge0BsaW5rIENvc3RNZXRyaWN9IGlmIGZvdW5kLCBvdGhlcndpc2UgYHVuZGVmaW5lZGAuXG4gICAgICovXG4gICAgZmluZChuYW1lOiBzdHJpbmcpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29zdE1ldHJpY3MuZmluZChtID0+IG0ubmFtZSA9PT0gbmFtZSk7XG4gICAgfVxufVxuXG4vKipcbiAqIEFuYWx5emUgdGhlIGNvc3Qgb2YgYSB3b3JrbG9hZCBhY3Jvc3MgbWFueSBwcm92aWRlciBjb25maWd1cmF0aW9ucy5cbiAqIEBwdWJsaWNcbiAqL1xuZXhwb3J0IG5hbWVzcGFjZSBDb3N0QW5hbHl6ZXIge1xuICAgIC8qKlxuICAgICAqIEFuIGlucHV0IHRvIHtAbGluayBDb3N0QW5hbHl6ZXIuYW5hbHl6ZX0sIHNwZWNpZnlpbmcgb25lXG4gICAgICogY29uZmlndXJhdGlvbiBvZiBmYWFzdC5qcyB0byBydW4gYWdhaW5zdCBhIHdvcmtsb2FkLiBTZWVcbiAgICAgKiB7QGxpbmsgQXdzT3B0aW9uc30gYW5kIHtAbGluayBHb29nbGVPcHRpb25zfS5cbiAgICAgKiBAcHVibGljXG4gICAgICovXG4gICAgZXhwb3J0IHR5cGUgQ29uZmlndXJhdGlvbiA9XG4gICAgICAgIHwge1xuICAgICAgICAgICAgICBwcm92aWRlcjogXCJhd3NcIjtcbiAgICAgICAgICAgICAgb3B0aW9uczogQXdzT3B0aW9ucztcbiAgICAgICAgICB9XG4gICAgICAgIHwge1xuICAgICAgICAgICAgICBwcm92aWRlcjogXCJnb29nbGVcIjtcbiAgICAgICAgICAgICAgb3B0aW9uczogR29vZ2xlT3B0aW9ucztcbiAgICAgICAgICB9O1xuXG4gICAgLyoqXG4gICAgICogRGVmYXVsdCBBV1MgY29zdCBhbmFseXplciBjb25maWd1cmF0aW9ucyBpbmNsdWRlIGFsbCBtZW1vcnkgc2l6ZXMgZm9yIEFXU1xuICAgICAqIExhbWJkYS5cbiAgICAgKiBAcmVtYXJrc1xuICAgICAqIFRoZSBkZWZhdWx0IEFXUyBjb3N0IGFuYWx5emVyIGNvbmZpZ3VyYXRpb25zIGluY2x1ZGUgZXZlcnkgbWVtb3J5IHNpemVcbiAgICAgKiBmcm9tIDEyOE1CIHRvIDMwMDhNQiBpbiA2NE1CIGluY3JlbWVudHMuIEVhY2ggY29uZmlndXJhdGlvbiBoYXMgdGhlXG4gICAgICogZm9sbG93aW5nIHNldHRpbmdzOlxuICAgICAqXG4gICAgICogYGBgdHlwZXNjcmlwdFxuICAgICAqIHtcbiAgICAgKiAgICAgcHJvdmlkZXI6IFwiYXdzXCIsXG4gICAgICogICAgIG9wdGlvbnM6IHtcbiAgICAgKiAgICAgICAgIG1vZGU6IFwiaHR0cHNcIixcbiAgICAgKiAgICAgICAgIG1lbW9yeVNpemUsXG4gICAgICogICAgICAgICB0aW1lb3V0OiAzMDAsXG4gICAgICogICAgICAgICBnYzogXCJvZmZcIixcbiAgICAgKiAgICAgICAgIGNoaWxkUHJvY2VzczogdHJ1ZVxuICAgICAqICAgICB9XG4gICAgICogfVxuICAgICAqIGBgYFxuICAgICAqXG4gICAgICogVXNlIGBBcnJheS5tYXBgIHRvIGNoYW5nZSBvciBgQXJyYXkuZmlsdGVyYCB0byByZW1vdmUgc29tZSBvZiB0aGVzZVxuICAgICAqIGNvbmZpZ3VyYXRpb25zLiBGb3IgZXhhbXBsZTpcbiAgICAgKlxuICAgICAqIGBgYHR5cGVzY3JpcHRcbiAgICAgKiBjb25zdCBjb25maWdzV2l0aEF0TGVhc3QxR0IgPSBhd3NDb25maWd1cmF0aW9ucy5maWx0ZXIoYyA9PiBjLm1lbW9yeVNpemUgPiAxMDI0KVxuICAgICAqIGNvbnN0IHNob3J0ZXJUaW1lb3V0ID0gYXdzQ29uZmlndXJhdGlvbnMubWFwKGMgPT4gKHsuLi5jLCB0aW1lb3V0OiA2MCB9KSk7XG4gICAgICogYGBgXG4gICAgICogQHB1YmxpY1xuICAgICAqL1xuICAgIGV4cG9ydCBjb25zdCBhd3NDb25maWd1cmF0aW9uczogQ29uZmlndXJhdGlvbltdID0gKCgpID0+IHtcbiAgICAgICAgY29uc3QgcnY6IENvbmZpZ3VyYXRpb25bXSA9IFtdO1xuICAgICAgICBmb3IgKGxldCBtZW1vcnlTaXplID0gMTI4OyBtZW1vcnlTaXplIDw9IDMwMDg7IG1lbW9yeVNpemUgKz0gNjQpIHtcbiAgICAgICAgICAgIHJ2LnB1c2goe1xuICAgICAgICAgICAgICAgIHByb3ZpZGVyOiBcImF3c1wiLFxuICAgICAgICAgICAgICAgIG9wdGlvbnM6IHtcbiAgICAgICAgICAgICAgICAgICAgbW9kZTogXCJodHRwc1wiLFxuICAgICAgICAgICAgICAgICAgICBtZW1vcnlTaXplLFxuICAgICAgICAgICAgICAgICAgICB0aW1lb3V0OiAzMDAsXG4gICAgICAgICAgICAgICAgICAgIGdjOiBcIm9mZlwiLFxuICAgICAgICAgICAgICAgICAgICBjaGlsZFByb2Nlc3M6IHRydWVcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcnY7XG4gICAgfSkoKTtcblxuICAgIC8qKlxuICAgICAqIERlZmF1bHQgR29vZ2xlIENsb3VkIEZ1bmN0aW9ucyBjb3N0IGFuYWx5emVyIGNvbmZpZ3VyYXRpb25zIGluY2x1ZGUgYWxsXG4gICAgICogYXZhaWxhYmxlIG1lbW9yeSBzaXplcy5cbiAgICAgKiBAcmVtYXJrc1xuICAgICAqIEVhY2ggZ29vZ2xlIGNvc3QgYW5hbHl6ZXIgY29uZmlndXJhdGlvbiBmb2xsb3dzIHRoaXMgdGVtcGxhdGU6XG4gICAgICpcbiAgICAgKiBgYGB0eXBlc2NyaXB0XG4gICAgICoge1xuICAgICAqICAgICBwcm92aWRlcjogXCJnb29nbGVcIixcbiAgICAgKiAgICAgb3B0aW9uczoge1xuICAgICAqICAgICAgICAgbW9kZTogXCJodHRwc1wiLFxuICAgICAqICAgICAgICAgbWVtb3J5U2l6ZSxcbiAgICAgKiAgICAgICAgIHRpbWVvdXQ6IDMwMCxcbiAgICAgKiAgICAgICAgIGdjOiBcIm9mZlwiLFxuICAgICAqICAgICAgICAgY2hpbGRQcm9jZXNzOiB0cnVlXG4gICAgICogICAgIH1cbiAgICAgKiB9XG4gICAgICogYGBgXG4gICAgICpcbiAgICAgKiB3aGVyZSBgbWVtb3J5U2l6ZWAgaXMgaW4gYFsxMjgsIDI1NiwgNTEyLCAxMDI0LCAyMDQ4XWAuXG4gICAgICogQHB1YmxpY1xuICAgICAqL1xuICAgIGV4cG9ydCBjb25zdCBnb29nbGVDb25maWd1cmF0aW9uczogQ29uZmlndXJhdGlvbltdID0gKCgpID0+IHtcbiAgICAgICAgY29uc3QgcnY6IENvbmZpZ3VyYXRpb25bXSA9IFtdO1xuICAgICAgICBmb3IgKGNvbnN0IG1lbW9yeVNpemUgb2YgWzEyOCwgMjU2LCA1MTIsIDEwMjQsIDIwNDhdKSB7XG4gICAgICAgICAgICBydi5wdXNoKHtcbiAgICAgICAgICAgICAgICBwcm92aWRlcjogXCJnb29nbGVcIixcbiAgICAgICAgICAgICAgICBvcHRpb25zOiB7XG4gICAgICAgICAgICAgICAgICAgIG1vZGU6IFwiaHR0cHNcIixcbiAgICAgICAgICAgICAgICAgICAgbWVtb3J5U2l6ZSxcbiAgICAgICAgICAgICAgICAgICAgdGltZW91dDogMzAwLFxuICAgICAgICAgICAgICAgICAgICBnYzogXCJvZmZcIixcbiAgICAgICAgICAgICAgICAgICAgY2hpbGRQcm9jZXNzOiB0cnVlXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJ2O1xuICAgIH0pKCk7XG5cbiAgICAvKipcbiAgICAgKiBVc2VyLWRlZmluZWQgY3VzdG9tIG1ldHJpY3MgZm9yIGEgd29ya2xvYWQuIFRoZXNlIGFyZSBhdXRvbWF0aWNhbGx5XG4gICAgICogc3VtbWFyaXplZCBpbiB0aGUgb3V0cHV0OyBzZWUge0BsaW5rIENvc3RBbmFseXplci5Xb3JrbG9hZH0uXG4gICAgICogQHB1YmxpY1xuICAgICAqL1xuICAgIGV4cG9ydCB0eXBlIFdvcmtsb2FkQXR0cmlidXRlPEEgZXh0ZW5kcyBzdHJpbmc+ID0geyBbYXR0ciBpbiBBXTogbnVtYmVyIH07XG5cbiAgICAvKipcbiAgICAgKiBBIHVzZXItZGVmaW5lZCBjb3N0IGFuYWx5emVyIHdvcmtsb2FkIGZvciB7QGxpbmsgQ29zdEFuYWx5emVyLmFuYWx5emV9LlxuICAgICAqIEBwdWJsaWNcbiAgICAgKiBFeGFtcGxlOlxuICAgICAqL1xuICAgIGV4cG9ydCBpbnRlcmZhY2UgV29ya2xvYWQ8VCBleHRlbmRzIG9iamVjdCwgQSBleHRlbmRzIHN0cmluZz4ge1xuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIGltcG9ydGVkIG1vZHVsZSB0aGF0IGNvbnRhaW5zIHRoZSBjbG91ZCBmdW5jdGlvbnMgdG8gdGVzdC5cbiAgICAgICAgICovXG4gICAgICAgIGZ1bmNzOiBUO1xuICAgICAgICAvKipcbiAgICAgICAgICogQSBmdW5jdGlvbiB0aGF0IGV4ZWN1dGVzIGNsb3VkIGZ1bmN0aW9ucyBvblxuICAgICAgICAgKiBgZmFhc3RNb2R1bGUuZnVuY3Rpb25zLipgLiBUaGUgd29yayBmdW5jdGlvbiBzaG91bGQgcmV0dXJuIGB2b2lkYCBpZlxuICAgICAgICAgKiB0aGVyZSBhcmUgbm8gY3VzdG9tIHdvcmtsb2FkIGF0dHJpYnV0ZXMuIE90aGVyd2lzZSwgaXQgc2hvdWxkIHJldHVyblxuICAgICAgICAgKiBhIHtAbGluayBDb3N0QW5hbHl6ZXIuV29ya2xvYWRBdHRyaWJ1dGV9IG9iamVjdCB3aGljaCBtYXBzXG4gICAgICAgICAqIHVzZXItZGVmaW5lZCBhdHRyaWJ1dGUgbmFtZXMgdG8gbnVtZXJpY2FsIHZhbHVlcyBmb3IgdGhlIHdvcmtsb2FkLlxuICAgICAgICAgKiBGb3IgZXhhbXBsZSwgdGhpcyBtaWdodCBtZWFzdXJlIGJhbmR3aWR0aCBvciBzb21lIG90aGVyIG1ldHJpYyBub3RcbiAgICAgICAgICogdHJhY2tlZCBieSBmYWFzdC5qcywgYnV0IGFyZSByZWxldmFudCBmb3IgZXZhbHVhdGluZyB0aGVcbiAgICAgICAgICogY29zdC1wZXJmb3JtYW5jZSB0cmFkZW9mZiBvZiB0aGUgY29uZmlndXJhdGlvbnMgYW5hbHl6ZWQgYnkgdGhlIGNvc3RcbiAgICAgICAgICogYW5hbHl6ZXIuXG4gICAgICAgICAqL1xuICAgICAgICB3b3JrOiAoZmFhc3RNb2R1bGU6IEZhYXN0TW9kdWxlPFQ+KSA9PiBQcm9taXNlPFdvcmtsb2FkQXR0cmlidXRlPEE+IHwgdm9pZD47XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBBbiBhcnJheSBvZiBjb25maWd1cmF0aW9ucyB0byBydW4gdGhlIHdvcmsgZnVuY3Rpb24gYWdhaW5zdCAoc2VlXG4gICAgICAgICAqIHtAbGluayBDb3N0QW5hbHl6ZXIuQ29uZmlndXJhdGlvbn0pLiBGb3IgZXhhbXBsZSwgZWFjaCBlbnRyeSBpbiB0aGVcbiAgICAgICAgICogYXJyYXkgbWF5IHNwZWNpZnkgYSBwcm92aWRlciwgbWVtb3J5IHNpemUsIGFuZCBvdGhlciBvcHRpb25zLlxuICAgICAgICAgKiBEZWZhdWx0OiB7QGxpbmsgQ29zdEFuYWx5emVyLmF3c0NvbmZpZ3VyYXRpb25zfS5cbiAgICAgICAgICovXG4gICAgICAgIGNvbmZpZ3VyYXRpb25zPzogQ29uZmlndXJhdGlvbltdO1xuICAgICAgICAvKipcbiAgICAgICAgICogQ29tYmluZSB7QGxpbmsgQ29zdEFuYWx5emVyLldvcmtsb2FkQXR0cmlidXRlfSBpbnN0YW5jZXMgcmV0dXJuZWRcbiAgICAgICAgICogZnJvbSBtdWx0aXBsZSB3b3JrbG9hZCBleGVjdXRpb25zIChjYXVzZWQgYnkgdmFsdWUgb2ZcbiAgICAgICAgICoge0BsaW5rIENvc3RBbmFseXplci5Xb3JrbG9hZC5yZXBldGl0aW9uc30pLiBUaGUgZGVmYXVsdCBpcyBhIGZ1bmN0aW9uXG4gICAgICAgICAqIHRoYXQgdGFrZXMgdGhlIGF2ZXJhZ2Ugb2YgZWFjaCBhdHRyaWJ1dGUuXG4gICAgICAgICAqL1xuICAgICAgICBzdW1tYXJpemU/OiAoc3VtbWFyaWVzOiBXb3JrbG9hZEF0dHJpYnV0ZTxBPltdKSA9PiBXb3JrbG9hZEF0dHJpYnV0ZTxBPjtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIEZvcm1hdCBhbiBhdHRyaWJ1dGUgdmFsdWUgZm9yIGNvbnNvbGUgb3V0cHV0LiBUaGlzIGlzIGRpc3BsYXllZCBieVxuICAgICAgICAgKiB0aGUgY29zdCBhbmFseXplciB3aGVuIGFsbCBvZiB0aGUgcmVwZXRpdGlvbnMgZm9yIGEgY29uZmlndXJhdGlvblxuICAgICAgICAgKiBoYXZlIGNvbXBsZXRlZC4gVGhlIGRlZmF1bHQgcmV0dXJuc1xuICAgICAgICAgKiBgJHthdHRyaWJ1dGV9OiR7dmFsdWUudG9GaXhlZCgxKX1gLlxuICAgICAgICAgKi9cbiAgICAgICAgZm9ybWF0PzogKGF0dHI6IEEsIHZhbHVlOiBudW1iZXIpID0+IHN0cmluZztcbiAgICAgICAgLyoqXG4gICAgICAgICAqIEZvcm1hdCBhbiBhdHRyaWJ1dGUgdmFsdWUgZm9yIENTVi4gVGhlIGRlZmF1bHQgcmV0dXJuc1xuICAgICAgICAgKiBgdmFsdWUudG9GaXhlZCgxKWAuXG4gICAgICAgICAqL1xuICAgICAgICBmb3JtYXRDU1Y/OiAoYXR0cjogQSwgdmFsdWU6IG51bWJlcikgPT4gc3RyaW5nO1xuICAgICAgICAvKipcbiAgICAgICAgICogSWYgdHJ1ZSwgZG8gbm90IG91dHB1dCBsaXZlIHJlc3VsdHMgdG8gdGhlIGNvbnNvbGUuIENhbiBiZSB1c2VmdWwgZm9yXG4gICAgICAgICAqIHJ1bm5pbmcgdGhlIGNvc3QgYW5hbHl6ZXIgYXMgcGFydCBvZiBhdXRvbWF0ZWQgdGVzdHMuIERlZmF1bHQ6IGZhbHNlLlxuICAgICAgICAgKi9cbiAgICAgICAgc2lsZW50PzogYm9vbGVhbjtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIFRoZSBudW1iZXIgb2YgcmVwZXRpdGlvbnMgdG8gcnVuIHRoZSB3b3JrbG9hZCBmb3IgZWFjaCBjb3N0IGFuYWx5emVyXG4gICAgICAgICAqIGNvbmZpZ3VyYXRpb24uIEhpZ2hlciByZXBldGl0aW9ucyBoZWxwIHJlZHVjZSB0aGUgaml0dGVyIGluIHRoZVxuICAgICAgICAgKiByZXN1bHRzLiBSZXBldGl0aW9ucyBleGVjdXRlIGluIHRoZSBzYW1lIEZhYXN0TW9kdWxlIGluc3RhbmNlLlxuICAgICAgICAgKiBEZWZhdWx0OiAxMC5cbiAgICAgICAgICovXG4gICAgICAgIHJlcGV0aXRpb25zPzogbnVtYmVyO1xuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIGFtb3VudCBvZiBjb25jdXJyZW5jeSB0byBhbGxvdy4gQ29uY3VycmVuY3kgY2FuIGFyaXNlIGZyb21cbiAgICAgICAgICogbXVsdGlwbGUgcmVwZXRpdGlvbnMgb2YgdGhlIHNhbWUgY29uZmlndXJhdGlvbiwgb3IgY29uY3VycmVuY3RcbiAgICAgICAgICogZXhlY3V0aW9ucyBvZiBkaWZmZXJlbnQgY29uZmlndXJhdGlvbnMuIFRoaXMgY29uY3VycmVuY3kgbGltaXRcbiAgICAgICAgICogdGhyb3R0bGVzIHRoZSB0b3RhbCBudW1iZXIgb2YgY29uY3VycmVudCB3b3JrbG9hZCBleGVjdXRpb25zIGFjcm9zc1xuICAgICAgICAgKiBib3RoIG9mIHRoZXNlIHNvdXJjZXMgb2YgY29uY3VycmVuY3kuIERlZmF1bHQ6IDY0LlxuICAgICAgICAgKi9cbiAgICAgICAgY29uY3VycmVuY3k/OiBudW1iZXI7XG4gICAgfVxuXG4gICAgY29uc3Qgd29ya2xvYWREZWZhdWx0cyA9IHtcbiAgICAgICAgY29uZmlndXJhdGlvbnM6IGF3c0NvbmZpZ3VyYXRpb25zLFxuICAgICAgICBzdW1tYXJpemU6IHN1bW1hcml6ZU1lYW4sXG4gICAgICAgIGZvcm1hdDogZGVmYXVsdEZvcm1hdCxcbiAgICAgICAgZm9ybWF0Q1NWOiBkZWZhdWx0Rm9ybWF0Q1NWLFxuICAgICAgICBzaWxlbnQ6IGZhbHNlLFxuICAgICAgICByZXBldGl0aW9uczogMTAsXG4gICAgICAgIGNvbmN1cnJlbmN5OiA2NFxuICAgIH07XG5cbiAgICBmdW5jdGlvbiBkZWZhdWx0Rm9ybWF0KGF0dHI6IHN0cmluZywgdmFsdWU6IG51bWJlcikge1xuICAgICAgICByZXR1cm4gYCR7YXR0cn06JHtmMSh2YWx1ZSl9YDtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBkZWZhdWx0Rm9ybWF0Q1NWKF86IHN0cmluZywgdmFsdWU6IG51bWJlcikge1xuICAgICAgICByZXR1cm4gZjEodmFsdWUpO1xuICAgIH1cblxuICAgIGNvbnN0IHBzID0gKG46IG51bWJlcikgPT4gKG4gLyAxMDAwKS50b0ZpeGVkKDMpO1xuXG4gICAgZnVuY3Rpb24gc3VtbWFyaXplTWVhbjxBIGV4dGVuZHMgc3RyaW5nPihhdHRyaWJ1dGVzOiBXb3JrbG9hZEF0dHJpYnV0ZTxBPltdKSB7XG4gICAgICAgIGNvbnN0IHN0YXRzOiB7IFthdHRyOiBzdHJpbmddOiBTdGF0aXN0aWNzIH0gPSB7fTtcbiAgICAgICAgYXR0cmlidXRlcy5mb3JFYWNoKGEgPT5cbiAgICAgICAgICAgIGtleXNPZihhKS5mb3JFYWNoKGF0dHIgPT4ge1xuICAgICAgICAgICAgICAgIGlmICghKGF0dHIgaW4gc3RhdHMpKSB7XG4gICAgICAgICAgICAgICAgICAgIHN0YXRzW2F0dHJdID0gbmV3IFN0YXRpc3RpY3MoKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgc3RhdHNbYXR0cl0udXBkYXRlKGFbYXR0cl0pO1xuICAgICAgICAgICAgfSlcbiAgICAgICAgKTtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0ge30gYXMgYW55O1xuICAgICAgICBrZXlzT2Yoc3RhdHMpLmZvckVhY2goYXR0ciA9PiB7XG4gICAgICAgICAgICByZXN1bHRbYXR0cl0gPSBzdGF0c1thdHRyXS5tZWFuO1xuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBBIGNvc3QgZXN0aW1hdGUgcmVzdWx0IGZvciBhIHNwZWNpZmljIGNvc3QgYW5hbHl6ZXIgY29uZmlndXJhdGlvbi5cbiAgICAgKiBAcHVibGljXG4gICAgICovXG4gICAgZXhwb3J0IGludGVyZmFjZSBFc3RpbWF0ZTxBIGV4dGVuZHMgc3RyaW5nPiB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgY29zdCBzbmFwc2hvdCBmb3IgdGhlIGNvc3QgYW5hbHlzaXMgb2YgdGhlIHNwZWNpZmljICh3b3JrbG9hZCxcbiAgICAgICAgICogY29uZmlndXJhdGlvbikgY29tYmluYXRpb24uIFNlZSB7QGxpbmsgQ29zdFNuYXBzaG90fS5cbiAgICAgICAgICovXG4gICAgICAgIGNvc3RTbmFwc2hvdDogQ29zdFNuYXBzaG90O1xuICAgICAgICAvKipcbiAgICAgICAgICogVGhlIHdvcmxvYWQgY29uZmlndXJhdGlvbiB0aGF0IHdhcyBhbmFseXplZC4gU2VlXG4gICAgICAgICAqIHtAbGluayBDb3N0QW5hbHl6ZXIuQ29uZmlndXJhdGlvbn0uXG4gICAgICAgICAqL1xuICAgICAgICBjb25maWc6IENvbmZpZ3VyYXRpb247XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBBZGRpdGlvbmFsIHdvcmtsb2FkIG1ldHJpY3MgcmV0dXJuZWQgZnJvbSB0aGUgd29yayBmdW5jdGlvbi4gU2VlXG4gICAgICAgICAqIHtAbGluayBDb3N0QW5hbHl6ZXIuV29ya2xvYWRBdHRyaWJ1dGV9LlxuICAgICAgICAgKi9cbiAgICAgICAgZXh0cmFNZXRyaWNzOiBXb3JrbG9hZEF0dHJpYnV0ZTxBPjtcbiAgICB9XG5cbiAgICBhc3luYyBmdW5jdGlvbiBlc3RpbWF0ZTxUIGV4dGVuZHMgb2JqZWN0LCBLIGV4dGVuZHMgc3RyaW5nPihcbiAgICAgICAgd29ya2xvYWQ6IFJlcXVpcmVkPFdvcmtsb2FkPFQsIEs+PixcbiAgICAgICAgY29uZmlnOiBDb25maWd1cmF0aW9uXG4gICAgKTogUHJvbWlzZTxFc3RpbWF0ZTxLPj4ge1xuICAgICAgICBjb25zdCB7IHByb3ZpZGVyLCBvcHRpb25zIH0gPSBjb25maWc7XG4gICAgICAgIGNvbnN0IGZhYXN0TW9kdWxlID0gYXdhaXQgZmFhc3QocHJvdmlkZXIsIHdvcmtsb2FkLmZ1bmNzLCBvcHRpb25zKTtcbiAgICAgICAgY29uc3QgeyByZXBldGl0aW9ucywgY29uY3VycmVuY3k6IHJlcGV0aXRpb25Db25jdXJyZW5jeSB9ID0gd29ya2xvYWQ7XG4gICAgICAgIGNvbnN0IGRvV29yayA9IHRocm90dGxlKHsgY29uY3VycmVuY3k6IHJlcGV0aXRpb25Db25jdXJyZW5jeSB9LCB3b3JrbG9hZC53b3JrKTtcbiAgICAgICAgY29uc3QgcmVzdWx0czogUHJvbWlzZTxXb3JrbG9hZEF0dHJpYnV0ZTxLPiB8IHZvaWQ+W10gPSBbXTtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCByZXBldGl0aW9uczsgaSsrKSB7XG4gICAgICAgICAgICByZXN1bHRzLnB1c2goZG9Xb3JrKGZhYXN0TW9kdWxlKS5jYXRjaChfID0+IHt9KSk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgcnYgPSAoYXdhaXQgUHJvbWlzZS5hbGwocmVzdWx0cykpLmZpbHRlcihyID0+IHIpIGFzIFdvcmtsb2FkQXR0cmlidXRlPEs+W107XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmNsZWFudXAoKTtcbiAgICAgICAgY29uc3QgY29zdFNuYXBzaG90ID0gYXdhaXQgZmFhc3RNb2R1bGUuY29zdFNuYXBzaG90KCk7XG4gICAgICAgIGNvbnN0IGV4dHJhTWV0cmljcyA9IHdvcmtsb2FkLnN1bW1hcml6ZShydik7XG4gICAgICAgIHJldHVybiB7IGNvc3RTbmFwc2hvdCwgY29uZmlnLCBleHRyYU1ldHJpY3MgfTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBFc3RpbWF0ZSB0aGUgY29zdCBvZiBhIHdvcmtsb2FkIHVzaW5nIG11bHRpcGxlIGNvbmZpZ3VyYXRpb25zIGFuZFxuICAgICAqIHByb3ZpZGVycy5cbiAgICAgKiBAcGFyYW0gdXNlcldvcmtsb2FkIC0gYSB7QGxpbmsgQ29zdEFuYWx5emVyLldvcmtsb2FkfSBvYmplY3Qgc3BlY2lmeWluZ1xuICAgICAqIHRoZSB3b3JrbG9hZCB0byBydW4gYW5kIGFkZGl0aW9uYWwgcGFyYW1ldGVycy5cbiAgICAgKiBAcmV0dXJucyBBIHByb21pc2UgZm9yIGEge0BsaW5rIENvc3RBbmFseXplci5SZXN1bHR9XG4gICAgICogQHB1YmxpY1xuICAgICAqIEByZW1hcmtzXG4gICAgICogSXQgY2FuIGJlIGRlY2VwdGl2ZWx5IGRpZmZpY3VsdCB0byBzZXQgb3B0aW1hbCBwYXJhbWV0ZXJzIGZvciBBV1MgTGFtYmRhXG4gICAgICogYW5kIHNpbWlsYXIgc2VydmljZXMuIE9uIHRoZSBzdXJmYWNlIHRoZXJlIGFwcGVhcnMgdG8gYmUgb25seSBvbmVcbiAgICAgKiBwYXJhbWV0ZXI6IG1lbW9yeSBzaXplLiBDaG9vc2luZyBtb3JlIG1lbW9yeSBhbHNvIGdpdmVzIG1vcmUgQ1BVXG4gICAgICogcGVyZm9ybWFuY2UsIGJ1dCBpdCdzIHVuY2xlYXIgaG93IG11Y2guIEl0J3MgYWxzbyB1bmNsZWFyIHdoZXJlIHNpbmdsZVxuICAgICAqIGNvcmUgcGVyZm9ybWFuY2Ugc3RvcHMgZ2V0dGluZyBiZXR0ZXIuIFRoZSB3b3JrbG9hZCBjb3N0IGFuYWx5emVyIHNvbHZlc1xuICAgICAqIHRoZXNlIHByb2JsZW1zIGJ5IG1ha2luZyBpdCBlYXN5IHRvIHJ1biBjb3N0IGV4cGVyaW1lbnRzLlxuICAgICAqIGBgYHRleHRcbiAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChBV1MpXG4gICAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg4pSM4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSQXG4gICAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg4pSM4pSA4pSA4pSA4pSA4pa24pSCIDEyOE1CIOKUglxuICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIOKUgiAgICAg4pSU4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSYXG4gICAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg4pSCICAgICDilIzilIDilIDilIDilIDilIDilIDilIDilJBcbiAgICAgKiAgICAgICAgICAgICAgICAgICAgICDilIzilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilJAgICAgIOKUnOKUgOKUgOKUgOKUgOKWtuKUgiAyNTZNQiDilIJcbiAgICAgKiAg4pSM4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSQICAgIOKUgiAgICAgICAgICAgICAgICAg4pSCICAgICDilIIgICAgIOKUlOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUmFxuICAgICAqICDilIIgICB3b3JrbG9hZCAgIOKUguKUgOKUgOKUgOKWtuKUgiAgICAgICAgICAgICAgICAg4pSCICAgICDilIIgICAgICAgIC4uLlxuICAgICAqICDilJTilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilJggICAg4pSCICAgICAgICAgICAgICAgICDilIIgICAgIOKUgiAgICAg4pSM4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSQXG4gICAgICogICAgICAgICAgICAgICAgICAgICAg4pSCICBjb3N0IGFuYWx5emVyICDilILilIDilIDilIDilIDilIDilLzilIDilIDilIDilIDilrbilIIzMDA4TUIg4pSCXG4gICAgICogIOKUjOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUkCAgICDilIIgICAgICAgICAgICAgICAgIOKUgiAgICAg4pSCICAgICDilJTilIDilIDilIDilIDilIDilIDilIDilJhcbiAgICAgKiAg4pSCY29uZmlndXJhdGlvbnPilILilIDilIDilIDilrbilIIgICAgICAgICAgICAgICAgIOKUgiAgICAg4pSCXG4gICAgICogIOKUlOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUmCAgICDilIIgICAgICAgICAgICAgICAgIOKUgiAgICAg4pSCICAgICAoR29vZ2xlKVxuICAgICAqICAgICAgICAgICAgICAgICAgICAgIOKUlOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUmCAgICAg4pSCICAgICDilIzilIDilIDilIDilIDilIDilIDilIDilJBcbiAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICDilJzilIDilIDilIDilIDilrbilIIgMTI4TUIg4pSCXG4gICAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg4pSCICAgICDilJTilIDilIDilIDilIDilIDilIDilIDilJhcbiAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICDilIIgICAgIOKUjOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUkFxuICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIOKUlOKUgOKUgOKUgOKUgOKWtuKUgiAyNTZNQiDilIJcbiAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICDilJTilIDilIDilIDilIDilIDilIDilIDilJhcbiAgICAgKiBgYGBcbiAgICAgKiBgY29zdEFuYWx5emVyYCBpcyB0aGUgZW50cnkgcG9pbnQuIEl0IGF1dG9tYXRpY2FsbHkgcnVucyB0aGlzIHdvcmtsb2FkXG4gICAgICogYWdhaW5zdCBtdWx0aXBsZSBjb25maWd1cmF0aW9ucyBpbiBwYXJhbGxlbC4gVGhlbiBpdCB1c2VzIGZhYXN0LmpzJyBjb3N0XG4gICAgICogc25hcHNob3QgbWVjaGFuaXNtIHRvIGF1dG9tYXRpY2FsbHkgZGV0ZXJtaW5lIHRoZSBwcmljZSBvZiBydW5uaW5nIHRoZVxuICAgICAqIHdvcmtsb2FkIHdpdGggZWFjaCBjb25maWd1cmF0aW9uLlxuICAgICAqXG4gICAgICogRXhhbXBsZTpcbiAgICAgKlxuICAgICAqIGBgYHR5cGVzY3JpcHRcbiAgICAgKiAvLyBmdW5jdGlvbnMudHNcbiAgICAgKiBleHBvcnQgZnVuY3Rpb24gcmFuZG9tTnVtYmVycyhuOiBudW1iZXIpIHtcbiAgICAgKiAgICAgbGV0IHN1bSA9IDA7XG4gICAgICogICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbjsgaSsrKSB7XG4gICAgICogICAgICAgICBzdW0gKz0gTWF0aC5yYW5kb20oKTtcbiAgICAgKiAgICAgfVxuICAgICAqICAgICByZXR1cm4gc3VtO1xuICAgICAqIH1cbiAgICAgKlxuICAgICAqIC8vIGNvc3QtYW5hbHl6ZXItZXhhbXBsZS50c1xuICAgICAqIGltcG9ydCB7IHdyaXRlRmlsZVN5bmMgfSBmcm9tIFwiZnNcIjtcbiAgICAgKiBpbXBvcnQgeyBDb3N0QW5hbHl6ZXIsIEZhYXN0TW9kdWxlIH0gZnJvbSBcImZhYXN0anNcIjtcbiAgICAgKiBpbXBvcnQgKiBhcyBmdW5jcyBmcm9tIFwiLi9mdW5jdGlvbnNcIjtcbiAgICAgKlxuICAgICAqIGFzeW5jIGZ1bmN0aW9uIHdvcmsoZmFhc3RNb2R1bGU6IEZhYXN0TW9kdWxlPHR5cGVvZiBmdW5jcz4pIHtcbiAgICAgKiAgICAgYXdhaXQgZmFhc3RNb2R1bGUuZnVuY3Rpb25zLnJhbmRvbU51bWJlcnMoMTAwMDAwMDAwKTtcbiAgICAgKiB9XG4gICAgICpcbiAgICAgKiBhc3luYyBmdW5jdGlvbiBtYWluKCkge1xuICAgICAqICAgICBjb25zdCByZXN1bHRzID0gYXdhaXQgQ29zdEFuYWx5emVyLmFuYWx5emUoeyBmdW5jcywgd29yayB9KTtcbiAgICAgKiAgICAgd3JpdGVGaWxlU3luYyhcImNvc3QuY3N2XCIsIHJlc3VsdHMuY3N2KCkpO1xuICAgICAqIH1cbiAgICAgKlxuICAgICAqIG1haW4oKTtcbiAgICAgKiBgYGBcbiAgICAgKlxuICAgICAqIEV4YW1wbGUgb3V0cHV0ICh0aGlzIGlzIHByaW50ZWQgdG8gYGNvbnNvbGUubG9nYCB1bmxlc3MgdGhlXG4gICAgICoge0BsaW5rIENvc3RBbmFseXplci5Xb3JrbG9hZC5zaWxlbnR9IGlzIGB0cnVlYCk6XG4gICAgICogYGBgdGV4dFxuICAgICAqICAg4pyUIGF3cyAxMjhNQiBxdWV1ZSAxNS4zODVzIDAuMjc0z4MgJDAuMDAwMDM5MjFcbiAgICAgKiAgIOKclCBhd3MgMTkyTUIgcXVldWUgMTAuMDI0cyAwLjIzMM+DICQwLjAwMDAzNTc2XG4gICAgICogICDinJQgYXdzIDI1Nk1CIHF1ZXVlIDguMDc3cyAwLjIwNM+DICQwLjAwMDAzNzc5XG4gICAgICogICAgICDilrIgICAg4payICAgICDilrIgICAgIOKWsiAgICAgICDilrIgICAgICAgIOKWslxuICAgICAqICAgICAg4pSCICAgIOKUgiAgICAg4pSCICAgICDilIIgICAgICAg4pSCICAgICAgICDilIJcbiAgICAgKiAgcHJvdmlkZXIg4pSCICAgIG1vZGUgICDilIIgICAgIHN0ZGV2ICAgICBhdmVyYWdlXG4gICAgICogICAgICAgICAgIOKUgiAgICAgICAgICAg4pSCICAgZXhlY3V0aW9uICBlc3RpbWF0ZWRcbiAgICAgKiAgICAgICAgIG1lbW9yeSAgICAgICAg4pSCICAgICB0aW1lICAgICAgIGNvc3RcbiAgICAgKiAgICAgICAgICBzaXplICAgICAgICAg4pSCXG4gICAgICogICAgICAgICAgICAgICAgIGF2ZXJhZ2UgY2xvdWRcbiAgICAgKiAgICAgICAgICAgICAgICAgZXhlY3V0aW9uIHRpbWVcbiAgICAgKiBgYGBcbiAgICAgKlxuICAgICAqIFRoZSBvdXRwdXQgbGlzdHMgdGhlIHByb3ZpZGVyLCBtZW1vcnkgc2l6ZSwgKHtAbGluayBDb21tb25PcHRpb25zLm1vZGV9KSxcbiAgICAgKiBhdmVyYWdlIHRpbWUgb2YgYSBzaW5nbGUgZXhlY3V0aW9uIG9mIHRoZSB3b3JrbG9hZCwgdGhlIHN0YW5kYXJkXG4gICAgICogZGV2aWF0aW9uIChpbiBzZWNvbmRzKSBvZiB0aGUgZXhlY3V0aW9uIHRpbWUsIGFuZCBhdmVyYWdlIGVzdGltYXRlZCBjb3N0XG4gICAgICogZm9yIGEgc2luZ2xlIHJ1biBvZiB0aGUgd29ya2xvYWQuXG4gICAgICpcbiAgICAgKiBUaGUgXCJleGVjdXRpb24gdGltZVwiIHJlZmVyZW5jZWQgaGVyZSBpcyBub3Qgd2FsbCBjbG9jayB0aW1lLCBidXQgcmF0aGVyXG4gICAgICogZXhlY3V0aW9uIHRpbWUgaW4gdGhlIGNsb3VkIGZ1bmN0aW9uLiBUaGUgZXhlY3V0aW9uIHRpbWUgZG9lcyBub3QgaW5jbHVkZVxuICAgICAqIGFueSB0aW1lIHRoZSB3b3JrbG9hZCBzcGVuZHMgd2FpdGluZyBsb2NhbGx5LiBJZiB0aGUgd29ya2xvYWQgaW52b2tlc1xuICAgICAqIG11bHRpcGxlIGNsb3VkIGZ1bmN0aW9ucywgdGhlaXIgZXhlY3V0aW9uIHRpbWVzIHdpbGwgYmUgc3VtbWVkIGV2ZW4gaWZcbiAgICAgKiB0aGV5IGhhcHBlbiBjb25jdXJyZW50bHkuIFRoaXMgZW5zdXJlcyB0aGUgZXhlY3V0aW9uIHRpbWUgYW5kIGNvc3QgYXJlXG4gICAgICogYWxpZ25lZC5cbiAgICAgKi9cbiAgICBleHBvcnQgYXN5bmMgZnVuY3Rpb24gYW5hbHl6ZTxUIGV4dGVuZHMgb2JqZWN0LCBBIGV4dGVuZHMgc3RyaW5nPihcbiAgICAgICAgdXNlcldvcmtsb2FkOiBXb3JrbG9hZDxULCBBPlxuICAgICkge1xuICAgICAgICBjb25zdCBzY2hlZHVsZUVzdGltYXRlID0gdGhyb3R0bGU8XG4gICAgICAgICAgICBbUmVxdWlyZWQ8V29ya2xvYWQ8VCwgQT4+LCBDb25maWd1cmF0aW9uXSxcbiAgICAgICAgICAgIEVzdGltYXRlPEE+XG4gICAgICAgID4oXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgY29uY3VycmVuY3k6IDgsXG4gICAgICAgICAgICAgICAgcmF0ZTogNCxcbiAgICAgICAgICAgICAgICBidXJzdDogMSxcbiAgICAgICAgICAgICAgICByZXRyeTogM1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGVzdGltYXRlXG4gICAgICAgICk7XG5cbiAgICAgICAgY29uc3QgeyBjb25jdXJyZW5jeSA9IHdvcmtsb2FkRGVmYXVsdHMuY29uY3VycmVuY3kgfSA9IHVzZXJXb3JrbG9hZDtcbiAgICAgICAgY29uc3Qgd29ya2xvYWQ6IFJlcXVpcmVkPFdvcmtsb2FkPFQsIEE+PiA9IHtcbiAgICAgICAgICAgIC4uLndvcmtsb2FkRGVmYXVsdHMsXG4gICAgICAgICAgICAuLi51c2VyV29ya2xvYWQsXG4gICAgICAgICAgICB3b3JrOiB0aHJvdHRsZSh7IGNvbmN1cnJlbmN5IH0sIHVzZXJXb3JrbG9hZC53b3JrKVxuICAgICAgICB9O1xuXG4gICAgICAgIGNvbnN0IHsgY29uZmlndXJhdGlvbnMgfSA9IHdvcmtsb2FkO1xuICAgICAgICBjb25zdCBwcm9taXNlcyA9IGNvbmZpZ3VyYXRpb25zLm1hcChjb25maWcgPT4gc2NoZWR1bGVFc3RpbWF0ZSh3b3JrbG9hZCwgY29uZmlnKSk7XG5cbiAgICAgICAgY29uc3QgZm9ybWF0ID0gd29ya2xvYWQuZm9ybWF0IHx8IGRlZmF1bHRGb3JtYXQ7XG5cbiAgICAgICAgY29uc3QgcmVuZGVyZXIgPSB3b3JrbG9hZC5zaWxlbnQgPyBcInNpbGVudFwiIDogXCJkZWZhdWx0XCI7XG5cbiAgICAgICAgY29uc3QgbGlzdCA9IG5ldyBMaXN0cihcbiAgICAgICAgICAgIHByb21pc2VzLm1hcCgocHJvbWlzZSwgaSkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IHsgcHJvdmlkZXIsIG9wdGlvbnMgfSA9IGNvbmZpZ3VyYXRpb25zW2ldO1xuICAgICAgICAgICAgICAgIGNvbnN0IHsgbWVtb3J5U2l6ZSwgbW9kZSB9ID0gb3B0aW9ucztcblxuICAgICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgICAgIHRpdGxlOiBgJHtwcm92aWRlcn0gJHttZW1vcnlTaXplfU1CICR7bW9kZX1gLFxuICAgICAgICAgICAgICAgICAgICB0YXNrOiBhc3luYyAoXzogYW55LCB0YXNrOiBMaXN0ci5MaXN0clRhc2tXcmFwcGVyKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCB7IGNvc3RTbmFwc2hvdCwgZXh0cmFNZXRyaWNzIH0gPSBhd2FpdCBwcm9taXNlO1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgdG90YWwgPSAoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29zdFNuYXBzaG90LnRvdGFsKCkgLyB3b3JrbG9hZC5yZXBldGl0aW9uc1xuICAgICAgICAgICAgICAgICAgICAgICAgKS50b0ZpeGVkKDgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgeyBlcnJvcnMgfSA9IGNvc3RTbmFwc2hvdC5zdGF0cztcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHsgZXhlY3V0aW9uVGltZSB9ID0gY29zdFNuYXBzaG90LnN0YXRzO1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgbWVzc2FnZSA9IGAke3BzKGV4ZWN1dGlvblRpbWUubWVhbil9cyAke3BzKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4ZWN1dGlvblRpbWUuc3RkZXZcbiAgICAgICAgICAgICAgICAgICAgICAgICl9z4MgJCR7dG90YWx9YDtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGVyck1lc3NhZ2UgPSBlcnJvcnMgPiAwID8gYCAoJHtlcnJvcnN9IGVycm9ycylgIDogXCJcIjtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGV4dHJhID0ga2V5c09mKGV4dHJhTWV0cmljcylcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAubWFwKGsgPT4gZm9ybWF0KGssIGV4dHJhTWV0cmljc1trXSkpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLmpvaW4oXCIgXCIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGFzay50aXRsZSA9IGAke3Rhc2sudGl0bGV9ICR7bWVzc2FnZX0ke2Vyck1lc3NhZ2V9ICR7ZXh0cmF9YDtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9KSxcbiAgICAgICAgICAgIHsgY29uY3VycmVudDogOCwgbm9uVFRZUmVuZGVyZXI6IHJlbmRlcmVyLCByZW5kZXJlciB9XG4gICAgICAgICk7XG5cbiAgICAgICAgYXdhaXQgbGlzdC5ydW4oKTtcbiAgICAgICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IFByb21pc2UuYWxsKHByb21pc2VzKTtcbiAgICAgICAgcmVzdWx0cy5zb3J0KFxuICAgICAgICAgICAgKGEsIGIpID0+XG4gICAgICAgICAgICAgICAgYS5jb3N0U25hcHNob3Qub3B0aW9ucy5tZW1vcnlTaXplISAtIGIuY29zdFNuYXBzaG90Lm9wdGlvbnMubWVtb3J5U2l6ZSFcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIG5ldyBSZXN1bHQod29ya2xvYWQsIHJlc3VsdHMpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENvc3QgYW5hbHl6ZXIgcmVzdWx0cyBmb3IgZWFjaCB3b3JrbG9hZCBhbmQgY29uZmlndXJhdGlvbi5cbiAgICAgKiBAcmVtYXJrc1xuICAgICAqIFRoZSBgZXN0aW1hdGVzYCBwcm9wZXJ0eSBoYXMgdGhlIGNvc3QgZXN0aW1hdGVzIGZvciBlYWNoIGNvbmZpZ3VyYXRpb24uXG4gICAgICogU2VlIHtAbGluayBDb3N0QW5hbHl6ZXIuRXN0aW1hdGV9LlxuICAgICAqIEBwdWJsaWNcbiAgICAgKi9cbiAgICBleHBvcnQgY2xhc3MgUmVzdWx0PFQgZXh0ZW5kcyBvYmplY3QsIEEgZXh0ZW5kcyBzdHJpbmc+IHtcbiAgICAgICAgLyoqIEBpbnRlcm5hbCAqL1xuICAgICAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgICAgIC8qKiBUaGUgd29ya2xvYWQgYW5hbHl6ZWQuICovXG4gICAgICAgICAgICByZWFkb25seSB3b3JrbG9hZDogUmVxdWlyZWQ8V29ya2xvYWQ8VCwgQT4+LFxuICAgICAgICAgICAgLyoqXG4gICAgICAgICAgICAgKiBDb3N0IGVzdGltYXRlcyBmb3IgZWFjaCBjb25maWd1cmF0aW9uIG9mIHRoZSB3b3JrbG9hZC4gU2VlXG4gICAgICAgICAgICAgKiB7QGxpbmsgQ29zdEFuYWx5emVyLkVzdGltYXRlfS5cbiAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgcmVhZG9ubHkgZXN0aW1hdGVzOiBFc3RpbWF0ZTxBPltdXG4gICAgICAgICkge31cblxuICAgICAgICAvKipcbiAgICAgICAgICogQ29tbWEtc2VwYXJhdGVkIG91dHB1dCBvZiBjb3N0IGFuYWx5emVyLiBPbmUgbGluZSBwZXIgY29zdCBhbmFseXplclxuICAgICAgICAgKiBjb25maWd1cmF0aW9uLlxuICAgICAgICAgKiBAcmVtYXJrc1xuICAgICAgICAgKiBUaGUgY29sdW1ucyBhcmU6XG4gICAgICAgICAqXG4gICAgICAgICAqIC0gYG1lbW9yeWA6IFRoZSBtZW1vcnkgc2l6ZSBhbGxvY2F0ZWQuXG4gICAgICAgICAqXG4gICAgICAgICAqIC0gYGNsb3VkYDogVGhlIGNsb3VkIHByb3ZpZGVyLlxuICAgICAgICAgKlxuICAgICAgICAgKiAtIGBtb2RlYDogU2VlIHtAbGluayBDb21tb25PcHRpb25zLm1vZGV9LlxuICAgICAgICAgKlxuICAgICAgICAgKiAtIGBvcHRpb25zYDogQSBzdHJpbmcgc3VtbWFyaXppbmcgb3RoZXIgZmFhc3QuanMgb3B0aW9ucyBhcHBsaWVkIHRvIHRoZVxuICAgICAgICAgKiAgIGB3b3JrbG9hZGAuIFNlZSB7QGxpbmsgQ29tbW9uT3B0aW9uc30uXG4gICAgICAgICAqXG4gICAgICAgICAqIC0gYGNvbXBsZXRlZGA6IE51bWJlciBvZiByZXBldGl0aW9ucyB0aGF0IHN1Y2Nlc3NmdWxseSBjb21wbGV0ZWQuXG4gICAgICAgICAqXG4gICAgICAgICAqIC0gYGVycm9yc2A6IE51bWJlciBvZiBpbnZvY2F0aW9ucyB0aGF0IGZhaWxlZC5cbiAgICAgICAgICpcbiAgICAgICAgICogLSBgcmV0cmllc2A6IE51bWJlciBvZiByZXRyaWVzIHRoYXQgd2VyZSBhdHRlbXB0ZWQuXG4gICAgICAgICAqXG4gICAgICAgICAqIC0gYGNvc3RgOiBUaGUgYXZlcmFnZSBjb3N0IG9mIGV4ZWN1dGluZyB0aGUgd29ya2xvYWQgb25jZS5cbiAgICAgICAgICpcbiAgICAgICAgICogLSBgZXhlY3V0aW9uVGltZWA6IHRoZSBhZ2dyZWdhdGUgdGltZSBzcGVudCBleGVjdXRpbmcgb24gdGhlIHByb3ZpZGVyIGZvclxuICAgICAgICAgKiAgIGFsbCBjbG91ZCBmdW5jdGlvbiBpbnZvY2F0aW9ucyBpbiB0aGUgd29ya2xvYWQuIFRoaXMgaXMgYXZlcmFnZWQgYWNyb3NzXG4gICAgICAgICAqICAgcmVwZXRpdGlvbnMuXG4gICAgICAgICAqXG4gICAgICAgICAqIC0gYGV4ZWN1dGlvblRpbWVTdGRldmA6IFRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgYGV4ZWN1dGlvblRpbWVgLlxuICAgICAgICAgKlxuICAgICAgICAgKiAtIGBiaWxsZWRUaW1lYDogdGhlIHNhbWUgYXMgYGV4ZWN0aW9uVGltZWAsIGV4Y2VwdCByb3VuZGVkIHVwIHRvIHRoZSBuZXh0XG4gICAgICAgICAqICAgMTAwbXMgZm9yIGVhY2ggaW52b2NhdGlvbi4gVXN1YWxseSB2ZXJ5IGNsb3NlIHRvIGBleGVjdXRpb25UaW1lYC5cbiAgICAgICAgICovXG4gICAgICAgIGNzdigpIHtcbiAgICAgICAgICAgIGNvbnN0IGF0dHJpYnV0ZXMgPSBuZXcgU2V0PEE+KCk7XG4gICAgICAgICAgICB0aGlzLmVzdGltYXRlcy5mb3JFYWNoKGVzdCA9PlxuICAgICAgICAgICAgICAgIGtleXNPZihlc3QuZXh0cmFNZXRyaWNzKS5mb3JFYWNoKGtleSA9PiBhdHRyaWJ1dGVzLmFkZChrZXkpKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGNvbnN0IGNvbHVtbnMgPSBbXG4gICAgICAgICAgICAgICAgXCJtZW1vcnlcIixcbiAgICAgICAgICAgICAgICBcImNsb3VkXCIsXG4gICAgICAgICAgICAgICAgXCJtb2RlXCIsXG4gICAgICAgICAgICAgICAgXCJvcHRpb25zXCIsXG4gICAgICAgICAgICAgICAgXCJjb21wbGV0ZWRcIixcbiAgICAgICAgICAgICAgICBcImVycm9yc1wiLFxuICAgICAgICAgICAgICAgIFwicmV0cmllc1wiLFxuICAgICAgICAgICAgICAgIFwiY29zdFwiLFxuICAgICAgICAgICAgICAgIFwiZXhlY3V0aW9uVGltZVwiLFxuICAgICAgICAgICAgICAgIFwiZXhlY3V0aW9uVGltZVN0ZGV2XCIsXG4gICAgICAgICAgICAgICAgXCJiaWxsZWRUaW1lXCIsXG4gICAgICAgICAgICAgICAgLi4uYXR0cmlidXRlc1xuICAgICAgICAgICAgXTtcbiAgICAgICAgICAgIGxldCBydiA9IGNvbHVtbnMuam9pbihcIixcIikgKyBcIlxcblwiO1xuXG4gICAgICAgICAgICB0aGlzLmVzdGltYXRlcy5mb3JFYWNoKCh7IGNvc3RTbmFwc2hvdCwgZXh0cmFNZXRyaWNzIH0pID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCB7IG1lbW9yeVNpemUsIG1vZGUsIC4uLnJlc3QgfSA9IGNvc3RTbmFwc2hvdC5vcHRpb25zO1xuICAgICAgICAgICAgICAgIGNvbnN0IG9wdGlvbnMgPSBgXCIke2luc3BlY3QocmVzdCkucmVwbGFjZSgnXCInLCAnXCJcIicpfVwiYDtcbiAgICAgICAgICAgICAgICBjb25zdCB7IGNvbXBsZXRlZCwgZXJyb3JzLCByZXRyaWVzLCBleGVjdXRpb25UaW1lLCBlc3RpbWF0ZWRCaWxsZWRUaW1lIH0gPVxuICAgICAgICAgICAgICAgICAgICBjb3N0U25hcHNob3Quc3RhdHM7XG4gICAgICAgICAgICAgICAgY29uc3QgY29zdCA9IChjb3N0U25hcHNob3QudG90YWwoKSAvIHRoaXMud29ya2xvYWQucmVwZXRpdGlvbnMpLnRvRml4ZWQoXG4gICAgICAgICAgICAgICAgICAgIDhcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIGNvbnN0IGZvcm1hdHRlciA9IHRoaXMud29ya2xvYWQuZm9ybWF0Q1NWIHx8IGRlZmF1bHRGb3JtYXRDU1Y7XG5cbiAgICAgICAgICAgICAgICBjb25zdCBtZXRyaWNzOiB7IFthdHRyIGluIHN0cmluZ106IHN0cmluZyB9ID0ge307XG4gICAgICAgICAgICAgICAgZm9yIChjb25zdCBhdHRyIG9mIGF0dHJpYnV0ZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgbWV0cmljc1thdHRyXSA9IGZvcm1hdHRlcihhdHRyLCBleHRyYU1ldHJpY3NbYXR0cl0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjb25zdCByb3cgPSB7XG4gICAgICAgICAgICAgICAgICAgIG1lbW9yeTogbWVtb3J5U2l6ZSxcbiAgICAgICAgICAgICAgICAgICAgY2xvdWQ6IGNvc3RTbmFwc2hvdC5wcm92aWRlcixcbiAgICAgICAgICAgICAgICAgICAgbW9kZSxcbiAgICAgICAgICAgICAgICAgICAgb3B0aW9ucyxcbiAgICAgICAgICAgICAgICAgICAgY29tcGxldGVkLFxuICAgICAgICAgICAgICAgICAgICBlcnJvcnMsXG4gICAgICAgICAgICAgICAgICAgIHJldHJpZXMsXG4gICAgICAgICAgICAgICAgICAgIGNvc3Q6IGAkJHtjb3N0fWAsXG4gICAgICAgICAgICAgICAgICAgIGV4ZWN1dGlvblRpbWU6IHBzKGV4ZWN1dGlvblRpbWUubWVhbiksXG4gICAgICAgICAgICAgICAgICAgIGV4ZWN1dGlvblRpbWVTdGRldjogcHMoZXhlY3V0aW9uVGltZS5zdGRldiksXG4gICAgICAgICAgICAgICAgICAgIGJpbGxlZFRpbWU6IHBzKGVzdGltYXRlZEJpbGxlZFRpbWUubWVhbiksXG4gICAgICAgICAgICAgICAgICAgIC4uLm1ldHJpY3NcbiAgICAgICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICAgICAgcnYgKz0ga2V5c09mKHJvdylcbiAgICAgICAgICAgICAgICAgICAgLm1hcChrID0+IFN0cmluZyhyb3dba10pKVxuICAgICAgICAgICAgICAgICAgICAuam9pbihcIixcIik7XG4gICAgICAgICAgICAgICAgcnYgKz0gXCJcXG5cIjtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0dXJuIHJ2O1xuICAgICAgICB9XG4gICAgfVxufVxuIl19
\No newline at end of file