UNPKG

46.3 kBJavaScriptView Raw
1/**
2 * @license
3 * Copyright 2020 Google LLC. All Rights Reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 * =============================================================================
16 */
17import { concat, keep, reshape, scalar, slice, stack, tensor, tidy, unstack } from '@tensorflow/tfjs-core';
18import { assertShapesMatchAllowUndefinedSize, inferElementShape, mergeElementShape } from './tensor_utils';
19/**
20 * TensorList stores a container of `tf.Tensor` objects, which are accessible
21 * via tensors field.
22 *
23 * In order to get a copy of the underlying list, use the copy method:
24 * ```
25 * TensorList b = a.copy();
26 * b.tensors().pushBack(t); // This does not modify a.tensors().
27 * ```
28 *
29 * Note that this is not a deep copy: the memory locations of the underlying
30 * tensors will still point to the same locations of the corresponding tensors
31 * in the original.
32 */
33export class TensorList {
34 /**
35 *
36 * @param tensors list of tensors
37 * @param elementShape shape of each tensor, this can be a single number (any
38 * shape is allowed) or partial shape (dim = -1).
39 * @param elementDtype data type of each tensor
40 * @param maxNumElements The maximum allowed size of `tensors`. Defaults to -1
41 * meaning that the size of `tensors` is unbounded.
42 */
43 constructor(tensors, elementShape, elementDtype, maxNumElements = -1) {
44 this.tensors = tensors;
45 this.elementShape = elementShape;
46 this.elementDtype = elementDtype;
47 if (tensors != null) {
48 tensors.forEach(tensor => {
49 if (elementDtype !== tensor.dtype) {
50 throw new Error(`Invalid data types; op elements ${elementDtype}, but list elements ${tensor.dtype}`);
51 }
52 assertShapesMatchAllowUndefinedSize(elementShape, tensor.shape, 'TensorList shape mismatch: ');
53 keep(tensor);
54 });
55 }
56 this.idTensor = scalar(0);
57 this.maxNumElements = maxNumElements;
58 keep(this.idTensor);
59 }
60 get id() {
61 return this.idTensor.id;
62 }
63 /**
64 * Get a new TensorList containing a copy of the underlying tensor container.
65 */
66 copy() {
67 return new TensorList([...this.tensors], this.elementShape, this.elementDtype);
68 }
69 /**
70 * Dispose the tensors and idTensor and clear the tensor list.
71 */
72 clearAndClose(keepIds) {
73 this.tensors.forEach(tensor => {
74 if (keepIds == null || !keepIds.has(tensor.id)) {
75 tensor.dispose();
76 }
77 });
78 this.tensors.length = 0;
79 this.idTensor.dispose();
80 }
81 /**
82 * The size of the tensors in the tensor list.
83 */
84 size() {
85 return this.tensors.length;
86 }
87 /**
88 * Return a tensor that stacks a list of rank-R tf.Tensors into one rank-(R+1)
89 * tf.Tensor.
90 * @param elementShape shape of each tensor
91 * @param elementDtype data type of each tensor
92 * @param numElements the number of elements to stack
93 */
94 stack(elementShape, elementDtype, numElements = -1) {
95 if (elementDtype !== this.elementDtype) {
96 throw new Error(`Invalid data types; op elements ${elementDtype}, but list elements ${this.elementDtype}`);
97 }
98 if (numElements !== -1 && this.tensors.length !== numElements) {
99 throw new Error(`Operation expected a list with ${numElements} elements but got a list with ${this.tensors.length} elements.`);
100 }
101 assertShapesMatchAllowUndefinedSize(elementShape, this.elementShape, 'TensorList shape mismatch: ');
102 const outputElementShape = inferElementShape(this.elementShape, this.tensors, elementShape);
103 return tidy(() => {
104 const reshapedTensors = this.tensors.map(tensor => reshape(tensor, outputElementShape));
105 return stack(reshapedTensors, 0);
106 });
107 }
108 /**
109 * Pop a tensor from the end of the list.
110 * @param elementShape shape of the tensor
111 * @param elementDtype data type of the tensor
112 */
113 popBack(elementShape, elementDtype) {
114 if (elementDtype !== this.elementDtype) {
115 throw new Error(`Invalid data types; op elements ${elementDtype}, but list elements ${this.elementDtype}`);
116 }
117 if (this.size() === 0) {
118 throw new Error('Trying to pop from an empty list.');
119 }
120 const outputElementShape = inferElementShape(this.elementShape, this.tensors, elementShape);
121 const tensor = this.tensors.pop();
122 assertShapesMatchAllowUndefinedSize(tensor.shape, elementShape, 'TensorList shape mismatch: ');
123 return reshape(tensor, outputElementShape);
124 }
125 /**
126 * Push a tensor to the end of the list.
127 * @param tensor Tensor to be pushed.
128 */
129 pushBack(tensor) {
130 if (tensor.dtype !== this.elementDtype) {
131 throw new Error(`Invalid data types; op elements ${tensor.dtype}, but list elements ${this.elementDtype}`);
132 }
133 assertShapesMatchAllowUndefinedSize(tensor.shape, this.elementShape, 'TensorList shape mismatch: ');
134 if (this.maxNumElements === this.size()) {
135 throw new Error(`Trying to push element into a full list.`);
136 }
137 keep(tensor);
138 this.tensors.push(tensor);
139 }
140 /**
141 * Update the size of the list.
142 * @param size the new size of the list.
143 */
144 resize(size) {
145 if (size < 0) {
146 throw new Error(`TensorListResize expects size to be non-negative. Got: ${size}`);
147 }
148 if (this.maxNumElements !== -1 && size > this.maxNumElements) {
149 throw new Error(`TensorListResize input size ${size} is greater maxNumElement ${this.maxNumElements}.`);
150 }
151 this.tensors.length = size;
152 }
153 /**
154 * Retrieve the element at the provided index
155 * @param elementShape shape of the tensor
156 * @param elementDtype dtype of the tensor
157 * @param elementIndex index of the tensor
158 */
159 getItem(elementIndex, elementShape, elementDtype) {
160 if (elementDtype !== this.elementDtype) {
161 throw new Error(`Invalid data types; op elements ${elementDtype}, but list elements ${this.elementDtype}`);
162 }
163 if (elementIndex < 0 || elementIndex > this.tensors.length) {
164 throw new Error(`Trying to access element ${elementIndex} in a list with ${this.tensors.length} elements.`);
165 }
166 if (this.tensors[elementIndex] == null) {
167 throw new Error(`element at index ${elementIndex} is null.`);
168 }
169 assertShapesMatchAllowUndefinedSize(this.tensors[elementIndex].shape, elementShape, 'TensorList shape mismatch: ');
170 const outputElementShape = inferElementShape(this.elementShape, this.tensors, elementShape);
171 return reshape(this.tensors[elementIndex], outputElementShape);
172 }
173 /**
174 * Set the tensor at the index
175 * @param elementIndex index of the tensor
176 * @param tensor the tensor to be inserted into the list
177 */
178 setItem(elementIndex, tensor) {
179 if (tensor.dtype !== this.elementDtype) {
180 throw new Error(`Invalid data types; op elements ${tensor.dtype}, but list elements ${this.elementDtype}`);
181 }
182 if (elementIndex < 0 ||
183 this.maxNumElements !== -1 && elementIndex >= this.maxNumElements) {
184 throw new Error(`Trying to set element ${elementIndex} in a list with max ${this.maxNumElements} elements.`);
185 }
186 assertShapesMatchAllowUndefinedSize(this.elementShape, tensor.shape, 'TensorList shape mismatch: ');
187 keep(tensor);
188 this.tensors[elementIndex] = tensor;
189 }
190 /**
191 * Return selected values in the TensorList as a stacked Tensor. All of
192 * selected values must have been written and their shapes must all match.
193 * @param indices indices of tensors to gather
194 * @param elementDtype output tensor dtype
195 * @param elementShape output tensor element shape
196 */
197 gather(indices, elementDtype, elementShape) {
198 if (elementDtype !== this.elementDtype) {
199 throw new Error(`Invalid data types; op elements ${elementDtype}, but list elements ${this.elementDtype}`);
200 }
201 assertShapesMatchAllowUndefinedSize(this.elementShape, elementShape, 'TensorList shape mismatch: ');
202 // When indices is greater than the size of the list, indices beyond the
203 // size of the list are ignored.
204 indices = indices.slice(0, this.size());
205 const outputElementShape = inferElementShape(this.elementShape, this.tensors, elementShape);
206 if (indices.length === 0) {
207 return tensor([], [0].concat(outputElementShape));
208 }
209 return tidy(() => {
210 const tensors = indices.map(i => reshape(this.tensors[i], outputElementShape));
211 return stack(tensors, 0);
212 });
213 }
214 /**
215 * Return the values in the TensorList as a concatenated Tensor.
216 * @param elementDtype output tensor dtype
217 * @param elementShape output tensor element shape
218 */
219 concat(elementDtype, elementShape) {
220 if (!!elementDtype && elementDtype !== this.elementDtype) {
221 throw new Error(`TensorList dtype is ${this.elementDtype} but concat requested dtype ${elementDtype}`);
222 }
223 assertShapesMatchAllowUndefinedSize(this.elementShape, elementShape, 'TensorList shape mismatch: ');
224 const outputElementShape = inferElementShape(this.elementShape, this.tensors, elementShape);
225 if (this.size() === 0) {
226 return tensor([], [0].concat(outputElementShape));
227 }
228 return tidy(() => {
229 const tensors = this.tensors.map(t => reshape(t, outputElementShape));
230 return concat(tensors, 0);
231 });
232 }
233}
234/**
235 * Creates a TensorList which, when stacked, has the value of tensor.
236 * @param tensor from tensor
237 * @param elementShape output tensor element shape
238 */
239export function fromTensor(tensor, elementShape, elementDtype) {
240 const dtype = tensor.dtype;
241 if (tensor.shape.length < 1) {
242 throw new Error(`Tensor must be at least a vector, but saw shape: ${tensor.shape}`);
243 }
244 if (tensor.dtype !== elementDtype) {
245 throw new Error(`Invalid data types; op elements ${tensor.dtype}, but list elements ${elementDtype}`);
246 }
247 const tensorElementShape = tensor.shape.slice(1);
248 assertShapesMatchAllowUndefinedSize(tensorElementShape, elementShape, 'TensorList shape mismatch: ');
249 const tensorList = unstack(tensor);
250 return new TensorList(tensorList, elementShape, dtype);
251}
252/**
253 * Return a TensorList of the given size with empty elements.
254 * @param elementShape the shape of the future elements of the list
255 * @param elementDtype the desired type of elements in the list
256 * @param numElements the number of elements to reserve
257 */
258export function reserve(elementShape, elementDtype, numElements) {
259 return new TensorList([], elementShape, elementDtype, numElements);
260}
261/**
262 * Put tensors at specific indices of a stacked tensor into a TensorList.
263 * @param indices list of indices on how to scatter the tensor.
264 * @param tensor input tensor.
265 * @param elementShape the shape of the future elements of the list
266 * @param numElements the number of elements to scatter
267 */
268export function scatter(tensor, indices, elementShape, numElements) {
269 if (indices.length !== tensor.shape[0]) {
270 throw new Error(`Expected len(indices) == tensor.shape[0], but saw: ${indices.length} vs. ${tensor.shape[0]}`);
271 }
272 const maxIndex = Math.max(...indices);
273 if (numElements != null && numElements !== -1 && maxIndex >= numElements) {
274 throw new Error(`Max index must be < array size (${maxIndex} vs. ${numElements})`);
275 }
276 const list = new TensorList([], elementShape, tensor.dtype, numElements);
277 const tensors = unstack(tensor, 0);
278 indices.forEach((value, index) => {
279 list.setItem(value, tensors[index]);
280 });
281 return list;
282}
283/**
284 * Split the values of a Tensor into a TensorList.
285 * @param length the lengths to use when splitting value along
286 * its first dimension.
287 * @param tensor the tensor to split.
288 * @param elementShape the shape of the future elements of the list
289 */
290export function split(tensor, length, elementShape) {
291 let totalLength = 0;
292 const cumulativeLengths = length.map(len => {
293 totalLength += len;
294 return totalLength;
295 });
296 if (totalLength !== tensor.shape[0]) {
297 throw new Error(`Expected sum of lengths to be equal to
298 tensor.shape[0], but sum of lengths is
299 ${totalLength}, and tensor's shape is: ${tensor.shape}`);
300 }
301 const shapeWithoutFirstDim = tensor.shape.slice(1);
302 const outputElementShape = mergeElementShape(shapeWithoutFirstDim, elementShape);
303 const elementPerRow = totalLength === 0 ? 0 : tensor.size / totalLength;
304 const tensors = tidy(() => {
305 const tensors = [];
306 tensor = reshape(tensor, [1, totalLength, elementPerRow]);
307 for (let i = 0; i < length.length; ++i) {
308 const previousLength = (i === 0) ? 0 : cumulativeLengths[i - 1];
309 const indices = [0, previousLength, 0];
310 const sizes = [1, length[i], elementPerRow];
311 tensors[i] = reshape(slice(tensor, indices, sizes), outputElementShape);
312 }
313 tensor.dispose();
314 return tensors;
315 });
316 const list = new TensorList([], elementShape, tensor.dtype, length.length);
317 for (let i = 0; i < tensors.length; i++) {
318 list.setItem(i, tensors[i]);
319 }
320 return list;
321}
322//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuc29yX2xpc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi90ZmpzLWNvbnZlcnRlci9zcmMvZXhlY3V0b3IvdGVuc29yX2xpc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBRUgsT0FBTyxFQUFDLE1BQU0sRUFBWSxJQUFJLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFVLE1BQU0sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFDLE1BQU0sdUJBQXVCLENBQUM7QUFFM0gsT0FBTyxFQUFDLG1DQUFtQyxFQUFFLGlCQUFpQixFQUFFLGlCQUFpQixFQUFDLE1BQU0sZ0JBQWdCLENBQUM7QUFFekc7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUVILE1BQU0sT0FBTyxVQUFVO0lBT3JCOzs7Ozs7OztPQVFHO0lBQ0gsWUFDYSxPQUFpQixFQUFXLFlBQTZCLEVBQ3pELFlBQXNCLEVBQUUsY0FBYyxHQUFHLENBQUMsQ0FBQztRQUQzQyxZQUFPLEdBQVAsT0FBTyxDQUFVO1FBQVcsaUJBQVksR0FBWixZQUFZLENBQWlCO1FBQ3pELGlCQUFZLEdBQVosWUFBWSxDQUFVO1FBQ2pDLElBQUksT0FBTyxJQUFJLElBQUksRUFBRTtZQUNuQixPQUFPLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxFQUFFO2dCQUN2QixJQUFJLFlBQVksS0FBSyxNQUFNLENBQUMsS0FBSyxFQUFFO29CQUNqQyxNQUFNLElBQUksS0FBSyxDQUFDLG1DQUNaLFlBQVksdUJBQXVCLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO2lCQUN4RDtnQkFDRCxtQ0FBbUMsQ0FDL0IsWUFBWSxFQUFFLE1BQU0sQ0FBQyxLQUFLLEVBQUUsNkJBQTZCLENBQUMsQ0FBQztnQkFFL0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQ2YsQ0FBQyxDQUFDLENBQUM7U0FDSjtRQUNELElBQUks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QyxDQUFDO1lBQzVDLE9BQU8sQ0FBQyxDQUFDLENBQUMsR0FBRyxPQUFPLENBQ2hCLEtBQUssQ0FBQyxNQUFNLEVBQUUsT0FBTyxFQUFFLEtBQUssQ0FBQyxFQUFFLGtCQUE4QixDQUFDLENBQUM7U0FDcEU7UUFDRCxNQUFNLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDakIsT0FBTyxPQUFPLENBQUM7SUFDakIsQ0FBQyxDQUFDLENBQUM7SUFFSCxNQUFNLElBQUksR0FBRyxJQUFJLFVBQVUsQ0FBQyxFQUFFLEVBQUUsWUFBWSxFQUFFLE1BQU0sQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBRTNFLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxPQUFPLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFO1FBQ3ZDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0tBQzdCO0lBQ0QsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IDIwMjAgR29vZ2xlIExMQy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqL1xuXG5pbXBvcnQge2NvbmNhdCwgRGF0YVR5cGUsIGtlZXAsIHJlc2hhcGUsIHNjYWxhciwgc2xpY2UsIHN0YWNrLCBUZW5zb3IsIHRlbnNvciwgdGlkeSwgdW5zdGFja30gZnJvbSAnQHRlbnNvcmZsb3cvdGZqcy1jb3JlJztcblxuaW1wb3J0IHthc3NlcnRTaGFwZXNNYXRjaEFsbG93VW5kZWZpbmVkU2l6ZSwgaW5mZXJFbGVtZW50U2hhcGUsIG1lcmdlRWxlbWVudFNoYXBlfSBmcm9tICcuL3RlbnNvcl91dGlscyc7XG5cbi8qKlxuICogVGVuc29yTGlzdCBzdG9yZXMgYSBjb250YWluZXIgb2YgYHRmLlRlbnNvcmAgb2JqZWN0cywgd2hpY2ggYXJlIGFjY2Vzc2libGVcbiAqIHZpYSB0ZW5zb3JzIGZpZWxkLlxuICpcbiAqIEluIG9yZGVyIHRvIGdldCBhIGNvcHkgb2YgdGhlIHVuZGVybHlpbmcgbGlzdCwgdXNlIHRoZSBjb3B5IG1ldGhvZDpcbiAqIGBgYFxuICogICAgVGVuc29yTGlzdCBiID0gYS5jb3B5KCk7XG4gKiAgICBiLnRlbnNvcnMoKS5wdXNoQmFjayh0KTsgIC8vIFRoaXMgZG9lcyBub3QgbW9kaWZ5IGEudGVuc29ycygpLlxuICogYGBgXG4gKlxuICogTm90ZSB0aGF0IHRoaXMgaXMgbm90IGEgZGVlcCBjb3B5OiB0aGUgbWVtb3J5IGxvY2F0aW9ucyBvZiB0aGUgdW5kZXJseWluZ1xuICogdGVuc29ycyB3aWxsIHN0aWxsIHBvaW50IHRvIHRoZSBzYW1lIGxvY2F0aW9ucyBvZiB0aGUgY29ycmVzcG9uZGluZyB0ZW5zb3JzXG4gKiBpbiB0aGUgb3JpZ2luYWwuXG4gKi9cblxuZXhwb3J0IGNsYXNzIFRlbnNvckxpc3Qge1xuICByZWFkb25seSBpZFRlbnNvcjogVGVuc29yO1xuICBtYXhOdW1FbGVtZW50czogbnVtYmVyO1xuXG4gIGdldCBpZCgpIHtcbiAgICByZXR1cm4gdGhpcy5pZFRlbnNvci5pZDtcbiAgfVxuICAvKipcbiAgICpcbiAgICogQHBhcmFtIHRlbnNvcnMgbGlzdCBvZiB0ZW5zb3JzXG4gICAqIEBwYXJhbSBlbGVtZW50U2hhcGUgc2hhcGUgb2YgZWFjaCB0ZW5zb3IsIHRoaXMgY2FuIGJlIGEgc2luZ2xlIG51bWJlciAoYW55XG4gICAqIHNoYXBlIGlzIGFsbG93ZWQpIG9yIHBhcnRpYWwgc2hhcGUgKGRpbSA9IC0xKS5cbiAgICogQHBhcmFtIGVsZW1lbnREdHlwZSBkYXRhIHR5cGUgb2YgZWFjaCB0ZW5zb3JcbiAgICogQHBhcmFtIG1heE51bUVsZW1lbnRzIFRoZSBtYXhpbXVtIGFsbG93ZWQgc2l6ZSBvZiBgdGVuc29yc2AuIERlZmF1bHRzIHRvIC0xXG4gICAqICAgbWVhbmluZyB0aGF0IHRoZSBzaXplIG9mIGB0ZW5zb3JzYCBpcyB1bmJvdW5kZWQuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHJlYWRvbmx5IHRlbnNvcnM6IFRlbnNvcltdLCByZWFkb25seSBlbGVtZW50U2hhcGU6IG51bWJlcnxudW1iZXJbXSxcbiAgICAgIHJlYWRvbmx5IGVsZW1lbnREdHlwZTogRGF0YVR5cGUsIG1heE51bUVsZW1lbnRzID0gLTEpIHtcbiAgICBpZiAodGVuc29ycyAhPSBudWxsKSB7XG4gICAgICB0ZW5zb3JzLmZvckVhY2godGVuc29yID0+IHtcbiAgICAgICAgaWYgKGVsZW1lbnREdHlwZSAhPT0gdGVuc29yLmR0eXBlKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGRhdGEgdHlwZXM7IG9wIGVsZW1lbnRzICR7XG4gICAgICAgICAgICAgIGVsZW1lbnREdHlwZX0sIGJ1dCBsaXN0IGVsZW1lbnRzICR7dGVuc29yLmR0eXBlfWApO1xuICAgICAgICB9XG4gICAgICAgIGFzc2VydFNoYXBlc01hdGNoQWxsb3dVbmRlZmluZWRTaXplKFxuICAgICAgICAgICAgZWxlbWVudFNoYXBlLCB0ZW5zb3Iuc2hhcGUsICdUZW5zb3JMaXN0IHNoYXBlIG1pc21hdGNoOiAnKTtcblxuICAgICAgICBrZWVwKHRlbnNvcik7XG4gICAgICB9KTtcbiAgICB9XG4gICAgdGhpcy5pZFRlbnNvciA9IHNjYWxhcigwKTtcbiAgICB0aGlzLm1heE51bUVsZW1lbnRzID0gbWF4TnVtRWxlbWVudHM7XG4gICAga2VlcCh0aGlzLmlkVGVuc29yKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgYSBuZXcgVGVuc29yTGlzdCBjb250YWluaW5nIGEgY29weSBvZiB0aGUgdW5kZXJseWluZyB0ZW5zb3IgY29udGFpbmVyLlxuICAgKi9cbiAgY29weSgpOiBUZW5zb3JMaXN0IHtcbiAgICByZXR1cm4gbmV3IFRlbnNvckxpc3QoXG4gICAgICAgIFsuLi50aGlzLnRlbnNvcnNdLCB0aGlzLmVsZW1lbnRTaGFwZSwgdGhpcy5lbGVtZW50RHR5cGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIERpc3Bvc2UgdGhlIHRlbnNvcnMgYW5kIGlkVGVuc29yIGFuZCBjbGVhciB0aGUgdGVuc29yIGxpc3QuXG4gICAqL1xuICBjbGVhckFuZENsb3NlKGtlZXBJZHM/OiBTZXQ8bnVtYmVyPikge1xuICAgIHRoaXMudGVuc29ycy5mb3JFYWNoKHRlbnNvciA9PiB7XG4gICAgICBpZiAoa2VlcElkcyA9PSBudWxsIHx8ICFrZWVwSWRzLmhhcyh0ZW5zb3IuaWQpKSB7XG4gICAgICAgIHRlbnNvci5kaXNwb3NlKCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgdGhpcy50ZW5zb3JzLmxlbmd0aCA9IDA7XG4gICAgdGhpcy5pZFRlbnNvci5kaXNwb3NlKCk7XG4gIH1cbiAgLyoqXG4gICAqIFRoZSBzaXplIG9mIHRoZSB0ZW5zb3JzIGluIHRoZSB0ZW5zb3IgbGlzdC5cbiAgICovXG4gIHNpemUoKSB7XG4gICAgcmV0dXJuIHRoaXMudGVuc29ycy5sZW5ndGg7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJuIGEgdGVuc29yIHRoYXQgc3RhY2tzIGEgbGlzdCBvZiByYW5rLVIgdGYuVGVuc29ycyBpbnRvIG9uZSByYW5rLShSKzEpXG4gICAqIHRmLlRlbnNvci5cbiAgICogQHBhcmFtIGVsZW1lbnRTaGFwZSBzaGFwZSBvZiBlYWNoIHRlbnNvclxuICAgKiBAcGFyYW0gZWxlbWVudER0eXBlIGRhdGEgdHlwZSBvZiBlYWNoIHRlbnNvclxuICAgKiBAcGFyYW0gbnVtRWxlbWVudHMgdGhlIG51bWJlciBvZiBlbGVtZW50cyB0byBzdGFja1xuICAgKi9cbiAgc3RhY2soZWxlbWVudFNoYXBlOiBudW1iZXJbXSwgZWxlbWVudER0eXBlOiBEYXRhVHlwZSwgbnVtRWxlbWVudHMgPSAtMSk6XG4gICAgICBUZW5zb3Ige1xuICAgIGlmIChlbGVtZW50RHR5cGUgIT09IHRoaXMuZWxlbWVudER0eXBlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgZGF0YSB0eXBlczsgb3AgZWxlbWVudHMgJHtcbiAgICAgICAgICBlbGVtZW50RHR5cGV9LCBidXQgbGlzdCBlbGVtZW50cyAke3RoaXMuZWxlbWVudER0eXBlfWApO1xuICAgIH1cbiAgICBpZiAobnVtRWxlbWVudHMgIT09IC0xICYmIHRoaXMudGVuc29ycy5sZW5ndGggIT09IG51bUVsZW1lbnRzKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYE9wZXJhdGlvbiBleHBlY3RlZCBhIGxpc3Qgd2l0aCAke1xuICAgICAgICAgIG51bUVsZW1lbnRzfSBlbGVtZW50cyBidXQgZ290IGEgbGlzdCB3aXRoICR7XG4gICAgICAgICAgdGhpcy50ZW5zb3JzLmxlbmd0aH0gZWxlbWVudHMuYCk7XG4gICAgfVxuICAgIGFzc2VydFNoYXBlc01hdGNoQWxsb3dVbmRlZmluZWRTaXplKFxuICAgICAgICBlbGVtZW50U2hhcGUsIHRoaXMuZWxlbWVudFNoYXBlLCAnVGVuc29yTGlzdCBzaGFwZSBtaXNtYXRjaDogJyk7XG4gICAgY29uc3Qgb3V0cHV0RWxlbWVudFNoYXBlID1cbiAgICAgICAgaW5mZXJFbGVtZW50U2hhcGUodGhpcy5lbGVtZW50U2hhcGUsIHRoaXMudGVuc29ycywgZWxlbWVudFNoYXBlKTtcbiAgICByZXR1cm4gdGlkeSgoKSA9PiB7XG4gICAgICBjb25zdCByZXNoYXBlZFRlbnNvcnMgPVxuICAgICAgICAgIHRoaXMudGVuc29ycy5tYXAodGVuc29yID0+IHJlc2hhcGUodGVuc29yLCBvdXRwdXRFbGVtZW50U2hhcGUpKTtcbiAgICAgIHJldHVybiBzdGFjayhyZXNoYXBlZFRlbnNvcnMsIDApO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFBvcCBhIHRlbnNvciBmcm9tIHRoZSBlbmQgb2YgdGhlIGxpc3QuXG4gICAqIEBwYXJhbSBlbGVtZW50U2hhcGUgc2hhcGUgb2YgdGhlIHRlbnNvclxuICAgKiBAcGFyYW0gZWxlbWVudER0eXBlIGRhdGEgdHlwZSBvZiB0aGUgdGVuc29yXG4gICAqL1xuICBwb3BCYWNrKGVsZW1lbnRTaGFwZTogbnVtYmVyW10sIGVsZW1lbnREdHlwZTogRGF0YVR5cGUpOiBUZW5zb3Ige1xuICAgIGlmIChlbGVtZW50RHR5cGUgIT09IHRoaXMuZWxlbWVudER0eXBlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgZGF0YSB0eXBlczsgb3AgZWxlbWVudHMgJHtcbiAgICAgICAgICBlbGVtZW50RHR5cGV9LCBidXQgbGlzdCBlbGVtZW50cyAke3RoaXMuZWxlbWVudER0eXBlfWApO1xuICAgIH1cblxuICAgIGlmICh0aGlzLnNpemUoKSA9PT0gMCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdUcnlpbmcgdG8gcG9wIGZyb20gYW4gZW1wdHkgbGlzdC4nKTtcbiAgICB9XG4gICAgY29uc3Qgb3V0cHV0RWxlbWVudFNoYXBlID1cbiAgICAgICAgaW5mZXJFbGVtZW50U2hhcGUodGhpcy5lbGVtZW50U2hhcGUsIHRoaXMudGVuc29ycywgZWxlbWVudFNoYXBlKTtcbiAgICBjb25zdCB0ZW5zb3IgPSB0aGlzLnRlbnNvcnMucG9wKCk7XG5cbiAgICBhc3NlcnRTaGFwZXNNYXRjaEFsbG93VW5kZWZpbmVkU2l6ZShcbiAgICAgICAgdGVuc29yLnNoYXBlLCBlbGVtZW50U2hhcGUsICdUZW5zb3JMaXN0IHNoYXBlIG1pc21hdGNoOiAnKTtcblxuICAgIHJldHVybiByZXNoYXBlKHRlbnNvciwgb3V0cHV0RWxlbWVudFNoYXBlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQdXNoIGEgdGVuc29yIHRvIHRoZSBlbmQgb2YgdGhlIGxpc3QuXG4gICAqIEBwYXJhbSB0ZW5zb3IgVGVuc29yIHRvIGJlIHB1c2hlZC5cbiAgICovXG4gIHB1c2hCYWNrKHRlbnNvcjogVGVuc29yKSB7XG4gICAgaWYgKHRlbnNvci5kdHlwZSAhPT0gdGhpcy5lbGVtZW50RHR5cGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBkYXRhIHR5cGVzOyBvcCBlbGVtZW50cyAke1xuICAgICAgICAgIHRlbnNvci5kdHlwZX0sIGJ1dCBsaXN0IGVsZW1lbnRzICR7dGhpcy5lbGVtZW50RHR5cGV9YCk7XG4gICAgfVxuXG4gICAgYXNzZXJ0U2hhcGVzTWF0Y2hBbGxvd1VuZGVmaW5lZFNpemUoXG4gICAgICAgIHRlbnNvci5zaGFwZSwgdGhpcy5lbGVtZW50U2hhcGUsICdUZW5zb3JMaXN0IHNoYXBlIG1pc21hdGNoOiAnKTtcblxuICAgIGlmICh0aGlzLm1heE51bUVsZW1lbnRzID09PSB0aGlzLnNpemUoKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBUcnlpbmcgdG8gcHVzaCBlbGVtZW50IGludG8gYSBmdWxsIGxpc3QuYCk7XG4gICAgfVxuICAgIGtlZXAodGVuc29yKTtcbiAgICB0aGlzLnRlbnNvcnMucHVzaCh0ZW5zb3IpO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZSB0aGUgc2l6ZSBvZiB0aGUgbGlzdC5cbiAgICogQHBhcmFtIHNpemUgdGhlIG5ldyBzaXplIG9mIHRoZSBsaXN0LlxuICAgKi9cbiAgcmVzaXplKHNpemU6IG51bWJlcikge1xuICAgIGlmIChzaXplIDwgMCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGBUZW5zb3JMaXN0UmVzaXplIGV4cGVjdHMgc2l6ZSB0byBiZSBub24tbmVnYXRpdmUuIEdvdDogJHtzaXplfWApO1xuICAgIH1cblxuICAgIGlmICh0aGlzLm1heE51bUVsZW1lbnRzICE9PSAtMSAmJiBzaXplID4gdGhpcy5tYXhOdW1FbGVtZW50cykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBUZW5zb3JMaXN0UmVzaXplIGlucHV0IHNpemUgJHtcbiAgICAgICAgICBzaXplfSBpcyBncmVhdGVyIG1heE51bUVsZW1lbnQgJHt0aGlzLm1heE51bUVsZW1lbnRzfS5gKTtcbiAgICB9XG4gICAgdGhpcy50ZW5zb3JzLmxlbmd0aCA9IHNpemU7XG4gIH1cblxuICAvKipcbiAgICogUmV0cmlldmUgdGhlIGVsZW1lbnQgYXQgdGhlIHByb3ZpZGVkIGluZGV4XG4gICAqIEBwYXJhbSBlbGVtZW50U2hhcGUgc2hhcGUgb2YgdGhlIHRlbnNvclxuICAgKiBAcGFyYW0gZWxlbWVudER0eXBlIGR0eXBlIG9mIHRoZSB0ZW5zb3JcbiAgICogQHBhcmFtIGVsZW1lbnRJbmRleCBpbmRleCBvZiB0aGUgdGVuc29yXG4gICAqL1xuICBnZXRJdGVtKGVsZW1lbnRJbmRleDogbnVtYmVyLCBlbGVtZW50U2hhcGU6IG51bWJlcltdLCBlbGVtZW50RHR5cGU6IERhdGFUeXBlKTpcbiAgICAgIFRlbnNvciB7XG4gICAgaWYgKGVsZW1lbnREdHlwZSAhPT0gdGhpcy5lbGVtZW50RHR5cGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBkYXRhIHR5cGVzOyBvcCBlbGVtZW50cyAke1xuICAgICAgICAgIGVsZW1lbnREdHlwZX0sIGJ1dCBsaXN0IGVsZW1lbnRzICR7dGhpcy5lbGVtZW50RHR5cGV9YCk7XG4gICAgfVxuICAgIGlmIChlbGVtZW50SW5kZXggPCAwIHx8IGVsZW1lbnRJbmRleCA+IHRoaXMudGVuc29ycy5sZW5ndGgpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgVHJ5aW5nIHRvIGFjY2VzcyBlbGVtZW50ICR7XG4gICAgICAgICAgZWxlbWVudEluZGV4fSBpbiBhIGxpc3Qgd2l0aCAke3RoaXMudGVuc29ycy5sZW5ndGh9IGVsZW1lbnRzLmApO1xuICAgIH1cblxuICAgIGlmICh0aGlzLnRlbnNvcnNbZWxlbWVudEluZGV4XSA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYGVsZW1lbnQgYXQgaW5kZXggJHtlbGVtZW50SW5kZXh9IGlzIG51bGwuYCk7XG4gICAgfVxuXG4gICAgYXNzZXJ0U2hhcGVzTWF0Y2hBbGxvd1VuZGVmaW5lZFNpemUoXG4gICAgICAgIHRoaXMudGVuc29yc1tlbGVtZW50SW5kZXhdLnNoYXBlLCBlbGVtZW50U2hhcGUsXG4gICAgICAgICdUZW5zb3JMaXN0IHNoYXBlIG1pc21hdGNoOiAnKTtcbiAgICBjb25zdCBvdXRwdXRFbGVtZW50U2hhcGUgPVxuICAgICAgICBpbmZlckVsZW1lbnRTaGFwZSh0aGlzLmVsZW1lbnRTaGFwZSwgdGhpcy50ZW5zb3JzLCBlbGVtZW50U2hhcGUpO1xuICAgIHJldHVybiByZXNoYXBlKHRoaXMudGVuc29yc1tlbGVtZW50SW5kZXhdLCBvdXRwdXRFbGVtZW50U2hhcGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldCB0aGUgdGVuc29yIGF0IHRoZSBpbmRleFxuICAgKiBAcGFyYW0gZWxlbWVudEluZGV4IGluZGV4IG9mIHRoZSB0ZW5zb3JcbiAgICogQHBhcmFtIHRlbnNvciB0aGUgdGVuc29yIHRvIGJlIGluc2VydGVkIGludG8gdGhlIGxpc3RcbiAgICovXG4gIHNldEl0ZW0oZWxlbWVudEluZGV4OiBudW1iZXIsIHRlbnNvcjogVGVuc29yKSB7XG4gICAgaWYgKHRlbnNvci5kdHlwZSAhPT0gdGhpcy5lbGVtZW50RHR5cGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBkYXRhIHR5cGVzOyBvcCBlbGVtZW50cyAke1xuICAgICAgICAgIHRlbnNvci5kdHlwZX0sIGJ1dCBsaXN0IGVsZW1lbnRzICR7dGhpcy5lbGVtZW50RHR5cGV9YCk7XG4gICAgfVxuXG4gICAgaWYgKGVsZW1lbnRJbmRleCA8IDAgfHxcbiAgICAgICAgdGhpcy5tYXhOdW1FbGVtZW50cyAhPT0gLTEgJiYgZWxlbWVudEluZGV4ID49IHRoaXMubWF4TnVtRWxlbWVudHMpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgVHJ5aW5nIHRvIHNldCBlbGVtZW50ICR7XG4gICAgICAgICAgZWxlbWVudEluZGV4fSBpbiBhIGxpc3Qgd2l0aCBtYXggJHt0aGlzLm1heE51bUVsZW1lbnRzfSBlbGVtZW50cy5gKTtcbiAgICB9XG5cbiAgICBhc3NlcnRTaGFwZXNNYXRjaEFsbG93VW5kZWZpbmVkU2l6ZShcbiAgICAgICAgdGhpcy5lbGVtZW50U2hhcGUsIHRlbnNvci5zaGFwZSwgJ1RlbnNvckxpc3Qgc2hhcGUgbWlzbWF0Y2g6ICcpO1xuICAgIGtlZXAodGVuc29yKTtcbiAgICB0aGlzLnRlbnNvcnNbZWxlbWVudEluZGV4XSA9IHRlbnNvcjtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm4gc2VsZWN0ZWQgdmFsdWVzIGluIHRoZSBUZW5zb3JMaXN0IGFzIGEgc3RhY2tlZCBUZW5zb3IuIEFsbCBvZlxuICAgKiBzZWxlY3RlZCB2YWx1ZXMgbXVzdCBoYXZlIGJlZW4gd3JpdHRlbiBhbmQgdGhlaXIgc2hhcGVzIG11c3QgYWxsIG1hdGNoLlxuICAgKiBAcGFyYW0gaW5kaWNlcyBpbmRpY2VzIG9mIHRlbnNvcnMgdG8gZ2F0aGVyXG4gICAqIEBwYXJhbSBlbGVtZW50RHR5cGUgb3V0cHV0IHRlbnNvciBkdHlwZVxuICAgKiBAcGFyYW0gZWxlbWVudFNoYXBlIG91dHB1dCB0ZW5zb3IgZWxlbWVudCBzaGFwZVxuICAgKi9cbiAgZ2F0aGVyKGluZGljZXM6IG51bWJlcltdLCBlbGVtZW50RHR5cGU6IERhdGFUeXBlLCBlbGVtZW50U2hhcGU6IG51bWJlcltdKTpcbiAgICAgIFRlbnNvciB7XG4gICAgaWYgKGVsZW1lbnREdHlwZSAhPT0gdGhpcy5lbGVtZW50RHR5cGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBkYXRhIHR5cGVzOyBvcCBlbGVtZW50cyAke1xuICAgICAgICAgIGVsZW1lbnREdHlwZX0sIGJ1dCBsaXN0IGVsZW1lbnRzICR7dGhpcy5lbGVtZW50RHR5cGV9YCk7XG4gICAgfVxuXG4gICAgYXNzZXJ0U2hhcGVzTWF0Y2hBbGxvd1VuZGVmaW5lZFNpemUoXG4gICAgICAgIHRoaXMuZWxlbWVudFNoYXBlLCBlbGVtZW50U2hhcGUsICdUZW5zb3JMaXN0IHNoYXBlIG1pc21hdGNoOiAnKTtcblxuICAgIC8vIFdoZW4gaW5kaWNlcyBpcyBncmVhdGVyIHRoYW4gdGhlIHNpemUgb2YgdGhlIGxpc3QsIGluZGljZXMgYmV5b25kIHRoZVxuICAgIC8vIHNpemUgb2YgdGhlIGxpc3QgYXJlIGlnbm9yZWQuXG4gICAgaW5kaWNlcyA9IGluZGljZXMuc2xpY2UoMCwgdGhpcy5zaXplKCkpO1xuICAgIGNvbnN0IG91dHB1dEVsZW1lbnRTaGFwZSA9XG4gICAgICAgIGluZmVyRWxlbWVudFNoYXBlKHRoaXMuZWxlbWVudFNoYXBlLCB0aGlzLnRlbnNvcnMsIGVsZW1lbnRTaGFwZSk7XG4gICAgaWYgKGluZGljZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm4gdGVuc29yKFtdLCBbMF0uY29uY2F0KG91dHB1dEVsZW1lbnRTaGFwZSkpO1xuICAgIH1cblxuICAgIHJldHVybiB0aWR5KCgpID0+IHtcbiAgICAgIGNvbnN0IHRlbnNvcnMgPVxuICAgICAgICAgIGluZGljZXMubWFwKGkgPT4gcmVzaGFwZSh0aGlzLnRlbnNvcnNbaV0sIG91dHB1dEVsZW1lbnRTaGFwZSkpO1xuICAgICAgcmV0dXJuIHN0YWNrKHRlbnNvcnMsIDApO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybiB0aGUgdmFsdWVzIGluIHRoZSBUZW5zb3JMaXN0IGFzIGEgY29uY2F0ZW5hdGVkIFRlbnNvci5cbiAgICogQHBhcmFtIGVsZW1lbnREdHlwZSBvdXRwdXQgdGVuc29yIGR0eXBlXG4gICAqIEBwYXJhbSBlbGVtZW50U2hhcGUgb3V0cHV0IHRlbnNvciBlbGVtZW50IHNoYXBlXG4gICAqL1xuICBjb25jYXQoZWxlbWVudER0eXBlOiBEYXRhVHlwZSwgZWxlbWVudFNoYXBlOiBudW1iZXJbXSk6IFRlbnNvciB7XG4gICAgaWYgKCEhZWxlbWVudER0eXBlICYmIGVsZW1lbnREdHlwZSAhPT0gdGhpcy5lbGVtZW50RHR5cGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgVGVuc29yTGlzdCBkdHlwZSBpcyAke1xuICAgICAgICAgIHRoaXMuZWxlbWVudER0eXBlfSBidXQgY29uY2F0IHJlcXVlc3RlZCBkdHlwZSAke2VsZW1lbnREdHlwZX1gKTtcbiAgICB9XG5cbiAgICBhc3NlcnRTaGFwZXNNYXRjaEFsbG93VW5kZWZpbmVkU2l6ZShcbiAgICAgICAgdGhpcy5lbGVtZW50U2hhcGUsIGVsZW1lbnRTaGFwZSwgJ1RlbnNvckxpc3Qgc2hhcGUgbWlzbWF0Y2g6ICcpO1xuICAgIGNvbnN0IG91dHB1dEVsZW1lbnRTaGFwZSA9XG4gICAgICAgIGluZmVyRWxlbWVudFNoYXBlKHRoaXMuZWxlbWVudFNoYXBlLCB0aGlzLnRlbnNvcnMsIGVsZW1lbnRTaGFwZSk7XG5cbiAgICBpZiAodGhpcy5zaXplKCkgPT09IDApIHtcbiAgICAgIHJldHVybiB0ZW5zb3IoW10sIFswXS5jb25jYXQob3V0cHV0RWxlbWVudFNoYXBlKSk7XG4gICAgfVxuICAgIHJldHVybiB0aWR5KCgpID0+IHtcbiAgICAgIGNvbnN0IHRlbnNvcnMgPSB0aGlzLnRlbnNvcnMubWFwKHQgPT4gcmVzaGFwZSh0LCBvdXRwdXRFbGVtZW50U2hhcGUpKTtcbiAgICAgIHJldHVybiBjb25jYXQodGVuc29ycywgMCk7XG4gICAgfSk7XG4gIH1cbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgVGVuc29yTGlzdCB3aGljaCwgd2hlbiBzdGFja2VkLCBoYXMgdGhlIHZhbHVlIG9mIHRlbnNvci5cbiAqIEBwYXJhbSB0ZW5zb3IgZnJvbSB0ZW5zb3JcbiAqIEBwYXJhbSBlbGVtZW50U2hhcGUgb3V0cHV0IHRlbnNvciBlbGVtZW50IHNoYXBlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmcm9tVGVuc29yKFxuICAgIHRlbnNvcjogVGVuc29yLCBlbGVtZW50U2hhcGU6IG51bWJlcltdLCBlbGVtZW50RHR5cGU6IERhdGFUeXBlKSB7XG4gIGNvbnN0IGR0eXBlID0gdGVuc29yLmR0eXBlO1xuICBpZiAodGVuc29yLnNoYXBlLmxlbmd0aCA8IDEpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBUZW5zb3IgbXVzdCBiZSBhdCBsZWFzdCBhIHZlY3RvciwgYnV0IHNhdyBzaGFwZTogJHt0ZW5zb3Iuc2hhcGV9YCk7XG4gIH1cbiAgaWYgKHRlbnNvci5kdHlwZSAhPT0gZWxlbWVudER0eXBlKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGRhdGEgdHlwZXM7IG9wIGVsZW1lbnRzICR7XG4gICAgICAgIHRlbnNvci5kdHlwZX0sIGJ1dCBsaXN0IGVsZW1lbnRzICR7ZWxlbWVudER0eXBlfWApO1xuICB9XG4gIGNvbnN0IHRlbnNvckVsZW1lbnRTaGFwZSA9IHRlbnNvci5zaGFwZS5zbGljZSgxKTtcbiAgYXNzZXJ0U2hhcGVzTWF0Y2hBbGxvd1VuZGVmaW5lZFNpemUoXG4gICAgICB0ZW5zb3JFbGVtZW50U2hhcGUsIGVsZW1lbnRTaGFwZSwgJ1RlbnNvckxpc3Qgc2hhcGUgbWlzbWF0Y2g6ICcpO1xuICBjb25zdCB0ZW5zb3JMaXN0OiBUZW5zb3JbXSA9IHVuc3RhY2sodGVuc29yKTtcbiAgcmV0dXJuIG5ldyBUZW5zb3JMaXN0KHRlbnNvckxpc3QsIGVsZW1lbnRTaGFwZSwgZHR5cGUpO1xufVxuXG4vKipcbiAqIFJldHVybiBhIFRlbnNvckxpc3Qgb2YgdGhlIGdpdmVuIHNpemUgd2l0aCBlbXB0eSBlbGVtZW50cy5cbiAqIEBwYXJhbSBlbGVtZW50U2hhcGUgdGhlIHNoYXBlIG9mIHRoZSBmdXR1cmUgZWxlbWVudHMgb2YgdGhlIGxpc3RcbiAqIEBwYXJhbSBlbGVtZW50RHR5cGUgdGhlIGRlc2lyZWQgdHlwZSBvZiBlbGVtZW50cyBpbiB0aGUgbGlzdFxuICogQHBhcmFtIG51bUVsZW1lbnRzIHRoZSBudW1iZXIgb2YgZWxlbWVudHMgdG8gcmVzZXJ2ZVxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzZXJ2ZShcbiAgICBlbGVtZW50U2hhcGU6IG51bWJlcltdLCBlbGVtZW50RHR5cGU6IERhdGFUeXBlLCBudW1FbGVtZW50czogbnVtYmVyKSB7XG4gIHJldHVybiBuZXcgVGVuc29yTGlzdChbXSwgZWxlbWVudFNoYXBlLCBlbGVtZW50RHR5cGUsIG51bUVsZW1lbnRzKTtcbn1cblxuLyoqXG4gKiBQdXQgdGVuc29ycyBhdCBzcGVjaWZpYyBpbmRpY2VzIG9mIGEgc3RhY2tlZCB0ZW5zb3IgaW50byBhIFRlbnNvckxpc3QuXG4gKiBAcGFyYW0gaW5kaWNlcyBsaXN0IG9mIGluZGljZXMgb24gaG93IHRvIHNjYXR0ZXIgdGhlIHRlbnNvci5cbiAqIEBwYXJhbSB0ZW5zb3IgaW5wdXQgdGVuc29yLlxuICogQHBhcmFtIGVsZW1lbnRTaGFwZSB0aGUgc2hhcGUgb2YgdGhlIGZ1dHVyZSBlbGVtZW50cyBvZiB0aGUgbGlzdFxuICogQHBhcmFtIG51bUVsZW1lbnRzIHRoZSBudW1iZXIgb2YgZWxlbWVudHMgdG8gc2NhdHRlclxuICovXG5leHBvcnQgZnVuY3Rpb24gc2NhdHRlcihcbiAgICB0ZW5zb3I6IFRlbnNvciwgaW5kaWNlczogbnVtYmVyW10sIGVsZW1lbnRTaGFwZTogbnVtYmVyW10sXG4gICAgbnVtRWxlbWVudHM/OiBudW1iZXIpOiBUZW5zb3JMaXN0IHtcbiAgaWYgKGluZGljZXMubGVuZ3RoICE9PSB0ZW5zb3Iuc2hhcGVbMF0pIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIGxlbihpbmRpY2VzKSA9PSB0ZW5zb3Iuc2hhcGVbMF0sIGJ1dCBzYXc6ICR7XG4gICAgICAgIGluZGljZXMubGVuZ3RofSB2cy4gJHt0ZW5zb3Iuc2hhcGVbMF19YCk7XG4gIH1cblxuICBjb25zdCBtYXhJbmRleCA9IE1hdGgubWF4KC4uLmluZGljZXMpO1xuXG4gIGlmIChudW1FbGVtZW50cyAhPSBudWxsICYmIG51bUVsZW1lbnRzICE9PSAtMSAmJiBtYXhJbmRleCA+PSBudW1FbGVtZW50cykge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYE1heCBpbmRleCBtdXN0IGJlIDwgYXJyYXkgc2l6ZSAoJHttYXhJbmRleH0gIHZzLiAke251bUVsZW1lbnRzfSlgKTtcbiAgfVxuXG4gIGNvbnN0IGxpc3QgPSBuZXcgVGVuc29yTGlzdChbXSwgZWxlbWVudFNoYXBlLCB0ZW5zb3IuZHR5cGUsIG51bUVsZW1lbnRzKTtcbiAgY29uc3QgdGVuc29ycyA9IHVuc3RhY2sodGVuc29yLCAwKTtcbiAgaW5kaWNlcy5mb3JFYWNoKCh2YWx1ZSwgaW5kZXgpID0+IHtcbiAgICBsaXN0LnNldEl0ZW0odmFsdWUsIHRlbnNvcnNbaW5kZXhdKTtcbiAgfSk7XG4gIHJldHVybiBsaXN0O1xufVxuXG4vKipcbiAqIFNwbGl0IHRoZSB2YWx1ZXMgb2YgYSBUZW5zb3IgaW50byBhIFRlbnNvckxpc3QuXG4gKiBAcGFyYW0gbGVuZ3RoIHRoZSBsZW5ndGhzIHRvIHVzZSB3aGVuIHNwbGl0dGluZyB2YWx1ZSBhbG9uZ1xuICogICAgaXRzIGZpcnN0IGRpbWVuc2lvbi5cbiAqIEBwYXJhbSB0ZW5zb3IgdGhlIHRlbnNvciB0byBzcGxpdC5cbiAqIEBwYXJhbSBlbGVtZW50U2hhcGUgdGhlIHNoYXBlIG9mIHRoZSBmdXR1cmUgZWxlbWVudHMgb2YgdGhlIGxpc3RcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNwbGl0KFxuICAgIHRlbnNvcjogVGVuc29yLCBsZW5ndGg6IG51bWJlcltdLCBlbGVtZW50U2hhcGU6IG51bWJlcltdKSB7XG4gIGxldCB0b3RhbExlbmd0aCA9IDA7XG4gIGNvbnN0IGN1bXVsYXRpdmVMZW5ndGhzID0gbGVuZ3RoLm1hcChsZW4gPT4ge1xuICAgIHRvdGFsTGVuZ3RoICs9IGxlbjtcbiAgICByZXR1cm4gdG90YWxMZW5ndGg7XG4gIH0pO1xuXG4gIGlmICh0b3RhbExlbmd0aCAhPT0gdGVuc29yLnNoYXBlWzBdKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCBzdW0gb2YgbGVuZ3RocyB0byBiZSBlcXVhbCB0b1xuICAgICAgICAgIHRlbnNvci5zaGFwZVswXSwgYnV0IHN1bSBvZiBsZW5ndGhzIGlzXG4gICAgICAgICR7dG90YWxMZW5ndGh9LCBhbmQgdGVuc29yJ3Mgc2hhcGUgaXM6ICR7dGVuc29yLnNoYXBlfWApO1xuICB9XG5cbiAgY29uc3Qgc2hhcGVXaXRob3V0Rmlyc3REaW0gPSB0ZW5zb3Iuc2hhcGUuc2xpY2UoMSk7XG4gIGNvbnN0IG91dHB1dEVsZW1lbnRTaGFwZSA9XG4gICAgICBtZXJnZUVsZW1lbnRTaGFwZShzaGFwZVdpdGhvdXRGaXJzdERpbSwgZWxlbWVudFNoYXBlKTtcbiAgY29uc3QgZWxlbWVudFBlclJvdyA9IHRvdGFsTGVuZ3RoID09PSAwID8gMCA6IHRlbnNvci5zaXplIC8gdG90YWxMZW5ndGg7XG4gIGNvbnN0IHRlbnNvcnM6IFRlbnNvcltdID0gdGlkeSgoKSA9PiB7XG4gICAgY29uc3QgdGVuc29ycyA9IFtdO1xuICAgIHRlbnNvciA9IHJlc2hhcGUodGVuc29yLCBbMSwgdG90YWxMZW5ndGgsIGVsZW1lbnRQZXJSb3ddKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxlbmd0aC5sZW5ndGg7ICsraSkge1xuICAgICAgY29uc3QgcHJldmlvdXNMZW5ndGggPSAoaSA9PT0gMCkgPyAwIDogY3VtdWxhdGl2ZUxlbmd0aHNbaSAtIDFdO1xuICAgICAgY29uc3QgaW5kaWNlcyA9IFswLCBwcmV2aW91c0xlbmd0aCwgMF07XG4gICAgICBjb25zdCBzaXplcyA9IFsxLCBsZW5ndGhbaV0sIGVsZW1lbnRQZXJSb3ddO1xuICAgICAgdGVuc29yc1tpXSA9IHJlc2hhcGUoXG4gICAgICAgICAgc2xpY2UodGVuc29yLCBpbmRpY2VzLCBzaXplcyksIG91dHB1dEVsZW1lbnRTaGFwZSBhcyBudW1iZXJbXSk7XG4gICAgfVxuICAgIHRlbnNvci5kaXNwb3NlKCk7XG4gICAgcmV0dXJuIHRlbnNvcnM7XG4gIH0pO1xuXG4gIGNvbnN0IGxpc3QgPSBuZXcgVGVuc29yTGlzdChbXSwgZWxlbWVudFNoYXBlLCB0ZW5zb3IuZHR5cGUsIGxlbmd0aC5sZW5ndGgpO1xuXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgdGVuc29ycy5sZW5ndGg7IGkrKykge1xuICAgIGxpc3Quc2V0SXRlbShpLCB0ZW5zb3JzW2ldKTtcbiAgfVxuICByZXR1cm4gbGlzdDtcbn1cbiJdfQ==
\No newline at end of file