1 | ;
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.CostAnalyzer = exports.CostSnapshot = exports.CostMetric = void 0;
|
4 | const Listr = require("listr");
|
5 | const util_1 = require("util");
|
6 | const index_1 = require("../index");
|
7 | const shared_1 = require("./shared");
|
8 | const 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 | */
|
14 | class 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 | }
|
58 | exports.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 | */
|
161 | class 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 | }
|
247 | exports.CostSnapshot = CostSnapshot;
|
248 | /**
|
249 | * Analyze the cost of a workload across many provider configurations.
|
250 | * @public
|
251 | */
|
252 | var 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 |