@jsprismarine/raknet
Version:
Basic RakNet implementation written in TypeScript
383 lines (377 loc) • 58.1 kB
JavaScript
'use strict';
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
const BinaryStream = require('@jsprismarine/jsbinaryutils');
const assert = require('node:assert');
const Constants = require('./Constants.cjs.cjs');
const protocol_ACK = require('./protocol/ACK.cjs.cjs');
const protocol_BitFlags = require('./protocol/BitFlags.cjs.cjs');
const protocol_Frame = require('./protocol/Frame.cjs.cjs');
const protocol_FrameReliability = require('./protocol/FrameReliability.cjs.cjs');
const protocol_FrameSet = require('./protocol/FrameSet.cjs.cjs');
const protocol_MessageIdentifiers = require('./protocol/MessageIdentifiers.cjs.cjs');
const protocol_NACK = require('./protocol/NACK.cjs.cjs');
const protocol_PacketPool = require('./protocol/PacketPool.cjs.cjs');
const protocol_connection_ConnectedPing = require('./protocol/connection/ConnectedPing.cjs.cjs');
const protocol_connection_ConnectedPong = require('./protocol/connection/ConnectedPong.cjs.cjs');
const protocol_login_ConnectionRequest = require('./protocol/login/ConnectionRequest.cjs.cjs');
const protocol_login_ConnectionRequestAccepted = require('./protocol/login/ConnectionRequestAccepted.cjs.cjs');
const utils_InetAddress = require('./utils/InetAddress.cjs.cjs');
const _interopDefault = e => e && e.__esModule ? e : { default: e };
const BinaryStream__default = /*#__PURE__*/_interopDefault(BinaryStream);
const assert__default = /*#__PURE__*/_interopDefault(assert);
var RakNetPriority = /* @__PURE__ */ ((RakNetPriority2) => {
RakNetPriority2[RakNetPriority2["NORMAL"] = 0] = "NORMAL";
RakNetPriority2[RakNetPriority2["IMMEDIATE"] = 1] = "IMMEDIATE";
return RakNetPriority2;
})(RakNetPriority || {});
var SessionStatus = /* @__PURE__ */ ((SessionStatus2) => {
SessionStatus2[SessionStatus2["CONNECTING"] = 0] = "CONNECTING";
SessionStatus2[SessionStatus2["CONNECTED"] = 1] = "CONNECTED";
SessionStatus2[SessionStatus2["DISCONNECTING"] = 2] = "DISCONNECTING";
SessionStatus2[SessionStatus2["DISCONNECTED"] = 3] = "DISCONNECTED";
return SessionStatus2;
})(SessionStatus || {});
class Session {
constructor(listener, mtuSize, rinfo, guid) {
this.listener = listener;
this.mtuSize = mtuSize;
this.rinfo = rinfo;
this.guid = guid;
this.lastUpdate = Date.now();
this.outputOrderIndex = new Array(Constants.MAX_CHANNELS).fill(0);
this.outputSequenceIndex = new Array(Constants.MAX_CHANNELS).fill(0);
this.inputOrderIndex = new Array(Constants.MAX_CHANNELS).fill(0);
for (let i = 0; i < Constants.MAX_CHANNELS; i++) {
this.inputOrderingQueue.set(i, /* @__PURE__ */ new Map());
}
this.inputHighestSequenceIndex = new Array(Constants.MAX_CHANNELS).fill(0);
}
state = 0 /* CONNECTING */;
outputFrameQueue = new protocol_FrameSet.default();
outputSequenceNumber = 0;
// TODO: find a better name
outputReliableIndex = 0;
outputBackupQueue = /* @__PURE__ */ new Map();
outputOrderIndex;
outputSequenceIndex;
receivedFrameSequences = /* @__PURE__ */ new Set();
lostFrameSequences = /* @__PURE__ */ new Set();
// Map holding fragments of fragmented packets
fragmentsQueue = /* @__PURE__ */ new Map();
outputFragmentIndex = 0;
lastInputSequenceNumber = -1;
inputHighestSequenceIndex;
inputOrderIndex;
inputOrderingQueue = /* @__PURE__ */ new Map();
// Last timestamp of packet received, helpful for timeout
lastUpdate = Date.now();
active = true;
// Packet pool is the best option to reduce allocations
packetPool = new protocol_PacketPool.default();
// Lookup table for packet handlers, always O(1) in average and worst case
packetHandlers = {
// 0x40 | 0xc0 = MessageHeaders.ACKNOWLEDGE_PACKET
[protocol_MessageIdentifiers.MessageIdentifiers.ACKNOWLEDGE_PACKET]: (buffer) => {
const ack = this.packetPool.getAckInstance();
ack.buffer = buffer;
ack.decode();
this.handleACK(ack);
},
[protocol_MessageIdentifiers.MessageIdentifiers.NACKNOWLEDGE_PACKET]: (buffer) => {
const nack = this.packetPool.getNackInstance();
nack.buffer = buffer;
nack.decode();
this.handleNACK(nack);
},
[protocol_BitFlags.BitFlags.VALID]: (buffer) => {
const frameSet = this.packetPool.getFrameSetInstance();
frameSet.buffer = buffer;
frameSet.decode();
this.handleFrameSet(frameSet);
}
};
update(timestamp) {
if (!this.isActive() && !this.isDisconnected() && this.lastUpdate + 1e4 < timestamp) {
this.disconnect("timeout");
return;
}
this.active = false;
if (this.receivedFrameSequences.size > 0) {
const ack = new protocol_ACK.default();
ack.sequenceNumbers = Array.from(this.receivedFrameSequences).map((seq) => {
this.receivedFrameSequences.delete(seq);
return seq;
});
this.sendPacket(ack);
}
if (this.lostFrameSequences.size > 0) {
const pk = new protocol_NACK.default();
pk.sequenceNumbers = Array.from(this.lostFrameSequences).map((seq) => {
this.lostFrameSequences.delete(seq);
return seq;
});
this.sendPacket(pk);
}
this.sendFrameQueue();
}
// https://github.com/facebookarchive/RakNet/blob/1a169895a900c9fc4841c556e16514182b75faf8/Source/ReliabilityLayer.cpp#L635
handle(buffer) {
this.active = true;
this.lastUpdate = Date.now();
const handler = this.packetHandlers[buffer[0] & 240];
if (handler) {
handler(buffer);
} else {
this.listener.getLogger().verbose(`Received an unknown packet type=${buffer[0]}`);
}
}
handleFrameSet(frameSet) {
if (this.receivedFrameSequences.has(frameSet.sequenceNumber)) {
this.listener.getLogger().debug(`Discarded duplicated packet from client=${this.getAddress()}`);
return;
}
this.lostFrameSequences.delete(frameSet.sequenceNumber);
if (frameSet.sequenceNumber < this.lastInputSequenceNumber || frameSet.sequenceNumber === this.lastInputSequenceNumber) {
return;
}
this.receivedFrameSequences.add(frameSet.sequenceNumber);
const diff = frameSet.sequenceNumber - this.lastInputSequenceNumber;
if (diff !== 1) {
for (let i = this.lastInputSequenceNumber + 1; i < frameSet.sequenceNumber; i++) {
if (!this.receivedFrameSequences.has(i)) {
this.lostFrameSequences.add(i);
}
}
}
this.lastInputSequenceNumber = frameSet.sequenceNumber;
for (const frame of frameSet.frames) {
this.handleFrame(frame);
}
}
handleACK(ack) {
for (const seq of ack.sequenceNumbers) {
this.outputBackupQueue.delete(seq);
}
}
handleNACK(nack) {
for (const seq of nack.sequenceNumbers) {
if (this.outputBackupQueue.has(seq)) {
const lostFrames = this.outputBackupQueue.get(seq);
for (const lostFrame of lostFrames) {
this.sendFrame(lostFrame, 1 /* IMMEDIATE */);
}
this.outputBackupQueue.delete(seq);
}
}
}
handleFrame(frame) {
if (frame.isFragmented()) {
this.handleFragment(frame);
return;
}
const orderChannel = frame.orderChannel;
const orderIndex = frame.orderIndex;
const sequenceIndex = frame.sequenceIndex;
if (frame.isSequenced()) {
if (sequenceIndex < this.inputHighestSequenceIndex[orderChannel] || orderIndex < this.inputOrderIndex[orderChannel]) {
return;
}
this.inputHighestSequenceIndex[orderChannel] = sequenceIndex + 1;
this.handlePacket(frame);
} else if (frame.isOrdered()) {
if (orderIndex === this.inputOrderIndex[orderChannel]) {
this.inputHighestSequenceIndex[orderChannel] = 0;
this.inputOrderIndex[orderChannel] = orderIndex + 1;
this.handlePacket(frame);
let i = this.inputOrderIndex[orderChannel];
const outOfOrderQueue = this.inputOrderingQueue.get(orderChannel);
for (; outOfOrderQueue.has(i); i++) {
this.handlePacket(outOfOrderQueue.get(i));
outOfOrderQueue.delete(i);
}
this.inputOrderingQueue.set(orderChannel, outOfOrderQueue);
this.inputOrderIndex[orderChannel] = i;
} else if (orderIndex > this.inputOrderIndex[orderChannel]) {
const unordered = this.inputOrderingQueue.get(orderChannel);
unordered.set(orderIndex, frame);
}
} else {
this.handlePacket(frame);
}
}
sendFrame(frame, flags = 0 /* NORMAL */) {
assert__default.default(typeof frame.orderChannel === "number", "Frame OrderChannel cannot be null");
if (frame.isSequenced()) {
frame.orderIndex = this.outputOrderIndex[frame.orderChannel];
frame.sequenceIndex = this.outputSequenceIndex[frame.orderChannel]++;
} else if (frame.isOrderedExclusive()) {
frame.orderIndex = this.outputOrderIndex[frame.orderChannel]++;
this.outputSequenceIndex[frame.orderChannel] = 0;
}
frame.reliableIndex = this.outputReliableIndex++;
const maxSize = this.getMTU() - protocol_FrameSet.DATAGRAM_HEADER_BYTE_LENGTH - protocol_Frame.MAX_FRAME_BYTE_LENGTH;
if (frame.content.byteLength > maxSize) {
const buffer = Buffer.from(frame.content);
const fragmentId = this.outputFragmentIndex++ % 65536;
for (let i = 0; i < buffer.byteLength; i += maxSize) {
if (i !== 0) {
frame.reliableIndex = this.outputReliableIndex++;
}
frame.content = buffer.slice(i, i + maxSize);
frame.fragmentIndex = i / maxSize;
frame.fragmentId = fragmentId;
frame.fragmentSize = Math.ceil(buffer.byteLength / maxSize);
this.addFrameToQueue(frame, flags | 1 /* IMMEDIATE */);
}
} else {
this.addFrameToQueue(frame, flags);
}
}
addFrameToQueue(frame, priority = 0 /* NORMAL */) {
let length = 4;
for (const queuedFrame of this.outputFrameQueue.frames) {
length += queuedFrame.getByteLength();
}
if (length + frame.getByteLength() > this.mtuSize - 36) {
this.sendFrameQueue();
}
this.outputFrameQueue.frames.push(frame);
if (priority === 1 /* IMMEDIATE */) {
this.sendFrameQueue();
}
}
handlePacket(packet) {
const id = packet.content[0];
if (this.state === 0 /* CONNECTING */) {
if (id === protocol_MessageIdentifiers.MessageIdentifiers.CONNECTION_REQUEST) {
this.handleConnectionRequest(packet.content).then(
(encapsulated) => this.sendFrame(encapsulated, 1 /* IMMEDIATE */),
() => {
}
);
} else if (id === protocol_MessageIdentifiers.MessageIdentifiers.NEW_INCOMING_CONNECTION) {
this.state = 1 /* CONNECTED */;
this.listener.emit("openConnection", this);
}
} else if (id === protocol_MessageIdentifiers.MessageIdentifiers.DISCONNECTION_NOTIFICATION) {
this.disconnect();
} else if (id === protocol_MessageIdentifiers.MessageIdentifiers.CONNECTED_PING) {
this.handleConnectedPing(packet.content).then(
(encapsulated) => this.sendFrame(encapsulated),
() => {
}
);
} else if (this.state === 1 /* CONNECTED */) {
this.listener.emit("encapsulated", packet, this.getAddress());
}
}
async handleConnectionRequest(buffer) {
const dataPacket = new protocol_login_ConnectionRequest.default(buffer);
dataPacket.decode();
const pk = new protocol_login_ConnectionRequestAccepted.default();
pk.clientAddress = this.getAddress();
pk.requestTimestamp = dataPacket.requestTimestamp;
pk.acceptedTimestamp = BigInt(Date.now());
pk.encode();
const sendPacket = new protocol_Frame.default();
sendPacket.reliability = protocol_FrameReliability.FrameReliability.UNRELIABLE;
sendPacket.orderChannel = 0;
sendPacket.content = pk.getBuffer();
return sendPacket;
}
async handleConnectedPing(buffer) {
const dataPacket = new protocol_connection_ConnectedPing.default(buffer);
dataPacket.decode();
const pk = new protocol_connection_ConnectedPong.default();
pk.clientTimestamp = dataPacket.clientTimestamp;
pk.serverTimestamp = BigInt(Date.now());
pk.encode();
const sendPacket = new protocol_Frame.default();
sendPacket.reliability = protocol_FrameReliability.FrameReliability.UNRELIABLE;
sendPacket.orderChannel = 0;
sendPacket.content = pk.getBuffer();
return sendPacket;
}
handleFragment(frame) {
if (!this.fragmentsQueue.has(frame.fragmentId)) {
this.fragmentsQueue.set(frame.fragmentId, /* @__PURE__ */ new Map([[frame.fragmentIndex, frame]]));
} else {
const value = this.fragmentsQueue.get(frame.fragmentId);
value.set(frame.fragmentIndex, frame);
if (value.size === frame.fragmentSize) {
const stream = new BinaryStream__default.default();
for (let i = 0; i < value.size; i++) {
const splitPacket = value.get(i);
stream.write(splitPacket.content);
}
const assembledFrame = new protocol_Frame.default();
assembledFrame.content = stream.getBuffer();
assembledFrame.reliability = frame.reliability;
assembledFrame.reliableIndex = frame.reliableIndex;
assembledFrame.sequenceIndex = frame.sequenceIndex;
assembledFrame.orderIndex = frame.orderIndex;
assembledFrame.orderChannel = frame.orderChannel;
this.fragmentsQueue.delete(frame.fragmentId);
this.handleFrame(assembledFrame);
}
}
}
sendFrameQueue() {
if (this.outputFrameQueue.frames.length > 0) {
this.outputFrameQueue.sequenceNumber = this.outputSequenceNumber++;
this.sendFrameSet(this.outputFrameQueue);
this.outputFrameQueue = new protocol_FrameSet.default();
}
}
sendFrameSet(frameSet) {
this.sendPacket(frameSet);
this.outputBackupQueue.set(
frameSet.sequenceNumber,
frameSet.frames.filter((frame) => frame.isReliable())
);
}
sendPacket(packet) {
this.listener.sendPacket(packet, this.rinfo);
}
close() {
const stream = new BinaryStream__default.default(Buffer.from("\0\0\b", "binary"));
this.addFrameToQueue(new protocol_Frame.default().fromBinary(stream), 1 /* IMMEDIATE */);
}
/**
* Kick a client
* @param reason - the reason message, optional
*/
disconnect(reason = "client disconnect") {
this.close();
this.state = 3 /* DISCONNECTED */;
this.update(Date.now());
this.listener.removeSession(this, reason);
}
getState() {
return this.state;
}
isActive() {
return this.active;
}
isDisconnected() {
return this.state === 3 /* DISCONNECTED */;
}
getListener() {
return this.listener;
}
/**
* Returns the maxmium transfer unit
* for this connection.
* @returns {number} the UDP adjusted.
*/
getMTU() {
return this.mtuSize - Constants.UDP_HEADER_SIZE;
}
getAddress() {
return new utils_InetAddress.default(this.rinfo.address, this.rinfo.port, 4);
}
}
exports.RakNetPriority = RakNetPriority;
exports.SessionStatus = SessionStatus;
exports.default = Session;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2Vzc2lvbi5janMuY2pzIiwic291cmNlcyI6WyIuLi9zcmMvU2Vzc2lvbi50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgQmluYXJ5U3RyZWFtIGZyb20gJ0Bqc3ByaXNtYXJpbmUvanNiaW5hcnl1dGlscyc7XG5pbXBvcnQgYXNzZXJ0IGZyb20gJ25vZGU6YXNzZXJ0JztcbmltcG9ydCB7IE1BWF9DSEFOTkVMUywgVURQX0hFQURFUl9TSVpFIH0gZnJvbSAnLi9Db25zdGFudHMnO1xuaW1wb3J0IEFDSyBmcm9tICcuL3Byb3RvY29sL0FDSyc7XG5pbXBvcnQgQml0RmxhZ3MgZnJvbSAnLi9wcm90b2NvbC9CaXRGbGFncyc7XG5pbXBvcnQgRnJhbWUsIHsgTUFYX0ZSQU1FX0JZVEVfTEVOR1RIIH0gZnJvbSAnLi9wcm90b2NvbC9GcmFtZSc7XG5pbXBvcnQgRnJhbWVSZWxpYWJpbGl0eSBmcm9tICcuL3Byb3RvY29sL0ZyYW1lUmVsaWFiaWxpdHknO1xuaW1wb3J0IEZyYW1lU2V0LCB7IERBVEFHUkFNX0hFQURFUl9CWVRFX0xFTkdUSCB9IGZyb20gJy4vcHJvdG9jb2wvRnJhbWVTZXQnO1xuaW1wb3J0IHsgTWVzc2FnZUlkZW50aWZpZXJzIH0gZnJvbSAnLi9wcm90b2NvbC9NZXNzYWdlSWRlbnRpZmllcnMnO1xuaW1wb3J0IE5BQ0sgZnJvbSAnLi9wcm90b2NvbC9OQUNLJztcbmltcG9ydCBQYWNrZXRQb29sIGZyb20gJy4vcHJvdG9jb2wvUGFja2V0UG9vbCc7XG5pbXBvcnQgQ29ubmVjdGVkUGluZyBmcm9tICcuL3Byb3RvY29sL2Nvbm5lY3Rpb24vQ29ubmVjdGVkUGluZyc7XG5pbXBvcnQgQ29ubmVjdGVkUG9uZyBmcm9tICcuL3Byb3RvY29sL2Nvbm5lY3Rpb24vQ29ubmVjdGVkUG9uZyc7XG5pbXBvcnQgQ29ubmVjdGlvblJlcXVlc3QgZnJvbSAnLi9wcm90b2NvbC9sb2dpbi9Db25uZWN0aW9uUmVxdWVzdCc7XG5pbXBvcnQgQ29ubmVjdGlvblJlcXVlc3RBY2NlcHRlZCBmcm9tICcuL3Byb3RvY29sL2xvZ2luL0Nvbm5lY3Rpb25SZXF1ZXN0QWNjZXB0ZWQnO1xuaW1wb3J0IEluZXRBZGRyZXNzIGZyb20gJy4vdXRpbHMvSW5ldEFkZHJlc3MnO1xuXG5pbXBvcnQgdHlwZSB7IFJlbW90ZUluZm8gfSBmcm9tICdub2RlOmRncmFtJztcbmltcG9ydCB0eXBlIFJha05ldExpc3RlbmVyIGZyb20gJy4vU2VydmVyU29ja2V0JztcbmltcG9ydCB0eXBlIFBhY2tldCBmcm9tICcuL3Byb3RvY29sL1BhY2tldCc7XG5cbmV4cG9ydCBlbnVtIFJha05ldFByaW9yaXR5IHtcbiAgICBOT1JNQUwsXG4gICAgSU1NRURJQVRFXG59XG5cbmV4cG9ydCBlbnVtIFNlc3Npb25TdGF0dXMge1xuICAgIENPTk5FQ1RJTkcsXG4gICAgQ09OTkVDVEVELFxuICAgIERJU0NPTk5FQ1RJTkcsXG4gICAgRElTQ09OTkVDVEVEXG59XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFNlc3Npb24ge1xuICAgIHByaXZhdGUgc3RhdGUgPSBTZXNzaW9uU3RhdHVzLkNPTk5FQ1RJTkc7XG5cbiAgICBwcml2YXRlIG91dHB1dEZyYW1lUXVldWUgPSBuZXcgRnJhbWVTZXQoKTtcbiAgICBwcml2YXRlIG91dHB1dFNlcXVlbmNlTnVtYmVyID0gMDsgLy8gVE9ETzogZmluZCBhIGJldHRlciBuYW1lXG4gICAgcHJpdmF0ZSBvdXRwdXRSZWxpYWJsZUluZGV4ID0gMDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IG91dHB1dEJhY2t1cFF1ZXVlOiBNYXA8bnVtYmVyLCBGcmFtZVtdPiA9IG5ldyBNYXAoKTtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgb3V0cHV0T3JkZXJJbmRleDogbnVtYmVyW107XG4gICAgcHJpdmF0ZSByZWFkb25seSBvdXRwdXRTZXF1ZW5jZUluZGV4OiBudW1iZXJbXTtcblxuICAgIHByaXZhdGUgcmVjZWl2ZWRGcmFtZVNlcXVlbmNlczogU2V0PG51bWJlcj4gPSBuZXcgU2V0KCk7XG4gICAgcHJpdmF0ZSBsb3N0RnJhbWVTZXF1ZW5jZXM6IFNldDxudW1iZXI+ID0gbmV3IFNldCgpO1xuXG4gICAgLy8gTWFwIGhvbGRpbmcgZnJhZ21lbnRzIG9mIGZyYWdtZW50ZWQgcGFja2V0c1xuICAgIHByaXZhdGUgcmVhZG9ubHkgZnJhZ21lbnRzUXVldWU6IE1hcDxudW1iZXIsIE1hcDxudW1iZXIsIEZyYW1lPj4gPSBuZXcgTWFwKCk7XG4gICAgcHJpdmF0ZSBvdXRwdXRGcmFnbWVudEluZGV4ID0gMDtcblxuICAgIHByaXZhdGUgbGFzdElucHV0U2VxdWVuY2VOdW1iZXIgPSAtMTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGlucHV0SGlnaGVzdFNlcXVlbmNlSW5kZXg6IG51bWJlcltdO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgaW5wdXRPcmRlckluZGV4OiBudW1iZXJbXTtcbiAgICBwcml2YXRlIGlucHV0T3JkZXJpbmdRdWV1ZTogTWFwPG51bWJlciwgTWFwPG51bWJlciwgRnJhbWU+PiA9IG5ldyBNYXAoKTtcblxuICAgIC8vIExhc3QgdGltZXN0YW1wIG9mIHBhY2tldCByZWNlaXZlZCwgaGVscGZ1bCBmb3IgdGltZW91dFxuICAgIHByaXZhdGUgbGFzdFVwZGF0ZTogbnVtYmVyID0gRGF0ZS5ub3coKTtcbiAgICBwcml2YXRlIGFjdGl2ZSA9IHRydWU7XG5cbiAgICAvLyBQYWNrZXQgcG9vbCBpcyB0aGUgYmVzdCBvcHRpb24gdG8gcmVkdWNlIGFsbG9jYXRpb25zXG4gICAgcHJpdmF0ZSByZWFkb25seSBwYWNrZXRQb29sID0gbmV3IFBhY2tldFBvb2woKTtcblxuICAgIHB1YmxpYyBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHJpdmF0ZSByZWFkb25seSBsaXN0ZW5lcjogUmFrTmV0TGlzdGVuZXIsXG4gICAgICAgIHByaXZhdGUgcmVhZG9ubHkgbXR1U2l6ZTogbnVtYmVyLFxuICAgICAgICBwdWJsaWMgcmVhZG9ubHkgcmluZm86IFJlbW90ZUluZm8sXG4gICAgICAgIHB1YmxpYyByZWFkb25seSBndWlkOiBiaWdpbnRcbiAgICApIHtcbiAgICAgICAgdGhpcy5sYXN0VXBkYXRlID0gRGF0ZS5ub3coKTtcblxuICAgICAgICB0aGlzLm91dHB1dE9yZGVySW5kZXggPSBuZXcgQXJyYXkoTUFYX0NIQU5ORUxTKS5maWxsKDApO1xuICAgICAgICB0aGlzLm91dHB1dFNlcXVlbmNlSW5kZXggPSBuZXcgQXJyYXkoTUFYX0NIQU5ORUxTKS5maWxsKDApO1xuXG4gICAgICAgIHRoaXMuaW5wdXRPcmRlckluZGV4ID0gbmV3IEFycmF5KE1BWF9DSEFOTkVMUykuZmlsbCgwKTtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBNQVhfQ0hBTk5FTFM7IGkrKykge1xuICAgICAgICAgICAgdGhpcy5pbnB1dE9yZGVyaW5nUXVldWUuc2V0KGksIG5ldyBNYXAoKSk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmlucHV0SGlnaGVzdFNlcXVlbmNlSW5kZXggPSBuZXcgQXJyYXkoTUFYX0NIQU5ORUxTKS5maWxsKDApO1xuICAgIH1cblxuICAgIC8vIExvb2t1cCB0YWJsZSBmb3IgcGFja2V0IGhhbmRsZXJzLCBhbHdheXMgTygxKSBpbiBhdmVyYWdlIGFuZCB3b3JzdCBjYXNlXG4gICAgcHJpdmF0ZSByZWFkb25seSBwYWNrZXRIYW5kbGVyczogUmVjb3JkPG51bWJlciwgKGJ1ZmZlcjogQnVmZmVyKSA9PiB2b2lkPiA9IHtcbiAgICAgICAgLy8gMHg0MCB8IDB4YzAgPSBNZXNzYWdlSGVhZGVycy5BQ0tOT1dMRURHRV9QQUNLRVRcbiAgICAgICAgW01lc3NhZ2VJZGVudGlmaWVycy5BQ0tOT1dMRURHRV9QQUNLRVRdOiAoYnVmZmVyOiBCdWZmZXIpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGFjayA9IHRoaXMucGFja2V0UG9vbC5nZXRBY2tJbnN0YW5jZSgpO1xuICAgICAgICAgICAgKGFjayBhcyBhbnkpLmJ1ZmZlciA9IGJ1ZmZlcjtcbiAgICAgICAgICAgIGFjay5kZWNvZGUoKTtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlQUNLKGFjayk7XG4gICAgICAgIH0sXG4gICAgICAgIFtNZXNzYWdlSWRlbnRpZmllcnMuTkFDS05PV0xFREdFX1BBQ0tFVF06IChidWZmZXI6IEJ1ZmZlcikgPT4ge1xuICAgICAgICAgICAgY29uc3QgbmFjayA9IHRoaXMucGFja2V0UG9vbC5nZXROYWNrSW5zdGFuY2UoKTtcbiAgICAgICAgICAgIChuYWNrIGFzIGFueSkuYnVmZmVyID0gYnVmZmVyO1xuICAgICAgICAgICAgbmFjay5kZWNvZGUoKTtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlTkFDSyhuYWNrKTtcbiAgICAgICAgfSxcbiAgICAgICAgW0JpdEZsYWdzLlZBTElEXTogKGJ1ZmZlcjogQnVmZmVyKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBmcmFtZVNldCA9IHRoaXMucGFja2V0UG9vbC5nZXRGcmFtZVNldEluc3RhbmNlKCk7XG4gICAgICAgICAgICAoZnJhbWVTZXQgYXMgYW55KS5idWZmZXIgPSBidWZmZXI7XG4gICAgICAgICAgICBmcmFtZVNldC5kZWNvZGUoKTtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlRnJhbWVTZXQoZnJhbWVTZXQpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHB1YmxpYyB1cGRhdGUodGltZXN0YW1wOiBudW1iZXIpOiB2b2lkIHtcbiAgICAgICAgaWYgKCF0aGlzLmlzQWN0aXZlKCkgJiYgIXRoaXMuaXNEaXNjb25uZWN0ZWQoKSAmJiB0aGlzLmxhc3RVcGRhdGUgKyAxMF8wMDAgPCB0aW1lc3RhbXApIHtcbiAgICAgICAgICAgIHRoaXMuZGlzY29ubmVjdCgndGltZW91dCcpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5hY3RpdmUgPSBmYWxzZTtcblxuICAgICAgICAvLyBUT0RPOiBhIHF1ZXVlIGp1c3QgZm9yIEFDS3Mgc2VxdWVuY2VzIHRvIGF2b2lkIGR1cGxpY2F0ZWRzXG4gICAgICAgIGlmICh0aGlzLnJlY2VpdmVkRnJhbWVTZXF1ZW5jZXMuc2l6ZSA+IDApIHtcbiAgICAgICAgICAgIGNvbnN0IGFjayA9IG5ldyBBQ0soKTtcbiAgICAgICAgICAgIGFjay5zZXF1ZW5jZU51bWJlcnMgPSBBcnJheS5mcm9tKHRoaXMucmVjZWl2ZWRGcmFtZVNlcXVlbmNlcykubWFwKChzZXEpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLnJlY2VpdmVkRnJhbWVTZXF1ZW5jZXMuZGVsZXRlKHNlcSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHNlcTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhpcy5zZW5kUGFja2V0KGFjayk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5sb3N0RnJhbWVTZXF1ZW5jZXMuc2l6ZSA+IDApIHtcbiAgICAgICAgICAgIGNvbnN0IHBrID0gbmV3IE5BQ0soKTtcbiAgICAgICAgICAgIHBrLnNlcXVlbmNlTnVtYmVycyA9IEFycmF5LmZyb20odGhpcy5sb3N0RnJhbWVTZXF1ZW5jZXMpLm1hcCgoc2VxKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb3N0RnJhbWVTZXF1ZW5jZXMuZGVsZXRlKHNlcSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHNlcTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhpcy5zZW5kUGFja2V0KHBrKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuc2VuZEZyYW1lUXVldWUoKTtcbiAgICB9XG5cbiAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2thcmNoaXZlL1Jha05ldC9ibG9iLzFhMTY5ODk1YTkwMGM5ZmM0ODQxYzU1NmUxNjUxNDE4MmI3NWZhZjgvU291cmNlL1JlbGlhYmlsaXR5TGF5ZXIuY3BwI0w2MzVcbiAgICBwdWJsaWMgaGFuZGxlKGJ1ZmZlcjogQnVmZmVyKTogdm9pZCB7XG4gICAgICAgIHRoaXMuYWN0aXZlID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5sYXN0VXBkYXRlID0gRGF0ZS5ub3coKTtcblxuICAgICAgICAvLyBNYXNrIHRoZSBsb3dlciA0IGJpdHMgb2YgdGhlIGhlYWRlclxuICAgICAgICAvLyB0byBnZXQgdGhlIHJhbmdlIG9mIGhlYWRlciB2YWx1ZXNcbiAgICAgICAgY29uc3QgaGFuZGxlciA9IHRoaXMucGFja2V0SGFuZGxlcnNbYnVmZmVyWzBdISAmIDB4ZjBdO1xuICAgICAgICBpZiAoaGFuZGxlcikge1xuICAgICAgICAgICAgaGFuZGxlcihidWZmZXIpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5nZXRMb2dnZXIoKS52ZXJib3NlKGBSZWNlaXZlZCBhbiB1bmtub3duIHBhY2tldCB0eXBlPSR7YnVmZmVyWzBdfWApO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBoYW5kbGVGcmFtZVNldChmcmFtZVNldDogRnJhbWVTZXQpOiB2b2lkIHtcbiAgICAgICAgLy8gVE9ETzogYWRkaXRpb25hbCBzYW5pdHkgY2hlY2tzXG4gICAgICAgIGlmICh0aGlzLnJlY2VpdmVkRnJhbWVTZXF1ZW5jZXMuaGFzKGZyYW1lU2V0LnNlcXVlbmNlTnVtYmVyKSkge1xuICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5nZXRMb2dnZXIoKS5kZWJ1ZyhgRGlzY2FyZGVkIGR1cGxpY2F0ZWQgcGFja2V0IGZyb20gY2xpZW50PSR7dGhpcy5nZXRBZGRyZXNzKCl9YCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBpZiBpdCB3YXMgbWlzc2luZywgcmVtb3ZlIGZyb20gdGhlIHF1ZXVlIGJlY2F1c2Ugd2UgcmVjZWl2ZWQgaXQgbm93XG4gICAgICAgIHRoaXMubG9zdEZyYW1lU2VxdWVuY2VzLmRlbGV0ZShmcmFtZVNldC5zZXF1ZW5jZU51bWJlcik7XG5cbiAgICAgICAgLy8gRm9yIG5vdyBpcyBnb29kIGxpa2UgdGhhdFxuICAgICAgICBpZiAoXG4gICAgICAgICAgICBmcmFtZVNldC5zZXF1ZW5jZU51bWJlciA8IHRoaXMubGFzdElucHV0U2VxdWVuY2VOdW1iZXIgfHxcbiAgICAgICAgICAgIGZyYW1lU2V0LnNlcXVlbmNlTnVtYmVyID09PSB0aGlzLmxhc3RJbnB1dFNlcXVlbmNlTnVtYmVyXG4gICAgICAgICkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gQWRkIHRoZSBmcmFtZSB0byB0aGUgQUNLIHF1ZXVlXG4gICAgICAgIHRoaXMucmVjZWl2ZWRGcmFtZVNlcXVlbmNlcy5hZGQoZnJhbWVTZXQuc2VxdWVuY2VOdW1iZXIpO1xuXG4gICAgICAgIC8vIENoZWNrIGlmIHRoZXJlIGFyZSBtaXNzaW5nIHBhY2tldHMgYmV0d2VlbiB0aGUgcmVjZWl2ZWQgcGFja2V0IGFuZCB0aGUgbGFzdCByZWNlaXZlZCBvbmVcbiAgICAgICAgY29uc3QgZGlmZiA9IGZyYW1lU2V0LnNlcXVlbmNlTnVtYmVyIC0gdGhpcy5sYXN0SW5wdXRTZXF1ZW5jZU51bWJlcjtcblxuICAgICAgICAvLyBDaGVjayBpZiB0aGUgc2VxdWVuY2UgaGFzIGEgaG9sZSBkdWUgdG8gYSBsb3N0IHBhY2tldFxuICAgICAgICBpZiAoZGlmZiAhPT0gMSkge1xuICAgICAgICAgICAgLy8gQXMgaSBzYWlkIGJlZm9yZSwgdGhlcmUgd2Ugc2VhcmNoIGZvciBtaXNzaW5nIHBhY2tldHMgaW4gdGhlIGxpc3Qgb2YgdGhlIHJlY2lldmVkIG9uZXNcbiAgICAgICAgICAgIGZvciAobGV0IGkgPSB0aGlzLmxhc3RJbnB1dFNlcXVlbmNlTnVtYmVyICsgMTsgaSA8IGZyYW1lU2V0LnNlcXVlbmNlTnVtYmVyOyBpKyspIHtcbiAgICAgICAgICAgICAgICAvLyBBZGRpbmcgdGhlIHBhY2tldCBzZXF1ZW5jZSBudW1iZXIgdG8gdGhlIE5BQ0sgcXVldWUgYW5kIHRoZW4gc2VuZGluZyBhIE5BQ0tcbiAgICAgICAgICAgICAgICAvLyB3aWxsIG1ha2UgdGhlIENsaWVudCBzZW5kaW5nIGFnYWluIHRoZSBsb3N0IHBhY2tldFxuICAgICAgICAgICAgICAgIGlmICghdGhpcy5yZWNlaXZlZEZyYW1lU2VxdWVuY2VzLmhhcyhpKSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmxvc3RGcmFtZVNlcXVlbmNlcy5hZGQoaSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gSWYgd2UgcmVjZWl2ZWQgYSBsb3N0IHBhY2tldCB3ZSBzZW50IGluIE5BQ0sgb3IgYSBub3JtYWwgc2VxdWVuY2VkIG9uZVxuICAgICAgICB0aGlzLmxhc3RJbnB1dFNlcXVlbmNlTnVtYmVyID0gZnJhbWVTZXQuc2VxdWVuY2VOdW1iZXI7XG5cbiAgICAgICAgLy8gSGFuZGxlIGVuY2Fwc3VsYXRlZFxuICAgICAgICBmb3IgKGNvbnN0IGZyYW1lIG9mIGZyYW1lU2V0LmZyYW1lcykge1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVGcmFtZShmcmFtZSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGhhbmRsZUFDSyhhY2s6IEFDSyk6IHZvaWQge1xuICAgICAgICAvLyBUT0RPOiBwaW5nIGNhbGN1bGF0aW9uXG5cbiAgICAgICAgZm9yIChjb25zdCBzZXEgb2YgYWNrLnNlcXVlbmNlTnVtYmVycykge1xuICAgICAgICAgICAgdGhpcy5vdXRwdXRCYWNrdXBRdWV1ZS5kZWxldGUoc2VxKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgaGFuZGxlTkFDSyhuYWNrOiBOQUNLKTogdm9pZCB7XG4gICAgICAgIGZvciAoY29uc3Qgc2VxIG9mIG5hY2suc2VxdWVuY2VOdW1iZXJzKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5vdXRwdXRCYWNrdXBRdWV1ZS5oYXMoc2VxKSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGxvc3RGcmFtZXMgPSB0aGlzLm91dHB1dEJhY2t1cFF1ZXVlLmdldChzZXEpITtcbiAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IGxvc3RGcmFtZSBvZiBsb3N0RnJhbWVzKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2VuZEZyYW1lKGxvc3RGcmFtZSwgUmFrTmV0UHJpb3JpdHkuSU1NRURJQVRFKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdGhpcy5vdXRwdXRCYWNrdXBRdWV1ZS5kZWxldGUoc2VxKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgaGFuZGxlRnJhbWUoZnJhbWU6IEZyYW1lKTogdm9pZCB7XG4gICAgICAgIGlmIChmcmFtZS5pc0ZyYWdtZW50ZWQoKSkge1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVGcmFnbWVudChmcmFtZSk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBvcmRlckNoYW5uZWwgPSBmcmFtZS5vcmRlckNoYW5uZWwhO1xuICAgICAgICBjb25zdCBvcmRlckluZGV4ID0gZnJhbWUub3JkZXJJbmRleCE7XG4gICAgICAgIGNvbnN0IHNlcXVlbmNlSW5kZXggPSBmcmFtZS5zZXF1ZW5jZUluZGV4ITtcblxuICAgICAgICBpZiAoZnJhbWUuaXNTZXF1ZW5jZWQoKSkge1xuICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgIHNlcXVlbmNlSW5kZXggPCB0aGlzLmlucHV0SGlnaGVzdFNlcXVlbmNlSW5kZXhbb3JkZXJDaGFubmVsXSEgfHxcbiAgICAgICAgICAgICAgICBvcmRlckluZGV4IDwgdGhpcy5pbnB1dE9yZGVySW5kZXhbb3JkZXJDaGFubmVsXSFcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgIC8vIFNlcXVlbmNlZCBwYWNrZXQgaXMgdG9vIG9sZCwgZGlzY2FyZCBpdFxuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdGhpcy5pbnB1dEhpZ2hlc3RTZXF1ZW5jZUluZGV4W29yZGVyQ2hhbm5lbF0gPSBzZXF1ZW5jZUluZGV4ICsgMTtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlUGFja2V0KGZyYW1lKTtcbiAgICAgICAgfSBlbHNlIGlmIChmcmFtZS5pc09yZGVyZWQoKSkge1xuICAgICAgICAgICAgaWYgKG9yZGVySW5kZXggPT09IHRoaXMuaW5wdXRPcmRlckluZGV4W29yZGVyQ2hhbm5lbF0pIHtcbiAgICAgICAgICAgICAgICB0aGlzLmlucHV0SGlnaGVzdFNlcXVlbmNlSW5kZXhbb3JkZXJDaGFubmVsXSA9IDA7XG4gICAgICAgICAgICAgICAgdGhpcy5pbnB1dE9yZGVySW5kZXhbb3JkZXJDaGFubmVsXSA9IG9yZGVySW5kZXggKyAxO1xuXG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVQYWNrZXQoZnJhbWUpO1xuICAgICAgICAgICAgICAgIGxldCBpID0gdGhpcy5pbnB1dE9yZGVySW5kZXhbb3JkZXJDaGFubmVsXSE7XG4gICAgICAgICAgICAgICAgY29uc3Qgb3V0T2ZPcmRlclF1ZXVlID0gdGhpcy5pbnB1dE9yZGVyaW5nUXVldWUuZ2V0KG9yZGVyQ2hhbm5lbCkhO1xuICAgICAgICAgICAgICAgIGZvciAoOyBvdXRPZk9yZGVyUXVldWUuaGFzKGkpOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVQYWNrZXQob3V0T2ZPcmRlclF1ZXVlLmdldChpKSEpO1xuICAgICAgICAgICAgICAgICAgICBvdXRPZk9yZGVyUXVldWUuZGVsZXRlKGkpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIFNldCB0aGUgdXBkYXRlZCBxdWV1ZVxuICAgICAgICAgICAgICAgIHRoaXMuaW5wdXRPcmRlcmluZ1F1ZXVlLnNldChvcmRlckNoYW5uZWwsIG91dE9mT3JkZXJRdWV1ZSk7XG4gICAgICAgICAgICAgICAgdGhpcy5pbnB1dE9yZGVySW5kZXhbb3JkZXJDaGFubmVsXSA9IGk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKG9yZGVySW5kZXggPiB0aGlzLmlucHV0T3JkZXJJbmRleFtvcmRlckNoYW5uZWxdISkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHVub3JkZXJlZCA9IHRoaXMuaW5wdXRPcmRlcmluZ1F1ZXVlLmdldChvcmRlckNoYW5uZWwpITtcbiAgICAgICAgICAgICAgICB1bm9yZGVyZWQuc2V0KG9yZGVySW5kZXgsIGZyYW1lKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlUGFja2V0KGZyYW1lKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBzZW5kRnJhbWUoZnJhbWU6IEZyYW1lLCBmbGFncyA9IFJha05ldFByaW9yaXR5Lk5PUk1BTCk6IHZvaWQge1xuICAgICAgICBhc3NlcnQodHlwZW9mIGZyYW1lLm9yZGVyQ2hhbm5lbCA9PT0gJ251bWJlcicsICdGcmFtZSBPcmRlckNoYW5uZWwgY2Fubm90IGJlIG51bGwnKTtcblxuICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2thcmNoaXZlL1Jha05ldC9ibG9iLzFhMTY5ODk1YTkwMGM5ZmM0ODQxYzU1NmUxNjUxNDE4MmI3NWZhZjgvU291cmNlL1JlbGlhYmlsaXR5TGF5ZXIuY3BwI0wxNjI1XG4gICAgICAgIGlmIChmcmFtZS5pc1NlcXVlbmNlZCgpKSB7XG4gICAgICAgICAgICAvLyBTZXF1ZW5jZWQgcGFja2V0cyBkb24ndCBpbmNyZWFzZSB0aGUgb3JkZXJlZCBjaGFubmVsIGluZGV4XG4gICAgICAgICAgICBmcmFtZS5vcmRlckluZGV4ID0gdGhpcy5vdXRwdXRPcmRlckluZGV4W2ZyYW1lLm9yZGVyQ2hhbm5lbF0hO1xuICAgICAgICAgICAgZnJhbWUuc2VxdWVuY2VJbmRleCA9IHRoaXMub3V0cHV0U2VxdWVuY2VJbmRleFtmcmFtZS5vcmRlckNoYW5uZWxdISsrO1xuICAgICAgICB9IGVsc2UgaWYgKGZyYW1lLmlzT3JkZXJlZEV4Y2x1c2l2ZSgpKSB7XG4gICAgICAgICAgICAvLyBpbXBsaWVzIHNlcXVlbmNlZCwgYnV0IHdlIGhhdmUgdG8gZGlzdGluY3QgdGhlbVxuICAgICAgICAgICAgZnJhbWUub3JkZXJJbmRleCA9IHRoaXMub3V0cHV0T3JkZXJJbmRleFtmcmFtZS5vcmRlckNoYW5uZWxdISsrO1xuICAgICAgICAgICAgdGhpcy5vdXRwdXRTZXF1ZW5jZUluZGV4W2ZyYW1lLm9yZGVyQ2hhbm5lbF0gPSAwO1xuICAgICAgICB9XG5cbiAgICAgICAgZnJhbWUucmVsaWFibGVJbmRleCA9IHRoaXMub3V0cHV0UmVsaWFibGVJbmRleCsrO1xuXG4gICAgICAgIC8vIFNwbGl0IHBhY2tldCBpZiBiaWdnZXIgdGhhbiBNVFUgc2l6ZVxuICAgICAgICBjb25zdCBtYXhTaXplID0gdGhpcy5nZXRNVFUoKSAtIERBVEFHUkFNX0hFQURFUl9CWVRFX0xFTkdUSCAtIE1BWF9GUkFNRV9CWVRFX0xFTkdUSDtcbiAgICAgICAgaWYgKGZyYW1lLmNvbnRlbnQuYnl0ZUxlbmd0aCA+IG1heFNpemUpIHtcbiAgICAgICAgICAgIC8vIElmIHdlIHVzZSB0aGUgZnJhbWUgYXMgcmVmZXJlbmNlLCB3ZSBoYXZlIHRvIGNvcHkgc29tZXdoZXJlXG4gICAgICAgICAgICAvLyB0aGUgb3JpZ2luYWwgYnVmZmVyLiBUaGVuIHdlIHdpbGwgcG9pbnQgdG8gdGhpcyBidWZmZXIgY29udGVudFxuICAgICAgICAgICAgY29uc3QgYnVmZmVyID0gQnVmZmVyLmZyb20oZnJhbWUuY29udGVudCk7XG4gICAgICAgICAgICBjb25zdCBmcmFnbWVudElkID0gdGhpcy5vdXRwdXRGcmFnbWVudEluZGV4KysgJSA2NTUzNjtcbiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYnVmZmVyLmJ5dGVMZW5ndGg7IGkgKz0gbWF4U2l6ZSkge1xuICAgICAgICAgICAgICAgIC8vIExpa2UgdGhlIG9yaWdpbmFsIHJha25ldCwgd2UgbGlrZSB0byB1c2UgYSBwb2ludGVyIHRvIHRoZSBvcmlnaW5hbFxuICAgICAgICAgICAgICAgIC8vIGZyYW1lLCB3ZSBkb24ndCByZWFsbHkgY2FyZSBhYm91dCBzaWRlIGVmZmVjdHMgaW4gdGhpcyBjYXNlLlxuICAgICAgICAgICAgICAgIC8vIFJha05ldCB3aWxsIGFsbG9jYXRlIHRoZSB3aG9sZSBzdHJjdHVyZSBjb250YWluaW5nIHNwbGl0cyxcbiAgICAgICAgICAgICAgICAvLyBidXQgaSB0aGluayBjYWNoaW5nIGp1c3QgdGhlIGJ1ZmZlciBpcyBlbm91Z2guXG4gICAgICAgICAgICAgICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2ZhY2Vib29rYXJjaGl2ZS9SYWtOZXQvYmxvYi8xYTE2OTg5NWE5MDBjOWZjNDg0MWM1NTZlMTY1MTQxODJiNzVmYWY4L1NvdXJjZS9SZWxpYWJpbGl0eUxheWVyLmNwcCNMMjk2M1xuXG4gICAgICAgICAgICAgICAgLy8gU2tpcCB0aGUgZmlyc3QgaW5kZXggYXMgaXQncyBhbHJlYWR5IGluY3JlYXNlZCBieSBpdHNlbGYuXG4gICAgICAgICAgICAgICAgaWYgKGkgIT09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgZnJhbWUucmVsaWFibGVJbmRleCA9IHRoaXMub3V0cHV0UmVsaWFibGVJbmRleCsrO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGZyYW1lLmNvbnRlbnQgPSBidWZmZXIuc2xpY2UoaSwgaSArIG1heFNpemUpO1xuICAgICAgICAgICAgICAgIGZyYW1lLmZyYWdtZW50SW5kZXggPSBpIC8gbWF4U2l6ZTtcbiAgICAgICAgICAgICAgICBmcmFtZS5mcmFnbWVudElkID0gZnJhZ21lbnRJZDtcbiAgICAgICAgICAgICAgICBmcmFtZS5mcmFnbWVudFNpemUgPSBNYXRoLmNlaWwoYnVmZmVyLmJ5dGVMZW5ndGggLyBtYXhTaXplKTtcbiAgICAgICAgICAgICAgICB0aGlzLmFkZEZyYW1lVG9RdWV1ZShmcmFtZSwgZmxhZ3MgfCBSYWtOZXRQcmlvcml0eS5JTU1FRElBVEUpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5hZGRGcmFtZVRvUXVldWUoZnJhbWUsIGZsYWdzKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgYWRkRnJhbWVUb1F1ZXVlKGZyYW1lOiBGcmFtZSwgcHJpb3JpdHkgPSBSYWtOZXRQcmlvcml0eS5OT1JNQUwpOiB2b2lkIHtcbiAgICAgICAgbGV0IGxlbmd0aCA9IDQ7IC8vIGRhdGFncmFtIGhlYWRlciBzaXplXG4gICAgICAgIGZvciAoY29uc3QgcXVldWVkRnJhbWUgb2YgdGhpcy5vdXRwdXRGcmFtZVF1ZXVlLmZyYW1lcykge1xuICAgICAgICAgICAgbGVuZ3RoICs9IHF1ZXVlZEZyYW1lLmdldEJ5dGVMZW5ndGgoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChsZW5ndGggKyBmcmFtZS5nZXRCeXRlTGVuZ3RoKCkgPiB0aGlzLm10dVNpemUgLSAzNikge1xuICAgICAgICAgICAgdGhpcy5zZW5kRnJhbWVRdWV1ZSgpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5vdXRwdXRGcmFtZVF1ZXVlLmZyYW1lcy5wdXNoKGZyYW1lKTtcblxuICAgICAgICBpZiAocHJpb3JpdHkgPT09IFJha05ldFByaW9yaXR5LklNTUVESUFURSkge1xuICAgICAgICAgICAgdGhpcy5zZW5kRnJhbWVRdWV1ZSgpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBoYW5kbGVQYWNrZXQocGFja2V0OiBGcmFtZSk6IHZvaWQge1xuICAgICAgICBjb25zdCBpZCA9IHBhY2tldC5jb250ZW50WzBdO1xuXG4gICAgICAgIGlmICh0aGlzLnN0YXRlID09PSBTZXNzaW9uU3RhdHVzLkNPTk5FQ1RJTkcpIHtcbiAgICAgICAgICAgIGlmIChpZCA9PT0gTWVzc2FnZUlkZW50aWZpZXJzLkNPTk5FQ1RJT05fUkVRVUVTVCkge1xuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlQ29ubmVjdGlvblJlcXVlc3QocGFja2V0LmNvbnRlbnQpLnRoZW4oXG4gICAgICAgICAgICAgICAgICAgIChlbmNhcHN1bGF0ZWQpID0+IHRoaXMuc2VuZEZyYW1lKGVuY2Fwc3VsYXRlZCwgUmFrTmV0UHJpb3JpdHkuSU1NRURJQVRFKSxcbiAgICAgICAgICAgICAgICAgICAgKCkgPT4ge31cbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChpZCA9PT0gTWVzc2FnZUlkZW50aWZpZXJzLk5FV19JTkNPTUlOR19DT05ORUNUSU9OKSB7XG4gICAgICAgICAgICAgICAgLy8gVE9ETzogb25saW5lIG1vZGVcbiAgICAgICAgICAgICAgICB0aGlzLnN0YXRlID0gU2Vzc2lvblN0YXR1cy5DT05ORUNURUQ7XG4gICAgICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5lbWl0KCdvcGVuQ29ubmVjdGlvbicsIHRoaXMpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKGlkID09PSBNZXNzYWdlSWRlbnRpZmllcnMuRElTQ09OTkVDVElPTl9OT1RJRklDQVRJT04pIHtcbiAgICAgICAgICAgIHRoaXMuZGlzY29ubmVjdCgpO1xuICAgICAgICB9IGVsc2UgaWYgKGlkID09PSBNZXNzYWdlSWRlbnRpZmllcnMuQ09OTkVDVEVEX1BJTkcpIHtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlQ29ubmVjdGVkUGluZyhwYWNrZXQuY29udGVudCkudGhlbihcbiAgICAgICAgICAgICAgICAoZW5jYXBzdWxhdGVkKSA9PiB0aGlzLnNlbmRGcmFtZShlbmNhcHN1bGF0ZWQpLFxuICAgICAgICAgICAgICAgICgpID0+IHt9XG4gICAgICAgICAgICApO1xuICAgICAgICB9IGVsc2UgaWYgKHRoaXMuc3RhdGUgPT09IFNlc3Npb25TdGF0dXMuQ09OTkVDVEVEKSB7XG4gICAgICAgICAgICB0aGlzLmxpc3RlbmVyLmVtaXQoJ2VuY2Fwc3VsYXRlZCcsIHBhY2tldCwgdGhpcy5nZXRBZGRyZXNzKCkpOyAvLyBUbyBmaXQgaW4gc29mdHdhcmUgbmVlZHMgbGF0ZXJcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBoYW5kbGVDb25uZWN0aW9uUmVxdWVzdChidWZmZXI6IEJ1ZmZlcik6IFByb21pc2U8RnJhbWU+IHtcbiAgICAgICAgY29uc3QgZGF0YVBhY2tldCA9IG5ldyBDb25uZWN0aW9uUmVxdWVzdChidWZmZXIpO1xuICAgICAgICBkYXRhUGFja2V0LmRlY29kZSgpO1xuXG4gICAgICAgIGNvbnN0IHBrID0gbmV3IENvbm5lY3Rpb25SZXF1ZXN0QWNjZXB0ZWQoKTtcbiAgICAgICAgcGsuY2xpZW50QWRkcmVzcyA9IHRoaXMuZ2V0QWRkcmVzcygpO1xuICAgICAgICBway5yZXF1ZXN0VGltZXN0YW1wID0gZGF0YVBhY2tldC5yZXF1ZXN0VGltZXN0YW1wO1xuICAgICAgICBway5hY2NlcHRlZFRpbWVzdGFtcCA9IEJpZ0ludChEYXRlLm5vdygpKTtcbiAgICAgICAgcGsuZW5jb2RlKCk7XG5cbiAgICAgICAgY29uc3Qgc2VuZFBhY2tldCA9IG5ldyBGcmFtZSgpO1xuICAgICAgICBzZW5kUGFja2V0LnJlbGlhYmlsaXR5ID0gRnJhbWVSZWxpYWJpbGl0eS5VTlJFTElBQkxFO1xuICAgICAgICBzZW5kUGFja2V0Lm9yZGVyQ2hhbm5lbCA9IDA7XG4gICAgICAgIHNlbmRQYWNrZXQuY29udGVudCA9IHBrLmdldEJ1ZmZlcigpO1xuXG4gICAgICAgIHJldHVybiBzZW5kUGFja2V0O1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBoYW5kbGVDb25uZWN0ZWRQaW5nKGJ1ZmZlcjogQnVmZmVyKTogUHJvbWlzZTxGcmFtZT4ge1xuICAgICAgICBjb25zdCBkYXRhUGFja2V0ID0gbmV3IENvbm5lY3RlZFBpbmcoYnVmZmVyKTtcbiAgICAgICAgZGF0YVBhY2tldC5kZWNvZGUoKTtcblxuICAgICAgICBjb25zdCBwayA9IG5ldyBDb25uZWN0ZWRQb25nKCk7XG4gICAgICAgIHBrLmNsaWVudFRpbWVzdGFtcCA9IGRhdGFQYWNrZXQuY2xpZW50VGltZXN0YW1wO1xuICAgICAgICBway5zZXJ2ZXJUaW1lc3RhbXAgPSBCaWdJbnQoRGF0ZS5ub3coKSk7XG4gICAgICAgIHBrLmVuY29kZSgpO1xuXG4gICAgICAgIGNvbnN0IHNlbmRQYWNrZXQgPSBuZXcgRnJhbWUoKTtcbiAgICAgICAgc2VuZFBhY2tldC5yZWxpYWJpbGl0eSA9IEZyYW1lUmVsaWFiaWxpdHkuVU5SRUxJQUJMRTtcbiAgICAgICAgc2VuZFBhY2tldC5vcmRlckNoYW5uZWwgPSAwO1xuICAgICAgICBzZW5kUGFja2V0LmNvbnRlbnQgPSBway5nZXRCdWZmZXIoKTtcblxuICAgICAgICByZXR1cm4gc2VuZFBhY2tldDtcbiAgICB9XG5cbiAgICBwdWJsaWMgaGFuZGxlRnJhZ21lbnQoZnJhbWU6IEZyYW1lKTogdm9pZCB7XG4gICAgICAgIGlmICghdGhpcy5mcmFnbWVudHNRdWV1ZS5oYXMoZnJhbWUuZnJhZ21lbnRJZCkpIHtcbiAgICAgICAgICAgIHRoaXMuZnJhZ21lbnRzUXVldWUuc2V0KGZyYW1lLmZyYWdtZW50SWQsIG5ldyBNYXAoW1tmcmFtZS5mcmFnbWVudEluZGV4LCBmcmFtZV1dKSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zdCB2YWx1ZSA9IHRoaXMuZnJhZ21lbnRzUXVldWUuZ2V0KGZyYW1lLmZyYWdtZW50SWQpITtcbiAgICAgICAgICAgIHZhbHVlLnNldChmcmFtZS5mcmFnbWVudEluZGV4LCBmcmFtZSk7XG5cbiAgICAgICAgICAgIC8vIElmIHdlIGhhdmUgYWxsIHBpZWNlcywgcHV0IHRoZW0gdG9nZXRoZXJcbiAgICAgICAgICAgIGlmICh2YWx1ZS5zaXplID09PSBmcmFtZS5mcmFnbWVudFNpemUpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBzdHJlYW0gPSBuZXcgQmluYXJ5U3RyZWFtKCk7XG4gICAgICAgICAgICAgICAgLy8gRW5zdXJlIHRoZSBjb3JyZWN0bmVzcyBvZiB0aGUgYnVmZmVyIG9yZGVyc1xuICAgICAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWUuc2l6ZTsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHNwbGl0UGFja2V0ID0gdmFsdWUuZ2V0KGkpITtcbiAgICAgICAgICAgICAgICAgICAgc3RyZWFtLndyaXRlKHNwbGl0UGFja2V0LmNvbnRlbnQpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGNvbnN0IGFzc2VtYmxlZEZyYW1lID0gbmV3IEZyYW1lKCk7XG4gICAgICAgICAgICAgICAgYXNzZW1ibGVkRnJhbWUuY29udGVudCA9IHN0cmVhbS5nZXRCdWZmZXIoKTtcblxuICAgICAgICAgICAgICAgIGFzc2VtYmxlZEZyYW1lLnJlbGlhYmlsaXR5ID0gZnJhbWUucmVsaWFiaWxpdHk7XG4gICAgICAgICAgICAgICAgYXNzZW1ibGVkRnJhbWUucmVsaWFibGVJbmRleCA9IGZyYW1lLnJlbGlhYmxlSW5kZXg7XG4gICAgICAgICAgICAgICAgYXNzZW1ibGVkRnJhbWUuc2VxdWVuY2VJbmRleCA9IGZyYW1lLnNlcXVlbmNlSW5kZXg7XG4gICAgICAgICAgICAgICAgYXNzZW1ibGVkRnJhbWUub3JkZXJJbmRleCA9IGZyYW1lLm9yZGVySW5kZXg7XG4gICAgICAgICAgICAgICAgYXNzZW1ibGVkRnJhbWUub3JkZXJDaGFubmVsID0gZnJhbWUub3JkZXJDaGFubmVsO1xuXG4gICAgICAgICAgICAgICAgdGhpcy5mcmFnbWVudHNRdWV1ZS5kZWxldGUoZnJhbWUuZnJhZ21lbnRJZCk7XG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVGcmFtZShhc3NlbWJsZWRGcmFtZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgc2VuZEZyYW1lUXVldWUoKTogdm9pZCB7XG4gICAgICAgIGlmICh0aGlzLm91dHB1dEZyYW1lUXVldWUuZnJhbWVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIHRoaXMub3V0cHV0RnJhbWVRdWV1ZS5zZXF1ZW5jZU51bWJlciA9IHRoaXMub3V0cHV0U2VxdWVuY2VOdW1iZXIrKztcbiAgICAgICAgICAgIHRoaXMuc2VuZEZyYW1lU2V0KHRoaXMub3V0cHV0RnJhbWVRdWV1ZSk7XG4gICAgICAgICAgICB0aGlzLm91dHB1dEZyYW1lUXVldWUgPSBuZXcgRnJhbWVTZXQoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgc2VuZEZyYW1lU2V0KGZyYW1lU2V0OiBGcmFtZVNldCk6IHZvaWQge1xuICAgICAgICB0aGlzLnNlbmRQYWNrZXQoZnJhbWVTZXQpO1xuICAgICAgICB0aGlzLm91dHB1dEJhY2t1cFF1ZXVlLnNldChcbiAgICAgICAgICAgIGZyYW1lU2V0LnNlcXVlbmNlTnVtYmVyLFxuICAgICAgICAgICAgZnJhbWVTZXQuZnJhbWVzLmZpbHRlcigoZnJhbWUpID0+IGZyYW1lLmlzUmVsaWFibGUoKSlcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNlbmRQYWNrZXQocGFja2V0OiBQYWNrZXQpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5saXN0ZW5lci5zZW5kUGFja2V0KHBhY2tldCwgdGhpcy5yaW5mbyk7XG4gICAgfVxuXG4gICAgcHVibGljIGNsb3NlKCk6IHZvaWQge1xuICAgICAgICBjb25zdCBzdHJlYW0gPSBuZXcgQmluYXJ5U3RyZWFtKEJ1ZmZlci5mcm9tKCdcXHUwMDAwXFx1MDAwMFxcdTAwMDhcXHUwMDE1JywgJ2JpbmFyeScpKTtcbiAgICAgICAgdGhpcy5hZGRGcmFtZVRvUXVldWUobmV3IEZyYW1lKCkuZnJvbUJpbmFyeShzdHJlYW0pLCBSYWtOZXRQcmlvcml0eS5JTU1FRElBVEUpOyAvLyBDbGllbnQgZGlzY2NvbmVjdCBwYWNrZXQgMHgxNVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEtpY2sgYSBjbGllbnRcbiAgICAgKiBAcGFyYW0gcmVhc29uIC0gdGhlIHJlYXNvbiBtZXNzYWdlLCBvcHRpb25hbFxuICAgICAqL1xuICAgIHB1YmxpYyBkaXNjb25uZWN0KHJlYXNvbiA9ICdjbGllbnQgZGlzY29ubmVjdCcpOiB2b2lkIHtcbiAgICAgICAgLy8gVE9ETzogcmV3cml0ZSwgd29ya3MgYnV0IGNhbiBiZSBpbXByb3ZlZFxuICAgICAgICB0aGlzLmNsb3NlKCk7XG4gICAgICAgIC8vIFNlbmQgZGlzY29ubmVjdCBBQ0tcbiAgICAgICAgdGhpcy5zdGF0ZSA9IFNlc3Npb25TdGF0dXMuRElTQ09OTkVDVEVEO1xuICAgICAgICB0aGlzLnVwZGF0ZShEYXRlLm5vdygpKTtcbiAgICAgICAgdGhpcy5saXN0ZW5lci5yZW1vdmVTZXNzaW9uKHRoaXMsIHJlYXNvbik7XG4gICAgfVxuXG4gICAgcHVibGljIGdldFN0YXRlKCk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiB0aGlzLnN0YXRlO1xuICAgIH1cblxuICAgIHB1YmxpYyBpc0FjdGl2ZSgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYWN0aXZlO1xuICAgIH1cblxuICAgIHB1YmxpYyBpc0Rpc2Nvbm5lY3RlZCgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc3RhdGUgPT09IFNlc3Npb25TdGF0dXMuRElTQ09OTkVDVEVEO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXRMaXN0ZW5lcigpOiBSYWtOZXRMaXN0ZW5lciB7XG4gICAgICAgIHJldHVybiB0aGlzLmxpc3RlbmVyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIG1heG1pdW0gdHJhbnNmZXIgdW5pdFxuICAgICAqIGZvciB0aGlzIGNvbm5lY3Rpb24uXG4gICAgICogQHJldHVybnMge251bWJlcn0gdGhlIFVEUCBhZGp1c3RlZC5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0TVRVKCk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiB0aGlzLm10dVNpemUgLSBVRFBfSEVBREVSX1NJWkU7XG4gICAgfVxuXG4gICAgcHVibGljIGdldEFkZHJlc3MoKTogSW5ldEFkZHJlc3Mge1xuICAgICAgICByZXR1cm4gbmV3IEluZXRBZGRyZXNzKHRoaXMucmluZm8uYWRkcmVzcywgdGhpcy5yaW5mby5wb3J0LCA0KTtcbiAgICB9XG59XG4iXSwibmFtZXMiOlsiUmFrTmV0UHJpb3JpdHkiLCJTZXNzaW9uU3RhdHVzIiwiTUFYX0NIQU5ORUxTIiwiRnJhbWVTZXQiLCJQYWNrZXRQb29sIiwiTWVzc2FnZUlkZW50aWZpZXJzIiwiQml0RmxhZ3MiLCJBQ0siLCJOQUNLIiwiYXNzZXJ0IiwiREFUQUdSQU1fSEVBREVSX0JZVEVfTEVOR1RIIiwiTUFYX0ZSQU1FX0JZVEVfTEVOR1RIIiwiQ29ubmVjdGlvblJlcXVlc3QiLCJDb25uZWN0aW9uUmVxdWVzdEFjY2VwdGVkIiwiRnJhbWUiLCJGcmFtZVJlbGlhYmlsaXR5IiwiQ29ubmVjdGVkUGluZyIsIkNvbm5lY3RlZFBvbmciLCJCaW5hcnlTdHJlYW0iLCJVRFBfSEVBREVSX1NJWkUiLCJJbmV0QWRkcmVzcyJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFxQlksSUFBQSxjQUFBLHFCQUFBQSxlQUFMLEtBQUE7QUFDSCxFQUFBQSxlQUFBLENBQUEsZUFBQSxDQUFBLFFBQUEsQ0FBQSxHQUFBLENBQUEsQ0FBQSxHQUFBLFFBQUE7QUFDQSxFQUFBQSxlQUFBLENBQUEsZUFBQSxDQUFBLFdBQUEsQ0FBQSxHQUFBLENBQUEsQ0FBQSxHQUFBLFdBQUE7QUFGUSxFQUFBQSxPQUFBQSxlQUFBQTtBQUFBLENBQUEsRUFBQSxjQUFBLElBQUEsRUFBQTtBQUtBLElBQUEsYUFBQSxxQkFBQUMsY0FBTCxLQUFBO0FBQ0gsRUFBQUEsY0FBQSxDQUFBLGNBQUEsQ0FBQSxZQUFBLENBQUEsR0FBQSxDQUFBLENBQUEsR0FBQSxZQUFBO0FBQ0EsRUFBQUEsY0FBQSxDQUFBLGNBQUEsQ0FBQSxXQUFBLENBQUEsR0FBQSxDQUFBLENBQUEsR0FBQSxXQUFBO0FBQ0EsRUFBQUEsY0FBQSxDQUFBLGNBQUEsQ0FBQSxlQUFBLENBQUEsR0FBQSxDQUFBLENBQUEsR0FBQSxlQUFBO0FBQ0EsRUFBQUEsY0FBQSxDQUFBLGNBQUEsQ0FBQSxjQUFBLENBQUEsR0FBQSxDQUFBLENBQUEsR0FBQSxjQUFBO0FBSlEsRUFBQUEsT0FBQUEsY0FBQUE7QUFBQSxDQUFBLEVBQUEsYUFBQSxJQUFBLEVBQUE7QUFPWixNQUFxQixPQUFRLENBQUE7QUFBQSxFQThCbEIsV0FDYyxDQUFBLFFBQUEsRUFDQSxPQUNELEVBQUEsS0FBQSxFQUNBLElBQ2xCLEVBQUE7QUFKbUIsSUFBQSxJQUFBLENBQUEsUUFBQSxHQUFBLFFBQUE7QUFDQSxJQUFBLElBQUEsQ0FBQSxPQUFBLEdBQUEsT0FBQTtBQUNELElBQUEsSUFBQSxDQUFBLEtBQUEsR0FBQSxLQUFBO0FBQ0EsSUFBQSxJQUFBLENBQUEsSUFBQSxHQUFBLElBQUE7QUFFaEIsSUFBSyxJQUFBLENBQUEsVUFBQSxHQUFhLEtBQUssR0FBSSxFQUFBO0FBRTNCLElBQUEsSUFBQSxDQUFLLG1CQUFtQixJQUFJLEtBQUEsQ0FBTUMsc0JBQVksQ0FBQSxDQUFFLEtBQUssQ0FBQyxDQUFBO0FBQ3RELElBQUEsSUFBQSxDQUFLLHNCQUFzQixJQUFJLEtBQUEsQ0FBTUEsc0JBQVksQ0FBQSxDQUFFLEtBQUssQ0FBQyxDQUFBO0FBRXpELElBQUEsSUFBQSxDQUFLLGtCQUFrQixJQUFJLEtBQUEsQ0FBTUEsc0JBQVksQ0FBQSxDQUFFLEtBQUssQ0FBQyxDQUFBO0FBQ3JELElBQUEsS0FBQSxJQUFTLENBQUksR0FBQSxDQUFBLEVBQUcsQ0FBSSxHQUFBQSxzQkFBQSxFQUFjLENBQUssRUFBQSxFQUFBO0FBQ25DLE1BQUEsSUFBQSxDQUFLLGtCQUFtQixDQUFBLEdBQUEsQ0FBSSxDQUFHLGtCQUFBLElBQUksS0FBSyxDQUFBO0FBQUE7QUFHNUMsSUFBQSxJQUFBLENBQUssNEJBQTRCLElBQUksS0FBQSxDQUFNQSxzQkFBWSxDQUFBLENBQUUsS0FBSyxDQUFDLENBQUE7QUFBQTtBQUNuRSxFQTlDUSxLQUFRLEdBQUEsQ0FBQTtBQUFBLEVBRVIsZ0JBQUEsR0FBbUIsSUFBSUMseUJBQVMsRUFBQTtBQUFBLEVBQ2hDLG9CQUF1QixHQUFBLENBQUE7QUFBQTtBQUFBLEVBQ3ZCLG1CQUFzQixHQUFBLENBQUE7QUFBQSxFQUNiLGlCQUFBLHVCQUE4QyxHQUFJLEVBQUE7QUFBQSxFQUVsRCxnQkFBQTtBQUFBLEVBQ0EsbUJBQUE7QUFBQSxFQUVULHNCQUFBLHVCQUEwQyxHQUFJLEVBQUE7QUFBQSxFQUM5QyxrQkFBQSx1QkFBc0MsR0FBSSxFQUFBO0FBQUE7QUFBQSxFQUdqQyxjQUFBLHVCQUFzRCxHQUFJLEVBQUE7QUFBQSxFQUNuRSxtQkFBc0IsR0FBQSxDQUFBO0FBQUEsRUFFdEIsdUJBQTBCLEdBQUEsRUFBQTtBQUFBLEVBQ2pCLHlCQUFBO0FBQUEsRUFDQSxlQUFBO0FBQUEsRUFDVCxrQkFBQSx1QkFBMEQsR0FBSSxFQUFBO0FBQUE7QUFBQSxFQUc5RCxVQUFBLEdBQXFCLEtBQUssR0FBSSxFQUFBO0FBQUEsRUFDOUIsTUFBUyxHQUFBLElBQUE7QUFBQTtBQUFBLEVBR0EsVUFBQSxHQUFhLElBQUlDLDJCQUFXLEVBQUE7QUFBQTtBQUFBLEVBc0I1QixjQUEyRCxHQUFBO0FBQUE7QUFBQSxJQUV4RSxDQUFDQyw4Q0FBQSxDQUFtQixrQkFBa0IsR0FBRyxDQUFDLE1BQW1CLEtBQUE7QUFDekQsTUFBTSxNQUFBLEdBQUEsR0FBTSxJQUFLLENBQUEsVUFBQSxDQUFXLGNBQWUsRUFBQTtBQUMzQyxNQUFDLElBQVksTUFBUyxHQUFBLE1BQUE7QUFDdEIsTUFBQSxHQUFBLENBQUksTUFBTyxFQUFBO0FBQ1gsTUFBQSxJQUFBLENBQUssVUFBVSxHQUFHLENBQUE7QUFBQSxLQUN0QjtBQUFBLElBQ0EsQ0FBQ0EsOENBQUEsQ0FBbUIsbUJBQW1CLEdBQUcsQ0FBQyxNQUFtQixLQUFBO0FBQzFELE1BQU0sTUFBQSxJQUFBLEdBQU8sSUFBSyxDQUFBLFVBQUEsQ0FBVyxlQUFnQixFQUFBO0FBQzdDLE1BQUMsS0FBYSxNQUFTLEdBQUEsTUFBQTtBQUN2QixNQUFBLElBQUEsQ0FBSyxNQUFPLEVBQUE7QUFDWixNQUFBLElBQUEsQ0FBSyxXQUFXLElBQUksQ0FBQTtBQUFBLEtBQ3hCO0FBQUEsSUFDQSxDQUFDQywwQkFBQSxDQUFTLEtBQUssR0FBRyxDQUFDLE1BQW1CLEtBQUE7QUFDbEMsTUFBTSxNQUFBLFFBQUEsR0FBVyxJQUFLLENBQUEsVUFBQSxDQUFXLG1CQUFvQixFQUFBO0FBQ3JELE1BQUMsU0FBaUIsTUFBUyxHQUFBLE1BQUE7QUFDM0IsTUFBQSxRQUFBLENBQVMsTUFBTyxFQUFBO0FBQ2hCLE1BQUEsSUFBQSxDQUFLLGVBQWUsUUFBUSxDQUFBO0FBQUE7QUFDaEMsR0FDSjtBQUFBLEVBRU8sT0FBTyxTQUF5QixFQUFBO0FBQ25DLElBQUksSUFBQSxDQUFDLElBQUssQ0FBQSxRQUFBLEVBQWMsSUFBQSxDQUFDLElBQUssQ0FBQSxjQUFBLEVBQW9CLElBQUEsSUFBQSxDQUFLLFVBQWEsR0FBQSxHQUFBLEdBQVMsU0FBVyxFQUFBO0FBQ3BGLE1BQUEsSUFBQSxDQUFLLFdBQVcsU0FBUyxDQUFBO0FBQ3pCLE1BQUE7QUFBQTtBQUdKLElBQUEsSUFBQSxDQUFLLE1BQVMsR0FBQSxLQUFBO0FBR2QsSUFBSSxJQUFBLElBQUEsQ0FBSyxzQkFBdUIsQ0FBQSxJQUFBLEdBQU8sQ0FBRyxFQUFBO0FBQ3RDLE1BQU0sTUFBQSxHQUFBLEdBQU0sSUFBSUMsb0JBQUksRUFBQTtBQUNwQixNQUFJLEdBQUEsQ0FBQSxlQUFBLEdBQWtCLE1BQU0sSUFBSyxDQUFBLElBQUEsQ0FBSyxzQkFBc0IsQ0FBRSxDQUFBLEdBQUEsQ0FBSSxDQUFDLEdBQVEsS0FBQTtBQUN2RSxRQUFLLElBQUEsQ0FBQSxzQkFBQSxDQUF1QixPQUFPLEdBQUcsQ0FBQTtBQUN0QyxRQUFPLE9BQUEsR0FBQTtBQUFBLE9BQ1YsQ0FBQTtBQUNELE1BQUEsSUFBQSxDQUFLLFdBQVcsR0FBRyxDQUFBO0FBQUE7QUFHdkIsSUFBSSxJQUFBLElBQUEsQ0FBSyxrQkFBbUIsQ0FBQSxJQUFBLEdBQU8sQ0FBRyxFQUFBO0FBQ2xDLE1BQU0sTUFBQSxFQUFBLEdBQUssSUFBSUMscUJBQUssRUFBQTtBQUNwQixNQUFHLEVBQUEsQ0FBQSxlQUFBLEdBQWtCLE1BQU0sSUFBSyxDQUFBLElBQUEsQ0FBSyxrQkFBa0IsQ0FBRSxDQUFBLEdBQUEsQ0FBSSxDQUFDLEdBQVEsS0FBQTtBQUNsRSxRQUFLLElBQUEsQ0FBQSxrQkFBQSxDQUFtQixPQUFPLEdBQUcsQ0FBQTtBQUNsQyxRQUFPLE9BQUEsR0FBQTtBQUFBLE9BQ1YsQ0FBQTtBQUNELE1BQUEsSUFBQSxDQUFLLFdBQVcsRUFBRSxDQUFBO0FBQUE7QUFHdEIsSUFBQSxJQUFBLENBQUssY0FBZSxFQUFBO0FBQUE7QUFDeEI7QUFBQSxFQUdPLE9BQU8sTUFBc0IsRUFBQTtBQUNoQyxJQUFBLElBQUEsQ0FBSyxNQUFTLEdBQUEsSUFBQTtBQUNkLElBQUssSUFBQSxDQUFBLFVBQUEsR0FBYSxLQUFLLEdBQUksRUFBQTtBQUkzQixJQUFBLE1BQU0sVUFBVSxJQUFLLENBQUEsY0FBQSxDQUFlLE1BQU8sQ0FBQSxDQUFDLElBQUssR0FBSSxDQUFBO0FBQ3JELElBQUEsSUFBSSxPQUFTLEVBQUE7QUFDVCxNQUFBLE9BQUEsQ0FBUSxNQUFNLENBQUE7QUFBQSxLQUNYLE1BQUE7QUFDSCxNQUFLLElBQUEsQ0FBQSxRQUFBLENBQVMsV0FBWSxDQUFBLE9BQUEsQ0FBUSxtQ0FBbUMsTUFBTyxDQUFBLENBQUMsQ0FBQyxDQUFFLENBQUEsQ0FBQTtBQUFBO0FBQ3BGO0FBQ0osRUFFUSxlQUFlLFFBQTBCLEVBQUE7QUFFN0MsSUFBQSxJQUFJLElBQUssQ0FBQSxzQkFBQSxDQUF1QixHQUFJLENBQUEsUUFBQSxDQUFTLGNBQWMsQ0FBRyxFQUFBO0FBQzFELE1BQUssSUFBQSxDQUFBLFFBQUEsQ0FBUyxXQUFZLENBQUEsS0FBQSxDQUFNLDJDQUEyQyxJQUFLLENBQUEsVUFBQSxFQUFZLENBQUUsQ0FBQSxDQUFBO0FBQzlGLE1BQUE7QUFBQTtBQUlKLElBQUssSUFBQSxDQUFBLGtCQUFBLENBQW1CLE1BQU8sQ0FBQSxRQUFBLENBQVMsY0FBYyxDQUFBO0FBR3RELElBQUEsSUFDSSxTQUFTLGNBQWlCLEdBQUEsSUFBQSxDQUFLLDJCQUMvQixRQUFTLENBQUEsY0FBQSxLQUFtQixLQUFLLHVCQUNuQyxFQUFBO0FBQ0UsTUFBQTtBQUFBO0FBSUosSUFBSyxJQUFBLENBQUEsc0JBQUEsQ0FBdUIsR0FBSSxDQUFBLFFBQUEsQ0FBUyxjQUFjLENBQUE7QUFHdkQsSUFBTSxNQUFBLElBQUEsR0FBTyxRQUFTLENBQUEsY0FBQSxHQUFpQixJQUFLLENBQUEsdUJBQUE7QUFHNUMsSUFBQSxJQUFJLFNBQVMsQ0FBRyxFQUFBO0FBRVosTUFBQSxLQUFBLElBQVMsSUFBSSxJQUFLLENBQUEsdUJBQUEsR0FBMEIsR0FBRyxDQUFJLEdBQUEsUUFBQSxDQUFTLGdCQUFnQixDQUFLLEVBQUEsRUFBQTtBQUc3RSxRQUFBLElBQUksQ0FBQyxJQUFBLENBQUssc0JBQXVCLENBQUEsR0FBQSxDQUFJLENBQUMsQ0FBRyxFQUFBO0FBQ3JDLFVBQUssSUFBQSxDQUFBLGtCQUFBLENBQW1CLElBQUksQ0FBQyxDQUFBO0FBQUE7QUFDakM7QUFDSjtBQUlKLElBQUEsSUFBQSxDQUFLLDBCQUEwQixRQUFTLENBQUEsY0FBQTtBQUd4QyxJQUFXLEtBQUEsTUFBQSxLQUFBLElBQVMsU0FBUyxNQUFRLEVBQUE7QUFDakMsTUFBQSxJQUFBLENBQUssWUFBWSxLQUFLLENBQUE7QUFBQTtBQUMxQjtBQUNKLEVBRVEsVUFBVSxHQUFnQixFQUFBO0FBRzlCLElBQVcsS0FBQSxNQUFBLEdBQUEsSUFBTyxJQUFJLGVBQWlCLEVBQUE7QUFDbkMsTUFBSyxJQUFBLENBQUEsaUJBQUEsQ0FBa0IsT0FBTyxHQUFHLENBQUE7QUFBQTtBQUNyQztBQUNKLEVBRVEsV0FBVyxJQUFrQixFQUFBO0FBQ2pDLElBQVcsS0FBQSxNQUFBLEdBQUEsSUFBTyxLQUFLLGVBQWlCLEVBQUE7QUFDcEMsTUFBQSxJQUFJLElBQUssQ0FBQSxpQkFBQSxDQUFrQixHQUFJLENBQUEsR0FBRyxDQUFHLEVBQUE7QUFDakMsUUFBQSxNQUFNLFVBQWEsR0FBQSxJQUFBLENBQUssaUJBQWtCLENBQUEsR0FBQSxDQUFJLEdBQUcsQ0FBQTtBQUNqRCxRQUFBLEtBQUEsTUFBVyxhQUFhLFVBQVksRUFBQTtBQUNoQyxVQUFLLElBQUEsQ0FBQSxTQUFBLENBQVUsV0FBVyxDQUF3QixpQkFBQTtBQUFBO0FBRXRELFFBQUssSUFBQSxDQUFBLGlCQUFBLENBQWtCLE9BQU8sR0FBRyxDQUFBO0FBQUE7QUFDckM7QUFDSjtBQUNKLEVBRVEsWUFBWSxLQUFvQixFQUFBO0FBQ3BDLElBQUksSUFBQSxLQUFBLENBQU0sY0FBZ0IsRUFBQTtBQUN0QixNQUFBLElBQUEsQ0FBSyxlQUFlLEtBQUssQ0FBQTtBQUN6QixNQUFBO0FBQUE7QUFHSixJQUFBLE1BQU0sZUFBZSxLQUFNLENBQUEsWUFBQTtBQUMzQixJQUFBLE1BQU0sYUFBYSxLQUFNLENBQUEsVUFBQTtBQUN6QixJQUFBLE1BQU0sZ0JBQWdCLEtBQU0sQ0FBQSxhQUFBO0FBRTVCLElBQUksSUFBQSxLQUFBLENBQU0sYUFBZSxFQUFBO0FBQ3JCLE1BQ0ksSUFBQSxhQUFBLEdBQWdCLEtBQUsseUJBQTBCLENBQUEsWUFBWSxLQUMzRCxVQUFhLEdBQUEsSUFBQSxDQUFLLGVBQWdCLENBQUEsWUFBWSxDQUNoRCxFQUFBO0FBRUUsUUFBQTtBQUFBO0FBR0osTUFBSyxJQUFBLENBQUEseUJBQUEsQ0FBMEIsWUFBWSxDQUFBLEdBQUksYUFBZ0IsR0FBQSxDQUFBO0FBQy9ELE1BQUEsSUFBQSxDQUFLLGFBQWEsS0FBSyxDQUFBO0FBQUEsS0FDM0IsTUFBQSxJQUFXLEtBQU0sQ0FBQSxTQUFBLEVBQWEsRUFBQTtBQUMxQixNQUFBLElBQUksVUFBZSxLQUFBLElBQUEsQ0FBSyxlQUFnQixDQUFBLFlBQVksQ0FBRyxFQUFBO0FBQ25ELFFBQUssSUFBQSxDQUFBLHlCQUFBLENBQTBCLFlBQVksQ0FBSSxHQUFBLENBQUE7QUFDL0MsUUFBSyxJQUFBLENBQUEsZUFBQSxDQUFnQixZQUFZLENBQUEsR0FBSSxVQUFhLEdBQUEsQ0FBQTtBQUVsRCxRQUFBLElBQUEsQ0FBSyxhQUFhLEtBQUssQ0FBQTtBQUN2QixRQUFJLElBQUEsQ0FBQSxHQUFJLElBQUssQ0FBQSxlQUFBLENBQWdCLFlBQVksQ0FBQTtBQUN6QyxRQUFBLE1BQU0sZUFBa0IsR0FBQSxJQUFBLENBQUssa0JBQW1CLENBQUEsR0FBQSxDQUFJLFlBQVksQ0FBQTtBQUNoRSxRQUFBLE9BQU8sZUFBZ0IsQ0FBQSxHQUFBLENBQUksQ0FBQyxDQUFBLEVBQUcsQ0FBSyxFQUFBLEVBQUE7QUFDaEMsVUFBQSxJQUFBLENBQUssWUFBYSxDQUFBLGVBQUEsQ0FBZ0IsR0FBSSxDQUFBLENBQUMsQ0FBRSxDQUFBO0FBQ3pDLFVBQUEsZUFBQSxDQUFnQixPQUFPLENBQUMsQ0FBQTtBQUFBO0FBSTVCLFFBQUssSUFBQSxDQUFBLGtCQUFBLENBQW1CLEdBQUksQ0FBQSxZQUFBLEVBQWMsZUFBZSxDQUFBO0FBQ3pELFFBQUssSUFBQSxDQUFBLGVBQUEsQ0FBZ0IsWUFBWSxDQUFJLEdBQUEsQ0FBQTtBQUFBLE9BQzlCLE1BQUEsSUFBQSxVQUFBLEdBQWEsSUFBSyxDQUFBLGVBQUEsQ0FBZ0IsWUFBWSxDQUFJLEVBQUE7QUFDekQsUUFBQSxNQUFNLFNBQVksR0FBQSxJQUFBLENBQUssa0JBQW1CLENBQUEsR0FBQSxDQUFJLFlBQVksQ0FBQTtBQUMxRCxRQUFVLFNBQUEsQ0FBQSxHQUFBLENBQUksWUFBWSxLQUFLLENBQUE7QUFBQTtBQUNuQyxLQUNHLE1BQUE7QUFDSCxNQUFBLElBQUEsQ0FBSyxhQUFhLEtBQUssQ0FBQTtBQUFBO0FBQzNCO0FBQ0osRUFFTyxTQUFBLENBQVUsS0FBYyxFQUFBLEtBQUEsR0FBUSxDQUE2QixlQUFBO0FBQ2hFLElBQUFDLHVCQUFBLENBQU8sT0FBTyxLQUFBLENBQU0sWUFBaUIsS0FBQSxRQUFBLEVBQVUsbUNBQW1DLENBQUE7QUFHbEYsSUFBSSxJQUFBLEtBQUEsQ0FBTSxhQUFlLEVBQUE7QUFFckIsTUFBQSxLQUFBLENBQU0sVU