UNPKG

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)

316 lines (315 loc) 39.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "default", { enumerable: true, get: function() { return MemoryHistoryStorage; } }); const _historyStorage = /*#__PURE__*/ _interop_require_default(require("./historyStorage")); const _index = /*#__PURE__*/ _interop_require_default(require("./historyDatabase/index")); const _binarysearchtree = require("binary-search-tree"); const _logger = /*#__PURE__*/ _interop_require_default(require("../logger")); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } let MemoryHistoryStorage = class MemoryHistoryStorage extends _historyStorage.default { /** * Initializes the storage and loads required data from a persistent storage * @param {string} accountId account id * @param {string} [application] application. Default is MetaApi */ async initialize(accountId, application = "MetaApi") { await super.initialize(accountId, application); let { deals, historyOrders } = await this._historyDatabase.loadHistory(accountId, application); for (let deal of deals){ await this._addDeal(deal, true); } for (let historyOrder of historyOrders){ await this._addHistoryOrder(historyOrder, true); } } /** * Resets the storage. Intended for use in tests * @returns {Promise} promise when the history is removed */ async clear() { this._reset(); await this._historyDatabase.clear(this._accountId, this._application); } /** * Returns the time of the last history order record stored in the history storage * @param {Number} [instanceNumber] index of an account instance connected * @returns {Date} the time of the last history order record stored in the history storage */ lastHistoryOrderTime(instanceNumber) { return this._maxHistoryOrderTime; } /** * Returns the time of the last history deal record stored in the history storage * @param {Number} [instanceNumber] index of an account instance connected * @returns {Date} the time of the last history deal record stored in the history storage */ lastDealTime(instanceNumber) { return this._maxDealTime; } /** * Invoked when a new MetaTrader history order is added * @param {String} instanceIndex index of an account instance connected * @param {MetatraderOrder} historyOrder new MetaTrader history order */ async onHistoryOrderAdded(instanceIndex, historyOrder) { await this._addHistoryOrder(historyOrder); } /** * Invoked when a new MetaTrader history deal is added * @param {String} instanceIndex index of an account instance connected * @param {MetatraderDeal} deal new MetaTrader history deal */ async onDealAdded(instanceIndex, deal) { await this._addDeal(deal); } /** * Returns all deals * @returns {Array<MetatraderDeal>} all deals */ get deals() { return this.getDealsByTimeRange(new Date(0), new Date(8640000000000000)); } /** * Returns deals by ticket id * @param {string} id ticket id * @returns {Array<MetatraderDeal>} deals found */ getDealsByTicket(id) { let deals = Object.values(this._dealsByTicket[id] || {}); deals.sort(this._dealsComparator); return deals; } /** * Returns deals by position id * @param {string} positionId position id * @returns {Array<MetatraderDeal>} deals found */ getDealsByPosition(positionId) { let deals = Object.values(this._dealsByPosition[positionId] || {}); deals.sort(this._dealsComparator); return deals; } /** * Returns deals by time range * @param startTime start time, inclusive * @param endTime end time, inclusive * @returns {Array<MetatraderDeal>} deals found */ getDealsByTimeRange(startTime, endTime) { let deals = this._dealsByTime.betweenBounds({ $gte: { time: startTime, id: 0, entryType: "" }, $lte: { time: endTime, id: Number.MAX_VALUE, entryType: "" } }); return deals; } /** * Returns all history orders * @returns {Array<MetatraderOrder>} all history orders */ get historyOrders() { return this.getHistoryOrdersByTimeRange(new Date(0), new Date(8640000000000000)); } /** * Returns history orders by ticket id * @param {string} id ticket id * @returns {Array<MetatraderOrder>} history orders found */ getHistoryOrdersByTicket(id) { let historyOrders = Object.values(this._historyOrdersByTicket[id] || {}); historyOrders.sort(this._historyOrdersComparator); return historyOrders; } /** * Returns history orders by position id * @param {string} positionId position id * @returns {Array<MetatraderOrder>} history orders found */ getHistoryOrdersByPosition(positionId) { let historyOrders = Object.values(this._historyOrdersByPosition[positionId] || {}); historyOrders.sort(this._historyOrdersComparator); return historyOrders; } /** * Returns history orders by time range * @param startTime start time, inclusive * @param endTime end time, inclusive * @returns {Array<MetatraderOrder>} hisotry orders found */ getHistoryOrdersByTimeRange(startTime, endTime) { let historyOrders = this._historyOrdersByTime.betweenBounds({ $gte: { doneTime: startTime, id: 0, type: "", state: "" }, $lte: { doneTime: endTime, id: Number.MAX_VALUE, type: "", state: "" } }); return historyOrders; } /** * Invoked when a synchronization of history deals on a MetaTrader account have finished to indicate progress of an * initial terminal state synchronization * @param {String} instanceIndex index of an account instance connected * @param {String} synchronizationId synchronization request id * @return {Promise} promise which resolves when the asynchronous event is processed */ async onDealsSynchronized(instanceIndex, synchronizationId) { await this._flushDatabase(); await super.onDealsSynchronized(instanceIndex, synchronizationId); } _reset() { this._orderSynchronizationFinished = {}; this._dealSynchronizationFinished = {}; this._dealsByTicket = {}; this._dealsByPosition = {}; this._historyOrdersByTicket = {}; this._historyOrdersByPosition = {}; // eslint-disable-next-line complexity this._historyOrdersComparator = (o1, o2)=>{ let timeDiff = (o1.doneTime || new Date(0)).getTime() - (o2.doneTime || new Date(0)).getTime(); if (timeDiff === 0) { let idDiff = o1.id - o2.id; if (idDiff === 0) { if (o1.type > o2.type) { return 1; } else if (o1.type < o2.type) { return -1; } else { if (o1.state > o2.state) { return 1; } else if (o1.state < o2.state) { return -1; } else { return 0; } } } else { return idDiff; } } else { return timeDiff; } }; this._historyOrdersByTime = new _binarysearchtree.AVLTree({ compareKeys: this._historyOrdersComparator }); this._dealsComparator = (d1, d2)=>{ let timeDiff = (d1.time || new Date(0)).getTime() - (d2.time || new Date(0)).getTime(); if (timeDiff === 0) { let idDiff = d1.id - d2.id; if (idDiff === 0) { if (d1.entryType > d2.entryType) { return 1; } else if (d1.entryType < d2.entryType) { return -1; } else { return 0; } } else { return idDiff; } } else { return timeDiff; } }; this._dealsByTime = new _binarysearchtree.AVLTree({ compareKeys: this._dealsComparator }); this._maxHistoryOrderTime = new Date(0); this._maxDealTime = new Date(0); this._newHistoryOrders = []; this._newDeals = []; clearTimeout(this._flushTimeout); delete this._flushTimeout; } // eslint-disable-next-line complexity async _addDeal(deal, existing) { let key = this._getDealKey(deal); this._dealsByTicket[deal.id] = this._dealsByTicket[deal.id] || {}; let newDeal = !existing && !this._dealsByTicket[deal.id][key]; this._dealsByTicket[deal.id][key] = deal; if (deal.positionId) { this._dealsByPosition[deal.positionId] = this._dealsByPosition[deal.positionId] || {}; this._dealsByPosition[deal.positionId][key] = deal; } this._dealsByTime.delete(deal); this._dealsByTime.insert(deal, deal); if (deal.time && (!this._maxDealTime || this._maxDealTime.getTime() < deal.time.getTime())) { this._maxDealTime = deal.time; } if (newDeal) { this._newDeals.push(deal); clearTimeout(this._flushTimeout); this._flushTimeout = setTimeout(this._flushDatabase.bind(this), 5000); } } _getDealKey(deal) { return (deal.time || new Date(0)).toISOString() + ":" + deal.id + ":" + deal.entryType; } // eslint-disable-next-line complexity async _addHistoryOrder(historyOrder, existing) { let key = this._getHistoryOrderKey(historyOrder); this._historyOrdersByTicket[historyOrder.id] = this._historyOrdersByTicket[historyOrder.id] || {}; let newHistoryOrder = !existing && !this._historyOrdersByTicket[historyOrder.id][key]; this._historyOrdersByTicket[historyOrder.id][key] = historyOrder; if (historyOrder.positionId) { this._historyOrdersByPosition[historyOrder.positionId] = this._historyOrdersByPosition[historyOrder.positionId] || {}; this._historyOrdersByPosition[historyOrder.positionId][key] = historyOrder; } this._historyOrdersByTime.delete(historyOrder); this._historyOrdersByTime.insert(historyOrder, historyOrder); if (historyOrder.doneTime && (!this._maxHistoryOrderTime || this._maxHistoryOrderTime.getTime() < historyOrder.doneTime.getTime())) { this._maxHistoryOrderTime = historyOrder.doneTime; } if (newHistoryOrder) { this._newHistoryOrders.push(historyOrder); clearTimeout(this._flushTimeout); this._flushTimeout = setTimeout(this._flushDatabase.bind(this), 5000); } } _getHistoryOrderKey(historyOrder) { return (historyOrder.doneTime || new Date(0)).toISOString() + ":" + historyOrder.id + ":" + historyOrder.type + ":" + historyOrder.state; } async _flushDatabase() { if (this._flushPromise) { await this._flushPromise; } if (this._flushRunning) { return; } this._flushRunning = true; let resolve; this._flushPromise = new Promise((res)=>resolve = res); try { await this._historyDatabase.flush(this._accountId, this._application, this._newHistoryOrders, this._newDeals); this._newHistoryOrders = []; this._newDeals = []; this._logger.debug(`${this._accountId}: flushed history db`); } catch (err) { this._logger.warn(`${this._accountId}: error flushing history db`, err); this._flushTimeout = setTimeout(this._flushDatabase.bind(this), 15000); } finally{ resolve(); this._flushRunning = false; } } /** * Constructs the in-memory history store instance */ constructor(){ super(); this._historyDatabase = _index.default.getInstance(); this._reset(); this._logger = _logger.default.getLogger("MemoryHistoryStorage"); } }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBIaXN0b3J5U3RvcmFnZSBmcm9tICcuL2hpc3RvcnlTdG9yYWdlJztcbmltcG9ydCBIaXN0b3J5RGF0YWJhc2UgZnJvbSAnLi9oaXN0b3J5RGF0YWJhc2UvaW5kZXgnO1xuaW1wb3J0IHsgQVZMVHJlZSB9IGZyb20gJ2JpbmFyeS1zZWFyY2gtdHJlZSc7XG5pbXBvcnQgTG9nZ2VyTWFuYWdlciBmcm9tICcuLi9sb2dnZXInO1xuXG4vKipcbiAqIEhpc3Rvcnkgc3RvcmFnZSB3aGljaCBzdG9yZXMgTWV0YVRyYWRlciBoaXN0b3J5IGluIFJBTVxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBNZW1vcnlIaXN0b3J5U3RvcmFnZSBleHRlbmRzIEhpc3RvcnlTdG9yYWdlIHtcblxuICAvKipcbiAgICogQ29uc3RydWN0cyB0aGUgaW4tbWVtb3J5IGhpc3Rvcnkgc3RvcmUgaW5zdGFuY2VcbiAgICovXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5faGlzdG9yeURhdGFiYXNlID0gSGlzdG9yeURhdGFiYXNlLmdldEluc3RhbmNlKCk7XG4gICAgdGhpcy5fcmVzZXQoKTtcbiAgICB0aGlzLl9sb2dnZXIgPSBMb2dnZXJNYW5hZ2VyLmdldExvZ2dlcignTWVtb3J5SGlzdG9yeVN0b3JhZ2UnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbml0aWFsaXplcyB0aGUgc3RvcmFnZSBhbmQgbG9hZHMgcmVxdWlyZWQgZGF0YSBmcm9tIGEgcGVyc2lzdGVudCBzdG9yYWdlXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gW2FwcGxpY2F0aW9uXSBhcHBsaWNhdGlvbi4gRGVmYXVsdCBpcyBNZXRhQXBpXG4gICAqL1xuICBhc3luYyBpbml0aWFsaXplKGFjY291bnRJZCwgYXBwbGljYXRpb24gPSAnTWV0YUFwaScpIHtcbiAgICBhd2FpdCBzdXBlci5pbml0aWFsaXplKGFjY291bnRJZCwgYXBwbGljYXRpb24pO1xuICAgIGxldCB7ZGVhbHMsIGhpc3RvcnlPcmRlcnN9ID0gYXdhaXQgdGhpcy5faGlzdG9yeURhdGFiYXNlLmxvYWRIaXN0b3J5KGFjY291bnRJZCwgYXBwbGljYXRpb24pO1xuICAgIGZvciAobGV0IGRlYWwgb2YgZGVhbHMpIHtcbiAgICAgIGF3YWl0IHRoaXMuX2FkZERlYWwoZGVhbCwgdHJ1ZSk7XG4gICAgfVxuICAgIGZvciAobGV0IGhpc3RvcnlPcmRlciBvZiBoaXN0b3J5T3JkZXJzKSB7XG4gICAgICBhd2FpdCB0aGlzLl9hZGRIaXN0b3J5T3JkZXIoaGlzdG9yeU9yZGVyLCB0cnVlKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVzZXRzIHRoZSBzdG9yYWdlLiBJbnRlbmRlZCBmb3IgdXNlIGluIHRlc3RzXG4gICAqIEByZXR1cm5zIHtQcm9taXNlfSBwcm9taXNlIHdoZW4gdGhlIGhpc3RvcnkgaXMgcmVtb3ZlZFxuICAgKi9cbiAgYXN5bmMgY2xlYXIoKSB7XG4gICAgdGhpcy5fcmVzZXQoKTtcbiAgICBhd2FpdCB0aGlzLl9oaXN0b3J5RGF0YWJhc2UuY2xlYXIodGhpcy5fYWNjb3VudElkLCB0aGlzLl9hcHBsaWNhdGlvbik7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgdGltZSBvZiB0aGUgbGFzdCBoaXN0b3J5IG9yZGVyIHJlY29yZCBzdG9yZWQgaW4gdGhlIGhpc3Rvcnkgc3RvcmFnZVxuICAgKiBAcGFyYW0ge051bWJlcn0gW2luc3RhbmNlTnVtYmVyXSBpbmRleCBvZiBhbiBhY2NvdW50IGluc3RhbmNlIGNvbm5lY3RlZFxuICAgKiBAcmV0dXJucyB7RGF0ZX0gdGhlIHRpbWUgb2YgdGhlIGxhc3QgaGlzdG9yeSBvcmRlciByZWNvcmQgc3RvcmVkIGluIHRoZSBoaXN0b3J5IHN0b3JhZ2VcbiAgICovXG4gIGxhc3RIaXN0b3J5T3JkZXJUaW1lKGluc3RhbmNlTnVtYmVyKSB7XG4gICAgcmV0dXJuIHRoaXMuX21heEhpc3RvcnlPcmRlclRpbWU7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgdGltZSBvZiB0aGUgbGFzdCBoaXN0b3J5IGRlYWwgcmVjb3JkIHN0b3JlZCBpbiB0aGUgaGlzdG9yeSBzdG9yYWdlXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBbaW5zdGFuY2VOdW1iZXJdIGluZGV4IG9mIGFuIGFjY291bnQgaW5zdGFuY2UgY29ubmVjdGVkXG4gICAqIEByZXR1cm5zIHtEYXRlfSB0aGUgdGltZSBvZiB0aGUgbGFzdCBoaXN0b3J5IGRlYWwgcmVjb3JkIHN0b3JlZCBpbiB0aGUgaGlzdG9yeSBzdG9yYWdlXG4gICAqL1xuICBsYXN0RGVhbFRpbWUoaW5zdGFuY2VOdW1iZXIpIHtcbiAgICByZXR1cm4gdGhpcy5fbWF4RGVhbFRpbWU7XG4gIH1cblxuICAvKipcbiAgICogSW52b2tlZCB3aGVuIGEgbmV3IE1ldGFUcmFkZXIgaGlzdG9yeSBvcmRlciBpcyBhZGRlZFxuICAgKiBAcGFyYW0ge1N0cmluZ30gaW5zdGFuY2VJbmRleCBpbmRleCBvZiBhbiBhY2NvdW50IGluc3RhbmNlIGNvbm5lY3RlZFxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJPcmRlcn0gaGlzdG9yeU9yZGVyIG5ldyBNZXRhVHJhZGVyIGhpc3Rvcnkgb3JkZXJcbiAgICovXG4gIGFzeW5jIG9uSGlzdG9yeU9yZGVyQWRkZWQoaW5zdGFuY2VJbmRleCwgaGlzdG9yeU9yZGVyKSB7XG4gICAgYXdhaXQgdGhpcy5fYWRkSGlzdG9yeU9yZGVyKGhpc3RvcnlPcmRlcik7XG4gIH1cblxuICAvKipcbiAgICogSW52b2tlZCB3aGVuIGEgbmV3IE1ldGFUcmFkZXIgaGlzdG9yeSBkZWFsIGlzIGFkZGVkXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBpbnN0YW5jZUluZGV4IGluZGV4IG9mIGFuIGFjY291bnQgaW5zdGFuY2UgY29ubmVjdGVkXG4gICAqIEBwYXJhbSB7TWV0YXRyYWRlckRlYWx9IGRlYWwgbmV3IE1ldGFUcmFkZXIgaGlzdG9yeSBkZWFsXG4gICAqL1xuICBhc3luYyBvbkRlYWxBZGRlZChpbnN0YW5jZUluZGV4LCBkZWFsKSB7XG4gICAgYXdhaXQgdGhpcy5fYWRkRGVhbChkZWFsKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGFsbCBkZWFsc1xuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlckRlYWw+fSBhbGwgZGVhbHNcbiAgICovXG4gIGdldCBkZWFscygpIHtcbiAgICByZXR1cm4gdGhpcy5nZXREZWFsc0J5VGltZVJhbmdlKG5ldyBEYXRlKDApLCBuZXcgRGF0ZSg4NjQwMDAwMDAwMDAwMDAwKSk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBkZWFscyBieSB0aWNrZXQgaWRcbiAgICogQHBhcmFtIHtzdHJpbmd9IGlkIHRpY2tldCBpZFxuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlckRlYWw+fSBkZWFscyBmb3VuZFxuICAgKi9cbiAgZ2V0RGVhbHNCeVRpY2tldChpZCkge1xuICAgIGxldCBkZWFscyA9IE9iamVjdC52YWx1ZXModGhpcy5fZGVhbHNCeVRpY2tldFtpZF0gfHwge30pO1xuICAgIGRlYWxzLnNvcnQodGhpcy5fZGVhbHNDb21wYXJhdG9yKTtcbiAgICByZXR1cm4gZGVhbHM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBkZWFscyBieSBwb3NpdGlvbiBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gcG9zaXRpb25JZCBwb3NpdGlvbiBpZFxuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlckRlYWw+fSBkZWFscyBmb3VuZFxuICAgKi9cbiAgZ2V0RGVhbHNCeVBvc2l0aW9uKHBvc2l0aW9uSWQpIHtcbiAgICBsZXQgZGVhbHMgPSBPYmplY3QudmFsdWVzKHRoaXMuX2RlYWxzQnlQb3NpdGlvbltwb3NpdGlvbklkXSB8fCB7fSk7XG4gICAgZGVhbHMuc29ydCh0aGlzLl9kZWFsc0NvbXBhcmF0b3IpO1xuICAgIHJldHVybiBkZWFscztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGRlYWxzIGJ5IHRpbWUgcmFuZ2VcbiAgICogQHBhcmFtIHN0YXJ0VGltZSBzdGFydCB0aW1lLCBpbmNsdXNpdmVcbiAgICogQHBhcmFtIGVuZFRpbWUgZW5kIHRpbWUsIGluY2x1c2l2ZVxuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlckRlYWw+fSBkZWFscyBmb3VuZFxuICAgKi9cbiAgZ2V0RGVhbHNCeVRpbWVSYW5nZShzdGFydFRpbWUsIGVuZFRpbWUpIHtcbiAgICBsZXQgZGVhbHMgPSB0aGlzLl9kZWFsc0J5VGltZS5iZXR3ZWVuQm91bmRzKHtcbiAgICAgICRndGU6IHt0aW1lOiBzdGFydFRpbWUsIGlkOiAwLCBlbnRyeVR5cGU6ICcnfSxcbiAgICAgICRsdGU6IHt0aW1lOiBlbmRUaW1lLCBpZDogTnVtYmVyLk1BWF9WQUxVRSwgZW50cnlUeXBlOiAnJ31cbiAgICB9KTtcbiAgICByZXR1cm4gZGVhbHM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBhbGwgaGlzdG9yeSBvcmRlcnNcbiAgICogQHJldHVybnMge0FycmF5PE1ldGF0cmFkZXJPcmRlcj59IGFsbCBoaXN0b3J5IG9yZGVyc1xuICAgKi9cbiAgZ2V0IGhpc3RvcnlPcmRlcnMoKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0SGlzdG9yeU9yZGVyc0J5VGltZVJhbmdlKG5ldyBEYXRlKDApLCBuZXcgRGF0ZSg4NjQwMDAwMDAwMDAwMDAwKSk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBoaXN0b3J5IG9yZGVycyBieSB0aWNrZXQgaWRcbiAgICogQHBhcmFtIHtzdHJpbmd9IGlkIHRpY2tldCBpZFxuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlck9yZGVyPn0gaGlzdG9yeSBvcmRlcnMgZm91bmRcbiAgICovXG4gIGdldEhpc3RvcnlPcmRlcnNCeVRpY2tldChpZCkge1xuICAgIGxldCBoaXN0b3J5T3JkZXJzID0gT2JqZWN0LnZhbHVlcyh0aGlzLl9oaXN0b3J5T3JkZXJzQnlUaWNrZXRbaWRdIHx8IHt9KTtcbiAgICBoaXN0b3J5T3JkZXJzLnNvcnQodGhpcy5faGlzdG9yeU9yZGVyc0NvbXBhcmF0b3IpO1xuICAgIHJldHVybiBoaXN0b3J5T3JkZXJzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgaGlzdG9yeSBvcmRlcnMgYnkgcG9zaXRpb24gaWRcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBvc2l0aW9uSWQgcG9zaXRpb24gaWRcbiAgICogQHJldHVybnMge0FycmF5PE1ldGF0cmFkZXJPcmRlcj59IGhpc3Rvcnkgb3JkZXJzIGZvdW5kXG4gICAqL1xuICBnZXRIaXN0b3J5T3JkZXJzQnlQb3NpdGlvbihwb3NpdGlvbklkKSB7XG4gICAgbGV0IGhpc3RvcnlPcmRlcnMgPSBPYmplY3QudmFsdWVzKHRoaXMuX2hpc3RvcnlPcmRlcnNCeVBvc2l0aW9uW3Bvc2l0aW9uSWRdIHx8IHt9KTtcbiAgICBoaXN0b3J5T3JkZXJzLnNvcnQodGhpcy5faGlzdG9yeU9yZGVyc0NvbXBhcmF0b3IpO1xuICAgIHJldHVybiBoaXN0b3J5T3JkZXJzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgaGlzdG9yeSBvcmRlcnMgYnkgdGltZSByYW5nZVxuICAgKiBAcGFyYW0gc3RhcnRUaW1lIHN0YXJ0IHRpbWUsIGluY2x1c2l2ZVxuICAgKiBAcGFyYW0gZW5kVGltZSBlbmQgdGltZSwgaW5jbHVzaXZlXG4gICAqIEByZXR1cm5zIHtBcnJheTxNZXRhdHJhZGVyT3JkZXI+fSBoaXNvdHJ5IG9yZGVycyBmb3VuZFxuICAgKi9cbiAgZ2V0SGlzdG9yeU9yZGVyc0J5VGltZVJhbmdlKHN0YXJ0VGltZSwgZW5kVGltZSkge1xuICAgIGxldCBoaXN0b3J5T3JkZXJzID0gdGhpcy5faGlzdG9yeU9yZGVyc0J5VGltZS5iZXR3ZWVuQm91bmRzKHtcbiAgICAgICRndGU6IHtkb25lVGltZTogc3RhcnRUaW1lLCBpZDogMCwgdHlwZTogJycsIHN0YXRlOiAnJ30sXG4gICAgICAkbHRlOiB7ZG9uZVRpbWU6IGVuZFRpbWUsIGlkOiBOdW1iZXIuTUFYX1ZBTFVFLCB0eXBlOiAnJywgc3RhdGU6ICcnfVxuICAgIH0pO1xuICAgIHJldHVybiBoaXN0b3J5T3JkZXJzO1xuICB9XG5cbiAgLyoqXG4gICAqIEludm9rZWQgd2hlbiBhIHN5bmNocm9uaXphdGlvbiBvZiBoaXN0b3J5IGRlYWxzIG9uIGEgTWV0YVRyYWRlciBhY2NvdW50IGhhdmUgZmluaXNoZWQgdG8gaW5kaWNhdGUgcHJvZ3Jlc3Mgb2YgYW5cbiAgICogaW5pdGlhbCB0ZXJtaW5hbCBzdGF0ZSBzeW5jaHJvbml6YXRpb25cbiAgICogQHBhcmFtIHtTdHJpbmd9IGluc3RhbmNlSW5kZXggaW5kZXggb2YgYW4gYWNjb3VudCBpbnN0YW5jZSBjb25uZWN0ZWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IHN5bmNocm9uaXphdGlvbklkIHN5bmNocm9uaXphdGlvbiByZXF1ZXN0IGlkXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2Ugd2hpY2ggcmVzb2x2ZXMgd2hlbiB0aGUgYXN5bmNocm9ub3VzIGV2ZW50IGlzIHByb2Nlc3NlZFxuICAgKi9cbiAgYXN5bmMgb25EZWFsc1N5bmNocm9uaXplZChpbnN0YW5jZUluZGV4LCBzeW5jaHJvbml6YXRpb25JZCkge1xuICAgIGF3YWl0IHRoaXMuX2ZsdXNoRGF0YWJhc2UoKTtcbiAgICBhd2FpdCBzdXBlci5vbkRlYWxzU3luY2hyb25pemVkKGluc3RhbmNlSW5kZXgsIHN5bmNocm9uaXphdGlvbklkKTtcbiAgfVxuXG4gIF9yZXNldCgpIHtcbiAgICB0aGlzLl9vcmRlclN5bmNocm9uaXphdGlvbkZpbmlzaGVkID0ge307XG4gICAgdGhpcy5fZGVhbFN5bmNocm9uaXphdGlvbkZpbmlzaGVkID0ge307XG4gICAgdGhpcy5fZGVhbHNCeVRpY2tldCA9IHt9O1xuICAgIHRoaXMuX2RlYWxzQnlQb3NpdGlvbiA9IHt9O1xuICAgIHRoaXMuX2hpc3RvcnlPcmRlcnNCeVRpY2tldCA9IHt9O1xuICAgIHRoaXMuX2hpc3RvcnlPcmRlcnNCeVBvc2l0aW9uID0ge307XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNvbXBsZXhpdHlcbiAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQ29tcGFyYXRvciA9IChvMSwgbzIpID0+IHtcbiAgICAgIGxldCB0aW1lRGlmZiA9IChvMS5kb25lVGltZSB8fCBuZXcgRGF0ZSgwKSkuZ2V0VGltZSgpIC0gKG8yLmRvbmVUaW1lIHx8IG5ldyBEYXRlKDApKS5nZXRUaW1lKCk7XG4gICAgICBpZiAodGltZURpZmYgPT09IDApIHtcbiAgICAgICAgbGV0IGlkRGlmZiA9IG8xLmlkIC0gbzIuaWQ7XG4gICAgICAgIGlmIChpZERpZmYgPT09IDApIHtcbiAgICAgICAgICBpZiAobzEudHlwZSA+IG8yLnR5cGUpIHtcbiAgICAgICAgICAgIHJldHVybiAxO1xuICAgICAgICAgIH0gZWxzZSBpZiAobzEudHlwZSA8IG8yLnR5cGUpIHtcbiAgICAgICAgICAgIHJldHVybiAtMTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKG8xLnN0YXRlID4gbzIuc3RhdGUpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIDE7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKG8xLnN0YXRlIDwgbzIuc3RhdGUpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIC0xO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgcmV0dXJuIDA7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiBpZERpZmY7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiB0aW1lRGlmZjtcbiAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMuX2hpc3RvcnlPcmRlcnNCeVRpbWUgPSBuZXcgQVZMVHJlZSh7Y29tcGFyZUtleXM6IHRoaXMuX2hpc3RvcnlPcmRlcnNDb21wYXJhdG9yfSk7XG4gICAgdGhpcy5fZGVhbHNDb21wYXJhdG9yID0gKGQxLCBkMikgPT4ge1xuICAgICAgbGV0IHRpbWVEaWZmID0gKGQxLnRpbWUgfHwgbmV3IERhdGUoMCkpLmdldFRpbWUoKSAtIChkMi50aW1lIHx8IG5ldyBEYXRlKDApKS5nZXRUaW1lKCk7XG4gICAgICBpZiAodGltZURpZmYgPT09IDApIHtcbiAgICAgICAgbGV0IGlkRGlmZiA9IGQxLmlkIC0gZDIuaWQ7XG4gICAgICAgIGlmIChpZERpZmYgPT09IDApIHtcbiAgICAgICAgICBpZiAoZDEuZW50cnlUeXBlID4gZDIuZW50cnlUeXBlKSB7XG4gICAgICAgICAgICByZXR1cm4gMTtcbiAgICAgICAgICB9IGVsc2UgaWYgKGQxLmVudHJ5VHlwZSA8IGQyLmVudHJ5VHlwZSkge1xuICAgICAgICAgICAgcmV0dXJuIC0xO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gMDtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIGlkRGlmZjtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIHRpbWVEaWZmO1xuICAgICAgfVxuICAgIH07XG4gICAgdGhpcy5fZGVhbHNCeVRpbWUgPSBuZXcgQVZMVHJlZSh7Y29tcGFyZUtleXM6IHRoaXMuX2RlYWxzQ29tcGFyYXRvcn0pO1xuICAgIHRoaXMuX21heEhpc3RvcnlPcmRlclRpbWUgPSBuZXcgRGF0ZSgwKTtcbiAgICB0aGlzLl9tYXhEZWFsVGltZSA9IG5ldyBEYXRlKDApO1xuICAgIHRoaXMuX25ld0hpc3RvcnlPcmRlcnMgPSBbXTtcbiAgICB0aGlzLl9uZXdEZWFscyA9IFtdO1xuICAgIGNsZWFyVGltZW91dCh0aGlzLl9mbHVzaFRpbWVvdXQpO1xuICAgIGRlbGV0ZSB0aGlzLl9mbHVzaFRpbWVvdXQ7XG4gIH1cblxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eVxuICBhc3luYyBfYWRkRGVhbChkZWFsLCBleGlzdGluZykge1xuICAgIGxldCBrZXkgPSB0aGlzLl9nZXREZWFsS2V5KGRlYWwpO1xuICAgIHRoaXMuX2RlYWxzQnlUaWNrZXRbZGVhbC5pZF0gPSB0aGlzLl9kZWFsc0J5VGlja2V0W2RlYWwuaWRdIHx8IHt9O1xuICAgIGxldCBuZXdEZWFsID0gIWV4aXN0aW5nICYmICF0aGlzLl9kZWFsc0J5VGlja2V0W2RlYWwuaWRdW2tleV07XG4gICAgdGhpcy5fZGVhbHNCeVRpY2tldFtkZWFsLmlkXVtrZXldID0gZGVhbDtcbiAgICBpZiAoZGVhbC5wb3NpdGlvbklkKSB7XG4gICAgICB0aGlzLl9kZWFsc0J5UG9zaXRpb25bZGVhbC5wb3NpdGlvbklkXSA9IHRoaXMuX2RlYWxzQnlQb3NpdGlvbltkZWFsLnBvc2l0aW9uSWRdIHx8IHt9O1xuICAgICAgdGhpcy5fZGVhbHNCeVBvc2l0aW9uW2RlYWwucG9zaXRpb25JZF1ba2V5XSA9IGRlYWw7XG4gICAgfVxuICAgIHRoaXMuX2RlYWxzQnlUaW1lLmRlbGV0ZShkZWFsKTtcbiAgICB0aGlzLl9kZWFsc0J5VGltZS5pbnNlcnQoZGVhbCwgZGVhbCk7XG4gICAgaWYgKGRlYWwudGltZSAmJiAoIXRoaXMuX21heERlYWxUaW1lIHx8IHRoaXMuX21heERlYWxUaW1lLmdldFRpbWUoKSA8IGRlYWwudGltZS5nZXRUaW1lKCkpKSB7XG4gICAgICB0aGlzLl9tYXhEZWFsVGltZSA9IGRlYWwudGltZTtcbiAgICB9XG4gICAgaWYgKG5ld0RlYWwpIHtcbiAgICAgIHRoaXMuX25ld0RlYWxzLnB1c2goZGVhbCk7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5fZmx1c2hUaW1lb3V0KTtcbiAgICAgIHRoaXMuX2ZsdXNoVGltZW91dCA9IHNldFRpbWVvdXQodGhpcy5fZmx1c2hEYXRhYmFzZS5iaW5kKHRoaXMpLCA1MDAwKTtcbiAgICB9XG4gIH1cblxuICBfZ2V0RGVhbEtleShkZWFsKSB7XG4gICAgcmV0dXJuIChkZWFsLnRpbWUgfHwgbmV3IERhdGUoMCkpLnRvSVNPU3RyaW5nKCkgKyAnOicgKyBkZWFsLmlkICsgJzonICsgZGVhbC5lbnRyeVR5cGU7XG4gIH1cblxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eVxuICBhc3luYyBfYWRkSGlzdG9yeU9yZGVyKGhpc3RvcnlPcmRlciwgZXhpc3RpbmcpIHtcbiAgICBsZXQga2V5ID0gdGhpcy5fZ2V0SGlzdG9yeU9yZGVyS2V5KGhpc3RvcnlPcmRlcik7XG4gICAgdGhpcy5faGlzdG9yeU9yZGVyc0J5VGlja2V0W2hpc3RvcnlPcmRlci5pZF0gPSB0aGlzLl9oaXN0b3J5T3JkZXJzQnlUaWNrZXRbaGlzdG9yeU9yZGVyLmlkXSB8fCB7fTtcbiAgICBsZXQgbmV3SGlzdG9yeU9yZGVyID0gIWV4aXN0aW5nICYmICF0aGlzLl9oaXN0b3J5T3JkZXJzQnlUaWNrZXRbaGlzdG9yeU9yZGVyLmlkXVtrZXldO1xuICAgIHRoaXMuX2hpc3RvcnlPcmRlcnNCeVRpY2tldFtoaXN0b3J5T3JkZXIuaWRdW2tleV0gPSBoaXN0b3J5T3JkZXI7XG4gICAgaWYgKGhpc3RvcnlPcmRlci5wb3NpdGlvbklkKSB7XG4gICAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQnlQb3NpdGlvbltoaXN0b3J5T3JkZXIucG9zaXRpb25JZF0gPSB0aGlzLl9oaXN0b3J5T3JkZXJzQnlQb3NpdGlvbltoaXN0b3J5T3JkZXIucG9zaXRpb25JZF0gfHxcbiAgICAgICAge307XG4gICAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQnlQb3NpdGlvbltoaXN0b3J5T3JkZXIucG9zaXRpb25JZF1ba2V5XSA9IGhpc3RvcnlPcmRlcjtcbiAgICB9XG4gICAgdGhpcy5faGlzdG9yeU9yZGVyc0J5VGltZS5kZWxldGUoaGlzdG9yeU9yZGVyKTtcbiAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQnlUaW1lLmluc2VydChoaXN0b3J5T3JkZXIsIGhpc3RvcnlPcmRlcik7XG4gICAgaWYgKGhpc3RvcnlPcmRlci5kb25lVGltZSAmJiAoIXRoaXMuX21heEhpc3RvcnlPcmRlclRpbWUgfHxcbiAgICAgICAgdGhpcy5fbWF4SGlzdG9yeU9yZGVyVGltZS5nZXRUaW1lKCkgPCBoaXN0b3J5T3JkZXIuZG9uZVRpbWUuZ2V0VGltZSgpKSkge1xuICAgICAgdGhpcy5fbWF4SGlzdG9yeU9yZGVyVGltZSA9IGhpc3RvcnlPcmRlci5kb25lVGltZTtcbiAgICB9XG4gICAgaWYgKG5ld0hpc3RvcnlPcmRlcikge1xuICAgICAgdGhpcy5fbmV3SGlzdG9yeU9yZGVycy5wdXNoKGhpc3RvcnlPcmRlcik7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5fZmx1c2hUaW1lb3V0KTtcbiAgICAgIHRoaXMuX2ZsdXNoVGltZW91dCA9IHNldFRpbWVvdXQodGhpcy5fZmx1c2hEYXRhYmFzZS5iaW5kKHRoaXMpLCA1MDAwKTtcbiAgICB9XG4gIH1cblxuICBfZ2V0SGlzdG9yeU9yZGVyS2V5KGhpc3RvcnlPcmRlcikge1xuICAgIHJldHVybiAoaGlzdG9yeU9yZGVyLmRvbmVUaW1lIHx8IG5ldyBEYXRlKDApKS50b0lTT1N0cmluZygpICsgJzonICsgaGlzdG9yeU9yZGVyLmlkICsgJzonICtcbiAgICAgIGhpc3RvcnlPcmRlci50eXBlICsgJzonICsgaGlzdG9yeU9yZGVyLnN0YXRlO1xuICB9XG5cbiAgYXN5bmMgX2ZsdXNoRGF0YWJhc2UoKSB7XG4gICAgaWYgKHRoaXMuX2ZsdXNoUHJvbWlzZSkge1xuICAgICAgYXdhaXQgdGhpcy5fZmx1c2hQcm9taXNlO1xuICAgIH1cbiAgICBpZiAodGhpcy5fZmx1c2hSdW5uaW5nKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuX2ZsdXNoUnVubmluZyA9IHRydWU7XG4gICAgbGV0IHJlc29sdmU7XG4gICAgdGhpcy5fZmx1c2hQcm9taXNlID0gbmV3IFByb21pc2UocmVzID0+IHJlc29sdmUgPSByZXMpO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLl9oaXN0b3J5RGF0YWJhc2UuZmx1c2godGhpcy5fYWNjb3VudElkLCB0aGlzLl9hcHBsaWNhdGlvbiwgdGhpcy5fbmV3SGlzdG9yeU9yZGVycywgdGhpcy5fbmV3RGVhbHMpO1xuICAgICAgdGhpcy5fbmV3SGlzdG9yeU9yZGVycyA9IFtdO1xuICAgICAgdGhpcy5fbmV3RGVhbHMgPSBbXTtcbiAgICAgIHRoaXMuX2xvZ2dlci5kZWJ1ZyhgJHt0aGlzLl9hY2NvdW50SWR9OiBmbHVzaGVkIGhpc3RvcnkgZGJgKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHRoaXMuX2xvZ2dlci53YXJuKGAke3RoaXMuX2FjY291bnRJZH06IGVycm9yIGZsdXNoaW5nIGhpc3RvcnkgZGJgLCBlcnIpO1xuICAgICAgdGhpcy5fZmx1c2hUaW1lb3V0ID0gc2V0VGltZW91dCh0aGlzLl9mbHVzaERhdGFiYXNlLmJpbmQodGhpcyksIDE1MDAwKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgcmVzb2x2ZSgpO1xuICAgICAgdGhpcy5fZmx1c2hSdW5uaW5nID0gZmFsc2U7XG4gICAgfVxuICB9XG5cbn1cbiJdLCJuYW1lcyI6WyJNZW1vcnlIaXN0b3J5U3RvcmFnZSIsIkhpc3RvcnlTdG9yYWdlIiwiaW5pdGlhbGl6ZSIsImFjY291bnRJZCIsImFwcGxpY2F0aW9uIiwiZGVhbHMiLCJoaXN0b3J5T3JkZXJzIiwiX2hpc3RvcnlEYXRhYmFzZSIsImxvYWRIaXN0b3J5IiwiZGVhbCIsIl9hZGREZWFsIiwiaGlzdG9yeU9yZGVyIiwiX2FkZEhpc3RvcnlPcmRlciIsImNsZWFyIiwiX3Jlc2V0IiwiX2FjY291bnRJZCIsIl9hcHBsaWNhdGlvbiIsImxhc3RIaXN0b3J5T3JkZXJUaW1lIiwiaW5zdGFuY2VOdW1iZXIiLCJfbWF4SGlzdG9yeU9yZGVyVGltZSIsImxhc3REZWFsVGltZSIsIl9tYXhEZWFsVGltZSIsIm9uSGlzdG9yeU9yZGVyQWRkZWQiLCJpbnN0YW5jZUluZGV4Iiwib25EZWFsQWRkZWQiLCJnZXREZWFsc0J5VGltZVJhbmdlIiwiRGF0ZSIsImdldERlYWxzQnlUaWNrZXQiLCJpZCIsIk9iamVjdCIsInZhbHVlcyIsIl9kZWFsc0J5VGlja2V0Iiwic29ydCIsIl9kZWFsc0NvbXBhcmF0b3IiLCJnZXREZWFsc0J5UG9zaXRpb24iLCJwb3NpdGlvbklkIiwiX2RlYWxzQnlQb3NpdGlvbiIsInN0YXJ0VGltZSIsImVuZFRpbWUiLCJfZGVhbHNCeVRpbWUiLCJiZXR3ZWVuQm91bmRzIiwiJGd0ZSIsInRpbWUiLCJlbnRyeVR5cGUiLCIkbHRlIiwiTnVtYmVyIiwiTUFYX1ZBTFVFIiwiZ2V0SGlzdG9yeU9yZGVyc0J5VGltZVJhbmdlIiwiZ2V0SGlzdG9yeU9yZGVyc0J5VGlja2V0IiwiX2hpc3RvcnlPcmRlcnNCeVRpY2tldCIsIl9oaXN0b3J5T3JkZXJzQ29tcGFyYXRvciIsImdldEhpc3RvcnlPcmRlcnNCeVBvc2l0aW9uIiwiX2hpc3RvcnlPcmRlcnNCeVBvc2l0aW9uIiwiX2hpc3RvcnlPcmRlcnNCeVRpbWUiLCJkb25lVGltZSIsInR5cGUiLCJzdGF0ZSIsIm9uRGVhbHNTeW5jaHJvbml6ZWQiLCJzeW5jaHJvbml6YXRpb25JZCIsIl9mbHVzaERhdGFiYXNlIiwiX29yZGVyU3luY2hyb25pemF0aW9uRmluaXNoZWQiLCJfZGVhbFN5bmNocm9uaXphdGlvbkZpbmlzaGVkIiwibzEiLCJvMiIsInRpbWVEaWZmIiwiZ2V0VGltZSIsImlkRGlmZiIsIkFWTFRyZWUiLCJjb21wYXJlS2V5cyIsImQxIiwiZDIiLCJfbmV3SGlzdG9yeU9yZGVycyIsIl9uZXdEZWFscyIsImNsZWFyVGltZW91dCIsIl9mbHVzaFRpbWVvdXQiLCJleGlzdGluZyIsImtleSIsIl9nZXREZWFsS2V5IiwibmV3RGVhbCIsImRlbGV0ZSIsImluc2VydCIsInB1c2giLCJzZXRUaW1lb3V0IiwiYmluZCIsInRvSVNPU3RyaW5nIiwiX2dldEhpc3RvcnlPcmRlcktleSIsIm5ld0hpc3RvcnlPcmRlciIsIl9mbHVzaFByb21pc2UiLCJfZmx1c2hSdW5uaW5nIiwicmVzb2x2ZSIsIlByb21pc2UiLCJyZXMiLCJmbHVzaCIsIl9sb2dnZXIiLCJkZWJ1ZyIsImVyciIsIndhcm4iLCJjb25zdHJ1Y3RvciIsIkhpc3RvcnlEYXRhYmFzZSIsImdldEluc3RhbmNlIiwiTG9nZ2VyTWFuYWdlciIsImdldExvZ2dlciJdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7ZUFVcUJBOzs7dUVBUk07OERBQ0M7a0NBQ0o7K0RBQ0U7Ozs7OztBQUtYLElBQUEsQUFBTUEsdUJBQU4sTUFBTUEsNkJBQTZCQyx1QkFBYztJQVk5RDs7OztHQUlDLEdBQ0QsTUFBTUMsV0FBV0MsU0FBUyxFQUFFQyxjQUFjLFNBQVMsRUFBRTtRQUNuRCxNQUFNLEtBQUssQ0FBQ0YsV0FBV0MsV0FBV0M7UUFDbEMsSUFBSSxFQUFDQyxLQUFLLEVBQUVDLGFBQWEsRUFBQyxHQUFHLE1BQU0sSUFBSSxDQUFDQyxnQkFBZ0IsQ0FBQ0MsV0FBVyxDQUFDTCxXQUFXQztRQUNoRixLQUFLLElBQUlLLFFBQVFKLE1BQU87WUFDdEIsTUFBTSxJQUFJLENBQUNLLFFBQVEsQ0FBQ0QsTUFBTTtRQUM1QjtRQUNBLEtBQUssSUFBSUUsZ0JBQWdCTCxjQUFlO1lBQ3RDLE1BQU0sSUFBSSxDQUFDTSxnQkFBZ0IsQ0FBQ0QsY0FBYztRQUM1QztJQUNGO0lBRUE7OztHQUdDLEdBQ0QsTUFBTUUsUUFBUTtRQUNaLElBQUksQ0FBQ0MsTUFBTTtRQUNYLE1BQU0sSUFBSSxDQUFDUCxnQkFBZ0IsQ0FBQ00sS0FBSyxDQUFDLElBQUksQ0FBQ0UsVUFBVSxFQUFFLElBQUksQ0FBQ0MsWUFBWTtJQUN0RTtJQUVBOzs7O0dBSUMsR0FDREMscUJBQXFCQyxjQUFjLEVBQUU7UUFDbkMsT0FBTyxJQUFJLENBQUNDLG9CQUFvQjtJQUNsQztJQUVBOzs7O0dBSUMsR0FDREMsYUFBYUYsY0FBYyxFQUFFO1FBQzNCLE9BQU8sSUFBSSxDQUFDRyxZQUFZO0lBQzFCO0lBRUE7Ozs7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