tfjs-evolution 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +6 -1
- package/esm2022/lib/components/display-panel/display-panel.component.mjs +32 -5
- package/esm2022/lib/models/custom-mobilenet.mjs +182 -0
- package/esm2022/lib/models/teachable-evolution.mjs +424 -0
- package/esm2022/lib/utils/class.mjs +2 -0
- package/esm2022/lib/utils/tf.mjs +29 -0
- package/esm2022/lib/utils/util.mjs +165 -0
- package/fesm2022/tfjs-evolution.mjs +824 -4
- package/fesm2022/tfjs-evolution.mjs.map +1 -1
- package/lib/components/display-panel/display-panel.component.d.ts +4 -0
- package/lib/models/custom-mobilenet.d.ts +63 -0
- package/lib/models/teachable-evolution.d.ts +101 -0
- package/lib/utils/class.d.ts +5 -0
- package/lib/utils/tf.d.ts +9 -0
- package/lib/utils/util.d.ts +43 -0
- package/package.json +1 -1
@@ -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=
|