1 | /**
|
2 | * @license
|
3 | * Copyright 2018 Google LLC
|
4 | *
|
5 | * Use of this source code is governed by an MIT-style
|
6 | * license that can be found in the LICENSE file or at
|
7 | * https://opensource.org/licenses/MIT.
|
8 | * =============================================================================
|
9 | */
|
10 | /**
|
11 | * deeplearn.js backend.
|
12 | */
|
13 | import * as tfc from '@tensorflow/tfjs-core';
|
14 | import { onesLike as coreOnesLike, scalar, tensor1d, tidy, where, zerosLike as coreZerosLike } from '@tensorflow/tfjs-core';
|
15 | import { checkDataFormat } from '../common';
|
16 | import { NotImplementedError, ValueError } from '../errors';
|
17 | import * as math_utils from '../utils/math_utils';
|
18 | import { imageDataFormat } from './common';
|
19 | // tslint:enable
|
20 | /* Setting and getting backend from deeplearn.js. */
|
21 | // Default deeplearn.js backend is WebGL (GPU).
|
22 | let backend = 'webgl';
|
23 | export function setBackend(requestedBackend) {
|
24 | tfc.setBackend(requestedBackend);
|
25 | backend = requestedBackend;
|
26 | }
|
27 | export function getBackend() {
|
28 | return backend;
|
29 | }
|
30 | /**
|
31 | * Indicates whether the backend is operating symbolically.
|
32 | *
|
33 | * This function will be used to determine how to interpret user code. If
|
34 | * it returns true, calls to the backend construct a symbolic graph; if
|
35 | * it returns false, calls to the backend execute immediately.
|
36 | */
|
37 | export function isBackendSymbolic() {
|
38 | return false;
|
39 | }
|
40 | /**
|
41 | * Get the number of elements in a Tensor.
|
42 | * @param x The Tensor.
|
43 | * @return Number of elements in `x`.
|
44 | */
|
45 | export function countParams(x) {
|
46 | const shape = x.shape;
|
47 | if (shape.length > 0) {
|
48 | return shape.reduce((a, b) => a * b);
|
49 | }
|
50 | else {
|
51 | // Scalar.
|
52 | return 1;
|
53 | }
|
54 | }
|
55 | /**
|
56 | * Casts a tensor to a different dtype and returns it.
|
57 | * @param x Input tensor.
|
58 | * @param dtype String: 'float32'|'int32'|'bool'.
|
59 | * @returns Tensor of the specified `dtype`.
|
60 | */
|
61 | export function cast(x, dtype) {
|
62 | return tfc.cast(x, dtype);
|
63 | }
|
64 | /**
|
65 | * Adds a 1-sized dimension at index "axis".
|
66 | * @param x Input tensor.
|
67 | * @param axis Position where to add the new axis.
|
68 | * @returns Result of the dimension expansion.
|
69 | */
|
70 | export function expandDims(x, axis = -1) {
|
71 | const outShape = x.shape.slice();
|
72 | if (axis < 0) {
|
73 | axis = outShape.length + axis + 1;
|
74 | }
|
75 | outShape.splice(axis, 0, 1);
|
76 | return tfc.reshape(x, outShape);
|
77 | }
|
78 | /**
|
79 | * Repeats a 2D tensor.
|
80 | *
|
81 | * If `x` has shape `[samples, dim]` and `n` is 2, for example, the output
|
82 | * will have shape `[samples, 2, dim]`.
|
83 | *
|
84 | * @param x Input tensor.
|
85 | * @param n Integer, number of times to repeat.
|
86 | * @returns The result of the repeat operation.
|
87 | * @throws ValueError: If input tensor is not 2D.
|
88 | */
|
89 | export function repeat(x, n) {
|
90 | return tidy(() => {
|
91 | if (x.shape.length !== 2) {
|
92 | throw new ValueError(`repeat() expects a rank-2 tensor, but received a ` +
|
93 | `rank-${x.shape.length} tensor.`);
|
94 | }
|
95 | const y = expandDims(x, 1);
|
96 | return tile(y, [1, n, 1]);
|
97 | });
|
98 | }
|
99 | /**
|
100 | * Flatten a Tensor into 1D.
|
101 | * @param x Input tensor.
|
102 | * @return The result of the flattening `x`.
|
103 | */
|
104 | export function flatten(x) {
|
105 | const newShape = [math_utils.arrayProd(x.shape)];
|
106 | return tfc.reshape(x, newShape);
|
107 | }
|
108 | /**
|
109 | * Turn a nD tensor into a 2D tensor with same 0th dimension.
|
110 | * In other words, it flattens each data samples of a batch.
|
111 | *
|
112 | * @param x The tensor to flatten. The rank of this tensor is required to be 2
|
113 | * or higher.
|
114 | * @return The result of the flattening.
|
115 | */
|
116 | export function batchFlatten(x) {
|
117 | if (x.rank <= 1) {
|
118 | throw new ValueError(`batchFlatten requires a minimum rank of 2. Got rank: ${x.rank}.`);
|
119 | }
|
120 | const newShape = [x.shape[0], math_utils.arrayProd(x.shape, 1)];
|
121 | return tfc.reshape(x, newShape);
|
122 | }
|
123 | /**
|
124 | * Do slicing along the first axis.
|
125 | * @param array input `tf.Tensor`.
|
126 | * @param start starting index, inclusive.
|
127 | * @param size size of the slice along the first axis.
|
128 | * @returns result of the slicing.
|
129 | * @throws ValueError: If `array` is of an unsupported subtype of `tf.Tensor`.
|
130 | */
|
131 | export function sliceAlongFirstAxis(array, start, size) {
|
132 | return tidy(() => {
|
133 | switch (array.rank) {
|
134 | case 1:
|
135 | return tfc.slice1d(array, start, size);
|
136 | case 2:
|
137 | return tfc.slice2d(array, [start, 0], [size, array.shape[1]]);
|
138 | case 3:
|
139 | return tfc.slice3d(array, [start, 0, 0], [size, array.shape[1], array.shape[2]]);
|
140 | case 4:
|
141 | return tfc.slice4d(array, [start, 0, 0, 0], [size, array.shape[1], array.shape[2], array.shape[3]]);
|
142 | case 5:
|
143 | return tfc.slice(array, [start, 0, 0, 0, 0], [
|
144 | size, array.shape[1], array.shape[2], array.shape[3], array.shape[4]
|
145 | ]);
|
146 | case 6:
|
147 | return tfc.slice(array, [start, 0, 0, 0, 0, 0], [
|
148 | size, array.shape[1], array.shape[2], array.shape[3], array.shape[4],
|
149 | array.shape[5]
|
150 | ]);
|
151 | default:
|
152 | throw new ValueError(`sliceAlongFirstAxis() received an unsupported tensor rank: ` +
|
153 | `${array.rank}`);
|
154 | }
|
155 | });
|
156 | }
|
157 | /**
|
158 | * Do slicing along the last axis.
|
159 | * @param array input `tf.Tensor`.
|
160 | * @param start starting index, inclusive.
|
161 | * @param size size of the slice along the last axis.
|
162 | * @returns result of the slicing.
|
163 | * @throws ValueError: If `array` is of an unsupported subtype of `tf.Tensor`.
|
164 | */
|
165 | export function sliceAlongLastAxis(array, start, size) {
|
166 | return tidy(() => {
|
167 | switch (array.rank) {
|
168 | case 1:
|
169 | return tfc.slice1d(array, start, size);
|
170 | case 2:
|
171 | return tfc.slice2d(array, [0, start], [array.shape[0], size]);
|
172 | case 3:
|
173 | return tfc.slice3d(array, [0, 0, start], [array.shape[0], array.shape[1], size]);
|
174 | case 4:
|
175 | return tfc.slice4d(array, [0, 0, 0, start], [array.shape[0], array.shape[1], array.shape[2], size]);
|
176 | default:
|
177 | throw new ValueError(`sliceAlongLastAxis() received an unsupported tensor rank: ` +
|
178 | `${array.rank}`);
|
179 | }
|
180 | });
|
181 | }
|
182 | /**
|
183 | * Do slicing along the sepcified axis.
|
184 | * @param array input `tf.Tensor`.
|
185 | * @param start starting index, inclusive.
|
186 | * @param size of the slice along the chosen axis.
|
187 | * @param choose an axis.
|
188 | * @returns result of the slicing.
|
189 | * @throws ValueError: If `array` is of an unsupported subtype of `tf.Tensor`.
|
190 | */
|
191 | export function sliceAlongAxis(array, start, size, axis) {
|
192 | return tidy(() => {
|
193 | switch (array.rank) {
|
194 | case 1:
|
195 | return tfc.slice1d(array, start, size);
|
196 | case 2:
|
197 | switch (axis) {
|
198 | case 1:
|
199 | return sliceAlongFirstAxis(array, start, size);
|
200 | case 2:
|
201 | return sliceAlongLastAxis(array, start, size);
|
202 | default:
|
203 | throw new ValueError(`The axis is not within the rank of the tensor ` +
|
204 | `${axis}`);
|
205 | }
|
206 | case 3:
|
207 | switch (axis) {
|
208 | case 1:
|
209 | return sliceAlongFirstAxis(array, start, size);
|
210 | case 2:
|
211 | return tfc.slice3d(array, [0, start, 0], [array.shape[0], size, array.shape[2]]);
|
212 | case 3:
|
213 | return sliceAlongLastAxis(array, start, size);
|
214 | default:
|
215 | throw new ValueError(`The axis is not within the rank of the tensor ` +
|
216 | `${axis}`);
|
217 | }
|
218 | case 4:
|
219 | switch (axis) {
|
220 | case 1:
|
221 | return sliceAlongFirstAxis(array, start, size);
|
222 | case 2:
|
223 | return tfc.slice4d(array, [0, start, 0, 0], [array.shape[0], size, array.shape[2], array.shape[3]]);
|
224 | case 3:
|
225 | return tfc.slice4d(array, [0, 0, start, 0], [array.shape[0], array.shape[1], size, array.shape[3]]);
|
226 | case 4:
|
227 | return sliceAlongLastAxis(array, start, size);
|
228 | default:
|
229 | throw new ValueError(`The axis is not within the rank of the tensor ` +
|
230 | `${axis}`);
|
231 | }
|
232 | default:
|
233 | throw new ValueError(`sliceAlongLastAxis() received an unsupported tensor rank: ` +
|
234 | `${array.rank}`);
|
235 | }
|
236 | });
|
237 | }
|
238 | /**
|
239 | * Concatenates a list of tensors alongside the specified axis.
|
240 | * @param tensors `Array` of tensors to concatenate.
|
241 | * @param axis Concatenation axis.
|
242 | * @returns The result of the concatenation.
|
243 | */
|
244 | export function concatenate(tensors, axis = -1) {
|
245 | let rank;
|
246 | if (axis < 0) {
|
247 | rank = tensors[0].rank;
|
248 | if (rank !== 0) {
|
249 | axis = rank;
|
250 | }
|
251 | else {
|
252 | axis = 0;
|
253 | }
|
254 | }
|
255 | if (axis === tensors[0].rank) {
|
256 | // Porting Note: This is necessary because tfc.concat() requires axis to be
|
257 | // in the interval [-rank, rank).
|
258 | axis = -1;
|
259 | }
|
260 | // Porting Note: Sparse concat is not supported yet.
|
261 | return tfc.concat(tensors, axis);
|
262 | }
|
263 | /**
|
264 | * Concatenate two arrays along the first dimension.
|
265 | * @param a The 1st `tf.Tensor` to concatenate.
|
266 | * @param b The 2nd `tf.Tensor` to concatenate.
|
267 | * @returns Result of the concatenation.
|
268 | * @throws ValueError: If `a` is of an unsupported subtype of `tf.Tensor`.
|
269 | */
|
270 | export function concatAlongFirstAxis(a, b) {
|
271 | switch (a.rank) {
|
272 | case 1:
|
273 | return tfc.concat1d([a, b]);
|
274 | case 2:
|
275 | return tfc.concat2d([a, b], 0);
|
276 | case 3:
|
277 | return tfc.concat3d([a, b], 0);
|
278 | case 4:
|
279 | return tfc.concat4d([a, b], 0);
|
280 | default:
|
281 | throw new ValueError(`concatAlongFirstAxis() received an unsupported ` +
|
282 | `tensor rank: ${a.rank}`);
|
283 | }
|
284 | }
|
285 | /**
|
286 | * Creates a tensor by tiling `x` by `n`.
|
287 | * @param x A tensor.
|
288 | * @param n An Array of integers or a single integer. If an Array, the length
|
289 | * must be the same as the number of dimensions in `x`. If a single integer,
|
290 | * it will be treated as an Array of length 1.
|
291 | */
|
292 | export function tile(x, n) {
|
293 | if (!Array.isArray(n)) {
|
294 | n = [n];
|
295 | }
|
296 | if (x.rank !== n.length) {
|
297 | throw new ValueError(`The length of input n (${n.length}) does not match ` +
|
298 | `the number of dimensions in input x (${x.rank})`);
|
299 | }
|
300 | return tfc.tile(x, n);
|
301 | }
|
302 | /* Creation of random tensors. */
|
303 | /**
|
304 | * Get a tensor with normal distribution of values.
|
305 | *
|
306 | * @param shape Shape of the tensor.
|
307 | * @param mean mean value of the normal distribution.
|
308 | * @param stddev standard deviation of the normal distribution.
|
309 | * @param dtype
|
310 | * @param seed
|
311 | * @return The normal tensor.
|
312 | */
|
313 | export function randomNormal(shape, mean = 0.0, stddev = 1.0, dtype, seed) {
|
314 | return tfc.randomNormal(shape, mean, stddev, dtype, seed);
|
315 | }
|
316 | /* Linear Algebra */
|
317 | /**
|
318 | * Multiply two tensors and returns the result as a tensor.
|
319 | *
|
320 | * For 2D tensors, this is equivalent to matrix multiplication (matMul).
|
321 | * For tensors of higher ranks, it follows the Theano behavior,
|
322 | * (e.g. `(2, 3) * (4, 3, 5) -> (2, 4, 5)`). From the Theano documentation:
|
323 | *
|
324 | * For N dimensions it is a sum product over the last axis of x and the
|
325 | * second-to-last of y:
|
326 | *
|
327 | * @param a A tensor of at least rank 2.
|
328 | * @param b A tensor of at least rank 2.
|
329 | * @param activation (optional) A string identifying the activation
|
330 | * function.
|
331 | * @return Result of the dot operation.
|
332 | */
|
333 | export function dot(a, b, activation, bias) {
|
334 | if ((a.rank < 2) || (b.rank < 2)) {
|
335 | throw new NotImplementedError(`dot requires both inputs to be rank >= 2` +
|
336 | ` but got x shape = ${a.shape} and y shape = ${b.shape}`);
|
337 | }
|
338 | if (b.rank >= 3) {
|
339 | const xLastDim = a.shape.slice(-1)[0];
|
340 | const ySecondLastDim = b.shape.slice(-2)[0];
|
341 | if (xLastDim !== ySecondLastDim) {
|
342 | throw new NotImplementedError(`If rank y >= 3, then the second last dim` +
|
343 | ` of y must equal the last dim of x but got x shape = ${a.shape} and ` +
|
344 | ` y shape = ${b.shape}`);
|
345 | }
|
346 | }
|
347 | // Handle basic 2D x 2D case.
|
348 | if ((a.rank === 2) && (b.rank === 2)) {
|
349 | const transposeA = false;
|
350 | const transposeB = false;
|
351 | // tfc.fused.matMul only fuses certain activation functions. Unsupported
|
352 | // activation functions are treated as 'linear' activations, which is
|
353 | // equivalent to a no-op.
|
354 | return tfc.fused.matMul({
|
355 | a,
|
356 | b: b,
|
357 | transposeA,
|
358 | transposeB,
|
359 | bias: bias ? reshapeBias(a.rank, bias, imageDataFormat()) : null,
|
360 | activation
|
361 | });
|
362 | }
|
363 | else {
|
364 | // Reshape x into the analogous 2D Tensor.
|
365 | const aFirstDims = a.shape.slice(); // Holds all but the last dim of x.
|
366 | const aLastDim = aFirstDims.pop();
|
367 | a = tfc.reshape(a, [-1, aLastDim]);
|
368 | // Reshape y into the analogous 2D Tensor, and keep track of the
|
369 | // required dimensions to reproduce the output shape.
|
370 | const bShape = b.shape.slice();
|
371 | const bLastDim = bShape.pop();
|
372 | const ySecondLastDim = bShape.pop();
|
373 | const yOtherDims = [...bShape, bLastDim];
|
374 | // permutation should be like [r-2, 0, 1, 2, ... r-4, r-3, r-1]
|
375 | // where r is the rank of y.
|
376 | const perm = Array.from({ length: b.rank }, (_, i) => {
|
377 | if (i === 0) {
|
378 | return b.rank - 2;
|
379 | }
|
380 | else if (i <= b.rank - 2) {
|
381 | return i - 1;
|
382 | }
|
383 | return i;
|
384 | });
|
385 | b = tfc.reshape(tfc.transpose(b, perm), [ySecondLastDim, -1]);
|
386 | // Multiply x and y as 2D Tensors, and then reshape back to original.
|
387 | const outputShape = [...aFirstDims, ...yOtherDims];
|
388 | const transposeA = false;
|
389 | const transposeB = false;
|
390 | return tfc.reshape(tfc.fused.matMul({
|
391 | a,
|
392 | b,
|
393 | transposeA,
|
394 | transposeB,
|
395 | bias: bias ? reshapeBias(a.rank, bias, imageDataFormat()) : null,
|
396 | activation
|
397 | }), outputShape);
|
398 | }
|
399 | }
|
400 | /**
|
401 | * Compute the sign Tensor of an input Tensor.
|
402 | *
|
403 | * Elements of the input `tf.Tensor` that are === 0 are mapped to 0.
|
404 | * Elements of the input `tf.Tensor` that are > 0 are mapped to 1.
|
405 | * Elements of the input `tf.Tensor` that are < 0 are mapped to -1.
|
406 | *
|
407 | * @param x Input `tf.Tensor`.
|
408 | * @return The sign `tf.Tensor`.
|
409 | */
|
410 | export function sign(x) {
|
411 | // TODO(cais): Move to the core.
|
412 | return tidy(() => {
|
413 | const zerosLikeX = coreZerosLike(x);
|
414 | const onesLikeX = coreOnesLike(x);
|
415 | return where(tfc.equal(x, zerosLikeX), zerosLikeX, where(tfc.greater(x, coreZerosLike(x)), onesLikeX, tfc.mul(-1, onesLikeX)));
|
416 | });
|
417 | }
|
418 | /**
|
419 | * Computes the one-hot representation of an integer tensor.
|
420 | * @param indices nD integer tensor of shape
|
421 | * `(batch_size, dim1, dim2, ... dim(n-1))`
|
422 | * @param numClasses Integer, number of classes to consider.
|
423 | * @returns (n + 1)D one hot representation of the input
|
424 | * with shape `(batch_size, dim1, dim2, ... dim(n-1), num_classes)`
|
425 | */
|
426 | export function oneHot(indices, numClasses) {
|
427 | return tidy(() => {
|
428 | if (indices.rank !== 1) {
|
429 | throw new Error('Only 1D one-hot tensors are supported in the ' +
|
430 | 'deeplearn backend, at present.');
|
431 | }
|
432 | indices = tfc.cast(indices, 'int32');
|
433 | return tfc.cast(tfc.oneHot(indices, numClasses), 'float32');
|
434 | });
|
435 | }
|
436 | /* Elementary math functions. */
|
437 | /**
|
438 | * Retrieves the elements of indices `indices` in the tensor `reference`.
|
439 | * @param reference A tensor.
|
440 | * @param indices An integer tensor of indices or an `Array` of integers.
|
441 | * @param axis Axis along which to perform the gather operation.
|
442 | * @returns The result of the gathering as a tensor.
|
443 | */
|
444 | export function gather(reference, indices, axis) {
|
445 | return tidy(() => {
|
446 | if (Array.isArray(indices)) {
|
447 | indices = tensor1d(indices, 'int32');
|
448 | }
|
449 | else {
|
450 | indices = tfc.cast(indices, 'int32');
|
451 | }
|
452 | return tfc.gather(reference, indices, axis);
|
453 | });
|
454 | }
|
455 | /**
|
456 | * Element-wise square.
|
457 | * @param x Input tensor.
|
458 | * @return element-wise x^2
|
459 | */
|
460 | export function square(x) {
|
461 | return tfc.mul(x, x);
|
462 | }
|
463 | /**
|
464 | * Element-wise exponentiation.
|
465 | *
|
466 | * Porting Note: In PyKeras, `a` (the exponent) is a Python integer, which
|
467 | * takes advatnage of the backend's (e.g., TensorFlow's) automatic
|
468 | * conversion to tensor. Here we allow `a` to be either a number or a tensor.
|
469 | *
|
470 | * @param x The base tensor.
|
471 | * @param a The exponent, tensor or number. If a number, it is rounded to the
|
472 | * nearest integer and converted to a tensor.
|
473 | * @returns A tensor of the same shape as `x`.
|
474 | */
|
475 | export function pow(x, a) {
|
476 | return tidy(() => {
|
477 | if (typeof (a) === 'number') {
|
478 | a = scalar(Math.round(a), 'int32');
|
479 | }
|
480 | if (a.dtype !== 'int32') {
|
481 | throw new NotImplementedError(`Non-int32 dtype (${a.dtype}) is not supported by pow() yet`);
|
482 | }
|
483 | return tfc.pow(x, a);
|
484 | });
|
485 | }
|
486 | /**
|
487 | * Reshapes bias tensor according to rank of x.
|
488 | */
|
489 | function reshapeBias(xRank, bias, dataFormat) {
|
490 | const biasShape = bias.shape;
|
491 | if (bias.rank !== 1 && bias.rank !== xRank) {
|
492 | throw new ValueError(`Unexpected bias dimensions: ${bias.rank}` +
|
493 | `; expected it to be 1 or ${xRank}`);
|
494 | }
|
495 | if (xRank === 5) {
|
496 | if (dataFormat === 'channelsFirst') {
|
497 | if (biasShape.length === 1) {
|
498 | return tfc.reshape(bias, [1, biasShape[0], 1, 1, 1]);
|
499 | }
|
500 | else {
|
501 | return tfc.reshape(bias, [1, biasShape[3], biasShape[0], biasShape[1], biasShape[2]]);
|
502 | }
|
503 | }
|
504 | else if (dataFormat === 'channelsLast') {
|
505 | if (biasShape.length === 1) {
|
506 | return tfc.reshape(bias, [1, 1, 1, 1, biasShape[0]]);
|
507 | }
|
508 | else {
|
509 | return tfc.reshape(bias, [1].concat(biasShape));
|
510 | }
|
511 | }
|
512 | }
|
513 | else if (xRank === 4) {
|
514 | if (dataFormat === 'channelsFirst') {
|
515 | if (biasShape.length === 1) {
|
516 | return tfc.reshape(bias, [1, biasShape[0], 1, 1]);
|
517 | }
|
518 | else {
|
519 | return tfc.reshape(bias, [1, biasShape[2], biasShape[0], biasShape[1]]);
|
520 | }
|
521 | }
|
522 | else if (dataFormat === 'channelsLast') {
|
523 | if (biasShape.length === 1) {
|
524 | return tfc.reshape(bias, [1, 1, 1, biasShape[0]]);
|
525 | }
|
526 | else {
|
527 | return tfc.reshape(bias, [1].concat(biasShape));
|
528 | }
|
529 | }
|
530 | }
|
531 | else if (xRank === 3) {
|
532 | if (dataFormat === 'channelsFirst') {
|
533 | if (biasShape.length === 1) {
|
534 | return tfc.reshape(bias, [1, biasShape[0], 1]);
|
535 | }
|
536 | else {
|
537 | return tfc.reshape(bias, [1, biasShape[1], biasShape[0]]);
|
538 | }
|
539 | }
|
540 | else if (dataFormat === 'channelsLast') {
|
541 | if (biasShape.length === 1) {
|
542 | return tfc.reshape(bias, [1, 1, biasShape[0]]);
|
543 | }
|
544 | else {
|
545 | return tfc.reshape(bias, [1].concat(biasShape));
|
546 | }
|
547 | }
|
548 | }
|
549 | else if (xRank < 3) {
|
550 | return bias;
|
551 | }
|
552 | throw new ValueError(`Unsupported input rank by biasAdd: ${bias.rank}`);
|
553 | }
|
554 | /* Neural-network operations. */
|
555 | /**
|
556 | * Add a bias to a tensor.
|
557 | *
|
558 | * @param x The tensor to add the bias to.
|
559 | * @param bias The bias to add to `x`. Must be 1D or the same rank as `x`.
|
560 | * @return Result of the bias adding.
|
561 | * @throws ValueError: If the rank of `bias` is incorrect.
|
562 | */
|
563 | export function biasAdd(x, bias, dataFormat) {
|
564 | return tidy(() => {
|
565 | if (dataFormat == null) {
|
566 | dataFormat = imageDataFormat();
|
567 | }
|
568 | checkDataFormat(dataFormat);
|
569 | return tfc.add(x, reshapeBias(x.rank, bias, dataFormat));
|
570 | });
|
571 | }
|
572 | /**
|
573 | * Exponential linear unit (ELU).
|
574 | * @param x A tensor or variable to compute the activation function for.
|
575 | * @param alpha: A scalar, a scaling factor for the negative section.
|
576 | * @return Output of the ELU operation.
|
577 | */
|
578 | export function elu(x, alpha = 1) {
|
579 | // TODO(cais): Add support for alpha values other than 1.
|
580 | if (alpha !== 1) {
|
581 | throw new NotImplementedError(`Support for alpha values other than 1 (${alpha}) is not implemented ` +
|
582 | `yet.`);
|
583 | }
|
584 | return tfc.elu(x);
|
585 | }
|
586 | /**
|
587 | * Softsign of a tensor.
|
588 | *
|
589 | * Defined as x / (abs(x) + 1), element-wise.
|
590 | *
|
591 | * @param x: Input.
|
592 | * @returns Output.
|
593 | */
|
594 | export function softsign(x) {
|
595 | return tidy(() => tfc.div(x, tfc.add(tfc.abs(x), 1)));
|
596 | }
|
597 | /**
|
598 | * Sets entries in `x` to zero at random, while scaling the entire tensor.
|
599 | *
|
600 | * @param x input tensor.
|
601 | * @param level fraction of the entries in the tensor that will be set to 0.
|
602 | * @param noiseShape shape of randomly generated keep/drop flags, must be
|
603 | * broadcastable to the shape of `x`. Optional.
|
604 | * @param seed random seed to ensure determinism. Optional.
|
605 | * @returns Result of the dropout operation.
|
606 | */
|
607 | export function dropout(x, level, noiseShape, seed) {
|
608 | return tidy(() => tfc.dropout(x, level, noiseShape, seed));
|
609 | }
|
610 | /**
|
611 | * Element-wise, segment-wise linear approximation of sigmoid.
|
612 | *
|
613 | * Returns `0.` if `x < -2.5`, `1.` if `x > 2.5`.
|
614 | * In `-2.5 <= x <= 2.5`, returns `0.2 * x + 0.5`.
|
615 | *
|
616 | * @param x Input tensor.
|
617 | * @returns Output tensor.
|
618 | */
|
619 | export function hardSigmoid(x) {
|
620 | return tidy(() => {
|
621 | const y = tfc.add(.5, tfc.mul(.2, x));
|
622 | return tfc.clipByValue(y, 0, 1);
|
623 | });
|
624 | }
|
625 | /**
|
626 | * Invoke `x` in the training phase, and `alt` otherwise.
|
627 | *
|
628 | * Porting Note: We do not create placeholder tensors for the `training`
|
629 | * boolean flag here, because there is no such thing in the TF.js imperative
|
630 | * backend.
|
631 | *
|
632 | * @param x The function to invoke iff `training` is `true`.
|
633 | * @param alt The function to invoke iff `training` is `false`.
|
634 | * @param training Boolean flag for whether training phase is active.
|
635 | * @returns The return value of `x()` if `training` is `true`, or the return
|
636 | * value of `alt()` if `training` is `false`.
|
637 | */
|
638 | export function inTrainPhase(x, alt, training = false) {
|
639 | return training ? x() : alt();
|
640 | }
|
641 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGZqc19iYWNrZW5kLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vdGZqcy1sYXllcnMvc3JjL2JhY2tlbmQvdGZqc19iYWNrZW5kLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7OztHQVFHO0FBRUg7O0dBRUc7QUFFSCxPQUFPLEtBQUssR0FBRyxNQUFNLHVCQUF1QixDQUFDO0FBQzdDLE9BQU8sRUFBQyxRQUFRLElBQUksWUFBWSxFQUFFLE1BQU0sRUFBb0IsUUFBUSxFQUEwQyxJQUFJLEVBQUUsS0FBSyxFQUFFLFNBQVMsSUFBSSxhQUFhLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUNwTCxPQUFPLEVBQUMsZUFBZSxFQUFDLE1BQU0sV0FBVyxDQUFDO0FBQzFDLE9BQU8sRUFBQyxtQkFBbUIsRUFBRSxVQUFVLEVBQUMsTUFBTSxXQUFXLENBQUM7QUFHMUQsT0FBTyxLQUFLLFVBQVUsTUFBTSxxQkFBcUIsQ0FBQztBQUVsRCxPQUFPLEVBQUMsZUFBZSxFQUFDLE1BQU0sVUFBVSxDQUFDO0FBRXpDLGdCQUFnQjtBQUVoQixvREFBb0Q7QUFFcEQsK0NBQStDO0FBQy9DLElBQUksT0FBTyxHQUFrQixPQUFPLENBQUM7QUFFckMsTUFBTSxVQUFVLFVBQVUsQ0FBQyxnQkFBK0I7SUFDeEQsR0FBRyxDQUFDLFVBQVUsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ2pDLE9BQU8sR0FBRyxnQkFBZ0IsQ0FBQztBQUM3QixDQUFDO0FBRUQsTUFBTSxVQUFVLFVBQVU7SUFDeEIsT0FBTyxPQUFPLENBQUM7QUFDakIsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILE1BQU0sVUFBVSxpQkFBaUI7SUFDL0IsT0FBTyxLQUFLLENBQUM7QUFDZixDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSxXQUFXLENBQUMsQ0FBVztJQUNyQyxNQUFNLEtBQUssR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDO0lBQ3RCLElB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R0FBRyxLQUFLO0lBQ3hFLE9BQU8sUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxFQUFFLENBQUM7QUFDaEMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCAyMDE4IEdvb2dsZSBMTENcbiAqXG4gKiBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhbiBNSVQtc3R5bGVcbiAqIGxpY2Vuc2UgdGhhdCBjYW4gYmUgZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBvciBhdFxuICogaHR0cHM6Ly9vcGVuc291cmNlLm9yZy9saWNlbnNlcy9NSVQuXG4gKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICovXG5cbi8qKlxuICogZGVlcGxlYXJuLmpzIGJhY2tlbmQuXG4gKi9cblxuaW1wb3J0ICogYXMgdGZjIGZyb20gJ0B0ZW5zb3JmbG93L3RmanMtY29yZSc7XG5pbXBvcnQge29uZXNMaWtlIGFzIGNvcmVPbmVzTGlrZSwgc2NhbGFyLCBUZW5zb3IsIFRlbnNvcjFELCB0ZW5zb3IxZCwgVGVuc29yMkQsIFRlbnNvcjNELCBUZW5zb3I0RCwgVGVuc29yNUQsIHRpZHksIHdoZXJlLCB6ZXJvc0xpa2UgYXMgY29yZVplcm9zTGlrZX0gZnJvbSAnQHRlbnNvcmZsb3cvdGZqcy1jb3JlJztcbmltcG9ydCB7Y2hlY2tEYXRhRm9ybWF0fSBmcm9tICcuLi9jb21tb24nO1xuaW1wb3J0IHtOb3RJbXBsZW1lbnRlZEVycm9yLCBWYWx1ZUVycm9yfSBmcm9tICcuLi9lcnJvcnMnO1xuaW1wb3J0IHtEYXRhRm9ybWF0LCBTaGFwZX0gZnJvbSAnLi4va2VyYXNfZm9ybWF0L2NvbW1vbic7XG5pbXBvcnQge0hhc1NoYXBlfSBmcm9tICcuLi90eXBlcyc7XG5pbXBvcnQgKiBhcyBtYXRoX3V0aWxzIGZyb20gJy4uL3V0aWxzL21hdGhfdXRpbHMnO1xuXG5pbXBvcnQge2ltYWdlRGF0YUZvcm1hdH0gZnJvbSAnLi9jb21tb24nO1xuXG4vLyB0c2xpbnQ6ZW5hYmxlXG5cbi8qIFNldHRpbmcgYW5kIGdldHRpbmcgYmFja2VuZCBmcm9tIGRlZXBsZWFybi5qcy4gKi9cblxuLy8gRGVmYXVsdCBkZWVwbGVhcm4uanMgYmFja2VuZCBpcyBXZWJHTCAoR1BVKS5cbmxldCBiYWNrZW5kOiAnY3B1J3wnd2ViZ2wnID0gJ3dlYmdsJztcblxuZXhwb3J0IGZ1bmN0aW9uIHNldEJhY2tlbmQocmVxdWVzdGVkQmFja2VuZDogJ2NwdSd8J3dlYmdsJykge1xuICB0ZmMuc2V0QmFja2VuZChyZXF1ZXN0ZWRCYWNrZW5kKTtcbiAgYmFja2VuZCA9IHJlcXVlc3RlZEJhY2tlbmQ7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRCYWNrZW5kKCk6ICdjcHUnfCd3ZWJnbCcge1xuICByZXR1cm4gYmFja2VuZDtcbn1cblxuLyoqXG4gKiBJbmRpY2F0ZXMgd2hldGhlciB0aGUgYmFja2VuZCBpcyBvcGVyYXRpbmcgc3ltYm9saWNhbGx5LlxuICpcbiAqIFRoaXMgZnVuY3Rpb24gd2lsbCBiZSB1c2VkIHRvIGRldGVybWluZSBob3cgdG8gaW50ZXJwcmV0IHVzZXIgY29kZS4gSWZcbiAqIGl0IHJldHVybnMgdHJ1ZSwgY2FsbHMgdG8gdGhlIGJhY2tlbmQgY29uc3RydWN0IGEgc3ltYm9saWMgZ3JhcGg7IGlmXG4gKiBpdCByZXR1cm5zIGZhbHNlLCBjYWxscyB0byB0aGUgYmFja2VuZCBleGVjdXRlIGltbWVkaWF0ZWx5LlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNCYWNrZW5kU3ltYm9saWMoKTogYm9vbGVhbiB7XG4gIHJldHVybiBmYWxzZTtcbn1cblxuLyoqXG4gKiBHZXQgdGhlIG51bWJlciBvZiBlbGVtZW50cyBpbiBhIFRlbnNvci5cbiAqIEBwYXJhbSB4IFRoZSBUZW5zb3IuXG4gKiBAcmV0dXJuIE51bWJlciBvZiBlbGVtZW50cyBpbiBgeGAuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb3VudFBhcmFtcyh4OiBIYXNTaGFwZSk6IG51bWJlciB7XG4gIGNvbnN0IHNoYXBlID0geC5zaGFwZTtcbiAgaWYgKHNoYXBlLmxlbmd0aCA+IDApIHtcbiAgICByZXR1cm4gc2hhcGUucmVkdWNlKChhOiBudW1iZXIsIGI6IG51bWJlcikgPT4gYSAqIGIpO1xuICB9IGVsc2Uge1xuICAgIC8vIFNjYWxhci5cbiAgICByZXR1cm4gMTtcbiAgfVxufVxuXG4vKipcbiAqIENhc3RzIGEgdGVuc29yIHRvIGEgZGlmZmVyZW50IGR0eXBlIGFuZCByZXR1cm5zIGl0LlxuICogQHBhcmFtIHggSW5wdXQgdGVuc29yLlxuICogQHBhcmFtIGR0eXBlIFN0cmluZzogJ2Zsb2F0MzInfCdpbnQzMid8J2Jvb2wnLlxuICogQHJldHVybnMgVGVuc29yIG9mIHRoZSBzcGVjaWZpZWQgYGR0eXBlYC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNhc3QoeDogVGVuc29yLCBkdHlwZTogdGZjLkRhdGFUeXBlKTogVGVuc29yIHtcbiAgcmV0dXJuIHRmYy5jYXN0KHgsIGR0eXBlKTtcbn1cblxuLyoqXG4gKiBBZGRzIGEgMS1zaXplZCBkaW1lbnNpb24gYXQgaW5kZXggXCJheGlzXCIuXG4gKiBAcGFyYW0geCBJbnB1dCB0ZW5zb3IuXG4gKiBAcGFyYW0gYXhpcyBQb3NpdGlvbiB3aGVyZSB0byBhZGQgdGhlIG5ldyBheGlzLlxuICogQHJldHVybnMgUmVzdWx0IG9mIHRoZSBkaW1lbnNpb24gZXhwYW5zaW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZXhwYW5kRGltcyh4OiBUZW5zb3IsIGF4aXMgPSAtMSk6IFRlbnNvciB7XG4gIGNvbnN0IG91dFNoYXBlID0geC5zaGFwZS5zbGljZSgpO1xuICBpZiAoYXhpcyA8IDApIHtcbiAgICBheGlzID0gb3V0U2hhcGUubGVuZ3RoICsgYXhpcyArIDE7XG4gIH1cbiAgb3V0U2hhcGUuc3BsaWNlKGF4aXMsIDAsIDEpO1xuICByZXR1cm4gdGZjLnJlc2hhcGUoeCwgb3V0U2hhcGUpO1xufVxuXG4vKipcbiAqIFJlcGVhdHMgYSAyRCB0ZW5zb3IuXG4gKlxuICogSWYgYHhgIGhhcyBzaGFwZSBgW3NhbXBsZXMsIGRpbV1gIGFuZCBgbmAgaXMgMiwgZm9yIGV4YW1wbGUsIHRoZSBvdXRwdXRcbiAqIHdpbGwgaGF2ZSBzaGFwZSBgW3NhbXBsZXMsIDIsIGRpbV1gLlxuICpcbiAqIEBwYXJhbSB4IElucHV0IHRlbnNvci5cbiAqIEBwYXJhbSBuIEludGVnZXIsIG51bWJlciBvZiB0aW1lcyB0byByZXBlYXQuXG4gKiBAcmV0dXJucyBUaGUgcmVzdWx0IG9mIHRoZSByZXBlYXQgb3BlcmF0aW9uLlxuICogQHRocm93cyBWYWx1ZUVycm9yOiBJZiBpbnB1dCB0ZW5zb3IgaXMgbm90IDJELlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVwZWF0KHg6IFRlbnNvciwgbjogbnVtYmVyKTogVGVuc29yIHtcbiAgcmV0dXJuIHRpZHkoKCkgPT4ge1xuICAgIGlmICh4LnNoYXBlLmxlbmd0aCAhPT0gMikge1xuICAgICAgdGhyb3cgbmV3IFZhbHVlRXJyb3IoXG4gICAgICAgICAgYHJlcGVhdCgpIGV4cGVjdHMgYSByYW5rLTIgdGVuc29yLCBidXQgcmVjZWl2ZWQgYSBgICtcbiAgICAgICAgICBgcmFuay0ke3guc2hhcGUubGVuZ3RofSB0ZW5zb3IuYCk7XG4gICAgfVxuICAgIGNvbnN0IHkgPSBleHBhbmREaW1zKHgsIDEpO1xuICAgIHJldHVybiB0aWxlKHksIFsxLCBuLCAxXSk7XG4gIH0pO1xufVxuXG4vKipcbiAqIEZsYXR0ZW4gYSBUZW5zb3IgaW50byAxRC5cbiAqIEBwYXJhbSB4IElucHV0IHRlbnNvci5cbiAqIEByZXR1cm4gVGhlIHJlc3VsdCBvZiB0aGUgZmxhdHRlbmluZyBgeGAuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmbGF0dGVuKHg6IFRlbnNvcik6IFRlbnNvciB7XG4gIGNvbnN0IG5ld1NoYXBlID0gW21hdGhfdXRpbHMuYXJyYXlQcm9kKHguc2hhcGUpXTtcbiAgcmV0dXJuIHRmYy5yZXNoYXBlKHgsIG5ld1NoYXBlKTtcbn1cblxuLyoqXG4gKiBUdXJuIGEgbkQgdGVuc29yIGludG8gYSAyRCB0ZW5zb3Igd2l0aCBzYW1lIDB0aCBkaW1lbnNpb24uXG4gKiBJbiBvdGhlciB3b3JkcywgaXQgZmxhdHRlbnMgZWFjaCBkYXRhIHNhbXBsZXMgb2YgYSBiYXRjaC5cbiAqXG4gKiBAcGFyYW0geCBUaGUgdGVuc29yIHRvIGZsYXR0ZW4uIFRoZSByYW5rIG9mIHRoaXMgdGVuc29yIGlzIHJlcXVpcmVkIHRvIGJlIDJcbiAqICAgb3IgaGlnaGVyLlxuICogQHJldHVybiBUaGUgcmVzdWx0IG9mIHRoZSBmbGF0dGVuaW5nLlxuICovXG5leHBvcnQgZnVuY3Rpb24gYmF0Y2hGbGF0dGVuKHg6IFRlbnNvcik6IFRlbnNvciB7XG4gIGlmICh4LnJhbmsgPD0gMSkge1xuICAgIHRocm93IG5ldyBWYWx1ZUVycm9yKFxuICAgICAgICBgYmF0Y2hGbGF0dGVuIHJlcXVpcmVzIGEgbWluaW11bSByYW5rIG9mIDIuIEdvdCByYW5rOiAke3gucmFua30uYCk7XG4gIH1cbiAgY29uc3QgbmV3U2hhcGUgPSBbeC5zaGFwZVswXSwgbWF0aF91dGlscy5hcnJheVByb2QoeC5zaGFwZSwgMSldO1xuICByZXR1cm4gdGZjLnJlc2hhcGUoeCwgbmV3U2hhcGUpO1xufVxuXG4vKipcbiAqIERvIHNsaWNpbmcgYWxvbmcgdGhlIGZpcnN0IGF4aXMuXG4gKiBAcGFyYW0gYXJyYXkgaW5wdXQgYHRmLlRlbnNvcmAuXG4gKiBAcGFyYW0gc3RhcnQgc3RhcnRpbmcgaW5kZXgsIGluY2x1c2l2ZS5cbiAqIEBwYXJhbSBzaXplIHNpemUgb2YgdGhlIHNsaWNlIGFsb25nIHRoZSBmaXJzdCBheGlzLlxuICogQHJldHVybnMgcmVzdWx0IG9mIHRoZSBzbGljaW5nLlxuICogQHRocm93cyBWYWx1ZUVycm9yOiBJZiBgYXJyYXlgIGlzIG9mIGFuIHVuc3VwcG9ydGVkIHN1YnR5cGUgb2YgYHRmLlRlbnNvcmAuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzbGljZUFsb25nRmlyc3RBeGlzKFxuICAgIGFycmF5OiBUZW5zb3IsIHN0YXJ0OiBudW1iZXIsIHNpemU6IG51bWJlcik6IFRlbnNvciB7XG4gIHJldHVybiB0aWR5KCgpID0+IHtcbiAgICBzd2l0Y2ggKGFycmF5LnJhbmspIHtcbiAgICAgIGNhc2UgMTpcbiAgICAgICAgcmV0dXJuIHRmYy5zbGljZTFkKGFycmF5IGFzIFRlbnNvcjFELCBzdGFydCwgc2l6ZSk7XG4gICAgICBjYXNlIDI6XG4gICAgICAgIHJldHVybiB0ZmMuc2xpY2UyZChcbiAgICAgICAgICAgIGFycmF5IGFzIFRlbnNvcjJELCBbc3RhcnQsIDBdLCBbc2l6ZSwgYXJyYXkuc2hhcGVbMV1dKTtcbiAgICAgIGNhc2UgMzpcbiAgICAgICAgcmV0dXJuIHRmYy5zbGljZTNkKFxuICAgICAgICAgICAgYXJyYXkgYXMgVGVuc29yM0QsIFtzdGFydCwgMCwgMF0sXG4gICAgICAgICAgICBbc2l6ZSwgYXJyYXkuc2hhcGVbMV0sIGFycmF5LnNoYXBlWzJdXSk7XG4gICAgICBjYXNlIDQ6XG4gICAgICAgIHJldHVybiB0ZmMuc2xpY2U0ZChcbiAgICAgICAgICAgIGFycmF5IGFzIFRlbnNvcjRELCBbc3RhcnQsIDAsIDAsIDBdLFxuICAgICAgICAgICAgW3NpemUsIGFycmF5LnNoYXBlWzFdLCBhcnJheS5zaGFwZVsyXSwgYXJyYXkuc2hhcGVbM11dKTtcbiAgICAgIGNhc2UgNTpcbiAgICAgICAgcmV0dXJuIHRmYy5zbGljZShhcnJheSBhcyBUZW5zb3I1RCwgW3N0YXJ0LCAwLCAwLCAwLCAwXSwgW1xuICAgICAgICAgIHNpemUsIGFycmF5LnNoYXBlWzFdLCBhcnJheS5zaGFwZVsyXSwgYXJyYXkuc2hhcGVbM10sIGFycmF5LnNoYXBlWzRdXG4gICAgICAgIF0pO1xuICAgICAgY2FzZSA2OlxuICAgICAgICByZXR1cm4gdGZjLnNsaWNlKGFycmF5LCBbc3RhcnQsIDAsIDAsIDAsIDAsIDBdLCBbXG4gICAgICAgICAgc2l6ZSwgYXJyYXkuc2hhcGVbMV0sIGFycmF5LnNoYXBlWzJdLCBhcnJheS5zaGFwZVszXSwgYXJyYXkuc2hhcGVbNF0sXG4gICAgICAgICAgYXJyYXkuc2hhcGVbNV1cbiAgICAgICAgXSk7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICB0aHJvdyBuZXcgVmFsdWVFcnJvcihcbiAgICAgICAgICAgIGBzbGljZUFsb25nRmlyc3RBeGlzKCkgcmVjZWl2ZWQgYW4gdW5zdXBwb3J0ZWQgdGVuc29yIHJhbms6IGAgK1xuICAgICAgICAgICAgYCR7YXJyYXkucmFua31gKTtcbiAgICB9XG4gIH0pO1xufVxuXG4vKipcbiAqIERvIHNsaWNpbmcgYWxvbmcgdGhlIGxhc3QgYXhpcy5cbiAqIEBwYXJhbSBhcnJheSBpbnB1dCBgdGYuVGVuc29yYC5cbiAqIEBwYXJhbSBzdGFydCBzdGFydGluZyBpbmRleCwgaW5jbHVzaXZlLlxuICogQHBhcmFtIHNpemUgc2l6ZSBvZiB0aGUgc2xpY2UgYWxvbmcgdGhlIGxhc3QgYXhpcy5cbiAqIEByZXR1cm5zIHJlc3VsdCBvZiB0aGUgc2xpY2luZy5cbiAqIEB0aHJvd3MgVmFsdWVFcnJvcjogSWYgYGFycmF5YCBpcyBvZiBhbiB1bnN1cHBvcnRlZCBzdWJ0eXBlIG9mIGB0Zi5UZW5zb3JgLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2xpY2VBbG9uZ0xhc3RBeGlzKFxuICAgIGFycmF5OiBUZW5zb3IsIHN0YXJ0OiBudW1iZXIsIHNpemU6IG51bWJlcik6IFRlbnNvciB7XG4gIHJldHVybiB0aWR5KCgpID0+IHtcbiAgICBzd2l0Y2ggKGFycmF5LnJhbmspIHtcbiAgICAgIGNhc2UgMTpcbiAgICAgICAgcmV0dXJuIHRmYy5zbGljZTFkKGFycmF5IGFzIFRlbnNvcjFELCBzdGFydCwgc2l6ZSk7XG4gICAgICBjYXNlIDI6XG4gICAgICAgIHJldHVybiB0ZmMuc2xpY2UyZChcbiAgICAgICAgICAgIGFycmF5IGFzIFRlbnNvcjJELCBbMCwgc3RhcnRdLCBbYXJyYXkuc2hhcGVbMF0sIHNpemVdKTtcbiAgICAgIGNhc2UgMzpcbiAgICAgICAgcmV0dXJuIHRmYy5zbGljZTNkKFxuICAgICAgICAgICAgYXJyYXkgYXMgVGVuc29yM0QsIFswLCAwLCBzdGFydF0sXG4gICAgICAgICAgICBbYXJyYXkuc2hhcGVbMF0sIGFycmF5LnNoYXBlWzFdLCBzaXplXSk7XG4gICAgICBjYXNlIDQ6XG4gICAgICAgIHJldHVybiB0ZmMuc2xpY2U0ZChcbiAgICAgICAgICAgIGFycmF5IGFzIFRlbnNvcjRELCBbMCwgMCwgMCwgc3RhcnRdLFxuICAgICAgICAgICAgW2FycmF5LnNoYXBlWzBdLCBhcnJheS5zaGFwZVsxXSwgYXJyYXkuc2hhcGVbMl0sIHNpemVdKTtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHRocm93IG5ldyBWYWx1ZUVycm9yKFxuICAgICAgICAgICAgYHNsaWNlQWxvbmdMYXN0QXhpcygpIHJlY2VpdmVkIGFuIHVuc3VwcG9ydGVkIHRlbnNvciByYW5rOiBgICtcbiAgICAgICAgICAgIGAke2FycmF5LnJhbmt9YCk7XG4gICAgfVxuICB9KTtcbn1cblxuLyoqXG4gKiBEbyBzbGljaW5nIGFsb25nIHRoZSBzZXBjaWZpZWQgYXhpcy5cbiAqIEBwYXJhbSBhcnJheSBpbnB1dCBgdGYuVGVuc29yYC5cbiAqIEBwYXJhbSBzdGFydCBzdGFydGluZyBpbmRleCwgaW5jbHVzaXZlLlxuICogQHBhcmFtIHNpemUgb2YgdGhlIHNsaWNlIGFsb25nIHRoZSBjaG9zZW4gYXhpcy5cbiAqIEBwYXJhbSBjaG9vc2UgYW4gYXhpcy5cbiAqIEByZXR1cm5zIHJlc3VsdCBvZiB0aGUgc2xpY2luZy5cbiAqIEB0aHJvd3MgVmFsdWVFcnJvcjogSWYgYGFycmF5YCBpcyBvZiBhbiB1bnN1cHBvcnRlZCBzdWJ0eXBlIG9mIGB0Zi5UZW5zb3JgLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2xpY2VBbG9uZ0F4aXMoXG4gICAgYXJyYXk6IFRlbnNvciwgc3RhcnQ6IG51bWJlciwgc2l6ZTogbnVtYmVyLCBheGlzOiBudW1iZXIpOiBUZW5zb3Ige1xuICByZXR1cm4gdGlkeSgoKSA9PiB7XG4gICAgc3dpdGNoIChhcnJheS5yYW5rKSB7XG4gICAgICBjYXNlIDE6XG4gICAgICAgIHJldHVybiB0ZmMuc2xpY2UxZChhcnJheSBhcyBUZW5zb3IxRCwgc3RhcnQsIHNpemUpO1xuICAgICAgY2FzZSAyOlxuICAgICAgICBzd2l0Y2ggKGF4aXMpIHtcbiAgICAgICAgICBjYXNlIDE6XG4gICAgICAgICAgICByZXR1cm4gc2xpY2VBbG9uZ0ZpcnN0QXhpcyhhcnJheSwgc3RhcnQsIHNpemUpO1xuICAgICAgICAgIGNhc2UgMjpcbiAgICAgICAgICAgIHJldHVybiBzbGljZUFsb25nTGFzdEF4aXMoYXJyYXksIHN0YXJ0LCBzaXplKTtcbiAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgdGhyb3cgbmV3IFZhbHVlRXJyb3IoXG4gICAgICAgICAgICAgICAgYFRoZSBheGlzIGlzIG5vdCB3aXRoaW4gdGhlIHJhbmsgb2YgdGhlIHRlbnNvciBgICtcbiAgICAgICAgICAgICAgICBgJHtheGlzfWApO1xuICAgICAgICB9XG4gICAgICBjYXNlIDM6XG4gICAgICAgIHN3aXRjaCAoYXhpcykge1xuICAgICAgICAgIGNhc2UgMTpcbiAgICAgICAgICAgIHJldHVybiBzbGljZUFsb25nRmlyc3RBeGlzKGFycmF5LCBzdGFydCwgc2l6ZSk7XG4gICAgICAgICAgY2FzZSAyOlxuICAgICAgICAgICAgcmV0dXJuIHRmYy5zbGljZTNkKFxuICAgICAgICAgICAgICAgIGFycmF5IGFzIFRlbnNvcjNELCBbMCwgc3RhcnQsIDBdLFxuICAgICAgICAgICAgICAgIFthcnJheS5zaGFwZVswXSwgc2l6ZSwgYXJyYXkuc2hhcGVbMl1dKTtcbiAgICAgICAgICBjYXNlIDM6XG4gICAgICAgICAgICByZXR1cm4gc2xpY2VBbG9uZ0xhc3RBeGlzKGFycmF5LCBzdGFydCwgc2l6ZSk7XG4gICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgIHRocm93IG5ldyBWYWx1ZUVycm9yKFxuICAgICAgICAgICAgICAgIGBUaGUgYXhpcyBpcyBub3Qgd2l0aGluIHRoZSByYW5rIG9mIHRoZSB0ZW5zb3IgYCArXG4gICAgICAgICAgICAgICAgYCR7YXhpc31gKTtcbiAgICAgICAgfVxuICAgICAgY2FzZSA0OlxuICAgICAgICBzd2l0Y2ggKGF4aXMpIHtcbiAgICAgICAgICBjYXNlIDE6XG4gICAgICAgICAgICByZXR1cm4gc2xpY2VBbG9uZ0ZpcnN0QXhpcyhhcnJheSwgc3RhcnQsIHNpemUpO1xuICAgICAgICAgIGNhc2UgMjpcbiAgICAgICAgICAgIHJldHVybiB0ZmMuc2xpY2U0ZChcbiAgICAgICAgICAgICAgICBhcnJheSBhcyBUZW5zb3I0RCwgWzAsIHN0YXJ0LCAwLCAwXSxcbiAgICAgICAgICAgICAgICBbYXJyYXkuc2hhcGVbMF0sIHNpemUsIGFycmF5LnNoYXBlWzJdLCBhcnJheS5zaGFwZVszXV0pO1xuICAgICAgICAgIGNhc2UgMzpcbiAgICAgICAgICAgIHJldHVybiB0ZmMuc2xpY2U0ZChcbiAgICAgICAgICAgICAgICBhcnJheSBhcyBUZW5zb3I0RCwgWzAsIDAsIHN0YXJ0LCAwXSxcbiAgICAgICAgICAgICAgICBbYXJyYXkuc2hhcGVbMF0sIGFycmF5LnNoYXBlWzFdLCBzaXplLCBhcnJheS5zaGFwZVszXV0pO1xuICAgICAgICAgIGNhc2UgNDpcbiAgICAgICAgICAgIHJldHVybiBzbGljZUFsb25nTGFzdEF4aXMoYXJyYXksIHN0YXJ0LCBzaXplKTtcbiAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgdGhyb3cgbmV3IFZhbHVlRXJyb3IoXG4gICAgICAgICAgICAgICAgYFRoZSBheGlzIGlzIG5vdCB3aXRoaW4gdGhlIHJhbmsgb2YgdGhlIHRlbnNvciBgICtcbiAgICAgICAgICAgICAgICBgJHtheGlzfWApO1xuICAgICAgICB9XG4gICAgICBkZWZhdWx0OlxuICAgICAgICB0aHJvdyBuZXcgVmFsdWVFcnJvcihcbiAgICAgICAgICAgIGBzbGljZUFsb25nTGFzdEF4aXMoKSByZWNlaXZlZCBhbiB1bnN1cHBvcnRlZCB0ZW5zb3IgcmFuazogYCArXG4gICAgICAgICAgICBgJHthcnJheS5yYW5rfWApO1xuICAgIH1cbiAgfSk7XG59XG5cbi8qKlxuICogQ29uY2F0ZW5hdGVzIGEgbGlzdCBvZiB0ZW5zb3JzIGFsb25nc2lkZSB0aGUgc3BlY2lmaWVkIGF4aXMuXG4gKiBAcGFyYW0gdGVuc29ycyBgQXJyYXlgIG9mIHRlbnNvcnMgdG8gY29uY2F0ZW5hdGUuXG4gKiBAcGFyYW0gYXhpcyBDb25jYXRlbmF0aW9uIGF4aXMuXG4gKiBAcmV0dXJucyBUaGUgcmVzdWx0IG9mIHRoZSBjb25jYXRlbmF0aW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY29uY2F0ZW5hdGUodGVuc29yczogVGVuc29yW10sIGF4aXMgPSAtMSk6IFRlbnNvciB7XG4gIGxldCByYW5rOiBudW1iZXI7XG4gIGlmIChheGlzIDwgMCkge1xuICAgIHJhbmsgPSB0ZW5zb3JzWzBdLnJhbms7XG4gICAgaWYgKHJhbmsgIT09IDApIHtcbiAgICAgIGF4aXMgPSByYW5rO1xuICAgIH0gZWxzZSB7XG4gICAgICBheGlzID0gMDtcbiAgICB9XG4gIH1cbiAgaWYgKGF4aXMgPT09IHRlbnNvcnNbMF0ucmFuaykge1xuICAgIC8vIFBvcnRpbmcgTm90ZTogVGhpcyBpcyBuZWNlc3NhcnkgYmVjYXVzZSB0ZmMuY29uY2F0KCkgcmVxdWlyZXMgYXhpcyB0byBiZVxuICAgIC8vICAgaW4gdGhlIGludGVydmFsIFstcmFuaywgcmFuaykuXG4gICAgYXhpcyA9IC0xO1xuICB9XG4gIC8vIFBvcnRpbmcgTm90ZTogU3BhcnNlIGNvbmNhdCBpcyBub3Qgc3VwcG9ydGVkIHlldC5cbiAgcmV0dXJuIHRmYy5jb25jYXQodGVuc29ycywgYXhpcyk7XG59XG5cbi8qKlxuICogQ29uY2F0ZW5hdGUgdHdvIGFycmF5cyBhbG9uZyB0aGUgZmlyc3QgZGltZW5zaW9uLlxuICogQHBhcmFtIGEgVGhlIDFzdCBgdGYuVGVuc29yYCB0byBjb25jYXRlbmF0ZS5cbiAqIEBwYXJhbSBiIFRoZSAybmQgYHRmLlRlbnNvcmAgdG8gY29uY2F0ZW5hdGUuXG4gKiBAcmV0dXJucyBSZXN1bHQgb2YgdGhlIGNvbmNhdGVuYXRpb24uXG4gKiBAdGhyb3dzIFZhbHVlRXJyb3I6IElmIGBhYCBpcyBvZiBhbiB1bnN1cHBvcnRlZCBzdWJ0eXBlIG9mIGB0Zi5UZW5zb3JgLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY29uY2F0QWxvbmdGaXJzdEF4aXMoYTogVGVuc29yLCBiOiBUZW5zb3IpOiBUZW5zb3Ige1xuICBzd2l0Y2ggKGEucmFuaykge1xuICAgIGNhc2UgMTpcbiAgICAgIHJldHVybiB0ZmMuY29uY2F0MWQoW2EgYXMgVGVuc29yMUQsIGIgYXMgVGVuc29yMURdKTtcbiAgICBjYXNlIDI6XG4gICAgICByZXR1cm4gdGZjLmNvbmNhdDJkKFthIGFzIFRlbnNvcjJELCBiIGFzIFRlbnNvcjJEXSwgMCk7XG4gICAgY2FzZSAzOlxuICAgICAgcmV0dXJuIHRmYy5jb25jYXQzZChbYSBhcyBUZW5zb3IzRCwgYiBhcyBUZW5zb3IzRF0sIDApO1xuICAgIGNhc2UgNDpcbiAgICAgIHJldHVybiB0ZmMuY29uY2F0NGQoW2EgYXMgVGVuc29yNEQsIGIgYXMgVGVuc29yNERdLCAwKTtcbiAgICBkZWZhdWx0OlxuICAgICAgdGhyb3cgbmV3IFZhbHVlRXJyb3IoXG4gICAgICAgICAgYGNvbmNhdEFsb25nRmlyc3RBeGlzKCkgcmVjZWl2ZWQgYW4gdW5zdXBwb3J0ZWQgYCArXG4gICAgICAgICAgYHRlbnNvciByYW5rOiAke2EucmFua31gKTtcbiAgfVxufVxuXG4vKipcbiAqIENyZWF0ZXMgYSB0ZW5zb3IgYnkgdGlsaW5nIGB4YCBieSBgbmAuXG4gKiBAcGFyYW0geCBBIHRlbnNvci5cbiAqIEBwYXJhbSBuIEFuIEFycmF5IG9mIGludGVnZXJzIG9yIGEgc2luZ2xlIGludGVnZXIuIElmIGFuIEFycmF5LCB0aGUgbGVuZ3RoXG4gKiAgIG11c3QgYmUgdGhlIHNhbWUgYXMgdGhlIG51bWJlciBvZiBkaW1lbnNpb25zIGluIGB4YC4gSWYgYSBzaW5nbGUgaW50ZWdlcixcbiAqICAgaXQgd2lsbCBiZSB0cmVhdGVkIGFzIGFuIEFycmF5IG9mIGxlbmd0aCAxLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdGlsZSh4OiBUZW5zb3IsIG46IG51bWJlcnxudW1iZXJbXSk6IFRlbnNvciB7XG4gIGlmICghQXJyYXkuaXNBcnJheShuKSkge1xuICAgIG4gPSBbbl07XG4gIH1cbiAgaWYgKHgucmFuayAhPT0gbi5sZW5ndGgpIHtcbiAgICB0aHJvdyBuZXcgVmFsdWVFcnJvcihcbiAgICAgICAgYFRoZSBsZW5ndGggb2YgaW5wdXQgbiAoJHtuLmxlbmd0aH0pIGRvZXMgbm90IG1hdGNoIGAgK1xuICAgICAgICBgdGhlIG51bWJlciBvZiBkaW1lbnNpb25zIGluIGlucHV0IHggKCR7eC5yYW5rfSlgKTtcbiAgfVxuICByZXR1cm4gdGZjLnRpbGUoeCwgbik7XG59XG5cbi8qIENyZWF0aW9uIG9mIHJhbmRvbSB0ZW5zb3JzLiAqL1xuXG4vKipcbiAqIEdldCBhIHRlbnNvciB3aXRoIG5vcm1hbCBkaXN0cmlidXRpb24gb2YgdmFsdWVzLlxuICpcbiAqIEBwYXJhbSBzaGFwZSBTaGFwZSBvZiB0aGUgdGVuc29yLlxuICogQHBhcmFtIG1lYW4gbWVhbiB2YWx1ZSBvZiB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbi5cbiAqIEBwYXJhbSBzdGRkZXYgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uLlxuICogQHBhcmFtIGR0eXBlXG4gKiBAcGFyYW0gc2VlZFxuICogQHJldHVybiBUaGUgbm9ybWFsIHRlbnNvci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJhbmRvbU5vcm1hbChcbiAgICBzaGFwZTogU2hhcGUsIG1lYW4gPSAwLjAsIHN0ZGRldiA9IDEuMCwgZHR5cGU/OiAnZmxvYXQzMid8J2ludDMyJyxcbiAgICBzZWVkPzogbnVtYmVyKTogVGVuc29yIHtcbiAgcmV0dXJuIHRmYy5yYW5kb21Ob3JtYWwoc2hhcGUsIG1lYW4sIHN0ZGRldiwgZHR5cGUsIHNlZWQpO1xufVxuXG4vKiBMaW5lYXIgQWxnZWJyYSAqL1xuXG4vKipcbiAqIE11bHRpcGx5IHR3byB0ZW5zb3JzIGFuZCByZXR1cm5zIHRoZSByZXN1bHQgYXMgYSB0ZW5zb3IuXG4gKlxuICogRm9yIDJEIHRlbnNvcnMsIHRoaXMgaXMgZXF1aXZhbGVudCB0byBtYXRyaXggbXVsdGlwbGljYXRpb24gKG1hdE11bCkuXG4gKiBGb3IgdGVuc29ycyBvZiBoaWdoZXIgcmFua3MsIGl0IGZvbGxvd3MgdGhlIFRoZWFubyBiZWhhdmlvcixcbiAqIChlLmcuIGAoMiwgMykgKiAoNCwgMywgNSkgLT4gKDIsIDQsIDUpYCkuICBGcm9tIHRoZSBUaGVhbm8gZG9jdW1lbnRhdGlvbjpcbiAqXG4gKiBGb3IgTiBkaW1lbnNpb25zIGl0IGlzIGEgc3VtIHByb2R1Y3Qgb3ZlciB0aGUgbGFzdCBheGlzIG9mIHggYW5kIHRoZVxuICogc2Vjb25kLXRvLWxhc3Qgb2YgeTpcbiAqXG4gKiBAcGFyYW0gYSBBIHRlbnNvciBvZiBhdCBsZWFzdCByYW5rIDIuXG4gKiBAcGFyYW0gYiBBIHRlbnNvciBvZiBhdCBsZWFzdCByYW5rIDIuXG4gKiBAcGFyYW0gYWN0aXZhdGlvbiAob3B0aW9uYWwpIEEgc3RyaW5nIGlkZW50aWZ5aW5nIHRoZSBhY3RpdmF0aW9uXG4gKiAgIGZ1bmN0aW9uLlxuICogQHJldHVybiBSZXN1bHQgb2YgdGhlIGRvdCBvcGVyYXRpb24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkb3QoXG4gICAgYTogVGVuc29yLCBiOiBUZW5zb3IsIGFjdGl2YXRpb24/OiB0ZmMuZnVzZWQuQWN0aXZhdGlvbixcbiAgICBiaWFzPzogVGVuc29yKTogVGVuc29yIHtcbiAgaWYgKChhLnJhbmsgPCAyKSB8fCAoYi5yYW5rIDwgMikpIHtcbiAgICB0aHJvdyBuZXcgTm90SW1wbGVtZW50ZWRFcnJvcihcbiAgICAgICAgYGRvdCByZXF1aXJlcyBib3RoIGlucHV0cyB0byBiZSByYW5rID49IDJgICtcbiAgICAgICAgYCBidXQgZ290IHggc2hhcGUgPSAke2Euc2hhcGV9IGFuZCB5IHNoYXBlID0gJHtiLnNoYXBlfWApO1xuICB9XG4gIGlmIChiLnJhbmsgPj0gMykge1xuICAgIGNvbnN0IHhMYXN0RGltID0gYS5zaGFwZS5zbGljZSgtMSlbMF07XG4gICAgY29uc3QgeVNlY29uZExhc3REaW0gPSBiLnNoYXBlLnNsaWNlKC0yKVswXTtcbiAgICBpZiAoeExhc3REaW0gIT09IHlTZWNvbmRMYXN0RGltKSB7XG4gICAgICB0aHJvdyBuZXcgTm90SW1wbGVtZW50ZWRFcnJvcihcbiAgICAgICAgICBgSWYgcmFuayB5ID49IDMsIHRoZW4gdGhlIHNlY29uZCBsYXN0IGRpbWAgK1xuICAgICAgICAgIGAgb2YgeSBtdXN0IGVxdWFsIHRoZSBsYXN0IGRpbSBvZiB4IGJ1dCBnb3QgeCBzaGFwZSA9ICR7XG4gICAgICAgICAgICAgIGEuc2hhcGV9IGFuZCBgICtcbiAgICAgICAgICBgIHkgc2hhcGUgPSAke2Iuc2hhcGV9YCk7XG4gICAgfVxuICB9XG4gIC8vIEhhbmRsZSBiYXNpYyAyRCB4IDJEIGNhc2UuXG4gIGlmICgoYS5yYW5rID09PSAyKSAmJiAoYi5yYW5rID09PSAyKSkge1xuICAgIGNvbnN0IHRyYW5zcG9zZUEgPSBmYWxzZTtcbiAgICBjb25zdCB0cmFuc3Bvc2VCID0gZmFsc2U7XG4gICAgLy8gdGZjLmZ1c2VkLm1hdE11bCBvbmx5IGZ1c2VzIGNlcnRhaW4gYWN0aXZhdGlvbiBmdW5jdGlvbnMuIFVuc3VwcG9ydGVkXG4gICAgLy8gYWN0aXZhdGlvbiBmdW5jdGlvbnMgYXJlIHRyZWF0ZWQgYXMgJ2xpbmVhcicgYWN0aXZhdGlvbnMsIHdoaWNoIGlzXG4gICAgLy8gZXF1aXZhbGVudCB0byBhIG5vLW9wLlxuICAgIHJldHVybiB0ZmMuZnVzZWQubWF0TXVsKHtcbiAgICAgIGEsXG4gICAgICBiOiBiIGFzIFRlbnNvcjJELFxuICAgICAgdHJhbnNwb3NlQSxcbiAgICAgIHRyYW5zcG9zZUIsXG4gICAgICBiaWFzOiBiaWFzID8gcmVzaGFwZUJpYXMoYS5yYW5rLCBiaWFzLCBpbWFnZURhdGFGb3JtYXQoKSkgOiBudWxsLFxuICAgICAgYWN0aXZhdGlvblxuICAgIH0pO1xuICB9IGVsc2Uge1xuICAgIC8vIFJlc2hhcGUgeCBpbnRvIHRoZSBhbmFsb2dvdXMgMkQgVGVuc29yLlxuICAgIGNvbnN0IGFGaXJzdERpbXMgPSBhLnNoYXBlLnNsaWNlKCk7ICAvLyBIb2xkcyBhbGwgYnV0IHRoZSBsYXN0IGRpbSBvZiB4LlxuICAgIGNvbnN0IGFMYXN0RGltID0gYUZpcnN0RGltcy5wb3AoKTtcbiAgICBhID0gdGZjLnJlc2hhcGUoYSwgWy0xLCBhTGFzdERpbV0pO1xuXG4gICAgLy8gUmVzaGFwZSB5IGludG8gdGhlIGFuYWxvZ291cyAyRCBUZW5zb3IsIGFuZCBrZWVwIHRyYWNrIG9mIHRoZVxuICAgIC8vIHJlcXVpcmVkIGRpbWVuc2lvbnMgdG8gcmVwcm9kdWNlIHRoZSBvdXRwdXQgc2hhcGUuXG4gICAgY29uc3QgYlNoYXBlID0gYi5zaGFwZS5zbGljZSgpO1xuICAgIGNvbnN0IGJMYXN0RGltID0gYlNoYXBlLnBvcCgpO1xuICAgIGNvbnN0IHlTZWNvbmRMYXN0RGltID0gYlNoYXBlLnBvcCgpO1xuICAgIGNvbnN0IHlPdGhlckRpbXMgPSBbLi4uYlNoYXBlLCBiTGFzdERpbV07XG4gICAgLy8gcGVybXV0YXRpb24gc2hvdWxkIGJlIGxpa2UgW3ItMiwgMCwgMSwgMiwgLi4uIHItNCwgci0zLCByLTFdXG4gICAgLy8gd2hlcmUgciBpcyB0aGUgcmFuayBvZiB5LlxuICAgIGNvbnN0IHBlcm0gPSBBcnJheS5mcm9tKHtsZW5ndGg6IGIucmFua30sIChfLCBpKSA9PiB7XG4gICAgICBpZiAoaSA9PT0gMCkge1xuICAgICAgICByZXR1cm4gYi5yYW5rIC0gMjtcbiAgICAgIH0gZWxzZSBpZiAoaSA8PSBiLnJhbmsgLSAyKSB7XG4gICAgICAgIHJldHVybiBpIC0gMTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBpO1xuICAgIH0pO1xuICAgIGIgPSB0ZmMucmVzaGFwZSh0ZmMudHJhbnNwb3NlKGIsIHBlcm0pLCBbeVNlY29uZExhc3REaW0sIC0xXSk7XG5cbiAgICAvLyBNdWx0aXBseSB4IGFuZCB5IGFzIDJEIFRlbnNvcnMsIGFuZCB0aGVuIHJlc2hhcGUgYmFjayB0byBvcmlnaW5hbC5cbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9IFsuLi5hRmlyc3REaW1zLCAuLi55T3RoZXJEaW1zXTtcbiAgICBjb25zdCB0cmFuc3Bvc2VBID0gZmFsc2U7XG4gICAgY29uc3QgdHJhbnNwb3NlQiA9IGZhbHNlO1xuICAgIHJldHVybiB0ZmMucmVzaGFwZShcbiAgICAgICAgdGZjLmZ1c2VkLm1hdE11bCh7XG4gICAgICAgICAgYSxcbiAgICAgICAgICBiLFxuICAgICAgICAgIHRyYW5zcG9zZUEsXG4gICAgICAgICAgdHJhbnNwb3NlQixcbiAgICAgICAgICBiaWFzOiBiaWFzID8gcmVzaGFwZUJpYXMoYS5yYW5rLCBiaWFzLCBpbWFnZURhdGFGb3JtYXQoKSkgOiBudWxsLFxuICAgICAgICAgIGFjdGl2YXRpb25cbiAgICAgICAgfSksXG4gICAgICAgIG91dHB1dFNoYXBlKTtcbiAgfVxufVxuXG4vKipcbiAqIENvbXB1dGUgdGhlIHNpZ24gVGVuc29yIG9mIGFuIGlucHV0IFRlbnNvci5cbiAqXG4gKiBFbGVtZW50cyBvZiB0aGUgaW5wdXQgYHRmLlRlbnNvcmAgdGhhdCBhcmUgPT09IDAgYXJlIG1hcHBlZCB0byAwLlxuICogRWxlbWVudHMgb2YgdGhlIGlucHV0IGB0Zi5UZW5zb3JgIHRoYXQgYXJlID4gMCBhcmUgbWFwcGVkIHRvIDEuXG4gKiBFbGVtZW50cyBvZiB0aGUgaW5wdXQgYHRmLlRlbnNvcmAgdGhhdCBhcmUgPCAwIGFyZSBtYXBwZWQgdG8gLTEuXG4gKlxuICogQHBhcmFtIHggSW5wdXQgYHRmLlRlbnNvcmAuXG4gKiBAcmV0dXJuIFRoZSBzaWduIGB0Zi5UZW5zb3JgLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2lnbih4OiBUZW5zb3IpOiBUZW5zb3Ige1xuICAvLyBUT0RPKGNhaXMpOiBNb3ZlIHRvIHRoZSBjb3JlLlxuICByZXR1cm4gdGlkeSgoKSA9PiB7XG4gICAgY29uc3QgemVyb3NMaWtlWCA9IGNvcmVaZXJvc0xpa2UoeCk7XG4gICAgY29uc3Qgb25lc0xpa2VYID0gY29yZU9uZXNMaWtlKHgpO1xuICAgIHJldHVybiB3aGVyZShcbiAgICAgICAgdGZjLmVxdWFsKHgsIHplcm9zTGlrZVgpLCB6ZXJvc0xpa2VYLFxuICAgICAgICB3aGVyZShcbiAgICAgICAgICAgIHRmYy5ncmVhdGVyKHgsIGNvcmVaZXJvc0xpa2UoeCkpLCBvbmVzTGlrZVgsXG4gICAgICAgICAgICB0ZmMubXVsKC0xLCBvbmVzTGlrZVgpKSk7XG4gIH0pO1xufVxuXG4vKipcbiAqIENvbXB1dGVzIHRoZSBvbmUtaG90IHJlcHJlc2VudGF0aW9uIG9mIGFuIGludGVnZXIgdGVuc29yLlxuICogQHBhcmFtIGluZGljZXMgbkQgaW50ZWdlciB0ZW5zb3Igb2Ygc2hhcGVcbiAqICAgYChiYXRjaF9zaXplLCBkaW0xLCBkaW0yLCAuLi4gZGltKG4tMSkpYFxuICogQHBhcmFtIG51bUNsYXNzZXMgSW50ZWdlciwgbnVtYmVyIG9mIGNsYXNzZXMgdG8gY29uc2lkZXIuXG4gKiBAcmV0dXJucyAobiArIDEpRCBvbmUgaG90IHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpbnB1dFxuICogICB3aXRoIHNoYXBlIGAoYmF0Y2hfc2l6ZSwgZGltMSwgZGltMiwgLi4uIGRpbShuLTEpLCBudW1fY2xhc3NlcylgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBvbmVIb3QoaW5kaWNlczogVGVuc29yLCBudW1DbGFzc2VzOiBudW1iZXIpOiBUZW5zb3Ige1xuICByZXR1cm4gdGlkeSgoKSA9PiB7XG4gICAgaWYgKGluZGljZXMucmFuayAhPT0gMSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdPbmx5IDFEIG9uZS1ob3QgdGVuc29ycyBhcmUgc3VwcG9ydGVkIGluIHRoZSAnICtcbiAgICAgICAgICAnZGVlcGxlYXJuIGJhY2tlbmQsIGF0IHByZXNlbnQuJyk7XG4gICAgfVxuICAgIGluZGljZXMgPSB0ZmMuY2FzdChpbmRpY2VzLCAnaW50MzInKTtcbiAgICByZXR1cm4gdGZjLmNhc3QodGZjLm9uZUhvdChpbmRpY2VzIGFzIFRlbnNvcjFELCBudW1DbGFzc2VzKSwgJ2Zsb2F0MzInKTtcbiAgfSk7XG59XG5cbi8qIEVsZW1lbnRhcnkgbWF0aCBmdW5jdGlvbnMuICovXG5cbi8qKlxuICogUmV0cmlldmVzIHRoZSBlbGVtZW50cyBvZiBpbmRpY2VzIGBpbmRpY2VzYCBpbiB0aGUgdGVuc29yIGByZWZlcmVuY2VgLlxuICogQHBhcmFtIHJlZmVyZW5jZSBBIHRlbnNvci5cbiAqIEBwYXJhbSBpbmRpY2VzIEFuIGludGVnZXIgdGVuc29yIG9mIGluZGljZXMgb3IgYW4gYEFycmF5YCBvZiBpbnRlZ2Vycy5cbiAqIEBwYXJhbSBheGlzIEF4aXMgYWxvbmcgd2hpY2ggdG8gcGVyZm9ybSB0aGUgZ2F0aGVyIG9wZXJhdGlvbi5cbiAqIEByZXR1cm5zIFRoZSByZXN1bHQgb2YgdGhlIGdhdGhlcmluZyBhcyBhIHRlbnNvci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdhdGhlcihcbiAgICByZWZlcmVuY2U6IFRlbnNvciwgaW5kaWNlczogbnVtYmVyW118VGVuc29yMUQsIGF4aXM/OiBudW1iZXIpOiBUZW5zb3Ige1xuICByZXR1cm4gdGlkeSgoKSA9PiB7XG4gICAgaWYgKEFycmF5LmlzQXJyYXkoaW5kaWNlcykpIHtcbiAgICAgIGluZGljZXMgPSB0ZW5zb3IxZChpbmRpY2VzLCAnaW50MzInKTtcbiAgICB9IGVsc2Uge1xuICAgICAgaW5kaWNlcyA9IHRmYy5jYXN0KGluZGljZXMsICdpbnQzMicpO1xuICAgIH1cbiAgICByZXR1cm4gdGZjLmdhdGhlcihyZWZlcmVuY2UsIGluZGljZXMsIGF4aXMpO1xuICB9KTtcbn1cblxuLyoqXG4gKiBFbGVtZW50LXdpc2Ugc3F1YXJlLlxuICogQHBhcmFtIHggSW5wdXQgdGVuc29yLlxuICogQHJldHVybiBlbGVtZW50LXdpc2UgeF4yXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzcXVhcmUoeDogVGVuc29yKTogVGVuc29yIHtcbiAgcmV0dXJuIHRmYy5tdWwoeCwgeCk7XG59XG5cbi8qKlxuICogRWxlbWVudC13aXNlIGV4cG9uZW50aWF0aW9uLlxuICpcbiAqIFBvcnRpbmcgTm90ZTogSW4gUHlLZXJhcywgYGFgICh0aGUgZXhwb25lbnQpIGlzIGEgUHl0aG9uIGludGVnZXIsIHdoaWNoXG4gKiAgIHRha2VzIGFkdmF0bmFnZSBvZiB0aGUgYmFja2VuZCdzIChlLmcuLCBUZW5zb3JGbG93J3MpIGF1dG9tYXRpY1xuICogY29udmVyc2lvbiB0byB0ZW5zb3IuIEhlcmUgd2UgYWxsb3cgYGFgIHRvIGJlIGVpdGhlciBhIG51bWJlciBvciBhIHRlbnNvci5cbiAqXG4gKiBAcGFyYW0geCBUaGUgYmFzZSB0ZW5zb3IuXG4gKiBAcGFyYW0gYSBUaGUgZXhwb25lbnQsIHRlbnNvciBvciBudW1iZXIuIElmIGEgbnVtYmVyLCBpdCBpcyByb3VuZGVkIHRvIHRoZVxuICogICBuZWFyZXN0IGludGVnZXIgYW5kIGNvbnZlcnRlZCB0byBhIHRlbnNvci5cbiAqIEByZXR1cm5zIEEgdGVuc29yIG9mIHRoZSBzYW1lIHNoYXBlIGFzIGB4YC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBvdyh4OiBUZW5zb3IsIGE6IFRlbnNvcnxudW1iZXIpOiBUZW5zb3Ige1xuICByZXR1cm4gdGlkeSgoKSA9PiB7XG4gICAgaWYgKHR5cGVvZiAoYSkgPT09ICdudW1iZXInKSB7XG4gICAgICBhID0gc2NhbGFyKE1hdGgucm91bmQoYSksICdpbnQzMicpO1xuICAgIH1cbiAgICBpZiAoYS5kdHlwZSAhPT0gJ2ludDMyJykge1xuICAgICAgdGhyb3cgbmV3IE5vdEltcGxlbWVudGVkRXJyb3IoXG4gICAgICAgICAgYE5vbi1pbnQzMiBkdHlwZSAoJHthLmR0eXBlfSkgaXMgbm90IHN1cHBvcnRlZCBieSBwb3coKSB5ZXRgKTtcbiAgICB9XG4gICAgcmV0dXJuIHRmYy5wb3coeCwgYSk7XG4gIH0pO1xufVxuXG4vKipcbiAqIFJlc2hhcGVzIGJpYXMgdGVuc29yIGFjY29yZGluZyB0byByYW5rIG9mIHguXG4gKi9cbmZ1bmN0aW9uIHJlc2hhcGVCaWFzKHhSYW5rOiBudW1iZXIsIGJpYXM6IFRlbnNvciwgZGF0YUZvcm1hdDogc3RyaW5nKSB7XG4gIGNvbnN0IGJpYXNTaGFwZSA9IGJpYXMuc2hhcGU7XG5cbiAgaWYgKGJpYXMucmFuayAhPT0gMSAmJiBiaWFzLnJhbmsgIT09IHhSYW5rKSB7XG4gICAgdGhyb3cgbmV3IFZhbHVlRXJyb3IoXG4gICAgICAgIGBVbmV4cGVjdGVkIGJpYXMgZGltZW5zaW9uczogJHtiaWFzLnJhbmt9YCArXG4gICAgICAgIGA7IGV4cGVjdGVkIGl0IHRvIGJlIDEgb3IgJHt4UmFua31gKTtcbiAgfVxuXG4gIGlmICh4UmFuayA9PT0gNSkge1xuICAgIGlmIChkYXRhRm9ybWF0ID09PSAnY2hhbm5lbHNGaXJzdCcpIHtcbiAgICAgIGlmIChiaWFzU2hhcGUubGVuZ3RoID09PSAxKSB7XG4gICAgICAgIHJldHVybiB0ZmMucmVzaGFwZShiaWFzLCBbMSwgYmlhc1NoYXBlWzBdLCAxLCAxLCAxXSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gdGZjLnJlc2hhcGUoXG4gICAgICAgICAgICBiaWFzLCBbMSwgYmlhc1NoYXBlWzNdLCBiaWFzU2hhcGVbMF0sIGJpYXNTaGFwZVsxXSwgYmlhc1NoYXBlWzJdXSk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChkYXRhRm9ybWF0ID09PSAnY2hhbm5lbHNMYXN0Jykge1xuICAgICAgaWYgKGJpYXNTaGFwZS5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgcmV0dXJuIHRmYy5yZXNoYXBlKGJpYXMsIFsxLCAxLCAxLCAxLCBiaWFzU2hhcGVbMF1dKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiB0ZmMucmVzaGFwZShiaWFzLCBbMV0uY29uY2F0KGJpYXNTaGFwZSkpO1xuICAgICAgfVxuICAgIH1cbiAgfSBlbHNlIGlmICh4UmFuayA9PT0gNCkge1xuICAgIGlmIChkYXRhRm9ybWF0ID09PSAnY2hhbm5lbHNGaXJzdCcpIHtcbiAgICAgIGlmIChiaWFzU2hhcGUubGVuZ3RoID09PSAxKSB7XG4gICAgICAgIHJldHVybiB0ZmMucmVzaGFwZShiaWFzLCBbMSwgYmlhc1NoYXBlWzBdLCAxLCAxXSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gdGZjLnJlc2hhcGUoYmlhcywgWzEsIGJpYXNTaGFwZVsyXSwgYmlhc1NoYXBlWzBdLCBiaWFzU2hhcGVbMV1dKTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKGRhdGFGb3JtYXQgPT09ICdjaGFubmVsc0xhc3QnKSB7XG4gICAgICBpZiAoYmlhc1NoYXBlLmxlbmd0aCA9PT0gMSkge1xuICAgICAgICByZXR1cm4gdGZjLnJlc2hhcGUoYmlhcywgWzEsIDEsIDEsIGJpYXNTaGFwZVswXV0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIHRmYy5yZXNoYXBlKGJpYXMsIFsxXS5jb25jYXQoYmlhc1NoYXBlKSk7XG4gICAgICB9XG4gICAgfVxuICB9IGVsc2UgaWYgKHhSYW5rID09PSAzKSB7XG4gICAgaWYgKGRhdGFGb3JtYXQgPT09ICdjaGFubmVsc0ZpcnN0Jykge1xuICAgICAgaWYgKGJpYXNTaGFwZS5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgcmV0dXJuIHRmYy5yZXNoYXBlKGJpYXMsIFsxLCBiaWFzU2hhcGVbMF0sIDFdKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiB0ZmMucmVzaGFwZShiaWFzLCBbMSwgYmlhc1NoYXBlWzFdLCBiaWFzU2hhcGVbMF1dKTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKGRhdGFGb3JtYXQgPT09ICdjaGFubmVsc0xhc3QnKSB7XG4gICAgICBpZiAoYmlhc1NoYXBlLmxlbmd0aCA9PT0gMSkge1xuICAgICAgICByZXR1cm4gdGZjLnJlc2hhcGUoYmlhcywgWzEsIDEsIGJpYXNTaGFwZVswXV0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIHRmYy5yZXNoYXBlKGJpYXMsIFsxXS5jb25jYXQoYmlhc1NoYXBlKSk7XG4gICAgICB9XG4gICAgfVxuICB9IGVsc2UgaWYgKHhSYW5rIDwgMykge1xuICAgIHJldHVybiBiaWFzO1xuICB9XG4gIHRocm93IG5ldyBWYWx1ZUVycm9yKGBVbnN1cHBvcnRlZCBpbnB1dCByYW5rIGJ5IGJpYXNBZGQ6ICR7Ymlhcy5yYW5rfWApO1xufVxuXG4vKiBOZXVyYWwtbmV0d29yayBvcGVyYXRpb25zLiAqL1xuXG4vKipcbiAqIEFkZCBhIGJpYXMgdG8gYSB0ZW5zb3IuXG4gKlxuICogQHBhcmFtIHggVGhlIHRlbnNvciB0byBhZGQgdGhlIGJpYXMgdG8uXG4gKiBAcGFyYW0gYmlhcyBUaGUgYmlhcyB0byBhZGQgdG8gYHhgLiBNdXN0IGJlIDFEIG9yIHRoZSBzYW1lIHJhbmsgYXMgYHhgLlxuICogQHJldHVybiBSZXN1bHQgb2YgdGhlIGJpYXMgYWRkaW5nLlxuICogQHRocm93cyBWYWx1ZUVycm9yOiBJZiB0aGUgcmFuayBvZiBgYmlhc2AgaXMgaW5jb3JyZWN0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gYmlhc0FkZChcbiAgICB4OiBUZW5zb3IsIGJpYXM6IFRlbnNvciwgZGF0YUZvcm1hdD86IERhdGFGb3JtYXQpOiBUZW5zb3Ige1xuICByZXR1cm4gdGlkeSgoKSA9PiB7XG4gICAgaWYgKGRhdGFGb3JtYXQgPT0gbnVsbCkge1xuICAgICAgZGF0YUZvcm1hdCA9IGltYWdlRGF0YUZvcm1hdCgpO1xuICAgIH1cbiAgICBjaGVja0RhdGFGb3JtYXQoZGF0YUZvcm1hdCk7XG5cbiAgICByZXR1cm4gdGZjLmFkZCh4LCByZXNoYXBlQmlhcyh4LnJhbmssIGJpYXMsIGRhdGFGb3JtYXQpKTtcbiAgfSk7XG59XG5cbi8qKlxuICogRXhwb25lbnRpYWwgbGluZWFyIHVuaXQgKEVMVSkuXG4gKiBAcGFyYW0geCBBIHRlbnNvciBvciB2YXJpYWJsZSB0byBjb21wdXRlIHRoZSBhY3RpdmF0aW9uIGZ1bmN0aW9uIGZvci5cbiAqIEBwYXJhbSBhbHBoYTogQSBzY2FsYXIsIGEgc2NhbGluZyBmYWN0b3IgZm9yIHRoZSBuZWdhdGl2ZSBzZWN0aW9uLlxuICogQHJldHVybiBPdXRwdXQgb2YgdGhlIEVMVSBvcGVyYXRpb24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBlbHUoeDogVGVuc29yLCBhbHBoYSA9IDEpOiBUZW5zb3Ige1xuICAvLyBUT0RPKGNhaXMpOiBBZGQgc3VwcG9ydCBmb3IgYWxwaGEgdmFsdWVzIG90aGVyIHRoYW4gMS5cbiAgaWYgKGFscGhhICE9PSAxKSB7XG4gICAgdGhyb3cgbmV3IE5vdEltcGxlbWVudGVkRXJyb3IoXG4gICAgICAgIGBTdXBwb3J0IGZvciBhbHBoYSB2YWx1ZXMgb3RoZXIgdGhhbiAxICgke2FscGhhfSkgaXMgbm90IGltcGxlbWVudGVkIGAgK1xuICAgICAgICBgeWV0LmApO1xuICB9XG4gIHJldHVybiB0ZmMuZWx1KHgpO1xufVxuXG4vKipcbiAqIFNvZnRzaWduIG9mIGEgdGVuc29yLlxuICpcbiAqIERlZmluZWQgYXMgeCAvIChhYnMoeCkgKyAxKSwgZWxlbWVudC13aXNlLlxuICpcbiAqIEBwYXJhbSB4OiBJbnB1dC5cbiAqIEByZXR1cm5zIE91dHB1dC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNvZnRzaWduKHg6IFRlbnNvcik6IFRlbnNvciB7XG4gIHJldHVybiB0aWR5KCgpID0+IHRmYy5kaXYoeCwgdGZjLmFkZCh0ZmMuYWJzKHgpLCAxKSkpO1xufVxuXG4vKipcbiAqIFNldHMgZW50cmllcyBpbiBgeGAgdG8gemVybyBhdCByYW5kb20sIHdoaWxlIHNjYWxpbmcgdGhlIGVudGlyZSB0ZW5zb3IuXG4gKlxuICogQHBhcmFtIHggaW5wdXQgdGVuc29yLlxuICogQHBhcmFtIGxldmVsIGZyYWN0aW9uIG9mIHRoZSBlbnRyaWVzIGluIHRoZSB0ZW5zb3IgdGhhdCB3aWxsIGJlIHNldCB0byAwLlxuICogQHBhcmFtIG5vaXNlU2hhcGUgc2hhcGUgb2YgcmFuZG9tbHkgZ2VuZXJhdGVkIGtlZXAvZHJvcCBmbGFncywgbXVzdCBiZVxuICogICBicm9hZGNhc3RhYmxlIHRvIHRoZSBzaGFwZSBvZiBgeGAuIE9wdGlvbmFsLlxuICogQHBhcmFtIHNlZWQgcmFuZG9tIHNlZWQgdG8gZW5zdXJlIGRldGVybWluaXNtLiBPcHRpb25hbC5cbiAqIEByZXR1cm5zIFJlc3VsdCBvZiB0aGUgZHJvcG91dCBvcGVyYXRpb24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkcm9wb3V0KFxuICAgIHg6IFRlbnNvciwgbGV2ZWw6IG51bWJlciwgbm9pc2VTaGFwZT86IG51bWJlcltdLCBzZWVkPzogbnVtYmVyKTogVGVuc29yIHtcbiAgcmV0dXJuIHRpZHkoKCkgPT4gdGZjLmRyb3BvdXQoeCwgbGV2ZWwsIG5vaXNlU2hhcGUsIHNlZWQpKTtcbn1cblxuLyoqXG4gKiBFbGVtZW50LXdpc2UsIHNlZ21lbnQtd2lzZSBsaW5lYXIgYXBwcm94aW1hdGlvbiBvZiBzaWdtb2lkLlxuICpcbiAqIFJldHVybnMgYDAuYCBpZiBgeCA8IC0yLjVgLCBgMS5gIGlmIGB4ID4gMi41YC5cbiAqIEluIGAtMi41IDw9IHggPD0gMi41YCwgcmV0dXJucyBgMC4yICogeCArIDAuNWAuXG4gKlxuICogQHBhcmFtIHggSW5wdXQgdGVuc29yLlxuICogQHJldHVybnMgT3V0cHV0IHRlbnNvci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGhhcmRTaWdtb2lkKHg6IFRlbnNvcik6IFRlbnNvciB7XG4gIHJldHVybiB0aWR5KCgpID0+IHtcbiAgICBjb25zdCB5ID0gdGZjLmFkZCguNSwgdGZjLm11bCguMiwgeCkpO1xuICAgIHJldHVybiB0ZmMuY2xpcEJ5VmFsdWUoeSwgMCwgMSk7XG4gIH0pO1xufVxuXG4vKipcbiAqIEludm9rZSBgeGAgaW4gdGhlIHRyYWluaW5nIHBoYXNlLCBhbmQgYGFsdGAgb3RoZXJ3aXNlLlxuICpcbiAqIFBvcnRpbmcgTm90ZTogV2UgZG8gbm90IGNyZWF0ZSBwbGFjZWhvbGRlciB0ZW5zb3JzIGZvciB0aGUgYHRyYWluaW5nYFxuICogYm9vbGVhbiBmbGFnIGhlcmUsIGJlY2F1c2UgdGhlcmUgaXMgbm8gc3VjaCB0aGluZyBpbiB0aGUgVEYuanMgaW1wZXJhdGl2ZVxuICogYmFja2VuZC5cbiAqXG4gKiBAcGFyYW0geCBUaGUgZnVuY3Rpb24gdG8gaW52b2tlIGlmZiBgdHJhaW5pbmdgIGlzIGB0cnVlYC5cbiAqIEBwYXJhbSBhbHQgVGhlIGZ1bmN0aW9uIHRvIGludm9rZSBpZmYgYHRyYWluaW5nYCBpcyBgZmFsc2VgLlxuICogQHBhcmFtIHRyYWluaW5nIEJvb2xlYW4gZmxhZyBmb3Igd2hldGhlciB0cmFpbmluZyBwaGFzZSBpcyBhY3RpdmUuXG4gKiBAcmV0dXJucyBUaGUgcmV0dXJuIHZhbHVlIG9mIGB4KClgIGlmIGB0cmFpbmluZ2AgaXMgYHRydWVgLCBvciB0aGUgcmV0dXJuXG4gKiAgIHZhbHVlIG9mIGBhbHQoKWAgaWYgYHRyYWluaW5nYCBpcyBgZmFsc2VgLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaW5UcmFpblBoYXNlPFQ+KHg6ICgpID0+IFQsIGFsdDogKCkgPT4gVCwgdHJhaW5pbmcgPSBmYWxzZSk6IFQge1xuICByZXR1cm4gdHJhaW5pbmcgPyB4KCkgOiBhbHQoKTtcbn1cbiJdfQ== |
\ | No newline at end of file |