metaapi.cloud-sdk
Version:
SDK for MetaApi, a professional cloud forex API which includes MetaTrader REST API and MetaTrader websocket API. Supports both MetaTrader 5 (MT5) and MetaTrader 4 (MT4). CopyFactory copy trading API included. (https://metaapi.cloud)
2,360 lines • 367 kB
JavaScript
'use strict';
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _async_to_generator(fn) {
return function() {
var self = this, args = arguments;
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
function _define_property(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _object_spread(target) {
for(var i = 1; i < arguments.length; i++){
var source = arguments[i] != null ? arguments[i] : {};
var ownKeys = Object.keys(source);
if (typeof Object.getOwnPropertySymbols === "function") {
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
}));
}
ownKeys.forEach(function(key) {
_define_property(target, key, source[key]);
});
}
return target;
}
import randomstring from 'randomstring';
import socketIO from 'socket.io-client';
import TimeoutError from '../timeoutError';
import { ValidationError, NotFoundError, InternalError, UnauthorizedError, TooManyRequestsError, ForbiddenError } from '../errorHandler';
import OptionsValidator from '../optionsValidator';
import NotSynchronizedError from './notSynchronizedError';
import NotConnectedError from './notConnectedError';
import TradeError from './tradeError';
import PacketOrderer from './packetOrderer';
import SynchronizationThrottler from './synchronizationThrottler';
import SubscriptionManager from './subscriptionManager';
import LoggerManager from '../../logger';
import any from 'promise.any';
import LatencyService from './latencyService';
import _ from 'lodash';
export * from './metaApiWebsocket.client.schemas';
let PacketLogger;
if (typeof window === 'undefined') {
PacketLogger = require('./packetLogger').default;
}
/**
* MetaApi websocket API client (see https://metaapi.cloud/docs/client/websocket/overview/)
*/ let MetaApiWebsocketClient = class MetaApiWebsocketClient {
/**
* Restarts the account synchronization process on an out of order packet
* @param {String} accountId account id
* @param {Number} instanceIndex instance index
* @param {Number} expectedSequenceNumber expected s/n
* @param {Number} actualSequenceNumber actual s/n
* @param {Object} packet packet data
* @param {Date} receivedAt time the packet was received at
*/ onOutOfOrderPacket(accountId, instanceIndex, expectedSequenceNumber, actualSequenceNumber, packet, receivedAt) {
const primaryAccountId = this._accountsByReplicaId[accountId];
if (this._subscriptionManager.isSubscriptionActive(accountId)) {
const level = this._latencyService.getSynchronizedAccountInstances(primaryAccountId).length ? 'debug' : 'error';
this._logger[level]('MetaApi websocket client received an out of order ' + `packet type ${packet.type} for account id ${accountId}:${instanceIndex}. Expected s/n ` + `${expectedSequenceNumber} does not match the actual of ${actualSequenceNumber}`);
this.ensureSubscribe(accountId, instanceIndex);
}
}
/**
* Patch server URL for use in unit tests
* @param {String} url patched server URL
*/ set url(url) {
this._url = url;
}
/**
* Websocket client predefined region
* @returns {String} predefined region
*/ get region() {
return this._region;
}
/**
* Returns the list of socket instance dictionaries
* @return {Object[]} list of socket instance dictionaries
*/ get socketInstances() {
return this._socketInstances;
}
/**
* Returns the dictionary of socket instances by account ids
* @return {Object} dictionary of socket instances by account ids
*/ get socketInstancesByAccounts() {
return this._socketInstancesByAccounts;
}
/**
* Returns the dictionary of account replicas by region
* @return {Object} dictionary of account replicas by region
*/ get accountReplicas() {
return this._accountReplicas;
}
/**
* Returns the dictionary of primary account ids by replica ids
* @return {Object} dictionary of primary account ids by replica ids
*/ get accountsByReplicaId() {
return this._accountsByReplicaId;
}
/**
* Returns clear account cache job. Used for tests
* @return {Function} clear account cache job
*/ get clearAccountCacheJob() {
return this._clearAccountCacheJob.bind(this);
}
/**
* Returns latency service
* @returns {LatencyService} latency service
*/ get latencyService() {
return this._latencyService;
}
/**
* Returns the list of subscribed account ids
* @param {Number} instanceNumber instance index number
* @param {String} socketInstanceIndex socket instance index
* @param {String} region server region
* @return {string[]} list of subscribed account ids
*/ subscribedAccountIds(instanceNumber, socketInstanceIndex, region) {
const connectedIds = [];
if (this._socketInstancesByAccounts[instanceNumber]) {
Object.keys(this._connectedHosts).forEach((instanceId)=>{
const accountId = instanceId.split(':')[0];
const accountRegion = this.getAccountRegion(accountId);
if (!connectedIds.includes(accountId) && this._socketInstancesByAccounts[instanceNumber][accountId] !== undefined && (this._socketInstancesByAccounts[instanceNumber][accountId] === socketInstanceIndex || socketInstanceIndex === undefined) && accountRegion === region) {
connectedIds.push(accountId);
}
});
}
return connectedIds;
}
/**
* Returns websocket client connection status
* @param {Number} instanceNumber instance index number
* @param {Number} socketInstanceIndex socket instance index
* @param {String} region server region
* @returns {Boolean} websocket client connection status
*/ connected(instanceNumber, socketInstanceIndex, region) {
const instance = this._socketInstances[region] && this._socketInstances[region][instanceNumber].length > socketInstanceIndex ? this._socketInstances[region][instanceNumber][socketInstanceIndex] : null;
return instance && instance.socket && instance.socket.connected || false;
}
/**
* Returns list of accounts assigned to instance
* @param {Number} instanceNumber instance index number
* @param {String} socketInstanceIndex socket instance index
* @param {String} region server region
* @returns
*/ _getAssignedAccounts(instanceNumber, socketInstanceIndex, region) {
const accountIds = [];
Object.keys(this._socketInstancesByAccounts[instanceNumber]).forEach((key)=>{
const accountRegion = this.getAccountRegion(key);
if (accountRegion === region && this._socketInstancesByAccounts[instanceNumber][key] === socketInstanceIndex) {
accountIds.push(key);
}
});
return accountIds;
}
/**
* Returns account region by id
* @param {String} accountId account id
* @returns {String} account region
*/ getAccountRegion(accountId) {
return this._regionsByAccounts[accountId] && this._regionsByAccounts[accountId].region;
}
/**
* Adds account cache info
* @param {String} accountId account id
* @param {Object} replicas account replicas, including primary replica
*/ addAccountCache(accountId, replicas) {
this._accountReplicas[accountId] = replicas;
Object.keys(replicas).forEach((region)=>{
const replicaId = replicas[region];
if (!this._regionsByAccounts[replicaId]) {
this._regionsByAccounts[replicaId] = {
region,
connections: 1,
lastUsed: Date.now()
};
} else {
this._regionsByAccounts[replicaId].connections++;
}
this._accountsByReplicaId[replicaId] = accountId;
});
this._logger.debug(`${accountId}: added account cache`);
}
/**
* Updates account cache info
* @param {String} accountId account id
* @param {Object} replicas account replicas
*/ updateAccountCache(accountId, replicas) {
const oldReplicas = this._accountReplicas[accountId];
if (oldReplicas) {
const connectionCount = this._regionsByAccounts[accountId].connections;
Object.keys(oldReplicas).forEach((region)=>{
const replicaId = replicas[region];
delete this._accountsByReplicaId[replicaId];
delete this._regionsByAccounts[replicaId];
});
this._accountReplicas[accountId] = replicas;
Object.keys(replicas).forEach((region)=>{
const replicaId = replicas[region];
this._regionsByAccounts[replicaId] = {
region,
connections: connectionCount,
lastUsed: Date.now()
};
this._accountsByReplicaId[replicaId] = accountId;
});
this._logger.debug(`${accountId}: updated account cache`);
}
}
/**
* Removes account region info
* @param {String} accountId account id
*/ removeAccountCache(accountId) {
var _this__regionsByAccounts_accountId;
if (((_this__regionsByAccounts_accountId = this._regionsByAccounts[accountId]) === null || _this__regionsByAccounts_accountId === void 0 ? void 0 : _this__regionsByAccounts_accountId.connections) > 0) {
this._regionsByAccounts[accountId].connections--;
}
}
/**
* Locks subscription for a socket instance based on TooManyRequestsError metadata
* @param {Number} instanceNumber instance index number
* @param {String} socketInstanceIndex socket instance index
* @param {String} region server region
* @param {Object} metadata TooManyRequestsError metadata
*/ lockSocketInstance(instanceNumber, socketInstanceIndex, region, metadata) {
var _this = this;
return _async_to_generator(function*() {
if (metadata.type === 'LIMIT_ACCOUNT_SUBSCRIPTIONS_PER_USER') {
_this._subscribeLock = {
recommendedRetryTime: metadata.recommendedRetryTime,
lockedAtAccounts: _this.subscribedAccountIds(instanceNumber, undefined, region).length,
lockedAtTime: Date.now()
};
} else {
const subscribedAccounts = _this.subscribedAccountIds(instanceNumber, socketInstanceIndex, region);
if (subscribedAccounts.length === 0) {
const socketInstance = _this.socketInstances[region][instanceNumber][socketInstanceIndex];
socketInstance.socket.close();
yield _this._reconnect(instanceNumber, socketInstanceIndex, region);
} else {
const instance = _this.socketInstances[region][instanceNumber][socketInstanceIndex];
instance.subscribeLock = {
recommendedRetryTime: metadata.recommendedRetryTime,
type: metadata.type,
lockedAtAccounts: subscribedAccounts.length
};
}
}
})();
}
/**
* Connects to MetaApi server via socket.io protocol
* @param {Number} instanceNumber instance index number
* @param {String} region server region
* @returns {Promise} promise which resolves when connection is established
*/ connect(instanceNumber, region) {
var _this = this;
return _async_to_generator(function*() {
var _this__socketInstances, _region, _this__socketInstances_region, _instanceNumber;
if (_this._region && region !== _this._region) {
throw new ValidationError(`Trying to connect to ${region} region, but configured with ${_this._region}`);
}
let clientId = Math.random();
let resolve;
let result = new Promise((res, rej)=>{
resolve = res;
});
(_this__socketInstances = _this._socketInstances)[_region = region] || (_this__socketInstances[_region] = {});
(_this__socketInstances_region = _this._socketInstances[region])[_instanceNumber = instanceNumber] || (_this__socketInstances_region[_instanceNumber] = []);
const socketInstanceIndex = _this._socketInstances[region][instanceNumber].length;
const instance = {
id: socketInstanceIndex,
reconnectWaitTime: _this._socketMinimumReconnectTimeout,
connected: false,
requestResolves: {},
resolved: false,
connectResult: result,
sessionId: randomstring.generate(32),
isReconnecting: false,
socket: null,
synchronizationThrottler: new SynchronizationThrottler(_this, socketInstanceIndex, instanceNumber, region, _this._synchronizationThrottlerOpts),
subscribeLock: null,
instanceNumber
};
instance.connected = true;
_this._socketInstances[region][instanceNumber].push(instance);
instance.synchronizationThrottler.start();
const serverUrl = yield _this._getServerUrl(instanceNumber, socketInstanceIndex, region);
const socketInstance = socketIO(serverUrl, {
path: '/ws',
reconnection: true,
reconnectionDelay: 1000,
reconnectionDelayMax: 5000,
reconnectionAttempts: Infinity,
timeout: _this._connectTimeout,
extraHeaders: {
'Client-Id': clientId
},
query: {
'auth-token': _this._token,
clientId: clientId,
protocol: 3
}
});
instance.socket = socketInstance;
socketInstance.on('connect', /*#__PURE__*/ _async_to_generator(function*() {
// eslint-disable-next-line no-console
_this._logger.info(`${region}:${instanceNumber}: MetaApi websocket client connected to the MetaApi server`);
instance.reconnectWaitTime = _this._socketMinimumReconnectTimeout;
instance.isReconnecting = false;
if (!instance.resolved) {
instance.resolved = true;
resolve();
} else {
yield _this._fireReconnected(instanceNumber, instance.id, region);
}
if (!instance.connected) {
instance.socket.close();
}
}));
socketInstance.on('reconnect', /*#__PURE__*/ _async_to_generator(function*() {
instance.isReconnecting = false;
_this._logger.info(`${region}:${instanceNumber}: MetaApi websocket client reconnected`);
yield _this._fireReconnected(instanceNumber, instance.id, region);
}));
socketInstance.on('connect_error', function() {
var _ref = _async_to_generator(function*(err) {
// eslint-disable-next-line no-console
_this._logger.error(`${region}:${instanceNumber}: MetaApi websocket client connection error`, err);
instance.isReconnecting = false;
yield _this._reconnect(instanceNumber, instance.id, region);
});
return function(err) {
return _ref.apply(this, arguments);
};
}());
socketInstance.on('connect_timeout', function() {
var _ref = _async_to_generator(function*(timeout) {
// eslint-disable-next-line no-console
_this._logger.error(`${region}:${instanceNumber}: MetaApi websocket client connection timeout`);
instance.isReconnecting = false;
if (!instance.resolved) {
yield _this._reconnect(instanceNumber, instance.id, region);
}
});
return function(timeout) {
return _ref.apply(this, arguments);
};
}());
socketInstance.on('disconnect', function() {
var _ref = _async_to_generator(function*(reason) {
instance.synchronizationThrottler.onDisconnect();
// eslint-disable-next-line no-console
_this._logger.info(`${region}:${instanceNumber}: MetaApi websocket client disconnected from the ` + `MetaApi server because of ${reason}`);
instance.isReconnecting = false;
yield _this._reconnect(instanceNumber, instance.id, region);
});
return function(reason) {
return _ref.apply(this, arguments);
};
}());
socketInstance.on('error', function() {
var _ref = _async_to_generator(function*(error) {
// eslint-disable-next-line no-console
_this._logger.error(`${region}:${instanceNumber}: MetaApi websocket client error`, error);
instance.isReconnecting = false;
yield _this._reconnect(instanceNumber, instance.id, region);
});
return function(error) {
return _ref.apply(this, arguments);
};
}());
socketInstance.on('response', (data)=>{
if (typeof data === 'string') {
data = JSON.parse(data);
}
_this._logger.debug(()=>`${data.accountId}: Response received: ${JSON.stringify({
requestId: data.requestId,
timestamps: data.timestamps
})}`);
let requestResolve = instance.requestResolves[data.requestId] || {
resolve: ()=>{},
reject: ()=>{}
};
delete instance.requestResolves[data.requestId];
_this._convertIsoTimeToDate(data);
requestResolve.resolve(data);
if (data.timestamps && requestResolve.type) {
data.timestamps.clientProcessingFinished = new Date();
for (let listener of _this._latencyListeners){
Promise.resolve().then(()=>requestResolve.type === 'trade' ? listener.onTrade(data.accountId, data.timestamps) : listener.onResponse(data.accountId, requestResolve.type, data.timestamps)).catch((error)=>_this._logger.error('Failed to process onResponse event for account ' + data.accountId + ', request type ' + requestResolve.type, error));
}
}
});
socketInstance.on('processingError', (data)=>{
let requestResolve = instance.requestResolves[data.requestId] || {
resolve: ()=>{},
reject: ()=>{}
};
delete instance.requestResolves[data.requestId];
requestResolve.reject(_this._convertError(data));
});
// eslint-disable-next-line complexity
socketInstance.on('synchronization', function() {
var _ref = _async_to_generator(function*(data) {
var _this__regionsByAccounts, _data_accountId;
if (typeof data === 'string') {
data = JSON.parse(data);
}
if (data.instanceIndex && data.instanceIndex !== instanceNumber) {
_this._logger.trace(()=>`${data.accountId}:${data.instanceNumber}: received packet with wrong instance ` + `index via a socket with instance number of ${instanceNumber}, data=${JSON.stringify({
type: data.type,
sequenceNumber: data.sequenceNumber,
sequenceTimestamp: data.sequenceTimestamp,
synchronizationId: data.synchronizationId,
application: data.application,
host: data.host,
specificationsUpdated: data.specificationsUpdated,
positionsUpdated: data.positionsUpdated,
ordersUpdated: data.ordersUpdated,
specifications: data.specifications ? (data.specifications || []).length : undefined
})}`);
return;
}
(_this__regionsByAccounts = _this._regionsByAccounts)[_data_accountId = data.accountId] || (_this__regionsByAccounts[_data_accountId] = {
region,
connections: 0,
lastUsed: Date.now()
});
_this._logger.trace(()=>`${data.accountId}:${data.instanceIndex}: Sync packet received: ${JSON.stringify({
type: data.type,
sequenceNumber: data.sequenceNumber,
sequenceTimestamp: data.sequenceTimestamp,
synchronizationId: data.synchronizationId,
application: data.application,
host: data.host,
specificationsUpdated: data.specificationsUpdated,
positionsUpdated: data.positionsUpdated,
ordersUpdated: data.ordersUpdated,
specifications: data.specifications ? (data.specifications || []).length : undefined
})}, ` + `active listeners: ${(_this._synchronizationListeners[data.accountId] || []).length}`);
let activeSynchronizationIds = instance.synchronizationThrottler.activeSynchronizationIds;
if (!data.synchronizationId || activeSynchronizationIds.includes(data.synchronizationId)) {
if (_this._packetLogger) {
yield _this._packetLogger.logPacket(data);
}
const ignoredPacketTypes = [
'disconnected',
'status',
'keepalive'
];
if (!_this._subscriptionManager.isSubscriptionActive(data.accountId) && !ignoredPacketTypes.includes(data.type)) {
_this._logger.debug(`${data.accountId}: Packet arrived to inactive connection, attempting` + ` unsubscribe, packet: ${data.type}`);
if (_this._throttleRequest('unsubscribe', data.accountId, data.instanceIndex, _this._unsubscribeThrottlingInterval)) {
_this.unsubscribe(data.accountId).catch((err)=>{
_this._logger.warn(`${data.accountId}:${data.instanceIndex || 0}: failed to unsubscribe`, err);
});
}
return;
}
_this._convertIsoTimeToDate(data);
} else {
data.type = 'noop';
}
_this.queuePacket(instance, data);
});
return function(data) {
return _ref.apply(this, arguments);
};
}());
socketInstance.on('metadata', (data)=>{
instance.clientApiHostname = data.clientApiHostname;
});
return result;
})();
}
/**
* Closes connection to MetaApi server
*/ close() {
Object.keys(this._socketInstances).forEach((region)=>{
Object.keys(this._socketInstances[region]).forEach((instanceNumber)=>{
this._socketInstances[region][instanceNumber].forEach(function() {
var _ref = _async_to_generator(function*(instance) {
if (instance.connected) {
instance.connected = false;
yield instance.socket.close();
for (let requestResolve of Object.values(instance.requestResolves)){
requestResolve.reject(new Error('MetaApi connection closed'));
}
instance.requestResolves = {};
}
});
return function(instance) {
return _ref.apply(this, arguments);
};
}());
this._socketInstancesByAccounts[instanceNumber] = {};
this._socketInstances[region][instanceNumber] = [];
});
});
this._synchronizationListeners = {};
this._latencyListeners = [];
this._packetOrderer.stop();
}
/**
* Stops the client
*/ stop() {
clearInterval(this._clearAccountCacheInterval);
clearInterval(this._clearInactiveSyncDataInterval);
this._latencyService.stop();
}
/**
* Returns account information for a specified MetaTrader account.
* @param {String} accountId id of the MetaTrader account to return information for
* @param {GetAccountInformationOptions} [options] additional request options
* @returns {Promise<MetatraderAccountInformation>} promise resolving with account information
*/ getAccountInformation(accountId, options) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, _object_spread({
application: 'RPC',
type: 'getAccountInformation'
}, options));
return response.accountInformation;
})();
}
/**
* Returns positions for a specified MetaTrader account.
* @param {String} accountId id of the MetaTrader account to return information for
* @param {GetPositionsOptions} [options] additional request options
* @returns {Promise<Array<MetatraderPosition>} promise resolving with array of open positions
*/ getPositions(accountId, options) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, _object_spread({
application: 'RPC',
type: 'getPositions'
}, options));
return response.positions;
})();
}
/**
* Returns specific position for a MetaTrader account.
* @param {String} accountId id of the MetaTrader account to return information for
* @param {String} positionId position id
* @param {GetPositionOptions} [options] additional request options
* @return {Promise<MetatraderPosition>} promise resolving with MetaTrader position found
*/ getPosition(accountId, positionId, options) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, _object_spread({
application: 'RPC',
type: 'getPosition',
positionId
}, options));
return response.position;
})();
}
/**
* Returns open orders for a specified MetaTrader account.
* @param {String} accountId id of the MetaTrader account to return information for
* @param {GetOrdersOptions} [options] additional request options
* @return {Promise<Array<MetatraderOrder>>} promise resolving with open MetaTrader orders
*/ getOrders(accountId, options) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, _object_spread({
application: 'RPC',
type: 'getOrders'
}, options));
return response.orders;
})();
}
/**
* Returns specific open order for a MetaTrader account.
* @param {String} accountId id of the MetaTrader account to return information for
* @param {String} orderId order id (ticket number)
* @param {GetOrderOptions} [options] additional request options
* @return {Promise<MetatraderOrder>} promise resolving with metatrader order found
*/ getOrder(accountId, orderId, options) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, _object_spread({
application: 'RPC',
type: 'getOrder',
orderId
}, options));
return response.order;
})();
}
/**
* MetaTrader history orders search query response
* @typedef {Object} MetatraderHistoryOrders
* @property {Array<MetatraderOrder>} historyOrders array of history orders returned
* @property {Boolean} synchronizing flag indicating that history order initial synchronization is still in progress
* and thus search results may be incomplete
*/ /**
* Returns the history of completed orders for a specific ticket number.
* @param {String} accountId id of the MetaTrader account to return information for
* @param {String} ticket ticket number (order id)
* @returns {Promise<MetatraderHistoryOrders>} promise resolving with request results containing history orders found
*/ getHistoryOrdersByTicket(accountId, ticket) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, {
application: 'RPC',
type: 'getHistoryOrdersByTicket',
ticket
});
return {
historyOrders: response.historyOrders,
synchronizing: response.synchronizing
};
})();
}
/**
* Returns the history of completed orders for a specific position id
* @param {String} accountId id of the MetaTrader account to return information for
* @param {String} positionId position id
* @returns {Promise<MetatraderHistoryOrders>} promise resolving with request results containing history orders found
*/ getHistoryOrdersByPosition(accountId, positionId) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, {
application: 'RPC',
type: 'getHistoryOrdersByPosition',
positionId
});
return {
historyOrders: response.historyOrders,
synchronizing: response.synchronizing
};
})();
}
/**
* Returns the history of completed orders for a specific time range
* @param {String} accountId id of the MetaTrader account to return information for
* @param {Date} startTime start of time range, inclusive
* @param {Date} endTime end of time range, exclusive
* @param {Number} offset pagination offset, default is 0
* @param {Number} limit pagination limit, default is 1000
* @returns {Promise<MetatraderHistoryOrders>} promise resolving with request results containing history orders found
*/ getHistoryOrdersByTimeRange(accountId, startTime, endTime, offset = 0, limit = 1000) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, {
application: 'RPC',
type: 'getHistoryOrdersByTimeRange',
startTime,
endTime,
offset,
limit
});
return {
historyOrders: response.historyOrders,
synchronizing: response.synchronizing
};
})();
}
/**
* MetaTrader history deals search query response
* @typedef {Object} MetatraderDeals
* @property {Array<MetatraderDeal>} deals array of history deals returned
* @property {Boolean} synchronizing flag indicating that deal initial synchronization is still in progress
* and thus search results may be incomplete
*/ /**
* MetaTrader deal
* @typedef {Object} MetatraderDeal
* @property {String} id deal id (ticket number)
* @property {String} type deal type (one of DEAL_TYPE_BUY, DEAL_TYPE_SELL, DEAL_TYPE_BALANCE, DEAL_TYPE_CREDIT,
* DEAL_TYPE_CHARGE, DEAL_TYPE_CORRECTION, DEAL_TYPE_BONUS, DEAL_TYPE_COMMISSION, DEAL_TYPE_COMMISSION_DAILY,
* DEAL_TYPE_COMMISSION_MONTHLY, DEAL_TYPE_COMMISSION_AGENT_DAILY, DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
* DEAL_TYPE_INTEREST, DEAL_TYPE_BUY_CANCELED, DEAL_TYPE_SELL_CANCELED, DEAL_DIVIDEND, DEAL_DIVIDEND_FRANKED,
* DEAL_TAX). See https://www.mql5.com/en/docs/constants/tradingconstants/dealproperties#enum_deal_type
* @property {String} entryType deal entry type (one of DEAL_ENTRY_IN, DEAL_ENTRY_OUT, DEAL_ENTRY_INOUT,
* DEAL_ENTRY_OUT_BY). See https://www.mql5.com/en/docs/constants/tradingconstants/dealproperties#enum_deal_entry
* @property {String} [symbol] symbol deal relates to
* @property {Number} [magic] deal magic number, identifies the EA which initiated the deal
* @property {Date} time time the deal was conducted at
* @property {String} brokerTime time time the deal was conducted at, in broker timezone, YYYY-MM-DD HH:mm:ss.SSS format
* @property {Number} [volume] deal volume
* @property {Number} [price] the price the deal was conducted at
* @property {Number} [commission] deal commission
* @property {Number} [swap] deal swap
* @property {Number} profit deal profit
* @property {String} [positionId] id of position the deal relates to
* @property {String} [orderId] id of order the deal relates to
* @property {String} [comment] deal comment. The sum of the line lengths of the comment and the clientId
* must be less than or equal to 26. For more information see https://metaapi.cloud/docs/client/clientIdUsage/
* @property {String} [brokerComment] current comment value on broker side (possibly overriden by the broker)
* @property {String} [clientId] client-assigned id. The id value can be assigned when submitting a trade and
* will be present on position, history orders and history deals related to the trade. You can use this field to bind
* your trades to objects in your application and then track trade progress. The sum of the line lengths of the
* comment and the clientId must be less than or equal to 26. For more information see
* https://metaapi.cloud/docs/client/clientIdUsage/
* @property {String} platform platform id (mt4 or mt5)
* @property {String} [reason] optional deal execution reason. One of DEAL_REASON_CLIENT, DEAL_REASON_MOBILE,
* DEAL_REASON_WEB, DEAL_REASON_EXPERT, DEAL_REASON_SL, DEAL_REASON_TP, DEAL_REASON_SO, DEAL_REASON_ROLLOVER,
* DEAL_REASON_VMARGIN, DEAL_REASON_SPLIT, DEAL_REASON_UNKNOWN. See
* https://www.mql5.com/en/docs/constants/tradingconstants/dealproperties#enum_deal_reason.
* @property {Number} [accountCurrencyExchangeRate] current exchange rate of account currency into account base
* currency (USD if you did not override it)
* @property {number} [stopLoss] deal stop loss. For MT5 opening deal this is the SL of the order opening the
* position. For MT4 deals or MT5 closing deal this is the last known position SL.
* @property {number} [takeProfit] deal take profit. For MT5 opening deal this is the TP of the order opening the
* position. For MT4 deals or MT5 closing deal this is the last known position TP.
*/ /**
* Returns history deals with a specific ticket number
* @param {String} accountId id of the MetaTrader account to return information for
* @param {String} ticket ticket number (deal id for MT5 or order id for MT4)
* @returns {Promise<MetatraderDeals>} promise resolving with request results containing deals found
*/ getDealsByTicket(accountId, ticket) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, {
application: 'RPC',
type: 'getDealsByTicket',
ticket
});
return {
deals: response.deals,
synchronizing: response.synchronizing
};
})();
}
/**
* Returns history deals for a specific position id
* @param {String} accountId id of the MetaTrader account to return information for
* @param {String} positionId position id
* @returns {Promise<MetatraderDeals>} promise resolving with request results containing deals found
*/ getDealsByPosition(accountId, positionId) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, {
application: 'RPC',
type: 'getDealsByPosition',
positionId
});
return {
deals: response.deals,
synchronizing: response.synchronizing
};
})();
}
/**
* Returns history deals with for a specific time range
* @param {String} accountId id of the MetaTrader account to return information for
* @param {Date} startTime start of time range, inclusive
* @param {Date} endTime end of time range, exclusive
* @param {Number} offset pagination offset, default is 0
* @param {Number} limit pagination limit, default is 1000
* @returns {Promise<MetatraderDeals>} promise resolving with request results containing deals found
*/ getDealsByTimeRange(accountId, startTime, endTime, offset = 0, limit = 1000) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, {
application: 'RPC',
type: 'getDealsByTimeRange',
startTime,
endTime,
offset,
limit
});
return {
deals: response.deals,
synchronizing: response.synchronizing
};
})();
}
/**
* Clears the order and transaction history of a specified application and removes the application
* @param {String} accountId id of the MetaTrader account to remove history and application for
* @return {Promise} promise resolving when the history is cleared
*/ removeApplication(accountId) {
return this.rpcRequest(accountId, {
type: 'removeApplication'
});
}
/**
* MetaTrader trade response
* @typedef {Object} MetatraderTradeResponse
* @property {Number} numericCode numeric response code, see
* https://www.mql5.com/en/docs/constants/errorswarnings/enum_trade_return_codes and
* https://book.mql4.com/appendix/errors. Response codes which indicate success are 0, 10008-10010, 10025. The rest
* codes are errors
* @property {String} stringCode string response code, see
* https://www.mql5.com/en/docs/constants/errorswarnings/enum_trade_return_codes and
* https://book.mql4.com/appendix/errors. Response codes which indicate success are ERR_NO_ERROR,
* TRADE_RETCODE_PLACED, TRADE_RETCODE_DONE, TRADE_RETCODE_DONE_PARTIAL, TRADE_RETCODE_NO_CHANGES. The rest codes are
* errors.
* @property {String} message human-readable response message
* @property {String} orderId order id which was created/modified during the trade
* @property {String} positionId position id which was modified during the trade
*/ /**
* Execute a trade on a connected MetaTrader account
* @param {String} accountId id of the MetaTrader account to execute trade for
* @param {MetatraderTrade} trade trade to execute (see docs for possible trade types)
* @param {String} [application] application to use
* @param {String} [reliability] account reliability
* @returns {Promise<MetatraderTradeResponse>} promise resolving with trade result
* @throws {TradeError} on trade error, check error properties for error code details
*/ // eslint-disable-next-line complexity
trade(accountId, trade, application, reliability) {
var _this = this;
return _async_to_generator(function*() {
let response;
if (application === 'RPC') {
response = yield _this.rpcRequest(accountId, {
type: 'trade',
trade,
application
});
} else {
response = yield _this.rpcRequestAllInstances(accountId, {
type: 'trade',
trade,
application: application || _this._application,
requestId: randomstring.generate(32)
}, reliability);
}
response.response = response.response || {};
response.response.stringCode = response.response.stringCode || response.response.description;
response.response.numericCode = response.response.numericCode !== undefined ? response.response.numericCode : response.response.error;
if ([
'ERR_NO_ERROR',
'TRADE_RETCODE_PLACED',
'TRADE_RETCODE_DONE',
'TRADE_RETCODE_DONE_PARTIAL',
'TRADE_RETCODE_NO_CHANGES'
].includes(response.response.stringCode || response.response.description)) {
return response.response;
} else {
throw new TradeError(response.response.message, response.response.numericCode, response.response.stringCode);
}
})();
}
/**
* Creates a task that ensures the account gets subscribed to the server
* @param {String} accountId account id to subscribe
* @param {Number} instanceNumber instance index number
*/ ensureSubscribe(accountId, instanceNumber) {
this._subscriptionManager.scheduleSubscribe(accountId, instanceNumber);
}
/**
* Subscribes to the Metatrader terminal events
* @param {String} accountId id of the MetaTrader account to subscribe to
* @param {Number} instanceNumber instance index number
* @returns {Promise} promise which resolves when subscription started
*/ subscribe(accountId, instanceNumber) {
return this._subscriptionManager.subscribe(accountId, instanceNumber);
}
/**
* Requests the terminal to start synchronization process
* @param {String} accountId id of the MetaTrader account to synchronize
* @param {Number} instanceIndex instance index
* @param {String} host name of host to synchronize with
* @param {String} synchronizationId synchronization request id
* @param {Date} startingHistoryOrderTime from what date to start synchronizing history orders from. If not specified,
* the entire order history will be downloaded.
* @param {Date} startingDealTime from what date to start deal synchronization from. If not specified, then all
* history deals will be downloaded.
* @param {Function} getHashes function to get terminal state hashes
* @returns {Promise} promise which resolves when synchronization started
*/ synchronize(accountId, instanceIndex, host, synchronizationId, startingHistoryOrderTime, startingDealTime, hashes) {
var _this = this;
return _async_to_generator(function*() {
if (_this._getSocketInstanceByAccount(accountId, instanceIndex) === undefined) {
_this._logger.debug(`${accountId}:${instanceIndex}: creating socket instance on synchronize`);
yield _this._createSocketInstanceByAccount(accountId, instanceIndex);
}
const syncThrottler = _this._getSocketInstanceByAccount(accountId, instanceIndex).synchronizationThrottler;
_this._synchronizationHashes[synchronizationId] = hashes;
_this._synchronizationHashes[synchronizationId].lastUpdated = Date.now();
return syncThrottler.scheduleSynchronize(accountId, {
requestId: synchronizationId,
version: 2,
type: 'synchronize',
startingHistoryOrderTime,
startingDealTime,
instanceIndex,
host
}, hashes);
})();
}
/**
* Waits for server-side terminal state synchronization to complete
* @param {String} accountId id of the MetaTrader account to synchronize
* @param {Number} [instanceNumber] instance index number
* @param {String} applicationPattern MetaApi application regular expression pattern, default is .*
* @param {Number} timeoutInSeconds timeout in seconds, default is 300 seconds
* @param {String} [application] application to synchronize with
* @returns {Promise} promise which resolves when synchronization started
*/ waitSynchronized(accountId, instanceNumber, applicationPattern, timeoutInSeconds, application) {
return this.rpcRequest(accountId, {
type: 'waitSynchronized',
applicationPattern,
timeoutInSeconds,
instanceIndex: instanceNumber,
application: application || this._application
}, timeoutInSeconds + 1);
}
/**
* Market data subscription
* @typedef {Object} MarketDataSubscription
* @property {string} type subscription type, one of quotes, candles, ticks, or marketDepth
* @property {string} [timeframe] when subscription type is candles, defines the timeframe according to which the
* candles must be generated. Allowed values for MT5 are 1m, 2m, 3m, 4m, 5m, 6m, 10m, 12m, 15m, 20m, 30m, 1h, 2h, 3h,
* 4h, 6h, 8h, 12h, 1d, 1w, 1mn. Allowed values for MT4 are 1m, 5m, 15m 30m, 1h, 4h, 1d, 1w, 1mn
* @property {number} [intervalInMilliseconds] defines how frequently the terminal will stream data to client. If not
* set, then the value configured in account will be used
*/ /**
* Subscribes on market data of specified symbol
* @param {String} accountId id of the MetaTrader account
* @param {String} symbol symbol (e.g. currency pair or an index)
* @param {Array<MarketDataSubscription>} subscriptions array of market data subscription to create or update
* @param {String} [reliability] account reliability
* @returns {Promise} promise which resolves when subscription request was processed
*/ subscribeToMarketData(accountId, symbol, subscriptions, reliability) {
return this.rpcRequestAllInstances(accountId, {
type: 'subscribeToMarketData',
symbol,
subscriptions
}, reliability);
}
/**
* Refreshes market data subscriptions on the server to prevent them from expiring
* @param {String} accountId id of the MetaTrader account
* @param {Number} instanceNumber instance index number
* @param {Array} subscriptions array of subscriptions to refresh
*/ refreshMarketDataSubscriptions(accountId, instanceNumber, subscriptions) {
return this.rpcRequest(accountId, {
type: 'refreshMarketDataSubscriptions',
subscriptions,
instanceIndex: instanceNumber
});
}
/**
* Market data unsubscription
* @typedef {Object} MarketDataUnsubscription
* @property {string} type subscription type, one of quotes, candles, ticks, or marketDepth
*/ /**
* Unsubscribes from market data of specified symbol
* @param {String} accountId id of the MetaTrader account
* @param {String} symbol symbol (e.g. currency pair or an index)
* @param {Array<MarketDataUnsubscription>} subscriptions array of subscriptions to cancel
* @param {String} [reliability] account reliability
* @returns {Promise} promise which resolves when unsubscription request was processed
*/ unsubscribeFromMarketData(accountId, symbol, subscriptions, reliability) {
return this.rpcRequestAllInstances(accountId, {
type: 'unsubscribeFromMarketData',
symbol,
subscriptions
}, reliability);
}
/**
* Retrieves symbols available on an account
* @param {String} accountId id of the MetaTrader account to retrieve symbols for
* @returns {Promise<Array<string>>} promise which resolves when symbols are retrieved
*/ getSymbols(accountId) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, {
application: 'RPC',
type: 'getSymbols'
});
return response.symbols;
})();
}
/**
* Retrieves specification for a symbol
* @param {String} accountId id of the MetaTrader account to retrieve symbol specification for
* @param {String} symbol symbol to retrieve specification for
* @returns {Promise<MetatraderSymbolSpecification>} promise which resolves when specification is retrieved
*/ getSymbolSpecification(accountId, symbol) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, {
application: 'RPC',
type: 'getSymbolSpecification',
symbol
});
return response.specification;
})();
}
/**
* Retrieves price for a symbol
* @param {String} accountId id of the MetaTrader account to retrieve symbol price for
* @param {String} symbol symbol to retrieve price for
* @param {boolean} keepSubscription if set to true, the account will get a long-term subscription to symbol market
* data. Long-term subscription means that on subsequent calls you will get updated value faster. If set to false or
* not set, the subscription will be set to expire in 12 minutes.
* @returns {Promise<MetatraderSymbolPrice>} promise which resolves when price is retrieved
*/ getSymbolPrice(accountId, symbol, keepSubscription = false) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, {
application: 'RPC',
type: 'getSymbolPrice',
symbol,
keepSubscription
});
return response.price;
})();
}
/**
* Retrieves price for a symbol
* @param {string} accountId id of the MetaTrader account to retrieve candle for
* @param {string} symbol symbol to retrieve candle for
* @param {string} timeframe defines the timeframe according to which the candle must be generated. Allowed values for
* MT5 are 1m, 2m, 3m, 4m, 5m, 6m, 10m, 12m, 15m, 20m, 30m, 1h, 2h, 3h, 4h, 6h, 8h, 12h, 1d, 1w, 1mn. Allowed values
* for MT4 are 1m, 5m, 15m 30m, 1h, 4h, 1d, 1w, 1mn
* @param {boolean} keepSubscription if set to true, the account will get a long-term subscription to symbol market
* data. Long-term subscription means that on subsequent calls you will get updated value faster. If set to false or
* not set, the subscription will be set to expire in 12 minutes.
* @returns {Promise<MetatraderCandle>} promise which resolves when candle is retrieved
*/ getCandle(accountId, symbol, timeframe, keepSubscription = false) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, {
application: 'RPC',
type: 'getCandle',
symbol,
timeframe,
keepSubscription
});
return response.candle;
})();
}
/**
* Retrieves latest tick for a symbol. MT4 G1 accounts do not support this API
* @param {string} accountId id of the MetaTrader account to retrieve symbol tick for
* @param {string} symbol symbol to retrieve tick for
* @param {boolean} keepSubscription if set to true, the account will get a long-term subscription to symbol market
* data. Long-term subscription means that on subsequent calls you will get updated value faster. If set to false or
* not set, the subscription will be set to expire in 12 minutes.
* @returns {Promise<MetatraderTick>} promise which resolves when tick is retrieved
*/ getTick(accountId, symbol, keepSubscription = false) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, {
application: 'RPC',
type: 'getTick',
symbol,
keepSubscription
});
return response.tick;
})();
}
/**
* Retrieves latest order book for a symbol. MT4 accounts do not support this API
* @param {string} accountId id of the MetaTrader account to retrieve symbol order book for
* @param {string} symbol symbol to retrieve order book for
* @param {boolean} keepSubscription if set to true, the account will get a long-term subscription to symbol market
* data. Long-term subscription means that on subsequent calls you will get updated value faster. If set to false or
* not set, the subscription will be set to expire in 12 minutes.
* @returns {Promise<MetatraderBook>} promise which resolves when order book is retrieved
*/ getBook(accountId, symbol, keepSubscription = false) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, {
application: 'RPC',
type: 'getBook',
symbol,
keepSubscription
});
return response.book;
})();
}
/**
* Forces refresh of most recent quote updates for symbols subscribed to by the terminal
* @param {string} accountId id of the MetaTrader account
* @returns {Promise<string[]>} promise which resolves with recent quote symbols that was initiated to process
*/ refreshTerminalState(accountId) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, {
application: 'RPC',
type: 'refreshTerminalState'
});
return response.symbols;
})();
}
/**
* Forces refresh and retrieves latest quotes for a subset of symbols the terminal is subscribed to
* @param {string} accountId id of the MetaTrader account
* @param {string[]} symbols quote symbols to refresh
* @returns {Promise<RefreshedQuotes>} refreshed quotes and basic account information info
*/ refreshSymbolQuotes(accountId, symbols) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, {
application: 'RPC',
type: 'refreshSymbolQuotes',
symbols
});
return response.refreshedQuotes;
})();
}
/**
* Sends client uptime stats to the server.
* @param {String} accountId id of the MetaTrader account to save uptime
* @param {Object} uptime uptime statistics to send to the server
* @returns {Promise} promise which resolves when uptime statistics is submitted
*/ saveUptime(accountId, uptime) {
return this.rpcRequest(accountId, {
type: 'saveUptime',
uptime
});
}
/**
* Unsubscribe from account
* @param {String} accountId id of the MetaTrader account to unsubscribe
* @returns {Promise} promise which resolves when socket unsubscribed
*/ unsubscribe(accountId) {
var _this = this;
return _async_to_generator(function*() {
const region = _this.getAccountRegion(accountId);
_this._latencyService.onUnsubscribe(accountId);
Object.keys(_this._updateEvents).filter((key)=>key.startsWith(accountId)).forEach((key)=>delete _this._updateEvents[key]);
if (_this._socketInstances[region]) {
yield Promise.all(Object.keys(_this._socketInstances[region]).map(function() {
var _ref = _async_to_generator(function*(instanceNumber) {
try {
yield _this._subscriptionManager.unsubscribe(accountId, Number(instanceNumber));
delete _this._socketInstancesByAccounts[instanceNumber][accountId];
} catch (err) {
if (![
'TimeoutError',
'NotFoundError'
].includes(err.name)) {
_this._logger.warn(`${accountId}:${instanceNumber}: failed to unsubscribe`, err);
}
}
});
return function(instanceNumber) {
return _ref.apply(this, arguments);
};
}()));
}
})();
}
/**
* Current server time (see https://metaapi.cloud/docs/client/models/serverTime/)
* @typedef {Object} ServerTime
* @property {Date} time current server time
* @property {String} brokerTime current broker time, in broker timezone, YYYY-MM-DD HH:mm:ss.SSS format
* @property {Date} [lastQuoteTime] last quote time
* @property {String} [lastQuoteBrokerTime] last quote time, in broker timezone, YYYY-MM-DD HH:mm:ss.SSS format
*/ /**
* Returns server time for a specified MetaTrader account
* @param {string} accountId id of the MetaTrader account to return server time for
* @returns {Promise<ServerTime>} promise resolving with server time
*/ getServerTime(accountId) {
var _this = this;
return _async_to_generator(function*() {
let response = yield _this.rpcRequest(accountId, {
application: 'RPC',
type: 'getServerTime'
});
return response.serverTime;
})();
}
/**
* Margin required to open a trade (see https://metaapi.cloud/docs/client/models/margin/)
* @typedef {Object} Margin
* @property {number} [margin] margin required to open a trade. If margin can not be calculated, then this field is
* not defined
*/ /**
* Contains order to calculate margin for (see https://metaapi.cloud/docs/client/models/marginOrder/)
* @typedef {Object} MarginOrder
* @property {string} symbol order symbol
* @property {string} type order type, one of ORDER_TYPE_BUY or ORDER_TYPE_SELL
* @property {number} volume order volume, must be greater than 0
* @property {number} openPrice order open price, must be greater than 0
*/ /**
* Calculates margin required to open a trade on the specified trading account
* @param {string} accountId id of the trading account to calculate margin for
* @param {string} application application to send the request to
* @param {string} reliability account reliability
* @param {MarginOrder} order order to calculate margin for
* @returns {Promise<Margin>} promise resolving with margin calculation result
*/ calculateMargin(accountId, application, reliability, order) {
var _this = this;
return _async_to_generator(function*() {
let response;
if (application === 'RPC') {
response = yield _this.rpcRequest(accountId, {
application,
type: 'calculateMargin',
order
});
} else {
response = yield _this.rpcRequestAllInstances(accountId, {
application,
type: 'calculateMargin',
order
}, reliability);
}
return response.margin;
})();
}
/**
* Calls onUnsubscribeRegion listener event
* @param {String} accountId account id
* @param {String} region account region to unsubscribe
*/ unsubscribeAccountRegion(accountId, region) {
var _this = this;
return _async_to_generator(function*() {
const unsubscribePromises = [];
for (let listener of _this._synchronizationListeners[accountId] || []){
unsubscribePromises.push(_this._processEvent(()=>listener.onUnsubscribeRegion(region), `${accountId}:${region}:onUnsubscribeRegion`, true).catch((err)=>_this._logger.error(`${accountId}:${region}: Failed to notify listener about onUnsubscribeRegion event`, err)));
}
yield Promise.all(unsubscribePromises);
})();
}
/**
* Adds synchronization listener for specific account
* @param {String} accountId account id
* @param {SynchronizationListener} listener synchronization listener to add
*/ addSynchronizationListener(accountId, listener) {
this._logger.trace(`${accountId}: Added synchronization listener`);
let listeners = this._synchronizationListeners[accountId];
if (!listeners) {
listeners = [];
this._synchronizationListeners[accountId] = listeners;
}
listeners.push(listener);
}
/**
* Removes synchronization listener for specific account
* @param {String} accountId account id
* @param {SynchronizationListener} listener synchronization listener to remove
*/ removeSynchronizationListener(accountId, listener) {
this._logger.trace(`${accountId}: Removed synchronization listener`);
let listeners = this._synchronizationListeners[accountId];
if (!listeners) {
listeners = [];
}
listeners = listeners.filter((l)=>l !== listener);
this._synchronizationListeners[accountId] = listeners;
}
/**
* Adds latency listener
* @param {LatencyListener} listener latency listener to add
*/ addLatencyListener(listener) {
this._latencyListeners.push(listener);
}
/**
* Removes latency listener
* @param {LatencyListener} listener latency listener to remove
*/ removeLatencyListener(listener) {
this._latencyListeners = this._latencyListeners.filter((l)=>l !== listener);
}
/**
* Adds reconnect listener
* @param {ReconnectListener} listener reconnect listener to add
* @param {String} accountId account id of listener
*/ addReconnectListener(listener, accountId) {
this._reconnectListeners.push({
accountId,
listener
});
}
/**
* Removes reconnect listener
* @param {ReconnectListener} listener listener to remove
*/ removeReconnectListener(listener) {
this._reconnectListeners = this._reconnectListeners.filter((l)=>l.listener !== listener);
}
/**
* Removes all listeners. Intended for use in unit tests.
*/ removeAllListeners() {
this._synchronizationListeners = {};
this._reconnectListeners = [];
}
/**
* Clears account or replica data from client records and unsubscribes
* @param {string} accountId account id to process the removal of
*/ onAccountDeleted(accountId) {
this._subscriptionManager.cancelAccount(accountId);
this._latencyService.onUnsubscribe(accountId);
const masterAccountId = this._accountsByReplicaId[accountId];
if (masterAccountId) {
if (masterAccountId === accountId) {
const regionData = this._accountReplicas[masterAccountId];
const replicas = Object.values(regionData);
Object.keys(this._synchronizationIdByInstance).filter((instance)=>instance.startsWith(`${masterAccountId}`)).forEach((instance)=>delete this._synchronizationIdByInstance[instance]);
replicas.forEach((replica)=>{
Object.values(this._socketInstancesByAccounts).forEach((instance)=>delete instance[replica]);
delete this._accountsByReplicaId[replica];
delete this._regionsByAccounts[replica];
});
delete this._accountReplicas[masterAccountId];
this._logger.debug(`${masterAccountId}: processed primary account removal`);
} else {
Object.values(this._socketInstancesByAccounts).forEach((instance)=>delete instance[accountId]);
const regionData = this._regionsByAccounts[accountId];
if (regionData) {
const region = regionData.region;
Object.keys(this._synchronizationIdByInstance).filter((instance)=>instance.startsWith(`${masterAccountId}:${region}`)).forEach((instance)=>delete this._synchronizationIdByInstance[instance]);
delete this._accountReplicas[masterAccountId][region];
this._logger.debug(`${masterAccountId}: processed removal of replica ${accountId}`);
}
delete this._accountsByReplicaId[accountId];
delete this._regionsByAccounts[accountId];
}
}
}
/**
* Queues an account packet for processing
* @param {Object} packet packet to process
*/ queuePacket(socket, packet) {
const accountId = packet.accountId;
const packets = this._packetOrderer.restoreOrder({
accountId: packet.accountId,
host: packet.host,
instanceIndex: packet.instanceIndex,
sequenceNumber: packet.sequenceNumber,
sequenceTimestamp: packet.sequenceTimestamp,
synchronizationId: packet.synchronizationId,
type: packet.type,
socket,
packet
}).filter((p)=>p.type !== 'noop');
if (this._sequentialEventProcessing && packet.sequenceNumber !== undefined) {
const events = packets.map((item)=>()=>this._processSynchronizationPacket(item.socket, item.packet));
if (!this._eventQueues[accountId]) {
this._eventQueues[accountId] = events;
this._callAccountEvents(accountId);
} else {
this._eventQueues[accountId] = this._eventQueues[accountId].concat(events);
}
} else {
packets.forEach((item)=>this._processSynchronizationPacket(item.socket, item.packet));
}
}
/**
* Queues account event for processing
* @param {String} accountId account id
* @param {String} name event label name
* @param {Function} callable async or regular function to execute
*/ queueEvent(accountId, name, callable) {
let event = ()=>this._processEvent(callable, `${accountId}:${name}`);
if (this._sequentialEventProcessing) {
if (!this._eventQueues[accountId]) {
this._eventQueues[accountId] = [
event
];
this._callAccountEvents(accountId);
} else {
this._eventQueues[accountId].push(event);
}
} else {
event();
}
}
_callAccountEvents(accountId) {
var _this = this;
return _async_to_generator(function*() {
if (_this._eventQueues[accountId]) {
while(_this._eventQueues[accountId].length){
yield _this._eventQueues[accountId][0]();
_this._eventQueues[accountId].shift();
}
delete _this._eventQueues[accountId];
}
})();
}
_reconnect(instanceNumber, socketInstanceIndex, region) {
var _this = this;
return _async_to_generator(function*() {
const instance = _this.socketInstances[region][instanceNumber][socketInstanceIndex];
if (instance) {
while(!instance.socket.connected && !instance.isReconnecting && instance.connected){
yield _this._tryReconnect(instanceNumber, socketInstanceIndex, region);
}
}
})();
}
_tryReconnect(instanceNumber, socketInstanceIndex, region) {
const instance = this.socketInstances[region][instanceNumber][socketInstanceIndex];
instance.reconnectWaitTime = Math.min(instance.reconnectWaitTime * 2, 30000);
var _this = this;
return new Promise((resolve)=>setTimeout(/*#__PURE__*/ _async_to_generator(function*() {
if (!instance.socket.connected && !instance.isReconnecting && instance.connected) {
try {
instance.sessionId = randomstring.generate(32);
const clientId = Math.random();
instance.socket.close();
instance.socket.io.opts.extraHeaders['Client-Id'] = clientId;
instance.socket.io.opts.query.clientId = clientId;
instance.isReconnecting = true;
instance.socket.io.uri = yield _this._getServerUrl(instanceNumber, socketInstanceIndex, region);
instance.socket.connect();
} catch (error) {
instance.isReconnecting = false;
}
}
resolve();
}), instance.reconnectWaitTime));
}
/**
* Simulataneously sends RPC requests to all synchronized instances
* @param {String} accountId metatrader account id
* @param {Object} request base request data
* @param {String} [reliability] account reliability
* @param {Number} [timeoutInSeconds] request timeout in seconds
*/ rpcRequestAllInstances(accountId, request, reliability, timeoutInSeconds) {
var _this = this;
return _async_to_generator(function*() {
if (reliability === 'high') {
try {
return yield any([
0,
1
].map((instanceNumber)=>{
return _this.rpcRequest(accountId, Object.assign({}, request, {
instanceIndex: instanceNumber
}), timeoutInSeconds);
}));
} catch (error) {
throw error.errors[0];
}
} else {
return yield _this.rpcRequest(accountId, request, timeoutInSeconds);
}
})();
}
/**
* Makes a RPC request
* @param {String} accountId replica account id
* @param {Object} request base request data
* @param {Number} [timeoutInSeconds] request timeout in seconds
*/ //eslint-disable-next-line complexity, max-statements
rpcRequest(accountId, request, timeoutInSeconds) {
var _this = this;
return _async_to_generator(function*() {
var _this__socketInstancesByAccounts, _instanceNumber, _this__socketInstances, _region, _this__socketInstances_region, _instanceNumber1;
const ignoredRequestTypes = [
'subscribe',
'synchronize',
'refreshMarketDataSubscriptions',
'unsubscribe'
];
const primaryAccountId = _this._accountsByReplicaId[accountId];
let connectedInstance = _this._latencyService.getActiveAccountInstances(primaryAccountId)[0];
if (!ignoredRequestTypes.includes(request.type)) {
connectedInstance || (connectedInstance = yield _this._latencyService.waitConnectedInstance(accountId));
const activeRegion = connectedInstance.split(':')[1];
accountId = _this._accountReplicas[primaryAccountId][activeRegion];
}
let socketInstanceIndex = null;
let instanceNumber = 0;
const region = _this.getAccountRegion(accountId);
_this._refreshAccountRegion(accountId);
if (request.instanceIndex !== undefined) {
instanceNumber = request.instanceIndex;
} else {
if (connectedInstance) {
instanceNumber = Number(connectedInstance.split(':')[2]);
}
if (request.application !== 'RPC') {
request = Object.assign({}, request, {
instanceIndex: instanceNumber
});
}
}
(_this__socketInstancesByAccounts = _this._socketInstancesByAccounts)[_instanceNumber = instanceNumber] || (_this__socketInstancesByAccounts[_instanceNumber] = {});
(_this__socketInstances = _this._socketInstances)[_region = region] || (_this__socketInstances[_region] = {});
(_this__socketInstances_region = _this._socketInstances[region])[_instanceNumber1 = instanceNumber] || (_this__socketInstances_region[_instanceNumber1] = []);
if (_this._socketInstancesByAccounts[instanceNumber][accountId] !== undefined) {
socketInstanceIndex = _this._socketInstancesByAccounts[instanceNumber][accountId];
} else {
_this._logger.debug(`${accountId}:${instanceNumber}: creating socket instance on RPC request`);
yield _this._createSocketInstanceByAccount(accountId, instanceNumber);
socketInstanceIndex = _this._socketInstancesByAccounts[instanceNumber][accountId];
}
const instance = _this._socketInstances[region][instanceNumber][socketInstanceIndex];
if (!instance.connected) {
_this._logger.debug(`${accountId}:${instanceNumber}: connecting socket instance on RPC request`);
yield _this.connect(instanceNumber, region);
} else if (!_this.connected(instanceNumber, socketInstanceIndex, region)) {
yield instance.connectResult;
}
if (request.type === 'subscribe') {
request.sessionId = instance.sessionId;
}
if ([
'trade',
'subscribe'
].includes(request.type)) {
return _this._makeRequest(accountId, instanceNumber, request, timeoutInSeconds);
}
let retryCounter = 0;
while(true){
try {
return yield _this._makeRequest(accountId, instanceNumber, request, timeoutInSeconds);
} catch (err) {
if (err.name === 'TooManyRequestsError') {
let calcRetryCounter = retryCounter;
let calcRequestTime = 0;
while(calcRetryCounter < _this._retries){
calcRetryCounter++;
calcRequestTime += Math.min(Math.pow(2, calcRetryCounter) * _this._minRetryDelayInSeconds, _this._maxRetryDelayInSeconds) * 1000;
}
const retryTime = new Date(err.metadata.recommendedRetryTime).getTime();
if (Date.now() + calcRequestTime > retryTime && retryCounter < _this._retries) {
if (Date.now() < retryTime) {
yield new Promise((res)=>setTimeout(res, retryTime - Date.now()));
}
retryCounter++;
} else {
throw err;
}
} else if ([
'NotSynchronizedError',
'TimeoutError',
'NotAuthenticatedError',
'InternalError'
].includes(err.name) && retryCounter < _this._retries) {
yield new Promise((res)=>setTimeout(res, Math.min(Math.pow(2, retryCounter) * _this._minRetryDelayInSeconds, _this._maxRetryDelayInSeconds) * 1000));
retryCounter++;
} else {
throw err;
}
if (_this._socketInstancesByAccounts[instanceNumber][accountId] === undefined) {
throw err;
}
}
}
})();
}
_makeRequest(accountId, instanceNumber, request, timeoutInSeconds) {
const socketInstance = this._getSocketInstanceByAccount(accountId, instanceNumber);
let requestId = request.requestId || randomstring.generate(32);
request.timestamps = {
clientProcessingStarted: new Date()
};
let result = Promise.race([
new Promise((resolve, reject)=>socketInstance.requestResolves[requestId] = {
resolve,
reject,
type: request.type
}),
new Promise((resolve, reject)=>setTimeout(()=>{
reject(new TimeoutError(`MetaApi websocket client request ${request.requestId} of type ${request.type} ` + 'timed out. Please make sure your account is connected to broker before retrying your request.'));
delete socketInstance.requestResolves[requestId];
}, timeoutInSeconds * 1000 || this._requestTimeout))
]);
request.accountId = accountId;
request.application = request.application || this._application;
if (!request.requestId) {
request.requestId = requestId;
}
if (request.type === 'unsubscribe' || request.application === 'RPC' || request.instanceIndex === socketInstance.instanceNumber) {
this._logger.debug(()=>`${accountId}: Sending request: ${JSON.stringify(request)}`);
socketInstance.socket.emit('request', request);
return result;
} else {
this._logger.trace(()=>`${accountId}:${request.instanceIndex}: skipping request because it is being sent to ` + `the socket of the wrong instance index, request=${JSON.stringify(request)}`);
return result;
}
}
// eslint-disable-next-line complexity
_convertError(data) {
if (data.error === 'ValidationError') {
return new ValidationError(data.message, data.details);
} else if (data.error === 'NotFoundError') {
return new NotFoundError(data.message);
} else if (data.error === 'NotSynchronizedError') {
return new NotSynchronizedError(data.message);
} else if (data.error === 'TimeoutError') {
return new TimeoutError(data.message);
} else if (data.error === 'NotAuthenticatedError') {
return new NotConnectedError(data.message);
} else if (data.error === 'ForbiddenError') {
return new ForbiddenError(data.message);
} else if (data.error === 'TradeError') {
return new TradeError(data.message, data.numericCode, data.stringCode);
} else if (data.error === 'UnauthorizedError') {
this.close();
return new UnauthorizedError(data.message);
} else if (data.error === 'TooManyRequestsError') {
return new TooManyRequestsError(data.message, data.metadata);
} else {
return new InternalError(data.message);
}
}
// eslint-disable-next-line complexity
_convertIsoTimeToDate(packet) {
// eslint-disable-next-line guard-for-in
for(let field in packet){
let value = packet[field];
if (typeof value === 'string' && field.match(/time$|Time$/) && !field.match(/brokerTime$|BrokerTime$|timeframe$/)) {
packet[field] = new Date(value);
}
if (Array.isArray(value)) {
for (let item of value){
this._convertIsoTimeToDate(item);
}
}
if (typeof value === 'object') {
this._convertIsoTimeToDate(value);
}
}
if (packet && packet.timestamps) {
// eslint-disable-next-line guard-for-in
for(let field in packet.timestamps){
packet.timestamps[field] = new Date(packet.timestamps[field]);
}
}
if (packet && packet.type === 'prices') {
for (let price of packet.prices || []){
if (price.timestamps) {
// eslint-disable-next-line guard-for-in
for(let field in price.timestamps){
price.timestamps[field] = new Date(price.timestamps[field]);
}
}
}
}
}
/**
* MetaTrader symbol price. Contains current price for a symbol (see
* https://metaapi.cloud/docs/client/models/metatraderSymbolPrice/)
* @typedef {Object} MetatraderSymbolPrice
* @property {String} symbol symbol (e.g. a currency pair or an index)
* @property {Number} bid bid price
* @property {Number} ask ask price
* @property {Number} profitTickValue tick value for a profitable position
* @property {Number} lossTickValue tick value for a losing position
* @property {Number} [accountCurrencyExchangeRate] current exchange rate of account currency into account base
* currency (USD if you did not override it)
* @property {Date} time quote time, in ISO format
* @property {String} brokerTime time quote time, in broker timezone, YYYY-MM-DD HH:mm:ss.SSS format
*/ /**
* MetaTrader candle
* @typedef {Object} MetatraderCandle
* @property {string} symbol symbol (e.g. currency pair or an index)
* @property {string} timeframe timeframe candle was generated for, e.g. 1h. One of 1m, 2m, 3m, 4m, 5m, 6m, 10m, 12m,
* 15m, 20m, 30m, 1h, 2h, 3h, 4h, 6h, 8h, 12h, 1d, 1w, 1mn
* @property {Date} time candle opening time
* @property {string} brokerTime candle opening time, in broker timezone, YYYY-MM-DD HH:mm:ss.SSS format
* @property {number} open open price
* @property {number} high high price
* @property {number} low low price
* @property {number} close close price
* @property {number} tickVolume tick volume, i.e. number of ticks inside the candle
* @property {number} spread spread in points
* @property {number} volume trade volume
*/ /**
* MetaTrader tick data
* @typedef {Object} MetatraderTick
* @property {string} symbol symbol (e.g. a currency pair or an index)
* @property {Date} time time
* @property {string} brokerTime time, in broker timezone, YYYY-MM-DD HH:mm:ss.SSS format
* @property {number} [bid] bid price
* @property {number} [ask] ask price
* @property {number} [last] last deal price
* @property {number} [volume] volume for the current last deal price
* @property {string} side is tick a result of buy or sell deal, one of buy or sell
*/ /**
* MetaTrader order book
* @typedef {Object} MetatraderBook
* @property {string} symbol symbol (e.g. a currency pair or an index)
* @property {Date} time time
* @property {string} brokerTime time, in broker timezone, YYYY-MM-DD HH:mm:ss.SSS format
* @property {Array<MetatraderBookEntry>} book list of order book entries
*/ /**
* MetaTrader order book entry
* @typedef {Object} MetatraderBookEntry
* @property {string} type entry type, one of BOOK_TYPE_SELL, BOOK_TYPE_BUY, BOOK_TYPE_SELL_MARKET,
* BOOK_TYPE_BUY_MARKET
* @property {number} price price
* @property {number} volume volume
*/ // eslint-disable-next-line complexity,max-statements
_processSynchronizationPacket(socket, data) {
var _this = this;
return _async_to_generator(function*() {
try {
const instanceNumber = data.instanceIndex || 0;
const socketInstance = _this._getSocketInstanceByAccount(data.accountId, instanceNumber);
if (data.synchronizationId && socketInstance) {
socketInstance.synchronizationThrottler.updateSynchronizationId(data.synchronizationId);
}
const region = _this.getAccountRegion(data.accountId);
const primaryAccountId = _this._accountsByReplicaId[data.accountId];
let instanceId = primaryAccountId + ':' + region + ':' + instanceNumber + ':' + (data.host || 0);
let instanceIndex = region + ':' + instanceNumber + ':' + (data.host || 0);
let sourceLabel = _.compact([
instanceIndex,
socket.clientApiHostname
]).join(':');
const isOnlyActiveInstance = ()=>{
const activeInstanceIds = Object.keys(_this._connectedHosts).filter((instance)=>instance.startsWith(primaryAccountId + ':' + region + ':' + instanceNumber));
return !activeInstanceIds.length || activeInstanceIds.length === 1 && activeInstanceIds[0] === instanceId;
};
const cancelDisconnectTimer = ()=>{
if (_this._statusTimers[instanceId]) {
clearTimeout(_this._statusTimers[instanceId]);
}
};
const resetDisconnectTimer = ()=>{
cancelDisconnectTimer();
_this._statusTimers[instanceId] = setTimeout(()=>{
_this._logger.warn(`${data.accountId}:${sourceLabel}: timed out waiting for connection status`);
if (isOnlyActiveInstance()) {
_this._subscriptionManager.onTimeout(data.accountId, 0);
_this._subscriptionManager.onTimeout(data.accountId, 1);
}
_this.queueEvent(primaryAccountId, `${instanceIndex}:onDisconnected`, ()=>onDisconnected(true));
clearTimeout(_this._statusTimers[instanceId]);
}, 60000);
};
// eslint-disable-next-line complexity
const onDisconnected = function() {
var _ref = _async_to_generator(function*(isTimeout = false) {
if (_this._connectedHosts[instanceId]) {
_this._latencyService.onDisconnected(instanceId);
if (isOnlyActiveInstance()) {
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onDisconnected(instanceIndex), `${primaryAccountId}:${sourceLabel}:onDisconnected`);
}
}
_this._packetOrderer.onStreamClosed(instanceId);
socketInstance === null || socketInstance === void 0 ? void 0 : socketInstance.synchronizationThrottler.removeIdByParameters(data.accountId, instanceNumber, data.host);
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onStreamClosed(instanceIndex), `${primaryAccountId}:${sourceLabel}:onStreamClosed`);
}
delete _this._connectedHosts[instanceId];
if (isOnlyActiveInstance() && !isTimeout) {
yield _this._subscriptionManager.onDisconnected(data.accountId, 0);
yield _this._subscriptionManager.onDisconnected(data.accountId, 1);
}
}
});
return function onDisconnected() {
return _ref.apply(this, arguments);
};
}();
if (data.type === 'authenticated') {
resetDisconnectTimer();
if (!data.sessionId || socketInstance && data.sessionId === socketInstance.sessionId) {
_this._latencyService.onConnected(instanceId);
_this._connectedHosts[instanceId] = data.host;
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onConnected(instanceIndex, data.replicas), `${primaryAccountId}:${sourceLabel}:onConnected`);
}
_this._subscriptionManager.cancelSubscribe(data.accountId + ':' + instanceNumber);
if (data.replicas === 1) {
_this._subscriptionManager.cancelAccount(data.accountId);
} else {
_this._subscriptionManager.cancelSubscribe(data.accountId + ':' + instanceNumber);
}
}
} else if (data.type === 'disconnected') {
cancelDisconnectTimer();
yield onDisconnected();
} else if (data.type === 'synchronizationStarted') {
_this._updateEvents[instanceId] = [];
_this._synchronizationFlags[data.synchronizationId] = {
accountId: data.accountId,
instanceNumber,
specificationsUpdated: data.specificationsHashIndex === undefined,
positionsUpdated: data.positionsHashIndex === undefined,
ordersUpdated: data.ordersHashIndex === undefined
};
_this._synchronizationIdByInstance[instanceId] = data.synchronizationId;
const specificationsHash = data.specificationsHashIndex !== undefined ? _this._synchronizationHashes[data.synchronizationId] && _this._synchronizationHashes[data.synchronizationId].specificationsHashes[data.specificationsHashIndex] : undefined;
const positionsHash = data.positionsHashIndex !== undefined ? _this._synchronizationHashes[data.synchronizationId] && _this._synchronizationHashes[data.synchronizationId].positionsHashes[data.positionsHashIndex] : undefined;
const ordersHash = data.ordersHashIndex !== undefined ? _this._synchronizationHashes[data.synchronizationId] && _this._synchronizationHashes[data.synchronizationId].ordersHashes[data.ordersHashIndex] : undefined;
delete _this._synchronizationHashes[data.synchronizationId];
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onSynchronizationStarted(instanceIndex, specificationsHash, positionsHash, ordersHash, data.synchronizationId), `${primaryAccountId}:${sourceLabel}:onSynchronizationStarted`);
}
} else if (data.type === 'accountInformation') {
if (data.synchronizationId && data.synchronizationId !== _this._synchronizationIdByInstance[instanceId]) {
return;
}
if (data.accountInformation) {
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
try {
yield _this._processEvent(()=>listener.onAccountInformationUpdated(instanceIndex, data.accountInformation), `${primaryAccountId}:${sourceLabel}:onAccountInformationUpdated`, true);
// eslint-disable-next-line max-depth
if (_this._synchronizationFlags[data.synchronizationId] && !_this._synchronizationFlags[data.synchronizationId].positionsUpdated) {
yield _this._processEvent(()=>listener.onPositionsSynchronized(instanceIndex, data.synchronizationId), `${primaryAccountId}:${sourceLabel}:onPositionsSynchronized`, true);
// eslint-disable-next-line max-depth
if (!_this._synchronizationFlags[data.synchronizationId].ordersUpdated) {
yield _this._processEvent(()=>listener.onPendingOrdersSynchronized(instanceIndex, data.synchronizationId), `${primaryAccountId}:${sourceLabel}:onPendingOrdersSynchronized`, true);
}
}
} catch (err) {
_this._logger.error(`${primaryAccountId}:${sourceLabel}: Failed to notify listener ` + 'about accountInformation event', err);
}
}
if (_this._synchronizationFlags[data.synchronizationId] && !_this._synchronizationFlags[data.synchronizationId].positionsUpdated && !_this._synchronizationFlags[data.synchronizationId].ordersUpdated) {
delete _this._synchronizationFlags[data.synchronizationId];
}
}
} else if (data.type === 'deals') {
if (data.synchronizationId && data.synchronizationId !== _this._synchronizationIdByInstance[instanceId]) {
return;
}
for (let deal of data.deals || []){
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onDealAdded(instanceIndex, deal), `${primaryAccountId}:${sourceLabel}:onDealAdded`);
}
}
} else if (data.type === 'orders') {
if (data.synchronizationId && data.synchronizationId !== _this._synchronizationIdByInstance[instanceId]) {
return;
}
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
try {
yield _this._processEvent(()=>listener.onPendingOrdersReplaced(instanceIndex, data.orders || []), `${primaryAccountId}:${sourceLabel}:onPendingOrdersReplaced`, true);
yield _this._processEvent(()=>listener.onPendingOrdersSynchronized(instanceIndex, data.synchronizationId), `${primaryAccountId}:${sourceLabel}:onPendingOrdersSynchronized`, true);
} catch (err) {
_this._logger.error(`${primaryAccountId}:${sourceLabel}: Failed to notify listener ` + 'about orders event', err);
}
}
if (_this._synchronizationFlags[data.synchronizationId]) {
delete _this._synchronizationFlags[data.synchronizationId];
}
} else if (data.type === 'historyOrders') {
if (data.synchronizationId && data.synchronizationId !== _this._synchronizationIdByInstance[instanceId]) {
return;
}
for (let historyOrder of data.historyOrders || []){
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onHistoryOrderAdded(instanceIndex, historyOrder), `${primaryAccountId}:${sourceLabel}:onHistoryOrderAdded`);
}
}
} else if (data.type === 'positions') {
if (data.synchronizationId && data.synchronizationId !== _this._synchronizationIdByInstance[instanceId]) {
return;
}
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
try {
yield _this._processEvent(()=>listener.onPositionsReplaced(instanceIndex, data.positions || []), `${primaryAccountId}:${sourceLabel}:onPositionsReplaced`, true);
yield _this._processEvent(()=>listener.onPositionsSynchronized(instanceIndex, data.synchronizationId), `${primaryAccountId}:${sourceLabel}:onPositionsSynchronized`, true);
if (_this._synchronizationFlags[data.synchronizationId] && !_this._synchronizationFlags[data.synchronizationId].ordersUpdated) {
yield _this._processEvent(()=>listener.onPendingOrdersSynchronized(instanceIndex, data.synchronizationId), `${primaryAccountId}:${sourceLabel}:onPendingOrdersSynchronized`, true);
}
} catch (err) {
_this._logger.error(`${primaryAccountId}:${sourceLabel}: Failed to notify listener ` + 'about positions event', err);
}
}
if (_this._synchronizationFlags[data.synchronizationId] && !_this._synchronizationFlags[data.synchronizationId].ordersUpdated) {
delete _this._synchronizationFlags[data.synchronizationId];
}
} else if (data.type === 'update') {
var _this__updateEvents_instanceId;
(_this__updateEvents_instanceId = _this._updateEvents[instanceId]) === null || _this__updateEvents_instanceId === void 0 ? void 0 : _this__updateEvents_instanceId.push({
socket,
data
});
if (data.accountInformation) {
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onAccountInformationUpdated(instanceIndex, data.accountInformation), `${primaryAccountId}:${sourceLabel}:onAccountInformationUpdated`);
}
}
const updatedPositions = data.updatedPositions || [];
const removedPositionIds = data.removedPositionIds || [];
if (updatedPositions.length || removedPositionIds.length) {
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onPositionsUpdated(instanceIndex, updatedPositions, removedPositionIds), `${primaryAccountId}:${sourceLabel}:onPositionsUpdated`);
}
}
for (let position of updatedPositions){
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onPositionUpdated(instanceIndex, position), `${primaryAccountId}:${sourceLabel}:onPositionUpdated`);
}
}
for (let positionId of removedPositionIds){
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onPositionRemoved(instanceIndex, positionId), `${primaryAccountId}:${sourceLabel}:onPositionRemoved`);
}
}
const updatedOrders = data.updatedOrders || [];
const completedOrderIds = data.completedOrderIds || [];
if (updatedOrders.length || completedOrderIds.length) {
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onPendingOrdersUpdated(instanceIndex, updatedOrders, completedOrderIds), `${primaryAccountId}:${sourceLabel}:onPendingOrdersUpdated`);
}
}
for (let order of updatedOrders){
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onPendingOrderUpdated(instanceIndex, order), `${primaryAccountId}:${sourceLabel}:onPendingOrderUpdated`);
}
}
for (let orderId of completedOrderIds){
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onPendingOrderCompleted(instanceIndex, orderId), `${primaryAccountId}:${sourceLabel}:onPendingOrderCompleted`);
}
}
for (let historyOrder of data.historyOrders || []){
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onHistoryOrderAdded(instanceIndex, historyOrder), `${primaryAccountId}:${sourceLabel}:onHistoryOrderAdded`);
}
}
for (let deal of data.deals || []){
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onDealAdded(instanceIndex, deal), `${primaryAccountId}:${sourceLabel}:onDealAdded`);
}
}
if (data.timestamps) {
data.timestamps.clientProcessingFinished = new Date();
// eslint-disable-next-line max-depth
for (let listener of _this._latencyListeners || []){
yield _this._processEvent(()=>listener.onUpdate(data.accountId, data.timestamps), `${primaryAccountId}:${sourceLabel}:onUpdate`);
}
}
} else if (data.type === 'dealSynchronizationFinished') {
if (data.synchronizationId && data.synchronizationId !== _this._synchronizationIdByInstance[instanceId]) {
delete _this._synchronizationIdByInstance[instanceId];
return;
}
_this._latencyService.onDealsSynchronized(instanceId);
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
socketInstance === null || socketInstance === void 0 ? void 0 : socketInstance.synchronizationThrottler.removeSynchronizationId(data.synchronizationId);
yield _this._processEvent(()=>listener.onDealsSynchronized(instanceIndex, data.synchronizationId), `${primaryAccountId}:${sourceLabel}:onDealsSynchronized`);
}
if (_this._updateEvents[instanceId]) {
let eventCalls = _this._updateEvents[instanceId].map((event)=>()=>_this._processSynchronizationPacket(event.socket, event.data));
if (_this._eventQueues[primaryAccountId]) {
_this._eventQueues[primaryAccountId] = eventCalls.concat(_this._eventQueues[primaryAccountId]);
delete _this._updateEvents[instanceId];
} else {
_this._eventQueues[primaryAccountId] = eventCalls;
delete _this._updateEvents[instanceId];
_this._callAccountEvents(primaryAccountId);
}
}
} else if (data.type === 'orderSynchronizationFinished') {
if (data.synchronizationId && data.synchronizationId !== _this._synchronizationIdByInstance[instanceId]) {
return;
}
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onHistoryOrdersSynchronized(instanceIndex, data.synchronizationId), `${primaryAccountId}:${sourceLabel}:onHistoryOrdersSynchronized`);
}
} else if (data.type === 'status') {
if (!_this._connectedHosts[instanceId]) {
if (_this._statusTimers[instanceId] && data.authenticated && (_this._subscriptionManager.isDisconnectedRetryMode(data.accountId, instanceNumber) || !_this._subscriptionManager.isAccountSubscribing(data.accountId, instanceNumber))) {
_this._subscriptionManager.cancelSubscribe(data.accountId + ':' + instanceNumber);
yield new Promise((res)=>setTimeout(res, 10));
// eslint-disable-next-line no-console
_this._logger.info('it seems like we are not connected to a running API server yet, retrying subscription for account' + _.compact([
instanceId,
socket.clientApiHostname
]).join(':'));
_this.ensureSubscribe(data.accountId, instanceNumber);
}
} else {
resetDisconnectTimer();
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onBrokerConnectionStatusChanged(instanceIndex, !!data.connected), `${primaryAccountId}:${sourceLabel}:onBrokerConnectionStatusChanged`);
}
if (data.healthStatus) {
// eslint-disable-next-line max-depth
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onHealthStatus(instanceIndex, data.healthStatus), `${primaryAccountId}:${sourceLabel}:onHealthStatus`);
}
}
}
} else if (data.type === 'downgradeSubscription') {
_this._logger.info(`${primaryAccountId}:${sourceLabel}: Market data subscriptions for symbol ` + `${data.symbol} were downgraded by the server due to rate limits. Updated subscriptions: ` + `${JSON.stringify(data.updates)}, removed subscriptions: ${JSON.stringify(data.unsubscriptions)}. ` + 'Please read https://metaapi.cloud/docs/client/rateLimiting/ for more details.');
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onSubscriptionDowngraded(instanceIndex, data.symbol, data.updates, data.unsubscriptions), `${primaryAccountId}:${sourceLabel}:onSubscriptionDowngraded`);
}
} else if (data.type === 'specifications') {
if (data.synchronizationId && data.synchronizationId !== _this._synchronizationIdByInstance[instanceId]) {
return;
}
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onSymbolSpecificationsUpdated(instanceIndex, data.specifications || [], data.removedSymbols || []), `${primaryAccountId}:${sourceLabel}:onSymbolSpecificationsUpdated`);
}
for (let specification of data.specifications || []){
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onSymbolSpecificationUpdated(instanceIndex, specification), `${primaryAccountId}:${sourceLabel}:onSymbolSpecificationUpdated`);
}
}
for (let removedSymbol of data.removedSymbols || []){
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onSymbolSpecificationRemoved(instanceIndex, removedSymbol), `${primaryAccountId}:${sourceLabel}:onSymbolSpecificationRemoved`);
}
}
} else if (data.type === 'prices') {
if (data.synchronizationId && data.synchronizationId !== _this._synchronizationIdByInstance[instanceId]) {
return;
}
let prices = data.prices || [];
let candles = data.candles || [];
let ticks = data.ticks || [];
let books = data.books || [];
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
if (prices.length) {
yield _this._processEvent(()=>listener.onSymbolPricesUpdated(instanceIndex, prices, data.equity, data.margin, data.freeMargin, data.marginLevel, data.accountCurrencyExchangeRate), `${primaryAccountId}:${sourceLabel}:onSymbolPricesUpdated`);
}
if (candles.length) {
yield _this._processEvent(()=>listener.onCandlesUpdated(instanceIndex, candles, data.equity, data.margin, data.freeMargin, data.marginLevel, data.accountCurrencyExchangeRate), `${primaryAccountId}:${sourceLabel}:onCandlesUpdated`);
}
if (ticks.length) {
yield _this._processEvent(()=>listener.onTicksUpdated(instanceIndex, ticks, data.equity, data.margin, data.freeMargin, data.marginLevel, data.accountCurrencyExchangeRate), `${primaryAccountId}:${sourceLabel}:onTicksUpdated`);
}
if (books.length) {
yield _this._processEvent(()=>listener.onBooksUpdated(instanceIndex, books, data.equity, data.margin, data.freeMargin, data.marginLevel, data.accountCurrencyExchangeRate), `${primaryAccountId}:${sourceLabel}:onBooksUpdated`);
}
}
for (let price of prices){
for (let listener of _this._synchronizationListeners[primaryAccountId] || []){
yield _this._processEvent(()=>listener.onSymbolPriceUpdated(instanceIndex, price), `${primaryAccountId}:${sourceLabel}:onSymbolPriceUpdated`);
}
}
for (let price of prices){
if (price.timestamps) {
price.timestamps.clientProcessingFinished = new Date();
// eslint-disable-next-line max-depth
for (let listener of _this._latencyListeners || []){
yield _this._processEvent(()=>listener.onSymbolPrice(data.accountId, price.symbol, price.timestamps), `${primaryAccountId}:${sourceLabel}:onSymbolPrice`);
}
}
}
}
} catch (err) {
_this._logger.error('Failed to process incoming synchronization packet', err);
}
})();
}
_processEvent(callable, label, throwError) {
var _this = this;
return _async_to_generator(function*() {
const startTime = Date.now();
let isLongEvent = false;
let isEventDone = false;
const checkLongEvent = function() {
var _ref = _async_to_generator(function*() {
yield new Promise((res)=>setTimeout(res, 1000));
if (!isEventDone) {
isLongEvent = true;
_this._logger.warn(`${label}: event is taking more than 1 second to process`);
}
});
return function checkLongEvent() {
return _ref.apply(this, arguments);
};
}();
checkLongEvent();
try {
yield callable();
} catch (err) {
if (throwError) {
throw err;
}
_this._logger.error(`${label}: event failed with error`, err);
}
isEventDone = true;
if (isLongEvent) {
_this._logger.warn(`${label}: finished in ${Math.floor((Date.now() - startTime) / 1000)} seconds`);
}
})();
}
_fireReconnected(instanceNumber, socketInstanceIndex, region) {
var _this = this;
return _async_to_generator(function*() {
try {
const reconnectListeners = [];
for (let listener of _this._reconnectListeners){
if (_this._socketInstancesByAccounts[instanceNumber][listener.accountId] === socketInstanceIndex && _this.getAccountRegion(listener.accountId) === region) {
reconnectListeners.push(listener);
}
}
Object.keys(_this._synchronizationFlags).forEach((synchronizationId)=>{
const accountId = _this._synchronizationFlags[synchronizationId].accountId;
if (_this._socketInstancesByAccounts[instanceNumber][accountId] === socketInstanceIndex && _this._synchronizationFlags[synchronizationId].instanceNumber === instanceNumber && _this._regionsByAccounts[accountId] && _this._regionsByAccounts[accountId].region === region) {
delete _this._synchronizationFlags[synchronizationId];
}
});
const reconnectAccountIds = reconnectListeners.map((listener)=>listener.accountId);
_this._subscriptionManager.onReconnected(instanceNumber, socketInstanceIndex, reconnectAccountIds);
_this._packetOrderer.onReconnected(reconnectAccountIds);
for (let listener of reconnectListeners){
Promise.resolve(listener.listener.onReconnected(region, instanceNumber)).catch((err)=>_this._logger.error('Failed to notify reconnect listener', err));
}
} catch (err) {
_this._logger.error('Failed to process reconnected event', err);
}
})();
}
_getSocketInstanceByAccount(accountId, instanceNumber) {
const region = this.getAccountRegion(accountId);
return this._socketInstances[region][instanceNumber][this._socketInstancesByAccounts[instanceNumber][accountId]];
}
getUrlSettings(instanceNumber, region) {
var _this = this;
return _async_to_generator(function*() {
if (_this._url) {
return {
url: _this._url,
isSharedClientApi: true
};
}
const urlSettings = yield _this._domainClient.getSettings();
const getUrl = (hostname)=>`https://${hostname}.${region}-${String.fromCharCode(97 + Number(instanceNumber))}.${urlSettings.domain}`;
let url;
if (_this._useSharedClientApi) {
url = getUrl(_this._hostname);
} else {
url = getUrl(urlSettings.hostname);
}
const isSharedClientApi = url === getUrl(_this._hostname);
return {
url,
isSharedClientApi
};
})();
}
// eslint-disable-next-line complexity
_getServerUrl(instanceNumber, socketInstanceIndex, region) {
var _this = this;
return _async_to_generator(function*() {
if (_this._url) {
return _this._url;
}
while(_this.socketInstances[region][instanceNumber][socketInstanceIndex].connected){
try {
const urlSettings = yield _this.getUrlSettings(instanceNumber, region);
const url = urlSettings.url;
const isSharedClientApi = urlSettings.isSharedClientApi;
let logMessage = 'Connecting MetaApi websocket client to the MetaApi server ' + `via ${url} ${isSharedClientApi ? 'shared' : 'dedicated'} server.`;
if (_this._firstConnect && !isSharedClientApi) {
logMessage += ' Please note that it can take up to 3 minutes for your dedicated server to start for the ' + 'first time. During this time it is OK if you see some connection errors.';
_this._firstConnect = false;
}
_this._logger.info(logMessage);
return url;
} catch (err) {
_this._logger.error('Failed to retrieve server URL', err);
yield new Promise((res)=>setTimeout(res, 1000));
}
}
})();
}
_throttleRequest(type, accountId, instanceNumber, timeInMs) {
this._lastRequestsTime[instanceNumber] = this._lastRequestsTime[instanceNumber] || {};
this._lastRequestsTime[instanceNumber][type] = this._lastRequestsTime[instanceNumber][type] || {};
let lastTime = this._lastRequestsTime[instanceNumber][type][accountId];
if (!lastTime || lastTime < Date.now() - timeInMs) {
this._lastRequestsTime[instanceNumber][type][accountId] = Date.now();
return !!lastTime;
}
return false;
}
_refreshAccountRegion(accountId) {
if (this._regionsByAccounts[accountId]) {
this._regionsByAccounts[accountId].lastUsed = Date.now();
}
}
//eslint-disable-next-line complexity
_createSocketInstanceByAccount(accountId, instanceNumber) {
var _this = this;
return _async_to_generator(function*() {
const region = _this.getAccountRegion(accountId);
if (_this._socketInstancesByAccounts[instanceNumber][accountId] === undefined) {
let socketInstanceIndex = null;
while(_this._subscribeLock && (new Date(_this._subscribeLock.recommendedRetryTime).getTime() > Date.now() && _this.subscribedAccountIds(instanceNumber, undefined, region).length < _this._subscribeLock.lockedAtAccounts || new Date(_this._subscribeLock.lockedAtTime).getTime() + _this._subscribeCooldownInSeconds * 1000 > Date.now() && _this.subscribedAccountIds(instanceNumber, undefined, region).length >= _this._subscribeLock.lockedAtAccounts)){
yield new Promise((res)=>setTimeout(res, 1000));
}
for(let index = 0; index < _this._socketInstances[region][instanceNumber].length; index++){
const accountCounter = _this._getAssignedAccounts(instanceNumber, index, region).length;
const instance = _this.socketInstances[region][instanceNumber][index];
if (instance.subscribeLock) {
if (instance.subscribeLock.type === 'LIMIT_ACCOUNT_SUBSCRIPTIONS_PER_USER_PER_SERVER' && (new Date(instance.subscribeLock.recommendedRetryTime).getTime() > Date.now() || _this.subscribedAccountIds(instanceNumber, index, region).length >= instance.subscribeLock.lockedAtAccounts)) {
continue;
}
if (instance.subscribeLock.type === 'LIMIT_ACCOUNT_SUBSCRIPTIONS_PER_SERVER' && new Date(instance.subscribeLock.recommendedRetryTime).getTime() > Date.now() && _this.subscribedAccountIds(instanceNumber, index, region).length >= instance.subscribeLock.lockedAtAccounts) {
continue;
}
}
if (accountCounter < _this._maxAccountsPerInstance) {
socketInstanceIndex = index;
break;
}
}
if (socketInstanceIndex === null) {
socketInstanceIndex = _this._socketInstances[region][instanceNumber].length;
yield _this.connect(instanceNumber, region);
}
_this._socketInstancesByAccounts[instanceNumber][accountId] = socketInstanceIndex;
}
})();
}
_clearAccountCacheJob() {
const date = Date.now();
Object.keys(this._regionsByAccounts).forEach((replicaId)=>{
const data = this._regionsByAccounts[replicaId];
if (data && data.connections === 0 && date - data.lastUsed > 2 * 60 * 60 * 1000) {
const primaryAccountId = this._accountsByReplicaId[replicaId];
const replicas = Object.values(this._accountReplicas[primaryAccountId] || {});
replicas.forEach((replica)=>{
delete this._accountsByReplicaId[replica];
delete this._regionsByAccounts[replica];
});
delete this._accountReplicas[primaryAccountId];
this._logger.debug(`${primaryAccountId}: removed expired account replicas data`);
}
});
}
_clearInactiveSyncDataJob() {
const date = Date.now();
Object.keys(this._synchronizationHashes).forEach((synchronizationId)=>{
if (this._synchronizationHashes[synchronizationId].lastUpdated < date - 30 * 60 * 1000) {
delete this._synchronizationHashes[synchronizationId];
}
});
}
/**
* @typedef MetaApiWebsocketClientOptions MetaApi websocket client options
* @property {string} [region] region to connect
* @property {Boolean} [disableInternalJobs] whether to not run internal interval jobs. Used for tests only
*/ /**
* Constructs MetaApi websocket API client instance
* @param {MetaApi} metaApi metaApi instance
* @param {DomainClient} domainClient domain client
* @param {String} token authorization token
* @param {MetaApiWebsocketClientOptions} opts websocket client options
*/ // eslint-disable-next-line complexity,max-statements
constructor(metaApi, domainClient, token, opts){
_define_property(this, "_domainClient", void 0);
_define_property(this, "_application", void 0);
_define_property(this, "_domain", void 0);
_define_property(this, "_region", void 0);
_define_property(this, "_hostname", void 0);
_define_property(this, "_metaApi", void 0);
_define_property(this, "_url", void 0);
_define_property(this, "_requestTimeout", void 0);
_define_property(this, "_connectTimeout", void 0);
_define_property(this, "_retries", void 0);
_define_property(this, "_minRetryDelayInSeconds", void 0);
_define_property(this, "_maxRetryDelayInSeconds", void 0);
_define_property(this, "_maxAccountsPerInstance", void 0);
_define_property(this, "_subscribeCooldownInSeconds", void 0);
_define_property(this, "_sequentialEventProcessing", void 0);
_define_property(this, "_useSharedClientApi", void 0);
_define_property(this, "_unsubscribeThrottlingInterval", void 0);
_define_property(this, "_socketMinimumReconnectTimeout", void 0);
_define_property(this, "_latencyService", void 0);
_define_property(this, "_token", void 0);
_define_property(this, "_synchronizationListeners", void 0);
_define_property(this, "_latencyListeners", void 0);
_define_property(this, "_reconnectListeners", void 0);
_define_property(this, "_connectedHosts", void 0);
_define_property(this, "_socketInstances", {});
_define_property(this, "_socketInstancesByAccounts", {});
_define_property(this, "_regionsByAccounts", {});
_define_property(this, "_accountsByReplicaId", {});
_define_property(this, "_accountReplicas", {});
_define_property(this, "_synchronizationThrottlerOpts", void 0);
_define_property(this, "_subscriptionManager", void 0);
_define_property(this, "_statusTimers", void 0);
_define_property(this, "_eventQueues", {});
_define_property(this, "_synchronizationFlags", {});
_define_property(this, "_synchronizationIdByInstance", void 0);
_define_property(this, "_subscribeLock", void 0);
_define_property(this, "_firstConnect", void 0);
_define_property(this, "_lastRequestsTime", void 0);
_define_property(this, "_packetOrderer", void 0);
_define_property(this, "_synchronizationHashes", void 0);
_define_property(this, "_updateEvents", {});
_define_property(this, "_packetLogger", void 0);
_define_property(this, "_logger", void 0);
_define_property(this, "_clearAccountCacheInterval", void 0);
_define_property(this, "_clearInactiveSyncDataInterval", void 0);
const validator = new OptionsValidator();
opts = opts || {};
opts.packetOrderingTimeout = validator.validateNonZero(opts.packetOrderingTimeout, 60, 'packetOrderingTimeout');
opts.synchronizationThrottler = opts.synchronizationThrottler || {};
this._domainClient = domainClient;
this._application = opts.application || 'MetaApi';
this._domain = opts.domain || 'agiliumtrade.agiliumtrade.ai';
this._region = opts.region;
this._hostname = 'mt-client-api-v1';
this._metaApi = metaApi;
this._url = null;
this._requestTimeout = validator.validateNonZero(opts.requestTimeout, 60, 'requestTimeout') * 1000;
this._connectTimeout = validator.validateNonZero(opts.connectTimeout, 60, 'connectTimeout') * 1000;
const retryOpts = opts.retryOpts || {};
this._retries = validator.validateNumber(retryOpts.retries, 5, 'retryOpts.retries');
this._minRetryDelayInSeconds = validator.validateNonZero(retryOpts.minDelayInSeconds, 1, 'retryOpts.minDelayInSeconds');
this._maxRetryDelayInSeconds = validator.validateNonZero(retryOpts.maxDelayInSeconds, 30, 'retryOpts.maxDelayInSeconds');
this._maxAccountsPerInstance = 100;
this._subscribeCooldownInSeconds = validator.validateNonZero(retryOpts.subscribeCooldownInSeconds, 600, 'retryOpts.subscribeCooldownInSeconds');
this._sequentialEventProcessing = true;
this._useSharedClientApi = validator.validateBoolean(opts.useSharedClientApi, false, 'useSharedClientApi');
this._unsubscribeThrottlingInterval = validator.validateNonZero(opts.unsubscribeThrottlingIntervalInSeconds, 10, 'unsubscribeThrottlingIntervalInSeconds') * 1000;
this._socketMinimumReconnectTimeout = 500;
this._latencyService = new LatencyService(this, token, this._connectTimeout);
this._token = token;
this._synchronizationListeners = {};
this._latencyListeners = [];
this._reconnectListeners = [];
this._connectedHosts = {};
this._synchronizationThrottlerOpts = opts.synchronizationThrottler;
this._subscriptionManager = new SubscriptionManager(this, metaApi);
this._statusTimers = {};
this._synchronizationIdByInstance = {};
this._subscribeLock = null;
this._firstConnect = true;
this._lastRequestsTime = {};
this._packetOrderer = new PacketOrderer(this, opts.packetOrderingTimeout);
this._packetOrderer.start();
this._synchronizationHashes = {};
if (opts.packetLogger && opts.packetLogger.enabled) {
this._packetLogger = new PacketLogger(opts.packetLogger);
this._packetLogger.start();
}
this._logger = LoggerManager.getLogger('MetaApiWebsocketClient');
if (!opts.disableInternalJobs) {
this._clearAccountCacheInterval = setInterval(this._clearAccountCacheJob.bind(this), 30 * 60 * 1000);
this._clearInactiveSyncDataInterval = setInterval(this._clearInactiveSyncDataJob.bind(this), 5 * 60 * 1000);
}
}
};
export default MetaApiWebsocketClient;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCByYW5kb21zdHJpbmcgZnJvbSAncmFuZG9tc3RyaW5nJztcbmltcG9ydCBzb2NrZXRJTyBmcm9tICdzb2NrZXQuaW8tY2xpZW50JztcbmltcG9ydCBUaW1lb3V0RXJyb3IgZnJvbSAnLi4vdGltZW91dEVycm9yJztcbmltcG9ydCB7VmFsaWRhdGlvbkVycm9yLCBOb3RGb3VuZEVycm9yLCBJbnRlcm5hbEVycm9yLCBVbmF1dGhvcml6ZWRFcnJvciwgVG9vTWFueVJlcXVlc3RzRXJyb3IsIEZvcmJpZGRlbkVycm9yfVxuICBmcm9tICcuLi9lcnJvckhhbmRsZXInO1xuaW1wb3J0IE9wdGlvbnNWYWxpZGF0b3IgZnJvbSAnLi4vb3B0aW9uc1ZhbGlkYXRvcic7XG5pbXBvcnQgTm90U3luY2hyb25pemVkRXJyb3IgZnJvbSAnLi9ub3RTeW5jaHJvbml6ZWRFcnJvcic7XG5pbXBvcnQgTm90Q29ubmVjdGVkRXJyb3IgZnJvbSAnLi9ub3RDb25uZWN0ZWRFcnJvcic7XG5pbXBvcnQgVHJhZGVFcnJvciBmcm9tICcuL3RyYWRlRXJyb3InO1xuaW1wb3J0IFBhY2tldE9yZGVyZXIgZnJvbSAnLi9wYWNrZXRPcmRlcmVyJztcbmltcG9ydCBTeW5jaHJvbml6YXRpb25UaHJvdHRsZXIgZnJvbSAnLi9zeW5jaHJvbml6YXRpb25UaHJvdHRsZXInO1xuaW1wb3J0IFN1YnNjcmlwdGlvbk1hbmFnZXIgZnJvbSAnLi9zdWJzY3JpcHRpb25NYW5hZ2VyJztcbmltcG9ydCBMb2dnZXJNYW5hZ2VyIGZyb20gJy4uLy4uL2xvZ2dlcic7XG5pbXBvcnQgYW55IGZyb20gJ3Byb21pc2UuYW55JztcbmltcG9ydCBMYXRlbmN5U2VydmljZSBmcm9tICcuL2xhdGVuY3lTZXJ2aWNlJztcbmltcG9ydCB7TWV0YXRyYWRlclN5bWJvbFNwZWNpZmljYXRpb259IGZyb20gJy4vbWV0YUFwaVdlYnNvY2tldC5jbGllbnQuc2NoZW1hcyc7XG5pbXBvcnQgTWV0YXRyYWRlckFjY291bnQgZnJvbSAnLi4vLi4vbWV0YUFwaS9tZXRhdHJhZGVyQWNjb3VudCc7XG5pbXBvcnQgRG9tYWluQ2xpZW50IGZyb20gJy4uL2RvbWFpbi5jbGllbnQnO1xuaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcblxuZXhwb3J0ICogZnJvbSAnLi9tZXRhQXBpV2Vic29ja2V0LmNsaWVudC5zY2hlbWFzJztcblxubGV0IFBhY2tldExvZ2dlcjtcbmlmICh0eXBlb2Ygd2luZG93ID09PSAndW5kZWZpbmVkJykgeyAvLyBkb24ndCBpbXBvcnQgUGFja2V0TG9nZ2VyIGZvciBicm93c2VyIHZlcnNpb25cbiAgUGFja2V0TG9nZ2VyID0gcmVxdWlyZSgnLi9wYWNrZXRMb2dnZXInKS5kZWZhdWx0O1xufVxuXG4vKipcbiAqIE1ldGFBcGkgd2Vic29ja2V0IEFQSSBjbGllbnQgKHNlZSBodHRwczovL21ldGFhcGkuY2xvdWQvZG9jcy9jbGllbnQvd2Vic29ja2V0L292ZXJ2aWV3LylcbiAqL1xuY2xhc3MgTWV0YUFwaVdlYnNvY2tldENsaWVudCB7XG4gIFxuICBwcml2YXRlIF9kb21haW5DbGllbnQ6IERvbWFpbkNsaWVudDtcbiAgcHJpdmF0ZSBfYXBwbGljYXRpb246IGFueTtcbiAgcHJpdmF0ZSBfZG9tYWluOiBhbnk7XG4gIHByaXZhdGUgX3JlZ2lvbjogYW55O1xuICBwcml2YXRlIF9ob3N0bmFtZTogc3RyaW5nO1xuICBwcml2YXRlIF9tZXRhQXBpOiBhbnk7XG4gIHByaXZhdGUgX3VybDogbnVsbDtcbiAgcHJpdmF0ZSBfcmVxdWVzdFRpbWVvdXQ6IG51bWJlcjtcbiAgcHJpdmF0ZSBfY29ubmVjdFRpbWVvdXQ6IG51bWJlcjtcbiAgcHJpdmF0ZSBfcmV0cmllczogYW55O1xuICBwcml2YXRlIF9taW5SZXRyeURlbGF5SW5TZWNvbmRzOiBhbnk7XG4gIHByaXZhdGUgX21heFJldHJ5RGVsYXlJblNlY29uZHM6IGFueTtcbiAgcHJpdmF0ZSBfbWF4QWNjb3VudHNQZXJJbnN0YW5jZTogbnVtYmVyO1xuICBwcml2YXRlIF9zdWJzY3JpYmVDb29sZG93bkluU2Vjb25kczogYW55O1xuICBwcml2YXRlIF9zZXF1ZW50aWFsRXZlbnRQcm9jZXNzaW5nOiBib29sZWFuO1xuICBwcml2YXRlIF91c2VTaGFyZWRDbGllbnRBcGk6IGFueTtcbiAgcHJpdmF0ZSBfdW5zdWJzY3JpYmVUaHJvdHRsaW5nSW50ZXJ2YWw6IG51bWJlcjtcbiAgcHJpdmF0ZSBfc29ja2V0TWluaW11bVJlY29ubmVjdFRpbWVvdXQ6IG51bWJlcjtcbiAgcHJpdmF0ZSBfbGF0ZW5jeVNlcnZpY2U6IExhdGVuY3lTZXJ2aWNlO1xuICBwcml2YXRlIF90b2tlbjogYW55O1xuICBwcml2YXRlIF9zeW5jaHJvbml6YXRpb25MaXN0ZW5lcnM6IHt9O1xuICBwcml2YXRlIF9sYXRlbmN5TGlzdGVuZXJzOiBhbnlbXTtcbiAgcHJpdmF0ZSBfcmVjb25uZWN0TGlzdGVuZXJzOiBhbnlbXTtcbiAgcHJpdmF0ZSBfY29ubmVjdGVkSG9zdHM6IHt9O1xuICBwcml2YXRlIF9zb2NrZXRJbnN0YW5jZXM6IFNvY2tldEluc3RhbmNlcyA9IHt9O1xuICBwcml2YXRlIF9zb2NrZXRJbnN0YW5jZXNCeUFjY291bnRzOiB7W2luc3RhbmNlTnVtYmVyOiBudW1iZXJdOiB7W2FjY291bnRJZDogc3RyaW5nXTogbnVtYmVyfX0gPSB7fTtcbiAgcHJpdmF0ZSBfcmVnaW9uc0J5QWNjb3VudHM6IFJlZ2lvbnNCeUFjY291bnRzID0ge307XG4gIHByaXZhdGUgX2FjY291bnRzQnlSZXBsaWNhSWQ6IE1ldGFBcGlXZWJzb2NrZXRDbGllbnQuQWNjb3VudHNCeVJlcGxpY2EgPSB7fTtcbiAgcHJpdmF0ZSBfYWNjb3VudFJlcGxpY2FzOiBNZXRhQXBpV2Vic29ja2V0Q2xpZW50LkFjY291bnRSZXBsaWNhcyA9IHt9O1xuICBwcml2YXRlIF9zeW5jaHJvbml6YXRpb25UaHJvdHRsZXJPcHRzOiBhbnk7XG4gIHByaXZhdGUgX3N1YnNjcmlwdGlvbk1hbmFnZXI6IGFueTtcbiAgcHJpdmF0ZSBfc3RhdHVzVGltZXJzOiB7fTtcbiAgcHJpdmF0ZSBfZXZlbnRRdWV1ZXM6IHtbYWNjb3VudElkOiBzdHJpbmddOiBBcnJheTwoKSA9PiBQcm9taXNlPGFueT4+fSA9IHt9O1xuICBwcml2YXRlIF9zeW5jaHJvbml6YXRpb25GbGFnczoge1tzeW5jaHJvbml6YXRpb25JZDogc3RyaW5nXTogU3luY2hyb25pemF0aW9uRmxhZ30gPSB7fTtcbiAgcHJpdmF0ZSBfc3luY2hyb25pemF0aW9uSWRCeUluc3RhbmNlOiB7fTtcbiAgcHJpdmF0ZSBfc3Vic2NyaWJlTG9jazogYW55O1xuICBwcml2YXRlIF9maXJzdENvbm5lY3Q6IGJvb2xlYW47XG4gIHByaXZhdGUgX2xhc3RSZXF1ZXN0c1RpbWU6IHt9O1xuICBwcml2YXRlIF9wYWNrZXRPcmRlcmVyOiBQYWNrZXRPcmRlcmVyO1xuICBwcml2YXRlIF9zeW5jaHJvbml6YXRpb25IYXNoZXM6IHt9O1xuICBwcml2YXRlIF91cGRhdGVFdmVudHM6IHtbaW5zdGFuY2VJZDogc3RyaW5nXTogUGFja2V0W119ID0ge307XG4gIHByaXZhdGUgX3BhY2tldExvZ2dlcjogYW55O1xuICBwcml2YXRlIF9sb2dnZXI6IGFueTtcbiAgcHJpdmF0ZSBfY2xlYXJBY2NvdW50Q2FjaGVJbnRlcnZhbDogTm9kZUpTLlRpbWVvdXQ7XG4gIHByaXZhdGUgX2NsZWFySW5hY3RpdmVTeW5jRGF0YUludGVydmFsOiBOb2RlSlMuVGltZW91dDtcblxuICAvKipcbiAgICogQHR5cGVkZWYgTWV0YUFwaVdlYnNvY2tldENsaWVudE9wdGlvbnMgTWV0YUFwaSB3ZWJzb2NrZXQgY2xpZW50IG9wdGlvbnNcbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IFtyZWdpb25dIHJlZ2lvbiB0byBjb25uZWN0XG4gICAqIEBwcm9wZXJ0eSB7Qm9vbGVhbn0gW2Rpc2FibGVJbnRlcm5hbEpvYnNdIHdoZXRoZXIgdG8gbm90IHJ1biBpbnRlcm5hbCBpbnRlcnZhbCBqb2JzLiBVc2VkIGZvciB0ZXN0cyBvbmx5XG4gICAqL1xuXG4gIC8qKlxuICAgKiBDb25zdHJ1Y3RzIE1ldGFBcGkgd2Vic29ja2V0IEFQSSBjbGllbnQgaW5zdGFuY2VcbiAgICogQHBhcmFtIHtNZXRhQXBpfSBtZXRhQXBpIG1ldGFBcGkgaW5zdGFuY2VcbiAgICogQHBhcmFtIHtEb21haW5DbGllbnR9IGRvbWFpbkNsaWVudCBkb21haW4gY2xpZW50XG4gICAqIEBwYXJhbSB7U3RyaW5nfSB0b2tlbiBhdXRob3JpemF0aW9uIHRva2VuXG4gICAqIEBwYXJhbSB7TWV0YUFwaVdlYnNvY2tldENsaWVudE9wdGlvbnN9IG9wdHMgd2Vic29ja2V0IGNsaWVudCBvcHRpb25zXG4gICAqL1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eSxtYXgtc3RhdGVtZW50c1xuICBjb25zdHJ1Y3RvcihtZXRhQXBpLCBkb21haW5DbGllbnQsIHRva2VuLCBvcHRzKSB7XG4gICAgY29uc3QgdmFsaWRhdG9yID0gbmV3IE9wdGlvbnNWYWxpZGF0b3IoKTtcbiAgICBvcHRzID0gb3B0cyB8fCB7fTtcbiAgICBvcHRzLnBhY2tldE9yZGVyaW5nVGltZW91dCA9IHZhbGlkYXRvci52YWxpZGF0ZU5vblplcm8ob3B0cy5wYWNrZXRPcmRlcmluZ1RpbWVvdXQsIDYwLCAncGFja2V0T3JkZXJpbmdUaW1lb3V0Jyk7XG4gICAgb3B0cy5zeW5jaHJvbml6YXRpb25UaHJvdHRsZXIgPSBvcHRzLnN5bmNocm9uaXphdGlvblRocm90dGxlciB8fCB7fTtcbiAgICB0aGlzLl9kb21haW5DbGllbnQgPSBkb21haW5DbGllbnQ7XG4gICAgdGhpcy5fYXBwbGljYXRpb24gPSBvcHRzLmFwcGxpY2F0aW9uIHx8ICdNZXRhQXBpJztcbiAgICB0aGlzLl9kb21haW4gPSBvcHRzLmRvbWFpbiB8fCAnYWdpbGl1bXRyYWRlLmFnaWxpdW10cmFkZS5haSc7XG4gICAgdGhpcy5fcmVnaW9uID0gb3B0cy5yZWdpb247XG4gICAgdGhpcy5faG9zdG5hbWUgPSAnbXQtY2xpZW50LWFwaS12MSc7XG4gICAgdGhpcy5fbWV0YUFwaSA9IG1ldGFBcGk7XG4gICAgdGhpcy5fdXJsID0gbnVsbDtcbiAgICB0aGlzLl9yZXF1ZXN0VGltZW91dCA9IHZhbGlkYXRvci52YWxpZGF0ZU5vblplcm8ob3B0cy5yZXF1ZXN0VGltZW91dCwgNjAsICdyZXF1ZXN0VGltZW91dCcpICogMTAwMDtcbiAgICB0aGlzLl9jb25uZWN0VGltZW91dCA9IHZhbGlkYXRvci52YWxpZGF0ZU5vblplcm8ob3B0cy5jb25uZWN0VGltZW91dCwgNjAsICdjb25uZWN0VGltZW91dCcpICogMTAwMDtcbiAgICBjb25zdCByZXRyeU9wdHMgPSBvcHRzLnJldHJ5T3B0cyB8fCB7fTtcbiAgICB0aGlzLl9yZXRyaWVzID0gdmFsaWRhdG9yLnZhbGlkYXRlTnVtYmVyKHJldHJ5T3B0cy5yZXRyaWVzLCA1LCAncmV0cnlPcHRzLnJldHJpZXMnKTtcbiAgICB0aGlzLl9taW5SZXRyeURlbGF5SW5TZWNvbmRzID0gdmFsaWRhdG9yLnZhbGlkYXRlTm9uWmVybyhyZXRyeU9wdHMubWluRGVsYXlJblNlY29uZHMsIDEsXG4gICAgICAncmV0cnlPcHRzLm1pbkRlbGF5SW5TZWNvbmRzJyk7XG4gICAgdGhpcy5fbWF4UmV0cnlEZWxheUluU2Vjb25kcyA9IHZhbGlkYXRvci52YWxpZGF0ZU5vblplcm8ocmV0cnlPcHRzLm1heERlbGF5SW5TZWNvbmRzLCAzMCxcbiAgICAgICdyZXRyeU9wdHMubWF4RGVsYXlJblNlY29uZHMnKTtcbiAgICB0aGlzLl9tYXhBY2NvdW50c1Blckluc3RhbmNlID0gMTAwO1xuICAgIHRoaXMuX3N1YnNjcmliZUNvb2xkb3duSW5TZWNvbmRzID0gdmFsaWRhdG9yLnZhbGlkYXRlTm9uWmVybyhyZXRyeU9wdHMuc3Vic2NyaWJlQ29vbGRvd25JblNlY29uZHMsIDYwMCwgXG4gICAgICAncmV0cnlPcHRzLnN1YnNjcmliZUNvb2xkb3duSW5TZWNvbmRzJyk7XG4gICAgdGhpcy5fc2VxdWVudGlhbEV2ZW50UHJvY2Vzc2luZyA9IHRydWU7XG4gICAgdGhpcy5fdXNlU2hhcmVkQ2xpZW50QXBpID0gdmFsaWRhdG9yLnZhbGlkYXRlQm9vbGVhbihvcHRzLnVzZVNoYXJlZENsaWVudEFwaSwgZmFsc2UsICd1c2VTaGFyZWRDbGllbnRBcGknKTtcbiAgICB0aGlzLl91bnN1YnNjcmliZVRocm90dGxpbmdJbnRlcnZhbCA9IHZhbGlkYXRvci52YWxpZGF0ZU5vblplcm8ob3B0cy51bnN1YnNjcmliZVRocm90dGxpbmdJbnRlcnZhbEluU2Vjb25kcywgMTAsXG4gICAgICAndW5zdWJzY3JpYmVUaHJvdHRsaW5nSW50ZXJ2YWxJblNlY29uZHMnKSAqIDEwMDA7XG4gICAgdGhpcy5fc29ja2V0TWluaW11bVJlY29ubmVjdFRpbWVvdXQgPSA1MDA7XG4gICAgdGhpcy5fbGF0ZW5jeVNlcnZpY2UgPSBuZXcgTGF0ZW5jeVNlcnZpY2UodGhpcywgdG9rZW4sIHRoaXMuX2Nvbm5lY3RUaW1lb3V0KTtcbiAgICB0aGlzLl90b2tlbiA9IHRva2VuO1xuICAgIHRoaXMuX3N5bmNocm9uaXphdGlvbkxpc3RlbmVycyA9IHt9O1xuICAgIHRoaXMuX2xhdGVuY3lMaXN0ZW5lcnMgPSBbXTtcbiAgICB0aGlzLl9yZWNvbm5lY3RMaXN0ZW5lcnMgPSBbXTtcbiAgICB0aGlzLl9jb25uZWN0ZWRIb3N0cyA9IHt9O1xuICAgIHRoaXMuX3N5bmNocm9uaXphdGlvblRocm90dGxlck9wdHMgPSBvcHRzLnN5bmNocm9uaXphdGlvblRocm90dGxlcjtcbiAgICB0aGlzLl9zdWJzY3JpcHRpb25NYW5hZ2VyID0gbmV3IFN1YnNjcmlwdGlvbk1hbmFnZXIodGhpcywgbWV0YUFwaSk7XG4gICAgdGhpcy5fc3RhdHVzVGltZXJzID0ge307XG4gICAgdGhpcy5fc3luY2hyb25pemF0aW9uSWRCeUluc3RhbmNlID0ge307XG4gICAgdGhpcy5fc3Vic2NyaWJlTG9jayA9IG51bGw7XG4gICAgdGhpcy5fZmlyc3RDb25uZWN0ID0gdHJ1ZTtcbiAgICB0aGlzLl9sYXN0UmVxdWVzdHNUaW1lID0ge307XG4gICAgdGhpcy5fcGFja2V0T3JkZXJlciA9IG5ldyBQYWNrZXRPcmRlcmVyKHRoaXMsIG9wdHMucGFja2V0T3JkZXJpbmdUaW1lb3V0KTtcbiAgICB0aGlzLl9wYWNrZXRPcmRlcmVyLnN0YXJ0KCk7XG4gICAgdGhpcy5fc3luY2hyb25pemF0aW9uSGFzaGVzID0ge307XG4gICAgaWYgKG9wdHMucGFja2V0TG9nZ2VyICYmIG9wdHMucGFja2V0TG9nZ2VyLmVuYWJsZWQpIHtcbiAgICAgIHRoaXMuX3BhY2tldExvZ2dlciA9IG5ldyBQYWNrZXRMb2dnZXIob3B0cy5wYWNrZXRMb2dnZXIpO1xuICAgICAgdGhpcy5fcGFja2V0TG9nZ2VyLnN0YXJ0KCk7XG4gICAgfVxuICAgIHRoaXMuX2xvZ2dlciA9IExvZ2dlck1hbmFnZXIuZ2V0TG9nZ2VyKCdNZXRhQXBpV2Vic29ja2V0Q2xpZW50Jyk7XG4gICAgaWYgKCFvcHRzLmRpc2FibGVJbnRlcm5hbEpvYnMpIHtcbiAgICAgIHRoaXMuX2NsZWFyQWNjb3VudENhY2hlSW50ZXJ2YWwgPSBzZXRJbnRlcnZhbCh0aGlzLl9jbGVhckFjY291bnRDYWNoZUpvYi5iaW5kKHRoaXMpLCAzMCAqIDYwICogMTAwMCk7XG4gICAgICB0aGlzLl9jbGVhckluYWN0aXZlU3luY0RhdGFJbnRlcnZhbCA9IHNldEludGVydmFsKHRoaXMuX2NsZWFySW5hY3RpdmVTeW5jRGF0YUpvYi5iaW5kKHRoaXMpLCA1ICogNjAgKiAxMDAwKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVzdGFydHMgdGhlIGFjY291bnQgc3luY2hyb25pemF0aW9uIHByb2Nlc3Mgb24gYW4gb3V0IG9mIG9yZGVyIHBhY2tldFxuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGFjY291bnQgaWRcbiAgICogQHBhcmFtIHtOdW1iZXJ9IGluc3RhbmNlSW5kZXggaW5zdGFuY2UgaW5kZXhcbiAgICogQHBhcmFtIHtOdW1iZXJ9IGV4cGVjdGVkU2VxdWVuY2VOdW1iZXIgZXhwZWN0ZWQgcy9uXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBhY3R1YWxTZXF1ZW5jZU51bWJlciBhY3R1YWwgcy9uXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBwYWNrZXQgcGFja2V0IGRhdGFcbiAgICogQHBhcmFtIHtEYXRlfSByZWNlaXZlZEF0IHRpbWUgdGhlIHBhY2tldCB3YXMgcmVjZWl2ZWQgYXRcbiAgICovXG4gIG9uT3V0T2ZPcmRlclBhY2tldChhY2NvdW50SWQsIGluc3RhbmNlSW5kZXgsIGV4cGVjdGVkU2VxdWVuY2VOdW1iZXIsIGFjdHVhbFNlcXVlbmNlTnVtYmVyLCBwYWNrZXQsIHJlY2VpdmVkQXQpIHtcbiAgICBjb25zdCBwcmltYXJ5QWNjb3VudElkID0gdGhpcy5fYWNjb3VudHNCeVJlcGxpY2FJZFthY2NvdW50SWRdO1xuICAgIGlmICh0aGlzLl9zdWJzY3JpcHRpb25NYW5hZ2VyLmlzU3Vic2NyaXB0aW9uQWN0aXZlKGFjY291bnRJZCkpIHtcbiAgICAgIGNvbnN0IGxldmVsID0gdGhpcy5fbGF0ZW5jeVNlcnZpY2UuZ2V0U3luY2hyb25pemVkQWNjb3VudEluc3RhbmNlcyhwcmltYXJ5QWNjb3VudElkKS5sZW5ndGggPyAnZGVidWcnIDogJ2Vycm9yJztcbiAgICAgIHRoaXMuX2xvZ2dlcltsZXZlbF0oJ01ldGFBcGkgd2Vic29ja2V0IGNsaWVudCByZWNlaXZlZCBhbiBvdXQgb2Ygb3JkZXIgJyArXG4gICAgICAgIGBwYWNrZXQgdHlwZSAke3BhY2tldC50eXBlfSBmb3IgYWNjb3VudCBpZCAke2FjY291bnRJZH06JHtpbnN0YW5jZUluZGV4fS4gRXhwZWN0ZWQgcy9uIGAgK1xuICAgICAgICBgJHtleHBlY3RlZFNlcXVlbmNlTnVtYmVyfSBkb2VzIG5vdCBtYXRjaCB0aGUgYWN0dWFsIG9mICR7YWN0dWFsU2VxdWVuY2VOdW1iZXJ9YCk7XG4gICAgICB0aGlzLmVuc3VyZVN1YnNjcmliZShhY2NvdW50SWQsIGluc3RhbmNlSW5kZXgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBQYXRjaCBzZXJ2ZXIgVVJMIGZvciB1c2UgaW4gdW5pdCB0ZXN0c1xuICAgKiBAcGFyYW0ge1N0cmluZ30gdXJsIHBhdGNoZWQgc2VydmVyIFVSTFxuICAgKi9cbiAgc2V0IHVybCh1cmwpIHtcbiAgICB0aGlzLl91cmwgPSB1cmw7XG4gIH1cblxuICAvKipcbiAgICogV2Vic29ja2V0IGNsaWVudCBwcmVkZWZpbmVkIHJlZ2lvblxuICAgKiBAcmV0dXJucyB7U3RyaW5nfSBwcmVkZWZpbmVkIHJlZ2lvblxuICAgKi9cbiAgZ2V0IHJlZ2lvbigpIHtcbiAgICByZXR1cm4gdGhpcy5fcmVnaW9uO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIGxpc3Qgb2Ygc29ja2V0IGluc3RhbmNlIGRpY3Rpb25hcmllc1xuICAgKiBAcmV0dXJuIHtPYmplY3RbXX0gbGlzdCBvZiBzb2NrZXQgaW5zdGFuY2UgZGljdGlvbmFyaWVzXG4gICAqL1xuICBnZXQgc29ja2V0SW5zdGFuY2VzKCkge1xuICAgIHJldHVybiB0aGlzLl9zb2NrZXRJbnN0YW5jZXM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgZGljdGlvbmFyeSBvZiBzb2NrZXQgaW5zdGFuY2VzIGJ5IGFjY291bnQgaWRzXG4gICAqIEByZXR1cm4ge09iamVjdH0gZGljdGlvbmFyeSBvZiBzb2NrZXQgaW5zdGFuY2VzIGJ5IGFjY291bnQgaWRzXG4gICAqL1xuICBnZXQgc29ja2V0SW5zdGFuY2VzQnlBY2NvdW50cygpIHtcbiAgICByZXR1cm4gdGhpcy5fc29ja2V0SW5zdGFuY2VzQnlBY2NvdW50cztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBkaWN0aW9uYXJ5IG9mIGFjY291bnQgcmVwbGljYXMgYnkgcmVnaW9uXG4gICAqIEByZXR1cm4ge09iamVjdH0gZGljdGlvbmFyeSBvZiBhY2NvdW50IHJlcGxpY2FzIGJ5IHJlZ2lvblxuICAgKi9cbiAgZ2V0IGFjY291bnRSZXBsaWNhcygpOiBNZXRhQXBpV2Vic29ja2V0Q2xpZW50LkFjY291bnRSZXBsaWNhcyB7XG4gICAgcmV0dXJuIHRoaXMuX2FjY291bnRSZXBsaWNhcztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBkaWN0aW9uYXJ5IG9mIHByaW1hcnkgYWNjb3VudCBpZHMgYnkgcmVwbGljYSBpZHNcbiAgICogQHJldHVybiB7T2JqZWN0fSBkaWN0aW9uYXJ5IG9mIHByaW1hcnkgYWNjb3VudCBpZHMgYnkgcmVwbGljYSBpZHNcbiAgICovXG4gIGdldCBhY2NvdW50c0J5UmVwbGljYUlkKCk6IE1ldGFBcGlXZWJzb2NrZXRDbGllbnQuQWNjb3VudHNCeVJlcGxpY2Ege1xuICAgIHJldHVybiB0aGlzLl9hY2NvdW50c0J5UmVwbGljYUlkO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgY2xlYXIgYWNjb3VudCBjYWNoZSBqb2IuIFVzZWQgZm9yIHRlc3RzXG4gICAqIEByZXR1cm4ge0Z1bmN0aW9ufSBjbGVhciBhY2NvdW50IGNhY2hlIGpvYlxuICAgKi9cbiAgZ2V0IGNsZWFyQWNjb3VudENhY2hlSm9iKCkge1xuICAgIHJldHVybiB0aGlzLl9jbGVhckFjY291bnRDYWNoZUpvYi5iaW5kKHRoaXMpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgbGF0ZW5jeSBzZXJ2aWNlXG4gICAqIEByZXR1cm5zIHtMYXRlbmN5U2VydmljZX0gbGF0ZW5jeSBzZXJ2aWNlXG4gICAqL1xuICBnZXQgbGF0ZW5jeVNlcnZpY2UoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2xhdGVuY3lTZXJ2aWNlO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIGxpc3Qgb2Ygc3Vic2NyaWJlZCBhY2NvdW50IGlkc1xuICAgKiBAcGFyYW0ge051bWJlcn0gaW5zdGFuY2VOdW1iZXIgaW5zdGFuY2UgaW5kZXggbnVtYmVyXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBzb2NrZXRJbnN0YW5jZUluZGV4IHNvY2tldCBpbnN0YW5jZSBpbmRleFxuICAgKiBAcGFyYW0ge1N0cmluZ30gcmVnaW9uIHNlcnZlciByZWdpb25cbiAgICogQHJldHVybiB7c3RyaW5nW119IGxpc3Qgb2Ygc3Vic2NyaWJlZCBhY2NvdW50IGlkc1xuICAgKi9cbiAgc3Vic2NyaWJlZEFjY291bnRJZHMoaW5zdGFuY2VOdW1iZXIsIHNvY2tldEluc3RhbmNlSW5kZXgsIHJlZ2lvbikge1xuICAgIGNvbnN0IGNvbm5lY3RlZElkcyA9IFtdO1xuICAgIGlmICh0aGlzLl9zb2NrZXRJbnN0YW5jZXNCeUFjY291bnRzW2luc3RhbmNlTnVtYmVyXSkge1xuICAgICAgT2JqZWN0LmtleXModGhpcy5fY29ubmVjdGVkSG9zdHMpLmZvckVhY2goaW5zdGFuY2VJZCA9PiB7XG4gICAgICAgIGNvbnN0IGFjY291bnRJZCA9IGluc3RhbmNlSWQuc3BsaXQoJzonKVswXTtcbiAgICAgICAgY29uc3QgYWNjb3VudFJlZ2lvbiA9IHRoaXMuZ2V0QWNjb3VudFJlZ2lvbihhY2NvdW50SWQpO1xuICAgICAgICBpZiAoIWNvbm5lY3RlZElkcy5pbmNsdWRlcyhhY2NvdW50SWQpICYmIFxuICAgICAgICB0aGlzLl9zb2NrZXRJbnN0YW5jZXNCeUFjY291bnRzW2luc3RhbmNlTnVtYmVyXVthY2NvdW50SWRdICE9PSB1bmRlZmluZWQgJiYgKFxuICAgICAgICAgIHRoaXMuX3NvY2tldEluc3RhbmNlc0J5QWNjb3VudHNbaW5zdGFuY2VOdW1iZXJdW2FjY291bnRJZF0gPT09IHNvY2tldEluc3RhbmNlSW5kZXggfHwgXG4gICAgICAgIHNvY2tldEluc3RhbmNlSW5kZXggPT09IHVuZGVmaW5lZCkgJiYgYWNjb3VudFJlZ2lvbiA9PT0gcmVnaW9uKSB7XG4gICAgICAgICAgY29ubmVjdGVkSWRzLnB1c2goYWNjb3VudElkKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiBjb25uZWN0ZWRJZHM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB3ZWJzb2NrZXQgY2xpZW50IGNvbm5lY3Rpb24gc3RhdHVzXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBpbnN0YW5jZU51bWJlciBpbnN0YW5jZSBpbmRleCBudW1iZXJcbiAgICogQHBhcmFtIHtOdW1iZXJ9IHNvY2tldEluc3RhbmNlSW5kZXggc29ja2V0IGluc3RhbmNlIGluZGV4XG4gICAqIEBwYXJhbSB7U3RyaW5nfSByZWdpb24gc2VydmVyIHJlZ2lvblxuICAgKiBAcmV0dXJucyB7Qm9vbGVhbn0gd2Vic29ja2V0IGNsaWVudCBjb25uZWN0aW9uIHN0YXR1c1xuICAgKi9cbiAgY29ubmVjdGVkKGluc3RhbmNlTnVtYmVyLCBzb2NrZXRJbnN0YW5jZUluZGV4LCByZWdpb24pIHtcbiAgICBjb25zdCBpbnN0YW5jZSA9IHRoaXMuX3NvY2tldEluc3RhbmNlc1tyZWdpb25dICYmIFxuICAgICAgdGhpcy5fc29ja2V0SW5zdGFuY2VzW3JlZ2lvbl1baW5zdGFuY2VOdW1iZXJdLmxlbmd0aCA+IHNvY2tldEluc3RhbmNlSW5kZXggPyBcbiAgICAgIHRoaXMuX3NvY2tldEluc3RhbmNlc1tyZWdpb25dW2luc3RhbmNlTnVtYmVyXVtzb2NrZXRJbnN0YW5jZUluZGV4XSA6IG51bGw7XG4gICAgcmV0dXJuIChpbnN0YW5jZSAmJiBpbnN0YW5jZS5zb2NrZXQgJiYgaW5zdGFuY2Uuc29ja2V0LmNvbm5lY3RlZCkgfHwgZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBsaXN0IG9mIGFjY291bnRzIGFzc2lnbmVkIHRvIGluc3RhbmNlXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBpbnN0YW5jZU51bWJlciBpbnN0YW5jZSBpbmRleCBudW1iZXJcbiAgICogQHBhcmFtIHtTdHJpbmd9IHNvY2tldEluc3RhbmNlSW5kZXggc29ja2V0IGluc3RhbmNlIGluZGV4XG4gICAqIEBwYXJhbSB7U3RyaW5nfSByZWdpb24gc2VydmVyIHJlZ2lvblxuICAgKiBAcmV0dXJucyBcbiAgICovXG4gIHByaXZhdGUgX2dldEFzc2lnbmVkQWNjb3VudHMoaW5zdGFuY2VOdW1iZXIsIHNvY2tldEluc3RhbmNlSW5kZXgsIHJlZ2lvbikge1xuICAgIGNvbnN0IGFjY291bnRJZHMgPSBbXTtcbiAgICBPYmplY3Qua2V5cyh0aGlzLl9zb2NrZXRJbnN0YW5jZXNCeUFjY291bnRzW2luc3RhbmNlTnVtYmVyXSkuZm9yRWFjaChrZXkgPT4ge1xuICAgICAgY29uc3QgYWNjb3VudFJlZ2lvbiA9IHRoaXMuZ2V0QWNjb3VudFJlZ2lvbihrZXkpO1xuICAgICAgaWYgKGFjY291bnRSZWdpb24gPT09IHJlZ2lvbiAmJlxuICAgICAgICB0aGlzLl9zb2NrZXRJbnN0YW5jZXNCeUFjY291bnRzW2luc3RhbmNlTnVtYmVyXVtrZXldID09PSBzb2NrZXRJbnN0YW5jZUluZGV4KSB7XG4gICAgICAgIGFjY291bnRJZHMucHVzaChrZXkpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHJldHVybiBhY2NvdW50SWRzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYWNjb3VudCByZWdpb24gYnkgaWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBhY2NvdW50IGlkXG4gICAqIEByZXR1cm5zIHtTdHJpbmd9IGFjY291bnQgcmVnaW9uXG4gICAqL1xuICBnZXRBY2NvdW50UmVnaW9uKGFjY291bnRJZCkge1xuICAgIHJldHVybiB0aGlzLl9yZWdpb25zQnlBY2NvdW50c1thY2NvdW50SWRdICYmIHRoaXMuX3JlZ2lvbnNCeUFjY291bnRzW2FjY291bnRJZF0ucmVnaW9uO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgYWNjb3VudCBjYWNoZSBpbmZvXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge09iamVjdH0gcmVwbGljYXMgYWNjb3VudCByZXBsaWNhcywgaW5jbHVkaW5nIHByaW1hcnkgcmVwbGljYVxuICAgKi9cbiAgYWRkQWNjb3VudENhY2hlKGFjY291bnRJZDogc3RyaW5nLCByZXBsaWNhczogTWV0YXRyYWRlckFjY291bnQuQWNjb3VudHNCeVJlZ2lvbikge1xuICAgIHRoaXMuX2FjY291bnRSZXBsaWNhc1thY2NvdW50SWRdID0gcmVwbGljYXM7XG4gICAgT2JqZWN0LmtleXMocmVwbGljYXMpLmZvckVhY2gocmVnaW9uID0+IHtcbiAgICAgIGNvbnN0IHJlcGxpY2FJZCA9IHJlcGxpY2FzW3JlZ2lvbl07XG4gICAgICBpZiAoIXRoaXMuX3JlZ2lvbnNCeUFjY291bnRzW3JlcGxpY2FJZF0pIHtcbiAgICAgICAgdGhpcy5fcmVnaW9uc0J5QWNjb3VudHNbcmVwbGljYUlkXSA9IHtcbiAgICAgICAgICByZWdpb24sXG4gICAgICAgICAgY29ubmVjdGlvbnM6IDEsXG4gICAgICAgICAgbGFzdFVzZWQ6IERhdGUubm93KClcbiAgICAgICAgfTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuX3JlZ2lvbnNCeUFjY291bnRzW3JlcGxpY2FJZF0uY29ubmVjdGlvbnMrKztcbiAgICAgIH1cbiAgICAgIHRoaXMuX2FjY291bnRzQnlSZXBsaWNhSWRbcmVwbGljYUlkXSA9IGFjY291bnRJZDtcbiAgICB9KTtcbiAgICB0aGlzLl9sb2dnZXIuZGVidWcoYCR7YWNjb3VudElkfTogYWRkZWQgYWNjb3VudCBjYWNoZWApO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgYWNjb3VudCBjYWNoZSBpbmZvXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge09iamVjdH0gcmVwbGljYXMgYWNjb3VudCByZXBsaWNhc1xuICAgKi9cbiAgdXBkYXRlQWNjb3VudENhY2hlKGFjY291bnRJZDogc3RyaW5nLCByZXBsaWNhczogTWV0YXRyYWRlckFjY291bnQuQWNjb3VudHNCeVJlZ2lvbikge1xuICAgIGNvbnN0IG9sZFJlcGxpY2FzID0gdGhpcy5fYWNjb3VudFJlcGxpY2FzW2FjY291bnRJZF07XG4gICAgaWYgKG9sZFJlcGxpY2FzKSB7XG4gICAgICBjb25zdCBjb25uZWN0aW9uQ291bnQgPSB0aGlzLl9yZWdpb25zQnlBY2NvdW50c1thY2NvdW50SWRdLmNvbm5lY3Rpb25zO1xuICAgICAgT2JqZWN0LmtleXMob2xkUmVwbGljYXMpLmZvckVhY2gocmVnaW9uID0+IHtcbiAgICAgICAgY29uc3QgcmVwbGljYUlkID0gcmVwbGljYXNbcmVnaW9uXTtcbiAgICAgICAgZGVsZXRlIHRoaXMuX2FjY291bnRzQnlSZXBsaWNhSWRbcmVwbGljYUlkXTtcbiAgICAgICAgZGVsZXRlIHRoaXMuX3JlZ2lvbnNCeUFjY291bnRzW3JlcGxpY2FJZF07XG4gICAgICB9KTtcbiAgICAgIHRoaXMuX2FjY291bnRSZXBsaWNhc1thY2NvdW50SWRdID0gcmVwbGljYXM7XG4gICAgICBPYmplY3Qua2V5cyhyZXBsaWNhcykuZm9yRWFjaChyZWdpb24gPT4ge1xuICAgICAgICBjb25zdCByZXBsaWNhSWQgPSByZXBsaWNhc1tyZWdpb25dO1xuICAgICAgICB0aGlzLl9yZWdpb25zQnlBY2NvdW50c1tyZXBsaWNhSWRdID0ge1xuICAgICAgICAgIHJlZ2lvbixcbiAgICAgICAgICBjb25uZWN0aW9uczogY29ubmVjdGlvbkNvdW50LFxuICAgICAgICAgIGxhc3RVc2VkOiBEYXRlLm5vdygpXG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuX2FjY291bnRzQnlSZXBsaWNhSWRbcmVwbGljYUlkXSA9IGFjY291bnRJZDtcbiAgICAgIH0pO1xuICAgICAgdGhpcy5fbG9nZ2VyLmRlYnVnKGAke2FjY291bnRJZH06IHVwZGF0ZWQgYWNjb3VudCBjYWNoZWApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZW1vdmVzIGFjY291bnQgcmVnaW9uIGluZm9cbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBhY2NvdW50IGlkXG4gICAqL1xuICByZW1vdmVBY2NvdW50Q2FjaGUoYWNjb3VudElkOiBzdHJpbmcpIHtcbiAgICBpZiAodGhpcy5fcmVnaW9uc0J5QWNjb3VudHNbYWNjb3VudElkXT8uY29ubmVjdGlvbnMgPiAwKSB7XG4gICAgICB0aGlzLl9yZWdpb25zQnlBY2NvdW50c1thY2NvdW50SWRdLmNvbm5lY3Rpb25zLS07IFxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBMb2NrcyBzdWJzY3JpcHRpb24gZm9yIGEgc29ja2V0IGluc3RhbmNlIGJhc2VkIG9uIFRvb01hbnlSZXF1ZXN0c0Vycm9yIG1ldGFkYXRhXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBpbnN0YW5jZU51bWJlciBpbnN0YW5jZSBpbmRleCBudW1iZXJcbiAgICogQHBhcmFtIHtTdHJpbmd9IHNvY2tldEluc3RhbmNlSW5kZXggc29ja2V0IGluc3RhbmNlIGluZGV4XG4gICAqIEBwYXJhbSB7U3RyaW5nfSByZWdpb24gc2VydmVyIHJlZ2lvblxuICAgKiBAcGFyYW0ge09iamVjdH0gbWV0YWRhdGEgVG9vTWFueVJlcXVlc3RzRXJyb3IgbWV0YWRhdGFcbiAgICovXG4gIGFzeW5jIGxvY2tTb2NrZXRJbnN0YW5jZShpbnN0YW5jZU51bWJlciwgc29ja2V0SW5zdGFuY2VJbmRleCwgcmVnaW9uLCBtZXRhZGF0YSkge1xuICAgIGlmIChtZXRhZGF0YS50eXBlID09PSAnTElNSVRfQUNDT1VOVF9TVUJTQ1JJUFRJT05TX1BFUl9VU0VSJykge1xuICAgICAgdGhpcy5fc3Vic2NyaWJlTG9jayA9IHtcbiAgICAgICAgcmVjb21tZW5kZWRSZXRyeVRpbWU6IG1ldGFkYXRhLnJlY29tbWVuZGVkUmV0cnlUaW1lLFxuICAgICAgICBsb2NrZWRBdEFjY291bnRzOiB0aGlzLnN1YnNjcmliZWRBY2NvdW50SWRzKGluc3RhbmNlTnVtYmVyLCB1bmRlZmluZWQsIHJlZ2lvbikubGVuZ3RoLFxuICAgICAgICBsb2NrZWRBdFRpbWU6IERhdGUubm93KClcbiAgICAgIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IHN1YnNjcmliZWRBY2NvdW50cyA9IHRoaXMuc3Vic2NyaWJlZEFjY291bnRJZHMoaW5zdGFuY2VOdW1iZXIsIHNvY2tldEluc3RhbmNlSW5kZXgsIHJlZ2lvbik7XG4gICAgICBpZiAoc3Vic2NyaWJlZEFjY291bnRzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICBjb25zdCBzb2NrZXRJbnN0YW5jZSA9IHRoaXMuc29ja2V0SW5zdGFuY2VzW3JlZ2lvbl1baW5zdGFuY2VOdW1iZXJdW3NvY2tldEluc3RhbmNlSW5kZXhdO1xuICAgICAgICBzb2NrZXRJbnN0YW5jZS5zb2NrZXQuY2xvc2UoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5fcmVjb25uZWN0KGluc3RhbmNlTnVtYmVyLCBzb2NrZXRJbnN0YW5jZUluZGV4LCByZWdpb24pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3QgaW5zdGFuY2UgPSB0aGlzLnNvY2tldEluc3RhbmNlc1tyZWdpb25dW2luc3RhbmNlTnVtYmVyXVtzb2NrZXRJbnN0YW5jZUluZGV4XTtcbiAgICAgICAgaW5zdGFuY2Uuc3Vic2NyaWJlTG9jayA9IHtcbiAgICAgICAgICByZWNvbW1lbmRlZFJldHJ5VGltZTogbWV0YWRhdGEucmVjb21tZW5kZWRSZXRyeVRpbWUsXG4gICAgICAgICAgdHlwZTogbWV0YWRhdGEudHlwZSxcbiAgICAgICAgICBsb2NrZWRBdEFjY291bnRzOiBzdWJzY3JpYmVkQWNjb3VudHMubGVuZ3RoXG4gICAgICAgIH07XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENvbm5lY3RzIHRvIE1ldGFBcGkgc2VydmVyIHZpYSBzb2NrZXQuaW8gcHJvdG9jb2xcbiAgICogQHBhcmFtIHtOdW1iZXJ9IGluc3RhbmNlTnVtYmVyIGluc3RhbmNlIGluZGV4IG51bWJlclxuICAgKiBAcGFyYW0ge1N0cmluZ30gcmVnaW9uIHNlcnZlciByZWdpb25cbiAgICogQHJldHVybnMge1Byb21pc2V9IHByb21pc2Ugd2hpY2ggcmVzb2x2ZXMgd2hlbiBjb25uZWN0aW9uIGlzIGVzdGFibGlzaGVkXG4gICAqL1xuICBhc3luYyBjb25uZWN0KGluc3RhbmNlTnVtYmVyLCByZWdpb24pIHtcbiAgICBpZiAodGhpcy5fcmVnaW9uICYmIHJlZ2lvbiAhPT0gdGhpcy5fcmVnaW9uKSB7XG4gICAgICB0aHJvdyBuZXcgVmFsaWRhdGlvbkVycm9yKGBUcnlpbmcgdG8gY29ubmVjdCB0byAke3JlZ2lvbn0gcmVnaW9uLCBidXQgY29uZmlndXJlZCB3aXRoICR7dGhpcy5fcmVnaW9ufWApO1xuICAgIH1cbiAgICBsZXQgY2xpZW50SWQgPSBNYXRoLnJhbmRvbSgpO1xuICAgIGxldCByZXNvbHZlO1xuICAgIGxldCByZXN1bHQgPSBuZXcgUHJvbWlzZSgocmVzLCByZWopID0+IHtcbiAgICAgIHJlc29sdmUgPSByZXM7XG4gICAgfSk7XG4gICAgdGhpcy5fc29ja2V0SW5zdGFuY2VzW3JlZ2lvbl0gfHw9IHt9O1xuICAgIHRoaXMuX3NvY2tldEluc3RhbmNlc1tyZWdpb25dW2luc3RhbmNlTnVtYmVyXSB8fD0gW107XG4gICAgY29uc3Qgc29ja2V0SW5zdGFuY2VJbmRleCA9IHRoaXMuX3NvY2tldEluc3RhbmNlc1tyZWdpb25dW2luc3RhbmNlTnVtYmVyXS5sZW5ndGg7XG4gICAgY29uc3QgaW5zdGFuY2U6IE1ldGFBcGlXZWJzb2NrZXRDbGllbnQuU29ja2V0SW5zdGFuY2UgPSB7XG4gICAgICBpZDogc29ja2V0SW5zdGFuY2VJbmRleCxcbiAgICAgIHJlY29ubmVjdFdhaXRUaW1lOiB0aGlzLl9zb2NrZXRNaW5pbXVtUmVjb25uZWN0VGltZW91dCxcbiAgICAgIGNvbm5lY3RlZDogZmFsc2UsXG4gICAgICByZXF1ZXN0UmVzb2x2ZXM6IHt9LFxuICAgICAgcmVzb2x2ZWQ6IGZhbHNlLFxuICAgICAgY29ubmVjdFJlc3VsdDogcmVzdWx0LFxuICAgICAgc2Vzc2lvbklkOiByYW5kb21zdHJpbmcuZ2VuZXJhdGUoMzIpLFxuICAgICAgaXNSZWNvbm5lY3Rpbmc6IGZhbHNlLFxuICAgICAgc29ja2V0OiBudWxsLFxuICAgICAgc3luY2hyb25pemF0aW9uVGhyb3R0bGVyOiBuZXcgU3luY2hyb25pemF0aW9uVGhyb3R0bGVyKHRoaXMsIHNvY2tldEluc3RhbmNlSW5kZXgsIGluc3RhbmNlTnVtYmVyLCByZWdpb24sXG4gICAgICAgIHRoaXMuX3N5bmNocm9uaXphdGlvblRocm90dGxlck9wdHMpLFxuICAgICAgc3Vic2NyaWJlTG9jazogbnVsbCxcbiAgICAgIGluc3RhbmNlTnVtYmVyXG4gICAgfTtcbiAgICBpbnN0YW5jZS5jb25uZWN0ZWQgPSB0cnVlO1xuICAgIHRoaXMuX3NvY2tldEluc3RhbmNlc1tyZWdpb25dW2luc3RhbmNlTnVtYmVyXS5wdXNoKGluc3RhbmNlKTtcbiAgICBpbnN0YW5jZS5zeW5jaHJvbml6YXRpb25UaHJvdHRsZXIuc3RhcnQoKTtcbiAgICBjb25zdCBzZXJ2ZXJVcmwgPSBhd2FpdCB0aGlzLl9nZXRTZXJ2ZXJVcmwoaW5zdGFuY2VOdW1iZXIsIHNvY2tldEluc3RhbmNlSW5kZXgsIHJlZ2lvbik7XG4gICAgY29uc3Qgc29ja2V0SW5zdGFuY2UgPSBzb2NrZXRJTyhzZXJ2ZXJVcmwsIHtcbiAgICAgIHBhdGg6ICcvd3MnLFxuICAgICAgcmVjb25uZWN0aW9uOiB0cnVlLFxuICAgICAgcmVjb25uZWN0aW9uRGVsYXk6IDEwMDAsXG4gICAgICByZWNvbm5lY3Rpb25EZWxheU1heDogNTAwMCxcbiAgICAgIHJlY29ubmVjdGlvbkF0dGVtcHRzOiBJbmZpbml0eSxcbiAgICAgIHRpbWVvdXQ6IHRoaXMuX2Nvbm5lY3RUaW1lb3V0LFxuICAgICAgZXh0cmFIZWFkZXJzOiB7XG4gICAgICAgICdDbGllbnQtSWQnOiBjbGllbnRJZFxuICAgICAgfSxcbiAgICAgIHF1ZXJ5OiB7XG4gICAgICAgICdhdXRoLXRva2VuJzogdGhpcy5fdG9rZW4sXG4gICAgICAgIGNsaWVudElkOiBjbGllbnRJZCxcbiAgICAgICAgcHJvdG9jb2w6IDNcbiAgICAgIH1cbiAgICB9KTtcbiAgICBpbnN0YW5jZS5zb2NrZXQgPSBzb2NrZXRJbnN0YW5jZTtcbiAgICBzb2NrZXRJbnN0YW5jZS5vbignY29ubmVjdCcsIGFzeW5jICgpID0+IHtcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgICB0aGlzLl9sb2dnZXIuaW5mbyhgJHtyZWdpb259OiR7aW5zdGFuY2VOdW1iZXJ9OiBNZXRhQXBpIHdlYnNvY2tldCBjbGllbnQgY29ubmVjdGVkIHRvIHRoZSBNZXRhQXBpIHNlcnZlcmApO1xuICAgICAgaW5zdGFuY2UucmVjb25uZWN0V2FpdFRpbWUgPSB0aGlzLl9zb2NrZXRNaW5pbXVtUmVjb25uZWN0VGltZW91dDtcbiAgICAgIGluc3RhbmNlLmlzUmVjb25uZWN0aW5nID0gZmFsc2U7XG4gICAgICBpZiAoIWluc3RhbmNlLnJlc29sdmVkKSB7XG4gICAgICAgIGluc3RhbmNlLnJlc29sdmVkID0gdHJ1ZTtcbiAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgYXdhaXQgdGhpcy5fZmlyZVJlY29ubmVjdGVkKGluc3RhbmNlTnVtYmVyLCBpbnN0YW5jZS5pZCwgcmVnaW9uKTtcbiAgICAgIH1cbiAgICAgIGlmICghaW5zdGFuY2UuY29ubmVjdGVkKSB7XG4gICAgICAgIGluc3RhbmNlLnNvY2tldC5jbG9zZSgpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHNvY2tldEluc3RhbmNlLm9uKCdyZWNvbm5lY3QnLCBhc3luYyAoKSA9PiB7XG4gICAgICBpbnN0YW5jZS5pc1JlY29ubmVjdGluZyA9IGZhbHNlO1xuICAgICAgdGhpcy5fbG9nZ2VyLmluZm8oYCR7cmVnaW9ufToke2luc3RhbmNlTnVtYmVyfTogTWV0YUFwaSB3ZWJzb2NrZXQgY2xpZW50IHJlY29ubmVjdGVkYCk7XG4gICAgICBhd2FpdCB0aGlzLl9maXJlUmVjb25uZWN0ZWQoaW5zdGFuY2VOdW1iZXIsIGluc3RhbmNlLmlkLCByZWdpb24pO1xuICAgIH0pO1xuICAgIHNvY2tldEluc3RhbmNlLm9uKCdjb25uZWN0X2Vycm9yJywgYXN5bmMgKGVycikgPT4ge1xuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgIHRoaXMuX2xvZ2dlci5lcnJvcihgJHtyZWdpb259OiR7aW5zdGFuY2VOdW1iZXJ9OiBNZXRhQXBpIHdlYnNvY2tldCBjbGllbnQgY29ubmVjdGlvbiBlcnJvcmAsIGVycik7XG4gICAgICBpbnN0YW5jZS5pc1JlY29ubmVjdGluZyA9IGZhbHNlO1xuICAgICAgYXdhaXQgdGhpcy5fcmVjb25uZWN0KGluc3RhbmNlTnVtYmVyLCBpbnN0YW5jZS5pZCwgcmVnaW9uKTtcbiAgICB9KTtcbiAgICBzb2NrZXRJbnN0YW5jZS5vbignY29ubmVjdF90aW1lb3V0JywgYXN5bmMgKHRpbWVvdXQpID0+IHtcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgICB0aGlzLl9sb2dnZXIuZXJyb3IoYCR7cmVnaW9ufToke2luc3RhbmNlTnVtYmVyfTogTWV0YUFwaSB3ZWJzb2NrZXQgY2xpZW50IGNvbm5lY3Rpb24gdGltZW91dGApO1xuICAgICAgaW5zdGFuY2UuaXNSZWNvbm5lY3RpbmcgPSBmYWxzZTtcbiAgICAgIGlmICghaW5zdGFuY2UucmVzb2x2ZWQpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5fcmVjb25uZWN0KGluc3RhbmNlTnVtYmVyLCBpbnN0YW5jZS5pZCwgcmVnaW9uKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICBzb2NrZXRJbnN0YW5jZS5vbignZGlzY29ubmVjdCcsIGFzeW5jIChyZWFzb24pID0+IHtcbiAgICAgIGluc3RhbmNlLnN5bmNocm9uaXphdGlvblRocm90dGxlci5vbkRpc2Nvbm5lY3QoKTtcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgICB0aGlzLl9sb2dnZXIuaW5mbyhgJHtyZWdpb259OiR7aW5zdGFuY2VOdW1iZXJ9OiBNZXRhQXBpIHdlYnNvY2tldCBjbGllbnQgZGlzY29ubmVjdGVkIGZyb20gdGhlIGAgK1xuICAgICAgICBgTWV0YUFwaSBzZXJ2ZXIgYmVjYXVzZSBvZiAke3JlYXNvbn1gKTtcbiAgICAgIGluc3RhbmNlLmlzUmVjb25uZWN0aW5nID0gZmFsc2U7XG4gICAgICBhd2FpdCB0aGlzLl9yZWNvbm5lY3QoaW5zdGFuY2VOdW1iZXIsIGluc3RhbmNlLmlkLCByZWdpb24pO1xuICAgIH0pO1xuICAgIHNvY2tldEluc3RhbmNlLm9uKCdlcnJvcicsIGFzeW5jIChlcnJvcikgPT4ge1xuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgIHRoaXMuX2xvZ2dlci5lcnJvcihgJHtyZWdpb259OiR7aW5zdGFuY2VOdW1iZXJ9OiBNZXRhQXBpIHdlYnNvY2tldCBjbGllbnQgZXJyb3JgLCBlcnJvcik7XG4gICAgICBpbnN0YW5jZS5pc1JlY29ubmVjdGluZyA9IGZhbHNlO1xuICAgICAgYXdhaXQgdGhpcy5fcmVjb25uZWN0KGluc3RhbmNlTnVtYmVyLCBpbnN0YW5jZS5pZCwgcmVnaW9uKTtcbiAgICB9KTtcbiAgICBzb2NrZXRJbnN0YW5jZS5vbigncmVzcG9uc2UnLCBkYXRhID0+IHtcbiAgICAgIGlmICh0eXBlb2YgZGF0YSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgZGF0YSA9IEpTT04ucGFyc2UoZGF0YSk7XG4gICAgICB9XG4gICAgICB0aGlzLl9sb2dnZXIuZGVidWcoKCkgPT4gYCR7ZGF0YS5hY2NvdW50SWR9OiBSZXNwb25zZSByZWNlaXZlZDogJHtKU09OLnN0cmluZ2lmeSh7XG4gICAgICAgIHJlcXVlc3RJZDogZGF0YS5yZXF1ZXN0SWQsIHRpbWVzdGFtcHM6IGRhdGEudGltZXN0YW1wc30pfWApO1xuICAgICAgbGV0IHJlcXVlc3RSZXNvbHZlID0gKGluc3RhbmNlLnJlcXVlc3RSZXNvbHZlc1tkYXRhLnJlcXVlc3RJZF0gfHwge3Jlc29sdmU6ICgpID0+IHt9LCByZWplY3Q6ICgpID0+IHt9fSk7XG4gICAgICBkZWxldGUgaW5zdGFuY2UucmVxdWVzdFJlc29sdmVzW2RhdGEucmVxdWVzdElkXTtcbiAgICAgIHRoaXMuX2NvbnZlcnRJc29UaW1lVG9EYXRlKGRhdGEpO1xuICAgICAgcmVxdWVzdFJlc29sdmUucmVzb2x2ZShkYXRhKTtcbiAgICAgIGlmIChkYXRhLnRpbWVzdGFtcHMgJiYgcmVxdWVzdFJlc29sdmUudHlwZSkge1xuICAgICAgICBkYXRhLnRpbWVzdGFtcHMuY2xpZW50UHJvY2Vzc2luZ0ZpbmlzaGVkID0gbmV3IERhdGUoKTtcbiAgICAgICAgZm9yIChsZXQgbGlzdGVuZXIgb2YgdGhpcy5fbGF0ZW5jeUxpc3RlbmVycykge1xuICAgICAgICAgIFByb21pc2UucmVzb2x2ZSgpXG4gICAgICAgICAgICAudGhlbigoKSA9PiByZXF1ZXN0UmVzb2x2ZS50eXBlID09PSAndHJhZGUnID9cbiAgICAgICAgICAgICAgbGlzdGVuZXIub25UcmFkZShkYXRhLmFjY291bnRJZCwgZGF0YS50aW1lc3RhbXBzKSA6XG4gICAgICAgICAgICAgIGxpc3RlbmVyLm9uUmVzcG9uc2UoZGF0YS5hY2NvdW50SWQsIHJlcXVlc3RSZXNvbHZlLnR5cGUsIGRhdGEudGltZXN0YW1wcykpXG4gICAgICAgICAgICAuY2F0Y2goZXJyb3IgPT4gdGhpcy5fbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gcHJvY2VzcyBvblJlc3BvbnNlIGV2ZW50IGZvciBhY2NvdW50ICcgK1xuICAgICAgICAgICAgICBkYXRhLmFjY291bnRJZCArICcsIHJlcXVlc3QgdHlwZSAnICsgcmVxdWVzdFJlc29sdmUudHlwZSwgZXJyb3IpKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICAgIHNvY2tldEluc3RhbmNlLm9uKCdwcm9jZXNzaW5nRXJyb3InLCBkYXRhID0+IHtcbiAgICAgIGxldCByZXF1ZXN0UmVzb2x2ZSA9IChpbnN0YW5jZS5yZXF1ZXN0UmVzb2x2ZXNbZGF0YS5yZXF1ZXN0SWRdIHx8IHtyZXNvbHZlOiAoKSA9PiB7fSwgcmVqZWN0OiAoKSA9PiB7fX0pO1xuICAgICAgZGVsZXRlIGluc3RhbmNlLnJlcXVlc3RSZXNvbHZlc1tkYXRhLnJlcXVlc3RJZF07XG4gICAgICByZXF1ZXN0UmVzb2x2ZS5yZWplY3QodGhpcy5fY29udmVydEVycm9yKGRhdGEpKTtcbiAgICB9KTtcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eVxuICAgIHNvY2tldEluc3RhbmNlLm9uKCdzeW5jaHJvbml6YXRpb24nLCBhc3luYyBkYXRhID0+IHtcbiAgICAgIGlmICh0eXBlb2YgZGF0YSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgZGF0YSA9IEpTT04ucGFyc2UoZGF0YSk7XG4gICAgICB9XG4gICAgICBpZiAoZGF0YS5pbnN0YW5jZUluZGV4ICYmIGRhdGEuaW5zdGFuY2VJbmRleCAhPT0gaW5zdGFuY2VOdW1iZXIpIHtcbiAgICAgICAgdGhpcy5fbG9nZ2VyLnRyYWNlKCgpID0+IGAke2RhdGEuYWNjb3VudElkfToke2RhdGEuaW5zdGFuY2VOdW1iZXJ9OiByZWNlaXZlZCBwYWNrZXQgd2l0aCB3cm9uZyBpbnN0YW5jZSBgICtcbiAgICAgICAgICBgaW5kZXggdmlhIGEgc29ja2V0IHdpdGggaW5zdGFuY2UgbnVtYmVyIG9mICR7aW5zdGFuY2VOdW1iZXJ9LCBkYXRhPSR7SlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICAgICAgdHlwZTogZGF0YS50eXBlLCBzZXF1ZW5jZU51bWJlcjogZGF0YS5zZXF1ZW5jZU51bWJlciwgc2VxdWVuY2VUaW1lc3RhbXA6IGRhdGEuc2VxdWVuY2VUaW1lc3RhbXAsXG4gICAgICAgICAgICBzeW5jaHJvbml6YXRpb25JZDogZGF0YS5zeW5jaHJvbml6YXRpb25JZCwgYXBwbGljYXRpb246IGRhdGEuYXBwbGljYXRpb24sIGhvc3Q6IGRhdGEuaG9zdCxcbiAgICAgICAgICAgIHNwZWNpZmljYXRpb25zVXBkYXRlZDogZGF0YS5zcGVjaWZpY2F0aW9uc1VwZGF0ZWQsIHBvc2l0aW9uc1VwZGF0ZWQ6IGRhdGEucG9zaXRpb25zVXBkYXRlZCxcbiAgICAgICAgICAgIG9yZGVyc1VwZGF0ZWQ6IGRhdGEub3JkZXJzVXBkYXRlZCxcbiAgICAgICAgICAgIHNwZWNpZmljYXRpb25zOiBkYXRhLnNwZWNpZmljYXRpb25zID8gKGRhdGEuc3BlY2lmaWNhdGlvbnMgfHwgW10pLmxlbmd0aCA6IHVuZGVmaW5lZH0pfWApO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLl9yZWdpb25zQnlBY2NvdW50c1tkYXRhLmFjY291bnRJZF0gfHw9IHtyZWdpb24sIGNvbm5lY3Rpb25zOiAwLCBsYXN0VXNlZDogRGF0ZS5ub3coKX07XG4gICAgICB0aGlzLl9sb2dnZXIudHJhY2UoKCkgPT4gYCR7ZGF0YS5hY2NvdW50SWR9OiR7ZGF0YS5pbnN0YW5jZUluZGV4fTogU3luYyBwYWNrZXQgcmVjZWl2ZWQ6ICR7SlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICB0eXBlOiBkYXRhLnR5cGUsIHNlcXVlbmNlTnVtYmVyOiBkYXRhLnNlcXVlbmNlTnVtYmVyLCBzZXF1ZW5jZVRpbWVzdGFtcDogZGF0YS5zZXF1ZW5jZVRpbWVzdGFtcCxcbiAgICAgICAgc3luY2hyb25pemF0aW9uSWQ6IGRhdGEuc3luY2hyb25pemF0aW9uSWQsIGFwcGxpY2F0aW9uOiBkYXRhLmFwcGxpY2F0aW9uLCBob3N0OiBkYXRhLmhvc3QsIFxuICAgICAgICBzcGVjaWZpY2F0aW9uc1VwZGF0ZWQ6IGRhdGEuc3BlY2lmaWNhdGlvbnNVcGRhdGVkLCBwb3NpdGlvbnNVcGRhdGVkOiBkYXRhLnBvc2l0aW9uc1VwZGF0ZWQsXG4gICAgICAgIG9yZGVyc1VwZGF0ZWQ6IGRhdGEub3JkZXJzVXBkYXRlZCwgXG4gICAgICAgIHNwZWNpZmljYXRpb25zOiBkYXRhLnNwZWNpZmljYXRpb25zID8gKGRhdGEuc3BlY2lmaWNhdGlvbnMgfHwgW10pLmxlbmd0aCA6IHVuZGVmaW5lZH0pfSwgYCArXG4gICAgICAgIGBhY3RpdmUgbGlzdGVuZXJzOiAkeyh0aGlzLl9zeW5jaHJvbml6YXRpb25MaXN0ZW5lcnNbZGF0YS5hY2NvdW50SWRdIHx8IFtdKS5sZW5ndGh9YCk7XG4gICAgICBsZXQgYWN0aXZlU3luY2hyb25pemF0aW9uSWRzID0gaW5zdGFuY2Uuc3luY2hyb25pemF0aW9uVGhyb3R0bGVyLmFjdGl2ZVN5bmNocm9uaXphdGlvbklkczsgXG4gICAgICBpZiAoIWRhdGEuc3luY2hyb25pemF0aW9uSWQgfHwgYWN0aXZlU3luY2hyb25pemF0aW9uSWRzLmluY2x1ZGVzKGRhdGEuc3luY2hyb25pemF0aW9uSWQpKSB7XG4gICAgICAgIGlmICh0aGlzLl9wYWNrZXRMb2dnZXIpIHtcbiAgICAgICAgICBhd2FpdCB0aGlzLl9wYWNrZXRMb2dnZXIubG9nUGFja2V0KGRhdGEpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGlnbm9yZWRQYWNrZXRUeXBlcyA9IFsnZGlzY29ubmVjdGVkJywgJ3N0YXR1cycsICdrZWVwYWxpdmUnXTtcbiAgICAgICAgaWYgKCF0aGlzLl9zdWJzY3JpcHRpb25NYW5hZ2VyLmlzU3Vic2NyaXB0aW9uQWN0aXZlKGRhdGEuYWNjb3VudElkKSAmJiBcbiAgICAgICAgICAhaWdub3JlZFBhY2tldFR5cGVzLmluY2x1ZGVzKGRhdGEudHlwZSkpIHtcbiAgICAgICAgICB0aGlzLl9sb2dnZXIuZGVidWcoYCR7ZGF0YS5hY2NvdW50SWR9OiBQYWNrZXQgYXJyaXZlZCB0byBpbmFjdGl2ZSBjb25uZWN0aW9uLCBhdHRlbXB0aW5nYCArXG4gICAgICAgICAgICBgIHVuc3Vic2NyaWJlLCBwYWNrZXQ6ICR7ZGF0YS50eXBlfWApO1xuICAgICAgICAgIGlmICh0aGlzLl90aHJvdHRsZVJlcXVlc3QoJ3Vuc3Vic2NyaWJlJywgZGF0YS5hY2NvdW50SWQsIGRhdGEuaW5zdGFuY2VJbmRleCwgXG4gICAgICAgICAgICB0aGlzLl91bnN1YnNjcmliZVRocm90dGxpbmdJbnRlcnZhbCkpIHtcbiAgICAgICAgICAgIHRoaXMudW5zdWJzY3JpYmUoZGF0YS5hY2NvdW50SWQpLmNhdGNoKGVyciA9PiB7XG4gICAgICAgICAgICAgIHRoaXMuX2xvZ2dlci53YXJuKGAke2RhdGEuYWNjb3VudElkfToke2RhdGEuaW5zdGFuY2VJbmRleCB8fCAwfTogZmFpbGVkIHRvIHVuc3Vic2NyaWJlYCwgZXJyKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5fY29udmVydElzb1RpbWVUb0RhdGUoZGF0YSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBkYXRhLnR5cGUgPSAnbm9vcCc7XG4gICAgICB9XG4gICAgICB0aGlzLnF1ZXVlUGFja2V0KGluc3RhbmNlLCBkYXRhKTtcbiAgICB9KTtcbiAgICBzb2NrZXRJbnN0YW5jZS5vbignbWV0YWRhdGEnLCAoZGF0YTogTWV0YWRhdGFQYWNrZXQpID0+IHtcbiAgICAgIGluc3RhbmNlLmNsaWVudEFwaUhvc3RuYW1lID0gZGF0YS5jbGllbnRBcGlIb3N0bmFtZTtcbiAgICB9KTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgLyoqXG4gICAqIENsb3NlcyBjb25uZWN0aW9uIHRvIE1ldGFBcGkgc2VydmVyXG4gICAqL1xuICBjbG9zZSgpIHtcbiAgICBPYmplY3Qua2V5cyh0aGlzLl9zb2NrZXRJbnN0YW5jZXMpLmZvckVhY2gocmVnaW9uID0+IHtcbiAgICAgIE9iamVjdC5rZXlzKHRoaXMuX3NvY2tldEluc3RhbmNlc1tyZWdpb25dKS5mb3JFYWNoKGluc3RhbmNlTnVtYmVyID0+IHtcbiAgICAgICAgdGhpcy5fc29ja2V0SW5zdGFuY2VzW3JlZ2lvbl1baW5zdGFuY2VOdW1iZXJdLmZvckVhY2goYXN5bmMgKGluc3RhbmNlKSA9PiB7XG4gICAgICAgICAgaWYgKGluc3RhbmNlLmNvbm5lY3RlZCkge1xuICAgICAgICAgICAgaW5zdGFuY2UuY29ubmVjdGVkID0gZmFsc2U7XG4gICAgICAgICAgICBhd2FpdCBpbnN0YW5jZS5zb2NrZXQuY2xvc2UoKTtcbiAgICAgICAgICAgIGZvciAobGV0IHJlcXVlc3RSZXNvbHZlIG9mIE9iamVjdC52YWx1ZXM8YW55PihpbnN0YW5jZS5yZXF1ZXN0UmVzb2x2ZXMpKSB7XG4gICAgICAgICAgICAgIHJlcXVlc3RSZXNvbHZlLnJlamVjdChuZXcgRXJyb3IoJ01ldGFBcGkgY29ubmVjdGlvbiBjbG9zZWQnKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpbnN0YW5jZS5yZXF1ZXN0UmVzb2x2ZXMgPSB7fTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLl9zb2NrZXRJbnN0YW5jZXNCeUFjY291bnRzW2luc3RhbmNlTnVtYmVyXSA9IHt9O1xuICAgICAgICB0aGlzLl9zb2NrZXRJbnN0YW5jZXNbcmVnaW9uXVtpbnN0YW5jZU51bWJlcl0gPSBbXTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICAgIHRoaXMuX3N5bmNocm9uaXphdGlvbkxpc3RlbmVycyA9IHt9O1xuICAgIHRoaXMuX2xhdGVuY3lMaXN0ZW5lcnMgPSBbXTtcbiAgICB0aGlzLl9wYWNrZXRPcmRlcmVyLnN0b3AoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdG9wcyB0aGUgY2xpZW50XG4gICAqL1xuICBzdG9wKCkge1xuICAgIGNsZWFySW50ZXJ2YWwodGhpcy5fY2xlYXJBY2NvdW50Q2FjaGVJbnRlcnZhbCk7XG4gICAgY2xlYXJJbnRlcnZhbCh0aGlzLl9jbGVhckluYWN0aXZlU3luY0RhdGFJbnRlcnZhbCk7XG4gICAgdGhpcy5fbGF0ZW5jeVNlcnZpY2Uuc3RvcCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYWNjb3VudCBpbmZvcm1hdGlvbiBmb3IgYSBzcGVjaWZpZWQgTWV0YVRyYWRlciBhY2NvdW50LlxuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGlkIG9mIHRoZSBNZXRhVHJhZGVyIGFjY291bnQgdG8gcmV0dXJuIGluZm9ybWF0aW9uIGZvclxuICAgKiBAcGFyYW0ge0dldEFjY291bnRJbmZvcm1hdGlvbk9wdGlvbnN9IFtvcHRpb25zXSBhZGRpdGlvbmFsIHJlcXVlc3Qgb3B0aW9uc1xuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxNZXRhdHJhZGVyQWNjb3VudEluZm9ybWF0aW9uPn0gcHJvbWlzZSByZXNvbHZpbmcgd2l0aCBhY2NvdW50IGluZm9ybWF0aW9uXG4gICAqL1xuICBhc3luYyBnZXRBY2NvdW50SW5mb3JtYXRpb24oYWNjb3VudElkLCBvcHRpb25zKSB7XG4gICAgbGV0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5ycGNSZXF1ZXN0KGFjY291bnRJZCwge2FwcGxpY2F0aW9uOiAnUlBDJywgdHlwZTogJ2dldEFjY291bnRJbmZvcm1hdGlvbicsIC4uLm9wdGlvbnN9KTtcbiAgICByZXR1cm4gcmVzcG9uc2UuYWNjb3VudEluZm9ybWF0aW9uO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgcG9zaXRpb25zIGZvciBhIHNwZWNpZmllZCBNZXRhVHJhZGVyIGFjY291bnQuXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgaWQgb2YgdGhlIE1ldGFUcmFkZXIgYWNjb3VudCB0byByZXR1cm4gaW5mb3JtYXRpb24gZm9yXG4gICAqIEBwYXJhbSB7R2V0UG9zaXRpb25zT3B0aW9uc30gW29wdGlvbnNdIGFkZGl0aW9uYWwgcmVxdWVzdCBvcHRpb25zXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPEFycmF5PE1ldGF0cmFkZXJQb3NpdGlvbj59IHByb21pc2UgcmVzb2x2aW5nIHdpdGggYXJyYXkgb2Ygb3BlbiBwb3NpdGlvbnNcbiAgICovXG4gIGFzeW5jIGdldFBvc2l0aW9ucyhhY2NvdW50SWQsIG9wdGlvbnMpIHtcbiAgICBsZXQgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLnJwY1JlcXVlc3QoYWNjb3VudElkLCB7YXBwbGljYXRpb246ICdSUEMnLCB0eXBlOiAnZ2V0UG9zaXRpb25zJywgLi4ub3B0aW9uc30pO1xuICAgIHJldHVybiByZXNwb25zZS5wb3NpdGlvbnM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBzcGVjaWZpYyBwb3NpdGlvbiBmb3IgYSBNZXRhVHJhZGVyIGFjY291bnQuXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgaWQgb2YgdGhlIE1ldGFUcmFkZXIgYWNjb3VudCB0byByZXR1cm4gaW5mb3JtYXRpb24gZm9yXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBwb3NpdGlvbklkIHBvc2l0aW9uIGlkXG4gICAqIEBwYXJhbSB7R2V0UG9zaXRpb25PcHRpb25zfSBbb3B0aW9uc10gYWRkaXRpb25hbCByZXF1ZXN0IG9wdGlvbnNcbiAgICogQHJldHVybiB7UHJvbWlzZTxNZXRhdHJhZGVyUG9zaXRpb24+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIE1ldGFUcmFkZXIgcG9zaXRpb24gZm91bmRcbiAgICovXG4gIGFzeW5jIGdldFBvc2l0aW9uKGFjY291bnRJZCwgcG9zaXRpb25JZCwgb3B0aW9ucykge1xuICAgIGxldCByZXNwb25zZSA9IGF3YWl0IHRoaXMucnBjUmVxdWVzdChhY2NvdW50SWQsIHthcHBsaWNhdGlvbjogJ1JQQycsIHR5cGU6ICdnZXRQb3NpdGlvbicsIHBvc2l0aW9uSWQsIC4uLm9wdGlvbnN9KTtcbiAgICByZXR1cm4gcmVzcG9uc2UucG9zaXRpb247XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBvcGVuIG9yZGVycyBmb3IgYSBzcGVjaWZpZWQgTWV0YVRyYWRlciBhY2NvdW50LlxuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGlkIG9mIHRoZSBNZXRhVHJhZGVyIGFjY291bnQgdG8gcmV0dXJuIGluZm9ybWF0aW9uIGZvclxuICAgKiBAcGFyYW0ge0dldE9yZGVyc09wdGlvbnN9IFtvcHRpb25zXSBhZGRpdGlvbmFsIHJlcXVlc3Qgb3B0aW9uc1xuICAgKiBAcmV0dXJuIHtQcm9taXNlPEFycmF5PE1ldGF0cmFkZXJPcmRlcj4+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIG9wZW4gTWV0YVRyYWRlciBvcmRlcnNcbiAgICovXG4gIGFzeW5jIGdldE9yZGVycyhhY2NvdW50SWQsIG9wdGlvbnMpIHtcbiAgICBsZXQgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLnJwY1JlcXVlc3QoYWNjb3VudElkLCB7YXBwbGljYXRpb246ICdSUEMnLCB0eXBlOiAnZ2V0T3JkZXJzJywgLi4ub3B0aW9uc30pO1xuICAgIHJldHVybiByZXNwb25zZS5vcmRlcnM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBzcGVjaWZpYyBvcGVuIG9yZGVyIGZvciBhIE1ldGFUcmFkZXIgYWNjb3VudC5cbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBpZCBvZiB0aGUgTWV0YVRyYWRlciBhY2NvdW50IHRvIHJldHVybiBpbmZvcm1hdGlvbiBmb3JcbiAgICogQHBhcmFtIHtTdHJpbmd9IG9yZGVySWQgb3JkZXIgaWQgKHRpY2tldCBudW1iZXIpXG4gICAqIEBwYXJhbSB7R2V0T3JkZXJPcHRpb25zfSBbb3B0aW9uc10gYWRkaXRpb25hbCByZXF1ZXN0IG9wdGlvbnNcbiAgICogQHJldHVybiB7UHJvbWlzZTxNZXRhdHJhZGVyT3JkZXI+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIG1ldGF0cmFkZXIgb3JkZXIgZm91bmRcbiAgICovXG4gIGFzeW5jIGdldE9yZGVyKGFjY291bnRJZCwgb3JkZXJJZCwgb3B0aW9ucykge1xuICAgIGxldCByZXNwb25zZSA9IGF3YWl0IHRoaXMucnBjUmVxdWVzdChhY2NvdW50SWQsIHthcHBsaWNhdGlvbjogJ1JQQycsIHR5cGU6ICdnZXRPcmRlcicsIG9yZGVySWQsIC4uLm9wdGlvbnN9KTtcbiAgICByZXR1cm4gcmVzcG9uc2Uub3JkZXI7XG4gIH1cblxuICAvKipcbiAgICogTWV0YVRyYWRlciBoaXN0b3J5IG9yZGVycyBzZWFyY2ggcXVlcnkgcmVzcG9uc2VcbiAgICogQHR5cGVkZWYge09iamVjdH0gTWV0YXRyYWRlckhpc3RvcnlPcmRlcnNcbiAgICogQHByb3BlcnR5IHtBcnJheTxNZXRhdHJhZGVyT3JkZXI+fSBoaXN0b3J5T3JkZXJzIGFycmF5IG9mIGhpc3Rvcnkgb3JkZXJzIHJldHVybmVkXG4gICAqIEBwcm9wZXJ0eSB7Qm9vbGVhbn0gc3luY2hyb25pemluZyBmbGFnIGluZGljYXRpbmcgdGhhdCBoaXN0b3J5IG9yZGVyIGluaXRpYWwgc3luY2hyb25pemF0aW9uIGlzIHN0aWxsIGluIHByb2dyZXNzXG4gICAqIGFuZCB0aHVzIHNlYXJjaCByZXN1bHRzIG1heSBiZSBpbmNvbXBsZXRlXG4gICAqL1xuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBoaXN0b3J5IG9mIGNvbXBsZXRlZCBvcmRlcnMgZm9yIGEgc3BlY2lmaWMgdGlja2V0IG51bWJlci5cbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBpZCBvZiB0aGUgTWV0YVRyYWRlciBhY2NvdW50IHRvIHJldHVybiBpbmZvcm1hdGlvbiBmb3JcbiAgICogQHBhcmFtIHtTdHJpbmd9IHRpY2tldCB0aWNrZXQgbnVtYmVyIChvcmRlciBpZClcbiAgICogQHJldHVybnMge1Byb21pc2U8TWV0YXRyYWRlckhpc3RvcnlPcmRlcnM+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIHJlcXVlc3QgcmVzdWx0cyBjb250YWluaW5nIGhpc3Rvcnkgb3JkZXJzIGZvdW5kXG4gICAqL1xuICBhc3luYyBnZXRIaXN0b3J5T3JkZXJzQnlUaWNrZXQoYWNjb3VudElkLCB0aWNrZXQpIHtcbiAgICBsZXQgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLnJwY1JlcXVlc3QoYWNjb3VudElkLCB7YXBwbGljYXRpb246ICdSUEMnLCB0eXBlOiAnZ2V0SGlzdG9yeU9yZGVyc0J5VGlja2V0JywgdGlja2V0fSk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGhpc3RvcnlPcmRlcnM6IHJlc3BvbnNlLmhpc3RvcnlPcmRlcnMsXG4gICAgICBzeW5jaHJvbml6aW5nOiByZXNwb25zZS5zeW5jaHJvbml6aW5nXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBoaXN0b3J5IG9mIGNvbXBsZXRlZCBvcmRlcnMgZm9yIGEgc3BlY2lmaWMgcG9zaXRpb24gaWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBpZCBvZiB0aGUgTWV0YVRyYWRlciBhY2NvdW50IHRvIHJldHVybiBpbmZvcm1hdGlvbiBmb3JcbiAgICogQHBhcmFtIHtTdHJpbmd9IHBvc2l0aW9uSWQgcG9zaXRpb24gaWRcbiAgICogQHJldHVybnMge1Byb21pc2U8TWV0YXRyYWRlckhpc3RvcnlPcmRlcnM+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIHJlcXVlc3QgcmVzdWx0cyBjb250YWluaW5nIGhpc3Rvcnkgb3JkZXJzIGZvdW5kXG4gICAqL1xuICBhc3luYyBnZXRIaXN0b3J5T3JkZXJzQnlQb3NpdGlvbihhY2NvdW50SWQsIHBvc2l0aW9uSWQpIHtcbiAgICBsZXQgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLnJwY1JlcXVlc3QoYWNjb3VudElkLCB7YXBwbGljYXRpb246ICdSUEMnLCB0eXBlOiAnZ2V0SGlzdG9yeU9yZGVyc0J5UG9zaXRpb24nLFxuICAgICAgcG9zaXRpb25JZH0pO1xuICAgIHJldHVybiB7XG4gICAgICBoaXN0b3J5T3JkZXJzOiByZXNwb25zZS5oaXN0b3J5T3JkZXJzLFxuICAgICAgc3luY2hyb25pemluZzogcmVzcG9uc2Uuc3luY2hyb25pemluZ1xuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgaGlzdG9yeSBvZiBjb21wbGV0ZWQgb3JkZXJzIGZvciBhIHNwZWNpZmljIHRpbWUgcmFuZ2VcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBpZCBvZiB0aGUgTWV0YVRyYWRlciBhY2NvdW50IHRvIHJldHVybiBpbmZvcm1hdGlvbiBmb3JcbiAgICogQHBhcmFtIHtEYXRlfSBzdGFydFRpbWUgc3RhcnQgb2YgdGltZSByYW5nZSwgaW5jbHVzaXZlXG4gICAqIEBwYXJhbSB7RGF0ZX0gZW5kVGltZSBlbmQgb2YgdGltZSByYW5nZSwgZXhjbHVzaXZlXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBvZmZzZXQgcGFnaW5hdGlvbiBvZmZzZXQsIGRlZmF1bHQgaXMgMFxuICAgKiBAcGFyYW0ge051bWJlcn0gbGltaXQgcGFnaW5hdGlvbiBsaW1pdCwgZGVmYXVsdCBpcyAxMDAwXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPE1ldGF0cmFkZXJIaXN0b3J5T3JkZXJzPn0gcHJvbWlzZSByZXNvbHZpbmcgd2l0aCByZXF1ZXN0IHJlc3VsdHMgY29udGFpbmluZyBoaXN0b3J5IG9yZGVycyBmb3VuZFxuICAgKi9cbiAgYXN5bmMgZ2V0SGlzdG9yeU9yZGVyc0J5VGltZVJhbmdlKGFjY291bnRJZCwgc3RhcnRUaW1lLCBlbmRUaW1lLCBvZmZzZXQgPSAwLCBsaW1pdCA9IDEwMDApIHtcbiAgICBsZXQgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLnJwY1JlcXVlc3QoYWNjb3VudElkLCB7YXBwbGljYXRpb246ICdSUEMnLCB0eXBlOiAnZ2V0SGlzdG9yeU9yZGVyc0J5VGltZVJhbmdlJyxcbiAgICAgIHN0YXJ0VGltZSwgZW5kVGltZSwgb2Zmc2V0LCBsaW1pdH0pO1xuICAgIHJldHVybiB7XG4gICAgICBoaXN0b3J5T3JkZXJzOiByZXNwb25zZS5oaXN0b3J5T3JkZXJzLFxuICAgICAgc3luY2hyb25pemluZzogcmVzcG9uc2Uuc3luY2hyb25pemluZ1xuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogTWV0YVRyYWRlciBoaXN0b3J5IGRlYWxzIHNlYXJjaCBxdWVyeSByZXNwb25zZVxuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBNZXRhdHJhZGVyRGVhbHNcbiAgICogQHByb3BlcnR5IHtBcnJheTxNZXRhdHJhZGVyRGVhbD59IGRlYWxzIGFycmF5IG9mIGhpc3RvcnkgZGVhbHMgcmV0dXJuZWRcbiAgICogQHByb3BlcnR5IHtCb29sZWFufSBzeW5jaHJvbml6aW5nIGZsYWcgaW5kaWNhdGluZyB0aGF0IGRlYWwgaW5pdGlhbCBzeW5jaHJvbml6YXRpb24gaXMgc3RpbGwgaW4gcHJvZ3Jlc3NcbiAgICogYW5kIHRodXMgc2VhcmNoIHJlc3VsdHMgbWF5IGJlIGluY29tcGxldGVcbiAgICovXG5cbiAgLyoqXG4gICAqIE1ldGFUcmFkZXIgZGVhbFxuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBNZXRhdHJhZGVyRGVhbFxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gaWQgZGVhbCBpZCAodGlja2V0IG51bWJlcilcbiAgICogQHByb3BlcnR5IHtTdHJpbmd9IHR5cGUgZGVhbCB0eXBlIChvbmUgb2YgREVBTF9UWVBFX0JVWSwgREVBTF9UWVBFX1NFTEwsIERFQUxfVFlQRV9CQUxBTkNFLCBERUFMX1RZUEVfQ1JFRElULFxuICAgKiBERUFMX1RZUEVfQ0hBUkdFLCBERUFMX1RZUEVfQ09SUkVDVElPTiwgREVBTF9UWVBFX0JPTlVTLCBERUFMX1RZUEVfQ09NTUlTU0lPTiwgREVBTF9UWVBFX0NPTU1JU1NJT05fREFJTFksXG4gICAqIERFQUxfVFlQRV9DT01NSVNTSU9OX01PTlRITFksIERFQUxfVFlQRV9DT01NSVNTSU9OX0FHRU5UX0RBSUxZLCBERUFMX1RZUEVfQ09NTUlTU0lPTl9BR0VOVF9NT05USExZLFxuICAgKiBERUFMX1RZUEVfSU5URVJFU1QsIERFQUxfVFlQRV9CVVlfQ0FOQ0VMRUQsIERFQUxfVFlQRV9TRUxMX0NBTkNFTEVELCBERUFMX0RJVklERU5ELCBERUFMX0RJVklERU5EX0ZSQU5LRUQsXG4gICAqIERFQUxfVEFYKS4gU2VlIGh0dHBzOi8vd3d3Lm1xbDUuY29tL2VuL2RvY3MvY29uc3RhbnRzL3RyYWRpbmdjb25zdGFudHMvZGVhbHByb3BlcnRpZXMjZW51bV9kZWFsX3R5cGVcbiAgICogQHByb3BlcnR5IHtTdHJpbmd9IGVudHJ5VHlwZSBkZWFsIGVudHJ5IHR5cGUgKG9uZSBvZiBERUFMX0VOVFJZX0lOLCBERUFMX0VOVFJZX09VVCwgREVBTF9FTlRSWV9JTk9VVCxcbiAgICogREVBTF9FTlRSWV9PVVRfQlkpLiBTZWUgaHR0cHM6Ly93d3cubXFsNS5jb20vZW4vZG9jcy9jb25zdGFudHMvdHJhZGluZ2NvbnN0YW50cy9kZWFscHJvcGVydGllcyNlbnVtX2RlYWxfZW50cnlcbiAgICogQHByb3BlcnR5IHtTdHJpbmd9IFtzeW1ib2xdIHN5bWJvbCBkZWFsIHJlbGF0ZXMgdG9cbiAgICogQHByb3BlcnR5IHtOdW1iZXJ9IFttYWdpY10gZGVhbCBtYWdpYyBudW1iZXIsIGlkZW50aWZpZXMgdGhlIEVBIHdoaWNoIGluaXRpYXRlZCB0aGUgZGVhbFxuICAgKiBAcHJvcGVydHkge0RhdGV9IHRpbWUgdGltZSB0aGUgZGVhbCB3YXMgY29uZHVjdGVkIGF0XG4gICAqIEBwcm9wZXJ0eSB7U3RyaW5nfSBicm9rZXJUaW1lIHRpbWUgdGltZSB0aGUgZGVhbCB3YXMgY29uZHVjdGVkIGF0LCBpbiBicm9rZXIgdGltZXpvbmUsIFlZWVktTU0tREQgSEg6bW06c3MuU1NTIGZvcm1hdFxuICAgKiBAcHJvcGVydHkge051bWJlcn0gW3ZvbHVtZV0gZGVhbCB2b2x1bWVcbiAgICogQHByb3BlcnR5IHtOdW1iZXJ9IFtwcmljZV0gdGhlIHByaWNlIHRoZSBkZWFsIHdhcyBjb25kdWN0ZWQgYXRcbiAgICogQHByb3BlcnR5IHtOdW1iZXJ9IFtjb21taXNzaW9uXSBkZWFsIGNvbW1pc3Npb25cbiAgICogQHByb3BlcnR5IHtOdW1iZXJ9IFtzd2FwXSBkZWFsIHN3YXBcbiAgICogQHByb3BlcnR5IHtOdW1iZXJ9IHByb2ZpdCBkZWFsIHByb2ZpdFxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gW3Bvc2l0aW9uSWRdIGlkIG9mIHBvc2l0aW9uIHRoZSBkZWFsIHJlbGF0ZXMgdG9cbiAgICogQHByb3BlcnR5IHtTdHJpbmd9IFtvcmRlcklkXSBpZCBvZiBvcmRlciB0aGUgZGVhbCByZWxhdGVzIHRvXG4gICAqIEBwcm9wZXJ0eSB7U3RyaW5nfSBbY29tbWVudF0gZGVhbCBjb21tZW50LiBUaGUgc3VtIG9mIHRoZSBsaW5lIGxlbmd0aHMgb2YgdGhlIGNvbW1lbnQgYW5kIHRoZSBjbGllbnRJZFxuICAgKiBtdXN0IGJlIGxlc3MgdGhhbiBvciBlcXVhbCB0byAyNi4gRm9yIG1vcmUgaW5mb3JtYXRpb24gc2VlIGh0dHBzOi8vbWV0YWFwaS5jbG91ZC9kb2NzL2NsaWVudC9jbGllbnRJZFVzYWdlL1xuICAgKiBAcHJvcGVydHkge1N0cmluZ30gW2Jyb2tlckNvbW1lbnRdIGN1cnJlbnQgY29tbWVudCB2YWx1ZSBvbiBicm9rZXIgc2lkZSAocG9zc2libHkgb3ZlcnJpZGVuIGJ5IHRoZSBicm9rZXIpXG4gICAqIEBwcm9wZXJ0eSB7U3RyaW5nfSBbY2xpZW50SWRdIGNsaWVudC1hc3NpZ25lZCBpZC4gVGhlIGlkIHZhbHVlIGNhbiBiZSBhc3NpZ25lZCB3aGVuIHN1Ym1pdHRpbmcgYSB0cmFkZSBhbmRcbiAgICogd2lsbCBiZSBwcmVzZW50IG9uIHBvc2l0aW9uLCBoaXN0b3J5IG9yZGVycyBhbmQgaGlzdG9yeSBkZWFscyByZWxhdGVkIHRvIHRoZSB0cmFkZS4gWW91IGNhbiB1c2UgdGhpcyBmaWVsZCB0byBiaW5kXG4gICAqIHlvdXIgdHJhZGVzIHRvIG9iamVjdHMgaW4geW91ciBhcHBsaWNhdGlvbiBhbmQgdGhlbiB0cmFjayB0cmFkZSBwcm9ncmVzcy4gVGhlIHN1bSBvZiB0aGUgbGluZSBsZW5ndGhzIG9mIHRoZVxuICAgKiBjb21tZW50IGFuZCB0aGUgY2xpZW50SWQgbXVzdCBiZSBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gMjYuIEZvciBtb3JlIGluZm9ybWF0aW9uIHNlZVxuICAgKiBodHRwczovL21ldGFhcGkuY2xvdWQvZG9jcy9jbGllbnQvY2xpZW50SWRVc2FnZS9cbiAgICogQHByb3BlcnR5IHtTdHJpbmd9IHBsYXRmb3JtIHBsYXRmb3JtIGlkIChtdDQgb3IgbXQ1KVxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gW3JlYXNvbl0gb3B0aW9uYWwgZGVhbCBleGVjdXRpb24gcmVhc29uLiBPbmUgb2YgREVBTF9SRUFTT05fQ0xJRU5ULCBERUFMX1JFQVNPTl9NT0JJTEUsXG4gICAqIERFQUxfUkVBU09OX1dFQiwgREVBTF9SRUFTT05fRVhQRVJULCBERUFMX1JFQVNPTl9TTCwgREVBTF9SRUFTT05fVFAsIERFQUxfUkVBU09OX1NPLCBERUFMX1JFQVNPTl9ST0xMT1ZFUixcbiAgICogREVBTF9SRUFTT05fVk1BUkdJTiwgREVBTF9SRUFTT05fU1BMSVQsIERFQUxfUkVBU09OX1VOS05PV04uIFNlZVxuICAgKiBodHRwczovL3d3dy5tcWw1LmNvbS9lbi9kb2NzL2NvbnN0YW50cy90cmFkaW5nY29uc3RhbnRzL2RlYWxwcm9wZXJ0aWVzI2VudW1fZGVhbF9yZWFzb24uXG4gICAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBbYWNjb3VudEN1cnJlbmN5RXhjaGFuZ2VSYXRlXSBjdXJyZW50IGV4Y2hhbmdlIHJhdGUgb2YgYWNjb3VudCBjdXJyZW5jeSBpbnRvIGFjY291bnQgYmFzZVxuICAgKiBjdXJyZW5jeSAoVVNEIGlmIHlvdSBkaWQgbm90IG92ZXJyaWRlIGl0KVxuICAgKiBAcHJvcGVydHkge251bWJlcn0gW3N0b3BMb3NzXSBkZWFsIHN0b3AgbG9zcy4gRm9yIE1UNSBvcGVuaW5nIGRlYWwgdGhpcyBpcyB0aGUgU0wgb2YgdGhlIG9yZGVyIG9wZW5pbmcgdGhlIFxuICAgKiBwb3NpdGlvbi4gRm9yIE1UNCBkZWFscyBvciBNVDUgY2xvc2luZyBkZWFsIHRoaXMgaXMgdGhlIGxhc3Qga25vd24gcG9zaXRpb24gU0wuXG4gICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBbdGFrZVByb2ZpdF0gZGVhbCB0YWtlIHByb2ZpdC4gRm9yIE1UNSBvcGVuaW5nIGRlYWwgdGhpcyBpcyB0aGUgVFAgb2YgdGhlIG9yZGVyIG9wZW5pbmcgdGhlIFxuICAgKiBwb3NpdGlvbi4gRm9yIE1UNCBkZWFscyBvciBNVDUgY2xvc2luZyBkZWFsIHRoaXMgaXMgdGhlIGxhc3Qga25vd24gcG9zaXRpb24gVFAuXG4gICAqL1xuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGhpc3RvcnkgZGVhbHMgd2l0aCBhIHNwZWNpZmljIHRpY2tldCBudW1iZXJcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBpZCBvZiB0aGUgTWV0YVRyYWRlciBhY2NvdW50IHRvIHJldHVybiBpbmZvcm1hdGlvbiBmb3JcbiAgICogQHBhcmFtIHtTdHJpbmd9IHRpY2tldCB0aWNrZXQgbnVtYmVyIChkZWFsIGlkIGZvciBNVDUgb3Igb3JkZXIgaWQgZm9yIE1UNClcbiAgICogQHJldHVybnMge1Byb21pc2U8TWV0YXRyYWRlckRlYWxzPn0gcHJvbWlzZSByZXNvbHZpbmcgd2l0aCByZXF1ZXN0IHJlc3VsdHMgY29udGFpbmluZyBkZWFscyBmb3VuZFxuICAgKi9cbiAgYXN5bmMgZ2V0RGVhbHNCeVRpY2tldChhY2NvdW50SWQsIHRpY2tldCkge1xuICAgIGxldCByZXNwb25zZSA9IGF3YWl0IHRoaXMucnBjUmVxdWVzdChhY2NvdW50SWQsIHthcHBsaWNhdGlvbjogJ1JQQycsIHR5cGU6ICdnZXREZWFsc0J5VGlja2V0JywgdGlja2V0fSk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGRlYWxzOiByZXNwb25zZS5kZWFscyxcbiAgICAgIHN5bmNocm9uaXppbmc6IHJlc3BvbnNlLnN5bmNocm9uaXppbmdcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgaGlzdG9yeSBkZWFscyBmb3IgYSBzcGVjaWZpYyBwb3NpdGlvbiBpZFxuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGlkIG9mIHRoZSBNZXRhVHJhZGVyIGFjY291bnQgdG8gcmV0dXJuIGluZm9ybWF0aW9uIGZvclxuICAgKiBAcGFyYW0ge1N0cmluZ30gcG9zaXRpb25JZCBwb3NpdGlvbiBpZFxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxNZXRhdHJhZGVyRGVhbHM+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIHJlcXVlc3QgcmVzdWx0cyBjb250YWluaW5nIGRlYWxzIGZvdW5kXG4gICAqL1xuICBhc3luYyBnZXREZWFsc0J5UG9zaXRpb24oYWNjb3VudElkLCBwb3NpdGlvbklkKSB7XG4gICAgbGV0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5ycGNSZXF1ZXN0KGFjY291bnRJZCwge2FwcGxpY2F0aW9uOiAnUlBDJywgdHlwZTogJ2dldERlYWxzQnlQb3NpdGlvbicsIHBvc2l0aW9uSWR9KTtcbiAgICByZXR1cm4ge1xuICAgICAgZGVhbHM6IHJlc3BvbnNlLmRlYWxzLFxuICAgICAgc3luY2hyb25pemluZzogcmVzcG9uc2Uuc3luY2hyb25pemluZ1xuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBoaXN0b3J5IGRlYWxzIHdpdGggZm9yIGEgc3BlY2lmaWMgdGltZSByYW5nZVxuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGlkIG9mIHRoZSBNZXRhVHJhZGVyIGFjY291bnQgdG8gcmV0dXJuIGluZm9ybWF0aW9uIGZvclxuICAgKiBAcGFyYW0ge0RhdGV9IHN0YXJ0VGltZSBzdGFydCBvZiB0aW1lIHJhbmdlLCBpbmNsdXNpdmVcbiAgICogQHBhcmFtIHtEYXRlfSBlbmRUaW1lIGVuZCBvZiB0aW1lIHJhbmdlLCBleGNsdXNpdmVcbiAgICogQHBhcmFtIHtOdW1iZXJ9IG9mZnNldCBwYWdpbmF0aW9uIG9mZnNldCwgZGVmYXVsdCBpcyAwXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBsaW1pdCBwYWdpbmF0aW9uIGxpbWl0LCBkZWZhdWx0IGlzIDEwMDBcbiAgICogQHJldHVybnMge1Byb21pc2U8TWV0YXRyYWRlckRlYWxzPn0gcHJvbWlzZSByZXNvbHZpbmcgd2l0aCByZXF1ZXN0IHJlc3VsdHMgY29udGFpbmluZyBkZWFscyBmb3VuZFxuICAgKi9cbiAgYXN5bmMgZ2V0RGVhbHNCeVRpbWVSYW5nZShhY2NvdW50SWQsIHN0YXJ0VGltZSwgZW5kVGltZSwgb2Zmc2V0ID0gMCwgbGltaXQgPSAxMDAwKSB7XG4gICAgbGV0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5ycGNSZXF1ZXN0KGFjY291bnRJZCwge2FwcGxpY2F0aW9uOiAnUlBDJywgdHlwZTogJ2dldERlYWxzQnlUaW1lUmFuZ2UnLCBzdGFydFRpbWUsXG4gICAgICBlbmRUaW1lLCBvZmZzZXQsIGxpbWl0fSk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGRlYWxzOiByZXNwb25zZS5kZWFscyxcbiAgICAgIHN5bmNocm9uaXppbmc6IHJlc3BvbnNlLnN5bmNocm9uaXppbmdcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIENsZWFycyB0aGUgb3JkZXIgYW5kIHRyYW5zYWN0aW9uIGhpc3Rvcnkgb2YgYSBzcGVjaWZpZWQgYXBwbGljYXRpb24gYW5kIHJlbW92ZXMgdGhlIGFwcGxpY2F0aW9uXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgaWQgb2YgdGhlIE1ldGFUcmFkZXIgYWNjb3VudCB0byByZW1vdmUgaGlzdG9yeSBhbmQgYXBwbGljYXRpb24gZm9yXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2UgcmVzb2x2aW5nIHdoZW4gdGhlIGhpc3RvcnkgaXMgY2xlYXJlZFxuICAgKi9cbiAgcmVtb3ZlQXBwbGljYXRpb24oYWNjb3VudElkKSB7XG4gICAgcmV0dXJuIHRoaXMucnBjUmVxdWVzdChhY2NvdW50SWQsIHt0eXBlOiAncmVtb3ZlQXBwbGljYXRpb24nfSk7XG4gIH1cblxuICAvKipcbiAgICogTWV0YVRyYWRlciB0cmFkZSByZXNwb25zZVxuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBNZXRhdHJhZGVyVHJhZGVSZXNwb25zZVxuICAgKiBAcHJvcGVydHkge051bWJlcn0gbnVtZXJpY0NvZGUgbnVtZXJpYyByZXNwb25zZSBjb2RlLCBzZWVcbiAgICogaHR0cHM6Ly93d3cubXFsNS5jb20vZW4vZG9jcy9jb25zdGFudHMvZXJyb3Jzd2FybmluZ3MvZW51bV90cmFkZV9yZXR1cm5fY29kZXMgYW5kXG4gICAqIGh0dHBzOi8vYm9vay5tcWw0LmNvbS9hcHBlbmRpeC9lcnJvcnMuIFJlc3BvbnNlIGNvZGVzIHdoaWNoIGluZGljYXRlIHN1Y2Nlc3MgYXJlIDAsIDEwMDA4LTEwMDEwLCAxMDAyNS4gVGhlIHJlc3RcbiAgICogY29kZXMgYXJlIGVycm9yc1xuICAgKiBAcHJvcGVydHkge1N0cmluZ30gc3RyaW5nQ29kZSBzdHJpbmcgcmVzcG9uc2UgY29kZSwgc2VlXG4gICAqIGh0dHBzOi8vd3d3Lm1xbDUuY29tL2VuL2RvY3MvY29uc3RhbnRzL2Vycm9yc3dhcm5pbmdzL2VudW1fdHJhZGVfcmV0dXJuX2NvZGVzIGFuZFxuICAgKiBodHRwczovL2Jvb2subXFsNC5jb20vYXBwZW5kaXgvZXJyb3JzLiBSZXNwb25zZSBjb2RlcyB3aGljaCBpbmRpY2F0ZSBzdWNjZXNzIGFyZSBFUlJfTk9fRVJST1IsXG4gICAqIFRSQURFX1JFVENPREVfUExBQ0VELCBUUkFERV9SRVRDT0RFX0RPTkUsIFRSQURFX1JFVENPREVfRE9ORV9QQVJUSUFMLCBUUkFERV9SRVRDT0RFX05PX0NIQU5HRVMuIFRoZSByZXN0IGNvZGVzIGFyZVxuICAgKiBlcnJvcnMuXG4gICAqIEBwcm9wZXJ0eSB7U3RyaW5nfSBtZXNzYWdlIGh1bWFuLXJlYWRhYmxlIHJlc3BvbnNlIG1lc3NhZ2VcbiAgICogQHByb3BlcnR5IHtTdHJpbmd9IG9yZGVySWQgb3JkZXIgaWQgd2hpY2ggd2FzIGNyZWF0ZWQvbW9kaWZpZWQgZHVyaW5nIHRoZSB0cmFkZVxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gcG9zaXRpb25JZCBwb3NpdGlvbiBpZCB3aGljaCB3YXMgbW9kaWZpZWQgZHVyaW5nIHRoZSB0cmFkZVxuICAgKi9cblxuICAvKipcbiAgICogRXhlY3V0ZSBhIHRyYWRlIG9uIGEgY29ubmVjdGVkIE1ldGFUcmFkZXIgYWNjb3VudFxuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGlkIG9mIHRoZSBNZXRhVHJhZGVyIGFjY291bnQgdG8gZXhlY3V0ZSB0cmFkZSBmb3JcbiAgICogQHBhcmFtIHtNZXRhdHJhZGVyVHJhZGV9IHRyYWRlIHRyYWRlIHRvIGV4ZWN1dGUgKHNlZSBkb2NzIGZvciBwb3NzaWJsZSB0cmFkZSB0eXBlcylcbiAgICogQHBhcmFtIHtTdHJpbmd9IFthcHBsaWNhdGlvbl0gYXBwbGljYXRpb24gdG8gdXNlXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBbcmVsaWFiaWxpdHldIGFjY291bnQgcmVsaWFiaWxpdHlcbiAgICogQHJldHVybnMge1Byb21pc2U8TWV0YXRyYWRlclRyYWRlUmVzcG9uc2U+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIHRyYWRlIHJlc3VsdFxuICAgKiBAdGhyb3dzIHtUcmFkZUVycm9yfSBvbiB0cmFkZSBlcnJvciwgY2hlY2sgZXJyb3IgcHJvcGVydGllcyBmb3IgZXJyb3IgY29kZSBkZXRhaWxzXG4gICAqL1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eVxuICBhc3luYyB0cmFkZShhY2NvdW50SWQsIHRyYWRlLCBhcHBsaWNhdGlvbiwgcmVsaWFiaWxpdHkpIHtcbiAgICBsZXQgcmVzcG9uc2U7XG4gICAgaWYgKGFwcGxpY2F0aW9uID09PSAnUlBDJykge1xuICAgICAgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLnJwY1JlcXVlc3QoYWNjb3VudElkLCB7dHlwZTogJ3RyYWRlJywgdHJhZGUsIGFwcGxpY2F0aW9ufSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJlc3BvbnNlID0gYXdhaXQgdGhpcy5ycGNSZXF1ZXN0QWxsSW5zdGFuY2VzKGFjY291bnRJZCwge3R5cGU6ICd0cmFkZScsIHRyYWRlLFxuICAgICAgICBhcHBsaWNhdGlvbjogYXBwbGljYXRpb24gfHwgdGhpcy5fYXBwbGljYXRpb24sIHJlcXVlc3RJZDogcmFuZG9tc3RyaW5nLmdlbmVyYXRlKDMyKX0sIHJlbGlhYmlsaXR5KTtcbiAgICB9XG4gICAgcmVzcG9uc2UucmVzcG9uc2UgPSByZXNwb25zZS5yZXNwb25zZSB8fCB7fTtcbiAgICByZXNwb25zZS5yZXNwb25zZS5zdHJpbmdDb2RlID0gcmVzcG9uc2UucmVzcG9uc2Uuc3RyaW5nQ29kZSB8fCByZXNwb25zZS5yZXNwb25zZS5kZXNjcmlwdGlvbjtcbiAgICByZXNwb25zZS5yZXNwb25zZS5udW1lcmljQ29kZSA9IHJlc3BvbnNlLnJlc3BvbnNlLm51bWVyaWNDb2RlICE9PSB1bmRlZmluZWQgPyByZXNwb25zZS5yZXNwb25zZS5udW1lcmljQ29kZSA6XG4gICAgICByZXNwb25zZS5yZXNwb25zZS5lcnJvcjtcbiAgICBpZiAoWydFUlJfTk9fRVJST1InLCAnVFJBREVfUkVUQ09ERV9QTEFDRUQnLCAnVFJBREVfUkVUQ09ERV9ET05FJywgJ1RSQURFX1JFVENPREVfRE9ORV9QQVJUSUFMJyxcbiAgICAgICdUUkFERV9SRVRDT0RFX05PX0NIQU5HRVMnXS5pbmNsdWRlcyhyZXNwb25zZS5yZXNwb25zZS5zdHJpbmdDb2RlIHx8IHJlc3BvbnNlLnJlc3BvbnNlLmRlc2NyaXB0aW9uKSkge1xuICAgICAgcmV0dXJuIHJlc3BvbnNlLnJlc3BvbnNlO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgVHJhZGVFcnJvcihyZXNwb25zZS5yZXNwb25zZS5tZXNzYWdlLCByZXNwb25zZS5yZXNwb25zZS5udW1lcmljQ29kZSwgcmVzcG9uc2UucmVzcG9uc2Uuc3RyaW5nQ29kZSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSB0YXNrIHRoYXQgZW5zdXJlcyB0aGUgYWNjb3VudCBnZXRzIHN1YnNjcmliZWQgdG8gdGhlIHNlcnZlclxuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGFjY291bnQgaWQgdG8gc3Vic2NyaWJlXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBpbnN0YW5jZU51bWJlciBpbnN0YW5jZSBpbmRleCBudW1iZXJcbiAgICovXG4gIGVuc3VyZVN1YnNjcmliZShhY2NvdW50SWQsIGluc3RhbmNlTnVtYmVyKSB7XG4gICAgdGhpcy5fc3Vic2NyaXB0aW9uTWFuYWdlci5zY2hlZHVsZVN1YnNjcmliZShhY2NvdW50SWQsIGluc3RhbmNlTnVtYmVyKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdWJzY3JpYmVzIHRvIHRoZSBNZXRhdHJhZGVyIHRlcm1pbmFsIGV2ZW50c1xuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGlkIG9mIHRoZSBNZXRhVHJhZGVyIGFjY291bnQgdG8gc3Vic2NyaWJlIHRvXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBpbnN0YW5jZU51bWJlciBpbnN0YW5jZSBpbmRleCBudW1iZXJcbiAgICogQHJldHVybnMge1Byb21pc2V9IHByb21pc2Ugd2hpY2ggcmVzb2x2ZXMgd2hlbiBzdWJzY3JpcHRpb24gc3RhcnRlZFxuICAgKi9cbiAgc3Vic2NyaWJlKGFjY291bnRJZCwgaW5zdGFuY2VOdW1iZXIpIHtcbiAgICByZXR1cm4gdGhpcy5fc3Vic2NyaXB0aW9uTWFuYWdlci5zdWJzY3JpYmUoYWNjb3VudElkLCBpbnN0YW5jZU51bWJlcik7XG4gIH1cblxuICAvKipcbiAgICogUmVxdWVzdHMgdGhlIHRlcm1pbmFsIHRvIHN0YXJ0IHN5bmNocm9uaXphdGlvbiBwcm9jZXNzXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgaWQgb2YgdGhlIE1ldGFUcmFkZXIgYWNjb3VudCB0byBzeW5jaHJvbml6ZVxuICAgKiBAcGFyYW0ge051bWJlcn0gaW5zdGFuY2VJbmRleCBpbnN0YW5jZSBpbmRleFxuICAgKiBAcGFyYW0ge1N0cmluZ30gaG9zdCBuYW1lIG9mIGhvc3QgdG8gc3luY2hyb25pemUgd2l0aFxuICAgKiBAcGFyYW0ge1N0cmluZ30gc3luY2hyb25pemF0aW9uSWQgc3luY2hyb25pemF0aW9uIHJlcXVlc3QgaWRcbiAgICogQHBhcmFtIHtEYXRlfSBzdGFydGluZ0hpc3RvcnlPcmRlclRpbWUgZnJvbSB3aGF0IGRhdGUgdG8gc3RhcnQgc3luY2hyb25pemluZyBoaXN0b3J5IG9yZGVycyBmcm9tLiBJZiBub3Qgc3BlY2lmaWVkLFxuICAgKiB0aGUgZW50aXJlIG9yZGVyIGhpc3Rvcnkgd2lsbCBiZSBkb3dubG9hZGVkLlxuICAgKiBAcGFyYW0ge0RhdGV9IHN0YXJ0aW5nRGVhbFRpbWUgZnJvbSB3aGF0IGRhdGUgdG8gc3RhcnQgZGVhbCBzeW5jaHJvbml6YXRpb24gZnJvbS4gSWYgbm90IHNwZWNpZmllZCwgdGhlbiBhbGxcbiAgICogaGlzdG9yeSBkZWFscyB3aWxsIGJlIGRvd25sb2FkZWQuXG4gICAqIEBwYXJhbSB7RnVuY3Rpb259IGdldEhhc2hlcyBmdW5jdGlvbiB0byBnZXQgdGVybWluYWwgc3RhdGUgaGFzaGVzXG4gICAqIEByZXR1cm5zIHtQcm9taXNlfSBwcm9taXNlIHdoaWNoIHJlc29sdmVzIHdoZW4gc3luY2hyb25pemF0aW9uIHN0YXJ0ZWRcbiAgICovXG4gIGFzeW5jIHN5bmNocm9uaXplKFxuICAgIGFjY291bnRJZCwgaW5zdGFuY2VJbmRleCwgaG9zdCwgc3luY2hyb25pemF0aW9uSWQsIHN0YXJ0aW5nSGlzdG9yeU9yZGVyVGltZSwgc3RhcnRpbmdEZWFsVGltZSwgaGFzaGVzXG4gICkge1xuICAgIGlmICh0aGlzLl9nZXRTb2NrZXRJbnN0YW5jZUJ5QWNjb3VudChhY2NvdW50SWQsIGluc3RhbmNlSW5kZXgpID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRoaXMuX2xvZ2dlci5kZWJ1ZyhgJHthY2NvdW50SWR9OiR7aW5zdGFuY2VJbmRleH06IGNyZWF0aW5nIHNvY2tldCBpbnN0YW5jZSBvbiBzeW5jaHJvbml6ZWApO1xuICAgICAgYXdhaXQgdGhpcy5fY3JlYXRlU29ja2V0SW5zdGFuY2VCeUFjY291bnQoYWNjb3VudElkLCBpbnN0YW5jZUluZGV4KTtcbiAgICB9XG4gICAgY29uc3Qgc3luY1Rocm90dGxlciA9IHRoaXMuX2dldFNvY2tldEluc3RhbmNlQnlBY2NvdW50KGFjY291bnRJZCwgaW5zdGFuY2VJbmRleCkuc3luY2hyb25pemF0aW9uVGhyb3R0bGVyO1xuICAgIHRoaXMuX3N5bmNocm9uaXphdGlvbkhhc2hlc1tzeW5jaHJvbml6YXRpb25JZF0gPSBoYXNoZXM7XG4gICAgdGhpcy5fc3luY2hyb25pemF0aW9uSGFzaGVzW3N5bmNocm9uaXphdGlvbklkXS5sYXN0VXBkYXRlZCA9IERhdGUubm93KCk7XG4gICAgcmV0dXJuIHN5bmNUaHJvdHRsZXIuc2NoZWR1bGVTeW5jaHJvbml6ZShhY2NvdW50SWQsIHtyZXF1ZXN0SWQ6IHN5bmNocm9uaXphdGlvbklkLCB2ZXJzaW9uOiAyLFxuICAgICAgdHlwZTogJ3N5bmNocm9uaXplJywgc3RhcnRpbmdIaXN0b3J5T3JkZXJUaW1lLCBzdGFydGluZ0RlYWxUaW1lLCBpbnN0YW5jZUluZGV4LCBob3N0fSwgaGFzaGVzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXYWl0cyBmb3Igc2VydmVyLXNpZGUgdGVybWluYWwgc3RhdGUgc3luY2hyb25pemF0aW9uIHRvIGNvbXBsZXRlXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgaWQgb2YgdGhlIE1ldGFUcmFkZXIgYWNjb3VudCB0byBzeW5jaHJvbml6ZVxuICAgKiBAcGFyYW0ge051bWJlcn0gW2luc3RhbmNlTnVtYmVyXSBpbnN0YW5jZSBpbmRleCBudW1iZXJcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFwcGxpY2F0aW9uUGF0dGVybiBNZXRhQXBpIGFwcGxpY2F0aW9uIHJlZ3VsYXIgZXhwcmVzc2lvbiBwYXR0ZXJuLCBkZWZhdWx0IGlzIC4qXG4gICAqIEBwYXJhbSB7TnVtYmVyfSB0aW1lb3V0SW5TZWNvbmRzIHRpbWVvdXQgaW4gc2Vjb25kcywgZGVmYXVsdCBpcyAzMDAgc2Vjb25kc1xuICAgKiBAcGFyYW0ge1N0cmluZ30gW2FwcGxpY2F0aW9uXSBhcHBsaWNhdGlvbiB0byBzeW5jaHJvbml6ZSB3aXRoXG4gICAqIEByZXR1cm5zIHtQcm9taXNlfSBwcm9taXNlIHdoaWNoIHJlc29sdmVzIHdoZW4gc3luY2hyb25pemF0aW9uIHN0YXJ0ZWRcbiAgICovXG4gIHdhaXRTeW5jaHJvbml6ZWQoYWNjb3VudElkLCBpbnN0YW5jZU51bWJlciwgYXBwbGljYXRpb25QYXR0ZXJuLCB0aW1lb3V0SW5TZWNvbmRzLCBhcHBsaWNhdGlvbj8pIHtcbiAgICByZXR1cm4gdGhpcy5ycGNSZXF1ZXN0KGFjY291bnRJZCwge3R5cGU6ICd3YWl0U3luY2hyb25pemVkJywgYXBwbGljYXRpb25QYXR0ZXJuLCB0aW1lb3V0SW5TZWNvbmRzLFxuICAgICAgaW5zdGFuY2VJbmRleDogaW5zdGFuY2VOdW1iZXIsIGFwcGxpY2F0aW9uOiBhcHBsaWNhdGlvbiB8fCB0aGlzLl9hcHBsaWNhdGlvbn0sXG4gICAgdGltZW91dEluU2Vjb25kcyArIDEpO1xuICB9XG5cbiAgLyoqXG4gICAqIE1hcmtldCBkYXRhIHN1YnNjcmlwdGlvblxuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBNYXJrZXREYXRhU3Vic2NyaXB0aW9uXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSB0eXBlIHN1YnNjcmlwdGlvbiB0eXBlLCBvbmUgb2YgcXVvdGVzLCBjYW5kbGVzLCB0aWNrcywgb3IgbWFya2V0RGVwdGhcbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IFt0aW1lZnJhbWVdIHdoZW4gc3Vic2NyaXB0aW9uIHR5cGUgaXMgY2FuZGxlcywgZGVmaW5lcyB0aGUgdGltZWZyYW1lIGFjY29yZGluZyB0byB3aGljaCB0aGVcbiAgICogY2FuZGxlcyBtdXN0IGJlIGdlbmVyYXRlZC4gQWxsb3dlZCB2YWx1ZXMgZm9yIE1UNSBhcmUgMW0sIDJtLCAzbSwgNG0sIDVtLCA2bSwgMTBtLCAxMm0sIDE1bSwgMjBtLCAzMG0sIDFoLCAyaCwgM2gsXG4gICAqIDRoLCA2aCwgOGgsIDEyaCwgMWQsIDF3LCAxbW4uIEFsbG93ZWQgdmFsdWVzIGZvciBNVDQgYXJlIDFtLCA1bSwgMTVtIDMwbSwgMWgsIDRoLCAxZCwgMXcsIDFtblxuICAgKiBAcHJvcGVydHkge251bWJlcn0gW2ludGVydmFsSW5NaWxsaXNlY29uZHNdIGRlZmluZXMgaG93IGZyZXF1ZW50bHkgdGhlIHRlcm1pbmFsIHdpbGwgc3RyZWFtIGRhdGEgdG8gY2xpZW50LiBJZiBub3RcbiAgICogc2V0LCB0aGVuIHRoZSB2YWx1ZSBjb25maWd1cmVkIGluIGFjY291bnQgd2lsbCBiZSB1c2VkXG4gICAqL1xuXG4gIC8qKlxuICAgKiBTdWJzY3JpYmVzIG9uIG1hcmtldCBkYXRhIG9mIHNwZWNpZmllZCBzeW1ib2xcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBpZCBvZiB0aGUgTWV0YVRyYWRlciBhY2NvdW50XG4gICAqIEBwYXJhbSB7U3RyaW5nfSBzeW1ib2wgc3ltYm9sIChlLmcuIGN1cnJlbmN5IHBhaXIgb3IgYW4gaW5kZXgpXG4gICAqIEBwYXJhbSB7QXJyYXk8TWFya2V0RGF0YVN1YnNjcmlwdGlvbj59IHN1YnNjcmlwdGlvbnMgYXJyYXkgb2YgbWFya2V0IGRhdGEgc3Vic2NyaXB0aW9uIHRvIGNyZWF0ZSBvciB1cGRhdGVcbiAgICogQHBhcmFtIHtTdHJpbmd9IFtyZWxpYWJpbGl0eV0gYWNjb3VudCByZWxpYWJpbGl0eVxuICAgKiBAcmV0dXJucyB7UHJvbWlzZX0gcHJvbWlzZSB3aGljaCByZXNvbHZlcyB3aGVuIHN1YnNjcmlwdGlvbiByZXF1ZXN0IHdhcyBwcm9jZXNzZWRcbiAgICovXG4gIHN1YnNjcmliZVRvTWFya2V0RGF0YShhY2NvdW50SWQsIHN5bWJvbCwgc3Vic2NyaXB0aW9ucywgcmVsaWFiaWxpdHkpIHtcbiAgICByZXR1cm4gdGhpcy5ycGNSZXF1ZXN0QWxsSW5zdGFuY2VzKGFjY291bnRJZCxcbiAgICAgIHt0eXBlOiAnc3Vic2NyaWJlVG9NYXJrZXREYXRhJywgc3ltYm9sLCBzdWJzY3JpcHRpb25zfSwgcmVsaWFiaWxpdHkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlZnJlc2hlcyBtYXJrZXQgZGF0YSBzdWJzY3JpcHRpb25zIG9uIHRoZSBzZXJ2ZXIgdG8gcHJldmVudCB0aGVtIGZyb20gZXhwaXJpbmdcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBpZCBvZiB0aGUgTWV0YVRyYWRlciBhY2NvdW50XG4gICAqIEBwYXJhbSB7TnVtYmVyfSBpbnN0YW5jZU51bWJlciBpbnN0YW5jZSBpbmRleCBudW1iZXJcbiAgICogQHBhcmFtIHtBcnJheX0gc3Vic2NyaXB0aW9ucyBhcnJheSBvZiBzdWJzY3JpcHRpb25zIHRvIHJlZnJlc2hcbiAgICovXG4gIHJlZnJlc2hNYXJrZXREYXRhU3Vic2NyaXB0aW9ucyhhY2NvdW50SWQsIGluc3RhbmNlTnVtYmVyLCBzdWJzY3JpcHRpb25zKSB7XG4gICAgcmV0dXJuIHRoaXMucnBjUmVxdWVzdChhY2NvdW50SWQsIHt0eXBlOiAncmVmcmVzaE1hcmtldERhdGFTdWJzY3JpcHRpb25zJywgc3Vic2NyaXB0aW9ucyxcbiAgICAgIGluc3RhbmNlSW5kZXg6IGluc3RhbmNlTnVtYmVyfSk7XG4gIH1cblxuICAvKipcbiAgICogTWFya2V0IGRhdGEgdW5zdWJzY3JpcHRpb25cbiAgICogQHR5cGVkZWYge09iamVjdH0gTWFya2V0RGF0YVVuc3Vic2NyaXB0aW9uXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSB0eXBlIHN1YnNjcmlwdGlvbiB0eXBlLCBvbmUgb2YgcXVvdGVzLCBjYW5kbGVzLCB0aWNrcywgb3IgbWFya2V0RGVwdGhcbiAgICovXG5cbiAgLyoqXG4gICAqIFVuc3Vic2NyaWJlcyBmcm9tIG1hcmtldCBkYXRhIG9mIHNwZWNpZmllZCBzeW1ib2xcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBpZCBvZiB0aGUgTWV0YVRyYWRlciBhY2NvdW50XG4gICAqIEBwYXJhbSB7U3RyaW5nfSBzeW1ib2wgc3ltYm9sIChlLmcuIGN1cnJlbmN5IHBhaXIgb3IgYW4gaW5kZXgpXG4gICAqIEBwYXJhbSB7QXJyYXk8TWFya2V0RGF0YVVuc3Vic2NyaXB0aW9uPn0gc3Vic2NyaXB0aW9ucyBhcnJheSBvZiBzdWJzY3JpcHRpb25zIHRvIGNhbmNlbFxuICAgKiBAcGFyYW0ge1N0cmluZ30gW3JlbGlhYmlsaXR5XSBhY2NvdW50IHJlbGlhYmlsaXR5XG4gICAqIEByZXR1cm5zIHtQcm9taXNlfSBwcm9taXNlIHdoaWNoIHJlc29sdmVzIHdoZW4gdW5zdWJzY3JpcHRpb24gcmVxdWVzdCB3YXMgcHJvY2Vzc2VkXG4gICAqL1xuICB1bnN1YnNjcmliZUZyb21NYXJrZXREYXRhKGFjY291bnRJZCwgc3ltYm9sLCBzdWJzY3JpcHRpb25zLCByZWxpYWJpbGl0eSkge1xuICAgIHJldHVybiB0aGlzLnJwY1JlcXVlc3RBbGxJbnN0YW5jZXMoYWNjb3VudElkLCB7dHlwZTogJ3Vuc3Vic2NyaWJlRnJvbU1hcmtldERhdGEnLCBzeW1ib2wsIHN1YnNjcmlwdGlvbnN9LFxuICAgICAgcmVsaWFiaWxpdHkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHJpZXZlcyBzeW1ib2xzIGF2YWlsYWJsZSBvbiBhbiBhY2NvdW50XG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgaWQgb2YgdGhlIE1ldGFUcmFkZXIgYWNjb3VudCB0byByZXRyaWV2ZSBzeW1ib2xzIGZvclxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxBcnJheTxzdHJpbmc+Pn0gcHJvbWlzZSB3aGljaCByZXNvbHZlcyB3aGVuIHN5bWJvbHMgYXJlIHJldHJpZXZlZFxuICAgKi9cbiAgYXN5bmMgZ2V0U3ltYm9scyhhY2NvdW50SWQpIHtcbiAgICBsZXQgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLnJwY1JlcXVlc3QoYWNjb3VudElkLCB7YXBwbGljYXRpb246ICdSUEMnLCB0eXBlOiAnZ2V0U3ltYm9scyd9KTtcbiAgICByZXR1cm4gcmVzcG9uc2Uuc3ltYm9scztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXRyaWV2ZXMgc3BlY2lmaWNhdGlvbiBmb3IgYSBzeW1ib2xcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBpZCBvZiB0aGUgTWV0YVRyYWRlciBhY2NvdW50IHRvIHJldHJpZXZlIHN5bWJvbCBzcGVjaWZpY2F0aW9uIGZvclxuICAgKiBAcGFyYW0ge1N0cmluZ30gc3ltYm9sIHN5bWJvbCB0byByZXRyaWV2ZSBzcGVjaWZpY2F0aW9uIGZvclxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxNZXRhdHJhZGVyU3ltYm9sU3BlY2lmaWNhdGlvbj59IHByb21pc2Ugd2hpY2ggcmVzb2x2ZXMgd2hlbiBzcGVjaWZpY2F0aW9uIGlzIHJldHJpZXZlZFxuICAgKi9cbiAgYXN5bmMgZ2V0U3ltYm9sU3BlY2lmaWNhdGlvbihhY2NvdW50SWQsIHN5bWJvbCkge1xuICAgIGxldCByZXNwb25zZSA9IGF3YWl0IHRoaXMucnBjUmVxdWVzdChhY2NvdW50SWQsIHthcHBsaWNhdGlvbjogJ1JQQycsIHR5cGU6ICdnZXRTeW1ib2xTcGVjaWZpY2F0aW9uJywgc3ltYm9sfSk7XG4gICAgcmV0dXJuIHJlc3BvbnNlLnNwZWNpZmljYXRpb247XG4gIH1cblxuICAvKipcbiAgICogUmV0cmlldmVzIHByaWNlIGZvciBhIHN5bWJvbFxuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGlkIG9mIHRoZSBNZXRhVHJhZGVyIGFjY291bnQgdG8gcmV0cmlldmUgc3ltYm9sIHByaWNlIGZvclxuICAgKiBAcGFyYW0ge1N0cmluZ30gc3ltYm9sIHN5bWJvbCB0byByZXRyaWV2ZSBwcmljZSBmb3JcbiAgICogQHBhcmFtIHtib29sZWFufSBrZWVwU3Vic2NyaXB0aW9uIGlmIHNldCB0byB0cnVlLCB0aGUgYWNjb3VudCB3aWxsIGdldCBhIGxvbmctdGVybSBzdWJzY3JpcHRpb24gdG8gc3ltYm9sIG1hcmtldFxuICAgKiBkYXRhLiBMb25nLXRlcm0gc3Vic2NyaXB0aW9uIG1lYW5zIHRoYXQgb24gc3Vic2VxdWVudCBjYWxscyB5b3Ugd2lsbCBnZXQgdXBkYXRlZCB2YWx1ZSBmYXN0ZXIuIElmIHNldCB0byBmYWxzZSBvclxuICAgKiBub3Qgc2V0LCB0aGUgc3Vic2NyaXB0aW9uIHdpbGwgYmUgc2V0IHRvIGV4cGlyZSBpbiAxMiBtaW51dGVzLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxNZXRhdHJhZGVyU3ltYm9sUHJpY2U+fSBwcm9taXNlIHdoaWNoIHJlc29sdmVzIHdoZW4gcHJpY2UgaXMgcmV0cmlldmVkXG4gICAqL1xuICBhc3luYyBnZXRTeW1ib2xQcmljZShhY2NvdW50SWQsIHN5bWJvbCwga2VlcFN1YnNjcmlwdGlvbiA9IGZhbHNlKSB7XG4gICAgbGV0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5ycGNSZXF1ZXN0KGFjY291bnRJZCwge2FwcGxpY2F0aW9uOiAnUlBDJywgdHlwZTogJ2dldFN5bWJvbFByaWNlJywgc3ltYm9sLFxuICAgICAga2VlcFN1YnNjcmlwdGlvbn0pO1xuICAgIHJldHVybiByZXNwb25zZS5wcmljZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXRyaWV2ZXMgcHJpY2UgZm9yIGEgc3ltYm9sXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhY2NvdW50SWQgaWQgb2YgdGhlIE1ldGFUcmFkZXIgYWNjb3VudCB0byByZXRyaWV2ZSBjYW5kbGUgZm9yXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBzeW1ib2wgc3ltYm9sIHRvIHJldHJpZXZlIGNhbmRsZSBmb3JcbiAgICogQHBhcmFtIHtzdHJpbmd9IHRpbWVmcmFtZSBkZWZpbmVzIHRoZSB0aW1lZnJhbWUgYWNjb3JkaW5nIHRvIHdoaWNoIHRoZSBjYW5kbGUgbXVzdCBiZSBnZW5lcmF0ZWQuIEFsbG93ZWQgdmFsdWVzIGZvclxuICAgKiBNVDUgYXJlIDFtLCAybSwgM20sIDRtLCA1bSwgNm0sIDEwbSwgMTJtLCAxNW0sIDIwbSwgMzBtLCAxaCwgMmgsIDNoLCA0aCwgNmgsIDhoLCAxMmgsIDFkLCAxdywgMW1uLiBBbGxvd2VkIHZhbHVlc1xuICAgKiBmb3IgTVQ0IGFyZSAxbSwgNW0sIDE1bSAzMG0sIDFoLCA0aCwgMWQsIDF3LCAxbW5cbiAgICogQHBhcmFtIHtib29sZWFufSBrZWVwU3Vic2NyaXB0aW9uIGlmIHNldCB0byB0cnVlLCB0aGUgYWNjb3VudCB3aWxsIGdldCBhIGxvbmctdGVybSBzdWJzY3JpcHRpb24gdG8gc3ltYm9sIG1hcmtldFxuICAgKiBkYXRhLiBMb25nLXRlcm0gc3Vic2NyaXB0aW9uIG1lYW5zIHRoYXQgb24gc3Vic2VxdWVudCBjYWxscyB5b3Ugd2lsbCBnZXQgdXBkYXRlZCB2YWx1ZSBmYXN0ZXIuIElmIHNldCB0byBmYWxzZSBvclxuICAgKiBub3Qgc2V0LCB0aGUgc3Vic2NyaXB0aW9uIHdpbGwgYmUgc2V0IHRvIGV4cGlyZSBpbiAxMiBtaW51dGVzLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxNZXRhdHJhZGVyQ2FuZGxlPn0gcHJvbWlzZSB3aGljaCByZXNvbHZlcyB3aGVuIGNhbmRsZSBpcyByZXRyaWV2ZWRcbiAgICovXG4gIGFzeW5jIGdldENhbmRsZShhY2NvdW50SWQsIHN5bWJvbCwgdGltZWZyYW1lLCBrZWVwU3Vic2NyaXB0aW9uID0gZmFsc2UpIHtcbiAgICBsZXQgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLnJwY1JlcXVlc3QoYWNjb3VudElkLCB7YXBwbGljYXRpb246ICdSUEMnLCB0eXBlOiAnZ2V0Q2FuZGxlJywgc3ltYm9sLCB0aW1lZnJhbWUsXG4gICAgICBrZWVwU3Vic2NyaXB0aW9ufSk7XG4gICAgcmV0dXJuIHJlc3BvbnNlLmNhbmRsZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXRyaWV2ZXMgbGF0ZXN0IHRpY2sgZm9yIGEgc3ltYm9sLiBNVDQgRzEgYWNjb3VudHMgZG8gbm90IHN1cHBvcnQgdGhpcyBBUElcbiAgICogQHBhcmFtIHtzdHJpbmd9IGFjY291bnRJZCBpZCBvZiB0aGUgTWV0YVRyYWRlciBhY2NvdW50IHRvIHJldHJpZXZlIHN5bWJvbCB0aWNrIGZvclxuICAgKiBAcGFyYW0ge3N0cmluZ30gc3ltYm9sIHN5bWJvbCB0byByZXRyaWV2ZSB0aWNrIGZvclxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IGtlZXBTdWJzY3JpcHRpb24gaWYgc2V0IHRvIHRydWUsIHRoZSBhY2NvdW50IHdpbGwgZ2V0IGEgbG9uZy10ZXJtIHN1YnNjcmlwdGlvbiB0byBzeW1ib2wgbWFya2V0XG4gICAqIGRhdGEuIExvbmctdGVybSBzdWJzY3JpcHRpb24gbWVhbnMgdGhhdCBvbiBzdWJzZXF1ZW50IGNhbGxzIHlvdSB3aWxsIGdldCB1cGRhdGVkIHZhbHVlIGZhc3Rlci4gSWYgc2V0IHRvIGZhbHNlIG9yXG4gICAqIG5vdCBzZXQsIHRoZSBzdWJzY3JpcHRpb24gd2lsbCBiZSBzZXQgdG8gZXhwaXJlIGluIDEyIG1pbnV0ZXMuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPE1ldGF0cmFkZXJUaWNrPn0gcHJvbWlzZSB3aGljaCByZXNvbHZlcyB3aGVuIHRpY2sgaXMgcmV0cmlldmVkXG4gICAqL1xuICBhc3luYyBnZXRUaWNrKGFjY291bnRJZCwgc3ltYm9sLCBrZWVwU3Vic2NyaXB0aW9uID0gZmFsc2UpIHtcbiAgICBsZXQgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLnJwY1JlcXVlc3QoYWNjb3VudElkLCB7YXBwbGljYXRpb246ICdSUEMnLCB0eXBlOiAnZ2V0VGljaycsIHN5bWJvbCwga2VlcFN1YnNjcmlwdGlvbn0pO1xuICAgIHJldHVybiByZXNwb25zZS50aWNrO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHJpZXZlcyBsYXRlc3Qgb3JkZXIgYm9vayBmb3IgYSBzeW1ib2wuIE1UNCBhY2NvdW50cyBkbyBub3Qgc3VwcG9ydCB0aGlzIEFQSVxuICAgKiBAcGFyYW0ge3N0cmluZ30gYWNjb3VudElkIGlkIG9mIHRoZSBNZXRhVHJhZGVyIGFjY291bnQgdG8gcmV0cmlldmUgc3ltYm9sIG9yZGVyIGJvb2sgZm9yXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBzeW1ib2wgc3ltYm9sIHRvIHJldHJpZXZlIG9yZGVyIGJvb2sgZm9yXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0ga2VlcFN1YnNjcmlwdGlvbiBpZiBzZXQgdG8gdHJ1ZSwgdGhlIGFjY291bnQgd2lsbCBnZXQgYSBsb25nLXRlcm0gc3Vic2NyaXB0aW9uIHRvIHN5bWJvbCBtYXJrZXRcbiAgICogZGF0YS4gTG9uZy10ZXJtIHN1YnNjcmlwdGlvbiBtZWFucyB0aGF0IG9uIHN1YnNlcXVlbnQgY2FsbHMgeW91IHdpbGwgZ2V0IHVwZGF0ZWQgdmFsdWUgZmFzdGVyLiBJZiBzZXQgdG8gZmFsc2Ugb3JcbiAgICogbm90IHNldCwgdGhlIHN1YnNjcmlwdGlvbiB3aWxsIGJlIHNldCB0byBleHBpcmUgaW4gMTIgbWludXRlcy5cbiAgICogQHJldHVybnMge1Byb21pc2U8TWV0YXRyYWRlckJvb2s+fSBwcm9taXNlIHdoaWNoIHJlc29sdmVzIHdoZW4gb3JkZXIgYm9vayBpcyByZXRyaWV2ZWRcbiAgICovXG4gIGFzeW5jIGdldEJvb2soYWNjb3VudElkLCBzeW1ib2wsIGtlZXBTdWJzY3JpcHRpb24gPSBmYWxzZSkge1xuICAgIGxldCByZXNwb25zZSA9IGF3YWl0IHRoaXMucnBjUmVxdWVzdChhY2NvdW50SWQsIHthcHBsaWNhdGlvbjogJ1JQQycsIHR5cGU6ICdnZXRCb29rJywgc3ltYm9sLCBrZWVwU3Vic2NyaXB0aW9ufSk7XG4gICAgcmV0dXJuIHJlc3BvbnNlLmJvb2s7XG4gIH1cblxuICAvKipcbiAgICogRm9yY2VzIHJlZnJlc2ggb2YgbW9zdCByZWNlbnQgcXVvdGUgdXBkYXRlcyBmb3Igc3ltYm9scyBzdWJzY3JpYmVkIHRvIGJ5IHRoZSB0ZXJtaW5hbFxuICAgKiBAcGFyYW0ge3N0cmluZ30gYWNjb3VudElkIGlkIG9mIHRoZSBNZXRhVHJhZGVyIGFjY291bnRcbiAgICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nW10+fSBwcm9taXNlIHdoaWNoIHJlc29sdmVzIHdpdGggcmVjZW50IHF1b3RlIHN5bWJvbHMgdGhhdCB3YXMgaW5pdGlhdGVkIHRvIHByb2Nlc3NcbiAgICovXG4gIGFzeW5jIHJlZnJlc2hUZXJtaW5hbFN0YXRlKGFjY291bnRJZCkge1xuICAgIGxldCByZXNwb25zZSA9IGF3YWl0IHRoaXMucnBjUmVxdWVzdChhY2NvdW50SWQsIHthcHBsaWNhdGlvbjogJ1JQQycsIHR5cGU6ICdyZWZyZXNoVGVybWluYWxTdGF0ZSd9KTtcbiAgICByZXR1cm4gcmVzcG9uc2Uuc3ltYm9scztcbiAgfVxuXG4gIC8qKlxuICAgKiBGb3JjZXMgcmVmcmVzaCBhbmQgcmV0cmlldmVzIGxhdGVzdCBxdW90ZXMgZm9yIGEgc3Vic2V0IG9mIHN5bWJvbHMgdGhlIHRlcm1pbmFsIGlzIHN1YnNjcmliZWQgdG9cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFjY291bnRJZCBpZCBvZiB0aGUgTWV0YVRyYWRlciBhY2NvdW50XG4gICAqIEBwYXJhbSB7c3RyaW5nW119IHN5bWJvbHMgcXVvdGUgc3ltYm9scyB0byByZWZyZXNoXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFJlZnJlc2hlZFF1b3Rlcz59IHJlZnJlc2hlZCBxdW90ZXMgYW5kIGJhc2ljIGFjY291bnQgaW5mb3JtYXRpb24gaW5mb1xuICAgKi9cbiAgYXN5bmMgcmVmcmVzaFN5bWJvbFF1b3RlcyhhY2NvdW50SWQsIHN5bWJvbHMpIHtcbiAgICBsZXQgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLnJwY1JlcXVlc3QoYWNjb3VudElkLCB7YXBwbGljYXRpb246ICdSUEMnLCB0eXBlOiAncmVmcmVzaFN5bWJvbFF1b3RlcycsIHN5bWJvbHN9KTtcbiAgICByZXR1cm4gcmVzcG9uc2UucmVmcmVzaGVkUXVvdGVzO1xuICB9XG5cbiAgLyoqXG4gICAqIFNlbmRzIGNsaWVudCB1cHRpbWUgc3RhdHMgdG8gdGhlIHNlcnZlci5cbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBpZCBvZiB0aGUgTWV0YVRyYWRlciBhY2NvdW50IHRvIHNhdmUgdXB0aW1lXG4gICAqIEBwYXJhbSB7T2JqZWN0fSB1cHRpbWUgdXB0aW1lIHN0YXRpc3RpY3MgdG8gc2VuZCB0byB0aGUgc2VydmVyXG4gICAqIEByZXR1cm5zIHtQcm9taXNlfSBwcm9taXNlIHdoaWNoIHJlc29sdmVzIHdoZW4gdXB0aW1lIHN0YXRpc3RpY3MgaXMgc3VibWl0dGVkXG4gICAqL1xuICBzYXZlVXB0aW1lKGFjY291bnRJZCwgdXB0aW1lKSB7XG4gICAgcmV0dXJuIHRoaXMucnBjUmVxdWVzdChhY2NvdW50SWQsIHt0eXBlOiAnc2F2ZVVwdGltZScsIHVwdGltZX0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFVuc3Vic2NyaWJlIGZyb20gYWNjb3VudFxuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGlkIG9mIHRoZSBNZXRhVHJhZGVyIGFjY291bnQgdG8gdW5zdWJzY3JpYmVcbiAgICogQHJldHVybnMge1Byb21pc2V9IHByb21pc2Ugd2hpY2ggcmVzb2x2ZXMgd2hlbiBzb2NrZXQgdW5zdWJzY3JpYmVkXG4gICAqL1xuICBhc3luYyB1bnN1YnNjcmliZShhY2NvdW50SWQpIHtcbiAgICBjb25zdCByZWdpb24gPSB0aGlzLmdldEFjY291bnRSZWdpb24oYWNjb3VudElkKTtcbiAgICB0aGlzLl9sYXRlbmN5U2VydmljZS5vblVuc3Vic2NyaWJlKGFjY291bnRJZCk7XG4gICAgT2JqZWN0LmtleXModGhpcy5fdXBkYXRlRXZlbnRzKVxuICAgICAgLmZpbHRlcihrZXkgPT4ga2V5LnN0YXJ0c1dpdGgoYWNjb3VudElkKSlcbiAgICAgIC5mb3JFYWNoKGtleSA9PiBkZWxldGUgdGhpcy5fdXBkYXRlRXZlbnRzW2tleV0pO1xuICAgIGlmICh0aGlzLl9zb2NrZXRJbnN0YW5jZXNbcmVnaW9uXSkge1xuICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoT2JqZWN0LmtleXModGhpcy5fc29ja2V0SW5zdGFuY2VzW3JlZ2lvbl0pLm1hcChhc3luYyBpbnN0YW5jZU51bWJlciA9PiB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgYXdhaXQgdGhpcy5fc3Vic2NyaXB0aW9uTWFuYWdlci51bnN1YnNjcmliZShhY2NvdW50SWQsIE51bWJlcihpbnN0YW5jZU51bWJlcikpO1xuICAgICAgICAgIGRlbGV0ZSB0aGlzLl9zb2NrZXRJbnN0YW5jZXNCeUFjY291bnRzW2luc3RhbmNlTnVtYmVyXVthY2NvdW50SWRdO1xuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICBpZiAoIShbJ1RpbWVvdXRFcnJvcicsICdOb3RGb3VuZEVycm9yJ10uaW5jbHVkZXMoZXJyLm5hbWUpKSkge1xuICAgICAgICAgICAgdGhpcy5fbG9nZ2VyLndhcm4oYCR7YWNjb3VudElkfToke2luc3RhbmNlTnVtYmVyfTogZmFpbGVkIHRvIHVuc3Vic2NyaWJlYCwgZXJyKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ3VycmVudCBzZXJ2ZXIgdGltZSAoc2VlIGh0dHBzOi8vbWV0YWFwaS5jbG91ZC9kb2NzL2NsaWVudC9tb2RlbHMvc2VydmVyVGltZS8pXG4gICAqIEB0eXBlZGVmIHtPYmplY3R9IFNlcnZlclRpbWVcbiAgICogQHByb3BlcnR5IHtEYXRlfSB0aW1lIGN1cnJlbnQgc2VydmVyIHRpbWVcbiAgICogQHByb3BlcnR5IHtTdHJpbmd9IGJyb2tlclRpbWUgY3VycmVudCBicm9rZXIgdGltZSwgaW4gYnJva2VyIHRpbWV6b25lLCBZWVlZLU1NLUREIEhIOm1tOnNzLlNTUyBmb3JtYXRcbiAgICogQHByb3BlcnR5IHtEYXRlfSBbbGFzdFF1b3RlVGltZV0gbGFzdCBxdW90ZSB0aW1lXG4gICAqIEBwcm9wZXJ0eSB7U3RyaW5nfSBbbGFzdFF1b3RlQnJva2VyVGltZV0gbGFzdCBxdW90ZSB0aW1lLCBpbiBicm9rZXIgdGltZXpvbmUsIFlZWVktTU0tREQgSEg6bW06c3MuU1NTIGZvcm1hdFxuICAgKi9cblxuICAvKipcbiAgICogUmV0dXJucyBzZXJ2ZXIgdGltZSBmb3IgYSBzcGVjaWZpZWQgTWV0YVRyYWRlciBhY2NvdW50XG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhY2NvdW50SWQgaWQgb2YgdGhlIE1ldGFUcmFkZXIgYWNjb3VudCB0byByZXR1cm4gc2VydmVyIHRpbWUgZm9yXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFNlcnZlclRpbWU+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIHNlcnZlciB0aW1lXG4gICAqL1xuICBhc3luYyBnZXRTZXJ2ZXJUaW1lKGFjY291bnRJZCkge1xuICAgIGxldCByZXNwb25zZSA9IGF3YWl0IHRoaXMucnBjUmVxdWVzdChhY2NvdW50SWQsIHthcHBsaWNhdGlvbjogJ1JQQycsIHR5cGU6ICdnZXRTZXJ2ZXJUaW1lJ30pO1xuICAgIHJldHVybiByZXNwb25zZS5zZXJ2ZXJUaW1lO1xuICB9XG5cbiAgLyoqXG4gICAqIE1hcmdpbiByZXF1aXJlZCB0byBvcGVuIGEgdHJhZGUgKHNlZSBodHRwczovL21ldGFhcGkuY2xvdWQvZG9jcy9jbGllbnQvbW9kZWxzL21hcmdpbi8pXG4gICAqIEB0eXBlZGVmIHtPYmplY3R9IE1hcmdpblxuICAgKiBAcHJvcGVydHkge251bWJlcn0gW21hcmdpbl0gbWFyZ2luIHJlcXVpcmVkIHRvIG9wZW4gYSB0cmFkZS4gSWYgbWFyZ2luIGNhbiBub3QgYmUgY2FsY3VsYXRlZCwgdGhlbiB0aGlzIGZpZWxkIGlzXG4gICAqIG5vdCBkZWZpbmVkXG4gICAqL1xuXG4gIC8qKlxuICAgKiBDb250YWlucyBvcmRlciB0byBjYWxjdWxhdGUgbWFyZ2luIGZvciAoc2VlIGh0dHBzOi8vbWV0YWFwaS5jbG91ZC9kb2NzL2NsaWVudC9tb2RlbHMvbWFyZ2luT3JkZXIvKVxuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBNYXJnaW5PcmRlclxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gc3ltYm9sIG9yZGVyIHN5bWJvbFxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gdHlwZSBvcmRlciB0eXBlLCBvbmUgb2YgT1JERVJfVFlQRV9CVVkgb3IgT1JERVJfVFlQRV9TRUxMXG4gICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSB2b2x1bWUgb3JkZXIgdm9sdW1lLCBtdXN0IGJlIGdyZWF0ZXIgdGhhbiAwXG4gICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBvcGVuUHJpY2Ugb3JkZXIgb3BlbiBwcmljZSwgbXVzdCBiZSBncmVhdGVyIHRoYW4gMFxuICAgKi9cblxuICAvKipcbiAgICogQ2FsY3VsYXRlcyBtYXJnaW4gcmVxdWlyZWQgdG8gb3BlbiBhIHRyYWRlIG9uIHRoZSBzcGVjaWZpZWQgdHJhZGluZyBhY2NvdW50XG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhY2NvdW50SWQgaWQgb2YgdGhlIHRyYWRpbmcgYWNjb3VudCB0byBjYWxjdWxhdGUgbWFyZ2luIGZvclxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXBwbGljYXRpb24gYXBwbGljYXRpb24gdG8gc2VuZCB0aGUgcmVxdWVzdCB0b1xuICAgKiBAcGFyYW0ge3N0cmluZ30gcmVsaWFiaWxpdHkgYWNjb3VudCByZWxpYWJpbGl0eVxuICAgKiBAcGFyYW0ge01hcmdpbk9yZGVyfSBvcmRlciBvcmRlciB0byBjYWxjdWxhdGUgbWFyZ2luIGZvclxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxNYXJnaW4+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIG1hcmdpbiBjYWxjdWxhdGlvbiByZXN1bHRcbiAgICovXG4gIGFzeW5jIGNhbGN1bGF0ZU1hcmdpbihhY2NvdW50SWQsIGFwcGxpY2F0aW9uLCByZWxpYWJpbGl0eSwgb3JkZXIpIHtcbiAgICBsZXQgcmVzcG9uc2U7XG4gICAgaWYgKGFwcGxpY2F0aW9uID09PSAnUlBDJykge1xuICAgICAgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLnJwY1JlcXVlc3QoYWNjb3VudElkLCB7YXBwbGljYXRpb24sIHR5cGU6ICdjYWxjdWxhdGVNYXJnaW4nLCBvcmRlcn0pO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXNwb25zZSA9IGF3YWl0IHRoaXMucnBjUmVxdWVzdEFsbEluc3RhbmNlcyhhY2NvdW50SWQsIHthcHBsaWNhdGlvbiwgdHlwZTogJ2NhbGN1bGF0ZU1hcmdpbicsIG9yZGVyfSxcbiAgICAgICAgcmVsaWFiaWxpdHkpO1xuICAgIH1cbiAgICByZXR1cm4gcmVzcG9uc2UubWFyZ2luO1xuICB9XG5cbiAgLyoqXG4gICAqIENhbGxzIG9uVW5zdWJzY3JpYmVSZWdpb24gbGlzdGVuZXIgZXZlbnQgXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge1N0cmluZ30gcmVnaW9uIGFjY291bnQgcmVnaW9uIHRvIHVuc3Vic2NyaWJlXG4gICAqL1xuICBhc3luYyB1bnN1YnNjcmliZUFjY291bnRSZWdpb24oYWNjb3VudElkLCByZWdpb24pIHtcbiAgICBjb25zdCB1bnN1YnNjcmliZVByb21pc2VzID0gW107XG4gICAgZm9yIChsZXQgbGlzdGVuZXIgb2YgdGhpcy5fc3luY2hyb25pemF0aW9uTGlzdGVuZXJzW2FjY291bnRJZF0gfHwgW10pIHtcbiAgICAgIHVuc3Vic2NyaWJlUHJvbWlzZXMucHVzaCh0aGlzXG4gICAgICAgIC5fcHJvY2Vzc0V2ZW50KCgpID0+IGxpc3RlbmVyLm9uVW5zdWJzY3JpYmVSZWdpb24ocmVnaW9uKSwgYCR7YWNjb3VudElkfToke3JlZ2lvbn06b25VbnN1YnNjcmliZVJlZ2lvbmAsIHRydWUpXG4gICAgICAgIC5jYXRjaChlcnIgPT4gdGhpcy5fbG9nZ2VyLmVycm9yKFxuICAgICAgICAgIGAke2FjY291bnRJZH06JHtyZWdpb259OiBGYWlsZWQgdG8gbm90aWZ5IGxpc3RlbmVyIGFib3V0IG9uVW5zdWJzY3JpYmVSZWdpb24gZXZlbnRgLCBlcnJcbiAgICAgICAgKSlcbiAgICAgICk7XG4gICAgfVxuICAgIGF3YWl0IFByb21pc2UuYWxsKHVuc3Vic2NyaWJlUHJvbWlzZXMpO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgc3luY2hyb25pemF0aW9uIGxpc3RlbmVyIGZvciBzcGVjaWZpYyBhY2NvdW50XG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge1N5bmNocm9uaXphdGlvbkxpc3RlbmVyfSBsaXN0ZW5lciBzeW5jaHJvbml6YXRpb24gbGlzdGVuZXIgdG8gYWRkXG4gICAqL1xuICBhZGRTeW5jaHJvbml6YXRpb25MaXN0ZW5lcihhY2NvdW50SWQsIGxpc3RlbmVyKSB7XG4gICAgdGhpcy5fbG9nZ2VyLnRyYWNlKGAke2FjY291bnRJZH06IEFkZGVkIHN5bmNocm9uaXphdGlvbiBsaXN0ZW5lcmApO1xuICAgIGxldCBsaXN0ZW5lcnMgPSB0aGlzLl9zeW5jaHJvbml6YXRpb25MaXN0ZW5lcnNbYWNjb3VudElkXTtcbiAgICBpZiAoIWxpc3RlbmVycykge1xuICAgICAgbGlzdGVuZXJzID0gW107XG4gICAgICB0aGlzLl9zeW5jaHJvbml6YXRpb25MaXN0ZW5lcnNbYWNjb3VudElkXSA9IGxpc3RlbmVycztcbiAgICB9XG4gICAgbGlzdGVuZXJzLnB1c2gobGlzdGVuZXIpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgc3luY2hyb25pemF0aW9uIGxpc3RlbmVyIGZvciBzcGVjaWZpYyBhY2NvdW50XG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge1N5bmNocm9uaXphdGlvbkxpc3RlbmVyfSBsaXN0ZW5lciBzeW5jaHJvbml6YXRpb24gbGlzdGVuZXIgdG8gcmVtb3ZlXG4gICAqL1xuICByZW1vdmVTeW5jaHJvbml6YXRpb25MaXN0ZW5lcihhY2NvdW50SWQsIGxpc3RlbmVyKSB7XG4gICAgdGhpcy5fbG9nZ2VyLnRyYWNlKGAke2FjY291bnRJZH06IFJlbW92ZWQgc3luY2hyb25pemF0aW9uIGxpc3RlbmVyYCk7XG4gICAgbGV0IGxpc3RlbmVycyA9IHRoaXMuX3N5bmNocm9uaXphdGlvbkxpc3RlbmVyc1thY2NvdW50SWRdO1xuICAgIGlmICghbGlzdGVuZXJzKSB7XG4gICAgICBsaXN0ZW5lcnMgPSBbXTtcbiAgICB9XG4gICAgbGlzdGVuZXJzID0gbGlzdGVuZXJzLmZpbHRlcihsID0+IGwgIT09IGxpc3RlbmVyKTtcbiAgICB0aGlzLl9zeW5jaHJvbml6YXRpb25MaXN0ZW5lcnNbYWNjb3VudElkXSA9IGxpc3RlbmVycztcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGRzIGxhdGVuY3kgbGlzdGVuZXJcbiAgICogQHBhcmFtIHtMYXRlbmN5TGlzdGVuZXJ9IGxpc3RlbmVyIGxhdGVuY3kgbGlzdGVuZXIgdG8gYWRkXG4gICAqL1xuICBhZGRMYXRlbmN5TGlzdGVuZXIobGlzdGVuZXIpIHtcbiAgICB0aGlzLl9sYXRlbmN5TGlzdGVuZXJzLnB1c2gobGlzdGVuZXIpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgbGF0ZW5jeSBsaXN0ZW5lclxuICAgKiBAcGFyYW0ge0xhdGVuY3lMaXN0ZW5lcn0gbGlzdGVuZXIgbGF0ZW5jeSBsaXN0ZW5lciB0byByZW1vdmVcbiAgICovXG4gIHJlbW92ZUxhdGVuY3lMaXN0ZW5lcihsaXN0ZW5lcikge1xuICAgIHRoaXMuX2xhdGVuY3lMaXN0ZW5lcnMgPSB0aGlzLl9sYXRlbmN5TGlzdGVuZXJzLmZpbHRlcihsID0+IGwgIT09IGxpc3RlbmVyKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGRzIHJlY29ubmVjdCBsaXN0ZW5lclxuICAgKiBAcGFyYW0ge1JlY29ubmVjdExpc3RlbmVyfSBsaXN0ZW5lciByZWNvbm5lY3QgbGlzdGVuZXIgdG8gYWRkXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZCBvZiBsaXN0ZW5lclxuICAgKi9cbiAgYWRkUmVjb25uZWN0TGlzdGVuZXIobGlzdGVuZXIsIGFjY291bnRJZCkge1xuICAgIHRoaXMuX3JlY29ubmVjdExpc3RlbmVycy5wdXNoKHthY2NvdW50SWQsIGxpc3RlbmVyfSk7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyByZWNvbm5lY3QgbGlzdGVuZXJcbiAgICogQHBhcmFtIHtSZWNvbm5lY3RMaXN0ZW5lcn0gbGlzdGVuZXIgbGlzdGVuZXIgdG8gcmVtb3ZlXG4gICAqL1xuICByZW1vdmVSZWNvbm5lY3RMaXN0ZW5lcihsaXN0ZW5lcikge1xuICAgIHRoaXMuX3JlY29ubmVjdExpc3RlbmVycyA9IHRoaXMuX3JlY29ubmVjdExpc3RlbmVycy5maWx0ZXIobCA9PiBsLmxpc3RlbmVyICE9PSBsaXN0ZW5lcik7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhbGwgbGlzdGVuZXJzLiBJbnRlbmRlZCBmb3IgdXNlIGluIHVuaXQgdGVzdHMuXG4gICAqL1xuICByZW1vdmVBbGxMaXN0ZW5lcnMoKSB7XG4gICAgdGhpcy5fc3luY2hyb25pemF0aW9uTGlzdGVuZXJzID0ge307XG4gICAgdGhpcy5fcmVjb25uZWN0TGlzdGVuZXJzID0gW107XG4gIH1cblxuICAvKipcbiAgICogQ2xlYXJzIGFjY291bnQgb3IgcmVwbGljYSBkYXRhIGZyb20gY2xpZW50IHJlY29yZHMgYW5kIHVuc3Vic2NyaWJlc1xuICAgKiBAcGFyYW0ge3N0cmluZ30gYWNjb3VudElkIGFjY291bnQgaWQgdG8gcHJvY2VzcyB0aGUgcmVtb3ZhbCBvZlxuICAgKi9cbiAgb25BY2NvdW50RGVsZXRlZChhY2NvdW50SWQpIHtcbiAgICB0aGlzLl9zdWJzY3JpcHRpb25NYW5hZ2VyLmNhbmNlbEFjY291bnQoYWNjb3VudElkKTtcbiAgICB0aGlzLl9sYXRlbmN5U2VydmljZS5vblVuc3Vic2NyaWJlKGFjY291bnRJZCk7XG4gICAgY29uc3QgbWFzdGVyQWNjb3VudElkID0gdGhpcy5fYWNjb3VudHNCeVJlcGxpY2FJZFthY2NvdW50SWRdO1xuICAgIGlmIChtYXN0ZXJBY2NvdW50SWQpIHtcbiAgICAgIGlmIChtYXN0ZXJBY2NvdW50SWQgPT09IGFjY291bnRJZCkge1xuICAgICAgICBjb25zdCByZWdpb25EYXRhID0gdGhpcy5fYWNjb3VudFJlcGxpY2FzW21hc3RlckFjY291bnRJZF07XG4gICAgICAgIGNvbnN0IHJlcGxpY2FzID0gT2JqZWN0LnZhbHVlczxhbnk+KHJlZ2lvbkRhdGEpO1xuICAgICAgICBPYmplY3Qua2V5cyh0aGlzLl9zeW5jaHJvbml6YXRpb25JZEJ5SW5zdGFuY2UpXG4gICAgICAgICAgLmZpbHRlcihpbnN0YW5jZSA9PiBpbnN0YW5jZS5zdGFydHNXaXRoKGAke21hc3RlckFjY291bnRJZH1gKSlcbiAgICAgICAgICAuZm9yRWFjaChpbnN0YW5jZSA9PiBkZWxldGUgdGhpcy5fc3luY2hyb25pemF0aW9uSWRCeUluc3RhbmNlW2luc3RhbmNlXSk7XG4gICAgICAgIHJlcGxpY2FzLmZvckVhY2gocmVwbGljYSA9PiB7XG4gICAgICAgICAgT2JqZWN0LnZhbHVlcyh0aGlzLl9zb2NrZXRJbnN0YW5jZXNCeUFjY291bnRzKS5mb3JFYWNoKGluc3RhbmNlID0+IGRlbGV0ZSBpbnN0YW5jZVtyZXBsaWNhXSk7XG4gICAgICAgICAgZGVsZXRlIHRoaXMuX2FjY291bnRzQnlSZXBsaWNhSWRbcmVwbGljYV07XG4gICAgICAgICAgZGVsZXRlIHRoaXMuX3JlZ2lvbnNCeUFjY291bnRzW3JlcGxpY2FdO1xuICAgICAgICB9KTtcbiAgICAgICAgZGVsZXRlIHRoaXMuX2FjY291bnRSZXBsaWNhc1ttYXN0ZXJBY2NvdW50SWRdO1xuICAgICAgICB0aGlzLl9sb2dnZXIuZGVidWcoYCR7bWFzdGVyQWNjb3VudElkfTogcHJvY2Vzc2VkIHByaW1hcnkgYWNjb3VudCByZW1vdmFsYCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBPYmplY3QudmFsdWVzKHRoaXMuX3NvY2tldEluc3RhbmNlc0J5QWNjb3VudHMpLmZvckVhY2goaW5zdGFuY2UgPT4gZGVsZXRlIGluc3RhbmNlW2FjY291bnRJZF0pO1xuICAgICAgICBjb25zdCByZWdpb25EYXRhID0gdGhpcy5fcmVnaW9uc0J5QWNjb3VudHNbYWNjb3VudElkXTtcbiAgICAgICAgaWYgKHJlZ2lvbkRhdGEpIHtcbiAgICAgICAgICBjb25zdCByZWdpb24gPSByZWdpb25EYXRhLnJlZ2lvbjtcbiAgICAgICAgICBPYmplY3Qua2V5cyh0aGlzLl9zeW5jaHJvbml6YXRpb25JZEJ5SW5zdGFuY2UpXG4gICAgICAgICAgICAuZmlsdGVyKGluc3RhbmNlID0+IGluc3RhbmNlLnN0YXJ0c1dpdGgoYCR7bWFzdGVyQWNjb3VudElkfToke3JlZ2lvbn1gKSlcbiAgICAgICAgICAgIC5mb3JFYWNoKGluc3RhbmNlID0+IGRlbGV0ZSB0aGlzLl9zeW5jaHJvbml6YXRpb25JZEJ5SW5zdGFuY2VbaW5zdGFuY2VdKTtcbiAgICAgICAgICBkZWxldGUgdGhpcy5fYWNjb3VudFJlcGxpY2FzW21hc3RlckFjY291bnRJZF1bcmVnaW9uXTtcbiAgICAgICAgICB0aGlzLl9sb2dnZXIuZGVidWcoYCR7bWFzdGVyQWNjb3VudElkfTogcHJvY2Vzc2VkIHJlbW92YWwgb2YgcmVwbGljYSAke2FjY291bnRJZH1gKTtcbiAgICAgICAgfVxuICAgICAgICBkZWxldGUgdGhpcy5fYWNjb3VudHNCeVJlcGxpY2FJZFthY2NvdW50SWRdO1xuICAgICAgICBkZWxldGUgdGhpcy5fcmVnaW9uc0J5QWNjb3VudHNbYWNjb3VudElkXTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBRdWV1ZXMgYW4gYWNjb3VudCBwYWNrZXQgZm9yIHByb2Nlc3NpbmdcbiAgICogQHBhcmFtIHtPYmplY3R9IHBhY2tldCBwYWNrZXQgdG8gcHJvY2Vzc1xuICAgKi9cbiAgcXVldWVQYWNrZXQoc29ja2V0OiBNZXRhQXBpV2Vic29ja2V0Q2xpZW50LlNvY2tldEluc3RhbmNlLCBwYWNrZXQpIHtcbiAgICBjb25zdCBhY2NvdW50SWQgPSBwYWNrZXQuYWNjb3VudElkO1xuICAgIGNvbnN0IHBhY2tldHMgPSB0aGlzLl9wYWNrZXRPcmRlcmVyLnJlc3RvcmVPcmRlcjxSZXN0b3JlZE9yZGVyUGFja2V0Pih7XG4gICAgICBhY2NvdW50SWQ6IHBhY2tldC5hY2NvdW50SWQsXG4gICAgICBob3N0OiBwYWNrZXQuaG9zdCxcbiAgICAgIGluc3RhbmNlSW5kZXg6IHBhY2tldC5pbnN0YW5jZUluZGV4LFxuICAgICAgc2VxdWVuY2VOdW1iZXI6IHBhY2tldC5zZXF1ZW5jZU51bWJlcixcbiAgICAgIHNlcXVlbmNlVGltZXN0YW1wOiBwYWNrZXQuc2VxdWVuY2VUaW1lc3RhbXAsXG4gICAgICBzeW5jaHJvbml6YXRpb25JZDogcGFja2V0LnN5bmNocm9uaXphdGlvbklkLFxuICAgICAgdHlwZTogcGFja2V0LnR5cGUsXG4gICAgICBzb2NrZXQsIHBhY2tldFxuICAgIH0pLmZpbHRlcihwID0+IHAudHlwZSAhPT0gJ25vb3AnKTtcbiAgICBpZiAodGhpcy5fc2VxdWVudGlhbEV2ZW50UHJvY2Vzc2luZyAmJiBwYWNrZXQuc2VxdWVuY2VOdW1iZXIgIT09IHVuZGVmaW5lZCkge1xuICAgICAgY29uc3QgZXZlbnRzID0gcGFja2V0cy5tYXAoKGl0ZW06IFJlc3RvcmVkT3JkZXJQYWNrZXQpID0+ICgpID0+IChcbiAgICAgICAgdGhpcy5fcHJvY2Vzc1N5bmNocm9uaXphdGlvblBhY2tldChpdGVtLnNvY2tldCwgaXRlbS5wYWNrZXQpXG4gICAgICApKTtcbiAgICAgIGlmICghdGhpcy5fZXZlbnRRdWV1ZXNbYWNjb3VudElkXSkge1xuICAgICAgICB0aGlzLl9ldmVudFF1ZXVlc1thY2NvdW50SWRdID0gZXZlbnRzO1xuICAgICAgICB0aGlzLl9jYWxsQWNjb3VudEV2ZW50cyhhY2NvdW50SWQpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5fZXZlbnRRdWV1ZXNbYWNjb3VudElkXSA9IHRoaXMuX2V2ZW50UXVldWVzW2FjY291bnRJZF0uY29uY2F0KGV2ZW50cyk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHBhY2tldHMuZm9yRWFjaCgoaXRlbTogUmVzdG9yZWRPcmRlclBhY2tldCkgPT4gdGhpcy5fcHJvY2Vzc1N5bmNocm9uaXphdGlvblBhY2tldChpdGVtLnNvY2tldCwgaXRlbS5wYWNrZXQpKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUXVldWVzIGFjY291bnQgZXZlbnQgZm9yIHByb2Nlc3NpbmdcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBhY2NvdW50IGlkXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBuYW1lIGV2ZW50IGxhYmVsIG5hbWVcbiAgICogQHBhcmFtIHtGdW5jdGlvbn0gY2FsbGFibGUgYXN5bmMgb3IgcmVndWxhciBmdW5jdGlvbiB0byBleGVjdXRlXG4gICAqL1xuICBxdWV1ZUV2ZW50KGFjY291bnRJZCwgbmFtZSwgY2FsbGFibGUpIHtcbiAgICBsZXQgZXZlbnQgPSAoKSA9PiB0aGlzLl9wcm9jZXNzRXZlbnQoY2FsbGFibGUsIGAke2FjY291bnRJZH06JHtuYW1lfWApO1xuICAgIGlmICh0aGlzLl9zZXF1ZW50aWFsRXZlbnRQcm9jZXNzaW5nKSB7XG4gICAgICBpZiAoIXRoaXMuX2V2ZW50UXVldWVzW2FjY291bnRJZF0pIHsgXG4gICAgICAgIHRoaXMuX2V2ZW50UXVldWVzW2FjY291bnRJZF0gPSBbZXZlbnRdO1xuICAgICAgICB0aGlzLl9jYWxsQWNjb3VudEV2ZW50cyhhY2NvdW50SWQpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5fZXZlbnRRdWV1ZXNbYWNjb3VudElkXS5wdXNoKGV2ZW50KTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgZXZlbnQoKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBfY2FsbEFjY291bnRFdmVudHMoYWNjb3VudElkKSB7XG4gICAgaWYgKHRoaXMuX2V2ZW50UXVldWVzW2FjY291bnRJZF0pIHtcbiAgICAgIHdoaWxlKHRoaXMuX2V2ZW50UXVldWVzW2FjY291bnRJZF0ubGVuZ3RoKSB7XG4gICAgICAgIGF3YWl0IHRoaXMuX2V2ZW50UXVldWVzW2FjY291bnRJZF1bMF0oKTtcbiAgICAgICAgdGhpcy5fZXZlbnRRdWV1ZXNbYWNjb3VudElkXS5zaGlmdCgpO1xuICAgICAgfVxuICAgICAgZGVsZXRlIHRoaXMuX2V2ZW50UXVldWVzW2FjY291bnRJZF07XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgX3JlY29ubmVjdChpbnN0YW5jZU51bWJlciwgc29ja2V0SW5zdGFuY2VJbmRleCwgcmVnaW9uKSB7XG4gICAgY29uc3QgaW5zdGFuY2UgPSB0aGlzLnNvY2tldEluc3RhbmNlc1tyZWdpb25dW2luc3RhbmNlTnVtYmVyXVtzb2NrZXRJbnN0YW5jZUluZGV4XTtcbiAgICBpZiAoaW5zdGFuY2UpIHtcbiAgICAgIHdoaWxlICghaW5zdGFuY2Uuc29ja2V0LmNvbm5lY3RlZCAmJiAhaW5zdGFuY2UuaXNSZWNvbm5lY3RpbmcgJiYgaW5zdGFuY2UuY29ubmVjdGVkKSB7XG4gICAgICAgIGF3YWl0IHRoaXMuX3RyeVJlY29ubmVjdChpbnN0YW5jZU51bWJlciwgc29ja2V0SW5zdGFuY2VJbmRleCwgcmVnaW9uKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBfdHJ5UmVjb25uZWN0KGluc3RhbmNlTnVtYmVyLCBzb2NrZXRJbnN0YW5jZUluZGV4LCByZWdpb24pIHtcbiAgICBjb25zdCBpbnN0YW5jZSA9IHRoaXMuc29ja2V0SW5zdGFuY2VzW3JlZ2lvbl1baW5zdGFuY2VOdW1iZXJdW3NvY2tldEluc3RhbmNlSW5kZXhdO1xuICAgIGluc3RhbmNlLnJlY29ubmVjdFdhaXRUaW1lID0gTWF0aC5taW4oaW5zdGFuY2UucmVjb25uZWN0V2FpdFRpbWUgKiAyLCAzMDAwMCk7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KGFzeW5jICgpID0+IHtcbiAgICAgIGlmICghaW5zdGFuY2Uuc29ja2V0LmNvbm5lY3RlZCAmJiAhaW5zdGFuY2UuaXNSZWNvbm5lY3RpbmcgJiYgaW5zdGFuY2UuY29ubmVjdGVkKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgaW5zdGFuY2Uuc2Vzc2lvbklkID0gcmFuZG9tc3RyaW5nLmdlbmVyYXRlKDMyKTtcbiAgICAgICAgICBjb25zdCBjbGllbnRJZCA9IE1hdGgucmFuZG9tKCk7XG4gICAgICAgICAgaW5zdGFuY2Uuc29ja2V0LmNsb3NlKCk7XG4gICAgICAgICAgaW5zdGFuY2Uuc29ja2V0LmlvLm9wdHMuZXh0cmFIZWFkZXJzWydDbGllbnQtSWQnXSA9IGNsaWVudElkO1xuICAgICAgICAgIGluc3RhbmNlLnNvY2tldC5pby5vcHRzLnF1ZXJ5LmNsaWVudElkID0gY2xpZW50SWQ7XG4gICAgICAgICAgaW5zdGFuY2UuaXNSZWNvbm5lY3RpbmcgPSB0cnVlO1xuICAgICAgICAgIGluc3RhbmNlLnNvY2tldC5pby51cmkgPSBhd2FpdCB0aGlzLl9nZXRTZXJ2ZXJVcmwoaW5zdGFuY2VOdW1iZXIsIHNvY2tldEluc3RhbmNlSW5kZXgsIHJlZ2lvbik7XG4gICAgICAgICAgaW5zdGFuY2Uuc29ja2V0LmNvbm5lY3QoKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICBpbnN0YW5jZS5pc1JlY29ubmVjdGluZyA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXNvbHZlKCk7XG4gICAgfSwgaW5zdGFuY2UucmVjb25uZWN0V2FpdFRpbWUpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTaW11bGF0YW5lb3VzbHkgc2VuZHMgUlBDIHJlcXVlc3RzIHRvIGFsbCBzeW5jaHJvbml6ZWQgaW5zdGFuY2VzXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgbWV0YXRyYWRlciBhY2NvdW50IGlkXG4gICAqIEBwYXJhbSB7T2JqZWN0fSByZXF1ZXN0IGJhc2UgcmVxdWVzdCBkYXRhXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBbcmVsaWFiaWxpdHldIGFjY291bnQgcmVsaWFiaWxpdHlcbiAgICogQHBhcmFtIHtOdW1iZXJ9IFt0aW1lb3V0SW5TZWNvbmRzXSByZXF1ZXN0IHRpbWVvdXQgaW4gc2Vjb25kc1xuICAgKi9cbiAgYXN5bmMgcnBjUmVxdWVzdEFsbEluc3RhbmNlcyhhY2NvdW50SWQsIHJlcXVlc3QsIHJlbGlhYmlsaXR5PywgdGltZW91dEluU2Vjb25kcz8pIHtcbiAgICBpZiAocmVsaWFiaWxpdHkgPT09ICdoaWdoJykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IGFueShbMCwgMV0ubWFwKGluc3RhbmNlTnVtYmVyID0+IHtcbiAgICAgICAgICByZXR1cm4gdGhpcy5ycGNSZXF1ZXN0KGFjY291bnRJZCwgT2JqZWN0LmFzc2lnbih7fSwgcmVxdWVzdCwgXG4gICAgICAgICAgICB7aW5zdGFuY2VJbmRleDogaW5zdGFuY2VOdW1iZXJ9KSwgdGltZW91dEluU2Vjb25kcyk7XG4gICAgICAgIH0pKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIHRocm93IGVycm9yLmVycm9yc1swXTsgXG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBhd2FpdCB0aGlzLnJwY1JlcXVlc3QoYWNjb3VudElkLCByZXF1ZXN0LCB0aW1lb3V0SW5TZWNvbmRzKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogTWFrZXMgYSBSUEMgcmVxdWVzdFxuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIHJlcGxpY2EgYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdCBiYXNlIHJlcXVlc3QgZGF0YVxuICAgKiBAcGFyYW0ge051bWJlcn0gW3RpbWVvdXRJblNlY29uZHNdIHJlcXVlc3QgdGltZW91dCBpbiBzZWNvbmRzXG4gICAqL1xuICAvL2VzbGludC1kaXNhYmxlLW5leHQtbGluZSBjb21wbGV4aXR5LCBtYXgtc3RhdGVtZW50c1xuICBhc3luYyBycGNSZXF1ZXN0KGFjY291bnRJZDogc3RyaW5nLCByZXF1ZXN0LCB0aW1lb3V0SW5TZWNvbmRzPyk6IFByb21pc2U8YW55PiB7XG4gICAgY29uc3QgaWdub3JlZFJlcXVlc3RUeXBlcyA9IFsnc3Vic2NyaWJlJywgJ3N5bmNocm9uaXplJywgJ3JlZnJlc2hNYXJrZXREYXRhU3Vic2NyaXB0aW9ucycsICd1bnN1YnNjcmliZSddO1xuICAgIGNvbnN0IHByaW1hcnlBY2NvdW50SWQgPSB0aGlzLl9hY2NvdW50c0J5UmVwbGljYUlkW2FjY291bnRJZF07XG4gICAgbGV0IGNvbm5lY3RlZEluc3RhbmNlID0gdGhpcy5fbGF0ZW5jeVNlcnZpY2UuZ2V0QWN0aXZlQWNjb3VudEluc3RhbmNlcyhwcmltYXJ5QWNjb3VudElkKVswXTtcbiAgICBpZiAoIWlnbm9yZWRSZXF1ZXN0VHlwZXMuaW5jbHVkZXMocmVxdWVzdC50eXBlKSkge1xuICAgICAgY29ubmVjdGVkSW5zdGFuY2UgfHw9IGF3YWl0IHRoaXMuX2xhdGVuY3lTZXJ2aWNlLndhaXRDb25uZWN0ZWRJbnN0YW5jZShhY2NvdW50SWQpO1xuICAgICAgY29uc3QgYWN0aXZlUmVnaW9uID0gY29ubmVjdGVkSW5zdGFuY2Uuc3BsaXQoJzonKVsxXTtcbiAgICAgIGFjY291bnRJZCA9IHRoaXMuX2FjY291bnRSZXBsaWNhc1twcmltYXJ5QWNjb3VudElkXVthY3RpdmVSZWdpb25dO1xuICAgIH1cbiAgICBsZXQgc29ja2V0SW5zdGFuY2VJbmRleCA9IG51bGw7XG4gICAgbGV0IGluc3RhbmNlTnVtYmVyID0gMDtcbiAgICBjb25zdCByZWdpb24gPSB0aGlzLmdldEFjY291bnRSZWdpb24oYWNjb3VudElkKTtcbiAgICB0aGlzLl9yZWZyZXNoQWNjb3VudFJlZ2lvbihhY2NvdW50SWQpO1xuICAgIGlmIChyZXF1ZXN0Lmluc3RhbmNlSW5kZXggIT09IHVuZGVmaW5lZCkge1xuICAgICAgaW5zdGFuY2VOdW1iZXIgPSByZXF1ZXN0Lmluc3RhbmNlSW5kZXg7XG4gICAgfSBlbHNlIHtcbiAgICAgIGlmIChjb25uZWN0ZWRJbnN0YW5jZSkge1xuICAgICAgICBpbnN0YW5jZU51bWJlciA9IE51bWJlcihjb25uZWN0ZWRJbnN0YW5jZS5zcGxpdCgnOicpWzJdKTtcbiAgICAgIH1cbiAgICAgIGlmIChyZXF1ZXN0LmFwcGxpY2F0aW9uICE9PSAnUlBDJykge1xuICAgICAgICByZXF1ZXN0ID0gT2JqZWN0LmFzc2lnbih7fSwgcmVxdWVzdCwge2luc3RhbmNlSW5kZXg6IGluc3RhbmNlTnVtYmVyfSk7XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMuX3NvY2tldEluc3RhbmNlc0J5QWNjb3VudHNbaW5zdGFuY2VOdW1iZXJdIHx8PSB7fTtcbiAgICB0aGlzLl9zb2NrZXRJbnN0YW5jZXNbcmVnaW9uXSB8fD0ge307XG4gICAgdGhpcy5fc29ja2V0SW5zdGFuY2VzW3JlZ2lvbl1baW5zdGFuY2VOdW1iZXJdIHx8PSBbXTtcbiAgICBpZiAodGhpcy5fc29ja2V0SW5zdGFuY2VzQnlBY2NvdW50c1tpbnN0YW5jZU51bWJlcl1bYWNjb3VudElkXSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBzb2NrZXRJbnN0YW5jZUluZGV4ID0gdGhpcy5fc29ja2V0SW5zdGFuY2VzQnlBY2NvdW50c1tpbnN0YW5jZU51bWJlcl1bYWNjb3VudElkXTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5fbG9nZ2VyLmRlYnVnKGAke2FjY291bnRJZH06JHtpbnN0YW5jZU51bWJlcn06IGNyZWF0aW5nIHNvY2tldCBpbnN0YW5jZSBvbiBSUEMgcmVxdWVzdGApO1xuICAgICAgYXdhaXQgdGhpcy5fY3JlYXRlU29ja2V0SW5zdGFuY2VCeUFjY291bnQoYWNjb3VudElkLCBpbnN0YW5jZU51bWJlcik7XG4gICAgICBzb2NrZXRJbnN0YW5jZUluZGV4ID0gdGhpcy5fc29ja2V0SW5zdGFuY2VzQnlBY2NvdW50c1tpbnN0YW5jZU51bWJlcl1bYWNjb3VudElkXTtcbiAgICB9XG4gICAgY29uc3QgaW5zdGFuY2UgPSB0aGlzLl9zb2NrZXRJbnN0YW5jZXNbcmVnaW9uXVtpbnN0YW5jZU51bWJlcl1bc29ja2V0SW5zdGFuY2VJbmRleF07XG4gICAgaWYgKCFpbnN0YW5jZS5jb25uZWN0ZWQpIHtcbiAgICAgIHRoaXMuX2xvZ2dlci5kZWJ1ZyhgJHthY2NvdW50SWR9OiR7aW5zdGFuY2VOdW1iZXJ9OiBjb25uZWN0aW5nIHNvY2tldCBpbnN0YW5jZSBvbiBSUEMgcmVxdWVzdGApO1xuICAgICAgYXdhaXQgdGhpcy5jb25uZWN0KGluc3RhbmNlTnVtYmVyLCByZWdpb24pO1xuICAgIH0gZWxzZSBpZiAoIXRoaXMuY29ubmVjdGVkKGluc3RhbmNlTnVtYmVyLCBzb2NrZXRJbnN0YW5jZUluZGV4LCByZWdpb24pKSB7XG4gICAgICBhd2FpdCBpbnN0YW5jZS5jb25uZWN0UmVzdWx0O1xuICAgIH1cbiAgICBpZiAocmVxdWVzdC50eXBlID09PSAnc3Vic2NyaWJlJykge1xuICAgICAgcmVxdWVzdC5zZXNzaW9uSWQgPSBpbnN0YW5jZS5zZXNzaW9uSWQ7XG4gICAgfVxuICAgIGlmIChbJ3RyYWRlJywgJ3N1YnNjcmliZSddLmluY2x1ZGVzKHJlcXVlc3QudHlwZSkpIHtcbiAgICAgIHJldHVybiB0aGlzLl9tYWtlUmVxdWVzdChhY2NvdW50SWQsIGluc3RhbmNlTnVtYmVyLCByZXF1ZXN0LCB0aW1lb3V0SW5TZWNvbmRzKTtcbiAgICB9XG4gICAgbGV0IHJldHJ5Q291bnRlciA9IDA7XG4gICAgd2hpbGUgKHRydWUpIHsgLy9lc2xpbnQtZGlzYWJsZS1saW5lIG5vLWNvbnN0YW50LWNvbmRpdGlvblxuICAgICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuX21ha2VSZXF1ZXN0KGFjY291bnRJZCwgaW5zdGFuY2VOdW1iZXIsIHJlcXVlc3QsIHRpbWVvdXRJblNlY29uZHMpO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGlmIChlcnIubmFtZSA9PT0gJ1Rvb01hbnlSZXF1ZXN0c0Vycm9yJykge1xuICAgICAgICAgIGxldCBjYWxjUmV0cnlDb3VudGVyID0gcmV0cnlDb3VudGVyO1xuICAgICAgICAgIGxldCBjYWxjUmVxdWVzdFRpbWUgPSAwO1xuICAgICAgICAgIHdoaWxlIChjYWxjUmV0cnlDb3VudGVyIDwgdGhpcy5fcmV0cmllcykge1xuICAgICAgICAgICAgY2FsY1JldHJ5Q291bnRlcisrO1xuICAgICAgICAgICAgY2FsY1JlcXVlc3RUaW1lICs9IE1hdGgubWluKE1hdGgucG93KDIsIGNhbGNSZXRyeUNvdW50ZXIpICogdGhpcy5fbWluUmV0cnlEZWxheUluU2Vjb25kcyxcbiAgICAgICAgICAgICAgdGhpcy5fbWF4UmV0cnlEZWxheUluU2Vjb25kcykgKiAxMDAwO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCByZXRyeVRpbWUgPSBuZXcgRGF0ZShlcnIubWV0YWRhdGEucmVjb21tZW5kZWRSZXRyeVRpbWUpLmdldFRpbWUoKTtcbiAgICAgICAgICBpZiAoRGF0ZS5ub3coKSArIGNhbGNSZXF1ZXN0VGltZSA+IHJldHJ5VGltZSAmJiByZXRyeUNvdW50ZXIgPCB0aGlzLl9yZXRyaWVzKSB7XG4gICAgICAgICAgICBpZiAoRGF0ZS5ub3coKSA8IHJldHJ5VGltZSkge1xuICAgICAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXMgPT4gc2V0VGltZW91dChyZXMsIHJldHJ5VGltZSAtIERhdGUubm93KCkpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHJ5Q291bnRlcisrO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aHJvdyBlcnI7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKFsnTm90U3luY2hyb25pemVkRXJyb3InLCAnVGltZW91dEVycm9yJywgJ05vdEF1dGhlbnRpY2F0ZWRFcnJvcicsXG4gICAgICAgICAgJ0ludGVybmFsRXJyb3InXS5pbmNsdWRlcyhlcnIubmFtZSkgJiYgXG4gICAgICAgICAgcmV0cnlDb3VudGVyIDwgdGhpcy5fcmV0cmllcykge1xuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKHJlcyA9PiBzZXRUaW1lb3V0KHJlcywgTWF0aC5taW4oTWF0aC5wb3coMiwgcmV0cnlDb3VudGVyKSAqIFxuICAgICAgICAgICAgdGhpcy5fbWluUmV0cnlEZWxheUluU2Vjb25kcywgdGhpcy5fbWF4UmV0cnlEZWxheUluU2Vjb25kcykgKiAxMDAwKSk7XG4gICAgICAgICAgcmV0cnlDb3VudGVyKys7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhyb3cgZXJyO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLl9zb2NrZXRJbnN0YW5jZXNCeUFjY291bnRzW2luc3RhbmNlTnVtYmVyXVthY2NvdW50SWRdID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICB0aHJvdyBlcnI7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBwcml2YXRlIF9tYWtlUmVxdWVzdChhY2NvdW50SWQsIGluc3RhbmNlTnVtYmVyLCByZXF1ZXN0LCB0aW1lb3V0SW5TZWNvbmRzKSB7XG4gICAgY29uc3Qgc29ja2V0SW5zdGFuY2UgPSB0aGlzLl9nZXRTb2NrZXRJbnN0YW5jZUJ5QWNjb3VudChhY2NvdW50SWQsIGluc3RhbmNlTnVtYmVyKTtcbiAgICBsZXQgcmVxdWVzdElkID0gcmVxdWVzdC5yZXF1ZXN0SWQgfHwgcmFuZG9tc3RyaW5nLmdlbmVyYXRlKDMyKTtcbiAgICByZXF1ZXN0LnRpbWVzdGFtcHMgPSB7Y2xpZW50UHJvY2Vzc2luZ1N0YXJ0ZWQ6IG5ldyBEYXRlKCl9O1xuICAgIGxldCByZXN1bHQgPSBQcm9taXNlLnJhY2UoW1xuICAgICAgbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gc29ja2V0SW5zdGFuY2UucmVxdWVzdFJlc29sdmVzW3JlcXVlc3RJZF0gPSBcbiAgICAgICAge3Jlc29sdmUsIHJlamVjdCwgdHlwZTogcmVxdWVzdC50eXBlfSksXG4gICAgICBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgcmVqZWN0KG5ldyBUaW1lb3V0RXJyb3IoYE1ldGFBcGkgd2Vic29ja2V0IGNsaWVudCByZXF1ZXN0ICR7cmVxdWVzdC5yZXF1ZXN0SWR9IG9mIHR5cGUgJHtyZXF1ZXN0LnR5cGV9IGAgK1xuICAgICAgICAgICd0aW1lZCBvdXQuIFBsZWFzZSBtYWtlIHN1cmUgeW91ciBhY2NvdW50IGlzIGNvbm5lY3RlZCB0byBicm9rZXIgYmVmb3JlIHJldHJ5aW5nIHlvdXIgcmVxdWVzdC4nKSk7XG4gICAgICAgIGRlbGV0ZSBzb2NrZXRJbnN0YW5jZS5yZXF1ZXN0UmVzb2x2ZXNbcmVxdWVzdElkXTtcbiAgICAgIH0sICh0aW1lb3V0SW5TZWNvbmRzICogMTAwMCkgfHwgdGhpcy5fcmVxdWVzdFRpbWVvdXQpKVxuICAgIF0pO1xuICAgIHJlcXVlc3QuYWNjb3VudElkID0gYWNjb3VudElkO1xuICAgIHJlcXVlc3QuYXBwbGljYXRpb24gPSByZXF1ZXN0LmFwcGxpY2F0aW9uIHx8IHRoaXMuX2FwcGxpY2F0aW9uO1xuICAgIGlmICghcmVxdWVzdC5yZXF1ZXN0SWQpIHtcbiAgICAgIHJlcXVlc3QucmVxdWVzdElkID0gcmVxdWVzdElkO1xuICAgIH1cbiAgICBpZiAocmVxdWVzdC50eXBlID09PSAndW5zdWJzY3JpYmUnIHx8IHJlcXVlc3QuYXBwbGljYXRpb24gPT09ICdSUEMnIHx8XG4gICAgICByZXF1ZXN0Lmluc3RhbmNlSW5kZXggPT09IHNvY2tldEluc3RhbmNlLmluc3RhbmNlTnVtYmVyKSB7XG4gICAgICB0aGlzLl9sb2dnZXIuZGVidWcoKCkgPT4gYCR7YWNjb3VudElkfTogU2VuZGluZyByZXF1ZXN0OiAke0pTT04uc3RyaW5naWZ5KHJlcXVlc3QpfWApO1xuICAgICAgc29ja2V0SW5zdGFuY2Uuc29ja2V0LmVtaXQoJ3JlcXVlc3QnLCByZXF1ZXN0KTtcbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuX2xvZ2dlci50cmFjZSgoKSA9PiBgJHthY2NvdW50SWR9OiR7cmVxdWVzdC5pbnN0YW5jZUluZGV4fTogc2tpcHBpbmcgcmVxdWVzdCBiZWNhdXNlIGl0IGlzIGJlaW5nIHNlbnQgdG8gYCArXG4gICAgICAgIGB0aGUgc29ja2V0IG9mIHRoZSB3cm9uZyBpbnN0YW5jZSBpbmRleCwgcmVxdWVzdD0ke0pTT04uc3RyaW5naWZ5KHJlcXVlc3QpfWApO1xuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG4gIH1cblxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eVxuICBfY29udmVydEVycm9yKGRhdGEpIHtcbiAgICBpZiAoZGF0YS5lcnJvciA9PT0gJ1ZhbGlkYXRpb25FcnJvcicpIHtcbiAgICAgIHJldHVybiBuZXcgVmFsaWRhdGlvbkVycm9yKGRhdGEubWVzc2FnZSwgZGF0YS5kZXRhaWxzKTtcbiAgICB9IGVsc2UgaWYgKGRhdGEuZXJyb3IgPT09ICdOb3RGb3VuZEVycm9yJykge1xuICAgICAgcmV0dXJuIG5ldyBOb3RGb3VuZEVycm9yKGRhdGEubWVzc2FnZSk7XG4gICAgfSBlbHNlIGlmIChkYXRhLmVycm9yID09PSAnTm90U3luY2hyb25pemVkRXJyb3InKSB7XG4gICAgICByZXR1cm4gbmV3IE5vdFN5bmNocm9uaXplZEVycm9yKGRhdGEubWVzc2FnZSk7XG4gICAgfSBlbHNlIGlmIChkYXRhLmVycm9yID09PSAnVGltZW91dEVycm9yJykge1xuICAgICAgcmV0dXJuIG5ldyBUaW1lb3V0RXJyb3IoZGF0YS5tZXNzYWdlKTtcbiAgICB9IGVsc2UgaWYgKGRhdGEuZXJyb3IgPT09ICdOb3RBdXRoZW50aWNhdGVkRXJyb3InKSB7XG4gICAgICByZXR1cm4gbmV3IE5vdENvbm5lY3RlZEVycm9yKGRhdGEubWVzc2FnZSk7XG4gICAgfSBlbHNlIGlmIChkYXRhLmVycm9yID09PSAnRm9yYmlkZGVuRXJyb3InKSB7XG4gICAgICByZXR1cm4gbmV3IEZvcmJpZGRlbkVycm9yKGRhdGEubWVzc2FnZSk7XG4gICAgfSBlbHNlIGlmIChkYXRhLmVycm9yID09PSAnVHJhZGVFcnJvcicpIHtcbiAgICAgIHJldHVybiBuZXcgVHJhZGVFcnJvcihkYXRhLm1lc3NhZ2UsIGRhdGEubnVtZXJpY0NvZGUsIGRhdGEuc3RyaW5nQ29kZSk7XG4gICAgfSBlbHNlIGlmIChkYXRhLmVycm9yID09PSAnVW5hdXRob3JpemVkRXJyb3InKSB7XG4gICAgICB0aGlzLmNsb3NlKCk7XG4gICAgICByZXR1cm4gbmV3IFVuYXV0aG9yaXplZEVycm9yKGRhdGEubWVzc2FnZSk7XG4gICAgfSBlbHNlIGlmIChkYXRhLmVycm9yID09PSAnVG9vTWFueVJlcXVlc3RzRXJyb3InKSB7XG4gICAgICByZXR1cm4gbmV3IFRvb01hbnlSZXF1ZXN0c0Vycm9yKGRhdGEubWVzc2FnZSwgZGF0YS5tZXRhZGF0YSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBuZXcgSW50ZXJuYWxFcnJvcihkYXRhLm1lc3NhZ2UpO1xuICAgIH1cbiAgfVxuXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjb21wbGV4aXR5XG4gIF9jb252ZXJ0SXNvVGltZVRvRGF0ZShwYWNrZXQpIHtcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZ3VhcmQtZm9yLWluXG4gICAgZm9yIChsZXQgZmllbGQgaW4gcGFja2V0KSB7XG4gICAgICBsZXQgdmFsdWUgPSBwYWNrZXRbZmllbGRdO1xuICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgJiYgZmllbGQubWF0Y2goL3RpbWUkfFRpbWUkLykgJiYgXG4gICAgICAgICFmaWVsZC5tYXRjaCgvYnJva2VyVGltZSR8QnJva2VyVGltZSR8dGltZWZyYW1lJC8pKSB7XG4gICAgICAgIHBhY2tldFtmaWVsZF0gPSBuZXcgRGF0ZSh2YWx1ZSk7XG4gICAgICB9XG4gICAgICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHtcbiAgICAgICAgZm9yIChsZXQgaXRlbSBvZiB2YWx1ZSkge1xuICAgICAgICAgIHRoaXMuX2NvbnZlcnRJc29UaW1lVG9EYXRlKGl0ZW0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnb2JqZWN0Jykge1xuICAgICAgICB0aGlzLl9jb252ZXJ0SXNvVGltZVRvRGF0ZSh2YWx1ZSk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChwYWNrZXQgJiYgcGFja2V0LnRpbWVzdGFtcHMpIHtcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBndWFyZC1mb3ItaW5cbiAgICAgIGZvciAobGV0IGZpZWxkIGluIHBhY2tldC50aW1lc3RhbXBzKSB7XG4gICAgICAgIHBhY2tldC50aW1lc3RhbXBzW2ZpZWxkXSA9IG5ldyBEYXRlKHBhY2tldC50aW1lc3RhbXBzW2ZpZWxkXSk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChwYWNrZXQgJiYgcGFja2V0LnR5cGUgPT09ICdwcmljZXMnKSB7XG4gICAgICBmb3IgKGxldCBwcmljZSBvZiBwYWNrZXQucHJpY2VzIHx8IFtdKSB7XG4gICAgICAgIGlmIChwcmljZS50aW1lc3RhbXBzKSB7XG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGd1YXJkLWZvci1pblxuICAgICAgICAgIGZvciAobGV0IGZpZWxkIGluIHByaWNlLnRpbWVzdGFtcHMpIHtcbiAgICAgICAgICAgIHByaWNlLnRpbWVzdGFtcHNbZmllbGRdID0gbmV3IERhdGUocHJpY2UudGltZXN0YW1wc1tmaWVsZF0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBNZXRhVHJhZGVyIHN5bWJvbCBwcmljZS4gQ29udGFpbnMgY3VycmVudCBwcmljZSBmb3IgYSBzeW1ib2wgKHNlZVxuICAgKiBodHRwczovL21ldGFhcGkuY2xvdWQvZG9jcy9jbGllbnQvbW9kZWxzL21ldGF0cmFkZXJTeW1ib2xQcmljZS8pXG4gICAqIEB0eXBlZGVmIHtPYmplY3R9IE1ldGF0cmFkZXJTeW1ib2xQcmljZVxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gc3ltYm9sIHN5bWJvbCAoZS5nLiBhIGN1cnJlbmN5IHBhaXIgb3IgYW4gaW5kZXgpXG4gICAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBiaWQgYmlkIHByaWNlXG4gICAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBhc2sgYXNrIHByaWNlXG4gICAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBwcm9maXRUaWNrVmFsdWUgdGljayB2YWx1ZSBmb3IgYSBwcm9maXRhYmxlIHBvc2l0aW9uXG4gICAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBsb3NzVGlja1ZhbHVlIHRpY2sgdmFsdWUgZm9yIGEgbG9zaW5nIHBvc2l0aW9uXG4gICAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBbYWNjb3VudEN1cnJlbmN5RXhjaGFuZ2VSYXRlXSBjdXJyZW50IGV4Y2hhbmdlIHJhdGUgb2YgYWNjb3VudCBjdXJyZW5jeSBpbnRvIGFjY291bnQgYmFzZVxuICAgKiBjdXJyZW5jeSAoVVNEIGlmIHlvdSBkaWQgbm90IG92ZXJyaWRlIGl0KVxuICAgKiBAcHJvcGVydHkge0RhdGV9IHRpbWUgcXVvdGUgdGltZSwgaW4gSVNPIGZvcm1hdFxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gYnJva2VyVGltZSB0aW1lIHF1b3RlIHRpbWUsIGluIGJyb2tlciB0aW1lem9uZSwgWVlZWS1NTS1ERCBISDptbTpzcy5TU1MgZm9ybWF0XG4gICAqL1xuXG4gIC8qKlxuICAgKiBNZXRhVHJhZGVyIGNhbmRsZVxuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBNZXRhdHJhZGVyQ2FuZGxlXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBzeW1ib2wgc3ltYm9sIChlLmcuIGN1cnJlbmN5IHBhaXIgb3IgYW4gaW5kZXgpXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSB0aW1lZnJhbWUgdGltZWZyYW1lIGNhbmRsZSB3YXMgZ2VuZXJhdGVkIGZvciwgZS5nLiAxaC4gT25lIG9mIDFtLCAybSwgM20sIDRtLCA1bSwgNm0sIDEwbSwgMTJtLFxuICAgKiAxNW0sIDIwbSwgMzBtLCAxaCwgMmgsIDNoLCA0aCwgNmgsIDhoLCAxMmgsIDFkLCAxdywgMW1uXG4gICAqIEBwcm9wZXJ0eSB7RGF0ZX0gdGltZSBjYW5kbGUgb3BlbmluZyB0aW1lXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBicm9rZXJUaW1lIGNhbmRsZSBvcGVuaW5nIHRpbWUsIGluIGJyb2tlciB0aW1lem9uZSwgWVlZWS1NTS1ERCBISDptbTpzcy5TU1MgZm9ybWF0XG4gICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBvcGVuIG9wZW4gcHJpY2VcbiAgICogQHByb3BlcnR5IHtudW1iZXJ9IGhpZ2ggaGlnaCBwcmljZVxuICAgKiBAcHJvcGVydHkge251bWJlcn0gbG93IGxvdyBwcmljZVxuICAgKiBAcHJvcGVydHkge251bWJlcn0gY2xvc2UgY2xvc2UgcHJpY2VcbiAgICogQHByb3BlcnR5IHtudW1iZXJ9IHRpY2tWb2x1bWUgdGljayB2b2x1bWUsIGkuZS4gbnVtYmVyIG9mIHRpY2tzIGluc2lkZSB0aGUgY2FuZGxlXG4gICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBzcHJlYWQgc3ByZWFkIGluIHBvaW50c1xuICAgKiBAcHJvcGVydHkge251bWJlcn0gdm9sdW1lIHRyYWRlIHZvbHVtZVxuICAgKi9cblxuICAvKipcbiAgICogTWV0YVRyYWRlciB0aWNrIGRhdGFcbiAgICogQHR5cGVkZWYge09iamVjdH0gTWV0YXRyYWRlclRpY2tcbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IHN5bWJvbCBzeW1ib2wgKGUuZy4gYSBjdXJyZW5jeSBwYWlyIG9yIGFuIGluZGV4KVxuICAgKiBAcHJvcGVydHkge0RhdGV9IHRpbWUgdGltZVxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gYnJva2VyVGltZSB0aW1lLCBpbiBicm9rZXIgdGltZXpvbmUsIFlZWVktTU0tREQgSEg6bW06c3MuU1NTIGZvcm1hdFxuICAgKiBAcHJvcGVydHkge251bWJlcn0gW2JpZF0gYmlkIHByaWNlXG4gICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBbYXNrXSBhc2sgcHJpY2VcbiAgICogQHByb3BlcnR5IHtudW1iZXJ9IFtsYXN0XSBsYXN0IGRlYWwgcHJpY2VcbiAgICogQHByb3BlcnR5IHtudW1iZXJ9IFt2b2x1bWVdIHZvbHVtZSBmb3IgdGhlIGN1cnJlbnQgbGFzdCBkZWFsIHByaWNlXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBzaWRlIGlzIHRpY2sgYSByZXN1bHQgb2YgYnV5IG9yIHNlbGwgZGVhbCwgb25lIG9mIGJ1eSBvciBzZWxsXG4gICAqL1xuXG4gIC8qKlxuICAgKiBNZXRhVHJhZGVyIG9yZGVyIGJvb2tcbiAgICogQHR5cGVkZWYge09iamVjdH0gTWV0YXRyYWRlckJvb2tcbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IHN5bWJvbCBzeW1ib2wgKGUuZy4gYSBjdXJyZW5jeSBwYWlyIG9yIGFuIGluZGV4KVxuICAgKiBAcHJvcGVydHkge0RhdGV9IHRpbWUgdGltZVxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gYnJva2VyVGltZSB0aW1lLCBpbiBicm9rZXIgdGltZXpvbmUsIFlZWVktTU0tREQgSEg6bW06c3MuU1NTIGZvcm1hdFxuICAgKiBAcHJvcGVydHkge0FycmF5PE1ldGF0cmFkZXJCb29rRW50cnk+fSBib29rIGxpc3Qgb2Ygb3JkZXIgYm9vayBlbnRyaWVzXG4gICAqL1xuXG4gIC8qKlxuICAgKiBNZXRhVHJhZGVyIG9yZGVyIGJvb2sgZW50cnlcbiAgICogQHR5cGVkZWYge09iamVjdH0gTWV0YXRyYWRlckJvb2tFbnRyeVxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gdHlwZSBlbnRyeSB0eXBlLCBvbmUgb2YgQk9PS19UWVBFX1NFTEwsIEJPT0tfVFlQRV9CVVksIEJPT0tfVFlQRV9TRUxMX01BUktFVCxcbiAgICogQk9PS19UWVBFX0JVWV9NQVJLRVRcbiAgICogQHByb3BlcnR5IHtudW1iZXJ9IHByaWNlIHByaWNlXG4gICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSB2b2x1bWUgdm9sdW1lXG4gICAqL1xuXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjb21wbGV4aXR5LG1heC1zdGF0ZW1lbnRzXG4gIHByaXZhdGUgYXN5bmMgX3Byb2Nlc3NTeW5jaHJvbml6YXRpb25QYWNrZXQoc29ja2V0OiBNZXRhQXBpV2Vic29ja2V0Q2xpZW50LlNvY2tldEluc3RhbmNlLCBkYXRhKSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGluc3RhbmNlTnVtYmVyID0gZGF0YS5pbnN0YW5jZUluZGV4IHx8IDA7XG4gICAgICBjb25zdCBzb2NrZXRJbnN0YW5jZSA9IHRoaXMuX2dldFNvY2tldEluc3RhbmNlQnlBY2NvdW50KGRhdGEuYWNjb3VudElkLCBpbnN0YW5jZU51bWJlcik7XG4gICAgICBpZiAoZGF0YS5zeW5jaHJvbml6YXRpb25JZCAmJiBzb2NrZXRJbnN0YW5jZSkge1xuICAgICAgICBzb2NrZXRJbnN0YW5jZS5zeW5jaHJvbml6YXRpb25UaHJvdHRsZXIudXBkYXRlU3luY2hyb25pemF0aW9uSWQoZGF0YS5zeW5jaHJvbml6YXRpb25JZCk7XG4gICAgICB9XG4gICAgICBjb25zdCByZWdpb24gPSB0aGlzLmdldEFjY291bnRSZWdpb24oZGF0YS5hY2NvdW50SWQpO1xuICAgICAgY29uc3QgcHJpbWFyeUFjY291bnRJZCA9IHRoaXMuX2FjY291bnRzQnlSZXBsaWNhSWRbZGF0YS5hY2NvdW50SWRdO1xuICAgICAgbGV0IGluc3RhbmNlSWQgPSBwcmltYXJ5QWNjb3VudElkICsgJzonICsgcmVnaW9uICsgJzonICsgaW5zdGFuY2VOdW1iZXIgKyAnOicgKyAoZGF0YS5ob3N0IHx8IDApO1xuICAgICAgbGV0IGluc3RhbmNlSW5kZXggPSByZWdpb24gKyAnOicgKyBpbnN0YW5jZU51bWJlciArICc6JyArIChkYXRhLmhvc3QgfHwgMCk7XG4gICAgICBsZXQgc291cmNlTGFiZWwgPSBfLmNvbXBhY3QoW2luc3RhbmNlSW5kZXgsIHNvY2tldC5jbGllbnRBcGlIb3N0bmFtZV0pLmpvaW4oJzonKTtcblxuICAgICAgY29uc3QgaXNPbmx5QWN0aXZlSW5zdGFuY2UgPSAoKSA9PiB7XG4gICAgICAgIGNvbnN0IGFjdGl2ZUluc3RhbmNlSWRzID0gT2JqZWN0LmtleXModGhpcy5fY29ubmVjdGVkSG9zdHMpLmZpbHRlcihpbnN0YW5jZSA9PiBcbiAgICAgICAgICBpbnN0YW5jZS5zdGFydHNXaXRoKHByaW1hcnlBY2NvdW50SWQgKyAnOicgKyByZWdpb24gKyAnOicgKyBpbnN0YW5jZU51bWJlcikpO1xuICAgICAgICByZXR1cm4gIWFjdGl2ZUluc3RhbmNlSWRzLmxlbmd0aCB8fCBhY3RpdmVJbnN0YW5jZUlkcy5sZW5ndGggPT09IDEgJiYgYWN0aXZlSW5zdGFuY2VJZHNbMF0gPT09IGluc3RhbmNlSWQ7XG4gICAgICB9O1xuXG4gICAgICBjb25zdCBjYW5jZWxEaXNjb25uZWN0VGltZXIgPSAoKSA9PiB7XG4gICAgICAgIGlmICh0aGlzLl9zdGF0dXNUaW1lcnNbaW5zdGFuY2VJZF0pIHtcbiAgICAgICAgICBjbGVhclRpbWVvdXQodGhpcy5fc3RhdHVzVGltZXJzW2luc3RhbmNlSWRdKTtcbiAgICAgICAgfVxuICAgICAgfTtcblxuICAgICAgY29uc3QgcmVzZXREaXNjb25uZWN0VGltZXIgPSAoKSA9PiB7XG4gICAgICAgIGNhbmNlbERpc2Nvbm5lY3RUaW1lcigpO1xuICAgICAgICB0aGlzLl9zdGF0dXNUaW1lcnNbaW5zdGFuY2VJZF0gPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICB0aGlzLl9sb2dnZXIud2FybihgJHtkYXRhLmFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06IHRpbWVkIG91dCB3YWl0aW5nIGZvciBjb25uZWN0aW9uIHN0YXR1c2ApO1xuICAgICAgICAgIGlmIChpc09ubHlBY3RpdmVJbnN0YW5jZSgpKSB7XG4gICAgICAgICAgICB0aGlzLl9zdWJzY3JpcHRpb25NYW5hZ2VyLm9uVGltZW91dChkYXRhLmFjY291bnRJZCwgMCk7XG4gICAgICAgICAgICB0aGlzLl9zdWJzY3JpcHRpb25NYW5hZ2VyLm9uVGltZW91dChkYXRhLmFjY291bnRJZCwgMSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHRoaXMucXVldWVFdmVudChwcmltYXJ5QWNjb3VudElkLCBgJHtpbnN0YW5jZUluZGV4fTpvbkRpc2Nvbm5lY3RlZGAsICgpID0+IG9uRGlzY29ubmVjdGVkKHRydWUpKTtcbiAgICAgICAgICBjbGVhclRpbWVvdXQodGhpcy5fc3RhdHVzVGltZXJzW2luc3RhbmNlSWRdKTtcbiAgICAgICAgfSwgNjAwMDApO1xuICAgICAgfTtcblxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNvbXBsZXhpdHlcbiAgICAgIGNvbnN0IG9uRGlzY29ubmVjdGVkID0gYXN5bmMgKGlzVGltZW91dCA9IGZhbHNlKSA9PiB7IFxuICAgICAgICBpZiAodGhpcy5fY29ubmVjdGVkSG9zdHNbaW5zdGFuY2VJZF0pIHtcbiAgICAgICAgICB0aGlzLl9sYXRlbmN5U2VydmljZS5vbkRpc2Nvbm5lY3RlZChpbnN0YW5jZUlkKTtcbiAgICAgICAgICBpZiAoaXNPbmx5QWN0aXZlSW5zdGFuY2UoKSkge1xuICAgICAgICAgICAgZm9yIChsZXQgbGlzdGVuZXIgb2YgdGhpcy5fc3luY2hyb25pemF0aW9uTGlzdGVuZXJzW3ByaW1hcnlBY2NvdW50SWRdIHx8IFtdKSB7XG4gICAgICAgICAgICAgIGF3YWl0IHRoaXMuX3Byb2Nlc3NFdmVudChcbiAgICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vbkRpc2Nvbm5lY3RlZChpbnN0YW5jZUluZGV4KSxcbiAgICAgICAgICAgICAgICBgJHtwcmltYXJ5QWNjb3VudElkfToke3NvdXJjZUxhYmVsfTpvbkRpc2Nvbm5lY3RlZGApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICB0aGlzLl9wYWNrZXRPcmRlcmVyLm9uU3RyZWFtQ2xvc2VkKGluc3RhbmNlSWQpO1xuICAgICAgICAgIHNvY2tldEluc3RhbmNlPy5zeW5jaHJvbml6YXRpb25UaHJvdHRsZXIucmVtb3ZlSWRCeVBhcmFtZXRlcnMoZGF0YS5hY2NvdW50SWQsIGluc3RhbmNlTnVtYmVyLCBkYXRhLmhvc3QpO1xuICAgICAgICAgIGZvciAobGV0IGxpc3RlbmVyIG9mIHRoaXMuX3N5bmNocm9uaXphdGlvbkxpc3RlbmVyc1twcmltYXJ5QWNjb3VudElkXSB8fCBbXSkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vblN0cmVhbUNsb3NlZChpbnN0YW5jZUluZGV4KSxcbiAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25TdHJlYW1DbG9zZWRgKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgZGVsZXRlIHRoaXMuX2Nvbm5lY3RlZEhvc3RzW2luc3RhbmNlSWRdO1xuICAgICAgICAgIGlmIChpc09ubHlBY3RpdmVJbnN0YW5jZSgpICYmICFpc1RpbWVvdXQpIHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuX3N1YnNjcmlwdGlvbk1hbmFnZXIub25EaXNjb25uZWN0ZWQoZGF0YS5hY2NvdW50SWQsIDApO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5fc3Vic2NyaXB0aW9uTWFuYWdlci5vbkRpc2Nvbm5lY3RlZChkYXRhLmFjY291bnRJZCwgMSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgaWYgKGRhdGEudHlwZSA9PT0gJ2F1dGhlbnRpY2F0ZWQnKSB7XG4gICAgICAgIHJlc2V0RGlzY29ubmVjdFRpbWVyKCk7XG4gICAgICAgIGlmICghZGF0YS5zZXNzaW9uSWQgfHwgc29ja2V0SW5zdGFuY2UgJiYgZGF0YS5zZXNzaW9uSWQgPT09IHNvY2tldEluc3RhbmNlLnNlc3Npb25JZCkge1xuICAgICAgICAgIHRoaXMuX2xhdGVuY3lTZXJ2aWNlLm9uQ29ubmVjdGVkKGluc3RhbmNlSWQpO1xuICAgICAgICAgIHRoaXMuX2Nvbm5lY3RlZEhvc3RzW2luc3RhbmNlSWRdID0gZGF0YS5ob3N0O1xuICAgICAgICAgIGZvciAobGV0IGxpc3RlbmVyIG9mIHRoaXMuX3N5bmNocm9uaXphdGlvbkxpc3RlbmVyc1twcmltYXJ5QWNjb3VudElkXSB8fCBbXSkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vbkNvbm5lY3RlZChpbnN0YW5jZUluZGV4LCBkYXRhLnJlcGxpY2FzKSxcbiAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25Db25uZWN0ZWRgKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgdGhpcy5fc3Vic2NyaXB0aW9uTWFuYWdlci5jYW5jZWxTdWJzY3JpYmUoZGF0YS5hY2NvdW50SWQgKyAnOicgKyBpbnN0YW5jZU51bWJlcik7XG4gICAgICAgICAgaWYgKGRhdGEucmVwbGljYXMgPT09IDEpIHtcbiAgICAgICAgICAgIHRoaXMuX3N1YnNjcmlwdGlvbk1hbmFnZXIuY2FuY2VsQWNjb3VudChkYXRhLmFjY291bnRJZCk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuX3N1YnNjcmlwdGlvbk1hbmFnZXIuY2FuY2VsU3Vic2NyaWJlKGRhdGEuYWNjb3VudElkICsgJzonICsgaW5zdGFuY2VOdW1iZXIpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChkYXRhLnR5cGUgPT09ICdkaXNjb25uZWN0ZWQnKSB7XG4gICAgICAgIGNhbmNlbERpc2Nvbm5lY3RUaW1lcigpO1xuICAgICAgICBhd2FpdCBvbkRpc2Nvbm5lY3RlZCgpO1xuICAgICAgfSBlbHNlIGlmIChkYXRhLnR5cGUgPT09ICdzeW5jaHJvbml6YXRpb25TdGFydGVkJykge1xuICAgICAgICB0aGlzLl91cGRhdGVFdmVudHNbaW5zdGFuY2VJZF0gPSBbXTtcbiAgICAgICAgdGhpcy5fc3luY2hyb25pemF0aW9uRmxhZ3NbZGF0YS5zeW5jaHJvbml6YXRpb25JZF0gPSB7XG4gICAgICAgICAgYWNjb3VudElkOiBkYXRhLmFjY291bnRJZCwgaW5zdGFuY2VOdW1iZXIsXG4gICAgICAgICAgc3BlY2lmaWNhdGlvbnNVcGRhdGVkOiBkYXRhLnNwZWNpZmljYXRpb25zSGFzaEluZGV4ID09PSB1bmRlZmluZWQsXG4gICAgICAgICAgcG9zaXRpb25zVXBkYXRlZDogZGF0YS5wb3NpdGlvbnNIYXNoSW5kZXggPT09IHVuZGVmaW5lZCxcbiAgICAgICAgICBvcmRlcnNVcGRhdGVkOiBkYXRhLm9yZGVyc0hhc2hJbmRleCA9PT0gdW5kZWZpbmVkXG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuX3N5bmNocm9uaXphdGlvbklkQnlJbnN0YW5jZVtpbnN0YW5jZUlkXSA9IGRhdGEuc3luY2hyb25pemF0aW9uSWQ7XG4gICAgICAgIGNvbnN0IHNwZWNpZmljYXRpb25zSGFzaCA9IChkYXRhLnNwZWNpZmljYXRpb25zSGFzaEluZGV4ICE9PSB1bmRlZmluZWQpID8gXG4gICAgICAgICAgdGhpcy5fc3luY2hyb25pemF0aW9uSGFzaGVzW2RhdGEuc3luY2hyb25pemF0aW9uSWRdICYmIFxuICAgICAgICAgIHRoaXMuX3N5bmNocm9uaXphdGlvbkhhc2hlc1tkYXRhLnN5bmNocm9uaXphdGlvbklkXVxuICAgICAgICAgICAgLnNwZWNpZmljYXRpb25zSGFzaGVzW2RhdGEuc3BlY2lmaWNhdGlvbnNIYXNoSW5kZXhdIDogdW5kZWZpbmVkO1xuICAgICAgICBjb25zdCBwb3NpdGlvbnNIYXNoID0gKGRhdGEucG9zaXRpb25zSGFzaEluZGV4ICE9PSB1bmRlZmluZWQpID8gXG4gICAgICAgICAgdGhpcy5fc3luY2hyb25pemF0aW9uSGFzaGVzW2RhdGEuc3luY2hyb25pemF0aW9uSWRdICYmIFxuICAgICAgICAgIHRoaXMuX3N5bmNocm9uaXphdGlvbkhhc2hlc1tkYXRhLnN5bmNocm9uaXphdGlvbklkXS5wb3NpdGlvbnNIYXNoZXNbZGF0YS5wb3NpdGlvbnNIYXNoSW5kZXhdIDogdW5kZWZpbmVkO1xuICAgICAgICBjb25zdCBvcmRlcnNIYXNoID0gKGRhdGEub3JkZXJzSGFzaEluZGV4ICE9PSB1bmRlZmluZWQpID8gXG4gICAgICAgICAgdGhpcy5fc3luY2hyb25pemF0aW9uSGFzaGVzW2RhdGEuc3luY2hyb25pemF0aW9uSWRdICYmIFxuICAgICAgICAgIHRoaXMuX3N5bmNocm9uaXphdGlvbkhhc2hlc1tkYXRhLnN5bmNocm9uaXphdGlvbklkXS5vcmRlcnNIYXNoZXNbZGF0YS5vcmRlcnNIYXNoSW5kZXhdIDogdW5kZWZpbmVkO1xuICAgICAgICBkZWxldGUgdGhpcy5fc3luY2hyb25pemF0aW9uSGFzaGVzW2RhdGEuc3luY2hyb25pemF0aW9uSWRdO1xuICAgICAgICBmb3IgKGxldCBsaXN0ZW5lciBvZiB0aGlzLl9zeW5jaHJvbml6YXRpb25MaXN0ZW5lcnNbcHJpbWFyeUFjY291bnRJZF0gfHwgW10pIHtcbiAgICAgICAgICBhd2FpdCB0aGlzLl9wcm9jZXNzRXZlbnQoXG4gICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vblN5bmNocm9uaXphdGlvblN0YXJ0ZWQoaW5zdGFuY2VJbmRleCwgc3BlY2lmaWNhdGlvbnNIYXNoLFxuICAgICAgICAgICAgICBwb3NpdGlvbnNIYXNoLCBvcmRlcnNIYXNoLCBkYXRhLnN5bmNocm9uaXphdGlvbklkKSxcbiAgICAgICAgICAgIGAke3ByaW1hcnlBY2NvdW50SWR9OiR7c291cmNlTGFiZWx9Om9uU3luY2hyb25pemF0aW9uU3RhcnRlZGApO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGRhdGEudHlwZSA9PT0gJ2FjY291bnRJbmZvcm1hdGlvbicpIHtcbiAgICAgICAgaWYgKGRhdGEuc3luY2hyb25pemF0aW9uSWQgJiYgZGF0YS5zeW5jaHJvbml6YXRpb25JZCAhPT0gdGhpcy5fc3luY2hyb25pemF0aW9uSWRCeUluc3RhbmNlW2luc3RhbmNlSWRdKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGlmIChkYXRhLmFjY291bnRJbmZvcm1hdGlvbikge1xuICAgICAgICAgIGZvciAobGV0IGxpc3RlbmVyIG9mIHRoaXMuX3N5bmNocm9uaXphdGlvbkxpc3RlbmVyc1twcmltYXJ5QWNjb3VudElkXSB8fCBbXSkge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgICAgICgpID0+IGxpc3RlbmVyLm9uQWNjb3VudEluZm9ybWF0aW9uVXBkYXRlZChpbnN0YW5jZUluZGV4LCBkYXRhLmFjY291bnRJbmZvcm1hdGlvbiksXG4gICAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25BY2NvdW50SW5mb3JtYXRpb25VcGRhdGVkYCwgdHJ1ZSk7XG4gICAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBtYXgtZGVwdGhcbiAgICAgICAgICAgICAgaWYgKHRoaXMuX3N5bmNocm9uaXphdGlvbkZsYWdzW2RhdGEuc3luY2hyb25pemF0aW9uSWRdICYmXG4gICAgICAgICAgICAgICAgIXRoaXMuX3N5bmNocm9uaXphdGlvbkZsYWdzW2RhdGEuc3luY2hyb25pemF0aW9uSWRdLnBvc2l0aW9uc1VwZGF0ZWQpIHtcbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLl9wcm9jZXNzRXZlbnQoXG4gICAgICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vblBvc2l0aW9uc1N5bmNocm9uaXplZChpbnN0YW5jZUluZGV4LCBkYXRhLnN5bmNocm9uaXphdGlvbklkKSxcbiAgICAgICAgICAgICAgICAgIGAke3ByaW1hcnlBY2NvdW50SWR9OiR7c291cmNlTGFiZWx9Om9uUG9zaXRpb25zU3luY2hyb25pemVkYCwgdHJ1ZSk7XG4gICAgICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG1heC1kZXB0aFxuICAgICAgICAgICAgICAgIGlmICghdGhpcy5fc3luY2hyb25pemF0aW9uRmxhZ3NbZGF0YS5zeW5jaHJvbml6YXRpb25JZF0ub3JkZXJzVXBkYXRlZCkge1xuICAgICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vblBlbmRpbmdPcmRlcnNTeW5jaHJvbml6ZWQoaW5zdGFuY2VJbmRleCwgZGF0YS5zeW5jaHJvbml6YXRpb25JZCksXG4gICAgICAgICAgICAgICAgICAgIGAke3ByaW1hcnlBY2NvdW50SWR9OiR7c291cmNlTGFiZWx9Om9uUGVuZGluZ09yZGVyc1N5bmNocm9uaXplZGAsIHRydWUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICAgIHRoaXMuX2xvZ2dlci5lcnJvcihgJHtwcmltYXJ5QWNjb3VudElkfToke3NvdXJjZUxhYmVsfTogRmFpbGVkIHRvIG5vdGlmeSBsaXN0ZW5lciBgICtcbiAgICAgICAgICAgICAgICAnYWJvdXQgYWNjb3VudEluZm9ybWF0aW9uIGV2ZW50JywgZXJyKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKHRoaXMuX3N5bmNocm9uaXphdGlvbkZsYWdzW2RhdGEuc3luY2hyb25pemF0aW9uSWRdICYmIFxuICAgICAgICAgICAgICAhdGhpcy5fc3luY2hyb25pemF0aW9uRmxhZ3NbZGF0YS5zeW5jaHJvbml6YXRpb25JZF0ucG9zaXRpb25zVXBkYXRlZCAmJiBcbiAgICAgICAgICAgICAgIXRoaXMuX3N5bmNocm9uaXphdGlvbkZsYWdzW2RhdGEuc3luY2hyb25pemF0aW9uSWRdLm9yZGVyc1VwZGF0ZWQpIHtcbiAgICAgICAgICAgIGRlbGV0ZSB0aGlzLl9zeW5jaHJvbml6YXRpb25GbGFnc1tkYXRhLnN5bmNocm9uaXphdGlvbklkXTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoZGF0YS50eXBlID09PSAnZGVhbHMnKSB7XG4gICAgICAgIGlmIChkYXRhLnN5bmNocm9uaXphdGlvbklkICYmIGRhdGEuc3luY2hyb25pemF0aW9uSWQgIT09IHRoaXMuX3N5bmNocm9uaXphdGlvbklkQnlJbnN0YW5jZVtpbnN0YW5jZUlkXSkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBmb3IgKGxldCBkZWFsIG9mIChkYXRhLmRlYWxzIHx8IFtdKSkge1xuICAgICAgICAgIGZvciAobGV0IGxpc3RlbmVyIG9mIHRoaXMuX3N5bmNocm9uaXphdGlvbkxpc3RlbmVyc1twcmltYXJ5QWNjb3VudElkXSB8fCBbXSkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vbkRlYWxBZGRlZChpbnN0YW5jZUluZGV4LCBkZWFsKSxcbiAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25EZWFsQWRkZWRgKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoZGF0YS50eXBlID09PSAnb3JkZXJzJykge1xuICAgICAgICBpZiAoZGF0YS5zeW5jaHJvbml6YXRpb25JZCAmJiBkYXRhLnN5bmNocm9uaXphdGlvbklkICE9PSB0aGlzLl9zeW5jaHJvbml6YXRpb25JZEJ5SW5zdGFuY2VbaW5zdGFuY2VJZF0pIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChsZXQgbGlzdGVuZXIgb2YgdGhpcy5fc3luY2hyb25pemF0aW9uTGlzdGVuZXJzW3ByaW1hcnlBY2NvdW50SWRdIHx8IFtdKSB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuX3Byb2Nlc3NFdmVudChcbiAgICAgICAgICAgICAgKCkgPT4gbGlzdGVuZXIub25QZW5kaW5nT3JkZXJzUmVwbGFjZWQoaW5zdGFuY2VJbmRleCwgZGF0YS5vcmRlcnMgfHwgW10pLFxuICAgICAgICAgICAgICBgJHtwcmltYXJ5QWNjb3VudElkfToke3NvdXJjZUxhYmVsfTpvblBlbmRpbmdPcmRlcnNSZXBsYWNlZGAsIHRydWUpO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vblBlbmRpbmdPcmRlcnNTeW5jaHJvbml6ZWQoaW5zdGFuY2VJbmRleCwgZGF0YS5zeW5jaHJvbml6YXRpb25JZCksXG4gICAgICAgICAgICAgIGAke3ByaW1hcnlBY2NvdW50SWR9OiR7c291cmNlTGFiZWx9Om9uUGVuZGluZ09yZGVyc1N5bmNocm9uaXplZGAsIHRydWUpO1xuICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgdGhpcy5fbG9nZ2VyLmVycm9yKGAke3ByaW1hcnlBY2NvdW50SWR9OiR7c291cmNlTGFiZWx9OiBGYWlsZWQgdG8gbm90aWZ5IGxpc3RlbmVyIGAgK1xuICAgICAgICAgICAgICAnYWJvdXQgb3JkZXJzIGV2ZW50JywgZXJyKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuX3N5bmNocm9uaXphdGlvbkZsYWdzW2RhdGEuc3luY2hyb25pemF0aW9uSWRdKSB7XG4gICAgICAgICAgZGVsZXRlIHRoaXMuX3N5bmNocm9uaXphdGlvbkZsYWdzW2RhdGEuc3luY2hyb25pemF0aW9uSWRdO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGRhdGEudHlwZSA9PT0gJ2hpc3RvcnlPcmRlcnMnKSB7XG4gICAgICAgIGlmIChkYXRhLnN5bmNocm9uaXphdGlvbklkICYmIGRhdGEuc3luY2hyb25pemF0aW9uSWQgIT09IHRoaXMuX3N5bmNocm9uaXphdGlvbklkQnlJbnN0YW5jZVtpbnN0YW5jZUlkXSkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBmb3IgKGxldCBoaXN0b3J5T3JkZXIgb2YgKGRhdGEuaGlzdG9yeU9yZGVycyB8fCBbXSkpIHtcbiAgICAgICAgICBmb3IgKGxldCBsaXN0ZW5lciBvZiB0aGlzLl9zeW5jaHJvbml6YXRpb25MaXN0ZW5lcnNbcHJpbWFyeUFjY291bnRJZF0gfHwgW10pIHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuX3Byb2Nlc3NFdmVudChcbiAgICAgICAgICAgICAgKCkgPT4gbGlzdGVuZXIub25IaXN0b3J5T3JkZXJBZGRlZChpbnN0YW5jZUluZGV4LCBoaXN0b3J5T3JkZXIpLFxuICAgICAgICAgICAgICBgJHtwcmltYXJ5QWNjb3VudElkfToke3NvdXJjZUxhYmVsfTpvbkhpc3RvcnlPcmRlckFkZGVkYCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGRhdGEudHlwZSA9PT0gJ3Bvc2l0aW9ucycpIHtcbiAgICAgICAgaWYgKGRhdGEuc3luY2hyb25pemF0aW9uSWQgJiYgZGF0YS5zeW5jaHJvbml6YXRpb25JZCAhPT0gdGhpcy5fc3luY2hyb25pemF0aW9uSWRCeUluc3RhbmNlW2luc3RhbmNlSWRdKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGZvciAobGV0IGxpc3RlbmVyIG9mIHRoaXMuX3N5bmNocm9uaXphdGlvbkxpc3RlbmVyc1twcmltYXJ5QWNjb3VudElkXSB8fCBbXSkge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLl9wcm9jZXNzRXZlbnQoXG4gICAgICAgICAgICAgICgpID0+IGxpc3RlbmVyLm9uUG9zaXRpb25zUmVwbGFjZWQoaW5zdGFuY2VJbmRleCwgZGF0YS5wb3NpdGlvbnMgfHwgW10pLFxuICAgICAgICAgICAgICBgJHtwcmltYXJ5QWNjb3VudElkfToke3NvdXJjZUxhYmVsfTpvblBvc2l0aW9uc1JlcGxhY2VkYCwgdHJ1ZSk7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLl9wcm9jZXNzRXZlbnQoXG4gICAgICAgICAgICAgICgpID0+IGxpc3RlbmVyLm9uUG9zaXRpb25zU3luY2hyb25pemVkKGluc3RhbmNlSW5kZXgsIGRhdGEuc3luY2hyb25pemF0aW9uSWQpLFxuICAgICAgICAgICAgICBgJHtwcmltYXJ5QWNjb3VudElkfToke3NvdXJjZUxhYmVsfTpvblBvc2l0aW9uc1N5bmNocm9uaXplZGAsIHRydWUpO1xuICAgICAgICAgICAgaWYgKHRoaXMuX3N5bmNocm9uaXphdGlvbkZsYWdzW2RhdGEuc3luY2hyb25pemF0aW9uSWRdICYmXG4gICAgICAgICAgICAgICF0aGlzLl9zeW5jaHJvbml6YXRpb25GbGFnc1tkYXRhLnN5bmNocm9uaXphdGlvbklkXS5vcmRlcnNVcGRhdGVkKSB7XG4gICAgICAgICAgICAgIGF3YWl0IHRoaXMuX3Byb2Nlc3NFdmVudChcbiAgICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vblBlbmRpbmdPcmRlcnNTeW5jaHJvbml6ZWQoaW5zdGFuY2VJbmRleCwgZGF0YS5zeW5jaHJvbml6YXRpb25JZCksXG4gICAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25QZW5kaW5nT3JkZXJzU3luY2hyb25pemVkYCwgdHJ1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICB0aGlzLl9sb2dnZXIuZXJyb3IoYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06IEZhaWxlZCB0byBub3RpZnkgbGlzdGVuZXIgYCArXG4gICAgICAgICAgICAgICdhYm91dCBwb3NpdGlvbnMgZXZlbnQnLCBlcnIpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5fc3luY2hyb25pemF0aW9uRmxhZ3NbZGF0YS5zeW5jaHJvbml6YXRpb25JZF0gJiYgXG4gICAgICAgICAgIXRoaXMuX3N5bmNocm9uaXphdGlvbkZsYWdzW2RhdGEuc3luY2hyb25pemF0aW9uSWRdLm9yZGVyc1VwZGF0ZWQpIHtcbiAgICAgICAgICBkZWxldGUgdGhpcy5fc3luY2hyb25pemF0aW9uRmxhZ3NbZGF0YS5zeW5jaHJvbml6YXRpb25JZF07XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoZGF0YS50eXBlID09PSAndXBkYXRlJykge1xuICAgICAgICB0aGlzLl91cGRhdGVFdmVudHNbaW5zdGFuY2VJZF0/LnB1c2goe3NvY2tldCwgZGF0YX0pO1xuICAgICAgICBpZiAoZGF0YS5hY2NvdW50SW5mb3JtYXRpb24pIHtcbiAgICAgICAgICBmb3IgKGxldCBsaXN0ZW5lciBvZiB0aGlzLl9zeW5jaHJvbml6YXRpb25MaXN0ZW5lcnNbcHJpbWFyeUFjY291bnRJZF0gfHwgW10pIHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuX3Byb2Nlc3NFdmVudChcbiAgICAgICAgICAgICAgKCkgPT4gbGlzdGVuZXIub25BY2NvdW50SW5mb3JtYXRpb25VcGRhdGVkKGluc3RhbmNlSW5kZXgsIGRhdGEuYWNjb3VudEluZm9ybWF0aW9uKSxcbiAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25BY2NvdW50SW5mb3JtYXRpb25VcGRhdGVkYCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHVwZGF0ZWRQb3NpdGlvbnMgPSBkYXRhLnVwZGF0ZWRQb3NpdGlvbnMgfHwgW107XG4gICAgICAgIGNvbnN0IHJlbW92ZWRQb3NpdGlvbklkcyA9IGRhdGEucmVtb3ZlZFBvc2l0aW9uSWRzIHx8IFtdO1xuICAgICAgICBpZiAodXBkYXRlZFBvc2l0aW9ucy5sZW5ndGggfHwgcmVtb3ZlZFBvc2l0aW9uSWRzLmxlbmd0aCkge1xuICAgICAgICAgIGZvciAobGV0IGxpc3RlbmVyIG9mIHRoaXMuX3N5bmNocm9uaXphdGlvbkxpc3RlbmVyc1twcmltYXJ5QWNjb3VudElkXSB8fCBbXSkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vblBvc2l0aW9uc1VwZGF0ZWQoaW5zdGFuY2VJbmRleCwgdXBkYXRlZFBvc2l0aW9ucywgcmVtb3ZlZFBvc2l0aW9uSWRzKSxcbiAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25Qb3NpdGlvbnNVcGRhdGVkYCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGZvciAobGV0IHBvc2l0aW9uIG9mIHVwZGF0ZWRQb3NpdGlvbnMpIHtcbiAgICAgICAgICBmb3IgKGxldCBsaXN0ZW5lciBvZiB0aGlzLl9zeW5jaHJvbml6YXRpb25MaXN0ZW5lcnNbcHJpbWFyeUFjY291bnRJZF0gfHwgW10pIHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuX3Byb2Nlc3NFdmVudChcbiAgICAgICAgICAgICAgKCkgPT4gbGlzdGVuZXIub25Qb3NpdGlvblVwZGF0ZWQoaW5zdGFuY2VJbmRleCwgcG9zaXRpb24pLFxuICAgICAgICAgICAgICBgJHtwcmltYXJ5QWNjb3VudElkfToke3NvdXJjZUxhYmVsfTpvblBvc2l0aW9uVXBkYXRlZGApO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBmb3IgKGxldCBwb3NpdGlvbklkIG9mIHJlbW92ZWRQb3NpdGlvbklkcykge1xuICAgICAgICAgIGZvciAobGV0IGxpc3RlbmVyIG9mIHRoaXMuX3N5bmNocm9uaXphdGlvbkxpc3RlbmVyc1twcmltYXJ5QWNjb3VudElkXSB8fCBbXSkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vblBvc2l0aW9uUmVtb3ZlZChpbnN0YW5jZUluZGV4LCBwb3NpdGlvbklkKSxcbiAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25Qb3NpdGlvblJlbW92ZWRgKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgdXBkYXRlZE9yZGVycyA9IGRhdGEudXBkYXRlZE9yZGVycyB8fCBbXTtcbiAgICAgICAgY29uc3QgY29tcGxldGVkT3JkZXJJZHMgPSBkYXRhLmNvbXBsZXRlZE9yZGVySWRzIHx8IFtdO1xuICAgICAgICBpZiAodXBkYXRlZE9yZGVycy5sZW5ndGggfHwgY29tcGxldGVkT3JkZXJJZHMubGVuZ3RoKSB7XG4gICAgICAgICAgZm9yIChsZXQgbGlzdGVuZXIgb2YgdGhpcy5fc3luY2hyb25pemF0aW9uTGlzdGVuZXJzW3ByaW1hcnlBY2NvdW50SWRdIHx8IFtdKSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLl9wcm9jZXNzRXZlbnQoXG4gICAgICAgICAgICAgICgpID0+IGxpc3RlbmVyLm9uUGVuZGluZ09yZGVyc1VwZGF0ZWQoaW5zdGFuY2VJbmRleCwgdXBkYXRlZE9yZGVycywgY29tcGxldGVkT3JkZXJJZHMpLFxuICAgICAgICAgICAgICBgJHtwcmltYXJ5QWNjb3VudElkfToke3NvdXJjZUxhYmVsfTpvblBlbmRpbmdPcmRlcnNVcGRhdGVkYCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGZvciAobGV0IG9yZGVyIG9mIHVwZGF0ZWRPcmRlcnMpIHtcbiAgICAgICAgICBmb3IgKGxldCBsaXN0ZW5lciBvZiB0aGlzLl9zeW5jaHJvbml6YXRpb25MaXN0ZW5lcnNbcHJpbWFyeUFjY291bnRJZF0gfHwgW10pIHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuX3Byb2Nlc3NFdmVudChcbiAgICAgICAgICAgICAgKCkgPT4gbGlzdGVuZXIub25QZW5kaW5nT3JkZXJVcGRhdGVkKGluc3RhbmNlSW5kZXgsIG9yZGVyKSxcbiAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25QZW5kaW5nT3JkZXJVcGRhdGVkYCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGZvciAobGV0IG9yZGVySWQgb2YgY29tcGxldGVkT3JkZXJJZHMpIHtcbiAgICAgICAgICBmb3IgKGxldCBsaXN0ZW5lciBvZiB0aGlzLl9zeW5jaHJvbml6YXRpb25MaXN0ZW5lcnNbcHJpbWFyeUFjY291bnRJZF0gfHwgW10pIHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuX3Byb2Nlc3NFdmVudChcbiAgICAgICAgICAgICAgKCkgPT4gbGlzdGVuZXIub25QZW5kaW5nT3JkZXJDb21wbGV0ZWQoaW5zdGFuY2VJbmRleCwgb3JkZXJJZCksXG4gICAgICAgICAgICAgIGAke3ByaW1hcnlBY2NvdW50SWR9OiR7c291cmNlTGFiZWx9Om9uUGVuZGluZ09yZGVyQ29tcGxldGVkYCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGZvciAobGV0IGhpc3RvcnlPcmRlciBvZiAoZGF0YS5oaXN0b3J5T3JkZXJzIHx8IFtdKSkge1xuICAgICAgICAgIGZvciAobGV0IGxpc3RlbmVyIG9mIHRoaXMuX3N5bmNocm9uaXphdGlvbkxpc3RlbmVyc1twcmltYXJ5QWNjb3VudElkXSB8fCBbXSkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vbkhpc3RvcnlPcmRlckFkZGVkKGluc3RhbmNlSW5kZXgsIGhpc3RvcnlPcmRlciksXG4gICAgICAgICAgICAgIGAke3ByaW1hcnlBY2NvdW50SWR9OiR7c291cmNlTGFiZWx9Om9uSGlzdG9yeU9yZGVyQWRkZWRgKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChsZXQgZGVhbCBvZiAoZGF0YS5kZWFscyB8fCBbXSkpIHtcbiAgICAgICAgICBmb3IgKGxldCBsaXN0ZW5lciBvZiB0aGlzLl9zeW5jaHJvbml6YXRpb25MaXN0ZW5lcnNbcHJpbWFyeUFjY291bnRJZF0gfHwgW10pIHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuX3Byb2Nlc3NFdmVudChcbiAgICAgICAgICAgICAgKCkgPT4gbGlzdGVuZXIub25EZWFsQWRkZWQoaW5zdGFuY2VJbmRleCwgZGVhbCksXG4gICAgICAgICAgICAgIGAke3ByaW1hcnlBY2NvdW50SWR9OiR7c291cmNlTGFiZWx9Om9uRGVhbEFkZGVkYCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmIChkYXRhLnRpbWVzdGFtcHMpIHtcbiAgICAgICAgICBkYXRhLnRpbWVzdGFtcHMuY2xpZW50UHJvY2Vzc2luZ0ZpbmlzaGVkID0gbmV3IERhdGUoKTtcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbWF4LWRlcHRoXG4gICAgICAgICAgZm9yIChsZXQgbGlzdGVuZXIgb2YgdGhpcy5fbGF0ZW5jeUxpc3RlbmVycyB8fCBbXSkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vblVwZGF0ZShkYXRhLmFjY291bnRJZCwgZGF0YS50aW1lc3RhbXBzKSxcbiAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25VcGRhdGVgKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoZGF0YS50eXBlID09PSAnZGVhbFN5bmNocm9uaXphdGlvbkZpbmlzaGVkJykge1xuICAgICAgICBpZiAoZGF0YS5zeW5jaHJvbml6YXRpb25JZCAmJiBkYXRhLnN5bmNocm9uaXphdGlvbklkICE9PSB0aGlzLl9zeW5jaHJvbml6YXRpb25JZEJ5SW5zdGFuY2VbaW5zdGFuY2VJZF0pIHtcbiAgICAgICAgICBkZWxldGUgdGhpcy5fc3luY2hyb25pemF0aW9uSWRCeUluc3RhbmNlW2luc3RhbmNlSWRdO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLl9sYXRlbmN5U2VydmljZS5vbkRlYWxzU3luY2hyb25pemVkKGluc3RhbmNlSWQpO1xuICAgICAgICBmb3IgKGxldCBsaXN0ZW5lciBvZiB0aGlzLl9zeW5jaHJvbml6YXRpb25MaXN0ZW5lcnNbcHJpbWFyeUFjY291bnRJZF0gfHwgW10pIHtcbiAgICAgICAgICBzb2NrZXRJbnN0YW5jZT8uc3luY2hyb25pemF0aW9uVGhyb3R0bGVyLnJlbW92ZVN5bmNocm9uaXphdGlvbklkKGRhdGEuc3luY2hyb25pemF0aW9uSWQpO1xuICAgICAgICAgIGF3YWl0IHRoaXMuX3Byb2Nlc3NFdmVudChcbiAgICAgICAgICAgICgpID0+IGxpc3RlbmVyLm9uRGVhbHNTeW5jaHJvbml6ZWQoaW5zdGFuY2VJbmRleCwgZGF0YS5zeW5jaHJvbml6YXRpb25JZCksXG4gICAgICAgICAgICBgJHtwcmltYXJ5QWNjb3VudElkfToke3NvdXJjZUxhYmVsfTpvbkRlYWxzU3luY2hyb25pemVkYCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuX3VwZGF0ZUV2ZW50c1tpbnN0YW5jZUlkXSkge1xuICAgICAgICAgIGxldCBldmVudENhbGxzID0gdGhpcy5fdXBkYXRlRXZlbnRzW2luc3RhbmNlSWRdLm1hcChldmVudCA9PiAoXG4gICAgICAgICAgICAoKSA9PiB0aGlzLl9wcm9jZXNzU3luY2hyb25pemF0aW9uUGFja2V0KGV2ZW50LnNvY2tldCwgZXZlbnQuZGF0YSlcbiAgICAgICAgICApKTtcbiAgICAgICAgICBpZiAodGhpcy5fZXZlbnRRdWV1ZXNbcHJpbWFyeUFjY291bnRJZF0pIHtcbiAgICAgICAgICAgIHRoaXMuX2V2ZW50UXVldWVzW3ByaW1hcnlBY2NvdW50SWRdID0gZXZlbnRDYWxscy5jb25jYXQodGhpcy5fZXZlbnRRdWV1ZXNbcHJpbWFyeUFjY291bnRJZF0pO1xuICAgICAgICAgICAgZGVsZXRlIHRoaXMuX3VwZGF0ZUV2ZW50c1tpbnN0YW5jZUlkXTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5fZXZlbnRRdWV1ZXNbcHJpbWFyeUFjY291bnRJZF0gPSBldmVudENhbGxzO1xuICAgICAgICAgICAgZGVsZXRlIHRoaXMuX3VwZGF0ZUV2ZW50c1tpbnN0YW5jZUlkXTtcbiAgICAgICAgICAgIHRoaXMuX2NhbGxBY2NvdW50RXZlbnRzKHByaW1hcnlBY2NvdW50SWQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChkYXRhLnR5cGUgPT09ICdvcmRlclN5bmNocm9uaXphdGlvbkZpbmlzaGVkJykge1xuICAgICAgICBpZiAoZGF0YS5zeW5jaHJvbml6YXRpb25JZCAmJiBkYXRhLnN5bmNocm9uaXphdGlvbklkICE9PSB0aGlzLl9zeW5jaHJvbml6YXRpb25JZEJ5SW5zdGFuY2VbaW5zdGFuY2VJZF0pIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChsZXQgbGlzdGVuZXIgb2YgdGhpcy5fc3luY2hyb25pemF0aW9uTGlzdGVuZXJzW3ByaW1hcnlBY2NvdW50SWRdIHx8IFtdKSB7XG4gICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgKCkgPT4gbGlzdGVuZXIub25IaXN0b3J5T3JkZXJzU3luY2hyb25pemVkKGluc3RhbmNlSW5kZXgsIGRhdGEuc3luY2hyb25pemF0aW9uSWQpLFxuICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25IaXN0b3J5T3JkZXJzU3luY2hyb25pemVkYCk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoZGF0YS50eXBlID09PSAnc3RhdHVzJykge1xuICAgICAgICBpZiAoIXRoaXMuX2Nvbm5lY3RlZEhvc3RzW2luc3RhbmNlSWRdKSB7XG4gICAgICAgICAgaWYgKHRoaXMuX3N0YXR1c1RpbWVyc1tpbnN0YW5jZUlkXSAmJiBkYXRhLmF1dGhlbnRpY2F0ZWQgJiYgXG4gICAgICAgICAgICAgICh0aGlzLl9zdWJzY3JpcHRpb25NYW5hZ2VyLmlzRGlzY29ubmVjdGVkUmV0cnlNb2RlKGRhdGEuYWNjb3VudElkLCBpbnN0YW5jZU51bWJlcikgfHwgXG4gICAgICAgICAgICAgICF0aGlzLl9zdWJzY3JpcHRpb25NYW5hZ2VyLmlzQWNjb3VudFN1YnNjcmliaW5nKGRhdGEuYWNjb3VudElkLCBpbnN0YW5jZU51bWJlcikpKSB7XG4gICAgICAgICAgICB0aGlzLl9zdWJzY3JpcHRpb25NYW5hZ2VyLmNhbmNlbFN1YnNjcmliZShkYXRhLmFjY291bnRJZCArICc6JyArIGluc3RhbmNlTnVtYmVyKTtcbiAgICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKHJlcyA9PiBzZXRUaW1lb3V0KHJlcywgMTApKTtcbiAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgICAgICAgICB0aGlzLl9sb2dnZXIuaW5mbyhcbiAgICAgICAgICAgICAgJ2l0IHNlZW1zIGxpa2Ugd2UgYXJlIG5vdCBjb25uZWN0ZWQgdG8gYSBydW5uaW5nIEFQSSBzZXJ2ZXIgeWV0LCByZXRyeWluZyBzdWJzY3JpcHRpb24gZm9yIGFjY291bnQnICtcbiAgICAgICAgICAgICAgXy5jb21wYWN0KFtpbnN0YW5jZUlkLCBzb2NrZXQuY2xpZW50QXBpSG9zdG5hbWVdKS5qb2luKCc6JylcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICB0aGlzLmVuc3VyZVN1YnNjcmliZShkYXRhLmFjY291bnRJZCwgaW5zdGFuY2VOdW1iZXIpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXNldERpc2Nvbm5lY3RUaW1lcigpO1xuICAgICAgICAgIGZvciAobGV0IGxpc3RlbmVyIG9mIHRoaXMuX3N5bmNocm9uaXphdGlvbkxpc3RlbmVyc1twcmltYXJ5QWNjb3VudElkXSB8fCBbXSkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vbkJyb2tlckNvbm5lY3Rpb25TdGF0dXNDaGFuZ2VkKGluc3RhbmNlSW5kZXgsICEhZGF0YS5jb25uZWN0ZWQpLFxuICAgICAgICAgICAgICBgJHtwcmltYXJ5QWNjb3VudElkfToke3NvdXJjZUxhYmVsfTpvbkJyb2tlckNvbm5lY3Rpb25TdGF0dXNDaGFuZ2VkYCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChkYXRhLmhlYWx0aFN0YXR1cykge1xuICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG1heC1kZXB0aFxuICAgICAgICAgICAgZm9yIChsZXQgbGlzdGVuZXIgb2YgdGhpcy5fc3luY2hyb25pemF0aW9uTGlzdGVuZXJzW3ByaW1hcnlBY2NvdW50SWRdIHx8IFtdKSB7XG4gICAgICAgICAgICAgIGF3YWl0IHRoaXMuX3Byb2Nlc3NFdmVudChcbiAgICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vbkhlYWx0aFN0YXR1cyhpbnN0YW5jZUluZGV4LCBkYXRhLmhlYWx0aFN0YXR1cyksXG4gICAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25IZWFsdGhTdGF0dXNgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoZGF0YS50eXBlID09PSAnZG93bmdyYWRlU3Vic2NyaXB0aW9uJykge1xuICAgICAgICB0aGlzLl9sb2dnZXIuaW5mbyhgJHtwcmltYXJ5QWNjb3VudElkfToke3NvdXJjZUxhYmVsfTogTWFya2V0IGRhdGEgc3Vic2NyaXB0aW9ucyBmb3Igc3ltYm9sIGAgK1xuICAgICAgICAgIGAke2RhdGEuc3ltYm9sfSB3ZXJlIGRvd25ncmFkZWQgYnkgdGhlIHNlcnZlciBkdWUgdG8gcmF0ZSBsaW1pdHMuIFVwZGF0ZWQgc3Vic2NyaXB0aW9uczogYCArXG4gICAgICAgICAgYCR7SlNPTi5zdHJpbmdpZnkoZGF0YS51cGRhdGVzKX0sIHJlbW92ZWQgc3Vic2NyaXB0aW9uczogJHtKU09OLnN0cmluZ2lmeShkYXRhLnVuc3Vic2NyaXB0aW9ucyl9LiBgICtcbiAgICAgICAgICAnUGxlYXNlIHJlYWQgaHR0cHM6Ly9tZXRhYXBpLmNsb3VkL2RvY3MvY2xpZW50L3JhdGVMaW1pdGluZy8gZm9yIG1vcmUgZGV0YWlscy4nKTtcbiAgICAgICAgZm9yIChsZXQgbGlzdGVuZXIgb2YgdGhpcy5fc3luY2hyb25pemF0aW9uTGlzdGVuZXJzW3ByaW1hcnlBY2NvdW50SWRdIHx8IFtdKSB7XG4gICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgKCkgPT4gbGlzdGVuZXIub25TdWJzY3JpcHRpb25Eb3duZ3JhZGVkKGluc3RhbmNlSW5kZXgsIGRhdGEuc3ltYm9sLCBkYXRhLnVwZGF0ZXMsIGRhdGEudW5zdWJzY3JpcHRpb25zKSxcbiAgICAgICAgICAgIGAke3ByaW1hcnlBY2NvdW50SWR9OiR7c291cmNlTGFiZWx9Om9uU3Vic2NyaXB0aW9uRG93bmdyYWRlZGApO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGRhdGEudHlwZSA9PT0gJ3NwZWNpZmljYXRpb25zJykge1xuICAgICAgICBpZiAoZGF0YS5zeW5jaHJvbml6YXRpb25JZCAmJiBkYXRhLnN5bmNocm9uaXphdGlvbklkICE9PSB0aGlzLl9zeW5jaHJvbml6YXRpb25JZEJ5SW5zdGFuY2VbaW5zdGFuY2VJZF0pIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChsZXQgbGlzdGVuZXIgb2YgdGhpcy5fc3luY2hyb25pemF0aW9uTGlzdGVuZXJzW3ByaW1hcnlBY2NvdW50SWRdIHx8IFtdKSB7XG4gICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgKCkgPT4gbGlzdGVuZXIub25TeW1ib2xTcGVjaWZpY2F0aW9uc1VwZGF0ZWQoaW5zdGFuY2VJbmRleCwgZGF0YS5zcGVjaWZpY2F0aW9ucyB8fCBbXSxcbiAgICAgICAgICAgICAgZGF0YS5yZW1vdmVkU3ltYm9scyB8fCBbXSksIGAke3ByaW1hcnlBY2NvdW50SWR9OiR7c291cmNlTGFiZWx9Om9uU3ltYm9sU3BlY2lmaWNhdGlvbnNVcGRhdGVkYCk7XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChsZXQgc3BlY2lmaWNhdGlvbiBvZiAoZGF0YS5zcGVjaWZpY2F0aW9ucyB8fCBbXSkpIHtcbiAgICAgICAgICBmb3IgKGxldCBsaXN0ZW5lciBvZiB0aGlzLl9zeW5jaHJvbml6YXRpb25MaXN0ZW5lcnNbcHJpbWFyeUFjY291bnRJZF0gfHwgW10pIHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuX3Byb2Nlc3NFdmVudChcbiAgICAgICAgICAgICAgKCkgPT4gbGlzdGVuZXIub25TeW1ib2xTcGVjaWZpY2F0aW9uVXBkYXRlZChpbnN0YW5jZUluZGV4LCBzcGVjaWZpY2F0aW9uKSxcbiAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25TeW1ib2xTcGVjaWZpY2F0aW9uVXBkYXRlZGApO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBmb3IgKGxldCByZW1vdmVkU3ltYm9sIG9mIChkYXRhLnJlbW92ZWRTeW1ib2xzIHx8IFtdKSkge1xuICAgICAgICAgIGZvciAobGV0IGxpc3RlbmVyIG9mIHRoaXMuX3N5bmNocm9uaXphdGlvbkxpc3RlbmVyc1twcmltYXJ5QWNjb3VudElkXSB8fCBbXSkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vblN5bWJvbFNwZWNpZmljYXRpb25SZW1vdmVkKGluc3RhbmNlSW5kZXgsIHJlbW92ZWRTeW1ib2wpLFxuICAgICAgICAgICAgICBgJHtwcmltYXJ5QWNjb3VudElkfToke3NvdXJjZUxhYmVsfTpvblN5bWJvbFNwZWNpZmljYXRpb25SZW1vdmVkYCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGRhdGEudHlwZSA9PT0gJ3ByaWNlcycpIHtcbiAgICAgICAgaWYgKGRhdGEuc3luY2hyb25pemF0aW9uSWQgJiYgZGF0YS5zeW5jaHJvbml6YXRpb25JZCAhPT0gdGhpcy5fc3luY2hyb25pemF0aW9uSWRCeUluc3RhbmNlW2luc3RhbmNlSWRdKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGxldCBwcmljZXMgPSBkYXRhLnByaWNlcyB8fCBbXTtcbiAgICAgICAgbGV0IGNhbmRsZXMgPSBkYXRhLmNhbmRsZXMgfHwgW107XG4gICAgICAgIGxldCB0aWNrcyA9IGRhdGEudGlja3MgfHwgW107XG4gICAgICAgIGxldCBib29rcyA9IGRhdGEuYm9va3MgfHwgW107XG4gICAgICAgIGZvciAobGV0IGxpc3RlbmVyIG9mIHRoaXMuX3N5bmNocm9uaXphdGlvbkxpc3RlbmVyc1twcmltYXJ5QWNjb3VudElkXSB8fCBbXSkge1xuICAgICAgICAgIGlmIChwcmljZXMubGVuZ3RoKSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLl9wcm9jZXNzRXZlbnQoXG4gICAgICAgICAgICAgICgpID0+IGxpc3RlbmVyLm9uU3ltYm9sUHJpY2VzVXBkYXRlZChpbnN0YW5jZUluZGV4LCBwcmljZXMsIGRhdGEuZXF1aXR5LCBkYXRhLm1hcmdpbiwgZGF0YS5mcmVlTWFyZ2luLFxuICAgICAgICAgICAgICAgIGRhdGEubWFyZ2luTGV2ZWwsIGRhdGEuYWNjb3VudEN1cnJlbmN5RXhjaGFuZ2VSYXRlKSxcbiAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25TeW1ib2xQcmljZXNVcGRhdGVkYCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChjYW5kbGVzLmxlbmd0aCkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgICAoKSA9PiBsaXN0ZW5lci5vbkNhbmRsZXNVcGRhdGVkKGluc3RhbmNlSW5kZXgsIGNhbmRsZXMsIGRhdGEuZXF1aXR5LCBkYXRhLm1hcmdpbiwgZGF0YS5mcmVlTWFyZ2luLFxuICAgICAgICAgICAgICAgIGRhdGEubWFyZ2luTGV2ZWwsIGRhdGEuYWNjb3VudEN1cnJlbmN5RXhjaGFuZ2VSYXRlKSxcbiAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25DYW5kbGVzVXBkYXRlZGApO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAodGlja3MubGVuZ3RoKSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLl9wcm9jZXNzRXZlbnQoXG4gICAgICAgICAgICAgICgpID0+IGxpc3RlbmVyLm9uVGlja3NVcGRhdGVkKGluc3RhbmNlSW5kZXgsIHRpY2tzLCBkYXRhLmVxdWl0eSwgZGF0YS5tYXJnaW4sIGRhdGEuZnJlZU1hcmdpbixcbiAgICAgICAgICAgICAgICBkYXRhLm1hcmdpbkxldmVsLCBkYXRhLmFjY291bnRDdXJyZW5jeUV4Y2hhbmdlUmF0ZSksXG4gICAgICAgICAgICAgIGAke3ByaW1hcnlBY2NvdW50SWR9OiR7c291cmNlTGFiZWx9Om9uVGlja3NVcGRhdGVkYCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChib29rcy5sZW5ndGgpIHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuX3Byb2Nlc3NFdmVudChcbiAgICAgICAgICAgICAgKCkgPT4gbGlzdGVuZXIub25Cb29rc1VwZGF0ZWQoaW5zdGFuY2VJbmRleCwgYm9va3MsIGRhdGEuZXF1aXR5LCBkYXRhLm1hcmdpbiwgZGF0YS5mcmVlTWFyZ2luLFxuICAgICAgICAgICAgICAgIGRhdGEubWFyZ2luTGV2ZWwsIGRhdGEuYWNjb3VudEN1cnJlbmN5RXhjaGFuZ2VSYXRlKSxcbiAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25Cb29rc1VwZGF0ZWRgKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChsZXQgcHJpY2Ugb2YgcHJpY2VzKSB7XG4gICAgICAgICAgZm9yIChsZXQgbGlzdGVuZXIgb2YgdGhpcy5fc3luY2hyb25pemF0aW9uTGlzdGVuZXJzW3ByaW1hcnlBY2NvdW50SWRdIHx8IFtdKSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLl9wcm9jZXNzRXZlbnQoXG4gICAgICAgICAgICAgICgpID0+IGxpc3RlbmVyLm9uU3ltYm9sUHJpY2VVcGRhdGVkKGluc3RhbmNlSW5kZXgsIHByaWNlKSxcbiAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25TeW1ib2xQcmljZVVwZGF0ZWRgKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChsZXQgcHJpY2Ugb2YgcHJpY2VzKSB7XG4gICAgICAgICAgaWYgKHByaWNlLnRpbWVzdGFtcHMpIHtcbiAgICAgICAgICAgIHByaWNlLnRpbWVzdGFtcHMuY2xpZW50UHJvY2Vzc2luZ0ZpbmlzaGVkID0gbmV3IERhdGUoKTtcbiAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBtYXgtZGVwdGhcbiAgICAgICAgICAgIGZvciAobGV0IGxpc3RlbmVyIG9mIHRoaXMuX2xhdGVuY3lMaXN0ZW5lcnMgfHwgW10pIHtcbiAgICAgICAgICAgICAgYXdhaXQgdGhpcy5fcHJvY2Vzc0V2ZW50KFxuICAgICAgICAgICAgICAgICgpID0+IGxpc3RlbmVyLm9uU3ltYm9sUHJpY2UoZGF0YS5hY2NvdW50SWQsIHByaWNlLnN5bWJvbCwgcHJpY2UudGltZXN0YW1wcyksXG4gICAgICAgICAgICAgICAgYCR7cHJpbWFyeUFjY291bnRJZH06JHtzb3VyY2VMYWJlbH06b25TeW1ib2xQcmljZWApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgdGhpcy5fbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gcHJvY2VzcyBpbmNvbWluZyBzeW5jaHJvbml6YXRpb24gcGFja2V0JywgZXJyKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBfcHJvY2Vzc0V2ZW50KGNhbGxhYmxlLCBsYWJlbCwgdGhyb3dFcnJvcj8pIHtcbiAgICBjb25zdCBzdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICAgIGxldCBpc0xvbmdFdmVudCA9IGZhbHNlO1xuICAgIGxldCBpc0V2ZW50RG9uZSA9IGZhbHNlO1xuXG4gICAgY29uc3QgY2hlY2tMb25nRXZlbnQgPSBhc3luYyAoKSA9PiB7XG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXMgPT4gc2V0VGltZW91dChyZXMsIDEwMDApKTtcbiAgICAgIGlmICghaXNFdmVudERvbmUpIHtcbiAgICAgICAgaXNMb25nRXZlbnQgPSB0cnVlO1xuICAgICAgICB0aGlzLl9sb2dnZXIud2FybihgJHtsYWJlbH06IGV2ZW50IGlzIHRha2luZyBtb3JlIHRoYW4gMSBzZWNvbmQgdG8gcHJvY2Vzc2ApO1xuICAgICAgfVxuICAgIH07XG5cbiAgICBjaGVja0xvbmdFdmVudCgpO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBjYWxsYWJsZSgpO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgaWYgKHRocm93RXJyb3IpIHtcbiAgICAgICAgdGhyb3cgZXJyO1xuICAgICAgfVxuICAgICAgdGhpcy5fbG9nZ2VyLmVycm9yKGAke2xhYmVsfTogZXZlbnQgZmFpbGVkIHdpdGggZXJyb3JgLCBlcnIpO1xuICAgIH1cbiAgICBpc0V2ZW50RG9uZSA9IHRydWU7XG4gICAgaWYgKGlzTG9uZ0V2ZW50KSB7XG4gICAgICB0aGlzLl9sb2dnZXIud2FybihgJHtsYWJlbH06IGZpbmlzaGVkIGluICR7TWF0aC5mbG9vcigoRGF0ZS5ub3coKSAtIHN0YXJ0VGltZSkgLyAxMDAwKX0gc2Vjb25kc2ApO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIF9maXJlUmVjb25uZWN0ZWQoaW5zdGFuY2VOdW1iZXIsIHNvY2tldEluc3RhbmNlSW5kZXgsIHJlZ2lvbikge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZWNvbm5lY3RMaXN0ZW5lcnMgPSBbXTtcbiAgICAgIGZvciAobGV0IGxpc3RlbmVyIG9mIHRoaXMuX3JlY29ubmVjdExpc3RlbmVycykge1xuICAgICAgICBpZiAodGhpcy5fc29ja2V0SW5zdGFuY2VzQnlBY2NvdW50c1tpbnN0YW5jZU51bWJlcl1bbGlzdGVuZXIuYWNjb3VudElkXSA9PT0gc29ja2V0SW5zdGFuY2VJbmRleCAmJiBcbiAgICAgICAgICB0aGlzLmdldEFjY291bnRSZWdpb24obGlzdGVuZXIuYWNjb3VudElkKSA9PT0gcmVnaW9uKSB7XG4gICAgICAgICAgcmVjb25uZWN0TGlzdGVuZXJzLnB1c2gobGlzdGVuZXIpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBPYmplY3Qua2V5cyh0aGlzLl9zeW5jaHJvbml6YXRpb25GbGFncykuZm9yRWFjaChzeW5jaHJvbml6YXRpb25JZCA9PiB7XG4gICAgICAgIGNvbnN0IGFjY291bnRJZCA9IHRoaXMuX3N5bmNocm9uaXphdGlvbkZsYWdzW3N5bmNocm9uaXphdGlvbklkXS5hY2NvdW50SWQ7XG4gICAgICAgIGlmICh0aGlzLl9zb2NrZXRJbnN0YW5jZXNCeUFjY291bnRzW2luc3RhbmNlTnVtYmVyXVthY2NvdW50SWRdID09PSBzb2NrZXRJbnN0YW5jZUluZGV4XG4gICAgICAgICAgICAmJiB0aGlzLl9zeW5jaHJvbml6YXRpb25GbGFnc1tzeW5jaHJvbml6YXRpb25JZF0uaW5zdGFuY2VOdW1iZXIgPT09IGluc3RhbmNlTnVtYmVyXG4gICAgICAgICAgICAmJiB0aGlzLl9yZWdpb25zQnlBY2NvdW50c1thY2NvdW50SWRdXG4gICAgICAgICAgICAmJiB0aGlzLl9yZWdpb25zQnlBY2NvdW50c1thY2NvdW50SWRdLnJlZ2lvbiA9PT0gcmVnaW9uKSB7XG4gICAgICAgICAgZGVsZXRlIHRoaXMuX3N5bmNocm9uaXphdGlvbkZsYWdzW3N5bmNocm9uaXphdGlvbklkXTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICBjb25zdCByZWNvbm5lY3RBY2NvdW50SWRzID0gcmVjb25uZWN0TGlzdGVuZXJzLm1hcChsaXN0ZW5lciA9PiBsaXN0ZW5lci5hY2NvdW50SWQpO1xuICAgICAgdGhpcy5fc3Vic2NyaXB0aW9uTWFuYWdlci5vblJlY29ubmVjdGVkKGluc3RhbmNlTnVtYmVyLCBzb2NrZXRJbnN0YW5jZUluZGV4LCByZWNvbm5lY3RBY2NvdW50SWRzKTtcbiAgICAgIHRoaXMuX3BhY2tldE9yZGVyZXIub25SZWNvbm5lY3RlZChyZWNvbm5lY3RBY2NvdW50SWRzKTtcblxuICAgICAgZm9yIChsZXQgbGlzdGVuZXIgb2YgcmVjb25uZWN0TGlzdGVuZXJzKSB7XG4gICAgICAgIFByb21pc2UucmVzb2x2ZShsaXN0ZW5lci5saXN0ZW5lci5vblJlY29ubmVjdGVkKHJlZ2lvbiwgaW5zdGFuY2VOdW1iZXIpKVxuICAgICAgICAgIC5jYXRjaChlcnIgPT4gdGhpcy5fbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gbm90aWZ5IHJlY29ubmVjdCBsaXN0ZW5lcicsIGVycikpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgdGhpcy5fbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gcHJvY2VzcyByZWNvbm5lY3RlZCBldmVudCcsIGVycik7XG4gICAgfVxuICB9XG5cbiAgX2dldFNvY2tldEluc3RhbmNlQnlBY2NvdW50KGFjY291bnRJZCwgaW5zdGFuY2VOdW1iZXIpIHtcbiAgICBjb25zdCByZWdpb24gPSB0aGlzLmdldEFjY291bnRSZWdpb24oYWNjb3VudElkKTtcbiAgICByZXR1cm4gdGhpcy5fc29ja2V0SW5zdGFuY2VzW3JlZ2lvbl1baW5zdGFuY2VOdW1iZXJdW3RoaXMuX3NvY2tldEluc3RhbmNlc0J5QWNjb3VudHNbaW5zdGFuY2VOdW1iZXJdW2FjY291bnRJZF1dO1xuICB9XG5cbiAgYXN5bmMgZ2V0VXJsU2V0dGluZ3MoaW5zdGFuY2VOdW1iZXIsIHJlZ2lvbikge1xuICAgIGlmICh0aGlzLl91cmwpIHtcbiAgICAgIHJldHVybiB7dXJsOiB0aGlzLl91cmwsIGlzU2hhcmVkQ2xpZW50QXBpOiB0cnVlfTtcbiAgICB9XG5cbiAgICBjb25zdCB1cmxTZXR0aW5ncyA9IGF3YWl0IHRoaXMuX2RvbWFpbkNsaWVudC5nZXRTZXR0aW5ncygpO1xuICAgIGNvbnN0IGdldFVybCA9IChob3N0bmFtZSkgPT4gXG4gICAgICBgaHR0cHM6Ly8ke2hvc3RuYW1lfS4ke3JlZ2lvbn0tJHtTdHJpbmcuZnJvbUNoYXJDb2RlKDk3ICsgTnVtYmVyKGluc3RhbmNlTnVtYmVyKSl9LiR7dXJsU2V0dGluZ3MuZG9tYWlufWA7XG5cbiAgICBsZXQgdXJsO1xuICAgIGlmICh0aGlzLl91c2VTaGFyZWRDbGllbnRBcGkpIHtcbiAgICAgIHVybCA9IGdldFVybCh0aGlzLl9ob3N0bmFtZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHVybCA9IGdldFVybCh1cmxTZXR0aW5ncy5ob3N0bmFtZSk7XG4gICAgfVxuICAgIGNvbnN0IGlzU2hhcmVkQ2xpZW50QXBpID0gdXJsID09PSBnZXRVcmwodGhpcy5faG9zdG5hbWUpO1xuICAgIHJldHVybiB7dXJsLCBpc1NoYXJlZENsaWVudEFwaX07XG4gIH1cblxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eVxuICBwcml2YXRlIGFzeW5jIF9nZXRTZXJ2ZXJVcmwoaW5zdGFuY2VOdW1iZXI6IG51bWJlciwgc29ja2V0SW5zdGFuY2VJbmRleCwgcmVnaW9uKSB7XG4gICAgaWYgKHRoaXMuX3VybCkge1xuICAgICAgcmV0dXJuIHRoaXMuX3VybDtcbiAgICB9XG5cbiAgICB3aGlsZSAodGhpcy5zb2NrZXRJbnN0YW5jZXNbcmVnaW9uXVtpbnN0YW5jZU51bWJlcl1bc29ja2V0SW5zdGFuY2VJbmRleF0uY29ubmVjdGVkKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCB1cmxTZXR0aW5ncyA9IGF3YWl0IHRoaXMuZ2V0VXJsU2V0dGluZ3MoaW5zdGFuY2VOdW1iZXIsIHJlZ2lvbik7XG4gICAgICAgIGNvbnN0IHVybCA9IHVybFNldHRpbmdzLnVybDtcbiAgICAgICAgY29uc3QgaXNTaGFyZWRDbGllbnRBcGkgPSB1cmxTZXR0aW5ncy5pc1NoYXJlZENsaWVudEFwaTtcbiAgICAgICAgbGV0IGxvZ01lc3NhZ2UgPSAnQ29ubmVjdGluZyBNZXRhQXBpIHdlYnNvY2tldCBjbGllbnQgdG8gdGhlIE1ldGFBcGkgc2VydmVyICcgK1xuICAgICAgYHZpYSAke3VybH0gJHtpc1NoYXJlZENsaWVudEFwaSA/ICdzaGFyZWQnIDogJ2RlZGljYXRlZCd9IHNlcnZlci5gO1xuICAgICAgICBpZiAodGhpcy5fZmlyc3RDb25uZWN0ICYmICFpc1NoYXJlZENsaWVudEFwaSkge1xuICAgICAgICAgIGxvZ01lc3NhZ2UgKz0gJyBQbGVhc2Ugbm90ZSB0aGF0IGl0IGNhbiB0YWtlIHVwIHRvIDMgbWludXRlcyBmb3IgeW91ciBkZWRpY2F0ZWQgc2VydmVyIHRvIHN0YXJ0IGZvciB0aGUgJyArXG4gICAgICAgICdmaXJzdCB0aW1lLiBEdXJpbmcgdGhpcyB0aW1lIGl0IGlzIE9LIGlmIHlvdSBzZWUgc29tZSBjb25uZWN0aW9uIGVycm9ycy4nO1xuICAgICAgICAgIHRoaXMuX2ZpcnN0Q29ubmVjdCA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuX2xvZ2dlci5pbmZvKGxvZ01lc3NhZ2UpO1xuICAgICAgICByZXR1cm4gdXJsO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIHRoaXMuX2xvZ2dlci5lcnJvcignRmFpbGVkIHRvIHJldHJpZXZlIHNlcnZlciBVUkwnLCBlcnIpO1xuICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXMgPT4gc2V0VGltZW91dChyZXMsIDEwMDApKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBfdGhyb3R0bGVSZXF1ZXN0KHR5cGUsIGFjY291bnRJZCwgaW5zdGFuY2VOdW1iZXIsIHRpbWVJbk1zKSB7XG4gICAgdGhpcy5fbGFzdFJlcXVlc3RzVGltZVtpbnN0YW5jZU51bWJlcl0gPSB0aGlzLl9sYXN0UmVxdWVzdHNUaW1lW2luc3RhbmNlTnVtYmVyXSB8fCB7fTtcbiAgICB0aGlzLl9sYXN0UmVxdWVzdHNUaW1lW2luc3RhbmNlTnVtYmVyXVt0eXBlXSA9IHRoaXMuX2xhc3RSZXF1ZXN0c1RpbWVbaW5zdGFuY2VOdW1iZXJdW3R5cGVdIHx8IHt9O1xuICAgIGxldCBsYXN0VGltZSA9IHRoaXMuX2xhc3RSZXF1ZXN0c1RpbWVbaW5zdGFuY2VOdW1iZXJdW3R5cGVdW2FjY291bnRJZF07XG4gICAgaWYgKCFsYXN0VGltZSB8fCAobGFzdFRpbWUgPCBEYXRlLm5vdygpIC0gdGltZUluTXMpKSB7XG4gICAgICB0aGlzLl9sYXN0UmVxdWVzdHNUaW1lW2luc3RhbmNlTnVtYmVyXVt0eXBlXVthY2NvdW50SWRdID0gRGF0ZS5ub3coKTtcbiAgICAgIHJldHVybiAhIWxhc3RUaW1lO1xuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBfcmVmcmVzaEFjY291bnRSZWdpb24oYWNjb3VudElkKSB7XG4gICAgaWYgKHRoaXMuX3JlZ2lvbnNCeUFjY291bnRzW2FjY291bnRJZF0pIHtcbiAgICAgIHRoaXMuX3JlZ2lvbnNCeUFjY291bnRzW2FjY291bnRJZF0ubGFzdFVzZWQgPSBEYXRlLm5vdygpO1xuICAgIH1cbiAgfVxuXG4gIC8vZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNvbXBsZXhpdHlcbiAgYXN5bmMgX2NyZWF0ZVNvY2tldEluc3RhbmNlQnlBY2NvdW50KGFjY291bnRJZCwgaW5zdGFuY2VOdW1iZXIpIHtcbiAgICBjb25zdCByZWdpb24gPSB0aGlzLmdldEFjY291bnRSZWdpb24oYWNjb3VudElkKTtcbiAgICBpZiAodGhpcy5fc29ja2V0SW5zdGFuY2VzQnlBY2NvdW50c1tpbnN0YW5jZU51bWJlcl1bYWNjb3VudElkXSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICBsZXQgc29ja2V0SW5zdGFuY2VJbmRleCA9IG51bGw7XG4gICAgICB3aGlsZSAodGhpcy5fc3Vic2NyaWJlTG9jayAmJiAoKG5ldyBEYXRlKHRoaXMuX3N1YnNjcmliZUxvY2sucmVjb21tZW5kZWRSZXRyeVRpbWUpLmdldFRpbWUoKSA+IERhdGUubm93KCkgJiYgXG4gICAgICAgICAgdGhpcy5zdWJzY3JpYmVkQWNjb3VudElkcyhpbnN0YW5jZU51bWJlciwgdW5kZWZpbmVkLCByZWdpb24pLmxlbmd0aCA8IFxuICAgICAgICAgIHRoaXMuX3N1YnNjcmliZUxvY2subG9ja2VkQXRBY2NvdW50cykgfHwgXG4gICAgICAgICAgKG5ldyBEYXRlKHRoaXMuX3N1YnNjcmliZUxvY2subG9ja2VkQXRUaW1lKS5nZXRUaW1lKCkgKyB0aGlzLl9zdWJzY3JpYmVDb29sZG93bkluU2Vjb25kcyAqIDEwMDAgPiBcbiAgICAgICAgICBEYXRlLm5vdygpICYmIHRoaXMuc3Vic2NyaWJlZEFjY291bnRJZHMoaW5zdGFuY2VOdW1iZXIsIHVuZGVmaW5lZCwgcmVnaW9uKS5sZW5ndGggPj0gXG4gICAgICAgICAgdGhpcy5fc3Vic2NyaWJlTG9jay5sb2NrZWRBdEFjY291bnRzKSkpIHtcbiAgICAgICAgYXdhaXQgbmV3IFByb21pc2UocmVzID0+IHNldFRpbWVvdXQocmVzLCAxMDAwKSk7XG4gICAgICB9XG4gICAgICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgdGhpcy5fc29ja2V0SW5zdGFuY2VzW3JlZ2lvbl1baW5zdGFuY2VOdW1iZXJdLmxlbmd0aDsgaW5kZXgrKykge1xuICAgICAgICBjb25zdCBhY2NvdW50Q291bnRlciA9IHRoaXMuX2dldEFzc2lnbmVkQWNjb3VudHMoaW5zdGFuY2VOdW1iZXIsIGluZGV4LCByZWdpb24pLmxlbmd0aDtcbiAgICAgICAgY29uc3QgaW5zdGFuY2UgPSB0aGlzLnNvY2tldEluc3RhbmNlc1tyZWdpb25dW2luc3RhbmNlTnVtYmVyXVtpbmRleF07XG4gICAgICAgIGlmIChpbnN0YW5jZS5zdWJzY3JpYmVMb2NrKSB7XG4gICAgICAgICAgaWYgKGluc3RhbmNlLnN1YnNjcmliZUxvY2sudHlwZSA9PT0gJ0xJTUlUX0FDQ09VTlRfU1VCU0NSSVBUSU9OU19QRVJfVVNFUl9QRVJfU0VSVkVSJyAmJiBcbiAgICAgICAgICAgIChuZXcgRGF0ZShpbnN0YW5jZS5zdWJzY3JpYmVMb2NrLnJlY29tbWVuZGVkUmV0cnlUaW1lKS5nZXRUaW1lKCkgPiBEYXRlLm5vdygpIHx8IFxuICAgICAgICAgICAgdGhpcy5zdWJzY3JpYmVkQWNjb3VudElkcyhpbnN0YW5jZU51bWJlciwgaW5kZXgsIHJlZ2lvbikubGVuZ3RoID49IFxuICAgICAgICAgICAgaW5zdGFuY2Uuc3Vic2NyaWJlTG9jay5sb2NrZWRBdEFjY291bnRzKSkge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChpbnN0YW5jZS5zdWJzY3JpYmVMb2NrLnR5cGUgPT09ICdMSU1JVF9BQ0NPVU5UX1NVQlNDUklQVElPTlNfUEVSX1NFUlZFUicgJiYgXG4gICAgICAgICAgICBuZXcgRGF0ZShpbnN0YW5jZS5zdWJzY3JpYmVMb2NrLnJlY29tbWVuZGVkUmV0cnlUaW1lKS5nZXRUaW1lKCkgPiBEYXRlLm5vdygpICYmXG4gICAgICAgICAgICB0aGlzLnN1YnNjcmliZWRBY2NvdW50SWRzKGluc3RhbmNlTnVtYmVyLCBpbmRleCwgcmVnaW9uKS5sZW5ndGggPj0gXG4gICAgICAgICAgICBpbnN0YW5jZS5zdWJzY3JpYmVMb2NrLmxvY2tlZEF0QWNjb3VudHMpIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAoYWNjb3VudENvdW50ZXIgPCB0aGlzLl9tYXhBY2NvdW50c1Blckluc3RhbmNlKSB7XG4gICAgICAgICAgc29ja2V0SW5zdGFuY2VJbmRleCA9IGluZGV4O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoc29ja2V0SW5zdGFuY2VJbmRleCA9PT0gbnVsbCkge1xuICAgICAgICBzb2NrZXRJbnN0YW5jZUluZGV4ID0gdGhpcy5fc29ja2V0SW5zdGFuY2VzW3JlZ2lvbl1baW5zdGFuY2VOdW1iZXJdLmxlbmd0aDtcbiAgICAgICAgYXdhaXQgdGhpcy5jb25uZWN0KGluc3RhbmNlTnVtYmVyLCByZWdpb24pO1xuICAgICAgfVxuICAgICAgdGhpcy5fc29ja2V0SW5zdGFuY2VzQnlBY2NvdW50c1tpbnN0YW5jZU51bWJlcl1bYWNjb3VudElkXSA9IHNvY2tldEluc3RhbmNlSW5kZXg7XG4gICAgfVxuICB9ICBcblxuICBwcml2YXRlIF9jbGVhckFjY291bnRDYWNoZUpvYigpIHtcbiAgICBjb25zdCBkYXRlID0gRGF0ZS5ub3coKTtcbiAgICBPYmplY3Qua2V5cyh0aGlzLl9yZWdpb25zQnlBY2NvdW50cykuZm9yRWFjaChyZXBsaWNhSWQgPT4ge1xuICAgICAgY29uc3QgZGF0YSA9IHRoaXMuX3JlZ2lvbnNCeUFjY291bnRzW3JlcGxpY2FJZF07XG4gICAgICBpZiAoZGF0YSAmJiBkYXRhLmNvbm5lY3Rpb25zID09PSAwICYmIGRhdGUgLSBkYXRhLmxhc3RVc2VkID4gMiAqIDYwICogNjAgKiAxMDAwKSB7XG4gICAgICAgIGNvbnN0IHByaW1hcnlBY2NvdW50SWQgPSB0aGlzLl9hY2NvdW50c0J5UmVwbGljYUlkW3JlcGxpY2FJZF07XG4gICAgICAgIGNvbnN0IHJlcGxpY2FzID0gT2JqZWN0LnZhbHVlczxhbnk+KHRoaXMuX2FjY291bnRSZXBsaWNhc1twcmltYXJ5QWNjb3VudElkXSB8fCB7fSk7XG4gICAgICAgIHJlcGxpY2FzLmZvckVhY2gocmVwbGljYSA9PiB7XG4gICAgICAgICAgZGVsZXRlIHRoaXMuX2FjY291bnRzQnlSZXBsaWNhSWRbcmVwbGljYV07XG4gICAgICAgICAgZGVsZXRlIHRoaXMuX3JlZ2lvbnNCeUFjY291bnRzW3JlcGxpY2FdO1xuICAgICAgICB9KTtcbiAgICAgICAgZGVsZXRlIHRoaXMuX2FjY291bnRSZXBsaWNhc1twcmltYXJ5QWNjb3VudElkXTtcbiAgICAgICAgdGhpcy5fbG9nZ2VyLmRlYnVnKGAke3ByaW1hcnlBY2NvdW50SWR9OiByZW1vdmVkIGV4cGlyZWQgYWNjb3VudCByZXBsaWNhcyBkYXRhYCk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBfY2xlYXJJbmFjdGl2ZVN5bmNEYXRhSm9iKCkge1xuICAgIGNvbnN0IGRhdGUgPSBEYXRlLm5vdygpO1xuICAgIE9iamVjdC5rZXlzKHRoaXMuX3N5bmNocm9uaXphdGlvbkhhc2hlcykuZm9yRWFjaChzeW5jaHJvbml6YXRpb25JZCA9PiB7XG4gICAgICBpZiAodGhpcy5fc3luY2hyb25pemF0aW9uSGFzaGVzW3N5bmNocm9uaXphdGlvbklkXS5sYXN0VXBkYXRlZCA8IGRhdGUgLSAzMCAqIDYwICogMTAwMCkge1xuICAgICAgICBkZWxldGUgdGhpcy5fc3luY2hyb25pemF0aW9uSGFzaGVzW3N5bmNocm9uaXphdGlvbklkXTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxufVxuXG5uYW1lc3BhY2UgTWV0YUFwaVdlYnNvY2tldENsaWVudCB7XG5cbiAgLyoqIEFjY291bnQgcmVwbGljYXMgYnkgcmVnaW9uIChpbmNsdWRpbmcgcHJpbWFyeSByZXBsaWNhKSBieSBwcmltYXJ5IElEICovXG4gIGV4cG9ydCB0eXBlIEFjY291bnRSZXBsaWNhcyA9IHtbcHJpbWFyeUlkOiBzdHJpbmddOiBNZXRhdHJhZGVyQWNjb3VudC5BY2NvdW50c0J5UmVnaW9ufTtcblxuICAvKipcbiAgICogQWNjb3VudCBJRHMgYnkgcmVwbGljYVxuICAgKiAtIGtleXMgYXJlIHJlcGxpY2Egb3IgcHJpbWFyeSBhY2NvdW50IElEc1xuICAgKiAtIHZhbHVlcyBhcmUgcHJpbWFyeSBhY2NvdW50IElEc1xuICAgKi9cbiAgZXhwb3J0IHR5cGUgQWNjb3VudHNCeVJlcGxpY2EgPSB7W3JlcGxpY2FJZDogc3RyaW5nXTogc3RyaW5nfTtcblxuICAvKipcbiAgICogU29ja2V0IGluc3RhbmNlXG4gICAqIEB0b2RvIGRlc2NyaWJlIGZpZWxkc1xuICAgKi9cbiAgZXhwb3J0IHR5cGUgU29ja2V0SW5zdGFuY2UgPSB7XG4gICAgaWQ6IG51bWJlcjtcbiAgICByZWNvbm5lY3RXYWl0VGltZTogbnVtYmVyO1xuICAgIGNvbm5lY3RlZDogYm9vbGVhbjtcbiAgICByZXF1ZXN0UmVzb2x2ZXM6IGFueTtcbiAgICByZXNvbHZlZDogYm9vbGVhbjtcbiAgICBjb25uZWN0UmVzdWx0OiBQcm9taXNlPGFueT47XG4gICAgc2Vzc2lvbklkOiBzdHJpbmc7XG4gICAgaXNSZWNvbm5lY3Rpbmc6IGJvb2xlYW47XG4gICAgc29ja2V0PzogYW55O1xuICAgIC8qKiBUaGUgZmluYWwgY2xpZW50IEFQSSBob3N0bmFtZSB0aGUgU0RLIGhhcyBjb25uZWN0ZWQgdG8gYmVoaW5kIGxvYWQgYmFsYW5jZXIgKi9cbiAgICBjbGllbnRBcGlIb3N0bmFtZT86IHN0cmluZztcbiAgICBzeW5jaHJvbml6YXRpb25UaHJvdHRsZXI6IFN5bmNocm9uaXphdGlvblRocm90dGxlcjtcbiAgICBzdWJzY3JpYmVMb2NrPzogYW55O1xuICAgIGluc3RhbmNlTnVtYmVyOiBudW1iZXI7XG4gIH07XG59XG5cbmV4cG9ydCBkZWZhdWx0IE1ldGFBcGlXZWJzb2NrZXRDbGllbnQ7XG5cbi8qKlxuICogUmVnaW9ucyBieSBhY2NvdW50c1xuICogLSBrZXlzIGFyZSByZXBsaWNhIG9yIHByaW1hcnkgYWNjb3VudCBJRFxuICovXG50eXBlIFJlZ2lvbnNCeUFjY291bnRzID0ge1thY2NvdW50SWQ6IHN0cmluZ106IHtcbiAgcmVnaW9uOiBzdHJpbmc7XG4gIGNvbm5lY3Rpb25zOiBudW1iZXI7XG4gIGxhc3RVc2VkOiBudW1iZXI7XG59fTtcblxudHlwZSBTeW5jaHJvbml6YXRpb25GbGFnID0ge1xuICBhY2NvdW50SWQ6IHN0cmluZyxcbiAgaW5zdGFuY2VOdW1iZXI6IG51bWJlcjtcbiAgc3BlY2lmaWNhdGlvbnNVcGRhdGVkOiBib29sZWFuO1xuICBwb3NpdGlvbnNVcGRhdGVkOiBib29sZWFuO1xuICBvcmRlcnNVcGRhdGVkOiBib29sZWFuO1xufTtcblxudHlwZSBTb2NrZXRJbnN0YW5jZXMgPSB7W3JlZ2lvbjogc3RyaW5nXToge1tpbnN0YW5jZU51bWJlcjogbnVtYmVyXTogTWV0YUFwaVdlYnNvY2tldENsaWVudC5Tb2NrZXRJbnN0YW5jZVtdfX07XG5cbnR5cGUgUGFja2V0ID0ge1xuICBzb2NrZXQ6IE1ldGFBcGlXZWJzb2NrZXRDbGllbnQuU29ja2V0SW5zdGFuY2U7XG4gIGRhdGE6IGFueTtcbn07XG5cbnR5cGUgUmVzdG9yZWRPcmRlclBhY2tldCA9IFBhY2tldE9yZGVyZXIuUGFja2V0ICYge1xuICBzb2NrZXQ6IE1ldGFBcGlXZWJzb2NrZXRDbGllbnQuU29ja2V0SW5zdGFuY2U7XG4gIHBhY2tldDogYW55O1xufTtcblxudHlwZSBNZXRhZGF0YVBhY2tldCA9IHtcbiAgY2xpZW50QXBpSG9zdG5hbWU6IHN0cmluZztcbn07XG4iXSwibmFtZXMiOlsicmFuZG9tc3RyaW5nIiwic29ja2V0SU8iLCJUaW1lb3V0RXJyb3IiLCJWYWxpZGF0aW9uRXJyb3IiLCJOb3RGb3VuZEVycm9yIiwiSW50ZXJuYWxFcnJvciIsIlVuYXV0aG9yaXplZEVycm9yIiwiVG9vTWFueVJlcXVlc3RzRXJyb3IiLCJGb3JiaWRkZW5FcnJvciIsIk9wdGlvbnNWYWxpZGF0b3IiLCJOb3RTeW5jaHJvbml6ZWRFcnJvciIsIk5vdENvbm5lY3RlZEVycm9yIiwiVHJhZGVFcnJvciIsIlBhY2tldE9yZGVyZXIiLCJTeW5jaHJvbml6YXRpb25UaHJvdHRsZXIiLCJTdWJzY3JpcHRpb25NYW5hZ2VyIiwiTG9nZ2VyTWFuYWdlciIsImFueSIsIkxhdGVuY3lTZXJ2aWNlIiwiXyIsIlBhY2tldExvZ2dlciIsIndpbmRvdyIsInJlcXVpcmUiLCJkZWZhdWx0IiwiTWV0YUFwaVdlYnNvY2tldENsaWVudCIsIm9uT3V0T2ZPcmRlclBhY2tldCIsImFjY291bnRJZCIsImluc3RhbmNlSW5kZXgiLCJleHBlY3RlZFNlcXVlbmNlTnVtYmVyIiwiYWN0dWFsU2VxdWVuY2VOdW1iZXIiLCJwYWNrZXQiLCJyZWNlaXZlZEF0IiwicHJpbWFyeUFjY291bnRJZCIsIl9hY2NvdW50c0J5UmVwbGljYUlkIiwiX3N1YnNjcmlwdGlvbk1hbmFnZXIiLCJpc1N1YnNjcmlwdGlvbkFjdGl2ZSIsImxldmVsIiwiX2xhdGVuY3lTZXJ2aWNlIiwiZ2V0U3luY2hyb25pemVkQWNjb3VudEluc3RhbmNlcyIsImxlbmd0aCIsIl9sb2dnZXIiLCJ0eXBlIiwiZW5zdXJlU3Vic2NyaWJlIiwidXJsIiwiX3VybCIsInJlZ2lvbiIsIl9yZWdpb24iLCJzb2NrZXRJbnN0YW5jZXMiLCJfc29ja2V0SW5zdGFuY2VzIiwic29ja2V0SW5zdGFuY2VzQnlBY2NvdW50cyIsIl9zb2NrZXRJbnN0YW5jZXNCeUFjY291bnRzIiwiYWNjb3VudFJlcGxpY2FzIiwiX2FjY291bnRSZXBsaWNhcyIsImFjY291bnRzQnlSZXBsaWNhSWQiLCJjbGVhckFjY291bnRDYWNoZUpvYiIsIl9jbGVhckFjY291bnRDYWNoZUpvYiIsImJpbmQiLCJsYXRlbmN5U2VydmljZSIsInN1YnNjcmliZWRBY2NvdW50SWRzIiwiaW5zdGFuY2VOdW1iZXIiLCJzb2NrZXRJbnN0YW5jZUluZGV4IiwiY29ubmVjdGVkSWRzIiwiT2JqZWN0Iiwia2V5cyIsIl9jb25uZWN0ZWRIb3N0cyIsImZvckVhY2giLCJpbnN0YW5jZUlkIiwic3BsaXQiLCJhY2NvdW50UmVnaW9uIiwiZ2V0QWNjb3VudFJlZ2lvbiIsImluY2x1ZGVzIiwidW5kZWZpbmVkIiwicHVzaCIsImNvbm5lY3RlZCIsImluc3RhbmNlIiwic29ja2V0IiwiX2dldEFzc2lnbmVkQWNjb3VudHMiLCJhY2NvdW50SWRzIiwia2V5IiwiX3JlZ2lvbnNCeUFjY291bnRzIiwiYWRkQWNjb3VudENhY2hlIiwicmVwbGljYXMiLCJyZXBsaWNhSWQiLCJjb25uZWN0aW9ucyIsImxhc3RVc2VkIiwiRGF0ZSIsIm5vdyIsImRlYnVnIiwidXBkYXRlQWNjb3VudENhY2hlIiwib2xkUmVwbGljYXMiLCJjb25uZWN0aW9uQ291bnQiLCJyZW1vdmVBY2NvdW50Q2FjaGUiLCJsb2NrU29ja2V0SW5zdGFuY2UiLCJtZXRhZGF0YSIsIl9zdWJzY3JpYmVMb2NrIiwicmVjb21tZW5kZWRSZXRyeVRpbWUiLCJsb2NrZWRBdEFjY291bnRzIiwibG9ja2VkQXRUaW1lIiwic3Vic2NyaWJlZEFjY291bnRzIiwic29ja2V0SW5zdGFuY2UiLCJjbG9zZSIsIl9yZWNvbm5lY3QiLCJzdWJzY3JpYmVMb2NrIiwiY29ubmVjdCIsImNsaWVudElkIiwiTWF0aCIsInJhbmRvbSIsInJlc29sdmUiLCJyZXN1bHQiLCJQcm9taXNlIiwicmVzIiwicmVqIiwiaWQiLCJyZWNvbm5lY3RXYWl0VGltZSIsIl9zb2NrZXRNaW5pbXVtUmVjb25uZWN0VGltZW91dCIsInJlcXVlc3RSZXNvbHZlcyIsInJlc29sdmVkIiwiY29ubmVjdFJlc3VsdCIsInNlc3Npb25JZCIsImdlbmVyYXRlIiwiaXNSZWNvbm5lY3RpbmciLCJzeW5jaHJvbml6YXRpb25UaHJvdHRsZXIiLCJfc3luY2hyb25pemF0aW9uVGhyb3R0bGVyT3B0cyIsInN0YXJ0Iiwic2VydmVyVXJsIiwiX2dldFNlcnZlclVybCIsInBhdGgiLCJyZWNvbm5lY3Rpb24iLCJyZWNvbm5lY3Rpb25EZWxheSIsInJlY29ubmVjdGlvbkRlbGF5TWF4IiwicmVjb25uZWN0aW9uQXR0ZW1wdHMiLCJJbmZpbml0eSIsInRpbWVvdXQiLCJfY29ubmVjdFRpbWVvdXQiLCJleHRyYUhlYWRlcnMiLCJxdWVyeSIsIl90b2tlbiIsInByb3RvY29sIiwib24iLCJpbmZvIiwiX2ZpcmVSZWNvbm5lY3RlZCIsImVyciIsImVycm9yIiwicmVhc29uIiwib25EaXNjb25uZWN0IiwiZGF0YSIsIkpTT04iLCJwYXJzZSIsInN0cmluZ2lmeSIsInJlcXVlc3RJZCIsInRpbWVzdGFtcHMiLCJyZXF1ZXN0UmVzb2x2ZSIsInJlamVjdCIsIl9jb252ZXJ0SXNvVGltZVRvRGF0ZSIsImNsaWVudFByb2Nlc3NpbmdGaW5pc2hlZCIsImxpc3RlbmVyIiwiX2xhdGVuY3lMaXN0ZW5lcnMiLCJ0aGVuIiwib25UcmFkZSIsIm9uUmVzcG9uc2UiLCJjYXRjaCIsIl9jb252ZXJ0RXJyb3IiLCJ0cmFjZSIsInNlcXVlbmNlTnVtYmVyIiwic2VxdWVuY2VUaW1lc3RhbXAiLCJzeW5jaHJvbml6YXRpb25JZCIsImFwcGxpY2F0aW9uIiwiaG9zdCIsInNwZWNpZmljYXRpb25zVXBkYXRlZCIsInBvc2l0aW9uc1VwZGF0ZWQiLCJvcmRlcnNVcGRhdGVkIiwic3BlY2lmaWNhdGlvbnMiLCJfc3luY2hyb25pemF0aW9uTGlzdGVuZXJzIiwiYWN0aXZlU3luY2hyb25pemF0aW9uSWRzIiwiX3BhY2tldExvZ2dlciIsImxvZ1BhY2tldCIsImlnbm9yZWRQYWNrZXRUeXBlcyIsIl90aHJvdHRsZVJlcXVlc3QiLCJfdW5zdWJzY3JpYmVUaHJvdHRsaW5nSW50ZXJ2YWwiLCJ1bnN1YnNjcmliZSIsIndhcm4iLCJxdWV1ZVBhY2tldCIsImNsaWVudEFwaUhvc3RuYW1lIiwidmFsdWVzIiwiRXJyb3IiLCJfcGFja2V0T3JkZXJlciIsInN0b3AiLCJjbGVhckludGVydmFsIiwiX2NsZWFyQWNjb3VudENhY2hlSW50ZXJ2YWwiLCJfY2xlYXJJbmFjdGl2ZVN5bmNEYXRhSW50ZXJ2YWwiLCJnZXRBY2NvdW50SW5mb3JtYXRpb24iLCJvcHRpb25zIiwicmVzcG9uc2UiLCJycGNSZXF1ZXN0IiwiYWNjb3VudEluZm9ybWF0aW9uIiwiZ2V0UG9zaXRpb25zIiwicG9zaXRpb25zIiwiZ2V0UG9zaXRpb24iLCJwb3NpdGlvbklkIiwicG9zaXRpb24iLCJnZXRPcmRlcnMiLCJvcmRlcnMiLCJnZXRPcmRlciIsIm9yZGVySWQiLCJvcmRlciIsImdldEhpc3RvcnlPcmRlcnNCeVRpY2tldCIsInRpY2tldCIsImhpc3RvcnlPcmRlcnMiLCJzeW5jaHJvbml6aW5nIiwiZ2V0SGlzdG9yeU9yZGVyc0J5UG9zaXRpb24iLCJnZXRIaXN0b3J5T3JkZXJzQnlUaW1lUmFuZ2UiLCJzdGFydFRpbWUiLCJlbmRUaW1lIiwib2Zmc2V0IiwibGltaXQiLCJnZXREZWFsc0J5VGlja2V0IiwiZGVhbHMiLCJnZXREZWFsc0J5UG9zaXRpb24iLCJnZXREZWFsc0J5VGltZVJhbmdlIiwicmVtb3ZlQXBwbGljYXRpb24iLCJ0cmFkZSIsInJlbGlhYmlsaXR5IiwicnBjUmVxdWVzdEFsbEluc3RhbmNlcyIsIl9hcHBsaWNhdGlvbiIsInN0cmluZ0NvZGUiLCJkZXNjcmlwdGlvbiIsIm51bWVyaWNDb2RlIiwibWVzc2FnZSIsInNjaGVkdWxlU3Vic2NyaWJlIiwic3Vic2NyaWJlIiwic3luY2hyb25pemUiLCJzdGFydGluZ0hpc3RvcnlPcmRlclRpbWUiLCJzdGFydGluZ0RlYWxUaW1lIiwiaGFzaGVzIiwiX2dldFNvY2tldEluc3RhbmNlQnlBY2NvdW50IiwiX2NyZWF0ZVNvY2tldEluc3RhbmNlQnlBY2NvdW50Iiwic3luY1Rocm90dGxlciIsIl9zeW5jaHJvbml6YXRpb25IYXNoZXMiLCJsYXN0VXBkYXRlZCIsInNjaGVkdWxlU3luY2hyb25pemUiLCJ2ZXJzaW9uIiwid2FpdFN5bmNocm9uaXplZCIsImFwcGxpY2F0aW9uUGF0dGVybiIsInRpbWVvdXRJblNlY29uZHMiLCJzdWJzY3JpYmVUb01hcmtldERhdGEiLCJzeW1ib2wiLCJzdWJzY3JpcHRpb25zIiwicmVmcmVzaE1hcmtldERhdGFTdWJzY3JpcHRpb25zIiwidW5zdWJzY3JpYmVGcm9tTWFya2V0RGF0YSIsImdldFN5bWJvbHMiLCJzeW1ib2xzIiwiZ2V0U3ltYm9sU3BlY2lmaWNhdGlvbiIsInNwZWNpZmljYXRpb24iLCJnZXRTeW1ib2xQcmljZSIsImtlZXBTdWJzY3JpcHRpb24iLCJwcmljZSIsImdldENhbmRsZSIsInRpbWVmcmFtZSIsImNhbmRsZSIsImdldFRpY2siLCJ0aWNrIiwiZ2V0Qm9vayIsImJvb2siLCJyZWZyZXNoVGVybWluYWxTdGF0ZSIsInJlZnJlc2hTeW1ib2xRdW90ZXMiLCJyZWZyZXNoZWRRdW90ZXMiLCJzYXZlVXB0aW1lIiwidXB0aW1lIiwib25VbnN1YnNjcmliZSIsIl91cGRhdGVFdmVudHMiLCJmaWx0ZXIiLCJzdGFydHNXaXRoIiwiYWxsIiwibWFwIiwiTnVtYmVyIiwibmFtZSIsImdldFNlcnZlclRpbWUiLCJzZXJ2ZXJUaW1lIiwiY2FsY3VsYXRlTWFyZ2luIiwibWFyZ2luIiwidW5zdWJzY3JpYmVBY2NvdW50UmVnaW9uIiwidW5zdWJzY3JpYmVQcm9taXNlcyIsIl9wcm9jZXNzRXZlbnQiLCJvblVuc3Vic2NyaWJlUmVnaW9uIiwiYWRkU3luY2hyb25pemF0aW9uTGlzdGVuZXIiLCJsaXN0ZW5lcnMiLCJyZW1vdmVTeW5jaHJvbml6YXRpb25MaXN0ZW5lciIsImwiLCJhZGRMYXRlbmN5TGlzdGVuZXIiLCJyZW1vdmVMYXRlbmN5TGlzdGVuZXIiLCJhZGRSZWNvbm5lY3RMaXN0ZW5lciIsIl9yZWNvbm5lY3RMaXN0ZW5lcnMiLCJyZW1vdmVSZWNvbm5lY3RMaXN0ZW5lciIsInJlbW92ZUFsbExpc3RlbmVycyIsIm9uQWNjb3VudERlbGV0ZWQiLCJjYW5jZWxBY2NvdW50IiwibWFzdGVyQWNjb3VudElkIiwicmVnaW9uRGF0YSIsIl9zeW5jaHJvbml6YXRpb25JZEJ5SW5zdGFuY2UiLCJyZXBsaWNhIiwicGFja2V0cyIsInJlc3RvcmVPcmRlciIsInAiLCJfc2VxdWVudGlhbEV2ZW50UHJvY2Vzc2luZyIsImV2ZW50cyIsIml0ZW0iLCJfcHJvY2Vzc1N5bmNocm9uaXphdGlvblBhY2tldCIsIl9ldmVudFF1ZXVlcyIsIl9jYWxsQWNjb3VudEV2ZW50cyIsImNvbmNhdCIsInF1ZXVlRXZlbnQiLCJjYWxsYWJsZSIsImV2ZW50Iiwic2hpZnQiLCJfdHJ5UmVjb25uZWN0IiwibWluIiwic2V0VGltZW91dCIsImlvIiwib3B0cyIsInVyaSIsInJlcXVlc3QiLCJhc3NpZ24iLCJlcnJvcnMiLCJpZ25vcmVkUmVxdWVzdFR5cGVzIiwiY29ubmVjdGVkSW5zdGFuY2UiLCJnZXRBY3RpdmVBY2NvdW50SW5zdGFuY2VzIiwid2FpdENvbm5lY3RlZEluc3RhbmNlIiwiYWN0aXZlUmVnaW9uIiwiX3JlZnJlc2hBY2NvdW50UmVnaW9uIiwiX21ha2VSZXF1ZXN0IiwicmV0cnlDb3VudGVyIiwiY2FsY1JldHJ5Q291bnRlciIsImNhbGNSZXF1ZXN0VGltZSIsIl9yZXRyaWVzIiwicG93IiwiX21pblJldHJ5RGVsYXlJblNlY29uZHMiLCJfbWF4UmV0cnlEZWxheUluU2Vjb25kcyIsInJldHJ5VGltZSIsImdldFRpbWUiLCJjbGllbnRQcm9jZXNzaW5nU3RhcnRlZCIsInJhY2UiLCJfcmVxdWVzdFRpbWVvdXQiLCJlbWl0IiwiZGV0YWlscyIsImZpZWxkIiwidmFsdWUiLCJtYXRjaCIsIkFycmF5IiwiaXNBcnJheSIsInByaWNlcyIsInVwZGF0ZVN5bmNocm9uaXphdGlvbklkIiwic291cmNlTGFiZWwiLCJjb21wYWN0Iiwiam9pbiIsImlzT25seUFjdGl2ZUluc3RhbmNlIiwiYWN0aXZlSW5zdGFuY2VJZHMiLCJjYW5jZWxEaXNjb25uZWN0VGltZXIiLCJfc3RhdHVzVGltZXJzIiwiY2xlYXJUaW1lb3V0IiwicmVzZXREaXNjb25uZWN0VGltZXIiLCJvblRpbWVvdXQiLCJvbkRpc2Nvbm5lY3RlZCIsImlzVGltZW91dCIsIm9uU3RyZWFtQ2xvc2VkIiwicmVtb3ZlSWRCeVBhcmFtZXRlcnMiLCJvbkNvbm5lY3RlZCIsImNhbmNlbFN1YnNjcmliZSIsIl9zeW5jaHJvbml6YXRpb25GbGFncyIsInNwZWNpZmljYXRpb25zSGFzaEluZGV4IiwicG9zaXRpb25zSGFzaEluZGV4Iiwib3JkZXJzSGFzaEluZGV4Iiwic3BlY2lmaWNhdGlvbnNIYXNoIiwic3BlY2lmaWNhdGlvbnNIYXNoZXMiLCJwb3NpdGlvbnNIYXNoIiwicG9zaXRpb25zSGFzaGVzIiwib3JkZXJzSGFzaCIsIm9yZGVyc0hhc2hlcyIsIm9uU3luY2hyb25pemF0aW9uU3RhcnRlZCIsIm9uQWNjb3VudEluZm9ybWF0aW9uVXBkYXRlZCIsIm9uUG9zaXRpb25zU3luY2hyb25pemVkIiwib25QZW5kaW5nT3JkZXJzU3luY2hyb25pemVkIiwiZGVhbCIsIm9uRGVhbEFkZGVkIiwib25QZW5kaW5nT3JkZXJzUmVwbGFjZWQiLCJoaXN0b3J5T3JkZXIiLCJvbkhpc3RvcnlPcmRlckFkZGVkIiwib25Qb3NpdGlvbnNSZXBsYWNlZCIsInVwZGF0ZWRQb3NpdGlvbnMiLCJyZW1vdmVkUG9zaXRpb25JZHMiLCJvblBvc2l0aW9uc1VwZGF0ZWQiLCJvblBvc2l0aW9uVXBkYXRlZCIsIm9uUG9zaXRpb25SZW1vdmVkIiwidXBkYXRlZE9yZGVycyIsImNvbXBsZXRlZE9yZGVySWRzIiwib25QZW5kaW5nT3JkZXJzVXBkYXRlZCIsIm9uUGVuZGluZ09yZGVyVXBkYXRlZCIsIm9uUGVuZGluZ09yZGVyQ29tcGxldGVkIiwib25VcGRhdGUiLCJvbkRlYWxzU3luY2hyb25pemVkIiwicmVtb3ZlU3luY2hyb25pemF0aW9uSWQiLCJldmVudENhbGxzIiwib25IaXN0b3J5T3JkZXJzU3luY2hyb25pemVkIiwiYXV0aGVudGljYXRlZCIsImlzRGlzY29ubmVjdGVkUmV0cnlNb2RlIiwiaXNBY2NvdW50U3Vic2NyaWJpbmciLCJvbkJyb2tlckNvbm5lY3Rpb25TdGF0dXNDaGFuZ2VkIiwiaGVhbHRoU3RhdHVzIiwib25IZWFsdGhTdGF0dXMiLCJ1cGRhdGVzIiwidW5zdWJzY3JpcHRpb25zIiwib25TdWJzY3JpcHRpb25Eb3duZ3JhZGVkIiwib25TeW1ib2xTcGVjaWZpY2F0aW9uc1VwZGF0ZWQiLCJyZW1vdmVkU3ltYm9scyIsIm9uU3ltYm9sU3BlY2lmaWNhdGlvblVwZGF0ZWQiLCJyZW1vdmVkU3ltYm9sIiwib25TeW1ib2xTcGVjaWZpY2F0aW9uUmVtb3ZlZCIsImNhbmRsZXMiLCJ0aWNrcyIsImJvb2tzIiwib25TeW1ib2xQcmljZXNVcGRhdGVkIiwiZXF1aXR5IiwiZnJlZU1hcmdpbiIsIm1hcmdpbkxldmVsIiwiYWNjb3VudEN1cnJlbmN5RXhjaGFuZ2VSYXRlIiwib25DYW5kbGVzVXBkYXRlZCIsIm9uVGlja3NVcGRhdGVkIiwib25Cb29rc1VwZGF0ZWQiLCJvblN5bWJvbFByaWNlVXBkYXRlZCIsIm9uU3ltYm9sUHJpY2UiLCJsYWJlbCIsInRocm93RXJyb3IiLCJpc0xvbmdFdmVudCIsImlzRXZlbnREb25lIiwiY2hlY2tMb25nRXZlbnQiLCJmbG9vciIsInJlY29ubmVjdExpc3RlbmVycyIsInJlY29ubmVjdEFjY291bnRJZHMiLCJvblJlY29ubmVjdGVkIiwiZ2V0VXJsU2V0dGluZ3MiLCJpc1NoYXJlZENsaWVudEFwaSIsInVybFNldHRpbmdzIiwiX2RvbWFpbkNsaWVudCIsImdldFNldHRpbmdzIiwiZ2V0VXJsIiwiaG9zdG5hbWUiLCJTdHJpbmciLCJmcm9tQ2hhckNvZGUiLCJkb21haW4iLCJfdXNlU2hhcmVkQ2xpZW50QXBpIiwiX2hvc3RuYW1lIiwibG9nTWVzc2FnZSIsIl9maXJzdENvbm5lY3QiLCJ0aW1lSW5NcyIsIl9sYXN0UmVxdWVzdHNUaW1lIiwibGFzdFRpbWUiLCJfc3Vic2NyaWJlQ29vbGRvd25JblNlY29uZHMiLCJpbmRleCIsImFjY291bnRDb3VudGVyIiwiX21heEFjY291bnRzUGVySW5zdGFuY2UiLCJkYXRlIiwiX2NsZWFySW5hY3RpdmVTeW5jRGF0YUpvYiIsImNvbnN0cnVjdG9yIiwibWV0YUFwaSIsImRvbWFpbkNsaWVudCIsInRva2VuIiwiX2RvbWFpbiIsIl9tZXRhQXBpIiwidmFsaWRhdG9yIiwicGFja2V0T3JkZXJpbmdUaW1lb3V0IiwidmFsaWRhdGVOb25aZXJvIiwicmVxdWVzdFRpbWVvdXQiLCJjb25uZWN0VGltZW91dCIsInJldHJ5T3B0cyIsInZhbGlkYXRlTnVtYmVyIiwicmV0cmllcyIsIm1pbkRlbGF5SW5TZWNvbmRzIiwibWF4RGVsYXlJblNlY29uZHMiLCJzdWJzY3JpYmVDb29sZG93bkluU2Vjb25kcyIsInZhbGlkYXRlQm9vbGVhbiIsInVzZVNoYXJlZENsaWVudEFwaSIsInVuc3Vic2NyaWJlVGhyb3R0bGluZ0ludGVydmFsSW5TZWNvbmRzIiwicGFja2V0TG9nZ2VyIiwiZW5hYmxlZCIsImdldExvZ2dlciIsImRpc2FibGVJbnRlcm5hbEpvYnMiLCJzZXRJbnRlcnZhbCJdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFFQSxPQUFPQSxrQkFBa0IsZUFBZTtBQUN4QyxPQUFPQyxjQUFjLG1CQUFtQjtBQUN4QyxPQUFPQyxrQkFBa0Isa0JBQWtCO0FBQzNDLFNBQVFDLGVBQWUsRUFBRUMsYUFBYSxFQUFFQyxhQUFhLEVBQUVDLGlCQUFpQixFQUFFQyxvQkFBb0IsRUFBRUMsY0FBYyxRQUN2RyxrQkFBa0I7QUFDekIsT0FBT0Msc0JBQXNCLHNCQUFzQjtBQUNuRCxPQUFPQywwQkFBMEIseUJBQXlCO0FBQzFELE9BQU9DLHVCQUF1QixzQkFBc0I7QUFDcEQsT0FBT0MsZ0JBQWdCLGVBQWU7QUFDdEMsT0FBT0MsbUJBQW1CLGtCQUFrQjtBQUM1QyxPQUFPQyw4QkFBOEIsNkJBQTZCO0FBQ2xFLE9BQU9DLHlCQUF5Qix3QkFBd0I7QUFDeEQsT0FBT0MsbUJBQW1CLGVBQWU7QUFDekMsT0FBT0MsU0FBUyxjQUFjO0FBQzlCLE9BQU9DLG9CQUFvQixtQkFBbUI7QUFJOUMsT0FBT0MsT0FBTyxTQUFTO0FBRXZCLGNBQWMsb0NBQW9DO0FBRWxELElBQUlDO0FBQ0osSUFBSSxPQUFPQyxXQUFXLGFBQWE7SUFDakNELGVBQWVFLFFBQVEsa0JBQWtCQyxPQUFPO0FBQ2xEO0FBRUE7O0NBRUMsR0FDRCxJQUFBLEFBQU1DLHlCQUFOLE1BQU1BO0lBcUhKOzs7Ozs7OztHQVFDLEdBQ0RDLG1CQUFtQkMsU0FBUyxFQUFFQyxhQUFhLEVBQUVDLHNCQUFzQixFQUFFQyxvQkFBb0IsRUFBRUMsTUFBTSxFQUFFQyxVQUFVLEVBQUU7UUFDN0csTUFBTUMsbUJBQW1CLElBQUksQ0FBQ0Msb0JBQW9CLENBQUNQLFVBQVU7UUFDN0QsSUFBSSxJQUFJLENBQUNRLG9CQUFvQixDQUFDQyxvQkFBb0IsQ0FBQ1QsWUFBWTtZQUM3RCxNQUFNVSxRQUFRLElBQUksQ0FBQ0MsZUFBZSxDQUFDQywrQkFBK0IsQ0FBQ04sa0JBQWtCTyxNQUFNLEdBQUcsVUFBVTtZQUN4RyxJQUFJLENBQUNDLE9BQU8sQ0FBQ0osTUFBTSxDQUFDLHVEQUNsQixDQUFDLFlBQVksRUFBRU4sT0FBT1csSUFBSSxDQUFDLGdCQUFnQixFQUFFZixVQUFVLENBQUMsRUFBRUMsY0FBYyxlQUFlLENBQUMsR0FDeEYsQ0FBQyxFQUFFQyx1QkFBdUIsOEJBQThCLEVBQUVDLHFCQUFxQixDQUFDO1lBQ2xGLElBQUksQ0FBQ2EsZUFBZSxDQUFDaEIsV0FBV0M7UUFDbEM7SUFDRjtJQUVBOzs7R0FHQyxHQUNELElBQUlnQixJQUFJQSxHQUFHLEVBQUU7UUFDWCxJQUFJLENBQUNDLElBQUksR0FBR0Q7SUFDZDtJQUVBOzs7R0FHQyxHQUNELElBQUlFLFNBQVM7UUFDWCxPQUFPLElBQUksQ0FBQ0MsT0FBTztJQUNyQjtJQUVBOzs7R0FHQyxHQUNELElBQUlDLGtCQUFrQjtRQUNwQixPQUFPLElBQUksQ0FBQ0MsZ0JBQWdCO0lBQzlCO0lBRUE7OztHQUdDLEdBQ0QsSUFBSUMsNEJBQTRCO1FBQzlCLE9BQU8sSUFBSSxDQUFDQywwQkFBMEI7SUFDeEM7SUFFQTs7O0dBR0MsR0FDRCxJQUFJQyxrQkFBMEQ7UUFDNUQsT0FBTyxJQUFJLENBQUNDLGdCQUFnQjtJQUM5QjtJQUVBOzs7R0FHQyxHQUNELElBQUlDLHNCQUFnRTtRQUNsRSxPQUFPLElBQUksQ0FBQ3BCLG9CQUFvQjtJQUNsQztJQUVBOzs7R0FHQyxHQUNELElBQUlxQix1QkFBdUI7UUFDekIsT0FBTyxJQUFJLENBQUNDLHFCQUFxQixDQUFDQyxJQUFJLENBQUMsSUFBSTtJQUM3QztJQUVBOzs7R0FHQyxHQUNELElBQUlDLGlCQUFpQjtRQUNuQixPQUFPLElBQUksQ0FBQ3BCLGVBQWU7SUFDN0I7SUFFQTs7Ozs7O0dBTUMsR0FDRHFCLHFCQUFxQkMsY0FBYyxFQUFFQyxtQkFBbUIsRUFBRWYsTUFBTSxFQUFFO1FBQ2hFLE1BQU1nQixlQUFlLEVBQUU7UUFDdkIsSUFBSSxJQUFJLENBQUNYLDBCQUEwQixDQUFDUyxlQUFlLEVBQUU7WUFDbkRHLE9BQU9DLElBQUksQ0FBQyxJQUFJLENBQUNDLGVBQWUsRUFBRUMsT0FBTyxDQUFDQyxDQUFBQTtnQkFDeEMsTUFBTXhDLFlBQVl3QyxXQUFXQyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUU7Z0JBQzFDLE1BQU1DLGdCQUFnQixJQUFJLENBQUNDLGdCQUFnQixDQUFDM0M7Z0JBQzVDLElBQUksQ0FBQ21DLGFBQWFTLFFBQVEsQ0FBQzVDLGNBQzNCLElBQUksQ0FBQ3dCLDBCQUEwQixDQUFDUyxlQUFlLENBQUNqQyxVQUFVLEtBQUs2QyxhQUM3RCxDQUFBLElBQUksQ0FBQ3JCLDBCQUEwQixDQUFDUyxlQUFlLENBQUNqQyxVQUFVLEtBQUtrQyx1QkFDakVBLHdCQUF3QlcsU0FBUSxLQUFNSCxrQkFBa0J2QixRQUFRO29CQUM5RGdCLGFBQWFXLElBQUksQ0FBQzlDO2dCQUNwQjtZQUNGO1FBQ0Y7UUFDQSxPQUFPbUM7SUFDVDtJQUVBOzs7Ozs7R0FNQyxHQUNEWSxVQUFVZCxjQUFjLEVBQUVDLG1CQUFtQixFQUFFZixNQUFNLEVBQUU7UUFDckQsTUFBTTZCLFdBQVcsSUFBSSxDQUFDMUIsZ0JBQWdCLENBQUNILE9BQU8sSUFDNUMsSUFBSSxDQUFDRyxnQkFBZ0IsQ0FBQ0gsT0FBTyxDQUFDYyxlQUFlLENBQUNwQixNQUFNLEdBQUdxQixzQkFDdkQsSUFBSSxDQUFDWixnQkFBZ0IsQ0FBQ0gsT0FBTyxDQUFDYyxlQUFlLENBQUNDLG9CQUFvQixHQUFHO1FBQ3ZFLE9BQU8sQUFBQ2MsWUFBWUEsU0FBU0MsTUFBTSxJQUFJRCxTQUFTQyxNQUFNLENBQUNGLFNBQVMsSUFBSztJQUN2RTtJQUVBOzs7Ozs7R0FNQyxHQUNELEFBQVFHLHFCQUFxQmpCLGNBQWMsRUFBRUMsbUJBQW1CLEVBQUVmLE1BQU0sRUFBRTtRQUN4RSxNQUFNZ0MsYUFBYSxFQUFFO1FBQ3JCZixPQUFPQyxJQUFJLENBQUMsSUFBSSxDQUFDYiwwQkFBMEIsQ0FBQ1MsZUFBZSxFQUFFTSxPQUFPLENBQUNhLENBQUFBO1lBQ25FLE1BQU1WLGdCQUFnQixJQUFJLENBQUNDLGdCQUFnQixDQUFDUztZQUM1QyxJQUFJVixrQkFBa0J2QixVQUNwQixJQUFJLENBQUNLLDBCQUEwQixDQUFDUyxlQUFlLENBQUNtQixJQUFJLEtBQUtsQixxQkFBcUI7Z0JBQzlFaUIsV0FBV0wsSUFBSSxDQUFDTTtZQUNsQjtRQUNGO1FBQ0EsT0FBT0Q7SUFDVDtJQUVBOzs7O0dBSUMsR0FDRFIsaUJBQWlCM0MsU0FBUyxFQUFFO1FBQzFCLE9BQU8sSUFBSSxDQUFDcUQsa0JBQWtCLENBQUNyRCxVQUFVLElBQUksSUFBSSxDQUFDcUQsa0JBQWtCLENBQUNyRCxVQUFVLENBQUNtQixNQUFNO0lBQ3hGO0lBRUE7Ozs7R0FJQyxHQUNEbUMsZ0JBQWdCdEQsU0FBaUIsRUFBRXVELFFBQTRDLEVBQUU7UUFDL0UsSUFBSSxDQUFDN0IsZ0JBQWdCLENBQUMxQixVQUFVLEdBQUd1RDtRQUNuQ25CLE9BQU9DLElBQUksQ0FBQ2tCLFVBQVVoQixPQUFPLENBQUNwQixDQUFBQTtZQUM1QixNQUFNcUMsWUFBWUQsUUFBUSxDQUFDcEMsT0FBTztZQUNsQyxJQUFJLENBQUMsSUFBSSxDQUFDa0Msa0JBQWtCLENBQUNHLFVBQVUsRUFBRTtnQkFDdkMsSUFBSSxDQUFDSCxrQkFBa0IsQ0FBQ0csVUFBVSxHQUFHO29CQUNuQ3JDO29CQUNBc0MsYUFBYTtvQkFDYkMsVUFBVUMsS0FBS0MsR0FBRztnQkFDcEI7WUFDRixPQUFPO2dCQUNMLElBQUksQ0FBQ1Asa0JBQWtCLENBQUNHLFVBQVUsQ0FBQ0MsV0FBVztZQUNoRDtZQUNBLElBQUksQ0FBQ2xELG9CQUFvQixDQUFDaUQsVUFBVSxHQUFHeEQ7UUFDekM7UUFDQSxJQUFJLENBQUNjLE9BQU8sQ0FBQytDLEtBQUssQ0FBQyxDQUFDLEVBQUU3RCxVQUFVLHFCQUFxQixDQUFDO0lBQ3hEO0lBRUE7Ozs7R0FJQyxHQUNEOEQsbUJBQW1COUQsU0FBaUIsRUFBRXVELFFBQTRDLEVBQUU7UUFDbEYsTUFBTVEsY0FBYyxJQUFJLENBQUNyQyxnQkFBZ0IsQ0FBQzFCLFVBQVU7UUFDcEQsSUFBSStELGFBQWE7WUFDZixNQUFNQyxrQkFBa0IsSUFBSSxDQUFDWCxrQkFBa0IsQ0FBQ3JELFVBQVUsQ0FBQ3lELFdBQVc7WUFDdEVyQixPQUFPQyxJQUFJLENBQUMwQixhQUFheEIsT0FBTyxDQUFDcEIsQ0FBQUE7Z0JBQy9CLE1BQU1xQyxZQUFZRCxRQUFRLENBQUNwQyxPQUFPO2dCQUNsQyxPQUFPLElBQUksQ0FBQ1osb0JBQW9CLENBQUNpRCxVQUFVO2dCQUMzQyxPQUFPLElBQUksQ0FBQ0gsa0JBQWtCLENBQUNHLFVBQVU7WUFDM0M7WUFDQSxJQUFJLENBQUM5QixnQkFBZ0IsQ0FBQzFCLFVBQVUsR0FBR3VEO1lBQ25DbkIsT0FBT0MsSUFBSSxDQUFDa0IsVUFBVWhCLE9BQU8sQ0FBQ3BCLENBQUFBO2dCQUM1QixNQUFNcUMsWUFBWUQsUUFBUSxDQUFDcEMsT0FBTztnQkFDbEMsSUFBSSxDQUFDa0Msa0JBQWtCLENBQUNHLFVBQVUsR0FBRztvQkFDbkNyQztvQkFDQXNDLGFBQWFPO29CQUNiTixVQUFVQyxLQUFLQyxHQUFHO2dCQUNwQjtnQkFDQSxJQUFJLENBQUNyRCxvQkFBb0IsQ0FBQ2lELFVBQVUsR0FBR3hEO1lBQ3pDO1lBQ0EsSUFBSSxDQUFDYyxPQUFPLENBQUMrQyxLQUFLLENBQUMsQ0FBQyxFQUFFN0QsVUFBVSx1QkFBdUIsQ0FBQztRQUMxRDtJQUNGO0lBRUE7OztHQUdDLEdBQ0RpRSxtQkFBbUJqRSxTQUFpQixFQUFFO1lBQ2hDO1FBQUosSUFBSSxFQUFBLHFDQUFBLElBQUksQ0FBQ3FELGtCQUFrQixDQUFDckQsVUFBVSxjQUFsQyx5REFBQSxtQ0FBb0N5RCxXQUFXLElBQUcsR0FBRztZQUN2RCxJQUFJLENBQUNKLGtCQUFrQixDQUFDckQsVUFBVSxDQUFDeUQsV0FBVztRQUNoRDtJQUNGO0lBRUE7Ozs7OztHQU1DLEdBQ0QsQUFBTVMsbUJBQW1CakMsY0FBYyxFQUFFQyxtQkFBbUIsRUFBRWYsTUFBTSxFQUFFZ0QsUUFBUTs7ZUFBOUUsb0JBQUE7WUFDRSxJQUFJQSxTQUFTcEQsSUFBSSxLQUFLLHdDQUF3QztnQkFDNUQsTUFBS3FELGNBQWMsR0FBRztvQkFDcEJDLHNCQUFzQkYsU0FBU0Usb0JBQW9CO29CQUNuREMsa0JBQWtCLE1BQUt0QyxvQkFBb0IsQ0FBQ0MsZ0JBQWdCWSxXQUFXMUIsUUFBUU4sTUFBTTtvQkFDckYwRCxjQUFjWixLQUFLQyxHQUFHO2dCQUN4QjtZQUNGLE9BQU87Z0JBQ0wsTUFBTVkscUJBQXFCLE1BQUt4QyxvQkFBb0IsQ0FBQ0MsZ0JBQWdCQyxxQkFBcUJmO2dCQUMxRixJQUFJcUQsbUJBQW1CM0QsTUFBTSxLQUFLLEdBQUc7b0JBQ25DLE1BQU00RCxpQkFBaUIsTUFBS3BELGVBQWUsQ0FBQ0YsT0FBTyxDQUFDYyxlQUFlLENBQUNDLG9CQUFvQjtvQkFDeEZ1QyxlQUFleEIsTUFBTSxDQUFDeUIsS0FBSztvQkFDM0IsTUFBTSxNQUFLQyxVQUFVLENBQUMxQyxnQkFBZ0JDLHFCQUFxQmY7Z0JBQzdELE9BQU87b0JBQ0wsTUFBTTZCLFdBQVcsTUFBSzNCLGVBQWUsQ0FBQ0YsT0FBTyxDQUFDYyxlQUFlLENBQUNDLG9CQUFvQjtvQkFDbEZjLFNBQVM0QixhQUFhLEdBQUc7d0JBQ3ZCUCxzQkFBc0JGLFNBQVNFLG9CQUFvQjt3QkFDbkR0RCxNQUFNb0QsU0FBU3BELElBQUk7d0JBQ25CdUQsa0JBQWtCRSxtQkFBbUIzRCxNQUFNO29CQUM3QztnQkFDRjtZQUNGO1FBQ0Y7O0lBRUE7Ozs7O0dBS0MsR0FDRCxBQUFNZ0UsUUFBUTVDLGNBQWMsRUFBRWQsTUFBTTs7ZUFBcEMsb0JBQUE7Z0JBU0Usd0JBQXNCQSxTQUN0QiwrQkFBOEJjO1lBVDlCLElBQUksTUFBS2IsT0FBTyxJQUFJRCxXQUFXLE1BQUtDLE9BQU8sRUFBRTtnQkFDM0MsTUFBTSxJQUFJM0MsZ0JBQWdCLENBQUMscUJBQXFCLEVBQUUwQyxPQUFPLDZCQUE2QixFQUFFLE1BQUtDLE9BQU8sQ0FBQyxDQUFDO1lBQ3hHO1lBQ0EsSUFBSTBELFdBQVdDLEtBQUtDLE1BQU07WUFDMUIsSUFBSUM7WUFDSixJQUFJQyxTQUFTLElBQUlDLFFBQVEsQ0FBQ0MsS0FBS0M7Z0JBQzdCSixVQUFVRztZQUNaO1lBQ0EsQ0FBQSx5QkFBQSxNQUFLOUQsZ0JBQWdCLENBQUEsQ0FBQ0gsVUFBQUEsT0FBTyxLQUE3QixzQkFBcUIsQ0FBQ0EsUUFBTyxHQUFLLENBQUM7WUFDbkMsQ0FBQSxnQ0FBQSxNQUFLRyxnQkFBZ0IsQ0FBQ0gsT0FBTyxDQUFBLENBQUNjLGtCQUFBQSxlQUFlLEtBQTdDLDZCQUE2QixDQUFDQSxnQkFBZSxHQUFLLEVBQUU7WUFDcEQsTUFBTUMsc0JBQXNCLE1BQUtaLGdCQUFnQixDQUFDSCxPQUFPLENBQUNjLGVBQWUsQ0FBQ3BCLE1BQU07WUFDaEYsTUFBTW1DLFdBQWtEO2dCQUN0RHNDLElBQUlwRDtnQkFDSnFELG1CQUFtQixNQUFLQyw4QkFBOEI7Z0JBQ3REekMsV0FBVztnQkFDWDBDLGlCQUFpQixDQUFDO2dCQUNsQkMsVUFBVTtnQkFDVkMsZUFBZVQ7Z0JBQ2ZVLFdBQVd0SCxhQUFhdUgsUUFBUSxDQUFDO2dCQUNqQ0MsZ0JBQWdCO2dCQUNoQjdDLFFBQVE7Z0JBQ1I4QywwQkFBMEIsSUFBSTNHLGdDQUErQjhDLHFCQUFxQkQsZ0JBQWdCZCxRQUNoRyxNQUFLNkUsNkJBQTZCO2dCQUNwQ3BCLGVBQWU7Z0JBQ2YzQztZQUNGO1lBQ0FlLFNBQVNELFNBQVMsR0FBRztZQUNyQixNQUFLekIsZ0JBQWdCLENBQUNILE9BQU8sQ0FBQ2MsZUFBZSxDQUFDYSxJQUFJLENBQUNFO1lBQ25EQSxTQUFTK0Msd0JBQXdCLENBQUNFLEtBQUs7WUFDdkMsTUFBTUMsWUFBWSxNQUFNLE1BQUtDLGFBQWEsQ0FBQ2xFLGdCQUFnQkMscUJBQXFCZjtZQUNoRixNQUFNc0QsaUJBQWlCbEcsU0FBUzJILFdBQVc7Z0JBQ3pDRSxNQUFNO2dCQUNOQyxjQUFjO2dCQUNkQyxtQkFBbUI7Z0JBQ25CQyxzQkFBc0I7Z0JBQ3RCQyxzQkFBc0JDO2dCQUN0QkMsU0FBUyxNQUFLQyxlQUFlO2dCQUM3QkMsY0FBYztvQkFDWixhQUFhOUI7Z0JBQ2Y7Z0JBQ0ErQixPQUFPO29CQUNMLGNBQWMsTUFBS0MsTUFBTTtvQkFDekJoQyxVQUFVQTtvQkFDVmlDLFVBQVU7Z0JBQ1o7WUFDRjtZQUNBL0QsU0FBU0MsTUFBTSxHQUFHd0I7WUFDbEJBLGVBQWV1QyxFQUFFLENBQUMseUJBQVcsb0JBQUE7Z0JBQzNCLHNDQUFzQztnQkFDdEMsTUFBS2xHLE9BQU8sQ0FBQ21HLElBQUksQ0FBQyxDQUFDLEVBQUU5RixPQUFPLENBQUMsRUFBRWMsZUFBZSwwREFBMEQsQ0FBQztnQkFDekdlLFNBQVN1QyxpQkFBaUIsR0FBRyxNQUFLQyw4QkFBOEI7Z0JBQ2hFeEMsU0FBUzhDLGNBQWMsR0FBRztnQkFDMUIsSUFBSSxDQUFDOUMsU0FBUzBDLFFBQVEsRUFBRTtvQkFDdEIxQyxTQUFTMEMsUUFBUSxHQUFHO29CQUNwQlQ7Z0JBQ0YsT0FBTztvQkFDTCxNQUFNLE1BQUtpQyxnQkFBZ0IsQ0FBQ2pGLGdCQUFnQmUsU0FBU3NDLEVBQUUsRUFBRW5FO2dCQUMzRDtnQkFDQSxJQUFJLENBQUM2QixTQUFTRCxTQUFTLEVBQUU7b0JBQ3ZCQyxTQUFTQyxNQUFNLENBQUN5QixLQUFLO2dCQUN2QjtZQUNGO1lBQ0FELGVBQWV1QyxFQUFFLENBQUMsMkJBQWEsb0JBQUE7Z0JBQzdCaEUsU0FBUzhDLGNBQWMsR0FBRztnQkFDMUIsTUFBS2hGLE9BQU8sQ0FBQ21HLElBQUksQ0FBQyxDQUFDLEVBQUU5RixPQUFPLENBQUMsRUFBRWMsZUFBZSxzQ0FBc0MsQ0FBQztnQkFDckYsTUFBTSxNQUFLaUYsZ0JBQWdCLENBQUNqRixnQkFBZ0JlLFNBQVNzQyxFQUFFLEVBQUVuRTtZQUMzRDtZQUNBc0QsZUFBZXVDLEVBQUUsQ0FBQzsyQkFBaUIsb0JBQUEsVUFBT0c7b0JBQ3hDLHNDQUFzQztvQkFDdEMsTUFBS3JHLE9BQU8sQ0FBQ3NHLEtBQUssQ0FBQyxDQUFDLEVBQUVqRyxPQUFPLENBQUMsRUFBRWMsZUFBZSwyQ0FBMkMsQ0FBQyxFQUFFa0Y7b0JBQzdGbkUsU0FBUzhDLGNBQWMsR0FBRztvQkFDMUIsTUFBTSxNQUFLbkIsVUFBVSxDQUFDMUMsZ0JBQWdCZSxTQUFTc0MsRUFBRSxFQUFFbkU7Z0JBQ3JEO2dDQUwwQ2dHOzs7O1lBTTFDMUMsZUFBZXVDLEVBQUUsQ0FBQzsyQkFBbUIsb0JBQUEsVUFBT047b0JBQzFDLHNDQUFzQztvQkFDdEMsTUFBSzVGLE9BQU8sQ0FBQ3NHLEtBQUssQ0FBQyxDQUFDLEVBQUVqRyxPQUFPLENBQUMsRUFBRWMsZUFBZSw2Q0FBNkMsQ0FBQztvQkFDN0ZlLFNBQVM4QyxjQUFjLEdBQUc7b0JBQzFCLElBQUksQ0FBQzlDLFNBQVMwQyxRQUFRLEVBQUU7d0JBQ3RCLE1BQU0sTUFBS2YsVUFBVSxDQUFDMUMsZ0JBQWdCZSxTQUFTc0MsRUFBRSxFQUFFbkU7b0JBQ3JEO2dCQUNGO2dDQVA0Q3VGOzs7O1lBUTVDakMsZUFBZXVDLEVBQUUsQ0FBQzsyQkFBYyxvQkFBQSxVQUFPSztvQkFDckNyRSxTQUFTK0Msd0JBQXdCLENBQUN1QixZQUFZO29CQUM5QyxzQ0FBc0M7b0JBQ3RDLE1BQUt4RyxPQUFPLENBQUNtRyxJQUFJLENBQUMsQ0FBQyxFQUFFOUYsT0FBTyxDQUFDLEVBQUVjLGVBQWUsaURBQWlELENBQUMsR0FDOUYsQ0FBQywwQkFBMEIsRUFBRW9GLE9BQU8sQ0FBQztvQkFDdkNyRSxTQUFTOEMsY0FBYyxHQUFHO29CQUMxQixNQUFNLE1BQUtuQixVQUFVLENBQUMxQyxnQkFBZ0JlLFNBQVNzQyxFQUFFLEVBQUVuRTtnQkFDckQ7Z0NBUHVDa0c7Ozs7WUFRdkM1QyxlQUFldUMsRUFBRSxDQUFDOzJCQUFTLG9CQUFBLFVBQU9JO29CQUNoQyxzQ0FBc0M7b0JBQ3RDLE1BQUt0RyxPQUFPLENBQUNzRyxLQUFLLENBQUMsQ0FBQyxFQUFFakcsT0FBTyxDQUFDLEVBQUVjLGVBQWUsZ0NBQWdDLENBQUMsRUFBRW1GO29CQUNsRnBFLFNBQVM4QyxjQUFjLEdBQUc7b0JBQzFCLE1BQU0sTUFBS25CLFVBQVUsQ0FBQzFDLGdCQUFnQmUsU0FBU3NDLEVBQUUsRUFBRW5FO2dCQUNyRDtnQ0FMa0NpRzs7OztZQU1sQzNDLGVBQWV1QyxFQUFFLENBQUMsWUFBWU8sQ0FBQUE7Z0JBQzVCLElBQUksT0FBT0EsU0FBUyxVQUFVO29CQUM1QkEsT0FBT0MsS0FBS0MsS0FBSyxDQUFDRjtnQkFDcEI7Z0JBQ0EsTUFBS3pHLE9BQU8sQ0FBQytDLEtBQUssQ0FBQyxJQUFNLENBQUMsRUFBRTBELEtBQUt2SCxTQUFTLENBQUMscUJBQXFCLEVBQUV3SCxLQUFLRSxTQUFTLENBQUM7d0JBQy9FQyxXQUFXSixLQUFLSSxTQUFTO3dCQUFFQyxZQUFZTCxLQUFLSyxVQUFVO29CQUFBLEdBQUcsQ0FBQztnQkFDNUQsSUFBSUMsaUJBQWtCN0UsU0FBU3lDLGVBQWUsQ0FBQzhCLEtBQUtJLFNBQVMsQ0FBQyxJQUFJO29CQUFDMUMsU0FBUyxLQUFPO29CQUFHNkMsUUFBUSxLQUFPO2dCQUFDO2dCQUN0RyxPQUFPOUUsU0FBU3lDLGVBQWUsQ0FBQzhCLEtBQUtJLFNBQVMsQ0FBQztnQkFDL0MsTUFBS0kscUJBQXFCLENBQUNSO2dCQUMzQk0sZUFBZTVDLE9BQU8sQ0FBQ3NDO2dCQUN2QixJQUFJQSxLQUFLSyxVQUFVLElBQUlDLGVBQWU5RyxJQUFJLEVBQUU7b0JBQzFDd0csS0FBS0ssVUFBVSxDQUFDSSx3QkFBd0IsR0FBRyxJQUFJckU7b0JBQy9DLEtBQUssSUFBSXNFLFlBQVksTUFBS0MsaUJBQWlCLENBQUU7d0JBQzNDL0MsUUFBUUYsT0FBTyxHQUNaa0QsSUFBSSxDQUFDLElBQU1OLGVBQWU5RyxJQUFJLEtBQUssVUFDbENrSCxTQUFTRyxPQUFPLENBQUNiLEtBQUt2SCxTQUFTLEVBQUV1SCxLQUFLSyxVQUFVLElBQ2hESyxTQUFTSSxVQUFVLENBQUNkLEtBQUt2SCxTQUFTLEVBQUU2SCxlQUFlOUcsSUFBSSxFQUFFd0csS0FBS0ssVUFBVSxHQUN6RVUsS0FBSyxDQUFDbEIsQ0FBQUEsUUFBUyxNQUFLdEcsT0FBTyxDQUFDc0csS0FBSyxDQUFDLG9EQUNqQ0csS0FBS3ZILFNBQVMsR0FBRyxvQkFBb0I2SCxlQUFlOUcsSUFBSSxFQUFFcUc7b0JBQ2hFO2dCQUNGO1lBQ0Y7WUFDQTNDLGVBQWV1QyxFQUFFLENBQUMsbUJBQW1CTyxDQUFBQTtnQkFDbkMsSUFBSU0saUJBQWtCN0UsU0FBU3lDLGVBQWUsQ0FBQzhCLEtBQUtJLFNBQVMsQ0FBQyxJQUFJO29CQUFDMUMsU0FBUyxLQUFPO29CQUFHNkMsUUFBUSxLQUFPO2dCQUFDO2dCQUN0RyxPQUFPOUUsU0FBU3lDLGVBQWUsQ0FBQzhCLEtBQUtJLFNBQVMsQ0FBQztnQkFDL0NFLGVBQWVDLE1BQU0sQ0FBQyxNQUFLUyxhQUFhLENBQUNoQjtZQUMzQztZQUNBLHNDQUFzQztZQUN0QzlDLGVBQWV1QyxFQUFFLENBQUM7MkJBQW1CLG9CQUFBLFVBQU1PO3dCQWN6QywwQkFBd0JBO29CQWJ4QixJQUFJLE9BQU9BLFNBQVMsVUFBVTt3QkFDNUJBLE9BQU9DLEtBQUtDLEtBQUssQ0FBQ0Y7b0JBQ3BCO29CQUNBLElBQUlBLEtBQUt0SCxhQUFhLElBQUlzSCxLQUFLdEgsYUFBYSxLQUFLZ0MsZ0JBQWdCO3dCQUMvRCxNQUFLbkIsT0FBTyxDQUFDMEgsS0FBSyxDQUFDLElBQU0sQ0FBQyxFQUFFakIsS0FBS3ZILFNBQVMsQ0FBQyxDQUFDLEVBQUV1SCxLQUFLdEYsY0FBYyxDQUFDLHNDQUFzQyxDQUFDLEdBQ3ZHLENBQUMsMkNBQTJDLEVBQUVBLGVBQWUsT0FBTyxFQUFFdUYsS0FBS0UsU0FBUyxDQUFDO2dDQUNuRjNHLE1BQU13RyxLQUFLeEcsSUFBSTtnQ0FBRTBILGdCQUFnQmxCLEtBQUtrQixjQUFjO2dDQUFFQyxtQkFBbUJuQixLQUFLbUIsaUJBQWlCO2dDQUMvRkMsbUJBQW1CcEIsS0FBS29CLGlCQUFpQjtnQ0FBRUMsYUFBYXJCLEtBQUtxQixXQUFXO2dDQUFFQyxNQUFNdEIsS0FBS3NCLElBQUk7Z0NBQ3pGQyx1QkFBdUJ2QixLQUFLdUIscUJBQXFCO2dDQUFFQyxrQkFBa0J4QixLQUFLd0IsZ0JBQWdCO2dDQUMxRkMsZUFBZXpCLEtBQUt5QixhQUFhO2dDQUNqQ0MsZ0JBQWdCMUIsS0FBSzBCLGNBQWMsR0FBRyxBQUFDMUIsQ0FBQUEsS0FBSzBCLGNBQWMsSUFBSSxFQUFFLEFBQUQsRUFBR3BJLE1BQU0sR0FBR2dDOzRCQUFTLEdBQUcsQ0FBQzt3QkFDNUY7b0JBQ0Y7b0JBQ0EsQ0FBQSwyQkFBQSxNQUFLUSxrQkFBa0IsQ0FBQSxDQUFDa0Usa0JBQUFBLEtBQUt2SCxTQUFTLENBQUMsS0FBdkMsd0JBQXVCLENBQUN1SCxnQkFBZSxHQUFLO3dCQUFDcEc7d0JBQVFzQyxhQUFhO3dCQUFHQyxVQUFVQyxLQUFLQyxHQUFHO29CQUFFO29CQUN6RixNQUFLOUMsT0FBTyxDQUFDMEgsS0FBSyxDQUFDLElBQU0sQ0FBQyxFQUFFakIsS0FBS3ZILFNBQVMsQ0FBQyxDQUFDLEVBQUV1SCxLQUFLdEgsYUFBYSxDQUFDLHdCQUF3QixFQUFFdUgsS0FBS0UsU0FBUyxDQUFDOzRCQUN4RzNHLE1BQU13RyxLQUFLeEcsSUFBSTs0QkFBRTBILGdCQUFnQmxCLEtBQUtrQixjQUFjOzRCQUFFQyxtQkFBbUJuQixLQUFLbUIsaUJBQWlCOzRCQUMvRkMsbUJBQW1CcEIsS0FBS29CLGlCQUFpQjs0QkFBRUMsYUFBYXJCLEtBQUtxQixXQUFXOzRCQUFFQyxNQUFNdEIsS0FBS3NCLElBQUk7NEJBQ3pGQyx1QkFBdUJ2QixLQUFLdUIscUJBQXFCOzRCQUFFQyxrQkFBa0J4QixLQUFLd0IsZ0JBQWdCOzRCQUMxRkMsZUFBZXpCLEtBQUt5QixhQUFhOzRCQUNqQ0MsZ0JBQWdCMUIsS0FBSzBCLGNBQWMsR0FBRyxBQUFDMUIsQ0FBQUEsS0FBSzBCLGNBQWMsSUFBSSxFQUFFLEFBQUQsRUFBR3BJLE1BQU0sR0FBR2dDO3dCQUFTLEdBQUcsRUFBRSxDQUFDLEdBQzFGLENBQUMsa0JBQWtCLEVBQUUsQUFBQyxDQUFBLE1BQUtxRyx5QkFBeUIsQ0FBQzNCLEtBQUt2SCxTQUFTLENBQUMsSUFBSSxFQUFFLEFBQUQsRUFBR2EsTUFBTSxDQUFDLENBQUM7b0JBQ3RGLElBQUlzSSwyQkFBMkJuRyxTQUFTK0Msd0JBQXdCLENBQUNvRCx3QkFBd0I7b0JBQ3pGLElBQUksQ0FBQzVCLEtBQUtvQixpQkFBaUIsSUFBSVEseUJBQXlCdkcsUUFBUSxDQUFDMkUsS0FBS29CLGlCQUFpQixHQUFHO3dCQUN4RixJQUFJLE1BQUtTLGFBQWEsRUFBRTs0QkFDdEIsTUFBTSxNQUFLQSxhQUFhLENBQUNDLFNBQVMsQ0FBQzlCO3dCQUNyQzt3QkFDQSxNQUFNK0IscUJBQXFCOzRCQUFDOzRCQUFnQjs0QkFBVTt5QkFBWTt3QkFDbEUsSUFBSSxDQUFDLE1BQUs5SSxvQkFBb0IsQ0FBQ0Msb0JBQW9CLENBQUM4RyxLQUFLdkgsU0FBUyxLQUNoRSxDQUFDc0osbUJBQW1CMUcsUUFBUSxDQUFDMkUsS0FBS3hHLElBQUksR0FBRzs0QkFDekMsTUFBS0QsT0FBTyxDQUFDK0MsS0FBSyxDQUFDLENBQUMsRUFBRTBELEtBQUt2SCxTQUFTLENBQUMsbURBQW1ELENBQUMsR0FDdkYsQ0FBQyxzQkFBc0IsRUFBRXVILEtBQUt4RyxJQUFJLENBQUMsQ0FBQzs0QkFDdEMsSUFBSSxNQUFLd0ksZ0JBQWdCLENBQUMsZUFBZWhDLEtBQUt2SCxTQUFTLEVBQUV1SCxLQUFLdEgsYUFBYSxFQUN6RSxNQUFLdUosOEJBQThCLEdBQUc7Z0NBQ3RDLE1BQUtDLFdBQVcsQ0FBQ2xDLEtBQUt2SCxTQUFTLEVBQUVzSSxLQUFLLENBQUNuQixDQUFBQTtvQ0FDckMsTUFBS3JHLE9BQU8sQ0FBQzRJLElBQUksQ0FBQyxDQUFDLEVBQUVuQyxLQUFLdkgsU0FBUyxDQUFDLENBQUMsRUFBRXVILEtBQUt0SCxhQUFhLElBQUksRUFBRSx1QkFBdUIsQ0FBQyxFQUFFa0g7Z0NBQzNGOzRCQUNGOzRCQUNBO3dCQUNGO3dCQUNBLE1BQUtZLHFCQUFxQixDQUFDUjtvQkFDN0IsT0FBTzt3QkFDTEEsS0FBS3hHLElBQUksR0FBRztvQkFDZDtvQkFDQSxNQUFLNEksV0FBVyxDQUFDM0csVUFBVXVFO2dCQUM3QjtnQ0E3QzJDQTs7OztZQThDM0M5QyxlQUFldUMsRUFBRSxDQUFDLFlBQVksQ0FBQ087Z0JBQzdCdkUsU0FBUzRHLGlCQUFpQixHQUFHckMsS0FBS3FDLGlCQUFpQjtZQUNyRDtZQUNBLE9BQU8xRTtRQUNUOztJQUVBOztHQUVDLEdBQ0RSLFFBQVE7UUFDTnRDLE9BQU9DLElBQUksQ0FBQyxJQUFJLENBQUNmLGdCQUFnQixFQUFFaUIsT0FBTyxDQUFDcEIsQ0FBQUE7WUFDekNpQixPQUFPQyxJQUFJLENBQUMsSUFBSSxDQUFDZixnQkFBZ0IsQ0FBQ0gsT0FBTyxFQUFFb0IsT0FBTyxDQUFDTixDQUFBQTtnQkFDakQsSUFBSSxDQUFDWCxnQkFBZ0IsQ0FBQ0gsT0FBTyxDQUFDYyxlQUFlLENBQUNNLE9BQU87K0JBQUMsb0JBQUEsVUFBT1M7d0JBQzNELElBQUlBLFNBQVNELFNBQVMsRUFBRTs0QkFDdEJDLFNBQVNELFNBQVMsR0FBRzs0QkFDckIsTUFBTUMsU0FBU0MsTUFBTSxDQUFDeUIsS0FBSzs0QkFDM0IsS0FBSyxJQUFJbUQsa0JBQWtCekYsT0FBT3lILE1BQU0sQ0FBTTdHLFNBQVN5QyxlQUFlLEVBQUc7Z0NBQ3ZFb0MsZUFBZUMsTUFBTSxDQUFDLElBQUlnQyxNQUFNOzRCQUNsQzs0QkFDQTlHLFNBQVN5QyxlQUFlLEdBQUcsQ0FBQzt3QkFDOUI7b0JBQ0Y7b0NBVDZEekM7Ozs7Z0JBVTdELElBQUksQ0FBQ3hCLDBCQUEwQixDQUFDUyxlQUFlLEdBQUcsQ0FBQztnQkFDbkQsSUFBSSxDQUFDWCxnQkFBZ0IsQ0FBQ0gsT0FBTyxDQUFDYyxlQUFlLEdBQUcsRUFBRTtZQUNwRDtRQUNGO1FBQ0EsSUFBSSxDQUFDaUgseUJBQXlCLEdBQUcsQ0FBQztRQUNsQyxJQUFJLENBQUNoQixpQkFBaUIsR0FBRyxFQUFFO1FBQzNCLElBQUksQ0FBQzZCLGNBQWMsQ0FBQ0MsSUFBSTtJQUMxQjtJQUVBOztHQUVDLEdBQ0RBLE9BQU87UUFDTEMsY0FBYyxJQUFJLENBQUNDLDBCQUEwQjtRQUM3Q0QsY0FBYyxJQUFJLENBQUNFLDhCQUE4QjtRQUNqRCxJQUFJLENBQUN4SixlQUFlLENBQUNxSixJQUFJO0lBQzNCO0lBRUE7Ozs7O0dBS0MsR0FDRCxBQUFNSSxzQkFBc0JwSyxTQUFTLEVBQUVxSyxPQUFPOztlQUE5QyxvQkFBQTtZQUNFLElBQUlDLFdBQVcsTUFBTSxNQUFLQyxVQUFVLENBQUN2SyxXQUFXO2dCQUFDNEksYUFBYTtnQkFBTzdILE1BQU07ZUFBNEJzSjtZQUN2RyxPQUFPQyxTQUFTRSxrQkFBa0I7UUFDcEM7O0lBRUE7Ozs7O0dBS0MsR0FDRCxBQUFNQyxhQUFhekssU0FBUyxFQUFFcUssT0FBTzs7ZUFBckMsb0JBQUE7WUFDRSxJQUFJQyxXQUFXLE1BQU0sTUFBS0MsVUFBVSxDQUFDdkssV0FBVztnQkFBQzRJLGFBQWE7Z0JBQU83SCxNQUFNO2VBQW1Cc0o7WUFDOUYsT0FBT0MsU0FBU0ksU0FBUztRQUMzQjs7SUFFQTs7Ozs7O0dBTUMsR0FDRCxBQUFNQyxZQUFZM0ssU0FBUyxFQUFFNEssVUFBVSxFQUFFUCxPQUFPOztlQUFoRCxvQkFBQTtZQUNFLElBQUlDLFdBQVcsTUFBTSxNQUFLQyxVQUFVLENBQUN2SyxXQUFXO2dCQUFDNEksYUFBYTtnQkFBTzdILE1BQU07Z0JBQWU2SjtlQUFlUDtZQUN6RyxPQUFPQyxTQUFTTyxRQUFRO1FBQzFCOztJQUVBOzs7OztHQUtDLEdBQ0QsQUFBTUMsVUFBVTlLLFNBQVMsRUFBRXFLLE9BQU87O2VBQWxDLG9CQUFBO1lBQ0UsSUFBSUMsV0FBVyxNQUFNLE1BQUtDLFVBQVUsQ0FBQ3ZLLFdBQVc7Z0JBQUM0SSxhQUFhO2dCQUFPN0gsTUFBTTtlQUFnQnNKO1lBQzNGLE9BQU9DLFNBQVNTLE1BQU07UUFDeEI7O0lBRUE7Ozs7OztHQU1DLEdBQ0QsQUFBTUMsU0FBU2hMLFNBQVMsRUFBRWlMLE9BQU8sRUFBRVosT0FBTzs7ZUFBMUMsb0JBQUE7WUFDRSxJQUFJQyxXQUFXLE1BQU0sTUFBS0MsVUFBVSxDQUFDdkssV0FBVztnQkFBQzRJLGFBQWE7Z0JBQU83SCxNQUFNO2dCQUFZa0s7ZUFBWVo7WUFDbkcsT0FBT0MsU0FBU1ksS0FBSztRQUN2Qjs7SUFFQTs7Ozs7O0dBTUMsR0FFRDs7Ozs7R0FLQyxHQUNELEFBQU1DLHlCQUF5Qm5MLFNBQVMsRUFBRW9MLE1BQU07O2VBQWhELG9CQUFBO1lBQ0UsSUFBSWQsV0FBVyxNQUFNLE1BQUtDLFVBQVUsQ0FBQ3ZLLFdBQVc7Z0JBQUM0SSxhQUFhO2dCQUFPN0gsTUFBTTtnQkFBNEJxSztZQUFNO1lBQzdHLE9BQU87Z0JBQ0xDLGVBQWVmLFNBQVNlLGFBQWE7Z0JBQ3JDQyxlQUFlaEIsU0FBU2dCLGFBQWE7WUFDdkM7UUFDRjs7SUFFQTs7Ozs7R0FLQyxHQUNELEFBQU1DLDJCQUEyQnZMLFNBQVMsRUFBRTRLLFVBQVU7O2VBQXRELG9CQUFBO1lBQ0UsSUFBSU4sV0FBVyxNQUFNLE1BQUtDLFVBQVUsQ0FBQ3ZLLFdBQVc7Z0JBQUM0SSxhQUFhO2dCQUFPN0gsTUFBTTtnQkFDekU2SjtZQUFVO1lBQ1osT0FBTztnQkFDTFMsZUFBZWYsU0FBU2UsYUFBYTtnQkFDckNDLGVBQWVoQixTQUFTZ0IsYUFBYTtZQUN2QztRQUNGOztJQUVBOzs7Ozs7OztHQVFDLEdBQ0QsQUFBTUUsNEJBQTRCeEwsU0FBUyxFQUFFeUwsU0FBUyxFQUFFQyxPQUFPLEVBQUVDLFNBQVMsQ0FBQyxFQUFFQyxRQUFRLElBQUk7O2VBQXpGLG9CQUFBO1lBQ0UsSUFBSXRCLFdBQVcsTUFBTSxNQUFLQyxVQUFVLENBQUN2SyxXQUFXO2dCQUFDNEksYUFBYTtnQkFBTzdILE1BQU07Z0JBQ3pFMEs7Z0JBQVdDO2dCQUFTQztnQkFBUUM7WUFBSztZQUNuQyxPQUFPO2dCQUNMUCxlQUFlZixTQUFTZSxhQUFhO2dCQUNyQ0MsZUFBZWhCLFNBQVNnQixhQUFhO1lBQ3ZDO1FBQ0Y7O0lBRUE7Ozs7OztHQU1DLEdBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBeUNDLEdBRUQ7Ozs7O0dBS0MsR0FDRCxBQUFNTyxpQkFBaUI3TCxTQUFTLEVBQUVvTCxNQUFNOztlQUF4QyxvQkFBQTtZQUNFLElBQUlkLFdBQVcsTUFBTSxNQUFLQyxVQUFVLENBQUN2SyxXQUFXO2dCQUFDNEksYUFBYTtnQkFBTzdILE1BQU07Z0JBQW9CcUs7WUFBTTtZQUNyRyxPQUFPO2dCQUNMVSxPQUFPeEIsU0FBU3dCLEtBQUs7Z0JBQ3JCUixlQUFlaEIsU0FBU2dCLGFBQWE7WUFDdkM7UUFDRjs7SUFFQTs7Ozs7R0FLQyxHQUNELEFBQU1TLG1CQUFtQi9MLFNBQVMsRUFBRTRLLFVBQVU7O2VBQTlDLG9CQUFBO1lBQ0UsSUFBSU4sV0FBVyxNQUFNLE1BQUtDLFVBQVUsQ0FBQ3ZLLFdBQVc7Z0JBQUM0SSxhQUFhO2dCQUFPN0gsTUFBTTtnQkFBc0I2SjtZQUFVO1lBQzNHLE9BQU87Z0JBQ0xrQixPQUFPeEIsU0FBU3dCLEtBQUs7Z0JBQ3JCUixlQUFlaEIsU0FBU2dCLGFBQWE7WUFDdkM7UUFDRjs7SUFFQTs7Ozs7Ozs7R0FRQyxHQUNELEFBQU1VLG9CQUFvQmhNLFNBQVMsRUFBRXlMLFNBQVMsRUFBRUMsT0FBTyxFQUFFQyxTQUFTLENBQUMsRUFBRUMsUUFBUSxJQUFJOztlQUFqRixvQkFBQTtZQUNFLElBQUl0QixXQUFXLE1BQU0sTUFBS0MsVUFBVSxDQUFDdkssV0FBVztnQkFBQzRJLGFBQWE7Z0JBQU83SCxNQUFNO2dCQUF1QjBLO2dCQUNoR0M7Z0JBQVNDO2dCQUFRQztZQUFLO1lBQ3hCLE9BQU87Z0JBQ0xFLE9BQU94QixTQUFTd0IsS0FBSztnQkFDckJSLGVBQWVoQixTQUFTZ0IsYUFBYTtZQUN2QztRQUNGOztJQUVBOzs7O0dBSUMsR0FDRFcsa0JBQWtCak0sU0FBUyxFQUFFO1FBQzNCLE9BQU8sSUFBSSxDQUFDdUssVUFBVSxDQUFDdkssV0FBVztZQUFDZSxNQUFNO1FBQW1CO0lBQzlEO0lBRUE7Ozs7Ozs7Ozs7Ozs7OztHQWVDLEdBRUQ7Ozs7Ozs7O0dBUUMsR0FDRCxzQ0FBc0M7SUFDaENtTCxNQUFNbE0sU0FBUyxFQUFFa00sS0FBSyxFQUFFdEQsV0FBVyxFQUFFdUQsV0FBVzs7ZUFBdEQsb0JBQUE7WUFDRSxJQUFJN0I7WUFDSixJQUFJMUIsZ0JBQWdCLE9BQU87Z0JBQ3pCMEIsV0FBVyxNQUFNLE1BQUtDLFVBQVUsQ0FBQ3ZLLFdBQVc7b0JBQUNlLE1BQU07b0JBQVNtTDtvQkFBT3REO2dCQUFXO1lBQ2hGLE9BQU87Z0JBQ0wwQixXQUFXLE1BQU0sTUFBSzhCLHNCQUFzQixDQUFDcE0sV0FBVztvQkFBQ2UsTUFBTTtvQkFBU21MO29CQUN0RXRELGFBQWFBLGVBQWUsTUFBS3lELFlBQVk7b0JBQUUxRSxXQUFXckosYUFBYXVILFFBQVEsQ0FBQztnQkFBRyxHQUFHc0c7WUFDMUY7WUFDQTdCLFNBQVNBLFFBQVEsR0FBR0EsU0FBU0EsUUFBUSxJQUFJLENBQUM7WUFDMUNBLFNBQVNBLFFBQVEsQ0FBQ2dDLFVBQVUsR0FBR2hDLFNBQVNBLFFBQVEsQ0FBQ2dDLFVBQVUsSUFBSWhDLFNBQVNBLFFBQVEsQ0FBQ2lDLFdBQVc7WUFDNUZqQyxTQUFTQSxRQUFRLENBQUNrQyxXQUFXLEdBQUdsQyxTQUFTQSxRQUFRLENBQUNrQyxXQUFXLEtBQUszSixZQUFZeUgsU0FBU0EsUUFBUSxDQUFDa0MsV0FBVyxHQUN6R2xDLFNBQVNBLFFBQVEsQ0FBQ2xELEtBQUs7WUFDekIsSUFBSTtnQkFBQztnQkFBZ0I7Z0JBQXdCO2dCQUFzQjtnQkFDakU7YUFBMkIsQ0FBQ3hFLFFBQVEsQ0FBQzBILFNBQVNBLFFBQVEsQ0FBQ2dDLFVBQVUsSUFBSWhDLFNBQVNBLFFBQVEsQ0FBQ2lDLFdBQVcsR0FBRztnQkFDckcsT0FBT2pDLFNBQVNBLFFBQVE7WUFDMUIsT0FBTztnQkFDTCxNQUFNLElBQUlwTCxXQUFXb0wsU0FBU0EsUUFBUSxDQUFDbUMsT0FBTyxFQUFFbkMsU0FBU0EsUUFBUSxDQUFDa0MsV0FBVyxFQUFFbEMsU0FBU0EsUUFBUSxDQUFDZ0MsVUFBVTtZQUM3RztRQUNGOztJQUVBOzs7O0dBSUMsR0FDRHRMLGdCQUFnQmhCLFNBQVMsRUFBRWlDLGNBQWMsRUFBRTtRQUN6QyxJQUFJLENBQUN6QixvQkFBb0IsQ0FBQ2tNLGlCQUFpQixDQUFDMU0sV0FBV2lDO0lBQ3pEO0lBRUE7Ozs7O0dBS0MsR0FDRDBLLFVBQVUzTSxTQUFTLEVBQUVpQyxjQUFjLEVBQUU7UUFDbkMsT0FBTyxJQUFJLENBQUN6QixvQkFBb0IsQ0FBQ21NLFNBQVMsQ0FBQzNNLFdBQVdpQztJQUN4RDtJQUVBOzs7Ozs7Ozs7Ozs7R0FZQyxHQUNELEFBQU0ySyxZQUNKNU0sU0FBUyxFQUFFQyxhQUFhLEVBQUU0SSxJQUFJLEVBQUVGLGlCQUFpQixFQUFFa0Usd0JBQXdCLEVBQUVDLGdCQUFnQixFQUFFQyxNQUFNOztlQUR2RyxvQkFBQTtZQUdFLElBQUksTUFBS0MsMkJBQTJCLENBQUNoTixXQUFXQyxtQkFBbUI0QyxXQUFXO2dCQUM1RSxNQUFLL0IsT0FBTyxDQUFDK0MsS0FBSyxDQUFDLENBQUMsRUFBRTdELFVBQVUsQ0FBQyxFQUFFQyxjQUFjLHlDQUF5QyxDQUFDO2dCQUMzRixNQUFNLE1BQUtnTiw4QkFBOEIsQ0FBQ2pOLFdBQVdDO1lBQ3ZEO1lBQ0EsTUFBTWlOLGdCQUFnQixNQUFLRiwyQkFBMkIsQ0FBQ2hOLFdBQVdDLGVBQWU4Rix3QkFBd0I7WUFDekcsTUFBS29ILHNCQUFzQixDQUFDeEUsa0JBQWtCLEdBQUdvRTtZQUNqRCxNQUFLSSxzQkFBc0IsQ0FBQ3hFLGtCQUFrQixDQUFDeUUsV0FBVyxHQUFHekosS0FBS0MsR0FBRztZQUNyRSxPQUFPc0osY0FBY0csbUJBQW1CLENBQUNyTixXQUFXO2dCQUFDMkgsV0FBV2dCO2dCQUFtQjJFLFNBQVM7Z0JBQzFGdk0sTUFBTTtnQkFBZThMO2dCQUEwQkM7Z0JBQWtCN007Z0JBQWU0STtZQUFJLEdBQUdrRTtRQUMzRjs7SUFFQTs7Ozs7Ozs7R0FRQyxHQUNEUSxpQkFBaUJ2TixTQUFTLEVBQUVpQyxjQUFjLEVBQUV1TCxrQkFBa0IsRUFBRUMsZ0JBQWdCLEVBQUU3RSxXQUFZLEVBQUU7UUFDOUYsT0FBTyxJQUFJLENBQUMyQixVQUFVLENBQUN2SyxXQUFXO1lBQUNlLE1BQU07WUFBb0J5TTtZQUFvQkM7WUFDL0V4TixlQUFlZ0M7WUFBZ0IyRyxhQUFhQSxlQUFlLElBQUksQ0FBQ3lELFlBQVk7UUFBQSxHQUM5RW9CLG1CQUFtQjtJQUNyQjtJQUVBOzs7Ozs7Ozs7R0FTQyxHQUVEOzs7Ozs7O0dBT0MsR0FDREMsc0JBQXNCMU4sU0FBUyxFQUFFMk4sTUFBTSxFQUFFQyxhQUFhLEVBQUV6QixXQUFXLEVBQUU7UUFDbkUsT0FBTyxJQUFJLENBQUNDLHNCQUFzQixDQUFDcE0sV0FDakM7WUFBQ2UsTUFBTTtZQUF5QjRNO1lBQVFDO1FBQWEsR0FBR3pCO0lBQzVEO0lBRUE7Ozs7O0dBS0MsR0FDRDBCLCtCQUErQjdOLFNBQVMsRUFBRWlDLGNBQWMsRUFBRTJMLGFBQWEsRUFBRTtRQUN2RSxPQUFPLElBQUksQ0FBQ3JELFVBQVUsQ0FBQ3ZLLFdBQVc7WUFBQ2UsTUFBTTtZQUFrQzZNO1lBQ3pFM04sZUFBZWdDO1FBQWM7SUFDakM7SUFFQTs7OztHQUlDLEdBRUQ7Ozs7Ozs7R0FPQyxHQUNENkwsMEJBQTBCOU4sU0FBUyxFQUFFMk4sTUFBTSxFQUFFQyxhQUFhLEVBQUV6QixXQUFXLEVBQUU7UUFDdkUsT0FBTyxJQUFJLENBQUNDLHNCQUFzQixDQUFDcE0sV0FBVztZQUFDZSxNQUFNO1lBQTZCNE07WUFBUUM7UUFBYSxHQUNyR3pCO0lBQ0o7SUFFQTs7OztHQUlDLEdBQ0QsQUFBTTRCLFdBQVcvTixTQUFTOztlQUExQixvQkFBQTtZQUNFLElBQUlzSyxXQUFXLE1BQU0sTUFBS0MsVUFBVSxDQUFDdkssV0FBVztnQkFBQzRJLGFBQWE7Z0JBQU83SCxNQUFNO1lBQVk7WUFDdkYsT0FBT3VKLFNBQVMwRCxPQUFPO1FBQ3pCOztJQUVBOzs7OztHQUtDLEdBQ0QsQUFBTUMsdUJBQXVCak8sU0FBUyxFQUFFMk4sTUFBTTs7ZUFBOUMsb0JBQUE7WUFDRSxJQUFJckQsV0FBVyxNQUFNLE1BQUtDLFVBQVUsQ0FBQ3ZLLFdBQVc7Z0JBQUM0SSxhQUFhO2dCQUFPN0gsTUFBTTtnQkFBMEI0TTtZQUFNO1lBQzNHLE9BQU9yRCxTQUFTNEQsYUFBYTtRQUMvQjs7SUFFQTs7Ozs7Ozs7R0FRQyxHQUNELEFBQU1DLGVBQWVuTyxTQUFTLEVBQUUyTixNQUFNLEVBQUVTLG1CQUFtQixLQUFLOztlQUFoRSxvQkFBQTtZQUNFLElBQUk5RCxXQUFXLE1BQU0sTUFBS0MsVUFBVSxDQUFDdkssV0FBVztnQkFBQzRJLGFBQWE7Z0JBQU83SCxNQUFNO2dCQUFrQjRNO2dCQUMzRlM7WUFBZ0I7WUFDbEIsT0FBTzlELFNBQVMrRCxLQUFLO1FBQ3ZCOztJQUVBOzs7Ozs7Ozs7OztHQVdDLEdBQ0QsQUFBTUMsVUFBVXRPLFNBQVMsRUFBRTJOLE1BQU0sRUFBRVksU0FBUyxFQUFFSCxtQkFBbUIsS0FBSzs7ZUFBdEUsb0JBQUE7WUFDRSxJQUFJOUQsV0FBVyxNQUFNLE1BQUtDLFVBQVUsQ0FBQ3ZLLFdBQVc7Z0JBQUM0SSxhQUFhO2dCQUFPN0gsTUFBTTtnQkFBYTRNO2dCQUFRWTtnQkFDOUZIO1lBQWdCO1lBQ2xCLE9BQU85RCxTQUFTa0UsTUFBTTtRQUN4Qjs7SUFFQTs7Ozs7Ozs7R0FRQyxHQUNELEFBQU1DLFFBQVF6TyxTQUFTLEVBQUUyTixNQUFNLEVBQUVTLG1CQUFtQixLQUFLOztlQUF6RCxvQkFBQTtZQUNFLElBQUk5RCxXQUFXLE1BQU0sTUFBS0MsVUFBVSxDQUFDdkssV0FBVztnQkFBQzRJLGFBQWE7Z0JBQU83SCxNQUFNO2dCQUFXNE07Z0JBQVFTO1lBQWdCO1lBQzlHLE9BQU85RCxTQUFTb0UsSUFBSTtRQUN0Qjs7SUFFQTs7Ozs7Ozs7R0FRQyxHQUNELEFBQU1DLFFBQVEzTyxTQUFTLEVBQUUyTixNQUFNLEVBQUVTLG1CQUFtQixLQUFLOztlQUF6RCxvQkFBQTtZQUNFLElBQUk5RCxXQUFXLE1BQU0sTUFBS0MsVUFBVSxDQUFDdkssV0FBVztnQkFBQzRJLGFBQWE7Z0JBQU83SCxNQUFNO2dCQUFXNE07Z0JBQVFTO1lBQWdCO1lBQzlHLE9BQU85RCxTQUFTc0UsSUFBSTtRQUN0Qjs7SUFFQTs7OztHQUlDLEdBQ0QsQUFBTUMscUJBQXFCN08sU0FBUzs7ZUFBcEMsb0JBQUE7WUFDRSxJQUFJc0ssV0FBVyxNQUFNLE1BQUtDLFVBQVUsQ0FBQ3ZLLFdBQVc7Z0JBQUM0SSxhQUFhO2dCQUFPN0gsTUFBTTtZQUFzQjtZQUNqRyxPQUFPdUosU0FBUzBELE9BQU87UUFDekI7O0lBRUE7Ozs7O0dBS0MsR0FDRCxBQUFNYyxvQkFBb0I5TyxTQUFTLEVBQUVnTyxPQUFPOztlQUE1QyxvQkFBQTtZQUNFLElBQUkxRCxXQUFXLE1BQU0sTUFBS0MsVUFBVSxDQUFDdkssV0FBVztnQkFBQzRJLGFBQWE7Z0JBQU83SCxNQUFNO2dCQUF1QmlOO1lBQU87WUFDekcsT0FBTzFELFNBQVN5RSxlQUFlO1FBQ2pDOztJQUVBOzs7OztHQUtDLEdBQ0RDLFdBQVdoUCxTQUFTLEVBQUVpUCxNQUFNLEVBQUU7UUFDNUIsT0FBTyxJQUFJLENBQUMxRSxVQUFVLENBQUN2SyxXQUFXO1lBQUNlLE1BQU07WUFBY2tPO1FBQU07SUFDL0Q7SUFFQTs7OztHQUlDLEdBQ0QsQUFBTXhGLFlBQVl6SixTQUFTOztlQUEzQixvQkFBQTtZQUNFLE1BQU1tQixTQUFTLE1BQUt3QixnQkFBZ0IsQ0FBQzNDO1lBQ3JDLE1BQUtXLGVBQWUsQ0FBQ3VPLGFBQWEsQ0FBQ2xQO1lBQ25Db0MsT0FBT0MsSUFBSSxDQUFDLE1BQUs4TSxhQUFhLEVBQzNCQyxNQUFNLENBQUNoTSxDQUFBQSxNQUFPQSxJQUFJaU0sVUFBVSxDQUFDclAsWUFDN0J1QyxPQUFPLENBQUNhLENBQUFBLE1BQU8sT0FBTyxNQUFLK0wsYUFBYSxDQUFDL0wsSUFBSTtZQUNoRCxJQUFJLE1BQUs5QixnQkFBZ0IsQ0FBQ0gsT0FBTyxFQUFFO2dCQUNqQyxNQUFNZ0UsUUFBUW1LLEdBQUcsQ0FBQ2xOLE9BQU9DLElBQUksQ0FBQyxNQUFLZixnQkFBZ0IsQ0FBQ0gsT0FBTyxFQUFFb08sR0FBRzsrQkFBQyxvQkFBQSxVQUFNdE47d0JBQ3JFLElBQUk7NEJBQ0YsTUFBTSxNQUFLekIsb0JBQW9CLENBQUNpSixXQUFXLENBQUN6SixXQUFXd1AsT0FBT3ZOOzRCQUM5RCxPQUFPLE1BQUtULDBCQUEwQixDQUFDUyxlQUFlLENBQUNqQyxVQUFVO3dCQUNuRSxFQUFFLE9BQU9tSCxLQUFLOzRCQUNaLElBQUksQ0FBRTtnQ0FBQztnQ0FBZ0I7NkJBQWdCLENBQUN2RSxRQUFRLENBQUN1RSxJQUFJc0ksSUFBSSxHQUFJO2dDQUMzRCxNQUFLM08sT0FBTyxDQUFDNEksSUFBSSxDQUFDLENBQUMsRUFBRTFKLFVBQVUsQ0FBQyxFQUFFaUMsZUFBZSx1QkFBdUIsQ0FBQyxFQUFFa0Y7NEJBQzdFO3dCQUNGO29CQUNGO29DQVR1RWxGOzs7O1lBVXpFO1FBQ0Y7O0lBRUE7Ozs7Ozs7R0FPQyxHQUVEOzs7O0dBSUMsR0FDRCxBQUFNeU4sY0FBYzFQLFNBQVM7O2VBQTdCLG9CQUFBO1lBQ0UsSUFBSXNLLFdBQVcsTUFBTSxNQUFLQyxVQUFVLENBQUN2SyxXQUFXO2dCQUFDNEksYUFBYTtnQkFBTzdILE1BQU07WUFBZTtZQUMxRixPQUFPdUosU0FBU3FGLFVBQVU7UUFDNUI7O0lBRUE7Ozs7O0dBS0MsR0FFRDs7Ozs7OztHQU9DLEdBRUQ7Ozs7Ozs7R0FPQyxHQUNELEFBQU1DLGdCQUFnQjVQLFNBQVMsRUFBRTRJLFdBQVcsRUFBRXVELFdBQVcsRUFBRWpCLEtBQUs7O2VBQWhFLG9CQUFBO1lBQ0UsSUFBSVo7WUFDSixJQUFJMUIsZ0JBQWdCLE9BQU87Z0JBQ3pCMEIsV0FBVyxNQUFNLE1BQUtDLFVBQVUsQ0FBQ3ZLLFdBQVc7b0JBQUM0STtvQkFBYTdILE1BQU07b0JBQW1CbUs7Z0JBQUs7WUFDMUYsT0FBTztnQkFDTFosV0FBVyxNQUFNLE1BQUs4QixzQkFBc0IsQ0FBQ3BNLFdBQVc7b0JBQUM0STtvQkFBYTdILE1BQU07b0JBQW1CbUs7Z0JBQUssR0FDbEdpQjtZQUNKO1lBQ0EsT0FBTzdCLFNBQVN1RixNQUFNO1FBQ3hCOztJQUVBOzs7O0dBSUMsR0FDRCxBQUFNQyx5QkFBeUI5UCxTQUFTLEVBQUVtQixNQUFNOztlQUFoRCxvQkFBQTtZQUNFLE1BQU00TyxzQkFBc0IsRUFBRTtZQUM5QixLQUFLLElBQUk5SCxZQUFZLE1BQUtpQix5QkFBeUIsQ0FBQ2xKLFVBQVUsSUFBSSxFQUFFLENBQUU7Z0JBQ3BFK1Asb0JBQW9Cak4sSUFBSSxDQUFDLE1BQ3RCa04sYUFBYSxDQUFDLElBQU0vSCxTQUFTZ0ksbUJBQW1CLENBQUM5TyxTQUFTLENBQUMsRUFBRW5CLFVBQVUsQ0FBQyxFQUFFbUIsT0FBTyxvQkFBb0IsQ0FBQyxFQUFFLE1BQ3hHbUgsS0FBSyxDQUFDbkIsQ0FBQUEsTUFBTyxNQUFLckcsT0FBTyxDQUFDc0csS0FBSyxDQUM5QixDQUFDLEVBQUVwSCxVQUFVLENBQUMsRUFBRW1CLE9BQU8sMkRBQTJELENBQUMsRUFBRWdHO1lBRzNGO1lBQ0EsTUFBTWhDLFFBQVFtSyxHQUFHLENBQUNTO1FBQ3BCOztJQUVBOzs7O0dBSUMsR0FDREcsMkJBQTJCbFEsU0FBUyxFQUFFaUksUUFBUSxFQUFFO1FBQzlDLElBQUksQ0FBQ25ILE9BQU8sQ0FBQzBILEtBQUssQ0FBQyxDQUFDLEVBQUV4SSxVQUFVLGdDQUFnQyxDQUFDO1FBQ2pFLElBQUltUSxZQUFZLElBQUksQ0FBQ2pILHlCQUF5QixDQUFDbEosVUFBVTtRQUN6RCxJQUFJLENBQUNtUSxXQUFXO1lBQ2RBLFlBQVksRUFBRTtZQUNkLElBQUksQ0FBQ2pILHlCQUF5QixDQUFDbEosVUFBVSxHQUFHbVE7UUFDOUM7UUFDQUEsVUFBVXJOLElBQUksQ0FBQ21GO0lBQ2pCO0lBRUE7Ozs7R0FJQyxHQUNEbUksOEJBQThCcFEsU0FBUyxFQUFFaUksUUFBUSxFQUFFO1FBQ2pELElBQUksQ0FBQ25ILE9BQU8sQ0FBQzBILEtBQUssQ0FBQyxDQUFDLEVBQUV4SSxVQUFVLGtDQUFrQyxDQUFDO1FBQ25FLElBQUltUSxZQUFZLElBQUksQ0FBQ2pILHlCQUF5QixDQUFDbEosVUFBVTtRQUN6RCxJQUFJLENBQUNtUSxXQUFXO1lBQ2RBLFlBQVksRUFBRTtRQUNoQjtRQUNBQSxZQUFZQSxVQUFVZixNQUFNLENBQUNpQixDQUFBQSxJQUFLQSxNQUFNcEk7UUFDeEMsSUFBSSxDQUFDaUIseUJBQXlCLENBQUNsSixVQUFVLEdBQUdtUTtJQUM5QztJQUVBOzs7R0FHQyxHQUNERyxtQkFBbUJySSxRQUFRLEVBQUU7UUFDM0IsSUFBSSxDQUFDQyxpQkFBaUIsQ0FBQ3BGLElBQUksQ0FBQ21GO0lBQzlCO0lBRUE7OztHQUdDLEdBQ0RzSSxzQkFBc0J0SSxRQUFRLEVBQUU7UUFDOUIsSUFBSSxDQUFDQyxpQkFBaUIsR0FBRyxJQUFJLENBQUNBLGlCQUFpQixDQUFDa0gsTUFBTSxDQUFDaUIsQ0FBQUEsSUFBS0EsTUFBTXBJO0lBQ3BFO0lBRUE7Ozs7R0FJQyxHQUNEdUkscUJBQXFCdkksUUFBUSxFQUFFakksU0FBUyxFQUFFO1FBQ3hDLElBQUksQ0FBQ3lRLG1CQUFtQixDQUFDM04sSUFBSSxDQUFDO1lBQUM5QztZQUFXaUk7UUFBUTtJQUNwRDtJQUVBOzs7R0FHQyxHQUNEeUksd0JBQXdCekksUUFBUSxFQUFFO1FBQ2hDLElBQUksQ0FBQ3dJLG1CQUFtQixHQUFHLElBQUksQ0FBQ0EsbUJBQW1CLENBQUNyQixNQUFNLENBQUNpQixDQUFBQSxJQUFLQSxFQUFFcEksUUFBUSxLQUFLQTtJQUNqRjtJQUVBOztHQUVDLEdBQ0QwSSxxQkFBcUI7UUFDbkIsSUFBSSxDQUFDekgseUJBQXlCLEdBQUcsQ0FBQztRQUNsQyxJQUFJLENBQUN1SCxtQkFBbUIsR0FBRyxFQUFFO0lBQy9CO0lBRUE7OztHQUdDLEdBQ0RHLGlCQUFpQjVRLFNBQVMsRUFBRTtRQUMxQixJQUFJLENBQUNRLG9CQUFvQixDQUFDcVEsYUFBYSxDQUFDN1E7UUFDeEMsSUFBSSxDQUFDVyxlQUFlLENBQUN1TyxhQUFhLENBQUNsUDtRQUNuQyxNQUFNOFEsa0JBQWtCLElBQUksQ0FBQ3ZRLG9CQUFvQixDQUFDUCxVQUFVO1FBQzVELElBQUk4USxpQkFBaUI7WUFDbkIsSUFBSUEsb0JBQW9COVEsV0FBVztnQkFDakMsTUFBTStRLGFBQWEsSUFBSSxDQUFDclAsZ0JBQWdCLENBQUNvUCxnQkFBZ0I7Z0JBQ3pELE1BQU12TixXQUFXbkIsT0FBT3lILE1BQU0sQ0FBTWtIO2dCQUNwQzNPLE9BQU9DLElBQUksQ0FBQyxJQUFJLENBQUMyTyw0QkFBNEIsRUFDMUM1QixNQUFNLENBQUNwTSxDQUFBQSxXQUFZQSxTQUFTcU0sVUFBVSxDQUFDLENBQUMsRUFBRXlCLGdCQUFnQixDQUFDLEdBQzNEdk8sT0FBTyxDQUFDUyxDQUFBQSxXQUFZLE9BQU8sSUFBSSxDQUFDZ08sNEJBQTRCLENBQUNoTyxTQUFTO2dCQUN6RU8sU0FBU2hCLE9BQU8sQ0FBQzBPLENBQUFBO29CQUNmN08sT0FBT3lILE1BQU0sQ0FBQyxJQUFJLENBQUNySSwwQkFBMEIsRUFBRWUsT0FBTyxDQUFDUyxDQUFBQSxXQUFZLE9BQU9BLFFBQVEsQ0FBQ2lPLFFBQVE7b0JBQzNGLE9BQU8sSUFBSSxDQUFDMVEsb0JBQW9CLENBQUMwUSxRQUFRO29CQUN6QyxPQUFPLElBQUksQ0FBQzVOLGtCQUFrQixDQUFDNE4sUUFBUTtnQkFDekM7Z0JBQ0EsT0FBTyxJQUFJLENBQUN2UCxnQkFBZ0IsQ0FBQ29QLGdCQUFnQjtnQkFDN0MsSUFBSSxDQUFDaFEsT0FBTyxDQUFDK0MsS0FBSyxDQUFDLENBQUMsRUFBRWlOLGdCQUFnQixtQ0FBbUMsQ0FBQztZQUM1RSxPQUFPO2dCQUNMMU8sT0FBT3lILE1BQU0sQ0FBQyxJQUFJLENBQUNySSwwQkFBMEIsRUFBRWUsT0FBTyxDQUFDUyxDQUFBQSxXQUFZLE9BQU9BLFFBQVEsQ0FBQ2hELFVBQVU7Z0JBQzdGLE1BQU0rUSxhQUFhLElBQUksQ0FBQzFOLGtCQUFrQixDQUFDckQsVUFBVTtnQkFDckQsSUFBSStRLFlBQVk7b0JBQ2QsTUFBTTVQLFNBQVM0UCxXQUFXNVAsTUFBTTtvQkFDaENpQixPQUFPQyxJQUFJLENBQUMsSUFBSSxDQUFDMk8sNEJBQTRCLEVBQzFDNUIsTUFBTSxDQUFDcE0sQ0FBQUEsV0FBWUEsU0FBU3FNLFVBQVUsQ0FBQyxDQUFDLEVBQUV5QixnQkFBZ0IsQ0FBQyxFQUFFM1AsT0FBTyxDQUFDLEdBQ3JFb0IsT0FBTyxDQUFDUyxDQUFBQSxXQUFZLE9BQU8sSUFBSSxDQUFDZ08sNEJBQTRCLENBQUNoTyxTQUFTO29CQUN6RSxPQUFPLElBQUksQ0FBQ3RCLGdCQUFnQixDQUFDb1AsZ0JBQWdCLENBQUMzUCxPQUFPO29CQUNyRCxJQUFJLENBQUNMLE9BQU8sQ0FBQytDLEtBQUssQ0FBQyxDQUFDLEVBQUVpTixnQkFBZ0IsK0JBQStCLEVBQUU5USxVQUFVLENBQUM7Z0JBQ3BGO2dCQUNBLE9BQU8sSUFBSSxDQUFDTyxvQkFBb0IsQ0FBQ1AsVUFBVTtnQkFDM0MsT0FBTyxJQUFJLENBQUNxRCxrQkFBa0IsQ0FBQ3JELFVBQVU7WUFDM0M7UUFDRjtJQUNGO0lBRUE7OztHQUdDLEdBQ0QySixZQUFZMUcsTUFBNkMsRUFBRTdDLE1BQU0sRUFBRTtRQUNqRSxNQUFNSixZQUFZSSxPQUFPSixTQUFTO1FBQ2xDLE1BQU1rUixVQUFVLElBQUksQ0FBQ25ILGNBQWMsQ0FBQ29ILFlBQVksQ0FBc0I7WUFDcEVuUixXQUFXSSxPQUFPSixTQUFTO1lBQzNCNkksTUFBTXpJLE9BQU95SSxJQUFJO1lBQ2pCNUksZUFBZUcsT0FBT0gsYUFBYTtZQUNuQ3dJLGdCQUFnQnJJLE9BQU9xSSxjQUFjO1lBQ3JDQyxtQkFBbUJ0SSxPQUFPc0ksaUJBQWlCO1lBQzNDQyxtQkFBbUJ2SSxPQUFPdUksaUJBQWlCO1lBQzNDNUgsTUFBTVgsT0FBT1csSUFBSTtZQUNqQmtDO1lBQVE3QztRQUNWLEdBQUdnUCxNQUFNLENBQUNnQyxDQUFBQSxJQUFLQSxFQUFFclEsSUFBSSxLQUFLO1FBQzFCLElBQUksSUFBSSxDQUFDc1EsMEJBQTBCLElBQUlqUixPQUFPcUksY0FBYyxLQUFLNUYsV0FBVztZQUMxRSxNQUFNeU8sU0FBU0osUUFBUTNCLEdBQUcsQ0FBQyxDQUFDZ0MsT0FBOEIsSUFDeEQsSUFBSSxDQUFDQyw2QkFBNkIsQ0FBQ0QsS0FBS3RPLE1BQU0sRUFBRXNPLEtBQUtuUixNQUFNO1lBRTdELElBQUksQ0FBQyxJQUFJLENBQUNxUixZQUFZLENBQUN6UixVQUFVLEVBQUU7Z0JBQ2pDLElBQUksQ0FBQ3lSLFlBQVksQ0FBQ3pSLFVBQVUsR0FBR3NSO2dCQUMvQixJQUFJLENBQUNJLGtCQUFrQixDQUFDMVI7WUFDMUIsT0FBTztnQkFDTCxJQUFJLENBQUN5UixZQUFZLENBQUN6UixVQUFVLEdBQUcsSUFBSSxDQUFDeVIsWUFBWSxDQUFDelIsVUFBVSxDQUFDMlIsTUFBTSxDQUFDTDtZQUNyRTtRQUNGLE9BQU87WUFDTEosUUFBUTNPLE9BQU8sQ0FBQyxDQUFDZ1AsT0FBOEIsSUFBSSxDQUFDQyw2QkFBNkIsQ0FBQ0QsS0FBS3RPLE1BQU0sRUFBRXNPLEtBQUtuUixNQUFNO1FBQzVHO0lBQ0Y7SUFFQTs7Ozs7R0FLQyxHQUNEd1IsV0FBVzVSLFNBQVMsRUFBRXlQLElBQUksRUFBRW9DLFFBQVEsRUFBRTtRQUNwQyxJQUFJQyxRQUFRLElBQU0sSUFBSSxDQUFDOUIsYUFBYSxDQUFDNkIsVUFBVSxDQUFDLEVBQUU3UixVQUFVLENBQUMsRUFBRXlQLEtBQUssQ0FBQztRQUNyRSxJQUFJLElBQUksQ0FBQzRCLDBCQUEwQixFQUFFO1lBQ25DLElBQUksQ0FBQyxJQUFJLENBQUNJLFlBQVksQ0FBQ3pSLFVBQVUsRUFBRTtnQkFDakMsSUFBSSxDQUFDeVIsWUFBWSxDQUFDelIsVUFBVSxHQUFHO29CQUFDOFI7aUJBQU07Z0JBQ3RDLElBQUksQ0FBQ0osa0JBQWtCLENBQUMxUjtZQUMxQixPQUFPO2dCQUNMLElBQUksQ0FBQ3lSLFlBQVksQ0FBQ3pSLFVBQVUsQ0FBQzhDLElBQUksQ0FBQ2dQO1lBQ3BDO1FBQ0YsT0FBTztZQUNMQTtRQUNGO0lBQ0Y7SUFFTUosbUJBQW1CMVIsU0FBUzs7ZUFBbEMsb0JBQUE7WUFDRSxJQUFJLE1BQUt5UixZQUFZLENBQUN6UixVQUFVLEVBQUU7Z0JBQ2hDLE1BQU0sTUFBS3lSLFlBQVksQ0FBQ3pSLFVBQVUsQ0FBQ2EsTUFBTSxDQUFFO29CQUN6QyxNQUFNLE1BQUs0USxZQUFZLENBQUN6UixVQUFVLENBQUMsRUFBRTtvQkFDckMsTUFBS3lSLFlBQVksQ0FBQ3pSLFVBQVUsQ0FBQytSLEtBQUs7Z0JBQ3BDO2dCQUNBLE9BQU8sTUFBS04sWUFBWSxDQUFDelIsVUFBVTtZQUNyQztRQUNGOztJQUVNMkUsV0FBVzFDLGNBQWMsRUFBRUMsbUJBQW1CLEVBQUVmLE1BQU07O2VBQTVELG9CQUFBO1lBQ0UsTUFBTTZCLFdBQVcsTUFBSzNCLGVBQWUsQ0FBQ0YsT0FBTyxDQUFDYyxlQUFlLENBQUNDLG9CQUFvQjtZQUNsRixJQUFJYyxVQUFVO2dCQUNaLE1BQU8sQ0FBQ0EsU0FBU0MsTUFBTSxDQUFDRixTQUFTLElBQUksQ0FBQ0MsU0FBUzhDLGNBQWMsSUFBSTlDLFNBQVNELFNBQVMsQ0FBRTtvQkFDbkYsTUFBTSxNQUFLaVAsYUFBYSxDQUFDL1AsZ0JBQWdCQyxxQkFBcUJmO2dCQUNoRTtZQUNGO1FBQ0Y7O0lBRUE2USxjQUFjL1AsY0FBYyxFQUFFQyxtQkFBbUIsRUFBRWYsTUFBTSxFQUFFO1FBQ3pELE1BQU02QixXQUFXLElBQUksQ0FBQzNCLGVBQWUsQ0FBQ0YsT0FBTyxDQUFDYyxlQUFlLENBQUNDLG9CQUFvQjtRQUNsRmMsU0FBU3VDLGlCQUFpQixHQUFHUixLQUFLa04sR0FBRyxDQUFDalAsU0FBU3VDLGlCQUFpQixHQUFHLEdBQUc7O1FBQ3RFLE9BQU8sSUFBSUosUUFBYyxDQUFDRixVQUFZaU4seUJBQVcsb0JBQUE7Z0JBQy9DLElBQUksQ0FBQ2xQLFNBQVNDLE1BQU0sQ0FBQ0YsU0FBUyxJQUFJLENBQUNDLFNBQVM4QyxjQUFjLElBQUk5QyxTQUFTRCxTQUFTLEVBQUU7b0JBQ2hGLElBQUk7d0JBQ0ZDLFNBQVM0QyxTQUFTLEdBQUd0SCxhQUFhdUgsUUFBUSxDQUFDO3dCQUMzQyxNQUFNZixXQUFXQyxLQUFLQyxNQUFNO3dCQUM1QmhDLFNBQVNDLE1BQU0sQ0FBQ3lCLEtBQUs7d0JBQ3JCMUIsU0FBU0MsTUFBTSxDQUFDa1AsRUFBRSxDQUFDQyxJQUFJLENBQUN4TCxZQUFZLENBQUMsWUFBWSxHQUFHOUI7d0JBQ3BEOUIsU0FBU0MsTUFBTSxDQUFDa1AsRUFBRSxDQUFDQyxJQUFJLENBQUN2TCxLQUFLLENBQUMvQixRQUFRLEdBQUdBO3dCQUN6QzlCLFNBQVM4QyxjQUFjLEdBQUc7d0JBQzFCOUMsU0FBU0MsTUFBTSxDQUFDa1AsRUFBRSxDQUFDRSxHQUFHLEdBQUcsTUFBTSxNQUFLbE0sYUFBYSxDQUFDbEUsZ0JBQWdCQyxxQkFBcUJmO3dCQUN2RjZCLFNBQVNDLE1BQU0sQ0FBQzRCLE9BQU87b0JBQ3pCLEVBQUUsT0FBT3VDLE9BQU87d0JBQ2RwRSxTQUFTOEMsY0FBYyxHQUFHO29CQUM1QjtnQkFDRjtnQkFDQWI7WUFDRixJQUFHakMsU0FBU3VDLGlCQUFpQjtJQUMvQjtJQUVBOzs7Ozs7R0FNQyxHQUNELEFBQU02Ryx1QkFBdUJwTSxTQUFTLEVBQUVzUyxPQUFPLEVBQUVuRyxXQUFZLEVBQUVzQixnQkFBaUI7O2VBQWhGLG9CQUFBO1lBQ0UsSUFBSXRCLGdCQUFnQixRQUFRO2dCQUMxQixJQUFJO29CQUNGLE9BQU8sTUFBTTVNLElBQUk7d0JBQUM7d0JBQUc7cUJBQUUsQ0FBQ2dRLEdBQUcsQ0FBQ3ROLENBQUFBO3dCQUMxQixPQUFPLE1BQUtzSSxVQUFVLENBQUN2SyxXQUFXb0MsT0FBT21RLE1BQU0sQ0FBQyxDQUFDLEdBQUdELFNBQ2xEOzRCQUFDclMsZUFBZWdDO3dCQUFjLElBQUl3TDtvQkFDdEM7Z0JBQ0YsRUFBRSxPQUFPckcsT0FBTztvQkFDZCxNQUFNQSxNQUFNb0wsTUFBTSxDQUFDLEVBQUU7Z0JBQ3ZCO1lBQ0YsT0FBTztnQkFDTCxPQUFPLE1BQU0sTUFBS2pJLFVBQVUsQ0FBQ3ZLLFdBQVdzUyxTQUFTN0U7WUFDbkQ7UUFDRjs7SUFFQTs7Ozs7R0FLQyxHQUNELHFEQUFxRDtJQUMvQ2xELFdBQVd2SyxTQUFpQixFQUFFc1MsT0FBTyxFQUFFN0UsZ0JBQWlCOztlQUE5RCxvQkFBQTtnQkF1QkUsa0NBQWdDeEwsaUJBQ2hDLHdCQUFzQmQsU0FDdEIsK0JBQThCYztZQXhCOUIsTUFBTXdRLHNCQUFzQjtnQkFBQztnQkFBYTtnQkFBZTtnQkFBa0M7YUFBYztZQUN6RyxNQUFNblMsbUJBQW1CLE1BQUtDLG9CQUFvQixDQUFDUCxVQUFVO1lBQzdELElBQUkwUyxvQkFBb0IsTUFBSy9SLGVBQWUsQ0FBQ2dTLHlCQUF5QixDQUFDclMsaUJBQWlCLENBQUMsRUFBRTtZQUMzRixJQUFJLENBQUNtUyxvQkFBb0I3UCxRQUFRLENBQUMwUCxRQUFRdlIsSUFBSSxHQUFHO2dCQUMvQzJSLHNCQUFBQSxvQkFBc0IsTUFBTSxNQUFLL1IsZUFBZSxDQUFDaVMscUJBQXFCLENBQUM1UztnQkFDdkUsTUFBTTZTLGVBQWVILGtCQUFrQmpRLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRTtnQkFDcER6QyxZQUFZLE1BQUswQixnQkFBZ0IsQ0FBQ3BCLGlCQUFpQixDQUFDdVMsYUFBYTtZQUNuRTtZQUNBLElBQUkzUSxzQkFBc0I7WUFDMUIsSUFBSUQsaUJBQWlCO1lBQ3JCLE1BQU1kLFNBQVMsTUFBS3dCLGdCQUFnQixDQUFDM0M7WUFDckMsTUFBSzhTLHFCQUFxQixDQUFDOVM7WUFDM0IsSUFBSXNTLFFBQVFyUyxhQUFhLEtBQUs0QyxXQUFXO2dCQUN2Q1osaUJBQWlCcVEsUUFBUXJTLGFBQWE7WUFDeEMsT0FBTztnQkFDTCxJQUFJeVMsbUJBQW1CO29CQUNyQnpRLGlCQUFpQnVOLE9BQU9rRCxrQkFBa0JqUSxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUU7Z0JBQ3pEO2dCQUNBLElBQUk2UCxRQUFRMUosV0FBVyxLQUFLLE9BQU87b0JBQ2pDMEosVUFBVWxRLE9BQU9tUSxNQUFNLENBQUMsQ0FBQyxHQUFHRCxTQUFTO3dCQUFDclMsZUFBZWdDO29CQUFjO2dCQUNyRTtZQUNGO1lBQ0EsQ0FBQSxtQ0FBQSxNQUFLVCwwQkFBMEIsQ0FBQSxDQUFDUyxrQkFBQUEsZUFBZSxLQUEvQyxnQ0FBK0IsQ0FBQ0EsZ0JBQWUsR0FBSyxDQUFDO1lBQ3JELENBQUEseUJBQUEsTUFBS1gsZ0JBQWdCLENBQUEsQ0FBQ0gsVUFBQUEsT0FBTyxLQUE3QixzQkFBcUIsQ0FBQ0EsUUFBTyxHQUFLLENBQUM7WUFDbkMsQ0FBQSxnQ0FBQSxNQUFLRyxnQkFBZ0IsQ0FBQ0gsT0FBTyxDQUFBLENBQUNjLG1CQUFBQSxlQUFlLEtBQTdDLDZCQUE2QixDQUFDQSxpQkFBZSxHQUFLLEVBQUU7WUFDcEQsSUFBSSxNQUFLVCwwQkFBMEIsQ0FBQ1MsZUFBZSxDQUFDakMsVUFBVSxLQUFLNkMsV0FBVztnQkFDNUVYLHNCQUFzQixNQUFLViwwQkFBMEIsQ0FBQ1MsZUFBZSxDQUFDakMsVUFBVTtZQUNsRixPQUFPO2dCQUNMLE1BQUtjLE9BQU8sQ0FBQytDLEtBQUssQ0FBQyxDQUFDLEVBQUU3RCxVQUFVLENBQUMsRUFBRWlDLGVBQWUseUNBQXlDLENBQUM7Z0JBQzVGLE1BQU0sTUFBS2dMLDhCQUE4QixDQUFDak4sV0FBV2lDO2dCQUNyREMsc0JBQXNCLE1BQUtWLDBCQUEwQixDQUFDUyxlQUFlLENBQUNqQyxVQUFVO1lBQ2xGO1lBQ0EsTUFBTWdELFdBQVcsTUFBSzFCLGdCQUFnQixDQUFDSCxPQUFPLENBQUNjLGVBQWUsQ0FBQ0Msb0JBQW9CO1lBQ25GLElBQUksQ0FBQ2MsU0FBU0QsU0FBUyxFQUFFO2dCQUN2QixNQUFLakMsT0FBTyxDQUFDK0MsS0FBSyxDQUFDLENBQUMsRUFBRTdELFVBQVUsQ0FBQyxFQUFFaUMsZUFBZSwyQ0FBMkMsQ0FBQztnQkFDOUYsTUFBTSxNQUFLNEMsT0FBTyxDQUFDNUMsZ0JBQWdCZDtZQUNyQyxPQUFPLElBQUksQ0FBQyxNQUFLNEIsU0FBUyxDQUFDZCxnQkFBZ0JDLHFCQUFxQmYsU0FBUztnQkFDdkUsTUFBTTZCLFNBQVMyQyxhQUFhO1lBQzlCO1lBQ0EsSUFBSTJNLFFBQVF2UixJQUFJLEtBQUssYUFBYTtnQkFDaEN1UixRQUFRMU0sU0FBUyxHQUFHNUMsU0FBUzRDLFNBQVM7WUFDeEM7WUFDQSxJQUFJO2dCQUFDO2dCQUFTO2FBQVksQ0FBQ2hELFFBQVEsQ0FBQzBQLFFBQVF2UixJQUFJLEdBQUc7Z0JBQ2pELE9BQU8sTUFBS2dTLFlBQVksQ0FBQy9TLFdBQVdpQyxnQkFBZ0JxUSxTQUFTN0U7WUFDL0Q7WUFDQSxJQUFJdUYsZUFBZTtZQUNuQixNQUFPLEtBQU07Z0JBQ1gsSUFBSTtvQkFDRixPQUFPLE1BQU0sTUFBS0QsWUFBWSxDQUFDL1MsV0FBV2lDLGdCQUFnQnFRLFNBQVM3RTtnQkFDckUsRUFBRSxPQUFPdEcsS0FBSztvQkFDWixJQUFJQSxJQUFJc0ksSUFBSSxLQUFLLHdCQUF3Qjt3QkFDdkMsSUFBSXdELG1CQUFtQkQ7d0JBQ3ZCLElBQUlFLGtCQUFrQjt3QkFDdEIsTUFBT0QsbUJBQW1CLE1BQUtFLFFBQVEsQ0FBRTs0QkFDdkNGOzRCQUNBQyxtQkFBbUJuTyxLQUFLa04sR0FBRyxDQUFDbE4sS0FBS3FPLEdBQUcsQ0FBQyxHQUFHSCxvQkFBb0IsTUFBS0ksdUJBQXVCLEVBQ3RGLE1BQUtDLHVCQUF1QixJQUFJO3dCQUNwQzt3QkFDQSxNQUFNQyxZQUFZLElBQUk1UCxLQUFLd0QsSUFBSWhELFFBQVEsQ0FBQ0Usb0JBQW9CLEVBQUVtUCxPQUFPO3dCQUNyRSxJQUFJN1AsS0FBS0MsR0FBRyxLQUFLc1Asa0JBQWtCSyxhQUFhUCxlQUFlLE1BQUtHLFFBQVEsRUFBRTs0QkFDNUUsSUFBSXhQLEtBQUtDLEdBQUcsS0FBSzJQLFdBQVc7Z0NBQzFCLE1BQU0sSUFBSXBPLFFBQVFDLENBQUFBLE1BQU84TSxXQUFXOU0sS0FBS21PLFlBQVk1UCxLQUFLQyxHQUFHOzRCQUMvRDs0QkFDQW9QO3dCQUNGLE9BQU87NEJBQ0wsTUFBTTdMO3dCQUNSO29CQUNGLE9BQU8sSUFBSTt3QkFBQzt3QkFBd0I7d0JBQWdCO3dCQUNsRDtxQkFBZ0IsQ0FBQ3ZFLFFBQVEsQ0FBQ3VFLElBQUlzSSxJQUFJLEtBQ2xDdUQsZUFBZSxNQUFLRyxRQUFRLEVBQUU7d0JBQzlCLE1BQU0sSUFBSWhPLFFBQVFDLENBQUFBLE1BQU84TSxXQUFXOU0sS0FBS0wsS0FBS2tOLEdBQUcsQ0FBQ2xOLEtBQUtxTyxHQUFHLENBQUMsR0FBR0osZ0JBQzVELE1BQUtLLHVCQUF1QixFQUFFLE1BQUtDLHVCQUF1QixJQUFJO3dCQUNoRU47b0JBQ0YsT0FBTzt3QkFDTCxNQUFNN0w7b0JBQ1I7b0JBQ0EsSUFBSSxNQUFLM0YsMEJBQTBCLENBQUNTLGVBQWUsQ0FBQ2pDLFVBQVUsS0FBSzZDLFdBQVc7d0JBQzVFLE1BQU1zRTtvQkFDUjtnQkFDRjtZQUNGO1FBQ0Y7O0lBRVE0TCxhQUFhL1MsU0FBUyxFQUFFaUMsY0FBYyxFQUFFcVEsT0FBTyxFQUFFN0UsZ0JBQWdCLEVBQUU7UUFDekUsTUFBTWhKLGlCQUFpQixJQUFJLENBQUN1SSwyQkFBMkIsQ0FBQ2hOLFdBQVdpQztRQUNuRSxJQUFJMEYsWUFBWTJLLFFBQVEzSyxTQUFTLElBQUlySixhQUFhdUgsUUFBUSxDQUFDO1FBQzNEeU0sUUFBUTFLLFVBQVUsR0FBRztZQUFDNkwseUJBQXlCLElBQUk5UDtRQUFNO1FBQ3pELElBQUl1QixTQUFTQyxRQUFRdU8sSUFBSSxDQUFDO1lBQ3hCLElBQUl2TyxRQUFRLENBQUNGLFNBQVM2QyxTQUFXckQsZUFBZWdCLGVBQWUsQ0FBQ2tDLFVBQVUsR0FDeEU7b0JBQUMxQztvQkFBUzZDO29CQUFRL0csTUFBTXVSLFFBQVF2UixJQUFJO2dCQUFBO1lBQ3RDLElBQUlvRSxRQUFRLENBQUNGLFNBQVM2QyxTQUFXb0ssV0FBVztvQkFDMUNwSyxPQUFPLElBQUl0SixhQUFhLENBQUMsaUNBQWlDLEVBQUU4VCxRQUFRM0ssU0FBUyxDQUFDLFNBQVMsRUFBRTJLLFFBQVF2UixJQUFJLENBQUMsQ0FBQyxDQUFDLEdBQ3RHO29CQUNGLE9BQU8wRCxlQUFlZ0IsZUFBZSxDQUFDa0MsVUFBVTtnQkFDbEQsR0FBRyxBQUFDOEYsbUJBQW1CLFFBQVMsSUFBSSxDQUFDa0csZUFBZTtTQUNyRDtRQUNEckIsUUFBUXRTLFNBQVMsR0FBR0E7UUFDcEJzUyxRQUFRMUosV0FBVyxHQUFHMEosUUFBUTFKLFdBQVcsSUFBSSxJQUFJLENBQUN5RCxZQUFZO1FBQzlELElBQUksQ0FBQ2lHLFFBQVEzSyxTQUFTLEVBQUU7WUFDdEIySyxRQUFRM0ssU0FBUyxHQUFHQTtRQUN0QjtRQUNBLElBQUkySyxRQUFRdlIsSUFBSSxLQUFLLGlCQUFpQnVSLFFBQVExSixXQUFXLEtBQUssU0FDNUQwSixRQUFRclMsYUFBYSxLQUFLd0UsZUFBZXhDLGNBQWMsRUFBRTtZQUN6RCxJQUFJLENBQUNuQixPQUFPLENBQUMrQyxLQUFLLENBQUMsSUFBTSxDQUFDLEVBQUU3RCxVQUFVLG1CQUFtQixFQUFFd0gsS0FBS0UsU0FBUyxDQUFDNEssU0FBUyxDQUFDO1lBQ3BGN04sZUFBZXhCLE1BQU0sQ0FBQzJRLElBQUksQ0FBQyxXQUFXdEI7WUFDdEMsT0FBT3BOO1FBQ1QsT0FBTztZQUNMLElBQUksQ0FBQ3BFLE9BQU8sQ0FBQzBILEtBQUssQ0FBQyxJQUFNLENBQUMsRUFBRXhJLFVBQVUsQ0FBQyxFQUFFc1MsUUFBUXJTLGFBQWEsQ0FBQywrQ0FBK0MsQ0FBQyxHQUM3RyxDQUFDLGdEQUFnRCxFQUFFdUgsS0FBS0UsU0FBUyxDQUFDNEssU0FBUyxDQUFDO1lBQzlFLE9BQU9wTjtRQUNUO0lBQ0Y7SUFFQSxzQ0FBc0M7SUFDdENxRCxjQUFjaEIsSUFBSSxFQUFFO1FBQ2xCLElBQUlBLEtBQUtILEtBQUssS0FBSyxtQkFBbUI7WUFDcEMsT0FBTyxJQUFJM0ksZ0JBQWdCOEksS0FBS2tGLE9BQU8sRUFBRWxGLEtBQUtzTSxPQUFPO1FBQ3ZELE9BQU8sSUFBSXRNLEtBQUtILEtBQUssS0FBSyxpQkFBaUI7WUFDekMsT0FBTyxJQUFJMUksY0FBYzZJLEtBQUtrRixPQUFPO1FBQ3ZDLE9BQU8sSUFBSWxGLEtBQUtILEtBQUssS0FBSyx3QkFBd0I7WUFDaEQsT0FBTyxJQUFJcEkscUJBQXFCdUksS0FBS2tGLE9BQU87UUFDOUMsT0FBTyxJQUFJbEYsS0FBS0gsS0FBSyxLQUFLLGdCQUFnQjtZQUN4QyxPQUFPLElBQUk1SSxhQUFhK0ksS0FBS2tGLE9BQU87UUFDdEMsT0FBTyxJQUFJbEYsS0FBS0gsS0FBSyxLQUFLLHlCQUF5QjtZQUNqRCxPQUFPLElBQUluSSxrQkFBa0JzSSxLQUFLa0YsT0FBTztRQUMzQyxPQUFPLElBQUlsRixLQUFLSCxLQUFLLEtBQUssa0JBQWtCO1lBQzFDLE9BQU8sSUFBSXRJLGVBQWV5SSxLQUFLa0YsT0FBTztRQUN4QyxPQUFPLElBQUlsRixLQUFLSCxLQUFLLEtBQUssY0FBYztZQUN0QyxPQUFPLElBQUlsSSxXQUFXcUksS0FBS2tGLE9BQU8sRUFBRWxGLEtBQUtpRixXQUFXLEVBQUVqRixLQUFLK0UsVUFBVTtRQUN2RSxPQUFPLElBQUkvRSxLQUFLSCxLQUFLLEtBQUsscUJBQXFCO1lBQzdDLElBQUksQ0FBQzFDLEtBQUs7WUFDVixPQUFPLElBQUk5RixrQkFBa0IySSxLQUFLa0YsT0FBTztRQUMzQyxPQUFPLElBQUlsRixLQUFLSCxLQUFLLEtBQUssd0JBQXdCO1lBQ2hELE9BQU8sSUFBSXZJLHFCQUFxQjBJLEtBQUtrRixPQUFPLEVBQUVsRixLQUFLcEQsUUFBUTtRQUM3RCxPQUFPO1lBQ0wsT0FBTyxJQUFJeEYsY0FBYzRJLEtBQUtrRixPQUFPO1FBQ3ZDO0lBQ0Y7SUFFQSxzQ0FBc0M7SUFDdEMxRSxzQkFBc0IzSCxNQUFNLEVBQUU7UUFDNUIsd0NBQXdDO1FBQ3hDLElBQUssSUFBSTBULFNBQVMxVCxPQUFRO1lBQ3hCLElBQUkyVCxRQUFRM1QsTUFBTSxDQUFDMFQsTUFBTTtZQUN6QixJQUFJLE9BQU9DLFVBQVUsWUFBWUQsTUFBTUUsS0FBSyxDQUFDLGtCQUMzQyxDQUFDRixNQUFNRSxLQUFLLENBQUMsdUNBQXVDO2dCQUNwRDVULE1BQU0sQ0FBQzBULE1BQU0sR0FBRyxJQUFJblEsS0FBS29RO1lBQzNCO1lBQ0EsSUFBSUUsTUFBTUMsT0FBTyxDQUFDSCxRQUFRO2dCQUN4QixLQUFLLElBQUl4QyxRQUFRd0MsTUFBTztvQkFDdEIsSUFBSSxDQUFDaE0scUJBQXFCLENBQUN3SjtnQkFDN0I7WUFDRjtZQUNBLElBQUksT0FBT3dDLFVBQVUsVUFBVTtnQkFDN0IsSUFBSSxDQUFDaE0scUJBQXFCLENBQUNnTTtZQUM3QjtRQUNGO1FBQ0EsSUFBSTNULFVBQVVBLE9BQU93SCxVQUFVLEVBQUU7WUFDL0Isd0NBQXdDO1lBQ3hDLElBQUssSUFBSWtNLFNBQVMxVCxPQUFPd0gsVUFBVSxDQUFFO2dCQUNuQ3hILE9BQU93SCxVQUFVLENBQUNrTSxNQUFNLEdBQUcsSUFBSW5RLEtBQUt2RCxPQUFPd0gsVUFBVSxDQUFDa00sTUFBTTtZQUM5RDtRQUNGO1FBQ0EsSUFBSTFULFVBQVVBLE9BQU9XLElBQUksS0FBSyxVQUFVO1lBQ3RDLEtBQUssSUFBSXNOLFNBQVNqTyxPQUFPK1QsTUFBTSxJQUFJLEVBQUUsQ0FBRTtnQkFDckMsSUFBSTlGLE1BQU16RyxVQUFVLEVBQUU7b0JBQ3BCLHdDQUF3QztvQkFDeEMsSUFBSyxJQUFJa00sU0FBU3pGLE1BQU16RyxVQUFVLENBQUU7d0JBQ2xDeUcsTUFBTXpHLFVBQVUsQ0FBQ2tNLE1BQU0sR0FBRyxJQUFJblEsS0FBSzBLLE1BQU16RyxVQUFVLENBQUNrTSxNQUFNO29CQUM1RDtnQkFDRjtZQUNGO1FBQ0Y7SUFDRjtJQUVBOzs7Ozs7Ozs7Ozs7O0dBYUMsR0FFRDs7Ozs7Ozs7Ozs7Ozs7O0dBZUMsR0FFRDs7Ozs7Ozs7Ozs7R0FXQyxHQUVEOzs7Ozs7O0dBT0MsR0FFRDs7Ozs7OztHQU9DLEdBRUQscURBQXFEO0lBQ3ZDdEMsOEJBQThCdk8sTUFBNkMsRUFBRXNFLElBQUk7O2VBQS9GLG9CQUFBO1lBQ0UsSUFBSTtnQkFDRixNQUFNdEYsaUJBQWlCc0YsS0FBS3RILGFBQWEsSUFBSTtnQkFDN0MsTUFBTXdFLGlCQUFpQixNQUFLdUksMkJBQTJCLENBQUN6RixLQUFLdkgsU0FBUyxFQUFFaUM7Z0JBQ3hFLElBQUlzRixLQUFLb0IsaUJBQWlCLElBQUlsRSxnQkFBZ0I7b0JBQzVDQSxlQUFlc0Isd0JBQXdCLENBQUNxTyx1QkFBdUIsQ0FBQzdNLEtBQUtvQixpQkFBaUI7Z0JBQ3hGO2dCQUNBLE1BQU14SCxTQUFTLE1BQUt3QixnQkFBZ0IsQ0FBQzRFLEtBQUt2SCxTQUFTO2dCQUNuRCxNQUFNTSxtQkFBbUIsTUFBS0Msb0JBQW9CLENBQUNnSCxLQUFLdkgsU0FBUyxDQUFDO2dCQUNsRSxJQUFJd0MsYUFBYWxDLG1CQUFtQixNQUFNYSxTQUFTLE1BQU1jLGlCQUFpQixNQUFPc0YsQ0FBQUEsS0FBS3NCLElBQUksSUFBSSxDQUFBO2dCQUM5RixJQUFJNUksZ0JBQWdCa0IsU0FBUyxNQUFNYyxpQkFBaUIsTUFBT3NGLENBQUFBLEtBQUtzQixJQUFJLElBQUksQ0FBQTtnQkFDeEUsSUFBSXdMLGNBQWM1VSxFQUFFNlUsT0FBTyxDQUFDO29CQUFDclU7b0JBQWVnRCxPQUFPMkcsaUJBQWlCO2lCQUFDLEVBQUUySyxJQUFJLENBQUM7Z0JBRTVFLE1BQU1DLHVCQUF1QjtvQkFDM0IsTUFBTUMsb0JBQW9CclMsT0FBT0MsSUFBSSxDQUFDLE1BQUtDLGVBQWUsRUFBRThNLE1BQU0sQ0FBQ3BNLENBQUFBLFdBQ2pFQSxTQUFTcU0sVUFBVSxDQUFDL08sbUJBQW1CLE1BQU1hLFNBQVMsTUFBTWM7b0JBQzlELE9BQU8sQ0FBQ3dTLGtCQUFrQjVULE1BQU0sSUFBSTRULGtCQUFrQjVULE1BQU0sS0FBSyxLQUFLNFQsaUJBQWlCLENBQUMsRUFBRSxLQUFLalM7Z0JBQ2pHO2dCQUVBLE1BQU1rUyx3QkFBd0I7b0JBQzVCLElBQUksTUFBS0MsYUFBYSxDQUFDblMsV0FBVyxFQUFFO3dCQUNsQ29TLGFBQWEsTUFBS0QsYUFBYSxDQUFDblMsV0FBVztvQkFDN0M7Z0JBQ0Y7Z0JBRUEsTUFBTXFTLHVCQUF1QjtvQkFDM0JIO29CQUNBLE1BQUtDLGFBQWEsQ0FBQ25TLFdBQVcsR0FBRzBQLFdBQVc7d0JBQzFDLE1BQUtwUixPQUFPLENBQUM0SSxJQUFJLENBQUMsQ0FBQyxFQUFFbkMsS0FBS3ZILFNBQVMsQ0FBQyxDQUFDLEVBQUVxVSxZQUFZLHlDQUF5QyxDQUFDO3dCQUM3RixJQUFJRyx3QkFBd0I7NEJBQzFCLE1BQUtoVSxvQkFBb0IsQ0FBQ3NVLFNBQVMsQ0FBQ3ZOLEtBQUt2SCxTQUFTLEVBQUU7NEJBQ3BELE1BQUtRLG9CQUFvQixDQUFDc1UsU0FBUyxDQUFDdk4sS0FBS3ZILFNBQVMsRUFBRTt3QkFDdEQ7d0JBQ0EsTUFBSzRSLFVBQVUsQ0FBQ3RSLGtCQUFrQixDQUFDLEVBQUVMLGNBQWMsZUFBZSxDQUFDLEVBQUUsSUFBTThVLGVBQWU7d0JBQzFGSCxhQUFhLE1BQUtELGFBQWEsQ0FBQ25TLFdBQVc7b0JBQzdDLEdBQUc7Z0JBQ0w7Z0JBRUEsc0NBQXNDO2dCQUN0QyxNQUFNdVM7K0JBQWlCLG9CQUFBLFVBQU9DLFlBQVksS0FBSzt3QkFDN0MsSUFBSSxNQUFLMVMsZUFBZSxDQUFDRSxXQUFXLEVBQUU7NEJBQ3BDLE1BQUs3QixlQUFlLENBQUNvVSxjQUFjLENBQUN2Uzs0QkFDcEMsSUFBSWdTLHdCQUF3QjtnQ0FDMUIsS0FBSyxJQUFJdk0sWUFBWSxNQUFLaUIseUJBQXlCLENBQUM1SSxpQkFBaUIsSUFBSSxFQUFFLENBQUU7b0NBQzNFLE1BQU0sTUFBSzBQLGFBQWEsQ0FDdEIsSUFBTS9ILFNBQVM4TSxjQUFjLENBQUM5VSxnQkFDOUIsQ0FBQyxFQUFFSyxpQkFBaUIsQ0FBQyxFQUFFK1QsWUFBWSxlQUFlLENBQUM7Z0NBQ3ZEOzRCQUNGOzRCQUNBLE1BQUt0SyxjQUFjLENBQUNrTCxjQUFjLENBQUN6Uzs0QkFDbkNpQywyQkFBQUEscUNBQUFBLGVBQWdCc0Isd0JBQXdCLENBQUNtUCxvQkFBb0IsQ0FBQzNOLEtBQUt2SCxTQUFTLEVBQUVpQyxnQkFBZ0JzRixLQUFLc0IsSUFBSTs0QkFDdkcsS0FBSyxJQUFJWixZQUFZLE1BQUtpQix5QkFBeUIsQ0FBQzVJLGlCQUFpQixJQUFJLEVBQUUsQ0FBRTtnQ0FDM0UsTUFBTSxNQUFLMFAsYUFBYSxDQUN0QixJQUFNL0gsU0FBU2dOLGNBQWMsQ0FBQ2hWLGdCQUM5QixDQUFDLEVBQUVLLGlCQUFpQixDQUFDLEVBQUUrVCxZQUFZLGVBQWUsQ0FBQzs0QkFDdkQ7NEJBQ0EsT0FBTyxNQUFLL1IsZUFBZSxDQUFDRSxXQUFXOzRCQUN2QyxJQUFJZ1MsMEJBQTBCLENBQUNRLFdBQVc7Z0NBQ3hDLE1BQU0sTUFBS3hVLG9CQUFvQixDQUFDdVUsY0FBYyxDQUFDeE4sS0FBS3ZILFNBQVMsRUFBRTtnQ0FDL0QsTUFBTSxNQUFLUSxvQkFBb0IsQ0FBQ3VVLGNBQWMsQ0FBQ3hOLEtBQUt2SCxTQUFTLEVBQUU7NEJBQ2pFO3dCQUNGO29CQUNGO29DQXZCTStVOzs7O2dCQXdCTixJQUFJeE4sS0FBS3hHLElBQUksS0FBSyxpQkFBaUI7b0JBQ2pDOFQ7b0JBQ0EsSUFBSSxDQUFDdE4sS0FBSzNCLFNBQVMsSUFBSW5CLGtCQUFrQjhDLEtBQUszQixTQUFTLEtBQUtuQixlQUFlbUIsU0FBUyxFQUFFO3dCQUNwRixNQUFLakYsZUFBZSxDQUFDd1UsV0FBVyxDQUFDM1M7d0JBQ2pDLE1BQUtGLGVBQWUsQ0FBQ0UsV0FBVyxHQUFHK0UsS0FBS3NCLElBQUk7d0JBQzVDLEtBQUssSUFBSVosWUFBWSxNQUFLaUIseUJBQXlCLENBQUM1SSxpQkFBaUIsSUFBSSxFQUFFLENBQUU7NEJBQzNFLE1BQU0sTUFBSzBQLGFBQWEsQ0FDdEIsSUFBTS9ILFNBQVNrTixXQUFXLENBQUNsVixlQUFlc0gsS0FBS2hFLFFBQVEsR0FDdkQsQ0FBQyxFQUFFakQsaUJBQWlCLENBQUMsRUFBRStULFlBQVksWUFBWSxDQUFDO3dCQUNwRDt3QkFDQSxNQUFLN1Qsb0JBQW9CLENBQUM0VSxlQUFlLENBQUM3TixLQUFLdkgsU0FBUyxHQUFHLE1BQU1pQzt3QkFDakUsSUFBSXNGLEtBQUtoRSxRQUFRLEtBQUssR0FBRzs0QkFDdkIsTUFBSy9DLG9CQUFvQixDQUFDcVEsYUFBYSxDQUFDdEosS0FBS3ZILFNBQVM7d0JBQ3hELE9BQU87NEJBQ0wsTUFBS1Esb0JBQW9CLENBQUM0VSxlQUFlLENBQUM3TixLQUFLdkgsU0FBUyxHQUFHLE1BQU1pQzt3QkFDbkU7b0JBQ0Y7Z0JBQ0YsT0FBTyxJQUFJc0YsS0FBS3hHLElBQUksS0FBSyxnQkFBZ0I7b0JBQ3ZDMlQ7b0JBQ0EsTUFBTUs7Z0JBQ1IsT0FBTyxJQUFJeE4sS0FBS3hHLElBQUksS0FBSywwQkFBMEI7b0JBQ2pELE1BQUtvTyxhQUFhLENBQUMzTSxXQUFXLEdBQUcsRUFBRTtvQkFDbkMsTUFBSzZTLHFCQUFxQixDQUFDOU4sS0FBS29CLGlCQUFpQixDQUFDLEdBQUc7d0JBQ25EM0ksV0FBV3VILEtBQUt2SCxTQUFTO3dCQUFFaUM7d0JBQzNCNkcsdUJBQXVCdkIsS0FBSytOLHVCQUF1QixLQUFLelM7d0JBQ3hEa0csa0JBQWtCeEIsS0FBS2dPLGtCQUFrQixLQUFLMVM7d0JBQzlDbUcsZUFBZXpCLEtBQUtpTyxlQUFlLEtBQUszUztvQkFDMUM7b0JBQ0EsTUFBS21PLDRCQUE0QixDQUFDeE8sV0FBVyxHQUFHK0UsS0FBS29CLGlCQUFpQjtvQkFDdEUsTUFBTThNLHFCQUFxQixBQUFDbE8sS0FBSytOLHVCQUF1QixLQUFLelMsWUFDM0QsTUFBS3NLLHNCQUFzQixDQUFDNUYsS0FBS29CLGlCQUFpQixDQUFDLElBQ25ELE1BQUt3RSxzQkFBc0IsQ0FBQzVGLEtBQUtvQixpQkFBaUIsQ0FBQyxDQUNoRCtNLG9CQUFvQixDQUFDbk8sS0FBSytOLHVCQUF1QixDQUFDLEdBQUd6UztvQkFDMUQsTUFBTThTLGdCQUFnQixBQUFDcE8sS0FBS2dPLGtCQUFrQixLQUFLMVMsWUFDakQsTUFBS3NLLHNCQUFzQixDQUFDNUYsS0FBS29CLGlCQUFpQixDQUFDLElBQ25ELE1BQUt3RSxzQkFBc0IsQ0FBQzVGLEtBQUtvQixpQkFBaUIsQ0FBQyxDQUFDaU4sZUFBZSxDQUFDck8sS0FBS2dPLGtCQUFrQixDQUFDLEdBQUcxUztvQkFDakcsTUFBTWdULGFBQWEsQUFBQ3RPLEtBQUtpTyxlQUFlLEtBQUszUyxZQUMzQyxNQUFLc0ssc0JBQXNCLENBQUM1RixLQUFLb0IsaUJBQWlCLENBQUMsSUFDbkQsTUFBS3dFLHNCQUFzQixDQUFDNUYsS0FBS29CLGlCQUFpQixDQUFDLENBQUNtTixZQUFZLENBQUN2TyxLQUFLaU8sZUFBZSxDQUFDLEdBQUczUztvQkFDM0YsT0FBTyxNQUFLc0ssc0JBQXNCLENBQUM1RixLQUFLb0IsaUJBQWlCLENBQUM7b0JBQzFELEtBQUssSUFBSVYsWUFBWSxNQUFLaUIseUJBQXlCLENBQUM1SSxpQkFBaUIsSUFBSSxFQUFFLENBQUU7d0JBQzNFLE1BQU0sTUFBSzBQLGFBQWEsQ0FDdEIsSUFBTS9ILFNBQVM4Tix3QkFBd0IsQ0FBQzlWLGVBQWV3VixvQkFDckRFLGVBQWVFLFlBQVl0TyxLQUFLb0IsaUJBQWlCLEdBQ25ELENBQUMsRUFBRXJJLGlCQUFpQixDQUFDLEVBQUUrVCxZQUFZLHlCQUF5QixDQUFDO29CQUNqRTtnQkFDRixPQUFPLElBQUk5TSxLQUFLeEcsSUFBSSxLQUFLLHNCQUFzQjtvQkFDN0MsSUFBSXdHLEtBQUtvQixpQkFBaUIsSUFBSXBCLEtBQUtvQixpQkFBaUIsS0FBSyxNQUFLcUksNEJBQTRCLENBQUN4TyxXQUFXLEVBQUU7d0JBQ3RHO29CQUNGO29CQUNBLElBQUkrRSxLQUFLaUQsa0JBQWtCLEVBQUU7d0JBQzNCLEtBQUssSUFBSXZDLFlBQVksTUFBS2lCLHlCQUF5QixDQUFDNUksaUJBQWlCLElBQUksRUFBRSxDQUFFOzRCQUMzRSxJQUFJO2dDQUNGLE1BQU0sTUFBSzBQLGFBQWEsQ0FDdEIsSUFBTS9ILFNBQVMrTiwyQkFBMkIsQ0FBQy9WLGVBQWVzSCxLQUFLaUQsa0JBQWtCLEdBQ2pGLENBQUMsRUFBRWxLLGlCQUFpQixDQUFDLEVBQUUrVCxZQUFZLDRCQUE0QixDQUFDLEVBQUU7Z0NBQ3BFLHFDQUFxQztnQ0FDckMsSUFBSSxNQUFLZ0IscUJBQXFCLENBQUM5TixLQUFLb0IsaUJBQWlCLENBQUMsSUFDcEQsQ0FBQyxNQUFLME0scUJBQXFCLENBQUM5TixLQUFLb0IsaUJBQWlCLENBQUMsQ0FBQ0ksZ0JBQWdCLEVBQUU7b0NBQ3RFLE1BQU0sTUFBS2lILGFBQWEsQ0FDdEIsSUFBTS9ILFNBQVNnTyx1QkFBdUIsQ0FBQ2hXLGVBQWVzSCxLQUFLb0IsaUJBQWlCLEdBQzVFLENBQUMsRUFBRXJJLGlCQUFpQixDQUFDLEVBQUUrVCxZQUFZLHdCQUF3QixDQUFDLEVBQUU7b0NBQ2hFLHFDQUFxQztvQ0FDckMsSUFBSSxDQUFDLE1BQUtnQixxQkFBcUIsQ0FBQzlOLEtBQUtvQixpQkFBaUIsQ0FBQyxDQUFDSyxhQUFhLEVBQUU7d0NBQ3JFLE1BQU0sTUFBS2dILGFBQWEsQ0FDdEIsSUFBTS9ILFNBQVNpTywyQkFBMkIsQ0FBQ2pXLGVBQWVzSCxLQUFLb0IsaUJBQWlCLEdBQ2hGLENBQUMsRUFBRXJJLGlCQUFpQixDQUFDLEVBQUUrVCxZQUFZLDRCQUE0QixDQUFDLEVBQUU7b0NBQ3RFO2dDQUNGOzRCQUNGLEVBQUUsT0FBT2xOLEtBQUs7Z0NBQ1osTUFBS3JHLE9BQU8sQ0FBQ3NHLEtBQUssQ0FBQyxDQUFDLEVBQUU5RyxpQkFBaUIsQ0FBQyxFQUFFK1QsWUFBWSw0QkFBNEIsQ0FBQyxHQUNqRixrQ0FBa0NsTjs0QkFDdEM7d0JBQ0Y7d0JBQ0EsSUFBSSxNQUFLa08scUJBQXFCLENBQUM5TixLQUFLb0IsaUJBQWlCLENBQUMsSUFDbEQsQ0FBQyxNQUFLME0scUJBQXFCLENBQUM5TixLQUFLb0IsaUJBQWlCLENBQUMsQ0FBQ0ksZ0JBQWdCLElBQ3BFLENBQUMsTUFBS3NNLHFCQUFxQixDQUFDOU4sS0FBS29CLGlCQUFpQixDQUFDLENBQUNLLGFBQWEsRUFBRTs0QkFDckUsT0FBTyxNQUFLcU0scUJBQXFCLENBQUM5TixLQUFLb0IsaUJBQWlCLENBQUM7d0JBQzNEO29CQUNGO2dCQUNGLE9BQU8sSUFBSXBCLEtBQUt4RyxJQUFJLEtBQUssU0FBUztvQkFDaEMsSUFBSXdHLEtBQUtvQixpQkFBaUIsSUFBSXBCLEtBQUtvQixpQkFBaUIsS0FBSyxNQUFLcUksNEJBQTRCLENBQUN4TyxXQUFXLEVBQUU7d0JBQ3RHO29CQUNGO29CQUNBLEtBQUssSUFBSTJULFFBQVM1TyxLQUFLdUUsS0FBSyxJQUFJLEVBQUUsQ0FBRzt3QkFDbkMsS0FBSyxJQUFJN0QsWUFBWSxNQUFLaUIseUJBQXlCLENBQUM1SSxpQkFBaUIsSUFBSSxFQUFFLENBQUU7NEJBQzNFLE1BQU0sTUFBSzBQLGFBQWEsQ0FDdEIsSUFBTS9ILFNBQVNtTyxXQUFXLENBQUNuVyxlQUFla1csT0FDMUMsQ0FBQyxFQUFFN1YsaUJBQWlCLENBQUMsRUFBRStULFlBQVksWUFBWSxDQUFDO3dCQUNwRDtvQkFDRjtnQkFDRixPQUFPLElBQUk5TSxLQUFLeEcsSUFBSSxLQUFLLFVBQVU7b0JBQ2pDLElBQUl3RyxLQUFLb0IsaUJBQWlCLElBQUlwQixLQUFLb0IsaUJBQWlCLEtBQUssTUFBS3FJLDRCQUE0QixDQUFDeE8sV0FBVyxFQUFFO3dCQUN0RztvQkFDRjtvQkFDQSxLQUFLLElBQUl5RixZQUFZLE1BQUtpQix5QkFBeUIsQ0FBQzVJLGlCQUFpQixJQUFJLEVBQUUsQ0FBRTt3QkFDM0UsSUFBSTs0QkFDRixNQUFNLE1BQUswUCxhQUFhLENBQ3RCLElBQU0vSCxTQUFTb08sdUJBQXVCLENBQUNwVyxlQUFlc0gsS0FBS3dELE1BQU0sSUFBSSxFQUFFLEdBQ3ZFLENBQUMsRUFBRXpLLGlCQUFpQixDQUFDLEVBQUUrVCxZQUFZLHdCQUF3QixDQUFDLEVBQUU7NEJBQ2hFLE1BQU0sTUFBS3JFLGFBQWEsQ0FDdEIsSUFBTS9ILFNBQVNpTywyQkFBMkIsQ0FBQ2pXLGVBQWVzSCxLQUFLb0IsaUJBQWlCLEdBQ2hGLENBQUMsRUFBRXJJLGlCQUFpQixDQUFDLEVBQUUrVCxZQUFZLDRCQUE0QixDQUFDLEVBQUU7d0JBQ3RFLEVBQUUsT0FBT2xOLEtBQUs7NEJBQ1osTUFBS3JHLE9BQU8sQ0FBQ3NHLEtBQUssQ0FBQyxDQUFDLEVBQUU5RyxpQkFBaUIsQ0FBQyxFQUFFK1QsWUFBWSw0QkFBNEIsQ0FBQyxHQUNqRixzQkFBc0JsTjt3QkFDMUI7b0JBQ0Y7b0JBQ0EsSUFBSSxNQUFLa08scUJBQXFCLENBQUM5TixLQUFLb0IsaUJBQWlCLENBQUMsRUFBRTt3QkFDdEQsT0FBTyxNQUFLME0scUJBQXFCLENBQUM5TixLQUFLb0IsaUJBQWlCLENBQUM7b0JBQzNEO2dCQUNGLE9BQU8sSUFBSXBCLEtBQUt4RyxJQUFJLEtBQUssaUJBQWlCO29CQUN4QyxJQUFJd0csS0FBS29CLGlCQUFpQixJQUFJcEIsS0FBS29CLGlCQUFpQixLQUFLLE1BQUtxSSw0QkFBNEIsQ0FBQ3hPLFdBQVcsRUFBRTt3QkFDdEc7b0JBQ0Y7b0JBQ0EsS0FBSyxJQUFJOFQsZ0JBQWlCL08sS0FBSzhELGFBQWEsSUFBSSxFQUFFLENBQUc7d0JBQ25ELEtBQUssSUFBSXBELFlBQVksTUFBS2lCLHlCQUF5QixDQUFDNUksaUJBQWlCLElBQUksRUFBRSxDQUFFOzRCQUMzRSxNQUFNLE1BQUswUCxhQUFhLENBQ3RCLElBQU0vSCxTQUFTc08sbUJBQW1CLENBQUN0VyxlQUFlcVcsZUFDbEQsQ0FBQyxFQUFFaFcsaUJBQWlCLENBQUMsRUFBRStULFlBQVksb0JBQW9CLENBQUM7d0JBQzVEO29CQUNGO2dCQUNGLE9BQU8sSUFBSTlNLEtBQUt4RyxJQUFJLEtBQUssYUFBYTtvQkFDcEMsSUFBSXdHLEtBQUtvQixpQkFBaUIsSUFBSXBCLEtBQUtvQixpQkFBaUIsS0FBSyxNQUFLcUksNEJBQTRCLENBQUN4TyxXQUFXLEVBQUU7d0JBQ3RHO29CQUNGO29CQUNBLEtBQUssSUFBSXlGLFlBQVksTUFBS2lCLHlCQUF5QixDQUFDNUksaUJBQWlCLElBQUksRUFBRSxDQUFFO3dCQUMzRSxJQUFJOzRCQUNGLE1BQU0sTUFBSzBQLGFBQWEsQ0FDdEIsSUFBTS9ILFNBQVN1TyxtQkFBbUIsQ0FBQ3ZXLGVBQWVzSCxLQUFLbUQsU0FBUyxJQUFJLEVBQUUsR0FDdEUsQ0FBQyxFQUFFcEssaUJBQWlCLENBQUMsRUFBRStULFlBQVksb0JBQW9CLENBQUMsRUFBRTs0QkFDNUQsTUFBTSxNQUFLckUsYUFBYSxDQUN0QixJQUFNL0gsU0FBU2dPLHVCQUF1QixDQUFDaFcsZUFBZXNILEtBQUtvQixpQkFBaUIsR0FDNUUsQ0FBQyxFQUFFckksaUJBQWlCLENBQUMsRUFBRStULFlBQVksd0JBQXdCLENBQUMsRUFBRTs0QkFDaEUsSUFBSSxNQUFLZ0IscUJBQXFCLENBQUM5TixLQUFLb0IsaUJBQWlCLENBQUMsSUFDcEQsQ0FBQyxNQUFLME0scUJBQXFCLENBQUM5TixLQUFLb0IsaUJBQWlCLENBQUMsQ0FBQ0ssYUFBYSxFQUFFO2dDQUNuRSxNQUFNLE1BQUtnSCxhQUFhLENBQ3RCLElBQU0vSCxTQUFTaU8sMkJBQTJCLENBQUNqVyxlQUFlc0gsS0FBS29CLGlCQUFpQixHQUNoRixDQUFDLEVBQUVySSxpQkFBaUIsQ0FBQyxFQUFFK1QsWUFBWSw0QkFBNEIsQ0FBQyxFQUFFOzRCQUN0RTt3QkFDRixFQUFFLE9BQU9sTixLQUFLOzRCQUNaLE1BQUtyRyxPQUFPLENBQUNzRyxLQUFLLENBQUMsQ0FBQyxFQUFFOUcsaUJBQWlCLENBQUMsRUFBRStULFlBQVksNEJBQTRCLENBQUMsR0FDakYseUJBQXlCbE47d0JBQzdCO29CQUNGO29CQUNBLElBQUksTUFBS2tPLHFCQUFxQixDQUFDOU4sS0FBS29CLGlCQUFpQixDQUFDLElBQ3BELENBQUMsTUFBSzBNLHFCQUFxQixDQUFDOU4sS0FBS29CLGlCQUFpQixDQUFDLENBQUNLLGFBQWEsRUFBRTt3QkFDbkUsT0FBTyxNQUFLcU0scUJBQXFCLENBQUM5TixLQUFLb0IsaUJBQWlCLENBQUM7b0JBQzNEO2dCQUNGLE9BQU8sSUFBSXBCLEtBQUt4RyxJQUFJLEtBQUssVUFBVTt3QkFDakM7cUJBQUEsaUNBQUEsTUFBS29PLGFBQWEsQ0FBQzNNLFdBQVcsY0FBOUIscURBQUEsK0JBQWdDTSxJQUFJLENBQUM7d0JBQUNHO3dCQUFRc0U7b0JBQUk7b0JBQ2xELElBQUlBLEtBQUtpRCxrQkFBa0IsRUFBRTt3QkFDM0IsS0FBSyxJQUFJdkMsWUFBWSxNQUFLaUIseUJBQXlCLENBQUM1SSxpQkFBaUIsSUFBSSxFQUFFLENBQUU7NEJBQzNFLE1BQU0sTUFBSzBQLGFBQWEsQ0FDdEIsSUFBTS9ILFNBQVMrTiwyQkFBMkIsQ0FBQy9WLGVBQWVzSCxLQUFLaUQsa0JBQWtCLEdBQ2pGLENBQUMsRUFBRWxLLGlCQUFpQixDQUFDLEVBQUUrVCxZQUFZLDRCQUE0QixDQUFDO3dCQUNwRTtvQkFDRjtvQkFDQSxNQUFNb0MsbUJBQW1CbFAsS0FBS2tQLGdCQUFnQixJQUFJLEVBQUU7b0JBQ3BELE1BQU1DLHFCQUFxQm5QLEtBQUttUCxrQkFBa0IsSUFBSSxFQUFFO29CQUN4RCxJQUFJRCxpQkFBaUI1VixNQUFNLElBQUk2VixtQkFBbUI3VixNQUFNLEVBQUU7d0JBQ3hELEtBQUssSUFBSW9ILFlBQVksTUFBS2lCLHlCQUF5QixDQUFDNUksaUJBQWlCLElBQUksRUFBRSxDQUFFOzRCQUMzRSxNQUFNLE1BQUswUCxhQUFhLENBQ3RCLElBQU0vSCxTQUFTME8sa0JBQWtCLENBQUMxVyxlQUFld1csa0JBQWtCQyxxQkFDbkUsQ0FBQyxFQUFFcFcsaUJBQWlCLENBQUMsRUFBRStULFlBQVksbUJBQW1CLENBQUM7d0JBQzNEO29CQUNGO29CQUNBLEtBQUssSUFBSXhKLFlBQVk0TCxpQkFBa0I7d0JBQ3JDLEtBQUssSUFBSXhPLFlBQVksTUFBS2lCLHlCQUF5QixDQUFDNUksaUJBQWlCLElBQUksRUFBRSxDQUFFOzRCQUMzRSxNQUFNLE1BQUswUCxhQUFhLENBQ3RCLElBQU0vSCxTQUFTMk8saUJBQWlCLENBQUMzVyxlQUFlNEssV0FDaEQsQ0FBQyxFQUFFdkssaUJBQWlCLENBQUMsRUFBRStULFlBQVksa0JBQWtCLENBQUM7d0JBQzFEO29CQUNGO29CQUNBLEtBQUssSUFBSXpKLGNBQWM4TCxtQkFBb0I7d0JBQ3pDLEtBQUssSUFBSXpPLFlBQVksTUFBS2lCLHlCQUF5QixDQUFDNUksaUJBQWlCLElBQUksRUFBRSxDQUFFOzRCQUMzRSxNQUFNLE1BQUswUCxhQUFhLENBQ3RCLElBQU0vSCxTQUFTNE8saUJBQWlCLENBQUM1VyxlQUFlMkssYUFDaEQsQ0FBQyxFQUFFdEssaUJBQWlCLENBQUMsRUFBRStULFlBQVksa0JBQWtCLENBQUM7d0JBQzFEO29CQUNGO29CQUNBLE1BQU15QyxnQkFBZ0J2UCxLQUFLdVAsYUFBYSxJQUFJLEVBQUU7b0JBQzlDLE1BQU1DLG9CQUFvQnhQLEtBQUt3UCxpQkFBaUIsSUFBSSxFQUFFO29CQUN0RCxJQUFJRCxjQUFjalcsTUFBTSxJQUFJa1csa0JBQWtCbFcsTUFBTSxFQUFFO3dCQUNwRCxLQUFLLElBQUlvSCxZQUFZLE1BQUtpQix5QkFBeUIsQ0FBQzVJLGlCQUFpQixJQUFJLEVBQUUsQ0FBRTs0QkFDM0UsTUFBTSxNQUFLMFAsYUFBYSxDQUN0QixJQUFNL0gsU0FBUytPLHNCQUFzQixDQUFDL1csZUFBZTZXLGVBQWVDLG9CQUNwRSxDQUFDLEVBQUV6VyxpQkFBaUIsQ0FBQyxFQUFFK1QsWUFBWSx1QkFBdUIsQ0FBQzt3QkFDL0Q7b0JBQ0Y7b0JBQ0EsS0FBSyxJQUFJbkosU0FBUzRMLGNBQWU7d0JBQy9CLEtBQUssSUFBSTdPLFlBQVksTUFBS2lCLHlCQUF5QixDQUFDNUksaUJBQWlCLElBQUksRUFBRSxDQUFFOzRCQUMzRSxNQUFNLE1BQUswUCxhQUFhLENBQ3RCLElBQU0vSCxTQUFTZ1AscUJBQXFCLENBQUNoWCxlQUFlaUwsUUFDcEQsQ0FBQyxFQUFFNUssaUJBQWlCLENBQUMsRUFBRStULFlBQVksc0JBQXNCLENBQUM7d0JBQzlEO29CQUNGO29CQUNBLEtBQUssSUFBSXBKLFdBQVc4TCxrQkFBbUI7d0JBQ3JDLEtBQUssSUFBSTlPLFlBQVksTUFBS2lCLHlCQUF5QixDQUFDNUksaUJBQWlCLElBQUksRUFBRSxDQUFFOzRCQUMzRSxNQUFNLE1BQUswUCxhQUFhLENBQ3RCLElBQU0vSCxTQUFTaVAsdUJBQXVCLENBQUNqWCxlQUFlZ0wsVUFDdEQsQ0FBQyxFQUFFM0ssaUJBQWlCLENBQUMsRUFBRStULFlBQVksd0JBQXdCLENBQUM7d0JBQ2hFO29CQUNGO29CQUNBLEtBQUssSUFBSWlDLGdCQUFpQi9PLEtBQUs4RCxhQUFhLElBQUksRUFBRSxDQUFHO3dCQUNuRCxLQUFLLElBQUlwRCxZQUFZLE1BQUtpQix5QkFBeUIsQ0FBQzVJLGlCQUFpQixJQUFJLEVBQUUsQ0FBRTs0QkFDM0UsTUFBTSxNQUFLMFAsYUFBYSxDQUN0QixJQUFNL0gsU0FBU3NPLG1CQUFtQixDQUFDdFcsZUFBZXFXLGVBQ2xELENBQUMsRUFBRWhXLGlCQUFpQixDQUFDLEVBQUUrVCxZQUFZLG9CQUFvQixDQUFDO3dCQUM1RDtvQkFDRjtvQkFDQSxLQUFLLElBQUk4QixRQUFTNU8sS0FBS3VFLEtBQUssSUFBSSxFQUFFLENBQUc7d0JBQ25DLEtBQUssSUFBSTdELFlBQVksTUFBS2lCLHlCQUF5QixDQUFDNUksaUJBQWlCLElBQUksRUFBRSxDQUFFOzRCQUMzRSxNQUFNLE1BQUswUCxhQUFhLENBQ3RCLElBQU0vSCxTQUFTbU8sV0FBVyxDQUFDblcsZUFBZWtXLE9BQzFDLENBQUMsRUFBRTdWLGlCQUFpQixDQUFDLEVBQUUrVCxZQUFZLFlBQVksQ0FBQzt3QkFDcEQ7b0JBQ0Y7b0JBQ0EsSUFBSTlNLEtBQUtLLFVBQVUsRUFBRTt3QkFDbkJMLEtBQUtLLFVBQVUsQ0FBQ0ksd0JBQXdCLEdBQUcsSUFBSXJFO3dCQUMvQyxxQ0FBcUM7d0JBQ3JDLEtBQUssSUFBSXNFLFlBQVksTUFBS0MsaUJBQWlCLElBQUksRUFBRSxDQUFFOzRCQUNqRCxNQUFNLE1BQUs4SCxhQUFhLENBQ3RCLElBQU0vSCxTQUFTa1AsUUFBUSxDQUFDNVAsS0FBS3ZILFNBQVMsRUFBRXVILEtBQUtLLFVBQVUsR0FDdkQsQ0FBQyxFQUFFdEgsaUJBQWlCLENBQUMsRUFBRStULFlBQVksU0FBUyxDQUFDO3dCQUNqRDtvQkFDRjtnQkFDRixPQUFPLElBQUk5TSxLQUFLeEcsSUFBSSxLQUFLLCtCQUErQjtvQkFDdEQsSUFBSXdHLEtBQUtvQixpQkFBaUIsSUFBSXBCLEtBQUtvQixpQkFBaUIsS0FBSyxNQUFLcUksNEJBQTRCLENBQUN4TyxXQUFXLEVBQUU7d0JBQ3RHLE9BQU8sTUFBS3dPLDRCQUE0QixDQUFDeE8sV0FBVzt3QkFDcEQ7b0JBQ0Y7b0JBQ0EsTUFBSzdCLGVBQWUsQ0FBQ3lXLG1CQUFtQixDQUFDNVU7b0JBQ3pDLEtBQUssSUFBSXlGLFlBQVksTUFBS2lCLHlCQUF5QixDQUFDNUksaUJBQWlCLElBQUksRUFBRSxDQUFFO3dCQUMzRW1FLDJCQUFBQSxxQ0FBQUEsZUFBZ0JzQix3QkFBd0IsQ0FBQ3NSLHVCQUF1QixDQUFDOVAsS0FBS29CLGlCQUFpQjt3QkFDdkYsTUFBTSxNQUFLcUgsYUFBYSxDQUN0QixJQUFNL0gsU0FBU21QLG1CQUFtQixDQUFDblgsZUFBZXNILEtBQUtvQixpQkFBaUIsR0FDeEUsQ0FBQyxFQUFFckksaUJBQWlCLENBQUMsRUFBRStULFlBQVksb0JBQW9CLENBQUM7b0JBQzVEO29CQUNBLElBQUksTUFBS2xGLGFBQWEsQ0FBQzNNLFdBQVcsRUFBRTt3QkFDbEMsSUFBSThVLGFBQWEsTUFBS25JLGFBQWEsQ0FBQzNNLFdBQVcsQ0FBQytNLEdBQUcsQ0FBQ3VDLENBQUFBLFFBQ2xELElBQU0sTUFBS04sNkJBQTZCLENBQUNNLE1BQU03TyxNQUFNLEVBQUU2TyxNQUFNdkssSUFBSTt3QkFFbkUsSUFBSSxNQUFLa0ssWUFBWSxDQUFDblIsaUJBQWlCLEVBQUU7NEJBQ3ZDLE1BQUttUixZQUFZLENBQUNuUixpQkFBaUIsR0FBR2dYLFdBQVczRixNQUFNLENBQUMsTUFBS0YsWUFBWSxDQUFDblIsaUJBQWlCOzRCQUMzRixPQUFPLE1BQUs2TyxhQUFhLENBQUMzTSxXQUFXO3dCQUN2QyxPQUFPOzRCQUNMLE1BQUtpUCxZQUFZLENBQUNuUixpQkFBaUIsR0FBR2dYOzRCQUN0QyxPQUFPLE1BQUtuSSxhQUFhLENBQUMzTSxXQUFXOzRCQUNyQyxNQUFLa1Asa0JBQWtCLENBQUNwUjt3QkFDMUI7b0JBQ0Y7Z0JBQ0YsT0FBTyxJQUFJaUgsS0FBS3hHLElBQUksS0FBSyxnQ0FBZ0M7b0JBQ3ZELElBQUl3RyxLQUFLb0IsaUJBQWlCLElBQUlwQixLQUFLb0IsaUJBQWlCLEtBQUssTUFBS3FJLDRCQUE0QixDQUFDeE8sV0FBVyxFQUFFO3dCQUN0RztvQkFDRjtvQkFDQSxLQUFLLElBQUl5RixZQUFZLE1BQUtpQix5QkFBeUIsQ0FBQzVJLGlCQUFpQixJQUFJLEVBQUUsQ0FBRTt3QkFDM0UsTUFBTSxNQUFLMFAsYUFBYSxDQUN0QixJQUFNL0gsU0FBU3NQLDJCQUEyQixDQUFDdFgsZUFBZXNILEtBQUtvQixpQkFBaUIsR0FDaEYsQ0FBQyxFQUFFckksaUJBQWlCLENBQUMsRUFBRStULFlBQVksNEJBQTRCLENBQUM7b0JBQ3BFO2dCQUNGLE9BQU8sSUFBSTlNLEtBQUt4RyxJQUFJLEtBQUssVUFBVTtvQkFDakMsSUFBSSxDQUFDLE1BQUt1QixlQUFlLENBQUNFLFdBQVcsRUFBRTt3QkFDckMsSUFBSSxNQUFLbVMsYUFBYSxDQUFDblMsV0FBVyxJQUFJK0UsS0FBS2lRLGFBQWEsSUFDbkQsQ0FBQSxNQUFLaFgsb0JBQW9CLENBQUNpWCx1QkFBdUIsQ0FBQ2xRLEtBQUt2SCxTQUFTLEVBQUVpQyxtQkFDbkUsQ0FBQyxNQUFLekIsb0JBQW9CLENBQUNrWCxvQkFBb0IsQ0FBQ25RLEtBQUt2SCxTQUFTLEVBQUVpQyxlQUFjLEdBQUk7NEJBQ3BGLE1BQUt6QixvQkFBb0IsQ0FBQzRVLGVBQWUsQ0FBQzdOLEtBQUt2SCxTQUFTLEdBQUcsTUFBTWlDOzRCQUNqRSxNQUFNLElBQUlrRCxRQUFRQyxDQUFBQSxNQUFPOE0sV0FBVzlNLEtBQUs7NEJBQ3pDLHNDQUFzQzs0QkFDdEMsTUFBS3RFLE9BQU8sQ0FBQ21HLElBQUksQ0FDZixzR0FDQXhILEVBQUU2VSxPQUFPLENBQUM7Z0NBQUM5UjtnQ0FBWVMsT0FBTzJHLGlCQUFpQjs2QkFBQyxFQUFFMkssSUFBSSxDQUFDOzRCQUV6RCxNQUFLdlQsZUFBZSxDQUFDdUcsS0FBS3ZILFNBQVMsRUFBRWlDO3dCQUN2QztvQkFDRixPQUFPO3dCQUNMNFM7d0JBQ0EsS0FBSyxJQUFJNU0sWUFBWSxNQUFLaUIseUJBQXlCLENBQUM1SSxpQkFBaUIsSUFBSSxFQUFFLENBQUU7NEJBQzNFLE1BQU0sTUFBSzBQLGFBQWEsQ0FDdEIsSUFBTS9ILFNBQVMwUCwrQkFBK0IsQ0FBQzFYLGVBQWUsQ0FBQyxDQUFDc0gsS0FBS3hFLFNBQVMsR0FDOUUsQ0FBQyxFQUFFekMsaUJBQWlCLENBQUMsRUFBRStULFlBQVksZ0NBQWdDLENBQUM7d0JBQ3hFO3dCQUNBLElBQUk5TSxLQUFLcVEsWUFBWSxFQUFFOzRCQUNyQixxQ0FBcUM7NEJBQ3JDLEtBQUssSUFBSTNQLFlBQVksTUFBS2lCLHlCQUF5QixDQUFDNUksaUJBQWlCLElBQUksRUFBRSxDQUFFO2dDQUMzRSxNQUFNLE1BQUswUCxhQUFhLENBQ3RCLElBQU0vSCxTQUFTNFAsY0FBYyxDQUFDNVgsZUFBZXNILEtBQUtxUSxZQUFZLEdBQzlELENBQUMsRUFBRXRYLGlCQUFpQixDQUFDLEVBQUUrVCxZQUFZLGVBQWUsQ0FBQzs0QkFDdkQ7d0JBQ0Y7b0JBQ0Y7Z0JBQ0YsT0FBTyxJQUFJOU0sS0FBS3hHLElBQUksS0FBSyx5QkFBeUI7b0JBQ2hELE1BQUtELE9BQU8sQ0FBQ21HLElBQUksQ0FBQyxDQUFDLEVBQUUzRyxpQkFBaUIsQ0FBQyxFQUFFK1QsWUFBWSx1Q0FBdUMsQ0FBQyxHQUMzRixDQUFDLEVBQUU5TSxLQUFLb0csTUFBTSxDQUFDLDBFQUEwRSxDQUFDLEdBQzFGLENBQUMsRUFBRW5HLEtBQUtFLFNBQVMsQ0FBQ0gsS0FBS3VRLE9BQU8sRUFBRSx5QkFBeUIsRUFBRXRRLEtBQUtFLFNBQVMsQ0FBQ0gsS0FBS3dRLGVBQWUsRUFBRSxFQUFFLENBQUMsR0FDbkc7b0JBQ0YsS0FBSyxJQUFJOVAsWUFBWSxNQUFLaUIseUJBQXlCLENBQUM1SSxpQkFBaUIsSUFBSSxFQUFFLENBQUU7d0JBQzNFLE1BQU0sTUFBSzBQLGFBQWEsQ0FDdEIsSUFBTS9ILFNBQVMrUCx3QkFBd0IsQ0FBQy9YLGVBQWVzSCxLQUFLb0csTUFBTSxFQUFFcEcsS0FBS3VRLE9BQU8sRUFBRXZRLEtBQUt3USxlQUFlLEdBQ3RHLENBQUMsRUFBRXpYLGlCQUFpQixDQUFDLEVBQUUrVCxZQUFZLHlCQUF5QixDQUFDO29CQUNqRTtnQkFDRixPQUFPLElBQUk5TSxLQUFLeEcsSUFBSSxLQUFLLGtCQUFrQjtvQkFDekMsSUFBSXdHLEtBQUtvQixpQkFBaUIsSUFBSXBCLEtBQUtvQixpQkFBaUIsS0FBSyxNQUFLcUksNEJBQTRCLENBQUN4TyxXQUFXLEVBQUU7d0JBQ3RHO29CQUNGO29CQUNBLEtBQUssSUFBSXlGLFlBQVksTUFBS2lCLHlCQUF5QixDQUFDNUksaUJBQWlCLElBQUksRUFBRSxDQUFFO3dCQUMzRSxNQUFNLE1BQUswUCxhQUFhLENBQ3RCLElBQU0vSCxTQUFTZ1EsNkJBQTZCLENBQUNoWSxlQUFlc0gsS0FBSzBCLGNBQWMsSUFBSSxFQUFFLEVBQ25GMUIsS0FBSzJRLGNBQWMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxFQUFFNVgsaUJBQWlCLENBQUMsRUFBRStULFlBQVksOEJBQThCLENBQUM7b0JBQ3BHO29CQUNBLEtBQUssSUFBSW5HLGlCQUFrQjNHLEtBQUswQixjQUFjLElBQUksRUFBRSxDQUFHO3dCQUNyRCxLQUFLLElBQUloQixZQUFZLE1BQUtpQix5QkFBeUIsQ0FBQzVJLGlCQUFpQixJQUFJLEVBQUUsQ0FBRTs0QkFDM0UsTUFBTSxNQUFLMFAsYUFBYSxDQUN0QixJQUFNL0gsU0FBU2tRLDRCQUE0QixDQUFDbFksZUFBZWlPLGdCQUMzRCxDQUFDLEVBQUU1TixpQkFBaUIsQ0FBQyxFQUFFK1QsWUFBWSw2QkFBNkIsQ0FBQzt3QkFDckU7b0JBQ0Y7b0JBQ0EsS0FBSyxJQUFJK0QsaUJBQWtCN1EsS0FBSzJRLGNBQWMsSUFBSSxFQUFFLENBQUc7d0JBQ3JELEtBQUssSUFBSWpRLFlBQVksTUFBS2lCLHlCQUF5QixDQUFDNUksaUJBQWlCLElBQUksRUFBRSxDQUFFOzRCQUMzRSxNQUFNLE1BQUswUCxhQUFhLENBQ3RCLElBQU0vSCxTQUFTb1EsNEJBQTRCLENBQUNwWSxlQUFlbVksZ0JBQzNELENBQUMsRUFBRTlYLGlCQUFpQixDQUFDLEVBQUUrVCxZQUFZLDZCQUE2QixDQUFDO3dCQUNyRTtvQkFDRjtnQkFDRixPQUFPLElBQUk5TSxLQUFLeEcsSUFBSSxLQUFLLFVBQVU7b0JBQ2pDLElBQUl3RyxLQUFLb0IsaUJBQWlCLElBQUlwQixLQUFLb0IsaUJBQWlCLEtBQUssTUFBS3FJLDRCQUE0QixDQUFDeE8sV0FBVyxFQUFFO3dCQUN0RztvQkFDRjtvQkFDQSxJQUFJMlIsU0FBUzVNLEtBQUs0TSxNQUFNLElBQUksRUFBRTtvQkFDOUIsSUFBSW1FLFVBQVUvUSxLQUFLK1EsT0FBTyxJQUFJLEVBQUU7b0JBQ2hDLElBQUlDLFFBQVFoUixLQUFLZ1IsS0FBSyxJQUFJLEVBQUU7b0JBQzVCLElBQUlDLFFBQVFqUixLQUFLaVIsS0FBSyxJQUFJLEVBQUU7b0JBQzVCLEtBQUssSUFBSXZRLFlBQVksTUFBS2lCLHlCQUF5QixDQUFDNUksaUJBQWlCLElBQUksRUFBRSxDQUFFO3dCQUMzRSxJQUFJNlQsT0FBT3RULE1BQU0sRUFBRTs0QkFDakIsTUFBTSxNQUFLbVAsYUFBYSxDQUN0QixJQUFNL0gsU0FBU3dRLHFCQUFxQixDQUFDeFksZUFBZWtVLFFBQVE1TSxLQUFLbVIsTUFBTSxFQUFFblIsS0FBS3NJLE1BQU0sRUFBRXRJLEtBQUtvUixVQUFVLEVBQ25HcFIsS0FBS3FSLFdBQVcsRUFBRXJSLEtBQUtzUiwyQkFBMkIsR0FDcEQsQ0FBQyxFQUFFdlksaUJBQWlCLENBQUMsRUFBRStULFlBQVksc0JBQXNCLENBQUM7d0JBQzlEO3dCQUNBLElBQUlpRSxRQUFRelgsTUFBTSxFQUFFOzRCQUNsQixNQUFNLE1BQUttUCxhQUFhLENBQ3RCLElBQU0vSCxTQUFTNlEsZ0JBQWdCLENBQUM3WSxlQUFlcVksU0FBUy9RLEtBQUttUixNQUFNLEVBQUVuUixLQUFLc0ksTUFBTSxFQUFFdEksS0FBS29SLFVBQVUsRUFDL0ZwUixLQUFLcVIsV0FBVyxFQUFFclIsS0FBS3NSLDJCQUEyQixHQUNwRCxDQUFDLEVBQUV2WSxpQkFBaUIsQ0FBQyxFQUFFK1QsWUFBWSxpQkFBaUIsQ0FBQzt3QkFDekQ7d0JBQ0EsSUFBSWtFLE1BQU0xWCxNQUFNLEVBQUU7NEJBQ2hCLE1BQU0sTUFBS21QLGFBQWEsQ0FDdEIsSUFBTS9ILFNBQVM4USxjQUFjLENBQUM5WSxlQUFlc1ksT0FBT2hSLEtBQUttUixNQUFNLEVBQUVuUixLQUFLc0ksTUFBTSxFQUFFdEksS0FBS29SLFVBQVUsRUFDM0ZwUixLQUFLcVIsV0FBVyxFQUFFclIsS0FBS3NSLDJCQUEyQixHQUNwRCxDQUFDLEVBQUV2WSxpQkFBaUIsQ0FBQyxFQUFFK1QsWUFBWSxlQUFlLENBQUM7d0JBQ3ZEO3dCQUNBLElBQUltRSxNQUFNM1gsTUFBTSxFQUFFOzRCQUNoQixNQUFNLE1BQUttUCxhQUFhLENBQ3RCLElBQU0vSCxTQUFTK1EsY0FBYyxDQUFDL1ksZUFBZXVZLE9BQU9qUixLQUFLbVIsTUFBTSxFQUFFblIsS0FBS3NJLE1BQU0sRUFBRXRJLEtBQUtvUixVQUFVLEVBQzNGcFIsS0FBS3FSLFdBQVcsRUFBRXJSLEtBQUtzUiwyQkFBMkIsR0FDcEQsQ0FBQyxFQUFFdlksaUJBQWlCLENBQUMsRUFBRStULFlBQVksZUFBZSxDQUFDO3dCQUN2RDtvQkFDRjtvQkFDQSxLQUFLLElBQUloRyxTQUFTOEYsT0FBUTt3QkFDeEIsS0FBSyxJQUFJbE0sWUFBWSxNQUFLaUIseUJBQXlCLENBQUM1SSxpQkFBaUIsSUFBSSxFQUFFLENBQUU7NEJBQzNFLE1BQU0sTUFBSzBQLGFBQWEsQ0FDdEIsSUFBTS9ILFNBQVNnUixvQkFBb0IsQ0FBQ2haLGVBQWVvTyxRQUNuRCxDQUFDLEVBQUUvTixpQkFBaUIsQ0FBQyxFQUFFK1QsWUFBWSxxQkFBcUIsQ0FBQzt3QkFDN0Q7b0JBQ0Y7b0JBQ0EsS0FBSyxJQUFJaEcsU0FBUzhGLE9BQVE7d0JBQ3hCLElBQUk5RixNQUFNekcsVUFBVSxFQUFFOzRCQUNwQnlHLE1BQU16RyxVQUFVLENBQUNJLHdCQUF3QixHQUFHLElBQUlyRTs0QkFDaEQscUNBQXFDOzRCQUNyQyxLQUFLLElBQUlzRSxZQUFZLE1BQUtDLGlCQUFpQixJQUFJLEVBQUUsQ0FBRTtnQ0FDakQsTUFBTSxNQUFLOEgsYUFBYSxDQUN0QixJQUFNL0gsU0FBU2lSLGFBQWEsQ0FBQzNSLEtBQUt2SCxTQUFTLEVBQUVxTyxNQUFNVixNQUFNLEVBQUVVLE1BQU16RyxVQUFVLEdBQzNFLENBQUMsRUFBRXRILGlCQUFpQixDQUFDLEVBQUUrVCxZQUFZLGNBQWMsQ0FBQzs0QkFDdEQ7d0JBQ0Y7b0JBQ0Y7Z0JBQ0Y7WUFDRixFQUFFLE9BQU9sTixLQUFLO2dCQUNaLE1BQUtyRyxPQUFPLENBQUNzRyxLQUFLLENBQUMscURBQXFERDtZQUMxRTtRQUNGOztJQUVNNkksY0FBYzZCLFFBQVEsRUFBRXNILEtBQUssRUFBRUMsVUFBVzs7ZUFBaEQsb0JBQUE7WUFDRSxNQUFNM04sWUFBWTlILEtBQUtDLEdBQUc7WUFDMUIsSUFBSXlWLGNBQWM7WUFDbEIsSUFBSUMsY0FBYztZQUVsQixNQUFNQzsyQkFBaUIsb0JBQUE7b0JBQ3JCLE1BQU0sSUFBSXBVLFFBQVFDLENBQUFBLE1BQU84TSxXQUFXOU0sS0FBSztvQkFDekMsSUFBSSxDQUFDa1UsYUFBYTt3QkFDaEJELGNBQWM7d0JBQ2QsTUFBS3ZZLE9BQU8sQ0FBQzRJLElBQUksQ0FBQyxDQUFDLEVBQUV5UCxNQUFNLCtDQUErQyxDQUFDO29CQUM3RTtnQkFDRjtnQ0FOTUk7Ozs7WUFRTkE7WUFDQSxJQUFJO2dCQUNGLE1BQU0xSDtZQUNSLEVBQUUsT0FBTzFLLEtBQUs7Z0JBQ1osSUFBSWlTLFlBQVk7b0JBQ2QsTUFBTWpTO2dCQUNSO2dCQUNBLE1BQUtyRyxPQUFPLENBQUNzRyxLQUFLLENBQUMsQ0FBQyxFQUFFK1IsTUFBTSx5QkFBeUIsQ0FBQyxFQUFFaFM7WUFDMUQ7WUFDQW1TLGNBQWM7WUFDZCxJQUFJRCxhQUFhO2dCQUNmLE1BQUt2WSxPQUFPLENBQUM0SSxJQUFJLENBQUMsQ0FBQyxFQUFFeVAsTUFBTSxjQUFjLEVBQUVwVSxLQUFLeVUsS0FBSyxDQUFDLEFBQUM3VixDQUFBQSxLQUFLQyxHQUFHLEtBQUs2SCxTQUFRLElBQUssTUFBTSxRQUFRLENBQUM7WUFDbEc7UUFDRjs7SUFFTXZFLGlCQUFpQmpGLGNBQWMsRUFBRUMsbUJBQW1CLEVBQUVmLE1BQU07O2VBQWxFLG9CQUFBO1lBQ0UsSUFBSTtnQkFDRixNQUFNc1kscUJBQXFCLEVBQUU7Z0JBQzdCLEtBQUssSUFBSXhSLFlBQVksTUFBS3dJLG1CQUFtQixDQUFFO29CQUM3QyxJQUFJLE1BQUtqUCwwQkFBMEIsQ0FBQ1MsZUFBZSxDQUFDZ0csU0FBU2pJLFNBQVMsQ0FBQyxLQUFLa0MsdUJBQzFFLE1BQUtTLGdCQUFnQixDQUFDc0YsU0FBU2pJLFNBQVMsTUFBTW1CLFFBQVE7d0JBQ3REc1ksbUJBQW1CM1csSUFBSSxDQUFDbUY7b0JBQzFCO2dCQUNGO2dCQUNBN0YsT0FBT0MsSUFBSSxDQUFDLE1BQUtnVCxxQkFBcUIsRUFBRTlTLE9BQU8sQ0FBQ29HLENBQUFBO29CQUM5QyxNQUFNM0ksWUFBWSxNQUFLcVYscUJBQXFCLENBQUMxTSxrQkFBa0IsQ0FBQzNJLFNBQVM7b0JBQ3pFLElBQUksTUFBS3dCLDBCQUEwQixDQUFDUyxlQUFlLENBQUNqQyxVQUFVLEtBQUtrQyx1QkFDNUQsTUFBS21ULHFCQUFxQixDQUFDMU0sa0JBQWtCLENBQUMxRyxjQUFjLEtBQUtBLGtCQUNqRSxNQUFLb0Isa0JBQWtCLENBQUNyRCxVQUFVLElBQ2xDLE1BQUtxRCxrQkFBa0IsQ0FBQ3JELFVBQVUsQ0FBQ21CLE1BQU0sS0FBS0EsUUFBUTt3QkFDM0QsT0FBTyxNQUFLa1UscUJBQXFCLENBQUMxTSxrQkFBa0I7b0JBQ3REO2dCQUNGO2dCQUNBLE1BQU0rUSxzQkFBc0JELG1CQUFtQmxLLEdBQUcsQ0FBQ3RILENBQUFBLFdBQVlBLFNBQVNqSSxTQUFTO2dCQUNqRixNQUFLUSxvQkFBb0IsQ0FBQ21aLGFBQWEsQ0FBQzFYLGdCQUFnQkMscUJBQXFCd1g7Z0JBQzdFLE1BQUszUCxjQUFjLENBQUM0UCxhQUFhLENBQUNEO2dCQUVsQyxLQUFLLElBQUl6UixZQUFZd1IsbUJBQW9CO29CQUN2Q3RVLFFBQVFGLE9BQU8sQ0FBQ2dELFNBQVNBLFFBQVEsQ0FBQzBSLGFBQWEsQ0FBQ3hZLFFBQVFjLGlCQUNyRHFHLEtBQUssQ0FBQ25CLENBQUFBLE1BQU8sTUFBS3JHLE9BQU8sQ0FBQ3NHLEtBQUssQ0FBQyx1Q0FBdUNEO2dCQUM1RTtZQUNGLEVBQUUsT0FBT0EsS0FBSztnQkFDWixNQUFLckcsT0FBTyxDQUFDc0csS0FBSyxDQUFDLHVDQUF1Q0Q7WUFDNUQ7UUFDRjs7SUFFQTZGLDRCQUE0QmhOLFNBQVMsRUFBRWlDLGNBQWMsRUFBRTtRQUNyRCxNQUFNZCxTQUFTLElBQUksQ0FBQ3dCLGdCQUFnQixDQUFDM0M7UUFDckMsT0FBTyxJQUFJLENBQUNzQixnQkFBZ0IsQ0FBQ0gsT0FBTyxDQUFDYyxlQUFlLENBQUMsSUFBSSxDQUFDVCwwQkFBMEIsQ0FBQ1MsZUFBZSxDQUFDakMsVUFBVSxDQUFDO0lBQ2xIO0lBRU00WixlQUFlM1gsY0FBYyxFQUFFZCxNQUFNOztlQUEzQyxvQkFBQTtZQUNFLElBQUksTUFBS0QsSUFBSSxFQUFFO2dCQUNiLE9BQU87b0JBQUNELEtBQUssTUFBS0MsSUFBSTtvQkFBRTJZLG1CQUFtQjtnQkFBSTtZQUNqRDtZQUVBLE1BQU1DLGNBQWMsTUFBTSxNQUFLQyxhQUFhLENBQUNDLFdBQVc7WUFDeEQsTUFBTUMsU0FBUyxDQUFDQyxXQUNkLENBQUMsUUFBUSxFQUFFQSxTQUFTLENBQUMsRUFBRS9ZLE9BQU8sQ0FBQyxFQUFFZ1osT0FBT0MsWUFBWSxDQUFDLEtBQUs1SyxPQUFPdk4saUJBQWlCLENBQUMsRUFBRTZYLFlBQVlPLE1BQU0sQ0FBQyxDQUFDO1lBRTNHLElBQUlwWjtZQUNKLElBQUksTUFBS3FaLG1CQUFtQixFQUFFO2dCQUM1QnJaLE1BQU1nWixPQUFPLE1BQUtNLFNBQVM7WUFDN0IsT0FBTztnQkFDTHRaLE1BQU1nWixPQUFPSCxZQUFZSSxRQUFRO1lBQ25DO1lBQ0EsTUFBTUwsb0JBQW9CNVksUUFBUWdaLE9BQU8sTUFBS00sU0FBUztZQUN2RCxPQUFPO2dCQUFDdFo7Z0JBQUs0WTtZQUFpQjtRQUNoQzs7SUFFQSxzQ0FBc0M7SUFDeEIxVCxjQUFjbEUsY0FBc0IsRUFBRUMsbUJBQW1CLEVBQUVmLE1BQU07O2VBQS9FLG9CQUFBO1lBQ0UsSUFBSSxNQUFLRCxJQUFJLEVBQUU7Z0JBQ2IsT0FBTyxNQUFLQSxJQUFJO1lBQ2xCO1lBRUEsTUFBTyxNQUFLRyxlQUFlLENBQUNGLE9BQU8sQ0FBQ2MsZUFBZSxDQUFDQyxvQkFBb0IsQ0FBQ2EsU0FBUyxDQUFFO2dCQUNsRixJQUFJO29CQUNGLE1BQU0rVyxjQUFjLE1BQU0sTUFBS0YsY0FBYyxDQUFDM1gsZ0JBQWdCZDtvQkFDOUQsTUFBTUYsTUFBTTZZLFlBQVk3WSxHQUFHO29CQUMzQixNQUFNNFksb0JBQW9CQyxZQUFZRCxpQkFBaUI7b0JBQ3ZELElBQUlXLGFBQWEsK0RBQ25CLENBQUMsSUFBSSxFQUFFdlosSUFBSSxDQUFDLEVBQUU0WSxvQkFBb0IsV0FBVyxZQUFZLFFBQVEsQ0FBQztvQkFDaEUsSUFBSSxNQUFLWSxhQUFhLElBQUksQ0FBQ1osbUJBQW1CO3dCQUM1Q1csY0FBYyw4RkFDaEI7d0JBQ0UsTUFBS0MsYUFBYSxHQUFHO29CQUN2QjtvQkFDQSxNQUFLM1osT0FBTyxDQUFDbUcsSUFBSSxDQUFDdVQ7b0JBQ2xCLE9BQU92WjtnQkFDVCxFQUFFLE9BQU9rRyxLQUFLO29CQUNaLE1BQUtyRyxPQUFPLENBQUNzRyxLQUFLLENBQUMsaUNBQWlDRDtvQkFDcEQsTUFBTSxJQUFJaEMsUUFBUUMsQ0FBQUEsTUFBTzhNLFdBQVc5TSxLQUFLO2dCQUMzQztZQUNGO1FBQ0Y7O0lBRUFtRSxpQkFBaUJ4SSxJQUFJLEVBQUVmLFNBQVMsRUFBRWlDLGNBQWMsRUFBRXlZLFFBQVEsRUFBRTtRQUMxRCxJQUFJLENBQUNDLGlCQUFpQixDQUFDMVksZUFBZSxHQUFHLElBQUksQ0FBQzBZLGlCQUFpQixDQUFDMVksZUFBZSxJQUFJLENBQUM7UUFDcEYsSUFBSSxDQUFDMFksaUJBQWlCLENBQUMxWSxlQUFlLENBQUNsQixLQUFLLEdBQUcsSUFBSSxDQUFDNFosaUJBQWlCLENBQUMxWSxlQUFlLENBQUNsQixLQUFLLElBQUksQ0FBQztRQUNoRyxJQUFJNlosV0FBVyxJQUFJLENBQUNELGlCQUFpQixDQUFDMVksZUFBZSxDQUFDbEIsS0FBSyxDQUFDZixVQUFVO1FBQ3RFLElBQUksQ0FBQzRhLFlBQWFBLFdBQVdqWCxLQUFLQyxHQUFHLEtBQUs4VyxVQUFXO1lBQ25ELElBQUksQ0FBQ0MsaUJBQWlCLENBQUMxWSxlQUFlLENBQUNsQixLQUFLLENBQUNmLFVBQVUsR0FBRzJELEtBQUtDLEdBQUc7WUFDbEUsT0FBTyxDQUFDLENBQUNnWDtRQUNYO1FBQ0EsT0FBTztJQUNUO0lBRUE5SCxzQkFBc0I5UyxTQUFTLEVBQUU7UUFDL0IsSUFBSSxJQUFJLENBQUNxRCxrQkFBa0IsQ0FBQ3JELFVBQVUsRUFBRTtZQUN0QyxJQUFJLENBQUNxRCxrQkFBa0IsQ0FBQ3JELFVBQVUsQ0FBQzBELFFBQVEsR0FBR0MsS0FBS0MsR0FBRztRQUN4RDtJQUNGO0lBRUEscUNBQXFDO0lBQy9CcUosK0JBQStCak4sU0FBUyxFQUFFaUMsY0FBYzs7ZUFBOUQsb0JBQUE7WUFDRSxNQUFNZCxTQUFTLE1BQUt3QixnQkFBZ0IsQ0FBQzNDO1lBQ3JDLElBQUksTUFBS3dCLDBCQUEwQixDQUFDUyxlQUFlLENBQUNqQyxVQUFVLEtBQUs2QyxXQUFXO2dCQUM1RSxJQUFJWCxzQkFBc0I7Z0JBQzFCLE1BQU8sTUFBS2tDLGNBQWMsSUFBSyxDQUFBLEFBQUMsSUFBSVQsS0FBSyxNQUFLUyxjQUFjLENBQUNDLG9CQUFvQixFQUFFbVAsT0FBTyxLQUFLN1AsS0FBS0MsR0FBRyxNQUNuRyxNQUFLNUIsb0JBQW9CLENBQUNDLGdCQUFnQlksV0FBVzFCLFFBQVFOLE1BQU0sR0FDbkUsTUFBS3VELGNBQWMsQ0FBQ0UsZ0JBQWdCLElBQ25DLElBQUlYLEtBQUssTUFBS1MsY0FBYyxDQUFDRyxZQUFZLEVBQUVpUCxPQUFPLEtBQUssTUFBS3FILDJCQUEyQixHQUFHLE9BQzNGbFgsS0FBS0MsR0FBRyxNQUFNLE1BQUs1QixvQkFBb0IsQ0FBQ0MsZ0JBQWdCWSxXQUFXMUIsUUFBUU4sTUFBTSxJQUNqRixNQUFLdUQsY0FBYyxDQUFDRSxnQkFBZ0IsRUFBSTtvQkFDMUMsTUFBTSxJQUFJYSxRQUFRQyxDQUFBQSxNQUFPOE0sV0FBVzlNLEtBQUs7Z0JBQzNDO2dCQUNBLElBQUssSUFBSTBWLFFBQVEsR0FBR0EsUUFBUSxNQUFLeFosZ0JBQWdCLENBQUNILE9BQU8sQ0FBQ2MsZUFBZSxDQUFDcEIsTUFBTSxFQUFFaWEsUUFBUztvQkFDekYsTUFBTUMsaUJBQWlCLE1BQUs3WCxvQkFBb0IsQ0FBQ2pCLGdCQUFnQjZZLE9BQU8zWixRQUFRTixNQUFNO29CQUN0RixNQUFNbUMsV0FBVyxNQUFLM0IsZUFBZSxDQUFDRixPQUFPLENBQUNjLGVBQWUsQ0FBQzZZLE1BQU07b0JBQ3BFLElBQUk5WCxTQUFTNEIsYUFBYSxFQUFFO3dCQUMxQixJQUFJNUIsU0FBUzRCLGFBQWEsQ0FBQzdELElBQUksS0FBSyxxREFDakMsQ0FBQSxJQUFJNEMsS0FBS1gsU0FBUzRCLGFBQWEsQ0FBQ1Asb0JBQW9CLEVBQUVtUCxPQUFPLEtBQUs3UCxLQUFLQyxHQUFHLE1BQzNFLE1BQUs1QixvQkFBb0IsQ0FBQ0MsZ0JBQWdCNlksT0FBTzNaLFFBQVFOLE1BQU0sSUFDL0RtQyxTQUFTNEIsYUFBYSxDQUFDTixnQkFBZ0IsQUFBRCxHQUFJOzRCQUMxQzt3QkFDRjt3QkFDQSxJQUFJdEIsU0FBUzRCLGFBQWEsQ0FBQzdELElBQUksS0FBSyw0Q0FDbEMsSUFBSTRDLEtBQUtYLFNBQVM0QixhQUFhLENBQUNQLG9CQUFvQixFQUFFbVAsT0FBTyxLQUFLN1AsS0FBS0MsR0FBRyxNQUMxRSxNQUFLNUIsb0JBQW9CLENBQUNDLGdCQUFnQjZZLE9BQU8zWixRQUFRTixNQUFNLElBQy9EbUMsU0FBUzRCLGFBQWEsQ0FBQ04sZ0JBQWdCLEVBQUU7NEJBQ3pDO3dCQUNGO29CQUNGO29CQUNBLElBQUl5VyxpQkFBaUIsTUFBS0MsdUJBQXVCLEVBQUU7d0JBQ2pEOVksc0JBQXNCNFk7d0JBQ3RCO29CQUNGO2dCQUNGO2dCQUNBLElBQUk1WSx3QkFBd0IsTUFBTTtvQkFDaENBLHNCQUFzQixNQUFLWixnQkFBZ0IsQ0FBQ0gsT0FBTyxDQUFDYyxlQUFlLENBQUNwQixNQUFNO29CQUMxRSxNQUFNLE1BQUtnRSxPQUFPLENBQUM1QyxnQkFBZ0JkO2dCQUNyQztnQkFDQSxNQUFLSywwQkFBMEIsQ0FBQ1MsZUFBZSxDQUFDakMsVUFBVSxHQUFHa0M7WUFDL0Q7UUFDRjs7SUFFUUwsd0JBQXdCO1FBQzlCLE1BQU1vWixPQUFPdFgsS0FBS0MsR0FBRztRQUNyQnhCLE9BQU9DLElBQUksQ0FBQyxJQUFJLENBQUNnQixrQkFBa0IsRUFBRWQsT0FBTyxDQUFDaUIsQ0FBQUE7WUFDM0MsTUFBTStELE9BQU8sSUFBSSxDQUFDbEUsa0JBQWtCLENBQUNHLFVBQVU7WUFDL0MsSUFBSStELFFBQVFBLEtBQUs5RCxXQUFXLEtBQUssS0FBS3dYLE9BQU8xVCxLQUFLN0QsUUFBUSxHQUFHLElBQUksS0FBSyxLQUFLLE1BQU07Z0JBQy9FLE1BQU1wRCxtQkFBbUIsSUFBSSxDQUFDQyxvQkFBb0IsQ0FBQ2lELFVBQVU7Z0JBQzdELE1BQU1ELFdBQVduQixPQUFPeUgsTUFBTSxDQUFNLElBQUksQ0FBQ25JLGdCQUFnQixDQUFDcEIsaUJBQWlCLElBQUksQ0FBQztnQkFDaEZpRCxTQUFTaEIsT0FBTyxDQUFDME8sQ0FBQUE7b0JBQ2YsT0FBTyxJQUFJLENBQUMxUSxvQkFBb0IsQ0FBQzBRLFFBQVE7b0JBQ3pDLE9BQU8sSUFBSSxDQUFDNU4sa0JBQWtCLENBQUM0TixRQUFRO2dCQUN6QztnQkFDQSxPQUFPLElBQUksQ0FBQ3ZQLGdCQUFnQixDQUFDcEIsaUJBQWlCO2dCQUM5QyxJQUFJLENBQUNRLE9BQU8sQ0FBQytDLEtBQUssQ0FBQyxDQUFDLEVBQUV2RCxpQkFBaUIsdUNBQXVDLENBQUM7WUFDakY7UUFDRjtJQUNGO0lBRUE0YSw0QkFBNEI7UUFDMUIsTUFBTUQsT0FBT3RYLEtBQUtDLEdBQUc7UUFDckJ4QixPQUFPQyxJQUFJLENBQUMsSUFBSSxDQUFDOEssc0JBQXNCLEVBQUU1SyxPQUFPLENBQUNvRyxDQUFBQTtZQUMvQyxJQUFJLElBQUksQ0FBQ3dFLHNCQUFzQixDQUFDeEUsa0JBQWtCLENBQUN5RSxXQUFXLEdBQUc2TixPQUFPLEtBQUssS0FBSyxNQUFNO2dCQUN0RixPQUFPLElBQUksQ0FBQzlOLHNCQUFzQixDQUFDeEUsa0JBQWtCO1lBQ3ZEO1FBQ0Y7SUFDRjtJQW5yRUE7Ozs7R0FJQyxHQUVEOzs7Ozs7R0FNQyxHQUNELHFEQUFxRDtJQUNyRHdTLFlBQVlDLE9BQU8sRUFBRUMsWUFBWSxFQUFFQyxLQUFLLEVBQUVsSixJQUFJLENBQUU7UUE1RGhELHVCQUFRMkgsaUJBQVIsS0FBQTtRQUNBLHVCQUFRMU4sZ0JBQVIsS0FBQTtRQUNBLHVCQUFRa1AsV0FBUixLQUFBO1FBQ0EsdUJBQVFuYSxXQUFSLEtBQUE7UUFDQSx1QkFBUW1aLGFBQVIsS0FBQTtRQUNBLHVCQUFRaUIsWUFBUixLQUFBO1FBQ0EsdUJBQVF0YSxRQUFSLEtBQUE7UUFDQSx1QkFBUXlTLG1CQUFSLEtBQUE7UUFDQSx1QkFBUWhOLG1CQUFSLEtBQUE7UUFDQSx1QkFBUXdNLFlBQVIsS0FBQTtRQUNBLHVCQUFRRSwyQkFBUixLQUFBO1FBQ0EsdUJBQVFDLDJCQUFSLEtBQUE7UUFDQSx1QkFBUTBILDJCQUFSLEtBQUE7UUFDQSx1QkFBUUgsK0JBQVIsS0FBQTtRQUNBLHVCQUFReEosOEJBQVIsS0FBQTtRQUNBLHVCQUFRaUosdUJBQVIsS0FBQTtRQUNBLHVCQUFROVEsa0NBQVIsS0FBQTtRQUNBLHVCQUFRaEUsa0NBQVIsS0FBQTtRQUNBLHVCQUFRN0UsbUJBQVIsS0FBQTtRQUNBLHVCQUFRbUcsVUFBUixLQUFBO1FBQ0EsdUJBQVFvQyw2QkFBUixLQUFBO1FBQ0EsdUJBQVFoQixxQkFBUixLQUFBO1FBQ0EsdUJBQVF1SSx1QkFBUixLQUFBO1FBQ0EsdUJBQVFuTyxtQkFBUixLQUFBO1FBQ0EsdUJBQVFoQixvQkFBb0MsQ0FBQztRQUM3Qyx1QkFBUUUsOEJBQXdGLENBQUM7UUFDakcsdUJBQVE2QixzQkFBd0MsQ0FBQztRQUNqRCx1QkFBUTlDLHdCQUFpRSxDQUFDO1FBQzFFLHVCQUFRbUIsb0JBQTJELENBQUM7UUFDcEUsdUJBQVFzRSxpQ0FBUixLQUFBO1FBQ0EsdUJBQVF4Rix3QkFBUixLQUFBO1FBQ0EsdUJBQVFtVSxpQkFBUixLQUFBO1FBQ0EsdUJBQVFsRCxnQkFBaUUsQ0FBQztRQUMxRSx1QkFBUTRELHlCQUE0RSxDQUFDO1FBQ3JGLHVCQUFRckUsZ0NBQVIsS0FBQTtRQUNBLHVCQUFRNU0sa0JBQVIsS0FBQTtRQUNBLHVCQUFRcVcsaUJBQVIsS0FBQTtRQUNBLHVCQUFRRSxxQkFBUixLQUFBO1FBQ0EsdUJBQVE1USxrQkFBUixLQUFBO1FBQ0EsdUJBQVFvRCwwQkFBUixLQUFBO1FBQ0EsdUJBQVFnQyxpQkFBa0QsQ0FBQztRQUMzRCx1QkFBUS9GLGlCQUFSLEtBQUE7UUFDQSx1QkFBUXRJLFdBQVIsS0FBQTtRQUNBLHVCQUFRb0osOEJBQVIsS0FBQTtRQUNBLHVCQUFRQyxrQ0FBUixLQUFBO1FBaUJFLE1BQU1zUixZQUFZLElBQUkxYztRQUN0QnFULE9BQU9BLFFBQVEsQ0FBQztRQUNoQkEsS0FBS3NKLHFCQUFxQixHQUFHRCxVQUFVRSxlQUFlLENBQUN2SixLQUFLc0oscUJBQXFCLEVBQUUsSUFBSTtRQUN2RnRKLEtBQUtyTSx3QkFBd0IsR0FBR3FNLEtBQUtyTSx3QkFBd0IsSUFBSSxDQUFDO1FBQ2xFLElBQUksQ0FBQ2dVLGFBQWEsR0FBR3NCO1FBQ3JCLElBQUksQ0FBQ2hQLFlBQVksR0FBRytGLEtBQUt4SixXQUFXLElBQUk7UUFDeEMsSUFBSSxDQUFDMlMsT0FBTyxHQUFHbkosS0FBS2lJLE1BQU0sSUFBSTtRQUM5QixJQUFJLENBQUNqWixPQUFPLEdBQUdnUixLQUFLalIsTUFBTTtRQUMxQixJQUFJLENBQUNvWixTQUFTLEdBQUc7UUFDakIsSUFBSSxDQUFDaUIsUUFBUSxHQUFHSjtRQUNoQixJQUFJLENBQUNsYSxJQUFJLEdBQUc7UUFDWixJQUFJLENBQUN5UyxlQUFlLEdBQUc4SCxVQUFVRSxlQUFlLENBQUN2SixLQUFLd0osY0FBYyxFQUFFLElBQUksb0JBQW9CO1FBQzlGLElBQUksQ0FBQ2pWLGVBQWUsR0FBRzhVLFVBQVVFLGVBQWUsQ0FBQ3ZKLEtBQUt5SixjQUFjLEVBQUUsSUFBSSxvQkFBb0I7UUFDOUYsTUFBTUMsWUFBWTFKLEtBQUswSixTQUFTLElBQUksQ0FBQztRQUNyQyxJQUFJLENBQUMzSSxRQUFRLEdBQUdzSSxVQUFVTSxjQUFjLENBQUNELFVBQVVFLE9BQU8sRUFBRSxHQUFHO1FBQy9ELElBQUksQ0FBQzNJLHVCQUF1QixHQUFHb0ksVUFBVUUsZUFBZSxDQUFDRyxVQUFVRyxpQkFBaUIsRUFBRSxHQUNwRjtRQUNGLElBQUksQ0FBQzNJLHVCQUF1QixHQUFHbUksVUFBVUUsZUFBZSxDQUFDRyxVQUFVSSxpQkFBaUIsRUFBRSxJQUNwRjtRQUNGLElBQUksQ0FBQ2xCLHVCQUF1QixHQUFHO1FBQy9CLElBQUksQ0FBQ0gsMkJBQTJCLEdBQUdZLFVBQVVFLGVBQWUsQ0FBQ0csVUFBVUssMEJBQTBCLEVBQUUsS0FDakc7UUFDRixJQUFJLENBQUM5SywwQkFBMEIsR0FBRztRQUNsQyxJQUFJLENBQUNpSixtQkFBbUIsR0FBR21CLFVBQVVXLGVBQWUsQ0FBQ2hLLEtBQUtpSyxrQkFBa0IsRUFBRSxPQUFPO1FBQ3JGLElBQUksQ0FBQzdTLDhCQUE4QixHQUFHaVMsVUFBVUUsZUFBZSxDQUFDdkosS0FBS2tLLHNDQUFzQyxFQUFFLElBQzNHLDRDQUE0QztRQUM5QyxJQUFJLENBQUM5Vyw4QkFBOEIsR0FBRztRQUN0QyxJQUFJLENBQUM3RSxlQUFlLEdBQUcsSUFBSW5CLGVBQWUsSUFBSSxFQUFFOGIsT0FBTyxJQUFJLENBQUMzVSxlQUFlO1FBQzNFLElBQUksQ0FBQ0csTUFBTSxHQUFHd1U7UUFDZCxJQUFJLENBQUNwUyx5QkFBeUIsR0FBRyxDQUFDO1FBQ2xDLElBQUksQ0FBQ2hCLGlCQUFpQixHQUFHLEVBQUU7UUFDM0IsSUFBSSxDQUFDdUksbUJBQW1CLEdBQUcsRUFBRTtRQUM3QixJQUFJLENBQUNuTyxlQUFlLEdBQUcsQ0FBQztRQUN4QixJQUFJLENBQUMwRCw2QkFBNkIsR0FBR29NLEtBQUtyTSx3QkFBd0I7UUFDbEUsSUFBSSxDQUFDdkYsb0JBQW9CLEdBQUcsSUFBSW5CLG9CQUFvQixJQUFJLEVBQUUrYjtRQUMxRCxJQUFJLENBQUN6RyxhQUFhLEdBQUcsQ0FBQztRQUN0QixJQUFJLENBQUMzRCw0QkFBNEIsR0FBRyxDQUFDO1FBQ3JDLElBQUksQ0FBQzVNLGNBQWMsR0FBRztRQUN0QixJQUFJLENBQUNxVyxhQUFhLEdBQUc7UUFDckIsSUFBSSxDQUFDRSxpQkFBaUIsR0FBRyxDQUFDO1FBQzFCLElBQUksQ0FBQzVRLGNBQWMsR0FBRyxJQUFJNUssY0FBYyxJQUFJLEVBQUVpVCxLQUFLc0oscUJBQXFCO1FBQ3hFLElBQUksQ0FBQzNSLGNBQWMsQ0FBQzlELEtBQUs7UUFDekIsSUFBSSxDQUFDa0gsc0JBQXNCLEdBQUcsQ0FBQztRQUMvQixJQUFJaUYsS0FBS21LLFlBQVksSUFBSW5LLEtBQUttSyxZQUFZLENBQUNDLE9BQU8sRUFBRTtZQUNsRCxJQUFJLENBQUNwVCxhQUFhLEdBQUcsSUFBSTFKLGFBQWEwUyxLQUFLbUssWUFBWTtZQUN2RCxJQUFJLENBQUNuVCxhQUFhLENBQUNuRCxLQUFLO1FBQzFCO1FBQ0EsSUFBSSxDQUFDbkYsT0FBTyxHQUFHeEIsY0FBY21kLFNBQVMsQ0FBQztRQUN2QyxJQUFJLENBQUNySyxLQUFLc0ssbUJBQW1CLEVBQUU7WUFDN0IsSUFBSSxDQUFDeFMsMEJBQTBCLEdBQUd5UyxZQUFZLElBQUksQ0FBQzlhLHFCQUFxQixDQUFDQyxJQUFJLENBQUMsSUFBSSxHQUFHLEtBQUssS0FBSztZQUMvRixJQUFJLENBQUNxSSw4QkFBOEIsR0FBR3dTLFlBQVksSUFBSSxDQUFDekIseUJBQXlCLENBQUNwWixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksS0FBSztRQUN4RztJQUNGO0FBaW5FRjtBQW9DQSxlQUFlaEMsdUJBQXVCIn0=