tfjs-evolution 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,424 @@
1
+ import { CustomMobileNet } from "./custom-mobilenet";
2
+ import * as tf from '@tensorflow/tfjs';
3
+ import { capture } from '../utils/tf';
4
+ import { Util } from "../utils/util";
5
+ import * as tfvis from '@tensorflow/tfjs-vis';
6
+ const VALIDATION_FRACTION = 0.15;
7
+ /**
8
+ * Receives a Metadata object and fills in the optional fields such as timeStamp
9
+ * @param data a Metadata object
10
+ */
11
+ const fillMetadata = (data) => {
12
+ // util.assert(typeof data.tfjsVersion === 'string', () => `metadata.tfjsVersion is invalid`);
13
+ // data.packageVersion = data.packageVersion || version;
14
+ data.packageName = data.packageName || '@teachablemachine/image';
15
+ data.timeStamp = data.timeStamp || new Date().toISOString();
16
+ data.userMetadata = data.userMetadata || {};
17
+ data.modelName = data.modelName || 'untitled';
18
+ data.labels = data.labels || [];
19
+ // data.imageSize = data.imageSize || IMAGE_SIZE;
20
+ return data;
21
+ };
22
+ export class TeachableMobileNet extends CustomMobileNet {
23
+ // Array of all the examples collected.
24
+ /**
25
+ It is static since all the instance will share the same features, for saving memory and time.
26
+ The idea is avoiding restoring the features individually and having the recalculate them for every new
27
+ individuals.
28
+ */
29
+ static { this.examples = []; }
30
+ // Number of total samples
31
+ static { this.totalSamples = 0; }
32
+ static { this.classes_names = []; }
33
+ constructor() {
34
+ super();
35
+ this.classes = [];
36
+ this.createHead();
37
+ }
38
+ /**
39
+ * This method will return the head, the trainable part, the part under evolution.
40
+ */
41
+ getHead() {
42
+ return this.trainingModel;
43
+ }
44
+ /**
45
+ * Create the head for transfer learning.
46
+ * This is the trainable section of the transfer learning.
47
+ */
48
+ createHead() {
49
+ const inputSize = TeachableMobileNet.getinputShape();
50
+ this.trainingModel = tf.sequential({
51
+ layers: [
52
+ tf.layers.dense({
53
+ inputShape: [inputSize],
54
+ units: 100,
55
+ activation: 'relu',
56
+ useBias: true
57
+ }),
58
+ tf.layers.dense({
59
+ useBias: false,
60
+ activation: 'softmax',
61
+ units: TeachableMobileNet.classes_names.length
62
+ })
63
+ ]
64
+ });
65
+ const optimizer = tf.train.adam();
66
+ // const optimizer = tf.train.rmsprop(params.learningRate);
67
+ this.trainingModel.compile({
68
+ optimizer,
69
+ // loss: 'binaryCrossentropy',
70
+ loss: 'categoricalCrossentropy',
71
+ metrics: ['accuracy']
72
+ });
73
+ }
74
+ async train() {
75
+ const trainingSurface = { name: 'Loss and MSE', tab: 'Training' };
76
+ const dataset = TeachableMobileNet.convertToTfDataset();
77
+ //Salving a copy of the validation dataset, for later
78
+ TeachableMobileNet.validationDataset = dataset.validationDataset;
79
+ // console.log("Dataset for training: ", dataset.trainDataset);
80
+ const trainData = dataset.trainDataset.batch(30);
81
+ const validationData = dataset.validationDataset.batch(10);
82
+ // this.createHead();
83
+ const callbacks = [
84
+ // Show on a tfjs-vis visor the loss and accuracy values at the end of each epoch.
85
+ tfvis.show.fitCallbacks(trainingSurface, ['loss', 'acc', "val_loss", "val_acc"], {
86
+ callbacks: ['onEpochEnd'],
87
+ }),
88
+ {},
89
+ ];
90
+ const history = await this.trainingModel.fitDataset(trainData, {
91
+ epochs: 100,
92
+ validationData,
93
+ callbacks
94
+ }).then((info) => {
95
+ console.log('Precisão final', info.history.val_acc[info.history.acc.length - 1]);
96
+ });
97
+ // await this.accuracy_per_class();
98
+ // console.log("History: ", history.history.acc);
99
+ // await this.trainingModel.fit(this.featureX, this.target, {})
100
+ }
101
+ async accuracy_per_class(confusion_matrix_recipient) {
102
+ /**Calculating Accuracy per class */
103
+ const accuracyperclass = await this.calculateAccuracyPerClass(TeachableMobileNet.validationDataset);
104
+ // console.log("Accuracy per class: ", accuracyperclass);
105
+ //Confusion matrix
106
+ // Calling tf.confusionMatrix() method
107
+ const output = tf.math.confusionMatrix(accuracyperclass.reference, accuracyperclass.predictions, TeachableMobileNet.classes_names.length);
108
+ // Printing output
109
+ output.print();
110
+ const confusion_matrix = output.dataSync();
111
+ // console.log(confusion_matrix);
112
+ // console.log(confusion_matrix[TeachableMobileNet.classes_names.length + TeachableMobileNet.classes_names.length]);
113
+ const accuracy = [];
114
+ for (let i = 0; i < TeachableMobileNet.classes_names.length; i++) {
115
+ accuracy.push(confusion_matrix[TeachableMobileNet.classes_names.length * i + i] / TeachableMobileNet.numValidation);
116
+ }
117
+ console.log("Accuracy per class: ", accuracy);
118
+ for (let i = 0; i < TeachableMobileNet.classes_names.length; i++) {
119
+ confusion_matrix_recipient.push([]);
120
+ for (let j = 0; j < TeachableMobileNet.classes_names.length; j++) {
121
+ confusion_matrix_recipient[i].push([]);
122
+ confusion_matrix_recipient[i][j] = confusion_matrix[TeachableMobileNet.classes_names.length * i + j] / TeachableMobileNet.numValidation;
123
+ confusion_matrix_recipient[i][j] = (confusion_matrix_recipient[i][j].toFixed(2)) * 100;
124
+ }
125
+ // accuracy.push(confusion_matrix[TeachableMobileNet.classes_names.length*i+ i]/TeachableMobileNet.numValidation)
126
+ }
127
+ console.log("Confusion matrix as a matrix");
128
+ console.log(confusion_matrix_recipient);
129
+ return accuracy.map((elem) => elem.toFixed(2) * 100);
130
+ }
131
+ async loadImages(number_of_species, classes_names, options) {
132
+ TeachableMobileNet.classes_names = classes_names;
133
+ await this.add_species(number_of_species, options);
134
+ }
135
+ async add_species(number_of_species, options) {
136
+ //Loading feature model, used to create features from images
137
+ // await this.loadFeatureModel();
138
+ for (let i = 0; i < TeachableMobileNet.classes_names.length; i++) {
139
+ // this.add_images(this.classes_names[i], number_of_species, options);
140
+ }
141
+ }
142
+ /**
143
+ *
144
+ * @param name - name of the class receiving an example
145
+ * @param number_of_species - how many images to add
146
+ * @param options - details on the location of the images
147
+ */
148
+ async add_images(name, number_of_species, options) {
149
+ const class_add = [];
150
+ for (let i = 0; i < number_of_species; i++) {
151
+ // class_add.push(`${options.base}/${name}/${options.file_name} ${i}.${options.file_extension}`);
152
+ //Uploading images
153
+ const cake = new Image();
154
+ // cake.src = `${options.base}/${name}/${options.file_name} ${i}.${options.file_extension}`;
155
+ cake.height = 224;
156
+ cake.width = 224;
157
+ cake.src = "./assets/dataset/Can%C3%A1rio-da-Terra/image%200.jpeg";
158
+ // console.log("Image location: ", cake.src )
159
+ await new Promise((resolve, reject) => {
160
+ cake.onload = () => {
161
+ //Finding the correspondent index of the class with name given
162
+ const index = TeachableMobileNet.classes_names.findIndex((elem) => elem === name);
163
+ // this.addExample(index, cake);
164
+ resolve();
165
+ };
166
+ cake.onerror = (error) => {
167
+ // Handle error if the image fails to load
168
+ reject(error);
169
+ };
170
+ });
171
+ }
172
+ // this.classes.push({name: name, images: class_add})
173
+ }
174
+ /**
175
+ * This method will transform images into tensors
176
+ * @param number_of_classes - number of classes
177
+ * @param classes_names - name of each class
178
+ */
179
+ async createTensors(number_of_classes, classes_names) {
180
+ let output = [];
181
+ /** There is a function on TensorFlow.js that also does that */
182
+ const signatures = new Util().identityMatrix(number_of_classes);
183
+ for (let i = 0; i < number_of_classes; i++) {
184
+ this.classes[i].signature = signatures[i];
185
+ this.classes[i].name = classes_names[i];
186
+ for (let j = 0; j < this.classes[i].images.length; j++) {
187
+ }
188
+ }
189
+ }
190
+ /**
191
+ * Add a sample of data under the provided className
192
+ * @param className the classification this example belongs to
193
+ * @param sample the image / tensor that belongs in this classification
194
+ */
195
+ // public async addExample(className: number, sample: HTMLCanvasElement | tf.Tensor) {
196
+ static async addExample(className, name, sample) {
197
+ // console.log("Adding a new example...")
198
+ const cap = isTensor(sample) ? sample : capture(sample);
199
+ //Getting the features
200
+ const example = this.truncatedModel.predict(cap);
201
+ // console.log("Shape after feature extraction: ", example.shape)
202
+ const activation = example.dataSync();
203
+ //Very important to clean the memory aftermath, it makes the difference
204
+ cap.dispose();
205
+ example.dispose();
206
+ // //Accessing the instance variable, not the local ones
207
+ // // save samples of each class separately
208
+ if (!TeachableMobileNet.examples[className])
209
+ //and an empty array, make sure there is not empty elements.
210
+ //it will create issue when transforming to tensors
211
+ TeachableMobileNet.examples[className] = [];
212
+ if (!TeachableMobileNet.classes_names[className])
213
+ //Saving the lable when it first appears
214
+ TeachableMobileNet.classes_names[className] = name;
215
+ TeachableMobileNet.examples[className].push(activation);
216
+ // // increase our sample counter
217
+ TeachableMobileNet.totalSamples++;
218
+ }
219
+ /**
220
+ * process the current examples provided to calculate labels and format
221
+ * into proper tf.data.Dataset
222
+ */
223
+ static prepare() {
224
+ for (const classes in TeachableMobileNet.examples) {
225
+ if (classes.length === 0) {
226
+ throw new Error('Add some examples before training');
227
+ }
228
+ }
229
+ const datasets = this.convertToTfDataset();
230
+ this.trainDataset = datasets.trainDataset;
231
+ this.validationDataset = datasets.validationDataset;
232
+ }
233
+ prepareDataset() {
234
+ for (let i = 0; i < TeachableMobileNet.numClasses; i++) {
235
+ //Different from the original implementation of TM, mine is using example as static.
236
+ //The goal is saving memory by using a single instance of the variable
237
+ TeachableMobileNet.examples[i] = [];
238
+ }
239
+ }
240
+ /**
241
+ * Process the examples by first shuffling randomly per class, then adding
242
+ * one-hot labels, then splitting into training/validation datsets, and finally
243
+ * sorting one last time
244
+ */
245
+ static convertToTfDataset() {
246
+ // first shuffle each class individually
247
+ // TODO: we could basically replicate this by insterting randomly
248
+ for (let i = 0; i < TeachableMobileNet.examples.length; i++) {
249
+ TeachableMobileNet.examples[i] = fisherYates(TeachableMobileNet.examples[i], this.seed);
250
+ }
251
+ // then break into validation and test datasets
252
+ let trainDataset = [];
253
+ let validationDataset = [];
254
+ // for each class, add samples to train and validation dataset
255
+ for (let i = 0; i < TeachableMobileNet.examples.length; i++) {
256
+ // console.log("Number of classes: ", TeachableMobileNet.classes_names.length);
257
+ const y = flatOneHot(i, TeachableMobileNet.classes_names.length);
258
+ const classLength = TeachableMobileNet.examples[i].length;
259
+ // console.log("Number of elements per class: ", classLength);
260
+ const numValidation = Math.ceil(VALIDATION_FRACTION * classLength);
261
+ const numTrain = classLength - numValidation;
262
+ this.numValidation = numValidation;
263
+ /**It is visiting per class, thus, it is possible to fix y, the target label */
264
+ const classTrain = this.examples[i].slice(0, numTrain).map((dataArray) => {
265
+ return { data: dataArray, label: y };
266
+ });
267
+ const classValidation = this.examples[i].slice(numTrain).map((dataArray) => {
268
+ return { data: dataArray, label: y };
269
+ });
270
+ trainDataset = trainDataset.concat(classTrain);
271
+ validationDataset = validationDataset.concat(classValidation);
272
+ }
273
+ // console.log("Training element: ", trainDataset[trainDataset.length-1])
274
+ // console.log("Training length: ", trainDataset.length)
275
+ // console.log("validation length: ", validationDataset.length);
276
+ // finally shuffle both train and validation datasets
277
+ trainDataset = fisherYates(trainDataset, this.seed);
278
+ validationDataset = fisherYates(validationDataset, this.seed);
279
+ const trainX = tf.data.array(trainDataset.map(sample => sample.data));
280
+ const validationX = tf.data.array(validationDataset.map(sample => sample.data));
281
+ const trainY = tf.data.array(trainDataset.map(sample => sample.label));
282
+ const validationY = tf.data.array(validationDataset.map(sample => sample.label));
283
+ // return tf.data dataset objects
284
+ return {
285
+ trainDataset: tf.data.zip({ xs: trainX, ys: trainY }),
286
+ validationDataset: tf.data.zip({ xs: validationX, ys: validationY })
287
+ };
288
+ }
289
+ datasetForEvaluation() {
290
+ }
291
+ async evaluate() {
292
+ if (!TeachableMobileNet.feature_aux) {
293
+ const features = [];
294
+ const targets = [];
295
+ for (let i = 0; i < TeachableMobileNet.examples.length; i++) {
296
+ const y = flatOneHot(i, TeachableMobileNet.classes_names.length);
297
+ //For class i, push all the examples.
298
+ TeachableMobileNet.examples[i].forEach((elemn) => {
299
+ //Pushing the target signature
300
+ targets.push(y);
301
+ //Pushing features
302
+ features.push(elemn);
303
+ });
304
+ }
305
+ TeachableMobileNet.feature_aux = tf.tensor(features);
306
+ TeachableMobileNet.target_aux = tf.tensor(targets);
307
+ }
308
+ const aux = this.trainingModel.evaluate(TeachableMobileNet.feature_aux, TeachableMobileNet.target_aux);
309
+ return aux[1].dataSync()[0];
310
+ }
311
+ // async evaluate(){
312
+ // const features: any=[];
313
+ // const targets: any=[];
314
+ // for (let i = 0; i < TeachableMobileNet.examples.length; i++) {
315
+ // const y = flatOneHot(i, TeachableMobileNet.classes_names.length);
316
+ // //For class i, push all the examples.
317
+ // TeachableMobileNet.examples[i].forEach((elemn)=>{
318
+ // //Pushing the target signature
319
+ // targets.push(y);
320
+ // //Pushing features
321
+ // features.push(elemn)
322
+ // })
323
+ // }
324
+ // const aux_features= tf.tensor(features);
325
+ // const aux_target= tf.tensor(targets);
326
+ // // console.log("Tensor stack for evaluation: ", aux_features.shape)
327
+ // const aux: any = this.trainingModel.evaluate(aux_features, aux_target);
328
+ // return aux[1].dataSync()[0];
329
+ // }
330
+ /*** Final statistics */
331
+ /*
332
+ * Calculate each class accuracy using the validation dataset
333
+ */
334
+ async calculateAccuracyPerClass(validationData) {
335
+ const validationXs = TeachableMobileNet.validationDataset.mapAsync(async (dataset) => {
336
+ return dataset.xs;
337
+ });
338
+ const validationYs = TeachableMobileNet.validationDataset.mapAsync(async (dataset) => {
339
+ return dataset.ys;
340
+ });
341
+ // console.log("validation dataset: ", validationXs);
342
+ // console.log("For calculating batch size: ", validationYs);
343
+ // we need to split our validation data into batches in case it is too large to fit in memory
344
+ const batchSize = Math.min(validationYs.size, 32);
345
+ // const batchSize =1;
346
+ const iterations = Math.ceil(validationYs.size / batchSize);
347
+ // console.log("Batch size: ", batchSize);
348
+ const batchesX = validationXs.batch(batchSize);
349
+ const batchesY = validationYs.batch(batchSize);
350
+ const itX = await batchesX.iterator();
351
+ const itY = await batchesY.iterator();
352
+ const allX = [];
353
+ const allY = [];
354
+ for (let i = 0; i < iterations; i++) {
355
+ // 1. get the prediction values in batches
356
+ const batchedXTensor = await itX.next();
357
+ // console.log("Batch size on accuracy per class: ", batchedXTensor.value.shape);
358
+ const batchedXPredictionTensor = this.trainingModel.predict(batchedXTensor.value);
359
+ const argMaxX = batchedXPredictionTensor.argMax(1); // Returns the indices of the max values along an axis
360
+ allX.push(argMaxX);
361
+ // 2. get the ground truth label values in batches
362
+ const batchedYTensor = await itY.next();
363
+ const argMaxY = batchedYTensor.value.argMax(1); // Returns the indices of the max values along an axis
364
+ allY.push(argMaxY);
365
+ // 3. dispose of all our tensors
366
+ batchedXTensor.value.dispose();
367
+ batchedXPredictionTensor.dispose();
368
+ batchedYTensor.value.dispose();
369
+ }
370
+ // concatenate all the results of the batches
371
+ const reference = tf.concat(allY); // this is the ground truth
372
+ const predictions = tf.concat(allX); // this is the prediction our model is guessing
373
+ // console.log("this is the ground truth: ", reference.dataSync())
374
+ // console.log("This is the prediction our model is guessing: ", predictions.dataSync())
375
+ // only if we concatenated more than one tensor for preference and reference
376
+ if (iterations !== 1) {
377
+ for (let i = 0; i < allX.length; i++) {
378
+ allX[i].dispose();
379
+ allY[i].dispose();
380
+ }
381
+ }
382
+ // console.log("Lengtth: ", await reference.dataSync().length)
383
+ // const accuracyperclass=[];
384
+ // const reference_aux= await reference.dataSync();
385
+ // const prediction_aux= await predictions.dataSync();
386
+ // console.log( predictions.dataSync());
387
+ // reference_aux.forEach((element, index) => {
388
+ // if()
389
+ // });
390
+ return { reference, predictions };
391
+ }
392
+ } //end of class
393
+ /***Support methods (helpers) */
394
+ const isTensor = (c) => typeof c.dataId === 'object' && typeof c.shape === 'object';
395
+ /**
396
+ * Converts an integer into its one-hot representation and returns
397
+ * the data as a JS Array.
398
+ */
399
+ function flatOneHot(label, numClasses) {
400
+ const labelOneHot = new Array(numClasses).fill(0);
401
+ labelOneHot[label] = 1;
402
+ return labelOneHot;
403
+ }
404
+ /**
405
+ * Shuffle an array of Float32Array or Samples using Fisher-Yates algorithm
406
+ * Takes an optional seed value to make shuffling predictable
407
+ */
408
+ function fisherYates(array, seed) {
409
+ const length = array.length;
410
+ // need to clone array or we'd be editing original as we goo
411
+ const shuffled = array.slice();
412
+ for (let i = (length - 1); i > 0; i -= 1) {
413
+ let randomIndex;
414
+ if (seed) {
415
+ randomIndex = Math.floor(seed() * (i + 1));
416
+ }
417
+ else {
418
+ randomIndex = Math.floor(Math.random() * (i + 1));
419
+ }
420
+ [shuffled[i], shuffled[randomIndex]] = [shuffled[randomIndex], shuffled[i]];
421
+ }
422
+ return shuffled;
423
+ }
424
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVhY2hhYmxlLWV2b2x1dGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3RmanMtZXZvbHV0aW9uL3NyYy9saWIvbW9kZWxzL3RlYWNoYWJsZS1ldm9sdXRpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGVBQWUsRUFBMEIsTUFBTSxvQkFBb0IsQ0FBQztBQUU3RSxPQUFPLEtBQUssRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRXZDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFdEMsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUlyQyxPQUFPLEtBQUssS0FBSyxNQUFNLHNCQUFzQixDQUFDO0FBSTlDLE1BQU0sbUJBQW1CLEdBQUcsSUFBSSxDQUFDO0FBc0JqQzs7O0dBR0c7QUFDSCxNQUFNLFlBQVksR0FBRyxDQUFDLElBQXVCLEVBQUUsRUFBRTtJQUMvQyw4RkFBOEY7SUFDOUYsd0RBQXdEO0lBQ3hELElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDLFdBQVcsSUFBSSx5QkFBeUIsQ0FBQztJQUNqRSxJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxTQUFTLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUM1RCxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQyxZQUFZLElBQUksRUFBRSxDQUFDO0lBQzVDLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLFNBQVMsSUFBSSxVQUFVLENBQUM7SUFDOUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxJQUFJLEVBQUUsQ0FBQztJQUNoQyxpREFBaUQ7SUFDakQsT0FBTyxJQUFnQixDQUFDO0FBQzFCLENBQUMsQ0FBQztBQUVGLE1BQU0sT0FBTyxrQkFBbUIsU0FBUSxlQUFlO0lBRW5ELHVDQUF1QztJQUN2Qzs7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QUFDbEIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEN1c3RvbU1vYmlsZU5ldCwgbG9hZFRydW5jYXRlZE1vYmlsZU5ldCB9IGZyb20gXCIuL2N1c3RvbS1tb2JpbGVuZXRcIjtcclxuXHJcbmltcG9ydCAqIGFzIHRmIGZyb20gJ0B0ZW5zb3JmbG93L3RmanMnO1xyXG5cclxuaW1wb3J0IHsgY2FwdHVyZSB9IGZyb20gJy4uL3V0aWxzL3RmJztcclxuaW1wb3J0IHsgQ2xhc3MgfSBmcm9tICcuLi91dGlscy9jbGFzcyc7XHJcbmltcG9ydCB7IFV0aWwgfSBmcm9tIFwiLi4vdXRpbHMvdXRpbFwiO1xyXG5cclxuaW1wb3J0ICogYXMgc2VlZHJhbmRvbSBmcm9tICdzZWVkcmFuZG9tJztcclxuXHJcbmltcG9ydCAqIGFzIHRmdmlzIGZyb20gJ0B0ZW5zb3JmbG93L3RmanMtdmlzJztcclxuXHJcbmltcG9ydCB7IFRlbnNvckNvbnRhaW5lciB9IGZyb20gJ0B0ZW5zb3JmbG93L3RmanMtY29yZS9kaXN0L3RlbnNvcl90eXBlcyc7XHJcblxyXG5jb25zdCBWQUxJREFUSU9OX0ZSQUNUSU9OID0gMC4xNTtcclxuXHJcblxyXG5cclxuLyoqXHJcbiAqIHRoZSBtZXRhZGF0YSB0byBkZXNjcmliZSB0aGUgbW9kZWwncyBjcmVhdGlvbixcclxuICogaW5jbHVkZXMgdGhlIGxhYmVscyBhc3NvY2lhdGVkIHdpdGggdGhlIGNsYXNzZXNcclxuICogYW5kIHZlcnNpb25pbmcgaW5mb3JtYXRpb24gZnJvbSB0cmFpbmluZy5cclxuICovXHJcbmV4cG9ydCBpbnRlcmZhY2UgTWV0YWRhdGEge1xyXG4gIHRmanNWZXJzaW9uOiBzdHJpbmc7XHJcbiAgdG1WZXJzaW9uPzogc3RyaW5nO1xyXG4gIHBhY2thZ2VWZXJzaW9uOiBzdHJpbmc7XHJcbiAgcGFja2FnZU5hbWU6IHN0cmluZztcclxuICBtb2RlbE5hbWU/OiBzdHJpbmc7XHJcbiAgdGltZVN0YW1wPzogc3RyaW5nO1xyXG4gIGxhYmVsczogc3RyaW5nW107XHJcbiAgdXNlck1ldGFkYXRhPzoge307XHJcbiAgZ3JheXNjYWxlPzogYm9vbGVhbjtcclxuICBpbWFnZVNpemU/OiBudW1iZXI7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWNlaXZlcyBhIE1ldGFkYXRhIG9iamVjdCBhbmQgZmlsbHMgaW4gdGhlIG9wdGlvbmFsIGZpZWxkcyBzdWNoIGFzIHRpbWVTdGFtcFxyXG4gKiBAcGFyYW0gZGF0YSBhIE1ldGFkYXRhIG9iamVjdFxyXG4gKi9cclxuY29uc3QgZmlsbE1ldGFkYXRhID0gKGRhdGE6IFBhcnRpYWw8TWV0YWRhdGE+KSA9PiB7XHJcbiAgLy8gdXRpbC5hc3NlcnQodHlwZW9mIGRhdGEudGZqc1ZlcnNpb24gPT09ICdzdHJpbmcnLCAoKSA9PiBgbWV0YWRhdGEudGZqc1ZlcnNpb24gaXMgaW52YWxpZGApO1xyXG4gIC8vIGRhdGEucGFja2FnZVZlcnNpb24gPSBkYXRhLnBhY2thZ2VWZXJzaW9uIHx8IHZlcnNpb247XHJcbiAgZGF0YS5wYWNrYWdlTmFtZSA9IGRhdGEucGFja2FnZU5hbWUgfHwgJ0B0ZWFjaGFibGVtYWNoaW5lL2ltYWdlJztcclxuICBkYXRhLnRpbWVTdGFtcCA9IGRhdGEudGltZVN0YW1wIHx8IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKTtcclxuICBkYXRhLnVzZXJNZXRhZGF0YSA9IGRhdGEudXNlck1ldGFkYXRhIHx8IHt9O1xyXG4gIGRhdGEubW9kZWxOYW1lID0gZGF0YS5tb2RlbE5hbWUgfHwgJ3VudGl0bGVkJztcclxuICBkYXRhLmxhYmVscyA9IGRhdGEubGFiZWxzIHx8IFtdO1xyXG4gIC8vIGRhdGEuaW1hZ2VTaXplID0gZGF0YS5pbWFnZVNpemUgfHwgSU1BR0VfU0laRTtcclxuICByZXR1cm4gZGF0YSBhcyBNZXRhZGF0YTtcclxufTtcclxuXHJcbmV4cG9ydCBjbGFzcyBUZWFjaGFibGVNb2JpbGVOZXQgZXh0ZW5kcyBDdXN0b21Nb2JpbGVOZXQge1xyXG5cclxuICAgIC8vIEFycmF5IG9mIGFsbCB0aGUgZXhhbXBsZXMgY29sbGVjdGVkLlxyXG4gICAgLyoqXHJcbiAgICAgIEl0IGlzIHN0YXRpYyBzaW5jZSBhbGwgdGhlIGluc3RhbmNlIHdpbGwgc2hhcmUgdGhlIHNhbWUgZmVhdHVyZXMsIGZvciBzYXZpbmcgbWVtb3J5IGFuZCB0aW1lLlxyXG4gICAgICBUaGUgaWRlYSBpcyBhdm9pZGluZyByZXN0b3JpbmcgdGhlIGZlYXR1cmVzIGluZGl2aWR1YWxseSBhbmQgaGF2aW5nIHRoZSByZWNhbGN1bGF0ZSB0aGVtIGZvciBldmVyeSBuZXdcclxuICAgICAgaW5kaXZpZHVhbHMuXHJcbiAgICAgKi9cclxuICAgIHB1YmxpYyBzdGF0aWMgZXhhbXBsZXM6IEZsb2F0MzJBcnJheVtdW10gPSBbXTtcclxuXHJcbiAgICAvLyBOdW1iZXIgb2YgdG90YWwgc2FtcGxlc1xyXG4gICAgcHJpdmF0ZSBzdGF0aWMgdG90YWxTYW1wbGVzID0gMDtcclxuXHJcbiAgICBjbGFzc2VzOiBDbGFzc1tdPVtdO1xyXG5cclxuICAgIHN0YXRpYyBjbGFzc2VzX25hbWVzOiBzdHJpbmdbXT1bXTtcclxuXHJcbiAgICBzdGF0aWMgbnVtQ2xhc3NlczogbnVtYmVyO1xyXG5cclxuICAgICAgICAvKipcclxuICAgICAqIHRoZSB0cmFpbmluZyBtb2RlbCBmb3IgdHJhbnNmZXIgbGVhcm5pbmdcclxuICAgICAqL1xyXG4gICAgcHJvdGVjdGVkIHRyYWluaW5nTW9kZWwhOiB0Zi5MYXllcnNNb2RlbDtcclxuICB0cmFpbkRhdGFzZXQ6IGFueTtcclxuICB2YWxpZGF0aW9uRGF0YXNldDogYW55O1xyXG4gIHN0YXRpYyB0cmFpbkRhdGFzZXQ6IGFueTtcclxuICBzdGF0aWMgdmFsaWRhdGlvbkRhdGFzZXQ6IGFueTtcclxuICBzdGF0aWMgbnVtVmFsaWRhdGlvbjogbnVtYmVyO1xyXG5cclxuICBjb25zdHJ1Y3Rvcigpe1xyXG4gICAgc3VwZXIoKTtcclxuICAgIHRoaXMuY3JlYXRlSGVhZCgpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogVGhpcyBtZXRob2Qgd2lsbCByZXR1cm4gdGhlIGhlYWQsIHRoZSB0cmFpbmFibGUgcGFydCwgdGhlIHBhcnQgdW5kZXIgZXZvbHV0aW9uLlxyXG4gICAqL1xyXG4gIGdldEhlYWQoKXtcclxuICAgIHJldHVybiB0aGlzLnRyYWluaW5nTW9kZWw7XHJcbiAgfVxyXG5cclxuIC8qKlxyXG4gICogQ3JlYXRlIHRoZSBoZWFkIGZvciB0cmFuc2ZlciBsZWFybmluZy5cclxuICAqIFRoaXMgaXMgdGhlIHRyYWluYWJsZSBzZWN0aW9uIG9mIHRoZSB0cmFuc2ZlciBsZWFybmluZy5cclxuICAqL1xyXG4gY3JlYXRlSGVhZCgpe1xyXG5cclxuICBjb25zdCBpbnB1dFNpemU9IFRlYWNoYWJsZU1vYmlsZU5ldC5nZXRpbnB1dFNoYXBlKCk7XHJcblxyXG4gIHRoaXMudHJhaW5pbmdNb2RlbCA9IHRmLnNlcXVlbnRpYWwoe1xyXG4gICAgbGF5ZXJzOiBbXHJcbiAgICAgIHRmLmxheWVycy5kZW5zZSh7XHJcbiAgICAgICAgaW5wdXRTaGFwZTogW2lucHV0U2l6ZV0sXHJcbiAgICAgICAgdW5pdHM6MTAwLFxyXG4gICAgICAgIGFjdGl2YXRpb246ICdyZWx1JywgIFxyXG4gICAgICAgIHVzZUJpYXM6IHRydWVcclxuICAgIH0pLFxyXG4gICAgdGYubGF5ZXJzLmRlbnNlKHsgIFxyXG4gICAgICB1c2VCaWFzOiBmYWxzZSxcclxuICAgICAgYWN0aXZhdGlvbjogJ3NvZnRtYXgnLFxyXG4gICAgICB1bml0czogVGVhY2hhYmxlTW9iaWxlTmV0LmNsYXNzZXNfbmFtZXMubGVuZ3RoXHJcbiAgfSlcclxuICAgIF1cclxuICB9KTtcclxuXHJcbiAgY29uc3Qgb3B0aW1pemVyID0gdGYudHJhaW4uYWRhbSgpO1xyXG4gIC8vIGNvbnN0IG9wdGltaXplciA9IHRmLnRyYWluLnJtc3Byb3AocGFyYW1zLmxlYXJuaW5nUmF0ZSk7XHJcblxyXG4gIHRoaXMudHJhaW5pbmdNb2RlbC5jb21waWxlKHtcclxuICAgICAgb3B0aW1pemVyLFxyXG4gICAgICAvLyBsb3NzOiAnYmluYXJ5Q3Jvc3NlbnRyb3B5JyxcclxuICAgICAgbG9zczogJ2NhdGVnb3JpY2FsQ3Jvc3NlbnRyb3B5JyxcclxuICAgICAgbWV0cmljczogWydhY2N1cmFjeSddXHJcbiAgfSk7XHJcbiB9XHJcbiBcclxuIGFzeW5jICB0cmFpbigpe1xyXG5cclxuICBjb25zdCB0cmFpbmluZ1N1cmZhY2UgPSB7IG5hbWU6ICdMb3NzIGFuZCBNU0UnLCB0YWI6ICdUcmFpbmluZycgfTtcclxuXHJcbiAgY29uc3QgZGF0YXNldD0gVGVhY2hhYmxlTW9iaWxlTmV0LmNvbnZlcnRUb1RmRGF0YXNldCgpO1xyXG5cclxuICAvL1NhbHZpbmcgYSBjb3B5IG9mIHRoZSB2YWxpZGF0aW9uIGRhdGFzZXQsIGZvciBsYXRlclxyXG4gIFRlYWNoYWJsZU1vYmlsZU5ldC52YWxpZGF0aW9uRGF0YXNldD0gZGF0YXNldC52YWxpZGF0aW9uRGF0YXNldDtcclxuXHJcbiAgLy8gY29uc29sZS5sb2coXCJEYXRhc2V0IGZvciB0cmFpbmluZzogXCIsIGRhdGFzZXQudHJhaW5EYXRhc2V0KTtcclxuXHJcbiAgY29uc3QgdHJhaW5EYXRhID0gZGF0YXNldC50cmFpbkRhdGFzZXQuYmF0Y2goMzApO1xyXG4gIGNvbnN0IHZhbGlkYXRpb25EYXRhID0gZGF0YXNldC52YWxpZGF0aW9uRGF0YXNldC5iYXRjaCgxMCk7XHJcblxyXG4gIC8vIHRoaXMuY3JlYXRlSGVhZCgpO1xyXG5cclxuICBjb25zdCBjYWxsYmFja3M9IFtcclxuICAgIC8vIFNob3cgb24gYSB0ZmpzLXZpcyB2aXNvciB0aGUgbG9zcyBhbmQgYWNjdXJhY3kgdmFsdWVzIGF0IHRoZSBlbmQgb2YgZWFjaCBlcG9jaC5cclxuICAgIHRmdmlzLnNob3cuZml0Q2FsbGJhY2tzKHRyYWluaW5nU3VyZmFjZSwgWydsb3NzJywgJ2FjYycsIFwidmFsX2xvc3NcIiwgXCJ2YWxfYWNjXCJdLFxyXG4gICAgICB7XHJcbiAgICAgICAgY2FsbGJhY2tzOiBbJ29uRXBvY2hFbmQnXSxcclxuICAgICAgfVxyXG4gICAgKSxcclxuICAgIHt9LF1cclxuXHJcbiAgY29uc3QgaGlzdG9yeTogYW55ID0gYXdhaXQgdGhpcy50cmFpbmluZ01vZGVsLmZpdERhdGFzZXQodHJhaW5EYXRhLCB7XHJcbiAgICBlcG9jaHM6IDEwMCxcclxuICAgIHZhbGlkYXRpb25EYXRhLFxyXG4gICAgY2FsbGJhY2tzXHJcbn0pLnRoZW4oKGluZm86YW55KT0+e1xyXG4gICBjb25zb2xlLmxvZygnUHJlY2lzw6NvIGZpbmFsJywgaW5mby5oaXN0b3J5LnZhbF9hY2NbaW5mby5oaXN0b3J5LmFjYy5sZW5ndGgtMV0pO1xyXG59KTtcclxuXHJcblxyXG4vLyBhd2FpdCB0aGlzLmFjY3VyYWN5X3Blcl9jbGFzcygpO1xyXG5cclxuXHJcbi8vIGNvbnNvbGUubG9nKFwiSGlzdG9yeTogXCIsIGhpc3RvcnkuaGlzdG9yeS5hY2MpO1xyXG4gIC8vIGF3YWl0IHRoaXMudHJhaW5pbmdNb2RlbC5maXQodGhpcy5mZWF0dXJlWCwgdGhpcy50YXJnZXQsIHt9KVxyXG4gfVxyXG5cclxuYXN5bmMgYWNjdXJhY3lfcGVyX2NsYXNzKGNvbmZ1c2lvbl9tYXRyaXhfcmVjaXBpZW50OiBhbnkpe1xyXG4gIC8qKkNhbGN1bGF0aW5nIEFjY3VyYWN5IHBlciBjbGFzcyAqL1xyXG5jb25zdCBhY2N1cmFjeXBlcmNsYXNzOiBhbnkgPSAgYXdhaXQgdGhpcy5jYWxjdWxhdGVBY2N1cmFjeVBlckNsYXNzKFRlYWNoYWJsZU1vYmlsZU5ldC52YWxpZGF0aW9uRGF0YXNldCk7XHJcbi8vIGNvbnNvbGUubG9nKFwiQWNjdXJhY3kgcGVyIGNsYXNzOiBcIiwgYWNjdXJhY3lwZXJjbGFzcyk7XHJcblxyXG4vL0NvbmZ1c2lvbiBtYXRyaXhcclxuLy8gQ2FsbGluZyB0Zi5jb25mdXNpb25NYXRyaXgoKSBtZXRob2QgXHJcbmNvbnN0IG91dHB1dCA9IHRmLm1hdGguY29uZnVzaW9uTWF0cml4KCBhY2N1cmFjeXBlcmNsYXNzLnJlZmVyZW5jZSwgYWNjdXJhY3lwZXJjbGFzcy5wcmVkaWN0aW9ucywgVGVhY2hhYmxlTW9iaWxlTmV0LmNsYXNzZXNfbmFtZXMubGVuZ3RoKTsgXHJcbiAgXHJcbi8vIFByaW50aW5nIG91dHB1dCBcclxub3V0cHV0LnByaW50KClcclxuY29uc3QgY29uZnVzaW9uX21hdHJpeD0gIG91dHB1dC5kYXRhU3luYygpO1xyXG5cclxuLy8gY29uc29sZS5sb2coY29uZnVzaW9uX21hdHJpeCk7XHJcbi8vIGNvbnNvbGUubG9nKGNvbmZ1c2lvbl9tYXRyaXhbVGVhY2hhYmxlTW9iaWxlTmV0LmNsYXNzZXNfbmFtZXMubGVuZ3RoICsgVGVhY2hhYmxlTW9iaWxlTmV0LmNsYXNzZXNfbmFtZXMubGVuZ3RoXSk7XHJcblxyXG5jb25zdCBhY2N1cmFjeSA9IFtdO1xyXG5cclxuZm9yKGxldCBpPTA7IGk8VGVhY2hhYmxlTW9iaWxlTmV0LmNsYXNzZXNfbmFtZXMubGVuZ3RoO2krKyl7XHJcblxyXG4gIGFjY3VyYWN5LnB1c2goY29uZnVzaW9uX21hdHJpeFtUZWFjaGFibGVNb2JpbGVOZXQuY2xhc3Nlc19uYW1lcy5sZW5ndGgqaSsgaV0vVGVhY2hhYmxlTW9iaWxlTmV0Lm51bVZhbGlkYXRpb24pXHJcbn1cclxuXHJcbmNvbnNvbGUubG9nKFwiQWNjdXJhY3kgcGVyIGNsYXNzOiBcIiwgYWNjdXJhY3kpO1xyXG5cclxuZm9yKGxldCBpPTA7IGk8VGVhY2hhYmxlTW9iaWxlTmV0LmNsYXNzZXNfbmFtZXMubGVuZ3RoO2krKyl7XHJcblxyXG4gIGNvbmZ1c2lvbl9tYXRyaXhfcmVjaXBpZW50LnB1c2goW10pO1xyXG5cclxuICBmb3IobGV0IGo9MDsgajxUZWFjaGFibGVNb2JpbGVOZXQuY2xhc3Nlc19uYW1lcy5sZW5ndGg7aisrKXtcclxuICAgIGNvbmZ1c2lvbl9tYXRyaXhfcmVjaXBpZW50W2ldLnB1c2goW10pO1xyXG5cclxuICAgIGNvbmZ1c2lvbl9tYXRyaXhfcmVjaXBpZW50W2ldW2pdPSBjb25mdXNpb25fbWF0cml4W1RlYWNoYWJsZU1vYmlsZU5ldC5jbGFzc2VzX25hbWVzLmxlbmd0aCppKyBqXS9UZWFjaGFibGVNb2JpbGVOZXQubnVtVmFsaWRhdGlvblxyXG4gICAgY29uZnVzaW9uX21hdHJpeF9yZWNpcGllbnRbaV1bal09KGNvbmZ1c2lvbl9tYXRyaXhfcmVjaXBpZW50W2ldW2pdLnRvRml4ZWQoMikpKjEwMDtcclxuICB9XHJcblxyXG4gIC8vIGFjY3VyYWN5LnB1c2goY29uZnVzaW9uX21hdHJpeFtUZWFjaGFibGVNb2JpbGVOZXQuY2xhc3Nlc19uYW1lcy5sZW5ndGgqaSsgaV0vVGVhY2hhYmxlTW9iaWxlTmV0Lm51bVZhbGlkYXRpb24pXHJcbn1cclxuXHJcbmNvbnNvbGUubG9nKFwiQ29uZnVzaW9uIG1hdHJpeCBhcyBhIG1hdHJpeFwiKVxyXG5jb25zb2xlLmxvZyhjb25mdXNpb25fbWF0cml4X3JlY2lwaWVudCk7XHJcblxyXG5yZXR1cm4gYWNjdXJhY3kubWFwKChlbGVtOiBhbnkpPT5lbGVtLnRvRml4ZWQoMikqMTAwKTtcclxufVxyXG5cclxuIFxyXG4gYXN5bmMgbG9hZEltYWdlcyhudW1iZXJfb2Zfc3BlY2llczogbnVtYmVyLCBjbGFzc2VzX25hbWVzOiBzdHJpbmdbXSwgb3B0aW9uczogb2JqZWN0KXtcclxuICAgICAgICBUZWFjaGFibGVNb2JpbGVOZXQuY2xhc3Nlc19uYW1lcz1jbGFzc2VzX25hbWVzO1xyXG4gICAgXHJcbiAgICAgICAgYXdhaXQgdGhpcy5hZGRfc3BlY2llcyhudW1iZXJfb2Zfc3BlY2llcywgb3B0aW9ucyk7ICAgICAgICBcclxuXHJcbn1cclxuXHJcbmFzeW5jIGFkZF9zcGVjaWVzKG51bWJlcl9vZl9zcGVjaWVzOiBudW1iZXIsIG9wdGlvbnM6IG9iamVjdCl7XHJcbiAgXHJcbiAgICAvL0xvYWRpbmcgZmVhdHVyZSBtb2RlbCwgdXNlZCB0byBjcmVhdGUgZmVhdHVyZXMgZnJvbSBpbWFnZXNcclxuICAgIC8vICBhd2FpdCB0aGlzLmxvYWRGZWF0dXJlTW9kZWwoKTtcclxuXHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IFRlYWNoYWJsZU1vYmlsZU5ldC5jbGFzc2VzX25hbWVzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgIC8vIHRoaXMuYWRkX2ltYWdlcyh0aGlzLmNsYXNzZXNfbmFtZXNbaV0sIG51bWJlcl9vZl9zcGVjaWVzLCBvcHRpb25zKTtcclxuICAgIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFxyXG4gKiBAcGFyYW0gbmFtZSAtIG5hbWUgb2YgdGhlIGNsYXNzIHJlY2VpdmluZyBhbiBleGFtcGxlXHJcbiAqIEBwYXJhbSBudW1iZXJfb2Zfc3BlY2llcyAtIGhvdyBtYW55IGltYWdlcyB0byBhZGRcclxuICogQHBhcmFtIG9wdGlvbnMgLSBkZXRhaWxzIG9uIHRoZSBsb2NhdGlvbiBvZiB0aGUgaW1hZ2VzXHJcbiAqL1xyXG5hc3luYyBhZGRfaW1hZ2VzKG5hbWU6IHN0cmluZywgbnVtYmVyX29mX3NwZWNpZXM6IG51bWJlciwgb3B0aW9uczogYW55KXsgICBcclxuXHJcbiAgICBjb25zdCBjbGFzc19hZGQ6IGFueT0gW107XHJcblxyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBudW1iZXJfb2Zfc3BlY2llczsgaSsrKSB7ICAgICAgXHJcbiAgICAvLyAgIGNsYXNzX2FkZC5wdXNoKGAke29wdGlvbnMuYmFzZX0vJHtuYW1lfS8ke29wdGlvbnMuZmlsZV9uYW1lfSAke2l9LiR7b3B0aW9ucy5maWxlX2V4dGVuc2lvbn1gKTtcclxuICAgICAgICBcclxuICAgICAgICAvL1VwbG9hZGluZyBpbWFnZXNcclxuICAgICAgICBjb25zdCBjYWtlID0gbmV3IEltYWdlKCk7ICAgICAgICBcclxuICAgICAgICAvLyBjYWtlLnNyYyA9IGAke29wdGlvbnMuYmFzZX0vJHtuYW1lfS8ke29wdGlvbnMuZmlsZV9uYW1lfSAke2l9LiR7b3B0aW9ucy5maWxlX2V4dGVuc2lvbn1gO1xyXG4gICAgICAgIGNha2UuaGVpZ2h0PTIyNDtcclxuICAgICAgICBjYWtlLndpZHRoPTIyNDtcclxuICAgICAgICBjYWtlLnNyYz1cIi4vYXNzZXRzL2RhdGFzZXQvQ2FuJUMzJUExcmlvLWRhLVRlcnJhL2ltYWdlJTIwMC5qcGVnXCJcclxuICAgICAgICAvLyBjb25zb2xlLmxvZyhcIkltYWdlIGxvY2F0aW9uOiBcIiwgY2FrZS5zcmMgKVxyXG5cclxuICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgIGNha2Uub25sb2FkID0gKCkgPT4ge1xyXG5cclxuICAgICAgICAgICAgICAgIC8vRmluZGluZyB0aGUgY29ycmVzcG9uZGVudCBpbmRleCBvZiB0aGUgY2xhc3Mgd2l0aCBuYW1lIGdpdmVuXHJcbiAgICAgICAgICAgICAgICBjb25zdCBpbmRleD0gVGVhY2hhYmxlTW9iaWxlTmV0LmNsYXNzZXNfbmFtZXMuZmluZEluZGV4KChlbGVtKT0+ZWxlbT09PW5hbWUpXHJcblxyXG4gICAgICAgICAgICAgICAgLy8gdGhpcy5hZGRFeGFtcGxlKGluZGV4LCBjYWtlKTtcclxuXHJcbiAgICAgICAgICAgICAgcmVzb2x2ZSgpO1xyXG4gICAgICAgICAgICB9O1xyXG4gIFxyXG4gICAgICAgICAgICBjYWtlLm9uZXJyb3IgPSAoZXJyb3IpID0+IHtcclxuICAgICAgICAgICAgICAvLyBIYW5kbGUgZXJyb3IgaWYgdGhlIGltYWdlIGZhaWxzIHRvIGxvYWRcclxuICAgICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgICB9O1xyXG4gICAgICAgICAgfSk7XHJcbiAgfVxyXG5cclxuICAvLyB0aGlzLmNsYXNzZXMucHVzaCh7bmFtZTogbmFtZSwgaW1hZ2VzOiBjbGFzc19hZGR9KSAgXHJcblxyXG4gIH1cclxuXHJcblxyXG5cclxuICAgICAgXHJcbi8qKlxyXG4gKiBUaGlzIG1ldGhvZCB3aWxsIHRyYW5zZm9ybSBpbWFnZXMgaW50byB0ZW5zb3JzXHJcbiAqIEBwYXJhbSBudW1iZXJfb2ZfY2xhc3NlcyAtIG51bWJlciBvZiBjbGFzc2VzXHJcbiAqIEBwYXJhbSBjbGFzc2VzX25hbWVzIC0gbmFtZSBvZiBlYWNoIGNsYXNzXHJcbiAqL1xyXG5hc3luYyBjcmVhdGVUZW5zb3JzKG51bWJlcl9vZl9jbGFzc2VzOiBudW1iZXIsIGNsYXNzZXNfbmFtZXM6IHN0cmluZ1tdKSB7XHJcblxyXG4gICAgbGV0IG91dHB1dDogYW55ID0gW107XHJcblxyXG4gICAgLyoqIFRoZXJlIGlzIGEgZnVuY3Rpb24gb24gVGVuc29yRmxvdy5qcyB0aGF0IGFsc28gZG9lcyB0aGF0ICovXHJcbiAgICBjb25zdCBzaWduYXR1cmVzPSBuZXcgVXRpbCgpLmlkZW50aXR5TWF0cml4KG51bWJlcl9vZl9jbGFzc2VzKTtcclxuXHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG51bWJlcl9vZl9jbGFzc2VzOyBpKyspIHtcclxuXHJcbiAgICAgICAgdGhpcy5jbGFzc2VzW2ldLnNpZ25hdHVyZT1zaWduYXR1cmVzW2ldO1xyXG4gICAgICAgIHRoaXMuY2xhc3Nlc1tpXS5uYW1lPWNsYXNzZXNfbmFtZXNbaV07XHJcblxyXG4gICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5jbGFzc2VzW2ldLmltYWdlcy5sZW5ndGg7IGorKykge1xyXG5cclxuXHJcbiAgICAgICAgfVxyXG5cclxuICAgIH1cclxufVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogQWRkIGEgc2FtcGxlIG9mIGRhdGEgdW5kZXIgdGhlIHByb3ZpZGVkIGNsYXNzTmFtZVxyXG4gICAgICogQHBhcmFtIGNsYXNzTmFtZSB0aGUgY2xhc3NpZmljYXRpb24gdGhpcyBleGFtcGxlIGJlbG9uZ3MgdG9cclxuICAgICAqIEBwYXJhbSBzYW1wbGUgdGhlIGltYWdlIC8gdGVuc29yIHRoYXQgYmVsb25ncyBpbiB0aGlzIGNsYXNzaWZpY2F0aW9uXHJcbiAgICAgKi9cclxuICAgIC8vIHB1YmxpYyBhc3luYyBhZGRFeGFtcGxlKGNsYXNzTmFtZTogbnVtYmVyLCBzYW1wbGU6IEhUTUxDYW52YXNFbGVtZW50IHwgdGYuVGVuc29yKSB7XHJcbnB1YmxpYyBzdGF0aWMgYXN5bmMgYWRkRXhhbXBsZShjbGFzc05hbWU6IG51bWJlciwgbmFtZTogc3RyaW5nLCBzYW1wbGU6IEhUTUxJbWFnZUVsZW1lbnQgfCBIVE1MQ2FudmFzRWxlbWVudCB8IHRmLlRlbnNvcikge1xyXG5cclxuICAgICAgICAgICAgLy8gY29uc29sZS5sb2coXCJBZGRpbmcgYSBuZXcgZXhhbXBsZS4uLlwiKSBcclxuICAgICAgICAgIFxyXG4gICAgICAgICAgICBjb25zdCBjYXAgPSBpc1RlbnNvcihzYW1wbGUpID8gc2FtcGxlIDogY2FwdHVyZShzYW1wbGUpO1xyXG4gICAgICAgICAgICBcclxuICAgICAgICAgICAgXHJcbiAgICAgICAgICAgIC8vR2V0dGluZyB0aGUgZmVhdHVyZXNcclxuICAgICAgICAgICAgY29uc3QgZXhhbXBsZSA9IHRoaXMudHJ1bmNhdGVkTW9kZWwucHJlZGljdChjYXApIGFzIHRmLlRlbnNvcjtcclxuICAgICAgICAgICAgLy8gY29uc29sZS5sb2coXCJTaGFwZSBhZnRlciBmZWF0dXJlIGV4dHJhY3Rpb246IFwiLCBleGFtcGxlLnNoYXBlKSAgICAgICAgICBcclxuICAgICAgICAgICBcclxuXHJcbiAgICAgICAgICAgIGNvbnN0IGFjdGl2YXRpb24gPSBleGFtcGxlLmRhdGFTeW5jKCkgYXMgRmxvYXQzMkFycmF5O1xyXG4gICAgICAgICAgICBcclxuICAgICAgICAgICAgLy9WZXJ5IGltcG9ydGFudCB0byBjbGVhbiB0aGUgbWVtb3J5IGFmdGVybWF0aCwgaXQgbWFrZXMgdGhlIGRpZmZlcmVuY2VcclxuICAgICAgICAgICAgY2FwLmRpc3Bvc2UoKTtcclxuICAgICAgICAgICAgZXhhbXBsZS5kaXNwb3NlKCk7XHJcbiAgICBcclxuICAgICAgICAgICAgLy8gLy9BY2Nlc3NpbmcgdGhlIGluc3RhbmNlIHZhcmlhYmxlLCBub3QgdGhlIGxvY2FsIG9uZXNcclxuICAgICAgICAgICAgLy8gLy8gc2F2ZSBzYW1wbGVzIG9mIGVhY2ggY2xhc3Mgc2VwYXJhdGVseSBcclxuICAgICAgICAgICAgXHJcbiAgICAgICAgICAgIGlmKCFUZWFjaGFibGVNb2JpbGVOZXQuZXhhbXBsZXNbY2xhc3NOYW1lXSlcclxuICAgICAgICAgICAgICAvL2FuZCBhbiBlbXB0eSBhcnJheSwgbWFrZSBzdXJlIHRoZXJlIGlzIG5vdCBlbXB0eSBlbGVtZW50cy4gXHJcbiAgICAgICAgICAgICAgLy9pdCB3aWxsIGNyZWF0ZSBpc3N1ZSB3aGVuIHRyYW5zZm9ybWluZyB0byB0ZW5zb3JzXHJcbiAgICAgICAgICAgICAgVGVhY2hhYmxlTW9iaWxlTmV0LmV4YW1wbGVzW2NsYXNzTmFtZV09W11cclxuXHJcblxyXG4gICAgICAgICAgICAgIGlmKCFUZWFjaGFibGVNb2JpbGVOZXQuY2xhc3Nlc19uYW1lc1tjbGFzc05hbWVdKVxyXG4gICAgICAgICAgICAgICAgLy9TYXZpbmcgdGhlIGxhYmxlIHdoZW4gaXQgZmlyc3QgYXBwZWFyc1xyXG4gICAgICAgICAgICAgICAgVGVhY2hhYmxlTW9iaWxlTmV0LmNsYXNzZXNfbmFtZXNbY2xhc3NOYW1lXT1uYW1lO1xyXG5cclxuICAgICAgICAgICAgVGVhY2hhYmxlTW9iaWxlTmV0LmV4YW1wbGVzW2NsYXNzTmFtZV0ucHVzaChhY3RpdmF0aW9uKTtcclxuICAgIFxyXG4gICAgICAgICAgICAvLyAvLyBpbmNyZWFzZSBvdXIgc2FtcGxlIGNvdW50ZXJcclxuICAgICAgICAgICAgVGVhY2hhYmxlTW9iaWxlTmV0LnRvdGFsU2FtcGxlcysrO1xyXG4gICAgICAgIH1cclxuXHJcblxyXG4gICAvKipcclxuICAgICAqIHByb2Nlc3MgdGhlIGN1cnJlbnQgZXhhbXBsZXMgcHJvdmlkZWQgdG8gY2FsY3VsYXRlIGxhYmVscyBhbmQgZm9ybWF0XHJcbiAgICAgKiBpbnRvIHByb3BlciB0Zi5kYXRhLkRhdGFzZXRcclxuICAgICAqL1xyXG4gICBzdGF0aWMgcHJlcGFyZSgpIHtcclxuICAgIGZvciAoY29uc3QgY2xhc3NlcyBpbiBUZWFjaGFibGVNb2JpbGVOZXQuZXhhbXBsZXMpe1xyXG4gICAgICAgIGlmIChjbGFzc2VzLmxlbmd0aCA9PT0gMCkge1xyXG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0FkZCBzb21lIGV4YW1wbGVzIGJlZm9yZSB0cmFpbmluZycpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBjb25zdCBkYXRhc2V0czogYW55ID0gdGhpcy5jb252ZXJ0VG9UZkRhdGFzZXQoKTtcclxuXHJcbiAgICB0aGlzLnRyYWluRGF0YXNldCA9IGRhdGFzZXRzLnRyYWluRGF0YXNldDtcclxuICAgIHRoaXMudmFsaWRhdGlvbkRhdGFzZXQgPSBkYXRhc2V0cy52YWxpZGF0aW9uRGF0YXNldDtcclxufVxyXG5cclxucHVibGljIHByZXBhcmVEYXRhc2V0KCkge1xyXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgVGVhY2hhYmxlTW9iaWxlTmV0Lm51bUNsYXNzZXM7IGkrKykge1xyXG4gICAgLy9EaWZmZXJlbnQgZnJvbSB0aGUgb3JpZ2luYWwgaW1wbGVtZW50YXRpb24gb2YgVE0sIG1pbmUgaXMgdXNpbmcgZXhhbXBsZSBhcyBzdGF0aWMuXHJcbiAgICAvL1RoZSBnb2FsIGlzIHNhdmluZyBtZW1vcnkgYnkgdXNpbmcgYSBzaW5nbGUgaW5zdGFuY2Ugb2YgdGhlIHZhcmlhYmxlXHJcbiAgICBUZWFjaGFibGVNb2JpbGVOZXQuZXhhbXBsZXNbaV0gPSBbXTtcclxuICB9XHJcbn1cclxuXHJcbiBcclxuICAgIC8vIE9wdGlvbmFsIHNlZWQgdG8gbWFrZSBzaHVmZmxpbmcgb2YgZGF0YSBwcmVkaWN0YWJsZVxyXG4gICAgc3RhdGljIHNlZWQ6IHNlZWRyYW5kb20ucHJuZztcclxuXHJcblxyXG5cclxuICAgIFxyXG4vKipcclxuICAgICAqIFByb2Nlc3MgdGhlIGV4YW1wbGVzIGJ5IGZpcnN0IHNodWZmbGluZyByYW5kb21seSBwZXIgY2xhc3MsIHRoZW4gYWRkaW5nXHJcbiAgICAgKiBvbmUtaG90IGxhYmVscywgdGhlbiBzcGxpdHRpbmcgaW50byB0cmFpbmluZy92YWxpZGF0aW9uIGRhdHNldHMsIGFuZCBmaW5hbGx5XHJcbiAgICAgKiBzb3J0aW5nIG9uZSBsYXN0IHRpbWVcclxuICAgICAqL1xyXG4gXHJcbnN0YXRpYyBjb252ZXJ0VG9UZkRhdGFzZXQoKSB7XHJcblxyXG4gICAgICAgICAvLyBmaXJzdCBzaHVmZmxlIGVhY2ggY2xhc3MgaW5kaXZpZHVhbGx5XHJcbiAgICAgICAgLy8gVE9ETzogd2UgY291bGQgYmFzaWNhbGx5IHJlcGxpY2F0ZSB0aGlzIGJ5IGluc3RlcnRpbmcgcmFuZG9tbHlcclxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IFRlYWNoYWJsZU1vYmlsZU5ldC5leGFtcGxlcy5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgICAgVGVhY2hhYmxlTW9iaWxlTmV0LmV4YW1wbGVzW2ldID0gZmlzaGVyWWF0ZXMoVGVhY2hhYmxlTW9iaWxlTmV0LmV4YW1wbGVzW2ldLCB0aGlzLnNlZWQpIGFzIEZsb2F0MzJBcnJheVtdO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyB0aGVuIGJyZWFrIGludG8gdmFsaWRhdGlvbiBhbmQgdGVzdCBkYXRhc2V0c1xyXG4gICAgICBsZXQgdHJhaW5EYXRhc2V0OiBTYW1wbGVbXSA9IFtdO1xyXG4gICAgICBsZXQgdmFsaWRhdGlvbkRhdGFzZXQ6IFNhbXBsZVtdID0gW107XHJcblxyXG4gICAgICAvLyBmb3IgZWFjaCBjbGFzcywgYWRkIHNhbXBsZXMgdG8gdHJhaW4gYW5kIHZhbGlkYXRpb24gZGF0YXNldFxyXG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IFRlYWNoYWJsZU1vYmlsZU5ldC5leGFtcGxlcy5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgICBcclxuICAgICAgICAvLyBjb25zb2xlLmxvZyhcIk51bWJlciBvZiBjbGFzc2VzOiBcIiwgVGVhY2hhYmxlTW9iaWxlTmV0LmNsYXNzZXNfbmFtZXMubGVuZ3RoKTtcclxuXHJcbiAgICAgICAgY29uc3QgeSA9IGZsYXRPbmVIb3QoaSwgVGVhY2hhYmxlTW9iaWxlTmV0LmNsYXNzZXNfbmFtZXMubGVuZ3RoKTtcclxuXHJcbiAgICAgICAgY29uc3QgY2xhc3NMZW5ndGggPSBUZWFjaGFibGVNb2JpbGVOZXQuZXhhbXBsZXNbaV0ubGVuZ3RoO1xyXG5cclxuICAgICAgICAvLyBjb25zb2xlLmxvZyhcIk51bWJlciBvZiBlbGVtZW50cyBwZXIgY2xhc3M6IFwiLCBjbGFzc0xlbmd0aCk7XHJcblxyXG4gICAgICAgIGNvbnN0IG51bVZhbGlkYXRpb24gPSBNYXRoLmNlaWwoVkFMSURBVElPTl9GUkFDVElPTiAqIGNsYXNzTGVuZ3RoKTtcclxuICAgICAgICBjb25zdCBudW1UcmFpbiA9IGNsYXNzTGVuZ3RoIC0gbnVtVmFsaWRhdGlvbjtcclxuXHJcbiAgICAgICAgdGhpcy5udW1WYWxpZGF0aW9uPW51bVZhbGlkYXRpb247XHJcbiAgICAgICBcclxuICAgICAgICAvKipJdCBpcyB2aXNpdGluZyBwZXIgY2xhc3MsIHRodXMsIGl0IGlzIHBvc3NpYmxlIHRvIGZpeCB5LCB0aGUgdGFyZ2V0IGxhYmVsICovXHJcbiAgICAgXHJcbiAgICAgIGNvbnN0IGNsYXNzVHJhaW4gPSB0aGlzLmV4YW1wbGVzW2ldLnNsaWNlKDAsIG51bVRyYWluKS5tYXAoKGRhdGFBcnJheSkgPT4ge1xyXG4gICAgICAgICAgcmV0dXJuIHsgZGF0YTogZGF0YUFycmF5LCBsYWJlbDogeSB9O1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAgIGNvbnN0IGNsYXNzVmFsaWRhdGlvbiA9IHRoaXMuZXhhbXBsZXNbaV0uc2xpY2UobnVtVHJhaW4pLm1hcCgoZGF0YUFycmF5KSA9PiB7XHJcbiAgICAgICAgICByZXR1cm4geyBkYXRhOiBkYXRhQXJyYXksIGxhYmVsOiB5IH07XHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgdHJhaW5EYXRhc2V0ID0gdHJhaW5EYXRhc2V0LmNvbmNhdChjbGFzc1RyYWluKTtcclxuICAgICAgdmFsaWRhdGlvbkRhdGFzZXQgPSB2YWxpZGF0aW9uRGF0YXNldC5jb25jYXQoY2xhc3NWYWxpZGF0aW9uKTsgXHJcblxyXG5cclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gY29uc29sZS5sb2coXCJUcmFpbmluZyBlbGVtZW50OiBcIiwgdHJhaW5EYXRhc2V0W3RyYWluRGF0YXNldC5sZW5ndGgtMV0pICAgICAgXHJcbiAgICAgIC8vIGNvbnNvbGUubG9nKFwiVHJhaW5pbmcgbGVuZ3RoOiBcIiwgdHJhaW5EYXRhc2V0Lmxlbmd0aClcclxuICAgICAgLy8gY29uc29sZS5sb2coXCJ2YWxpZGF0aW9uIGxlbmd0aDogXCIsIHZhbGlkYXRpb25EYXRhc2V0Lmxlbmd0aCk7XHJcblxyXG4gICAgICBcclxuICAgICAgICAvLyBmaW5hbGx5IHNodWZmbGUgYm90aCB0cmFpbiBhbmQgdmFsaWRhdGlvbiBkYXRhc2V0c1xyXG4gICAgICAgIHRyYWluRGF0YXNldCA9IGZpc2hlcllhdGVzKHRyYWluRGF0YXNldCwgdGhpcy5zZWVkKSBhcyBTYW1wbGVbXTtcclxuICAgICAgICB2YWxpZGF0aW9uRGF0YXNldCA9IGZpc2hlcllhdGVzKHZhbGlkYXRpb25EYXRhc2V0LCB0aGlzLnNlZWQpIGFzIFNhbXBsZVtdO1xyXG5cclxuICAgICAgICBjb25zdCB0cmFpblggPSB0Zi5kYXRhLmFycmF5KHRyYWluRGF0YXNldC5tYXAoc2FtcGxlID0+IHNhbXBsZS5kYXRhKSk7XHJcbiAgICAgICAgY29uc3QgdmFsaWRhdGlvblggPSB0Zi5kYXRhLmFycmF5KHZhbGlkYXRpb25EYXRhc2V0Lm1hcChzYW1wbGUgPT4gc2FtcGxlLmRhdGEpKTtcclxuICAgICAgICBjb25zdCB0cmFpblkgPSB0Zi5kYXRhLmFycmF5KHRyYWluRGF0YXNldC5tYXAoc2FtcGxlID0+IHNhbXBsZS5sYWJlbCkpO1xyXG4gICAgICAgIGNvbnN0IHZhbGlkYXRpb25ZID0gdGYuZGF0YS5hcnJheSh2YWxpZGF0aW9uRGF0YXNldC5tYXAoc2FtcGxlID0+IHNhbXBsZS5sYWJlbCkpO1xyXG5cclxuICAgICAgICAgICAgICAgIC8vIHJldHVybiB0Zi5kYXRhIGRhdGFzZXQgb2JqZWN0c1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgICAgICAgdHJhaW5EYXRhc2V0OiB0Zi5kYXRhLnppcCh7IHhzOiB0cmFpblgsICB5czogdHJhaW5ZfSksXHJcbiAgICAgICAgICAgICAgICAgIHZhbGlkYXRpb25EYXRhc2V0OiB0Zi5kYXRhLnppcCh7IHhzOiB2YWxpZGF0aW9uWCwgIHlzOiB2YWxpZGF0aW9uWX0pXHJcbiAgICAgICAgICAgICAgfTtcclxuXHJcblxyXG59XHJcblxyXG5kYXRhc2V0Rm9yRXZhbHVhdGlvbigpe1xyXG5cclxufVxyXG5cclxuLyoqTWV0cmljcyAqL1xyXG5cclxuc3RhdGljIGZlYXR1cmVfYXV4OiBhbnk7XHJcbnN0YXRpYyB0YXJnZXRfYXV4OiBhbnk7XHJcblxyXG5hc3luYyBldmFsdWF0ZSgpe1xyXG4gICBcclxuICBpZighVGVhY2hhYmxlTW9iaWxlTmV0LmZlYXR1cmVfYXV4KXtcclxuICAgIGNvbnN0IGZlYXR1cmVzOiBhbnk9W107XHJcbiAgY29uc3QgdGFyZ2V0czogYW55PVtdO1xyXG5cclxuICBmb3IgKGxldCBpID0gMDsgaSA8IFRlYWNoYWJsZU1vYmlsZU5ldC5leGFtcGxlcy5sZW5ndGg7IGkrKykge1xyXG5cclxuICAgIGNvbnN0IHkgPSBmbGF0T25lSG90KGksIFRlYWNoYWJsZU1vYmlsZU5ldC5jbGFzc2VzX25hbWVzLmxlbmd0aCk7XHJcbiAgICBcclxuXHJcblxyXG4gICAgLy9Gb3IgY2xhc3MgaSwgcHVzaCBhbGwgdGhlIGV4YW1wbGVzLlxyXG4gICAgVGVhY2hhYmxlTW9iaWxlTmV0LmV4YW1wbGVzW2ldLmZvckVhY2goKGVsZW1uKT0+e1xyXG5cclxuICAgICAgIC8vUHVzaGluZyB0aGUgdGFyZ2V0IHNpZ25hdHVyZVxyXG4gICAgICAgdGFyZ2V0cy5wdXNoKHkpO1xyXG4gICAgICAgXHJcbiAgICAgICAvL1B1c2hpbmcgZmVhdHVyZXNcclxuICAgICAgIGZlYXR1cmVzLnB1c2goZWxlbW4pXHJcbiAgICB9KVxyXG5cclxuICB9ICBcclxuXHJcbiAgVGVhY2hhYmxlTW9iaWxlTmV0LmZlYXR1cmVfYXV4PSB0Zi50ZW5zb3IoZmVhdHVyZXMpO1xyXG4gIFRlYWNoYWJsZU1vYmlsZU5ldC50YXJnZXRfYXV4PSB0Zi50ZW5zb3IodGFyZ2V0cyk7XHJcblxyXG4gIH0gXHJcblxyXG4gIGNvbnN0IGF1eDogYW55ID0gdGhpcy50cmFpbmluZ01vZGVsLmV2YWx1YXRlKFRlYWNoYWJsZU1vYmlsZU5ldC5mZWF0dXJlX2F1eCwgVGVhY2hhYmxlTW9iaWxlTmV0LnRhcmdldF9hdXgpO1xyXG4gIFxyXG4gIHJldHVybiBhdXhbMV0uZGF0YVN5bmMoKVswXTtcclxuXHJcblxyXG59XHJcblxyXG5cclxuLy8gYXN5bmMgZXZhbHVhdGUoKXtcclxuXHJcbi8vICAgY29uc3QgZmVhdHVyZXM6IGFueT1bXTtcclxuLy8gICBjb25zdCB0YXJnZXRzOiBhbnk9W107XHJcblxyXG4vLyAgIGZvciAobGV0IGkgPSAwOyBpIDwgVGVhY2hhYmxlTW9iaWxlTmV0LmV4YW1wbGVzLmxlbmd0aDsgaSsrKSB7XHJcblxyXG4vLyAgICAgY29uc3QgeSA9IGZsYXRPbmVIb3QoaSwgVGVhY2hhYmxlTW9iaWxlTmV0LmNsYXNzZXNfbmFtZXMubGVuZ3RoKTtcclxuICAgIFxyXG5cclxuXHJcbi8vICAgICAvL0ZvciBjbGFzcyBpLCBwdXNoIGFsbCB0aGUgZXhhbXBsZXMuXHJcbi8vICAgICBUZWFjaGFibGVNb2JpbGVOZXQuZXhhbXBsZXNbaV0uZm9yRWFjaCgoZWxlbW4pPT57XHJcblxyXG4vLyAgICAgICAgLy9QdXNoaW5nIHRoZSB0YXJnZXQgc2lnbmF0dXJlXHJcbi8vICAgICAgICB0YXJnZXRzLnB1c2goeSk7XHJcbiAgICAgICBcclxuLy8gICAgICAgIC8vUHVzaGluZyBmZWF0dXJlc1xyXG4vLyAgICAgICAgZmVhdHVyZXMucHVzaChlbGVtbilcclxuLy8gICAgIH0pXHJcblxyXG4vLyAgIH0gIFxyXG5cclxuLy8gICBjb25zdCBhdXhfZmVhdHVyZXM9IHRmLnRlbnNvcihmZWF0dXJlcyk7XHJcbi8vICAgY29uc3QgYXV4X3RhcmdldD0gdGYudGVuc29yKHRhcmdldHMpO1xyXG5cclxuICBcclxuLy8gICAvLyBjb25zb2xlLmxvZyhcIlRlbnNvciBzdGFjayBmb3IgZXZhbHVhdGlvbjogXCIsIGF1eF9mZWF0dXJlcy5zaGFwZSlcclxuXHJcbi8vICAgY29uc3QgYXV4OiBhbnkgPSB0aGlzLnRyYWluaW5nTW9kZWwuZXZhbHVhdGUoYXV4X2ZlYXR1cmVzLCBhdXhfdGFyZ2V0KTtcclxuXHJcblxyXG4vLyAgIHJldHVybiBhdXhbMV0uZGF0YVN5bmMoKVswXTtcclxuXHJcblxyXG4vLyB9XHJcblxyXG5cclxuLyoqKiBGaW5hbCBzdGF0aXN0aWNzICovXHJcbi8qIFxyXG4gICAgICogQ2FsY3VsYXRlIGVhY2ggY2xhc3MgYWNjdXJhY3kgdXNpbmcgdGhlIHZhbGlkYXRpb24gZGF0YXNldFxyXG4qL1xyXG5wdWJsaWMgYXN5bmMgY2FsY3VsYXRlQWNjdXJhY3lQZXJDbGFzcyh2YWxpZGF0aW9uRGF0YTogYW55KSB7XHJcblxyXG4gIGNvbnN0IHZhbGlkYXRpb25YcyA9IFRlYWNoYWJsZU1vYmlsZU5ldC52YWxpZGF0aW9uRGF0YXNldC5tYXBBc3luYyhhc3luYyAoZGF0YXNldDogVGVuc29yQ29udGFpbmVyKSA9PiB7XHJcbiAgICByZXR1cm4gKGRhdGFzZXQgYXMgeyB4czogVGVuc29yQ29udGFpbmVyLCB5czogVGVuc29yQ29udGFpbmVyfSkueHM7XHJcbn0pO1xyXG5cclxuY29uc3QgdmFsaWRhdGlvbllzID0gVGVhY2hhYmxlTW9iaWxlTmV0LnZhbGlkYXRpb25EYXRhc2V0Lm1hcEFzeW5jKGFzeW5jIChkYXRhc2V0OiBUZW5zb3JDb250YWluZXIpID0+IHtcclxuICAgIHJldHVybiAoZGF0YXNldCBhcyB7IHhzOiBUZW5zb3JDb250YWluZXIsIHlzOiBUZW5zb3JDb250YWluZXJ9KS55cztcclxufSk7XHJcblxyXG4vLyBjb25zb2xlLmxvZyhcInZhbGlkYXRpb24gZGF0YXNldDogXCIsIHZhbGlkYXRpb25Ycyk7XHJcbiBcclxuLy8gY29uc29sZS5sb2coXCJGb3IgY2FsY3VsYXRpbmcgYmF0Y2ggc2l6ZTogXCIsIHZhbGlkYXRpb25Zcyk7XHJcblxyXG4vLyB3ZSBuZWVkIHRvIHNwbGl0IG91ciB2YWxpZGF0aW9uIGRhdGEgaW50byBiYXRjaGVzIGluIGNhc2UgaXQgaXMgdG9vIGxhcmdlIHRvIGZpdCBpbiBtZW1vcnlcclxuY29uc3QgYmF0Y2hTaXplID0gTWF0aC5taW4odmFsaWRhdGlvbllzLnNpemUsIDMyKTtcclxuLy8gY29uc3QgYmF0Y2hTaXplID0xO1xyXG5cclxuY29uc3QgaXRlcmF0aW9ucyA9IE1hdGguY2VpbCh2YWxpZGF0aW9uWXMuc2l6ZSAvIGJhdGNoU2l6ZSk7XHJcblxyXG4vLyBjb25zb2xlLmxvZyhcIkJhdGNoIHNpemU6IFwiLCBiYXRjaFNpemUpO1xyXG5cclxuY29uc3QgYmF0Y2hlc1ggPSB2YWxpZGF0aW9uWHMuYmF0Y2goYmF0Y2hTaXplKTtcclxuXHJcbmNvbnN0IGJhdGNoZXNZID0gdmFsaWRhdGlvbllzLmJhdGNoKGJhdGNoU2l6ZSk7XHJcbmNvbnN0IGl0WCA9IGF3YWl0IGJhdGNoZXNYLml0ZXJhdG9yKCk7XHJcbmNvbnN0IGl0WSA9IGF3YWl0IGJhdGNoZXNZLml0ZXJhdG9yKCk7XHJcbmNvbnN0IGFsbFggPSBbXTtcclxuY29uc3QgYWxsWSA9IFtdO1xyXG5cclxuZm9yIChsZXQgaSA9IDA7IGkgPCBpdGVyYXRpb25zOyBpKyspIHtcclxuXHJcbiAgICAvLyAxLiBnZXQgdGhlIHByZWRpY3Rpb24gdmFsdWVzIGluIGJhdGNoZXNcclxuICAgICBjb25zdCBiYXRjaGVkWFRlbnNvciA9IGF3YWl0IGl0WC5uZXh0KCk7ICBcclxuXHJcbiAgICAvLyAgY29uc29sZS5sb2coXCJCYXRjaCBzaXplIG9uIGFjY3VyYWN5IHBlciBjbGFzczogXCIsIGJhdGNoZWRYVGVuc29yLnZhbHVlLnNoYXBlKTtcclxuXHJcbiAgICBjb25zdCBiYXRjaGVkWFByZWRpY3Rpb25UZW5zb3IgPSB0aGlzLnRyYWluaW5nTW9kZWwucHJlZGljdChiYXRjaGVkWFRlbnNvci52YWx1ZSkgYXMgdGYuVGVuc29yO1xyXG5cclxuICAgIGNvbnN0IGFyZ01heFggPSBiYXRjaGVkWFByZWRpY3Rpb25UZW5zb3IuYXJnTWF4KDEpOyAvLyBSZXR1cm5zIHRoZSBpbmRpY2VzIG9mIHRoZSBtYXggdmFsdWVzIGFsb25nIGFuIGF4aXNcclxuXHJcbiAgICBhbGxYLnB1c2goYXJnTWF4WCk7XHJcblxyXG4gICAgLy8gMi4gZ2V0IHRoZSBncm91bmQgdHJ1dGggbGFiZWwgdmFsdWVzIGluIGJhdGNoZXNcclxuICAgIGNvbnN0IGJhdGNoZWRZVGVuc29yID0gYXdhaXQgaXRZLm5leHQoKTtcclxuICAgIGNvbnN0IGFyZ01heFkgPSBiYXRjaGVkWVRlbnNvci52YWx1ZS5hcmdNYXgoMSk7IC8vIFJldHVybnMgdGhlIGluZGljZXMgb2YgdGhlIG1heCB2YWx1ZXMgYWxvbmcgYW4gYXhpc1xyXG5cclxuICAgIGFsbFkucHVzaChhcmdNYXhZKTtcclxuICAgIFxyXG4gICAgLy8gMy4gZGlzcG9zZSBvZiBhbGwgb3VyIHRlbnNvcnNcclxuICAgICBiYXRjaGVkWFRlbnNvci52YWx1ZS5kaXNwb3NlKCk7XHJcbiAgICAgYmF0Y2hlZFhQcmVkaWN0aW9uVGVuc29yLmRpc3Bvc2UoKTtcclxuICAgICBiYXRjaGVkWVRlbnNvci52YWx1ZS5kaXNwb3NlKCk7XHJcbn1cclxuXHJcbiAgICAgIC8vIGNvbmNhdGVuYXRlIGFsbCB0aGUgcmVzdWx0cyBvZiB0aGUgYmF0Y2hlc1xyXG4gICAgICBjb25zdCByZWZlcmVuY2UgPSB0Zi5jb25jYXQoYWxsWSk7IC8vIHRoaXMgaXMgdGhlIGdyb3VuZCB0cnV0aFxyXG4gICAgICBjb25zdCBwcmVkaWN0aW9ucyA9IHRmLmNvbmNhdChhbGxYKTsgLy8gdGhpcyBpcyB0aGUgcHJlZGljdGlvbiBvdXIgbW9kZWwgaXMgZ3Vlc3NpbmdcclxuICAgICAgXHJcbiAgICAgIC8vIGNvbnNvbGUubG9nKFwidGhpcyBpcyB0aGUgZ3JvdW5kIHRydXRoOiBcIiwgIHJlZmVyZW5jZS5kYXRhU3luYygpKVxyXG4gICAgICAvLyBjb25zb2xlLmxvZyhcIlRoaXMgaXMgdGhlIHByZWRpY3Rpb24gb3VyIG1vZGVsIGlzIGd1ZXNzaW5nOiBcIiwgIHByZWRpY3Rpb25zLmRhdGFTeW5jKCkpXHJcblxyXG4gICAgICAgIC8vIG9ubHkgaWYgd2UgY29uY2F0ZW5hdGVkIG1vcmUgdGhhbiBvbmUgdGVuc29yIGZvciBwcmVmZXJlbmNlIGFuZCByZWZlcmVuY2VcclxuICAgICAgICBpZiAoaXRlcmF0aW9ucyAhPT0gMSkge1xyXG4gICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGFsbFgubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICAgICAgICAgIGFsbFhbaV0uZGlzcG9zZSgpO1xyXG4gICAgICAgICAgICAgICAgYWxsWVtpXS5kaXNwb3NlKCk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgLy8gIGNvbnNvbGUubG9nKFwiTGVuZ3R0aDogXCIsIGF3YWl0IHJlZmVyZW5jZS5kYXRhU3luYygpLmxlbmd0aCkgIFxyXG4gICAgXHJcbiAgICAvLyBjb25zdCBhY2N1cmFjeXBlcmNsYXNzPVtdO1xyXG5cclxuICAgIC8vIGNvbnN0IHJlZmVyZW5jZV9hdXg9IGF3YWl0IHJlZmVyZW5jZS5kYXRhU3luYygpO1xyXG4gICAgLy8gY29uc3QgcHJlZGljdGlvbl9hdXg9IGF3YWl0IHByZWRpY3Rpb25zLmRhdGFTeW5jKCk7XHJcbiAgICAvLyBjb25zb2xlLmxvZyggcHJlZGljdGlvbnMuZGF0YVN5bmMoKSk7XHJcblxyXG4gICAgLy8gcmVmZXJlbmNlX2F1eC5mb3JFYWNoKChlbGVtZW50LCBpbmRleCkgPT4ge1xyXG4gICAgLy8gICBpZigpXHJcbiAgICAgIFxyXG4gICAgLy8gfSk7ICBcclxuXHJcblxyXG4gICAgICAgIHJldHVybiB7IHJlZmVyZW5jZSwgcHJlZGljdGlvbnMgfTsgIFxyXG5cclxufVxyXG5cclxufS8vZW5kIG9mIGNsYXNzXHJcblxyXG5cclxuXHJcblxyXG5cclxuLyoqKlN1cHBvcnQgbWV0aG9kcyAoaGVscGVycykgKi9cclxuXHJcbmNvbnN0IGlzVGVuc29yID0gKGM6IGFueSk6IGMgaXMgdGYuVGVuc29yID0+XHJcbiAgICB0eXBlb2YgYy5kYXRhSWQgPT09ICdvYmplY3QnICYmIHR5cGVvZiBjLnNoYXBlID09PSAnb2JqZWN0JztcclxuLyoqXHJcbiAqIENvbnZlcnRzIGFuIGludGVnZXIgaW50byBpdHMgb25lLWhvdCByZXByZXNlbnRhdGlvbiBhbmQgcmV0dXJuc1xyXG4gKiB0aGUgZGF0YSBhcyBhIEpTIEFycmF5LlxyXG4gKi9cclxuZnVuY3Rpb24gZmxhdE9uZUhvdChsYWJlbDogbnVtYmVyLCBudW1DbGFzc2VzOiBudW1iZXIpIHtcclxuXHJcbiAgY29uc3QgbGFiZWxPbmVIb3QgPSBuZXcgQXJyYXkobnVtQ2xhc3NlcykuZmlsbCgwKSBhcyBudW1iZXJbXTtcclxuICBsYWJlbE9uZUhvdFtsYWJlbF0gPSAxO1xyXG5cclxuICByZXR1cm4gbGFiZWxPbmVIb3Q7XHJcbn1cclxuXHJcbmludGVyZmFjZSBTYW1wbGUge1xyXG4gIGRhdGE6IEZsb2F0MzJBcnJheTtcclxuICBsYWJlbDogbnVtYmVyW107XHJcbn1cclxuXHJcblxyXG4vKipcclxuICogU2h1ZmZsZSBhbiBhcnJheSBvZiBGbG9hdDMyQXJyYXkgb3IgU2FtcGxlcyB1c2luZyBGaXNoZXItWWF0ZXMgYWxnb3JpdGhtXHJcbiAqIFRha2VzIGFuIG9wdGlvbmFsIHNlZWQgdmFsdWUgdG8gbWFrZSBzaHVmZmxpbmcgcHJlZGljdGFibGVcclxuICovXHJcbmZ1bmN0aW9uIGZpc2hlcllhdGVzKGFycmF5OiBGbG9hdDMyQXJyYXlbXSB8IFNhbXBsZVtdLCBzZWVkPzogc2VlZHJhbmRvbS5wcm5nKSB7XHJcbiAgY29uc3QgbGVuZ3RoID0gYXJyYXkubGVuZ3RoO1xyXG5cclxuICAvLyBuZWVkIHRvIGNsb25lIGFycmF5IG9yIHdlJ2QgYmUgZWRpdGluZyBvcmlnaW5hbCBhcyB3ZSBnb29cclxuICBjb25zdCBzaHVmZmxlZCA9IGFycmF5LnNsaWNlKCk7XHJcblxyXG4gIGZvciAobGV0IGkgPSAobGVuZ3RoIC0gMSk7IGkgPiAwOyBpIC09IDEpIHtcclxuICAgICAgbGV0IHJhbmRvbUluZGV4IDtcclxuICAgICAgaWYgKHNlZWQpIHtcclxuICAgICAgICAgIHJhbmRvbUluZGV4ID0gTWF0aC5mbG9vcihzZWVkKCkgKiAoaSArIDEpKTtcclxuICAgICAgfVxyXG4gICAgICBlbHNlIHtcclxuICAgICAgICAgIHJhbmRvbUluZGV4ID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogKGkgKyAxKSk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIFtzaHVmZmxlZFtpXSwgc2h1ZmZsZWRbcmFuZG9tSW5kZXhdXSA9IFtzaHVmZmxlZFtyYW5kb21JbmRleF0sc2h1ZmZsZWRbaV1dO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIHNodWZmbGVkO1xyXG59XHJcbiJdfQ==
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy90ZmpzLWV2b2x1dGlvbi9zcmMvbGliL3V0aWxzL2NsYXNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgaW50ZXJmYWNlIENsYXNzIHtcclxuICAgIGltYWdlczogc3RyaW5nW11cclxuICAgIHNpZ25hdHVyZT86IG51bWJlcltdXHJcbiAgICBuYW1lPzogc3RyaW5nICAgIFxyXG59XHJcbiJdfQ==
@@ -0,0 +1,29 @@
1
+ import * as tf from '@tensorflow/tfjs';
2
+ /**
3
+ * Receives an image and normalizes it between -1 and 1.
4
+ * Returns a batched image (1 - element batch) of shape [1, w, h, c]
5
+ * @param rasterElement the element with pixels to convert to a Tensor
6
+ * @param grayscale optinal flag that changes the crop to [1, w, h, 1]
7
+ */
8
+ export function capture(rasterElement, grayscale) {
9
+ return tf.tidy(() => {
10
+ // console.log("Not a tensor....")
11
+ const pixels = tf.browser.fromPixels(rasterElement);
12
+ // crop the image so we're using the center square
13
+ const cropped = cropTensor(pixels);
14
+ // Expand the outer most dimension so we have a batch size of 1
15
+ const batchedImage = cropped.expandDims(0);
16
+ // Normalize the image between -1 and a1. The image comes in between 0-255
17
+ // so we divide by 127 and subtract 1.
18
+ return batchedImage.toFloat().div(tf.scalar(127)).sub(tf.scalar(1));
19
+ });
20
+ }
21
+ export function cropTensor(img) {
22
+ const size = Math.min(img.shape[0], img.shape[1]);
23
+ const centerHeight = img.shape[0] / 2;
24
+ const beginHeight = centerHeight - (size / 2);
25
+ const centerWidth = img.shape[1] / 2;
26
+ const beginWidth = centerWidth - (size / 2);
27
+ return img.slice([beginHeight, beginWidth, 0], [size, size, 3]);
28
+ }
29
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGYuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy90ZmpzLWV2b2x1dGlvbi9zcmMvbGliL3V0aWxzL3RmLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFdkM7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQWMsT0FBTyxDQUFDLGFBQXNFLEVBQUUsU0FBbUI7SUFDbkgsT0FBTyxFQUFFLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRTtRQUNoQixrQ0FBa0M7UUFDbEMsTUFBTSxNQUFNLEdBQUcsRUFBRSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLENBQUM7UUFFcEQsa0RBQWtEO1FBQ2xELE1BQU0sT0FBTyxHQUFHLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUVuQywrREFBK0Q7UUFDL0QsTUFBTSxZQUFZLEdBQUcsT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUUzQywwRUFBMEU7UUFDMUUsc0NBQXNDO1FBQ3RDLE9BQU8sWUFBWSxDQUFDLE9BQU8sRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN4RSxDQUFDLENBQUMsQ0FBQztBQUNQLENBQUM7QUFJRCxNQUFNLFVBQVUsVUFBVSxDQUFFLEdBQWdCO0lBRXhDLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDbEQsTUFBTSxZQUFZLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDdEMsTUFBTSxXQUFXLEdBQUcsWUFBWSxHQUFHLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQzlDLE1BQU0sV0FBVyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ3JDLE1BQU0sVUFBVSxHQUFHLFdBQVcsR0FBRyxDQUFDLElBQUksR0FBRyxDQUFDLENBQUMsQ0FBQztJQUc1QyxPQUFPLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxXQUFXLEVBQUUsVUFBVSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ3BFLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyB0ZiBmcm9tICdAdGVuc29yZmxvdy90ZmpzJztcclxuXHJcbi8qKlxyXG4gKiBSZWNlaXZlcyBhbiBpbWFnZSBhbmQgbm9ybWFsaXplcyBpdCBiZXR3ZWVuIC0xIGFuZCAxLlxyXG4gKiBSZXR1cm5zIGEgYmF0Y2hlZCBpbWFnZSAoMSAtIGVsZW1lbnQgYmF0Y2gpIG9mIHNoYXBlIFsxLCB3LCBoLCBjXVxyXG4gKiBAcGFyYW0gcmFzdGVyRWxlbWVudCB0aGUgZWxlbWVudCB3aXRoIHBpeGVscyB0byBjb252ZXJ0IHRvIGEgVGVuc29yXHJcbiAqIEBwYXJhbSBncmF5c2NhbGUgb3B0aW5hbCBmbGFnIHRoYXQgY2hhbmdlcyB0aGUgY3JvcCB0byBbMSwgdywgaCwgMV1cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiAgICAgY2FwdHVyZShyYXN0ZXJFbGVtZW50OiBIVE1MSW1hZ2VFbGVtZW50IHwgSFRNTFZpZGVvRWxlbWVudCB8IEhUTUxDYW52YXNFbGVtZW50LCBncmF5c2NhbGU/OiBib29sZWFuKSB7XHJcbiAgICByZXR1cm4gdGYudGlkeSgoKSA9PiB7XHJcbiAgICAgICAgLy8gY29uc29sZS5sb2coXCJOb3QgYSB0ZW5zb3IuLi4uXCIpXHJcbiAgICAgICAgY29uc3QgcGl4ZWxzID0gdGYuYnJvd3Nlci5mcm9tUGl4ZWxzKHJhc3RlckVsZW1lbnQpO1xyXG5cclxuICAgICAgICAvLyBjcm9wIHRoZSBpbWFnZSBzbyB3ZSdyZSB1c2luZyB0aGUgY2VudGVyIHNxdWFyZVxyXG4gICAgICAgIGNvbnN0IGNyb3BwZWQgPSBjcm9wVGVuc29yKHBpeGVscyk7XHJcblxyXG4gICAgICAgIC8vIEV4cGFuZCB0aGUgb3V0ZXIgbW9zdCBkaW1lbnNpb24gc28gd2UgaGF2ZSBhIGJhdGNoIHNpemUgb2YgMVxyXG4gICAgICAgIGNvbnN0IGJhdGNoZWRJbWFnZSA9IGNyb3BwZWQuZXhwYW5kRGltcygwKTtcclxuXHJcbiAgICAgICAgLy8gTm9ybWFsaXplIHRoZSBpbWFnZSBiZXR3ZWVuIC0xIGFuZCBhMS4gVGhlIGltYWdlIGNvbWVzIGluIGJldHdlZW4gMC0yNTVcclxuICAgICAgICAvLyBzbyB3ZSBkaXZpZGUgYnkgMTI3IGFuZCBzdWJ0cmFjdCAxLlxyXG4gICAgICAgIHJldHVybiBiYXRjaGVkSW1hZ2UudG9GbG9hdCgpLmRpdih0Zi5zY2FsYXIoMTI3KSkuc3ViKHRmLnNjYWxhcigxKSk7XHJcbiAgICB9KTtcclxufVxyXG5cclxuXHJcblxyXG5leHBvcnQgZnVuY3Rpb24gY3JvcFRlbnNvciggaW1nOiB0Zi5UZW5zb3IzRCApIDogdGYuVGVuc29yM0Qge1xyXG4gICAgXHJcbiAgICBjb25zdCBzaXplID0gTWF0aC5taW4oaW1nLnNoYXBlWzBdLCBpbWcuc2hhcGVbMV0pO1xyXG4gICAgY29uc3QgY2VudGVySGVpZ2h0ID0gaW1nLnNoYXBlWzBdIC8gMjtcclxuICAgIGNvbnN0IGJlZ2luSGVpZ2h0ID0gY2VudGVySGVpZ2h0IC0gKHNpemUgLyAyKTtcclxuICAgIGNvbnN0IGNlbnRlcldpZHRoID0gaW1nLnNoYXBlWzFdIC8gMjtcclxuICAgIGNvbnN0IGJlZ2luV2lkdGggPSBjZW50ZXJXaWR0aCAtIChzaXplIC8gMik7XHJcbiAgICBcclxuXHJcbiAgICByZXR1cm4gaW1nLnNsaWNlKFtiZWdpbkhlaWdodCwgYmVnaW5XaWR0aCwgMF0sIFtzaXplLCBzaXplLCAzXSk7XHJcbn0iXX0=