ketcher-standalone
Version:
Web-based molecule sketcher
582 lines (512 loc) • 9.56 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.
***************************************************************************/
import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties';
import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
import _asyncToGenerator from '@babel/runtime/helpers/asyncToGenerator';
import _classCallCheck from '@babel/runtime/helpers/classCallCheck';
import _createClass from '@babel/runtime/helpers/createClass';
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import _regeneratorRuntime from '@babel/runtime/regenerator';
import { pickStandardServerOptions, CoreEditor, getLabelRenderModeForIndigo, ChemicalMimeType } from 'ketcher-core';
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["CalculateMacromoleculeProperties"] = 13] = "CalculateMacromoleculeProperties";
})(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["CalculateMacromoleculeProperties"] = "calculateMacroProperties";
})(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["AXOLABS"] = "axo-labs";
SupportedFormat["HELM"] = "helm";
SupportedFormat["RDF"] = "rdf";
SupportedFormat["MonomerLibrary"] = "monomer-library";
})(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/IGQgOiB7CgkJCQllbnVtZXJhYmxlOiB0cnVlLAoJCQkJZ2V0OiBmdW5jdGlvbiAoKSB7CgkJCQkJcmV0dXJuIG5ba107CgkJCQl9CgkJCX0pOwoJCX0pOwoJCXJldHVybiBhOwoJfQoKCWZ1bmN0aW9uIGNvbW1vbmpzUmVxdWlyZSAoKSB7CgkJdGhyb3cgbmV3IEVycm9yKCdEeW5hbWljIHJlcXVpcmVzIGFyZSBub3QgY3VycmVudGx5IHN1cHBvcnRlZCBieSBAcm9sbHVwL3BsdWdpbi1jb21tb25qcycpOwoJfQoKCXZhciBfdHlwZW9mXzEgPSBjcmVhdGVDb21tb25qc01vZHVsZShmdW5jdGlvbiAobW9kdWxlKSB7CglmdW5jdGlvbiBfdHlwZW9mKG8pIHsKCSAgIkBiYWJlbC9oZWxwZXJzIC0gdHlwZW9mIjsKCgkgIHJldHVybiBtb2R1bGUuZXhwb3J0cyA9IF90eXBlb2YgPSAiZnVuY3Rpb24iID09IHR5cGVvZiBTeW1ib2wgJiYgInN5bWJvbCIgPT0gdHlwZW9mIFN5bWJvbC5pdGVyYXRvciA/IGZ1bmN0aW9uIChvKSB7CgkgICAgcmV0dXJuIHR5cGVvZiBvOwoJICB9IDogZnVuY3Rpb24gKG8pIHsKCSAgICByZXR1cm4gbyAmJiAiZnVuY3Rpb24iID09IHR5cGVvZiBTeW1ib2wgJiYgby5jb25zdHJ1Y3RvciA9PT0gU3ltYm9sICYmIG8gIT09IFN5bWJvbC5wcm90b3R5cGUgPyAic3ltYm9sIiA6IHR5cGVvZiBvOwoJICB9LCBtb2R1bGUuZXhwb3J0cy5fX2VzTW9kdWxlID0gdHJ1ZSwgbW9kdWxlLmV4cG9ydHNbImRlZmF1bHQiXSA9IG1vZHVsZS5leHBvcnRzLCBfdHlwZW9mKG8pOwoJfQoJbW9kdWxlLmV4cG9ydHMgPSBfdHlwZW9mLCBtb2R1bGUuZXhwb3J0cy5fX2VzTW9kdWxlID0gdHJ1ZSwgbW9kdWxlLmV4cG9ydHNbImRlZmF1bHQiXSA9IG1vZHVsZS5leHBvcnRzOwoJfSk7CgoJdmFyIHRvUHJpbWl0aXZlXzEgPSBjcmVhdGVDb21tb25qc01vZHVsZShmdW5jdGlvbiAobW9kdWxlKSB7Cgl2YXIgX3R5cGVvZiA9IF90eXBlb2ZfMVsiZGVmYXVsdCJdOwoJZnVuY3Rpb24gdG9QcmltaXRpdmUodCwgcikgewoJICBpZiAoIm9iamVjdCIgIT0gX3R5cGVvZih0KSB8fCAhdCkgcmV0dXJuIHQ7CgkgIHZhciBlID0gdFtTeW1ib2wudG9QcmltaXRpdmVdOwoJICBpZiAodm9pZCAwICE9PSBlKSB7CgkgICAgdmFyIGkgPSBlLmNhbGwodCwgciB8fCAiZGVmYXVsdCIpOwoJICAgIGlmICgib2JqZWN0IiAhPSBfdHlwZW9mKGkpKSByZXR1cm4gaTsKCSAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCJAQHRvUHJpbWl0aXZlIG11c3QgcmV0dXJuIGEgcHJpbWl0aXZlIHZhbHVlLiIpOwoJICB9CgkgIHJldHVybiAoInN0cmluZyIgPT09IHIgPyBTdHJpbmcgOiBOdW1iZXIpKHQpOwoJfQoJbW9kdWxlLmV4cG9ydHMgPSB0b1ByaW1pdGl2ZSwgbW9kdWxlLmV4cG9ydHMuX19lc01vZHVsZSA9IHRydWUsIG1vZHVsZS5leHBvcnRzWyJkZWZhdWx0Il0gPSBtb2R1bGUuZXhwb3J0czsKCX0pOwoKCXZhciB0b1Byb3BlcnR5S2V5XzEgPSBjcmVhdGVDb21tb25qc01vZHVsZShmdW5jdGlvbiAobW9kdWxlKSB7Cgl2YXIgX3R5cGVvZiA9IF90eXBlb2ZfMVsiZGVmYXVsdCJdOwoKCWZ1bmN0aW9uIHRvUHJvcGVydHlLZXkodCkgewoJICB2YXIgaSA9IHRvUHJpbWl0aXZlXzEodCwgInN0cmluZyIpOwoJICByZXR1cm4gInN5bWJvbCIgPT0gX3R5cGVvZihpKSA/IGkgOiBpICsgIiI7Cgl9Cgltb2R1bGUuZXhwb3J0cyA9IHRvUHJvcGVydHlLZXksIG1vZHVsZS5leHBvcnRzLl9fZXNNb2R1bGUgPSB0cnVlLCBtb2R1bGUuZXhwb3J0c1siZGVmYXVsdCJdID0gbW9kdWxlLmV4cG9ydHM7Cgl9KTsKCgl2YXIgZGVmaW5lUHJvcGVydHkgPSBjcmVhdGVDb21tb25qc01vZHVsZShmdW5jdGlvbiAobW9kdWxlKSB7CglmdW5jdGlvbiBfZGVmaW5lUHJvcGVydHkoZSwgciwgdCkgewoJICByZXR1cm4gKHIgPSB0b1Byb3BlcnR5S2V5XzEocikpIGluIGUgPyBPYmplY3QuZGVmaW5lUHJvcGVydHkoZSwgciwgewoJICAgIHZhbHVlOiB0LAoJICAgIGVudW1lcmFibGU6ICEwLAoJICAgIGNvbmZpZ3VyYWJsZTogITAsCgkgICAgd3JpdGFibGU6ICEwCgkgIH0pIDogZVtyXSA9IHQsIGU7Cgl9Cgltb2R1bGUuZXhwb3J0cyA9IF9kZWZpbmVQcm9wZXJ0eSwgbW9kdWxlLmV4cG9ydHMuX19lc01vZHVsZSA9IHRydWUsIG1vZHVsZS5leHBvcnRzWyJkZWZhdWx0Il0gPSBtb2R1bGUuZXhwb3J0czsKCX0pOwoKCXZhciBfZGVmaW5lUHJvcGVydHkgPSAvKkBfX1BVUkVfXyovZ2V0RGVmYXVsdEV4cG9ydEZyb21DanMoZGVmaW5lUHJvcGVydHkpOwoKCXZhciBhcnJheVdpdGhIb2xlcyA9IGNyZWF0ZUNvbW1vbmpzTW9kdWxlKGZ1bmN0aW9uIChtb2R1bGUpIHsKCWZ1bmN0aW9uIF9hcnJheVdpdGhIb2xlcyhyKSB7CgkgIGlmIChBcnJheS5pc0FycmF5KHIpKSByZXR1cm4gcjsKCX0KCW1vZHVsZS5leHBvcnRzID0gX2FycmF5V2l0aEhvbGVzLCBtb2R1bGUuZXhwb3J0cy5fX2VzTW9kdWxlID0gdHJ1ZSwgbW9kdWxlLmV4cG9ydHNbImRlZmF1bHQiXSA9IG1vZHVsZS5leHBvcnRzOwoJfSk7CgoJdmFyIGl0ZXJhYmxlVG9BcnJheUxpbWl0ID0gY3JlYXRlQ29tbW9uanNNb2R1bGUoZnVuY3Rpb24gKG1vZHVsZSkgewoJZnVuY3Rpb24gX2l0ZXJhYmxlVG9BcnJheUxpbWl0KHIsIGwpIHsKCSAgdmFyIHQgPSBudWxsID09IHIgPyBudWxsIDogInVuZGVmaW5lZCIgIT0gdHlwZW9mIFN5bWJvbCAmJiByW1N5bWJvbC5pdGVyYXRvcl0gfHwgclsiQEBpdGVyYXRvciJdOwoJICBpZiAobnVsbCAhPSB0KSB7CgkgICAgdmFyIGUsCgkgICAgICBuLAoJICAgICAgaSwKCSAgICAgIHUsCgkgICAgICBhID0gW10sCgkgICAgICBmID0gITAsCgkgICAgICBvID0gITE7CgkgICAgdHJ5IHsKCSAgICAgIGlmIChpID0gKHQgPSB0LmNhbGwocikpLm5leHQsIDAgPT09IGwpIHsKCSAgICAgICAgaWYgKE9iamVjdCh0KSAhPT0gdCkgcmV0dXJuOwoJICAgICAgICBmID0gITE7CgkgICAgICB9IGVsc2UgZm9yICg7ICEoZiA9IChlID0gaS5jYWxsKHQpKS5kb25lKSAmJiAoYS5wdXNoKGUudmFsdWUpLCBhLmxlbmd0aCAhPT0gbCk7IGYgPSAhMCk7CgkgICAgfSBjYXRjaCAocikgewoJICAgICAgbyA9ICEwLCBuID0gcjsKCSAgICB9IGZpbmFsbHkgewoJICAgICAgdHJ5IHsKCSAgICAgICAgaWYgKCFmICYmIG51bGwgIT0gdFsicmV0dXJuIl0gJiYgKHUgPSB0WyJyZXR1cm4iXSgpLCBPYmplY3QodSkgIT09IHUpKSByZXR1cm47CgkgICAgICB9IGZpbmFsbHkgewoJICAgICAgICBpZiAobykgdGhyb3cgbjsKCSAgICAgIH0KCSAgICB9CgkgICAgcmV0dXJuIGE7CgkgIH0KCX0KCW1vZHVsZS5leHBvcnRzID0gX2l0ZXJhYmxlVG9BcnJheUxpbWl0LCBtb2R1bGUuZXhwb3J0cy5fX2VzTW9kdWxlID0gdHJ1ZSwgbW9kdWxlLmV4cG9ydHNbImRlZmF1bHQiXSA9IG1vZHVsZS5leHBvcnRzOwoJfSk7CgoJdmFyIGFycmF5TGlrZVRvQXJyYXkgPSBjcmVhdGVDb21tb25qc01vZHVsZShmdW5jdGlvbiAobW9kdWxlKSB7CglmdW5jdGlvbiBfYXJyYXlMaWtlVG9BcnJheShyLCBhKSB7CgkgIChudWxsID09IGEgfHwgYSA+IHIubGVuZ3RoKSAmJiAoYSA9IHIubGVuZ3RoKTsKCSAgZm9yICh2YXIgZSA9IDAsIG4gPSBBcnJheShhKTsgZSA8IGE7IGUrKykgbltlXSA9IHJbZV07CgkgIHJldHVybiBuOwoJfQoJbW9kdWxlLmV4cG9ydHMgPSBfYXJyYXlMaWtlVG9BcnJheSwgbW9kdWxlLmV4cG9ydHMuX19lc01vZHVsZSA9IHRydWUsIG1vZHVsZS5leHBvcnRzWyJkZWZhdWx0Il0gPSBtb2R1bGUuZXhwb3J0czsKCX0pOwoKCXZhciB1bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheSA9IGNyZWF0ZUNvbW1vbmpzTW9kdWxlKGZ1bmN0aW9uIChtb2R1bGUpIHsKCWZ1bmN0aW9uIF91bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheShyLCBhKSB7CgkgIGlmIChyKSB7CgkgICAgaWYgKCJzdHJpbmciID09IHR5cGVvZiByKSByZXR1cm4gYXJyYXlMaWtlVG9BcnJheShyLCBhKTsKCSAgICB2YXIgdCA9IHt9LnRvU3RyaW5nLmNhbGwocikuc2xpY2UoOCwgLTEpOwoJICAgIHJldHVybiAiT2JqZWN0IiA9PT0gdCAmJiByLmNvbnN0cnVjdG9yICYmICh0ID0gci5jb25zdHJ1Y3Rvci5uYW1lKSwgIk1hcCIgPT09IHQgfHwgIlNldCIgPT09IHQgPyBBcnJheS5mcm9tKHIpIDogIkFyZ3VtZW50cyIgPT09IHQgfHwgL14oPzpVaXxJKW50KD86OHwxNnwzMikoPzpDbGFtcGVkKT9BcnJheSQvLnRlc3QodCkgPyBhcnJheUxpa2VUb0FycmF5KHIsIGEpIDogdm9pZCAwOwoJICB9Cgl9Cgltb2R1bGUuZXhwb3J0cyA9IF91bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheSwgbW9kdWxlLmV4cG9ydHMuX19lc01vZHVsZSA9IHRydWUsIG1vZHVsZS5leHBvcnRzWyJkZWZhdWx0Il0gPSBtb2R1bGUuZXhwb3J0czsKCX0pOwoKCXZhciBub25JdGVyYWJsZVJlc3QgPSBjcmVhdGVDb21tb25qc01vZHVsZShmdW5jdGlvbiAobW9kdWxlKSB7CglmdW5jdGlvbiBfbm9uSXRlcmFibGVSZXN0KCkgewoJICB0aHJvdyBuZXcgVHlwZUVycm9yKCJJbnZhbGlkIGF0dGVtcHQgdG8gZGVzdHJ1Y3R1cmUgbm9uLWl0ZXJhYmxlIGluc3RhbmNlLlxuSW4gb3JkZXIgdG8gYmUgaXRlcmFibGUsIG5vbi1hcnJheSBvYmplY3RzIG11c3QgaGF2ZSBhIFtTeW1ib2wuaXRlcmF0b3JdKCkgbWV0aG9kLiIpOwoJfQoJbW9kdWxlLmV4cG9ydHMgPSBfbm9uSXRlcmFibGVSZXN0LCBtb2R1bGUuZXhwb3J0cy5fX2VzTW9kdWxlID0gdHJ1ZSwgbW9kdWxlLmV4cG9ydHNbImRlZmF1bHQiXSA9IG1vZHVsZS5leHBvcnRzOwoJfSk7CgoJdmFyIHNsaWNlZFRvQXJyYXkgPSBjcmVhdGVDb21tb25qc01vZHVsZShmdW5jdGlvbiAobW9kdWxlKSB7CglmdW5jdGlvbiBfc2xpY2VkVG9BcnJheShyLCBlKSB7CgkgIHJldHVybiBhcnJheVdpdGhIb2xlcyhyKSB8fCBpdGVyYWJsZVRvQXJyYXlMaW1pdChyLCBlKSB8fCB1bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheShyLCBlKSB8fCBub25JdGVyYWJsZVJlc3QoKTsKCX0KCW1vZHVsZS5leHBvcnRzID0gX3NsaWNlZFRvQXJyYXksIG1vZHVsZS5leHBvcnRzLl9fZXNNb2R1bGUgPSB0cnVlLCBtb2R1bGUuZXhwb3J0c1siZGVmYXVsdCJdID0gbW9kdWxlLmV4cG9ydHM7Cgl9KTsKCgl2YXIgX3NsaWNlZFRvQXJyYXkgPSAvKkBfX1BVUkVfXyovZ2V0RGVmYXVsdEV4cG9ydEZyb21DanMoc2xpY2VkVG9BcnJheSk7CgoJdmFyIENvbW1hbmQ7CgkoZnVuY3Rpb24gKENvbW1hbmQpIHsKCSAgQ29tbWFuZFtDb21tYW5kWyJJbmZvIl0gPSAwXSA9ICJJbmZvIjsKCSAgQ29tbWFuZFtDb21tYW5kWyJDb252ZXJ0Il0gPSAxXSA9ICJDb252ZXJ0IjsKCSAgQ29tbWFuZFtDb21tYW5kWyJMYXlvdXQiXSA9IDJdID0gIkxheW91dCI7CgkgIENvbW1hbmRbQ29tbWFuZFsiQ2xlYW4iXSA9IDNdID0gIkNsZWFuIjsKCSAgQ29tbWFuZFtDb21tYW5kWyJBcm9tYXRpemUiXSA9IDRdID0gIkFyb21hdGl6ZSI7CgkgIENvbW1hbmRbQ29tbWFuZFsiRGVhcm9tYXRpemUiXSA9IDVdID0gIkRlYXJvbWF0aXplIjsKCSAgQ29tbWFuZFtDb21tYW5kWyJDYWxjdWxhdGVDaXAiXSA9IDZdID0gIkNhbGN1bGF0ZUNpcCI7CgkgIENvbW1hbmRbQ29tbWFuZFsiQXV0b21hcCJdID0gN10gPSAiQXV0b21hcCI7CgkgIENvbW1hbmRbQ29tbWFuZFsiQ2hlY2siXSA9IDhdID0gIkNoZWNrIjsKCSAgQ29tbWFuZFtDb21tYW5kWyJDYWxjdWxhdGUiXSA9IDldID0gIkNhbGN1bGF0ZSI7CgkgIENvbW1hbmRbQ29tbWFuZFsiR2VuZXJhdGVJbWFnZUFzQmFzZTY0Il0gPSAxMF0gPSAiR2VuZXJhdGVJbWFnZUFzQmFzZTY0IjsKCSAgQ29tbWFuZFtDb21tYW5kWyJHZXRJbkNoSUtleSJdID0gMTFdID0gIkdldEluQ2hJS2V5IjsKCSAgQ29tbWFuZFtDb21tYW5kWyJFeHBsaWNpdEh5ZHJvZ2VucyJdID0gMTJdID0gIkV4cGxpY2l0SHlkcm9nZW5zIjsKCSAgQ29tbWFuZFtDb21tYW5kWyJDYWxjdWxhdGVNYWNyb21vbGVjdWxlUHJvcGVydGllcyJdID0gMTNdID0gIkNhbGN1bGF0ZU1hY3JvbW9sZWN1bGVQcm9wZXJ0aWVzIjsKCX0pKENvbW1hbmQgfHwgKENvbW1hbmQgPSB7fSkpOwoJdmFyIFdvcmtlckV2ZW50OwoJKGZ1bmN0aW9uIChXb3JrZXJFdmVudCkgewoJICBXb3JrZXJFdmVudFsiSW5mbyJdID0gImluZm8iOwoJICBXb3JrZXJFdmVudFsiQ29udmVydCJdID0gImNvbnZlcnQiOwoJICBXb3JrZXJFdmVudFsiTGF5b3V0Il0gPSAibGF5b3V0IjsKCSAgV29ya2VyRXZlbnRbIkNsZWFuIl0gPSAiY2xlYW4iOwoJICBXb3JrZXJFdmVudFsiQXJvbWF0aXplIl0gPSAiYXJvbWF0aXplIjsKCSAgV29ya2VyRXZlbnRbIkRlYXJvbWF0aXplIl0gPSAiZGVhcm9tYXRpemUiOwoJICBXb3JrZXJFdmVudFsiQ2FsY3VsYXRlQ2lwIl0gPSAiY2FsY3VsYXRlQ2lwIjsKCSAgV29ya2VyRXZlbnRbIkF1dG9tYXAiXSA9ICJhdXRvbWFwIjsKCSAgV29ya2VyRXZlbnRbIkNoZWNrIl0gPSAiY2hlY2siOwoJICBXb3JrZXJFdmVudFsiQ2FsY3VsYXRlIl0gPSAiY2FsY3VsYXRlIjsKCSAgV29ya2VyRXZlbnRbIkdlbmVyYXRlSW1hZ2VBc0Jhc2U2NCJdID0gImdlbmVyYXRlSW1hZ2VBc0Jhc2U2NCI7CgkgIFdvcmtlckV2ZW50WyJHZXRJbkNoSUtleSJdID0gImdldEluQ2hJS2V5IjsKCSAgV29ya2VyRXZlbnRbIkV4cGxpY2l0SHlkcm9nZW5zIl0gPSAiY29udmVydF9leHBsaWNpdF9oeWRyb2dlbnMiOwoJICBXb3JrZXJFdmVudFsiQ2FsY3VsYXRlTWFjcm9tb2xlY3VsZVByb3BlcnRpZXMiXSA9ICJjYWxjdWxhdGVNYWNyb1Byb3BlcnRpZXMiOwoJfSkoV29ya2VyRXZlbnQgfHwgKFdvcmtlckV2ZW50ID0ge30pKTsKCXZhciBTdXBwb3J0ZWRGb3JtYXQ7CgkoZnVuY3Rpb24gKFN1cHBvcnRlZEZvcm1hdCkgewoJICBTdXBwb3J0ZWRGb3JtYXRbIlJ4biJdID0gInJ4bmZpbGUiOwoJICBTdXBwb3J0ZWRGb3JtYXRbIk1vbCJdID0gIm1vbGZpbGUiOwoJICBTdXBwb3J0ZWRGb3JtYXRbIlNtaWxlcyJdID0gInNtaWxlcyI7CgkgIFN1cHBvcnRlZEZvcm1hdFsiU21hcnRzIl0gPSAic21hcnRzIjsKCSAgU3VwcG9ydGVkRm9ybWF0WyJDTUwiXSA9ICJjbWwiOwoJICBTdXBwb3J0ZWRGb3JtYXRbIkluQ2hJIl0gPSAiaW5jaGkiOwoJICBTdXBwb3J0ZWRGb3JtYXRbIkluQ2hJQXV4SW5mbyJdID0gImluY2hpLWF1eCI7CgkgIFN1cHBvcnRlZEZvcm1hdFsiSW5DaElLZXkiXSA9ICJpbmNoaS1rZXkiOwoJICBTdXBwb3J0ZWRGb3JtYXRbIktldCJdID0gImtldCI7CgkgIFN1cHBvcnRlZEZvcm1hdFsiQ0RYIl0gPSAiY2R4IjsKCSAgU3VwcG9ydGVkRm9ybWF0WyJDRFhNTCJdID0gImNkeG1sIjsKCSAgU3VwcG9ydGVkRm9ybWF0WyJTREYiXSA9ICJzZGYiOwoJICBTdXBwb3J0ZWRGb3JtYXRbIkZBU1RBIl0gPSAiZmFzdGEiOwoJICBTdXBwb3J0ZWRGb3JtYXRbIlNFUVVFTkNFIl0gPSAic2VxdWVuY2UiOwoJICBTdXBwb3J0ZWRGb3JtYXRbIlNFUVVFTkNFXzNfTEVUVEVSIl0gPSAicGVwdGlkZS1zZXF1ZW5jZS0zLWxldHRlciI7CgkgIFN1cHBvcnRlZEZvcm1hdFsiSURUIl0gPSAiaWR0IjsKCSAgU3VwcG9ydGVkRm9ybWF0WyJBWE9MQUJTIl0gPSAiYXhvLWxhYnMiOwoJICBTdXBwb3J0ZWRGb3JtYXRbIkhFTE0iXSA9ICJoZWxtIjsKCSAgU3VwcG9ydGVkRm9ybWF0WyJSREYiXSA9ICJyZGYiOwoJICBTdXBwb3J0ZWRGb3JtYXRbIk1vbm9tZXJMaWJyYXJ5Il0gPSAibW9ub21lci1saWJyYXJ5IjsKCX0pKFN1cHBvcnRlZEZvcm1hdCB8fCAoU3VwcG9ydGVkRm9ybWF0ID0ge30pKTsKCgl2YXIgZW1wdHkgPSB7fTsKCgl2YXIgZW1wdHkkMSA9IC8qI19fUFVSRV9fKi9PYmplY3QuZnJlZXplKHsKCQlfX3Byb3RvX186IG51bGwsCgkJJ2RlZmF1bHQnOiBlbXB0eQoJfSk7CgoJLy8gQ29weXJpZ2h0IEpveWVudCwgSW5jLiBhbmQgb3RoZXIgTm9kZSBjb250cmlidXRvcnMuCgkvLwoJLy8gUGVybWlzc2lvbiBpcyBoZXJlYnkgZ3JhbnRlZCwgZnJlZSBvZiBjaGFyZ2UsIHRvIGFueSBwZXJzb24gb2J0YWluaW5nIGEKCS8vIGNvcHkgb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUKCS8vICJTb2Z0d2FyZSIpLCB0byBkZWFsIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcKCS8vIHdpdGhvdXQgbGltaXRhdGlvbiB0aGUgcmlnaHRzIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSwgcHVibGlzaCwKCS8vIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsIGNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXQKCS8vIHBlcnNvbnMgdG8gd2hvbSB0aGUgU29mdHdhcmUgaXMgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZQoJLy8gZm9sbG93aW5nIGNvbmRpdGlvbnM6CgkvLwoJLy8gVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2Ugc2hhbGwgYmUgaW5jbHVkZWQKCS8vIGluIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLgoJLy8KCS8vIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCAiQVMgSVMiLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBFWFBSRVNTCgkvLyBPUiBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GCgkvLyBNRVJDSEFOVEFCSUxJVFksIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQuIElOCgkvLyBOTyBFVkVOVCBTSEFMTCBUSEUgQVVUSE9SUyBPUiBDT1BZUklHSFQgSE9MREVSUyBCRSBMSUFCTEUgRk9SIEFOWSBDTEFJTSwKCS8vIERBTUFHRVMgT1IgT1RIRVIgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUgoJLy8gT1RIRVJXSVNFLCBBUklTSU5HIEZST00sIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFNPRlRXQVJFIE9SIFRIRQoJLy8gVVNFIE9SIE9USEVSIERFQUxJTkdTIElOIFRIRSBTT0ZUV0FSRS4KCgkvLyByZXNvbHZlcyAuIGFuZCAuLiBlbGVtZW50cyBpbiBhIHBhdGggYXJyYXkgd2l0aCBkaXJlY3RvcnkgbmFtZXMgdGhlcmUKCS8vIG11c3QgYmUgbm8gc2xhc2hlcywgZW1wdHkgZWxlbWVudHMsIG9yIGRldmljZSBuYW1lcyAoYzpcKSBpbiB0aGUgYXJyYXkKCS8vIChzbyBhbHNvIG5vIGxlYWRpbmcgYW5kIHRyYWlsaW5nIHNsYXNoZXMgLSBpdCBkb2VzIG5vdCBkaXN0aW5ndWlzaAoJLy8gcmVsYXRpdmUgYW5kIGFic29sdXRlIHBhdGhzKQoJZnVuY3Rpb24gbm9ybWFsaXplQXJyYXkocGFydHMsIGFsbG93QWJvdmVSb290KSB7CgkgIC8vIGlmIHRoZSBwYXRoIHRyaWVzIHRvIGdvIGFib3ZlIHRoZSByb290LCBgdXBgIGVuZHMgdXAgPiAwCgkgIHZhciB1cCA9IDA7CgkgIGZvciAodmFyIGkgPSBwYXJ0cy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkgewoJICAgIHZhciBsYXN0ID0gcGFydHNbaV07CgkgICAgaWYgKGxhc3QgPT09ICcuJykgewoJICAgICAgcGFydHMuc3BsaWNlKGksIDEpOwoJICAgIH0gZWxzZSBpZiAobGFzdCA9PT0gJy4uJykgewoJICAgICAgcGFydHMuc3BsaWNlKGksIDEpOwoJICAgICAgdXArKzsKCSAgICB9IGVsc2UgaWYgKHVwKSB7CgkgICAgICBwYXJ0cy5zcGxpY2UoaSwgMSk7CgkgICAgICB1cC0tOwoJICAgIH0KCSAgfQoKCSAgLy8gaWYgdGhlIHBhdGggaXMgYWxsb3dlZCB0byBnbyBhYm92ZSB0aGUgcm9vdCwgcmVzdG9yZSBsZWFkaW5nIC4ucwoJICBpZiAoYWxsb3dBYm92ZVJvb3QpIHsKCSAgICBmb3IgKDsgdXAtLTsgdXApIHsKCSAgICAgIHBhcnRzLnVuc2hpZnQoJy4uJyk7CgkgICAgfQoJICB9CgoJICByZXR1cm4gcGFydHM7Cgl9CgoJLy8gU3BsaXQgYSBmaWxlbmFtZSBpbnRvIFtyb290LCBkaXIsIGJhc2VuYW1lLCBleHRdLCB1bml4IHZlcnNpb24KCS8vICdyb290JyBpcyBqdXN0IGEgc2xhc2gsIG9yIG5vdGhpbmcuCgl2YXIgc3BsaXRQYXRoUmUgPQoJICAgIC9eKFwvP3wpKFtcc1xTXSo/KSgoPzpcLnsxLDJ9fFteXC9dKz98KShcLlteLlwvXSp8KSkoPzpbXC9dKikkLzsKCXZhciBzcGxpdFBhdGggPSBmdW5jdGlvbihmaWxlbmFtZSkgewoJICByZXR1cm4gc3BsaXRQYXRoUmUuZXhlYyhmaWxlbmFtZSkuc2xpY2UoMSk7Cgl9OwoKCS8vIHBhdGgucmVzb2x2ZShbZnJvbSAuLi5dLCB0bykKCS8vIHBvc2l4IHZlcnNpb24KCWZ1bmN0aW9uIHJlc29sdmUoKSB7CgkgIHZhciByZXNvbHZlZFBhdGggPSAnJywKCSAgICAgIHJlc29sdmVkQWJzb2x1dGUgPSBmYWxzZTsKCgkgIGZvciAodmFyIGkgPSBhcmd1bWVudHMubGVuZ3RoIC0gMTsgaSA+PSAtMSAmJiAhcmVzb2x2ZWRBYnNvbHV0ZTsgaS0tKSB7CgkgICAgdmFyIHBhdGggPSAoaSA+PSAwKSA/IGFyZ3VtZW50c1tpXSA6ICcvJzsKCgkgICAgLy8gU2tpcCBlbXB0eSBhbmQgaW52YWxpZCBlbnRyaWVzCgkgICAgaWYgKHR5cGVvZiBwYXRoICE9PSAnc3RyaW5nJykgewoJICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignQXJndW1lbnRzIHRvIHBhdGgucmVzb2x2ZSBtdXN0IGJlIHN0cmluZ3MnKTsKCSAgICB9IGVsc2UgaWYgKCFwYXRoKSB7CgkgICAgICBjb250aW51ZTsKCSAgICB9CgoJICAgIHJlc29sdmVkUGF0aCA9IHBhdGggKyAnLycgKyByZXNvbHZlZFBhdGg7CgkgICAgcmVzb2x2ZWRBYnNvbHV0ZSA9IHBhdGguY2hhckF0KDApID09PSAnLyc7CgkgIH0KCgkgIC8vIEF0IHRoaXMgcG9pbnQgdGhlIHBhdGggc2hvdWxkIGJlIHJlc29sdmVkIHRvIGEgZnVsbCBhYnNvbHV0ZSBwYXRoLCBidXQKCSAgLy8gaGFuZGxlIHJlbGF0aXZlIHBhdGhzIHRvIGJlIHNhZmUgKG1pZ2h0IGhhcHBlbiB3aGVuIHByb2Nlc3MuY3dkKCkgZmFpbHMpCgoJICAvLyBOb3JtYWxpemUgdGhlIHBhdGgKCSAgcmVzb2x2ZWRQYXRoID0gbm9ybWFsaXplQXJyYXkoZmlsdGVyKHJlc29sdmVkUGF0aC5zcGxpdCgnLycpLCBmdW5jdGlvbihwKSB7CgkgICAgcmV0dXJuICEhcDsKCSAgfSksICFyZXNvbHZlZEFic29sdXRlKS5qb2luKCcvJyk7CgoJICByZXR1cm4gKChyZXNvbHZlZEFic29sdXRlID8gJy8nIDogJycpICsgcmVzb2x2ZWRQYXRoKSB8fCAnLic7Cgl9CgkvLyBwYXRoLm5vcm1hbGl6ZShwYXRoKQoJLy8gcG9zaXggdmVyc2lvbgoJZnVuY3Rpb24gbm9ybWFsaXplKHBhdGgpIHsKCSAgdmFyIGlzUGF0aEFic29sdXRlID0gaXNBYnNvbHV0ZShwYXRoKSwKCSAgICAgIHRyYWlsaW5nU2xhc2ggPSBzdWJzdHIocGF0aCwgLTEpID09PSAnLyc7CgoJICAvLyBOb3JtYWxpemUgdGhlIHBhdGgKCSAgcGF0aCA9IG5vcm1hbGl6ZUFycmF5KGZpbHRlcihwYXRoLnNwbGl0KCcvJyksIGZ1bmN0aW9uKHApIHsKCSAgICByZXR1cm4gISFwOwoJICB9KSwgIWlzUGF0aEFic29sdXRlKS5qb2luKCcvJyk7CgoJICBpZiAoIXBhdGggJiYgIWlzUGF0aEFic29sdXRlKSB7CgkgICAgcGF0aCA9ICcuJzsKCSAgfQoJICBpZiAocGF0aCAmJiB0cmFpbGluZ1NsYXNoKSB7CgkgICAgcGF0aCArPSAnLyc7CgkgIH0KCgkgIHJldHVybiAoaXNQYXRoQWJzb2x1dGUgPyAnLycgOiAnJykgKyBwYXRoOwoJfQoJLy8gcG9zaXggdmVyc2lvbgoJZnVuY3Rpb24gaXNBYnNvbHV0ZShwYXRoKSB7CgkgIHJldHVybiBwYXRoLmNoYXJBdCgwKSA9PT0gJy8nOwoJfQoKCS8vIHBvc2l4IHZlcnNpb24KCWZ1bmN0aW9uIGpvaW4oKSB7CgkgIHZhciBwYXRocyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cywgMCk7CgkgIHJldHVybiBub3JtYWxpemUoZmlsdGVyKHBhdGhzLCBmdW5jdGlvbihwLCBpbmRleCkgewoJICAgIGlmICh0eXBlb2YgcCAhPT0gJ3N0cmluZycpIHsKCSAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0FyZ3VtZW50cyB0byBwYXRoLmpvaW4gbXVzdCBiZSBzdHJpbmdzJyk7CgkgICAgfQoJICAgIHJldHVybiBwOwoJICB9KS5qb2luKCcvJykpOwoJfQoKCgkvLyBwYXRoLnJlbGF0aXZlKGZyb20sIHRvKQoJLy8gcG9zaXggdmVyc2lvbgoJZnVuY3Rpb24gcmVsYXRpdmUoZnJvbSwgdG8pIHsKCSAgZnJvbSA9IHJlc29sdmUoZnJvbSkuc3Vic3RyKDEpOwoJICB0byA9IHJlc29sdmUodG8pLnN1YnN0cigxKTsKCgkgIGZ1bmN0aW9uIHRyaW0oYXJyKSB7CgkgICAgdmFyIHN0YXJ0ID0gMDsKCSAgICBmb3IgKDsgc3RhcnQgPCBhcnIubGVuZ3RoOyBzdGFydCsrKSB7CgkgICAgICBpZiAoYXJyW3N0YXJ0XSAhPT0gJycpIGJyZWFrOwoJICAgIH0KCgkgICAgdmFyIGVuZCA9IGFyci5sZW5ndGggLSAxOwoJICAgIGZvciAoOyBlbmQgPj0gMDsgZW5kLS0pIHsKCSAgICAgIGlmIChhcnJbZW5kXSAhPT0gJycpIGJyZWFrOwoJICAgIH0KCgkgICAgaWYgKHN0YXJ0ID4gZW5kKSByZXR1cm4gW107CgkgICAgcmV0dXJuIGFyci5zbGljZShzdGFydCwgZW5kIC0gc3RhcnQgKyAxKTsKCSAgfQoKCSAgdmFyIGZyb21QYXJ0cyA9IHRyaW0oZnJvbS5zcGxpdCgnLycpKTsKCSAgdmFyIHRvUGFydHMgPSB0cmltKHRvLnNwbGl0KCcvJykpOwoKCSAgdmFyIGxlbmd0aCA9IE1hdGgubWluKGZyb21QYXJ0cy5sZW5ndGgsIHRvUGFydHMubGVuZ3RoKTsKCSAgdmFyIHNhbWVQYXJ0c0xlbmd0aCA9IGxlbmd0aDsKCSAgZm9yICh2YXIgaSA9IDA7IGkgPCBsZW5ndGg7IGkrKykgewoJICAgIGlmIChmcm9tUGFydHNbaV0gIT09IHRvUGFydHNbaV0pIHsKCSAgICAgIHNhbWVQYXJ0c0xlbmd0aCA9IGk7CgkgICAgICBicmVhazsKCSAgICB9CgkgIH0KCgkgIHZhciBvdXRwdXRQYXJ0cyA9IFtdOwoJICBmb3IgKHZhciBpID0gc2FtZVBhcnRzTGVuZ3RoOyBpIDwgZnJvbVBhcnRzLmxlbmd0aDsgaSsrKSB7CgkgICAgb3V0cHV0UGFydHMucHVzaCgnLi4nKTsKCSAgfQoKCSAgb3V0cHV0UGFydHMgPSBvdXRwdXRQYXJ0cy5jb25jYXQodG9QYXJ0cy5zbGljZShzYW1lUGFydHNMZW5ndGgpKTsKCgkgIHJldHVybiBvdXRwdXRQYXJ0cy5qb2luKCcvJyk7Cgl9CgoJdmFyIHNlcCA9ICcvJzsKCXZhciBkZWxpbWl0ZXIgPSAnOic7CgoJZnVuY3Rpb24gZGlybmFtZShwYXRoKSB7CgkgIHZhciByZXN1bHQgPSBzcGxpdFBhdGgocGF0aCksCgkgICAgICByb290ID0gcmVzdWx0WzBdLAoJICAgICAgZGlyID0gcmVzdWx0WzFdOwoKCSAgaWYgKCFyb290ICYmICFkaXIpIHsKCSAgICAvLyBObyBkaXJuYW1lIHdoYXRzb2V2ZXIKCSAgICByZXR1cm4gJy4nOwoJICB9CgoJICBpZiAoZGlyKSB7CgkgICAgLy8gSXQgaGFzIGEgZGlybmFtZSwgc3RyaXAgdHJhaWxpbmcgc2xhc2gKCSAgICBkaXIgPSBkaXIuc3Vic3RyKDAsIGRpci5sZW5ndGggLSAxKTsKCSAgfQoKCSAgcmV0dXJuIHJvb3QgKyBkaXI7Cgl9CgoJZnVuY3Rpb24gYmFzZW5hbWUocGF0aCwgZXh0KSB7CgkgIHZhciBmID0gc3BsaXRQYXRoKHBhdGgpWzJdOwoJICAvLyBUT0RPOiBtYWtlIHRoaXMgY29tcGFyaXNvbiBjYXNlLWluc2Vuc2l0aXZlIG9uIHdpbmRvd3M/CgkgIGlmIChleHQgJiYgZi5zdWJzdHIoLTEgKiBleHQubGVuZ3RoKSA9PT0gZXh0KSB7CgkgICAgZiA9IGYuc3Vic3RyKDAsIGYubGVuZ3RoIC0gZXh0Lmxlbmd0aCk7CgkgIH0KCSAgcmV0dXJuIGY7Cgl9CgoKCWZ1bmN0aW9uIGV4dG5hbWUocGF0aCkgewoJICByZXR1cm4gc3BsaXRQYXRoKHBhdGgpWzNdOwoJfQoJdmFyIHBhdGggPSB7CgkgIGV4dG5hbWU6IGV4dG5hbWUsCgkgIGJhc2VuYW1lOiBiYXNlbmFtZSwKCSAgZGlybmFtZTogZGlybmFtZSwKCSAgc2VwOiBzZXAsCgkgIGRlbGltaXRlcjogZGVsaW1pdGVyLAoJICByZWxhdGl2ZTogcmVsYXRpdmUsCgkgIGpvaW46IGpvaW4sCgkgIGlzQWJzb2x1dGU6IGlzQWJzb2x1dGUsCgkgIG5vcm1hbGl6ZTogbm9ybWFsaXplLAoJICByZXNvbHZlOiByZXNvbHZlCgl9OwoJZnVuY3Rpb24gZmlsdGVyICh4cywgZikgewoJICAgIGlmICh4cy5maWx0ZXIpIHJldHVybiB4cy5maWx0ZXIoZik7CgkgICAgdmFyIHJlcyA9IFtdOwoJICAgIGZvciAodmFyIGkgPSAwOyBpIDwgeHMubGVuZ3RoOyBpKyspIHsKCSAgICAgICAgaWYgKGYoeHNbaV0sIGksIHhzKSkgcmVzLnB1c2goeHNbaV0pOwoJICAgIH0KCSAgICByZXR1cm4gcmVzOwoJfQoKCS8vIFN0cmluZy5wcm90b3R5cGUuc3Vic3RyIC0gbmVnYXRpdmUgaW5kZXggZG9uJ3Qgd29yayBpbiBJRTgKCXZhciBzdWJzdHIgPSAnYWInLnN1YnN0cigtMSkgPT09ICdiJyA/CgkgICAgZnVuY3Rpb24gKHN0ciwgc3RhcnQsIGxlbikgeyByZXR1cm4gc3RyLnN1YnN0cihzdGFydCwgbGVuKSB9IDoKCSAgICBmdW5jdGlvbiAoc3RyLCBzdGFydCwgbGVuKSB7CgkgICAgICAgIGlmIChzdGFydCA8IDApIHN0YXJ0ID0gc3RyLmxlbmd0aCArIHN0YXJ0OwoJICAgICAgICByZXR1cm4gc3RyLnN1YnN0cihzdGFydCwgbGVuKTsKCSAgICB9Cgk7CgoJdmFyIHBhdGgkMSA9IC8qI19fUFVSRV9fKi9PYmplY3QuZnJlZXplKHsKCQlfX3Byb3RvX186IG51bGwsCgkJcmVzb2x2ZTogcmVzb2x2ZSwKCQlub3JtYWxpemU6IG5vcm1hbGl6ZSwKCQlpc0Fic29sdXRlOiBpc0Fic29sdXRlLAoJCWpvaW46IGpvaW4sCgkJcmVsYXRpdmU6IHJlbGF0aXZlLAoJCXNlcDogc2VwLAoJCWRlbGltaXRlcjogZGVsaW1pdGVyLAoJCWRpcm5hbWU6IGRpcm5hbWUsCgkJYmFzZW5hbWU6IGJhc2VuYW1lLAoJCWV4dG5hbWU6IGV4dG5hbWUsCgkJJ2RlZmF1bHQnOiBwYXRoCgl9KTsKCgl2YXIgcmVxdWlyZSQkMiA9IC8qQF9fUFVSRV9fKi9nZXRBdWdtZW50ZWROYW1lc3BhY2UoZW1wdHkkMSk7CgoJdmFyIHJlcXVpcmUkJDEgPSAvKkBfX1BVUkVfXyovZ2V0QXVnbWVudGVkTmFtZXNwYWNlKHBhdGgkMSk7CgoJdmFyIGluZGlnb0tldGNoZXJOb3JlbmRlciA9IGNyZWF0ZUNvbW1vbmpzTW9kdWxlKGZ1bmN0aW9uIChtb2R1bGUsIGV4cG9ydHMpIHsKCXZhciBNb2R1bGUgPSAoKCkgPT4gewoJICB2YXIgX3NjcmlwdE5hbWUgPSB0eXBlb2YgZG9jdW1lbnQgIT0gJ3VuZGVmaW5lZCcgPyBkb2N1bWVudC5jdXJyZW50U2NyaXB0Py5zcmMgOiB1bmRlZmluZWQ7CgkgIGlmICh0eXBlb2YgX19maWxlbmFtZSAhPSAndW5kZWZpbmVkJykgX3NjcmlwdE5hbWUgfHw9IF9fZmlsZW5hbWU7CgkgIHJldHVybiAoCglmdW5jdGlvbihtb2R1bGVBcmcgPSB7fSkgewoJICB2YXIgbW9kdWxlUnRuOwoKCXZhciBNb2R1bGU9T2JqZWN0LmFzc2lnbih7fSxtb2R1bGVBcmcpO3ZhciByZWFkeVByb21pc2VSZXNvbHZlLHJlYWR5UHJvbWlzZVJlamVjdDt2YXIgcmVhZHlQcm9taXNlPW5ldyBQcm9taXNlKChyZXNvbHZlLHJlamVjdCk9PntyZWFkeVByb21pc2VSZXNvbHZlPXJlc29sdmU7cmVhZHlQcm9taXNlUmVqZWN0PXJlamVjdDt9KTtbImdldEV4Y2VwdGlvbk1lc3NhZ2UiLCJpbmNyZW1lbnRFeGNlcHRpb25SZWZjb3VudCIsImRlY3JlbWVudEV4Y2VwdGlvblJlZmNvdW50IiwiX21lbW9yeSIsIl9fX2luZGlyZWN0X2Z1bmN0aW9uX3RhYmxlIiwiX2pzVGhyb3ciLCJfcHJpbnRfanNuIiwib25SdW50aW1lSW5pdGlhbGl6ZWQiXS5mb3JFYWNoKHByb3A9PntpZighT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihyZWFkeVByb21pc2UscHJvcCkpe09iamVjdC5kZWZpbmVQcm9wZXJ0eShyZWFkeVByb21pc2UscHJvcCx7Z2V0OigpPT5hYm9ydCgiWW91IGFyZSBnZXR0aW5nICIrcHJvcCsiIG9uIHRoZSBQcm9taXNlIG9iamVjdCwgaW5zdGVhZCBvZiB0aGUgaW5zdGFuY2UuIFVzZSAudGhlbigpIHRvIGdldCBjYWxsZWQgYmFjayB3aXRoIHRoZSBpbnN0YW5jZSwgc2VlIHRoZSBNT0RVTEFSSVpFIGRvY3MgaW4gc3JjL3NldHRpbmdzLmpzIiksc2V0OigpPT5hYm9ydCgiWW91IGFyZSBzZXR0aW5nICIrcHJvcCsiIG9uIHRoZSBQcm9taXNlIG9iamVjdCwgaW5zdGVhZCBvZiB0aGUgaW5zdGFuY2UuIFVzZSAudGhlbigpIHRvIGdldCBjYWxsZWQgYmFjayB3aXRoIHRoZSBpbnN0YW5jZSwgc2VlIHRoZSBNT0RVTEFSSVpFIGRvY3MgaW4gc3JjL3NldHRpbmdzLmpzIil9KTt9fSk7dmFyIEVOVklST05NRU5UX0lTX1dFQj10eXBlb2Ygd2luZG93PT0ib2JqZWN0Ijt2YXIgRU5WSVJPTk1FTlRfSVNfV09SS0VSPXR5cGVvZiBpbXBvcnRTY3JpcHRzPT0iZnVuY3Rpb24iO3ZhciBFTlZJUk9OTUVOVF9JU19OT0RFPXR5cGVvZiBwcm9jZXNzPT0ib2JqZWN0IiYmdHlwZW9mIHByb2Nlc3MudmVyc2lvbnM9PSJvYmplY3QiJiZ0eXBlb2YgcHJvY2Vzcy52ZXJzaW9ucy5ub2RlPT0ic3RyaW5nIjt2YXIgRU5WSVJPTk1FTlRfSVNfU0hFTEw9IUVOVklST05NRU5UX0lTX1dFQiYmIUVOVklST05NRU5UX0lTX05PREUmJiFFTlZJUk9OTUVOVF9JU19XT1JLRVI7aWYoTW9kdWxlWyJFTlZJUk9OTUVOVCJdKXt0aHJvdyBuZXcgRXJyb3IoIk1vZHVsZS5FTlZJUk9OTUVOVCBoYXMgYmVlbiBkZXByZWNhdGVkLiBUbyBmb3JjZSB0aGUgZW52aXJvbm1lbnQsIHVzZSB0aGUgRU5WSVJPTk1FTlQgY29tcGlsZS10aW1lIG9wdGlvbiAoZm9yIGV4YW1wbGUsIC1zRU5WSVJPTk1FTlQ9d2ViIG9yIC1zRU5WSVJPTk1FTlQ9bm9kZSkiKX12YXIgbW9kdWxlT3ZlcnJpZGVzPU9iamVjdC5hc3NpZ24oe30sTW9kdWxlKTt2YXIgdGhpc1Byb2dyYW09Ii4vdGhpcy5wcm9ncmFtIjt2YXIgc2NyaXB0RGlyZWN0b3J5PSIiO3ZhciByZWFkXyxyZWFkQXN5bmMscmVhZEJpbmFyeTtpZihFTlZJUk9OTUVOVF9JU19OT0RFKXtpZih0eXBlb2YgcHJvY2Vzcz09InVuZGVmaW5lZCJ8fCFwcm9jZXNzLnJlbGVhc2V8fHByb2Nlc3MucmVsZWFzZS5uYW1lIT09Im5vZGUiKXRocm93IG5ldyBFcnJvcigibm90IGNvbXBpbGVkIGZvciB0aGlzIGVudmlyb25tZW50IChkaWQgeW91IGJ1aWxkIHRvIEhUTUwgYW5kIHRyeSB0byBydW4gaXQgbm90IG9uIHRoZSB3ZWIsIG9yIHNldCBFTlZJUk9OTUVOVCB0byBzb21ldGhpbmcgLSBsaWtlIG5vZGUgLSBhbmQgcnVuIGl0IHNvbWVwbGFjZSBlbHNlIC0gbGlrZSBvbiB0aGUgd2ViPykiKTt2YXIgbm9kZVZlcnNpb249cHJvY2Vzcy52ZXJzaW9ucy5ub2RlO3ZhciBudW1lcmljVmVyc2lvbj1ub2RlVmVyc2lvbi5zcGxpdCgiLiIpLnNsaWNlKDAsMyk7bnVtZXJpY1ZlcnNpb249bnVtZXJpY1ZlcnNpb25bMF0qMWU0K251bWVyaWNWZXJzaW9uWzFdKjEwMCtudW1lcmljVmVyc2lvblsyXS5zcGxpdCgiLSIpWzBdKjE7aWYobnVtZXJpY1ZlcnNpb248MTZlNCl7dGhyb3cgbmV3IEVycm9yKCJUaGlzIGVtc2NyaXB0ZW4tZ2VuZXJhdGVkIGNvZGUgcmVxdWlyZXMgbm9kZSB2MTYuMC4wIChkZXRlY3RlZCB2Iitub2RlVmVyc2lvbisiKSIpfXZhciBmcz1yZXF1aXJlJCQyO3ZhciBub2RlUGF0aD1yZXF1aXJlJCQxO3NjcmlwdERpcmVjdG9yeT1fX2Rpcm5hbWUrIi8iO3JlYWRfPShmaWxlbmFtZSxiaW5hcnkpPT57ZmlsZW5hbWU9aXNGaWxlVVJJKGZpbGVuYW1lKT9uZXcgVVJMKGZpbGVuYW1lKTpub2RlUGF0aC5ub3JtYWxpemUoZmlsZW5hbWUpO3JldHVybiBmcy5yZWFkRmlsZVN5bmMoZmlsZW5hbWUsYmluYXJ5P3VuZGVmaW5lZDoidXRmOCIpfTtyZWFkQmluYXJ5PWZpbGVuYW1lPT57dmFyIHJldD1yZWFkXyhmaWxlbmFtZSx0cnVlKTtpZighcmV0LmJ1ZmZlcil7cmV0PW5ldyBVaW50OEFycmF5KHJldCk7fWFzc2VydChyZXQuYnVmZmVyKTtyZXR1cm4gcmV0fTtyZWFkQXN5bmM9KGZpbGVuYW1lLG9ubG9hZCxvbmVycm9yLGJpbmFyeT10cnVlKT0+e2ZpbGVuYW1lPWlzRmlsZVVSSShmaWxlbmFtZSk/bmV3IFVSTChmaWxlbmFtZSk6bm9kZVBhdGgubm9ybWFsaXplKGZpbGVuYW1lKTtmcy5yZWFkRmlsZShmaWxlbmFtZSxiaW5hcnk/dW5kZWZpbmVkOiJ1dGY4IiwoZXJyLGRhdGEpPT57aWYoZXJyKW9uZXJyb3IoZXJyKTtlbHNlIG9ubG9hZChiaW5hcnk/ZGF0YS5idWZmZXI6ZGF0YSk7fSk7fTtpZighTW9kdWxlWyJ0aGlzUHJvZ3JhbSJdJiZwcm9jZXNzLmFyZ3YubGVuZ3RoPjEpe3RoaXNQcm9ncmFtPXByb2Nlc3MuYXJndlsxXS5yZXBsYWNlKC9cXC9nLCIvIik7fXByb2Nlc3MuYXJndi5zbGljZSgyKTt9ZWxzZSBpZihFTlZJUk9OTUVOVF9JU19TSEVMTCl7aWYodHlwZW9mIHByb2Nlc3M9PSJvYmplY3QiJiZ0eXBlb2YgY29tbW9uanNSZXF1aXJlPT09ImZ1bmN0aW9uInx8dHlwZW9mIHdpbmRvdz09Im9iamVjdCJ8fHR5cGVvZiBpbXBvcnRTY3JpcHRzPT0iZnVuY3Rpb24iKXRocm93IG5ldyBFcnJvcigibm90IGNvbXBpbGVkIGZvciB0aGlzIGVudmlyb25tZW50IChkaWQgeW91IGJ1aWxkIHRvIEhUTUwgYW5kIHRyeSB0byBydW4gaXQgbm90IG9uIHRoZSB3ZWIsIG9yIHNldCBFTlZJUk9OTUVOVCB0byBzb21ldGhpbmcgLSBsaWtlIG5vZGUgLSBhbmQgcnVuIGl0IHNvbWVwbGFjZSBlbHNlIC0gbGlrZSBvbiB0aGUgd2ViPykiKX1lbHNlIGlmKEVOVklST05NRU5UX0lTX1dFQnx8RU5WSVJPTk1FTlRfSVNfV09SS0VSKXtpZihFTlZJUk9OTUVOVF9JU19XT1JLRVIpe3NjcmlwdERpcmVjdG9yeT1zZWxmLmxvY2F0aW9uLmhyZWY7fWVsc2UgaWYodHlwZW9mIGRvY3VtZW50IT0idW5kZWZpbmVkIiYmZG9jdW1lbnQuY3VycmVudFNjcmlwdCl7c2NyaXB0RGlyZWN0b3J5PWRvY3VtZW50LmN1cnJlbnRTY3JpcHQuc3JjO31pZihfc2NyaXB0TmFtZSl7c2NyaXB0RGlyZWN0b3J5PV9zY3JpcHROYW1lO31pZihzY3JpcHREaXJlY3Rvcnkuc3RhcnRzV2l0aCgiYmxvYjoiKSl7c2NyaXB0RGlyZWN0b3J5PSIiO31lbHNlIHtzY3JpcHREaXJlY3Rvcnk9c2NyaXB0RGlyZWN0b3J5LnN1YnN0cigwLHNjcmlwdERpcmVjdG9yeS5yZXBsYWNlKC9bPyNdLiovLCIiKS5sYXN0SW5kZXhPZigiLyIpKzEpO31pZighKHR5cGVvZiB3aW5kb3c9PSJvYmplY3QifHx0eXBlb2YgaW1wb3J0U2NyaXB0cz09ImZ1bmN0aW9uIikpdGhyb3cgbmV3IEVycm9yKCJub3QgY29tcGlsZWQgZm9yIHRoaXMgZW52aXJvbm1lbnQgKGRpZCB5b3UgYnVpbGQgdG8gSFRNTCBhbmQgdHJ5IHRvIHJ1biBpdCBub3Qgb24gdGhlIHdlYiwgb3Igc2V0IEVOVklST05NRU5UIHRvIHNvbWV0aGluZyAtIGxpa2Ugbm9kZSAtIGFuZCBydW4gaXQgc29tZXBsYWNlIGVsc2UgLSBsaWtlIG9uIHRoZSB3ZWI/KSIpO3tyZWFkXz11cmw9Pnt2YXIgeGhyPW5ldyBYTUxIdHRwUmVxdWVzdDt4aHIub3BlbigiR0VUIix1cmwsZmFsc2UpO3hoci5zZW5kKG51bGwpO3JldHVybiB4aHIucmVzcG9uc2VUZXh0fTtpZihFTlZJUk9OTUVOVF9JU19XT1JLRVIpe3JlYWRCaW5hcnk9dXJsPT57dmFyIHhocj1uZXcgWE1MSHR0cFJlcXVlc3Q7eGhyLm9wZW4oIkdFVCIsdXJsLGZhbHNlKTt4aHIucmVzcG9uc2VUeXBlPSJhcnJheWJ1ZmZlciI7eGhyLnNlbmQobnVsbCk7cmV0dXJuIG5ldyBVaW50OEFycmF5KHhoci5yZXNwb25zZSl9O31yZWFkQXN5bmM9KHVybCxvbmxvYWQsb25lcnJvcik9Pnt2YXIgeGhyPW5ldyBYTUxIdHRwUmVxdWVzdDt4aHIub3BlbigiR0VUIix1cmwsdHJ1ZSk7eGhyLnJlc3BvbnNlVHlwZT0iYXJyYXlidWZmZXIiO3hoci5vbmxvYWQ9KCk9PntpZih4aHIuc3RhdHVzPT0yMDB8fHhoci5zdGF0dXM9PTAmJnhoci5yZXNwb25zZSl7b25sb2FkKHhoci5yZXNwb25zZSk7cmV0dXJufW9uZXJyb3IoKTt9O3hoci5vbmVycm9yPW9uZXJyb3I7eGhyLnNlbmQobnVsbCk7fTt9fWVsc2Uge3Rocm93IG5ldyBFcnJvcigiZW52aXJvbm1lbnQgZGV0ZWN0aW9uIGVycm9yIil9dmFyIG91dD1Nb2R1bGVbInByaW50Il18fGNvbnNvbGUubG9nLmJpbmQoY29uc29sZSk7dmFyIGVycj1Nb2R1bGVbInByaW50RXJyIl18fGNvbnNvbGUuZXJyb3IuYmluZChjb25zb2xlKTtPYmplY3QuYXNzaWduKE1vZHVsZSxtb2R1bGVPdmVycmlkZXMpO21vZHVsZU92ZXJyaWRlcz1udWxsO2NoZWNrSW5jb21pbmdNb2R1bGVBUEkoKTtpZihNb2R1bGVbImFyZ3VtZW50cyJdKU1vZHVsZVsiYXJndW1lbnRzIl07bGVnYWN5TW9kdWxlUHJvcCgiYXJndW1lbnRzIiwiYXJndW1lbnRzXyIpO2lmKE1vZHVsZVsidGhpc1Byb2dyYW0iXSl0aGlzUHJvZ3JhbT1Nb2R1bGVbInRoaXNQcm9ncmFtIl07bGVnYWN5TW9kdWxlUHJvcCgidGhpc1Byb2dyYW0iLCJ0aGlzUHJvZ3JhbSIpO2lmKE1vZHVsZVsicXVpdCJdKU1vZHVsZVsicXVpdCJdO2xlZ2FjeU1vZHVsZVByb3AoInF1aXQiLCJxdWl0XyIpO2Fzc2VydCh0eXBlb2YgTW9kdWxlWyJtZW1vcnlJbml0aWFsaXplclByZWZpeFVSTCJdPT0idW5kZWZpbmVkIiwiTW9kdWxlLm1lbW9yeUluaXRpYWxpemVyUHJlZml4VVJMIG9wdGlvbiB3YXMgcmVtb3ZlZCwgdXNlIE1vZHVsZS5sb2NhdGVGaWxlIGluc3RlYWQiKTthc3NlcnQodHlwZW9mIE1vZHVsZVsicHRocmVhZE1haW5QcmVmaXhVUkwiXT09InVuZGVmaW5lZCIsIk1vZHVsZS5wdGhyZWFkTWFpblByZWZpeFVSTCBvcHRpb24gd2FzIHJlbW92ZWQsIHVzZSBNb2R1bGUubG9jYXRlRmlsZSBpbnN0ZWFkIik7YXNzZXJ0KHR5cGVvZiBNb2R1bGVbImNkSW5pdGlhbGl6ZXJQcmVmaXhVUkwiXT09InVuZGVmaW5lZCIsIk1vZHVsZS5jZEluaXRpYWxpemVyUHJlZml4VVJMIG9wdGlvbiB3YXMgcmVtb3ZlZCwgdXNlIE1vZHVsZS5sb2NhdGVGaWxlIGluc3RlYWQiKTthc3NlcnQodHlwZW9mIE1vZHVsZVsiZmlsZVBhY2thZ2VQcmVmaXhVUkwiXT09InVuZGVmaW5lZCIsIk1vZHVsZS5maWxlUGFja2FnZVByZWZpeFVSTCBvcHRpb24gd2FzIHJlbW92ZWQsIHVzZSBNb2R1bGUubG9jYXRlRmlsZSBpbnN0ZWFkIik7YXNzZXJ0KHR5cGVvZiBNb2R1bGVbInJlYWQiXT09InVuZGVmaW5lZCIsIk1vZHVsZS5yZWFkIG9wdGlvbiB3YXMgcmVtb3ZlZCAobW9kaWZ5IHJlYWRfIGluIEpTKSIpO2Fzc2VydCh0eXBlb2YgTW9kdWxlWyJyZWFkQXN5bmMiXT09InVuZGVmaW5lZCIsIk1vZHVsZS5yZWFkQXN5bmMgb3B0aW9uIHdhcyByZW1vdmVkIChtb2RpZnkgcmVhZEFzeW5jIGluIEpTKSIpO2Fzc2VydCh0eXBlb2YgTW9kdWxlWyJyZWFkQmluYXJ5Il09PSJ1bmRlZmluZWQiLCJNb2R1bGUucmVhZEJpbmFyeSBvcHRpb24gd2FzIHJlbW92ZWQgKG1vZGlmeSByZWFkQmluYXJ5IGluIEpTKSIpO2Fzc2VydCh0eXBlb2YgTW9kdWxlWyJzZXRXaW5kb3dUaXRsZSJdPT0idW5kZWZpbmVkIiwiTW9kdWxlLnNldFdpbmRvd1RpdGxlIG9wdGlvbiB3YXMgcmVtb3ZlZCAobW9kaWZ5IGVtc2NyaXB0ZW5fc2V0X3dpbmRvd190aXRsZSBpbiBKUykiKTthc3NlcnQodHlwZW9mIE1vZHVsZVsiVE9UQUxfTUVNT1JZIl09PSJ1bmRlZmluZWQiLCJNb2R1bGUuVE9UQUxfTUVNT1JZIGhhcyBiZWVuIHJlbmFtZWQgTW9kdWxlLklOSVRJQUxfTUVNT1JZIik7bGVnYWN5TW9kdWxlUHJvcCgiYXNtIiwid2FzbUV4cG9ydHMiKTtsZWdhY3lNb2R1bGVQcm9wKCJyZWFkIiwicmVhZF8iKTtsZWdhY3lNb2R1bGVQcm9wKCJyZWFkQXN5bmMiLCJyZWFkQXN5bmMiKTtsZWdhY3lNb2R1bGVQcm9wKCJyZWFkQmluYXJ5IiwicmVhZEJpbmFyeSIpO2xlZ2FjeU1vZHVsZVByb3AoInNldFdpbmRvd1RpdGxlIiwic2V0V2luZG93VGl0bGUiKTthc3NlcnQoIUVOVklST05NRU5UX0lTX1NIRUxMLCJzaGVsbCBlbnZpcm9ubWVudCBkZXRlY3RlZCBidXQgbm90IGVuYWJsZWQgYXQgYnVpbGQgdGltZS4gIEFkZCBgc2hlbGxgIHRvIGAtc0VOVklST05NRU5UYCB0byBlbmFibGUuIik7dmFyIHdhc21CaW5hcnk7aWYoTW9kdWxlWyJ3YXNtQmluYXJ5Il0pd2FzbUJpbmFyeT1Nb2R1bGVbIndhc21CaW5hcnkiXTtsZWdhY3lNb2R1bGVQcm9wKCJ3YXNtQmluYXJ5Iiwid2FzbUJpbmFyeSIpO2lmKHR5cGVvZiBXZWJBc3NlbWJseSE9Im9iamVjdCIpe2Vycigibm8gbmF0aXZlIHdhc20gc3VwcG9ydCBkZXRlY3RlZCIpO31mdW5jdGlvbiBpbnRBcnJheUZyb21CYXNlNjQocyl7aWYodHlwZW9mIEVOVklST05NRU5UX0lTX05PREUhPSJ1bmRlZmluZWQiJiZFTlZJUk9OTUVOVF9JU19OT0RFKXt2YXIgYnVmPUJ1ZmZlci5mcm9tKHMsImJhc2U2NCIpO3JldHVybiBuZXcgVWludDhBcnJheShidWYuYnVmZmVyLGJ1Zi5ieXRlT2Zmc2V0LGJ1Zi5sZW5ndGgpfXZhciBkZWNvZGVkPWF0b2Iocyk7dmFyIGJ5dGVzPW5ldyBVaW50OEFycmF5KGRlY29kZWQubGVuZ3RoKTtmb3IodmFyIGk9MDtpPGRlY29kZWQubGVuZ3RoOysraSl7Ynl0ZXNbaV09ZGVjb2RlZC5jaGFyQ29kZUF0KGkpO31yZXR1cm4gYnl0ZXN9ZnVuY3Rpb24gdHJ5UGFyc2VBc0RhdGFVUkkoZmlsZW5hbWUpe2lmKCFpc0RhdGFVUkkoZmlsZW5hbWUpKXtyZXR1cm59cmV0dXJuIGludEFycmF5RnJvbUJhc2U2NChmaWxlbmFtZS5zbGljZShkYXRhVVJJUHJlZml4Lmxlbmd0aCkpfXZhciB3YXNtTWVtb3J5O3ZhciBBQk9SVD1mYWxzZTtmdW5jdGlvbiBhc3NlcnQoY29uZGl0aW9uLHRleHQpe2lmKCFjb25kaXRpb24pe2Fib3J0KCJBc3NlcnRpb24gZmFpbGVkIisodGV4dD8iOiAiK3RleHQ6IiIpKTt9fXZhciBIRUFQOCxIRUFQVTgsSEVBUDE2LEhFQVBVMTYsSEVBUDMyLEhFQVBVMzIsSEVBUEYzMixIRUFQRjY0O2Z1bmN0aW9uIHVwZGF0ZU1lbW9yeVZpZXdzKCl7dmFyIGI9d2FzbU1lbW9yeS5idWZmZXI7TW9kdWxlWyJIRUFQOCJdPUhFQVA4PW5ldyBJbnQ4QXJyYXkoYik7TW9kdWxlWyJIRUFQMTYiXT1IRUFQMTY9bmV3IEludDE2QXJyYXkoYik7TW9kdWxlWyJIRUFQVTgiXT1IRUFQVTg9bmV3IFVpbnQ4QXJyYXkoYik7TW9kdWxlWyJIRUFQVTE2Il09SEVBUFUxNj1uZXcgVWludDE2QXJyYXkoYik7TW9kdWxlWyJIRUFQMzIiXT1IRUFQMzI9bmV3IEludDMyQXJyYXkoYik7TW9kdWxlWyJIRUFQVTMyIl09SEVBUFUzMj1uZXcgVWludDMyQXJyYXkoYik7TW9kdWxlWyJIRUFQRjMyIl09SEVBUEYzMj1uZXcgRmxvYXQzMkFycmF5KGIpO01vZHVsZVsiSEVBUEY2NCJdPUhFQVBGNjQ9bmV3IEZsb2F0NjRBcnJheShiKTt9YXNzZXJ0KCFNb2R1bGVbIlNUQUNLX1NJWkUiXSwiU1RBQ0tfU0laRSBjYW4gbm8gbG9uZ2VyIGJlIHNldCBhdCBydW50aW1lLiAgVXNlIC1zU1RBQ0tfU0laRSBhdCBsaW5rIHRpbWUiKTthc3NlcnQodHlwZW9mIEludDMyQXJyYXkhPSJ1bmRlZmluZWQiJiZ0eXBlb2YgRmxvYXQ2NEFycmF5IT09InVuZGVmaW5lZCImJkludDMyQXJyYXkucHJvdG90eXBlLnN1YmFycmF5IT11bmRlZmluZWQmJkludDMyQXJyYXkucHJvdG90eXBlLnNldCE9dW5kZWZpbmVkLCJKUyBlbmdpbmUgZG9lcyBub3QgcHJvdmlkZSBmdWxsIHR5cGVkIGFycmF5IHN1cHBvcnQiKTthc3NlcnQoIU1vZHVsZVsid2FzbU1lbW9yeSJdLCJVc2Ugb2YgYHdhc21NZW1vcnlgIGRldGVjdGVkLiAgVXNlIC1zSU1QT1JURURfTUVNT1JZIHRvIGRlZmluZSB3YXNtTWVtb3J5IGV4dGVybmFsbHkiKTthc3NlcnQoIU1vZHVsZVsiSU5JVElBTF9NRU1PUlkiXSwiRGV0ZWN0ZWQgcnVudGltZSBJTklUS