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 | /* Original source: keras/callbacks.py */
|
11 | import { BaseCallback } from './base_callbacks';
|
12 | import { LayersModel } from './engine/training';
|
13 | import { NotImplementedError } from './errors';
|
14 | import { resolveScalarsInLogs } from './logs';
|
15 | export class Callback extends BaseCallback {
|
16 | constructor() {
|
17 | super(...arguments);
|
18 | /** Instance of `keras.models.Model`. Reference of the model being trained. */
|
19 | this.model = null;
|
20 | }
|
21 | setModel(model) {
|
22 | if (!(model instanceof LayersModel)) {
|
23 | throw new Error('model must be a LayersModel, not some other Container');
|
24 | }
|
25 | this.model = model;
|
26 | }
|
27 | }
|
28 | function less(currVal, prevVal) {
|
29 | return currVal < prevVal;
|
30 | }
|
31 | function greater(currVal, prevVal) {
|
32 | return currVal > prevVal;
|
33 | }
|
34 | /**
|
35 | * A Callback that stops training when a monitored quantity has stopped
|
36 | * improving.
|
37 | */
|
38 | export class EarlyStopping extends Callback {
|
39 | constructor(args) {
|
40 | super();
|
41 | if (args == null) {
|
42 | args = {};
|
43 | }
|
44 | if (args.restoreBestWeights) {
|
45 | throw new NotImplementedError('restoreBestWeights = True is not implemented in EarlyStopping yet.');
|
46 | }
|
47 | this.monitor = args.monitor || 'val_loss';
|
48 | this.minDelta = Math.abs(args.minDelta || 0);
|
49 | this.patience = args.patience || 0;
|
50 | this.verbose = args.verbose || 0;
|
51 | this.mode = args.mode || 'auto';
|
52 | this.baseline = args.baseline;
|
53 | if (['auto', 'min', 'max'].indexOf(this.mode) === -1) {
|
54 | console.warn(`EarlyStopping mode '${this.mode}' is invalid. ` +
|
55 | `Falling back to mode 'auto'.`);
|
56 | this.mode = 'auto';
|
57 | }
|
58 | if (this.mode === 'min') {
|
59 | this.monitorFunc = less;
|
60 | }
|
61 | else if (this.mode === 'max') {
|
62 | this.monitorFunc = greater;
|
63 | }
|
64 | else {
|
65 | // For mode === 'auto'.
|
66 | if (this.monitor.indexOf('acc') !== -1) {
|
67 | this.monitorFunc = greater;
|
68 | }
|
69 | else {
|
70 | this.monitorFunc = less;
|
71 | }
|
72 | }
|
73 | if (this.monitorFunc === less) {
|
74 | this.minDelta *= -1;
|
75 | }
|
76 | }
|
77 | async onTrainBegin(logs) {
|
78 | this.wait = 0;
|
79 | this.stoppedEpoch = 0;
|
80 | if (this.baseline != null) {
|
81 | this.best = this.baseline;
|
82 | }
|
83 | else {
|
84 | this.best = this.monitorFunc === less ? Infinity : -Infinity;
|
85 | }
|
86 | }
|
87 | async onEpochEnd(epoch, logs) {
|
88 | await resolveScalarsInLogs(logs);
|
89 | const current = this.getMonitorValue(logs);
|
90 | if (current == null) {
|
91 | return;
|
92 | }
|
93 | if (this.monitorFunc(current - this.minDelta, this.best)) {
|
94 | this.best = current;
|
95 | this.wait = 0;
|
96 | // TODO(cais): Logic for restoreBestWeights.
|
97 | }
|
98 | else {
|
99 | this.wait++;
|
100 | if (this.wait >= this.patience) {
|
101 | this.stoppedEpoch = epoch;
|
102 | this.model.stopTraining = true;
|
103 | }
|
104 | // TODO(cais): Logic for restoreBestWeights.
|
105 | }
|
106 | }
|
107 | async onTrainEnd(logs) {
|
108 | if (this.stoppedEpoch > 0 && this.verbose) {
|
109 | console.log(`Epoch ${this.stoppedEpoch}: early stopping.`);
|
110 | }
|
111 | }
|
112 | getMonitorValue(logs) {
|
113 | if (logs == null) {
|
114 | logs = {};
|
115 | }
|
116 | const monitorValue = logs[this.monitor];
|
117 | if (monitorValue == null) {
|
118 | console.warn(`Metric for EarlyStopping ${this.monitor} is not available. ` +
|
119 | `Available metrics are: ${Object.keys(logs)}`);
|
120 | }
|
121 | return monitorValue;
|
122 | }
|
123 | }
|
124 | /**
|
125 | * Factory function for a Callback that stops training when a monitored
|
126 | * quantity has stopped improving.
|
127 | *
|
128 | * Early stopping is a type of regularization, and protects model against
|
129 | * overfitting.
|
130 | *
|
131 | * The following example based on fake data illustrates how this callback
|
132 | * can be used during `tf.LayersModel.fit()`:
|
133 | *
|
134 | * ```js
|
135 | * const model = tf.sequential();
|
136 | * model.add(tf.layers.dense({
|
137 | * units: 3,
|
138 | * activation: 'softmax',
|
139 | * kernelInitializer: 'ones',
|
140 | * inputShape: [2]
|
141 | * }));
|
142 | * const xs = tf.tensor2d([1, 2, 3, 4], [2, 2]);
|
143 | * const ys = tf.tensor2d([[1, 0, 0], [0, 1, 0]], [2, 3]);
|
144 | * const xsVal = tf.tensor2d([4, 3, 2, 1], [2, 2]);
|
145 | * const ysVal = tf.tensor2d([[0, 0, 1], [0, 1, 0]], [2, 3]);
|
146 | * model.compile(
|
147 | * {loss: 'categoricalCrossentropy', optimizer: 'sgd', metrics: ['acc']});
|
148 | *
|
149 | * // Without the EarlyStopping callback, the val_acc value would be:
|
150 | * // 0.5, 0.5, 0.5, 0.5, ...
|
151 | * // With val_acc being monitored, training should stop after the 2nd epoch.
|
152 | * const history = await model.fit(xs, ys, {
|
153 | * epochs: 10,
|
154 | * validationData: [xsVal, ysVal],
|
155 | * callbacks: tf.callbacks.earlyStopping({monitor: 'val_acc'})
|
156 | * });
|
157 | *
|
158 | * // Expect to see a length-2 array.
|
159 | * console.log(history.history.val_acc);
|
160 | * ```
|
161 | *
|
162 | * @doc {
|
163 | * heading: 'Callbacks',
|
164 | * namespace: 'callbacks'
|
165 | * }
|
166 | */
|
167 | export function earlyStopping(args) {
|
168 | return new EarlyStopping(args);
|
169 | }
|
170 | export const callbacks = { earlyStopping };
|
171 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FsbGJhY2tzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vdGZqcy1sYXllcnMvc3JjL2NhbGxiYWNrcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7R0FRRztBQUVILHlDQUF5QztBQUV6QyxPQUFPLEVBQUMsWUFBWSxFQUFDLE1BQU0sa0JBQWtCLENBQUM7QUFFOUMsT0FBTyxFQUFDLFdBQVcsRUFBQyxNQUFNLG1CQUFtQixDQUFDO0FBQzlDLE9BQU8sRUFBQyxtQkFBbUIsRUFBQyxNQUFNLFVBQVUsQ0FBQztBQUM3QyxPQUFPLEVBQU8sb0JBQW9CLEVBQUMsTUFBTSxRQUFRLENBQUM7QUFFbEQsTUFBTSxPQUFnQixRQUFTLFNBQVEsWUFBWTtJQUFuRDs7UUFDRSw4RUFBOEU7UUFDOUUsVUFBSyxHQUFnQixJQUFJLENBQUM7SUFRNUIsQ0FBQztJQU5VLFFBQVEsQ0FBQyxLQUFnQjtRQUNoQyxJQUFJLENBQUMsQ0FBQyxLQUFLLFlBQVksV0FBVyxDQUFDLEVBQUU7WUFDbkMsTUFBTSxJQUFJLEtBQUssQ0FBQyx1REFBdUQsQ0FBQyxDQUFDO1NBQzFFO1FBQ0QsSUFBSSxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUM7SUFDckIsQ0FBQztDQUNGO0FBNERELFNBQVMsSUFBSSxDQUFDLE9BQWUsRUFBRSxPQUFlO0lBQzVDLE9BQU8sT0FBTyxHQUFHLE9BQU8sQ0FBQztBQUMzQixDQUFDO0FBRUQsU0FBUyxPQUFPLENBQUMsT0FBZSxFQUFFLE9BQWU7SUFDL0MsT0FBTyxPQUFPLEdBQUcsT0FBTyxDQUFDO0FBQzNCLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sYUFBYyxTQUFRLFFBQVE7SUFjekMsWUFBWSxJQUFnQztRQUMxQyxLQUFLLEVBQUUsQ0FBQztRQUNSLElBQUksSUFBSSxJQUFJLElBQUksRUFBRTtZQUNoQixJQUFJLEdBQUcsRUFBRSxDQUFDO1NBQ1g7UUFDRCxJQUFJLElBQUks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ZSBMTENcbiAqXG4gKiBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhbiBNSVQtc3R5bGVcbiAqIGxpY2Vuc2UgdGhhdCBjYW4gYmUgZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBvciBhdFxuICogaHR0cHM6Ly9vcGVuc291cmNlLm9yZy9saWNlbnNlcy9NSVQuXG4gKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICovXG5cbi8qIE9yaWdpbmFsIHNvdXJjZToga2VyYXMvY2FsbGJhY2tzLnB5ICovXG5cbmltcG9ydCB7QmFzZUNhbGxiYWNrfSBmcm9tICcuL2Jhc2VfY2FsbGJhY2tzJztcbmltcG9ydCB7Q29udGFpbmVyfSBmcm9tICcuL2VuZ2luZS9jb250YWluZXInO1xuaW1wb3J0IHtMYXllcnNNb2RlbH0gZnJvbSAnLi9lbmdpbmUvdHJhaW5pbmcnO1xuaW1wb3J0IHtOb3RJbXBsZW1lbnRlZEVycm9yfSBmcm9tICcuL2Vycm9ycyc7XG5pbXBvcnQge0xvZ3MsIHJlc29sdmVTY2FsYXJzSW5Mb2dzfSBmcm9tICcuL2xvZ3MnO1xuXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgQ2FsbGJhY2sgZXh0ZW5kcyBCYXNlQ2FsbGJhY2sge1xuICAvKiogSW5zdGFuY2Ugb2YgYGtlcmFzLm1vZGVscy5Nb2RlbGAuIFJlZmVyZW5jZSBvZiB0aGUgbW9kZWwgYmVpbmcgdHJhaW5lZC4gKi9cbiAgbW9kZWw6IExheWVyc01vZGVsID0gbnVsbDtcblxuICBvdmVycmlkZSBzZXRNb2RlbChtb2RlbDogQ29udGFpbmVyKTogdm9pZCB7XG4gICAgaWYgKCEobW9kZWwgaW5zdGFuY2VvZiBMYXllcnNNb2RlbCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbW9kZWwgbXVzdCBiZSBhIExheWVyc01vZGVsLCBub3Qgc29tZSBvdGhlciBDb250YWluZXInKTtcbiAgICB9XG4gICAgdGhpcy5tb2RlbCA9IG1vZGVsO1xuICB9XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRWFybHlTdG9wcGluZ0NhbGxiYWNrQXJncyB7XG4gIC8qKlxuICAgKiBRdWFudGl0eSB0byBiZSBtb25pdG9yZWQuXG4gICAqXG4gICAqIERlZmF1bHRzIHRvICd2YWxfbG9zcycuXG4gICAqL1xuICBtb25pdG9yPzogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBNaW5pbXVtIGNoYW5nZSBpbiB0aGUgbW9uaXRvcmVkIHF1YW50aXR5IHRvIHF1YWxpZnkgYXMgaW1wcm92ZW1lbnQsXG4gICAqIGkuZS4sIGFuIGFic29sdXRlIGNoYW5nZSBvZiBsZXNzIHRoYW4gYG1pbkRlbHRhYCB3aWxsIGNvdW50IGFzIG5vXG4gICAqIGltcHJvdmVtZW50LlxuICAgKlxuICAgKiBEZWZhdWx0cyB0byAwLlxuICAgKi9cbiAgbWluRGVsdGE/OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIE51bWJlciBvZiBlcG9jaHMgd2l0aCBubyBpbXByb3ZlbWVudCBhZnRlciB3aGljaCB0cmFpbmluZyB3aWxsIGJlIHN0b3BwZWQuXG4gICAqXG4gICAqIERlZmF1bHRzIHRvIDAuXG4gICAqL1xuICBwYXRpZW5jZT86IG51bWJlcjtcblxuICAvKiogVmVyYm9zaXR5IG1vZGUuICovXG4gIHZlcmJvc2U/OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIE1vZGU6IG9uZSBvZiAnbWluJywgJ21heCcsIGFuZCAnYXV0bycuXG4gICAqIC0gSW4gJ21pbicgbW9kZSwgdHJhaW5pbmcgd2lsbCBiZSBzdG9wcGVkIHdoZW4gdGhlIHF1YW50aXR5IG1vbml0b3JlZCBoYXNcbiAgICogICBzdG9wcGVkIGRlY3JlYXNpbmcuXG4gICAqIC0gSW4gJ21heCcgbW9kZSwgdHJhaW5pbmcgd2lsbCBiZSBzdG9wcGVkIHdoZW4gdGhlIHF1YW50aXR5IG1vbml0b3JlZCBoYXNcbiAgICogICBzdG9wcGVkIGluY3JlYXNpbmcuXG4gICAqIC0gSW4gJ2F1dG8nIG1vZGUsIHRoZSBkaXJlY3Rpb24gaXMgaW5mZXJyZWQgYXV0b21hdGljYWxseSBmcm9tIHRoZSBuYW1lIG9mXG4gICAqICAgdGhlIG1vbml0b3JlZCBxdWFudGl0eS5cbiAgICpcbiAgICogRGVmYXVsdHMgdG8gJ2F1dG8nLlxuICAgKi9cbiAgbW9kZT86ICdhdXRvJ3wnbWluJ3wnbWF4JztcblxuICAvKipcbiAgICogQmFzZWxpbmUgdmFsdWUgb2YgdGhlIG1vbml0b3JlZCBxdWFudGl0eS5cbiAgICpcbiAgICogSWYgc3BlY2lmaWVkLCB0cmFpbmluZyB3aWxsIGJlIHN0b3BwZWQgaWYgdGhlIG1vZGVsIGRvZXNuJ3Qgc2hvd1xuICAgKiBpbXByb3ZlbWVudCBvdmVyIHRoZSBiYXNlbGluZS5cbiAgICovXG4gIGJhc2VsaW5lPzogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHRvIHJlc3RvcmUgbW9kZWwgd2VpZ2h0cyBmcm9tIHRoZSBlcG9jaCB3aXRoIHRoZSBiZXN0IHZhbHVlXG4gICAqIG9mIHRoZSBtb25pdG9yZWQgcXVhbnRpdHkuIElmIGBGYWxzZWAsIHRoZSBtb2RlbCB3ZWlnaHRzIG9idGFpbmVkIGF0IHRoZVxuICAgKiBsYXN0IHN0ZXAgb2YgdHJhaW5pbmcgYXJlIHVzZWQuXG4gICAqXG4gICAqICoqYFRydWVgIGlzIG5vdCBzdXBwb3J0ZWQgeWV0LioqXG4gICAqL1xuICByZXN0b3JlQmVzdFdlaWdodHM/OiBib29sZWFuO1xufVxuXG5mdW5jdGlvbiBsZXNzKGN1cnJWYWw6IG51bWJlciwgcHJldlZhbDogbnVtYmVyKSB7XG4gIHJldHVybiBjdXJyVmFsIDwgcHJldlZhbDtcbn1cblxuZnVuY3Rpb24gZ3JlYXRlcihjdXJyVmFsOiBudW1iZXIsIHByZXZWYWw6IG51bWJlcikge1xuICByZXR1cm4gY3VyclZhbCA+IHByZXZWYWw7XG59XG5cbi8qKlxuICogQSBDYWxsYmFjayB0aGF0IHN0b3BzIHRyYWluaW5nIHdoZW4gYSBtb25pdG9yZWQgcXVhbnRpdHkgaGFzIHN0b3BwZWRcbiAqIGltcHJvdmluZy5cbiAqL1xuZXhwb3J0IGNsYXNzIEVhcmx5U3RvcHBpbmcgZXh0ZW5kcyBDYWxsYmFjayB7XG4gIHByb3RlY3RlZCByZWFkb25seSBtb25pdG9yOiBzdHJpbmc7XG4gIHByb3RlY3RlZCByZWFkb25seSBtaW5EZWx0YTogbnVtYmVyO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgcGF0aWVuY2U6IG51bWJlcjtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGJhc2VsaW5lOiBudW1iZXI7XG4gIHByb3RlY3RlZCByZWFkb25seSB2ZXJib3NlOiBudW1iZXI7XG4gIHByb3RlY3RlZCByZWFkb25seSBtb2RlOiAnYXV0byd8J21pbid8J21heCc7XG5cbiAgcHJvdGVjdGVkIG1vbml0b3JGdW5jOiAoY3VyclZhbDogbnVtYmVyLCBwcmV2VmFsOiBudW1iZXIpID0+IGJvb2xlYW47XG5cbiAgcHJpdmF0ZSB3YWl0OiBudW1iZXI7XG4gIHByaXZhdGUgc3RvcHBlZEVwb2NoOiBudW1iZXI7XG4gIHByaXZhdGUgYmVzdDogbnVtYmVyO1xuXG4gIGNvbnN0cnVjdG9yKGFyZ3M/OiBFYXJseVN0b3BwaW5nQ2FsbGJhY2tBcmdzKSB7XG4gICAgc3VwZXIoKTtcbiAgICBpZiAoYXJncyA9PSBudWxsKSB7XG4gICAgICBhcmdzID0ge307XG4gICAgfVxuICAgIGlmIChhcmdzLnJlc3RvcmVCZXN0V2VpZ2h0cykge1xuICAgICAgdGhyb3cgbmV3IE5vdEltcGxlbWVudGVkRXJyb3IoXG4gICAgICAgICAgJ3Jlc3RvcmVCZXN0V2VpZ2h0cyA9IFRydWUgaXMgbm90IGltcGxlbWVudGVkIGluIEVhcmx5U3RvcHBpbmcgeWV0LicpO1xuICAgIH1cblxuICAgIHRoaXMubW9uaXRvciA9IGFyZ3MubW9uaXRvciB8fCAndmFsX2xvc3MnO1xuICAgIHRoaXMubWluRGVsdGEgPSBNYXRoLmFicyhhcmdzLm1pbkRlbHRhIHx8IDApO1xuICAgIHRoaXMucGF0aWVuY2UgPSBhcmdzLnBhdGllbmNlIHx8IDA7XG4gICAgdGhpcy52ZXJib3NlID0gYXJncy52ZXJib3NlIHx8IDA7XG4gICAgdGhpcy5tb2RlID0gYXJncy5tb2RlIHx8ICdhdXRvJztcbiAgICB0aGlzLmJhc2VsaW5lID0gYXJncy5iYXNlbGluZTtcblxuICAgIGlmIChbJ2F1dG8nLCAnbWluJywgJ21heCddLmluZGV4T2YodGhpcy5tb2RlKSA9PT0gLTEpIHtcbiAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgICBgRWFybHlTdG9wcGluZyBtb2RlICcke3RoaXMubW9kZX0nIGlzIGludmFsaWQuIGAgK1xuICAgICAgICAgIGBGYWxsaW5nIGJhY2sgdG8gbW9kZSAnYXV0bycuYCk7XG4gICAgICB0aGlzLm1vZGUgPSAnYXV0byc7XG4gICAgfVxuXG4gICAgaWYgKHRoaXMubW9kZSA9PT0gJ21pbicpIHtcbiAgICAgIHRoaXMubW9uaXRvckZ1bmMgPSBsZXNzO1xuICAgIH0gZWxzZSBpZiAodGhpcy5tb2RlID09PSAnbWF4Jykge1xuICAgICAgdGhpcy5tb25pdG9yRnVuYyA9IGdyZWF0ZXI7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIEZvciBtb2RlID09PSAnYXV0bycuXG4gICAgICBpZiAodGhpcy5tb25pdG9yLmluZGV4T2YoJ2FjYycpICE9PSAtMSkge1xuICAgICAgICB0aGlzLm1vbml0b3JGdW5jID0gZ3JlYXRlcjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMubW9uaXRvckZ1bmMgPSBsZXNzO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmICh0aGlzLm1vbml0b3JGdW5jID09PSBsZXNzKSB7XG4gICAgICB0aGlzLm1pbkRlbHRhICo9IC0xO1xuICAgIH1cbiAgfVxuXG4gIG92ZXJyaWRlIGFzeW5jIG9uVHJhaW5CZWdpbihsb2dzPzogTG9ncykge1xuICAgIHRoaXMud2FpdCA9IDA7XG4gICAgdGhpcy5zdG9wcGVkRXBvY2ggPSAwO1xuICAgIGlmICh0aGlzLmJhc2VsaW5lICE9IG51bGwpIHtcbiAgICAgIHRoaXMuYmVzdCA9IHRoaXMuYmFzZWxpbmU7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuYmVzdCA9IHRoaXMubW9uaXRvckZ1bmMgPT09IGxlc3MgPyBJbmZpbml0eSA6IC1JbmZpbml0eTtcbiAgICB9XG4gIH1cblxuICBvdmVycmlkZSBhc3luYyBvbkVwb2NoRW5kKGVwb2NoOiBudW1iZXIsIGxvZ3M/OiBMb2dzKSB7XG4gICAgYXdhaXQgcmVzb2x2ZVNjYWxhcnNJbkxvZ3MobG9ncyk7XG4gICAgY29uc3QgY3VycmVudCA9IHRoaXMuZ2V0TW9uaXRvclZhbHVlKGxvZ3MpO1xuICAgIGlmIChjdXJyZW50ID09IG51bGwpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5tb25pdG9yRnVuYyhjdXJyZW50IC0gdGhpcy5taW5EZWx0YSwgdGhpcy5iZXN0KSkge1xuICAgICAgdGhpcy5iZXN0ID0gY3VycmVudDtcbiAgICAgIHRoaXMud2FpdCA9IDA7XG4gICAgICAvLyBUT0RPKGNhaXMpOiBMb2dpYyBmb3IgcmVzdG9yZUJlc3RXZWlnaHRzLlxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLndhaXQrKztcbiAgICAgIGlmICh0aGlzLndhaXQgPj0gdGhpcy5wYXRpZW5jZSkge1xuICAgICAgICB0aGlzLnN0b3BwZWRFcG9jaCA9IGVwb2NoO1xuICAgICAgICB0aGlzLm1vZGVsLnN0b3BUcmFpbmluZyA9IHRydWU7XG4gICAgICB9XG4gICAgICAvLyBUT0RPKGNhaXMpOiBMb2dpYyBmb3IgcmVzdG9yZUJlc3RXZWlnaHRzLlxuICAgIH1cbiAgfVxuXG4gIG92ZXJyaWRlIGFzeW5jIG9uVHJhaW5FbmQobG9ncz86IExvZ3MpIHtcbiAgICBpZiAodGhpcy5zdG9wcGVkRXBvY2ggPiAwICYmIHRoaXMudmVyYm9zZSkge1xuICAgICAgY29uc29sZS5sb2coYEVwb2NoICR7dGhpcy5zdG9wcGVkRXBvY2h9OiBlYXJseSBzdG9wcGluZy5gKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGdldE1vbml0b3JWYWx1ZShsb2dzOiBMb2dzKSB7XG4gICAgaWYgKGxvZ3MgPT0gbnVsbCkge1xuICAgICAgbG9ncyA9IHt9O1xuICAgIH1cbiAgICBjb25zdCBtb25pdG9yVmFsdWUgPSBsb2dzW3RoaXMubW9uaXRvcl07XG4gICAgaWYgKG1vbml0b3JWYWx1ZSA9PSBudWxsKSB7XG4gICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgYE1ldHJpYyBmb3IgRWFybHlTdG9wcGluZyAke3RoaXMubW9uaXRvcn0gaXMgbm90IGF2YWlsYWJsZS4gYCArXG4gICAgICAgICAgYEF2YWlsYWJsZSBtZXRyaWNzIGFyZTogJHtPYmplY3Qua2V5cyhsb2dzKX1gKTtcbiAgICB9XG4gICAgcmV0dXJuIG1vbml0b3JWYWx1ZTtcbiAgfVxufVxuXG4vKipcbiAqIEZhY3RvcnkgZnVuY3Rpb24gZm9yIGEgQ2FsbGJhY2sgdGhhdCBzdG9wcyB0cmFpbmluZyB3aGVuIGEgbW9uaXRvcmVkXG4gKiBxdWFudGl0eSBoYXMgc3RvcHBlZCBpbXByb3ZpbmcuXG4gKlxuICogRWFybHkgc3RvcHBpbmcgaXMgYSB0eXBlIG9mIHJlZ3VsYXJpemF0aW9uLCBhbmQgcHJvdGVjdHMgbW9kZWwgYWdhaW5zdFxuICogb3ZlcmZpdHRpbmcuXG4gKlxuICogVGhlIGZvbGxvd2luZyBleGFtcGxlIGJhc2VkIG9uIGZha2UgZGF0YSBpbGx1c3RyYXRlcyBob3cgdGhpcyBjYWxsYmFja1xuICogY2FuIGJlIHVzZWQgZHVyaW5nIGB0Zi5MYXllcnNNb2RlbC5maXQoKWA6XG4gKlxuICogYGBganNcbiAqIGNvbnN0IG1vZGVsID0gdGYuc2VxdWVudGlhbCgpO1xuICogbW9kZWwuYWRkKHRmLmxheWVycy5kZW5zZSh7XG4gKiAgIHVuaXRzOiAzLFxuICogICBhY3RpdmF0aW9uOiAnc29mdG1heCcsXG4gKiAgIGtlcm5lbEluaXRpYWxpemVyOiAnb25lcycsXG4gKiAgIGlucHV0U2hhcGU6IFsyXVxuICogfSkpO1xuICogY29uc3QgeHMgPSB0Zi50ZW5zb3IyZChbMSwgMiwgMywgNF0sIFsyLCAyXSk7XG4gKiBjb25zdCB5cyA9IHRmLnRlbnNvcjJkKFtbMSwgMCwgMF0sIFswLCAxLCAwXV0sIFsyLCAzXSk7XG4gKiBjb25zdCB4c1ZhbCA9IHRmLnRlbnNvcjJkKFs0LCAzLCAyLCAxXSwgWzIsIDJdKTtcbiAqIGNvbnN0IHlzVmFsID0gdGYudGVuc29yMmQoW1swLCAwLCAxXSwgWzAsIDEsIDBdXSwgWzIsIDNdKTtcbiAqIG1vZGVsLmNvbXBpbGUoXG4gKiAgICAge2xvc3M6ICdjYXRlZ29yaWNhbENyb3NzZW50cm9weScsIG9wdGltaXplcjogJ3NnZCcsIG1ldHJpY3M6IFsnYWNjJ119KTtcbiAqXG4gKiAvLyBXaXRob3V0IHRoZSBFYXJseVN0b3BwaW5nIGNhbGxiYWNrLCB0aGUgdmFsX2FjYyB2YWx1ZSB3b3VsZCBiZTpcbiAqIC8vICAgMC41LCAwLjUsIDAuNSwgMC41LCAuLi5cbiAqIC8vIFdpdGggdmFsX2FjYyBiZWluZyBtb25pdG9yZWQsIHRyYWluaW5nIHNob3VsZCBzdG9wIGFmdGVyIHRoZSAybmQgZXBvY2guXG4gKiBjb25zdCBoaXN0b3J5ID0gYXdhaXQgbW9kZWwuZml0KHhzLCB5cywge1xuICogICBlcG9jaHM6IDEwLFxuICogICB2YWxpZGF0aW9uRGF0YTogW3hzVmFsLCB5c1ZhbF0sXG4gKiAgIGNhbGxiYWNrczogdGYuY2FsbGJhY2tzLmVhcmx5U3RvcHBpbmcoe21vbml0b3I6ICd2YWxfYWNjJ30pXG4gKiB9KTtcbiAqXG4gKiAvLyBFeHBlY3QgdG8gc2VlIGEgbGVuZ3RoLTIgYXJyYXkuXG4gKiBjb25zb2xlLmxvZyhoaXN0b3J5Lmhpc3RvcnkudmFsX2FjYyk7XG4gKiBgYGBcbiAqXG4gKiBAZG9jIHtcbiAqICAgaGVhZGluZzogJ0NhbGxiYWNrcycsXG4gKiAgIG5hbWVzcGFjZTogJ2NhbGxiYWNrcydcbiAqIH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGVhcmx5U3RvcHBpbmcoYXJncz86IEVhcmx5U3RvcHBpbmdDYWxsYmFja0FyZ3MpIHtcbiAgcmV0dXJuIG5ldyBFYXJseVN0b3BwaW5nKGFyZ3MpO1xufVxuXG5leHBvcnQgY29uc3QgY2FsbGJhY2tzID0ge2Vhcmx5U3RvcHBpbmd9O1xuIl19 |
\ | No newline at end of file |