ketcher-standalone
Version:
Web-based molecule sketcher
592 lines (518 loc) • 15.3 MB
JavaScript
/****************************************************************************
* Copyright 2021 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties');
var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
var _asyncToGenerator = require('@babel/runtime/helpers/asyncToGenerator');
var _classCallCheck = require('@babel/runtime/helpers/classCallCheck');
var _createClass = require('@babel/runtime/helpers/createClass');
var _defineProperty = require('@babel/runtime/helpers/defineProperty');
var _regeneratorRuntime = require('@babel/runtime/regenerator');
var ketcherCore = require('ketcher-core');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var _objectWithoutProperties__default = /*#__PURE__*/_interopDefaultLegacy(_objectWithoutProperties);
var _slicedToArray__default = /*#__PURE__*/_interopDefaultLegacy(_slicedToArray);
var _asyncToGenerator__default = /*#__PURE__*/_interopDefaultLegacy(_asyncToGenerator);
var _classCallCheck__default = /*#__PURE__*/_interopDefaultLegacy(_classCallCheck);
var _createClass__default = /*#__PURE__*/_interopDefaultLegacy(_createClass);
var _defineProperty__default = /*#__PURE__*/_interopDefaultLegacy(_defineProperty);
var _regeneratorRuntime__default = /*#__PURE__*/_interopDefaultLegacy(_regeneratorRuntime);
var Command;
(function (Command) {
Command[Command["Info"] = 0] = "Info";
Command[Command["Convert"] = 1] = "Convert";
Command[Command["Layout"] = 2] = "Layout";
Command[Command["Clean"] = 3] = "Clean";
Command[Command["Aromatize"] = 4] = "Aromatize";
Command[Command["Dearomatize"] = 5] = "Dearomatize";
Command[Command["CalculateCip"] = 6] = "CalculateCip";
Command[Command["Automap"] = 7] = "Automap";
Command[Command["Check"] = 8] = "Check";
Command[Command["Calculate"] = 9] = "Calculate";
Command[Command["GenerateImageAsBase64"] = 10] = "GenerateImageAsBase64";
Command[Command["GetInChIKey"] = 11] = "GetInChIKey";
Command[Command["ExplicitHydrogens"] = 12] = "ExplicitHydrogens";
})(Command || (Command = {}));
var WorkerEvent;
(function (WorkerEvent) {
WorkerEvent["Info"] = "info";
WorkerEvent["Convert"] = "convert";
WorkerEvent["Layout"] = "layout";
WorkerEvent["Clean"] = "clean";
WorkerEvent["Aromatize"] = "aromatize";
WorkerEvent["Dearomatize"] = "dearomatize";
WorkerEvent["CalculateCip"] = "calculateCip";
WorkerEvent["Automap"] = "automap";
WorkerEvent["Check"] = "check";
WorkerEvent["Calculate"] = "calculate";
WorkerEvent["GenerateImageAsBase64"] = "generateImageAsBase64";
WorkerEvent["GetInChIKey"] = "getInChIKey";
WorkerEvent["ExplicitHydrogens"] = "convert_explicit_hydrogens";
})(WorkerEvent || (WorkerEvent = {}));
var SupportedFormat;
(function (SupportedFormat) {
SupportedFormat["Rxn"] = "rxnfile";
SupportedFormat["Mol"] = "molfile";
SupportedFormat["Smiles"] = "smiles";
SupportedFormat["Smarts"] = "smarts";
SupportedFormat["CML"] = "cml";
SupportedFormat["InChI"] = "inchi";
SupportedFormat["InChIAuxInfo"] = "inchi-aux";
SupportedFormat["InChIKey"] = "inchi-key";
SupportedFormat["Ket"] = "ket";
SupportedFormat["CDX"] = "cdx";
SupportedFormat["CDXML"] = "cdxml";
SupportedFormat["SDF"] = "sdf";
SupportedFormat["FASTA"] = "fasta";
SupportedFormat["SEQUENCE"] = "sequence";
SupportedFormat["SEQUENCE_3_LETTER"] = "peptide-sequence-3-letter";
SupportedFormat["IDT"] = "idt";
SupportedFormat["HELM"] = "helm";
SupportedFormat["RDF"] = "rdf";
})(SupportedFormat || (SupportedFormat = {}));
var domain;
// This constructor is used to store event handlers. Instantiating this is
// faster than explicitly calling `Object.create(null)` to get a "clean" empty
// object (tested with v8 v4.9).
function EventHandlers() {}
EventHandlers.prototype = Object.create(null);
function EventEmitter() {
EventEmitter.init.call(this);
}
// nodejs oddity
// require('events') === require('events').EventEmitter
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.usingDomains = false;
EventEmitter.prototype.domain = undefined;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
EventEmitter.defaultMaxListeners = 10;
EventEmitter.init = function() {
this.domain = null;
if (EventEmitter.usingDomains) {
// if there is an active domain, then attach to it.
if (domain.active ) ;
}
if (!this._events || this._events === Object.getPrototypeOf(this)._events) {
this._events = new EventHandlers();
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || undefined;
};
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || isNaN(n))
throw new TypeError('"n" argument must be a positive number');
this._maxListeners = n;
return this;
};
function $getMaxListeners(that) {
if (that._maxListeners === undefined)
return EventEmitter.defaultMaxListeners;
return that._maxListeners;
}
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
return $getMaxListeners(this);
};
// These standalone emit* functions are used to optimize calling of event
// handlers for fast cases because emit() itself often has a variable number of
// arguments and can be deoptimized because of that. These functions always have
// the same number of arguments and thus do not get deoptimized, so the code
// inside them can execute faster.
function emitNone(handler, isFn, self) {
if (isFn)
handler.call(self);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self);
}
}
function emitOne(handler, isFn, self, arg1) {
if (isFn)
handler.call(self, arg1);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1);
}
}
function emitTwo(handler, isFn, self, arg1, arg2) {
if (isFn)
handler.call(self, arg1, arg2);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1, arg2);
}
}
function emitThree(handler, isFn, self, arg1, arg2, arg3) {
if (isFn)
handler.call(self, arg1, arg2, arg3);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1, arg2, arg3);
}
}
function emitMany(handler, isFn, self, args) {
if (isFn)
handler.apply(self, args);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].apply(self, args);
}
}
EventEmitter.prototype.emit = function emit(type) {
var er, handler, len, args, i, events, domain;
var doError = (type === 'error');
events = this._events;
if (events)
doError = (doError && events.error == null);
else if (!doError)
return false;
domain = this.domain;
// If there is no 'error' event listener then throw.
if (doError) {
er = arguments[1];
if (domain) {
if (!er)
er = new Error('Uncaught, unspecified "error" event');
er.domainEmitter = this;
er.domain = domain;
er.domainThrown = false;
domain.emit('error', er);
} else if (er instanceof Error) {
throw er; // Unhandled 'error' event
} else {
// At least give some kind of context to the user
var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
err.context = er;
throw err;
}
return false;
}
handler = events[type];
if (!handler)
return false;
var isFn = typeof handler === 'function';
len = arguments.length;
switch (len) {
// fast cases
case 1:
emitNone(handler, isFn, this);
break;
case 2:
emitOne(handler, isFn, this, arguments[1]);
break;
case 3:
emitTwo(handler, isFn, this, arguments[1], arguments[2]);
break;
case 4:
emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
break;
// slower
default:
args = new Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
emitMany(handler, isFn, this, args);
}
return true;
};
function _addListener(target, type, listener, prepend) {
var m;
var events;
var existing;
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
events = target._events;
if (!events) {
events = target._events = new EventHandlers();
target._eventsCount = 0;
} else {
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (events.newListener) {
target.emit('newListener', type,
listener.listener ? listener.listener : listener);
// Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object
events = target._events;
}
existing = events[type];
}
if (!existing) {
// Optimize the case of one listener. Don't need the extra array object.
existing = events[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === 'function') {
// Adding the second element, need to change to array.
existing = events[type] = prepend ? [listener, existing] :
[existing, listener];
} else {
// If we've already got an array, just append.
if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
}
// Check for listener leak
if (!existing.warned) {
m = $getMaxListeners(target);
if (m && m > 0 && existing.length > m) {
existing.warned = true;
var w = new Error('Possible EventEmitter memory leak detected. ' +
existing.length + ' ' + type + ' listeners added. ' +
'Use emitter.setMaxListeners() to increase limit');
w.name = 'MaxListenersExceededWarning';
w.emitter = target;
w.type = type;
w.count = existing.length;
emitWarning(w);
}
}
}
return target;
}
function emitWarning(e) {
typeof console.warn === 'function' ? console.warn(e) : console.log(e);
}
EventEmitter.prototype.addListener = function addListener(type, listener) {
return _addListener(this, type, listener, false);
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.prependListener =
function prependListener(type, listener) {
return _addListener(this, type, listener, true);
};
function _onceWrap(target, type, listener) {
var fired = false;
function g() {
target.removeListener(type, g);
if (!fired) {
fired = true;
listener.apply(target, arguments);
}
}
g.listener = listener;
return g;
}
EventEmitter.prototype.once = function once(type, listener) {
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
this.on(type, _onceWrap(this, type, listener));
return this;
};
EventEmitter.prototype.prependOnceListener =
function prependOnceListener(type, listener) {
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
this.prependListener(type, _onceWrap(this, type, listener));
return this;
};
// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener =
function removeListener(type, listener) {
var list, events, position, i, originalListener;
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
events = this._events;
if (!events)
return this;
list = events[type];
if (!list)
return this;
if (list === listener || (list.listener && list.listener === listener)) {
if (--this._eventsCount === 0)
this._events = new EventHandlers();
else {
delete events[type];
if (events.removeListener)
this.emit('removeListener', type, list.listener || listener);
}
} else if (typeof list !== 'function') {
position = -1;
for (i = list.length; i-- > 0;) {
if (list[i] === listener ||
(list[i].listener && list[i].listener === listener)) {
originalListener = list[i].listener;
position = i;
break;
}
}
if (position < 0)
return this;
if (list.length === 1) {
list[0] = undefined;
if (--this._eventsCount === 0) {
this._events = new EventHandlers();
return this;
} else {
delete events[type];
}
} else {
spliceOne(list, position);
}
if (events.removeListener)
this.emit('removeListener', type, originalListener || listener);
}
return this;
};
EventEmitter.prototype.removeAllListeners =
function removeAllListeners(type) {
var listeners, events;
events = this._events;
if (!events)
return this;
// not listening for removeListener, no need to emit
if (!events.removeListener) {
if (arguments.length === 0) {
this._events = new EventHandlers();
this._eventsCount = 0;
} else if (events[type]) {
if (--this._eventsCount === 0)
this._events = new EventHandlers();
else
delete events[type];
}
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
var keys = Object.keys(events);
for (var i = 0, key; i < keys.length; ++i) {
key = keys[i];
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = new EventHandlers();
this._eventsCount = 0;
return this;
}
listeners = events[type];
if (typeof listeners === 'function') {
this.removeListener(type, listeners);
} else if (listeners) {
// LIFO order
do {
this.removeListener(type, listeners[listeners.length - 1]);
} while (listeners[0]);
}
return this;
};
EventEmitter.prototype.listeners = function listeners(type) {
var evlistener;
var ret;
var events = this._events;
if (!events)
ret = [];
else {
evlistener = events[type];
if (!evlistener)
ret = [];
else if (typeof evlistener === 'function')
ret = [evlistener.listener || evlistener];
else
ret = unwrapListeners(evlistener);
}
return ret;
};
EventEmitter.listenerCount = function(emitter, type) {
if (typeof emitter.listenerCount === 'function') {
return emitter.listenerCount(type);
} else {
return listenerCount.call(emitter, type);
}
};
EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
var events = this._events;
if (events) {
var evlistener = events[type];
if (typeof evlistener === 'function') {
return 1;
} else if (evlistener) {
return evlistener.length;
}
}
return 0;
}
EventEmitter.prototype.eventNames = function eventNames() {
return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
};
// About 1.5x faster than the two-arg version of Array#splice().
function spliceOne(list, index) {
for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
list[i] = list[k];
list.pop();
}
function arrayClone(arr, i) {
var copy = new Array(i);
while (i--)
copy[i] = arr[i];
return copy;
}
function unwrapListeners(arr) {
var ret = new Array(arr.length);
for (var i = 0; i < ret.length; ++i) {
ret[i] = arr[i].listener || arr[i];
}
return ret;
}
var STRUCT_SERVICE_NO_RENDER_INITIALIZED_EVENT = 'struct-service-no-render-initialized';
var STRUCT_SERVICE_INITIALIZED_EVENT = 'struct-service-initialized';
function decodeBase64(base64, enableUnicode) {
var binaryString = atob(base64);
if (enableUnicode) {
var binaryView = new Uint8Array(binaryString.length);
for (var i = 0, n = binaryString.length; i < n; ++i) {
binaryView[i] = binaryString.charCodeAt(i);
}
return String.fromCharCode.apply(null, new Uint16Array(binaryView.buffer));
}
return binaryString;
}
function createURL(base64, sourcemapArg, enableUnicodeArg) {
var sourcemap = sourcemapArg === undefined ? null : sourcemapArg;
var enableUnicode = enableUnicodeArg === undefined ? false : enableUnicodeArg;
var source = decodeBase64(base64, enableUnicode);
var start = source.indexOf('\n', 10) + 1;
var body = source.substring(start) + (sourcemap ? '\/\/# sourceMappingURL=' + sourcemap : '');
var blob = new Blob([body], { type: 'application/javascript' });
return URL.createObjectURL(blob);
}
function createBase64WorkerFactory(base64, sourcemapArg, enableUnicodeArg) {
var url;
return function WorkerFactory(options) {
url = url || createURL(base64, sourcemapArg, enableUnicodeArg);
return new Worker(url, options);
};
}
var WorkerFactory = createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewoJJ3VzZSBzdHJpY3QnOwoKCWZ1bmN0aW9uIGdldERlZmF1bHRFeHBvcnRGcm9tQ2pzICh4KSB7CgkJcmV0dXJuIHggJiYgeC5fX2VzTW9kdWxlICYmIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh4LCAnZGVmYXVsdCcpID8geFsnZGVmYXVsdCddIDogeDsKCX0KCglmdW5jdGlvbiBjcmVhdGVDb21tb25qc01vZHVsZShmbiwgYmFzZWRpciwgbW9kdWxlKSB7CgkJcmV0dXJuIG1vZHVsZSA9IHsKCQkJcGF0aDogYmFzZWRpciwKCQkJZXhwb3J0czoge30sCgkJCXJlcXVpcmU6IGZ1bmN0aW9uIChwYXRoLCBiYXNlKSB7CgkJCQlyZXR1cm4gY29tbW9uanNSZXF1aXJlKHBhdGgsIChiYXNlID09PSB1bmRlZmluZWQgfHwgYmFzZSA9PT0gbnVsbCkgPyBtb2R1bGUucGF0aCA6IGJhc2UpOwoJCQl9CgkJfSwgZm4obW9kdWxlLCBtb2R1bGUuZXhwb3J0cyksIG1vZHVsZS5leHBvcnRzOwoJfQoKCWZ1bmN0aW9uIGdldEF1Z21lbnRlZE5hbWVzcGFjZShuKSB7CgkJaWYgKG4uX19lc01vZHVsZSkgcmV0dXJuIG47CgkJdmFyIGEgPSBPYmplY3QuZGVmaW5lUHJvcGVydHkoe30sICdfX2VzTW9kdWxlJywge3ZhbHVlOiB0cnVlfSk7CgkJT2JqZWN0LmtleXMobikuZm9yRWFjaChmdW5jdGlvbiAoaykgewoJCQl2YXIgZCA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3Iobiwgayk7CgkJCU9iamVjdC5kZWZpbmVQcm9wZXJ0eShhLCBrLCBkLmdldCA/IGQgOiB7CgkJCQllbnVtZXJhYmxlOiB0cnVlLAoJCQkJZ2V0OiBmdW5jdGlvbiAoKSB7CgkJCQkJcmV0dXJuIG5ba107CgkJCQl9CgkJCX0pOwoJCX0pOwoJCXJldHVybiBhOwoJfQoKCWZ1bmN0aW9uIGNvbW1vbmpzUmVxdWlyZSAoKSB7CgkJdGhyb3cgbmV3IEVycm9yKCdEeW5hbWljIHJlcXVpcmVzIGFyZSBub3QgY3VycmVudGx5IHN1cHBvcnRlZCBieSBAcm9sbHVwL3BsdWdpbi1jb21tb25qcycpOwoJfQoKCXZhciBfdHlwZW9mXzEgPSBjcmVhdGVDb21tb25qc01vZHVsZShmdW5jdGlvbiAobW9kdWxlKSB7CglmdW5jdGlvbiBfdHlwZW9mKG8pIHsKCSAgIkBiYWJlbC9oZWxwZXJzIC0gdHlwZW9mIjsKCgkgIHJldHVybiAobW9kdWxlLmV4cG9ydHMgPSBfdHlwZW9mID0gImZ1bmN0aW9uIiA9PSB0eXBlb2YgU3ltYm9sICYmICJzeW1ib2wiID09IHR5cGVvZiBTeW1ib2wuaXRlcmF0b3IgPyBmdW5jdGlvbiAobykgewoJICAgIHJldHVybiB0eXBlb2YgbzsKCSAgfSA6IGZ1bmN0aW9uIChvKSB7CgkgICAgcmV0dXJuIG8gJiYgImZ1bmN0aW9uIiA9PSB0eXBlb2YgU3ltYm9sICYmIG8uY29uc3RydWN0b3IgPT09IFN5bWJvbCAmJiBvICE9PSBTeW1ib2wucHJvdG90eXBlID8gInN5bWJvbCIgOiB0eXBlb2YgbzsKCSAgfSwgbW9kdWxlLmV4cG9ydHMuX19lc01vZHVsZSA9IHRydWUsIG1vZHVsZS5leHBvcnRzWyJkZWZhdWx0Il0gPSBtb2R1bGUuZXhwb3J0cyksIF90eXBlb2Yobyk7Cgl9Cgltb2R1bGUuZXhwb3J0cyA9IF90eXBlb2YsIG1vZHVsZS5leHBvcnRzLl9fZXNNb2R1bGUgPSB0cnVlLCBtb2R1bGUuZXhwb3J0c1siZGVmYXVsdCJdID0gbW9kdWxlLmV4cG9ydHM7Cgl9KTsKCgl2YXIgdG9QcmltaXRpdmVfMSA9IGNyZWF0ZUNvbW1vbmpzTW9kdWxlKGZ1bmN0aW9uIChtb2R1bGUpIHsKCXZhciBfdHlwZW9mID0gX3R5cGVvZl8xWyJkZWZhdWx0Il07CglmdW5jdGlvbiB0b1ByaW1pdGl2ZSh0LCByKSB7CgkgIGlmICgib2JqZWN0IiAhPSBfdHlwZW9mKHQpIHx8ICF0KSByZXR1cm4gdDsKCSAgdmFyIGUgPSB0W1N5bWJvbC50b1ByaW1pdGl2ZV07CgkgIGlmICh2b2lkIDAgIT09IGUpIHsKCSAgICB2YXIgaSA9IGUuY2FsbCh0LCByIHx8ICJkZWZhdWx0Iik7CgkgICAgaWYgKCJvYmplY3QiICE9IF90eXBlb2YoaSkpIHJldHVybiBpOwoJICAgIHRocm93IG5ldyBUeXBlRXJyb3IoIkBAdG9QcmltaXRpdmUgbXVzdCByZXR1cm4gYSBwcmltaXRpdmUgdmFsdWUuIik7CgkgIH0KCSAgcmV0dXJuICgic3RyaW5nIiA9PT0gciA/IFN0cmluZyA6IE51bWJlcikodCk7Cgl9Cgltb2R1bGUuZXhwb3J0cyA9IHRvUHJpbWl0aXZlLCBtb2R1bGUuZXhwb3J0cy5fX2VzTW9kdWxlID0gdHJ1ZSwgbW9kdWxlLmV4cG9ydHNbImRlZmF1bHQiXSA9IG1vZHVsZS5leHBvcnRzOwoJfSk7CgoJdmFyIHRvUHJvcGVydHlLZXlfMSA9IGNyZWF0ZUNvbW1vbmpzTW9kdWxlKGZ1bmN0aW9uIChtb2R1bGUpIHsKCXZhciBfdHlwZW9mID0gX3R5cGVvZl8xWyJkZWZhdWx0Il07CgoJZnVuY3Rpb24gdG9Qcm9wZXJ0eUtleSh0KSB7CgkgIHZhciBpID0gdG9QcmltaXRpdmVfMSh0LCAic3RyaW5nIik7CgkgIHJldHVybiAic3ltYm9sIiA9PSBfdHlwZW9mKGkpID8gaSA6IFN0cmluZyhpKTsKCX0KCW1vZHVsZS5leHBvcnRzID0gdG9Qcm9wZXJ0eUtleSwgbW9kdWxlLmV4cG9ydHMuX19lc01vZHVsZSA9IHRydWUsIG1vZHVsZS5leHBvcnRzWyJkZWZhdWx0Il0gPSBtb2R1bGUuZXhwb3J0czsKCX0pOwoKCXZhciBkZWZpbmVQcm9wZXJ0eSA9IGNyZWF0ZUNvbW1vbmpzTW9kdWxlKGZ1bmN0aW9uIChtb2R1bGUpIHsKCWZ1bmN0aW9uIF9kZWZpbmVQcm9wZXJ0eShvYmosIGtleSwgdmFsdWUpIHsKCSAga2V5ID0gdG9Qcm9wZXJ0eUtleV8xKGtleSk7CgkgIGlmIChrZXkgaW4gb2JqKSB7CgkgICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KG9iaiwga2V5LCB7CgkgICAgICB2YWx1ZTogdmFsdWUsCgkgICAgICBlbnVtZXJhYmxlOiB0cnVlLAoJICAgICAgY29uZmlndXJhYmxlOiB0cnVlLAoJICAgICAgd3JpdGFibGU6IHRydWUKCSAgICB9KTsKCSAgfSBlbHNlIHsKCSAgICBvYmpba2V5XSA9IHZhbHVlOwoJICB9CgkgIHJldHVybiBvYmo7Cgl9Cgltb2R1bGUuZXhwb3J0cyA9IF9kZWZpbmVQcm9wZXJ0eSwgbW9kdWxlLmV4cG9ydHMuX19lc01vZHVsZSA9IHRydWUsIG1vZHVsZS5leHBvcnRzWyJkZWZhdWx0Il0gPSBtb2R1bGUuZXhwb3J0czsKCX0pOwoKCXZhciBfZGVmaW5lUHJvcGVydHkgPSAvKkBfX1BVUkVfXyovZ2V0RGVmYXVsdEV4cG9ydEZyb21DanMoZGVmaW5lUHJvcGVydHkpOwoKCXZhciBhcnJheVdpdGhIb2xlcyA9IGNyZWF0ZUNvbW1vbmpzTW9kdWxlKGZ1bmN0aW9uIChtb2R1bGUpIHsKCWZ1bmN0aW9uIF9hcnJheVdpdGhIb2xlcyhhcnIpIHsKCSAgaWYgKEFycmF5LmlzQXJyYXkoYXJyKSkgcmV0dXJuIGFycjsKCX0KCW1vZHVsZS5leHBvcnRzID0gX2FycmF5V2l0aEhvbGVzLCBtb2R1bGUuZXhwb3J0cy5fX2VzTW9kdWxlID0gdHJ1ZSwgbW9kdWxlLmV4cG9ydHNbImRlZmF1bHQiXSA9IG1vZHVsZS5leHBvcnRzOwoJfSk7CgoJdmFyIGl0ZXJhYmxlVG9BcnJheUxpbWl0ID0gY3JlYXRlQ29tbW9uanNNb2R1bGUoZnVuY3Rpb24gKG1vZHVsZSkgewoJZnVuY3Rpb24gX2l0ZXJhYmxlVG9BcnJheUxpbWl0KHIsIGwpIHsKCSAgdmFyIHQgPSBudWxsID09IHIgPyBudWxsIDogInVuZGVmaW5lZCIgIT0gdHlwZW9mIFN5bWJvbCAmJiByW1N5bWJvbC5pdGVyYXRvcl0gfHwgclsiQEBpdGVyYXRvciJdOwoJICBpZiAobnVsbCAhPSB0KSB7CgkgICAgdmFyIGUsCgkgICAgICBuLAoJICAgICAgaSwKCSAgICAgIHUsCgkgICAgICBhID0gW10sCgkgICAgICBmID0gITAsCgkgICAgICBvID0gITE7CgkgICAgdHJ5IHsKCSAgICAgIGlmIChpID0gKHQgPSB0LmNhbGwocikpLm5leHQsIDAgPT09IGwpIHsKCSAgICAgICAgaWYgKE9iamVjdCh0KSAhPT0gdCkgcmV0dXJuOwoJICAgICAgICBmID0gITE7CgkgICAgICB9IGVsc2UgZm9yICg7ICEoZiA9IChlID0gaS5jYWxsKHQpKS5kb25lKSAmJiAoYS5wdXNoKGUudmFsdWUpLCBhLmxlbmd0aCAhPT0gbCk7IGYgPSAhMCk7CgkgICAgfSBjYXRjaCAocikgewoJICAgICAgbyA9ICEwLCBuID0gcjsKCSAgICB9IGZpbmFsbHkgewoJICAgICAgdHJ5IHsKCSAgICAgICAgaWYgKCFmICYmIG51bGwgIT0gdFsicmV0dXJuIl0gJiYgKHUgPSB0WyJyZXR1cm4iXSgpLCBPYmplY3QodSkgIT09IHUpKSByZXR1cm47CgkgICAgICB9IGZpbmFsbHkgewoJICAgICAgICBpZiAobykgdGhyb3cgbjsKCSAgICAgIH0KCSAgICB9CgkgICAgcmV0dXJuIGE7CgkgIH0KCX0KCW1vZHVsZS5leHBvcnRzID0gX2l0ZXJhYmxlVG9BcnJheUxpbWl0LCBtb2R1bGUuZXhwb3J0cy5fX2VzTW9kdWxlID0gdHJ1ZSwgbW9kdWxlLmV4cG9ydHNbImRlZmF1bHQiXSA9IG1vZHVsZS5leHBvcnRzOwoJfSk7CgoJdmFyIGFycmF5TGlrZVRvQXJyYXkgPSBjcmVhdGVDb21tb25qc01vZHVsZShmdW5jdGlvbiAobW9kdWxlKSB7CglmdW5jdGlvbiBfYXJyYXlMaWtlVG9BcnJheShhcnIsIGxlbikgewoJICBpZiAobGVuID09IG51bGwgfHwgbGVuID4gYXJyLmxlbmd0aCkgbGVuID0gYXJyLmxlbmd0aDsKCSAgZm9yICh2YXIgaSA9IDAsIGFycjIgPSBuZXcgQXJyYXkobGVuKTsgaSA8IGxlbjsgaSsrKSBhcnIyW2ldID0gYXJyW2ldOwoJICByZXR1cm4gYXJyMjsKCX0KCW1vZHVsZS5leHBvcnRzID0gX2FycmF5TGlrZVRvQXJyYXksIG1vZHVsZS5leHBvcnRzLl9fZXNNb2R1bGUgPSB0cnVlLCBtb2R1bGUuZXhwb3J0c1siZGVmYXVsdCJdID0gbW9kdWxlLmV4cG9ydHM7Cgl9KTsKCgl2YXIgdW5zdXBwb3J0ZWRJdGVyYWJsZVRvQXJyYXkgPSBjcmVhdGVDb21tb25qc01vZHVsZShmdW5jdGlvbiAobW9kdWxlKSB7CglmdW5jdGlvbiBfdW5zdXBwb3J0ZWRJdGVyYWJsZVRvQXJyYXkobywgbWluTGVuKSB7CgkgIGlmICghbykgcmV0dXJuOwoJICBpZiAodHlwZW9mIG8gPT09ICJzdHJpbmciKSByZXR1cm4gYXJyYXlMaWtlVG9BcnJheShvLCBtaW5MZW4pOwoJICB2YXIgbiA9IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChvKS5zbGljZSg4LCAtMSk7CgkgIGlmIChuID09PSAiT2JqZWN0IiAmJiBvLmNvbnN0cnVjdG9yKSBuID0gby5jb25zdHJ1Y3Rvci5uYW1lOwoJICBpZiAobiA9PT0gIk1hcCIgfHwgbiA9PT0gIlNldCIpIHJldHVybiBBcnJheS5mcm9tKG8pOwoJICBpZiAobiA9PT0gIkFyZ3VtZW50cyIgfHwgL14oPzpVaXxJKW50KD86OHwxNnwzMikoPzpDbGFtcGVkKT9BcnJheSQvLnRlc3QobikpIHJldHVybiBhcnJheUxpa2VUb0FycmF5KG8sIG1pbkxlbik7Cgl9Cgltb2R1bGUuZXhwb3J0cyA9IF91bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheSwgbW9kdWxlLmV4cG9ydHMuX19lc01vZHVsZSA9IHRydWUsIG1vZHVsZS5leHBvcnRzWyJkZWZhdWx0Il0gPSBtb2R1bGUuZXhwb3J0czsKCX0pOwoKCXZhciBub25JdGVyYWJsZVJlc3QgPSBjcmVhdGVDb21tb25qc01vZHVsZShmdW5jdGlvbiAobW9kdWxlKSB7CglmdW5jdGlvbiBfbm9uSXRlcmFibGVSZXN0KCkgewoJICB0aHJvdyBuZXcgVHlwZUVycm9yKCJJbnZhbGlkIGF0dGVtcHQgdG8gZGVzdHJ1Y3R1cmUgbm9uLWl0ZXJhYmxlIGluc3RhbmNlLlxuSW4gb3JkZXIgdG8gYmUgaXRlcmFibGUsIG5vbi1hcnJheSBvYmplY3RzIG11c3QgaGF2ZSBhIFtTeW1ib2wuaXRlcmF0b3JdKCkgbWV0aG9kLiIpOwoJfQoJbW9kdWxlLmV4cG9ydHMgPSBfbm9uSXRlcmFibGVSZXN0LCBtb2R1bGUuZXhwb3J0cy5fX2VzTW9kdWxlID0gdHJ1ZSwgbW9kdWxlLmV4cG9ydHNbImRlZmF1bHQiXSA9IG1vZHVsZS5leHBvcnRzOwoJfSk7CgoJdmFyIHNsaWNlZFRvQXJyYXkgPSBjcmVhdGVDb21tb25qc01vZHVsZShmdW5jdGlvbiAobW9kdWxlKSB7CglmdW5jdGlvbiBfc2xpY2VkVG9BcnJheShhcnIsIGkpIHsKCSAgcmV0dXJuIGFycmF5V2l0aEhvbGVzKGFycikgfHwgaXRlcmFibGVUb0FycmF5TGltaXQoYXJyLCBpKSB8fCB1bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheShhcnIsIGkpIHx8IG5vbkl0ZXJhYmxlUmVzdCgpOwoJfQoJbW9kdWxlLmV4cG9ydHMgPSBfc2xpY2VkVG9BcnJheSwgbW9kdWxlLmV4cG9ydHMuX19lc01vZHVsZSA9IHRydWUsIG1vZHVsZS5leHBvcnRzWyJkZWZhdWx0Il0gPSBtb2R1bGUuZXhwb3J0czsKCX0pOwoKCXZhciBfc2xpY2VkVG9BcnJheSA9IC8qQF9fUFVSRV9fKi9nZXREZWZhdWx0RXhwb3J0RnJvbUNqcyhzbGljZWRUb0FycmF5KTsKCgl2YXIgQ29tbWFuZDsKCShmdW5jdGlvbiAoQ29tbWFuZCkgewoJICBDb21tYW5kW0NvbW1hbmRbIkluZm8iXSA9IDBdID0gIkluZm8iOwoJICBDb21tYW5kW0NvbW1hbmRbIkNvbnZlcnQiXSA9IDFdID0gIkNvbnZlcnQiOwoJICBDb21tYW5kW0NvbW1hbmRbIkxheW91dCJdID0gMl0gPSAiTGF5b3V0IjsKCSAgQ29tbWFuZFtDb21tYW5kWyJDbGVhbiJdID0gM10gPSAiQ2xlYW4iOwoJICBDb21tYW5kW0NvbW1hbmRbIkFyb21hdGl6ZSJdID0gNF0gPSAiQXJvbWF0aXplIjsKCSAgQ29tbWFuZFtDb21tYW5kWyJEZWFyb21hdGl6ZSJdID0gNV0gPSAiRGVhcm9tYXRpemUiOwoJICBDb21tYW5kW0NvbW1hbmRbIkNhbGN1bGF0ZUNpcCJdID0gNl0gPSAiQ2FsY3VsYXRlQ2lwIjsKCSAgQ29tbWFuZFtDb21tYW5kWyJBdXRvbWFwIl0gPSA3XSA9ICJBdXRvbWFwIjsKCSAgQ29tbWFuZFtDb21tYW5kWyJDaGVjayJdID0gOF0gPSAiQ2hlY2siOwoJICBDb21tYW5kW0NvbW1hbmRbIkNhbGN1bGF0ZSJdID0gOV0gPSAiQ2FsY3VsYXRlIjsKCSAgQ29tbWFuZFtDb21tYW5kWyJHZW5lcmF0ZUltYWdlQXNCYXNlNjQiXSA9IDEwXSA9ICJHZW5lcmF0ZUltYWdlQXNCYXNlNjQiOwoJICBDb21tYW5kW0NvbW1hbmRbIkdldEluQ2hJS2V5Il0gPSAxMV0gPSAiR2V0SW5DaElLZXkiOwoJICBDb21tYW5kW0NvbW1hbmRbIkV4cGxpY2l0SHlkcm9nZW5zIl0gPSAxMl0gPSAiRXhwbGljaXRIeWRyb2dlbnMiOwoJfSkoQ29tbWFuZCB8fCAoQ29tbWFuZCA9IHt9KSk7Cgl2YXIgV29ya2VyRXZlbnQ7CgkoZnVuY3Rpb24gKFdvcmtlckV2ZW50KSB7CgkgIFdvcmtlckV2ZW50WyJJbmZvIl0gPSAiaW5mbyI7CgkgIFdvcmtlckV2ZW50WyJDb252ZXJ0Il0gPSAiY29udmVydCI7CgkgIFdvcmtlckV2ZW50WyJMYXlvdXQiXSA9ICJsYXlvdXQiOwoJICBXb3JrZXJFdmVudFsiQ2xlYW4iXSA9ICJjbGVhbiI7CgkgIFdvcmtlckV2ZW50WyJBcm9tYXRpemUiXSA9ICJhcm9tYXRpemUiOwoJICBXb3JrZXJFdmVudFsiRGVhcm9tYXRpemUiXSA9ICJkZWFyb21hdGl6ZSI7CgkgIFdvcmtlckV2ZW50WyJDYWxjdWxhdGVDaXAiXSA9ICJjYWxjdWxhdGVDaXAiOwoJICBXb3JrZXJFdmVudFsiQXV0b21hcCJdID0gImF1dG9tYXAiOwoJICBXb3JrZXJFdmVudFsiQ2hlY2siXSA9ICJjaGVjayI7CgkgIFdvcmtlckV2ZW50WyJDYWxjdWxhdGUiXSA9ICJjYWxjdWxhdGUiOwoJICBXb3JrZXJFdmVudFsiR2VuZXJhdGVJbWFnZUFzQmFzZTY0Il0gPSAiZ2VuZXJhdGVJbWFnZUFzQmFzZTY0IjsKCSAgV29ya2VyRXZlbnRbIkdldEluQ2hJS2V5Il0gPSAiZ2V0SW5DaElLZXkiOwoJICBXb3JrZXJFdmVudFsiRXhwbGljaXRIeWRyb2dlbnMiXSA9ICJjb252ZXJ0X2V4cGxpY2l0X2h5ZHJvZ2VucyI7Cgl9KShXb3JrZXJFdmVudCB8fCAoV29ya2VyRXZlbnQgPSB7fSkpOwoJdmFyIFN1cHBvcnRlZEZvcm1hdDsKCShmdW5jdGlvbiAoU3VwcG9ydGVkRm9ybWF0KSB7CgkgIFN1cHBvcnRlZEZvcm1hdFsiUnhuIl0gPSAicnhuZmlsZSI7CgkgIFN1cHBvcnRlZEZvcm1hdFsiTW9sIl0gPSAibW9sZmlsZSI7CgkgIFN1cHBvcnRlZEZvcm1hdFsiU21pbGVzIl0gPSAic21pbGVzIjsKCSAgU3VwcG9ydGVkRm9ybWF0WyJTbWFydHMiXSA9ICJzbWFydHMiOwoJICBTdXBwb3J0ZWRGb3JtYXRbIkNNTCJdID0gImNtbCI7CgkgIFN1cHBvcnRlZEZvcm1hdFsiSW5DaEkiXSA9ICJpbmNoaSI7CgkgIFN1cHBvcnRlZEZvcm1hdFsiSW5DaElBdXhJbmZvIl0gPSAiaW5jaGktYXV4IjsKCSAgU3VwcG9ydGVkRm9ybWF0WyJJbkNoSUtleSJdID0gImluY2hpLWtleSI7CgkgIFN1cHBvcnRlZEZvcm1hdFsiS2V0Il0gPSAia2V0IjsKCSAgU3VwcG9ydGVkRm9ybWF0WyJDRFgiXSA9ICJjZHgiOwoJICBTdXBwb3J0ZWRGb3JtYXRbIkNEWE1MIl0gPSAiY2R4bWwiOwoJICBTdXBwb3J0ZWRGb3JtYXRbIlNERiJdID0gInNkZiI7CgkgIFN1cHBvcnRlZEZvcm1hdFsiRkFTVEEiXSA9ICJmYXN0YSI7CgkgIFN1cHBvcnRlZEZvcm1hdFsiU0VRVUVOQ0UiXSA9ICJzZXF1ZW5jZSI7CgkgIFN1cHBvcnRlZEZvcm1hdFsiU0VRVUVOQ0VfM19MRVRURVIiXSA9ICJwZXB0aWRlLXNlcXVlbmNlLTMtbGV0dGVyIjsKCSAgU3VwcG9ydGVkRm9ybWF0WyJJRFQiXSA9ICJpZHQiOwoJICBTdXBwb3J0ZWRGb3JtYXRbIkhFTE0iXSA9ICJoZWxtIjsKCSAgU3VwcG9ydGVkRm9ybWF0WyJSREYiXSA9ICJyZGYiOwoJfSkoU3VwcG9ydGVkRm9ybWF0IHx8IChTdXBwb3J0ZWRGb3JtYXQgPSB7fSkpOwoKCXZhciBlbXB0eSA9IHt9OwoKCXZhciBlbXB0eSQxID0gLyojX19QVVJFX18qL09iamVjdC5mcmVlemUoewoJCV9fcHJvdG9fXzogbnVsbCwKCQknZGVmYXVsdCc6IGVtcHR5Cgl9KTsKCgkvLyBDb3B5cmlnaHQgSm95ZW50LCBJbmMuIGFuZCBvdGhlciBOb2RlIGNvbnRyaWJ1dG9ycy4KCS8vCgkvLyBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYQoJLy8gY29weSBvZiB0aGlzIHNvZnR3YXJlIGFuZCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gZmlsZXMgKHRoZQoJLy8gIlNvZnR3YXJlIiksIHRvIGRlYWwgaW4gdGhlIFNvZnR3YXJlIHdpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZwoJLy8gd2l0aG91dCBsaW1pdGF0aW9uIHRoZSByaWdodHMgdG8gdXNlLCBjb3B5LCBtb2RpZnksIG1lcmdlLCBwdWJsaXNoLAoJLy8gZGlzdHJpYnV0ZSwgc3VibGljZW5zZSwgYW5kL29yIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdAoJLy8gcGVyc29ucyB0byB3aG9tIHRoZSBTb2Z0d2FyZSBpcyBmdXJuaXNoZWQgdG8gZG8gc28sIHN1YmplY3QgdG8gdGhlCgkvLyBmb2xsb3dpbmcgY29uZGl0aW9uczoKCS8vCgkvLyBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZAoJLy8gaW4gYWxsIGNvcGllcyBvciBzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuCgkvLwoJLy8gVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEICJBUyBJUyIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1MKCS8vIE9SIElNUExJRUQsIElOQ0xVRElORyBCVVQgTk9UIExJTUlURUQgVE8gVEhFIFdBUlJBTlRJRVMgT0YKCS8vIE1FUkNIQU5UQUJJTElUWSwgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU4KCS8vIE5PIEVWRU5UIFNIQUxMIFRIRSBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLAoJLy8gREFNQUdFUyBPUiBPVEhFUiBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQU4gQUNUSU9OIE9GIENPTlRSQUNULCBUT1JUIE9SCgkvLyBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSwgT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFCgkvLyBVU0UgT1IgT1RIRVIgREVBTElOR1MgSU4gVEhFIFNPRlRXQVJFLgoKCS8vIHJlc29sdmVzIC4gYW5kIC4uIGVsZW1lbnRzIGluIGEgcGF0aCBhcnJheSB3aXRoIGRpcmVjdG9yeSBuYW1lcyB0aGVyZQoJLy8gbXVzdCBiZSBubyBzbGFzaGVzLCBlbXB0eSBlbGVtZW50cywgb3IgZGV2aWNlIG5hbWVzIChjOlwpIGluIHRoZSBhcnJheQoJLy8gKHNvIGFsc28gbm8gbGVhZGluZyBhbmQgdHJhaWxpbmcgc2xhc2hlcyAtIGl0IGRvZXMgbm90IGRpc3Rpbmd1aXNoCgkvLyByZWxhdGl2ZSBhbmQgYWJzb2x1dGUgcGF0aHMpCglmdW5jdGlvbiBub3JtYWxpemVBcnJheShwYXJ0cywgYWxsb3dBYm92ZVJvb3QpIHsKCSAgLy8gaWYgdGhlIHBhdGggdHJpZXMgdG8gZ28gYWJvdmUgdGhlIHJvb3QsIGB1cGAgZW5kcyB1cCA+IDAKCSAgdmFyIHVwID0gMDsKCSAgZm9yICh2YXIgaSA9IHBhcnRzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7CgkgICAgdmFyIGxhc3QgPSBwYXJ0c1tpXTsKCSAgICBpZiAobGFzdCA9PT0gJy4nKSB7CgkgICAgICBwYXJ0cy5zcGxpY2UoaSwgMSk7CgkgICAgfSBlbHNlIGlmIChsYXN0ID09PSAnLi4nKSB7CgkgICAgICBwYXJ0cy5zcGxpY2UoaSwgMSk7CgkgICAgICB1cCsrOwoJICAgIH0gZWxzZSBpZiAodXApIHsKCSAgICAgIHBhcnRzLnNwbGljZShpLCAxKTsKCSAgICAgIHVwLS07CgkgICAgfQoJICB9CgoJICAvLyBpZiB0aGUgcGF0aCBpcyBhbGxvd2VkIHRvIGdvIGFib3ZlIHRoZSByb290LCByZXN0b3JlIGxlYWRpbmcgLi5zCgkgIGlmIChhbGxvd0Fib3ZlUm9vdCkgewoJICAgIGZvciAoOyB1cC0tOyB1cCkgewoJICAgICAgcGFydHMudW5zaGlmdCgnLi4nKTsKCSAgICB9CgkgIH0KCgkgIHJldHVybiBwYXJ0czsKCX0KCgkvLyBTcGxpdCBhIGZpbGVuYW1lIGludG8gW3Jvb3QsIGRpciwgYmFzZW5hbWUsIGV4dF0sIHVuaXggdmVyc2lvbgoJLy8gJ3Jvb3QnIGlzIGp1c3QgYSBzbGFzaCwgb3Igbm90aGluZy4KCXZhciBzcGxpdFBhdGhSZSA9CgkgICAgL14oXC8/fCkoW1xzXFNdKj8pKCg/OlwuezEsMn18W15cL10rP3wpKFwuW14uXC9dKnwpKSg/OltcL10qKSQvOwoJdmFyIHNwbGl0UGF0aCA9IGZ1bmN0aW9uKGZpbGVuYW1lKSB7CgkgIHJldHVybiBzcGxpdFBhdGhSZS5leGVjKGZpbGVuYW1lKS5zbGljZSgxKTsKCX07CgoJLy8gcGF0aC5yZXNvbHZlKFtmcm9tIC4uLl0sIHRvKQoJLy8gcG9zaXggdmVyc2lvbgoJZnVuY3Rpb24gcmVzb2x2ZSgpIHsKCSAgdmFyIHJlc29sdmVkUGF0aCA9ICcnLAoJICAgICAgcmVzb2x2ZWRBYnNvbHV0ZSA9IGZhbHNlOwoKCSAgZm9yICh2YXIgaSA9IGFyZ3VtZW50cy5sZW5ndGggLSAxOyBpID49IC0xICYmICFyZXNvbHZlZEFic29sdXRlOyBpLS0pIHsKCSAgICB2YXIgcGF0aCA9IChpID49IDApID8gYXJndW1lbnRzW2ldIDogJy8nOwoKCSAgICAvLyBTa2lwIGVtcHR5IGFuZCBpbnZhbGlkIGVudHJpZXMKCSAgICBpZiAodHlwZW9mIHBhdGggIT09ICdzdHJpbmcnKSB7CgkgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdBcmd1bWVudHMgdG8gcGF0aC5yZXNvbHZlIG11c3QgYmUgc3RyaW5ncycpOwoJICAgIH0gZWxzZSBpZiAoIXBhdGgpIHsKCSAgICAgIGNvbnRpbnVlOwoJICAgIH0KCgkgICAgcmVzb2x2ZWRQYXRoID0gcGF0aCArICcvJyArIHJlc29sdmVkUGF0aDsKCSAgICByZXNvbHZlZEFic29sdXRlID0gcGF0aC5jaGFyQXQoMCkgPT09ICcvJzsKCSAgfQoKCSAgLy8gQXQgdGhpcyBwb2ludCB0aGUgcGF0aCBzaG91bGQgYmUgcmVzb2x2ZWQgdG8gYSBmdWxsIGFic29sdXRlIHBhdGgsIGJ1dAoJICAvLyBoYW5kbGUgcmVsYXRpdmUgcGF0aHMgdG8gYmUgc2FmZSAobWlnaHQgaGFwcGVuIHdoZW4gcHJvY2Vzcy5jd2QoKSBmYWlscykKCgkgIC8vIE5vcm1hbGl6ZSB0aGUgcGF0aAoJICByZXNvbHZlZFBhdGggPSBub3JtYWxpemVBcnJheShmaWx0ZXIocmVzb2x2ZWRQYXRoLnNwbGl0KCcvJyksIGZ1bmN0aW9uKHApIHsKCSAgICByZXR1cm4gISFwOwoJICB9KSwgIXJlc29sdmVkQWJzb2x1dGUpLmpvaW4oJy8nKTsKCgkgIHJldHVybiAoKHJlc29sdmVkQWJzb2x1dGUgPyAnLycgOiAnJykgKyByZXNvbHZlZFBhdGgpIHx8ICcuJzsKCX0KCS8vIHBhdGgubm9ybWFsaXplKHBhdGgpCgkvLyBwb3NpeCB2ZXJzaW9uCglmdW5jdGlvbiBub3JtYWxpemUocGF0aCkgewoJICB2YXIgaXNQYXRoQWJzb2x1dGUgPSBpc0Fic29sdXRlKHBhdGgpLAoJICAgICAgdHJhaWxpbmdTbGFzaCA9IHN1YnN0cihwYXRoLCAtMSkgPT09ICcvJzsKCgkgIC8vIE5vcm1hbGl6ZSB0aGUgcGF0aAoJICBwYXRoID0gbm9ybWFsaXplQXJyYXkoZmlsdGVyKHBhdGguc3BsaXQoJy8nKSwgZnVuY3Rpb24ocCkgewoJICAgIHJldHVybiAhIXA7CgkgIH0pLCAhaXNQYXRoQWJzb2x1dGUpLmpvaW4oJy8nKTsKCgkgIGlmICghcGF0aCAmJiAhaXNQYXRoQWJzb2x1dGUpIHsKCSAgICBwYXRoID0gJy4nOwoJICB9CgkgIGlmIChwYXRoICYmIHRyYWlsaW5nU2xhc2gpIHsKCSAgICBwYXRoICs9ICcvJzsKCSAgfQoKCSAgcmV0dXJuIChpc1BhdGhBYnNvbHV0ZSA/ICcvJyA6ICcnKSArIHBhdGg7Cgl9CgkvLyBwb3NpeCB2ZXJzaW9uCglmdW5jdGlvbiBpc0Fic29sdXRlKHBhdGgpIHsKCSAgcmV0dXJuIHBhdGguY2hhckF0KDApID09PSAnLyc7Cgl9CgoJLy8gcG9zaXggdmVyc2lvbgoJZnVuY3Rpb24gam9pbigpIHsKCSAgdmFyIHBhdGhzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzLCAwKTsKCSAgcmV0dXJuIG5vcm1hbGl6ZShmaWx0ZXIocGF0aHMsIGZ1bmN0aW9uKHAsIGluZGV4KSB7CgkgICAgaWYgKHR5cGVvZiBwICE9PSAnc3RyaW5nJykgewoJICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignQXJndW1lbnRzIHRvIHBhdGguam9pbiBtdXN0IGJlIHN0cmluZ3MnKTsKCSAgICB9CgkgICAgcmV0dXJuIHA7CgkgIH0pLmpvaW4oJy8nKSk7Cgl9CgoKCS8vIHBhdGgucmVsYXRpdmUoZnJvbSwgdG8pCgkvLyBwb3NpeCB2ZXJzaW9uCglmdW5jdGlvbiByZWxhdGl2ZShmcm9tLCB0bykgewoJICBmcm9tID0gcmVzb2x2ZShmcm9tKS5zdWJzdHIoMSk7CgkgIHRvID0gcmVzb2x2ZSh0bykuc3Vic3RyKDEpOwoKCSAgZnVuY3Rpb24gdHJpbShhcnIpIHsKCSAgICB2YXIgc3RhcnQgPSAwOwoJICAgIGZvciAoOyBzdGFydCA8IGFyci5sZW5ndGg7IHN0YXJ0KyspIHsKCSAgICAgIGlmIChhcnJbc3RhcnRdICE9PSAnJykgYnJlYWs7CgkgICAgfQoKCSAgICB2YXIgZW5kID0gYXJyLmxlbmd0aCAtIDE7CgkgICAgZm9yICg7IGVuZCA+PSAwOyBlbmQtLSkgewoJICAgICAgaWYgKGFycltlbmRdICE9PSAnJykgYnJlYWs7CgkgICAgfQoKCSAgICBpZiAoc3RhcnQgPiBlbmQpIHJldHVybiBbXTsKCSAgICByZXR1cm4gYXJyLnNsaWNlKHN0YXJ0LCBlbmQgLSBzdGFydCArIDEpOwoJICB9CgoJICB2YXIgZnJvbVBhcnRzID0gdHJpbShmcm9tLnNwbGl0KCcvJykpOwoJICB2YXIgdG9QYXJ0cyA9IHRyaW0odG8uc3BsaXQoJy8nKSk7CgoJICB2YXIgbGVuZ3RoID0gTWF0aC5taW4oZnJvbVBhcnRzLmxlbmd0aCwgdG9QYXJ0cy5sZW5ndGgpOwoJICB2YXIgc2FtZVBhcnRzTGVuZ3RoID0gbGVuZ3RoOwoJICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbmd0aDsgaSsrKSB7CgkgICAgaWYgKGZyb21QYXJ0c1tpXSAhPT0gdG9QYXJ0c1tpXSkgewoJICAgICAgc2FtZVBhcnRzTGVuZ3RoID0gaTsKCSAgICAgIGJyZWFrOwoJICAgIH0KCSAgfQoKCSAgdmFyIG91dHB1dFBhcnRzID0gW107CgkgIGZvciAodmFyIGkgPSBzYW1lUGFydHNMZW5ndGg7IGkgPCBmcm9tUGFydHMubGVuZ3RoOyBpKyspIHsKCSAgICBvdXRwdXRQYXJ0cy5wdXNoKCcuLicpOwoJICB9CgoJICBvdXRwdXRQYXJ0cyA9IG91dHB1dFBhcnRzLmNvbmNhdCh0b1BhcnRzLnNsaWNlKHNhbWVQYXJ0c0xlbmd0aCkpOwoKCSAgcmV0dXJuIG91dHB1dFBhcnRzLmpvaW4oJy8nKTsKCX0KCgl2YXIgc2VwID0gJy8nOwoJdmFyIGRlbGltaXRlciA9ICc6JzsKCglmdW5jdGlvbiBkaXJuYW1lKHBhdGgpIHsKCSAgdmFyIHJlc3VsdCA9IHNwbGl0UGF0aChwYXRoKSwKCSAgICAgIHJvb3QgPSByZXN1bHRbMF0sCgkgICAgICBkaXIgPSByZXN1bHRbMV07CgoJICBpZiAoIXJvb3QgJiYgIWRpcikgewoJICAgIC8vIE5vIGRpcm5hbWUgd2hhdHNvZXZlcgoJICAgIHJldHVybiAnLic7CgkgIH0KCgkgIGlmIChkaXIpIHsKCSAgICAvLyBJdCBoYXMgYSBkaXJuYW1lLCBzdHJpcCB0cmFpbGluZyBzbGFzaAoJICAgIGRpciA9IGRpci5zdWJzdHIoMCwgZGlyLmxlbmd0aCAtIDEpOwoJICB9CgoJICByZXR1cm4gcm9vdCArIGRpcjsKCX0KCglmdW5jdGlvbiBiYXNlbmFtZShwYXRoLCBleHQpIHsKCSAgdmFyIGYgPSBzcGxpdFBhdGgocGF0aClbMl07CgkgIC8vIFRPRE86IG1ha2UgdGhpcyBjb21wYXJpc29uIGNhc2UtaW5zZW5zaXRpdmUgb24gd2luZG93cz8KCSAgaWYgKGV4dCAmJiBmLnN1YnN0cigtMSAqIGV4dC5sZW5ndGgpID09PSBleHQpIHsKCSAgICBmID0gZi5zdWJzdHIoMCwgZi5sZW5ndGggLSBleHQubGVuZ3RoKTsKCSAgfQoJICByZXR1cm4gZjsKCX0KCgoJZnVuY3Rpb24gZXh0bmFtZShwYXRoKSB7CgkgIHJldHVybiBzcGxpdFBhdGgocGF0aClbM107Cgl9Cgl2YXIgcGF0aCA9IHsKCSAgZXh0bmFtZTogZXh0bmFtZSwKCSAgYmFzZW5hbWU6IGJhc2VuYW1lLAoJICBkaXJuYW1lOiBkaXJuYW1lLAoJICBzZXA6IHNlcCwKCSAgZGVsaW1pdGVyOiBkZWxpbWl0ZXIsCgkgIHJlbGF0aXZlOiByZWxhdGl2ZSwKCSAgam9pbjogam9pbiwKCSAgaXNBYnNvbHV0ZTogaXNBYnNvbHV0ZSwKCSAgbm9ybWFsaXplOiBub3JtYWxpemUsCgkgIHJlc29sdmU6IHJlc29sdmUKCX07CglmdW5jdGlvbiBmaWx0ZXIgKHhzLCBmKSB7CgkgICAgaWYgKHhzLmZpbHRlcikgcmV0dXJuIHhzLmZpbHRlcihmKTsKCSAgICB2YXIgcmVzID0gW107CgkgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB4cy5sZW5ndGg7IGkrKykgewoJICAgICAgICBpZiAoZih4c1tpXSwgaSwgeHMpKSByZXMucHVzaCh4c1tpXSk7CgkgICAgfQoJICAgIHJldHVybiByZXM7Cgl9CgoJLy8gU3RyaW5nLnByb3RvdHlwZS5zdWJzdHIgLSBuZWdhdGl2ZSBpbmRleCBkb24ndCB3b3JrIGluIElFOAoJdmFyIHN1YnN0ciA9ICdhYicuc3Vic3RyKC0xKSA9PT0gJ2InID8KCSAgICBmdW5jdGlvbiAoc3RyLCBzdGFydCwgbGVuKSB7IHJldHVybiBzdHIuc3Vic3RyKHN0YXJ0LCBsZW4pIH0gOgoJICAgIGZ1bmN0aW9uIChzdHIsIHN0YXJ0LCBsZW4pIHsKCSAgICAgICAgaWYgKHN0YXJ0IDwgMCkgc3RhcnQgPSBzdHIubGVuZ3RoICsgc3RhcnQ7CgkgICAgICAgIHJldHVybiBzdHIuc3Vic3RyKHN0YXJ0LCBsZW4pOwoJICAgIH0KCTsKCgl2YXIgcGF0aCQxID0gLyojX19QVVJFX18qL09iamVjdC5mcmVlemUoewoJCV9fcHJvdG9fXzogbnVsbCwKCQlyZXNvbHZlOiByZXNvbHZlLAoJCW5vcm1hbGl6ZTogbm9ybWFsaXplLAoJCWlzQWJzb2x1dGU6IGlzQWJzb2x1dGUsCgkJam9pbjogam9pbiwKCQlyZWxhdGl2ZTogcmVsYXRpdmUsCgkJc2VwOiBzZXAsCgkJZGVsaW1pdGVyOiBkZWxpbWl0ZXIsCgkJZGlybmFtZTogZGlybmFtZSwKCQliYXNlbmFtZTogYmFzZW5hbWUsCgkJZXh0bmFtZTogZXh0bmFtZSwKCQknZGVmYXVsdCc6IHBhdGgKCX0pOwoKCXZhciByZXF1aXJlJCQyID0gLypAX19QVVJFX18qL2dldEF1Z21lbnRlZE5hbWVzcGFjZShlbXB0eSQxKTsKCgl2YXIgcmVxdWlyZSQkMSA9IC8qQF9fUFVSRV9fKi9nZXRBdWdtZW50ZWROYW1lc3BhY2UocGF0aCQxKTsKCgl2YXIgaW5kaWdvS2V0Y2hlciA9IGNyZWF0ZUNvbW1vbmpzTW9kdWxlKGZ1bmN0aW9uIChtb2R1bGUsIGV4cG9ydHMpIHsKCXZhciBNb2R1bGUgPSAoKCkgPT4gewoJICB2YXIgX3NjcmlwdE5hbWUgPSB0eXBlb2YgZG9jdW1lbnQgIT0gJ3VuZGVmaW5lZCcgPyBkb2N1bWVudC5jdXJyZW50U2NyaXB0Py5zcmMgOiB1bmRlZmluZWQ7CgkgIGlmICh0eXBlb2YgX19maWxlbmFtZSAhPSAndW5kZWZpbmVkJykgX3NjcmlwdE5hbWUgfHw9IF9fZmlsZW5hbWU7CgkgIHJldHVybiAoCglmdW5jdGlvbihtb2R1bGVBcmcgPSB7fSkgewoJICB2YXIgbW9kdWxlUnRuOwoKCXZhciBNb2R1bGU9T2JqZWN0LmFzc2lnbih7fSxtb2R1bGVBcmcpO3ZhciByZWFkeVByb21pc2VSZXNvbHZlLHJlYWR5UHJvbWlzZVJlamVjdDt2YXIgcmVhZHlQcm9taXNlPW5ldyBQcm9taXNlKChyZXNvbHZlLHJlamVjdCk9PntyZWFkeVByb21pc2VSZXNvbHZlPXJlc29sdmU7cmVhZHlQcm9taXNlUmVqZWN0PXJlamVjdDt9KTtbImdldEV4Y2VwdGlvbk1lc3NhZ2UiLCJpbmNyZW1lbnRFeGNlcHRpb25SZWZjb3VudCIsImRlY3JlbWVudEV4Y2VwdGlvblJlZmNvdW50IiwiX21lbW9yeSIsIl9fX2luZGlyZWN0X2Z1bmN0aW9uX3RhYmxlIiwiX2pzVGhyb3ciLCJfcHJpbnRfanNuIiwiX21haW4iLCJvblJ1bnRpbWVJbml0aWFsaXplZCJdLmZvckVhY2gocHJvcD0+e2lmKCFPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHJlYWR5UHJvbWlzZSxwcm9wKSl7T2JqZWN0LmRlZmluZVByb3BlcnR5KHJlYWR5UHJvbWlzZSxwcm9wLHtnZXQ6KCk9PmFib3J0KCJZb3UgYXJlIGdldHRpbmcgIitwcm9wKyIgb24gdGhlIFByb21pc2Ugb2JqZWN0LCBpbnN0ZWFkIG9mIHRoZSBpbnN0YW5jZS4gVXNlIC50aGVuKCkgdG8gZ2V0IGNhbGxlZCBiYWNrIHdpdGggdGhlIGluc3RhbmNlLCBzZWUgdGhlIE1PRFVMQVJJWkUgZG9jcyBpbiBzcmMvc2V0dGluZ3MuanMiKSxzZXQ6KCk9PmFib3J0KCJZb3UgYXJlIHNldHRpbmcgIitwcm9wKyIgb24gdGhlIFByb21pc2Ugb2JqZWN0LCBpbnN0ZWFkIG9mIHRoZSBpbnN0YW5jZS4gVXNlIC50aGVuKCkgdG8gZ2V0IGNhbGxlZCBiYWNrIHdpdGggdGhlIGluc3RhbmNlLCBzZWUgdGhlIE1PRFVMQVJJWkUgZG9jcyBpbiBzcmMvc2V0dGluZ3MuanMiKX0pO319KTt2YXIgRU5WSVJPTk1FTlRfSVNfV0VCPXR5cGVvZiB3aW5kb3c9PSJvYmplY3QiO3ZhciBFTlZJUk9OTUVOVF9JU19XT1JLRVI9dHlwZW9mIGltcG9ydFNjcmlwdHM9PSJmdW5jdGlvbiI7dmFyIEVOVklST05NRU5UX0lTX05PREU9dHlwZW9mIHByb2Nlc3M9PSJvYmplY3QiJiZ0eXBlb2YgcHJvY2Vzcy52ZXJzaW9ucz09Im9iamVjdCImJnR5cGVvZiBwcm9jZXNzLnZlcnNpb25zLm5vZGU9PSJzdHJpbmciO3ZhciBFTlZJUk9OTUVOVF9JU19TSEVMTD0hRU5WSVJPTk1FTlRfSVNfV0VCJiYhRU5WSVJPTk1FTlRfSVNfTk9ERSYmIUVOVklST05NRU5UX0lTX1dPUktFUjtpZihNb2R1bGVbIkVOVklST05NRU5UIl0pe3Rocm93IG5ldyBFcnJvcigiTW9kdWxlLkVOVklST05NRU5UIGhhcyBiZWVuIGRlcHJlY2F0ZWQuIFRvIGZvcmNlIHRoZSBlbnZpcm9ubWVudCwgdXNlIHRoZSBFTlZJUk9OTUVOVCBjb21waWxlLXRpbWUgb3B0aW9uIChmb3IgZXhhbXBsZSwgLXNFTlZJUk9OTUVOVD13ZWIgb3IgLXNFTlZJUk9OTUVOVD1ub2RlKSIpfXZhciBtb2R1bGVPdmVycmlkZXM9T2JqZWN0LmFzc2lnbih7fSxNb2R1bGUpO3ZhciB0aGlzUHJvZ3JhbT0iLi90aGlzLnByb2dyYW0iO3ZhciBxdWl0Xz0oc3RhdHVzLHRvVGhyb3cpPT57dGhyb3cgdG9UaHJvd307dmFyIHNjcmlwdERpcmVjdG9yeT0iIjt2YXIgcmVhZF8scmVhZEFzeW5jLHJlYWRCaW5hcnk7aWYoRU5WSVJPTk1FTlRfSVNfTk9ERSl7aWYodHlwZW9mIHByb2Nlc3M9PSJ1bmRlZmluZWQifHwhcHJvY2Vzcy5yZWxlYXNlfHxwcm9jZXNzLnJlbGVhc2UubmFtZSE9PSJub2RlIil0aHJvdyBuZXcgRXJyb3IoIm5vdCBjb21waWxlZCBmb3IgdGhpcyBlbnZpcm9ubWVudCAoZGlkIHlvdSBidWlsZCB0byBIVE1MIGFuZCB0cnkgdG8gcnVuIGl0IG5vdCBvbiB0aGUgd2ViLCBvciBzZXQgRU5WSVJPTk1FTlQgdG8gc29tZXRoaW5nIC0gbGlrZSBub2RlIC0gYW5kIHJ1biBpdCBzb21lcGxhY2UgZWxzZSAtIGxpa2Ugb24gdGhlIHdlYj8pIik7dmFyIG5vZGVWZXJzaW9uPXByb2Nlc3MudmVyc2lvbnMubm9kZTt2YXIgbnVtZXJpY1ZlcnNpb249bm9kZVZlcnNpb24uc3BsaXQoIi4iKS5zbGljZSgwLDMpO251bWVyaWNWZXJzaW9uPW51bWVyaWNWZXJzaW9uWzBdKjFlNCtudW1lcmljVmVyc2lvblsxXSoxMDArbnVtZXJpY1ZlcnNpb25bMl0uc3BsaXQoIi0iKVswXSoxO2lmKG51bWVyaWNWZXJzaW9uPDE2ZTQpe3Rocm93IG5ldyBFcnJvcigiVGhpcyBlbXNjcmlwdGVuLWdlbmVyYXRlZCBjb2RlIHJlcXVpcmVzIG5vZGUgdjE2LjAuMCAoZGV0ZWN0ZWQgdiIrbm9kZVZlcnNpb24rIikiKX12YXIgZnM9cmVxdWlyZSQkMjt2YXIgbm9kZVBhdGg9cmVxdWlyZSQkMTtzY3JpcHREaXJlY3Rvcnk9X19kaXJuYW1lKyIvIjtyZWFkXz0oZmlsZW5hbWUsYmluYXJ5KT0+e2ZpbGVuYW1lPWlzRmlsZVVSSShmaWxlbmFtZSk/bmV3IFVSTChmaWxlbmFtZSk6bm9kZVBhdGgubm9ybWFsaXplKGZpbGVuYW1lKTtyZXR1cm4gZnMucmVhZEZpbGVTeW5jKGZpbGVuYW1lLGJpbmFyeT91bmRlZmluZWQ6InV0ZjgiKX07cmVhZEJpbmFyeT1maWxlbmFtZT0+e3ZhciByZXQ9cmVhZF8oZmlsZW5hbWUsdHJ1ZSk7aWYoIXJldC5idWZmZXIpe3JldD1uZXcgVWludDhBcnJheShyZXQpO31hc3NlcnQocmV0LmJ1ZmZlcik7cmV0dXJuIHJldH07cmVhZEFzeW5jPShmaWxlbmFtZSxvbmxvYWQsb25lcnJvcixiaW5hcnk9dHJ1ZSk9PntmaWxlbmFtZT1pc0ZpbGVVUkkoZmlsZW5hbWUpP25ldyBVUkwoZmlsZW5hbWUpOm5vZGVQYXRoLm5vcm1hbGl6ZShmaWxlbmFtZSk7ZnMucmVhZEZpbGUoZmlsZW5hbWUsYmluYXJ5P3VuZGVmaW5lZDoidXRmOCIsKGVycixkYXRhKT0+e2lmKGVycilvbmVycm9yKGVycik7ZWxzZSBvbmxvYWQoYmluYXJ5P2RhdGEuYnVmZmVyOmRhdGEpO30pO307aWYoIU1vZHVsZVsidGhpc1Byb2dyYW0iXSYmcHJvY2Vzcy5hcmd2Lmxlbmd0aD4xKXt0aGlzUHJvZ3JhbT1wcm9jZXNzLmFyZ3ZbMV0ucmVwbGFjZSgvXFwvZywiLyIpO31wcm9jZXNzLmFyZ3Yuc2xpY2UoMik7cXVpdF89KHN0YXR1cyx0b1Rocm93KT0+e3Byb2Nlc3MuZXhpdENvZGU9c3RhdHVzO3Rocm93IHRvVGhyb3d9O31lbHNlIGlmKEVOVklST05NRU5UX0lTX1NIRUxMKXtpZih0eXBlb2YgcHJvY2Vzcz09Im9iamVjdCImJnR5cGVvZiBjb21tb25qc1JlcXVpcmU9PT0iZnVuY3Rpb24ifHx0eXBlb2Ygd2luZG93PT0ib2JqZWN0Inx8dHlwZW9mIGltcG9ydFNjcmlwdHM9PSJmdW5jdGlvbiIpdGhyb3cgbmV3IEVycm9yKCJub3QgY29tcGlsZWQgZm9yIHRoaXMgZW52aXJvbm1lbnQgKGRpZCB5b3UgYnVpbGQgdG8gSFRNTCBhbmQgdHJ5IHRvIHJ1biBpdCBub3Qgb24gdGhlIHdlYiwgb3Igc2V0IEVOVklST05NRU5UIHRvIHNvbWV0aGluZyAtIGxpa2Ugbm9kZSAtIGFuZCBydW4gaXQgc29tZXBsYWNlIGVsc2UgLSBsaWtlIG9uIHRoZSB3ZWI/KSIpfWVsc2UgaWYoRU5WSVJPTk1FTlRfSVNfV0VCfHxFTlZJUk9OTUVOVF9JU19XT1JLRVIpe2lmKEVOVklST05NRU5UX0lTX1dPUktFUil7c2NyaXB0RGlyZWN0b3J5PXNlbGYubG9jYXRpb24uaHJlZjt9ZWxzZSBpZih0eXBlb2YgZG9jdW1lbnQhPSJ1bmRlZmluZWQiJiZkb2N1bWVudC5jdXJyZW50U2NyaXB0KXtzY3JpcHREaXJlY3Rvcnk9ZG9jdW1lbnQuY3VycmVudFNjcmlwdC5zcmM7fWlmKF9zY3JpcHROYW1lKXtzY3JpcHREaXJlY3Rvcnk9X3NjcmlwdE5hbWU7fWlmKHNjcmlwdERpcmVjdG9yeS5zdGFydHNXaXRoKCJibG9iOiIpKXtzY3JpcHREaXJlY3Rvcnk9IiI7fWVsc2Uge3NjcmlwdERpcmVjdG9yeT1zY3JpcHREaXJlY3Rvcnkuc3Vic3RyKDAsc2NyaXB0RGlyZWN0b3J5LnJlcGxhY2UoL1s/I10uKi8sIiIpLmxhc3RJbmRleE9mKCIvIikrMSk7fWlmKCEodHlwZW9mIHdpbmRvdz09Im9iamVjdCJ8fHR5cGVvZiBpbXBvcnRTY3JpcHRzPT0iZnVuY3Rpb24iKSl0aHJvdyBuZXcgRXJyb3IoIm5vdCBjb21waWxlZCBmb3IgdGhpcyBlbnZpcm9ubWVudCAoZGlkIHlvdSBidWlsZCB0byBIVE1MIGFuZCB0cnkgdG8gcnVuIGl0IG5vdCBvbiB0aGUgd2ViLCBvciBzZXQgRU5WSVJPTk1FTlQgdG8gc29tZXRoaW5nIC0gbGlrZSBub2RlIC0gYW5kIHJ1biBpdCBzb21lcGxhY2UgZWxzZSAtIGxpa2Ugb24gdGhlIHdlYj8pIik7e3JlYWRfPXVybD0+e3ZhciB4aHI9bmV3IFhNTEh0dHBSZXF1ZXN0O3hoci5vcGVuKCJHRVQiLHVybCxmYWxzZSk7eGhyLnNlbmQobnVsbCk7cmV0dXJuIHhoci5yZXNwb25zZVRleHR9O2lmKEVOVklST05NRU5UX0lTX1dPUktFUil7cmVhZEJpbmFyeT11cmw9Pnt2YXIgeGhyPW5ldyBYTUxIdHRwUmVxdWVzdDt4aHIub3BlbigiR0VUIix1cmwsZmFsc2UpO3hoci5yZXNwb25zZVR5cGU9ImFycmF5YnVmZmVyIjt4aHIuc2VuZChudWxsKTtyZXR1cm4gbmV3IFVpbnQ4QXJyYXkoeGhyLnJlc3BvbnNlKX07fXJlYWRBc3luYz0odXJsLG9ubG9hZCxvbmVycm9yKT0+e3ZhciB4aHI9bmV3IFhNTEh0dHBSZXF1ZXN0O3hoci5vcGVuKCJHRVQiLHVybCx0cnVlKTt4aHIucmVzcG9uc2VUeXBlPSJhcnJheWJ1ZmZlciI7eGhyLm9ubG9hZD0oKT0+e2lmKHhoci5zdGF0dXM9PTIwMHx8eGhyLnN0YXR1cz09MCYmeGhyLnJlc3BvbnNlKXtvbmxvYWQoeGhyLnJlc3BvbnNlKTtyZXR1cm59b25lcnJvcigpO307eGhyLm9uZXJyb3I9b25lcnJvcjt4aHIuc2VuZChudWxsKTt9O319ZWxzZSB7dGhyb3cgbmV3IEVycm9yKCJlbnZpcm9ubWVudCBkZXRlY3Rpb24gZXJyb3IiKX12YXIgb3V0PU1vZHVsZVsicHJpbnQiXXx8Y29uc29sZS5sb2cuYmluZChjb25zb2xlKTt2YXIgZXJyPU1vZHVsZVsicHJpbnRFcnIiXXx8Y29uc29sZS5lcnJvci5iaW5kKGNvbnNvbGUpO09iamVjdC5hc3NpZ24oTW9kdWxlLG1vZHVsZU92ZXJyaWRlcyk7bW9kdWxlT3ZlcnJpZGVzPW51bGw7Y2hlY2tJbmNvbWluZ01vZHVsZUFQSSgpO2lmKE1vZHVsZVsiYXJndW1lbnRzIl0pTW9kdWxlWyJhcmd1bWVudHMiXTtsZWdhY3lNb2R1bGVQcm9wKCJhcmd1bWVudHMiLCJhcmd1bWVudHNfIik7aWYoTW9kdWxlWyJ0aGlzUHJvZ3JhbSJdKXRoaXNQcm9ncmFtPU1vZHVsZVsidGhpc1Byb2dyYW0iXTtsZWdhY3lNb2R1bGVQcm9wKCJ0aGlzUHJvZ3JhbSIsInRoaXNQcm9ncmFtIik7aWYoTW9kdWxlWyJxdWl0Il0pcXVpdF89TW9kdWxlWyJxdWl0Il07bGVnYWN5TW9kdWxlUHJvcCgicXVpdCIsInF1aXRfIik7YXNzZXJ0KHR5cGVvZiBNb2R1bGVbIm1lbW9yeUluaXRpYWxpemVyUHJlZml4VVJMIl09PSJ1bmRlZmluZWQiLCJNb2R1bGUubWVtb3J5SW5pdGlhbGl6ZXJQcmVmaXhVUkwgb3B0aW9uIHdhcyByZW1vdmVkLCB1c2UgTW9kdWxlLmxvY2F0ZUZpbGUgaW5zdGVhZCIpO2Fzc2VydCh0eXBlb2YgTW9kdWxlWyJwdGhyZWFkTWFpblByZWZpeFVSTCJdPT0idW5kZWZpbmVkIiwiTW9kdWxlLnB0aHJlYWRNYWluUHJlZml4VVJMIG9wdGlvbiB3YXMgcmVtb3ZlZCwgdXNlIE1vZHVsZS5sb2NhdGVGaWxlIGluc3RlYWQiKTthc3NlcnQodHlwZW9mIE1vZHVsZVsiY2RJbml0aWFsaXplclByZWZpeFVSTCJdPT0idW5kZWZpbmVkIiwiTW9kdWxlLmNkSW5pdGlhbGl6ZXJQcmVmaXhVUkwgb3B0aW9uIHdhcyByZW1vdmVkLCB1c2UgTW9kdWxlLmxvY2F0ZUZpbGUgaW5zdGVhZCIpO2Fzc2VydCh0eXBlb2YgTW9kdWxlWyJmaWxlUGFja2FnZVByZWZpeFVSTCJdPT0idW5kZWZpbmVkIiwiTW9kdWxlLmZpbGVQYWNrYWdlUHJlZml4VVJMIG9wdGlvbiB3YXMgcmVtb3ZlZCwgdXNlIE1vZHVsZS5sb2NhdGVGaWxlIGluc3RlYWQiKTthc3NlcnQodHlwZW9mIE1vZHVsZVsicmVhZCJdPT0idW5kZWZpbmVkIiwiTW9kdWxlLnJlYWQgb3B0aW9uIHdhcyByZW1vdmVkIChtb2RpZnkgcmVhZF8gaW4gSlMpIik7YXNzZXJ0KHR5cGVvZiBNb2R1bGVbInJlYWRBc3luYyJdPT0idW5kZWZpbmVkIiwiTW9kdWxlLnJlYWRBc3luYyBvcHRpb24gd2FzIHJlbW92ZWQgKG1vZGlmeSByZWFkQXN5bmMgaW4gSlMpIik7YXNzZXJ0KHR5cGVvZiBNb2R1bGVbInJlYWRCaW5hcnkiXT09InVuZGVmaW5lZCIsIk1vZHVsZS5yZWFkQmluYXJ5IG9wdGlvbiB3YXMgcmVtb3ZlZCAobW9kaWZ5IHJlYWRCaW5hcnkgaW4gSlMpIik7YXNzZXJ0KHR5cGVvZiBNb2R1bGVbInNldFdpbmRvd1RpdGxlIl09PSJ1bmRlZmluZWQiLCJNb2R1bGUuc2V0V2luZG93VGl0bGUgb3B0aW9uIHdhcyByZW1vdmVkIChtb2RpZnkgZW1zY3JpcHRlbl9zZXRfd2luZG93X3RpdGxlIGluIEpTKSIpO2Fzc2VydCh0eXBlb2YgTW9kdWxlWyJUT1RBTF9NRU1PUlkiXT09InVuZGVmaW5lZCIsIk1vZHVsZS5UT1RBTF9NRU1PUlkgaGFzIGJlZW4gcmVuYW1lZCBNb2R1bGUuSU5JVElBTF9NRU1PUlkiKTtsZWdhY3lNb2R1bGVQcm9wKCJhc20iLCJ3YXNtRXhwb3J0cyIpO2xlZ2FjeU1vZHVsZVByb3AoInJlYWQiLCJyZWFkXyIpO2xlZ2FjeU1vZHVsZVByb3AoInJlYWRBc3luYyIsInJlYWRBc3luYyIpO2xlZ2FjeU1vZHVsZVByb3AoInJlYWRCaW5hcnkiLCJyZWFkQmluYXJ5Iik7bGVnYWN5TW9kdWxlUHJvcCgic2V0V2luZG93VGl0bGUiLCJzZXRXaW5kb3dUaXRsZSIpO2Fzc2VydCghRU5WSVJPTk1FTlRfSVNfU0hFTEwsInNoZWxsIGVudmlyb25tZW50IGRldGVjdGVkIGJ1dCBub3QgZW5hYmxlZCBhdCBidWlsZCB0aW1lLiAgQWRkIGBzaGVsbGAgdG8gYC1zRU5WSVJPTk1FTlRgIHRvIGVuYWJsZS4iKTt2YXIgd2FzbUJpbmFyeTtpZihNb2R1bGVbIndhc21CaW5hcnkiXSl3YXNtQmluYXJ5PU1vZHVsZVsid2FzbUJpbmFyeSJdO2xlZ2FjeU1vZHVsZVByb3AoIndhc21CaW5hcnkiLCJ3YXNtQmluYXJ5Iik7aWYodHlwZW9mIFdlYkFzc2VtYmx5IT0ib2JqZWN0Iil7ZXJyKCJubyBuYXRpdmUgd2FzbSBzdXBwb3J0IGRldGVjdGVkIik7fWZ1bmN0aW9uIGludEFycmF5RnJvbUJhc2U2NChzKXtpZih0eXBlb2YgRU5WSVJPTk1FTlRfSVNfTk9ERSE9InVuZGVmaW5lZCImJkVOVklST05NRU5UX0lTX05PREUpe3ZhciBidWY9QnVmZmVyLmZyb20ocywiYmFzZTY0Iik7cmV0dXJuIG5ldyBVaW50OEFycmF5KGJ1Zi5idWZmZXIsYnVmLmJ5dGVPZmZzZXQsYnVmLmxlbmd0aCl9dmFyIGRlY29kZWQ9YXRvYihzKTt2YXIgYnl0ZXM9bmV3IFVpbnQ4QXJyYXkoZGVjb2RlZC5sZW5ndGgpO2Zvcih2YXIgaT0wO2k8ZGVjb2RlZC5sZW5ndGg7KytpKXtieXRlc1tpXT1kZWNvZGVkLmNoYXJDb2RlQXQoaSk7fXJldHVybiBieXRlc31mdW5jdGlvbiB0cnlQYXJzZUFzRGF0YVVSSShmaWxlbmFtZSl7aWYoIWlzRGF0YVVSSShmaWxlbmFtZSkpe3JldHVybn1yZXR1cm4gaW50QXJyYXlGcm9tQmFzZTY0KGZpbGVuYW1lLnNsaWNlKGRhdGFVUklQcmVmaXgubGVuZ3RoKSl9dmFyIHdhc21NZW1vcnk7dmFyIEFCT1JUPWZhbHNlO3ZhciBFWElUU1RBVFVTO2Z1bmN0aW9uIGFzc2VydChjb25kaXRpb24sdGV4dCl7aWYoIWNvbmRpdGlvbil7YWJvcnQoIkFzc2VydGlvbiBmYWlsZWQiKyh0ZXh0PyI6ICIrdGV4dDoiIikpO319dmFyIEhFQVA4LEhFQVBVOCxIRUFQMTYsSEVBUFUxNixIRUFQMzIsSEVBUFUzMixIRUFQRjMyLEhFQVBGNjQ7ZnVuY3Rpb24gdXBkYXRlTWVtb3J5Vmlld3MoKXt2YXIgYj13YXNtTWVtb3J5LmJ1ZmZlcjtNb2R1bGVbIkhFQVA4Il09SEVBUDg9bmV3IEludDhBcnJheShiKTtNb2R1bGVbIkhFQVAxNiJdPUhFQVAxNj1uZXcgSW50MTZBcnJheShiKTtNb2R1bGVbIkhFQVBVOCJdPUhFQVBVOD1uZXcgVWludDhBcnJheShiKTtNb2R1bGVbIkhFQVBVMTYiXT1IRUFQVTE2PW5ldyBVaW50MTZBcnJheShiKTtNb2R1bGVbIkhFQVAzMiJdPUhFQVAzMj1uZXcgSW50MzJBcnJheShiKTtNb2R1bGVbIkhFQVBVMzIiXT1IRUFQVTMyPW5ldyBVaW50MzJBcnJheShiKTtNb2R1bGVbIkhFQVBGMzIiXT1IRUFQRjMyPW5ldyBGbG9hdDMyQXJyYXkoYik7TW9kdWxlWyJIRUFQRjY0Il09SEVBUEY2ND1uZXcgRmxvYXQ2NEFycmF5KGIpO31hc3NlcnQoIU1vZHVsZVsiU1RBQ0tfU0laRSJdLCJTVEFDS19TSVpFIGNhbiBubyBsb25nZXIgYmUgc2V0IGF0IHJ1bnRpbWUuICBVc2UgLXNTVEFDS19TSVpFIGF0IGxpbmsgdGltZSIpO2Fzc2