From bb1ae30df92a201e31408120d4cd027aaa2a37b6 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Sat, 23 May 2015 08:59:40 +0200 Subject: [PATCH 01/69] add transact create --- eth/main.cpp | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/eth/main.cpp b/eth/main.cpp index 33d049843..88b9198a8 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -1032,6 +1032,67 @@ int main(int argc, char** argv) else cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA"; } + else if (c && cmd == "cretract") + { + auto const& bc =c->blockChain(); + auto h = bc.currentHash(); + auto blockData = bc.block(h); + BlockInfo info(blockData); + if (iss.peek() != -1) + { + u256 amount; + u256 gasPrice; + u256 gas; + string sechex; + string sdata; + + iss >> amount >> gasPrice >> gas >> sechex >> sdata; + + if (!gasPrice) + gasPrice = gasPricer->bid(priority); + + cnote << "Data:"; + cnote << sdata; + bytes data = dev::eth::parseData(sdata); + cnote << "Bytes:"; + string sbd = asString(data); + bytes bbd = asBytes(sbd); + stringstream ssbd; + ssbd << bbd; + cnote << ssbd.str(); + int ssize = sechex.length(); + u256 minGas = (u256)Transaction::gasRequired(data, 0); + if (gas < minGas) + cwarn << "Minimum gas amount is" << minGas; + else if (ssize < 40) + { + if (ssize > 0) + cwarn << "Invalid secret length:" << ssize; + } + else + { + try + { + Secret secret = h256(fromHex(sechex)); + c->submitTransaction(secret, amount, data, gas, gasPrice); + + //Address dest = h160(fromHex(hexAddr)); + //c->submitTransaction(secret, amount, dest, data, gas, gasPrice); + } + catch (BadHexCharacter& _e) + { + cwarn << "invalid hex character, transaction rejected"; + cwarn << boost::diagnostic_information(_e); + } + catch (...) + { + cwarn << "transaction rejected"; + } + } + } + else + cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA"; + } #if ETH_FATDB else if (c && cmd == "listcontracts") { From 777b298613c3c28cdd656db3f81a86657a70144c Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Fri, 5 Jun 2015 11:28:19 +0200 Subject: [PATCH 02/69] warnings fixed --- test/libp2p/peer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/libp2p/peer.cpp b/test/libp2p/peer.cpp index d46964d21..bcb6d4085 100644 --- a/test/libp2p/peer.cpp +++ b/test/libp2p/peer.cpp @@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(saveNodes) unsigned const c_nodes = 6; unsigned const c_peers = c_nodes - 1; - for (int i = 0; i < c_nodes; ++i) + for (unsigned i = 0; i < c_nodes; ++i) { Host* h = new Host("Test", NetworkPreferences("127.0.0.1", 30300 + i, false)); h->setIdealPeerCount(10); @@ -95,14 +95,14 @@ BOOST_AUTO_TEST_CASE(saveNodes) for (auto const& h: hosts) host.addNode(h->id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), h->listenPort(), h->listenPort())); - for (int i = 0; i < c_peers * 1000 && host.peerCount() < c_peers; i += c_step) + for (unsigned i = 0; i < c_peers * 1000 && host.peerCount() < c_peers; i += c_step) this_thread::sleep_for(chrono::milliseconds(c_step)); Host& host2 = *hosts.back(); for (auto const& h: hosts) host2.addNode(h->id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), h->listenPort(), h->listenPort())); - for (int i = 0; i < c_peers * 1000 && host2.peerCount() < c_peers; i += c_step) + for (unsigned i = 0; i < c_peers * 1000 && host2.peerCount() < c_peers; i += c_step) this_thread::sleep_for(chrono::milliseconds(c_step)); BOOST_CHECK_EQUAL(host.peerCount(), c_peers); From a7f861a46be770d37a2048900448e7d41cadd2ab Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Fri, 5 Jun 2015 11:31:38 +0200 Subject: [PATCH 03/69] comment updated --- libdevcore/Log.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libdevcore/Log.h b/libdevcore/Log.h index f104caccd..a4011fc0e 100644 --- a/libdevcore/Log.h +++ b/libdevcore/Log.h @@ -60,6 +60,7 @@ extern std::function g_logPost; extern std::map g_logOverride; /// Temporary changes system's verbosity for specific function. Restores the old verbosity when function returns. +/// Not thread-safe, use with caution! struct VerbosityHolder { VerbosityHolder(int _temporaryValue) : oldLogVerbosity(g_logVerbosity) { g_logVerbosity = _temporaryValue; } From 8404bceb0c9efd5d69de1918b3530918940a52bf Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 5 Jun 2015 14:43:38 +0200 Subject: [PATCH 04/69] Add ScenarioExecution + ScenarioLoader --- mix/ClientModel.cpp | 231 ++++++++++++++---------- mix/ClientModel.h | 34 +++- mix/ContractCallDataEncoder.cpp | 15 +- mix/ContractCallDataEncoder.h | 4 +- mix/MachineStates.h | 1 + mix/MixClient.cpp | 17 +- mix/MixClient.h | 5 +- mix/main.cpp | 4 +- mix/qml.qrc | 4 + mix/qml/Application.qml | 21 +-- mix/qml/Block.qml | 145 +++++++++++++++ mix/qml/BlockChain.qml | 306 ++++++++++++++++++++++++++++++++ mix/qml/Debugger.qml | 84 ++------- mix/qml/MainContent.qml | 72 +++++--- mix/qml/QAddressView.qml | 32 ++-- mix/qml/ScenarioExecution.qml | 57 ++++++ mix/qml/ScenarioLoader.qml | 77 ++++++++ mix/qml/StateListModel.qml | 129 +++++++++++--- mix/qml/StructView.qml | 1 + mix/qml/TransactionDialog.qml | 14 +- mix/qml/js/ProjectModel.js | 2 + mix/qml/js/TransactionHelper.js | 3 +- 22 files changed, 996 insertions(+), 262 deletions(-) create mode 100644 mix/qml/Block.qml create mode 100644 mix/qml/BlockChain.qml create mode 100644 mix/qml/ScenarioExecution.qml create mode 100644 mix/qml/ScenarioLoader.qml diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index cad210f1b..3cd401e23 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -82,12 +82,14 @@ ClientModel::ClientModel(): qRegisterMetaType("QCallData"); qRegisterMetaType("RecordLogEntry*"); - connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection); + //connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection); m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString())); m_ethAccounts = make_shared([=](){return m_client.get();}, std::vector()); m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), m_ethAccounts, std::vector(), m_client.get())); connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection); + + } ClientModel::~ClientModel() @@ -111,7 +113,7 @@ QString ClientModel::apiCall(QString const& _message) void ClientModel::mine() { - if (m_running || m_mining) + if (m_mining) BOOST_THROW_EXCEPTION(ExecutionStateException()); m_mining = true; emit miningStarted(); @@ -206,92 +208,131 @@ QVariantList ClientModel::gasCosts() const return res; } -void ClientModel::setupState(QVariantMap _state) +void ClientModel::setupScenario(QVariantMap _scenario) { - QVariantList stateAccounts = _state.value("accounts").toList(); - QVariantList stateContracts = _state.value("contracts").toList(); - QVariantList transactions = _state.value("transactions").toList(); + m_queueTransactions.clear(); + m_running = true; + + m_currentScenario = _scenario; + QVariantList blocks = _scenario.value("blocks").toList(); + QVariantList stateAccounts = _scenario.value("accounts").toList(); + + m_accounts.clear(); + std::vector userAccounts; + + for (auto const& b: stateAccounts) + { + QVariantMap account = b.toMap(); + Address address = {}; + if (account.contains("secret")) + { + KeyPair key(Secret(account.value("secret").toString().toStdString())); + userAccounts.push_back(key); + address = key.address(); + } + else if (account.contains("address")) + address = Address(fromHex(account.value("address").toString().toStdString())); + if (!address) + continue; + + m_accounts[address] = Account(qvariant_cast(account.value("balance"))->toU256Wei(), Account::NormalCreation); + } + m_ethAccounts->setAccounts(userAccounts); + for (auto const& b: blocks) + { + QVariantList transactions = b.toMap().value("transactions").toList(); + m_queueTransactions.push_back(transactions); + } + + m_client->resetState(m_accounts, Secret(m_currentScenario.value("miner").toMap().value("secret").toString().toStdString())); + + connect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock, Qt::QueuedConnection); + connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection); + connect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock, Qt::QueuedConnection); + processNextBlock(); +} - unordered_map accounts; - std::vector userAccounts; +void ClientModel::stopExecution() +{ + disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock); + disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock); + disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution); + m_running = false; +} - for (auto const& b: stateAccounts) - { - QVariantMap account = b.toMap(); - Address address = {}; - if (account.contains("secret")) - { - KeyPair key(Secret(account.value("secret").toString().toStdString())); - userAccounts.push_back(key); - address = key.address(); - } - else if (account.contains("address")) - address = Address(fromHex(account.value("address").toString().toStdString())); - if (!address) - continue; +void ClientModel::finalizeBlock() +{ + if (m_queueTransactions.size() > 0) + mine(); + else + { + disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock); + disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock); + disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution); + m_running = false; + emit runComplete(); + } +} - accounts[address] = Account(qvariant_cast(account.value("balance"))->toU256Wei(), Account::NormalCreation); - } - for (auto const& c: stateContracts) - { - QVariantMap contract = c.toMap(); - Address address = Address(fromHex(contract.value("address").toString().toStdString())); - Account account(qvariant_cast(contract.value("balance"))->toU256Wei(), Account::ContractConception); - bytes code = fromHex(contract.value("code").toString().toStdString()); - account.setCode(code); - QVariantMap storageMap = contract.value("storage").toMap(); - for(auto s = storageMap.cbegin(); s != storageMap.cend(); ++s) - account.setStorage(fromBigEndian(fromHex(s.key().toStdString())), fromBigEndian(fromHex(s.value().toString().toStdString()))); - accounts[address] = account; - } - vector transactionSequence; - for (auto const& t: transactions) - { - QVariantMap transaction = t.toMap(); - QString contractId = transaction.value("contractId").toString(); - QString functionId = transaction.value("functionId").toString(); - u256 gas = boost::get(qvariant_cast(transaction.value("gas"))->internalValue()); - bool gasAuto = transaction.value("gasAuto").toBool(); - u256 value = (qvariant_cast(transaction.value("value")))->toU256Wei(); - u256 gasPrice = (qvariant_cast(transaction.value("gasPrice")))->toU256Wei(); - QString sender = transaction.value("sender").toString(); - bool isContractCreation = transaction.value("isContractCreation").toBool(); - bool isFunctionCall = transaction.value("isFunctionCall").toBool(); - if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later - contractId = m_codeModel->contracts().keys()[0]; - TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()), isContractCreation, isFunctionCall); - transactionSettings.parameterValues = transaction.value("parameters").toMap(); - - if (contractId == functionId || functionId == "Constructor") - transactionSettings.functionId.clear(); - - transactionSequence.push_back(transactionSettings); - } - m_ethAccounts->setAccounts(userAccounts); - executeSequence(transactionSequence, accounts, Secret(_state.value("miner").toMap().value("secret").toString().toStdString())); +void ClientModel::processNextBlock() +{ + processNextTransactions(); } -void ClientModel::executeSequence(vector const& _sequence, std::unordered_map const& _accounts, Secret const& _miner) +void ClientModel::processNextTransactions() +{ + vector transactionSequence; + for (auto const& t: m_queueTransactions.front()) + { + QVariantMap transaction = t.toMap(); + QString contractId = transaction.value("contractId").toString(); + QString functionId = transaction.value("functionId").toString(); + bool gasAuto = transaction.value("gasAuto").toBool(); + u256 gas = 0; + if (transaction.value("gas").data()) + gas = boost::get(qvariant_cast(transaction.value("gas"))->internalValue()); + else + gasAuto = true; + + u256 value = (qvariant_cast(transaction.value("value")))->toU256Wei(); + u256 gasPrice = (qvariant_cast(transaction.value("gasPrice")))->toU256Wei(); + QString sender = transaction.value("sender").toString(); + bool isContractCreation = transaction.value("isContractCreation").toBool(); + bool isFunctionCall = transaction.value("isFunctionCall").toBool(); + if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later + contractId = m_codeModel->contracts().keys()[0]; + TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()), isContractCreation, isFunctionCall); + transactionSettings.parameterValues = transaction.value("parameters").toMap(); + + if (contractId == functionId || functionId == "Constructor") + transactionSettings.functionId.clear(); + + transactionSequence.push_back(transactionSettings); + } + m_queueTransactions.pop_front(); + executeSequence(transactionSequence); +} + +void ClientModel::executeSequence(vector const& _sequence) { if (m_running) { qWarning() << "Waiting for current execution to complete"; m_runFuture.waitForFinished(); } - m_running = true; emit runStarted(); - emit runStateChanged(); + //emit runStateChanged(); + - m_client->resetState(_accounts, _miner); //run sequence m_runFuture = QtConcurrent::run([=]() { try { vector
deployedContracts; - onStateReset(); + //onStateReset(); m_gasCosts.clear(); for (TransactionSettings const& transaction: _sequence) { @@ -319,12 +360,7 @@ void ClientModel::executeSequence(vector const& _sequence, break; } if (!f) - { - emit runFailed("Function '" + transaction.functionId + tr("' not found. Please check transactions or the contract code.")); - m_running = false; - emit runStateChanged(); - return; - } + emit runFailed("Function '" + transaction.functionId + tr("' not found. Please check transactions or the contract code.")); if (!transaction.functionId.isEmpty()) encoder.encode(f); for (QVariableDeclaration const* p: f->parametersList()) @@ -355,33 +391,34 @@ void ClientModel::executeSequence(vector const& _sequence, { auto contractAddressIter = m_contractAddresses.find(ctrInstance); if (contractAddressIter == m_contractAddresses.end()) - { - emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId); - m_running = false; - emit runStateChanged(); - return; - } - callAddress(contractAddressIter->second, encoder.encodedData(), transaction); + emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId); + callAddress(contractAddressIter->second, encoder.encodedData(), transaction); } m_gasCosts.append(m_client->lastExecution().gasUsed); onNewTransaction(); - } - m_running = false; - emit runComplete(); + emit runComplete(); + } } catch(boost::exception const&) { cerr << boost::current_exception_diagnostic_information(); emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); - } + } catch(exception const& e) { cerr << boost::current_exception_diagnostic_information(); emit runFailed(e.what()); - } - m_running = false; + } emit runStateChanged(); - }); + }); +} + +void ClientModel::executeTr(QVariantMap _tr) +{ + QVariantList trs; + trs.push_back(_tr); + m_queueTransactions.push_back(trs); + processNextTransactions(); } @@ -399,7 +436,7 @@ std::pair ClientModel::resolvePair(QString const& _contractId) QString ClientModel::resolveToken(std::pair const& _value, vector
const& _contracts) { if (_contracts.size() > 0) - return QString::fromStdString("0x" + dev::toHex(_contracts.at(_value.second).ref())); + return QString::fromStdString("0x" + dev::toHex(m_contractAddresses[_value].ref())); //dev::toHex(_contracts.at(_value.second).ref())); else return _value.first; } @@ -610,7 +647,7 @@ void ClientModel::emptyRecord() void ClientModel::debugRecord(unsigned _index) { ExecutionResult e = m_client->execution(_index); - showDebuggerForTransaction(e); + showDebuggerForTransaction(e); } Address ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction) @@ -631,7 +668,7 @@ RecordLogEntry* ClientModel::lastBlock() const strGas << blockInfo.gasUsed; stringstream strNumber; strNumber << blockInfo.number; - RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str())); + RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str()), QString(), tr("Block"), QVariantMap()); QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership); return record; } @@ -648,7 +685,7 @@ void ClientModel::onStateReset() void ClientModel::onNewTransaction() { ExecutionResult const& tr = m_client->lastExecution(); - unsigned block = m_client->number() + 1; + unsigned block = m_client->number() + 1; unsigned recordIndex = tr.executonIndex; QString transactionIndex = tr.isCall() ? QObject::tr("Call") : QString("%1:%2").arg(block).arg(tr.transactionIndex); QString address = QString::fromStdString(toJS(tr.address)); @@ -690,6 +727,7 @@ void ClientModel::onNewTransaction() Address contractAddress = (bool)tr.address ? tr.address : tr.contractAddress; auto contractAddressIter = m_contractNames.find(contractAddress); + QVariantMap inputParameters; if (contractAddressIter != m_contractNames.end()) { CompiledContract const& compilerRes = m_codeModel->contract(contractAddressIter->second); @@ -706,11 +744,20 @@ void ClientModel::onNewTransaction() returned += "("; returned += returnValues.join(", "); returned += ")"; - } + bytes data = tr.inputParameters; + data.erase(data.begin(), data.begin() + 4); + QStringList parameters = encoder.decode(funcDef->parametersList(), data); + for (int k = 0; k < parameters.length(); ++k) + inputParameters.insert(funcDef->parametersList().at(k)->name(), parameters.at(k)); + } } } - RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction, gasUsed); + LocalisedLogEntries logs = m_client->logs(); + QString sender = QString::fromStdString(dev::toHex(tr.sender.ref())); + QString label = contract + "." + function + "()"; + RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction, + gasUsed, sender, label, inputParameters); QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership); emit newRecord(log); } diff --git a/mix/ClientModel.h b/mix/ClientModel.h index e1648b78d..503b4189f 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -30,12 +30,13 @@ #include #include #include +#include #include "MachineStates.h" namespace dev { -namespace eth { class Account; class FixedAccountHolder; } +namespace eth { class FixedAccountHolder; } namespace mix { @@ -108,6 +109,12 @@ class RecordLogEntry: public QObject Q_PROPERTY(RecordType type MEMBER m_type CONSTANT) /// Gas used Q_PROPERTY(QString gasUsed MEMBER m_gasUsed CONSTANT) + /// Sender + Q_PROPERTY(QString sender MEMBER m_sender CONSTANT) + /// label + Q_PROPERTY(QString label MEMBER m_label CONSTANT) + /// input parameters + Q_PROPERTY(QVariantMap parameters MEMBER m_inputParameters CONSTANT) public: enum RecordType @@ -118,8 +125,10 @@ public: RecordLogEntry(): m_recordIndex(0), m_call(false), m_type(RecordType::Transaction) {} - RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed): - m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed) {} + RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed, + QString _sender, QString _label, QVariantMap _inputParameters): + m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed), + m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters) {} private: unsigned m_recordIndex; @@ -132,6 +141,9 @@ private: bool m_call; RecordType m_type; QString m_gasUsed; + QString m_sender; + QString m_label; + QVariantMap m_inputParameters; }; /** @@ -172,7 +184,12 @@ public: public slots: /// Setup state, run transaction sequence, show debugger for the last transaction /// @param _state JS object with state configuration - void setupState(QVariantMap _state); + //void setupState(QVariantMap _state); + /// Setup scenario, run transaction sequence, show debugger for the last transaction + /// @param _state JS object with state configuration + void setupScenario(QVariantMap _scenario); + /// Execute the given @param _tr on the curret state + void executeTr(QVariantMap _tr); /// Show the debugger for a specified record Q_INVOKABLE void debugRecord(unsigned _index); /// Show the debugger for an empty record @@ -224,7 +241,7 @@ private: RecordLogEntry* lastBlock() const; QVariantMap contractAddresses() const; QVariantList gasCosts() const; - void executeSequence(std::vector const& _sequence, std::unordered_map const& _accounts, Secret const& _miner); + void executeSequence(std::vector const& _sequence); dev::Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings()); void callAddress(Address const& _contract, bytes const& _data, TransactionSettings const& _tr); void onNewTransaction(); @@ -235,6 +252,10 @@ private: std::pair retrieveToken(QString const& _value, std::vector
const& _contracts); std::pair resolvePair(QString const& _contractId); QVariant formatStorageValue(SolidityType const& _type, std::unordered_map const& _storage, unsigned _offset, dev::u256 const& _slot); + void processNextTransactions(); + void processNextBlock(); + void finalizeBlock(); + void stopExecution(); std::atomic m_running; std::atomic m_mining; @@ -243,12 +264,15 @@ private: std::unique_ptr m_rpcConnector; std::unique_ptr m_web3Server; std::shared_ptr m_ethAccounts; + std::unordered_map m_accounts; QList m_gasCosts; std::map, Address> m_contractAddresses; std::map m_contractNames; std::map m_stdContractAddresses; std::map m_stdContractNames; CodeModel* m_codeModel = nullptr; + QList m_queueTransactions; + QVariantMap m_currentScenario; }; } diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 07ab8dd73..70c1e6068 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -29,6 +29,7 @@ #include "QVariableDefinition.h" #include "QFunctionDefinition.h" #include "ContractCallDataEncoder.h" +using namespace std; using namespace dev; using namespace dev::solidity; using namespace dev::mix; @@ -227,6 +228,18 @@ QVariant ContractCallDataEncoder::decode(SolidityType const& _type, bytes const& BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Parameter declaration not found")); } +QStringList ContractCallDataEncoder::decode(QList const& _returnParameters, vector _value) +{ + QStringList r; + for (int k = 0; k <_returnParameters.length(); k++) + { + QVariableDeclaration* dec = static_cast(_returnParameters.at(k)); + SolidityType const& type = dec->type()->type(); + r.append(decode(type, _value.at(k)).toString()); + } + return r; +} + QStringList ContractCallDataEncoder::decode(QList const& _returnParameters, bytes _value) { bytesConstRef value(&_value); @@ -238,7 +251,7 @@ QStringList ContractCallDataEncoder::decode(QList const& value.populate(&rawParam); value = value.cropped(32); QVariableDeclaration* dec = static_cast(_returnParameters.at(k)); - SolidityType const& type = dec->type()->type(); + SolidityType const& type = dec->type()->type(); r.append(decode(type, rawParam).toString()); } return r; diff --git a/mix/ContractCallDataEncoder.h b/mix/ContractCallDataEncoder.h index 2707845ae..57b364b49 100644 --- a/mix/ContractCallDataEncoder.h +++ b/mix/ContractCallDataEncoder.h @@ -48,7 +48,9 @@ public: void encode(QVariant const& _data, SolidityType const& _type); /// Decode variable in order to be sent to QML view. QStringList decode(QList const& _dec, bytes _value); - /// Decode single variable + /// Decode @param _parameters + QStringList decode(QList const& _parameters, std::vector _value); + /// Decode single variable QVariant decode(SolidityType const& _type, bytes const& _value); /// Get all encoded data encoded by encode function. bytes encodedData(); diff --git a/mix/MachineStates.h b/mix/MachineStates.h index 2a88d83bf..ae9804f7f 100644 --- a/mix/MachineStates.h +++ b/mix/MachineStates.h @@ -83,6 +83,7 @@ namespace mix dev::u256 gasUsed; unsigned transactionIndex; unsigned executonIndex = 0; + bytes inputParameters; bool isCall() const { return transactionIndex == std::numeric_limits::max(); } bool isConstructor() const { return !isCall() && !address; } diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index e3ed2eead..fcc72db9d 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -22,6 +22,7 @@ #include "MixClient.h" #include +#include #include #include #include @@ -69,19 +70,28 @@ bytes MixBlockChain::createGenesisBlock(h256 _stateRoot) MixClient::MixClient(std::string const& _dbPath): m_dbPath(_dbPath) { - resetState(std::unordered_map()); + resetState(std::unordered_map()); } MixClient::~MixClient() { } +LocalisedLogEntries MixClient::logs() +{ + return m_watches.at(0).changes; +} + void MixClient::resetState(std::unordered_map const& _accounts, Secret const& _miner) { + WriteGuard l(x_state); Guard fl(x_filtersWatches); m_filters.clear(); m_watches.clear(); + LogFilter filter; + m_filters.insert(std::make_pair(filter.sha3(), filter)); + m_watches.insert(std::make_pair(0, ClientWatch(filter.sha3(), Reaping::Automatic))); m_stateDB = OverlayDB(); SecureTrieDB accountState(&m_stateDB); @@ -122,7 +132,7 @@ Transaction MixClient::replaceGas(Transaction const& _t, u256 const& _gas, Secre void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call, bool _gasAuto, Secret const& _secret) { Transaction t = _gasAuto ? replaceGas(_t, m_state.gasLimitRemaining()) : _t; - // do debugging run first + // do debugging run first LastHashes lastHashes(256); lastHashes[0] = bc().numberHash(bc().number()); for (unsigned i = 1; i < 256; ++i) @@ -213,6 +223,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c }; ExecutionResult d; + d.inputParameters = t.data(); d.result = execution.executionResult(); d.machineStates = machineStates; d.executionCode = std::move(codes); @@ -227,7 +238,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c d.transactionIndex = m_state.pending().size(); d.executonIndex = m_executions.size(); - // execute on a state + // execute on a state if (!_call) { t = _gasAuto ? replaceGas(_t, d.gasUsed, _secret) : _t; diff --git a/mix/MixClient.h b/mix/MixClient.h index 4c5b51a09..852937634 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -25,6 +25,7 @@ #include #include +#include #include #include #include "MachineStates.h" @@ -76,6 +77,8 @@ public: using Interface::blockInfo; // to remove warning about hiding virtual function eth::BlockInfo blockInfo() const; + dev::eth::LocalisedLogEntries logs(); + protected: /// ClientBase methods using ClientBase::asOf; @@ -91,7 +94,7 @@ private: void noteChanged(h256Set const& _filters); dev::eth::Transaction replaceGas(dev::eth::Transaction const& _t, dev::u256 const& _gas, dev::Secret const& _secret = dev::Secret()); - eth::State m_state; + eth::State m_state; eth::State m_startState; OverlayDB m_stateDB; std::unique_ptr m_bc; diff --git a/mix/main.cpp b/mix/main.cpp index 78b5261ac..b4206ec51 100644 --- a/mix/main.cpp +++ b/mix/main.cpp @@ -32,9 +32,9 @@ int main(int _argc, char* _argv[]) { try { - MixApplication::initialize(); + MixApplication::initialize(); MixApplication app(_argc, _argv); - return app.exec(); + return app.exec(); } catch (boost::exception const& _e) { diff --git a/mix/qml.qrc b/mix/qml.qrc index 784404270..0227b4efd 100644 --- a/mix/qml.qrc +++ b/mix/qml.qrc @@ -64,5 +64,9 @@ qml/js/ansi2html.js qml/js/NetworkDeployment.js qml/js/InputValidator.js + qml/Block.qml + qml/BlockChain.qml + qml/ScenarioExecution.qml + qml/ScenarioLoader.qml diff --git a/mix/qml/Application.qml b/mix/qml/Application.qml index d041f421e..509e2e178 100644 --- a/mix/qml/Application.qml +++ b/mix/qml/Application.qml @@ -128,10 +128,8 @@ ApplicationWindow { MenuSeparator {} MenuItem { action: toggleProjectNavigatorAction } MenuItem { action: showHideRightPanelAction } - MenuItem { action: toggleTransactionLogAction } MenuItem { action: toggleWebPreviewAction } MenuItem { action: toggleWebPreviewOrientationAction } - //MenuItem { action: toggleCallsInLog } } } @@ -207,14 +205,14 @@ ApplicationWindow { enabled: codeModel.hasContract && !clientModel.running } - Action { + Action { id: toggleAssemblyDebuggingAction text: qsTr("Show VM Code") shortcut: "Ctrl+Alt+V" - onTriggered: mainContent.rightPane.assemblyMode = !mainContent.rightPane.assemblyMode; - checked: mainContent.rightPane.assemblyMode; + onTriggered: mainContent.debuggerPanel.assemblyMode = !mainContent.debuggerPanel.assemblyMode; + checked: mainContent.debuggerPanel.assemblyMode; enabled: true - } + } Action { id: toggleWebPreviewAction @@ -223,16 +221,7 @@ ApplicationWindow { checkable: true checked: mainContent.webViewVisible onTriggered: mainContent.toggleWebPreview(); - } - - Action { - id: toggleTransactionLogAction - text: qsTr("Show States and Transactions") - shortcut: "Alt+1" - checkable: true - checked: mainContent.rightPane.transactionLog.visible - onTriggered: mainContent.rightPane.transactionLog.visible = !mainContent.rightPane.transactionLog.visible - } + } Action { id: toggleProjectNavigatorAction diff --git a/mix/qml/Block.qml b/mix/qml/Block.qml new file mode 100644 index 000000000..e331dc6a6 --- /dev/null +++ b/mix/qml/Block.qml @@ -0,0 +1,145 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Layouts 1.1 +import Qt.labs.settings 1.0 +import "js/Debugger.js" as Debugger +import "js/ErrorLocationFormater.js" as ErrorLocationFormater +import "." + +ColumnLayout +{ + property variant transactions + property string status + property int number + Rectangle + { + width: parent.width + height: 50 + anchors.left: parent.left + anchors.leftMargin: statusWidth + Label { + text: + { + if (status === "mined") + return qsTr("BLOCK") + " " + number + else + return qsTr("BLOCK") + " pending" + } + + anchors.left: parent.left + } + } + + Repeater // List of transactions + { + id: transactionRepeater + model: transactions + Row + { + height: 50 + Rectangle + { + id: trSaveStatus + color: "transparent" + CheckBox + { + id: saveStatus + checked: { + if (index >= 0) + return transactions.get(index).saveStatus + else + return true + } + onCheckedChanged: + { + if (index >= 0) + transactions.get(index).saveStatus = checked + } + } + } + + Rectangle + { + width: parent.width + height: 50 + color: "#cccccc" + radius: 4 + Row + { + Label + { + id: status + width: statusWidth + } + Label + { + id: hash + width: fromWidth + text: { + if (index >= 0) + return transactions.get(index).sender + else + return "" + } + + clip: true + } + Label + { + id: func + text: { + if (index >= 0) + parent.userFrienldyToken(transactions.get(index).label) + else + return "" + } + + width: toWidth + clip: true + } + + function userFrienldyToken(value) + { + if (value && value.indexOf("<") === 0) + return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()"; + else + return value + } + + Label + { + id: returnValue + width: valueWidth + text: { + if (index >= 0 && transactions.get(index).returned) + return transactions.get(index).returned + else + return "" + } + clip: true + } + + Button + { + id: debug + width: logsWidth + text: "debug" + onClicked: + { + clientModel.debugRecord(transactions.get(index).recordIndex); + } + } + + Label + { + id: logs + width: logsWidth + } + } + } + } + } +} + diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml new file mode 100644 index 000000000..6e87ed52d --- /dev/null +++ b/mix/qml/BlockChain.qml @@ -0,0 +1,306 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Layouts 1.1 +import Qt.labs.settings 1.0 +import org.ethereum.qml.QEther 1.0 +import "js/Debugger.js" as Debugger +import "js/ErrorLocationFormater.js" as ErrorLocationFormater +import "js/TransactionHelper.js" as TransactionHelper +import "js/QEtherHelper.js" as QEtherHelper +import "." + +Column { + id: blockChainPanel + property variant model + spacing: 5 + + function load(scenario) + { + + if (!scenario) + return; + model = scenario + blockModel.clear() + for (var b in model.blocks) + blockModel.append(model.blocks[b]) + } + + property int statusWidth: 50 + property int fromWidth: 100 + property int toWidth: 250 + property int valueWidth: 100 + property int logsWidth: 50 + + Row + { + id: header + width: parent.width + Label + { + text: "Status" + width: statusWidth + } + Label + { + text: "From" + width: fromWidth + } + Label + { + text: "To" + width: toWidth + } + Label + { + text: "Value" + width: valueWidth + } + Label + { + text: "Logs" + width: logsWidth + } + } + + Rectangle + { + width: parent.width + height: 500 + border.color: "#cccccc" + border.width: 2 + color: "white" + ScrollView + { + width: parent.width + height: parent.height + ColumnLayout + { + Repeater // List of blocks + { + id: blockChainRepeater + width: parent.width + model: blockModel + Block + { + height: + { + if (index >= 0) + return 50 + 50 * blockModel.get(index).transactions.length + else + return 0 + } + + transactions: + { + if (index >= 0) + return blockModel.get(index).transactions + else + return [] + } + + status: + { + if (index >= 0) + return blockModel.get(index).status + else + return "" + } + + number: + { + if (index >= 0) + return blockModel.get(index).number + else + return 0 + } + } + } + } + } + } + + ListModel + { + id: blockModel + + function appendBlock(block) + { + blockModel.append(block); + } + + function appendTransaction(tr) + { + blockModel.get(blockModel.count - 1).transactions.append(tr) + } + + function removeTransaction(blockIndex, trIndex) + { + console.log(blockIndex) + console.log(trIndex) + blockModel.get(blockIndex).transactions.remove(trIndex) + } + + function removeLastBlock() + { + blockModel.remove(blockModel.count - 1) + } + + function removeBlock(index) + { + blockModel.remove(index) + } + + function getTransaction(block, tr) + { + return blockModel.get(block - 1).transactions.get(tr) + } + } + + RowLayout + { + width: parent.width + Button { + id: rebuild + text: qsTr("Rebuild") + onClicked: + { + for (var j = 0; j < model.blocks.length; j++) + { + for (var k = 0; k < model.blocks[j].transactions.length; k++) + { + if (!blockModel.get(j).transactions.get(k).saveStatus) + { + model.blocks[j].transactions.splice(k, 1) + blockModel.removeTransaction(j, k) + if (model.blocks[j].transactions.length === 0) + { + model.blocks[j].splice(j, 1); + blockModel.removeBlock(j); + } + } + } + } + clientModel.setupScenario(model); + } + } + + Button { + id: addTransaction + text: qsTr("Add Transaction") + onClicked: + { + var lastBlock = model.blocks[model.blocks.length - 1]; + if (lastBlock.status === "mined") + model.blocks.push(projectModel.stateListModel.createEmptyBlock()); + var item = TransactionHelper.defaultTransaction() + transactionDialog.stateAccounts = model.accounts + transactionDialog.open(model.blocks[model.blocks.length - 1].transactions.length, model.blocks.length - 1, item) + } + } + + Button { + id: addBlockBtn + text: qsTr("Add Block") + onClicked: + { + var lastBlock = model.blocks[model.blocks.length - 1] + if (lastBlock.status === "pending") + clientModel.mine() + else + addNewBlock() + } + + function addNewBlock() + { + var block = projectModel.stateListModel.createEmptyBlock() + model.blocks.push(block) + blockModel.appendBlock(block) + } + } + + Connections + { + target: clientModel + onNewBlock: + { + if (!clientModel.running) + { + var lastBlock = model.blocks[model.blocks.length - 1] + lastBlock.status = "mined" + lastBlock.number = model.blocks.length + var lastB = blockModel.get(model.blocks.length - 1) + lastB.status = "mined" + lastB.number = model.blocks.length + addBlockBtn.addNewBlock() + } + } + onStateCleared: + { + } + onNewRecord: + { + var blockIndex = _r.transactionIndex.split(":")[0] + var trIndex = _r.transactionIndex.split(":")[1] + if (parseInt(blockIndex) <= model.blocks.length) + { + var item = model.blocks[parseInt(blockIndex) - 1]; + if (parseInt(trIndex) <= item.transactions.length) + { + var tr = item.transactions[parseInt(trIndex)]; + tr.returned = _r.returned; + blockModel.getTransaction(blockIndex, trIndex).returned = _r.returned; + tr.recordIndex = _r.recordIndex; + blockModel.getTransaction(blockIndex, trIndex).recordIndex = _r.recordIndex; + return; + } + } + + // tr is not in the list. coming from JavaScript + var itemTr = TransactionHelper.defaultTransaction() + itemTr.functionId = _r.function + itemTr.contractId = _r.contract + itemTr.gasAuto = true + itemTr.parameters = _r.parameters + itemTr.isContractCreation = itemTr.functionId === itemTr.contractId + itemTr.label = _r.label + itemTr.isFunctionCall = itemTr.functionId !== "" + itemTr.returned = _r.returned + itemTr.value = QEtherHelper.createEther(_r.value, QEther.Wei) + itemTr.sender = _r.sender + itemTr.recordIndex = _r.recordIndex + + model.blocks[model.blocks.length - 1].transactions.push(itemTr) + blockModel.appendTransaction(itemTr) + } + onMiningComplete: + { + } + } + + Button { + id: newAccount + text: qsTr("New Account") + onClicked: { + model.accounts.push(projectModel.stateListModel.newAccount("1000000", QEther.Ether)) + } + } + } + + TransactionDialog { + id: transactionDialog + onAccepted: { + var item = transactionDialog.getItem() + var lastBlock = model.blocks[model.blocks.length - 1]; + if (lastBlock.status === "mined") + model.blocks.push(projectModel.stateListModel.createEmptyBlock()); + model.blocks[model.blocks.length - 1].transactions.push(item) + blockModel.appendTransaction(item) + if (!clientModel.running) + clientModel.executeTr(item) + } + } +} + + diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index 9decc91ae..7827931b0 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -11,7 +11,6 @@ import "." Rectangle { id: debugPanel - property alias transactionLog: transactionLog property alias debugSlider: statesSlider property alias solLocals: solLocals property alias solStorage: solStorage @@ -23,7 +22,7 @@ Rectangle { signal debugExecuteLocation(string documentId, var location) property string compilationErrorMessage property bool assemblyMode: false - + signal panelClosed objectName: "debugPanel" color: "#ededed" clip: true @@ -61,7 +60,6 @@ Rectangle { { Debugger.init(data); debugScrollArea.visible = true; - compilationErrorArea.visible = false; machineStates.visible = true; } if (giveFocus) @@ -97,85 +95,21 @@ Rectangle { Settings { id: splitSettings - property alias transactionLogHeight: transactionLog.height property alias callStackHeight: callStackRect.height property alias storageHeightSettings: storageRect.height property alias memoryDumpHeightSettings: memoryRect.height property alias callDataHeightSettings: callDataRect.height - property alias transactionLogVisible: transactionLog.visible property alias solCallStackHeightSettings: solStackRect.height property alias solStorageHeightSettings: solStorageRect.height property alias solLocalsHeightSettings: solLocalsRect.height } - Rectangle - { - visible: false; - id: compilationErrorArea - width: parent.width - 20 - height: 600 - color: "#ededed" - anchors.left: parent.left - anchors.top: parent.top - anchors.margins: 10 - ColumnLayout - { - width: parent.width - anchors.top: parent.top - spacing: 15 - Rectangle - { - height: 15 - Button { - text: qsTr("Back to Debugger") - onClicked: { - debugScrollArea.visible = true; - compilationErrorArea.visible = false; - machineStates.visible = true; - } - } - } - - RowLayout - { - height: 100 - ColumnLayout - { - Text { - color: "red" - id: errorLocation - } - Text { - color: "#4a4a4a" - id: errorDetail - } - } - } - - Rectangle - { - width: parent.width - 6 - height: 2 - color: "#d0d0d0" - } - - RowLayout - { - Text - { - color: "#4a4a4a" - id: errorLine - } - } - } - } - - Splitter { + Splitter { id: debugScrollArea anchors.fill: parent orientation: Qt.Vertical - TransactionLog { + /*TransactionLog { id: transactionLog Layout.fillWidth: true Layout.minimumHeight: 130 @@ -186,7 +120,13 @@ Rectangle { anchors.leftMargin: machineStates.sideMargin anchors.rightMargin: machineStates.sideMargin anchors.topMargin: machineStates.sideMargin - } + }*/ + + Button + { + text: qsTr("close") + onClicked: panelClosed() + } ScrollView { @@ -230,7 +170,7 @@ Rectangle { spacing: 3 layoutDirection: Qt.LeftToRight - StepActionImage + /*StepActionImage { id: playAction enabledStateImg: "qrc:/qml/img/play_button.png" @@ -254,7 +194,7 @@ Rectangle { buttonShortcut: "Ctrl+Shift+F9" buttonTooltip: qsTr("Stop Debugging") visible: true - } + }*/ StepActionImage { diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 67f45f973..4af8ece46 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -20,13 +20,14 @@ Rectangle { anchors.fill: parent id: root - property alias rightViewVisible: rightView.visible + property alias rightViewVisible: scenarioExe.visible property alias webViewVisible: webPreview.visible property alias webView: webPreview property alias projectViewVisible: projectList.visible property alias projectNavigator: projectList property alias runOnProjectLoad: mainSettings.runOnProjectLoad - property alias rightPane: rightView + property alias rightPane: scenarioExe + property alias debuggerPanel: debugPanel property alias codeEditor: codeEditor property bool webViewHorizontal: codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally property bool firstCompile: true @@ -42,17 +43,17 @@ Rectangle { } } - Connections { - target: rightView - onDebugExecuteLocation: { + Connections { + target: debugPanel + onDebugExecuteLocation: { codeEditor.highlightExecution(documentId, location); } - } + } Connections { target: codeEditor onBreakpointsChanged: { - rightPane.setBreakpoints(codeEditor.getBreakpoints()); + debugPanel.setBreakpoints(codeEditor.getBreakpoints()); } } @@ -63,20 +64,20 @@ Rectangle { } function toggleRightView() { - rightView.visible = !rightView.visible; + scenarioExe.visible = !scenarioExe.visible; } function ensureRightView() { - rightView.visible = true; + scenarioExe.visible = true; } function rightViewIsVisible() { - return rightView.visible; + return scenarioExe.visible; } function hideRightView() { - rightView.visible = false; + scenarioExe.visible = lfalse; } function toggleWebPreview() { @@ -98,8 +99,8 @@ Rectangle { function displayCompilationErrorIfAny() { - rightView.visible = true; - rightView.displayCompilationErrorIfAny(); + scenarioExe.visible = true; + scenarioExe.displayCompilationErrorIfAny(); } Settings { @@ -153,7 +154,7 @@ Rectangle { id: splitSettings property alias projectWidth: projectList.width property alias contentViewWidth: contentView.width - property alias rightViewWidth: rightView.width + property alias rightViewWidth: scenarioExe.width } Splitter @@ -198,14 +199,41 @@ Rectangle { } } - Debugger { - visible: false; - id: rightView; - Layout.fillHeight: true - Keys.onEscapePressed: visible = false - Layout.minimumWidth: 515 - anchors.right: parent.right - } + ScenarioExecution + { + id: scenarioExe; + visible: false; + Layout.fillHeight: true + Keys.onEscapePressed: visible = false + Layout.minimumWidth: 650 + anchors.right: parent.right + } + + Debugger + { + id: debugPanel + visible: false + Layout.fillHeight: true + Keys.onEscapePressed: visible = false + Layout.minimumWidth: 650 + anchors.right: parent.right + } + + Connections { + target: clientModel + onDebugDataReady: { + scenarioExe.visible = false + debugPanel.visible = true + } + } + + Connections { + target: debugPanel + onPanelClosed: { + scenarioExe.visible = true + debugPanel.visible = false + } + } } } } diff --git a/mix/qml/QAddressView.qml b/mix/qml/QAddressView.qml index c880f0904..3c34d45eb 100644 --- a/mix/qml/QAddressView.qml +++ b/mix/qml/QAddressView.qml @@ -40,20 +40,28 @@ Item accountRef.clear(); accountRef.append({"itemid": " - "}); - if (subType === "contract" || subType === "address") + console.log(blockIndex) + console.log(transactionIndex) + if (subType === "contract" || subType === "address") { var trCr = 0; - for (var k = 0; k < transactionsModel.count; k++) - { - if (k >= transactionIndex) - break; - var tr = transactionsModel.get(k); - if (tr.functionId === tr.contractId /*&& (dec[1] === tr.contractId || item.subType === "address")*/) - { - accountRef.append({ "itemid": tr.contractId + " - " + trCr, "value": "<" + tr.contractId + " - " + trCr + ">", "type": "contract" }); - trCr++; - } - } + if (blockChainPanel) + for (var k = 0; k < blockChainPanel.model.blocks.length; k++) + { + if (k > blockIndex) + break; + for (var i = 0; i < blockChainPanel.model.blocks[k].transactions.length; i++) + { + if (i > transactionIndex) + break; + var tr = blockChainPanel.model.blocks[k].transactions[i] + if (tr.functionId === tr.contractId /*&& (dec[1] === tr.contractId || item.subType === "address")*/) + { + accountRef.append({ "itemid": tr.contractId + " - " + trCr, "value": "<" + tr.contractId + " - " + trCr + ">", "type": "contract" }); + trCr++; + } + } + } } if (subType === "address") { diff --git a/mix/qml/ScenarioExecution.qml b/mix/qml/ScenarioExecution.qml new file mode 100644 index 000000000..5fcbfab3e --- /dev/null +++ b/mix/qml/ScenarioExecution.qml @@ -0,0 +1,57 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Layouts 1.1 +import Qt.labs.settings 1.0 +import "js/Debugger.js" as Debugger +import "js/ErrorLocationFormater.js" as ErrorLocationFormater +import "." + +Rectangle { + + border.color: "red" + border.width: 1 + + Connections + { + target: projectModel + onProjectLoaded: { + loader.init() + } + + } + + Column + { + anchors.margins: 10 + anchors.fill: parent + spacing: 5 + ScenarioLoader + { + width: parent.width + id: loader + } + + Rectangle + { + width: parent.width + height: 1 + color: "#cccccc" + } + + Connections + { + target: loader + onLoaded: { + blockChain.load(scenario) + } + } + + BlockChain + { + id: blockChain + width: parent.width + } + } +} diff --git a/mix/qml/ScenarioLoader.qml b/mix/qml/ScenarioLoader.qml new file mode 100644 index 000000000..0b703c860 --- /dev/null +++ b/mix/qml/ScenarioLoader.qml @@ -0,0 +1,77 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Layouts 1.1 +import Qt.labs.settings 1.0 +import "js/Debugger.js" as Debugger +import "js/ErrorLocationFormater.js" as ErrorLocationFormater +import "." + +RowLayout +{ + signal restored(variant scenario) + signal saved(variant scenario) + signal duplicated(variant scenario) + signal loaded(variant scenario) + + function init() + { + scenarioList.load() + } + + id: blockChainSelector + ComboBox + { + id: scenarioList + model: projectModel.stateListModel + textRole: "title" + onCurrentIndexChanged: + { + restoreScenario.restore() + } + + function load() + { + var state = projectModel.stateListModel.getState(currentIndex) + loaded(state) + } + } + Button + { + id: restoreScenario + text: qsTr("Restore") + onClicked: { + restore() + } + + function restore() + { + var state = projectModel.stateListModel.reloadStateFromFromProject(scenarioList.currentIndex) + restored(state) + loaded(state) + } + + } + Button + { + id: saveScenario + text: qsTr("Save") + onClicked: { + projectModel.saveProjectFile() + saved(state) + } + } + Button + { + id: duplicateScenario + text: qsTr("Duplicate") + onClicked: { + var state = JSON.parse(JSON.stringify(projectModel.stateListModel.getState(scenarioList.currentIndex))) + state.title = qsTr("Copy of ") + state.title; + projectModel.stateListModel.appendState(state) + projectModel.stateListModel.save() + duplicated(state) + } + } +} diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index d3062af9e..d08ed668d 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -23,7 +23,8 @@ Item { return { title: s.title, transactions: s.transactions.filter(function(t) { return !t.stdContract; }).map(fromPlainTransactionItem), //support old projects by filtering std contracts - accounts: s.accounts.map(fromPlainAccountItem), + blocks: s.blocks.map(fromPlainBlockItem), + accounts: s.accounts.map(fromPlainAccountItem), contracts: s.contracts.map(fromPlainAccountItem), miner: s.miner }; @@ -58,7 +59,8 @@ Item { sender: t.sender, isContractCreation: t.isContractCreation, label: t.label, - isFunctionCall: t.isFunctionCall + isFunctionCall: t.isFunctionCall, + saveStatus: t.saveStatus }; if (r.isFunctionCall === undefined) @@ -76,10 +78,22 @@ Item { return r; } + function fromPlainBlockItem(b) + { + var r = { + hash: b.hash, + number: b.number, + transactions: b.transactions.filter(function(t) { return !t.stdContract; }).map(fromPlainTransactionItem), //support old projects by filtering std contracts + status: b.status + } + return r; + } + function toPlainStateItem(s) { return { title: s.title, - transactions: s.transactions.map(toPlainTransactionItem), + blocks: s.blocks.map(toPlainBlockItem), + transactions: s.transactions.map(toPlainTransactionItem), accounts: s.accounts.map(toPlainAccountItem), contracts: s.contracts.map(toPlainAccountItem), miner: s.miner @@ -96,6 +110,17 @@ Item { return ''; } + function toPlainBlockItem(b) + { + var r = { + hash: b.hash, + number: b.number, + transactions: b.transactions.map(toPlainTransactionItem), + status: b.status + } + return r; + } + function toPlainAccountItem(t) { return { @@ -112,6 +137,7 @@ Item { } function toPlainTransactionItem(t) { + console.log(JSON.stringify(t)); var r = { type: t.type, contractId: t.contractId, @@ -125,7 +151,8 @@ Item { parameters: {}, isContractCreation: t.isContractCreation, label: t.label, - isFunctionCall: t.isFunctionCall + isFunctionCall: t.isFunctionCall, + saveStatus: t.saveStatus }; for (var key in t.parameters) r.parameters[key] = t.parameters[key]; @@ -146,14 +173,16 @@ Item { projectData.states.push(toPlainStateItem(stateList[i])); } projectData.defaultStateIndex = stateListModel.defaultStateIndex; - } + stateListModel.data = projectData + + } onNewProject: { var state = toPlainStateItem(stateListModel.createDefaultState()); state.title = qsTr("Default"); projectData.states = [ state ]; projectData.defaultStateIndex = 0; - stateListModel.loadStatesFromProject(projectData); - } + stateListModel.loadStatesFromProject(projectData); + } } Connections { @@ -170,34 +199,40 @@ Item { id: stateDialog onAccepted: { var item = stateDialog.getItem(); - if (stateDialog.stateIndex < stateListModel.count) { - if (stateDialog.isDefault) - stateListModel.defaultStateIndex = stateIndex; - stateList[stateDialog.stateIndex] = item; - stateListModel.set(stateDialog.stateIndex, item); - } else { - if (stateDialog.isDefault) - stateListModel.defaultStateIndex = 0; - stateList.push(item); - stateListModel.append(item); - } - if (stateDialog.isDefault) - stateListModel.defaultStateChanged(); - stateListModel.save(); + saveState(item); } + + function saveState(item) + { + if (stateDialog.stateIndex < stateListModel.count) { + if (stateDialog.isDefault) + stateListModel.defaultStateIndex = stateIndex; + stateList[stateDialog.stateIndex] = item; + stateListModel.set(stateDialog.stateIndex, item); + } else { + if (stateDialog.isDefault) + stateListModel.defaultStateIndex = 0; + stateList.push(item); + stateListModel.append(item); + } + if (stateDialog.isDefault) + stateListModel.defaultStateChanged(); + stateListModel.save(); + } } ListModel { id: stateListModel property int defaultStateIndex: 0 - signal defaultStateChanged; + property variant data + signal defaultStateChanged; signal stateListModelReady; signal stateRun(int index) signal stateDeleted(int index) function defaultTransactionItem() { return TransactionHelper.defaultTransaction(); - } + } function newAccount(_balance, _unit, _secret) { @@ -208,15 +243,26 @@ Item { return { name: name, secret: _secret, balance: QEtherHelper.createEther(_balance, _unit), address: address }; } + function createEmptyBlock() + { + return { + hash: "", + number: -1, + transactions: [], + status: "pending" + } + } + function createDefaultState() { var item = { title: "", transactions: [], accounts: [], - contracts: [] - }; + contracts: [], + blocks: [{ status: "pending", number: -1, hash: "", transactions: []}] + }; - var account = newAccount("1000000", QEther.Ether, defaultAccount) + var account = newAccount("1000000", QEther.Ether, defaultAccount) item.accounts.push(account); item.miner = account; @@ -228,7 +274,8 @@ Item { ctorTr.label = qsTr("Deploy") + " " + ctorTr.contractId; ctorTr.sender = item.accounts[0].secret; item.transactions.push(ctorTr); - } + item.blocks[0].transactions.push(ctorTr) + } return item; } @@ -284,10 +331,20 @@ Item { stateDialog.open(stateListModel.count, item, false); } + function appendState(item) + { + stateListModel.append(item); + stateList.push(item); + } + function editState(index) { stateDialog.open(index, stateList[index], defaultStateIndex === index); } + function getState(index) { + return stateList[index]; + } + function debugDefaultState() { if (defaultStateIndex >= 0 && defaultStateIndex < stateList.length) runState(defaultStateIndex); @@ -295,8 +352,8 @@ Item { function runState(index) { var item = stateList[index]; - clientModel.setupState(item); - stateRun(index); + //clientModel.setupState(item); + //stateRun(index); } function deleteState(index) { @@ -322,8 +379,22 @@ Item { return stateList[defaultStateIndex].title; } + function reloadStateFromFromProject(index) + { + console.log(JSON.stringify(data)) + if (data) + { + var item = fromPlainStateItem(data.states[index]) + + stateListModel.set(index, item) + stateList[index] = item + return item + } + } + function loadStatesFromProject(projectData) { + data = projectData if (!projectData.states) projectData.states = []; if (projectData.defaultStateIndex !== undefined) diff --git a/mix/qml/StructView.qml b/mix/qml/StructView.qml index 029fd162d..9cb461204 100644 --- a/mix/qml/StructView.qml +++ b/mix/qml/StructView.qml @@ -9,6 +9,7 @@ Column property alias members: repeater.model //js array property variant accounts property var value: ({}) + property int blockIndex property int transactionIndex property string context Layout.fillWidth: true diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index df5ad781b..b53c7c787 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -17,6 +17,7 @@ Dialog { visible: false title: qsTr("Edit Transaction") property int transactionIndex + property int blockIndex property alias gas: gasValueEdit.gasValue; property alias gasAuto: gasAutoCheck.checked; property alias gasPrice: gasPriceField.value; @@ -27,21 +28,24 @@ Dialog { property var paramsModel: []; property bool useTransactionDefaultValue: false property alias stateAccounts: senderComboBox.model + property bool saveStatus signal accepted; StateDialogStyle { id: transactionDialogStyle } - function open(index, item) { + function open(index, blockIdx, item) { rowFunction.visible = !useTransactionDefaultValue; rowValue.visible = !useTransactionDefaultValue; rowGas.visible = !useTransactionDefaultValue; rowGasPrice.visible = !useTransactionDefaultValue; - transactionIndex = index; - typeLoader.transactionIndex = index; - + transactionIndex = index + blockIndex = blockIdx + typeLoader.transactionIndex = index + typeLoader.blockIndex = blockIdx + saveStatus = item.saveStatus gasValueEdit.gasValue = item.gas; gasAutoCheck.checked = item.gasAuto ? true : false; gasPriceField.value = item.gasPrice; @@ -229,7 +233,7 @@ Dialog { item.functionId = item.contractId; item.label = qsTr("Deploy") + " " + item.contractId; } - + item.saveStatus = saveStatus item.sender = senderComboBox.model[senderComboBox.currentIndex].secret; item.parameters = paramValues; return item; diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js index 6fce7686d..1e6169e5d 100644 --- a/mix/qml/js/ProjectModel.js +++ b/mix/qml/js/ProjectModel.js @@ -101,6 +101,8 @@ function loadProject(path) { console.log("Loading project at " + path); var projectFile = path + projectFileName; var json = fileIo.readFile(projectFile); + if (!json) + return; var projectData = JSON.parse(json); if (projectData.deploymentDir) projectModel.deploymentDir = projectData.deploymentDir diff --git a/mix/qml/js/TransactionHelper.js b/mix/qml/js/TransactionHelper.js index b9a011b66..8a7c5f5b8 100644 --- a/mix/qml/js/TransactionHelper.js +++ b/mix/qml/js/TransactionHelper.js @@ -12,7 +12,8 @@ function defaultTransaction() stdContract: false, isContractCreation: true, label: "", - isFunctionCall: true + isFunctionCall: true, + saveStatus: true }; } From 799ff95c28e4092694aadd1aeb07d1d4e3d915ba Mon Sep 17 00:00:00 2001 From: yann300 Date: Sun, 7 Jun 2015 12:19:04 +0200 Subject: [PATCH 05/69] - Catch events for each incoming transaction. --- mix/ClientModel.cpp | 302 ++++++++++-------- mix/ClientModel.h | 7 +- mix/ContractCallDataEncoder.cpp | 12 +- mix/ContractCallDataEncoder.h | 4 +- mix/MachineStates.h | 1 + mix/MixClient.cpp | 547 ++++++++++++++++---------------- mix/MixClient.h | 2 +- mix/QContractDefinition.cpp | 17 +- mix/QContractDefinition.h | 8 + mix/QFunctionDefinition.cpp | 32 +- mix/QFunctionDefinition.h | 7 + mix/QVariableDeclaration.cpp | 13 +- mix/QVariableDeclaration.h | 9 +- mix/qml/Block.qml | 28 +- mix/qml/BlockChain.qml | 11 +- 15 files changed, 571 insertions(+), 429 deletions(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 3cd401e23..156a5285f 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include "DebuggingStateWrapper.h" #include "Exceptions.h" #include "QContractDefinition.h" @@ -82,7 +83,7 @@ ClientModel::ClientModel(): qRegisterMetaType("QCallData"); qRegisterMetaType("RecordLogEntry*"); - //connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection); + //connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection); m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString())); m_ethAccounts = make_shared([=](){return m_client.get();}, std::vector()); @@ -113,7 +114,7 @@ QString ClientModel::apiCall(QString const& _message) void ClientModel::mine() { - if (m_mining) + if (m_mining) BOOST_THROW_EXCEPTION(ExecutionStateException()); m_mining = true; emit miningStarted(); @@ -210,108 +211,108 @@ QVariantList ClientModel::gasCosts() const void ClientModel::setupScenario(QVariantMap _scenario) { - m_queueTransactions.clear(); - m_running = true; - - m_currentScenario = _scenario; - QVariantList blocks = _scenario.value("blocks").toList(); - QVariantList stateAccounts = _scenario.value("accounts").toList(); - - m_accounts.clear(); - std::vector userAccounts; - - for (auto const& b: stateAccounts) - { - QVariantMap account = b.toMap(); - Address address = {}; - if (account.contains("secret")) - { - KeyPair key(Secret(account.value("secret").toString().toStdString())); - userAccounts.push_back(key); - address = key.address(); - } - else if (account.contains("address")) - address = Address(fromHex(account.value("address").toString().toStdString())); - if (!address) - continue; - - m_accounts[address] = Account(qvariant_cast(account.value("balance"))->toU256Wei(), Account::NormalCreation); - } - m_ethAccounts->setAccounts(userAccounts); - for (auto const& b: blocks) - { - QVariantList transactions = b.toMap().value("transactions").toList(); - m_queueTransactions.push_back(transactions); - } - - m_client->resetState(m_accounts, Secret(m_currentScenario.value("miner").toMap().value("secret").toString().toStdString())); - - connect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock, Qt::QueuedConnection); - connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection); - connect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock, Qt::QueuedConnection); - processNextBlock(); + m_queueTransactions.clear(); + m_running = true; + + m_currentScenario = _scenario; + QVariantList blocks = _scenario.value("blocks").toList(); + QVariantList stateAccounts = _scenario.value("accounts").toList(); + + m_accounts.clear(); + std::vector userAccounts; + + for (auto const& b: stateAccounts) + { + QVariantMap account = b.toMap(); + Address address = {}; + if (account.contains("secret")) + { + KeyPair key(Secret(account.value("secret").toString().toStdString())); + userAccounts.push_back(key); + address = key.address(); + } + else if (account.contains("address")) + address = Address(fromHex(account.value("address").toString().toStdString())); + if (!address) + continue; + + m_accounts[address] = Account(qvariant_cast(account.value("balance"))->toU256Wei(), Account::NormalCreation); + } + m_ethAccounts->setAccounts(userAccounts); + for (auto const& b: blocks) + { + QVariantList transactions = b.toMap().value("transactions").toList(); + m_queueTransactions.push_back(transactions); + } + + m_client->resetState(m_accounts, Secret(m_currentScenario.value("miner").toMap().value("secret").toString().toStdString())); + + connect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock, Qt::QueuedConnection); + connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection); + connect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock, Qt::QueuedConnection); + processNextBlock(); } void ClientModel::stopExecution() { - disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock); - disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock); - disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution); - m_running = false; + disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock); + disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock); + disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution); + m_running = false; } void ClientModel::finalizeBlock() { - if (m_queueTransactions.size() > 0) - mine(); - else - { - disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock); - disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock); - disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution); - m_running = false; - emit runComplete(); - } + if (m_queueTransactions.size() > 0) + mine(); + else + { + disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock); + disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock); + disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution); + m_running = false; + emit runComplete(); + } } void ClientModel::processNextBlock() { - processNextTransactions(); + processNextTransactions(); } void ClientModel::processNextTransactions() { - vector transactionSequence; - for (auto const& t: m_queueTransactions.front()) - { - QVariantMap transaction = t.toMap(); - QString contractId = transaction.value("contractId").toString(); - QString functionId = transaction.value("functionId").toString(); - bool gasAuto = transaction.value("gasAuto").toBool(); - u256 gas = 0; - if (transaction.value("gas").data()) - gas = boost::get(qvariant_cast(transaction.value("gas"))->internalValue()); - else - gasAuto = true; - - u256 value = (qvariant_cast(transaction.value("value")))->toU256Wei(); - u256 gasPrice = (qvariant_cast(transaction.value("gasPrice")))->toU256Wei(); - QString sender = transaction.value("sender").toString(); - bool isContractCreation = transaction.value("isContractCreation").toBool(); - bool isFunctionCall = transaction.value("isFunctionCall").toBool(); - if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later - contractId = m_codeModel->contracts().keys()[0]; - TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()), isContractCreation, isFunctionCall); - transactionSettings.parameterValues = transaction.value("parameters").toMap(); - - if (contractId == functionId || functionId == "Constructor") - transactionSettings.functionId.clear(); - - transactionSequence.push_back(transactionSettings); - } - m_queueTransactions.pop_front(); - executeSequence(transactionSequence); + vector transactionSequence; + for (auto const& t: m_queueTransactions.front()) + { + QVariantMap transaction = t.toMap(); + QString contractId = transaction.value("contractId").toString(); + QString functionId = transaction.value("functionId").toString(); + bool gasAuto = transaction.value("gasAuto").toBool(); + u256 gas = 0; + if (transaction.value("gas").data()) + gas = boost::get(qvariant_cast(transaction.value("gas"))->internalValue()); + else + gasAuto = true; + + u256 value = (qvariant_cast(transaction.value("value")))->toU256Wei(); + u256 gasPrice = (qvariant_cast(transaction.value("gasPrice")))->toU256Wei(); + QString sender = transaction.value("sender").toString(); + bool isContractCreation = transaction.value("isContractCreation").toBool(); + bool isFunctionCall = transaction.value("isFunctionCall").toBool(); + if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later + contractId = m_codeModel->contracts().keys()[0]; + TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()), isContractCreation, isFunctionCall); + transactionSettings.parameterValues = transaction.value("parameters").toMap(); + + if (contractId == functionId || functionId == "Constructor") + transactionSettings.functionId.clear(); + + transactionSequence.push_back(transactionSettings); + } + m_queueTransactions.pop_front(); + executeSequence(transactionSequence); } void ClientModel::executeSequence(vector const& _sequence) @@ -323,7 +324,7 @@ void ClientModel::executeSequence(vector const& _sequence) } emit runStarted(); - //emit runStateChanged(); + //emit runStateChanged(); //run sequence @@ -332,7 +333,7 @@ void ClientModel::executeSequence(vector const& _sequence) try { vector
deployedContracts; - //onStateReset(); + //onStateReset(); m_gasCosts.clear(); for (TransactionSettings const& transaction: _sequence) { @@ -360,7 +361,7 @@ void ClientModel::executeSequence(vector const& _sequence) break; } if (!f) - emit runFailed("Function '" + transaction.functionId + tr("' not found. Please check transactions or the contract code.")); + emit runFailed("Function '" + transaction.functionId + tr("' not found. Please check transactions or the contract code.")); if (!transaction.functionId.isEmpty()) encoder.encode(f); for (QVariableDeclaration const* p: f->parametersList()) @@ -391,62 +392,62 @@ void ClientModel::executeSequence(vector const& _sequence) { auto contractAddressIter = m_contractAddresses.find(ctrInstance); if (contractAddressIter == m_contractAddresses.end()) - emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId); - callAddress(contractAddressIter->second, encoder.encodedData(), transaction); + emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId); + callAddress(contractAddressIter->second, encoder.encodedData(), transaction); } m_gasCosts.append(m_client->lastExecution().gasUsed); onNewTransaction(); - emit runComplete(); - } + emit runComplete(); + } } catch(boost::exception const&) { cerr << boost::current_exception_diagnostic_information(); emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); - } + } catch(exception const& e) { cerr << boost::current_exception_diagnostic_information(); emit runFailed(e.what()); - } + } emit runStateChanged(); - }); + }); } void ClientModel::executeTr(QVariantMap _tr) { - QVariantList trs; - trs.push_back(_tr); - m_queueTransactions.push_back(trs); - processNextTransactions(); + QVariantList trs; + trs.push_back(_tr); + m_queueTransactions.push_back(trs); + processNextTransactions(); } std::pair ClientModel::resolvePair(QString const& _contractId) { - std::pair ret = std::make_pair(_contractId, 0); - if (_contractId.startsWith("<") && _contractId.endsWith(">")) - { - QStringList values = ret.first.remove("<").remove(">").split(" - "); - ret = std::make_pair(values[0], values[1].toUInt()); - } - return ret; + std::pair ret = std::make_pair(_contractId, 0); + if (_contractId.startsWith("<") && _contractId.endsWith(">")) + { + QStringList values = ret.first.remove("<").remove(">").split(" - "); + ret = std::make_pair(values[0], values[1].toUInt()); + } + return ret; } QString ClientModel::resolveToken(std::pair const& _value, vector
const& _contracts) { if (_contracts.size() > 0) - return QString::fromStdString("0x" + dev::toHex(m_contractAddresses[_value].ref())); //dev::toHex(_contracts.at(_value.second).ref())); + return QString::fromStdString("0x" + dev::toHex(m_contractAddresses[_value].ref())); //dev::toHex(_contracts.at(_value.second).ref())); else return _value.first; } std::pair ClientModel::retrieveToken(QString const& _value, vector
const& _contracts) { - std::pair ret; - ret.first = _value; - ret.second = _contracts.size() - 1; - return ret; + std::pair ret; + ret.first = _value; + ret.second = _contracts.size() - 1; + return ret; } void ClientModel::showDebugger() @@ -647,7 +648,7 @@ void ClientModel::emptyRecord() void ClientModel::debugRecord(unsigned _index) { ExecutionResult e = m_client->execution(_index); - showDebuggerForTransaction(e); + showDebuggerForTransaction(e); } Address ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction) @@ -668,7 +669,7 @@ RecordLogEntry* ClientModel::lastBlock() const strGas << blockInfo.gasUsed; stringstream strNumber; strNumber << blockInfo.number; - RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str()), QString(), tr("Block"), QVariantMap()); + RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str()), QString(), tr("Block"), QVariantMap(), QVariantList()); QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership); return record; } @@ -685,7 +686,7 @@ void ClientModel::onStateReset() void ClientModel::onNewTransaction() { ExecutionResult const& tr = m_client->lastExecution(); - unsigned block = m_client->number() + 1; + unsigned block = m_client->number() + 1; unsigned recordIndex = tr.executonIndex; QString transactionIndex = tr.isCall() ? QObject::tr("Call") : QString("%1:%2").arg(block).arg(tr.transactionIndex); QString address = QString::fromStdString(toJS(tr.address)); @@ -727,9 +728,11 @@ void ClientModel::onNewTransaction() Address contractAddress = (bool)tr.address ? tr.address : tr.contractAddress; auto contractAddressIter = m_contractNames.find(contractAddress); - QVariantMap inputParameters; + QVariantMap inputParameters; + QVariantList logs; if (contractAddressIter != m_contractNames.end()) { + ContractCallDataEncoder encoder; CompiledContract const& compilerRes = m_codeModel->contract(contractAddressIter->second); const QContractDefinition* def = compilerRes.contract(); contract = def->name(); @@ -739,25 +742,64 @@ void ClientModel::onNewTransaction() if (funcDef) { function = funcDef->name(); - ContractCallDataEncoder encoder; QStringList returnValues = encoder.decode(funcDef->returnParameters(), tr.result.output); returned += "("; returned += returnValues.join(", "); returned += ")"; - bytes data = tr.inputParameters; - data.erase(data.begin(), data.begin() + 4); - QStringList parameters = encoder.decode(funcDef->parametersList(), data); - for (int k = 0; k < parameters.length(); ++k) - inputParameters.insert(funcDef->parametersList().at(k)->name(), parameters.at(k)); - } + bytes data = tr.inputParameters; + data.erase(data.begin(), data.begin() + 4); + QStringList parameters = encoder.decode(funcDef->parametersList(), data); + for (int k = 0; k < parameters.length(); ++k) + inputParameters.insert(funcDef->parametersList().at(k)->name(), parameters.at(k)); + } } - } - LocalisedLogEntries logs = m_client->logs(); - QString sender = QString::fromStdString(dev::toHex(tr.sender.ref())); - QString label = contract + "." + function + "()"; - RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction, - gasUsed, sender, label, inputParameters); + // Fill generated logs and decode parameters + for (auto const& log: tr.logs) + { + QVariantMap l; + l.insert("address", QString::fromStdString(log.address.hex())); + auto const& sign = log.topics.front(); // first hash supposed to be the event signature. To check + auto dataIterator = log.data.begin(); + int topicDataIndex = 1; + for (auto const& event: def->eventsList()) + { + if (sign == event->fullHash()) + { + QVariantList paramsList; + l.insert("name", event->name()); + for (auto const& e: event->parametersList()) + { + bytes data; + QString param; + if (!e->isIndexed()) + { + data = bytes(dataIterator, dataIterator + 32); + dataIterator = dataIterator + 32; + } + else + { + data = log.topics.at(topicDataIndex).asBytes(); + topicDataIndex++; + } + param = encoder.decode(e, data); + QVariantMap p; + p.insert("indexed", e->isIndexed()); + p.insert("value", param); + p.insert("name", e->name()); + paramsList.push_back(p); + } + l.insert("param", paramsList); + break; + } + } + logs.push_back(l); + } + } + QString sender = QString::fromStdString(dev::toHex(tr.sender.ref())); + QString label = contract + "." + function + "()"; + RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction, + gasUsed, sender, label, inputParameters, logs); QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership); emit newRecord(log); } diff --git a/mix/ClientModel.h b/mix/ClientModel.h index 503b4189f..7cc7b03f6 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -115,6 +115,8 @@ class RecordLogEntry: public QObject Q_PROPERTY(QString label MEMBER m_label CONSTANT) /// input parameters Q_PROPERTY(QVariantMap parameters MEMBER m_inputParameters CONSTANT) + /// logs + Q_PROPERTY(QVariantList logs MEMBER m_logs CONSTANT) public: enum RecordType @@ -126,9 +128,9 @@ public: RecordLogEntry(): m_recordIndex(0), m_call(false), m_type(RecordType::Transaction) {} RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed, - QString _sender, QString _label, QVariantMap _inputParameters): + QString _sender, QString _label, QVariantMap _inputParameters, QVariantList _logs): m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed), - m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters) {} + m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters), m_logs(_logs) {} private: unsigned m_recordIndex; @@ -144,6 +146,7 @@ private: QString m_sender; QString m_label; QVariantMap m_inputParameters; + QVariantList m_logs; }; /** diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 70c1e6068..553c5e075 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -228,16 +228,10 @@ QVariant ContractCallDataEncoder::decode(SolidityType const& _type, bytes const& BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Parameter declaration not found")); } -QStringList ContractCallDataEncoder::decode(QList const& _returnParameters, vector _value) +QString ContractCallDataEncoder::decode(QVariableDeclaration* const& _param, bytes _value) { - QStringList r; - for (int k = 0; k <_returnParameters.length(); k++) - { - QVariableDeclaration* dec = static_cast(_returnParameters.at(k)); - SolidityType const& type = dec->type()->type(); - r.append(decode(type, _value.at(k)).toString()); - } - return r; + SolidityType const& type = _param->type()->type(); + return decode(type, _value).toString(); } QStringList ContractCallDataEncoder::decode(QList const& _returnParameters, bytes _value) diff --git a/mix/ContractCallDataEncoder.h b/mix/ContractCallDataEncoder.h index 57b364b49..748239c5b 100644 --- a/mix/ContractCallDataEncoder.h +++ b/mix/ContractCallDataEncoder.h @@ -48,8 +48,8 @@ public: void encode(QVariant const& _data, SolidityType const& _type); /// Decode variable in order to be sent to QML view. QStringList decode(QList const& _dec, bytes _value); - /// Decode @param _parameters - QStringList decode(QList const& _parameters, std::vector _value); + /// Decode @param _parameter + QString decode(QVariableDeclaration* const& _param, bytes _value); /// Decode single variable QVariant decode(SolidityType const& _type, bytes const& _value); /// Get all encoded data encoded by encode function. diff --git a/mix/MachineStates.h b/mix/MachineStates.h index ae9804f7f..1b50c062b 100644 --- a/mix/MachineStates.h +++ b/mix/MachineStates.h @@ -84,6 +84,7 @@ namespace mix unsigned transactionIndex; unsigned executonIndex = 0; bytes inputParameters; + eth::LocalisedLogEntries logs; bool isCall() const { return transactionIndex == std::numeric_limits::max(); } bool isConstructor() const { return !isCall() && !address; } diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index fcc72db9d..fc2a153c5 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** @file MixClient.cpp * @author Arkadiy Paronyan arkadiy@ethdev.com @@ -48,27 +48,27 @@ namespace struct MixPow //dummy POW { - typedef int Solution; - static void assignResult(int, BlockInfo const&) {} - static bool verify(BlockInfo const&) { return true; } + typedef int Solution; + static void assignResult(int, BlockInfo const&) {} + static bool verify(BlockInfo const&) { return true; } }; } bytes MixBlockChain::createGenesisBlock(h256 _stateRoot) { - RLPStream block(3); - block.appendList(15) - << h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie - << LogBloom() << c_mixGenesisDifficulty << 0 << c_genesisGasLimit << 0 << (unsigned)0 - << std::string() << h256() << h64(u64(42)); - block.appendRaw(RLPEmptyList); - block.appendRaw(RLPEmptyList); - return block.out(); + RLPStream block(3); + block.appendList(15) + << h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie + << LogBloom() << c_mixGenesisDifficulty << 0 << c_genesisGasLimit << 0 << (unsigned)0 + << std::string() << h256() << h64(u64(42)); + block.appendRaw(RLPEmptyList); + block.appendRaw(RLPEmptyList); + return block.out(); } MixClient::MixClient(std::string const& _dbPath): - m_dbPath(_dbPath) + m_dbPath(_dbPath) { resetState(std::unordered_map()); } @@ -85,335 +85,350 @@ LocalisedLogEntries MixClient::logs() void MixClient::resetState(std::unordered_map const& _accounts, Secret const& _miner) { - WriteGuard l(x_state); - Guard fl(x_filtersWatches); - m_filters.clear(); - m_watches.clear(); - LogFilter filter; - m_filters.insert(std::make_pair(filter.sha3(), filter)); - m_watches.insert(std::make_pair(0, ClientWatch(filter.sha3(), Reaping::Automatic))); - - m_stateDB = OverlayDB(); - SecureTrieDB accountState(&m_stateDB); - accountState.init(); - - dev::eth::commit(_accounts, static_cast(m_stateDB), accountState); - h256 stateRoot = accountState.root(); - m_bc.reset(); - m_bc.reset(new MixBlockChain(m_dbPath, stateRoot)); - m_state = eth::State(m_stateDB, BaseState::PreExisting, KeyPair(_miner).address()); - m_state.sync(bc()); - m_startState = m_state; - WriteGuard lx(x_executions); - m_executions.clear(); + WriteGuard l(x_state); + Guard fl(x_filtersWatches); + m_filters.clear(); + m_watches.clear(); + //LogFilter filter; + //m_filters.insert(std::make_pair(filter.sha3(), filter)); + //m_watches.insert(std::make_pair(0, ClientWatch(filter.sha3(), Reaping::Automatic))); + + m_stateDB = OverlayDB(); + SecureTrieDB accountState(&m_stateDB); + accountState.init(); + + dev::eth::commit(_accounts, static_cast(m_stateDB), accountState); + h256 stateRoot = accountState.root(); + m_bc.reset(); + m_bc.reset(new MixBlockChain(m_dbPath, stateRoot)); + m_state = eth::State(m_stateDB, BaseState::PreExisting, KeyPair(_miner).address()); + m_state.sync(bc()); + m_startState = m_state; + WriteGuard lx(x_executions); + m_executions.clear(); } Transaction MixClient::replaceGas(Transaction const& _t, u256 const& _gas, Secret const& _secret) { - Transaction ret; - if (_secret) - { - if (_t.isCreation()) - ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce(), _secret); - else - ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce(), _secret); - } - else - { - if (_t.isCreation()) - ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce()); - else - ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce()); - ret.forceSender(_t.safeSender()); - } - return ret; + Transaction ret; + if (_secret) + { + if (_t.isCreation()) + ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce(), _secret); + else + ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce(), _secret); + } + else + { + if (_t.isCreation()) + ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce()); + else + ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce()); + ret.forceSender(_t.safeSender()); + } + return ret; } void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call, bool _gasAuto, Secret const& _secret) { - Transaction t = _gasAuto ? replaceGas(_t, m_state.gasLimitRemaining()) : _t; + Transaction t = _gasAuto ? replaceGas(_t, m_state.gasLimitRemaining()) : _t; // do debugging run first - LastHashes lastHashes(256); - lastHashes[0] = bc().numberHash(bc().number()); - for (unsigned i = 1; i < 256; ++i) - lastHashes[i] = lastHashes[i - 1] ? bc().details(lastHashes[i - 1]).parent : h256(); - - State execState = _state; - execState.addBalance(t.sender(), t.gas() * t.gasPrice()); //give it enough balance for gas estimation - Executive execution(execState, lastHashes, 0); - execution.initialize(t); - execution.execute(); - std::vector machineStates; - std::vector levels; - std::vector codes; - std::map codeIndexes; - std::vector data; - std::map dataIndexes; - bytes const* lastCode = nullptr; - bytesConstRef const* lastData = nullptr; - unsigned codeIndex = 0; - unsigned dataIndex = 0; - auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt) - { - VM& vm = *static_cast(voidVM); - ExtVM const& ext = *static_cast(voidExt); - if (lastCode == nullptr || lastCode != &ext.code) - { - auto const& iter = codeIndexes.find(&ext.code); - if (iter != codeIndexes.end()) - codeIndex = iter->second; - else - { - codeIndex = codes.size(); - codes.push_back(MachineCode({ext.myAddress, ext.code})); - codeIndexes[&ext.code] = codeIndex; - } - lastCode = &ext.code; - } - - if (lastData == nullptr || lastData != &ext.data) - { - auto const& iter = dataIndexes.find(&ext.data); - if (iter != dataIndexes.end()) - dataIndex = iter->second; - else - { - dataIndex = data.size(); - data.push_back(ext.data.toBytes()); - dataIndexes[&ext.data] = dataIndex; - } - lastData = &ext.data; - } - - if (levels.size() < ext.depth) - levels.push_back(machineStates.size() - 1); - else - levels.resize(ext.depth); - - machineStates.emplace_back(MachineState({steps, vm.curPC(), inst, newMemSize, vm.gas(), - vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels, codeIndex, dataIndex})); - }; - - execution.go(onOp); - execution.finalize(); - dev::eth::ExecutionResult er = execution.executionResult(); - - switch (er.excepted) - { - case TransactionException::None: - break; - case TransactionException::NotEnoughCash: - BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Insufficient balance for contract deployment")); - case TransactionException::OutOfGasBase: - case TransactionException::OutOfGas: - BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas")); - case TransactionException::BlockGasLimitReached: - BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Block gas limit reached")); - case TransactionException::OutOfStack: - BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Out of stack")); - case TransactionException::StackUnderflow: - BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Stack underflow")); - //these should not happen in mix - case TransactionException::Unknown: - case TransactionException::BadInstruction: - case TransactionException::BadJumpDestination: - case TransactionException::InvalidSignature: - case TransactionException::InvalidNonce: - BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Internal execution error")); - }; - - ExecutionResult d; + LastHashes lastHashes(256); + lastHashes[0] = bc().numberHash(bc().number()); + for (unsigned i = 1; i < 256; ++i) + lastHashes[i] = lastHashes[i - 1] ? bc().details(lastHashes[i - 1]).parent : h256(); + + State execState = _state; + execState.addBalance(t.sender(), t.gas() * t.gasPrice()); //give it enough balance for gas estimation + Executive execution(execState, lastHashes, 0); + execution.initialize(t); + execution.execute(); + std::vector machineStates; + std::vector levels; + std::vector codes; + std::map codeIndexes; + std::vector data; + std::map dataIndexes; + bytes const* lastCode = nullptr; + bytesConstRef const* lastData = nullptr; + unsigned codeIndex = 0; + unsigned dataIndex = 0; + auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt) + { + VM& vm = *static_cast(voidVM); + ExtVM const& ext = *static_cast(voidExt); + if (lastCode == nullptr || lastCode != &ext.code) + { + auto const& iter = codeIndexes.find(&ext.code); + if (iter != codeIndexes.end()) + codeIndex = iter->second; + else + { + codeIndex = codes.size(); + codes.push_back(MachineCode({ext.myAddress, ext.code})); + codeIndexes[&ext.code] = codeIndex; + } + lastCode = &ext.code; + } + + if (lastData == nullptr || lastData != &ext.data) + { + auto const& iter = dataIndexes.find(&ext.data); + if (iter != dataIndexes.end()) + dataIndex = iter->second; + else + { + dataIndex = data.size(); + data.push_back(ext.data.toBytes()); + dataIndexes[&ext.data] = dataIndex; + } + lastData = &ext.data; + } + + if (levels.size() < ext.depth) + levels.push_back(machineStates.size() - 1); + else + levels.resize(ext.depth); + + machineStates.emplace_back(MachineState({steps, vm.curPC(), inst, newMemSize, vm.gas(), + vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels, codeIndex, dataIndex})); + }; + + execution.go(onOp); + execution.finalize(); + dev::eth::ExecutionResult er = execution.executionResult(); + + switch (er.excepted) + { + case TransactionException::None: + break; + case TransactionException::NotEnoughCash: + BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Insufficient balance for contract deployment")); + case TransactionException::OutOfGasBase: + case TransactionException::OutOfGas: + BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas")); + case TransactionException::BlockGasLimitReached: + BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Block gas limit reached")); + case TransactionException::OutOfStack: + BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Out of stack")); + case TransactionException::StackUnderflow: + BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Stack underflow")); + //these should not happen in mix + case TransactionException::Unknown: + case TransactionException::BadInstruction: + case TransactionException::BadJumpDestination: + case TransactionException::InvalidSignature: + case TransactionException::InvalidNonce: + BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Internal execution error")); + }; + + ExecutionResult d; d.inputParameters = t.data(); - d.result = execution.executionResult(); - d.machineStates = machineStates; - d.executionCode = std::move(codes); - d.transactionData = std::move(data); - d.address = _t.receiveAddress(); - d.sender = _t.sender(); - d.value = _t.value(); - d.gasUsed = er.gasUsed + er.gasRefunded + c_callStipend; - if (_t.isCreation()) - d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce()))); - if (!_call) - d.transactionIndex = m_state.pending().size(); - d.executonIndex = m_executions.size(); + d.result = execution.executionResult(); + d.machineStates = machineStates; + d.executionCode = std::move(codes); + d.transactionData = std::move(data); + d.address = _t.receiveAddress(); + d.sender = _t.sender(); + d.value = _t.value(); + d.gasUsed = er.gasUsed + er.gasRefunded + c_callStipend; + if (_t.isCreation()) + d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce()))); + if (!_call) + d.transactionIndex = m_state.pending().size(); + d.executonIndex = m_executions.size(); // execute on a state - if (!_call) - { - t = _gasAuto ? replaceGas(_t, d.gasUsed, _secret) : _t; - er =_state.execute(lastHashes, t); - if (t.isCreation() && _state.code(d.contractAddress).empty()) - BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); - d.gasUsed = er.gasUsed + er.gasRefunded + er.gasForDeposit + c_callStipend; - // collect watches - h256Set changed; - Guard l(x_filtersWatches); - for (std::pair& i: m_filters) - if ((unsigned)i.second.filter.latest() > bc().number()) - { - // acceptable number. - auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1)); - if (m.size()) - { - // filter catches them - for (LogEntry const& l: m) - i.second.changes.push_back(LocalisedLogEntry(l, bc().number() + 1)); - changed.insert(i.first); - } - } - changed.insert(dev::eth::PendingChangedFilter); - noteChanged(changed); - } - WriteGuard l(x_executions); - m_executions.emplace_back(std::move(d)); + if (!_call) + { + t = _gasAuto ? replaceGas(_t, d.gasUsed, _secret) : _t; + er = _state.execute(lastHashes, t); + if (t.isCreation() && _state.code(d.contractAddress).empty()) + BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); + d.gasUsed = er.gasUsed + er.gasRefunded + er.gasForDeposit + c_callStipend; + // collect watches + h256Set changed; + Guard l(x_filtersWatches); + LocalisedLogEntries logs; + //for (unsigned i = 0; i < _state.pending().size(); ++i) + //{ + TransactionReceipt const& tr = _state.receipt(_state.pending().size() - 1); + + auto trHash = _state.pending().at(_state.pending().size() - 1).sha3(); + //for (std::pair& installedFilter: m_filters) + //{ + LogEntries le = tr.log(); // installedFilter.second.filter.matches(tr); + if (le.size()) + for (unsigned j = 0; j < le.size(); ++j) + logs.insert(logs.begin(), LocalisedLogEntry(le[j], bc().number() + 1, trHash)); + //} + //} + + /*if ((unsigned)i.second.filter.latest() > bc().number()) + { + // acceptable number. + auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1)); + if (m.size()) + { + // filter catches them + for (LogEntry const& l: m) + i.second.changes.push_back(LocalisedLogEntry(l, bc().number() + 1)); + changed.insert(i.first); + } + }*/ + changed.insert(dev::eth::PendingChangedFilter); + d.logs = logs; + //noteChanged(changed); + } + WriteGuard l(x_executions); + m_executions.emplace_back(std::move(d)); } void MixClient::mine() { - WriteGuard l(x_state); - m_state.commitToMine(bc()); - m_state.completeMine(0); - bc().import(m_state.blockData(), m_state.db(), ImportRequirements::Default & ~ImportRequirements::ValidNonce); - m_state.sync(bc()); - m_startState = m_state; - h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter }; - noteChanged(changed); + WriteGuard l(x_state); + m_state.commitToMine(bc()); + m_state.completeMine(0); + bc().import(m_state.blockData(), m_state.db(), ImportRequirements::Default & ~ImportRequirements::ValidNonce); + m_state.sync(bc()); + m_startState = m_state; + h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter }; + //noteChanged(changed); } ExecutionResult MixClient::lastExecution() const { - ReadGuard l(x_executions); - return m_executions.empty() ? ExecutionResult() : m_executions.back(); + ReadGuard l(x_executions); + return m_executions.empty() ? ExecutionResult() : m_executions.back(); } ExecutionResult MixClient::execution(unsigned _index) const { - ReadGuard l(x_executions); - return m_executions.at(_index); + ReadGuard l(x_executions); + return m_executions.at(_index); } State MixClient::asOf(h256 const& _block) const { - ReadGuard l(x_state); - return State(m_stateDB, bc(), _block); + ReadGuard l(x_state); + return State(m_stateDB, bc(), _block); } void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto) { - WriteGuard l(x_state); - u256 n = m_state.transactionsFrom(toAddress(_secret)); - Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); - executeTransaction(t, m_state, false, _gasAuto, _secret); + WriteGuard l(x_state); + u256 n = m_state.transactionsFrom(toAddress(_secret)); + Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); + executeTransaction(t, m_state, false, _gasAuto, _secret); } Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, bool _gasAuto) { - WriteGuard l(x_state); - u256 n = m_state.transactionsFrom(toAddress(_secret)); - eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); - executeTransaction(t, m_state, false, _gasAuto, _secret); - Address address = right160(sha3(rlpList(t.sender(), t.nonce()))); - return address; + WriteGuard l(x_state); + u256 n = m_state.transactionsFrom(toAddress(_secret)); + eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); + executeTransaction(t, m_state, false, _gasAuto, _secret); + Address address = right160(sha3(rlpList(t.sender(), t.nonce()))); + return address; } dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, bool _gasAuto, FudgeFactor _ff) { - (void)_blockNumber; - State temp = asOf(eth::PendingBlock); - u256 n = temp.transactionsFrom(_from); - Transaction t(_value, _gasPrice, _gas, _dest, _data, n); - t.forceSender(_from); - if (_ff == FudgeFactor::Lenient) - temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value())); - WriteGuard lw(x_state); //TODO: lock is required only for last execution state - executeTransaction(t, temp, true, _gasAuto); - return lastExecution().result; + (void)_blockNumber; + State temp = asOf(eth::PendingBlock); + u256 n = temp.transactionsFrom(_from); + Transaction t(_value, _gasPrice, _gas, _dest, _data, n); + t.forceSender(_from); + if (_ff == FudgeFactor::Lenient) + temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value())); + WriteGuard lw(x_state); //TODO: lock is required only for last execution state + executeTransaction(t, temp, true, _gasAuto); + return lastExecution().result; } void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) { - submitTransaction(_secret, _value, _dest, _data, _gas, _gasPrice, false); + submitTransaction(_secret, _value, _dest, _data, _gas, _gasPrice, false); } Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) { - return submitTransaction(_secret, _endowment, _init, _gas, _gasPrice, false); + return submitTransaction(_secret, _endowment, _init, _gas, _gasPrice, false); } dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, eth::FudgeFactor _ff) { - return call(_from, _value, _dest, _data, _gas, _gasPrice, _blockNumber, false, _ff); + return call(_from, _value, _dest, _data, _gas, _gasPrice, _blockNumber, false, _ff); } dev::eth::ExecutionResult MixClient::create(Address const& _from, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, eth::FudgeFactor _ff) { - (void)_blockNumber; - u256 n; - State temp; - { - ReadGuard lr(x_state); - temp = asOf(eth::PendingBlock); - n = temp.transactionsFrom(_from); - } - Transaction t(_value, _gasPrice, _gas, _data, n); - t.forceSender(_from); - if (_ff == FudgeFactor::Lenient) - temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value())); - WriteGuard lw(x_state); //TODO: lock is required only for last execution state - executeTransaction(t, temp, true, false); - return lastExecution().result; + (void)_blockNumber; + u256 n; + State temp; + { + ReadGuard lr(x_state); + temp = asOf(eth::PendingBlock); + n = temp.transactionsFrom(_from); + } + Transaction t(_value, _gasPrice, _gas, _data, n); + t.forceSender(_from); + if (_ff == FudgeFactor::Lenient) + temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value())); + WriteGuard lw(x_state); //TODO: lock is required only for last execution state + executeTransaction(t, temp, true, false); + return lastExecution().result; } -void MixClient::noteChanged(h256Set const& _filters) +/*void MixClient::noteChanged(h256Set const& _filters) { - for (auto& i: m_watches) - if (_filters.count(i.second.id)) - { - if (m_filters.count(i.second.id)) - i.second.changes += m_filters.at(i.second.id).changes; - else - i.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0)); - } - for (auto& i: m_filters) - i.second.changes.clear(); -} + for (auto& i: m_watches) + if (_filters.count(i.second.id)) + { + if (m_filters.count(i.second.id)) + i.second.changes += m_filters.at(i.second.id).changes; + else + i.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0)); + } + for (auto& i: m_filters) + i.second.changes.clear(); +}*/ eth::BlockInfo MixClient::blockInfo() const { - ReadGuard l(x_state); - return BlockInfo(bc().block()); + ReadGuard l(x_state); + return BlockInfo(bc().block()); } void MixClient::setAddress(Address _us) { - WriteGuard l(x_state); - m_state.setAddress(_us); + WriteGuard l(x_state); + m_state.setAddress(_us); } void MixClient::startMining() { - //no-op + //no-op } void MixClient::stopMining() { - //no-op + //no-op } bool MixClient::isMining() const { - return false; + return false; } uint64_t MixClient::hashrate() const { - return 0; + return 0; } eth::MiningProgress MixClient::miningProgress() const { - return eth::MiningProgress(); + return eth::MiningProgress(); } } diff --git a/mix/MixClient.h b/mix/MixClient.h index 852937634..ece603942 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -91,7 +91,7 @@ protected: private: void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call, bool _gasAuto, dev::Secret const& _secret = dev::Secret()); - void noteChanged(h256Set const& _filters); + //void noteChanged(h256Set const& _filters); dev::eth::Transaction replaceGas(dev::eth::Transaction const& _t, dev::u256 const& _gas, dev::Secret const& _secret = dev::Secret()); eth::State m_state; diff --git a/mix/QContractDefinition.cpp b/mix/QContractDefinition.cpp index 51b37e399..fe05e80be 100644 --- a/mix/QContractDefinition.cpp +++ b/mix/QContractDefinition.cpp @@ -39,8 +39,23 @@ QContractDefinition::QContractDefinition(QObject* _parent, dev::solidity::Contra else m_constructor = new QFunctionDefinition(parent); + std::vector found; + for (auto const& f: _contract->getDefinedFunctions()) + { + m_functions.append(new QFunctionDefinition(parent, f)); + found.push_back(f->getName()); + } + for (auto const& it: _contract->getInterfaceFunctions()) - m_functions.append(new QFunctionDefinition(parent, it.second)); + { + if (std::find(found.begin(), found.end(), it.second->getDeclaration().getName()) == found.end()) + m_functions.append(new QFunctionDefinition(parent, it.second)); + } + + + for (auto const& it: _contract->getEvents()) + m_events.append(new QFunctionDefinition(parent, it)); + } QFunctionDefinition const* QContractDefinition::getFunction(dev::FixedHash<4> _hash) const diff --git a/mix/QContractDefinition.h b/mix/QContractDefinition.h index ff0df1f15..f68133ab7 100644 --- a/mix/QContractDefinition.h +++ b/mix/QContractDefinition.h @@ -37,6 +37,7 @@ class QContractDefinition: public QBasicNodeDefinition Q_OBJECT Q_PROPERTY(QQmlListProperty functions READ functions CONSTANT) Q_PROPERTY(dev::mix::QFunctionDefinition* constructor READ constructor CONSTANT) + Q_PROPERTY(QQmlListProperty events READ events CONSTANT) public: QContractDefinition(QObject* _parent, solidity::ContractDefinition const* _contract); @@ -44,12 +45,19 @@ public: QQmlListProperty functions() const { return QQmlListProperty(const_cast(this), const_cast(this)->m_functions); } /// Get the constructor of the contract. QFunctionDefinition* constructor() const { return m_constructor; } + /// Get all the functions of the contract. QList const& functionsList() const { return m_functions; } /// Find function by hash, returns nullptr if not found QFunctionDefinition const* getFunction(dev::FixedHash<4> _hash) const; + /// Get events + QQmlListProperty events() const { return QQmlListProperty(const_cast(this), const_cast(this)->m_events); } + /// Get events + QList const& eventsList() const { return m_events; } + private: QList m_functions; QFunctionDefinition* m_constructor; + QList m_events; }; } diff --git a/mix/QFunctionDefinition.cpp b/mix/QFunctionDefinition.cpp index e6764d712..52d8ad30c 100644 --- a/mix/QFunctionDefinition.cpp +++ b/mix/QFunctionDefinition.cpp @@ -28,15 +28,41 @@ using namespace dev::solidity; using namespace dev::mix; -QFunctionDefinition::QFunctionDefinition(QObject* _parent, dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(_parent, &_f->getDeclaration()), m_hash(dev::sha3(_f->externalSignature())) +QFunctionDefinition::QFunctionDefinition(QObject* _parent, dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(_parent, &_f->getDeclaration()), m_hash(dev::sha3(_f->externalSignature())), + m_fullHash(dev::sha3(_f->externalSignature())) +{ + init(_f); +} + +QFunctionDefinition::QFunctionDefinition(QObject* _parent, ASTPointer const& _f): QBasicNodeDefinition(_parent, _f.get()), m_hash(dev::sha3(_f->externalSignature())), + m_fullHash(dev::sha3(_f->externalSignature())) +{ + + for (unsigned i = 0; i < _f->getParameters().size(); ++i) + m_parameters.append(new QVariableDeclaration(parent(), _f->getParameters().at(i))); + + for (unsigned i = 0; i < _f->getReturnParameters().size(); ++i) + m_returnParameters.append(new QVariableDeclaration(parent(), _f->getReturnParameters().at(i))); +} + +QFunctionDefinition::QFunctionDefinition(QObject* _parent, ASTPointer const& _e): QBasicNodeDefinition(_parent, _e.get()) +{ + for (unsigned i = 0; i < _e->getParameters().size(); ++i) + m_parameters.append(new QVariableDeclaration(parent(), _e->getParameters().at(i))); + FunctionTypePointer _f = std::make_shared(*_e); + m_hash = (FixedHash<4>)dev::sha3(_f->externalSignature(_e->getName())); + m_fullHash = dev::sha3(_f->externalSignature(_e->getName())); +} + +void QFunctionDefinition::init(dev::solidity::FunctionTypePointer _f) { auto paramNames = _f->getParameterNames(); auto paramTypes = _f->getParameterTypes(); auto returnNames = _f->getReturnParameterNames(); auto returnTypes = _f->getReturnParameterTypes(); for (unsigned i = 0; i < paramNames.size(); ++i) - m_parameters.append(new QVariableDeclaration(_parent, paramNames[i], paramTypes[i].get())); + m_parameters.append(new QVariableDeclaration(parent(), paramNames[i], paramTypes[i].get())); for (unsigned i = 0; i < returnNames.size(); ++i) - m_returnParameters.append(new QVariableDeclaration(_parent, returnNames[i], returnTypes[i].get())); + m_returnParameters.append(new QVariableDeclaration(parent(), returnNames[i], returnTypes[i].get())); } diff --git a/mix/QFunctionDefinition.h b/mix/QFunctionDefinition.h index 18f2d911b..a9c45ffcd 100644 --- a/mix/QFunctionDefinition.h +++ b/mix/QFunctionDefinition.h @@ -41,6 +41,10 @@ public: QFunctionDefinition(){} QFunctionDefinition(QObject* _parent): QBasicNodeDefinition(_parent) {} QFunctionDefinition(QObject* _parent, solidity::FunctionTypePointer const& _f); + QFunctionDefinition(QObject* _parent, solidity::ASTPointer const& _f); + QFunctionDefinition(QObject* _parent, solidity::ASTPointer const& _f); + /// Init members + void init(dev::solidity::FunctionTypePointer _f); /// Get all input parameters of this function. QList const& parametersList() const { return m_parameters; } /// Get all input parameters of this function as QML property. @@ -49,10 +53,13 @@ public: QList returnParameters() const { return m_returnParameters; } /// Get the hash of this function declaration on the contract ABI. FixedHash<4> hash() const { return m_hash; } + /// Get the full hash of this function declaration on the contract ABI. + FixedHash<32> fullHash() const { return m_fullHash; } private: int m_index; FixedHash<4> m_hash; + FixedHash<32> m_fullHash; QList m_parameters; QList m_returnParameters; void initQParameters(); diff --git a/mix/QVariableDeclaration.cpp b/mix/QVariableDeclaration.cpp index 3bbb0d523..4299e34ee 100644 --- a/mix/QVariableDeclaration.cpp +++ b/mix/QVariableDeclaration.cpp @@ -24,27 +24,32 @@ #include #include "CodeModel.h" +using namespace solidity; + namespace dev { namespace mix { -QVariableDeclaration::QVariableDeclaration(QObject* _parent, solidity::VariableDeclaration const* _v): - QBasicNodeDefinition(_parent, _v), +QVariableDeclaration::QVariableDeclaration(QObject* _parent, ASTPointer const _v): + QBasicNodeDefinition(_parent, _v.get()), m_type(new QSolidityType(this, CodeModel::nodeType(_v->getType().get()))) { + m_isIndexed = _v->isIndexed(); } -QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type): +QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type, bool _isIndexed): QBasicNodeDefinition(_parent, _name), m_type(new QSolidityType(_parent, _type)) { + m_isIndexed = _isIndexed; } -QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type): +QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type, bool _isIndexed): QBasicNodeDefinition(_parent, _name), m_type(new QSolidityType(this, CodeModel::nodeType(_type))) { + m_isIndexed = _isIndexed; } QSolidityType::QSolidityType(QObject* _parent, SolidityType const& _type): diff --git a/mix/QVariableDeclaration.h b/mix/QVariableDeclaration.h index 4309550b2..85c719987 100644 --- a/mix/QVariableDeclaration.h +++ b/mix/QVariableDeclaration.h @@ -21,6 +21,7 @@ #include #include +#include #include "QBasicNodeDefinition.h" #include "SolidityType.h" @@ -82,14 +83,16 @@ class QVariableDeclaration: public QBasicNodeDefinition public: QVariableDeclaration() {} - QVariableDeclaration(QObject* _parent, solidity::VariableDeclaration const* _v); - QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type); - QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type); + QVariableDeclaration(QObject* _parent, solidity::ASTPointer const _v); + QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type, bool _isIndexed = false); + QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type, bool _isIndexed = false); QSolidityType* type() const { return m_type; } void setType(QSolidityType* _type) { m_type = _type; } + bool isIndexed() { return m_isIndexed; } private: QSolidityType* m_type; + bool m_isIndexed; }; diff --git a/mix/qml/Block.qml b/mix/qml/Block.qml index e331dc6a6..c3ce7865a 100644 --- a/mix/qml/Block.qml +++ b/mix/qml/Block.qml @@ -121,22 +121,36 @@ ColumnLayout clip: true } + Label + { + id: logs + width: logsWidth + text: { + if (index >= 0 && transactions.get(index).logs) + { + for (var k in transactions.get(index).logs) + { + console.log("_________________________") + console.log(JSON.stringify(transactions.get(index).logs[k])) + console.log("_________________________") + } + return transactions.get(index).logs.length + } + else + return 0 + } + } + Button { id: debug - width: logsWidth + width: debugActionWidth text: "debug" onClicked: { clientModel.debugRecord(transactions.get(index).recordIndex); } } - - Label - { - id: logs - width: logsWidth - } } } } diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml index 6e87ed52d..6098ec035 100644 --- a/mix/qml/BlockChain.qml +++ b/mix/qml/BlockChain.qml @@ -30,8 +30,9 @@ Column { property int statusWidth: 50 property int fromWidth: 100 property int toWidth: 250 - property int valueWidth: 100 + property int valueWidth: 50 property int logsWidth: 50 + property int debugActionWidth: 50 Row { @@ -62,6 +63,11 @@ Column { text: "Logs" width: logsWidth } + Label + { + text: "Action" + width: debugActionWidth + } } Rectangle @@ -253,6 +259,8 @@ Column { blockModel.getTransaction(blockIndex, trIndex).returned = _r.returned; tr.recordIndex = _r.recordIndex; blockModel.getTransaction(blockIndex, trIndex).recordIndex = _r.recordIndex; + tr.logs = _r.logs; + blockModel.getTransaction(blockIndex, trIndex).logs = _r.logs; return; } } @@ -270,6 +278,7 @@ Column { itemTr.value = QEtherHelper.createEther(_r.value, QEther.Wei) itemTr.sender = _r.sender itemTr.recordIndex = _r.recordIndex + itemTr.logs = _r.logs model.blocks[model.blocks.length - 1].transactions.push(itemTr) blockModel.appendTransaction(itemTr) From 6e6ebc60371c09b08c2dbd6a6411d6f693ff8a4e Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 8 Jun 2015 13:42:39 +0200 Subject: [PATCH 06/69] ScenarioExecution.qml ux design --- mix/qml.qrc | 1 + mix/qml/Block.qml | 169 +++++++++------ mix/qml/BlockChain.qml | 334 +++++++++++++++++------------ mix/qml/ScenarioButton.qml | 62 ++++++ mix/qml/ScenarioExecution.qml | 7 +- mix/qml/ScenarioLoader.qml | 27 ++- mix/qml/StateListModel.qml | 5 +- mix/qml/img/duplicateIcon.png | Bin 0 -> 1020 bytes mix/qml/img/duplicateIcon@2x.png | Bin 0 -> 2326 bytes mix/qml/img/newIcon.png | Bin 0 -> 801 bytes mix/qml/img/newIcon@2x.png | Bin 0 -> 1768 bytes mix/qml/img/recycle-discard.png | Bin 0 -> 723 bytes mix/qml/img/recycle-discard@2x.png | Bin 0 -> 1568 bytes mix/qml/img/recycle-icon.png | Bin 0 -> 697 bytes mix/qml/img/recycle-icon@2x.png | Bin 0 -> 1435 bytes mix/qml/img/recycle-keep.png | Bin 0 -> 904 bytes mix/qml/img/recycle-keep@2x.png | Bin 0 -> 2141 bytes mix/qml/img/restoreIcon.png | Bin 0 -> 1124 bytes mix/qml/img/restoreIcon@2x.png | Bin 0 -> 2447 bytes mix/qml/img/saveIcon.png | Bin 0 -> 1068 bytes mix/qml/img/saveIcon@2x.png | Bin 0 -> 2359 bytes mix/res.qrc | 12 ++ 22 files changed, 409 insertions(+), 208 deletions(-) create mode 100644 mix/qml/ScenarioButton.qml create mode 100644 mix/qml/img/duplicateIcon.png create mode 100644 mix/qml/img/duplicateIcon@2x.png create mode 100644 mix/qml/img/newIcon.png create mode 100644 mix/qml/img/newIcon@2x.png create mode 100644 mix/qml/img/recycle-discard.png create mode 100644 mix/qml/img/recycle-discard@2x.png create mode 100644 mix/qml/img/recycle-icon.png create mode 100644 mix/qml/img/recycle-icon@2x.png create mode 100644 mix/qml/img/recycle-keep.png create mode 100644 mix/qml/img/recycle-keep@2x.png create mode 100644 mix/qml/img/restoreIcon.png create mode 100644 mix/qml/img/restoreIcon@2x.png create mode 100644 mix/qml/img/saveIcon.png create mode 100644 mix/qml/img/saveIcon@2x.png diff --git a/mix/qml.qrc b/mix/qml.qrc index 0227b4efd..16bb01b83 100644 --- a/mix/qml.qrc +++ b/mix/qml.qrc @@ -68,5 +68,6 @@ qml/BlockChain.qml qml/ScenarioExecution.qml qml/ScenarioLoader.qml + qml/ScenarioButton.qml diff --git a/mix/qml/Block.qml b/mix/qml/Block.qml index c3ce7865a..d3775ea5c 100644 --- a/mix/qml/Block.qml +++ b/mix/qml/Block.qml @@ -10,25 +10,40 @@ import "." ColumnLayout { + id: root property variant transactions property string status property int number - Rectangle - { - width: parent.width - height: 50 - anchors.left: parent.left - anchors.leftMargin: statusWidth - Label { - text: - { - if (status === "mined") - return qsTr("BLOCK") + " " + number - else - return qsTr("BLOCK") + " pending" - } + property int blockWidth: Layout.preferredWidth - statusWidth - horizontalMargin + property int horizontalMargin: 10 + property int trHeight: 30 + spacing: 0 + RowLayout + { + Layout.preferredHeight: trHeight + Layout.preferredWidth: blockWidth + id: rowHeader + Rectangle + { + color: "#DEDCDC" + Layout.preferredWidth: blockWidth + Layout.preferredHeight: trHeight + radius: 4 anchors.left: parent.left + anchors.leftMargin: statusWidth + 5 + Label { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: horizontalMargin + text: + { + if (status === "mined") + return qsTr("BLOCK") + " " + number + else + return qsTr("BLOCK") + " pending" + } + } } } @@ -36,57 +51,80 @@ ColumnLayout { id: transactionRepeater model: transactions - Row + + RowLayout { - height: 50 + Layout.preferredHeight: trHeight Rectangle { id: trSaveStatus + Layout.preferredWidth: statusWidth + Layout.preferredHeight: trHeight color: "transparent" - CheckBox + property bool saveStatus + + Image { + id: saveStatusImage + source: "qrc:/qml/img/recycle-discard@2x.png" + width: statusWidth + fillMode: Image.PreserveAspectFit + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + + Component.onCompleted: { - id: saveStatus - checked: { - if (index >= 0) - return transactions.get(index).saveStatus - else - return true - } - onCheckedChanged: + if (index >= 0) + saveStatus = transactions.get(index).saveStatus + } + + onSaveStatusChanged: + { + if (saveStatus) + saveStatusImage.source = "qrc:/qml/img/recycle-keep@2x.png" + else + saveStatusImage.source = "qrc:/qml/img/recycle-discard@2x.png" + + if (index >= 0) + transactions.get(index).saveStatus = saveStatus + } + + MouseArea { + id: statusMouseArea + anchors.fill: parent + onClicked: { - if (index >= 0) - transactions.get(index).saveStatus = checked + parent.saveStatus = !parent.saveStatus } } } Rectangle { - width: parent.width - height: 50 - color: "#cccccc" - radius: 4 - Row + Layout.preferredWidth: blockWidth + Layout.preferredHeight: parent.height + color: "#DEDCDC" + RowLayout { - Label - { - id: status - width: statusWidth - } - Label + anchors.verticalCenter: parent.verticalCenter + spacing: cellSpacing + Text { id: hash - width: fromWidth + anchors.left: parent.left + anchors.leftMargin: horizontalMargin + Layout.preferredWidth: fromWidth + elide: Text.ElideRight + maximumLineCount: 1 text: { if (index >= 0) return transactions.get(index).sender else return "" } - - clip: true } - Label + + Text { id: func text: { @@ -95,9 +133,9 @@ ColumnLayout else return "" } - - width: toWidth - clip: true + elide: Text.ElideRight + maximumLineCount: 1 + Layout.preferredWidth: toWidth } function userFrienldyToken(value) @@ -108,43 +146,52 @@ ColumnLayout return value } - Label + Text { id: returnValue - width: valueWidth + elide: Text.ElideRight + maximumLineCount: 1 + Layout.preferredWidth: valueWidth text: { if (index >= 0 && transactions.get(index).returned) return transactions.get(index).returned else return "" } - clip: true } - Label + Rectangle { - id: logs + Layout.preferredWidth: logsWidth + Layout.preferredHeight: trHeight - 10 width: logsWidth - text: { - if (index >= 0 && transactions.get(index).logs) - { - for (var k in transactions.get(index).logs) + color: "transparent" + Text + { + id: logs + anchors.left: parent.left + anchors.leftMargin: 10 + text: { + if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count) { - console.log("_________________________") - console.log(JSON.stringify(transactions.get(index).logs[k])) - console.log("_________________________") + for (var k = 0; k < transactions.get(index).logs.count; k++) + { + /*console.log("_________________________") + console.log(JSON.stringify(transactions.get(index).logs[k])) + console.log("_________________________")*/ + } + return transactions.get(index).logs.count } - return transactions.get(index).logs.length + else + return "" } - else - return 0 } } Button { id: debug - width: debugActionWidth + Layout.preferredWidth: debugActionWidth text: "debug" onClicked: { diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml index 6098ec035..2fc78b0b1 100644 --- a/mix/qml/BlockChain.qml +++ b/mix/qml/BlockChain.qml @@ -11,91 +11,126 @@ import "js/TransactionHelper.js" as TransactionHelper import "js/QEtherHelper.js" as QEtherHelper import "." -Column { +ColumnLayout { id: blockChainPanel property variant model - spacing: 5 + spacing: 0 + property int previousWidth + onWidthChanged: + { + + if (width <= 630 || previousWidth <= 630) + { + fromWidth = 100 + toWidth = 100 + valueWidth = 200 + } + else + { + var diff = (width - previousWidth) / 3; + fromWidth = fromWidth + diff < 100 ? 100 : fromWidth + diff + toWidth = toWidth + diff < 100 ? 100 : toWidth + diff + valueWidth = valueWidth + diff < 200 ? 200 : valueWidth + diff + } + previousWidth = width + } function load(scenario) { - if (!scenario) return; model = scenario blockModel.clear() for (var b in model.blocks) blockModel.append(model.blocks[b]) + previousWidth = width } - property int statusWidth: 50 + property int statusWidth: 30 property int fromWidth: 100 - property int toWidth: 250 - property int valueWidth: 50 + property int toWidth: 100 + property int valueWidth: 200 property int logsWidth: 50 property int debugActionWidth: 50 + property int horizontalMargin: 10 + property int cellSpacing: 10 - Row + RowLayout { id: header - width: parent.width - Label - { - text: "Status" - width: statusWidth + spacing: 0 + Layout.preferredHeight: 25 + Image { + id: debugImage + source: "qrc:/qml/img/recycle-icon@2x.png" + Layout.preferredWidth: statusWidth + Layout.preferredHeight: 25 + fillMode: Image.PreserveAspectFit } - Label + Rectangle { - text: "From" - width: fromWidth + Layout.preferredWidth: fromWidth + cellSpacing + Label + { + anchors.verticalCenter: parent.verticalCenter + text: "From" + anchors.left: parent.left + anchors.leftMargin: horizontalMargin + 5 + } } Label { text: "To" - width: toWidth + Layout.preferredWidth: toWidth + cellSpacing } Label { text: "Value" - width: valueWidth + Layout.preferredWidth: valueWidth + cellSpacing } Label { text: "Logs" - width: logsWidth + Layout.preferredWidth: logsWidth + cellSpacing } Label { text: "Action" - width: debugActionWidth + Layout.preferredWidth: debugActionWidth } } Rectangle { - width: parent.width - height: 500 + Layout.preferredHeight: 500 + Layout.preferredWidth: parent.width + //height: 500 border.color: "#cccccc" border.width: 2 color: "white" ScrollView { - width: parent.width - height: parent.height + id: blockChainScrollView + anchors.fill: parent + anchors.topMargin: 10 ColumnLayout { + id: blockChainLayout + width: parent.width + spacing: 10 Repeater // List of blocks { id: blockChainRepeater - width: parent.width model: blockModel Block { - height: + Layout.preferredWidth: blockChainScrollView.width + Layout.preferredHeight: { if (index >= 0) - return 50 + 50 * blockModel.get(index).transactions.length + return 30 + 30 * blockModel.get(index).transactions.count else - return 0 + return 50 } transactions: @@ -143,8 +178,6 @@ Column { function removeTransaction(blockIndex, trIndex) { - console.log(blockIndex) - console.log(trIndex) blockModel.get(blockIndex).transactions.remove(trIndex) } @@ -160,139 +193,174 @@ Column { function getTransaction(block, tr) { - return blockModel.get(block - 1).transactions.get(tr) + return blockModel.get(block).transactions.get(tr) + } + + function setTransaction(blockIndex, trIndex, tr) + { + blockModel.get(blockIndex).transactions.set(trIndex, tr) + } + + function setTransactionProperty(blockIndex, trIndex, propertyName, value) + { + blockModel.get(blockIndex).transactions.set(trIndex, { propertyName: value }) } } - RowLayout + Rectangle { - width: parent.width - Button { - id: rebuild - text: qsTr("Rebuild") - onClicked: - { - for (var j = 0; j < model.blocks.length; j++) + Layout.preferredWidth: parent.width + RowLayout + { + width: parent.width + anchors.top: parent.top + anchors.topMargin: 10 + ScenarioButton { + id: rebuild + text: qsTr("Rebuild") + onClicked: { - for (var k = 0; k < model.blocks[j].transactions.length; k++) + for (var j = 0; j < model.blocks.length; j++) { - if (!blockModel.get(j).transactions.get(k).saveStatus) + for (var k = 0; k < model.blocks[j].transactions.length; k++) { - model.blocks[j].transactions.splice(k, 1) - blockModel.removeTransaction(j, k) - if (model.blocks[j].transactions.length === 0) + if (!blockModel.get(j).transactions.get(k).saveStatus) { - model.blocks[j].splice(j, 1); - blockModel.removeBlock(j); + model.blocks[j].transactions.splice(k, 1) + blockModel.removeTransaction(j, k) + if (model.blocks[j].transactions.length === 0) + { + model.blocks.splice(j, 1); + blockModel.removeBlock(j); + } } } } + clientModel.setupScenario(model); } - clientModel.setupScenario(model); - } - } - Button { - id: addTransaction - text: qsTr("Add Transaction") - onClicked: - { - var lastBlock = model.blocks[model.blocks.length - 1]; - if (lastBlock.status === "mined") - model.blocks.push(projectModel.stateListModel.createEmptyBlock()); - var item = TransactionHelper.defaultTransaction() - transactionDialog.stateAccounts = model.accounts - transactionDialog.open(model.blocks[model.blocks.length - 1].transactions.length, model.blocks.length - 1, item) + Layout.preferredWidth: 100 + Layout.preferredHeight: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/recycle-icon@2x.png" } - } - Button { - id: addBlockBtn - text: qsTr("Add Block") - onClicked: - { - var lastBlock = model.blocks[model.blocks.length - 1] - if (lastBlock.status === "pending") - clientModel.mine() - else - addNewBlock() - } - - function addNewBlock() - { - var block = projectModel.stateListModel.createEmptyBlock() - model.blocks.push(block) - blockModel.appendBlock(block) + ScenarioButton { + id: addTransaction + text: qsTr("Add Transaction") + onClicked: + { + var lastBlock = model.blocks[model.blocks.length - 1]; + if (lastBlock.status === "mined") + model.blocks.push(projectModel.stateListModel.createEmptyBlock()); + var item = TransactionHelper.defaultTransaction() + transactionDialog.stateAccounts = model.accounts + transactionDialog.open(model.blocks[model.blocks.length - 1].transactions.length, model.blocks.length - 1, item) + } + Layout.preferredWidth: 100 + Layout.preferredHeight: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/recycle-icon@2x.png" } - } - Connections - { - target: clientModel - onNewBlock: - { - if (!clientModel.running) + ScenarioButton { + id: addBlockBtn + text: qsTr("Add Block") + onClicked: { var lastBlock = model.blocks[model.blocks.length - 1] - lastBlock.status = "mined" - lastBlock.number = model.blocks.length - var lastB = blockModel.get(model.blocks.length - 1) - lastB.status = "mined" - lastB.number = model.blocks.length - addBlockBtn.addNewBlock() + if (lastBlock.status === "pending") + clientModel.mine() + else + addNewBlock() } + + function addNewBlock() + { + var block = projectModel.stateListModel.createEmptyBlock() + model.blocks.push(block) + blockModel.appendBlock(block) + } + Layout.preferredWidth: 100 + Layout.preferredHeight: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/recycle-icon@2x.png" } - onStateCleared: - { - } - onNewRecord: + + Connections { - var blockIndex = _r.transactionIndex.split(":")[0] - var trIndex = _r.transactionIndex.split(":")[1] - if (parseInt(blockIndex) <= model.blocks.length) + target: clientModel + onNewBlock: { - var item = model.blocks[parseInt(blockIndex) - 1]; - if (parseInt(trIndex) <= item.transactions.length) + if (!clientModel.running) { - var tr = item.transactions[parseInt(trIndex)]; - tr.returned = _r.returned; - blockModel.getTransaction(blockIndex, trIndex).returned = _r.returned; - tr.recordIndex = _r.recordIndex; - blockModel.getTransaction(blockIndex, trIndex).recordIndex = _r.recordIndex; - tr.logs = _r.logs; - blockModel.getTransaction(blockIndex, trIndex).logs = _r.logs; - return; + var lastBlock = model.blocks[model.blocks.length - 1] + lastBlock.status = "mined" + lastBlock.number = model.blocks.length + var lastB = blockModel.get(model.blocks.length - 1) + lastB.status = "mined" + lastB.number = model.blocks.length + addBlockBtn.addNewBlock() } } + onStateCleared: + { + } + onNewRecord: + { + var blockIndex = parseInt(_r.transactionIndex.split(":")[0]) - 1 + var trIndex = parseInt(_r.transactionIndex.split(":")[1]) + if (blockIndex <= model.blocks.length) + { + var item = model.blocks[blockIndex] + if (trIndex <= item.transactions.length) + { + var tr = item.transactions[trIndex] + tr.returned = _r.returned + tr.recordIndex = _r.recordIndex + tr.logs = _r.logs + var trModel = blockModel.getTransaction(blockIndex, trIndex) + trModel.returned = _r.returned + trModel.recordIndex = _r.recordIndex + trModel.logs = _r.logs + blockModel.setTransaction(blockIndex, trIndex, trModel) + return; + } + } - // tr is not in the list. coming from JavaScript - var itemTr = TransactionHelper.defaultTransaction() - itemTr.functionId = _r.function - itemTr.contractId = _r.contract - itemTr.gasAuto = true - itemTr.parameters = _r.parameters - itemTr.isContractCreation = itemTr.functionId === itemTr.contractId - itemTr.label = _r.label - itemTr.isFunctionCall = itemTr.functionId !== "" - itemTr.returned = _r.returned - itemTr.value = QEtherHelper.createEther(_r.value, QEther.Wei) - itemTr.sender = _r.sender - itemTr.recordIndex = _r.recordIndex - itemTr.logs = _r.logs - - model.blocks[model.blocks.length - 1].transactions.push(itemTr) - blockModel.appendTransaction(itemTr) - } - onMiningComplete: - { + // tr is not in the list. coming from JavaScript + var itemTr = TransactionHelper.defaultTransaction() + itemTr.saveStatus = false + itemTr.functionId = _r.function + itemTr.contractId = _r.contract + itemTr.gasAuto = true + itemTr.parameters = _r.parameters + itemTr.isContractCreation = itemTr.functionId === itemTr.contractId + itemTr.label = _r.label + itemTr.isFunctionCall = itemTr.functionId !== "" + itemTr.returned = _r.returned + itemTr.value = QEtherHelper.createEther(_r.value, QEther.Wei) + itemTr.sender = _r.sender + itemTr.recordIndex = _r.recordIndex + itemTr.logs = _r.logs + model.blocks[model.blocks.length - 1].transactions.push(itemTr) + blockModel.appendTransaction(itemTr) + } + onMiningComplete: + { + } } - } - Button { - id: newAccount - text: qsTr("New Account") - onClicked: { - model.accounts.push(projectModel.stateListModel.newAccount("1000000", QEther.Ether)) + ScenarioButton { + id: newAccount + text: qsTr("New Account") + onClicked: { + model.accounts.push(projectModel.stateListModel.newAccount("1000000", QEther.Ether)) + } + Layout.preferredWidth: 100 + Layout.preferredHeight: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/recycle-icon@2x.png" } } } diff --git a/mix/qml/ScenarioButton.qml b/mix/qml/ScenarioButton.qml new file mode 100644 index 000000000..e8884cc2e --- /dev/null +++ b/mix/qml/ScenarioButton.qml @@ -0,0 +1,62 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.0 +import QtQuick.Controls.Styles 1.1 + +Rectangle { + id: buttonActionContainer + property string text + property string buttonShortcut + property string sourceImg + signal clicked + + Rectangle { + id: contentRectangle + anchors.fill: parent + border.color: "#cccccc" + border.width: 1 + radius: 4 + Image { + id: debugImage + anchors { + centerIn: parent + bottomMargin: debugImg.pressed ? 0 : 1; + topMargin: debugImg.pressed ? 1 : 0; + } + source: sourceImg + fillMode: Image.PreserveAspectFit + height: 30 + } + + Button { + anchors.fill: parent + id: debugImg + action: buttonAction + style: ButtonStyle { + background: Rectangle { + color: "transparent" + } + } + } + + Action { + id: buttonAction + shortcut: buttonShortcut + onTriggered: { + buttonActionContainer.clicked(); + } + } + } + + Rectangle + { + anchors.top: contentRectangle.bottom + anchors.topMargin: 15 + width: parent.width + Text + { + text: buttonActionContainer.text + anchors.centerIn: parent + } + } +} diff --git a/mix/qml/ScenarioExecution.qml b/mix/qml/ScenarioExecution.qml index 5fcbfab3e..d02e7d3f8 100644 --- a/mix/qml/ScenarioExecution.qml +++ b/mix/qml/ScenarioExecution.qml @@ -9,9 +9,7 @@ import "js/ErrorLocationFormater.js" as ErrorLocationFormater import "." Rectangle { - - border.color: "red" - border.width: 1 + color: "#ededed" Connections { @@ -26,9 +24,10 @@ Rectangle { { anchors.margins: 10 anchors.fill: parent - spacing: 5 + spacing: 10 ScenarioLoader { + height: 70 width: parent.width id: loader } diff --git a/mix/qml/ScenarioLoader.qml b/mix/qml/ScenarioLoader.qml index 0b703c860..b89c1222b 100644 --- a/mix/qml/ScenarioLoader.qml +++ b/mix/qml/ScenarioLoader.qml @@ -37,32 +37,39 @@ RowLayout loaded(state) } } - Button - { + + ScenarioButton { id: restoreScenario - text: qsTr("Restore") + Layout.preferredWidth: 100 + Layout.preferredHeight: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/restoreIcon@2x.png" onClicked: { restore() } - + text: qsTr("Restore") function restore() { var state = projectModel.stateListModel.reloadStateFromFromProject(scenarioList.currentIndex) restored(state) loaded(state) } - } - Button - { + + ScenarioButton { id: saveScenario text: qsTr("Save") onClicked: { projectModel.saveProjectFile() saved(state) } + Layout.preferredWidth: 100 + Layout.preferredHeight: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/saveIcon@2x.png" } - Button + + ScenarioButton { id: duplicateScenario text: qsTr("Duplicate") @@ -73,5 +80,9 @@ RowLayout projectModel.stateListModel.save() duplicated(state) } + Layout.preferredWidth: 100 + Layout.preferredHeight: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/duplicateIcon@2x.png" } } diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index d08ed668d..44873e8db 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -16,7 +16,9 @@ Item { property string defaultAccount: "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074" //support for old project function fromPlainStateItem(s) { - if (!s.accounts) + console.log("èèèèèèèèèèèè"); + console.log(s); + if (!s.accounts) s.accounts = [stateListModel.newAccount("1000000", QEther.Ether, defaultAccount)]; //support for old project if (!s.contracts) s.contracts = []; @@ -137,7 +139,6 @@ Item { } function toPlainTransactionItem(t) { - console.log(JSON.stringify(t)); var r = { type: t.type, contractId: t.contractId, diff --git a/mix/qml/img/duplicateIcon.png b/mix/qml/img/duplicateIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..b3a2554208e3c2a942a9bfa9484a422c6d8a138e GIT binary patch literal 1020 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 KMNUMnLSTZ1M@~2Z literal 0 HcmV?d00001 diff --git a/mix/qml/img/recycle-discard.png b/mix/qml/img/recycle-discard.png new file mode 100644 index 0000000000000000000000000000000000000000..af1d0959435e93e4d5f55ae572e5b60c42b567c9 GIT binary patch literal 723 zcmV;^0xbQBP)Px%jY&j7R7ef&RZU1#Q562pozX@o9j9p=eUG3BgCK|*TSP);KMTR4l`W#6jV_{v z?IRbtX&KU{bwO>aMKV&PP+6jlLl8j(wdt8DL>fdh&OM#03_J#3^Lz^-mVVsQ0*K7OO1pK{WK|8|Wp7b+qhSiY%A>8vU}h1I zwU9L)CYqQ+^A0b+NC963QUNI>WI;AW`s5ac4OZnbv#Te%fPZ` zK1zhBA{rW`rer&>WG4YJRUd&eFbzAQlsYLV|H548>csqqw015r_$Dz<^da6`x^x|yUsncluhVeuG4n%3xRHmw&ut4N=dja~los>Ky5%FlT3x+C zYf5~U=E#2XFCU*7sPC1OhM{THE7&ZojF3H_Mryag9LDzrd78Hg+&hj`)XOjIWpL|d zm)fkg!quF%9BDqX=3)u-UasX`NYMx}pNF)=dJ2uq!TKTanAFTEnHPUzp=kU!8ooC0 z5zgHXSlx=LABy7Zfk;Z6?~vA?!KxjSqBXscicpd+^jo^)q}Hn={Xk(O@}N4Bo)D6E z*kFFYLfg#UF0{sL+Cw$WlO6TAQb002ovPDHLk FV1gLqOIZK_ literal 0 HcmV?d00001 diff --git a/mix/qml/img/recycle-discard@2x.png b/mix/qml/img/recycle-discard@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c51c21d21199ede62c15ebb50e7635af58e1bfbe GIT binary patch literal 1568 zcmV+*2H*LKP)Px)+DSw~RA>e5S!-+*RTTct?6#Dp@~A+|u0gCyM1^z#K|~TUL@>oC0SQqO!!MMm zA%sXm(D+BA(I_MkA^L~J00}XuXh?iSssuyZ-4K5eQ3wHd1vP?DD5X2c?@pO^y4`Mf zw^J%QO=s`xoO93l=G=47y#oY)0RjO60RjO60RjO60RjO60RjO60RjO6*9L(mjGLCQ zqB|QAjpUm#ftm?PiGKP6VmIca>(7|gALdec6Bu*5CGv$pmV^Xf9>DObTyh#wJsA=@ z0Fi~|g3tG%dRQ*H{`OsS0Dy44h>S6cvz#?lDjN=>>L#xt^pH6r3GWxdIEN0HG8)!Y zFO6Sy@k>xKauSF>PvbTcfsPlTda_;d&~&7ZaO^Pxvjc((BU2UUY_%VmhIB(Qa}-KD zC;%Acl87y#k=yx{o3^U{DXLpkWE}wrb@U-^>y-u5aOSXS55(V~0bq=2Ms%Jf_?Vth znQa{3{LwaTpFb0)k7tkh*&b8?j3H)62#M?`2wgjZ>hTuM+&dem&-iIbcH)#1|DNe_Eta zk~bl?&=TE7nWB(Q361_3N9z0HCH3U!p=3{z!h1}VR+SC3@nkM8wWT>%e_wqH{hBlh zL&rR!jGpyT0wYKE#aGC7BVFPz*FMIHpS!Sz+`075yrzB zwl!ORDQ({Ya1;b)QOVKS3MpEhx0u~uNP7lSC%Jr9xU>vji@A$DEnd3LN9p7i>dnx0Dba_&dF8ZoeQ*gwu`LrR(K{e$A*chnVpOEing!it9RrxIati( z%kjEgwE6n{1ps=tJ6S6(m$c>&p}Ll5fE{5ge2{?N4_IZcOmBG)P9I}C>GTPPFg+;g z+kvl{AhWc;8C1p^rOHu0neKaL3DD*!*~d=gY5+0MMcx9x z^b^v1Z$oSqx6bv<@J4mxEx4gOd2_V1U~&(kuO49AZL3AfA7or|K72Uy@Jw(OWpk@j z#xFw7eZLtaGUsa%9WItmB1F6jEB<{D&|MeomX*$2CHxdWV;_?^TcI^><( zK(+#}a@pQVt!r7H=RLqsRFyM63}IK~XQWRtMr6D^=7WifzC|w@Z~A#rW`ZlP)Z^~S(6nb;i-?;+*0D%C30D%C30D%C30D=D<0{;O2V5305 SQ6=#J0000Px%b4f%&R7ef&mCb7tK^VrHY!bTEARbz%he$qbz$isL8bmSVON+(7!GlPVib%ne zs0S51cn}1^yP~Lw`7)A#ARpHZZ`f#Tqw(~-EDi)3=A{#=AGwx z-e+bfBO}8$bl`t?z_VEca=BdEDVNJj43CJ<(y#&33`V+WGSYS82ESLgsK}+DvF(I}*S}`^xz)&{ydiA~ZA5+O;gpIyq-?;o91`)!dN)Hc9|q zrnjhWFBZSD0yAO={(;b|th3^!G~!mHuyfr8NFMS8#3yUD+DA5s{@2!OwQh$(p)({2 z7brGdm#8ds1A|bUJ-VWcU?5pj*a>j-Xf&J6cd=OP8zAF=jsY4FM2w4E zH1Y1AW<4cH#7IBLbed5iXTDOY*#1kpeuxB#RpSUw;M7eVIdAnyS?$^KSR#=Ka0e!~ f9C!%$Pdo4n78mV*6YJYD00000NkvXXu0mjf;|oPn literal 0 HcmV?d00001 diff --git a/mix/qml/img/recycle-icon@2x.png b/mix/qml/img/recycle-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b590b2adb7698c418a65899f13189bf8b3fa92 GIT binary patch literal 1435 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 literal 0 HcmV?d00001 diff --git a/mix/qml/img/restoreIcon@2x.png b/mix/qml/img/restoreIcon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3160b0bf348f7a9b2136ffa84c8eb11f19afbac2 GIT binary patch literal 2447 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 N002ovPDHLkV1ho6ksSa4 literal 0 HcmV?d00001 diff --git a/mix/qml/img/saveIcon.png b/mix/qml/img/saveIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..46e17522b27729f5d8ad767813183ce12f2bfe03 GIT binary patch literal 1068 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 literal 0 HcmV?d00001 diff --git a/mix/res.qrc b/mix/res.qrc index ac0af57ac..e84f77d84 100644 --- a/mix/res.qrc +++ b/mix/res.qrc @@ -68,5 +68,17 @@ qml/img/warningicon.png qml/img/warningicon@2x.png qml/QAddressView.qml + qml/img/recycle-discard.png + qml/img/recycle-discard@2x.png + qml/img/recycle-icon.png + qml/img/recycle-icon@2x.png + qml/img/recycle-keep.png + qml/img/recycle-keep@2x.png + qml/img/restoreIcon.png + qml/img/restoreIcon@2x.png + qml/img/saveIcon.png + qml/img/saveIcon@2x.png + qml/img/duplicateIcon.png + qml/img/duplicateIcon@2x.png From 885494fe2feaaed50c55352d249492344146dfc1 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 8 Jun 2015 15:17:34 +0200 Subject: [PATCH 07/69] Display Catched Events --- mix/ClientModel.cpp | 6 + mix/qml/Block.qml | 240 ++++++++++++++++++++++++++----------- mix/qml/BlockChain.qml | 5 +- mix/qml/StateListModel.qml | 2 - 4 files changed, 176 insertions(+), 77 deletions(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 156a5285f..195f49f67 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -759,6 +759,12 @@ void ClientModel::onNewTransaction() { QVariantMap l; l.insert("address", QString::fromStdString(log.address.hex())); + std::ostringstream s; + s << log.data; + l.insert("data", QString::fromStdString(s.str())); + std::ostringstream streamTopic; + streamTopic << log.topics; + l.insert("topic", QString::fromStdString(streamTopic.str())); auto const& sign = log.topics.front(); // first hash supposed to be the event signature. To check auto dataIterator = log.data.begin(); int topicDataIndex = 1; diff --git a/mix/qml/Block.qml b/mix/qml/Block.qml index d3775ea5c..b3865ac4a 100644 --- a/mix/qml/Block.qml +++ b/mix/qml/Block.qml @@ -18,6 +18,28 @@ ColumnLayout property int horizontalMargin: 10 property int trHeight: 30 spacing: 0 + property int openedTr: 0 + + function calculateHeight() + { + if (transactions) + { + if (index >= 0) + return 30 + 30 * transactions.count + openedTr + else + return 30 + } + else + return 30 + } + + onOpenedTrChanged: + { + Layout.preferredHeight = calculateHeight() + height = calculateHeight() + } + + RowLayout { @@ -54,13 +76,43 @@ ColumnLayout RowLayout { + id: rowTransaction Layout.preferredHeight: trHeight + function displayContent() + { + logsText.text = "" + if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count) + { + for (var k = 0; k < transactions.get(index).logs.count; k++) + { + var log = transactions.get(index).logs.get(k) + if (log.name) + logsText.text += log.name + ":\n" + else + logsText.text += "log:\n" + + if (log.param) + for (var i = 0; i < log.param.count; i++) + { + var p = log.param.get(i) + logsText.text += p.name + " = " + p.value + " - indexed:" + p.indexed + "\n" + } + else{ + logsText.text += "From : " + log.address + "\n" + } + } + logsText.text += "\n\n" + } + rowDetailedContent.visible = !rowDetailedContent.visible + } + Rectangle { id: trSaveStatus Layout.preferredWidth: statusWidth Layout.preferredHeight: trHeight color: "transparent" + anchors.top: parent.top property bool saveStatus Image { @@ -104,98 +156,144 @@ ColumnLayout Layout.preferredWidth: blockWidth Layout.preferredHeight: parent.height color: "#DEDCDC" - RowLayout + id: rowContentTr + anchors.top: parent.top + ColumnLayout { - anchors.verticalCenter: parent.verticalCenter - spacing: cellSpacing - Text + anchors.top: parent.top + spacing: 10 + RowLayout { - id: hash - anchors.left: parent.left - anchors.leftMargin: horizontalMargin - Layout.preferredWidth: fromWidth - elide: Text.ElideRight - maximumLineCount: 1 - text: { - if (index >= 0) - return transactions.get(index).sender - else - return "" + anchors.top: parent.top + anchors.verticalCenter: parent.verticalCenter + spacing: cellSpacing + Text + { + id: hash + anchors.left: parent.left + anchors.leftMargin: horizontalMargin + Layout.preferredWidth: fromWidth + elide: Text.ElideRight + maximumLineCount: 1 + text: { + if (index >= 0) + return transactions.get(index).sender + else + return "" + } } - } - Text - { - id: func - text: { - if (index >= 0) - parent.userFrienldyToken(transactions.get(index).label) - else - return "" + Text + { + id: func + text: { + if (index >= 0) + parent.userFrienldyToken(transactions.get(index).label) + else + return "" + } + elide: Text.ElideRight + maximumLineCount: 1 + Layout.preferredWidth: toWidth } - elide: Text.ElideRight - maximumLineCount: 1 - Layout.preferredWidth: toWidth - } - function userFrienldyToken(value) - { - if (value && value.indexOf("<") === 0) - return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()"; - else - return value - } - - Text - { - id: returnValue - elide: Text.ElideRight - maximumLineCount: 1 - Layout.preferredWidth: valueWidth - text: { - if (index >= 0 && transactions.get(index).returned) - return transactions.get(index).returned + function userFrienldyToken(value) + { + if (value && value.indexOf("<") === 0) + return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()"; else - return "" + return value } - } - Rectangle - { - Layout.preferredWidth: logsWidth - Layout.preferredHeight: trHeight - 10 - width: logsWidth - color: "transparent" Text { - id: logs - anchors.left: parent.left - anchors.leftMargin: 10 + id: returnValue + elide: Text.ElideRight + maximumLineCount: 1 + Layout.preferredWidth: valueWidth text: { - if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count) - { - for (var k = 0; k < transactions.get(index).logs.count; k++) + if (index >= 0 && transactions.get(index).returned) + return transactions.get(index).returned + else + return "" + } + } + + Rectangle + { + Layout.preferredWidth: logsWidth + Layout.preferredHeight: trHeight - 10 + width: logsWidth + color: "transparent" + Text + { + id: logs + anchors.left: parent.left + anchors.leftMargin: 10 + text: { + if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count) { - /*console.log("_________________________") - console.log(JSON.stringify(transactions.get(index).logs[k])) - console.log("_________________________")*/ + for (var k = 0; k < transactions.get(index).logs.count; k++) + { + console.log("_________________________") + console.log(JSON.stringify(transactions.get(index).logs[k])) + console.log("_________________________") + } + return transactions.get(index).logs.count } - return transactions.get(index).logs.count + else + return "" } - else - return "" + } + MouseArea { + anchors.fill: parent + onClicked: { + rowTransaction.displayContent(); + } + } + } + + Button + { + id: debug + Layout.preferredWidth: debugActionWidth + text: "debug" + onClicked: + { + clientModel.debugRecord(transactions.get(index).recordIndex); } } } - Button + RowLayout { - id: debug - Layout.preferredWidth: debugActionWidth - text: "debug" - onClicked: + id: rowDetailedContent + visible: false + Layout.preferredHeight:{ + if (index >= 0 && transactions.get(index).logs) + return 100 * transactions.get(index).logs.count + else + return 100 + } + onVisibleChanged: { - clientModel.debugRecord(transactions.get(index).recordIndex); + var lognb = transactions.get(index).logs.count + if (visible) + { + rowContentTr.Layout.preferredHeight = trHeight + 100 * lognb + openedTr += 100 * lognb + } + else + { + rowContentTr.Layout.preferredHeight = trHeight + openedTr -= 100 * lognb + } + } + + Text { + anchors.left: parent.left + anchors.leftMargin: horizontalMargin + id: logsText } } } diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml index 2fc78b0b1..e9b1fc1db 100644 --- a/mix/qml/BlockChain.qml +++ b/mix/qml/BlockChain.qml @@ -127,10 +127,7 @@ ColumnLayout { Layout.preferredWidth: blockChainScrollView.width Layout.preferredHeight: { - if (index >= 0) - return 30 + 30 * blockModel.get(index).transactions.count - else - return 50 + return calculateHeight() } transactions: diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index 44873e8db..dac3e0831 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -16,8 +16,6 @@ Item { property string defaultAccount: "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074" //support for old project function fromPlainStateItem(s) { - console.log("èèèèèèèèèèèè"); - console.log(s); if (!s.accounts) s.accounts = [stateListModel.newAccount("1000000", QEther.Ether, defaultAccount)]; //support for old project if (!s.contracts) From e2f83a88407b86b6224d71cb3bb1dba868fed296 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 8 Jun 2015 16:39:48 +0200 Subject: [PATCH 08/69] add Timer to avoid FutureTime --- mix/qml/Block.qml | 8 -------- mix/qml/BlockChain.qml | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/mix/qml/Block.qml b/mix/qml/Block.qml index b3865ac4a..0fa69f1bd 100644 --- a/mix/qml/Block.qml +++ b/mix/qml/Block.qml @@ -232,15 +232,7 @@ ColumnLayout anchors.leftMargin: 10 text: { if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count) - { - for (var k = 0; k < transactions.get(index).logs.count; k++) - { - console.log("_________________________") - console.log(JSON.stringify(transactions.get(index).logs[k])) - console.log("_________________________") - } return transactions.get(index).logs.count - } else return "" } diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml index e9b1fc1db..202d6d75e 100644 --- a/mix/qml/BlockChain.qml +++ b/mix/qml/BlockChain.qml @@ -260,14 +260,29 @@ ColumnLayout { sourceImg: "qrc:/qml/img/recycle-icon@2x.png" } + Timer + { + id: ensureNotFuturetime + interval: 1000 + repeat: false + running: false + } + ScenarioButton { id: addBlockBtn text: qsTr("Add Block") onClicked: { + if (ensureNotFuturetime.running) + return + if (clientModel.mining || clientModel.running) + return var lastBlock = model.blocks[model.blocks.length - 1] if (lastBlock.status === "pending") + { + ensureNotFuturetime.start() clientModel.mine() + } else addNewBlock() } From 13d99b71c7533436628eb157775fc576deb05a64 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 8 Jun 2015 16:48:54 +0200 Subject: [PATCH 09/69] Avoid future time for Rebuild action --- mix/qml/BlockChain.qml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml index 202d6d75e..4556bd204 100644 --- a/mix/qml/BlockChain.qml +++ b/mix/qml/BlockChain.qml @@ -217,6 +217,8 @@ ColumnLayout { text: qsTr("Rebuild") onClicked: { + if (ensureNotFuturetime.running) + return; for (var j = 0; j < model.blocks.length; j++) { for (var k = 0; k < model.blocks[j].transactions.length; k++) @@ -233,6 +235,7 @@ ColumnLayout { } } } + ensureNotFuturetime.start() clientModel.setupScenario(model); } From bce5486554a8fd5be83f76f3c93b2651df646778 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 9 Jun 2015 08:51:33 +0200 Subject: [PATCH 10/69] misc changes --- mix/ClientModel.cpp | 51 +++++++++++----------- mix/ClientModel.h | 2 +- mix/qml/BlockChain.qml | 54 +++++++++++++++++------ mix/qml/ScenarioButton.qml | 9 ++-- mix/qml/ScenarioLoader.qml | 88 ++++++++++++++++++++------------------ 5 files changed, 119 insertions(+), 85 deletions(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 195f49f67..bdb8a0d55 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -211,6 +211,7 @@ QVariantList ClientModel::gasCosts() const void ClientModel::setupScenario(QVariantMap _scenario) { + WriteGuard(x_queueTransactions); m_queueTransactions.clear(); m_running = true; @@ -220,7 +221,6 @@ void ClientModel::setupScenario(QVariantMap _scenario) m_accounts.clear(); std::vector userAccounts; - for (auto const& b: stateAccounts) { QVariantMap account = b.toMap(); @@ -239,23 +239,29 @@ void ClientModel::setupScenario(QVariantMap _scenario) m_accounts[address] = Account(qvariant_cast(account.value("balance"))->toU256Wei(), Account::NormalCreation); } m_ethAccounts->setAccounts(userAccounts); + + bool trToExecute = false; for (auto const& b: blocks) { QVariantList transactions = b.toMap().value("transactions").toList(); m_queueTransactions.push_back(transactions); + trToExecute = transactions.size() > 0; } - m_client->resetState(m_accounts, Secret(m_currentScenario.value("miner").toMap().value("secret").toString().toStdString())); - - connect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock, Qt::QueuedConnection); - connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection); - connect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock, Qt::QueuedConnection); - processNextBlock(); + if (m_queueTransactions.count() > 0 && trToExecute) + { + connect(this, &ClientModel::newBlock, this, &ClientModel::processNextTransactions, Qt::QueuedConnection); + connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection); + connect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock, Qt::QueuedConnection); + processNextTransactions(); + } + else + m_running = false; } void ClientModel::stopExecution() { - disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock); + disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextTransactions); disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock); disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution); m_running = false; @@ -263,26 +269,19 @@ void ClientModel::stopExecution() void ClientModel::finalizeBlock() { + m_queueTransactions.pop_front();// pop last execution group. The last block is never mined (pending block) if (m_queueTransactions.size() > 0) mine(); else { - disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock); - disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock); - disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution); - m_running = false; + stopExecution(); emit runComplete(); } } - -void ClientModel::processNextBlock() -{ - processNextTransactions(); -} - void ClientModel::processNextTransactions() { + WriteGuard(x_queueTransactions); vector transactionSequence; for (auto const& t: m_queueTransactions.front()) { @@ -311,7 +310,6 @@ void ClientModel::processNextTransactions() transactionSequence.push_back(transactionSettings); } - m_queueTransactions.pop_front(); executeSequence(transactionSequence); } @@ -322,18 +320,13 @@ void ClientModel::executeSequence(vector const& _sequence) qWarning() << "Waiting for current execution to complete"; m_runFuture.waitForFinished(); } - emit runStarted(); - //emit runStateChanged(); - - //run sequence m_runFuture = QtConcurrent::run([=]() { try { vector
deployedContracts; - //onStateReset(); m_gasCosts.clear(); for (TransactionSettings const& transaction: _sequence) { @@ -397,8 +390,8 @@ void ClientModel::executeSequence(vector const& _sequence) } m_gasCosts.append(m_client->lastExecution().gasUsed); onNewTransaction(); - emit runComplete(); } + emit runComplete(); } catch(boost::exception const&) { @@ -416,10 +409,16 @@ void ClientModel::executeSequence(vector const& _sequence) void ClientModel::executeTr(QVariantMap _tr) { + WriteGuard(x_queueTransactions); QVariantList trs; trs.push_back(_tr); m_queueTransactions.push_back(trs); - processNextTransactions(); + if (!m_running) + { + m_running = true; + connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection); + processNextTransactions(); + } } diff --git a/mix/ClientModel.h b/mix/ClientModel.h index 7cc7b03f6..ed178cfe4 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -256,7 +256,6 @@ private: std::pair resolvePair(QString const& _contractId); QVariant formatStorageValue(SolidityType const& _type, std::unordered_map const& _storage, unsigned _offset, dev::u256 const& _slot); void processNextTransactions(); - void processNextBlock(); void finalizeBlock(); void stopExecution(); @@ -276,6 +275,7 @@ private: CodeModel* m_codeModel = nullptr; QList m_queueTransactions; QVariantMap m_currentScenario; + mutable boost::shared_mutex x_queueTransactions; }; } diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml index 4556bd204..b73d9acbb 100644 --- a/mix/qml/BlockChain.qml +++ b/mix/qml/BlockChain.qml @@ -104,7 +104,6 @@ ColumnLayout { { Layout.preferredHeight: 500 Layout.preferredWidth: parent.width - //height: 500 border.color: "#cccccc" border.width: 2 color: "white" @@ -209,9 +208,10 @@ ColumnLayout { Layout.preferredWidth: parent.width RowLayout { - width: parent.width + width: 4 * 100 anchors.top: parent.top anchors.topMargin: 10 + spacing: 0 ScenarioButton { id: rebuild text: qsTr("Rebuild") @@ -219,22 +219,40 @@ ColumnLayout { { if (ensureNotFuturetime.running) return; + var retBlocks = []; for (var j = 0; j < model.blocks.length; j++) { + var b = model.blocks[j]; + var block = { + hash: b.hash, + number: b.number, + transactions: [], + status: b.status + } for (var k = 0; k < model.blocks[j].transactions.length; k++) { - if (!blockModel.get(j).transactions.get(k).saveStatus) + if (blockModel.get(j).transactions.get(k).saveStatus) { - model.blocks[j].transactions.splice(k, 1) - blockModel.removeTransaction(j, k) - if (model.blocks[j].transactions.length === 0) - { - model.blocks.splice(j, 1); - blockModel.removeBlock(j); - } + block.transactions.push(model.blocks[j].transactions[k]); } } + if (block.transactions.length > 0) + { + retBlocks.push(block) + } + } + if (retBlocks.length === 0) + { + retBlocks.push(projectModel.stateListModel.createEmptyBlock()) + } + + model.blocks = retBlocks + blockModel.clear() + for (var j = 0; j < model.blocks.length; j++) + { + blockModel.append(model.blocks[j]) } + ensureNotFuturetime.start() clientModel.setupScenario(model); } @@ -280,14 +298,21 @@ ColumnLayout { return if (clientModel.mining || clientModel.running) return - var lastBlock = model.blocks[model.blocks.length - 1] - if (lastBlock.status === "pending") + console.log("lllll"); + if (model.blocks.length > 0) { - ensureNotFuturetime.start() - clientModel.mine() + var lastBlock = model.blocks[model.blocks.length - 1] + if (lastBlock.status === "pending") + { + ensureNotFuturetime.start() + clientModel.mine() + } + else + addNewBlock() } else addNewBlock() + } function addNewBlock() @@ -355,6 +380,7 @@ ColumnLayout { itemTr.isFunctionCall = itemTr.functionId !== "" itemTr.returned = _r.returned itemTr.value = QEtherHelper.createEther(_r.value, QEther.Wei) + console.log("sender " + _r.sender) itemTr.sender = _r.sender itemTr.recordIndex = _r.recordIndex itemTr.logs = _r.logs diff --git a/mix/qml/ScenarioButton.qml b/mix/qml/ScenarioButton.qml index e8884cc2e..a2ccf3f78 100644 --- a/mix/qml/ScenarioButton.qml +++ b/mix/qml/ScenarioButton.qml @@ -19,9 +19,12 @@ Rectangle { Image { id: debugImage anchors { - centerIn: parent - bottomMargin: debugImg.pressed ? 0 : 1; - topMargin: debugImg.pressed ? 1 : 0; + left: parent.left + right: parent.right + top: parent.top + bottom: parent.bottom + bottomMargin: debugImg.pressed ? 0 : 2; + topMargin: debugImg.pressed ? 2 : 0; } source: sourceImg fillMode: Image.PreserveAspectFit diff --git a/mix/qml/ScenarioLoader.qml b/mix/qml/ScenarioLoader.qml index b89c1222b..366813a7a 100644 --- a/mix/qml/ScenarioLoader.qml +++ b/mix/qml/ScenarioLoader.qml @@ -14,7 +14,7 @@ RowLayout signal saved(variant scenario) signal duplicated(variant scenario) signal loaded(variant scenario) - + spacing: 0 function init() { scenarioList.load() @@ -38,51 +38,57 @@ RowLayout } } - ScenarioButton { - id: restoreScenario - Layout.preferredWidth: 100 + Row + { + Layout.preferredWidth: 100 * 3 Layout.preferredHeight: 30 - buttonShortcut: "" - sourceImg: "qrc:/qml/img/restoreIcon@2x.png" - onClicked: { - restore() - } - text: qsTr("Restore") - function restore() - { - var state = projectModel.stateListModel.reloadStateFromFromProject(scenarioList.currentIndex) - restored(state) - loaded(state) + spacing: 0 + ScenarioButton { + id: restoreScenario + width: 100 + height: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/restoreIcon@2x.png" + onClicked: { + restore() + } + text: qsTr("Restore") + function restore() + { + var state = projectModel.stateListModel.reloadStateFromFromProject(scenarioList.currentIndex) + restored(state) + loaded(state) + } } - } - ScenarioButton { - id: saveScenario - text: qsTr("Save") - onClicked: { - projectModel.saveProjectFile() - saved(state) + ScenarioButton { + id: saveScenario + text: qsTr("Save") + onClicked: { + projectModel.saveProjectFile() + saved(state) + } + width: 100 + height: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/saveIcon@2x.png" } - Layout.preferredWidth: 100 - Layout.preferredHeight: 30 - buttonShortcut: "" - sourceImg: "qrc:/qml/img/saveIcon@2x.png" - } - ScenarioButton - { - id: duplicateScenario - text: qsTr("Duplicate") - onClicked: { - var state = JSON.parse(JSON.stringify(projectModel.stateListModel.getState(scenarioList.currentIndex))) - state.title = qsTr("Copy of ") + state.title; - projectModel.stateListModel.appendState(state) - projectModel.stateListModel.save() - duplicated(state) + ScenarioButton + { + id: duplicateScenario + text: qsTr("Duplicate") + onClicked: { + var state = JSON.parse(JSON.stringify(projectModel.stateListModel.getState(scenarioList.currentIndex))) + state.title = qsTr("Copy of ") + state.title; + projectModel.stateListModel.appendState(state) + projectModel.stateListModel.save() + duplicated(state) + } + width: 100 + height: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/duplicateIcon@2x.png" } - Layout.preferredWidth: 100 - Layout.preferredHeight: 30 - buttonShortcut: "" - sourceImg: "qrc:/qml/img/duplicateIcon@2x.png" } } From 950cded071128990dd022bfe22415525bd7826e3 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 9 Jun 2015 11:28:23 +0200 Subject: [PATCH 11/69] - add new icons - display tr name in debug Panel --- mix/MixClient.cpp | 1 - mix/qml/Block.qml | 33 ++++++--- mix/qml/BlockChain.qml | 16 ++--- mix/qml/Debugger.qml | 108 +++++++++++++++++------------ mix/qml/MainContent.qml | 4 ++ mix/qml/ScenarioExecution.qml | 1 + mix/qml/ScenarioLoader.qml | 11 ++- mix/qml/StateListModel.qml | 12 +++- mix/qml/img/duplicateIcon.png | Bin 1020 -> 0 bytes mix/qml/img/duplicateIcon@2x.png | Bin 2326 -> 0 bytes mix/qml/img/recycle-discard.png | Bin 723 -> 0 bytes mix/qml/img/recycle-discard@2x.png | Bin 1568 -> 0 bytes mix/qml/img/recycle-icon.png | Bin 697 -> 0 bytes mix/qml/img/recycle-icon@2x.png | Bin 1435 -> 0 bytes mix/qml/img/recycle-keep.png | Bin 904 -> 0 bytes mix/qml/img/recycle-keep@2x.png | Bin 2141 -> 0 bytes mix/qml/img/restoreIcon.png | Bin 1124 -> 0 bytes mix/qml/img/restoreIcon@2x.png | Bin 2447 -> 0 bytes mix/qml/img/saveIcon.png | Bin 1068 -> 0 bytes mix/qml/img/saveIcon@2x.png | Bin 2359 -> 0 bytes mix/res.qrc | 34 +++++---- 21 files changed, 137 insertions(+), 83 deletions(-) delete mode 100644 mix/qml/img/duplicateIcon.png delete mode 100644 mix/qml/img/duplicateIcon@2x.png delete mode 100644 mix/qml/img/recycle-discard.png delete mode 100644 mix/qml/img/recycle-discard@2x.png delete mode 100644 mix/qml/img/recycle-icon.png delete mode 100644 mix/qml/img/recycle-icon@2x.png delete mode 100644 mix/qml/img/recycle-keep.png delete mode 100644 mix/qml/img/recycle-keep@2x.png delete mode 100644 mix/qml/img/restoreIcon.png delete mode 100644 mix/qml/img/restoreIcon@2x.png delete mode 100644 mix/qml/img/saveIcon.png delete mode 100644 mix/qml/img/saveIcon@2x.png diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index f4b0c63a1..90b44b0e7 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -131,7 +131,6 @@ Transaction MixClient::replaceGas(Transaction const& _t, u256 const& _gas, Secre void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call, bool _gasAuto, Secret const& _secret) { -<<<<<<< HEAD Transaction t = _gasAuto ? replaceGas(_t, m_state.gasLimitRemaining()) : _t; // do debugging run first LastHashes lastHashes(256); diff --git a/mix/qml/Block.qml b/mix/qml/Block.qml index 0fa69f1bd..ba587e4a1 100644 --- a/mix/qml/Block.qml +++ b/mix/qml/Block.qml @@ -19,6 +19,7 @@ ColumnLayout property int trHeight: 30 spacing: 0 property int openedTr: 0 + property int blockIndex function calculateHeight() { @@ -117,7 +118,7 @@ ColumnLayout Image { id: saveStatusImage - source: "qrc:/qml/img/recycle-discard@2x.png" + source: "qrc:/qml/img/recyclediscard@2x.png" width: statusWidth fillMode: Image.PreserveAspectFit anchors.verticalCenter: parent.verticalCenter @@ -133,9 +134,9 @@ ColumnLayout onSaveStatusChanged: { if (saveStatus) - saveStatusImage.source = "qrc:/qml/img/recycle-keep@2x.png" + saveStatusImage.source = "qrc:/qml/img/recyclekeep@2x.png" else - saveStatusImage.source = "qrc:/qml/img/recycle-discard@2x.png" + saveStatusImage.source = "qrc:/qml/img/recyclediscard@2x.png" if (index >= 0) transactions.get(index).saveStatus = saveStatus @@ -245,14 +246,30 @@ ColumnLayout } } - Button + Rectangle { - id: debug Layout.preferredWidth: debugActionWidth - text: "debug" - onClicked: + Layout.preferredHeight: trHeight - 10 + color: "transparent" + Image { + source: "qrc:/qml/img/rightarrow@2x.png" + width: statusWidth + fillMode: Image.PreserveAspectFit + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + visible: transactions.get(index).recordIndex !== undefined + } + MouseArea { - clientModel.debugRecord(transactions.get(index).recordIndex); + anchors.fill: parent + onClicked: + { + if (transactions.get(index).recordIndex) + { + debugTrRequested = [ blockIndex, index ] + clientModel.debugRecord(transactions.get(index).recordIndex); + } + } } } } diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml index b73d9acbb..75073860f 100644 --- a/mix/qml/BlockChain.qml +++ b/mix/qml/BlockChain.qml @@ -16,6 +16,7 @@ ColumnLayout { property variant model spacing: 0 property int previousWidth + property variant debugTrRequested: [] onWidthChanged: { @@ -62,7 +63,7 @@ ColumnLayout { Layout.preferredHeight: 25 Image { id: debugImage - source: "qrc:/qml/img/recycle-icon@2x.png" + source: "qrc:/qml/img/recycleicon@2x.png" Layout.preferredWidth: statusWidth Layout.preferredHeight: 25 fillMode: Image.PreserveAspectFit @@ -95,7 +96,7 @@ ColumnLayout { } Label { - text: "Action" + text: "" Layout.preferredWidth: debugActionWidth } } @@ -128,7 +129,7 @@ ColumnLayout { { return calculateHeight() } - + blockIndex: index transactions: { if (index >= 0) @@ -260,7 +261,7 @@ ColumnLayout { Layout.preferredWidth: 100 Layout.preferredHeight: 30 buttonShortcut: "" - sourceImg: "qrc:/qml/img/recycle-icon@2x.png" + sourceImg: "qrc:/qml/img/recycleicon@2x.png" } ScenarioButton { @@ -278,7 +279,7 @@ ColumnLayout { Layout.preferredWidth: 100 Layout.preferredHeight: 30 buttonShortcut: "" - sourceImg: "qrc:/qml/img/recycle-icon@2x.png" + sourceImg: "qrc:/qml/img/sendtransactionicon@2x.png" } Timer @@ -324,7 +325,7 @@ ColumnLayout { Layout.preferredWidth: 100 Layout.preferredHeight: 30 buttonShortcut: "" - sourceImg: "qrc:/qml/img/recycle-icon@2x.png" + sourceImg: "qrc:/qml/img/addblock@2x.png" } Connections @@ -380,7 +381,6 @@ ColumnLayout { itemTr.isFunctionCall = itemTr.functionId !== "" itemTr.returned = _r.returned itemTr.value = QEtherHelper.createEther(_r.value, QEther.Wei) - console.log("sender " + _r.sender) itemTr.sender = _r.sender itemTr.recordIndex = _r.recordIndex itemTr.logs = _r.logs @@ -401,7 +401,7 @@ ColumnLayout { Layout.preferredWidth: 100 Layout.preferredHeight: 30 buttonShortcut: "" - sourceImg: "qrc:/qml/img/recycle-icon@2x.png" + sourceImg: "qrc:/qml/img/newaccounticon@2x.png" } } } diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index 7827931b0..3a0ed61a4 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -11,7 +11,7 @@ import "." Rectangle { id: debugPanel - property alias debugSlider: statesSlider + property alias debugSlider: statesSlider property alias solLocals: solLocals property alias solStorage: solStorage property alias solCallStack: solCallStack @@ -39,6 +39,11 @@ Rectangle { machineStates.updateHeight(); } + function setTr(tr) + { + trName.text = tr.label + } + function displayCompilationErrorIfAny() { debugScrollArea.visible = false; @@ -104,28 +109,67 @@ Rectangle { property alias solLocalsHeightSettings: solLocalsRect.height } - Splitter { + ColumnLayout { id: debugScrollArea anchors.fill: parent - orientation: Qt.Vertical + //orientation: Qt.Vertical + spacing: 0 + RowLayout + { + Layout.preferredWidth: parent.width + Layout.preferredHeight: 30 + Rectangle + { + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + color: "transparent" + Text { + anchors.centerIn: parent + text: qsTr("Current Transaction") + } + + Rectangle + { + anchors.left: parent.left + anchors.leftMargin: 10 + width: 30 + height: parent.height + color: "transparent" + anchors.verticalCenter: parent.verticalCenter + Image { + source: "qrc:/qml/img/leftarrow@2x.png" + width: parent.width + fillMode: Image.PreserveAspectFit + anchors.centerIn: parent + } + MouseArea + { + anchors.fill: parent + onClicked: + { + Debugger.init(null); + panelClosed() + } + } + } + } + } - /*TransactionLog { - id: transactionLog - Layout.fillWidth: true - Layout.minimumHeight: 130 - height: 250 - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: machineStates.sideMargin - anchors.rightMargin: machineStates.sideMargin - anchors.topMargin: machineStates.sideMargin - }*/ - - Button + RowLayout { - text: qsTr("close") - onClicked: panelClosed() + Layout.preferredWidth: parent.width + Layout.preferredHeight: 30 + Rectangle + { + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + color: "#2C79D3" + Text { + id: trName + color: "white" + anchors.centerIn: parent + } + } } ScrollView @@ -170,32 +214,6 @@ Rectangle { spacing: 3 layoutDirection: Qt.LeftToRight - /*StepActionImage - { - id: playAction - enabledStateImg: "qrc:/qml/img/play_button.png" - disableStateImg: "qrc:/qml/img/play_button.png" - buttonLeft: true - onClicked: projectModel.stateListModel.runState(transactionLog.selectedStateIndex) - width: 23 - buttonShortcut: "Ctrl+Shift+F8" - buttonTooltip: qsTr("Start Debugging") - visible: true - Layout.alignment: Qt.AlignLeft - } - - StepActionImage - { - id: pauseAction - enabledStateImg: "qrc:/qml/img/stop_button2x.png" - disableStateImg: "qrc:/qml/img/stop_button2x.png" - onClicked: Debugger.init(null); - width: 23 - buttonShortcut: "Ctrl+Shift+F9" - buttonTooltip: qsTr("Stop Debugging") - visible: true - }*/ - StepActionImage { id: runBackAction; diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 4af8ece46..21aed8af1 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -224,6 +224,10 @@ Rectangle { onDebugDataReady: { scenarioExe.visible = false debugPanel.visible = true + if (scenarioExe.bc.debugTrRequested) + { + debugPanel.setTr(scenarioExe.bc.model.blocks[scenarioExe.bc.debugTrRequested[0]].transactions[scenarioExe.bc.debugTrRequested[1]]) + } } } diff --git a/mix/qml/ScenarioExecution.qml b/mix/qml/ScenarioExecution.qml index d02e7d3f8..a5053e4d7 100644 --- a/mix/qml/ScenarioExecution.qml +++ b/mix/qml/ScenarioExecution.qml @@ -10,6 +10,7 @@ import "." Rectangle { color: "#ededed" + property alias bc: blockChain Connections { diff --git a/mix/qml/ScenarioLoader.qml b/mix/qml/ScenarioLoader.qml index 366813a7a..c659eb297 100644 --- a/mix/qml/ScenarioLoader.qml +++ b/mix/qml/ScenarioLoader.qml @@ -48,7 +48,7 @@ RowLayout width: 100 height: 30 buttonShortcut: "" - sourceImg: "qrc:/qml/img/restoreIcon@2x.png" + sourceImg: "qrc:/qml/img/restoreicon@2x.png" onClicked: { restore() } @@ -71,7 +71,7 @@ RowLayout width: 100 height: 30 buttonShortcut: "" - sourceImg: "qrc:/qml/img/saveIcon@2x.png" + sourceImg: "qrc:/qml/img/saveicon@2x.png" } ScenarioButton @@ -79,16 +79,13 @@ RowLayout id: duplicateScenario text: qsTr("Duplicate") onClicked: { - var state = JSON.parse(JSON.stringify(projectModel.stateListModel.getState(scenarioList.currentIndex))) - state.title = qsTr("Copy of ") + state.title; - projectModel.stateListModel.appendState(state) - projectModel.stateListModel.save() + projectModel.stateListModel.duplicateState(scenarioList.currentIndex) duplicated(state) } width: 100 height: 30 buttonShortcut: "" - sourceImg: "qrc:/qml/img/duplicateIcon@2x.png" + sourceImg: "qrc:/qml/img/duplicateicon@2x.png" } } } diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index dac3e0831..cb9688524 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -16,6 +16,7 @@ Item { property string defaultAccount: "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074" //support for old project function fromPlainStateItem(s) { + console.log("ggg " + s) if (!s.accounts) s.accounts = [stateListModel.newAccount("1000000", QEther.Ether, defaultAccount)]; //support for old project if (!s.contracts) @@ -242,6 +243,15 @@ Item { return { name: name, secret: _secret, balance: QEtherHelper.createEther(_balance, _unit), address: address }; } + function duplicateState(index) + { + var state = stateList[index] + var item = fromPlainStateItem(toPlainStateItem(state)) + item.title = qsTr("Copy of") + " " + state.title + appendState(item) + save() + } + function createEmptyBlock() { return { @@ -380,11 +390,9 @@ Item { function reloadStateFromFromProject(index) { - console.log(JSON.stringify(data)) if (data) { var item = fromPlainStateItem(data.states[index]) - stateListModel.set(index, item) stateList[index] = item return item diff --git a/mix/qml/img/duplicateIcon.png b/mix/qml/img/duplicateIcon.png deleted file mode 100644 index b3a2554208e3c2a942a9bfa9484a422c6d8a138e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1020 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 FV1gLqOIZK_ diff --git a/mix/qml/img/recycle-discard@2x.png b/mix/qml/img/recycle-discard@2x.png deleted file mode 100644 index c51c21d21199ede62c15ebb50e7635af58e1bfbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1568 zcmV+*2H*LKP)Px)+DSw~RA>e5S!-+*RTTct?6#Dp@~A+|u0gCyM1^z#K|~TUL@>oC0SQqO!!MMm zA%sXm(D+BA(I_MkA^L~J00}XuXh?iSssuyZ-4K5eQ3wHd1vP?DD5X2c?@pO^y4`Mf zw^J%QO=s`xoO93l=G=47y#oY)0RjO60RjO60RjO60RjO60RjO60RjO6*9L(mjGLCQ zqB|QAjpUm#ftm?PiGKP6VmIca>(7|gALdec6Bu*5CGv$pmV^Xf9>DObTyh#wJsA=@ z0Fi~|g3tG%dRQ*H{`OsS0Dy44h>S6cvz#?lDjN=>>L#xt^pH6r3GWxdIEN0HG8)!Y zFO6Sy@k>xKauSF>PvbTcfsPlTda_;d&~&7ZaO^Pxvjc((BU2UUY_%VmhIB(Qa}-KD zC;%Acl87y#k=yx{o3^U{DXLpkWE}wrb@U-^>y-u5aOSXS55(V~0bq=2Ms%Jf_?Vth znQa{3{LwaTpFb0)k7tkh*&b8?j3H)62#M?`2wgjZ>hTuM+&dem&-iIbcH)#1|DNe_Eta zk~bl?&=TE7nWB(Q361_3N9z0HCH3U!p=3{z!h1}VR+SC3@nkM8wWT>%e_wqH{hBlh zL&rR!jGpyT0wYKE#aGC7BVFPz*FMIHpS!Sz+`075yrzB zwl!ORDQ({Ya1;b)QOVKS3MpEhx0u~uNP7lSC%Jr9xU>vji@A$DEnd3LN9p7i>dnx0Dba_&dF8ZoeQ*gwu`LrR(K{e$A*chnVpOEing!it9RrxIati( z%kjEgwE6n{1ps=tJ6S6(m$c>&p}Ll5fE{5ge2{?N4_IZcOmBG)P9I}C>GTPPFg+;g z+kvl{AhWc;8C1p^rOHu0neKaL3DD*!*~d=gY5+0MMcx9x z^b^v1Z$oSqx6bv<@J4mxEx4gOd2_V1U~&(kuO49AZL3AfA7or|K72Uy@Jw(OWpk@j z#xFw7eZLtaGUsa%9WItmB1F6jEB<{D&|MeomX*$2CHxdWV;_?^TcI^><( zK(+#}a@pQVt!r7H=RLqsRFyM63}IK~XQWRtMr6D^=7WifzC|w@Z~A#rW`ZlP)Z^~S(6nb;i-?;+*0D%C30D%C30D%C30D=D<0{;O2V5305 SQ6=#J0000Px%b4f%&R7ef&mCb7tK^VrHY!bTEARbz%he$qbz$isL8bmSVON+(7!GlPVib%ne zs0S51cn}1^yP~Lw`7)A#ARpHZZ`f#Tqw(~-EDi)3=A{#=AGwx z-e+bfBO}8$bl`t?z_VEca=BdEDVNJj43CJ<(y#&33`V+WGSYS82ESLgsK}+DvF(I}*S}`^xz)&{ydiA~ZA5+O;gpIyq-?;o91`)!dN)Hc9|q zrnjhWFBZSD0yAO={(;b|th3^!G~!mHuyfr8NFMS8#3yUD+DA5s{@2!OwQh$(p)({2 z7brGdm#8ds1A|bUJ-VWcU?5pj*a>j-Xf&J6cd=OP8zAF=jsY4FM2w4E zH1Y1AW<4cH#7IBLbed5iXTDOY*#1kpeuxB#RpSUw;M7eVIdAnyS?$^KSR#=Ka0e!~ f9C!%$Pdo4n78mV*6YJYD00000NkvXXu0mjf;|oPn diff --git a/mix/qml/img/recycle-icon@2x.png b/mix/qml/img/recycle-icon@2x.png deleted file mode 100644 index 75b590b2adb7698c418a65899f13189bf8b3fa92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1435 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 diff --git a/mix/qml/img/restoreIcon@2x.png b/mix/qml/img/restoreIcon@2x.png deleted file mode 100644 index 3160b0bf348f7a9b2136ffa84c8eb11f19afbac2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2447 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 N002ovPDHLkV1ho6ksSa4 diff --git a/mix/qml/img/saveIcon.png b/mix/qml/img/saveIcon.png deleted file mode 100644 index 46e17522b27729f5d8ad767813183ce12f2bfe03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1068 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 diff --git a/mix/res.qrc b/mix/res.qrc index e84f77d84..b50aa3d9f 100644 --- a/mix/res.qrc +++ b/mix/res.qrc @@ -68,17 +68,27 @@ qml/img/warningicon.png qml/img/warningicon@2x.png qml/QAddressView.qml - qml/img/recycle-discard.png - qml/img/recycle-discard@2x.png - qml/img/recycle-icon.png - qml/img/recycle-icon@2x.png - qml/img/recycle-keep.png - qml/img/recycle-keep@2x.png - qml/img/restoreIcon.png - qml/img/restoreIcon@2x.png - qml/img/saveIcon.png - qml/img/saveIcon@2x.png - qml/img/duplicateIcon.png - qml/img/duplicateIcon@2x.png + qml/img/addblock.png + qml/img/addblock@2x.png + qml/img/duplicateicon.png + qml/img/duplicateicon@2x.png + qml/img/leftarrow.png + qml/img/leftarrow@2x.png + qml/img/newaccounticon.png + qml/img/newaccounticon@2x.png + qml/img/recyclediscard.png + qml/img/recyclediscard@2x.png + qml/img/recycleicon.png + qml/img/recycleicon@2x.png + qml/img/recyclekeep.png + qml/img/recyclekeep@2x.png + qml/img/restoreicon.png + qml/img/restoreicon@2x.png + qml/img/rightarrow.png + qml/img/rightarrow@2x.png + qml/img/saveicon.png + qml/img/saveicon@2x.png + qml/img/sendtransactionicon.png + qml/img/sendtransactionicon@2x.png From f78b6166d7a05fa6e01d6ad188456651f02e508d Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Tue, 9 Jun 2015 12:15:44 +0200 Subject: [PATCH 12/69] add eth(CLI) functionalities --- eth/main.cpp | 133 ++++++++++++++++++++++++++++++++++++++--- libdevcore/FixedHash.h | 2 +- libethereum/Client.h | 2 + 3 files changed, 129 insertions(+), 8 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index 2162776ea..66e3a7ed3 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -84,15 +84,24 @@ void interactiveHelp() << " minestop Stops mining." << endl << " mineforce Forces mining, even when there are no transactions." << endl << " block Gives the current block height." << endl + << " blockhashfromnumber Gives the block hash with the givne number." << endl + << " numberfromblockhash Gives the block number with the given hash." << endl + << " blockqueue Gives the current block queue status." << endl + << " findblock Searches for the block in the blockchain and blockqueue." << endl + << " firstunknown Gives the first unknown block from the blockqueue." << endl + << " retryunknown retries to import all unknown blocks from the blockqueue." << endl << " accounts Gives information on all owned accounts (balances, mining beneficiary and default signer)." << endl << " newaccount Creates a new account with the given name." << endl << " transact Execute a given transaction." << endl + << " txcreate Execute a given contract creation transaction." << endl << " send Execute a given transaction with current secret." << endl << " contract Create a new contract with current secret." << endl << " peers List the peers that are connected" << endl #if ETH_FATDB || !ETH_TRUE << " listaccounts List the accounts on the network." << endl << " listcontracts List the contracts on the network." << endl + << " balanceat
Gives the balance of the given account." << endl + << " codeat
Gives the code of the given account." << endl #endif << " setsigningkey Set the address with which to sign transactions." << endl << " setaddress Set the coinbase (mining payout) address." << endl @@ -961,9 +970,85 @@ int main(int argc, char** argv) cout << "Current mining beneficiary:" << endl << beneficiary << endl; cout << "Current signing account:" << endl << signingKey << endl; } + else if (c && cmd == "blockhashfromnumber") + { + if (iss.peek() != -1) + { + unsigned number; + iss >> number; + cout << " hash of block: " << c->hashFromNumber(number).hex() << endl; + } + } + else if (c && cmd == "numberfromblockhash") + { + if (iss.peek() != -1) + { + string stringHash; + iss >> stringHash; + + h256 hash = h256(fromHex(stringHash)); + cout << " number of block: " << c->numberFromHash(hash) << endl; + } + } else if (c && cmd == "block") { - cout << "Current block: " <blockChain().details().number << endl; + cout << "Current block: " << c->blockChain().details().number << endl; + } + else if (c && cmd == "blockqueue") + { + cout << "Current blockqueue status: " << endl << c->blockQueueStatus() << endl; + } + else if (c && cmd == "findblock") + { + if (iss.peek() != -1) + { + string stringHash; + iss >> stringHash; + + h256 hash = h256(fromHex(stringHash)); + + // search in blockchain + cout << "search in blockchain... " << endl; + try + { + cout << c->blockInfo(hash) << endl; + } + catch(Exception& _e) + { + cout << "block not in blockchain" << endl; + cout << boost::diagnostic_information(_e) << endl; + } + + cout << "search in blockqueue... " << endl; + + switch(c->blockQueue().blockStatus(hash)) + { + case QueueStatus::Ready: + cout << "Ready" << endl; + break; + case QueueStatus::UnknownParent: + cout << "UnknownParent" << endl; + break; + case QueueStatus::Bad: + cout << "Bad" << endl; + break; + case QueueStatus::Unknown: + cout << "Unknown" << endl; + break; + default: + cout << "invalid queueStatus" << endl; + } + } + else + cwarn << "Require parameter: findblock HASH"; + } + else if (c && cmd == "firstunknown") + { + cout << "first unknown blockhash: " << c->blockQueue().firstUnknown().hex() << endl; + } + else if (c && cmd == "retryunknown") + { + c->retryUnkonwn(); } else if (cmd == "peers") { @@ -1077,7 +1162,7 @@ int main(int argc, char** argv) else cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA"; } - else if (c && cmd == "cretract") + else if (c && cmd == "txcreate") { auto const& bc =c->blockChain(); auto h = bc.currentHash(); @@ -1119,10 +1204,7 @@ int main(int argc, char** argv) try { Secret secret = h256(fromHex(sechex)); - c->submitTransaction(secret, amount, data, gas, gasPrice); - - //Address dest = h160(fromHex(hexAddr)); - //c->submitTransaction(secret, amount, dest, data, gas, gasPrice); + cout << " new contract address : " << c->submitTransaction(secret, amount, data, gas, gasPrice) << endl; } catch (BadHexCharacter& _e) { @@ -1136,7 +1218,7 @@ int main(int argc, char** argv) } } else - cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA"; + cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET INIT"; } #if ETH_FATDB else if (c && cmd == "listcontracts") @@ -1161,6 +1243,43 @@ int main(int argc, char** argv) cout << ss << endl; } } + else if (c && cmd == "balanceat") + { + if (iss.peek() != -1) + { + string stringHash; + iss >> stringHash; + + Address address = h160(fromHex(stringHash)); + + cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address)) << endl; + } + } + // TODO implement << operator for std::unorderd_map +// else if (c && cmd == "storageat") +// { +// if (iss.peek() != -1) +// { +// string stringHash; +// iss >> stringHash; + +// Address address = h160(fromHex(stringHash)); + +// cout << "storage at " << stringHash << " is: " << c->storageAt(address) << endl; +// } +// } + else if (c && cmd == "codeat") + { + if (iss.peek() != -1) + { + string stringHash; + iss >> stringHash; + + Address address = h160(fromHex(stringHash)); + + cout << "code at " << stringHash << " is: " << toHex(c->codeAt(address)) << endl; + } + } #endif else if (c && cmd == "send") { diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 9b25837af..88bc0fe95 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -113,7 +113,7 @@ public: /// @returns an abridged version of the hash as a user-readable hex string. std::string abridged() const { return toHex(ref().cropped(0, 4)) + "\342\200\246"; } - /// @returns an abridged version of the hash as a user-readable hex string. + /// @returns the hash as a user-readable hex string. std::string hex() const { return toHex(ref()); } /// @returns a mutable byte vector_ref to the object's data. diff --git a/libethereum/Client.h b/libethereum/Client.h index 5132b5a30..90200c20b 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -156,6 +156,8 @@ public: CanonBlockChain const& blockChain() const { return m_bc; } /// Get some information on the block queue. BlockQueueStatus blockQueueStatus() const { return m_bq.status(); } + /// Get the block queue. + BlockQueue const& blockQueue() const { return m_bq; } // Mining stuff: From af9c64da917ff7b65f36faf4bfd298a0cb339b7e Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Tue, 9 Jun 2015 14:10:18 +0200 Subject: [PATCH 13/69] add queuestatus::importing --- eth/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/eth/main.cpp b/eth/main.cpp index 66e3a7ed3..8b3e1fa78 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -1026,6 +1026,9 @@ int main(int argc, char** argv) case QueueStatus::Ready: cout << "Ready" << endl; break; + case QueueStatus::Importing: + cout << "Importing" << endl; + break; case QueueStatus::UnknownParent: cout << "UnknownParent" << endl; break; From 2e639c198a3aa3729377085191587ef354761176 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 9 Jun 2015 21:06:39 +0200 Subject: [PATCH 14/69] misc changes --- mix/ClientModel.cpp | 63 ++++++++++++++++----------- mix/ClientModel.h | 6 ++- mix/qml/Block.qml | 35 ++++++++++++++- mix/qml/BlockChain.qml | 73 +++++++++++++++++++++++-------- mix/qml/MainContent.qml | 2 +- mix/qml/ScenarioButton.qml | 2 + mix/qml/ScenarioLoader.qml | 88 +++++++++++++++++++++++++++++++++++++- 7 files changed, 220 insertions(+), 49 deletions(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index bdb8a0d55..c834ca5c7 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -83,14 +83,11 @@ ClientModel::ClientModel(): qRegisterMetaType("QCallData"); qRegisterMetaType("RecordLogEntry*"); - //connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection); m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString())); m_ethAccounts = make_shared([=](){return m_client.get();}, std::vector()); m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), m_ethAccounts, std::vector(), m_client.get())); connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection); - - } ClientModel::~ClientModel() @@ -211,8 +208,8 @@ QVariantList ClientModel::gasCosts() const void ClientModel::setupScenario(QVariantMap _scenario) { + onStateReset(); WriteGuard(x_queueTransactions); - m_queueTransactions.clear(); m_running = true; m_currentScenario = _scenario; @@ -220,7 +217,7 @@ void ClientModel::setupScenario(QVariantMap _scenario) QVariantList stateAccounts = _scenario.value("accounts").toList(); m_accounts.clear(); - std::vector userAccounts; + m_accountsSecret.clear(); for (auto const& b: stateAccounts) { QVariantMap account = b.toMap(); @@ -228,7 +225,7 @@ void ClientModel::setupScenario(QVariantMap _scenario) if (account.contains("secret")) { KeyPair key(Secret(account.value("secret").toString().toStdString())); - userAccounts.push_back(key); + m_accountsSecret.push_back(key); address = key.address(); } else if (account.contains("address")) @@ -238,7 +235,7 @@ void ClientModel::setupScenario(QVariantMap _scenario) m_accounts[address] = Account(qvariant_cast(account.value("balance"))->toU256Wei(), Account::NormalCreation); } - m_ethAccounts->setAccounts(userAccounts); + m_ethAccounts->setAccounts(m_accountsSecret); bool trToExecute = false; for (auto const& b: blocks) @@ -250,15 +247,20 @@ void ClientModel::setupScenario(QVariantMap _scenario) m_client->resetState(m_accounts, Secret(m_currentScenario.value("miner").toMap().value("secret").toString().toStdString())); if (m_queueTransactions.count() > 0 && trToExecute) { - connect(this, &ClientModel::newBlock, this, &ClientModel::processNextTransactions, Qt::QueuedConnection); - connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection); - connect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock, Qt::QueuedConnection); + setupExecutionChain(); processNextTransactions(); } else m_running = false; } +void ClientModel::setupExecutionChain() +{ + connect(this, &ClientModel::newBlock, this, &ClientModel::processNextTransactions, Qt::QueuedConnection); + connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection); + connect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock, Qt::QueuedConnection); +} + void ClientModel::stopExecution() { disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextTransactions); @@ -302,7 +304,8 @@ void ClientModel::processNextTransactions() bool isFunctionCall = transaction.value("isFunctionCall").toBool(); if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later contractId = m_codeModel->contracts().keys()[0]; - TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()), isContractCreation, isFunctionCall); + Secret f = Secret(sender.toStdString()); + TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, f, isContractCreation, isFunctionCall); transactionSettings.parameterValues = transaction.value("parameters").toMap(); if (contractId == functionId || functionId == "Constructor") @@ -326,12 +329,11 @@ void ClientModel::executeSequence(vector const& _sequence) { try { - vector
deployedContracts; m_gasCosts.clear(); for (TransactionSettings const& transaction: _sequence) { std::pair ctrInstance = resolvePair(transaction.contractId); - QString address = resolveToken(ctrInstance, deployedContracts); + QString address = resolveToken(ctrInstance); if (!transaction.isFunctionCall) { callAddress(Address(address.toStdString()), bytes(), transaction); @@ -364,7 +366,7 @@ void ClientModel::executeSequence(vector const& _sequence) if (type->type().type == SolidityType::Type::Address && value.toString().startsWith("<")) { std::pair ctrParamInstance = resolvePair(value.toString()); - value = QVariant(resolveToken(ctrParamInstance, deployedContracts)); + value = QVariant(resolveToken(ctrParamInstance)); } encoder.encode(value, type->type()); } @@ -374,8 +376,7 @@ void ClientModel::executeSequence(vector const& _sequence) bytes param = encoder.encodedData(); contractCode.insert(contractCode.end(), param.begin(), param.end()); Address newAddress = deployContract(contractCode, transaction); - deployedContracts.push_back(newAddress); - std::pair contractToken = retrieveToken(transaction.contractId, deployedContracts); + std::pair contractToken = retrieveToken(transaction.contractId); m_contractAddresses[contractToken] = newAddress; m_contractNames[newAddress] = contractToken.first; contractAddressesChanged(); @@ -416,7 +417,7 @@ void ClientModel::executeTr(QVariantMap _tr) if (!m_running) { m_running = true; - connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection); + setupExecutionChain(); processNextTransactions(); } } @@ -433,19 +434,19 @@ std::pair ClientModel::resolvePair(QString const& _contractId) return ret; } -QString ClientModel::resolveToken(std::pair const& _value, vector
const& _contracts) +QString ClientModel::resolveToken(std::pair const& _value) { - if (_contracts.size() > 0) - return QString::fromStdString("0x" + dev::toHex(m_contractAddresses[_value].ref())); //dev::toHex(_contracts.at(_value.second).ref())); + if (m_contractAddresses.size() > 0) + return QString::fromStdString("0x" + dev::toHex(m_contractAddresses[_value].ref())); else return _value.first; } -std::pair ClientModel::retrieveToken(QString const& _value, vector
const& _contracts) +std::pair ClientModel::retrieveToken(QString const& _value) { std::pair ret; ret.first = _value; - ret.second = _contracts.size() - 1; + ret.second = m_contractAddresses.size(); return ret; } @@ -679,6 +680,7 @@ void ClientModel::onStateReset() m_contractNames.clear(); m_stdContractAddresses.clear(); m_stdContractNames.clear(); + m_queueTransactions.clear(); emit stateCleared(); } @@ -801,8 +803,21 @@ void ClientModel::onNewTransaction() logs.push_back(l); } } - QString sender = QString::fromStdString(dev::toHex(tr.sender.ref())); - QString label = contract + "." + function + "()"; + + QString sender; + for (auto const& secret: m_accountsSecret) + { + if (secret.address() == tr.sender) + { + sender = QString::fromStdString(dev::toHex(secret.secret().ref())); + break; + } + } + QString label; + if (function != QObject::tr("")) + label = contract + "." + function + "()"; + else + label = contract; RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction, gasUsed, sender, label, inputParameters, logs); QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership); diff --git a/mix/ClientModel.h b/mix/ClientModel.h index ed178cfe4..c062aa4a7 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -251,13 +251,14 @@ private: void onStateReset(); void showDebuggerForTransaction(ExecutionResult const& _t); QVariant formatValue(SolidityType const& _type, dev::u256 const& _value); - QString resolveToken(std::pair const& _value, std::vector
const& _contracts); - std::pair retrieveToken(QString const& _value, std::vector
const& _contracts); + QString resolveToken(std::pair const& _value); + std::pair retrieveToken(QString const& _value); std::pair resolvePair(QString const& _contractId); QVariant formatStorageValue(SolidityType const& _type, std::unordered_map const& _storage, unsigned _offset, dev::u256 const& _slot); void processNextTransactions(); void finalizeBlock(); void stopExecution(); + void setupExecutionChain(); std::atomic m_running; std::atomic m_mining; @@ -267,6 +268,7 @@ private: std::unique_ptr m_web3Server; std::shared_ptr m_ethAccounts; std::unordered_map m_accounts; + std::vector m_accountsSecret; QList m_gasCosts; std::map, Address> m_contractAddresses; std::map m_contractNames; diff --git a/mix/qml/Block.qml b/mix/qml/Block.qml index ba587e4a1..b56b1f4d0 100644 --- a/mix/qml/Block.qml +++ b/mix/qml/Block.qml @@ -20,6 +20,7 @@ ColumnLayout spacing: 0 property int openedTr: 0 property int blockIndex + property variant scenario function calculateHeight() { @@ -201,7 +202,12 @@ ColumnLayout function userFrienldyToken(value) { if (value && value.indexOf("<") === 0) - return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()"; + { + if (value.split("> ")[1] === " - ") + return value.split(" - ")[0].replace("<", "") + else + return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()"; + } else return value } @@ -251,7 +257,34 @@ ColumnLayout Layout.preferredWidth: debugActionWidth Layout.preferredHeight: trHeight - 10 color: "transparent" + + Image { + source: "qrc:/qml/img/edit.png" + width: 18 + fillMode: Image.PreserveAspectFit + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + MouseArea + { + anchors.fill: parent + onClicked: + { + transactionDialog.stateAccounts = scenario.accounts + transactionDialog.execute = false + transactionDialog.open(index, blockIndex, transactions.get(index)) + } + } + } + + Rectangle + { + Layout.preferredWidth: debugActionWidth + Layout.preferredHeight: trHeight - 10 + color: "transparent" + Image { + id: debugImg source: "qrc:/qml/img/rightarrow@2x.png" width: statusWidth fillMode: Image.PreserveAspectFit diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml index 75073860f..5efdf8a60 100644 --- a/mix/qml/BlockChain.qml +++ b/mix/qml/BlockChain.qml @@ -17,6 +17,12 @@ ColumnLayout { spacing: 0 property int previousWidth property variant debugTrRequested: [] + signal chainChanged + + onChainChanged: { + reBuildNeeded.start() + } + onWidthChanged: { @@ -40,6 +46,8 @@ ColumnLayout { { if (!scenario) return; + if (model) + chainChanged() model = scenario blockModel.clear() for (var b in model.blocks) @@ -124,6 +132,7 @@ ColumnLayout { model: blockModel Block { + scenario: blockChainPanel.model Layout.preferredWidth: blockChainScrollView.width Layout.preferredHeight: { @@ -220,6 +229,7 @@ ColumnLayout { { if (ensureNotFuturetime.running) return; + reBuildNeeded.stop() var retBlocks = []; for (var j = 0; j < model.blocks.length; j++) { @@ -233,26 +243,18 @@ ColumnLayout { for (var k = 0; k < model.blocks[j].transactions.length; k++) { if (blockModel.get(j).transactions.get(k).saveStatus) - { block.transactions.push(model.blocks[j].transactions[k]); - } } if (block.transactions.length > 0) - { retBlocks.push(block) - } } if (retBlocks.length === 0) - { retBlocks.push(projectModel.stateListModel.createEmptyBlock()) - } model.blocks = retBlocks blockModel.clear() for (var j = 0; j < model.blocks.length; j++) - { blockModel.append(model.blocks[j]) - } ensureNotFuturetime.start() clientModel.setupScenario(model); @@ -262,6 +264,23 @@ ColumnLayout { Layout.preferredHeight: 30 buttonShortcut: "" sourceImg: "qrc:/qml/img/recycleicon@2x.png" + Timer + { + id: reBuildNeeded + repeat: true + interval: 1000 + running: false + onTriggered: { + if (!parent.fillColor || parent.fillColor === "white") + parent.fillColor = "orange" + else + parent.fillColor = "white" + } + onRunningChanged: { + if (!running) + parent.fillColor = "white" + } + } } ScenarioButton { @@ -274,6 +293,7 @@ ColumnLayout { model.blocks.push(projectModel.stateListModel.createEmptyBlock()); var item = TransactionHelper.defaultTransaction() transactionDialog.stateAccounts = model.accounts + transactionDialog.execute = true transactionDialog.open(model.blocks[model.blocks.length - 1].transactions.length, model.blocks.length - 1, item) } Layout.preferredWidth: 100 @@ -299,7 +319,6 @@ ColumnLayout { return if (clientModel.mining || clientModel.running) return - console.log("lllll"); if (model.blocks.length > 0) { var lastBlock = model.blocks[model.blocks.length - 1] @@ -351,25 +370,30 @@ ColumnLayout { { var blockIndex = parseInt(_r.transactionIndex.split(":")[0]) - 1 var trIndex = parseInt(_r.transactionIndex.split(":")[1]) - if (blockIndex <= model.blocks.length) + console.log("kkkk" + blockIndex) + console.log(trIndex) + if (blockIndex <= model.blocks.length - 1) { var item = model.blocks[blockIndex] - if (trIndex <= item.transactions.length) + if (trIndex <= item.transactions.length - 1) { var tr = item.transactions[trIndex] + console.log(JSON.stringify(_r)) tr.returned = _r.returned tr.recordIndex = _r.recordIndex tr.logs = _r.logs + tr.sender = _r.sender var trModel = blockModel.getTransaction(blockIndex, trIndex) trModel.returned = _r.returned trModel.recordIndex = _r.recordIndex trModel.logs = _r.logs + trModel.sender = _r.sender blockModel.setTransaction(blockIndex, trIndex, trModel) return; } } - // tr is not in the list. coming from JavaScript + // tr is not in the list. var itemTr = TransactionHelper.defaultTransaction() itemTr.saveStatus = false itemTr.functionId = _r.function @@ -397,6 +421,7 @@ ColumnLayout { text: qsTr("New Account") onClicked: { model.accounts.push(projectModel.stateListModel.newAccount("1000000", QEther.Ether)) + chainChanged() } Layout.preferredWidth: 100 Layout.preferredHeight: 30 @@ -408,15 +433,25 @@ ColumnLayout { TransactionDialog { id: transactionDialog + property bool execute onAccepted: { var item = transactionDialog.getItem() - var lastBlock = model.blocks[model.blocks.length - 1]; - if (lastBlock.status === "mined") - model.blocks.push(projectModel.stateListModel.createEmptyBlock()); - model.blocks[model.blocks.length - 1].transactions.push(item) - blockModel.appendTransaction(item) - if (!clientModel.running) - clientModel.executeTr(item) + if (execute) + { + var lastBlock = model.blocks[model.blocks.length - 1]; + if (lastBlock.status === "mined") + model.blocks.push(projectModel.stateListModel.createEmptyBlock()); + model.blocks[model.blocks.length - 1].transactions.push(item) + blockModel.appendTransaction(item) + if (!clientModel.running) + clientModel.executeTr(item) + } + else { + model.blocks[blockIndex].transactions[transactionIndex] = item + blockModel.setTransaction(blockIndex, transactionIndex, item) + chainChanged() + } + } } } diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 21aed8af1..22aa78f38 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -234,8 +234,8 @@ Rectangle { Connections { target: debugPanel onPanelClosed: { - scenarioExe.visible = true debugPanel.visible = false + scenarioExe.visible = true } } } diff --git a/mix/qml/ScenarioButton.qml b/mix/qml/ScenarioButton.qml index a2ccf3f78..dbe6dd13a 100644 --- a/mix/qml/ScenarioButton.qml +++ b/mix/qml/ScenarioButton.qml @@ -8,6 +8,7 @@ Rectangle { property string text property string buttonShortcut property string sourceImg + property string fillColor signal clicked Rectangle { @@ -16,6 +17,7 @@ Rectangle { border.color: "#cccccc" border.width: 1 radius: 4 + color: parent.fillColor ? parent.fillColor : "white" Image { id: debugImage anchors { diff --git a/mix/qml/ScenarioLoader.qml b/mix/qml/ScenarioLoader.qml index c659eb297..50e958a5c 100644 --- a/mix/qml/ScenarioLoader.qml +++ b/mix/qml/ScenarioLoader.qml @@ -1,8 +1,10 @@ import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 -import QtQuick.Dialogs 1.1 import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.2 +import QtQuick.Window 2.0 +import QtQuick.Dialogs 1.1 import Qt.labs.settings 1.0 import "js/Debugger.js" as Debugger import "js/ErrorLocationFormater.js" as ErrorLocationFormater @@ -21,6 +23,75 @@ RowLayout } id: blockChainSelector + + Dialog { + id: newStateWin + modality: Qt.ApplicationModal + title: qsTr("New Project"); + + width: 320 + height: 120 + + visible: false + + contentItem: Rectangle { + anchors.fill: parent + anchors.margins: 10 + RowLayout { + anchors.verticalCenter: parent.verticalCenter + Text { + text: qsTr("Name:") + } + + Rectangle + { + Layout.preferredWidth: 250 + Layout.preferredHeight: parent.height + border.width: 1 + border.color: "#cccccc" + TextInput + { + anchors.fill: parent + id: stateName + } + } + } + RowLayout + { + anchors.bottom: parent.bottom + anchors.right: parent.right; + function acceptAndClose() + { + var item = projectModel.stateListModel.createDefaultState(); + item.title = stateName.text + projectModel.stateListModel.appendState(item) + projectModel.stateListModel.save() + close() + scenarioList.currentIndex = projectModel.stateListModel.count - 1 + } + + function close() + { + newStateWin.close() + stateName.text = "" + } + + Button { + id: okButton; + enabled: stateName.text !== "" + text: qsTr("OK"); + onClicked: { + parent.acceptAndClose(); + } + } + Button { + text: qsTr("Cancel"); + onClicked: parent.close(); + } + } + } + } + ComboBox { id: scenarioList @@ -40,7 +111,7 @@ RowLayout Row { - Layout.preferredWidth: 100 * 3 + Layout.preferredWidth: 100 * 4 Layout.preferredHeight: 30 spacing: 0 ScenarioButton { @@ -87,5 +158,18 @@ RowLayout buttonShortcut: "" sourceImg: "qrc:/qml/img/duplicateicon@2x.png" } + + ScenarioButton { + id: addScenario + width: 100 + height: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/plus.png" + onClicked: { + newStateWin.open() + } + text: qsTr("New") + } } + } From a96ed0da181d4a4afe618c8a543fdb4b49a00d2c Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 10 Jun 2015 09:40:39 +0200 Subject: [PATCH 15/69] - bug fixes - support for old projects --- mix/ClientModel.cpp | 19 ++++++++++++++- mix/qml/Block.qml | 2 +- mix/qml/BlockChain.qml | 45 ++++++++++++++++++++++++++++------- mix/qml/StateListModel.qml | 34 +++++++++++++++++++------- mix/qml/TransactionDialog.qml | 2 +- 5 files changed, 81 insertions(+), 21 deletions(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index c834ca5c7..a5936d910 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -431,12 +431,16 @@ std::pair ClientModel::resolvePair(QString const& _contractId) QStringList values = ret.first.remove("<").remove(">").split(" - "); ret = std::make_pair(values[0], values[1].toUInt()); } + if (_contractId.startsWith("0x")) + ret = std::make_pair(_contractId, -2); return ret; } QString ClientModel::resolveToken(std::pair const& _value) { - if (m_contractAddresses.size() > 0) + if (_value.second == -2) + return _value.first; + else if (m_contractAddresses.size() > 0) return QString::fromStdString("0x" + dev::toHex(m_contractAddresses[_value].ref())); else return _value.first; @@ -737,6 +741,8 @@ void ClientModel::onNewTransaction() CompiledContract const& compilerRes = m_codeModel->contract(contractAddressIter->second); const QContractDefinition* def = compilerRes.contract(); contract = def->name(); + if (creation) + function = contract; if (abi) { QFunctionDefinition const* funcDef = def->getFunction(functionHash); @@ -818,6 +824,17 @@ void ClientModel::onNewTransaction() label = contract + "." + function + "()"; else label = contract; + + if (!creation) + for (auto const& ctr: m_contractAddresses) + { + if (ctr.second == tr.address) + { + contract = "<" + ctr.first.first + " - " + QString::number(ctr.first.second) + ">"; + break; + } + } + RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction, gasUsed, sender, label, inputParameters, logs); QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership); diff --git a/mix/qml/Block.qml b/mix/qml/Block.qml index b56b1f4d0..576cc5215 100644 --- a/mix/qml/Block.qml +++ b/mix/qml/Block.qml @@ -297,7 +297,7 @@ ColumnLayout anchors.fill: parent onClicked: { - if (transactions.get(index).recordIndex) + if (transactions.get(index).recordIndex !== undefined) { debugTrRequested = [ blockIndex, index ] clientModel.debugRecord(transactions.get(index).recordIndex); diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml index 5efdf8a60..ea5a09cb5 100644 --- a/mix/qml/BlockChain.qml +++ b/mix/qml/BlockChain.qml @@ -230,7 +230,9 @@ ColumnLayout { if (ensureNotFuturetime.running) return; reBuildNeeded.stop() + console.log("model block length " + model.blocks.length) var retBlocks = []; + var bAdded = 0; for (var j = 0; j < model.blocks.length; j++) { var b = model.blocks[j]; @@ -243,13 +245,30 @@ ColumnLayout { for (var k = 0; k < model.blocks[j].transactions.length; k++) { if (blockModel.get(j).transactions.get(k).saveStatus) - block.transactions.push(model.blocks[j].transactions[k]); + { + var tr = model.blocks[j].transactions[k] + tr.saveStatus = true + block.transactions.push(tr); + } + } if (block.transactions.length > 0) + { + bAdded++ + block.number = bAdded + block.status = "mined" retBlocks.push(block) + } + } if (retBlocks.length === 0) retBlocks.push(projectModel.stateListModel.createEmptyBlock()) + else + { + var last = retBlocks[retBlocks.length - 1] + last.number = -1 + last.status = "pending" + } model.blocks = retBlocks blockModel.clear() @@ -289,8 +308,14 @@ ColumnLayout { onClicked: { var lastBlock = model.blocks[model.blocks.length - 1]; + console.log(lastBlock.status + "!!!") if (lastBlock.status === "mined") - model.blocks.push(projectModel.stateListModel.createEmptyBlock()); + { + var newblock = projectModel.stateListModel.createEmptyBlock() + blockModel.appendBlock(newblock) + model.blocks.push(newblock); + } + var item = TransactionHelper.defaultTransaction() transactionDialog.stateAccounts = model.accounts transactionDialog.execute = true @@ -352,6 +377,7 @@ ColumnLayout { target: clientModel onNewBlock: { + console.log("new Block!!") if (!clientModel.running) { var lastBlock = model.blocks[model.blocks.length - 1] @@ -370,15 +396,12 @@ ColumnLayout { { var blockIndex = parseInt(_r.transactionIndex.split(":")[0]) - 1 var trIndex = parseInt(_r.transactionIndex.split(":")[1]) - console.log("kkkk" + blockIndex) - console.log(trIndex) if (blockIndex <= model.blocks.length - 1) { var item = model.blocks[blockIndex] if (trIndex <= item.transactions.length - 1) { var tr = item.transactions[trIndex] - console.log(JSON.stringify(_r)) tr.returned = _r.returned tr.recordIndex = _r.recordIndex tr.logs = _r.logs @@ -408,6 +431,7 @@ ColumnLayout { itemTr.sender = _r.sender itemTr.recordIndex = _r.recordIndex itemTr.logs = _r.logs + console.log(JSON.stringify(itemTr)) model.blocks[model.blocks.length - 1].transactions.push(itemTr) blockModel.appendTransaction(itemTr) } @@ -421,7 +445,6 @@ ColumnLayout { text: qsTr("New Account") onClicked: { model.accounts.push(projectModel.stateListModel.newAccount("1000000", QEther.Ether)) - chainChanged() } Layout.preferredWidth: 100 Layout.preferredHeight: 30 @@ -438,11 +461,15 @@ ColumnLayout { var item = transactionDialog.getItem() if (execute) { + console.log("cc " + model.blocks.length) var lastBlock = model.blocks[model.blocks.length - 1]; + console.log("mined?" + lastBlock.status) if (lastBlock.status === "mined") - model.blocks.push(projectModel.stateListModel.createEmptyBlock()); - model.blocks[model.blocks.length - 1].transactions.push(item) - blockModel.appendTransaction(item) + { + var newBlock = projectModel.stateListModel.createEmptyBlock(); + model.blocks.push(newBlock); + blockModel.appendBlock(newBlock) + } if (!clientModel.running) clientModel.executeTr(item) } diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index cb9688524..f056acc22 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -16,19 +16,32 @@ Item { property string defaultAccount: "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074" //support for old project function fromPlainStateItem(s) { - console.log("ggg " + s) if (!s.accounts) s.accounts = [stateListModel.newAccount("1000000", QEther.Ether, defaultAccount)]; //support for old project if (!s.contracts) s.contracts = []; - return { - title: s.title, - transactions: s.transactions.filter(function(t) { return !t.stdContract; }).map(fromPlainTransactionItem), //support old projects by filtering std contracts - blocks: s.blocks.map(fromPlainBlockItem), - accounts: s.accounts.map(fromPlainAccountItem), - contracts: s.contracts.map(fromPlainAccountItem), - miner: s.miner - }; + + var ret = {}; + ret.title = s.title; + ret.transactions = s.transactions.filter(function(t) { return !t.stdContract; }).map(fromPlainTransactionItem); //support old projects by filtering std contracts + if (s.blocks) + ret.blocks = s.blocks.map(fromPlainBlockItem); + ret.accounts = s.accounts.map(fromPlainAccountItem); + ret.contracts = s.contracts.map(fromPlainAccountItem); + ret.miner = s.miner; + + // support old projects + if (!ret.blocks) + { + ret.blocks = [{ + hash: "", + number: -1, + transactions: [], + status: "pending" + }] + for (var j in ret.transactions) + ret.blocks[0].transactions.push(fromPlainTransactionItem(toPlainTransactionItem(ret.transactions[j]))) + } } function fromPlainAccountItem(t) @@ -64,6 +77,9 @@ Item { saveStatus: t.saveStatus }; + if (r.saveStatus === undefined) + r.saveStatus = true + if (r.isFunctionCall === undefined) r.isFunctionCall = true; diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index b53c7c787..56810b375 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -64,7 +64,7 @@ Dialog { for (var c in contracts) { contractsModel.append({ cid: c, text: contracts[c].contract.name }); if (contracts[c].contract.name === contractId) - contractIndex = contractsModel.count - 1; + contractIndex = contractsModel.count - 1; } if (contractIndex == -1 && contractsModel.count > 0) From ec62858be10594e2d3220e2455561e70355abea2 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 10 Jun 2015 10:57:37 +0200 Subject: [PATCH 16/69] cosmetic changes --- mix/ClientModel.cpp | 2 +- mix/ClientModel.h | 57 +- mix/ContractCallDataEncoder.cpp | 2 +- mix/ContractCallDataEncoder.h | 2 +- mix/MachineStates.h | 86 +-- mix/MixClient.cpp | 55 +- mix/MixClient.h | 5 +- mix/QContractDefinition.cpp | 2 +- mix/main.cpp | 4 +- mix/qml/Application.qml | 10 +- mix/qml/Block.qml | 614 ++++++++++----------- mix/qml/BlockChain.qml | 931 ++++++++++++++++---------------- mix/qml/Debugger.qml | 144 ++--- mix/qml/MainContent.qml | 108 ++-- mix/qml/QAddressView.qml | 3 - mix/qml/ScenarioButton.qml | 112 ++-- mix/qml/ScenarioExecution.qml | 78 +-- mix/qml/ScenarioLoader.qml | 318 +++++------ mix/qml/StateListModel.qml | 241 +++++---- mix/qml/StructView.qml | 2 +- mix/qml/TransactionDialog.qml | 20 +- mix/qml/js/ProjectModel.js | 110 ++-- mix/qml/js/TransactionHelper.js | 4 +- 23 files changed, 1422 insertions(+), 1488 deletions(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 9c3ab5832..1da664fbe 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -438,7 +438,7 @@ std::pair ClientModel::resolvePair(QString const& _contractId) QString ClientModel::resolveToken(std::pair const& _value) { - if (_value.second == -2) + if (_value.second == -2) //-2: first contains a real address return _value.first; else if (m_contractAddresses.size() > 0) return QString::fromStdString("0x" + dev::toHex(m_contractAddresses[_value].ref())); diff --git a/mix/ClientModel.h b/mix/ClientModel.h index c062aa4a7..32348b603 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -109,14 +109,14 @@ class RecordLogEntry: public QObject Q_PROPERTY(RecordType type MEMBER m_type CONSTANT) /// Gas used Q_PROPERTY(QString gasUsed MEMBER m_gasUsed CONSTANT) - /// Sender - Q_PROPERTY(QString sender MEMBER m_sender CONSTANT) - /// label - Q_PROPERTY(QString label MEMBER m_label CONSTANT) - /// input parameters - Q_PROPERTY(QVariantMap parameters MEMBER m_inputParameters CONSTANT) - /// logs - Q_PROPERTY(QVariantList logs MEMBER m_logs CONSTANT) + /// Sender + Q_PROPERTY(QString sender MEMBER m_sender CONSTANT) + /// label + Q_PROPERTY(QString label MEMBER m_label CONSTANT) + /// input parameters + Q_PROPERTY(QVariantMap parameters MEMBER m_inputParameters CONSTANT) + /// logs + Q_PROPERTY(QVariantList logs MEMBER m_logs CONSTANT) public: enum RecordType @@ -127,10 +127,10 @@ public: RecordLogEntry(): m_recordIndex(0), m_call(false), m_type(RecordType::Transaction) {} - RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed, - QString _sender, QString _label, QVariantMap _inputParameters, QVariantList _logs): - m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed), - m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters), m_logs(_logs) {} + RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed, + QString _sender, QString _label, QVariantMap _inputParameters, QVariantList _logs): + m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed), + m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters), m_logs(_logs) {} private: unsigned m_recordIndex; @@ -143,10 +143,10 @@ private: bool m_call; RecordType m_type; QString m_gasUsed; - QString m_sender; - QString m_label; - QVariantMap m_inputParameters; - QVariantList m_logs; + QString m_sender; + QString m_label; + QVariantMap m_inputParameters; + QVariantList m_logs; }; /** @@ -185,14 +185,11 @@ public: Q_INVOKABLE QString toHex(QString const& _int); public slots: - /// Setup state, run transaction sequence, show debugger for the last transaction + /// Setup scenario, run transaction sequence, show debugger for the last transaction /// @param _state JS object with state configuration - //void setupState(QVariantMap _state); - /// Setup scenario, run transaction sequence, show debugger for the last transaction - /// @param _state JS object with state configuration - void setupScenario(QVariantMap _scenario); - /// Execute the given @param _tr on the curret state - void executeTr(QVariantMap _tr); + void setupScenario(QVariantMap _scenario); + /// Execute the given @param _tr on the current state + void executeTr(QVariantMap _tr); /// Show the debugger for a specified record Q_INVOKABLE void debugRecord(unsigned _index); /// Show the debugger for an empty record @@ -244,7 +241,7 @@ private: RecordLogEntry* lastBlock() const; QVariantMap contractAddresses() const; QVariantList gasCosts() const; - void executeSequence(std::vector const& _sequence); + void executeSequence(std::vector const& _sequence); dev::Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings()); void callAddress(Address const& _contract, bytes const& _data, TransactionSettings const& _tr); void onNewTransaction(); @@ -255,9 +252,9 @@ private: std::pair retrieveToken(QString const& _value); std::pair resolvePair(QString const& _contractId); QVariant formatStorageValue(SolidityType const& _type, std::unordered_map const& _storage, unsigned _offset, dev::u256 const& _slot); - void processNextTransactions(); - void finalizeBlock(); - void stopExecution(); + void processNextTransactions(); + void finalizeBlock(); + void stopExecution(); void setupExecutionChain(); std::atomic m_running; @@ -267,7 +264,7 @@ private: std::unique_ptr m_rpcConnector; std::unique_ptr m_web3Server; std::shared_ptr m_ethAccounts; - std::unordered_map m_accounts; + std::unordered_map m_accounts; std::vector m_accountsSecret; QList m_gasCosts; std::map, Address> m_contractAddresses; @@ -275,8 +272,8 @@ private: std::map m_stdContractAddresses; std::map m_stdContractNames; CodeModel* m_codeModel = nullptr; - QList m_queueTransactions; - QVariantMap m_currentScenario; + QList m_queueTransactions; + QVariantMap m_currentScenario; mutable boost::shared_mutex x_queueTransactions; }; diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 553c5e075..d805822aa 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -245,7 +245,7 @@ QStringList ContractCallDataEncoder::decode(QList const& value.populate(&rawParam); value = value.cropped(32); QVariableDeclaration* dec = static_cast(_returnParameters.at(k)); - SolidityType const& type = dec->type()->type(); + SolidityType const& type = dec->type()->type(); r.append(decode(type, rawParam).toString()); } return r; diff --git a/mix/ContractCallDataEncoder.h b/mix/ContractCallDataEncoder.h index 748239c5b..8fcc65c0a 100644 --- a/mix/ContractCallDataEncoder.h +++ b/mix/ContractCallDataEncoder.h @@ -50,7 +50,7 @@ public: QStringList decode(QList const& _dec, bytes _value); /// Decode @param _parameter QString decode(QVariableDeclaration* const& _param, bytes _value); - /// Decode single variable + /// Decode single variable QVariant decode(SolidityType const& _type, bytes const& _value); /// Get all encoded data encoded by encode function. bytes encodedData(); diff --git a/mix/MachineStates.h b/mix/MachineStates.h index 1b50c062b..9a9acecf4 100644 --- a/mix/MachineStates.h +++ b/mix/MachineStates.h @@ -37,59 +37,59 @@ namespace dev namespace mix { - /** +/** * @brief Store information about a machine state. */ - struct MachineState - { - uint64_t steps; - dev::u256 curPC; - dev::eth::Instruction inst; - dev::bigint newMemSize; - dev::u256 gas; - dev::u256s stack; - dev::bytes memory; - dev::bigint gasCost; - std::unordered_map storage; - std::vector levels; - unsigned codeIndex; - unsigned dataIndex; - }; +struct MachineState +{ + uint64_t steps; + dev::u256 curPC; + dev::eth::Instruction inst; + dev::bigint newMemSize; + dev::u256 gas; + dev::u256s stack; + dev::bytes memory; + dev::bigint gasCost; + std::unordered_map storage; + std::vector levels; + unsigned codeIndex; + unsigned dataIndex; +}; - /** +/** * @brief Executed conract code info */ - struct MachineCode - { - dev::Address address; - bytes code; - }; +struct MachineCode +{ + dev::Address address; + bytes code; +}; - /** +/** * @brief Store information about a machine states. */ - struct ExecutionResult - { - ExecutionResult(): transactionIndex(std::numeric_limits::max()) {} +struct ExecutionResult +{ + ExecutionResult(): transactionIndex(std::numeric_limits::max()) {} - std::vector machineStates; - std::vector transactionData; - std::vector executionCode; - dev::eth::ExecutionResult result; - dev::Address address; - dev::Address sender; - dev::Address contractAddress; - dev::u256 value; - dev::u256 gasUsed; - unsigned transactionIndex; - unsigned executonIndex = 0; - bytes inputParameters; - eth::LocalisedLogEntries logs; + std::vector machineStates; + std::vector transactionData; + std::vector executionCode; + dev::eth::ExecutionResult result; + dev::Address address; + dev::Address sender; + dev::Address contractAddress; + dev::u256 value; + dev::u256 gasUsed; + unsigned transactionIndex; + unsigned executonIndex = 0; + bytes inputParameters; + eth::LocalisedLogEntries logs; - bool isCall() const { return transactionIndex == std::numeric_limits::max(); } - bool isConstructor() const { return !isCall() && !address; } - }; + bool isCall() const { return transactionIndex == std::numeric_limits::max(); } + bool isConstructor() const { return !isCall() && !address; } +}; - using ExecutionResults = std::vector; +using ExecutionResults = std::vector; } } diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 62d6636c2..31b4772b4 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -73,25 +73,11 @@ MixClient::MixClient(std::string const& _dbPath): resetState(std::unordered_map()); } -MixClient::~MixClient() -{ -} - -LocalisedLogEntries MixClient::logs() -{ - return m_watches.at(0).changes; -} - void MixClient::resetState(std::unordered_map const& _accounts, Secret const& _miner) { WriteGuard l(x_state); Guard fl(x_filtersWatches); - m_filters.clear(); - m_watches.clear(); - //LogFilter filter; - //m_filters.insert(std::make_pair(filter.sha3(), filter)); - //m_watches.insert(std::make_pair(0, ClientWatch(filter.sha3(), Reaping::Automatic))); m_stateDB = OverlayDB(); SecureTrieDB accountState(&m_stateDB); @@ -261,39 +247,15 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c if (t.isCreation() && _state.code(d.contractAddress).empty()) BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); d.gasUsed = er.gasUsed + er.gasRefunded + er.gasForDeposit + c_callStipend; - // collect watches - h256Set changed; - Guard l(x_filtersWatches); LocalisedLogEntries logs; - //for (unsigned i = 0; i < _state.pending().size(); ++i) - //{ TransactionReceipt const& tr = _state.receipt(_state.pending().size() - 1); auto trHash = _state.pending().at(_state.pending().size() - 1).sha3(); - //for (std::pair& installedFilter: m_filters) - //{ - LogEntries le = tr.log(); // installedFilter.second.filter.matches(tr); + LogEntries le = tr.log(); if (le.size()) for (unsigned j = 0; j < le.size(); ++j) logs.insert(logs.begin(), LocalisedLogEntry(le[j], bc().number() + 1, trHash)); - //} - //} - - /*if ((unsigned)i.second.filter.latest() > bc().number()) - { - // acceptable number. - auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1)); - if (m.size()) - { - // filter catches them - for (LogEntry const& l: m) - i.second.changes.push_back(LocalisedLogEntry(l, bc().number() + 1)); - changed.insert(i.first); - } - }*/ - changed.insert(dev::eth::PendingChangedFilter); d.logs = logs; - //noteChanged(changed); } WriteGuard l(x_executions); m_executions.emplace_back(std::move(d)); @@ -308,7 +270,6 @@ void MixClient::mine() m_state.sync(bc()); m_startState = m_state; h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter }; - //noteChanged(changed); } ExecutionResult MixClient::lastExecution() const @@ -395,20 +356,6 @@ dev::eth::ExecutionResult MixClient::create(Address const& _from, u256 _value, b return lastExecution().result; } -/*void MixClient::noteChanged(h256Set const& _filters) -{ - for (auto& i: m_watches) - if (_filters.count(i.second.id)) - { - if (m_filters.count(i.second.id)) - i.second.changes += m_filters.at(i.second.id).changes; - else - i.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0)); - } - for (auto& i: m_filters) - i.second.changes.clear(); -}*/ - eth::BlockInfo MixClient::blockInfo() const { ReadGuard l(x_state); diff --git a/mix/MixClient.h b/mix/MixClient.h index 4e05e5104..afb9f5430 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -77,8 +77,6 @@ public: using Interface::blockInfo; // to remove warning about hiding virtual function eth::BlockInfo blockInfo() const; - dev::eth::LocalisedLogEntries logs(); - protected: /// ClientBase methods using ClientBase::asOf; @@ -91,10 +89,9 @@ protected: private: void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call, bool _gasAuto, dev::Secret const& _secret = dev::Secret()); - //void noteChanged(h256Set const& _filters); dev::eth::Transaction replaceGas(dev::eth::Transaction const& _t, dev::u256 const& _gas, dev::Secret const& _secret = dev::Secret()); - eth::State m_state; + eth::State m_state; eth::State m_startState; OverlayDB m_stateDB; std::unique_ptr m_bc; diff --git a/mix/QContractDefinition.cpp b/mix/QContractDefinition.cpp index fe05e80be..f4022d1ff 100644 --- a/mix/QContractDefinition.cpp +++ b/mix/QContractDefinition.cpp @@ -53,7 +53,7 @@ QContractDefinition::QContractDefinition(QObject* _parent, dev::solidity::Contra } - for (auto const& it: _contract->getEvents()) + for (auto const& it: _contract->getEvents()) m_events.append(new QFunctionDefinition(parent, it)); } diff --git a/mix/main.cpp b/mix/main.cpp index b4206ec51..78b5261ac 100644 --- a/mix/main.cpp +++ b/mix/main.cpp @@ -32,9 +32,9 @@ int main(int _argc, char* _argv[]) { try { - MixApplication::initialize(); + MixApplication::initialize(); MixApplication app(_argc, _argv); - return app.exec(); + return app.exec(); } catch (boost::exception const& _e) { diff --git a/mix/qml/Application.qml b/mix/qml/Application.qml index 509e2e178..61a2e09b1 100644 --- a/mix/qml/Application.qml +++ b/mix/qml/Application.qml @@ -205,14 +205,14 @@ ApplicationWindow { enabled: codeModel.hasContract && !clientModel.running } - Action { + Action { id: toggleAssemblyDebuggingAction text: qsTr("Show VM Code") shortcut: "Ctrl+Alt+V" - onTriggered: mainContent.debuggerPanel.assemblyMode = !mainContent.debuggerPanel.assemblyMode; - checked: mainContent.debuggerPanel.assemblyMode; + onTriggered: mainContent.debuggerPanel.assemblyMode = !mainContent.debuggerPanel.assemblyMode; + checked: mainContent.debuggerPanel.assemblyMode; enabled: true - } + } Action { id: toggleWebPreviewAction @@ -221,7 +221,7 @@ ApplicationWindow { checkable: true checked: mainContent.webViewVisible onTriggered: mainContent.toggleWebPreview(); - } + } Action { id: toggleProjectNavigatorAction diff --git a/mix/qml/Block.qml b/mix/qml/Block.qml index 576cc5215..6fb274ccd 100644 --- a/mix/qml/Block.qml +++ b/mix/qml/Block.qml @@ -10,337 +10,337 @@ import "." ColumnLayout { - id: root - property variant transactions - property string status - property int number - property int blockWidth: Layout.preferredWidth - statusWidth - horizontalMargin - property int horizontalMargin: 10 - property int trHeight: 30 - spacing: 0 - property int openedTr: 0 - property int blockIndex - property variant scenario + id: root + property variant transactions + property string status + property int number + property int blockWidth: Layout.preferredWidth - statusWidth - horizontalMargin + property int horizontalMargin: 10 + property int trHeight: 30 + spacing: 0 + property int openedTr: 0 + property int blockIndex + property variant scenario - function calculateHeight() - { - if (transactions) - { - if (index >= 0) - return 30 + 30 * transactions.count + openedTr - else - return 30 - } - else - return 30 - } + function calculateHeight() + { + if (transactions) + { + if (index >= 0) + return 30 + 30 * transactions.count + openedTr + else + return 30 + } + else + return 30 + } - onOpenedTrChanged: - { - Layout.preferredHeight = calculateHeight() - height = calculateHeight() - } + onOpenedTrChanged: + { + Layout.preferredHeight = calculateHeight() + height = calculateHeight() + } - RowLayout - { - Layout.preferredHeight: trHeight - Layout.preferredWidth: blockWidth - id: rowHeader - Rectangle - { - color: "#DEDCDC" - Layout.preferredWidth: blockWidth - Layout.preferredHeight: trHeight - radius: 4 - anchors.left: parent.left - anchors.leftMargin: statusWidth + 5 - Label { - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: horizontalMargin - text: - { - if (status === "mined") - return qsTr("BLOCK") + " " + number - else - return qsTr("BLOCK") + " pending" - } - } - } - } + RowLayout + { + Layout.preferredHeight: trHeight + Layout.preferredWidth: blockWidth + id: rowHeader + Rectangle + { + color: "#DEDCDC" + Layout.preferredWidth: blockWidth + Layout.preferredHeight: trHeight + radius: 4 + anchors.left: parent.left + anchors.leftMargin: statusWidth + 5 + Label { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: horizontalMargin + text: + { + if (status === "mined") + return qsTr("BLOCK") + " " + number + else + return qsTr("BLOCK") + " pending" + } + } + } + } - Repeater // List of transactions - { - id: transactionRepeater - model: transactions + Repeater // List of transactions + { + id: transactionRepeater + model: transactions - RowLayout - { - id: rowTransaction - Layout.preferredHeight: trHeight - function displayContent() - { - logsText.text = "" - if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count) - { - for (var k = 0; k < transactions.get(index).logs.count; k++) - { - var log = transactions.get(index).logs.get(k) - if (log.name) - logsText.text += log.name + ":\n" - else - logsText.text += "log:\n" + RowLayout + { + id: rowTransaction + Layout.preferredHeight: trHeight + function displayContent() + { + logsText.text = "" + if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count) + { + for (var k = 0; k < transactions.get(index).logs.count; k++) + { + var log = transactions.get(index).logs.get(k) + if (log.name) + logsText.text += log.name + ":\n" + else + logsText.text += "log:\n" - if (log.param) - for (var i = 0; i < log.param.count; i++) - { - var p = log.param.get(i) - logsText.text += p.name + " = " + p.value + " - indexed:" + p.indexed + "\n" - } - else{ - logsText.text += "From : " + log.address + "\n" - } - } - logsText.text += "\n\n" - } - rowDetailedContent.visible = !rowDetailedContent.visible - } + if (log.param) + for (var i = 0; i < log.param.count; i++) + { + var p = log.param.get(i) + logsText.text += p.name + " = " + p.value + " - indexed:" + p.indexed + "\n" + } + else{ + logsText.text += "From : " + log.address + "\n" + } + } + logsText.text += "\n\n" + } + rowDetailedContent.visible = !rowDetailedContent.visible + } - Rectangle - { - id: trSaveStatus - Layout.preferredWidth: statusWidth - Layout.preferredHeight: trHeight - color: "transparent" - anchors.top: parent.top - property bool saveStatus + Rectangle + { + id: trSaveStatus + Layout.preferredWidth: statusWidth + Layout.preferredHeight: trHeight + color: "transparent" + anchors.top: parent.top + property bool saveStatus - Image { - id: saveStatusImage - source: "qrc:/qml/img/recyclediscard@2x.png" - width: statusWidth - fillMode: Image.PreserveAspectFit - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - } + Image { + id: saveStatusImage + source: "qrc:/qml/img/recyclediscard@2x.png" + width: statusWidth + fillMode: Image.PreserveAspectFit + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } - Component.onCompleted: - { - if (index >= 0) - saveStatus = transactions.get(index).saveStatus - } + Component.onCompleted: + { + if (index >= 0) + saveStatus = transactions.get(index).saveStatus + } - onSaveStatusChanged: - { - if (saveStatus) - saveStatusImage.source = "qrc:/qml/img/recyclekeep@2x.png" - else - saveStatusImage.source = "qrc:/qml/img/recyclediscard@2x.png" + onSaveStatusChanged: + { + if (saveStatus) + saveStatusImage.source = "qrc:/qml/img/recyclekeep@2x.png" + else + saveStatusImage.source = "qrc:/qml/img/recyclediscard@2x.png" - if (index >= 0) - transactions.get(index).saveStatus = saveStatus - } + if (index >= 0) + transactions.get(index).saveStatus = saveStatus + } - MouseArea { - id: statusMouseArea - anchors.fill: parent - onClicked: - { - parent.saveStatus = !parent.saveStatus - } - } - } + MouseArea { + id: statusMouseArea + anchors.fill: parent + onClicked: + { + parent.saveStatus = !parent.saveStatus + } + } + } - Rectangle - { - Layout.preferredWidth: blockWidth - Layout.preferredHeight: parent.height - color: "#DEDCDC" - id: rowContentTr - anchors.top: parent.top - ColumnLayout - { - anchors.top: parent.top - spacing: 10 - RowLayout - { - anchors.top: parent.top - anchors.verticalCenter: parent.verticalCenter - spacing: cellSpacing - Text - { - id: hash - anchors.left: parent.left - anchors.leftMargin: horizontalMargin - Layout.preferredWidth: fromWidth - elide: Text.ElideRight - maximumLineCount: 1 - text: { - if (index >= 0) - return transactions.get(index).sender - else - return "" - } - } + Rectangle + { + Layout.preferredWidth: blockWidth + Layout.preferredHeight: parent.height + color: "#DEDCDC" + id: rowContentTr + anchors.top: parent.top + ColumnLayout + { + anchors.top: parent.top + spacing: 10 + RowLayout + { + anchors.top: parent.top + anchors.verticalCenter: parent.verticalCenter + spacing: cellSpacing + Text + { + id: hash + anchors.left: parent.left + anchors.leftMargin: horizontalMargin + Layout.preferredWidth: fromWidth + elide: Text.ElideRight + maximumLineCount: 1 + text: { + if (index >= 0) + return transactions.get(index).sender + else + return "" + } + } - Text - { - id: func - text: { - if (index >= 0) - parent.userFrienldyToken(transactions.get(index).label) - else - return "" - } - elide: Text.ElideRight - maximumLineCount: 1 - Layout.preferredWidth: toWidth - } + Text + { + id: func + text: { + if (index >= 0) + parent.userFrienldyToken(transactions.get(index).label) + else + return "" + } + elide: Text.ElideRight + maximumLineCount: 1 + Layout.preferredWidth: toWidth + } - function userFrienldyToken(value) - { - if (value && value.indexOf("<") === 0) - { - if (value.split("> ")[1] === " - ") - return value.split(" - ")[0].replace("<", "") - else - return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()"; - } - else - return value - } + function userFrienldyToken(value) + { + if (value && value.indexOf("<") === 0) + { + if (value.split("> ")[1] === " - ") + return value.split(" - ")[0].replace("<", "") + else + return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()"; + } + else + return value + } - Text - { - id: returnValue - elide: Text.ElideRight - maximumLineCount: 1 - Layout.preferredWidth: valueWidth - text: { - if (index >= 0 && transactions.get(index).returned) - return transactions.get(index).returned - else - return "" - } - } + Text + { + id: returnValue + elide: Text.ElideRight + maximumLineCount: 1 + Layout.preferredWidth: valueWidth + text: { + if (index >= 0 && transactions.get(index).returned) + return transactions.get(index).returned + else + return "" + } + } - Rectangle - { - Layout.preferredWidth: logsWidth - Layout.preferredHeight: trHeight - 10 - width: logsWidth - color: "transparent" - Text - { - id: logs - anchors.left: parent.left - anchors.leftMargin: 10 - text: { - if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count) - return transactions.get(index).logs.count - else - return "" - } - } - MouseArea { - anchors.fill: parent - onClicked: { - rowTransaction.displayContent(); - } - } - } + Rectangle + { + Layout.preferredWidth: logsWidth + Layout.preferredHeight: trHeight - 10 + width: logsWidth + color: "transparent" + Text + { + id: logs + anchors.left: parent.left + anchors.leftMargin: 10 + text: { + if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count) + return transactions.get(index).logs.count + else + return "" + } + } + MouseArea { + anchors.fill: parent + onClicked: { + rowTransaction.displayContent(); + } + } + } - Rectangle - { - Layout.preferredWidth: debugActionWidth - Layout.preferredHeight: trHeight - 10 - color: "transparent" + Rectangle + { + Layout.preferredWidth: debugActionWidth + Layout.preferredHeight: trHeight - 10 + color: "transparent" - Image { - source: "qrc:/qml/img/edit.png" - width: 18 - fillMode: Image.PreserveAspectFit - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - } - MouseArea - { - anchors.fill: parent - onClicked: - { - transactionDialog.stateAccounts = scenario.accounts - transactionDialog.execute = false - transactionDialog.open(index, blockIndex, transactions.get(index)) - } - } - } + Image { + source: "qrc:/qml/img/edit.png" + width: 18 + fillMode: Image.PreserveAspectFit + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + MouseArea + { + anchors.fill: parent + onClicked: + { + transactionDialog.stateAccounts = scenario.accounts + transactionDialog.execute = false + transactionDialog.open(index, blockIndex, transactions.get(index)) + } + } + } - Rectangle - { - Layout.preferredWidth: debugActionWidth - Layout.preferredHeight: trHeight - 10 - color: "transparent" + Rectangle + { + Layout.preferredWidth: debugActionWidth + Layout.preferredHeight: trHeight - 10 + color: "transparent" - Image { - id: debugImg - source: "qrc:/qml/img/rightarrow@2x.png" - width: statusWidth - fillMode: Image.PreserveAspectFit - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - visible: transactions.get(index).recordIndex !== undefined - } - MouseArea - { - anchors.fill: parent - onClicked: - { - if (transactions.get(index).recordIndex !== undefined) - { - debugTrRequested = [ blockIndex, index ] - clientModel.debugRecord(transactions.get(index).recordIndex); - } - } - } - } - } + Image { + id: debugImg + source: "qrc:/qml/img/rightarrow@2x.png" + width: statusWidth + fillMode: Image.PreserveAspectFit + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + visible: transactions.get(index).recordIndex !== undefined + } + MouseArea + { + anchors.fill: parent + onClicked: + { + if (transactions.get(index).recordIndex !== undefined) + { + debugTrRequested = [ blockIndex, index ] + clientModel.debugRecord(transactions.get(index).recordIndex); + } + } + } + } + } - RowLayout - { - id: rowDetailedContent - visible: false - Layout.preferredHeight:{ - if (index >= 0 && transactions.get(index).logs) - return 100 * transactions.get(index).logs.count - else - return 100 - } - onVisibleChanged: - { - var lognb = transactions.get(index).logs.count - if (visible) - { - rowContentTr.Layout.preferredHeight = trHeight + 100 * lognb - openedTr += 100 * lognb - } - else - { - rowContentTr.Layout.preferredHeight = trHeight - openedTr -= 100 * lognb - } - } + RowLayout + { + id: rowDetailedContent + visible: false + Layout.preferredHeight:{ + if (index >= 0 && transactions.get(index).logs) + return 100 * transactions.get(index).logs.count + else + return 100 + } + onVisibleChanged: + { + var lognb = transactions.get(index).logs.count + if (visible) + { + rowContentTr.Layout.preferredHeight = trHeight + 100 * lognb + openedTr += 100 * lognb + } + else + { + rowContentTr.Layout.preferredHeight = trHeight + openedTr -= 100 * lognb + } + } - Text { - anchors.left: parent.left - anchors.leftMargin: horizontalMargin - id: logsText - } - } - } - } - } - } + Text { + anchors.left: parent.left + anchors.leftMargin: horizontalMargin + id: logsText + } + } + } + } + } + } } diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml index 69e2bbf34..d3fad6fda 100644 --- a/mix/qml/BlockChain.qml +++ b/mix/qml/BlockChain.qml @@ -12,474 +12,469 @@ import "js/QEtherHelper.js" as QEtherHelper import "." ColumnLayout { - id: blockChainPanel - property variant model - spacing: 0 - property int previousWidth - property variant debugTrRequested: [] - signal chainChanged - - onChainChanged: { - reBuildNeeded.start() - } - - onWidthChanged: - { - - if (width <= 630 || previousWidth <= 630) - { - fromWidth = 100 - toWidth = 100 - valueWidth = 200 - } - else - { - var diff = (width - previousWidth) / 3; - fromWidth = fromWidth + diff < 100 ? 100 : fromWidth + diff - toWidth = toWidth + diff < 100 ? 100 : toWidth + diff - valueWidth = valueWidth + diff < 200 ? 200 : valueWidth + diff - } - previousWidth = width - } - - function load(scenario) - { - if (!scenario) - return; - if (model) - chainChanged() - model = scenario - blockModel.clear() - for (var b in model.blocks) - blockModel.append(model.blocks[b]) - previousWidth = width - } - - property int statusWidth: 30 - property int fromWidth: 100 - property int toWidth: 100 - property int valueWidth: 200 - property int logsWidth: 50 - property int debugActionWidth: 50 - property int horizontalMargin: 10 - property int cellSpacing: 10 - - RowLayout - { - id: header - spacing: 0 - Layout.preferredHeight: 25 - Image { - id: debugImage - source: "qrc:/qml/img/recycleicon@2x.png" - Layout.preferredWidth: statusWidth - Layout.preferredHeight: 25 - fillMode: Image.PreserveAspectFit - } - Rectangle - { - Layout.preferredWidth: fromWidth + cellSpacing - Label - { - anchors.verticalCenter: parent.verticalCenter - text: "From" - anchors.left: parent.left - anchors.leftMargin: horizontalMargin + 5 - } - } - Label - { - text: "To" - Layout.preferredWidth: toWidth + cellSpacing - } - Label - { - text: "Value" - Layout.preferredWidth: valueWidth + cellSpacing - } - Label - { - text: "Logs" - Layout.preferredWidth: logsWidth + cellSpacing - } - Label - { - text: "" - Layout.preferredWidth: debugActionWidth - } - } - - Rectangle - { - Layout.preferredHeight: 500 - Layout.preferredWidth: parent.width - border.color: "#cccccc" - border.width: 2 - color: "white" - ScrollView - { - id: blockChainScrollView - anchors.fill: parent - anchors.topMargin: 10 - ColumnLayout - { - id: blockChainLayout - width: parent.width - spacing: 10 - Repeater // List of blocks - { - id: blockChainRepeater - model: blockModel - Block - { - scenario: blockChainPanel.model - Layout.preferredWidth: blockChainScrollView.width - Layout.preferredHeight: - { - return calculateHeight() - } - blockIndex: index - transactions: - { - if (index >= 0) - return blockModel.get(index).transactions - else - return [] - } - - status: - { - if (index >= 0) - return blockModel.get(index).status - else - return "" - } - - number: - { - if (index >= 0) - return blockModel.get(index).number - else - return 0 - } - } - } - } - } - } - - ListModel - { - id: blockModel - - function appendBlock(block) - { - blockModel.append(block); - } - - function appendTransaction(tr) - { - blockModel.get(blockModel.count - 1).transactions.append(tr) - } - - function removeTransaction(blockIndex, trIndex) - { - blockModel.get(blockIndex).transactions.remove(trIndex) - } - - function removeLastBlock() - { - blockModel.remove(blockModel.count - 1) - } - - function removeBlock(index) - { - blockModel.remove(index) - } - - function getTransaction(block, tr) - { - return blockModel.get(block).transactions.get(tr) - } - - function setTransaction(blockIndex, trIndex, tr) - { - blockModel.get(blockIndex).transactions.set(trIndex, tr) - } - - function setTransactionProperty(blockIndex, trIndex, propertyName, value) - { - blockModel.get(blockIndex).transactions.set(trIndex, { propertyName: value }) - } - } - - Rectangle - { - Layout.preferredWidth: parent.width - RowLayout - { - width: 4 * 100 - anchors.top: parent.top - anchors.topMargin: 10 - spacing: 0 - ScenarioButton { - id: rebuild - text: qsTr("Rebuild") - onClicked: - { - if (ensureNotFuturetime.running) - return; - reBuildNeeded.stop() - var retBlocks = []; - var bAdded = 0; - for (var j = 0; j < model.blocks.length; j++) - { - var b = model.blocks[j]; - var block = { - hash: b.hash, - number: b.number, - transactions: [], - status: b.status - } - for (var k = 0; k < model.blocks[j].transactions.length; k++) - { - if (blockModel.get(j).transactions.get(k).saveStatus) - { - var tr = model.blocks[j].transactions[k] - tr.saveStatus = true - block.transactions.push(tr); - } - - } - if (block.transactions.length > 0) - { - bAdded++ - block.number = bAdded - block.status = "mined" - retBlocks.push(block) - } - - } - if (retBlocks.length === 0) - retBlocks.push(projectModel.stateListModel.createEmptyBlock()) - else - { - var last = retBlocks[retBlocks.length - 1] - last.number = -1 - last.status = "pending" - } - - model.blocks = retBlocks - blockModel.clear() - for (var j = 0; j < model.blocks.length; j++) - blockModel.append(model.blocks[j]) - - ensureNotFuturetime.start() - clientModel.setupScenario(model); - } - - Layout.preferredWidth: 100 - Layout.preferredHeight: 30 - buttonShortcut: "" - sourceImg: "qrc:/qml/img/recycleicon@2x.png" - Timer - { - id: reBuildNeeded - repeat: true - interval: 1000 - running: false - onTriggered: { - if (!parent.fillColor || parent.fillColor === "white") - parent.fillColor = "orange" - else - parent.fillColor = "white" - } - onRunningChanged: { - if (!running) - parent.fillColor = "white" - } - } - } - - ScenarioButton { - id: addTransaction - text: qsTr("Add Transaction") - onClicked: - { - var lastBlock = model.blocks[model.blocks.length - 1]; - console.log(lastBlock.status + "!!!") - if (lastBlock.status === "mined") - { - var newblock = projectModel.stateListModel.createEmptyBlock() - blockModel.appendBlock(newblock) - model.blocks.push(newblock); - } - - var item = TransactionHelper.defaultTransaction() - transactionDialog.stateAccounts = model.accounts - transactionDialog.execute = true - transactionDialog.open(model.blocks[model.blocks.length - 1].transactions.length, model.blocks.length - 1, item) - } - Layout.preferredWidth: 100 - Layout.preferredHeight: 30 - buttonShortcut: "" - sourceImg: "qrc:/qml/img/sendtransactionicon@2x.png" - } - - Timer - { - id: ensureNotFuturetime - interval: 1000 - repeat: false - running: false - } - - ScenarioButton { - id: addBlockBtn - text: qsTr("Add Block") - onClicked: - { - if (ensureNotFuturetime.running) - return - if (clientModel.mining || clientModel.running) - return - if (model.blocks.length > 0) - { - var lastBlock = model.blocks[model.blocks.length - 1] - if (lastBlock.status === "pending") - { - ensureNotFuturetime.start() - clientModel.mine() - } - else - addNewBlock() - } - else - addNewBlock() - - } - - function addNewBlock() - { - var block = projectModel.stateListModel.createEmptyBlock() - model.blocks.push(block) - blockModel.appendBlock(block) - } - Layout.preferredWidth: 100 - Layout.preferredHeight: 30 - buttonShortcut: "" - sourceImg: "qrc:/qml/img/addblock@2x.png" - } - - Connections - { - target: clientModel - onNewBlock: - { - console.log("new Block!!") - if (!clientModel.running) - { - var lastBlock = model.blocks[model.blocks.length - 1] - lastBlock.status = "mined" - lastBlock.number = model.blocks.length - var lastB = blockModel.get(model.blocks.length - 1) - lastB.status = "mined" - lastB.number = model.blocks.length - addBlockBtn.addNewBlock() - } - } - onStateCleared: - { - } - onNewRecord: - { - var blockIndex = parseInt(_r.transactionIndex.split(":")[0]) - 1 - var trIndex = parseInt(_r.transactionIndex.split(":")[1]) - if (blockIndex <= model.blocks.length - 1) - { - var item = model.blocks[blockIndex] - if (trIndex <= item.transactions.length - 1) - { - var tr = item.transactions[trIndex] - tr.returned = _r.returned - tr.recordIndex = _r.recordIndex - tr.logs = _r.logs - tr.sender = _r.sender - var trModel = blockModel.getTransaction(blockIndex, trIndex) - trModel.returned = _r.returned - trModel.recordIndex = _r.recordIndex - trModel.logs = _r.logs - trModel.sender = _r.sender - blockModel.setTransaction(blockIndex, trIndex, trModel) - return; - } - } - - // tr is not in the list. - var itemTr = TransactionHelper.defaultTransaction() - itemTr.saveStatus = false - itemTr.functionId = _r.function - itemTr.contractId = _r.contract - itemTr.gasAuto = true - itemTr.parameters = _r.parameters - itemTr.isContractCreation = itemTr.functionId === itemTr.contractId - itemTr.label = _r.label - itemTr.isFunctionCall = itemTr.functionId !== "" - itemTr.returned = _r.returned - itemTr.value = QEtherHelper.createEther(_r.value, QEther.Wei) - itemTr.sender = _r.sender - itemTr.recordIndex = _r.recordIndex - itemTr.logs = _r.logs - console.log(JSON.stringify(itemTr)) - model.blocks[model.blocks.length - 1].transactions.push(itemTr) - blockModel.appendTransaction(itemTr) - } - onMiningComplete: - { - } - } - - ScenarioButton { - id: newAccount - text: qsTr("New Account") - onClicked: { - model.accounts.push(projectModel.stateListModel.newAccount("1000000", QEther.Ether)) - } - Layout.preferredWidth: 100 - Layout.preferredHeight: 30 - buttonShortcut: "" - sourceImg: "qrc:/qml/img/newaccounticon@2x.png" - } - } - } - - TransactionDialog { - id: transactionDialog - property bool execute - onAccepted: { - var item = transactionDialog.getItem() - if (execute) - { - console.log("cc " + model.blocks.length) - var lastBlock = model.blocks[model.blocks.length - 1]; - console.log("mined?" + lastBlock.status) - if (lastBlock.status === "mined") - { - var newBlock = projectModel.stateListModel.createEmptyBlock(); - model.blocks.push(newBlock); - blockModel.appendBlock(newBlock) - } - if (!clientModel.running) - clientModel.executeTr(item) - } - else { - model.blocks[blockIndex].transactions[transactionIndex] = item - blockModel.setTransaction(blockIndex, transactionIndex, item) - chainChanged() - } - - } - } + id: blockChainPanel + property variant model + spacing: 0 + property int previousWidth + property variant debugTrRequested: [] + signal chainChanged + + onChainChanged: { + reBuildNeeded.start() + } + + onWidthChanged: + { + + if (width <= 630 || previousWidth <= 630) + { + fromWidth = 100 + toWidth = 100 + valueWidth = 200 + } + else + { + var diff = (width - previousWidth) / 3; + fromWidth = fromWidth + diff < 100 ? 100 : fromWidth + diff + toWidth = toWidth + diff < 100 ? 100 : toWidth + diff + valueWidth = valueWidth + diff < 200 ? 200 : valueWidth + diff + } + previousWidth = width + } + + function load(scenario) + { + if (!scenario) + return; + if (model) + chainChanged() + model = scenario + blockModel.clear() + for (var b in model.blocks) + blockModel.append(model.blocks[b]) + previousWidth = width + } + + property int statusWidth: 30 + property int fromWidth: 100 + property int toWidth: 100 + property int valueWidth: 200 + property int logsWidth: 50 + property int debugActionWidth: 50 + property int horizontalMargin: 10 + property int cellSpacing: 10 + + RowLayout + { + id: header + spacing: 0 + Layout.preferredHeight: 25 + Image { + id: debugImage + source: "qrc:/qml/img/recycleicon@2x.png" + Layout.preferredWidth: statusWidth + Layout.preferredHeight: 25 + fillMode: Image.PreserveAspectFit + } + Rectangle + { + Layout.preferredWidth: fromWidth + cellSpacing + Label + { + anchors.verticalCenter: parent.verticalCenter + text: "From" + anchors.left: parent.left + anchors.leftMargin: horizontalMargin + 5 + } + } + Label + { + text: "To" + Layout.preferredWidth: toWidth + cellSpacing + } + Label + { + text: "Value" + Layout.preferredWidth: valueWidth + cellSpacing + } + Label + { + text: "Logs" + Layout.preferredWidth: logsWidth + cellSpacing + } + Label + { + text: "" + Layout.preferredWidth: debugActionWidth + } + } + + Rectangle + { + Layout.preferredHeight: 500 + Layout.preferredWidth: parent.width + border.color: "#cccccc" + border.width: 2 + color: "white" + ScrollView + { + id: blockChainScrollView + anchors.fill: parent + anchors.topMargin: 10 + ColumnLayout + { + id: blockChainLayout + width: parent.width + spacing: 10 + Repeater // List of blocks + { + id: blockChainRepeater + model: blockModel + Block + { + scenario: blockChainPanel.model + Layout.preferredWidth: blockChainScrollView.width + Layout.preferredHeight: + { + return calculateHeight() + } + blockIndex: index + transactions: + { + if (index >= 0) + return blockModel.get(index).transactions + else + return [] + } + + status: + { + if (index >= 0) + return blockModel.get(index).status + else + return "" + } + + number: + { + if (index >= 0) + return blockModel.get(index).number + else + return 0 + } + } + } + } + } + } + + ListModel + { + id: blockModel + + function appendBlock(block) + { + blockModel.append(block); + } + + function appendTransaction(tr) + { + blockModel.get(blockModel.count - 1).transactions.append(tr) + } + + function removeTransaction(blockIndex, trIndex) + { + blockModel.get(blockIndex).transactions.remove(trIndex) + } + + function removeLastBlock() + { + blockModel.remove(blockModel.count - 1) + } + + function removeBlock(index) + { + blockModel.remove(index) + } + + function getTransaction(block, tr) + { + return blockModel.get(block).transactions.get(tr) + } + + function setTransaction(blockIndex, trIndex, tr) + { + blockModel.get(blockIndex).transactions.set(trIndex, tr) + } + + function setTransactionProperty(blockIndex, trIndex, propertyName, value) + { + blockModel.get(blockIndex).transactions.set(trIndex, { propertyName: value }) + } + } + + Rectangle + { + Layout.preferredWidth: parent.width + RowLayout + { + width: 4 * 100 + anchors.top: parent.top + anchors.topMargin: 10 + spacing: 0 + ScenarioButton { + id: rebuild + text: qsTr("Rebuild") + onClicked: + { + if (ensureNotFuturetime.running) + return; + reBuildNeeded.stop() + var retBlocks = []; + var bAdded = 0; + for (var j = 0; j < model.blocks.length; j++) + { + var b = model.blocks[j]; + var block = { + hash: b.hash, + number: b.number, + transactions: [], + status: b.status + } + for (var k = 0; k < model.blocks[j].transactions.length; k++) + { + if (blockModel.get(j).transactions.get(k).saveStatus) + { + var tr = model.blocks[j].transactions[k] + tr.saveStatus = true + block.transactions.push(tr); + } + + } + if (block.transactions.length > 0) + { + bAdded++ + block.number = bAdded + block.status = "mined" + retBlocks.push(block) + } + + } + if (retBlocks.length === 0) + retBlocks.push(projectModel.stateListModel.createEmptyBlock()) + else + { + var last = retBlocks[retBlocks.length - 1] + last.number = -1 + last.status = "pending" + } + + model.blocks = retBlocks + blockModel.clear() + for (var j = 0; j < model.blocks.length; j++) + blockModel.append(model.blocks[j]) + + ensureNotFuturetime.start() + clientModel.setupScenario(model); + } + + Layout.preferredWidth: 100 + Layout.preferredHeight: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/recycleicon@2x.png" + Timer + { + id: reBuildNeeded + repeat: true + interval: 1000 + running: false + onTriggered: { + if (!parent.fillColor || parent.fillColor === "white") + parent.fillColor = "orange" + else + parent.fillColor = "white" + } + onRunningChanged: { + if (!running) + parent.fillColor = "white" + } + } + } + + ScenarioButton { + id: addTransaction + text: qsTr("Add Transaction") + onClicked: + { + var lastBlock = model.blocks[model.blocks.length - 1]; + if (lastBlock.status === "mined") + { + var newblock = projectModel.stateListModel.createEmptyBlock() + blockModel.appendBlock(newblock) + model.blocks.push(newblock); + } + + var item = TransactionHelper.defaultTransaction() + transactionDialog.stateAccounts = model.accounts + transactionDialog.execute = true + transactionDialog.open(model.blocks[model.blocks.length - 1].transactions.length, model.blocks.length - 1, item) + } + Layout.preferredWidth: 100 + Layout.preferredHeight: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/sendtransactionicon@2x.png" + } + + Timer + { + id: ensureNotFuturetime + interval: 1000 + repeat: false + running: false + } + + ScenarioButton { + id: addBlockBtn + text: qsTr("Add Block") + onClicked: + { + if (ensureNotFuturetime.running) + return + if (clientModel.mining || clientModel.running) + return + if (model.blocks.length > 0) + { + var lastBlock = model.blocks[model.blocks.length - 1] + if (lastBlock.status === "pending") + { + ensureNotFuturetime.start() + clientModel.mine() + } + else + addNewBlock() + } + else + addNewBlock() + + } + + function addNewBlock() + { + var block = projectModel.stateListModel.createEmptyBlock() + model.blocks.push(block) + blockModel.appendBlock(block) + } + Layout.preferredWidth: 100 + Layout.preferredHeight: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/addblock@2x.png" + } + + Connections + { + target: clientModel + onNewBlock: + { + if (!clientModel.running) + { + var lastBlock = model.blocks[model.blocks.length - 1] + lastBlock.status = "mined" + lastBlock.number = model.blocks.length + var lastB = blockModel.get(model.blocks.length - 1) + lastB.status = "mined" + lastB.number = model.blocks.length + addBlockBtn.addNewBlock() + } + } + onStateCleared: + { + } + onNewRecord: + { + var blockIndex = parseInt(_r.transactionIndex.split(":")[0]) - 1 + var trIndex = parseInt(_r.transactionIndex.split(":")[1]) + if (blockIndex <= model.blocks.length - 1) + { + var item = model.blocks[blockIndex] + if (trIndex <= item.transactions.length - 1) + { + var tr = item.transactions[trIndex] + tr.returned = _r.returned + tr.recordIndex = _r.recordIndex + tr.logs = _r.logs + tr.sender = _r.sender + var trModel = blockModel.getTransaction(blockIndex, trIndex) + trModel.returned = _r.returned + trModel.recordIndex = _r.recordIndex + trModel.logs = _r.logs + trModel.sender = _r.sender + blockModel.setTransaction(blockIndex, trIndex, trModel) + return; + } + } + + // tr is not in the list. + var itemTr = TransactionHelper.defaultTransaction() + itemTr.saveStatus = false + itemTr.functionId = _r.function + itemTr.contractId = _r.contract + itemTr.gasAuto = true + itemTr.parameters = _r.parameters + itemTr.isContractCreation = itemTr.functionId === itemTr.contractId + itemTr.label = _r.label + itemTr.isFunctionCall = itemTr.functionId !== "" + itemTr.returned = _r.returned + itemTr.value = QEtherHelper.createEther(_r.value, QEther.Wei) + itemTr.sender = _r.sender + itemTr.recordIndex = _r.recordIndex + itemTr.logs = _r.logs + model.blocks[model.blocks.length - 1].transactions.push(itemTr) + blockModel.appendTransaction(itemTr) + } + onMiningComplete: + { + } + } + + ScenarioButton { + id: newAccount + text: qsTr("New Account") + onClicked: { + model.accounts.push(projectModel.stateListModel.newAccount("1000000", QEther.Ether)) + } + Layout.preferredWidth: 100 + Layout.preferredHeight: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/newaccounticon@2x.png" + } + } + } + + TransactionDialog { + id: transactionDialog + property bool execute + onAccepted: { + var item = transactionDialog.getItem() + if (execute) + { + var lastBlock = model.blocks[model.blocks.length - 1]; + if (lastBlock.status === "mined") + { + var newBlock = projectModel.stateListModel.createEmptyBlock(); + model.blocks.push(newBlock); + blockModel.appendBlock(newBlock) + } + if (!clientModel.running) + clientModel.executeTr(item) + } + else { + model.blocks[blockIndex].transactions[transactionIndex] = item + blockModel.setTransaction(blockIndex, transactionIndex, item) + chainChanged() + } + + } + } } diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index 3a0ed61a4..0129f864d 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -11,7 +11,7 @@ import "." Rectangle { id: debugPanel - property alias debugSlider: statesSlider + property alias debugSlider: statesSlider property alias solLocals: solLocals property alias solStorage: solStorage property alias solCallStack: solCallStack @@ -22,7 +22,7 @@ Rectangle { signal debugExecuteLocation(string documentId, var location) property string compilationErrorMessage property bool assemblyMode: false - signal panelClosed + signal panelClosed objectName: "debugPanel" color: "#ededed" clip: true @@ -39,10 +39,10 @@ Rectangle { machineStates.updateHeight(); } - function setTr(tr) - { - trName.text = tr.label - } + function setTr(tr) + { + trName.text = tr.label + } function displayCompilationErrorIfAny() { @@ -109,68 +109,68 @@ Rectangle { property alias solLocalsHeightSettings: solLocalsRect.height } - ColumnLayout { + ColumnLayout { id: debugScrollArea anchors.fill: parent - //orientation: Qt.Vertical - spacing: 0 - RowLayout - { - Layout.preferredWidth: parent.width - Layout.preferredHeight: 30 - Rectangle - { - Layout.preferredWidth: parent.width - Layout.preferredHeight: parent.height - color: "transparent" - Text { - anchors.centerIn: parent - text: qsTr("Current Transaction") - } - - Rectangle - { - anchors.left: parent.left - anchors.leftMargin: 10 - width: 30 - height: parent.height - color: "transparent" - anchors.verticalCenter: parent.verticalCenter - Image { - source: "qrc:/qml/img/leftarrow@2x.png" - width: parent.width - fillMode: Image.PreserveAspectFit - anchors.centerIn: parent - } - MouseArea - { - anchors.fill: parent - onClicked: - { - Debugger.init(null); - panelClosed() - } - } - } - } - } - - RowLayout - { - Layout.preferredWidth: parent.width - Layout.preferredHeight: 30 - Rectangle - { - Layout.preferredWidth: parent.width - Layout.preferredHeight: parent.height - color: "#2C79D3" - Text { - id: trName - color: "white" - anchors.centerIn: parent - } - } - } + //orientation: Qt.Vertical + spacing: 0 + RowLayout + { + Layout.preferredWidth: parent.width + Layout.preferredHeight: 30 + Rectangle + { + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + color: "transparent" + Text { + anchors.centerIn: parent + text: qsTr("Current Transaction") + } + + Rectangle + { + anchors.left: parent.left + anchors.leftMargin: 10 + width: 30 + height: parent.height + color: "transparent" + anchors.verticalCenter: parent.verticalCenter + Image { + source: "qrc:/qml/img/leftarrow@2x.png" + width: parent.width + fillMode: Image.PreserveAspectFit + anchors.centerIn: parent + } + MouseArea + { + anchors.fill: parent + onClicked: + { + Debugger.init(null); + panelClosed() + } + } + } + } + } + + RowLayout + { + Layout.preferredWidth: parent.width + Layout.preferredHeight: 30 + Rectangle + { + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + color: "#2C79D3" + Text { + id: trName + color: "white" + anchors.centerIn: parent + } + } + } ScrollView { @@ -520,12 +520,12 @@ Rectangle { } Rectangle { - id: separator - width: parent.width; - height: 1; - color: "#cccccc" - anchors.bottom: parent.bottom - } + id: separator + width: parent.width; + height: 1; + color: "#cccccc" + anchors.bottom: parent.bottom + } } } } diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 22aa78f38..de19baf35 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -20,14 +20,14 @@ Rectangle { anchors.fill: parent id: root - property alias rightViewVisible: scenarioExe.visible + property alias rightViewVisible: scenarioExe.visible property alias webViewVisible: webPreview.visible property alias webView: webPreview property alias projectViewVisible: projectList.visible property alias projectNavigator: projectList property alias runOnProjectLoad: mainSettings.runOnProjectLoad - property alias rightPane: scenarioExe - property alias debuggerPanel: debugPanel + property alias rightPane: scenarioExe + property alias debuggerPanel: debugPanel property alias codeEditor: codeEditor property bool webViewHorizontal: codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally property bool firstCompile: true @@ -43,17 +43,17 @@ Rectangle { } } - Connections { - target: debugPanel - onDebugExecuteLocation: { + Connections { + target: debugPanel + onDebugExecuteLocation: { codeEditor.highlightExecution(documentId, location); } - } + } Connections { target: codeEditor onBreakpointsChanged: { - debugPanel.setBreakpoints(codeEditor.getBreakpoints()); + debugPanel.setBreakpoints(codeEditor.getBreakpoints()); } } @@ -64,20 +64,20 @@ Rectangle { } function toggleRightView() { - scenarioExe.visible = !scenarioExe.visible; + scenarioExe.visible = !scenarioExe.visible; } function ensureRightView() { - scenarioExe.visible = true; + scenarioExe.visible = true; } function rightViewIsVisible() { - return scenarioExe.visible; + return scenarioExe.visible; } function hideRightView() { - scenarioExe.visible = lfalse; + scenarioExe.visible = lfalse; } function toggleWebPreview() { @@ -99,8 +99,8 @@ Rectangle { function displayCompilationErrorIfAny() { - scenarioExe.visible = true; - scenarioExe.displayCompilationErrorIfAny(); + scenarioExe.visible = true; + scenarioExe.displayCompilationErrorIfAny(); } Settings { @@ -154,7 +154,7 @@ Rectangle { id: splitSettings property alias projectWidth: projectList.width property alias contentViewWidth: contentView.width - property alias rightViewWidth: scenarioExe.width + property alias rightViewWidth: scenarioExe.width } Splitter @@ -199,45 +199,45 @@ Rectangle { } } - ScenarioExecution - { - id: scenarioExe; - visible: false; - Layout.fillHeight: true - Keys.onEscapePressed: visible = false - Layout.minimumWidth: 650 - anchors.right: parent.right - } - - Debugger - { - id: debugPanel - visible: false - Layout.fillHeight: true - Keys.onEscapePressed: visible = false - Layout.minimumWidth: 650 - anchors.right: parent.right - } - - Connections { - target: clientModel - onDebugDataReady: { - scenarioExe.visible = false - debugPanel.visible = true - if (scenarioExe.bc.debugTrRequested) - { - debugPanel.setTr(scenarioExe.bc.model.blocks[scenarioExe.bc.debugTrRequested[0]].transactions[scenarioExe.bc.debugTrRequested[1]]) - } - } - } - - Connections { - target: debugPanel - onPanelClosed: { - debugPanel.visible = false - scenarioExe.visible = true - } - } + ScenarioExecution + { + id: scenarioExe; + visible: false; + Layout.fillHeight: true + Keys.onEscapePressed: visible = false + Layout.minimumWidth: 650 + anchors.right: parent.right + } + + Debugger + { + id: debugPanel + visible: false + Layout.fillHeight: true + Keys.onEscapePressed: visible = false + Layout.minimumWidth: 650 + anchors.right: parent.right + } + + Connections { + target: clientModel + onDebugDataReady: { + scenarioExe.visible = false + debugPanel.visible = true + if (scenarioExe.bc.debugTrRequested) + { + debugPanel.setTr(scenarioExe.bc.model.blocks[scenarioExe.bc.debugTrRequested[0]].transactions[scenarioExe.bc.debugTrRequested[1]]) + } + } + } + + Connections { + target: debugPanel + onPanelClosed: { + debugPanel.visible = false + scenarioExe.visible = true + } + } } } } diff --git a/mix/qml/QAddressView.qml b/mix/qml/QAddressView.qml index 3c34d45eb..4781d092b 100644 --- a/mix/qml/QAddressView.qml +++ b/mix/qml/QAddressView.qml @@ -39,9 +39,6 @@ Item { accountRef.clear(); accountRef.append({"itemid": " - "}); - - console.log(blockIndex) - console.log(transactionIndex) if (subType === "contract" || subType === "address") { var trCr = 0; diff --git a/mix/qml/ScenarioButton.qml b/mix/qml/ScenarioButton.qml index dbe6dd13a..4dd7bc53f 100644 --- a/mix/qml/ScenarioButton.qml +++ b/mix/qml/ScenarioButton.qml @@ -4,64 +4,64 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls.Styles 1.1 Rectangle { - id: buttonActionContainer - property string text - property string buttonShortcut - property string sourceImg - property string fillColor - signal clicked + id: buttonActionContainer + property string text + property string buttonShortcut + property string sourceImg + property string fillColor + signal clicked - Rectangle { - id: contentRectangle - anchors.fill: parent - border.color: "#cccccc" - border.width: 1 - radius: 4 - color: parent.fillColor ? parent.fillColor : "white" - Image { - id: debugImage - anchors { - left: parent.left - right: parent.right - top: parent.top - bottom: parent.bottom - bottomMargin: debugImg.pressed ? 0 : 2; - topMargin: debugImg.pressed ? 2 : 0; - } - source: sourceImg - fillMode: Image.PreserveAspectFit - height: 30 - } + Rectangle { + id: contentRectangle + anchors.fill: parent + border.color: "#cccccc" + border.width: 1 + radius: 4 + color: parent.fillColor ? parent.fillColor : "white" + Image { + id: debugImage + anchors { + left: parent.left + right: parent.right + top: parent.top + bottom: parent.bottom + bottomMargin: debugImg.pressed ? 0 : 2; + topMargin: debugImg.pressed ? 2 : 0; + } + source: sourceImg + fillMode: Image.PreserveAspectFit + height: 30 + } - Button { - anchors.fill: parent - id: debugImg - action: buttonAction - style: ButtonStyle { - background: Rectangle { - color: "transparent" - } - } - } + Button { + anchors.fill: parent + id: debugImg + action: buttonAction + style: ButtonStyle { + background: Rectangle { + color: "transparent" + } + } + } - Action { - id: buttonAction - shortcut: buttonShortcut - onTriggered: { - buttonActionContainer.clicked(); - } - } - } + Action { + id: buttonAction + shortcut: buttonShortcut + onTriggered: { + buttonActionContainer.clicked(); + } + } + } - Rectangle - { - anchors.top: contentRectangle.bottom - anchors.topMargin: 15 - width: parent.width - Text - { - text: buttonActionContainer.text - anchors.centerIn: parent - } - } + Rectangle + { + anchors.top: contentRectangle.bottom + anchors.topMargin: 15 + width: parent.width + Text + { + text: buttonActionContainer.text + anchors.centerIn: parent + } + } } diff --git a/mix/qml/ScenarioExecution.qml b/mix/qml/ScenarioExecution.qml index a5053e4d7..bc872bff7 100644 --- a/mix/qml/ScenarioExecution.qml +++ b/mix/qml/ScenarioExecution.qml @@ -9,49 +9,49 @@ import "js/ErrorLocationFormater.js" as ErrorLocationFormater import "." Rectangle { - color: "#ededed" - property alias bc: blockChain + color: "#ededed" + property alias bc: blockChain - Connections - { - target: projectModel - onProjectLoaded: { - loader.init() - } + Connections + { + target: projectModel + onProjectLoaded: { + loader.init() + } - } + } - Column - { - anchors.margins: 10 - anchors.fill: parent - spacing: 10 - ScenarioLoader - { - height: 70 - width: parent.width - id: loader - } + Column + { + anchors.margins: 10 + anchors.fill: parent + spacing: 10 + ScenarioLoader + { + height: 70 + width: parent.width + id: loader + } - Rectangle - { - width: parent.width - height: 1 - color: "#cccccc" - } + Rectangle + { + width: parent.width + height: 1 + color: "#cccccc" + } - Connections - { - target: loader - onLoaded: { - blockChain.load(scenario) - } - } + Connections + { + target: loader + onLoaded: { + blockChain.load(scenario) + } + } - BlockChain - { - id: blockChain - width: parent.width - } - } + BlockChain + { + id: blockChain + width: parent.width + } + } } diff --git a/mix/qml/ScenarioLoader.qml b/mix/qml/ScenarioLoader.qml index 50e958a5c..93f4f059b 100644 --- a/mix/qml/ScenarioLoader.qml +++ b/mix/qml/ScenarioLoader.qml @@ -12,164 +12,164 @@ import "." RowLayout { - signal restored(variant scenario) - signal saved(variant scenario) - signal duplicated(variant scenario) - signal loaded(variant scenario) - spacing: 0 - function init() - { - scenarioList.load() - } - - id: blockChainSelector - - Dialog { - id: newStateWin - modality: Qt.ApplicationModal - title: qsTr("New Project"); - - width: 320 - height: 120 - - visible: false - - contentItem: Rectangle { - anchors.fill: parent - anchors.margins: 10 - RowLayout { - anchors.verticalCenter: parent.verticalCenter - Text { - text: qsTr("Name:") - } - - Rectangle - { - Layout.preferredWidth: 250 - Layout.preferredHeight: parent.height - border.width: 1 - border.color: "#cccccc" - TextInput - { - anchors.fill: parent - id: stateName - } - } - } - RowLayout - { - anchors.bottom: parent.bottom - anchors.right: parent.right; - function acceptAndClose() - { - var item = projectModel.stateListModel.createDefaultState(); - item.title = stateName.text - projectModel.stateListModel.appendState(item) - projectModel.stateListModel.save() - close() - scenarioList.currentIndex = projectModel.stateListModel.count - 1 - } - - function close() - { - newStateWin.close() - stateName.text = "" - } - - Button { - id: okButton; - enabled: stateName.text !== "" - text: qsTr("OK"); - onClicked: { - parent.acceptAndClose(); - } - } - Button { - text: qsTr("Cancel"); - onClicked: parent.close(); - } - } - } - } - - ComboBox - { - id: scenarioList - model: projectModel.stateListModel - textRole: "title" - onCurrentIndexChanged: - { - restoreScenario.restore() - } - - function load() - { - var state = projectModel.stateListModel.getState(currentIndex) - loaded(state) - } - } - - Row - { - Layout.preferredWidth: 100 * 4 - Layout.preferredHeight: 30 - spacing: 0 - ScenarioButton { - id: restoreScenario - width: 100 - height: 30 - buttonShortcut: "" - sourceImg: "qrc:/qml/img/restoreicon@2x.png" - onClicked: { - restore() - } - text: qsTr("Restore") - function restore() - { - var state = projectModel.stateListModel.reloadStateFromFromProject(scenarioList.currentIndex) - restored(state) - loaded(state) - } - } - - ScenarioButton { - id: saveScenario - text: qsTr("Save") - onClicked: { - projectModel.saveProjectFile() - saved(state) - } - width: 100 - height: 30 - buttonShortcut: "" - sourceImg: "qrc:/qml/img/saveicon@2x.png" - } - - ScenarioButton - { - id: duplicateScenario - text: qsTr("Duplicate") - onClicked: { - projectModel.stateListModel.duplicateState(scenarioList.currentIndex) - duplicated(state) - } - width: 100 - height: 30 - buttonShortcut: "" - sourceImg: "qrc:/qml/img/duplicateicon@2x.png" - } - - ScenarioButton { - id: addScenario - width: 100 - height: 30 - buttonShortcut: "" - sourceImg: "qrc:/qml/img/plus.png" - onClicked: { - newStateWin.open() - } - text: qsTr("New") - } - } + signal restored(variant scenario) + signal saved(variant scenario) + signal duplicated(variant scenario) + signal loaded(variant scenario) + spacing: 0 + function init() + { + scenarioList.load() + } + + id: blockChainSelector + + Dialog { + id: newStateWin + modality: Qt.ApplicationModal + title: qsTr("New Project"); + + width: 320 + height: 120 + + visible: false + + contentItem: Rectangle { + anchors.fill: parent + anchors.margins: 10 + RowLayout { + anchors.verticalCenter: parent.verticalCenter + Text { + text: qsTr("Name:") + } + + Rectangle + { + Layout.preferredWidth: 250 + Layout.preferredHeight: parent.height + border.width: 1 + border.color: "#cccccc" + TextInput + { + anchors.fill: parent + id: stateName + } + } + } + RowLayout + { + anchors.bottom: parent.bottom + anchors.right: parent.right; + function acceptAndClose() + { + var item = projectModel.stateListModel.createDefaultState(); + item.title = stateName.text + projectModel.stateListModel.appendState(item) + projectModel.stateListModel.save() + close() + scenarioList.currentIndex = projectModel.stateListModel.count - 1 + } + + function close() + { + newStateWin.close() + stateName.text = "" + } + + Button { + id: okButton; + enabled: stateName.text !== "" + text: qsTr("OK"); + onClicked: { + parent.acceptAndClose(); + } + } + Button { + text: qsTr("Cancel"); + onClicked: parent.close(); + } + } + } + } + + ComboBox + { + id: scenarioList + model: projectModel.stateListModel + textRole: "title" + onCurrentIndexChanged: + { + restoreScenario.restore() + } + + function load() + { + var state = projectModel.stateListModel.getState(currentIndex) + loaded(state) + } + } + + Row + { + Layout.preferredWidth: 100 * 4 + Layout.preferredHeight: 30 + spacing: 0 + ScenarioButton { + id: restoreScenario + width: 100 + height: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/restoreicon@2x.png" + onClicked: { + restore() + } + text: qsTr("Restore") + function restore() + { + var state = projectModel.stateListModel.reloadStateFromFromProject(scenarioList.currentIndex) + restored(state) + loaded(state) + } + } + + ScenarioButton { + id: saveScenario + text: qsTr("Save") + onClicked: { + projectModel.saveProjectFile() + saved(state) + } + width: 100 + height: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/saveicon@2x.png" + } + + ScenarioButton + { + id: duplicateScenario + text: qsTr("Duplicate") + onClicked: { + projectModel.stateListModel.duplicateState(scenarioList.currentIndex) + duplicated(state) + } + width: 100 + height: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/duplicateicon@2x.png" + } + + ScenarioButton { + id: addScenario + width: 100 + height: 30 + buttonShortcut: "" + sourceImg: "qrc:/qml/img/plus.png" + onClicked: { + newStateWin.open() + } + text: qsTr("New") + } + } } diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index f056acc22..992113cc6 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -16,32 +16,33 @@ Item { property string defaultAccount: "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074" //support for old project function fromPlainStateItem(s) { - if (!s.accounts) + if (!s.accounts) s.accounts = [stateListModel.newAccount("1000000", QEther.Ether, defaultAccount)]; //support for old project if (!s.contracts) s.contracts = []; - var ret = {}; - ret.title = s.title; - ret.transactions = s.transactions.filter(function(t) { return !t.stdContract; }).map(fromPlainTransactionItem); //support old projects by filtering std contracts - if (s.blocks) - ret.blocks = s.blocks.map(fromPlainBlockItem); - ret.accounts = s.accounts.map(fromPlainAccountItem); - ret.contracts = s.contracts.map(fromPlainAccountItem); - ret.miner = s.miner; - - // support old projects - if (!ret.blocks) - { - ret.blocks = [{ - hash: "", - number: -1, - transactions: [], - status: "pending" - }] - for (var j in ret.transactions) - ret.blocks[0].transactions.push(fromPlainTransactionItem(toPlainTransactionItem(ret.transactions[j]))) - } + var ret = {}; + ret.title = s.title; + ret.transactions = s.transactions.filter(function(t) { return !t.stdContract; }).map(fromPlainTransactionItem); //support old projects by filtering std contracts + if (s.blocks) + ret.blocks = s.blocks.map(fromPlainBlockItem); + ret.accounts = s.accounts.map(fromPlainAccountItem); + ret.contracts = s.contracts.map(fromPlainAccountItem); + ret.miner = s.miner; + + // support old projects + if (!ret.blocks) + { + ret.blocks = [{ + hash: "", + number: -1, + transactions: [], + status: "pending" + }] + for (var j in ret.transactions) + ret.blocks[0].transactions.push(fromPlainTransactionItem(toPlainTransactionItem(ret.transactions[j]))) + } + return ret; } function fromPlainAccountItem(t) @@ -73,12 +74,12 @@ Item { sender: t.sender, isContractCreation: t.isContractCreation, label: t.label, - isFunctionCall: t.isFunctionCall, - saveStatus: t.saveStatus + isFunctionCall: t.isFunctionCall, + saveStatus: t.saveStatus }; - if (r.saveStatus === undefined) - r.saveStatus = true + if (r.saveStatus === undefined) + r.saveStatus = true if (r.isFunctionCall === undefined) r.isFunctionCall = true; @@ -95,22 +96,22 @@ Item { return r; } - function fromPlainBlockItem(b) - { - var r = { - hash: b.hash, - number: b.number, - transactions: b.transactions.filter(function(t) { return !t.stdContract; }).map(fromPlainTransactionItem), //support old projects by filtering std contracts - status: b.status - } - return r; - } + function fromPlainBlockItem(b) + { + var r = { + hash: b.hash, + number: b.number, + transactions: b.transactions.filter(function(t) { return !t.stdContract; }).map(fromPlainTransactionItem), //support old projects by filtering std contracts + status: b.status + } + return r; + } function toPlainStateItem(s) { return { title: s.title, - blocks: s.blocks.map(toPlainBlockItem), - transactions: s.transactions.map(toPlainTransactionItem), + blocks: s.blocks.map(toPlainBlockItem), + transactions: s.transactions.map(toPlainTransactionItem), accounts: s.accounts.map(toPlainAccountItem), contracts: s.contracts.map(toPlainAccountItem), miner: s.miner @@ -127,16 +128,16 @@ Item { return ''; } - function toPlainBlockItem(b) - { - var r = { - hash: b.hash, - number: b.number, - transactions: b.transactions.map(toPlainTransactionItem), - status: b.status - } - return r; - } + function toPlainBlockItem(b) + { + var r = { + hash: b.hash, + number: b.number, + transactions: b.transactions.map(toPlainTransactionItem), + status: b.status + } + return r; + } function toPlainAccountItem(t) { @@ -167,8 +168,8 @@ Item { parameters: {}, isContractCreation: t.isContractCreation, label: t.label, - isFunctionCall: t.isFunctionCall, - saveStatus: t.saveStatus + isFunctionCall: t.isFunctionCall, + saveStatus: t.saveStatus }; for (var key in t.parameters) r.parameters[key] = t.parameters[key]; @@ -189,16 +190,16 @@ Item { projectData.states.push(toPlainStateItem(stateList[i])); } projectData.defaultStateIndex = stateListModel.defaultStateIndex; - stateListModel.data = projectData + stateListModel.data = projectData - } + } onNewProject: { var state = toPlainStateItem(stateListModel.createDefaultState()); state.title = qsTr("Default"); projectData.states = [ state ]; projectData.defaultStateIndex = 0; - stateListModel.loadStatesFromProject(projectData); - } + stateListModel.loadStatesFromProject(projectData); + } } Connections { @@ -215,40 +216,40 @@ Item { id: stateDialog onAccepted: { var item = stateDialog.getItem(); - saveState(item); + saveState(item); } - function saveState(item) - { - if (stateDialog.stateIndex < stateListModel.count) { - if (stateDialog.isDefault) - stateListModel.defaultStateIndex = stateIndex; - stateList[stateDialog.stateIndex] = item; - stateListModel.set(stateDialog.stateIndex, item); - } else { - if (stateDialog.isDefault) - stateListModel.defaultStateIndex = 0; - stateList.push(item); - stateListModel.append(item); - } - if (stateDialog.isDefault) - stateListModel.defaultStateChanged(); - stateListModel.save(); - } + function saveState(item) + { + if (stateDialog.stateIndex < stateListModel.count) { + if (stateDialog.isDefault) + stateListModel.defaultStateIndex = stateIndex; + stateList[stateDialog.stateIndex] = item; + stateListModel.set(stateDialog.stateIndex, item); + } else { + if (stateDialog.isDefault) + stateListModel.defaultStateIndex = 0; + stateList.push(item); + stateListModel.append(item); + } + if (stateDialog.isDefault) + stateListModel.defaultStateChanged(); + stateListModel.save(); + } } ListModel { id: stateListModel property int defaultStateIndex: 0 - property variant data - signal defaultStateChanged; + property variant data + signal defaultStateChanged; signal stateListModelReady; signal stateRun(int index) signal stateDeleted(int index) function defaultTransactionItem() { return TransactionHelper.defaultTransaction(); - } + } function newAccount(_balance, _unit, _secret) { @@ -259,35 +260,35 @@ Item { return { name: name, secret: _secret, balance: QEtherHelper.createEther(_balance, _unit), address: address }; } - function duplicateState(index) - { - var state = stateList[index] - var item = fromPlainStateItem(toPlainStateItem(state)) - item.title = qsTr("Copy of") + " " + state.title - appendState(item) - save() - } - - function createEmptyBlock() - { - return { - hash: "", - number: -1, - transactions: [], - status: "pending" - } - } + function duplicateState(index) + { + var state = stateList[index] + var item = fromPlainStateItem(toPlainStateItem(state)) + item.title = qsTr("Copy of") + " " + state.title + appendState(item) + save() + } + + function createEmptyBlock() + { + return { + hash: "", + number: -1, + transactions: [], + status: "pending" + } + } function createDefaultState() { var item = { title: "", transactions: [], accounts: [], - contracts: [], - blocks: [{ status: "pending", number: -1, hash: "", transactions: []}] - }; + contracts: [], + blocks: [{ status: "pending", number: -1, hash: "", transactions: []}] + }; - var account = newAccount("1000000", QEther.Ether, defaultAccount) + var account = newAccount("1000000", QEther.Ether, defaultAccount) item.accounts.push(account); item.miner = account; @@ -299,8 +300,8 @@ Item { ctorTr.label = qsTr("Deploy") + " " + ctorTr.contractId; ctorTr.sender = item.accounts[0].secret; item.transactions.push(ctorTr); - item.blocks[0].transactions.push(ctorTr) - } + item.blocks[0].transactions.push(ctorTr) + } return item; } @@ -356,19 +357,19 @@ Item { stateDialog.open(stateListModel.count, item, false); } - function appendState(item) - { - stateListModel.append(item); - stateList.push(item); - } + function appendState(item) + { + stateListModel.append(item); + stateList.push(item); + } function editState(index) { stateDialog.open(index, stateList[index], defaultStateIndex === index); } - function getState(index) { - return stateList[index]; - } + function getState(index) { + return stateList[index]; + } function debugDefaultState() { if (defaultStateIndex >= 0 && defaultStateIndex < stateList.length) @@ -377,8 +378,8 @@ Item { function runState(index) { var item = stateList[index]; - //clientModel.setupState(item); - //stateRun(index); + clientModel.setupScenario(item); + stateRun(index); } function deleteState(index) { @@ -404,20 +405,20 @@ Item { return stateList[defaultStateIndex].title; } - function reloadStateFromFromProject(index) - { - if (data) - { - var item = fromPlainStateItem(data.states[index]) - stateListModel.set(index, item) - stateList[index] = item - return item - } - } + function reloadStateFromFromProject(index) + { + if (data) + { + var item = fromPlainStateItem(data.states[index]) + stateListModel.set(index, item) + stateList[index] = item + return item + } + } function loadStatesFromProject(projectData) { - data = projectData + data = projectData if (!projectData.states) projectData.states = []; if (projectData.defaultStateIndex !== undefined) diff --git a/mix/qml/StructView.qml b/mix/qml/StructView.qml index 9cb461204..880f22057 100644 --- a/mix/qml/StructView.qml +++ b/mix/qml/StructView.qml @@ -9,7 +9,7 @@ Column property alias members: repeater.model //js array property variant accounts property var value: ({}) - property int blockIndex + property int blockIndex property int transactionIndex property string context Layout.fillWidth: true diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 56810b375..287ba08c2 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -17,7 +17,7 @@ Dialog { visible: false title: qsTr("Edit Transaction") property int transactionIndex - property int blockIndex + property int blockIndex property alias gas: gasValueEdit.gasValue; property alias gasAuto: gasAutoCheck.checked; property alias gasPrice: gasPriceField.value; @@ -28,24 +28,24 @@ Dialog { property var paramsModel: []; property bool useTransactionDefaultValue: false property alias stateAccounts: senderComboBox.model - property bool saveStatus + property bool saveStatus signal accepted; StateDialogStyle { id: transactionDialogStyle } - function open(index, blockIdx, item) { + function open(index, blockIdx, item) { rowFunction.visible = !useTransactionDefaultValue; rowValue.visible = !useTransactionDefaultValue; rowGas.visible = !useTransactionDefaultValue; rowGasPrice.visible = !useTransactionDefaultValue; - transactionIndex = index - blockIndex = blockIdx - typeLoader.transactionIndex = index - typeLoader.blockIndex = blockIdx - saveStatus = item.saveStatus + transactionIndex = index + blockIndex = blockIdx + typeLoader.transactionIndex = index + typeLoader.blockIndex = blockIdx + saveStatus = item.saveStatus gasValueEdit.gasValue = item.gas; gasAutoCheck.checked = item.gasAuto ? true : false; gasPriceField.value = item.gasPrice; @@ -64,7 +64,7 @@ Dialog { for (var c in contracts) { contractsModel.append({ cid: c, text: contracts[c].contract.name }); if (contracts[c].contract.name === contractId) - contractIndex = contractsModel.count - 1; + contractIndex = contractsModel.count - 1; } if (contractIndex == -1 && contractsModel.count > 0) @@ -233,7 +233,7 @@ Dialog { item.functionId = item.contractId; item.label = qsTr("Deploy") + " " + item.contractId; } - item.saveStatus = saveStatus + item.saveStatus = saveStatus item.sender = senderComboBox.model[senderComboBox.currentIndex].secret; item.parameters = paramValues; return item; diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js index 1e6169e5d..31f30e2da 100644 --- a/mix/qml/js/ProjectModel.js +++ b/mix/qml/js/ProjectModel.js @@ -82,9 +82,9 @@ function saveProjectFile() }; for (var i = 0; i < projectListModel.count; i++) projectData.files.push({ - title: projectListModel.get(i).name, - fileName: projectListModel.get(i).fileName, - }); + title: projectListModel.get(i).name, + fileName: projectListModel.get(i).fileName, + }); projectFileSaving(projectData); var json = JSON.stringify(projectData, null, "\t"); @@ -98,52 +98,52 @@ function saveProjectFile() function loadProject(path) { closeProject(function() { - console.log("Loading project at " + path); - var projectFile = path + projectFileName; - var json = fileIo.readFile(projectFile); - if (!json) - return; - var projectData = JSON.parse(json); - if (projectData.deploymentDir) - projectModel.deploymentDir = projectData.deploymentDir - if (projectData.packageHash) - deploymentDialog.packageHash = projectData.packageHash - if (projectData.packageBase64) - deploymentDialog.packageBase64 = projectData.packageBase64 - if (projectData.applicationUrlEth) - deploymentDialog.applicationUrlEth = projectData.applicationUrlEth - if (projectData.applicationUrlHttp) - deploymentDialog.applicationUrlHttp = projectData.applicationUrlHttp - if (!projectData.title) { - var parts = path.split("/"); - projectData.title = parts[parts.length - 2]; - } - deploymentAddresses = projectData.deploymentAddresses ? projectData.deploymentAddresses : []; - projectTitle = projectData.title; - projectPath = path; - if (!projectData.files) - projectData.files = []; - - for(var i = 0; i < projectData.files.length; i++) { - var entry = projectData.files[i]; - if (typeof(entry) === "string") - addFile(entry); //TODO: remove old project file support - else - addFile(entry.fileName, entry.title); - } - if (mainApplication.trackLastProject) - projectSettings.lastProjectPath = path; - projectLoading(projectData); - projectLoaded() - - //TODO: move this to codemodel - var contractSources = {}; - for (var d = 0; d < listModel.count; d++) { - var doc = listModel.get(d); - if (doc.isContract) - contractSources[doc.documentId] = fileIo.readFile(doc.path); - } - codeModel.reset(contractSources); + console.log("Loading project at " + path); + var projectFile = path + projectFileName; + var json = fileIo.readFile(projectFile); + if (!json) + return; + var projectData = JSON.parse(json); + if (projectData.deploymentDir) + projectModel.deploymentDir = projectData.deploymentDir + if (projectData.packageHash) + deploymentDialog.packageHash = projectData.packageHash + if (projectData.packageBase64) + deploymentDialog.packageBase64 = projectData.packageBase64 + if (projectData.applicationUrlEth) + deploymentDialog.applicationUrlEth = projectData.applicationUrlEth + if (projectData.applicationUrlHttp) + deploymentDialog.applicationUrlHttp = projectData.applicationUrlHttp + if (!projectData.title) { + var parts = path.split("/"); + projectData.title = parts[parts.length - 2]; + } + deploymentAddresses = projectData.deploymentAddresses ? projectData.deploymentAddresses : []; + projectTitle = projectData.title; + projectPath = path; + if (!projectData.files) + projectData.files = []; + + for(var i = 0; i < projectData.files.length; i++) { + var entry = projectData.files[i]; + if (typeof(entry) === "string") + addFile(entry); //TODO: remove old project file support + else + addFile(entry.fileName, entry.title); + } + if (mainApplication.trackLastProject) + projectSettings.lastProjectPath = path; + projectLoading(projectData); + projectLoaded() + + //TODO: move this to codemodel + var contractSources = {}; + for (var d = 0; d < listModel.count; d++) { + var doc = listModel.get(d); + if (doc.isContract) + contractSources[doc.documentId] = fileIo.readFile(doc.path); + } + codeModel.reset(contractSources); }); } @@ -162,12 +162,12 @@ function addFile(fileName, title) { path: p, fileName: fileName, name: title !== undefined ? title : fileName, - documentId: fileName, - syntaxMode: syntaxMode, - isText: isContract || isHtml || isCss || isJs, - isContract: isContract, - isHtml: isHtml, - groupName: groupName + documentId: fileName, + syntaxMode: syntaxMode, + isText: isContract || isHtml || isCss || isJs, + isContract: isContract, + isHtml: isHtml, + groupName: groupName }; projectListModel.append(docData); diff --git a/mix/qml/js/TransactionHelper.js b/mix/qml/js/TransactionHelper.js index 8a7c5f5b8..f8bad03ed 100644 --- a/mix/qml/js/TransactionHelper.js +++ b/mix/qml/js/TransactionHelper.js @@ -12,8 +12,8 @@ function defaultTransaction() stdContract: false, isContractCreation: true, label: "", - isFunctionCall: true, - saveStatus: true + isFunctionCall: true, + saveStatus: true }; } From cb380d66bbbee9f5f92e9a6c1393b4db4cf3fc89 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 10 Jun 2015 14:34:24 +0200 Subject: [PATCH 17/69] - allow ignore nonce checking --- libethereum/BlockChain.cpp | 10 +++++++--- libethereum/BlockChain.h | 2 +- mix/MixClient.cpp | 4 ++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 479524f89..1d53e73f9 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -357,7 +357,7 @@ pair BlockChain::attemptImport(bytes const& _block, O { try { - return make_pair(ImportResult::Success, import(verifyBlock(_block, m_onBad), _stateDB, _ir)); + return make_pair(ImportResult::Success, import(verifyBlock(_block, m_onBad, _ir), _stateDB, _ir)); } catch (UnknownParent&) { @@ -1063,12 +1063,16 @@ bytes BlockChain::block(h256 const& _hash) const return m_blocks[_hash]; } -VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function const& _onBad) +VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function const& _onBad, ImportRequirements::value _ir) { VerifiedBlockRef res; try { - res.info.populate(_block, CheckEverything); + Strictness strictNess = Strictness::CheckEverything; + if (_ir & ~ImportRequirements::ValidNonce) + strictNess = Strictness::IgnoreNonce; + + res.info.populate(_block, strictNess); res.info.verifyInternals(&_block); } catch (Exception& ex) diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 4934d4f2a..1aa15b856 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -258,7 +258,7 @@ public: void garbageCollect(bool _force = false); /// Verify block and prepare it for enactment - static VerifiedBlockRef verifyBlock(bytes const& _block, std::function const& _onBad = std::function()); + static VerifiedBlockRef verifyBlock(bytes const& _block, std::function const& _onBad = std::function(), ImportRequirements::value _ir = ImportRequirements::Default); /// Change the function that is called with a bad block. template void setOnBad(T const& _t) { m_onBad = _t; } diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 31b4772b4..df3795c7e 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -73,6 +73,10 @@ MixClient::MixClient(std::string const& _dbPath): resetState(std::unordered_map()); } +MixClient::~MixClient() +{ +} + void MixClient::resetState(std::unordered_map const& _accounts, Secret const& _miner) { From 8605b8c9ce604ba55cad80c8f837d2fdc0e77bbb Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 11 Jun 2015 09:25:19 +0200 Subject: [PATCH 18/69] use all peers for re-syncing --- libethereum/EthereumHost.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 0c59d3839..33f95695b 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -367,7 +367,7 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool if (_complete) { m_needSyncBlocks = true; - continueSync(_peer); + continueSync(); } else if (syncByNumber && m_hashMan.isComplete()) { @@ -540,7 +540,7 @@ void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r) _peer->m_knownBlocks.insert(h); if (sync) - continueSync(_peer); + continueSync(); } } From 0c08092f6a040bfc194061510d58937c0384da8f Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 11 Jun 2015 09:48:04 +0200 Subject: [PATCH 19/69] Fix the chunks auto detecting code --- libethash-cl/ethash_cl_miner.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp index 6907a7bd0..1a5eaddc3 100644 --- a/libethash-cl/ethash_cl_miner.cpp +++ b/libethash-cl/ethash_cl_miner.cpp @@ -267,9 +267,19 @@ bool ethash_cl_miner::init( ETHCL_LOG("Using device: " << device.getInfo().c_str() << "(" << device_version.c_str() << ")"); // configure chunk number depending on max allocateable memory - cl_ulong result; + cl_ulong result; device.getInfo(CL_DEVICE_MAX_MEM_ALLOC_SIZE, &result); - m_dagChunksNum = result >= ETHASH_CL_MINIMUM_MEMORY ? 4 : 1; + if (result >= ETHASH_CL_MINIMUM_MEMORY) + { + m_dagChunksNum = 1; + ETHCL_LOG("Using 1 big chunk. Max OpenCL allocateable memory is" << result); + } + else + { + m_dagChunksNum = 4; + ETHCL_LOG("Using 4 chunks. Max OpenCL allocateable memory is" << result); + } + if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0) { ETHCL_LOG("OpenCL 1.0 is not supported."); From 5a6f6a55f2d36a2d7cd9d1b1b0baf78bcee206f3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 11 Jun 2015 17:54:11 +0900 Subject: [PATCH 20/69] Cleanups and fix for sending transactions when not syncing. --- libethereum/Client.cpp | 9 +++------ libethereum/EthereumHost.cpp | 3 ++- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index d1ede38ac..5581d1071 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -614,13 +614,10 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution) void Client::syncBlockQueue() { ImportRoute ir; - cwork << "BQ ==> CHAIN ==> STATE"; - { - tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 90 + 10); - if (ir.first.empty()) - return; - } + tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 10 + 5); + if (ir.first.empty()) + return; onChainChanged(ir); } diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 0c59d3839..db4eabd14 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -93,7 +93,7 @@ void EthereumHost::doWork() bool netChange = ensureInitialised(); auto h = m_chain.currentHash(); // If we've finished our initial sync (including getting all the blocks into the chain so as to reduce invalid transactions), start trading transactions & blocks - if (isSyncing() && m_chain.isKnown(m_latestBlockSent)) + if (!isSyncing() && m_chain.isKnown(m_latestBlockSent)) { if (m_newTransactions) { @@ -152,6 +152,7 @@ void EthereumHost::maintainTransactions() RLPStream ts; _p->prep(ts, TransactionsPacket, n).appendRaw(b, n); _p->sealAndSend(ts); + cnote << "Sent" << n << "transactions to " << _p->session()->info().clientVersion; } _p->m_requireTransactions = false; }); From cf4c191a98d3950fe61daa9764cbbf09805498b0 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 11 Jun 2015 12:27:39 +0200 Subject: [PATCH 21/69] CLMiner, query only GPU devices By default now, CPU is not considered an OpenCL device. Also added a new argument --allow-opencl-cpu, that would allow OpenCL to include CPU devices if the user's openCL implementation caters for it. --- ethminer/MinerAux.h | 2 ++ libethash-cl/ethash_cl_miner.cpp | 36 ++++++++++++++++++-------- libethash-cl/ethash_cl_miner.h | 5 ++++ libethash-cl/ethash_cl_miner_kernel.cl | 4 +-- libethcore/Ethash.cpp | 5 ++++ libethcore/Ethash.h | 2 ++ 6 files changed, 40 insertions(+), 14 deletions(-) diff --git a/ethminer/MinerAux.h b/ethminer/MinerAux.h index 476c810c2..aac68f1a7 100644 --- a/ethminer/MinerAux.h +++ b/ethminer/MinerAux.h @@ -132,6 +132,8 @@ public: ProofOfWork::GPUMiner::listDevices(); exit(0); } + else if (arg == "--allow-opencl-cpu") + ProofOfWork::GPUMiner::allowCPU(); else if (arg == "--phone-home" && i + 1 < argc) { string m = argv[++i]; diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp index 1a5eaddc3..054985a38 100644 --- a/libethash-cl/ethash_cl_miner.cpp +++ b/libethash-cl/ethash_cl_miner.cpp @@ -51,6 +51,8 @@ using namespace std; // TODO: If at any point we can use libdevcore in here then we should switch to using a LogChannel #define ETHCL_LOG(_contents) cout << "[OPENCL]:" << _contents << endl +// Types of OpenCL devices we are interested in +#define ETHCL_QUERIED_DEVICE_TYPES (CL_DEVICE_TYPE_GPU | CL_DEVICE_TYPE_ACCELERATOR) static void addDefinition(string& _source, char const* _id, unsigned _value) { @@ -82,9 +84,8 @@ string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _deviceId) } // get GPU device of the selected platform - vector devices; unsigned platform_num = min(_platformId, platforms.size() - 1); - platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices); + vector devices = getDevices(platforms, _platformId); if (devices.empty()) { ETHCL_LOG("No OpenCL devices found."); @@ -99,6 +100,17 @@ string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _deviceId) return "{ \"platform\": \"" + platforms[platform_num].getInfo() + "\", \"device\": \"" + device.getInfo() + "\", \"version\": \"" + device_version + "\" }"; } +std::vector ethash_cl_miner::getDevices(std::vector const& _platforms, unsigned _platformId) +{ + vector devices; + unsigned platform_num = min(_platformId, _platforms.size() - 1); + _platforms[platform_num].getDevices( + s_allowCPU ? CL_DEVICE_TYPE_ALL : ETHCL_QUERIED_DEVICE_TYPES, + &devices + ); + return devices; +} + unsigned ethash_cl_miner::getNumPlatforms() { vector platforms; @@ -116,9 +128,7 @@ unsigned ethash_cl_miner::getNumDevices(unsigned _platformId) return 0; } - vector devices; - unsigned platform_num = min(_platformId, platforms.size() - 1); - platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices); + vector devices = getDevices(platforms, _platformId); if (devices.empty()) { ETHCL_LOG("No OpenCL devices found."); @@ -152,6 +162,13 @@ bool ethash_cl_miner::configureGPU() ); } +bool ethash_cl_miner::s_allowCPU = false; + +void ethash_cl_miner::allowCPU() +{ + s_allowCPU = true; +} + bool ethash_cl_miner::searchForAllDevices(function _callback) { vector platforms; @@ -175,8 +192,7 @@ bool ethash_cl_miner::searchForAllDevices(unsigned _platformId, function= platforms.size()) return false; - vector devices; - platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices); + vector devices = getDevices(platforms, _platformId); for (cl::Device const& device: devices) if (_callback(device)) return true; @@ -204,8 +220,7 @@ void ethash_cl_miner::doForAllDevices(unsigned _platformId, function= platforms.size()) return; - vector devices; - platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices); + vector devices = getDevices(platforms, _platformId); for (cl::Device const& device: devices) _callback(device); } @@ -253,8 +268,7 @@ bool ethash_cl_miner::init( ETHCL_LOG("Using platform: " << platforms[_platformId].getInfo().c_str()); // get GPU device of the default platform - vector devices; - platforms[_platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices); + vector devices = getDevices(platforms, _platformId); if (devices.empty()) { ETHCL_LOG("No OpenCL devices found."); diff --git a/libethash-cl/ethash_cl_miner.h b/libethash-cl/ethash_cl_miner.h index 4c986053a..dc3f7eca0 100644 --- a/libethash-cl/ethash_cl_miner.h +++ b/libethash-cl/ethash_cl_miner.h @@ -41,6 +41,7 @@ public: static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0); static void listDevices(); static bool configureGPU(); + static void allowCPU(); bool init( uint8_t const* _dag, @@ -56,6 +57,9 @@ public: void search_chunk(uint8_t const* header, uint64_t target, search_hook& hook); private: + + static std::vector getDevices(std::vector const& _platforms, unsigned _platformId); + enum { c_max_search_results = 63, c_num_buffers = 2, c_hash_batch_size = 1024, c_search_batch_size = 1024*256 }; cl::Context m_context; @@ -70,4 +74,5 @@ private: unsigned m_workgroup_size; bool m_opencl_1_1; + static bool s_allowCPU; }; diff --git a/libethash-cl/ethash_cl_miner_kernel.cl b/libethash-cl/ethash_cl_miner_kernel.cl index 8567bb164..46c94f241 100644 --- a/libethash-cl/ethash_cl_miner_kernel.cl +++ b/libethash-cl/ethash_cl_miner_kernel.cl @@ -275,8 +275,6 @@ uint inner_loop_chunks(uint4 init, uint thread_id, __local uint* share, __global return fnv_reduce(mix); } - - uint inner_loop(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, uint isolate) { uint4 mix = init; @@ -585,4 +583,4 @@ __kernel void ethash_search_chunks( uint slot = min(convert_uint(MAX_OUTPUTS), convert_uint(atomic_inc(&g_output[0]) + 1)); g_output[slot] = gid; } -} \ No newline at end of file +} diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 9ac4474ba..09ecfe1a3 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -366,6 +366,11 @@ void Ethash::GPUMiner::pause() stopWorking(); } +void Ethash::GPUMiner::allowCPU() +{ + return ethash_cl_miner::allowCPU(); +} + std::string Ethash::GPUMiner::platformInfo() { return ethash_cl_miner::platform_info(s_platformId, s_deviceId); diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index 49a8ae006..91ca5f8fd 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -92,6 +92,7 @@ public: static void setDefaultDevice(unsigned) {} static void listDevices() {} static bool configureGPU() { return false; } + static void allowCPU() {} static void setNumInstances(unsigned _instances) { s_numInstances = std::min(_instances, std::thread::hardware_concurrency()); } protected: void kickOff() override @@ -121,6 +122,7 @@ public: static unsigned getNumDevices(); static void listDevices(); static bool configureGPU(); + static void allowCPU(); static void setDefaultPlatform(unsigned _id) { s_platformId = _id; } static void setDefaultDevice(unsigned _id) { s_deviceId = _id; } static void setNumInstances(unsigned _instances) { s_numInstances = std::min(_instances, getNumDevices()); } From 43f8d638fbe5bd3842fe8ace3656cdf8e5b12e54 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Thu, 11 Jun 2015 13:05:58 +0200 Subject: [PATCH 22/69] add transactnonce to ethcli --- eth/main.cpp | 70 ++++++++++++++++++++++++++++++++++++++ libethereum/ClientBase.cpp | 15 ++++++++ libethereum/ClientBase.h | 1 + 3 files changed, 86 insertions(+) diff --git a/eth/main.cpp b/eth/main.cpp index 8b3e1fa78..9128d9391 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -93,6 +93,7 @@ void interactiveHelp() << " accounts Gives information on all owned accounts (balances, mining beneficiary and default signer)." << endl << " newaccount Creates a new account with the given name." << endl << " transact Execute a given transaction." << endl + << " transactnonce Execute a given transaction with a specified nonce." << endl << " txcreate Execute a given contract creation transaction." << endl << " send Execute a given transaction with current secret." << endl << " contract Create a new contract with current secret." << endl @@ -1165,6 +1166,75 @@ int main(int argc, char** argv) else cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA"; } + + else if (c && cmd == "transactnonce") + { + auto const& bc =c->blockChain(); + auto h = bc.currentHash(); + auto blockData = bc.block(h); + BlockInfo info(blockData); + if (iss.peek() != -1) + { + string hexAddr; + u256 amount; + u256 gasPrice; + u256 gas; + string sechex; + string sdata; + u256 nonce; + + iss >> hexAddr >> amount >> gasPrice >> gas >> sechex >> sdata >> nonce; + + if (!gasPrice) + gasPrice = gasPricer->bid(priority); + + cnote << "Data:"; + cnote << sdata; + bytes data = dev::eth::parseData(sdata); + cnote << "Bytes:"; + string sbd = asString(data); + bytes bbd = asBytes(sbd); + stringstream ssbd; + ssbd << bbd; + cnote << ssbd.str(); + int ssize = sechex.length(); + int size = hexAddr.length(); + u256 minGas = (u256)Transaction::gasRequired(data, 0); + if (size < 40) + { + if (size > 0) + cwarn << "Invalid address length:" << size; + } + else if (gas < minGas) + cwarn << "Minimum gas amount is" << minGas; + else if (ssize < 40) + { + if (ssize > 0) + cwarn << "Invalid secret length:" << ssize; + } + else + { + try + { + Secret secret = h256(fromHex(sechex)); + Address dest = h160(fromHex(hexAddr)); + c->submitTransaction(secret, amount, dest, data, gas, gasPrice, nonce); + } + catch (BadHexCharacter& _e) + { + cwarn << "invalid hex character, transaction rejected"; + cwarn << boost::diagnostic_information(_e); + } + catch (...) + { + cwarn << "transaction rejected"; + } + } + } + else + cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA NONCE"; + } + else if (c && cmd == "txcreate") { auto const& bc =c->blockChain(); diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 8dc666bb5..19f0fe737 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -45,6 +45,21 @@ State ClientBase::asOf(BlockNumber _h) const return asOf(bc().numberHash(_h)); } +void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, u256 _nonce) +{ + prepareForTransaction(); + + auto a = toAddress(_secret); + u256 n = postMine().transactionsFrom(a); + cdebug << "submitTx: " << a << "postMine=" << n << "; tq=" << m_tq.maxNonce(a); + + Transaction t(_value, _gasPrice, _gas, _dest, _data, _nonce, _secret); + m_tq.import(t.rlp()); + + StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged()); + cnote << "New transaction " << t << "(maxNonce for sender" << a << "is" << m_tq.maxNonce(a) << ")"; +} + void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) { prepareForTransaction(); diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index 2b271b1df..19bb2088d 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -76,6 +76,7 @@ public: virtual ~ClientBase() {} /// Submits the given message-call transaction. + virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, u256 _nonce); virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override; /// Submits a new contract-creation transaction. From eaee35dd0b6f94b94a2e6946696fdf9848fbc7b0 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 11 Jun 2015 12:58:28 +0200 Subject: [PATCH 23/69] Refactor in OpenCL argument passing - No need for many different functions to set each single option for the miner. First we set all options and then we execute them. This way --list-devices will give different results with --alow-opencl-cpu and without it as it should. - If the user has no GPU with sufficient memory we no longer default to CPU. It's better to throw an error and let the user remove the argument. It's easy to miss the defaulting to CPU log message. --- ethminer/MinerAux.h | 36 ++++++++++++++------------------ libethash-cl/ethash_cl_miner.cpp | 8 ++----- libethash-cl/ethash_cl_miner.h | 3 +-- libethcore/Ethash.cpp | 11 ++++------ libethcore/Ethash.h | 11 ++-------- 5 files changed, 25 insertions(+), 44 deletions(-) diff --git a/ethminer/MinerAux.h b/ethminer/MinerAux.h index aac68f1a7..2816f637a 100644 --- a/ethminer/MinerAux.h +++ b/ethminer/MinerAux.h @@ -128,12 +128,9 @@ public: throw BadArgument(); } else if (arg == "--list-devices") - { - ProofOfWork::GPUMiner::listDevices(); - exit(0); - } + shouldListDevices = true; else if (arg == "--allow-opencl-cpu") - ProofOfWork::GPUMiner::allowCPU(); + clAllowCPU = true; else if (arg == "--phone-home" && i + 1 < argc) { string m = argv[++i]; @@ -177,18 +174,7 @@ public: else if (arg == "-C" || arg == "--cpu") m_minerType = MinerType::CPU; else if (arg == "-G" || arg == "--opencl") - { - if (!ProofOfWork::GPUMiner::configureGPU()) - { - cout << "No GPU device with sufficient memory was found. Defaulting to CPU" << endl; - m_minerType = MinerType::CPU; - } - else - { - m_minerType = MinerType::GPU; - miningThreads = 1; - } - } + m_minerType = MinerType::GPU; else if (arg == "--no-precompute") { precompute = false; @@ -264,13 +250,22 @@ public: void execute() { + if (shouldListDevices) + { + ProofOfWork::GPUMiner::listDevices(); + exit(0); + } + if (m_minerType == MinerType::CPU) ProofOfWork::CPUMiner::setNumInstances(miningThreads); else if (m_minerType == MinerType::GPU) { - ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform); - ProofOfWork::GPUMiner::setDefaultDevice(openclDevice); ProofOfWork::GPUMiner::setNumInstances(miningThreads); + if (!ProofOfWork::GPUMiner::configureGPU(openclPlatform, openclDevice, clAllowCPU)) + { + cout << "No GPU device with sufficient memory was found. Can't GPU mine. Remove the -G argument" << endl; + exit(1); + } } if (mode == OperationMode::DAGInit) doInitDAG(initDAG); @@ -495,7 +490,8 @@ private: unsigned openclPlatform = 0; unsigned openclDevice = 0; unsigned miningThreads = UINT_MAX; - unsigned dagChunks = 1; + bool shouldListDevices = false; + bool clAllowCPU = false; /// DAG initialisation param. unsigned initDAG = 0; diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp index 054985a38..db08d10c3 100644 --- a/libethash-cl/ethash_cl_miner.cpp +++ b/libethash-cl/ethash_cl_miner.cpp @@ -137,8 +137,9 @@ unsigned ethash_cl_miner::getNumDevices(unsigned _platformId) return devices.size(); } -bool ethash_cl_miner::configureGPU() +bool ethash_cl_miner::configureGPU(bool _allowCPU) { + s_allowCPU = _allowCPU; return searchForAllDevices([](cl::Device const _device) -> bool { cl_ulong result; @@ -164,11 +165,6 @@ bool ethash_cl_miner::configureGPU() bool ethash_cl_miner::s_allowCPU = false; -void ethash_cl_miner::allowCPU() -{ - s_allowCPU = true; -} - bool ethash_cl_miner::searchForAllDevices(function _callback) { vector platforms; diff --git a/libethash-cl/ethash_cl_miner.h b/libethash-cl/ethash_cl_miner.h index dc3f7eca0..44ec7eba9 100644 --- a/libethash-cl/ethash_cl_miner.h +++ b/libethash-cl/ethash_cl_miner.h @@ -40,8 +40,7 @@ public: static unsigned getNumDevices(unsigned _platformId = 0); static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0); static void listDevices(); - static bool configureGPU(); - static void allowCPU(); + static bool configureGPU(bool _allowCPU); bool init( uint8_t const* _dag, diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 09ecfe1a3..20ba539d1 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -366,11 +366,6 @@ void Ethash::GPUMiner::pause() stopWorking(); } -void Ethash::GPUMiner::allowCPU() -{ - return ethash_cl_miner::allowCPU(); -} - std::string Ethash::GPUMiner::platformInfo() { return ethash_cl_miner::platform_info(s_platformId, s_deviceId); @@ -386,9 +381,11 @@ void Ethash::GPUMiner::listDevices() return ethash_cl_miner::listDevices(); } -bool Ethash::GPUMiner::configureGPU() +bool Ethash::GPUMiner::configureGPU(unsigned _platformId, unsigned _deviceId, bool _allowCPU) { - return ethash_cl_miner::configureGPU(); + s_platformId = _platformId; + s_deviceId = _deviceId; + return ethash_cl_miner::configureGPU(_allowCPU); } #endif diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index 91ca5f8fd..ee4c7d008 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -87,12 +87,8 @@ public: static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); } static std::string platformInfo(); - static void setDefaultPlatform(unsigned) {} - static void setDagChunks(unsigned) {} - static void setDefaultDevice(unsigned) {} static void listDevices() {} - static bool configureGPU() { return false; } - static void allowCPU() {} + static bool configureGPU(unsigned, unsigned, bool) { return false; } static void setNumInstances(unsigned _instances) { s_numInstances = std::min(_instances, std::thread::hardware_concurrency()); } protected: void kickOff() override @@ -121,10 +117,7 @@ public: static std::string platformInfo(); static unsigned getNumDevices(); static void listDevices(); - static bool configureGPU(); - static void allowCPU(); - static void setDefaultPlatform(unsigned _id) { s_platformId = _id; } - static void setDefaultDevice(unsigned _id) { s_deviceId = _id; } + static bool configureGPU(unsigned _platformId, unsigned _deviceId, bool _allowCPU); static void setNumInstances(unsigned _instances) { s_numInstances = std::min(_instances, getNumDevices()); } protected: From 474705ba8089986056b98958865dacb202a586d2 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 11 Jun 2015 13:26:05 +0200 Subject: [PATCH 24/69] Styling changes --- ethminer/MinerAux.h | 72 +++++++++++++------------- libethash-cl/ethash_cl_miner_kernel.cl | 2 + 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/ethminer/MinerAux.h b/ethminer/MinerAux.h index 2816f637a..65311b019 100644 --- a/ethminer/MinerAux.h +++ b/ethminer/MinerAux.h @@ -97,11 +97,11 @@ public: if ((arg == "-F" || arg == "--farm") && i + 1 < argc) { mode = OperationMode::Farm; - farmURL = argv[++i]; + m_farmURL = argv[++i]; } else if (arg == "--farm-recheck" && i + 1 < argc) try { - farmRecheckPeriod = stol(argv[++i]); + m_farmRecheckPeriod = stol(argv[++i]); } catch (...) { @@ -110,7 +110,7 @@ public: } else if (arg == "--opencl-platform" && i + 1 < argc) try { - openclPlatform = stol(argv[++i]); + m_openclPlatform = stol(argv[++i]); } catch (...) { @@ -119,8 +119,8 @@ public: } else if (arg == "--opencl-device" && i + 1 < argc) try { - openclDevice = stol(argv[++i]); - miningThreads = 1; + m_openclDevice = stol(argv[++i]); + m_miningThreads = 1; } catch (...) { @@ -128,16 +128,16 @@ public: throw BadArgument(); } else if (arg == "--list-devices") - shouldListDevices = true; + m_shouldListDevices = true; else if (arg == "--allow-opencl-cpu") - clAllowCPU = true; + m_clAllowCPU = true; else if (arg == "--phone-home" && i + 1 < argc) { string m = argv[++i]; if (isTrue(m)) - phoneHome = true; + m_phoneHome = true; else if (isFalse(m)) - phoneHome = false; + m_phoneHome = false; else { cerr << "Bad " << arg << " option: " << m << endl; @@ -146,7 +146,7 @@ public: } else if (arg == "--benchmark-warmup" && i + 1 < argc) try { - benchmarkWarmup = stol(argv[++i]); + m_benchmarkWarmup = stol(argv[++i]); } catch (...) { @@ -155,7 +155,7 @@ public: } else if (arg == "--benchmark-trial" && i + 1 < argc) try { - benchmarkTrial = stol(argv[++i]); + m_benchmarkTrial = stol(argv[++i]); } catch (...) { @@ -164,7 +164,7 @@ public: } else if (arg == "--benchmark-trials" && i + 1 < argc) try { - benchmarkTrials = stol(argv[++i]); + m_benchmarkTrials = stol(argv[++i]); } catch (...) { @@ -177,7 +177,7 @@ public: m_minerType = MinerType::GPU; else if (arg == "--no-precompute") { - precompute = false; + m_precompute = false; } else if ((arg == "-D" || arg == "--create-dag") && i + 1 < argc) { @@ -185,7 +185,7 @@ public: mode = OperationMode::DAGInit; try { - initDAG = stol(m); + m_initDAG = stol(m); } catch (...) { @@ -235,7 +235,7 @@ public: else if ((arg == "-t" || arg == "--mining-threads") && i + 1 < argc) { try { - miningThreads = stol(argv[++i]); + m_miningThreads = stol(argv[++i]); } catch (...) { @@ -250,29 +250,29 @@ public: void execute() { - if (shouldListDevices) + if (m_shouldListDevices) { ProofOfWork::GPUMiner::listDevices(); exit(0); } if (m_minerType == MinerType::CPU) - ProofOfWork::CPUMiner::setNumInstances(miningThreads); + ProofOfWork::CPUMiner::setNumInstances(m_miningThreads); else if (m_minerType == MinerType::GPU) { - ProofOfWork::GPUMiner::setNumInstances(miningThreads); - if (!ProofOfWork::GPUMiner::configureGPU(openclPlatform, openclDevice, clAllowCPU)) + ProofOfWork::GPUMiner::setNumInstances(m_miningThreads); + if (!ProofOfWork::GPUMiner::configureGPU(m_openclPlatform, m_openclDevice, m_clAllowCPU)) { cout << "No GPU device with sufficient memory was found. Can't GPU mine. Remove the -G argument" << endl; exit(1); } } if (mode == OperationMode::DAGInit) - doInitDAG(initDAG); + doInitDAG(m_initDAG); else if (mode == OperationMode::Benchmark) - doBenchmark(m_minerType, phoneHome, benchmarkWarmup, benchmarkTrial, benchmarkTrials); + doBenchmark(m_minerType, m_phoneHome, m_benchmarkWarmup, m_benchmarkTrial, m_benchmarkTrials); else if (mode == OperationMode::Farm) - doFarm(m_minerType, farmURL, farmRecheckPeriod); + doFarm(m_minerType, m_farmURL, m_farmRecheckPeriod); } static void streamHelp(ostream& _out) @@ -438,7 +438,7 @@ private: cnote << "Grabbing DAG for" << newSeedHash; if (!(dag = EthashAux::full(newSeedHash, true, [&](unsigned _pc){ cout << "\rCreating DAG. " << _pc << "% done..." << flush; return 0; }))) BOOST_THROW_EXCEPTION(DAGCreationFailure()); - if (precompute) + if (m_precompute) EthashAux::computeFull(sha3(newSeedHash), true); if (hh != current.headerHash) { @@ -487,23 +487,23 @@ private: /// Mining options MinerType m_minerType = MinerType::CPU; - unsigned openclPlatform = 0; - unsigned openclDevice = 0; - unsigned miningThreads = UINT_MAX; - bool shouldListDevices = false; - bool clAllowCPU = false; + unsigned m_openclPlatform = 0; + unsigned m_openclDevice = 0; + unsigned m_miningThreads = UINT_MAX; + bool m_shouldListDevices = false; + bool m_clAllowCPU = false; /// DAG initialisation param. - unsigned initDAG = 0; + unsigned m_initDAG = 0; /// Benchmarking params - bool phoneHome = true; - unsigned benchmarkWarmup = 3; - unsigned benchmarkTrial = 3; - unsigned benchmarkTrials = 5; + bool m_phoneHome = true; + unsigned m_benchmarkWarmup = 3; + unsigned m_benchmarkTrial = 3; + unsigned m_benchmarkTrials = 5; /// Farm params - string farmURL = "http://127.0.0.1:8545"; - unsigned farmRecheckPeriod = 500; - bool precompute = true; + string m_farmURL = "http://127.0.0.1:8545"; + unsigned m_farmRecheckPeriod = 500; + bool m_precompute = true; }; diff --git a/libethash-cl/ethash_cl_miner_kernel.cl b/libethash-cl/ethash_cl_miner_kernel.cl index 46c94f241..2143435ed 100644 --- a/libethash-cl/ethash_cl_miner_kernel.cl +++ b/libethash-cl/ethash_cl_miner_kernel.cl @@ -275,6 +275,8 @@ uint inner_loop_chunks(uint4 init, uint thread_id, __local uint* share, __global return fnv_reduce(mix); } + + uint inner_loop(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, uint isolate) { uint4 mix = init; From 7604f4c22476185f62e51e80faac22ee18a21321 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 11 Jun 2015 15:17:25 +0200 Subject: [PATCH 25/69] Smarter and more flexible GPU memory check - Added new option --cl-extragpumem with which you can let the OpenCL miner know how much GPU memory you believe your system would need for miscellaneous stuff like Windowing system rendering e.t.c. The default is 350 MB. - Added new option --curent-block with which you can let the miner know the current block during the configuration phase and as such help him provide a much more accurate calculation of how much GPU memory is required. - Added help(documentation) for some arguments that did not have one --- ethminer/MinerAux.h | 20 +++++++++++++++++++- libethash-cl/ethash_cl_miner.cpp | 17 +++++++++++------ libethash-cl/ethash_cl_miner.h | 7 ++++++- libethcore/Ethash.cpp | 10 ++++++++-- libethcore/Ethash.h | 10 ++++++++-- libethcore/EthashAux.cpp | 5 +++++ libethcore/EthashAux.h | 1 + 7 files changed, 58 insertions(+), 12 deletions(-) diff --git a/ethminer/MinerAux.h b/ethminer/MinerAux.h index 65311b019..f49fbd70d 100644 --- a/ethminer/MinerAux.h +++ b/ethminer/MinerAux.h @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -131,6 +132,8 @@ public: m_shouldListDevices = true; else if (arg == "--allow-opencl-cpu") m_clAllowCPU = true; + else if (arg == "--cl-extragpu-mem" && i + 1 < argc) + m_extraGPUMemory = 1000000 * stol(argv[++i]); else if (arg == "--phone-home" && i + 1 < argc) { string m = argv[++i]; @@ -175,6 +178,8 @@ public: m_minerType = MinerType::CPU; else if (arg == "-G" || arg == "--opencl") m_minerType = MinerType::GPU; + else if (arg == "--current-block" && i + 1 < argc) + m_currentBlock = stol(argv[++i]); else if (arg == "--no-precompute") { m_precompute = false; @@ -261,7 +266,13 @@ public: else if (m_minerType == MinerType::GPU) { ProofOfWork::GPUMiner::setNumInstances(m_miningThreads); - if (!ProofOfWork::GPUMiner::configureGPU(m_openclPlatform, m_openclDevice, m_clAllowCPU)) + if (!ProofOfWork::GPUMiner::configureGPU( + m_openclPlatform, + m_openclDevice, + m_clAllowCPU, + m_extraGPUMemory, + m_currentBlock + )) { cout << "No GPU device with sufficient memory was found. Can't GPU mine. Remove the -G argument" << endl; exit(1); @@ -303,6 +314,10 @@ public: << " --opencl-platform When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl << " --opencl-device When mining using -G/--opencl use OpenCL device n (default: 0)." << endl << " -t, --mining-threads Limit number of CPU/GPU miners to n (default: use everything available on selected platform)" << endl + << " --allow-opencl-cpu Allows CPU to be considered as an OpenCL device if the OpenCL platform supports it." << endl + << " --list-devices List the detected OpenCL devices and exit." < m_currentBlock; + // default value is 350MB of GPU memory for other stuff (windows system rendering, e.t.c.) + unsigned m_extraGPUMemory = 350000000; /// DAG initialisation param. unsigned m_initDAG = 0; diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp index db08d10c3..5f36393c6 100644 --- a/libethash-cl/ethash_cl_miner.cpp +++ b/libethash-cl/ethash_cl_miner.cpp @@ -32,11 +32,11 @@ #include #include #include +#include #include "ethash_cl_miner.h" #include "ethash_cl_miner_kernel.h" #define ETHASH_BYTES 32 -#define ETHASH_CL_MINIMUM_MEMORY 2000000000 // workaround lame platforms #if !CL_VERSION_1_2 @@ -137,14 +137,18 @@ unsigned ethash_cl_miner::getNumDevices(unsigned _platformId) return devices.size(); } -bool ethash_cl_miner::configureGPU(bool _allowCPU) +bool ethash_cl_miner::configureGPU(bool _allowCPU, unsigned _extraGPUMemory, boost::optional _currentBlock) { s_allowCPU = _allowCPU; - return searchForAllDevices([](cl::Device const _device) -> bool + s_extraRequiredGPUMem = _extraGPUMemory; + // by default let's only consider the DAG of the first epoch + uint64_t dagSize = _currentBlock ? ethash_get_datasize(*_currentBlock) : 1073739904U; + uint64_t requiredSize = dagSize + _extraGPUMemory; + return searchForAllDevices([&requiredSize](cl::Device const _device) -> bool { cl_ulong result; _device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result); - if (result >= ETHASH_CL_MINIMUM_MEMORY) + if (result >= requiredSize) { ETHCL_LOG( "Found suitable OpenCL device [" << _device.getInfo() @@ -156,7 +160,7 @@ bool ethash_cl_miner::configureGPU(bool _allowCPU) ETHCL_LOG( "OpenCL device " << _device.getInfo() << " has insufficient GPU memory." << result << - " bytes of memory found < " << ETHASH_CL_MINIMUM_MEMORY << " bytes of memory required" + " bytes of memory found < " << requiredSize << " bytes of memory required" ); return false; } @@ -164,6 +168,7 @@ bool ethash_cl_miner::configureGPU(bool _allowCPU) } bool ethash_cl_miner::s_allowCPU = false; +unsigned ethash_cl_miner::s_extraRequiredGPUMem; bool ethash_cl_miner::searchForAllDevices(function _callback) { @@ -279,7 +284,7 @@ bool ethash_cl_miner::init( // configure chunk number depending on max allocateable memory cl_ulong result; device.getInfo(CL_DEVICE_MAX_MEM_ALLOC_SIZE, &result); - if (result >= ETHASH_CL_MINIMUM_MEMORY) + if (result >= _dagSize) { m_dagChunksNum = 1; ETHCL_LOG("Using 1 big chunk. Max OpenCL allocateable memory is" << result); diff --git a/libethash-cl/ethash_cl_miner.h b/libethash-cl/ethash_cl_miner.h index 44ec7eba9..b17fdeeb4 100644 --- a/libethash-cl/ethash_cl_miner.h +++ b/libethash-cl/ethash_cl_miner.h @@ -12,6 +12,7 @@ #include "cl.hpp" #endif +#include #include #include #include @@ -40,7 +41,7 @@ public: static unsigned getNumDevices(unsigned _platformId = 0); static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0); static void listDevices(); - static bool configureGPU(bool _allowCPU); + static bool configureGPU(bool _allowCPU, unsigned _extraGPUMemory, boost::optional _currentBlock); bool init( uint8_t const* _dag, @@ -73,5 +74,9 @@ private: unsigned m_workgroup_size; bool m_opencl_1_1; + /// Allow CPU to appear as an OpenCL device or not. Default is false static bool s_allowCPU; + /// GPU memory required for other things, like window rendering e.t.c. + /// User can set it via the --cl-extragpu-mem argument. + static unsigned s_extraRequiredGPUMem; }; diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 20ba539d1..8600dd51a 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -381,11 +381,17 @@ void Ethash::GPUMiner::listDevices() return ethash_cl_miner::listDevices(); } -bool Ethash::GPUMiner::configureGPU(unsigned _platformId, unsigned _deviceId, bool _allowCPU) +bool Ethash::GPUMiner::configureGPU( + unsigned _platformId, + unsigned _deviceId, + bool _allowCPU, + unsigned _extraGPUMemory, + boost::optional _currentBlock +) { s_platformId = _platformId; s_deviceId = _deviceId; - return ethash_cl_miner::configureGPU(_allowCPU); + return ethash_cl_miner::configureGPU(_allowCPU, _extraGPUMemory, _currentBlock); } #endif diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index ee4c7d008..d9584f0d4 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -88,7 +88,7 @@ public: static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); } static std::string platformInfo(); static void listDevices() {} - static bool configureGPU(unsigned, unsigned, bool) { return false; } + static bool configureGPU(unsigned, unsigned, bool, unsigned, boost::optional) { return false; } static void setNumInstances(unsigned _instances) { s_numInstances = std::min(_instances, std::thread::hardware_concurrency()); } protected: void kickOff() override @@ -117,7 +117,13 @@ public: static std::string platformInfo(); static unsigned getNumDevices(); static void listDevices(); - static bool configureGPU(unsigned _platformId, unsigned _deviceId, bool _allowCPU); + static bool configureGPU( + unsigned _platformId, + unsigned _deviceId, + bool _allowCPU, + unsigned _extraGPUMemory, + boost::optional _currentBlock + ); static void setNumInstances(unsigned _instances) { s_numInstances = std::min(_instances, getNumDevices()); } protected: diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp index 0c1e84ebc..51a606ff8 100644 --- a/libethcore/EthashAux.cpp +++ b/libethcore/EthashAux.cpp @@ -54,6 +54,11 @@ uint64_t EthashAux::cacheSize(BlockInfo const& _header) return ethash_get_cachesize((uint64_t)_header.number); } +uint64_t EthashAux::dataSize(uint64_t _blockNumber) +{ + return ethash_get_datasize(_blockNumber); +} + h256 EthashAux::seedHash(unsigned _number) { unsigned epoch = _number / ETHASH_EPOCH_LENGTH; diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h index e6fed519f..47180bfd2 100644 --- a/libethcore/EthashAux.h +++ b/libethcore/EthashAux.h @@ -66,6 +66,7 @@ public: static h256 seedHash(unsigned _number); static uint64_t number(h256 const& _seedHash); static uint64_t cacheSize(BlockInfo const& _header); + static uint64_t dataSize(uint64_t _blockNumber); static LightType light(h256 const& _seedHash); From 9e9a33b7134a8565cc478b647090d87fb9fa82fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 11 Jun 2015 11:17:57 +0200 Subject: [PATCH 26/69] Set stack memory limit on OSX. --- cmake/EthCompilerSettings.cmake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 53535a489..d21a3d4b1 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -55,6 +55,11 @@ else () message(WARNING "Your compiler is not tested, if you run into any issues, we'd welcome any patches.") endif () +# Set stack memory limits +if (APPLE) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-stack_size,1000000") +endif() + if (PROFILING AND (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) set(CMAKE_CXX_FLAGS "-g ${CMAKE_CXX_FLAGS}") set(CMAKE_C_FLAGS "-g ${CMAKE_C_FLAGS}") From 7a557e42df3afc8d6c9702753f4fe56eeb49031d Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 11 Jun 2015 15:46:29 +0200 Subject: [PATCH 27/69] small changed based on comments --- libethereum/BlockChain.cpp | 6 +++--- mix/ClientModel.cpp | 3 +-- mix/ClientModel.h | 1 - mix/QFunctionDefinition.cpp | 1 - mix/QVariableDeclaration.cpp | 9 +++------ 5 files changed, 7 insertions(+), 13 deletions(-) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 1d53e73f9..5a0b085a7 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -1068,11 +1068,11 @@ VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function 0; } - m_client->resetState(m_accounts, Secret(m_currentScenario.value("miner").toMap().value("secret").toString().toStdString())); + m_client->resetState(m_accounts, Secret(_scenario.value("miner").toMap().value("secret").toString().toStdString())); if (m_queueTransactions.count() > 0 && trToExecute) { setupExecutionChain(); diff --git a/mix/ClientModel.h b/mix/ClientModel.h index 32348b603..b5b783b3d 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -273,7 +273,6 @@ private: std::map m_stdContractNames; CodeModel* m_codeModel = nullptr; QList m_queueTransactions; - QVariantMap m_currentScenario; mutable boost::shared_mutex x_queueTransactions; }; diff --git a/mix/QFunctionDefinition.cpp b/mix/QFunctionDefinition.cpp index 52d8ad30c..1a1dd8083 100644 --- a/mix/QFunctionDefinition.cpp +++ b/mix/QFunctionDefinition.cpp @@ -37,7 +37,6 @@ QFunctionDefinition::QFunctionDefinition(QObject* _parent, dev::solidity::Functi QFunctionDefinition::QFunctionDefinition(QObject* _parent, ASTPointer const& _f): QBasicNodeDefinition(_parent, _f.get()), m_hash(dev::sha3(_f->externalSignature())), m_fullHash(dev::sha3(_f->externalSignature())) { - for (unsigned i = 0; i < _f->getParameters().size(); ++i) m_parameters.append(new QVariableDeclaration(parent(), _f->getParameters().at(i))); diff --git a/mix/QVariableDeclaration.cpp b/mix/QVariableDeclaration.cpp index 4299e34ee..a086585cd 100644 --- a/mix/QVariableDeclaration.cpp +++ b/mix/QVariableDeclaration.cpp @@ -33,23 +33,20 @@ namespace mix QVariableDeclaration::QVariableDeclaration(QObject* _parent, ASTPointer const _v): QBasicNodeDefinition(_parent, _v.get()), - m_type(new QSolidityType(this, CodeModel::nodeType(_v->getType().get()))) + m_type(new QSolidityType(this, CodeModel::nodeType(_v->getType().get()))), m_isIndexed(_v->isIndexed()) { - m_isIndexed = _v->isIndexed(); } QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type, bool _isIndexed): QBasicNodeDefinition(_parent, _name), - m_type(new QSolidityType(_parent, _type)) + m_type(new QSolidityType(_parent, _type)), m_isIndexed(_isIndexed) { - m_isIndexed = _isIndexed; } QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type, bool _isIndexed): QBasicNodeDefinition(_parent, _name), - m_type(new QSolidityType(this, CodeModel::nodeType(_type))) + m_type(new QSolidityType(this, CodeModel::nodeType(_type))), m_isIndexed(_isIndexed) { - m_isIndexed = _isIndexed; } QSolidityType::QSolidityType(QObject* _parent, SolidityType const& _type): From 1593d20e6c9d3b0d57b4d743346f4163f793ec1d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 11 Jun 2015 22:53:21 +0900 Subject: [PATCH 28/69] Fix for FixedHash::operator~(). --- libdevcore/FixedHash.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 88bc0fe95..2d1822b2c 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -100,7 +100,7 @@ public: FixedHash operator|(FixedHash const& _c) const { return FixedHash(*this) |= _c; } FixedHash& operator&=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] &= _c.m_data[i]; return *this; } FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; } - FixedHash& operator~() { for (unsigned i = 0; i < N; ++i) m_data[i] = ~m_data[i]; return *this; } + FixedHash operator~() const { FixedHash ret; for (unsigned i = 0; i < N; ++i) ret[i] = ~m_data[i]; return ret; } /// @returns true if all bytes in @a _c are set in this object. bool contains(FixedHash const& _c) const { return (*this & _c) == _c; } From 237d044a73fe0b28a4b61eb676ac08300d090095 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Jun 2015 00:41:28 +0900 Subject: [PATCH 29/69] Reprocess in eth -i, don't network by default. --- eth/main.cpp | 29 ++++++++++++++++++++++++++--- libethereum/State.cpp | 7 ++++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index 5497a2cda..a7f483966 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -108,6 +108,7 @@ void interactiveHelp() << " exportconfig Export the config (.RLP) to the path provided." << endl << " importconfig Import the config (.RLP) from the path provided." << endl << " inspect Dumps a contract to /.evm." << endl + << " reprocess Reprocess a given block." << endl << " dumptrace Dumps a transaction trace" << endl << "to . should be one of pretty, standard, standard+." << endl << " dumpreceipt Dumps a transation receipt." << endl << " exit Exits the application." << endl; @@ -806,13 +807,19 @@ int main(int argc, char** argv) cout << "Transaction Signer: " << signingKey << endl; cout << "Mining Benefactor: " << beneficiary << endl; - web3.startNetwork(); - cout << "Node ID: " << web3.enode() << endl; + + if (bootstrap || !remoteHost.empty()) + { + web3.startNetwork(); + cout << "Node ID: " << web3.enode() << endl; + } + else + cout << "Networking disabled. To start, use netstart or pass -b or a remote host." << endl; if (bootstrap) for (auto const& i: Host::pocHosts()) web3.requirePeer(i.first, i.second); - if (remoteHost.size()) + if (!remoteHost.empty()) web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort)); #if ETH_JSONRPC || !ETH_TRUE @@ -1403,6 +1410,22 @@ int main(int argc, char** argv) cout << "Hex: " << toHex(rb) << endl; cout << r << endl; } + else if (c && cmd == "reprocess") + { + string block; + iss >> block; + h256 blockHash; + try + { + if (block.size() == 64 || block.size() == 66) + blockHash = h256(block); + else + blockHash = c->blockChain().numberHash(stoi(block)); + c->state(blockHash); + } + catch (...) + {} + } else if (c && cmd == "dumptrace") { unsigned block; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index c80863d2f..a4b784b6f 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -141,7 +141,12 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h, ImportRequire // 2. Enact the block's transactions onto this state. m_ourAddress = bi.coinbaseAddress; - enact(BlockChain::verifyBlock(b), _bc, _ir); + boost::timer t; + auto vb = BlockChain::verifyBlock(b); + cnote << "verifyBlock:" << t.elapsed(); + t.restart(); + enact(vb, _bc, _ir); + cnote << "enact:" << t.elapsed(); } else { From cb133837c4f27df8c9e86652358da16ecf53a924 Mon Sep 17 00:00:00 2001 From: Dimitry Date: Thu, 11 Jun 2015 18:53:29 +0300 Subject: [PATCH 30/69] expectOut fill section to vmTests --- test/TestHelper.cpp | 2 ++ test/libevm/vm.cpp | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 873ea21e2..733ccb6d0 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -352,6 +352,8 @@ void ImportTest::exportTest(bytes const& _output, State const& _statePost) BOOST_CHECK_MESSAGE((m_TestObject["out"].get_str() == m_TestObject["expectOut"].get_str()), warning); else BOOST_WARN_MESSAGE((m_TestObject["out"].get_str() == m_TestObject["expectOut"].get_str()), warning); + + m_TestObject.erase(m_TestObject.find("expectOut")); } // export logs diff --git a/test/libevm/vm.cpp b/test/libevm/vm.cpp index ec66837e1..5bbab2e1c 100644 --- a/test/libevm/vm.cpp +++ b/test/libevm/vm.cpp @@ -388,6 +388,19 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) o["callcreates"] = fev.exportCallCreates(); o["out"] = output.size() > 4096 ? "#" + toString(output.size()) : toHex(output, 2, HexPrefix::Add); + + // compare expected output with post output + if (o.count("expectOut") > 0) + { + std::string warning = "Check State: Error! Unexpected output: " + o["out"].get_str() + " Expected: " + o["expectOut"].get_str(); + if (Options::get().checkState) + BOOST_CHECK_MESSAGE((o["out"].get_str() == o["expectOut"].get_str()), warning); + else + BOOST_WARN_MESSAGE((o["out"].get_str() == o["expectOut"].get_str()), warning); + + o.erase(o.find("expectOut")); + } + o["gas"] = toCompactHex(fev.gas, HexPrefix::Add, 1); o["logs"] = exportLog(fev.sub.logs); } From 566b2b635447cc3313817eb8482ae04af2a99eb7 Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 11 Jun 2015 18:41:31 +0200 Subject: [PATCH 31/69] resync after downloading unknown new block --- libethereum/EthereumHost.cpp | 36 +++++++++++++++++++++++++++++------- libethereum/EthereumHost.h | 2 ++ libp2p/Host.cpp | 19 ++++++++++++------- libp2p/Host.h | 1 + 4 files changed, 44 insertions(+), 14 deletions(-) diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 33f95695b..4609fddb6 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -81,6 +81,7 @@ void EthereumHost::reset() m_hashMan.reset(m_chain.number() + 1); m_needSyncBlocks = true; m_needSyncHashes = true; + m_syncingNewHashes = false; m_syncingLatestHash = h256(); m_syncingTotalDifficulty = 0; m_latestBlockSent = h256(); @@ -88,6 +89,14 @@ void EthereumHost::reset() m_hashes.clear(); } +void EthereumHost::resetSyncTo(h256 const& _h) +{ + m_needSyncHashes = true; + m_needSyncBlocks = true; + m_syncingLatestHash = _h; + m_syncingNewHashes = false; +} + void EthereumHost::doWork() { bool netChange = ensureInitialised(); @@ -366,8 +375,13 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool } if (_complete) { + clog(NetMessageSummary) << "Start new blocks download..."; m_needSyncBlocks = true; - continueSync(); + m_syncingNewHashes = true; + m_man.resetToChain(m_hashes); + m_hashes.clear(); + m_hashMan.reset(m_chain.number() + 1); + continueSync(_peer); } else if (syncByNumber && m_hashMan.isComplete()) { @@ -425,6 +439,7 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r) unsigned unknown = 0; unsigned got = 0; unsigned repeated = 0; + h256 lastUnknown; for (unsigned i = 0; i < itemCount; ++i) { @@ -453,6 +468,7 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r) break; case ImportResult::UnknownParent: + lastUnknown = h; unknown++; break; @@ -467,6 +483,13 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r) } clog(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received."; + + if (m_syncingNewHashes && unknown > 0) + { + _peer->m_latestHash = lastUnknown; + resetSyncTo(lastUnknown); + } + continueSync(_peer); } @@ -486,7 +509,7 @@ void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes) void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r) { RecursiveGuard l(x_sync); - if (isSyncing_UNSAFE() || _peer->isConversing()) + if ((isSyncing_UNSAFE() || _peer->isConversing()) && !m_syncingNewHashes) { clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading."; return; @@ -526,9 +549,7 @@ void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r) clog(NetMessageSummary) << "Received block with no known parent. Resyncing..."; _peer->m_latestHash = h; _peer->m_totalDifficulty = difficulty; - m_needSyncHashes = true; - m_needSyncBlocks = true; - m_syncingLatestHash = h; + resetSyncTo(h);; sync = true; } } @@ -646,7 +667,7 @@ void EthereumHost::continueSync(EthereumPeer* _peer) { _peer->requestHashes(m_syncingLatestHash); m_syncingV61 = false; - m_estimatedHashes = _peer->m_expectedHashes; + m_estimatedHashes = _peer->m_expectedHashes - (_peer->m_protocolVersion == protocolVersion() ? 0 : c_chainReorgSize); } else _peer->setIdle(); @@ -658,6 +679,7 @@ void EthereumHost::continueSync(EthereumPeer* _peer) { // Done our chain-get. m_needSyncBlocks = false; + m_syncingNewHashes = false; clog(NetNote) << "Chain download complete."; // 1/100th for each useful block hash. _peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers? @@ -742,6 +764,6 @@ HashChainStatus EthereumHost::status() RecursiveGuard l(x_sync); if (m_syncingV61) return HashChainStatus { static_cast(m_hashMan.chainSize()), static_cast(m_hashMan.gotCount()), false }; - return HashChainStatus { m_estimatedHashes > 0 ? m_estimatedHashes - c_chainReorgSize : 0, static_cast(m_hashes.size()), m_estimatedHashes > 0 }; + return HashChainStatus { m_estimatedHashes > 0 ? m_estimatedHashes : 0, static_cast(m_hashes.size()), m_estimatedHashes > 0 }; } diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index 17684fea1..392e234f3 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -97,6 +97,7 @@ private: void foreachPeerPtr(std::function)> const& _f) const; void foreachPeer(std::function const& _f) const; bool isSyncing_UNSAFE() const; + void resetSyncTo(h256 const& _h); /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. void doWork(); @@ -151,6 +152,7 @@ private: bool m_needSyncBlocks = true; ///< Indicates if we still need to download some blocks h256 m_syncingLatestHash; ///< Latest block's hash, as of the current sync. u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty, as of the current sync. + bool m_syncingNewHashes = false; ///< True if currently downloading hashes received with NewHashes h256s m_hashes; ///< List of hashes with unknown block numbers. Used for PV60 chain downloading and catching up to a particular unknown unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only. bool m_syncingV61 = false; ///< True if recent activity was over pv61+. Used for status reporting only. diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index f4cceebf2..628b59767 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -61,17 +61,22 @@ ReputationManager::ReputationManager() void ReputationManager::noteRude(Session const& _s, std::string const& _sub) { - m_nodes[make_pair(_s.id(), _s.info().clientVersion)].subs[_sub].isRude = true; + DEV_WRITE_GUARDED(x_nodes) + m_nodes[make_pair(_s.id(), _s.info().clientVersion)].subs[_sub].isRude = true; } bool ReputationManager::isRude(Session const& _s, std::string const& _sub) const { - auto nit = m_nodes.find(make_pair(_s.id(), _s.info().clientVersion)); - if (nit == m_nodes.end()) - return false; - auto sit = nit->second.subs.find(_sub); - bool ret = sit == nit->second.subs.end() ? false : sit->second.isRude; - return _sub.empty() ? ret : (ret || isRude(_s)); + DEV_READ_GUARDED(x_nodes) + { + auto nit = m_nodes.find(make_pair(_s.id(), _s.info().clientVersion)); + if (nit == m_nodes.end()) + return false; + auto sit = nit->second.subs.find(_sub); + bool ret = sit == nit->second.subs.end() ? false : sit->second.isRude; + return _sub.empty() ? ret : (ret || isRude(_s)); + } + return false; } Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bytesConstRef _restoreNetwork): diff --git a/libp2p/Host.h b/libp2p/Host.h index 132dd379a..b32c495cb 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -99,6 +99,7 @@ public: private: std::unordered_map, Reputation> m_nodes; ///< Nodes that were impolite while syncing. We avoid syncing from these if possible. + SharedMutex mutable x_nodes; }; /** From 057311778ce257978005b85f992c57ddcbc9faef Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 11 Jun 2015 19:15:53 -0400 Subject: [PATCH 32/69] Prep for cleanup, subprotocol packet queues, and framing. Disown rlpxsocket from handshake and rename FrameIO to FrameCoder. --- libp2p/Host.cpp | 6 +- libp2p/Host.h | 5 +- .../{RLPxFrameIO.cpp => RLPXFrameCoder.cpp} | 30 +++++----- libp2p/{RLPxFrameIO.h => RLPXFrameCoder.h} | 49 +++------------- libp2p/RLPXSocket.cpp | 0 libp2p/RLPXSocket.h | 56 +++++++++++++++++++ libp2p/RLPxHandshake.cpp | 6 +- libp2p/RLPxHandshake.h | 9 +-- libp2p/Session.cpp | 35 ++++++------ libp2p/Session.h | 11 ++-- 10 files changed, 118 insertions(+), 89 deletions(-) rename libp2p/{RLPxFrameIO.cpp => RLPXFrameCoder.cpp} (89%) rename libp2p/{RLPxFrameIO.h => RLPXFrameCoder.h} (67%) create mode 100644 libp2p/RLPXSocket.cpp create mode 100644 libp2p/RLPXSocket.h diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index f47db3c2b..c5e209d70 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -172,7 +172,7 @@ void Host::doneWorking() m_sessions.clear(); } -void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint) +void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameCoder* _io, std::shared_ptr const& _s) { // session maybe ingress or egress so m_peers and node table entries may not exist shared_ptr p; @@ -192,7 +192,7 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io } if (p->isOffline()) p->m_lastConnected = std::chrono::system_clock::now(); - p->endpoint.address = _endpoint.address(); + p->endpoint.address = _s->remoteEndpoint().address(); auto protocolVersion = _rlp[0].toInt(); auto clientVersion = _rlp[1].toString(); @@ -211,7 +211,7 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io clog(NetMessageSummary) << "Hello: " << clientVersion << "V[" << protocolVersion << "]" << _id << showbase << capslog.str() << dec << listenPort; // create session so disconnects are managed - auto ps = make_shared(this, _io, p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[2].toSet(), 0, map()})); + auto ps = make_shared(this, _io, _s, p, PeerSessionInfo({_id, clientVersion, p->endpoint.address.to_string(), listenPort, chrono::steady_clock::duration(), _rlp[2].toSet(), 0, map()})); if (protocolVersion < dev::p2p::c_protocolVersion - 1) { ps->disconnect(IncompatibleProtocol); diff --git a/libp2p/Host.h b/libp2p/Host.h index 3c7ce257a..95b77d5c9 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -40,7 +40,8 @@ #include "HostCapability.h" #include "Network.h" #include "Peer.h" -#include "RLPxFrameIO.h" +#include "RLPXSocket.h" +#include "RLPXFrameCoder.h" #include "Common.h" namespace ba = boost::asio; namespace bi = ba::ip; @@ -158,7 +159,7 @@ public: NodeId id() const { return m_alias.pub(); } /// Validates and starts peer session, taking ownership of _io. Disconnects and returns false upon error. - void startPeerSession(Public const& _id, RLP const& _hello, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint); + void startPeerSession(Public const& _id, RLP const& _hello, RLPXFrameCoder* _io, std::shared_ptr const& _s); protected: void onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e); diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPXFrameCoder.cpp similarity index 89% rename from libp2p/RLPxFrameIO.cpp rename to libp2p/RLPXFrameCoder.cpp index 4fa8557ba..c4bb46814 100644 --- a/libp2p/RLPxFrameIO.cpp +++ b/libp2p/RLPXFrameCoder.cpp @@ -14,16 +14,14 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file RLPXFrameIO.cpp +/** @file RLPXFrameCoder.cpp * @author Alex Leverington * @date 2015 */ -#include "RLPxFrameIO.h" +#include "RLPXFrameCoder.h" + #include -#include "Host.h" -#include "Session.h" -#include "Peer.h" #include "RLPxHandshake.h" using namespace std; @@ -31,7 +29,7 @@ using namespace dev; using namespace dev::p2p; using namespace CryptoPP; -RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket) +RLPXFrameCoder::RLPXFrameCoder(RLPXHandshake const& _init) { // we need: // originated? @@ -94,7 +92,7 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket) m_ingressMac.Update(keyMaterial.data(), keyMaterial.size()); } -void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) +void RLPXFrameCoder::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) { // _packet = type || rlpList() @@ -126,7 +124,7 @@ void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) egressDigest().ref().copyTo(macRef); } -bool RLPXFrameIO::authAndDecryptHeader(bytesRef io) +bool RLPXFrameCoder::authAndDecryptHeader(bytesRef io) { asserts(io.size() == h256::size); updateIngressMACWithHeader(io); @@ -138,7 +136,7 @@ bool RLPXFrameIO::authAndDecryptHeader(bytesRef io) return true; } -bool RLPXFrameIO::authAndDecryptFrame(bytesRef io) +bool RLPXFrameCoder::authAndDecryptFrame(bytesRef io) { bytesRef cipherText(io.cropped(0, io.size() - h128::size)); updateIngressMACWithFrame(cipherText); @@ -149,7 +147,7 @@ bool RLPXFrameIO::authAndDecryptFrame(bytesRef io) return true; } -h128 RLPXFrameIO::egressDigest() +h128 RLPXFrameCoder::egressDigest() { SHA3_256 h(m_egressMac); h128 digest; @@ -157,7 +155,7 @@ h128 RLPXFrameIO::egressDigest() return digest; } -h128 RLPXFrameIO::ingressDigest() +h128 RLPXFrameCoder::ingressDigest() { SHA3_256 h(m_ingressMac); h128 digest; @@ -165,29 +163,29 @@ h128 RLPXFrameIO::ingressDigest() return digest; } -void RLPXFrameIO::updateEgressMACWithHeader(bytesConstRef _headerCipher) +void RLPXFrameCoder::updateEgressMACWithHeader(bytesConstRef _headerCipher) { updateMAC(m_egressMac, _headerCipher.cropped(0, 16)); } -void RLPXFrameIO::updateEgressMACWithFrame(bytesConstRef _cipher) +void RLPXFrameCoder::updateEgressMACWithFrame(bytesConstRef _cipher) { m_egressMac.Update(_cipher.data(), _cipher.size()); updateMAC(m_egressMac); } -void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher) +void RLPXFrameCoder::updateIngressMACWithHeader(bytesConstRef _headerCipher) { updateMAC(m_ingressMac, _headerCipher.cropped(0, 16)); } -void RLPXFrameIO::updateIngressMACWithFrame(bytesConstRef _cipher) +void RLPXFrameCoder::updateIngressMACWithFrame(bytesConstRef _cipher) { m_ingressMac.Update(_cipher.data(), _cipher.size()); updateMAC(m_ingressMac); } -void RLPXFrameIO::updateMAC(SHA3_256& _mac, bytesConstRef _seed) +void RLPXFrameCoder::updateMAC(SHA3_256& _mac, bytesConstRef _seed) { if (_seed.size() && _seed.size() != h128::size) asserts(false); diff --git a/libp2p/RLPxFrameIO.h b/libp2p/RLPXFrameCoder.h similarity index 67% rename from libp2p/RLPxFrameIO.h rename to libp2p/RLPXFrameCoder.h index 0f0504e48..7c5eedbff 100644 --- a/libp2p/RLPxFrameIO.h +++ b/libp2p/RLPXFrameCoder.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file RLPXFrameIO.h +/** @file RLPXFrameCoder.h * @author Alex Leverington * @date 2015 */ @@ -23,13 +23,10 @@ #pragma once #include -#include +#include #include #include -#include #include "Common.h" -namespace ba = boost::asio; -namespace bi = boost::asio::ip; namespace dev { @@ -39,45 +36,21 @@ namespace p2p class RLPXHandshake; /** - * @brief Encoder/decoder transport for RLPx connections established by RLPXHandshake. - * Managed (via shared_ptr) socket for use by RLPXHandshake and RLPXFrameIO. - * - * Thread Safety - * Distinct Objects: Safe. - * Shared objects: Unsafe. - * * an instance method must not be called concurrently - * * a writeSingleFramePacket can be called concurrent to authAndDecryptHeader OR authAndDecryptFrame - */ -class RLPXSocket: public std::enable_shared_from_this -{ -public: - RLPXSocket(bi::tcp::socket* _socket): m_socket(std::move(*_socket)) {} - ~RLPXSocket() { close(); } - - bool isConnected() const { return m_socket.is_open(); } - void close() { try { boost::system::error_code ec; m_socket.shutdown(bi::tcp::socket::shutdown_both, ec); if (m_socket.is_open()) m_socket.close(); } catch (...){} } - bi::tcp::endpoint remoteEndpoint() { try { return m_socket.remote_endpoint(); } catch (...){ return bi::tcp::endpoint(); } } - bi::tcp::socket& ref() { return m_socket; } - -protected: - bi::tcp::socket m_socket; -}; - -/** - * @brief Encoder/decoder transport for RLPx connections established by RLPXHandshake. + * @brief Encoder/decoder transport for RLPx connection established by RLPXHandshake. * * Thread Safety * Distinct Objects: Safe. * Shared objects: Unsafe. */ -class RLPXFrameIO +class RLPXFrameCoder { + friend class RLPXFrameIOMux; friend class Session; public: /// Constructor. /// Requires instance of RLPXHandshake which has completed first two phases of handshake. - RLPXFrameIO(RLPXHandshake const& _init); - ~RLPXFrameIO() {} + RLPXFrameCoder(RLPXHandshake const& _init); + ~RLPXFrameCoder() {} /// Encrypt _packet as RLPx frame. void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes); @@ -93,7 +66,7 @@ public: /// Return first 16 bytes of current digest from ingress mac. h128 ingressDigest(); - + protected: /// Update state of egress MAC with frame header. void updateEgressMACWithHeader(bytesConstRef _headerCipher); @@ -106,9 +79,7 @@ protected: /// Update state of ingress MAC with frame. void updateIngressMACWithFrame(bytesConstRef _cipher); - - bi::tcp::socket& socket() { return m_socket->ref(); } - + private: /// Update state of _mac. void updateMAC(CryptoPP::SHA3_256& _mac, bytesConstRef _seed = bytesConstRef()); @@ -125,8 +96,6 @@ private: CryptoPP::SHA3_256 m_egressMac; ///< State of MAC for egress ciphertext. CryptoPP::SHA3_256 m_ingressMac; ///< State of MAC for ingress ciphertext. - - std::shared_ptr m_socket; }; } diff --git a/libp2p/RLPXSocket.cpp b/libp2p/RLPXSocket.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/libp2p/RLPXSocket.h b/libp2p/RLPXSocket.h new file mode 100644 index 000000000..389418c76 --- /dev/null +++ b/libp2p/RLPXSocket.h @@ -0,0 +1,56 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file RLPXSocket.h + * @author Alex Leverington + * @date 2015 + */ + +#pragma once + +#include "Common.h" + +namespace dev +{ +namespace p2p +{ + +/** + * @brief Shared pointer wrapper for ASIO TCP socket. + * + * Thread Safety + * Distinct Objects: Safe. + * Shared objects: Unsafe. + * * an instance method must not be called concurrently + */ +class RLPXSocket: public std::enable_shared_from_this +{ +public: + /// Constructor. Dereferences and takes ownership of _socket. + RLPXSocket(bi::tcp::socket* _socket): m_socket(std::move(*_socket)) {} + ~RLPXSocket() { close(); } + + bool isConnected() const { return m_socket.is_open(); } + void close() { try { boost::system::error_code ec; m_socket.shutdown(bi::tcp::socket::shutdown_both, ec); if (m_socket.is_open()) m_socket.close(); } catch (...){} } + bi::tcp::endpoint remoteEndpoint() { try { return m_socket.remote_endpoint(); } catch (...){ return bi::tcp::endpoint(); } } + bi::tcp::socket& ref() { return m_socket; } + +protected: + bi::tcp::socket m_socket; +}; + +} +} \ No newline at end of file diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index 8caf6e4f3..47604eedc 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -179,7 +179,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) /// This pointer will be freed if there is an error otherwise /// it will be passed to Host which will take ownership. - m_io = new RLPXFrameIO(*this); + m_io = new RLPXFrameCoder(*this); // old packet format // 5 arguments, HelloPacket @@ -200,7 +200,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) } else if (m_nextState == ReadHello) { - // Authenticate and decrypt initial hello frame with initial RLPXFrameIO + // Authenticate and decrypt initial hello frame with initial RLPXFrameCoder // and request m_host to start session. m_nextState = StartSession; @@ -269,7 +269,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) try { RLP rlp(frame.cropped(1), RLP::ThrowOnFail | RLP::FailIfTooSmall); - m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint()); + m_host->startPeerSession(m_remote, rlp, m_io, m_socket); } catch (std::exception const& _e) { diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index 47f6afb57..6ed0819c6 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -25,7 +25,8 @@ #include #include #include -#include "RLPxFrameIO.h" +#include "RLPXSocket.h" +#include "RLPXFrameCoder.h" #include "Common.h" namespace ba = boost::asio; namespace bi = boost::asio::ip; @@ -36,7 +37,7 @@ namespace p2p { /** - * @brief Setup inbound or outbound connection for communication over RLPXFrameIO. + * @brief Setup inbound or outbound connection for communication over RLPXFrameCoder. * RLPx Spec: https://github.com/ethereum/devp2p/blob/master/rlpx.md#encrypted-handshake * * @todo Implement StartSession transition via lambda which is passed to constructor. @@ -47,7 +48,7 @@ namespace p2p */ class RLPXHandshake: public std::enable_shared_from_this { - friend class RLPXFrameIO; + friend class RLPXFrameCoder; /// Sequential states of handshake enum State @@ -122,7 +123,7 @@ protected: /// Used to read and write RLPx encrypted frames for last step of handshake authentication. /// Passed onto Host which will take ownership. - RLPXFrameIO* m_io = nullptr; + RLPXFrameCoder* m_io = nullptr; std::shared_ptr m_socket; ///< Socket. boost::asio::deadline_timer m_idleTimer; ///< Timer which enforces c_timeout. diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 462fea7b1..260ce10ac 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -27,23 +27,24 @@ #include #include #include +#include "RLPxHandshake.h" #include "Host.h" #include "Capability.h" using namespace std; using namespace dev; using namespace dev::p2p; -Session::Session(Host* _s, RLPXFrameIO* _io, std::shared_ptr const& _n, PeerSessionInfo _info): - m_server(_s), +Session::Session(Host* _h, RLPXFrameCoder* _io, std::shared_ptr const& _s, std::shared_ptr const& _n, PeerSessionInfo _info): + m_server(_h), m_io(_io), - m_socket(m_io->socket()), + m_socket(_s), m_peer(_n), m_info(_info), m_ping(chrono::steady_clock::time_point::max()) { m_peer->m_lastDisconnect = NoDisconnect; m_lastReceived = m_connect = chrono::steady_clock::now(); - m_info.socketId = _io->socket().native_handle(); + m_info.socketId = m_socket->ref().native_handle(); } Session::~Session() @@ -59,11 +60,12 @@ Session::~Session() try { - if (m_socket.is_open()) + bi::tcp::socket& socket = m_socket->ref(); + if (socket.is_open()) { boost::system::error_code ec; - m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - m_socket.close(); + socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + socket.close(); } } catch (...){} @@ -303,7 +305,7 @@ void Session::send(bytes&& _msg) if (!checkPacket(msg)) clog(NetWarn) << "INVALID PACKET CONSTRUCTED!"; - if (!m_socket.is_open()) + if (!m_socket->ref().is_open()) return; bool doWrite = false; @@ -326,7 +328,7 @@ void Session::write() out = &m_writeQueue[0]; } auto self(shared_from_this()); - ba::async_write(m_socket, ba::buffer(*out), [this, self](boost::system::error_code ec, std::size_t /*length*/) + ba::async_write(m_socket->ref(), ba::buffer(*out), [this, self](boost::system::error_code ec, std::size_t /*length*/) { ThreadContext tc(info().id.abridged()); ThreadContext tc2(info().clientVersion); @@ -352,13 +354,14 @@ void Session::drop(DisconnectReason _reason) { if (m_dropped) return; - if (m_socket.is_open()) + bi::tcp::socket& socket = m_socket->ref(); + if (socket.is_open()) try { - clog(NetConnect) << "Closing " << m_socket.remote_endpoint() << "(" << reasonOf(_reason) << ")"; + clog(NetConnect) << "Closing " << socket.remote_endpoint() << "(" << reasonOf(_reason) << ")"; boost::system::error_code ec; - m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - m_socket.close(); + socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + socket.close(); } catch (...) {} @@ -379,7 +382,7 @@ void Session::disconnect(DisconnectReason _reason) m_peer->endpoint, // TODO: may not be 100% accurate m_server->peerCount() ); - if (m_socket.is_open()) + if (m_socket->ref().is_open()) { RLPStream s; prep(s, DisconnectPacket, 1) << (int)_reason; @@ -401,7 +404,7 @@ void Session::doRead() return; auto self(shared_from_this()); - ba::async_read(m_socket, boost::asio::buffer(m_data, h256::size), [this,self](boost::system::error_code ec, std::size_t length) + ba::async_read(m_socket->ref(), boost::asio::buffer(m_data, h256::size), [this,self](boost::system::error_code ec, std::size_t length) { ThreadContext tc(info().id.abridged()); ThreadContext tc2(info().clientVersion); @@ -438,7 +441,7 @@ void Session::doRead() /// read padded frame and mac auto tlen = frameSize + ((16 - (frameSize % 16)) % 16) + h128::size; - ba::async_read(m_socket, boost::asio::buffer(m_data, tlen), [this, self, headerRLP, frameSize, tlen](boost::system::error_code ec, std::size_t length) + ba::async_read(m_socket->ref(), boost::asio::buffer(m_data, tlen), [this, self, headerRLP, frameSize, tlen](boost::system::error_code ec, std::size_t length) { ThreadContext tc(info().id.abridged()); ThreadContext tc2(info().clientVersion); diff --git a/libp2p/Session.h b/libp2p/Session.h index bcbf8022b..08006f890 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -33,7 +33,8 @@ #include #include #include -#include "RLPxHandshake.h" +#include "RLPXFrameCoder.h" +#include "RLPXSocket.h" #include "Common.h" namespace dev @@ -54,7 +55,7 @@ class Session: public std::enable_shared_from_this friend class HostCapabilityFace; public: - Session(Host* _server, RLPXFrameIO* _io, std::shared_ptr const& _n, PeerSessionInfo _info); + Session(Host* _server, RLPXFrameCoder* _io, std::shared_ptr const& _s, std::shared_ptr const& _n, PeerSessionInfo _info); virtual ~Session(); void start(); @@ -62,7 +63,7 @@ public: void ping(); - bool isConnected() const { return m_socket.is_open(); } + bool isConnected() const { return m_socket->ref().is_open(); } NodeId id() const; unsigned socketId() const { return m_info.socketId; } @@ -105,8 +106,8 @@ private: Host* m_server; ///< The host that owns us. Never null. - RLPXFrameIO* m_io; ///< Transport over which packets are sent. - bi::tcp::socket& m_socket; ///< Socket for the peer's connection. + RLPXFrameCoder* m_io; ///< Transport over which packets are sent. + std::shared_ptr m_socket; ///< Socket of peer's connection. Mutex x_writeQueue; ///< Mutex for the write queue. std::deque m_writeQueue; ///< The write queue. std::array m_data; ///< Buffer for ingress packet data. From 3e46b869ad9efb98e38106085f6baf8bb621b6cb Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Jun 2015 12:59:52 +0900 Subject: [PATCH 33/69] Allow unlocking of wallets over RPC. --- alethzero/OurWebThreeStubServer.cpp | 2 +- eth/main.cpp | 4 ++-- libethereum/ClientBase.cpp | 17 ++--------------- libweb3jsonrpc/WebThreeStubServer.cpp | 16 +++++++++++----- libweb3jsonrpc/WebThreeStubServer.h | 9 ++++++++- libweb3jsonrpc/WebThreeStubServerBase.h | 1 + libweb3jsonrpc/abstractwebthreestubserver.h | 6 ++++++ libweb3jsonrpc/spec.json | 1 + test/libweb3jsonrpc/webthreestubclient.h | 10 ++++++++++ 9 files changed, 42 insertions(+), 24 deletions(-) diff --git a/alethzero/OurWebThreeStubServer.cpp b/alethzero/OurWebThreeStubServer.cpp index 7e9836818..85f930792 100644 --- a/alethzero/OurWebThreeStubServer.cpp +++ b/alethzero/OurWebThreeStubServer.cpp @@ -34,7 +34,7 @@ OurWebThreeStubServer::OurWebThreeStubServer( WebThreeDirect& _web3, Main* _main ): - WebThreeStubServer(_conn, _web3, make_shared(_web3, _main), _main->owned().toVector().toStdVector()), + WebThreeStubServer(_conn, _web3, make_shared(_web3, _main), _main->owned().toVector().toStdVector(), _main->keyManager()), m_main(_main) { } diff --git a/eth/main.cpp b/eth/main.cpp index 47dbae318..cfe000500 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -829,7 +829,7 @@ int main(int argc, char** argv) if (jsonrpc > -1) { jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector())); + jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector(), keyManager)); jsonrpcServer->StartListening(); } #endif @@ -973,7 +973,7 @@ int main(int argc, char** argv) if (jsonrpc < 0) jsonrpc = SensibleHttpPort; jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector())); + jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector(), keyManager)); jsonrpcServer->StartListening(); } else if (cmd == "jsonstop") diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 38e92fcd7..d5bc0dbb9 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -49,30 +49,17 @@ void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, b { prepareForTransaction(); - auto a = toAddress(_secret); - u256 n = postMine().transactionsFrom(a); - cdebug << "submitTx: " << a << "postMine=" << n << "; tq=" << m_tq.maxNonce(a); - Transaction t(_value, _gasPrice, _gas, _dest, _data, _nonce, _secret); m_tq.import(t.rlp()); StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged()); - cnote << "New transaction " << t << "(maxNonce for sender" << a << "is" << m_tq.maxNonce(a) << ")"; + cnote << "New transaction " << t; } void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) { - prepareForTransaction(); - auto a = toAddress(_secret); - u256 n = postMine().transactionsFrom(a); - cdebug << "submitTx: " << a << "postMine=" << n << "; tq=" << m_tq.maxNonce(a); - n = max(n, m_tq.maxNonce(a)); - Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); - m_tq.import(t.rlp()); - - StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged()); - cnote << "New transaction " << t << "(maxNonce for sender" << a << "is" << m_tq.maxNonce(a) << ")"; + submitTransaction(_secret, _value, _dest, _data, _gas, _gasPrice, max(postMine().transactionsFrom(a), m_tq.maxNonce(a))); } Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) diff --git a/libweb3jsonrpc/WebThreeStubServer.cpp b/libweb3jsonrpc/WebThreeStubServer.cpp index 44f7f521c..eae15cd7e 100644 --- a/libweb3jsonrpc/WebThreeStubServer.cpp +++ b/libweb3jsonrpc/WebThreeStubServer.cpp @@ -24,18 +24,18 @@ // Make sure boost/asio.hpp is included before windows.h. #include #include - -#include #include +#include +#include #include "WebThreeStubServer.h" - using namespace std; using namespace dev; using namespace dev::eth; -WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, shared_ptr const& _ethAccounts, std::vector const& _shhAccounts): +WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, shared_ptr const& _ethAccounts, std::vector const& _shhAccounts, KeyManager& _keyMan): WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts), - m_web3(_web3) + m_web3(_web3), + m_keyMan(_keyMan) { auto path = getDataDir() + "/.web3"; boost::filesystem::create_directories(path); @@ -44,6 +44,12 @@ WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, ldb::DB::Open(o, path, &m_db); } +bool WebThreeStubServer::eth_notePassword(string const& _password) +{ + m_keyMan.notePassword(_password); + return true; +} + std::string WebThreeStubServer::web3_clientVersion() { return m_web3.clientVersion(); diff --git a/libweb3jsonrpc/WebThreeStubServer.h b/libweb3jsonrpc/WebThreeStubServer.h index 1eddc22d4..68de1808b 100644 --- a/libweb3jsonrpc/WebThreeStubServer.h +++ b/libweb3jsonrpc/WebThreeStubServer.h @@ -33,6 +33,10 @@ namespace dev { class WebThreeDirect; +namespace eth +{ +class KeyManager; +} } /** @@ -41,7 +45,7 @@ class WebThreeDirect; class WebThreeStubServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace { public: - WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::shared_ptr const& _ethAccounts, std::vector const& _shhAccounts); + WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::shared_ptr const& _ethAccounts, std::vector const& _shhAccounts, dev::eth::KeyManager& _keyMan); virtual std::string web3_clientVersion() override; @@ -54,8 +58,11 @@ private: virtual std::string get(std::string const& _name, std::string const& _key) override; virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) override; + virtual bool eth_notePassword(std::string const& _password); + private: dev::WebThreeDirect& m_web3; + dev::eth::KeyManager& m_keyMan; leveldb::ReadOptions m_readOptions; leveldb::WriteOptions m_writeOptions; leveldb::DB* m_db; diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index ec24cb08d..07d1d6827 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -122,6 +122,7 @@ public: virtual std::string eth_signTransaction(Json::Value const& _transaction); virtual Json::Value eth_inspectTransaction(std::string const& _rlp); virtual bool eth_injectTransaction(std::string const& _rlp); + virtual bool eth_notePassword(std::string const&) { return false; } virtual bool db_put(std::string const& _name, std::string const& _key, std::string const& _value); virtual std::string db_get(std::string const& _name, std::string const& _key); diff --git a/libweb3jsonrpc/abstractwebthreestubserver.h b/libweb3jsonrpc/abstractwebthreestubserver.h index 522d418c1..2f5764197 100644 --- a/libweb3jsonrpc/abstractwebthreestubserver.h +++ b/libweb3jsonrpc/abstractwebthreestubserver.h @@ -64,6 +64,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerbindAndAddMethod(jsonrpc::Procedure("eth_signTransaction", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_signTransactionI); this->bindAndAddMethod(jsonrpc::Procedure("eth_inspectTransaction", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_inspectTransactionI); this->bindAndAddMethod(jsonrpc::Procedure("eth_injectTransaction", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_injectTransactionI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_notePassword", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_notePasswordI); this->bindAndAddMethod(jsonrpc::Procedure("db_put", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putI); this->bindAndAddMethod(jsonrpc::Procedure("db_get", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_getI); this->bindAndAddMethod(jsonrpc::Procedure("shh_post", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::shh_postI); @@ -301,6 +302,10 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServereth_injectTransaction(request[0u].asString()); } + inline virtual void eth_notePasswordI(const Json::Value &request, Json::Value &response) + { + response = this->eth_notePassword(request[0u].asString()); + } inline virtual void db_putI(const Json::Value &request, Json::Value &response) { response = this->db_put(request[0u].asString(), request[1u].asString(), request[2u].asString()); @@ -398,6 +403,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerCallMethod("eth_notePassword",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } bool db_put(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException) { Json::Value p; From 6b80953aa3bfa4423f3e5f950474e12142169890 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 12 Jun 2015 10:30:44 +0200 Subject: [PATCH 34/69] State management in EthereumHost, better sync progress reporting. Invariants checking --- alethzero/Main.ui | 9 ++- alethzero/MainWin.cpp | 13 +++- libethereum/Client.cpp | 4 +- libethereum/Client.h | 2 +- libethereum/CommonNet.h | 25 +++++--- libethereum/DownloadMan.h | 5 -- libethereum/EthereumHost.cpp | 113 +++++++++++++++++++++++++---------- libethereum/EthereumHost.h | 32 ++++++---- 8 files changed, 138 insertions(+), 65 deletions(-) diff --git a/alethzero/Main.ui b/alethzero/Main.ui index efb6256af..f798437e6 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -44,7 +44,14 @@ 0 bytes used - + + + + + + + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 4d96b77ff..a2abb1068 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -198,6 +198,7 @@ Main::Main(QWidget *parent) : statusBar()->addPermanentWidget(ui->balance); statusBar()->addPermanentWidget(ui->peerCount); statusBar()->addPermanentWidget(ui->mineStatus); + statusBar()->addPermanentWidget(ui->syncStatus); statusBar()->addPermanentWidget(ui->chainStatus); statusBar()->addPermanentWidget(ui->blockCount); @@ -1245,9 +1246,15 @@ void Main::refreshBlockCount() { auto d = ethereum()->blockChain().details(); BlockQueueStatus b = ethereum()->blockQueueStatus(); - HashChainStatus h = ethereum()->hashChainStatus(); - ui->chainStatus->setText(QString("%10/%11%12 hashes %3 importing %4 ready %5 verifying %6 unverified %7 future %8 unknown %9 bad %1 #%2") - .arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet").arg(d.number).arg(b.importing).arg(b.verified).arg(b.verifying).arg(b.unverified).arg(b.future).arg(b.unknown).arg(b.bad).arg(h.received).arg(h.estimated ? "~" : "").arg(h.total)); + SyncStatus sync = ethereum()->syncStatus(); + QString syncStatus = EthereumHost::stateName(sync.state); + if (sync.state == SyncState::HashesParallel || sync.state == SyncState::HashesSingle) + syncStatus += QString(": %1/%2%3").arg(sync.hashesReceived).arg(sync.hashesEstimated ? "~" : "").arg(sync.hashesTotal); + if (sync.state == SyncState::Blocks || sync.state == SyncState::NewBlocks) + syncStatus += QString(": %1/%2").arg(sync.blocksReceived).arg(sync.blocksTotal); + ui->syncStatus->setText(syncStatus); + ui->chainStatus->setText(QString("%3 importing %4 ready %5 verifying %6 unverified %7 future %8 unknown %9 bad %1 #%2") + .arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet").arg(d.number).arg(b.importing).arg(b.verified).arg(b.verifying).arg(b.unverified).arg(b.future).arg(b.unknown).arg(b.bad)); } void Main::on_turboMining_triggered() diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index d1ede38ac..23e4f86c8 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -904,8 +904,8 @@ void Client::flushTransactions() doWork(); } -HashChainStatus Client::hashChainStatus() const +SyncStatus Client::syncStatus() const { auto h = m_host.lock(); - return h ? h->status() : HashChainStatus { 0, 0, false }; + return h ? h->status() : SyncStatus(); } diff --git a/libethereum/Client.h b/libethereum/Client.h index cba93290b..5cae50ed0 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -157,7 +157,7 @@ public: /// Get some information on the block queue. BlockQueueStatus blockQueueStatus() const { return m_bq.status(); } /// Get some information on the block queue. - HashChainStatus hashChainStatus() const; + SyncStatus syncStatus() const; /// Get the block queue. BlockQueue const& blockQueue() const { return m_bq; } diff --git a/libethereum/CommonNet.h b/libethereum/CommonNet.h index 2eb2d77c8..b960c8f3f 100644 --- a/libethereum/CommonNet.h +++ b/libethereum/CommonNet.h @@ -77,18 +77,27 @@ enum class Asking Nothing }; -enum class Syncing +enum class SyncState { - Waiting, - Executing, - Done + Idle, ///< Initial chain sync complete. Waiting for new packets + WaitingQueue, ///< Block downloading paused. Waiting for block queue to process blocks and free space + HashesNegotiate, ///< Waiting for first hashes to arrive + HashesSingle, ///< Locked on and downloading hashes from a single peer + HashesParallel, ///< Downloading hashes from multiple peers over + Blocks, ///< Downloading blocks + NewBlocks, ///< Downloading blocks learned from NewHashes packet + + Size /// Must be kept last }; -struct HashChainStatus +struct SyncStatus { - unsigned total; - unsigned received; - bool estimated; + SyncState state = SyncState::Idle; + unsigned hashesTotal = 0; + unsigned hashesReceived = 0; + bool hashesEstimated = false; + unsigned blocksTotal = 0; + unsigned blocksReceived = 0; }; } diff --git a/libethereum/DownloadMan.h b/libethereum/DownloadMan.h index 0c27e84ea..ac99e1d36 100644 --- a/libethereum/DownloadMan.h +++ b/libethereum/DownloadMan.h @@ -253,11 +253,6 @@ public: return m_got.full(); } - unsigned gotCount() const - { - return m_got.size(); - } - size_t chainSize() const { ReadGuard l(m_lock); return m_chainCount; } size_t chainEmpty() const { ReadGuard l(m_lock); return m_chainCount == 0; } void foreachSub(std::function const& _f) const { ReadGuard l(x_subs); for(auto i: m_subs) _f(*i); } diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 4609fddb6..661f34b11 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -41,6 +41,8 @@ using namespace p2p; unsigned const EthereumHost::c_oldProtocolVersion = 60; //TODO: remove this once v61+ is common unsigned const c_chainReorgSize = 30000; +char const* const EthereumHost::s_stateNames[static_cast(SyncState::Size)] = {"Idle", "WaitingQueue", "HashesNegotiate", "HashesSingle", "HashesParallel", "Blocks", "NewBlocks" }; + EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId): HostCapability(), Worker ("ethsync"), @@ -49,6 +51,7 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu m_bq (_bq), m_networkId (_networkId) { + setState(SyncState::HashesNegotiate); m_latestBlockSent = _ch.currentHash(); m_hashMan.reset(m_chain.number() + 1); m_bqRoomAvailable = m_bq.onRoomAvailable([this](){ m_continueSync = true; }); @@ -79,9 +82,7 @@ void EthereumHost::reset() foreachPeer([](EthereumPeer* _p) { _p->abortSync(); }); m_man.resetToChain(h256s()); m_hashMan.reset(m_chain.number() + 1); - m_needSyncBlocks = true; - m_needSyncHashes = true; - m_syncingNewHashes = false; + setState(SyncState::HashesNegotiate); m_syncingLatestHash = h256(); m_syncingTotalDifficulty = 0; m_latestBlockSent = h256(); @@ -91,10 +92,18 @@ void EthereumHost::reset() void EthereumHost::resetSyncTo(h256 const& _h) { - m_needSyncHashes = true; - m_needSyncBlocks = true; + setState(SyncState::HashesNegotiate); m_syncingLatestHash = _h; - m_syncingNewHashes = false; +} + + +void EthereumHost::setState(SyncState _s) +{ + if (m_state != _s) + { + clog(NetAllDetail) << "SyncState changed from " << stateName(m_state) << " to " << stateName(_s); + m_state = _s; + } } void EthereumHost::doWork() @@ -255,6 +264,7 @@ void EthereumHost::maintainBlocks(h256 const& _currentHash) void EthereumHost::onPeerStatus(EthereumPeer* _peer) { RecursiveGuard l(x_sync); + DEV_INVARIANT_CHECK; if (_peer->m_genesisHash != m_chain.genesisHash()) _peer->disable("Invalid genesis hash"); else if (_peer->m_protocolVersion != protocolVersion() && _peer->m_protocolVersion != c_oldProtocolVersion) @@ -274,13 +284,14 @@ void EthereumHost::onPeerStatus(EthereumPeer* _peer) _peer->m_expectedHashes = (unsigned)_peer->m_latestBlockNumber - m_chain.number(); if (_peer->m_expectedHashes > estimatedHashes) _peer->disable("Too many hashes"); - else if (m_needSyncHashes && m_hashMan.chainSize() < _peer->m_expectedHashes) + else if (needHashes() && m_hashMan.chainSize() < _peer->m_expectedHashes) m_hashMan.resetToRange(m_chain.number() + 1, _peer->m_expectedHashes); } else _peer->m_expectedHashes = estimatedHashes; continueSync(_peer); } + DEV_INVARIANT_CHECK; } unsigned EthereumHost::estimateHashes() @@ -309,6 +320,7 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes) void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool _complete) { + DEV_INVARIANT_CHECK; if (_hashes.empty()) { _peer->m_hashSub.doneFetch(); @@ -376,8 +388,8 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool if (_complete) { clog(NetMessageSummary) << "Start new blocks download..."; - m_needSyncBlocks = true; - m_syncingNewHashes = true; + m_syncingLatestHash = h256(); + setState(SyncState::NewBlocks); m_man.resetToChain(m_hashes); m_hashes.clear(); m_hashMan.reset(m_chain.number() + 1); @@ -386,40 +398,41 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool else if (syncByNumber && m_hashMan.isComplete()) { // Done our chain-get. - m_needSyncHashes = false; clog(NetNote) << "Hashes download complete."; - // 1/100th for each useful block hash. - _peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers? - m_hashMan.reset(m_chain.number() + 1); - continueSync(); + onPeerDoneHashes(_peer, false); } else if (m_hashes.size() > _peer->m_expectedHashes) { _peer->disable("Too many hashes"); m_hashes.clear(); m_syncingLatestHash = h256(); + setState(SyncState::HashesNegotiate); continueSync(); ///Try with some other peer, keep the chain } else continueSync(_peer); /// Grab next hashes + DEV_INVARIANT_CHECK; } void EthereumHost::onPeerDoneHashes(EthereumPeer* _peer, bool _localChain) { assert(_peer->m_asking == Asking::Nothing); - m_needSyncHashes = false; + m_syncingLatestHash = h256(); + setState(SyncState::Blocks); if (_peer->m_protocolVersion != protocolVersion() || _localChain) { m_man.resetToChain(m_hashes); - m_hashes.clear(); - m_hashMan.reset(m_chain.number() + 1); + _peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers? } + m_hashMan.reset(m_chain.number() + 1); + m_hashes.clear(); continueSync(); } void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r) { RecursiveGuard l(x_sync); + DEV_INVARIANT_CHECK; _peer->setAsking(Asking::Nothing); unsigned itemCount = _r.itemCount(); clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks"); @@ -484,19 +497,21 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r) clog(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received."; - if (m_syncingNewHashes && unknown > 0) + if (m_state == SyncState::NewBlocks && unknown > 0) { _peer->m_latestHash = lastUnknown; resetSyncTo(lastUnknown); } continueSync(_peer); + DEV_INVARIANT_CHECK; } void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes) { RecursiveGuard l(x_sync); - if (isSyncing_UNSAFE() || _peer->isConversing()) + DEV_INVARIANT_CHECK; + if (isSyncing() || _peer->isConversing()) { clog(NetMessageSummary) << "Ignoring new hashes since we're already downloading."; return; @@ -504,12 +519,14 @@ void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes) clog(NetNote) << "New block hash discovered: syncing without help."; _peer->m_syncHashNumber = 0; onPeerHashes(_peer, _hashes, true); + DEV_INVARIANT_CHECK; } void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r) { RecursiveGuard l(x_sync); - if ((isSyncing_UNSAFE() || _peer->isConversing()) && !m_syncingNewHashes) + DEV_INVARIANT_CHECK; + if ((isSyncing() || _peer->isConversing()) && m_state != SyncState::NewBlocks) { clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading."; return; @@ -563,6 +580,7 @@ void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r) if (sync) continueSync(); } + DEV_INVARIANT_CHECK; } void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r) @@ -607,6 +625,8 @@ void EthereumHost::onPeerAborting(EthereumPeer* _peer) void EthereumHost::continueSync() { + if (m_state == SyncState::WaitingQueue) + setState(m_lastActiveState); clog(NetAllDetail) << "Continuing sync for all peers"; foreachPeer([&](EthereumPeer* _p) { @@ -617,10 +637,11 @@ void EthereumHost::continueSync() void EthereumHost::continueSync(EthereumPeer* _peer) { + DEV_INVARIANT_CHECK; assert(_peer->m_asking == Asking::Nothing); bool otherPeerV60Sync = false; bool otherPeerV61Sync = false; - if (m_needSyncHashes) + if (needHashes()) { if (!peerShouldGrabChain(_peer)) { @@ -652,7 +673,7 @@ void EthereumHost::continueSync(EthereumPeer* _peer) } if (_peer->m_protocolVersion == protocolVersion() && !m_hashMan.isComplete()) { - m_syncingV61 = true; + setState(SyncState::HashesParallel); _peer->requestHashes(); /// v61+ and not catching up to a particular hash } else @@ -666,20 +687,19 @@ void EthereumHost::continueSync(EthereumPeer* _peer) if (_peer->m_totalDifficulty >= m_syncingTotalDifficulty) { _peer->requestHashes(m_syncingLatestHash); - m_syncingV61 = false; + setState(SyncState::HashesSingle); m_estimatedHashes = _peer->m_expectedHashes - (_peer->m_protocolVersion == protocolVersion() ? 0 : c_chainReorgSize); } else _peer->setIdle(); } } - else if (m_needSyncBlocks) + else if (needBlocks()) { if (m_man.isComplete()) { // Done our chain-get. - m_needSyncBlocks = false; - m_syncingNewHashes = false; + setState(SyncState::Idle); clog(NetNote) << "Chain download complete."; // 1/100th for each useful block hash. _peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers? @@ -700,6 +720,8 @@ void EthereumHost::continueSync(EthereumPeer* _peer) else if (m_bq.knownFull()) { clog(NetAllDetail) << "Waiting for block queue before downloading blocks"; + m_lastActiveState = m_state; + setState(SyncState::WaitingQueue); _peer->setIdle(); } else @@ -708,6 +730,7 @@ void EthereumHost::continueSync(EthereumPeer* _peer) } else _peer->setIdle(); + DEV_INVARIANT_CHECK; } bool EthereumHost::peerCanHelp(EthereumPeer* _peer) const @@ -754,16 +777,42 @@ bool EthereumHost::peerShouldGrabChain(EthereumPeer* _peer) const } } -bool EthereumHost::isSyncing_UNSAFE() const +bool EthereumHost::isSyncing() const { - return m_needSyncBlocks || m_needSyncHashes; + return m_state != SyncState::Idle; } -HashChainStatus EthereumHost::status() +SyncStatus EthereumHost::status() const { RecursiveGuard l(x_sync); - if (m_syncingV61) - return HashChainStatus { static_cast(m_hashMan.chainSize()), static_cast(m_hashMan.gotCount()), false }; - return HashChainStatus { m_estimatedHashes > 0 ? m_estimatedHashes : 0, static_cast(m_hashes.size()), m_estimatedHashes > 0 }; + SyncStatus res; + res.state = m_state; + if (m_state == SyncState::HashesParallel) + { + res.hashesReceived = m_hashMan.hashesGot().size(); + res.hashesTotal = m_hashMan.chainSize(); + } + else if (m_state == SyncState::HashesSingle) + { + res.hashesTotal = m_estimatedHashes; + res.hashesReceived = static_cast(m_hashes.size()); + res.hashesEstimated = true; + } + else if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks || m_state == SyncState::WaitingQueue) + { + res.blocksTotal = m_man.chainSize(); + res.blocksReceived = m_man.blocksGot().size(); + } + return res; } + +bool EthereumHost::invariants() const +{ + if (m_state == SyncState::HashesNegotiate && !m_hashes.empty()) + return false; + if (needBlocks() && (m_syncingLatestHash || !m_hashes.empty())) + return false; + + return true; +} diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index 392e234f3..098d893ee 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -54,9 +54,10 @@ class BlockQueue; * @warning None of this is thread-safe. You have been warned. * @doWork Syncs to peers and sends new blocks and transactions. */ -class EthereumHost: public p2p::HostCapability, Worker +class EthereumHost: public p2p::HostCapability, Worker, HasInvariants { public: + /// Start server, but don't listen. EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId); @@ -70,7 +71,7 @@ public: void reset(); DownloadMan const& downloadMan() const { return m_man; } - bool isSyncing() const { RecursiveGuard l(x_sync); return isSyncing_UNSAFE(); } + bool isSyncing() const; bool isBanned(p2p::NodeId const& _id) const { return !!m_banned.count(_id); } void noteNewTransactions() { m_newTransactions = true; } @@ -87,17 +88,21 @@ public: DownloadMan& downloadMan() { return m_man; } HashDownloadMan& hashDownloadMan() { return m_hashMan; } BlockChain const& chain() { return m_chain; } - HashChainStatus status(); + SyncStatus status() const; + static char const* stateName(SyncState _s) { return s_stateNames[static_cast(_s)]; } static unsigned const c_oldProtocolVersion; private: + static char const* const s_stateNames[static_cast(SyncState::Size)]; + std::tuple>, std::vector>, std::vector>> randomSelection(unsigned _percent = 25, std::function const& _allow = [](EthereumPeer const*){ return true; }); void foreachPeerPtr(std::function)> const& _f) const; void foreachPeer(std::function const& _f) const; - bool isSyncing_UNSAFE() const; void resetSyncTo(h256 const& _h); + bool needHashes() const { return m_state == SyncState::HashesNegotiate || m_state == SyncState::HashesSingle || m_state == SyncState::HashesParallel; } + bool needBlocks() const { return m_state == SyncState::Blocks || m_state == SyncState::NewBlocks; } /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. void doWork(); @@ -128,6 +133,9 @@ private: bool peerCanHelp(EthereumPeer* _peer) const; unsigned estimateHashes(); void estimatePeerHashes(EthereumPeer* _peer); + void setState(SyncState _s); + + bool invariants() const override; BlockChain const& m_chain; TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. @@ -148,15 +156,13 @@ private: bool m_newBlocks = false; mutable RecursiveMutex x_sync; - bool m_needSyncHashes = true; ///< Indicates if need to downlad hashes - bool m_needSyncBlocks = true; ///< Indicates if we still need to download some blocks - h256 m_syncingLatestHash; ///< Latest block's hash, as of the current sync. - u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty, as of the current sync. - bool m_syncingNewHashes = false; ///< True if currently downloading hashes received with NewHashes - h256s m_hashes; ///< List of hashes with unknown block numbers. Used for PV60 chain downloading and catching up to a particular unknown - unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only. - bool m_syncingV61 = false; ///< True if recent activity was over pv61+. Used for status reporting only. - bool m_continueSync = false; ///< True when the block queue has processed a block; we should restart grabbing blocks. + SyncState m_state = SyncState::Idle; ///< Current sync state + SyncState m_lastActiveState = SyncState::Idle; ///< Saved state before entering waiting queue mode + h256 m_syncingLatestHash; ///< Latest block's hash, as of the current sync. + u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty, as of the current sync. + h256s m_hashes; ///< List of hashes with unknown block numbers. Used for PV60 chain downloading and catching up to a particular unknown + unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only. + bool m_continueSync = false; ///< True when the block queue has processed a block; we should restart grabbing blocks. }; } From 20e4fc7b93e547cb7a8e1c57743b951fdfcbc2fb Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Jun 2015 17:33:21 +0900 Subject: [PATCH 35/69] First JSONRPC admin method. --- alethzero/DappLoader.cpp | 1 + alethzero/DappLoader.h | 3 +++ alethzero/MainWin.cpp | 8 ++++++- alethzero/OurWebThreeStubServer.cpp | 15 ++++-------- alethzero/OurWebThreeStubServer.h | 7 +----- eth/main.cpp | 26 ++++++++++++++++----- libdevcore/Base64.cpp | 12 ++++++---- libjsqrc/ethereumjs/lib/web3.js | 12 ++++++++++ libjsqrc/ethereumjs/lib/web3/eth.js | 10 ++++---- libweb3jsonrpc/WebThreeStubServer.cpp | 24 +++++++++++++++++++ libweb3jsonrpc/WebThreeStubServer.h | 13 +++++++++++ libweb3jsonrpc/WebThreeStubServerBase.h | 4 +++- libweb3jsonrpc/abstractwebthreestubserver.h | 6 +++++ libweb3jsonrpc/spec.json | 5 +++- test/libweb3jsonrpc/webthreestubclient.h | 10 ++++++++ 15 files changed, 122 insertions(+), 34 deletions(-) diff --git a/alethzero/DappLoader.cpp b/alethzero/DappLoader.cpp index b2249ae5b..662a1afbc 100644 --- a/alethzero/DappLoader.cpp +++ b/alethzero/DappLoader.cpp @@ -108,6 +108,7 @@ void DappLoader::downloadComplete(QNetworkReply* _reply) //inject web3 js QByteArray content = "\n"); content.append(_reply->readAll()); QString contentType = _reply->header(QNetworkRequest::ContentTypeHeader).toString(); diff --git a/alethzero/DappLoader.h b/alethzero/DappLoader.h index f2d3ee5e5..39176e750 100644 --- a/alethzero/DappLoader.h +++ b/alethzero/DappLoader.h @@ -78,6 +78,8 @@ public: ///@param _uri Page Uri void loadPage(QString const& _uri); + void setSessionKey(std::string const& _s) { m_sessionKey = _s; } + signals: void dappReady(Dapp& _dapp); void pageReady(QByteArray const& _content, QString const& _mimeType, QUrl const& _uri); @@ -99,5 +101,6 @@ private: std::set m_pageUrls; QByteArray m_web3Js; dev::Address m_nameReg; + std::string m_sessionKey; }; diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 4d96b77ff..28bcd7ab4 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -209,7 +209,9 @@ Main::Main(QWidget *parent) : m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), WithExisting::Trust, {"eth"/*, "shh"*/}, p2p::NetworkPreferences(), network)); m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads)); - m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), this)); + auto w3ss = new OurWebThreeStubServer(*m_httpConnector, this); + m_server.reset(w3ss); + auto sessionKey = w3ss->newSession({true}); connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString))); m_server->setIdentities(keysAsVector(owned())); m_server->StartListening(); @@ -226,12 +228,16 @@ Main::Main(QWidget *parent) : m_dappHost.reset(new DappHost(8081)); m_dappLoader = new DappLoader(this, web3(), getNameReg()); + m_dappLoader->setSessionKey(sessionKey); connect(m_dappLoader, &DappLoader::dappReady, this, &Main::dappLoaded); connect(m_dappLoader, &DappLoader::pageReady, this, &Main::pageLoaded); // ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true); // QWebEngineInspector* inspector = new QWebEngineInspector(); // inspector->setPage(page); setBeneficiary(*m_keyManager.accounts().begin()); + + ethereum()->setDefault(LatestBlock); + readSettings(); m_transact = new Transact(this, this); diff --git a/alethzero/OurWebThreeStubServer.cpp b/alethzero/OurWebThreeStubServer.cpp index 85f930792..39ab69a19 100644 --- a/alethzero/OurWebThreeStubServer.cpp +++ b/alethzero/OurWebThreeStubServer.cpp @@ -31,10 +31,9 @@ using namespace dev::eth; OurWebThreeStubServer::OurWebThreeStubServer( jsonrpc::AbstractServerConnector& _conn, - WebThreeDirect& _web3, Main* _main ): - WebThreeStubServer(_conn, _web3, make_shared(_web3, _main), _main->owned().toVector().toStdVector(), _main->keyManager()), + WebThreeStubServer(_conn, *_main->web3(), make_shared(_main), _main->owned().toVector().toStdVector(), _main->keyManager()), m_main(_main) { } @@ -46,12 +45,8 @@ string OurWebThreeStubServer::shh_newIdentity() return toJS(kp.pub()); } -OurAccountHolder::OurAccountHolder( - WebThreeDirect& _web3, - Main* _main -): - AccountHolder([=](){ return m_web3->ethereum(); }), - m_web3(&_web3), +OurAccountHolder::OurAccountHolder(Main* _main): + AccountHolder([=](){ return _main->ethereum(); }), m_main(_main) { connect(_main, SIGNAL(poll()), this, SLOT(doValidations())); @@ -135,7 +130,7 @@ void OurAccountHolder::doValidations() else // sign and submit. if (Secret s = m_main->retrieveSecret(t.from)) - m_web3->ethereum()->submitTransaction(s, t); + m_main->ethereum()->submitTransaction(s, t); } } @@ -155,7 +150,7 @@ bool OurAccountHolder::validateTransaction(TransactionSkeleton const& _t, bool _ return showCreationNotice(_t, _toProxy); } - h256 contractCodeHash = m_web3->ethereum()->postState().codeHash(_t.to); + h256 contractCodeHash = m_main->ethereum()->postState().codeHash(_t.to); if (contractCodeHash == EmptySHA3) { // recipient has no code - nothing special about this transaction, show basic value transfer info diff --git a/alethzero/OurWebThreeStubServer.h b/alethzero/OurWebThreeStubServer.h index a07188b2d..43985b640 100644 --- a/alethzero/OurWebThreeStubServer.h +++ b/alethzero/OurWebThreeStubServer.h @@ -34,10 +34,7 @@ class OurAccountHolder: public QObject, public dev::eth::AccountHolder Q_OBJECT public: - OurAccountHolder( - dev::WebThreeDirect& _web3, - Main* _main - ); + OurAccountHolder(Main* _main); public slots: void doValidations(); @@ -59,7 +56,6 @@ private: std::queue m_queued; dev::Mutex x_queued; - dev::WebThreeDirect* m_web3; Main* m_main; }; @@ -70,7 +66,6 @@ class OurWebThreeStubServer: public QObject, public WebThreeStubServer public: OurWebThreeStubServer( jsonrpc::AbstractServerConnector& _conn, - dev::WebThreeDirect& _web3, Main* main ); diff --git a/eth/main.cpp b/eth/main.cpp index cfe000500..259a993c9 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -126,6 +126,7 @@ void help() #if ETH_JSONRPC || !ETH_TRUE << " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl + << " --admin Specify admin session key for JSON-RPC (default: auto-generated and printed at startup)." << endl #endif << " -K,--kill First kill the blockchain." << endl << " -R,--rebuild Rebuild the blockchain from the existing database." << endl @@ -288,6 +289,7 @@ int main(int argc, char** argv) #if ETH_JSONRPC int jsonrpc = -1; #endif + string jsonAdmin; bool upnp = true; WithExisting killChain = WithExisting::Trust; bool jit = false; @@ -599,6 +601,8 @@ int main(int argc, char** argv) jsonrpc = jsonrpc == -1 ? SensibleHttpPort : jsonrpc; else if (arg == "--json-rpc-port" && i + 1 < argc) jsonrpc = atoi(argv[++i]); + else if (arg == "--json-admin" && i + 1 < argc) + jsonAdmin = argv[++i]; #endif #if ETH_JSCONSOLE else if (arg == "--console") @@ -817,12 +821,6 @@ int main(int argc, char** argv) else cout << "Networking disabled. To start, use netstart or pass -b or a remote host." << endl; - if (bootstrap) - for (auto const& i: Host::pocHosts()) - web3.requirePeer(i.first, i.second); - if (!remoteHost.empty()) - web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort)); - #if ETH_JSONRPC || !ETH_TRUE shared_ptr jsonrpcServer; unique_ptr jsonrpcConnector; @@ -831,9 +829,20 @@ int main(int argc, char** argv) jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector(), keyManager)); jsonrpcServer->StartListening(); + if (jsonAdmin.empty()) + jsonAdmin = jsonrpcServer->newSession(SessionPermissions{true}); + else + jsonrpcServer->addSession(jsonAdmin, SessionPermissions{true}); + cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl; } #endif + if (bootstrap) + for (auto const& i: Host::pocHosts()) + web3.requirePeer(i.first, i.second); + if (!remoteHost.empty()) + web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort)); + signal(SIGABRT, &sighandler); signal(SIGTERM, &sighandler); signal(SIGINT, &sighandler); @@ -975,6 +984,11 @@ int main(int argc, char** argv) jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector(), keyManager)); jsonrpcServer->StartListening(); + if (jsonAdmin.empty()) + jsonAdmin = jsonrpcServer->newSession(SessionPermissions{true}); + else + jsonrpcServer->addSession(jsonAdmin, SessionPermissions{true}); + cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl; } else if (cmd == "jsonstop") { diff --git a/libdevcore/Base64.cpp b/libdevcore/Base64.cpp index e36f8a18a..f97c82156 100644 --- a/libdevcore/Base64.cpp +++ b/libdevcore/Base64.cpp @@ -29,11 +29,13 @@ #include "Base64.h" using namespace dev; -static inline bool is_base64(byte c) { +static inline bool is_base64(byte c) +{ return (isalnum(c) || (c == '+') || (c == '/')); } -static inline byte find_base64_char_index(byte c) { +static inline byte find_base64_char_index(byte c) +{ if ('A' <= c && c <= 'Z') return c - 'A'; else if ('a' <= c && c <= 'z') return c - 'a' + 1 + find_base64_char_index('Z'); else if ('0' <= c && c <= '9') return c - '0' + 1 + find_base64_char_index('z'); @@ -42,7 +44,8 @@ static inline byte find_base64_char_index(byte c) { else return 1 + find_base64_char_index('/'); } -std::string dev::toBase64(bytesConstRef _in) { +std::string dev::toBase64(bytesConstRef _in) +{ static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" @@ -91,7 +94,8 @@ std::string dev::toBase64(bytesConstRef _in) { return ret; } -bytes dev::fromBase64(std::string const& encoded_string) { +bytes dev::fromBase64(std::string const& encoded_string) +{ auto in_len = encoded_string.size(); int i = 0; int j = 0; diff --git a/libjsqrc/ethereumjs/lib/web3.js b/libjsqrc/ethereumjs/lib/web3.js index 021d896e6..982f75d9e 100644 --- a/libjsqrc/ethereumjs/lib/web3.js +++ b/libjsqrc/ethereumjs/lib/web3.js @@ -158,5 +158,17 @@ setupProperties(web3.eth, eth.properties); setupMethods(web3.db, db.methods); setupMethods(web3.shh, shh.methods); +web3.admin = {}; +web3.admin.setSessionKey = function(s) { web3.admin.sessionKey = s; }; + +var blockQueueStatus = new Method({ + name: 'blockQueueStatus', + call: 'admin_eth_blockQueueStatus', + params: 1, + inputFormatter: [function() { return web3.sessionKey; }] +}); + +setupMethods(web3.admin, [blockQueueStatus]); + module.exports = web3; diff --git a/libjsqrc/ethereumjs/lib/web3/eth.js b/libjsqrc/ethereumjs/lib/web3/eth.js index 87a2b290c..23297f1f0 100644 --- a/libjsqrc/ethereumjs/lib/web3/eth.js +++ b/libjsqrc/ethereumjs/lib/web3/eth.js @@ -77,11 +77,11 @@ var uncleCountCall = function (args) { /// @returns an array of objects describing web3.eth api methods var getBalance = new Method({ - name: 'getBalance', - call: 'eth_getBalance', - params: 2, - inputFormatter: [utils.toAddress, formatters.inputDefaultBlockNumberFormatter], - outputFormatter: formatters.outputBigNumberFormatter + name: 'getBalance', + call: 'eth_getBalance', + params: 2, + inputFormatter: [utils.toAddress, formatters.inputDefaultBlockNumberFormatter], + outputFormatter: formatters.outputBigNumberFormatter }); var getStorageAt = new Method({ diff --git a/libweb3jsonrpc/WebThreeStubServer.cpp b/libweb3jsonrpc/WebThreeStubServer.cpp index eae15cd7e..1bef71b59 100644 --- a/libweb3jsonrpc/WebThreeStubServer.cpp +++ b/libweb3jsonrpc/WebThreeStubServer.cpp @@ -44,12 +44,36 @@ WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, ldb::DB::Open(o, path, &m_db); } +std::string WebThreeStubServer::newSession(SessionPermissions const& _p) +{ + std::string s = toBase64(h64::random().ref()); + m_sessions[s] = _p; + return s; +} + bool WebThreeStubServer::eth_notePassword(string const& _password) { m_keyMan.notePassword(_password); return true; } +Json::Value WebThreeStubServer::admin_eth_blockQueueStatus(string const& _session) +{ + Json::Value ret; + if (isAdmin(_session)) + { + BlockQueueStatus bqs = m_web3.ethereum()->blockQueue().status(); + ret["importing"] = (int)bqs.importing; + ret["verified"] = (int)bqs.verified; + ret["verifying"] = (int)bqs.verifying; + ret["unverified"] = (int)bqs.unverified; + ret["future"] = (int)bqs.future; + ret["unknown"] = (int)bqs.unknown; + ret["bad"] = (int)bqs.bad; + } + return ret; +} + std::string WebThreeStubServer::web3_clientVersion() { return m_web3.clientVersion(); diff --git a/libweb3jsonrpc/WebThreeStubServer.h b/libweb3jsonrpc/WebThreeStubServer.h index 68de1808b..4a9dd1911 100644 --- a/libweb3jsonrpc/WebThreeStubServer.h +++ b/libweb3jsonrpc/WebThreeStubServer.h @@ -39,6 +39,11 @@ class KeyManager; } } +struct SessionPermissions +{ + bool admin; +}; + /** * @brief JSON-RPC api implementation for WebThreeDirect */ @@ -49,7 +54,12 @@ public: virtual std::string web3_clientVersion() override; + std::string newSession(SessionPermissions const& _p); + void addSession(std::string const& _session, SessionPermissions const& _p) { m_sessions[_session] = _p; } + private: + bool isAdmin(std::string const& _session) const { auto it = m_sessions.find(_session); return it != m_sessions.end() && it->second.admin; } + virtual dev::eth::Interface* client() override; virtual std::shared_ptr face() override; virtual dev::WebThreeNetworkFace* network() override; @@ -59,6 +69,7 @@ private: virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) override; virtual bool eth_notePassword(std::string const& _password); + virtual Json::Value admin_eth_blockQueueStatus(std::string const& _session); private: dev::WebThreeDirect& m_web3; @@ -66,4 +77,6 @@ private: leveldb::ReadOptions m_readOptions; leveldb::WriteOptions m_writeOptions; leveldb::DB* m_db; + + std::unordered_map m_sessions; }; diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index 07d1d6827..93a54cce7 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -136,7 +136,9 @@ public: virtual bool shh_uninstallFilter(std::string const& _filterId); virtual Json::Value shh_getFilterChanges(std::string const& _filterId); virtual Json::Value shh_getMessages(std::string const& _filterId); - + + virtual Json::Value admin_eth_blockQueueStatus(std::string const&) { return Json::Value(); } + void setIdentities(std::vector const& _ids); std::map const& ids() const { return m_shhIds; } diff --git a/libweb3jsonrpc/abstractwebthreestubserver.h b/libweb3jsonrpc/abstractwebthreestubserver.h index 2f5764197..675a03614 100644 --- a/libweb3jsonrpc/abstractwebthreestubserver.h +++ b/libweb3jsonrpc/abstractwebthreestubserver.h @@ -76,6 +76,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerbindAndAddMethod(jsonrpc::Procedure("shh_uninstallFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_uninstallFilterI); this->bindAndAddMethod(jsonrpc::Procedure("shh_getFilterChanges", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_getFilterChangesI); this->bindAndAddMethod(jsonrpc::Procedure("shh_getMessages", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_getMessagesI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_blockQueueStatus", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_blockQueueStatusI); } inline virtual void web3_sha3I(const Json::Value &request, Json::Value &response) @@ -351,6 +352,10 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServershh_getMessages(request[0u].asString()); } + inline virtual void admin_eth_blockQueueStatusI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_blockQueueStatus(request[0u].asString()); + } virtual std::string web3_sha3(const std::string& param1) = 0; virtual std::string web3_clientVersion() = 0; virtual std::string net_version() = 0; @@ -415,6 +420,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerCallMethod("admin_eth_blockQueueStatus",p); + if (result.isObject()) + return result; + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } }; #endif //JSONRPC_CPP_STUB_WEBTHREESTUBCLIENT_H_ From 35b786be2479f23c3b1388cead3eb3d4e38bf45b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 12 Jun 2015 10:41:06 +0200 Subject: [PATCH 36/69] OS stack memory offloading for VM execution. --- libethereum/ExtVM.cpp | 48 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/libethereum/ExtVM.cpp b/libethereum/ExtVM.cpp index 782d4d79c..d9ff738ed 100644 --- a/libethereum/ExtVM.cpp +++ b/libethereum/ExtVM.cpp @@ -20,18 +20,58 @@ */ #include "ExtVM.h" - +#include +#include #include "Executive.h" -using namespace std; + using namespace dev; using namespace dev::eth; +namespace +{ +static unsigned const c_depthLimit = 1024; +static size_t const c_singleExecutionStackSize = 8 * 1024; +static size_t const c_defaultStackSize = 512 * 1024; +static unsigned const c_offloadPoint = c_defaultStackSize / c_singleExecutionStackSize; + +void goOnOffloadedStack(Executive& _e, OnOpFunc const& _onOp) +{ + cnote << "CALL OFFLOADING: offloading point " << c_offloadPoint; + boost::thread::attributes attrs; + attrs.set_stack_size((c_depthLimit - c_offloadPoint) * c_singleExecutionStackSize); + + std::exception_ptr exception; + boost::thread{attrs, [&]{ + cnote << "OFFLOADING thread"; + try + { + _e.go(_onOp); + } + catch (...) + { + cnote << "!!!!!!!!!!! exception in offloading!!!!!!!!!!!!"; + exception = std::current_exception(); + } + }}.join(); + if (exception) + std::rethrow_exception(exception); +} + +void go(unsigned _depth, Executive& _e, OnOpFunc const& _onOp) +{ + if (_depth == c_offloadPoint) + goOnOffloadedStack(_e, _onOp); + else + _e.go(_onOp); +} +} + bool ExtVM::call(CallParameters& _p) { Executive e(m_s, lastHashes, depth + 1); if (!e.call(_p, gasPrice, origin)) { - e.go(_p.onOp); + go(depth, e, _p.onOp); e.accrueSubState(sub); } _p.gas = e.gas(); @@ -47,7 +87,7 @@ h160 ExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc Executive e(m_s, lastHashes, depth + 1); if (!e.create(myAddress, _endowment, gasPrice, io_gas, _code, origin)) { - e.go(_onOp); + go(depth, e, _onOp); e.accrueSubState(sub); } io_gas = e.gas(); From 1b4745c95b00d364fedd2afbd8b3ba181b569e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 12 Jun 2015 10:42:01 +0200 Subject: [PATCH 37/69] Do not set stack memory limit on OSX. --- cmake/EthCompilerSettings.cmake | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index d21a3d4b1..53535a489 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -55,11 +55,6 @@ else () message(WARNING "Your compiler is not tested, if you run into any issues, we'd welcome any patches.") endif () -# Set stack memory limits -if (APPLE) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-stack_size,1000000") -endif() - if (PROFILING AND (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) set(CMAKE_CXX_FLAGS "-g ${CMAKE_CXX_FLAGS}") set(CMAKE_C_FLAGS "-g ${CMAKE_C_FLAGS}") From 8ccd0bb22967492b9a72ad4f006c2f4f837f1ab5 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 12 Jun 2015 11:02:03 +0200 Subject: [PATCH 38/69] added missing resync guard --- libethereum/EthereumHost.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 661f34b11..c5cc1a6e0 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -128,6 +128,7 @@ void EthereumHost::doWork() if (m_continueSync) { m_continueSync = false; + RecursiveGuard l(x_sync); continueSync(); } From 6c45ac6ab10651e11a0b978845a87ce2d6b14a1e Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 12 Jun 2015 11:06:05 +0200 Subject: [PATCH 39/69] Optimize RETURN x 0 to STOP. --- libevmasm/CommonSubexpressionEliminator.cpp | 48 +++++++++++++-------- libsolidity/Compiler.cpp | 5 ++- test/libsolidity/SolidityOptimizer.cpp | 8 ++++ 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp index 2c4742d61..8fb4625a8 100644 --- a/libevmasm/CommonSubexpressionEliminator.cpp +++ b/libevmasm/CommonSubexpressionEliminator.cpp @@ -79,31 +79,43 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _co void CommonSubexpressionEliminator::optimizeBreakingItem() { - if (!m_breakingItem || *m_breakingItem != AssemblyItem(Instruction::JUMPI)) + if (!m_breakingItem) return; + ExpressionClasses& classes = m_state.expressionClasses(); SourceLocation const& location = m_breakingItem->getLocation(); - AssemblyItem::JumpType jumpType = m_breakingItem->getJumpType(); - - Id condition = m_state.stackElement(m_state.stackHeight() - 1, location); - Id zero = m_state.expressionClasses().find(u256(0)); - if (m_state.expressionClasses().knownToBeDifferent(condition, zero)) + if (*m_breakingItem == AssemblyItem(Instruction::JUMPI)) { - feedItem(AssemblyItem(Instruction::SWAP1, location), true); - feedItem(AssemblyItem(Instruction::POP, location), true); + AssemblyItem::JumpType jumpType = m_breakingItem->getJumpType(); - AssemblyItem item(Instruction::JUMP, location); - item.setJumpType(jumpType); - m_breakingItem = m_state.expressionClasses().storeItem(item); - return; + Id condition = m_state.stackElement(m_state.stackHeight() - 1, location); + if (classes.knownNonZero(condition)) + { + feedItem(AssemblyItem(Instruction::SWAP1, location), true); + feedItem(AssemblyItem(Instruction::POP, location), true); + + AssemblyItem item(Instruction::JUMP, location); + item.setJumpType(jumpType); + m_breakingItem = classes.storeItem(item); + } + else if (classes.knownZero(condition)) + { + AssemblyItem it(Instruction::POP, location); + feedItem(it, true); + feedItem(it, true); + m_breakingItem = nullptr; + } } - Id negatedCondition = m_state.expressionClasses().find(Instruction::ISZERO, {condition}); - if (m_state.expressionClasses().knownToBeDifferent(negatedCondition, zero)) + else if (*m_breakingItem == AssemblyItem(Instruction::RETURN)) { - AssemblyItem it(Instruction::POP, location); - feedItem(it, true); - feedItem(it, true); - m_breakingItem = nullptr; + Id size = m_state.stackElement(m_state.stackHeight() - 1, location); + if (classes.knownZero(size)) + { + feedItem(AssemblyItem(Instruction::POP, location), true); + feedItem(AssemblyItem(Instruction::POP, location), true); + AssemblyItem item(Instruction::STOP, location); + m_breakingItem = classes.storeItem(item); + } } } diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 950b1e756..b2b8bfa4c 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -298,7 +298,10 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters) stackDepth -= type->getSizeOnStack(); } // note that the stack is not cleaned up here - m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN; + if (dataOffset == 0) + m_context << eth::Instruction::STOP; + else + m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN; } void Compiler::registerStateVariables(ContractDefinition const& _contract) diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index 397ee6316..85a88c030 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -944,6 +944,14 @@ BOOST_AUTO_TEST_CASE(cse_access_previous_sequence) // 0, SLOAD, 1, ADD, SSTORE, 0 SLOAD } +BOOST_AUTO_TEST_CASE(cse_optimise_return) +{ + checkCSE( + AssemblyItems{u256(0), u256(7), Instruction::RETURN}, + AssemblyItems{Instruction::STOP} + ); +} + BOOST_AUTO_TEST_CASE(control_flow_graph_remove_unused) { // remove parts of the code that are unused From 4b2d62814f42ce77be5cac76d6e9920e114585df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 12 Jun 2015 11:15:47 +0200 Subject: [PATCH 40/69] Change stack offloading point. --- libethereum/ExtVM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethereum/ExtVM.cpp b/libethereum/ExtVM.cpp index d9ff738ed..b32d308f2 100644 --- a/libethereum/ExtVM.cpp +++ b/libethereum/ExtVM.cpp @@ -30,7 +30,7 @@ using namespace dev::eth; namespace { static unsigned const c_depthLimit = 1024; -static size_t const c_singleExecutionStackSize = 8 * 1024; +static size_t const c_singleExecutionStackSize = 12 * 1024; static size_t const c_defaultStackSize = 512 * 1024; static unsigned const c_offloadPoint = c_defaultStackSize / c_singleExecutionStackSize; From ed65ebdede34bcfa744df1b914cd723ba7af27a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 12 Jun 2015 13:27:26 +0200 Subject: [PATCH 41/69] Remove debug logs and add comments. --- libethereum/ExtVM.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/libethereum/ExtVM.cpp b/libethereum/ExtVM.cpp index b32d308f2..305462511 100644 --- a/libethereum/ExtVM.cpp +++ b/libethereum/ExtVM.cpp @@ -29,28 +29,35 @@ using namespace dev::eth; namespace { + static unsigned const c_depthLimit = 1024; + +/// Upper bound of stack space needed by single CALL/CREATE execution. Set experimentally. static size_t const c_singleExecutionStackSize = 12 * 1024; + +/// Standard OSX thread stack limit. Should be reasonable for other platforms too. static size_t const c_defaultStackSize = 512 * 1024; + +/// On what depth execution should be offloaded to additional separated stack space. static unsigned const c_offloadPoint = c_defaultStackSize / c_singleExecutionStackSize; void goOnOffloadedStack(Executive& _e, OnOpFunc const& _onOp) { - cnote << "CALL OFFLOADING: offloading point " << c_offloadPoint; + // Set new stack size enouth to handle the rest of the calls up to the limit. boost::thread::attributes attrs; attrs.set_stack_size((c_depthLimit - c_offloadPoint) * c_singleExecutionStackSize); + // Create new thread with big stack and join immediately. + // TODO: It is possible to switch the implementation to Boost.Context or similar when the API is stable. std::exception_ptr exception; boost::thread{attrs, [&]{ - cnote << "OFFLOADING thread"; try { _e.go(_onOp); } catch (...) { - cnote << "!!!!!!!!!!! exception in offloading!!!!!!!!!!!!"; - exception = std::current_exception(); + exception = std::current_exception(); // Catch all exceptions to be rethrown in parent thread. } }}.join(); if (exception) @@ -59,6 +66,10 @@ void goOnOffloadedStack(Executive& _e, OnOpFunc const& _onOp) void go(unsigned _depth, Executive& _e, OnOpFunc const& _onOp) { + // If in the offloading point we need to switch to additional separated stack space. + // Current stack is too small to handle more CALL/CREATE executions. + // It needs to be done only once as newly allocated stack space it enough to handle + // the rest of the calls up to the depth limit (c_depthLimit). if (_depth == c_offloadPoint) goOnOffloadedStack(_e, _onOp); else From b84c28e0849cfc59f68f2ca6be90edda02f03648 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 12 Jun 2015 15:46:09 +0200 Subject: [PATCH 42/69] Allow forcing single chunk DAG upload to GPU A new argument is added. --force-single-chunk allows the user to overwrite auto chunk detection and force DAG uploading in a single chunk. This should only be used if the user is 100% certain that their card can actually enqueue a DAG for writting bigger than the MAX_MEM_ALLOC_SIZE. OpenCL says this is undefined behaviour so use at your own risk. Still, some cards seem to be able to upload the DAG in a single chunk even if OpenCL thinks they can't, thus the decision to add this option. --- ethminer/MinerAux.h | 5 +++++ libethash-cl/ethash_cl_miner.cpp | 18 ++++++++++++++---- libethash-cl/ethash_cl_miner.h | 9 ++++++++- libethcore/Ethash.cpp | 3 ++- libethcore/Ethash.h | 3 ++- 5 files changed, 31 insertions(+), 7 deletions(-) diff --git a/ethminer/MinerAux.h b/ethminer/MinerAux.h index f49fbd70d..6de3913e4 100644 --- a/ethminer/MinerAux.h +++ b/ethminer/MinerAux.h @@ -134,6 +134,8 @@ public: m_clAllowCPU = true; else if (arg == "--cl-extragpu-mem" && i + 1 < argc) m_extraGPUMemory = 1000000 * stol(argv[++i]); + else if (arg == "--force-single-chunk") + m_forceSingleChunk = true; else if (arg == "--phone-home" && i + 1 < argc) { string m = argv[++i]; @@ -271,6 +273,7 @@ public: m_openclDevice, m_clAllowCPU, m_extraGPUMemory, + m_forceSingleChunk, m_currentBlock )) { @@ -318,6 +321,7 @@ public: << " --list-devices List the detected OpenCL devices and exit." < m_currentBlock; // default value is 350MB of GPU memory for other stuff (windows system rendering, e.t.c.) unsigned m_extraGPUMemory = 350000000; diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp index 5f36393c6..6c2f8269a 100644 --- a/libethash-cl/ethash_cl_miner.cpp +++ b/libethash-cl/ethash_cl_miner.cpp @@ -137,9 +137,15 @@ unsigned ethash_cl_miner::getNumDevices(unsigned _platformId) return devices.size(); } -bool ethash_cl_miner::configureGPU(bool _allowCPU, unsigned _extraGPUMemory, boost::optional _currentBlock) +bool ethash_cl_miner::configureGPU( + bool _allowCPU, + unsigned _extraGPUMemory, + bool _forceSingleChunk, + boost::optional _currentBlock +) { s_allowCPU = _allowCPU; + s_forceSingleChunk = _forceSingleChunk; s_extraRequiredGPUMem = _extraGPUMemory; // by default let's only consider the DAG of the first epoch uint64_t dagSize = _currentBlock ? ethash_get_datasize(*_currentBlock) : 1073739904U; @@ -168,6 +174,7 @@ bool ethash_cl_miner::configureGPU(bool _allowCPU, unsigned _extraGPUMemory, boo } bool ethash_cl_miner::s_allowCPU = false; +bool ethash_cl_miner::s_forceSingleChunk = false; unsigned ethash_cl_miner::s_extraRequiredGPUMem; bool ethash_cl_miner::searchForAllDevices(function _callback) @@ -284,15 +291,18 @@ bool ethash_cl_miner::init( // configure chunk number depending on max allocateable memory cl_ulong result; device.getInfo(CL_DEVICE_MAX_MEM_ALLOC_SIZE, &result); - if (result >= _dagSize) + if (s_forceSingleChunk || result >= _dagSize) { m_dagChunksNum = 1; - ETHCL_LOG("Using 1 big chunk. Max OpenCL allocateable memory is" << result); + ETHCL_LOG( + ((result <= _dagSize && s_forceSingleChunk) ? "Forcing single chunk. Good luck!\n" : "") << + "Using 1 big chunk. Max OpenCL allocateable memory is " << result + ); } else { m_dagChunksNum = 4; - ETHCL_LOG("Using 4 chunks. Max OpenCL allocateable memory is" << result); + ETHCL_LOG("Using 4 chunks. Max OpenCL allocateable memory is " << result); } if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0) diff --git a/libethash-cl/ethash_cl_miner.h b/libethash-cl/ethash_cl_miner.h index b17fdeeb4..cc01b0057 100644 --- a/libethash-cl/ethash_cl_miner.h +++ b/libethash-cl/ethash_cl_miner.h @@ -41,7 +41,12 @@ public: static unsigned getNumDevices(unsigned _platformId = 0); static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0); static void listDevices(); - static bool configureGPU(bool _allowCPU, unsigned _extraGPUMemory, boost::optional _currentBlock); + static bool configureGPU( + bool _allowCPU, + unsigned _extraGPUMemory, + bool _forceSingleChunk, + boost::optional _currentBlock + ); bool init( uint8_t const* _dag, @@ -74,6 +79,8 @@ private: unsigned m_workgroup_size; bool m_opencl_1_1; + /// Force dag upload to GPU in a single chunk even if OpenCL thinks you can't do it. Use at your own risk. + static bool s_forceSingleChunk; /// Allow CPU to appear as an OpenCL device or not. Default is false static bool s_allowCPU; /// GPU memory required for other things, like window rendering e.t.c. diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 8600dd51a..2c743f33b 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -386,12 +386,13 @@ bool Ethash::GPUMiner::configureGPU( unsigned _deviceId, bool _allowCPU, unsigned _extraGPUMemory, + bool _forceSingleChunk, boost::optional _currentBlock ) { s_platformId = _platformId; s_deviceId = _deviceId; - return ethash_cl_miner::configureGPU(_allowCPU, _extraGPUMemory, _currentBlock); + return ethash_cl_miner::configureGPU(_allowCPU, _extraGPUMemory, _forceSingleChunk, _currentBlock); } #endif diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index d9584f0d4..a5a7856f1 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -88,7 +88,7 @@ public: static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); } static std::string platformInfo(); static void listDevices() {} - static bool configureGPU(unsigned, unsigned, bool, unsigned, boost::optional) { return false; } + static bool configureGPU(unsigned, unsigned, bool, unsigned, bool, boost::optional) { return false; } static void setNumInstances(unsigned _instances) { s_numInstances = std::min(_instances, std::thread::hardware_concurrency()); } protected: void kickOff() override @@ -122,6 +122,7 @@ public: unsigned _deviceId, bool _allowCPU, unsigned _extraGPUMemory, + bool _forceSingleChunk, boost::optional _currentBlock ); static void setNumInstances(unsigned _instances) { s_numInstances = std::min(_instances, getNumDevices()); } From 113acd7bba5481565dad1862c2e883251571334d Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 12 Jun 2015 18:12:21 +0200 Subject: [PATCH 43/69] made signals/handler not dependent on destruction order --- libethcore/Common.h | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/libethcore/Common.h b/libethcore/Common.h index 64eb6de29..6f23cb0e8 100644 --- a/libethcore/Common.h +++ b/libethcore/Common.h @@ -123,28 +123,43 @@ struct ImportRequirements class Signal { public: + using Callback = std::function; + class HandlerAux { friend class Signal; public: ~HandlerAux() { if (m_s) m_s->m_fire.erase(m_i); m_s = nullptr; } + void reset() { m_s = nullptr; } + void fire() { m_h(); } private: - HandlerAux(unsigned _i, Signal* _s): m_i(_i), m_s(_s) {} + HandlerAux(unsigned _i, Signal* _s, Callback const& _h): m_i(_i), m_s(_s), m_h(_h) {} unsigned m_i = 0; Signal* m_s = nullptr; + Callback m_h; }; - using Callback = std::function; + ~Signal() + { + for (auto const& h : m_fire) + h.second->reset(); + } - std::shared_ptr add(Callback const& _h) { auto n = m_fire.empty() ? 0 : (m_fire.rbegin()->first + 1); m_fire[n] = _h; return std::shared_ptr(new HandlerAux(n, this)); } + std::shared_ptr add(Callback const& _h) + { + auto n = m_fire.empty() ? 0 : (m_fire.rbegin()->first + 1); + auto h = std::shared_ptr(new HandlerAux(n, this, _h)); + m_fire[n] = h; + return h; + } - void operator()() { for (auto const& f: m_fire) f.second(); } + void operator()() { for (auto const& f: m_fire) f.second->fire(); } private: - std::map m_fire; + std::map> m_fire; }; using Handler = std::shared_ptr; From eb140df18ae82f7e79b6c53553486026c20dac06 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 13 Jun 2015 17:46:16 +0800 Subject: [PATCH 44/69] Admin methods. --- libdevcore/Common.cpp | 2 +- libethereum/Client.cpp | 75 +- libethereum/Client.h | 2 + libjsqrc/ethereumjs/dist/web3-light.js | 5586 ------------------- libjsqrc/ethereumjs/dist/web3-light.min.js | 2 - libjsqrc/ethereumjs/dist/web3.js | 27 +- libjsqrc/ethereumjs/dist/web3.min.js | 4 +- libjsqrc/ethereumjs/gulpfile.js | 2 +- libjsqrc/ethereumjs/lib/web3.js | 5 +- libjsqrc/ethereumjs/lib/web3/property.js | 1 + libweb3jsonrpc/WebThreeStubServer.h | 2 +- libweb3jsonrpc/WebThreeStubServerBase.cpp | 77 + libweb3jsonrpc/WebThreeStubServerBase.h | 26 +- libweb3jsonrpc/abstractwebthreestubserver.h | 126 + libweb3jsonrpc/spec.json | 23 +- test/libweb3jsonrpc/webthreestubclient.h | 227 + 16 files changed, 552 insertions(+), 5635 deletions(-) delete mode 100644 libjsqrc/ethereumjs/dist/web3-light.js delete mode 100644 libjsqrc/ethereumjs/dist/web3-light.min.js diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 5fe2775d2..22ea584c1 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -28,7 +28,7 @@ using namespace dev; namespace dev { -char const* Version = "0.9.25"; +char const* Version = "0.9.26"; const u256 UndefinedU256 = ~(u256)0; diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 304185a4d..657e25772 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -611,11 +611,21 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution) return true; } +unsigned static const c_syncMin = 1; +unsigned static const c_syncMax = 100; +double static const c_targetDuration = 0.5; + void Client::syncBlockQueue() { ImportRoute ir; cwork << "BQ ==> CHAIN ==> STATE"; - tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 10 + 5); + boost::timer t; + tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, m_syncAmount); + double elapsed = t.elapsed(); + if (elapsed > 0.55 && m_syncAmount > c_syncMin) + m_syncAmount = max(c_syncMin, m_syncAmount * 9 / 10); + else if (elapsed < 0.45 && m_syncAmount < c_syncMax) + m_syncAmount = min(c_syncMax, m_syncAmount * 11 / 10 + 1); if (ir.first.empty()) return; onChainChanged(ir); @@ -688,43 +698,46 @@ void Client::onChainChanged(ImportRoute const& _ir) // RESTART MINING - bool preChanged = false; - State newPreMine; - DEV_READ_GUARDED(x_preMine) - newPreMine = m_preMine; + if (!m_bq.items().first) + { + bool preChanged = false; + State newPreMine; + DEV_READ_GUARDED(x_preMine) + newPreMine = m_preMine; - // TODO: use m_postMine to avoid re-evaluating our own blocks. - preChanged = newPreMine.sync(m_bc); + // TODO: use m_postMine to avoid re-evaluating our own blocks. + preChanged = newPreMine.sync(m_bc); - if (preChanged || m_postMine.address() != m_preMine.address()) - { - if (isMining()) - cnote << "New block on chain."; + if (preChanged || m_postMine.address() != m_preMine.address()) + { + if (isMining()) + cnote << "New block on chain."; + + DEV_WRITE_GUARDED(x_preMine) + m_preMine = newPreMine; + DEV_WRITE_GUARDED(x_working) + m_working = newPreMine; + DEV_READ_GUARDED(x_postMine) + for (auto const& t: m_postMine.pending()) + { + clog(ClientNote) << "Resubmitting post-mine transaction " << t; + auto ir = m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry); + if (ir != ImportResult::Success) + onTransactionQueueReady(); + } + DEV_READ_GUARDED(x_working) DEV_WRITE_GUARDED(x_postMine) + m_postMine = m_working; - DEV_WRITE_GUARDED(x_preMine) - m_preMine = newPreMine; - DEV_WRITE_GUARDED(x_working) - m_working = newPreMine; - DEV_READ_GUARDED(x_postMine) - for (auto const& t: m_postMine.pending()) - { - clog(ClientNote) << "Resubmitting post-mine transaction " << t; - auto ir = m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry); - if (ir != ImportResult::Success) - onTransactionQueueReady(); - } - DEV_READ_GUARDED(x_working) DEV_WRITE_GUARDED(x_postMine) - m_postMine = m_working; + changeds.insert(PendingChangedFilter); - changeds.insert(PendingChangedFilter); + onPostStateChanged(); + } - onPostStateChanged(); + // Quick hack for now - the TQ at this point already has the prior pending transactions in it; + // we should resync with it manually until we are stricter about what constitutes "knowing". + onTransactionQueueReady(); } - // Quick hack for now - the TQ at this point already has the prior pending transactions in it; - // we should resync with it manually until we are stricter about what constitutes "knowing". - onTransactionQueueReady(); - noteChanged(changeds); } diff --git a/libethereum/Client.h b/libethereum/Client.h index 5cae50ed0..b70f01155 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -338,6 +338,8 @@ private: mutable std::chrono::system_clock::time_point m_lastTick = std::chrono::system_clock::now(); ///< When did we last tick()? + unsigned m_syncAmount = 50; ///< Number of blocks to sync in each go. + ActivityReport m_report; std::condition_variable m_signalled; diff --git a/libjsqrc/ethereumjs/dist/web3-light.js b/libjsqrc/ethereumjs/dist/web3-light.js deleted file mode 100644 index c1452e786..000000000 --- a/libjsqrc/ethereumjs/dist/web3-light.js +++ /dev/null @@ -1,5586 +0,0 @@ -require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o. -*/ -/** - * @file coder.js - * @author Marek Kotewicz - * @date 2015 - */ - -var BigNumber = require('bignumber.js'); -var utils = require('../utils/utils'); -var f = require('./formatters'); -var SolidityParam = require('./param'); - -/** - * Should be used to check if a type is an array type - * - * @method isArrayType - * @param {String} type - * @return {Bool} true is the type is an array, otherwise false - */ -var isArrayType = function (type) { - return type.slice(-2) === '[]'; -}; - -/** - * SolidityType prototype is used to encode/decode solidity params of certain type - */ -var SolidityType = function (config) { - this._name = config.name; - this._match = config.match; - this._mode = config.mode; - this._inputFormatter = config.inputFormatter; - this._outputFormatter = config.outputFormatter; -}; - -/** - * Should be used to determine if this SolidityType do match given type - * - * @method isType - * @param {String} name - * @return {Bool} true if type match this SolidityType, otherwise false - */ -SolidityType.prototype.isType = function (name) { - if (this._match === 'strict') { - return this._name === name || (name.indexOf(this._name) === 0 && name.slice(this._name.length) === '[]'); - } else if (this._match === 'prefix') { - // TODO better type detection! - return name.indexOf(this._name) === 0; - } -}; - -/** - * Should be used to transform plain param to SolidityParam object - * - * @method formatInput - * @param {Object} param - plain object, or an array of objects - * @param {Bool} arrayType - true if a param should be encoded as an array - * @return {SolidityParam} encoded param wrapped in SolidityParam object - */ -SolidityType.prototype.formatInput = function (param, arrayType) { - if (utils.isArray(param) && arrayType) { // TODO: should fail if this two are not the same - var self = this; - return param.map(function (p) { - return self._inputFormatter(p); - }).reduce(function (acc, current) { - return acc.combine(current); - }, f.formatInputInt(param.length)).withOffset(32); - } - return this._inputFormatter(param); -}; - -/** - * Should be used to transoform SolidityParam to plain param - * - * @method formatOutput - * @param {SolidityParam} byteArray - * @param {Bool} arrayType - true if a param should be decoded as an array - * @return {Object} plain decoded param - */ -SolidityType.prototype.formatOutput = function (param, arrayType) { - if (arrayType) { - // let's assume, that we solidity will never return long arrays :P - var result = []; - var length = new BigNumber(param.dynamicPart().slice(0, 64), 16); - for (var i = 0; i < length * 64; i += 64) { - result.push(this._outputFormatter(new SolidityParam(param.dynamicPart().substr(i + 64, 64)))); - } - return result; - } - return this._outputFormatter(param); -}; - -/** - * Should be used to slice single param from bytes - * - * @method sliceParam - * @param {String} bytes - * @param {Number} index of param to slice - * @param {String} type - * @returns {SolidityParam} param - */ -SolidityType.prototype.sliceParam = function (bytes, index, type) { - if (this._mode === 'bytes') { - return SolidityParam.decodeBytes(bytes, index); - } else if (isArrayType(type)) { - return SolidityParam.decodeArray(bytes, index); - } - return SolidityParam.decodeParam(bytes, index); -}; - -/** - * SolidityCoder prototype should be used to encode/decode solidity params of any type - */ -var SolidityCoder = function (types) { - this._types = types; -}; - -/** - * This method should be used to transform type to SolidityType - * - * @method _requireType - * @param {String} type - * @returns {SolidityType} - * @throws {Error} throws if no matching type is found - */ -SolidityCoder.prototype._requireType = function (type) { - var solidityType = this._types.filter(function (t) { - return t.isType(type); - })[0]; - - if (!solidityType) { - throw Error('invalid solidity type!: ' + type); - } - - return solidityType; -}; - -/** - * Should be used to transform plain param of given type to SolidityParam - * - * @method _formatInput - * @param {String} type of param - * @param {Object} plain param - * @return {SolidityParam} - */ -SolidityCoder.prototype._formatInput = function (type, param) { - return this._requireType(type).formatInput(param, isArrayType(type)); -}; - -/** - * Should be used to encode plain param - * - * @method encodeParam - * @param {String} type - * @param {Object} plain param - * @return {String} encoded plain param - */ -SolidityCoder.prototype.encodeParam = function (type, param) { - return this._formatInput(type, param).encode(); -}; - -/** - * Should be used to encode list of params - * - * @method encodeParams - * @param {Array} types - * @param {Array} params - * @return {String} encoded list of params - */ -SolidityCoder.prototype.encodeParams = function (types, params) { - var self = this; - var solidityParams = types.map(function (type, index) { - return self._formatInput(type, params[index]); - }); - - return SolidityParam.encodeList(solidityParams); -}; - -/** - * Should be used to decode bytes to plain param - * - * @method decodeParam - * @param {String} type - * @param {String} bytes - * @return {Object} plain param - */ -SolidityCoder.prototype.decodeParam = function (type, bytes) { - return this.decodeParams([type], bytes)[0]; -}; - -/** - * Should be used to decode list of params - * - * @method decodeParam - * @param {Array} types - * @param {String} bytes - * @return {Array} array of plain params - */ -SolidityCoder.prototype.decodeParams = function (types, bytes) { - var self = this; - return types.map(function (type, index) { - var solidityType = self._requireType(type); - var p = solidityType.sliceParam(bytes, index, type); - return solidityType.formatOutput(p, isArrayType(type)); - }); -}; - -var coder = new SolidityCoder([ - new SolidityType({ - name: 'address', - match: 'strict', - mode: 'value', - inputFormatter: f.formatInputInt, - outputFormatter: f.formatOutputAddress - }), - new SolidityType({ - name: 'bool', - match: 'strict', - mode: 'value', - inputFormatter: f.formatInputBool, - outputFormatter: f.formatOutputBool - }), - new SolidityType({ - name: 'int', - match: 'prefix', - mode: 'value', - inputFormatter: f.formatInputInt, - outputFormatter: f.formatOutputInt, - }), - new SolidityType({ - name: 'uint', - match: 'prefix', - mode: 'value', - inputFormatter: f.formatInputInt, - outputFormatter: f.formatOutputUInt - }), - new SolidityType({ - name: 'bytes', - match: 'strict', - mode: 'bytes', - inputFormatter: f.formatInputDynamicBytes, - outputFormatter: f.formatOutputDynamicBytes - }), - new SolidityType({ - name: 'bytes', - match: 'prefix', - mode: 'value', - inputFormatter: f.formatInputBytes, - outputFormatter: f.formatOutputBytes - }), - new SolidityType({ - name: 'real', - match: 'prefix', - mode: 'value', - inputFormatter: f.formatInputReal, - outputFormatter: f.formatOutputReal - }), - new SolidityType({ - name: 'ureal', - match: 'prefix', - mode: 'value', - inputFormatter: f.formatInputReal, - outputFormatter: f.formatOutputUReal - }) -]); - -module.exports = coder; - - -},{"../utils/utils":7,"./formatters":2,"./param":3,"bignumber.js":"bignumber.js"}],2:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file formatters.js - * @author Marek Kotewicz - * @date 2015 - */ - -var BigNumber = require('bignumber.js'); -var utils = require('../utils/utils'); -var c = require('../utils/config'); -var SolidityParam = require('./param'); - - -/** - * Formats input value to byte representation of int - * If value is negative, return it's two's complement - * If the value is floating point, round it down - * - * @method formatInputInt - * @param {String|Number|BigNumber} value that needs to be formatted - * @returns {SolidityParam} - */ -var formatInputInt = function (value) { - var padding = c.ETH_PADDING * 2; - BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); - var result = utils.padLeft(utils.toTwosComplement(value).round().toString(16), padding); - return new SolidityParam(result); -}; - -/** - * Formats input value to byte representation of string - * - * @method formatInputBytes - * @param {String} - * @returns {SolidityParam} - */ -var formatInputBytes = function (value) { - var result = utils.fromAscii(value, c.ETH_PADDING).substr(2); - return new SolidityParam(result); -}; - -/** - * Formats input value to byte representation of string - * - * @method formatInputDynamicBytes - * @param {String} - * @returns {SolidityParam} - */ -var formatInputDynamicBytes = function (value) { - var result = utils.fromAscii(value, c.ETH_PADDING).substr(2); - return new SolidityParam(formatInputInt(value.length).value + result, 32); -}; - -/** - * Formats input value to byte representation of bool - * - * @method formatInputBool - * @param {Boolean} - * @returns {SolidityParam} - */ -var formatInputBool = function (value) { - var result = '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0'); - return new SolidityParam(result); -}; - -/** - * Formats input value to byte representation of real - * Values are multiplied by 2^m and encoded as integers - * - * @method formatInputReal - * @param {String|Number|BigNumber} - * @returns {SolidityParam} - */ -var formatInputReal = function (value) { - return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); -}; - -/** - * Check if input value is negative - * - * @method signedIsNegative - * @param {String} value is hex format - * @returns {Boolean} true if it is negative, otherwise false - */ -var signedIsNegative = function (value) { - return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1'; -}; - -/** - * Formats right-aligned output bytes to int - * - * @method formatOutputInt - * @param {SolidityParam} param - * @returns {BigNumber} right-aligned output bytes formatted to big number - */ -var formatOutputInt = function (param) { - var value = param.staticPart() || "0"; - - // check if it's negative number - // it it is, return two's complement - if (signedIsNegative(value)) { - return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1); - } - return new BigNumber(value, 16); -}; - -/** - * Formats right-aligned output bytes to uint - * - * @method formatOutputUInt - * @param {SolidityParam} - * @returns {BigNumeber} right-aligned output bytes formatted to uint - */ -var formatOutputUInt = function (param) { - var value = param.staticPart() || "0"; - return new BigNumber(value, 16); -}; - -/** - * Formats right-aligned output bytes to real - * - * @method formatOutputReal - * @param {SolidityParam} - * @returns {BigNumber} input bytes formatted to real - */ -var formatOutputReal = function (param) { - return formatOutputInt(param).dividedBy(new BigNumber(2).pow(128)); -}; - -/** - * Formats right-aligned output bytes to ureal - * - * @method formatOutputUReal - * @param {SolidityParam} - * @returns {BigNumber} input bytes formatted to ureal - */ -var formatOutputUReal = function (param) { - return formatOutputUInt(param).dividedBy(new BigNumber(2).pow(128)); -}; - -/** - * Should be used to format output bool - * - * @method formatOutputBool - * @param {SolidityParam} - * @returns {Boolean} right-aligned input bytes formatted to bool - */ -var formatOutputBool = function (param) { - return param.staticPart() === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; -}; - -/** - * Should be used to format output string - * - * @method formatOutputBytes - * @param {SolidityParam} left-aligned hex representation of string - * @returns {String} ascii string - */ -var formatOutputBytes = function (param) { - // length might also be important! - return utils.toAscii(param.staticPart()); -}; - -/** - * Should be used to format output string - * - * @method formatOutputDynamicBytes - * @param {SolidityParam} left-aligned hex representation of string - * @returns {String} ascii string - */ -var formatOutputDynamicBytes = function (param) { - // length might also be important! - return utils.toAscii(param.dynamicPart().slice(64)); -}; - -/** - * Should be used to format output address - * - * @method formatOutputAddress - * @param {SolidityParam} right-aligned input bytes - * @returns {String} address - */ -var formatOutputAddress = function (param) { - var value = param.staticPart(); - return "0x" + value.slice(value.length - 40, value.length); -}; - -module.exports = { - formatInputInt: formatInputInt, - formatInputBytes: formatInputBytes, - formatInputDynamicBytes: formatInputDynamicBytes, - formatInputBool: formatInputBool, - formatInputReal: formatInputReal, - formatOutputInt: formatOutputInt, - formatOutputUInt: formatOutputUInt, - formatOutputReal: formatOutputReal, - formatOutputUReal: formatOutputUReal, - formatOutputBool: formatOutputBool, - formatOutputBytes: formatOutputBytes, - formatOutputDynamicBytes: formatOutputDynamicBytes, - formatOutputAddress: formatOutputAddress -}; - - -},{"../utils/config":5,"../utils/utils":7,"./param":3,"bignumber.js":"bignumber.js"}],3:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file param.js - * @author Marek Kotewicz - * @date 2015 - */ - -var utils = require('../utils/utils'); - -/** - * SolidityParam object prototype. - * Should be used when encoding, decoding solidity bytes - */ -var SolidityParam = function (value, offset) { - this.value = value || ''; - this.offset = offset; // offset in bytes -}; - -/** - * This method should be used to get length of params's dynamic part - * - * @method dynamicPartLength - * @returns {Number} length of dynamic part (in bytes) - */ -SolidityParam.prototype.dynamicPartLength = function () { - return this.dynamicPart().length / 2; -}; - -/** - * This method should be used to create copy of solidity param with different offset - * - * @method withOffset - * @param {Number} offset length in bytes - * @returns {SolidityParam} new solidity param with applied offset - */ -SolidityParam.prototype.withOffset = function (offset) { - return new SolidityParam(this.value, offset); -}; - -/** - * This method should be used to combine solidity params together - * eg. when appending an array - * - * @method combine - * @param {SolidityParam} param with which we should combine - * @param {SolidityParam} result of combination - */ -SolidityParam.prototype.combine = function (param) { - return new SolidityParam(this.value + param.value); -}; - -/** - * This method should be called to check if param has dynamic size. - * If it has, it returns true, otherwise false - * - * @method isDynamic - * @returns {Boolean} - */ -SolidityParam.prototype.isDynamic = function () { - return this.value.length > 64 || this.offset !== undefined; -}; - -/** - * This method should be called to transform offset to bytes - * - * @method offsetAsBytes - * @returns {String} bytes representation of offset - */ -SolidityParam.prototype.offsetAsBytes = function () { - return !this.isDynamic() ? '' : utils.padLeft(utils.toTwosComplement(this.offset).toString(16), 64); -}; - -/** - * This method should be called to get static part of param - * - * @method staticPart - * @returns {String} offset if it is a dynamic param, otherwise value - */ -SolidityParam.prototype.staticPart = function () { - if (!this.isDynamic()) { - return this.value; - } - return this.offsetAsBytes(); -}; - -/** - * This method should be called to get dynamic part of param - * - * @method dynamicPart - * @returns {String} returns a value if it is a dynamic param, otherwise empty string - */ -SolidityParam.prototype.dynamicPart = function () { - return this.isDynamic() ? this.value : ''; -}; - -/** - * This method should be called to encode param - * - * @method encode - * @returns {String} - */ -SolidityParam.prototype.encode = function () { - return this.staticPart() + this.dynamicPart(); -}; - -/** - * This method should be called to encode array of params - * - * @method encodeList - * @param {Array[SolidityParam]} params - * @returns {String} - */ -SolidityParam.encodeList = function (params) { - - // updating offsets - var totalOffset = params.length * 32; - var offsetParams = params.map(function (param) { - if (!param.isDynamic()) { - return param; - } - var offset = totalOffset; - totalOffset += param.dynamicPartLength(); - return param.withOffset(offset); - }); - - // encode everything! - return offsetParams.reduce(function (result, param) { - return result + param.dynamicPart(); - }, offsetParams.reduce(function (result, param) { - return result + param.staticPart(); - }, '')); -}; - -/** - * This method should be used to decode plain (static) solidity param at given index - * - * @method decodeParam - * @param {String} bytes - * @param {Number} index - * @returns {SolidityParam} - */ -SolidityParam.decodeParam = function (bytes, index) { - index = index || 0; - return new SolidityParam(bytes.substr(index * 64, 64)); -}; - -/** - * This method should be called to get offset value from bytes at given index - * - * @method getOffset - * @param {String} bytes - * @param {Number} index - * @returns {Number} offset as number - */ -var getOffset = function (bytes, index) { - // we can do this cause offset is rather small - return parseInt('0x' + bytes.substr(index * 64, 64)); -}; - -/** - * This method should be called to decode solidity bytes param at given index - * - * @method decodeBytes - * @param {String} bytes - * @param {Number} index - * @returns {SolidityParam} - */ -SolidityParam.decodeBytes = function (bytes, index) { - index = index || 0; - //TODO add support for strings longer than 32 bytes - //var length = parseInt('0x' + bytes.substr(offset * 64, 64)); - - var offset = getOffset(bytes, index); - - // 2 * , cause we also parse length - return new SolidityParam(bytes.substr(offset * 2, 2 * 64), 0); -}; - -/** - * This method should be used to decode solidity array at given index - * - * @method decodeArray - * @param {String} bytes - * @param {Number} index - * @returns {SolidityParam} - */ -SolidityParam.decodeArray = function (bytes, index) { - index = index || 0; - var offset = getOffset(bytes, index); - var length = parseInt('0x' + bytes.substr(offset * 2, 64)); - return new SolidityParam(bytes.substr(offset * 2, (length + 1) * 64), 0); -}; - -module.exports = SolidityParam; - - -},{"../utils/utils":7}],4:[function(require,module,exports){ -'use strict'; - -// go env doesn't have and need XMLHttpRequest -if (typeof XMLHttpRequest === 'undefined') { - exports.XMLHttpRequest = {}; -} else { - exports.XMLHttpRequest = XMLHttpRequest; // jshint ignore:line -} - - -},{}],5:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file config.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -/** - * Utils - * - * @module utils - */ - -/** - * Utility functions - * - * @class [utils] config - * @constructor - */ - -/// required to define ETH_BIGNUMBER_ROUNDING_MODE -var BigNumber = require('bignumber.js'); - -var ETH_UNITS = [ - 'wei', - 'kwei', - 'Mwei', - 'Gwei', - 'szabo', - 'finney', - 'femtoether', - 'picoether', - 'nanoether', - 'microether', - 'milliether', - 'nano', - 'micro', - 'milli', - 'ether', - 'grand', - 'Mether', - 'Gether', - 'Tether', - 'Pether', - 'Eether', - 'Zether', - 'Yether', - 'Nether', - 'Dether', - 'Vether', - 'Uether' -]; - -module.exports = { - ETH_PADDING: 32, - ETH_SIGNATURE_LENGTH: 4, - ETH_UNITS: ETH_UNITS, - ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }, - ETH_POLLING_TIMEOUT: 1000, - defaultBlock: 'latest', - defaultAccount: undefined -}; - - -},{"bignumber.js":"bignumber.js"}],6:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file sha3.js - * @author Marek Kotewicz - * @date 2015 - */ - -var utils = require('./utils'); -var sha3 = require('crypto-js/sha3'); - -module.exports = function (str, isNew) { - if (str.substr(0, 2) === '0x' && !isNew) { - console.warn('requirement of using web3.fromAscii before sha3 is deprecated'); - console.warn('new usage: \'web3.sha3("hello")\''); - console.warn('see https://github.com/ethereum/web3.js/pull/205'); - console.warn('if you need to hash hex value, you can do \'sha3("0xfff", true)\''); - str = utils.toAscii(str); - } - - return sha3(str, { - outputLength: 256 - }).toString(); -}; - - -},{"./utils":7,"crypto-js/sha3":33}],7:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file utils.js - * @author Marek Kotewicz - * @date 2015 - */ - -/** - * Utils - * - * @module utils - */ - -/** - * Utility functions - * - * @class [utils] utils - * @constructor - */ - -var BigNumber = require('bignumber.js'); - -var unitMap = { - 'wei': '1', - 'kwei': '1000', - 'ada': '1000', - 'femtoether': '1000', - 'mwei': '1000000', - 'babbage': '1000000', - 'picoether': '1000000', - 'gwei': '1000000000', - 'shannon': '1000000000', - 'nanoether': '1000000000', - 'nano': '1000000000', - 'szabo': '1000000000000', - 'microether': '1000000000000', - 'micro': '1000000000000', - 'finney': '1000000000000000', - 'milliether': '1000000000000000', - 'milli': '1000000000000000', - 'ether': '1000000000000000000', - 'kether': '1000000000000000000000', - 'grand': '1000000000000000000000', - 'einstein': '1000000000000000000000', - 'mether': '1000000000000000000000000', - 'gether': '1000000000000000000000000000', - 'tether': '1000000000000000000000000000000' -}; - -/** - * Should be called to pad string to expected length - * - * @method padLeft - * @param {String} string to be padded - * @param {Number} characters that result string should have - * @param {String} sign, by default 0 - * @returns {String} right aligned string - */ -var padLeft = function (string, chars, sign) { - return new Array(chars - string.length + 1).join(sign ? sign : "0") + string; -}; - -/** - * Should be called to get sting from it's hex representation - * - * @method toAscii - * @param {String} string in hex - * @returns {String} ascii string representation of hex value - */ -var toAscii = function(hex) { -// Find termination - var str = ""; - var i = 0, l = hex.length; - if (hex.substring(0, 2) === '0x') { - i = 2; - } - for (; i < l; i+=2) { - var code = parseInt(hex.substr(i, 2), 16); - if (code === 0) { - break; - } - - str += String.fromCharCode(code); - } - - return str; -}; - -/** - * Shold be called to get hex representation (prefixed by 0x) of ascii string - * - * @method toHexNative - * @param {String} string - * @returns {String} hex representation of input string - */ -var toHexNative = function(str) { - var hex = ""; - for(var i = 0; i < str.length; i++) { - var n = str.charCodeAt(i).toString(16); - hex += n.length < 2 ? '0' + n : n; - } - - return hex; -}; - -/** - * Shold be called to get hex representation (prefixed by 0x) of ascii string - * - * @method fromAscii - * @param {String} string - * @param {Number} optional padding - * @returns {String} hex representation of input string - */ -var fromAscii = function(str, pad) { - pad = pad === undefined ? 0 : pad; - var hex = toHexNative(str); - while (hex.length < pad*2) - hex += "00"; - return "0x" + hex; -}; - -/** - * Should be used to create full function/event name from json abi - * - * @method transformToFullName - * @param {Object} json-abi - * @return {String} full fnction/event name - */ -var transformToFullName = function (json) { - if (json.name.indexOf('(') !== -1) { - return json.name; - } - - var typeName = json.inputs.map(function(i){return i.type; }).join(); - return json.name + '(' + typeName + ')'; -}; - -/** - * Should be called to get display name of contract function - * - * @method extractDisplayName - * @param {String} name of function/event - * @returns {String} display name for function/event eg. multiply(uint256) -> multiply - */ -var extractDisplayName = function (name) { - var length = name.indexOf('('); - return length !== -1 ? name.substr(0, length) : name; -}; - -/// @returns overloaded part of function/event name -var extractTypeName = function (name) { - /// TODO: make it invulnerable - var length = name.indexOf('('); - return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : ""; -}; - -/** - * Converts value to it's decimal representation in string - * - * @method toDecimal - * @param {String|Number|BigNumber} - * @return {String} - */ -var toDecimal = function (value) { - return toBigNumber(value).toNumber(); -}; - -/** - * Converts value to it's hex representation - * - * @method fromDecimal - * @param {String|Number|BigNumber} - * @return {String} - */ -var fromDecimal = function (value) { - var number = toBigNumber(value); - var result = number.toString(16); - - return number.lessThan(0) ? '-0x' + result.substr(1) : '0x' + result; -}; - -/** - * Auto converts any given value into it's hex representation. - * - * And even stringifys objects before. - * - * @method toHex - * @param {String|Number|BigNumber|Object} - * @return {String} - */ -var toHex = function (val) { - /*jshint maxcomplexity:7 */ - - if (isBoolean(val)) - return fromDecimal(+val); - - if (isBigNumber(val)) - return fromDecimal(val); - - if (isObject(val)) - return fromAscii(JSON.stringify(val)); - - // if its a negative number, pass it through fromDecimal - if (isString(val)) { - if (val.indexOf('-0x') === 0) - return fromDecimal(val); - else if (!isFinite(val)) - return fromAscii(val); - } - - return fromDecimal(val); -}; - -/** - * Returns value of unit in Wei - * - * @method getValueOfUnit - * @param {String} unit the unit to convert to, default ether - * @returns {BigNumber} value of the unit (in Wei) - * @throws error if the unit is not correct:w - */ -var getValueOfUnit = function (unit) { - unit = unit ? unit.toLowerCase() : 'ether'; - var unitValue = unitMap[unit]; - if (unitValue === undefined) { - throw new Error('This unit doesn\'t exists, please use the one of the following units' + JSON.stringify(unitMap, null, 2)); - } - return new BigNumber(unitValue, 10); -}; - -/** - * Takes a number of wei and converts it to any other ether unit. - * - * Possible units are: - * SI Short SI Full Effigy Other - * - kwei femtoether ada - * - mwei picoether babbage - * - gwei nanoether shannon nano - * - -- microether szabo micro - * - -- milliether finney milli - * - ether -- -- - * - kether einstein grand - * - mether - * - gether - * - tether - * - * @method fromWei - * @param {Number|String} number can be a number, number string or a HEX of a decimal - * @param {String} unit the unit to convert to, default ether - * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number -*/ -var fromWei = function(number, unit) { - var returnValue = toBigNumber(number).dividedBy(getValueOfUnit(unit)); - - return isBigNumber(number) ? returnValue : returnValue.toString(10); -}; - -/** - * Takes a number of a unit and converts it to wei. - * - * Possible units are: - * SI Short SI Full Effigy Other - * - kwei femtoether ada - * - mwei picoether babbage - * - gwei nanoether shannon nano - * - -- microether szabo micro - * - -- milliether finney milli - * - ether -- -- - * - kether einstein grand - * - mether - * - gether - * - tether - * - * @method toWei - * @param {Number|String|BigNumber} number can be a number, number string or a HEX of a decimal - * @param {String} unit the unit to convert from, default ether - * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number -*/ -var toWei = function(number, unit) { - var returnValue = toBigNumber(number).times(getValueOfUnit(unit)); - - return isBigNumber(number) ? returnValue : returnValue.toString(10); -}; - -/** - * Takes an input and transforms it into an bignumber - * - * @method toBigNumber - * @param {Number|String|BigNumber} a number, string, HEX string or BigNumber - * @return {BigNumber} BigNumber -*/ -var toBigNumber = function(number) { - /*jshint maxcomplexity:5 */ - number = number || 0; - if (isBigNumber(number)) - return number; - - if (isString(number) && (number.indexOf('0x') === 0 || number.indexOf('-0x') === 0)) { - return new BigNumber(number.replace('0x',''), 16); - } - - return new BigNumber(number.toString(10), 10); -}; - -/** - * Takes and input transforms it into bignumber and if it is negative value, into two's complement - * - * @method toTwosComplement - * @param {Number|String|BigNumber} - * @return {BigNumber} - */ -var toTwosComplement = function (number) { - var bigNumber = toBigNumber(number); - if (bigNumber.lessThan(0)) { - return new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(bigNumber).plus(1); - } - return bigNumber; -}; - -/** - * Checks if the given string is strictly an address - * - * @method isStrictAddress - * @param {String} address the given HEX adress - * @return {Boolean} -*/ -var isStrictAddress = function (address) { - return /^0x[0-9a-f]{40}$/.test(address); -}; - -/** - * Checks if the given string is an address - * - * @method isAddress - * @param {String} address the given HEX adress - * @return {Boolean} -*/ -var isAddress = function (address) { - return /^(0x)?[0-9a-f]{40}$/.test(address); -}; - -/** - * Transforms given string to valid 20 bytes-length addres with 0x prefix - * - * @method toAddress - * @param {String} address - * @return {String} formatted address - */ -var toAddress = function (address) { - if (isStrictAddress(address)) { - return address; - } - - if (/^[0-9a-f]{40}$/.test(address)) { - return '0x' + address; - } - - return '0x' + padLeft(toHex(address).substr(2), 40); -}; - - -/** - * Returns true if object is BigNumber, otherwise false - * - * @method isBigNumber - * @param {Object} - * @return {Boolean} - */ -var isBigNumber = function (object) { - return object instanceof BigNumber || - (object && object.constructor && object.constructor.name === 'BigNumber'); -}; - -/** - * Returns true if object is string, otherwise false - * - * @method isString - * @param {Object} - * @return {Boolean} - */ -var isString = function (object) { - return typeof object === 'string' || - (object && object.constructor && object.constructor.name === 'String'); -}; - -/** - * Returns true if object is function, otherwise false - * - * @method isFunction - * @param {Object} - * @return {Boolean} - */ -var isFunction = function (object) { - return typeof object === 'function'; -}; - -/** - * Returns true if object is Objet, otherwise false - * - * @method isObject - * @param {Object} - * @return {Boolean} - */ -var isObject = function (object) { - return typeof object === 'object'; -}; - -/** - * Returns true if object is boolean, otherwise false - * - * @method isBoolean - * @param {Object} - * @return {Boolean} - */ -var isBoolean = function (object) { - return typeof object === 'boolean'; -}; - -/** - * Returns true if object is array, otherwise false - * - * @method isArray - * @param {Object} - * @return {Boolean} - */ -var isArray = function (object) { - return object instanceof Array; -}; - -/** - * Returns true if given string is valid json object - * - * @method isJson - * @param {String} - * @return {Boolean} - */ -var isJson = function (str) { - try { - return !!JSON.parse(str); - } catch (e) { - return false; - } -}; - -/** - * This method should be called to check if string is valid ethereum IBAN number - * Supports direct and indirect IBANs - * - * @method isIBAN - * @param {String} - * @return {Boolean} - */ -var isIBAN = function (iban) { - return /^XE[0-9]{2}(ETH[0-9A-Z]{13}|[0-9A-Z]{30})$/.test(iban); -}; - -module.exports = { - padLeft: padLeft, - toHex: toHex, - toDecimal: toDecimal, - fromDecimal: fromDecimal, - toAscii: toAscii, - fromAscii: fromAscii, - transformToFullName: transformToFullName, - extractDisplayName: extractDisplayName, - extractTypeName: extractTypeName, - toWei: toWei, - fromWei: fromWei, - toBigNumber: toBigNumber, - toTwosComplement: toTwosComplement, - toAddress: toAddress, - isBigNumber: isBigNumber, - isStrictAddress: isStrictAddress, - isAddress: isAddress, - isFunction: isFunction, - isString: isString, - isObject: isObject, - isBoolean: isBoolean, - isArray: isArray, - isJson: isJson, - isIBAN: isIBAN -}; - - -},{"bignumber.js":"bignumber.js"}],8:[function(require,module,exports){ -module.exports={ - "version": "0.5.0" -} - -},{}],9:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file web3.js - * @authors: - * Jeffrey Wilcke - * Marek Kotewicz - * Marian Oancea - * Fabian Vogelsteller - * Gav Wood - * @date 2014 - */ - -var version = require('./version.json'); -var net = require('./web3/net'); -var eth = require('./web3/eth'); -var db = require('./web3/db'); -var shh = require('./web3/shh'); -var watches = require('./web3/watches'); -var Filter = require('./web3/filter'); -var utils = require('./utils/utils'); -var formatters = require('./web3/formatters'); -var RequestManager = require('./web3/requestmanager'); -var c = require('./utils/config'); -var Property = require('./web3/property'); -var Batch = require('./web3/batch'); -var sha3 = require('./utils/sha3'); - -var web3Properties = [ - new Property({ - name: 'version.client', - getter: 'web3_clientVersion' - }), - new Property({ - name: 'version.network', - getter: 'net_version', - inputFormatter: utils.toDecimal - }), - new Property({ - name: 'version.ethereum', - getter: 'eth_protocolVersion', - inputFormatter: utils.toDecimal - }), - new Property({ - name: 'version.whisper', - getter: 'shh_version', - inputFormatter: utils.toDecimal - }) -]; - -/// creates methods in a given object based on method description on input -/// setups api calls for these methods -var setupMethods = function (obj, methods) { - methods.forEach(function (method) { - method.attachToObject(obj); - }); -}; - -/// creates properties in a given object based on properties description on input -/// setups api calls for these properties -var setupProperties = function (obj, properties) { - properties.forEach(function (property) { - property.attachToObject(obj); - }); -}; - -/// setups web3 object, and it's in-browser executed methods -var web3 = {}; -web3.providers = {}; -web3.version = {}; -web3.version.api = version.version; -web3.eth = {}; - -/*jshint maxparams:4 */ -web3.eth.filter = function (fil, eventParams, options, formatter) { - - // if its event, treat it differently - // TODO: simplify and remove - if (fil._isEvent) { - return fil(eventParams, options); - } - - // what outputLogFormatter? that's wrong - //return new Filter(fil, watches.eth(), formatters.outputLogFormatter); - return new Filter(fil, watches.eth(), formatter || formatters.outputLogFormatter); -}; -/*jshint maxparams:3 */ - -web3.shh = {}; -web3.shh.filter = function (fil) { - return new Filter(fil, watches.shh(), formatters.outputPostFormatter); -}; -web3.net = {}; -web3.db = {}; -web3.setProvider = function (provider) { - RequestManager.getInstance().setProvider(provider); -}; -web3.reset = function () { - RequestManager.getInstance().reset(); - c.defaultBlock = 'latest'; - c.defaultAccount = undefined; -}; -web3.toHex = utils.toHex; -web3.toAscii = utils.toAscii; -web3.fromAscii = utils.fromAscii; -web3.toDecimal = utils.toDecimal; -web3.fromDecimal = utils.fromDecimal; -web3.toBigNumber = utils.toBigNumber; -web3.toWei = utils.toWei; -web3.fromWei = utils.fromWei; -web3.isAddress = utils.isAddress; -web3.isIBAN = utils.isIBAN; -web3.sha3 = sha3; -web3.createBatch = function () { - return new Batch(); -}; - -// ADD defaultblock -Object.defineProperty(web3.eth, 'defaultBlock', { - get: function () { - return c.defaultBlock; - }, - set: function (val) { - c.defaultBlock = val; - return val; - } -}); - -Object.defineProperty(web3.eth, 'defaultAccount', { - get: function () { - return c.defaultAccount; - }, - set: function (val) { - c.defaultAccount = val; - return val; - } -}); - -/// setups all api methods -setupProperties(web3, web3Properties); -setupMethods(web3.net, net.methods); -setupProperties(web3.net, net.properties); -setupMethods(web3.eth, eth.methods); -setupProperties(web3.eth, eth.properties); -setupMethods(web3.db, db.methods); -setupMethods(web3.shh, shh.methods); - -module.exports = web3; - - -},{"./utils/config":5,"./utils/sha3":6,"./utils/utils":7,"./version.json":8,"./web3/batch":10,"./web3/db":12,"./web3/eth":14,"./web3/filter":16,"./web3/formatters":17,"./web3/net":24,"./web3/property":25,"./web3/requestmanager":27,"./web3/shh":28,"./web3/watches":30}],10:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file batch.js - * @author Marek Kotewicz - * @date 2015 - */ - -var RequestManager = require('./requestmanager'); - -var Batch = function () { - this.requests = []; -}; - -/** - * Should be called to add create new request to batch request - * - * @method add - * @param {Object} jsonrpc requet object - */ -Batch.prototype.add = function (request) { - this.requests.push(request); -}; - -/** - * Should be called to execute batch request - * - * @method execute - */ -Batch.prototype.execute = function () { - var requests = this.requests; - RequestManager.getInstance().sendBatch(requests, function (err, results) { - results = results || []; - requests.map(function (request, index) { - return results[index] || {}; - }).map(function (result, index) { - return requests[index].format ? requests[index].format(result.result) : result.result; - }).forEach(function (result, index) { - if (requests[index].callback) { - requests[index].callback(err, result); - } - }); - }); -}; - -module.exports = Batch; - - -},{"./requestmanager":27}],11:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file contract.js - * @author Marek Kotewicz - * @date 2014 - */ - -var web3 = require('../web3'); -var utils = require('../utils/utils'); -var coder = require('../solidity/coder'); -var SolidityEvent = require('./event'); -var SolidityFunction = require('./function'); - -/** - * Should be called to encode constructor params - * - * @method encodeConstructorParams - * @param {Array} abi - * @param {Array} constructor params - */ -var encodeConstructorParams = function (abi, params) { - return abi.filter(function (json) { - return json.type === 'constructor' && json.inputs.length === params.length; - }).map(function (json) { - return json.inputs.map(function (input) { - return input.type; - }); - }).map(function (types) { - return coder.encodeParams(types, params); - })[0] || ''; -}; - -/** - * Should be called to add functions to contract object - * - * @method addFunctionsToContract - * @param {Contract} contract - * @param {Array} abi - */ -var addFunctionsToContract = function (contract, abi) { - abi.filter(function (json) { - return json.type === 'function'; - }).map(function (json) { - return new SolidityFunction(json, contract.address); - }).forEach(function (f) { - f.attachToContract(contract); - }); -}; - -/** - * Should be called to add events to contract object - * - * @method addEventsToContract - * @param {Contract} contract - * @param {Array} abi - */ -var addEventsToContract = function (contract, abi) { - abi.filter(function (json) { - return json.type === 'event'; - }).map(function (json) { - return new SolidityEvent(json, contract.address); - }).forEach(function (e) { - e.attachToContract(contract); - }); -}; - -/** - * Should be called to create new ContractFactory - * - * @method contract - * @param {Array} abi - * @returns {ContractFactory} new contract factory - */ -var contract = function (abi) { - return new ContractFactory(abi); -}; - -/** - * Should be called to create new ContractFactory instance - * - * @method ContractFactory - * @param {Array} abi - */ -var ContractFactory = function (abi) { - this.abi = abi; -}; - -/** - * Should be called to create new contract on a blockchain - * - * @method new - * @param {Any} contract constructor param1 (optional) - * @param {Any} contract constructor param2 (optional) - * @param {Object} contract transaction object (required) - * @param {Function} callback - * @returns {Contract} returns contract if no callback was passed, - * otherwise calls callback function (err, contract) - */ -ContractFactory.prototype.new = function () { - // parse arguments - var options = {}; // required! - var callback; - - var args = Array.prototype.slice.call(arguments); - if (utils.isFunction(args[args.length - 1])) { - callback = args.pop(); - } - - var last = args[args.length - 1]; - if (utils.isObject(last) && !utils.isArray(last)) { - options = args.pop(); - } - - // throw an error if there are no options - - var bytes = encodeConstructorParams(this.abi, args); - options.data += bytes; - - if (!callback) { - var address = web3.eth.sendTransaction(options); - return this.at(address); - } - - var self = this; - web3.eth.sendTransaction(options, function (err, address) { - if (err) { - callback(err); - } - self.at(address, callback); - }); -}; - -/** - * Should be called to get access to existing contract on a blockchain - * - * @method at - * @param {Address} contract address (required) - * @param {Function} callback {optional) - * @returns {Contract} returns contract if no callback was passed, - * otherwise calls callback function (err, contract) - */ -ContractFactory.prototype.at = function (address, callback) { - // TODO: address is required - - if (callback) { - callback(null, new Contract(this.abi, address)); - } - return new Contract(this.abi, address); -}; - -/** - * Should be called to create new contract instance - * - * @method Contract - * @param {Array} abi - * @param {Address} contract address - */ -var Contract = function (abi, address) { - this.address = address; - addFunctionsToContract(this, abi); - addEventsToContract(this, abi); -}; - -module.exports = contract; - - -},{"../solidity/coder":1,"../utils/utils":7,"../web3":9,"./event":15,"./function":18}],12:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file db.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -var Method = require('./method'); - -var putString = new Method({ - name: 'putString', - call: 'db_putString', - params: 3 -}); - - -var getString = new Method({ - name: 'getString', - call: 'db_getString', - params: 2 -}); - -var putHex = new Method({ - name: 'putHex', - call: 'db_putHex', - params: 3 -}); - -var getHex = new Method({ - name: 'getHex', - call: 'db_getHex', - params: 2 -}); - -var methods = [ - putString, getString, putHex, getHex -]; - -module.exports = { - methods: methods -}; - -},{"./method":22}],13:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file errors.js - * @author Marek Kotewicz - * @date 2015 - */ - -module.exports = { - InvalidNumberOfParams: function () { - return new Error('Invalid number of input parameters'); - }, - InvalidConnection: function (host){ - return new Error('CONNECTION ERROR: Couldn\'t connect to node '+ host +', is it running?'); - }, - InvalidProvider: function () { - return new Error('Providor not set or invalid'); - }, - InvalidResponse: function (result){ - var message = !!result && !!result.error && !!result.error.message ? result.error.message : 'Invalid JSON RPC response'; - return new Error(message); - } -}; - - -},{}],14:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file eth.js - * @author Marek Kotewicz - * @author Fabian Vogelsteller - * @date 2015 - */ - -/** - * Web3 - * - * @module web3 - */ - -/** - * Eth methods and properties - * - * An example method object can look as follows: - * - * { - * name: 'getBlock', - * call: blockCall, - * params: 2, - * outputFormatter: formatters.outputBlockFormatter, - * inputFormatter: [ // can be a formatter funciton or an array of functions. Where each item in the array will be used for one parameter - * utils.toHex, // formats paramter 1 - * function(param){ return !!param; } // formats paramter 2 - * ] - * }, - * - * @class [web3] eth - * @constructor - */ - -"use strict"; - -var formatters = require('./formatters'); -var utils = require('../utils/utils'); -var Method = require('./method'); -var Property = require('./property'); - -var blockCall = function (args) { - return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? "eth_getBlockByHash" : "eth_getBlockByNumber"; -}; - -var transactionFromBlockCall = function (args) { - return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getTransactionByBlockHashAndIndex' : 'eth_getTransactionByBlockNumberAndIndex'; -}; - -var uncleCall = function (args) { - return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleByBlockHashAndIndex' : 'eth_getUncleByBlockNumberAndIndex'; -}; - -var getBlockTransactionCountCall = function (args) { - return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getBlockTransactionCountByHash' : 'eth_getBlockTransactionCountByNumber'; -}; - -var uncleCountCall = function (args) { - return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleCountByBlockHash' : 'eth_getUncleCountByBlockNumber'; -}; - -/// @returns an array of objects describing web3.eth api methods - -var getBalance = new Method({ - name: 'getBalance', - call: 'eth_getBalance', - params: 2, - inputFormatter: [utils.toAddress, formatters.inputDefaultBlockNumberFormatter], - outputFormatter: formatters.outputBigNumberFormatter -}); - -var getStorageAt = new Method({ - name: 'getStorageAt', - call: 'eth_getStorageAt', - params: 3, - inputFormatter: [null, utils.toHex, formatters.inputDefaultBlockNumberFormatter] -}); - -var getCode = new Method({ - name: 'getCode', - call: 'eth_getCode', - params: 2, - inputFormatter: [utils.toAddress, formatters.inputDefaultBlockNumberFormatter] -}); - -var getBlock = new Method({ - name: 'getBlock', - call: blockCall, - params: 2, - inputFormatter: [formatters.inputBlockNumberFormatter, function (val) { return !!val; }], - outputFormatter: formatters.outputBlockFormatter -}); - -var getUncle = new Method({ - name: 'getUncle', - call: uncleCall, - params: 2, - inputFormatter: [formatters.inputBlockNumberFormatter, utils.toHex], - outputFormatter: formatters.outputBlockFormatter, - -}); - -var getCompilers = new Method({ - name: 'getCompilers', - call: 'eth_getCompilers', - params: 0 -}); - -var getBlockTransactionCount = new Method({ - name: 'getBlockTransactionCount', - call: getBlockTransactionCountCall, - params: 1, - inputFormatter: [formatters.inputBlockNumberFormatter], - outputFormatter: utils.toDecimal -}); - -var getBlockUncleCount = new Method({ - name: 'getBlockUncleCount', - call: uncleCountCall, - params: 1, - inputFormatter: [formatters.inputBlockNumberFormatter], - outputFormatter: utils.toDecimal -}); - -var getTransaction = new Method({ - name: 'getTransaction', - call: 'eth_getTransactionByHash', - params: 1, - outputFormatter: formatters.outputTransactionFormatter -}); - -var getTransactionFromBlock = new Method({ - name: 'getTransactionFromBlock', - call: transactionFromBlockCall, - params: 2, - inputFormatter: [formatters.inputBlockNumberFormatter, utils.toHex], - outputFormatter: formatters.outputTransactionFormatter -}); - -var getTransactionCount = new Method({ - name: 'getTransactionCount', - call: 'eth_getTransactionCount', - params: 2, - inputFormatter: [null, formatters.inputDefaultBlockNumberFormatter], - outputFormatter: utils.toDecimal -}); - -var sendTransaction = new Method({ - name: 'sendTransaction', - call: 'eth_sendTransaction', - params: 1, - inputFormatter: [formatters.inputTransactionFormatter] -}); - -var call = new Method({ - name: 'call', - call: 'eth_call', - params: 2, - inputFormatter: [formatters.inputTransactionFormatter, formatters.inputDefaultBlockNumberFormatter] -}); - -var estimateGas = new Method({ - name: 'estimateGas', - call: 'eth_estimateGas', - params: 1, - inputFormatter: [formatters.inputTransactionFormatter], - outputFormatter: utils.toDecimal -}); - -var compileSolidity = new Method({ - name: 'compile.solidity', - call: 'eth_compileSolidity', - params: 1 -}); - -var compileLLL = new Method({ - name: 'compile.lll', - call: 'eth_compileLLL', - params: 1 -}); - -var compileSerpent = new Method({ - name: 'compile.serpent', - call: 'eth_compileSerpent', - params: 1 -}); - -var submitWork = new Method({ - name: 'submitWork', - call: 'eth_submitWork', - params: 3 -}); - -var getWork = new Method({ - name: 'getWork', - call: 'eth_getWork', - params: 0 -}); - -var methods = [ - getBalance, - getStorageAt, - getCode, - getBlock, - getUncle, - getCompilers, - getBlockTransactionCount, - getBlockUncleCount, - getTransaction, - getTransactionFromBlock, - getTransactionCount, - call, - estimateGas, - sendTransaction, - compileSolidity, - compileLLL, - compileSerpent, - submitWork, - getWork -]; - -/// @returns an array of objects describing web3.eth api properties - - - -var properties = [ - new Property({ - name: 'coinbase', - getter: 'eth_coinbase' - }), - new Property({ - name: 'mining', - getter: 'eth_mining' - }), - new Property({ - name: 'hashrate', - getter: 'eth_hashrate', - outputFormatter: utils.toDecimal - }), - new Property({ - name: 'gasPrice', - getter: 'eth_gasPrice', - outputFormatter: formatters.outputBigNumberFormatter - }), - new Property({ - name: 'accounts', - getter: 'eth_accounts' - }), - new Property({ - name: 'blockNumber', - getter: 'eth_blockNumber', - outputFormatter: utils.toDecimal - }) -]; - -module.exports = { - methods: methods, - properties: properties -}; - - -},{"../utils/utils":7,"./formatters":17,"./method":22,"./property":25}],15:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file event.js - * @author Marek Kotewicz - * @date 2014 - */ - -var utils = require('../utils/utils'); -var coder = require('../solidity/coder'); -var web3 = require('../web3'); -var formatters = require('./formatters'); -var sha3 = require('../utils/sha3'); - -/** - * This prototype should be used to create event filters - */ -var SolidityEvent = function (json, address) { - this._params = json.inputs; - this._name = utils.transformToFullName(json); - this._address = address; - this._anonymous = json.anonymous; -}; - -/** - * Should be used to get filtered param types - * - * @method types - * @param {Bool} decide if returned typed should be indexed - * @return {Array} array of types - */ -SolidityEvent.prototype.types = function (indexed) { - return this._params.filter(function (i) { - return i.indexed === indexed; - }).map(function (i) { - return i.type; - }); -}; - -/** - * Should be used to get event display name - * - * @method displayName - * @return {String} event display name - */ -SolidityEvent.prototype.displayName = function () { - return utils.extractDisplayName(this._name); -}; - -/** - * Should be used to get event type name - * - * @method typeName - * @return {String} event type name - */ -SolidityEvent.prototype.typeName = function () { - return utils.extractTypeName(this._name); -}; - -/** - * Should be used to get event signature - * - * @method signature - * @return {String} event signature - */ -SolidityEvent.prototype.signature = function () { - return sha3(this._name); -}; - -/** - * Should be used to encode indexed params and options to one final object - * - * @method encode - * @param {Object} indexed - * @param {Object} options - * @return {Object} everything combined together and encoded - */ -SolidityEvent.prototype.encode = function (indexed, options) { - indexed = indexed || {}; - options = options || {}; - var result = {}; - - ['fromBlock', 'toBlock'].filter(function (f) { - return options[f] !== undefined; - }).forEach(function (f) { - result[f] = formatters.inputBlockNumberFormatter(options[f]); - }); - - result.topics = []; - - if (!this._anonymous) { - result.address = this._address; - result.topics.push('0x' + this.signature()); - } - - var indexedTopics = this._params.filter(function (i) { - return i.indexed === true; - }).map(function (i) { - var value = indexed[i.name]; - if (value === undefined || value === null) { - return null; - } - - if (utils.isArray(value)) { - return value.map(function (v) { - return '0x' + coder.encodeParam(i.type, v); - }); - } - return '0x' + coder.encodeParam(i.type, value); - }); - - result.topics = result.topics.concat(indexedTopics); - - return result; -}; - -/** - * Should be used to decode indexed params and options - * - * @method decode - * @param {Object} data - * @return {Object} result object with decoded indexed && not indexed params - */ -SolidityEvent.prototype.decode = function (data) { - - data.data = data.data || ''; - data.topics = data.topics || []; - - var argTopics = this._anonymous ? data.topics : data.topics.slice(1); - var indexedData = argTopics.map(function (topics) { return topics.slice(2); }).join(""); - var indexedParams = coder.decodeParams(this.types(true), indexedData); - - var notIndexedData = data.data.slice(2); - var notIndexedParams = coder.decodeParams(this.types(false), notIndexedData); - - var result = formatters.outputLogFormatter(data); - result.event = this.displayName(); - result.address = data.address; - - result.args = this._params.reduce(function (acc, current) { - acc[current.name] = current.indexed ? indexedParams.shift() : notIndexedParams.shift(); - return acc; - }, {}); - - delete result.data; - delete result.topics; - - return result; -}; - -/** - * Should be used to create new filter object from event - * - * @method execute - * @param {Object} indexed - * @param {Object} options - * @return {Object} filter object - */ -SolidityEvent.prototype.execute = function (indexed, options) { - var o = this.encode(indexed, options); - var formatter = this.decode.bind(this); - return web3.eth.filter(o, undefined, undefined, formatter); -}; - -/** - * Should be used to attach event to contract object - * - * @method attachToContract - * @param {Contract} - */ -SolidityEvent.prototype.attachToContract = function (contract) { - var execute = this.execute.bind(this); - var displayName = this.displayName(); - if (!contract[displayName]) { - contract[displayName] = execute; - } - contract[displayName][this.typeName()] = this.execute.bind(this, contract); -}; - -module.exports = SolidityEvent; - - -},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"../web3":9,"./formatters":17}],16:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file filter.js - * @authors: - * Jeffrey Wilcke - * Marek Kotewicz - * Marian Oancea - * Fabian Vogelsteller - * Gav Wood - * @date 2014 - */ - -var RequestManager = require('./requestmanager'); -var formatters = require('./formatters'); -var utils = require('../utils/utils'); - -/** -* Converts a given topic to a hex string, but also allows null values. -* -* @param {Mixed} value -* @return {String} -*/ -var toTopic = function(value){ - - if(value === null || typeof value === 'undefined') - return null; - - value = String(value); - - if(value.indexOf('0x') === 0) - return value; - else - return utils.fromAscii(value); -}; - -/// This method should be called on options object, to verify deprecated properties && lazy load dynamic ones -/// @param should be string or object -/// @returns options string or object -var getOptions = function (options) { - - if (utils.isString(options)) { - return options; - } - - options = options || {}; - - // make sure topics, get converted to hex - options.topics = options.topics || []; - options.topics = options.topics.map(function(topic){ - return (utils.isArray(topic)) ? topic.map(toTopic) : toTopic(topic); - }); - - // lazy load - return { - topics: options.topics, - to: options.to, - address: options.address, - fromBlock: formatters.inputBlockNumberFormatter(options.fromBlock), - toBlock: formatters.inputBlockNumberFormatter(options.toBlock) - }; -}; - -var Filter = function (options, methods, formatter) { - var implementation = {}; - methods.forEach(function (method) { - method.attachToObject(implementation); - }); - this.options = getOptions(options); - this.implementation = implementation; - this.callbacks = []; - this.formatter = formatter; - this.filterId = this.implementation.newFilter(this.options); -}; - -Filter.prototype.watch = function (callback) { - this.callbacks.push(callback); - var self = this; - - var onMessage = function (error, messages) { - if (error) { - return self.callbacks.forEach(function (callback) { - callback(error); - }); - } - - messages.forEach(function (message) { - message = self.formatter ? self.formatter(message) : message; - self.callbacks.forEach(function (callback) { - callback(null, message); - }); - }); - }; - - // call getFilterLogs on start - if (!utils.isString(this.options)) { - this.get(function (err, messages) { - // don't send all the responses to all the watches again... just to this one - if (err) { - callback(err); - } - - messages.forEach(function (message) { - callback(null, message); - }); - }); - } - - RequestManager.getInstance().startPolling({ - method: this.implementation.poll.call, - params: [this.filterId], - }, this.filterId, onMessage, this.stopWatching.bind(this)); -}; - -Filter.prototype.stopWatching = function () { - RequestManager.getInstance().stopPolling(this.filterId); - this.implementation.uninstallFilter(this.filterId); - this.callbacks = []; -}; - -Filter.prototype.get = function (callback) { - var self = this; - if (utils.isFunction(callback)) { - this.implementation.getLogs(this.filterId, function(err, res){ - if (err) { - callback(err); - } else { - callback(null, res.map(function (log) { - return self.formatter ? self.formatter(log) : log; - })); - } - }); - } else { - var logs = this.implementation.getLogs(this.filterId); - return logs.map(function (log) { - return self.formatter ? self.formatter(log) : log; - }); - } -}; - -module.exports = Filter; - - -},{"../utils/utils":7,"./formatters":17,"./requestmanager":27}],17:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file formatters.js - * @author Marek Kotewicz - * @author Fabian Vogelsteller - * @date 2015 - */ - -var utils = require('../utils/utils'); -var config = require('../utils/config'); - -/** - * Should the format output to a big number - * - * @method outputBigNumberFormatter - * @param {String|Number|BigNumber} - * @returns {BigNumber} object - */ -var outputBigNumberFormatter = function (number) { - return utils.toBigNumber(number); -}; - -var isPredefinedBlockNumber = function (blockNumber) { - return blockNumber === 'latest' || blockNumber === 'pending' || blockNumber === 'earliest'; -}; - -var inputDefaultBlockNumberFormatter = function (blockNumber) { - if (blockNumber === undefined) { - return config.defaultBlock; - } - return inputBlockNumberFormatter(blockNumber); -}; - -var inputBlockNumberFormatter = function (blockNumber) { - if (blockNumber === undefined) { - return undefined; - } else if (isPredefinedBlockNumber(blockNumber)) { - return blockNumber; - } - return utils.toHex(blockNumber); -}; - -/** - * Formats the input of a transaction and converts all values to HEX - * - * @method inputTransactionFormatter - * @param {Object} transaction options - * @returns object -*/ -var inputTransactionFormatter = function (options){ - - options.from = options.from || config.defaultAccount; - - // make code -> data - if (options.code) { - options.data = options.code; - delete options.code; - } - - ['gasPrice', 'gas', 'value', 'nonce'].filter(function (key) { - return options[key] !== undefined; - }).forEach(function(key){ - options[key] = utils.fromDecimal(options[key]); - }); - - return options; -}; - -/** - * Formats the output of a transaction to its proper values - * - * @method outputTransactionFormatter - * @param {Object} transaction - * @returns {Object} transaction -*/ -var outputTransactionFormatter = function (tx){ - tx.blockNumber = utils.toDecimal(tx.blockNumber); - tx.transactionIndex = utils.toDecimal(tx.transactionIndex); - tx.nonce = utils.toDecimal(tx.nonce); - tx.gas = utils.toDecimal(tx.gas); - tx.gasPrice = utils.toBigNumber(tx.gasPrice); - tx.value = utils.toBigNumber(tx.value); - return tx; -}; - -/** - * Formats the output of a block to its proper values - * - * @method outputBlockFormatter - * @param {Object} block object - * @returns {Object} block object -*/ -var outputBlockFormatter = function(block) { - - // transform to number - block.gasLimit = utils.toDecimal(block.gasLimit); - block.gasUsed = utils.toDecimal(block.gasUsed); - block.size = utils.toDecimal(block.size); - block.timestamp = utils.toDecimal(block.timestamp); - block.number = utils.toDecimal(block.number); - - block.difficulty = utils.toBigNumber(block.difficulty); - block.totalDifficulty = utils.toBigNumber(block.totalDifficulty); - - if (utils.isArray(block.transactions)) { - block.transactions.forEach(function(item){ - if(!utils.isString(item)) - return outputTransactionFormatter(item); - }); - } - - return block; -}; - -/** - * Formats the output of a log - * - * @method outputLogFormatter - * @param {Object} log object - * @returns {Object} log -*/ -var outputLogFormatter = function(log) { - if (log === null) { // 'pending' && 'latest' filters are nulls - return null; - } - - log.blockNumber = utils.toDecimal(log.blockNumber); - log.transactionIndex = utils.toDecimal(log.transactionIndex); - log.logIndex = utils.toDecimal(log.logIndex); - - return log; -}; - -/** - * Formats the input of a whisper post and converts all values to HEX - * - * @method inputPostFormatter - * @param {Object} transaction object - * @returns {Object} -*/ -var inputPostFormatter = function(post) { - - post.payload = utils.toHex(post.payload); - post.ttl = utils.fromDecimal(post.ttl); - post.workToProve = utils.fromDecimal(post.workToProve); - post.priority = utils.fromDecimal(post.priority); - - // fallback - if (!utils.isArray(post.topics)) { - post.topics = post.topics ? [post.topics] : []; - } - - // format the following options - post.topics = post.topics.map(function(topic){ - return utils.fromAscii(topic); - }); - - return post; -}; - -/** - * Formats the output of a received post message - * - * @method outputPostFormatter - * @param {Object} - * @returns {Object} - */ -var outputPostFormatter = function(post){ - - post.expiry = utils.toDecimal(post.expiry); - post.sent = utils.toDecimal(post.sent); - post.ttl = utils.toDecimal(post.ttl); - post.workProved = utils.toDecimal(post.workProved); - post.payloadRaw = post.payload; - post.payload = utils.toAscii(post.payload); - - if (utils.isJson(post.payload)) { - post.payload = JSON.parse(post.payload); - } - - // format the following options - if (!post.topics) { - post.topics = []; - } - post.topics = post.topics.map(function(topic){ - return utils.toAscii(topic); - }); - - return post; -}; - -module.exports = { - inputDefaultBlockNumberFormatter: inputDefaultBlockNumberFormatter, - inputBlockNumberFormatter: inputBlockNumberFormatter, - inputTransactionFormatter: inputTransactionFormatter, - inputPostFormatter: inputPostFormatter, - outputBigNumberFormatter: outputBigNumberFormatter, - outputTransactionFormatter: outputTransactionFormatter, - outputBlockFormatter: outputBlockFormatter, - outputLogFormatter: outputLogFormatter, - outputPostFormatter: outputPostFormatter -}; - - -},{"../utils/config":5,"../utils/utils":7}],18:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file function.js - * @author Marek Kotewicz - * @date 2015 - */ - -var web3 = require('../web3'); -var coder = require('../solidity/coder'); -var utils = require('../utils/utils'); -var sha3 = require('../utils/sha3'); - -/** - * This prototype should be used to call/sendTransaction to solidity functions - */ -var SolidityFunction = function (json, address) { - this._inputTypes = json.inputs.map(function (i) { - return i.type; - }); - this._outputTypes = json.outputs.map(function (i) { - return i.type; - }); - this._constant = json.constant; - this._name = utils.transformToFullName(json); - this._address = address; -}; - -SolidityFunction.prototype.extractCallback = function (args) { - if (utils.isFunction(args[args.length - 1])) { - return args.pop(); // modify the args array! - } -}; - -/** - * Should be used to create payload from arguments - * - * @method toPayload - * @param {Array} solidity function params - * @param {Object} optional payload options - */ -SolidityFunction.prototype.toPayload = function (args) { - var options = {}; - if (args.length > this._inputTypes.length && utils.isObject(args[args.length -1])) { - options = args[args.length - 1]; - } - options.to = this._address; - options.data = '0x' + this.signature() + coder.encodeParams(this._inputTypes, args); - return options; -}; - -/** - * Should be used to get function signature - * - * @method signature - * @return {String} function signature - */ -SolidityFunction.prototype.signature = function () { - return sha3(this._name).slice(0, 8); -}; - - -SolidityFunction.prototype.unpackOutput = function (output) { - if (!output) { - return; - } - - output = output.length >= 2 ? output.slice(2) : output; - var result = coder.decodeParams(this._outputTypes, output); - return result.length === 1 ? result[0] : result; -}; - -/** - * Calls a contract function. - * - * @method call - * @param {...Object} Contract function arguments - * @param {function} If the last argument is a function, the contract function - * call will be asynchronous, and the callback will be passed the - * error and result. - * @return {String} output bytes - */ -SolidityFunction.prototype.call = function () { - var args = Array.prototype.slice.call(arguments).filter(function (a) {return a !== undefined; }); - var callback = this.extractCallback(args); - var payload = this.toPayload(args); - - if (!callback) { - var output = web3.eth.call(payload); - return this.unpackOutput(output); - } - - var self = this; - web3.eth.call(payload, function (error, output) { - callback(error, self.unpackOutput(output)); - }); -}; - -/** - * Should be used to sendTransaction to solidity function - * - * @method sendTransaction - * @param {Object} options - */ -SolidityFunction.prototype.sendTransaction = function () { - var args = Array.prototype.slice.call(arguments).filter(function (a) {return a !== undefined; }); - var callback = this.extractCallback(args); - var payload = this.toPayload(args); - - if (!callback) { - return web3.eth.sendTransaction(payload); - } - - web3.eth.sendTransaction(payload, callback); -}; - -/** - * Should be used to estimateGas of solidity function - * - * @method estimateGas - * @param {Object} options - */ -SolidityFunction.prototype.estimateGas = function () { - var args = Array.prototype.slice.call(arguments); - var callback = this.extractCallback(args); - var payload = this.toPayload(args); - - if (!callback) { - return web3.eth.estimateGas(payload); - } - - web3.eth.estimateGas(payload, callback); -}; - -/** - * Should be used to get function display name - * - * @method displayName - * @return {String} display name of the function - */ -SolidityFunction.prototype.displayName = function () { - return utils.extractDisplayName(this._name); -}; - -/** - * Should be used to get function type name - * - * @method typeName - * @return {String} type name of the function - */ -SolidityFunction.prototype.typeName = function () { - return utils.extractTypeName(this._name); -}; - -/** - * Should be called to get rpc requests from solidity function - * - * @method request - * @returns {Object} - */ -SolidityFunction.prototype.request = function () { - var args = Array.prototype.slice.call(arguments); - var callback = this.extractCallback(args); - var payload = this.toPayload(args); - var format = this.unpackOutput.bind(this); - - return { - callback: callback, - payload: payload, - format: format - }; -}; - -/** - * Should be called to execute function - * - * @method execute - */ -SolidityFunction.prototype.execute = function () { - var transaction = !this._constant; - - // send transaction - if (transaction) { - return this.sendTransaction.apply(this, Array.prototype.slice.call(arguments)); - } - - // call - return this.call.apply(this, Array.prototype.slice.call(arguments)); -}; - -/** - * Should be called to attach function to contract - * - * @method attachToContract - * @param {Contract} - */ -SolidityFunction.prototype.attachToContract = function (contract) { - var execute = this.execute.bind(this); - execute.request = this.request.bind(this); - execute.call = this.call.bind(this); - execute.sendTransaction = this.sendTransaction.bind(this); - execute.estimateGas = this.estimateGas.bind(this); - var displayName = this.displayName(); - if (!contract[displayName]) { - contract[displayName] = execute; - } - contract[displayName][this.typeName()] = execute; // circular!!!! -}; - -module.exports = SolidityFunction; - - -},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"../web3":9}],19:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file httpprovider.js - * @authors: - * Marek Kotewicz - * Marian Oancea - * Fabian Vogelsteller - * @date 2014 - */ - -"use strict"; - -var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line -var errors = require('./errors'); - -var HttpProvider = function (host) { - this.host = host || 'http://localhost:8545'; -}; - -HttpProvider.prototype.send = function (payload) { - var request = new XMLHttpRequest(); - - request.open('POST', this.host, false); - - try { - request.send(JSON.stringify(payload)); - } catch(error) { - throw errors.InvalidConnection(this.host); - } - - - // check request.status - // TODO: throw an error here! it cannot silently fail!!! - //if (request.status !== 200) { - //return; - //} - - var result = request.responseText; - - try { - result = JSON.parse(result); - } catch(e) { - throw errors.InvalidResponse(result); - } - - return result; -}; - -HttpProvider.prototype.sendAsync = function (payload, callback) { - var request = new XMLHttpRequest(); - request.onreadystatechange = function() { - if (request.readyState === 4) { - var result = request.responseText; - var error = null; - - try { - result = JSON.parse(result); - } catch(e) { - error = errors.InvalidResponse(result); - } - - callback(error, result); - } - }; - - request.open('POST', this.host, true); - - try { - request.send(JSON.stringify(payload)); - } catch(error) { - callback(errors.InvalidConnection(this.host)); - } -}; - -module.exports = HttpProvider; - - -},{"./errors":13,"xmlhttprequest":4}],20:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file icap.js - * @author Marek Kotewicz - * @date 2015 - */ - -var utils = require('../utils/utils'); - -/** - * This prototype should be used to extract necessary information from iban address - * - * @param {String} iban - */ -var ICAP = function (iban) { - this._iban = iban; -}; - -/** - * Should be called to check if icap is correct - * - * @method isValid - * @returns {Boolean} true if it is, otherwise false - */ -ICAP.prototype.isValid = function () { - return utils.isIBAN(this._iban); -}; - -/** - * Should be called to check if iban number is direct - * - * @method isDirect - * @returns {Boolean} true if it is, otherwise false - */ -ICAP.prototype.isDirect = function () { - return this._iban.length === 34; -}; - -/** - * Should be called to check if iban number if indirect - * - * @method isIndirect - * @returns {Boolean} true if it is, otherwise false - */ -ICAP.prototype.isIndirect = function () { - return this._iban.length === 20; -}; - -/** - * Should be called to get iban checksum - * Uses the mod-97-10 checksumming protocol (ISO/IEC 7064:2003) - * - * @method checksum - * @returns {String} checksum - */ -ICAP.prototype.checksum = function () { - return this._iban.substr(2, 2); -}; - -/** - * Should be called to get institution identifier - * eg. XREG - * - * @method institution - * @returns {String} institution identifier - */ -ICAP.prototype.institution = function () { - return this.isIndirect() ? this._iban.substr(7, 4) : ''; -}; - -/** - * Should be called to get client identifier within institution - * eg. GAVOFYORK - * - * @method client - * @returns {String} client identifier - */ -ICAP.prototype.client = function () { - return this.isIndirect() ? this._iban.substr(11) : ''; -}; - -/** - * Should be called to get client direct address - * - * @method address - * @returns {String} client direct address - */ -ICAP.prototype.address = function () { - return this.isDirect() ? this._iban.substr(4) : ''; -}; - -module.exports = ICAP; - - -},{"../utils/utils":7}],21:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file jsonrpc.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -var Jsonrpc = function () { - // singleton pattern - if (arguments.callee._singletonInstance) { - return arguments.callee._singletonInstance; - } - arguments.callee._singletonInstance = this; - - this.messageId = 1; -}; - -/** - * @return {Jsonrpc} singleton - */ -Jsonrpc.getInstance = function () { - var instance = new Jsonrpc(); - return instance; -}; - -/** - * Should be called to valid json create payload object - * - * @method toPayload - * @param {Function} method of jsonrpc call, required - * @param {Array} params, an array of method params, optional - * @returns {Object} valid jsonrpc payload object - */ -Jsonrpc.prototype.toPayload = function (method, params) { - if (!method) - console.error('jsonrpc method should be specified!'); - - return { - jsonrpc: '2.0', - method: method, - params: params || [], - id: this.messageId++ - }; -}; - -/** - * Should be called to check if jsonrpc response is valid - * - * @method isValidResponse - * @param {Object} - * @returns {Boolean} true if response is valid, otherwise false - */ -Jsonrpc.prototype.isValidResponse = function (response) { - return !!response && - !response.error && - response.jsonrpc === '2.0' && - typeof response.id === 'number' && - response.result !== undefined; // only undefined is not valid json object -}; - -/** - * Should be called to create batch payload object - * - * @method toBatchPayload - * @param {Array} messages, an array of objects with method (required) and params (optional) fields - * @returns {Array} batch payload - */ -Jsonrpc.prototype.toBatchPayload = function (messages) { - var self = this; - return messages.map(function (message) { - return self.toPayload(message.method, message.params); - }); -}; - -module.exports = Jsonrpc; - - -},{}],22:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file method.js - * @author Marek Kotewicz - * @date 2015 - */ - -var RequestManager = require('./requestmanager'); -var utils = require('../utils/utils'); -var errors = require('./errors'); - -var Method = function (options) { - this.name = options.name; - this.call = options.call; - this.params = options.params || 0; - this.inputFormatter = options.inputFormatter; - this.outputFormatter = options.outputFormatter; -}; - -/** - * Should be used to determine name of the jsonrpc method based on arguments - * - * @method getCall - * @param {Array} arguments - * @return {String} name of jsonrpc method - */ -Method.prototype.getCall = function (args) { - return utils.isFunction(this.call) ? this.call(args) : this.call; -}; - -/** - * Should be used to extract callback from array of arguments. Modifies input param - * - * @method extractCallback - * @param {Array} arguments - * @return {Function|Null} callback, if exists - */ -Method.prototype.extractCallback = function (args) { - if (utils.isFunction(args[args.length - 1])) { - return args.pop(); // modify the args array! - } -}; - -/** - * Should be called to check if the number of arguments is correct - * - * @method validateArgs - * @param {Array} arguments - * @throws {Error} if it is not - */ -Method.prototype.validateArgs = function (args) { - if (args.length !== this.params) { - throw errors.InvalidNumberOfParams(); - } -}; - -/** - * Should be called to format input args of method - * - * @method formatInput - * @param {Array} - * @return {Array} - */ -Method.prototype.formatInput = function (args) { - if (!this.inputFormatter) { - return args; - } - - return this.inputFormatter.map(function (formatter, index) { - return formatter ? formatter(args[index]) : args[index]; - }); -}; - -/** - * Should be called to format output(result) of method - * - * @method formatOutput - * @param {Object} - * @return {Object} - */ -Method.prototype.formatOutput = function (result) { - return this.outputFormatter && result !== null ? this.outputFormatter(result) : result; -}; - -/** - * Should attach function to method - * - * @method attachToObject - * @param {Object} - * @param {Function} - */ -Method.prototype.attachToObject = function (obj) { - var func = this.send.bind(this); - func.request = this.request.bind(this); - func.call = this.call; // that's ugly. filter.js uses it - var name = this.name.split('.'); - if (name.length > 1) { - obj[name[0]] = obj[name[0]] || {}; - obj[name[0]][name[1]] = func; - } else { - obj[name[0]] = func; - } -}; - -/** - * Should create payload from given input args - * - * @method toPayload - * @param {Array} args - * @return {Object} - */ -Method.prototype.toPayload = function (args) { - var call = this.getCall(args); - var callback = this.extractCallback(args); - var params = this.formatInput(args); - this.validateArgs(params); - - return { - method: call, - params: params, - callback: callback - }; -}; - -/** - * Should be called to create pure JSONRPC request which can be used in batch request - * - * @method request - * @param {...} params - * @return {Object} jsonrpc request - */ -Method.prototype.request = function () { - var payload = this.toPayload(Array.prototype.slice.call(arguments)); - payload.format = this.formatOutput.bind(this); - return payload; -}; - -/** - * Should send request to the API - * - * @method send - * @param list of params - * @return result - */ -Method.prototype.send = function () { - var payload = this.toPayload(Array.prototype.slice.call(arguments)); - if (payload.callback) { - var self = this; - return RequestManager.getInstance().sendAsync(payload, function (err, result) { - payload.callback(err, self.formatOutput(result)); - }); - } - return this.formatOutput(RequestManager.getInstance().send(payload)); -}; - -module.exports = Method; - - -},{"../utils/utils":7,"./errors":13,"./requestmanager":27}],23:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file namereg.js - * @author Marek Kotewicz - * @date 2015 - */ - -var contract = require('./contract'); - -var address = '0xc6d9d2cd449a754c494264e1809c50e34d64562b'; - -var abi = [ - {"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"name","outputs":[{"name":"o_name","type":"bytes32"}],"type":"function"}, - {"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"}, - {"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"content","outputs":[{"name":"","type":"bytes32"}],"type":"function"}, - {"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"addr","outputs":[{"name":"","type":"address"}],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[],"type":"function"}, - {"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"subRegistrar","outputs":[{"name":"o_subRegistrar","type":"address"}],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_newOwner","type":"address"}],"name":"transfer","outputs":[],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_registrar","type":"address"}],"name":"setSubRegistrar","outputs":[],"type":"function"}, - {"constant":false,"inputs":[],"name":"Registrar","outputs":[],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_a","type":"address"},{"name":"_primary","type":"bool"}],"name":"setAddress","outputs":[],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_content","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"disown","outputs":[],"type":"function"}, - {"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"register","outputs":[{"name":"","type":"address"}],"type":"function"}, - {"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"}],"name":"Changed","type":"event"}, - {"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"addr","type":"address"}],"name":"PrimaryChanged","type":"event"} -]; - -module.exports = contract(abi).at(address); - - -},{"./contract":11}],24:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file eth.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -var utils = require('../utils/utils'); -var Property = require('./property'); - -/// @returns an array of objects describing web3.eth api methods -var methods = [ -]; - -/// @returns an array of objects describing web3.eth api properties -var properties = [ - new Property({ - name: 'listening', - getter: 'net_listening' - }), - new Property({ - name: 'peerCount', - getter: 'net_peerCount', - outputFormatter: utils.toDecimal - }) -]; - - -module.exports = { - methods: methods, - properties: properties -}; - - -},{"../utils/utils":7,"./property":25}],25:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file property.js - * @author Fabian Vogelsteller - * @author Marek Kotewicz - * @date 2015 - */ - -var RequestManager = require('./requestmanager'); - -var Property = function (options) { - this.name = options.name; - this.getter = options.getter; - this.setter = options.setter; - this.outputFormatter = options.outputFormatter; - this.inputFormatter = options.inputFormatter; -}; - -/** - * Should be called to format input args of method - * - * @method formatInput - * @param {Array} - * @return {Array} - */ -Property.prototype.formatInput = function (arg) { - return this.inputFormatter ? this.inputFormatter(arg) : arg; -}; - -/** - * Should be called to format output(result) of method - * - * @method formatOutput - * @param {Object} - * @return {Object} - */ -Property.prototype.formatOutput = function (result) { - return this.outputFormatter && result !== null ? this.outputFormatter(result) : result; -}; - -/** - * Should attach function to method - * - * @method attachToObject - * @param {Object} - * @param {Function} - */ -Property.prototype.attachToObject = function (obj) { - var proto = { - get: this.get.bind(this), - }; - - var names = this.name.split('.'); - var name = names[0]; - if (names.length > 1) { - obj[names[0]] = obj[names[0]] || {}; - obj = obj[names[0]]; - name = names[1]; - } - - Object.defineProperty(obj, name, proto); - - var toAsyncName = function (prefix, name) { - return prefix + name.charAt(0).toUpperCase() + name.slice(1); - }; - - obj[toAsyncName('get', name)] = this.getAsync.bind(this); -}; - -/** - * Should be used to get value of the property - * - * @method get - * @return {Object} value of the property - */ -Property.prototype.get = function () { - return this.formatOutput(RequestManager.getInstance().send({ - method: this.getter - })); -}; - -/** - * Should be used to asynchrounously get value of property - * - * @method getAsync - * @param {Function} - */ -Property.prototype.getAsync = function (callback) { - var self = this; - RequestManager.getInstance().sendAsync({ - method: this.getter - }, function (err, result) { - if (err) { - return callback(err); - } - callback(err, self.formatOutput(result)); - }); -}; - -module.exports = Property; - - -},{"./requestmanager":27}],26:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file qtsync.js - * @authors: - * Marek Kotewicz - * Marian Oancea - * @date 2014 - */ - -var QtSyncProvider = function () { -}; - -QtSyncProvider.prototype.send = function (payload) { - var result = navigator.qt.callMethod(JSON.stringify(payload)); - return JSON.parse(result); -}; - -module.exports = QtSyncProvider; - - -},{}],27:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file requestmanager.js - * @author Jeffrey Wilcke - * @author Marek Kotewicz - * @author Marian Oancea - * @author Fabian Vogelsteller - * @author Gav Wood - * @date 2014 - */ - -var Jsonrpc = require('./jsonrpc'); -var utils = require('../utils/utils'); -var c = require('../utils/config'); -var errors = require('./errors'); - -/** - * It's responsible for passing messages to providers - * It's also responsible for polling the ethereum node for incoming messages - * Default poll timeout is 1 second - * Singleton - */ -var RequestManager = function (provider) { - // singleton pattern - if (arguments.callee._singletonInstance) { - return arguments.callee._singletonInstance; - } - arguments.callee._singletonInstance = this; - - this.provider = provider; - this.polls = []; - this.timeout = null; - this.poll(); -}; - -/** - * @return {RequestManager} singleton - */ -RequestManager.getInstance = function () { - var instance = new RequestManager(); - return instance; -}; - -/** - * Should be used to synchronously send request - * - * @method send - * @param {Object} data - * @return {Object} - */ -RequestManager.prototype.send = function (data) { - if (!this.provider) { - console.error(errors.InvalidProvider()); - return null; - } - - var payload = Jsonrpc.getInstance().toPayload(data.method, data.params); - var result = this.provider.send(payload); - - if (!Jsonrpc.getInstance().isValidResponse(result)) { - throw errors.InvalidResponse(result); - } - - return result.result; -}; - -/** - * Should be used to asynchronously send request - * - * @method sendAsync - * @param {Object} data - * @param {Function} callback - */ -RequestManager.prototype.sendAsync = function (data, callback) { - if (!this.provider) { - return callback(errors.InvalidProvider()); - } - - var payload = Jsonrpc.getInstance().toPayload(data.method, data.params); - this.provider.sendAsync(payload, function (err, result) { - if (err) { - return callback(err); - } - - if (!Jsonrpc.getInstance().isValidResponse(result)) { - return callback(errors.InvalidResponse(result)); - } - - callback(null, result.result); - }); -}; - -/** - * Should be called to asynchronously send batch request - * - * @method sendBatch - * @param {Array} batch data - * @param {Function} callback - */ -RequestManager.prototype.sendBatch = function (data, callback) { - if (!this.provider) { - return callback(errors.InvalidProvider()); - } - - var payload = Jsonrpc.getInstance().toBatchPayload(data); - - this.provider.sendAsync(payload, function (err, results) { - if (err) { - return callback(err); - } - - if (!utils.isArray(results)) { - return callback(errors.InvalidResponse(results)); - } - - callback(err, results); - }); -}; - -/** - * Should be used to set provider of request manager - * - * @method setProvider - * @param {Object} - */ -RequestManager.prototype.setProvider = function (p) { - this.provider = p; -}; - -/*jshint maxparams:4 */ - -/** - * Should be used to start polling - * - * @method startPolling - * @param {Object} data - * @param {Number} pollId - * @param {Function} callback - * @param {Function} uninstall - * - * @todo cleanup number of params - */ -RequestManager.prototype.startPolling = function (data, pollId, callback, uninstall) { - this.polls.push({data: data, id: pollId, callback: callback, uninstall: uninstall}); -}; -/*jshint maxparams:3 */ - -/** - * Should be used to stop polling for filter with given id - * - * @method stopPolling - * @param {Number} pollId - */ -RequestManager.prototype.stopPolling = function (pollId) { - for (var i = this.polls.length; i--;) { - var poll = this.polls[i]; - if (poll.id === pollId) { - this.polls.splice(i, 1); - } - } -}; - -/** - * Should be called to reset polling mechanism of request manager - * - * @method reset - */ -RequestManager.prototype.reset = function () { - this.polls.forEach(function (poll) { - poll.uninstall(poll.id); - }); - this.polls = []; - - if (this.timeout) { - clearTimeout(this.timeout); - this.timeout = null; - } - this.poll(); -}; - -/** - * Should be called to poll for changes on filter with given id - * - * @method poll - */ -RequestManager.prototype.poll = function () { - this.timeout = setTimeout(this.poll.bind(this), c.ETH_POLLING_TIMEOUT); - - if (!this.polls.length) { - return; - } - - if (!this.provider) { - console.error(errors.InvalidProvider()); - return; - } - - var payload = Jsonrpc.getInstance().toBatchPayload(this.polls.map(function (data) { - return data.data; - })); - - var self = this; - this.provider.sendAsync(payload, function (error, results) { - // TODO: console log? - if (error) { - return; - } - - if (!utils.isArray(results)) { - throw errors.InvalidResponse(results); - } - - results.map(function (result, index) { - result.callback = self.polls[index].callback; - return result; - }).filter(function (result) { - var valid = Jsonrpc.getInstance().isValidResponse(result); - if (!valid) { - result.callback(errors.InvalidResponse(result)); - } - return valid; - }).filter(function (result) { - return utils.isArray(result.result) && result.result.length > 0; - }).forEach(function (result) { - result.callback(null, result.result); - }); - }); -}; - -module.exports = RequestManager; - - -},{"../utils/config":5,"../utils/utils":7,"./errors":13,"./jsonrpc":21}],28:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file shh.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -var Method = require('./method'); -var formatters = require('./formatters'); - -var post = new Method({ - name: 'post', - call: 'shh_post', - params: 1, - inputFormatter: [formatters.inputPostFormatter] -}); - -var newIdentity = new Method({ - name: 'newIdentity', - call: 'shh_newIdentity', - params: 0 -}); - -var hasIdentity = new Method({ - name: 'hasIdentity', - call: 'shh_hasIdentity', - params: 1 -}); - -var newGroup = new Method({ - name: 'newGroup', - call: 'shh_newGroup', - params: 0 -}); - -var addToGroup = new Method({ - name: 'addToGroup', - call: 'shh_addToGroup', - params: 0 -}); - -var methods = [ - post, - newIdentity, - hasIdentity, - newGroup, - addToGroup -]; - -module.exports = { - methods: methods -}; - - -},{"./formatters":17,"./method":22}],29:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file transfer.js - * @author Marek Kotewicz - * @date 2015 - */ - -var web3 = require('../web3'); -var ICAP = require('./icap'); -var namereg = require('./namereg'); -var contract = require('./contract'); - -/** - * Should be used to make ICAP transfer - * - * @method transfer - * @param {String} iban number - * @param {String} from (address) - * @param {Value} value to be tranfered - * @param {Function} callback, callback - */ -var transfer = function (from, iban, value, callback) { - var icap = new ICAP(iban); - if (!icap.isValid()) { - throw new Error('invalid iban address'); - } - - if (icap.isDirect()) { - return transferToAddress(from, icap.address(), value, callback); - } - - if (!callback) { - var address = namereg.addr(icap.institution()); - return deposit(from, address, value, icap.client()); - } - - namereg.addr(icap.insitution(), function (err, address) { - return deposit(from, address, value, icap.client(), callback); - }); - -}; - -/** - * Should be used to transfer funds to certain address - * - * @method transferToAddress - * @param {String} address - * @param {String} from (address) - * @param {Value} value to be tranfered - * @param {Function} callback, callback - */ -var transferToAddress = function (from, address, value, callback) { - return web3.eth.sendTransaction({ - address: address, - from: from, - value: value - }, callback); -}; - -/** - * Should be used to deposit funds to generic Exchange contract (must implement deposit(bytes32) method!) - * - * @method deposit - * @param {String} address - * @param {String} from (address) - * @param {Value} value to be tranfered - * @param {String} client unique identifier - * @param {Function} callback, callback - */ -var deposit = function (from, address, value, client, callback) { - var abi = [{"constant":false,"inputs":[{"name":"name","type":"bytes32"}],"name":"deposit","outputs":[],"type":"function"}]; - return contract(abi).at(address).deposit(client, { - from: from, - value: value - }, callback); -}; - -module.exports = transfer; - - -},{"../web3":9,"./contract":11,"./icap":20,"./namereg":23}],30:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file watches.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -var Method = require('./method'); - -/// @returns an array of objects describing web3.eth.filter api methods -var eth = function () { - var newFilterCall = function (args) { - var type = args[0]; - - switch(type) { - case 'latest': - args.pop(); - this.params = 0; - return 'eth_newBlockFilter'; - case 'pending': - args.pop(); - this.params = 0; - return 'eth_newPendingTransactionFilter'; - default: - return 'eth_newFilter'; - } - }; - - var newFilter = new Method({ - name: 'newFilter', - call: newFilterCall, - params: 1 - }); - - var uninstallFilter = new Method({ - name: 'uninstallFilter', - call: 'eth_uninstallFilter', - params: 1 - }); - - var getLogs = new Method({ - name: 'getLogs', - call: 'eth_getFilterLogs', - params: 1 - }); - - var poll = new Method({ - name: 'poll', - call: 'eth_getFilterChanges', - params: 1 - }); - - return [ - newFilter, - uninstallFilter, - getLogs, - poll - ]; -}; - -/// @returns an array of objects describing web3.shh.watch api methods -var shh = function () { - var newFilter = new Method({ - name: 'newFilter', - call: 'shh_newFilter', - params: 1 - }); - - var uninstallFilter = new Method({ - name: 'uninstallFilter', - call: 'shh_uninstallFilter', - params: 1 - }); - - var getLogs = new Method({ - name: 'getLogs', - call: 'shh_getMessages', - params: 1 - }); - - var poll = new Method({ - name: 'poll', - call: 'shh_getFilterChanges', - params: 1 - }); - - return [ - newFilter, - uninstallFilter, - getLogs, - poll - ]; -}; - -module.exports = { - eth: eth, - shh: shh -}; - - -},{"./method":22}],31:[function(require,module,exports){ - -},{}],32:[function(require,module,exports){ -;(function (root, factory) { - if (typeof exports === "object") { - // CommonJS - module.exports = exports = factory(); - } - else if (typeof define === "function" && define.amd) { - // AMD - define([], factory); - } - else { - // Global (browser) - root.CryptoJS = factory(); - } -}(this, function () { - - /** - * CryptoJS core components. - */ - var CryptoJS = CryptoJS || (function (Math, undefined) { - /** - * CryptoJS namespace. - */ - var C = {}; - - /** - * Library namespace. - */ - var C_lib = C.lib = {}; - - /** - * Base object for prototypal inheritance. - */ - var Base = C_lib.Base = (function () { - function F() {} - - return { - /** - * Creates a new object that inherits from this object. - * - * @param {Object} overrides Properties to copy into the new object. - * - * @return {Object} The new object. - * - * @static - * - * @example - * - * var MyType = CryptoJS.lib.Base.extend({ - * field: 'value', - * - * method: function () { - * } - * }); - */ - extend: function (overrides) { - // Spawn - F.prototype = this; - var subtype = new F(); - - // Augment - if (overrides) { - subtype.mixIn(overrides); - } - - // Create default initializer - if (!subtype.hasOwnProperty('init')) { - subtype.init = function () { - subtype.$super.init.apply(this, arguments); - }; - } - - // Initializer's prototype is the subtype object - subtype.init.prototype = subtype; - - // Reference supertype - subtype.$super = this; - - return subtype; - }, - - /** - * Extends this object and runs the init method. - * Arguments to create() will be passed to init(). - * - * @return {Object} The new object. - * - * @static - * - * @example - * - * var instance = MyType.create(); - */ - create: function () { - var instance = this.extend(); - instance.init.apply(instance, arguments); - - return instance; - }, - - /** - * Initializes a newly created object. - * Override this method to add some logic when your objects are created. - * - * @example - * - * var MyType = CryptoJS.lib.Base.extend({ - * init: function () { - * // ... - * } - * }); - */ - init: function () { - }, - - /** - * Copies properties into this object. - * - * @param {Object} properties The properties to mix in. - * - * @example - * - * MyType.mixIn({ - * field: 'value' - * }); - */ - mixIn: function (properties) { - for (var propertyName in properties) { - if (properties.hasOwnProperty(propertyName)) { - this[propertyName] = properties[propertyName]; - } - } - - // IE won't copy toString using the loop above - if (properties.hasOwnProperty('toString')) { - this.toString = properties.toString; - } - }, - - /** - * Creates a copy of this object. - * - * @return {Object} The clone. - * - * @example - * - * var clone = instance.clone(); - */ - clone: function () { - return this.init.prototype.extend(this); - } - }; - }()); - - /** - * An array of 32-bit words. - * - * @property {Array} words The array of 32-bit words. - * @property {number} sigBytes The number of significant bytes in this word array. - */ - var WordArray = C_lib.WordArray = Base.extend({ - /** - * Initializes a newly created word array. - * - * @param {Array} words (Optional) An array of 32-bit words. - * @param {number} sigBytes (Optional) The number of significant bytes in the words. - * - * @example - * - * var wordArray = CryptoJS.lib.WordArray.create(); - * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]); - * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6); - */ - init: function (words, sigBytes) { - words = this.words = words || []; - - if (sigBytes != undefined) { - this.sigBytes = sigBytes; - } else { - this.sigBytes = words.length * 4; - } - }, - - /** - * Converts this word array to a string. - * - * @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex - * - * @return {string} The stringified word array. - * - * @example - * - * var string = wordArray + ''; - * var string = wordArray.toString(); - * var string = wordArray.toString(CryptoJS.enc.Utf8); - */ - toString: function (encoder) { - return (encoder || Hex).stringify(this); - }, - - /** - * Concatenates a word array to this word array. - * - * @param {WordArray} wordArray The word array to append. - * - * @return {WordArray} This word array. - * - * @example - * - * wordArray1.concat(wordArray2); - */ - concat: function (wordArray) { - // Shortcuts - var thisWords = this.words; - var thatWords = wordArray.words; - var thisSigBytes = this.sigBytes; - var thatSigBytes = wordArray.sigBytes; - - // Clamp excess bits - this.clamp(); - - // Concat - if (thisSigBytes % 4) { - // Copy one byte at a time - for (var i = 0; i < thatSigBytes; i++) { - var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; - thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8); - } - } else { - // Copy one word at a time - for (var i = 0; i < thatSigBytes; i += 4) { - thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2]; - } - } - this.sigBytes += thatSigBytes; - - // Chainable - return this; - }, - - /** - * Removes insignificant bits. - * - * @example - * - * wordArray.clamp(); - */ - clamp: function () { - // Shortcuts - var words = this.words; - var sigBytes = this.sigBytes; - - // Clamp - words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8); - words.length = Math.ceil(sigBytes / 4); - }, - - /** - * Creates a copy of this word array. - * - * @return {WordArray} The clone. - * - * @example - * - * var clone = wordArray.clone(); - */ - clone: function () { - var clone = Base.clone.call(this); - clone.words = this.words.slice(0); - - return clone; - }, - - /** - * Creates a word array filled with random bytes. - * - * @param {number} nBytes The number of random bytes to generate. - * - * @return {WordArray} The random word array. - * - * @static - * - * @example - * - * var wordArray = CryptoJS.lib.WordArray.random(16); - */ - random: function (nBytes) { - var words = []; - - var r = (function (m_w) { - var m_w = m_w; - var m_z = 0x3ade68b1; - var mask = 0xffffffff; - - return function () { - m_z = (0x9069 * (m_z & 0xFFFF) + (m_z >> 0x10)) & mask; - m_w = (0x4650 * (m_w & 0xFFFF) + (m_w >> 0x10)) & mask; - var result = ((m_z << 0x10) + m_w) & mask; - result /= 0x100000000; - result += 0.5; - return result * (Math.random() > .5 ? 1 : -1); - } - }); - - for (var i = 0, rcache; i < nBytes; i += 4) { - var _r = r((rcache || Math.random()) * 0x100000000); - - rcache = _r() * 0x3ade67b7; - words.push((_r() * 0x100000000) | 0); - } - - return new WordArray.init(words, nBytes); - } - }); - - /** - * Encoder namespace. - */ - var C_enc = C.enc = {}; - - /** - * Hex encoding strategy. - */ - var Hex = C_enc.Hex = { - /** - * Converts a word array to a hex string. - * - * @param {WordArray} wordArray The word array. - * - * @return {string} The hex string. - * - * @static - * - * @example - * - * var hexString = CryptoJS.enc.Hex.stringify(wordArray); - */ - stringify: function (wordArray) { - // Shortcuts - var words = wordArray.words; - var sigBytes = wordArray.sigBytes; - - // Convert - var hexChars = []; - for (var i = 0; i < sigBytes; i++) { - var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; - hexChars.push((bite >>> 4).toString(16)); - hexChars.push((bite & 0x0f).toString(16)); - } - - return hexChars.join(''); - }, - - /** - * Converts a hex string to a word array. - * - * @param {string} hexStr The hex string. - * - * @return {WordArray} The word array. - * - * @static - * - * @example - * - * var wordArray = CryptoJS.enc.Hex.parse(hexString); - */ - parse: function (hexStr) { - // Shortcut - var hexStrLength = hexStr.length; - - // Convert - var words = []; - for (var i = 0; i < hexStrLength; i += 2) { - words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4); - } - - return new WordArray.init(words, hexStrLength / 2); - } - }; - - /** - * Latin1 encoding strategy. - */ - var Latin1 = C_enc.Latin1 = { - /** - * Converts a word array to a Latin1 string. - * - * @param {WordArray} wordArray The word array. - * - * @return {string} The Latin1 string. - * - * @static - * - * @example - * - * var latin1String = CryptoJS.enc.Latin1.stringify(wordArray); - */ - stringify: function (wordArray) { - // Shortcuts - var words = wordArray.words; - var sigBytes = wordArray.sigBytes; - - // Convert - var latin1Chars = []; - for (var i = 0; i < sigBytes; i++) { - var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; - latin1Chars.push(String.fromCharCode(bite)); - } - - return latin1Chars.join(''); - }, - - /** - * Converts a Latin1 string to a word array. - * - * @param {string} latin1Str The Latin1 string. - * - * @return {WordArray} The word array. - * - * @static - * - * @example - * - * var wordArray = CryptoJS.enc.Latin1.parse(latin1String); - */ - parse: function (latin1Str) { - // Shortcut - var latin1StrLength = latin1Str.length; - - // Convert - var words = []; - for (var i = 0; i < latin1StrLength; i++) { - words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8); - } - - return new WordArray.init(words, latin1StrLength); - } - }; - - /** - * UTF-8 encoding strategy. - */ - var Utf8 = C_enc.Utf8 = { - /** - * Converts a word array to a UTF-8 string. - * - * @param {WordArray} wordArray The word array. - * - * @return {string} The UTF-8 string. - * - * @static - * - * @example - * - * var utf8String = CryptoJS.enc.Utf8.stringify(wordArray); - */ - stringify: function (wordArray) { - try { - return decodeURIComponent(escape(Latin1.stringify(wordArray))); - } catch (e) { - throw new Error('Malformed UTF-8 data'); - } - }, - - /** - * Converts a UTF-8 string to a word array. - * - * @param {string} utf8Str The UTF-8 string. - * - * @return {WordArray} The word array. - * - * @static - * - * @example - * - * var wordArray = CryptoJS.enc.Utf8.parse(utf8String); - */ - parse: function (utf8Str) { - return Latin1.parse(unescape(encodeURIComponent(utf8Str))); - } - }; - - /** - * Abstract buffered block algorithm template. - * - * The property blockSize must be implemented in a concrete subtype. - * - * @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0 - */ - var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({ - /** - * Resets this block algorithm's data buffer to its initial state. - * - * @example - * - * bufferedBlockAlgorithm.reset(); - */ - reset: function () { - // Initial values - this._data = new WordArray.init(); - this._nDataBytes = 0; - }, - - /** - * Adds new data to this block algorithm's buffer. - * - * @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8. - * - * @example - * - * bufferedBlockAlgorithm._append('data'); - * bufferedBlockAlgorithm._append(wordArray); - */ - _append: function (data) { - // Convert string to WordArray, else assume WordArray already - if (typeof data == 'string') { - data = Utf8.parse(data); - } - - // Append - this._data.concat(data); - this._nDataBytes += data.sigBytes; - }, - - /** - * Processes available data blocks. - * - * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype. - * - * @param {boolean} doFlush Whether all blocks and partial blocks should be processed. - * - * @return {WordArray} The processed data. - * - * @example - * - * var processedData = bufferedBlockAlgorithm._process(); - * var processedData = bufferedBlockAlgorithm._process(!!'flush'); - */ - _process: function (doFlush) { - // Shortcuts - var data = this._data; - var dataWords = data.words; - var dataSigBytes = data.sigBytes; - var blockSize = this.blockSize; - var blockSizeBytes = blockSize * 4; - - // Count blocks ready - var nBlocksReady = dataSigBytes / blockSizeBytes; - if (doFlush) { - // Round up to include partial blocks - nBlocksReady = Math.ceil(nBlocksReady); - } else { - // Round down to include only full blocks, - // less the number of blocks that must remain in the buffer - nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0); - } - - // Count words ready - var nWordsReady = nBlocksReady * blockSize; - - // Count bytes ready - var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes); - - // Process blocks - if (nWordsReady) { - for (var offset = 0; offset < nWordsReady; offset += blockSize) { - // Perform concrete-algorithm logic - this._doProcessBlock(dataWords, offset); - } - - // Remove processed words - var processedWords = dataWords.splice(0, nWordsReady); - data.sigBytes -= nBytesReady; - } - - // Return processed words - return new WordArray.init(processedWords, nBytesReady); - }, - - /** - * Creates a copy of this object. - * - * @return {Object} The clone. - * - * @example - * - * var clone = bufferedBlockAlgorithm.clone(); - */ - clone: function () { - var clone = Base.clone.call(this); - clone._data = this._data.clone(); - - return clone; - }, - - _minBufferSize: 0 - }); - - /** - * Abstract hasher template. - * - * @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits) - */ - var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({ - /** - * Configuration options. - */ - cfg: Base.extend(), - - /** - * Initializes a newly created hasher. - * - * @param {Object} cfg (Optional) The configuration options to use for this hash computation. - * - * @example - * - * var hasher = CryptoJS.algo.SHA256.create(); - */ - init: function (cfg) { - // Apply config defaults - this.cfg = this.cfg.extend(cfg); - - // Set initial values - this.reset(); - }, - - /** - * Resets this hasher to its initial state. - * - * @example - * - * hasher.reset(); - */ - reset: function () { - // Reset data buffer - BufferedBlockAlgorithm.reset.call(this); - - // Perform concrete-hasher logic - this._doReset(); - }, - - /** - * Updates this hasher with a message. - * - * @param {WordArray|string} messageUpdate The message to append. - * - * @return {Hasher} This hasher. - * - * @example - * - * hasher.update('message'); - * hasher.update(wordArray); - */ - update: function (messageUpdate) { - // Append - this._append(messageUpdate); - - // Update the hash - this._process(); - - // Chainable - return this; - }, - - /** - * Finalizes the hash computation. - * Note that the finalize operation is effectively a destructive, read-once operation. - * - * @param {WordArray|string} messageUpdate (Optional) A final message update. - * - * @return {WordArray} The hash. - * - * @example - * - * var hash = hasher.finalize(); - * var hash = hasher.finalize('message'); - * var hash = hasher.finalize(wordArray); - */ - finalize: function (messageUpdate) { - // Final message update - if (messageUpdate) { - this._append(messageUpdate); - } - - // Perform concrete-hasher logic - var hash = this._doFinalize(); - - return hash; - }, - - blockSize: 512/32, - - /** - * Creates a shortcut function to a hasher's object interface. - * - * @param {Hasher} hasher The hasher to create a helper for. - * - * @return {Function} The shortcut function. - * - * @static - * - * @example - * - * var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256); - */ - _createHelper: function (hasher) { - return function (message, cfg) { - return new hasher.init(cfg).finalize(message); - }; - }, - - /** - * Creates a shortcut function to the HMAC's object interface. - * - * @param {Hasher} hasher The hasher to use in this HMAC helper. - * - * @return {Function} The shortcut function. - * - * @static - * - * @example - * - * var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256); - */ - _createHmacHelper: function (hasher) { - return function (message, key) { - return new C_algo.HMAC.init(hasher, key).finalize(message); - }; - } - }); - - /** - * Algorithm namespace. - */ - var C_algo = C.algo = {}; - - return C; - }(Math)); - - - return CryptoJS; - -})); -},{}],33:[function(require,module,exports){ -;(function (root, factory, undef) { - if (typeof exports === "object") { - // CommonJS - module.exports = exports = factory(require("./core"), require("./x64-core")); - } - else if (typeof define === "function" && define.amd) { - // AMD - define(["./core", "./x64-core"], factory); - } - else { - // Global (browser) - factory(root.CryptoJS); - } -}(this, function (CryptoJS) { - - (function (Math) { - // Shortcuts - var C = CryptoJS; - var C_lib = C.lib; - var WordArray = C_lib.WordArray; - var Hasher = C_lib.Hasher; - var C_x64 = C.x64; - var X64Word = C_x64.Word; - var C_algo = C.algo; - - // Constants tables - var RHO_OFFSETS = []; - var PI_INDEXES = []; - var ROUND_CONSTANTS = []; - - // Compute Constants - (function () { - // Compute rho offset constants - var x = 1, y = 0; - for (var t = 0; t < 24; t++) { - RHO_OFFSETS[x + 5 * y] = ((t + 1) * (t + 2) / 2) % 64; - - var newX = y % 5; - var newY = (2 * x + 3 * y) % 5; - x = newX; - y = newY; - } - - // Compute pi index constants - for (var x = 0; x < 5; x++) { - for (var y = 0; y < 5; y++) { - PI_INDEXES[x + 5 * y] = y + ((2 * x + 3 * y) % 5) * 5; - } - } - - // Compute round constants - var LFSR = 0x01; - for (var i = 0; i < 24; i++) { - var roundConstantMsw = 0; - var roundConstantLsw = 0; - - for (var j = 0; j < 7; j++) { - if (LFSR & 0x01) { - var bitPosition = (1 << j) - 1; - if (bitPosition < 32) { - roundConstantLsw ^= 1 << bitPosition; - } else /* if (bitPosition >= 32) */ { - roundConstantMsw ^= 1 << (bitPosition - 32); - } - } - - // Compute next LFSR - if (LFSR & 0x80) { - // Primitive polynomial over GF(2): x^8 + x^6 + x^5 + x^4 + 1 - LFSR = (LFSR << 1) ^ 0x71; - } else { - LFSR <<= 1; - } - } - - ROUND_CONSTANTS[i] = X64Word.create(roundConstantMsw, roundConstantLsw); - } - }()); - - // Reusable objects for temporary values - var T = []; - (function () { - for (var i = 0; i < 25; i++) { - T[i] = X64Word.create(); - } - }()); - - /** - * SHA-3 hash algorithm. - */ - var SHA3 = C_algo.SHA3 = Hasher.extend({ - /** - * Configuration options. - * - * @property {number} outputLength - * The desired number of bits in the output hash. - * Only values permitted are: 224, 256, 384, 512. - * Default: 512 - */ - cfg: Hasher.cfg.extend({ - outputLength: 512 - }), - - _doReset: function () { - var state = this._state = [] - for (var i = 0; i < 25; i++) { - state[i] = new X64Word.init(); - } - - this.blockSize = (1600 - 2 * this.cfg.outputLength) / 32; - }, - - _doProcessBlock: function (M, offset) { - // Shortcuts - var state = this._state; - var nBlockSizeLanes = this.blockSize / 2; - - // Absorb - for (var i = 0; i < nBlockSizeLanes; i++) { - // Shortcuts - var M2i = M[offset + 2 * i]; - var M2i1 = M[offset + 2 * i + 1]; - - // Swap endian - M2i = ( - (((M2i << 8) | (M2i >>> 24)) & 0x00ff00ff) | - (((M2i << 24) | (M2i >>> 8)) & 0xff00ff00) - ); - M2i1 = ( - (((M2i1 << 8) | (M2i1 >>> 24)) & 0x00ff00ff) | - (((M2i1 << 24) | (M2i1 >>> 8)) & 0xff00ff00) - ); - - // Absorb message into state - var lane = state[i]; - lane.high ^= M2i1; - lane.low ^= M2i; - } - - // Rounds - for (var round = 0; round < 24; round++) { - // Theta - for (var x = 0; x < 5; x++) { - // Mix column lanes - var tMsw = 0, tLsw = 0; - for (var y = 0; y < 5; y++) { - var lane = state[x + 5 * y]; - tMsw ^= lane.high; - tLsw ^= lane.low; - } - - // Temporary values - var Tx = T[x]; - Tx.high = tMsw; - Tx.low = tLsw; - } - for (var x = 0; x < 5; x++) { - // Shortcuts - var Tx4 = T[(x + 4) % 5]; - var Tx1 = T[(x + 1) % 5]; - var Tx1Msw = Tx1.high; - var Tx1Lsw = Tx1.low; - - // Mix surrounding columns - var tMsw = Tx4.high ^ ((Tx1Msw << 1) | (Tx1Lsw >>> 31)); - var tLsw = Tx4.low ^ ((Tx1Lsw << 1) | (Tx1Msw >>> 31)); - for (var y = 0; y < 5; y++) { - var lane = state[x + 5 * y]; - lane.high ^= tMsw; - lane.low ^= tLsw; - } - } - - // Rho Pi - for (var laneIndex = 1; laneIndex < 25; laneIndex++) { - // Shortcuts - var lane = state[laneIndex]; - var laneMsw = lane.high; - var laneLsw = lane.low; - var rhoOffset = RHO_OFFSETS[laneIndex]; - - // Rotate lanes - if (rhoOffset < 32) { - var tMsw = (laneMsw << rhoOffset) | (laneLsw >>> (32 - rhoOffset)); - var tLsw = (laneLsw << rhoOffset) | (laneMsw >>> (32 - rhoOffset)); - } else /* if (rhoOffset >= 32) */ { - var tMsw = (laneLsw << (rhoOffset - 32)) | (laneMsw >>> (64 - rhoOffset)); - var tLsw = (laneMsw << (rhoOffset - 32)) | (laneLsw >>> (64 - rhoOffset)); - } - - // Transpose lanes - var TPiLane = T[PI_INDEXES[laneIndex]]; - TPiLane.high = tMsw; - TPiLane.low = tLsw; - } - - // Rho pi at x = y = 0 - var T0 = T[0]; - var state0 = state[0]; - T0.high = state0.high; - T0.low = state0.low; - - // Chi - for (var x = 0; x < 5; x++) { - for (var y = 0; y < 5; y++) { - // Shortcuts - var laneIndex = x + 5 * y; - var lane = state[laneIndex]; - var TLane = T[laneIndex]; - var Tx1Lane = T[((x + 1) % 5) + 5 * y]; - var Tx2Lane = T[((x + 2) % 5) + 5 * y]; - - // Mix rows - lane.high = TLane.high ^ (~Tx1Lane.high & Tx2Lane.high); - lane.low = TLane.low ^ (~Tx1Lane.low & Tx2Lane.low); - } - } - - // Iota - var lane = state[0]; - var roundConstant = ROUND_CONSTANTS[round]; - lane.high ^= roundConstant.high; - lane.low ^= roundConstant.low;; - } - }, - - _doFinalize: function () { - // Shortcuts - var data = this._data; - var dataWords = data.words; - var nBitsTotal = this._nDataBytes * 8; - var nBitsLeft = data.sigBytes * 8; - var blockSizeBits = this.blockSize * 32; - - // Add padding - dataWords[nBitsLeft >>> 5] |= 0x1 << (24 - nBitsLeft % 32); - dataWords[((Math.ceil((nBitsLeft + 1) / blockSizeBits) * blockSizeBits) >>> 5) - 1] |= 0x80; - data.sigBytes = dataWords.length * 4; - - // Hash final blocks - this._process(); - - // Shortcuts - var state = this._state; - var outputLengthBytes = this.cfg.outputLength / 8; - var outputLengthLanes = outputLengthBytes / 8; - - // Squeeze - var hashWords = []; - for (var i = 0; i < outputLengthLanes; i++) { - // Shortcuts - var lane = state[i]; - var laneMsw = lane.high; - var laneLsw = lane.low; - - // Swap endian - laneMsw = ( - (((laneMsw << 8) | (laneMsw >>> 24)) & 0x00ff00ff) | - (((laneMsw << 24) | (laneMsw >>> 8)) & 0xff00ff00) - ); - laneLsw = ( - (((laneLsw << 8) | (laneLsw >>> 24)) & 0x00ff00ff) | - (((laneLsw << 24) | (laneLsw >>> 8)) & 0xff00ff00) - ); - - // Squeeze state to retrieve hash - hashWords.push(laneLsw); - hashWords.push(laneMsw); - } - - // Return final computed hash - return new WordArray.init(hashWords, outputLengthBytes); - }, - - clone: function () { - var clone = Hasher.clone.call(this); - - var state = clone._state = this._state.slice(0); - for (var i = 0; i < 25; i++) { - state[i] = state[i].clone(); - } - - return clone; - } - }); - - /** - * Shortcut function to the hasher's object interface. - * - * @param {WordArray|string} message The message to hash. - * - * @return {WordArray} The hash. - * - * @static - * - * @example - * - * var hash = CryptoJS.SHA3('message'); - * var hash = CryptoJS.SHA3(wordArray); - */ - C.SHA3 = Hasher._createHelper(SHA3); - - /** - * Shortcut function to the HMAC's object interface. - * - * @param {WordArray|string} message The message to hash. - * @param {WordArray|string} key The secret key. - * - * @return {WordArray} The HMAC. - * - * @static - * - * @example - * - * var hmac = CryptoJS.HmacSHA3(message, key); - */ - C.HmacSHA3 = Hasher._createHmacHelper(SHA3); - }(Math)); - - - return CryptoJS.SHA3; - -})); -},{"./core":32,"./x64-core":34}],34:[function(require,module,exports){ -;(function (root, factory) { - if (typeof exports === "object") { - // CommonJS - module.exports = exports = factory(require("./core")); - } - else if (typeof define === "function" && define.amd) { - // AMD - define(["./core"], factory); - } - else { - // Global (browser) - factory(root.CryptoJS); - } -}(this, function (CryptoJS) { - - (function (undefined) { - // Shortcuts - var C = CryptoJS; - var C_lib = C.lib; - var Base = C_lib.Base; - var X32WordArray = C_lib.WordArray; - - /** - * x64 namespace. - */ - var C_x64 = C.x64 = {}; - - /** - * A 64-bit word. - */ - var X64Word = C_x64.Word = Base.extend({ - /** - * Initializes a newly created 64-bit word. - * - * @param {number} high The high 32 bits. - * @param {number} low The low 32 bits. - * - * @example - * - * var x64Word = CryptoJS.x64.Word.create(0x00010203, 0x04050607); - */ - init: function (high, low) { - this.high = high; - this.low = low; - } - - /** - * Bitwise NOTs this word. - * - * @return {X64Word} A new x64-Word object after negating. - * - * @example - * - * var negated = x64Word.not(); - */ - // not: function () { - // var high = ~this.high; - // var low = ~this.low; - - // return X64Word.create(high, low); - // }, - - /** - * Bitwise ANDs this word with the passed word. - * - * @param {X64Word} word The x64-Word to AND with this word. - * - * @return {X64Word} A new x64-Word object after ANDing. - * - * @example - * - * var anded = x64Word.and(anotherX64Word); - */ - // and: function (word) { - // var high = this.high & word.high; - // var low = this.low & word.low; - - // return X64Word.create(high, low); - // }, - - /** - * Bitwise ORs this word with the passed word. - * - * @param {X64Word} word The x64-Word to OR with this word. - * - * @return {X64Word} A new x64-Word object after ORing. - * - * @example - * - * var ored = x64Word.or(anotherX64Word); - */ - // or: function (word) { - // var high = this.high | word.high; - // var low = this.low | word.low; - - // return X64Word.create(high, low); - // }, - - /** - * Bitwise XORs this word with the passed word. - * - * @param {X64Word} word The x64-Word to XOR with this word. - * - * @return {X64Word} A new x64-Word object after XORing. - * - * @example - * - * var xored = x64Word.xor(anotherX64Word); - */ - // xor: function (word) { - // var high = this.high ^ word.high; - // var low = this.low ^ word.low; - - // return X64Word.create(high, low); - // }, - - /** - * Shifts this word n bits to the left. - * - * @param {number} n The number of bits to shift. - * - * @return {X64Word} A new x64-Word object after shifting. - * - * @example - * - * var shifted = x64Word.shiftL(25); - */ - // shiftL: function (n) { - // if (n < 32) { - // var high = (this.high << n) | (this.low >>> (32 - n)); - // var low = this.low << n; - // } else { - // var high = this.low << (n - 32); - // var low = 0; - // } - - // return X64Word.create(high, low); - // }, - - /** - * Shifts this word n bits to the right. - * - * @param {number} n The number of bits to shift. - * - * @return {X64Word} A new x64-Word object after shifting. - * - * @example - * - * var shifted = x64Word.shiftR(7); - */ - // shiftR: function (n) { - // if (n < 32) { - // var low = (this.low >>> n) | (this.high << (32 - n)); - // var high = this.high >>> n; - // } else { - // var low = this.high >>> (n - 32); - // var high = 0; - // } - - // return X64Word.create(high, low); - // }, - - /** - * Rotates this word n bits to the left. - * - * @param {number} n The number of bits to rotate. - * - * @return {X64Word} A new x64-Word object after rotating. - * - * @example - * - * var rotated = x64Word.rotL(25); - */ - // rotL: function (n) { - // return this.shiftL(n).or(this.shiftR(64 - n)); - // }, - - /** - * Rotates this word n bits to the right. - * - * @param {number} n The number of bits to rotate. - * - * @return {X64Word} A new x64-Word object after rotating. - * - * @example - * - * var rotated = x64Word.rotR(7); - */ - // rotR: function (n) { - // return this.shiftR(n).or(this.shiftL(64 - n)); - // }, - - /** - * Adds this word with the passed word. - * - * @param {X64Word} word The x64-Word to add with this word. - * - * @return {X64Word} A new x64-Word object after adding. - * - * @example - * - * var added = x64Word.add(anotherX64Word); - */ - // add: function (word) { - // var low = (this.low + word.low) | 0; - // var carry = (low >>> 0) < (this.low >>> 0) ? 1 : 0; - // var high = (this.high + word.high + carry) | 0; - - // return X64Word.create(high, low); - // } - }); - - /** - * An array of 64-bit words. - * - * @property {Array} words The array of CryptoJS.x64.Word objects. - * @property {number} sigBytes The number of significant bytes in this word array. - */ - var X64WordArray = C_x64.WordArray = Base.extend({ - /** - * Initializes a newly created word array. - * - * @param {Array} words (Optional) An array of CryptoJS.x64.Word objects. - * @param {number} sigBytes (Optional) The number of significant bytes in the words. - * - * @example - * - * var wordArray = CryptoJS.x64.WordArray.create(); - * - * var wordArray = CryptoJS.x64.WordArray.create([ - * CryptoJS.x64.Word.create(0x00010203, 0x04050607), - * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f) - * ]); - * - * var wordArray = CryptoJS.x64.WordArray.create([ - * CryptoJS.x64.Word.create(0x00010203, 0x04050607), - * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f) - * ], 10); - */ - init: function (words, sigBytes) { - words = this.words = words || []; - - if (sigBytes != undefined) { - this.sigBytes = sigBytes; - } else { - this.sigBytes = words.length * 8; - } - }, - - /** - * Converts this 64-bit word array to a 32-bit word array. - * - * @return {CryptoJS.lib.WordArray} This word array's data as a 32-bit word array. - * - * @example - * - * var x32WordArray = x64WordArray.toX32(); - */ - toX32: function () { - // Shortcuts - var x64Words = this.words; - var x64WordsLength = x64Words.length; - - // Convert - var x32Words = []; - for (var i = 0; i < x64WordsLength; i++) { - var x64Word = x64Words[i]; - x32Words.push(x64Word.high); - x32Words.push(x64Word.low); - } - - return X32WordArray.create(x32Words, this.sigBytes); - }, - - /** - * Creates a copy of this word array. - * - * @return {X64WordArray} The clone. - * - * @example - * - * var clone = x64WordArray.clone(); - */ - clone: function () { - var clone = Base.clone.call(this); - - // Clone "words" array - var words = clone.words = this.words.slice(0); - - // Clone each X64Word object - var wordsLength = words.length; - for (var i = 0; i < wordsLength; i++) { - words[i] = words[i].clone(); - } - - return clone; - } - }); - }()); - - - return CryptoJS; - -})); -},{"./core":32}],"bignumber.js":[function(require,module,exports){ -'use strict'; - -module.exports = BigNumber; // jshint ignore:line - - -},{}],"web3":[function(require,module,exports){ -var web3 = require('./lib/web3'); -web3.providers.HttpProvider = require('./lib/web3/httpprovider'); -web3.providers.QtSyncProvider = require('./lib/web3/qtsync'); -web3.eth.contract = require('./lib/web3/contract'); -web3.eth.namereg = require('./lib/web3/namereg'); -web3.eth.sendIBANTransaction = require('./lib/web3/transfer'); - -// dont override global variable -if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') { - window.web3 = web3; -} - -module.exports = web3; - - -},{"./lib/web3":9,"./lib/web3/contract":11,"./lib/web3/httpprovider":19,"./lib/web3/namereg":23,"./lib/web3/qtsync":26,"./lib/web3/transfer":29}]},{},["web3"]) -//# sourceMappingURL=data:application/json;charset:utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJsaWIvc29saWRpdHkvY29kZXIuanMiLCJsaWIvc29saWRpdHkvZm9ybWF0dGVycy5qcyIsImxpYi9zb2xpZGl0eS9wYXJhbS5qcyIsImxpYi91dGlscy9icm93c2VyLXhoci5qcyIsImxpYi91dGlscy9jb25maWcuanMiLCJsaWIvdXRpbHMvc2hhMy5qcyIsImxpYi91dGlscy91dGlscy5qcyIsImxpYi92ZXJzaW9uLmpzb24iLCJsaWIvd2ViMy5qcyIsImxpYi93ZWIzL2JhdGNoLmpzIiwibGliL3dlYjMvY29udHJhY3QuanMiLCJsaWIvd2ViMy9kYi5qcyIsImxpYi93ZWIzL2Vycm9ycy5qcyIsImxpYi93ZWIzL2V0aC5qcyIsImxpYi93ZWIzL2V2ZW50LmpzIiwibGliL3dlYjMvZmlsdGVyLmpzIiwibGliL3dlYjMvZm9ybWF0dGVycy5qcyIsImxpYi93ZWIzL2Z1bmN0aW9uLmpzIiwibGliL3dlYjMvaHR0cHByb3ZpZGVyLmpzIiwibGliL3dlYjMvaWNhcC5qcyIsImxpYi93ZWIzL2pzb25ycGMuanMiLCJsaWIvd2ViMy9tZXRob2QuanMiLCJsaWIvd2ViMy9uYW1lcmVnLmpzIiwibGliL3dlYjMvbmV0LmpzIiwibGliL3dlYjMvcHJvcGVydHkuanMiLCJsaWIvd2ViMy9xdHN5bmMuanMiLCJsaWIvd2ViMy9yZXF1ZXN0bWFuYWdlci5qcyIsImxpYi93ZWIzL3NoaC5qcyIsImxpYi93ZWIzL3RyYW5zZmVyLmpzIiwibGliL3dlYjMvd2F0Y2hlcy5qcyIsIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L2xpYi9fZW1wdHkuanMiLCJub2RlX21vZHVsZXMvY3J5cHRvLWpzL2NvcmUuanMiLCJub2RlX21vZHVsZXMvY3J5cHRvLWpzL3NoYTMuanMiLCJub2RlX21vZHVsZXMvY3J5cHRvLWpzL3g2NC1jb3JlLmpzIiwibGliL3V0aWxzL2Jyb3dzZXItYm4uanMiLCJpbmRleC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFSQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMU5BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xOQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNUQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5RUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdkNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xmQTtBQUNBO0FBQ0E7QUFDQTs7QUNIQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3REE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcExBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4REE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3RDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDblJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25NQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDM0pBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxTkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDak9BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1R0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1S0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDaERBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwSEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3RQQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcEVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOUZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xIQTs7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNydUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbFVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQy9TQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ0pBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBcbiAqIEBmaWxlIGNvZGVyLmpzXG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciBCaWdOdW1iZXIgPSByZXF1aXJlKCdiaWdudW1iZXIuanMnKTtcbnZhciB1dGlscyA9IHJlcXVpcmUoJy4uL3V0aWxzL3V0aWxzJyk7XG52YXIgZiA9IHJlcXVpcmUoJy4vZm9ybWF0dGVycycpO1xudmFyIFNvbGlkaXR5UGFyYW0gPSByZXF1aXJlKCcuL3BhcmFtJyk7XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gY2hlY2sgaWYgYSB0eXBlIGlzIGFuIGFycmF5IHR5cGVcbiAqXG4gKiBAbWV0aG9kIGlzQXJyYXlUeXBlXG4gKiBAcGFyYW0ge1N0cmluZ30gdHlwZVxuICogQHJldHVybiB7Qm9vbH0gdHJ1ZSBpcyB0aGUgdHlwZSBpcyBhbiBhcnJheSwgb3RoZXJ3aXNlIGZhbHNlXG4gKi9cbnZhciBpc0FycmF5VHlwZSA9IGZ1bmN0aW9uICh0eXBlKSB7XG4gICAgcmV0dXJuIHR5cGUuc2xpY2UoLTIpID09PSAnW10nO1xufTtcblxuLyoqXG4gKiBTb2xpZGl0eVR5cGUgcHJvdG90eXBlIGlzIHVzZWQgdG8gZW5jb2RlL2RlY29kZSBzb2xpZGl0eSBwYXJhbXMgb2YgY2VydGFpbiB0eXBlXG4gKi9cbnZhciBTb2xpZGl0eVR5cGUgPSBmdW5jdGlvbiAoY29uZmlnKSB7XG4gICAgdGhpcy5fbmFtZSA9IGNvbmZpZy5uYW1lO1xuICAgIHRoaXMuX21hdGNoID0gY29uZmlnLm1hdGNoO1xuICAgIHRoaXMuX21vZGUgPSBjb25maWcubW9kZTtcbiAgICB0aGlzLl9pbnB1dEZvcm1hdHRlciA9IGNvbmZpZy5pbnB1dEZvcm1hdHRlcjtcbiAgICB0aGlzLl9vdXRwdXRGb3JtYXR0ZXIgPSBjb25maWcub3V0cHV0Rm9ybWF0dGVyO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBkZXRlcm1pbmUgaWYgdGhpcyBTb2xpZGl0eVR5cGUgZG8gbWF0Y2ggZ2l2ZW4gdHlwZVxuICpcbiAqIEBtZXRob2QgaXNUeXBlXG4gKiBAcGFyYW0ge1N0cmluZ30gbmFtZVxuICogQHJldHVybiB7Qm9vbH0gdHJ1ZSBpZiB0eXBlIG1hdGNoIHRoaXMgU29saWRpdHlUeXBlLCBvdGhlcndpc2UgZmFsc2VcbiAqL1xuU29saWRpdHlUeXBlLnByb3RvdHlwZS5pc1R5cGUgPSBmdW5jdGlvbiAobmFtZSkge1xuICAgIGlmICh0aGlzLl9tYXRjaCA9PT0gJ3N0cmljdCcpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX25hbWUgPT09IG5hbWUgfHwgKG5hbWUuaW5kZXhPZih0aGlzLl9uYW1lKSA9PT0gMCAmJiBuYW1lLnNsaWNlKHRoaXMuX25hbWUubGVuZ3RoKSA9PT0gJ1tdJyk7XG4gICAgfSBlbHNlIGlmICh0aGlzLl9tYXRjaCA9PT0gJ3ByZWZpeCcpIHtcbiAgICAgICAgLy8gVE9ETyBiZXR0ZXIgdHlwZSBkZXRlY3Rpb24hXG4gICAgICAgIHJldHVybiBuYW1lLmluZGV4T2YodGhpcy5fbmFtZSkgPT09IDA7XG4gICAgfVxufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byB0cmFuc2Zvcm0gcGxhaW4gcGFyYW0gdG8gU29saWRpdHlQYXJhbSBvYmplY3RcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdElucHV0XG4gKiBAcGFyYW0ge09iamVjdH0gcGFyYW0gLSBwbGFpbiBvYmplY3QsIG9yIGFuIGFycmF5IG9mIG9iamVjdHNcbiAqIEBwYXJhbSB7Qm9vbH0gYXJyYXlUeXBlIC0gdHJ1ZSBpZiBhIHBhcmFtIHNob3VsZCBiZSBlbmNvZGVkIGFzIGFuIGFycmF5XG4gKiBAcmV0dXJuIHtTb2xpZGl0eVBhcmFtfSBlbmNvZGVkIHBhcmFtIHdyYXBwZWQgaW4gU29saWRpdHlQYXJhbSBvYmplY3QgXG4gKi9cblNvbGlkaXR5VHlwZS5wcm90b3R5cGUuZm9ybWF0SW5wdXQgPSBmdW5jdGlvbiAocGFyYW0sIGFycmF5VHlwZSkge1xuICAgIGlmICh1dGlscy5pc0FycmF5KHBhcmFtKSAmJiBhcnJheVR5cGUpIHsgLy8gVE9ETzogc2hvdWxkIGZhaWwgaWYgdGhpcyB0d28gYXJlIG5vdCB0aGUgc2FtZVxuICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgIHJldHVybiBwYXJhbS5tYXAoZnVuY3Rpb24gKHApIHtcbiAgICAgICAgICAgIHJldHVybiBzZWxmLl9pbnB1dEZvcm1hdHRlcihwKTtcbiAgICAgICAgfSkucmVkdWNlKGZ1bmN0aW9uIChhY2MsIGN1cnJlbnQpIHtcbiAgICAgICAgICAgIHJldHVybiBhY2MuY29tYmluZShjdXJyZW50KTtcbiAgICAgICAgfSwgZi5mb3JtYXRJbnB1dEludChwYXJhbS5sZW5ndGgpKS53aXRoT2Zmc2V0KDMyKTtcbiAgICB9IFxuICAgIHJldHVybiB0aGlzLl9pbnB1dEZvcm1hdHRlcihwYXJhbSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIHRyYW5zb2Zvcm0gU29saWRpdHlQYXJhbSB0byBwbGFpbiBwYXJhbVxuICpcbiAqIEBtZXRob2QgZm9ybWF0T3V0cHV0XG4gKiBAcGFyYW0ge1NvbGlkaXR5UGFyYW19IGJ5dGVBcnJheVxuICogQHBhcmFtIHtCb29sfSBhcnJheVR5cGUgLSB0cnVlIGlmIGEgcGFyYW0gc2hvdWxkIGJlIGRlY29kZWQgYXMgYW4gYXJyYXlcbiAqIEByZXR1cm4ge09iamVjdH0gcGxhaW4gZGVjb2RlZCBwYXJhbVxuICovXG5Tb2xpZGl0eVR5cGUucHJvdG90eXBlLmZvcm1hdE91dHB1dCA9IGZ1bmN0aW9uIChwYXJhbSwgYXJyYXlUeXBlKSB7XG4gICAgaWYgKGFycmF5VHlwZSkge1xuICAgICAgICAvLyBsZXQncyBhc3N1bWUsIHRoYXQgd2Ugc29saWRpdHkgd2lsbCBuZXZlciByZXR1cm4gbG9uZyBhcnJheXMgOlAgXG4gICAgICAgIHZhciByZXN1bHQgPSBbXTtcbiAgICAgICAgdmFyIGxlbmd0aCA9IG5ldyBCaWdOdW1iZXIocGFyYW0uZHluYW1pY1BhcnQoKS5zbGljZSgwLCA2NCksIDE2KTtcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsZW5ndGggKiA2NDsgaSArPSA2NCkge1xuICAgICAgICAgICAgcmVzdWx0LnB1c2godGhpcy5fb3V0cHV0Rm9ybWF0dGVyKG5ldyBTb2xpZGl0eVBhcmFtKHBhcmFtLmR5bmFtaWNQYXJ0KCkuc3Vic3RyKGkgKyA2NCwgNjQpKSkpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9vdXRwdXRGb3JtYXR0ZXIocGFyYW0pO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBzbGljZSBzaW5nbGUgcGFyYW0gZnJvbSBieXRlc1xuICpcbiAqIEBtZXRob2Qgc2xpY2VQYXJhbVxuICogQHBhcmFtIHtTdHJpbmd9IGJ5dGVzXG4gKiBAcGFyYW0ge051bWJlcn0gaW5kZXggb2YgcGFyYW0gdG8gc2xpY2VcbiAqIEBwYXJhbSB7U3RyaW5nfSB0eXBlXG4gKiBAcmV0dXJucyB7U29saWRpdHlQYXJhbX0gcGFyYW1cbiAqL1xuU29saWRpdHlUeXBlLnByb3RvdHlwZS5zbGljZVBhcmFtID0gZnVuY3Rpb24gKGJ5dGVzLCBpbmRleCwgdHlwZSkge1xuICAgIGlmICh0aGlzLl9tb2RlID09PSAnYnl0ZXMnKSB7XG4gICAgICAgIHJldHVybiBTb2xpZGl0eVBhcmFtLmRlY29kZUJ5dGVzKGJ5dGVzLCBpbmRleCk7XG4gICAgfSBlbHNlIGlmIChpc0FycmF5VHlwZSh0eXBlKSkge1xuICAgICAgICByZXR1cm4gU29saWRpdHlQYXJhbS5kZWNvZGVBcnJheShieXRlcywgaW5kZXgpO1xuICAgIH1cbiAgICByZXR1cm4gU29saWRpdHlQYXJhbS5kZWNvZGVQYXJhbShieXRlcywgaW5kZXgpO1xufTtcblxuLyoqXG4gKiBTb2xpZGl0eUNvZGVyIHByb3RvdHlwZSBzaG91bGQgYmUgdXNlZCB0byBlbmNvZGUvZGVjb2RlIHNvbGlkaXR5IHBhcmFtcyBvZiBhbnkgdHlwZVxuICovXG52YXIgU29saWRpdHlDb2RlciA9IGZ1bmN0aW9uICh0eXBlcykge1xuICAgIHRoaXMuX3R5cGVzID0gdHlwZXM7XG59O1xuXG4vKipcbiAqIFRoaXMgbWV0aG9kIHNob3VsZCBiZSB1c2VkIHRvIHRyYW5zZm9ybSB0eXBlIHRvIFNvbGlkaXR5VHlwZVxuICpcbiAqIEBtZXRob2QgX3JlcXVpcmVUeXBlXG4gKiBAcGFyYW0ge1N0cmluZ30gdHlwZVxuICogQHJldHVybnMge1NvbGlkaXR5VHlwZX0gXG4gKiBAdGhyb3dzIHtFcnJvcn0gdGhyb3dzIGlmIG5vIG1hdGNoaW5nIHR5cGUgaXMgZm91bmRcbiAqL1xuU29saWRpdHlDb2Rlci5wcm90b3R5cGUuX3JlcXVpcmVUeXBlID0gZnVuY3Rpb24gKHR5cGUpIHtcbiAgICB2YXIgc29saWRpdHlUeXBlID0gdGhpcy5fdHlwZXMuZmlsdGVyKGZ1bmN0aW9uICh0KSB7XG4gICAgICAgIHJldHVybiB0LmlzVHlwZSh0eXBlKTtcbiAgICB9KVswXTtcblxuICAgIGlmICghc29saWRpdHlUeXBlKSB7XG4gICAgICAgIHRocm93IEVycm9yKCdpbnZhbGlkIHNvbGlkaXR5IHR5cGUhOiAnICsgdHlwZSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHNvbGlkaXR5VHlwZTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gdHJhbnNmb3JtIHBsYWluIHBhcmFtIG9mIGdpdmVuIHR5cGUgdG8gU29saWRpdHlQYXJhbVxuICpcbiAqIEBtZXRob2QgX2Zvcm1hdElucHV0XG4gKiBAcGFyYW0ge1N0cmluZ30gdHlwZSBvZiBwYXJhbVxuICogQHBhcmFtIHtPYmplY3R9IHBsYWluIHBhcmFtXG4gKiBAcmV0dXJuIHtTb2xpZGl0eVBhcmFtfVxuICovXG5Tb2xpZGl0eUNvZGVyLnByb3RvdHlwZS5fZm9ybWF0SW5wdXQgPSBmdW5jdGlvbiAodHlwZSwgcGFyYW0pIHtcbiAgICByZXR1cm4gdGhpcy5fcmVxdWlyZVR5cGUodHlwZSkuZm9ybWF0SW5wdXQocGFyYW0sIGlzQXJyYXlUeXBlKHR5cGUpKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZW5jb2RlIHBsYWluIHBhcmFtXG4gKlxuICogQG1ldGhvZCBlbmNvZGVQYXJhbVxuICogQHBhcmFtIHtTdHJpbmd9IHR5cGVcbiAqIEBwYXJhbSB7T2JqZWN0fSBwbGFpbiBwYXJhbVxuICogQHJldHVybiB7U3RyaW5nfSBlbmNvZGVkIHBsYWluIHBhcmFtXG4gKi9cblNvbGlkaXR5Q29kZXIucHJvdG90eXBlLmVuY29kZVBhcmFtID0gZnVuY3Rpb24gKHR5cGUsIHBhcmFtKSB7XG4gICAgcmV0dXJuIHRoaXMuX2Zvcm1hdElucHV0KHR5cGUsIHBhcmFtKS5lbmNvZGUoKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZW5jb2RlIGxpc3Qgb2YgcGFyYW1zXG4gKlxuICogQG1ldGhvZCBlbmNvZGVQYXJhbXNcbiAqIEBwYXJhbSB7QXJyYXl9IHR5cGVzXG4gKiBAcGFyYW0ge0FycmF5fSBwYXJhbXNcbiAqIEByZXR1cm4ge1N0cmluZ30gZW5jb2RlZCBsaXN0IG9mIHBhcmFtc1xuICovXG5Tb2xpZGl0eUNvZGVyLnByb3RvdHlwZS5lbmNvZGVQYXJhbXMgPSBmdW5jdGlvbiAodHlwZXMsIHBhcmFtcykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgc29saWRpdHlQYXJhbXMgPSB0eXBlcy5tYXAoZnVuY3Rpb24gKHR5cGUsIGluZGV4KSB7XG4gICAgICAgIHJldHVybiBzZWxmLl9mb3JtYXRJbnB1dCh0eXBlLCBwYXJhbXNbaW5kZXhdKTtcbiAgICB9KTtcblxuICAgIHJldHVybiBTb2xpZGl0eVBhcmFtLmVuY29kZUxpc3Qoc29saWRpdHlQYXJhbXMpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBkZWNvZGUgYnl0ZXMgdG8gcGxhaW4gcGFyYW1cbiAqXG4gKiBAbWV0aG9kIGRlY29kZVBhcmFtXG4gKiBAcGFyYW0ge1N0cmluZ30gdHlwZVxuICogQHBhcmFtIHtTdHJpbmd9IGJ5dGVzXG4gKiBAcmV0dXJuIHtPYmplY3R9IHBsYWluIHBhcmFtXG4gKi9cblNvbGlkaXR5Q29kZXIucHJvdG90eXBlLmRlY29kZVBhcmFtID0gZnVuY3Rpb24gKHR5cGUsIGJ5dGVzKSB7XG4gICAgcmV0dXJuIHRoaXMuZGVjb2RlUGFyYW1zKFt0eXBlXSwgYnl0ZXMpWzBdO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBkZWNvZGUgbGlzdCBvZiBwYXJhbXNcbiAqXG4gKiBAbWV0aG9kIGRlY29kZVBhcmFtXG4gKiBAcGFyYW0ge0FycmF5fSB0eXBlc1xuICogQHBhcmFtIHtTdHJpbmd9IGJ5dGVzXG4gKiBAcmV0dXJuIHtBcnJheX0gYXJyYXkgb2YgcGxhaW4gcGFyYW1zXG4gKi9cblNvbGlkaXR5Q29kZXIucHJvdG90eXBlLmRlY29kZVBhcmFtcyA9IGZ1bmN0aW9uICh0eXBlcywgYnl0ZXMpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgcmV0dXJuIHR5cGVzLm1hcChmdW5jdGlvbiAodHlwZSwgaW5kZXgpIHtcbiAgICAgICAgdmFyIHNvbGlkaXR5VHlwZSA9IHNlbGYuX3JlcXVpcmVUeXBlKHR5cGUpO1xuICAgICAgICB2YXIgcCA9IHNvbGlkaXR5VHlwZS5zbGljZVBhcmFtKGJ5dGVzLCBpbmRleCwgdHlwZSk7XG4gICAgICAgIHJldHVybiBzb2xpZGl0eVR5cGUuZm9ybWF0T3V0cHV0KHAsIGlzQXJyYXlUeXBlKHR5cGUpKTtcbiAgICB9KTtcbn07XG5cbnZhciBjb2RlciA9IG5ldyBTb2xpZGl0eUNvZGVyKFtcbiAgICBuZXcgU29saWRpdHlUeXBlKHtcbiAgICAgICAgbmFtZTogJ2FkZHJlc3MnLFxuICAgICAgICBtYXRjaDogJ3N0cmljdCcsXG4gICAgICAgIG1vZGU6ICd2YWx1ZScsXG4gICAgICAgIGlucHV0Rm9ybWF0dGVyOiBmLmZvcm1hdElucHV0SW50LFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IGYuZm9ybWF0T3V0cHV0QWRkcmVzc1xuICAgIH0pLFxuICAgIG5ldyBTb2xpZGl0eVR5cGUoe1xuICAgICAgICBuYW1lOiAnYm9vbCcsXG4gICAgICAgIG1hdGNoOiAnc3RyaWN0JyxcbiAgICAgICAgbW9kZTogJ3ZhbHVlJyxcbiAgICAgICAgaW5wdXRGb3JtYXR0ZXI6IGYuZm9ybWF0SW5wdXRCb29sLFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IGYuZm9ybWF0T3V0cHV0Qm9vbFxuICAgIH0pLFxuICAgIG5ldyBTb2xpZGl0eVR5cGUoe1xuICAgICAgICBuYW1lOiAnaW50JyxcbiAgICAgICAgbWF0Y2g6ICdwcmVmaXgnLFxuICAgICAgICBtb2RlOiAndmFsdWUnLFxuICAgICAgICBpbnB1dEZvcm1hdHRlcjogZi5mb3JtYXRJbnB1dEludCxcbiAgICAgICAgb3V0cHV0Rm9ybWF0dGVyOiBmLmZvcm1hdE91dHB1dEludCxcbiAgICB9KSxcbiAgICBuZXcgU29saWRpdHlUeXBlKHtcbiAgICAgICAgbmFtZTogJ3VpbnQnLFxuICAgICAgICBtYXRjaDogJ3ByZWZpeCcsXG4gICAgICAgIG1vZGU6ICd2YWx1ZScsXG4gICAgICAgIGlucHV0Rm9ybWF0dGVyOiBmLmZvcm1hdElucHV0SW50LFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IGYuZm9ybWF0T3V0cHV0VUludFxuICAgIH0pLFxuICAgIG5ldyBTb2xpZGl0eVR5cGUoe1xuICAgICAgICBuYW1lOiAnYnl0ZXMnLFxuICAgICAgICBtYXRjaDogJ3N0cmljdCcsXG4gICAgICAgIG1vZGU6ICdieXRlcycsXG4gICAgICAgIGlucHV0Rm9ybWF0dGVyOiBmLmZvcm1hdElucHV0RHluYW1pY0J5dGVzLFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IGYuZm9ybWF0T3V0cHV0RHluYW1pY0J5dGVzXG4gICAgfSksXG4gICAgbmV3IFNvbGlkaXR5VHlwZSh7XG4gICAgICAgIG5hbWU6ICdieXRlcycsXG4gICAgICAgIG1hdGNoOiAncHJlZml4JyxcbiAgICAgICAgbW9kZTogJ3ZhbHVlJyxcbiAgICAgICAgaW5wdXRGb3JtYXR0ZXI6IGYuZm9ybWF0SW5wdXRCeXRlcyxcbiAgICAgICAgb3V0cHV0Rm9ybWF0dGVyOiBmLmZvcm1hdE91dHB1dEJ5dGVzXG4gICAgfSksXG4gICAgbmV3IFNvbGlkaXR5VHlwZSh7XG4gICAgICAgIG5hbWU6ICdyZWFsJyxcbiAgICAgICAgbWF0Y2g6ICdwcmVmaXgnLFxuICAgICAgICBtb2RlOiAndmFsdWUnLFxuICAgICAgICBpbnB1dEZvcm1hdHRlcjogZi5mb3JtYXRJbnB1dFJlYWwsXG4gICAgICAgIG91dHB1dEZvcm1hdHRlcjogZi5mb3JtYXRPdXRwdXRSZWFsXG4gICAgfSksXG4gICAgbmV3IFNvbGlkaXR5VHlwZSh7XG4gICAgICAgIG5hbWU6ICd1cmVhbCcsXG4gICAgICAgIG1hdGNoOiAncHJlZml4JyxcbiAgICAgICAgbW9kZTogJ3ZhbHVlJyxcbiAgICAgICAgaW5wdXRGb3JtYXR0ZXI6IGYuZm9ybWF0SW5wdXRSZWFsLFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IGYuZm9ybWF0T3V0cHV0VVJlYWxcbiAgICB9KVxuXSk7XG5cbm1vZHVsZS5leHBvcnRzID0gY29kZXI7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIFxuICogQGZpbGUgZm9ybWF0dGVycy5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgQmlnTnVtYmVyID0gcmVxdWlyZSgnYmlnbnVtYmVyLmpzJyk7XG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xudmFyIGMgPSByZXF1aXJlKCcuLi91dGlscy9jb25maWcnKTtcbnZhciBTb2xpZGl0eVBhcmFtID0gcmVxdWlyZSgnLi9wYXJhbScpO1xuXG5cbi8qKlxuICogRm9ybWF0cyBpbnB1dCB2YWx1ZSB0byBieXRlIHJlcHJlc2VudGF0aW9uIG9mIGludFxuICogSWYgdmFsdWUgaXMgbmVnYXRpdmUsIHJldHVybiBpdCdzIHR3bydzIGNvbXBsZW1lbnRcbiAqIElmIHRoZSB2YWx1ZSBpcyBmbG9hdGluZyBwb2ludCwgcm91bmQgaXQgZG93blxuICpcbiAqIEBtZXRob2QgZm9ybWF0SW5wdXRJbnRcbiAqIEBwYXJhbSB7U3RyaW5nfE51bWJlcnxCaWdOdW1iZXJ9IHZhbHVlIHRoYXQgbmVlZHMgdG8gYmUgZm9ybWF0dGVkXG4gKiBAcmV0dXJucyB7U29saWRpdHlQYXJhbX1cbiAqL1xudmFyIGZvcm1hdElucHV0SW50ID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgdmFyIHBhZGRpbmcgPSBjLkVUSF9QQURESU5HICogMjtcbiAgICBCaWdOdW1iZXIuY29uZmlnKGMuRVRIX0JJR05VTUJFUl9ST1VORElOR19NT0RFKTtcbiAgICB2YXIgcmVzdWx0ID0gdXRpbHMucGFkTGVmdCh1dGlscy50b1R3b3NDb21wbGVtZW50KHZhbHVlKS5yb3VuZCgpLnRvU3RyaW5nKDE2KSwgcGFkZGluZyk7XG4gICAgcmV0dXJuIG5ldyBTb2xpZGl0eVBhcmFtKHJlc3VsdCk7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgaW5wdXQgdmFsdWUgdG8gYnl0ZSByZXByZXNlbnRhdGlvbiBvZiBzdHJpbmdcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdElucHV0Qnl0ZXNcbiAqIEBwYXJhbSB7U3RyaW5nfVxuICogQHJldHVybnMge1NvbGlkaXR5UGFyYW19XG4gKi9cbnZhciBmb3JtYXRJbnB1dEJ5dGVzID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgdmFyIHJlc3VsdCA9IHV0aWxzLmZyb21Bc2NpaSh2YWx1ZSwgYy5FVEhfUEFERElORykuc3Vic3RyKDIpO1xuICAgIHJldHVybiBuZXcgU29saWRpdHlQYXJhbShyZXN1bHQpO1xufTtcblxuLyoqXG4gKiBGb3JtYXRzIGlucHV0IHZhbHVlIHRvIGJ5dGUgcmVwcmVzZW50YXRpb24gb2Ygc3RyaW5nXG4gKlxuICogQG1ldGhvZCBmb3JtYXRJbnB1dER5bmFtaWNCeXRlc1xuICogQHBhcmFtIHtTdHJpbmd9XG4gKiBAcmV0dXJucyB7U29saWRpdHlQYXJhbX1cbiAqL1xudmFyIGZvcm1hdElucHV0RHluYW1pY0J5dGVzID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgdmFyIHJlc3VsdCA9IHV0aWxzLmZyb21Bc2NpaSh2YWx1ZSwgYy5FVEhfUEFERElORykuc3Vic3RyKDIpO1xuICAgIHJldHVybiBuZXcgU29saWRpdHlQYXJhbShmb3JtYXRJbnB1dEludCh2YWx1ZS5sZW5ndGgpLnZhbHVlICsgcmVzdWx0LCAzMik7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgaW5wdXQgdmFsdWUgdG8gYnl0ZSByZXByZXNlbnRhdGlvbiBvZiBib29sXG4gKlxuICogQG1ldGhvZCBmb3JtYXRJbnB1dEJvb2xcbiAqIEBwYXJhbSB7Qm9vbGVhbn1cbiAqIEByZXR1cm5zIHtTb2xpZGl0eVBhcmFtfVxuICovXG52YXIgZm9ybWF0SW5wdXRCb29sID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgdmFyIHJlc3VsdCA9ICcwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAnICsgKHZhbHVlID8gICcxJyA6ICcwJyk7XG4gICAgcmV0dXJuIG5ldyBTb2xpZGl0eVBhcmFtKHJlc3VsdCk7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgaW5wdXQgdmFsdWUgdG8gYnl0ZSByZXByZXNlbnRhdGlvbiBvZiByZWFsXG4gKiBWYWx1ZXMgYXJlIG11bHRpcGxpZWQgYnkgMl5tIGFuZCBlbmNvZGVkIGFzIGludGVnZXJzXG4gKlxuICogQG1ldGhvZCBmb3JtYXRJbnB1dFJlYWxcbiAqIEBwYXJhbSB7U3RyaW5nfE51bWJlcnxCaWdOdW1iZXJ9XG4gKiBAcmV0dXJucyB7U29saWRpdHlQYXJhbX1cbiAqL1xudmFyIGZvcm1hdElucHV0UmVhbCA9IGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgIHJldHVybiBmb3JtYXRJbnB1dEludChuZXcgQmlnTnVtYmVyKHZhbHVlKS50aW1lcyhuZXcgQmlnTnVtYmVyKDIpLnBvdygxMjgpKSk7XG59O1xuXG4vKipcbiAqIENoZWNrIGlmIGlucHV0IHZhbHVlIGlzIG5lZ2F0aXZlXG4gKlxuICogQG1ldGhvZCBzaWduZWRJc05lZ2F0aXZlXG4gKiBAcGFyYW0ge1N0cmluZ30gdmFsdWUgaXMgaGV4IGZvcm1hdFxuICogQHJldHVybnMge0Jvb2xlYW59IHRydWUgaWYgaXQgaXMgbmVnYXRpdmUsIG90aGVyd2lzZSBmYWxzZVxuICovXG52YXIgc2lnbmVkSXNOZWdhdGl2ZSA9IGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgIHJldHVybiAobmV3IEJpZ051bWJlcih2YWx1ZS5zdWJzdHIoMCwgMSksIDE2KS50b1N0cmluZygyKS5zdWJzdHIoMCwgMSkpID09PSAnMSc7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgcmlnaHQtYWxpZ25lZCBvdXRwdXQgYnl0ZXMgdG8gaW50XG4gKlxuICogQG1ldGhvZCBmb3JtYXRPdXRwdXRJbnRcbiAqIEBwYXJhbSB7U29saWRpdHlQYXJhbX0gcGFyYW1cbiAqIEByZXR1cm5zIHtCaWdOdW1iZXJ9IHJpZ2h0LWFsaWduZWQgb3V0cHV0IGJ5dGVzIGZvcm1hdHRlZCB0byBiaWcgbnVtYmVyXG4gKi9cbnZhciBmb3JtYXRPdXRwdXRJbnQgPSBmdW5jdGlvbiAocGFyYW0pIHtcbiAgICB2YXIgdmFsdWUgPSBwYXJhbS5zdGF0aWNQYXJ0KCkgfHwgXCIwXCI7XG5cbiAgICAvLyBjaGVjayBpZiBpdCdzIG5lZ2F0aXZlIG51bWJlclxuICAgIC8vIGl0IGl0IGlzLCByZXR1cm4gdHdvJ3MgY29tcGxlbWVudFxuICAgIGlmIChzaWduZWRJc05lZ2F0aXZlKHZhbHVlKSkge1xuICAgICAgICByZXR1cm4gbmV3IEJpZ051bWJlcih2YWx1ZSwgMTYpLm1pbnVzKG5ldyBCaWdOdW1iZXIoJ2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYnLCAxNikpLm1pbnVzKDEpO1xuICAgIH1cbiAgICByZXR1cm4gbmV3IEJpZ051bWJlcih2YWx1ZSwgMTYpO1xufTtcblxuLyoqXG4gKiBGb3JtYXRzIHJpZ2h0LWFsaWduZWQgb3V0cHV0IGJ5dGVzIHRvIHVpbnRcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdE91dHB1dFVJbnRcbiAqIEBwYXJhbSB7U29saWRpdHlQYXJhbX1cbiAqIEByZXR1cm5zIHtCaWdOdW1lYmVyfSByaWdodC1hbGlnbmVkIG91dHB1dCBieXRlcyBmb3JtYXR0ZWQgdG8gdWludFxuICovXG52YXIgZm9ybWF0T3V0cHV0VUludCA9IGZ1bmN0aW9uIChwYXJhbSkge1xuICAgIHZhciB2YWx1ZSA9IHBhcmFtLnN0YXRpY1BhcnQoKSB8fCBcIjBcIjtcbiAgICByZXR1cm4gbmV3IEJpZ051bWJlcih2YWx1ZSwgMTYpO1xufTtcblxuLyoqXG4gKiBGb3JtYXRzIHJpZ2h0LWFsaWduZWQgb3V0cHV0IGJ5dGVzIHRvIHJlYWxcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdE91dHB1dFJlYWxcbiAqIEBwYXJhbSB7U29saWRpdHlQYXJhbX1cbiAqIEByZXR1cm5zIHtCaWdOdW1iZXJ9IGlucHV0IGJ5dGVzIGZvcm1hdHRlZCB0byByZWFsXG4gKi9cbnZhciBmb3JtYXRPdXRwdXRSZWFsID0gZnVuY3Rpb24gKHBhcmFtKSB7XG4gICAgcmV0dXJuIGZvcm1hdE91dHB1dEludChwYXJhbSkuZGl2aWRlZEJ5KG5ldyBCaWdOdW1iZXIoMikucG93KDEyOCkpOyBcbn07XG5cbi8qKlxuICogRm9ybWF0cyByaWdodC1hbGlnbmVkIG91dHB1dCBieXRlcyB0byB1cmVhbFxuICpcbiAqIEBtZXRob2QgZm9ybWF0T3V0cHV0VVJlYWxcbiAqIEBwYXJhbSB7U29saWRpdHlQYXJhbX1cbiAqIEByZXR1cm5zIHtCaWdOdW1iZXJ9IGlucHV0IGJ5dGVzIGZvcm1hdHRlZCB0byB1cmVhbFxuICovXG52YXIgZm9ybWF0T3V0cHV0VVJlYWwgPSBmdW5jdGlvbiAocGFyYW0pIHtcbiAgICByZXR1cm4gZm9ybWF0T3V0cHV0VUludChwYXJhbSkuZGl2aWRlZEJ5KG5ldyBCaWdOdW1iZXIoMikucG93KDEyOCkpOyBcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZm9ybWF0IG91dHB1dCBib29sXG4gKlxuICogQG1ldGhvZCBmb3JtYXRPdXRwdXRCb29sXG4gKiBAcGFyYW0ge1NvbGlkaXR5UGFyYW19XG4gKiBAcmV0dXJucyB7Qm9vbGVhbn0gcmlnaHQtYWxpZ25lZCBpbnB1dCBieXRlcyBmb3JtYXR0ZWQgdG8gYm9vbFxuICovXG52YXIgZm9ybWF0T3V0cHV0Qm9vbCA9IGZ1bmN0aW9uIChwYXJhbSkge1xuICAgIHJldHVybiBwYXJhbS5zdGF0aWNQYXJ0KCkgPT09ICcwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxJyA/IHRydWUgOiBmYWxzZTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZm9ybWF0IG91dHB1dCBzdHJpbmdcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdE91dHB1dEJ5dGVzXG4gKiBAcGFyYW0ge1NvbGlkaXR5UGFyYW19IGxlZnQtYWxpZ25lZCBoZXggcmVwcmVzZW50YXRpb24gb2Ygc3RyaW5nXG4gKiBAcmV0dXJucyB7U3RyaW5nfSBhc2NpaSBzdHJpbmdcbiAqL1xudmFyIGZvcm1hdE91dHB1dEJ5dGVzID0gZnVuY3Rpb24gKHBhcmFtKSB7XG4gICAgLy8gbGVuZ3RoIG1pZ2h0IGFsc28gYmUgaW1wb3J0YW50IVxuICAgIHJldHVybiB1dGlscy50b0FzY2lpKHBhcmFtLnN0YXRpY1BhcnQoKSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGZvcm1hdCBvdXRwdXQgc3RyaW5nXG4gKlxuICogQG1ldGhvZCBmb3JtYXRPdXRwdXREeW5hbWljQnl0ZXNcbiAqIEBwYXJhbSB7U29saWRpdHlQYXJhbX0gbGVmdC1hbGlnbmVkIGhleCByZXByZXNlbnRhdGlvbiBvZiBzdHJpbmdcbiAqIEByZXR1cm5zIHtTdHJpbmd9IGFzY2lpIHN0cmluZ1xuICovXG52YXIgZm9ybWF0T3V0cHV0RHluYW1pY0J5dGVzID0gZnVuY3Rpb24gKHBhcmFtKSB7XG4gICAgLy8gbGVuZ3RoIG1pZ2h0IGFsc28gYmUgaW1wb3J0YW50IVxuICAgIHJldHVybiB1dGlscy50b0FzY2lpKHBhcmFtLmR5bmFtaWNQYXJ0KCkuc2xpY2UoNjQpKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZm9ybWF0IG91dHB1dCBhZGRyZXNzXG4gKlxuICogQG1ldGhvZCBmb3JtYXRPdXRwdXRBZGRyZXNzXG4gKiBAcGFyYW0ge1NvbGlkaXR5UGFyYW19IHJpZ2h0LWFsaWduZWQgaW5wdXQgYnl0ZXNcbiAqIEByZXR1cm5zIHtTdHJpbmd9IGFkZHJlc3NcbiAqL1xudmFyIGZvcm1hdE91dHB1dEFkZHJlc3MgPSBmdW5jdGlvbiAocGFyYW0pIHtcbiAgICB2YXIgdmFsdWUgPSBwYXJhbS5zdGF0aWNQYXJ0KCk7XG4gICAgcmV0dXJuIFwiMHhcIiArIHZhbHVlLnNsaWNlKHZhbHVlLmxlbmd0aCAtIDQwLCB2YWx1ZS5sZW5ndGgpO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgZm9ybWF0SW5wdXRJbnQ6IGZvcm1hdElucHV0SW50LFxuICAgIGZvcm1hdElucHV0Qnl0ZXM6IGZvcm1hdElucHV0Qnl0ZXMsXG4gICAgZm9ybWF0SW5wdXREeW5hbWljQnl0ZXM6IGZvcm1hdElucHV0RHluYW1pY0J5dGVzLFxuICAgIGZvcm1hdElucHV0Qm9vbDogZm9ybWF0SW5wdXRCb29sLFxuICAgIGZvcm1hdElucHV0UmVhbDogZm9ybWF0SW5wdXRSZWFsLFxuICAgIGZvcm1hdE91dHB1dEludDogZm9ybWF0T3V0cHV0SW50LFxuICAgIGZvcm1hdE91dHB1dFVJbnQ6IGZvcm1hdE91dHB1dFVJbnQsXG4gICAgZm9ybWF0T3V0cHV0UmVhbDogZm9ybWF0T3V0cHV0UmVhbCxcbiAgICBmb3JtYXRPdXRwdXRVUmVhbDogZm9ybWF0T3V0cHV0VVJlYWwsXG4gICAgZm9ybWF0T3V0cHV0Qm9vbDogZm9ybWF0T3V0cHV0Qm9vbCxcbiAgICBmb3JtYXRPdXRwdXRCeXRlczogZm9ybWF0T3V0cHV0Qnl0ZXMsXG4gICAgZm9ybWF0T3V0cHV0RHluYW1pY0J5dGVzOiBmb3JtYXRPdXRwdXREeW5hbWljQnl0ZXMsXG4gICAgZm9ybWF0T3V0cHV0QWRkcmVzczogZm9ybWF0T3V0cHV0QWRkcmVzc1xufTtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSBwYXJhbS5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xuXG4vKipcbiAqIFNvbGlkaXR5UGFyYW0gb2JqZWN0IHByb3RvdHlwZS5cbiAqIFNob3VsZCBiZSB1c2VkIHdoZW4gZW5jb2RpbmcsIGRlY29kaW5nIHNvbGlkaXR5IGJ5dGVzXG4gKi9cbnZhciBTb2xpZGl0eVBhcmFtID0gZnVuY3Rpb24gKHZhbHVlLCBvZmZzZXQpIHtcbiAgICB0aGlzLnZhbHVlID0gdmFsdWUgfHwgJyc7XG4gICAgdGhpcy5vZmZzZXQgPSBvZmZzZXQ7IC8vIG9mZnNldCBpbiBieXRlc1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgdXNlZCB0byBnZXQgbGVuZ3RoIG9mIHBhcmFtcydzIGR5bmFtaWMgcGFydFxuICogXG4gKiBAbWV0aG9kIGR5bmFtaWNQYXJ0TGVuZ3RoXG4gKiBAcmV0dXJucyB7TnVtYmVyfSBsZW5ndGggb2YgZHluYW1pYyBwYXJ0IChpbiBieXRlcylcbiAqL1xuU29saWRpdHlQYXJhbS5wcm90b3R5cGUuZHluYW1pY1BhcnRMZW5ndGggPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuZHluYW1pY1BhcnQoKS5sZW5ndGggLyAyO1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgdXNlZCB0byBjcmVhdGUgY29weSBvZiBzb2xpZGl0eSBwYXJhbSB3aXRoIGRpZmZlcmVudCBvZmZzZXRcbiAqXG4gKiBAbWV0aG9kIHdpdGhPZmZzZXRcbiAqIEBwYXJhbSB7TnVtYmVyfSBvZmZzZXQgbGVuZ3RoIGluIGJ5dGVzXG4gKiBAcmV0dXJucyB7U29saWRpdHlQYXJhbX0gbmV3IHNvbGlkaXR5IHBhcmFtIHdpdGggYXBwbGllZCBvZmZzZXRcbiAqL1xuU29saWRpdHlQYXJhbS5wcm90b3R5cGUud2l0aE9mZnNldCA9IGZ1bmN0aW9uIChvZmZzZXQpIHtcbiAgICByZXR1cm4gbmV3IFNvbGlkaXR5UGFyYW0odGhpcy52YWx1ZSwgb2Zmc2V0KTtcbn07XG5cbi8qKlxuICogVGhpcyBtZXRob2Qgc2hvdWxkIGJlIHVzZWQgdG8gY29tYmluZSBzb2xpZGl0eSBwYXJhbXMgdG9nZXRoZXJcbiAqIGVnLiB3aGVuIGFwcGVuZGluZyBhbiBhcnJheVxuICpcbiAqIEBtZXRob2QgY29tYmluZVxuICogQHBhcmFtIHtTb2xpZGl0eVBhcmFtfSBwYXJhbSB3aXRoIHdoaWNoIHdlIHNob3VsZCBjb21iaW5lXG4gKiBAcGFyYW0ge1NvbGlkaXR5UGFyYW19IHJlc3VsdCBvZiBjb21iaW5hdGlvblxuICovXG5Tb2xpZGl0eVBhcmFtLnByb3RvdHlwZS5jb21iaW5lID0gZnVuY3Rpb24gKHBhcmFtKSB7XG4gICAgcmV0dXJuIG5ldyBTb2xpZGl0eVBhcmFtKHRoaXMudmFsdWUgKyBwYXJhbS52YWx1ZSk7IFxufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgY2FsbGVkIHRvIGNoZWNrIGlmIHBhcmFtIGhhcyBkeW5hbWljIHNpemUuXG4gKiBJZiBpdCBoYXMsIGl0IHJldHVybnMgdHJ1ZSwgb3RoZXJ3aXNlIGZhbHNlXG4gKlxuICogQG1ldGhvZCBpc0R5bmFtaWNcbiAqIEByZXR1cm5zIHtCb29sZWFufVxuICovXG5Tb2xpZGl0eVBhcmFtLnByb3RvdHlwZS5pc0R5bmFtaWMgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMudmFsdWUubGVuZ3RoID4gNjQgfHwgdGhpcy5vZmZzZXQgIT09IHVuZGVmaW5lZDtcbn07XG5cbi8qKlxuICogVGhpcyBtZXRob2Qgc2hvdWxkIGJlIGNhbGxlZCB0byB0cmFuc2Zvcm0gb2Zmc2V0IHRvIGJ5dGVzXG4gKlxuICogQG1ldGhvZCBvZmZzZXRBc0J5dGVzXG4gKiBAcmV0dXJucyB7U3RyaW5nfSBieXRlcyByZXByZXNlbnRhdGlvbiBvZiBvZmZzZXRcbiAqL1xuU29saWRpdHlQYXJhbS5wcm90b3R5cGUub2Zmc2V0QXNCeXRlcyA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gIXRoaXMuaXNEeW5hbWljKCkgPyAnJyA6IHV0aWxzLnBhZExlZnQodXRpbHMudG9Ud29zQ29tcGxlbWVudCh0aGlzLm9mZnNldCkudG9TdHJpbmcoMTYpLCA2NCk7XG59O1xuXG4vKipcbiAqIFRoaXMgbWV0aG9kIHNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IHN0YXRpYyBwYXJ0IG9mIHBhcmFtXG4gKlxuICogQG1ldGhvZCBzdGF0aWNQYXJ0XG4gKiBAcmV0dXJucyB7U3RyaW5nfSBvZmZzZXQgaWYgaXQgaXMgYSBkeW5hbWljIHBhcmFtLCBvdGhlcndpc2UgdmFsdWVcbiAqL1xuU29saWRpdHlQYXJhbS5wcm90b3R5cGUuc3RhdGljUGFydCA9IGZ1bmN0aW9uICgpIHtcbiAgICBpZiAoIXRoaXMuaXNEeW5hbWljKCkpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMudmFsdWU7IFxuICAgIH0gXG4gICAgcmV0dXJuIHRoaXMub2Zmc2V0QXNCeXRlcygpO1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgY2FsbGVkIHRvIGdldCBkeW5hbWljIHBhcnQgb2YgcGFyYW1cbiAqXG4gKiBAbWV0aG9kIGR5bmFtaWNQYXJ0XG4gKiBAcmV0dXJucyB7U3RyaW5nfSByZXR1cm5zIGEgdmFsdWUgaWYgaXQgaXMgYSBkeW5hbWljIHBhcmFtLCBvdGhlcndpc2UgZW1wdHkgc3RyaW5nXG4gKi9cblNvbGlkaXR5UGFyYW0ucHJvdG90eXBlLmR5bmFtaWNQYXJ0ID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB0aGlzLmlzRHluYW1pYygpID8gdGhpcy52YWx1ZSA6ICcnO1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgY2FsbGVkIHRvIGVuY29kZSBwYXJhbVxuICpcbiAqIEBtZXRob2QgZW5jb2RlXG4gKiBAcmV0dXJucyB7U3RyaW5nfVxuICovXG5Tb2xpZGl0eVBhcmFtLnByb3RvdHlwZS5lbmNvZGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuc3RhdGljUGFydCgpICsgdGhpcy5keW5hbWljUGFydCgpO1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgY2FsbGVkIHRvIGVuY29kZSBhcnJheSBvZiBwYXJhbXNcbiAqXG4gKiBAbWV0aG9kIGVuY29kZUxpc3RcbiAqIEBwYXJhbSB7QXJyYXlbU29saWRpdHlQYXJhbV19IHBhcmFtc1xuICogQHJldHVybnMge1N0cmluZ31cbiAqL1xuU29saWRpdHlQYXJhbS5lbmNvZGVMaXN0ID0gZnVuY3Rpb24gKHBhcmFtcykge1xuICAgIFxuICAgIC8vIHVwZGF0aW5nIG9mZnNldHNcbiAgICB2YXIgdG90YWxPZmZzZXQgPSBwYXJhbXMubGVuZ3RoICogMzI7XG4gICAgdmFyIG9mZnNldFBhcmFtcyA9IHBhcmFtcy5tYXAoZnVuY3Rpb24gKHBhcmFtKSB7XG4gICAgICAgIGlmICghcGFyYW0uaXNEeW5hbWljKCkpIHtcbiAgICAgICAgICAgIHJldHVybiBwYXJhbTtcbiAgICAgICAgfVxuICAgICAgICB2YXIgb2Zmc2V0ID0gdG90YWxPZmZzZXQ7XG4gICAgICAgIHRvdGFsT2Zmc2V0ICs9IHBhcmFtLmR5bmFtaWNQYXJ0TGVuZ3RoKCk7XG4gICAgICAgIHJldHVybiBwYXJhbS53aXRoT2Zmc2V0KG9mZnNldCk7XG4gICAgfSk7XG5cbiAgICAvLyBlbmNvZGUgZXZlcnl0aGluZyFcbiAgICByZXR1cm4gb2Zmc2V0UGFyYW1zLnJlZHVjZShmdW5jdGlvbiAocmVzdWx0LCBwYXJhbSkge1xuICAgICAgICByZXR1cm4gcmVzdWx0ICsgcGFyYW0uZHluYW1pY1BhcnQoKTtcbiAgICB9LCBvZmZzZXRQYXJhbXMucmVkdWNlKGZ1bmN0aW9uIChyZXN1bHQsIHBhcmFtKSB7XG4gICAgICAgIHJldHVybiByZXN1bHQgKyBwYXJhbS5zdGF0aWNQYXJ0KCk7XG4gICAgfSwgJycpKTtcbn07XG5cbi8qKlxuICogVGhpcyBtZXRob2Qgc2hvdWxkIGJlIHVzZWQgdG8gZGVjb2RlIHBsYWluIChzdGF0aWMpIHNvbGlkaXR5IHBhcmFtIGF0IGdpdmVuIGluZGV4XG4gKlxuICogQG1ldGhvZCBkZWNvZGVQYXJhbVxuICogQHBhcmFtIHtTdHJpbmd9IGJ5dGVzXG4gKiBAcGFyYW0ge051bWJlcn0gaW5kZXhcbiAqIEByZXR1cm5zIHtTb2xpZGl0eVBhcmFtfVxuICovXG5Tb2xpZGl0eVBhcmFtLmRlY29kZVBhcmFtID0gZnVuY3Rpb24gKGJ5dGVzLCBpbmRleCkge1xuICAgIGluZGV4ID0gaW5kZXggfHwgMDtcbiAgICByZXR1cm4gbmV3IFNvbGlkaXR5UGFyYW0oYnl0ZXMuc3Vic3RyKGluZGV4ICogNjQsIDY0KSk7IFxufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgY2FsbGVkIHRvIGdldCBvZmZzZXQgdmFsdWUgZnJvbSBieXRlcyBhdCBnaXZlbiBpbmRleFxuICpcbiAqIEBtZXRob2QgZ2V0T2Zmc2V0XG4gKiBAcGFyYW0ge1N0cmluZ30gYnl0ZXNcbiAqIEBwYXJhbSB7TnVtYmVyfSBpbmRleFxuICogQHJldHVybnMge051bWJlcn0gb2Zmc2V0IGFzIG51bWJlclxuICovXG52YXIgZ2V0T2Zmc2V0ID0gZnVuY3Rpb24gKGJ5dGVzLCBpbmRleCkge1xuICAgIC8vIHdlIGNhbiBkbyB0aGlzIGNhdXNlIG9mZnNldCBpcyByYXRoZXIgc21hbGxcbiAgICByZXR1cm4gcGFyc2VJbnQoJzB4JyArIGJ5dGVzLnN1YnN0cihpbmRleCAqIDY0LCA2NCkpO1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgY2FsbGVkIHRvIGRlY29kZSBzb2xpZGl0eSBieXRlcyBwYXJhbSBhdCBnaXZlbiBpbmRleFxuICpcbiAqIEBtZXRob2QgZGVjb2RlQnl0ZXNcbiAqIEBwYXJhbSB7U3RyaW5nfSBieXRlc1xuICogQHBhcmFtIHtOdW1iZXJ9IGluZGV4XG4gKiBAcmV0dXJucyB7U29saWRpdHlQYXJhbX1cbiAqL1xuU29saWRpdHlQYXJhbS5kZWNvZGVCeXRlcyA9IGZ1bmN0aW9uIChieXRlcywgaW5kZXgpIHtcbiAgICBpbmRleCA9IGluZGV4IHx8IDA7XG4gICAgLy9UT0RPIGFkZCBzdXBwb3J0IGZvciBzdHJpbmdzIGxvbmdlciB0aGFuIDMyIGJ5dGVzXG4gICAgLy92YXIgbGVuZ3RoID0gcGFyc2VJbnQoJzB4JyArIGJ5dGVzLnN1YnN0cihvZmZzZXQgKiA2NCwgNjQpKTtcblxuICAgIHZhciBvZmZzZXQgPSBnZXRPZmZzZXQoYnl0ZXMsIGluZGV4KTtcblxuICAgIC8vIDIgKiAsIGNhdXNlIHdlIGFsc28gcGFyc2UgbGVuZ3RoXG4gICAgcmV0dXJuIG5ldyBTb2xpZGl0eVBhcmFtKGJ5dGVzLnN1YnN0cihvZmZzZXQgKiAyLCAyICogNjQpLCAwKTtcbn07XG5cbi8qKlxuICogVGhpcyBtZXRob2Qgc2hvdWxkIGJlIHVzZWQgdG8gZGVjb2RlIHNvbGlkaXR5IGFycmF5IGF0IGdpdmVuIGluZGV4XG4gKlxuICogQG1ldGhvZCBkZWNvZGVBcnJheVxuICogQHBhcmFtIHtTdHJpbmd9IGJ5dGVzXG4gKiBAcGFyYW0ge051bWJlcn0gaW5kZXhcbiAqIEByZXR1cm5zIHtTb2xpZGl0eVBhcmFtfVxuICovXG5Tb2xpZGl0eVBhcmFtLmRlY29kZUFycmF5ID0gZnVuY3Rpb24gKGJ5dGVzLCBpbmRleCkge1xuICAgIGluZGV4ID0gaW5kZXggfHwgMDtcbiAgICB2YXIgb2Zmc2V0ID0gZ2V0T2Zmc2V0KGJ5dGVzLCBpbmRleCk7XG4gICAgdmFyIGxlbmd0aCA9IHBhcnNlSW50KCcweCcgKyBieXRlcy5zdWJzdHIob2Zmc2V0ICogMiwgNjQpKTtcbiAgICByZXR1cm4gbmV3IFNvbGlkaXR5UGFyYW0oYnl0ZXMuc3Vic3RyKG9mZnNldCAqIDIsIChsZW5ndGggKyAxKSAqIDY0KSwgMCk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFNvbGlkaXR5UGFyYW07XG5cbiIsIid1c2Ugc3RyaWN0JztcblxuLy8gZ28gZW52IGRvZXNuJ3QgaGF2ZSBhbmQgbmVlZCBYTUxIdHRwUmVxdWVzdFxuaWYgKHR5cGVvZiBYTUxIdHRwUmVxdWVzdCA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICBleHBvcnRzLlhNTEh0dHBSZXF1ZXN0ID0ge307XG59IGVsc2Uge1xuICAgIGV4cG9ydHMuWE1MSHR0cFJlcXVlc3QgPSBYTUxIdHRwUmVxdWVzdDsgLy8ganNoaW50IGlnbm9yZTpsaW5lXG59XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIEBmaWxlIGNvbmZpZy5qc1xuICogQGF1dGhvcnM6XG4gKiAgIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbi8qKlxuICogVXRpbHNcbiAqIFxuICogQG1vZHVsZSB1dGlsc1xuICovXG5cbi8qKlxuICogVXRpbGl0eSBmdW5jdGlvbnNcbiAqIFxuICogQGNsYXNzIFt1dGlsc10gY29uZmlnXG4gKiBAY29uc3RydWN0b3JcbiAqL1xuXG4vLy8gcmVxdWlyZWQgdG8gZGVmaW5lIEVUSF9CSUdOVU1CRVJfUk9VTkRJTkdfTU9ERVxudmFyIEJpZ051bWJlciA9IHJlcXVpcmUoJ2JpZ251bWJlci5qcycpO1xuXG52YXIgRVRIX1VOSVRTID0gW1xuICAgICd3ZWknLFxuICAgICdrd2VpJyxcbiAgICAnTXdlaScsXG4gICAgJ0d3ZWknLFxuICAgICdzemFibycsXG4gICAgJ2Zpbm5leScsXG4gICAgJ2ZlbXRvZXRoZXInLFxuICAgICdwaWNvZXRoZXInLFxuICAgICduYW5vZXRoZXInLFxuICAgICdtaWNyb2V0aGVyJyxcbiAgICAnbWlsbGlldGhlcicsXG4gICAgJ25hbm8nLFxuICAgICdtaWNybycsXG4gICAgJ21pbGxpJyxcbiAgICAnZXRoZXInLFxuICAgICdncmFuZCcsXG4gICAgJ01ldGhlcicsXG4gICAgJ0dldGhlcicsXG4gICAgJ1RldGhlcicsXG4gICAgJ1BldGhlcicsXG4gICAgJ0VldGhlcicsXG4gICAgJ1pldGhlcicsXG4gICAgJ1lldGhlcicsXG4gICAgJ05ldGhlcicsXG4gICAgJ0RldGhlcicsXG4gICAgJ1ZldGhlcicsXG4gICAgJ1VldGhlcidcbl07XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIEVUSF9QQURESU5HOiAzMixcbiAgICBFVEhfU0lHTkFUVVJFX0xFTkdUSDogNCxcbiAgICBFVEhfVU5JVFM6IEVUSF9VTklUUyxcbiAgICBFVEhfQklHTlVNQkVSX1JPVU5ESU5HX01PREU6IHsgUk9VTkRJTkdfTU9ERTogQmlnTnVtYmVyLlJPVU5EX0RPV04gfSxcbiAgICBFVEhfUE9MTElOR19USU1FT1VUOiAxMDAwLFxuICAgIGRlZmF1bHRCbG9jazogJ2xhdGVzdCcsXG4gICAgZGVmYXVsdEFjY291bnQ6IHVuZGVmaW5lZFxufTtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSBzaGEzLmpzXG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciB1dGlscyA9IHJlcXVpcmUoJy4vdXRpbHMnKTtcbnZhciBzaGEzID0gcmVxdWlyZSgnY3J5cHRvLWpzL3NoYTMnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoc3RyLCBpc05ldykge1xuICAgIGlmIChzdHIuc3Vic3RyKDAsIDIpID09PSAnMHgnICYmICFpc05ldykge1xuICAgICAgICBjb25zb2xlLndhcm4oJ3JlcXVpcmVtZW50IG9mIHVzaW5nIHdlYjMuZnJvbUFzY2lpIGJlZm9yZSBzaGEzIGlzIGRlcHJlY2F0ZWQnKTtcbiAgICAgICAgY29uc29sZS53YXJuKCduZXcgdXNhZ2U6IFxcJ3dlYjMuc2hhMyhcImhlbGxvXCIpXFwnJyk7XG4gICAgICAgIGNvbnNvbGUud2Fybignc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9ldGhlcmV1bS93ZWIzLmpzL3B1bGwvMjA1Jyk7XG4gICAgICAgIGNvbnNvbGUud2FybignaWYgeW91IG5lZWQgdG8gaGFzaCBoZXggdmFsdWUsIHlvdSBjYW4gZG8gXFwnc2hhMyhcIjB4ZmZmXCIsIHRydWUpXFwnJyk7XG4gICAgICAgIHN0ciA9IHV0aWxzLnRvQXNjaWkoc3RyKTtcbiAgICB9XG5cbiAgICByZXR1cm4gc2hhMyhzdHIsIHtcbiAgICAgICAgb3V0cHV0TGVuZ3RoOiAyNTZcbiAgICB9KS50b1N0cmluZygpO1xufTtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSB1dGlscy5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG4vKipcbiAqIFV0aWxzXG4gKiBcbiAqIEBtb2R1bGUgdXRpbHNcbiAqL1xuXG4vKipcbiAqIFV0aWxpdHkgZnVuY3Rpb25zXG4gKiBcbiAqIEBjbGFzcyBbdXRpbHNdIHV0aWxzXG4gKiBAY29uc3RydWN0b3JcbiAqL1xuXG52YXIgQmlnTnVtYmVyID0gcmVxdWlyZSgnYmlnbnVtYmVyLmpzJyk7XG5cbnZhciB1bml0TWFwID0ge1xuICAgICd3ZWknOiAgICAgICAgICAnMScsXG4gICAgJ2t3ZWknOiAgICAgICAgICcxMDAwJyxcbiAgICAnYWRhJzogICAgICAgICAgJzEwMDAnLFxuICAgICdmZW10b2V0aGVyJzogICAnMTAwMCcsXG4gICAgJ213ZWknOiAgICAgICAgICcxMDAwMDAwJyxcbiAgICAnYmFiYmFnZSc6ICAgICAgJzEwMDAwMDAnLFxuICAgICdwaWNvZXRoZXInOiAgICAnMTAwMDAwMCcsXG4gICAgJ2d3ZWknOiAgICAgICAgICcxMDAwMDAwMDAwJyxcbiAgICAnc2hhbm5vbic6ICAgICAgJzEwMDAwMDAwMDAnLFxuICAgICduYW5vZXRoZXInOiAgICAnMTAwMDAwMDAwMCcsXG4gICAgJ25hbm8nOiAgICAgICAgICcxMDAwMDAwMDAwJyxcbiAgICAnc3phYm8nOiAgICAgICAgJzEwMDAwMDAwMDAwMDAnLFxuICAgICdtaWNyb2V0aGVyJzogICAnMTAwMDAwMDAwMDAwMCcsXG4gICAgJ21pY3JvJzogICAgICAgICcxMDAwMDAwMDAwMDAwJyxcbiAgICAnZmlubmV5JzogICAgICAgJzEwMDAwMDAwMDAwMDAwMDAnLFxuICAgICdtaWxsaWV0aGVyJzogICAgJzEwMDAwMDAwMDAwMDAwMDAnLFxuICAgICdtaWxsaSc6ICAgICAgICAgJzEwMDAwMDAwMDAwMDAwMDAnLFxuICAgICdldGhlcic6ICAgICAgICAnMTAwMDAwMDAwMDAwMDAwMDAwMCcsXG4gICAgJ2tldGhlcic6ICAgICAgICcxMDAwMDAwMDAwMDAwMDAwMDAwMDAwJyxcbiAgICAnZ3JhbmQnOiAgICAgICAgJzEwMDAwMDAwMDAwMDAwMDAwMDAwMDAnLFxuICAgICdlaW5zdGVpbic6ICAgICAnMTAwMDAwMDAwMDAwMDAwMDAwMDAwMCcsXG4gICAgJ21ldGhlcic6ICAgICAgICcxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwJyxcbiAgICAnZ2V0aGVyJzogICAgICAgJzEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAnLFxuICAgICd0ZXRoZXInOiAgICAgICAnMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCdcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBwYWQgc3RyaW5nIHRvIGV4cGVjdGVkIGxlbmd0aFxuICpcbiAqIEBtZXRob2QgcGFkTGVmdFxuICogQHBhcmFtIHtTdHJpbmd9IHN0cmluZyB0byBiZSBwYWRkZWRcbiAqIEBwYXJhbSB7TnVtYmVyfSBjaGFyYWN0ZXJzIHRoYXQgcmVzdWx0IHN0cmluZyBzaG91bGQgaGF2ZVxuICogQHBhcmFtIHtTdHJpbmd9IHNpZ24sIGJ5IGRlZmF1bHQgMFxuICogQHJldHVybnMge1N0cmluZ30gcmlnaHQgYWxpZ25lZCBzdHJpbmdcbiAqL1xudmFyIHBhZExlZnQgPSBmdW5jdGlvbiAoc3RyaW5nLCBjaGFycywgc2lnbikge1xuICAgIHJldHVybiBuZXcgQXJyYXkoY2hhcnMgLSBzdHJpbmcubGVuZ3RoICsgMSkuam9pbihzaWduID8gc2lnbiA6IFwiMFwiKSArIHN0cmluZztcbn07XG5cbi8qKiBcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IHN0aW5nIGZyb20gaXQncyBoZXggcmVwcmVzZW50YXRpb25cbiAqXG4gKiBAbWV0aG9kIHRvQXNjaWlcbiAqIEBwYXJhbSB7U3RyaW5nfSBzdHJpbmcgaW4gaGV4XG4gKiBAcmV0dXJucyB7U3RyaW5nfSBhc2NpaSBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgaGV4IHZhbHVlXG4gKi9cbnZhciB0b0FzY2lpID0gZnVuY3Rpb24oaGV4KSB7XG4vLyBGaW5kIHRlcm1pbmF0aW9uXG4gICAgdmFyIHN0ciA9IFwiXCI7XG4gICAgdmFyIGkgPSAwLCBsID0gaGV4Lmxlbmd0aDtcbiAgICBpZiAoaGV4LnN1YnN0cmluZygwLCAyKSA9PT0gJzB4Jykge1xuICAgICAgICBpID0gMjtcbiAgICB9XG4gICAgZm9yICg7IGkgPCBsOyBpKz0yKSB7XG4gICAgICAgIHZhciBjb2RlID0gcGFyc2VJbnQoaGV4LnN1YnN0cihpLCAyKSwgMTYpO1xuICAgICAgICBpZiAoY29kZSA9PT0gMCkge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cblxuICAgICAgICBzdHIgKz0gU3RyaW5nLmZyb21DaGFyQ29kZShjb2RlKTtcbiAgICB9XG5cbiAgICByZXR1cm4gc3RyO1xufTtcbiAgICBcbi8qKlxuICogU2hvbGQgYmUgY2FsbGVkIHRvIGdldCBoZXggcmVwcmVzZW50YXRpb24gKHByZWZpeGVkIGJ5IDB4KSBvZiBhc2NpaSBzdHJpbmcgXG4gKlxuICogQG1ldGhvZCB0b0hleE5hdGl2ZVxuICogQHBhcmFtIHtTdHJpbmd9IHN0cmluZ1xuICogQHJldHVybnMge1N0cmluZ30gaGV4IHJlcHJlc2VudGF0aW9uIG9mIGlucHV0IHN0cmluZ1xuICovXG52YXIgdG9IZXhOYXRpdmUgPSBmdW5jdGlvbihzdHIpIHtcbiAgICB2YXIgaGV4ID0gXCJcIjtcbiAgICBmb3IodmFyIGkgPSAwOyBpIDwgc3RyLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIHZhciBuID0gc3RyLmNoYXJDb2RlQXQoaSkudG9TdHJpbmcoMTYpO1xuICAgICAgICBoZXggKz0gbi5sZW5ndGggPCAyID8gJzAnICsgbiA6IG47XG4gICAgfVxuXG4gICAgcmV0dXJuIGhleDtcbn07XG5cbi8qKlxuICogU2hvbGQgYmUgY2FsbGVkIHRvIGdldCBoZXggcmVwcmVzZW50YXRpb24gKHByZWZpeGVkIGJ5IDB4KSBvZiBhc2NpaSBzdHJpbmcgXG4gKlxuICogQG1ldGhvZCBmcm9tQXNjaWlcbiAqIEBwYXJhbSB7U3RyaW5nfSBzdHJpbmdcbiAqIEBwYXJhbSB7TnVtYmVyfSBvcHRpb25hbCBwYWRkaW5nXG4gKiBAcmV0dXJucyB7U3RyaW5nfSBoZXggcmVwcmVzZW50YXRpb24gb2YgaW5wdXQgc3RyaW5nXG4gKi9cbnZhciBmcm9tQXNjaWkgPSBmdW5jdGlvbihzdHIsIHBhZCkge1xuICAgIHBhZCA9IHBhZCA9PT0gdW5kZWZpbmVkID8gMCA6IHBhZDtcbiAgICB2YXIgaGV4ID0gdG9IZXhOYXRpdmUoc3RyKTtcbiAgICB3aGlsZSAoaGV4Lmxlbmd0aCA8IHBhZCoyKVxuICAgICAgICBoZXggKz0gXCIwMFwiO1xuICAgIHJldHVybiBcIjB4XCIgKyBoZXg7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGNyZWF0ZSBmdWxsIGZ1bmN0aW9uL2V2ZW50IG5hbWUgZnJvbSBqc29uIGFiaVxuICpcbiAqIEBtZXRob2QgdHJhbnNmb3JtVG9GdWxsTmFtZVxuICogQHBhcmFtIHtPYmplY3R9IGpzb24tYWJpXG4gKiBAcmV0dXJuIHtTdHJpbmd9IGZ1bGwgZm5jdGlvbi9ldmVudCBuYW1lXG4gKi9cbnZhciB0cmFuc2Zvcm1Ub0Z1bGxOYW1lID0gZnVuY3Rpb24gKGpzb24pIHtcbiAgICBpZiAoanNvbi5uYW1lLmluZGV4T2YoJygnKSAhPT0gLTEpIHtcbiAgICAgICAgcmV0dXJuIGpzb24ubmFtZTtcbiAgICB9XG5cbiAgICB2YXIgdHlwZU5hbWUgPSBqc29uLmlucHV0cy5tYXAoZnVuY3Rpb24oaSl7cmV0dXJuIGkudHlwZTsgfSkuam9pbigpO1xuICAgIHJldHVybiBqc29uLm5hbWUgKyAnKCcgKyB0eXBlTmFtZSArICcpJztcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBnZXQgZGlzcGxheSBuYW1lIG9mIGNvbnRyYWN0IGZ1bmN0aW9uXG4gKiBcbiAqIEBtZXRob2QgZXh0cmFjdERpc3BsYXlOYW1lXG4gKiBAcGFyYW0ge1N0cmluZ30gbmFtZSBvZiBmdW5jdGlvbi9ldmVudFxuICogQHJldHVybnMge1N0cmluZ30gZGlzcGxheSBuYW1lIGZvciBmdW5jdGlvbi9ldmVudCBlZy4gbXVsdGlwbHkodWludDI1NikgLT4gbXVsdGlwbHlcbiAqL1xudmFyIGV4dHJhY3REaXNwbGF5TmFtZSA9IGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgdmFyIGxlbmd0aCA9IG5hbWUuaW5kZXhPZignKCcpOyBcbiAgICByZXR1cm4gbGVuZ3RoICE9PSAtMSA/IG5hbWUuc3Vic3RyKDAsIGxlbmd0aCkgOiBuYW1lO1xufTtcblxuLy8vIEByZXR1cm5zIG92ZXJsb2FkZWQgcGFydCBvZiBmdW5jdGlvbi9ldmVudCBuYW1lXG52YXIgZXh0cmFjdFR5cGVOYW1lID0gZnVuY3Rpb24gKG5hbWUpIHtcbiAgICAvLy8gVE9ETzogbWFrZSBpdCBpbnZ1bG5lcmFibGVcbiAgICB2YXIgbGVuZ3RoID0gbmFtZS5pbmRleE9mKCcoJyk7XG4gICAgcmV0dXJuIGxlbmd0aCAhPT0gLTEgPyBuYW1lLnN1YnN0cihsZW5ndGggKyAxLCBuYW1lLmxlbmd0aCAtIDEgLSAobGVuZ3RoICsgMSkpLnJlcGxhY2UoJyAnLCAnJykgOiBcIlwiO1xufTtcblxuLyoqXG4gKiBDb252ZXJ0cyB2YWx1ZSB0byBpdCdzIGRlY2ltYWwgcmVwcmVzZW50YXRpb24gaW4gc3RyaW5nXG4gKlxuICogQG1ldGhvZCB0b0RlY2ltYWxcbiAqIEBwYXJhbSB7U3RyaW5nfE51bWJlcnxCaWdOdW1iZXJ9XG4gKiBAcmV0dXJuIHtTdHJpbmd9XG4gKi9cbnZhciB0b0RlY2ltYWwgPSBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICByZXR1cm4gdG9CaWdOdW1iZXIodmFsdWUpLnRvTnVtYmVyKCk7XG59O1xuXG4vKipcbiAqIENvbnZlcnRzIHZhbHVlIHRvIGl0J3MgaGV4IHJlcHJlc2VudGF0aW9uXG4gKlxuICogQG1ldGhvZCBmcm9tRGVjaW1hbFxuICogQHBhcmFtIHtTdHJpbmd8TnVtYmVyfEJpZ051bWJlcn1cbiAqIEByZXR1cm4ge1N0cmluZ31cbiAqL1xudmFyIGZyb21EZWNpbWFsID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgdmFyIG51bWJlciA9IHRvQmlnTnVtYmVyKHZhbHVlKTtcbiAgICB2YXIgcmVzdWx0ID0gbnVtYmVyLnRvU3RyaW5nKDE2KTtcblxuICAgIHJldHVybiBudW1iZXIubGVzc1RoYW4oMCkgPyAnLTB4JyArIHJlc3VsdC5zdWJzdHIoMSkgOiAnMHgnICsgcmVzdWx0O1xufTtcblxuLyoqXG4gKiBBdXRvIGNvbnZlcnRzIGFueSBnaXZlbiB2YWx1ZSBpbnRvIGl0J3MgaGV4IHJlcHJlc2VudGF0aW9uLlxuICpcbiAqIEFuZCBldmVuIHN0cmluZ2lmeXMgb2JqZWN0cyBiZWZvcmUuXG4gKlxuICogQG1ldGhvZCB0b0hleFxuICogQHBhcmFtIHtTdHJpbmd8TnVtYmVyfEJpZ051bWJlcnxPYmplY3R9XG4gKiBAcmV0dXJuIHtTdHJpbmd9XG4gKi9cbnZhciB0b0hleCA9IGZ1bmN0aW9uICh2YWwpIHtcbiAgICAvKmpzaGludCBtYXhjb21wbGV4aXR5OjcgKi9cblxuICAgIGlmIChpc0Jvb2xlYW4odmFsKSlcbiAgICAgICAgcmV0dXJuIGZyb21EZWNpbWFsKCt2YWwpO1xuXG4gICAgaWYgKGlzQmlnTnVtYmVyKHZhbCkpXG4gICAgICAgIHJldHVybiBmcm9tRGVjaW1hbCh2YWwpO1xuXG4gICAgaWYgKGlzT2JqZWN0KHZhbCkpXG4gICAgICAgIHJldHVybiBmcm9tQXNjaWkoSlNPTi5zdHJpbmdpZnkodmFsKSk7XG5cbiAgICAvLyBpZiBpdHMgYSBuZWdhdGl2ZSBudW1iZXIsIHBhc3MgaXQgdGhyb3VnaCBmcm9tRGVjaW1hbFxuICAgIGlmIChpc1N0cmluZyh2YWwpKSB7XG4gICAgICAgIGlmICh2YWwuaW5kZXhPZignLTB4JykgPT09IDApXG4gICAgICAgICAgIHJldHVybiBmcm9tRGVjaW1hbCh2YWwpO1xuICAgICAgICBlbHNlIGlmICghaXNGaW5pdGUodmFsKSlcbiAgICAgICAgICAgIHJldHVybiBmcm9tQXNjaWkodmFsKTtcbiAgICB9XG5cbiAgICByZXR1cm4gZnJvbURlY2ltYWwodmFsKTtcbn07XG5cbi8qKlxuICogUmV0dXJucyB2YWx1ZSBvZiB1bml0IGluIFdlaVxuICpcbiAqIEBtZXRob2QgZ2V0VmFsdWVPZlVuaXRcbiAqIEBwYXJhbSB7U3RyaW5nfSB1bml0IHRoZSB1bml0IHRvIGNvbnZlcnQgdG8sIGRlZmF1bHQgZXRoZXJcbiAqIEByZXR1cm5zIHtCaWdOdW1iZXJ9IHZhbHVlIG9mIHRoZSB1bml0IChpbiBXZWkpXG4gKiBAdGhyb3dzIGVycm9yIGlmIHRoZSB1bml0IGlzIG5vdCBjb3JyZWN0OndcbiAqL1xudmFyIGdldFZhbHVlT2ZVbml0ID0gZnVuY3Rpb24gKHVuaXQpIHtcbiAgICB1bml0ID0gdW5pdCA/IHVuaXQudG9Mb3dlckNhc2UoKSA6ICdldGhlcic7XG4gICAgdmFyIHVuaXRWYWx1ZSA9IHVuaXRNYXBbdW5pdF07XG4gICAgaWYgKHVuaXRWYWx1ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignVGhpcyB1bml0IGRvZXNuXFwndCBleGlzdHMsIHBsZWFzZSB1c2UgdGhlIG9uZSBvZiB0aGUgZm9sbG93aW5nIHVuaXRzJyArIEpTT04uc3RyaW5naWZ5KHVuaXRNYXAsIG51bGwsIDIpKTtcbiAgICB9XG4gICAgcmV0dXJuIG5ldyBCaWdOdW1iZXIodW5pdFZhbHVlLCAxMCk7XG59O1xuXG4vKipcbiAqIFRha2VzIGEgbnVtYmVyIG9mIHdlaSBhbmQgY29udmVydHMgaXQgdG8gYW55IG90aGVyIGV0aGVyIHVuaXQuXG4gKlxuICogUG9zc2libGUgdW5pdHMgYXJlOlxuICogICBTSSBTaG9ydCAgIFNJIEZ1bGwgICAgICAgIEVmZmlneSAgICAgICBPdGhlclxuICogLSBrd2VpICAgICAgIGZlbXRvZXRoZXIgICAgIGFkYVxuICogLSBtd2VpICAgICAgIHBpY29ldGhlciAgICAgIGJhYmJhZ2VcbiAqIC0gZ3dlaSAgICAgICBuYW5vZXRoZXIgICAgICBzaGFubm9uICAgICAgbmFub1xuICogLSAtLSAgICAgICAgIG1pY3JvZXRoZXIgICAgIHN6YWJvICAgICAgICBtaWNyb1xuICogLSAtLSAgICAgICAgIG1pbGxpZXRoZXIgICAgIGZpbm5leSAgICAgICBtaWxsaVxuICogLSBldGhlciAgICAgIC0tICAgICAgICAgICAgIC0tXG4gKiAtIGtldGhlciAgICAgICAgICAgICAgICAgICAgZWluc3RlaW4gICAgIGdyYW5kIFxuICogLSBtZXRoZXJcbiAqIC0gZ2V0aGVyXG4gKiAtIHRldGhlclxuICpcbiAqIEBtZXRob2QgZnJvbVdlaVxuICogQHBhcmFtIHtOdW1iZXJ8U3RyaW5nfSBudW1iZXIgY2FuIGJlIGEgbnVtYmVyLCBudW1iZXIgc3RyaW5nIG9yIGEgSEVYIG9mIGEgZGVjaW1hbFxuICogQHBhcmFtIHtTdHJpbmd9IHVuaXQgdGhlIHVuaXQgdG8gY29udmVydCB0bywgZGVmYXVsdCBldGhlclxuICogQHJldHVybiB7U3RyaW5nfE9iamVjdH0gV2hlbiBnaXZlbiBhIEJpZ051bWJlciBvYmplY3QgaXQgcmV0dXJucyBvbmUgYXMgd2VsbCwgb3RoZXJ3aXNlIGEgbnVtYmVyXG4qL1xudmFyIGZyb21XZWkgPSBmdW5jdGlvbihudW1iZXIsIHVuaXQpIHtcbiAgICB2YXIgcmV0dXJuVmFsdWUgPSB0b0JpZ051bWJlcihudW1iZXIpLmRpdmlkZWRCeShnZXRWYWx1ZU9mVW5pdCh1bml0KSk7XG5cbiAgICByZXR1cm4gaXNCaWdOdW1iZXIobnVtYmVyKSA/IHJldHVyblZhbHVlIDogcmV0dXJuVmFsdWUudG9TdHJpbmcoMTApOyBcbn07XG5cbi8qKlxuICogVGFrZXMgYSBudW1iZXIgb2YgYSB1bml0IGFuZCBjb252ZXJ0cyBpdCB0byB3ZWkuXG4gKlxuICogUG9zc2libGUgdW5pdHMgYXJlOlxuICogICBTSSBTaG9ydCAgIFNJIEZ1bGwgICAgICAgIEVmZmlneSAgICAgICBPdGhlclxuICogLSBrd2VpICAgICAgIGZlbXRvZXRoZXIgICAgIGFkYVxuICogLSBtd2VpICAgICAgIHBpY29ldGhlciAgICAgIGJhYmJhZ2UgICAgICAgXG4gKiAtIGd3ZWkgICAgICAgbmFub2V0aGVyICAgICAgc2hhbm5vbiAgICAgIG5hbm9cbiAqIC0gLS0gICAgICAgICBtaWNyb2V0aGVyICAgICBzemFibyAgICAgICAgbWljcm9cbiAqIC0gLS0gICAgICAgICBtaWxsaWV0aGVyICAgICBmaW5uZXkgICAgICAgbWlsbGlcbiAqIC0gZXRoZXIgICAgICAtLSAgICAgICAgICAgICAtLVxuICogLSBrZXRoZXIgICAgICAgICAgICAgICAgICAgIGVpbnN0ZWluICAgICBncmFuZCBcbiAqIC0gbWV0aGVyXG4gKiAtIGdldGhlclxuICogLSB0ZXRoZXJcbiAqXG4gKiBAbWV0aG9kIHRvV2VpXG4gKiBAcGFyYW0ge051bWJlcnxTdHJpbmd8QmlnTnVtYmVyfSBudW1iZXIgY2FuIGJlIGEgbnVtYmVyLCBudW1iZXIgc3RyaW5nIG9yIGEgSEVYIG9mIGEgZGVjaW1hbFxuICogQHBhcmFtIHtTdHJpbmd9IHVuaXQgdGhlIHVuaXQgdG8gY29udmVydCBmcm9tLCBkZWZhdWx0IGV0aGVyXG4gKiBAcmV0dXJuIHtTdHJpbmd8T2JqZWN0fSBXaGVuIGdpdmVuIGEgQmlnTnVtYmVyIG9iamVjdCBpdCByZXR1cm5zIG9uZSBhcyB3ZWxsLCBvdGhlcndpc2UgYSBudW1iZXJcbiovXG52YXIgdG9XZWkgPSBmdW5jdGlvbihudW1iZXIsIHVuaXQpIHtcbiAgICB2YXIgcmV0dXJuVmFsdWUgPSB0b0JpZ051bWJlcihudW1iZXIpLnRpbWVzKGdldFZhbHVlT2ZVbml0KHVuaXQpKTtcblxuICAgIHJldHVybiBpc0JpZ051bWJlcihudW1iZXIpID8gcmV0dXJuVmFsdWUgOiByZXR1cm5WYWx1ZS50b1N0cmluZygxMCk7IFxufTtcblxuLyoqXG4gKiBUYWtlcyBhbiBpbnB1dCBhbmQgdHJhbnNmb3JtcyBpdCBpbnRvIGFuIGJpZ251bWJlclxuICpcbiAqIEBtZXRob2QgdG9CaWdOdW1iZXJcbiAqIEBwYXJhbSB7TnVtYmVyfFN0cmluZ3xCaWdOdW1iZXJ9IGEgbnVtYmVyLCBzdHJpbmcsIEhFWCBzdHJpbmcgb3IgQmlnTnVtYmVyXG4gKiBAcmV0dXJuIHtCaWdOdW1iZXJ9IEJpZ051bWJlclxuKi9cbnZhciB0b0JpZ051bWJlciA9IGZ1bmN0aW9uKG51bWJlcikge1xuICAgIC8qanNoaW50IG1heGNvbXBsZXhpdHk6NSAqL1xuICAgIG51bWJlciA9IG51bWJlciB8fCAwO1xuICAgIGlmIChpc0JpZ051bWJlcihudW1iZXIpKVxuICAgICAgICByZXR1cm4gbnVtYmVyO1xuXG4gICAgaWYgKGlzU3RyaW5nKG51bWJlcikgJiYgKG51bWJlci5pbmRleE9mKCcweCcpID09PSAwIHx8IG51bWJlci5pbmRleE9mKCctMHgnKSA9PT0gMCkpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBCaWdOdW1iZXIobnVtYmVyLnJlcGxhY2UoJzB4JywnJyksIDE2KTtcbiAgICB9XG4gICBcbiAgICByZXR1cm4gbmV3IEJpZ051bWJlcihudW1iZXIudG9TdHJpbmcoMTApLCAxMCk7XG59O1xuXG4vKipcbiAqIFRha2VzIGFuZCBpbnB1dCB0cmFuc2Zvcm1zIGl0IGludG8gYmlnbnVtYmVyIGFuZCBpZiBpdCBpcyBuZWdhdGl2ZSB2YWx1ZSwgaW50byB0d28ncyBjb21wbGVtZW50XG4gKlxuICogQG1ldGhvZCB0b1R3b3NDb21wbGVtZW50XG4gKiBAcGFyYW0ge051bWJlcnxTdHJpbmd8QmlnTnVtYmVyfVxuICogQHJldHVybiB7QmlnTnVtYmVyfVxuICovXG52YXIgdG9Ud29zQ29tcGxlbWVudCA9IGZ1bmN0aW9uIChudW1iZXIpIHtcbiAgICB2YXIgYmlnTnVtYmVyID0gdG9CaWdOdW1iZXIobnVtYmVyKTtcbiAgICBpZiAoYmlnTnVtYmVyLmxlc3NUaGFuKDApKSB7XG4gICAgICAgIHJldHVybiBuZXcgQmlnTnVtYmVyKFwiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZlwiLCAxNikucGx1cyhiaWdOdW1iZXIpLnBsdXMoMSk7XG4gICAgfVxuICAgIHJldHVybiBiaWdOdW1iZXI7XG59O1xuXG4vKipcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gc3RyaW5nIGlzIHN0cmljdGx5IGFuIGFkZHJlc3NcbiAqXG4gKiBAbWV0aG9kIGlzU3RyaWN0QWRkcmVzc1xuICogQHBhcmFtIHtTdHJpbmd9IGFkZHJlc3MgdGhlIGdpdmVuIEhFWCBhZHJlc3NcbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4qL1xudmFyIGlzU3RyaWN0QWRkcmVzcyA9IGZ1bmN0aW9uIChhZGRyZXNzKSB7XG4gICAgcmV0dXJuIC9eMHhbMC05YS1mXXs0MH0kLy50ZXN0KGFkZHJlc3MpO1xufTtcblxuLyoqXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIHN0cmluZyBpcyBhbiBhZGRyZXNzXG4gKlxuICogQG1ldGhvZCBpc0FkZHJlc3NcbiAqIEBwYXJhbSB7U3RyaW5nfSBhZGRyZXNzIHRoZSBnaXZlbiBIRVggYWRyZXNzXG4gKiBAcmV0dXJuIHtCb29sZWFufVxuKi9cbnZhciBpc0FkZHJlc3MgPSBmdW5jdGlvbiAoYWRkcmVzcykge1xuICAgIHJldHVybiAvXigweCk/WzAtOWEtZl17NDB9JC8udGVzdChhZGRyZXNzKTtcbn07XG5cbi8qKlxuICogVHJhbnNmb3JtcyBnaXZlbiBzdHJpbmcgdG8gdmFsaWQgMjAgYnl0ZXMtbGVuZ3RoIGFkZHJlcyB3aXRoIDB4IHByZWZpeFxuICpcbiAqIEBtZXRob2QgdG9BZGRyZXNzXG4gKiBAcGFyYW0ge1N0cmluZ30gYWRkcmVzc1xuICogQHJldHVybiB7U3RyaW5nfSBmb3JtYXR0ZWQgYWRkcmVzc1xuICovXG52YXIgdG9BZGRyZXNzID0gZnVuY3Rpb24gKGFkZHJlc3MpIHtcbiAgICBpZiAoaXNTdHJpY3RBZGRyZXNzKGFkZHJlc3MpKSB7XG4gICAgICAgIHJldHVybiBhZGRyZXNzO1xuICAgIH1cbiAgICBcbiAgICBpZiAoL15bMC05YS1mXXs0MH0kLy50ZXN0KGFkZHJlc3MpKSB7XG4gICAgICAgIHJldHVybiAnMHgnICsgYWRkcmVzcztcbiAgICB9XG5cbiAgICByZXR1cm4gJzB4JyArIHBhZExlZnQodG9IZXgoYWRkcmVzcykuc3Vic3RyKDIpLCA0MCk7XG59O1xuXG5cbi8qKlxuICogUmV0dXJucyB0cnVlIGlmIG9iamVjdCBpcyBCaWdOdW1iZXIsIG90aGVyd2lzZSBmYWxzZVxuICpcbiAqIEBtZXRob2QgaXNCaWdOdW1iZXJcbiAqIEBwYXJhbSB7T2JqZWN0fVxuICogQHJldHVybiB7Qm9vbGVhbn0gXG4gKi9cbnZhciBpc0JpZ051bWJlciA9IGZ1bmN0aW9uIChvYmplY3QpIHtcbiAgICByZXR1cm4gb2JqZWN0IGluc3RhbmNlb2YgQmlnTnVtYmVyIHx8XG4gICAgICAgIChvYmplY3QgJiYgb2JqZWN0LmNvbnN0cnVjdG9yICYmIG9iamVjdC5jb25zdHJ1Y3Rvci5uYW1lID09PSAnQmlnTnVtYmVyJyk7XG59O1xuXG4vKipcbiAqIFJldHVybnMgdHJ1ZSBpZiBvYmplY3QgaXMgc3RyaW5nLCBvdGhlcndpc2UgZmFsc2VcbiAqIFxuICogQG1ldGhvZCBpc1N0cmluZ1xuICogQHBhcmFtIHtPYmplY3R9XG4gKiBAcmV0dXJuIHtCb29sZWFufVxuICovXG52YXIgaXNTdHJpbmcgPSBmdW5jdGlvbiAob2JqZWN0KSB7XG4gICAgcmV0dXJuIHR5cGVvZiBvYmplY3QgPT09ICdzdHJpbmcnIHx8XG4gICAgICAgIChvYmplY3QgJiYgb2JqZWN0LmNvbnN0cnVjdG9yICYmIG9iamVjdC5jb25zdHJ1Y3Rvci5uYW1lID09PSAnU3RyaW5nJyk7XG59O1xuXG4vKipcbiAqIFJldHVybnMgdHJ1ZSBpZiBvYmplY3QgaXMgZnVuY3Rpb24sIG90aGVyd2lzZSBmYWxzZVxuICpcbiAqIEBtZXRob2QgaXNGdW5jdGlvblxuICogQHBhcmFtIHtPYmplY3R9XG4gKiBAcmV0dXJuIHtCb29sZWFufVxuICovXG52YXIgaXNGdW5jdGlvbiA9IGZ1bmN0aW9uIChvYmplY3QpIHtcbiAgICByZXR1cm4gdHlwZW9mIG9iamVjdCA9PT0gJ2Z1bmN0aW9uJztcbn07XG5cbi8qKlxuICogUmV0dXJucyB0cnVlIGlmIG9iamVjdCBpcyBPYmpldCwgb3RoZXJ3aXNlIGZhbHNlXG4gKlxuICogQG1ldGhvZCBpc09iamVjdFxuICogQHBhcmFtIHtPYmplY3R9XG4gKiBAcmV0dXJuIHtCb29sZWFufVxuICovXG52YXIgaXNPYmplY3QgPSBmdW5jdGlvbiAob2JqZWN0KSB7XG4gICAgcmV0dXJuIHR5cGVvZiBvYmplY3QgPT09ICdvYmplY3QnO1xufTtcblxuLyoqXG4gKiBSZXR1cm5zIHRydWUgaWYgb2JqZWN0IGlzIGJvb2xlYW4sIG90aGVyd2lzZSBmYWxzZVxuICpcbiAqIEBtZXRob2QgaXNCb29sZWFuXG4gKiBAcGFyYW0ge09iamVjdH1cbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4gKi9cbnZhciBpc0Jvb2xlYW4gPSBmdW5jdGlvbiAob2JqZWN0KSB7XG4gICAgcmV0dXJuIHR5cGVvZiBvYmplY3QgPT09ICdib29sZWFuJztcbn07XG5cbi8qKlxuICogUmV0dXJucyB0cnVlIGlmIG9iamVjdCBpcyBhcnJheSwgb3RoZXJ3aXNlIGZhbHNlXG4gKlxuICogQG1ldGhvZCBpc0FycmF5XG4gKiBAcGFyYW0ge09iamVjdH1cbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4gKi9cbnZhciBpc0FycmF5ID0gZnVuY3Rpb24gKG9iamVjdCkge1xuICAgIHJldHVybiBvYmplY3QgaW5zdGFuY2VvZiBBcnJheTsgXG59O1xuXG4vKipcbiAqIFJldHVybnMgdHJ1ZSBpZiBnaXZlbiBzdHJpbmcgaXMgdmFsaWQganNvbiBvYmplY3RcbiAqIFxuICogQG1ldGhvZCBpc0pzb25cbiAqIEBwYXJhbSB7U3RyaW5nfVxuICogQHJldHVybiB7Qm9vbGVhbn1cbiAqL1xudmFyIGlzSnNvbiA9IGZ1bmN0aW9uIChzdHIpIHtcbiAgICB0cnkge1xuICAgICAgICByZXR1cm4gISFKU09OLnBhcnNlKHN0cik7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgY2FsbGVkIHRvIGNoZWNrIGlmIHN0cmluZyBpcyB2YWxpZCBldGhlcmV1bSBJQkFOIG51bWJlclxuICogU3VwcG9ydHMgZGlyZWN0IGFuZCBpbmRpcmVjdCBJQkFOc1xuICpcbiAqIEBtZXRob2QgaXNJQkFOXG4gKiBAcGFyYW0ge1N0cmluZ31cbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4gKi9cbnZhciBpc0lCQU4gPSBmdW5jdGlvbiAoaWJhbikge1xuICAgIHJldHVybiAvXlhFWzAtOV17Mn0oRVRIWzAtOUEtWl17MTN9fFswLTlBLVpdezMwfSkkLy50ZXN0KGliYW4pO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgcGFkTGVmdDogcGFkTGVmdCxcbiAgICB0b0hleDogdG9IZXgsXG4gICAgdG9EZWNpbWFsOiB0b0RlY2ltYWwsXG4gICAgZnJvbURlY2ltYWw6IGZyb21EZWNpbWFsLFxuICAgIHRvQXNjaWk6IHRvQXNjaWksXG4gICAgZnJvbUFzY2lpOiBmcm9tQXNjaWksXG4gICAgdHJhbnNmb3JtVG9GdWxsTmFtZTogdHJhbnNmb3JtVG9GdWxsTmFtZSxcbiAgICBleHRyYWN0RGlzcGxheU5hbWU6IGV4dHJhY3REaXNwbGF5TmFtZSxcbiAgICBleHRyYWN0VHlwZU5hbWU6IGV4dHJhY3RUeXBlTmFtZSxcbiAgICB0b1dlaTogdG9XZWksXG4gICAgZnJvbVdlaTogZnJvbVdlaSxcbiAgICB0b0JpZ051bWJlcjogdG9CaWdOdW1iZXIsXG4gICAgdG9Ud29zQ29tcGxlbWVudDogdG9Ud29zQ29tcGxlbWVudCxcbiAgICB0b0FkZHJlc3M6IHRvQWRkcmVzcyxcbiAgICBpc0JpZ051bWJlcjogaXNCaWdOdW1iZXIsXG4gICAgaXNTdHJpY3RBZGRyZXNzOiBpc1N0cmljdEFkZHJlc3MsXG4gICAgaXNBZGRyZXNzOiBpc0FkZHJlc3MsXG4gICAgaXNGdW5jdGlvbjogaXNGdW5jdGlvbixcbiAgICBpc1N0cmluZzogaXNTdHJpbmcsXG4gICAgaXNPYmplY3Q6IGlzT2JqZWN0LFxuICAgIGlzQm9vbGVhbjogaXNCb29sZWFuLFxuICAgIGlzQXJyYXk6IGlzQXJyYXksXG4gICAgaXNKc29uOiBpc0pzb24sXG4gICAgaXNJQkFOOiBpc0lCQU5cbn07XG5cbiIsIm1vZHVsZS5leHBvcnRzPXtcbiAgICBcInZlcnNpb25cIjogXCIwLjUuMFwiXG59XG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBAZmlsZSB3ZWIzLmpzXG4gKiBAYXV0aG9yczpcbiAqICAgSmVmZnJleSBXaWxja2UgPGplZmZAZXRoZGV2LmNvbT5cbiAqICAgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiAgIE1hcmlhbiBPYW5jZWEgPG1hcmlhbkBldGhkZXYuY29tPlxuICogICBGYWJpYW4gVm9nZWxzdGVsbGVyIDxmYWJpYW5AZXRoZGV2LmNvbT5cbiAqICAgR2F2IFdvb2QgPGdAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTRcbiAqL1xuXG52YXIgdmVyc2lvbiA9IHJlcXVpcmUoJy4vdmVyc2lvbi5qc29uJyk7XG52YXIgbmV0ID0gcmVxdWlyZSgnLi93ZWIzL25ldCcpO1xudmFyIGV0aCA9IHJlcXVpcmUoJy4vd2ViMy9ldGgnKTtcbnZhciBkYiA9IHJlcXVpcmUoJy4vd2ViMy9kYicpO1xudmFyIHNoaCA9IHJlcXVpcmUoJy4vd2ViMy9zaGgnKTtcbnZhciB3YXRjaGVzID0gcmVxdWlyZSgnLi93ZWIzL3dhdGNoZXMnKTtcbnZhciBGaWx0ZXIgPSByZXF1aXJlKCcuL3dlYjMvZmlsdGVyJyk7XG52YXIgdXRpbHMgPSByZXF1aXJlKCcuL3V0aWxzL3V0aWxzJyk7XG52YXIgZm9ybWF0dGVycyA9IHJlcXVpcmUoJy4vd2ViMy9mb3JtYXR0ZXJzJyk7XG52YXIgUmVxdWVzdE1hbmFnZXIgPSByZXF1aXJlKCcuL3dlYjMvcmVxdWVzdG1hbmFnZXInKTtcbnZhciBjID0gcmVxdWlyZSgnLi91dGlscy9jb25maWcnKTtcbnZhciBQcm9wZXJ0eSA9IHJlcXVpcmUoJy4vd2ViMy9wcm9wZXJ0eScpO1xudmFyIEJhdGNoID0gcmVxdWlyZSgnLi93ZWIzL2JhdGNoJyk7XG52YXIgc2hhMyA9IHJlcXVpcmUoJy4vdXRpbHMvc2hhMycpO1xuXG52YXIgd2ViM1Byb3BlcnRpZXMgPSBbXG4gICAgbmV3IFByb3BlcnR5KHtcbiAgICAgICAgbmFtZTogJ3ZlcnNpb24uY2xpZW50JyxcbiAgICAgICAgZ2V0dGVyOiAnd2ViM19jbGllbnRWZXJzaW9uJ1xuICAgIH0pLFxuICAgIG5ldyBQcm9wZXJ0eSh7XG4gICAgICAgIG5hbWU6ICd2ZXJzaW9uLm5ldHdvcmsnLFxuICAgICAgICBnZXR0ZXI6ICduZXRfdmVyc2lvbicsXG4gICAgICAgIGlucHV0Rm9ybWF0dGVyOiB1dGlscy50b0RlY2ltYWxcbiAgICB9KSxcbiAgICBuZXcgUHJvcGVydHkoe1xuICAgICAgICBuYW1lOiAndmVyc2lvbi5ldGhlcmV1bScsXG4gICAgICAgIGdldHRlcjogJ2V0aF9wcm90b2NvbFZlcnNpb24nLFxuICAgICAgICBpbnB1dEZvcm1hdHRlcjogdXRpbHMudG9EZWNpbWFsXG4gICAgfSksXG4gICAgbmV3IFByb3BlcnR5KHtcbiAgICAgICAgbmFtZTogJ3ZlcnNpb24ud2hpc3BlcicsXG4gICAgICAgIGdldHRlcjogJ3NoaF92ZXJzaW9uJyxcbiAgICAgICAgaW5wdXRGb3JtYXR0ZXI6IHV0aWxzLnRvRGVjaW1hbFxuICAgIH0pXG5dO1xuXG4vLy8gY3JlYXRlcyBtZXRob2RzIGluIGEgZ2l2ZW4gb2JqZWN0IGJhc2VkIG9uIG1ldGhvZCBkZXNjcmlwdGlvbiBvbiBpbnB1dFxuLy8vIHNldHVwcyBhcGkgY2FsbHMgZm9yIHRoZXNlIG1ldGhvZHNcbnZhciBzZXR1cE1ldGhvZHMgPSBmdW5jdGlvbiAob2JqLCBtZXRob2RzKSB7XG4gICAgbWV0aG9kcy5mb3JFYWNoKGZ1bmN0aW9uIChtZXRob2QpIHtcbiAgICAgICAgbWV0aG9kLmF0dGFjaFRvT2JqZWN0KG9iaik7XG4gICAgfSk7XG59O1xuXG4vLy8gY3JlYXRlcyBwcm9wZXJ0aWVzIGluIGEgZ2l2ZW4gb2JqZWN0IGJhc2VkIG9uIHByb3BlcnRpZXMgZGVzY3JpcHRpb24gb24gaW5wdXRcbi8vLyBzZXR1cHMgYXBpIGNhbGxzIGZvciB0aGVzZSBwcm9wZXJ0aWVzXG52YXIgc2V0dXBQcm9wZXJ0aWVzID0gZnVuY3Rpb24gKG9iaiwgcHJvcGVydGllcykge1xuICAgIHByb3BlcnRpZXMuZm9yRWFjaChmdW5jdGlvbiAocHJvcGVydHkpIHtcbiAgICAgICAgcHJvcGVydHkuYXR0YWNoVG9PYmplY3Qob2JqKTtcbiAgICB9KTtcbn07XG5cbi8vLyBzZXR1cHMgd2ViMyBvYmplY3QsIGFuZCBpdCdzIGluLWJyb3dzZXIgZXhlY3V0ZWQgbWV0aG9kc1xudmFyIHdlYjMgPSB7fTtcbndlYjMucHJvdmlkZXJzID0ge307XG53ZWIzLnZlcnNpb24gPSB7fTtcbndlYjMudmVyc2lvbi5hcGkgPSB2ZXJzaW9uLnZlcnNpb247XG53ZWIzLmV0aCA9IHt9O1xuXG4vKmpzaGludCBtYXhwYXJhbXM6NCAqL1xud2ViMy5ldGguZmlsdGVyID0gZnVuY3Rpb24gKGZpbCwgZXZlbnRQYXJhbXMsIG9wdGlvbnMsIGZvcm1hdHRlcikge1xuXG4gICAgLy8gaWYgaXRzIGV2ZW50LCB0cmVhdCBpdCBkaWZmZXJlbnRseVxuICAgIC8vIFRPRE86IHNpbXBsaWZ5IGFuZCByZW1vdmVcbiAgICBpZiAoZmlsLl9pc0V2ZW50KSB7XG4gICAgICAgIHJldHVybiBmaWwoZXZlbnRQYXJhbXMsIG9wdGlvbnMpO1xuICAgIH1cblxuICAgIC8vIHdoYXQgb3V0cHV0TG9nRm9ybWF0dGVyPyB0aGF0J3Mgd3JvbmdcbiAgICAvL3JldHVybiBuZXcgRmlsdGVyKGZpbCwgd2F0Y2hlcy5ldGgoKSwgZm9ybWF0dGVycy5vdXRwdXRMb2dGb3JtYXR0ZXIpO1xuICAgIHJldHVybiBuZXcgRmlsdGVyKGZpbCwgd2F0Y2hlcy5ldGgoKSwgZm9ybWF0dGVyIHx8IGZvcm1hdHRlcnMub3V0cHV0TG9nRm9ybWF0dGVyKTtcbn07XG4vKmpzaGludCBtYXhwYXJhbXM6MyAqL1xuXG53ZWIzLnNoaCA9IHt9O1xud2ViMy5zaGguZmlsdGVyID0gZnVuY3Rpb24gKGZpbCkge1xuICAgIHJldHVybiBuZXcgRmlsdGVyKGZpbCwgd2F0Y2hlcy5zaGgoKSwgZm9ybWF0dGVycy5vdXRwdXRQb3N0Rm9ybWF0dGVyKTtcbn07XG53ZWIzLm5ldCA9IHt9O1xud2ViMy5kYiA9IHt9O1xud2ViMy5zZXRQcm92aWRlciA9IGZ1bmN0aW9uIChwcm92aWRlcikge1xuICAgIFJlcXVlc3RNYW5hZ2VyLmdldEluc3RhbmNlKCkuc2V0UHJvdmlkZXIocHJvdmlkZXIpO1xufTtcbndlYjMucmVzZXQgPSBmdW5jdGlvbiAoKSB7XG4gICAgUmVxdWVzdE1hbmFnZXIuZ2V0SW5zdGFuY2UoKS5yZXNldCgpO1xuICAgIGMuZGVmYXVsdEJsb2NrID0gJ2xhdGVzdCc7XG4gICAgYy5kZWZhdWx0QWNjb3VudCA9IHVuZGVmaW5lZDtcbn07XG53ZWIzLnRvSGV4ID0gdXRpbHMudG9IZXg7XG53ZWIzLnRvQXNjaWkgPSB1dGlscy50b0FzY2lpO1xud2ViMy5mcm9tQXNjaWkgPSB1dGlscy5mcm9tQXNjaWk7XG53ZWIzLnRvRGVjaW1hbCA9IHV0aWxzLnRvRGVjaW1hbDtcbndlYjMuZnJvbURlY2ltYWwgPSB1dGlscy5mcm9tRGVjaW1hbDtcbndlYjMudG9CaWdOdW1iZXIgPSB1dGlscy50b0JpZ051bWJlcjtcbndlYjMudG9XZWkgPSB1dGlscy50b1dlaTtcbndlYjMuZnJvbVdlaSA9IHV0aWxzLmZyb21XZWk7XG53ZWIzLmlzQWRkcmVzcyA9IHV0aWxzLmlzQWRkcmVzcztcbndlYjMuaXNJQkFOID0gdXRpbHMuaXNJQkFOO1xud2ViMy5zaGEzID0gc2hhMztcbndlYjMuY3JlYXRlQmF0Y2ggPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIG5ldyBCYXRjaCgpO1xufTtcblxuLy8gQUREIGRlZmF1bHRibG9ja1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KHdlYjMuZXRoLCAnZGVmYXVsdEJsb2NrJywge1xuICAgIGdldDogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gYy5kZWZhdWx0QmxvY2s7XG4gICAgfSxcbiAgICBzZXQ6IGZ1bmN0aW9uICh2YWwpIHtcbiAgICAgICAgYy5kZWZhdWx0QmxvY2sgPSB2YWw7XG4gICAgICAgIHJldHVybiB2YWw7XG4gICAgfVxufSk7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh3ZWIzLmV0aCwgJ2RlZmF1bHRBY2NvdW50Jywge1xuICAgIGdldDogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gYy5kZWZhdWx0QWNjb3VudDtcbiAgICB9LFxuICAgIHNldDogZnVuY3Rpb24gKHZhbCkge1xuICAgICAgICBjLmRlZmF1bHRBY2NvdW50ID0gdmFsO1xuICAgICAgICByZXR1cm4gdmFsO1xuICAgIH1cbn0pO1xuXG4vLy8gc2V0dXBzIGFsbCBhcGkgbWV0aG9kc1xuc2V0dXBQcm9wZXJ0aWVzKHdlYjMsIHdlYjNQcm9wZXJ0aWVzKTtcbnNldHVwTWV0aG9kcyh3ZWIzLm5ldCwgbmV0Lm1ldGhvZHMpO1xuc2V0dXBQcm9wZXJ0aWVzKHdlYjMubmV0LCBuZXQucHJvcGVydGllcyk7XG5zZXR1cE1ldGhvZHMod2ViMy5ldGgsIGV0aC5tZXRob2RzKTtcbnNldHVwUHJvcGVydGllcyh3ZWIzLmV0aCwgZXRoLnByb3BlcnRpZXMpO1xuc2V0dXBNZXRob2RzKHdlYjMuZGIsIGRiLm1ldGhvZHMpO1xuc2V0dXBNZXRob2RzKHdlYjMuc2hoLCBzaGgubWV0aG9kcyk7XG5cbm1vZHVsZS5leHBvcnRzID0gd2ViMztcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSBiYXRjaC5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgUmVxdWVzdE1hbmFnZXIgPSByZXF1aXJlKCcuL3JlcXVlc3RtYW5hZ2VyJyk7XG5cbnZhciBCYXRjaCA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLnJlcXVlc3RzID0gW107XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gYWRkIGNyZWF0ZSBuZXcgcmVxdWVzdCB0byBiYXRjaCByZXF1ZXN0XG4gKlxuICogQG1ldGhvZCBhZGRcbiAqIEBwYXJhbSB7T2JqZWN0fSBqc29ucnBjIHJlcXVldCBvYmplY3RcbiAqL1xuQmF0Y2gucHJvdG90eXBlLmFkZCA9IGZ1bmN0aW9uIChyZXF1ZXN0KSB7XG4gICAgdGhpcy5yZXF1ZXN0cy5wdXNoKHJlcXVlc3QpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGV4ZWN1dGUgYmF0Y2ggcmVxdWVzdFxuICpcbiAqIEBtZXRob2QgZXhlY3V0ZVxuICovXG5CYXRjaC5wcm90b3R5cGUuZXhlY3V0ZSA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgcmVxdWVzdHMgPSB0aGlzLnJlcXVlc3RzO1xuICAgIFJlcXVlc3RNYW5hZ2VyLmdldEluc3RhbmNlKCkuc2VuZEJhdGNoKHJlcXVlc3RzLCBmdW5jdGlvbiAoZXJyLCByZXN1bHRzKSB7XG4gICAgICAgIHJlc3VsdHMgPSByZXN1bHRzIHx8IFtdO1xuICAgICAgICByZXF1ZXN0cy5tYXAoZnVuY3Rpb24gKHJlcXVlc3QsIGluZGV4KSB7XG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0c1tpbmRleF0gfHwge307XG4gICAgICAgIH0pLm1hcChmdW5jdGlvbiAocmVzdWx0LCBpbmRleCkge1xuICAgICAgICAgICAgcmV0dXJuIHJlcXVlc3RzW2luZGV4XS5mb3JtYXQgPyByZXF1ZXN0c1tpbmRleF0uZm9ybWF0KHJlc3VsdC5yZXN1bHQpIDogcmVzdWx0LnJlc3VsdDtcbiAgICAgICAgfSkuZm9yRWFjaChmdW5jdGlvbiAocmVzdWx0LCBpbmRleCkge1xuICAgICAgICAgICAgaWYgKHJlcXVlc3RzW2luZGV4XS5jYWxsYmFjaykge1xuICAgICAgICAgICAgICAgIHJlcXVlc3RzW2luZGV4XS5jYWxsYmFjayhlcnIsIHJlc3VsdCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH0pOyBcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gQmF0Y2g7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIFxuICogQGZpbGUgY29udHJhY3QuanNcbiAqIEBhdXRob3IgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE0XG4gKi9cblxudmFyIHdlYjMgPSByZXF1aXJlKCcuLi93ZWIzJyk7IFxudmFyIHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMvdXRpbHMnKTtcbnZhciBjb2RlciA9IHJlcXVpcmUoJy4uL3NvbGlkaXR5L2NvZGVyJyk7XG52YXIgU29saWRpdHlFdmVudCA9IHJlcXVpcmUoJy4vZXZlbnQnKTtcbnZhciBTb2xpZGl0eUZ1bmN0aW9uID0gcmVxdWlyZSgnLi9mdW5jdGlvbicpO1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZW5jb2RlIGNvbnN0cnVjdG9yIHBhcmFtc1xuICpcbiAqIEBtZXRob2QgZW5jb2RlQ29uc3RydWN0b3JQYXJhbXNcbiAqIEBwYXJhbSB7QXJyYXl9IGFiaVxuICogQHBhcmFtIHtBcnJheX0gY29uc3RydWN0b3IgcGFyYW1zXG4gKi9cbnZhciBlbmNvZGVDb25zdHJ1Y3RvclBhcmFtcyA9IGZ1bmN0aW9uIChhYmksIHBhcmFtcykge1xuICAgIHJldHVybiBhYmkuZmlsdGVyKGZ1bmN0aW9uIChqc29uKSB7XG4gICAgICAgIHJldHVybiBqc29uLnR5cGUgPT09ICdjb25zdHJ1Y3RvcicgJiYganNvbi5pbnB1dHMubGVuZ3RoID09PSBwYXJhbXMubGVuZ3RoO1xuICAgIH0pLm1hcChmdW5jdGlvbiAoanNvbikge1xuICAgICAgICByZXR1cm4ganNvbi5pbnB1dHMubWFwKGZ1bmN0aW9uIChpbnB1dCkge1xuICAgICAgICAgICAgcmV0dXJuIGlucHV0LnR5cGU7XG4gICAgICAgIH0pO1xuICAgIH0pLm1hcChmdW5jdGlvbiAodHlwZXMpIHtcbiAgICAgICAgcmV0dXJuIGNvZGVyLmVuY29kZVBhcmFtcyh0eXBlcywgcGFyYW1zKTtcbiAgICB9KVswXSB8fCAnJztcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBhZGQgZnVuY3Rpb25zIHRvIGNvbnRyYWN0IG9iamVjdFxuICpcbiAqIEBtZXRob2QgYWRkRnVuY3Rpb25zVG9Db250cmFjdFxuICogQHBhcmFtIHtDb250cmFjdH0gY29udHJhY3RcbiAqIEBwYXJhbSB7QXJyYXl9IGFiaVxuICovXG52YXIgYWRkRnVuY3Rpb25zVG9Db250cmFjdCA9IGZ1bmN0aW9uIChjb250cmFjdCwgYWJpKSB7XG4gICAgYWJpLmZpbHRlcihmdW5jdGlvbiAoanNvbikge1xuICAgICAgICByZXR1cm4ganNvbi50eXBlID09PSAnZnVuY3Rpb24nO1xuICAgIH0pLm1hcChmdW5jdGlvbiAoanNvbikge1xuICAgICAgICByZXR1cm4gbmV3IFNvbGlkaXR5RnVuY3Rpb24oanNvbiwgY29udHJhY3QuYWRkcmVzcyk7XG4gICAgfSkuZm9yRWFjaChmdW5jdGlvbiAoZikge1xuICAgICAgICBmLmF0dGFjaFRvQ29udHJhY3QoY29udHJhY3QpO1xuICAgIH0pO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGFkZCBldmVudHMgdG8gY29udHJhY3Qgb2JqZWN0XG4gKlxuICogQG1ldGhvZCBhZGRFdmVudHNUb0NvbnRyYWN0XG4gKiBAcGFyYW0ge0NvbnRyYWN0fSBjb250cmFjdFxuICogQHBhcmFtIHtBcnJheX0gYWJpXG4gKi9cbnZhciBhZGRFdmVudHNUb0NvbnRyYWN0ID0gZnVuY3Rpb24gKGNvbnRyYWN0LCBhYmkpIHtcbiAgICBhYmkuZmlsdGVyKGZ1bmN0aW9uIChqc29uKSB7XG4gICAgICAgIHJldHVybiBqc29uLnR5cGUgPT09ICdldmVudCc7XG4gICAgfSkubWFwKGZ1bmN0aW9uIChqc29uKSB7XG4gICAgICAgIHJldHVybiBuZXcgU29saWRpdHlFdmVudChqc29uLCBjb250cmFjdC5hZGRyZXNzKTtcbiAgICB9KS5mb3JFYWNoKGZ1bmN0aW9uIChlKSB7XG4gICAgICAgIGUuYXR0YWNoVG9Db250cmFjdChjb250cmFjdCk7XG4gICAgfSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gY3JlYXRlIG5ldyBDb250cmFjdEZhY3RvcnlcbiAqXG4gKiBAbWV0aG9kIGNvbnRyYWN0XG4gKiBAcGFyYW0ge0FycmF5fSBhYmlcbiAqIEByZXR1cm5zIHtDb250cmFjdEZhY3Rvcnl9IG5ldyBjb250cmFjdCBmYWN0b3J5XG4gKi9cbnZhciBjb250cmFjdCA9IGZ1bmN0aW9uIChhYmkpIHtcbiAgICByZXR1cm4gbmV3IENvbnRyYWN0RmFjdG9yeShhYmkpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGNyZWF0ZSBuZXcgQ29udHJhY3RGYWN0b3J5IGluc3RhbmNlXG4gKlxuICogQG1ldGhvZCBDb250cmFjdEZhY3RvcnlcbiAqIEBwYXJhbSB7QXJyYXl9IGFiaVxuICovXG52YXIgQ29udHJhY3RGYWN0b3J5ID0gZnVuY3Rpb24gKGFiaSkge1xuICAgIHRoaXMuYWJpID0gYWJpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGNyZWF0ZSBuZXcgY29udHJhY3Qgb24gYSBibG9ja2NoYWluXG4gKiBcbiAqIEBtZXRob2QgbmV3XG4gKiBAcGFyYW0ge0FueX0gY29udHJhY3QgY29uc3RydWN0b3IgcGFyYW0xIChvcHRpb25hbClcbiAqIEBwYXJhbSB7QW55fSBjb250cmFjdCBjb25zdHJ1Y3RvciBwYXJhbTIgKG9wdGlvbmFsKVxuICogQHBhcmFtIHtPYmplY3R9IGNvbnRyYWN0IHRyYW5zYWN0aW9uIG9iamVjdCAocmVxdWlyZWQpXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFja1xuICogQHJldHVybnMge0NvbnRyYWN0fSByZXR1cm5zIGNvbnRyYWN0IGlmIG5vIGNhbGxiYWNrIHdhcyBwYXNzZWQsXG4gKiBvdGhlcndpc2UgY2FsbHMgY2FsbGJhY2sgZnVuY3Rpb24gKGVyciwgY29udHJhY3QpXG4gKi9cbkNvbnRyYWN0RmFjdG9yeS5wcm90b3R5cGUubmV3ID0gZnVuY3Rpb24gKCkge1xuICAgIC8vIHBhcnNlIGFyZ3VtZW50c1xuICAgIHZhciBvcHRpb25zID0ge307IC8vIHJlcXVpcmVkIVxuICAgIHZhciBjYWxsYmFjaztcblxuICAgIHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKTtcbiAgICBpZiAodXRpbHMuaXNGdW5jdGlvbihhcmdzW2FyZ3MubGVuZ3RoIC0gMV0pKSB7XG4gICAgICAgIGNhbGxiYWNrID0gYXJncy5wb3AoKTtcbiAgICB9XG5cbiAgICB2YXIgbGFzdCA9IGFyZ3NbYXJncy5sZW5ndGggLSAxXTtcbiAgICBpZiAodXRpbHMuaXNPYmplY3QobGFzdCkgJiYgIXV0aWxzLmlzQXJyYXkobGFzdCkpIHtcbiAgICAgICAgb3B0aW9ucyA9IGFyZ3MucG9wKCk7XG4gICAgfVxuXG4gICAgLy8gdGhyb3cgYW4gZXJyb3IgaWYgdGhlcmUgYXJlIG5vIG9wdGlvbnNcblxuICAgIHZhciBieXRlcyA9IGVuY29kZUNvbnN0cnVjdG9yUGFyYW1zKHRoaXMuYWJpLCBhcmdzKTtcbiAgICBvcHRpb25zLmRhdGEgKz0gYnl0ZXM7XG5cbiAgICBpZiAoIWNhbGxiYWNrKSB7XG4gICAgICAgIHZhciBhZGRyZXNzID0gd2ViMy5ldGguc2VuZFRyYW5zYWN0aW9uKG9wdGlvbnMpO1xuICAgICAgICByZXR1cm4gdGhpcy5hdChhZGRyZXNzKTtcbiAgICB9XG4gIFxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB3ZWIzLmV0aC5zZW5kVHJhbnNhY3Rpb24ob3B0aW9ucywgZnVuY3Rpb24gKGVyciwgYWRkcmVzcykge1xuICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgICBjYWxsYmFjayhlcnIpO1xuICAgICAgICB9XG4gICAgICAgIHNlbGYuYXQoYWRkcmVzcywgY2FsbGJhY2spOyBcbiAgICB9KTsgXG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IGFjY2VzcyB0byBleGlzdGluZyBjb250cmFjdCBvbiBhIGJsb2NrY2hhaW5cbiAqXG4gKiBAbWV0aG9kIGF0XG4gKiBAcGFyYW0ge0FkZHJlc3N9IGNvbnRyYWN0IGFkZHJlc3MgKHJlcXVpcmVkKVxuICogQHBhcmFtIHtGdW5jdGlvbn0gY2FsbGJhY2sge29wdGlvbmFsKVxuICogQHJldHVybnMge0NvbnRyYWN0fSByZXR1cm5zIGNvbnRyYWN0IGlmIG5vIGNhbGxiYWNrIHdhcyBwYXNzZWQsXG4gKiBvdGhlcndpc2UgY2FsbHMgY2FsbGJhY2sgZnVuY3Rpb24gKGVyciwgY29udHJhY3QpXG4gKi9cbkNvbnRyYWN0RmFjdG9yeS5wcm90b3R5cGUuYXQgPSBmdW5jdGlvbiAoYWRkcmVzcywgY2FsbGJhY2spIHtcbiAgICAvLyBUT0RPOiBhZGRyZXNzIGlzIHJlcXVpcmVkXG4gICAgXG4gICAgaWYgKGNhbGxiYWNrKSB7XG4gICAgICAgIGNhbGxiYWNrKG51bGwsIG5ldyBDb250cmFjdCh0aGlzLmFiaSwgYWRkcmVzcykpO1xuICAgIH0gXG4gICAgcmV0dXJuIG5ldyBDb250cmFjdCh0aGlzLmFiaSwgYWRkcmVzcyk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gY3JlYXRlIG5ldyBjb250cmFjdCBpbnN0YW5jZVxuICpcbiAqIEBtZXRob2QgQ29udHJhY3RcbiAqIEBwYXJhbSB7QXJyYXl9IGFiaVxuICogQHBhcmFtIHtBZGRyZXNzfSBjb250cmFjdCBhZGRyZXNzXG4gKi9cbnZhciBDb250cmFjdCA9IGZ1bmN0aW9uIChhYmksIGFkZHJlc3MpIHtcbiAgICB0aGlzLmFkZHJlc3MgPSBhZGRyZXNzO1xuICAgIGFkZEZ1bmN0aW9uc1RvQ29udHJhY3QodGhpcywgYWJpKTtcbiAgICBhZGRFdmVudHNUb0NvbnRyYWN0KHRoaXMsIGFiaSk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IGNvbnRyYWN0O1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBAZmlsZSBkYi5qc1xuICogQGF1dGhvcnM6XG4gKiAgIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciBNZXRob2QgPSByZXF1aXJlKCcuL21ldGhvZCcpO1xuXG52YXIgcHV0U3RyaW5nID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ3B1dFN0cmluZycsXG4gICAgY2FsbDogJ2RiX3B1dFN0cmluZycsXG4gICAgcGFyYW1zOiAzXG59KTtcblxuXG52YXIgZ2V0U3RyaW5nID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2dldFN0cmluZycsXG4gICAgY2FsbDogJ2RiX2dldFN0cmluZycsXG4gICAgcGFyYW1zOiAyXG59KTtcblxudmFyIHB1dEhleCA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdwdXRIZXgnLFxuICAgIGNhbGw6ICdkYl9wdXRIZXgnLFxuICAgIHBhcmFtczogM1xufSk7XG5cbnZhciBnZXRIZXggPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0SGV4JyxcbiAgICBjYWxsOiAnZGJfZ2V0SGV4JyxcbiAgICBwYXJhbXM6IDJcbn0pO1xuXG52YXIgbWV0aG9kcyA9IFtcbiAgICBwdXRTdHJpbmcsIGdldFN0cmluZywgcHV0SGV4LCBnZXRIZXhcbl07XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIG1ldGhvZHM6IG1ldGhvZHNcbn07XG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBcbiAqIEBmaWxlIGVycm9ycy5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBJbnZhbGlkTnVtYmVyT2ZQYXJhbXM6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBFcnJvcignSW52YWxpZCBudW1iZXIgb2YgaW5wdXQgcGFyYW1ldGVycycpO1xuICAgIH0sXG4gICAgSW52YWxpZENvbm5lY3Rpb246IGZ1bmN0aW9uIChob3N0KXtcbiAgICAgICAgcmV0dXJuIG5ldyBFcnJvcignQ09OTkVDVElPTiBFUlJPUjogQ291bGRuXFwndCBjb25uZWN0IHRvIG5vZGUgJysgaG9zdCArJywgaXMgaXQgcnVubmluZz8nKTtcbiAgICB9LFxuICAgIEludmFsaWRQcm92aWRlcjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gbmV3IEVycm9yKCdQcm92aWRvciBub3Qgc2V0IG9yIGludmFsaWQnKTtcbiAgICB9LFxuICAgIEludmFsaWRSZXNwb25zZTogZnVuY3Rpb24gKHJlc3VsdCl7XG4gICAgICAgIHZhciBtZXNzYWdlID0gISFyZXN1bHQgJiYgISFyZXN1bHQuZXJyb3IgJiYgISFyZXN1bHQuZXJyb3IubWVzc2FnZSA/IHJlc3VsdC5lcnJvci5tZXNzYWdlIDogJ0ludmFsaWQgSlNPTiBSUEMgcmVzcG9uc2UnO1xuICAgICAgICByZXR1cm4gbmV3IEVycm9yKG1lc3NhZ2UpO1xuICAgIH1cbn07XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqXG4gKiBAZmlsZSBldGguanNcbiAqIEBhdXRob3IgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAYXV0aG9yIEZhYmlhbiBWb2dlbHN0ZWxsZXIgPGZhYmlhbkBldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbi8qKlxuICogV2ViM1xuICpcbiAqIEBtb2R1bGUgd2ViM1xuICovXG5cbi8qKlxuICogRXRoIG1ldGhvZHMgYW5kIHByb3BlcnRpZXNcbiAqXG4gKiBBbiBleGFtcGxlIG1ldGhvZCBvYmplY3QgY2FuIGxvb2sgYXMgZm9sbG93czpcbiAqXG4gKiAgICAgIHtcbiAqICAgICAgbmFtZTogJ2dldEJsb2NrJyxcbiAqICAgICAgY2FsbDogYmxvY2tDYWxsLFxuICogICAgICBwYXJhbXM6IDIsXG4gKiAgICAgIG91dHB1dEZvcm1hdHRlcjogZm9ybWF0dGVycy5vdXRwdXRCbG9ja0Zvcm1hdHRlcixcbiAqICAgICAgaW5wdXRGb3JtYXR0ZXI6IFsgLy8gY2FuIGJlIGEgZm9ybWF0dGVyIGZ1bmNpdG9uIG9yIGFuIGFycmF5IG9mIGZ1bmN0aW9ucy4gV2hlcmUgZWFjaCBpdGVtIGluIHRoZSBhcnJheSB3aWxsIGJlIHVzZWQgZm9yIG9uZSBwYXJhbWV0ZXJcbiAqICAgICAgICAgICB1dGlscy50b0hleCwgLy8gZm9ybWF0cyBwYXJhbXRlciAxXG4gKiAgICAgICAgICAgZnVuY3Rpb24ocGFyYW0peyByZXR1cm4gISFwYXJhbTsgfSAvLyBmb3JtYXRzIHBhcmFtdGVyIDJcbiAqICAgICAgICAgXVxuICogICAgICAgfSxcbiAqXG4gKiBAY2xhc3MgW3dlYjNdIGV0aFxuICogQGNvbnN0cnVjdG9yXG4gKi9cblxuXCJ1c2Ugc3RyaWN0XCI7XG5cbnZhciBmb3JtYXR0ZXJzID0gcmVxdWlyZSgnLi9mb3JtYXR0ZXJzJyk7XG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xudmFyIE1ldGhvZCA9IHJlcXVpcmUoJy4vbWV0aG9kJyk7XG52YXIgUHJvcGVydHkgPSByZXF1aXJlKCcuL3Byb3BlcnR5Jyk7XG5cbnZhciBibG9ja0NhbGwgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIHJldHVybiAodXRpbHMuaXNTdHJpbmcoYXJnc1swXSkgJiYgYXJnc1swXS5pbmRleE9mKCcweCcpID09PSAwKSA/IFwiZXRoX2dldEJsb2NrQnlIYXNoXCIgOiBcImV0aF9nZXRCbG9ja0J5TnVtYmVyXCI7XG59O1xuXG52YXIgdHJhbnNhY3Rpb25Gcm9tQmxvY2tDYWxsID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICByZXR1cm4gKHV0aWxzLmlzU3RyaW5nKGFyZ3NbMF0pICYmIGFyZ3NbMF0uaW5kZXhPZignMHgnKSA9PT0gMCkgPyAnZXRoX2dldFRyYW5zYWN0aW9uQnlCbG9ja0hhc2hBbmRJbmRleCcgOiAnZXRoX2dldFRyYW5zYWN0aW9uQnlCbG9ja051bWJlckFuZEluZGV4Jztcbn07XG5cbnZhciB1bmNsZUNhbGwgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIHJldHVybiAodXRpbHMuaXNTdHJpbmcoYXJnc1swXSkgJiYgYXJnc1swXS5pbmRleE9mKCcweCcpID09PSAwKSA/ICdldGhfZ2V0VW5jbGVCeUJsb2NrSGFzaEFuZEluZGV4JyA6ICdldGhfZ2V0VW5jbGVCeUJsb2NrTnVtYmVyQW5kSW5kZXgnO1xufTtcblxudmFyIGdldEJsb2NrVHJhbnNhY3Rpb25Db3VudENhbGwgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIHJldHVybiAodXRpbHMuaXNTdHJpbmcoYXJnc1swXSkgJiYgYXJnc1swXS5pbmRleE9mKCcweCcpID09PSAwKSA/ICdldGhfZ2V0QmxvY2tUcmFuc2FjdGlvbkNvdW50QnlIYXNoJyA6ICdldGhfZ2V0QmxvY2tUcmFuc2FjdGlvbkNvdW50QnlOdW1iZXInO1xufTtcblxudmFyIHVuY2xlQ291bnRDYWxsID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICByZXR1cm4gKHV0aWxzLmlzU3RyaW5nKGFyZ3NbMF0pICYmIGFyZ3NbMF0uaW5kZXhPZignMHgnKSA9PT0gMCkgPyAnZXRoX2dldFVuY2xlQ291bnRCeUJsb2NrSGFzaCcgOiAnZXRoX2dldFVuY2xlQ291bnRCeUJsb2NrTnVtYmVyJztcbn07XG5cbi8vLyBAcmV0dXJucyBhbiBhcnJheSBvZiBvYmplY3RzIGRlc2NyaWJpbmcgd2ViMy5ldGggYXBpIG1ldGhvZHNcblxudmFyIGdldEJhbGFuY2UgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0QmFsYW5jZScsXG4gICAgY2FsbDogJ2V0aF9nZXRCYWxhbmNlJyxcbiAgICBwYXJhbXM6IDIsXG4gICAgaW5wdXRGb3JtYXR0ZXI6IFt1dGlscy50b0FkZHJlc3MsIGZvcm1hdHRlcnMuaW5wdXREZWZhdWx0QmxvY2tOdW1iZXJGb3JtYXR0ZXJdLFxuICAgIG91dHB1dEZvcm1hdHRlcjogZm9ybWF0dGVycy5vdXRwdXRCaWdOdW1iZXJGb3JtYXR0ZXJcbn0pO1xuXG52YXIgZ2V0U3RvcmFnZUF0ID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2dldFN0b3JhZ2VBdCcsXG4gICAgY2FsbDogJ2V0aF9nZXRTdG9yYWdlQXQnLFxuICAgIHBhcmFtczogMyxcbiAgICBpbnB1dEZvcm1hdHRlcjogW251bGwsIHV0aWxzLnRvSGV4LCBmb3JtYXR0ZXJzLmlucHV0RGVmYXVsdEJsb2NrTnVtYmVyRm9ybWF0dGVyXVxufSk7XG5cbnZhciBnZXRDb2RlID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2dldENvZGUnLFxuICAgIGNhbGw6ICdldGhfZ2V0Q29kZScsXG4gICAgcGFyYW1zOiAyLFxuICAgIGlucHV0Rm9ybWF0dGVyOiBbdXRpbHMudG9BZGRyZXNzLCBmb3JtYXR0ZXJzLmlucHV0RGVmYXVsdEJsb2NrTnVtYmVyRm9ybWF0dGVyXVxufSk7XG5cbnZhciBnZXRCbG9jayA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdnZXRCbG9jaycsXG4gICAgY2FsbDogYmxvY2tDYWxsLFxuICAgIHBhcmFtczogMixcbiAgICBpbnB1dEZvcm1hdHRlcjogW2Zvcm1hdHRlcnMuaW5wdXRCbG9ja051bWJlckZvcm1hdHRlciwgZnVuY3Rpb24gKHZhbCkgeyByZXR1cm4gISF2YWw7IH1dLFxuICAgIG91dHB1dEZvcm1hdHRlcjogZm9ybWF0dGVycy5vdXRwdXRCbG9ja0Zvcm1hdHRlclxufSk7XG5cbnZhciBnZXRVbmNsZSA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdnZXRVbmNsZScsXG4gICAgY2FsbDogdW5jbGVDYWxsLFxuICAgIHBhcmFtczogMixcbiAgICBpbnB1dEZvcm1hdHRlcjogW2Zvcm1hdHRlcnMuaW5wdXRCbG9ja051bWJlckZvcm1hdHRlciwgdXRpbHMudG9IZXhdLFxuICAgIG91dHB1dEZvcm1hdHRlcjogZm9ybWF0dGVycy5vdXRwdXRCbG9ja0Zvcm1hdHRlcixcblxufSk7XG5cbnZhciBnZXRDb21waWxlcnMgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0Q29tcGlsZXJzJyxcbiAgICBjYWxsOiAnZXRoX2dldENvbXBpbGVycycsXG4gICAgcGFyYW1zOiAwXG59KTtcblxudmFyIGdldEJsb2NrVHJhbnNhY3Rpb25Db3VudCA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdnZXRCbG9ja1RyYW5zYWN0aW9uQ291bnQnLFxuICAgIGNhbGw6IGdldEJsb2NrVHJhbnNhY3Rpb25Db3VudENhbGwsXG4gICAgcGFyYW1zOiAxLFxuICAgIGlucHV0Rm9ybWF0dGVyOiBbZm9ybWF0dGVycy5pbnB1dEJsb2NrTnVtYmVyRm9ybWF0dGVyXSxcbiAgICBvdXRwdXRGb3JtYXR0ZXI6IHV0aWxzLnRvRGVjaW1hbFxufSk7XG5cbnZhciBnZXRCbG9ja1VuY2xlQ291bnQgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0QmxvY2tVbmNsZUNvdW50JyxcbiAgICBjYWxsOiB1bmNsZUNvdW50Q2FsbCxcbiAgICBwYXJhbXM6IDEsXG4gICAgaW5wdXRGb3JtYXR0ZXI6IFtmb3JtYXR0ZXJzLmlucHV0QmxvY2tOdW1iZXJGb3JtYXR0ZXJdLFxuICAgIG91dHB1dEZvcm1hdHRlcjogdXRpbHMudG9EZWNpbWFsXG59KTtcblxudmFyIGdldFRyYW5zYWN0aW9uID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2dldFRyYW5zYWN0aW9uJyxcbiAgICBjYWxsOiAnZXRoX2dldFRyYW5zYWN0aW9uQnlIYXNoJyxcbiAgICBwYXJhbXM6IDEsXG4gICAgb3V0cHV0Rm9ybWF0dGVyOiBmb3JtYXR0ZXJzLm91dHB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyXG59KTtcblxudmFyIGdldFRyYW5zYWN0aW9uRnJvbUJsb2NrID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2dldFRyYW5zYWN0aW9uRnJvbUJsb2NrJyxcbiAgICBjYWxsOiB0cmFuc2FjdGlvbkZyb21CbG9ja0NhbGwsXG4gICAgcGFyYW1zOiAyLFxuICAgIGlucHV0Rm9ybWF0dGVyOiBbZm9ybWF0dGVycy5pbnB1dEJsb2NrTnVtYmVyRm9ybWF0dGVyLCB1dGlscy50b0hleF0sXG4gICAgb3V0cHV0Rm9ybWF0dGVyOiBmb3JtYXR0ZXJzLm91dHB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyXG59KTtcblxudmFyIGdldFRyYW5zYWN0aW9uQ291bnQgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0VHJhbnNhY3Rpb25Db3VudCcsXG4gICAgY2FsbDogJ2V0aF9nZXRUcmFuc2FjdGlvbkNvdW50JyxcbiAgICBwYXJhbXM6IDIsXG4gICAgaW5wdXRGb3JtYXR0ZXI6IFtudWxsLCBmb3JtYXR0ZXJzLmlucHV0RGVmYXVsdEJsb2NrTnVtYmVyRm9ybWF0dGVyXSxcbiAgICBvdXRwdXRGb3JtYXR0ZXI6IHV0aWxzLnRvRGVjaW1hbFxufSk7XG5cbnZhciBzZW5kVHJhbnNhY3Rpb24gPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnc2VuZFRyYW5zYWN0aW9uJyxcbiAgICBjYWxsOiAnZXRoX3NlbmRUcmFuc2FjdGlvbicsXG4gICAgcGFyYW1zOiAxLFxuICAgIGlucHV0Rm9ybWF0dGVyOiBbZm9ybWF0dGVycy5pbnB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyXVxufSk7XG5cbnZhciBjYWxsID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2NhbGwnLFxuICAgIGNhbGw6ICdldGhfY2FsbCcsXG4gICAgcGFyYW1zOiAyLFxuICAgIGlucHV0Rm9ybWF0dGVyOiBbZm9ybWF0dGVycy5pbnB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyLCBmb3JtYXR0ZXJzLmlucHV0RGVmYXVsdEJsb2NrTnVtYmVyRm9ybWF0dGVyXVxufSk7XG5cbnZhciBlc3RpbWF0ZUdhcyA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdlc3RpbWF0ZUdhcycsXG4gICAgY2FsbDogJ2V0aF9lc3RpbWF0ZUdhcycsXG4gICAgcGFyYW1zOiAxLFxuICAgIGlucHV0Rm9ybWF0dGVyOiBbZm9ybWF0dGVycy5pbnB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyXSxcbiAgICBvdXRwdXRGb3JtYXR0ZXI6IHV0aWxzLnRvRGVjaW1hbFxufSk7XG5cbnZhciBjb21waWxlU29saWRpdHkgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnY29tcGlsZS5zb2xpZGl0eScsXG4gICAgY2FsbDogJ2V0aF9jb21waWxlU29saWRpdHknLFxuICAgIHBhcmFtczogMVxufSk7XG5cbnZhciBjb21waWxlTExMID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2NvbXBpbGUubGxsJyxcbiAgICBjYWxsOiAnZXRoX2NvbXBpbGVMTEwnLFxuICAgIHBhcmFtczogMVxufSk7XG5cbnZhciBjb21waWxlU2VycGVudCA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdjb21waWxlLnNlcnBlbnQnLFxuICAgIGNhbGw6ICdldGhfY29tcGlsZVNlcnBlbnQnLFxuICAgIHBhcmFtczogMVxufSk7XG5cbnZhciBzdWJtaXRXb3JrID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ3N1Ym1pdFdvcmsnLFxuICAgIGNhbGw6ICdldGhfc3VibWl0V29yaycsXG4gICAgcGFyYW1zOiAzXG59KTtcblxudmFyIGdldFdvcmsgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0V29yaycsXG4gICAgY2FsbDogJ2V0aF9nZXRXb3JrJyxcbiAgICBwYXJhbXM6IDBcbn0pO1xuXG52YXIgbWV0aG9kcyA9IFtcbiAgICBnZXRCYWxhbmNlLFxuICAgIGdldFN0b3JhZ2VBdCxcbiAgICBnZXRDb2RlLFxuICAgIGdldEJsb2NrLFxuICAgIGdldFVuY2xlLFxuICAgIGdldENvbXBpbGVycyxcbiAgICBnZXRCbG9ja1RyYW5zYWN0aW9uQ291bnQsXG4gICAgZ2V0QmxvY2tVbmNsZUNvdW50LFxuICAgIGdldFRyYW5zYWN0aW9uLFxuICAgIGdldFRyYW5zYWN0aW9uRnJvbUJsb2NrLFxuICAgIGdldFRyYW5zYWN0aW9uQ291bnQsXG4gICAgY2FsbCxcbiAgICBlc3RpbWF0ZUdhcyxcbiAgICBzZW5kVHJhbnNhY3Rpb24sXG4gICAgY29tcGlsZVNvbGlkaXR5LFxuICAgIGNvbXBpbGVMTEwsXG4gICAgY29tcGlsZVNlcnBlbnQsXG4gICAgc3VibWl0V29yayxcbiAgICBnZXRXb3JrXG5dO1xuXG4vLy8gQHJldHVybnMgYW4gYXJyYXkgb2Ygb2JqZWN0cyBkZXNjcmliaW5nIHdlYjMuZXRoIGFwaSBwcm9wZXJ0aWVzXG5cblxuXG52YXIgcHJvcGVydGllcyA9IFtcbiAgICBuZXcgUHJvcGVydHkoe1xuICAgICAgICBuYW1lOiAnY29pbmJhc2UnLFxuICAgICAgICBnZXR0ZXI6ICdldGhfY29pbmJhc2UnXG4gICAgfSksXG4gICAgbmV3IFByb3BlcnR5KHtcbiAgICAgICAgbmFtZTogJ21pbmluZycsXG4gICAgICAgIGdldHRlcjogJ2V0aF9taW5pbmcnXG4gICAgfSksXG4gICAgbmV3IFByb3BlcnR5KHtcbiAgICAgICAgbmFtZTogJ2hhc2hyYXRlJyxcbiAgICAgICAgZ2V0dGVyOiAnZXRoX2hhc2hyYXRlJyxcbiAgICAgICAgb3V0cHV0Rm9ybWF0dGVyOiB1dGlscy50b0RlY2ltYWxcbiAgICB9KSxcbiAgICBuZXcgUHJvcGVydHkoe1xuICAgICAgICBuYW1lOiAnZ2FzUHJpY2UnLFxuICAgICAgICBnZXR0ZXI6ICdldGhfZ2FzUHJpY2UnLFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IGZvcm1hdHRlcnMub3V0cHV0QmlnTnVtYmVyRm9ybWF0dGVyXG4gICAgfSksXG4gICAgbmV3IFByb3BlcnR5KHtcbiAgICAgICAgbmFtZTogJ2FjY291bnRzJyxcbiAgICAgICAgZ2V0dGVyOiAnZXRoX2FjY291bnRzJ1xuICAgIH0pLFxuICAgIG5ldyBQcm9wZXJ0eSh7XG4gICAgICAgIG5hbWU6ICdibG9ja051bWJlcicsXG4gICAgICAgIGdldHRlcjogJ2V0aF9ibG9ja051bWJlcicsXG4gICAgICAgIG91dHB1dEZvcm1hdHRlcjogdXRpbHMudG9EZWNpbWFsXG4gICAgfSlcbl07XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIG1ldGhvZHM6IG1ldGhvZHMsXG4gICAgcHJvcGVydGllczogcHJvcGVydGllc1xufTtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSBldmVudC5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTRcbiAqL1xuXG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xudmFyIGNvZGVyID0gcmVxdWlyZSgnLi4vc29saWRpdHkvY29kZXInKTtcbnZhciB3ZWIzID0gcmVxdWlyZSgnLi4vd2ViMycpO1xudmFyIGZvcm1hdHRlcnMgPSByZXF1aXJlKCcuL2Zvcm1hdHRlcnMnKTtcbnZhciBzaGEzID0gcmVxdWlyZSgnLi4vdXRpbHMvc2hhMycpO1xuXG4vKipcbiAqIFRoaXMgcHJvdG90eXBlIHNob3VsZCBiZSB1c2VkIHRvIGNyZWF0ZSBldmVudCBmaWx0ZXJzXG4gKi9cbnZhciBTb2xpZGl0eUV2ZW50ID0gZnVuY3Rpb24gKGpzb24sIGFkZHJlc3MpIHtcbiAgICB0aGlzLl9wYXJhbXMgPSBqc29uLmlucHV0cztcbiAgICB0aGlzLl9uYW1lID0gdXRpbHMudHJhbnNmb3JtVG9GdWxsTmFtZShqc29uKTtcbiAgICB0aGlzLl9hZGRyZXNzID0gYWRkcmVzcztcbiAgICB0aGlzLl9hbm9ueW1vdXMgPSBqc29uLmFub255bW91cztcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZ2V0IGZpbHRlcmVkIHBhcmFtIHR5cGVzXG4gKlxuICogQG1ldGhvZCB0eXBlc1xuICogQHBhcmFtIHtCb29sfSBkZWNpZGUgaWYgcmV0dXJuZWQgdHlwZWQgc2hvdWxkIGJlIGluZGV4ZWRcbiAqIEByZXR1cm4ge0FycmF5fSBhcnJheSBvZiB0eXBlc1xuICovXG5Tb2xpZGl0eUV2ZW50LnByb3RvdHlwZS50eXBlcyA9IGZ1bmN0aW9uIChpbmRleGVkKSB7XG4gICAgcmV0dXJuIHRoaXMuX3BhcmFtcy5maWx0ZXIoZnVuY3Rpb24gKGkpIHtcbiAgICAgICAgcmV0dXJuIGkuaW5kZXhlZCA9PT0gaW5kZXhlZDtcbiAgICB9KS5tYXAoZnVuY3Rpb24gKGkpIHtcbiAgICAgICAgcmV0dXJuIGkudHlwZTtcbiAgICB9KTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZ2V0IGV2ZW50IGRpc3BsYXkgbmFtZVxuICpcbiAqIEBtZXRob2QgZGlzcGxheU5hbWVcbiAqIEByZXR1cm4ge1N0cmluZ30gZXZlbnQgZGlzcGxheSBuYW1lXG4gKi9cblNvbGlkaXR5RXZlbnQucHJvdG90eXBlLmRpc3BsYXlOYW1lID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB1dGlscy5leHRyYWN0RGlzcGxheU5hbWUodGhpcy5fbmFtZSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGdldCBldmVudCB0eXBlIG5hbWVcbiAqXG4gKiBAbWV0aG9kIHR5cGVOYW1lXG4gKiBAcmV0dXJuIHtTdHJpbmd9IGV2ZW50IHR5cGUgbmFtZVxuICovXG5Tb2xpZGl0eUV2ZW50LnByb3RvdHlwZS50eXBlTmFtZSA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gdXRpbHMuZXh0cmFjdFR5cGVOYW1lKHRoaXMuX25hbWUpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBnZXQgZXZlbnQgc2lnbmF0dXJlXG4gKlxuICogQG1ldGhvZCBzaWduYXR1cmVcbiAqIEByZXR1cm4ge1N0cmluZ30gZXZlbnQgc2lnbmF0dXJlXG4gKi9cblNvbGlkaXR5RXZlbnQucHJvdG90eXBlLnNpZ25hdHVyZSA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gc2hhMyh0aGlzLl9uYW1lKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZW5jb2RlIGluZGV4ZWQgcGFyYW1zIGFuZCBvcHRpb25zIHRvIG9uZSBmaW5hbCBvYmplY3RcbiAqIFxuICogQG1ldGhvZCBlbmNvZGVcbiAqIEBwYXJhbSB7T2JqZWN0fSBpbmRleGVkXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9uc1xuICogQHJldHVybiB7T2JqZWN0fSBldmVyeXRoaW5nIGNvbWJpbmVkIHRvZ2V0aGVyIGFuZCBlbmNvZGVkXG4gKi9cblNvbGlkaXR5RXZlbnQucHJvdG90eXBlLmVuY29kZSA9IGZ1bmN0aW9uIChpbmRleGVkLCBvcHRpb25zKSB7XG4gICAgaW5kZXhlZCA9IGluZGV4ZWQgfHwge307XG4gICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG4gICAgdmFyIHJlc3VsdCA9IHt9O1xuXG4gICAgWydmcm9tQmxvY2snLCAndG9CbG9jayddLmZpbHRlcihmdW5jdGlvbiAoZikge1xuICAgICAgICByZXR1cm4gb3B0aW9uc1tmXSAhPT0gdW5kZWZpbmVkO1xuICAgIH0pLmZvckVhY2goZnVuY3Rpb24gKGYpIHtcbiAgICAgICAgcmVzdWx0W2ZdID0gZm9ybWF0dGVycy5pbnB1dEJsb2NrTnVtYmVyRm9ybWF0dGVyKG9wdGlvbnNbZl0pO1xuICAgIH0pO1xuXG4gICAgcmVzdWx0LnRvcGljcyA9IFtdO1xuXG4gICAgaWYgKCF0aGlzLl9hbm9ueW1vdXMpIHtcbiAgICAgICAgcmVzdWx0LmFkZHJlc3MgPSB0aGlzLl9hZGRyZXNzO1xuICAgICAgICByZXN1bHQudG9waWNzLnB1c2goJzB4JyArIHRoaXMuc2lnbmF0dXJlKCkpO1xuICAgIH1cblxuICAgIHZhciBpbmRleGVkVG9waWNzID0gdGhpcy5fcGFyYW1zLmZpbHRlcihmdW5jdGlvbiAoaSkge1xuICAgICAgICByZXR1cm4gaS5pbmRleGVkID09PSB0cnVlO1xuICAgIH0pLm1hcChmdW5jdGlvbiAoaSkge1xuICAgICAgICB2YXIgdmFsdWUgPSBpbmRleGVkW2kubmFtZV07XG4gICAgICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8IHZhbHVlID09PSBudWxsKSB7XG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgaWYgKHV0aWxzLmlzQXJyYXkodmFsdWUpKSB7XG4gICAgICAgICAgICByZXR1cm4gdmFsdWUubWFwKGZ1bmN0aW9uICh2KSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuICcweCcgKyBjb2Rlci5lbmNvZGVQYXJhbShpLnR5cGUsIHYpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuICcweCcgKyBjb2Rlci5lbmNvZGVQYXJhbShpLnR5cGUsIHZhbHVlKTtcbiAgICB9KTtcblxuICAgIHJlc3VsdC50b3BpY3MgPSByZXN1bHQudG9waWNzLmNvbmNhdChpbmRleGVkVG9waWNzKTtcblxuICAgIHJldHVybiByZXN1bHQ7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGRlY29kZSBpbmRleGVkIHBhcmFtcyBhbmQgb3B0aW9uc1xuICpcbiAqIEBtZXRob2QgZGVjb2RlXG4gKiBAcGFyYW0ge09iamVjdH0gZGF0YVxuICogQHJldHVybiB7T2JqZWN0fSByZXN1bHQgb2JqZWN0IHdpdGggZGVjb2RlZCBpbmRleGVkICYmIG5vdCBpbmRleGVkIHBhcmFtc1xuICovXG5Tb2xpZGl0eUV2ZW50LnByb3RvdHlwZS5kZWNvZGUgPSBmdW5jdGlvbiAoZGF0YSkge1xuIFxuICAgIGRhdGEuZGF0YSA9IGRhdGEuZGF0YSB8fCAnJztcbiAgICBkYXRhLnRvcGljcyA9IGRhdGEudG9waWNzIHx8IFtdO1xuXG4gICAgdmFyIGFyZ1RvcGljcyA9IHRoaXMuX2Fub255bW91cyA/IGRhdGEudG9waWNzIDogZGF0YS50b3BpY3Muc2xpY2UoMSk7XG4gICAgdmFyIGluZGV4ZWREYXRhID0gYXJnVG9waWNzLm1hcChmdW5jdGlvbiAodG9waWNzKSB7IHJldHVybiB0b3BpY3Muc2xpY2UoMik7IH0pLmpvaW4oXCJcIik7XG4gICAgdmFyIGluZGV4ZWRQYXJhbXMgPSBjb2Rlci5kZWNvZGVQYXJhbXModGhpcy50eXBlcyh0cnVlKSwgaW5kZXhlZERhdGEpOyBcblxuICAgIHZhciBub3RJbmRleGVkRGF0YSA9IGRhdGEuZGF0YS5zbGljZSgyKTtcbiAgICB2YXIgbm90SW5kZXhlZFBhcmFtcyA9IGNvZGVyLmRlY29kZVBhcmFtcyh0aGlzLnR5cGVzKGZhbHNlKSwgbm90SW5kZXhlZERhdGEpO1xuICAgIFxuICAgIHZhciByZXN1bHQgPSBmb3JtYXR0ZXJzLm91dHB1dExvZ0Zvcm1hdHRlcihkYXRhKTtcbiAgICByZXN1bHQuZXZlbnQgPSB0aGlzLmRpc3BsYXlOYW1lKCk7XG4gICAgcmVzdWx0LmFkZHJlc3MgPSBkYXRhLmFkZHJlc3M7XG5cbiAgICByZXN1bHQuYXJncyA9IHRoaXMuX3BhcmFtcy5yZWR1Y2UoZnVuY3Rpb24gKGFjYywgY3VycmVudCkge1xuICAgICAgICBhY2NbY3VycmVudC5uYW1lXSA9IGN1cnJlbnQuaW5kZXhlZCA/IGluZGV4ZWRQYXJhbXMuc2hpZnQoKSA6IG5vdEluZGV4ZWRQYXJhbXMuc2hpZnQoKTtcbiAgICAgICAgcmV0dXJuIGFjYztcbiAgICB9LCB7fSk7XG5cbiAgICBkZWxldGUgcmVzdWx0LmRhdGE7XG4gICAgZGVsZXRlIHJlc3VsdC50b3BpY3M7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBjcmVhdGUgbmV3IGZpbHRlciBvYmplY3QgZnJvbSBldmVudFxuICpcbiAqIEBtZXRob2QgZXhlY3V0ZVxuICogQHBhcmFtIHtPYmplY3R9IGluZGV4ZWRcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zXG4gKiBAcmV0dXJuIHtPYmplY3R9IGZpbHRlciBvYmplY3RcbiAqL1xuU29saWRpdHlFdmVudC5wcm90b3R5cGUuZXhlY3V0ZSA9IGZ1bmN0aW9uIChpbmRleGVkLCBvcHRpb25zKSB7XG4gICAgdmFyIG8gPSB0aGlzLmVuY29kZShpbmRleGVkLCBvcHRpb25zKTtcbiAgICB2YXIgZm9ybWF0dGVyID0gdGhpcy5kZWNvZGUuYmluZCh0aGlzKTtcbiAgICByZXR1cm4gd2ViMy5ldGguZmlsdGVyKG8sIHVuZGVmaW5lZCwgdW5kZWZpbmVkLCBmb3JtYXR0ZXIpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBhdHRhY2ggZXZlbnQgdG8gY29udHJhY3Qgb2JqZWN0XG4gKlxuICogQG1ldGhvZCBhdHRhY2hUb0NvbnRyYWN0XG4gKiBAcGFyYW0ge0NvbnRyYWN0fVxuICovXG5Tb2xpZGl0eUV2ZW50LnByb3RvdHlwZS5hdHRhY2hUb0NvbnRyYWN0ID0gZnVuY3Rpb24gKGNvbnRyYWN0KSB7XG4gICAgdmFyIGV4ZWN1dGUgPSB0aGlzLmV4ZWN1dGUuYmluZCh0aGlzKTtcbiAgICB2YXIgZGlzcGxheU5hbWUgPSB0aGlzLmRpc3BsYXlOYW1lKCk7XG4gICAgaWYgKCFjb250cmFjdFtkaXNwbGF5TmFtZV0pIHtcbiAgICAgICAgY29udHJhY3RbZGlzcGxheU5hbWVdID0gZXhlY3V0ZTtcbiAgICB9XG4gICAgY29udHJhY3RbZGlzcGxheU5hbWVdW3RoaXMudHlwZU5hbWUoKV0gPSB0aGlzLmV4ZWN1dGUuYmluZCh0aGlzLCBjb250cmFjdCk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFNvbGlkaXR5RXZlbnQ7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIEBmaWxlIGZpbHRlci5qc1xuICogQGF1dGhvcnM6XG4gKiAgIEplZmZyZXkgV2lsY2tlIDxqZWZmQGV0aGRldi5jb20+XG4gKiAgIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogICBNYXJpYW4gT2FuY2VhIDxtYXJpYW5AZXRoZGV2LmNvbT5cbiAqICAgRmFiaWFuIFZvZ2Vsc3RlbGxlciA8ZmFiaWFuQGV0aGRldi5jb20+XG4gKiAgIEdhdiBXb29kIDxnQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE0XG4gKi9cblxudmFyIFJlcXVlc3RNYW5hZ2VyID0gcmVxdWlyZSgnLi9yZXF1ZXN0bWFuYWdlcicpO1xudmFyIGZvcm1hdHRlcnMgPSByZXF1aXJlKCcuL2Zvcm1hdHRlcnMnKTtcbnZhciB1dGlscyA9IHJlcXVpcmUoJy4uL3V0aWxzL3V0aWxzJyk7XG5cbi8qKlxuKiBDb252ZXJ0cyBhIGdpdmVuIHRvcGljIHRvIGEgaGV4IHN0cmluZywgYnV0IGFsc28gYWxsb3dzIG51bGwgdmFsdWVzLlxuKlxuKiBAcGFyYW0ge01peGVkfSB2YWx1ZVxuKiBAcmV0dXJuIHtTdHJpbmd9XG4qL1xudmFyIHRvVG9waWMgPSBmdW5jdGlvbih2YWx1ZSl7XG5cbiAgICBpZih2YWx1ZSA9PT0gbnVsbCB8fCB0eXBlb2YgdmFsdWUgPT09ICd1bmRlZmluZWQnKVxuICAgICAgICByZXR1cm4gbnVsbDtcblxuICAgIHZhbHVlID0gU3RyaW5nKHZhbHVlKTtcblxuICAgIGlmKHZhbHVlLmluZGV4T2YoJzB4JykgPT09IDApXG4gICAgICAgIHJldHVybiB2YWx1ZTtcbiAgICBlbHNlXG4gICAgICAgIHJldHVybiB1dGlscy5mcm9tQXNjaWkodmFsdWUpO1xufTtcblxuLy8vIFRoaXMgbWV0aG9kIHNob3VsZCBiZSBjYWxsZWQgb24gb3B0aW9ucyBvYmplY3QsIHRvIHZlcmlmeSBkZXByZWNhdGVkIHByb3BlcnRpZXMgJiYgbGF6eSBsb2FkIGR5bmFtaWMgb25lc1xuLy8vIEBwYXJhbSBzaG91bGQgYmUgc3RyaW5nIG9yIG9iamVjdFxuLy8vIEByZXR1cm5zIG9wdGlvbnMgc3RyaW5nIG9yIG9iamVjdFxudmFyIGdldE9wdGlvbnMgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuXG4gICAgaWYgKHV0aWxzLmlzU3RyaW5nKG9wdGlvbnMpKSB7XG4gICAgICAgIHJldHVybiBvcHRpb25zO1xuICAgIH0gXG5cbiAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcblxuICAgIC8vIG1ha2Ugc3VyZSB0b3BpY3MsIGdldCBjb252ZXJ0ZWQgdG8gaGV4XG4gICAgb3B0aW9ucy50b3BpY3MgPSBvcHRpb25zLnRvcGljcyB8fCBbXTtcbiAgICBvcHRpb25zLnRvcGljcyA9IG9wdGlvbnMudG9waWNzLm1hcChmdW5jdGlvbih0b3BpYyl7XG4gICAgICAgIHJldHVybiAodXRpbHMuaXNBcnJheSh0b3BpYykpID8gdG9waWMubWFwKHRvVG9waWMpIDogdG9Ub3BpYyh0b3BpYyk7XG4gICAgfSk7XG5cbiAgICAvLyBsYXp5IGxvYWRcbiAgICByZXR1cm4ge1xuICAgICAgICB0b3BpY3M6IG9wdGlvbnMudG9waWNzLFxuICAgICAgICB0bzogb3B0aW9ucy50byxcbiAgICAgICAgYWRkcmVzczogb3B0aW9ucy5hZGRyZXNzLFxuICAgICAgICBmcm9tQmxvY2s6IGZvcm1hdHRlcnMuaW5wdXRCbG9ja051bWJlckZvcm1hdHRlcihvcHRpb25zLmZyb21CbG9jayksXG4gICAgICAgIHRvQmxvY2s6IGZvcm1hdHRlcnMuaW5wdXRCbG9ja051bWJlckZvcm1hdHRlcihvcHRpb25zLnRvQmxvY2spIFxuICAgIH07IFxufTtcblxudmFyIEZpbHRlciA9IGZ1bmN0aW9uIChvcHRpb25zLCBtZXRob2RzLCBmb3JtYXR0ZXIpIHtcbiAgICB2YXIgaW1wbGVtZW50YXRpb24gPSB7fTtcbiAgICBtZXRob2RzLmZvckVhY2goZnVuY3Rpb24gKG1ldGhvZCkge1xuICAgICAgICBtZXRob2QuYXR0YWNoVG9PYmplY3QoaW1wbGVtZW50YXRpb24pO1xuICAgIH0pO1xuICAgIHRoaXMub3B0aW9ucyA9IGdldE9wdGlvbnMob3B0aW9ucyk7XG4gICAgdGhpcy5pbXBsZW1lbnRhdGlvbiA9IGltcGxlbWVudGF0aW9uO1xuICAgIHRoaXMuY2FsbGJhY2tzID0gW107XG4gICAgdGhpcy5mb3JtYXR0ZXIgPSBmb3JtYXR0ZXI7XG4gICAgdGhpcy5maWx0ZXJJZCA9IHRoaXMuaW1wbGVtZW50YXRpb24ubmV3RmlsdGVyKHRoaXMub3B0aW9ucyk7XG59O1xuXG5GaWx0ZXIucHJvdG90eXBlLndhdGNoID0gZnVuY3Rpb24gKGNhbGxiYWNrKSB7XG4gICAgdGhpcy5jYWxsYmFja3MucHVzaChjYWxsYmFjayk7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gICAgdmFyIG9uTWVzc2FnZSA9IGZ1bmN0aW9uIChlcnJvciwgbWVzc2FnZXMpIHtcbiAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgICByZXR1cm4gc2VsZi5jYWxsYmFja3MuZm9yRWFjaChmdW5jdGlvbiAoY2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgICBjYWxsYmFjayhlcnJvcik7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuXG4gICAgICAgIG1lc3NhZ2VzLmZvckVhY2goZnVuY3Rpb24gKG1lc3NhZ2UpIHtcbiAgICAgICAgICAgIG1lc3NhZ2UgPSBzZWxmLmZvcm1hdHRlciA/IHNlbGYuZm9ybWF0dGVyKG1lc3NhZ2UpIDogbWVzc2FnZTtcbiAgICAgICAgICAgIHNlbGYuY2FsbGJhY2tzLmZvckVhY2goZnVuY3Rpb24gKGNhbGxiYWNrKSB7XG4gICAgICAgICAgICAgICAgY2FsbGJhY2sobnVsbCwgbWVzc2FnZSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfTtcblxuICAgIC8vIGNhbGwgZ2V0RmlsdGVyTG9ncyBvbiBzdGFydFxuICAgIGlmICghdXRpbHMuaXNTdHJpbmcodGhpcy5vcHRpb25zKSkge1xuICAgICAgICB0aGlzLmdldChmdW5jdGlvbiAoZXJyLCBtZXNzYWdlcykge1xuICAgICAgICAgICAgLy8gZG9uJ3Qgc2VuZCBhbGwgdGhlIHJlc3BvbnNlcyB0byBhbGwgdGhlIHdhdGNoZXMgYWdhaW4uLi4ganVzdCB0byB0aGlzIG9uZVxuICAgICAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgICAgICAgIGNhbGxiYWNrKGVycik7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIG1lc3NhZ2VzLmZvckVhY2goZnVuY3Rpb24gKG1lc3NhZ2UpIHtcbiAgICAgICAgICAgICAgICBjYWxsYmFjayhudWxsLCBtZXNzYWdlKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBSZXF1ZXN0TWFuYWdlci5nZXRJbnN0YW5jZSgpLnN0YXJ0UG9sbGluZyh7XG4gICAgICAgIG1ldGhvZDogdGhpcy5pbXBsZW1lbnRhdGlvbi5wb2xsLmNhbGwsXG4gICAgICAgIHBhcmFtczogW3RoaXMuZmlsdGVySWRdLFxuICAgIH0sIHRoaXMuZmlsdGVySWQsIG9uTWVzc2FnZSwgdGhpcy5zdG9wV2F0Y2hpbmcuYmluZCh0aGlzKSk7XG59O1xuXG5GaWx0ZXIucHJvdG90eXBlLnN0b3BXYXRjaGluZyA9IGZ1bmN0aW9uICgpIHtcbiAgICBSZXF1ZXN0TWFuYWdlci5nZXRJbnN0YW5jZSgpLnN0b3BQb2xsaW5nKHRoaXMuZmlsdGVySWQpO1xuICAgIHRoaXMuaW1wbGVtZW50YXRpb24udW5pbnN0YWxsRmlsdGVyKHRoaXMuZmlsdGVySWQpO1xuICAgIHRoaXMuY2FsbGJhY2tzID0gW107XG59O1xuXG5GaWx0ZXIucHJvdG90eXBlLmdldCA9IGZ1bmN0aW9uIChjYWxsYmFjaykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBpZiAodXRpbHMuaXNGdW5jdGlvbihjYWxsYmFjaykpIHtcbiAgICAgICAgdGhpcy5pbXBsZW1lbnRhdGlvbi5nZXRMb2dzKHRoaXMuZmlsdGVySWQsIGZ1bmN0aW9uKGVyciwgcmVzKXtcbiAgICAgICAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgICAgICAgICBjYWxsYmFjayhlcnIpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBjYWxsYmFjayhudWxsLCByZXMubWFwKGZ1bmN0aW9uIChsb2cpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHNlbGYuZm9ybWF0dGVyID8gc2VsZi5mb3JtYXR0ZXIobG9nKSA6IGxvZztcbiAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHZhciBsb2dzID0gdGhpcy5pbXBsZW1lbnRhdGlvbi5nZXRMb2dzKHRoaXMuZmlsdGVySWQpO1xuICAgICAgICByZXR1cm4gbG9ncy5tYXAoZnVuY3Rpb24gKGxvZykge1xuICAgICAgICAgICAgcmV0dXJuIHNlbGYuZm9ybWF0dGVyID8gc2VsZi5mb3JtYXR0ZXIobG9nKSA6IGxvZztcbiAgICAgICAgfSk7XG4gICAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBGaWx0ZXI7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIFxuICogQGZpbGUgZm9ybWF0dGVycy5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBhdXRob3IgRmFiaWFuIFZvZ2Vsc3RlbGxlciA8ZmFiaWFuQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE1XG4gKi9cblxudmFyIHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMvdXRpbHMnKTtcbnZhciBjb25maWcgPSByZXF1aXJlKCcuLi91dGlscy9jb25maWcnKTtcblxuLyoqXG4gKiBTaG91bGQgdGhlIGZvcm1hdCBvdXRwdXQgdG8gYSBiaWcgbnVtYmVyXG4gKlxuICogQG1ldGhvZCBvdXRwdXRCaWdOdW1iZXJGb3JtYXR0ZXJcbiAqIEBwYXJhbSB7U3RyaW5nfE51bWJlcnxCaWdOdW1iZXJ9XG4gKiBAcmV0dXJucyB7QmlnTnVtYmVyfSBvYmplY3RcbiAqL1xudmFyIG91dHB1dEJpZ051bWJlckZvcm1hdHRlciA9IGZ1bmN0aW9uIChudW1iZXIpIHtcbiAgICByZXR1cm4gdXRpbHMudG9CaWdOdW1iZXIobnVtYmVyKTtcbn07XG5cbnZhciBpc1ByZWRlZmluZWRCbG9ja051bWJlciA9IGZ1bmN0aW9uIChibG9ja051bWJlcikge1xuICAgIHJldHVybiBibG9ja051bWJlciA9PT0gJ2xhdGVzdCcgfHwgYmxvY2tOdW1iZXIgPT09ICdwZW5kaW5nJyB8fCBibG9ja051bWJlciA9PT0gJ2VhcmxpZXN0Jztcbn07XG5cbnZhciBpbnB1dERlZmF1bHRCbG9ja051bWJlckZvcm1hdHRlciA9IGZ1bmN0aW9uIChibG9ja051bWJlcikge1xuICAgIGlmIChibG9ja051bWJlciA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBjb25maWcuZGVmYXVsdEJsb2NrO1xuICAgIH1cbiAgICByZXR1cm4gaW5wdXRCbG9ja051bWJlckZvcm1hdHRlcihibG9ja051bWJlcik7XG59O1xuXG52YXIgaW5wdXRCbG9ja051bWJlckZvcm1hdHRlciA9IGZ1bmN0aW9uIChibG9ja051bWJlcikge1xuICAgIGlmIChibG9ja051bWJlciA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfSBlbHNlIGlmIChpc1ByZWRlZmluZWRCbG9ja051bWJlcihibG9ja051bWJlcikpIHtcbiAgICAgICAgcmV0dXJuIGJsb2NrTnVtYmVyO1xuICAgIH1cbiAgICByZXR1cm4gdXRpbHMudG9IZXgoYmxvY2tOdW1iZXIpO1xufTtcblxuLyoqXG4gKiBGb3JtYXRzIHRoZSBpbnB1dCBvZiBhIHRyYW5zYWN0aW9uIGFuZCBjb252ZXJ0cyBhbGwgdmFsdWVzIHRvIEhFWFxuICpcbiAqIEBtZXRob2QgaW5wdXRUcmFuc2FjdGlvbkZvcm1hdHRlclxuICogQHBhcmFtIHtPYmplY3R9IHRyYW5zYWN0aW9uIG9wdGlvbnNcbiAqIEByZXR1cm5zIG9iamVjdFxuKi9cbnZhciBpbnB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyID0gZnVuY3Rpb24gKG9wdGlvbnMpe1xuXG4gICAgb3B0aW9ucy5mcm9tID0gb3B0aW9ucy5mcm9tIHx8IGNvbmZpZy5kZWZhdWx0QWNjb3VudDtcblxuICAgIC8vIG1ha2UgY29kZSAtPiBkYXRhXG4gICAgaWYgKG9wdGlvbnMuY29kZSkge1xuICAgICAgICBvcHRpb25zLmRhdGEgPSBvcHRpb25zLmNvZGU7XG4gICAgICAgIGRlbGV0ZSBvcHRpb25zLmNvZGU7XG4gICAgfVxuXG4gICAgWydnYXNQcmljZScsICdnYXMnLCAndmFsdWUnLCAnbm9uY2UnXS5maWx0ZXIoZnVuY3Rpb24gKGtleSkge1xuICAgICAgICByZXR1cm4gb3B0aW9uc1trZXldICE9PSB1bmRlZmluZWQ7XG4gICAgfSkuZm9yRWFjaChmdW5jdGlvbihrZXkpe1xuICAgICAgICBvcHRpb25zW2tleV0gPSB1dGlscy5mcm9tRGVjaW1hbChvcHRpb25zW2tleV0pO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIG9wdGlvbnM7IFxufTtcblxuLyoqXG4gKiBGb3JtYXRzIHRoZSBvdXRwdXQgb2YgYSB0cmFuc2FjdGlvbiB0byBpdHMgcHJvcGVyIHZhbHVlc1xuICogXG4gKiBAbWV0aG9kIG91dHB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyXG4gKiBAcGFyYW0ge09iamVjdH0gdHJhbnNhY3Rpb25cbiAqIEByZXR1cm5zIHtPYmplY3R9IHRyYW5zYWN0aW9uXG4qL1xudmFyIG91dHB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyID0gZnVuY3Rpb24gKHR4KXtcbiAgICB0eC5ibG9ja051bWJlciA9IHV0aWxzLnRvRGVjaW1hbCh0eC5ibG9ja051bWJlcik7XG4gICAgdHgudHJhbnNhY3Rpb25JbmRleCA9IHV0aWxzLnRvRGVjaW1hbCh0eC50cmFuc2FjdGlvbkluZGV4KTtcbiAgICB0eC5ub25jZSA9IHV0aWxzLnRvRGVjaW1hbCh0eC5ub25jZSk7XG4gICAgdHguZ2FzID0gdXRpbHMudG9EZWNpbWFsKHR4Lmdhcyk7XG4gICAgdHguZ2FzUHJpY2UgPSB1dGlscy50b0JpZ051bWJlcih0eC5nYXNQcmljZSk7XG4gICAgdHgudmFsdWUgPSB1dGlscy50b0JpZ051bWJlcih0eC52YWx1ZSk7XG4gICAgcmV0dXJuIHR4O1xufTtcblxuLyoqXG4gKiBGb3JtYXRzIHRoZSBvdXRwdXQgb2YgYSBibG9jayB0byBpdHMgcHJvcGVyIHZhbHVlc1xuICpcbiAqIEBtZXRob2Qgb3V0cHV0QmxvY2tGb3JtYXR0ZXJcbiAqIEBwYXJhbSB7T2JqZWN0fSBibG9jayBvYmplY3QgXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBibG9jayBvYmplY3RcbiovXG52YXIgb3V0cHV0QmxvY2tGb3JtYXR0ZXIgPSBmdW5jdGlvbihibG9jaykge1xuXG4gICAgLy8gdHJhbnNmb3JtIHRvIG51bWJlclxuICAgIGJsb2NrLmdhc0xpbWl0ID0gdXRpbHMudG9EZWNpbWFsKGJsb2NrLmdhc0xpbWl0KTtcbiAgICBibG9jay5nYXNVc2VkID0gdXRpbHMudG9EZWNpbWFsKGJsb2NrLmdhc1VzZWQpO1xuICAgIGJsb2NrLnNpemUgPSB1dGlscy50b0RlY2ltYWwoYmxvY2suc2l6ZSk7XG4gICAgYmxvY2sudGltZXN0YW1wID0gdXRpbHMudG9EZWNpbWFsKGJsb2NrLnRpbWVzdGFtcCk7XG4gICAgYmxvY2subnVtYmVyID0gdXRpbHMudG9EZWNpbWFsKGJsb2NrLm51bWJlcik7XG5cbiAgICBibG9jay5kaWZmaWN1bHR5ID0gdXRpbHMudG9CaWdOdW1iZXIoYmxvY2suZGlmZmljdWx0eSk7XG4gICAgYmxvY2sudG90YWxEaWZmaWN1bHR5ID0gdXRpbHMudG9CaWdOdW1iZXIoYmxvY2sudG90YWxEaWZmaWN1bHR5KTtcblxuICAgIGlmICh1dGlscy5pc0FycmF5KGJsb2NrLnRyYW5zYWN0aW9ucykpIHtcbiAgICAgICAgYmxvY2sudHJhbnNhY3Rpb25zLmZvckVhY2goZnVuY3Rpb24oaXRlbSl7XG4gICAgICAgICAgICBpZighdXRpbHMuaXNTdHJpbmcoaXRlbSkpXG4gICAgICAgICAgICAgICAgcmV0dXJuIG91dHB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyKGl0ZW0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gYmxvY2s7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgdGhlIG91dHB1dCBvZiBhIGxvZ1xuICogXG4gKiBAbWV0aG9kIG91dHB1dExvZ0Zvcm1hdHRlclxuICogQHBhcmFtIHtPYmplY3R9IGxvZyBvYmplY3RcbiAqIEByZXR1cm5zIHtPYmplY3R9IGxvZ1xuKi9cbnZhciBvdXRwdXRMb2dGb3JtYXR0ZXIgPSBmdW5jdGlvbihsb2cpIHtcbiAgICBpZiAobG9nID09PSBudWxsKSB7IC8vICdwZW5kaW5nJyAmJiAnbGF0ZXN0JyBmaWx0ZXJzIGFyZSBudWxsc1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBsb2cuYmxvY2tOdW1iZXIgPSB1dGlscy50b0RlY2ltYWwobG9nLmJsb2NrTnVtYmVyKTtcbiAgICBsb2cudHJhbnNhY3Rpb25JbmRleCA9IHV0aWxzLnRvRGVjaW1hbChsb2cudHJhbnNhY3Rpb25JbmRleCk7XG4gICAgbG9nLmxvZ0luZGV4ID0gdXRpbHMudG9EZWNpbWFsKGxvZy5sb2dJbmRleCk7XG5cbiAgICByZXR1cm4gbG9nO1xufTtcblxuLyoqXG4gKiBGb3JtYXRzIHRoZSBpbnB1dCBvZiBhIHdoaXNwZXIgcG9zdCBhbmQgY29udmVydHMgYWxsIHZhbHVlcyB0byBIRVhcbiAqXG4gKiBAbWV0aG9kIGlucHV0UG9zdEZvcm1hdHRlclxuICogQHBhcmFtIHtPYmplY3R9IHRyYW5zYWN0aW9uIG9iamVjdFxuICogQHJldHVybnMge09iamVjdH1cbiovXG52YXIgaW5wdXRQb3N0Rm9ybWF0dGVyID0gZnVuY3Rpb24ocG9zdCkge1xuXG4gICAgcG9zdC5wYXlsb2FkID0gdXRpbHMudG9IZXgocG9zdC5wYXlsb2FkKTtcbiAgICBwb3N0LnR0bCA9IHV0aWxzLmZyb21EZWNpbWFsKHBvc3QudHRsKTtcbiAgICBwb3N0LndvcmtUb1Byb3ZlID0gdXRpbHMuZnJvbURlY2ltYWwocG9zdC53b3JrVG9Qcm92ZSk7XG4gICAgcG9zdC5wcmlvcml0eSA9IHV0aWxzLmZyb21EZWNpbWFsKHBvc3QucHJpb3JpdHkpO1xuXG4gICAgLy8gZmFsbGJhY2tcbiAgICBpZiAoIXV0aWxzLmlzQXJyYXkocG9zdC50b3BpY3MpKSB7XG4gICAgICAgIHBvc3QudG9waWNzID0gcG9zdC50b3BpY3MgPyBbcG9zdC50b3BpY3NdIDogW107XG4gICAgfVxuXG4gICAgLy8gZm9ybWF0IHRoZSBmb2xsb3dpbmcgb3B0aW9uc1xuICAgIHBvc3QudG9waWNzID0gcG9zdC50b3BpY3MubWFwKGZ1bmN0aW9uKHRvcGljKXtcbiAgICAgICAgcmV0dXJuIHV0aWxzLmZyb21Bc2NpaSh0b3BpYyk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gcG9zdDsgXG59O1xuXG4vKipcbiAqIEZvcm1hdHMgdGhlIG91dHB1dCBvZiBhIHJlY2VpdmVkIHBvc3QgbWVzc2FnZVxuICpcbiAqIEBtZXRob2Qgb3V0cHV0UG9zdEZvcm1hdHRlclxuICogQHBhcmFtIHtPYmplY3R9XG4gKiBAcmV0dXJucyB7T2JqZWN0fVxuICovXG52YXIgb3V0cHV0UG9zdEZvcm1hdHRlciA9IGZ1bmN0aW9uKHBvc3Qpe1xuXG4gICAgcG9zdC5leHBpcnkgPSB1dGlscy50b0RlY2ltYWwocG9zdC5leHBpcnkpO1xuICAgIHBvc3Quc2VudCA9IHV0aWxzLnRvRGVjaW1hbChwb3N0LnNlbnQpO1xuICAgIHBvc3QudHRsID0gdXRpbHMudG9EZWNpbWFsKHBvc3QudHRsKTtcbiAgICBwb3N0LndvcmtQcm92ZWQgPSB1dGlscy50b0RlY2ltYWwocG9zdC53b3JrUHJvdmVkKTtcbiAgICBwb3N0LnBheWxvYWRSYXcgPSBwb3N0LnBheWxvYWQ7XG4gICAgcG9zdC5wYXlsb2FkID0gdXRpbHMudG9Bc2NpaShwb3N0LnBheWxvYWQpO1xuXG4gICAgaWYgKHV0aWxzLmlzSnNvbihwb3N0LnBheWxvYWQpKSB7XG4gICAgICAgIHBvc3QucGF5bG9hZCA9IEpTT04ucGFyc2UocG9zdC5wYXlsb2FkKTtcbiAgICB9XG5cbiAgICAvLyBmb3JtYXQgdGhlIGZvbGxvd2luZyBvcHRpb25zXG4gICAgaWYgKCFwb3N0LnRvcGljcykge1xuICAgICAgICBwb3N0LnRvcGljcyA9IFtdO1xuICAgIH1cbiAgICBwb3N0LnRvcGljcyA9IHBvc3QudG9waWNzLm1hcChmdW5jdGlvbih0b3BpYyl7XG4gICAgICAgIHJldHVybiB1dGlscy50b0FzY2lpKHRvcGljKTtcbiAgICB9KTtcblxuICAgIHJldHVybiBwb3N0O1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgaW5wdXREZWZhdWx0QmxvY2tOdW1iZXJGb3JtYXR0ZXI6IGlucHV0RGVmYXVsdEJsb2NrTnVtYmVyRm9ybWF0dGVyLFxuICAgIGlucHV0QmxvY2tOdW1iZXJGb3JtYXR0ZXI6IGlucHV0QmxvY2tOdW1iZXJGb3JtYXR0ZXIsXG4gICAgaW5wdXRUcmFuc2FjdGlvbkZvcm1hdHRlcjogaW5wdXRUcmFuc2FjdGlvbkZvcm1hdHRlcixcbiAgICBpbnB1dFBvc3RGb3JtYXR0ZXI6IGlucHV0UG9zdEZvcm1hdHRlcixcbiAgICBvdXRwdXRCaWdOdW1iZXJGb3JtYXR0ZXI6IG91dHB1dEJpZ051bWJlckZvcm1hdHRlcixcbiAgICBvdXRwdXRUcmFuc2FjdGlvbkZvcm1hdHRlcjogb3V0cHV0VHJhbnNhY3Rpb25Gb3JtYXR0ZXIsXG4gICAgb3V0cHV0QmxvY2tGb3JtYXR0ZXI6IG91dHB1dEJsb2NrRm9ybWF0dGVyLFxuICAgIG91dHB1dExvZ0Zvcm1hdHRlcjogb3V0cHV0TG9nRm9ybWF0dGVyLFxuICAgIG91dHB1dFBvc3RGb3JtYXR0ZXI6IG91dHB1dFBvc3RGb3JtYXR0ZXJcbn07XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqXG4gKiBAZmlsZSBmdW5jdGlvbi5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgd2ViMyA9IHJlcXVpcmUoJy4uL3dlYjMnKTtcbnZhciBjb2RlciA9IHJlcXVpcmUoJy4uL3NvbGlkaXR5L2NvZGVyJyk7XG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xudmFyIHNoYTMgPSByZXF1aXJlKCcuLi91dGlscy9zaGEzJyk7XG5cbi8qKlxuICogVGhpcyBwcm90b3R5cGUgc2hvdWxkIGJlIHVzZWQgdG8gY2FsbC9zZW5kVHJhbnNhY3Rpb24gdG8gc29saWRpdHkgZnVuY3Rpb25zXG4gKi9cbnZhciBTb2xpZGl0eUZ1bmN0aW9uID0gZnVuY3Rpb24gKGpzb24sIGFkZHJlc3MpIHtcbiAgICB0aGlzLl9pbnB1dFR5cGVzID0ganNvbi5pbnB1dHMubWFwKGZ1bmN0aW9uIChpKSB7XG4gICAgICAgIHJldHVybiBpLnR5cGU7XG4gICAgfSk7XG4gICAgdGhpcy5fb3V0cHV0VHlwZXMgPSBqc29uLm91dHB1dHMubWFwKGZ1bmN0aW9uIChpKSB7XG4gICAgICAgIHJldHVybiBpLnR5cGU7XG4gICAgfSk7XG4gICAgdGhpcy5fY29uc3RhbnQgPSBqc29uLmNvbnN0YW50O1xuICAgIHRoaXMuX25hbWUgPSB1dGlscy50cmFuc2Zvcm1Ub0Z1bGxOYW1lKGpzb24pO1xuICAgIHRoaXMuX2FkZHJlc3MgPSBhZGRyZXNzO1xufTtcblxuU29saWRpdHlGdW5jdGlvbi5wcm90b3R5cGUuZXh0cmFjdENhbGxiYWNrID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICBpZiAodXRpbHMuaXNGdW5jdGlvbihhcmdzW2FyZ3MubGVuZ3RoIC0gMV0pKSB7XG4gICAgICAgIHJldHVybiBhcmdzLnBvcCgpOyAvLyBtb2RpZnkgdGhlIGFyZ3MgYXJyYXkhXG4gICAgfVxufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBjcmVhdGUgcGF5bG9hZCBmcm9tIGFyZ3VtZW50c1xuICpcbiAqIEBtZXRob2QgdG9QYXlsb2FkXG4gKiBAcGFyYW0ge0FycmF5fSBzb2xpZGl0eSBmdW5jdGlvbiBwYXJhbXNcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25hbCBwYXlsb2FkIG9wdGlvbnNcbiAqL1xuU29saWRpdHlGdW5jdGlvbi5wcm90b3R5cGUudG9QYXlsb2FkID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICB2YXIgb3B0aW9ucyA9IHt9O1xuICAgIGlmIChhcmdzLmxlbmd0aCA+IHRoaXMuX2lucHV0VHlwZXMubGVuZ3RoICYmIHV0aWxzLmlzT2JqZWN0KGFyZ3NbYXJncy5sZW5ndGggLTFdKSkge1xuICAgICAgICBvcHRpb25zID0gYXJnc1thcmdzLmxlbmd0aCAtIDFdO1xuICAgIH1cbiAgICBvcHRpb25zLnRvID0gdGhpcy5fYWRkcmVzcztcbiAgICBvcHRpb25zLmRhdGEgPSAnMHgnICsgdGhpcy5zaWduYXR1cmUoKSArIGNvZGVyLmVuY29kZVBhcmFtcyh0aGlzLl9pbnB1dFR5cGVzLCBhcmdzKTtcbiAgICByZXR1cm4gb3B0aW9ucztcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZ2V0IGZ1bmN0aW9uIHNpZ25hdHVyZVxuICpcbiAqIEBtZXRob2Qgc2lnbmF0dXJlXG4gKiBAcmV0dXJuIHtTdHJpbmd9IGZ1bmN0aW9uIHNpZ25hdHVyZVxuICovXG5Tb2xpZGl0eUZ1bmN0aW9uLnByb3RvdHlwZS5zaWduYXR1cmUgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHNoYTModGhpcy5fbmFtZSkuc2xpY2UoMCwgOCk7XG59O1xuXG5cblNvbGlkaXR5RnVuY3Rpb24ucHJvdG90eXBlLnVucGFja091dHB1dCA9IGZ1bmN0aW9uIChvdXRwdXQpIHtcbiAgICBpZiAoIW91dHB1dCkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgb3V0cHV0ID0gb3V0cHV0Lmxlbmd0aCA+PSAyID8gb3V0cHV0LnNsaWNlKDIpIDogb3V0cHV0O1xuICAgIHZhciByZXN1bHQgPSBjb2Rlci5kZWNvZGVQYXJhbXModGhpcy5fb3V0cHV0VHlwZXMsIG91dHB1dCk7XG4gICAgcmV0dXJuIHJlc3VsdC5sZW5ndGggPT09IDEgPyByZXN1bHRbMF0gOiByZXN1bHQ7XG59O1xuXG4vKipcbiAqIENhbGxzIGEgY29udHJhY3QgZnVuY3Rpb24uXG4gKlxuICogQG1ldGhvZCBjYWxsXG4gKiBAcGFyYW0gey4uLk9iamVjdH0gQ29udHJhY3QgZnVuY3Rpb24gYXJndW1lbnRzXG4gKiBAcGFyYW0ge2Z1bmN0aW9ufSBJZiB0aGUgbGFzdCBhcmd1bWVudCBpcyBhIGZ1bmN0aW9uLCB0aGUgY29udHJhY3QgZnVuY3Rpb25cbiAqICAgY2FsbCB3aWxsIGJlIGFzeW5jaHJvbm91cywgYW5kIHRoZSBjYWxsYmFjayB3aWxsIGJlIHBhc3NlZCB0aGVcbiAqICAgZXJyb3IgYW5kIHJlc3VsdC5cbiAqIEByZXR1cm4ge1N0cmluZ30gb3V0cHV0IGJ5dGVzXG4gKi9cblNvbGlkaXR5RnVuY3Rpb24ucHJvdG90eXBlLmNhbGwgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGFyZ3MgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMpLmZpbHRlcihmdW5jdGlvbiAoYSkge3JldHVybiBhICE9PSB1bmRlZmluZWQ7IH0pO1xuICAgIHZhciBjYWxsYmFjayA9IHRoaXMuZXh0cmFjdENhbGxiYWNrKGFyZ3MpO1xuICAgIHZhciBwYXlsb2FkID0gdGhpcy50b1BheWxvYWQoYXJncyk7XG5cbiAgICBpZiAoIWNhbGxiYWNrKSB7XG4gICAgICAgIHZhciBvdXRwdXQgPSB3ZWIzLmV0aC5jYWxsKHBheWxvYWQpO1xuICAgICAgICByZXR1cm4gdGhpcy51bnBhY2tPdXRwdXQob3V0cHV0KTtcbiAgICB9IFxuICAgICAgICBcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgd2ViMy5ldGguY2FsbChwYXlsb2FkLCBmdW5jdGlvbiAoZXJyb3IsIG91dHB1dCkge1xuICAgICAgICBjYWxsYmFjayhlcnJvciwgc2VsZi51bnBhY2tPdXRwdXQob3V0cHV0KSk7XG4gICAgfSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIHNlbmRUcmFuc2FjdGlvbiB0byBzb2xpZGl0eSBmdW5jdGlvblxuICpcbiAqIEBtZXRob2Qgc2VuZFRyYW5zYWN0aW9uXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9uc1xuICovXG5Tb2xpZGl0eUZ1bmN0aW9uLnByb3RvdHlwZS5zZW5kVHJhbnNhY3Rpb24gPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGFyZ3MgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMpLmZpbHRlcihmdW5jdGlvbiAoYSkge3JldHVybiBhICE9PSB1bmRlZmluZWQ7IH0pO1xuICAgIHZhciBjYWxsYmFjayA9IHRoaXMuZXh0cmFjdENhbGxiYWNrKGFyZ3MpO1xuICAgIHZhciBwYXlsb2FkID0gdGhpcy50b1BheWxvYWQoYXJncyk7XG5cbiAgICBpZiAoIWNhbGxiYWNrKSB7XG4gICAgICAgIHJldHVybiB3ZWIzLmV0aC5zZW5kVHJhbnNhY3Rpb24ocGF5bG9hZCk7XG4gICAgfVxuXG4gICAgd2ViMy5ldGguc2VuZFRyYW5zYWN0aW9uKHBheWxvYWQsIGNhbGxiYWNrKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZXN0aW1hdGVHYXMgb2Ygc29saWRpdHkgZnVuY3Rpb25cbiAqXG4gKiBAbWV0aG9kIGVzdGltYXRlR2FzXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9uc1xuICovXG5Tb2xpZGl0eUZ1bmN0aW9uLnByb3RvdHlwZS5lc3RpbWF0ZUdhcyA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cyk7XG4gICAgdmFyIGNhbGxiYWNrID0gdGhpcy5leHRyYWN0Q2FsbGJhY2soYXJncyk7XG4gICAgdmFyIHBheWxvYWQgPSB0aGlzLnRvUGF5bG9hZChhcmdzKTtcblxuICAgIGlmICghY2FsbGJhY2spIHtcbiAgICAgICAgcmV0dXJuIHdlYjMuZXRoLmVzdGltYXRlR2FzKHBheWxvYWQpO1xuICAgIH1cblxuICAgIHdlYjMuZXRoLmVzdGltYXRlR2FzKHBheWxvYWQsIGNhbGxiYWNrKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZ2V0IGZ1bmN0aW9uIGRpc3BsYXkgbmFtZVxuICpcbiAqIEBtZXRob2QgZGlzcGxheU5hbWVcbiAqIEByZXR1cm4ge1N0cmluZ30gZGlzcGxheSBuYW1lIG9mIHRoZSBmdW5jdGlvblxuICovXG5Tb2xpZGl0eUZ1bmN0aW9uLnByb3RvdHlwZS5kaXNwbGF5TmFtZSA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gdXRpbHMuZXh0cmFjdERpc3BsYXlOYW1lKHRoaXMuX25hbWUpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBnZXQgZnVuY3Rpb24gdHlwZSBuYW1lXG4gKlxuICogQG1ldGhvZCB0eXBlTmFtZVxuICogQHJldHVybiB7U3RyaW5nfSB0eXBlIG5hbWUgb2YgdGhlIGZ1bmN0aW9uXG4gKi9cblNvbGlkaXR5RnVuY3Rpb24ucHJvdG90eXBlLnR5cGVOYW1lID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB1dGlscy5leHRyYWN0VHlwZU5hbWUodGhpcy5fbmFtZSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IHJwYyByZXF1ZXN0cyBmcm9tIHNvbGlkaXR5IGZ1bmN0aW9uXG4gKlxuICogQG1ldGhvZCByZXF1ZXN0XG4gKiBAcmV0dXJucyB7T2JqZWN0fVxuICovXG5Tb2xpZGl0eUZ1bmN0aW9uLnByb3RvdHlwZS5yZXF1ZXN0ID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKTtcbiAgICB2YXIgY2FsbGJhY2sgPSB0aGlzLmV4dHJhY3RDYWxsYmFjayhhcmdzKTtcbiAgICB2YXIgcGF5bG9hZCA9IHRoaXMudG9QYXlsb2FkKGFyZ3MpO1xuICAgIHZhciBmb3JtYXQgPSB0aGlzLnVucGFja091dHB1dC5iaW5kKHRoaXMpO1xuICAgIFxuICAgIHJldHVybiB7XG4gICAgICAgIGNhbGxiYWNrOiBjYWxsYmFjayxcbiAgICAgICAgcGF5bG9hZDogcGF5bG9hZCwgXG4gICAgICAgIGZvcm1hdDogZm9ybWF0XG4gICAgfTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBleGVjdXRlIGZ1bmN0aW9uXG4gKlxuICogQG1ldGhvZCBleGVjdXRlXG4gKi9cblNvbGlkaXR5RnVuY3Rpb24ucHJvdG90eXBlLmV4ZWN1dGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIHRyYW5zYWN0aW9uID0gIXRoaXMuX2NvbnN0YW50O1xuXG4gICAgLy8gc2VuZCB0cmFuc2FjdGlvblxuICAgIGlmICh0cmFuc2FjdGlvbikge1xuICAgICAgICByZXR1cm4gdGhpcy5zZW5kVHJhbnNhY3Rpb24uYXBwbHkodGhpcywgQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKSk7XG4gICAgfVxuXG4gICAgLy8gY2FsbFxuICAgIHJldHVybiB0aGlzLmNhbGwuYXBwbHkodGhpcywgQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gYXR0YWNoIGZ1bmN0aW9uIHRvIGNvbnRyYWN0XG4gKlxuICogQG1ldGhvZCBhdHRhY2hUb0NvbnRyYWN0XG4gKiBAcGFyYW0ge0NvbnRyYWN0fVxuICovXG5Tb2xpZGl0eUZ1bmN0aW9uLnByb3RvdHlwZS5hdHRhY2hUb0NvbnRyYWN0ID0gZnVuY3Rpb24gKGNvbnRyYWN0KSB7XG4gICAgdmFyIGV4ZWN1dGUgPSB0aGlzLmV4ZWN1dGUuYmluZCh0aGlzKTtcbiAgICBleGVjdXRlLnJlcXVlc3QgPSB0aGlzLnJlcXVlc3QuYmluZCh0aGlzKTtcbiAgICBleGVjdXRlLmNhbGwgPSB0aGlzLmNhbGwuYmluZCh0aGlzKTtcbiAgICBleGVjdXRlLnNlbmRUcmFuc2FjdGlvbiA9IHRoaXMuc2VuZFRyYW5zYWN0aW9uLmJpbmQodGhpcyk7XG4gICAgZXhlY3V0ZS5lc3RpbWF0ZUdhcyA9IHRoaXMuZXN0aW1hdGVHYXMuYmluZCh0aGlzKTtcbiAgICB2YXIgZGlzcGxheU5hbWUgPSB0aGlzLmRpc3BsYXlOYW1lKCk7XG4gICAgaWYgKCFjb250cmFjdFtkaXNwbGF5TmFtZV0pIHtcbiAgICAgICAgY29udHJhY3RbZGlzcGxheU5hbWVdID0gZXhlY3V0ZTtcbiAgICB9XG4gICAgY29udHJhY3RbZGlzcGxheU5hbWVdW3RoaXMudHlwZU5hbWUoKV0gPSBleGVjdXRlOyAvLyBjaXJjdWxhciEhISFcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gU29saWRpdHlGdW5jdGlvbjtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogQGZpbGUgaHR0cHByb3ZpZGVyLmpzXG4gKiBAYXV0aG9yczpcbiAqICAgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiAgIE1hcmlhbiBPYW5jZWEgPG1hcmlhbkBldGhkZXYuY29tPlxuICogICBGYWJpYW4gVm9nZWxzdGVsbGVyIDxmYWJpYW5AZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTRcbiAqL1xuXG5cInVzZSBzdHJpY3RcIjtcblxudmFyIFhNTEh0dHBSZXF1ZXN0ID0gcmVxdWlyZSgneG1saHR0cHJlcXVlc3QnKS5YTUxIdHRwUmVxdWVzdDsgLy8ganNoaW50IGlnbm9yZTpsaW5lXG52YXIgZXJyb3JzID0gcmVxdWlyZSgnLi9lcnJvcnMnKTtcblxudmFyIEh0dHBQcm92aWRlciA9IGZ1bmN0aW9uIChob3N0KSB7XG4gICAgdGhpcy5ob3N0ID0gaG9zdCB8fCAnaHR0cDovL2xvY2FsaG9zdDo4NTQ1Jztcbn07XG5cbkh0dHBQcm92aWRlci5wcm90b3R5cGUuc2VuZCA9IGZ1bmN0aW9uIChwYXlsb2FkKSB7XG4gICAgdmFyIHJlcXVlc3QgPSBuZXcgWE1MSHR0cFJlcXVlc3QoKTtcblxuICAgIHJlcXVlc3Qub3BlbignUE9TVCcsIHRoaXMuaG9zdCwgZmFsc2UpO1xuICAgIFxuICAgIHRyeSB7XG4gICAgICAgIHJlcXVlc3Quc2VuZChKU09OLnN0cmluZ2lmeShwYXlsb2FkKSk7XG4gICAgfSBjYXRjaChlcnJvcikge1xuICAgICAgICB0aHJvdyBlcnJvcnMuSW52YWxpZENvbm5lY3Rpb24odGhpcy5ob3N0KTtcbiAgICB9XG5cblxuICAgIC8vIGNoZWNrIHJlcXVlc3Quc3RhdHVzXG4gICAgLy8gVE9ETzogdGhyb3cgYW4gZXJyb3IgaGVyZSEgaXQgY2Fubm90IHNpbGVudGx5IGZhaWwhISFcbiAgICAvL2lmIChyZXF1ZXN0LnN0YXR1cyAhPT0gMjAwKSB7XG4gICAgICAgIC8vcmV0dXJuO1xuICAgIC8vfVxuXG4gICAgdmFyIHJlc3VsdCA9IHJlcXVlc3QucmVzcG9uc2VUZXh0O1xuXG4gICAgdHJ5IHtcbiAgICAgICAgcmVzdWx0ID0gSlNPTi5wYXJzZShyZXN1bHQpO1xuICAgIH0gY2F0Y2goZSkge1xuICAgICAgICB0aHJvdyBlcnJvcnMuSW52YWxpZFJlc3BvbnNlKHJlc3VsdCk7ICAgICAgICAgICAgICAgIFxuICAgIH1cblxuICAgIHJldHVybiByZXN1bHQ7XG59O1xuXG5IdHRwUHJvdmlkZXIucHJvdG90eXBlLnNlbmRBc3luYyA9IGZ1bmN0aW9uIChwYXlsb2FkLCBjYWxsYmFjaykge1xuICAgIHZhciByZXF1ZXN0ID0gbmV3IFhNTEh0dHBSZXF1ZXN0KCk7XG4gICAgcmVxdWVzdC5vbnJlYWR5c3RhdGVjaGFuZ2UgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgaWYgKHJlcXVlc3QucmVhZHlTdGF0ZSA9PT0gNCkge1xuICAgICAgICAgICAgdmFyIHJlc3VsdCA9IHJlcXVlc3QucmVzcG9uc2VUZXh0O1xuICAgICAgICAgICAgdmFyIGVycm9yID0gbnVsbDtcblxuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICByZXN1bHQgPSBKU09OLnBhcnNlKHJlc3VsdCk7XG4gICAgICAgICAgICB9IGNhdGNoKGUpIHtcbiAgICAgICAgICAgICAgICBlcnJvciA9IGVycm9ycy5JbnZhbGlkUmVzcG9uc2UocmVzdWx0KTsgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNhbGxiYWNrKGVycm9yLCByZXN1bHQpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHJlcXVlc3Qub3BlbignUE9TVCcsIHRoaXMuaG9zdCwgdHJ1ZSk7XG5cbiAgICB0cnkge1xuICAgICAgICByZXF1ZXN0LnNlbmQoSlNPTi5zdHJpbmdpZnkocGF5bG9hZCkpO1xuICAgIH0gY2F0Y2goZXJyb3IpIHtcbiAgICAgICAgY2FsbGJhY2soZXJyb3JzLkludmFsaWRDb25uZWN0aW9uKHRoaXMuaG9zdCkpO1xuICAgIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gSHR0cFByb3ZpZGVyO1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBcbiAqIEBmaWxlIGljYXAuanNcbiAqIEBhdXRob3IgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE1XG4gKi9cblxudmFyIHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMvdXRpbHMnKTtcblxuLyoqXG4gKiBUaGlzIHByb3RvdHlwZSBzaG91bGQgYmUgdXNlZCB0byBleHRyYWN0IG5lY2Vzc2FyeSBpbmZvcm1hdGlvbiBmcm9tIGliYW4gYWRkcmVzc1xuICpcbiAqIEBwYXJhbSB7U3RyaW5nfSBpYmFuXG4gKi9cbnZhciBJQ0FQID0gZnVuY3Rpb24gKGliYW4pIHtcbiAgICB0aGlzLl9pYmFuID0gaWJhbjtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBjaGVjayBpZiBpY2FwIGlzIGNvcnJlY3RcbiAqXG4gKiBAbWV0aG9kIGlzVmFsaWRcbiAqIEByZXR1cm5zIHtCb29sZWFufSB0cnVlIGlmIGl0IGlzLCBvdGhlcndpc2UgZmFsc2VcbiAqL1xuSUNBUC5wcm90b3R5cGUuaXNWYWxpZCA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gdXRpbHMuaXNJQkFOKHRoaXMuX2liYW4pO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGNoZWNrIGlmIGliYW4gbnVtYmVyIGlzIGRpcmVjdFxuICpcbiAqIEBtZXRob2QgaXNEaXJlY3RcbiAqIEByZXR1cm5zIHtCb29sZWFufSB0cnVlIGlmIGl0IGlzLCBvdGhlcndpc2UgZmFsc2VcbiAqL1xuSUNBUC5wcm90b3R5cGUuaXNEaXJlY3QgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2liYW4ubGVuZ3RoID09PSAzNDtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBjaGVjayBpZiBpYmFuIG51bWJlciBpZiBpbmRpcmVjdFxuICpcbiAqIEBtZXRob2QgaXNJbmRpcmVjdFxuICogQHJldHVybnMge0Jvb2xlYW59IHRydWUgaWYgaXQgaXMsIG90aGVyd2lzZSBmYWxzZVxuICovXG5JQ0FQLnByb3RvdHlwZS5pc0luZGlyZWN0ID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB0aGlzLl9pYmFuLmxlbmd0aCA9PT0gMjA7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IGliYW4gY2hlY2tzdW1cbiAqIFVzZXMgdGhlIG1vZC05Ny0xMCBjaGVja3N1bW1pbmcgcHJvdG9jb2wgKElTTy9JRUMgNzA2NDoyMDAzKVxuICpcbiAqIEBtZXRob2QgY2hlY2tzdW1cbiAqIEByZXR1cm5zIHtTdHJpbmd9IGNoZWNrc3VtXG4gKi9cbklDQVAucHJvdG90eXBlLmNoZWNrc3VtID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB0aGlzLl9pYmFuLnN1YnN0cigyLCAyKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBnZXQgaW5zdGl0dXRpb24gaWRlbnRpZmllclxuICogZWcuIFhSRUdcbiAqXG4gKiBAbWV0aG9kIGluc3RpdHV0aW9uXG4gKiBAcmV0dXJucyB7U3RyaW5nfSBpbnN0aXR1dGlvbiBpZGVudGlmaWVyXG4gKi9cbklDQVAucHJvdG90eXBlLmluc3RpdHV0aW9uID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB0aGlzLmlzSW5kaXJlY3QoKSA/IHRoaXMuX2liYW4uc3Vic3RyKDcsIDQpIDogJyc7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IGNsaWVudCBpZGVudGlmaWVyIHdpdGhpbiBpbnN0aXR1dGlvblxuICogZWcuIEdBVk9GWU9SS1xuICpcbiAqIEBtZXRob2QgY2xpZW50XG4gKiBAcmV0dXJucyB7U3RyaW5nfSBjbGllbnQgaWRlbnRpZmllclxuICovXG5JQ0FQLnByb3RvdHlwZS5jbGllbnQgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuaXNJbmRpcmVjdCgpID8gdGhpcy5faWJhbi5zdWJzdHIoMTEpIDogJyc7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IGNsaWVudCBkaXJlY3QgYWRkcmVzc1xuICpcbiAqIEBtZXRob2QgYWRkcmVzc1xuICogQHJldHVybnMge1N0cmluZ30gY2xpZW50IGRpcmVjdCBhZGRyZXNzXG4gKi9cbklDQVAucHJvdG90eXBlLmFkZHJlc3MgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuaXNEaXJlY3QoKSA/IHRoaXMuX2liYW4uc3Vic3RyKDQpIDogJyc7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IElDQVA7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIEBmaWxlIGpzb25ycGMuanNcbiAqIEBhdXRob3JzOlxuICogICBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgSnNvbnJwYyA9IGZ1bmN0aW9uICgpIHtcbiAgICAvLyBzaW5nbGV0b24gcGF0dGVyblxuICAgIGlmIChhcmd1bWVudHMuY2FsbGVlLl9zaW5nbGV0b25JbnN0YW5jZSkge1xuICAgICAgICByZXR1cm4gYXJndW1lbnRzLmNhbGxlZS5fc2luZ2xldG9uSW5zdGFuY2U7XG4gICAgfVxuICAgIGFyZ3VtZW50cy5jYWxsZWUuX3NpbmdsZXRvbkluc3RhbmNlID0gdGhpcztcblxuICAgIHRoaXMubWVzc2FnZUlkID0gMTtcbn07XG5cbi8qKlxuICogQHJldHVybiB7SnNvbnJwY30gc2luZ2xldG9uXG4gKi9cbkpzb25ycGMuZ2V0SW5zdGFuY2UgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGluc3RhbmNlID0gbmV3IEpzb25ycGMoKTtcbiAgICByZXR1cm4gaW5zdGFuY2U7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gdmFsaWQganNvbiBjcmVhdGUgcGF5bG9hZCBvYmplY3RcbiAqXG4gKiBAbWV0aG9kIHRvUGF5bG9hZFxuICogQHBhcmFtIHtGdW5jdGlvbn0gbWV0aG9kIG9mIGpzb25ycGMgY2FsbCwgcmVxdWlyZWRcbiAqIEBwYXJhbSB7QXJyYXl9IHBhcmFtcywgYW4gYXJyYXkgb2YgbWV0aG9kIHBhcmFtcywgb3B0aW9uYWxcbiAqIEByZXR1cm5zIHtPYmplY3R9IHZhbGlkIGpzb25ycGMgcGF5bG9hZCBvYmplY3RcbiAqL1xuSnNvbnJwYy5wcm90b3R5cGUudG9QYXlsb2FkID0gZnVuY3Rpb24gKG1ldGhvZCwgcGFyYW1zKSB7XG4gICAgaWYgKCFtZXRob2QpXG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ2pzb25ycGMgbWV0aG9kIHNob3VsZCBiZSBzcGVjaWZpZWQhJyk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgICBqc29ucnBjOiAnMi4wJyxcbiAgICAgICAgbWV0aG9kOiBtZXRob2QsXG4gICAgICAgIHBhcmFtczogcGFyYW1zIHx8IFtdLFxuICAgICAgICBpZDogdGhpcy5tZXNzYWdlSWQrK1xuICAgIH07XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gY2hlY2sgaWYganNvbnJwYyByZXNwb25zZSBpcyB2YWxpZFxuICpcbiAqIEBtZXRob2QgaXNWYWxpZFJlc3BvbnNlXG4gKiBAcGFyYW0ge09iamVjdH1cbiAqIEByZXR1cm5zIHtCb29sZWFufSB0cnVlIGlmIHJlc3BvbnNlIGlzIHZhbGlkLCBvdGhlcndpc2UgZmFsc2VcbiAqL1xuSnNvbnJwYy5wcm90b3R5cGUuaXNWYWxpZFJlc3BvbnNlID0gZnVuY3Rpb24gKHJlc3BvbnNlKSB7XG4gICAgcmV0dXJuICEhcmVzcG9uc2UgJiZcbiAgICAgICAgIXJlc3BvbnNlLmVycm9yICYmXG4gICAgICAgIHJlc3BvbnNlLmpzb25ycGMgPT09ICcyLjAnICYmXG4gICAgICAgIHR5cGVvZiByZXNwb25zZS5pZCA9PT0gJ251bWJlcicgJiZcbiAgICAgICAgcmVzcG9uc2UucmVzdWx0ICE9PSB1bmRlZmluZWQ7IC8vIG9ubHkgdW5kZWZpbmVkIGlzIG5vdCB2YWxpZCBqc29uIG9iamVjdFxufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGNyZWF0ZSBiYXRjaCBwYXlsb2FkIG9iamVjdFxuICpcbiAqIEBtZXRob2QgdG9CYXRjaFBheWxvYWRcbiAqIEBwYXJhbSB7QXJyYXl9IG1lc3NhZ2VzLCBhbiBhcnJheSBvZiBvYmplY3RzIHdpdGggbWV0aG9kIChyZXF1aXJlZCkgYW5kIHBhcmFtcyAob3B0aW9uYWwpIGZpZWxkc1xuICogQHJldHVybnMge0FycmF5fSBiYXRjaCBwYXlsb2FkXG4gKi9cbkpzb25ycGMucHJvdG90eXBlLnRvQmF0Y2hQYXlsb2FkID0gZnVuY3Rpb24gKG1lc3NhZ2VzKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHJldHVybiBtZXNzYWdlcy5tYXAoZnVuY3Rpb24gKG1lc3NhZ2UpIHtcbiAgICAgICAgcmV0dXJuIHNlbGYudG9QYXlsb2FkKG1lc3NhZ2UubWV0aG9kLCBtZXNzYWdlLnBhcmFtcyk7XG4gICAgfSk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEpzb25ycGM7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqXG4gKiBAZmlsZSBtZXRob2QuanNcbiAqIEBhdXRob3IgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE1XG4gKi9cblxudmFyIFJlcXVlc3RNYW5hZ2VyID0gcmVxdWlyZSgnLi9yZXF1ZXN0bWFuYWdlcicpO1xudmFyIHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMvdXRpbHMnKTtcbnZhciBlcnJvcnMgPSByZXF1aXJlKCcuL2Vycm9ycycpO1xuXG52YXIgTWV0aG9kID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICB0aGlzLm5hbWUgPSBvcHRpb25zLm5hbWU7XG4gICAgdGhpcy5jYWxsID0gb3B0aW9ucy5jYWxsO1xuICAgIHRoaXMucGFyYW1zID0gb3B0aW9ucy5wYXJhbXMgfHwgMDtcbiAgICB0aGlzLmlucHV0Rm9ybWF0dGVyID0gb3B0aW9ucy5pbnB1dEZvcm1hdHRlcjtcbiAgICB0aGlzLm91dHB1dEZvcm1hdHRlciA9IG9wdGlvbnMub3V0cHV0Rm9ybWF0dGVyO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBkZXRlcm1pbmUgbmFtZSBvZiB0aGUganNvbnJwYyBtZXRob2QgYmFzZWQgb24gYXJndW1lbnRzXG4gKlxuICogQG1ldGhvZCBnZXRDYWxsXG4gKiBAcGFyYW0ge0FycmF5fSBhcmd1bWVudHNcbiAqIEByZXR1cm4ge1N0cmluZ30gbmFtZSBvZiBqc29ucnBjIG1ldGhvZFxuICovXG5NZXRob2QucHJvdG90eXBlLmdldENhbGwgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIHJldHVybiB1dGlscy5pc0Z1bmN0aW9uKHRoaXMuY2FsbCkgPyB0aGlzLmNhbGwoYXJncykgOiB0aGlzLmNhbGw7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGV4dHJhY3QgY2FsbGJhY2sgZnJvbSBhcnJheSBvZiBhcmd1bWVudHMuIE1vZGlmaWVzIGlucHV0IHBhcmFtXG4gKlxuICogQG1ldGhvZCBleHRyYWN0Q2FsbGJhY2tcbiAqIEBwYXJhbSB7QXJyYXl9IGFyZ3VtZW50c1xuICogQHJldHVybiB7RnVuY3Rpb258TnVsbH0gY2FsbGJhY2ssIGlmIGV4aXN0c1xuICovXG5NZXRob2QucHJvdG90eXBlLmV4dHJhY3RDYWxsYmFjayA9IGZ1bmN0aW9uIChhcmdzKSB7XG4gICAgaWYgKHV0aWxzLmlzRnVuY3Rpb24oYXJnc1thcmdzLmxlbmd0aCAtIDFdKSkge1xuICAgICAgICByZXR1cm4gYXJncy5wb3AoKTsgLy8gbW9kaWZ5IHRoZSBhcmdzIGFycmF5IVxuICAgIH1cbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBjaGVjayBpZiB0aGUgbnVtYmVyIG9mIGFyZ3VtZW50cyBpcyBjb3JyZWN0XG4gKiBcbiAqIEBtZXRob2QgdmFsaWRhdGVBcmdzXG4gKiBAcGFyYW0ge0FycmF5fSBhcmd1bWVudHNcbiAqIEB0aHJvd3Mge0Vycm9yfSBpZiBpdCBpcyBub3RcbiAqL1xuTWV0aG9kLnByb3RvdHlwZS52YWxpZGF0ZUFyZ3MgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIGlmIChhcmdzLmxlbmd0aCAhPT0gdGhpcy5wYXJhbXMpIHtcbiAgICAgICAgdGhyb3cgZXJyb3JzLkludmFsaWROdW1iZXJPZlBhcmFtcygpO1xuICAgIH1cbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBmb3JtYXQgaW5wdXQgYXJncyBvZiBtZXRob2RcbiAqIFxuICogQG1ldGhvZCBmb3JtYXRJbnB1dFxuICogQHBhcmFtIHtBcnJheX1cbiAqIEByZXR1cm4ge0FycmF5fVxuICovXG5NZXRob2QucHJvdG90eXBlLmZvcm1hdElucHV0ID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICBpZiAoIXRoaXMuaW5wdXRGb3JtYXR0ZXIpIHtcbiAgICAgICAgcmV0dXJuIGFyZ3M7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuaW5wdXRGb3JtYXR0ZXIubWFwKGZ1bmN0aW9uIChmb3JtYXR0ZXIsIGluZGV4KSB7XG4gICAgICAgIHJldHVybiBmb3JtYXR0ZXIgPyBmb3JtYXR0ZXIoYXJnc1tpbmRleF0pIDogYXJnc1tpbmRleF07XG4gICAgfSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZm9ybWF0IG91dHB1dChyZXN1bHQpIG9mIG1ldGhvZFxuICpcbiAqIEBtZXRob2QgZm9ybWF0T3V0cHV0XG4gKiBAcGFyYW0ge09iamVjdH1cbiAqIEByZXR1cm4ge09iamVjdH1cbiAqL1xuTWV0aG9kLnByb3RvdHlwZS5mb3JtYXRPdXRwdXQgPSBmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgcmV0dXJuIHRoaXMub3V0cHV0Rm9ybWF0dGVyICYmIHJlc3VsdCAhPT0gbnVsbCA/IHRoaXMub3V0cHV0Rm9ybWF0dGVyKHJlc3VsdCkgOiByZXN1bHQ7XG59O1xuXG4vKipcbiAqIFNob3VsZCBhdHRhY2ggZnVuY3Rpb24gdG8gbWV0aG9kXG4gKiBcbiAqIEBtZXRob2QgYXR0YWNoVG9PYmplY3RcbiAqIEBwYXJhbSB7T2JqZWN0fVxuICogQHBhcmFtIHtGdW5jdGlvbn1cbiAqL1xuTWV0aG9kLnByb3RvdHlwZS5hdHRhY2hUb09iamVjdCA9IGZ1bmN0aW9uIChvYmopIHtcbiAgICB2YXIgZnVuYyA9IHRoaXMuc2VuZC5iaW5kKHRoaXMpO1xuICAgIGZ1bmMucmVxdWVzdCA9IHRoaXMucmVxdWVzdC5iaW5kKHRoaXMpO1xuICAgIGZ1bmMuY2FsbCA9IHRoaXMuY2FsbDsgLy8gdGhhdCdzIHVnbHkuIGZpbHRlci5qcyB1c2VzIGl0XG4gICAgdmFyIG5hbWUgPSB0aGlzLm5hbWUuc3BsaXQoJy4nKTtcbiAgICBpZiAobmFtZS5sZW5ndGggPiAxKSB7XG4gICAgICAgIG9ialtuYW1lWzBdXSA9IG9ialtuYW1lWzBdXSB8fCB7fTtcbiAgICAgICAgb2JqW25hbWVbMF1dW25hbWVbMV1dID0gZnVuYztcbiAgICB9IGVsc2Uge1xuICAgICAgICBvYmpbbmFtZVswXV0gPSBmdW5jOyBcbiAgICB9XG59O1xuXG4vKipcbiAqIFNob3VsZCBjcmVhdGUgcGF5bG9hZCBmcm9tIGdpdmVuIGlucHV0IGFyZ3NcbiAqXG4gKiBAbWV0aG9kIHRvUGF5bG9hZFxuICogQHBhcmFtIHtBcnJheX0gYXJnc1xuICogQHJldHVybiB7T2JqZWN0fVxuICovXG5NZXRob2QucHJvdG90eXBlLnRvUGF5bG9hZCA9IGZ1bmN0aW9uIChhcmdzKSB7XG4gICAgdmFyIGNhbGwgPSB0aGlzLmdldENhbGwoYXJncyk7XG4gICAgdmFyIGNhbGxiYWNrID0gdGhpcy5leHRyYWN0Q2FsbGJhY2soYXJncyk7XG4gICAgdmFyIHBhcmFtcyA9IHRoaXMuZm9ybWF0SW5wdXQoYXJncyk7XG4gICAgdGhpcy52YWxpZGF0ZUFyZ3MocGFyYW1zKTtcblxuICAgIHJldHVybiB7XG4gICAgICAgIG1ldGhvZDogY2FsbCxcbiAgICAgICAgcGFyYW1zOiBwYXJhbXMsXG4gICAgICAgIGNhbGxiYWNrOiBjYWxsYmFja1xuICAgIH07XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gY3JlYXRlIHB1cmUgSlNPTlJQQyByZXF1ZXN0IHdoaWNoIGNhbiBiZSB1c2VkIGluIGJhdGNoIHJlcXVlc3RcbiAqXG4gKiBAbWV0aG9kIHJlcXVlc3RcbiAqIEBwYXJhbSB7Li4ufSBwYXJhbXNcbiAqIEByZXR1cm4ge09iamVjdH0ganNvbnJwYyByZXF1ZXN0XG4gKi9cbk1ldGhvZC5wcm90b3R5cGUucmVxdWVzdCA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgcGF5bG9hZCA9IHRoaXMudG9QYXlsb2FkKEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cykpO1xuICAgIHBheWxvYWQuZm9ybWF0ID0gdGhpcy5mb3JtYXRPdXRwdXQuYmluZCh0aGlzKTtcbiAgICByZXR1cm4gcGF5bG9hZDtcbn07XG5cbi8qKlxuICogU2hvdWxkIHNlbmQgcmVxdWVzdCB0byB0aGUgQVBJXG4gKlxuICogQG1ldGhvZCBzZW5kXG4gKiBAcGFyYW0gbGlzdCBvZiBwYXJhbXNcbiAqIEByZXR1cm4gcmVzdWx0XG4gKi9cbk1ldGhvZC5wcm90b3R5cGUuc2VuZCA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgcGF5bG9hZCA9IHRoaXMudG9QYXlsb2FkKEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cykpO1xuICAgIGlmIChwYXlsb2FkLmNhbGxiYWNrKSB7XG4gICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgcmV0dXJuIFJlcXVlc3RNYW5hZ2VyLmdldEluc3RhbmNlKCkuc2VuZEFzeW5jKHBheWxvYWQsIGZ1bmN0aW9uIChlcnIsIHJlc3VsdCkge1xuICAgICAgICAgICAgcGF5bG9hZC5jYWxsYmFjayhlcnIsIHNlbGYuZm9ybWF0T3V0cHV0KHJlc3VsdCkpO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZm9ybWF0T3V0cHV0KFJlcXVlc3RNYW5hZ2VyLmdldEluc3RhbmNlKCkuc2VuZChwYXlsb2FkKSk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IE1ldGhvZDtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSBuYW1lcmVnLmpzXG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciBjb250cmFjdCA9IHJlcXVpcmUoJy4vY29udHJhY3QnKTtcblxudmFyIGFkZHJlc3MgPSAnMHhjNmQ5ZDJjZDQ0OWE3NTRjNDk0MjY0ZTE4MDljNTBlMzRkNjQ1NjJiJztcblxudmFyIGFiaSA9IFtcbiAgICB7XCJjb25zdGFudFwiOnRydWUsXCJpbnB1dHNcIjpbe1wibmFtZVwiOlwiX293bmVyXCIsXCJ0eXBlXCI6XCJhZGRyZXNzXCJ9XSxcIm5hbWVcIjpcIm5hbWVcIixcIm91dHB1dHNcIjpbe1wibmFtZVwiOlwib19uYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImNvbnN0YW50XCI6dHJ1ZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJfbmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifV0sXCJuYW1lXCI6XCJvd25lclwiLFwib3V0cHV0c1wiOlt7XCJuYW1lXCI6XCJcIixcInR5cGVcIjpcImFkZHJlc3NcIn1dLFwidHlwZVwiOlwiZnVuY3Rpb25cIn0sXG4gICAge1wiY29uc3RhbnRcIjp0cnVlLFwiaW5wdXRzXCI6W3tcIm5hbWVcIjpcIl9uYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcIm5hbWVcIjpcImNvbnRlbnRcIixcIm91dHB1dHNcIjpbe1wibmFtZVwiOlwiXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImNvbnN0YW50XCI6dHJ1ZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJfbmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifV0sXCJuYW1lXCI6XCJhZGRyXCIsXCJvdXRwdXRzXCI6W3tcIm5hbWVcIjpcIlwiLFwidHlwZVwiOlwiYWRkcmVzc1wifV0sXCJ0eXBlXCI6XCJmdW5jdGlvblwifSxcbiAgICB7XCJjb25zdGFudFwiOmZhbHNlLFwiaW5wdXRzXCI6W3tcIm5hbWVcIjpcIl9uYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcIm5hbWVcIjpcInJlc2VydmVcIixcIm91dHB1dHNcIjpbXSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImNvbnN0YW50XCI6dHJ1ZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJfbmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifV0sXCJuYW1lXCI6XCJzdWJSZWdpc3RyYXJcIixcIm91dHB1dHNcIjpbe1wibmFtZVwiOlwib19zdWJSZWdpc3RyYXJcIixcInR5cGVcIjpcImFkZHJlc3NcIn1dLFwidHlwZVwiOlwiZnVuY3Rpb25cIn0sXG4gICAge1wiY29uc3RhbnRcIjpmYWxzZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJfbmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifSx7XCJuYW1lXCI6XCJfbmV3T3duZXJcIixcInR5cGVcIjpcImFkZHJlc3NcIn1dLFwibmFtZVwiOlwidHJhbnNmZXJcIixcIm91dHB1dHNcIjpbXSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImNvbnN0YW50XCI6ZmFsc2UsXCJpbnB1dHNcIjpbe1wibmFtZVwiOlwiX25hbWVcIixcInR5cGVcIjpcImJ5dGVzMzJcIn0se1wibmFtZVwiOlwiX3JlZ2lzdHJhclwiLFwidHlwZVwiOlwiYWRkcmVzc1wifV0sXCJuYW1lXCI6XCJzZXRTdWJSZWdpc3RyYXJcIixcIm91dHB1dHNcIjpbXSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImNvbnN0YW50XCI6ZmFsc2UsXCJpbnB1dHNcIjpbXSxcIm5hbWVcIjpcIlJlZ2lzdHJhclwiLFwib3V0cHV0c1wiOltdLFwidHlwZVwiOlwiZnVuY3Rpb25cIn0sXG4gICAge1wiY29uc3RhbnRcIjpmYWxzZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJfbmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifSx7XCJuYW1lXCI6XCJfYVwiLFwidHlwZVwiOlwiYWRkcmVzc1wifSx7XCJuYW1lXCI6XCJfcHJpbWFyeVwiLFwidHlwZVwiOlwiYm9vbFwifV0sXCJuYW1lXCI6XCJzZXRBZGRyZXNzXCIsXCJvdXRwdXRzXCI6W10sXCJ0eXBlXCI6XCJmdW5jdGlvblwifSxcbiAgICB7XCJjb25zdGFudFwiOmZhbHNlLFwiaW5wdXRzXCI6W3tcIm5hbWVcIjpcIl9uYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9LHtcIm5hbWVcIjpcIl9jb250ZW50XCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcIm5hbWVcIjpcInNldENvbnRlbnRcIixcIm91dHB1dHNcIjpbXSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImNvbnN0YW50XCI6ZmFsc2UsXCJpbnB1dHNcIjpbe1wibmFtZVwiOlwiX25hbWVcIixcInR5cGVcIjpcImJ5dGVzMzJcIn1dLFwibmFtZVwiOlwiZGlzb3duXCIsXCJvdXRwdXRzXCI6W10sXCJ0eXBlXCI6XCJmdW5jdGlvblwifSxcbiAgICB7XCJjb25zdGFudFwiOnRydWUsXCJpbnB1dHNcIjpbe1wibmFtZVwiOlwiX25hbWVcIixcInR5cGVcIjpcImJ5dGVzMzJcIn1dLFwibmFtZVwiOlwicmVnaXN0ZXJcIixcIm91dHB1dHNcIjpbe1wibmFtZVwiOlwiXCIsXCJ0eXBlXCI6XCJhZGRyZXNzXCJ9XSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImFub255bW91c1wiOmZhbHNlLFwiaW5wdXRzXCI6W3tcImluZGV4ZWRcIjp0cnVlLFwibmFtZVwiOlwibmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifV0sXCJuYW1lXCI6XCJDaGFuZ2VkXCIsXCJ0eXBlXCI6XCJldmVudFwifSxcbiAgICB7XCJhbm9ueW1vdXNcIjpmYWxzZSxcImlucHV0c1wiOlt7XCJpbmRleGVkXCI6dHJ1ZSxcIm5hbWVcIjpcIm5hbWVcIixcInR5cGVcIjpcImJ5dGVzMzJcIn0se1wiaW5kZXhlZFwiOnRydWUsXCJuYW1lXCI6XCJhZGRyXCIsXCJ0eXBlXCI6XCJhZGRyZXNzXCJ9XSxcIm5hbWVcIjpcIlByaW1hcnlDaGFuZ2VkXCIsXCJ0eXBlXCI6XCJldmVudFwifVxuXTtcblxubW9kdWxlLmV4cG9ydHMgPSBjb250cmFjdChhYmkpLmF0KGFkZHJlc3MpO1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBAZmlsZSBldGguanNcbiAqIEBhdXRob3JzOlxuICogICBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xudmFyIFByb3BlcnR5ID0gcmVxdWlyZSgnLi9wcm9wZXJ0eScpO1xuXG4vLy8gQHJldHVybnMgYW4gYXJyYXkgb2Ygb2JqZWN0cyBkZXNjcmliaW5nIHdlYjMuZXRoIGFwaSBtZXRob2RzXG52YXIgbWV0aG9kcyA9IFtcbl07XG5cbi8vLyBAcmV0dXJucyBhbiBhcnJheSBvZiBvYmplY3RzIGRlc2NyaWJpbmcgd2ViMy5ldGggYXBpIHByb3BlcnRpZXNcbnZhciBwcm9wZXJ0aWVzID0gW1xuICAgIG5ldyBQcm9wZXJ0eSh7XG4gICAgICAgIG5hbWU6ICdsaXN0ZW5pbmcnLFxuICAgICAgICBnZXR0ZXI6ICduZXRfbGlzdGVuaW5nJ1xuICAgIH0pLFxuICAgIG5ldyBQcm9wZXJ0eSh7XG4gICAgICAgIG5hbWU6ICdwZWVyQ291bnQnLFxuICAgICAgICBnZXR0ZXI6ICduZXRfcGVlckNvdW50JyxcbiAgICAgICAgb3V0cHV0Rm9ybWF0dGVyOiB1dGlscy50b0RlY2ltYWxcbiAgICB9KVxuXTtcblxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBtZXRob2RzOiBtZXRob2RzLFxuICAgIHByb3BlcnRpZXM6IHByb3BlcnRpZXNcbn07XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqXG4gKiBAZmlsZSBwcm9wZXJ0eS5qc1xuICogQGF1dGhvciBGYWJpYW4gVm9nZWxzdGVsbGVyIDxmYWJpYW5AZnJvemVtYW4uZGU+XG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciBSZXF1ZXN0TWFuYWdlciA9IHJlcXVpcmUoJy4vcmVxdWVzdG1hbmFnZXInKTtcblxudmFyIFByb3BlcnR5ID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICB0aGlzLm5hbWUgPSBvcHRpb25zLm5hbWU7XG4gICAgdGhpcy5nZXR0ZXIgPSBvcHRpb25zLmdldHRlcjtcbiAgICB0aGlzLnNldHRlciA9IG9wdGlvbnMuc2V0dGVyO1xuICAgIHRoaXMub3V0cHV0Rm9ybWF0dGVyID0gb3B0aW9ucy5vdXRwdXRGb3JtYXR0ZXI7XG4gICAgdGhpcy5pbnB1dEZvcm1hdHRlciA9IG9wdGlvbnMuaW5wdXRGb3JtYXR0ZXI7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZm9ybWF0IGlucHV0IGFyZ3Mgb2YgbWV0aG9kXG4gKiBcbiAqIEBtZXRob2QgZm9ybWF0SW5wdXRcbiAqIEBwYXJhbSB7QXJyYXl9XG4gKiBAcmV0dXJuIHtBcnJheX1cbiAqL1xuUHJvcGVydHkucHJvdG90eXBlLmZvcm1hdElucHV0ID0gZnVuY3Rpb24gKGFyZykge1xuICAgIHJldHVybiB0aGlzLmlucHV0Rm9ybWF0dGVyID8gdGhpcy5pbnB1dEZvcm1hdHRlcihhcmcpIDogYXJnO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGZvcm1hdCBvdXRwdXQocmVzdWx0KSBvZiBtZXRob2RcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdE91dHB1dFxuICogQHBhcmFtIHtPYmplY3R9XG4gKiBAcmV0dXJuIHtPYmplY3R9XG4gKi9cblByb3BlcnR5LnByb3RvdHlwZS5mb3JtYXRPdXRwdXQgPSBmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgcmV0dXJuIHRoaXMub3V0cHV0Rm9ybWF0dGVyICYmIHJlc3VsdCAhPT0gbnVsbCA/IHRoaXMub3V0cHV0Rm9ybWF0dGVyKHJlc3VsdCkgOiByZXN1bHQ7XG59O1xuXG4vKipcbiAqIFNob3VsZCBhdHRhY2ggZnVuY3Rpb24gdG8gbWV0aG9kXG4gKiBcbiAqIEBtZXRob2QgYXR0YWNoVG9PYmplY3RcbiAqIEBwYXJhbSB7T2JqZWN0fVxuICogQHBhcmFtIHtGdW5jdGlvbn1cbiAqL1xuUHJvcGVydHkucHJvdG90eXBlLmF0dGFjaFRvT2JqZWN0ID0gZnVuY3Rpb24gKG9iaikge1xuICAgIHZhciBwcm90byA9IHtcbiAgICAgICAgZ2V0OiB0aGlzLmdldC5iaW5kKHRoaXMpLFxuICAgIH07XG5cbiAgICB2YXIgbmFtZXMgPSB0aGlzLm5hbWUuc3BsaXQoJy4nKTtcbiAgICB2YXIgbmFtZSA9IG5hbWVzWzBdO1xuICAgIGlmIChuYW1lcy5sZW5ndGggPiAxKSB7XG4gICAgICAgIG9ialtuYW1lc1swXV0gPSBvYmpbbmFtZXNbMF1dIHx8IHt9O1xuICAgICAgICBvYmogPSBvYmpbbmFtZXNbMF1dO1xuICAgICAgICBuYW1lID0gbmFtZXNbMV07XG4gICAgfVxuICAgIFxuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShvYmosIG5hbWUsIHByb3RvKTtcblxuICAgIHZhciB0b0FzeW5jTmFtZSA9IGZ1bmN0aW9uIChwcmVmaXgsIG5hbWUpIHtcbiAgICAgICAgcmV0dXJuIHByZWZpeCArIG5hbWUuY2hhckF0KDApLnRvVXBwZXJDYXNlKCkgKyBuYW1lLnNsaWNlKDEpO1xuICAgIH07XG5cbiAgICBvYmpbdG9Bc3luY05hbWUoJ2dldCcsIG5hbWUpXSA9IHRoaXMuZ2V0QXN5bmMuYmluZCh0aGlzKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZ2V0IHZhbHVlIG9mIHRoZSBwcm9wZXJ0eVxuICpcbiAqIEBtZXRob2QgZ2V0XG4gKiBAcmV0dXJuIHtPYmplY3R9IHZhbHVlIG9mIHRoZSBwcm9wZXJ0eVxuICovXG5Qcm9wZXJ0eS5wcm90b3R5cGUuZ2V0ID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB0aGlzLmZvcm1hdE91dHB1dChSZXF1ZXN0TWFuYWdlci5nZXRJbnN0YW5jZSgpLnNlbmQoe1xuICAgICAgICBtZXRob2Q6IHRoaXMuZ2V0dGVyXG4gICAgfSkpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBhc3luY2hyb3Vub3VzbHkgZ2V0IHZhbHVlIG9mIHByb3BlcnR5XG4gKlxuICogQG1ldGhvZCBnZXRBc3luY1xuICogQHBhcmFtIHtGdW5jdGlvbn1cbiAqL1xuUHJvcGVydHkucHJvdG90eXBlLmdldEFzeW5jID0gZnVuY3Rpb24gKGNhbGxiYWNrKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIFJlcXVlc3RNYW5hZ2VyLmdldEluc3RhbmNlKCkuc2VuZEFzeW5jKHtcbiAgICAgICAgbWV0aG9kOiB0aGlzLmdldHRlclxuICAgIH0sIGZ1bmN0aW9uIChlcnIsIHJlc3VsdCkge1xuICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgICByZXR1cm4gY2FsbGJhY2soZXJyKTtcbiAgICAgICAgfVxuICAgICAgICBjYWxsYmFjayhlcnIsIHNlbGYuZm9ybWF0T3V0cHV0KHJlc3VsdCkpO1xuICAgIH0pO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBQcm9wZXJ0eTtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogQGZpbGUgcXRzeW5jLmpzXG4gKiBAYXV0aG9yczpcbiAqICAgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiAgIE1hcmlhbiBPYW5jZWEgPG1hcmlhbkBldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNFxuICovXG5cbnZhciBRdFN5bmNQcm92aWRlciA9IGZ1bmN0aW9uICgpIHtcbn07XG5cblF0U3luY1Byb3ZpZGVyLnByb3RvdHlwZS5zZW5kID0gZnVuY3Rpb24gKHBheWxvYWQpIHtcbiAgICB2YXIgcmVzdWx0ID0gbmF2aWdhdG9yLnF0LmNhbGxNZXRob2QoSlNPTi5zdHJpbmdpZnkocGF5bG9hZCkpO1xuICAgIHJldHVybiBKU09OLnBhcnNlKHJlc3VsdCk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFF0U3luY1Byb3ZpZGVyO1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBcbiAqIEBmaWxlIHJlcXVlc3RtYW5hZ2VyLmpzXG4gKiBAYXV0aG9yIEplZmZyZXkgV2lsY2tlIDxqZWZmQGV0aGRldi5jb20+XG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGF1dGhvciBNYXJpYW4gT2FuY2VhIDxtYXJpYW5AZXRoZGV2LmNvbT5cbiAqIEBhdXRob3IgRmFiaWFuIFZvZ2Vsc3RlbGxlciA8ZmFiaWFuQGV0aGRldi5jb20+XG4gKiBAYXV0aG9yIEdhdiBXb29kIDxnQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE0XG4gKi9cblxudmFyIEpzb25ycGMgPSByZXF1aXJlKCcuL2pzb25ycGMnKTtcbnZhciB1dGlscyA9IHJlcXVpcmUoJy4uL3V0aWxzL3V0aWxzJyk7XG52YXIgYyA9IHJlcXVpcmUoJy4uL3V0aWxzL2NvbmZpZycpO1xudmFyIGVycm9ycyA9IHJlcXVpcmUoJy4vZXJyb3JzJyk7XG5cbi8qKlxuICogSXQncyByZXNwb25zaWJsZSBmb3IgcGFzc2luZyBtZXNzYWdlcyB0byBwcm92aWRlcnNcbiAqIEl0J3MgYWxzbyByZXNwb25zaWJsZSBmb3IgcG9sbGluZyB0aGUgZXRoZXJldW0gbm9kZSBmb3IgaW5jb21pbmcgbWVzc2FnZXNcbiAqIERlZmF1bHQgcG9sbCB0aW1lb3V0IGlzIDEgc2Vjb25kXG4gKiBTaW5nbGV0b25cbiAqL1xudmFyIFJlcXVlc3RNYW5hZ2VyID0gZnVuY3Rpb24gKHByb3ZpZGVyKSB7XG4gICAgLy8gc2luZ2xldG9uIHBhdHRlcm5cbiAgICBpZiAoYXJndW1lbnRzLmNhbGxlZS5fc2luZ2xldG9uSW5zdGFuY2UpIHtcbiAgICAgICAgcmV0dXJuIGFyZ3VtZW50cy5jYWxsZWUuX3NpbmdsZXRvbkluc3RhbmNlO1xuICAgIH1cbiAgICBhcmd1bWVudHMuY2FsbGVlLl9zaW5nbGV0b25JbnN0YW5jZSA9IHRoaXM7XG5cbiAgICB0aGlzLnByb3ZpZGVyID0gcHJvdmlkZXI7XG4gICAgdGhpcy5wb2xscyA9IFtdO1xuICAgIHRoaXMudGltZW91dCA9IG51bGw7XG4gICAgdGhpcy5wb2xsKCk7XG59O1xuXG4vKipcbiAqIEByZXR1cm4ge1JlcXVlc3RNYW5hZ2VyfSBzaW5nbGV0b25cbiAqL1xuUmVxdWVzdE1hbmFnZXIuZ2V0SW5zdGFuY2UgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGluc3RhbmNlID0gbmV3IFJlcXVlc3RNYW5hZ2VyKCk7XG4gICAgcmV0dXJuIGluc3RhbmNlO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBzeW5jaHJvbm91c2x5IHNlbmQgcmVxdWVzdFxuICpcbiAqIEBtZXRob2Qgc2VuZFxuICogQHBhcmFtIHtPYmplY3R9IGRhdGFcbiAqIEByZXR1cm4ge09iamVjdH1cbiAqL1xuUmVxdWVzdE1hbmFnZXIucHJvdG90eXBlLnNlbmQgPSBmdW5jdGlvbiAoZGF0YSkge1xuICAgIGlmICghdGhpcy5wcm92aWRlcikge1xuICAgICAgICBjb25zb2xlLmVycm9yKGVycm9ycy5JbnZhbGlkUHJvdmlkZXIoKSk7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIHZhciBwYXlsb2FkID0gSnNvbnJwYy5nZXRJbnN0YW5jZSgpLnRvUGF5bG9hZChkYXRhLm1ldGhvZCwgZGF0YS5wYXJhbXMpO1xuICAgIHZhciByZXN1bHQgPSB0aGlzLnByb3ZpZGVyLnNlbmQocGF5bG9hZCk7XG5cbiAgICBpZiAoIUpzb25ycGMuZ2V0SW5zdGFuY2UoKS5pc1ZhbGlkUmVzcG9uc2UocmVzdWx0KSkge1xuICAgICAgICB0aHJvdyBlcnJvcnMuSW52YWxpZFJlc3BvbnNlKHJlc3VsdCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdC5yZXN1bHQ7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGFzeW5jaHJvbm91c2x5IHNlbmQgcmVxdWVzdFxuICpcbiAqIEBtZXRob2Qgc2VuZEFzeW5jXG4gKiBAcGFyYW0ge09iamVjdH0gZGF0YVxuICogQHBhcmFtIHtGdW5jdGlvbn0gY2FsbGJhY2tcbiAqL1xuUmVxdWVzdE1hbmFnZXIucHJvdG90eXBlLnNlbmRBc3luYyA9IGZ1bmN0aW9uIChkYXRhLCBjYWxsYmFjaykge1xuICAgIGlmICghdGhpcy5wcm92aWRlcikge1xuICAgICAgICByZXR1cm4gY2FsbGJhY2soZXJyb3JzLkludmFsaWRQcm92aWRlcigpKTtcbiAgICB9XG5cbiAgICB2YXIgcGF5bG9hZCA9IEpzb25ycGMuZ2V0SW5zdGFuY2UoKS50b1BheWxvYWQoZGF0YS5tZXRob2QsIGRhdGEucGFyYW1zKTtcbiAgICB0aGlzLnByb3ZpZGVyLnNlbmRBc3luYyhwYXlsb2FkLCBmdW5jdGlvbiAoZXJyLCByZXN1bHQpIHtcbiAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrKGVycik7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIGlmICghSnNvbnJwYy5nZXRJbnN0YW5jZSgpLmlzVmFsaWRSZXNwb25zZShyZXN1bHQpKSB7XG4gICAgICAgICAgICByZXR1cm4gY2FsbGJhY2soZXJyb3JzLkludmFsaWRSZXNwb25zZShyZXN1bHQpKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNhbGxiYWNrKG51bGwsIHJlc3VsdC5yZXN1bHQpO1xuICAgIH0pO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGFzeW5jaHJvbm91c2x5IHNlbmQgYmF0Y2ggcmVxdWVzdFxuICpcbiAqIEBtZXRob2Qgc2VuZEJhdGNoXG4gKiBAcGFyYW0ge0FycmF5fSBiYXRjaCBkYXRhXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFja1xuICovXG5SZXF1ZXN0TWFuYWdlci5wcm90b3R5cGUuc2VuZEJhdGNoID0gZnVuY3Rpb24gKGRhdGEsIGNhbGxiYWNrKSB7XG4gICAgaWYgKCF0aGlzLnByb3ZpZGVyKSB7XG4gICAgICAgIHJldHVybiBjYWxsYmFjayhlcnJvcnMuSW52YWxpZFByb3ZpZGVyKCkpO1xuICAgIH1cblxuICAgIHZhciBwYXlsb2FkID0gSnNvbnJwYy5nZXRJbnN0YW5jZSgpLnRvQmF0Y2hQYXlsb2FkKGRhdGEpO1xuXG4gICAgdGhpcy5wcm92aWRlci5zZW5kQXN5bmMocGF5bG9hZCwgZnVuY3Rpb24gKGVyciwgcmVzdWx0cykge1xuICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgICByZXR1cm4gY2FsbGJhY2soZXJyKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghdXRpbHMuaXNBcnJheShyZXN1bHRzKSkge1xuICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrKGVycm9ycy5JbnZhbGlkUmVzcG9uc2UocmVzdWx0cykpO1xuICAgICAgICB9XG5cbiAgICAgICAgY2FsbGJhY2soZXJyLCByZXN1bHRzKTtcbiAgICB9KTsgXG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIHNldCBwcm92aWRlciBvZiByZXF1ZXN0IG1hbmFnZXJcbiAqXG4gKiBAbWV0aG9kIHNldFByb3ZpZGVyXG4gKiBAcGFyYW0ge09iamVjdH1cbiAqL1xuUmVxdWVzdE1hbmFnZXIucHJvdG90eXBlLnNldFByb3ZpZGVyID0gZnVuY3Rpb24gKHApIHtcbiAgICB0aGlzLnByb3ZpZGVyID0gcDtcbn07XG5cbi8qanNoaW50IG1heHBhcmFtczo0ICovXG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gc3RhcnQgcG9sbGluZ1xuICpcbiAqIEBtZXRob2Qgc3RhcnRQb2xsaW5nXG4gKiBAcGFyYW0ge09iamVjdH0gZGF0YVxuICogQHBhcmFtIHtOdW1iZXJ9IHBvbGxJZFxuICogQHBhcmFtIHtGdW5jdGlvbn0gY2FsbGJhY2tcbiAqIEBwYXJhbSB7RnVuY3Rpb259IHVuaW5zdGFsbFxuICpcbiAqIEB0b2RvIGNsZWFudXAgbnVtYmVyIG9mIHBhcmFtc1xuICovXG5SZXF1ZXN0TWFuYWdlci5wcm90b3R5cGUuc3RhcnRQb2xsaW5nID0gZnVuY3Rpb24gKGRhdGEsIHBvbGxJZCwgY2FsbGJhY2ssIHVuaW5zdGFsbCkge1xuICAgIHRoaXMucG9sbHMucHVzaCh7ZGF0YTogZGF0YSwgaWQ6IHBvbGxJZCwgY2FsbGJhY2s6IGNhbGxiYWNrLCB1bmluc3RhbGw6IHVuaW5zdGFsbH0pO1xufTtcbi8qanNoaW50IG1heHBhcmFtczozICovXG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gc3RvcCBwb2xsaW5nIGZvciBmaWx0ZXIgd2l0aCBnaXZlbiBpZFxuICpcbiAqIEBtZXRob2Qgc3RvcFBvbGxpbmdcbiAqIEBwYXJhbSB7TnVtYmVyfSBwb2xsSWRcbiAqL1xuUmVxdWVzdE1hbmFnZXIucHJvdG90eXBlLnN0b3BQb2xsaW5nID0gZnVuY3Rpb24gKHBvbGxJZCkge1xuICAgIGZvciAodmFyIGkgPSB0aGlzLnBvbGxzLmxlbmd0aDsgaS0tOykge1xuICAgICAgICB2YXIgcG9sbCA9IHRoaXMucG9sbHNbaV07XG4gICAgICAgIGlmIChwb2xsLmlkID09PSBwb2xsSWQpIHtcbiAgICAgICAgICAgIHRoaXMucG9sbHMuc3BsaWNlKGksIDEpO1xuICAgICAgICB9XG4gICAgfVxufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIHJlc2V0IHBvbGxpbmcgbWVjaGFuaXNtIG9mIHJlcXVlc3QgbWFuYWdlclxuICpcbiAqIEBtZXRob2QgcmVzZXRcbiAqL1xuUmVxdWVzdE1hbmFnZXIucHJvdG90eXBlLnJlc2V0ID0gZnVuY3Rpb24gKCkge1xuICAgIHRoaXMucG9sbHMuZm9yRWFjaChmdW5jdGlvbiAocG9sbCkge1xuICAgICAgICBwb2xsLnVuaW5zdGFsbChwb2xsLmlkKTsgXG4gICAgfSk7XG4gICAgdGhpcy5wb2xscyA9IFtdO1xuXG4gICAgaWYgKHRoaXMudGltZW91dCkge1xuICAgICAgICBjbGVhclRpbWVvdXQodGhpcy50aW1lb3V0KTtcbiAgICAgICAgdGhpcy50aW1lb3V0ID0gbnVsbDtcbiAgICB9XG4gICAgdGhpcy5wb2xsKCk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gcG9sbCBmb3IgY2hhbmdlcyBvbiBmaWx0ZXIgd2l0aCBnaXZlbiBpZFxuICpcbiAqIEBtZXRob2QgcG9sbFxuICovXG5SZXF1ZXN0TWFuYWdlci5wcm90b3R5cGUucG9sbCA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLnRpbWVvdXQgPSBzZXRUaW1lb3V0KHRoaXMucG9sbC5iaW5kKHRoaXMpLCBjLkVUSF9QT0xMSU5HX1RJTUVPVVQpO1xuXG4gICAgaWYgKCF0aGlzLnBvbGxzLmxlbmd0aCkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLnByb3ZpZGVyKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoZXJyb3JzLkludmFsaWRQcm92aWRlcigpKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBwYXlsb2FkID0gSnNvbnJwYy5nZXRJbnN0YW5jZSgpLnRvQmF0Y2hQYXlsb2FkKHRoaXMucG9sbHMubWFwKGZ1bmN0aW9uIChkYXRhKSB7XG4gICAgICAgIHJldHVybiBkYXRhLmRhdGE7XG4gICAgfSkpO1xuXG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHRoaXMucHJvdmlkZXIuc2VuZEFzeW5jKHBheWxvYWQsIGZ1bmN0aW9uIChlcnJvciwgcmVzdWx0cykge1xuICAgICAgICAvLyBUT0RPOiBjb25zb2xlIGxvZz9cbiAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgICAgIFxuICAgICAgICBpZiAoIXV0aWxzLmlzQXJyYXkocmVzdWx0cykpIHtcbiAgICAgICAgICAgIHRocm93IGVycm9ycy5JbnZhbGlkUmVzcG9uc2UocmVzdWx0cyk7XG4gICAgICAgIH1cblxuICAgICAgICByZXN1bHRzLm1hcChmdW5jdGlvbiAocmVzdWx0LCBpbmRleCkge1xuICAgICAgICAgICAgcmVzdWx0LmNhbGxiYWNrID0gc2VsZi5wb2xsc1tpbmRleF0uY2FsbGJhY2s7XG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgICB9KS5maWx0ZXIoZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgdmFyIHZhbGlkID0gSnNvbnJwYy5nZXRJbnN0YW5jZSgpLmlzVmFsaWRSZXNwb25zZShyZXN1bHQpO1xuICAgICAgICAgICAgaWYgKCF2YWxpZCkge1xuICAgICAgICAgICAgICAgIHJlc3VsdC5jYWxsYmFjayhlcnJvcnMuSW52YWxpZFJlc3BvbnNlKHJlc3VsdCkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHZhbGlkO1xuICAgICAgICB9KS5maWx0ZXIoZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgcmV0dXJuIHV0aWxzLmlzQXJyYXkocmVzdWx0LnJlc3VsdCkgJiYgcmVzdWx0LnJlc3VsdC5sZW5ndGggPiAwO1xuICAgICAgICB9KS5mb3JFYWNoKGZ1bmN0aW9uIChyZXN1bHQpIHtcbiAgICAgICAgICAgIHJlc3VsdC5jYWxsYmFjayhudWxsLCByZXN1bHQucmVzdWx0KTtcbiAgICAgICAgfSk7XG4gICAgfSk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFJlcXVlc3RNYW5hZ2VyO1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBAZmlsZSBzaGguanNcbiAqIEBhdXRob3JzOlxuICogICBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgTWV0aG9kID0gcmVxdWlyZSgnLi9tZXRob2QnKTtcbnZhciBmb3JtYXR0ZXJzID0gcmVxdWlyZSgnLi9mb3JtYXR0ZXJzJyk7XG5cbnZhciBwb3N0ID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ3Bvc3QnLCBcbiAgICBjYWxsOiAnc2hoX3Bvc3QnLCBcbiAgICBwYXJhbXM6IDEsXG4gICAgaW5wdXRGb3JtYXR0ZXI6IFtmb3JtYXR0ZXJzLmlucHV0UG9zdEZvcm1hdHRlcl1cbn0pO1xuXG52YXIgbmV3SWRlbnRpdHkgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnbmV3SWRlbnRpdHknLFxuICAgIGNhbGw6ICdzaGhfbmV3SWRlbnRpdHknLFxuICAgIHBhcmFtczogMFxufSk7XG5cbnZhciBoYXNJZGVudGl0eSA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdoYXNJZGVudGl0eScsXG4gICAgY2FsbDogJ3NoaF9oYXNJZGVudGl0eScsXG4gICAgcGFyYW1zOiAxXG59KTtcblxudmFyIG5ld0dyb3VwID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ25ld0dyb3VwJyxcbiAgICBjYWxsOiAnc2hoX25ld0dyb3VwJyxcbiAgICBwYXJhbXM6IDBcbn0pO1xuXG52YXIgYWRkVG9Hcm91cCA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdhZGRUb0dyb3VwJyxcbiAgICBjYWxsOiAnc2hoX2FkZFRvR3JvdXAnLFxuICAgIHBhcmFtczogMFxufSk7XG5cbnZhciBtZXRob2RzID0gW1xuICAgIHBvc3QsXG4gICAgbmV3SWRlbnRpdHksXG4gICAgaGFzSWRlbnRpdHksXG4gICAgbmV3R3JvdXAsXG4gICAgYWRkVG9Hcm91cFxuXTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgbWV0aG9kczogbWV0aG9kc1xufTtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSB0cmFuc2Zlci5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgd2ViMyA9IHJlcXVpcmUoJy4uL3dlYjMnKTtcbnZhciBJQ0FQID0gcmVxdWlyZSgnLi9pY2FwJyk7XG52YXIgbmFtZXJlZyA9IHJlcXVpcmUoJy4vbmFtZXJlZycpO1xudmFyIGNvbnRyYWN0ID0gcmVxdWlyZSgnLi9jb250cmFjdCcpO1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIG1ha2UgSUNBUCB0cmFuc2ZlclxuICpcbiAqIEBtZXRob2QgdHJhbnNmZXJcbiAqIEBwYXJhbSB7U3RyaW5nfSBpYmFuIG51bWJlclxuICogQHBhcmFtIHtTdHJpbmd9IGZyb20gKGFkZHJlc3MpXG4gKiBAcGFyYW0ge1ZhbHVlfSB2YWx1ZSB0byBiZSB0cmFuZmVyZWRcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrLCBjYWxsYmFja1xuICovXG52YXIgdHJhbnNmZXIgPSBmdW5jdGlvbiAoZnJvbSwgaWJhbiwgdmFsdWUsIGNhbGxiYWNrKSB7XG4gICAgdmFyIGljYXAgPSBuZXcgSUNBUChpYmFuKTsgXG4gICAgaWYgKCFpY2FwLmlzVmFsaWQoKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ2ludmFsaWQgaWJhbiBhZGRyZXNzJyk7XG4gICAgfVxuXG4gICAgaWYgKGljYXAuaXNEaXJlY3QoKSkge1xuICAgICAgICByZXR1cm4gdHJhbnNmZXJUb0FkZHJlc3MoZnJvbSwgaWNhcC5hZGRyZXNzKCksIHZhbHVlLCBjYWxsYmFjayk7XG4gICAgfVxuICAgIFxuICAgIGlmICghY2FsbGJhY2spIHtcbiAgICAgICAgdmFyIGFkZHJlc3MgPSBuYW1lcmVnLmFkZHIoaWNhcC5pbnN0aXR1dGlvbigpKTtcbiAgICAgICAgcmV0dXJuIGRlcG9zaXQoZnJvbSwgYWRkcmVzcywgdmFsdWUsIGljYXAuY2xpZW50KCkpO1xuICAgIH1cblxuICAgIG5hbWVyZWcuYWRkcihpY2FwLmluc2l0dXRpb24oKSwgZnVuY3Rpb24gKGVyciwgYWRkcmVzcykge1xuICAgICAgICByZXR1cm4gZGVwb3NpdChmcm9tLCBhZGRyZXNzLCB2YWx1ZSwgaWNhcC5jbGllbnQoKSwgY2FsbGJhY2spO1xuICAgIH0pO1xuICAgIFxufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byB0cmFuc2ZlciBmdW5kcyB0byBjZXJ0YWluIGFkZHJlc3NcbiAqXG4gKiBAbWV0aG9kIHRyYW5zZmVyVG9BZGRyZXNzXG4gKiBAcGFyYW0ge1N0cmluZ30gYWRkcmVzc1xuICogQHBhcmFtIHtTdHJpbmd9IGZyb20gKGFkZHJlc3MpXG4gKiBAcGFyYW0ge1ZhbHVlfSB2YWx1ZSB0byBiZSB0cmFuZmVyZWRcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrLCBjYWxsYmFja1xuICovXG52YXIgdHJhbnNmZXJUb0FkZHJlc3MgPSBmdW5jdGlvbiAoZnJvbSwgYWRkcmVzcywgdmFsdWUsIGNhbGxiYWNrKSB7XG4gICAgcmV0dXJuIHdlYjMuZXRoLnNlbmRUcmFuc2FjdGlvbih7XG4gICAgICAgIGFkZHJlc3M6IGFkZHJlc3MsXG4gICAgICAgIGZyb206IGZyb20sXG4gICAgICAgIHZhbHVlOiB2YWx1ZVxuICAgIH0sIGNhbGxiYWNrKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZGVwb3NpdCBmdW5kcyB0byBnZW5lcmljIEV4Y2hhbmdlIGNvbnRyYWN0IChtdXN0IGltcGxlbWVudCBkZXBvc2l0KGJ5dGVzMzIpIG1ldGhvZCEpXG4gKlxuICogQG1ldGhvZCBkZXBvc2l0XG4gKiBAcGFyYW0ge1N0cmluZ30gYWRkcmVzc1xuICogQHBhcmFtIHtTdHJpbmd9IGZyb20gKGFkZHJlc3MpXG4gKiBAcGFyYW0ge1ZhbHVlfSB2YWx1ZSB0byBiZSB0cmFuZmVyZWRcbiAqIEBwYXJhbSB7U3RyaW5nfSBjbGllbnQgdW5pcXVlIGlkZW50aWZpZXJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrLCBjYWxsYmFja1xuICovXG52YXIgZGVwb3NpdCA9IGZ1bmN0aW9uIChmcm9tLCBhZGRyZXNzLCB2YWx1ZSwgY2xpZW50LCBjYWxsYmFjaykge1xuICAgIHZhciBhYmkgPSBbe1wiY29uc3RhbnRcIjpmYWxzZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJuYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcIm5hbWVcIjpcImRlcG9zaXRcIixcIm91dHB1dHNcIjpbXSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9XTtcbiAgICByZXR1cm4gY29udHJhY3QoYWJpKS5hdChhZGRyZXNzKS5kZXBvc2l0KGNsaWVudCwge1xuICAgICAgICBmcm9tOiBmcm9tLFxuICAgICAgICB2YWx1ZTogdmFsdWVcbiAgICB9LCBjYWxsYmFjayk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IHRyYW5zZmVyO1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBAZmlsZSB3YXRjaGVzLmpzXG4gKiBAYXV0aG9yczpcbiAqICAgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE1XG4gKi9cblxudmFyIE1ldGhvZCA9IHJlcXVpcmUoJy4vbWV0aG9kJyk7XG5cbi8vLyBAcmV0dXJucyBhbiBhcnJheSBvZiBvYmplY3RzIGRlc2NyaWJpbmcgd2ViMy5ldGguZmlsdGVyIGFwaSBtZXRob2RzXG52YXIgZXRoID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciBuZXdGaWx0ZXJDYWxsID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICAgICAgdmFyIHR5cGUgPSBhcmdzWzBdO1xuXG4gICAgICAgIHN3aXRjaCh0eXBlKSB7XG4gICAgICAgICAgICBjYXNlICdsYXRlc3QnOlxuICAgICAgICAgICAgICAgIGFyZ3MucG9wKCk7XG4gICAgICAgICAgICAgICAgdGhpcy5wYXJhbXMgPSAwO1xuICAgICAgICAgICAgICAgIHJldHVybiAnZXRoX25ld0Jsb2NrRmlsdGVyJztcbiAgICAgICAgICAgIGNhc2UgJ3BlbmRpbmcnOlxuICAgICAgICAgICAgICAgIGFyZ3MucG9wKCk7XG4gICAgICAgICAgICAgICAgdGhpcy5wYXJhbXMgPSAwO1xuICAgICAgICAgICAgICAgIHJldHVybiAnZXRoX25ld1BlbmRpbmdUcmFuc2FjdGlvbkZpbHRlcic7XG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgIHJldHVybiAnZXRoX25ld0ZpbHRlcic7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgdmFyIG5ld0ZpbHRlciA9IG5ldyBNZXRob2Qoe1xuICAgICAgICBuYW1lOiAnbmV3RmlsdGVyJyxcbiAgICAgICAgY2FsbDogbmV3RmlsdGVyQ2FsbCxcbiAgICAgICAgcGFyYW1zOiAxXG4gICAgfSk7XG5cbiAgICB2YXIgdW5pbnN0YWxsRmlsdGVyID0gbmV3IE1ldGhvZCh7XG4gICAgICAgIG5hbWU6ICd1bmluc3RhbGxGaWx0ZXInLFxuICAgICAgICBjYWxsOiAnZXRoX3VuaW5zdGFsbEZpbHRlcicsXG4gICAgICAgIHBhcmFtczogMVxuICAgIH0pO1xuXG4gICAgdmFyIGdldExvZ3MgPSBuZXcgTWV0aG9kKHtcbiAgICAgICAgbmFtZTogJ2dldExvZ3MnLFxuICAgICAgICBjYWxsOiAnZXRoX2dldEZpbHRlckxvZ3MnLFxuICAgICAgICBwYXJhbXM6IDFcbiAgICB9KTtcblxuICAgIHZhciBwb2xsID0gbmV3IE1ldGhvZCh7XG4gICAgICAgIG5hbWU6ICdwb2xsJyxcbiAgICAgICAgY2FsbDogJ2V0aF9nZXRGaWx0ZXJDaGFuZ2VzJyxcbiAgICAgICAgcGFyYW1zOiAxXG4gICAgfSk7XG5cbiAgICByZXR1cm4gW1xuICAgICAgICBuZXdGaWx0ZXIsXG4gICAgICAgIHVuaW5zdGFsbEZpbHRlcixcbiAgICAgICAgZ2V0TG9ncyxcbiAgICAgICAgcG9sbFxuICAgIF07XG59O1xuXG4vLy8gQHJldHVybnMgYW4gYXJyYXkgb2Ygb2JqZWN0cyBkZXNjcmliaW5nIHdlYjMuc2hoLndhdGNoIGFwaSBtZXRob2RzXG52YXIgc2hoID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciBuZXdGaWx0ZXIgPSBuZXcgTWV0aG9kKHtcbiAgICAgICAgbmFtZTogJ25ld0ZpbHRlcicsXG4gICAgICAgIGNhbGw6ICdzaGhfbmV3RmlsdGVyJyxcbiAgICAgICAgcGFyYW1zOiAxXG4gICAgfSk7XG5cbiAgICB2YXIgdW5pbnN0YWxsRmlsdGVyID0gbmV3IE1ldGhvZCh7XG4gICAgICAgIG5hbWU6ICd1bmluc3RhbGxGaWx0ZXInLFxuICAgICAgICBjYWxsOiAnc2hoX3VuaW5zdGFsbEZpbHRlcicsXG4gICAgICAgIHBhcmFtczogMVxuICAgIH0pO1xuXG4gICAgdmFyIGdldExvZ3MgPSBuZXcgTWV0aG9kKHtcbiAgICAgICAgbmFtZTogJ2dldExvZ3MnLFxuICAgICAgICBjYWxsOiAnc2hoX2dldE1lc3NhZ2VzJyxcbiAgICAgICAgcGFyYW1zOiAxXG4gICAgfSk7XG5cbiAgICB2YXIgcG9sbCA9IG5ldyBNZXRob2Qoe1xuICAgICAgICBuYW1lOiAncG9sbCcsXG4gICAgICAgIGNhbGw6ICdzaGhfZ2V0RmlsdGVyQ2hhbmdlcycsXG4gICAgICAgIHBhcmFtczogMVxuICAgIH0pO1xuXG4gICAgcmV0dXJuIFtcbiAgICAgICAgbmV3RmlsdGVyLFxuICAgICAgICB1bmluc3RhbGxGaWx0ZXIsXG4gICAgICAgIGdldExvZ3MsXG4gICAgICAgIHBvbGxcbiAgICBdO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgZXRoOiBldGgsXG4gICAgc2hoOiBzaGhcbn07XG5cbiIsbnVsbCwiOyhmdW5jdGlvbiAocm9vdCwgZmFjdG9yeSkge1xuXHRpZiAodHlwZW9mIGV4cG9ydHMgPT09IFwib2JqZWN0XCIpIHtcblx0XHQvLyBDb21tb25KU1xuXHRcdG1vZHVsZS5leHBvcnRzID0gZXhwb3J0cyA9IGZhY3RvcnkoKTtcblx0fVxuXHRlbHNlIGlmICh0eXBlb2YgZGVmaW5lID09PSBcImZ1bmN0aW9uXCIgJiYgZGVmaW5lLmFtZCkge1xuXHRcdC8vIEFNRFxuXHRcdGRlZmluZShbXSwgZmFjdG9yeSk7XG5cdH1cblx0ZWxzZSB7XG5cdFx0Ly8gR2xvYmFsIChicm93c2VyKVxuXHRcdHJvb3QuQ3J5cHRvSlMgPSBmYWN0b3J5KCk7XG5cdH1cbn0odGhpcywgZnVuY3Rpb24gKCkge1xuXG5cdC8qKlxuXHQgKiBDcnlwdG9KUyBjb3JlIGNvbXBvbmVudHMuXG5cdCAqL1xuXHR2YXIgQ3J5cHRvSlMgPSBDcnlwdG9KUyB8fCAoZnVuY3Rpb24gKE1hdGgsIHVuZGVmaW5lZCkge1xuXHQgICAgLyoqXG5cdCAgICAgKiBDcnlwdG9KUyBuYW1lc3BhY2UuXG5cdCAgICAgKi9cblx0ICAgIHZhciBDID0ge307XG5cblx0ICAgIC8qKlxuXHQgICAgICogTGlicmFyeSBuYW1lc3BhY2UuXG5cdCAgICAgKi9cblx0ICAgIHZhciBDX2xpYiA9IEMubGliID0ge307XG5cblx0ICAgIC8qKlxuXHQgICAgICogQmFzZSBvYmplY3QgZm9yIHByb3RvdHlwYWwgaW5oZXJpdGFuY2UuXG5cdCAgICAgKi9cblx0ICAgIHZhciBCYXNlID0gQ19saWIuQmFzZSA9IChmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgZnVuY3Rpb24gRigpIHt9XG5cblx0ICAgICAgICByZXR1cm4ge1xuXHQgICAgICAgICAgICAvKipcblx0ICAgICAgICAgICAgICogQ3JlYXRlcyBhIG5ldyBvYmplY3QgdGhhdCBpbmhlcml0cyBmcm9tIHRoaXMgb2JqZWN0LlxuXHQgICAgICAgICAgICAgKlxuXHQgICAgICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3ZlcnJpZGVzIFByb3BlcnRpZXMgdG8gY29weSBpbnRvIHRoZSBuZXcgb2JqZWN0LlxuXHQgICAgICAgICAgICAgKlxuXHQgICAgICAgICAgICAgKiBAcmV0dXJuIHtPYmplY3R9IFRoZSBuZXcgb2JqZWN0LlxuXHQgICAgICAgICAgICAgKlxuXHQgICAgICAgICAgICAgKiBAc3RhdGljXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqICAgICB2YXIgTXlUeXBlID0gQ3J5cHRvSlMubGliLkJhc2UuZXh0ZW5kKHtcblx0ICAgICAgICAgICAgICogICAgICAgICBmaWVsZDogJ3ZhbHVlJyxcblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogICAgICAgICBtZXRob2Q6IGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgICogICAgICAgICB9XG5cdCAgICAgICAgICAgICAqICAgICB9KTtcblx0ICAgICAgICAgICAgICovXG5cdCAgICAgICAgICAgIGV4dGVuZDogZnVuY3Rpb24gKG92ZXJyaWRlcykge1xuXHQgICAgICAgICAgICAgICAgLy8gU3Bhd25cblx0ICAgICAgICAgICAgICAgIEYucHJvdG90eXBlID0gdGhpcztcblx0ICAgICAgICAgICAgICAgIHZhciBzdWJ0eXBlID0gbmV3IEYoKTtcblxuXHQgICAgICAgICAgICAgICAgLy8gQXVnbWVudFxuXHQgICAgICAgICAgICAgICAgaWYgKG92ZXJyaWRlcykge1xuXHQgICAgICAgICAgICAgICAgICAgIHN1YnR5cGUubWl4SW4ob3ZlcnJpZGVzKTtcblx0ICAgICAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICAgICAgLy8gQ3JlYXRlIGRlZmF1bHQgaW5pdGlhbGl6ZXJcblx0ICAgICAgICAgICAgICAgIGlmICghc3VidHlwZS5oYXNPd25Qcm9wZXJ0eSgnaW5pdCcpKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgc3VidHlwZS5pbml0ID0gZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICAgICAgICAgICAgICBzdWJ0eXBlLiRzdXBlci5pbml0LmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG5cdCAgICAgICAgICAgICAgICAgICAgfTtcblx0ICAgICAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICAgICAgLy8gSW5pdGlhbGl6ZXIncyBwcm90b3R5cGUgaXMgdGhlIHN1YnR5cGUgb2JqZWN0XG5cdCAgICAgICAgICAgICAgICBzdWJ0eXBlLmluaXQucHJvdG90eXBlID0gc3VidHlwZTtcblxuXHQgICAgICAgICAgICAgICAgLy8gUmVmZXJlbmNlIHN1cGVydHlwZVxuXHQgICAgICAgICAgICAgICAgc3VidHlwZS4kc3VwZXIgPSB0aGlzO1xuXG5cdCAgICAgICAgICAgICAgICByZXR1cm4gc3VidHlwZTtcblx0ICAgICAgICAgICAgfSxcblxuXHQgICAgICAgICAgICAvKipcblx0ICAgICAgICAgICAgICogRXh0ZW5kcyB0aGlzIG9iamVjdCBhbmQgcnVucyB0aGUgaW5pdCBtZXRob2QuXG5cdCAgICAgICAgICAgICAqIEFyZ3VtZW50cyB0byBjcmVhdGUoKSB3aWxsIGJlIHBhc3NlZCB0byBpbml0KCkuXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqIEByZXR1cm4ge09iamVjdH0gVGhlIG5ldyBvYmplY3QuXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqIEBzdGF0aWNcblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogICAgIHZhciBpbnN0YW5jZSA9IE15VHlwZS5jcmVhdGUoKTtcblx0ICAgICAgICAgICAgICovXG5cdCAgICAgICAgICAgIGNyZWF0ZTogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICAgICAgdmFyIGluc3RhbmNlID0gdGhpcy5leHRlbmQoKTtcblx0ICAgICAgICAgICAgICAgIGluc3RhbmNlLmluaXQuYXBwbHkoaW5zdGFuY2UsIGFyZ3VtZW50cyk7XG5cblx0ICAgICAgICAgICAgICAgIHJldHVybiBpbnN0YW5jZTtcblx0ICAgICAgICAgICAgfSxcblxuXHQgICAgICAgICAgICAvKipcblx0ICAgICAgICAgICAgICogSW5pdGlhbGl6ZXMgYSBuZXdseSBjcmVhdGVkIG9iamVjdC5cblx0ICAgICAgICAgICAgICogT3ZlcnJpZGUgdGhpcyBtZXRob2QgdG8gYWRkIHNvbWUgbG9naWMgd2hlbiB5b3VyIG9iamVjdHMgYXJlIGNyZWF0ZWQuXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqICAgICB2YXIgTXlUeXBlID0gQ3J5cHRvSlMubGliLkJhc2UuZXh0ZW5kKHtcblx0ICAgICAgICAgICAgICogICAgICAgICBpbml0OiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgICAqICAgICAgICAgICAgIC8vIC4uLlxuXHQgICAgICAgICAgICAgKiAgICAgICAgIH1cblx0ICAgICAgICAgICAgICogICAgIH0pO1xuXHQgICAgICAgICAgICAgKi9cblx0ICAgICAgICAgICAgaW5pdDogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICB9LFxuXG5cdCAgICAgICAgICAgIC8qKlxuXHQgICAgICAgICAgICAgKiBDb3BpZXMgcHJvcGVydGllcyBpbnRvIHRoaXMgb2JqZWN0LlxuXHQgICAgICAgICAgICAgKlxuXHQgICAgICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gcHJvcGVydGllcyBUaGUgcHJvcGVydGllcyB0byBtaXggaW4uXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqICAgICBNeVR5cGUubWl4SW4oe1xuXHQgICAgICAgICAgICAgKiAgICAgICAgIGZpZWxkOiAndmFsdWUnXG5cdCAgICAgICAgICAgICAqICAgICB9KTtcblx0ICAgICAgICAgICAgICovXG5cdCAgICAgICAgICAgIG1peEluOiBmdW5jdGlvbiAocHJvcGVydGllcykge1xuXHQgICAgICAgICAgICAgICAgZm9yICh2YXIgcHJvcGVydHlOYW1lIGluIHByb3BlcnRpZXMpIHtcblx0ICAgICAgICAgICAgICAgICAgICBpZiAocHJvcGVydGllcy5oYXNPd25Qcm9wZXJ0eShwcm9wZXJ0eU5hbWUpKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHRoaXNbcHJvcGVydHlOYW1lXSA9IHByb3BlcnRpZXNbcHJvcGVydHlOYW1lXTtcblx0ICAgICAgICAgICAgICAgICAgICB9XG5cdCAgICAgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgICAgIC8vIElFIHdvbid0IGNvcHkgdG9TdHJpbmcgdXNpbmcgdGhlIGxvb3AgYWJvdmVcblx0ICAgICAgICAgICAgICAgIGlmIChwcm9wZXJ0aWVzLmhhc093blByb3BlcnR5KCd0b1N0cmluZycpKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgdGhpcy50b1N0cmluZyA9IHByb3BlcnRpZXMudG9TdHJpbmc7XG5cdCAgICAgICAgICAgICAgICB9XG5cdCAgICAgICAgICAgIH0sXG5cblx0ICAgICAgICAgICAgLyoqXG5cdCAgICAgICAgICAgICAqIENyZWF0ZXMgYSBjb3B5IG9mIHRoaXMgb2JqZWN0LlxuXHQgICAgICAgICAgICAgKlxuXHQgICAgICAgICAgICAgKiBAcmV0dXJuIHtPYmplY3R9IFRoZSBjbG9uZS5cblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogICAgIHZhciBjbG9uZSA9IGluc3RhbmNlLmNsb25lKCk7XG5cdCAgICAgICAgICAgICAqL1xuXHQgICAgICAgICAgICBjbG9uZTogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuaW5pdC5wcm90b3R5cGUuZXh0ZW5kKHRoaXMpO1xuXHQgICAgICAgICAgICB9XG5cdCAgICAgICAgfTtcblx0ICAgIH0oKSk7XG5cblx0ICAgIC8qKlxuXHQgICAgICogQW4gYXJyYXkgb2YgMzItYml0IHdvcmRzLlxuXHQgICAgICpcblx0ICAgICAqIEBwcm9wZXJ0eSB7QXJyYXl9IHdvcmRzIFRoZSBhcnJheSBvZiAzMi1iaXQgd29yZHMuXG5cdCAgICAgKiBAcHJvcGVydHkge251bWJlcn0gc2lnQnl0ZXMgVGhlIG51bWJlciBvZiBzaWduaWZpY2FudCBieXRlcyBpbiB0aGlzIHdvcmQgYXJyYXkuXG5cdCAgICAgKi9cblx0ICAgIHZhciBXb3JkQXJyYXkgPSBDX2xpYi5Xb3JkQXJyYXkgPSBCYXNlLmV4dGVuZCh7XG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogSW5pdGlhbGl6ZXMgYSBuZXdseSBjcmVhdGVkIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge0FycmF5fSB3b3JkcyAoT3B0aW9uYWwpIEFuIGFycmF5IG9mIDMyLWJpdCB3b3Jkcy5cblx0ICAgICAgICAgKiBAcGFyYW0ge251bWJlcn0gc2lnQnl0ZXMgKE9wdGlvbmFsKSBUaGUgbnVtYmVyIG9mIHNpZ25pZmljYW50IGJ5dGVzIGluIHRoZSB3b3Jkcy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHdvcmRBcnJheSA9IENyeXB0b0pTLmxpYi5Xb3JkQXJyYXkuY3JlYXRlKCk7XG5cdCAgICAgICAgICogICAgIHZhciB3b3JkQXJyYXkgPSBDcnlwdG9KUy5saWIuV29yZEFycmF5LmNyZWF0ZShbMHgwMDAxMDIwMywgMHgwNDA1MDYwN10pO1xuXHQgICAgICAgICAqICAgICB2YXIgd29yZEFycmF5ID0gQ3J5cHRvSlMubGliLldvcmRBcnJheS5jcmVhdGUoWzB4MDAwMTAyMDMsIDB4MDQwNTA2MDddLCA2KTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBpbml0OiBmdW5jdGlvbiAod29yZHMsIHNpZ0J5dGVzKSB7XG5cdCAgICAgICAgICAgIHdvcmRzID0gdGhpcy53b3JkcyA9IHdvcmRzIHx8IFtdO1xuXG5cdCAgICAgICAgICAgIGlmIChzaWdCeXRlcyAhPSB1bmRlZmluZWQpIHtcblx0ICAgICAgICAgICAgICAgIHRoaXMuc2lnQnl0ZXMgPSBzaWdCeXRlcztcblx0ICAgICAgICAgICAgfSBlbHNlIHtcblx0ICAgICAgICAgICAgICAgIHRoaXMuc2lnQnl0ZXMgPSB3b3Jkcy5sZW5ndGggKiA0O1xuXHQgICAgICAgICAgICB9XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENvbnZlcnRzIHRoaXMgd29yZCBhcnJheSB0byBhIHN0cmluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7RW5jb2Rlcn0gZW5jb2RlciAoT3B0aW9uYWwpIFRoZSBlbmNvZGluZyBzdHJhdGVneSB0byB1c2UuIERlZmF1bHQ6IENyeXB0b0pTLmVuYy5IZXhcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge3N0cmluZ30gVGhlIHN0cmluZ2lmaWVkIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciBzdHJpbmcgPSB3b3JkQXJyYXkgKyAnJztcblx0ICAgICAgICAgKiAgICAgdmFyIHN0cmluZyA9IHdvcmRBcnJheS50b1N0cmluZygpO1xuXHQgICAgICAgICAqICAgICB2YXIgc3RyaW5nID0gd29yZEFycmF5LnRvU3RyaW5nKENyeXB0b0pTLmVuYy5VdGY4KTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICB0b1N0cmluZzogZnVuY3Rpb24gKGVuY29kZXIpIHtcblx0ICAgICAgICAgICAgcmV0dXJuIChlbmNvZGVyIHx8IEhleCkuc3RyaW5naWZ5KHRoaXMpO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDb25jYXRlbmF0ZXMgYSB3b3JkIGFycmF5IHRvIHRoaXMgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7V29yZEFycmF5fSB3b3JkQXJyYXkgVGhlIHdvcmQgYXJyYXkgdG8gYXBwZW5kLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7V29yZEFycmF5fSBUaGlzIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHdvcmRBcnJheTEuY29uY2F0KHdvcmRBcnJheTIpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIGNvbmNhdDogZnVuY3Rpb24gKHdvcmRBcnJheSkge1xuXHQgICAgICAgICAgICAvLyBTaG9ydGN1dHNcblx0ICAgICAgICAgICAgdmFyIHRoaXNXb3JkcyA9IHRoaXMud29yZHM7XG5cdCAgICAgICAgICAgIHZhciB0aGF0V29yZHMgPSB3b3JkQXJyYXkud29yZHM7XG5cdCAgICAgICAgICAgIHZhciB0aGlzU2lnQnl0ZXMgPSB0aGlzLnNpZ0J5dGVzO1xuXHQgICAgICAgICAgICB2YXIgdGhhdFNpZ0J5dGVzID0gd29yZEFycmF5LnNpZ0J5dGVzO1xuXG5cdCAgICAgICAgICAgIC8vIENsYW1wIGV4Y2VzcyBiaXRzXG5cdCAgICAgICAgICAgIHRoaXMuY2xhbXAoKTtcblxuXHQgICAgICAgICAgICAvLyBDb25jYXRcblx0ICAgICAgICAgICAgaWYgKHRoaXNTaWdCeXRlcyAlIDQpIHtcblx0ICAgICAgICAgICAgICAgIC8vIENvcHkgb25lIGJ5dGUgYXQgYSB0aW1lXG5cdCAgICAgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRoYXRTaWdCeXRlczsgaSsrKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgdmFyIHRoYXRCeXRlID0gKHRoYXRXb3Jkc1tpID4+PiAyXSA+Pj4gKDI0IC0gKGkgJSA0KSAqIDgpKSAmIDB4ZmY7XG5cdCAgICAgICAgICAgICAgICAgICAgdGhpc1dvcmRzWyh0aGlzU2lnQnl0ZXMgKyBpKSA+Pj4gMl0gfD0gdGhhdEJ5dGUgPDwgKDI0IC0gKCh0aGlzU2lnQnl0ZXMgKyBpKSAlIDQpICogOCk7XG5cdCAgICAgICAgICAgICAgICB9XG5cdCAgICAgICAgICAgIH0gZWxzZSB7XG5cdCAgICAgICAgICAgICAgICAvLyBDb3B5IG9uZSB3b3JkIGF0IGEgdGltZVxuXHQgICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aGF0U2lnQnl0ZXM7IGkgKz0gNCkge1xuXHQgICAgICAgICAgICAgICAgICAgIHRoaXNXb3Jkc1sodGhpc1NpZ0J5dGVzICsgaSkgPj4+IDJdID0gdGhhdFdvcmRzW2kgPj4+IDJdO1xuXHQgICAgICAgICAgICAgICAgfVxuXHQgICAgICAgICAgICB9XG5cdCAgICAgICAgICAgIHRoaXMuc2lnQnl0ZXMgKz0gdGhhdFNpZ0J5dGVzO1xuXG5cdCAgICAgICAgICAgIC8vIENoYWluYWJsZVxuXHQgICAgICAgICAgICByZXR1cm4gdGhpcztcblx0ICAgICAgICB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogUmVtb3ZlcyBpbnNpZ25pZmljYW50IGJpdHMuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHdvcmRBcnJheS5jbGFtcCgpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIGNsYW1wOiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgIC8vIFNob3J0Y3V0c1xuXHQgICAgICAgICAgICB2YXIgd29yZHMgPSB0aGlzLndvcmRzO1xuXHQgICAgICAgICAgICB2YXIgc2lnQnl0ZXMgPSB0aGlzLnNpZ0J5dGVzO1xuXG5cdCAgICAgICAgICAgIC8vIENsYW1wXG5cdCAgICAgICAgICAgIHdvcmRzW3NpZ0J5dGVzID4+PiAyXSAmPSAweGZmZmZmZmZmIDw8ICgzMiAtIChzaWdCeXRlcyAlIDQpICogOCk7XG5cdCAgICAgICAgICAgIHdvcmRzLmxlbmd0aCA9IE1hdGguY2VpbChzaWdCeXRlcyAvIDQpO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDcmVhdGVzIGEgY29weSBvZiB0aGlzIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtXb3JkQXJyYXl9IFRoZSBjbG9uZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIGNsb25lID0gd29yZEFycmF5LmNsb25lKCk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgY2xvbmU6IGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgdmFyIGNsb25lID0gQmFzZS5jbG9uZS5jYWxsKHRoaXMpO1xuXHQgICAgICAgICAgICBjbG9uZS53b3JkcyA9IHRoaXMud29yZHMuc2xpY2UoMCk7XG5cblx0ICAgICAgICAgICAgcmV0dXJuIGNsb25lO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDcmVhdGVzIGEgd29yZCBhcnJheSBmaWxsZWQgd2l0aCByYW5kb20gYnl0ZXMuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge251bWJlcn0gbkJ5dGVzIFRoZSBudW1iZXIgb2YgcmFuZG9tIGJ5dGVzIHRvIGdlbmVyYXRlLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7V29yZEFycmF5fSBUaGUgcmFuZG9tIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAc3RhdGljXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciB3b3JkQXJyYXkgPSBDcnlwdG9KUy5saWIuV29yZEFycmF5LnJhbmRvbSgxNik7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgcmFuZG9tOiBmdW5jdGlvbiAobkJ5dGVzKSB7XG5cdCAgICAgICAgICAgIHZhciB3b3JkcyA9IFtdO1xuXG5cdCAgICAgICAgICAgIHZhciByID0gKGZ1bmN0aW9uIChtX3cpIHtcblx0ICAgICAgICAgICAgICAgIHZhciBtX3cgPSBtX3c7XG5cdCAgICAgICAgICAgICAgICB2YXIgbV96ID0gMHgzYWRlNjhiMTtcblx0ICAgICAgICAgICAgICAgIHZhciBtYXNrID0gMHhmZmZmZmZmZjtcblxuXHQgICAgICAgICAgICAgICAgcmV0dXJuIGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgICAgICAgICBtX3ogPSAoMHg5MDY5ICogKG1feiAmIDB4RkZGRikgKyAobV96ID4+IDB4MTApKSAmIG1hc2s7XG5cdCAgICAgICAgICAgICAgICAgICAgbV93ID0gKDB4NDY1MCAqIChtX3cgJiAweEZGRkYpICsgKG1fdyA+PiAweDEwKSkgJiBtYXNrO1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciByZXN1bHQgPSAoKG1feiA8PCAweDEwKSArIG1fdykgJiBtYXNrO1xuXHQgICAgICAgICAgICAgICAgICAgIHJlc3VsdCAvPSAweDEwMDAwMDAwMDtcblx0ICAgICAgICAgICAgICAgICAgICByZXN1bHQgKz0gMC41O1xuXHQgICAgICAgICAgICAgICAgICAgIHJldHVybiByZXN1bHQgKiAoTWF0aC5yYW5kb20oKSA+IC41ID8gMSA6IC0xKTtcblx0ICAgICAgICAgICAgICAgIH1cblx0ICAgICAgICAgICAgfSk7XG5cblx0ICAgICAgICAgICAgZm9yICh2YXIgaSA9IDAsIHJjYWNoZTsgaSA8IG5CeXRlczsgaSArPSA0KSB7XG5cdCAgICAgICAgICAgICAgICB2YXIgX3IgPSByKChyY2FjaGUgfHwgTWF0aC5yYW5kb20oKSkgKiAweDEwMDAwMDAwMCk7XG5cblx0ICAgICAgICAgICAgICAgIHJjYWNoZSA9IF9yKCkgKiAweDNhZGU2N2I3O1xuXHQgICAgICAgICAgICAgICAgd29yZHMucHVzaCgoX3IoKSAqIDB4MTAwMDAwMDAwKSB8IDApO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgcmV0dXJuIG5ldyBXb3JkQXJyYXkuaW5pdCh3b3JkcywgbkJ5dGVzKTtcblx0ICAgICAgICB9XG5cdCAgICB9KTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBFbmNvZGVyIG5hbWVzcGFjZS5cblx0ICAgICAqL1xuXHQgICAgdmFyIENfZW5jID0gQy5lbmMgPSB7fTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBIZXggZW5jb2Rpbmcgc3RyYXRlZ3kuXG5cdCAgICAgKi9cblx0ICAgIHZhciBIZXggPSBDX2VuYy5IZXggPSB7XG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQ29udmVydHMgYSB3b3JkIGFycmF5IHRvIGEgaGV4IHN0cmluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7V29yZEFycmF5fSB3b3JkQXJyYXkgVGhlIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtzdHJpbmd9IFRoZSBoZXggc3RyaW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHN0YXRpY1xuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgaGV4U3RyaW5nID0gQ3J5cHRvSlMuZW5jLkhleC5zdHJpbmdpZnkod29yZEFycmF5KTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBzdHJpbmdpZnk6IGZ1bmN0aW9uICh3b3JkQXJyYXkpIHtcblx0ICAgICAgICAgICAgLy8gU2hvcnRjdXRzXG5cdCAgICAgICAgICAgIHZhciB3b3JkcyA9IHdvcmRBcnJheS53b3Jkcztcblx0ICAgICAgICAgICAgdmFyIHNpZ0J5dGVzID0gd29yZEFycmF5LnNpZ0J5dGVzO1xuXG5cdCAgICAgICAgICAgIC8vIENvbnZlcnRcblx0ICAgICAgICAgICAgdmFyIGhleENoYXJzID0gW107XG5cdCAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgc2lnQnl0ZXM7IGkrKykge1xuXHQgICAgICAgICAgICAgICAgdmFyIGJpdGUgPSAod29yZHNbaSA+Pj4gMl0gPj4+ICgyNCAtIChpICUgNCkgKiA4KSkgJiAweGZmO1xuXHQgICAgICAgICAgICAgICAgaGV4Q2hhcnMucHVzaCgoYml0ZSA+Pj4gNCkudG9TdHJpbmcoMTYpKTtcblx0ICAgICAgICAgICAgICAgIGhleENoYXJzLnB1c2goKGJpdGUgJiAweDBmKS50b1N0cmluZygxNikpO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgcmV0dXJuIGhleENoYXJzLmpvaW4oJycpO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDb252ZXJ0cyBhIGhleCBzdHJpbmcgdG8gYSB3b3JkIGFycmF5LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtzdHJpbmd9IGhleFN0ciBUaGUgaGV4IHN0cmluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1dvcmRBcnJheX0gVGhlIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAc3RhdGljXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciB3b3JkQXJyYXkgPSBDcnlwdG9KUy5lbmMuSGV4LnBhcnNlKGhleFN0cmluZyk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgcGFyc2U6IGZ1bmN0aW9uIChoZXhTdHIpIHtcblx0ICAgICAgICAgICAgLy8gU2hvcnRjdXRcblx0ICAgICAgICAgICAgdmFyIGhleFN0ckxlbmd0aCA9IGhleFN0ci5sZW5ndGg7XG5cblx0ICAgICAgICAgICAgLy8gQ29udmVydFxuXHQgICAgICAgICAgICB2YXIgd29yZHMgPSBbXTtcblx0ICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBoZXhTdHJMZW5ndGg7IGkgKz0gMikge1xuXHQgICAgICAgICAgICAgICAgd29yZHNbaSA+Pj4gM10gfD0gcGFyc2VJbnQoaGV4U3RyLnN1YnN0cihpLCAyKSwgMTYpIDw8ICgyNCAtIChpICUgOCkgKiA0KTtcblx0ICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgIHJldHVybiBuZXcgV29yZEFycmF5LmluaXQod29yZHMsIGhleFN0ckxlbmd0aCAvIDIpO1xuXHQgICAgICAgIH1cblx0ICAgIH07XG5cblx0ICAgIC8qKlxuXHQgICAgICogTGF0aW4xIGVuY29kaW5nIHN0cmF0ZWd5LlxuXHQgICAgICovXG5cdCAgICB2YXIgTGF0aW4xID0gQ19lbmMuTGF0aW4xID0ge1xuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENvbnZlcnRzIGEgd29yZCBhcnJheSB0byBhIExhdGluMSBzdHJpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge1dvcmRBcnJheX0gd29yZEFycmF5IFRoZSB3b3JkIGFycmF5LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7c3RyaW5nfSBUaGUgTGF0aW4xIHN0cmluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBzdGF0aWNcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIGxhdGluMVN0cmluZyA9IENyeXB0b0pTLmVuYy5MYXRpbjEuc3RyaW5naWZ5KHdvcmRBcnJheSk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgc3RyaW5naWZ5OiBmdW5jdGlvbiAod29yZEFycmF5KSB7XG5cdCAgICAgICAgICAgIC8vIFNob3J0Y3V0c1xuXHQgICAgICAgICAgICB2YXIgd29yZHMgPSB3b3JkQXJyYXkud29yZHM7XG5cdCAgICAgICAgICAgIHZhciBzaWdCeXRlcyA9IHdvcmRBcnJheS5zaWdCeXRlcztcblxuXHQgICAgICAgICAgICAvLyBDb252ZXJ0XG5cdCAgICAgICAgICAgIHZhciBsYXRpbjFDaGFycyA9IFtdO1xuXHQgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHNpZ0J5dGVzOyBpKyspIHtcblx0ICAgICAgICAgICAgICAgIHZhciBiaXRlID0gKHdvcmRzW2kgPj4+IDJdID4+PiAoMjQgLSAoaSAlIDQpICogOCkpICYgMHhmZjtcblx0ICAgICAgICAgICAgICAgIGxhdGluMUNoYXJzLnB1c2goU3RyaW5nLmZyb21DaGFyQ29kZShiaXRlKSk7XG5cdCAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICByZXR1cm4gbGF0aW4xQ2hhcnMuam9pbignJyk7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENvbnZlcnRzIGEgTGF0aW4xIHN0cmluZyB0byBhIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gbGF0aW4xU3RyIFRoZSBMYXRpbjEgc3RyaW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7V29yZEFycmF5fSBUaGUgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBzdGF0aWNcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHdvcmRBcnJheSA9IENyeXB0b0pTLmVuYy5MYXRpbjEucGFyc2UobGF0aW4xU3RyaW5nKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBwYXJzZTogZnVuY3Rpb24gKGxhdGluMVN0cikge1xuXHQgICAgICAgICAgICAvLyBTaG9ydGN1dFxuXHQgICAgICAgICAgICB2YXIgbGF0aW4xU3RyTGVuZ3RoID0gbGF0aW4xU3RyLmxlbmd0aDtcblxuXHQgICAgICAgICAgICAvLyBDb252ZXJ0XG5cdCAgICAgICAgICAgIHZhciB3b3JkcyA9IFtdO1xuXHQgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxhdGluMVN0ckxlbmd0aDsgaSsrKSB7XG5cdCAgICAgICAgICAgICAgICB3b3Jkc1tpID4+PiAyXSB8PSAobGF0aW4xU3RyLmNoYXJDb2RlQXQoaSkgJiAweGZmKSA8PCAoMjQgLSAoaSAlIDQpICogOCk7XG5cdCAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICByZXR1cm4gbmV3IFdvcmRBcnJheS5pbml0KHdvcmRzLCBsYXRpbjFTdHJMZW5ndGgpO1xuXHQgICAgICAgIH1cblx0ICAgIH07XG5cblx0ICAgIC8qKlxuXHQgICAgICogVVRGLTggZW5jb2Rpbmcgc3RyYXRlZ3kuXG5cdCAgICAgKi9cblx0ICAgIHZhciBVdGY4ID0gQ19lbmMuVXRmOCA9IHtcblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDb252ZXJ0cyBhIHdvcmQgYXJyYXkgdG8gYSBVVEYtOCBzdHJpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge1dvcmRBcnJheX0gd29yZEFycmF5IFRoZSB3b3JkIGFycmF5LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7c3RyaW5nfSBUaGUgVVRGLTggc3RyaW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHN0YXRpY1xuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgdXRmOFN0cmluZyA9IENyeXB0b0pTLmVuYy5VdGY4LnN0cmluZ2lmeSh3b3JkQXJyYXkpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIHN0cmluZ2lmeTogZnVuY3Rpb24gKHdvcmRBcnJheSkge1xuXHQgICAgICAgICAgICB0cnkge1xuXHQgICAgICAgICAgICAgICAgcmV0dXJuIGRlY29kZVVSSUNvbXBvbmVudChlc2NhcGUoTGF0aW4xLnN0cmluZ2lmeSh3b3JkQXJyYXkpKSk7XG5cdCAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcblx0ICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignTWFsZm9ybWVkIFVURi04IGRhdGEnKTtcblx0ICAgICAgICAgICAgfVxuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDb252ZXJ0cyBhIFVURi04IHN0cmluZyB0byBhIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gdXRmOFN0ciBUaGUgVVRGLTggc3RyaW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7V29yZEFycmF5fSBUaGUgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBzdGF0aWNcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHdvcmRBcnJheSA9IENyeXB0b0pTLmVuYy5VdGY4LnBhcnNlKHV0ZjhTdHJpbmcpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIHBhcnNlOiBmdW5jdGlvbiAodXRmOFN0cikge1xuXHQgICAgICAgICAgICByZXR1cm4gTGF0aW4xLnBhcnNlKHVuZXNjYXBlKGVuY29kZVVSSUNvbXBvbmVudCh1dGY4U3RyKSkpO1xuXHQgICAgICAgIH1cblx0ICAgIH07XG5cblx0ICAgIC8qKlxuXHQgICAgICogQWJzdHJhY3QgYnVmZmVyZWQgYmxvY2sgYWxnb3JpdGhtIHRlbXBsYXRlLlxuXHQgICAgICpcblx0ICAgICAqIFRoZSBwcm9wZXJ0eSBibG9ja1NpemUgbXVzdCBiZSBpbXBsZW1lbnRlZCBpbiBhIGNvbmNyZXRlIHN1YnR5cGUuXG5cdCAgICAgKlxuXHQgICAgICogQHByb3BlcnR5IHtudW1iZXJ9IF9taW5CdWZmZXJTaXplIFRoZSBudW1iZXIgb2YgYmxvY2tzIHRoYXQgc2hvdWxkIGJlIGtlcHQgdW5wcm9jZXNzZWQgaW4gdGhlIGJ1ZmZlci4gRGVmYXVsdDogMFxuXHQgICAgICovXG5cdCAgICB2YXIgQnVmZmVyZWRCbG9ja0FsZ29yaXRobSA9IENfbGliLkJ1ZmZlcmVkQmxvY2tBbGdvcml0aG0gPSBCYXNlLmV4dGVuZCh7XG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogUmVzZXRzIHRoaXMgYmxvY2sgYWxnb3JpdGhtJ3MgZGF0YSBidWZmZXIgdG8gaXRzIGluaXRpYWwgc3RhdGUuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIGJ1ZmZlcmVkQmxvY2tBbGdvcml0aG0ucmVzZXQoKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICByZXNldDogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICAvLyBJbml0aWFsIHZhbHVlc1xuXHQgICAgICAgICAgICB0aGlzLl9kYXRhID0gbmV3IFdvcmRBcnJheS5pbml0KCk7XG5cdCAgICAgICAgICAgIHRoaXMuX25EYXRhQnl0ZXMgPSAwO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBBZGRzIG5ldyBkYXRhIHRvIHRoaXMgYmxvY2sgYWxnb3JpdGhtJ3MgYnVmZmVyLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtXb3JkQXJyYXl8c3RyaW5nfSBkYXRhIFRoZSBkYXRhIHRvIGFwcGVuZC4gU3RyaW5ncyBhcmUgY29udmVydGVkIHRvIGEgV29yZEFycmF5IHVzaW5nIFVURi04LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICBidWZmZXJlZEJsb2NrQWxnb3JpdGhtLl9hcHBlbmQoJ2RhdGEnKTtcblx0ICAgICAgICAgKiAgICAgYnVmZmVyZWRCbG9ja0FsZ29yaXRobS5fYXBwZW5kKHdvcmRBcnJheSk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgX2FwcGVuZDogZnVuY3Rpb24gKGRhdGEpIHtcblx0ICAgICAgICAgICAgLy8gQ29udmVydCBzdHJpbmcgdG8gV29yZEFycmF5LCBlbHNlIGFzc3VtZSBXb3JkQXJyYXkgYWxyZWFkeVxuXHQgICAgICAgICAgICBpZiAodHlwZW9mIGRhdGEgPT0gJ3N0cmluZycpIHtcblx0ICAgICAgICAgICAgICAgIGRhdGEgPSBVdGY4LnBhcnNlKGRhdGEpO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgLy8gQXBwZW5kXG5cdCAgICAgICAgICAgIHRoaXMuX2RhdGEuY29uY2F0KGRhdGEpO1xuXHQgICAgICAgICAgICB0aGlzLl9uRGF0YUJ5dGVzICs9IGRhdGEuc2lnQnl0ZXM7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIFByb2Nlc3NlcyBhdmFpbGFibGUgZGF0YSBibG9ja3MuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBUaGlzIG1ldGhvZCBpbnZva2VzIF9kb1Byb2Nlc3NCbG9jayhvZmZzZXQpLCB3aGljaCBtdXN0IGJlIGltcGxlbWVudGVkIGJ5IGEgY29uY3JldGUgc3VidHlwZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gZG9GbHVzaCBXaGV0aGVyIGFsbCBibG9ja3MgYW5kIHBhcnRpYWwgYmxvY2tzIHNob3VsZCBiZSBwcm9jZXNzZWQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtXb3JkQXJyYXl9IFRoZSBwcm9jZXNzZWQgZGF0YS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHByb2Nlc3NlZERhdGEgPSBidWZmZXJlZEJsb2NrQWxnb3JpdGhtLl9wcm9jZXNzKCk7XG5cdCAgICAgICAgICogICAgIHZhciBwcm9jZXNzZWREYXRhID0gYnVmZmVyZWRCbG9ja0FsZ29yaXRobS5fcHJvY2VzcyghISdmbHVzaCcpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIF9wcm9jZXNzOiBmdW5jdGlvbiAoZG9GbHVzaCkge1xuXHQgICAgICAgICAgICAvLyBTaG9ydGN1dHNcblx0ICAgICAgICAgICAgdmFyIGRhdGEgPSB0aGlzLl9kYXRhO1xuXHQgICAgICAgICAgICB2YXIgZGF0YVdvcmRzID0gZGF0YS53b3Jkcztcblx0ICAgICAgICAgICAgdmFyIGRhdGFTaWdCeXRlcyA9IGRhdGEuc2lnQnl0ZXM7XG5cdCAgICAgICAgICAgIHZhciBibG9ja1NpemUgPSB0aGlzLmJsb2NrU2l6ZTtcblx0ICAgICAgICAgICAgdmFyIGJsb2NrU2l6ZUJ5dGVzID0gYmxvY2tTaXplICogNDtcblxuXHQgICAgICAgICAgICAvLyBDb3VudCBibG9ja3MgcmVhZHlcblx0ICAgICAgICAgICAgdmFyIG5CbG9ja3NSZWFkeSA9IGRhdGFTaWdCeXRlcyAvIGJsb2NrU2l6ZUJ5dGVzO1xuXHQgICAgICAgICAgICBpZiAoZG9GbHVzaCkge1xuXHQgICAgICAgICAgICAgICAgLy8gUm91bmQgdXAgdG8gaW5jbHVkZSBwYXJ0aWFsIGJsb2Nrc1xuXHQgICAgICAgICAgICAgICAgbkJsb2Nrc1JlYWR5ID0gTWF0aC5jZWlsKG5CbG9ja3NSZWFkeSk7XG5cdCAgICAgICAgICAgIH0gZWxzZSB7XG5cdCAgICAgICAgICAgICAgICAvLyBSb3VuZCBkb3duIHRvIGluY2x1ZGUgb25seSBmdWxsIGJsb2Nrcyxcblx0ICAgICAgICAgICAgICAgIC8vIGxlc3MgdGhlIG51bWJlciBvZiBibG9ja3MgdGhhdCBtdXN0IHJlbWFpbiBpbiB0aGUgYnVmZmVyXG5cdCAgICAgICAgICAgICAgICBuQmxvY2tzUmVhZHkgPSBNYXRoLm1heCgobkJsb2Nrc1JlYWR5IHwgMCkgLSB0aGlzLl9taW5CdWZmZXJTaXplLCAwKTtcblx0ICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgIC8vIENvdW50IHdvcmRzIHJlYWR5XG5cdCAgICAgICAgICAgIHZhciBuV29yZHNSZWFkeSA9IG5CbG9ja3NSZWFkeSAqIGJsb2NrU2l6ZTtcblxuXHQgICAgICAgICAgICAvLyBDb3VudCBieXRlcyByZWFkeVxuXHQgICAgICAgICAgICB2YXIgbkJ5dGVzUmVhZHkgPSBNYXRoLm1pbihuV29yZHNSZWFkeSAqIDQsIGRhdGFTaWdCeXRlcyk7XG5cblx0ICAgICAgICAgICAgLy8gUHJvY2VzcyBibG9ja3Ncblx0ICAgICAgICAgICAgaWYgKG5Xb3Jkc1JlYWR5KSB7XG5cdCAgICAgICAgICAgICAgICBmb3IgKHZhciBvZmZzZXQgPSAwOyBvZmZzZXQgPCBuV29yZHNSZWFkeTsgb2Zmc2V0ICs9IGJsb2NrU2l6ZSkge1xuXHQgICAgICAgICAgICAgICAgICAgIC8vIFBlcmZvcm0gY29uY3JldGUtYWxnb3JpdGhtIGxvZ2ljXG5cdCAgICAgICAgICAgICAgICAgICAgdGhpcy5fZG9Qcm9jZXNzQmxvY2soZGF0YVdvcmRzLCBvZmZzZXQpO1xuXHQgICAgICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgICAgICAvLyBSZW1vdmUgcHJvY2Vzc2VkIHdvcmRzXG5cdCAgICAgICAgICAgICAgICB2YXIgcHJvY2Vzc2VkV29yZHMgPSBkYXRhV29yZHMuc3BsaWNlKDAsIG5Xb3Jkc1JlYWR5KTtcblx0ICAgICAgICAgICAgICAgIGRhdGEuc2lnQnl0ZXMgLT0gbkJ5dGVzUmVhZHk7XG5cdCAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICAvLyBSZXR1cm4gcHJvY2Vzc2VkIHdvcmRzXG5cdCAgICAgICAgICAgIHJldHVybiBuZXcgV29yZEFycmF5LmluaXQocHJvY2Vzc2VkV29yZHMsIG5CeXRlc1JlYWR5KTtcblx0ICAgICAgICB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQ3JlYXRlcyBhIGNvcHkgb2YgdGhpcyBvYmplY3QuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtPYmplY3R9IFRoZSBjbG9uZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIGNsb25lID0gYnVmZmVyZWRCbG9ja0FsZ29yaXRobS5jbG9uZSgpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgIHZhciBjbG9uZSA9IEJhc2UuY2xvbmUuY2FsbCh0aGlzKTtcblx0ICAgICAgICAgICAgY2xvbmUuX2RhdGEgPSB0aGlzLl9kYXRhLmNsb25lKCk7XG5cblx0ICAgICAgICAgICAgcmV0dXJuIGNsb25lO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICBfbWluQnVmZmVyU2l6ZTogMFxuXHQgICAgfSk7XG5cblx0ICAgIC8qKlxuXHQgICAgICogQWJzdHJhY3QgaGFzaGVyIHRlbXBsYXRlLlxuXHQgICAgICpcblx0ICAgICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBibG9ja1NpemUgVGhlIG51bWJlciBvZiAzMi1iaXQgd29yZHMgdGhpcyBoYXNoZXIgb3BlcmF0ZXMgb24uIERlZmF1bHQ6IDE2ICg1MTIgYml0cylcblx0ICAgICAqL1xuXHQgICAgdmFyIEhhc2hlciA9IENfbGliLkhhc2hlciA9IEJ1ZmZlcmVkQmxvY2tBbGdvcml0aG0uZXh0ZW5kKHtcblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDb25maWd1cmF0aW9uIG9wdGlvbnMuXG5cdCAgICAgICAgICovXG5cdCAgICAgICAgY2ZnOiBCYXNlLmV4dGVuZCgpLFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogSW5pdGlhbGl6ZXMgYSBuZXdseSBjcmVhdGVkIGhhc2hlci5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBjZmcgKE9wdGlvbmFsKSBUaGUgY29uZmlndXJhdGlvbiBvcHRpb25zIHRvIHVzZSBmb3IgdGhpcyBoYXNoIGNvbXB1dGF0aW9uLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgaGFzaGVyID0gQ3J5cHRvSlMuYWxnby5TSEEyNTYuY3JlYXRlKCk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgaW5pdDogZnVuY3Rpb24gKGNmZykge1xuXHQgICAgICAgICAgICAvLyBBcHBseSBjb25maWcgZGVmYXVsdHNcblx0ICAgICAgICAgICAgdGhpcy5jZmcgPSB0aGlzLmNmZy5leHRlbmQoY2ZnKTtcblxuXHQgICAgICAgICAgICAvLyBTZXQgaW5pdGlhbCB2YWx1ZXNcblx0ICAgICAgICAgICAgdGhpcy5yZXNldCgpO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBSZXNldHMgdGhpcyBoYXNoZXIgdG8gaXRzIGluaXRpYWwgc3RhdGUuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIGhhc2hlci5yZXNldCgpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIHJlc2V0OiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgIC8vIFJlc2V0IGRhdGEgYnVmZmVyXG5cdCAgICAgICAgICAgIEJ1ZmZlcmVkQmxvY2tBbGdvcml0aG0ucmVzZXQuY2FsbCh0aGlzKTtcblxuXHQgICAgICAgICAgICAvLyBQZXJmb3JtIGNvbmNyZXRlLWhhc2hlciBsb2dpY1xuXHQgICAgICAgICAgICB0aGlzLl9kb1Jlc2V0KCk7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIFVwZGF0ZXMgdGhpcyBoYXNoZXIgd2l0aCBhIG1lc3NhZ2UuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge1dvcmRBcnJheXxzdHJpbmd9IG1lc3NhZ2VVcGRhdGUgVGhlIG1lc3NhZ2UgdG8gYXBwZW5kLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7SGFzaGVyfSBUaGlzIGhhc2hlci5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgaGFzaGVyLnVwZGF0ZSgnbWVzc2FnZScpO1xuXHQgICAgICAgICAqICAgICBoYXNoZXIudXBkYXRlKHdvcmRBcnJheSk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgdXBkYXRlOiBmdW5jdGlvbiAobWVzc2FnZVVwZGF0ZSkge1xuXHQgICAgICAgICAgICAvLyBBcHBlbmRcblx0ICAgICAgICAgICAgdGhpcy5fYXBwZW5kKG1lc3NhZ2VVcGRhdGUpO1xuXG5cdCAgICAgICAgICAgIC8vIFVwZGF0ZSB0aGUgaGFzaFxuXHQgICAgICAgICAgICB0aGlzLl9wcm9jZXNzKCk7XG5cblx0ICAgICAgICAgICAgLy8gQ2hhaW5hYmxlXG5cdCAgICAgICAgICAgIHJldHVybiB0aGlzO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBGaW5hbGl6ZXMgdGhlIGhhc2ggY29tcHV0YXRpb24uXG5cdCAgICAgICAgICogTm90ZSB0aGF0IHRoZSBmaW5hbGl6ZSBvcGVyYXRpb24gaXMgZWZmZWN0aXZlbHkgYSBkZXN0cnVjdGl2ZSwgcmVhZC1vbmNlIG9wZXJhdGlvbi5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7V29yZEFycmF5fHN0cmluZ30gbWVzc2FnZVVwZGF0ZSAoT3B0aW9uYWwpIEEgZmluYWwgbWVzc2FnZSB1cGRhdGUuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtXb3JkQXJyYXl9IFRoZSBoYXNoLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgaGFzaCA9IGhhc2hlci5maW5hbGl6ZSgpO1xuXHQgICAgICAgICAqICAgICB2YXIgaGFzaCA9IGhhc2hlci5maW5hbGl6ZSgnbWVzc2FnZScpO1xuXHQgICAgICAgICAqICAgICB2YXIgaGFzaCA9IGhhc2hlci5maW5hbGl6ZSh3b3JkQXJyYXkpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIGZpbmFsaXplOiBmdW5jdGlvbiAobWVzc2FnZVVwZGF0ZSkge1xuXHQgICAgICAgICAgICAvLyBGaW5hbCBtZXNzYWdlIHVwZGF0ZVxuXHQgICAgICAgICAgICBpZiAobWVzc2FnZVVwZGF0ZSkge1xuXHQgICAgICAgICAgICAgICAgdGhpcy5fYXBwZW5kKG1lc3NhZ2VVcGRhdGUpO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgLy8gUGVyZm9ybSBjb25jcmV0ZS1oYXNoZXIgbG9naWNcblx0ICAgICAgICAgICAgdmFyIGhhc2ggPSB0aGlzLl9kb0ZpbmFsaXplKCk7XG5cblx0ICAgICAgICAgICAgcmV0dXJuIGhhc2g7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIGJsb2NrU2l6ZTogNTEyLzMyLFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQ3JlYXRlcyBhIHNob3J0Y3V0IGZ1bmN0aW9uIHRvIGEgaGFzaGVyJ3Mgb2JqZWN0IGludGVyZmFjZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7SGFzaGVyfSBoYXNoZXIgVGhlIGhhc2hlciB0byBjcmVhdGUgYSBoZWxwZXIgZm9yLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7RnVuY3Rpb259IFRoZSBzaG9ydGN1dCBmdW5jdGlvbi5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBzdGF0aWNcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIFNIQTI1NiA9IENyeXB0b0pTLmxpYi5IYXNoZXIuX2NyZWF0ZUhlbHBlcihDcnlwdG9KUy5hbGdvLlNIQTI1Nik7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgX2NyZWF0ZUhlbHBlcjogZnVuY3Rpb24gKGhhc2hlcikge1xuXHQgICAgICAgICAgICByZXR1cm4gZnVuY3Rpb24gKG1lc3NhZ2UsIGNmZykge1xuXHQgICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBoYXNoZXIuaW5pdChjZmcpLmZpbmFsaXplKG1lc3NhZ2UpO1xuXHQgICAgICAgICAgICB9O1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDcmVhdGVzIGEgc2hvcnRjdXQgZnVuY3Rpb24gdG8gdGhlIEhNQUMncyBvYmplY3QgaW50ZXJmYWNlLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtIYXNoZXJ9IGhhc2hlciBUaGUgaGFzaGVyIHRvIHVzZSBpbiB0aGlzIEhNQUMgaGVscGVyLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7RnVuY3Rpb259IFRoZSBzaG9ydGN1dCBmdW5jdGlvbi5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBzdGF0aWNcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIEhtYWNTSEEyNTYgPSBDcnlwdG9KUy5saWIuSGFzaGVyLl9jcmVhdGVIbWFjSGVscGVyKENyeXB0b0pTLmFsZ28uU0hBMjU2KTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBfY3JlYXRlSG1hY0hlbHBlcjogZnVuY3Rpb24gKGhhc2hlcikge1xuXHQgICAgICAgICAgICByZXR1cm4gZnVuY3Rpb24gKG1lc3NhZ2UsIGtleSkge1xuXHQgICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBDX2FsZ28uSE1BQy5pbml0KGhhc2hlciwga2V5KS5maW5hbGl6ZShtZXNzYWdlKTtcblx0ICAgICAgICAgICAgfTtcblx0ICAgICAgICB9XG5cdCAgICB9KTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBBbGdvcml0aG0gbmFtZXNwYWNlLlxuXHQgICAgICovXG5cdCAgICB2YXIgQ19hbGdvID0gQy5hbGdvID0ge307XG5cblx0ICAgIHJldHVybiBDO1xuXHR9KE1hdGgpKTtcblxuXG5cdHJldHVybiBDcnlwdG9KUztcblxufSkpOyIsIjsoZnVuY3Rpb24gKHJvb3QsIGZhY3RvcnksIHVuZGVmKSB7XG5cdGlmICh0eXBlb2YgZXhwb3J0cyA9PT0gXCJvYmplY3RcIikge1xuXHRcdC8vIENvbW1vbkpTXG5cdFx0bW9kdWxlLmV4cG9ydHMgPSBleHBvcnRzID0gZmFjdG9yeShyZXF1aXJlKFwiLi9jb3JlXCIpLCByZXF1aXJlKFwiLi94NjQtY29yZVwiKSk7XG5cdH1cblx0ZWxzZSBpZiAodHlwZW9mIGRlZmluZSA9PT0gXCJmdW5jdGlvblwiICYmIGRlZmluZS5hbWQpIHtcblx0XHQvLyBBTURcblx0XHRkZWZpbmUoW1wiLi9jb3JlXCIsIFwiLi94NjQtY29yZVwiXSwgZmFjdG9yeSk7XG5cdH1cblx0ZWxzZSB7XG5cdFx0Ly8gR2xvYmFsIChicm93c2VyKVxuXHRcdGZhY3Rvcnkocm9vdC5DcnlwdG9KUyk7XG5cdH1cbn0odGhpcywgZnVuY3Rpb24gKENyeXB0b0pTKSB7XG5cblx0KGZ1bmN0aW9uIChNYXRoKSB7XG5cdCAgICAvLyBTaG9ydGN1dHNcblx0ICAgIHZhciBDID0gQ3J5cHRvSlM7XG5cdCAgICB2YXIgQ19saWIgPSBDLmxpYjtcblx0ICAgIHZhciBXb3JkQXJyYXkgPSBDX2xpYi5Xb3JkQXJyYXk7XG5cdCAgICB2YXIgSGFzaGVyID0gQ19saWIuSGFzaGVyO1xuXHQgICAgdmFyIENfeDY0ID0gQy54NjQ7XG5cdCAgICB2YXIgWDY0V29yZCA9IENfeDY0LldvcmQ7XG5cdCAgICB2YXIgQ19hbGdvID0gQy5hbGdvO1xuXG5cdCAgICAvLyBDb25zdGFudHMgdGFibGVzXG5cdCAgICB2YXIgUkhPX09GRlNFVFMgPSBbXTtcblx0ICAgIHZhciBQSV9JTkRFWEVTICA9IFtdO1xuXHQgICAgdmFyIFJPVU5EX0NPTlNUQU5UUyA9IFtdO1xuXG5cdCAgICAvLyBDb21wdXRlIENvbnN0YW50c1xuXHQgICAgKGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAvLyBDb21wdXRlIHJobyBvZmZzZXQgY29uc3RhbnRzXG5cdCAgICAgICAgdmFyIHggPSAxLCB5ID0gMDtcblx0ICAgICAgICBmb3IgKHZhciB0ID0gMDsgdCA8IDI0OyB0KyspIHtcblx0ICAgICAgICAgICAgUkhPX09GRlNFVFNbeCArIDUgKiB5XSA9ICgodCArIDEpICogKHQgKyAyKSAvIDIpICUgNjQ7XG5cblx0ICAgICAgICAgICAgdmFyIG5ld1ggPSB5ICUgNTtcblx0ICAgICAgICAgICAgdmFyIG5ld1kgPSAoMiAqIHggKyAzICogeSkgJSA1O1xuXHQgICAgICAgICAgICB4ID0gbmV3WDtcblx0ICAgICAgICAgICAgeSA9IG5ld1k7XG5cdCAgICAgICAgfVxuXG5cdCAgICAgICAgLy8gQ29tcHV0ZSBwaSBpbmRleCBjb25zdGFudHNcblx0ICAgICAgICBmb3IgKHZhciB4ID0gMDsgeCA8IDU7IHgrKykge1xuXHQgICAgICAgICAgICBmb3IgKHZhciB5ID0gMDsgeSA8IDU7IHkrKykge1xuXHQgICAgICAgICAgICAgICAgUElfSU5ERVhFU1t4ICsgNSAqIHldID0geSArICgoMiAqIHggKyAzICogeSkgJSA1KSAqIDU7XG5cdCAgICAgICAgICAgIH1cblx0ICAgICAgICB9XG5cblx0ICAgICAgICAvLyBDb21wdXRlIHJvdW5kIGNvbnN0YW50c1xuXHQgICAgICAgIHZhciBMRlNSID0gMHgwMTtcblx0ICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IDI0OyBpKyspIHtcblx0ICAgICAgICAgICAgdmFyIHJvdW5kQ29uc3RhbnRNc3cgPSAwO1xuXHQgICAgICAgICAgICB2YXIgcm91bmRDb25zdGFudExzdyA9IDA7XG5cblx0ICAgICAgICAgICAgZm9yICh2YXIgaiA9IDA7IGogPCA3OyBqKyspIHtcblx0ICAgICAgICAgICAgICAgIGlmIChMRlNSICYgMHgwMSkge1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciBiaXRQb3NpdGlvbiA9ICgxIDw8IGopIC0gMTtcblx0ICAgICAgICAgICAgICAgICAgICBpZiAoYml0UG9zaXRpb24gPCAzMikge1xuXHQgICAgICAgICAgICAgICAgICAgICAgICByb3VuZENvbnN0YW50THN3IF49IDEgPDwgYml0UG9zaXRpb247XG5cdCAgICAgICAgICAgICAgICAgICAgfSBlbHNlIC8qIGlmIChiaXRQb3NpdGlvbiA+PSAzMikgKi8ge1xuXHQgICAgICAgICAgICAgICAgICAgICAgICByb3VuZENvbnN0YW50TXN3IF49IDEgPDwgKGJpdFBvc2l0aW9uIC0gMzIpO1xuXHQgICAgICAgICAgICAgICAgICAgIH1cblx0ICAgICAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICAgICAgLy8gQ29tcHV0ZSBuZXh0IExGU1Jcblx0ICAgICAgICAgICAgICAgIGlmIChMRlNSICYgMHg4MCkge1xuXHQgICAgICAgICAgICAgICAgICAgIC8vIFByaW1pdGl2ZSBwb2x5bm9taWFsIG92ZXIgR0YoMik6IHheOCArIHheNiArIHheNSArIHheNCArIDFcblx0ICAgICAgICAgICAgICAgICAgICBMRlNSID0gKExGU1IgPDwgMSkgXiAweDcxO1xuXHQgICAgICAgICAgICAgICAgfSBlbHNlIHtcblx0ICAgICAgICAgICAgICAgICAgICBMRlNSIDw8PSAxO1xuXHQgICAgICAgICAgICAgICAgfVxuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgUk9VTkRfQ09OU1RBTlRTW2ldID0gWDY0V29yZC5jcmVhdGUocm91bmRDb25zdGFudE1zdywgcm91bmRDb25zdGFudExzdyk7XG5cdCAgICAgICAgfVxuXHQgICAgfSgpKTtcblxuXHQgICAgLy8gUmV1c2FibGUgb2JqZWN0cyBmb3IgdGVtcG9yYXJ5IHZhbHVlc1xuXHQgICAgdmFyIFQgPSBbXTtcblx0ICAgIChmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCAyNTsgaSsrKSB7XG5cdCAgICAgICAgICAgIFRbaV0gPSBYNjRXb3JkLmNyZWF0ZSgpO1xuXHQgICAgICAgIH1cblx0ICAgIH0oKSk7XG5cblx0ICAgIC8qKlxuXHQgICAgICogU0hBLTMgaGFzaCBhbGdvcml0aG0uXG5cdCAgICAgKi9cblx0ICAgIHZhciBTSEEzID0gQ19hbGdvLlNIQTMgPSBIYXNoZXIuZXh0ZW5kKHtcblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDb25maWd1cmF0aW9uIG9wdGlvbnMuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcHJvcGVydHkge251bWJlcn0gb3V0cHV0TGVuZ3RoXG5cdCAgICAgICAgICogICBUaGUgZGVzaXJlZCBudW1iZXIgb2YgYml0cyBpbiB0aGUgb3V0cHV0IGhhc2guXG5cdCAgICAgICAgICogICBPbmx5IHZhbHVlcyBwZXJtaXR0ZWQgYXJlOiAyMjQsIDI1NiwgMzg0LCA1MTIuXG5cdCAgICAgICAgICogICBEZWZhdWx0OiA1MTJcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBjZmc6IEhhc2hlci5jZmcuZXh0ZW5kKHtcblx0ICAgICAgICAgICAgb3V0cHV0TGVuZ3RoOiA1MTJcblx0ICAgICAgICB9KSxcblxuXHQgICAgICAgIF9kb1Jlc2V0OiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgIHZhciBzdGF0ZSA9IHRoaXMuX3N0YXRlID0gW11cblx0ICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCAyNTsgaSsrKSB7XG5cdCAgICAgICAgICAgICAgICBzdGF0ZVtpXSA9IG5ldyBYNjRXb3JkLmluaXQoKTtcblx0ICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgIHRoaXMuYmxvY2tTaXplID0gKDE2MDAgLSAyICogdGhpcy5jZmcub3V0cHV0TGVuZ3RoKSAvIDMyO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICBfZG9Qcm9jZXNzQmxvY2s6IGZ1bmN0aW9uIChNLCBvZmZzZXQpIHtcblx0ICAgICAgICAgICAgLy8gU2hvcnRjdXRzXG5cdCAgICAgICAgICAgIHZhciBzdGF0ZSA9IHRoaXMuX3N0YXRlO1xuXHQgICAgICAgICAgICB2YXIgbkJsb2NrU2l6ZUxhbmVzID0gdGhpcy5ibG9ja1NpemUgLyAyO1xuXG5cdCAgICAgICAgICAgIC8vIEFic29yYlxuXHQgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IG5CbG9ja1NpemVMYW5lczsgaSsrKSB7XG5cdCAgICAgICAgICAgICAgICAvLyBTaG9ydGN1dHNcblx0ICAgICAgICAgICAgICAgIHZhciBNMmkgID0gTVtvZmZzZXQgKyAyICogaV07XG5cdCAgICAgICAgICAgICAgICB2YXIgTTJpMSA9IE1bb2Zmc2V0ICsgMiAqIGkgKyAxXTtcblxuXHQgICAgICAgICAgICAgICAgLy8gU3dhcCBlbmRpYW5cblx0ICAgICAgICAgICAgICAgIE0yaSA9IChcblx0ICAgICAgICAgICAgICAgICAgICAoKChNMmkgPDwgOCkgIHwgKE0yaSA+Pj4gMjQpKSAmIDB4MDBmZjAwZmYpIHxcblx0ICAgICAgICAgICAgICAgICAgICAoKChNMmkgPDwgMjQpIHwgKE0yaSA+Pj4gOCkpICAmIDB4ZmYwMGZmMDApXG5cdCAgICAgICAgICAgICAgICApO1xuXHQgICAgICAgICAgICAgICAgTTJpMSA9IChcblx0ICAgICAgICAgICAgICAgICAgICAoKChNMmkxIDw8IDgpICB8IChNMmkxID4+PiAyNCkpICYgMHgwMGZmMDBmZikgfFxuXHQgICAgICAgICAgICAgICAgICAgICgoKE0yaTEgPDwgMjQpIHwgKE0yaTEgPj4+IDgpKSAgJiAweGZmMDBmZjAwKVxuXHQgICAgICAgICAgICAgICAgKTtcblxuXHQgICAgICAgICAgICAgICAgLy8gQWJzb3JiIG1lc3NhZ2UgaW50byBzdGF0ZVxuXHQgICAgICAgICAgICAgICAgdmFyIGxhbmUgPSBzdGF0ZVtpXTtcblx0ICAgICAgICAgICAgICAgIGxhbmUuaGlnaCBePSBNMmkxO1xuXHQgICAgICAgICAgICAgICAgbGFuZS5sb3cgIF49IE0yaTtcblx0ICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgIC8vIFJvdW5kc1xuXHQgICAgICAgICAgICBmb3IgKHZhciByb3VuZCA9IDA7IHJvdW5kIDwgMjQ7IHJvdW5kKyspIHtcblx0ICAgICAgICAgICAgICAgIC8vIFRoZXRhXG5cdCAgICAgICAgICAgICAgICBmb3IgKHZhciB4ID0gMDsgeCA8IDU7IHgrKykge1xuXHQgICAgICAgICAgICAgICAgICAgIC8vIE1peCBjb2x1bW4gbGFuZXNcblx0ICAgICAgICAgICAgICAgICAgICB2YXIgdE1zdyA9IDAsIHRMc3cgPSAwO1xuXHQgICAgICAgICAgICAgICAgICAgIGZvciAodmFyIHkgPSAwOyB5IDwgNTsgeSsrKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHZhciBsYW5lID0gc3RhdGVbeCArIDUgKiB5XTtcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdE1zdyBePSBsYW5lLmhpZ2g7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHRMc3cgXj0gbGFuZS5sb3c7XG5cdCAgICAgICAgICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgICAgICAgICAgLy8gVGVtcG9yYXJ5IHZhbHVlc1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciBUeCA9IFRbeF07XG5cdCAgICAgICAgICAgICAgICAgICAgVHguaGlnaCA9IHRNc3c7XG5cdCAgICAgICAgICAgICAgICAgICAgVHgubG93ICA9IHRMc3c7XG5cdCAgICAgICAgICAgICAgICB9XG5cdCAgICAgICAgICAgICAgICBmb3IgKHZhciB4ID0gMDsgeCA8IDU7IHgrKykge1xuXHQgICAgICAgICAgICAgICAgICAgIC8vIFNob3J0Y3V0c1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciBUeDQgPSBUWyh4ICsgNCkgJSA1XTtcblx0ICAgICAgICAgICAgICAgICAgICB2YXIgVHgxID0gVFsoeCArIDEpICUgNV07XG5cdCAgICAgICAgICAgICAgICAgICAgdmFyIFR4MU1zdyA9IFR4MS5oaWdoO1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciBUeDFMc3cgPSBUeDEubG93O1xuXG5cdCAgICAgICAgICAgICAgICAgICAgLy8gTWl4IHN1cnJvdW5kaW5nIGNvbHVtbnNcblx0ICAgICAgICAgICAgICAgICAgICB2YXIgdE1zdyA9IFR4NC5oaWdoIF4gKChUeDFNc3cgPDwgMSkgfCAoVHgxTHN3ID4+PiAzMSkpO1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciB0THN3ID0gVHg0LmxvdyAgXiAoKFR4MUxzdyA8PCAxKSB8IChUeDFNc3cgPj4+IDMxKSk7XG5cdCAgICAgICAgICAgICAgICAgICAgZm9yICh2YXIgeSA9IDA7IHkgPCA1OyB5KyspIHtcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGxhbmUgPSBzdGF0ZVt4ICsgNSAqIHldO1xuXHQgICAgICAgICAgICAgICAgICAgICAgICBsYW5lLmhpZ2ggXj0gdE1zdztcblx0ICAgICAgICAgICAgICAgICAgICAgICAgbGFuZS5sb3cgIF49IHRMc3c7XG5cdCAgICAgICAgICAgICAgICAgICAgfVxuXHQgICAgICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgICAgICAvLyBSaG8gUGlcblx0ICAgICAgICAgICAgICAgIGZvciAodmFyIGxhbmVJbmRleCA9IDE7IGxhbmVJbmRleCA8IDI1OyBsYW5lSW5kZXgrKykge1xuXHQgICAgICAgICAgICAgICAgICAgIC8vIFNob3J0Y3V0c1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciBsYW5lID0gc3RhdGVbbGFuZUluZGV4XTtcblx0ICAgICAgICAgICAgICAgICAgICB2YXIgbGFuZU1zdyA9IGxhbmUuaGlnaDtcblx0ICAgICAgICAgICAgICAgICAgICB2YXIgbGFuZUxzdyA9IGxhbmUubG93O1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciByaG9PZmZzZXQgPSBSSE9fT0ZGU0VUU1tsYW5lSW5kZXhdO1xuXG5cdCAgICAgICAgICAgICAgICAgICAgLy8gUm90YXRlIGxhbmVzXG5cdCAgICAgICAgICAgICAgICAgICAgaWYgKHJob09mZnNldCA8IDMyKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHZhciB0TXN3ID0gKGxhbmVNc3cgPDwgcmhvT2Zmc2V0KSB8IChsYW5lTHN3ID4+PiAoMzIgLSByaG9PZmZzZXQpKTtcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHRMc3cgPSAobGFuZUxzdyA8PCByaG9PZmZzZXQpIHwgKGxhbmVNc3cgPj4+ICgzMiAtIHJob09mZnNldCkpO1xuXHQgICAgICAgICAgICAgICAgICAgIH0gZWxzZSAvKiBpZiAocmhvT2Zmc2V0ID49IDMyKSAqLyB7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHZhciB0TXN3ID0gKGxhbmVMc3cgPDwgKHJob09mZnNldCAtIDMyKSkgfCAobGFuZU1zdyA+Pj4gKDY0IC0gcmhvT2Zmc2V0KSk7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHZhciB0THN3ID0gKGxhbmVNc3cgPDwgKHJob09mZnNldCAtIDMyKSkgfCAobGFuZUxzdyA+Pj4gKDY0IC0gcmhvT2Zmc2V0KSk7XG5cdCAgICAgICAgICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgICAgICAgICAgLy8gVHJhbnNwb3NlIGxhbmVzXG5cdCAgICAgICAgICAgICAgICAgICAgdmFyIFRQaUxhbmUgPSBUW1BJX0lOREVYRVNbbGFuZUluZGV4XV07XG5cdCAgICAgICAgICAgICAgICAgICAgVFBpTGFuZS5oaWdoID0gdE1zdztcblx0ICAgICAgICAgICAgICAgICAgICBUUGlMYW5lLmxvdyAgPSB0THN3O1xuXHQgICAgICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgICAgICAvLyBSaG8gcGkgYXQgeCA9IHkgPSAwXG5cdCAgICAgICAgICAgICAgICB2YXIgVDAgPSBUWzBdO1xuXHQgICAgICAgICAgICAgICAgdmFyIHN0YXRlMCA9IHN0YXRlWzBdO1xuXHQgICAgICAgICAgICAgICAgVDAuaGlnaCA9IHN0YXRlMC5oaWdoO1xuXHQgICAgICAgICAgICAgICAgVDAubG93ICA9IHN0YXRlMC5sb3c7XG5cblx0ICAgICAgICAgICAgICAgIC8vIENoaVxuXHQgICAgICAgICAgICAgICAgZm9yICh2YXIgeCA9IDA7IHggPCA1OyB4KyspIHtcblx0ICAgICAgICAgICAgICAgICAgICBmb3IgKHZhciB5ID0gMDsgeSA8IDU7IHkrKykge1xuXHQgICAgICAgICAgICAgICAgICAgICAgICAvLyBTaG9ydGN1dHNcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGxhbmVJbmRleCA9IHggKyA1ICogeTtcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGxhbmUgPSBzdGF0ZVtsYW5lSW5kZXhdO1xuXHQgICAgICAgICAgICAgICAgICAgICAgICB2YXIgVExhbmUgPSBUW2xhbmVJbmRleF07XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHZhciBUeDFMYW5lID0gVFsoKHggKyAxKSAlIDUpICsgNSAqIHldO1xuXHQgICAgICAgICAgICAgICAgICAgICAgICB2YXIgVHgyTGFuZSA9IFRbKCh4ICsgMikgJSA1KSArIDUgKiB5XTtcblxuXHQgICAgICAgICAgICAgICAgICAgICAgICAvLyBNaXggcm93c1xuXHQgICAgICAgICAgICAgICAgICAgICAgICBsYW5lLmhpZ2ggPSBUTGFuZS5oaWdoIF4gKH5UeDFMYW5lLmhpZ2ggJiBUeDJMYW5lLmhpZ2gpO1xuXHQgICAgICAgICAgICAgICAgICAgICAgICBsYW5lLmxvdyAgPSBUTGFuZS5sb3cgIF4gKH5UeDFMYW5lLmxvdyAgJiBUeDJMYW5lLmxvdyk7XG5cdCAgICAgICAgICAgICAgICAgICAgfVxuXHQgICAgICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgICAgICAvLyBJb3RhXG5cdCAgICAgICAgICAgICAgICB2YXIgbGFuZSA9IHN0YXRlWzBdO1xuXHQgICAgICAgICAgICAgICAgdmFyIHJvdW5kQ29uc3RhbnQgPSBST1VORF9DT05TVEFOVFNbcm91bmRdO1xuXHQgICAgICAgICAgICAgICAgbGFuZS5oaWdoIF49IHJvdW5kQ29uc3RhbnQuaGlnaDtcblx0ICAgICAgICAgICAgICAgIGxhbmUubG93ICBePSByb3VuZENvbnN0YW50Lmxvdzs7XG5cdCAgICAgICAgICAgIH1cblx0ICAgICAgICB9LFxuXG5cdCAgICAgICAgX2RvRmluYWxpemU6IGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgLy8gU2hvcnRjdXRzXG5cdCAgICAgICAgICAgIHZhciBkYXRhID0gdGhpcy5fZGF0YTtcblx0ICAgICAgICAgICAgdmFyIGRhdGFXb3JkcyA9IGRhdGEud29yZHM7XG5cdCAgICAgICAgICAgIHZhciBuQml0c1RvdGFsID0gdGhpcy5fbkRhdGFCeXRlcyAqIDg7XG5cdCAgICAgICAgICAgIHZhciBuQml0c0xlZnQgPSBkYXRhLnNpZ0J5dGVzICogODtcblx0ICAgICAgICAgICAgdmFyIGJsb2NrU2l6ZUJpdHMgPSB0aGlzLmJsb2NrU2l6ZSAqIDMyO1xuXG5cdCAgICAgICAgICAgIC8vIEFkZCBwYWRkaW5nXG5cdCAgICAgICAgICAgIGRhdGFXb3Jkc1tuQml0c0xlZnQgPj4+IDVdIHw9IDB4MSA8PCAoMjQgLSBuQml0c0xlZnQgJSAzMik7XG5cdCAgICAgICAgICAgIGRhdGFXb3Jkc1soKE1hdGguY2VpbCgobkJpdHNMZWZ0ICsgMSkgLyBibG9ja1NpemVCaXRzKSAqIGJsb2NrU2l6ZUJpdHMpID4+PiA1KSAtIDFdIHw9IDB4ODA7XG5cdCAgICAgICAgICAgIGRhdGEuc2lnQnl0ZXMgPSBkYXRhV29yZHMubGVuZ3RoICogNDtcblxuXHQgICAgICAgICAgICAvLyBIYXNoIGZpbmFsIGJsb2Nrc1xuXHQgICAgICAgICAgICB0aGlzLl9wcm9jZXNzKCk7XG5cblx0ICAgICAgICAgICAgLy8gU2hvcnRjdXRzXG5cdCAgICAgICAgICAgIHZhciBzdGF0ZSA9IHRoaXMuX3N0YXRlO1xuXHQgICAgICAgICAgICB2YXIgb3V0cHV0TGVuZ3RoQnl0ZXMgPSB0aGlzLmNmZy5vdXRwdXRMZW5ndGggLyA4O1xuXHQgICAgICAgICAgICB2YXIgb3V0cHV0TGVuZ3RoTGFuZXMgPSBvdXRwdXRMZW5ndGhCeXRlcyAvIDg7XG5cblx0ICAgICAgICAgICAgLy8gU3F1ZWV6ZVxuXHQgICAgICAgICAgICB2YXIgaGFzaFdvcmRzID0gW107XG5cdCAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgb3V0cHV0TGVuZ3RoTGFuZXM7IGkrKykge1xuXHQgICAgICAgICAgICAgICAgLy8gU2hvcnRjdXRzXG5cdCAgICAgICAgICAgICAgICB2YXIgbGFuZSA9IHN0YXRlW2ldO1xuXHQgICAgICAgICAgICAgICAgdmFyIGxhbmVNc3cgPSBsYW5lLmhpZ2g7XG5cdCAgICAgICAgICAgICAgICB2YXIgbGFuZUxzdyA9IGxhbmUubG93O1xuXG5cdCAgICAgICAgICAgICAgICAvLyBTd2FwIGVuZGlhblxuXHQgICAgICAgICAgICAgICAgbGFuZU1zdyA9IChcblx0ICAgICAgICAgICAgICAgICAgICAoKChsYW5lTXN3IDw8IDgpICB8IChsYW5lTXN3ID4+PiAyNCkpICYgMHgwMGZmMDBmZikgfFxuXHQgICAgICAgICAgICAgICAgICAgICgoKGxhbmVNc3cgPDwgMjQpIHwgKGxhbmVNc3cgPj4+IDgpKSAgJiAweGZmMDBmZjAwKVxuXHQgICAgICAgICAgICAgICAgKTtcblx0ICAgICAgICAgICAgICAgIGxhbmVMc3cgPSAoXG5cdCAgICAgICAgICAgICAgICAgICAgKCgobGFuZUxzdyA8PCA4KSAgfCAobGFuZUxzdyA+Pj4gMjQpKSAmIDB4MDBmZjAwZmYpIHxcblx0ICAgICAgICAgICAgICAgICAgICAoKChsYW5lTHN3IDw8IDI0KSB8IChsYW5lTHN3ID4+PiA4KSkgICYgMHhmZjAwZmYwMClcblx0ICAgICAgICAgICAgICAgICk7XG5cblx0ICAgICAgICAgICAgICAgIC8vIFNxdWVlemUgc3RhdGUgdG8gcmV0cmlldmUgaGFzaFxuXHQgICAgICAgICAgICAgICAgaGFzaFdvcmRzLnB1c2gobGFuZUxzdyk7XG5cdCAgICAgICAgICAgICAgICBoYXNoV29yZHMucHVzaChsYW5lTXN3KTtcblx0ICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgIC8vIFJldHVybiBmaW5hbCBjb21wdXRlZCBoYXNoXG5cdCAgICAgICAgICAgIHJldHVybiBuZXcgV29yZEFycmF5LmluaXQoaGFzaFdvcmRzLCBvdXRwdXRMZW5ndGhCeXRlcyk7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgIHZhciBjbG9uZSA9IEhhc2hlci5jbG9uZS5jYWxsKHRoaXMpO1xuXG5cdCAgICAgICAgICAgIHZhciBzdGF0ZSA9IGNsb25lLl9zdGF0ZSA9IHRoaXMuX3N0YXRlLnNsaWNlKDApO1xuXHQgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IDI1OyBpKyspIHtcblx0ICAgICAgICAgICAgICAgIHN0YXRlW2ldID0gc3RhdGVbaV0uY2xvbmUoKTtcblx0ICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgIHJldHVybiBjbG9uZTtcblx0ICAgICAgICB9XG5cdCAgICB9KTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBTaG9ydGN1dCBmdW5jdGlvbiB0byB0aGUgaGFzaGVyJ3Mgb2JqZWN0IGludGVyZmFjZS5cblx0ICAgICAqXG5cdCAgICAgKiBAcGFyYW0ge1dvcmRBcnJheXxzdHJpbmd9IG1lc3NhZ2UgVGhlIG1lc3NhZ2UgdG8gaGFzaC5cblx0ICAgICAqXG5cdCAgICAgKiBAcmV0dXJuIHtXb3JkQXJyYXl9IFRoZSBoYXNoLlxuXHQgICAgICpcblx0ICAgICAqIEBzdGF0aWNcblx0ICAgICAqXG5cdCAgICAgKiBAZXhhbXBsZVxuXHQgICAgICpcblx0ICAgICAqICAgICB2YXIgaGFzaCA9IENyeXB0b0pTLlNIQTMoJ21lc3NhZ2UnKTtcblx0ICAgICAqICAgICB2YXIgaGFzaCA9IENyeXB0b0pTLlNIQTMod29yZEFycmF5KTtcblx0ICAgICAqL1xuXHQgICAgQy5TSEEzID0gSGFzaGVyLl9jcmVhdGVIZWxwZXIoU0hBMyk7XG5cblx0ICAgIC8qKlxuXHQgICAgICogU2hvcnRjdXQgZnVuY3Rpb24gdG8gdGhlIEhNQUMncyBvYmplY3QgaW50ZXJmYWNlLlxuXHQgICAgICpcblx0ICAgICAqIEBwYXJhbSB7V29yZEFycmF5fHN0cmluZ30gbWVzc2FnZSBUaGUgbWVzc2FnZSB0byBoYXNoLlxuXHQgICAgICogQHBhcmFtIHtXb3JkQXJyYXl8c3RyaW5nfSBrZXkgVGhlIHNlY3JldCBrZXkuXG5cdCAgICAgKlxuXHQgICAgICogQHJldHVybiB7V29yZEFycmF5fSBUaGUgSE1BQy5cblx0ICAgICAqXG5cdCAgICAgKiBAc3RhdGljXG5cdCAgICAgKlxuXHQgICAgICogQGV4YW1wbGVcblx0ICAgICAqXG5cdCAgICAgKiAgICAgdmFyIGhtYWMgPSBDcnlwdG9KUy5IbWFjU0hBMyhtZXNzYWdlLCBrZXkpO1xuXHQgICAgICovXG5cdCAgICBDLkhtYWNTSEEzID0gSGFzaGVyLl9jcmVhdGVIbWFjSGVscGVyKFNIQTMpO1xuXHR9KE1hdGgpKTtcblxuXG5cdHJldHVybiBDcnlwdG9KUy5TSEEzO1xuXG59KSk7IiwiOyhmdW5jdGlvbiAocm9vdCwgZmFjdG9yeSkge1xuXHRpZiAodHlwZW9mIGV4cG9ydHMgPT09IFwib2JqZWN0XCIpIHtcblx0XHQvLyBDb21tb25KU1xuXHRcdG1vZHVsZS5leHBvcnRzID0gZXhwb3J0cyA9IGZhY3RvcnkocmVxdWlyZShcIi4vY29yZVwiKSk7XG5cdH1cblx0ZWxzZSBpZiAodHlwZW9mIGRlZmluZSA9PT0gXCJmdW5jdGlvblwiICYmIGRlZmluZS5hbWQpIHtcblx0XHQvLyBBTURcblx0XHRkZWZpbmUoW1wiLi9jb3JlXCJdLCBmYWN0b3J5KTtcblx0fVxuXHRlbHNlIHtcblx0XHQvLyBHbG9iYWwgKGJyb3dzZXIpXG5cdFx0ZmFjdG9yeShyb290LkNyeXB0b0pTKTtcblx0fVxufSh0aGlzLCBmdW5jdGlvbiAoQ3J5cHRvSlMpIHtcblxuXHQoZnVuY3Rpb24gKHVuZGVmaW5lZCkge1xuXHQgICAgLy8gU2hvcnRjdXRzXG5cdCAgICB2YXIgQyA9IENyeXB0b0pTO1xuXHQgICAgdmFyIENfbGliID0gQy5saWI7XG5cdCAgICB2YXIgQmFzZSA9IENfbGliLkJhc2U7XG5cdCAgICB2YXIgWDMyV29yZEFycmF5ID0gQ19saWIuV29yZEFycmF5O1xuXG5cdCAgICAvKipcblx0ICAgICAqIHg2NCBuYW1lc3BhY2UuXG5cdCAgICAgKi9cblx0ICAgIHZhciBDX3g2NCA9IEMueDY0ID0ge307XG5cblx0ICAgIC8qKlxuXHQgICAgICogQSA2NC1iaXQgd29yZC5cblx0ICAgICAqL1xuXHQgICAgdmFyIFg2NFdvcmQgPSBDX3g2NC5Xb3JkID0gQmFzZS5leHRlbmQoe1xuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIEluaXRpYWxpemVzIGEgbmV3bHkgY3JlYXRlZCA2NC1iaXQgd29yZC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7bnVtYmVyfSBoaWdoIFRoZSBoaWdoIDMyIGJpdHMuXG5cdCAgICAgICAgICogQHBhcmFtIHtudW1iZXJ9IGxvdyBUaGUgbG93IDMyIGJpdHMuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciB4NjRXb3JkID0gQ3J5cHRvSlMueDY0LldvcmQuY3JlYXRlKDB4MDAwMTAyMDMsIDB4MDQwNTA2MDcpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIGluaXQ6IGZ1bmN0aW9uIChoaWdoLCBsb3cpIHtcblx0ICAgICAgICAgICAgdGhpcy5oaWdoID0gaGlnaDtcblx0ICAgICAgICAgICAgdGhpcy5sb3cgPSBsb3c7XG5cdCAgICAgICAgfVxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQml0d2lzZSBOT1RzIHRoaXMgd29yZC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1g2NFdvcmR9IEEgbmV3IHg2NC1Xb3JkIG9iamVjdCBhZnRlciBuZWdhdGluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIG5lZ2F0ZWQgPSB4NjRXb3JkLm5vdCgpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIC8vIG5vdDogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICAvLyB2YXIgaGlnaCA9IH50aGlzLmhpZ2g7XG5cdCAgICAgICAgICAgIC8vIHZhciBsb3cgPSB+dGhpcy5sb3c7XG5cblx0ICAgICAgICAgICAgLy8gcmV0dXJuIFg2NFdvcmQuY3JlYXRlKGhpZ2gsIGxvdyk7XG5cdCAgICAgICAgLy8gfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIEJpdHdpc2UgQU5EcyB0aGlzIHdvcmQgd2l0aCB0aGUgcGFzc2VkIHdvcmQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge1g2NFdvcmR9IHdvcmQgVGhlIHg2NC1Xb3JkIHRvIEFORCB3aXRoIHRoaXMgd29yZC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1g2NFdvcmR9IEEgbmV3IHg2NC1Xb3JkIG9iamVjdCBhZnRlciBBTkRpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciBhbmRlZCA9IHg2NFdvcmQuYW5kKGFub3RoZXJYNjRXb3JkKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICAvLyBhbmQ6IGZ1bmN0aW9uICh3b3JkKSB7XG5cdCAgICAgICAgICAgIC8vIHZhciBoaWdoID0gdGhpcy5oaWdoICYgd29yZC5oaWdoO1xuXHQgICAgICAgICAgICAvLyB2YXIgbG93ID0gdGhpcy5sb3cgJiB3b3JkLmxvdztcblxuXHQgICAgICAgICAgICAvLyByZXR1cm4gWDY0V29yZC5jcmVhdGUoaGlnaCwgbG93KTtcblx0ICAgICAgICAvLyB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQml0d2lzZSBPUnMgdGhpcyB3b3JkIHdpdGggdGhlIHBhc3NlZCB3b3JkLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtYNjRXb3JkfSB3b3JkIFRoZSB4NjQtV29yZCB0byBPUiB3aXRoIHRoaXMgd29yZC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1g2NFdvcmR9IEEgbmV3IHg2NC1Xb3JkIG9iamVjdCBhZnRlciBPUmluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIG9yZWQgPSB4NjRXb3JkLm9yKGFub3RoZXJYNjRXb3JkKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICAvLyBvcjogZnVuY3Rpb24gKHdvcmQpIHtcblx0ICAgICAgICAgICAgLy8gdmFyIGhpZ2ggPSB0aGlzLmhpZ2ggfCB3b3JkLmhpZ2g7XG5cdCAgICAgICAgICAgIC8vIHZhciBsb3cgPSB0aGlzLmxvdyB8IHdvcmQubG93O1xuXG5cdCAgICAgICAgICAgIC8vIHJldHVybiBYNjRXb3JkLmNyZWF0ZShoaWdoLCBsb3cpO1xuXHQgICAgICAgIC8vIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBCaXR3aXNlIFhPUnMgdGhpcyB3b3JkIHdpdGggdGhlIHBhc3NlZCB3b3JkLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtYNjRXb3JkfSB3b3JkIFRoZSB4NjQtV29yZCB0byBYT1Igd2l0aCB0aGlzIHdvcmQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtYNjRXb3JkfSBBIG5ldyB4NjQtV29yZCBvYmplY3QgYWZ0ZXIgWE9SaW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgeG9yZWQgPSB4NjRXb3JkLnhvcihhbm90aGVyWDY0V29yZCk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgLy8geG9yOiBmdW5jdGlvbiAod29yZCkge1xuXHQgICAgICAgICAgICAvLyB2YXIgaGlnaCA9IHRoaXMuaGlnaCBeIHdvcmQuaGlnaDtcblx0ICAgICAgICAgICAgLy8gdmFyIGxvdyA9IHRoaXMubG93IF4gd29yZC5sb3c7XG5cblx0ICAgICAgICAgICAgLy8gcmV0dXJuIFg2NFdvcmQuY3JlYXRlKGhpZ2gsIGxvdyk7XG5cdCAgICAgICAgLy8gfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIFNoaWZ0cyB0aGlzIHdvcmQgbiBiaXRzIHRvIHRoZSBsZWZ0LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtudW1iZXJ9IG4gVGhlIG51bWJlciBvZiBiaXRzIHRvIHNoaWZ0LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7WDY0V29yZH0gQSBuZXcgeDY0LVdvcmQgb2JqZWN0IGFmdGVyIHNoaWZ0aW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgc2hpZnRlZCA9IHg2NFdvcmQuc2hpZnRMKDI1KTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICAvLyBzaGlmdEw6IGZ1bmN0aW9uIChuKSB7XG5cdCAgICAgICAgICAgIC8vIGlmIChuIDwgMzIpIHtcblx0ICAgICAgICAgICAgICAgIC8vIHZhciBoaWdoID0gKHRoaXMuaGlnaCA8PCBuKSB8ICh0aGlzLmxvdyA+Pj4gKDMyIC0gbikpO1xuXHQgICAgICAgICAgICAgICAgLy8gdmFyIGxvdyA9IHRoaXMubG93IDw8IG47XG5cdCAgICAgICAgICAgIC8vIH0gZWxzZSB7XG5cdCAgICAgICAgICAgICAgICAvLyB2YXIgaGlnaCA9IHRoaXMubG93IDw8IChuIC0gMzIpO1xuXHQgICAgICAgICAgICAgICAgLy8gdmFyIGxvdyA9IDA7XG5cdCAgICAgICAgICAgIC8vIH1cblxuXHQgICAgICAgICAgICAvLyByZXR1cm4gWDY0V29yZC5jcmVhdGUoaGlnaCwgbG93KTtcblx0ICAgICAgICAvLyB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogU2hpZnRzIHRoaXMgd29yZCBuIGJpdHMgdG8gdGhlIHJpZ2h0LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtudW1iZXJ9IG4gVGhlIG51bWJlciBvZiBiaXRzIHRvIHNoaWZ0LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7WDY0V29yZH0gQSBuZXcgeDY0LVdvcmQgb2JqZWN0IGFmdGVyIHNoaWZ0aW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgc2hpZnRlZCA9IHg2NFdvcmQuc2hpZnRSKDcpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIC8vIHNoaWZ0UjogZnVuY3Rpb24gKG4pIHtcblx0ICAgICAgICAgICAgLy8gaWYgKG4gPCAzMikge1xuXHQgICAgICAgICAgICAgICAgLy8gdmFyIGxvdyA9ICh0aGlzLmxvdyA+Pj4gbikgfCAodGhpcy5oaWdoIDw8ICgzMiAtIG4pKTtcblx0ICAgICAgICAgICAgICAgIC8vIHZhciBoaWdoID0gdGhpcy5oaWdoID4+PiBuO1xuXHQgICAgICAgICAgICAvLyB9IGVsc2Uge1xuXHQgICAgICAgICAgICAgICAgLy8gdmFyIGxvdyA9IHRoaXMuaGlnaCA+Pj4gKG4gLSAzMik7XG5cdCAgICAgICAgICAgICAgICAvLyB2YXIgaGlnaCA9IDA7XG5cdCAgICAgICAgICAgIC8vIH1cblxuXHQgICAgICAgICAgICAvLyByZXR1cm4gWDY0V29yZC5jcmVhdGUoaGlnaCwgbG93KTtcblx0ICAgICAgICAvLyB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogUm90YXRlcyB0aGlzIHdvcmQgbiBiaXRzIHRvIHRoZSBsZWZ0LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtudW1iZXJ9IG4gVGhlIG51bWJlciBvZiBiaXRzIHRvIHJvdGF0ZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1g2NFdvcmR9IEEgbmV3IHg2NC1Xb3JkIG9iamVjdCBhZnRlciByb3RhdGluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHJvdGF0ZWQgPSB4NjRXb3JkLnJvdEwoMjUpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIC8vIHJvdEw6IGZ1bmN0aW9uIChuKSB7XG5cdCAgICAgICAgICAgIC8vIHJldHVybiB0aGlzLnNoaWZ0TChuKS5vcih0aGlzLnNoaWZ0Uig2NCAtIG4pKTtcblx0ICAgICAgICAvLyB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogUm90YXRlcyB0aGlzIHdvcmQgbiBiaXRzIHRvIHRoZSByaWdodC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7bnVtYmVyfSBuIFRoZSBudW1iZXIgb2YgYml0cyB0byByb3RhdGUuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtYNjRXb3JkfSBBIG5ldyB4NjQtV29yZCBvYmplY3QgYWZ0ZXIgcm90YXRpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciByb3RhdGVkID0geDY0V29yZC5yb3RSKDcpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIC8vIHJvdFI6IGZ1bmN0aW9uIChuKSB7XG5cdCAgICAgICAgICAgIC8vIHJldHVybiB0aGlzLnNoaWZ0UihuKS5vcih0aGlzLnNoaWZ0TCg2NCAtIG4pKTtcblx0ICAgICAgICAvLyB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQWRkcyB0aGlzIHdvcmQgd2l0aCB0aGUgcGFzc2VkIHdvcmQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge1g2NFdvcmR9IHdvcmQgVGhlIHg2NC1Xb3JkIHRvIGFkZCB3aXRoIHRoaXMgd29yZC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1g2NFdvcmR9IEEgbmV3IHg2NC1Xb3JkIG9iamVjdCBhZnRlciBhZGRpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciBhZGRlZCA9IHg2NFdvcmQuYWRkKGFub3RoZXJYNjRXb3JkKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICAvLyBhZGQ6IGZ1bmN0aW9uICh3b3JkKSB7XG5cdCAgICAgICAgICAgIC8vIHZhciBsb3cgPSAodGhpcy5sb3cgKyB3b3JkLmxvdykgfCAwO1xuXHQgICAgICAgICAgICAvLyB2YXIgY2FycnkgPSAobG93ID4+PiAwKSA8ICh0aGlzLmxvdyA+Pj4gMCkgPyAxIDogMDtcblx0ICAgICAgICAgICAgLy8gdmFyIGhpZ2ggPSAodGhpcy5oaWdoICsgd29yZC5oaWdoICsgY2FycnkpIHwgMDtcblxuXHQgICAgICAgICAgICAvLyByZXR1cm4gWDY0V29yZC5jcmVhdGUoaGlnaCwgbG93KTtcblx0ICAgICAgICAvLyB9XG5cdCAgICB9KTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBBbiBhcnJheSBvZiA2NC1iaXQgd29yZHMuXG5cdCAgICAgKlxuXHQgICAgICogQHByb3BlcnR5IHtBcnJheX0gd29yZHMgVGhlIGFycmF5IG9mIENyeXB0b0pTLng2NC5Xb3JkIG9iamVjdHMuXG5cdCAgICAgKiBAcHJvcGVydHkge251bWJlcn0gc2lnQnl0ZXMgVGhlIG51bWJlciBvZiBzaWduaWZpY2FudCBieXRlcyBpbiB0aGlzIHdvcmQgYXJyYXkuXG5cdCAgICAgKi9cblx0ICAgIHZhciBYNjRXb3JkQXJyYXkgPSBDX3g2NC5Xb3JkQXJyYXkgPSBCYXNlLmV4dGVuZCh7XG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogSW5pdGlhbGl6ZXMgYSBuZXdseSBjcmVhdGVkIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge0FycmF5fSB3b3JkcyAoT3B0aW9uYWwpIEFuIGFycmF5IG9mIENyeXB0b0pTLng2NC5Xb3JkIG9iamVjdHMuXG5cdCAgICAgICAgICogQHBhcmFtIHtudW1iZXJ9IHNpZ0J5dGVzIChPcHRpb25hbCkgVGhlIG51bWJlciBvZiBzaWduaWZpY2FudCBieXRlcyBpbiB0aGUgd29yZHMuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciB3b3JkQXJyYXkgPSBDcnlwdG9KUy54NjQuV29yZEFycmF5LmNyZWF0ZSgpO1xuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciB3b3JkQXJyYXkgPSBDcnlwdG9KUy54NjQuV29yZEFycmF5LmNyZWF0ZShbXG5cdCAgICAgICAgICogICAgICAgICBDcnlwdG9KUy54NjQuV29yZC5jcmVhdGUoMHgwMDAxMDIwMywgMHgwNDA1MDYwNyksXG5cdCAgICAgICAgICogICAgICAgICBDcnlwdG9KUy54NjQuV29yZC5jcmVhdGUoMHgxODE5MWExYiwgMHgxYzFkMWUxZilcblx0ICAgICAgICAgKiAgICAgXSk7XG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHdvcmRBcnJheSA9IENyeXB0b0pTLng2NC5Xb3JkQXJyYXkuY3JlYXRlKFtcblx0ICAgICAgICAgKiAgICAgICAgIENyeXB0b0pTLng2NC5Xb3JkLmNyZWF0ZSgweDAwMDEwMjAzLCAweDA0MDUwNjA3KSxcblx0ICAgICAgICAgKiAgICAgICAgIENyeXB0b0pTLng2NC5Xb3JkLmNyZWF0ZSgweDE4MTkxYTFiLCAweDFjMWQxZTFmKVxuXHQgICAgICAgICAqICAgICBdLCAxMCk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgaW5pdDogZnVuY3Rpb24gKHdvcmRzLCBzaWdCeXRlcykge1xuXHQgICAgICAgICAgICB3b3JkcyA9IHRoaXMud29yZHMgPSB3b3JkcyB8fCBbXTtcblxuXHQgICAgICAgICAgICBpZiAoc2lnQnl0ZXMgIT0gdW5kZWZpbmVkKSB7XG5cdCAgICAgICAgICAgICAgICB0aGlzLnNpZ0J5dGVzID0gc2lnQnl0ZXM7XG5cdCAgICAgICAgICAgIH0gZWxzZSB7XG5cdCAgICAgICAgICAgICAgICB0aGlzLnNpZ0J5dGVzID0gd29yZHMubGVuZ3RoICogODtcblx0ICAgICAgICAgICAgfVxuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDb252ZXJ0cyB0aGlzIDY0LWJpdCB3b3JkIGFycmF5IHRvIGEgMzItYml0IHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtDcnlwdG9KUy5saWIuV29yZEFycmF5fSBUaGlzIHdvcmQgYXJyYXkncyBkYXRhIGFzIGEgMzItYml0IHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciB4MzJXb3JkQXJyYXkgPSB4NjRXb3JkQXJyYXkudG9YMzIoKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICB0b1gzMjogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICAvLyBTaG9ydGN1dHNcblx0ICAgICAgICAgICAgdmFyIHg2NFdvcmRzID0gdGhpcy53b3Jkcztcblx0ICAgICAgICAgICAgdmFyIHg2NFdvcmRzTGVuZ3RoID0geDY0V29yZHMubGVuZ3RoO1xuXG5cdCAgICAgICAgICAgIC8vIENvbnZlcnRcblx0ICAgICAgICAgICAgdmFyIHgzMldvcmRzID0gW107XG5cdCAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgeDY0V29yZHNMZW5ndGg7IGkrKykge1xuXHQgICAgICAgICAgICAgICAgdmFyIHg2NFdvcmQgPSB4NjRXb3Jkc1tpXTtcblx0ICAgICAgICAgICAgICAgIHgzMldvcmRzLnB1c2goeDY0V29yZC5oaWdoKTtcblx0ICAgICAgICAgICAgICAgIHgzMldvcmRzLnB1c2goeDY0V29yZC5sb3cpO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgcmV0dXJuIFgzMldvcmRBcnJheS5jcmVhdGUoeDMyV29yZHMsIHRoaXMuc2lnQnl0ZXMpO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDcmVhdGVzIGEgY29weSBvZiB0aGlzIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtYNjRXb3JkQXJyYXl9IFRoZSBjbG9uZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIGNsb25lID0geDY0V29yZEFycmF5LmNsb25lKCk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgY2xvbmU6IGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgdmFyIGNsb25lID0gQmFzZS5jbG9uZS5jYWxsKHRoaXMpO1xuXG5cdCAgICAgICAgICAgIC8vIENsb25lIFwid29yZHNcIiBhcnJheVxuXHQgICAgICAgICAgICB2YXIgd29yZHMgPSBjbG9uZS53b3JkcyA9IHRoaXMud29yZHMuc2xpY2UoMCk7XG5cblx0ICAgICAgICAgICAgLy8gQ2xvbmUgZWFjaCBYNjRXb3JkIG9iamVjdFxuXHQgICAgICAgICAgICB2YXIgd29yZHNMZW5ndGggPSB3b3Jkcy5sZW5ndGg7XG5cdCAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgd29yZHNMZW5ndGg7IGkrKykge1xuXHQgICAgICAgICAgICAgICAgd29yZHNbaV0gPSB3b3Jkc1tpXS5jbG9uZSgpO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgcmV0dXJuIGNsb25lO1xuXHQgICAgICAgIH1cblx0ICAgIH0pO1xuXHR9KCkpO1xuXG5cblx0cmV0dXJuIENyeXB0b0pTO1xuXG59KSk7IiwiJ3VzZSBzdHJpY3QnO1xuXG5tb2R1bGUuZXhwb3J0cyA9IEJpZ051bWJlcjsgLy8ganNoaW50IGlnbm9yZTpsaW5lXG5cbiIsInZhciB3ZWIzID0gcmVxdWlyZSgnLi9saWIvd2ViMycpO1xud2ViMy5wcm92aWRlcnMuSHR0cFByb3ZpZGVyID0gcmVxdWlyZSgnLi9saWIvd2ViMy9odHRwcHJvdmlkZXInKTtcbndlYjMucHJvdmlkZXJzLlF0U3luY1Byb3ZpZGVyID0gcmVxdWlyZSgnLi9saWIvd2ViMy9xdHN5bmMnKTtcbndlYjMuZXRoLmNvbnRyYWN0ID0gcmVxdWlyZSgnLi9saWIvd2ViMy9jb250cmFjdCcpO1xud2ViMy5ldGgubmFtZXJlZyA9IHJlcXVpcmUoJy4vbGliL3dlYjMvbmFtZXJlZycpO1xud2ViMy5ldGguc2VuZElCQU5UcmFuc2FjdGlvbiA9IHJlcXVpcmUoJy4vbGliL3dlYjMvdHJhbnNmZXInKTtcblxuLy8gZG9udCBvdmVycmlkZSBnbG9iYWwgdmFyaWFibGVcbmlmICh0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyAmJiB0eXBlb2Ygd2luZG93LndlYjMgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgd2luZG93LndlYjMgPSB3ZWIzO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHdlYjM7XG5cbiJdfQ== diff --git a/libjsqrc/ethereumjs/dist/web3-light.min.js b/libjsqrc/ethereumjs/dist/web3-light.min.js deleted file mode 100644 index ea9ef2bba..000000000 --- a/libjsqrc/ethereumjs/dist/web3-light.min.js +++ /dev/null @@ -1,2 +0,0 @@ -require=function t(e,n,r){function o(a,s){if(!n[a]){if(!e[a]){var u="function"==typeof require&&require;if(!s&&u)return u(a,!0);if(i)return i(a,!0);var c=new Error("Cannot find module '"+a+"'");throw c.code="MODULE_NOT_FOUND",c}var p=n[a]={exports:{}};e[a][0].call(p.exports,function(t){var n=e[a][1][t];return o(n?n:t)},p,p.exports,t,e,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;ai;i+=64)n.push(this._outputFormatter(new a(t.dynamicPart().substr(i+64,64))));return n}return this._outputFormatter(t)},u.prototype.sliceParam=function(t,e,n){return"bytes"===this._mode?a.decodeBytes(t,e):s(n)?a.decodeArray(t,e):a.decodeParam(t,e)};var c=function(t){this._types=t};c.prototype._requireType=function(t){var e=this._types.filter(function(e){return e.isType(t)})[0];if(!e)throw Error("invalid solidity type!: "+t);return e},c.prototype._formatInput=function(t,e){return this._requireType(t).formatInput(e,s(t))},c.prototype.encodeParam=function(t,e){return this._formatInput(t,e).encode()},c.prototype.encodeParams=function(t,e){var n=this,r=t.map(function(t,r){return n._formatInput(t,e[r])});return a.encodeList(r)},c.prototype.decodeParam=function(t,e){return this.decodeParams([t],e)[0]},c.prototype.decodeParams=function(t,e){var n=this;return t.map(function(t,r){var o=n._requireType(t),i=o.sliceParam(e,r,t);return o.formatOutput(i,s(t))})};var p=new c([new u({name:"address",match:"strict",mode:"value",inputFormatter:i.formatInputInt,outputFormatter:i.formatOutputAddress}),new u({name:"bool",match:"strict",mode:"value",inputFormatter:i.formatInputBool,outputFormatter:i.formatOutputBool}),new u({name:"int",match:"prefix",mode:"value",inputFormatter:i.formatInputInt,outputFormatter:i.formatOutputInt}),new u({name:"uint",match:"prefix",mode:"value",inputFormatter:i.formatInputInt,outputFormatter:i.formatOutputUInt}),new u({name:"bytes",match:"strict",mode:"bytes",inputFormatter:i.formatInputDynamicBytes,outputFormatter:i.formatOutputDynamicBytes}),new u({name:"bytes",match:"prefix",mode:"value",inputFormatter:i.formatInputBytes,outputFormatter:i.formatOutputBytes}),new u({name:"real",match:"prefix",mode:"value",inputFormatter:i.formatInputReal,outputFormatter:i.formatOutputReal}),new u({name:"ureal",match:"prefix",mode:"value",inputFormatter:i.formatInputReal,outputFormatter:i.formatOutputUReal})]);e.exports=p},{"../utils/utils":7,"./formatters":2,"./param":3,"bignumber.js":"bignumber.js"}],2:[function(t,e,n){var r=t("bignumber.js"),o=t("../utils/utils"),i=t("../utils/config"),a=t("./param"),s=function(t){var e=2*i.ETH_PADDING;r.config(i.ETH_BIGNUMBER_ROUNDING_MODE);var n=o.padLeft(o.toTwosComplement(t).round().toString(16),e);return new a(n)},u=function(t){var e=o.fromAscii(t,i.ETH_PADDING).substr(2);return new a(e)},c=function(t){var e=o.fromAscii(t,i.ETH_PADDING).substr(2);return new a(s(t.length).value+e,32)},p=function(t){var e="000000000000000000000000000000000000000000000000000000000000000"+(t?"1":"0");return new a(e)},l=function(t){return s(new r(t).times(new r(2).pow(128)))},f=function(t){return"1"===new r(t.substr(0,1),16).toString(2).substr(0,1)},m=function(t){var e=t.staticPart()||"0";return f(e)?new r(e,16).minus(new r("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16)).minus(1):new r(e,16)},h=function(t){var e=t.staticPart()||"0";return new r(e,16)},d=function(t){return m(t).dividedBy(new r(2).pow(128))},y=function(t){return h(t).dividedBy(new r(2).pow(128))},g=function(t){return"0000000000000000000000000000000000000000000000000000000000000001"===t.staticPart()?!0:!1},v=function(t){return o.toAscii(t.staticPart())},b=function(t){return o.toAscii(t.dynamicPart().slice(64))},w=function(t){var e=t.staticPart();return"0x"+e.slice(e.length-40,e.length)};e.exports={formatInputInt:s,formatInputBytes:u,formatInputDynamicBytes:c,formatInputBool:p,formatInputReal:l,formatOutputInt:m,formatOutputUInt:h,formatOutputReal:d,formatOutputUReal:y,formatOutputBool:g,formatOutputBytes:v,formatOutputDynamicBytes:b,formatOutputAddress:w}},{"../utils/config":5,"../utils/utils":7,"./param":3,"bignumber.js":"bignumber.js"}],3:[function(t,e,n){var r=t("../utils/utils"),o=function(t,e){this.value=t||"",this.offset=e};o.prototype.dynamicPartLength=function(){return this.dynamicPart().length/2},o.prototype.withOffset=function(t){return new o(this.value,t)},o.prototype.combine=function(t){return new o(this.value+t.value)},o.prototype.isDynamic=function(){return this.value.length>64||void 0!==this.offset},o.prototype.offsetAsBytes=function(){return this.isDynamic()?r.padLeft(r.toTwosComplement(this.offset).toString(16),64):""},o.prototype.staticPart=function(){return this.isDynamic()?this.offsetAsBytes():this.value},o.prototype.dynamicPart=function(){return this.isDynamic()?this.value:""},o.prototype.encode=function(){return this.staticPart()+this.dynamicPart()},o.encodeList=function(t){var e=32*t.length,n=t.map(function(t){if(!t.isDynamic())return t;var n=e;return e+=t.dynamicPartLength(),t.withOffset(n)});return n.reduce(function(t,e){return t+e.dynamicPart()},n.reduce(function(t,e){return t+e.staticPart()},""))},o.decodeParam=function(t,e){return e=e||0,new o(t.substr(64*e,64))};var i=function(t,e){return parseInt("0x"+t.substr(64*e,64))};o.decodeBytes=function(t,e){e=e||0;var n=i(t,e);return new o(t.substr(2*n,128),0)},o.decodeArray=function(t,e){e=e||0;var n=i(t,e),r=parseInt("0x"+t.substr(2*n,64));return new o(t.substr(2*n,64*(r+1)),0)},e.exports=o},{"../utils/utils":7}],4:[function(t,e,n){"use strict";n.XMLHttpRequest="undefined"==typeof XMLHttpRequest?{}:XMLHttpRequest},{}],5:[function(t,e,n){var r=t("bignumber.js"),o=["wei","kwei","Mwei","Gwei","szabo","finney","femtoether","picoether","nanoether","microether","milliether","nano","micro","milli","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_UNITS:o,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:r.ROUND_DOWN},ETH_POLLING_TIMEOUT:1e3,defaultBlock:"latest",defaultAccount:void 0}},{"bignumber.js":"bignumber.js"}],6:[function(t,e,n){var r=t("./utils"),o=t("crypto-js/sha3");e.exports=function(t,e){return"0x"!==t.substr(0,2)||e||(console.warn("requirement of using web3.fromAscii before sha3 is deprecated"),console.warn("new usage: 'web3.sha3(\"hello\")'"),console.warn("see https://github.com/ethereum/web3.js/pull/205"),console.warn("if you need to hash hex value, you can do 'sha3(\"0xfff\", true)'"),t=r.toAscii(t)),o(t,{outputLength:256}).toString()}},{"./utils":7,"crypto-js/sha3":33}],7:[function(t,e,n){var r=t("bignumber.js"),o={wei:"1",kwei:"1000",ada:"1000",femtoether:"1000",mwei:"1000000",babbage:"1000000",picoether:"1000000",gwei:"1000000000",shannon:"1000000000",nanoether:"1000000000",nano:"1000000000",szabo:"1000000000000",microether:"1000000000000",micro:"1000000000000",finney:"1000000000000000",milliether:"1000000000000000",milli:"1000000000000000",ether:"1000000000000000000",kether:"1000000000000000000000",grand:"1000000000000000000000",einstein:"1000000000000000000000",mether:"1000000000000000000000000",gether:"1000000000000000000000000000",tether:"1000000000000000000000000000000"},i=function(t,e,n){return new Array(e-t.length+1).join(n?n:"0")+t},a=function(t){var e="",n=0,r=t.length;for("0x"===t.substring(0,2)&&(n=2);r>n;n+=2){var o=parseInt(t.substr(n,2),16);if(0===o)break;e+=String.fromCharCode(o)}return e},s=function(t){for(var e="",n=0;nthis._inputTypes.length&&i.isObject(t[t.length-1])&&(e=t[t.length-1]),e.to=this._address,e.data="0x"+this.signature()+o.encodeParams(this._inputTypes,t),e},s.prototype.signature=function(){return a(this._name).slice(0,8)},s.prototype.unpackOutput=function(t){if(t){t=t.length>=2?t.slice(2):t;var e=o.decodeParams(this._outputTypes,t);return 1===e.length?e[0]:e}},s.prototype.call=function(){var t=Array.prototype.slice.call(arguments).filter(function(t){return void 0!==t}),e=this.extractCallback(t),n=this.toPayload(t);if(!e){var o=r.eth.call(n);return this.unpackOutput(o)}var i=this;r.eth.call(n,function(t,n){e(t,i.unpackOutput(n))})},s.prototype.sendTransaction=function(){var t=Array.prototype.slice.call(arguments).filter(function(t){return void 0!==t}),e=this.extractCallback(t),n=this.toPayload(t);return e?void r.eth.sendTransaction(n,e):r.eth.sendTransaction(n)},s.prototype.estimateGas=function(){var t=Array.prototype.slice.call(arguments),e=this.extractCallback(t),n=this.toPayload(t);return e?void r.eth.estimateGas(n,e):r.eth.estimateGas(n)},s.prototype.displayName=function(){return i.extractDisplayName(this._name)},s.prototype.typeName=function(){return i.extractTypeName(this._name)},s.prototype.request=function(){var t=Array.prototype.slice.call(arguments),e=this.extractCallback(t),n=this.toPayload(t),r=this.unpackOutput.bind(this);return{callback:e,payload:n,format:r}},s.prototype.execute=function(){var t=!this._constant;return t?this.sendTransaction.apply(this,Array.prototype.slice.call(arguments)):this.call.apply(this,Array.prototype.slice.call(arguments))},s.prototype.attachToContract=function(t){var e=this.execute.bind(this);e.request=this.request.bind(this),e.call=this.call.bind(this),e.sendTransaction=this.sendTransaction.bind(this),e.estimateGas=this.estimateGas.bind(this);var n=this.displayName();t[n]||(t[n]=e),t[n][this.typeName()]=e},e.exports=s},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"../web3":9}],19:[function(t,e,n){"use strict";var r=t("xmlhttprequest").XMLHttpRequest,o=t("./errors"),i=function(t){this.host=t||"http://localhost:8545"};i.prototype.send=function(t){var e=new r;e.open("POST",this.host,!1);try{e.send(JSON.stringify(t))}catch(n){throw o.InvalidConnection(this.host)}var i=e.responseText;try{i=JSON.parse(i)}catch(a){throw o.InvalidResponse(i)}return i},i.prototype.sendAsync=function(t,e){var n=new r;n.onreadystatechange=function(){if(4===n.readyState){var t=n.responseText,r=null;try{t=JSON.parse(t)}catch(i){r=o.InvalidResponse(t)}e(r,t)}},n.open("POST",this.host,!0);try{n.send(JSON.stringify(t))}catch(i){e(o.InvalidConnection(this.host))}},e.exports=i},{"./errors":13,xmlhttprequest:4}],20:[function(t,e,n){var r=t("../utils/utils"),o=function(t){this._iban=t};o.prototype.isValid=function(){return r.isIBAN(this._iban)},o.prototype.isDirect=function(){return 34===this._iban.length},o.prototype.isIndirect=function(){return 20===this._iban.length},o.prototype.checksum=function(){return this._iban.substr(2,2)},o.prototype.institution=function(){return this.isIndirect()?this._iban.substr(7,4):""},o.prototype.client=function(){return this.isIndirect()?this._iban.substr(11):""},o.prototype.address=function(){return this.isDirect()?this._iban.substr(4):""},e.exports=o},{"../utils/utils":7}],21:[function(t,e,n){var r=function(){return arguments.callee._singletonInstance?arguments.callee._singletonInstance:(arguments.callee._singletonInstance=this,void(this.messageId=1))};r.getInstance=function(){var t=new r;return t},r.prototype.toPayload=function(t,e){return t||console.error("jsonrpc method should be specified!"),{jsonrpc:"2.0",method:t,params:e||[],id:this.messageId++}},r.prototype.isValidResponse=function(t){return!!t&&!t.error&&"2.0"===t.jsonrpc&&"number"==typeof t.id&&void 0!==t.result},r.prototype.toBatchPayload=function(t){var e=this;return t.map(function(t){return e.toPayload(t.method,t.params)})},e.exports=r},{}],22:[function(t,e,n){var r=t("./requestmanager"),o=t("../utils/utils"),i=t("./errors"),a=function(t){this.name=t.name,this.call=t.call,this.params=t.params||0,this.inputFormatter=t.inputFormatter,this.outputFormatter=t.outputFormatter};a.prototype.getCall=function(t){return o.isFunction(this.call)?this.call(t):this.call},a.prototype.extractCallback=function(t){return o.isFunction(t[t.length-1])?t.pop():void 0},a.prototype.validateArgs=function(t){if(t.length!==this.params)throw i.InvalidNumberOfParams()},a.prototype.formatInput=function(t){return this.inputFormatter?this.inputFormatter.map(function(e,n){return e?e(t[n]):t[n]}):t},a.prototype.formatOutput=function(t){return this.outputFormatter&&null!==t?this.outputFormatter(t):t},a.prototype.attachToObject=function(t){var e=this.send.bind(this);e.request=this.request.bind(this),e.call=this.call;var n=this.name.split(".");n.length>1?(t[n[0]]=t[n[0]]||{},t[n[0]][n[1]]=e):t[n[0]]=e},a.prototype.toPayload=function(t){var e=this.getCall(t),n=this.extractCallback(t),r=this.formatInput(t);return this.validateArgs(r),{method:e,params:r,callback:n}},a.prototype.request=function(){var t=this.toPayload(Array.prototype.slice.call(arguments));return t.format=this.formatOutput.bind(this),t},a.prototype.send=function(){var t=this.toPayload(Array.prototype.slice.call(arguments));if(t.callback){var e=this;return r.getInstance().sendAsync(t,function(n,r){t.callback(n,e.formatOutput(r))})}return this.formatOutput(r.getInstance().send(t))},e.exports=a},{"../utils/utils":7,"./errors":13,"./requestmanager":27}],23:[function(t,e,n){var r=t("./contract"),o="0xc6d9d2cd449a754c494264e1809c50e34d64562b",i=[{constant:!0,inputs:[{name:"_owner",type:"address"}],name:"name",outputs:[{name:"o_name",type:"bytes32"}],type:"function"},{constant:!0,inputs:[{name:"_name",type:"bytes32"}],name:"owner",outputs:[{name:"",type:"address"}],type:"function"},{constant:!0,inputs:[{name:"_name",type:"bytes32"}],name:"content",outputs:[{name:"",type:"bytes32"}],type:"function"},{constant:!0,inputs:[{name:"_name",type:"bytes32"}],name:"addr",outputs:[{name:"",type:"address"}],type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"}],name:"reserve",outputs:[],type:"function"},{constant:!0,inputs:[{name:"_name",type:"bytes32"}],name:"subRegistrar",outputs:[{name:"o_subRegistrar",type:"address"}],type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"},{name:"_newOwner",type:"address"}],name:"transfer",outputs:[],type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"},{name:"_registrar",type:"address"}],name:"setSubRegistrar",outputs:[],type:"function"},{constant:!1,inputs:[],name:"Registrar",outputs:[],type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"},{name:"_a",type:"address"},{name:"_primary",type:"bool"}],name:"setAddress",outputs:[],type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"},{name:"_content",type:"bytes32"}],name:"setContent",outputs:[], -type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"}],name:"disown",outputs:[],type:"function"},{constant:!0,inputs:[{name:"_name",type:"bytes32"}],name:"register",outputs:[{name:"",type:"address"}],type:"function"},{anonymous:!1,inputs:[{indexed:!0,name:"name",type:"bytes32"}],name:"Changed",type:"event"},{anonymous:!1,inputs:[{indexed:!0,name:"name",type:"bytes32"},{indexed:!0,name:"addr",type:"address"}],name:"PrimaryChanged",type:"event"}];e.exports=r(i).at(o)},{"./contract":11}],24:[function(t,e,n){var r=t("../utils/utils"),o=t("./property"),i=[],a=[new o({name:"listening",getter:"net_listening"}),new o({name:"peerCount",getter:"net_peerCount",outputFormatter:r.toDecimal})];e.exports={methods:i,properties:a}},{"../utils/utils":7,"./property":25}],25:[function(t,e,n){var r=t("./requestmanager"),o=function(t){this.name=t.name,this.getter=t.getter,this.setter=t.setter,this.outputFormatter=t.outputFormatter,this.inputFormatter=t.inputFormatter};o.prototype.formatInput=function(t){return this.inputFormatter?this.inputFormatter(t):t},o.prototype.formatOutput=function(t){return this.outputFormatter&&null!==t?this.outputFormatter(t):t},o.prototype.attachToObject=function(t){var e={get:this.get.bind(this)},n=this.name.split("."),r=n[0];n.length>1&&(t[n[0]]=t[n[0]]||{},t=t[n[0]],r=n[1]),Object.defineProperty(t,r,e);var o=function(t,e){return t+e.charAt(0).toUpperCase()+e.slice(1)};t[o("get",r)]=this.getAsync.bind(this)},o.prototype.get=function(){return this.formatOutput(r.getInstance().send({method:this.getter}))},o.prototype.getAsync=function(t){var e=this;r.getInstance().sendAsync({method:this.getter},function(n,r){return n?t(n):void t(n,e.formatOutput(r))})},e.exports=o},{"./requestmanager":27}],26:[function(t,e,n){var r=function(){};r.prototype.send=function(t){var e=navigator.qt.callMethod(JSON.stringify(t));return JSON.parse(e)},e.exports=r},{}],27:[function(t,e,n){var r=t("./jsonrpc"),o=t("../utils/utils"),i=t("../utils/config"),a=t("./errors"),s=function(t){return arguments.callee._singletonInstance?arguments.callee._singletonInstance:(arguments.callee._singletonInstance=this,this.provider=t,this.polls=[],this.timeout=null,void this.poll())};s.getInstance=function(){var t=new s;return t},s.prototype.send=function(t){if(!this.provider)return console.error(a.InvalidProvider()),null;var e=r.getInstance().toPayload(t.method,t.params),n=this.provider.send(e);if(!r.getInstance().isValidResponse(n))throw a.InvalidResponse(n);return n.result},s.prototype.sendAsync=function(t,e){if(!this.provider)return e(a.InvalidProvider());var n=r.getInstance().toPayload(t.method,t.params);this.provider.sendAsync(n,function(t,n){return t?e(t):r.getInstance().isValidResponse(n)?void e(null,n.result):e(a.InvalidResponse(n))})},s.prototype.sendBatch=function(t,e){if(!this.provider)return e(a.InvalidProvider());var n=r.getInstance().toBatchPayload(t);this.provider.sendAsync(n,function(t,n){return t?e(t):o.isArray(n)?void e(t,n):e(a.InvalidResponse(n))})},s.prototype.setProvider=function(t){this.provider=t},s.prototype.startPolling=function(t,e,n,r){this.polls.push({data:t,id:e,callback:n,uninstall:r})},s.prototype.stopPolling=function(t){for(var e=this.polls.length;e--;){var n=this.polls[e];n.id===t&&this.polls.splice(e,1)}},s.prototype.reset=function(){this.polls.forEach(function(t){t.uninstall(t.id)}),this.polls=[],this.timeout&&(clearTimeout(this.timeout),this.timeout=null),this.poll()},s.prototype.poll=function(){if(this.timeout=setTimeout(this.poll.bind(this),i.ETH_POLLING_TIMEOUT),this.polls.length){if(!this.provider)return void console.error(a.InvalidProvider());var t=r.getInstance().toBatchPayload(this.polls.map(function(t){return t.data})),e=this;this.provider.sendAsync(t,function(t,n){if(!t){if(!o.isArray(n))throw a.InvalidResponse(n);n.map(function(t,n){return t.callback=e.polls[n].callback,t}).filter(function(t){var e=r.getInstance().isValidResponse(t);return e||t.callback(a.InvalidResponse(t)),e}).filter(function(t){return o.isArray(t.result)&&t.result.length>0}).forEach(function(t){t.callback(null,t.result)})}})}},e.exports=s},{"../utils/config":5,"../utils/utils":7,"./errors":13,"./jsonrpc":21}],28:[function(t,e,n){var r=t("./method"),o=t("./formatters"),i=new r({name:"post",call:"shh_post",params:1,inputFormatter:[o.inputPostFormatter]}),a=new r({name:"newIdentity",call:"shh_newIdentity",params:0}),s=new r({name:"hasIdentity",call:"shh_hasIdentity",params:1}),u=new r({name:"newGroup",call:"shh_newGroup",params:0}),c=new r({name:"addToGroup",call:"shh_addToGroup",params:0}),p=[i,a,s,u,c];e.exports={methods:p}},{"./formatters":17,"./method":22}],29:[function(t,e,n){var r=t("../web3"),o=t("./icap"),i=t("./namereg"),a=t("./contract"),s=function(t,e,n,r){var a=new o(e);if(!a.isValid())throw new Error("invalid iban address");if(a.isDirect())return u(t,a.address(),n,r);if(!r){var s=i.addr(a.institution());return c(t,s,n,a.client())}i.addr(a.insitution(),function(e,o){return c(t,o,n,a.client(),r)})},u=function(t,e,n,o){return r.eth.sendTransaction({address:e,from:t,value:n},o)},c=function(t,e,n,r,o){var i=[{constant:!1,inputs:[{name:"name",type:"bytes32"}],name:"deposit",outputs:[],type:"function"}];return a(i).at(e).deposit(r,{from:t,value:n},o)};e.exports=s},{"../web3":9,"./contract":11,"./icap":20,"./namereg":23}],30:[function(t,e,n){var r=t("./method"),o=function(){var t=function(t){var e=t[0];switch(e){case"latest":return t.pop(),this.params=0,"eth_newBlockFilter";case"pending":return t.pop(),this.params=0,"eth_newPendingTransactionFilter";default:return"eth_newFilter"}},e=new r({name:"newFilter",call:t,params:1}),n=new r({name:"uninstallFilter",call:"eth_uninstallFilter",params:1}),o=new r({name:"getLogs",call:"eth_getFilterLogs",params:1}),i=new r({name:"poll",call:"eth_getFilterChanges",params:1});return[e,n,o,i]},i=function(){var t=new r({name:"newFilter",call:"shh_newFilter",params:1}),e=new r({name:"uninstallFilter",call:"shh_uninstallFilter",params:1}),n=new r({name:"getLogs",call:"shh_getMessages",params:1}),o=new r({name:"poll",call:"shh_getFilterChanges",params:1});return[t,e,n,o]};e.exports={eth:o,shh:i}},{"./method":22}],31:[function(t,e,n){},{}],32:[function(t,e,n){!function(t,r){"object"==typeof n?e.exports=n=r():"function"==typeof define&&define.amd?define([],r):t.CryptoJS=r()}(this,function(){var t=t||function(t,e){var n={},r=n.lib={},o=r.Base=function(){function t(){}return{extend:function(e){t.prototype=this;var n=new t;return e&&n.mixIn(e),n.hasOwnProperty("init")||(n.init=function(){n.$super.init.apply(this,arguments)}),n.init.prototype=n,n.$super=this,n},create:function(){var t=this.extend();return t.init.apply(t,arguments),t},init:function(){},mixIn:function(t){for(var e in t)t.hasOwnProperty(e)&&(this[e]=t[e]);t.hasOwnProperty("toString")&&(this.toString=t.toString)},clone:function(){return this.init.prototype.extend(this)}}}(),i=r.WordArray=o.extend({init:function(t,n){t=this.words=t||[],this.sigBytes=n!=e?n:4*t.length},toString:function(t){return(t||s).stringify(this)},concat:function(t){var e=this.words,n=t.words,r=this.sigBytes,o=t.sigBytes;if(this.clamp(),r%4)for(var i=0;o>i;i++){var a=n[i>>>2]>>>24-i%4*8&255;e[r+i>>>2]|=a<<24-(r+i)%4*8}else for(var i=0;o>i;i+=4)e[r+i>>>2]=n[i>>>2];return this.sigBytes+=o,this},clamp:function(){var e=this.words,n=this.sigBytes;e[n>>>2]&=4294967295<<32-n%4*8,e.length=t.ceil(n/4)},clone:function(){var t=o.clone.call(this);return t.words=this.words.slice(0),t},random:function(e){for(var n,r=[],o=function(e){var e=e,n=987654321,r=4294967295;return function(){n=36969*(65535&n)+(n>>16)&r,e=18e3*(65535&e)+(e>>16)&r;var o=(n<<16)+e&r;return o/=4294967296,o+=.5,o*(t.random()>.5?1:-1)}},a=0;e>a;a+=4){var s=o(4294967296*(n||t.random()));n=987654071*s(),r.push(4294967296*s()|0)}return new i.init(r,e)}}),a=n.enc={},s=a.Hex={stringify:function(t){for(var e=t.words,n=t.sigBytes,r=[],o=0;n>o;o++){var i=e[o>>>2]>>>24-o%4*8&255;r.push((i>>>4).toString(16)),r.push((15&i).toString(16))}return r.join("")},parse:function(t){for(var e=t.length,n=[],r=0;e>r;r+=2)n[r>>>3]|=parseInt(t.substr(r,2),16)<<24-r%8*4;return new i.init(n,e/2)}},u=a.Latin1={stringify:function(t){for(var e=t.words,n=t.sigBytes,r=[],o=0;n>o;o++){var i=e[o>>>2]>>>24-o%4*8&255;r.push(String.fromCharCode(i))}return r.join("")},parse:function(t){for(var e=t.length,n=[],r=0;e>r;r++)n[r>>>2]|=(255&t.charCodeAt(r))<<24-r%4*8;return new i.init(n,e)}},c=a.Utf8={stringify:function(t){try{return decodeURIComponent(escape(u.stringify(t)))}catch(e){throw new Error("Malformed UTF-8 data")}},parse:function(t){return u.parse(unescape(encodeURIComponent(t)))}},p=r.BufferedBlockAlgorithm=o.extend({reset:function(){this._data=new i.init,this._nDataBytes=0},_append:function(t){"string"==typeof t&&(t=c.parse(t)),this._data.concat(t),this._nDataBytes+=t.sigBytes},_process:function(e){var n=this._data,r=n.words,o=n.sigBytes,a=this.blockSize,s=4*a,u=o/s;u=e?t.ceil(u):t.max((0|u)-this._minBufferSize,0);var c=u*a,p=t.min(4*c,o);if(c){for(var l=0;c>l;l+=a)this._doProcessBlock(r,l);var f=r.splice(0,c);n.sigBytes-=p}return new i.init(f,p)},clone:function(){var t=o.clone.call(this);return t._data=this._data.clone(),t},_minBufferSize:0}),l=(r.Hasher=p.extend({cfg:o.extend(),init:function(t){this.cfg=this.cfg.extend(t),this.reset()},reset:function(){p.reset.call(this),this._doReset()},update:function(t){return this._append(t),this._process(),this},finalize:function(t){t&&this._append(t);var e=this._doFinalize();return e},blockSize:16,_createHelper:function(t){return function(e,n){return new t.init(n).finalize(e)}},_createHmacHelper:function(t){return function(e,n){return new l.HMAC.init(t,n).finalize(e)}}}),n.algo={});return n}(Math);return t})},{}],33:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./x64-core")):"function"==typeof define&&define.amd?define(["./core","./x64-core"],o):o(r.CryptoJS)}(this,function(t){return function(e){var n=t,r=n.lib,o=r.WordArray,i=r.Hasher,a=n.x64,s=a.Word,u=n.algo,c=[],p=[],l=[];!function(){for(var t=1,e=0,n=0;24>n;n++){c[t+5*e]=(n+1)*(n+2)/2%64;var r=e%5,o=(2*t+3*e)%5;t=r,e=o}for(var t=0;5>t;t++)for(var e=0;5>e;e++)p[t+5*e]=e+(2*t+3*e)%5*5;for(var i=1,a=0;24>a;a++){for(var u=0,f=0,m=0;7>m;m++){if(1&i){var h=(1<h?f^=1<t;t++)f[t]=s.create()}();var m=u.SHA3=i.extend({cfg:i.cfg.extend({outputLength:512}),_doReset:function(){for(var t=this._state=[],e=0;25>e;e++)t[e]=new s.init;this.blockSize=(1600-2*this.cfg.outputLength)/32},_doProcessBlock:function(t,e){for(var n=this._state,r=this.blockSize/2,o=0;r>o;o++){var i=t[e+2*o],a=t[e+2*o+1];i=16711935&(i<<8|i>>>24)|4278255360&(i<<24|i>>>8),a=16711935&(a<<8|a>>>24)|4278255360&(a<<24|a>>>8);var s=n[o];s.high^=a,s.low^=i}for(var u=0;24>u;u++){for(var m=0;5>m;m++){for(var h=0,d=0,y=0;5>y;y++){var s=n[m+5*y];h^=s.high,d^=s.low}var g=f[m];g.high=h,g.low=d}for(var m=0;5>m;m++)for(var v=f[(m+4)%5],b=f[(m+1)%5],w=b.high,_=b.low,h=v.high^(w<<1|_>>>31),d=v.low^(_<<1|w>>>31),y=0;5>y;y++){var s=n[m+5*y];s.high^=h,s.low^=d}for(var x=1;25>x;x++){var s=n[x],F=s.high,B=s.low,I=c[x];if(32>I)var h=F<>>32-I,d=B<>>32-I;else var h=B<>>64-I,d=F<>>64-I;var N=f[p[x]];N.high=h,N.low=d}var k=f[0],A=n[0];k.high=A.high,k.low=A.low;for(var m=0;5>m;m++)for(var y=0;5>y;y++){var x=m+5*y,s=n[x],P=f[x],O=f[(m+1)%5+5*y],T=f[(m+2)%5+5*y];s.high=P.high^~O.high&T.high,s.low=P.low^~O.low&T.low}var s=n[0],D=l[u];s.high^=D.high,s.low^=D.low}},_doFinalize:function(){var t=this._data,n=t.words,r=(8*this._nDataBytes,8*t.sigBytes),i=32*this.blockSize;n[r>>>5]|=1<<24-r%32,n[(e.ceil((r+1)/i)*i>>>5)-1]|=128,t.sigBytes=4*n.length,this._process();for(var a=this._state,s=this.cfg.outputLength/8,u=s/8,c=[],p=0;u>p;p++){var l=a[p],f=l.high,m=l.low;f=16711935&(f<<8|f>>>24)|4278255360&(f<<24|f>>>8),m=16711935&(m<<8|m>>>24)|4278255360&(m<<24|m>>>8),c.push(m),c.push(f)}return new o.init(c,s)},clone:function(){for(var t=i.clone.call(this),e=t._state=this._state.slice(0),n=0;25>n;n++)e[n]=e[n].clone();return t}});n.SHA3=i._createHelper(m),n.HmacSHA3=i._createHmacHelper(m)}(Math),t.SHA3})},{"./core":32,"./x64-core":34}],34:[function(t,e,n){!function(r,o){"object"==typeof n?e.exports=n=o(t("./core")):"function"==typeof define&&define.amd?define(["./core"],o):o(r.CryptoJS)}(this,function(t){return function(e){{var n=t,r=n.lib,o=r.Base,i=r.WordArray,a=n.x64={};a.Word=o.extend({init:function(t,e){this.high=t,this.low=e}}),a.WordArray=o.extend({init:function(t,n){t=this.words=t||[],this.sigBytes=n!=e?n:8*t.length},toX32:function(){for(var t=this.words,e=t.length,n=[],r=0;e>r;r++){var o=t[r];n.push(o.high),n.push(o.low)}return i.create(n,this.sigBytes)},clone:function(){for(var t=o.clone.call(this),e=t.words=this.words.slice(0),n=e.length,r=0;n>r;r++)e[r]=e[r].clone();return t}})}}(),t})},{"./core":32}],"bignumber.js":[function(t,e,n){"use strict";e.exports=BigNumber},{}],web3:[function(t,e,n){var r=t("./lib/web3");r.providers.HttpProvider=t("./lib/web3/httpprovider"),r.providers.QtSyncProvider=t("./lib/web3/qtsync"),r.eth.contract=t("./lib/web3/contract"),r.eth.namereg=t("./lib/web3/namereg"),r.eth.sendIBANTransaction=t("./lib/web3/transfer"),"undefined"!=typeof window&&"undefined"==typeof window.web3&&(window.web3=r),e.exports=r},{"./lib/web3":9,"./lib/web3/contract":11,"./lib/web3/httpprovider":19,"./lib/web3/namereg":23,"./lib/web3/qtsync":26,"./lib/web3/transfer":29}]},{},["web3"]); \ No newline at end of file diff --git a/libjsqrc/ethereumjs/dist/web3.js b/libjsqrc/ethereumjs/dist/web3.js index 2ff72ea6c..ec5cdfe83 100644 --- a/libjsqrc/ethereumjs/dist/web3.js +++ b/libjsqrc/ethereumjs/dist/web3.js @@ -1388,6 +1388,7 @@ var Filter = require('./web3/filter'); var utils = require('./utils/utils'); var formatters = require('./web3/formatters'); var RequestManager = require('./web3/requestmanager'); +var Method = require('./web3/method'); var c = require('./utils/config'); var Property = require('./web3/property'); var Batch = require('./web3/batch'); @@ -1512,10 +1513,22 @@ setupProperties(web3.eth, eth.properties); setupMethods(web3.db, db.methods); setupMethods(web3.shh, shh.methods); +web3.admin = {}; +web3.admin.setSessionKey = function(s) { web3.admin.sessionKey = s; }; + +var blockQueueStatus = new Method({ + name: 'blockQueueStatus', + call: 'admin_eth_blockQueueStatus', + params: 1, + inputFormatter: [function() { return web3.admin.sessionKey; }] +}); + +setupMethods(web3.admin, [blockQueueStatus]); + module.exports = web3; -},{"./utils/config":5,"./utils/sha3":6,"./utils/utils":7,"./version.json":8,"./web3/batch":10,"./web3/db":12,"./web3/eth":14,"./web3/filter":16,"./web3/formatters":17,"./web3/net":24,"./web3/property":25,"./web3/requestmanager":27,"./web3/shh":28,"./web3/watches":30}],10:[function(require,module,exports){ +},{"./utils/config":5,"./utils/sha3":6,"./utils/utils":7,"./version.json":8,"./web3/batch":10,"./web3/db":12,"./web3/eth":14,"./web3/filter":16,"./web3/formatters":17,"./web3/method":22,"./web3/net":24,"./web3/property":25,"./web3/requestmanager":27,"./web3/shh":28,"./web3/watches":30}],10:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1938,11 +1951,11 @@ var uncleCountCall = function (args) { /// @returns an array of objects describing web3.eth api methods var getBalance = new Method({ - name: 'getBalance', - call: 'eth_getBalance', - params: 2, - inputFormatter: [utils.toAddress, formatters.inputDefaultBlockNumberFormatter], - outputFormatter: formatters.outputBigNumberFormatter + name: 'getBalance', + call: 'eth_getBalance', + params: 2, + inputFormatter: [utils.toAddress, formatters.inputDefaultBlockNumberFormatter], + outputFormatter: formatters.outputBigNumberFormatter }); var getStorageAt = new Method({ @@ -8262,4 +8275,4 @@ module.exports = web3; },{"./lib/web3":9,"./lib/web3/contract":11,"./lib/web3/httpprovider":19,"./lib/web3/namereg":23,"./lib/web3/qtsync":26,"./lib/web3/transfer":29}]},{},["web3"]) -//# sourceMappingURL=data:application/json;charset:utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJsaWIvc29saWRpdHkvY29kZXIuanMiLCJsaWIvc29saWRpdHkvZm9ybWF0dGVycy5qcyIsImxpYi9zb2xpZGl0eS9wYXJhbS5qcyIsImxpYi91dGlscy9icm93c2VyLXhoci5qcyIsImxpYi91dGlscy9jb25maWcuanMiLCJsaWIvdXRpbHMvc2hhMy5qcyIsImxpYi91dGlscy91dGlscy5qcyIsImxpYi92ZXJzaW9uLmpzb24iLCJsaWIvd2ViMy5qcyIsImxpYi93ZWIzL2JhdGNoLmpzIiwibGliL3dlYjMvY29udHJhY3QuanMiLCJsaWIvd2ViMy9kYi5qcyIsImxpYi93ZWIzL2Vycm9ycy5qcyIsImxpYi93ZWIzL2V0aC5qcyIsImxpYi93ZWIzL2V2ZW50LmpzIiwibGliL3dlYjMvZmlsdGVyLmpzIiwibGliL3dlYjMvZm9ybWF0dGVycy5qcyIsImxpYi93ZWIzL2Z1bmN0aW9uLmpzIiwibGliL3dlYjMvaHR0cHByb3ZpZGVyLmpzIiwibGliL3dlYjMvaWNhcC5qcyIsImxpYi93ZWIzL2pzb25ycGMuanMiLCJsaWIvd2ViMy9tZXRob2QuanMiLCJsaWIvd2ViMy9uYW1lcmVnLmpzIiwibGliL3dlYjMvbmV0LmpzIiwibGliL3dlYjMvcHJvcGVydHkuanMiLCJsaWIvd2ViMy9xdHN5bmMuanMiLCJsaWIvd2ViMy9yZXF1ZXN0bWFuYWdlci5qcyIsImxpYi93ZWIzL3NoaC5qcyIsImxpYi93ZWIzL3RyYW5zZmVyLmpzIiwibGliL3dlYjMvd2F0Y2hlcy5qcyIsIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L2xpYi9fZW1wdHkuanMiLCJub2RlX21vZHVsZXMvY3J5cHRvLWpzL2NvcmUuanMiLCJub2RlX21vZHVsZXMvY3J5cHRvLWpzL3NoYTMuanMiLCJub2RlX21vZHVsZXMvY3J5cHRvLWpzL3g2NC1jb3JlLmpzIiwiYmlnbnVtYmVyLmpzIiwiaW5kZXguanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxUkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFOQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsTkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDVEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3ZDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsZkE7QUFDQTtBQUNBO0FBQ0E7O0FDSEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbEtBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDN0RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3BMQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeERBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN0Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25SQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuTUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzNKQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMU5BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pPQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDM0ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUtBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcEhBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN0UEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3BFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzlGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsSEE7O0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcnVCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xVQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvU0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzbkZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBcbiAqIEBmaWxlIGNvZGVyLmpzXG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciBCaWdOdW1iZXIgPSByZXF1aXJlKCdiaWdudW1iZXIuanMnKTtcbnZhciB1dGlscyA9IHJlcXVpcmUoJy4uL3V0aWxzL3V0aWxzJyk7XG52YXIgZiA9IHJlcXVpcmUoJy4vZm9ybWF0dGVycycpO1xudmFyIFNvbGlkaXR5UGFyYW0gPSByZXF1aXJlKCcuL3BhcmFtJyk7XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gY2hlY2sgaWYgYSB0eXBlIGlzIGFuIGFycmF5IHR5cGVcbiAqXG4gKiBAbWV0aG9kIGlzQXJyYXlUeXBlXG4gKiBAcGFyYW0ge1N0cmluZ30gdHlwZVxuICogQHJldHVybiB7Qm9vbH0gdHJ1ZSBpcyB0aGUgdHlwZSBpcyBhbiBhcnJheSwgb3RoZXJ3aXNlIGZhbHNlXG4gKi9cbnZhciBpc0FycmF5VHlwZSA9IGZ1bmN0aW9uICh0eXBlKSB7XG4gICAgcmV0dXJuIHR5cGUuc2xpY2UoLTIpID09PSAnW10nO1xufTtcblxuLyoqXG4gKiBTb2xpZGl0eVR5cGUgcHJvdG90eXBlIGlzIHVzZWQgdG8gZW5jb2RlL2RlY29kZSBzb2xpZGl0eSBwYXJhbXMgb2YgY2VydGFpbiB0eXBlXG4gKi9cbnZhciBTb2xpZGl0eVR5cGUgPSBmdW5jdGlvbiAoY29uZmlnKSB7XG4gICAgdGhpcy5fbmFtZSA9IGNvbmZpZy5uYW1lO1xuICAgIHRoaXMuX21hdGNoID0gY29uZmlnLm1hdGNoO1xuICAgIHRoaXMuX21vZGUgPSBjb25maWcubW9kZTtcbiAgICB0aGlzLl9pbnB1dEZvcm1hdHRlciA9IGNvbmZpZy5pbnB1dEZvcm1hdHRlcjtcbiAgICB0aGlzLl9vdXRwdXRGb3JtYXR0ZXIgPSBjb25maWcub3V0cHV0Rm9ybWF0dGVyO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBkZXRlcm1pbmUgaWYgdGhpcyBTb2xpZGl0eVR5cGUgZG8gbWF0Y2ggZ2l2ZW4gdHlwZVxuICpcbiAqIEBtZXRob2QgaXNUeXBlXG4gKiBAcGFyYW0ge1N0cmluZ30gbmFtZVxuICogQHJldHVybiB7Qm9vbH0gdHJ1ZSBpZiB0eXBlIG1hdGNoIHRoaXMgU29saWRpdHlUeXBlLCBvdGhlcndpc2UgZmFsc2VcbiAqL1xuU29saWRpdHlUeXBlLnByb3RvdHlwZS5pc1R5cGUgPSBmdW5jdGlvbiAobmFtZSkge1xuICAgIGlmICh0aGlzLl9tYXRjaCA9PT0gJ3N0cmljdCcpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX25hbWUgPT09IG5hbWUgfHwgKG5hbWUuaW5kZXhPZih0aGlzLl9uYW1lKSA9PT0gMCAmJiBuYW1lLnNsaWNlKHRoaXMuX25hbWUubGVuZ3RoKSA9PT0gJ1tdJyk7XG4gICAgfSBlbHNlIGlmICh0aGlzLl9tYXRjaCA9PT0gJ3ByZWZpeCcpIHtcbiAgICAgICAgLy8gVE9ETyBiZXR0ZXIgdHlwZSBkZXRlY3Rpb24hXG4gICAgICAgIHJldHVybiBuYW1lLmluZGV4T2YodGhpcy5fbmFtZSkgPT09IDA7XG4gICAgfVxufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byB0cmFuc2Zvcm0gcGxhaW4gcGFyYW0gdG8gU29saWRpdHlQYXJhbSBvYmplY3RcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdElucHV0XG4gKiBAcGFyYW0ge09iamVjdH0gcGFyYW0gLSBwbGFpbiBvYmplY3QsIG9yIGFuIGFycmF5IG9mIG9iamVjdHNcbiAqIEBwYXJhbSB7Qm9vbH0gYXJyYXlUeXBlIC0gdHJ1ZSBpZiBhIHBhcmFtIHNob3VsZCBiZSBlbmNvZGVkIGFzIGFuIGFycmF5XG4gKiBAcmV0dXJuIHtTb2xpZGl0eVBhcmFtfSBlbmNvZGVkIHBhcmFtIHdyYXBwZWQgaW4gU29saWRpdHlQYXJhbSBvYmplY3QgXG4gKi9cblNvbGlkaXR5VHlwZS5wcm90b3R5cGUuZm9ybWF0SW5wdXQgPSBmdW5jdGlvbiAocGFyYW0sIGFycmF5VHlwZSkge1xuICAgIGlmICh1dGlscy5pc0FycmF5KHBhcmFtKSAmJiBhcnJheVR5cGUpIHsgLy8gVE9ETzogc2hvdWxkIGZhaWwgaWYgdGhpcyB0d28gYXJlIG5vdCB0aGUgc2FtZVxuICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgIHJldHVybiBwYXJhbS5tYXAoZnVuY3Rpb24gKHApIHtcbiAgICAgICAgICAgIHJldHVybiBzZWxmLl9pbnB1dEZvcm1hdHRlcihwKTtcbiAgICAgICAgfSkucmVkdWNlKGZ1bmN0aW9uIChhY2MsIGN1cnJlbnQpIHtcbiAgICAgICAgICAgIHJldHVybiBhY2MuY29tYmluZShjdXJyZW50KTtcbiAgICAgICAgfSwgZi5mb3JtYXRJbnB1dEludChwYXJhbS5sZW5ndGgpKS53aXRoT2Zmc2V0KDMyKTtcbiAgICB9IFxuICAgIHJldHVybiB0aGlzLl9pbnB1dEZvcm1hdHRlcihwYXJhbSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIHRyYW5zb2Zvcm0gU29saWRpdHlQYXJhbSB0byBwbGFpbiBwYXJhbVxuICpcbiAqIEBtZXRob2QgZm9ybWF0T3V0cHV0XG4gKiBAcGFyYW0ge1NvbGlkaXR5UGFyYW19IGJ5dGVBcnJheVxuICogQHBhcmFtIHtCb29sfSBhcnJheVR5cGUgLSB0cnVlIGlmIGEgcGFyYW0gc2hvdWxkIGJlIGRlY29kZWQgYXMgYW4gYXJyYXlcbiAqIEByZXR1cm4ge09iamVjdH0gcGxhaW4gZGVjb2RlZCBwYXJhbVxuICovXG5Tb2xpZGl0eVR5cGUucHJvdG90eXBlLmZvcm1hdE91dHB1dCA9IGZ1bmN0aW9uIChwYXJhbSwgYXJyYXlUeXBlKSB7XG4gICAgaWYgKGFycmF5VHlwZSkge1xuICAgICAgICAvLyBsZXQncyBhc3N1bWUsIHRoYXQgd2Ugc29saWRpdHkgd2lsbCBuZXZlciByZXR1cm4gbG9uZyBhcnJheXMgOlAgXG4gICAgICAgIHZhciByZXN1bHQgPSBbXTtcbiAgICAgICAgdmFyIGxlbmd0aCA9IG5ldyBCaWdOdW1iZXIocGFyYW0uZHluYW1pY1BhcnQoKS5zbGljZSgwLCA2NCksIDE2KTtcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsZW5ndGggKiA2NDsgaSArPSA2NCkge1xuICAgICAgICAgICAgcmVzdWx0LnB1c2godGhpcy5fb3V0cHV0Rm9ybWF0dGVyKG5ldyBTb2xpZGl0eVBhcmFtKHBhcmFtLmR5bmFtaWNQYXJ0KCkuc3Vic3RyKGkgKyA2NCwgNjQpKSkpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9vdXRwdXRGb3JtYXR0ZXIocGFyYW0pO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBzbGljZSBzaW5nbGUgcGFyYW0gZnJvbSBieXRlc1xuICpcbiAqIEBtZXRob2Qgc2xpY2VQYXJhbVxuICogQHBhcmFtIHtTdHJpbmd9IGJ5dGVzXG4gKiBAcGFyYW0ge051bWJlcn0gaW5kZXggb2YgcGFyYW0gdG8gc2xpY2VcbiAqIEBwYXJhbSB7U3RyaW5nfSB0eXBlXG4gKiBAcmV0dXJucyB7U29saWRpdHlQYXJhbX0gcGFyYW1cbiAqL1xuU29saWRpdHlUeXBlLnByb3RvdHlwZS5zbGljZVBhcmFtID0gZnVuY3Rpb24gKGJ5dGVzLCBpbmRleCwgdHlwZSkge1xuICAgIGlmICh0aGlzLl9tb2RlID09PSAnYnl0ZXMnKSB7XG4gICAgICAgIHJldHVybiBTb2xpZGl0eVBhcmFtLmRlY29kZUJ5dGVzKGJ5dGVzLCBpbmRleCk7XG4gICAgfSBlbHNlIGlmIChpc0FycmF5VHlwZSh0eXBlKSkge1xuICAgICAgICByZXR1cm4gU29saWRpdHlQYXJhbS5kZWNvZGVBcnJheShieXRlcywgaW5kZXgpO1xuICAgIH1cbiAgICByZXR1cm4gU29saWRpdHlQYXJhbS5kZWNvZGVQYXJhbShieXRlcywgaW5kZXgpO1xufTtcblxuLyoqXG4gKiBTb2xpZGl0eUNvZGVyIHByb3RvdHlwZSBzaG91bGQgYmUgdXNlZCB0byBlbmNvZGUvZGVjb2RlIHNvbGlkaXR5IHBhcmFtcyBvZiBhbnkgdHlwZVxuICovXG52YXIgU29saWRpdHlDb2RlciA9IGZ1bmN0aW9uICh0eXBlcykge1xuICAgIHRoaXMuX3R5cGVzID0gdHlwZXM7XG59O1xuXG4vKipcbiAqIFRoaXMgbWV0aG9kIHNob3VsZCBiZSB1c2VkIHRvIHRyYW5zZm9ybSB0eXBlIHRvIFNvbGlkaXR5VHlwZVxuICpcbiAqIEBtZXRob2QgX3JlcXVpcmVUeXBlXG4gKiBAcGFyYW0ge1N0cmluZ30gdHlwZVxuICogQHJldHVybnMge1NvbGlkaXR5VHlwZX0gXG4gKiBAdGhyb3dzIHtFcnJvcn0gdGhyb3dzIGlmIG5vIG1hdGNoaW5nIHR5cGUgaXMgZm91bmRcbiAqL1xuU29saWRpdHlDb2Rlci5wcm90b3R5cGUuX3JlcXVpcmVUeXBlID0gZnVuY3Rpb24gKHR5cGUpIHtcbiAgICB2YXIgc29saWRpdHlUeXBlID0gdGhpcy5fdHlwZXMuZmlsdGVyKGZ1bmN0aW9uICh0KSB7XG4gICAgICAgIHJldHVybiB0LmlzVHlwZSh0eXBlKTtcbiAgICB9KVswXTtcblxuICAgIGlmICghc29saWRpdHlUeXBlKSB7XG4gICAgICAgIHRocm93IEVycm9yKCdpbnZhbGlkIHNvbGlkaXR5IHR5cGUhOiAnICsgdHlwZSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHNvbGlkaXR5VHlwZTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gdHJhbnNmb3JtIHBsYWluIHBhcmFtIG9mIGdpdmVuIHR5cGUgdG8gU29saWRpdHlQYXJhbVxuICpcbiAqIEBtZXRob2QgX2Zvcm1hdElucHV0XG4gKiBAcGFyYW0ge1N0cmluZ30gdHlwZSBvZiBwYXJhbVxuICogQHBhcmFtIHtPYmplY3R9IHBsYWluIHBhcmFtXG4gKiBAcmV0dXJuIHtTb2xpZGl0eVBhcmFtfVxuICovXG5Tb2xpZGl0eUNvZGVyLnByb3RvdHlwZS5fZm9ybWF0SW5wdXQgPSBmdW5jdGlvbiAodHlwZSwgcGFyYW0pIHtcbiAgICByZXR1cm4gdGhpcy5fcmVxdWlyZVR5cGUodHlwZSkuZm9ybWF0SW5wdXQocGFyYW0sIGlzQXJyYXlUeXBlKHR5cGUpKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZW5jb2RlIHBsYWluIHBhcmFtXG4gKlxuICogQG1ldGhvZCBlbmNvZGVQYXJhbVxuICogQHBhcmFtIHtTdHJpbmd9IHR5cGVcbiAqIEBwYXJhbSB7T2JqZWN0fSBwbGFpbiBwYXJhbVxuICogQHJldHVybiB7U3RyaW5nfSBlbmNvZGVkIHBsYWluIHBhcmFtXG4gKi9cblNvbGlkaXR5Q29kZXIucHJvdG90eXBlLmVuY29kZVBhcmFtID0gZnVuY3Rpb24gKHR5cGUsIHBhcmFtKSB7XG4gICAgcmV0dXJuIHRoaXMuX2Zvcm1hdElucHV0KHR5cGUsIHBhcmFtKS5lbmNvZGUoKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZW5jb2RlIGxpc3Qgb2YgcGFyYW1zXG4gKlxuICogQG1ldGhvZCBlbmNvZGVQYXJhbXNcbiAqIEBwYXJhbSB7QXJyYXl9IHR5cGVzXG4gKiBAcGFyYW0ge0FycmF5fSBwYXJhbXNcbiAqIEByZXR1cm4ge1N0cmluZ30gZW5jb2RlZCBsaXN0IG9mIHBhcmFtc1xuICovXG5Tb2xpZGl0eUNvZGVyLnByb3RvdHlwZS5lbmNvZGVQYXJhbXMgPSBmdW5jdGlvbiAodHlwZXMsIHBhcmFtcykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgc29saWRpdHlQYXJhbXMgPSB0eXBlcy5tYXAoZnVuY3Rpb24gKHR5cGUsIGluZGV4KSB7XG4gICAgICAgIHJldHVybiBzZWxmLl9mb3JtYXRJbnB1dCh0eXBlLCBwYXJhbXNbaW5kZXhdKTtcbiAgICB9KTtcblxuICAgIHJldHVybiBTb2xpZGl0eVBhcmFtLmVuY29kZUxpc3Qoc29saWRpdHlQYXJhbXMpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBkZWNvZGUgYnl0ZXMgdG8gcGxhaW4gcGFyYW1cbiAqXG4gKiBAbWV0aG9kIGRlY29kZVBhcmFtXG4gKiBAcGFyYW0ge1N0cmluZ30gdHlwZVxuICogQHBhcmFtIHtTdHJpbmd9IGJ5dGVzXG4gKiBAcmV0dXJuIHtPYmplY3R9IHBsYWluIHBhcmFtXG4gKi9cblNvbGlkaXR5Q29kZXIucHJvdG90eXBlLmRlY29kZVBhcmFtID0gZnVuY3Rpb24gKHR5cGUsIGJ5dGVzKSB7XG4gICAgcmV0dXJuIHRoaXMuZGVjb2RlUGFyYW1zKFt0eXBlXSwgYnl0ZXMpWzBdO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBkZWNvZGUgbGlzdCBvZiBwYXJhbXNcbiAqXG4gKiBAbWV0aG9kIGRlY29kZVBhcmFtXG4gKiBAcGFyYW0ge0FycmF5fSB0eXBlc1xuICogQHBhcmFtIHtTdHJpbmd9IGJ5dGVzXG4gKiBAcmV0dXJuIHtBcnJheX0gYXJyYXkgb2YgcGxhaW4gcGFyYW1zXG4gKi9cblNvbGlkaXR5Q29kZXIucHJvdG90eXBlLmRlY29kZVBhcmFtcyA9IGZ1bmN0aW9uICh0eXBlcywgYnl0ZXMpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgcmV0dXJuIHR5cGVzLm1hcChmdW5jdGlvbiAodHlwZSwgaW5kZXgpIHtcbiAgICAgICAgdmFyIHNvbGlkaXR5VHlwZSA9IHNlbGYuX3JlcXVpcmVUeXBlKHR5cGUpO1xuICAgICAgICB2YXIgcCA9IHNvbGlkaXR5VHlwZS5zbGljZVBhcmFtKGJ5dGVzLCBpbmRleCwgdHlwZSk7XG4gICAgICAgIHJldHVybiBzb2xpZGl0eVR5cGUuZm9ybWF0T3V0cHV0KHAsIGlzQXJyYXlUeXBlKHR5cGUpKTtcbiAgICB9KTtcbn07XG5cbnZhciBjb2RlciA9IG5ldyBTb2xpZGl0eUNvZGVyKFtcbiAgICBuZXcgU29saWRpdHlUeXBlKHtcbiAgICAgICAgbmFtZTogJ2FkZHJlc3MnLFxuICAgICAgICBtYXRjaDogJ3N0cmljdCcsXG4gICAgICAgIG1vZGU6ICd2YWx1ZScsXG4gICAgICAgIGlucHV0Rm9ybWF0dGVyOiBmLmZvcm1hdElucHV0SW50LFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IGYuZm9ybWF0T3V0cHV0QWRkcmVzc1xuICAgIH0pLFxuICAgIG5ldyBTb2xpZGl0eVR5cGUoe1xuICAgICAgICBuYW1lOiAnYm9vbCcsXG4gICAgICAgIG1hdGNoOiAnc3RyaWN0JyxcbiAgICAgICAgbW9kZTogJ3ZhbHVlJyxcbiAgICAgICAgaW5wdXRGb3JtYXR0ZXI6IGYuZm9ybWF0SW5wdXRCb29sLFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IGYuZm9ybWF0T3V0cHV0Qm9vbFxuICAgIH0pLFxuICAgIG5ldyBTb2xpZGl0eVR5cGUoe1xuICAgICAgICBuYW1lOiAnaW50JyxcbiAgICAgICAgbWF0Y2g6ICdwcmVmaXgnLFxuICAgICAgICBtb2RlOiAndmFsdWUnLFxuICAgICAgICBpbnB1dEZvcm1hdHRlcjogZi5mb3JtYXRJbnB1dEludCxcbiAgICAgICAgb3V0cHV0Rm9ybWF0dGVyOiBmLmZvcm1hdE91dHB1dEludCxcbiAgICB9KSxcbiAgICBuZXcgU29saWRpdHlUeXBlKHtcbiAgICAgICAgbmFtZTogJ3VpbnQnLFxuICAgICAgICBtYXRjaDogJ3ByZWZpeCcsXG4gICAgICAgIG1vZGU6ICd2YWx1ZScsXG4gICAgICAgIGlucHV0Rm9ybWF0dGVyOiBmLmZvcm1hdElucHV0SW50LFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IGYuZm9ybWF0T3V0cHV0VUludFxuICAgIH0pLFxuICAgIG5ldyBTb2xpZGl0eVR5cGUoe1xuICAgICAgICBuYW1lOiAnYnl0ZXMnLFxuICAgICAgICBtYXRjaDogJ3N0cmljdCcsXG4gICAgICAgIG1vZGU6ICdieXRlcycsXG4gICAgICAgIGlucHV0Rm9ybWF0dGVyOiBmLmZvcm1hdElucHV0RHluYW1pY0J5dGVzLFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IGYuZm9ybWF0T3V0cHV0RHluYW1pY0J5dGVzXG4gICAgfSksXG4gICAgbmV3IFNvbGlkaXR5VHlwZSh7XG4gICAgICAgIG5hbWU6ICdieXRlcycsXG4gICAgICAgIG1hdGNoOiAncHJlZml4JyxcbiAgICAgICAgbW9kZTogJ3ZhbHVlJyxcbiAgICAgICAgaW5wdXRGb3JtYXR0ZXI6IGYuZm9ybWF0SW5wdXRCeXRlcyxcbiAgICAgICAgb3V0cHV0Rm9ybWF0dGVyOiBmLmZvcm1hdE91dHB1dEJ5dGVzXG4gICAgfSksXG4gICAgbmV3IFNvbGlkaXR5VHlwZSh7XG4gICAgICAgIG5hbWU6ICdyZWFsJyxcbiAgICAgICAgbWF0Y2g6ICdwcmVmaXgnLFxuICAgICAgICBtb2RlOiAndmFsdWUnLFxuICAgICAgICBpbnB1dEZvcm1hdHRlcjogZi5mb3JtYXRJbnB1dFJlYWwsXG4gICAgICAgIG91dHB1dEZvcm1hdHRlcjogZi5mb3JtYXRPdXRwdXRSZWFsXG4gICAgfSksXG4gICAgbmV3IFNvbGlkaXR5VHlwZSh7XG4gICAgICAgIG5hbWU6ICd1cmVhbCcsXG4gICAgICAgIG1hdGNoOiAncHJlZml4JyxcbiAgICAgICAgbW9kZTogJ3ZhbHVlJyxcbiAgICAgICAgaW5wdXRGb3JtYXR0ZXI6IGYuZm9ybWF0SW5wdXRSZWFsLFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IGYuZm9ybWF0T3V0cHV0VVJlYWxcbiAgICB9KVxuXSk7XG5cbm1vZHVsZS5leHBvcnRzID0gY29kZXI7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIFxuICogQGZpbGUgZm9ybWF0dGVycy5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgQmlnTnVtYmVyID0gcmVxdWlyZSgnYmlnbnVtYmVyLmpzJyk7XG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xudmFyIGMgPSByZXF1aXJlKCcuLi91dGlscy9jb25maWcnKTtcbnZhciBTb2xpZGl0eVBhcmFtID0gcmVxdWlyZSgnLi9wYXJhbScpO1xuXG5cbi8qKlxuICogRm9ybWF0cyBpbnB1dCB2YWx1ZSB0byBieXRlIHJlcHJlc2VudGF0aW9uIG9mIGludFxuICogSWYgdmFsdWUgaXMgbmVnYXRpdmUsIHJldHVybiBpdCdzIHR3bydzIGNvbXBsZW1lbnRcbiAqIElmIHRoZSB2YWx1ZSBpcyBmbG9hdGluZyBwb2ludCwgcm91bmQgaXQgZG93blxuICpcbiAqIEBtZXRob2QgZm9ybWF0SW5wdXRJbnRcbiAqIEBwYXJhbSB7U3RyaW5nfE51bWJlcnxCaWdOdW1iZXJ9IHZhbHVlIHRoYXQgbmVlZHMgdG8gYmUgZm9ybWF0dGVkXG4gKiBAcmV0dXJucyB7U29saWRpdHlQYXJhbX1cbiAqL1xudmFyIGZvcm1hdElucHV0SW50ID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgdmFyIHBhZGRpbmcgPSBjLkVUSF9QQURESU5HICogMjtcbiAgICBCaWdOdW1iZXIuY29uZmlnKGMuRVRIX0JJR05VTUJFUl9ST1VORElOR19NT0RFKTtcbiAgICB2YXIgcmVzdWx0ID0gdXRpbHMucGFkTGVmdCh1dGlscy50b1R3b3NDb21wbGVtZW50KHZhbHVlKS5yb3VuZCgpLnRvU3RyaW5nKDE2KSwgcGFkZGluZyk7XG4gICAgcmV0dXJuIG5ldyBTb2xpZGl0eVBhcmFtKHJlc3VsdCk7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgaW5wdXQgdmFsdWUgdG8gYnl0ZSByZXByZXNlbnRhdGlvbiBvZiBzdHJpbmdcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdElucHV0Qnl0ZXNcbiAqIEBwYXJhbSB7U3RyaW5nfVxuICogQHJldHVybnMge1NvbGlkaXR5UGFyYW19XG4gKi9cbnZhciBmb3JtYXRJbnB1dEJ5dGVzID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgdmFyIHJlc3VsdCA9IHV0aWxzLmZyb21Bc2NpaSh2YWx1ZSwgYy5FVEhfUEFERElORykuc3Vic3RyKDIpO1xuICAgIHJldHVybiBuZXcgU29saWRpdHlQYXJhbShyZXN1bHQpO1xufTtcblxuLyoqXG4gKiBGb3JtYXRzIGlucHV0IHZhbHVlIHRvIGJ5dGUgcmVwcmVzZW50YXRpb24gb2Ygc3RyaW5nXG4gKlxuICogQG1ldGhvZCBmb3JtYXRJbnB1dER5bmFtaWNCeXRlc1xuICogQHBhcmFtIHtTdHJpbmd9XG4gKiBAcmV0dXJucyB7U29saWRpdHlQYXJhbX1cbiAqL1xudmFyIGZvcm1hdElucHV0RHluYW1pY0J5dGVzID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgdmFyIHJlc3VsdCA9IHV0aWxzLmZyb21Bc2NpaSh2YWx1ZSwgYy5FVEhfUEFERElORykuc3Vic3RyKDIpO1xuICAgIHJldHVybiBuZXcgU29saWRpdHlQYXJhbShmb3JtYXRJbnB1dEludCh2YWx1ZS5sZW5ndGgpLnZhbHVlICsgcmVzdWx0LCAzMik7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgaW5wdXQgdmFsdWUgdG8gYnl0ZSByZXByZXNlbnRhdGlvbiBvZiBib29sXG4gKlxuICogQG1ldGhvZCBmb3JtYXRJbnB1dEJvb2xcbiAqIEBwYXJhbSB7Qm9vbGVhbn1cbiAqIEByZXR1cm5zIHtTb2xpZGl0eVBhcmFtfVxuICovXG52YXIgZm9ybWF0SW5wdXRCb29sID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgdmFyIHJlc3VsdCA9ICcwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAnICsgKHZhbHVlID8gICcxJyA6ICcwJyk7XG4gICAgcmV0dXJuIG5ldyBTb2xpZGl0eVBhcmFtKHJlc3VsdCk7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgaW5wdXQgdmFsdWUgdG8gYnl0ZSByZXByZXNlbnRhdGlvbiBvZiByZWFsXG4gKiBWYWx1ZXMgYXJlIG11bHRpcGxpZWQgYnkgMl5tIGFuZCBlbmNvZGVkIGFzIGludGVnZXJzXG4gKlxuICogQG1ldGhvZCBmb3JtYXRJbnB1dFJlYWxcbiAqIEBwYXJhbSB7U3RyaW5nfE51bWJlcnxCaWdOdW1iZXJ9XG4gKiBAcmV0dXJucyB7U29saWRpdHlQYXJhbX1cbiAqL1xudmFyIGZvcm1hdElucHV0UmVhbCA9IGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgIHJldHVybiBmb3JtYXRJbnB1dEludChuZXcgQmlnTnVtYmVyKHZhbHVlKS50aW1lcyhuZXcgQmlnTnVtYmVyKDIpLnBvdygxMjgpKSk7XG59O1xuXG4vKipcbiAqIENoZWNrIGlmIGlucHV0IHZhbHVlIGlzIG5lZ2F0aXZlXG4gKlxuICogQG1ldGhvZCBzaWduZWRJc05lZ2F0aXZlXG4gKiBAcGFyYW0ge1N0cmluZ30gdmFsdWUgaXMgaGV4IGZvcm1hdFxuICogQHJldHVybnMge0Jvb2xlYW59IHRydWUgaWYgaXQgaXMgbmVnYXRpdmUsIG90aGVyd2lzZSBmYWxzZVxuICovXG52YXIgc2lnbmVkSXNOZWdhdGl2ZSA9IGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgIHJldHVybiAobmV3IEJpZ051bWJlcih2YWx1ZS5zdWJzdHIoMCwgMSksIDE2KS50b1N0cmluZygyKS5zdWJzdHIoMCwgMSkpID09PSAnMSc7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgcmlnaHQtYWxpZ25lZCBvdXRwdXQgYnl0ZXMgdG8gaW50XG4gKlxuICogQG1ldGhvZCBmb3JtYXRPdXRwdXRJbnRcbiAqIEBwYXJhbSB7U29saWRpdHlQYXJhbX0gcGFyYW1cbiAqIEByZXR1cm5zIHtCaWdOdW1iZXJ9IHJpZ2h0LWFsaWduZWQgb3V0cHV0IGJ5dGVzIGZvcm1hdHRlZCB0byBiaWcgbnVtYmVyXG4gKi9cbnZhciBmb3JtYXRPdXRwdXRJbnQgPSBmdW5jdGlvbiAocGFyYW0pIHtcbiAgICB2YXIgdmFsdWUgPSBwYXJhbS5zdGF0aWNQYXJ0KCkgfHwgXCIwXCI7XG5cbiAgICAvLyBjaGVjayBpZiBpdCdzIG5lZ2F0aXZlIG51bWJlclxuICAgIC8vIGl0IGl0IGlzLCByZXR1cm4gdHdvJ3MgY29tcGxlbWVudFxuICAgIGlmIChzaWduZWRJc05lZ2F0aXZlKHZhbHVlKSkge1xuICAgICAgICByZXR1cm4gbmV3IEJpZ051bWJlcih2YWx1ZSwgMTYpLm1pbnVzKG5ldyBCaWdOdW1iZXIoJ2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYnLCAxNikpLm1pbnVzKDEpO1xuICAgIH1cbiAgICByZXR1cm4gbmV3IEJpZ051bWJlcih2YWx1ZSwgMTYpO1xufTtcblxuLyoqXG4gKiBGb3JtYXRzIHJpZ2h0LWFsaWduZWQgb3V0cHV0IGJ5dGVzIHRvIHVpbnRcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdE91dHB1dFVJbnRcbiAqIEBwYXJhbSB7U29saWRpdHlQYXJhbX1cbiAqIEByZXR1cm5zIHtCaWdOdW1lYmVyfSByaWdodC1hbGlnbmVkIG91dHB1dCBieXRlcyBmb3JtYXR0ZWQgdG8gdWludFxuICovXG52YXIgZm9ybWF0T3V0cHV0VUludCA9IGZ1bmN0aW9uIChwYXJhbSkge1xuICAgIHZhciB2YWx1ZSA9IHBhcmFtLnN0YXRpY1BhcnQoKSB8fCBcIjBcIjtcbiAgICByZXR1cm4gbmV3IEJpZ051bWJlcih2YWx1ZSwgMTYpO1xufTtcblxuLyoqXG4gKiBGb3JtYXRzIHJpZ2h0LWFsaWduZWQgb3V0cHV0IGJ5dGVzIHRvIHJlYWxcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdE91dHB1dFJlYWxcbiAqIEBwYXJhbSB7U29saWRpdHlQYXJhbX1cbiAqIEByZXR1cm5zIHtCaWdOdW1iZXJ9IGlucHV0IGJ5dGVzIGZvcm1hdHRlZCB0byByZWFsXG4gKi9cbnZhciBmb3JtYXRPdXRwdXRSZWFsID0gZnVuY3Rpb24gKHBhcmFtKSB7XG4gICAgcmV0dXJuIGZvcm1hdE91dHB1dEludChwYXJhbSkuZGl2aWRlZEJ5KG5ldyBCaWdOdW1iZXIoMikucG93KDEyOCkpOyBcbn07XG5cbi8qKlxuICogRm9ybWF0cyByaWdodC1hbGlnbmVkIG91dHB1dCBieXRlcyB0byB1cmVhbFxuICpcbiAqIEBtZXRob2QgZm9ybWF0T3V0cHV0VVJlYWxcbiAqIEBwYXJhbSB7U29saWRpdHlQYXJhbX1cbiAqIEByZXR1cm5zIHtCaWdOdW1iZXJ9IGlucHV0IGJ5dGVzIGZvcm1hdHRlZCB0byB1cmVhbFxuICovXG52YXIgZm9ybWF0T3V0cHV0VVJlYWwgPSBmdW5jdGlvbiAocGFyYW0pIHtcbiAgICByZXR1cm4gZm9ybWF0T3V0cHV0VUludChwYXJhbSkuZGl2aWRlZEJ5KG5ldyBCaWdOdW1iZXIoMikucG93KDEyOCkpOyBcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZm9ybWF0IG91dHB1dCBib29sXG4gKlxuICogQG1ldGhvZCBmb3JtYXRPdXRwdXRCb29sXG4gKiBAcGFyYW0ge1NvbGlkaXR5UGFyYW19XG4gKiBAcmV0dXJucyB7Qm9vbGVhbn0gcmlnaHQtYWxpZ25lZCBpbnB1dCBieXRlcyBmb3JtYXR0ZWQgdG8gYm9vbFxuICovXG52YXIgZm9ybWF0T3V0cHV0Qm9vbCA9IGZ1bmN0aW9uIChwYXJhbSkge1xuICAgIHJldHVybiBwYXJhbS5zdGF0aWNQYXJ0KCkgPT09ICcwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxJyA/IHRydWUgOiBmYWxzZTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZm9ybWF0IG91dHB1dCBzdHJpbmdcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdE91dHB1dEJ5dGVzXG4gKiBAcGFyYW0ge1NvbGlkaXR5UGFyYW19IGxlZnQtYWxpZ25lZCBoZXggcmVwcmVzZW50YXRpb24gb2Ygc3RyaW5nXG4gKiBAcmV0dXJucyB7U3RyaW5nfSBhc2NpaSBzdHJpbmdcbiAqL1xudmFyIGZvcm1hdE91dHB1dEJ5dGVzID0gZnVuY3Rpb24gKHBhcmFtKSB7XG4gICAgLy8gbGVuZ3RoIG1pZ2h0IGFsc28gYmUgaW1wb3J0YW50IVxuICAgIHJldHVybiB1dGlscy50b0FzY2lpKHBhcmFtLnN0YXRpY1BhcnQoKSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGZvcm1hdCBvdXRwdXQgc3RyaW5nXG4gKlxuICogQG1ldGhvZCBmb3JtYXRPdXRwdXREeW5hbWljQnl0ZXNcbiAqIEBwYXJhbSB7U29saWRpdHlQYXJhbX0gbGVmdC1hbGlnbmVkIGhleCByZXByZXNlbnRhdGlvbiBvZiBzdHJpbmdcbiAqIEByZXR1cm5zIHtTdHJpbmd9IGFzY2lpIHN0cmluZ1xuICovXG52YXIgZm9ybWF0T3V0cHV0RHluYW1pY0J5dGVzID0gZnVuY3Rpb24gKHBhcmFtKSB7XG4gICAgLy8gbGVuZ3RoIG1pZ2h0IGFsc28gYmUgaW1wb3J0YW50IVxuICAgIHJldHVybiB1dGlscy50b0FzY2lpKHBhcmFtLmR5bmFtaWNQYXJ0KCkuc2xpY2UoNjQpKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZm9ybWF0IG91dHB1dCBhZGRyZXNzXG4gKlxuICogQG1ldGhvZCBmb3JtYXRPdXRwdXRBZGRyZXNzXG4gKiBAcGFyYW0ge1NvbGlkaXR5UGFyYW19IHJpZ2h0LWFsaWduZWQgaW5wdXQgYnl0ZXNcbiAqIEByZXR1cm5zIHtTdHJpbmd9IGFkZHJlc3NcbiAqL1xudmFyIGZvcm1hdE91dHB1dEFkZHJlc3MgPSBmdW5jdGlvbiAocGFyYW0pIHtcbiAgICB2YXIgdmFsdWUgPSBwYXJhbS5zdGF0aWNQYXJ0KCk7XG4gICAgcmV0dXJuIFwiMHhcIiArIHZhbHVlLnNsaWNlKHZhbHVlLmxlbmd0aCAtIDQwLCB2YWx1ZS5sZW5ndGgpO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgZm9ybWF0SW5wdXRJbnQ6IGZvcm1hdElucHV0SW50LFxuICAgIGZvcm1hdElucHV0Qnl0ZXM6IGZvcm1hdElucHV0Qnl0ZXMsXG4gICAgZm9ybWF0SW5wdXREeW5hbWljQnl0ZXM6IGZvcm1hdElucHV0RHluYW1pY0J5dGVzLFxuICAgIGZvcm1hdElucHV0Qm9vbDogZm9ybWF0SW5wdXRCb29sLFxuICAgIGZvcm1hdElucHV0UmVhbDogZm9ybWF0SW5wdXRSZWFsLFxuICAgIGZvcm1hdE91dHB1dEludDogZm9ybWF0T3V0cHV0SW50LFxuICAgIGZvcm1hdE91dHB1dFVJbnQ6IGZvcm1hdE91dHB1dFVJbnQsXG4gICAgZm9ybWF0T3V0cHV0UmVhbDogZm9ybWF0T3V0cHV0UmVhbCxcbiAgICBmb3JtYXRPdXRwdXRVUmVhbDogZm9ybWF0T3V0cHV0VVJlYWwsXG4gICAgZm9ybWF0T3V0cHV0Qm9vbDogZm9ybWF0T3V0cHV0Qm9vbCxcbiAgICBmb3JtYXRPdXRwdXRCeXRlczogZm9ybWF0T3V0cHV0Qnl0ZXMsXG4gICAgZm9ybWF0T3V0cHV0RHluYW1pY0J5dGVzOiBmb3JtYXRPdXRwdXREeW5hbWljQnl0ZXMsXG4gICAgZm9ybWF0T3V0cHV0QWRkcmVzczogZm9ybWF0T3V0cHV0QWRkcmVzc1xufTtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSBwYXJhbS5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xuXG4vKipcbiAqIFNvbGlkaXR5UGFyYW0gb2JqZWN0IHByb3RvdHlwZS5cbiAqIFNob3VsZCBiZSB1c2VkIHdoZW4gZW5jb2RpbmcsIGRlY29kaW5nIHNvbGlkaXR5IGJ5dGVzXG4gKi9cbnZhciBTb2xpZGl0eVBhcmFtID0gZnVuY3Rpb24gKHZhbHVlLCBvZmZzZXQpIHtcbiAgICB0aGlzLnZhbHVlID0gdmFsdWUgfHwgJyc7XG4gICAgdGhpcy5vZmZzZXQgPSBvZmZzZXQ7IC8vIG9mZnNldCBpbiBieXRlc1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgdXNlZCB0byBnZXQgbGVuZ3RoIG9mIHBhcmFtcydzIGR5bmFtaWMgcGFydFxuICogXG4gKiBAbWV0aG9kIGR5bmFtaWNQYXJ0TGVuZ3RoXG4gKiBAcmV0dXJucyB7TnVtYmVyfSBsZW5ndGggb2YgZHluYW1pYyBwYXJ0IChpbiBieXRlcylcbiAqL1xuU29saWRpdHlQYXJhbS5wcm90b3R5cGUuZHluYW1pY1BhcnRMZW5ndGggPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuZHluYW1pY1BhcnQoKS5sZW5ndGggLyAyO1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgdXNlZCB0byBjcmVhdGUgY29weSBvZiBzb2xpZGl0eSBwYXJhbSB3aXRoIGRpZmZlcmVudCBvZmZzZXRcbiAqXG4gKiBAbWV0aG9kIHdpdGhPZmZzZXRcbiAqIEBwYXJhbSB7TnVtYmVyfSBvZmZzZXQgbGVuZ3RoIGluIGJ5dGVzXG4gKiBAcmV0dXJucyB7U29saWRpdHlQYXJhbX0gbmV3IHNvbGlkaXR5IHBhcmFtIHdpdGggYXBwbGllZCBvZmZzZXRcbiAqL1xuU29saWRpdHlQYXJhbS5wcm90b3R5cGUud2l0aE9mZnNldCA9IGZ1bmN0aW9uIChvZmZzZXQpIHtcbiAgICByZXR1cm4gbmV3IFNvbGlkaXR5UGFyYW0odGhpcy52YWx1ZSwgb2Zmc2V0KTtcbn07XG5cbi8qKlxuICogVGhpcyBtZXRob2Qgc2hvdWxkIGJlIHVzZWQgdG8gY29tYmluZSBzb2xpZGl0eSBwYXJhbXMgdG9nZXRoZXJcbiAqIGVnLiB3aGVuIGFwcGVuZGluZyBhbiBhcnJheVxuICpcbiAqIEBtZXRob2QgY29tYmluZVxuICogQHBhcmFtIHtTb2xpZGl0eVBhcmFtfSBwYXJhbSB3aXRoIHdoaWNoIHdlIHNob3VsZCBjb21iaW5lXG4gKiBAcGFyYW0ge1NvbGlkaXR5UGFyYW19IHJlc3VsdCBvZiBjb21iaW5hdGlvblxuICovXG5Tb2xpZGl0eVBhcmFtLnByb3RvdHlwZS5jb21iaW5lID0gZnVuY3Rpb24gKHBhcmFtKSB7XG4gICAgcmV0dXJuIG5ldyBTb2xpZGl0eVBhcmFtKHRoaXMudmFsdWUgKyBwYXJhbS52YWx1ZSk7IFxufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgY2FsbGVkIHRvIGNoZWNrIGlmIHBhcmFtIGhhcyBkeW5hbWljIHNpemUuXG4gKiBJZiBpdCBoYXMsIGl0IHJldHVybnMgdHJ1ZSwgb3RoZXJ3aXNlIGZhbHNlXG4gKlxuICogQG1ldGhvZCBpc0R5bmFtaWNcbiAqIEByZXR1cm5zIHtCb29sZWFufVxuICovXG5Tb2xpZGl0eVBhcmFtLnByb3RvdHlwZS5pc0R5bmFtaWMgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMudmFsdWUubGVuZ3RoID4gNjQgfHwgdGhpcy5vZmZzZXQgIT09IHVuZGVmaW5lZDtcbn07XG5cbi8qKlxuICogVGhpcyBtZXRob2Qgc2hvdWxkIGJlIGNhbGxlZCB0byB0cmFuc2Zvcm0gb2Zmc2V0IHRvIGJ5dGVzXG4gKlxuICogQG1ldGhvZCBvZmZzZXRBc0J5dGVzXG4gKiBAcmV0dXJucyB7U3RyaW5nfSBieXRlcyByZXByZXNlbnRhdGlvbiBvZiBvZmZzZXRcbiAqL1xuU29saWRpdHlQYXJhbS5wcm90b3R5cGUub2Zmc2V0QXNCeXRlcyA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gIXRoaXMuaXNEeW5hbWljKCkgPyAnJyA6IHV0aWxzLnBhZExlZnQodXRpbHMudG9Ud29zQ29tcGxlbWVudCh0aGlzLm9mZnNldCkudG9TdHJpbmcoMTYpLCA2NCk7XG59O1xuXG4vKipcbiAqIFRoaXMgbWV0aG9kIHNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IHN0YXRpYyBwYXJ0IG9mIHBhcmFtXG4gKlxuICogQG1ldGhvZCBzdGF0aWNQYXJ0XG4gKiBAcmV0dXJucyB7U3RyaW5nfSBvZmZzZXQgaWYgaXQgaXMgYSBkeW5hbWljIHBhcmFtLCBvdGhlcndpc2UgdmFsdWVcbiAqL1xuU29saWRpdHlQYXJhbS5wcm90b3R5cGUuc3RhdGljUGFydCA9IGZ1bmN0aW9uICgpIHtcbiAgICBpZiAoIXRoaXMuaXNEeW5hbWljKCkpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMudmFsdWU7IFxuICAgIH0gXG4gICAgcmV0dXJuIHRoaXMub2Zmc2V0QXNCeXRlcygpO1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgY2FsbGVkIHRvIGdldCBkeW5hbWljIHBhcnQgb2YgcGFyYW1cbiAqXG4gKiBAbWV0aG9kIGR5bmFtaWNQYXJ0XG4gKiBAcmV0dXJucyB7U3RyaW5nfSByZXR1cm5zIGEgdmFsdWUgaWYgaXQgaXMgYSBkeW5hbWljIHBhcmFtLCBvdGhlcndpc2UgZW1wdHkgc3RyaW5nXG4gKi9cblNvbGlkaXR5UGFyYW0ucHJvdG90eXBlLmR5bmFtaWNQYXJ0ID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB0aGlzLmlzRHluYW1pYygpID8gdGhpcy52YWx1ZSA6ICcnO1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgY2FsbGVkIHRvIGVuY29kZSBwYXJhbVxuICpcbiAqIEBtZXRob2QgZW5jb2RlXG4gKiBAcmV0dXJucyB7U3RyaW5nfVxuICovXG5Tb2xpZGl0eVBhcmFtLnByb3RvdHlwZS5lbmNvZGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuc3RhdGljUGFydCgpICsgdGhpcy5keW5hbWljUGFydCgpO1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgY2FsbGVkIHRvIGVuY29kZSBhcnJheSBvZiBwYXJhbXNcbiAqXG4gKiBAbWV0aG9kIGVuY29kZUxpc3RcbiAqIEBwYXJhbSB7QXJyYXlbU29saWRpdHlQYXJhbV19IHBhcmFtc1xuICogQHJldHVybnMge1N0cmluZ31cbiAqL1xuU29saWRpdHlQYXJhbS5lbmNvZGVMaXN0ID0gZnVuY3Rpb24gKHBhcmFtcykge1xuICAgIFxuICAgIC8vIHVwZGF0aW5nIG9mZnNldHNcbiAgICB2YXIgdG90YWxPZmZzZXQgPSBwYXJhbXMubGVuZ3RoICogMzI7XG4gICAgdmFyIG9mZnNldFBhcmFtcyA9IHBhcmFtcy5tYXAoZnVuY3Rpb24gKHBhcmFtKSB7XG4gICAgICAgIGlmICghcGFyYW0uaXNEeW5hbWljKCkpIHtcbiAgICAgICAgICAgIHJldHVybiBwYXJhbTtcbiAgICAgICAgfVxuICAgICAgICB2YXIgb2Zmc2V0ID0gdG90YWxPZmZzZXQ7XG4gICAgICAgIHRvdGFsT2Zmc2V0ICs9IHBhcmFtLmR5bmFtaWNQYXJ0TGVuZ3RoKCk7XG4gICAgICAgIHJldHVybiBwYXJhbS53aXRoT2Zmc2V0KG9mZnNldCk7XG4gICAgfSk7XG5cbiAgICAvLyBlbmNvZGUgZXZlcnl0aGluZyFcbiAgICByZXR1cm4gb2Zmc2V0UGFyYW1zLnJlZHVjZShmdW5jdGlvbiAocmVzdWx0LCBwYXJhbSkge1xuICAgICAgICByZXR1cm4gcmVzdWx0ICsgcGFyYW0uZHluYW1pY1BhcnQoKTtcbiAgICB9LCBvZmZzZXRQYXJhbXMucmVkdWNlKGZ1bmN0aW9uIChyZXN1bHQsIHBhcmFtKSB7XG4gICAgICAgIHJldHVybiByZXN1bHQgKyBwYXJhbS5zdGF0aWNQYXJ0KCk7XG4gICAgfSwgJycpKTtcbn07XG5cbi8qKlxuICogVGhpcyBtZXRob2Qgc2hvdWxkIGJlIHVzZWQgdG8gZGVjb2RlIHBsYWluIChzdGF0aWMpIHNvbGlkaXR5IHBhcmFtIGF0IGdpdmVuIGluZGV4XG4gKlxuICogQG1ldGhvZCBkZWNvZGVQYXJhbVxuICogQHBhcmFtIHtTdHJpbmd9IGJ5dGVzXG4gKiBAcGFyYW0ge051bWJlcn0gaW5kZXhcbiAqIEByZXR1cm5zIHtTb2xpZGl0eVBhcmFtfVxuICovXG5Tb2xpZGl0eVBhcmFtLmRlY29kZVBhcmFtID0gZnVuY3Rpb24gKGJ5dGVzLCBpbmRleCkge1xuICAgIGluZGV4ID0gaW5kZXggfHwgMDtcbiAgICByZXR1cm4gbmV3IFNvbGlkaXR5UGFyYW0oYnl0ZXMuc3Vic3RyKGluZGV4ICogNjQsIDY0KSk7IFxufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgY2FsbGVkIHRvIGdldCBvZmZzZXQgdmFsdWUgZnJvbSBieXRlcyBhdCBnaXZlbiBpbmRleFxuICpcbiAqIEBtZXRob2QgZ2V0T2Zmc2V0XG4gKiBAcGFyYW0ge1N0cmluZ30gYnl0ZXNcbiAqIEBwYXJhbSB7TnVtYmVyfSBpbmRleFxuICogQHJldHVybnMge051bWJlcn0gb2Zmc2V0IGFzIG51bWJlclxuICovXG52YXIgZ2V0T2Zmc2V0ID0gZnVuY3Rpb24gKGJ5dGVzLCBpbmRleCkge1xuICAgIC8vIHdlIGNhbiBkbyB0aGlzIGNhdXNlIG9mZnNldCBpcyByYXRoZXIgc21hbGxcbiAgICByZXR1cm4gcGFyc2VJbnQoJzB4JyArIGJ5dGVzLnN1YnN0cihpbmRleCAqIDY0LCA2NCkpO1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgY2FsbGVkIHRvIGRlY29kZSBzb2xpZGl0eSBieXRlcyBwYXJhbSBhdCBnaXZlbiBpbmRleFxuICpcbiAqIEBtZXRob2QgZGVjb2RlQnl0ZXNcbiAqIEBwYXJhbSB7U3RyaW5nfSBieXRlc1xuICogQHBhcmFtIHtOdW1iZXJ9IGluZGV4XG4gKiBAcmV0dXJucyB7U29saWRpdHlQYXJhbX1cbiAqL1xuU29saWRpdHlQYXJhbS5kZWNvZGVCeXRlcyA9IGZ1bmN0aW9uIChieXRlcywgaW5kZXgpIHtcbiAgICBpbmRleCA9IGluZGV4IHx8IDA7XG4gICAgLy9UT0RPIGFkZCBzdXBwb3J0IGZvciBzdHJpbmdzIGxvbmdlciB0aGFuIDMyIGJ5dGVzXG4gICAgLy92YXIgbGVuZ3RoID0gcGFyc2VJbnQoJzB4JyArIGJ5dGVzLnN1YnN0cihvZmZzZXQgKiA2NCwgNjQpKTtcblxuICAgIHZhciBvZmZzZXQgPSBnZXRPZmZzZXQoYnl0ZXMsIGluZGV4KTtcblxuICAgIC8vIDIgKiAsIGNhdXNlIHdlIGFsc28gcGFyc2UgbGVuZ3RoXG4gICAgcmV0dXJuIG5ldyBTb2xpZGl0eVBhcmFtKGJ5dGVzLnN1YnN0cihvZmZzZXQgKiAyLCAyICogNjQpLCAwKTtcbn07XG5cbi8qKlxuICogVGhpcyBtZXRob2Qgc2hvdWxkIGJlIHVzZWQgdG8gZGVjb2RlIHNvbGlkaXR5IGFycmF5IGF0IGdpdmVuIGluZGV4XG4gKlxuICogQG1ldGhvZCBkZWNvZGVBcnJheVxuICogQHBhcmFtIHtTdHJpbmd9IGJ5dGVzXG4gKiBAcGFyYW0ge051bWJlcn0gaW5kZXhcbiAqIEByZXR1cm5zIHtTb2xpZGl0eVBhcmFtfVxuICovXG5Tb2xpZGl0eVBhcmFtLmRlY29kZUFycmF5ID0gZnVuY3Rpb24gKGJ5dGVzLCBpbmRleCkge1xuICAgIGluZGV4ID0gaW5kZXggfHwgMDtcbiAgICB2YXIgb2Zmc2V0ID0gZ2V0T2Zmc2V0KGJ5dGVzLCBpbmRleCk7XG4gICAgdmFyIGxlbmd0aCA9IHBhcnNlSW50KCcweCcgKyBieXRlcy5zdWJzdHIob2Zmc2V0ICogMiwgNjQpKTtcbiAgICByZXR1cm4gbmV3IFNvbGlkaXR5UGFyYW0oYnl0ZXMuc3Vic3RyKG9mZnNldCAqIDIsIChsZW5ndGggKyAxKSAqIDY0KSwgMCk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFNvbGlkaXR5UGFyYW07XG5cbiIsIid1c2Ugc3RyaWN0JztcblxuLy8gZ28gZW52IGRvZXNuJ3QgaGF2ZSBhbmQgbmVlZCBYTUxIdHRwUmVxdWVzdFxuaWYgKHR5cGVvZiBYTUxIdHRwUmVxdWVzdCA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICBleHBvcnRzLlhNTEh0dHBSZXF1ZXN0ID0ge307XG59IGVsc2Uge1xuICAgIGV4cG9ydHMuWE1MSHR0cFJlcXVlc3QgPSBYTUxIdHRwUmVxdWVzdDsgLy8ganNoaW50IGlnbm9yZTpsaW5lXG59XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIEBmaWxlIGNvbmZpZy5qc1xuICogQGF1dGhvcnM6XG4gKiAgIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbi8qKlxuICogVXRpbHNcbiAqIFxuICogQG1vZHVsZSB1dGlsc1xuICovXG5cbi8qKlxuICogVXRpbGl0eSBmdW5jdGlvbnNcbiAqIFxuICogQGNsYXNzIFt1dGlsc10gY29uZmlnXG4gKiBAY29uc3RydWN0b3JcbiAqL1xuXG4vLy8gcmVxdWlyZWQgdG8gZGVmaW5lIEVUSF9CSUdOVU1CRVJfUk9VTkRJTkdfTU9ERVxudmFyIEJpZ051bWJlciA9IHJlcXVpcmUoJ2JpZ251bWJlci5qcycpO1xuXG52YXIgRVRIX1VOSVRTID0gW1xuICAgICd3ZWknLFxuICAgICdrd2VpJyxcbiAgICAnTXdlaScsXG4gICAgJ0d3ZWknLFxuICAgICdzemFibycsXG4gICAgJ2Zpbm5leScsXG4gICAgJ2ZlbXRvZXRoZXInLFxuICAgICdwaWNvZXRoZXInLFxuICAgICduYW5vZXRoZXInLFxuICAgICdtaWNyb2V0aGVyJyxcbiAgICAnbWlsbGlldGhlcicsXG4gICAgJ25hbm8nLFxuICAgICdtaWNybycsXG4gICAgJ21pbGxpJyxcbiAgICAnZXRoZXInLFxuICAgICdncmFuZCcsXG4gICAgJ01ldGhlcicsXG4gICAgJ0dldGhlcicsXG4gICAgJ1RldGhlcicsXG4gICAgJ1BldGhlcicsXG4gICAgJ0VldGhlcicsXG4gICAgJ1pldGhlcicsXG4gICAgJ1lldGhlcicsXG4gICAgJ05ldGhlcicsXG4gICAgJ0RldGhlcicsXG4gICAgJ1ZldGhlcicsXG4gICAgJ1VldGhlcidcbl07XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIEVUSF9QQURESU5HOiAzMixcbiAgICBFVEhfU0lHTkFUVVJFX0xFTkdUSDogNCxcbiAgICBFVEhfVU5JVFM6IEVUSF9VTklUUyxcbiAgICBFVEhfQklHTlVNQkVSX1JPVU5ESU5HX01PREU6IHsgUk9VTkRJTkdfTU9ERTogQmlnTnVtYmVyLlJPVU5EX0RPV04gfSxcbiAgICBFVEhfUE9MTElOR19USU1FT1VUOiAxMDAwLFxuICAgIGRlZmF1bHRCbG9jazogJ2xhdGVzdCcsXG4gICAgZGVmYXVsdEFjY291bnQ6IHVuZGVmaW5lZFxufTtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSBzaGEzLmpzXG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciB1dGlscyA9IHJlcXVpcmUoJy4vdXRpbHMnKTtcbnZhciBzaGEzID0gcmVxdWlyZSgnY3J5cHRvLWpzL3NoYTMnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoc3RyLCBpc05ldykge1xuICAgIGlmIChzdHIuc3Vic3RyKDAsIDIpID09PSAnMHgnICYmICFpc05ldykge1xuICAgICAgICBjb25zb2xlLndhcm4oJ3JlcXVpcmVtZW50IG9mIHVzaW5nIHdlYjMuZnJvbUFzY2lpIGJlZm9yZSBzaGEzIGlzIGRlcHJlY2F0ZWQnKTtcbiAgICAgICAgY29uc29sZS53YXJuKCduZXcgdXNhZ2U6IFxcJ3dlYjMuc2hhMyhcImhlbGxvXCIpXFwnJyk7XG4gICAgICAgIGNvbnNvbGUud2Fybignc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9ldGhlcmV1bS93ZWIzLmpzL3B1bGwvMjA1Jyk7XG4gICAgICAgIGNvbnNvbGUud2FybignaWYgeW91IG5lZWQgdG8gaGFzaCBoZXggdmFsdWUsIHlvdSBjYW4gZG8gXFwnc2hhMyhcIjB4ZmZmXCIsIHRydWUpXFwnJyk7XG4gICAgICAgIHN0ciA9IHV0aWxzLnRvQXNjaWkoc3RyKTtcbiAgICB9XG5cbiAgICByZXR1cm4gc2hhMyhzdHIsIHtcbiAgICAgICAgb3V0cHV0TGVuZ3RoOiAyNTZcbiAgICB9KS50b1N0cmluZygpO1xufTtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSB1dGlscy5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG4vKipcbiAqIFV0aWxzXG4gKiBcbiAqIEBtb2R1bGUgdXRpbHNcbiAqL1xuXG4vKipcbiAqIFV0aWxpdHkgZnVuY3Rpb25zXG4gKiBcbiAqIEBjbGFzcyBbdXRpbHNdIHV0aWxzXG4gKiBAY29uc3RydWN0b3JcbiAqL1xuXG52YXIgQmlnTnVtYmVyID0gcmVxdWlyZSgnYmlnbnVtYmVyLmpzJyk7XG5cbnZhciB1bml0TWFwID0ge1xuICAgICd3ZWknOiAgICAgICAgICAnMScsXG4gICAgJ2t3ZWknOiAgICAgICAgICcxMDAwJyxcbiAgICAnYWRhJzogICAgICAgICAgJzEwMDAnLFxuICAgICdmZW10b2V0aGVyJzogICAnMTAwMCcsXG4gICAgJ213ZWknOiAgICAgICAgICcxMDAwMDAwJyxcbiAgICAnYmFiYmFnZSc6ICAgICAgJzEwMDAwMDAnLFxuICAgICdwaWNvZXRoZXInOiAgICAnMTAwMDAwMCcsXG4gICAgJ2d3ZWknOiAgICAgICAgICcxMDAwMDAwMDAwJyxcbiAgICAnc2hhbm5vbic6ICAgICAgJzEwMDAwMDAwMDAnLFxuICAgICduYW5vZXRoZXInOiAgICAnMTAwMDAwMDAwMCcsXG4gICAgJ25hbm8nOiAgICAgICAgICcxMDAwMDAwMDAwJyxcbiAgICAnc3phYm8nOiAgICAgICAgJzEwMDAwMDAwMDAwMDAnLFxuICAgICdtaWNyb2V0aGVyJzogICAnMTAwMDAwMDAwMDAwMCcsXG4gICAgJ21pY3JvJzogICAgICAgICcxMDAwMDAwMDAwMDAwJyxcbiAgICAnZmlubmV5JzogICAgICAgJzEwMDAwMDAwMDAwMDAwMDAnLFxuICAgICdtaWxsaWV0aGVyJzogICAgJzEwMDAwMDAwMDAwMDAwMDAnLFxuICAgICdtaWxsaSc6ICAgICAgICAgJzEwMDAwMDAwMDAwMDAwMDAnLFxuICAgICdldGhlcic6ICAgICAgICAnMTAwMDAwMDAwMDAwMDAwMDAwMCcsXG4gICAgJ2tldGhlcic6ICAgICAgICcxMDAwMDAwMDAwMDAwMDAwMDAwMDAwJyxcbiAgICAnZ3JhbmQnOiAgICAgICAgJzEwMDAwMDAwMDAwMDAwMDAwMDAwMDAnLFxuICAgICdlaW5zdGVpbic6ICAgICAnMTAwMDAwMDAwMDAwMDAwMDAwMDAwMCcsXG4gICAgJ21ldGhlcic6ICAgICAgICcxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwJyxcbiAgICAnZ2V0aGVyJzogICAgICAgJzEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAnLFxuICAgICd0ZXRoZXInOiAgICAgICAnMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCdcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBwYWQgc3RyaW5nIHRvIGV4cGVjdGVkIGxlbmd0aFxuICpcbiAqIEBtZXRob2QgcGFkTGVmdFxuICogQHBhcmFtIHtTdHJpbmd9IHN0cmluZyB0byBiZSBwYWRkZWRcbiAqIEBwYXJhbSB7TnVtYmVyfSBjaGFyYWN0ZXJzIHRoYXQgcmVzdWx0IHN0cmluZyBzaG91bGQgaGF2ZVxuICogQHBhcmFtIHtTdHJpbmd9IHNpZ24sIGJ5IGRlZmF1bHQgMFxuICogQHJldHVybnMge1N0cmluZ30gcmlnaHQgYWxpZ25lZCBzdHJpbmdcbiAqL1xudmFyIHBhZExlZnQgPSBmdW5jdGlvbiAoc3RyaW5nLCBjaGFycywgc2lnbikge1xuICAgIHJldHVybiBuZXcgQXJyYXkoY2hhcnMgLSBzdHJpbmcubGVuZ3RoICsgMSkuam9pbihzaWduID8gc2lnbiA6IFwiMFwiKSArIHN0cmluZztcbn07XG5cbi8qKiBcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IHN0aW5nIGZyb20gaXQncyBoZXggcmVwcmVzZW50YXRpb25cbiAqXG4gKiBAbWV0aG9kIHRvQXNjaWlcbiAqIEBwYXJhbSB7U3RyaW5nfSBzdHJpbmcgaW4gaGV4XG4gKiBAcmV0dXJucyB7U3RyaW5nfSBhc2NpaSBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgaGV4IHZhbHVlXG4gKi9cbnZhciB0b0FzY2lpID0gZnVuY3Rpb24oaGV4KSB7XG4vLyBGaW5kIHRlcm1pbmF0aW9uXG4gICAgdmFyIHN0ciA9IFwiXCI7XG4gICAgdmFyIGkgPSAwLCBsID0gaGV4Lmxlbmd0aDtcbiAgICBpZiAoaGV4LnN1YnN0cmluZygwLCAyKSA9PT0gJzB4Jykge1xuICAgICAgICBpID0gMjtcbiAgICB9XG4gICAgZm9yICg7IGkgPCBsOyBpKz0yKSB7XG4gICAgICAgIHZhciBjb2RlID0gcGFyc2VJbnQoaGV4LnN1YnN0cihpLCAyKSwgMTYpO1xuICAgICAgICBpZiAoY29kZSA9PT0gMCkge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cblxuICAgICAgICBzdHIgKz0gU3RyaW5nLmZyb21DaGFyQ29kZShjb2RlKTtcbiAgICB9XG5cbiAgICByZXR1cm4gc3RyO1xufTtcbiAgICBcbi8qKlxuICogU2hvbGQgYmUgY2FsbGVkIHRvIGdldCBoZXggcmVwcmVzZW50YXRpb24gKHByZWZpeGVkIGJ5IDB4KSBvZiBhc2NpaSBzdHJpbmcgXG4gKlxuICogQG1ldGhvZCB0b0hleE5hdGl2ZVxuICogQHBhcmFtIHtTdHJpbmd9IHN0cmluZ1xuICogQHJldHVybnMge1N0cmluZ30gaGV4IHJlcHJlc2VudGF0aW9uIG9mIGlucHV0IHN0cmluZ1xuICovXG52YXIgdG9IZXhOYXRpdmUgPSBmdW5jdGlvbihzdHIpIHtcbiAgICB2YXIgaGV4ID0gXCJcIjtcbiAgICBmb3IodmFyIGkgPSAwOyBpIDwgc3RyLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIHZhciBuID0gc3RyLmNoYXJDb2RlQXQoaSkudG9TdHJpbmcoMTYpO1xuICAgICAgICBoZXggKz0gbi5sZW5ndGggPCAyID8gJzAnICsgbiA6IG47XG4gICAgfVxuXG4gICAgcmV0dXJuIGhleDtcbn07XG5cbi8qKlxuICogU2hvbGQgYmUgY2FsbGVkIHRvIGdldCBoZXggcmVwcmVzZW50YXRpb24gKHByZWZpeGVkIGJ5IDB4KSBvZiBhc2NpaSBzdHJpbmcgXG4gKlxuICogQG1ldGhvZCBmcm9tQXNjaWlcbiAqIEBwYXJhbSB7U3RyaW5nfSBzdHJpbmdcbiAqIEBwYXJhbSB7TnVtYmVyfSBvcHRpb25hbCBwYWRkaW5nXG4gKiBAcmV0dXJucyB7U3RyaW5nfSBoZXggcmVwcmVzZW50YXRpb24gb2YgaW5wdXQgc3RyaW5nXG4gKi9cbnZhciBmcm9tQXNjaWkgPSBmdW5jdGlvbihzdHIsIHBhZCkge1xuICAgIHBhZCA9IHBhZCA9PT0gdW5kZWZpbmVkID8gMCA6IHBhZDtcbiAgICB2YXIgaGV4ID0gdG9IZXhOYXRpdmUoc3RyKTtcbiAgICB3aGlsZSAoaGV4Lmxlbmd0aCA8IHBhZCoyKVxuICAgICAgICBoZXggKz0gXCIwMFwiO1xuICAgIHJldHVybiBcIjB4XCIgKyBoZXg7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGNyZWF0ZSBmdWxsIGZ1bmN0aW9uL2V2ZW50IG5hbWUgZnJvbSBqc29uIGFiaVxuICpcbiAqIEBtZXRob2QgdHJhbnNmb3JtVG9GdWxsTmFtZVxuICogQHBhcmFtIHtPYmplY3R9IGpzb24tYWJpXG4gKiBAcmV0dXJuIHtTdHJpbmd9IGZ1bGwgZm5jdGlvbi9ldmVudCBuYW1lXG4gKi9cbnZhciB0cmFuc2Zvcm1Ub0Z1bGxOYW1lID0gZnVuY3Rpb24gKGpzb24pIHtcbiAgICBpZiAoanNvbi5uYW1lLmluZGV4T2YoJygnKSAhPT0gLTEpIHtcbiAgICAgICAgcmV0dXJuIGpzb24ubmFtZTtcbiAgICB9XG5cbiAgICB2YXIgdHlwZU5hbWUgPSBqc29uLmlucHV0cy5tYXAoZnVuY3Rpb24oaSl7cmV0dXJuIGkudHlwZTsgfSkuam9pbigpO1xuICAgIHJldHVybiBqc29uLm5hbWUgKyAnKCcgKyB0eXBlTmFtZSArICcpJztcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBnZXQgZGlzcGxheSBuYW1lIG9mIGNvbnRyYWN0IGZ1bmN0aW9uXG4gKiBcbiAqIEBtZXRob2QgZXh0cmFjdERpc3BsYXlOYW1lXG4gKiBAcGFyYW0ge1N0cmluZ30gbmFtZSBvZiBmdW5jdGlvbi9ldmVudFxuICogQHJldHVybnMge1N0cmluZ30gZGlzcGxheSBuYW1lIGZvciBmdW5jdGlvbi9ldmVudCBlZy4gbXVsdGlwbHkodWludDI1NikgLT4gbXVsdGlwbHlcbiAqL1xudmFyIGV4dHJhY3REaXNwbGF5TmFtZSA9IGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgdmFyIGxlbmd0aCA9IG5hbWUuaW5kZXhPZignKCcpOyBcbiAgICByZXR1cm4gbGVuZ3RoICE9PSAtMSA/IG5hbWUuc3Vic3RyKDAsIGxlbmd0aCkgOiBuYW1lO1xufTtcblxuLy8vIEByZXR1cm5zIG92ZXJsb2FkZWQgcGFydCBvZiBmdW5jdGlvbi9ldmVudCBuYW1lXG52YXIgZXh0cmFjdFR5cGVOYW1lID0gZnVuY3Rpb24gKG5hbWUpIHtcbiAgICAvLy8gVE9ETzogbWFrZSBpdCBpbnZ1bG5lcmFibGVcbiAgICB2YXIgbGVuZ3RoID0gbmFtZS5pbmRleE9mKCcoJyk7XG4gICAgcmV0dXJuIGxlbmd0aCAhPT0gLTEgPyBuYW1lLnN1YnN0cihsZW5ndGggKyAxLCBuYW1lLmxlbmd0aCAtIDEgLSAobGVuZ3RoICsgMSkpLnJlcGxhY2UoJyAnLCAnJykgOiBcIlwiO1xufTtcblxuLyoqXG4gKiBDb252ZXJ0cyB2YWx1ZSB0byBpdCdzIGRlY2ltYWwgcmVwcmVzZW50YXRpb24gaW4gc3RyaW5nXG4gKlxuICogQG1ldGhvZCB0b0RlY2ltYWxcbiAqIEBwYXJhbSB7U3RyaW5nfE51bWJlcnxCaWdOdW1iZXJ9XG4gKiBAcmV0dXJuIHtTdHJpbmd9XG4gKi9cbnZhciB0b0RlY2ltYWwgPSBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICByZXR1cm4gdG9CaWdOdW1iZXIodmFsdWUpLnRvTnVtYmVyKCk7XG59O1xuXG4vKipcbiAqIENvbnZlcnRzIHZhbHVlIHRvIGl0J3MgaGV4IHJlcHJlc2VudGF0aW9uXG4gKlxuICogQG1ldGhvZCBmcm9tRGVjaW1hbFxuICogQHBhcmFtIHtTdHJpbmd8TnVtYmVyfEJpZ051bWJlcn1cbiAqIEByZXR1cm4ge1N0cmluZ31cbiAqL1xudmFyIGZyb21EZWNpbWFsID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgdmFyIG51bWJlciA9IHRvQmlnTnVtYmVyKHZhbHVlKTtcbiAgICB2YXIgcmVzdWx0ID0gbnVtYmVyLnRvU3RyaW5nKDE2KTtcblxuICAgIHJldHVybiBudW1iZXIubGVzc1RoYW4oMCkgPyAnLTB4JyArIHJlc3VsdC5zdWJzdHIoMSkgOiAnMHgnICsgcmVzdWx0O1xufTtcblxuLyoqXG4gKiBBdXRvIGNvbnZlcnRzIGFueSBnaXZlbiB2YWx1ZSBpbnRvIGl0J3MgaGV4IHJlcHJlc2VudGF0aW9uLlxuICpcbiAqIEFuZCBldmVuIHN0cmluZ2lmeXMgb2JqZWN0cyBiZWZvcmUuXG4gKlxuICogQG1ldGhvZCB0b0hleFxuICogQHBhcmFtIHtTdHJpbmd8TnVtYmVyfEJpZ051bWJlcnxPYmplY3R9XG4gKiBAcmV0dXJuIHtTdHJpbmd9XG4gKi9cbnZhciB0b0hleCA9IGZ1bmN0aW9uICh2YWwpIHtcbiAgICAvKmpzaGludCBtYXhjb21wbGV4aXR5OjcgKi9cblxuICAgIGlmIChpc0Jvb2xlYW4odmFsKSlcbiAgICAgICAgcmV0dXJuIGZyb21EZWNpbWFsKCt2YWwpO1xuXG4gICAgaWYgKGlzQmlnTnVtYmVyKHZhbCkpXG4gICAgICAgIHJldHVybiBmcm9tRGVjaW1hbCh2YWwpO1xuXG4gICAgaWYgKGlzT2JqZWN0KHZhbCkpXG4gICAgICAgIHJldHVybiBmcm9tQXNjaWkoSlNPTi5zdHJpbmdpZnkodmFsKSk7XG5cbiAgICAvLyBpZiBpdHMgYSBuZWdhdGl2ZSBudW1iZXIsIHBhc3MgaXQgdGhyb3VnaCBmcm9tRGVjaW1hbFxuICAgIGlmIChpc1N0cmluZyh2YWwpKSB7XG4gICAgICAgIGlmICh2YWwuaW5kZXhPZignLTB4JykgPT09IDApXG4gICAgICAgICAgIHJldHVybiBmcm9tRGVjaW1hbCh2YWwpO1xuICAgICAgICBlbHNlIGlmICghaXNGaW5pdGUodmFsKSlcbiAgICAgICAgICAgIHJldHVybiBmcm9tQXNjaWkodmFsKTtcbiAgICB9XG5cbiAgICByZXR1cm4gZnJvbURlY2ltYWwodmFsKTtcbn07XG5cbi8qKlxuICogUmV0dXJucyB2YWx1ZSBvZiB1bml0IGluIFdlaVxuICpcbiAqIEBtZXRob2QgZ2V0VmFsdWVPZlVuaXRcbiAqIEBwYXJhbSB7U3RyaW5nfSB1bml0IHRoZSB1bml0IHRvIGNvbnZlcnQgdG8sIGRlZmF1bHQgZXRoZXJcbiAqIEByZXR1cm5zIHtCaWdOdW1iZXJ9IHZhbHVlIG9mIHRoZSB1bml0IChpbiBXZWkpXG4gKiBAdGhyb3dzIGVycm9yIGlmIHRoZSB1bml0IGlzIG5vdCBjb3JyZWN0OndcbiAqL1xudmFyIGdldFZhbHVlT2ZVbml0ID0gZnVuY3Rpb24gKHVuaXQpIHtcbiAgICB1bml0ID0gdW5pdCA/IHVuaXQudG9Mb3dlckNhc2UoKSA6ICdldGhlcic7XG4gICAgdmFyIHVuaXRWYWx1ZSA9IHVuaXRNYXBbdW5pdF07XG4gICAgaWYgKHVuaXRWYWx1ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignVGhpcyB1bml0IGRvZXNuXFwndCBleGlzdHMsIHBsZWFzZSB1c2UgdGhlIG9uZSBvZiB0aGUgZm9sbG93aW5nIHVuaXRzJyArIEpTT04uc3RyaW5naWZ5KHVuaXRNYXAsIG51bGwsIDIpKTtcbiAgICB9XG4gICAgcmV0dXJuIG5ldyBCaWdOdW1iZXIodW5pdFZhbHVlLCAxMCk7XG59O1xuXG4vKipcbiAqIFRha2VzIGEgbnVtYmVyIG9mIHdlaSBhbmQgY29udmVydHMgaXQgdG8gYW55IG90aGVyIGV0aGVyIHVuaXQuXG4gKlxuICogUG9zc2libGUgdW5pdHMgYXJlOlxuICogICBTSSBTaG9ydCAgIFNJIEZ1bGwgICAgICAgIEVmZmlneSAgICAgICBPdGhlclxuICogLSBrd2VpICAgICAgIGZlbXRvZXRoZXIgICAgIGFkYVxuICogLSBtd2VpICAgICAgIHBpY29ldGhlciAgICAgIGJhYmJhZ2VcbiAqIC0gZ3dlaSAgICAgICBuYW5vZXRoZXIgICAgICBzaGFubm9uICAgICAgbmFub1xuICogLSAtLSAgICAgICAgIG1pY3JvZXRoZXIgICAgIHN6YWJvICAgICAgICBtaWNyb1xuICogLSAtLSAgICAgICAgIG1pbGxpZXRoZXIgICAgIGZpbm5leSAgICAgICBtaWxsaVxuICogLSBldGhlciAgICAgIC0tICAgICAgICAgICAgIC0tXG4gKiAtIGtldGhlciAgICAgICAgICAgICAgICAgICAgZWluc3RlaW4gICAgIGdyYW5kIFxuICogLSBtZXRoZXJcbiAqIC0gZ2V0aGVyXG4gKiAtIHRldGhlclxuICpcbiAqIEBtZXRob2QgZnJvbVdlaVxuICogQHBhcmFtIHtOdW1iZXJ8U3RyaW5nfSBudW1iZXIgY2FuIGJlIGEgbnVtYmVyLCBudW1iZXIgc3RyaW5nIG9yIGEgSEVYIG9mIGEgZGVjaW1hbFxuICogQHBhcmFtIHtTdHJpbmd9IHVuaXQgdGhlIHVuaXQgdG8gY29udmVydCB0bywgZGVmYXVsdCBldGhlclxuICogQHJldHVybiB7U3RyaW5nfE9iamVjdH0gV2hlbiBnaXZlbiBhIEJpZ051bWJlciBvYmplY3QgaXQgcmV0dXJucyBvbmUgYXMgd2VsbCwgb3RoZXJ3aXNlIGEgbnVtYmVyXG4qL1xudmFyIGZyb21XZWkgPSBmdW5jdGlvbihudW1iZXIsIHVuaXQpIHtcbiAgICB2YXIgcmV0dXJuVmFsdWUgPSB0b0JpZ051bWJlcihudW1iZXIpLmRpdmlkZWRCeShnZXRWYWx1ZU9mVW5pdCh1bml0KSk7XG5cbiAgICByZXR1cm4gaXNCaWdOdW1iZXIobnVtYmVyKSA/IHJldHVyblZhbHVlIDogcmV0dXJuVmFsdWUudG9TdHJpbmcoMTApOyBcbn07XG5cbi8qKlxuICogVGFrZXMgYSBudW1iZXIgb2YgYSB1bml0IGFuZCBjb252ZXJ0cyBpdCB0byB3ZWkuXG4gKlxuICogUG9zc2libGUgdW5pdHMgYXJlOlxuICogICBTSSBTaG9ydCAgIFNJIEZ1bGwgICAgICAgIEVmZmlneSAgICAgICBPdGhlclxuICogLSBrd2VpICAgICAgIGZlbXRvZXRoZXIgICAgIGFkYVxuICogLSBtd2VpICAgICAgIHBpY29ldGhlciAgICAgIGJhYmJhZ2UgICAgICAgXG4gKiAtIGd3ZWkgICAgICAgbmFub2V0aGVyICAgICAgc2hhbm5vbiAgICAgIG5hbm9cbiAqIC0gLS0gICAgICAgICBtaWNyb2V0aGVyICAgICBzemFibyAgICAgICAgbWljcm9cbiAqIC0gLS0gICAgICAgICBtaWxsaWV0aGVyICAgICBmaW5uZXkgICAgICAgbWlsbGlcbiAqIC0gZXRoZXIgICAgICAtLSAgICAgICAgICAgICAtLVxuICogLSBrZXRoZXIgICAgICAgICAgICAgICAgICAgIGVpbnN0ZWluICAgICBncmFuZCBcbiAqIC0gbWV0aGVyXG4gKiAtIGdldGhlclxuICogLSB0ZXRoZXJcbiAqXG4gKiBAbWV0aG9kIHRvV2VpXG4gKiBAcGFyYW0ge051bWJlcnxTdHJpbmd8QmlnTnVtYmVyfSBudW1iZXIgY2FuIGJlIGEgbnVtYmVyLCBudW1iZXIgc3RyaW5nIG9yIGEgSEVYIG9mIGEgZGVjaW1hbFxuICogQHBhcmFtIHtTdHJpbmd9IHVuaXQgdGhlIHVuaXQgdG8gY29udmVydCBmcm9tLCBkZWZhdWx0IGV0aGVyXG4gKiBAcmV0dXJuIHtTdHJpbmd8T2JqZWN0fSBXaGVuIGdpdmVuIGEgQmlnTnVtYmVyIG9iamVjdCBpdCByZXR1cm5zIG9uZSBhcyB3ZWxsLCBvdGhlcndpc2UgYSBudW1iZXJcbiovXG52YXIgdG9XZWkgPSBmdW5jdGlvbihudW1iZXIsIHVuaXQpIHtcbiAgICB2YXIgcmV0dXJuVmFsdWUgPSB0b0JpZ051bWJlcihudW1iZXIpLnRpbWVzKGdldFZhbHVlT2ZVbml0KHVuaXQpKTtcblxuICAgIHJldHVybiBpc0JpZ051bWJlcihudW1iZXIpID8gcmV0dXJuVmFsdWUgOiByZXR1cm5WYWx1ZS50b1N0cmluZygxMCk7IFxufTtcblxuLyoqXG4gKiBUYWtlcyBhbiBpbnB1dCBhbmQgdHJhbnNmb3JtcyBpdCBpbnRvIGFuIGJpZ251bWJlclxuICpcbiAqIEBtZXRob2QgdG9CaWdOdW1iZXJcbiAqIEBwYXJhbSB7TnVtYmVyfFN0cmluZ3xCaWdOdW1iZXJ9IGEgbnVtYmVyLCBzdHJpbmcsIEhFWCBzdHJpbmcgb3IgQmlnTnVtYmVyXG4gKiBAcmV0dXJuIHtCaWdOdW1iZXJ9IEJpZ051bWJlclxuKi9cbnZhciB0b0JpZ051bWJlciA9IGZ1bmN0aW9uKG51bWJlcikge1xuICAgIC8qanNoaW50IG1heGNvbXBsZXhpdHk6NSAqL1xuICAgIG51bWJlciA9IG51bWJlciB8fCAwO1xuICAgIGlmIChpc0JpZ051bWJlcihudW1iZXIpKVxuICAgICAgICByZXR1cm4gbnVtYmVyO1xuXG4gICAgaWYgKGlzU3RyaW5nKG51bWJlcikgJiYgKG51bWJlci5pbmRleE9mKCcweCcpID09PSAwIHx8IG51bWJlci5pbmRleE9mKCctMHgnKSA9PT0gMCkpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBCaWdOdW1iZXIobnVtYmVyLnJlcGxhY2UoJzB4JywnJyksIDE2KTtcbiAgICB9XG4gICBcbiAgICByZXR1cm4gbmV3IEJpZ051bWJlcihudW1iZXIudG9TdHJpbmcoMTApLCAxMCk7XG59O1xuXG4vKipcbiAqIFRha2VzIGFuZCBpbnB1dCB0cmFuc2Zvcm1zIGl0IGludG8gYmlnbnVtYmVyIGFuZCBpZiBpdCBpcyBuZWdhdGl2ZSB2YWx1ZSwgaW50byB0d28ncyBjb21wbGVtZW50XG4gKlxuICogQG1ldGhvZCB0b1R3b3NDb21wbGVtZW50XG4gKiBAcGFyYW0ge051bWJlcnxTdHJpbmd8QmlnTnVtYmVyfVxuICogQHJldHVybiB7QmlnTnVtYmVyfVxuICovXG52YXIgdG9Ud29zQ29tcGxlbWVudCA9IGZ1bmN0aW9uIChudW1iZXIpIHtcbiAgICB2YXIgYmlnTnVtYmVyID0gdG9CaWdOdW1iZXIobnVtYmVyKTtcbiAgICBpZiAoYmlnTnVtYmVyLmxlc3NUaGFuKDApKSB7XG4gICAgICAgIHJldHVybiBuZXcgQmlnTnVtYmVyKFwiZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZlwiLCAxNikucGx1cyhiaWdOdW1iZXIpLnBsdXMoMSk7XG4gICAgfVxuICAgIHJldHVybiBiaWdOdW1iZXI7XG59O1xuXG4vKipcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gc3RyaW5nIGlzIHN0cmljdGx5IGFuIGFkZHJlc3NcbiAqXG4gKiBAbWV0aG9kIGlzU3RyaWN0QWRkcmVzc1xuICogQHBhcmFtIHtTdHJpbmd9IGFkZHJlc3MgdGhlIGdpdmVuIEhFWCBhZHJlc3NcbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4qL1xudmFyIGlzU3RyaWN0QWRkcmVzcyA9IGZ1bmN0aW9uIChhZGRyZXNzKSB7XG4gICAgcmV0dXJuIC9eMHhbMC05YS1mXXs0MH0kLy50ZXN0KGFkZHJlc3MpO1xufTtcblxuLyoqXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIHN0cmluZyBpcyBhbiBhZGRyZXNzXG4gKlxuICogQG1ldGhvZCBpc0FkZHJlc3NcbiAqIEBwYXJhbSB7U3RyaW5nfSBhZGRyZXNzIHRoZSBnaXZlbiBIRVggYWRyZXNzXG4gKiBAcmV0dXJuIHtCb29sZWFufVxuKi9cbnZhciBpc0FkZHJlc3MgPSBmdW5jdGlvbiAoYWRkcmVzcykge1xuICAgIHJldHVybiAvXigweCk/WzAtOWEtZl17NDB9JC8udGVzdChhZGRyZXNzKTtcbn07XG5cbi8qKlxuICogVHJhbnNmb3JtcyBnaXZlbiBzdHJpbmcgdG8gdmFsaWQgMjAgYnl0ZXMtbGVuZ3RoIGFkZHJlcyB3aXRoIDB4IHByZWZpeFxuICpcbiAqIEBtZXRob2QgdG9BZGRyZXNzXG4gKiBAcGFyYW0ge1N0cmluZ30gYWRkcmVzc1xuICogQHJldHVybiB7U3RyaW5nfSBmb3JtYXR0ZWQgYWRkcmVzc1xuICovXG52YXIgdG9BZGRyZXNzID0gZnVuY3Rpb24gKGFkZHJlc3MpIHtcbiAgICBpZiAoaXNTdHJpY3RBZGRyZXNzKGFkZHJlc3MpKSB7XG4gICAgICAgIHJldHVybiBhZGRyZXNzO1xuICAgIH1cbiAgICBcbiAgICBpZiAoL15bMC05YS1mXXs0MH0kLy50ZXN0KGFkZHJlc3MpKSB7XG4gICAgICAgIHJldHVybiAnMHgnICsgYWRkcmVzcztcbiAgICB9XG5cbiAgICByZXR1cm4gJzB4JyArIHBhZExlZnQodG9IZXgoYWRkcmVzcykuc3Vic3RyKDIpLCA0MCk7XG59O1xuXG5cbi8qKlxuICogUmV0dXJucyB0cnVlIGlmIG9iamVjdCBpcyBCaWdOdW1iZXIsIG90aGVyd2lzZSBmYWxzZVxuICpcbiAqIEBtZXRob2QgaXNCaWdOdW1iZXJcbiAqIEBwYXJhbSB7T2JqZWN0fVxuICogQHJldHVybiB7Qm9vbGVhbn0gXG4gKi9cbnZhciBpc0JpZ051bWJlciA9IGZ1bmN0aW9uIChvYmplY3QpIHtcbiAgICByZXR1cm4gb2JqZWN0IGluc3RhbmNlb2YgQmlnTnVtYmVyIHx8XG4gICAgICAgIChvYmplY3QgJiYgb2JqZWN0LmNvbnN0cnVjdG9yICYmIG9iamVjdC5jb25zdHJ1Y3Rvci5uYW1lID09PSAnQmlnTnVtYmVyJyk7XG59O1xuXG4vKipcbiAqIFJldHVybnMgdHJ1ZSBpZiBvYmplY3QgaXMgc3RyaW5nLCBvdGhlcndpc2UgZmFsc2VcbiAqIFxuICogQG1ldGhvZCBpc1N0cmluZ1xuICogQHBhcmFtIHtPYmplY3R9XG4gKiBAcmV0dXJuIHtCb29sZWFufVxuICovXG52YXIgaXNTdHJpbmcgPSBmdW5jdGlvbiAob2JqZWN0KSB7XG4gICAgcmV0dXJuIHR5cGVvZiBvYmplY3QgPT09ICdzdHJpbmcnIHx8XG4gICAgICAgIChvYmplY3QgJiYgb2JqZWN0LmNvbnN0cnVjdG9yICYmIG9iamVjdC5jb25zdHJ1Y3Rvci5uYW1lID09PSAnU3RyaW5nJyk7XG59O1xuXG4vKipcbiAqIFJldHVybnMgdHJ1ZSBpZiBvYmplY3QgaXMgZnVuY3Rpb24sIG90aGVyd2lzZSBmYWxzZVxuICpcbiAqIEBtZXRob2QgaXNGdW5jdGlvblxuICogQHBhcmFtIHtPYmplY3R9XG4gKiBAcmV0dXJuIHtCb29sZWFufVxuICovXG52YXIgaXNGdW5jdGlvbiA9IGZ1bmN0aW9uIChvYmplY3QpIHtcbiAgICByZXR1cm4gdHlwZW9mIG9iamVjdCA9PT0gJ2Z1bmN0aW9uJztcbn07XG5cbi8qKlxuICogUmV0dXJucyB0cnVlIGlmIG9iamVjdCBpcyBPYmpldCwgb3RoZXJ3aXNlIGZhbHNlXG4gKlxuICogQG1ldGhvZCBpc09iamVjdFxuICogQHBhcmFtIHtPYmplY3R9XG4gKiBAcmV0dXJuIHtCb29sZWFufVxuICovXG52YXIgaXNPYmplY3QgPSBmdW5jdGlvbiAob2JqZWN0KSB7XG4gICAgcmV0dXJuIHR5cGVvZiBvYmplY3QgPT09ICdvYmplY3QnO1xufTtcblxuLyoqXG4gKiBSZXR1cm5zIHRydWUgaWYgb2JqZWN0IGlzIGJvb2xlYW4sIG90aGVyd2lzZSBmYWxzZVxuICpcbiAqIEBtZXRob2QgaXNCb29sZWFuXG4gKiBAcGFyYW0ge09iamVjdH1cbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4gKi9cbnZhciBpc0Jvb2xlYW4gPSBmdW5jdGlvbiAob2JqZWN0KSB7XG4gICAgcmV0dXJuIHR5cGVvZiBvYmplY3QgPT09ICdib29sZWFuJztcbn07XG5cbi8qKlxuICogUmV0dXJucyB0cnVlIGlmIG9iamVjdCBpcyBhcnJheSwgb3RoZXJ3aXNlIGZhbHNlXG4gKlxuICogQG1ldGhvZCBpc0FycmF5XG4gKiBAcGFyYW0ge09iamVjdH1cbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4gKi9cbnZhciBpc0FycmF5ID0gZnVuY3Rpb24gKG9iamVjdCkge1xuICAgIHJldHVybiBvYmplY3QgaW5zdGFuY2VvZiBBcnJheTsgXG59O1xuXG4vKipcbiAqIFJldHVybnMgdHJ1ZSBpZiBnaXZlbiBzdHJpbmcgaXMgdmFsaWQganNvbiBvYmplY3RcbiAqIFxuICogQG1ldGhvZCBpc0pzb25cbiAqIEBwYXJhbSB7U3RyaW5nfVxuICogQHJldHVybiB7Qm9vbGVhbn1cbiAqL1xudmFyIGlzSnNvbiA9IGZ1bmN0aW9uIChzdHIpIHtcbiAgICB0cnkge1xuICAgICAgICByZXR1cm4gISFKU09OLnBhcnNlKHN0cik7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgY2FsbGVkIHRvIGNoZWNrIGlmIHN0cmluZyBpcyB2YWxpZCBldGhlcmV1bSBJQkFOIG51bWJlclxuICogU3VwcG9ydHMgZGlyZWN0IGFuZCBpbmRpcmVjdCBJQkFOc1xuICpcbiAqIEBtZXRob2QgaXNJQkFOXG4gKiBAcGFyYW0ge1N0cmluZ31cbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4gKi9cbnZhciBpc0lCQU4gPSBmdW5jdGlvbiAoaWJhbikge1xuICAgIHJldHVybiAvXlhFWzAtOV17Mn0oRVRIWzAtOUEtWl17MTN9fFswLTlBLVpdezMwfSkkLy50ZXN0KGliYW4pO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgcGFkTGVmdDogcGFkTGVmdCxcbiAgICB0b0hleDogdG9IZXgsXG4gICAgdG9EZWNpbWFsOiB0b0RlY2ltYWwsXG4gICAgZnJvbURlY2ltYWw6IGZyb21EZWNpbWFsLFxuICAgIHRvQXNjaWk6IHRvQXNjaWksXG4gICAgZnJvbUFzY2lpOiBmcm9tQXNjaWksXG4gICAgdHJhbnNmb3JtVG9GdWxsTmFtZTogdHJhbnNmb3JtVG9GdWxsTmFtZSxcbiAgICBleHRyYWN0RGlzcGxheU5hbWU6IGV4dHJhY3REaXNwbGF5TmFtZSxcbiAgICBleHRyYWN0VHlwZU5hbWU6IGV4dHJhY3RUeXBlTmFtZSxcbiAgICB0b1dlaTogdG9XZWksXG4gICAgZnJvbVdlaTogZnJvbVdlaSxcbiAgICB0b0JpZ051bWJlcjogdG9CaWdOdW1iZXIsXG4gICAgdG9Ud29zQ29tcGxlbWVudDogdG9Ud29zQ29tcGxlbWVudCxcbiAgICB0b0FkZHJlc3M6IHRvQWRkcmVzcyxcbiAgICBpc0JpZ051bWJlcjogaXNCaWdOdW1iZXIsXG4gICAgaXNTdHJpY3RBZGRyZXNzOiBpc1N0cmljdEFkZHJlc3MsXG4gICAgaXNBZGRyZXNzOiBpc0FkZHJlc3MsXG4gICAgaXNGdW5jdGlvbjogaXNGdW5jdGlvbixcbiAgICBpc1N0cmluZzogaXNTdHJpbmcsXG4gICAgaXNPYmplY3Q6IGlzT2JqZWN0LFxuICAgIGlzQm9vbGVhbjogaXNCb29sZWFuLFxuICAgIGlzQXJyYXk6IGlzQXJyYXksXG4gICAgaXNKc29uOiBpc0pzb24sXG4gICAgaXNJQkFOOiBpc0lCQU5cbn07XG5cbiIsIm1vZHVsZS5leHBvcnRzPXtcbiAgICBcInZlcnNpb25cIjogXCIwLjUuMFwiXG59XG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBAZmlsZSB3ZWIzLmpzXG4gKiBAYXV0aG9yczpcbiAqICAgSmVmZnJleSBXaWxja2UgPGplZmZAZXRoZGV2LmNvbT5cbiAqICAgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiAgIE1hcmlhbiBPYW5jZWEgPG1hcmlhbkBldGhkZXYuY29tPlxuICogICBGYWJpYW4gVm9nZWxzdGVsbGVyIDxmYWJpYW5AZXRoZGV2LmNvbT5cbiAqICAgR2F2IFdvb2QgPGdAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTRcbiAqL1xuXG52YXIgdmVyc2lvbiA9IHJlcXVpcmUoJy4vdmVyc2lvbi5qc29uJyk7XG52YXIgbmV0ID0gcmVxdWlyZSgnLi93ZWIzL25ldCcpO1xudmFyIGV0aCA9IHJlcXVpcmUoJy4vd2ViMy9ldGgnKTtcbnZhciBkYiA9IHJlcXVpcmUoJy4vd2ViMy9kYicpO1xudmFyIHNoaCA9IHJlcXVpcmUoJy4vd2ViMy9zaGgnKTtcbnZhciB3YXRjaGVzID0gcmVxdWlyZSgnLi93ZWIzL3dhdGNoZXMnKTtcbnZhciBGaWx0ZXIgPSByZXF1aXJlKCcuL3dlYjMvZmlsdGVyJyk7XG52YXIgdXRpbHMgPSByZXF1aXJlKCcuL3V0aWxzL3V0aWxzJyk7XG52YXIgZm9ybWF0dGVycyA9IHJlcXVpcmUoJy4vd2ViMy9mb3JtYXR0ZXJzJyk7XG52YXIgUmVxdWVzdE1hbmFnZXIgPSByZXF1aXJlKCcuL3dlYjMvcmVxdWVzdG1hbmFnZXInKTtcbnZhciBjID0gcmVxdWlyZSgnLi91dGlscy9jb25maWcnKTtcbnZhciBQcm9wZXJ0eSA9IHJlcXVpcmUoJy4vd2ViMy9wcm9wZXJ0eScpO1xudmFyIEJhdGNoID0gcmVxdWlyZSgnLi93ZWIzL2JhdGNoJyk7XG52YXIgc2hhMyA9IHJlcXVpcmUoJy4vdXRpbHMvc2hhMycpO1xuXG52YXIgd2ViM1Byb3BlcnRpZXMgPSBbXG4gICAgbmV3IFByb3BlcnR5KHtcbiAgICAgICAgbmFtZTogJ3ZlcnNpb24uY2xpZW50JyxcbiAgICAgICAgZ2V0dGVyOiAnd2ViM19jbGllbnRWZXJzaW9uJ1xuICAgIH0pLFxuICAgIG5ldyBQcm9wZXJ0eSh7XG4gICAgICAgIG5hbWU6ICd2ZXJzaW9uLm5ldHdvcmsnLFxuICAgICAgICBnZXR0ZXI6ICduZXRfdmVyc2lvbicsXG4gICAgICAgIGlucHV0Rm9ybWF0dGVyOiB1dGlscy50b0RlY2ltYWxcbiAgICB9KSxcbiAgICBuZXcgUHJvcGVydHkoe1xuICAgICAgICBuYW1lOiAndmVyc2lvbi5ldGhlcmV1bScsXG4gICAgICAgIGdldHRlcjogJ2V0aF9wcm90b2NvbFZlcnNpb24nLFxuICAgICAgICBpbnB1dEZvcm1hdHRlcjogdXRpbHMudG9EZWNpbWFsXG4gICAgfSksXG4gICAgbmV3IFByb3BlcnR5KHtcbiAgICAgICAgbmFtZTogJ3ZlcnNpb24ud2hpc3BlcicsXG4gICAgICAgIGdldHRlcjogJ3NoaF92ZXJzaW9uJyxcbiAgICAgICAgaW5wdXRGb3JtYXR0ZXI6IHV0aWxzLnRvRGVjaW1hbFxuICAgIH0pXG5dO1xuXG4vLy8gY3JlYXRlcyBtZXRob2RzIGluIGEgZ2l2ZW4gb2JqZWN0IGJhc2VkIG9uIG1ldGhvZCBkZXNjcmlwdGlvbiBvbiBpbnB1dFxuLy8vIHNldHVwcyBhcGkgY2FsbHMgZm9yIHRoZXNlIG1ldGhvZHNcbnZhciBzZXR1cE1ldGhvZHMgPSBmdW5jdGlvbiAob2JqLCBtZXRob2RzKSB7XG4gICAgbWV0aG9kcy5mb3JFYWNoKGZ1bmN0aW9uIChtZXRob2QpIHtcbiAgICAgICAgbWV0aG9kLmF0dGFjaFRvT2JqZWN0KG9iaik7XG4gICAgfSk7XG59O1xuXG4vLy8gY3JlYXRlcyBwcm9wZXJ0aWVzIGluIGEgZ2l2ZW4gb2JqZWN0IGJhc2VkIG9uIHByb3BlcnRpZXMgZGVzY3JpcHRpb24gb24gaW5wdXRcbi8vLyBzZXR1cHMgYXBpIGNhbGxzIGZvciB0aGVzZSBwcm9wZXJ0aWVzXG52YXIgc2V0dXBQcm9wZXJ0aWVzID0gZnVuY3Rpb24gKG9iaiwgcHJvcGVydGllcykge1xuICAgIHByb3BlcnRpZXMuZm9yRWFjaChmdW5jdGlvbiAocHJvcGVydHkpIHtcbiAgICAgICAgcHJvcGVydHkuYXR0YWNoVG9PYmplY3Qob2JqKTtcbiAgICB9KTtcbn07XG5cbi8vLyBzZXR1cHMgd2ViMyBvYmplY3QsIGFuZCBpdCdzIGluLWJyb3dzZXIgZXhlY3V0ZWQgbWV0aG9kc1xudmFyIHdlYjMgPSB7fTtcbndlYjMucHJvdmlkZXJzID0ge307XG53ZWIzLnZlcnNpb24gPSB7fTtcbndlYjMudmVyc2lvbi5hcGkgPSB2ZXJzaW9uLnZlcnNpb247XG53ZWIzLmV0aCA9IHt9O1xuXG4vKmpzaGludCBtYXhwYXJhbXM6NCAqL1xud2ViMy5ldGguZmlsdGVyID0gZnVuY3Rpb24gKGZpbCwgZXZlbnRQYXJhbXMsIG9wdGlvbnMsIGZvcm1hdHRlcikge1xuXG4gICAgLy8gaWYgaXRzIGV2ZW50LCB0cmVhdCBpdCBkaWZmZXJlbnRseVxuICAgIC8vIFRPRE86IHNpbXBsaWZ5IGFuZCByZW1vdmVcbiAgICBpZiAoZmlsLl9pc0V2ZW50KSB7XG4gICAgICAgIHJldHVybiBmaWwoZXZlbnRQYXJhbXMsIG9wdGlvbnMpO1xuICAgIH1cblxuICAgIC8vIHdoYXQgb3V0cHV0TG9nRm9ybWF0dGVyPyB0aGF0J3Mgd3JvbmdcbiAgICAvL3JldHVybiBuZXcgRmlsdGVyKGZpbCwgd2F0Y2hlcy5ldGgoKSwgZm9ybWF0dGVycy5vdXRwdXRMb2dGb3JtYXR0ZXIpO1xuICAgIHJldHVybiBuZXcgRmlsdGVyKGZpbCwgd2F0Y2hlcy5ldGgoKSwgZm9ybWF0dGVyIHx8IGZvcm1hdHRlcnMub3V0cHV0TG9nRm9ybWF0dGVyKTtcbn07XG4vKmpzaGludCBtYXhwYXJhbXM6MyAqL1xuXG53ZWIzLnNoaCA9IHt9O1xud2ViMy5zaGguZmlsdGVyID0gZnVuY3Rpb24gKGZpbCkge1xuICAgIHJldHVybiBuZXcgRmlsdGVyKGZpbCwgd2F0Y2hlcy5zaGgoKSwgZm9ybWF0dGVycy5vdXRwdXRQb3N0Rm9ybWF0dGVyKTtcbn07XG53ZWIzLm5ldCA9IHt9O1xud2ViMy5kYiA9IHt9O1xud2ViMy5zZXRQcm92aWRlciA9IGZ1bmN0aW9uIChwcm92aWRlcikge1xuICAgIFJlcXVlc3RNYW5hZ2VyLmdldEluc3RhbmNlKCkuc2V0UHJvdmlkZXIocHJvdmlkZXIpO1xufTtcbndlYjMucmVzZXQgPSBmdW5jdGlvbiAoKSB7XG4gICAgUmVxdWVzdE1hbmFnZXIuZ2V0SW5zdGFuY2UoKS5yZXNldCgpO1xuICAgIGMuZGVmYXVsdEJsb2NrID0gJ2xhdGVzdCc7XG4gICAgYy5kZWZhdWx0QWNjb3VudCA9IHVuZGVmaW5lZDtcbn07XG53ZWIzLnRvSGV4ID0gdXRpbHMudG9IZXg7XG53ZWIzLnRvQXNjaWkgPSB1dGlscy50b0FzY2lpO1xud2ViMy5mcm9tQXNjaWkgPSB1dGlscy5mcm9tQXNjaWk7XG53ZWIzLnRvRGVjaW1hbCA9IHV0aWxzLnRvRGVjaW1hbDtcbndlYjMuZnJvbURlY2ltYWwgPSB1dGlscy5mcm9tRGVjaW1hbDtcbndlYjMudG9CaWdOdW1iZXIgPSB1dGlscy50b0JpZ051bWJlcjtcbndlYjMudG9XZWkgPSB1dGlscy50b1dlaTtcbndlYjMuZnJvbVdlaSA9IHV0aWxzLmZyb21XZWk7XG53ZWIzLmlzQWRkcmVzcyA9IHV0aWxzLmlzQWRkcmVzcztcbndlYjMuaXNJQkFOID0gdXRpbHMuaXNJQkFOO1xud2ViMy5zaGEzID0gc2hhMztcbndlYjMuY3JlYXRlQmF0Y2ggPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIG5ldyBCYXRjaCgpO1xufTtcblxuLy8gQUREIGRlZmF1bHRibG9ja1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KHdlYjMuZXRoLCAnZGVmYXVsdEJsb2NrJywge1xuICAgIGdldDogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gYy5kZWZhdWx0QmxvY2s7XG4gICAgfSxcbiAgICBzZXQ6IGZ1bmN0aW9uICh2YWwpIHtcbiAgICAgICAgYy5kZWZhdWx0QmxvY2sgPSB2YWw7XG4gICAgICAgIHJldHVybiB2YWw7XG4gICAgfVxufSk7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh3ZWIzLmV0aCwgJ2RlZmF1bHRBY2NvdW50Jywge1xuICAgIGdldDogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gYy5kZWZhdWx0QWNjb3VudDtcbiAgICB9LFxuICAgIHNldDogZnVuY3Rpb24gKHZhbCkge1xuICAgICAgICBjLmRlZmF1bHRBY2NvdW50ID0gdmFsO1xuICAgICAgICByZXR1cm4gdmFsO1xuICAgIH1cbn0pO1xuXG4vLy8gc2V0dXBzIGFsbCBhcGkgbWV0aG9kc1xuc2V0dXBQcm9wZXJ0aWVzKHdlYjMsIHdlYjNQcm9wZXJ0aWVzKTtcbnNldHVwTWV0aG9kcyh3ZWIzLm5ldCwgbmV0Lm1ldGhvZHMpO1xuc2V0dXBQcm9wZXJ0aWVzKHdlYjMubmV0LCBuZXQucHJvcGVydGllcyk7XG5zZXR1cE1ldGhvZHMod2ViMy5ldGgsIGV0aC5tZXRob2RzKTtcbnNldHVwUHJvcGVydGllcyh3ZWIzLmV0aCwgZXRoLnByb3BlcnRpZXMpO1xuc2V0dXBNZXRob2RzKHdlYjMuZGIsIGRiLm1ldGhvZHMpO1xuc2V0dXBNZXRob2RzKHdlYjMuc2hoLCBzaGgubWV0aG9kcyk7XG5cbm1vZHVsZS5leHBvcnRzID0gd2ViMztcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSBiYXRjaC5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgUmVxdWVzdE1hbmFnZXIgPSByZXF1aXJlKCcuL3JlcXVlc3RtYW5hZ2VyJyk7XG5cbnZhciBCYXRjaCA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLnJlcXVlc3RzID0gW107XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gYWRkIGNyZWF0ZSBuZXcgcmVxdWVzdCB0byBiYXRjaCByZXF1ZXN0XG4gKlxuICogQG1ldGhvZCBhZGRcbiAqIEBwYXJhbSB7T2JqZWN0fSBqc29ucnBjIHJlcXVldCBvYmplY3RcbiAqL1xuQmF0Y2gucHJvdG90eXBlLmFkZCA9IGZ1bmN0aW9uIChyZXF1ZXN0KSB7XG4gICAgdGhpcy5yZXF1ZXN0cy5wdXNoKHJlcXVlc3QpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGV4ZWN1dGUgYmF0Y2ggcmVxdWVzdFxuICpcbiAqIEBtZXRob2QgZXhlY3V0ZVxuICovXG5CYXRjaC5wcm90b3R5cGUuZXhlY3V0ZSA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgcmVxdWVzdHMgPSB0aGlzLnJlcXVlc3RzO1xuICAgIFJlcXVlc3RNYW5hZ2VyLmdldEluc3RhbmNlKCkuc2VuZEJhdGNoKHJlcXVlc3RzLCBmdW5jdGlvbiAoZXJyLCByZXN1bHRzKSB7XG4gICAgICAgIHJlc3VsdHMgPSByZXN1bHRzIHx8IFtdO1xuICAgICAgICByZXF1ZXN0cy5tYXAoZnVuY3Rpb24gKHJlcXVlc3QsIGluZGV4KSB7XG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0c1tpbmRleF0gfHwge307XG4gICAgICAgIH0pLm1hcChmdW5jdGlvbiAocmVzdWx0LCBpbmRleCkge1xuICAgICAgICAgICAgcmV0dXJuIHJlcXVlc3RzW2luZGV4XS5mb3JtYXQgPyByZXF1ZXN0c1tpbmRleF0uZm9ybWF0KHJlc3VsdC5yZXN1bHQpIDogcmVzdWx0LnJlc3VsdDtcbiAgICAgICAgfSkuZm9yRWFjaChmdW5jdGlvbiAocmVzdWx0LCBpbmRleCkge1xuICAgICAgICAgICAgaWYgKHJlcXVlc3RzW2luZGV4XS5jYWxsYmFjaykge1xuICAgICAgICAgICAgICAgIHJlcXVlc3RzW2luZGV4XS5jYWxsYmFjayhlcnIsIHJlc3VsdCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH0pOyBcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gQmF0Y2g7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIFxuICogQGZpbGUgY29udHJhY3QuanNcbiAqIEBhdXRob3IgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE0XG4gKi9cblxudmFyIHdlYjMgPSByZXF1aXJlKCcuLi93ZWIzJyk7IFxudmFyIHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMvdXRpbHMnKTtcbnZhciBjb2RlciA9IHJlcXVpcmUoJy4uL3NvbGlkaXR5L2NvZGVyJyk7XG52YXIgU29saWRpdHlFdmVudCA9IHJlcXVpcmUoJy4vZXZlbnQnKTtcbnZhciBTb2xpZGl0eUZ1bmN0aW9uID0gcmVxdWlyZSgnLi9mdW5jdGlvbicpO1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZW5jb2RlIGNvbnN0cnVjdG9yIHBhcmFtc1xuICpcbiAqIEBtZXRob2QgZW5jb2RlQ29uc3RydWN0b3JQYXJhbXNcbiAqIEBwYXJhbSB7QXJyYXl9IGFiaVxuICogQHBhcmFtIHtBcnJheX0gY29uc3RydWN0b3IgcGFyYW1zXG4gKi9cbnZhciBlbmNvZGVDb25zdHJ1Y3RvclBhcmFtcyA9IGZ1bmN0aW9uIChhYmksIHBhcmFtcykge1xuICAgIHJldHVybiBhYmkuZmlsdGVyKGZ1bmN0aW9uIChqc29uKSB7XG4gICAgICAgIHJldHVybiBqc29uLnR5cGUgPT09ICdjb25zdHJ1Y3RvcicgJiYganNvbi5pbnB1dHMubGVuZ3RoID09PSBwYXJhbXMubGVuZ3RoO1xuICAgIH0pLm1hcChmdW5jdGlvbiAoanNvbikge1xuICAgICAgICByZXR1cm4ganNvbi5pbnB1dHMubWFwKGZ1bmN0aW9uIChpbnB1dCkge1xuICAgICAgICAgICAgcmV0dXJuIGlucHV0LnR5cGU7XG4gICAgICAgIH0pO1xuICAgIH0pLm1hcChmdW5jdGlvbiAodHlwZXMpIHtcbiAgICAgICAgcmV0dXJuIGNvZGVyLmVuY29kZVBhcmFtcyh0eXBlcywgcGFyYW1zKTtcbiAgICB9KVswXSB8fCAnJztcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBhZGQgZnVuY3Rpb25zIHRvIGNvbnRyYWN0IG9iamVjdFxuICpcbiAqIEBtZXRob2QgYWRkRnVuY3Rpb25zVG9Db250cmFjdFxuICogQHBhcmFtIHtDb250cmFjdH0gY29udHJhY3RcbiAqIEBwYXJhbSB7QXJyYXl9IGFiaVxuICovXG52YXIgYWRkRnVuY3Rpb25zVG9Db250cmFjdCA9IGZ1bmN0aW9uIChjb250cmFjdCwgYWJpKSB7XG4gICAgYWJpLmZpbHRlcihmdW5jdGlvbiAoanNvbikge1xuICAgICAgICByZXR1cm4ganNvbi50eXBlID09PSAnZnVuY3Rpb24nO1xuICAgIH0pLm1hcChmdW5jdGlvbiAoanNvbikge1xuICAgICAgICByZXR1cm4gbmV3IFNvbGlkaXR5RnVuY3Rpb24oanNvbiwgY29udHJhY3QuYWRkcmVzcyk7XG4gICAgfSkuZm9yRWFjaChmdW5jdGlvbiAoZikge1xuICAgICAgICBmLmF0dGFjaFRvQ29udHJhY3QoY29udHJhY3QpO1xuICAgIH0pO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGFkZCBldmVudHMgdG8gY29udHJhY3Qgb2JqZWN0XG4gKlxuICogQG1ldGhvZCBhZGRFdmVudHNUb0NvbnRyYWN0XG4gKiBAcGFyYW0ge0NvbnRyYWN0fSBjb250cmFjdFxuICogQHBhcmFtIHtBcnJheX0gYWJpXG4gKi9cbnZhciBhZGRFdmVudHNUb0NvbnRyYWN0ID0gZnVuY3Rpb24gKGNvbnRyYWN0LCBhYmkpIHtcbiAgICBhYmkuZmlsdGVyKGZ1bmN0aW9uIChqc29uKSB7XG4gICAgICAgIHJldHVybiBqc29uLnR5cGUgPT09ICdldmVudCc7XG4gICAgfSkubWFwKGZ1bmN0aW9uIChqc29uKSB7XG4gICAgICAgIHJldHVybiBuZXcgU29saWRpdHlFdmVudChqc29uLCBjb250cmFjdC5hZGRyZXNzKTtcbiAgICB9KS5mb3JFYWNoKGZ1bmN0aW9uIChlKSB7XG4gICAgICAgIGUuYXR0YWNoVG9Db250cmFjdChjb250cmFjdCk7XG4gICAgfSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gY3JlYXRlIG5ldyBDb250cmFjdEZhY3RvcnlcbiAqXG4gKiBAbWV0aG9kIGNvbnRyYWN0XG4gKiBAcGFyYW0ge0FycmF5fSBhYmlcbiAqIEByZXR1cm5zIHtDb250cmFjdEZhY3Rvcnl9IG5ldyBjb250cmFjdCBmYWN0b3J5XG4gKi9cbnZhciBjb250cmFjdCA9IGZ1bmN0aW9uIChhYmkpIHtcbiAgICByZXR1cm4gbmV3IENvbnRyYWN0RmFjdG9yeShhYmkpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGNyZWF0ZSBuZXcgQ29udHJhY3RGYWN0b3J5IGluc3RhbmNlXG4gKlxuICogQG1ldGhvZCBDb250cmFjdEZhY3RvcnlcbiAqIEBwYXJhbSB7QXJyYXl9IGFiaVxuICovXG52YXIgQ29udHJhY3RGYWN0b3J5ID0gZnVuY3Rpb24gKGFiaSkge1xuICAgIHRoaXMuYWJpID0gYWJpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGNyZWF0ZSBuZXcgY29udHJhY3Qgb24gYSBibG9ja2NoYWluXG4gKiBcbiAqIEBtZXRob2QgbmV3XG4gKiBAcGFyYW0ge0FueX0gY29udHJhY3QgY29uc3RydWN0b3IgcGFyYW0xIChvcHRpb25hbClcbiAqIEBwYXJhbSB7QW55fSBjb250cmFjdCBjb25zdHJ1Y3RvciBwYXJhbTIgKG9wdGlvbmFsKVxuICogQHBhcmFtIHtPYmplY3R9IGNvbnRyYWN0IHRyYW5zYWN0aW9uIG9iamVjdCAocmVxdWlyZWQpXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFja1xuICogQHJldHVybnMge0NvbnRyYWN0fSByZXR1cm5zIGNvbnRyYWN0IGlmIG5vIGNhbGxiYWNrIHdhcyBwYXNzZWQsXG4gKiBvdGhlcndpc2UgY2FsbHMgY2FsbGJhY2sgZnVuY3Rpb24gKGVyciwgY29udHJhY3QpXG4gKi9cbkNvbnRyYWN0RmFjdG9yeS5wcm90b3R5cGUubmV3ID0gZnVuY3Rpb24gKCkge1xuICAgIC8vIHBhcnNlIGFyZ3VtZW50c1xuICAgIHZhciBvcHRpb25zID0ge307IC8vIHJlcXVpcmVkIVxuICAgIHZhciBjYWxsYmFjaztcblxuICAgIHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKTtcbiAgICBpZiAodXRpbHMuaXNGdW5jdGlvbihhcmdzW2FyZ3MubGVuZ3RoIC0gMV0pKSB7XG4gICAgICAgIGNhbGxiYWNrID0gYXJncy5wb3AoKTtcbiAgICB9XG5cbiAgICB2YXIgbGFzdCA9IGFyZ3NbYXJncy5sZW5ndGggLSAxXTtcbiAgICBpZiAodXRpbHMuaXNPYmplY3QobGFzdCkgJiYgIXV0aWxzLmlzQXJyYXkobGFzdCkpIHtcbiAgICAgICAgb3B0aW9ucyA9IGFyZ3MucG9wKCk7XG4gICAgfVxuXG4gICAgLy8gdGhyb3cgYW4gZXJyb3IgaWYgdGhlcmUgYXJlIG5vIG9wdGlvbnNcblxuICAgIHZhciBieXRlcyA9IGVuY29kZUNvbnN0cnVjdG9yUGFyYW1zKHRoaXMuYWJpLCBhcmdzKTtcbiAgICBvcHRpb25zLmRhdGEgKz0gYnl0ZXM7XG5cbiAgICBpZiAoIWNhbGxiYWNrKSB7XG4gICAgICAgIHZhciBhZGRyZXNzID0gd2ViMy5ldGguc2VuZFRyYW5zYWN0aW9uKG9wdGlvbnMpO1xuICAgICAgICByZXR1cm4gdGhpcy5hdChhZGRyZXNzKTtcbiAgICB9XG4gIFxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB3ZWIzLmV0aC5zZW5kVHJhbnNhY3Rpb24ob3B0aW9ucywgZnVuY3Rpb24gKGVyciwgYWRkcmVzcykge1xuICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgICBjYWxsYmFjayhlcnIpO1xuICAgICAgICB9XG4gICAgICAgIHNlbGYuYXQoYWRkcmVzcywgY2FsbGJhY2spOyBcbiAgICB9KTsgXG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IGFjY2VzcyB0byBleGlzdGluZyBjb250cmFjdCBvbiBhIGJsb2NrY2hhaW5cbiAqXG4gKiBAbWV0aG9kIGF0XG4gKiBAcGFyYW0ge0FkZHJlc3N9IGNvbnRyYWN0IGFkZHJlc3MgKHJlcXVpcmVkKVxuICogQHBhcmFtIHtGdW5jdGlvbn0gY2FsbGJhY2sge29wdGlvbmFsKVxuICogQHJldHVybnMge0NvbnRyYWN0fSByZXR1cm5zIGNvbnRyYWN0IGlmIG5vIGNhbGxiYWNrIHdhcyBwYXNzZWQsXG4gKiBvdGhlcndpc2UgY2FsbHMgY2FsbGJhY2sgZnVuY3Rpb24gKGVyciwgY29udHJhY3QpXG4gKi9cbkNvbnRyYWN0RmFjdG9yeS5wcm90b3R5cGUuYXQgPSBmdW5jdGlvbiAoYWRkcmVzcywgY2FsbGJhY2spIHtcbiAgICAvLyBUT0RPOiBhZGRyZXNzIGlzIHJlcXVpcmVkXG4gICAgXG4gICAgaWYgKGNhbGxiYWNrKSB7XG4gICAgICAgIGNhbGxiYWNrKG51bGwsIG5ldyBDb250cmFjdCh0aGlzLmFiaSwgYWRkcmVzcykpO1xuICAgIH0gXG4gICAgcmV0dXJuIG5ldyBDb250cmFjdCh0aGlzLmFiaSwgYWRkcmVzcyk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gY3JlYXRlIG5ldyBjb250cmFjdCBpbnN0YW5jZVxuICpcbiAqIEBtZXRob2QgQ29udHJhY3RcbiAqIEBwYXJhbSB7QXJyYXl9IGFiaVxuICogQHBhcmFtIHtBZGRyZXNzfSBjb250cmFjdCBhZGRyZXNzXG4gKi9cbnZhciBDb250cmFjdCA9IGZ1bmN0aW9uIChhYmksIGFkZHJlc3MpIHtcbiAgICB0aGlzLmFkZHJlc3MgPSBhZGRyZXNzO1xuICAgIGFkZEZ1bmN0aW9uc1RvQ29udHJhY3QodGhpcywgYWJpKTtcbiAgICBhZGRFdmVudHNUb0NvbnRyYWN0KHRoaXMsIGFiaSk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IGNvbnRyYWN0O1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBAZmlsZSBkYi5qc1xuICogQGF1dGhvcnM6XG4gKiAgIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciBNZXRob2QgPSByZXF1aXJlKCcuL21ldGhvZCcpO1xuXG52YXIgcHV0U3RyaW5nID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ3B1dFN0cmluZycsXG4gICAgY2FsbDogJ2RiX3B1dFN0cmluZycsXG4gICAgcGFyYW1zOiAzXG59KTtcblxuXG52YXIgZ2V0U3RyaW5nID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2dldFN0cmluZycsXG4gICAgY2FsbDogJ2RiX2dldFN0cmluZycsXG4gICAgcGFyYW1zOiAyXG59KTtcblxudmFyIHB1dEhleCA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdwdXRIZXgnLFxuICAgIGNhbGw6ICdkYl9wdXRIZXgnLFxuICAgIHBhcmFtczogM1xufSk7XG5cbnZhciBnZXRIZXggPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0SGV4JyxcbiAgICBjYWxsOiAnZGJfZ2V0SGV4JyxcbiAgICBwYXJhbXM6IDJcbn0pO1xuXG52YXIgbWV0aG9kcyA9IFtcbiAgICBwdXRTdHJpbmcsIGdldFN0cmluZywgcHV0SGV4LCBnZXRIZXhcbl07XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIG1ldGhvZHM6IG1ldGhvZHNcbn07XG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBcbiAqIEBmaWxlIGVycm9ycy5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBJbnZhbGlkTnVtYmVyT2ZQYXJhbXM6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBFcnJvcignSW52YWxpZCBudW1iZXIgb2YgaW5wdXQgcGFyYW1ldGVycycpO1xuICAgIH0sXG4gICAgSW52YWxpZENvbm5lY3Rpb246IGZ1bmN0aW9uIChob3N0KXtcbiAgICAgICAgcmV0dXJuIG5ldyBFcnJvcignQ09OTkVDVElPTiBFUlJPUjogQ291bGRuXFwndCBjb25uZWN0IHRvIG5vZGUgJysgaG9zdCArJywgaXMgaXQgcnVubmluZz8nKTtcbiAgICB9LFxuICAgIEludmFsaWRQcm92aWRlcjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gbmV3IEVycm9yKCdQcm92aWRvciBub3Qgc2V0IG9yIGludmFsaWQnKTtcbiAgICB9LFxuICAgIEludmFsaWRSZXNwb25zZTogZnVuY3Rpb24gKHJlc3VsdCl7XG4gICAgICAgIHZhciBtZXNzYWdlID0gISFyZXN1bHQgJiYgISFyZXN1bHQuZXJyb3IgJiYgISFyZXN1bHQuZXJyb3IubWVzc2FnZSA/IHJlc3VsdC5lcnJvci5tZXNzYWdlIDogJ0ludmFsaWQgSlNPTiBSUEMgcmVzcG9uc2UnO1xuICAgICAgICByZXR1cm4gbmV3IEVycm9yKG1lc3NhZ2UpO1xuICAgIH1cbn07XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqXG4gKiBAZmlsZSBldGguanNcbiAqIEBhdXRob3IgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAYXV0aG9yIEZhYmlhbiBWb2dlbHN0ZWxsZXIgPGZhYmlhbkBldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbi8qKlxuICogV2ViM1xuICpcbiAqIEBtb2R1bGUgd2ViM1xuICovXG5cbi8qKlxuICogRXRoIG1ldGhvZHMgYW5kIHByb3BlcnRpZXNcbiAqXG4gKiBBbiBleGFtcGxlIG1ldGhvZCBvYmplY3QgY2FuIGxvb2sgYXMgZm9sbG93czpcbiAqXG4gKiAgICAgIHtcbiAqICAgICAgbmFtZTogJ2dldEJsb2NrJyxcbiAqICAgICAgY2FsbDogYmxvY2tDYWxsLFxuICogICAgICBwYXJhbXM6IDIsXG4gKiAgICAgIG91dHB1dEZvcm1hdHRlcjogZm9ybWF0dGVycy5vdXRwdXRCbG9ja0Zvcm1hdHRlcixcbiAqICAgICAgaW5wdXRGb3JtYXR0ZXI6IFsgLy8gY2FuIGJlIGEgZm9ybWF0dGVyIGZ1bmNpdG9uIG9yIGFuIGFycmF5IG9mIGZ1bmN0aW9ucy4gV2hlcmUgZWFjaCBpdGVtIGluIHRoZSBhcnJheSB3aWxsIGJlIHVzZWQgZm9yIG9uZSBwYXJhbWV0ZXJcbiAqICAgICAgICAgICB1dGlscy50b0hleCwgLy8gZm9ybWF0cyBwYXJhbXRlciAxXG4gKiAgICAgICAgICAgZnVuY3Rpb24ocGFyYW0peyByZXR1cm4gISFwYXJhbTsgfSAvLyBmb3JtYXRzIHBhcmFtdGVyIDJcbiAqICAgICAgICAgXVxuICogICAgICAgfSxcbiAqXG4gKiBAY2xhc3MgW3dlYjNdIGV0aFxuICogQGNvbnN0cnVjdG9yXG4gKi9cblxuXCJ1c2Ugc3RyaWN0XCI7XG5cbnZhciBmb3JtYXR0ZXJzID0gcmVxdWlyZSgnLi9mb3JtYXR0ZXJzJyk7XG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xudmFyIE1ldGhvZCA9IHJlcXVpcmUoJy4vbWV0aG9kJyk7XG52YXIgUHJvcGVydHkgPSByZXF1aXJlKCcuL3Byb3BlcnR5Jyk7XG5cbnZhciBibG9ja0NhbGwgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIHJldHVybiAodXRpbHMuaXNTdHJpbmcoYXJnc1swXSkgJiYgYXJnc1swXS5pbmRleE9mKCcweCcpID09PSAwKSA/IFwiZXRoX2dldEJsb2NrQnlIYXNoXCIgOiBcImV0aF9nZXRCbG9ja0J5TnVtYmVyXCI7XG59O1xuXG52YXIgdHJhbnNhY3Rpb25Gcm9tQmxvY2tDYWxsID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICByZXR1cm4gKHV0aWxzLmlzU3RyaW5nKGFyZ3NbMF0pICYmIGFyZ3NbMF0uaW5kZXhPZignMHgnKSA9PT0gMCkgPyAnZXRoX2dldFRyYW5zYWN0aW9uQnlCbG9ja0hhc2hBbmRJbmRleCcgOiAnZXRoX2dldFRyYW5zYWN0aW9uQnlCbG9ja051bWJlckFuZEluZGV4Jztcbn07XG5cbnZhciB1bmNsZUNhbGwgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIHJldHVybiAodXRpbHMuaXNTdHJpbmcoYXJnc1swXSkgJiYgYXJnc1swXS5pbmRleE9mKCcweCcpID09PSAwKSA/ICdldGhfZ2V0VW5jbGVCeUJsb2NrSGFzaEFuZEluZGV4JyA6ICdldGhfZ2V0VW5jbGVCeUJsb2NrTnVtYmVyQW5kSW5kZXgnO1xufTtcblxudmFyIGdldEJsb2NrVHJhbnNhY3Rpb25Db3VudENhbGwgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIHJldHVybiAodXRpbHMuaXNTdHJpbmcoYXJnc1swXSkgJiYgYXJnc1swXS5pbmRleE9mKCcweCcpID09PSAwKSA/ICdldGhfZ2V0QmxvY2tUcmFuc2FjdGlvbkNvdW50QnlIYXNoJyA6ICdldGhfZ2V0QmxvY2tUcmFuc2FjdGlvbkNvdW50QnlOdW1iZXInO1xufTtcblxudmFyIHVuY2xlQ291bnRDYWxsID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICByZXR1cm4gKHV0aWxzLmlzU3RyaW5nKGFyZ3NbMF0pICYmIGFyZ3NbMF0uaW5kZXhPZignMHgnKSA9PT0gMCkgPyAnZXRoX2dldFVuY2xlQ291bnRCeUJsb2NrSGFzaCcgOiAnZXRoX2dldFVuY2xlQ291bnRCeUJsb2NrTnVtYmVyJztcbn07XG5cbi8vLyBAcmV0dXJucyBhbiBhcnJheSBvZiBvYmplY3RzIGRlc2NyaWJpbmcgd2ViMy5ldGggYXBpIG1ldGhvZHNcblxudmFyIGdldEJhbGFuY2UgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0QmFsYW5jZScsXG4gICAgY2FsbDogJ2V0aF9nZXRCYWxhbmNlJyxcbiAgICBwYXJhbXM6IDIsXG4gICAgaW5wdXRGb3JtYXR0ZXI6IFt1dGlscy50b0FkZHJlc3MsIGZvcm1hdHRlcnMuaW5wdXREZWZhdWx0QmxvY2tOdW1iZXJGb3JtYXR0ZXJdLFxuICAgIG91dHB1dEZvcm1hdHRlcjogZm9ybWF0dGVycy5vdXRwdXRCaWdOdW1iZXJGb3JtYXR0ZXJcbn0pO1xuXG52YXIgZ2V0U3RvcmFnZUF0ID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2dldFN0b3JhZ2VBdCcsXG4gICAgY2FsbDogJ2V0aF9nZXRTdG9yYWdlQXQnLFxuICAgIHBhcmFtczogMyxcbiAgICBpbnB1dEZvcm1hdHRlcjogW251bGwsIHV0aWxzLnRvSGV4LCBmb3JtYXR0ZXJzLmlucHV0RGVmYXVsdEJsb2NrTnVtYmVyRm9ybWF0dGVyXVxufSk7XG5cbnZhciBnZXRDb2RlID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2dldENvZGUnLFxuICAgIGNhbGw6ICdldGhfZ2V0Q29kZScsXG4gICAgcGFyYW1zOiAyLFxuICAgIGlucHV0Rm9ybWF0dGVyOiBbdXRpbHMudG9BZGRyZXNzLCBmb3JtYXR0ZXJzLmlucHV0RGVmYXVsdEJsb2NrTnVtYmVyRm9ybWF0dGVyXVxufSk7XG5cbnZhciBnZXRCbG9jayA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdnZXRCbG9jaycsXG4gICAgY2FsbDogYmxvY2tDYWxsLFxuICAgIHBhcmFtczogMixcbiAgICBpbnB1dEZvcm1hdHRlcjogW2Zvcm1hdHRlcnMuaW5wdXRCbG9ja051bWJlckZvcm1hdHRlciwgZnVuY3Rpb24gKHZhbCkgeyByZXR1cm4gISF2YWw7IH1dLFxuICAgIG91dHB1dEZvcm1hdHRlcjogZm9ybWF0dGVycy5vdXRwdXRCbG9ja0Zvcm1hdHRlclxufSk7XG5cbnZhciBnZXRVbmNsZSA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdnZXRVbmNsZScsXG4gICAgY2FsbDogdW5jbGVDYWxsLFxuICAgIHBhcmFtczogMixcbiAgICBpbnB1dEZvcm1hdHRlcjogW2Zvcm1hdHRlcnMuaW5wdXRCbG9ja051bWJlckZvcm1hdHRlciwgdXRpbHMudG9IZXhdLFxuICAgIG91dHB1dEZvcm1hdHRlcjogZm9ybWF0dGVycy5vdXRwdXRCbG9ja0Zvcm1hdHRlcixcblxufSk7XG5cbnZhciBnZXRDb21waWxlcnMgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0Q29tcGlsZXJzJyxcbiAgICBjYWxsOiAnZXRoX2dldENvbXBpbGVycycsXG4gICAgcGFyYW1zOiAwXG59KTtcblxudmFyIGdldEJsb2NrVHJhbnNhY3Rpb25Db3VudCA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdnZXRCbG9ja1RyYW5zYWN0aW9uQ291bnQnLFxuICAgIGNhbGw6IGdldEJsb2NrVHJhbnNhY3Rpb25Db3VudENhbGwsXG4gICAgcGFyYW1zOiAxLFxuICAgIGlucHV0Rm9ybWF0dGVyOiBbZm9ybWF0dGVycy5pbnB1dEJsb2NrTnVtYmVyRm9ybWF0dGVyXSxcbiAgICBvdXRwdXRGb3JtYXR0ZXI6IHV0aWxzLnRvRGVjaW1hbFxufSk7XG5cbnZhciBnZXRCbG9ja1VuY2xlQ291bnQgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0QmxvY2tVbmNsZUNvdW50JyxcbiAgICBjYWxsOiB1bmNsZUNvdW50Q2FsbCxcbiAgICBwYXJhbXM6IDEsXG4gICAgaW5wdXRGb3JtYXR0ZXI6IFtmb3JtYXR0ZXJzLmlucHV0QmxvY2tOdW1iZXJGb3JtYXR0ZXJdLFxuICAgIG91dHB1dEZvcm1hdHRlcjogdXRpbHMudG9EZWNpbWFsXG59KTtcblxudmFyIGdldFRyYW5zYWN0aW9uID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2dldFRyYW5zYWN0aW9uJyxcbiAgICBjYWxsOiAnZXRoX2dldFRyYW5zYWN0aW9uQnlIYXNoJyxcbiAgICBwYXJhbXM6IDEsXG4gICAgb3V0cHV0Rm9ybWF0dGVyOiBmb3JtYXR0ZXJzLm91dHB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyXG59KTtcblxudmFyIGdldFRyYW5zYWN0aW9uRnJvbUJsb2NrID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2dldFRyYW5zYWN0aW9uRnJvbUJsb2NrJyxcbiAgICBjYWxsOiB0cmFuc2FjdGlvbkZyb21CbG9ja0NhbGwsXG4gICAgcGFyYW1zOiAyLFxuICAgIGlucHV0Rm9ybWF0dGVyOiBbZm9ybWF0dGVycy5pbnB1dEJsb2NrTnVtYmVyRm9ybWF0dGVyLCB1dGlscy50b0hleF0sXG4gICAgb3V0cHV0Rm9ybWF0dGVyOiBmb3JtYXR0ZXJzLm91dHB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyXG59KTtcblxudmFyIGdldFRyYW5zYWN0aW9uQ291bnQgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0VHJhbnNhY3Rpb25Db3VudCcsXG4gICAgY2FsbDogJ2V0aF9nZXRUcmFuc2FjdGlvbkNvdW50JyxcbiAgICBwYXJhbXM6IDIsXG4gICAgaW5wdXRGb3JtYXR0ZXI6IFtudWxsLCBmb3JtYXR0ZXJzLmlucHV0RGVmYXVsdEJsb2NrTnVtYmVyRm9ybWF0dGVyXSxcbiAgICBvdXRwdXRGb3JtYXR0ZXI6IHV0aWxzLnRvRGVjaW1hbFxufSk7XG5cbnZhciBzZW5kVHJhbnNhY3Rpb24gPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnc2VuZFRyYW5zYWN0aW9uJyxcbiAgICBjYWxsOiAnZXRoX3NlbmRUcmFuc2FjdGlvbicsXG4gICAgcGFyYW1zOiAxLFxuICAgIGlucHV0Rm9ybWF0dGVyOiBbZm9ybWF0dGVycy5pbnB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyXVxufSk7XG5cbnZhciBjYWxsID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2NhbGwnLFxuICAgIGNhbGw6ICdldGhfY2FsbCcsXG4gICAgcGFyYW1zOiAyLFxuICAgIGlucHV0Rm9ybWF0dGVyOiBbZm9ybWF0dGVycy5pbnB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyLCBmb3JtYXR0ZXJzLmlucHV0RGVmYXVsdEJsb2NrTnVtYmVyRm9ybWF0dGVyXVxufSk7XG5cbnZhciBlc3RpbWF0ZUdhcyA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdlc3RpbWF0ZUdhcycsXG4gICAgY2FsbDogJ2V0aF9lc3RpbWF0ZUdhcycsXG4gICAgcGFyYW1zOiAxLFxuICAgIGlucHV0Rm9ybWF0dGVyOiBbZm9ybWF0dGVycy5pbnB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyXSxcbiAgICBvdXRwdXRGb3JtYXR0ZXI6IHV0aWxzLnRvRGVjaW1hbFxufSk7XG5cbnZhciBjb21waWxlU29saWRpdHkgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnY29tcGlsZS5zb2xpZGl0eScsXG4gICAgY2FsbDogJ2V0aF9jb21waWxlU29saWRpdHknLFxuICAgIHBhcmFtczogMVxufSk7XG5cbnZhciBjb21waWxlTExMID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2NvbXBpbGUubGxsJyxcbiAgICBjYWxsOiAnZXRoX2NvbXBpbGVMTEwnLFxuICAgIHBhcmFtczogMVxufSk7XG5cbnZhciBjb21waWxlU2VycGVudCA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdjb21waWxlLnNlcnBlbnQnLFxuICAgIGNhbGw6ICdldGhfY29tcGlsZVNlcnBlbnQnLFxuICAgIHBhcmFtczogMVxufSk7XG5cbnZhciBzdWJtaXRXb3JrID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ3N1Ym1pdFdvcmsnLFxuICAgIGNhbGw6ICdldGhfc3VibWl0V29yaycsXG4gICAgcGFyYW1zOiAzXG59KTtcblxudmFyIGdldFdvcmsgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0V29yaycsXG4gICAgY2FsbDogJ2V0aF9nZXRXb3JrJyxcbiAgICBwYXJhbXM6IDBcbn0pO1xuXG52YXIgbWV0aG9kcyA9IFtcbiAgICBnZXRCYWxhbmNlLFxuICAgIGdldFN0b3JhZ2VBdCxcbiAgICBnZXRDb2RlLFxuICAgIGdldEJsb2NrLFxuICAgIGdldFVuY2xlLFxuICAgIGdldENvbXBpbGVycyxcbiAgICBnZXRCbG9ja1RyYW5zYWN0aW9uQ291bnQsXG4gICAgZ2V0QmxvY2tVbmNsZUNvdW50LFxuICAgIGdldFRyYW5zYWN0aW9uLFxuICAgIGdldFRyYW5zYWN0aW9uRnJvbUJsb2NrLFxuICAgIGdldFRyYW5zYWN0aW9uQ291bnQsXG4gICAgY2FsbCxcbiAgICBlc3RpbWF0ZUdhcyxcbiAgICBzZW5kVHJhbnNhY3Rpb24sXG4gICAgY29tcGlsZVNvbGlkaXR5LFxuICAgIGNvbXBpbGVMTEwsXG4gICAgY29tcGlsZVNlcnBlbnQsXG4gICAgc3VibWl0V29yayxcbiAgICBnZXRXb3JrXG5dO1xuXG4vLy8gQHJldHVybnMgYW4gYXJyYXkgb2Ygb2JqZWN0cyBkZXNjcmliaW5nIHdlYjMuZXRoIGFwaSBwcm9wZXJ0aWVzXG5cblxuXG52YXIgcHJvcGVydGllcyA9IFtcbiAgICBuZXcgUHJvcGVydHkoe1xuICAgICAgICBuYW1lOiAnY29pbmJhc2UnLFxuICAgICAgICBnZXR0ZXI6ICdldGhfY29pbmJhc2UnXG4gICAgfSksXG4gICAgbmV3IFByb3BlcnR5KHtcbiAgICAgICAgbmFtZTogJ21pbmluZycsXG4gICAgICAgIGdldHRlcjogJ2V0aF9taW5pbmcnXG4gICAgfSksXG4gICAgbmV3IFByb3BlcnR5KHtcbiAgICAgICAgbmFtZTogJ2hhc2hyYXRlJyxcbiAgICAgICAgZ2V0dGVyOiAnZXRoX2hhc2hyYXRlJyxcbiAgICAgICAgb3V0cHV0Rm9ybWF0dGVyOiB1dGlscy50b0RlY2ltYWxcbiAgICB9KSxcbiAgICBuZXcgUHJvcGVydHkoe1xuICAgICAgICBuYW1lOiAnZ2FzUHJpY2UnLFxuICAgICAgICBnZXR0ZXI6ICdldGhfZ2FzUHJpY2UnLFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IGZvcm1hdHRlcnMub3V0cHV0QmlnTnVtYmVyRm9ybWF0dGVyXG4gICAgfSksXG4gICAgbmV3IFByb3BlcnR5KHtcbiAgICAgICAgbmFtZTogJ2FjY291bnRzJyxcbiAgICAgICAgZ2V0dGVyOiAnZXRoX2FjY291bnRzJ1xuICAgIH0pLFxuICAgIG5ldyBQcm9wZXJ0eSh7XG4gICAgICAgIG5hbWU6ICdibG9ja051bWJlcicsXG4gICAgICAgIGdldHRlcjogJ2V0aF9ibG9ja051bWJlcicsXG4gICAgICAgIG91dHB1dEZvcm1hdHRlcjogdXRpbHMudG9EZWNpbWFsXG4gICAgfSlcbl07XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIG1ldGhvZHM6IG1ldGhvZHMsXG4gICAgcHJvcGVydGllczogcHJvcGVydGllc1xufTtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSBldmVudC5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTRcbiAqL1xuXG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xudmFyIGNvZGVyID0gcmVxdWlyZSgnLi4vc29saWRpdHkvY29kZXInKTtcbnZhciB3ZWIzID0gcmVxdWlyZSgnLi4vd2ViMycpO1xudmFyIGZvcm1hdHRlcnMgPSByZXF1aXJlKCcuL2Zvcm1hdHRlcnMnKTtcbnZhciBzaGEzID0gcmVxdWlyZSgnLi4vdXRpbHMvc2hhMycpO1xuXG4vKipcbiAqIFRoaXMgcHJvdG90eXBlIHNob3VsZCBiZSB1c2VkIHRvIGNyZWF0ZSBldmVudCBmaWx0ZXJzXG4gKi9cbnZhciBTb2xpZGl0eUV2ZW50ID0gZnVuY3Rpb24gKGpzb24sIGFkZHJlc3MpIHtcbiAgICB0aGlzLl9wYXJhbXMgPSBqc29uLmlucHV0cztcbiAgICB0aGlzLl9uYW1lID0gdXRpbHMudHJhbnNmb3JtVG9GdWxsTmFtZShqc29uKTtcbiAgICB0aGlzLl9hZGRyZXNzID0gYWRkcmVzcztcbiAgICB0aGlzLl9hbm9ueW1vdXMgPSBqc29uLmFub255bW91cztcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZ2V0IGZpbHRlcmVkIHBhcmFtIHR5cGVzXG4gKlxuICogQG1ldGhvZCB0eXBlc1xuICogQHBhcmFtIHtCb29sfSBkZWNpZGUgaWYgcmV0dXJuZWQgdHlwZWQgc2hvdWxkIGJlIGluZGV4ZWRcbiAqIEByZXR1cm4ge0FycmF5fSBhcnJheSBvZiB0eXBlc1xuICovXG5Tb2xpZGl0eUV2ZW50LnByb3RvdHlwZS50eXBlcyA9IGZ1bmN0aW9uIChpbmRleGVkKSB7XG4gICAgcmV0dXJuIHRoaXMuX3BhcmFtcy5maWx0ZXIoZnVuY3Rpb24gKGkpIHtcbiAgICAgICAgcmV0dXJuIGkuaW5kZXhlZCA9PT0gaW5kZXhlZDtcbiAgICB9KS5tYXAoZnVuY3Rpb24gKGkpIHtcbiAgICAgICAgcmV0dXJuIGkudHlwZTtcbiAgICB9KTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZ2V0IGV2ZW50IGRpc3BsYXkgbmFtZVxuICpcbiAqIEBtZXRob2QgZGlzcGxheU5hbWVcbiAqIEByZXR1cm4ge1N0cmluZ30gZXZlbnQgZGlzcGxheSBuYW1lXG4gKi9cblNvbGlkaXR5RXZlbnQucHJvdG90eXBlLmRpc3BsYXlOYW1lID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB1dGlscy5leHRyYWN0RGlzcGxheU5hbWUodGhpcy5fbmFtZSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGdldCBldmVudCB0eXBlIG5hbWVcbiAqXG4gKiBAbWV0aG9kIHR5cGVOYW1lXG4gKiBAcmV0dXJuIHtTdHJpbmd9IGV2ZW50IHR5cGUgbmFtZVxuICovXG5Tb2xpZGl0eUV2ZW50LnByb3RvdHlwZS50eXBlTmFtZSA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gdXRpbHMuZXh0cmFjdFR5cGVOYW1lKHRoaXMuX25hbWUpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBnZXQgZXZlbnQgc2lnbmF0dXJlXG4gKlxuICogQG1ldGhvZCBzaWduYXR1cmVcbiAqIEByZXR1cm4ge1N0cmluZ30gZXZlbnQgc2lnbmF0dXJlXG4gKi9cblNvbGlkaXR5RXZlbnQucHJvdG90eXBlLnNpZ25hdHVyZSA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gc2hhMyh0aGlzLl9uYW1lKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZW5jb2RlIGluZGV4ZWQgcGFyYW1zIGFuZCBvcHRpb25zIHRvIG9uZSBmaW5hbCBvYmplY3RcbiAqIFxuICogQG1ldGhvZCBlbmNvZGVcbiAqIEBwYXJhbSB7T2JqZWN0fSBpbmRleGVkXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9uc1xuICogQHJldHVybiB7T2JqZWN0fSBldmVyeXRoaW5nIGNvbWJpbmVkIHRvZ2V0aGVyIGFuZCBlbmNvZGVkXG4gKi9cblNvbGlkaXR5RXZlbnQucHJvdG90eXBlLmVuY29kZSA9IGZ1bmN0aW9uIChpbmRleGVkLCBvcHRpb25zKSB7XG4gICAgaW5kZXhlZCA9IGluZGV4ZWQgfHwge307XG4gICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG4gICAgdmFyIHJlc3VsdCA9IHt9O1xuXG4gICAgWydmcm9tQmxvY2snLCAndG9CbG9jayddLmZpbHRlcihmdW5jdGlvbiAoZikge1xuICAgICAgICByZXR1cm4gb3B0aW9uc1tmXSAhPT0gdW5kZWZpbmVkO1xuICAgIH0pLmZvckVhY2goZnVuY3Rpb24gKGYpIHtcbiAgICAgICAgcmVzdWx0W2ZdID0gZm9ybWF0dGVycy5pbnB1dEJsb2NrTnVtYmVyRm9ybWF0dGVyKG9wdGlvbnNbZl0pO1xuICAgIH0pO1xuXG4gICAgcmVzdWx0LnRvcGljcyA9IFtdO1xuXG4gICAgaWYgKCF0aGlzLl9hbm9ueW1vdXMpIHtcbiAgICAgICAgcmVzdWx0LmFkZHJlc3MgPSB0aGlzLl9hZGRyZXNzO1xuICAgICAgICByZXN1bHQudG9waWNzLnB1c2goJzB4JyArIHRoaXMuc2lnbmF0dXJlKCkpO1xuICAgIH1cblxuICAgIHZhciBpbmRleGVkVG9waWNzID0gdGhpcy5fcGFyYW1zLmZpbHRlcihmdW5jdGlvbiAoaSkge1xuICAgICAgICByZXR1cm4gaS5pbmRleGVkID09PSB0cnVlO1xuICAgIH0pLm1hcChmdW5jdGlvbiAoaSkge1xuICAgICAgICB2YXIgdmFsdWUgPSBpbmRleGVkW2kubmFtZV07XG4gICAgICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8IHZhbHVlID09PSBudWxsKSB7XG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgaWYgKHV0aWxzLmlzQXJyYXkodmFsdWUpKSB7XG4gICAgICAgICAgICByZXR1cm4gdmFsdWUubWFwKGZ1bmN0aW9uICh2KSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuICcweCcgKyBjb2Rlci5lbmNvZGVQYXJhbShpLnR5cGUsIHYpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuICcweCcgKyBjb2Rlci5lbmNvZGVQYXJhbShpLnR5cGUsIHZhbHVlKTtcbiAgICB9KTtcblxuICAgIHJlc3VsdC50b3BpY3MgPSByZXN1bHQudG9waWNzLmNvbmNhdChpbmRleGVkVG9waWNzKTtcblxuICAgIHJldHVybiByZXN1bHQ7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGRlY29kZSBpbmRleGVkIHBhcmFtcyBhbmQgb3B0aW9uc1xuICpcbiAqIEBtZXRob2QgZGVjb2RlXG4gKiBAcGFyYW0ge09iamVjdH0gZGF0YVxuICogQHJldHVybiB7T2JqZWN0fSByZXN1bHQgb2JqZWN0IHdpdGggZGVjb2RlZCBpbmRleGVkICYmIG5vdCBpbmRleGVkIHBhcmFtc1xuICovXG5Tb2xpZGl0eUV2ZW50LnByb3RvdHlwZS5kZWNvZGUgPSBmdW5jdGlvbiAoZGF0YSkge1xuIFxuICAgIGRhdGEuZGF0YSA9IGRhdGEuZGF0YSB8fCAnJztcbiAgICBkYXRhLnRvcGljcyA9IGRhdGEudG9waWNzIHx8IFtdO1xuXG4gICAgdmFyIGFyZ1RvcGljcyA9IHRoaXMuX2Fub255bW91cyA/IGRhdGEudG9waWNzIDogZGF0YS50b3BpY3Muc2xpY2UoMSk7XG4gICAgdmFyIGluZGV4ZWREYXRhID0gYXJnVG9waWNzLm1hcChmdW5jdGlvbiAodG9waWNzKSB7IHJldHVybiB0b3BpY3Muc2xpY2UoMik7IH0pLmpvaW4oXCJcIik7XG4gICAgdmFyIGluZGV4ZWRQYXJhbXMgPSBjb2Rlci5kZWNvZGVQYXJhbXModGhpcy50eXBlcyh0cnVlKSwgaW5kZXhlZERhdGEpOyBcblxuICAgIHZhciBub3RJbmRleGVkRGF0YSA9IGRhdGEuZGF0YS5zbGljZSgyKTtcbiAgICB2YXIgbm90SW5kZXhlZFBhcmFtcyA9IGNvZGVyLmRlY29kZVBhcmFtcyh0aGlzLnR5cGVzKGZhbHNlKSwgbm90SW5kZXhlZERhdGEpO1xuICAgIFxuICAgIHZhciByZXN1bHQgPSBmb3JtYXR0ZXJzLm91dHB1dExvZ0Zvcm1hdHRlcihkYXRhKTtcbiAgICByZXN1bHQuZXZlbnQgPSB0aGlzLmRpc3BsYXlOYW1lKCk7XG4gICAgcmVzdWx0LmFkZHJlc3MgPSBkYXRhLmFkZHJlc3M7XG5cbiAgICByZXN1bHQuYXJncyA9IHRoaXMuX3BhcmFtcy5yZWR1Y2UoZnVuY3Rpb24gKGFjYywgY3VycmVudCkge1xuICAgICAgICBhY2NbY3VycmVudC5uYW1lXSA9IGN1cnJlbnQuaW5kZXhlZCA/IGluZGV4ZWRQYXJhbXMuc2hpZnQoKSA6IG5vdEluZGV4ZWRQYXJhbXMuc2hpZnQoKTtcbiAgICAgICAgcmV0dXJuIGFjYztcbiAgICB9LCB7fSk7XG5cbiAgICBkZWxldGUgcmVzdWx0LmRhdGE7XG4gICAgZGVsZXRlIHJlc3VsdC50b3BpY3M7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBjcmVhdGUgbmV3IGZpbHRlciBvYmplY3QgZnJvbSBldmVudFxuICpcbiAqIEBtZXRob2QgZXhlY3V0ZVxuICogQHBhcmFtIHtPYmplY3R9IGluZGV4ZWRcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zXG4gKiBAcmV0dXJuIHtPYmplY3R9IGZpbHRlciBvYmplY3RcbiAqL1xuU29saWRpdHlFdmVudC5wcm90b3R5cGUuZXhlY3V0ZSA9IGZ1bmN0aW9uIChpbmRleGVkLCBvcHRpb25zKSB7XG4gICAgdmFyIG8gPSB0aGlzLmVuY29kZShpbmRleGVkLCBvcHRpb25zKTtcbiAgICB2YXIgZm9ybWF0dGVyID0gdGhpcy5kZWNvZGUuYmluZCh0aGlzKTtcbiAgICByZXR1cm4gd2ViMy5ldGguZmlsdGVyKG8sIHVuZGVmaW5lZCwgdW5kZWZpbmVkLCBmb3JtYXR0ZXIpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBhdHRhY2ggZXZlbnQgdG8gY29udHJhY3Qgb2JqZWN0XG4gKlxuICogQG1ldGhvZCBhdHRhY2hUb0NvbnRyYWN0XG4gKiBAcGFyYW0ge0NvbnRyYWN0fVxuICovXG5Tb2xpZGl0eUV2ZW50LnByb3RvdHlwZS5hdHRhY2hUb0NvbnRyYWN0ID0gZnVuY3Rpb24gKGNvbnRyYWN0KSB7XG4gICAgdmFyIGV4ZWN1dGUgPSB0aGlzLmV4ZWN1dGUuYmluZCh0aGlzKTtcbiAgICB2YXIgZGlzcGxheU5hbWUgPSB0aGlzLmRpc3BsYXlOYW1lKCk7XG4gICAgaWYgKCFjb250cmFjdFtkaXNwbGF5TmFtZV0pIHtcbiAgICAgICAgY29udHJhY3RbZGlzcGxheU5hbWVdID0gZXhlY3V0ZTtcbiAgICB9XG4gICAgY29udHJhY3RbZGlzcGxheU5hbWVdW3RoaXMudHlwZU5hbWUoKV0gPSB0aGlzLmV4ZWN1dGUuYmluZCh0aGlzLCBjb250cmFjdCk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFNvbGlkaXR5RXZlbnQ7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIEBmaWxlIGZpbHRlci5qc1xuICogQGF1dGhvcnM6XG4gKiAgIEplZmZyZXkgV2lsY2tlIDxqZWZmQGV0aGRldi5jb20+XG4gKiAgIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogICBNYXJpYW4gT2FuY2VhIDxtYXJpYW5AZXRoZGV2LmNvbT5cbiAqICAgRmFiaWFuIFZvZ2Vsc3RlbGxlciA8ZmFiaWFuQGV0aGRldi5jb20+XG4gKiAgIEdhdiBXb29kIDxnQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE0XG4gKi9cblxudmFyIFJlcXVlc3RNYW5hZ2VyID0gcmVxdWlyZSgnLi9yZXF1ZXN0bWFuYWdlcicpO1xudmFyIGZvcm1hdHRlcnMgPSByZXF1aXJlKCcuL2Zvcm1hdHRlcnMnKTtcbnZhciB1dGlscyA9IHJlcXVpcmUoJy4uL3V0aWxzL3V0aWxzJyk7XG5cbi8qKlxuKiBDb252ZXJ0cyBhIGdpdmVuIHRvcGljIHRvIGEgaGV4IHN0cmluZywgYnV0IGFsc28gYWxsb3dzIG51bGwgdmFsdWVzLlxuKlxuKiBAcGFyYW0ge01peGVkfSB2YWx1ZVxuKiBAcmV0dXJuIHtTdHJpbmd9XG4qL1xudmFyIHRvVG9waWMgPSBmdW5jdGlvbih2YWx1ZSl7XG5cbiAgICBpZih2YWx1ZSA9PT0gbnVsbCB8fCB0eXBlb2YgdmFsdWUgPT09ICd1bmRlZmluZWQnKVxuICAgICAgICByZXR1cm4gbnVsbDtcblxuICAgIHZhbHVlID0gU3RyaW5nKHZhbHVlKTtcblxuICAgIGlmKHZhbHVlLmluZGV4T2YoJzB4JykgPT09IDApXG4gICAgICAgIHJldHVybiB2YWx1ZTtcbiAgICBlbHNlXG4gICAgICAgIHJldHVybiB1dGlscy5mcm9tQXNjaWkodmFsdWUpO1xufTtcblxuLy8vIFRoaXMgbWV0aG9kIHNob3VsZCBiZSBjYWxsZWQgb24gb3B0aW9ucyBvYmplY3QsIHRvIHZlcmlmeSBkZXByZWNhdGVkIHByb3BlcnRpZXMgJiYgbGF6eSBsb2FkIGR5bmFtaWMgb25lc1xuLy8vIEBwYXJhbSBzaG91bGQgYmUgc3RyaW5nIG9yIG9iamVjdFxuLy8vIEByZXR1cm5zIG9wdGlvbnMgc3RyaW5nIG9yIG9iamVjdFxudmFyIGdldE9wdGlvbnMgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuXG4gICAgaWYgKHV0aWxzLmlzU3RyaW5nKG9wdGlvbnMpKSB7XG4gICAgICAgIHJldHVybiBvcHRpb25zO1xuICAgIH0gXG5cbiAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcblxuICAgIC8vIG1ha2Ugc3VyZSB0b3BpY3MsIGdldCBjb252ZXJ0ZWQgdG8gaGV4XG4gICAgb3B0aW9ucy50b3BpY3MgPSBvcHRpb25zLnRvcGljcyB8fCBbXTtcbiAgICBvcHRpb25zLnRvcGljcyA9IG9wdGlvbnMudG9waWNzLm1hcChmdW5jdGlvbih0b3BpYyl7XG4gICAgICAgIHJldHVybiAodXRpbHMuaXNBcnJheSh0b3BpYykpID8gdG9waWMubWFwKHRvVG9waWMpIDogdG9Ub3BpYyh0b3BpYyk7XG4gICAgfSk7XG5cbiAgICAvLyBsYXp5IGxvYWRcbiAgICByZXR1cm4ge1xuICAgICAgICB0b3BpY3M6IG9wdGlvbnMudG9waWNzLFxuICAgICAgICB0bzogb3B0aW9ucy50byxcbiAgICAgICAgYWRkcmVzczogb3B0aW9ucy5hZGRyZXNzLFxuICAgICAgICBmcm9tQmxvY2s6IGZvcm1hdHRlcnMuaW5wdXRCbG9ja051bWJlckZvcm1hdHRlcihvcHRpb25zLmZyb21CbG9jayksXG4gICAgICAgIHRvQmxvY2s6IGZvcm1hdHRlcnMuaW5wdXRCbG9ja051bWJlckZvcm1hdHRlcihvcHRpb25zLnRvQmxvY2spIFxuICAgIH07IFxufTtcblxudmFyIEZpbHRlciA9IGZ1bmN0aW9uIChvcHRpb25zLCBtZXRob2RzLCBmb3JtYXR0ZXIpIHtcbiAgICB2YXIgaW1wbGVtZW50YXRpb24gPSB7fTtcbiAgICBtZXRob2RzLmZvckVhY2goZnVuY3Rpb24gKG1ldGhvZCkge1xuICAgICAgICBtZXRob2QuYXR0YWNoVG9PYmplY3QoaW1wbGVtZW50YXRpb24pO1xuICAgIH0pO1xuICAgIHRoaXMub3B0aW9ucyA9IGdldE9wdGlvbnMob3B0aW9ucyk7XG4gICAgdGhpcy5pbXBsZW1lbnRhdGlvbiA9IGltcGxlbWVudGF0aW9uO1xuICAgIHRoaXMuY2FsbGJhY2tzID0gW107XG4gICAgdGhpcy5mb3JtYXR0ZXIgPSBmb3JtYXR0ZXI7XG4gICAgdGhpcy5maWx0ZXJJZCA9IHRoaXMuaW1wbGVtZW50YXRpb24ubmV3RmlsdGVyKHRoaXMub3B0aW9ucyk7XG59O1xuXG5GaWx0ZXIucHJvdG90eXBlLndhdGNoID0gZnVuY3Rpb24gKGNhbGxiYWNrKSB7XG4gICAgdGhpcy5jYWxsYmFja3MucHVzaChjYWxsYmFjayk7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gICAgdmFyIG9uTWVzc2FnZSA9IGZ1bmN0aW9uIChlcnJvciwgbWVzc2FnZXMpIHtcbiAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgICByZXR1cm4gc2VsZi5jYWxsYmFja3MuZm9yRWFjaChmdW5jdGlvbiAoY2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgICBjYWxsYmFjayhlcnJvcik7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuXG4gICAgICAgIG1lc3NhZ2VzLmZvckVhY2goZnVuY3Rpb24gKG1lc3NhZ2UpIHtcbiAgICAgICAgICAgIG1lc3NhZ2UgPSBzZWxmLmZvcm1hdHRlciA/IHNlbGYuZm9ybWF0dGVyKG1lc3NhZ2UpIDogbWVzc2FnZTtcbiAgICAgICAgICAgIHNlbGYuY2FsbGJhY2tzLmZvckVhY2goZnVuY3Rpb24gKGNhbGxiYWNrKSB7XG4gICAgICAgICAgICAgICAgY2FsbGJhY2sobnVsbCwgbWVzc2FnZSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfTtcblxuICAgIC8vIGNhbGwgZ2V0RmlsdGVyTG9ncyBvbiBzdGFydFxuICAgIGlmICghdXRpbHMuaXNTdHJpbmcodGhpcy5vcHRpb25zKSkge1xuICAgICAgICB0aGlzLmdldChmdW5jdGlvbiAoZXJyLCBtZXNzYWdlcykge1xuICAgICAgICAgICAgLy8gZG9uJ3Qgc2VuZCBhbGwgdGhlIHJlc3BvbnNlcyB0byBhbGwgdGhlIHdhdGNoZXMgYWdhaW4uLi4ganVzdCB0byB0aGlzIG9uZVxuICAgICAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgICAgICAgIGNhbGxiYWNrKGVycik7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIG1lc3NhZ2VzLmZvckVhY2goZnVuY3Rpb24gKG1lc3NhZ2UpIHtcbiAgICAgICAgICAgICAgICBjYWxsYmFjayhudWxsLCBtZXNzYWdlKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBSZXF1ZXN0TWFuYWdlci5nZXRJbnN0YW5jZSgpLnN0YXJ0UG9sbGluZyh7XG4gICAgICAgIG1ldGhvZDogdGhpcy5pbXBsZW1lbnRhdGlvbi5wb2xsLmNhbGwsXG4gICAgICAgIHBhcmFtczogW3RoaXMuZmlsdGVySWRdLFxuICAgIH0sIHRoaXMuZmlsdGVySWQsIG9uTWVzc2FnZSwgdGhpcy5zdG9wV2F0Y2hpbmcuYmluZCh0aGlzKSk7XG59O1xuXG5GaWx0ZXIucHJvdG90eXBlLnN0b3BXYXRjaGluZyA9IGZ1bmN0aW9uICgpIHtcbiAgICBSZXF1ZXN0TWFuYWdlci5nZXRJbnN0YW5jZSgpLnN0b3BQb2xsaW5nKHRoaXMuZmlsdGVySWQpO1xuICAgIHRoaXMuaW1wbGVtZW50YXRpb24udW5pbnN0YWxsRmlsdGVyKHRoaXMuZmlsdGVySWQpO1xuICAgIHRoaXMuY2FsbGJhY2tzID0gW107XG59O1xuXG5GaWx0ZXIucHJvdG90eXBlLmdldCA9IGZ1bmN0aW9uIChjYWxsYmFjaykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBpZiAodXRpbHMuaXNGdW5jdGlvbihjYWxsYmFjaykpIHtcbiAgICAgICAgdGhpcy5pbXBsZW1lbnRhdGlvbi5nZXRMb2dzKHRoaXMuZmlsdGVySWQsIGZ1bmN0aW9uKGVyciwgcmVzKXtcbiAgICAgICAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgICAgICAgICBjYWxsYmFjayhlcnIpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBjYWxsYmFjayhudWxsLCByZXMubWFwKGZ1bmN0aW9uIChsb2cpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHNlbGYuZm9ybWF0dGVyID8gc2VsZi5mb3JtYXR0ZXIobG9nKSA6IGxvZztcbiAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHZhciBsb2dzID0gdGhpcy5pbXBsZW1lbnRhdGlvbi5nZXRMb2dzKHRoaXMuZmlsdGVySWQpO1xuICAgICAgICByZXR1cm4gbG9ncy5tYXAoZnVuY3Rpb24gKGxvZykge1xuICAgICAgICAgICAgcmV0dXJuIHNlbGYuZm9ybWF0dGVyID8gc2VsZi5mb3JtYXR0ZXIobG9nKSA6IGxvZztcbiAgICAgICAgfSk7XG4gICAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBGaWx0ZXI7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIFxuICogQGZpbGUgZm9ybWF0dGVycy5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBhdXRob3IgRmFiaWFuIFZvZ2Vsc3RlbGxlciA8ZmFiaWFuQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE1XG4gKi9cblxudmFyIHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMvdXRpbHMnKTtcbnZhciBjb25maWcgPSByZXF1aXJlKCcuLi91dGlscy9jb25maWcnKTtcblxuLyoqXG4gKiBTaG91bGQgdGhlIGZvcm1hdCBvdXRwdXQgdG8gYSBiaWcgbnVtYmVyXG4gKlxuICogQG1ldGhvZCBvdXRwdXRCaWdOdW1iZXJGb3JtYXR0ZXJcbiAqIEBwYXJhbSB7U3RyaW5nfE51bWJlcnxCaWdOdW1iZXJ9XG4gKiBAcmV0dXJucyB7QmlnTnVtYmVyfSBvYmplY3RcbiAqL1xudmFyIG91dHB1dEJpZ051bWJlckZvcm1hdHRlciA9IGZ1bmN0aW9uIChudW1iZXIpIHtcbiAgICByZXR1cm4gdXRpbHMudG9CaWdOdW1iZXIobnVtYmVyKTtcbn07XG5cbnZhciBpc1ByZWRlZmluZWRCbG9ja051bWJlciA9IGZ1bmN0aW9uIChibG9ja051bWJlcikge1xuICAgIHJldHVybiBibG9ja051bWJlciA9PT0gJ2xhdGVzdCcgfHwgYmxvY2tOdW1iZXIgPT09ICdwZW5kaW5nJyB8fCBibG9ja051bWJlciA9PT0gJ2VhcmxpZXN0Jztcbn07XG5cbnZhciBpbnB1dERlZmF1bHRCbG9ja051bWJlckZvcm1hdHRlciA9IGZ1bmN0aW9uIChibG9ja051bWJlcikge1xuICAgIGlmIChibG9ja051bWJlciA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBjb25maWcuZGVmYXVsdEJsb2NrO1xuICAgIH1cbiAgICByZXR1cm4gaW5wdXRCbG9ja051bWJlckZvcm1hdHRlcihibG9ja051bWJlcik7XG59O1xuXG52YXIgaW5wdXRCbG9ja051bWJlckZvcm1hdHRlciA9IGZ1bmN0aW9uIChibG9ja051bWJlcikge1xuICAgIGlmIChibG9ja051bWJlciA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfSBlbHNlIGlmIChpc1ByZWRlZmluZWRCbG9ja051bWJlcihibG9ja051bWJlcikpIHtcbiAgICAgICAgcmV0dXJuIGJsb2NrTnVtYmVyO1xuICAgIH1cbiAgICByZXR1cm4gdXRpbHMudG9IZXgoYmxvY2tOdW1iZXIpO1xufTtcblxuLyoqXG4gKiBGb3JtYXRzIHRoZSBpbnB1dCBvZiBhIHRyYW5zYWN0aW9uIGFuZCBjb252ZXJ0cyBhbGwgdmFsdWVzIHRvIEhFWFxuICpcbiAqIEBtZXRob2QgaW5wdXRUcmFuc2FjdGlvbkZvcm1hdHRlclxuICogQHBhcmFtIHtPYmplY3R9IHRyYW5zYWN0aW9uIG9wdGlvbnNcbiAqIEByZXR1cm5zIG9iamVjdFxuKi9cbnZhciBpbnB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyID0gZnVuY3Rpb24gKG9wdGlvbnMpe1xuXG4gICAgb3B0aW9ucy5mcm9tID0gb3B0aW9ucy5mcm9tIHx8IGNvbmZpZy5kZWZhdWx0QWNjb3VudDtcblxuICAgIC8vIG1ha2UgY29kZSAtPiBkYXRhXG4gICAgaWYgKG9wdGlvbnMuY29kZSkge1xuICAgICAgICBvcHRpb25zLmRhdGEgPSBvcHRpb25zLmNvZGU7XG4gICAgICAgIGRlbGV0ZSBvcHRpb25zLmNvZGU7XG4gICAgfVxuXG4gICAgWydnYXNQcmljZScsICdnYXMnLCAndmFsdWUnLCAnbm9uY2UnXS5maWx0ZXIoZnVuY3Rpb24gKGtleSkge1xuICAgICAgICByZXR1cm4gb3B0aW9uc1trZXldICE9PSB1bmRlZmluZWQ7XG4gICAgfSkuZm9yRWFjaChmdW5jdGlvbihrZXkpe1xuICAgICAgICBvcHRpb25zW2tleV0gPSB1dGlscy5mcm9tRGVjaW1hbChvcHRpb25zW2tleV0pO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIG9wdGlvbnM7IFxufTtcblxuLyoqXG4gKiBGb3JtYXRzIHRoZSBvdXRwdXQgb2YgYSB0cmFuc2FjdGlvbiB0byBpdHMgcHJvcGVyIHZhbHVlc1xuICogXG4gKiBAbWV0aG9kIG91dHB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyXG4gKiBAcGFyYW0ge09iamVjdH0gdHJhbnNhY3Rpb25cbiAqIEByZXR1cm5zIHtPYmplY3R9IHRyYW5zYWN0aW9uXG4qL1xudmFyIG91dHB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyID0gZnVuY3Rpb24gKHR4KXtcbiAgICB0eC5ibG9ja051bWJlciA9IHV0aWxzLnRvRGVjaW1hbCh0eC5ibG9ja051bWJlcik7XG4gICAgdHgudHJhbnNhY3Rpb25JbmRleCA9IHV0aWxzLnRvRGVjaW1hbCh0eC50cmFuc2FjdGlvbkluZGV4KTtcbiAgICB0eC5ub25jZSA9IHV0aWxzLnRvRGVjaW1hbCh0eC5ub25jZSk7XG4gICAgdHguZ2FzID0gdXRpbHMudG9EZWNpbWFsKHR4Lmdhcyk7XG4gICAgdHguZ2FzUHJpY2UgPSB1dGlscy50b0JpZ051bWJlcih0eC5nYXNQcmljZSk7XG4gICAgdHgudmFsdWUgPSB1dGlscy50b0JpZ051bWJlcih0eC52YWx1ZSk7XG4gICAgcmV0dXJuIHR4O1xufTtcblxuLyoqXG4gKiBGb3JtYXRzIHRoZSBvdXRwdXQgb2YgYSBibG9jayB0byBpdHMgcHJvcGVyIHZhbHVlc1xuICpcbiAqIEBtZXRob2Qgb3V0cHV0QmxvY2tGb3JtYXR0ZXJcbiAqIEBwYXJhbSB7T2JqZWN0fSBibG9jayBvYmplY3QgXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBibG9jayBvYmplY3RcbiovXG52YXIgb3V0cHV0QmxvY2tGb3JtYXR0ZXIgPSBmdW5jdGlvbihibG9jaykge1xuXG4gICAgLy8gdHJhbnNmb3JtIHRvIG51bWJlclxuICAgIGJsb2NrLmdhc0xpbWl0ID0gdXRpbHMudG9EZWNpbWFsKGJsb2NrLmdhc0xpbWl0KTtcbiAgICBibG9jay5nYXNVc2VkID0gdXRpbHMudG9EZWNpbWFsKGJsb2NrLmdhc1VzZWQpO1xuICAgIGJsb2NrLnNpemUgPSB1dGlscy50b0RlY2ltYWwoYmxvY2suc2l6ZSk7XG4gICAgYmxvY2sudGltZXN0YW1wID0gdXRpbHMudG9EZWNpbWFsKGJsb2NrLnRpbWVzdGFtcCk7XG4gICAgYmxvY2subnVtYmVyID0gdXRpbHMudG9EZWNpbWFsKGJsb2NrLm51bWJlcik7XG5cbiAgICBibG9jay5kaWZmaWN1bHR5ID0gdXRpbHMudG9CaWdOdW1iZXIoYmxvY2suZGlmZmljdWx0eSk7XG4gICAgYmxvY2sudG90YWxEaWZmaWN1bHR5ID0gdXRpbHMudG9CaWdOdW1iZXIoYmxvY2sudG90YWxEaWZmaWN1bHR5KTtcblxuICAgIGlmICh1dGlscy5pc0FycmF5KGJsb2NrLnRyYW5zYWN0aW9ucykpIHtcbiAgICAgICAgYmxvY2sudHJhbnNhY3Rpb25zLmZvckVhY2goZnVuY3Rpb24oaXRlbSl7XG4gICAgICAgICAgICBpZighdXRpbHMuaXNTdHJpbmcoaXRlbSkpXG4gICAgICAgICAgICAgICAgcmV0dXJuIG91dHB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyKGl0ZW0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gYmxvY2s7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgdGhlIG91dHB1dCBvZiBhIGxvZ1xuICogXG4gKiBAbWV0aG9kIG91dHB1dExvZ0Zvcm1hdHRlclxuICogQHBhcmFtIHtPYmplY3R9IGxvZyBvYmplY3RcbiAqIEByZXR1cm5zIHtPYmplY3R9IGxvZ1xuKi9cbnZhciBvdXRwdXRMb2dGb3JtYXR0ZXIgPSBmdW5jdGlvbihsb2cpIHtcbiAgICBpZiAobG9nID09PSBudWxsKSB7IC8vICdwZW5kaW5nJyAmJiAnbGF0ZXN0JyBmaWx0ZXJzIGFyZSBudWxsc1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBsb2cuYmxvY2tOdW1iZXIgPSB1dGlscy50b0RlY2ltYWwobG9nLmJsb2NrTnVtYmVyKTtcbiAgICBsb2cudHJhbnNhY3Rpb25JbmRleCA9IHV0aWxzLnRvRGVjaW1hbChsb2cudHJhbnNhY3Rpb25JbmRleCk7XG4gICAgbG9nLmxvZ0luZGV4ID0gdXRpbHMudG9EZWNpbWFsKGxvZy5sb2dJbmRleCk7XG5cbiAgICByZXR1cm4gbG9nO1xufTtcblxuLyoqXG4gKiBGb3JtYXRzIHRoZSBpbnB1dCBvZiBhIHdoaXNwZXIgcG9zdCBhbmQgY29udmVydHMgYWxsIHZhbHVlcyB0byBIRVhcbiAqXG4gKiBAbWV0aG9kIGlucHV0UG9zdEZvcm1hdHRlclxuICogQHBhcmFtIHtPYmplY3R9IHRyYW5zYWN0aW9uIG9iamVjdFxuICogQHJldHVybnMge09iamVjdH1cbiovXG52YXIgaW5wdXRQb3N0Rm9ybWF0dGVyID0gZnVuY3Rpb24ocG9zdCkge1xuXG4gICAgcG9zdC5wYXlsb2FkID0gdXRpbHMudG9IZXgocG9zdC5wYXlsb2FkKTtcbiAgICBwb3N0LnR0bCA9IHV0aWxzLmZyb21EZWNpbWFsKHBvc3QudHRsKTtcbiAgICBwb3N0LndvcmtUb1Byb3ZlID0gdXRpbHMuZnJvbURlY2ltYWwocG9zdC53b3JrVG9Qcm92ZSk7XG4gICAgcG9zdC5wcmlvcml0eSA9IHV0aWxzLmZyb21EZWNpbWFsKHBvc3QucHJpb3JpdHkpO1xuXG4gICAgLy8gZmFsbGJhY2tcbiAgICBpZiAoIXV0aWxzLmlzQXJyYXkocG9zdC50b3BpY3MpKSB7XG4gICAgICAgIHBvc3QudG9waWNzID0gcG9zdC50b3BpY3MgPyBbcG9zdC50b3BpY3NdIDogW107XG4gICAgfVxuXG4gICAgLy8gZm9ybWF0IHRoZSBmb2xsb3dpbmcgb3B0aW9uc1xuICAgIHBvc3QudG9waWNzID0gcG9zdC50b3BpY3MubWFwKGZ1bmN0aW9uKHRvcGljKXtcbiAgICAgICAgcmV0dXJuIHV0aWxzLmZyb21Bc2NpaSh0b3BpYyk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gcG9zdDsgXG59O1xuXG4vKipcbiAqIEZvcm1hdHMgdGhlIG91dHB1dCBvZiBhIHJlY2VpdmVkIHBvc3QgbWVzc2FnZVxuICpcbiAqIEBtZXRob2Qgb3V0cHV0UG9zdEZvcm1hdHRlclxuICogQHBhcmFtIHtPYmplY3R9XG4gKiBAcmV0dXJucyB7T2JqZWN0fVxuICovXG52YXIgb3V0cHV0UG9zdEZvcm1hdHRlciA9IGZ1bmN0aW9uKHBvc3Qpe1xuXG4gICAgcG9zdC5leHBpcnkgPSB1dGlscy50b0RlY2ltYWwocG9zdC5leHBpcnkpO1xuICAgIHBvc3Quc2VudCA9IHV0aWxzLnRvRGVjaW1hbChwb3N0LnNlbnQpO1xuICAgIHBvc3QudHRsID0gdXRpbHMudG9EZWNpbWFsKHBvc3QudHRsKTtcbiAgICBwb3N0LndvcmtQcm92ZWQgPSB1dGlscy50b0RlY2ltYWwocG9zdC53b3JrUHJvdmVkKTtcbiAgICBwb3N0LnBheWxvYWRSYXcgPSBwb3N0LnBheWxvYWQ7XG4gICAgcG9zdC5wYXlsb2FkID0gdXRpbHMudG9Bc2NpaShwb3N0LnBheWxvYWQpO1xuXG4gICAgaWYgKHV0aWxzLmlzSnNvbihwb3N0LnBheWxvYWQpKSB7XG4gICAgICAgIHBvc3QucGF5bG9hZCA9IEpTT04ucGFyc2UocG9zdC5wYXlsb2FkKTtcbiAgICB9XG5cbiAgICAvLyBmb3JtYXQgdGhlIGZvbGxvd2luZyBvcHRpb25zXG4gICAgaWYgKCFwb3N0LnRvcGljcykge1xuICAgICAgICBwb3N0LnRvcGljcyA9IFtdO1xuICAgIH1cbiAgICBwb3N0LnRvcGljcyA9IHBvc3QudG9waWNzLm1hcChmdW5jdGlvbih0b3BpYyl7XG4gICAgICAgIHJldHVybiB1dGlscy50b0FzY2lpKHRvcGljKTtcbiAgICB9KTtcblxuICAgIHJldHVybiBwb3N0O1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgaW5wdXREZWZhdWx0QmxvY2tOdW1iZXJGb3JtYXR0ZXI6IGlucHV0RGVmYXVsdEJsb2NrTnVtYmVyRm9ybWF0dGVyLFxuICAgIGlucHV0QmxvY2tOdW1iZXJGb3JtYXR0ZXI6IGlucHV0QmxvY2tOdW1iZXJGb3JtYXR0ZXIsXG4gICAgaW5wdXRUcmFuc2FjdGlvbkZvcm1hdHRlcjogaW5wdXRUcmFuc2FjdGlvbkZvcm1hdHRlcixcbiAgICBpbnB1dFBvc3RGb3JtYXR0ZXI6IGlucHV0UG9zdEZvcm1hdHRlcixcbiAgICBvdXRwdXRCaWdOdW1iZXJGb3JtYXR0ZXI6IG91dHB1dEJpZ051bWJlckZvcm1hdHRlcixcbiAgICBvdXRwdXRUcmFuc2FjdGlvbkZvcm1hdHRlcjogb3V0cHV0VHJhbnNhY3Rpb25Gb3JtYXR0ZXIsXG4gICAgb3V0cHV0QmxvY2tGb3JtYXR0ZXI6IG91dHB1dEJsb2NrRm9ybWF0dGVyLFxuICAgIG91dHB1dExvZ0Zvcm1hdHRlcjogb3V0cHV0TG9nRm9ybWF0dGVyLFxuICAgIG91dHB1dFBvc3RGb3JtYXR0ZXI6IG91dHB1dFBvc3RGb3JtYXR0ZXJcbn07XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqXG4gKiBAZmlsZSBmdW5jdGlvbi5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgd2ViMyA9IHJlcXVpcmUoJy4uL3dlYjMnKTtcbnZhciBjb2RlciA9IHJlcXVpcmUoJy4uL3NvbGlkaXR5L2NvZGVyJyk7XG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xudmFyIHNoYTMgPSByZXF1aXJlKCcuLi91dGlscy9zaGEzJyk7XG5cbi8qKlxuICogVGhpcyBwcm90b3R5cGUgc2hvdWxkIGJlIHVzZWQgdG8gY2FsbC9zZW5kVHJhbnNhY3Rpb24gdG8gc29saWRpdHkgZnVuY3Rpb25zXG4gKi9cbnZhciBTb2xpZGl0eUZ1bmN0aW9uID0gZnVuY3Rpb24gKGpzb24sIGFkZHJlc3MpIHtcbiAgICB0aGlzLl9pbnB1dFR5cGVzID0ganNvbi5pbnB1dHMubWFwKGZ1bmN0aW9uIChpKSB7XG4gICAgICAgIHJldHVybiBpLnR5cGU7XG4gICAgfSk7XG4gICAgdGhpcy5fb3V0cHV0VHlwZXMgPSBqc29uLm91dHB1dHMubWFwKGZ1bmN0aW9uIChpKSB7XG4gICAgICAgIHJldHVybiBpLnR5cGU7XG4gICAgfSk7XG4gICAgdGhpcy5fY29uc3RhbnQgPSBqc29uLmNvbnN0YW50O1xuICAgIHRoaXMuX25hbWUgPSB1dGlscy50cmFuc2Zvcm1Ub0Z1bGxOYW1lKGpzb24pO1xuICAgIHRoaXMuX2FkZHJlc3MgPSBhZGRyZXNzO1xufTtcblxuU29saWRpdHlGdW5jdGlvbi5wcm90b3R5cGUuZXh0cmFjdENhbGxiYWNrID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICBpZiAodXRpbHMuaXNGdW5jdGlvbihhcmdzW2FyZ3MubGVuZ3RoIC0gMV0pKSB7XG4gICAgICAgIHJldHVybiBhcmdzLnBvcCgpOyAvLyBtb2RpZnkgdGhlIGFyZ3MgYXJyYXkhXG4gICAgfVxufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBjcmVhdGUgcGF5bG9hZCBmcm9tIGFyZ3VtZW50c1xuICpcbiAqIEBtZXRob2QgdG9QYXlsb2FkXG4gKiBAcGFyYW0ge0FycmF5fSBzb2xpZGl0eSBmdW5jdGlvbiBwYXJhbXNcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25hbCBwYXlsb2FkIG9wdGlvbnNcbiAqL1xuU29saWRpdHlGdW5jdGlvbi5wcm90b3R5cGUudG9QYXlsb2FkID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICB2YXIgb3B0aW9ucyA9IHt9O1xuICAgIGlmIChhcmdzLmxlbmd0aCA+IHRoaXMuX2lucHV0VHlwZXMubGVuZ3RoICYmIHV0aWxzLmlzT2JqZWN0KGFyZ3NbYXJncy5sZW5ndGggLTFdKSkge1xuICAgICAgICBvcHRpb25zID0gYXJnc1thcmdzLmxlbmd0aCAtIDFdO1xuICAgIH1cbiAgICBvcHRpb25zLnRvID0gdGhpcy5fYWRkcmVzcztcbiAgICBvcHRpb25zLmRhdGEgPSAnMHgnICsgdGhpcy5zaWduYXR1cmUoKSArIGNvZGVyLmVuY29kZVBhcmFtcyh0aGlzLl9pbnB1dFR5cGVzLCBhcmdzKTtcbiAgICByZXR1cm4gb3B0aW9ucztcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZ2V0IGZ1bmN0aW9uIHNpZ25hdHVyZVxuICpcbiAqIEBtZXRob2Qgc2lnbmF0dXJlXG4gKiBAcmV0dXJuIHtTdHJpbmd9IGZ1bmN0aW9uIHNpZ25hdHVyZVxuICovXG5Tb2xpZGl0eUZ1bmN0aW9uLnByb3RvdHlwZS5zaWduYXR1cmUgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHNoYTModGhpcy5fbmFtZSkuc2xpY2UoMCwgOCk7XG59O1xuXG5cblNvbGlkaXR5RnVuY3Rpb24ucHJvdG90eXBlLnVucGFja091dHB1dCA9IGZ1bmN0aW9uIChvdXRwdXQpIHtcbiAgICBpZiAoIW91dHB1dCkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgb3V0cHV0ID0gb3V0cHV0Lmxlbmd0aCA+PSAyID8gb3V0cHV0LnNsaWNlKDIpIDogb3V0cHV0O1xuICAgIHZhciByZXN1bHQgPSBjb2Rlci5kZWNvZGVQYXJhbXModGhpcy5fb3V0cHV0VHlwZXMsIG91dHB1dCk7XG4gICAgcmV0dXJuIHJlc3VsdC5sZW5ndGggPT09IDEgPyByZXN1bHRbMF0gOiByZXN1bHQ7XG59O1xuXG4vKipcbiAqIENhbGxzIGEgY29udHJhY3QgZnVuY3Rpb24uXG4gKlxuICogQG1ldGhvZCBjYWxsXG4gKiBAcGFyYW0gey4uLk9iamVjdH0gQ29udHJhY3QgZnVuY3Rpb24gYXJndW1lbnRzXG4gKiBAcGFyYW0ge2Z1bmN0aW9ufSBJZiB0aGUgbGFzdCBhcmd1bWVudCBpcyBhIGZ1bmN0aW9uLCB0aGUgY29udHJhY3QgZnVuY3Rpb25cbiAqICAgY2FsbCB3aWxsIGJlIGFzeW5jaHJvbm91cywgYW5kIHRoZSBjYWxsYmFjayB3aWxsIGJlIHBhc3NlZCB0aGVcbiAqICAgZXJyb3IgYW5kIHJlc3VsdC5cbiAqIEByZXR1cm4ge1N0cmluZ30gb3V0cHV0IGJ5dGVzXG4gKi9cblNvbGlkaXR5RnVuY3Rpb24ucHJvdG90eXBlLmNhbGwgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGFyZ3MgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMpLmZpbHRlcihmdW5jdGlvbiAoYSkge3JldHVybiBhICE9PSB1bmRlZmluZWQ7IH0pO1xuICAgIHZhciBjYWxsYmFjayA9IHRoaXMuZXh0cmFjdENhbGxiYWNrKGFyZ3MpO1xuICAgIHZhciBwYXlsb2FkID0gdGhpcy50b1BheWxvYWQoYXJncyk7XG5cbiAgICBpZiAoIWNhbGxiYWNrKSB7XG4gICAgICAgIHZhciBvdXRwdXQgPSB3ZWIzLmV0aC5jYWxsKHBheWxvYWQpO1xuICAgICAgICByZXR1cm4gdGhpcy51bnBhY2tPdXRwdXQob3V0cHV0KTtcbiAgICB9IFxuICAgICAgICBcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgd2ViMy5ldGguY2FsbChwYXlsb2FkLCBmdW5jdGlvbiAoZXJyb3IsIG91dHB1dCkge1xuICAgICAgICBjYWxsYmFjayhlcnJvciwgc2VsZi51bnBhY2tPdXRwdXQob3V0cHV0KSk7XG4gICAgfSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIHNlbmRUcmFuc2FjdGlvbiB0byBzb2xpZGl0eSBmdW5jdGlvblxuICpcbiAqIEBtZXRob2Qgc2VuZFRyYW5zYWN0aW9uXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9uc1xuICovXG5Tb2xpZGl0eUZ1bmN0aW9uLnByb3RvdHlwZS5zZW5kVHJhbnNhY3Rpb24gPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGFyZ3MgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMpLmZpbHRlcihmdW5jdGlvbiAoYSkge3JldHVybiBhICE9PSB1bmRlZmluZWQ7IH0pO1xuICAgIHZhciBjYWxsYmFjayA9IHRoaXMuZXh0cmFjdENhbGxiYWNrKGFyZ3MpO1xuICAgIHZhciBwYXlsb2FkID0gdGhpcy50b1BheWxvYWQoYXJncyk7XG5cbiAgICBpZiAoIWNhbGxiYWNrKSB7XG4gICAgICAgIHJldHVybiB3ZWIzLmV0aC5zZW5kVHJhbnNhY3Rpb24ocGF5bG9hZCk7XG4gICAgfVxuXG4gICAgd2ViMy5ldGguc2VuZFRyYW5zYWN0aW9uKHBheWxvYWQsIGNhbGxiYWNrKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZXN0aW1hdGVHYXMgb2Ygc29saWRpdHkgZnVuY3Rpb25cbiAqXG4gKiBAbWV0aG9kIGVzdGltYXRlR2FzXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9uc1xuICovXG5Tb2xpZGl0eUZ1bmN0aW9uLnByb3RvdHlwZS5lc3RpbWF0ZUdhcyA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cyk7XG4gICAgdmFyIGNhbGxiYWNrID0gdGhpcy5leHRyYWN0Q2FsbGJhY2soYXJncyk7XG4gICAgdmFyIHBheWxvYWQgPSB0aGlzLnRvUGF5bG9hZChhcmdzKTtcblxuICAgIGlmICghY2FsbGJhY2spIHtcbiAgICAgICAgcmV0dXJuIHdlYjMuZXRoLmVzdGltYXRlR2FzKHBheWxvYWQpO1xuICAgIH1cblxuICAgIHdlYjMuZXRoLmVzdGltYXRlR2FzKHBheWxvYWQsIGNhbGxiYWNrKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZ2V0IGZ1bmN0aW9uIGRpc3BsYXkgbmFtZVxuICpcbiAqIEBtZXRob2QgZGlzcGxheU5hbWVcbiAqIEByZXR1cm4ge1N0cmluZ30gZGlzcGxheSBuYW1lIG9mIHRoZSBmdW5jdGlvblxuICovXG5Tb2xpZGl0eUZ1bmN0aW9uLnByb3RvdHlwZS5kaXNwbGF5TmFtZSA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gdXRpbHMuZXh0cmFjdERpc3BsYXlOYW1lKHRoaXMuX25hbWUpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBnZXQgZnVuY3Rpb24gdHlwZSBuYW1lXG4gKlxuICogQG1ldGhvZCB0eXBlTmFtZVxuICogQHJldHVybiB7U3RyaW5nfSB0eXBlIG5hbWUgb2YgdGhlIGZ1bmN0aW9uXG4gKi9cblNvbGlkaXR5RnVuY3Rpb24ucHJvdG90eXBlLnR5cGVOYW1lID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB1dGlscy5leHRyYWN0VHlwZU5hbWUodGhpcy5fbmFtZSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IHJwYyByZXF1ZXN0cyBmcm9tIHNvbGlkaXR5IGZ1bmN0aW9uXG4gKlxuICogQG1ldGhvZCByZXF1ZXN0XG4gKiBAcmV0dXJucyB7T2JqZWN0fVxuICovXG5Tb2xpZGl0eUZ1bmN0aW9uLnByb3RvdHlwZS5yZXF1ZXN0ID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKTtcbiAgICB2YXIgY2FsbGJhY2sgPSB0aGlzLmV4dHJhY3RDYWxsYmFjayhhcmdzKTtcbiAgICB2YXIgcGF5bG9hZCA9IHRoaXMudG9QYXlsb2FkKGFyZ3MpO1xuICAgIHZhciBmb3JtYXQgPSB0aGlzLnVucGFja091dHB1dC5iaW5kKHRoaXMpO1xuICAgIFxuICAgIHJldHVybiB7XG4gICAgICAgIGNhbGxiYWNrOiBjYWxsYmFjayxcbiAgICAgICAgcGF5bG9hZDogcGF5bG9hZCwgXG4gICAgICAgIGZvcm1hdDogZm9ybWF0XG4gICAgfTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBleGVjdXRlIGZ1bmN0aW9uXG4gKlxuICogQG1ldGhvZCBleGVjdXRlXG4gKi9cblNvbGlkaXR5RnVuY3Rpb24ucHJvdG90eXBlLmV4ZWN1dGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIHRyYW5zYWN0aW9uID0gIXRoaXMuX2NvbnN0YW50O1xuXG4gICAgLy8gc2VuZCB0cmFuc2FjdGlvblxuICAgIGlmICh0cmFuc2FjdGlvbikge1xuICAgICAgICByZXR1cm4gdGhpcy5zZW5kVHJhbnNhY3Rpb24uYXBwbHkodGhpcywgQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKSk7XG4gICAgfVxuXG4gICAgLy8gY2FsbFxuICAgIHJldHVybiB0aGlzLmNhbGwuYXBwbHkodGhpcywgQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gYXR0YWNoIGZ1bmN0aW9uIHRvIGNvbnRyYWN0XG4gKlxuICogQG1ldGhvZCBhdHRhY2hUb0NvbnRyYWN0XG4gKiBAcGFyYW0ge0NvbnRyYWN0fVxuICovXG5Tb2xpZGl0eUZ1bmN0aW9uLnByb3RvdHlwZS5hdHRhY2hUb0NvbnRyYWN0ID0gZnVuY3Rpb24gKGNvbnRyYWN0KSB7XG4gICAgdmFyIGV4ZWN1dGUgPSB0aGlzLmV4ZWN1dGUuYmluZCh0aGlzKTtcbiAgICBleGVjdXRlLnJlcXVlc3QgPSB0aGlzLnJlcXVlc3QuYmluZCh0aGlzKTtcbiAgICBleGVjdXRlLmNhbGwgPSB0aGlzLmNhbGwuYmluZCh0aGlzKTtcbiAgICBleGVjdXRlLnNlbmRUcmFuc2FjdGlvbiA9IHRoaXMuc2VuZFRyYW5zYWN0aW9uLmJpbmQodGhpcyk7XG4gICAgZXhlY3V0ZS5lc3RpbWF0ZUdhcyA9IHRoaXMuZXN0aW1hdGVHYXMuYmluZCh0aGlzKTtcbiAgICB2YXIgZGlzcGxheU5hbWUgPSB0aGlzLmRpc3BsYXlOYW1lKCk7XG4gICAgaWYgKCFjb250cmFjdFtkaXNwbGF5TmFtZV0pIHtcbiAgICAgICAgY29udHJhY3RbZGlzcGxheU5hbWVdID0gZXhlY3V0ZTtcbiAgICB9XG4gICAgY29udHJhY3RbZGlzcGxheU5hbWVdW3RoaXMudHlwZU5hbWUoKV0gPSBleGVjdXRlOyAvLyBjaXJjdWxhciEhISFcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gU29saWRpdHlGdW5jdGlvbjtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogQGZpbGUgaHR0cHByb3ZpZGVyLmpzXG4gKiBAYXV0aG9yczpcbiAqICAgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiAgIE1hcmlhbiBPYW5jZWEgPG1hcmlhbkBldGhkZXYuY29tPlxuICogICBGYWJpYW4gVm9nZWxzdGVsbGVyIDxmYWJpYW5AZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTRcbiAqL1xuXG5cInVzZSBzdHJpY3RcIjtcblxudmFyIFhNTEh0dHBSZXF1ZXN0ID0gcmVxdWlyZSgneG1saHR0cHJlcXVlc3QnKS5YTUxIdHRwUmVxdWVzdDsgLy8ganNoaW50IGlnbm9yZTpsaW5lXG52YXIgZXJyb3JzID0gcmVxdWlyZSgnLi9lcnJvcnMnKTtcblxudmFyIEh0dHBQcm92aWRlciA9IGZ1bmN0aW9uIChob3N0KSB7XG4gICAgdGhpcy5ob3N0ID0gaG9zdCB8fCAnaHR0cDovL2xvY2FsaG9zdDo4NTQ1Jztcbn07XG5cbkh0dHBQcm92aWRlci5wcm90b3R5cGUuc2VuZCA9IGZ1bmN0aW9uIChwYXlsb2FkKSB7XG4gICAgdmFyIHJlcXVlc3QgPSBuZXcgWE1MSHR0cFJlcXVlc3QoKTtcblxuICAgIHJlcXVlc3Qub3BlbignUE9TVCcsIHRoaXMuaG9zdCwgZmFsc2UpO1xuICAgIFxuICAgIHRyeSB7XG4gICAgICAgIHJlcXVlc3Quc2VuZChKU09OLnN0cmluZ2lmeShwYXlsb2FkKSk7XG4gICAgfSBjYXRjaChlcnJvcikge1xuICAgICAgICB0aHJvdyBlcnJvcnMuSW52YWxpZENvbm5lY3Rpb24odGhpcy5ob3N0KTtcbiAgICB9XG5cblxuICAgIC8vIGNoZWNrIHJlcXVlc3Quc3RhdHVzXG4gICAgLy8gVE9ETzogdGhyb3cgYW4gZXJyb3IgaGVyZSEgaXQgY2Fubm90IHNpbGVudGx5IGZhaWwhISFcbiAgICAvL2lmIChyZXF1ZXN0LnN0YXR1cyAhPT0gMjAwKSB7XG4gICAgICAgIC8vcmV0dXJuO1xuICAgIC8vfVxuXG4gICAgdmFyIHJlc3VsdCA9IHJlcXVlc3QucmVzcG9uc2VUZXh0O1xuXG4gICAgdHJ5IHtcbiAgICAgICAgcmVzdWx0ID0gSlNPTi5wYXJzZShyZXN1bHQpO1xuICAgIH0gY2F0Y2goZSkge1xuICAgICAgICB0aHJvdyBlcnJvcnMuSW52YWxpZFJlc3BvbnNlKHJlc3VsdCk7ICAgICAgICAgICAgICAgIFxuICAgIH1cblxuICAgIHJldHVybiByZXN1bHQ7XG59O1xuXG5IdHRwUHJvdmlkZXIucHJvdG90eXBlLnNlbmRBc3luYyA9IGZ1bmN0aW9uIChwYXlsb2FkLCBjYWxsYmFjaykge1xuICAgIHZhciByZXF1ZXN0ID0gbmV3IFhNTEh0dHBSZXF1ZXN0KCk7XG4gICAgcmVxdWVzdC5vbnJlYWR5c3RhdGVjaGFuZ2UgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgaWYgKHJlcXVlc3QucmVhZHlTdGF0ZSA9PT0gNCkge1xuICAgICAgICAgICAgdmFyIHJlc3VsdCA9IHJlcXVlc3QucmVzcG9uc2VUZXh0O1xuICAgICAgICAgICAgdmFyIGVycm9yID0gbnVsbDtcblxuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICByZXN1bHQgPSBKU09OLnBhcnNlKHJlc3VsdCk7XG4gICAgICAgICAgICB9IGNhdGNoKGUpIHtcbiAgICAgICAgICAgICAgICBlcnJvciA9IGVycm9ycy5JbnZhbGlkUmVzcG9uc2UocmVzdWx0KTsgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNhbGxiYWNrKGVycm9yLCByZXN1bHQpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHJlcXVlc3Qub3BlbignUE9TVCcsIHRoaXMuaG9zdCwgdHJ1ZSk7XG5cbiAgICB0cnkge1xuICAgICAgICByZXF1ZXN0LnNlbmQoSlNPTi5zdHJpbmdpZnkocGF5bG9hZCkpO1xuICAgIH0gY2F0Y2goZXJyb3IpIHtcbiAgICAgICAgY2FsbGJhY2soZXJyb3JzLkludmFsaWRDb25uZWN0aW9uKHRoaXMuaG9zdCkpO1xuICAgIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gSHR0cFByb3ZpZGVyO1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBcbiAqIEBmaWxlIGljYXAuanNcbiAqIEBhdXRob3IgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE1XG4gKi9cblxudmFyIHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMvdXRpbHMnKTtcblxuLyoqXG4gKiBUaGlzIHByb3RvdHlwZSBzaG91bGQgYmUgdXNlZCB0byBleHRyYWN0IG5lY2Vzc2FyeSBpbmZvcm1hdGlvbiBmcm9tIGliYW4gYWRkcmVzc1xuICpcbiAqIEBwYXJhbSB7U3RyaW5nfSBpYmFuXG4gKi9cbnZhciBJQ0FQID0gZnVuY3Rpb24gKGliYW4pIHtcbiAgICB0aGlzLl9pYmFuID0gaWJhbjtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBjaGVjayBpZiBpY2FwIGlzIGNvcnJlY3RcbiAqXG4gKiBAbWV0aG9kIGlzVmFsaWRcbiAqIEByZXR1cm5zIHtCb29sZWFufSB0cnVlIGlmIGl0IGlzLCBvdGhlcndpc2UgZmFsc2VcbiAqL1xuSUNBUC5wcm90b3R5cGUuaXNWYWxpZCA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gdXRpbHMuaXNJQkFOKHRoaXMuX2liYW4pO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGNoZWNrIGlmIGliYW4gbnVtYmVyIGlzIGRpcmVjdFxuICpcbiAqIEBtZXRob2QgaXNEaXJlY3RcbiAqIEByZXR1cm5zIHtCb29sZWFufSB0cnVlIGlmIGl0IGlzLCBvdGhlcndpc2UgZmFsc2VcbiAqL1xuSUNBUC5wcm90b3R5cGUuaXNEaXJlY3QgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2liYW4ubGVuZ3RoID09PSAzNDtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBjaGVjayBpZiBpYmFuIG51bWJlciBpZiBpbmRpcmVjdFxuICpcbiAqIEBtZXRob2QgaXNJbmRpcmVjdFxuICogQHJldHVybnMge0Jvb2xlYW59IHRydWUgaWYgaXQgaXMsIG90aGVyd2lzZSBmYWxzZVxuICovXG5JQ0FQLnByb3RvdHlwZS5pc0luZGlyZWN0ID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB0aGlzLl9pYmFuLmxlbmd0aCA9PT0gMjA7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IGliYW4gY2hlY2tzdW1cbiAqIFVzZXMgdGhlIG1vZC05Ny0xMCBjaGVja3N1bW1pbmcgcHJvdG9jb2wgKElTTy9JRUMgNzA2NDoyMDAzKVxuICpcbiAqIEBtZXRob2QgY2hlY2tzdW1cbiAqIEByZXR1cm5zIHtTdHJpbmd9IGNoZWNrc3VtXG4gKi9cbklDQVAucHJvdG90eXBlLmNoZWNrc3VtID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB0aGlzLl9pYmFuLnN1YnN0cigyLCAyKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBnZXQgaW5zdGl0dXRpb24gaWRlbnRpZmllclxuICogZWcuIFhSRUdcbiAqXG4gKiBAbWV0aG9kIGluc3RpdHV0aW9uXG4gKiBAcmV0dXJucyB7U3RyaW5nfSBpbnN0aXR1dGlvbiBpZGVudGlmaWVyXG4gKi9cbklDQVAucHJvdG90eXBlLmluc3RpdHV0aW9uID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB0aGlzLmlzSW5kaXJlY3QoKSA/IHRoaXMuX2liYW4uc3Vic3RyKDcsIDQpIDogJyc7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IGNsaWVudCBpZGVudGlmaWVyIHdpdGhpbiBpbnN0aXR1dGlvblxuICogZWcuIEdBVk9GWU9SS1xuICpcbiAqIEBtZXRob2QgY2xpZW50XG4gKiBAcmV0dXJucyB7U3RyaW5nfSBjbGllbnQgaWRlbnRpZmllclxuICovXG5JQ0FQLnByb3RvdHlwZS5jbGllbnQgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuaXNJbmRpcmVjdCgpID8gdGhpcy5faWJhbi5zdWJzdHIoMTEpIDogJyc7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IGNsaWVudCBkaXJlY3QgYWRkcmVzc1xuICpcbiAqIEBtZXRob2QgYWRkcmVzc1xuICogQHJldHVybnMge1N0cmluZ30gY2xpZW50IGRpcmVjdCBhZGRyZXNzXG4gKi9cbklDQVAucHJvdG90eXBlLmFkZHJlc3MgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuaXNEaXJlY3QoKSA/IHRoaXMuX2liYW4uc3Vic3RyKDQpIDogJyc7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IElDQVA7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIEBmaWxlIGpzb25ycGMuanNcbiAqIEBhdXRob3JzOlxuICogICBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgSnNvbnJwYyA9IGZ1bmN0aW9uICgpIHtcbiAgICAvLyBzaW5nbGV0b24gcGF0dGVyblxuICAgIGlmIChhcmd1bWVudHMuY2FsbGVlLl9zaW5nbGV0b25JbnN0YW5jZSkge1xuICAgICAgICByZXR1cm4gYXJndW1lbnRzLmNhbGxlZS5fc2luZ2xldG9uSW5zdGFuY2U7XG4gICAgfVxuICAgIGFyZ3VtZW50cy5jYWxsZWUuX3NpbmdsZXRvbkluc3RhbmNlID0gdGhpcztcblxuICAgIHRoaXMubWVzc2FnZUlkID0gMTtcbn07XG5cbi8qKlxuICogQHJldHVybiB7SnNvbnJwY30gc2luZ2xldG9uXG4gKi9cbkpzb25ycGMuZ2V0SW5zdGFuY2UgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGluc3RhbmNlID0gbmV3IEpzb25ycGMoKTtcbiAgICByZXR1cm4gaW5zdGFuY2U7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gdmFsaWQganNvbiBjcmVhdGUgcGF5bG9hZCBvYmplY3RcbiAqXG4gKiBAbWV0aG9kIHRvUGF5bG9hZFxuICogQHBhcmFtIHtGdW5jdGlvbn0gbWV0aG9kIG9mIGpzb25ycGMgY2FsbCwgcmVxdWlyZWRcbiAqIEBwYXJhbSB7QXJyYXl9IHBhcmFtcywgYW4gYXJyYXkgb2YgbWV0aG9kIHBhcmFtcywgb3B0aW9uYWxcbiAqIEByZXR1cm5zIHtPYmplY3R9IHZhbGlkIGpzb25ycGMgcGF5bG9hZCBvYmplY3RcbiAqL1xuSnNvbnJwYy5wcm90b3R5cGUudG9QYXlsb2FkID0gZnVuY3Rpb24gKG1ldGhvZCwgcGFyYW1zKSB7XG4gICAgaWYgKCFtZXRob2QpXG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ2pzb25ycGMgbWV0aG9kIHNob3VsZCBiZSBzcGVjaWZpZWQhJyk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgICBqc29ucnBjOiAnMi4wJyxcbiAgICAgICAgbWV0aG9kOiBtZXRob2QsXG4gICAgICAgIHBhcmFtczogcGFyYW1zIHx8IFtdLFxuICAgICAgICBpZDogdGhpcy5tZXNzYWdlSWQrK1xuICAgIH07XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gY2hlY2sgaWYganNvbnJwYyByZXNwb25zZSBpcyB2YWxpZFxuICpcbiAqIEBtZXRob2QgaXNWYWxpZFJlc3BvbnNlXG4gKiBAcGFyYW0ge09iamVjdH1cbiAqIEByZXR1cm5zIHtCb29sZWFufSB0cnVlIGlmIHJlc3BvbnNlIGlzIHZhbGlkLCBvdGhlcndpc2UgZmFsc2VcbiAqL1xuSnNvbnJwYy5wcm90b3R5cGUuaXNWYWxpZFJlc3BvbnNlID0gZnVuY3Rpb24gKHJlc3BvbnNlKSB7XG4gICAgcmV0dXJuICEhcmVzcG9uc2UgJiZcbiAgICAgICAgIXJlc3BvbnNlLmVycm9yICYmXG4gICAgICAgIHJlc3BvbnNlLmpzb25ycGMgPT09ICcyLjAnICYmXG4gICAgICAgIHR5cGVvZiByZXNwb25zZS5pZCA9PT0gJ251bWJlcicgJiZcbiAgICAgICAgcmVzcG9uc2UucmVzdWx0ICE9PSB1bmRlZmluZWQ7IC8vIG9ubHkgdW5kZWZpbmVkIGlzIG5vdCB2YWxpZCBqc29uIG9iamVjdFxufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGNyZWF0ZSBiYXRjaCBwYXlsb2FkIG9iamVjdFxuICpcbiAqIEBtZXRob2QgdG9CYXRjaFBheWxvYWRcbiAqIEBwYXJhbSB7QXJyYXl9IG1lc3NhZ2VzLCBhbiBhcnJheSBvZiBvYmplY3RzIHdpdGggbWV0aG9kIChyZXF1aXJlZCkgYW5kIHBhcmFtcyAob3B0aW9uYWwpIGZpZWxkc1xuICogQHJldHVybnMge0FycmF5fSBiYXRjaCBwYXlsb2FkXG4gKi9cbkpzb25ycGMucHJvdG90eXBlLnRvQmF0Y2hQYXlsb2FkID0gZnVuY3Rpb24gKG1lc3NhZ2VzKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHJldHVybiBtZXNzYWdlcy5tYXAoZnVuY3Rpb24gKG1lc3NhZ2UpIHtcbiAgICAgICAgcmV0dXJuIHNlbGYudG9QYXlsb2FkKG1lc3NhZ2UubWV0aG9kLCBtZXNzYWdlLnBhcmFtcyk7XG4gICAgfSk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEpzb25ycGM7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqXG4gKiBAZmlsZSBtZXRob2QuanNcbiAqIEBhdXRob3IgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE1XG4gKi9cblxudmFyIFJlcXVlc3RNYW5hZ2VyID0gcmVxdWlyZSgnLi9yZXF1ZXN0bWFuYWdlcicpO1xudmFyIHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMvdXRpbHMnKTtcbnZhciBlcnJvcnMgPSByZXF1aXJlKCcuL2Vycm9ycycpO1xuXG52YXIgTWV0aG9kID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICB0aGlzLm5hbWUgPSBvcHRpb25zLm5hbWU7XG4gICAgdGhpcy5jYWxsID0gb3B0aW9ucy5jYWxsO1xuICAgIHRoaXMucGFyYW1zID0gb3B0aW9ucy5wYXJhbXMgfHwgMDtcbiAgICB0aGlzLmlucHV0Rm9ybWF0dGVyID0gb3B0aW9ucy5pbnB1dEZvcm1hdHRlcjtcbiAgICB0aGlzLm91dHB1dEZvcm1hdHRlciA9IG9wdGlvbnMub3V0cHV0Rm9ybWF0dGVyO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBkZXRlcm1pbmUgbmFtZSBvZiB0aGUganNvbnJwYyBtZXRob2QgYmFzZWQgb24gYXJndW1lbnRzXG4gKlxuICogQG1ldGhvZCBnZXRDYWxsXG4gKiBAcGFyYW0ge0FycmF5fSBhcmd1bWVudHNcbiAqIEByZXR1cm4ge1N0cmluZ30gbmFtZSBvZiBqc29ucnBjIG1ldGhvZFxuICovXG5NZXRob2QucHJvdG90eXBlLmdldENhbGwgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIHJldHVybiB1dGlscy5pc0Z1bmN0aW9uKHRoaXMuY2FsbCkgPyB0aGlzLmNhbGwoYXJncykgOiB0aGlzLmNhbGw7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGV4dHJhY3QgY2FsbGJhY2sgZnJvbSBhcnJheSBvZiBhcmd1bWVudHMuIE1vZGlmaWVzIGlucHV0IHBhcmFtXG4gKlxuICogQG1ldGhvZCBleHRyYWN0Q2FsbGJhY2tcbiAqIEBwYXJhbSB7QXJyYXl9IGFyZ3VtZW50c1xuICogQHJldHVybiB7RnVuY3Rpb258TnVsbH0gY2FsbGJhY2ssIGlmIGV4aXN0c1xuICovXG5NZXRob2QucHJvdG90eXBlLmV4dHJhY3RDYWxsYmFjayA9IGZ1bmN0aW9uIChhcmdzKSB7XG4gICAgaWYgKHV0aWxzLmlzRnVuY3Rpb24oYXJnc1thcmdzLmxlbmd0aCAtIDFdKSkge1xuICAgICAgICByZXR1cm4gYXJncy5wb3AoKTsgLy8gbW9kaWZ5IHRoZSBhcmdzIGFycmF5IVxuICAgIH1cbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBjaGVjayBpZiB0aGUgbnVtYmVyIG9mIGFyZ3VtZW50cyBpcyBjb3JyZWN0XG4gKiBcbiAqIEBtZXRob2QgdmFsaWRhdGVBcmdzXG4gKiBAcGFyYW0ge0FycmF5fSBhcmd1bWVudHNcbiAqIEB0aHJvd3Mge0Vycm9yfSBpZiBpdCBpcyBub3RcbiAqL1xuTWV0aG9kLnByb3RvdHlwZS52YWxpZGF0ZUFyZ3MgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIGlmIChhcmdzLmxlbmd0aCAhPT0gdGhpcy5wYXJhbXMpIHtcbiAgICAgICAgdGhyb3cgZXJyb3JzLkludmFsaWROdW1iZXJPZlBhcmFtcygpO1xuICAgIH1cbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBmb3JtYXQgaW5wdXQgYXJncyBvZiBtZXRob2RcbiAqIFxuICogQG1ldGhvZCBmb3JtYXRJbnB1dFxuICogQHBhcmFtIHtBcnJheX1cbiAqIEByZXR1cm4ge0FycmF5fVxuICovXG5NZXRob2QucHJvdG90eXBlLmZvcm1hdElucHV0ID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICBpZiAoIXRoaXMuaW5wdXRGb3JtYXR0ZXIpIHtcbiAgICAgICAgcmV0dXJuIGFyZ3M7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuaW5wdXRGb3JtYXR0ZXIubWFwKGZ1bmN0aW9uIChmb3JtYXR0ZXIsIGluZGV4KSB7XG4gICAgICAgIHJldHVybiBmb3JtYXR0ZXIgPyBmb3JtYXR0ZXIoYXJnc1tpbmRleF0pIDogYXJnc1tpbmRleF07XG4gICAgfSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZm9ybWF0IG91dHB1dChyZXN1bHQpIG9mIG1ldGhvZFxuICpcbiAqIEBtZXRob2QgZm9ybWF0T3V0cHV0XG4gKiBAcGFyYW0ge09iamVjdH1cbiAqIEByZXR1cm4ge09iamVjdH1cbiAqL1xuTWV0aG9kLnByb3RvdHlwZS5mb3JtYXRPdXRwdXQgPSBmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgcmV0dXJuIHRoaXMub3V0cHV0Rm9ybWF0dGVyICYmIHJlc3VsdCAhPT0gbnVsbCA/IHRoaXMub3V0cHV0Rm9ybWF0dGVyKHJlc3VsdCkgOiByZXN1bHQ7XG59O1xuXG4vKipcbiAqIFNob3VsZCBhdHRhY2ggZnVuY3Rpb24gdG8gbWV0aG9kXG4gKiBcbiAqIEBtZXRob2QgYXR0YWNoVG9PYmplY3RcbiAqIEBwYXJhbSB7T2JqZWN0fVxuICogQHBhcmFtIHtGdW5jdGlvbn1cbiAqL1xuTWV0aG9kLnByb3RvdHlwZS5hdHRhY2hUb09iamVjdCA9IGZ1bmN0aW9uIChvYmopIHtcbiAgICB2YXIgZnVuYyA9IHRoaXMuc2VuZC5iaW5kKHRoaXMpO1xuICAgIGZ1bmMucmVxdWVzdCA9IHRoaXMucmVxdWVzdC5iaW5kKHRoaXMpO1xuICAgIGZ1bmMuY2FsbCA9IHRoaXMuY2FsbDsgLy8gdGhhdCdzIHVnbHkuIGZpbHRlci5qcyB1c2VzIGl0XG4gICAgdmFyIG5hbWUgPSB0aGlzLm5hbWUuc3BsaXQoJy4nKTtcbiAgICBpZiAobmFtZS5sZW5ndGggPiAxKSB7XG4gICAgICAgIG9ialtuYW1lWzBdXSA9IG9ialtuYW1lWzBdXSB8fCB7fTtcbiAgICAgICAgb2JqW25hbWVbMF1dW25hbWVbMV1dID0gZnVuYztcbiAgICB9IGVsc2Uge1xuICAgICAgICBvYmpbbmFtZVswXV0gPSBmdW5jOyBcbiAgICB9XG59O1xuXG4vKipcbiAqIFNob3VsZCBjcmVhdGUgcGF5bG9hZCBmcm9tIGdpdmVuIGlucHV0IGFyZ3NcbiAqXG4gKiBAbWV0aG9kIHRvUGF5bG9hZFxuICogQHBhcmFtIHtBcnJheX0gYXJnc1xuICogQHJldHVybiB7T2JqZWN0fVxuICovXG5NZXRob2QucHJvdG90eXBlLnRvUGF5bG9hZCA9IGZ1bmN0aW9uIChhcmdzKSB7XG4gICAgdmFyIGNhbGwgPSB0aGlzLmdldENhbGwoYXJncyk7XG4gICAgdmFyIGNhbGxiYWNrID0gdGhpcy5leHRyYWN0Q2FsbGJhY2soYXJncyk7XG4gICAgdmFyIHBhcmFtcyA9IHRoaXMuZm9ybWF0SW5wdXQoYXJncyk7XG4gICAgdGhpcy52YWxpZGF0ZUFyZ3MocGFyYW1zKTtcblxuICAgIHJldHVybiB7XG4gICAgICAgIG1ldGhvZDogY2FsbCxcbiAgICAgICAgcGFyYW1zOiBwYXJhbXMsXG4gICAgICAgIGNhbGxiYWNrOiBjYWxsYmFja1xuICAgIH07XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gY3JlYXRlIHB1cmUgSlNPTlJQQyByZXF1ZXN0IHdoaWNoIGNhbiBiZSB1c2VkIGluIGJhdGNoIHJlcXVlc3RcbiAqXG4gKiBAbWV0aG9kIHJlcXVlc3RcbiAqIEBwYXJhbSB7Li4ufSBwYXJhbXNcbiAqIEByZXR1cm4ge09iamVjdH0ganNvbnJwYyByZXF1ZXN0XG4gKi9cbk1ldGhvZC5wcm90b3R5cGUucmVxdWVzdCA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgcGF5bG9hZCA9IHRoaXMudG9QYXlsb2FkKEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cykpO1xuICAgIHBheWxvYWQuZm9ybWF0ID0gdGhpcy5mb3JtYXRPdXRwdXQuYmluZCh0aGlzKTtcbiAgICByZXR1cm4gcGF5bG9hZDtcbn07XG5cbi8qKlxuICogU2hvdWxkIHNlbmQgcmVxdWVzdCB0byB0aGUgQVBJXG4gKlxuICogQG1ldGhvZCBzZW5kXG4gKiBAcGFyYW0gbGlzdCBvZiBwYXJhbXNcbiAqIEByZXR1cm4gcmVzdWx0XG4gKi9cbk1ldGhvZC5wcm90b3R5cGUuc2VuZCA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgcGF5bG9hZCA9IHRoaXMudG9QYXlsb2FkKEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cykpO1xuICAgIGlmIChwYXlsb2FkLmNhbGxiYWNrKSB7XG4gICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgcmV0dXJuIFJlcXVlc3RNYW5hZ2VyLmdldEluc3RhbmNlKCkuc2VuZEFzeW5jKHBheWxvYWQsIGZ1bmN0aW9uIChlcnIsIHJlc3VsdCkge1xuICAgICAgICAgICAgcGF5bG9hZC5jYWxsYmFjayhlcnIsIHNlbGYuZm9ybWF0T3V0cHV0KHJlc3VsdCkpO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZm9ybWF0T3V0cHV0KFJlcXVlc3RNYW5hZ2VyLmdldEluc3RhbmNlKCkuc2VuZChwYXlsb2FkKSk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IE1ldGhvZDtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSBuYW1lcmVnLmpzXG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciBjb250cmFjdCA9IHJlcXVpcmUoJy4vY29udHJhY3QnKTtcblxudmFyIGFkZHJlc3MgPSAnMHhjNmQ5ZDJjZDQ0OWE3NTRjNDk0MjY0ZTE4MDljNTBlMzRkNjQ1NjJiJztcblxudmFyIGFiaSA9IFtcbiAgICB7XCJjb25zdGFudFwiOnRydWUsXCJpbnB1dHNcIjpbe1wibmFtZVwiOlwiX293bmVyXCIsXCJ0eXBlXCI6XCJhZGRyZXNzXCJ9XSxcIm5hbWVcIjpcIm5hbWVcIixcIm91dHB1dHNcIjpbe1wibmFtZVwiOlwib19uYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImNvbnN0YW50XCI6dHJ1ZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJfbmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifV0sXCJuYW1lXCI6XCJvd25lclwiLFwib3V0cHV0c1wiOlt7XCJuYW1lXCI6XCJcIixcInR5cGVcIjpcImFkZHJlc3NcIn1dLFwidHlwZVwiOlwiZnVuY3Rpb25cIn0sXG4gICAge1wiY29uc3RhbnRcIjp0cnVlLFwiaW5wdXRzXCI6W3tcIm5hbWVcIjpcIl9uYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcIm5hbWVcIjpcImNvbnRlbnRcIixcIm91dHB1dHNcIjpbe1wibmFtZVwiOlwiXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImNvbnN0YW50XCI6dHJ1ZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJfbmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifV0sXCJuYW1lXCI6XCJhZGRyXCIsXCJvdXRwdXRzXCI6W3tcIm5hbWVcIjpcIlwiLFwidHlwZVwiOlwiYWRkcmVzc1wifV0sXCJ0eXBlXCI6XCJmdW5jdGlvblwifSxcbiAgICB7XCJjb25zdGFudFwiOmZhbHNlLFwiaW5wdXRzXCI6W3tcIm5hbWVcIjpcIl9uYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcIm5hbWVcIjpcInJlc2VydmVcIixcIm91dHB1dHNcIjpbXSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImNvbnN0YW50XCI6dHJ1ZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJfbmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifV0sXCJuYW1lXCI6XCJzdWJSZWdpc3RyYXJcIixcIm91dHB1dHNcIjpbe1wibmFtZVwiOlwib19zdWJSZWdpc3RyYXJcIixcInR5cGVcIjpcImFkZHJlc3NcIn1dLFwidHlwZVwiOlwiZnVuY3Rpb25cIn0sXG4gICAge1wiY29uc3RhbnRcIjpmYWxzZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJfbmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifSx7XCJuYW1lXCI6XCJfbmV3T3duZXJcIixcInR5cGVcIjpcImFkZHJlc3NcIn1dLFwibmFtZVwiOlwidHJhbnNmZXJcIixcIm91dHB1dHNcIjpbXSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImNvbnN0YW50XCI6ZmFsc2UsXCJpbnB1dHNcIjpbe1wibmFtZVwiOlwiX25hbWVcIixcInR5cGVcIjpcImJ5dGVzMzJcIn0se1wibmFtZVwiOlwiX3JlZ2lzdHJhclwiLFwidHlwZVwiOlwiYWRkcmVzc1wifV0sXCJuYW1lXCI6XCJzZXRTdWJSZWdpc3RyYXJcIixcIm91dHB1dHNcIjpbXSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImNvbnN0YW50XCI6ZmFsc2UsXCJpbnB1dHNcIjpbXSxcIm5hbWVcIjpcIlJlZ2lzdHJhclwiLFwib3V0cHV0c1wiOltdLFwidHlwZVwiOlwiZnVuY3Rpb25cIn0sXG4gICAge1wiY29uc3RhbnRcIjpmYWxzZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJfbmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifSx7XCJuYW1lXCI6XCJfYVwiLFwidHlwZVwiOlwiYWRkcmVzc1wifSx7XCJuYW1lXCI6XCJfcHJpbWFyeVwiLFwidHlwZVwiOlwiYm9vbFwifV0sXCJuYW1lXCI6XCJzZXRBZGRyZXNzXCIsXCJvdXRwdXRzXCI6W10sXCJ0eXBlXCI6XCJmdW5jdGlvblwifSxcbiAgICB7XCJjb25zdGFudFwiOmZhbHNlLFwiaW5wdXRzXCI6W3tcIm5hbWVcIjpcIl9uYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9LHtcIm5hbWVcIjpcIl9jb250ZW50XCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcIm5hbWVcIjpcInNldENvbnRlbnRcIixcIm91dHB1dHNcIjpbXSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImNvbnN0YW50XCI6ZmFsc2UsXCJpbnB1dHNcIjpbe1wibmFtZVwiOlwiX25hbWVcIixcInR5cGVcIjpcImJ5dGVzMzJcIn1dLFwibmFtZVwiOlwiZGlzb3duXCIsXCJvdXRwdXRzXCI6W10sXCJ0eXBlXCI6XCJmdW5jdGlvblwifSxcbiAgICB7XCJjb25zdGFudFwiOnRydWUsXCJpbnB1dHNcIjpbe1wibmFtZVwiOlwiX25hbWVcIixcInR5cGVcIjpcImJ5dGVzMzJcIn1dLFwibmFtZVwiOlwicmVnaXN0ZXJcIixcIm91dHB1dHNcIjpbe1wibmFtZVwiOlwiXCIsXCJ0eXBlXCI6XCJhZGRyZXNzXCJ9XSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImFub255bW91c1wiOmZhbHNlLFwiaW5wdXRzXCI6W3tcImluZGV4ZWRcIjp0cnVlLFwibmFtZVwiOlwibmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifV0sXCJuYW1lXCI6XCJDaGFuZ2VkXCIsXCJ0eXBlXCI6XCJldmVudFwifSxcbiAgICB7XCJhbm9ueW1vdXNcIjpmYWxzZSxcImlucHV0c1wiOlt7XCJpbmRleGVkXCI6dHJ1ZSxcIm5hbWVcIjpcIm5hbWVcIixcInR5cGVcIjpcImJ5dGVzMzJcIn0se1wiaW5kZXhlZFwiOnRydWUsXCJuYW1lXCI6XCJhZGRyXCIsXCJ0eXBlXCI6XCJhZGRyZXNzXCJ9XSxcIm5hbWVcIjpcIlByaW1hcnlDaGFuZ2VkXCIsXCJ0eXBlXCI6XCJldmVudFwifVxuXTtcblxubW9kdWxlLmV4cG9ydHMgPSBjb250cmFjdChhYmkpLmF0KGFkZHJlc3MpO1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBAZmlsZSBldGguanNcbiAqIEBhdXRob3JzOlxuICogICBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xudmFyIFByb3BlcnR5ID0gcmVxdWlyZSgnLi9wcm9wZXJ0eScpO1xuXG4vLy8gQHJldHVybnMgYW4gYXJyYXkgb2Ygb2JqZWN0cyBkZXNjcmliaW5nIHdlYjMuZXRoIGFwaSBtZXRob2RzXG52YXIgbWV0aG9kcyA9IFtcbl07XG5cbi8vLyBAcmV0dXJucyBhbiBhcnJheSBvZiBvYmplY3RzIGRlc2NyaWJpbmcgd2ViMy5ldGggYXBpIHByb3BlcnRpZXNcbnZhciBwcm9wZXJ0aWVzID0gW1xuICAgIG5ldyBQcm9wZXJ0eSh7XG4gICAgICAgIG5hbWU6ICdsaXN0ZW5pbmcnLFxuICAgICAgICBnZXR0ZXI6ICduZXRfbGlzdGVuaW5nJ1xuICAgIH0pLFxuICAgIG5ldyBQcm9wZXJ0eSh7XG4gICAgICAgIG5hbWU6ICdwZWVyQ291bnQnLFxuICAgICAgICBnZXR0ZXI6ICduZXRfcGVlckNvdW50JyxcbiAgICAgICAgb3V0cHV0Rm9ybWF0dGVyOiB1dGlscy50b0RlY2ltYWxcbiAgICB9KVxuXTtcblxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBtZXRob2RzOiBtZXRob2RzLFxuICAgIHByb3BlcnRpZXM6IHByb3BlcnRpZXNcbn07XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqXG4gKiBAZmlsZSBwcm9wZXJ0eS5qc1xuICogQGF1dGhvciBGYWJpYW4gVm9nZWxzdGVsbGVyIDxmYWJpYW5AZnJvemVtYW4uZGU+XG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciBSZXF1ZXN0TWFuYWdlciA9IHJlcXVpcmUoJy4vcmVxdWVzdG1hbmFnZXInKTtcblxudmFyIFByb3BlcnR5ID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcbiAgICB0aGlzLm5hbWUgPSBvcHRpb25zLm5hbWU7XG4gICAgdGhpcy5nZXR0ZXIgPSBvcHRpb25zLmdldHRlcjtcbiAgICB0aGlzLnNldHRlciA9IG9wdGlvbnMuc2V0dGVyO1xuICAgIHRoaXMub3V0cHV0Rm9ybWF0dGVyID0gb3B0aW9ucy5vdXRwdXRGb3JtYXR0ZXI7XG4gICAgdGhpcy5pbnB1dEZvcm1hdHRlciA9IG9wdGlvbnMuaW5wdXRGb3JtYXR0ZXI7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZm9ybWF0IGlucHV0IGFyZ3Mgb2YgbWV0aG9kXG4gKiBcbiAqIEBtZXRob2QgZm9ybWF0SW5wdXRcbiAqIEBwYXJhbSB7QXJyYXl9XG4gKiBAcmV0dXJuIHtBcnJheX1cbiAqL1xuUHJvcGVydHkucHJvdG90eXBlLmZvcm1hdElucHV0ID0gZnVuY3Rpb24gKGFyZykge1xuICAgIHJldHVybiB0aGlzLmlucHV0Rm9ybWF0dGVyID8gdGhpcy5pbnB1dEZvcm1hdHRlcihhcmcpIDogYXJnO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGZvcm1hdCBvdXRwdXQocmVzdWx0KSBvZiBtZXRob2RcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdE91dHB1dFxuICogQHBhcmFtIHtPYmplY3R9XG4gKiBAcmV0dXJuIHtPYmplY3R9XG4gKi9cblByb3BlcnR5LnByb3RvdHlwZS5mb3JtYXRPdXRwdXQgPSBmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgcmV0dXJuIHRoaXMub3V0cHV0Rm9ybWF0dGVyICYmIHJlc3VsdCAhPT0gbnVsbCA/IHRoaXMub3V0cHV0Rm9ybWF0dGVyKHJlc3VsdCkgOiByZXN1bHQ7XG59O1xuXG4vKipcbiAqIFNob3VsZCBhdHRhY2ggZnVuY3Rpb24gdG8gbWV0aG9kXG4gKiBcbiAqIEBtZXRob2QgYXR0YWNoVG9PYmplY3RcbiAqIEBwYXJhbSB7T2JqZWN0fVxuICogQHBhcmFtIHtGdW5jdGlvbn1cbiAqL1xuUHJvcGVydHkucHJvdG90eXBlLmF0dGFjaFRvT2JqZWN0ID0gZnVuY3Rpb24gKG9iaikge1xuICAgIHZhciBwcm90byA9IHtcbiAgICAgICAgZ2V0OiB0aGlzLmdldC5iaW5kKHRoaXMpLFxuICAgIH07XG5cbiAgICB2YXIgbmFtZXMgPSB0aGlzLm5hbWUuc3BsaXQoJy4nKTtcbiAgICB2YXIgbmFtZSA9IG5hbWVzWzBdO1xuICAgIGlmIChuYW1lcy5sZW5ndGggPiAxKSB7XG4gICAgICAgIG9ialtuYW1lc1swXV0gPSBvYmpbbmFtZXNbMF1dIHx8IHt9O1xuICAgICAgICBvYmogPSBvYmpbbmFtZXNbMF1dO1xuICAgICAgICBuYW1lID0gbmFtZXNbMV07XG4gICAgfVxuICAgIFxuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShvYmosIG5hbWUsIHByb3RvKTtcblxuICAgIHZhciB0b0FzeW5jTmFtZSA9IGZ1bmN0aW9uIChwcmVmaXgsIG5hbWUpIHtcbiAgICAgICAgcmV0dXJuIHByZWZpeCArIG5hbWUuY2hhckF0KDApLnRvVXBwZXJDYXNlKCkgKyBuYW1lLnNsaWNlKDEpO1xuICAgIH07XG5cbiAgICBvYmpbdG9Bc3luY05hbWUoJ2dldCcsIG5hbWUpXSA9IHRoaXMuZ2V0QXN5bmMuYmluZCh0aGlzKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZ2V0IHZhbHVlIG9mIHRoZSBwcm9wZXJ0eVxuICpcbiAqIEBtZXRob2QgZ2V0XG4gKiBAcmV0dXJuIHtPYmplY3R9IHZhbHVlIG9mIHRoZSBwcm9wZXJ0eVxuICovXG5Qcm9wZXJ0eS5wcm90b3R5cGUuZ2V0ID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB0aGlzLmZvcm1hdE91dHB1dChSZXF1ZXN0TWFuYWdlci5nZXRJbnN0YW5jZSgpLnNlbmQoe1xuICAgICAgICBtZXRob2Q6IHRoaXMuZ2V0dGVyXG4gICAgfSkpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBhc3luY2hyb3Vub3VzbHkgZ2V0IHZhbHVlIG9mIHByb3BlcnR5XG4gKlxuICogQG1ldGhvZCBnZXRBc3luY1xuICogQHBhcmFtIHtGdW5jdGlvbn1cbiAqL1xuUHJvcGVydHkucHJvdG90eXBlLmdldEFzeW5jID0gZnVuY3Rpb24gKGNhbGxiYWNrKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIFJlcXVlc3RNYW5hZ2VyLmdldEluc3RhbmNlKCkuc2VuZEFzeW5jKHtcbiAgICAgICAgbWV0aG9kOiB0aGlzLmdldHRlclxuICAgIH0sIGZ1bmN0aW9uIChlcnIsIHJlc3VsdCkge1xuICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgICByZXR1cm4gY2FsbGJhY2soZXJyKTtcbiAgICAgICAgfVxuICAgICAgICBjYWxsYmFjayhlcnIsIHNlbGYuZm9ybWF0T3V0cHV0KHJlc3VsdCkpO1xuICAgIH0pO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBQcm9wZXJ0eTtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogQGZpbGUgcXRzeW5jLmpzXG4gKiBAYXV0aG9yczpcbiAqICAgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiAgIE1hcmlhbiBPYW5jZWEgPG1hcmlhbkBldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNFxuICovXG5cbnZhciBRdFN5bmNQcm92aWRlciA9IGZ1bmN0aW9uICgpIHtcbn07XG5cblF0U3luY1Byb3ZpZGVyLnByb3RvdHlwZS5zZW5kID0gZnVuY3Rpb24gKHBheWxvYWQpIHtcbiAgICB2YXIgcmVzdWx0ID0gbmF2aWdhdG9yLnF0LmNhbGxNZXRob2QoSlNPTi5zdHJpbmdpZnkocGF5bG9hZCkpO1xuICAgIHJldHVybiBKU09OLnBhcnNlKHJlc3VsdCk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFF0U3luY1Byb3ZpZGVyO1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBcbiAqIEBmaWxlIHJlcXVlc3RtYW5hZ2VyLmpzXG4gKiBAYXV0aG9yIEplZmZyZXkgV2lsY2tlIDxqZWZmQGV0aGRldi5jb20+XG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGF1dGhvciBNYXJpYW4gT2FuY2VhIDxtYXJpYW5AZXRoZGV2LmNvbT5cbiAqIEBhdXRob3IgRmFiaWFuIFZvZ2Vsc3RlbGxlciA8ZmFiaWFuQGV0aGRldi5jb20+XG4gKiBAYXV0aG9yIEdhdiBXb29kIDxnQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE0XG4gKi9cblxudmFyIEpzb25ycGMgPSByZXF1aXJlKCcuL2pzb25ycGMnKTtcbnZhciB1dGlscyA9IHJlcXVpcmUoJy4uL3V0aWxzL3V0aWxzJyk7XG52YXIgYyA9IHJlcXVpcmUoJy4uL3V0aWxzL2NvbmZpZycpO1xudmFyIGVycm9ycyA9IHJlcXVpcmUoJy4vZXJyb3JzJyk7XG5cbi8qKlxuICogSXQncyByZXNwb25zaWJsZSBmb3IgcGFzc2luZyBtZXNzYWdlcyB0byBwcm92aWRlcnNcbiAqIEl0J3MgYWxzbyByZXNwb25zaWJsZSBmb3IgcG9sbGluZyB0aGUgZXRoZXJldW0gbm9kZSBmb3IgaW5jb21pbmcgbWVzc2FnZXNcbiAqIERlZmF1bHQgcG9sbCB0aW1lb3V0IGlzIDEgc2Vjb25kXG4gKiBTaW5nbGV0b25cbiAqL1xudmFyIFJlcXVlc3RNYW5hZ2VyID0gZnVuY3Rpb24gKHByb3ZpZGVyKSB7XG4gICAgLy8gc2luZ2xldG9uIHBhdHRlcm5cbiAgICBpZiAoYXJndW1lbnRzLmNhbGxlZS5fc2luZ2xldG9uSW5zdGFuY2UpIHtcbiAgICAgICAgcmV0dXJuIGFyZ3VtZW50cy5jYWxsZWUuX3NpbmdsZXRvbkluc3RhbmNlO1xuICAgIH1cbiAgICBhcmd1bWVudHMuY2FsbGVlLl9zaW5nbGV0b25JbnN0YW5jZSA9IHRoaXM7XG5cbiAgICB0aGlzLnByb3ZpZGVyID0gcHJvdmlkZXI7XG4gICAgdGhpcy5wb2xscyA9IFtdO1xuICAgIHRoaXMudGltZW91dCA9IG51bGw7XG4gICAgdGhpcy5wb2xsKCk7XG59O1xuXG4vKipcbiAqIEByZXR1cm4ge1JlcXVlc3RNYW5hZ2VyfSBzaW5nbGV0b25cbiAqL1xuUmVxdWVzdE1hbmFnZXIuZ2V0SW5zdGFuY2UgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGluc3RhbmNlID0gbmV3IFJlcXVlc3RNYW5hZ2VyKCk7XG4gICAgcmV0dXJuIGluc3RhbmNlO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBzeW5jaHJvbm91c2x5IHNlbmQgcmVxdWVzdFxuICpcbiAqIEBtZXRob2Qgc2VuZFxuICogQHBhcmFtIHtPYmplY3R9IGRhdGFcbiAqIEByZXR1cm4ge09iamVjdH1cbiAqL1xuUmVxdWVzdE1hbmFnZXIucHJvdG90eXBlLnNlbmQgPSBmdW5jdGlvbiAoZGF0YSkge1xuICAgIGlmICghdGhpcy5wcm92aWRlcikge1xuICAgICAgICBjb25zb2xlLmVycm9yKGVycm9ycy5JbnZhbGlkUHJvdmlkZXIoKSk7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIHZhciBwYXlsb2FkID0gSnNvbnJwYy5nZXRJbnN0YW5jZSgpLnRvUGF5bG9hZChkYXRhLm1ldGhvZCwgZGF0YS5wYXJhbXMpO1xuICAgIHZhciByZXN1bHQgPSB0aGlzLnByb3ZpZGVyLnNlbmQocGF5bG9hZCk7XG5cbiAgICBpZiAoIUpzb25ycGMuZ2V0SW5zdGFuY2UoKS5pc1ZhbGlkUmVzcG9uc2UocmVzdWx0KSkge1xuICAgICAgICB0aHJvdyBlcnJvcnMuSW52YWxpZFJlc3BvbnNlKHJlc3VsdCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdC5yZXN1bHQ7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGFzeW5jaHJvbm91c2x5IHNlbmQgcmVxdWVzdFxuICpcbiAqIEBtZXRob2Qgc2VuZEFzeW5jXG4gKiBAcGFyYW0ge09iamVjdH0gZGF0YVxuICogQHBhcmFtIHtGdW5jdGlvbn0gY2FsbGJhY2tcbiAqL1xuUmVxdWVzdE1hbmFnZXIucHJvdG90eXBlLnNlbmRBc3luYyA9IGZ1bmN0aW9uIChkYXRhLCBjYWxsYmFjaykge1xuICAgIGlmICghdGhpcy5wcm92aWRlcikge1xuICAgICAgICByZXR1cm4gY2FsbGJhY2soZXJyb3JzLkludmFsaWRQcm92aWRlcigpKTtcbiAgICB9XG5cbiAgICB2YXIgcGF5bG9hZCA9IEpzb25ycGMuZ2V0SW5zdGFuY2UoKS50b1BheWxvYWQoZGF0YS5tZXRob2QsIGRhdGEucGFyYW1zKTtcbiAgICB0aGlzLnByb3ZpZGVyLnNlbmRBc3luYyhwYXlsb2FkLCBmdW5jdGlvbiAoZXJyLCByZXN1bHQpIHtcbiAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrKGVycik7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIGlmICghSnNvbnJwYy5nZXRJbnN0YW5jZSgpLmlzVmFsaWRSZXNwb25zZShyZXN1bHQpKSB7XG4gICAgICAgICAgICByZXR1cm4gY2FsbGJhY2soZXJyb3JzLkludmFsaWRSZXNwb25zZShyZXN1bHQpKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNhbGxiYWNrKG51bGwsIHJlc3VsdC5yZXN1bHQpO1xuICAgIH0pO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGFzeW5jaHJvbm91c2x5IHNlbmQgYmF0Y2ggcmVxdWVzdFxuICpcbiAqIEBtZXRob2Qgc2VuZEJhdGNoXG4gKiBAcGFyYW0ge0FycmF5fSBiYXRjaCBkYXRhXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFja1xuICovXG5SZXF1ZXN0TWFuYWdlci5wcm90b3R5cGUuc2VuZEJhdGNoID0gZnVuY3Rpb24gKGRhdGEsIGNhbGxiYWNrKSB7XG4gICAgaWYgKCF0aGlzLnByb3ZpZGVyKSB7XG4gICAgICAgIHJldHVybiBjYWxsYmFjayhlcnJvcnMuSW52YWxpZFByb3ZpZGVyKCkpO1xuICAgIH1cblxuICAgIHZhciBwYXlsb2FkID0gSnNvbnJwYy5nZXRJbnN0YW5jZSgpLnRvQmF0Y2hQYXlsb2FkKGRhdGEpO1xuXG4gICAgdGhpcy5wcm92aWRlci5zZW5kQXN5bmMocGF5bG9hZCwgZnVuY3Rpb24gKGVyciwgcmVzdWx0cykge1xuICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgICByZXR1cm4gY2FsbGJhY2soZXJyKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghdXRpbHMuaXNBcnJheShyZXN1bHRzKSkge1xuICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrKGVycm9ycy5JbnZhbGlkUmVzcG9uc2UocmVzdWx0cykpO1xuICAgICAgICB9XG5cbiAgICAgICAgY2FsbGJhY2soZXJyLCByZXN1bHRzKTtcbiAgICB9KTsgXG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIHNldCBwcm92aWRlciBvZiByZXF1ZXN0IG1hbmFnZXJcbiAqXG4gKiBAbWV0aG9kIHNldFByb3ZpZGVyXG4gKiBAcGFyYW0ge09iamVjdH1cbiAqL1xuUmVxdWVzdE1hbmFnZXIucHJvdG90eXBlLnNldFByb3ZpZGVyID0gZnVuY3Rpb24gKHApIHtcbiAgICB0aGlzLnByb3ZpZGVyID0gcDtcbn07XG5cbi8qanNoaW50IG1heHBhcmFtczo0ICovXG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gc3RhcnQgcG9sbGluZ1xuICpcbiAqIEBtZXRob2Qgc3RhcnRQb2xsaW5nXG4gKiBAcGFyYW0ge09iamVjdH0gZGF0YVxuICogQHBhcmFtIHtOdW1iZXJ9IHBvbGxJZFxuICogQHBhcmFtIHtGdW5jdGlvbn0gY2FsbGJhY2tcbiAqIEBwYXJhbSB7RnVuY3Rpb259IHVuaW5zdGFsbFxuICpcbiAqIEB0b2RvIGNsZWFudXAgbnVtYmVyIG9mIHBhcmFtc1xuICovXG5SZXF1ZXN0TWFuYWdlci5wcm90b3R5cGUuc3RhcnRQb2xsaW5nID0gZnVuY3Rpb24gKGRhdGEsIHBvbGxJZCwgY2FsbGJhY2ssIHVuaW5zdGFsbCkge1xuICAgIHRoaXMucG9sbHMucHVzaCh7ZGF0YTogZGF0YSwgaWQ6IHBvbGxJZCwgY2FsbGJhY2s6IGNhbGxiYWNrLCB1bmluc3RhbGw6IHVuaW5zdGFsbH0pO1xufTtcbi8qanNoaW50IG1heHBhcmFtczozICovXG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gc3RvcCBwb2xsaW5nIGZvciBmaWx0ZXIgd2l0aCBnaXZlbiBpZFxuICpcbiAqIEBtZXRob2Qgc3RvcFBvbGxpbmdcbiAqIEBwYXJhbSB7TnVtYmVyfSBwb2xsSWRcbiAqL1xuUmVxdWVzdE1hbmFnZXIucHJvdG90eXBlLnN0b3BQb2xsaW5nID0gZnVuY3Rpb24gKHBvbGxJZCkge1xuICAgIGZvciAodmFyIGkgPSB0aGlzLnBvbGxzLmxlbmd0aDsgaS0tOykge1xuICAgICAgICB2YXIgcG9sbCA9IHRoaXMucG9sbHNbaV07XG4gICAgICAgIGlmIChwb2xsLmlkID09PSBwb2xsSWQpIHtcbiAgICAgICAgICAgIHRoaXMucG9sbHMuc3BsaWNlKGksIDEpO1xuICAgICAgICB9XG4gICAgfVxufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIHJlc2V0IHBvbGxpbmcgbWVjaGFuaXNtIG9mIHJlcXVlc3QgbWFuYWdlclxuICpcbiAqIEBtZXRob2QgcmVzZXRcbiAqL1xuUmVxdWVzdE1hbmFnZXIucHJvdG90eXBlLnJlc2V0ID0gZnVuY3Rpb24gKCkge1xuICAgIHRoaXMucG9sbHMuZm9yRWFjaChmdW5jdGlvbiAocG9sbCkge1xuICAgICAgICBwb2xsLnVuaW5zdGFsbChwb2xsLmlkKTsgXG4gICAgfSk7XG4gICAgdGhpcy5wb2xscyA9IFtdO1xuXG4gICAgaWYgKHRoaXMudGltZW91dCkge1xuICAgICAgICBjbGVhclRpbWVvdXQodGhpcy50aW1lb3V0KTtcbiAgICAgICAgdGhpcy50aW1lb3V0ID0gbnVsbDtcbiAgICB9XG4gICAgdGhpcy5wb2xsKCk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gcG9sbCBmb3IgY2hhbmdlcyBvbiBmaWx0ZXIgd2l0aCBnaXZlbiBpZFxuICpcbiAqIEBtZXRob2QgcG9sbFxuICovXG5SZXF1ZXN0TWFuYWdlci5wcm90b3R5cGUucG9sbCA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLnRpbWVvdXQgPSBzZXRUaW1lb3V0KHRoaXMucG9sbC5iaW5kKHRoaXMpLCBjLkVUSF9QT0xMSU5HX1RJTUVPVVQpO1xuXG4gICAgaWYgKCF0aGlzLnBvbGxzLmxlbmd0aCkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLnByb3ZpZGVyKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoZXJyb3JzLkludmFsaWRQcm92aWRlcigpKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBwYXlsb2FkID0gSnNvbnJwYy5nZXRJbnN0YW5jZSgpLnRvQmF0Y2hQYXlsb2FkKHRoaXMucG9sbHMubWFwKGZ1bmN0aW9uIChkYXRhKSB7XG4gICAgICAgIHJldHVybiBkYXRhLmRhdGE7XG4gICAgfSkpO1xuXG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHRoaXMucHJvdmlkZXIuc2VuZEFzeW5jKHBheWxvYWQsIGZ1bmN0aW9uIChlcnJvciwgcmVzdWx0cykge1xuICAgICAgICAvLyBUT0RPOiBjb25zb2xlIGxvZz9cbiAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgICAgIFxuICAgICAgICBpZiAoIXV0aWxzLmlzQXJyYXkocmVzdWx0cykpIHtcbiAgICAgICAgICAgIHRocm93IGVycm9ycy5JbnZhbGlkUmVzcG9uc2UocmVzdWx0cyk7XG4gICAgICAgIH1cblxuICAgICAgICByZXN1bHRzLm1hcChmdW5jdGlvbiAocmVzdWx0LCBpbmRleCkge1xuICAgICAgICAgICAgcmVzdWx0LmNhbGxiYWNrID0gc2VsZi5wb2xsc1tpbmRleF0uY2FsbGJhY2s7XG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgICB9KS5maWx0ZXIoZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgdmFyIHZhbGlkID0gSnNvbnJwYy5nZXRJbnN0YW5jZSgpLmlzVmFsaWRSZXNwb25zZShyZXN1bHQpO1xuICAgICAgICAgICAgaWYgKCF2YWxpZCkge1xuICAgICAgICAgICAgICAgIHJlc3VsdC5jYWxsYmFjayhlcnJvcnMuSW52YWxpZFJlc3BvbnNlKHJlc3VsdCkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHZhbGlkO1xuICAgICAgICB9KS5maWx0ZXIoZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgcmV0dXJuIHV0aWxzLmlzQXJyYXkocmVzdWx0LnJlc3VsdCkgJiYgcmVzdWx0LnJlc3VsdC5sZW5ndGggPiAwO1xuICAgICAgICB9KS5mb3JFYWNoKGZ1bmN0aW9uIChyZXN1bHQpIHtcbiAgICAgICAgICAgIHJlc3VsdC5jYWxsYmFjayhudWxsLCByZXN1bHQucmVzdWx0KTtcbiAgICAgICAgfSk7XG4gICAgfSk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFJlcXVlc3RNYW5hZ2VyO1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBAZmlsZSBzaGguanNcbiAqIEBhdXRob3JzOlxuICogICBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgTWV0aG9kID0gcmVxdWlyZSgnLi9tZXRob2QnKTtcbnZhciBmb3JtYXR0ZXJzID0gcmVxdWlyZSgnLi9mb3JtYXR0ZXJzJyk7XG5cbnZhciBwb3N0ID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ3Bvc3QnLCBcbiAgICBjYWxsOiAnc2hoX3Bvc3QnLCBcbiAgICBwYXJhbXM6IDEsXG4gICAgaW5wdXRGb3JtYXR0ZXI6IFtmb3JtYXR0ZXJzLmlucHV0UG9zdEZvcm1hdHRlcl1cbn0pO1xuXG52YXIgbmV3SWRlbnRpdHkgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnbmV3SWRlbnRpdHknLFxuICAgIGNhbGw6ICdzaGhfbmV3SWRlbnRpdHknLFxuICAgIHBhcmFtczogMFxufSk7XG5cbnZhciBoYXNJZGVudGl0eSA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdoYXNJZGVudGl0eScsXG4gICAgY2FsbDogJ3NoaF9oYXNJZGVudGl0eScsXG4gICAgcGFyYW1zOiAxXG59KTtcblxudmFyIG5ld0dyb3VwID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ25ld0dyb3VwJyxcbiAgICBjYWxsOiAnc2hoX25ld0dyb3VwJyxcbiAgICBwYXJhbXM6IDBcbn0pO1xuXG52YXIgYWRkVG9Hcm91cCA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdhZGRUb0dyb3VwJyxcbiAgICBjYWxsOiAnc2hoX2FkZFRvR3JvdXAnLFxuICAgIHBhcmFtczogMFxufSk7XG5cbnZhciBtZXRob2RzID0gW1xuICAgIHBvc3QsXG4gICAgbmV3SWRlbnRpdHksXG4gICAgaGFzSWRlbnRpdHksXG4gICAgbmV3R3JvdXAsXG4gICAgYWRkVG9Hcm91cFxuXTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgbWV0aG9kczogbWV0aG9kc1xufTtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSB0cmFuc2Zlci5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgd2ViMyA9IHJlcXVpcmUoJy4uL3dlYjMnKTtcbnZhciBJQ0FQID0gcmVxdWlyZSgnLi9pY2FwJyk7XG52YXIgbmFtZXJlZyA9IHJlcXVpcmUoJy4vbmFtZXJlZycpO1xudmFyIGNvbnRyYWN0ID0gcmVxdWlyZSgnLi9jb250cmFjdCcpO1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIG1ha2UgSUNBUCB0cmFuc2ZlclxuICpcbiAqIEBtZXRob2QgdHJhbnNmZXJcbiAqIEBwYXJhbSB7U3RyaW5nfSBpYmFuIG51bWJlclxuICogQHBhcmFtIHtTdHJpbmd9IGZyb20gKGFkZHJlc3MpXG4gKiBAcGFyYW0ge1ZhbHVlfSB2YWx1ZSB0byBiZSB0cmFuZmVyZWRcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrLCBjYWxsYmFja1xuICovXG52YXIgdHJhbnNmZXIgPSBmdW5jdGlvbiAoZnJvbSwgaWJhbiwgdmFsdWUsIGNhbGxiYWNrKSB7XG4gICAgdmFyIGljYXAgPSBuZXcgSUNBUChpYmFuKTsgXG4gICAgaWYgKCFpY2FwLmlzVmFsaWQoKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ2ludmFsaWQgaWJhbiBhZGRyZXNzJyk7XG4gICAgfVxuXG4gICAgaWYgKGljYXAuaXNEaXJlY3QoKSkge1xuICAgICAgICByZXR1cm4gdHJhbnNmZXJUb0FkZHJlc3MoZnJvbSwgaWNhcC5hZGRyZXNzKCksIHZhbHVlLCBjYWxsYmFjayk7XG4gICAgfVxuICAgIFxuICAgIGlmICghY2FsbGJhY2spIHtcbiAgICAgICAgdmFyIGFkZHJlc3MgPSBuYW1lcmVnLmFkZHIoaWNhcC5pbnN0aXR1dGlvbigpKTtcbiAgICAgICAgcmV0dXJuIGRlcG9zaXQoZnJvbSwgYWRkcmVzcywgdmFsdWUsIGljYXAuY2xpZW50KCkpO1xuICAgIH1cblxuICAgIG5hbWVyZWcuYWRkcihpY2FwLmluc2l0dXRpb24oKSwgZnVuY3Rpb24gKGVyciwgYWRkcmVzcykge1xuICAgICAgICByZXR1cm4gZGVwb3NpdChmcm9tLCBhZGRyZXNzLCB2YWx1ZSwgaWNhcC5jbGllbnQoKSwgY2FsbGJhY2spO1xuICAgIH0pO1xuICAgIFxufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byB0cmFuc2ZlciBmdW5kcyB0byBjZXJ0YWluIGFkZHJlc3NcbiAqXG4gKiBAbWV0aG9kIHRyYW5zZmVyVG9BZGRyZXNzXG4gKiBAcGFyYW0ge1N0cmluZ30gYWRkcmVzc1xuICogQHBhcmFtIHtTdHJpbmd9IGZyb20gKGFkZHJlc3MpXG4gKiBAcGFyYW0ge1ZhbHVlfSB2YWx1ZSB0byBiZSB0cmFuZmVyZWRcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrLCBjYWxsYmFja1xuICovXG52YXIgdHJhbnNmZXJUb0FkZHJlc3MgPSBmdW5jdGlvbiAoZnJvbSwgYWRkcmVzcywgdmFsdWUsIGNhbGxiYWNrKSB7XG4gICAgcmV0dXJuIHdlYjMuZXRoLnNlbmRUcmFuc2FjdGlvbih7XG4gICAgICAgIGFkZHJlc3M6IGFkZHJlc3MsXG4gICAgICAgIGZyb206IGZyb20sXG4gICAgICAgIHZhbHVlOiB2YWx1ZVxuICAgIH0sIGNhbGxiYWNrKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZGVwb3NpdCBmdW5kcyB0byBnZW5lcmljIEV4Y2hhbmdlIGNvbnRyYWN0IChtdXN0IGltcGxlbWVudCBkZXBvc2l0KGJ5dGVzMzIpIG1ldGhvZCEpXG4gKlxuICogQG1ldGhvZCBkZXBvc2l0XG4gKiBAcGFyYW0ge1N0cmluZ30gYWRkcmVzc1xuICogQHBhcmFtIHtTdHJpbmd9IGZyb20gKGFkZHJlc3MpXG4gKiBAcGFyYW0ge1ZhbHVlfSB2YWx1ZSB0byBiZSB0cmFuZmVyZWRcbiAqIEBwYXJhbSB7U3RyaW5nfSBjbGllbnQgdW5pcXVlIGlkZW50aWZpZXJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrLCBjYWxsYmFja1xuICovXG52YXIgZGVwb3NpdCA9IGZ1bmN0aW9uIChmcm9tLCBhZGRyZXNzLCB2YWx1ZSwgY2xpZW50LCBjYWxsYmFjaykge1xuICAgIHZhciBhYmkgPSBbe1wiY29uc3RhbnRcIjpmYWxzZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJuYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcIm5hbWVcIjpcImRlcG9zaXRcIixcIm91dHB1dHNcIjpbXSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9XTtcbiAgICByZXR1cm4gY29udHJhY3QoYWJpKS5hdChhZGRyZXNzKS5kZXBvc2l0KGNsaWVudCwge1xuICAgICAgICBmcm9tOiBmcm9tLFxuICAgICAgICB2YWx1ZTogdmFsdWVcbiAgICB9LCBjYWxsYmFjayk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IHRyYW5zZmVyO1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBAZmlsZSB3YXRjaGVzLmpzXG4gKiBAYXV0aG9yczpcbiAqICAgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE1XG4gKi9cblxudmFyIE1ldGhvZCA9IHJlcXVpcmUoJy4vbWV0aG9kJyk7XG5cbi8vLyBAcmV0dXJucyBhbiBhcnJheSBvZiBvYmplY3RzIGRlc2NyaWJpbmcgd2ViMy5ldGguZmlsdGVyIGFwaSBtZXRob2RzXG52YXIgZXRoID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciBuZXdGaWx0ZXJDYWxsID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICAgICAgdmFyIHR5cGUgPSBhcmdzWzBdO1xuXG4gICAgICAgIHN3aXRjaCh0eXBlKSB7XG4gICAgICAgICAgICBjYXNlICdsYXRlc3QnOlxuICAgICAgICAgICAgICAgIGFyZ3MucG9wKCk7XG4gICAgICAgICAgICAgICAgdGhpcy5wYXJhbXMgPSAwO1xuICAgICAgICAgICAgICAgIHJldHVybiAnZXRoX25ld0Jsb2NrRmlsdGVyJztcbiAgICAgICAgICAgIGNhc2UgJ3BlbmRpbmcnOlxuICAgICAgICAgICAgICAgIGFyZ3MucG9wKCk7XG4gICAgICAgICAgICAgICAgdGhpcy5wYXJhbXMgPSAwO1xuICAgICAgICAgICAgICAgIHJldHVybiAnZXRoX25ld1BlbmRpbmdUcmFuc2FjdGlvbkZpbHRlcic7XG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgIHJldHVybiAnZXRoX25ld0ZpbHRlcic7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgdmFyIG5ld0ZpbHRlciA9IG5ldyBNZXRob2Qoe1xuICAgICAgICBuYW1lOiAnbmV3RmlsdGVyJyxcbiAgICAgICAgY2FsbDogbmV3RmlsdGVyQ2FsbCxcbiAgICAgICAgcGFyYW1zOiAxXG4gICAgfSk7XG5cbiAgICB2YXIgdW5pbnN0YWxsRmlsdGVyID0gbmV3IE1ldGhvZCh7XG4gICAgICAgIG5hbWU6ICd1bmluc3RhbGxGaWx0ZXInLFxuICAgICAgICBjYWxsOiAnZXRoX3VuaW5zdGFsbEZpbHRlcicsXG4gICAgICAgIHBhcmFtczogMVxuICAgIH0pO1xuXG4gICAgdmFyIGdldExvZ3MgPSBuZXcgTWV0aG9kKHtcbiAgICAgICAgbmFtZTogJ2dldExvZ3MnLFxuICAgICAgICBjYWxsOiAnZXRoX2dldEZpbHRlckxvZ3MnLFxuICAgICAgICBwYXJhbXM6IDFcbiAgICB9KTtcblxuICAgIHZhciBwb2xsID0gbmV3IE1ldGhvZCh7XG4gICAgICAgIG5hbWU6ICdwb2xsJyxcbiAgICAgICAgY2FsbDogJ2V0aF9nZXRGaWx0ZXJDaGFuZ2VzJyxcbiAgICAgICAgcGFyYW1zOiAxXG4gICAgfSk7XG5cbiAgICByZXR1cm4gW1xuICAgICAgICBuZXdGaWx0ZXIsXG4gICAgICAgIHVuaW5zdGFsbEZpbHRlcixcbiAgICAgICAgZ2V0TG9ncyxcbiAgICAgICAgcG9sbFxuICAgIF07XG59O1xuXG4vLy8gQHJldHVybnMgYW4gYXJyYXkgb2Ygb2JqZWN0cyBkZXNjcmliaW5nIHdlYjMuc2hoLndhdGNoIGFwaSBtZXRob2RzXG52YXIgc2hoID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciBuZXdGaWx0ZXIgPSBuZXcgTWV0aG9kKHtcbiAgICAgICAgbmFtZTogJ25ld0ZpbHRlcicsXG4gICAgICAgIGNhbGw6ICdzaGhfbmV3RmlsdGVyJyxcbiAgICAgICAgcGFyYW1zOiAxXG4gICAgfSk7XG5cbiAgICB2YXIgdW5pbnN0YWxsRmlsdGVyID0gbmV3IE1ldGhvZCh7XG4gICAgICAgIG5hbWU6ICd1bmluc3RhbGxGaWx0ZXInLFxuICAgICAgICBjYWxsOiAnc2hoX3VuaW5zdGFsbEZpbHRlcicsXG4gICAgICAgIHBhcmFtczogMVxuICAgIH0pO1xuXG4gICAgdmFyIGdldExvZ3MgPSBuZXcgTWV0aG9kKHtcbiAgICAgICAgbmFtZTogJ2dldExvZ3MnLFxuICAgICAgICBjYWxsOiAnc2hoX2dldE1lc3NhZ2VzJyxcbiAgICAgICAgcGFyYW1zOiAxXG4gICAgfSk7XG5cbiAgICB2YXIgcG9sbCA9IG5ldyBNZXRob2Qoe1xuICAgICAgICBuYW1lOiAncG9sbCcsXG4gICAgICAgIGNhbGw6ICdzaGhfZ2V0RmlsdGVyQ2hhbmdlcycsXG4gICAgICAgIHBhcmFtczogMVxuICAgIH0pO1xuXG4gICAgcmV0dXJuIFtcbiAgICAgICAgbmV3RmlsdGVyLFxuICAgICAgICB1bmluc3RhbGxGaWx0ZXIsXG4gICAgICAgIGdldExvZ3MsXG4gICAgICAgIHBvbGxcbiAgICBdO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgZXRoOiBldGgsXG4gICAgc2hoOiBzaGhcbn07XG5cbiIsbnVsbCwiOyhmdW5jdGlvbiAocm9vdCwgZmFjdG9yeSkge1xuXHRpZiAodHlwZW9mIGV4cG9ydHMgPT09IFwib2JqZWN0XCIpIHtcblx0XHQvLyBDb21tb25KU1xuXHRcdG1vZHVsZS5leHBvcnRzID0gZXhwb3J0cyA9IGZhY3RvcnkoKTtcblx0fVxuXHRlbHNlIGlmICh0eXBlb2YgZGVmaW5lID09PSBcImZ1bmN0aW9uXCIgJiYgZGVmaW5lLmFtZCkge1xuXHRcdC8vIEFNRFxuXHRcdGRlZmluZShbXSwgZmFjdG9yeSk7XG5cdH1cblx0ZWxzZSB7XG5cdFx0Ly8gR2xvYmFsIChicm93c2VyKVxuXHRcdHJvb3QuQ3J5cHRvSlMgPSBmYWN0b3J5KCk7XG5cdH1cbn0odGhpcywgZnVuY3Rpb24gKCkge1xuXG5cdC8qKlxuXHQgKiBDcnlwdG9KUyBjb3JlIGNvbXBvbmVudHMuXG5cdCAqL1xuXHR2YXIgQ3J5cHRvSlMgPSBDcnlwdG9KUyB8fCAoZnVuY3Rpb24gKE1hdGgsIHVuZGVmaW5lZCkge1xuXHQgICAgLyoqXG5cdCAgICAgKiBDcnlwdG9KUyBuYW1lc3BhY2UuXG5cdCAgICAgKi9cblx0ICAgIHZhciBDID0ge307XG5cblx0ICAgIC8qKlxuXHQgICAgICogTGlicmFyeSBuYW1lc3BhY2UuXG5cdCAgICAgKi9cblx0ICAgIHZhciBDX2xpYiA9IEMubGliID0ge307XG5cblx0ICAgIC8qKlxuXHQgICAgICogQmFzZSBvYmplY3QgZm9yIHByb3RvdHlwYWwgaW5oZXJpdGFuY2UuXG5cdCAgICAgKi9cblx0ICAgIHZhciBCYXNlID0gQ19saWIuQmFzZSA9IChmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgZnVuY3Rpb24gRigpIHt9XG5cblx0ICAgICAgICByZXR1cm4ge1xuXHQgICAgICAgICAgICAvKipcblx0ICAgICAgICAgICAgICogQ3JlYXRlcyBhIG5ldyBvYmplY3QgdGhhdCBpbmhlcml0cyBmcm9tIHRoaXMgb2JqZWN0LlxuXHQgICAgICAgICAgICAgKlxuXHQgICAgICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gb3ZlcnJpZGVzIFByb3BlcnRpZXMgdG8gY29weSBpbnRvIHRoZSBuZXcgb2JqZWN0LlxuXHQgICAgICAgICAgICAgKlxuXHQgICAgICAgICAgICAgKiBAcmV0dXJuIHtPYmplY3R9IFRoZSBuZXcgb2JqZWN0LlxuXHQgICAgICAgICAgICAgKlxuXHQgICAgICAgICAgICAgKiBAc3RhdGljXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqICAgICB2YXIgTXlUeXBlID0gQ3J5cHRvSlMubGliLkJhc2UuZXh0ZW5kKHtcblx0ICAgICAgICAgICAgICogICAgICAgICBmaWVsZDogJ3ZhbHVlJyxcblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogICAgICAgICBtZXRob2Q6IGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgICogICAgICAgICB9XG5cdCAgICAgICAgICAgICAqICAgICB9KTtcblx0ICAgICAgICAgICAgICovXG5cdCAgICAgICAgICAgIGV4dGVuZDogZnVuY3Rpb24gKG92ZXJyaWRlcykge1xuXHQgICAgICAgICAgICAgICAgLy8gU3Bhd25cblx0ICAgICAgICAgICAgICAgIEYucHJvdG90eXBlID0gdGhpcztcblx0ICAgICAgICAgICAgICAgIHZhciBzdWJ0eXBlID0gbmV3IEYoKTtcblxuXHQgICAgICAgICAgICAgICAgLy8gQXVnbWVudFxuXHQgICAgICAgICAgICAgICAgaWYgKG92ZXJyaWRlcykge1xuXHQgICAgICAgICAgICAgICAgICAgIHN1YnR5cGUubWl4SW4ob3ZlcnJpZGVzKTtcblx0ICAgICAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICAgICAgLy8gQ3JlYXRlIGRlZmF1bHQgaW5pdGlhbGl6ZXJcblx0ICAgICAgICAgICAgICAgIGlmICghc3VidHlwZS5oYXNPd25Qcm9wZXJ0eSgnaW5pdCcpKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgc3VidHlwZS5pbml0ID0gZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICAgICAgICAgICAgICBzdWJ0eXBlLiRzdXBlci5pbml0LmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG5cdCAgICAgICAgICAgICAgICAgICAgfTtcblx0ICAgICAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICAgICAgLy8gSW5pdGlhbGl6ZXIncyBwcm90b3R5cGUgaXMgdGhlIHN1YnR5cGUgb2JqZWN0XG5cdCAgICAgICAgICAgICAgICBzdWJ0eXBlLmluaXQucHJvdG90eXBlID0gc3VidHlwZTtcblxuXHQgICAgICAgICAgICAgICAgLy8gUmVmZXJlbmNlIHN1cGVydHlwZVxuXHQgICAgICAgICAgICAgICAgc3VidHlwZS4kc3VwZXIgPSB0aGlzO1xuXG5cdCAgICAgICAgICAgICAgICByZXR1cm4gc3VidHlwZTtcblx0ICAgICAgICAgICAgfSxcblxuXHQgICAgICAgICAgICAvKipcblx0ICAgICAgICAgICAgICogRXh0ZW5kcyB0aGlzIG9iamVjdCBhbmQgcnVucyB0aGUgaW5pdCBtZXRob2QuXG5cdCAgICAgICAgICAgICAqIEFyZ3VtZW50cyB0byBjcmVhdGUoKSB3aWxsIGJlIHBhc3NlZCB0byBpbml0KCkuXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqIEByZXR1cm4ge09iamVjdH0gVGhlIG5ldyBvYmplY3QuXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqIEBzdGF0aWNcblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogICAgIHZhciBpbnN0YW5jZSA9IE15VHlwZS5jcmVhdGUoKTtcblx0ICAgICAgICAgICAgICovXG5cdCAgICAgICAgICAgIGNyZWF0ZTogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICAgICAgdmFyIGluc3RhbmNlID0gdGhpcy5leHRlbmQoKTtcblx0ICAgICAgICAgICAgICAgIGluc3RhbmNlLmluaXQuYXBwbHkoaW5zdGFuY2UsIGFyZ3VtZW50cyk7XG5cblx0ICAgICAgICAgICAgICAgIHJldHVybiBpbnN0YW5jZTtcblx0ICAgICAgICAgICAgfSxcblxuXHQgICAgICAgICAgICAvKipcblx0ICAgICAgICAgICAgICogSW5pdGlhbGl6ZXMgYSBuZXdseSBjcmVhdGVkIG9iamVjdC5cblx0ICAgICAgICAgICAgICogT3ZlcnJpZGUgdGhpcyBtZXRob2QgdG8gYWRkIHNvbWUgbG9naWMgd2hlbiB5b3VyIG9iamVjdHMgYXJlIGNyZWF0ZWQuXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqICAgICB2YXIgTXlUeXBlID0gQ3J5cHRvSlMubGliLkJhc2UuZXh0ZW5kKHtcblx0ICAgICAgICAgICAgICogICAgICAgICBpbml0OiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgICAqICAgICAgICAgICAgIC8vIC4uLlxuXHQgICAgICAgICAgICAgKiAgICAgICAgIH1cblx0ICAgICAgICAgICAgICogICAgIH0pO1xuXHQgICAgICAgICAgICAgKi9cblx0ICAgICAgICAgICAgaW5pdDogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICB9LFxuXG5cdCAgICAgICAgICAgIC8qKlxuXHQgICAgICAgICAgICAgKiBDb3BpZXMgcHJvcGVydGllcyBpbnRvIHRoaXMgb2JqZWN0LlxuXHQgICAgICAgICAgICAgKlxuXHQgICAgICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gcHJvcGVydGllcyBUaGUgcHJvcGVydGllcyB0byBtaXggaW4uXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqICAgICBNeVR5cGUubWl4SW4oe1xuXHQgICAgICAgICAgICAgKiAgICAgICAgIGZpZWxkOiAndmFsdWUnXG5cdCAgICAgICAgICAgICAqICAgICB9KTtcblx0ICAgICAgICAgICAgICovXG5cdCAgICAgICAgICAgIG1peEluOiBmdW5jdGlvbiAocHJvcGVydGllcykge1xuXHQgICAgICAgICAgICAgICAgZm9yICh2YXIgcHJvcGVydHlOYW1lIGluIHByb3BlcnRpZXMpIHtcblx0ICAgICAgICAgICAgICAgICAgICBpZiAocHJvcGVydGllcy5oYXNPd25Qcm9wZXJ0eShwcm9wZXJ0eU5hbWUpKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHRoaXNbcHJvcGVydHlOYW1lXSA9IHByb3BlcnRpZXNbcHJvcGVydHlOYW1lXTtcblx0ICAgICAgICAgICAgICAgICAgICB9XG5cdCAgICAgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgICAgIC8vIElFIHdvbid0IGNvcHkgdG9TdHJpbmcgdXNpbmcgdGhlIGxvb3AgYWJvdmVcblx0ICAgICAgICAgICAgICAgIGlmIChwcm9wZXJ0aWVzLmhhc093blByb3BlcnR5KCd0b1N0cmluZycpKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgdGhpcy50b1N0cmluZyA9IHByb3BlcnRpZXMudG9TdHJpbmc7XG5cdCAgICAgICAgICAgICAgICB9XG5cdCAgICAgICAgICAgIH0sXG5cblx0ICAgICAgICAgICAgLyoqXG5cdCAgICAgICAgICAgICAqIENyZWF0ZXMgYSBjb3B5IG9mIHRoaXMgb2JqZWN0LlxuXHQgICAgICAgICAgICAgKlxuXHQgICAgICAgICAgICAgKiBAcmV0dXJuIHtPYmplY3R9IFRoZSBjbG9uZS5cblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogICAgIHZhciBjbG9uZSA9IGluc3RhbmNlLmNsb25lKCk7XG5cdCAgICAgICAgICAgICAqL1xuXHQgICAgICAgICAgICBjbG9uZTogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuaW5pdC5wcm90b3R5cGUuZXh0ZW5kKHRoaXMpO1xuXHQgICAgICAgICAgICB9XG5cdCAgICAgICAgfTtcblx0ICAgIH0oKSk7XG5cblx0ICAgIC8qKlxuXHQgICAgICogQW4gYXJyYXkgb2YgMzItYml0IHdvcmRzLlxuXHQgICAgICpcblx0ICAgICAqIEBwcm9wZXJ0eSB7QXJyYXl9IHdvcmRzIFRoZSBhcnJheSBvZiAzMi1iaXQgd29yZHMuXG5cdCAgICAgKiBAcHJvcGVydHkge251bWJlcn0gc2lnQnl0ZXMgVGhlIG51bWJlciBvZiBzaWduaWZpY2FudCBieXRlcyBpbiB0aGlzIHdvcmQgYXJyYXkuXG5cdCAgICAgKi9cblx0ICAgIHZhciBXb3JkQXJyYXkgPSBDX2xpYi5Xb3JkQXJyYXkgPSBCYXNlLmV4dGVuZCh7XG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogSW5pdGlhbGl6ZXMgYSBuZXdseSBjcmVhdGVkIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge0FycmF5fSB3b3JkcyAoT3B0aW9uYWwpIEFuIGFycmF5IG9mIDMyLWJpdCB3b3Jkcy5cblx0ICAgICAgICAgKiBAcGFyYW0ge251bWJlcn0gc2lnQnl0ZXMgKE9wdGlvbmFsKSBUaGUgbnVtYmVyIG9mIHNpZ25pZmljYW50IGJ5dGVzIGluIHRoZSB3b3Jkcy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHdvcmRBcnJheSA9IENyeXB0b0pTLmxpYi5Xb3JkQXJyYXkuY3JlYXRlKCk7XG5cdCAgICAgICAgICogICAgIHZhciB3b3JkQXJyYXkgPSBDcnlwdG9KUy5saWIuV29yZEFycmF5LmNyZWF0ZShbMHgwMDAxMDIwMywgMHgwNDA1MDYwN10pO1xuXHQgICAgICAgICAqICAgICB2YXIgd29yZEFycmF5ID0gQ3J5cHRvSlMubGliLldvcmRBcnJheS5jcmVhdGUoWzB4MDAwMTAyMDMsIDB4MDQwNTA2MDddLCA2KTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBpbml0OiBmdW5jdGlvbiAod29yZHMsIHNpZ0J5dGVzKSB7XG5cdCAgICAgICAgICAgIHdvcmRzID0gdGhpcy53b3JkcyA9IHdvcmRzIHx8IFtdO1xuXG5cdCAgICAgICAgICAgIGlmIChzaWdCeXRlcyAhPSB1bmRlZmluZWQpIHtcblx0ICAgICAgICAgICAgICAgIHRoaXMuc2lnQnl0ZXMgPSBzaWdCeXRlcztcblx0ICAgICAgICAgICAgfSBlbHNlIHtcblx0ICAgICAgICAgICAgICAgIHRoaXMuc2lnQnl0ZXMgPSB3b3Jkcy5sZW5ndGggKiA0O1xuXHQgICAgICAgICAgICB9XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENvbnZlcnRzIHRoaXMgd29yZCBhcnJheSB0byBhIHN0cmluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7RW5jb2Rlcn0gZW5jb2RlciAoT3B0aW9uYWwpIFRoZSBlbmNvZGluZyBzdHJhdGVneSB0byB1c2UuIERlZmF1bHQ6IENyeXB0b0pTLmVuYy5IZXhcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge3N0cmluZ30gVGhlIHN0cmluZ2lmaWVkIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciBzdHJpbmcgPSB3b3JkQXJyYXkgKyAnJztcblx0ICAgICAgICAgKiAgICAgdmFyIHN0cmluZyA9IHdvcmRBcnJheS50b1N0cmluZygpO1xuXHQgICAgICAgICAqICAgICB2YXIgc3RyaW5nID0gd29yZEFycmF5LnRvU3RyaW5nKENyeXB0b0pTLmVuYy5VdGY4KTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICB0b1N0cmluZzogZnVuY3Rpb24gKGVuY29kZXIpIHtcblx0ICAgICAgICAgICAgcmV0dXJuIChlbmNvZGVyIHx8IEhleCkuc3RyaW5naWZ5KHRoaXMpO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDb25jYXRlbmF0ZXMgYSB3b3JkIGFycmF5IHRvIHRoaXMgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7V29yZEFycmF5fSB3b3JkQXJyYXkgVGhlIHdvcmQgYXJyYXkgdG8gYXBwZW5kLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7V29yZEFycmF5fSBUaGlzIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHdvcmRBcnJheTEuY29uY2F0KHdvcmRBcnJheTIpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIGNvbmNhdDogZnVuY3Rpb24gKHdvcmRBcnJheSkge1xuXHQgICAgICAgICAgICAvLyBTaG9ydGN1dHNcblx0ICAgICAgICAgICAgdmFyIHRoaXNXb3JkcyA9IHRoaXMud29yZHM7XG5cdCAgICAgICAgICAgIHZhciB0aGF0V29yZHMgPSB3b3JkQXJyYXkud29yZHM7XG5cdCAgICAgICAgICAgIHZhciB0aGlzU2lnQnl0ZXMgPSB0aGlzLnNpZ0J5dGVzO1xuXHQgICAgICAgICAgICB2YXIgdGhhdFNpZ0J5dGVzID0gd29yZEFycmF5LnNpZ0J5dGVzO1xuXG5cdCAgICAgICAgICAgIC8vIENsYW1wIGV4Y2VzcyBiaXRzXG5cdCAgICAgICAgICAgIHRoaXMuY2xhbXAoKTtcblxuXHQgICAgICAgICAgICAvLyBDb25jYXRcblx0ICAgICAgICAgICAgaWYgKHRoaXNTaWdCeXRlcyAlIDQpIHtcblx0ICAgICAgICAgICAgICAgIC8vIENvcHkgb25lIGJ5dGUgYXQgYSB0aW1lXG5cdCAgICAgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRoYXRTaWdCeXRlczsgaSsrKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgdmFyIHRoYXRCeXRlID0gKHRoYXRXb3Jkc1tpID4+PiAyXSA+Pj4gKDI0IC0gKGkgJSA0KSAqIDgpKSAmIDB4ZmY7XG5cdCAgICAgICAgICAgICAgICAgICAgdGhpc1dvcmRzWyh0aGlzU2lnQnl0ZXMgKyBpKSA+Pj4gMl0gfD0gdGhhdEJ5dGUgPDwgKDI0IC0gKCh0aGlzU2lnQnl0ZXMgKyBpKSAlIDQpICogOCk7XG5cdCAgICAgICAgICAgICAgICB9XG5cdCAgICAgICAgICAgIH0gZWxzZSB7XG5cdCAgICAgICAgICAgICAgICAvLyBDb3B5IG9uZSB3b3JkIGF0IGEgdGltZVxuXHQgICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aGF0U2lnQnl0ZXM7IGkgKz0gNCkge1xuXHQgICAgICAgICAgICAgICAgICAgIHRoaXNXb3Jkc1sodGhpc1NpZ0J5dGVzICsgaSkgPj4+IDJdID0gdGhhdFdvcmRzW2kgPj4+IDJdO1xuXHQgICAgICAgICAgICAgICAgfVxuXHQgICAgICAgICAgICB9XG5cdCAgICAgICAgICAgIHRoaXMuc2lnQnl0ZXMgKz0gdGhhdFNpZ0J5dGVzO1xuXG5cdCAgICAgICAgICAgIC8vIENoYWluYWJsZVxuXHQgICAgICAgICAgICByZXR1cm4gdGhpcztcblx0ICAgICAgICB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogUmVtb3ZlcyBpbnNpZ25pZmljYW50IGJpdHMuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHdvcmRBcnJheS5jbGFtcCgpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIGNsYW1wOiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgIC8vIFNob3J0Y3V0c1xuXHQgICAgICAgICAgICB2YXIgd29yZHMgPSB0aGlzLndvcmRzO1xuXHQgICAgICAgICAgICB2YXIgc2lnQnl0ZXMgPSB0aGlzLnNpZ0J5dGVzO1xuXG5cdCAgICAgICAgICAgIC8vIENsYW1wXG5cdCAgICAgICAgICAgIHdvcmRzW3NpZ0J5dGVzID4+PiAyXSAmPSAweGZmZmZmZmZmIDw8ICgzMiAtIChzaWdCeXRlcyAlIDQpICogOCk7XG5cdCAgICAgICAgICAgIHdvcmRzLmxlbmd0aCA9IE1hdGguY2VpbChzaWdCeXRlcyAvIDQpO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDcmVhdGVzIGEgY29weSBvZiB0aGlzIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtXb3JkQXJyYXl9IFRoZSBjbG9uZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIGNsb25lID0gd29yZEFycmF5LmNsb25lKCk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgY2xvbmU6IGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgdmFyIGNsb25lID0gQmFzZS5jbG9uZS5jYWxsKHRoaXMpO1xuXHQgICAgICAgICAgICBjbG9uZS53b3JkcyA9IHRoaXMud29yZHMuc2xpY2UoMCk7XG5cblx0ICAgICAgICAgICAgcmV0dXJuIGNsb25lO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDcmVhdGVzIGEgd29yZCBhcnJheSBmaWxsZWQgd2l0aCByYW5kb20gYnl0ZXMuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge251bWJlcn0gbkJ5dGVzIFRoZSBudW1iZXIgb2YgcmFuZG9tIGJ5dGVzIHRvIGdlbmVyYXRlLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7V29yZEFycmF5fSBUaGUgcmFuZG9tIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAc3RhdGljXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciB3b3JkQXJyYXkgPSBDcnlwdG9KUy5saWIuV29yZEFycmF5LnJhbmRvbSgxNik7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgcmFuZG9tOiBmdW5jdGlvbiAobkJ5dGVzKSB7XG5cdCAgICAgICAgICAgIHZhciB3b3JkcyA9IFtdO1xuXG5cdCAgICAgICAgICAgIHZhciByID0gKGZ1bmN0aW9uIChtX3cpIHtcblx0ICAgICAgICAgICAgICAgIHZhciBtX3cgPSBtX3c7XG5cdCAgICAgICAgICAgICAgICB2YXIgbV96ID0gMHgzYWRlNjhiMTtcblx0ICAgICAgICAgICAgICAgIHZhciBtYXNrID0gMHhmZmZmZmZmZjtcblxuXHQgICAgICAgICAgICAgICAgcmV0dXJuIGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgICAgICAgICBtX3ogPSAoMHg5MDY5ICogKG1feiAmIDB4RkZGRikgKyAobV96ID4+IDB4MTApKSAmIG1hc2s7XG5cdCAgICAgICAgICAgICAgICAgICAgbV93ID0gKDB4NDY1MCAqIChtX3cgJiAweEZGRkYpICsgKG1fdyA+PiAweDEwKSkgJiBtYXNrO1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciByZXN1bHQgPSAoKG1feiA8PCAweDEwKSArIG1fdykgJiBtYXNrO1xuXHQgICAgICAgICAgICAgICAgICAgIHJlc3VsdCAvPSAweDEwMDAwMDAwMDtcblx0ICAgICAgICAgICAgICAgICAgICByZXN1bHQgKz0gMC41O1xuXHQgICAgICAgICAgICAgICAgICAgIHJldHVybiByZXN1bHQgKiAoTWF0aC5yYW5kb20oKSA+IC41ID8gMSA6IC0xKTtcblx0ICAgICAgICAgICAgICAgIH1cblx0ICAgICAgICAgICAgfSk7XG5cblx0ICAgICAgICAgICAgZm9yICh2YXIgaSA9IDAsIHJjYWNoZTsgaSA8IG5CeXRlczsgaSArPSA0KSB7XG5cdCAgICAgICAgICAgICAgICB2YXIgX3IgPSByKChyY2FjaGUgfHwgTWF0aC5yYW5kb20oKSkgKiAweDEwMDAwMDAwMCk7XG5cblx0ICAgICAgICAgICAgICAgIHJjYWNoZSA9IF9yKCkgKiAweDNhZGU2N2I3O1xuXHQgICAgICAgICAgICAgICAgd29yZHMucHVzaCgoX3IoKSAqIDB4MTAwMDAwMDAwKSB8IDApO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgcmV0dXJuIG5ldyBXb3JkQXJyYXkuaW5pdCh3b3JkcywgbkJ5dGVzKTtcblx0ICAgICAgICB9XG5cdCAgICB9KTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBFbmNvZGVyIG5hbWVzcGFjZS5cblx0ICAgICAqL1xuXHQgICAgdmFyIENfZW5jID0gQy5lbmMgPSB7fTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBIZXggZW5jb2Rpbmcgc3RyYXRlZ3kuXG5cdCAgICAgKi9cblx0ICAgIHZhciBIZXggPSBDX2VuYy5IZXggPSB7XG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQ29udmVydHMgYSB3b3JkIGFycmF5IHRvIGEgaGV4IHN0cmluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7V29yZEFycmF5fSB3b3JkQXJyYXkgVGhlIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtzdHJpbmd9IFRoZSBoZXggc3RyaW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHN0YXRpY1xuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgaGV4U3RyaW5nID0gQ3J5cHRvSlMuZW5jLkhleC5zdHJpbmdpZnkod29yZEFycmF5KTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBzdHJpbmdpZnk6IGZ1bmN0aW9uICh3b3JkQXJyYXkpIHtcblx0ICAgICAgICAgICAgLy8gU2hvcnRjdXRzXG5cdCAgICAgICAgICAgIHZhciB3b3JkcyA9IHdvcmRBcnJheS53b3Jkcztcblx0ICAgICAgICAgICAgdmFyIHNpZ0J5dGVzID0gd29yZEFycmF5LnNpZ0J5dGVzO1xuXG5cdCAgICAgICAgICAgIC8vIENvbnZlcnRcblx0ICAgICAgICAgICAgdmFyIGhleENoYXJzID0gW107XG5cdCAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgc2lnQnl0ZXM7IGkrKykge1xuXHQgICAgICAgICAgICAgICAgdmFyIGJpdGUgPSAod29yZHNbaSA+Pj4gMl0gPj4+ICgyNCAtIChpICUgNCkgKiA4KSkgJiAweGZmO1xuXHQgICAgICAgICAgICAgICAgaGV4Q2hhcnMucHVzaCgoYml0ZSA+Pj4gNCkudG9TdHJpbmcoMTYpKTtcblx0ICAgICAgICAgICAgICAgIGhleENoYXJzLnB1c2goKGJpdGUgJiAweDBmKS50b1N0cmluZygxNikpO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgcmV0dXJuIGhleENoYXJzLmpvaW4oJycpO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDb252ZXJ0cyBhIGhleCBzdHJpbmcgdG8gYSB3b3JkIGFycmF5LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtzdHJpbmd9IGhleFN0ciBUaGUgaGV4IHN0cmluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1dvcmRBcnJheX0gVGhlIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAc3RhdGljXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciB3b3JkQXJyYXkgPSBDcnlwdG9KUy5lbmMuSGV4LnBhcnNlKGhleFN0cmluZyk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgcGFyc2U6IGZ1bmN0aW9uIChoZXhTdHIpIHtcblx0ICAgICAgICAgICAgLy8gU2hvcnRjdXRcblx0ICAgICAgICAgICAgdmFyIGhleFN0ckxlbmd0aCA9IGhleFN0ci5sZW5ndGg7XG5cblx0ICAgICAgICAgICAgLy8gQ29udmVydFxuXHQgICAgICAgICAgICB2YXIgd29yZHMgPSBbXTtcblx0ICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBoZXhTdHJMZW5ndGg7IGkgKz0gMikge1xuXHQgICAgICAgICAgICAgICAgd29yZHNbaSA+Pj4gM10gfD0gcGFyc2VJbnQoaGV4U3RyLnN1YnN0cihpLCAyKSwgMTYpIDw8ICgyNCAtIChpICUgOCkgKiA0KTtcblx0ICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgIHJldHVybiBuZXcgV29yZEFycmF5LmluaXQod29yZHMsIGhleFN0ckxlbmd0aCAvIDIpO1xuXHQgICAgICAgIH1cblx0ICAgIH07XG5cblx0ICAgIC8qKlxuXHQgICAgICogTGF0aW4xIGVuY29kaW5nIHN0cmF0ZWd5LlxuXHQgICAgICovXG5cdCAgICB2YXIgTGF0aW4xID0gQ19lbmMuTGF0aW4xID0ge1xuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENvbnZlcnRzIGEgd29yZCBhcnJheSB0byBhIExhdGluMSBzdHJpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge1dvcmRBcnJheX0gd29yZEFycmF5IFRoZSB3b3JkIGFycmF5LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7c3RyaW5nfSBUaGUgTGF0aW4xIHN0cmluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBzdGF0aWNcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIGxhdGluMVN0cmluZyA9IENyeXB0b0pTLmVuYy5MYXRpbjEuc3RyaW5naWZ5KHdvcmRBcnJheSk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgc3RyaW5naWZ5OiBmdW5jdGlvbiAod29yZEFycmF5KSB7XG5cdCAgICAgICAgICAgIC8vIFNob3J0Y3V0c1xuXHQgICAgICAgICAgICB2YXIgd29yZHMgPSB3b3JkQXJyYXkud29yZHM7XG5cdCAgICAgICAgICAgIHZhciBzaWdCeXRlcyA9IHdvcmRBcnJheS5zaWdCeXRlcztcblxuXHQgICAgICAgICAgICAvLyBDb252ZXJ0XG5cdCAgICAgICAgICAgIHZhciBsYXRpbjFDaGFycyA9IFtdO1xuXHQgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHNpZ0J5dGVzOyBpKyspIHtcblx0ICAgICAgICAgICAgICAgIHZhciBiaXRlID0gKHdvcmRzW2kgPj4+IDJdID4+PiAoMjQgLSAoaSAlIDQpICogOCkpICYgMHhmZjtcblx0ICAgICAgICAgICAgICAgIGxhdGluMUNoYXJzLnB1c2goU3RyaW5nLmZyb21DaGFyQ29kZShiaXRlKSk7XG5cdCAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICByZXR1cm4gbGF0aW4xQ2hhcnMuam9pbignJyk7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENvbnZlcnRzIGEgTGF0aW4xIHN0cmluZyB0byBhIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gbGF0aW4xU3RyIFRoZSBMYXRpbjEgc3RyaW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7V29yZEFycmF5fSBUaGUgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBzdGF0aWNcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHdvcmRBcnJheSA9IENyeXB0b0pTLmVuYy5MYXRpbjEucGFyc2UobGF0aW4xU3RyaW5nKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBwYXJzZTogZnVuY3Rpb24gKGxhdGluMVN0cikge1xuXHQgICAgICAgICAgICAvLyBTaG9ydGN1dFxuXHQgICAgICAgICAgICB2YXIgbGF0aW4xU3RyTGVuZ3RoID0gbGF0aW4xU3RyLmxlbmd0aDtcblxuXHQgICAgICAgICAgICAvLyBDb252ZXJ0XG5cdCAgICAgICAgICAgIHZhciB3b3JkcyA9IFtdO1xuXHQgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxhdGluMVN0ckxlbmd0aDsgaSsrKSB7XG5cdCAgICAgICAgICAgICAgICB3b3Jkc1tpID4+PiAyXSB8PSAobGF0aW4xU3RyLmNoYXJDb2RlQXQoaSkgJiAweGZmKSA8PCAoMjQgLSAoaSAlIDQpICogOCk7XG5cdCAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICByZXR1cm4gbmV3IFdvcmRBcnJheS5pbml0KHdvcmRzLCBsYXRpbjFTdHJMZW5ndGgpO1xuXHQgICAgICAgIH1cblx0ICAgIH07XG5cblx0ICAgIC8qKlxuXHQgICAgICogVVRGLTggZW5jb2Rpbmcgc3RyYXRlZ3kuXG5cdCAgICAgKi9cblx0ICAgIHZhciBVdGY4ID0gQ19lbmMuVXRmOCA9IHtcblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDb252ZXJ0cyBhIHdvcmQgYXJyYXkgdG8gYSBVVEYtOCBzdHJpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge1dvcmRBcnJheX0gd29yZEFycmF5IFRoZSB3b3JkIGFycmF5LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7c3RyaW5nfSBUaGUgVVRGLTggc3RyaW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHN0YXRpY1xuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgdXRmOFN0cmluZyA9IENyeXB0b0pTLmVuYy5VdGY4LnN0cmluZ2lmeSh3b3JkQXJyYXkpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIHN0cmluZ2lmeTogZnVuY3Rpb24gKHdvcmRBcnJheSkge1xuXHQgICAgICAgICAgICB0cnkge1xuXHQgICAgICAgICAgICAgICAgcmV0dXJuIGRlY29kZVVSSUNvbXBvbmVudChlc2NhcGUoTGF0aW4xLnN0cmluZ2lmeSh3b3JkQXJyYXkpKSk7XG5cdCAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcblx0ICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignTWFsZm9ybWVkIFVURi04IGRhdGEnKTtcblx0ICAgICAgICAgICAgfVxuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDb252ZXJ0cyBhIFVURi04IHN0cmluZyB0byBhIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gdXRmOFN0ciBUaGUgVVRGLTggc3RyaW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7V29yZEFycmF5fSBUaGUgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBzdGF0aWNcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHdvcmRBcnJheSA9IENyeXB0b0pTLmVuYy5VdGY4LnBhcnNlKHV0ZjhTdHJpbmcpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIHBhcnNlOiBmdW5jdGlvbiAodXRmOFN0cikge1xuXHQgICAgICAgICAgICByZXR1cm4gTGF0aW4xLnBhcnNlKHVuZXNjYXBlKGVuY29kZVVSSUNvbXBvbmVudCh1dGY4U3RyKSkpO1xuXHQgICAgICAgIH1cblx0ICAgIH07XG5cblx0ICAgIC8qKlxuXHQgICAgICogQWJzdHJhY3QgYnVmZmVyZWQgYmxvY2sgYWxnb3JpdGhtIHRlbXBsYXRlLlxuXHQgICAgICpcblx0ICAgICAqIFRoZSBwcm9wZXJ0eSBibG9ja1NpemUgbXVzdCBiZSBpbXBsZW1lbnRlZCBpbiBhIGNvbmNyZXRlIHN1YnR5cGUuXG5cdCAgICAgKlxuXHQgICAgICogQHByb3BlcnR5IHtudW1iZXJ9IF9taW5CdWZmZXJTaXplIFRoZSBudW1iZXIgb2YgYmxvY2tzIHRoYXQgc2hvdWxkIGJlIGtlcHQgdW5wcm9jZXNzZWQgaW4gdGhlIGJ1ZmZlci4gRGVmYXVsdDogMFxuXHQgICAgICovXG5cdCAgICB2YXIgQnVmZmVyZWRCbG9ja0FsZ29yaXRobSA9IENfbGliLkJ1ZmZlcmVkQmxvY2tBbGdvcml0aG0gPSBCYXNlLmV4dGVuZCh7XG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogUmVzZXRzIHRoaXMgYmxvY2sgYWxnb3JpdGhtJ3MgZGF0YSBidWZmZXIgdG8gaXRzIGluaXRpYWwgc3RhdGUuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIGJ1ZmZlcmVkQmxvY2tBbGdvcml0aG0ucmVzZXQoKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICByZXNldDogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICAvLyBJbml0aWFsIHZhbHVlc1xuXHQgICAgICAgICAgICB0aGlzLl9kYXRhID0gbmV3IFdvcmRBcnJheS5pbml0KCk7XG5cdCAgICAgICAgICAgIHRoaXMuX25EYXRhQnl0ZXMgPSAwO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBBZGRzIG5ldyBkYXRhIHRvIHRoaXMgYmxvY2sgYWxnb3JpdGhtJ3MgYnVmZmVyLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtXb3JkQXJyYXl8c3RyaW5nfSBkYXRhIFRoZSBkYXRhIHRvIGFwcGVuZC4gU3RyaW5ncyBhcmUgY29udmVydGVkIHRvIGEgV29yZEFycmF5IHVzaW5nIFVURi04LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICBidWZmZXJlZEJsb2NrQWxnb3JpdGhtLl9hcHBlbmQoJ2RhdGEnKTtcblx0ICAgICAgICAgKiAgICAgYnVmZmVyZWRCbG9ja0FsZ29yaXRobS5fYXBwZW5kKHdvcmRBcnJheSk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgX2FwcGVuZDogZnVuY3Rpb24gKGRhdGEpIHtcblx0ICAgICAgICAgICAgLy8gQ29udmVydCBzdHJpbmcgdG8gV29yZEFycmF5LCBlbHNlIGFzc3VtZSBXb3JkQXJyYXkgYWxyZWFkeVxuXHQgICAgICAgICAgICBpZiAodHlwZW9mIGRhdGEgPT0gJ3N0cmluZycpIHtcblx0ICAgICAgICAgICAgICAgIGRhdGEgPSBVdGY4LnBhcnNlKGRhdGEpO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgLy8gQXBwZW5kXG5cdCAgICAgICAgICAgIHRoaXMuX2RhdGEuY29uY2F0KGRhdGEpO1xuXHQgICAgICAgICAgICB0aGlzLl9uRGF0YUJ5dGVzICs9IGRhdGEuc2lnQnl0ZXM7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIFByb2Nlc3NlcyBhdmFpbGFibGUgZGF0YSBibG9ja3MuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBUaGlzIG1ldGhvZCBpbnZva2VzIF9kb1Byb2Nlc3NCbG9jayhvZmZzZXQpLCB3aGljaCBtdXN0IGJlIGltcGxlbWVudGVkIGJ5IGEgY29uY3JldGUgc3VidHlwZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gZG9GbHVzaCBXaGV0aGVyIGFsbCBibG9ja3MgYW5kIHBhcnRpYWwgYmxvY2tzIHNob3VsZCBiZSBwcm9jZXNzZWQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtXb3JkQXJyYXl9IFRoZSBwcm9jZXNzZWQgZGF0YS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHByb2Nlc3NlZERhdGEgPSBidWZmZXJlZEJsb2NrQWxnb3JpdGhtLl9wcm9jZXNzKCk7XG5cdCAgICAgICAgICogICAgIHZhciBwcm9jZXNzZWREYXRhID0gYnVmZmVyZWRCbG9ja0FsZ29yaXRobS5fcHJvY2VzcyghISdmbHVzaCcpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIF9wcm9jZXNzOiBmdW5jdGlvbiAoZG9GbHVzaCkge1xuXHQgICAgICAgICAgICAvLyBTaG9ydGN1dHNcblx0ICAgICAgICAgICAgdmFyIGRhdGEgPSB0aGlzLl9kYXRhO1xuXHQgICAgICAgICAgICB2YXIgZGF0YVdvcmRzID0gZGF0YS53b3Jkcztcblx0ICAgICAgICAgICAgdmFyIGRhdGFTaWdCeXRlcyA9IGRhdGEuc2lnQnl0ZXM7XG5cdCAgICAgICAgICAgIHZhciBibG9ja1NpemUgPSB0aGlzLmJsb2NrU2l6ZTtcblx0ICAgICAgICAgICAgdmFyIGJsb2NrU2l6ZUJ5dGVzID0gYmxvY2tTaXplICogNDtcblxuXHQgICAgICAgICAgICAvLyBDb3VudCBibG9ja3MgcmVhZHlcblx0ICAgICAgICAgICAgdmFyIG5CbG9ja3NSZWFkeSA9IGRhdGFTaWdCeXRlcyAvIGJsb2NrU2l6ZUJ5dGVzO1xuXHQgICAgICAgICAgICBpZiAoZG9GbHVzaCkge1xuXHQgICAgICAgICAgICAgICAgLy8gUm91bmQgdXAgdG8gaW5jbHVkZSBwYXJ0aWFsIGJsb2Nrc1xuXHQgICAgICAgICAgICAgICAgbkJsb2Nrc1JlYWR5ID0gTWF0aC5jZWlsKG5CbG9ja3NSZWFkeSk7XG5cdCAgICAgICAgICAgIH0gZWxzZSB7XG5cdCAgICAgICAgICAgICAgICAvLyBSb3VuZCBkb3duIHRvIGluY2x1ZGUgb25seSBmdWxsIGJsb2Nrcyxcblx0ICAgICAgICAgICAgICAgIC8vIGxlc3MgdGhlIG51bWJlciBvZiBibG9ja3MgdGhhdCBtdXN0IHJlbWFpbiBpbiB0aGUgYnVmZmVyXG5cdCAgICAgICAgICAgICAgICBuQmxvY2tzUmVhZHkgPSBNYXRoLm1heCgobkJsb2Nrc1JlYWR5IHwgMCkgLSB0aGlzLl9taW5CdWZmZXJTaXplLCAwKTtcblx0ICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgIC8vIENvdW50IHdvcmRzIHJlYWR5XG5cdCAgICAgICAgICAgIHZhciBuV29yZHNSZWFkeSA9IG5CbG9ja3NSZWFkeSAqIGJsb2NrU2l6ZTtcblxuXHQgICAgICAgICAgICAvLyBDb3VudCBieXRlcyByZWFkeVxuXHQgICAgICAgICAgICB2YXIgbkJ5dGVzUmVhZHkgPSBNYXRoLm1pbihuV29yZHNSZWFkeSAqIDQsIGRhdGFTaWdCeXRlcyk7XG5cblx0ICAgICAgICAgICAgLy8gUHJvY2VzcyBibG9ja3Ncblx0ICAgICAgICAgICAgaWYgKG5Xb3Jkc1JlYWR5KSB7XG5cdCAgICAgICAgICAgICAgICBmb3IgKHZhciBvZmZzZXQgPSAwOyBvZmZzZXQgPCBuV29yZHNSZWFkeTsgb2Zmc2V0ICs9IGJsb2NrU2l6ZSkge1xuXHQgICAgICAgICAgICAgICAgICAgIC8vIFBlcmZvcm0gY29uY3JldGUtYWxnb3JpdGhtIGxvZ2ljXG5cdCAgICAgICAgICAgICAgICAgICAgdGhpcy5fZG9Qcm9jZXNzQmxvY2soZGF0YVdvcmRzLCBvZmZzZXQpO1xuXHQgICAgICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgICAgICAvLyBSZW1vdmUgcHJvY2Vzc2VkIHdvcmRzXG5cdCAgICAgICAgICAgICAgICB2YXIgcHJvY2Vzc2VkV29yZHMgPSBkYXRhV29yZHMuc3BsaWNlKDAsIG5Xb3Jkc1JlYWR5KTtcblx0ICAgICAgICAgICAgICAgIGRhdGEuc2lnQnl0ZXMgLT0gbkJ5dGVzUmVhZHk7XG5cdCAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICAvLyBSZXR1cm4gcHJvY2Vzc2VkIHdvcmRzXG5cdCAgICAgICAgICAgIHJldHVybiBuZXcgV29yZEFycmF5LmluaXQocHJvY2Vzc2VkV29yZHMsIG5CeXRlc1JlYWR5KTtcblx0ICAgICAgICB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQ3JlYXRlcyBhIGNvcHkgb2YgdGhpcyBvYmplY3QuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtPYmplY3R9IFRoZSBjbG9uZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIGNsb25lID0gYnVmZmVyZWRCbG9ja0FsZ29yaXRobS5jbG9uZSgpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgIHZhciBjbG9uZSA9IEJhc2UuY2xvbmUuY2FsbCh0aGlzKTtcblx0ICAgICAgICAgICAgY2xvbmUuX2RhdGEgPSB0aGlzLl9kYXRhLmNsb25lKCk7XG5cblx0ICAgICAgICAgICAgcmV0dXJuIGNsb25lO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICBfbWluQnVmZmVyU2l6ZTogMFxuXHQgICAgfSk7XG5cblx0ICAgIC8qKlxuXHQgICAgICogQWJzdHJhY3QgaGFzaGVyIHRlbXBsYXRlLlxuXHQgICAgICpcblx0ICAgICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBibG9ja1NpemUgVGhlIG51bWJlciBvZiAzMi1iaXQgd29yZHMgdGhpcyBoYXNoZXIgb3BlcmF0ZXMgb24uIERlZmF1bHQ6IDE2ICg1MTIgYml0cylcblx0ICAgICAqL1xuXHQgICAgdmFyIEhhc2hlciA9IENfbGliLkhhc2hlciA9IEJ1ZmZlcmVkQmxvY2tBbGdvcml0aG0uZXh0ZW5kKHtcblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDb25maWd1cmF0aW9uIG9wdGlvbnMuXG5cdCAgICAgICAgICovXG5cdCAgICAgICAgY2ZnOiBCYXNlLmV4dGVuZCgpLFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogSW5pdGlhbGl6ZXMgYSBuZXdseSBjcmVhdGVkIGhhc2hlci5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBjZmcgKE9wdGlvbmFsKSBUaGUgY29uZmlndXJhdGlvbiBvcHRpb25zIHRvIHVzZSBmb3IgdGhpcyBoYXNoIGNvbXB1dGF0aW9uLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgaGFzaGVyID0gQ3J5cHRvSlMuYWxnby5TSEEyNTYuY3JlYXRlKCk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgaW5pdDogZnVuY3Rpb24gKGNmZykge1xuXHQgICAgICAgICAgICAvLyBBcHBseSBjb25maWcgZGVmYXVsdHNcblx0ICAgICAgICAgICAgdGhpcy5jZmcgPSB0aGlzLmNmZy5leHRlbmQoY2ZnKTtcblxuXHQgICAgICAgICAgICAvLyBTZXQgaW5pdGlhbCB2YWx1ZXNcblx0ICAgICAgICAgICAgdGhpcy5yZXNldCgpO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBSZXNldHMgdGhpcyBoYXNoZXIgdG8gaXRzIGluaXRpYWwgc3RhdGUuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIGhhc2hlci5yZXNldCgpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIHJlc2V0OiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgIC8vIFJlc2V0IGRhdGEgYnVmZmVyXG5cdCAgICAgICAgICAgIEJ1ZmZlcmVkQmxvY2tBbGdvcml0aG0ucmVzZXQuY2FsbCh0aGlzKTtcblxuXHQgICAgICAgICAgICAvLyBQZXJmb3JtIGNvbmNyZXRlLWhhc2hlciBsb2dpY1xuXHQgICAgICAgICAgICB0aGlzLl9kb1Jlc2V0KCk7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIFVwZGF0ZXMgdGhpcyBoYXNoZXIgd2l0aCBhIG1lc3NhZ2UuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge1dvcmRBcnJheXxzdHJpbmd9IG1lc3NhZ2VVcGRhdGUgVGhlIG1lc3NhZ2UgdG8gYXBwZW5kLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7SGFzaGVyfSBUaGlzIGhhc2hlci5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgaGFzaGVyLnVwZGF0ZSgnbWVzc2FnZScpO1xuXHQgICAgICAgICAqICAgICBoYXNoZXIudXBkYXRlKHdvcmRBcnJheSk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgdXBkYXRlOiBmdW5jdGlvbiAobWVzc2FnZVVwZGF0ZSkge1xuXHQgICAgICAgICAgICAvLyBBcHBlbmRcblx0ICAgICAgICAgICAgdGhpcy5fYXBwZW5kKG1lc3NhZ2VVcGRhdGUpO1xuXG5cdCAgICAgICAgICAgIC8vIFVwZGF0ZSB0aGUgaGFzaFxuXHQgICAgICAgICAgICB0aGlzLl9wcm9jZXNzKCk7XG5cblx0ICAgICAgICAgICAgLy8gQ2hhaW5hYmxlXG5cdCAgICAgICAgICAgIHJldHVybiB0aGlzO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBGaW5hbGl6ZXMgdGhlIGhhc2ggY29tcHV0YXRpb24uXG5cdCAgICAgICAgICogTm90ZSB0aGF0IHRoZSBmaW5hbGl6ZSBvcGVyYXRpb24gaXMgZWZmZWN0aXZlbHkgYSBkZXN0cnVjdGl2ZSwgcmVhZC1vbmNlIG9wZXJhdGlvbi5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7V29yZEFycmF5fHN0cmluZ30gbWVzc2FnZVVwZGF0ZSAoT3B0aW9uYWwpIEEgZmluYWwgbWVzc2FnZSB1cGRhdGUuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtXb3JkQXJyYXl9IFRoZSBoYXNoLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgaGFzaCA9IGhhc2hlci5maW5hbGl6ZSgpO1xuXHQgICAgICAgICAqICAgICB2YXIgaGFzaCA9IGhhc2hlci5maW5hbGl6ZSgnbWVzc2FnZScpO1xuXHQgICAgICAgICAqICAgICB2YXIgaGFzaCA9IGhhc2hlci5maW5hbGl6ZSh3b3JkQXJyYXkpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIGZpbmFsaXplOiBmdW5jdGlvbiAobWVzc2FnZVVwZGF0ZSkge1xuXHQgICAgICAgICAgICAvLyBGaW5hbCBtZXNzYWdlIHVwZGF0ZVxuXHQgICAgICAgICAgICBpZiAobWVzc2FnZVVwZGF0ZSkge1xuXHQgICAgICAgICAgICAgICAgdGhpcy5fYXBwZW5kKG1lc3NhZ2VVcGRhdGUpO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgLy8gUGVyZm9ybSBjb25jcmV0ZS1oYXNoZXIgbG9naWNcblx0ICAgICAgICAgICAgdmFyIGhhc2ggPSB0aGlzLl9kb0ZpbmFsaXplKCk7XG5cblx0ICAgICAgICAgICAgcmV0dXJuIGhhc2g7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIGJsb2NrU2l6ZTogNTEyLzMyLFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQ3JlYXRlcyBhIHNob3J0Y3V0IGZ1bmN0aW9uIHRvIGEgaGFzaGVyJ3Mgb2JqZWN0IGludGVyZmFjZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7SGFzaGVyfSBoYXNoZXIgVGhlIGhhc2hlciB0byBjcmVhdGUgYSBoZWxwZXIgZm9yLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7RnVuY3Rpb259IFRoZSBzaG9ydGN1dCBmdW5jdGlvbi5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBzdGF0aWNcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIFNIQTI1NiA9IENyeXB0b0pTLmxpYi5IYXNoZXIuX2NyZWF0ZUhlbHBlcihDcnlwdG9KUy5hbGdvLlNIQTI1Nik7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgX2NyZWF0ZUhlbHBlcjogZnVuY3Rpb24gKGhhc2hlcikge1xuXHQgICAgICAgICAgICByZXR1cm4gZnVuY3Rpb24gKG1lc3NhZ2UsIGNmZykge1xuXHQgICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBoYXNoZXIuaW5pdChjZmcpLmZpbmFsaXplKG1lc3NhZ2UpO1xuXHQgICAgICAgICAgICB9O1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDcmVhdGVzIGEgc2hvcnRjdXQgZnVuY3Rpb24gdG8gdGhlIEhNQUMncyBvYmplY3QgaW50ZXJmYWNlLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtIYXNoZXJ9IGhhc2hlciBUaGUgaGFzaGVyIHRvIHVzZSBpbiB0aGlzIEhNQUMgaGVscGVyLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7RnVuY3Rpb259IFRoZSBzaG9ydGN1dCBmdW5jdGlvbi5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBzdGF0aWNcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIEhtYWNTSEEyNTYgPSBDcnlwdG9KUy5saWIuSGFzaGVyLl9jcmVhdGVIbWFjSGVscGVyKENyeXB0b0pTLmFsZ28uU0hBMjU2KTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBfY3JlYXRlSG1hY0hlbHBlcjogZnVuY3Rpb24gKGhhc2hlcikge1xuXHQgICAgICAgICAgICByZXR1cm4gZnVuY3Rpb24gKG1lc3NhZ2UsIGtleSkge1xuXHQgICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBDX2FsZ28uSE1BQy5pbml0KGhhc2hlciwga2V5KS5maW5hbGl6ZShtZXNzYWdlKTtcblx0ICAgICAgICAgICAgfTtcblx0ICAgICAgICB9XG5cdCAgICB9KTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBBbGdvcml0aG0gbmFtZXNwYWNlLlxuXHQgICAgICovXG5cdCAgICB2YXIgQ19hbGdvID0gQy5hbGdvID0ge307XG5cblx0ICAgIHJldHVybiBDO1xuXHR9KE1hdGgpKTtcblxuXG5cdHJldHVybiBDcnlwdG9KUztcblxufSkpOyIsIjsoZnVuY3Rpb24gKHJvb3QsIGZhY3RvcnksIHVuZGVmKSB7XG5cdGlmICh0eXBlb2YgZXhwb3J0cyA9PT0gXCJvYmplY3RcIikge1xuXHRcdC8vIENvbW1vbkpTXG5cdFx0bW9kdWxlLmV4cG9ydHMgPSBleHBvcnRzID0gZmFjdG9yeShyZXF1aXJlKFwiLi9jb3JlXCIpLCByZXF1aXJlKFwiLi94NjQtY29yZVwiKSk7XG5cdH1cblx0ZWxzZSBpZiAodHlwZW9mIGRlZmluZSA9PT0gXCJmdW5jdGlvblwiICYmIGRlZmluZS5hbWQpIHtcblx0XHQvLyBBTURcblx0XHRkZWZpbmUoW1wiLi9jb3JlXCIsIFwiLi94NjQtY29yZVwiXSwgZmFjdG9yeSk7XG5cdH1cblx0ZWxzZSB7XG5cdFx0Ly8gR2xvYmFsIChicm93c2VyKVxuXHRcdGZhY3Rvcnkocm9vdC5DcnlwdG9KUyk7XG5cdH1cbn0odGhpcywgZnVuY3Rpb24gKENyeXB0b0pTKSB7XG5cblx0KGZ1bmN0aW9uIChNYXRoKSB7XG5cdCAgICAvLyBTaG9ydGN1dHNcblx0ICAgIHZhciBDID0gQ3J5cHRvSlM7XG5cdCAgICB2YXIgQ19saWIgPSBDLmxpYjtcblx0ICAgIHZhciBXb3JkQXJyYXkgPSBDX2xpYi5Xb3JkQXJyYXk7XG5cdCAgICB2YXIgSGFzaGVyID0gQ19saWIuSGFzaGVyO1xuXHQgICAgdmFyIENfeDY0ID0gQy54NjQ7XG5cdCAgICB2YXIgWDY0V29yZCA9IENfeDY0LldvcmQ7XG5cdCAgICB2YXIgQ19hbGdvID0gQy5hbGdvO1xuXG5cdCAgICAvLyBDb25zdGFudHMgdGFibGVzXG5cdCAgICB2YXIgUkhPX09GRlNFVFMgPSBbXTtcblx0ICAgIHZhciBQSV9JTkRFWEVTICA9IFtdO1xuXHQgICAgdmFyIFJPVU5EX0NPTlNUQU5UUyA9IFtdO1xuXG5cdCAgICAvLyBDb21wdXRlIENvbnN0YW50c1xuXHQgICAgKGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAvLyBDb21wdXRlIHJobyBvZmZzZXQgY29uc3RhbnRzXG5cdCAgICAgICAgdmFyIHggPSAxLCB5ID0gMDtcblx0ICAgICAgICBmb3IgKHZhciB0ID0gMDsgdCA8IDI0OyB0KyspIHtcblx0ICAgICAgICAgICAgUkhPX09GRlNFVFNbeCArIDUgKiB5XSA9ICgodCArIDEpICogKHQgKyAyKSAvIDIpICUgNjQ7XG5cblx0ICAgICAgICAgICAgdmFyIG5ld1ggPSB5ICUgNTtcblx0ICAgICAgICAgICAgdmFyIG5ld1kgPSAoMiAqIHggKyAzICogeSkgJSA1O1xuXHQgICAgICAgICAgICB4ID0gbmV3WDtcblx0ICAgICAgICAgICAgeSA9IG5ld1k7XG5cdCAgICAgICAgfVxuXG5cdCAgICAgICAgLy8gQ29tcHV0ZSBwaSBpbmRleCBjb25zdGFudHNcblx0ICAgICAgICBmb3IgKHZhciB4ID0gMDsgeCA8IDU7IHgrKykge1xuXHQgICAgICAgICAgICBmb3IgKHZhciB5ID0gMDsgeSA8IDU7IHkrKykge1xuXHQgICAgICAgICAgICAgICAgUElfSU5ERVhFU1t4ICsgNSAqIHldID0geSArICgoMiAqIHggKyAzICogeSkgJSA1KSAqIDU7XG5cdCAgICAgICAgICAgIH1cblx0ICAgICAgICB9XG5cblx0ICAgICAgICAvLyBDb21wdXRlIHJvdW5kIGNvbnN0YW50c1xuXHQgICAgICAgIHZhciBMRlNSID0gMHgwMTtcblx0ICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IDI0OyBpKyspIHtcblx0ICAgICAgICAgICAgdmFyIHJvdW5kQ29uc3RhbnRNc3cgPSAwO1xuXHQgICAgICAgICAgICB2YXIgcm91bmRDb25zdGFudExzdyA9IDA7XG5cblx0ICAgICAgICAgICAgZm9yICh2YXIgaiA9IDA7IGogPCA3OyBqKyspIHtcblx0ICAgICAgICAgICAgICAgIGlmIChMRlNSICYgMHgwMSkge1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciBiaXRQb3NpdGlvbiA9ICgxIDw8IGopIC0gMTtcblx0ICAgICAgICAgICAgICAgICAgICBpZiAoYml0UG9zaXRpb24gPCAzMikge1xuXHQgICAgICAgICAgICAgICAgICAgICAgICByb3VuZENvbnN0YW50THN3IF49IDEgPDwgYml0UG9zaXRpb247XG5cdCAgICAgICAgICAgICAgICAgICAgfSBlbHNlIC8qIGlmIChiaXRQb3NpdGlvbiA+PSAzMikgKi8ge1xuXHQgICAgICAgICAgICAgICAgICAgICAgICByb3VuZENvbnN0YW50TXN3IF49IDEgPDwgKGJpdFBvc2l0aW9uIC0gMzIpO1xuXHQgICAgICAgICAgICAgICAgICAgIH1cblx0ICAgICAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICAgICAgLy8gQ29tcHV0ZSBuZXh0IExGU1Jcblx0ICAgICAgICAgICAgICAgIGlmIChMRlNSICYgMHg4MCkge1xuXHQgICAgICAgICAgICAgICAgICAgIC8vIFByaW1pdGl2ZSBwb2x5bm9taWFsIG92ZXIgR0YoMik6IHheOCArIHheNiArIHheNSArIHheNCArIDFcblx0ICAgICAgICAgICAgICAgICAgICBMRlNSID0gKExGU1IgPDwgMSkgXiAweDcxO1xuXHQgICAgICAgICAgICAgICAgfSBlbHNlIHtcblx0ICAgICAgICAgICAgICAgICAgICBMRlNSIDw8PSAxO1xuXHQgICAgICAgICAgICAgICAgfVxuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgUk9VTkRfQ09OU1RBTlRTW2ldID0gWDY0V29yZC5jcmVhdGUocm91bmRDb25zdGFudE1zdywgcm91bmRDb25zdGFudExzdyk7XG5cdCAgICAgICAgfVxuXHQgICAgfSgpKTtcblxuXHQgICAgLy8gUmV1c2FibGUgb2JqZWN0cyBmb3IgdGVtcG9yYXJ5IHZhbHVlc1xuXHQgICAgdmFyIFQgPSBbXTtcblx0ICAgIChmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCAyNTsgaSsrKSB7XG5cdCAgICAgICAgICAgIFRbaV0gPSBYNjRXb3JkLmNyZWF0ZSgpO1xuXHQgICAgICAgIH1cblx0ICAgIH0oKSk7XG5cblx0ICAgIC8qKlxuXHQgICAgICogU0hBLTMgaGFzaCBhbGdvcml0aG0uXG5cdCAgICAgKi9cblx0ICAgIHZhciBTSEEzID0gQ19hbGdvLlNIQTMgPSBIYXNoZXIuZXh0ZW5kKHtcblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDb25maWd1cmF0aW9uIG9wdGlvbnMuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcHJvcGVydHkge251bWJlcn0gb3V0cHV0TGVuZ3RoXG5cdCAgICAgICAgICogICBUaGUgZGVzaXJlZCBudW1iZXIgb2YgYml0cyBpbiB0aGUgb3V0cHV0IGhhc2guXG5cdCAgICAgICAgICogICBPbmx5IHZhbHVlcyBwZXJtaXR0ZWQgYXJlOiAyMjQsIDI1NiwgMzg0LCA1MTIuXG5cdCAgICAgICAgICogICBEZWZhdWx0OiA1MTJcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBjZmc6IEhhc2hlci5jZmcuZXh0ZW5kKHtcblx0ICAgICAgICAgICAgb3V0cHV0TGVuZ3RoOiA1MTJcblx0ICAgICAgICB9KSxcblxuXHQgICAgICAgIF9kb1Jlc2V0OiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgIHZhciBzdGF0ZSA9IHRoaXMuX3N0YXRlID0gW11cblx0ICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCAyNTsgaSsrKSB7XG5cdCAgICAgICAgICAgICAgICBzdGF0ZVtpXSA9IG5ldyBYNjRXb3JkLmluaXQoKTtcblx0ICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgIHRoaXMuYmxvY2tTaXplID0gKDE2MDAgLSAyICogdGhpcy5jZmcub3V0cHV0TGVuZ3RoKSAvIDMyO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICBfZG9Qcm9jZXNzQmxvY2s6IGZ1bmN0aW9uIChNLCBvZmZzZXQpIHtcblx0ICAgICAgICAgICAgLy8gU2hvcnRjdXRzXG5cdCAgICAgICAgICAgIHZhciBzdGF0ZSA9IHRoaXMuX3N0YXRlO1xuXHQgICAgICAgICAgICB2YXIgbkJsb2NrU2l6ZUxhbmVzID0gdGhpcy5ibG9ja1NpemUgLyAyO1xuXG5cdCAgICAgICAgICAgIC8vIEFic29yYlxuXHQgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IG5CbG9ja1NpemVMYW5lczsgaSsrKSB7XG5cdCAgICAgICAgICAgICAgICAvLyBTaG9ydGN1dHNcblx0ICAgICAgICAgICAgICAgIHZhciBNMmkgID0gTVtvZmZzZXQgKyAyICogaV07XG5cdCAgICAgICAgICAgICAgICB2YXIgTTJpMSA9IE1bb2Zmc2V0ICsgMiAqIGkgKyAxXTtcblxuXHQgICAgICAgICAgICAgICAgLy8gU3dhcCBlbmRpYW5cblx0ICAgICAgICAgICAgICAgIE0yaSA9IChcblx0ICAgICAgICAgICAgICAgICAgICAoKChNMmkgPDwgOCkgIHwgKE0yaSA+Pj4gMjQpKSAmIDB4MDBmZjAwZmYpIHxcblx0ICAgICAgICAgICAgICAgICAgICAoKChNMmkgPDwgMjQpIHwgKE0yaSA+Pj4gOCkpICAmIDB4ZmYwMGZmMDApXG5cdCAgICAgICAgICAgICAgICApO1xuXHQgICAgICAgICAgICAgICAgTTJpMSA9IChcblx0ICAgICAgICAgICAgICAgICAgICAoKChNMmkxIDw8IDgpICB8IChNMmkxID4+PiAyNCkpICYgMHgwMGZmMDBmZikgfFxuXHQgICAgICAgICAgICAgICAgICAgICgoKE0yaTEgPDwgMjQpIHwgKE0yaTEgPj4+IDgpKSAgJiAweGZmMDBmZjAwKVxuXHQgICAgICAgICAgICAgICAgKTtcblxuXHQgICAgICAgICAgICAgICAgLy8gQWJzb3JiIG1lc3NhZ2UgaW50byBzdGF0ZVxuXHQgICAgICAgICAgICAgICAgdmFyIGxhbmUgPSBzdGF0ZVtpXTtcblx0ICAgICAgICAgICAgICAgIGxhbmUuaGlnaCBePSBNMmkxO1xuXHQgICAgICAgICAgICAgICAgbGFuZS5sb3cgIF49IE0yaTtcblx0ICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgIC8vIFJvdW5kc1xuXHQgICAgICAgICAgICBmb3IgKHZhciByb3VuZCA9IDA7IHJvdW5kIDwgMjQ7IHJvdW5kKyspIHtcblx0ICAgICAgICAgICAgICAgIC8vIFRoZXRhXG5cdCAgICAgICAgICAgICAgICBmb3IgKHZhciB4ID0gMDsgeCA8IDU7IHgrKykge1xuXHQgICAgICAgICAgICAgICAgICAgIC8vIE1peCBjb2x1bW4gbGFuZXNcblx0ICAgICAgICAgICAgICAgICAgICB2YXIgdE1zdyA9IDAsIHRMc3cgPSAwO1xuXHQgICAgICAgICAgICAgICAgICAgIGZvciAodmFyIHkgPSAwOyB5IDwgNTsgeSsrKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHZhciBsYW5lID0gc3RhdGVbeCArIDUgKiB5XTtcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdE1zdyBePSBsYW5lLmhpZ2g7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHRMc3cgXj0gbGFuZS5sb3c7XG5cdCAgICAgICAgICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgICAgICAgICAgLy8gVGVtcG9yYXJ5IHZhbHVlc1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciBUeCA9IFRbeF07XG5cdCAgICAgICAgICAgICAgICAgICAgVHguaGlnaCA9IHRNc3c7XG5cdCAgICAgICAgICAgICAgICAgICAgVHgubG93ICA9IHRMc3c7XG5cdCAgICAgICAgICAgICAgICB9XG5cdCAgICAgICAgICAgICAgICBmb3IgKHZhciB4ID0gMDsgeCA8IDU7IHgrKykge1xuXHQgICAgICAgICAgICAgICAgICAgIC8vIFNob3J0Y3V0c1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciBUeDQgPSBUWyh4ICsgNCkgJSA1XTtcblx0ICAgICAgICAgICAgICAgICAgICB2YXIgVHgxID0gVFsoeCArIDEpICUgNV07XG5cdCAgICAgICAgICAgICAgICAgICAgdmFyIFR4MU1zdyA9IFR4MS5oaWdoO1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciBUeDFMc3cgPSBUeDEubG93O1xuXG5cdCAgICAgICAgICAgICAgICAgICAgLy8gTWl4IHN1cnJvdW5kaW5nIGNvbHVtbnNcblx0ICAgICAgICAgICAgICAgICAgICB2YXIgdE1zdyA9IFR4NC5oaWdoIF4gKChUeDFNc3cgPDwgMSkgfCAoVHgxTHN3ID4+PiAzMSkpO1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciB0THN3ID0gVHg0LmxvdyAgXiAoKFR4MUxzdyA8PCAxKSB8IChUeDFNc3cgPj4+IDMxKSk7XG5cdCAgICAgICAgICAgICAgICAgICAgZm9yICh2YXIgeSA9IDA7IHkgPCA1OyB5KyspIHtcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGxhbmUgPSBzdGF0ZVt4ICsgNSAqIHldO1xuXHQgICAgICAgICAgICAgICAgICAgICAgICBsYW5lLmhpZ2ggXj0gdE1zdztcblx0ICAgICAgICAgICAgICAgICAgICAgICAgbGFuZS5sb3cgIF49IHRMc3c7XG5cdCAgICAgICAgICAgICAgICAgICAgfVxuXHQgICAgICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgICAgICAvLyBSaG8gUGlcblx0ICAgICAgICAgICAgICAgIGZvciAodmFyIGxhbmVJbmRleCA9IDE7IGxhbmVJbmRleCA8IDI1OyBsYW5lSW5kZXgrKykge1xuXHQgICAgICAgICAgICAgICAgICAgIC8vIFNob3J0Y3V0c1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciBsYW5lID0gc3RhdGVbbGFuZUluZGV4XTtcblx0ICAgICAgICAgICAgICAgICAgICB2YXIgbGFuZU1zdyA9IGxhbmUuaGlnaDtcblx0ICAgICAgICAgICAgICAgICAgICB2YXIgbGFuZUxzdyA9IGxhbmUubG93O1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciByaG9PZmZzZXQgPSBSSE9fT0ZGU0VUU1tsYW5lSW5kZXhdO1xuXG5cdCAgICAgICAgICAgICAgICAgICAgLy8gUm90YXRlIGxhbmVzXG5cdCAgICAgICAgICAgICAgICAgICAgaWYgKHJob09mZnNldCA8IDMyKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHZhciB0TXN3ID0gKGxhbmVNc3cgPDwgcmhvT2Zmc2V0KSB8IChsYW5lTHN3ID4+PiAoMzIgLSByaG9PZmZzZXQpKTtcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHRMc3cgPSAobGFuZUxzdyA8PCByaG9PZmZzZXQpIHwgKGxhbmVNc3cgPj4+ICgzMiAtIHJob09mZnNldCkpO1xuXHQgICAgICAgICAgICAgICAgICAgIH0gZWxzZSAvKiBpZiAocmhvT2Zmc2V0ID49IDMyKSAqLyB7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHZhciB0TXN3ID0gKGxhbmVMc3cgPDwgKHJob09mZnNldCAtIDMyKSkgfCAobGFuZU1zdyA+Pj4gKDY0IC0gcmhvT2Zmc2V0KSk7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHZhciB0THN3ID0gKGxhbmVNc3cgPDwgKHJob09mZnNldCAtIDMyKSkgfCAobGFuZUxzdyA+Pj4gKDY0IC0gcmhvT2Zmc2V0KSk7XG5cdCAgICAgICAgICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgICAgICAgICAgLy8gVHJhbnNwb3NlIGxhbmVzXG5cdCAgICAgICAgICAgICAgICAgICAgdmFyIFRQaUxhbmUgPSBUW1BJX0lOREVYRVNbbGFuZUluZGV4XV07XG5cdCAgICAgICAgICAgICAgICAgICAgVFBpTGFuZS5oaWdoID0gdE1zdztcblx0ICAgICAgICAgICAgICAgICAgICBUUGlMYW5lLmxvdyAgPSB0THN3O1xuXHQgICAgICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgICAgICAvLyBSaG8gcGkgYXQgeCA9IHkgPSAwXG5cdCAgICAgICAgICAgICAgICB2YXIgVDAgPSBUWzBdO1xuXHQgICAgICAgICAgICAgICAgdmFyIHN0YXRlMCA9IHN0YXRlWzBdO1xuXHQgICAgICAgICAgICAgICAgVDAuaGlnaCA9IHN0YXRlMC5oaWdoO1xuXHQgICAgICAgICAgICAgICAgVDAubG93ICA9IHN0YXRlMC5sb3c7XG5cblx0ICAgICAgICAgICAgICAgIC8vIENoaVxuXHQgICAgICAgICAgICAgICAgZm9yICh2YXIgeCA9IDA7IHggPCA1OyB4KyspIHtcblx0ICAgICAgICAgICAgICAgICAgICBmb3IgKHZhciB5ID0gMDsgeSA8IDU7IHkrKykge1xuXHQgICAgICAgICAgICAgICAgICAgICAgICAvLyBTaG9ydGN1dHNcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGxhbmVJbmRleCA9IHggKyA1ICogeTtcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGxhbmUgPSBzdGF0ZVtsYW5lSW5kZXhdO1xuXHQgICAgICAgICAgICAgICAgICAgICAgICB2YXIgVExhbmUgPSBUW2xhbmVJbmRleF07XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHZhciBUeDFMYW5lID0gVFsoKHggKyAxKSAlIDUpICsgNSAqIHldO1xuXHQgICAgICAgICAgICAgICAgICAgICAgICB2YXIgVHgyTGFuZSA9IFRbKCh4ICsgMikgJSA1KSArIDUgKiB5XTtcblxuXHQgICAgICAgICAgICAgICAgICAgICAgICAvLyBNaXggcm93c1xuXHQgICAgICAgICAgICAgICAgICAgICAgICBsYW5lLmhpZ2ggPSBUTGFuZS5oaWdoIF4gKH5UeDFMYW5lLmhpZ2ggJiBUeDJMYW5lLmhpZ2gpO1xuXHQgICAgICAgICAgICAgICAgICAgICAgICBsYW5lLmxvdyAgPSBUTGFuZS5sb3cgIF4gKH5UeDFMYW5lLmxvdyAgJiBUeDJMYW5lLmxvdyk7XG5cdCAgICAgICAgICAgICAgICAgICAgfVxuXHQgICAgICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgICAgICAvLyBJb3RhXG5cdCAgICAgICAgICAgICAgICB2YXIgbGFuZSA9IHN0YXRlWzBdO1xuXHQgICAgICAgICAgICAgICAgdmFyIHJvdW5kQ29uc3RhbnQgPSBST1VORF9DT05TVEFOVFNbcm91bmRdO1xuXHQgICAgICAgICAgICAgICAgbGFuZS5oaWdoIF49IHJvdW5kQ29uc3RhbnQuaGlnaDtcblx0ICAgICAgICAgICAgICAgIGxhbmUubG93ICBePSByb3VuZENvbnN0YW50Lmxvdzs7XG5cdCAgICAgICAgICAgIH1cblx0ICAgICAgICB9LFxuXG5cdCAgICAgICAgX2RvRmluYWxpemU6IGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgLy8gU2hvcnRjdXRzXG5cdCAgICAgICAgICAgIHZhciBkYXRhID0gdGhpcy5fZGF0YTtcblx0ICAgICAgICAgICAgdmFyIGRhdGFXb3JkcyA9IGRhdGEud29yZHM7XG5cdCAgICAgICAgICAgIHZhciBuQml0c1RvdGFsID0gdGhpcy5fbkRhdGFCeXRlcyAqIDg7XG5cdCAgICAgICAgICAgIHZhciBuQml0c0xlZnQgPSBkYXRhLnNpZ0J5dGVzICogODtcblx0ICAgICAgICAgICAgdmFyIGJsb2NrU2l6ZUJpdHMgPSB0aGlzLmJsb2NrU2l6ZSAqIDMyO1xuXG5cdCAgICAgICAgICAgIC8vIEFkZCBwYWRkaW5nXG5cdCAgICAgICAgICAgIGRhdGFXb3Jkc1tuQml0c0xlZnQgPj4+IDVdIHw9IDB4MSA8PCAoMjQgLSBuQml0c0xlZnQgJSAzMik7XG5cdCAgICAgICAgICAgIGRhdGFXb3Jkc1soKE1hdGguY2VpbCgobkJpdHNMZWZ0ICsgMSkgLyBibG9ja1NpemVCaXRzKSAqIGJsb2NrU2l6ZUJpdHMpID4+PiA1KSAtIDFdIHw9IDB4ODA7XG5cdCAgICAgICAgICAgIGRhdGEuc2lnQnl0ZXMgPSBkYXRhV29yZHMubGVuZ3RoICogNDtcblxuXHQgICAgICAgICAgICAvLyBIYXNoIGZpbmFsIGJsb2Nrc1xuXHQgICAgICAgICAgICB0aGlzLl9wcm9jZXNzKCk7XG5cblx0ICAgICAgICAgICAgLy8gU2hvcnRjdXRzXG5cdCAgICAgICAgICAgIHZhciBzdGF0ZSA9IHRoaXMuX3N0YXRlO1xuXHQgICAgICAgICAgICB2YXIgb3V0cHV0TGVuZ3RoQnl0ZXMgPSB0aGlzLmNmZy5vdXRwdXRMZW5ndGggLyA4O1xuXHQgICAgICAgICAgICB2YXIgb3V0cHV0TGVuZ3RoTGFuZXMgPSBvdXRwdXRMZW5ndGhCeXRlcyAvIDg7XG5cblx0ICAgICAgICAgICAgLy8gU3F1ZWV6ZVxuXHQgICAgICAgICAgICB2YXIgaGFzaFdvcmRzID0gW107XG5cdCAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgb3V0cHV0TGVuZ3RoTGFuZXM7IGkrKykge1xuXHQgICAgICAgICAgICAgICAgLy8gU2hvcnRjdXRzXG5cdCAgICAgICAgICAgICAgICB2YXIgbGFuZSA9IHN0YXRlW2ldO1xuXHQgICAgICAgICAgICAgICAgdmFyIGxhbmVNc3cgPSBsYW5lLmhpZ2g7XG5cdCAgICAgICAgICAgICAgICB2YXIgbGFuZUxzdyA9IGxhbmUubG93O1xuXG5cdCAgICAgICAgICAgICAgICAvLyBTd2FwIGVuZGlhblxuXHQgICAgICAgICAgICAgICAgbGFuZU1zdyA9IChcblx0ICAgICAgICAgICAgICAgICAgICAoKChsYW5lTXN3IDw8IDgpICB8IChsYW5lTXN3ID4+PiAyNCkpICYgMHgwMGZmMDBmZikgfFxuXHQgICAgICAgICAgICAgICAgICAgICgoKGxhbmVNc3cgPDwgMjQpIHwgKGxhbmVNc3cgPj4+IDgpKSAgJiAweGZmMDBmZjAwKVxuXHQgICAgICAgICAgICAgICAgKTtcblx0ICAgICAgICAgICAgICAgIGxhbmVMc3cgPSAoXG5cdCAgICAgICAgICAgICAgICAgICAgKCgobGFuZUxzdyA8PCA4KSAgfCAobGFuZUxzdyA+Pj4gMjQpKSAmIDB4MDBmZjAwZmYpIHxcblx0ICAgICAgICAgICAgICAgICAgICAoKChsYW5lTHN3IDw8IDI0KSB8IChsYW5lTHN3ID4+PiA4KSkgICYgMHhmZjAwZmYwMClcblx0ICAgICAgICAgICAgICAgICk7XG5cblx0ICAgICAgICAgICAgICAgIC8vIFNxdWVlemUgc3RhdGUgdG8gcmV0cmlldmUgaGFzaFxuXHQgICAgICAgICAgICAgICAgaGFzaFdvcmRzLnB1c2gobGFuZUxzdyk7XG5cdCAgICAgICAgICAgICAgICBoYXNoV29yZHMucHVzaChsYW5lTXN3KTtcblx0ICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgIC8vIFJldHVybiBmaW5hbCBjb21wdXRlZCBoYXNoXG5cdCAgICAgICAgICAgIHJldHVybiBuZXcgV29yZEFycmF5LmluaXQoaGFzaFdvcmRzLCBvdXRwdXRMZW5ndGhCeXRlcyk7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgIHZhciBjbG9uZSA9IEhhc2hlci5jbG9uZS5jYWxsKHRoaXMpO1xuXG5cdCAgICAgICAgICAgIHZhciBzdGF0ZSA9IGNsb25lLl9zdGF0ZSA9IHRoaXMuX3N0YXRlLnNsaWNlKDApO1xuXHQgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IDI1OyBpKyspIHtcblx0ICAgICAgICAgICAgICAgIHN0YXRlW2ldID0gc3RhdGVbaV0uY2xvbmUoKTtcblx0ICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgIHJldHVybiBjbG9uZTtcblx0ICAgICAgICB9XG5cdCAgICB9KTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBTaG9ydGN1dCBmdW5jdGlvbiB0byB0aGUgaGFzaGVyJ3Mgb2JqZWN0IGludGVyZmFjZS5cblx0ICAgICAqXG5cdCAgICAgKiBAcGFyYW0ge1dvcmRBcnJheXxzdHJpbmd9IG1lc3NhZ2UgVGhlIG1lc3NhZ2UgdG8gaGFzaC5cblx0ICAgICAqXG5cdCAgICAgKiBAcmV0dXJuIHtXb3JkQXJyYXl9IFRoZSBoYXNoLlxuXHQgICAgICpcblx0ICAgICAqIEBzdGF0aWNcblx0ICAgICAqXG5cdCAgICAgKiBAZXhhbXBsZVxuXHQgICAgICpcblx0ICAgICAqICAgICB2YXIgaGFzaCA9IENyeXB0b0pTLlNIQTMoJ21lc3NhZ2UnKTtcblx0ICAgICAqICAgICB2YXIgaGFzaCA9IENyeXB0b0pTLlNIQTMod29yZEFycmF5KTtcblx0ICAgICAqL1xuXHQgICAgQy5TSEEzID0gSGFzaGVyLl9jcmVhdGVIZWxwZXIoU0hBMyk7XG5cblx0ICAgIC8qKlxuXHQgICAgICogU2hvcnRjdXQgZnVuY3Rpb24gdG8gdGhlIEhNQUMncyBvYmplY3QgaW50ZXJmYWNlLlxuXHQgICAgICpcblx0ICAgICAqIEBwYXJhbSB7V29yZEFycmF5fHN0cmluZ30gbWVzc2FnZSBUaGUgbWVzc2FnZSB0byBoYXNoLlxuXHQgICAgICogQHBhcmFtIHtXb3JkQXJyYXl8c3RyaW5nfSBrZXkgVGhlIHNlY3JldCBrZXkuXG5cdCAgICAgKlxuXHQgICAgICogQHJldHVybiB7V29yZEFycmF5fSBUaGUgSE1BQy5cblx0ICAgICAqXG5cdCAgICAgKiBAc3RhdGljXG5cdCAgICAgKlxuXHQgICAgICogQGV4YW1wbGVcblx0ICAgICAqXG5cdCAgICAgKiAgICAgdmFyIGhtYWMgPSBDcnlwdG9KUy5IbWFjU0hBMyhtZXNzYWdlLCBrZXkpO1xuXHQgICAgICovXG5cdCAgICBDLkhtYWNTSEEzID0gSGFzaGVyLl9jcmVhdGVIbWFjSGVscGVyKFNIQTMpO1xuXHR9KE1hdGgpKTtcblxuXG5cdHJldHVybiBDcnlwdG9KUy5TSEEzO1xuXG59KSk7IiwiOyhmdW5jdGlvbiAocm9vdCwgZmFjdG9yeSkge1xuXHRpZiAodHlwZW9mIGV4cG9ydHMgPT09IFwib2JqZWN0XCIpIHtcblx0XHQvLyBDb21tb25KU1xuXHRcdG1vZHVsZS5leHBvcnRzID0gZXhwb3J0cyA9IGZhY3RvcnkocmVxdWlyZShcIi4vY29yZVwiKSk7XG5cdH1cblx0ZWxzZSBpZiAodHlwZW9mIGRlZmluZSA9PT0gXCJmdW5jdGlvblwiICYmIGRlZmluZS5hbWQpIHtcblx0XHQvLyBBTURcblx0XHRkZWZpbmUoW1wiLi9jb3JlXCJdLCBmYWN0b3J5KTtcblx0fVxuXHRlbHNlIHtcblx0XHQvLyBHbG9iYWwgKGJyb3dzZXIpXG5cdFx0ZmFjdG9yeShyb290LkNyeXB0b0pTKTtcblx0fVxufSh0aGlzLCBmdW5jdGlvbiAoQ3J5cHRvSlMpIHtcblxuXHQoZnVuY3Rpb24gKHVuZGVmaW5lZCkge1xuXHQgICAgLy8gU2hvcnRjdXRzXG5cdCAgICB2YXIgQyA9IENyeXB0b0pTO1xuXHQgICAgdmFyIENfbGliID0gQy5saWI7XG5cdCAgICB2YXIgQmFzZSA9IENfbGliLkJhc2U7XG5cdCAgICB2YXIgWDMyV29yZEFycmF5ID0gQ19saWIuV29yZEFycmF5O1xuXG5cdCAgICAvKipcblx0ICAgICAqIHg2NCBuYW1lc3BhY2UuXG5cdCAgICAgKi9cblx0ICAgIHZhciBDX3g2NCA9IEMueDY0ID0ge307XG5cblx0ICAgIC8qKlxuXHQgICAgICogQSA2NC1iaXQgd29yZC5cblx0ICAgICAqL1xuXHQgICAgdmFyIFg2NFdvcmQgPSBDX3g2NC5Xb3JkID0gQmFzZS5leHRlbmQoe1xuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIEluaXRpYWxpemVzIGEgbmV3bHkgY3JlYXRlZCA2NC1iaXQgd29yZC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7bnVtYmVyfSBoaWdoIFRoZSBoaWdoIDMyIGJpdHMuXG5cdCAgICAgICAgICogQHBhcmFtIHtudW1iZXJ9IGxvdyBUaGUgbG93IDMyIGJpdHMuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciB4NjRXb3JkID0gQ3J5cHRvSlMueDY0LldvcmQuY3JlYXRlKDB4MDAwMTAyMDMsIDB4MDQwNTA2MDcpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIGluaXQ6IGZ1bmN0aW9uIChoaWdoLCBsb3cpIHtcblx0ICAgICAgICAgICAgdGhpcy5oaWdoID0gaGlnaDtcblx0ICAgICAgICAgICAgdGhpcy5sb3cgPSBsb3c7XG5cdCAgICAgICAgfVxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQml0d2lzZSBOT1RzIHRoaXMgd29yZC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1g2NFdvcmR9IEEgbmV3IHg2NC1Xb3JkIG9iamVjdCBhZnRlciBuZWdhdGluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIG5lZ2F0ZWQgPSB4NjRXb3JkLm5vdCgpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIC8vIG5vdDogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICAvLyB2YXIgaGlnaCA9IH50aGlzLmhpZ2g7XG5cdCAgICAgICAgICAgIC8vIHZhciBsb3cgPSB+dGhpcy5sb3c7XG5cblx0ICAgICAgICAgICAgLy8gcmV0dXJuIFg2NFdvcmQuY3JlYXRlKGhpZ2gsIGxvdyk7XG5cdCAgICAgICAgLy8gfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIEJpdHdpc2UgQU5EcyB0aGlzIHdvcmQgd2l0aCB0aGUgcGFzc2VkIHdvcmQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge1g2NFdvcmR9IHdvcmQgVGhlIHg2NC1Xb3JkIHRvIEFORCB3aXRoIHRoaXMgd29yZC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1g2NFdvcmR9IEEgbmV3IHg2NC1Xb3JkIG9iamVjdCBhZnRlciBBTkRpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciBhbmRlZCA9IHg2NFdvcmQuYW5kKGFub3RoZXJYNjRXb3JkKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICAvLyBhbmQ6IGZ1bmN0aW9uICh3b3JkKSB7XG5cdCAgICAgICAgICAgIC8vIHZhciBoaWdoID0gdGhpcy5oaWdoICYgd29yZC5oaWdoO1xuXHQgICAgICAgICAgICAvLyB2YXIgbG93ID0gdGhpcy5sb3cgJiB3b3JkLmxvdztcblxuXHQgICAgICAgICAgICAvLyByZXR1cm4gWDY0V29yZC5jcmVhdGUoaGlnaCwgbG93KTtcblx0ICAgICAgICAvLyB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQml0d2lzZSBPUnMgdGhpcyB3b3JkIHdpdGggdGhlIHBhc3NlZCB3b3JkLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtYNjRXb3JkfSB3b3JkIFRoZSB4NjQtV29yZCB0byBPUiB3aXRoIHRoaXMgd29yZC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1g2NFdvcmR9IEEgbmV3IHg2NC1Xb3JkIG9iamVjdCBhZnRlciBPUmluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIG9yZWQgPSB4NjRXb3JkLm9yKGFub3RoZXJYNjRXb3JkKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICAvLyBvcjogZnVuY3Rpb24gKHdvcmQpIHtcblx0ICAgICAgICAgICAgLy8gdmFyIGhpZ2ggPSB0aGlzLmhpZ2ggfCB3b3JkLmhpZ2g7XG5cdCAgICAgICAgICAgIC8vIHZhciBsb3cgPSB0aGlzLmxvdyB8IHdvcmQubG93O1xuXG5cdCAgICAgICAgICAgIC8vIHJldHVybiBYNjRXb3JkLmNyZWF0ZShoaWdoLCBsb3cpO1xuXHQgICAgICAgIC8vIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBCaXR3aXNlIFhPUnMgdGhpcyB3b3JkIHdpdGggdGhlIHBhc3NlZCB3b3JkLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtYNjRXb3JkfSB3b3JkIFRoZSB4NjQtV29yZCB0byBYT1Igd2l0aCB0aGlzIHdvcmQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtYNjRXb3JkfSBBIG5ldyB4NjQtV29yZCBvYmplY3QgYWZ0ZXIgWE9SaW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgeG9yZWQgPSB4NjRXb3JkLnhvcihhbm90aGVyWDY0V29yZCk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgLy8geG9yOiBmdW5jdGlvbiAod29yZCkge1xuXHQgICAgICAgICAgICAvLyB2YXIgaGlnaCA9IHRoaXMuaGlnaCBeIHdvcmQuaGlnaDtcblx0ICAgICAgICAgICAgLy8gdmFyIGxvdyA9IHRoaXMubG93IF4gd29yZC5sb3c7XG5cblx0ICAgICAgICAgICAgLy8gcmV0dXJuIFg2NFdvcmQuY3JlYXRlKGhpZ2gsIGxvdyk7XG5cdCAgICAgICAgLy8gfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIFNoaWZ0cyB0aGlzIHdvcmQgbiBiaXRzIHRvIHRoZSBsZWZ0LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtudW1iZXJ9IG4gVGhlIG51bWJlciBvZiBiaXRzIHRvIHNoaWZ0LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7WDY0V29yZH0gQSBuZXcgeDY0LVdvcmQgb2JqZWN0IGFmdGVyIHNoaWZ0aW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgc2hpZnRlZCA9IHg2NFdvcmQuc2hpZnRMKDI1KTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICAvLyBzaGlmdEw6IGZ1bmN0aW9uIChuKSB7XG5cdCAgICAgICAgICAgIC8vIGlmIChuIDwgMzIpIHtcblx0ICAgICAgICAgICAgICAgIC8vIHZhciBoaWdoID0gKHRoaXMuaGlnaCA8PCBuKSB8ICh0aGlzLmxvdyA+Pj4gKDMyIC0gbikpO1xuXHQgICAgICAgICAgICAgICAgLy8gdmFyIGxvdyA9IHRoaXMubG93IDw8IG47XG5cdCAgICAgICAgICAgIC8vIH0gZWxzZSB7XG5cdCAgICAgICAgICAgICAgICAvLyB2YXIgaGlnaCA9IHRoaXMubG93IDw8IChuIC0gMzIpO1xuXHQgICAgICAgICAgICAgICAgLy8gdmFyIGxvdyA9IDA7XG5cdCAgICAgICAgICAgIC8vIH1cblxuXHQgICAgICAgICAgICAvLyByZXR1cm4gWDY0V29yZC5jcmVhdGUoaGlnaCwgbG93KTtcblx0ICAgICAgICAvLyB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogU2hpZnRzIHRoaXMgd29yZCBuIGJpdHMgdG8gdGhlIHJpZ2h0LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtudW1iZXJ9IG4gVGhlIG51bWJlciBvZiBiaXRzIHRvIHNoaWZ0LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7WDY0V29yZH0gQSBuZXcgeDY0LVdvcmQgb2JqZWN0IGFmdGVyIHNoaWZ0aW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgc2hpZnRlZCA9IHg2NFdvcmQuc2hpZnRSKDcpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIC8vIHNoaWZ0UjogZnVuY3Rpb24gKG4pIHtcblx0ICAgICAgICAgICAgLy8gaWYgKG4gPCAzMikge1xuXHQgICAgICAgICAgICAgICAgLy8gdmFyIGxvdyA9ICh0aGlzLmxvdyA+Pj4gbikgfCAodGhpcy5oaWdoIDw8ICgzMiAtIG4pKTtcblx0ICAgICAgICAgICAgICAgIC8vIHZhciBoaWdoID0gdGhpcy5oaWdoID4+PiBuO1xuXHQgICAgICAgICAgICAvLyB9IGVsc2Uge1xuXHQgICAgICAgICAgICAgICAgLy8gdmFyIGxvdyA9IHRoaXMuaGlnaCA+Pj4gKG4gLSAzMik7XG5cdCAgICAgICAgICAgICAgICAvLyB2YXIgaGlnaCA9IDA7XG5cdCAgICAgICAgICAgIC8vIH1cblxuXHQgICAgICAgICAgICAvLyByZXR1cm4gWDY0V29yZC5jcmVhdGUoaGlnaCwgbG93KTtcblx0ICAgICAgICAvLyB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogUm90YXRlcyB0aGlzIHdvcmQgbiBiaXRzIHRvIHRoZSBsZWZ0LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtudW1iZXJ9IG4gVGhlIG51bWJlciBvZiBiaXRzIHRvIHJvdGF0ZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1g2NFdvcmR9IEEgbmV3IHg2NC1Xb3JkIG9iamVjdCBhZnRlciByb3RhdGluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHJvdGF0ZWQgPSB4NjRXb3JkLnJvdEwoMjUpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIC8vIHJvdEw6IGZ1bmN0aW9uIChuKSB7XG5cdCAgICAgICAgICAgIC8vIHJldHVybiB0aGlzLnNoaWZ0TChuKS5vcih0aGlzLnNoaWZ0Uig2NCAtIG4pKTtcblx0ICAgICAgICAvLyB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogUm90YXRlcyB0aGlzIHdvcmQgbiBiaXRzIHRvIHRoZSByaWdodC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7bnVtYmVyfSBuIFRoZSBudW1iZXIgb2YgYml0cyB0byByb3RhdGUuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtYNjRXb3JkfSBBIG5ldyB4NjQtV29yZCBvYmplY3QgYWZ0ZXIgcm90YXRpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciByb3RhdGVkID0geDY0V29yZC5yb3RSKDcpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIC8vIHJvdFI6IGZ1bmN0aW9uIChuKSB7XG5cdCAgICAgICAgICAgIC8vIHJldHVybiB0aGlzLnNoaWZ0UihuKS5vcih0aGlzLnNoaWZ0TCg2NCAtIG4pKTtcblx0ICAgICAgICAvLyB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQWRkcyB0aGlzIHdvcmQgd2l0aCB0aGUgcGFzc2VkIHdvcmQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge1g2NFdvcmR9IHdvcmQgVGhlIHg2NC1Xb3JkIHRvIGFkZCB3aXRoIHRoaXMgd29yZC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1g2NFdvcmR9IEEgbmV3IHg2NC1Xb3JkIG9iamVjdCBhZnRlciBhZGRpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciBhZGRlZCA9IHg2NFdvcmQuYWRkKGFub3RoZXJYNjRXb3JkKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICAvLyBhZGQ6IGZ1bmN0aW9uICh3b3JkKSB7XG5cdCAgICAgICAgICAgIC8vIHZhciBsb3cgPSAodGhpcy5sb3cgKyB3b3JkLmxvdykgfCAwO1xuXHQgICAgICAgICAgICAvLyB2YXIgY2FycnkgPSAobG93ID4+PiAwKSA8ICh0aGlzLmxvdyA+Pj4gMCkgPyAxIDogMDtcblx0ICAgICAgICAgICAgLy8gdmFyIGhpZ2ggPSAodGhpcy5oaWdoICsgd29yZC5oaWdoICsgY2FycnkpIHwgMDtcblxuXHQgICAgICAgICAgICAvLyByZXR1cm4gWDY0V29yZC5jcmVhdGUoaGlnaCwgbG93KTtcblx0ICAgICAgICAvLyB9XG5cdCAgICB9KTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBBbiBhcnJheSBvZiA2NC1iaXQgd29yZHMuXG5cdCAgICAgKlxuXHQgICAgICogQHByb3BlcnR5IHtBcnJheX0gd29yZHMgVGhlIGFycmF5IG9mIENyeXB0b0pTLng2NC5Xb3JkIG9iamVjdHMuXG5cdCAgICAgKiBAcHJvcGVydHkge251bWJlcn0gc2lnQnl0ZXMgVGhlIG51bWJlciBvZiBzaWduaWZpY2FudCBieXRlcyBpbiB0aGlzIHdvcmQgYXJyYXkuXG5cdCAgICAgKi9cblx0ICAgIHZhciBYNjRXb3JkQXJyYXkgPSBDX3g2NC5Xb3JkQXJyYXkgPSBCYXNlLmV4dGVuZCh7XG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogSW5pdGlhbGl6ZXMgYSBuZXdseSBjcmVhdGVkIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge0FycmF5fSB3b3JkcyAoT3B0aW9uYWwpIEFuIGFycmF5IG9mIENyeXB0b0pTLng2NC5Xb3JkIG9iamVjdHMuXG5cdCAgICAgICAgICogQHBhcmFtIHtudW1iZXJ9IHNpZ0J5dGVzIChPcHRpb25hbCkgVGhlIG51bWJlciBvZiBzaWduaWZpY2FudCBieXRlcyBpbiB0aGUgd29yZHMuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciB3b3JkQXJyYXkgPSBDcnlwdG9KUy54NjQuV29yZEFycmF5LmNyZWF0ZSgpO1xuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciB3b3JkQXJyYXkgPSBDcnlwdG9KUy54NjQuV29yZEFycmF5LmNyZWF0ZShbXG5cdCAgICAgICAgICogICAgICAgICBDcnlwdG9KUy54NjQuV29yZC5jcmVhdGUoMHgwMDAxMDIwMywgMHgwNDA1MDYwNyksXG5cdCAgICAgICAgICogICAgICAgICBDcnlwdG9KUy54NjQuV29yZC5jcmVhdGUoMHgxODE5MWExYiwgMHgxYzFkMWUxZilcblx0ICAgICAgICAgKiAgICAgXSk7XG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHdvcmRBcnJheSA9IENyeXB0b0pTLng2NC5Xb3JkQXJyYXkuY3JlYXRlKFtcblx0ICAgICAgICAgKiAgICAgICAgIENyeXB0b0pTLng2NC5Xb3JkLmNyZWF0ZSgweDAwMDEwMjAzLCAweDA0MDUwNjA3KSxcblx0ICAgICAgICAgKiAgICAgICAgIENyeXB0b0pTLng2NC5Xb3JkLmNyZWF0ZSgweDE4MTkxYTFiLCAweDFjMWQxZTFmKVxuXHQgICAgICAgICAqICAgICBdLCAxMCk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgaW5pdDogZnVuY3Rpb24gKHdvcmRzLCBzaWdCeXRlcykge1xuXHQgICAgICAgICAgICB3b3JkcyA9IHRoaXMud29yZHMgPSB3b3JkcyB8fCBbXTtcblxuXHQgICAgICAgICAgICBpZiAoc2lnQnl0ZXMgIT0gdW5kZWZpbmVkKSB7XG5cdCAgICAgICAgICAgICAgICB0aGlzLnNpZ0J5dGVzID0gc2lnQnl0ZXM7XG5cdCAgICAgICAgICAgIH0gZWxzZSB7XG5cdCAgICAgICAgICAgICAgICB0aGlzLnNpZ0J5dGVzID0gd29yZHMubGVuZ3RoICogODtcblx0ICAgICAgICAgICAgfVxuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDb252ZXJ0cyB0aGlzIDY0LWJpdCB3b3JkIGFycmF5IHRvIGEgMzItYml0IHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtDcnlwdG9KUy5saWIuV29yZEFycmF5fSBUaGlzIHdvcmQgYXJyYXkncyBkYXRhIGFzIGEgMzItYml0IHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciB4MzJXb3JkQXJyYXkgPSB4NjRXb3JkQXJyYXkudG9YMzIoKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICB0b1gzMjogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICAvLyBTaG9ydGN1dHNcblx0ICAgICAgICAgICAgdmFyIHg2NFdvcmRzID0gdGhpcy53b3Jkcztcblx0ICAgICAgICAgICAgdmFyIHg2NFdvcmRzTGVuZ3RoID0geDY0V29yZHMubGVuZ3RoO1xuXG5cdCAgICAgICAgICAgIC8vIENvbnZlcnRcblx0ICAgICAgICAgICAgdmFyIHgzMldvcmRzID0gW107XG5cdCAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgeDY0V29yZHNMZW5ndGg7IGkrKykge1xuXHQgICAgICAgICAgICAgICAgdmFyIHg2NFdvcmQgPSB4NjRXb3Jkc1tpXTtcblx0ICAgICAgICAgICAgICAgIHgzMldvcmRzLnB1c2goeDY0V29yZC5oaWdoKTtcblx0ICAgICAgICAgICAgICAgIHgzMldvcmRzLnB1c2goeDY0V29yZC5sb3cpO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgcmV0dXJuIFgzMldvcmRBcnJheS5jcmVhdGUoeDMyV29yZHMsIHRoaXMuc2lnQnl0ZXMpO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDcmVhdGVzIGEgY29weSBvZiB0aGlzIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtYNjRXb3JkQXJyYXl9IFRoZSBjbG9uZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIGNsb25lID0geDY0V29yZEFycmF5LmNsb25lKCk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgY2xvbmU6IGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgdmFyIGNsb25lID0gQmFzZS5jbG9uZS5jYWxsKHRoaXMpO1xuXG5cdCAgICAgICAgICAgIC8vIENsb25lIFwid29yZHNcIiBhcnJheVxuXHQgICAgICAgICAgICB2YXIgd29yZHMgPSBjbG9uZS53b3JkcyA9IHRoaXMud29yZHMuc2xpY2UoMCk7XG5cblx0ICAgICAgICAgICAgLy8gQ2xvbmUgZWFjaCBYNjRXb3JkIG9iamVjdFxuXHQgICAgICAgICAgICB2YXIgd29yZHNMZW5ndGggPSB3b3Jkcy5sZW5ndGg7XG5cdCAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgd29yZHNMZW5ndGg7IGkrKykge1xuXHQgICAgICAgICAgICAgICAgd29yZHNbaV0gPSB3b3Jkc1tpXS5jbG9uZSgpO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgcmV0dXJuIGNsb25lO1xuXHQgICAgICAgIH1cblx0ICAgIH0pO1xuXHR9KCkpO1xuXG5cblx0cmV0dXJuIENyeXB0b0pTO1xuXG59KSk7IiwiLyohIGJpZ251bWJlci5qcyB2Mi4wLjcgaHR0cHM6Ly9naXRodWIuY29tL01pa2VNY2wvYmlnbnVtYmVyLmpzL0xJQ0VOQ0UgKi9cblxuOyhmdW5jdGlvbiAoZ2xvYmFsKSB7XG4gICAgJ3VzZSBzdHJpY3QnO1xuXG4gICAgLypcbiAgICAgIGJpZ251bWJlci5qcyB2Mi4wLjdcbiAgICAgIEEgSmF2YVNjcmlwdCBsaWJyYXJ5IGZvciBhcmJpdHJhcnktcHJlY2lzaW9uIGFyaXRobWV0aWMuXG4gICAgICBodHRwczovL2dpdGh1Yi5jb20vTWlrZU1jbC9iaWdudW1iZXIuanNcbiAgICAgIENvcHlyaWdodCAoYykgMjAxNSBNaWNoYWVsIE1jbGF1Z2hsaW4gPE04Y2g4OGxAZ21haWwuY29tPlxuICAgICAgTUlUIEV4cGF0IExpY2VuY2VcbiAgICAqL1xuXG5cbiAgICB2YXIgQmlnTnVtYmVyLCBjcnlwdG8sIHBhcnNlTnVtZXJpYyxcbiAgICAgICAgaXNOdW1lcmljID0gL14tPyhcXGQrKFxcLlxcZCopP3xcXC5cXGQrKShlWystXT9cXGQrKT8kL2ksXG4gICAgICAgIG1hdGhjZWlsID0gTWF0aC5jZWlsLFxuICAgICAgICBtYXRoZmxvb3IgPSBNYXRoLmZsb29yLFxuICAgICAgICBub3RCb29sID0gJyBub3QgYSBib29sZWFuIG9yIGJpbmFyeSBkaWdpdCcsXG4gICAgICAgIHJvdW5kaW5nTW9kZSA9ICdyb3VuZGluZyBtb2RlJyxcbiAgICAgICAgdG9vTWFueURpZ2l0cyA9ICdudW1iZXIgdHlwZSBoYXMgbW9yZSB0aGFuIDE1IHNpZ25pZmljYW50IGRpZ2l0cycsXG4gICAgICAgIEFMUEhBQkVUID0gJzAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ekFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaJF8nLFxuICAgICAgICBCQVNFID0gMWUxNCxcbiAgICAgICAgTE9HX0JBU0UgPSAxNCxcbiAgICAgICAgTUFYX1NBRkVfSU5URUdFUiA9IDB4MWZmZmZmZmZmZmZmZmYsICAgICAgICAgLy8gMl41MyAtIDFcbiAgICAgICAgLy8gTUFYX0lOVDMyID0gMHg3ZmZmZmZmZiwgICAgICAgICAgICAgICAgICAgLy8gMl4zMSAtIDFcbiAgICAgICAgUE9XU19URU4gPSBbMSwgMTAsIDEwMCwgMWUzLCAxZTQsIDFlNSwgMWU2LCAxZTcsIDFlOCwgMWU5LCAxZTEwLCAxZTExLCAxZTEyLCAxZTEzXSxcbiAgICAgICAgU1FSVF9CQVNFID0gMWU3LFxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFRoZSBsaW1pdCBvbiB0aGUgdmFsdWUgb2YgREVDSU1BTF9QTEFDRVMsIFRPX0VYUF9ORUcsIFRPX0VYUF9QT1MsIE1JTl9FWFAsIE1BWF9FWFAsIGFuZFxuICAgICAgICAgKiB0aGUgYXJndW1lbnRzIHRvIHRvRXhwb25lbnRpYWwsIHRvRml4ZWQsIHRvRm9ybWF0LCBhbmQgdG9QcmVjaXNpb24sIGJleW9uZCB3aGljaCBhblxuICAgICAgICAgKiBleGNlcHRpb24gaXMgdGhyb3duIChpZiBFUlJPUlMgaXMgdHJ1ZSkuXG4gICAgICAgICAqL1xuICAgICAgICBNQVggPSAxRTk7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyAwIHRvIE1BWF9JTlQzMlxuXG5cbiAgICAvKlxuICAgICAqIENyZWF0ZSBhbmQgcmV0dXJuIGEgQmlnTnVtYmVyIGNvbnN0cnVjdG9yLlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIGFub3RoZXIoY29uZmlnT2JqKSB7XG4gICAgICAgIHZhciBkaXYsXG5cbiAgICAgICAgICAgIC8vIGlkIHRyYWNrcyB0aGUgY2FsbGVyIGZ1bmN0aW9uLCBzbyBpdHMgbmFtZSBjYW4gYmUgaW5jbHVkZWQgaW4gZXJyb3IgbWVzc2FnZXMuXG4gICAgICAgICAgICBpZCA9IDAsXG4gICAgICAgICAgICBQID0gQmlnTnVtYmVyLnByb3RvdHlwZSxcbiAgICAgICAgICAgIE9ORSA9IG5ldyBCaWdOdW1iZXIoMSksXG5cblxuICAgICAgICAgICAgLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiBFRElUQUJMRSBERUZBVUxUUyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5cbiAgICAgICAgICAgIC8qXG4gICAgICAgICAgICAgKiBUaGUgZGVmYXVsdCB2YWx1ZXMgYmVsb3cgbXVzdCBiZSBpbnRlZ2VycyB3aXRoaW4gdGhlIGluY2x1c2l2ZSByYW5nZXMgc3RhdGVkLlxuICAgICAgICAgICAgICogVGhlIHZhbHVlcyBjYW4gYWxzbyBiZSBjaGFuZ2VkIGF0IHJ1bi10aW1lIHVzaW5nIEJpZ051bWJlci5jb25maWcuXG4gICAgICAgICAgICAgKi9cblxuICAgICAgICAgICAgLy8gVGhlIG1heGltdW0gbnVtYmVyIG9mIGRlY2ltYWwgcGxhY2VzIGZvciBvcGVyYXRpb25zIGludm9sdmluZyBkaXZpc2lvbi5cbiAgICAgICAgICAgIERFQ0lNQUxfUExBQ0VTID0gMjAsICAgICAgICAgICAgICAgICAgICAgLy8gMCB0byBNQVhcblxuICAgICAgICAgICAgLypcbiAgICAgICAgICAgICAqIFRoZSByb3VuZGluZyBtb2RlIHVzZWQgd2hlbiByb3VuZGluZyB0byB0aGUgYWJvdmUgZGVjaW1hbCBwbGFjZXMsIGFuZCB3aGVuIHVzaW5nXG4gICAgICAgICAgICAgKiB0b0V4cG9uZW50aWFsLCB0b0ZpeGVkLCB0b0Zvcm1hdCBhbmQgdG9QcmVjaXNpb24sIGFuZCByb3VuZCAoZGVmYXVsdCB2YWx1ZSkuXG4gICAgICAgICAgICAgKiBVUCAgICAgICAgIDAgQXdheSBmcm9tIHplcm8uXG4gICAgICAgICAgICAgKiBET1dOICAgICAgIDEgVG93YXJkcyB6ZXJvLlxuICAgICAgICAgICAgICogQ0VJTCAgICAgICAyIFRvd2FyZHMgK0luZmluaXR5LlxuICAgICAgICAgICAgICogRkxPT1IgICAgICAzIFRvd2FyZHMgLUluZmluaXR5LlxuICAgICAgICAgICAgICogSEFMRl9VUCAgICA0IFRvd2FyZHMgbmVhcmVzdCBuZWlnaGJvdXIuIElmIGVxdWlkaXN0YW50LCB1cC5cbiAgICAgICAgICAgICAqIEhBTEZfRE9XTiAgNSBUb3dhcmRzIG5lYXJlc3QgbmVpZ2hib3VyLiBJZiBlcXVpZGlzdGFudCwgZG93bi5cbiAgICAgICAgICAgICAqIEhBTEZfRVZFTiAgNiBUb3dhcmRzIG5lYXJlc3QgbmVpZ2hib3VyLiBJZiBlcXVpZGlzdGFudCwgdG93YXJkcyBldmVuIG5laWdoYm91ci5cbiAgICAgICAgICAgICAqIEhBTEZfQ0VJTCAgNyBUb3dhcmRzIG5lYXJlc3QgbmVpZ2hib3VyLiBJZiBlcXVpZGlzdGFudCwgdG93YXJkcyArSW5maW5pdHkuXG4gICAgICAgICAgICAgKiBIQUxGX0ZMT09SIDggVG93YXJkcyBuZWFyZXN0IG5laWdoYm91ci4gSWYgZXF1aWRpc3RhbnQsIHRvd2FyZHMgLUluZmluaXR5LlxuICAgICAgICAgICAgICovXG4gICAgICAgICAgICBST1VORElOR19NT0RFID0gNCwgICAgICAgICAgICAgICAgICAgICAgIC8vIDAgdG8gOFxuXG4gICAgICAgICAgICAvLyBFWFBPTkVOVElBTF9BVCA6IFtUT19FWFBfTkVHICwgVE9fRVhQX1BPU11cblxuICAgICAgICAgICAgLy8gVGhlIGV4cG9uZW50IHZhbHVlIGF0IGFuZCBiZW5lYXRoIHdoaWNoIHRvU3RyaW5nIHJldHVybnMgZXhwb25lbnRpYWwgbm90YXRpb24uXG4gICAgICAgICAgICAvLyBOdW1iZXIgdHlwZTogLTdcbiAgICAgICAgICAgIFRPX0VYUF9ORUcgPSAtNywgICAgICAgICAgICAgICAgICAgICAgICAgLy8gMCB0byAtTUFYXG5cbiAgICAgICAgICAgIC8vIFRoZSBleHBvbmVudCB2YWx1ZSBhdCBhbmQgYWJvdmUgd2hpY2ggdG9TdHJpbmcgcmV0dXJucyBleHBvbmVudGlhbCBub3RhdGlvbi5cbiAgICAgICAgICAgIC8vIE51bWJlciB0eXBlOiAyMVxuICAgICAgICAgICAgVE9fRVhQX1BPUyA9IDIxLCAgICAgICAgICAgICAgICAgICAgICAgICAvLyAwIHRvIE1BWFxuXG4gICAgICAgICAgICAvLyBSQU5HRSA6IFtNSU5fRVhQLCBNQVhfRVhQXVxuXG4gICAgICAgICAgICAvLyBUaGUgbWluaW11bSBleHBvbmVudCB2YWx1ZSwgYmVuZWF0aCB3aGljaCB1bmRlcmZsb3cgdG8gemVybyBvY2N1cnMuXG4gICAgICAgICAgICAvLyBOdW1iZXIgdHlwZTogLTMyNCAgKDVlLTMyNClcbiAgICAgICAgICAgIE1JTl9FWFAgPSAtMWU3LCAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gLTEgdG8gLU1BWFxuXG4gICAgICAgICAgICAvLyBUaGUgbWF4aW11bSBleHBvbmVudCB2YWx1ZSwgYWJvdmUgd2hpY2ggb3ZlcmZsb3cgdG8gSW5maW5pdHkgb2NjdXJzLlxuICAgICAgICAgICAgLy8gTnVtYmVyIHR5cGU6ICAzMDggICgxLjc5NzY5MzEzNDg2MjMxNTdlKzMwOClcbiAgICAgICAgICAgIC8vIEZvciBNQVhfRVhQID4gMWU3LCBlLmcuIG5ldyBCaWdOdW1iZXIoJzFlMTAwMDAwMDAwJykucGx1cygxKSBtYXkgYmUgc2xvdy5cbiAgICAgICAgICAgIE1BWF9FWFAgPSAxZTcsICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gMSB0byBNQVhcblxuICAgICAgICAgICAgLy8gV2hldGhlciBCaWdOdW1iZXIgRXJyb3JzIGFyZSBldmVyIHRocm93bi5cbiAgICAgICAgICAgIEVSUk9SUyA9IHRydWUsICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdHJ1ZSBvciBmYWxzZVxuXG4gICAgICAgICAgICAvLyBDaGFuZ2UgdG8gaW50VmFsaWRhdG9yTm9FcnJvcnMgaWYgRVJST1JTIGlzIGZhbHNlLlxuICAgICAgICAgICAgaXNWYWxpZEludCA9IGludFZhbGlkYXRvcldpdGhFcnJvcnMsICAgICAvLyBpbnRWYWxpZGF0b3JXaXRoRXJyb3JzL2ludFZhbGlkYXRvck5vRXJyb3JzXG5cbiAgICAgICAgICAgIC8vIFdoZXRoZXIgdG8gdXNlIGNyeXB0b2dyYXBoaWNhbGx5LXNlY3VyZSByYW5kb20gbnVtYmVyIGdlbmVyYXRpb24sIGlmIGF2YWlsYWJsZS5cbiAgICAgICAgICAgIENSWVBUTyA9IGZhbHNlLCAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdHJ1ZSBvciBmYWxzZVxuXG4gICAgICAgICAgICAvKlxuICAgICAgICAgICAgICogVGhlIG1vZHVsbyBtb2RlIHVzZWQgd2hlbiBjYWxjdWxhdGluZyB0aGUgbW9kdWx1czogYSBtb2Qgbi5cbiAgICAgICAgICAgICAqIFRoZSBxdW90aWVudCAocSA9IGEgLyBuKSBpcyBjYWxjdWxhdGVkIGFjY29yZGluZyB0byB0aGUgY29ycmVzcG9uZGluZyByb3VuZGluZyBtb2RlLlxuICAgICAgICAgICAgICogVGhlIHJlbWFpbmRlciAocikgaXMgY2FsY3VsYXRlZCBhczogciA9IGEgLSBuICogcS5cbiAgICAgICAgICAgICAqXG4gICAgICAgICAgICAgKiBVUCAgICAgICAgMCBUaGUgcmVtYWluZGVyIGlzIHBvc2l0aXZlIGlmIHRoZSBkaXZpZGVuZCBpcyBuZWdhdGl2ZSwgZWxzZSBpcyBuZWdhdGl2ZS5cbiAgICAgICAgICAgICAqIERPV04gICAgICAxIFRoZSByZW1haW5kZXIgaGFzIHRoZSBzYW1lIHNpZ24gYXMgdGhlIGRpdmlkZW5kLlxuICAgICAgICAgICAgICogICAgICAgICAgICAgVGhpcyBtb2R1bG8gbW9kZSBpcyBjb21tb25seSBrbm93biBhcyAndHJ1bmNhdGVkIGRpdmlzaW9uJyBhbmQgaXNcbiAgICAgICAgICAgICAqICAgICAgICAgICAgIGVxdWl2YWxlbnQgdG8gKGEgJSBuKSBpbiBKYXZhU2NyaXB0LlxuICAgICAgICAgICAgICogRkxPT1IgICAgIDMgVGhlIHJlbWFpbmRlciBoYXMgdGhlIHNhbWUgc2lnbiBhcyB0aGUgZGl2aXNvciAoUHl0aG9uICUpLlxuICAgICAgICAgICAgICogSEFMRl9FVkVOIDYgVGhpcyBtb2R1bG8gbW9kZSBpbXBsZW1lbnRzIHRoZSBJRUVFIDc1NCByZW1haW5kZXIgZnVuY3Rpb24uXG4gICAgICAgICAgICAgKiBFVUNMSUQgICAgOSBFdWNsaWRpYW4gZGl2aXNpb24uIHEgPSBzaWduKG4pICogZmxvb3IoYSAvIGFicyhuKSkuXG4gICAgICAgICAgICAgKiAgICAgICAgICAgICBUaGUgcmVtYWluZGVyIGlzIGFsd2F5cyBwb3NpdGl2ZS5cbiAgICAgICAgICAgICAqXG4gICAgICAgICAgICAgKiBUaGUgdHJ1bmNhdGVkIGRpdmlzaW9uLCBmbG9vcmVkIGRpdmlzaW9uLCBFdWNsaWRpYW4gZGl2aXNpb24gYW5kIElFRUUgNzU0IHJlbWFpbmRlclxuICAgICAgICAgICAgICogbW9kZXMgYXJlIGNvbW1vbmx5IHVzZWQgZm9yIHRoZSBtb2R1bHVzIG9wZXJhdGlvbi5cbiAgICAgICAgICAgICAqIEFsdGhvdWdoIHRoZSBvdGhlciByb3VuZGluZyBtb2RlcyBjYW4gYWxzbyBiZSB1c2VkLCB0aGV5IG1heSBub3QgZ2l2ZSB1c2VmdWwgcmVzdWx0cy5cbiAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgTU9EVUxPX01PREUgPSAxLCAgICAgICAgICAgICAgICAgICAgICAgICAvLyAwIHRvIDlcblxuICAgICAgICAgICAgLy8gVGhlIG1heGltdW0gbnVtYmVyIG9mIHNpZ25pZmljYW50IGRpZ2l0cyBvZiB0aGUgcmVzdWx0IG9mIHRoZSB0b1Bvd2VyIG9wZXJhdGlvbi5cbiAgICAgICAgICAgIC8vIElmIFBPV19QUkVDSVNJT04gaXMgMCwgdGhlcmUgd2lsbCBiZSB1bmxpbWl0ZWQgc2lnbmlmaWNhbnQgZGlnaXRzLlxuICAgICAgICAgICAgUE9XX1BSRUNJU0lPTiA9IDEwMCwgICAgICAgICAgICAgICAgICAgICAvLyAwIHRvIE1BWFxuXG4gICAgICAgICAgICAvLyBUaGUgZm9ybWF0IHNwZWNpZmljYXRpb24gdXNlZCBieSB0aGUgQmlnTnVtYmVyLnByb3RvdHlwZS50b0Zvcm1hdCBtZXRob2QuXG4gICAgICAgICAgICBGT1JNQVQgPSB7XG4gICAgICAgICAgICAgICAgZGVjaW1hbFNlcGFyYXRvcjogJy4nLFxuICAgICAgICAgICAgICAgIGdyb3VwU2VwYXJhdG9yOiAnLCcsXG4gICAgICAgICAgICAgICAgZ3JvdXBTaXplOiAzLFxuICAgICAgICAgICAgICAgIHNlY29uZGFyeUdyb3VwU2l6ZTogMCxcbiAgICAgICAgICAgICAgICBmcmFjdGlvbkdyb3VwU2VwYXJhdG9yOiAnXFx4QTAnLCAgICAgIC8vIG5vbi1icmVha2luZyBzcGFjZVxuICAgICAgICAgICAgICAgIGZyYWN0aW9uR3JvdXBTaXplOiAwXG4gICAgICAgICAgICB9O1xuXG5cbiAgICAgICAgLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cblxuXG4gICAgICAgIC8vIENPTlNUUlVDVE9SXG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBUaGUgQmlnTnVtYmVyIGNvbnN0cnVjdG9yIGFuZCBleHBvcnRlZCBmdW5jdGlvbi5cbiAgICAgICAgICogQ3JlYXRlIGFuZCByZXR1cm4gYSBuZXcgaW5zdGFuY2Ugb2YgYSBCaWdOdW1iZXIgb2JqZWN0LlxuICAgICAgICAgKlxuICAgICAgICAgKiBuIHtudW1iZXJ8c3RyaW5nfEJpZ051bWJlcn0gQSBudW1lcmljIHZhbHVlLlxuICAgICAgICAgKiBbYl0ge251bWJlcn0gVGhlIGJhc2Ugb2Ygbi4gSW50ZWdlciwgMiB0byA2NCBpbmNsdXNpdmUuXG4gICAgICAgICAqL1xuICAgICAgICBmdW5jdGlvbiBCaWdOdW1iZXIoIG4sIGIgKSB7XG4gICAgICAgICAgICB2YXIgYywgZSwgaSwgbnVtLCBsZW4sIHN0cixcbiAgICAgICAgICAgICAgICB4ID0gdGhpcztcblxuICAgICAgICAgICAgLy8gRW5hYmxlIGNvbnN0cnVjdG9yIHVzYWdlIHdpdGhvdXQgbmV3LlxuICAgICAgICAgICAgaWYgKCAhKCB4IGluc3RhbmNlb2YgQmlnTnVtYmVyICkgKSB7XG5cbiAgICAgICAgICAgICAgICAvLyAnQmlnTnVtYmVyKCkgY29uc3RydWN0b3IgY2FsbCB3aXRob3V0IG5ldzoge259J1xuICAgICAgICAgICAgICAgIGlmIChFUlJPUlMpIHJhaXNlKCAyNiwgJ2NvbnN0cnVjdG9yIGNhbGwgd2l0aG91dCBuZXcnLCBuICk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBCaWdOdW1iZXIoIG4sIGIgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gJ25ldyBCaWdOdW1iZXIoKSBiYXNlIG5vdCBhbiBpbnRlZ2VyOiB7Yn0nXG4gICAgICAgICAgICAvLyAnbmV3IEJpZ051bWJlcigpIGJhc2Ugb3V0IG9mIHJhbmdlOiB7Yn0nXG4gICAgICAgICAgICBpZiAoIGIgPT0gbnVsbCB8fCAhaXNWYWxpZEludCggYiwgMiwgNjQsIGlkLCAnYmFzZScgKSApIHtcblxuICAgICAgICAgICAgICAgIC8vIER1cGxpY2F0ZS5cbiAgICAgICAgICAgICAgICBpZiAoIG4gaW5zdGFuY2VvZiBCaWdOdW1iZXIgKSB7XG4gICAgICAgICAgICAgICAgICAgIHgucyA9IG4ucztcbiAgICAgICAgICAgICAgICAgICAgeC5lID0gbi5lO1xuICAgICAgICAgICAgICAgICAgICB4LmMgPSAoIG4gPSBuLmMgKSA/IG4uc2xpY2UoKSA6IG47XG4gICAgICAgICAgICAgICAgICAgIGlkID0gMDtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmICggKCBudW0gPSB0eXBlb2YgbiA9PSAnbnVtYmVyJyApICYmIG4gKiAwID09IDAgKSB7XG4gICAgICAgICAgICAgICAgICAgIHgucyA9IDEgLyBuIDwgMCA/ICggbiA9IC1uLCAtMSApIDogMTtcblxuICAgICAgICAgICAgICAgICAgICAvLyBGYXN0IHBhdGggZm9yIGludGVnZXJzLlxuICAgICAgICAgICAgICAgICAgICBpZiAoIG4gPT09IH5+biApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoIGUgPSAwLCBpID0gbjsgaSA+PSAxMDsgaSAvPSAxMCwgZSsrICk7XG4gICAgICAgICAgICAgICAgICAgICAgICB4LmUgPSBlO1xuICAgICAgICAgICAgICAgICAgICAgICAgeC5jID0gW25dO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWQgPSAwO1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgc3RyID0gbiArICcnO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGlmICggIWlzTnVtZXJpYy50ZXN0KCBzdHIgPSBuICsgJycgKSApIHJldHVybiBwYXJzZU51bWVyaWMoIHgsIHN0ciwgbnVtICk7XG4gICAgICAgICAgICAgICAgICAgIHgucyA9IHN0ci5jaGFyQ29kZUF0KDApID09PSA0NSA/ICggc3RyID0gc3RyLnNsaWNlKDEpLCAtMSApIDogMTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGIgPSBiIHwgMDtcbiAgICAgICAgICAgICAgICBzdHIgPSBuICsgJyc7XG5cbiAgICAgICAgICAgICAgICAvLyBFbnN1cmUgcmV0dXJuIHZhbHVlIGlzIHJvdW5kZWQgdG8gREVDSU1BTF9QTEFDRVMgYXMgd2l0aCBvdGhlciBiYXNlcy5cbiAgICAgICAgICAgICAgICAvLyBBbGxvdyBleHBvbmVudGlhbCBub3RhdGlvbiB0byBiZSB1c2VkIHdpdGggYmFzZSAxMCBhcmd1bWVudC5cbiAgICAgICAgICAgICAgICBpZiAoIGIgPT0gMTAgKSB7XG4gICAgICAgICAgICAgICAgICAgIHggPSBuZXcgQmlnTnVtYmVyKCBuIGluc3RhbmNlb2YgQmlnTnVtYmVyID8gbiA6IHN0ciApO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gcm91bmQoIHgsIERFQ0lNQUxfUExBQ0VTICsgeC5lICsgMSwgUk9VTkRJTkdfTU9ERSApO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIEF2b2lkIHBvdGVudGlhbCBpbnRlcnByZXRhdGlvbiBvZiBJbmZpbml0eSBhbmQgTmFOIGFzIGJhc2UgNDQrIHZhbHVlcy5cbiAgICAgICAgICAgICAgICAvLyBBbnkgbnVtYmVyIGluIGV4cG9uZW50aWFsIGZvcm0gd2lsbCBmYWlsIGR1ZSB0byB0aGUgW0VlXVsrLV0uXG4gICAgICAgICAgICAgICAgaWYgKCAoIG51bSA9IHR5cGVvZiBuID09ICdudW1iZXInICkgJiYgbiAqIDAgIT0gMCB8fFxuICAgICAgICAgICAgICAgICAgISggbmV3IFJlZ0V4cCggJ14tPycgKyAoIGMgPSAnWycgKyBBTFBIQUJFVC5zbGljZSggMCwgYiApICsgJ10rJyApICtcbiAgICAgICAgICAgICAgICAgICAgJyg/OlxcXFwuJyArIGMgKyAnKT8kJyxiIDwgMzcgPyAnaScgOiAnJyApICkudGVzdChzdHIpICkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gcGFyc2VOdW1lcmljKCB4LCBzdHIsIG51bSwgYiApO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmIChudW0pIHtcbiAgICAgICAgICAgICAgICAgICAgeC5zID0gMSAvIG4gPCAwID8gKCBzdHIgPSBzdHIuc2xpY2UoMSksIC0xICkgOiAxO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmICggRVJST1JTICYmIHN0ci5yZXBsYWNlKCAvXjBcXC4wKnxcXC4vLCAnJyApLmxlbmd0aCA+IDE1ICkge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyAnbmV3IEJpZ051bWJlcigpIG51bWJlciB0eXBlIGhhcyBtb3JlIHRoYW4gMTUgc2lnbmlmaWNhbnQgZGlnaXRzOiB7bn0nXG4gICAgICAgICAgICAgICAgICAgICAgICByYWlzZSggaWQsIHRvb01hbnlEaWdpdHMsIG4gKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIFByZXZlbnQgbGF0ZXIgY2hlY2sgZm9yIGxlbmd0aCBvbiBjb252ZXJ0ZWQgbnVtYmVyLlxuICAgICAgICAgICAgICAgICAgICBudW0gPSBmYWxzZTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICB4LnMgPSBzdHIuY2hhckNvZGVBdCgwKSA9PT0gNDUgPyAoIHN0ciA9IHN0ci5zbGljZSgxKSwgLTEgKSA6IDE7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgc3RyID0gY29udmVydEJhc2UoIHN0ciwgMTAsIGIsIHgucyApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBEZWNpbWFsIHBvaW50P1xuICAgICAgICAgICAgaWYgKCAoIGUgPSBzdHIuaW5kZXhPZignLicpICkgPiAtMSApIHN0ciA9IHN0ci5yZXBsYWNlKCAnLicsICcnICk7XG5cbiAgICAgICAgICAgIC8vIEV4cG9uZW50aWFsIGZvcm0/XG4gICAgICAgICAgICBpZiAoICggaSA9IHN0ci5zZWFyY2goIC9lL2kgKSApID4gMCApIHtcblxuICAgICAgICAgICAgICAgIC8vIERldGVybWluZSBleHBvbmVudC5cbiAgICAgICAgICAgICAgICBpZiAoIGUgPCAwICkgZSA9IGk7XG4gICAgICAgICAgICAgICAgZSArPSArc3RyLnNsaWNlKCBpICsgMSApO1xuICAgICAgICAgICAgICAgIHN0ciA9IHN0ci5zdWJzdHJpbmcoIDAsIGkgKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoIGUgPCAwICkge1xuXG4gICAgICAgICAgICAgICAgLy8gSW50ZWdlci5cbiAgICAgICAgICAgICAgICBlID0gc3RyLmxlbmd0aDtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gRGV0ZXJtaW5lIGxlYWRpbmcgemVyb3MuXG4gICAgICAgICAgICBmb3IgKCBpID0gMDsgc3RyLmNoYXJDb2RlQXQoaSkgPT09IDQ4OyBpKysgKTtcblxuICAgICAgICAgICAgLy8gRGV0ZXJtaW5lIHRyYWlsaW5nIHplcm9zLlxuICAgICAgICAgICAgZm9yICggbGVuID0gc3RyLmxlbmd0aDsgc3RyLmNoYXJDb2RlQXQoLS1sZW4pID09PSA0ODsgKTtcbiAgICAgICAgICAgIHN0ciA9IHN0ci5zbGljZSggaSwgbGVuICsgMSApO1xuXG4gICAgICAgICAgICBpZiAoc3RyKSB7XG4gICAgICAgICAgICAgICAgbGVuID0gc3RyLmxlbmd0aDtcblxuICAgICAgICAgICAgICAgIC8vIERpc2FsbG93IG51bWJlcnMgd2l0aCBvdmVyIDE1IHNpZ25pZmljYW50IGRpZ2l0cyBpZiBudW1iZXIgdHlwZS5cbiAgICAgICAgICAgICAgICAvLyAnbmV3IEJpZ051bWJlcigpIG51bWJlciB0eXBlIGhhcyBtb3JlIHRoYW4gMTUgc2lnbmlmaWNhbnQgZGlnaXRzOiB7bn0nXG4gICAgICAgICAgICAgICAgaWYgKCBudW0gJiYgRVJST1JTICYmIGxlbiA+IDE1ICkgcmFpc2UoIGlkLCB0b29NYW55RGlnaXRzLCB4LnMgKiBuICk7XG5cbiAgICAgICAgICAgICAgICBlID0gZSAtIGkgLSAxO1xuXG4gICAgICAgICAgICAgICAgIC8vIE92ZXJmbG93P1xuICAgICAgICAgICAgICAgIGlmICggZSA+IE1BWF9FWFAgKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gSW5maW5pdHkuXG4gICAgICAgICAgICAgICAgICAgIHguYyA9IHguZSA9IG51bGw7XG5cbiAgICAgICAgICAgICAgICAvLyBVbmRlcmZsb3c/XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmICggZSA8IE1JTl9FWFAgKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gWmVyby5cbiAgICAgICAgICAgICAgICAgICAgeC5jID0gWyB4LmUgPSAwIF07XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgeC5lID0gZTtcbiAgICAgICAgICAgICAgICAgICAgeC5jID0gW107XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gVHJhbnNmb3JtIGJhc2VcblxuICAgICAgICAgICAgICAgICAgICAvLyBlIGlzIHRoZSBiYXNlIDEwIGV4cG9uZW50LlxuICAgICAgICAgICAgICAgICAgICAvLyBpIGlzIHdoZXJlIHRvIHNsaWNlIHN0ciB0byBnZXQgdGhlIGZpcnN0IGVsZW1lbnQgb2YgdGhlIGNvZWZmaWNpZW50IGFycmF5LlxuICAgICAgICAgICAgICAgICAgICBpID0gKCBlICsgMSApICUgTE9HX0JBU0U7XG4gICAgICAgICAgICAgICAgICAgIGlmICggZSA8IDAgKSBpICs9IExPR19CQVNFO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmICggaSA8IGxlbiApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChpKSB4LmMucHVzaCggK3N0ci5zbGljZSggMCwgaSApICk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoIGxlbiAtPSBMT0dfQkFTRTsgaSA8IGxlbjsgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgeC5jLnB1c2goICtzdHIuc2xpY2UoIGksIGkgKz0gTE9HX0JBU0UgKSApO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgICBzdHIgPSBzdHIuc2xpY2UoaSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBpID0gTE9HX0JBU0UgLSBzdHIubGVuZ3RoO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgaSAtPSBsZW47XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBmb3IgKCA7IGktLTsgc3RyICs9ICcwJyApO1xuICAgICAgICAgICAgICAgICAgICB4LmMucHVzaCggK3N0ciApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgICAgICAgICAvLyBaZXJvLlxuICAgICAgICAgICAgICAgIHguYyA9IFsgeC5lID0gMCBdO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZCA9IDA7XG4gICAgICAgIH1cblxuXG4gICAgICAgIC8vIENPTlNUUlVDVE9SIFBST1BFUlRJRVNcblxuXG4gICAgICAgIEJpZ051bWJlci5hbm90aGVyID0gYW5vdGhlcjtcblxuICAgICAgICBCaWdOdW1iZXIuUk9VTkRfVVAgPSAwO1xuICAgICAgICBCaWdOdW1iZXIuUk9VTkRfRE9XTiA9IDE7XG4gICAgICAgIEJpZ051bWJlci5ST1VORF9DRUlMID0gMjtcbiAgICAgICAgQmlnTnVtYmVyLlJPVU5EX0ZMT09SID0gMztcbiAgICAgICAgQmlnTnVtYmVyLlJPVU5EX0hBTEZfVVAgPSA0O1xuICAgICAgICBCaWdOdW1iZXIuUk9VTkRfSEFMRl9ET1dOID0gNTtcbiAgICAgICAgQmlnTnVtYmVyLlJPVU5EX0hBTEZfRVZFTiA9IDY7XG4gICAgICAgIEJpZ051bWJlci5ST1VORF9IQUxGX0NFSUwgPSA3O1xuICAgICAgICBCaWdOdW1iZXIuUk9VTkRfSEFMRl9GTE9PUiA9IDg7XG4gICAgICAgIEJpZ051bWJlci5FVUNMSUQgPSA5O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogQ29uZmlndXJlIGluZnJlcXVlbnRseS1jaGFuZ2luZyBsaWJyYXJ5LXdpZGUgc2V0dGluZ3MuXG4gICAgICAgICAqXG4gICAgICAgICAqIEFjY2VwdCBhbiBvYmplY3Qgb3IgYW4gYXJndW1lbnQgbGlzdCwgd2l0aCBvbmUgb3IgbWFueSBvZiB0aGUgZm9sbG93aW5nIHByb3BlcnRpZXMgb3JcbiAgICAgICAgICogcGFyYW1ldGVycyByZXNwZWN0aXZlbHk6XG4gICAgICAgICAqXG4gICAgICAgICAqICAgREVDSU1BTF9QTEFDRVMgIHtudW1iZXJ9ICBJbnRlZ2VyLCAwIHRvIE1BWCBpbmNsdXNpdmVcbiAgICAgICAgICogICBST1VORElOR19NT0RFICAge251bWJlcn0gIEludGVnZXIsIDAgdG8gOCBpbmNsdXNpdmVcbiAgICAgICAgICogICBFWFBPTkVOVElBTF9BVCAge251bWJlcnxudW1iZXJbXX0gIEludGVnZXIsIC1NQVggdG8gTUFYIGluY2x1c2l2ZSBvclxuICAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW2ludGVnZXIgLU1BWCB0byAwIGluY2wuLCAwIHRvIE1BWCBpbmNsLl1cbiAgICAgICAgICogICBSQU5HRSAgICAgICAgICAge251bWJlcnxudW1iZXJbXX0gIE5vbi16ZXJvIGludGVnZXIsIC1NQVggdG8gTUFYIGluY2x1c2l2ZSBvclxuICAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW2ludGVnZXIgLU1BWCB0byAtMSBpbmNsLiwgaW50ZWdlciAxIHRvIE1BWCBpbmNsLl1cbiAgICAgICAgICogICBFUlJPUlMgICAgICAgICAge2Jvb2xlYW58bnVtYmVyfSAgIHRydWUsIGZhbHNlLCAxIG9yIDBcbiAgICAgICAgICogICBDUllQVE8gICAgICAgICAge2Jvb2xlYW58bnVtYmVyfSAgIHRydWUsIGZhbHNlLCAxIG9yIDBcbiAgICAgICAgICogICBNT0RVTE9fTU9ERSAgICAge251bWJlcn0gICAgICAgICAgIDAgdG8gOSBpbmNsdXNpdmVcbiAgICAgICAgICogICBQT1dfUFJFQ0lTSU9OICAge251bWJlcn0gICAgICAgICAgIDAgdG8gTUFYIGluY2x1c2l2ZVxuICAgICAgICAgKiAgIEZPUk1BVCAgICAgICAgICB7b2JqZWN0fSAgICAgICAgICAgU2VlIEJpZ051bWJlci5wcm90b3R5cGUudG9Gb3JtYXRcbiAgICAgICAgICogICAgICBkZWNpbWFsU2VwYXJhdG9yICAgICAgIHtzdHJpbmd9XG4gICAgICAgICAqICAgICAgZ3JvdXBTZXBhcmF0b3IgICAgICAgICB7c3RyaW5nfVxuICAgICAgICAgKiAgICAgIGdyb3VwU2l6ZSAgICAgICAgICAgICAge251bWJlcn1cbiAgICAgICAgICogICAgICBzZWNvbmRhcnlHcm91cFNpemUgICAgIHtudW1iZXJ9XG4gICAgICAgICAqICAgICAgZnJhY3Rpb25Hcm91cFNlcGFyYXRvciB7c3RyaW5nfVxuICAgICAgICAgKiAgICAgIGZyYWN0aW9uR3JvdXBTaXplICAgICAge251bWJlcn1cbiAgICAgICAgICpcbiAgICAgICAgICogKFRoZSB2YWx1ZXMgYXNzaWduZWQgdG8gdGhlIGFib3ZlIEZPUk1BVCBvYmplY3QgcHJvcGVydGllcyBhcmUgbm90IGNoZWNrZWQgZm9yIHZhbGlkaXR5LilcbiAgICAgICAgICpcbiAgICAgICAgICogRS5nLlxuICAgICAgICAgKiBCaWdOdW1iZXIuY29uZmlnKDIwLCA0KSBpcyBlcXVpdmFsZW50IHRvXG4gICAgICAgICAqIEJpZ051bWJlci5jb25maWcoeyBERUNJTUFMX1BMQUNFUyA6IDIwLCBST1VORElOR19NT0RFIDogNCB9KVxuICAgICAgICAgKlxuICAgICAgICAgKiBJZ25vcmUgcHJvcGVydGllcy9wYXJhbWV0ZXJzIHNldCB0byBudWxsIG9yIHVuZGVmaW5lZC5cbiAgICAgICAgICogUmV0dXJuIGFuIG9iamVjdCB3aXRoIHRoZSBwcm9wZXJ0aWVzIGN1cnJlbnQgdmFsdWVzLlxuICAgICAgICAgKi9cbiAgICAgICAgQmlnTnVtYmVyLmNvbmZpZyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciB2LCBwLFxuICAgICAgICAgICAgICAgIGkgPSAwLFxuICAgICAgICAgICAgICAgIHIgPSB7fSxcbiAgICAgICAgICAgICAgICBhID0gYXJndW1lbnRzLFxuICAgICAgICAgICAgICAgIG8gPSBhWzBdLFxuICAgICAgICAgICAgICAgIGhhcyA9IG8gJiYgdHlwZW9mIG8gPT0gJ29iamVjdCdcbiAgICAgICAgICAgICAgICAgID8gZnVuY3Rpb24gKCkgeyBpZiAoIG8uaGFzT3duUHJvcGVydHkocCkgKSByZXR1cm4gKCB2ID0gb1twXSApICE9IG51bGw7IH1cbiAgICAgICAgICAgICAgICAgIDogZnVuY3Rpb24gKCkgeyBpZiAoIGEubGVuZ3RoID4gaSApIHJldHVybiAoIHYgPSBhW2krK10gKSAhPSBudWxsOyB9O1xuXG4gICAgICAgICAgICAvLyBERUNJTUFMX1BMQUNFUyB7bnVtYmVyfSBJbnRlZ2VyLCAwIHRvIE1BWCBpbmNsdXNpdmUuXG4gICAgICAgICAgICAvLyAnY29uZmlnKCkgREVDSU1BTF9QTEFDRVMgbm90IGFuIGludGVnZXI6IHt2fSdcbiAgICAgICAgICAgIC8vICdjb25maWcoKSBERUNJTUFMX1BMQUNFUyBvdXQgb2YgcmFuZ2U6IHt2fSdcbiAgICAgICAgICAgIGlmICggaGFzKCBwID0gJ0RFQ0lNQUxfUExBQ0VTJyApICYmIGlzVmFsaWRJbnQoIHYsIDAsIE1BWCwgMiwgcCApICkge1xuICAgICAgICAgICAgICAgIERFQ0lNQUxfUExBQ0VTID0gdiB8IDA7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByW3BdID0gREVDSU1BTF9QTEFDRVM7XG5cbiAgICAgICAgICAgIC8vIFJPVU5ESU5HX01PREUge251bWJlcn0gSW50ZWdlciwgMCB0byA4IGluY2x1c2l2ZS5cbiAgICAgICAgICAgIC8vICdjb25maWcoKSBST1VORElOR19NT0RFIG5vdCBhbiBpbnRlZ2VyOiB7dn0nXG4gICAgICAgICAgICAvLyAnY29uZmlnKCkgUk9VTkRJTkdfTU9ERSBvdXQgb2YgcmFuZ2U6IHt2fSdcbiAgICAgICAgICAgIGlmICggaGFzKCBwID0gJ1JPVU5ESU5HX01PREUnICkgJiYgaXNWYWxpZEludCggdiwgMCwgOCwgMiwgcCApICkge1xuICAgICAgICAgICAgICAgIFJPVU5ESU5HX01PREUgPSB2IHwgMDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJbcF0gPSBST1VORElOR19NT0RFO1xuXG4gICAgICAgICAgICAvLyBFWFBPTkVOVElBTF9BVCB7bnVtYmVyfG51bWJlcltdfVxuICAgICAgICAgICAgLy8gSW50ZWdlciwgLU1BWCB0byBNQVggaW5jbHVzaXZlIG9yIFtpbnRlZ2VyIC1NQVggdG8gMCBpbmNsdXNpdmUsIDAgdG8gTUFYIGluY2x1c2l2ZV0uXG4gICAgICAgICAgICAvLyAnY29uZmlnKCkgRVhQT05FTlRJQUxfQVQgbm90IGFuIGludGVnZXI6IHt2fSdcbiAgICAgICAgICAgIC8vICdjb25maWcoKSBFWFBPTkVOVElBTF9BVCBvdXQgb2YgcmFuZ2U6IHt2fSdcbiAgICAgICAgICAgIGlmICggaGFzKCBwID0gJ0VYUE9ORU5USUFMX0FUJyApICkge1xuXG4gICAgICAgICAgICAgICAgaWYgKCBpc0FycmF5KHYpICkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoIGlzVmFsaWRJbnQoIHZbMF0sIC1NQVgsIDAsIDIsIHAgKSAmJiBpc1ZhbGlkSW50KCB2WzFdLCAwLCBNQVgsIDIsIHAgKSApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFRPX0VYUF9ORUcgPSB2WzBdIHwgMDtcbiAgICAgICAgICAgICAgICAgICAgICAgIFRPX0VYUF9QT1MgPSB2WzFdIHwgMDtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoIGlzVmFsaWRJbnQoIHYsIC1NQVgsIE1BWCwgMiwgcCApICkge1xuICAgICAgICAgICAgICAgICAgICBUT19FWFBfTkVHID0gLSggVE9fRVhQX1BPUyA9ICggdiA8IDAgPyAtdiA6IHYgKSB8IDAgKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByW3BdID0gWyBUT19FWFBfTkVHLCBUT19FWFBfUE9TIF07XG5cbiAgICAgICAgICAgIC8vIFJBTkdFIHtudW1iZXJ8bnVtYmVyW119IE5vbi16ZXJvIGludGVnZXIsIC1NQVggdG8gTUFYIGluY2x1c2l2ZSBvclxuICAgICAgICAgICAgLy8gW2ludGVnZXIgLU1BWCB0byAtMSBpbmNsdXNpdmUsIGludGVnZXIgMSB0byBNQVggaW5jbHVzaXZlXS5cbiAgICAgICAgICAgIC8vICdjb25maWcoKSBSQU5HRSBub3QgYW4gaW50ZWdlcjoge3Z9J1xuICAgICAgICAgICAgLy8gJ2NvbmZpZygpIFJBTkdFIGNhbm5vdCBiZSB6ZXJvOiB7dn0nXG4gICAgICAgICAgICAvLyAnY29uZmlnKCkgUkFOR0Ugb3V0IG9mIHJhbmdlOiB7dn0nXG4gICAgICAgICAgICBpZiAoIGhhcyggcCA9ICdSQU5HRScgKSApIHtcblxuICAgICAgICAgICAgICAgIGlmICggaXNBcnJheSh2KSApIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCBpc1ZhbGlkSW50KCB2WzBdLCAtTUFYLCAtMSwgMiwgcCApICYmIGlzVmFsaWRJbnQoIHZbMV0sIDEsIE1BWCwgMiwgcCApICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgTUlOX0VYUCA9IHZbMF0gfCAwO1xuICAgICAgICAgICAgICAgICAgICAgICAgTUFYX0VYUCA9IHZbMV0gfCAwO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmICggaXNWYWxpZEludCggdiwgLU1BWCwgTUFYLCAyLCBwICkgKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmICggdiB8IDAgKSBNSU5fRVhQID0gLSggTUFYX0VYUCA9ICggdiA8IDAgPyAtdiA6IHYgKSB8IDAgKTtcbiAgICAgICAgICAgICAgICAgICAgZWxzZSBpZiAoRVJST1JTKSByYWlzZSggMiwgcCArICcgY2Fubm90IGJlIHplcm8nLCB2ICk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcltwXSA9IFsgTUlOX0VYUCwgTUFYX0VYUCBdO1xuXG4gICAgICAgICAgICAvLyBFUlJPUlMge2Jvb2xlYW58bnVtYmVyfSB0cnVlLCBmYWxzZSwgMSBvciAwLlxuICAgICAgICAgICAgLy8gJ2NvbmZpZygpIEVSUk9SUyBub3QgYSBib29sZWFuIG9yIGJpbmFyeSBkaWdpdDoge3Z9J1xuICAgICAgICAgICAgaWYgKCBoYXMoIHAgPSAnRVJST1JTJyApICkge1xuXG4gICAgICAgICAgICAgICAgaWYgKCB2ID09PSAhIXYgfHwgdiA9PT0gMSB8fCB2ID09PSAwICkge1xuICAgICAgICAgICAgICAgICAgICBpZCA9IDA7XG4gICAgICAgICAgICAgICAgICAgIGlzVmFsaWRJbnQgPSAoIEVSUk9SUyA9ICEhdiApID8gaW50VmFsaWRhdG9yV2l0aEVycm9ycyA6IGludFZhbGlkYXRvck5vRXJyb3JzO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoRVJST1JTKSB7XG4gICAgICAgICAgICAgICAgICAgIHJhaXNlKCAyLCBwICsgbm90Qm9vbCwgdiApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJbcF0gPSBFUlJPUlM7XG5cbiAgICAgICAgICAgIC8vIENSWVBUTyB7Ym9vbGVhbnxudW1iZXJ9IHRydWUsIGZhbHNlLCAxIG9yIDAuXG4gICAgICAgICAgICAvLyAnY29uZmlnKCkgQ1JZUFRPIG5vdCBhIGJvb2xlYW4gb3IgYmluYXJ5IGRpZ2l0OiB7dn0nXG4gICAgICAgICAgICAvLyAnY29uZmlnKCkgY3J5cHRvIHVuYXZhaWxhYmxlOiB7Y3J5cHRvfSdcbiAgICAgICAgICAgIGlmICggaGFzKCBwID0gJ0NSWVBUTycgKSApIHtcblxuICAgICAgICAgICAgICAgIGlmICggdiA9PT0gISF2IHx8IHYgPT09IDEgfHwgdiA9PT0gMCApIHtcbiAgICAgICAgICAgICAgICAgICAgQ1JZUFRPID0gISEoIHYgJiYgY3J5cHRvICYmIHR5cGVvZiBjcnlwdG8gPT0gJ29iamVjdCcgKTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCB2ICYmICFDUllQVE8gJiYgRVJST1JTICkgcmFpc2UoIDIsICdjcnlwdG8gdW5hdmFpbGFibGUnLCBjcnlwdG8gKTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKEVSUk9SUykge1xuICAgICAgICAgICAgICAgICAgICByYWlzZSggMiwgcCArIG5vdEJvb2wsIHYgKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByW3BdID0gQ1JZUFRPO1xuXG4gICAgICAgICAgICAvLyBNT0RVTE9fTU9ERSB7bnVtYmVyfSBJbnRlZ2VyLCAwIHRvIDkgaW5jbHVzaXZlLlxuICAgICAgICAgICAgLy8gJ2NvbmZpZygpIE1PRFVMT19NT0RFIG5vdCBhbiBpbnRlZ2VyOiB7dn0nXG4gICAgICAgICAgICAvLyAnY29uZmlnKCkgTU9EVUxPX01PREUgb3V0IG9mIHJhbmdlOiB7dn0nXG4gICAgICAgICAgICBpZiAoIGhhcyggcCA9ICdNT0RVTE9fTU9ERScgKSAmJiBpc1ZhbGlkSW50KCB2LCAwLCA5LCAyLCBwICkgKSB7XG4gICAgICAgICAgICAgICAgTU9EVUxPX01PREUgPSB2IHwgMDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJbcF0gPSBNT0RVTE9fTU9ERTtcblxuICAgICAgICAgICAgLy8gUE9XX1BSRUNJU0lPTiB7bnVtYmVyfSBJbnRlZ2VyLCAwIHRvIE1BWCBpbmNsdXNpdmUuXG4gICAgICAgICAgICAvLyAnY29uZmlnKCkgUE9XX1BSRUNJU0lPTiBub3QgYW4gaW50ZWdlcjoge3Z9J1xuICAgICAgICAgICAgLy8gJ2NvbmZpZygpIFBPV19QUkVDSVNJT04gb3V0IG9mIHJhbmdlOiB7dn0nXG4gICAgICAgICAgICBpZiAoIGhhcyggcCA9ICdQT1dfUFJFQ0lTSU9OJyApICYmIGlzVmFsaWRJbnQoIHYsIDAsIE1BWCwgMiwgcCApICkge1xuICAgICAgICAgICAgICAgIFBPV19QUkVDSVNJT04gPSB2IHwgMDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJbcF0gPSBQT1dfUFJFQ0lTSU9OO1xuXG4gICAgICAgICAgICAvLyBGT1JNQVQge29iamVjdH1cbiAgICAgICAgICAgIC8vICdjb25maWcoKSBGT1JNQVQgbm90IGFuIG9iamVjdDoge3Z9J1xuICAgICAgICAgICAgaWYgKCBoYXMoIHAgPSAnRk9STUFUJyApICkge1xuXG4gICAgICAgICAgICAgICAgaWYgKCB0eXBlb2YgdiA9PSAnb2JqZWN0JyApIHtcbiAgICAgICAgICAgICAgICAgICAgRk9STUFUID0gdjtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKEVSUk9SUykge1xuICAgICAgICAgICAgICAgICAgICByYWlzZSggMiwgcCArICcgbm90IGFuIG9iamVjdCcsIHYgKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByW3BdID0gRk9STUFUO1xuXG4gICAgICAgICAgICByZXR1cm4gcjtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiBhIG5ldyBCaWdOdW1iZXIgd2hvc2UgdmFsdWUgaXMgdGhlIG1heGltdW0gb2YgdGhlIGFyZ3VtZW50cy5cbiAgICAgICAgICpcbiAgICAgICAgICogYXJndW1lbnRzIHtudW1iZXJ8c3RyaW5nfEJpZ051bWJlcn1cbiAgICAgICAgICovXG4gICAgICAgIEJpZ051bWJlci5tYXggPSBmdW5jdGlvbiAoKSB7IHJldHVybiBtYXhPck1pbiggYXJndW1lbnRzLCBQLmx0ICk7IH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gYSBuZXcgQmlnTnVtYmVyIHdob3NlIHZhbHVlIGlzIHRoZSBtaW5pbXVtIG9mIHRoZSBhcmd1bWVudHMuXG4gICAgICAgICAqXG4gICAgICAgICAqIGFyZ3VtZW50cyB7bnVtYmVyfHN0cmluZ3xCaWdOdW1iZXJ9XG4gICAgICAgICAqL1xuICAgICAgICBCaWdOdW1iZXIubWluID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gbWF4T3JNaW4oIGFyZ3VtZW50cywgUC5ndCApOyB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIGEgbmV3IEJpZ051bWJlciB3aXRoIGEgcmFuZG9tIHZhbHVlIGVxdWFsIHRvIG9yIGdyZWF0ZXIgdGhhbiAwIGFuZCBsZXNzIHRoYW4gMSxcbiAgICAgICAgICogYW5kIHdpdGggZHAsIG9yIERFQ0lNQUxfUExBQ0VTIGlmIGRwIGlzIG9taXR0ZWQsIGRlY2ltYWwgcGxhY2VzIChvciBsZXNzIGlmIHRyYWlsaW5nXG4gICAgICAgICAqIHplcm9zIGFyZSBwcm9kdWNlZCkuXG4gICAgICAgICAqXG4gICAgICAgICAqIFtkcF0ge251bWJlcn0gRGVjaW1hbCBwbGFjZXMuIEludGVnZXIsIDAgdG8gTUFYIGluY2x1c2l2ZS5cbiAgICAgICAgICpcbiAgICAgICAgICogJ3JhbmRvbSgpIGRlY2ltYWwgcGxhY2VzIG5vdCBhbiBpbnRlZ2VyOiB7ZHB9J1xuICAgICAgICAgKiAncmFuZG9tKCkgZGVjaW1hbCBwbGFjZXMgb3V0IG9mIHJhbmdlOiB7ZHB9J1xuICAgICAgICAgKiAncmFuZG9tKCkgY3J5cHRvIHVuYXZhaWxhYmxlOiB7Y3J5cHRvfSdcbiAgICAgICAgICovXG4gICAgICAgIEJpZ051bWJlci5yYW5kb20gPSAoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIHBvdzJfNTMgPSAweDIwMDAwMDAwMDAwMDAwO1xuXG4gICAgICAgICAgICAvLyBSZXR1cm4gYSA1MyBiaXQgaW50ZWdlciBuLCB3aGVyZSAwIDw9IG4gPCA5MDA3MTk5MjU0NzQwOTkyLlxuICAgICAgICAgICAgLy8gQ2hlY2sgaWYgTWF0aC5yYW5kb20oKSBwcm9kdWNlcyBtb3JlIHRoYW4gMzIgYml0cyBvZiByYW5kb21uZXNzLlxuICAgICAgICAgICAgLy8gSWYgaXQgZG9lcywgYXNzdW1lIGF0IGxlYXN0IDUzIGJpdHMgYXJlIHByb2R1Y2VkLCBvdGhlcndpc2UgYXNzdW1lIGF0IGxlYXN0IDMwIGJpdHMuXG4gICAgICAgICAgICAvLyAweDQwMDAwMDAwIGlzIDJeMzAsIDB4ODAwMDAwIGlzIDJeMjMsIDB4MWZmZmZmIGlzIDJeMjEgLSAxLlxuICAgICAgICAgICAgdmFyIHJhbmRvbTUzYml0SW50ID0gKE1hdGgucmFuZG9tKCkgKiBwb3cyXzUzKSAmIDB4MWZmZmZmXG4gICAgICAgICAgICAgID8gZnVuY3Rpb24gKCkgeyByZXR1cm4gbWF0aGZsb29yKCBNYXRoLnJhbmRvbSgpICogcG93Ml81MyApOyB9XG4gICAgICAgICAgICAgIDogZnVuY3Rpb24gKCkgeyByZXR1cm4gKChNYXRoLnJhbmRvbSgpICogMHg0MDAwMDAwMCB8IDApICogMHg4MDAwMDApICtcbiAgICAgICAgICAgICAgICAgIChNYXRoLnJhbmRvbSgpICogMHg4MDAwMDAgfCAwKTsgfTtcblxuICAgICAgICAgICAgcmV0dXJuIGZ1bmN0aW9uIChkcCkge1xuICAgICAgICAgICAgICAgIHZhciBhLCBiLCBlLCBrLCB2LFxuICAgICAgICAgICAgICAgICAgICBpID0gMCxcbiAgICAgICAgICAgICAgICAgICAgYyA9IFtdLFxuICAgICAgICAgICAgICAgICAgICByYW5kID0gbmV3IEJpZ051bWJlcihPTkUpO1xuXG4gICAgICAgICAgICAgICAgZHAgPSBkcCA9PSBudWxsIHx8ICFpc1ZhbGlkSW50KCBkcCwgMCwgTUFYLCAxNCApID8gREVDSU1BTF9QTEFDRVMgOiBkcCB8IDA7XG4gICAgICAgICAgICAgICAgayA9IG1hdGhjZWlsKCBkcCAvIExPR19CQVNFICk7XG5cbiAgICAgICAgICAgICAgICBpZiAoQ1JZUFRPKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gQnJvd3NlcnMgc3VwcG9ydGluZyBjcnlwdG8uZ2V0UmFuZG9tVmFsdWVzLlxuICAgICAgICAgICAgICAgICAgICBpZiAoIGNyeXB0byAmJiBjcnlwdG8uZ2V0UmFuZG9tVmFsdWVzICkge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBhID0gY3J5cHRvLmdldFJhbmRvbVZhbHVlcyggbmV3IFVpbnQzMkFycmF5KCBrICo9IDIgKSApO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBmb3IgKCA7IGkgPCBrOyApIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIDUzIGJpdHM6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gKChNYXRoLnBvdygyLCAzMikgLSAxKSAqIE1hdGgucG93KDIsIDIxKSkudG9TdHJpbmcoMilcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyAxMTExMSAxMTExMTExMSAxMTExMTExMSAxMTExMTExMSAxMTEwMDAwMCAwMDAwMDAwMCAwMDAwMDAwMFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vICgoTWF0aC5wb3coMiwgMzIpIC0gMSkgPj4+IDExKS50b1N0cmluZygyKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDExMTExIDExMTExMTExIDExMTExMTExXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gMHgyMDAwMCBpcyAyXjIxLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHYgPSBhW2ldICogMHgyMDAwMCArIChhW2kgKyAxXSA+Pj4gMTEpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gUmVqZWN0aW9uIHNhbXBsaW5nOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIDAgPD0gdiA8IDkwMDcxOTkyNTQ3NDA5OTJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBQcm9iYWJpbGl0eSB0aGF0IHYgPj0gOWUxNSwgaXNcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyA3MTk5MjU0NzQwOTkyIC8gOTAwNzE5OTI1NDc0MDk5MiB+PSAwLjAwMDgsIGkuZS4gMSBpbiAxMjUxXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCB2ID49IDllMTUgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGIgPSBjcnlwdG8uZ2V0UmFuZG9tVmFsdWVzKCBuZXcgVWludDMyQXJyYXkoMikgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYVtpXSA9IGJbMF07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFbaSArIDFdID0gYlsxXTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIDAgPD0gdiA8PSA4OTk5OTk5OTk5OTk5OTk5XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIDAgPD0gKHYgJSAxZTE0KSA8PSA5OTk5OTk5OTk5OTk5OVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjLnB1c2goIHYgJSAxZTE0ICk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGkgKz0gMjtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBpID0gayAvIDI7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gTm9kZS5qcyBzdXBwb3J0aW5nIGNyeXB0by5yYW5kb21CeXRlcy5cbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmICggY3J5cHRvICYmIGNyeXB0by5yYW5kb21CeXRlcyApIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gYnVmZmVyXG4gICAgICAgICAgICAgICAgICAgICAgICBhID0gY3J5cHRvLnJhbmRvbUJ5dGVzKCBrICo9IDcgKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgZm9yICggOyBpIDwgazsgKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyAweDEwMDAwMDAwMDAwMDAgaXMgMl40OCwgMHgxMDAwMDAwMDAwMCBpcyAyXjQwXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gMHgxMDAwMDAwMDAgaXMgMl4zMiwgMHgxMDAwMDAwIGlzIDJeMjRcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyAxMTExMSAxMTExMTExMSAxMTExMTExMSAxMTExMTExMSAxMTExMTExMSAxMTExMTExMSAxMTExMTExMVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIDAgPD0gdiA8IDkwMDcxOTkyNTQ3NDA5OTJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ID0gKCAoIGFbaV0gJiAzMSApICogMHgxMDAwMDAwMDAwMDAwICkgKyAoIGFbaSArIDFdICogMHgxMDAwMDAwMDAwMCApICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoIGFbaSArIDJdICogMHgxMDAwMDAwMDAgKSArICggYVtpICsgM10gKiAweDEwMDAwMDAgKSArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKCBhW2kgKyA0XSA8PCAxNiApICsgKCBhW2kgKyA1XSA8PCA4ICkgKyBhW2kgKyA2XTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICggdiA+PSA5ZTE1ICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcnlwdG8ucmFuZG9tQnl0ZXMoNykuY29weSggYSwgaSApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gMCA8PSAodiAlIDFlMTQpIDw9IDk5OTk5OTk5OTk5OTk5XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMucHVzaCggdiAlIDFlMTQgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaSArPSA3O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGkgPSBrIC8gNztcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmIChFUlJPUlMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJhaXNlKCAxNCwgJ2NyeXB0byB1bmF2YWlsYWJsZScsIGNyeXB0byApO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gVXNlIE1hdGgucmFuZG9tOiBDUllQVE8gaXMgZmFsc2Ugb3IgY3J5cHRvIGlzIHVuYXZhaWxhYmxlIGFuZCBFUlJPUlMgaXMgZmFsc2UuXG4gICAgICAgICAgICAgICAgaWYgKCFpKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgZm9yICggOyBpIDwgazsgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2ID0gcmFuZG9tNTNiaXRJbnQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICggdiA8IDllMTUgKSBjW2krK10gPSB2ICUgMWUxNDtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGsgPSBjWy0taV07XG4gICAgICAgICAgICAgICAgZHAgJT0gTE9HX0JBU0U7XG5cbiAgICAgICAgICAgICAgICAvLyBDb252ZXJ0IHRyYWlsaW5nIGRpZ2l0cyB0byB6ZXJvcyBhY2NvcmRpbmcgdG8gZHAuXG4gICAgICAgICAgICAgICAgaWYgKCBrICYmIGRwICkge1xuICAgICAgICAgICAgICAgICAgICB2ID0gUE9XU19URU5bTE9HX0JBU0UgLSBkcF07XG4gICAgICAgICAgICAgICAgICAgIGNbaV0gPSBtYXRoZmxvb3IoIGsgLyB2ICkgKiB2O1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIFJlbW92ZSB0cmFpbGluZyBlbGVtZW50cyB3aGljaCBhcmUgemVyby5cbiAgICAgICAgICAgICAgICBmb3IgKCA7IGNbaV0gPT09IDA7IGMucG9wKCksIGktLSApO1xuXG4gICAgICAgICAgICAgICAgLy8gWmVybz9cbiAgICAgICAgICAgICAgICBpZiAoIGkgPCAwICkge1xuICAgICAgICAgICAgICAgICAgICBjID0gWyBlID0gMCBdO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gUmVtb3ZlIGxlYWRpbmcgZWxlbWVudHMgd2hpY2ggYXJlIHplcm8gYW5kIGFkanVzdCBleHBvbmVudCBhY2NvcmRpbmdseS5cbiAgICAgICAgICAgICAgICAgICAgZm9yICggZSA9IC0xIDsgY1swXSA9PT0gMDsgYy5zaGlmdCgpLCBlIC09IExPR19CQVNFKTtcblxuICAgICAgICAgICAgICAgICAgICAvLyBDb3VudCB0aGUgZGlnaXRzIG9mIHRoZSBmaXJzdCBlbGVtZW50IG9mIGMgdG8gZGV0ZXJtaW5lIGxlYWRpbmcgemVyb3MsIGFuZC4uLlxuICAgICAgICAgICAgICAgICAgICBmb3IgKCBpID0gMSwgdiA9IGNbMF07IHYgPj0gMTA7IHYgLz0gMTAsIGkrKyk7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gYWRqdXN0IHRoZSBleHBvbmVudCBhY2NvcmRpbmdseS5cbiAgICAgICAgICAgICAgICAgICAgaWYgKCBpIDwgTE9HX0JBU0UgKSBlIC09IExPR19CQVNFIC0gaTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICByYW5kLmUgPSBlO1xuICAgICAgICAgICAgICAgIHJhbmQuYyA9IGM7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHJhbmQ7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9KSgpO1xuXG5cbiAgICAgICAgLy8gUFJJVkFURSBGVU5DVElPTlNcblxuXG4gICAgICAgIC8vIENvbnZlcnQgYSBudW1lcmljIHN0cmluZyBvZiBiYXNlSW4gdG8gYSBudW1lcmljIHN0cmluZyBvZiBiYXNlT3V0LlxuICAgICAgICBmdW5jdGlvbiBjb252ZXJ0QmFzZSggc3RyLCBiYXNlT3V0LCBiYXNlSW4sIHNpZ24gKSB7XG4gICAgICAgICAgICB2YXIgZCwgZSwgaywgciwgeCwgeGMsIHksXG4gICAgICAgICAgICAgICAgaSA9IHN0ci5pbmRleE9mKCAnLicgKSxcbiAgICAgICAgICAgICAgICBkcCA9IERFQ0lNQUxfUExBQ0VTLFxuICAgICAgICAgICAgICAgIHJtID0gUk9VTkRJTkdfTU9ERTtcblxuICAgICAgICAgICAgaWYgKCBiYXNlSW4gPCAzNyApIHN0ciA9IHN0ci50b0xvd2VyQ2FzZSgpO1xuXG4gICAgICAgICAgICAvLyBOb24taW50ZWdlci5cbiAgICAgICAgICAgIGlmICggaSA+PSAwICkge1xuICAgICAgICAgICAgICAgIGsgPSBQT1dfUFJFQ0lTSU9OO1xuXG4gICAgICAgICAgICAgICAgLy8gVW5saW1pdGVkIHByZWNpc2lvbi5cbiAgICAgICAgICAgICAgICBQT1dfUFJFQ0lTSU9OID0gMDtcbiAgICAgICAgICAgICAgICBzdHIgPSBzdHIucmVwbGFjZSggJy4nLCAnJyApO1xuICAgICAgICAgICAgICAgIHkgPSBuZXcgQmlnTnVtYmVyKGJhc2VJbik7XG4gICAgICAgICAgICAgICAgeCA9IHkucG93KCBzdHIubGVuZ3RoIC0gaSApO1xuICAgICAgICAgICAgICAgIFBPV19QUkVDSVNJT04gPSBrO1xuXG4gICAgICAgICAgICAgICAgLy8gQ29udmVydCBzdHIgYXMgaWYgYW4gaW50ZWdlciwgdGhlbiByZXN0b3JlIHRoZSBmcmFjdGlvbiBwYXJ0IGJ5IGRpdmlkaW5nIHRoZVxuICAgICAgICAgICAgICAgIC8vIHJlc3VsdCBieSBpdHMgYmFzZSByYWlzZWQgdG8gYSBwb3dlci5cbiAgICAgICAgICAgICAgICB5LmMgPSB0b0Jhc2VPdXQoIHRvRml4ZWRQb2ludCggY29lZmZUb1N0cmluZyggeC5jICksIHguZSApLCAxMCwgYmFzZU91dCApO1xuICAgICAgICAgICAgICAgIHkuZSA9IHkuYy5sZW5ndGg7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIENvbnZlcnQgdGhlIG51bWJlciBhcyBpbnRlZ2VyLlxuICAgICAgICAgICAgeGMgPSB0b0Jhc2VPdXQoIHN0ciwgYmFzZUluLCBiYXNlT3V0ICk7XG4gICAgICAgICAgICBlID0gayA9IHhjLmxlbmd0aDtcblxuICAgICAgICAgICAgLy8gUmVtb3ZlIHRyYWlsaW5nIHplcm9zLlxuICAgICAgICAgICAgZm9yICggOyB4Y1stLWtdID09IDA7IHhjLnBvcCgpICk7XG4gICAgICAgICAgICBpZiAoICF4Y1swXSApIHJldHVybiAnMCc7XG5cbiAgICAgICAgICAgIGlmICggaSA8IDAgKSB7XG4gICAgICAgICAgICAgICAgLS1lO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB4LmMgPSB4YztcbiAgICAgICAgICAgICAgICB4LmUgPSBlO1xuXG4gICAgICAgICAgICAgICAgLy8gc2lnbiBpcyBuZWVkZWQgZm9yIGNvcnJlY3Qgcm91bmRpbmcuXG4gICAgICAgICAgICAgICAgeC5zID0gc2lnbjtcbiAgICAgICAgICAgICAgICB4ID0gZGl2KCB4LCB5LCBkcCwgcm0sIGJhc2VPdXQgKTtcbiAgICAgICAgICAgICAgICB4YyA9IHguYztcbiAgICAgICAgICAgICAgICByID0geC5yO1xuICAgICAgICAgICAgICAgIGUgPSB4LmU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGQgPSBlICsgZHAgKyAxO1xuXG4gICAgICAgICAgICAvLyBUaGUgcm91bmRpbmcgZGlnaXQsIGkuZS4gdGhlIGRpZ2l0IHRvIHRoZSByaWdodCBvZiB0aGUgZGlnaXQgdGhhdCBtYXkgYmUgcm91bmRlZCB1cC5cbiAgICAgICAgICAgIGkgPSB4Y1tkXTtcbiAgICAgICAgICAgIGsgPSBiYXNlT3V0IC8gMjtcbiAgICAgICAgICAgIHIgPSByIHx8IGQgPCAwIHx8IHhjW2QgKyAxXSAhPSBudWxsO1xuXG4gICAgICAgICAgICByID0gcm0gPCA0ID8gKCBpICE9IG51bGwgfHwgciApICYmICggcm0gPT0gMCB8fCBybSA9PSAoIHgucyA8IDAgPyAzIDogMiApIClcbiAgICAgICAgICAgICAgICAgICAgICAgOiBpID4gayB8fCBpID09IGsgJiYoIHJtID09IDQgfHwgciB8fCBybSA9PSA2ICYmIHhjW2QgLSAxXSAmIDEgfHxcbiAgICAgICAgICAgICAgICAgICAgICAgICBybSA9PSAoIHgucyA8IDAgPyA4IDogNyApICk7XG5cbiAgICAgICAgICAgIGlmICggZCA8IDEgfHwgIXhjWzBdICkge1xuXG4gICAgICAgICAgICAgICAgLy8gMV4tZHAgb3IgMC5cbiAgICAgICAgICAgICAgICBzdHIgPSByID8gdG9GaXhlZFBvaW50KCAnMScsIC1kcCApIDogJzAnO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB4Yy5sZW5ndGggPSBkO1xuXG4gICAgICAgICAgICAgICAgaWYgKHIpIHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBSb3VuZGluZyB1cCBtYXkgbWVhbiB0aGUgcHJldmlvdXMgZGlnaXQgaGFzIHRvIGJlIHJvdW5kZWQgdXAgYW5kIHNvIG9uLlxuICAgICAgICAgICAgICAgICAgICBmb3IgKCAtLWJhc2VPdXQ7ICsreGNbLS1kXSA+IGJhc2VPdXQ7ICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgeGNbZF0gPSAwO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoICFkICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICsrZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4Yy51bnNoaWZ0KDEpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gRGV0ZXJtaW5lIHRyYWlsaW5nIHplcm9zLlxuICAgICAgICAgICAgICAgIGZvciAoIGsgPSB4Yy5sZW5ndGg7ICF4Y1stLWtdOyApO1xuXG4gICAgICAgICAgICAgICAgLy8gRS5nLiBbNCwgMTEsIDE1XSBiZWNvbWVzIDRiZi5cbiAgICAgICAgICAgICAgICBmb3IgKCBpID0gMCwgc3RyID0gJyc7IGkgPD0gazsgc3RyICs9IEFMUEhBQkVULmNoYXJBdCggeGNbaSsrXSApICk7XG4gICAgICAgICAgICAgICAgc3RyID0gdG9GaXhlZFBvaW50KCBzdHIsIGUgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gVGhlIGNhbGxlciB3aWxsIGFkZCB0aGUgc2lnbi5cbiAgICAgICAgICAgIHJldHVybiBzdHI7XG4gICAgICAgIH1cblxuXG4gICAgICAgIC8vIFBlcmZvcm0gZGl2aXNpb24gaW4gdGhlIHNwZWNpZmllZCBiYXNlLiBDYWxsZWQgYnkgZGl2IGFuZCBjb252ZXJ0QmFzZS5cbiAgICAgICAgZGl2ID0gKGZ1bmN0aW9uICgpIHtcblxuICAgICAgICAgICAgLy8gQXNzdW1lIG5vbi16ZXJvIHggYW5kIGsuXG4gICAgICAgICAgICBmdW5jdGlvbiBtdWx0aXBseSggeCwgaywgYmFzZSApIHtcbiAgICAgICAgICAgICAgICB2YXIgbSwgdGVtcCwgeGxvLCB4aGksXG4gICAgICAgICAgICAgICAgICAgIGNhcnJ5ID0gMCxcbiAgICAgICAgICAgICAgICAgICAgaSA9IHgubGVuZ3RoLFxuICAgICAgICAgICAgICAgICAgICBrbG8gPSBrICUgU1FSVF9CQVNFLFxuICAgICAgICAgICAgICAgICAgICBraGkgPSBrIC8gU1FSVF9CQVNFIHwgMDtcblxuICAgICAgICAgICAgICAgIGZvciAoIHggPSB4LnNsaWNlKCk7IGktLTsgKSB7XG4gICAgICAgICAgICAgICAgICAgIHhsbyA9IHhbaV0gJSBTUVJUX0JBU0U7XG4gICAgICAgICAgICAgICAgICAgIHhoaSA9IHhbaV0gLyBTUVJUX0JBU0UgfCAwO1xuICAgICAgICAgICAgICAgICAgICBtID0ga2hpICogeGxvICsgeGhpICoga2xvO1xuICAgICAgICAgICAgICAgICAgICB0ZW1wID0ga2xvICogeGxvICsgKCAoIG0gJSBTUVJUX0JBU0UgKSAqIFNRUlRfQkFTRSApICsgY2Fycnk7XG4gICAgICAgICAgICAgICAgICAgIGNhcnJ5ID0gKCB0ZW1wIC8gYmFzZSB8IDAgKSArICggbSAvIFNRUlRfQkFTRSB8IDAgKSArIGtoaSAqIHhoaTtcbiAgICAgICAgICAgICAgICAgICAgeFtpXSA9IHRlbXAgJSBiYXNlO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmIChjYXJyeSkgeC51bnNoaWZ0KGNhcnJ5KTtcblxuICAgICAgICAgICAgICAgIHJldHVybiB4O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBmdW5jdGlvbiBjb21wYXJlKCBhLCBiLCBhTCwgYkwgKSB7XG4gICAgICAgICAgICAgICAgdmFyIGksIGNtcDtcblxuICAgICAgICAgICAgICAgIGlmICggYUwgIT0gYkwgKSB7XG4gICAgICAgICAgICAgICAgICAgIGNtcCA9IGFMID4gYkwgPyAxIDogLTE7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgICAgICAgICBmb3IgKCBpID0gY21wID0gMDsgaSA8IGFMOyBpKysgKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICggYVtpXSAhPSBiW2ldICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNtcCA9IGFbaV0gPiBiW2ldID8gMSA6IC0xO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJldHVybiBjbXA7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGZ1bmN0aW9uIHN1YnRyYWN0KCBhLCBiLCBhTCwgYmFzZSApIHtcbiAgICAgICAgICAgICAgICB2YXIgaSA9IDA7XG5cbiAgICAgICAgICAgICAgICAvLyBTdWJ0cmFjdCBiIGZyb20gYS5cbiAgICAgICAgICAgICAgICBmb3IgKCA7IGFMLS07ICkge1xuICAgICAgICAgICAgICAgICAgICBhW2FMXSAtPSBpO1xuICAgICAgICAgICAgICAgICAgICBpID0gYVthTF0gPCBiW2FMXSA/IDEgOiAwO1xuICAgICAgICAgICAgICAgICAgICBhW2FMXSA9IGkgKiBiYXNlICsgYVthTF0gLSBiW2FMXTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBSZW1vdmUgbGVhZGluZyB6ZXJvcy5cbiAgICAgICAgICAgICAgICBmb3IgKCA7ICFhWzBdICYmIGEubGVuZ3RoID4gMTsgYS5zaGlmdCgpICk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIHg6IGRpdmlkZW5kLCB5OiBkaXZpc29yLlxuICAgICAgICAgICAgcmV0dXJuIGZ1bmN0aW9uICggeCwgeSwgZHAsIHJtLCBiYXNlICkge1xuICAgICAgICAgICAgICAgIHZhciBjbXAsIGUsIGksIG1vcmUsIG4sIHByb2QsIHByb2RMLCBxLCBxYywgcmVtLCByZW1MLCByZW0wLCB4aSwgeEwsIHljMCxcbiAgICAgICAgICAgICAgICAgICAgeUwsIHl6LFxuICAgICAgICAgICAgICAgICAgICBzID0geC5zID09IHkucyA/IDEgOiAtMSxcbiAgICAgICAgICAgICAgICAgICAgeGMgPSB4LmMsXG4gICAgICAgICAgICAgICAgICAgIHljID0geS5jO1xuXG4gICAgICAgICAgICAgICAgLy8gRWl0aGVyIE5hTiwgSW5maW5pdHkgb3IgMD9cbiAgICAgICAgICAgICAgICBpZiAoICF4YyB8fCAheGNbMF0gfHwgIXljIHx8ICF5Y1swXSApIHtcblxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gbmV3IEJpZ051bWJlcihcblxuICAgICAgICAgICAgICAgICAgICAgIC8vIFJldHVybiBOYU4gaWYgZWl0aGVyIE5hTiwgb3IgYm90aCBJbmZpbml0eSBvciAwLlxuICAgICAgICAgICAgICAgICAgICAgICF4LnMgfHwgIXkucyB8fCAoIHhjID8geWMgJiYgeGNbMF0gPT0geWNbMF0gOiAheWMgKSA/IE5hTiA6XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIFJldHVybiDCsTAgaWYgeCBpcyDCsTAgb3IgeSBpcyDCsUluZmluaXR5LCBvciByZXR1cm4gwrFJbmZpbml0eSBhcyB5IGlzIMKxMC5cbiAgICAgICAgICAgICAgICAgICAgICAgIHhjICYmIHhjWzBdID09IDAgfHwgIXljID8gcyAqIDAgOiBzIC8gMFxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHEgPSBuZXcgQmlnTnVtYmVyKHMpO1xuICAgICAgICAgICAgICAgIHFjID0gcS5jID0gW107XG4gICAgICAgICAgICAgICAgZSA9IHguZSAtIHkuZTtcbiAgICAgICAgICAgICAgICBzID0gZHAgKyBlICsgMTtcblxuICAgICAgICAgICAgICAgIGlmICggIWJhc2UgKSB7XG4gICAgICAgICAgICAgICAgICAgIGJhc2UgPSBCQVNFO1xuICAgICAgICAgICAgICAgICAgICBlID0gYml0Rmxvb3IoIHguZSAvIExPR19CQVNFICkgLSBiaXRGbG9vciggeS5lIC8gTE9HX0JBU0UgKTtcbiAgICAgICAgICAgICAgICAgICAgcyA9IHMgLyBMT0dfQkFTRSB8IDA7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gUmVzdWx0IGV4cG9uZW50IG1heSBiZSBvbmUgbGVzcyB0aGVuIHRoZSBjdXJyZW50IHZhbHVlIG9mIGUuXG4gICAgICAgICAgICAgICAgLy8gVGhlIGNvZWZmaWNpZW50cyBvZiB0aGUgQmlnTnVtYmVycyBmcm9tIGNvbnZlcnRCYXNlIG1heSBoYXZlIHRyYWlsaW5nIHplcm9zLlxuICAgICAgICAgICAgICAgIGZvciAoIGkgPSAwOyB5Y1tpXSA9PSAoIHhjW2ldIHx8IDAgKTsgaSsrICk7XG4gICAgICAgICAgICAgICAgaWYgKCB5Y1tpXSA+ICggeGNbaV0gfHwgMCApICkgZS0tO1xuXG4gICAgICAgICAgICAgICAgaWYgKCBzIDwgMCApIHtcbiAgICAgICAgICAgICAgICAgICAgcWMucHVzaCgxKTtcbiAgICAgICAgICAgICAgICAgICAgbW9yZSA9IHRydWU7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgeEwgPSB4Yy5sZW5ndGg7XG4gICAgICAgICAgICAgICAgICAgIHlMID0geWMubGVuZ3RoO1xuICAgICAgICAgICAgICAgICAgICBpID0gMDtcbiAgICAgICAgICAgICAgICAgICAgcyArPSAyO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIE5vcm1hbGlzZSB4YyBhbmQgeWMgc28gaGlnaGVzdCBvcmRlciBkaWdpdCBvZiB5YyBpcyA+PSBiYXNlIC8gMi5cblxuICAgICAgICAgICAgICAgICAgICBuID0gbWF0aGZsb29yKCBiYXNlIC8gKCB5Y1swXSArIDEgKSApO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIE5vdCBuZWNlc3NhcnksIGJ1dCB0byBoYW5kbGUgb2RkIGJhc2VzIHdoZXJlIHljWzBdID09ICggYmFzZSAvIDIgKSAtIDEuXG4gICAgICAgICAgICAgICAgICAgIC8vIGlmICggbiA+IDEgfHwgbisrID09IDEgJiYgeWNbMF0gPCBiYXNlIC8gMiApIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCBuID4gMSApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHljID0gbXVsdGlwbHkoIHljLCBuLCBiYXNlICk7XG4gICAgICAgICAgICAgICAgICAgICAgICB4YyA9IG11bHRpcGx5KCB4YywgbiwgYmFzZSApO1xuICAgICAgICAgICAgICAgICAgICAgICAgeUwgPSB5Yy5sZW5ndGg7XG4gICAgICAgICAgICAgICAgICAgICAgICB4TCA9IHhjLmxlbmd0aDtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIHhpID0geUw7XG4gICAgICAgICAgICAgICAgICAgIHJlbSA9IHhjLnNsaWNlKCAwLCB5TCApO1xuICAgICAgICAgICAgICAgICAgICByZW1MID0gcmVtLmxlbmd0aDtcblxuICAgICAgICAgICAgICAgICAgICAvLyBBZGQgemVyb3MgdG8gbWFrZSByZW1haW5kZXIgYXMgbG9uZyBhcyBkaXZpc29yLlxuICAgICAgICAgICAgICAgICAgICBmb3IgKCA7IHJlbUwgPCB5TDsgcmVtW3JlbUwrK10gPSAwICk7XG4gICAgICAgICAgICAgICAgICAgIHl6ID0geWMuc2xpY2UoKTtcbiAgICAgICAgICAgICAgICAgICAgeXoudW5zaGlmdCgwKTtcbiAgICAgICAgICAgICAgICAgICAgeWMwID0geWNbMF07XG4gICAgICAgICAgICAgICAgICAgIGlmICggeWNbMV0gPj0gYmFzZSAvIDIgKSB5YzArKztcbiAgICAgICAgICAgICAgICAgICAgLy8gTm90IG5lY2Vzc2FyeSwgYnV0IHRvIHByZXZlbnQgdHJpYWwgZGlnaXQgbiA+IGJhc2UsIHdoZW4gdXNpbmcgYmFzZSAzLlxuICAgICAgICAgICAgICAgICAgICAvLyBlbHNlIGlmICggYmFzZSA9PSAzICYmIHljMCA9PSAxICkgeWMwID0gMSArIDFlLTE1O1xuXG4gICAgICAgICAgICAgICAgICAgIGRvIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG4gPSAwO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBDb21wYXJlIGRpdmlzb3IgYW5kIHJlbWFpbmRlci5cbiAgICAgICAgICAgICAgICAgICAgICAgIGNtcCA9IGNvbXBhcmUoIHljLCByZW0sIHlMLCByZW1MICk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIGRpdmlzb3IgPCByZW1haW5kZXIuXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoIGNtcCA8IDAgKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBDYWxjdWxhdGUgdHJpYWwgZGlnaXQsIG4uXG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZW0wID0gcmVtWzBdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICggeUwgIT0gcmVtTCApIHJlbTAgPSByZW0wICogYmFzZSArICggcmVtWzFdIHx8IDAgKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIG4gaXMgaG93IG1hbnkgdGltZXMgdGhlIGRpdmlzb3IgZ29lcyBpbnRvIHRoZSBjdXJyZW50IHJlbWFpbmRlci5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gbWF0aGZsb29yKCByZW0wIC8geWMwICk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyAgQWxnb3JpdGhtOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vICAxLiBwcm9kdWN0ID0gZGl2aXNvciAqIHRyaWFsIGRpZ2l0IChuKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vICAyLiBpZiBwcm9kdWN0ID4gcmVtYWluZGVyOiBwcm9kdWN0IC09IGRpdmlzb3IsIG4tLVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vICAzLiByZW1haW5kZXIgLT0gcHJvZHVjdFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vICA0LiBpZiBwcm9kdWN0IHdhcyA8IHJlbWFpbmRlciBhdCAyOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vICAgIDUuIGNvbXBhcmUgbmV3IHJlbWFpbmRlciBhbmQgZGl2aXNvclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vICAgIDYuIElmIHJlbWFpbmRlciA+IGRpdmlzb3I6IHJlbWFpbmRlciAtPSBkaXZpc29yLCBuKytcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICggbiA+IDEgKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gbiBtYXkgYmUgPiBiYXNlIG9ubHkgd2hlbiBiYXNlIGlzIDMuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChuID49IGJhc2UpIG4gPSBiYXNlIC0gMTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBwcm9kdWN0ID0gZGl2aXNvciAqIHRyaWFsIGRpZ2l0LlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9kID0gbXVsdGlwbHkoIHljLCBuLCBiYXNlICk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2RMID0gcHJvZC5sZW5ndGg7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbUwgPSByZW0ubGVuZ3RoO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIENvbXBhcmUgcHJvZHVjdCBhbmQgcmVtYWluZGVyLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBJZiBwcm9kdWN0ID4gcmVtYWluZGVyLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBUcmlhbCBkaWdpdCBuIHRvbyBoaWdoLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBuIGlzIDEgdG9vIGhpZ2ggYWJvdXQgNSUgb2YgdGhlIHRpbWUsIGFuZCBpcyBub3Qga25vd24gdG8gaGF2ZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBldmVyIGJlZW4gbW9yZSB0aGFuIDEgdG9vIGhpZ2guXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWxlICggY29tcGFyZSggcHJvZCwgcmVtLCBwcm9kTCwgcmVtTCApID09IDEgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuLS07XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFN1YnRyYWN0IGRpdmlzb3IgZnJvbSBwcm9kdWN0LlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VidHJhY3QoIHByb2QsIHlMIDwgcHJvZEwgPyB5eiA6IHljLCBwcm9kTCwgYmFzZSApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvZEwgPSBwcm9kLmxlbmd0aDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNtcCA9IDE7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIG4gaXMgMCBvciAxLCBjbXAgaXMgLTEuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIG4gaXMgMCwgdGhlcmUgaXMgbm8gbmVlZCB0byBjb21wYXJlIHljIGFuZCByZW0gYWdhaW4gYmVsb3csXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHNvIGNoYW5nZSBjbXAgdG8gMSB0byBhdm9pZCBpdC5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gSWYgbiBpcyAxLCBsZWF2ZSBjbXAgYXMgLTEsIHNvIHljIGFuZCByZW0gYXJlIGNvbXBhcmVkIGFnYWluLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoIG4gPT0gMCApIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gZGl2aXNvciA8IHJlbWFpbmRlciwgc28gbiBtdXN0IGJlIGF0IGxlYXN0IDEuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbXAgPSBuID0gMTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHByb2R1Y3QgPSBkaXZpc29yXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2QgPSB5Yy5zbGljZSgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9kTCA9IHByb2QubGVuZ3RoO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICggcHJvZEwgPCByZW1MICkgcHJvZC51bnNoaWZ0KDApO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gU3VidHJhY3QgcHJvZHVjdCBmcm9tIHJlbWFpbmRlci5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJ0cmFjdCggcmVtLCBwcm9kLCByZW1MLCBiYXNlICk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVtTCA9IHJlbS5sZW5ndGg7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gSWYgcHJvZHVjdCB3YXMgPCByZW1haW5kZXIuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCBjbXAgPT0gLTEgKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQ29tcGFyZSBkaXZpc29yIGFuZCBuZXcgcmVtYWluZGVyLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBJZiBkaXZpc29yIDwgbmV3IHJlbWFpbmRlciwgc3VidHJhY3QgZGl2aXNvciBmcm9tIHJlbWFpbmRlci5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gVHJpYWwgZGlnaXQgbiB0b28gbG93LlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBuIGlzIDEgdG9vIGxvdyBhYm91dCA1JSBvZiB0aGUgdGltZSwgYW5kIHZlcnkgcmFyZWx5IDIgdG9vIGxvdy5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hpbGUgKCBjb21wYXJlKCB5YywgcmVtLCB5TCwgcmVtTCApIDwgMSApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4rKztcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gU3VidHJhY3QgZGl2aXNvciBmcm9tIHJlbWFpbmRlci5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnRyYWN0KCByZW0sIHlMIDwgcmVtTCA/IHl6IDogeWMsIHJlbUwsIGJhc2UgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbUwgPSByZW0ubGVuZ3RoO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmICggY21wID09PSAwICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4rKztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZW0gPSBbMF07XG4gICAgICAgICAgICAgICAgICAgICAgICB9IC8vIGVsc2UgY21wID09PSAxIGFuZCBuIHdpbGwgYmUgMFxuXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBBZGQgdGhlIG5leHQgZGlnaXQsIG4sIHRvIHRoZSByZXN1bHQgYXJyYXkuXG4gICAgICAgICAgICAgICAgICAgICAgICBxY1tpKytdID0gbjtcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gVXBkYXRlIHRoZSByZW1haW5kZXIuXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoIHJlbVswXSApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZW1bcmVtTCsrXSA9IHhjW3hpXSB8fCAwO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZW0gPSBbIHhjW3hpXSBdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbUwgPSAxO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9IHdoaWxlICggKCB4aSsrIDwgeEwgfHwgcmVtWzBdICE9IG51bGwgKSAmJiBzLS0gKTtcblxuICAgICAgICAgICAgICAgICAgICBtb3JlID0gcmVtWzBdICE9IG51bGw7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gTGVhZGluZyB6ZXJvP1xuICAgICAgICAgICAgICAgICAgICBpZiAoICFxY1swXSApIHFjLnNoaWZ0KCk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKCBiYXNlID09IEJBU0UgKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gVG8gY2FsY3VsYXRlIHEuZSwgZmlyc3QgZ2V0IHRoZSBudW1iZXIgb2YgZGlnaXRzIG9mIHFjWzBdLlxuICAgICAgICAgICAgICAgICAgICBmb3IgKCBpID0gMSwgcyA9IHFjWzBdOyBzID49IDEwOyBzIC89IDEwLCBpKysgKTtcbiAgICAgICAgICAgICAgICAgICAgcm91bmQoIHEsIGRwICsgKCBxLmUgPSBpICsgZSAqIExPR19CQVNFIC0gMSApICsgMSwgcm0sIG1vcmUgKTtcblxuICAgICAgICAgICAgICAgIC8vIENhbGxlciBpcyBjb252ZXJ0QmFzZS5cbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBxLmUgPSBlO1xuICAgICAgICAgICAgICAgICAgICBxLnIgPSArbW9yZTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICByZXR1cm4gcTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pKCk7XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gYSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSB2YWx1ZSBvZiBCaWdOdW1iZXIgbiBpbiBmaXhlZC1wb2ludCBvciBleHBvbmVudGlhbFxuICAgICAgICAgKiBub3RhdGlvbiByb3VuZGVkIHRvIHRoZSBzcGVjaWZpZWQgZGVjaW1hbCBwbGFjZXMgb3Igc2lnbmlmaWNhbnQgZGlnaXRzLlxuICAgICAgICAgKlxuICAgICAgICAgKiBuIGlzIGEgQmlnTnVtYmVyLlxuICAgICAgICAgKiBpIGlzIHRoZSBpbmRleCBvZiB0aGUgbGFzdCBkaWdpdCByZXF1aXJlZCAoaS5lLiB0aGUgZGlnaXQgdGhhdCBtYXkgYmUgcm91bmRlZCB1cCkuXG4gICAgICAgICAqIHJtIGlzIHRoZSByb3VuZGluZyBtb2RlLlxuICAgICAgICAgKiBjYWxsZXIgaXMgY2FsbGVyIGlkOiB0b0V4cG9uZW50aWFsIDE5LCB0b0ZpeGVkIDIwLCB0b0Zvcm1hdCAyMSwgdG9QcmVjaXNpb24gMjQuXG4gICAgICAgICAqL1xuICAgICAgICBmdW5jdGlvbiBmb3JtYXQoIG4sIGksIHJtLCBjYWxsZXIgKSB7XG4gICAgICAgICAgICB2YXIgYzAsIGUsIG5lLCBsZW4sIHN0cjtcblxuICAgICAgICAgICAgcm0gPSBybSAhPSBudWxsICYmIGlzVmFsaWRJbnQoIHJtLCAwLCA4LCBjYWxsZXIsIHJvdW5kaW5nTW9kZSApXG4gICAgICAgICAgICAgID8gcm0gfCAwIDogUk9VTkRJTkdfTU9ERTtcblxuICAgICAgICAgICAgaWYgKCAhbi5jICkgcmV0dXJuIG4udG9TdHJpbmcoKTtcbiAgICAgICAgICAgIGMwID0gbi5jWzBdO1xuICAgICAgICAgICAgbmUgPSBuLmU7XG5cbiAgICAgICAgICAgIGlmICggaSA9PSBudWxsICkge1xuICAgICAgICAgICAgICAgIHN0ciA9IGNvZWZmVG9TdHJpbmcoIG4uYyApO1xuICAgICAgICAgICAgICAgIHN0ciA9IGNhbGxlciA9PSAxOSB8fCBjYWxsZXIgPT0gMjQgJiYgbmUgPD0gVE9fRVhQX05FR1xuICAgICAgICAgICAgICAgICAgPyB0b0V4cG9uZW50aWFsKCBzdHIsIG5lIClcbiAgICAgICAgICAgICAgICAgIDogdG9GaXhlZFBvaW50KCBzdHIsIG5lICk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIG4gPSByb3VuZCggbmV3IEJpZ051bWJlcihuKSwgaSwgcm0gKTtcblxuICAgICAgICAgICAgICAgIC8vIG4uZSBtYXkgaGF2ZSBjaGFuZ2VkIGlmIHRoZSB2YWx1ZSB3YXMgcm91bmRlZCB1cC5cbiAgICAgICAgICAgICAgICBlID0gbi5lO1xuXG4gICAgICAgICAgICAgICAgc3RyID0gY29lZmZUb1N0cmluZyggbi5jICk7XG4gICAgICAgICAgICAgICAgbGVuID0gc3RyLmxlbmd0aDtcblxuICAgICAgICAgICAgICAgIC8vIHRvUHJlY2lzaW9uIHJldHVybnMgZXhwb25lbnRpYWwgbm90YXRpb24gaWYgdGhlIG51bWJlciBvZiBzaWduaWZpY2FudCBkaWdpdHNcbiAgICAgICAgICAgICAgICAvLyBzcGVjaWZpZWQgaXMgbGVzcyB0aGFuIHRoZSBudW1iZXIgb2YgZGlnaXRzIG5lY2Vzc2FyeSB0byByZXByZXNlbnQgdGhlIGludGVnZXJcbiAgICAgICAgICAgICAgICAvLyBwYXJ0IG9mIHRoZSB2YWx1ZSBpbiBmaXhlZC1wb2ludCBub3RhdGlvbi5cblxuICAgICAgICAgICAgICAgIC8vIEV4cG9uZW50aWFsIG5vdGF0aW9uLlxuICAgICAgICAgICAgICAgIGlmICggY2FsbGVyID09IDE5IHx8IGNhbGxlciA9PSAyNCAmJiAoIGkgPD0gZSB8fCBlIDw9IFRPX0VYUF9ORUcgKSApIHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBBcHBlbmQgemVyb3M/XG4gICAgICAgICAgICAgICAgICAgIGZvciAoIDsgbGVuIDwgaTsgc3RyICs9ICcwJywgbGVuKysgKTtcbiAgICAgICAgICAgICAgICAgICAgc3RyID0gdG9FeHBvbmVudGlhbCggc3RyLCBlICk7XG5cbiAgICAgICAgICAgICAgICAvLyBGaXhlZC1wb2ludCBub3RhdGlvbi5cbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBpIC09IG5lO1xuICAgICAgICAgICAgICAgICAgICBzdHIgPSB0b0ZpeGVkUG9pbnQoIHN0ciwgZSApO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIEFwcGVuZCB6ZXJvcz9cbiAgICAgICAgICAgICAgICAgICAgaWYgKCBlICsgMSA+IGxlbiApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICggLS1pID4gMCApIGZvciAoIHN0ciArPSAnLic7IGktLTsgc3RyICs9ICcwJyApO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgaSArPSBlIC0gbGVuO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCBpID4gMCApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoIGUgKyAxID09IGxlbiApIHN0ciArPSAnLic7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9yICggOyBpLS07IHN0ciArPSAnMCcgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIG4ucyA8IDAgJiYgYzAgPyAnLScgKyBzdHIgOiBzdHI7XG4gICAgICAgIH1cblxuXG4gICAgICAgIC8vIEhhbmRsZSBCaWdOdW1iZXIubWF4IGFuZCBCaWdOdW1iZXIubWluLlxuICAgICAgICBmdW5jdGlvbiBtYXhPck1pbiggYXJncywgbWV0aG9kICkge1xuICAgICAgICAgICAgdmFyIG0sIG4sXG4gICAgICAgICAgICAgICAgaSA9IDA7XG5cbiAgICAgICAgICAgIGlmICggaXNBcnJheSggYXJnc1swXSApICkgYXJncyA9IGFyZ3NbMF07XG4gICAgICAgICAgICBtID0gbmV3IEJpZ051bWJlciggYXJnc1swXSApO1xuXG4gICAgICAgICAgICBmb3IgKCA7ICsraSA8IGFyZ3MubGVuZ3RoOyApIHtcbiAgICAgICAgICAgICAgICBuID0gbmV3IEJpZ051bWJlciggYXJnc1tpXSApO1xuXG4gICAgICAgICAgICAgICAgLy8gSWYgYW55IG51bWJlciBpcyBOYU4sIHJldHVybiBOYU4uXG4gICAgICAgICAgICAgICAgaWYgKCAhbi5zICkge1xuICAgICAgICAgICAgICAgICAgICBtID0gbjtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmICggbWV0aG9kLmNhbGwoIG0sIG4gKSApIHtcbiAgICAgICAgICAgICAgICAgICAgbSA9IG47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gbTtcbiAgICAgICAgfVxuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIHRydWUgaWYgbiBpcyBhbiBpbnRlZ2VyIGluIHJhbmdlLCBvdGhlcndpc2UgdGhyb3cuXG4gICAgICAgICAqIFVzZSBmb3IgYXJndW1lbnQgdmFsaWRhdGlvbiB3aGVuIEVSUk9SUyBpcyB0cnVlLlxuICAgICAgICAgKi9cbiAgICAgICAgZnVuY3Rpb24gaW50VmFsaWRhdG9yV2l0aEVycm9ycyggbiwgbWluLCBtYXgsIGNhbGxlciwgbmFtZSApIHtcbiAgICAgICAgICAgIGlmICggbiA8IG1pbiB8fCBuID4gbWF4IHx8IG4gIT0gdHJ1bmNhdGUobikgKSB7XG4gICAgICAgICAgICAgICAgcmFpc2UoIGNhbGxlciwgKCBuYW1lIHx8ICdkZWNpbWFsIHBsYWNlcycgKSArXG4gICAgICAgICAgICAgICAgICAoIG4gPCBtaW4gfHwgbiA+IG1heCA/ICcgb3V0IG9mIHJhbmdlJyA6ICcgbm90IGFuIGludGVnZXInICksIG4gKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFN0cmlwIHRyYWlsaW5nIHplcm9zLCBjYWxjdWxhdGUgYmFzZSAxMCBleHBvbmVudCBhbmQgY2hlY2sgYWdhaW5zdCBNSU5fRVhQIGFuZCBNQVhfRVhQLlxuICAgICAgICAgKiBDYWxsZWQgYnkgbWludXMsIHBsdXMgYW5kIHRpbWVzLlxuICAgICAgICAgKi9cbiAgICAgICAgZnVuY3Rpb24gbm9ybWFsaXNlKCBuLCBjLCBlICkge1xuICAgICAgICAgICAgdmFyIGkgPSAxLFxuICAgICAgICAgICAgICAgIGogPSBjLmxlbmd0aDtcblxuICAgICAgICAgICAgIC8vIFJlbW92ZSB0cmFpbGluZyB6ZXJvcy5cbiAgICAgICAgICAgIGZvciAoIDsgIWNbLS1qXTsgYy5wb3AoKSApO1xuXG4gICAgICAgICAgICAvLyBDYWxjdWxhdGUgdGhlIGJhc2UgMTAgZXhwb25lbnQuIEZpcnN0IGdldCB0aGUgbnVtYmVyIG9mIGRpZ2l0cyBvZiBjWzBdLlxuICAgICAgICAgICAgZm9yICggaiA9IGNbMF07IGogPj0gMTA7IGogLz0gMTAsIGkrKyApO1xuXG4gICAgICAgICAgICAvLyBPdmVyZmxvdz9cbiAgICAgICAgICAgIGlmICggKCBlID0gaSArIGUgKiBMT0dfQkFTRSAtIDEgKSA+IE1BWF9FWFAgKSB7XG5cbiAgICAgICAgICAgICAgICAvLyBJbmZpbml0eS5cbiAgICAgICAgICAgICAgICBuLmMgPSBuLmUgPSBudWxsO1xuXG4gICAgICAgICAgICAvLyBVbmRlcmZsb3c/XG4gICAgICAgICAgICB9IGVsc2UgaWYgKCBlIDwgTUlOX0VYUCApIHtcblxuICAgICAgICAgICAgICAgIC8vIFplcm8uXG4gICAgICAgICAgICAgICAgbi5jID0gWyBuLmUgPSAwIF07XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIG4uZSA9IGU7XG4gICAgICAgICAgICAgICAgbi5jID0gYztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIG47XG4gICAgICAgIH1cblxuXG4gICAgICAgIC8vIEhhbmRsZSB2YWx1ZXMgdGhhdCBmYWlsIHRoZSB2YWxpZGl0eSB0ZXN0IGluIEJpZ051bWJlci5cbiAgICAgICAgcGFyc2VOdW1lcmljID0gKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBiYXNlUHJlZml4ID0gL14oLT8pMChbeGJvXSkvaSxcbiAgICAgICAgICAgICAgICBkb3RBZnRlciA9IC9eKFteLl0rKVxcLiQvLFxuICAgICAgICAgICAgICAgIGRvdEJlZm9yZSA9IC9eXFwuKFteLl0rKSQvLFxuICAgICAgICAgICAgICAgIGlzSW5maW5pdHlPck5hTiA9IC9eLT8oSW5maW5pdHl8TmFOKSQvLFxuICAgICAgICAgICAgICAgIHdoaXRlc3BhY2VPclBsdXMgPSAvXlxccypcXCt8Xlxccyt8XFxzKyQvZztcblxuICAgICAgICAgICAgcmV0dXJuIGZ1bmN0aW9uICggeCwgc3RyLCBudW0sIGIgKSB7XG4gICAgICAgICAgICAgICAgdmFyIGJhc2UsXG4gICAgICAgICAgICAgICAgICAgIHMgPSBudW0gPyBzdHIgOiBzdHIucmVwbGFjZSggd2hpdGVzcGFjZU9yUGx1cywgJycgKTtcblxuICAgICAgICAgICAgICAgIC8vIE5vIGV4Y2VwdGlvbiBvbiDCsUluZmluaXR5IG9yIE5hTi5cbiAgICAgICAgICAgICAgICBpZiAoIGlzSW5maW5pdHlPck5hTi50ZXN0KHMpICkge1xuICAgICAgICAgICAgICAgICAgICB4LnMgPSBpc05hTihzKSA/IG51bGwgOiBzIDwgMCA/IC0xIDogMTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBpZiAoICFudW0gKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGJhc2VQcmVmaXggPSAvXigtPykwKFt4Ym9dKSg/PVxcd1tcXHcuXSokKS9pXG4gICAgICAgICAgICAgICAgICAgICAgICBzID0gcy5yZXBsYWNlKCBiYXNlUHJlZml4LCBmdW5jdGlvbiAoIG0sIHAxLCBwMiApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYXNlID0gKCBwMiA9IHAyLnRvTG93ZXJDYXNlKCkgKSA9PSAneCcgPyAxNiA6IHAyID09ICdiJyA/IDIgOiA4O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiAhYiB8fCBiID09IGJhc2UgPyBwMSA6IG07XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGIpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYXNlID0gYjtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEUuZy4gJzEuJyB0byAnMScsICcuMScgdG8gJzAuMSdcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzID0gcy5yZXBsYWNlKCBkb3RBZnRlciwgJyQxJyApLnJlcGxhY2UoIGRvdEJlZm9yZSwgJzAuJDEnICk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICggc3RyICE9IHMgKSByZXR1cm4gbmV3IEJpZ051bWJlciggcywgYmFzZSApO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gJ25ldyBCaWdOdW1iZXIoKSBub3QgYSBudW1iZXI6IHtufSdcbiAgICAgICAgICAgICAgICAgICAgLy8gJ25ldyBCaWdOdW1iZXIoKSBub3QgYSBiYXNlIHtifSBudW1iZXI6IHtufSdcbiAgICAgICAgICAgICAgICAgICAgaWYgKEVSUk9SUykgcmFpc2UoIGlkLCAnbm90IGEnICsgKCBiID8gJyBiYXNlICcgKyBiIDogJycgKSArICcgbnVtYmVyJywgc3RyICk7XG4gICAgICAgICAgICAgICAgICAgIHgucyA9IG51bGw7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgeC5jID0geC5lID0gbnVsbDtcbiAgICAgICAgICAgICAgICBpZCA9IDA7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pKCk7XG5cblxuICAgICAgICAvLyBUaHJvdyBhIEJpZ051bWJlciBFcnJvci5cbiAgICAgICAgZnVuY3Rpb24gcmFpc2UoIGNhbGxlciwgbXNnLCB2YWwgKSB7XG4gICAgICAgICAgICB2YXIgZXJyb3IgPSBuZXcgRXJyb3IoIFtcbiAgICAgICAgICAgICAgICAnbmV3IEJpZ051bWJlcicsICAgICAvLyAwXG4gICAgICAgICAgICAgICAgJ2NtcCcsICAgICAgICAgICAgICAgLy8gMVxuICAgICAgICAgICAgICAgICdjb25maWcnLCAgICAgICAgICAgIC8vIDJcbiAgICAgICAgICAgICAgICAnZGl2JywgICAgICAgICAgICAgICAvLyAzXG4gICAgICAgICAgICAgICAgJ2RpdlRvSW50JywgICAgICAgICAgLy8gNFxuICAgICAgICAgICAgICAgICdlcScsICAgICAgICAgICAgICAgIC8vIDVcbiAgICAgICAgICAgICAgICAnZ3QnLCAgICAgICAgICAgICAgICAvLyA2XG4gICAgICAgICAgICAgICAgJ2d0ZScsICAgICAgICAgICAgICAgLy8gN1xuICAgICAgICAgICAgICAgICdsdCcsICAgICAgICAgICAgICAgIC8vIDhcbiAgICAgICAgICAgICAgICAnbHRlJywgICAgICAgICAgICAgICAvLyA5XG4gICAgICAgICAgICAgICAgJ21pbnVzJywgICAgICAgICAgICAgLy8gMTBcbiAgICAgICAgICAgICAgICAnbW9kJywgICAgICAgICAgICAgICAvLyAxMVxuICAgICAgICAgICAgICAgICdwbHVzJywgICAgICAgICAgICAgIC8vIDEyXG4gICAgICAgICAgICAgICAgJ3ByZWNpc2lvbicsICAgICAgICAgLy8gMTNcbiAgICAgICAgICAgICAgICAncmFuZG9tJywgICAgICAgICAgICAvLyAxNFxuICAgICAgICAgICAgICAgICdyb3VuZCcsICAgICAgICAgICAgIC8vIDE1XG4gICAgICAgICAgICAgICAgJ3NoaWZ0JywgICAgICAgICAgICAgLy8gMTZcbiAgICAgICAgICAgICAgICAndGltZXMnLCAgICAgICAgICAgICAvLyAxN1xuICAgICAgICAgICAgICAgICd0b0RpZ2l0cycsICAgICAgICAgIC8vIDE4XG4gICAgICAgICAgICAgICAgJ3RvRXhwb25lbnRpYWwnLCAgICAgLy8gMTlcbiAgICAgICAgICAgICAgICAndG9GaXhlZCcsICAgICAgICAgICAvLyAyMFxuICAgICAgICAgICAgICAgICd0b0Zvcm1hdCcsICAgICAgICAgIC8vIDIxXG4gICAgICAgICAgICAgICAgJ3RvRnJhY3Rpb24nLCAgICAgICAgLy8gMjJcbiAgICAgICAgICAgICAgICAncG93JywgICAgICAgICAgICAgICAvLyAyM1xuICAgICAgICAgICAgICAgICd0b1ByZWNpc2lvbicsICAgICAgIC8vIDI0XG4gICAgICAgICAgICAgICAgJ3RvU3RyaW5nJywgICAgICAgICAgLy8gMjVcbiAgICAgICAgICAgICAgICAnQmlnTnVtYmVyJyAgICAgICAgICAvLyAyNlxuICAgICAgICAgICAgXVtjYWxsZXJdICsgJygpICcgKyBtc2cgKyAnOiAnICsgdmFsICk7XG5cbiAgICAgICAgICAgIGVycm9yLm5hbWUgPSAnQmlnTnVtYmVyIEVycm9yJztcbiAgICAgICAgICAgIGlkID0gMDtcbiAgICAgICAgICAgIHRocm93IGVycm9yO1xuICAgICAgICB9XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSb3VuZCB4IHRvIHNkIHNpZ25pZmljYW50IGRpZ2l0cyB1c2luZyByb3VuZGluZyBtb2RlIHJtLiBDaGVjayBmb3Igb3Zlci91bmRlci1mbG93LlxuICAgICAgICAgKiBJZiByIGlzIHRydXRoeSwgaXQgaXMga25vd24gdGhhdCB0aGVyZSBhcmUgbW9yZSBkaWdpdHMgYWZ0ZXIgdGhlIHJvdW5kaW5nIGRpZ2l0LlxuICAgICAgICAgKi9cbiAgICAgICAgZnVuY3Rpb24gcm91bmQoIHgsIHNkLCBybSwgciApIHtcbiAgICAgICAgICAgIHZhciBkLCBpLCBqLCBrLCBuLCBuaSwgcmQsXG4gICAgICAgICAgICAgICAgeGMgPSB4LmMsXG4gICAgICAgICAgICAgICAgcG93czEwID0gUE9XU19URU47XG5cbiAgICAgICAgICAgIC8vIGlmIHggaXMgbm90IEluZmluaXR5IG9yIE5hTi4uLlxuICAgICAgICAgICAgaWYgKHhjKSB7XG5cbiAgICAgICAgICAgICAgICAvLyByZCBpcyB0aGUgcm91bmRpbmcgZGlnaXQsIGkuZS4gdGhlIGRpZ2l0IGFmdGVyIHRoZSBkaWdpdCB0aGF0IG1heSBiZSByb3VuZGVkIHVwLlxuICAgICAgICAgICAgICAgIC8vIG4gaXMgYSBiYXNlIDFlMTQgbnVtYmVyLCB0aGUgdmFsdWUgb2YgdGhlIGVsZW1lbnQgb2YgYXJyYXkgeC5jIGNvbnRhaW5pbmcgcmQuXG4gICAgICAgICAgICAgICAgLy8gbmkgaXMgdGhlIGluZGV4IG9mIG4gd2l0aGluIHguYy5cbiAgICAgICAgICAgICAgICAvLyBkIGlzIHRoZSBudW1iZXIgb2YgZGlnaXRzIG9mIG4uXG4gICAgICAgICAgICAgICAgLy8gaSBpcyB0aGUgaW5kZXggb2YgcmQgd2l0aGluIG4gaW5jbHVkaW5nIGxlYWRpbmcgemVyb3MuXG4gICAgICAgICAgICAgICAgLy8gaiBpcyB0aGUgYWN0dWFsIGluZGV4IG9mIHJkIHdpdGhpbiBuIChpZiA8IDAsIHJkIGlzIGEgbGVhZGluZyB6ZXJvKS5cbiAgICAgICAgICAgICAgICBvdXQ6IHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBHZXQgdGhlIG51bWJlciBvZiBkaWdpdHMgb2YgdGhlIGZpcnN0IGVsZW1lbnQgb2YgeGMuXG4gICAgICAgICAgICAgICAgICAgIGZvciAoIGQgPSAxLCBrID0geGNbMF07IGsgPj0gMTA7IGsgLz0gMTAsIGQrKyApO1xuICAgICAgICAgICAgICAgICAgICBpID0gc2QgLSBkO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIElmIHRoZSByb3VuZGluZyBkaWdpdCBpcyBpbiB0aGUgZmlyc3QgZWxlbWVudCBvZiB4Yy4uLlxuICAgICAgICAgICAgICAgICAgICBpZiAoIGkgPCAwICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaSArPSBMT0dfQkFTRTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGogPSBzZDtcbiAgICAgICAgICAgICAgICAgICAgICAgIG4gPSB4Y1sgbmkgPSAwIF07XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIEdldCB0aGUgcm91bmRpbmcgZGlnaXQgYXQgaW5kZXggaiBvZiBuLlxuICAgICAgICAgICAgICAgICAgICAgICAgcmQgPSBuIC8gcG93czEwWyBkIC0gaiAtIDEgXSAlIDEwIHwgMDtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5pID0gbWF0aGNlaWwoICggaSArIDEgKSAvIExPR19CQVNFICk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICggbmkgPj0geGMubGVuZ3RoICkge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHIpIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBOZWVkZWQgYnkgc3FydC5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9yICggOyB4Yy5sZW5ndGggPD0gbmk7IHhjLnB1c2goMCkgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbiA9IHJkID0gMDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZCA9IDE7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGkgJT0gTE9HX0JBU0U7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGogPSBpIC0gTE9HX0JBU0UgKyAxO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrIG91dDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4gPSBrID0geGNbbmldO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gR2V0IHRoZSBudW1iZXIgb2YgZGlnaXRzIG9mIG4uXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9yICggZCA9IDE7IGsgPj0gMTA7IGsgLz0gMTAsIGQrKyApO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gR2V0IHRoZSBpbmRleCBvZiByZCB3aXRoaW4gbi5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpICU9IExPR19CQVNFO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gR2V0IHRoZSBpbmRleCBvZiByZCB3aXRoaW4gbiwgYWRqdXN0ZWQgZm9yIGxlYWRpbmcgemVyb3MuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gVGhlIG51bWJlciBvZiBsZWFkaW5nIHplcm9zIG9mIG4gaXMgZ2l2ZW4gYnkgTE9HX0JBU0UgLSBkLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGogPSBpIC0gTE9HX0JBU0UgKyBkO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gR2V0IHRoZSByb3VuZGluZyBkaWdpdCBhdCBpbmRleCBqIG9mIG4uXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmQgPSBqIDwgMCA/IDAgOiBuIC8gcG93czEwWyBkIC0gaiAtIDEgXSAlIDEwIHwgMDtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIHIgPSByIHx8IHNkIDwgMCB8fFxuXG4gICAgICAgICAgICAgICAgICAgIC8vIEFyZSB0aGVyZSBhbnkgbm9uLXplcm8gZGlnaXRzIGFmdGVyIHRoZSByb3VuZGluZyBkaWdpdD9cbiAgICAgICAgICAgICAgICAgICAgLy8gVGhlIGV4cHJlc3Npb24gIG4gJSBwb3dzMTBbIGQgLSBqIC0gMSBdICByZXR1cm5zIGFsbCBkaWdpdHMgb2YgbiB0byB0aGUgcmlnaHRcbiAgICAgICAgICAgICAgICAgICAgLy8gb2YgdGhlIGRpZ2l0IGF0IGosIGUuZy4gaWYgbiBpcyA5MDg3MTQgYW5kIGogaXMgMiwgdGhlIGV4cHJlc3Npb24gZ2l2ZXMgNzE0LlxuICAgICAgICAgICAgICAgICAgICAgIHhjW25pICsgMV0gIT0gbnVsbCB8fCAoIGogPCAwID8gbiA6IG4gJSBwb3dzMTBbIGQgLSBqIC0gMSBdICk7XG5cbiAgICAgICAgICAgICAgICAgICAgciA9IHJtIDwgNFxuICAgICAgICAgICAgICAgICAgICAgID8gKCByZCB8fCByICkgJiYgKCBybSA9PSAwIHx8IHJtID09ICggeC5zIDwgMCA/IDMgOiAyICkgKVxuICAgICAgICAgICAgICAgICAgICAgIDogcmQgPiA1IHx8IHJkID09IDUgJiYgKCBybSA9PSA0IHx8IHIgfHwgcm0gPT0gNiAmJlxuXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBDaGVjayB3aGV0aGVyIHRoZSBkaWdpdCB0byB0aGUgbGVmdCBvZiB0aGUgcm91bmRpbmcgZGlnaXQgaXMgb2RkLlxuICAgICAgICAgICAgICAgICAgICAgICAgKCAoIGkgPiAwID8gaiA+IDAgPyBuIC8gcG93czEwWyBkIC0gaiBdIDogMCA6IHhjW25pIC0gMV0gKSAlIDEwICkgJiAxIHx8XG4gICAgICAgICAgICAgICAgICAgICAgICAgIHJtID09ICggeC5zIDwgMCA/IDggOiA3ICkgKTtcblxuICAgICAgICAgICAgICAgICAgICBpZiAoIHNkIDwgMSB8fCAheGNbMF0gKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB4Yy5sZW5ndGggPSAwO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAocikge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQ29udmVydCBzZCB0byBkZWNpbWFsIHBsYWNlcy5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZCAtPSB4LmUgKyAxO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gMSwgMC4xLCAwLjAxLCAwLjAwMSwgMC4wMDAxIGV0Yy5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4Y1swXSA9IHBvd3MxMFsgc2QgJSBMT0dfQkFTRSBdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHguZSA9IC1zZCB8fCAwO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFplcm8uXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgeGNbMF0gPSB4LmUgPSAwO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4geDtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIFJlbW92ZSBleGNlc3MgZGlnaXRzLlxuICAgICAgICAgICAgICAgICAgICBpZiAoIGkgPT0gMCApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHhjLmxlbmd0aCA9IG5pO1xuICAgICAgICAgICAgICAgICAgICAgICAgayA9IDE7XG4gICAgICAgICAgICAgICAgICAgICAgICBuaS0tO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgeGMubGVuZ3RoID0gbmkgKyAxO1xuICAgICAgICAgICAgICAgICAgICAgICAgayA9IHBvd3MxMFsgTE9HX0JBU0UgLSBpIF07XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIEUuZy4gNTY3MDAgYmVjb21lcyA1NjAwMCBpZiA3IGlzIHRoZSByb3VuZGluZyBkaWdpdC5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGogPiAwIG1lYW5zIGkgPiBudW1iZXIgb2YgbGVhZGluZyB6ZXJvcyBvZiBuLlxuICAgICAgICAgICAgICAgICAgICAgICAgeGNbbmldID0gaiA+IDAgPyBtYXRoZmxvb3IoIG4gLyBwb3dzMTBbIGQgLSBqIF0gJSBwb3dzMTBbal0gKSAqIGsgOiAwO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gUm91bmQgdXA/XG4gICAgICAgICAgICAgICAgICAgIGlmIChyKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoIDsgOyApIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIHRoZSBkaWdpdCB0byBiZSByb3VuZGVkIHVwIGlzIGluIHRoZSBmaXJzdCBlbGVtZW50IG9mIHhjLi4uXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCBuaSA9PSAwICkge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGkgd2lsbCBiZSB0aGUgbGVuZ3RoIG9mIHhjWzBdIGJlZm9yZSBrIGlzIGFkZGVkLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3IgKCBpID0gMSwgaiA9IHhjWzBdOyBqID49IDEwOyBqIC89IDEwLCBpKysgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaiA9IHhjWzBdICs9IGs7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoIGsgPSAxOyBqID49IDEwOyBqIC89IDEwLCBrKysgKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBpZiBpICE9IGsgdGhlIGxlbmd0aCBoYXMgaW5jcmVhc2VkLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoIGkgIT0gayApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHguZSsrO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCB4Y1swXSA9PSBCQVNFICkgeGNbMF0gPSAxO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeGNbbmldICs9IGs7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICggeGNbbmldICE9IEJBU0UgKSBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeGNbbmktLV0gPSAwO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrID0gMTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAvLyBSZW1vdmUgdHJhaWxpbmcgemVyb3MuXG4gICAgICAgICAgICAgICAgICAgIGZvciAoIGkgPSB4Yy5sZW5ndGg7IHhjWy0taV0gPT09IDA7IHhjLnBvcCgpICk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gT3ZlcmZsb3c/IEluZmluaXR5LlxuICAgICAgICAgICAgICAgIGlmICggeC5lID4gTUFYX0VYUCApIHtcbiAgICAgICAgICAgICAgICAgICAgeC5jID0geC5lID0gbnVsbDtcblxuICAgICAgICAgICAgICAgIC8vIFVuZGVyZmxvdz8gWmVyby5cbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCB4LmUgPCBNSU5fRVhQICkge1xuICAgICAgICAgICAgICAgICAgICB4LmMgPSBbIHguZSA9IDAgXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiB4O1xuICAgICAgICB9XG5cblxuICAgICAgICAvLyBQUk9UT1RZUEUvSU5TVEFOQ0UgTUVUSE9EU1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIGEgbmV3IEJpZ051bWJlciB3aG9zZSB2YWx1ZSBpcyB0aGUgYWJzb2x1dGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIuXG4gICAgICAgICAqL1xuICAgICAgICBQLmFic29sdXRlVmFsdWUgPSBQLmFicyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciB4ID0gbmV3IEJpZ051bWJlcih0aGlzKTtcbiAgICAgICAgICAgIGlmICggeC5zIDwgMCApIHgucyA9IDE7XG4gICAgICAgICAgICByZXR1cm4geDtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiBhIG5ldyBCaWdOdW1iZXIgd2hvc2UgdmFsdWUgaXMgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIHJvdW5kZWQgdG8gYSB3aG9sZVxuICAgICAgICAgKiBudW1iZXIgaW4gdGhlIGRpcmVjdGlvbiBvZiBJbmZpbml0eS5cbiAgICAgICAgICovXG4gICAgICAgIFAuY2VpbCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiByb3VuZCggbmV3IEJpZ051bWJlcih0aGlzKSwgdGhpcy5lICsgMSwgMiApO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuXG4gICAgICAgICAqIDEgaWYgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIGlzIGdyZWF0ZXIgdGhhbiB0aGUgdmFsdWUgb2YgQmlnTnVtYmVyKHksIGIpLFxuICAgICAgICAgKiAtMSBpZiB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgaXMgbGVzcyB0aGFuIHRoZSB2YWx1ZSBvZiBCaWdOdW1iZXIoeSwgYiksXG4gICAgICAgICAqIDAgaWYgdGhleSBoYXZlIHRoZSBzYW1lIHZhbHVlLFxuICAgICAgICAgKiBvciBudWxsIGlmIHRoZSB2YWx1ZSBvZiBlaXRoZXIgaXMgTmFOLlxuICAgICAgICAgKi9cbiAgICAgICAgUC5jb21wYXJlZFRvID0gUC5jbXAgPSBmdW5jdGlvbiAoIHksIGIgKSB7XG4gICAgICAgICAgICBpZCA9IDE7XG4gICAgICAgICAgICByZXR1cm4gY29tcGFyZSggdGhpcywgbmV3IEJpZ051bWJlciggeSwgYiApICk7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gdGhlIG51bWJlciBvZiBkZWNpbWFsIHBsYWNlcyBvZiB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIsIG9yIG51bGwgaWYgdGhlIHZhbHVlXG4gICAgICAgICAqIG9mIHRoaXMgQmlnTnVtYmVyIGlzIMKxSW5maW5pdHkgb3IgTmFOLlxuICAgICAgICAgKi9cbiAgICAgICAgUC5kZWNpbWFsUGxhY2VzID0gUC5kcCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBuLCB2LFxuICAgICAgICAgICAgICAgIGMgPSB0aGlzLmM7XG5cbiAgICAgICAgICAgIGlmICggIWMgKSByZXR1cm4gbnVsbDtcbiAgICAgICAgICAgIG4gPSAoICggdiA9IGMubGVuZ3RoIC0gMSApIC0gYml0Rmxvb3IoIHRoaXMuZSAvIExPR19CQVNFICkgKSAqIExPR19CQVNFO1xuXG4gICAgICAgICAgICAvLyBTdWJ0cmFjdCB0aGUgbnVtYmVyIG9mIHRyYWlsaW5nIHplcm9zIG9mIHRoZSBsYXN0IG51bWJlci5cbiAgICAgICAgICAgIGlmICggdiA9IGNbdl0gKSBmb3IgKCA7IHYgJSAxMCA9PSAwOyB2IC89IDEwLCBuLS0gKTtcbiAgICAgICAgICAgIGlmICggbiA8IDAgKSBuID0gMDtcblxuICAgICAgICAgICAgcmV0dXJuIG47XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiAgbiAvIDAgPSBJXG4gICAgICAgICAqICBuIC8gTiA9IE5cbiAgICAgICAgICogIG4gLyBJID0gMFxuICAgICAgICAgKiAgMCAvIG4gPSAwXG4gICAgICAgICAqICAwIC8gMCA9IE5cbiAgICAgICAgICogIDAgLyBOID0gTlxuICAgICAgICAgKiAgMCAvIEkgPSAwXG4gICAgICAgICAqICBOIC8gbiA9IE5cbiAgICAgICAgICogIE4gLyAwID0gTlxuICAgICAgICAgKiAgTiAvIE4gPSBOXG4gICAgICAgICAqICBOIC8gSSA9IE5cbiAgICAgICAgICogIEkgLyBuID0gSVxuICAgICAgICAgKiAgSSAvIDAgPSBJXG4gICAgICAgICAqICBJIC8gTiA9IE5cbiAgICAgICAgICogIEkgLyBJID0gTlxuICAgICAgICAgKlxuICAgICAgICAgKiBSZXR1cm4gYSBuZXcgQmlnTnVtYmVyIHdob3NlIHZhbHVlIGlzIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciBkaXZpZGVkIGJ5IHRoZSB2YWx1ZSBvZlxuICAgICAgICAgKiBCaWdOdW1iZXIoeSwgYiksIHJvdW5kZWQgYWNjb3JkaW5nIHRvIERFQ0lNQUxfUExBQ0VTIGFuZCBST1VORElOR19NT0RFLlxuICAgICAgICAgKi9cbiAgICAgICAgUC5kaXZpZGVkQnkgPSBQLmRpdiA9IGZ1bmN0aW9uICggeSwgYiApIHtcbiAgICAgICAgICAgIGlkID0gMztcbiAgICAgICAgICAgIHJldHVybiBkaXYoIHRoaXMsIG5ldyBCaWdOdW1iZXIoIHksIGIgKSwgREVDSU1BTF9QTEFDRVMsIFJPVU5ESU5HX01PREUgKTtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiBhIG5ldyBCaWdOdW1iZXIgd2hvc2UgdmFsdWUgaXMgdGhlIGludGVnZXIgcGFydCBvZiBkaXZpZGluZyB0aGUgdmFsdWUgb2YgdGhpc1xuICAgICAgICAgKiBCaWdOdW1iZXIgYnkgdGhlIHZhbHVlIG9mIEJpZ051bWJlcih5LCBiKS5cbiAgICAgICAgICovXG4gICAgICAgIFAuZGl2aWRlZFRvSW50ZWdlckJ5ID0gUC5kaXZUb0ludCA9IGZ1bmN0aW9uICggeSwgYiApIHtcbiAgICAgICAgICAgIGlkID0gNDtcbiAgICAgICAgICAgIHJldHVybiBkaXYoIHRoaXMsIG5ldyBCaWdOdW1iZXIoIHksIGIgKSwgMCwgMSApO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIHRydWUgaWYgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIGlzIGVxdWFsIHRvIHRoZSB2YWx1ZSBvZiBCaWdOdW1iZXIoeSwgYiksXG4gICAgICAgICAqIG90aGVyd2lzZSByZXR1cm5zIGZhbHNlLlxuICAgICAgICAgKi9cbiAgICAgICAgUC5lcXVhbHMgPSBQLmVxID0gZnVuY3Rpb24gKCB5LCBiICkge1xuICAgICAgICAgICAgaWQgPSA1O1xuICAgICAgICAgICAgcmV0dXJuIGNvbXBhcmUoIHRoaXMsIG5ldyBCaWdOdW1iZXIoIHksIGIgKSApID09PSAwO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIGEgbmV3IEJpZ051bWJlciB3aG9zZSB2YWx1ZSBpcyB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgcm91bmRlZCB0byBhIHdob2xlXG4gICAgICAgICAqIG51bWJlciBpbiB0aGUgZGlyZWN0aW9uIG9mIC1JbmZpbml0eS5cbiAgICAgICAgICovXG4gICAgICAgIFAuZmxvb3IgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICByZXR1cm4gcm91bmQoIG5ldyBCaWdOdW1iZXIodGhpcyksIHRoaXMuZSArIDEsIDMgKTtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiB0cnVlIGlmIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciBpcyBncmVhdGVyIHRoYW4gdGhlIHZhbHVlIG9mIEJpZ051bWJlcih5LCBiKSxcbiAgICAgICAgICogb3RoZXJ3aXNlIHJldHVybnMgZmFsc2UuXG4gICAgICAgICAqL1xuICAgICAgICBQLmdyZWF0ZXJUaGFuID0gUC5ndCA9IGZ1bmN0aW9uICggeSwgYiApIHtcbiAgICAgICAgICAgIGlkID0gNjtcbiAgICAgICAgICAgIHJldHVybiBjb21wYXJlKCB0aGlzLCBuZXcgQmlnTnVtYmVyKCB5LCBiICkgKSA+IDA7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gdHJ1ZSBpZiB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIHRoZSB2YWx1ZSBvZlxuICAgICAgICAgKiBCaWdOdW1iZXIoeSwgYiksIG90aGVyd2lzZSByZXR1cm5zIGZhbHNlLlxuICAgICAgICAgKi9cbiAgICAgICAgUC5ncmVhdGVyVGhhbk9yRXF1YWxUbyA9IFAuZ3RlID0gZnVuY3Rpb24gKCB5LCBiICkge1xuICAgICAgICAgICAgaWQgPSA3O1xuICAgICAgICAgICAgcmV0dXJuICggYiA9IGNvbXBhcmUoIHRoaXMsIG5ldyBCaWdOdW1iZXIoIHksIGIgKSApICkgPT09IDEgfHwgYiA9PT0gMDtcblxuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIHRydWUgaWYgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIGlzIGEgZmluaXRlIG51bWJlciwgb3RoZXJ3aXNlIHJldHVybnMgZmFsc2UuXG4gICAgICAgICAqL1xuICAgICAgICBQLmlzRmluaXRlID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgcmV0dXJuICEhdGhpcy5jO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIHRydWUgaWYgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIGlzIGFuIGludGVnZXIsIG90aGVyd2lzZSByZXR1cm4gZmFsc2UuXG4gICAgICAgICAqL1xuICAgICAgICBQLmlzSW50ZWdlciA9IFAuaXNJbnQgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICByZXR1cm4gISF0aGlzLmMgJiYgYml0Rmxvb3IoIHRoaXMuZSAvIExPR19CQVNFICkgPiB0aGlzLmMubGVuZ3RoIC0gMjtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiB0cnVlIGlmIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciBpcyBOYU4sIG90aGVyd2lzZSByZXR1cm5zIGZhbHNlLlxuICAgICAgICAgKi9cbiAgICAgICAgUC5pc05hTiA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiAhdGhpcy5zO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIHRydWUgaWYgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIGlzIG5lZ2F0aXZlLCBvdGhlcndpc2UgcmV0dXJucyBmYWxzZS5cbiAgICAgICAgICovXG4gICAgICAgIFAuaXNOZWdhdGl2ZSA9IFAuaXNOZWcgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5zIDwgMDtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiB0cnVlIGlmIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciBpcyAwIG9yIC0wLCBvdGhlcndpc2UgcmV0dXJucyBmYWxzZS5cbiAgICAgICAgICovXG4gICAgICAgIFAuaXNaZXJvID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgcmV0dXJuICEhdGhpcy5jICYmIHRoaXMuY1swXSA9PSAwO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIHRydWUgaWYgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIGlzIGxlc3MgdGhhbiB0aGUgdmFsdWUgb2YgQmlnTnVtYmVyKHksIGIpLFxuICAgICAgICAgKiBvdGhlcndpc2UgcmV0dXJucyBmYWxzZS5cbiAgICAgICAgICovXG4gICAgICAgIFAubGVzc1RoYW4gPSBQLmx0ID0gZnVuY3Rpb24gKCB5LCBiICkge1xuICAgICAgICAgICAgaWQgPSA4O1xuICAgICAgICAgICAgcmV0dXJuIGNvbXBhcmUoIHRoaXMsIG5ldyBCaWdOdW1iZXIoIHksIGIgKSApIDwgMDtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiB0cnVlIGlmIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciBpcyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gdGhlIHZhbHVlIG9mXG4gICAgICAgICAqIEJpZ051bWJlcih5LCBiKSwgb3RoZXJ3aXNlIHJldHVybnMgZmFsc2UuXG4gICAgICAgICAqL1xuICAgICAgICBQLmxlc3NUaGFuT3JFcXVhbFRvID0gUC5sdGUgPSBmdW5jdGlvbiAoIHksIGIgKSB7XG4gICAgICAgICAgICBpZCA9IDk7XG4gICAgICAgICAgICByZXR1cm4gKCBiID0gY29tcGFyZSggdGhpcywgbmV3IEJpZ051bWJlciggeSwgYiApICkgKSA9PT0gLTEgfHwgYiA9PT0gMDtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqICBuIC0gMCA9IG5cbiAgICAgICAgICogIG4gLSBOID0gTlxuICAgICAgICAgKiAgbiAtIEkgPSAtSVxuICAgICAgICAgKiAgMCAtIG4gPSAtblxuICAgICAgICAgKiAgMCAtIDAgPSAwXG4gICAgICAgICAqICAwIC0gTiA9IE5cbiAgICAgICAgICogIDAgLSBJID0gLUlcbiAgICAgICAgICogIE4gLSBuID0gTlxuICAgICAgICAgKiAgTiAtIDAgPSBOXG4gICAgICAgICAqICBOIC0gTiA9IE5cbiAgICAgICAgICogIE4gLSBJID0gTlxuICAgICAgICAgKiAgSSAtIG4gPSBJXG4gICAgICAgICAqICBJIC0gMCA9IElcbiAgICAgICAgICogIEkgLSBOID0gTlxuICAgICAgICAgKiAgSSAtIEkgPSBOXG4gICAgICAgICAqXG4gICAgICAgICAqIFJldHVybiBhIG5ldyBCaWdOdW1iZXIgd2hvc2UgdmFsdWUgaXMgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIG1pbnVzIHRoZSB2YWx1ZSBvZlxuICAgICAgICAgKiBCaWdOdW1iZXIoeSwgYikuXG4gICAgICAgICAqL1xuICAgICAgICBQLm1pbnVzID0gUC5zdWIgPSBmdW5jdGlvbiAoIHksIGIgKSB7XG4gICAgICAgICAgICB2YXIgaSwgaiwgdCwgeExUeSxcbiAgICAgICAgICAgICAgICB4ID0gdGhpcyxcbiAgICAgICAgICAgICAgICBhID0geC5zO1xuXG4gICAgICAgICAgICBpZCA9IDEwO1xuICAgICAgICAgICAgeSA9IG5ldyBCaWdOdW1iZXIoIHksIGIgKTtcbiAgICAgICAgICAgIGIgPSB5LnM7XG5cbiAgICAgICAgICAgIC8vIEVpdGhlciBOYU4/XG4gICAgICAgICAgICBpZiAoICFhIHx8ICFiICkgcmV0dXJuIG5ldyBCaWdOdW1iZXIoTmFOKTtcblxuICAgICAgICAgICAgLy8gU2lnbnMgZGlmZmVyP1xuICAgICAgICAgICAgaWYgKCBhICE9IGIgKSB7XG4gICAgICAgICAgICAgICAgeS5zID0gLWI7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHgucGx1cyh5KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHhlID0geC5lIC8gTE9HX0JBU0UsXG4gICAgICAgICAgICAgICAgeWUgPSB5LmUgLyBMT0dfQkFTRSxcbiAgICAgICAgICAgICAgICB4YyA9IHguYyxcbiAgICAgICAgICAgICAgICB5YyA9IHkuYztcblxuICAgICAgICAgICAgaWYgKCAheGUgfHwgIXllICkge1xuXG4gICAgICAgICAgICAgICAgLy8gRWl0aGVyIEluZmluaXR5P1xuICAgICAgICAgICAgICAgIGlmICggIXhjIHx8ICF5YyApIHJldHVybiB4YyA/ICggeS5zID0gLWIsIHkgKSA6IG5ldyBCaWdOdW1iZXIoIHljID8geCA6IE5hTiApO1xuXG4gICAgICAgICAgICAgICAgLy8gRWl0aGVyIHplcm8/XG4gICAgICAgICAgICAgICAgaWYgKCAheGNbMF0gfHwgIXljWzBdICkge1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIFJldHVybiB5IGlmIHkgaXMgbm9uLXplcm8sIHggaWYgeCBpcyBub24temVybywgb3IgemVybyBpZiBib3RoIGFyZSB6ZXJvLlxuICAgICAgICAgICAgICAgICAgICByZXR1cm4geWNbMF0gPyAoIHkucyA9IC1iLCB5ICkgOiBuZXcgQmlnTnVtYmVyKCB4Y1swXSA/IHggOlxuXG4gICAgICAgICAgICAgICAgICAgICAgLy8gSUVFRSA3NTQgKDIwMDgpIDYuMzogbiAtIG4gPSAtMCB3aGVuIHJvdW5kaW5nIHRvIC1JbmZpbml0eVxuICAgICAgICAgICAgICAgICAgICAgIFJPVU5ESU5HX01PREUgPT0gMyA/IC0wIDogMCApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgeGUgPSBiaXRGbG9vcih4ZSk7XG4gICAgICAgICAgICB5ZSA9IGJpdEZsb29yKHllKTtcbiAgICAgICAgICAgIHhjID0geGMuc2xpY2UoKTtcblxuICAgICAgICAgICAgLy8gRGV0ZXJtaW5lIHdoaWNoIGlzIHRoZSBiaWdnZXIgbnVtYmVyLlxuICAgICAgICAgICAgaWYgKCBhID0geGUgLSB5ZSApIHtcblxuICAgICAgICAgICAgICAgIGlmICggeExUeSA9IGEgPCAwICkge1xuICAgICAgICAgICAgICAgICAgICBhID0gLWE7XG4gICAgICAgICAgICAgICAgICAgIHQgPSB4YztcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICB5ZSA9IHhlO1xuICAgICAgICAgICAgICAgICAgICB0ID0geWM7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdC5yZXZlcnNlKCk7XG5cbiAgICAgICAgICAgICAgICAvLyBQcmVwZW5kIHplcm9zIHRvIGVxdWFsaXNlIGV4cG9uZW50cy5cbiAgICAgICAgICAgICAgICBmb3IgKCBiID0gYTsgYi0tOyB0LnB1c2goMCkgKTtcbiAgICAgICAgICAgICAgICB0LnJldmVyc2UoKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgICAgICAgICAvLyBFeHBvbmVudHMgZXF1YWwuIENoZWNrIGRpZ2l0IGJ5IGRpZ2l0LlxuICAgICAgICAgICAgICAgIGogPSAoIHhMVHkgPSAoIGEgPSB4Yy5sZW5ndGggKSA8ICggYiA9IHljLmxlbmd0aCApICkgPyBhIDogYjtcblxuICAgICAgICAgICAgICAgIGZvciAoIGEgPSBiID0gMDsgYiA8IGo7IGIrKyApIHtcblxuICAgICAgICAgICAgICAgICAgICBpZiAoIHhjW2JdICE9IHljW2JdICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgeExUeSA9IHhjW2JdIDwgeWNbYl07XG4gICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8geCA8IHk/IFBvaW50IHhjIHRvIHRoZSBhcnJheSBvZiB0aGUgYmlnZ2VyIG51bWJlci5cbiAgICAgICAgICAgIGlmICh4TFR5KSB0ID0geGMsIHhjID0geWMsIHljID0gdCwgeS5zID0gLXkucztcblxuICAgICAgICAgICAgYiA9ICggaiA9IHljLmxlbmd0aCApIC0gKCBpID0geGMubGVuZ3RoICk7XG5cbiAgICAgICAgICAgIC8vIEFwcGVuZCB6ZXJvcyB0byB4YyBpZiBzaG9ydGVyLlxuICAgICAgICAgICAgLy8gTm8gbmVlZCB0byBhZGQgemVyb3MgdG8geWMgaWYgc2hvcnRlciBhcyBzdWJ0cmFjdCBvbmx5IG5lZWRzIHRvIHN0YXJ0IGF0IHljLmxlbmd0aC5cbiAgICAgICAgICAgIGlmICggYiA+IDAgKSBmb3IgKCA7IGItLTsgeGNbaSsrXSA9IDAgKTtcbiAgICAgICAgICAgIGIgPSBCQVNFIC0gMTtcblxuICAgICAgICAgICAgLy8gU3VidHJhY3QgeWMgZnJvbSB4Yy5cbiAgICAgICAgICAgIGZvciAoIDsgaiA+IGE7ICkge1xuXG4gICAgICAgICAgICAgICAgaWYgKCB4Y1stLWpdIDwgeWNbal0gKSB7XG4gICAgICAgICAgICAgICAgICAgIGZvciAoIGkgPSBqOyBpICYmICF4Y1stLWldOyB4Y1tpXSA9IGIgKTtcbiAgICAgICAgICAgICAgICAgICAgLS14Y1tpXTtcbiAgICAgICAgICAgICAgICAgICAgeGNbal0gKz0gQkFTRTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB4Y1tqXSAtPSB5Y1tqXTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUmVtb3ZlIGxlYWRpbmcgemVyb3MgYW5kIGFkanVzdCBleHBvbmVudCBhY2NvcmRpbmdseS5cbiAgICAgICAgICAgIGZvciAoIDsgeGNbMF0gPT0gMDsgeGMuc2hpZnQoKSwgLS15ZSApO1xuXG4gICAgICAgICAgICAvLyBaZXJvP1xuICAgICAgICAgICAgaWYgKCAheGNbMF0gKSB7XG5cbiAgICAgICAgICAgICAgICAvLyBGb2xsb3dpbmcgSUVFRSA3NTQgKDIwMDgpIDYuMyxcbiAgICAgICAgICAgICAgICAvLyBuIC0gbiA9ICswICBidXQgIG4gLSBuID0gLTAgIHdoZW4gcm91bmRpbmcgdG93YXJkcyAtSW5maW5pdHkuXG4gICAgICAgICAgICAgICAgeS5zID0gUk9VTkRJTkdfTU9ERSA9PSAzID8gLTEgOiAxO1xuICAgICAgICAgICAgICAgIHkuYyA9IFsgeS5lID0gMCBdO1xuICAgICAgICAgICAgICAgIHJldHVybiB5O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBObyBuZWVkIHRvIGNoZWNrIGZvciBJbmZpbml0eSBhcyAreCAtICt5ICE9IEluZmluaXR5ICYmIC14IC0gLXkgIT0gSW5maW5pdHlcbiAgICAgICAgICAgIC8vIGZvciBmaW5pdGUgeCBhbmQgeS5cbiAgICAgICAgICAgIHJldHVybiBub3JtYWxpc2UoIHksIHhjLCB5ZSApO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogICBuICUgMCA9ICBOXG4gICAgICAgICAqICAgbiAlIE4gPSAgTlxuICAgICAgICAgKiAgIG4gJSBJID0gIG5cbiAgICAgICAgICogICAwICUgbiA9ICAwXG4gICAgICAgICAqICAtMCAlIG4gPSAtMFxuICAgICAgICAgKiAgIDAgJSAwID0gIE5cbiAgICAgICAgICogICAwICUgTiA9ICBOXG4gICAgICAgICAqICAgMCAlIEkgPSAgMFxuICAgICAgICAgKiAgIE4gJSBuID0gIE5cbiAgICAgICAgICogICBOICUgMCA9ICBOXG4gICAgICAgICAqICAgTiAlIE4gPSAgTlxuICAgICAgICAgKiAgIE4gJSBJID0gIE5cbiAgICAgICAgICogICBJICUgbiA9ICBOXG4gICAgICAgICAqICAgSSAlIDAgPSAgTlxuICAgICAgICAgKiAgIEkgJSBOID0gIE5cbiAgICAgICAgICogICBJICUgSSA9ICBOXG4gICAgICAgICAqXG4gICAgICAgICAqIFJldHVybiBhIG5ldyBCaWdOdW1iZXIgd2hvc2UgdmFsdWUgaXMgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIG1vZHVsbyB0aGUgdmFsdWUgb2ZcbiAgICAgICAgICogQmlnTnVtYmVyKHksIGIpLiBUaGUgcmVzdWx0IGRlcGVuZHMgb24gdGhlIHZhbHVlIG9mIE1PRFVMT19NT0RFLlxuICAgICAgICAgKi9cbiAgICAgICAgUC5tb2R1bG8gPSBQLm1vZCA9IGZ1bmN0aW9uICggeSwgYiApIHtcbiAgICAgICAgICAgIHZhciBxLCBzLFxuICAgICAgICAgICAgICAgIHggPSB0aGlzO1xuXG4gICAgICAgICAgICBpZCA9IDExO1xuICAgICAgICAgICAgeSA9IG5ldyBCaWdOdW1iZXIoIHksIGIgKTtcblxuICAgICAgICAgICAgLy8gUmV0dXJuIE5hTiBpZiB4IGlzIEluZmluaXR5IG9yIE5hTiwgb3IgeSBpcyBOYU4gb3IgemVyby5cbiAgICAgICAgICAgIGlmICggIXguYyB8fCAheS5zIHx8IHkuYyAmJiAheS5jWzBdICkge1xuICAgICAgICAgICAgICAgIHJldHVybiBuZXcgQmlnTnVtYmVyKE5hTik7XG5cbiAgICAgICAgICAgIC8vIFJldHVybiB4IGlmIHkgaXMgSW5maW5pdHkgb3IgeCBpcyB6ZXJvLlxuICAgICAgICAgICAgfSBlbHNlIGlmICggIXkuYyB8fCB4LmMgJiYgIXguY1swXSApIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gbmV3IEJpZ051bWJlcih4KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKCBNT0RVTE9fTU9ERSA9PSA5ICkge1xuXG4gICAgICAgICAgICAgICAgLy8gRXVjbGlkaWFuIGRpdmlzaW9uOiBxID0gc2lnbih5KSAqIGZsb29yKHggLyBhYnMoeSkpXG4gICAgICAgICAgICAgICAgLy8gciA9IHggLSBxeSAgICB3aGVyZSAgMCA8PSByIDwgYWJzKHkpXG4gICAgICAgICAgICAgICAgcyA9IHkucztcbiAgICAgICAgICAgICAgICB5LnMgPSAxO1xuICAgICAgICAgICAgICAgIHEgPSBkaXYoIHgsIHksIDAsIDMgKTtcbiAgICAgICAgICAgICAgICB5LnMgPSBzO1xuICAgICAgICAgICAgICAgIHEucyAqPSBzO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBxID0gZGl2KCB4LCB5LCAwLCBNT0RVTE9fTU9ERSApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4geC5taW51cyggcS50aW1lcyh5KSApO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIGEgbmV3IEJpZ051bWJlciB3aG9zZSB2YWx1ZSBpcyB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgbmVnYXRlZCxcbiAgICAgICAgICogaS5lLiBtdWx0aXBsaWVkIGJ5IC0xLlxuICAgICAgICAgKi9cbiAgICAgICAgUC5uZWdhdGVkID0gUC5uZWcgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgeCA9IG5ldyBCaWdOdW1iZXIodGhpcyk7XG4gICAgICAgICAgICB4LnMgPSAteC5zIHx8IG51bGw7XG4gICAgICAgICAgICByZXR1cm4geDtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqICBuICsgMCA9IG5cbiAgICAgICAgICogIG4gKyBOID0gTlxuICAgICAgICAgKiAgbiArIEkgPSBJXG4gICAgICAgICAqICAwICsgbiA9IG5cbiAgICAgICAgICogIDAgKyAwID0gMFxuICAgICAgICAgKiAgMCArIE4gPSBOXG4gICAgICAgICAqICAwICsgSSA9IElcbiAgICAgICAgICogIE4gKyBuID0gTlxuICAgICAgICAgKiAgTiArIDAgPSBOXG4gICAgICAgICAqICBOICsgTiA9IE5cbiAgICAgICAgICogIE4gKyBJID0gTlxuICAgICAgICAgKiAgSSArIG4gPSBJXG4gICAgICAgICAqICBJICsgMCA9IElcbiAgICAgICAgICogIEkgKyBOID0gTlxuICAgICAgICAgKiAgSSArIEkgPSBJXG4gICAgICAgICAqXG4gICAgICAgICAqIFJldHVybiBhIG5ldyBCaWdOdW1iZXIgd2hvc2UgdmFsdWUgaXMgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIHBsdXMgdGhlIHZhbHVlIG9mXG4gICAgICAgICAqIEJpZ051bWJlcih5LCBiKS5cbiAgICAgICAgICovXG4gICAgICAgIFAucGx1cyA9IFAuYWRkID0gZnVuY3Rpb24gKCB5LCBiICkge1xuICAgICAgICAgICAgdmFyIHQsXG4gICAgICAgICAgICAgICAgeCA9IHRoaXMsXG4gICAgICAgICAgICAgICAgYSA9IHgucztcblxuICAgICAgICAgICAgaWQgPSAxMjtcbiAgICAgICAgICAgIHkgPSBuZXcgQmlnTnVtYmVyKCB5LCBiICk7XG4gICAgICAgICAgICBiID0geS5zO1xuXG4gICAgICAgICAgICAvLyBFaXRoZXIgTmFOP1xuICAgICAgICAgICAgaWYgKCAhYSB8fCAhYiApIHJldHVybiBuZXcgQmlnTnVtYmVyKE5hTik7XG5cbiAgICAgICAgICAgIC8vIFNpZ25zIGRpZmZlcj9cbiAgICAgICAgICAgICBpZiAoIGEgIT0gYiApIHtcbiAgICAgICAgICAgICAgICB5LnMgPSAtYjtcbiAgICAgICAgICAgICAgICByZXR1cm4geC5taW51cyh5KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdmFyIHhlID0geC5lIC8gTE9HX0JBU0UsXG4gICAgICAgICAgICAgICAgeWUgPSB5LmUgLyBMT0dfQkFTRSxcbiAgICAgICAgICAgICAgICB4YyA9IHguYyxcbiAgICAgICAgICAgICAgICB5YyA9IHkuYztcblxuICAgICAgICAgICAgaWYgKCAheGUgfHwgIXllICkge1xuXG4gICAgICAgICAgICAgICAgLy8gUmV0dXJuIMKxSW5maW5pdHkgaWYgZWl0aGVyIMKxSW5maW5pdHkuXG4gICAgICAgICAgICAgICAgaWYgKCAheGMgfHwgIXljICkgcmV0dXJuIG5ldyBCaWdOdW1iZXIoIGEgLyAwICk7XG5cbiAgICAgICAgICAgICAgICAvLyBFaXRoZXIgemVybz9cbiAgICAgICAgICAgICAgICAvLyBSZXR1cm4geSBpZiB5IGlzIG5vbi16ZXJvLCB4IGlmIHggaXMgbm9uLXplcm8sIG9yIHplcm8gaWYgYm90aCBhcmUgemVyby5cbiAgICAgICAgICAgICAgICBpZiAoICF4Y1swXSB8fCAheWNbMF0gKSByZXR1cm4geWNbMF0gPyB5IDogbmV3IEJpZ051bWJlciggeGNbMF0gPyB4IDogYSAqIDAgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgeGUgPSBiaXRGbG9vcih4ZSk7XG4gICAgICAgICAgICB5ZSA9IGJpdEZsb29yKHllKTtcbiAgICAgICAgICAgIHhjID0geGMuc2xpY2UoKTtcblxuICAgICAgICAgICAgLy8gUHJlcGVuZCB6ZXJvcyB0byBlcXVhbGlzZSBleHBvbmVudHMuIEZhc3RlciB0byB1c2UgcmV2ZXJzZSB0aGVuIGRvIHVuc2hpZnRzLlxuICAgICAgICAgICAgaWYgKCBhID0geGUgLSB5ZSApIHtcbiAgICAgICAgICAgICAgICBpZiAoIGEgPiAwICkge1xuICAgICAgICAgICAgICAgICAgICB5ZSA9IHhlO1xuICAgICAgICAgICAgICAgICAgICB0ID0geWM7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgYSA9IC1hO1xuICAgICAgICAgICAgICAgICAgICB0ID0geGM7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdC5yZXZlcnNlKCk7XG4gICAgICAgICAgICAgICAgZm9yICggOyBhLS07IHQucHVzaCgwKSApO1xuICAgICAgICAgICAgICAgIHQucmV2ZXJzZSgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBhID0geGMubGVuZ3RoO1xuICAgICAgICAgICAgYiA9IHljLmxlbmd0aDtcblxuICAgICAgICAgICAgLy8gUG9pbnQgeGMgdG8gdGhlIGxvbmdlciBhcnJheSwgYW5kIGIgdG8gdGhlIHNob3J0ZXIgbGVuZ3RoLlxuICAgICAgICAgICAgaWYgKCBhIC0gYiA8IDAgKSB0ID0geWMsIHljID0geGMsIHhjID0gdCwgYiA9IGE7XG5cbiAgICAgICAgICAgIC8vIE9ubHkgc3RhcnQgYWRkaW5nIGF0IHljLmxlbmd0aCAtIDEgYXMgdGhlIGZ1cnRoZXIgZGlnaXRzIG9mIHhjIGNhbiBiZSBpZ25vcmVkLlxuICAgICAgICAgICAgZm9yICggYSA9IDA7IGI7ICkge1xuICAgICAgICAgICAgICAgIGEgPSAoIHhjWy0tYl0gPSB4Y1tiXSArIHljW2JdICsgYSApIC8gQkFTRSB8IDA7XG4gICAgICAgICAgICAgICAgeGNbYl0gJT0gQkFTRTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKGEpIHtcbiAgICAgICAgICAgICAgICB4Yy51bnNoaWZ0KGEpO1xuICAgICAgICAgICAgICAgICsreWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIE5vIG5lZWQgdG8gY2hlY2sgZm9yIHplcm8sIGFzICt4ICsgK3kgIT0gMCAmJiAteCArIC15ICE9IDBcbiAgICAgICAgICAgIC8vIHllID0gTUFYX0VYUCArIDEgcG9zc2libGVcbiAgICAgICAgICAgIHJldHVybiBub3JtYWxpc2UoIHksIHhjLCB5ZSApO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIHRoZSBudW1iZXIgb2Ygc2lnbmlmaWNhbnQgZGlnaXRzIG9mIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlci5cbiAgICAgICAgICpcbiAgICAgICAgICogW3pdIHtib29sZWFufG51bWJlcn0gV2hldGhlciB0byBjb3VudCBpbnRlZ2VyLXBhcnQgdHJhaWxpbmcgemVyb3M6IHRydWUsIGZhbHNlLCAxIG9yIDAuXG4gICAgICAgICAqL1xuICAgICAgICBQLnByZWNpc2lvbiA9IFAuc2QgPSBmdW5jdGlvbiAoeikge1xuICAgICAgICAgICAgdmFyIG4sIHYsXG4gICAgICAgICAgICAgICAgeCA9IHRoaXMsXG4gICAgICAgICAgICAgICAgYyA9IHguYztcblxuICAgICAgICAgICAgLy8gJ3ByZWNpc2lvbigpIGFyZ3VtZW50IG5vdCBhIGJvb2xlYW4gb3IgYmluYXJ5IGRpZ2l0OiB7en0nXG4gICAgICAgICAgICBpZiAoIHogIT0gbnVsbCAmJiB6ICE9PSAhIXogJiYgeiAhPT0gMSAmJiB6ICE9PSAwICkge1xuICAgICAgICAgICAgICAgIGlmIChFUlJPUlMpIHJhaXNlKCAxMywgJ2FyZ3VtZW50JyArIG5vdEJvb2wsIHogKTtcbiAgICAgICAgICAgICAgICBpZiAoIHogIT0gISF6ICkgeiA9IG51bGw7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICggIWMgKSByZXR1cm4gbnVsbDtcbiAgICAgICAgICAgIHYgPSBjLmxlbmd0aCAtIDE7XG4gICAgICAgICAgICBuID0gdiAqIExPR19CQVNFICsgMTtcblxuICAgICAgICAgICAgaWYgKCB2ID0gY1t2XSApIHtcblxuICAgICAgICAgICAgICAgIC8vIFN1YnRyYWN0IHRoZSBudW1iZXIgb2YgdHJhaWxpbmcgemVyb3Mgb2YgdGhlIGxhc3QgZWxlbWVudC5cbiAgICAgICAgICAgICAgICBmb3IgKCA7IHYgJSAxMCA9PSAwOyB2IC89IDEwLCBuLS0gKTtcblxuICAgICAgICAgICAgICAgIC8vIEFkZCB0aGUgbnVtYmVyIG9mIGRpZ2l0cyBvZiB0aGUgZmlyc3QgZWxlbWVudC5cbiAgICAgICAgICAgICAgICBmb3IgKCB2ID0gY1swXTsgdiA+PSAxMDsgdiAvPSAxMCwgbisrICk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICggeiAmJiB4LmUgKyAxID4gbiApIG4gPSB4LmUgKyAxO1xuXG4gICAgICAgICAgICByZXR1cm4gbjtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiBhIG5ldyBCaWdOdW1iZXIgd2hvc2UgdmFsdWUgaXMgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIHJvdW5kZWQgdG8gYSBtYXhpbXVtIG9mXG4gICAgICAgICAqIGRwIGRlY2ltYWwgcGxhY2VzIHVzaW5nIHJvdW5kaW5nIG1vZGUgcm0sIG9yIHRvIDAgYW5kIFJPVU5ESU5HX01PREUgcmVzcGVjdGl2ZWx5IGlmXG4gICAgICAgICAqIG9taXR0ZWQuXG4gICAgICAgICAqXG4gICAgICAgICAqIFtkcF0ge251bWJlcn0gRGVjaW1hbCBwbGFjZXMuIEludGVnZXIsIDAgdG8gTUFYIGluY2x1c2l2ZS5cbiAgICAgICAgICogW3JtXSB7bnVtYmVyfSBSb3VuZGluZyBtb2RlLiBJbnRlZ2VyLCAwIHRvIDggaW5jbHVzaXZlLlxuICAgICAgICAgKlxuICAgICAgICAgKiAncm91bmQoKSBkZWNpbWFsIHBsYWNlcyBvdXQgb2YgcmFuZ2U6IHtkcH0nXG4gICAgICAgICAqICdyb3VuZCgpIGRlY2ltYWwgcGxhY2VzIG5vdCBhbiBpbnRlZ2VyOiB7ZHB9J1xuICAgICAgICAgKiAncm91bmQoKSByb3VuZGluZyBtb2RlIG5vdCBhbiBpbnRlZ2VyOiB7cm19J1xuICAgICAgICAgKiAncm91bmQoKSByb3VuZGluZyBtb2RlIG91dCBvZiByYW5nZToge3JtfSdcbiAgICAgICAgICovXG4gICAgICAgIFAucm91bmQgPSBmdW5jdGlvbiAoIGRwLCBybSApIHtcbiAgICAgICAgICAgIHZhciBuID0gbmV3IEJpZ051bWJlcih0aGlzKTtcblxuICAgICAgICAgICAgaWYgKCBkcCA9PSBudWxsIHx8IGlzVmFsaWRJbnQoIGRwLCAwLCBNQVgsIDE1ICkgKSB7XG4gICAgICAgICAgICAgICAgcm91bmQoIG4sIH5+ZHAgKyB0aGlzLmUgKyAxLCBybSA9PSBudWxsIHx8XG4gICAgICAgICAgICAgICAgICAhaXNWYWxpZEludCggcm0sIDAsIDgsIDE1LCByb3VuZGluZ01vZGUgKSA/IFJPVU5ESU5HX01PREUgOiBybSB8IDAgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIG47XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gYSBuZXcgQmlnTnVtYmVyIHdob3NlIHZhbHVlIGlzIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciBzaGlmdGVkIGJ5IGsgcGxhY2VzXG4gICAgICAgICAqIChwb3dlcnMgb2YgMTApLiBTaGlmdCB0byB0aGUgcmlnaHQgaWYgbiA+IDAsIGFuZCB0byB0aGUgbGVmdCBpZiBuIDwgMC5cbiAgICAgICAgICpcbiAgICAgICAgICogayB7bnVtYmVyfSBJbnRlZ2VyLCAtTUFYX1NBRkVfSU5URUdFUiB0byBNQVhfU0FGRV9JTlRFR0VSIGluY2x1c2l2ZS5cbiAgICAgICAgICpcbiAgICAgICAgICogSWYgayBpcyBvdXQgb2YgcmFuZ2UgYW5kIEVSUk9SUyBpcyBmYWxzZSwgdGhlIHJlc3VsdCB3aWxsIGJlIMKxMCBpZiBrIDwgMCwgb3IgwrFJbmZpbml0eVxuICAgICAgICAgKiBvdGhlcndpc2UuXG4gICAgICAgICAqXG4gICAgICAgICAqICdzaGlmdCgpIGFyZ3VtZW50IG5vdCBhbiBpbnRlZ2VyOiB7a30nXG4gICAgICAgICAqICdzaGlmdCgpIGFyZ3VtZW50IG91dCBvZiByYW5nZToge2t9J1xuICAgICAgICAgKi9cbiAgICAgICAgUC5zaGlmdCA9IGZ1bmN0aW9uIChrKSB7XG4gICAgICAgICAgICB2YXIgbiA9IHRoaXM7XG4gICAgICAgICAgICByZXR1cm4gaXNWYWxpZEludCggaywgLU1BWF9TQUZFX0lOVEVHRVIsIE1BWF9TQUZFX0lOVEVHRVIsIDE2LCAnYXJndW1lbnQnIClcblxuICAgICAgICAgICAgICAvLyBrIDwgMWUrMjEsIG9yIHRydW5jYXRlKGspIHdpbGwgcHJvZHVjZSBleHBvbmVudGlhbCBub3RhdGlvbi5cbiAgICAgICAgICAgICAgPyBuLnRpbWVzKCAnMWUnICsgdHJ1bmNhdGUoaykgKVxuICAgICAgICAgICAgICA6IG5ldyBCaWdOdW1iZXIoIG4uYyAmJiBuLmNbMF0gJiYgKCBrIDwgLU1BWF9TQUZFX0lOVEVHRVIgfHwgayA+IE1BWF9TQUZFX0lOVEVHRVIgKVxuICAgICAgICAgICAgICAgID8gbi5zICogKCBrIDwgMCA/IDAgOiAxIC8gMCApXG4gICAgICAgICAgICAgICAgOiBuICk7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiAgc3FydCgtbikgPSAgTlxuICAgICAgICAgKiAgc3FydCggTikgPSAgTlxuICAgICAgICAgKiAgc3FydCgtSSkgPSAgTlxuICAgICAgICAgKiAgc3FydCggSSkgPSAgSVxuICAgICAgICAgKiAgc3FydCggMCkgPSAgMFxuICAgICAgICAgKiAgc3FydCgtMCkgPSAtMFxuICAgICAgICAgKlxuICAgICAgICAgKiBSZXR1cm4gYSBuZXcgQmlnTnVtYmVyIHdob3NlIHZhbHVlIGlzIHRoZSBzcXVhcmUgcm9vdCBvZiB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIsXG4gICAgICAgICAqIHJvdW5kZWQgYWNjb3JkaW5nIHRvIERFQ0lNQUxfUExBQ0VTIGFuZCBST1VORElOR19NT0RFLlxuICAgICAgICAgKi9cbiAgICAgICAgUC5zcXVhcmVSb290ID0gUC5zcXJ0ID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIG0sIG4sIHIsIHJlcCwgdCxcbiAgICAgICAgICAgICAgICB4ID0gdGhpcyxcbiAgICAgICAgICAgICAgICBjID0geC5jLFxuICAgICAgICAgICAgICAgIHMgPSB4LnMsXG4gICAgICAgICAgICAgICAgZSA9IHguZSxcbiAgICAgICAgICAgICAgICBkcCA9IERFQ0lNQUxfUExBQ0VTICsgNCxcbiAgICAgICAgICAgICAgICBoYWxmID0gbmV3IEJpZ051bWJlcignMC41Jyk7XG5cbiAgICAgICAgICAgIC8vIE5lZ2F0aXZlL05hTi9JbmZpbml0eS96ZXJvP1xuICAgICAgICAgICAgaWYgKCBzICE9PSAxIHx8ICFjIHx8ICFjWzBdICkge1xuICAgICAgICAgICAgICAgIHJldHVybiBuZXcgQmlnTnVtYmVyKCAhcyB8fCBzIDwgMCAmJiAoICFjIHx8IGNbMF0gKSA/IE5hTiA6IGMgPyB4IDogMSAvIDAgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gSW5pdGlhbCBlc3RpbWF0ZS5cbiAgICAgICAgICAgIHMgPSBNYXRoLnNxcnQoICt4ICk7XG5cbiAgICAgICAgICAgIC8vIE1hdGguc3FydCB1bmRlcmZsb3cvb3ZlcmZsb3c/XG4gICAgICAgICAgICAvLyBQYXNzIHggdG8gTWF0aC5zcXJ0IGFzIGludGVnZXIsIHRoZW4gYWRqdXN0IHRoZSBleHBvbmVudCBvZiB0aGUgcmVzdWx0LlxuICAgICAgICAgICAgaWYgKCBzID09IDAgfHwgcyA9PSAxIC8gMCApIHtcbiAgICAgICAgICAgICAgICBuID0gY29lZmZUb1N0cmluZyhjKTtcbiAgICAgICAgICAgICAgICBpZiAoICggbi5sZW5ndGggKyBlICkgJSAyID09IDAgKSBuICs9ICcwJztcbiAgICAgICAgICAgICAgICBzID0gTWF0aC5zcXJ0KG4pO1xuICAgICAgICAgICAgICAgIGUgPSBiaXRGbG9vciggKCBlICsgMSApIC8gMiApIC0gKCBlIDwgMCB8fCBlICUgMiApO1xuXG4gICAgICAgICAgICAgICAgaWYgKCBzID09IDEgLyAwICkge1xuICAgICAgICAgICAgICAgICAgICBuID0gJzFlJyArIGU7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgbiA9IHMudG9FeHBvbmVudGlhbCgpO1xuICAgICAgICAgICAgICAgICAgICBuID0gbi5zbGljZSggMCwgbi5pbmRleE9mKCdlJykgKyAxICkgKyBlO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHIgPSBuZXcgQmlnTnVtYmVyKG4pO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICByID0gbmV3IEJpZ051bWJlciggcyArICcnICk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIENoZWNrIGZvciB6ZXJvLlxuICAgICAgICAgICAgLy8gciBjb3VsZCBiZSB6ZXJvIGlmIE1JTl9FWFAgaXMgY2hhbmdlZCBhZnRlciB0aGUgdGhpcyB2YWx1ZSB3YXMgY3JlYXRlZC5cbiAgICAgICAgICAgIC8vIFRoaXMgd291bGQgY2F1c2UgYSBkaXZpc2lvbiBieSB6ZXJvICh4L3QpIGFuZCBoZW5jZSBJbmZpbml0eSBiZWxvdywgd2hpY2ggd291bGQgY2F1c2VcbiAgICAgICAgICAgIC8vIGNvZWZmVG9TdHJpbmcgdG8gdGhyb3cuXG4gICAgICAgICAgICBpZiAoIHIuY1swXSApIHtcbiAgICAgICAgICAgICAgICBlID0gci5lO1xuICAgICAgICAgICAgICAgIHMgPSBlICsgZHA7XG4gICAgICAgICAgICAgICAgaWYgKCBzIDwgMyApIHMgPSAwO1xuXG4gICAgICAgICAgICAgICAgLy8gTmV3dG9uLVJhcGhzb24gaXRlcmF0aW9uLlxuICAgICAgICAgICAgICAgIGZvciAoIDsgOyApIHtcbiAgICAgICAgICAgICAgICAgICAgdCA9IHI7XG4gICAgICAgICAgICAgICAgICAgIHIgPSBoYWxmLnRpbWVzKCB0LnBsdXMoIGRpdiggeCwgdCwgZHAsIDEgKSApICk7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKCBjb2VmZlRvU3RyaW5nKCB0LmMgICApLnNsaWNlKCAwLCBzICkgPT09ICggbiA9XG4gICAgICAgICAgICAgICAgICAgICAgICAgY29lZmZUb1N0cmluZyggci5jICkgKS5zbGljZSggMCwgcyApICkge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBUaGUgZXhwb25lbnQgb2YgciBtYXkgaGVyZSBiZSBvbmUgbGVzcyB0aGFuIHRoZSBmaW5hbCByZXN1bHQgZXhwb25lbnQsXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBlLmcgMC4wMDA5OTk5IChlLTQpIC0tPiAwLjAwMSAoZS0zKSwgc28gYWRqdXN0IHMgc28gdGhlIHJvdW5kaW5nIGRpZ2l0c1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gYXJlIGluZGV4ZWQgY29ycmVjdGx5LlxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCByLmUgPCBlICkgLS1zO1xuICAgICAgICAgICAgICAgICAgICAgICAgbiA9IG4uc2xpY2UoIHMgLSAzLCBzICsgMSApO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBUaGUgNHRoIHJvdW5kaW5nIGRpZ2l0IG1heSBiZSBpbiBlcnJvciBieSAtMSBzbyBpZiB0aGUgNCByb3VuZGluZyBkaWdpdHNcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGFyZSA5OTk5IG9yIDQ5OTkgKGkuZS4gYXBwcm9hY2hpbmcgYSByb3VuZGluZyBib3VuZGFyeSkgY29udGludWUgdGhlXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBpdGVyYXRpb24uXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoIG4gPT0gJzk5OTknIHx8ICFyZXAgJiYgbiA9PSAnNDk5OScgKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBPbiB0aGUgZmlyc3QgaXRlcmF0aW9uIG9ubHksIGNoZWNrIHRvIHNlZSBpZiByb3VuZGluZyB1cCBnaXZlcyB0aGVcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBleGFjdCByZXN1bHQgYXMgdGhlIG5pbmVzIG1heSBpbmZpbml0ZWx5IHJlcGVhdC5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoICFyZXAgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKCB0LCB0LmUgKyBERUNJTUFMX1BMQUNFUyArIDIsIDAgKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoIHQudGltZXModCkuZXEoeCkgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByID0gdDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZHAgKz0gNDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzICs9IDQ7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwID0gMTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBJZiByb3VuZGluZyBkaWdpdHMgYXJlIG51bGwsIDB7MCw0fSBvciA1MHswLDN9LCBjaGVjayBmb3IgZXhhY3RcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyByZXN1bHQuIElmIG5vdCwgdGhlbiB0aGVyZSBhcmUgZnVydGhlciBkaWdpdHMgYW5kIG0gd2lsbCBiZSB0cnV0aHkuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCAhK24gfHwgIStuLnNsaWNlKDEpICYmIG4uY2hhckF0KDApID09ICc1JyApIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBUcnVuY2F0ZSB0byB0aGUgZmlyc3Qgcm91bmRpbmcgZGlnaXQuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKCByLCByLmUgKyBERUNJTUFMX1BMQUNFUyArIDIsIDEgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbSA9ICFyLnRpbWVzKHIpLmVxKHgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gcm91bmQoIHIsIHIuZSArIERFQ0lNQUxfUExBQ0VTICsgMSwgUk9VTkRJTkdfTU9ERSwgbSApO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogIG4gKiAwID0gMFxuICAgICAgICAgKiAgbiAqIE4gPSBOXG4gICAgICAgICAqICBuICogSSA9IElcbiAgICAgICAgICogIDAgKiBuID0gMFxuICAgICAgICAgKiAgMCAqIDAgPSAwXG4gICAgICAgICAqICAwICogTiA9IE5cbiAgICAgICAgICogIDAgKiBJID0gTlxuICAgICAgICAgKiAgTiAqIG4gPSBOXG4gICAgICAgICAqICBOICogMCA9IE5cbiAgICAgICAgICogIE4gKiBOID0gTlxuICAgICAgICAgKiAgTiAqIEkgPSBOXG4gICAgICAgICAqICBJICogbiA9IElcbiAgICAgICAgICogIEkgKiAwID0gTlxuICAgICAgICAgKiAgSSAqIE4gPSBOXG4gICAgICAgICAqICBJICogSSA9IElcbiAgICAgICAgICpcbiAgICAgICAgICogUmV0dXJuIGEgbmV3IEJpZ051bWJlciB3aG9zZSB2YWx1ZSBpcyB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgdGltZXMgdGhlIHZhbHVlIG9mXG4gICAgICAgICAqIEJpZ051bWJlcih5LCBiKS5cbiAgICAgICAgICovXG4gICAgICAgIFAudGltZXMgPSBQLm11bCA9IGZ1bmN0aW9uICggeSwgYiApIHtcbiAgICAgICAgICAgIHZhciBjLCBlLCBpLCBqLCBrLCBtLCB4Y0wsIHhsbywgeGhpLCB5Y0wsIHlsbywgeWhpLCB6YyxcbiAgICAgICAgICAgICAgICBiYXNlLCBzcXJ0QmFzZSxcbiAgICAgICAgICAgICAgICB4ID0gdGhpcyxcbiAgICAgICAgICAgICAgICB4YyA9IHguYyxcbiAgICAgICAgICAgICAgICB5YyA9ICggaWQgPSAxNywgeSA9IG5ldyBCaWdOdW1iZXIoIHksIGIgKSApLmM7XG5cbiAgICAgICAgICAgIC8vIEVpdGhlciBOYU4sIMKxSW5maW5pdHkgb3IgwrEwP1xuICAgICAgICAgICAgaWYgKCAheGMgfHwgIXljIHx8ICF4Y1swXSB8fCAheWNbMF0gKSB7XG5cbiAgICAgICAgICAgICAgICAvLyBSZXR1cm4gTmFOIGlmIGVpdGhlciBpcyBOYU4sIG9yIG9uZSBpcyAwIGFuZCB0aGUgb3RoZXIgaXMgSW5maW5pdHkuXG4gICAgICAgICAgICAgICAgaWYgKCAheC5zIHx8ICF5LnMgfHwgeGMgJiYgIXhjWzBdICYmICF5YyB8fCB5YyAmJiAheWNbMF0gJiYgIXhjICkge1xuICAgICAgICAgICAgICAgICAgICB5LmMgPSB5LmUgPSB5LnMgPSBudWxsO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHkucyAqPSB4LnM7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gUmV0dXJuIMKxSW5maW5pdHkgaWYgZWl0aGVyIGlzIMKxSW5maW5pdHkuXG4gICAgICAgICAgICAgICAgICAgIGlmICggIXhjIHx8ICF5YyApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHkuYyA9IHkuZSA9IG51bGw7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gUmV0dXJuIMKxMCBpZiBlaXRoZXIgaXMgwrEwLlxuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgeS5jID0gWzBdO1xuICAgICAgICAgICAgICAgICAgICAgICAgeS5lID0gMDtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHJldHVybiB5O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBlID0gYml0Rmxvb3IoIHguZSAvIExPR19CQVNFICkgKyBiaXRGbG9vciggeS5lIC8gTE9HX0JBU0UgKTtcbiAgICAgICAgICAgIHkucyAqPSB4LnM7XG4gICAgICAgICAgICB4Y0wgPSB4Yy5sZW5ndGg7XG4gICAgICAgICAgICB5Y0wgPSB5Yy5sZW5ndGg7XG5cbiAgICAgICAgICAgIC8vIEVuc3VyZSB4YyBwb2ludHMgdG8gbG9uZ2VyIGFycmF5IGFuZCB4Y0wgdG8gaXRzIGxlbmd0aC5cbiAgICAgICAgICAgIGlmICggeGNMIDwgeWNMICkgemMgPSB4YywgeGMgPSB5YywgeWMgPSB6YywgaSA9IHhjTCwgeGNMID0geWNMLCB5Y0wgPSBpO1xuXG4gICAgICAgICAgICAvLyBJbml0aWFsaXNlIHRoZSByZXN1bHQgYXJyYXkgd2l0aCB6ZXJvcy5cbiAgICAgICAgICAgIGZvciAoIGkgPSB4Y0wgKyB5Y0wsIHpjID0gW107IGktLTsgemMucHVzaCgwKSApO1xuXG4gICAgICAgICAgICBiYXNlID0gQkFTRTtcbiAgICAgICAgICAgIHNxcnRCYXNlID0gU1FSVF9CQVNFO1xuXG4gICAgICAgICAgICBmb3IgKCBpID0geWNMOyAtLWkgPj0gMDsgKSB7XG4gICAgICAgICAgICAgICAgYyA9IDA7XG4gICAgICAgICAgICAgICAgeWxvID0geWNbaV0gJSBzcXJ0QmFzZTtcbiAgICAgICAgICAgICAgICB5aGkgPSB5Y1tpXSAvIHNxcnRCYXNlIHwgMDtcblxuICAgICAgICAgICAgICAgIGZvciAoIGsgPSB4Y0wsIGogPSBpICsgazsgaiA+IGk7ICkge1xuICAgICAgICAgICAgICAgICAgICB4bG8gPSB4Y1stLWtdICUgc3FydEJhc2U7XG4gICAgICAgICAgICAgICAgICAgIHhoaSA9IHhjW2tdIC8gc3FydEJhc2UgfCAwO1xuICAgICAgICAgICAgICAgICAgICBtID0geWhpICogeGxvICsgeGhpICogeWxvO1xuICAgICAgICAgICAgICAgICAgICB4bG8gPSB5bG8gKiB4bG8gKyAoICggbSAlIHNxcnRCYXNlICkgKiBzcXJ0QmFzZSApICsgemNbal0gKyBjO1xuICAgICAgICAgICAgICAgICAgICBjID0gKCB4bG8gLyBiYXNlIHwgMCApICsgKCBtIC8gc3FydEJhc2UgfCAwICkgKyB5aGkgKiB4aGk7XG4gICAgICAgICAgICAgICAgICAgIHpjW2otLV0gPSB4bG8gJSBiYXNlO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHpjW2pdID0gYztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKGMpIHtcbiAgICAgICAgICAgICAgICArK2U7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHpjLnNoaWZ0KCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiBub3JtYWxpc2UoIHksIHpjLCBlICk7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gYSBuZXcgQmlnTnVtYmVyIHdob3NlIHZhbHVlIGlzIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciByb3VuZGVkIHRvIGEgbWF4aW11bSBvZlxuICAgICAgICAgKiBzZCBzaWduaWZpY2FudCBkaWdpdHMgdXNpbmcgcm91bmRpbmcgbW9kZSBybSwgb3IgUk9VTkRJTkdfTU9ERSBpZiBybSBpcyBvbWl0dGVkLlxuICAgICAgICAgKlxuICAgICAgICAgKiBbc2RdIHtudW1iZXJ9IFNpZ25pZmljYW50IGRpZ2l0cy4gSW50ZWdlciwgMSB0byBNQVggaW5jbHVzaXZlLlxuICAgICAgICAgKiBbcm1dIHtudW1iZXJ9IFJvdW5kaW5nIG1vZGUuIEludGVnZXIsIDAgdG8gOCBpbmNsdXNpdmUuXG4gICAgICAgICAqXG4gICAgICAgICAqICd0b0RpZ2l0cygpIHByZWNpc2lvbiBvdXQgb2YgcmFuZ2U6IHtzZH0nXG4gICAgICAgICAqICd0b0RpZ2l0cygpIHByZWNpc2lvbiBub3QgYW4gaW50ZWdlcjoge3NkfSdcbiAgICAgICAgICogJ3RvRGlnaXRzKCkgcm91bmRpbmcgbW9kZSBub3QgYW4gaW50ZWdlcjoge3JtfSdcbiAgICAgICAgICogJ3RvRGlnaXRzKCkgcm91bmRpbmcgbW9kZSBvdXQgb2YgcmFuZ2U6IHtybX0nXG4gICAgICAgICAqL1xuICAgICAgICBQLnRvRGlnaXRzID0gZnVuY3Rpb24gKCBzZCwgcm0gKSB7XG4gICAgICAgICAgICB2YXIgbiA9IG5ldyBCaWdOdW1iZXIodGhpcyk7XG4gICAgICAgICAgICBzZCA9IHNkID09IG51bGwgfHwgIWlzVmFsaWRJbnQoIHNkLCAxLCBNQVgsIDE4LCAncHJlY2lzaW9uJyApID8gbnVsbCA6IHNkIHwgMDtcbiAgICAgICAgICAgIHJtID0gcm0gPT0gbnVsbCB8fCAhaXNWYWxpZEludCggcm0sIDAsIDgsIDE4LCByb3VuZGluZ01vZGUgKSA/IFJPVU5ESU5HX01PREUgOiBybSB8IDA7XG4gICAgICAgICAgICByZXR1cm4gc2QgPyByb3VuZCggbiwgc2QsIHJtICkgOiBuO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIGEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgaW4gZXhwb25lbnRpYWwgbm90YXRpb24gYW5kXG4gICAgICAgICAqIHJvdW5kZWQgdXNpbmcgUk9VTkRJTkdfTU9ERSB0byBkcCBmaXhlZCBkZWNpbWFsIHBsYWNlcy5cbiAgICAgICAgICpcbiAgICAgICAgICogW2RwXSB7bnVtYmVyfSBEZWNpbWFsIHBsYWNlcy4gSW50ZWdlciwgMCB0byBNQVggaW5jbHVzaXZlLlxuICAgICAgICAgKiBbcm1dIHtudW1iZXJ9IFJvdW5kaW5nIG1vZGUuIEludGVnZXIsIDAgdG8gOCBpbmNsdXNpdmUuXG4gICAgICAgICAqXG4gICAgICAgICAqICd0b0V4cG9uZW50aWFsKCkgZGVjaW1hbCBwbGFjZXMgbm90IGFuIGludGVnZXI6IHtkcH0nXG4gICAgICAgICAqICd0b0V4cG9uZW50aWFsKCkgZGVjaW1hbCBwbGFjZXMgb3V0IG9mIHJhbmdlOiB7ZHB9J1xuICAgICAgICAgKiAndG9FeHBvbmVudGlhbCgpIHJvdW5kaW5nIG1vZGUgbm90IGFuIGludGVnZXI6IHtybX0nXG4gICAgICAgICAqICd0b0V4cG9uZW50aWFsKCkgcm91bmRpbmcgbW9kZSBvdXQgb2YgcmFuZ2U6IHtybX0nXG4gICAgICAgICAqL1xuICAgICAgICBQLnRvRXhwb25lbnRpYWwgPSBmdW5jdGlvbiAoIGRwLCBybSApIHtcbiAgICAgICAgICAgIHJldHVybiBmb3JtYXQoIHRoaXMsXG4gICAgICAgICAgICAgIGRwICE9IG51bGwgJiYgaXNWYWxpZEludCggZHAsIDAsIE1BWCwgMTkgKSA/IH5+ZHAgKyAxIDogbnVsbCwgcm0sIDE5ICk7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gYSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciBpbiBmaXhlZC1wb2ludCBub3RhdGlvbiByb3VuZGluZ1xuICAgICAgICAgKiB0byBkcCBmaXhlZCBkZWNpbWFsIHBsYWNlcyB1c2luZyByb3VuZGluZyBtb2RlIHJtLCBvciBST1VORElOR19NT0RFIGlmIHJtIGlzIG9taXR0ZWQuXG4gICAgICAgICAqXG4gICAgICAgICAqIE5vdGU6IGFzIHdpdGggSmF2YVNjcmlwdCdzIG51bWJlciB0eXBlLCAoLTApLnRvRml4ZWQoMCkgaXMgJzAnLFxuICAgICAgICAgKiBidXQgZS5nLiAoLTAuMDAwMDEpLnRvRml4ZWQoMCkgaXMgJy0wJy5cbiAgICAgICAgICpcbiAgICAgICAgICogW2RwXSB7bnVtYmVyfSBEZWNpbWFsIHBsYWNlcy4gSW50ZWdlciwgMCB0byBNQVggaW5jbHVzaXZlLlxuICAgICAgICAgKiBbcm1dIHtudW1iZXJ9IFJvdW5kaW5nIG1vZGUuIEludGVnZXIsIDAgdG8gOCBpbmNsdXNpdmUuXG4gICAgICAgICAqXG4gICAgICAgICAqICd0b0ZpeGVkKCkgZGVjaW1hbCBwbGFjZXMgbm90IGFuIGludGVnZXI6IHtkcH0nXG4gICAgICAgICAqICd0b0ZpeGVkKCkgZGVjaW1hbCBwbGFjZXMgb3V0IG9mIHJhbmdlOiB7ZHB9J1xuICAgICAgICAgKiAndG9GaXhlZCgpIHJvdW5kaW5nIG1vZGUgbm90IGFuIGludGVnZXI6IHtybX0nXG4gICAgICAgICAqICd0b0ZpeGVkKCkgcm91bmRpbmcgbW9kZSBvdXQgb2YgcmFuZ2U6IHtybX0nXG4gICAgICAgICAqL1xuICAgICAgICBQLnRvRml4ZWQgPSBmdW5jdGlvbiAoIGRwLCBybSApIHtcbiAgICAgICAgICAgIHJldHVybiBmb3JtYXQoIHRoaXMsIGRwICE9IG51bGwgJiYgaXNWYWxpZEludCggZHAsIDAsIE1BWCwgMjAgKVxuICAgICAgICAgICAgICA/IH5+ZHAgKyB0aGlzLmUgKyAxIDogbnVsbCwgcm0sIDIwICk7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gYSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciBpbiBmaXhlZC1wb2ludCBub3RhdGlvbiByb3VuZGVkXG4gICAgICAgICAqIHVzaW5nIHJtIG9yIFJPVU5ESU5HX01PREUgdG8gZHAgZGVjaW1hbCBwbGFjZXMsIGFuZCBmb3JtYXR0ZWQgYWNjb3JkaW5nIHRvIHRoZSBwcm9wZXJ0aWVzXG4gICAgICAgICAqIG9mIHRoZSBGT1JNQVQgb2JqZWN0IChzZWUgQmlnTnVtYmVyLmNvbmZpZykuXG4gICAgICAgICAqXG4gICAgICAgICAqIEZPUk1BVCA9IHtcbiAgICAgICAgICogICAgICBkZWNpbWFsU2VwYXJhdG9yIDogJy4nLFxuICAgICAgICAgKiAgICAgIGdyb3VwU2VwYXJhdG9yIDogJywnLFxuICAgICAgICAgKiAgICAgIGdyb3VwU2l6ZSA6IDMsXG4gICAgICAgICAqICAgICAgc2Vjb25kYXJ5R3JvdXBTaXplIDogMCxcbiAgICAgICAgICogICAgICBmcmFjdGlvbkdyb3VwU2VwYXJhdG9yIDogJ1xceEEwJywgICAgLy8gbm9uLWJyZWFraW5nIHNwYWNlXG4gICAgICAgICAqICAgICAgZnJhY3Rpb25Hcm91cFNpemUgOiAwXG4gICAgICAgICAqIH07XG4gICAgICAgICAqXG4gICAgICAgICAqIFtkcF0ge251bWJlcn0gRGVjaW1hbCBwbGFjZXMuIEludGVnZXIsIDAgdG8gTUFYIGluY2x1c2l2ZS5cbiAgICAgICAgICogW3JtXSB7bnVtYmVyfSBSb3VuZGluZyBtb2RlLiBJbnRlZ2VyLCAwIHRvIDggaW5jbHVzaXZlLlxuICAgICAgICAgKlxuICAgICAgICAgKiAndG9Gb3JtYXQoKSBkZWNpbWFsIHBsYWNlcyBub3QgYW4gaW50ZWdlcjoge2RwfSdcbiAgICAgICAgICogJ3RvRm9ybWF0KCkgZGVjaW1hbCBwbGFjZXMgb3V0IG9mIHJhbmdlOiB7ZHB9J1xuICAgICAgICAgKiAndG9Gb3JtYXQoKSByb3VuZGluZyBtb2RlIG5vdCBhbiBpbnRlZ2VyOiB7cm19J1xuICAgICAgICAgKiAndG9Gb3JtYXQoKSByb3VuZGluZyBtb2RlIG91dCBvZiByYW5nZToge3JtfSdcbiAgICAgICAgICovXG4gICAgICAgIFAudG9Gb3JtYXQgPSBmdW5jdGlvbiAoIGRwLCBybSApIHtcbiAgICAgICAgICAgIHZhciBzdHIgPSBmb3JtYXQoIHRoaXMsIGRwICE9IG51bGwgJiYgaXNWYWxpZEludCggZHAsIDAsIE1BWCwgMjEgKVxuICAgICAgICAgICAgICA/IH5+ZHAgKyB0aGlzLmUgKyAxIDogbnVsbCwgcm0sIDIxICk7XG5cbiAgICAgICAgICAgIGlmICggdGhpcy5jICkge1xuICAgICAgICAgICAgICAgIHZhciBpLFxuICAgICAgICAgICAgICAgICAgICBhcnIgPSBzdHIuc3BsaXQoJy4nKSxcbiAgICAgICAgICAgICAgICAgICAgZzEgPSArRk9STUFULmdyb3VwU2l6ZSxcbiAgICAgICAgICAgICAgICAgICAgZzIgPSArRk9STUFULnNlY29uZGFyeUdyb3VwU2l6ZSxcbiAgICAgICAgICAgICAgICAgICAgZ3JvdXBTZXBhcmF0b3IgPSBGT1JNQVQuZ3JvdXBTZXBhcmF0b3IsXG4gICAgICAgICAgICAgICAgICAgIGludFBhcnQgPSBhcnJbMF0sXG4gICAgICAgICAgICAgICAgICAgIGZyYWN0aW9uUGFydCA9IGFyclsxXSxcbiAgICAgICAgICAgICAgICAgICAgaXNOZWcgPSB0aGlzLnMgPCAwLFxuICAgICAgICAgICAgICAgICAgICBpbnREaWdpdHMgPSBpc05lZyA/IGludFBhcnQuc2xpY2UoMSkgOiBpbnRQYXJ0LFxuICAgICAgICAgICAgICAgICAgICBsZW4gPSBpbnREaWdpdHMubGVuZ3RoO1xuXG4gICAgICAgICAgICAgICAgaWYgKGcyKSBpID0gZzEsIGcxID0gZzIsIGcyID0gaSwgbGVuIC09IGk7XG5cbiAgICAgICAgICAgICAgICBpZiAoIGcxID4gMCAmJiBsZW4gPiAwICkge1xuICAgICAgICAgICAgICAgICAgICBpID0gbGVuICUgZzEgfHwgZzE7XG4gICAgICAgICAgICAgICAgICAgIGludFBhcnQgPSBpbnREaWdpdHMuc3Vic3RyKCAwLCBpICk7XG5cbiAgICAgICAgICAgICAgICAgICAgZm9yICggOyBpIDwgbGVuOyBpICs9IGcxICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaW50UGFydCArPSBncm91cFNlcGFyYXRvciArIGludERpZ2l0cy5zdWJzdHIoIGksIGcxICk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBpZiAoIGcyID4gMCApIGludFBhcnQgKz0gZ3JvdXBTZXBhcmF0b3IgKyBpbnREaWdpdHMuc2xpY2UoaSk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChpc05lZykgaW50UGFydCA9ICctJyArIGludFBhcnQ7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgc3RyID0gZnJhY3Rpb25QYXJ0XG4gICAgICAgICAgICAgICAgICA/IGludFBhcnQgKyBGT1JNQVQuZGVjaW1hbFNlcGFyYXRvciArICggKCBnMiA9ICtGT1JNQVQuZnJhY3Rpb25Hcm91cFNpemUgKVxuICAgICAgICAgICAgICAgICAgICA/IGZyYWN0aW9uUGFydC5yZXBsYWNlKCBuZXcgUmVnRXhwKCAnXFxcXGR7JyArIGcyICsgJ31cXFxcQicsICdnJyApLFxuICAgICAgICAgICAgICAgICAgICAgICckJicgKyBGT1JNQVQuZnJhY3Rpb25Hcm91cFNlcGFyYXRvciApXG4gICAgICAgICAgICAgICAgICAgIDogZnJhY3Rpb25QYXJ0IClcbiAgICAgICAgICAgICAgICAgIDogaW50UGFydDtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIHN0cjtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiBhIHN0cmluZyBhcnJheSByZXByZXNlbnRpbmcgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIGFzIGEgc2ltcGxlIGZyYWN0aW9uIHdpdGhcbiAgICAgICAgICogYW4gaW50ZWdlciBudW1lcmF0b3IgYW5kIGFuIGludGVnZXIgZGVub21pbmF0b3IuIFRoZSBkZW5vbWluYXRvciB3aWxsIGJlIGEgcG9zaXRpdmVcbiAgICAgICAgICogbm9uLXplcm8gdmFsdWUgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIHRoZSBzcGVjaWZpZWQgbWF4aW11bSBkZW5vbWluYXRvci4gSWYgYSBtYXhpbXVtXG4gICAgICAgICAqIGRlbm9taW5hdG9yIGlzIG5vdCBzcGVjaWZpZWQsIHRoZSBkZW5vbWluYXRvciB3aWxsIGJlIHRoZSBsb3dlc3QgdmFsdWUgbmVjZXNzYXJ5IHRvXG4gICAgICAgICAqIHJlcHJlc2VudCB0aGUgbnVtYmVyIGV4YWN0bHkuXG4gICAgICAgICAqXG4gICAgICAgICAqIFttZF0ge251bWJlcnxzdHJpbmd8QmlnTnVtYmVyfSBJbnRlZ2VyID49IDEgYW5kIDwgSW5maW5pdHkuIFRoZSBtYXhpbXVtIGRlbm9taW5hdG9yLlxuICAgICAgICAgKlxuICAgICAgICAgKiAndG9GcmFjdGlvbigpIG1heCBkZW5vbWluYXRvciBub3QgYW4gaW50ZWdlcjoge21kfSdcbiAgICAgICAgICogJ3RvRnJhY3Rpb24oKSBtYXggZGVub21pbmF0b3Igb3V0IG9mIHJhbmdlOiB7bWR9J1xuICAgICAgICAgKi9cbiAgICAgICAgUC50b0ZyYWN0aW9uID0gZnVuY3Rpb24gKG1kKSB7XG4gICAgICAgICAgICB2YXIgYXJyLCBkMCwgZDIsIGUsIGV4cCwgbiwgbjAsIHEsIHMsXG4gICAgICAgICAgICAgICAgayA9IEVSUk9SUyxcbiAgICAgICAgICAgICAgICB4ID0gdGhpcyxcbiAgICAgICAgICAgICAgICB4YyA9IHguYyxcbiAgICAgICAgICAgICAgICBkID0gbmV3IEJpZ051bWJlcihPTkUpLFxuICAgICAgICAgICAgICAgIG4xID0gZDAgPSBuZXcgQmlnTnVtYmVyKE9ORSksXG4gICAgICAgICAgICAgICAgZDEgPSBuMCA9IG5ldyBCaWdOdW1iZXIoT05FKTtcblxuICAgICAgICAgICAgaWYgKCBtZCAhPSBudWxsICkge1xuICAgICAgICAgICAgICAgIEVSUk9SUyA9IGZhbHNlO1xuICAgICAgICAgICAgICAgIG4gPSBuZXcgQmlnTnVtYmVyKG1kKTtcbiAgICAgICAgICAgICAgICBFUlJPUlMgPSBrO1xuXG4gICAgICAgICAgICAgICAgaWYgKCAhKCBrID0gbi5pc0ludCgpICkgfHwgbi5sdChPTkUpICkge1xuXG4gICAgICAgICAgICAgICAgICAgIGlmIChFUlJPUlMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJhaXNlKCAyMixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgJ21heCBkZW5vbWluYXRvciAnICsgKCBrID8gJ291dCBvZiByYW5nZScgOiAnbm90IGFuIGludGVnZXInICksIG1kICk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAvLyBFUlJPUlMgaXMgZmFsc2U6XG4gICAgICAgICAgICAgICAgICAgIC8vIElmIG1kIGlzIGEgZmluaXRlIG5vbi1pbnRlZ2VyID49IDEsIHJvdW5kIGl0IHRvIGFuIGludGVnZXIgYW5kIHVzZSBpdC5cbiAgICAgICAgICAgICAgICAgICAgbWQgPSAhayAmJiBuLmMgJiYgcm91bmQoIG4sIG4uZSArIDEsIDEgKS5ndGUoT05FKSA/IG4gOiBudWxsO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKCAheGMgKSByZXR1cm4geC50b1N0cmluZygpO1xuICAgICAgICAgICAgcyA9IGNvZWZmVG9TdHJpbmcoeGMpO1xuXG4gICAgICAgICAgICAvLyBEZXRlcm1pbmUgaW5pdGlhbCBkZW5vbWluYXRvci5cbiAgICAgICAgICAgIC8vIGQgaXMgYSBwb3dlciBvZiAxMCBhbmQgdGhlIG1pbmltdW0gbWF4IGRlbm9taW5hdG9yIHRoYXQgc3BlY2lmaWVzIHRoZSB2YWx1ZSBleGFjdGx5LlxuICAgICAgICAgICAgZSA9IGQuZSA9IHMubGVuZ3RoIC0geC5lIC0gMTtcbiAgICAgICAgICAgIGQuY1swXSA9IFBPV1NfVEVOWyAoIGV4cCA9IGUgJSBMT0dfQkFTRSApIDwgMCA/IExPR19CQVNFICsgZXhwIDogZXhwIF07XG4gICAgICAgICAgICBtZCA9ICFtZCB8fCBuLmNtcChkKSA+IDAgPyAoIGUgPiAwID8gZCA6IG4xICkgOiBuO1xuXG4gICAgICAgICAgICBleHAgPSBNQVhfRVhQO1xuICAgICAgICAgICAgTUFYX0VYUCA9IDEgLyAwO1xuICAgICAgICAgICAgbiA9IG5ldyBCaWdOdW1iZXIocyk7XG5cbiAgICAgICAgICAgIC8vIG4wID0gZDEgPSAwXG4gICAgICAgICAgICBuMC5jWzBdID0gMDtcblxuICAgICAgICAgICAgZm9yICggOyA7ICkgIHtcbiAgICAgICAgICAgICAgICBxID0gZGl2KCBuLCBkLCAwLCAxICk7XG4gICAgICAgICAgICAgICAgZDIgPSBkMC5wbHVzKCBxLnRpbWVzKGQxKSApO1xuICAgICAgICAgICAgICAgIGlmICggZDIuY21wKG1kKSA9PSAxICkgYnJlYWs7XG4gICAgICAgICAgICAgICAgZDAgPSBkMTtcbiAgICAgICAgICAgICAgICBkMSA9IGQyO1xuICAgICAgICAgICAgICAgIG4xID0gbjAucGx1cyggcS50aW1lcyggZDIgPSBuMSApICk7XG4gICAgICAgICAgICAgICAgbjAgPSBkMjtcbiAgICAgICAgICAgICAgICBkID0gbi5taW51cyggcS50aW1lcyggZDIgPSBkICkgKTtcbiAgICAgICAgICAgICAgICBuID0gZDI7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGQyID0gZGl2KCBtZC5taW51cyhkMCksIGQxLCAwLCAxICk7XG4gICAgICAgICAgICBuMCA9IG4wLnBsdXMoIGQyLnRpbWVzKG4xKSApO1xuICAgICAgICAgICAgZDAgPSBkMC5wbHVzKCBkMi50aW1lcyhkMSkgKTtcbiAgICAgICAgICAgIG4wLnMgPSBuMS5zID0geC5zO1xuICAgICAgICAgICAgZSAqPSAyO1xuXG4gICAgICAgICAgICAvLyBEZXRlcm1pbmUgd2hpY2ggZnJhY3Rpb24gaXMgY2xvc2VyIHRvIHgsIG4wL2QwIG9yIG4xL2QxXG4gICAgICAgICAgICBhcnIgPSBkaXYoIG4xLCBkMSwgZSwgUk9VTkRJTkdfTU9ERSApLm1pbnVzKHgpLmFicygpLmNtcChcbiAgICAgICAgICAgICAgICAgIGRpdiggbjAsIGQwLCBlLCBST1VORElOR19NT0RFICkubWludXMoeCkuYWJzKCkgKSA8IDFcbiAgICAgICAgICAgICAgICAgICAgPyBbIG4xLnRvU3RyaW5nKCksIGQxLnRvU3RyaW5nKCkgXVxuICAgICAgICAgICAgICAgICAgICA6IFsgbjAudG9TdHJpbmcoKSwgZDAudG9TdHJpbmcoKSBdO1xuXG4gICAgICAgICAgICBNQVhfRVhQID0gZXhwO1xuICAgICAgICAgICAgcmV0dXJuIGFycjtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgY29udmVydGVkIHRvIGEgbnVtYmVyIHByaW1pdGl2ZS5cbiAgICAgICAgICovXG4gICAgICAgIFAudG9OdW1iZXIgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgeCA9IHRoaXM7XG5cbiAgICAgICAgICAgIC8vIEVuc3VyZSB6ZXJvIGhhcyBjb3JyZWN0IHNpZ24uXG4gICAgICAgICAgICByZXR1cm4gK3ggfHwgKCB4LnMgPyB4LnMgKiAwIDogTmFOICk7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gYSBCaWdOdW1iZXIgd2hvc2UgdmFsdWUgaXMgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIHJhaXNlZCB0byB0aGUgcG93ZXIgbi5cbiAgICAgICAgICogSWYgbiBpcyBuZWdhdGl2ZSByb3VuZCBhY2NvcmRpbmcgdG8gREVDSU1BTF9QTEFDRVMgYW5kIFJPVU5ESU5HX01PREUuXG4gICAgICAgICAqIElmIFBPV19QUkVDSVNJT04gaXMgbm90IDAsIHJvdW5kIHRvIFBPV19QUkVDSVNJT04gdXNpbmcgUk9VTkRJTkdfTU9ERS5cbiAgICAgICAgICpcbiAgICAgICAgICogbiB7bnVtYmVyfSBJbnRlZ2VyLCAtOTAwNzE5OTI1NDc0MDk5MiB0byA5MDA3MTk5MjU0NzQwOTkyIGluY2x1c2l2ZS5cbiAgICAgICAgICogKFBlcmZvcm1zIDU0IGxvb3AgaXRlcmF0aW9ucyBmb3IgbiBvZiA5MDA3MTk5MjU0NzQwOTkyLilcbiAgICAgICAgICpcbiAgICAgICAgICogJ3BvdygpIGV4cG9uZW50IG5vdCBhbiBpbnRlZ2VyOiB7bn0nXG4gICAgICAgICAqICdwb3coKSBleHBvbmVudCBvdXQgb2YgcmFuZ2U6IHtufSdcbiAgICAgICAgICovXG4gICAgICAgIFAudG9Qb3dlciA9IFAucG93ID0gZnVuY3Rpb24gKG4pIHtcbiAgICAgICAgICAgIHZhciBrLCB5LFxuICAgICAgICAgICAgICAgIGkgPSBtYXRoZmxvb3IoIG4gPCAwID8gLW4gOiArbiApLFxuICAgICAgICAgICAgICAgIHggPSB0aGlzO1xuXG4gICAgICAgICAgICAvLyBQYXNzIMKxSW5maW5pdHkgdG8gTWF0aC5wb3cgaWYgZXhwb25lbnQgaXMgb3V0IG9mIHJhbmdlLlxuICAgICAgICAgICAgaWYgKCAhaXNWYWxpZEludCggbiwgLU1BWF9TQUZFX0lOVEVHRVIsIE1BWF9TQUZFX0lOVEVHRVIsIDIzLCAnZXhwb25lbnQnICkgJiZcbiAgICAgICAgICAgICAgKCAhaXNGaW5pdGUobikgfHwgaSA+IE1BWF9TQUZFX0lOVEVHRVIgJiYgKCBuIC89IDAgKSB8fFxuICAgICAgICAgICAgICAgIHBhcnNlRmxvYXQobikgIT0gbiAmJiAhKCBuID0gTmFOICkgKSApIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gbmV3IEJpZ051bWJlciggTWF0aC5wb3coICt4LCBuICkgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gVHJ1bmNhdGluZyBlYWNoIGNvZWZmaWNpZW50IGFycmF5IHRvIGEgbGVuZ3RoIG9mIGsgYWZ0ZXIgZWFjaCBtdWx0aXBsaWNhdGlvbiBlcXVhdGVzXG4gICAgICAgICAgICAvLyB0byB0cnVuY2F0aW5nIHNpZ25pZmljYW50IGRpZ2l0cyB0byBQT1dfUFJFQ0lTSU9OICsgWzI4LCA0MV0sIGkuZS4gdGhlcmUgd2lsbCBiZSBhXG4gICAgICAgICAgICAvLyBtaW5pbXVtIG9mIDI4IGd1YXJkIGRpZ2l0cyByZXRhaW5lZC4gKFVzaW5nICsgMS41IHdvdWxkIGdpdmUgWzksIDIxXSBndWFyZCBkaWdpdHMuKVxuICAgICAgICAgICAgayA9IFBPV19QUkVDSVNJT04gPyBtYXRoY2VpbCggUE9XX1BSRUNJU0lPTiAvIExPR19CQVNFICsgMiApIDogMDtcbiAgICAgICAgICAgIHkgPSBuZXcgQmlnTnVtYmVyKE9ORSk7XG5cbiAgICAgICAgICAgIGZvciAoIDsgOyApIHtcblxuICAgICAgICAgICAgICAgIGlmICggaSAlIDIgKSB7XG4gICAgICAgICAgICAgICAgICAgIHkgPSB5LnRpbWVzKHgpO1xuICAgICAgICAgICAgICAgICAgICBpZiAoICF5LmMgKSBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgaWYgKCBrICYmIHkuYy5sZW5ndGggPiBrICkgeS5jLmxlbmd0aCA9IGs7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaSA9IG1hdGhmbG9vciggaSAvIDIgKTtcbiAgICAgICAgICAgICAgICBpZiAoICFpICkgYnJlYWs7XG5cbiAgICAgICAgICAgICAgICB4ID0geC50aW1lcyh4KTtcbiAgICAgICAgICAgICAgICBpZiAoIGsgJiYgeC5jICYmIHguYy5sZW5ndGggPiBrICkgeC5jLmxlbmd0aCA9IGs7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICggbiA8IDAgKSB5ID0gT05FLmRpdih5KTtcbiAgICAgICAgICAgIHJldHVybiBrID8gcm91bmQoIHksIFBPV19QUkVDSVNJT04sIFJPVU5ESU5HX01PREUgKSA6IHk7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gYSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciByb3VuZGVkIHRvIHNkIHNpZ25pZmljYW50IGRpZ2l0c1xuICAgICAgICAgKiB1c2luZyByb3VuZGluZyBtb2RlIHJtIG9yIFJPVU5ESU5HX01PREUuIElmIHNkIGlzIGxlc3MgdGhhbiB0aGUgbnVtYmVyIG9mIGRpZ2l0c1xuICAgICAgICAgKiBuZWNlc3NhcnkgdG8gcmVwcmVzZW50IHRoZSBpbnRlZ2VyIHBhcnQgb2YgdGhlIHZhbHVlIGluIGZpeGVkLXBvaW50IG5vdGF0aW9uLCB0aGVuIHVzZVxuICAgICAgICAgKiBleHBvbmVudGlhbCBub3RhdGlvbi5cbiAgICAgICAgICpcbiAgICAgICAgICogW3NkXSB7bnVtYmVyfSBTaWduaWZpY2FudCBkaWdpdHMuIEludGVnZXIsIDEgdG8gTUFYIGluY2x1c2l2ZS5cbiAgICAgICAgICogW3JtXSB7bnVtYmVyfSBSb3VuZGluZyBtb2RlLiBJbnRlZ2VyLCAwIHRvIDggaW5jbHVzaXZlLlxuICAgICAgICAgKlxuICAgICAgICAgKiAndG9QcmVjaXNpb24oKSBwcmVjaXNpb24gbm90IGFuIGludGVnZXI6IHtzZH0nXG4gICAgICAgICAqICd0b1ByZWNpc2lvbigpIHByZWNpc2lvbiBvdXQgb2YgcmFuZ2U6IHtzZH0nXG4gICAgICAgICAqICd0b1ByZWNpc2lvbigpIHJvdW5kaW5nIG1vZGUgbm90IGFuIGludGVnZXI6IHtybX0nXG4gICAgICAgICAqICd0b1ByZWNpc2lvbigpIHJvdW5kaW5nIG1vZGUgb3V0IG9mIHJhbmdlOiB7cm19J1xuICAgICAgICAgKi9cbiAgICAgICAgUC50b1ByZWNpc2lvbiA9IGZ1bmN0aW9uICggc2QsIHJtICkge1xuICAgICAgICAgICAgcmV0dXJuIGZvcm1hdCggdGhpcywgc2QgIT0gbnVsbCAmJiBpc1ZhbGlkSW50KCBzZCwgMSwgTUFYLCAyNCwgJ3ByZWNpc2lvbicgKVxuICAgICAgICAgICAgICA/IHNkIHwgMCA6IG51bGwsIHJtLCAyNCApO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIGEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgaW4gYmFzZSBiLCBvciBiYXNlIDEwIGlmIGIgaXNcbiAgICAgICAgICogb21pdHRlZC4gSWYgYSBiYXNlIGlzIHNwZWNpZmllZCwgaW5jbHVkaW5nIGJhc2UgMTAsIHJvdW5kIGFjY29yZGluZyB0byBERUNJTUFMX1BMQUNFUyBhbmRcbiAgICAgICAgICogUk9VTkRJTkdfTU9ERS4gSWYgYSBiYXNlIGlzIG5vdCBzcGVjaWZpZWQsIGFuZCB0aGlzIEJpZ051bWJlciBoYXMgYSBwb3NpdGl2ZSBleHBvbmVudFxuICAgICAgICAgKiB0aGF0IGlzIGVxdWFsIHRvIG9yIGdyZWF0ZXIgdGhhbiBUT19FWFBfUE9TLCBvciBhIG5lZ2F0aXZlIGV4cG9uZW50IGVxdWFsIHRvIG9yIGxlc3MgdGhhblxuICAgICAgICAgKiBUT19FWFBfTkVHLCByZXR1cm4gZXhwb25lbnRpYWwgbm90YXRpb24uXG4gICAgICAgICAqXG4gICAgICAgICAqIFtiXSB7bnVtYmVyfSBJbnRlZ2VyLCAyIHRvIDY0IGluY2x1c2l2ZS5cbiAgICAgICAgICpcbiAgICAgICAgICogJ3RvU3RyaW5nKCkgYmFzZSBub3QgYW4gaW50ZWdlcjoge2J9J1xuICAgICAgICAgKiAndG9TdHJpbmcoKSBiYXNlIG91dCBvZiByYW5nZToge2J9J1xuICAgICAgICAgKi9cbiAgICAgICAgUC50b1N0cmluZyA9IGZ1bmN0aW9uIChiKSB7XG4gICAgICAgICAgICB2YXIgc3RyLFxuICAgICAgICAgICAgICAgIG4gPSB0aGlzLFxuICAgICAgICAgICAgICAgIHMgPSBuLnMsXG4gICAgICAgICAgICAgICAgZSA9IG4uZTtcblxuICAgICAgICAgICAgLy8gSW5maW5pdHkgb3IgTmFOP1xuICAgICAgICAgICAgaWYgKCBlID09PSBudWxsICkge1xuXG4gICAgICAgICAgICAgICAgaWYgKHMpIHtcbiAgICAgICAgICAgICAgICAgICAgc3RyID0gJ0luZmluaXR5JztcbiAgICAgICAgICAgICAgICAgICAgaWYgKCBzIDwgMCApIHN0ciA9ICctJyArIHN0cjtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBzdHIgPSAnTmFOJztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHN0ciA9IGNvZWZmVG9TdHJpbmcoIG4uYyApO1xuXG4gICAgICAgICAgICAgICAgaWYgKCBiID09IG51bGwgfHwgIWlzVmFsaWRJbnQoIGIsIDIsIDY0LCAyNSwgJ2Jhc2UnICkgKSB7XG4gICAgICAgICAgICAgICAgICAgIHN0ciA9IGUgPD0gVE9fRVhQX05FRyB8fCBlID49IFRPX0VYUF9QT1NcbiAgICAgICAgICAgICAgICAgICAgICA/IHRvRXhwb25lbnRpYWwoIHN0ciwgZSApXG4gICAgICAgICAgICAgICAgICAgICAgOiB0b0ZpeGVkUG9pbnQoIHN0ciwgZSApO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHN0ciA9IGNvbnZlcnRCYXNlKCB0b0ZpeGVkUG9pbnQoIHN0ciwgZSApLCBiIHwgMCwgMTAsIHMgKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAoIHMgPCAwICYmIG4uY1swXSApIHN0ciA9ICctJyArIHN0cjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIHN0cjtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiBhIG5ldyBCaWdOdW1iZXIgd2hvc2UgdmFsdWUgaXMgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIHRydW5jYXRlZCB0byBhIHdob2xlXG4gICAgICAgICAqIG51bWJlci5cbiAgICAgICAgICovXG4gICAgICAgIFAudHJ1bmNhdGVkID0gUC50cnVuYyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiByb3VuZCggbmV3IEJpZ051bWJlcih0aGlzKSwgdGhpcy5lICsgMSwgMSApO1xuICAgICAgICB9O1xuXG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gYXMgdG9TdHJpbmcsIGJ1dCBkbyBub3QgYWNjZXB0IGEgYmFzZSBhcmd1bWVudC5cbiAgICAgICAgICovXG4gICAgICAgIFAudmFsdWVPZiA9IFAudG9KU09OID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMudG9TdHJpbmcoKTtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8vIEFsaWFzZXMgZm9yIEJpZ0RlY2ltYWwgbWV0aG9kcy5cbiAgICAgICAgLy9QLmFkZCA9IFAucGx1czsgICAgICAgICAvLyBQLmFkZCBpbmNsdWRlZCBhYm92ZVxuICAgICAgICAvL1Auc3VidHJhY3QgPSBQLm1pbnVzOyAgIC8vIFAuc3ViIGluY2x1ZGVkIGFib3ZlXG4gICAgICAgIC8vUC5tdWx0aXBseSA9IFAudGltZXM7ICAgLy8gUC5tdWwgaW5jbHVkZWQgYWJvdmVcbiAgICAgICAgLy9QLmRpdmlkZSA9IFAuZGl2O1xuICAgICAgICAvL1AucmVtYWluZGVyID0gUC5tb2Q7XG4gICAgICAgIC8vUC5jb21wYXJlVG8gPSBQLmNtcDtcbiAgICAgICAgLy9QLm5lZ2F0ZSA9IFAubmVnO1xuXG5cbiAgICAgICAgaWYgKCBjb25maWdPYmogIT0gbnVsbCApIEJpZ051bWJlci5jb25maWcoY29uZmlnT2JqKTtcblxuICAgICAgICByZXR1cm4gQmlnTnVtYmVyO1xuICAgIH1cblxuXG4gICAgLy8gUFJJVkFURSBIRUxQRVIgRlVOQ1RJT05TXG5cblxuICAgIGZ1bmN0aW9uIGJpdEZsb29yKG4pIHtcbiAgICAgICAgdmFyIGkgPSBuIHwgMDtcbiAgICAgICAgcmV0dXJuIG4gPiAwIHx8IG4gPT09IGkgPyBpIDogaSAtIDE7XG4gICAgfVxuXG5cbiAgICAvLyBSZXR1cm4gYSBjb2VmZmljaWVudCBhcnJheSBhcyBhIHN0cmluZyBvZiBiYXNlIDEwIGRpZ2l0cy5cbiAgICBmdW5jdGlvbiBjb2VmZlRvU3RyaW5nKGEpIHtcbiAgICAgICAgdmFyIHMsIHosXG4gICAgICAgICAgICBpID0gMSxcbiAgICAgICAgICAgIGogPSBhLmxlbmd0aCxcbiAgICAgICAgICAgIHIgPSBhWzBdICsgJyc7XG5cbiAgICAgICAgZm9yICggOyBpIDwgajsgKSB7XG4gICAgICAgICAgICBzID0gYVtpKytdICsgJyc7XG4gICAgICAgICAgICB6ID0gTE9HX0JBU0UgLSBzLmxlbmd0aDtcbiAgICAgICAgICAgIGZvciAoIDsgei0tOyBzID0gJzAnICsgcyApO1xuICAgICAgICAgICAgciArPSBzO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gRGV0ZXJtaW5lIHRyYWlsaW5nIHplcm9zLlxuICAgICAgICBmb3IgKCBqID0gci5sZW5ndGg7IHIuY2hhckNvZGVBdCgtLWopID09PSA0ODsgKTtcbiAgICAgICAgcmV0dXJuIHIuc2xpY2UoIDAsIGogKyAxIHx8IDEgKTtcbiAgICB9XG5cblxuICAgIC8vIENvbXBhcmUgdGhlIHZhbHVlIG9mIEJpZ051bWJlcnMgeCBhbmQgeS5cbiAgICBmdW5jdGlvbiBjb21wYXJlKCB4LCB5ICkge1xuICAgICAgICB2YXIgYSwgYixcbiAgICAgICAgICAgIHhjID0geC5jLFxuICAgICAgICAgICAgeWMgPSB5LmMsXG4gICAgICAgICAgICBpID0geC5zLFxuICAgICAgICAgICAgaiA9IHkucyxcbiAgICAgICAgICAgIGsgPSB4LmUsXG4gICAgICAgICAgICBsID0geS5lO1xuXG4gICAgICAgIC8vIEVpdGhlciBOYU4/XG4gICAgICAgIGlmICggIWkgfHwgIWogKSByZXR1cm4gbnVsbDtcblxuICAgICAgICBhID0geGMgJiYgIXhjWzBdO1xuICAgICAgICBiID0geWMgJiYgIXljWzBdO1xuXG4gICAgICAgIC8vIEVpdGhlciB6ZXJvP1xuICAgICAgICBpZiAoIGEgfHwgYiApIHJldHVybiBhID8gYiA/IDAgOiAtaiA6IGk7XG5cbiAgICAgICAgLy8gU2lnbnMgZGlmZmVyP1xuICAgICAgICBpZiAoIGkgIT0gaiApIHJldHVybiBpO1xuXG4gICAgICAgIGEgPSBpIDwgMDtcbiAgICAgICAgYiA9IGsgPT0gbDtcblxuICAgICAgICAvLyBFaXRoZXIgSW5maW5pdHk/XG4gICAgICAgIGlmICggIXhjIHx8ICF5YyApIHJldHVybiBiID8gMCA6ICF4YyBeIGEgPyAxIDogLTE7XG5cbiAgICAgICAgLy8gQ29tcGFyZSBleHBvbmVudHMuXG4gICAgICAgIGlmICggIWIgKSByZXR1cm4gayA+IGwgXiBhID8gMSA6IC0xO1xuXG4gICAgICAgIGogPSAoIGsgPSB4Yy5sZW5ndGggKSA8ICggbCA9IHljLmxlbmd0aCApID8gayA6IGw7XG5cbiAgICAgICAgLy8gQ29tcGFyZSBkaWdpdCBieSBkaWdpdC5cbiAgICAgICAgZm9yICggaSA9IDA7IGkgPCBqOyBpKysgKSBpZiAoIHhjW2ldICE9IHljW2ldICkgcmV0dXJuIHhjW2ldID4geWNbaV0gXiBhID8gMSA6IC0xO1xuXG4gICAgICAgIC8vIENvbXBhcmUgbGVuZ3Rocy5cbiAgICAgICAgcmV0dXJuIGsgPT0gbCA/IDAgOiBrID4gbCBeIGEgPyAxIDogLTE7XG4gICAgfVxuXG5cbiAgICAvKlxuICAgICAqIFJldHVybiB0cnVlIGlmIG4gaXMgYSB2YWxpZCBudW1iZXIgaW4gcmFuZ2UsIG90aGVyd2lzZSBmYWxzZS5cbiAgICAgKiBVc2UgZm9yIGFyZ3VtZW50IHZhbGlkYXRpb24gd2hlbiBFUlJPUlMgaXMgZmFsc2UuXG4gICAgICogTm90ZTogcGFyc2VJbnQoJzFlKzEnKSA9PSAxIGJ1dCBwYXJzZUZsb2F0KCcxZSsxJykgPT0gMTAuXG4gICAgICovXG4gICAgZnVuY3Rpb24gaW50VmFsaWRhdG9yTm9FcnJvcnMoIG4sIG1pbiwgbWF4ICkge1xuICAgICAgICByZXR1cm4gKCBuID0gdHJ1bmNhdGUobikgKSA+PSBtaW4gJiYgbiA8PSBtYXg7XG4gICAgfVxuXG5cbiAgICBmdW5jdGlvbiBpc0FycmF5KG9iaikge1xuICAgICAgICByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKG9iaikgPT0gJ1tvYmplY3QgQXJyYXldJztcbiAgICB9XG5cblxuICAgIC8qXG4gICAgICogQ29udmVydCBzdHJpbmcgb2YgYmFzZUluIHRvIGFuIGFycmF5IG9mIG51bWJlcnMgb2YgYmFzZU91dC5cbiAgICAgKiBFZy4gY29udmVydEJhc2UoJzI1NScsIDEwLCAxNikgcmV0dXJucyBbMTUsIDE1XS5cbiAgICAgKiBFZy4gY29udmVydEJhc2UoJ2ZmJywgMTYsIDEwKSByZXR1cm5zIFsyLCA1LCA1XS5cbiAgICAgKi9cbiAgICBmdW5jdGlvbiB0b0Jhc2VPdXQoIHN0ciwgYmFzZUluLCBiYXNlT3V0ICkge1xuICAgICAgICB2YXIgaixcbiAgICAgICAgICAgIGFyciA9IFswXSxcbiAgICAgICAgICAgIGFyckwsXG4gICAgICAgICAgICBpID0gMCxcbiAgICAgICAgICAgIGxlbiA9IHN0ci5sZW5ndGg7XG5cbiAgICAgICAgZm9yICggOyBpIDwgbGVuOyApIHtcbiAgICAgICAgICAgIGZvciAoIGFyckwgPSBhcnIubGVuZ3RoOyBhcnJMLS07IGFyclthcnJMXSAqPSBiYXNlSW4gKTtcbiAgICAgICAgICAgIGFyclsgaiA9IDAgXSArPSBBTFBIQUJFVC5pbmRleE9mKCBzdHIuY2hhckF0KCBpKysgKSApO1xuXG4gICAgICAgICAgICBmb3IgKCA7IGogPCBhcnIubGVuZ3RoOyBqKysgKSB7XG5cbiAgICAgICAgICAgICAgICBpZiAoIGFycltqXSA+IGJhc2VPdXQgLSAxICkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoIGFycltqICsgMV0gPT0gbnVsbCApIGFycltqICsgMV0gPSAwO1xuICAgICAgICAgICAgICAgICAgICBhcnJbaiArIDFdICs9IGFycltqXSAvIGJhc2VPdXQgfCAwO1xuICAgICAgICAgICAgICAgICAgICBhcnJbal0gJT0gYmFzZU91dDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gYXJyLnJldmVyc2UoKTtcbiAgICB9XG5cblxuICAgIGZ1bmN0aW9uIHRvRXhwb25lbnRpYWwoIHN0ciwgZSApIHtcbiAgICAgICAgcmV0dXJuICggc3RyLmxlbmd0aCA+IDEgPyBzdHIuY2hhckF0KDApICsgJy4nICsgc3RyLnNsaWNlKDEpIDogc3RyICkgK1xuICAgICAgICAgICggZSA8IDAgPyAnZScgOiAnZSsnICkgKyBlO1xuICAgIH1cblxuXG4gICAgZnVuY3Rpb24gdG9GaXhlZFBvaW50KCBzdHIsIGUgKSB7XG4gICAgICAgIHZhciBsZW4sIHo7XG5cbiAgICAgICAgLy8gTmVnYXRpdmUgZXhwb25lbnQ/XG4gICAgICAgIGlmICggZSA8IDAgKSB7XG5cbiAgICAgICAgICAgIC8vIFByZXBlbmQgemVyb3MuXG4gICAgICAgICAgICBmb3IgKCB6ID0gJzAuJzsgKytlOyB6ICs9ICcwJyApO1xuICAgICAgICAgICAgc3RyID0geiArIHN0cjtcblxuICAgICAgICAvLyBQb3NpdGl2ZSBleHBvbmVudFxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbGVuID0gc3RyLmxlbmd0aDtcblxuICAgICAgICAgICAgLy8gQXBwZW5kIHplcm9zLlxuICAgICAgICAgICAgaWYgKCArK2UgPiBsZW4gKSB7XG4gICAgICAgICAgICAgICAgZm9yICggeiA9ICcwJywgZSAtPSBsZW47IC0tZTsgeiArPSAnMCcgKTtcbiAgICAgICAgICAgICAgICBzdHIgKz0gejtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoIGUgPCBsZW4gKSB7XG4gICAgICAgICAgICAgICAgc3RyID0gc3RyLnNsaWNlKCAwLCBlICkgKyAnLicgKyBzdHIuc2xpY2UoZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gc3RyO1xuICAgIH1cblxuXG4gICAgZnVuY3Rpb24gdHJ1bmNhdGUobikge1xuICAgICAgICBuID0gcGFyc2VGbG9hdChuKTtcbiAgICAgICAgcmV0dXJuIG4gPCAwID8gbWF0aGNlaWwobikgOiBtYXRoZmxvb3Iobik7XG4gICAgfVxuXG5cbiAgICAvLyBFWFBPUlRcblxuXG4gICAgQmlnTnVtYmVyID0gYW5vdGhlcigpO1xuXG4gICAgLy8gQU1ELlxuICAgIGlmICggdHlwZW9mIGRlZmluZSA9PSAnZnVuY3Rpb24nICYmIGRlZmluZS5hbWQgKSB7XG4gICAgICAgIGRlZmluZSggZnVuY3Rpb24gKCkgeyByZXR1cm4gQmlnTnVtYmVyOyB9ICk7XG5cbiAgICAvLyBOb2RlIGFuZCBvdGhlciBlbnZpcm9ubWVudHMgdGhhdCBzdXBwb3J0IG1vZHVsZS5leHBvcnRzLlxuICAgIH0gZWxzZSBpZiAoIHR5cGVvZiBtb2R1bGUgIT0gJ3VuZGVmaW5lZCcgJiYgbW9kdWxlLmV4cG9ydHMgKSB7XG4gICAgICAgIG1vZHVsZS5leHBvcnRzID0gQmlnTnVtYmVyO1xuICAgICAgICBpZiAoICFjcnlwdG8gKSB0cnkgeyBjcnlwdG8gPSByZXF1aXJlKCdjcnlwdG8nKTsgfSBjYXRjaCAoZSkge31cblxuICAgIC8vIEJyb3dzZXIuXG4gICAgfSBlbHNlIHtcbiAgICAgICAgZ2xvYmFsLkJpZ051bWJlciA9IEJpZ051bWJlcjtcbiAgICB9XG59KSh0aGlzKTtcbiIsInZhciB3ZWIzID0gcmVxdWlyZSgnLi9saWIvd2ViMycpO1xud2ViMy5wcm92aWRlcnMuSHR0cFByb3ZpZGVyID0gcmVxdWlyZSgnLi9saWIvd2ViMy9odHRwcHJvdmlkZXInKTtcbndlYjMucHJvdmlkZXJzLlF0U3luY1Byb3ZpZGVyID0gcmVxdWlyZSgnLi9saWIvd2ViMy9xdHN5bmMnKTtcbndlYjMuZXRoLmNvbnRyYWN0ID0gcmVxdWlyZSgnLi9saWIvd2ViMy9jb250cmFjdCcpO1xud2ViMy5ldGgubmFtZXJlZyA9IHJlcXVpcmUoJy4vbGliL3dlYjMvbmFtZXJlZycpO1xud2ViMy5ldGguc2VuZElCQU5UcmFuc2FjdGlvbiA9IHJlcXVpcmUoJy4vbGliL3dlYjMvdHJhbnNmZXInKTtcblxuLy8gZG9udCBvdmVycmlkZSBnbG9iYWwgdmFyaWFibGVcbmlmICh0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyAmJiB0eXBlb2Ygd2luZG93LndlYjMgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgd2luZG93LndlYjMgPSB3ZWIzO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHdlYjM7XG5cbiJdfQ== +//# sourceMappingURL=data:application/json;charset:utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJsaWIvc29saWRpdHkvY29kZXIuanMiLCJsaWIvc29saWRpdHkvZm9ybWF0dGVycy5qcyIsImxpYi9zb2xpZGl0eS9wYXJhbS5qcyIsImxpYi91dGlscy9icm93c2VyLXhoci5qcyIsImxpYi91dGlscy9jb25maWcuanMiLCJsaWIvdXRpbHMvc2hhMy5qcyIsImxpYi91dGlscy91dGlscy5qcyIsImxpYi92ZXJzaW9uLmpzb24iLCJsaWIvd2ViMy5qcyIsImxpYi93ZWIzL2JhdGNoLmpzIiwibGliL3dlYjMvY29udHJhY3QuanMiLCJsaWIvd2ViMy9kYi5qcyIsImxpYi93ZWIzL2Vycm9ycy5qcyIsImxpYi93ZWIzL2V0aC5qcyIsImxpYi93ZWIzL2V2ZW50LmpzIiwibGliL3dlYjMvZmlsdGVyLmpzIiwibGliL3dlYjMvZm9ybWF0dGVycy5qcyIsImxpYi93ZWIzL2Z1bmN0aW9uLmpzIiwibGliL3dlYjMvaHR0cHByb3ZpZGVyLmpzIiwibGliL3dlYjMvaWNhcC5qcyIsImxpYi93ZWIzL2pzb25ycGMuanMiLCJsaWIvd2ViMy9tZXRob2QuanMiLCJsaWIvd2ViMy9uYW1lcmVnLmpzIiwibGliL3dlYjMvbmV0LmpzIiwibGliL3dlYjMvcHJvcGVydHkuanMiLCJsaWIvd2ViMy9xdHN5bmMuanMiLCJsaWIvd2ViMy9yZXF1ZXN0bWFuYWdlci5qcyIsImxpYi93ZWIzL3NoaC5qcyIsImxpYi93ZWIzL3RyYW5zZmVyLmpzIiwibGliL3dlYjMvd2F0Y2hlcy5qcyIsIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L2xpYi9fZW1wdHkuanMiLCJub2RlX21vZHVsZXMvY3J5cHRvLWpzL2NvcmUuanMiLCJub2RlX21vZHVsZXMvY3J5cHRvLWpzL3NoYTMuanMiLCJub2RlX21vZHVsZXMvY3J5cHRvLWpzL3g2NC1jb3JlLmpzIiwiYmlnbnVtYmVyLmpzIiwiaW5kZXguanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxUkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFOQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsTkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDVEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3ZDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsZkE7QUFDQTtBQUNBO0FBQ0E7O0FDSEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3REE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcExBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4REE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3RDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDblJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25NQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDM0pBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxTkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDak9BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1R0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1S0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDaERBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwSEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3RQQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcEVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOUZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xIQTs7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNydUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbFVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQy9TQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzNuRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIFxuICogQGZpbGUgY29kZXIuanNcbiAqIEBhdXRob3IgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE1XG4gKi9cblxudmFyIEJpZ051bWJlciA9IHJlcXVpcmUoJ2JpZ251bWJlci5qcycpO1xudmFyIHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMvdXRpbHMnKTtcbnZhciBmID0gcmVxdWlyZSgnLi9mb3JtYXR0ZXJzJyk7XG52YXIgU29saWRpdHlQYXJhbSA9IHJlcXVpcmUoJy4vcGFyYW0nKTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBjaGVjayBpZiBhIHR5cGUgaXMgYW4gYXJyYXkgdHlwZVxuICpcbiAqIEBtZXRob2QgaXNBcnJheVR5cGVcbiAqIEBwYXJhbSB7U3RyaW5nfSB0eXBlXG4gKiBAcmV0dXJuIHtCb29sfSB0cnVlIGlzIHRoZSB0eXBlIGlzIGFuIGFycmF5LCBvdGhlcndpc2UgZmFsc2VcbiAqL1xudmFyIGlzQXJyYXlUeXBlID0gZnVuY3Rpb24gKHR5cGUpIHtcbiAgICByZXR1cm4gdHlwZS5zbGljZSgtMikgPT09ICdbXSc7XG59O1xuXG4vKipcbiAqIFNvbGlkaXR5VHlwZSBwcm90b3R5cGUgaXMgdXNlZCB0byBlbmNvZGUvZGVjb2RlIHNvbGlkaXR5IHBhcmFtcyBvZiBjZXJ0YWluIHR5cGVcbiAqL1xudmFyIFNvbGlkaXR5VHlwZSA9IGZ1bmN0aW9uIChjb25maWcpIHtcbiAgICB0aGlzLl9uYW1lID0gY29uZmlnLm5hbWU7XG4gICAgdGhpcy5fbWF0Y2ggPSBjb25maWcubWF0Y2g7XG4gICAgdGhpcy5fbW9kZSA9IGNvbmZpZy5tb2RlO1xuICAgIHRoaXMuX2lucHV0Rm9ybWF0dGVyID0gY29uZmlnLmlucHV0Rm9ybWF0dGVyO1xuICAgIHRoaXMuX291dHB1dEZvcm1hdHRlciA9IGNvbmZpZy5vdXRwdXRGb3JtYXR0ZXI7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGRldGVybWluZSBpZiB0aGlzIFNvbGlkaXR5VHlwZSBkbyBtYXRjaCBnaXZlbiB0eXBlXG4gKlxuICogQG1ldGhvZCBpc1R5cGVcbiAqIEBwYXJhbSB7U3RyaW5nfSBuYW1lXG4gKiBAcmV0dXJuIHtCb29sfSB0cnVlIGlmIHR5cGUgbWF0Y2ggdGhpcyBTb2xpZGl0eVR5cGUsIG90aGVyd2lzZSBmYWxzZVxuICovXG5Tb2xpZGl0eVR5cGUucHJvdG90eXBlLmlzVHlwZSA9IGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgaWYgKHRoaXMuX21hdGNoID09PSAnc3RyaWN0Jykge1xuICAgICAgICByZXR1cm4gdGhpcy5fbmFtZSA9PT0gbmFtZSB8fCAobmFtZS5pbmRleE9mKHRoaXMuX25hbWUpID09PSAwICYmIG5hbWUuc2xpY2UodGhpcy5fbmFtZS5sZW5ndGgpID09PSAnW10nKTtcbiAgICB9IGVsc2UgaWYgKHRoaXMuX21hdGNoID09PSAncHJlZml4Jykge1xuICAgICAgICAvLyBUT0RPIGJldHRlciB0eXBlIGRldGVjdGlvbiFcbiAgICAgICAgcmV0dXJuIG5hbWUuaW5kZXhPZih0aGlzLl9uYW1lKSA9PT0gMDtcbiAgICB9XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIHRyYW5zZm9ybSBwbGFpbiBwYXJhbSB0byBTb2xpZGl0eVBhcmFtIG9iamVjdFxuICpcbiAqIEBtZXRob2QgZm9ybWF0SW5wdXRcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYXJhbSAtIHBsYWluIG9iamVjdCwgb3IgYW4gYXJyYXkgb2Ygb2JqZWN0c1xuICogQHBhcmFtIHtCb29sfSBhcnJheVR5cGUgLSB0cnVlIGlmIGEgcGFyYW0gc2hvdWxkIGJlIGVuY29kZWQgYXMgYW4gYXJyYXlcbiAqIEByZXR1cm4ge1NvbGlkaXR5UGFyYW19IGVuY29kZWQgcGFyYW0gd3JhcHBlZCBpbiBTb2xpZGl0eVBhcmFtIG9iamVjdCBcbiAqL1xuU29saWRpdHlUeXBlLnByb3RvdHlwZS5mb3JtYXRJbnB1dCA9IGZ1bmN0aW9uIChwYXJhbSwgYXJyYXlUeXBlKSB7XG4gICAgaWYgKHV0aWxzLmlzQXJyYXkocGFyYW0pICYmIGFycmF5VHlwZSkgeyAvLyBUT0RPOiBzaG91bGQgZmFpbCBpZiB0aGlzIHR3byBhcmUgbm90IHRoZSBzYW1lXG4gICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgcmV0dXJuIHBhcmFtLm1hcChmdW5jdGlvbiAocCkge1xuICAgICAgICAgICAgcmV0dXJuIHNlbGYuX2lucHV0Rm9ybWF0dGVyKHApO1xuICAgICAgICB9KS5yZWR1Y2UoZnVuY3Rpb24gKGFjYywgY3VycmVudCkge1xuICAgICAgICAgICAgcmV0dXJuIGFjYy5jb21iaW5lKGN1cnJlbnQpO1xuICAgICAgICB9LCBmLmZvcm1hdElucHV0SW50KHBhcmFtLmxlbmd0aCkpLndpdGhPZmZzZXQoMzIpO1xuICAgIH0gXG4gICAgcmV0dXJuIHRoaXMuX2lucHV0Rm9ybWF0dGVyKHBhcmFtKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gdHJhbnNvZm9ybSBTb2xpZGl0eVBhcmFtIHRvIHBsYWluIHBhcmFtXG4gKlxuICogQG1ldGhvZCBmb3JtYXRPdXRwdXRcbiAqIEBwYXJhbSB7U29saWRpdHlQYXJhbX0gYnl0ZUFycmF5XG4gKiBAcGFyYW0ge0Jvb2x9IGFycmF5VHlwZSAtIHRydWUgaWYgYSBwYXJhbSBzaG91bGQgYmUgZGVjb2RlZCBhcyBhbiBhcnJheVxuICogQHJldHVybiB7T2JqZWN0fSBwbGFpbiBkZWNvZGVkIHBhcmFtXG4gKi9cblNvbGlkaXR5VHlwZS5wcm90b3R5cGUuZm9ybWF0T3V0cHV0ID0gZnVuY3Rpb24gKHBhcmFtLCBhcnJheVR5cGUpIHtcbiAgICBpZiAoYXJyYXlUeXBlKSB7XG4gICAgICAgIC8vIGxldCdzIGFzc3VtZSwgdGhhdCB3ZSBzb2xpZGl0eSB3aWxsIG5ldmVyIHJldHVybiBsb25nIGFycmF5cyA6UCBcbiAgICAgICAgdmFyIHJlc3VsdCA9IFtdO1xuICAgICAgICB2YXIgbGVuZ3RoID0gbmV3IEJpZ051bWJlcihwYXJhbS5keW5hbWljUGFydCgpLnNsaWNlKDAsIDY0KSwgMTYpO1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbmd0aCAqIDY0OyBpICs9IDY0KSB7XG4gICAgICAgICAgICByZXN1bHQucHVzaCh0aGlzLl9vdXRwdXRGb3JtYXR0ZXIobmV3IFNvbGlkaXR5UGFyYW0ocGFyYW0uZHluYW1pY1BhcnQoKS5zdWJzdHIoaSArIDY0LCA2NCkpKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX291dHB1dEZvcm1hdHRlcihwYXJhbSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIHNsaWNlIHNpbmdsZSBwYXJhbSBmcm9tIGJ5dGVzXG4gKlxuICogQG1ldGhvZCBzbGljZVBhcmFtXG4gKiBAcGFyYW0ge1N0cmluZ30gYnl0ZXNcbiAqIEBwYXJhbSB7TnVtYmVyfSBpbmRleCBvZiBwYXJhbSB0byBzbGljZVxuICogQHBhcmFtIHtTdHJpbmd9IHR5cGVcbiAqIEByZXR1cm5zIHtTb2xpZGl0eVBhcmFtfSBwYXJhbVxuICovXG5Tb2xpZGl0eVR5cGUucHJvdG90eXBlLnNsaWNlUGFyYW0gPSBmdW5jdGlvbiAoYnl0ZXMsIGluZGV4LCB0eXBlKSB7XG4gICAgaWYgKHRoaXMuX21vZGUgPT09ICdieXRlcycpIHtcbiAgICAgICAgcmV0dXJuIFNvbGlkaXR5UGFyYW0uZGVjb2RlQnl0ZXMoYnl0ZXMsIGluZGV4KTtcbiAgICB9IGVsc2UgaWYgKGlzQXJyYXlUeXBlKHR5cGUpKSB7XG4gICAgICAgIHJldHVybiBTb2xpZGl0eVBhcmFtLmRlY29kZUFycmF5KGJ5dGVzLCBpbmRleCk7XG4gICAgfVxuICAgIHJldHVybiBTb2xpZGl0eVBhcmFtLmRlY29kZVBhcmFtKGJ5dGVzLCBpbmRleCk7XG59O1xuXG4vKipcbiAqIFNvbGlkaXR5Q29kZXIgcHJvdG90eXBlIHNob3VsZCBiZSB1c2VkIHRvIGVuY29kZS9kZWNvZGUgc29saWRpdHkgcGFyYW1zIG9mIGFueSB0eXBlXG4gKi9cbnZhciBTb2xpZGl0eUNvZGVyID0gZnVuY3Rpb24gKHR5cGVzKSB7XG4gICAgdGhpcy5fdHlwZXMgPSB0eXBlcztcbn07XG5cbi8qKlxuICogVGhpcyBtZXRob2Qgc2hvdWxkIGJlIHVzZWQgdG8gdHJhbnNmb3JtIHR5cGUgdG8gU29saWRpdHlUeXBlXG4gKlxuICogQG1ldGhvZCBfcmVxdWlyZVR5cGVcbiAqIEBwYXJhbSB7U3RyaW5nfSB0eXBlXG4gKiBAcmV0dXJucyB7U29saWRpdHlUeXBlfSBcbiAqIEB0aHJvd3Mge0Vycm9yfSB0aHJvd3MgaWYgbm8gbWF0Y2hpbmcgdHlwZSBpcyBmb3VuZFxuICovXG5Tb2xpZGl0eUNvZGVyLnByb3RvdHlwZS5fcmVxdWlyZVR5cGUgPSBmdW5jdGlvbiAodHlwZSkge1xuICAgIHZhciBzb2xpZGl0eVR5cGUgPSB0aGlzLl90eXBlcy5maWx0ZXIoZnVuY3Rpb24gKHQpIHtcbiAgICAgICAgcmV0dXJuIHQuaXNUeXBlKHR5cGUpO1xuICAgIH0pWzBdO1xuXG4gICAgaWYgKCFzb2xpZGl0eVR5cGUpIHtcbiAgICAgICAgdGhyb3cgRXJyb3IoJ2ludmFsaWQgc29saWRpdHkgdHlwZSE6ICcgKyB0eXBlKTtcbiAgICB9XG5cbiAgICByZXR1cm4gc29saWRpdHlUeXBlO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byB0cmFuc2Zvcm0gcGxhaW4gcGFyYW0gb2YgZ2l2ZW4gdHlwZSB0byBTb2xpZGl0eVBhcmFtXG4gKlxuICogQG1ldGhvZCBfZm9ybWF0SW5wdXRcbiAqIEBwYXJhbSB7U3RyaW5nfSB0eXBlIG9mIHBhcmFtXG4gKiBAcGFyYW0ge09iamVjdH0gcGxhaW4gcGFyYW1cbiAqIEByZXR1cm4ge1NvbGlkaXR5UGFyYW19XG4gKi9cblNvbGlkaXR5Q29kZXIucHJvdG90eXBlLl9mb3JtYXRJbnB1dCA9IGZ1bmN0aW9uICh0eXBlLCBwYXJhbSkge1xuICAgIHJldHVybiB0aGlzLl9yZXF1aXJlVHlwZSh0eXBlKS5mb3JtYXRJbnB1dChwYXJhbSwgaXNBcnJheVR5cGUodHlwZSkpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBlbmNvZGUgcGxhaW4gcGFyYW1cbiAqXG4gKiBAbWV0aG9kIGVuY29kZVBhcmFtXG4gKiBAcGFyYW0ge1N0cmluZ30gdHlwZVxuICogQHBhcmFtIHtPYmplY3R9IHBsYWluIHBhcmFtXG4gKiBAcmV0dXJuIHtTdHJpbmd9IGVuY29kZWQgcGxhaW4gcGFyYW1cbiAqL1xuU29saWRpdHlDb2Rlci5wcm90b3R5cGUuZW5jb2RlUGFyYW0gPSBmdW5jdGlvbiAodHlwZSwgcGFyYW0pIHtcbiAgICByZXR1cm4gdGhpcy5fZm9ybWF0SW5wdXQodHlwZSwgcGFyYW0pLmVuY29kZSgpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBlbmNvZGUgbGlzdCBvZiBwYXJhbXNcbiAqXG4gKiBAbWV0aG9kIGVuY29kZVBhcmFtc1xuICogQHBhcmFtIHtBcnJheX0gdHlwZXNcbiAqIEBwYXJhbSB7QXJyYXl9IHBhcmFtc1xuICogQHJldHVybiB7U3RyaW5nfSBlbmNvZGVkIGxpc3Qgb2YgcGFyYW1zXG4gKi9cblNvbGlkaXR5Q29kZXIucHJvdG90eXBlLmVuY29kZVBhcmFtcyA9IGZ1bmN0aW9uICh0eXBlcywgcGFyYW1zKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHZhciBzb2xpZGl0eVBhcmFtcyA9IHR5cGVzLm1hcChmdW5jdGlvbiAodHlwZSwgaW5kZXgpIHtcbiAgICAgICAgcmV0dXJuIHNlbGYuX2Zvcm1hdElucHV0KHR5cGUsIHBhcmFtc1tpbmRleF0pO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIFNvbGlkaXR5UGFyYW0uZW5jb2RlTGlzdChzb2xpZGl0eVBhcmFtcyk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGRlY29kZSBieXRlcyB0byBwbGFpbiBwYXJhbVxuICpcbiAqIEBtZXRob2QgZGVjb2RlUGFyYW1cbiAqIEBwYXJhbSB7U3RyaW5nfSB0eXBlXG4gKiBAcGFyYW0ge1N0cmluZ30gYnl0ZXNcbiAqIEByZXR1cm4ge09iamVjdH0gcGxhaW4gcGFyYW1cbiAqL1xuU29saWRpdHlDb2Rlci5wcm90b3R5cGUuZGVjb2RlUGFyYW0gPSBmdW5jdGlvbiAodHlwZSwgYnl0ZXMpIHtcbiAgICByZXR1cm4gdGhpcy5kZWNvZGVQYXJhbXMoW3R5cGVdLCBieXRlcylbMF07XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGRlY29kZSBsaXN0IG9mIHBhcmFtc1xuICpcbiAqIEBtZXRob2QgZGVjb2RlUGFyYW1cbiAqIEBwYXJhbSB7QXJyYXl9IHR5cGVzXG4gKiBAcGFyYW0ge1N0cmluZ30gYnl0ZXNcbiAqIEByZXR1cm4ge0FycmF5fSBhcnJheSBvZiBwbGFpbiBwYXJhbXNcbiAqL1xuU29saWRpdHlDb2Rlci5wcm90b3R5cGUuZGVjb2RlUGFyYW1zID0gZnVuY3Rpb24gKHR5cGVzLCBieXRlcykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICByZXR1cm4gdHlwZXMubWFwKGZ1bmN0aW9uICh0eXBlLCBpbmRleCkge1xuICAgICAgICB2YXIgc29saWRpdHlUeXBlID0gc2VsZi5fcmVxdWlyZVR5cGUodHlwZSk7XG4gICAgICAgIHZhciBwID0gc29saWRpdHlUeXBlLnNsaWNlUGFyYW0oYnl0ZXMsIGluZGV4LCB0eXBlKTtcbiAgICAgICAgcmV0dXJuIHNvbGlkaXR5VHlwZS5mb3JtYXRPdXRwdXQocCwgaXNBcnJheVR5cGUodHlwZSkpO1xuICAgIH0pO1xufTtcblxudmFyIGNvZGVyID0gbmV3IFNvbGlkaXR5Q29kZXIoW1xuICAgIG5ldyBTb2xpZGl0eVR5cGUoe1xuICAgICAgICBuYW1lOiAnYWRkcmVzcycsXG4gICAgICAgIG1hdGNoOiAnc3RyaWN0JyxcbiAgICAgICAgbW9kZTogJ3ZhbHVlJyxcbiAgICAgICAgaW5wdXRGb3JtYXR0ZXI6IGYuZm9ybWF0SW5wdXRJbnQsXG4gICAgICAgIG91dHB1dEZvcm1hdHRlcjogZi5mb3JtYXRPdXRwdXRBZGRyZXNzXG4gICAgfSksXG4gICAgbmV3IFNvbGlkaXR5VHlwZSh7XG4gICAgICAgIG5hbWU6ICdib29sJyxcbiAgICAgICAgbWF0Y2g6ICdzdHJpY3QnLFxuICAgICAgICBtb2RlOiAndmFsdWUnLFxuICAgICAgICBpbnB1dEZvcm1hdHRlcjogZi5mb3JtYXRJbnB1dEJvb2wsXG4gICAgICAgIG91dHB1dEZvcm1hdHRlcjogZi5mb3JtYXRPdXRwdXRCb29sXG4gICAgfSksXG4gICAgbmV3IFNvbGlkaXR5VHlwZSh7XG4gICAgICAgIG5hbWU6ICdpbnQnLFxuICAgICAgICBtYXRjaDogJ3ByZWZpeCcsXG4gICAgICAgIG1vZGU6ICd2YWx1ZScsXG4gICAgICAgIGlucHV0Rm9ybWF0dGVyOiBmLmZvcm1hdElucHV0SW50LFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IGYuZm9ybWF0T3V0cHV0SW50LFxuICAgIH0pLFxuICAgIG5ldyBTb2xpZGl0eVR5cGUoe1xuICAgICAgICBuYW1lOiAndWludCcsXG4gICAgICAgIG1hdGNoOiAncHJlZml4JyxcbiAgICAgICAgbW9kZTogJ3ZhbHVlJyxcbiAgICAgICAgaW5wdXRGb3JtYXR0ZXI6IGYuZm9ybWF0SW5wdXRJbnQsXG4gICAgICAgIG91dHB1dEZvcm1hdHRlcjogZi5mb3JtYXRPdXRwdXRVSW50XG4gICAgfSksXG4gICAgbmV3IFNvbGlkaXR5VHlwZSh7XG4gICAgICAgIG5hbWU6ICdieXRlcycsXG4gICAgICAgIG1hdGNoOiAnc3RyaWN0JyxcbiAgICAgICAgbW9kZTogJ2J5dGVzJyxcbiAgICAgICAgaW5wdXRGb3JtYXR0ZXI6IGYuZm9ybWF0SW5wdXREeW5hbWljQnl0ZXMsXG4gICAgICAgIG91dHB1dEZvcm1hdHRlcjogZi5mb3JtYXRPdXRwdXREeW5hbWljQnl0ZXNcbiAgICB9KSxcbiAgICBuZXcgU29saWRpdHlUeXBlKHtcbiAgICAgICAgbmFtZTogJ2J5dGVzJyxcbiAgICAgICAgbWF0Y2g6ICdwcmVmaXgnLFxuICAgICAgICBtb2RlOiAndmFsdWUnLFxuICAgICAgICBpbnB1dEZvcm1hdHRlcjogZi5mb3JtYXRJbnB1dEJ5dGVzLFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IGYuZm9ybWF0T3V0cHV0Qnl0ZXNcbiAgICB9KSxcbiAgICBuZXcgU29saWRpdHlUeXBlKHtcbiAgICAgICAgbmFtZTogJ3JlYWwnLFxuICAgICAgICBtYXRjaDogJ3ByZWZpeCcsXG4gICAgICAgIG1vZGU6ICd2YWx1ZScsXG4gICAgICAgIGlucHV0Rm9ybWF0dGVyOiBmLmZvcm1hdElucHV0UmVhbCxcbiAgICAgICAgb3V0cHV0Rm9ybWF0dGVyOiBmLmZvcm1hdE91dHB1dFJlYWxcbiAgICB9KSxcbiAgICBuZXcgU29saWRpdHlUeXBlKHtcbiAgICAgICAgbmFtZTogJ3VyZWFsJyxcbiAgICAgICAgbWF0Y2g6ICdwcmVmaXgnLFxuICAgICAgICBtb2RlOiAndmFsdWUnLFxuICAgICAgICBpbnB1dEZvcm1hdHRlcjogZi5mb3JtYXRJbnB1dFJlYWwsXG4gICAgICAgIG91dHB1dEZvcm1hdHRlcjogZi5mb3JtYXRPdXRwdXRVUmVhbFxuICAgIH0pXG5dKTtcblxubW9kdWxlLmV4cG9ydHMgPSBjb2RlcjtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSBmb3JtYXR0ZXJzLmpzXG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciBCaWdOdW1iZXIgPSByZXF1aXJlKCdiaWdudW1iZXIuanMnKTtcbnZhciB1dGlscyA9IHJlcXVpcmUoJy4uL3V0aWxzL3V0aWxzJyk7XG52YXIgYyA9IHJlcXVpcmUoJy4uL3V0aWxzL2NvbmZpZycpO1xudmFyIFNvbGlkaXR5UGFyYW0gPSByZXF1aXJlKCcuL3BhcmFtJyk7XG5cblxuLyoqXG4gKiBGb3JtYXRzIGlucHV0IHZhbHVlIHRvIGJ5dGUgcmVwcmVzZW50YXRpb24gb2YgaW50XG4gKiBJZiB2YWx1ZSBpcyBuZWdhdGl2ZSwgcmV0dXJuIGl0J3MgdHdvJ3MgY29tcGxlbWVudFxuICogSWYgdGhlIHZhbHVlIGlzIGZsb2F0aW5nIHBvaW50LCByb3VuZCBpdCBkb3duXG4gKlxuICogQG1ldGhvZCBmb3JtYXRJbnB1dEludFxuICogQHBhcmFtIHtTdHJpbmd8TnVtYmVyfEJpZ051bWJlcn0gdmFsdWUgdGhhdCBuZWVkcyB0byBiZSBmb3JtYXR0ZWRcbiAqIEByZXR1cm5zIHtTb2xpZGl0eVBhcmFtfVxuICovXG52YXIgZm9ybWF0SW5wdXRJbnQgPSBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICB2YXIgcGFkZGluZyA9IGMuRVRIX1BBRERJTkcgKiAyO1xuICAgIEJpZ051bWJlci5jb25maWcoYy5FVEhfQklHTlVNQkVSX1JPVU5ESU5HX01PREUpO1xuICAgIHZhciByZXN1bHQgPSB1dGlscy5wYWRMZWZ0KHV0aWxzLnRvVHdvc0NvbXBsZW1lbnQodmFsdWUpLnJvdW5kKCkudG9TdHJpbmcoMTYpLCBwYWRkaW5nKTtcbiAgICByZXR1cm4gbmV3IFNvbGlkaXR5UGFyYW0ocmVzdWx0KTtcbn07XG5cbi8qKlxuICogRm9ybWF0cyBpbnB1dCB2YWx1ZSB0byBieXRlIHJlcHJlc2VudGF0aW9uIG9mIHN0cmluZ1xuICpcbiAqIEBtZXRob2QgZm9ybWF0SW5wdXRCeXRlc1xuICogQHBhcmFtIHtTdHJpbmd9XG4gKiBAcmV0dXJucyB7U29saWRpdHlQYXJhbX1cbiAqL1xudmFyIGZvcm1hdElucHV0Qnl0ZXMgPSBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICB2YXIgcmVzdWx0ID0gdXRpbHMuZnJvbUFzY2lpKHZhbHVlLCBjLkVUSF9QQURESU5HKS5zdWJzdHIoMik7XG4gICAgcmV0dXJuIG5ldyBTb2xpZGl0eVBhcmFtKHJlc3VsdCk7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgaW5wdXQgdmFsdWUgdG8gYnl0ZSByZXByZXNlbnRhdGlvbiBvZiBzdHJpbmdcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdElucHV0RHluYW1pY0J5dGVzXG4gKiBAcGFyYW0ge1N0cmluZ31cbiAqIEByZXR1cm5zIHtTb2xpZGl0eVBhcmFtfVxuICovXG52YXIgZm9ybWF0SW5wdXREeW5hbWljQnl0ZXMgPSBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICB2YXIgcmVzdWx0ID0gdXRpbHMuZnJvbUFzY2lpKHZhbHVlLCBjLkVUSF9QQURESU5HKS5zdWJzdHIoMik7XG4gICAgcmV0dXJuIG5ldyBTb2xpZGl0eVBhcmFtKGZvcm1hdElucHV0SW50KHZhbHVlLmxlbmd0aCkudmFsdWUgKyByZXN1bHQsIDMyKTtcbn07XG5cbi8qKlxuICogRm9ybWF0cyBpbnB1dCB2YWx1ZSB0byBieXRlIHJlcHJlc2VudGF0aW9uIG9mIGJvb2xcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdElucHV0Qm9vbFxuICogQHBhcmFtIHtCb29sZWFufVxuICogQHJldHVybnMge1NvbGlkaXR5UGFyYW19XG4gKi9cbnZhciBmb3JtYXRJbnB1dEJvb2wgPSBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICB2YXIgcmVzdWx0ID0gJzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCcgKyAodmFsdWUgPyAgJzEnIDogJzAnKTtcbiAgICByZXR1cm4gbmV3IFNvbGlkaXR5UGFyYW0ocmVzdWx0KTtcbn07XG5cbi8qKlxuICogRm9ybWF0cyBpbnB1dCB2YWx1ZSB0byBieXRlIHJlcHJlc2VudGF0aW9uIG9mIHJlYWxcbiAqIFZhbHVlcyBhcmUgbXVsdGlwbGllZCBieSAyXm0gYW5kIGVuY29kZWQgYXMgaW50ZWdlcnNcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdElucHV0UmVhbFxuICogQHBhcmFtIHtTdHJpbmd8TnVtYmVyfEJpZ051bWJlcn1cbiAqIEByZXR1cm5zIHtTb2xpZGl0eVBhcmFtfVxuICovXG52YXIgZm9ybWF0SW5wdXRSZWFsID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgcmV0dXJuIGZvcm1hdElucHV0SW50KG5ldyBCaWdOdW1iZXIodmFsdWUpLnRpbWVzKG5ldyBCaWdOdW1iZXIoMikucG93KDEyOCkpKTtcbn07XG5cbi8qKlxuICogQ2hlY2sgaWYgaW5wdXQgdmFsdWUgaXMgbmVnYXRpdmVcbiAqXG4gKiBAbWV0aG9kIHNpZ25lZElzTmVnYXRpdmVcbiAqIEBwYXJhbSB7U3RyaW5nfSB2YWx1ZSBpcyBoZXggZm9ybWF0XG4gKiBAcmV0dXJucyB7Qm9vbGVhbn0gdHJ1ZSBpZiBpdCBpcyBuZWdhdGl2ZSwgb3RoZXJ3aXNlIGZhbHNlXG4gKi9cbnZhciBzaWduZWRJc05lZ2F0aXZlID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgcmV0dXJuIChuZXcgQmlnTnVtYmVyKHZhbHVlLnN1YnN0cigwLCAxKSwgMTYpLnRvU3RyaW5nKDIpLnN1YnN0cigwLCAxKSkgPT09ICcxJztcbn07XG5cbi8qKlxuICogRm9ybWF0cyByaWdodC1hbGlnbmVkIG91dHB1dCBieXRlcyB0byBpbnRcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdE91dHB1dEludFxuICogQHBhcmFtIHtTb2xpZGl0eVBhcmFtfSBwYXJhbVxuICogQHJldHVybnMge0JpZ051bWJlcn0gcmlnaHQtYWxpZ25lZCBvdXRwdXQgYnl0ZXMgZm9ybWF0dGVkIHRvIGJpZyBudW1iZXJcbiAqL1xudmFyIGZvcm1hdE91dHB1dEludCA9IGZ1bmN0aW9uIChwYXJhbSkge1xuICAgIHZhciB2YWx1ZSA9IHBhcmFtLnN0YXRpY1BhcnQoKSB8fCBcIjBcIjtcblxuICAgIC8vIGNoZWNrIGlmIGl0J3MgbmVnYXRpdmUgbnVtYmVyXG4gICAgLy8gaXQgaXQgaXMsIHJldHVybiB0d28ncyBjb21wbGVtZW50XG4gICAgaWYgKHNpZ25lZElzTmVnYXRpdmUodmFsdWUpKSB7XG4gICAgICAgIHJldHVybiBuZXcgQmlnTnVtYmVyKHZhbHVlLCAxNikubWludXMobmV3IEJpZ051bWJlcignZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZicsIDE2KSkubWludXMoMSk7XG4gICAgfVxuICAgIHJldHVybiBuZXcgQmlnTnVtYmVyKHZhbHVlLCAxNik7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgcmlnaHQtYWxpZ25lZCBvdXRwdXQgYnl0ZXMgdG8gdWludFxuICpcbiAqIEBtZXRob2QgZm9ybWF0T3V0cHV0VUludFxuICogQHBhcmFtIHtTb2xpZGl0eVBhcmFtfVxuICogQHJldHVybnMge0JpZ051bWViZXJ9IHJpZ2h0LWFsaWduZWQgb3V0cHV0IGJ5dGVzIGZvcm1hdHRlZCB0byB1aW50XG4gKi9cbnZhciBmb3JtYXRPdXRwdXRVSW50ID0gZnVuY3Rpb24gKHBhcmFtKSB7XG4gICAgdmFyIHZhbHVlID0gcGFyYW0uc3RhdGljUGFydCgpIHx8IFwiMFwiO1xuICAgIHJldHVybiBuZXcgQmlnTnVtYmVyKHZhbHVlLCAxNik7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgcmlnaHQtYWxpZ25lZCBvdXRwdXQgYnl0ZXMgdG8gcmVhbFxuICpcbiAqIEBtZXRob2QgZm9ybWF0T3V0cHV0UmVhbFxuICogQHBhcmFtIHtTb2xpZGl0eVBhcmFtfVxuICogQHJldHVybnMge0JpZ051bWJlcn0gaW5wdXQgYnl0ZXMgZm9ybWF0dGVkIHRvIHJlYWxcbiAqL1xudmFyIGZvcm1hdE91dHB1dFJlYWwgPSBmdW5jdGlvbiAocGFyYW0pIHtcbiAgICByZXR1cm4gZm9ybWF0T3V0cHV0SW50KHBhcmFtKS5kaXZpZGVkQnkobmV3IEJpZ051bWJlcigyKS5wb3coMTI4KSk7IFxufTtcblxuLyoqXG4gKiBGb3JtYXRzIHJpZ2h0LWFsaWduZWQgb3V0cHV0IGJ5dGVzIHRvIHVyZWFsXG4gKlxuICogQG1ldGhvZCBmb3JtYXRPdXRwdXRVUmVhbFxuICogQHBhcmFtIHtTb2xpZGl0eVBhcmFtfVxuICogQHJldHVybnMge0JpZ051bWJlcn0gaW5wdXQgYnl0ZXMgZm9ybWF0dGVkIHRvIHVyZWFsXG4gKi9cbnZhciBmb3JtYXRPdXRwdXRVUmVhbCA9IGZ1bmN0aW9uIChwYXJhbSkge1xuICAgIHJldHVybiBmb3JtYXRPdXRwdXRVSW50KHBhcmFtKS5kaXZpZGVkQnkobmV3IEJpZ051bWJlcigyKS5wb3coMTI4KSk7IFxufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBmb3JtYXQgb3V0cHV0IGJvb2xcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdE91dHB1dEJvb2xcbiAqIEBwYXJhbSB7U29saWRpdHlQYXJhbX1cbiAqIEByZXR1cm5zIHtCb29sZWFufSByaWdodC1hbGlnbmVkIGlucHV0IGJ5dGVzIGZvcm1hdHRlZCB0byBib29sXG4gKi9cbnZhciBmb3JtYXRPdXRwdXRCb29sID0gZnVuY3Rpb24gKHBhcmFtKSB7XG4gICAgcmV0dXJuIHBhcmFtLnN0YXRpY1BhcnQoKSA9PT0gJzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEnID8gdHJ1ZSA6IGZhbHNlO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBmb3JtYXQgb3V0cHV0IHN0cmluZ1xuICpcbiAqIEBtZXRob2QgZm9ybWF0T3V0cHV0Qnl0ZXNcbiAqIEBwYXJhbSB7U29saWRpdHlQYXJhbX0gbGVmdC1hbGlnbmVkIGhleCByZXByZXNlbnRhdGlvbiBvZiBzdHJpbmdcbiAqIEByZXR1cm5zIHtTdHJpbmd9IGFzY2lpIHN0cmluZ1xuICovXG52YXIgZm9ybWF0T3V0cHV0Qnl0ZXMgPSBmdW5jdGlvbiAocGFyYW0pIHtcbiAgICAvLyBsZW5ndGggbWlnaHQgYWxzbyBiZSBpbXBvcnRhbnQhXG4gICAgcmV0dXJuIHV0aWxzLnRvQXNjaWkocGFyYW0uc3RhdGljUGFydCgpKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZm9ybWF0IG91dHB1dCBzdHJpbmdcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdE91dHB1dER5bmFtaWNCeXRlc1xuICogQHBhcmFtIHtTb2xpZGl0eVBhcmFtfSBsZWZ0LWFsaWduZWQgaGV4IHJlcHJlc2VudGF0aW9uIG9mIHN0cmluZ1xuICogQHJldHVybnMge1N0cmluZ30gYXNjaWkgc3RyaW5nXG4gKi9cbnZhciBmb3JtYXRPdXRwdXREeW5hbWljQnl0ZXMgPSBmdW5jdGlvbiAocGFyYW0pIHtcbiAgICAvLyBsZW5ndGggbWlnaHQgYWxzbyBiZSBpbXBvcnRhbnQhXG4gICAgcmV0dXJuIHV0aWxzLnRvQXNjaWkocGFyYW0uZHluYW1pY1BhcnQoKS5zbGljZSg2NCkpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBmb3JtYXQgb3V0cHV0IGFkZHJlc3NcbiAqXG4gKiBAbWV0aG9kIGZvcm1hdE91dHB1dEFkZHJlc3NcbiAqIEBwYXJhbSB7U29saWRpdHlQYXJhbX0gcmlnaHQtYWxpZ25lZCBpbnB1dCBieXRlc1xuICogQHJldHVybnMge1N0cmluZ30gYWRkcmVzc1xuICovXG52YXIgZm9ybWF0T3V0cHV0QWRkcmVzcyA9IGZ1bmN0aW9uIChwYXJhbSkge1xuICAgIHZhciB2YWx1ZSA9IHBhcmFtLnN0YXRpY1BhcnQoKTtcbiAgICByZXR1cm4gXCIweFwiICsgdmFsdWUuc2xpY2UodmFsdWUubGVuZ3RoIC0gNDAsIHZhbHVlLmxlbmd0aCk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBmb3JtYXRJbnB1dEludDogZm9ybWF0SW5wdXRJbnQsXG4gICAgZm9ybWF0SW5wdXRCeXRlczogZm9ybWF0SW5wdXRCeXRlcyxcbiAgICBmb3JtYXRJbnB1dER5bmFtaWNCeXRlczogZm9ybWF0SW5wdXREeW5hbWljQnl0ZXMsXG4gICAgZm9ybWF0SW5wdXRCb29sOiBmb3JtYXRJbnB1dEJvb2wsXG4gICAgZm9ybWF0SW5wdXRSZWFsOiBmb3JtYXRJbnB1dFJlYWwsXG4gICAgZm9ybWF0T3V0cHV0SW50OiBmb3JtYXRPdXRwdXRJbnQsXG4gICAgZm9ybWF0T3V0cHV0VUludDogZm9ybWF0T3V0cHV0VUludCxcbiAgICBmb3JtYXRPdXRwdXRSZWFsOiBmb3JtYXRPdXRwdXRSZWFsLFxuICAgIGZvcm1hdE91dHB1dFVSZWFsOiBmb3JtYXRPdXRwdXRVUmVhbCxcbiAgICBmb3JtYXRPdXRwdXRCb29sOiBmb3JtYXRPdXRwdXRCb29sLFxuICAgIGZvcm1hdE91dHB1dEJ5dGVzOiBmb3JtYXRPdXRwdXRCeXRlcyxcbiAgICBmb3JtYXRPdXRwdXREeW5hbWljQnl0ZXM6IGZvcm1hdE91dHB1dER5bmFtaWNCeXRlcyxcbiAgICBmb3JtYXRPdXRwdXRBZGRyZXNzOiBmb3JtYXRPdXRwdXRBZGRyZXNzXG59O1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBcbiAqIEBmaWxlIHBhcmFtLmpzXG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciB1dGlscyA9IHJlcXVpcmUoJy4uL3V0aWxzL3V0aWxzJyk7XG5cbi8qKlxuICogU29saWRpdHlQYXJhbSBvYmplY3QgcHJvdG90eXBlLlxuICogU2hvdWxkIGJlIHVzZWQgd2hlbiBlbmNvZGluZywgZGVjb2Rpbmcgc29saWRpdHkgYnl0ZXNcbiAqL1xudmFyIFNvbGlkaXR5UGFyYW0gPSBmdW5jdGlvbiAodmFsdWUsIG9mZnNldCkge1xuICAgIHRoaXMudmFsdWUgPSB2YWx1ZSB8fCAnJztcbiAgICB0aGlzLm9mZnNldCA9IG9mZnNldDsgLy8gb2Zmc2V0IGluIGJ5dGVzXG59O1xuXG4vKipcbiAqIFRoaXMgbWV0aG9kIHNob3VsZCBiZSB1c2VkIHRvIGdldCBsZW5ndGggb2YgcGFyYW1zJ3MgZHluYW1pYyBwYXJ0XG4gKiBcbiAqIEBtZXRob2QgZHluYW1pY1BhcnRMZW5ndGhcbiAqIEByZXR1cm5zIHtOdW1iZXJ9IGxlbmd0aCBvZiBkeW5hbWljIHBhcnQgKGluIGJ5dGVzKVxuICovXG5Tb2xpZGl0eVBhcmFtLnByb3RvdHlwZS5keW5hbWljUGFydExlbmd0aCA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gdGhpcy5keW5hbWljUGFydCgpLmxlbmd0aCAvIDI7XG59O1xuXG4vKipcbiAqIFRoaXMgbWV0aG9kIHNob3VsZCBiZSB1c2VkIHRvIGNyZWF0ZSBjb3B5IG9mIHNvbGlkaXR5IHBhcmFtIHdpdGggZGlmZmVyZW50IG9mZnNldFxuICpcbiAqIEBtZXRob2Qgd2l0aE9mZnNldFxuICogQHBhcmFtIHtOdW1iZXJ9IG9mZnNldCBsZW5ndGggaW4gYnl0ZXNcbiAqIEByZXR1cm5zIHtTb2xpZGl0eVBhcmFtfSBuZXcgc29saWRpdHkgcGFyYW0gd2l0aCBhcHBsaWVkIG9mZnNldFxuICovXG5Tb2xpZGl0eVBhcmFtLnByb3RvdHlwZS53aXRoT2Zmc2V0ID0gZnVuY3Rpb24gKG9mZnNldCkge1xuICAgIHJldHVybiBuZXcgU29saWRpdHlQYXJhbSh0aGlzLnZhbHVlLCBvZmZzZXQpO1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgdXNlZCB0byBjb21iaW5lIHNvbGlkaXR5IHBhcmFtcyB0b2dldGhlclxuICogZWcuIHdoZW4gYXBwZW5kaW5nIGFuIGFycmF5XG4gKlxuICogQG1ldGhvZCBjb21iaW5lXG4gKiBAcGFyYW0ge1NvbGlkaXR5UGFyYW19IHBhcmFtIHdpdGggd2hpY2ggd2Ugc2hvdWxkIGNvbWJpbmVcbiAqIEBwYXJhbSB7U29saWRpdHlQYXJhbX0gcmVzdWx0IG9mIGNvbWJpbmF0aW9uXG4gKi9cblNvbGlkaXR5UGFyYW0ucHJvdG90eXBlLmNvbWJpbmUgPSBmdW5jdGlvbiAocGFyYW0pIHtcbiAgICByZXR1cm4gbmV3IFNvbGlkaXR5UGFyYW0odGhpcy52YWx1ZSArIHBhcmFtLnZhbHVlKTsgXG59O1xuXG4vKipcbiAqIFRoaXMgbWV0aG9kIHNob3VsZCBiZSBjYWxsZWQgdG8gY2hlY2sgaWYgcGFyYW0gaGFzIGR5bmFtaWMgc2l6ZS5cbiAqIElmIGl0IGhhcywgaXQgcmV0dXJucyB0cnVlLCBvdGhlcndpc2UgZmFsc2VcbiAqXG4gKiBAbWV0aG9kIGlzRHluYW1pY1xuICogQHJldHVybnMge0Jvb2xlYW59XG4gKi9cblNvbGlkaXR5UGFyYW0ucHJvdG90eXBlLmlzRHluYW1pYyA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gdGhpcy52YWx1ZS5sZW5ndGggPiA2NCB8fCB0aGlzLm9mZnNldCAhPT0gdW5kZWZpbmVkO1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgY2FsbGVkIHRvIHRyYW5zZm9ybSBvZmZzZXQgdG8gYnl0ZXNcbiAqXG4gKiBAbWV0aG9kIG9mZnNldEFzQnl0ZXNcbiAqIEByZXR1cm5zIHtTdHJpbmd9IGJ5dGVzIHJlcHJlc2VudGF0aW9uIG9mIG9mZnNldFxuICovXG5Tb2xpZGl0eVBhcmFtLnByb3RvdHlwZS5vZmZzZXRBc0J5dGVzID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiAhdGhpcy5pc0R5bmFtaWMoKSA/ICcnIDogdXRpbHMucGFkTGVmdCh1dGlscy50b1R3b3NDb21wbGVtZW50KHRoaXMub2Zmc2V0KS50b1N0cmluZygxNiksIDY0KTtcbn07XG5cbi8qKlxuICogVGhpcyBtZXRob2Qgc2hvdWxkIGJlIGNhbGxlZCB0byBnZXQgc3RhdGljIHBhcnQgb2YgcGFyYW1cbiAqXG4gKiBAbWV0aG9kIHN0YXRpY1BhcnRcbiAqIEByZXR1cm5zIHtTdHJpbmd9IG9mZnNldCBpZiBpdCBpcyBhIGR5bmFtaWMgcGFyYW0sIG90aGVyd2lzZSB2YWx1ZVxuICovXG5Tb2xpZGl0eVBhcmFtLnByb3RvdHlwZS5zdGF0aWNQYXJ0ID0gZnVuY3Rpb24gKCkge1xuICAgIGlmICghdGhpcy5pc0R5bmFtaWMoKSkge1xuICAgICAgICByZXR1cm4gdGhpcy52YWx1ZTsgXG4gICAgfSBcbiAgICByZXR1cm4gdGhpcy5vZmZzZXRBc0J5dGVzKCk7XG59O1xuXG4vKipcbiAqIFRoaXMgbWV0aG9kIHNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IGR5bmFtaWMgcGFydCBvZiBwYXJhbVxuICpcbiAqIEBtZXRob2QgZHluYW1pY1BhcnRcbiAqIEByZXR1cm5zIHtTdHJpbmd9IHJldHVybnMgYSB2YWx1ZSBpZiBpdCBpcyBhIGR5bmFtaWMgcGFyYW0sIG90aGVyd2lzZSBlbXB0eSBzdHJpbmdcbiAqL1xuU29saWRpdHlQYXJhbS5wcm90b3R5cGUuZHluYW1pY1BhcnQgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuaXNEeW5hbWljKCkgPyB0aGlzLnZhbHVlIDogJyc7XG59O1xuXG4vKipcbiAqIFRoaXMgbWV0aG9kIHNob3VsZCBiZSBjYWxsZWQgdG8gZW5jb2RlIHBhcmFtXG4gKlxuICogQG1ldGhvZCBlbmNvZGVcbiAqIEByZXR1cm5zIHtTdHJpbmd9XG4gKi9cblNvbGlkaXR5UGFyYW0ucHJvdG90eXBlLmVuY29kZSA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gdGhpcy5zdGF0aWNQYXJ0KCkgKyB0aGlzLmR5bmFtaWNQYXJ0KCk7XG59O1xuXG4vKipcbiAqIFRoaXMgbWV0aG9kIHNob3VsZCBiZSBjYWxsZWQgdG8gZW5jb2RlIGFycmF5IG9mIHBhcmFtc1xuICpcbiAqIEBtZXRob2QgZW5jb2RlTGlzdFxuICogQHBhcmFtIHtBcnJheVtTb2xpZGl0eVBhcmFtXX0gcGFyYW1zXG4gKiBAcmV0dXJucyB7U3RyaW5nfVxuICovXG5Tb2xpZGl0eVBhcmFtLmVuY29kZUxpc3QgPSBmdW5jdGlvbiAocGFyYW1zKSB7XG4gICAgXG4gICAgLy8gdXBkYXRpbmcgb2Zmc2V0c1xuICAgIHZhciB0b3RhbE9mZnNldCA9IHBhcmFtcy5sZW5ndGggKiAzMjtcbiAgICB2YXIgb2Zmc2V0UGFyYW1zID0gcGFyYW1zLm1hcChmdW5jdGlvbiAocGFyYW0pIHtcbiAgICAgICAgaWYgKCFwYXJhbS5pc0R5bmFtaWMoKSkge1xuICAgICAgICAgICAgcmV0dXJuIHBhcmFtO1xuICAgICAgICB9XG4gICAgICAgIHZhciBvZmZzZXQgPSB0b3RhbE9mZnNldDtcbiAgICAgICAgdG90YWxPZmZzZXQgKz0gcGFyYW0uZHluYW1pY1BhcnRMZW5ndGgoKTtcbiAgICAgICAgcmV0dXJuIHBhcmFtLndpdGhPZmZzZXQob2Zmc2V0KTtcbiAgICB9KTtcblxuICAgIC8vIGVuY29kZSBldmVyeXRoaW5nIVxuICAgIHJldHVybiBvZmZzZXRQYXJhbXMucmVkdWNlKGZ1bmN0aW9uIChyZXN1bHQsIHBhcmFtKSB7XG4gICAgICAgIHJldHVybiByZXN1bHQgKyBwYXJhbS5keW5hbWljUGFydCgpO1xuICAgIH0sIG9mZnNldFBhcmFtcy5yZWR1Y2UoZnVuY3Rpb24gKHJlc3VsdCwgcGFyYW0pIHtcbiAgICAgICAgcmV0dXJuIHJlc3VsdCArIHBhcmFtLnN0YXRpY1BhcnQoKTtcbiAgICB9LCAnJykpO1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgdXNlZCB0byBkZWNvZGUgcGxhaW4gKHN0YXRpYykgc29saWRpdHkgcGFyYW0gYXQgZ2l2ZW4gaW5kZXhcbiAqXG4gKiBAbWV0aG9kIGRlY29kZVBhcmFtXG4gKiBAcGFyYW0ge1N0cmluZ30gYnl0ZXNcbiAqIEBwYXJhbSB7TnVtYmVyfSBpbmRleFxuICogQHJldHVybnMge1NvbGlkaXR5UGFyYW19XG4gKi9cblNvbGlkaXR5UGFyYW0uZGVjb2RlUGFyYW0gPSBmdW5jdGlvbiAoYnl0ZXMsIGluZGV4KSB7XG4gICAgaW5kZXggPSBpbmRleCB8fCAwO1xuICAgIHJldHVybiBuZXcgU29saWRpdHlQYXJhbShieXRlcy5zdWJzdHIoaW5kZXggKiA2NCwgNjQpKTsgXG59O1xuXG4vKipcbiAqIFRoaXMgbWV0aG9kIHNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IG9mZnNldCB2YWx1ZSBmcm9tIGJ5dGVzIGF0IGdpdmVuIGluZGV4XG4gKlxuICogQG1ldGhvZCBnZXRPZmZzZXRcbiAqIEBwYXJhbSB7U3RyaW5nfSBieXRlc1xuICogQHBhcmFtIHtOdW1iZXJ9IGluZGV4XG4gKiBAcmV0dXJucyB7TnVtYmVyfSBvZmZzZXQgYXMgbnVtYmVyXG4gKi9cbnZhciBnZXRPZmZzZXQgPSBmdW5jdGlvbiAoYnl0ZXMsIGluZGV4KSB7XG4gICAgLy8gd2UgY2FuIGRvIHRoaXMgY2F1c2Ugb2Zmc2V0IGlzIHJhdGhlciBzbWFsbFxuICAgIHJldHVybiBwYXJzZUludCgnMHgnICsgYnl0ZXMuc3Vic3RyKGluZGV4ICogNjQsIDY0KSk7XG59O1xuXG4vKipcbiAqIFRoaXMgbWV0aG9kIHNob3VsZCBiZSBjYWxsZWQgdG8gZGVjb2RlIHNvbGlkaXR5IGJ5dGVzIHBhcmFtIGF0IGdpdmVuIGluZGV4XG4gKlxuICogQG1ldGhvZCBkZWNvZGVCeXRlc1xuICogQHBhcmFtIHtTdHJpbmd9IGJ5dGVzXG4gKiBAcGFyYW0ge051bWJlcn0gaW5kZXhcbiAqIEByZXR1cm5zIHtTb2xpZGl0eVBhcmFtfVxuICovXG5Tb2xpZGl0eVBhcmFtLmRlY29kZUJ5dGVzID0gZnVuY3Rpb24gKGJ5dGVzLCBpbmRleCkge1xuICAgIGluZGV4ID0gaW5kZXggfHwgMDtcbiAgICAvL1RPRE8gYWRkIHN1cHBvcnQgZm9yIHN0cmluZ3MgbG9uZ2VyIHRoYW4gMzIgYnl0ZXNcbiAgICAvL3ZhciBsZW5ndGggPSBwYXJzZUludCgnMHgnICsgYnl0ZXMuc3Vic3RyKG9mZnNldCAqIDY0LCA2NCkpO1xuXG4gICAgdmFyIG9mZnNldCA9IGdldE9mZnNldChieXRlcywgaW5kZXgpO1xuXG4gICAgLy8gMiAqICwgY2F1c2Ugd2UgYWxzbyBwYXJzZSBsZW5ndGhcbiAgICByZXR1cm4gbmV3IFNvbGlkaXR5UGFyYW0oYnl0ZXMuc3Vic3RyKG9mZnNldCAqIDIsIDIgKiA2NCksIDApO1xufTtcblxuLyoqXG4gKiBUaGlzIG1ldGhvZCBzaG91bGQgYmUgdXNlZCB0byBkZWNvZGUgc29saWRpdHkgYXJyYXkgYXQgZ2l2ZW4gaW5kZXhcbiAqXG4gKiBAbWV0aG9kIGRlY29kZUFycmF5XG4gKiBAcGFyYW0ge1N0cmluZ30gYnl0ZXNcbiAqIEBwYXJhbSB7TnVtYmVyfSBpbmRleFxuICogQHJldHVybnMge1NvbGlkaXR5UGFyYW19XG4gKi9cblNvbGlkaXR5UGFyYW0uZGVjb2RlQXJyYXkgPSBmdW5jdGlvbiAoYnl0ZXMsIGluZGV4KSB7XG4gICAgaW5kZXggPSBpbmRleCB8fCAwO1xuICAgIHZhciBvZmZzZXQgPSBnZXRPZmZzZXQoYnl0ZXMsIGluZGV4KTtcbiAgICB2YXIgbGVuZ3RoID0gcGFyc2VJbnQoJzB4JyArIGJ5dGVzLnN1YnN0cihvZmZzZXQgKiAyLCA2NCkpO1xuICAgIHJldHVybiBuZXcgU29saWRpdHlQYXJhbShieXRlcy5zdWJzdHIob2Zmc2V0ICogMiwgKGxlbmd0aCArIDEpICogNjQpLCAwKTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gU29saWRpdHlQYXJhbTtcblxuIiwiJ3VzZSBzdHJpY3QnO1xuXG4vLyBnbyBlbnYgZG9lc24ndCBoYXZlIGFuZCBuZWVkIFhNTEh0dHBSZXF1ZXN0XG5pZiAodHlwZW9mIFhNTEh0dHBSZXF1ZXN0ID09PSAndW5kZWZpbmVkJykge1xuICAgIGV4cG9ydHMuWE1MSHR0cFJlcXVlc3QgPSB7fTtcbn0gZWxzZSB7XG4gICAgZXhwb3J0cy5YTUxIdHRwUmVxdWVzdCA9IFhNTEh0dHBSZXF1ZXN0OyAvLyBqc2hpbnQgaWdub3JlOmxpbmVcbn1cblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogQGZpbGUgY29uZmlnLmpzXG4gKiBAYXV0aG9yczpcbiAqICAgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE1XG4gKi9cblxuLyoqXG4gKiBVdGlsc1xuICogXG4gKiBAbW9kdWxlIHV0aWxzXG4gKi9cblxuLyoqXG4gKiBVdGlsaXR5IGZ1bmN0aW9uc1xuICogXG4gKiBAY2xhc3MgW3V0aWxzXSBjb25maWdcbiAqIEBjb25zdHJ1Y3RvclxuICovXG5cbi8vLyByZXF1aXJlZCB0byBkZWZpbmUgRVRIX0JJR05VTUJFUl9ST1VORElOR19NT0RFXG52YXIgQmlnTnVtYmVyID0gcmVxdWlyZSgnYmlnbnVtYmVyLmpzJyk7XG5cbnZhciBFVEhfVU5JVFMgPSBbXG4gICAgJ3dlaScsXG4gICAgJ2t3ZWknLFxuICAgICdNd2VpJyxcbiAgICAnR3dlaScsXG4gICAgJ3N6YWJvJyxcbiAgICAnZmlubmV5JyxcbiAgICAnZmVtdG9ldGhlcicsXG4gICAgJ3BpY29ldGhlcicsXG4gICAgJ25hbm9ldGhlcicsXG4gICAgJ21pY3JvZXRoZXInLFxuICAgICdtaWxsaWV0aGVyJyxcbiAgICAnbmFubycsXG4gICAgJ21pY3JvJyxcbiAgICAnbWlsbGknLFxuICAgICdldGhlcicsXG4gICAgJ2dyYW5kJyxcbiAgICAnTWV0aGVyJyxcbiAgICAnR2V0aGVyJyxcbiAgICAnVGV0aGVyJyxcbiAgICAnUGV0aGVyJyxcbiAgICAnRWV0aGVyJyxcbiAgICAnWmV0aGVyJyxcbiAgICAnWWV0aGVyJyxcbiAgICAnTmV0aGVyJyxcbiAgICAnRGV0aGVyJyxcbiAgICAnVmV0aGVyJyxcbiAgICAnVWV0aGVyJ1xuXTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgRVRIX1BBRERJTkc6IDMyLFxuICAgIEVUSF9TSUdOQVRVUkVfTEVOR1RIOiA0LFxuICAgIEVUSF9VTklUUzogRVRIX1VOSVRTLFxuICAgIEVUSF9CSUdOVU1CRVJfUk9VTkRJTkdfTU9ERTogeyBST1VORElOR19NT0RFOiBCaWdOdW1iZXIuUk9VTkRfRE9XTiB9LFxuICAgIEVUSF9QT0xMSU5HX1RJTUVPVVQ6IDEwMDAsXG4gICAgZGVmYXVsdEJsb2NrOiAnbGF0ZXN0JyxcbiAgICBkZWZhdWx0QWNjb3VudDogdW5kZWZpbmVkXG59O1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBcbiAqIEBmaWxlIHNoYTMuanNcbiAqIEBhdXRob3IgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE1XG4gKi9cblxudmFyIHV0aWxzID0gcmVxdWlyZSgnLi91dGlscycpO1xudmFyIHNoYTMgPSByZXF1aXJlKCdjcnlwdG8tanMvc2hhMycpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChzdHIsIGlzTmV3KSB7XG4gICAgaWYgKHN0ci5zdWJzdHIoMCwgMikgPT09ICcweCcgJiYgIWlzTmV3KSB7XG4gICAgICAgIGNvbnNvbGUud2FybigncmVxdWlyZW1lbnQgb2YgdXNpbmcgd2ViMy5mcm9tQXNjaWkgYmVmb3JlIHNoYTMgaXMgZGVwcmVjYXRlZCcpO1xuICAgICAgICBjb25zb2xlLndhcm4oJ25ldyB1c2FnZTogXFwnd2ViMy5zaGEzKFwiaGVsbG9cIilcXCcnKTtcbiAgICAgICAgY29uc29sZS53YXJuKCdzZWUgaHR0cHM6Ly9naXRodWIuY29tL2V0aGVyZXVtL3dlYjMuanMvcHVsbC8yMDUnKTtcbiAgICAgICAgY29uc29sZS53YXJuKCdpZiB5b3UgbmVlZCB0byBoYXNoIGhleCB2YWx1ZSwgeW91IGNhbiBkbyBcXCdzaGEzKFwiMHhmZmZcIiwgdHJ1ZSlcXCcnKTtcbiAgICAgICAgc3RyID0gdXRpbHMudG9Bc2NpaShzdHIpO1xuICAgIH1cblxuICAgIHJldHVybiBzaGEzKHN0ciwge1xuICAgICAgICBvdXRwdXRMZW5ndGg6IDI1NlxuICAgIH0pLnRvU3RyaW5nKCk7XG59O1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBcbiAqIEBmaWxlIHV0aWxzLmpzXG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbi8qKlxuICogVXRpbHNcbiAqIFxuICogQG1vZHVsZSB1dGlsc1xuICovXG5cbi8qKlxuICogVXRpbGl0eSBmdW5jdGlvbnNcbiAqIFxuICogQGNsYXNzIFt1dGlsc10gdXRpbHNcbiAqIEBjb25zdHJ1Y3RvclxuICovXG5cbnZhciBCaWdOdW1iZXIgPSByZXF1aXJlKCdiaWdudW1iZXIuanMnKTtcblxudmFyIHVuaXRNYXAgPSB7XG4gICAgJ3dlaSc6ICAgICAgICAgICcxJyxcbiAgICAna3dlaSc6ICAgICAgICAgJzEwMDAnLFxuICAgICdhZGEnOiAgICAgICAgICAnMTAwMCcsXG4gICAgJ2ZlbXRvZXRoZXInOiAgICcxMDAwJyxcbiAgICAnbXdlaSc6ICAgICAgICAgJzEwMDAwMDAnLFxuICAgICdiYWJiYWdlJzogICAgICAnMTAwMDAwMCcsXG4gICAgJ3BpY29ldGhlcic6ICAgICcxMDAwMDAwJyxcbiAgICAnZ3dlaSc6ICAgICAgICAgJzEwMDAwMDAwMDAnLFxuICAgICdzaGFubm9uJzogICAgICAnMTAwMDAwMDAwMCcsXG4gICAgJ25hbm9ldGhlcic6ICAgICcxMDAwMDAwMDAwJyxcbiAgICAnbmFubyc6ICAgICAgICAgJzEwMDAwMDAwMDAnLFxuICAgICdzemFibyc6ICAgICAgICAnMTAwMDAwMDAwMDAwMCcsXG4gICAgJ21pY3JvZXRoZXInOiAgICcxMDAwMDAwMDAwMDAwJyxcbiAgICAnbWljcm8nOiAgICAgICAgJzEwMDAwMDAwMDAwMDAnLFxuICAgICdmaW5uZXknOiAgICAgICAnMTAwMDAwMDAwMDAwMDAwMCcsXG4gICAgJ21pbGxpZXRoZXInOiAgICAnMTAwMDAwMDAwMDAwMDAwMCcsXG4gICAgJ21pbGxpJzogICAgICAgICAnMTAwMDAwMDAwMDAwMDAwMCcsXG4gICAgJ2V0aGVyJzogICAgICAgICcxMDAwMDAwMDAwMDAwMDAwMDAwJyxcbiAgICAna2V0aGVyJzogICAgICAgJzEwMDAwMDAwMDAwMDAwMDAwMDAwMDAnLFxuICAgICdncmFuZCc6ICAgICAgICAnMTAwMDAwMDAwMDAwMDAwMDAwMDAwMCcsXG4gICAgJ2VpbnN0ZWluJzogICAgICcxMDAwMDAwMDAwMDAwMDAwMDAwMDAwJyxcbiAgICAnbWV0aGVyJzogICAgICAgJzEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAnLFxuICAgICdnZXRoZXInOiAgICAgICAnMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCcsXG4gICAgJ3RldGhlcic6ICAgICAgICcxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwJ1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIHBhZCBzdHJpbmcgdG8gZXhwZWN0ZWQgbGVuZ3RoXG4gKlxuICogQG1ldGhvZCBwYWRMZWZ0XG4gKiBAcGFyYW0ge1N0cmluZ30gc3RyaW5nIHRvIGJlIHBhZGRlZFxuICogQHBhcmFtIHtOdW1iZXJ9IGNoYXJhY3RlcnMgdGhhdCByZXN1bHQgc3RyaW5nIHNob3VsZCBoYXZlXG4gKiBAcGFyYW0ge1N0cmluZ30gc2lnbiwgYnkgZGVmYXVsdCAwXG4gKiBAcmV0dXJucyB7U3RyaW5nfSByaWdodCBhbGlnbmVkIHN0cmluZ1xuICovXG52YXIgcGFkTGVmdCA9IGZ1bmN0aW9uIChzdHJpbmcsIGNoYXJzLCBzaWduKSB7XG4gICAgcmV0dXJuIG5ldyBBcnJheShjaGFycyAtIHN0cmluZy5sZW5ndGggKyAxKS5qb2luKHNpZ24gPyBzaWduIDogXCIwXCIpICsgc3RyaW5nO1xufTtcblxuLyoqIFxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBnZXQgc3RpbmcgZnJvbSBpdCdzIGhleCByZXByZXNlbnRhdGlvblxuICpcbiAqIEBtZXRob2QgdG9Bc2NpaVxuICogQHBhcmFtIHtTdHJpbmd9IHN0cmluZyBpbiBoZXhcbiAqIEByZXR1cm5zIHtTdHJpbmd9IGFzY2lpIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiBoZXggdmFsdWVcbiAqL1xudmFyIHRvQXNjaWkgPSBmdW5jdGlvbihoZXgpIHtcbi8vIEZpbmQgdGVybWluYXRpb25cbiAgICB2YXIgc3RyID0gXCJcIjtcbiAgICB2YXIgaSA9IDAsIGwgPSBoZXgubGVuZ3RoO1xuICAgIGlmIChoZXguc3Vic3RyaW5nKDAsIDIpID09PSAnMHgnKSB7XG4gICAgICAgIGkgPSAyO1xuICAgIH1cbiAgICBmb3IgKDsgaSA8IGw7IGkrPTIpIHtcbiAgICAgICAgdmFyIGNvZGUgPSBwYXJzZUludChoZXguc3Vic3RyKGksIDIpLCAxNik7XG4gICAgICAgIGlmIChjb2RlID09PSAwKSB7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuXG4gICAgICAgIHN0ciArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKGNvZGUpO1xuICAgIH1cblxuICAgIHJldHVybiBzdHI7XG59O1xuICAgIFxuLyoqXG4gKiBTaG9sZCBiZSBjYWxsZWQgdG8gZ2V0IGhleCByZXByZXNlbnRhdGlvbiAocHJlZml4ZWQgYnkgMHgpIG9mIGFzY2lpIHN0cmluZyBcbiAqXG4gKiBAbWV0aG9kIHRvSGV4TmF0aXZlXG4gKiBAcGFyYW0ge1N0cmluZ30gc3RyaW5nXG4gKiBAcmV0dXJucyB7U3RyaW5nfSBoZXggcmVwcmVzZW50YXRpb24gb2YgaW5wdXQgc3RyaW5nXG4gKi9cbnZhciB0b0hleE5hdGl2ZSA9IGZ1bmN0aW9uKHN0cikge1xuICAgIHZhciBoZXggPSBcIlwiO1xuICAgIGZvcih2YXIgaSA9IDA7IGkgPCBzdHIubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgdmFyIG4gPSBzdHIuY2hhckNvZGVBdChpKS50b1N0cmluZygxNik7XG4gICAgICAgIGhleCArPSBuLmxlbmd0aCA8IDIgPyAnMCcgKyBuIDogbjtcbiAgICB9XG5cbiAgICByZXR1cm4gaGV4O1xufTtcblxuLyoqXG4gKiBTaG9sZCBiZSBjYWxsZWQgdG8gZ2V0IGhleCByZXByZXNlbnRhdGlvbiAocHJlZml4ZWQgYnkgMHgpIG9mIGFzY2lpIHN0cmluZyBcbiAqXG4gKiBAbWV0aG9kIGZyb21Bc2NpaVxuICogQHBhcmFtIHtTdHJpbmd9IHN0cmluZ1xuICogQHBhcmFtIHtOdW1iZXJ9IG9wdGlvbmFsIHBhZGRpbmdcbiAqIEByZXR1cm5zIHtTdHJpbmd9IGhleCByZXByZXNlbnRhdGlvbiBvZiBpbnB1dCBzdHJpbmdcbiAqL1xudmFyIGZyb21Bc2NpaSA9IGZ1bmN0aW9uKHN0ciwgcGFkKSB7XG4gICAgcGFkID0gcGFkID09PSB1bmRlZmluZWQgPyAwIDogcGFkO1xuICAgIHZhciBoZXggPSB0b0hleE5hdGl2ZShzdHIpO1xuICAgIHdoaWxlIChoZXgubGVuZ3RoIDwgcGFkKjIpXG4gICAgICAgIGhleCArPSBcIjAwXCI7XG4gICAgcmV0dXJuIFwiMHhcIiArIGhleDtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gY3JlYXRlIGZ1bGwgZnVuY3Rpb24vZXZlbnQgbmFtZSBmcm9tIGpzb24gYWJpXG4gKlxuICogQG1ldGhvZCB0cmFuc2Zvcm1Ub0Z1bGxOYW1lXG4gKiBAcGFyYW0ge09iamVjdH0ganNvbi1hYmlcbiAqIEByZXR1cm4ge1N0cmluZ30gZnVsbCBmbmN0aW9uL2V2ZW50IG5hbWVcbiAqL1xudmFyIHRyYW5zZm9ybVRvRnVsbE5hbWUgPSBmdW5jdGlvbiAoanNvbikge1xuICAgIGlmIChqc29uLm5hbWUuaW5kZXhPZignKCcpICE9PSAtMSkge1xuICAgICAgICByZXR1cm4ganNvbi5uYW1lO1xuICAgIH1cblxuICAgIHZhciB0eXBlTmFtZSA9IGpzb24uaW5wdXRzLm1hcChmdW5jdGlvbihpKXtyZXR1cm4gaS50eXBlOyB9KS5qb2luKCk7XG4gICAgcmV0dXJuIGpzb24ubmFtZSArICcoJyArIHR5cGVOYW1lICsgJyknO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGdldCBkaXNwbGF5IG5hbWUgb2YgY29udHJhY3QgZnVuY3Rpb25cbiAqIFxuICogQG1ldGhvZCBleHRyYWN0RGlzcGxheU5hbWVcbiAqIEBwYXJhbSB7U3RyaW5nfSBuYW1lIG9mIGZ1bmN0aW9uL2V2ZW50XG4gKiBAcmV0dXJucyB7U3RyaW5nfSBkaXNwbGF5IG5hbWUgZm9yIGZ1bmN0aW9uL2V2ZW50IGVnLiBtdWx0aXBseSh1aW50MjU2KSAtPiBtdWx0aXBseVxuICovXG52YXIgZXh0cmFjdERpc3BsYXlOYW1lID0gZnVuY3Rpb24gKG5hbWUpIHtcbiAgICB2YXIgbGVuZ3RoID0gbmFtZS5pbmRleE9mKCcoJyk7IFxuICAgIHJldHVybiBsZW5ndGggIT09IC0xID8gbmFtZS5zdWJzdHIoMCwgbGVuZ3RoKSA6IG5hbWU7XG59O1xuXG4vLy8gQHJldHVybnMgb3ZlcmxvYWRlZCBwYXJ0IG9mIGZ1bmN0aW9uL2V2ZW50IG5hbWVcbnZhciBleHRyYWN0VHlwZU5hbWUgPSBmdW5jdGlvbiAobmFtZSkge1xuICAgIC8vLyBUT0RPOiBtYWtlIGl0IGludnVsbmVyYWJsZVxuICAgIHZhciBsZW5ndGggPSBuYW1lLmluZGV4T2YoJygnKTtcbiAgICByZXR1cm4gbGVuZ3RoICE9PSAtMSA/IG5hbWUuc3Vic3RyKGxlbmd0aCArIDEsIG5hbWUubGVuZ3RoIC0gMSAtIChsZW5ndGggKyAxKSkucmVwbGFjZSgnICcsICcnKSA6IFwiXCI7XG59O1xuXG4vKipcbiAqIENvbnZlcnRzIHZhbHVlIHRvIGl0J3MgZGVjaW1hbCByZXByZXNlbnRhdGlvbiBpbiBzdHJpbmdcbiAqXG4gKiBAbWV0aG9kIHRvRGVjaW1hbFxuICogQHBhcmFtIHtTdHJpbmd8TnVtYmVyfEJpZ051bWJlcn1cbiAqIEByZXR1cm4ge1N0cmluZ31cbiAqL1xudmFyIHRvRGVjaW1hbCA9IGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgIHJldHVybiB0b0JpZ051bWJlcih2YWx1ZSkudG9OdW1iZXIoKTtcbn07XG5cbi8qKlxuICogQ29udmVydHMgdmFsdWUgdG8gaXQncyBoZXggcmVwcmVzZW50YXRpb25cbiAqXG4gKiBAbWV0aG9kIGZyb21EZWNpbWFsXG4gKiBAcGFyYW0ge1N0cmluZ3xOdW1iZXJ8QmlnTnVtYmVyfVxuICogQHJldHVybiB7U3RyaW5nfVxuICovXG52YXIgZnJvbURlY2ltYWwgPSBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICB2YXIgbnVtYmVyID0gdG9CaWdOdW1iZXIodmFsdWUpO1xuICAgIHZhciByZXN1bHQgPSBudW1iZXIudG9TdHJpbmcoMTYpO1xuXG4gICAgcmV0dXJuIG51bWJlci5sZXNzVGhhbigwKSA/ICctMHgnICsgcmVzdWx0LnN1YnN0cigxKSA6ICcweCcgKyByZXN1bHQ7XG59O1xuXG4vKipcbiAqIEF1dG8gY29udmVydHMgYW55IGdpdmVuIHZhbHVlIGludG8gaXQncyBoZXggcmVwcmVzZW50YXRpb24uXG4gKlxuICogQW5kIGV2ZW4gc3RyaW5naWZ5cyBvYmplY3RzIGJlZm9yZS5cbiAqXG4gKiBAbWV0aG9kIHRvSGV4XG4gKiBAcGFyYW0ge1N0cmluZ3xOdW1iZXJ8QmlnTnVtYmVyfE9iamVjdH1cbiAqIEByZXR1cm4ge1N0cmluZ31cbiAqL1xudmFyIHRvSGV4ID0gZnVuY3Rpb24gKHZhbCkge1xuICAgIC8qanNoaW50IG1heGNvbXBsZXhpdHk6NyAqL1xuXG4gICAgaWYgKGlzQm9vbGVhbih2YWwpKVxuICAgICAgICByZXR1cm4gZnJvbURlY2ltYWwoK3ZhbCk7XG5cbiAgICBpZiAoaXNCaWdOdW1iZXIodmFsKSlcbiAgICAgICAgcmV0dXJuIGZyb21EZWNpbWFsKHZhbCk7XG5cbiAgICBpZiAoaXNPYmplY3QodmFsKSlcbiAgICAgICAgcmV0dXJuIGZyb21Bc2NpaShKU09OLnN0cmluZ2lmeSh2YWwpKTtcblxuICAgIC8vIGlmIGl0cyBhIG5lZ2F0aXZlIG51bWJlciwgcGFzcyBpdCB0aHJvdWdoIGZyb21EZWNpbWFsXG4gICAgaWYgKGlzU3RyaW5nKHZhbCkpIHtcbiAgICAgICAgaWYgKHZhbC5pbmRleE9mKCctMHgnKSA9PT0gMClcbiAgICAgICAgICAgcmV0dXJuIGZyb21EZWNpbWFsKHZhbCk7XG4gICAgICAgIGVsc2UgaWYgKCFpc0Zpbml0ZSh2YWwpKVxuICAgICAgICAgICAgcmV0dXJuIGZyb21Bc2NpaSh2YWwpO1xuICAgIH1cblxuICAgIHJldHVybiBmcm9tRGVjaW1hbCh2YWwpO1xufTtcblxuLyoqXG4gKiBSZXR1cm5zIHZhbHVlIG9mIHVuaXQgaW4gV2VpXG4gKlxuICogQG1ldGhvZCBnZXRWYWx1ZU9mVW5pdFxuICogQHBhcmFtIHtTdHJpbmd9IHVuaXQgdGhlIHVuaXQgdG8gY29udmVydCB0bywgZGVmYXVsdCBldGhlclxuICogQHJldHVybnMge0JpZ051bWJlcn0gdmFsdWUgb2YgdGhlIHVuaXQgKGluIFdlaSlcbiAqIEB0aHJvd3MgZXJyb3IgaWYgdGhlIHVuaXQgaXMgbm90IGNvcnJlY3Q6d1xuICovXG52YXIgZ2V0VmFsdWVPZlVuaXQgPSBmdW5jdGlvbiAodW5pdCkge1xuICAgIHVuaXQgPSB1bml0ID8gdW5pdC50b0xvd2VyQ2FzZSgpIDogJ2V0aGVyJztcbiAgICB2YXIgdW5pdFZhbHVlID0gdW5pdE1hcFt1bml0XTtcbiAgICBpZiAodW5pdFZhbHVlID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdUaGlzIHVuaXQgZG9lc25cXCd0IGV4aXN0cywgcGxlYXNlIHVzZSB0aGUgb25lIG9mIHRoZSBmb2xsb3dpbmcgdW5pdHMnICsgSlNPTi5zdHJpbmdpZnkodW5pdE1hcCwgbnVsbCwgMikpO1xuICAgIH1cbiAgICByZXR1cm4gbmV3IEJpZ051bWJlcih1bml0VmFsdWUsIDEwKTtcbn07XG5cbi8qKlxuICogVGFrZXMgYSBudW1iZXIgb2Ygd2VpIGFuZCBjb252ZXJ0cyBpdCB0byBhbnkgb3RoZXIgZXRoZXIgdW5pdC5cbiAqXG4gKiBQb3NzaWJsZSB1bml0cyBhcmU6XG4gKiAgIFNJIFNob3J0ICAgU0kgRnVsbCAgICAgICAgRWZmaWd5ICAgICAgIE90aGVyXG4gKiAtIGt3ZWkgICAgICAgZmVtdG9ldGhlciAgICAgYWRhXG4gKiAtIG13ZWkgICAgICAgcGljb2V0aGVyICAgICAgYmFiYmFnZVxuICogLSBnd2VpICAgICAgIG5hbm9ldGhlciAgICAgIHNoYW5ub24gICAgICBuYW5vXG4gKiAtIC0tICAgICAgICAgbWljcm9ldGhlciAgICAgc3phYm8gICAgICAgIG1pY3JvXG4gKiAtIC0tICAgICAgICAgbWlsbGlldGhlciAgICAgZmlubmV5ICAgICAgIG1pbGxpXG4gKiAtIGV0aGVyICAgICAgLS0gICAgICAgICAgICAgLS1cbiAqIC0ga2V0aGVyICAgICAgICAgICAgICAgICAgICBlaW5zdGVpbiAgICAgZ3JhbmQgXG4gKiAtIG1ldGhlclxuICogLSBnZXRoZXJcbiAqIC0gdGV0aGVyXG4gKlxuICogQG1ldGhvZCBmcm9tV2VpXG4gKiBAcGFyYW0ge051bWJlcnxTdHJpbmd9IG51bWJlciBjYW4gYmUgYSBudW1iZXIsIG51bWJlciBzdHJpbmcgb3IgYSBIRVggb2YgYSBkZWNpbWFsXG4gKiBAcGFyYW0ge1N0cmluZ30gdW5pdCB0aGUgdW5pdCB0byBjb252ZXJ0IHRvLCBkZWZhdWx0IGV0aGVyXG4gKiBAcmV0dXJuIHtTdHJpbmd8T2JqZWN0fSBXaGVuIGdpdmVuIGEgQmlnTnVtYmVyIG9iamVjdCBpdCByZXR1cm5zIG9uZSBhcyB3ZWxsLCBvdGhlcndpc2UgYSBudW1iZXJcbiovXG52YXIgZnJvbVdlaSA9IGZ1bmN0aW9uKG51bWJlciwgdW5pdCkge1xuICAgIHZhciByZXR1cm5WYWx1ZSA9IHRvQmlnTnVtYmVyKG51bWJlcikuZGl2aWRlZEJ5KGdldFZhbHVlT2ZVbml0KHVuaXQpKTtcblxuICAgIHJldHVybiBpc0JpZ051bWJlcihudW1iZXIpID8gcmV0dXJuVmFsdWUgOiByZXR1cm5WYWx1ZS50b1N0cmluZygxMCk7IFxufTtcblxuLyoqXG4gKiBUYWtlcyBhIG51bWJlciBvZiBhIHVuaXQgYW5kIGNvbnZlcnRzIGl0IHRvIHdlaS5cbiAqXG4gKiBQb3NzaWJsZSB1bml0cyBhcmU6XG4gKiAgIFNJIFNob3J0ICAgU0kgRnVsbCAgICAgICAgRWZmaWd5ICAgICAgIE90aGVyXG4gKiAtIGt3ZWkgICAgICAgZmVtdG9ldGhlciAgICAgYWRhXG4gKiAtIG13ZWkgICAgICAgcGljb2V0aGVyICAgICAgYmFiYmFnZSAgICAgICBcbiAqIC0gZ3dlaSAgICAgICBuYW5vZXRoZXIgICAgICBzaGFubm9uICAgICAgbmFub1xuICogLSAtLSAgICAgICAgIG1pY3JvZXRoZXIgICAgIHN6YWJvICAgICAgICBtaWNyb1xuICogLSAtLSAgICAgICAgIG1pbGxpZXRoZXIgICAgIGZpbm5leSAgICAgICBtaWxsaVxuICogLSBldGhlciAgICAgIC0tICAgICAgICAgICAgIC0tXG4gKiAtIGtldGhlciAgICAgICAgICAgICAgICAgICAgZWluc3RlaW4gICAgIGdyYW5kIFxuICogLSBtZXRoZXJcbiAqIC0gZ2V0aGVyXG4gKiAtIHRldGhlclxuICpcbiAqIEBtZXRob2QgdG9XZWlcbiAqIEBwYXJhbSB7TnVtYmVyfFN0cmluZ3xCaWdOdW1iZXJ9IG51bWJlciBjYW4gYmUgYSBudW1iZXIsIG51bWJlciBzdHJpbmcgb3IgYSBIRVggb2YgYSBkZWNpbWFsXG4gKiBAcGFyYW0ge1N0cmluZ30gdW5pdCB0aGUgdW5pdCB0byBjb252ZXJ0IGZyb20sIGRlZmF1bHQgZXRoZXJcbiAqIEByZXR1cm4ge1N0cmluZ3xPYmplY3R9IFdoZW4gZ2l2ZW4gYSBCaWdOdW1iZXIgb2JqZWN0IGl0IHJldHVybnMgb25lIGFzIHdlbGwsIG90aGVyd2lzZSBhIG51bWJlclxuKi9cbnZhciB0b1dlaSA9IGZ1bmN0aW9uKG51bWJlciwgdW5pdCkge1xuICAgIHZhciByZXR1cm5WYWx1ZSA9IHRvQmlnTnVtYmVyKG51bWJlcikudGltZXMoZ2V0VmFsdWVPZlVuaXQodW5pdCkpO1xuXG4gICAgcmV0dXJuIGlzQmlnTnVtYmVyKG51bWJlcikgPyByZXR1cm5WYWx1ZSA6IHJldHVyblZhbHVlLnRvU3RyaW5nKDEwKTsgXG59O1xuXG4vKipcbiAqIFRha2VzIGFuIGlucHV0IGFuZCB0cmFuc2Zvcm1zIGl0IGludG8gYW4gYmlnbnVtYmVyXG4gKlxuICogQG1ldGhvZCB0b0JpZ051bWJlclxuICogQHBhcmFtIHtOdW1iZXJ8U3RyaW5nfEJpZ051bWJlcn0gYSBudW1iZXIsIHN0cmluZywgSEVYIHN0cmluZyBvciBCaWdOdW1iZXJcbiAqIEByZXR1cm4ge0JpZ051bWJlcn0gQmlnTnVtYmVyXG4qL1xudmFyIHRvQmlnTnVtYmVyID0gZnVuY3Rpb24obnVtYmVyKSB7XG4gICAgLypqc2hpbnQgbWF4Y29tcGxleGl0eTo1ICovXG4gICAgbnVtYmVyID0gbnVtYmVyIHx8IDA7XG4gICAgaWYgKGlzQmlnTnVtYmVyKG51bWJlcikpXG4gICAgICAgIHJldHVybiBudW1iZXI7XG5cbiAgICBpZiAoaXNTdHJpbmcobnVtYmVyKSAmJiAobnVtYmVyLmluZGV4T2YoJzB4JykgPT09IDAgfHwgbnVtYmVyLmluZGV4T2YoJy0weCcpID09PSAwKSkge1xuICAgICAgICByZXR1cm4gbmV3IEJpZ051bWJlcihudW1iZXIucmVwbGFjZSgnMHgnLCcnKSwgMTYpO1xuICAgIH1cbiAgIFxuICAgIHJldHVybiBuZXcgQmlnTnVtYmVyKG51bWJlci50b1N0cmluZygxMCksIDEwKTtcbn07XG5cbi8qKlxuICogVGFrZXMgYW5kIGlucHV0IHRyYW5zZm9ybXMgaXQgaW50byBiaWdudW1iZXIgYW5kIGlmIGl0IGlzIG5lZ2F0aXZlIHZhbHVlLCBpbnRvIHR3bydzIGNvbXBsZW1lbnRcbiAqXG4gKiBAbWV0aG9kIHRvVHdvc0NvbXBsZW1lbnRcbiAqIEBwYXJhbSB7TnVtYmVyfFN0cmluZ3xCaWdOdW1iZXJ9XG4gKiBAcmV0dXJuIHtCaWdOdW1iZXJ9XG4gKi9cbnZhciB0b1R3b3NDb21wbGVtZW50ID0gZnVuY3Rpb24gKG51bWJlcikge1xuICAgIHZhciBiaWdOdW1iZXIgPSB0b0JpZ051bWJlcihudW1iZXIpO1xuICAgIGlmIChiaWdOdW1iZXIubGVzc1RoYW4oMCkpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBCaWdOdW1iZXIoXCJmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmXCIsIDE2KS5wbHVzKGJpZ051bWJlcikucGx1cygxKTtcbiAgICB9XG4gICAgcmV0dXJuIGJpZ051bWJlcjtcbn07XG5cbi8qKlxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBzdHJpbmcgaXMgc3RyaWN0bHkgYW4gYWRkcmVzc1xuICpcbiAqIEBtZXRob2QgaXNTdHJpY3RBZGRyZXNzXG4gKiBAcGFyYW0ge1N0cmluZ30gYWRkcmVzcyB0aGUgZ2l2ZW4gSEVYIGFkcmVzc1xuICogQHJldHVybiB7Qm9vbGVhbn1cbiovXG52YXIgaXNTdHJpY3RBZGRyZXNzID0gZnVuY3Rpb24gKGFkZHJlc3MpIHtcbiAgICByZXR1cm4gL14weFswLTlhLWZdezQwfSQvLnRlc3QoYWRkcmVzcyk7XG59O1xuXG4vKipcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gc3RyaW5nIGlzIGFuIGFkZHJlc3NcbiAqXG4gKiBAbWV0aG9kIGlzQWRkcmVzc1xuICogQHBhcmFtIHtTdHJpbmd9IGFkZHJlc3MgdGhlIGdpdmVuIEhFWCBhZHJlc3NcbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4qL1xudmFyIGlzQWRkcmVzcyA9IGZ1bmN0aW9uIChhZGRyZXNzKSB7XG4gICAgcmV0dXJuIC9eKDB4KT9bMC05YS1mXXs0MH0kLy50ZXN0KGFkZHJlc3MpO1xufTtcblxuLyoqXG4gKiBUcmFuc2Zvcm1zIGdpdmVuIHN0cmluZyB0byB2YWxpZCAyMCBieXRlcy1sZW5ndGggYWRkcmVzIHdpdGggMHggcHJlZml4XG4gKlxuICogQG1ldGhvZCB0b0FkZHJlc3NcbiAqIEBwYXJhbSB7U3RyaW5nfSBhZGRyZXNzXG4gKiBAcmV0dXJuIHtTdHJpbmd9IGZvcm1hdHRlZCBhZGRyZXNzXG4gKi9cbnZhciB0b0FkZHJlc3MgPSBmdW5jdGlvbiAoYWRkcmVzcykge1xuICAgIGlmIChpc1N0cmljdEFkZHJlc3MoYWRkcmVzcykpIHtcbiAgICAgICAgcmV0dXJuIGFkZHJlc3M7XG4gICAgfVxuICAgIFxuICAgIGlmICgvXlswLTlhLWZdezQwfSQvLnRlc3QoYWRkcmVzcykpIHtcbiAgICAgICAgcmV0dXJuICcweCcgKyBhZGRyZXNzO1xuICAgIH1cblxuICAgIHJldHVybiAnMHgnICsgcGFkTGVmdCh0b0hleChhZGRyZXNzKS5zdWJzdHIoMiksIDQwKTtcbn07XG5cblxuLyoqXG4gKiBSZXR1cm5zIHRydWUgaWYgb2JqZWN0IGlzIEJpZ051bWJlciwgb3RoZXJ3aXNlIGZhbHNlXG4gKlxuICogQG1ldGhvZCBpc0JpZ051bWJlclxuICogQHBhcmFtIHtPYmplY3R9XG4gKiBAcmV0dXJuIHtCb29sZWFufSBcbiAqL1xudmFyIGlzQmlnTnVtYmVyID0gZnVuY3Rpb24gKG9iamVjdCkge1xuICAgIHJldHVybiBvYmplY3QgaW5zdGFuY2VvZiBCaWdOdW1iZXIgfHxcbiAgICAgICAgKG9iamVjdCAmJiBvYmplY3QuY29uc3RydWN0b3IgJiYgb2JqZWN0LmNvbnN0cnVjdG9yLm5hbWUgPT09ICdCaWdOdW1iZXInKTtcbn07XG5cbi8qKlxuICogUmV0dXJucyB0cnVlIGlmIG9iamVjdCBpcyBzdHJpbmcsIG90aGVyd2lzZSBmYWxzZVxuICogXG4gKiBAbWV0aG9kIGlzU3RyaW5nXG4gKiBAcGFyYW0ge09iamVjdH1cbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4gKi9cbnZhciBpc1N0cmluZyA9IGZ1bmN0aW9uIChvYmplY3QpIHtcbiAgICByZXR1cm4gdHlwZW9mIG9iamVjdCA9PT0gJ3N0cmluZycgfHxcbiAgICAgICAgKG9iamVjdCAmJiBvYmplY3QuY29uc3RydWN0b3IgJiYgb2JqZWN0LmNvbnN0cnVjdG9yLm5hbWUgPT09ICdTdHJpbmcnKTtcbn07XG5cbi8qKlxuICogUmV0dXJucyB0cnVlIGlmIG9iamVjdCBpcyBmdW5jdGlvbiwgb3RoZXJ3aXNlIGZhbHNlXG4gKlxuICogQG1ldGhvZCBpc0Z1bmN0aW9uXG4gKiBAcGFyYW0ge09iamVjdH1cbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4gKi9cbnZhciBpc0Z1bmN0aW9uID0gZnVuY3Rpb24gKG9iamVjdCkge1xuICAgIHJldHVybiB0eXBlb2Ygb2JqZWN0ID09PSAnZnVuY3Rpb24nO1xufTtcblxuLyoqXG4gKiBSZXR1cm5zIHRydWUgaWYgb2JqZWN0IGlzIE9iamV0LCBvdGhlcndpc2UgZmFsc2VcbiAqXG4gKiBAbWV0aG9kIGlzT2JqZWN0XG4gKiBAcGFyYW0ge09iamVjdH1cbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4gKi9cbnZhciBpc09iamVjdCA9IGZ1bmN0aW9uIChvYmplY3QpIHtcbiAgICByZXR1cm4gdHlwZW9mIG9iamVjdCA9PT0gJ29iamVjdCc7XG59O1xuXG4vKipcbiAqIFJldHVybnMgdHJ1ZSBpZiBvYmplY3QgaXMgYm9vbGVhbiwgb3RoZXJ3aXNlIGZhbHNlXG4gKlxuICogQG1ldGhvZCBpc0Jvb2xlYW5cbiAqIEBwYXJhbSB7T2JqZWN0fVxuICogQHJldHVybiB7Qm9vbGVhbn1cbiAqL1xudmFyIGlzQm9vbGVhbiA9IGZ1bmN0aW9uIChvYmplY3QpIHtcbiAgICByZXR1cm4gdHlwZW9mIG9iamVjdCA9PT0gJ2Jvb2xlYW4nO1xufTtcblxuLyoqXG4gKiBSZXR1cm5zIHRydWUgaWYgb2JqZWN0IGlzIGFycmF5LCBvdGhlcndpc2UgZmFsc2VcbiAqXG4gKiBAbWV0aG9kIGlzQXJyYXlcbiAqIEBwYXJhbSB7T2JqZWN0fVxuICogQHJldHVybiB7Qm9vbGVhbn1cbiAqL1xudmFyIGlzQXJyYXkgPSBmdW5jdGlvbiAob2JqZWN0KSB7XG4gICAgcmV0dXJuIG9iamVjdCBpbnN0YW5jZW9mIEFycmF5OyBcbn07XG5cbi8qKlxuICogUmV0dXJucyB0cnVlIGlmIGdpdmVuIHN0cmluZyBpcyB2YWxpZCBqc29uIG9iamVjdFxuICogXG4gKiBAbWV0aG9kIGlzSnNvblxuICogQHBhcmFtIHtTdHJpbmd9XG4gKiBAcmV0dXJuIHtCb29sZWFufVxuICovXG52YXIgaXNKc29uID0gZnVuY3Rpb24gKHN0cikge1xuICAgIHRyeSB7XG4gICAgICAgIHJldHVybiAhIUpTT04ucGFyc2Uoc3RyKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG59O1xuXG4vKipcbiAqIFRoaXMgbWV0aG9kIHNob3VsZCBiZSBjYWxsZWQgdG8gY2hlY2sgaWYgc3RyaW5nIGlzIHZhbGlkIGV0aGVyZXVtIElCQU4gbnVtYmVyXG4gKiBTdXBwb3J0cyBkaXJlY3QgYW5kIGluZGlyZWN0IElCQU5zXG4gKlxuICogQG1ldGhvZCBpc0lCQU5cbiAqIEBwYXJhbSB7U3RyaW5nfVxuICogQHJldHVybiB7Qm9vbGVhbn1cbiAqL1xudmFyIGlzSUJBTiA9IGZ1bmN0aW9uIChpYmFuKSB7XG4gICAgcmV0dXJuIC9eWEVbMC05XXsyfShFVEhbMC05QS1aXXsxM318WzAtOUEtWl17MzB9KSQvLnRlc3QoaWJhbik7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBwYWRMZWZ0OiBwYWRMZWZ0LFxuICAgIHRvSGV4OiB0b0hleCxcbiAgICB0b0RlY2ltYWw6IHRvRGVjaW1hbCxcbiAgICBmcm9tRGVjaW1hbDogZnJvbURlY2ltYWwsXG4gICAgdG9Bc2NpaTogdG9Bc2NpaSxcbiAgICBmcm9tQXNjaWk6IGZyb21Bc2NpaSxcbiAgICB0cmFuc2Zvcm1Ub0Z1bGxOYW1lOiB0cmFuc2Zvcm1Ub0Z1bGxOYW1lLFxuICAgIGV4dHJhY3REaXNwbGF5TmFtZTogZXh0cmFjdERpc3BsYXlOYW1lLFxuICAgIGV4dHJhY3RUeXBlTmFtZTogZXh0cmFjdFR5cGVOYW1lLFxuICAgIHRvV2VpOiB0b1dlaSxcbiAgICBmcm9tV2VpOiBmcm9tV2VpLFxuICAgIHRvQmlnTnVtYmVyOiB0b0JpZ051bWJlcixcbiAgICB0b1R3b3NDb21wbGVtZW50OiB0b1R3b3NDb21wbGVtZW50LFxuICAgIHRvQWRkcmVzczogdG9BZGRyZXNzLFxuICAgIGlzQmlnTnVtYmVyOiBpc0JpZ051bWJlcixcbiAgICBpc1N0cmljdEFkZHJlc3M6IGlzU3RyaWN0QWRkcmVzcyxcbiAgICBpc0FkZHJlc3M6IGlzQWRkcmVzcyxcbiAgICBpc0Z1bmN0aW9uOiBpc0Z1bmN0aW9uLFxuICAgIGlzU3RyaW5nOiBpc1N0cmluZyxcbiAgICBpc09iamVjdDogaXNPYmplY3QsXG4gICAgaXNCb29sZWFuOiBpc0Jvb2xlYW4sXG4gICAgaXNBcnJheTogaXNBcnJheSxcbiAgICBpc0pzb246IGlzSnNvbixcbiAgICBpc0lCQU46IGlzSUJBTlxufTtcblxuIiwibW9kdWxlLmV4cG9ydHM9e1xuICAgIFwidmVyc2lvblwiOiBcIjAuNS4wXCJcbn1cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIEBmaWxlIHdlYjMuanNcbiAqIEBhdXRob3JzOlxuICogICBKZWZmcmV5IFdpbGNrZSA8amVmZkBldGhkZXYuY29tPlxuICogICBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqICAgTWFyaWFuIE9hbmNlYSA8bWFyaWFuQGV0aGRldi5jb20+XG4gKiAgIEZhYmlhbiBWb2dlbHN0ZWxsZXIgPGZhYmlhbkBldGhkZXYuY29tPlxuICogICBHYXYgV29vZCA8Z0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNFxuICovXG5cbnZhciB2ZXJzaW9uID0gcmVxdWlyZSgnLi92ZXJzaW9uLmpzb24nKTtcbnZhciBuZXQgPSByZXF1aXJlKCcuL3dlYjMvbmV0Jyk7XG52YXIgZXRoID0gcmVxdWlyZSgnLi93ZWIzL2V0aCcpO1xudmFyIGRiID0gcmVxdWlyZSgnLi93ZWIzL2RiJyk7XG52YXIgc2hoID0gcmVxdWlyZSgnLi93ZWIzL3NoaCcpO1xudmFyIHdhdGNoZXMgPSByZXF1aXJlKCcuL3dlYjMvd2F0Y2hlcycpO1xudmFyIEZpbHRlciA9IHJlcXVpcmUoJy4vd2ViMy9maWx0ZXInKTtcbnZhciB1dGlscyA9IHJlcXVpcmUoJy4vdXRpbHMvdXRpbHMnKTtcbnZhciBmb3JtYXR0ZXJzID0gcmVxdWlyZSgnLi93ZWIzL2Zvcm1hdHRlcnMnKTtcbnZhciBSZXF1ZXN0TWFuYWdlciA9IHJlcXVpcmUoJy4vd2ViMy9yZXF1ZXN0bWFuYWdlcicpO1xudmFyIE1ldGhvZCA9IHJlcXVpcmUoJy4vd2ViMy9tZXRob2QnKTtcbnZhciBjID0gcmVxdWlyZSgnLi91dGlscy9jb25maWcnKTtcbnZhciBQcm9wZXJ0eSA9IHJlcXVpcmUoJy4vd2ViMy9wcm9wZXJ0eScpO1xudmFyIEJhdGNoID0gcmVxdWlyZSgnLi93ZWIzL2JhdGNoJyk7XG52YXIgc2hhMyA9IHJlcXVpcmUoJy4vdXRpbHMvc2hhMycpO1xuXG52YXIgd2ViM1Byb3BlcnRpZXMgPSBbXG4gICAgbmV3IFByb3BlcnR5KHtcbiAgICAgICAgbmFtZTogJ3ZlcnNpb24uY2xpZW50JyxcbiAgICAgICAgZ2V0dGVyOiAnd2ViM19jbGllbnRWZXJzaW9uJ1xuICAgIH0pLFxuICAgIG5ldyBQcm9wZXJ0eSh7XG4gICAgICAgIG5hbWU6ICd2ZXJzaW9uLm5ldHdvcmsnLFxuICAgICAgICBnZXR0ZXI6ICduZXRfdmVyc2lvbicsXG4gICAgICAgIGlucHV0Rm9ybWF0dGVyOiB1dGlscy50b0RlY2ltYWxcbiAgICB9KSxcbiAgICBuZXcgUHJvcGVydHkoe1xuICAgICAgICBuYW1lOiAndmVyc2lvbi5ldGhlcmV1bScsXG4gICAgICAgIGdldHRlcjogJ2V0aF9wcm90b2NvbFZlcnNpb24nLFxuICAgICAgICBpbnB1dEZvcm1hdHRlcjogdXRpbHMudG9EZWNpbWFsXG4gICAgfSksXG4gICAgbmV3IFByb3BlcnR5KHtcbiAgICAgICAgbmFtZTogJ3ZlcnNpb24ud2hpc3BlcicsXG4gICAgICAgIGdldHRlcjogJ3NoaF92ZXJzaW9uJyxcbiAgICAgICAgaW5wdXRGb3JtYXR0ZXI6IHV0aWxzLnRvRGVjaW1hbFxuICAgIH0pXG5dO1xuXG4vLy8gY3JlYXRlcyBtZXRob2RzIGluIGEgZ2l2ZW4gb2JqZWN0IGJhc2VkIG9uIG1ldGhvZCBkZXNjcmlwdGlvbiBvbiBpbnB1dFxuLy8vIHNldHVwcyBhcGkgY2FsbHMgZm9yIHRoZXNlIG1ldGhvZHNcbnZhciBzZXR1cE1ldGhvZHMgPSBmdW5jdGlvbiAob2JqLCBtZXRob2RzKSB7XG4gICAgbWV0aG9kcy5mb3JFYWNoKGZ1bmN0aW9uIChtZXRob2QpIHtcbiAgICAgICAgbWV0aG9kLmF0dGFjaFRvT2JqZWN0KG9iaik7XG4gICAgfSk7XG59O1xuXG4vLy8gY3JlYXRlcyBwcm9wZXJ0aWVzIGluIGEgZ2l2ZW4gb2JqZWN0IGJhc2VkIG9uIHByb3BlcnRpZXMgZGVzY3JpcHRpb24gb24gaW5wdXRcbi8vLyBzZXR1cHMgYXBpIGNhbGxzIGZvciB0aGVzZSBwcm9wZXJ0aWVzXG52YXIgc2V0dXBQcm9wZXJ0aWVzID0gZnVuY3Rpb24gKG9iaiwgcHJvcGVydGllcykge1xuICAgIHByb3BlcnRpZXMuZm9yRWFjaChmdW5jdGlvbiAocHJvcGVydHkpIHtcbiAgICAgICAgcHJvcGVydHkuYXR0YWNoVG9PYmplY3Qob2JqKTtcbiAgICB9KTtcbn07XG5cbi8vLyBzZXR1cHMgd2ViMyBvYmplY3QsIGFuZCBpdCdzIGluLWJyb3dzZXIgZXhlY3V0ZWQgbWV0aG9kc1xudmFyIHdlYjMgPSB7fTtcbndlYjMucHJvdmlkZXJzID0ge307XG53ZWIzLnZlcnNpb24gPSB7fTtcbndlYjMudmVyc2lvbi5hcGkgPSB2ZXJzaW9uLnZlcnNpb247XG53ZWIzLmV0aCA9IHt9O1xuXG4vKmpzaGludCBtYXhwYXJhbXM6NCAqL1xud2ViMy5ldGguZmlsdGVyID0gZnVuY3Rpb24gKGZpbCwgZXZlbnRQYXJhbXMsIG9wdGlvbnMsIGZvcm1hdHRlcikge1xuXG4gICAgLy8gaWYgaXRzIGV2ZW50LCB0cmVhdCBpdCBkaWZmZXJlbnRseVxuICAgIC8vIFRPRE86IHNpbXBsaWZ5IGFuZCByZW1vdmVcbiAgICBpZiAoZmlsLl9pc0V2ZW50KSB7XG4gICAgICAgIHJldHVybiBmaWwoZXZlbnRQYXJhbXMsIG9wdGlvbnMpO1xuICAgIH1cblxuICAgIC8vIHdoYXQgb3V0cHV0TG9nRm9ybWF0dGVyPyB0aGF0J3Mgd3JvbmdcbiAgICAvL3JldHVybiBuZXcgRmlsdGVyKGZpbCwgd2F0Y2hlcy5ldGgoKSwgZm9ybWF0dGVycy5vdXRwdXRMb2dGb3JtYXR0ZXIpO1xuICAgIHJldHVybiBuZXcgRmlsdGVyKGZpbCwgd2F0Y2hlcy5ldGgoKSwgZm9ybWF0dGVyIHx8IGZvcm1hdHRlcnMub3V0cHV0TG9nRm9ybWF0dGVyKTtcbn07XG4vKmpzaGludCBtYXhwYXJhbXM6MyAqL1xuXG53ZWIzLnNoaCA9IHt9O1xud2ViMy5zaGguZmlsdGVyID0gZnVuY3Rpb24gKGZpbCkge1xuICAgIHJldHVybiBuZXcgRmlsdGVyKGZpbCwgd2F0Y2hlcy5zaGgoKSwgZm9ybWF0dGVycy5vdXRwdXRQb3N0Rm9ybWF0dGVyKTtcbn07XG53ZWIzLm5ldCA9IHt9O1xud2ViMy5kYiA9IHt9O1xud2ViMy5zZXRQcm92aWRlciA9IGZ1bmN0aW9uIChwcm92aWRlcikge1xuICAgIFJlcXVlc3RNYW5hZ2VyLmdldEluc3RhbmNlKCkuc2V0UHJvdmlkZXIocHJvdmlkZXIpO1xufTtcbndlYjMucmVzZXQgPSBmdW5jdGlvbiAoKSB7XG4gICAgUmVxdWVzdE1hbmFnZXIuZ2V0SW5zdGFuY2UoKS5yZXNldCgpO1xuICAgIGMuZGVmYXVsdEJsb2NrID0gJ2xhdGVzdCc7XG4gICAgYy5kZWZhdWx0QWNjb3VudCA9IHVuZGVmaW5lZDtcbn07XG53ZWIzLnRvSGV4ID0gdXRpbHMudG9IZXg7XG53ZWIzLnRvQXNjaWkgPSB1dGlscy50b0FzY2lpO1xud2ViMy5mcm9tQXNjaWkgPSB1dGlscy5mcm9tQXNjaWk7XG53ZWIzLnRvRGVjaW1hbCA9IHV0aWxzLnRvRGVjaW1hbDtcbndlYjMuZnJvbURlY2ltYWwgPSB1dGlscy5mcm9tRGVjaW1hbDtcbndlYjMudG9CaWdOdW1iZXIgPSB1dGlscy50b0JpZ051bWJlcjtcbndlYjMudG9XZWkgPSB1dGlscy50b1dlaTtcbndlYjMuZnJvbVdlaSA9IHV0aWxzLmZyb21XZWk7XG53ZWIzLmlzQWRkcmVzcyA9IHV0aWxzLmlzQWRkcmVzcztcbndlYjMuaXNJQkFOID0gdXRpbHMuaXNJQkFOO1xud2ViMy5zaGEzID0gc2hhMztcbndlYjMuY3JlYXRlQmF0Y2ggPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIG5ldyBCYXRjaCgpO1xufTtcblxuLy8gQUREIGRlZmF1bHRibG9ja1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KHdlYjMuZXRoLCAnZGVmYXVsdEJsb2NrJywge1xuICAgIGdldDogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gYy5kZWZhdWx0QmxvY2s7XG4gICAgfSxcbiAgICBzZXQ6IGZ1bmN0aW9uICh2YWwpIHtcbiAgICAgICAgYy5kZWZhdWx0QmxvY2sgPSB2YWw7XG4gICAgICAgIHJldHVybiB2YWw7XG4gICAgfVxufSk7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh3ZWIzLmV0aCwgJ2RlZmF1bHRBY2NvdW50Jywge1xuICAgIGdldDogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gYy5kZWZhdWx0QWNjb3VudDtcbiAgICB9LFxuICAgIHNldDogZnVuY3Rpb24gKHZhbCkge1xuICAgICAgICBjLmRlZmF1bHRBY2NvdW50ID0gdmFsO1xuICAgICAgICByZXR1cm4gdmFsO1xuICAgIH1cbn0pO1xuXG4vLy8gc2V0dXBzIGFsbCBhcGkgbWV0aG9kc1xuc2V0dXBQcm9wZXJ0aWVzKHdlYjMsIHdlYjNQcm9wZXJ0aWVzKTtcbnNldHVwTWV0aG9kcyh3ZWIzLm5ldCwgbmV0Lm1ldGhvZHMpO1xuc2V0dXBQcm9wZXJ0aWVzKHdlYjMubmV0LCBuZXQucHJvcGVydGllcyk7XG5zZXR1cE1ldGhvZHMod2ViMy5ldGgsIGV0aC5tZXRob2RzKTtcbnNldHVwUHJvcGVydGllcyh3ZWIzLmV0aCwgZXRoLnByb3BlcnRpZXMpO1xuc2V0dXBNZXRob2RzKHdlYjMuZGIsIGRiLm1ldGhvZHMpO1xuc2V0dXBNZXRob2RzKHdlYjMuc2hoLCBzaGgubWV0aG9kcyk7XG5cbndlYjMuYWRtaW4gPSB7fTtcbndlYjMuYWRtaW4uc2V0U2Vzc2lvbktleSA9IGZ1bmN0aW9uKHMpIHsgd2ViMy5hZG1pbi5zZXNzaW9uS2V5ID0gczsgfTtcblxudmFyIGJsb2NrUXVldWVTdGF0dXMgPSBuZXcgTWV0aG9kKHtcblx0bmFtZTogJ2Jsb2NrUXVldWVTdGF0dXMnLFxuXHRjYWxsOiAnYWRtaW5fZXRoX2Jsb2NrUXVldWVTdGF0dXMnLFxuXHRwYXJhbXM6IDEsXG5cdGlucHV0Rm9ybWF0dGVyOiBbZnVuY3Rpb24oKSB7IHJldHVybiB3ZWIzLmFkbWluLnNlc3Npb25LZXk7IH1dXG59KTtcblxuc2V0dXBNZXRob2RzKHdlYjMuYWRtaW4sIFtibG9ja1F1ZXVlU3RhdHVzXSk7XG5cbm1vZHVsZS5leHBvcnRzID0gd2ViMztcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSBiYXRjaC5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgUmVxdWVzdE1hbmFnZXIgPSByZXF1aXJlKCcuL3JlcXVlc3RtYW5hZ2VyJyk7XG5cbnZhciBCYXRjaCA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLnJlcXVlc3RzID0gW107XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gYWRkIGNyZWF0ZSBuZXcgcmVxdWVzdCB0byBiYXRjaCByZXF1ZXN0XG4gKlxuICogQG1ldGhvZCBhZGRcbiAqIEBwYXJhbSB7T2JqZWN0fSBqc29ucnBjIHJlcXVldCBvYmplY3RcbiAqL1xuQmF0Y2gucHJvdG90eXBlLmFkZCA9IGZ1bmN0aW9uIChyZXF1ZXN0KSB7XG4gICAgdGhpcy5yZXF1ZXN0cy5wdXNoKHJlcXVlc3QpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGV4ZWN1dGUgYmF0Y2ggcmVxdWVzdFxuICpcbiAqIEBtZXRob2QgZXhlY3V0ZVxuICovXG5CYXRjaC5wcm90b3R5cGUuZXhlY3V0ZSA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgcmVxdWVzdHMgPSB0aGlzLnJlcXVlc3RzO1xuICAgIFJlcXVlc3RNYW5hZ2VyLmdldEluc3RhbmNlKCkuc2VuZEJhdGNoKHJlcXVlc3RzLCBmdW5jdGlvbiAoZXJyLCByZXN1bHRzKSB7XG4gICAgICAgIHJlc3VsdHMgPSByZXN1bHRzIHx8IFtdO1xuICAgICAgICByZXF1ZXN0cy5tYXAoZnVuY3Rpb24gKHJlcXVlc3QsIGluZGV4KSB7XG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0c1tpbmRleF0gfHwge307XG4gICAgICAgIH0pLm1hcChmdW5jdGlvbiAocmVzdWx0LCBpbmRleCkge1xuICAgICAgICAgICAgcmV0dXJuIHJlcXVlc3RzW2luZGV4XS5mb3JtYXQgPyByZXF1ZXN0c1tpbmRleF0uZm9ybWF0KHJlc3VsdC5yZXN1bHQpIDogcmVzdWx0LnJlc3VsdDtcbiAgICAgICAgfSkuZm9yRWFjaChmdW5jdGlvbiAocmVzdWx0LCBpbmRleCkge1xuICAgICAgICAgICAgaWYgKHJlcXVlc3RzW2luZGV4XS5jYWxsYmFjaykge1xuICAgICAgICAgICAgICAgIHJlcXVlc3RzW2luZGV4XS5jYWxsYmFjayhlcnIsIHJlc3VsdCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH0pOyBcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gQmF0Y2g7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIFxuICogQGZpbGUgY29udHJhY3QuanNcbiAqIEBhdXRob3IgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE0XG4gKi9cblxudmFyIHdlYjMgPSByZXF1aXJlKCcuLi93ZWIzJyk7IFxudmFyIHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMvdXRpbHMnKTtcbnZhciBjb2RlciA9IHJlcXVpcmUoJy4uL3NvbGlkaXR5L2NvZGVyJyk7XG52YXIgU29saWRpdHlFdmVudCA9IHJlcXVpcmUoJy4vZXZlbnQnKTtcbnZhciBTb2xpZGl0eUZ1bmN0aW9uID0gcmVxdWlyZSgnLi9mdW5jdGlvbicpO1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZW5jb2RlIGNvbnN0cnVjdG9yIHBhcmFtc1xuICpcbiAqIEBtZXRob2QgZW5jb2RlQ29uc3RydWN0b3JQYXJhbXNcbiAqIEBwYXJhbSB7QXJyYXl9IGFiaVxuICogQHBhcmFtIHtBcnJheX0gY29uc3RydWN0b3IgcGFyYW1zXG4gKi9cbnZhciBlbmNvZGVDb25zdHJ1Y3RvclBhcmFtcyA9IGZ1bmN0aW9uIChhYmksIHBhcmFtcykge1xuICAgIHJldHVybiBhYmkuZmlsdGVyKGZ1bmN0aW9uIChqc29uKSB7XG4gICAgICAgIHJldHVybiBqc29uLnR5cGUgPT09ICdjb25zdHJ1Y3RvcicgJiYganNvbi5pbnB1dHMubGVuZ3RoID09PSBwYXJhbXMubGVuZ3RoO1xuICAgIH0pLm1hcChmdW5jdGlvbiAoanNvbikge1xuICAgICAgICByZXR1cm4ganNvbi5pbnB1dHMubWFwKGZ1bmN0aW9uIChpbnB1dCkge1xuICAgICAgICAgICAgcmV0dXJuIGlucHV0LnR5cGU7XG4gICAgICAgIH0pO1xuICAgIH0pLm1hcChmdW5jdGlvbiAodHlwZXMpIHtcbiAgICAgICAgcmV0dXJuIGNvZGVyLmVuY29kZVBhcmFtcyh0eXBlcywgcGFyYW1zKTtcbiAgICB9KVswXSB8fCAnJztcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBhZGQgZnVuY3Rpb25zIHRvIGNvbnRyYWN0IG9iamVjdFxuICpcbiAqIEBtZXRob2QgYWRkRnVuY3Rpb25zVG9Db250cmFjdFxuICogQHBhcmFtIHtDb250cmFjdH0gY29udHJhY3RcbiAqIEBwYXJhbSB7QXJyYXl9IGFiaVxuICovXG52YXIgYWRkRnVuY3Rpb25zVG9Db250cmFjdCA9IGZ1bmN0aW9uIChjb250cmFjdCwgYWJpKSB7XG4gICAgYWJpLmZpbHRlcihmdW5jdGlvbiAoanNvbikge1xuICAgICAgICByZXR1cm4ganNvbi50eXBlID09PSAnZnVuY3Rpb24nO1xuICAgIH0pLm1hcChmdW5jdGlvbiAoanNvbikge1xuICAgICAgICByZXR1cm4gbmV3IFNvbGlkaXR5RnVuY3Rpb24oanNvbiwgY29udHJhY3QuYWRkcmVzcyk7XG4gICAgfSkuZm9yRWFjaChmdW5jdGlvbiAoZikge1xuICAgICAgICBmLmF0dGFjaFRvQ29udHJhY3QoY29udHJhY3QpO1xuICAgIH0pO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGFkZCBldmVudHMgdG8gY29udHJhY3Qgb2JqZWN0XG4gKlxuICogQG1ldGhvZCBhZGRFdmVudHNUb0NvbnRyYWN0XG4gKiBAcGFyYW0ge0NvbnRyYWN0fSBjb250cmFjdFxuICogQHBhcmFtIHtBcnJheX0gYWJpXG4gKi9cbnZhciBhZGRFdmVudHNUb0NvbnRyYWN0ID0gZnVuY3Rpb24gKGNvbnRyYWN0LCBhYmkpIHtcbiAgICBhYmkuZmlsdGVyKGZ1bmN0aW9uIChqc29uKSB7XG4gICAgICAgIHJldHVybiBqc29uLnR5cGUgPT09ICdldmVudCc7XG4gICAgfSkubWFwKGZ1bmN0aW9uIChqc29uKSB7XG4gICAgICAgIHJldHVybiBuZXcgU29saWRpdHlFdmVudChqc29uLCBjb250cmFjdC5hZGRyZXNzKTtcbiAgICB9KS5mb3JFYWNoKGZ1bmN0aW9uIChlKSB7XG4gICAgICAgIGUuYXR0YWNoVG9Db250cmFjdChjb250cmFjdCk7XG4gICAgfSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gY3JlYXRlIG5ldyBDb250cmFjdEZhY3RvcnlcbiAqXG4gKiBAbWV0aG9kIGNvbnRyYWN0XG4gKiBAcGFyYW0ge0FycmF5fSBhYmlcbiAqIEByZXR1cm5zIHtDb250cmFjdEZhY3Rvcnl9IG5ldyBjb250cmFjdCBmYWN0b3J5XG4gKi9cbnZhciBjb250cmFjdCA9IGZ1bmN0aW9uIChhYmkpIHtcbiAgICByZXR1cm4gbmV3IENvbnRyYWN0RmFjdG9yeShhYmkpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGNyZWF0ZSBuZXcgQ29udHJhY3RGYWN0b3J5IGluc3RhbmNlXG4gKlxuICogQG1ldGhvZCBDb250cmFjdEZhY3RvcnlcbiAqIEBwYXJhbSB7QXJyYXl9IGFiaVxuICovXG52YXIgQ29udHJhY3RGYWN0b3J5ID0gZnVuY3Rpb24gKGFiaSkge1xuICAgIHRoaXMuYWJpID0gYWJpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGNyZWF0ZSBuZXcgY29udHJhY3Qgb24gYSBibG9ja2NoYWluXG4gKiBcbiAqIEBtZXRob2QgbmV3XG4gKiBAcGFyYW0ge0FueX0gY29udHJhY3QgY29uc3RydWN0b3IgcGFyYW0xIChvcHRpb25hbClcbiAqIEBwYXJhbSB7QW55fSBjb250cmFjdCBjb25zdHJ1Y3RvciBwYXJhbTIgKG9wdGlvbmFsKVxuICogQHBhcmFtIHtPYmplY3R9IGNvbnRyYWN0IHRyYW5zYWN0aW9uIG9iamVjdCAocmVxdWlyZWQpXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFja1xuICogQHJldHVybnMge0NvbnRyYWN0fSByZXR1cm5zIGNvbnRyYWN0IGlmIG5vIGNhbGxiYWNrIHdhcyBwYXNzZWQsXG4gKiBvdGhlcndpc2UgY2FsbHMgY2FsbGJhY2sgZnVuY3Rpb24gKGVyciwgY29udHJhY3QpXG4gKi9cbkNvbnRyYWN0RmFjdG9yeS5wcm90b3R5cGUubmV3ID0gZnVuY3Rpb24gKCkge1xuICAgIC8vIHBhcnNlIGFyZ3VtZW50c1xuICAgIHZhciBvcHRpb25zID0ge307IC8vIHJlcXVpcmVkIVxuICAgIHZhciBjYWxsYmFjaztcblxuICAgIHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKTtcbiAgICBpZiAodXRpbHMuaXNGdW5jdGlvbihhcmdzW2FyZ3MubGVuZ3RoIC0gMV0pKSB7XG4gICAgICAgIGNhbGxiYWNrID0gYXJncy5wb3AoKTtcbiAgICB9XG5cbiAgICB2YXIgbGFzdCA9IGFyZ3NbYXJncy5sZW5ndGggLSAxXTtcbiAgICBpZiAodXRpbHMuaXNPYmplY3QobGFzdCkgJiYgIXV0aWxzLmlzQXJyYXkobGFzdCkpIHtcbiAgICAgICAgb3B0aW9ucyA9IGFyZ3MucG9wKCk7XG4gICAgfVxuXG4gICAgLy8gdGhyb3cgYW4gZXJyb3IgaWYgdGhlcmUgYXJlIG5vIG9wdGlvbnNcblxuICAgIHZhciBieXRlcyA9IGVuY29kZUNvbnN0cnVjdG9yUGFyYW1zKHRoaXMuYWJpLCBhcmdzKTtcbiAgICBvcHRpb25zLmRhdGEgKz0gYnl0ZXM7XG5cbiAgICBpZiAoIWNhbGxiYWNrKSB7XG4gICAgICAgIHZhciBhZGRyZXNzID0gd2ViMy5ldGguc2VuZFRyYW5zYWN0aW9uKG9wdGlvbnMpO1xuICAgICAgICByZXR1cm4gdGhpcy5hdChhZGRyZXNzKTtcbiAgICB9XG4gIFxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB3ZWIzLmV0aC5zZW5kVHJhbnNhY3Rpb24ob3B0aW9ucywgZnVuY3Rpb24gKGVyciwgYWRkcmVzcykge1xuICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgICBjYWxsYmFjayhlcnIpO1xuICAgICAgICB9XG4gICAgICAgIHNlbGYuYXQoYWRkcmVzcywgY2FsbGJhY2spOyBcbiAgICB9KTsgXG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZ2V0IGFjY2VzcyB0byBleGlzdGluZyBjb250cmFjdCBvbiBhIGJsb2NrY2hhaW5cbiAqXG4gKiBAbWV0aG9kIGF0XG4gKiBAcGFyYW0ge0FkZHJlc3N9IGNvbnRyYWN0IGFkZHJlc3MgKHJlcXVpcmVkKVxuICogQHBhcmFtIHtGdW5jdGlvbn0gY2FsbGJhY2sge29wdGlvbmFsKVxuICogQHJldHVybnMge0NvbnRyYWN0fSByZXR1cm5zIGNvbnRyYWN0IGlmIG5vIGNhbGxiYWNrIHdhcyBwYXNzZWQsXG4gKiBvdGhlcndpc2UgY2FsbHMgY2FsbGJhY2sgZnVuY3Rpb24gKGVyciwgY29udHJhY3QpXG4gKi9cbkNvbnRyYWN0RmFjdG9yeS5wcm90b3R5cGUuYXQgPSBmdW5jdGlvbiAoYWRkcmVzcywgY2FsbGJhY2spIHtcbiAgICAvLyBUT0RPOiBhZGRyZXNzIGlzIHJlcXVpcmVkXG4gICAgXG4gICAgaWYgKGNhbGxiYWNrKSB7XG4gICAgICAgIGNhbGxiYWNrKG51bGwsIG5ldyBDb250cmFjdCh0aGlzLmFiaSwgYWRkcmVzcykpO1xuICAgIH0gXG4gICAgcmV0dXJuIG5ldyBDb250cmFjdCh0aGlzLmFiaSwgYWRkcmVzcyk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gY3JlYXRlIG5ldyBjb250cmFjdCBpbnN0YW5jZVxuICpcbiAqIEBtZXRob2QgQ29udHJhY3RcbiAqIEBwYXJhbSB7QXJyYXl9IGFiaVxuICogQHBhcmFtIHtBZGRyZXNzfSBjb250cmFjdCBhZGRyZXNzXG4gKi9cbnZhciBDb250cmFjdCA9IGZ1bmN0aW9uIChhYmksIGFkZHJlc3MpIHtcbiAgICB0aGlzLmFkZHJlc3MgPSBhZGRyZXNzO1xuICAgIGFkZEZ1bmN0aW9uc1RvQ29udHJhY3QodGhpcywgYWJpKTtcbiAgICBhZGRFdmVudHNUb0NvbnRyYWN0KHRoaXMsIGFiaSk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IGNvbnRyYWN0O1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBAZmlsZSBkYi5qc1xuICogQGF1dGhvcnM6XG4gKiAgIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciBNZXRob2QgPSByZXF1aXJlKCcuL21ldGhvZCcpO1xuXG52YXIgcHV0U3RyaW5nID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ3B1dFN0cmluZycsXG4gICAgY2FsbDogJ2RiX3B1dFN0cmluZycsXG4gICAgcGFyYW1zOiAzXG59KTtcblxuXG52YXIgZ2V0U3RyaW5nID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2dldFN0cmluZycsXG4gICAgY2FsbDogJ2RiX2dldFN0cmluZycsXG4gICAgcGFyYW1zOiAyXG59KTtcblxudmFyIHB1dEhleCA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdwdXRIZXgnLFxuICAgIGNhbGw6ICdkYl9wdXRIZXgnLFxuICAgIHBhcmFtczogM1xufSk7XG5cbnZhciBnZXRIZXggPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0SGV4JyxcbiAgICBjYWxsOiAnZGJfZ2V0SGV4JyxcbiAgICBwYXJhbXM6IDJcbn0pO1xuXG52YXIgbWV0aG9kcyA9IFtcbiAgICBwdXRTdHJpbmcsIGdldFN0cmluZywgcHV0SGV4LCBnZXRIZXhcbl07XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIG1ldGhvZHM6IG1ldGhvZHNcbn07XG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBcbiAqIEBmaWxlIGVycm9ycy5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBJbnZhbGlkTnVtYmVyT2ZQYXJhbXM6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBFcnJvcignSW52YWxpZCBudW1iZXIgb2YgaW5wdXQgcGFyYW1ldGVycycpO1xuICAgIH0sXG4gICAgSW52YWxpZENvbm5lY3Rpb246IGZ1bmN0aW9uIChob3N0KXtcbiAgICAgICAgcmV0dXJuIG5ldyBFcnJvcignQ09OTkVDVElPTiBFUlJPUjogQ291bGRuXFwndCBjb25uZWN0IHRvIG5vZGUgJysgaG9zdCArJywgaXMgaXQgcnVubmluZz8nKTtcbiAgICB9LFxuICAgIEludmFsaWRQcm92aWRlcjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gbmV3IEVycm9yKCdQcm92aWRvciBub3Qgc2V0IG9yIGludmFsaWQnKTtcbiAgICB9LFxuICAgIEludmFsaWRSZXNwb25zZTogZnVuY3Rpb24gKHJlc3VsdCl7XG4gICAgICAgIHZhciBtZXNzYWdlID0gISFyZXN1bHQgJiYgISFyZXN1bHQuZXJyb3IgJiYgISFyZXN1bHQuZXJyb3IubWVzc2FnZSA/IHJlc3VsdC5lcnJvci5tZXNzYWdlIDogJ0ludmFsaWQgSlNPTiBSUEMgcmVzcG9uc2UnO1xuICAgICAgICByZXR1cm4gbmV3IEVycm9yKG1lc3NhZ2UpO1xuICAgIH1cbn07XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqXG4gKiBAZmlsZSBldGguanNcbiAqIEBhdXRob3IgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAYXV0aG9yIEZhYmlhbiBWb2dlbHN0ZWxsZXIgPGZhYmlhbkBldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbi8qKlxuICogV2ViM1xuICpcbiAqIEBtb2R1bGUgd2ViM1xuICovXG5cbi8qKlxuICogRXRoIG1ldGhvZHMgYW5kIHByb3BlcnRpZXNcbiAqXG4gKiBBbiBleGFtcGxlIG1ldGhvZCBvYmplY3QgY2FuIGxvb2sgYXMgZm9sbG93czpcbiAqXG4gKiAgICAgIHtcbiAqICAgICAgbmFtZTogJ2dldEJsb2NrJyxcbiAqICAgICAgY2FsbDogYmxvY2tDYWxsLFxuICogICAgICBwYXJhbXM6IDIsXG4gKiAgICAgIG91dHB1dEZvcm1hdHRlcjogZm9ybWF0dGVycy5vdXRwdXRCbG9ja0Zvcm1hdHRlcixcbiAqICAgICAgaW5wdXRGb3JtYXR0ZXI6IFsgLy8gY2FuIGJlIGEgZm9ybWF0dGVyIGZ1bmNpdG9uIG9yIGFuIGFycmF5IG9mIGZ1bmN0aW9ucy4gV2hlcmUgZWFjaCBpdGVtIGluIHRoZSBhcnJheSB3aWxsIGJlIHVzZWQgZm9yIG9uZSBwYXJhbWV0ZXJcbiAqICAgICAgICAgICB1dGlscy50b0hleCwgLy8gZm9ybWF0cyBwYXJhbXRlciAxXG4gKiAgICAgICAgICAgZnVuY3Rpb24ocGFyYW0peyByZXR1cm4gISFwYXJhbTsgfSAvLyBmb3JtYXRzIHBhcmFtdGVyIDJcbiAqICAgICAgICAgXVxuICogICAgICAgfSxcbiAqXG4gKiBAY2xhc3MgW3dlYjNdIGV0aFxuICogQGNvbnN0cnVjdG9yXG4gKi9cblxuXCJ1c2Ugc3RyaWN0XCI7XG5cbnZhciBmb3JtYXR0ZXJzID0gcmVxdWlyZSgnLi9mb3JtYXR0ZXJzJyk7XG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xudmFyIE1ldGhvZCA9IHJlcXVpcmUoJy4vbWV0aG9kJyk7XG52YXIgUHJvcGVydHkgPSByZXF1aXJlKCcuL3Byb3BlcnR5Jyk7XG5cbnZhciBibG9ja0NhbGwgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIHJldHVybiAodXRpbHMuaXNTdHJpbmcoYXJnc1swXSkgJiYgYXJnc1swXS5pbmRleE9mKCcweCcpID09PSAwKSA/IFwiZXRoX2dldEJsb2NrQnlIYXNoXCIgOiBcImV0aF9nZXRCbG9ja0J5TnVtYmVyXCI7XG59O1xuXG52YXIgdHJhbnNhY3Rpb25Gcm9tQmxvY2tDYWxsID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICByZXR1cm4gKHV0aWxzLmlzU3RyaW5nKGFyZ3NbMF0pICYmIGFyZ3NbMF0uaW5kZXhPZignMHgnKSA9PT0gMCkgPyAnZXRoX2dldFRyYW5zYWN0aW9uQnlCbG9ja0hhc2hBbmRJbmRleCcgOiAnZXRoX2dldFRyYW5zYWN0aW9uQnlCbG9ja051bWJlckFuZEluZGV4Jztcbn07XG5cbnZhciB1bmNsZUNhbGwgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIHJldHVybiAodXRpbHMuaXNTdHJpbmcoYXJnc1swXSkgJiYgYXJnc1swXS5pbmRleE9mKCcweCcpID09PSAwKSA/ICdldGhfZ2V0VW5jbGVCeUJsb2NrSGFzaEFuZEluZGV4JyA6ICdldGhfZ2V0VW5jbGVCeUJsb2NrTnVtYmVyQW5kSW5kZXgnO1xufTtcblxudmFyIGdldEJsb2NrVHJhbnNhY3Rpb25Db3VudENhbGwgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIHJldHVybiAodXRpbHMuaXNTdHJpbmcoYXJnc1swXSkgJiYgYXJnc1swXS5pbmRleE9mKCcweCcpID09PSAwKSA/ICdldGhfZ2V0QmxvY2tUcmFuc2FjdGlvbkNvdW50QnlIYXNoJyA6ICdldGhfZ2V0QmxvY2tUcmFuc2FjdGlvbkNvdW50QnlOdW1iZXInO1xufTtcblxudmFyIHVuY2xlQ291bnRDYWxsID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICByZXR1cm4gKHV0aWxzLmlzU3RyaW5nKGFyZ3NbMF0pICYmIGFyZ3NbMF0uaW5kZXhPZignMHgnKSA9PT0gMCkgPyAnZXRoX2dldFVuY2xlQ291bnRCeUJsb2NrSGFzaCcgOiAnZXRoX2dldFVuY2xlQ291bnRCeUJsb2NrTnVtYmVyJztcbn07XG5cbi8vLyBAcmV0dXJucyBhbiBhcnJheSBvZiBvYmplY3RzIGRlc2NyaWJpbmcgd2ViMy5ldGggYXBpIG1ldGhvZHNcblxudmFyIGdldEJhbGFuY2UgPSBuZXcgTWV0aG9kKHtcblx0bmFtZTogJ2dldEJhbGFuY2UnLFxuXHRjYWxsOiAnZXRoX2dldEJhbGFuY2UnLFxuXHRwYXJhbXM6IDIsXG5cdGlucHV0Rm9ybWF0dGVyOiBbdXRpbHMudG9BZGRyZXNzLCBmb3JtYXR0ZXJzLmlucHV0RGVmYXVsdEJsb2NrTnVtYmVyRm9ybWF0dGVyXSxcblx0b3V0cHV0Rm9ybWF0dGVyOiBmb3JtYXR0ZXJzLm91dHB1dEJpZ051bWJlckZvcm1hdHRlclxufSk7XG5cbnZhciBnZXRTdG9yYWdlQXQgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0U3RvcmFnZUF0JyxcbiAgICBjYWxsOiAnZXRoX2dldFN0b3JhZ2VBdCcsXG4gICAgcGFyYW1zOiAzLFxuICAgIGlucHV0Rm9ybWF0dGVyOiBbbnVsbCwgdXRpbHMudG9IZXgsIGZvcm1hdHRlcnMuaW5wdXREZWZhdWx0QmxvY2tOdW1iZXJGb3JtYXR0ZXJdXG59KTtcblxudmFyIGdldENvZGUgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0Q29kZScsXG4gICAgY2FsbDogJ2V0aF9nZXRDb2RlJyxcbiAgICBwYXJhbXM6IDIsXG4gICAgaW5wdXRGb3JtYXR0ZXI6IFt1dGlscy50b0FkZHJlc3MsIGZvcm1hdHRlcnMuaW5wdXREZWZhdWx0QmxvY2tOdW1iZXJGb3JtYXR0ZXJdXG59KTtcblxudmFyIGdldEJsb2NrID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2dldEJsb2NrJyxcbiAgICBjYWxsOiBibG9ja0NhbGwsXG4gICAgcGFyYW1zOiAyLFxuICAgIGlucHV0Rm9ybWF0dGVyOiBbZm9ybWF0dGVycy5pbnB1dEJsb2NrTnVtYmVyRm9ybWF0dGVyLCBmdW5jdGlvbiAodmFsKSB7IHJldHVybiAhIXZhbDsgfV0sXG4gICAgb3V0cHV0Rm9ybWF0dGVyOiBmb3JtYXR0ZXJzLm91dHB1dEJsb2NrRm9ybWF0dGVyXG59KTtcblxudmFyIGdldFVuY2xlID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2dldFVuY2xlJyxcbiAgICBjYWxsOiB1bmNsZUNhbGwsXG4gICAgcGFyYW1zOiAyLFxuICAgIGlucHV0Rm9ybWF0dGVyOiBbZm9ybWF0dGVycy5pbnB1dEJsb2NrTnVtYmVyRm9ybWF0dGVyLCB1dGlscy50b0hleF0sXG4gICAgb3V0cHV0Rm9ybWF0dGVyOiBmb3JtYXR0ZXJzLm91dHB1dEJsb2NrRm9ybWF0dGVyLFxuXG59KTtcblxudmFyIGdldENvbXBpbGVycyA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdnZXRDb21waWxlcnMnLFxuICAgIGNhbGw6ICdldGhfZ2V0Q29tcGlsZXJzJyxcbiAgICBwYXJhbXM6IDBcbn0pO1xuXG52YXIgZ2V0QmxvY2tUcmFuc2FjdGlvbkNvdW50ID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2dldEJsb2NrVHJhbnNhY3Rpb25Db3VudCcsXG4gICAgY2FsbDogZ2V0QmxvY2tUcmFuc2FjdGlvbkNvdW50Q2FsbCxcbiAgICBwYXJhbXM6IDEsXG4gICAgaW5wdXRGb3JtYXR0ZXI6IFtmb3JtYXR0ZXJzLmlucHV0QmxvY2tOdW1iZXJGb3JtYXR0ZXJdLFxuICAgIG91dHB1dEZvcm1hdHRlcjogdXRpbHMudG9EZWNpbWFsXG59KTtcblxudmFyIGdldEJsb2NrVW5jbGVDb3VudCA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdnZXRCbG9ja1VuY2xlQ291bnQnLFxuICAgIGNhbGw6IHVuY2xlQ291bnRDYWxsLFxuICAgIHBhcmFtczogMSxcbiAgICBpbnB1dEZvcm1hdHRlcjogW2Zvcm1hdHRlcnMuaW5wdXRCbG9ja051bWJlckZvcm1hdHRlcl0sXG4gICAgb3V0cHV0Rm9ybWF0dGVyOiB1dGlscy50b0RlY2ltYWxcbn0pO1xuXG52YXIgZ2V0VHJhbnNhY3Rpb24gPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0VHJhbnNhY3Rpb24nLFxuICAgIGNhbGw6ICdldGhfZ2V0VHJhbnNhY3Rpb25CeUhhc2gnLFxuICAgIHBhcmFtczogMSxcbiAgICBvdXRwdXRGb3JtYXR0ZXI6IGZvcm1hdHRlcnMub3V0cHV0VHJhbnNhY3Rpb25Gb3JtYXR0ZXJcbn0pO1xuXG52YXIgZ2V0VHJhbnNhY3Rpb25Gcm9tQmxvY2sgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnZ2V0VHJhbnNhY3Rpb25Gcm9tQmxvY2snLFxuICAgIGNhbGw6IHRyYW5zYWN0aW9uRnJvbUJsb2NrQ2FsbCxcbiAgICBwYXJhbXM6IDIsXG4gICAgaW5wdXRGb3JtYXR0ZXI6IFtmb3JtYXR0ZXJzLmlucHV0QmxvY2tOdW1iZXJGb3JtYXR0ZXIsIHV0aWxzLnRvSGV4XSxcbiAgICBvdXRwdXRGb3JtYXR0ZXI6IGZvcm1hdHRlcnMub3V0cHV0VHJhbnNhY3Rpb25Gb3JtYXR0ZXJcbn0pO1xuXG52YXIgZ2V0VHJhbnNhY3Rpb25Db3VudCA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdnZXRUcmFuc2FjdGlvbkNvdW50JyxcbiAgICBjYWxsOiAnZXRoX2dldFRyYW5zYWN0aW9uQ291bnQnLFxuICAgIHBhcmFtczogMixcbiAgICBpbnB1dEZvcm1hdHRlcjogW251bGwsIGZvcm1hdHRlcnMuaW5wdXREZWZhdWx0QmxvY2tOdW1iZXJGb3JtYXR0ZXJdLFxuICAgIG91dHB1dEZvcm1hdHRlcjogdXRpbHMudG9EZWNpbWFsXG59KTtcblxudmFyIHNlbmRUcmFuc2FjdGlvbiA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdzZW5kVHJhbnNhY3Rpb24nLFxuICAgIGNhbGw6ICdldGhfc2VuZFRyYW5zYWN0aW9uJyxcbiAgICBwYXJhbXM6IDEsXG4gICAgaW5wdXRGb3JtYXR0ZXI6IFtmb3JtYXR0ZXJzLmlucHV0VHJhbnNhY3Rpb25Gb3JtYXR0ZXJdXG59KTtcblxudmFyIGNhbGwgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnY2FsbCcsXG4gICAgY2FsbDogJ2V0aF9jYWxsJyxcbiAgICBwYXJhbXM6IDIsXG4gICAgaW5wdXRGb3JtYXR0ZXI6IFtmb3JtYXR0ZXJzLmlucHV0VHJhbnNhY3Rpb25Gb3JtYXR0ZXIsIGZvcm1hdHRlcnMuaW5wdXREZWZhdWx0QmxvY2tOdW1iZXJGb3JtYXR0ZXJdXG59KTtcblxudmFyIGVzdGltYXRlR2FzID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2VzdGltYXRlR2FzJyxcbiAgICBjYWxsOiAnZXRoX2VzdGltYXRlR2FzJyxcbiAgICBwYXJhbXM6IDEsXG4gICAgaW5wdXRGb3JtYXR0ZXI6IFtmb3JtYXR0ZXJzLmlucHV0VHJhbnNhY3Rpb25Gb3JtYXR0ZXJdLFxuICAgIG91dHB1dEZvcm1hdHRlcjogdXRpbHMudG9EZWNpbWFsXG59KTtcblxudmFyIGNvbXBpbGVTb2xpZGl0eSA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdjb21waWxlLnNvbGlkaXR5JyxcbiAgICBjYWxsOiAnZXRoX2NvbXBpbGVTb2xpZGl0eScsXG4gICAgcGFyYW1zOiAxXG59KTtcblxudmFyIGNvbXBpbGVMTEwgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnY29tcGlsZS5sbGwnLFxuICAgIGNhbGw6ICdldGhfY29tcGlsZUxMTCcsXG4gICAgcGFyYW1zOiAxXG59KTtcblxudmFyIGNvbXBpbGVTZXJwZW50ID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2NvbXBpbGUuc2VycGVudCcsXG4gICAgY2FsbDogJ2V0aF9jb21waWxlU2VycGVudCcsXG4gICAgcGFyYW1zOiAxXG59KTtcblxudmFyIHN1Ym1pdFdvcmsgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnc3VibWl0V29yaycsXG4gICAgY2FsbDogJ2V0aF9zdWJtaXRXb3JrJyxcbiAgICBwYXJhbXM6IDNcbn0pO1xuXG52YXIgZ2V0V29yayA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICdnZXRXb3JrJyxcbiAgICBjYWxsOiAnZXRoX2dldFdvcmsnLFxuICAgIHBhcmFtczogMFxufSk7XG5cbnZhciBtZXRob2RzID0gW1xuICAgIGdldEJhbGFuY2UsXG4gICAgZ2V0U3RvcmFnZUF0LFxuICAgIGdldENvZGUsXG4gICAgZ2V0QmxvY2ssXG4gICAgZ2V0VW5jbGUsXG4gICAgZ2V0Q29tcGlsZXJzLFxuICAgIGdldEJsb2NrVHJhbnNhY3Rpb25Db3VudCxcbiAgICBnZXRCbG9ja1VuY2xlQ291bnQsXG4gICAgZ2V0VHJhbnNhY3Rpb24sXG4gICAgZ2V0VHJhbnNhY3Rpb25Gcm9tQmxvY2ssXG4gICAgZ2V0VHJhbnNhY3Rpb25Db3VudCxcbiAgICBjYWxsLFxuICAgIGVzdGltYXRlR2FzLFxuICAgIHNlbmRUcmFuc2FjdGlvbixcbiAgICBjb21waWxlU29saWRpdHksXG4gICAgY29tcGlsZUxMTCxcbiAgICBjb21waWxlU2VycGVudCxcbiAgICBzdWJtaXRXb3JrLFxuICAgIGdldFdvcmtcbl07XG5cbi8vLyBAcmV0dXJucyBhbiBhcnJheSBvZiBvYmplY3RzIGRlc2NyaWJpbmcgd2ViMy5ldGggYXBpIHByb3BlcnRpZXNcblxuXG5cbnZhciBwcm9wZXJ0aWVzID0gW1xuICAgIG5ldyBQcm9wZXJ0eSh7XG4gICAgICAgIG5hbWU6ICdjb2luYmFzZScsXG4gICAgICAgIGdldHRlcjogJ2V0aF9jb2luYmFzZSdcbiAgICB9KSxcbiAgICBuZXcgUHJvcGVydHkoe1xuICAgICAgICBuYW1lOiAnbWluaW5nJyxcbiAgICAgICAgZ2V0dGVyOiAnZXRoX21pbmluZydcbiAgICB9KSxcbiAgICBuZXcgUHJvcGVydHkoe1xuICAgICAgICBuYW1lOiAnaGFzaHJhdGUnLFxuICAgICAgICBnZXR0ZXI6ICdldGhfaGFzaHJhdGUnLFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IHV0aWxzLnRvRGVjaW1hbFxuICAgIH0pLFxuICAgIG5ldyBQcm9wZXJ0eSh7XG4gICAgICAgIG5hbWU6ICdnYXNQcmljZScsXG4gICAgICAgIGdldHRlcjogJ2V0aF9nYXNQcmljZScsXG4gICAgICAgIG91dHB1dEZvcm1hdHRlcjogZm9ybWF0dGVycy5vdXRwdXRCaWdOdW1iZXJGb3JtYXR0ZXJcbiAgICB9KSxcbiAgICBuZXcgUHJvcGVydHkoe1xuICAgICAgICBuYW1lOiAnYWNjb3VudHMnLFxuICAgICAgICBnZXR0ZXI6ICdldGhfYWNjb3VudHMnXG4gICAgfSksXG4gICAgbmV3IFByb3BlcnR5KHtcbiAgICAgICAgbmFtZTogJ2Jsb2NrTnVtYmVyJyxcbiAgICAgICAgZ2V0dGVyOiAnZXRoX2Jsb2NrTnVtYmVyJyxcbiAgICAgICAgb3V0cHV0Rm9ybWF0dGVyOiB1dGlscy50b0RlY2ltYWxcbiAgICB9KVxuXTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgbWV0aG9kczogbWV0aG9kcyxcbiAgICBwcm9wZXJ0aWVzOiBwcm9wZXJ0aWVzXG59O1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBcbiAqIEBmaWxlIGV2ZW50LmpzXG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNFxuICovXG5cbnZhciB1dGlscyA9IHJlcXVpcmUoJy4uL3V0aWxzL3V0aWxzJyk7XG52YXIgY29kZXIgPSByZXF1aXJlKCcuLi9zb2xpZGl0eS9jb2RlcicpO1xudmFyIHdlYjMgPSByZXF1aXJlKCcuLi93ZWIzJyk7XG52YXIgZm9ybWF0dGVycyA9IHJlcXVpcmUoJy4vZm9ybWF0dGVycycpO1xudmFyIHNoYTMgPSByZXF1aXJlKCcuLi91dGlscy9zaGEzJyk7XG5cbi8qKlxuICogVGhpcyBwcm90b3R5cGUgc2hvdWxkIGJlIHVzZWQgdG8gY3JlYXRlIGV2ZW50IGZpbHRlcnNcbiAqL1xudmFyIFNvbGlkaXR5RXZlbnQgPSBmdW5jdGlvbiAoanNvbiwgYWRkcmVzcykge1xuICAgIHRoaXMuX3BhcmFtcyA9IGpzb24uaW5wdXRzO1xuICAgIHRoaXMuX25hbWUgPSB1dGlscy50cmFuc2Zvcm1Ub0Z1bGxOYW1lKGpzb24pO1xuICAgIHRoaXMuX2FkZHJlc3MgPSBhZGRyZXNzO1xuICAgIHRoaXMuX2Fub255bW91cyA9IGpzb24uYW5vbnltb3VzO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBnZXQgZmlsdGVyZWQgcGFyYW0gdHlwZXNcbiAqXG4gKiBAbWV0aG9kIHR5cGVzXG4gKiBAcGFyYW0ge0Jvb2x9IGRlY2lkZSBpZiByZXR1cm5lZCB0eXBlZCBzaG91bGQgYmUgaW5kZXhlZFxuICogQHJldHVybiB7QXJyYXl9IGFycmF5IG9mIHR5cGVzXG4gKi9cblNvbGlkaXR5RXZlbnQucHJvdG90eXBlLnR5cGVzID0gZnVuY3Rpb24gKGluZGV4ZWQpIHtcbiAgICByZXR1cm4gdGhpcy5fcGFyYW1zLmZpbHRlcihmdW5jdGlvbiAoaSkge1xuICAgICAgICByZXR1cm4gaS5pbmRleGVkID09PSBpbmRleGVkO1xuICAgIH0pLm1hcChmdW5jdGlvbiAoaSkge1xuICAgICAgICByZXR1cm4gaS50eXBlO1xuICAgIH0pO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBnZXQgZXZlbnQgZGlzcGxheSBuYW1lXG4gKlxuICogQG1ldGhvZCBkaXNwbGF5TmFtZVxuICogQHJldHVybiB7U3RyaW5nfSBldmVudCBkaXNwbGF5IG5hbWVcbiAqL1xuU29saWRpdHlFdmVudC5wcm90b3R5cGUuZGlzcGxheU5hbWUgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHV0aWxzLmV4dHJhY3REaXNwbGF5TmFtZSh0aGlzLl9uYW1lKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZ2V0IGV2ZW50IHR5cGUgbmFtZVxuICpcbiAqIEBtZXRob2QgdHlwZU5hbWVcbiAqIEByZXR1cm4ge1N0cmluZ30gZXZlbnQgdHlwZSBuYW1lXG4gKi9cblNvbGlkaXR5RXZlbnQucHJvdG90eXBlLnR5cGVOYW1lID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB1dGlscy5leHRyYWN0VHlwZU5hbWUodGhpcy5fbmFtZSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGdldCBldmVudCBzaWduYXR1cmVcbiAqXG4gKiBAbWV0aG9kIHNpZ25hdHVyZVxuICogQHJldHVybiB7U3RyaW5nfSBldmVudCBzaWduYXR1cmVcbiAqL1xuU29saWRpdHlFdmVudC5wcm90b3R5cGUuc2lnbmF0dXJlID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiBzaGEzKHRoaXMuX25hbWUpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBlbmNvZGUgaW5kZXhlZCBwYXJhbXMgYW5kIG9wdGlvbnMgdG8gb25lIGZpbmFsIG9iamVjdFxuICogXG4gKiBAbWV0aG9kIGVuY29kZVxuICogQHBhcmFtIHtPYmplY3R9IGluZGV4ZWRcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zXG4gKiBAcmV0dXJuIHtPYmplY3R9IGV2ZXJ5dGhpbmcgY29tYmluZWQgdG9nZXRoZXIgYW5kIGVuY29kZWRcbiAqL1xuU29saWRpdHlFdmVudC5wcm90b3R5cGUuZW5jb2RlID0gZnVuY3Rpb24gKGluZGV4ZWQsIG9wdGlvbnMpIHtcbiAgICBpbmRleGVkID0gaW5kZXhlZCB8fCB7fTtcbiAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcbiAgICB2YXIgcmVzdWx0ID0ge307XG5cbiAgICBbJ2Zyb21CbG9jaycsICd0b0Jsb2NrJ10uZmlsdGVyKGZ1bmN0aW9uIChmKSB7XG4gICAgICAgIHJldHVybiBvcHRpb25zW2ZdICE9PSB1bmRlZmluZWQ7XG4gICAgfSkuZm9yRWFjaChmdW5jdGlvbiAoZikge1xuICAgICAgICByZXN1bHRbZl0gPSBmb3JtYXR0ZXJzLmlucHV0QmxvY2tOdW1iZXJGb3JtYXR0ZXIob3B0aW9uc1tmXSk7XG4gICAgfSk7XG5cbiAgICByZXN1bHQudG9waWNzID0gW107XG5cbiAgICBpZiAoIXRoaXMuX2Fub255bW91cykge1xuICAgICAgICByZXN1bHQuYWRkcmVzcyA9IHRoaXMuX2FkZHJlc3M7XG4gICAgICAgIHJlc3VsdC50b3BpY3MucHVzaCgnMHgnICsgdGhpcy5zaWduYXR1cmUoKSk7XG4gICAgfVxuXG4gICAgdmFyIGluZGV4ZWRUb3BpY3MgPSB0aGlzLl9wYXJhbXMuZmlsdGVyKGZ1bmN0aW9uIChpKSB7XG4gICAgICAgIHJldHVybiBpLmluZGV4ZWQgPT09IHRydWU7XG4gICAgfSkubWFwKGZ1bmN0aW9uIChpKSB7XG4gICAgICAgIHZhciB2YWx1ZSA9IGluZGV4ZWRbaS5uYW1lXTtcbiAgICAgICAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQgfHwgdmFsdWUgPT09IG51bGwpIHtcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICBpZiAodXRpbHMuaXNBcnJheSh2YWx1ZSkpIHtcbiAgICAgICAgICAgIHJldHVybiB2YWx1ZS5tYXAoZnVuY3Rpb24gKHYpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gJzB4JyArIGNvZGVyLmVuY29kZVBhcmFtKGkudHlwZSwgdik7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gJzB4JyArIGNvZGVyLmVuY29kZVBhcmFtKGkudHlwZSwgdmFsdWUpO1xuICAgIH0pO1xuXG4gICAgcmVzdWx0LnRvcGljcyA9IHJlc3VsdC50b3BpY3MuY29uY2F0KGluZGV4ZWRUb3BpY3MpO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZGVjb2RlIGluZGV4ZWQgcGFyYW1zIGFuZCBvcHRpb25zXG4gKlxuICogQG1ldGhvZCBkZWNvZGVcbiAqIEBwYXJhbSB7T2JqZWN0fSBkYXRhXG4gKiBAcmV0dXJuIHtPYmplY3R9IHJlc3VsdCBvYmplY3Qgd2l0aCBkZWNvZGVkIGluZGV4ZWQgJiYgbm90IGluZGV4ZWQgcGFyYW1zXG4gKi9cblNvbGlkaXR5RXZlbnQucHJvdG90eXBlLmRlY29kZSA9IGZ1bmN0aW9uIChkYXRhKSB7XG4gXG4gICAgZGF0YS5kYXRhID0gZGF0YS5kYXRhIHx8ICcnO1xuICAgIGRhdGEudG9waWNzID0gZGF0YS50b3BpY3MgfHwgW107XG5cbiAgICB2YXIgYXJnVG9waWNzID0gdGhpcy5fYW5vbnltb3VzID8gZGF0YS50b3BpY3MgOiBkYXRhLnRvcGljcy5zbGljZSgxKTtcbiAgICB2YXIgaW5kZXhlZERhdGEgPSBhcmdUb3BpY3MubWFwKGZ1bmN0aW9uICh0b3BpY3MpIHsgcmV0dXJuIHRvcGljcy5zbGljZSgyKTsgfSkuam9pbihcIlwiKTtcbiAgICB2YXIgaW5kZXhlZFBhcmFtcyA9IGNvZGVyLmRlY29kZVBhcmFtcyh0aGlzLnR5cGVzKHRydWUpLCBpbmRleGVkRGF0YSk7IFxuXG4gICAgdmFyIG5vdEluZGV4ZWREYXRhID0gZGF0YS5kYXRhLnNsaWNlKDIpO1xuICAgIHZhciBub3RJbmRleGVkUGFyYW1zID0gY29kZXIuZGVjb2RlUGFyYW1zKHRoaXMudHlwZXMoZmFsc2UpLCBub3RJbmRleGVkRGF0YSk7XG4gICAgXG4gICAgdmFyIHJlc3VsdCA9IGZvcm1hdHRlcnMub3V0cHV0TG9nRm9ybWF0dGVyKGRhdGEpO1xuICAgIHJlc3VsdC5ldmVudCA9IHRoaXMuZGlzcGxheU5hbWUoKTtcbiAgICByZXN1bHQuYWRkcmVzcyA9IGRhdGEuYWRkcmVzcztcblxuICAgIHJlc3VsdC5hcmdzID0gdGhpcy5fcGFyYW1zLnJlZHVjZShmdW5jdGlvbiAoYWNjLCBjdXJyZW50KSB7XG4gICAgICAgIGFjY1tjdXJyZW50Lm5hbWVdID0gY3VycmVudC5pbmRleGVkID8gaW5kZXhlZFBhcmFtcy5zaGlmdCgpIDogbm90SW5kZXhlZFBhcmFtcy5zaGlmdCgpO1xuICAgICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcblxuICAgIGRlbGV0ZSByZXN1bHQuZGF0YTtcbiAgICBkZWxldGUgcmVzdWx0LnRvcGljcztcblxuICAgIHJldHVybiByZXN1bHQ7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGNyZWF0ZSBuZXcgZmlsdGVyIG9iamVjdCBmcm9tIGV2ZW50XG4gKlxuICogQG1ldGhvZCBleGVjdXRlXG4gKiBAcGFyYW0ge09iamVjdH0gaW5kZXhlZFxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnNcbiAqIEByZXR1cm4ge09iamVjdH0gZmlsdGVyIG9iamVjdFxuICovXG5Tb2xpZGl0eUV2ZW50LnByb3RvdHlwZS5leGVjdXRlID0gZnVuY3Rpb24gKGluZGV4ZWQsIG9wdGlvbnMpIHtcbiAgICB2YXIgbyA9IHRoaXMuZW5jb2RlKGluZGV4ZWQsIG9wdGlvbnMpO1xuICAgIHZhciBmb3JtYXR0ZXIgPSB0aGlzLmRlY29kZS5iaW5kKHRoaXMpO1xuICAgIHJldHVybiB3ZWIzLmV0aC5maWx0ZXIobywgdW5kZWZpbmVkLCB1bmRlZmluZWQsIGZvcm1hdHRlcik7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGF0dGFjaCBldmVudCB0byBjb250cmFjdCBvYmplY3RcbiAqXG4gKiBAbWV0aG9kIGF0dGFjaFRvQ29udHJhY3RcbiAqIEBwYXJhbSB7Q29udHJhY3R9XG4gKi9cblNvbGlkaXR5RXZlbnQucHJvdG90eXBlLmF0dGFjaFRvQ29udHJhY3QgPSBmdW5jdGlvbiAoY29udHJhY3QpIHtcbiAgICB2YXIgZXhlY3V0ZSA9IHRoaXMuZXhlY3V0ZS5iaW5kKHRoaXMpO1xuICAgIHZhciBkaXNwbGF5TmFtZSA9IHRoaXMuZGlzcGxheU5hbWUoKTtcbiAgICBpZiAoIWNvbnRyYWN0W2Rpc3BsYXlOYW1lXSkge1xuICAgICAgICBjb250cmFjdFtkaXNwbGF5TmFtZV0gPSBleGVjdXRlO1xuICAgIH1cbiAgICBjb250cmFjdFtkaXNwbGF5TmFtZV1bdGhpcy50eXBlTmFtZSgpXSA9IHRoaXMuZXhlY3V0ZS5iaW5kKHRoaXMsIGNvbnRyYWN0KTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gU29saWRpdHlFdmVudDtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogQGZpbGUgZmlsdGVyLmpzXG4gKiBAYXV0aG9yczpcbiAqICAgSmVmZnJleSBXaWxja2UgPGplZmZAZXRoZGV2LmNvbT5cbiAqICAgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiAgIE1hcmlhbiBPYW5jZWEgPG1hcmlhbkBldGhkZXYuY29tPlxuICogICBGYWJpYW4gVm9nZWxzdGVsbGVyIDxmYWJpYW5AZXRoZGV2LmNvbT5cbiAqICAgR2F2IFdvb2QgPGdAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTRcbiAqL1xuXG52YXIgUmVxdWVzdE1hbmFnZXIgPSByZXF1aXJlKCcuL3JlcXVlc3RtYW5hZ2VyJyk7XG52YXIgZm9ybWF0dGVycyA9IHJlcXVpcmUoJy4vZm9ybWF0dGVycycpO1xudmFyIHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMvdXRpbHMnKTtcblxuLyoqXG4qIENvbnZlcnRzIGEgZ2l2ZW4gdG9waWMgdG8gYSBoZXggc3RyaW5nLCBidXQgYWxzbyBhbGxvd3MgbnVsbCB2YWx1ZXMuXG4qXG4qIEBwYXJhbSB7TWl4ZWR9IHZhbHVlXG4qIEByZXR1cm4ge1N0cmluZ31cbiovXG52YXIgdG9Ub3BpYyA9IGZ1bmN0aW9uKHZhbHVlKXtcblxuICAgIGlmKHZhbHVlID09PSBudWxsIHx8IHR5cGVvZiB2YWx1ZSA9PT0gJ3VuZGVmaW5lZCcpXG4gICAgICAgIHJldHVybiBudWxsO1xuXG4gICAgdmFsdWUgPSBTdHJpbmcodmFsdWUpO1xuXG4gICAgaWYodmFsdWUuaW5kZXhPZignMHgnKSA9PT0gMClcbiAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgIGVsc2VcbiAgICAgICAgcmV0dXJuIHV0aWxzLmZyb21Bc2NpaSh2YWx1ZSk7XG59O1xuXG4vLy8gVGhpcyBtZXRob2Qgc2hvdWxkIGJlIGNhbGxlZCBvbiBvcHRpb25zIG9iamVjdCwgdG8gdmVyaWZ5IGRlcHJlY2F0ZWQgcHJvcGVydGllcyAmJiBsYXp5IGxvYWQgZHluYW1pYyBvbmVzXG4vLy8gQHBhcmFtIHNob3VsZCBiZSBzdHJpbmcgb3Igb2JqZWN0XG4vLy8gQHJldHVybnMgb3B0aW9ucyBzdHJpbmcgb3Igb2JqZWN0XG52YXIgZ2V0T3B0aW9ucyA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XG5cbiAgICBpZiAodXRpbHMuaXNTdHJpbmcob3B0aW9ucykpIHtcbiAgICAgICAgcmV0dXJuIG9wdGlvbnM7XG4gICAgfSBcblxuICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuXG4gICAgLy8gbWFrZSBzdXJlIHRvcGljcywgZ2V0IGNvbnZlcnRlZCB0byBoZXhcbiAgICBvcHRpb25zLnRvcGljcyA9IG9wdGlvbnMudG9waWNzIHx8IFtdO1xuICAgIG9wdGlvbnMudG9waWNzID0gb3B0aW9ucy50b3BpY3MubWFwKGZ1bmN0aW9uKHRvcGljKXtcbiAgICAgICAgcmV0dXJuICh1dGlscy5pc0FycmF5KHRvcGljKSkgPyB0b3BpYy5tYXAodG9Ub3BpYykgOiB0b1RvcGljKHRvcGljKTtcbiAgICB9KTtcblxuICAgIC8vIGxhenkgbG9hZFxuICAgIHJldHVybiB7XG4gICAgICAgIHRvcGljczogb3B0aW9ucy50b3BpY3MsXG4gICAgICAgIHRvOiBvcHRpb25zLnRvLFxuICAgICAgICBhZGRyZXNzOiBvcHRpb25zLmFkZHJlc3MsXG4gICAgICAgIGZyb21CbG9jazogZm9ybWF0dGVycy5pbnB1dEJsb2NrTnVtYmVyRm9ybWF0dGVyKG9wdGlvbnMuZnJvbUJsb2NrKSxcbiAgICAgICAgdG9CbG9jazogZm9ybWF0dGVycy5pbnB1dEJsb2NrTnVtYmVyRm9ybWF0dGVyKG9wdGlvbnMudG9CbG9jaykgXG4gICAgfTsgXG59O1xuXG52YXIgRmlsdGVyID0gZnVuY3Rpb24gKG9wdGlvbnMsIG1ldGhvZHMsIGZvcm1hdHRlcikge1xuICAgIHZhciBpbXBsZW1lbnRhdGlvbiA9IHt9O1xuICAgIG1ldGhvZHMuZm9yRWFjaChmdW5jdGlvbiAobWV0aG9kKSB7XG4gICAgICAgIG1ldGhvZC5hdHRhY2hUb09iamVjdChpbXBsZW1lbnRhdGlvbik7XG4gICAgfSk7XG4gICAgdGhpcy5vcHRpb25zID0gZ2V0T3B0aW9ucyhvcHRpb25zKTtcbiAgICB0aGlzLmltcGxlbWVudGF0aW9uID0gaW1wbGVtZW50YXRpb247XG4gICAgdGhpcy5jYWxsYmFja3MgPSBbXTtcbiAgICB0aGlzLmZvcm1hdHRlciA9IGZvcm1hdHRlcjtcbiAgICB0aGlzLmZpbHRlcklkID0gdGhpcy5pbXBsZW1lbnRhdGlvbi5uZXdGaWx0ZXIodGhpcy5vcHRpb25zKTtcbn07XG5cbkZpbHRlci5wcm90b3R5cGUud2F0Y2ggPSBmdW5jdGlvbiAoY2FsbGJhY2spIHtcbiAgICB0aGlzLmNhbGxiYWNrcy5wdXNoKGNhbGxiYWNrKTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgICB2YXIgb25NZXNzYWdlID0gZnVuY3Rpb24gKGVycm9yLCBtZXNzYWdlcykge1xuICAgICAgICBpZiAoZXJyb3IpIHtcbiAgICAgICAgICAgIHJldHVybiBzZWxmLmNhbGxiYWNrcy5mb3JFYWNoKGZ1bmN0aW9uIChjYWxsYmFjaykge1xuICAgICAgICAgICAgICAgIGNhbGxiYWNrKGVycm9yKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG5cbiAgICAgICAgbWVzc2FnZXMuZm9yRWFjaChmdW5jdGlvbiAobWVzc2FnZSkge1xuICAgICAgICAgICAgbWVzc2FnZSA9IHNlbGYuZm9ybWF0dGVyID8gc2VsZi5mb3JtYXR0ZXIobWVzc2FnZSkgOiBtZXNzYWdlO1xuICAgICAgICAgICAgc2VsZi5jYWxsYmFja3MuZm9yRWFjaChmdW5jdGlvbiAoY2FsbGJhY2spIHtcbiAgICAgICAgICAgICAgICBjYWxsYmFjayhudWxsLCBtZXNzYWdlKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9O1xuXG4gICAgLy8gY2FsbCBnZXRGaWx0ZXJMb2dzIG9uIHN0YXJ0XG4gICAgaWYgKCF1dGlscy5pc1N0cmluZyh0aGlzLm9wdGlvbnMpKSB7XG4gICAgICAgIHRoaXMuZ2V0KGZ1bmN0aW9uIChlcnIsIG1lc3NhZ2VzKSB7XG4gICAgICAgICAgICAvLyBkb24ndCBzZW5kIGFsbCB0aGUgcmVzcG9uc2VzIHRvIGFsbCB0aGUgd2F0Y2hlcyBhZ2Fpbi4uLiBqdXN0IHRvIHRoaXMgb25lXG4gICAgICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgICAgICAgY2FsbGJhY2soZXJyKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgbWVzc2FnZXMuZm9yRWFjaChmdW5jdGlvbiAobWVzc2FnZSkge1xuICAgICAgICAgICAgICAgIGNhbGxiYWNrKG51bGwsIG1lc3NhZ2UpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIFJlcXVlc3RNYW5hZ2VyLmdldEluc3RhbmNlKCkuc3RhcnRQb2xsaW5nKHtcbiAgICAgICAgbWV0aG9kOiB0aGlzLmltcGxlbWVudGF0aW9uLnBvbGwuY2FsbCxcbiAgICAgICAgcGFyYW1zOiBbdGhpcy5maWx0ZXJJZF0sXG4gICAgfSwgdGhpcy5maWx0ZXJJZCwgb25NZXNzYWdlLCB0aGlzLnN0b3BXYXRjaGluZy5iaW5kKHRoaXMpKTtcbn07XG5cbkZpbHRlci5wcm90b3R5cGUuc3RvcFdhdGNoaW5nID0gZnVuY3Rpb24gKCkge1xuICAgIFJlcXVlc3RNYW5hZ2VyLmdldEluc3RhbmNlKCkuc3RvcFBvbGxpbmcodGhpcy5maWx0ZXJJZCk7XG4gICAgdGhpcy5pbXBsZW1lbnRhdGlvbi51bmluc3RhbGxGaWx0ZXIodGhpcy5maWx0ZXJJZCk7XG4gICAgdGhpcy5jYWxsYmFja3MgPSBbXTtcbn07XG5cbkZpbHRlci5wcm90b3R5cGUuZ2V0ID0gZnVuY3Rpb24gKGNhbGxiYWNrKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIGlmICh1dGlscy5pc0Z1bmN0aW9uKGNhbGxiYWNrKSkge1xuICAgICAgICB0aGlzLmltcGxlbWVudGF0aW9uLmdldExvZ3ModGhpcy5maWx0ZXJJZCwgZnVuY3Rpb24oZXJyLCByZXMpe1xuICAgICAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgICAgICAgIGNhbGxiYWNrKGVycik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGNhbGxiYWNrKG51bGwsIHJlcy5tYXAoZnVuY3Rpb24gKGxvZykge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gc2VsZi5mb3JtYXR0ZXIgPyBzZWxmLmZvcm1hdHRlcihsb2cpIDogbG9nO1xuICAgICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgdmFyIGxvZ3MgPSB0aGlzLmltcGxlbWVudGF0aW9uLmdldExvZ3ModGhpcy5maWx0ZXJJZCk7XG4gICAgICAgIHJldHVybiBsb2dzLm1hcChmdW5jdGlvbiAobG9nKSB7XG4gICAgICAgICAgICByZXR1cm4gc2VsZi5mb3JtYXR0ZXIgPyBzZWxmLmZvcm1hdHRlcihsb2cpIDogbG9nO1xuICAgICAgICB9KTtcbiAgICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEZpbHRlcjtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogXG4gKiBAZmlsZSBmb3JtYXR0ZXJzLmpzXG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGF1dGhvciBGYWJpYW4gVm9nZWxzdGVsbGVyIDxmYWJpYW5AZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xudmFyIGNvbmZpZyA9IHJlcXVpcmUoJy4uL3V0aWxzL2NvbmZpZycpO1xuXG4vKipcbiAqIFNob3VsZCB0aGUgZm9ybWF0IG91dHB1dCB0byBhIGJpZyBudW1iZXJcbiAqXG4gKiBAbWV0aG9kIG91dHB1dEJpZ051bWJlckZvcm1hdHRlclxuICogQHBhcmFtIHtTdHJpbmd8TnVtYmVyfEJpZ051bWJlcn1cbiAqIEByZXR1cm5zIHtCaWdOdW1iZXJ9IG9iamVjdFxuICovXG52YXIgb3V0cHV0QmlnTnVtYmVyRm9ybWF0dGVyID0gZnVuY3Rpb24gKG51bWJlcikge1xuICAgIHJldHVybiB1dGlscy50b0JpZ051bWJlcihudW1iZXIpO1xufTtcblxudmFyIGlzUHJlZGVmaW5lZEJsb2NrTnVtYmVyID0gZnVuY3Rpb24gKGJsb2NrTnVtYmVyKSB7XG4gICAgcmV0dXJuIGJsb2NrTnVtYmVyID09PSAnbGF0ZXN0JyB8fCBibG9ja051bWJlciA9PT0gJ3BlbmRpbmcnIHx8IGJsb2NrTnVtYmVyID09PSAnZWFybGllc3QnO1xufTtcblxudmFyIGlucHV0RGVmYXVsdEJsb2NrTnVtYmVyRm9ybWF0dGVyID0gZnVuY3Rpb24gKGJsb2NrTnVtYmVyKSB7XG4gICAgaWYgKGJsb2NrTnVtYmVyID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGNvbmZpZy5kZWZhdWx0QmxvY2s7XG4gICAgfVxuICAgIHJldHVybiBpbnB1dEJsb2NrTnVtYmVyRm9ybWF0dGVyKGJsb2NrTnVtYmVyKTtcbn07XG5cbnZhciBpbnB1dEJsb2NrTnVtYmVyRm9ybWF0dGVyID0gZnVuY3Rpb24gKGJsb2NrTnVtYmVyKSB7XG4gICAgaWYgKGJsb2NrTnVtYmVyID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9IGVsc2UgaWYgKGlzUHJlZGVmaW5lZEJsb2NrTnVtYmVyKGJsb2NrTnVtYmVyKSkge1xuICAgICAgICByZXR1cm4gYmxvY2tOdW1iZXI7XG4gICAgfVxuICAgIHJldHVybiB1dGlscy50b0hleChibG9ja051bWJlcik7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgdGhlIGlucHV0IG9mIGEgdHJhbnNhY3Rpb24gYW5kIGNvbnZlcnRzIGFsbCB2YWx1ZXMgdG8gSEVYXG4gKlxuICogQG1ldGhvZCBpbnB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyXG4gKiBAcGFyYW0ge09iamVjdH0gdHJhbnNhY3Rpb24gb3B0aW9uc1xuICogQHJldHVybnMgb2JqZWN0XG4qL1xudmFyIGlucHV0VHJhbnNhY3Rpb25Gb3JtYXR0ZXIgPSBmdW5jdGlvbiAob3B0aW9ucyl7XG5cbiAgICBvcHRpb25zLmZyb20gPSBvcHRpb25zLmZyb20gfHwgY29uZmlnLmRlZmF1bHRBY2NvdW50O1xuXG4gICAgLy8gbWFrZSBjb2RlIC0+IGRhdGFcbiAgICBpZiAob3B0aW9ucy5jb2RlKSB7XG4gICAgICAgIG9wdGlvbnMuZGF0YSA9IG9wdGlvbnMuY29kZTtcbiAgICAgICAgZGVsZXRlIG9wdGlvbnMuY29kZTtcbiAgICB9XG5cbiAgICBbJ2dhc1ByaWNlJywgJ2dhcycsICd2YWx1ZScsICdub25jZSddLmZpbHRlcihmdW5jdGlvbiAoa2V5KSB7XG4gICAgICAgIHJldHVybiBvcHRpb25zW2tleV0gIT09IHVuZGVmaW5lZDtcbiAgICB9KS5mb3JFYWNoKGZ1bmN0aW9uKGtleSl7XG4gICAgICAgIG9wdGlvbnNba2V5XSA9IHV0aWxzLmZyb21EZWNpbWFsKG9wdGlvbnNba2V5XSk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gb3B0aW9uczsgXG59O1xuXG4vKipcbiAqIEZvcm1hdHMgdGhlIG91dHB1dCBvZiBhIHRyYW5zYWN0aW9uIHRvIGl0cyBwcm9wZXIgdmFsdWVzXG4gKiBcbiAqIEBtZXRob2Qgb3V0cHV0VHJhbnNhY3Rpb25Gb3JtYXR0ZXJcbiAqIEBwYXJhbSB7T2JqZWN0fSB0cmFuc2FjdGlvblxuICogQHJldHVybnMge09iamVjdH0gdHJhbnNhY3Rpb25cbiovXG52YXIgb3V0cHV0VHJhbnNhY3Rpb25Gb3JtYXR0ZXIgPSBmdW5jdGlvbiAodHgpe1xuICAgIHR4LmJsb2NrTnVtYmVyID0gdXRpbHMudG9EZWNpbWFsKHR4LmJsb2NrTnVtYmVyKTtcbiAgICB0eC50cmFuc2FjdGlvbkluZGV4ID0gdXRpbHMudG9EZWNpbWFsKHR4LnRyYW5zYWN0aW9uSW5kZXgpO1xuICAgIHR4Lm5vbmNlID0gdXRpbHMudG9EZWNpbWFsKHR4Lm5vbmNlKTtcbiAgICB0eC5nYXMgPSB1dGlscy50b0RlY2ltYWwodHguZ2FzKTtcbiAgICB0eC5nYXNQcmljZSA9IHV0aWxzLnRvQmlnTnVtYmVyKHR4Lmdhc1ByaWNlKTtcbiAgICB0eC52YWx1ZSA9IHV0aWxzLnRvQmlnTnVtYmVyKHR4LnZhbHVlKTtcbiAgICByZXR1cm4gdHg7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgdGhlIG91dHB1dCBvZiBhIGJsb2NrIHRvIGl0cyBwcm9wZXIgdmFsdWVzXG4gKlxuICogQG1ldGhvZCBvdXRwdXRCbG9ja0Zvcm1hdHRlclxuICogQHBhcmFtIHtPYmplY3R9IGJsb2NrIG9iamVjdCBcbiAqIEByZXR1cm5zIHtPYmplY3R9IGJsb2NrIG9iamVjdFxuKi9cbnZhciBvdXRwdXRCbG9ja0Zvcm1hdHRlciA9IGZ1bmN0aW9uKGJsb2NrKSB7XG5cbiAgICAvLyB0cmFuc2Zvcm0gdG8gbnVtYmVyXG4gICAgYmxvY2suZ2FzTGltaXQgPSB1dGlscy50b0RlY2ltYWwoYmxvY2suZ2FzTGltaXQpO1xuICAgIGJsb2NrLmdhc1VzZWQgPSB1dGlscy50b0RlY2ltYWwoYmxvY2suZ2FzVXNlZCk7XG4gICAgYmxvY2suc2l6ZSA9IHV0aWxzLnRvRGVjaW1hbChibG9jay5zaXplKTtcbiAgICBibG9jay50aW1lc3RhbXAgPSB1dGlscy50b0RlY2ltYWwoYmxvY2sudGltZXN0YW1wKTtcbiAgICBibG9jay5udW1iZXIgPSB1dGlscy50b0RlY2ltYWwoYmxvY2subnVtYmVyKTtcblxuICAgIGJsb2NrLmRpZmZpY3VsdHkgPSB1dGlscy50b0JpZ051bWJlcihibG9jay5kaWZmaWN1bHR5KTtcbiAgICBibG9jay50b3RhbERpZmZpY3VsdHkgPSB1dGlscy50b0JpZ051bWJlcihibG9jay50b3RhbERpZmZpY3VsdHkpO1xuXG4gICAgaWYgKHV0aWxzLmlzQXJyYXkoYmxvY2sudHJhbnNhY3Rpb25zKSkge1xuICAgICAgICBibG9jay50cmFuc2FjdGlvbnMuZm9yRWFjaChmdW5jdGlvbihpdGVtKXtcbiAgICAgICAgICAgIGlmKCF1dGlscy5pc1N0cmluZyhpdGVtKSlcbiAgICAgICAgICAgICAgICByZXR1cm4gb3V0cHV0VHJhbnNhY3Rpb25Gb3JtYXR0ZXIoaXRlbSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHJldHVybiBibG9jaztcbn07XG5cbi8qKlxuICogRm9ybWF0cyB0aGUgb3V0cHV0IG9mIGEgbG9nXG4gKiBcbiAqIEBtZXRob2Qgb3V0cHV0TG9nRm9ybWF0dGVyXG4gKiBAcGFyYW0ge09iamVjdH0gbG9nIG9iamVjdFxuICogQHJldHVybnMge09iamVjdH0gbG9nXG4qL1xudmFyIG91dHB1dExvZ0Zvcm1hdHRlciA9IGZ1bmN0aW9uKGxvZykge1xuICAgIGlmIChsb2cgPT09IG51bGwpIHsgLy8gJ3BlbmRpbmcnICYmICdsYXRlc3QnIGZpbHRlcnMgYXJlIG51bGxzXG4gICAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGxvZy5ibG9ja051bWJlciA9IHV0aWxzLnRvRGVjaW1hbChsb2cuYmxvY2tOdW1iZXIpO1xuICAgIGxvZy50cmFuc2FjdGlvbkluZGV4ID0gdXRpbHMudG9EZWNpbWFsKGxvZy50cmFuc2FjdGlvbkluZGV4KTtcbiAgICBsb2cubG9nSW5kZXggPSB1dGlscy50b0RlY2ltYWwobG9nLmxvZ0luZGV4KTtcblxuICAgIHJldHVybiBsb2c7XG59O1xuXG4vKipcbiAqIEZvcm1hdHMgdGhlIGlucHV0IG9mIGEgd2hpc3BlciBwb3N0IGFuZCBjb252ZXJ0cyBhbGwgdmFsdWVzIHRvIEhFWFxuICpcbiAqIEBtZXRob2QgaW5wdXRQb3N0Rm9ybWF0dGVyXG4gKiBAcGFyYW0ge09iamVjdH0gdHJhbnNhY3Rpb24gb2JqZWN0XG4gKiBAcmV0dXJucyB7T2JqZWN0fVxuKi9cbnZhciBpbnB1dFBvc3RGb3JtYXR0ZXIgPSBmdW5jdGlvbihwb3N0KSB7XG5cbiAgICBwb3N0LnBheWxvYWQgPSB1dGlscy50b0hleChwb3N0LnBheWxvYWQpO1xuICAgIHBvc3QudHRsID0gdXRpbHMuZnJvbURlY2ltYWwocG9zdC50dGwpO1xuICAgIHBvc3Qud29ya1RvUHJvdmUgPSB1dGlscy5mcm9tRGVjaW1hbChwb3N0LndvcmtUb1Byb3ZlKTtcbiAgICBwb3N0LnByaW9yaXR5ID0gdXRpbHMuZnJvbURlY2ltYWwocG9zdC5wcmlvcml0eSk7XG5cbiAgICAvLyBmYWxsYmFja1xuICAgIGlmICghdXRpbHMuaXNBcnJheShwb3N0LnRvcGljcykpIHtcbiAgICAgICAgcG9zdC50b3BpY3MgPSBwb3N0LnRvcGljcyA/IFtwb3N0LnRvcGljc10gOiBbXTtcbiAgICB9XG5cbiAgICAvLyBmb3JtYXQgdGhlIGZvbGxvd2luZyBvcHRpb25zXG4gICAgcG9zdC50b3BpY3MgPSBwb3N0LnRvcGljcy5tYXAoZnVuY3Rpb24odG9waWMpe1xuICAgICAgICByZXR1cm4gdXRpbHMuZnJvbUFzY2lpKHRvcGljKTtcbiAgICB9KTtcblxuICAgIHJldHVybiBwb3N0OyBcbn07XG5cbi8qKlxuICogRm9ybWF0cyB0aGUgb3V0cHV0IG9mIGEgcmVjZWl2ZWQgcG9zdCBtZXNzYWdlXG4gKlxuICogQG1ldGhvZCBvdXRwdXRQb3N0Rm9ybWF0dGVyXG4gKiBAcGFyYW0ge09iamVjdH1cbiAqIEByZXR1cm5zIHtPYmplY3R9XG4gKi9cbnZhciBvdXRwdXRQb3N0Rm9ybWF0dGVyID0gZnVuY3Rpb24ocG9zdCl7XG5cbiAgICBwb3N0LmV4cGlyeSA9IHV0aWxzLnRvRGVjaW1hbChwb3N0LmV4cGlyeSk7XG4gICAgcG9zdC5zZW50ID0gdXRpbHMudG9EZWNpbWFsKHBvc3Quc2VudCk7XG4gICAgcG9zdC50dGwgPSB1dGlscy50b0RlY2ltYWwocG9zdC50dGwpO1xuICAgIHBvc3Qud29ya1Byb3ZlZCA9IHV0aWxzLnRvRGVjaW1hbChwb3N0LndvcmtQcm92ZWQpO1xuICAgIHBvc3QucGF5bG9hZFJhdyA9IHBvc3QucGF5bG9hZDtcbiAgICBwb3N0LnBheWxvYWQgPSB1dGlscy50b0FzY2lpKHBvc3QucGF5bG9hZCk7XG5cbiAgICBpZiAodXRpbHMuaXNKc29uKHBvc3QucGF5bG9hZCkpIHtcbiAgICAgICAgcG9zdC5wYXlsb2FkID0gSlNPTi5wYXJzZShwb3N0LnBheWxvYWQpO1xuICAgIH1cblxuICAgIC8vIGZvcm1hdCB0aGUgZm9sbG93aW5nIG9wdGlvbnNcbiAgICBpZiAoIXBvc3QudG9waWNzKSB7XG4gICAgICAgIHBvc3QudG9waWNzID0gW107XG4gICAgfVxuICAgIHBvc3QudG9waWNzID0gcG9zdC50b3BpY3MubWFwKGZ1bmN0aW9uKHRvcGljKXtcbiAgICAgICAgcmV0dXJuIHV0aWxzLnRvQXNjaWkodG9waWMpO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIHBvc3Q7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBpbnB1dERlZmF1bHRCbG9ja051bWJlckZvcm1hdHRlcjogaW5wdXREZWZhdWx0QmxvY2tOdW1iZXJGb3JtYXR0ZXIsXG4gICAgaW5wdXRCbG9ja051bWJlckZvcm1hdHRlcjogaW5wdXRCbG9ja051bWJlckZvcm1hdHRlcixcbiAgICBpbnB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyOiBpbnB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyLFxuICAgIGlucHV0UG9zdEZvcm1hdHRlcjogaW5wdXRQb3N0Rm9ybWF0dGVyLFxuICAgIG91dHB1dEJpZ051bWJlckZvcm1hdHRlcjogb3V0cHV0QmlnTnVtYmVyRm9ybWF0dGVyLFxuICAgIG91dHB1dFRyYW5zYWN0aW9uRm9ybWF0dGVyOiBvdXRwdXRUcmFuc2FjdGlvbkZvcm1hdHRlcixcbiAgICBvdXRwdXRCbG9ja0Zvcm1hdHRlcjogb3V0cHV0QmxvY2tGb3JtYXR0ZXIsXG4gICAgb3V0cHV0TG9nRm9ybWF0dGVyOiBvdXRwdXRMb2dGb3JtYXR0ZXIsXG4gICAgb3V0cHV0UG9zdEZvcm1hdHRlcjogb3V0cHV0UG9zdEZvcm1hdHRlclxufTtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKipcbiAqIEBmaWxlIGZ1bmN0aW9uLmpzXG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciB3ZWIzID0gcmVxdWlyZSgnLi4vd2ViMycpO1xudmFyIGNvZGVyID0gcmVxdWlyZSgnLi4vc29saWRpdHkvY29kZXInKTtcbnZhciB1dGlscyA9IHJlcXVpcmUoJy4uL3V0aWxzL3V0aWxzJyk7XG52YXIgc2hhMyA9IHJlcXVpcmUoJy4uL3V0aWxzL3NoYTMnKTtcblxuLyoqXG4gKiBUaGlzIHByb3RvdHlwZSBzaG91bGQgYmUgdXNlZCB0byBjYWxsL3NlbmRUcmFuc2FjdGlvbiB0byBzb2xpZGl0eSBmdW5jdGlvbnNcbiAqL1xudmFyIFNvbGlkaXR5RnVuY3Rpb24gPSBmdW5jdGlvbiAoanNvbiwgYWRkcmVzcykge1xuICAgIHRoaXMuX2lucHV0VHlwZXMgPSBqc29uLmlucHV0cy5tYXAoZnVuY3Rpb24gKGkpIHtcbiAgICAgICAgcmV0dXJuIGkudHlwZTtcbiAgICB9KTtcbiAgICB0aGlzLl9vdXRwdXRUeXBlcyA9IGpzb24ub3V0cHV0cy5tYXAoZnVuY3Rpb24gKGkpIHtcbiAgICAgICAgcmV0dXJuIGkudHlwZTtcbiAgICB9KTtcbiAgICB0aGlzLl9jb25zdGFudCA9IGpzb24uY29uc3RhbnQ7XG4gICAgdGhpcy5fbmFtZSA9IHV0aWxzLnRyYW5zZm9ybVRvRnVsbE5hbWUoanNvbik7XG4gICAgdGhpcy5fYWRkcmVzcyA9IGFkZHJlc3M7XG59O1xuXG5Tb2xpZGl0eUZ1bmN0aW9uLnByb3RvdHlwZS5leHRyYWN0Q2FsbGJhY2sgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIGlmICh1dGlscy5pc0Z1bmN0aW9uKGFyZ3NbYXJncy5sZW5ndGggLSAxXSkpIHtcbiAgICAgICAgcmV0dXJuIGFyZ3MucG9wKCk7IC8vIG1vZGlmeSB0aGUgYXJncyBhcnJheSFcbiAgICB9XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGNyZWF0ZSBwYXlsb2FkIGZyb20gYXJndW1lbnRzXG4gKlxuICogQG1ldGhvZCB0b1BheWxvYWRcbiAqIEBwYXJhbSB7QXJyYXl9IHNvbGlkaXR5IGZ1bmN0aW9uIHBhcmFtc1xuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbmFsIHBheWxvYWQgb3B0aW9uc1xuICovXG5Tb2xpZGl0eUZ1bmN0aW9uLnByb3RvdHlwZS50b1BheWxvYWQgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIHZhciBvcHRpb25zID0ge307XG4gICAgaWYgKGFyZ3MubGVuZ3RoID4gdGhpcy5faW5wdXRUeXBlcy5sZW5ndGggJiYgdXRpbHMuaXNPYmplY3QoYXJnc1thcmdzLmxlbmd0aCAtMV0pKSB7XG4gICAgICAgIG9wdGlvbnMgPSBhcmdzW2FyZ3MubGVuZ3RoIC0gMV07XG4gICAgfVxuICAgIG9wdGlvbnMudG8gPSB0aGlzLl9hZGRyZXNzO1xuICAgIG9wdGlvbnMuZGF0YSA9ICcweCcgKyB0aGlzLnNpZ25hdHVyZSgpICsgY29kZXIuZW5jb2RlUGFyYW1zKHRoaXMuX2lucHV0VHlwZXMsIGFyZ3MpO1xuICAgIHJldHVybiBvcHRpb25zO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBnZXQgZnVuY3Rpb24gc2lnbmF0dXJlXG4gKlxuICogQG1ldGhvZCBzaWduYXR1cmVcbiAqIEByZXR1cm4ge1N0cmluZ30gZnVuY3Rpb24gc2lnbmF0dXJlXG4gKi9cblNvbGlkaXR5RnVuY3Rpb24ucHJvdG90eXBlLnNpZ25hdHVyZSA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gc2hhMyh0aGlzLl9uYW1lKS5zbGljZSgwLCA4KTtcbn07XG5cblxuU29saWRpdHlGdW5jdGlvbi5wcm90b3R5cGUudW5wYWNrT3V0cHV0ID0gZnVuY3Rpb24gKG91dHB1dCkge1xuICAgIGlmICghb3V0cHV0KSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBvdXRwdXQgPSBvdXRwdXQubGVuZ3RoID49IDIgPyBvdXRwdXQuc2xpY2UoMikgOiBvdXRwdXQ7XG4gICAgdmFyIHJlc3VsdCA9IGNvZGVyLmRlY29kZVBhcmFtcyh0aGlzLl9vdXRwdXRUeXBlcywgb3V0cHV0KTtcbiAgICByZXR1cm4gcmVzdWx0Lmxlbmd0aCA9PT0gMSA/IHJlc3VsdFswXSA6IHJlc3VsdDtcbn07XG5cbi8qKlxuICogQ2FsbHMgYSBjb250cmFjdCBmdW5jdGlvbi5cbiAqXG4gKiBAbWV0aG9kIGNhbGxcbiAqIEBwYXJhbSB7Li4uT2JqZWN0fSBDb250cmFjdCBmdW5jdGlvbiBhcmd1bWVudHNcbiAqIEBwYXJhbSB7ZnVuY3Rpb259IElmIHRoZSBsYXN0IGFyZ3VtZW50IGlzIGEgZnVuY3Rpb24sIHRoZSBjb250cmFjdCBmdW5jdGlvblxuICogICBjYWxsIHdpbGwgYmUgYXN5bmNocm9ub3VzLCBhbmQgdGhlIGNhbGxiYWNrIHdpbGwgYmUgcGFzc2VkIHRoZVxuICogICBlcnJvciBhbmQgcmVzdWx0LlxuICogQHJldHVybiB7U3RyaW5nfSBvdXRwdXQgYnl0ZXNcbiAqL1xuU29saWRpdHlGdW5jdGlvbi5wcm90b3R5cGUuY2FsbCA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cykuZmlsdGVyKGZ1bmN0aW9uIChhKSB7cmV0dXJuIGEgIT09IHVuZGVmaW5lZDsgfSk7XG4gICAgdmFyIGNhbGxiYWNrID0gdGhpcy5leHRyYWN0Q2FsbGJhY2soYXJncyk7XG4gICAgdmFyIHBheWxvYWQgPSB0aGlzLnRvUGF5bG9hZChhcmdzKTtcblxuICAgIGlmICghY2FsbGJhY2spIHtcbiAgICAgICAgdmFyIG91dHB1dCA9IHdlYjMuZXRoLmNhbGwocGF5bG9hZCk7XG4gICAgICAgIHJldHVybiB0aGlzLnVucGFja091dHB1dChvdXRwdXQpO1xuICAgIH0gXG4gICAgICAgIFxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB3ZWIzLmV0aC5jYWxsKHBheWxvYWQsIGZ1bmN0aW9uIChlcnJvciwgb3V0cHV0KSB7XG4gICAgICAgIGNhbGxiYWNrKGVycm9yLCBzZWxmLnVucGFja091dHB1dChvdXRwdXQpKTtcbiAgICB9KTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gc2VuZFRyYW5zYWN0aW9uIHRvIHNvbGlkaXR5IGZ1bmN0aW9uXG4gKlxuICogQG1ldGhvZCBzZW5kVHJhbnNhY3Rpb25cbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zXG4gKi9cblNvbGlkaXR5RnVuY3Rpb24ucHJvdG90eXBlLnNlbmRUcmFuc2FjdGlvbiA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cykuZmlsdGVyKGZ1bmN0aW9uIChhKSB7cmV0dXJuIGEgIT09IHVuZGVmaW5lZDsgfSk7XG4gICAgdmFyIGNhbGxiYWNrID0gdGhpcy5leHRyYWN0Q2FsbGJhY2soYXJncyk7XG4gICAgdmFyIHBheWxvYWQgPSB0aGlzLnRvUGF5bG9hZChhcmdzKTtcblxuICAgIGlmICghY2FsbGJhY2spIHtcbiAgICAgICAgcmV0dXJuIHdlYjMuZXRoLnNlbmRUcmFuc2FjdGlvbihwYXlsb2FkKTtcbiAgICB9XG5cbiAgICB3ZWIzLmV0aC5zZW5kVHJhbnNhY3Rpb24ocGF5bG9hZCwgY2FsbGJhY2spO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBlc3RpbWF0ZUdhcyBvZiBzb2xpZGl0eSBmdW5jdGlvblxuICpcbiAqIEBtZXRob2QgZXN0aW1hdGVHYXNcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zXG4gKi9cblNvbGlkaXR5RnVuY3Rpb24ucHJvdG90eXBlLmVzdGltYXRlR2FzID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKTtcbiAgICB2YXIgY2FsbGJhY2sgPSB0aGlzLmV4dHJhY3RDYWxsYmFjayhhcmdzKTtcbiAgICB2YXIgcGF5bG9hZCA9IHRoaXMudG9QYXlsb2FkKGFyZ3MpO1xuXG4gICAgaWYgKCFjYWxsYmFjaykge1xuICAgICAgICByZXR1cm4gd2ViMy5ldGguZXN0aW1hdGVHYXMocGF5bG9hZCk7XG4gICAgfVxuXG4gICAgd2ViMy5ldGguZXN0aW1hdGVHYXMocGF5bG9hZCwgY2FsbGJhY2spO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBnZXQgZnVuY3Rpb24gZGlzcGxheSBuYW1lXG4gKlxuICogQG1ldGhvZCBkaXNwbGF5TmFtZVxuICogQHJldHVybiB7U3RyaW5nfSBkaXNwbGF5IG5hbWUgb2YgdGhlIGZ1bmN0aW9uXG4gKi9cblNvbGlkaXR5RnVuY3Rpb24ucHJvdG90eXBlLmRpc3BsYXlOYW1lID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB1dGlscy5leHRyYWN0RGlzcGxheU5hbWUodGhpcy5fbmFtZSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGdldCBmdW5jdGlvbiB0eXBlIG5hbWVcbiAqXG4gKiBAbWV0aG9kIHR5cGVOYW1lXG4gKiBAcmV0dXJuIHtTdHJpbmd9IHR5cGUgbmFtZSBvZiB0aGUgZnVuY3Rpb25cbiAqL1xuU29saWRpdHlGdW5jdGlvbi5wcm90b3R5cGUudHlwZU5hbWUgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHV0aWxzLmV4dHJhY3RUeXBlTmFtZSh0aGlzLl9uYW1lKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBnZXQgcnBjIHJlcXVlc3RzIGZyb20gc29saWRpdHkgZnVuY3Rpb25cbiAqXG4gKiBAbWV0aG9kIHJlcXVlc3RcbiAqIEByZXR1cm5zIHtPYmplY3R9XG4gKi9cblNvbGlkaXR5RnVuY3Rpb24ucHJvdG90eXBlLnJlcXVlc3QgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGFyZ3MgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMpO1xuICAgIHZhciBjYWxsYmFjayA9IHRoaXMuZXh0cmFjdENhbGxiYWNrKGFyZ3MpO1xuICAgIHZhciBwYXlsb2FkID0gdGhpcy50b1BheWxvYWQoYXJncyk7XG4gICAgdmFyIGZvcm1hdCA9IHRoaXMudW5wYWNrT3V0cHV0LmJpbmQodGhpcyk7XG4gICAgXG4gICAgcmV0dXJuIHtcbiAgICAgICAgY2FsbGJhY2s6IGNhbGxiYWNrLFxuICAgICAgICBwYXlsb2FkOiBwYXlsb2FkLCBcbiAgICAgICAgZm9ybWF0OiBmb3JtYXRcbiAgICB9O1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGV4ZWN1dGUgZnVuY3Rpb25cbiAqXG4gKiBAbWV0aG9kIGV4ZWN1dGVcbiAqL1xuU29saWRpdHlGdW5jdGlvbi5wcm90b3R5cGUuZXhlY3V0ZSA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgdHJhbnNhY3Rpb24gPSAhdGhpcy5fY29uc3RhbnQ7XG5cbiAgICAvLyBzZW5kIHRyYW5zYWN0aW9uXG4gICAgaWYgKHRyYW5zYWN0aW9uKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnNlbmRUcmFuc2FjdGlvbi5hcHBseSh0aGlzLCBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMpKTtcbiAgICB9XG5cbiAgICAvLyBjYWxsXG4gICAgcmV0dXJuIHRoaXMuY2FsbC5hcHBseSh0aGlzLCBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMpKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBhdHRhY2ggZnVuY3Rpb24gdG8gY29udHJhY3RcbiAqXG4gKiBAbWV0aG9kIGF0dGFjaFRvQ29udHJhY3RcbiAqIEBwYXJhbSB7Q29udHJhY3R9XG4gKi9cblNvbGlkaXR5RnVuY3Rpb24ucHJvdG90eXBlLmF0dGFjaFRvQ29udHJhY3QgPSBmdW5jdGlvbiAoY29udHJhY3QpIHtcbiAgICB2YXIgZXhlY3V0ZSA9IHRoaXMuZXhlY3V0ZS5iaW5kKHRoaXMpO1xuICAgIGV4ZWN1dGUucmVxdWVzdCA9IHRoaXMucmVxdWVzdC5iaW5kKHRoaXMpO1xuICAgIGV4ZWN1dGUuY2FsbCA9IHRoaXMuY2FsbC5iaW5kKHRoaXMpO1xuICAgIGV4ZWN1dGUuc2VuZFRyYW5zYWN0aW9uID0gdGhpcy5zZW5kVHJhbnNhY3Rpb24uYmluZCh0aGlzKTtcbiAgICBleGVjdXRlLmVzdGltYXRlR2FzID0gdGhpcy5lc3RpbWF0ZUdhcy5iaW5kKHRoaXMpO1xuICAgIHZhciBkaXNwbGF5TmFtZSA9IHRoaXMuZGlzcGxheU5hbWUoKTtcbiAgICBpZiAoIWNvbnRyYWN0W2Rpc3BsYXlOYW1lXSkge1xuICAgICAgICBjb250cmFjdFtkaXNwbGF5TmFtZV0gPSBleGVjdXRlO1xuICAgIH1cbiAgICBjb250cmFjdFtkaXNwbGF5TmFtZV1bdGhpcy50eXBlTmFtZSgpXSA9IGV4ZWN1dGU7IC8vIGNpcmN1bGFyISEhIVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBTb2xpZGl0eUZ1bmN0aW9uO1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBAZmlsZSBodHRwcHJvdmlkZXIuanNcbiAqIEBhdXRob3JzOlxuICogICBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqICAgTWFyaWFuIE9hbmNlYSA8bWFyaWFuQGV0aGRldi5jb20+XG4gKiAgIEZhYmlhbiBWb2dlbHN0ZWxsZXIgPGZhYmlhbkBldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNFxuICovXG5cblwidXNlIHN0cmljdFwiO1xuXG52YXIgWE1MSHR0cFJlcXVlc3QgPSByZXF1aXJlKCd4bWxodHRwcmVxdWVzdCcpLlhNTEh0dHBSZXF1ZXN0OyAvLyBqc2hpbnQgaWdub3JlOmxpbmVcbnZhciBlcnJvcnMgPSByZXF1aXJlKCcuL2Vycm9ycycpO1xuXG52YXIgSHR0cFByb3ZpZGVyID0gZnVuY3Rpb24gKGhvc3QpIHtcbiAgICB0aGlzLmhvc3QgPSBob3N0IHx8ICdodHRwOi8vbG9jYWxob3N0Ojg1NDUnO1xufTtcblxuSHR0cFByb3ZpZGVyLnByb3RvdHlwZS5zZW5kID0gZnVuY3Rpb24gKHBheWxvYWQpIHtcbiAgICB2YXIgcmVxdWVzdCA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO1xuXG4gICAgcmVxdWVzdC5vcGVuKCdQT1NUJywgdGhpcy5ob3N0LCBmYWxzZSk7XG4gICAgXG4gICAgdHJ5IHtcbiAgICAgICAgcmVxdWVzdC5zZW5kKEpTT04uc3RyaW5naWZ5KHBheWxvYWQpKTtcbiAgICB9IGNhdGNoKGVycm9yKSB7XG4gICAgICAgIHRocm93IGVycm9ycy5JbnZhbGlkQ29ubmVjdGlvbih0aGlzLmhvc3QpO1xuICAgIH1cblxuXG4gICAgLy8gY2hlY2sgcmVxdWVzdC5zdGF0dXNcbiAgICAvLyBUT0RPOiB0aHJvdyBhbiBlcnJvciBoZXJlISBpdCBjYW5ub3Qgc2lsZW50bHkgZmFpbCEhIVxuICAgIC8vaWYgKHJlcXVlc3Quc3RhdHVzICE9PSAyMDApIHtcbiAgICAgICAgLy9yZXR1cm47XG4gICAgLy99XG5cbiAgICB2YXIgcmVzdWx0ID0gcmVxdWVzdC5yZXNwb25zZVRleHQ7XG5cbiAgICB0cnkge1xuICAgICAgICByZXN1bHQgPSBKU09OLnBhcnNlKHJlc3VsdCk7XG4gICAgfSBjYXRjaChlKSB7XG4gICAgICAgIHRocm93IGVycm9ycy5JbnZhbGlkUmVzcG9uc2UocmVzdWx0KTsgICAgICAgICAgICAgICAgXG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdDtcbn07XG5cbkh0dHBQcm92aWRlci5wcm90b3R5cGUuc2VuZEFzeW5jID0gZnVuY3Rpb24gKHBheWxvYWQsIGNhbGxiYWNrKSB7XG4gICAgdmFyIHJlcXVlc3QgPSBuZXcgWE1MSHR0cFJlcXVlc3QoKTtcbiAgICByZXF1ZXN0Lm9ucmVhZHlzdGF0ZWNoYW5nZSA9IGZ1bmN0aW9uKCkge1xuICAgICAgICBpZiAocmVxdWVzdC5yZWFkeVN0YXRlID09PSA0KSB7XG4gICAgICAgICAgICB2YXIgcmVzdWx0ID0gcmVxdWVzdC5yZXNwb25zZVRleHQ7XG4gICAgICAgICAgICB2YXIgZXJyb3IgPSBudWxsO1xuXG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIHJlc3VsdCA9IEpTT04ucGFyc2UocmVzdWx0KTtcbiAgICAgICAgICAgIH0gY2F0Y2goZSkge1xuICAgICAgICAgICAgICAgIGVycm9yID0gZXJyb3JzLkludmFsaWRSZXNwb25zZShyZXN1bHQpOyAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY2FsbGJhY2soZXJyb3IsIHJlc3VsdCk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgcmVxdWVzdC5vcGVuKCdQT1NUJywgdGhpcy5ob3N0LCB0cnVlKTtcblxuICAgIHRyeSB7XG4gICAgICAgIHJlcXVlc3Quc2VuZChKU09OLnN0cmluZ2lmeShwYXlsb2FkKSk7XG4gICAgfSBjYXRjaChlcnJvcikge1xuICAgICAgICBjYWxsYmFjayhlcnJvcnMuSW52YWxpZENvbm5lY3Rpb24odGhpcy5ob3N0KSk7XG4gICAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBIdHRwUHJvdmlkZXI7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIFxuICogQGZpbGUgaWNhcC5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xuXG4vKipcbiAqIFRoaXMgcHJvdG90eXBlIHNob3VsZCBiZSB1c2VkIHRvIGV4dHJhY3QgbmVjZXNzYXJ5IGluZm9ybWF0aW9uIGZyb20gaWJhbiBhZGRyZXNzXG4gKlxuICogQHBhcmFtIHtTdHJpbmd9IGliYW5cbiAqL1xudmFyIElDQVAgPSBmdW5jdGlvbiAoaWJhbikge1xuICAgIHRoaXMuX2liYW4gPSBpYmFuO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGNoZWNrIGlmIGljYXAgaXMgY29ycmVjdFxuICpcbiAqIEBtZXRob2QgaXNWYWxpZFxuICogQHJldHVybnMge0Jvb2xlYW59IHRydWUgaWYgaXQgaXMsIG90aGVyd2lzZSBmYWxzZVxuICovXG5JQ0FQLnByb3RvdHlwZS5pc1ZhbGlkID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB1dGlscy5pc0lCQU4odGhpcy5faWJhbik7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gY2hlY2sgaWYgaWJhbiBudW1iZXIgaXMgZGlyZWN0XG4gKlxuICogQG1ldGhvZCBpc0RpcmVjdFxuICogQHJldHVybnMge0Jvb2xlYW59IHRydWUgaWYgaXQgaXMsIG90aGVyd2lzZSBmYWxzZVxuICovXG5JQ0FQLnByb3RvdHlwZS5pc0RpcmVjdCA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gdGhpcy5faWJhbi5sZW5ndGggPT09IDM0O1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGNoZWNrIGlmIGliYW4gbnVtYmVyIGlmIGluZGlyZWN0XG4gKlxuICogQG1ldGhvZCBpc0luZGlyZWN0XG4gKiBAcmV0dXJucyB7Qm9vbGVhbn0gdHJ1ZSBpZiBpdCBpcywgb3RoZXJ3aXNlIGZhbHNlXG4gKi9cbklDQVAucHJvdG90eXBlLmlzSW5kaXJlY3QgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2liYW4ubGVuZ3RoID09PSAyMDtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBnZXQgaWJhbiBjaGVja3N1bVxuICogVXNlcyB0aGUgbW9kLTk3LTEwIGNoZWNrc3VtbWluZyBwcm90b2NvbCAoSVNPL0lFQyA3MDY0OjIwMDMpXG4gKlxuICogQG1ldGhvZCBjaGVja3N1bVxuICogQHJldHVybnMge1N0cmluZ30gY2hlY2tzdW1cbiAqL1xuSUNBUC5wcm90b3R5cGUuY2hlY2tzdW0gPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2liYW4uc3Vic3RyKDIsIDIpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGdldCBpbnN0aXR1dGlvbiBpZGVudGlmaWVyXG4gKiBlZy4gWFJFR1xuICpcbiAqIEBtZXRob2QgaW5zdGl0dXRpb25cbiAqIEByZXR1cm5zIHtTdHJpbmd9IGluc3RpdHV0aW9uIGlkZW50aWZpZXJcbiAqL1xuSUNBUC5wcm90b3R5cGUuaW5zdGl0dXRpb24gPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuaXNJbmRpcmVjdCgpID8gdGhpcy5faWJhbi5zdWJzdHIoNywgNCkgOiAnJztcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBnZXQgY2xpZW50IGlkZW50aWZpZXIgd2l0aGluIGluc3RpdHV0aW9uXG4gKiBlZy4gR0FWT0ZZT1JLXG4gKlxuICogQG1ldGhvZCBjbGllbnRcbiAqIEByZXR1cm5zIHtTdHJpbmd9IGNsaWVudCBpZGVudGlmaWVyXG4gKi9cbklDQVAucHJvdG90eXBlLmNsaWVudCA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gdGhpcy5pc0luZGlyZWN0KCkgPyB0aGlzLl9pYmFuLnN1YnN0cigxMSkgOiAnJztcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBnZXQgY2xpZW50IGRpcmVjdCBhZGRyZXNzXG4gKlxuICogQG1ldGhvZCBhZGRyZXNzXG4gKiBAcmV0dXJucyB7U3RyaW5nfSBjbGllbnQgZGlyZWN0IGFkZHJlc3NcbiAqL1xuSUNBUC5wcm90b3R5cGUuYWRkcmVzcyA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gdGhpcy5pc0RpcmVjdCgpID8gdGhpcy5faWJhbi5zdWJzdHIoNCkgOiAnJztcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gSUNBUDtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKiogQGZpbGUganNvbnJwYy5qc1xuICogQGF1dGhvcnM6XG4gKiAgIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciBKc29ucnBjID0gZnVuY3Rpb24gKCkge1xuICAgIC8vIHNpbmdsZXRvbiBwYXR0ZXJuXG4gICAgaWYgKGFyZ3VtZW50cy5jYWxsZWUuX3NpbmdsZXRvbkluc3RhbmNlKSB7XG4gICAgICAgIHJldHVybiBhcmd1bWVudHMuY2FsbGVlLl9zaW5nbGV0b25JbnN0YW5jZTtcbiAgICB9XG4gICAgYXJndW1lbnRzLmNhbGxlZS5fc2luZ2xldG9uSW5zdGFuY2UgPSB0aGlzO1xuXG4gICAgdGhpcy5tZXNzYWdlSWQgPSAxO1xufTtcblxuLyoqXG4gKiBAcmV0dXJuIHtKc29ucnBjfSBzaW5nbGV0b25cbiAqL1xuSnNvbnJwYy5nZXRJbnN0YW5jZSA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgaW5zdGFuY2UgPSBuZXcgSnNvbnJwYygpO1xuICAgIHJldHVybiBpbnN0YW5jZTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byB2YWxpZCBqc29uIGNyZWF0ZSBwYXlsb2FkIG9iamVjdFxuICpcbiAqIEBtZXRob2QgdG9QYXlsb2FkXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBtZXRob2Qgb2YganNvbnJwYyBjYWxsLCByZXF1aXJlZFxuICogQHBhcmFtIHtBcnJheX0gcGFyYW1zLCBhbiBhcnJheSBvZiBtZXRob2QgcGFyYW1zLCBvcHRpb25hbFxuICogQHJldHVybnMge09iamVjdH0gdmFsaWQganNvbnJwYyBwYXlsb2FkIG9iamVjdFxuICovXG5Kc29ucnBjLnByb3RvdHlwZS50b1BheWxvYWQgPSBmdW5jdGlvbiAobWV0aG9kLCBwYXJhbXMpIHtcbiAgICBpZiAoIW1ldGhvZClcbiAgICAgICAgY29uc29sZS5lcnJvcignanNvbnJwYyBtZXRob2Qgc2hvdWxkIGJlIHNwZWNpZmllZCEnKTtcblxuICAgIHJldHVybiB7XG4gICAgICAgIGpzb25ycGM6ICcyLjAnLFxuICAgICAgICBtZXRob2Q6IG1ldGhvZCxcbiAgICAgICAgcGFyYW1zOiBwYXJhbXMgfHwgW10sXG4gICAgICAgIGlkOiB0aGlzLm1lc3NhZ2VJZCsrXG4gICAgfTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBjaGVjayBpZiBqc29ucnBjIHJlc3BvbnNlIGlzIHZhbGlkXG4gKlxuICogQG1ldGhvZCBpc1ZhbGlkUmVzcG9uc2VcbiAqIEBwYXJhbSB7T2JqZWN0fVxuICogQHJldHVybnMge0Jvb2xlYW59IHRydWUgaWYgcmVzcG9uc2UgaXMgdmFsaWQsIG90aGVyd2lzZSBmYWxzZVxuICovXG5Kc29ucnBjLnByb3RvdHlwZS5pc1ZhbGlkUmVzcG9uc2UgPSBmdW5jdGlvbiAocmVzcG9uc2UpIHtcbiAgICByZXR1cm4gISFyZXNwb25zZSAmJlxuICAgICAgICAhcmVzcG9uc2UuZXJyb3IgJiZcbiAgICAgICAgcmVzcG9uc2UuanNvbnJwYyA9PT0gJzIuMCcgJiZcbiAgICAgICAgdHlwZW9mIHJlc3BvbnNlLmlkID09PSAnbnVtYmVyJyAmJlxuICAgICAgICByZXNwb25zZS5yZXN1bHQgIT09IHVuZGVmaW5lZDsgLy8gb25seSB1bmRlZmluZWQgaXMgbm90IHZhbGlkIGpzb24gb2JqZWN0XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gY3JlYXRlIGJhdGNoIHBheWxvYWQgb2JqZWN0XG4gKlxuICogQG1ldGhvZCB0b0JhdGNoUGF5bG9hZFxuICogQHBhcmFtIHtBcnJheX0gbWVzc2FnZXMsIGFuIGFycmF5IG9mIG9iamVjdHMgd2l0aCBtZXRob2QgKHJlcXVpcmVkKSBhbmQgcGFyYW1zIChvcHRpb25hbCkgZmllbGRzXG4gKiBAcmV0dXJucyB7QXJyYXl9IGJhdGNoIHBheWxvYWRcbiAqL1xuSnNvbnJwYy5wcm90b3R5cGUudG9CYXRjaFBheWxvYWQgPSBmdW5jdGlvbiAobWVzc2FnZXMpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgcmV0dXJuIG1lc3NhZ2VzLm1hcChmdW5jdGlvbiAobWVzc2FnZSkge1xuICAgICAgICByZXR1cm4gc2VsZi50b1BheWxvYWQobWVzc2FnZS5tZXRob2QsIG1lc3NhZ2UucGFyYW1zKTtcbiAgICB9KTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gSnNvbnJwYztcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKipcbiAqIEBmaWxlIG1ldGhvZC5qc1xuICogQGF1dGhvciBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgUmVxdWVzdE1hbmFnZXIgPSByZXF1aXJlKCcuL3JlcXVlc3RtYW5hZ2VyJyk7XG52YXIgdXRpbHMgPSByZXF1aXJlKCcuLi91dGlscy91dGlscycpO1xudmFyIGVycm9ycyA9IHJlcXVpcmUoJy4vZXJyb3JzJyk7XG5cbnZhciBNZXRob2QgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgIHRoaXMubmFtZSA9IG9wdGlvbnMubmFtZTtcbiAgICB0aGlzLmNhbGwgPSBvcHRpb25zLmNhbGw7XG4gICAgdGhpcy5wYXJhbXMgPSBvcHRpb25zLnBhcmFtcyB8fCAwO1xuICAgIHRoaXMuaW5wdXRGb3JtYXR0ZXIgPSBvcHRpb25zLmlucHV0Rm9ybWF0dGVyO1xuICAgIHRoaXMub3V0cHV0Rm9ybWF0dGVyID0gb3B0aW9ucy5vdXRwdXRGb3JtYXR0ZXI7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGRldGVybWluZSBuYW1lIG9mIHRoZSBqc29ucnBjIG1ldGhvZCBiYXNlZCBvbiBhcmd1bWVudHNcbiAqXG4gKiBAbWV0aG9kIGdldENhbGxcbiAqIEBwYXJhbSB7QXJyYXl9IGFyZ3VtZW50c1xuICogQHJldHVybiB7U3RyaW5nfSBuYW1lIG9mIGpzb25ycGMgbWV0aG9kXG4gKi9cbk1ldGhvZC5wcm90b3R5cGUuZ2V0Q2FsbCA9IGZ1bmN0aW9uIChhcmdzKSB7XG4gICAgcmV0dXJuIHV0aWxzLmlzRnVuY3Rpb24odGhpcy5jYWxsKSA/IHRoaXMuY2FsbChhcmdzKSA6IHRoaXMuY2FsbDtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gZXh0cmFjdCBjYWxsYmFjayBmcm9tIGFycmF5IG9mIGFyZ3VtZW50cy4gTW9kaWZpZXMgaW5wdXQgcGFyYW1cbiAqXG4gKiBAbWV0aG9kIGV4dHJhY3RDYWxsYmFja1xuICogQHBhcmFtIHtBcnJheX0gYXJndW1lbnRzXG4gKiBAcmV0dXJuIHtGdW5jdGlvbnxOdWxsfSBjYWxsYmFjaywgaWYgZXhpc3RzXG4gKi9cbk1ldGhvZC5wcm90b3R5cGUuZXh0cmFjdENhbGxiYWNrID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICBpZiAodXRpbHMuaXNGdW5jdGlvbihhcmdzW2FyZ3MubGVuZ3RoIC0gMV0pKSB7XG4gICAgICAgIHJldHVybiBhcmdzLnBvcCgpOyAvLyBtb2RpZnkgdGhlIGFyZ3MgYXJyYXkhXG4gICAgfVxufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGNoZWNrIGlmIHRoZSBudW1iZXIgb2YgYXJndW1lbnRzIGlzIGNvcnJlY3RcbiAqIFxuICogQG1ldGhvZCB2YWxpZGF0ZUFyZ3NcbiAqIEBwYXJhbSB7QXJyYXl9IGFyZ3VtZW50c1xuICogQHRocm93cyB7RXJyb3J9IGlmIGl0IGlzIG5vdFxuICovXG5NZXRob2QucHJvdG90eXBlLnZhbGlkYXRlQXJncyA9IGZ1bmN0aW9uIChhcmdzKSB7XG4gICAgaWYgKGFyZ3MubGVuZ3RoICE9PSB0aGlzLnBhcmFtcykge1xuICAgICAgICB0aHJvdyBlcnJvcnMuSW52YWxpZE51bWJlck9mUGFyYW1zKCk7XG4gICAgfVxufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgY2FsbGVkIHRvIGZvcm1hdCBpbnB1dCBhcmdzIG9mIG1ldGhvZFxuICogXG4gKiBAbWV0aG9kIGZvcm1hdElucHV0XG4gKiBAcGFyYW0ge0FycmF5fVxuICogQHJldHVybiB7QXJyYXl9XG4gKi9cbk1ldGhvZC5wcm90b3R5cGUuZm9ybWF0SW5wdXQgPSBmdW5jdGlvbiAoYXJncykge1xuICAgIGlmICghdGhpcy5pbnB1dEZvcm1hdHRlcikge1xuICAgICAgICByZXR1cm4gYXJncztcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5pbnB1dEZvcm1hdHRlci5tYXAoZnVuY3Rpb24gKGZvcm1hdHRlciwgaW5kZXgpIHtcbiAgICAgICAgcmV0dXJuIGZvcm1hdHRlciA/IGZvcm1hdHRlcihhcmdzW2luZGV4XSkgOiBhcmdzW2luZGV4XTtcbiAgICB9KTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBmb3JtYXQgb3V0cHV0KHJlc3VsdCkgb2YgbWV0aG9kXG4gKlxuICogQG1ldGhvZCBmb3JtYXRPdXRwdXRcbiAqIEBwYXJhbSB7T2JqZWN0fVxuICogQHJldHVybiB7T2JqZWN0fVxuICovXG5NZXRob2QucHJvdG90eXBlLmZvcm1hdE91dHB1dCA9IGZ1bmN0aW9uIChyZXN1bHQpIHtcbiAgICByZXR1cm4gdGhpcy5vdXRwdXRGb3JtYXR0ZXIgJiYgcmVzdWx0ICE9PSBudWxsID8gdGhpcy5vdXRwdXRGb3JtYXR0ZXIocmVzdWx0KSA6IHJlc3VsdDtcbn07XG5cbi8qKlxuICogU2hvdWxkIGF0dGFjaCBmdW5jdGlvbiB0byBtZXRob2RcbiAqIFxuICogQG1ldGhvZCBhdHRhY2hUb09iamVjdFxuICogQHBhcmFtIHtPYmplY3R9XG4gKiBAcGFyYW0ge0Z1bmN0aW9ufVxuICovXG5NZXRob2QucHJvdG90eXBlLmF0dGFjaFRvT2JqZWN0ID0gZnVuY3Rpb24gKG9iaikge1xuICAgIHZhciBmdW5jID0gdGhpcy5zZW5kLmJpbmQodGhpcyk7XG4gICAgZnVuYy5yZXF1ZXN0ID0gdGhpcy5yZXF1ZXN0LmJpbmQodGhpcyk7XG4gICAgZnVuYy5jYWxsID0gdGhpcy5jYWxsOyAvLyB0aGF0J3MgdWdseS4gZmlsdGVyLmpzIHVzZXMgaXRcbiAgICB2YXIgbmFtZSA9IHRoaXMubmFtZS5zcGxpdCgnLicpO1xuICAgIGlmIChuYW1lLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgb2JqW25hbWVbMF1dID0gb2JqW25hbWVbMF1dIHx8IHt9O1xuICAgICAgICBvYmpbbmFtZVswXV1bbmFtZVsxXV0gPSBmdW5jO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIG9ialtuYW1lWzBdXSA9IGZ1bmM7IFxuICAgIH1cbn07XG5cbi8qKlxuICogU2hvdWxkIGNyZWF0ZSBwYXlsb2FkIGZyb20gZ2l2ZW4gaW5wdXQgYXJnc1xuICpcbiAqIEBtZXRob2QgdG9QYXlsb2FkXG4gKiBAcGFyYW0ge0FycmF5fSBhcmdzXG4gKiBAcmV0dXJuIHtPYmplY3R9XG4gKi9cbk1ldGhvZC5wcm90b3R5cGUudG9QYXlsb2FkID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgICB2YXIgY2FsbCA9IHRoaXMuZ2V0Q2FsbChhcmdzKTtcbiAgICB2YXIgY2FsbGJhY2sgPSB0aGlzLmV4dHJhY3RDYWxsYmFjayhhcmdzKTtcbiAgICB2YXIgcGFyYW1zID0gdGhpcy5mb3JtYXRJbnB1dChhcmdzKTtcbiAgICB0aGlzLnZhbGlkYXRlQXJncyhwYXJhbXMpO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgICAgbWV0aG9kOiBjYWxsLFxuICAgICAgICBwYXJhbXM6IHBhcmFtcyxcbiAgICAgICAgY2FsbGJhY2s6IGNhbGxiYWNrXG4gICAgfTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBjcmVhdGUgcHVyZSBKU09OUlBDIHJlcXVlc3Qgd2hpY2ggY2FuIGJlIHVzZWQgaW4gYmF0Y2ggcmVxdWVzdFxuICpcbiAqIEBtZXRob2QgcmVxdWVzdFxuICogQHBhcmFtIHsuLi59IHBhcmFtc1xuICogQHJldHVybiB7T2JqZWN0fSBqc29ucnBjIHJlcXVlc3RcbiAqL1xuTWV0aG9kLnByb3RvdHlwZS5yZXF1ZXN0ID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciBwYXlsb2FkID0gdGhpcy50b1BheWxvYWQoQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKSk7XG4gICAgcGF5bG9hZC5mb3JtYXQgPSB0aGlzLmZvcm1hdE91dHB1dC5iaW5kKHRoaXMpO1xuICAgIHJldHVybiBwYXlsb2FkO1xufTtcblxuLyoqXG4gKiBTaG91bGQgc2VuZCByZXF1ZXN0IHRvIHRoZSBBUElcbiAqXG4gKiBAbWV0aG9kIHNlbmRcbiAqIEBwYXJhbSBsaXN0IG9mIHBhcmFtc1xuICogQHJldHVybiByZXN1bHRcbiAqL1xuTWV0aG9kLnByb3RvdHlwZS5zZW5kID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciBwYXlsb2FkID0gdGhpcy50b1BheWxvYWQoQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKSk7XG4gICAgaWYgKHBheWxvYWQuY2FsbGJhY2spIHtcbiAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICByZXR1cm4gUmVxdWVzdE1hbmFnZXIuZ2V0SW5zdGFuY2UoKS5zZW5kQXN5bmMocGF5bG9hZCwgZnVuY3Rpb24gKGVyciwgcmVzdWx0KSB7XG4gICAgICAgICAgICBwYXlsb2FkLmNhbGxiYWNrKGVyciwgc2VsZi5mb3JtYXRPdXRwdXQocmVzdWx0KSk7XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5mb3JtYXRPdXRwdXQoUmVxdWVzdE1hbmFnZXIuZ2V0SW5zdGFuY2UoKS5zZW5kKHBheWxvYWQpKTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gTWV0aG9kO1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBcbiAqIEBmaWxlIG5hbWVyZWcuanNcbiAqIEBhdXRob3IgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE1XG4gKi9cblxudmFyIGNvbnRyYWN0ID0gcmVxdWlyZSgnLi9jb250cmFjdCcpO1xuXG52YXIgYWRkcmVzcyA9ICcweGM2ZDlkMmNkNDQ5YTc1NGM0OTQyNjRlMTgwOWM1MGUzNGQ2NDU2MmInO1xuXG52YXIgYWJpID0gW1xuICAgIHtcImNvbnN0YW50XCI6dHJ1ZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJfb3duZXJcIixcInR5cGVcIjpcImFkZHJlc3NcIn1dLFwibmFtZVwiOlwibmFtZVwiLFwib3V0cHV0c1wiOlt7XCJuYW1lXCI6XCJvX25hbWVcIixcInR5cGVcIjpcImJ5dGVzMzJcIn1dLFwidHlwZVwiOlwiZnVuY3Rpb25cIn0sXG4gICAge1wiY29uc3RhbnRcIjp0cnVlLFwiaW5wdXRzXCI6W3tcIm5hbWVcIjpcIl9uYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcIm5hbWVcIjpcIm93bmVyXCIsXCJvdXRwdXRzXCI6W3tcIm5hbWVcIjpcIlwiLFwidHlwZVwiOlwiYWRkcmVzc1wifV0sXCJ0eXBlXCI6XCJmdW5jdGlvblwifSxcbiAgICB7XCJjb25zdGFudFwiOnRydWUsXCJpbnB1dHNcIjpbe1wibmFtZVwiOlwiX25hbWVcIixcInR5cGVcIjpcImJ5dGVzMzJcIn1dLFwibmFtZVwiOlwiY29udGVudFwiLFwib3V0cHV0c1wiOlt7XCJuYW1lXCI6XCJcIixcInR5cGVcIjpcImJ5dGVzMzJcIn1dLFwidHlwZVwiOlwiZnVuY3Rpb25cIn0sXG4gICAge1wiY29uc3RhbnRcIjp0cnVlLFwiaW5wdXRzXCI6W3tcIm5hbWVcIjpcIl9uYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcIm5hbWVcIjpcImFkZHJcIixcIm91dHB1dHNcIjpbe1wibmFtZVwiOlwiXCIsXCJ0eXBlXCI6XCJhZGRyZXNzXCJ9XSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImNvbnN0YW50XCI6ZmFsc2UsXCJpbnB1dHNcIjpbe1wibmFtZVwiOlwiX25hbWVcIixcInR5cGVcIjpcImJ5dGVzMzJcIn1dLFwibmFtZVwiOlwicmVzZXJ2ZVwiLFwib3V0cHV0c1wiOltdLFwidHlwZVwiOlwiZnVuY3Rpb25cIn0sXG4gICAge1wiY29uc3RhbnRcIjp0cnVlLFwiaW5wdXRzXCI6W3tcIm5hbWVcIjpcIl9uYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcIm5hbWVcIjpcInN1YlJlZ2lzdHJhclwiLFwib3V0cHV0c1wiOlt7XCJuYW1lXCI6XCJvX3N1YlJlZ2lzdHJhclwiLFwidHlwZVwiOlwiYWRkcmVzc1wifV0sXCJ0eXBlXCI6XCJmdW5jdGlvblwifSxcbiAgICB7XCJjb25zdGFudFwiOmZhbHNlLFwiaW5wdXRzXCI6W3tcIm5hbWVcIjpcIl9uYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9LHtcIm5hbWVcIjpcIl9uZXdPd25lclwiLFwidHlwZVwiOlwiYWRkcmVzc1wifV0sXCJuYW1lXCI6XCJ0cmFuc2ZlclwiLFwib3V0cHV0c1wiOltdLFwidHlwZVwiOlwiZnVuY3Rpb25cIn0sXG4gICAge1wiY29uc3RhbnRcIjpmYWxzZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJfbmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifSx7XCJuYW1lXCI6XCJfcmVnaXN0cmFyXCIsXCJ0eXBlXCI6XCJhZGRyZXNzXCJ9XSxcIm5hbWVcIjpcInNldFN1YlJlZ2lzdHJhclwiLFwib3V0cHV0c1wiOltdLFwidHlwZVwiOlwiZnVuY3Rpb25cIn0sXG4gICAge1wiY29uc3RhbnRcIjpmYWxzZSxcImlucHV0c1wiOltdLFwibmFtZVwiOlwiUmVnaXN0cmFyXCIsXCJvdXRwdXRzXCI6W10sXCJ0eXBlXCI6XCJmdW5jdGlvblwifSxcbiAgICB7XCJjb25zdGFudFwiOmZhbHNlLFwiaW5wdXRzXCI6W3tcIm5hbWVcIjpcIl9uYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9LHtcIm5hbWVcIjpcIl9hXCIsXCJ0eXBlXCI6XCJhZGRyZXNzXCJ9LHtcIm5hbWVcIjpcIl9wcmltYXJ5XCIsXCJ0eXBlXCI6XCJib29sXCJ9XSxcIm5hbWVcIjpcInNldEFkZHJlc3NcIixcIm91dHB1dHNcIjpbXSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImNvbnN0YW50XCI6ZmFsc2UsXCJpbnB1dHNcIjpbe1wibmFtZVwiOlwiX25hbWVcIixcInR5cGVcIjpcImJ5dGVzMzJcIn0se1wibmFtZVwiOlwiX2NvbnRlbnRcIixcInR5cGVcIjpcImJ5dGVzMzJcIn1dLFwibmFtZVwiOlwic2V0Q29udGVudFwiLFwib3V0cHV0c1wiOltdLFwidHlwZVwiOlwiZnVuY3Rpb25cIn0sXG4gICAge1wiY29uc3RhbnRcIjpmYWxzZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJfbmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifV0sXCJuYW1lXCI6XCJkaXNvd25cIixcIm91dHB1dHNcIjpbXSxcInR5cGVcIjpcImZ1bmN0aW9uXCJ9LFxuICAgIHtcImNvbnN0YW50XCI6dHJ1ZSxcImlucHV0c1wiOlt7XCJuYW1lXCI6XCJfbmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifV0sXCJuYW1lXCI6XCJyZWdpc3RlclwiLFwib3V0cHV0c1wiOlt7XCJuYW1lXCI6XCJcIixcInR5cGVcIjpcImFkZHJlc3NcIn1dLFwidHlwZVwiOlwiZnVuY3Rpb25cIn0sXG4gICAge1wiYW5vbnltb3VzXCI6ZmFsc2UsXCJpbnB1dHNcIjpbe1wiaW5kZXhlZFwiOnRydWUsXCJuYW1lXCI6XCJuYW1lXCIsXCJ0eXBlXCI6XCJieXRlczMyXCJ9XSxcIm5hbWVcIjpcIkNoYW5nZWRcIixcInR5cGVcIjpcImV2ZW50XCJ9LFxuICAgIHtcImFub255bW91c1wiOmZhbHNlLFwiaW5wdXRzXCI6W3tcImluZGV4ZWRcIjp0cnVlLFwibmFtZVwiOlwibmFtZVwiLFwidHlwZVwiOlwiYnl0ZXMzMlwifSx7XCJpbmRleGVkXCI6dHJ1ZSxcIm5hbWVcIjpcImFkZHJcIixcInR5cGVcIjpcImFkZHJlc3NcIn1dLFwibmFtZVwiOlwiUHJpbWFyeUNoYW5nZWRcIixcInR5cGVcIjpcImV2ZW50XCJ9XG5dO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGNvbnRyYWN0KGFiaSkuYXQoYWRkcmVzcyk7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIEBmaWxlIGV0aC5qc1xuICogQGF1dGhvcnM6XG4gKiAgIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciB1dGlscyA9IHJlcXVpcmUoJy4uL3V0aWxzL3V0aWxzJyk7XG52YXIgUHJvcGVydHkgPSByZXF1aXJlKCcuL3Byb3BlcnR5Jyk7XG5cbi8vLyBAcmV0dXJucyBhbiBhcnJheSBvZiBvYmplY3RzIGRlc2NyaWJpbmcgd2ViMy5ldGggYXBpIG1ldGhvZHNcbnZhciBtZXRob2RzID0gW1xuXTtcblxuLy8vIEByZXR1cm5zIGFuIGFycmF5IG9mIG9iamVjdHMgZGVzY3JpYmluZyB3ZWIzLmV0aCBhcGkgcHJvcGVydGllc1xudmFyIHByb3BlcnRpZXMgPSBbXG4gICAgbmV3IFByb3BlcnR5KHtcbiAgICAgICAgbmFtZTogJ2xpc3RlbmluZycsXG4gICAgICAgIGdldHRlcjogJ25ldF9saXN0ZW5pbmcnXG4gICAgfSksXG4gICAgbmV3IFByb3BlcnR5KHtcbiAgICAgICAgbmFtZTogJ3BlZXJDb3VudCcsXG4gICAgICAgIGdldHRlcjogJ25ldF9wZWVyQ291bnQnLFxuICAgICAgICBvdXRwdXRGb3JtYXR0ZXI6IHV0aWxzLnRvRGVjaW1hbFxuICAgIH0pXG5dO1xuXG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIG1ldGhvZHM6IG1ldGhvZHMsXG4gICAgcHJvcGVydGllczogcHJvcGVydGllc1xufTtcblxuIiwiLypcbiAgICBUaGlzIGZpbGUgaXMgcGFydCBvZiBldGhlcmV1bS5qcy5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnlcbiAgICBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvclxuICAgIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uXG5cbiAgICBldGhlcmV1bS5qcyBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLFxuICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG4gICAgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZVxuICAgIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG4gICAgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlXG4gICAgYWxvbmcgd2l0aCBldGhlcmV1bS5qcy4gIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi5cbiovXG4vKipcbiAqIEBmaWxlIHByb3BlcnR5LmpzXG4gKiBAYXV0aG9yIEZhYmlhbiBWb2dlbHN0ZWxsZXIgPGZhYmlhbkBmcm96ZW1hbi5kZT5cbiAqIEBhdXRob3IgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE1XG4gKi9cblxudmFyIFJlcXVlc3RNYW5hZ2VyID0gcmVxdWlyZSgnLi9yZXF1ZXN0bWFuYWdlcicpO1xuXG52YXIgUHJvcGVydHkgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgIHRoaXMubmFtZSA9IG9wdGlvbnMubmFtZTtcbiAgICB0aGlzLmdldHRlciA9IG9wdGlvbnMuZ2V0dGVyO1xuICAgIHRoaXMuc2V0dGVyID0gb3B0aW9ucy5zZXR0ZXI7XG4gICAgdGhpcy5vdXRwdXRGb3JtYXR0ZXIgPSBvcHRpb25zLm91dHB1dEZvcm1hdHRlcjtcbiAgICB0aGlzLmlucHV0Rm9ybWF0dGVyID0gb3B0aW9ucy5pbnB1dEZvcm1hdHRlcjtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBmb3JtYXQgaW5wdXQgYXJncyBvZiBtZXRob2RcbiAqIFxuICogQG1ldGhvZCBmb3JtYXRJbnB1dFxuICogQHBhcmFtIHtBcnJheX1cbiAqIEByZXR1cm4ge0FycmF5fVxuICovXG5Qcm9wZXJ0eS5wcm90b3R5cGUuZm9ybWF0SW5wdXQgPSBmdW5jdGlvbiAoYXJnKSB7XG4gICAgcmV0dXJuIHRoaXMuaW5wdXRGb3JtYXR0ZXIgPyB0aGlzLmlucHV0Rm9ybWF0dGVyKGFyZykgOiBhcmc7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gZm9ybWF0IG91dHB1dChyZXN1bHQpIG9mIG1ldGhvZFxuICpcbiAqIEBtZXRob2QgZm9ybWF0T3V0cHV0XG4gKiBAcGFyYW0ge09iamVjdH1cbiAqIEByZXR1cm4ge09iamVjdH1cbiAqL1xuUHJvcGVydHkucHJvdG90eXBlLmZvcm1hdE91dHB1dCA9IGZ1bmN0aW9uIChyZXN1bHQpIHtcbiAgICByZXR1cm4gdGhpcy5vdXRwdXRGb3JtYXR0ZXIgJiYgcmVzdWx0ICE9PSBudWxsID8gdGhpcy5vdXRwdXRGb3JtYXR0ZXIocmVzdWx0KSA6IHJlc3VsdDtcbn07XG5cbi8qKlxuICogU2hvdWxkIGF0dGFjaCBmdW5jdGlvbiB0byBtZXRob2RcbiAqIFxuICogQG1ldGhvZCBhdHRhY2hUb09iamVjdFxuICogQHBhcmFtIHtPYmplY3R9XG4gKiBAcGFyYW0ge0Z1bmN0aW9ufVxuICovXG5Qcm9wZXJ0eS5wcm90b3R5cGUuYXR0YWNoVG9PYmplY3QgPSBmdW5jdGlvbiAob2JqKSB7XG4gICAgdmFyIHByb3RvID0ge1xuICAgICAgICBnZXQ6IHRoaXMuZ2V0LmJpbmQodGhpcyksXG4gICAgfTtcblxuICAgIHZhciBuYW1lcyA9IHRoaXMubmFtZS5zcGxpdCgnLicpO1xuICAgIHZhciBuYW1lID0gbmFtZXNbMF07XG4gICAgaWYgKG5hbWVzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgb2JqW25hbWVzWzBdXSA9IG9ialtuYW1lc1swXV0gfHwge307XG4gICAgICAgIG9iaiA9IG9ialtuYW1lc1swXV07XG4gICAgICAgIG5hbWUgPSBuYW1lc1sxXTtcbiAgICB9XG4gICAgXG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KG9iaiwgbmFtZSwgcHJvdG8pO1xuXG4gICAgdmFyIHRvQXN5bmNOYW1lID0gZnVuY3Rpb24gKHByZWZpeCwgbmFtZSkge1xuICAgICAgICByZXR1cm4gcHJlZml4ICsgbmFtZS5jaGFyQXQoMCkudG9VcHBlckNhc2UoKSArIG5hbWUuc2xpY2UoMSk7XG4gICAgfTtcblxuICAgIG9ialt0b0FzeW5jTmFtZSgnZ2V0JywgbmFtZSldID0gdGhpcy5nZXRBc3luYy5iaW5kKHRoaXMpO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBnZXQgdmFsdWUgb2YgdGhlIHByb3BlcnR5XG4gKlxuICogQG1ldGhvZCBnZXRcbiAqIEByZXR1cm4ge09iamVjdH0gdmFsdWUgb2YgdGhlIHByb3BlcnR5XG4gKi9cblByb3BlcnR5LnByb3RvdHlwZS5nZXQgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHRoaXMuZm9ybWF0T3V0cHV0KFJlcXVlc3RNYW5hZ2VyLmdldEluc3RhbmNlKCkuc2VuZCh7XG4gICAgICAgIG1ldGhvZDogdGhpcy5nZXR0ZXJcbiAgICB9KSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIGFzeW5jaHJvdW5vdXNseSBnZXQgdmFsdWUgb2YgcHJvcGVydHlcbiAqXG4gKiBAbWV0aG9kIGdldEFzeW5jXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufVxuICovXG5Qcm9wZXJ0eS5wcm90b3R5cGUuZ2V0QXN5bmMgPSBmdW5jdGlvbiAoY2FsbGJhY2spIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgUmVxdWVzdE1hbmFnZXIuZ2V0SW5zdGFuY2UoKS5zZW5kQXN5bmMoe1xuICAgICAgICBtZXRob2Q6IHRoaXMuZ2V0dGVyXG4gICAgfSwgZnVuY3Rpb24gKGVyciwgcmVzdWx0KSB7XG4gICAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgICAgIHJldHVybiBjYWxsYmFjayhlcnIpO1xuICAgICAgICB9XG4gICAgICAgIGNhbGxiYWNrKGVyciwgc2VsZi5mb3JtYXRPdXRwdXQocmVzdWx0KSk7XG4gICAgfSk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFByb3BlcnR5O1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBAZmlsZSBxdHN5bmMuanNcbiAqIEBhdXRob3JzOlxuICogICBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqICAgTWFyaWFuIE9hbmNlYSA8bWFyaWFuQGV0aGRldi5jb20+XG4gKiBAZGF0ZSAyMDE0XG4gKi9cblxudmFyIFF0U3luY1Byb3ZpZGVyID0gZnVuY3Rpb24gKCkge1xufTtcblxuUXRTeW5jUHJvdmlkZXIucHJvdG90eXBlLnNlbmQgPSBmdW5jdGlvbiAocGF5bG9hZCkge1xuICAgIHZhciByZXN1bHQgPSBuYXZpZ2F0b3IucXQuY2FsbE1ldGhvZChKU09OLnN0cmluZ2lmeShwYXlsb2FkKSk7XG4gICAgcmV0dXJuIEpTT04ucGFyc2UocmVzdWx0KTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gUXRTeW5jUHJvdmlkZXI7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIFxuICogQGZpbGUgcmVxdWVzdG1hbmFnZXIuanNcbiAqIEBhdXRob3IgSmVmZnJleSBXaWxja2UgPGplZmZAZXRoZGV2LmNvbT5cbiAqIEBhdXRob3IgTWFyZWsgS290ZXdpY3ogPG1hcmVrQGV0aGRldi5jb20+XG4gKiBAYXV0aG9yIE1hcmlhbiBPYW5jZWEgPG1hcmlhbkBldGhkZXYuY29tPlxuICogQGF1dGhvciBGYWJpYW4gVm9nZWxzdGVsbGVyIDxmYWJpYW5AZXRoZGV2LmNvbT5cbiAqIEBhdXRob3IgR2F2IFdvb2QgPGdAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTRcbiAqL1xuXG52YXIgSnNvbnJwYyA9IHJlcXVpcmUoJy4vanNvbnJwYycpO1xudmFyIHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMvdXRpbHMnKTtcbnZhciBjID0gcmVxdWlyZSgnLi4vdXRpbHMvY29uZmlnJyk7XG52YXIgZXJyb3JzID0gcmVxdWlyZSgnLi9lcnJvcnMnKTtcblxuLyoqXG4gKiBJdCdzIHJlc3BvbnNpYmxlIGZvciBwYXNzaW5nIG1lc3NhZ2VzIHRvIHByb3ZpZGVyc1xuICogSXQncyBhbHNvIHJlc3BvbnNpYmxlIGZvciBwb2xsaW5nIHRoZSBldGhlcmV1bSBub2RlIGZvciBpbmNvbWluZyBtZXNzYWdlc1xuICogRGVmYXVsdCBwb2xsIHRpbWVvdXQgaXMgMSBzZWNvbmRcbiAqIFNpbmdsZXRvblxuICovXG52YXIgUmVxdWVzdE1hbmFnZXIgPSBmdW5jdGlvbiAocHJvdmlkZXIpIHtcbiAgICAvLyBzaW5nbGV0b24gcGF0dGVyblxuICAgIGlmIChhcmd1bWVudHMuY2FsbGVlLl9zaW5nbGV0b25JbnN0YW5jZSkge1xuICAgICAgICByZXR1cm4gYXJndW1lbnRzLmNhbGxlZS5fc2luZ2xldG9uSW5zdGFuY2U7XG4gICAgfVxuICAgIGFyZ3VtZW50cy5jYWxsZWUuX3NpbmdsZXRvbkluc3RhbmNlID0gdGhpcztcblxuICAgIHRoaXMucHJvdmlkZXIgPSBwcm92aWRlcjtcbiAgICB0aGlzLnBvbGxzID0gW107XG4gICAgdGhpcy50aW1lb3V0ID0gbnVsbDtcbiAgICB0aGlzLnBvbGwoKTtcbn07XG5cbi8qKlxuICogQHJldHVybiB7UmVxdWVzdE1hbmFnZXJ9IHNpbmdsZXRvblxuICovXG5SZXF1ZXN0TWFuYWdlci5nZXRJbnN0YW5jZSA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgaW5zdGFuY2UgPSBuZXcgUmVxdWVzdE1hbmFnZXIoKTtcbiAgICByZXR1cm4gaW5zdGFuY2U7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIHN5bmNocm9ub3VzbHkgc2VuZCByZXF1ZXN0XG4gKlxuICogQG1ldGhvZCBzZW5kXG4gKiBAcGFyYW0ge09iamVjdH0gZGF0YVxuICogQHJldHVybiB7T2JqZWN0fVxuICovXG5SZXF1ZXN0TWFuYWdlci5wcm90b3R5cGUuc2VuZCA9IGZ1bmN0aW9uIChkYXRhKSB7XG4gICAgaWYgKCF0aGlzLnByb3ZpZGVyKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoZXJyb3JzLkludmFsaWRQcm92aWRlcigpKTtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgdmFyIHBheWxvYWQgPSBKc29ucnBjLmdldEluc3RhbmNlKCkudG9QYXlsb2FkKGRhdGEubWV0aG9kLCBkYXRhLnBhcmFtcyk7XG4gICAgdmFyIHJlc3VsdCA9IHRoaXMucHJvdmlkZXIuc2VuZChwYXlsb2FkKTtcblxuICAgIGlmICghSnNvbnJwYy5nZXRJbnN0YW5jZSgpLmlzVmFsaWRSZXNwb25zZShyZXN1bHQpKSB7XG4gICAgICAgIHRocm93IGVycm9ycy5JbnZhbGlkUmVzcG9uc2UocmVzdWx0KTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0LnJlc3VsdDtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gYXN5bmNocm9ub3VzbHkgc2VuZCByZXF1ZXN0XG4gKlxuICogQG1ldGhvZCBzZW5kQXN5bmNcbiAqIEBwYXJhbSB7T2JqZWN0fSBkYXRhXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFja1xuICovXG5SZXF1ZXN0TWFuYWdlci5wcm90b3R5cGUuc2VuZEFzeW5jID0gZnVuY3Rpb24gKGRhdGEsIGNhbGxiYWNrKSB7XG4gICAgaWYgKCF0aGlzLnByb3ZpZGVyKSB7XG4gICAgICAgIHJldHVybiBjYWxsYmFjayhlcnJvcnMuSW52YWxpZFByb3ZpZGVyKCkpO1xuICAgIH1cblxuICAgIHZhciBwYXlsb2FkID0gSnNvbnJwYy5nZXRJbnN0YW5jZSgpLnRvUGF5bG9hZChkYXRhLm1ldGhvZCwgZGF0YS5wYXJhbXMpO1xuICAgIHRoaXMucHJvdmlkZXIuc2VuZEFzeW5jKHBheWxvYWQsIGZ1bmN0aW9uIChlcnIsIHJlc3VsdCkge1xuICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgICByZXR1cm4gY2FsbGJhY2soZXJyKTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgaWYgKCFKc29ucnBjLmdldEluc3RhbmNlKCkuaXNWYWxpZFJlc3BvbnNlKHJlc3VsdCkpIHtcbiAgICAgICAgICAgIHJldHVybiBjYWxsYmFjayhlcnJvcnMuSW52YWxpZFJlc3BvbnNlKHJlc3VsdCkpO1xuICAgICAgICB9XG5cbiAgICAgICAgY2FsbGJhY2sobnVsbCwgcmVzdWx0LnJlc3VsdCk7XG4gICAgfSk7XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gYXN5bmNocm9ub3VzbHkgc2VuZCBiYXRjaCByZXF1ZXN0XG4gKlxuICogQG1ldGhvZCBzZW5kQmF0Y2hcbiAqIEBwYXJhbSB7QXJyYXl9IGJhdGNoIGRhdGFcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrXG4gKi9cblJlcXVlc3RNYW5hZ2VyLnByb3RvdHlwZS5zZW5kQmF0Y2ggPSBmdW5jdGlvbiAoZGF0YSwgY2FsbGJhY2spIHtcbiAgICBpZiAoIXRoaXMucHJvdmlkZXIpIHtcbiAgICAgICAgcmV0dXJuIGNhbGxiYWNrKGVycm9ycy5JbnZhbGlkUHJvdmlkZXIoKSk7XG4gICAgfVxuXG4gICAgdmFyIHBheWxvYWQgPSBKc29ucnBjLmdldEluc3RhbmNlKCkudG9CYXRjaFBheWxvYWQoZGF0YSk7XG5cbiAgICB0aGlzLnByb3ZpZGVyLnNlbmRBc3luYyhwYXlsb2FkLCBmdW5jdGlvbiAoZXJyLCByZXN1bHRzKSB7XG4gICAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgICAgIHJldHVybiBjYWxsYmFjayhlcnIpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCF1dGlscy5pc0FycmF5KHJlc3VsdHMpKSB7XG4gICAgICAgICAgICByZXR1cm4gY2FsbGJhY2soZXJyb3JzLkludmFsaWRSZXNwb25zZShyZXN1bHRzKSk7XG4gICAgICAgIH1cblxuICAgICAgICBjYWxsYmFjayhlcnIsIHJlc3VsdHMpO1xuICAgIH0pOyBcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gc2V0IHByb3ZpZGVyIG9mIHJlcXVlc3QgbWFuYWdlclxuICpcbiAqIEBtZXRob2Qgc2V0UHJvdmlkZXJcbiAqIEBwYXJhbSB7T2JqZWN0fVxuICovXG5SZXF1ZXN0TWFuYWdlci5wcm90b3R5cGUuc2V0UHJvdmlkZXIgPSBmdW5jdGlvbiAocCkge1xuICAgIHRoaXMucHJvdmlkZXIgPSBwO1xufTtcblxuLypqc2hpbnQgbWF4cGFyYW1zOjQgKi9cblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBzdGFydCBwb2xsaW5nXG4gKlxuICogQG1ldGhvZCBzdGFydFBvbGxpbmdcbiAqIEBwYXJhbSB7T2JqZWN0fSBkYXRhXG4gKiBAcGFyYW0ge051bWJlcn0gcG9sbElkXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFja1xuICogQHBhcmFtIHtGdW5jdGlvbn0gdW5pbnN0YWxsXG4gKlxuICogQHRvZG8gY2xlYW51cCBudW1iZXIgb2YgcGFyYW1zXG4gKi9cblJlcXVlc3RNYW5hZ2VyLnByb3RvdHlwZS5zdGFydFBvbGxpbmcgPSBmdW5jdGlvbiAoZGF0YSwgcG9sbElkLCBjYWxsYmFjaywgdW5pbnN0YWxsKSB7XG4gICAgdGhpcy5wb2xscy5wdXNoKHtkYXRhOiBkYXRhLCBpZDogcG9sbElkLCBjYWxsYmFjazogY2FsbGJhY2ssIHVuaW5zdGFsbDogdW5pbnN0YWxsfSk7XG59O1xuLypqc2hpbnQgbWF4cGFyYW1zOjMgKi9cblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBzdG9wIHBvbGxpbmcgZm9yIGZpbHRlciB3aXRoIGdpdmVuIGlkXG4gKlxuICogQG1ldGhvZCBzdG9wUG9sbGluZ1xuICogQHBhcmFtIHtOdW1iZXJ9IHBvbGxJZFxuICovXG5SZXF1ZXN0TWFuYWdlci5wcm90b3R5cGUuc3RvcFBvbGxpbmcgPSBmdW5jdGlvbiAocG9sbElkKSB7XG4gICAgZm9yICh2YXIgaSA9IHRoaXMucG9sbHMubGVuZ3RoOyBpLS07KSB7XG4gICAgICAgIHZhciBwb2xsID0gdGhpcy5wb2xsc1tpXTtcbiAgICAgICAgaWYgKHBvbGwuaWQgPT09IHBvbGxJZCkge1xuICAgICAgICAgICAgdGhpcy5wb2xscy5zcGxpY2UoaSwgMSk7XG4gICAgICAgIH1cbiAgICB9XG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSBjYWxsZWQgdG8gcmVzZXQgcG9sbGluZyBtZWNoYW5pc20gb2YgcmVxdWVzdCBtYW5hZ2VyXG4gKlxuICogQG1ldGhvZCByZXNldFxuICovXG5SZXF1ZXN0TWFuYWdlci5wcm90b3R5cGUucmVzZXQgPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5wb2xscy5mb3JFYWNoKGZ1bmN0aW9uIChwb2xsKSB7XG4gICAgICAgIHBvbGwudW5pbnN0YWxsKHBvbGwuaWQpOyBcbiAgICB9KTtcbiAgICB0aGlzLnBvbGxzID0gW107XG5cbiAgICBpZiAodGhpcy50aW1lb3V0KSB7XG4gICAgICAgIGNsZWFyVGltZW91dCh0aGlzLnRpbWVvdXQpO1xuICAgICAgICB0aGlzLnRpbWVvdXQgPSBudWxsO1xuICAgIH1cbiAgICB0aGlzLnBvbGwoKTtcbn07XG5cbi8qKlxuICogU2hvdWxkIGJlIGNhbGxlZCB0byBwb2xsIGZvciBjaGFuZ2VzIG9uIGZpbHRlciB3aXRoIGdpdmVuIGlkXG4gKlxuICogQG1ldGhvZCBwb2xsXG4gKi9cblJlcXVlc3RNYW5hZ2VyLnByb3RvdHlwZS5wb2xsID0gZnVuY3Rpb24gKCkge1xuICAgIHRoaXMudGltZW91dCA9IHNldFRpbWVvdXQodGhpcy5wb2xsLmJpbmQodGhpcyksIGMuRVRIX1BPTExJTkdfVElNRU9VVCk7XG5cbiAgICBpZiAoIXRoaXMucG9sbHMubGVuZ3RoKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMucHJvdmlkZXIpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihlcnJvcnMuSW52YWxpZFByb3ZpZGVyKCkpO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIHBheWxvYWQgPSBKc29ucnBjLmdldEluc3RhbmNlKCkudG9CYXRjaFBheWxvYWQodGhpcy5wb2xscy5tYXAoZnVuY3Rpb24gKGRhdGEpIHtcbiAgICAgICAgcmV0dXJuIGRhdGEuZGF0YTtcbiAgICB9KSk7XG5cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5wcm92aWRlci5zZW5kQXN5bmMocGF5bG9hZCwgZnVuY3Rpb24gKGVycm9yLCByZXN1bHRzKSB7XG4gICAgICAgIC8vIFRPRE86IGNvbnNvbGUgbG9nP1xuICAgICAgICBpZiAoZXJyb3IpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICAgICAgXG4gICAgICAgIGlmICghdXRpbHMuaXNBcnJheShyZXN1bHRzKSkge1xuICAgICAgICAgICAgdGhyb3cgZXJyb3JzLkludmFsaWRSZXNwb25zZShyZXN1bHRzKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJlc3VsdHMubWFwKGZ1bmN0aW9uIChyZXN1bHQsIGluZGV4KSB7XG4gICAgICAgICAgICByZXN1bHQuY2FsbGJhY2sgPSBzZWxmLnBvbGxzW2luZGV4XS5jYWxsYmFjaztcbiAgICAgICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICAgIH0pLmZpbHRlcihmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgICAgICAgICB2YXIgdmFsaWQgPSBKc29ucnBjLmdldEluc3RhbmNlKCkuaXNWYWxpZFJlc3BvbnNlKHJlc3VsdCk7XG4gICAgICAgICAgICBpZiAoIXZhbGlkKSB7XG4gICAgICAgICAgICAgICAgcmVzdWx0LmNhbGxiYWNrKGVycm9ycy5JbnZhbGlkUmVzcG9uc2UocmVzdWx0KSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdmFsaWQ7XG4gICAgICAgIH0pLmZpbHRlcihmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgICAgICAgICByZXR1cm4gdXRpbHMuaXNBcnJheShyZXN1bHQucmVzdWx0KSAmJiByZXN1bHQucmVzdWx0Lmxlbmd0aCA+IDA7XG4gICAgICAgIH0pLmZvckVhY2goZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgcmVzdWx0LmNhbGxiYWNrKG51bGwsIHJlc3VsdC5yZXN1bHQpO1xuICAgICAgICB9KTtcbiAgICB9KTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gUmVxdWVzdE1hbmFnZXI7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIEBmaWxlIHNoaC5qc1xuICogQGF1dGhvcnM6XG4gKiAgIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciBNZXRob2QgPSByZXF1aXJlKCcuL21ldGhvZCcpO1xudmFyIGZvcm1hdHRlcnMgPSByZXF1aXJlKCcuL2Zvcm1hdHRlcnMnKTtcblxudmFyIHBvc3QgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAncG9zdCcsIFxuICAgIGNhbGw6ICdzaGhfcG9zdCcsIFxuICAgIHBhcmFtczogMSxcbiAgICBpbnB1dEZvcm1hdHRlcjogW2Zvcm1hdHRlcnMuaW5wdXRQb3N0Rm9ybWF0dGVyXVxufSk7XG5cbnZhciBuZXdJZGVudGl0eSA9IG5ldyBNZXRob2Qoe1xuICAgIG5hbWU6ICduZXdJZGVudGl0eScsXG4gICAgY2FsbDogJ3NoaF9uZXdJZGVudGl0eScsXG4gICAgcGFyYW1zOiAwXG59KTtcblxudmFyIGhhc0lkZW50aXR5ID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2hhc0lkZW50aXR5JyxcbiAgICBjYWxsOiAnc2hoX2hhc0lkZW50aXR5JyxcbiAgICBwYXJhbXM6IDFcbn0pO1xuXG52YXIgbmV3R3JvdXAgPSBuZXcgTWV0aG9kKHtcbiAgICBuYW1lOiAnbmV3R3JvdXAnLFxuICAgIGNhbGw6ICdzaGhfbmV3R3JvdXAnLFxuICAgIHBhcmFtczogMFxufSk7XG5cbnZhciBhZGRUb0dyb3VwID0gbmV3IE1ldGhvZCh7XG4gICAgbmFtZTogJ2FkZFRvR3JvdXAnLFxuICAgIGNhbGw6ICdzaGhfYWRkVG9Hcm91cCcsXG4gICAgcGFyYW1zOiAwXG59KTtcblxudmFyIG1ldGhvZHMgPSBbXG4gICAgcG9zdCxcbiAgICBuZXdJZGVudGl0eSxcbiAgICBoYXNJZGVudGl0eSxcbiAgICBuZXdHcm91cCxcbiAgICBhZGRUb0dyb3VwXG5dO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBtZXRob2RzOiBtZXRob2RzXG59O1xuXG4iLCIvKlxuICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIGV0aGVyZXVtLmpzLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieVxuICAgIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4gICAgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi5cblxuICAgIGV0aGVyZXVtLmpzIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsXG4gICAgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2ZcbiAgICBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG4gICAgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuXG5cbiAgICBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2VcbiAgICBhbG9uZyB3aXRoIGV0aGVyZXVtLmpzLiAgSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LlxuKi9cbi8qKiBcbiAqIEBmaWxlIHRyYW5zZmVyLmpzXG4gKiBAYXV0aG9yIE1hcmVrIEtvdGV3aWN6IDxtYXJla0BldGhkZXYuY29tPlxuICogQGRhdGUgMjAxNVxuICovXG5cbnZhciB3ZWIzID0gcmVxdWlyZSgnLi4vd2ViMycpO1xudmFyIElDQVAgPSByZXF1aXJlKCcuL2ljYXAnKTtcbnZhciBuYW1lcmVnID0gcmVxdWlyZSgnLi9uYW1lcmVnJyk7XG52YXIgY29udHJhY3QgPSByZXF1aXJlKCcuL2NvbnRyYWN0Jyk7XG5cbi8qKlxuICogU2hvdWxkIGJlIHVzZWQgdG8gbWFrZSBJQ0FQIHRyYW5zZmVyXG4gKlxuICogQG1ldGhvZCB0cmFuc2ZlclxuICogQHBhcmFtIHtTdHJpbmd9IGliYW4gbnVtYmVyXG4gKiBAcGFyYW0ge1N0cmluZ30gZnJvbSAoYWRkcmVzcylcbiAqIEBwYXJhbSB7VmFsdWV9IHZhbHVlIHRvIGJlIHRyYW5mZXJlZFxuICogQHBhcmFtIHtGdW5jdGlvbn0gY2FsbGJhY2ssIGNhbGxiYWNrXG4gKi9cbnZhciB0cmFuc2ZlciA9IGZ1bmN0aW9uIChmcm9tLCBpYmFuLCB2YWx1ZSwgY2FsbGJhY2spIHtcbiAgICB2YXIgaWNhcCA9IG5ldyBJQ0FQKGliYW4pOyBcbiAgICBpZiAoIWljYXAuaXNWYWxpZCgpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZCBpYmFuIGFkZHJlc3MnKTtcbiAgICB9XG5cbiAgICBpZiAoaWNhcC5pc0RpcmVjdCgpKSB7XG4gICAgICAgIHJldHVybiB0cmFuc2ZlclRvQWRkcmVzcyhmcm9tLCBpY2FwLmFkZHJlc3MoKSwgdmFsdWUsIGNhbGxiYWNrKTtcbiAgICB9XG4gICAgXG4gICAgaWYgKCFjYWxsYmFjaykge1xuICAgICAgICB2YXIgYWRkcmVzcyA9IG5hbWVyZWcuYWRkcihpY2FwLmluc3RpdHV0aW9uKCkpO1xuICAgICAgICByZXR1cm4gZGVwb3NpdChmcm9tLCBhZGRyZXNzLCB2YWx1ZSwgaWNhcC5jbGllbnQoKSk7XG4gICAgfVxuXG4gICAgbmFtZXJlZy5hZGRyKGljYXAuaW5zaXR1dGlvbigpLCBmdW5jdGlvbiAoZXJyLCBhZGRyZXNzKSB7XG4gICAgICAgIHJldHVybiBkZXBvc2l0KGZyb20sIGFkZHJlc3MsIHZhbHVlLCBpY2FwLmNsaWVudCgpLCBjYWxsYmFjayk7XG4gICAgfSk7XG4gICAgXG59O1xuXG4vKipcbiAqIFNob3VsZCBiZSB1c2VkIHRvIHRyYW5zZmVyIGZ1bmRzIHRvIGNlcnRhaW4gYWRkcmVzc1xuICpcbiAqIEBtZXRob2QgdHJhbnNmZXJUb0FkZHJlc3NcbiAqIEBwYXJhbSB7U3RyaW5nfSBhZGRyZXNzXG4gKiBAcGFyYW0ge1N0cmluZ30gZnJvbSAoYWRkcmVzcylcbiAqIEBwYXJhbSB7VmFsdWV9IHZhbHVlIHRvIGJlIHRyYW5mZXJlZFxuICogQHBhcmFtIHtGdW5jdGlvbn0gY2FsbGJhY2ssIGNhbGxiYWNrXG4gKi9cbnZhciB0cmFuc2ZlclRvQWRkcmVzcyA9IGZ1bmN0aW9uIChmcm9tLCBhZGRyZXNzLCB2YWx1ZSwgY2FsbGJhY2spIHtcbiAgICByZXR1cm4gd2ViMy5ldGguc2VuZFRyYW5zYWN0aW9uKHtcbiAgICAgICAgYWRkcmVzczogYWRkcmVzcyxcbiAgICAgICAgZnJvbTogZnJvbSxcbiAgICAgICAgdmFsdWU6IHZhbHVlXG4gICAgfSwgY2FsbGJhY2spO1xufTtcblxuLyoqXG4gKiBTaG91bGQgYmUgdXNlZCB0byBkZXBvc2l0IGZ1bmRzIHRvIGdlbmVyaWMgRXhjaGFuZ2UgY29udHJhY3QgKG11c3QgaW1wbGVtZW50IGRlcG9zaXQoYnl0ZXMzMikgbWV0aG9kISlcbiAqXG4gKiBAbWV0aG9kIGRlcG9zaXRcbiAqIEBwYXJhbSB7U3RyaW5nfSBhZGRyZXNzXG4gKiBAcGFyYW0ge1N0cmluZ30gZnJvbSAoYWRkcmVzcylcbiAqIEBwYXJhbSB7VmFsdWV9IHZhbHVlIHRvIGJlIHRyYW5mZXJlZFxuICogQHBhcmFtIHtTdHJpbmd9IGNsaWVudCB1bmlxdWUgaWRlbnRpZmllclxuICogQHBhcmFtIHtGdW5jdGlvbn0gY2FsbGJhY2ssIGNhbGxiYWNrXG4gKi9cbnZhciBkZXBvc2l0ID0gZnVuY3Rpb24gKGZyb20sIGFkZHJlc3MsIHZhbHVlLCBjbGllbnQsIGNhbGxiYWNrKSB7XG4gICAgdmFyIGFiaSA9IFt7XCJjb25zdGFudFwiOmZhbHNlLFwiaW5wdXRzXCI6W3tcIm5hbWVcIjpcIm5hbWVcIixcInR5cGVcIjpcImJ5dGVzMzJcIn1dLFwibmFtZVwiOlwiZGVwb3NpdFwiLFwib3V0cHV0c1wiOltdLFwidHlwZVwiOlwiZnVuY3Rpb25cIn1dO1xuICAgIHJldHVybiBjb250cmFjdChhYmkpLmF0KGFkZHJlc3MpLmRlcG9zaXQoY2xpZW50LCB7XG4gICAgICAgIGZyb206IGZyb20sXG4gICAgICAgIHZhbHVlOiB2YWx1ZVxuICAgIH0sIGNhbGxiYWNrKTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gdHJhbnNmZXI7XG5cbiIsIi8qXG4gICAgVGhpcyBmaWxlIGlzIHBhcnQgb2YgZXRoZXJldW0uanMuXG5cbiAgICBldGhlcmV1bS5qcyBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5XG4gICAgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5XG4gICAgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3JcbiAgICAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG4gICAgZXRoZXJldW0uanMgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbiAgICBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZlxuICAgIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGVcbiAgICBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy5cblxuICAgIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuICAgIGFsb25nIHdpdGggZXRoZXJldW0uanMuICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuLyoqIEBmaWxlIHdhdGNoZXMuanNcbiAqIEBhdXRob3JzOlxuICogICBNYXJlayBLb3Rld2ljeiA8bWFyZWtAZXRoZGV2LmNvbT5cbiAqIEBkYXRlIDIwMTVcbiAqL1xuXG52YXIgTWV0aG9kID0gcmVxdWlyZSgnLi9tZXRob2QnKTtcblxuLy8vIEByZXR1cm5zIGFuIGFycmF5IG9mIG9iamVjdHMgZGVzY3JpYmluZyB3ZWIzLmV0aC5maWx0ZXIgYXBpIG1ldGhvZHNcbnZhciBldGggPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIG5ld0ZpbHRlckNhbGwgPSBmdW5jdGlvbiAoYXJncykge1xuICAgICAgICB2YXIgdHlwZSA9IGFyZ3NbMF07XG5cbiAgICAgICAgc3dpdGNoKHR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgJ2xhdGVzdCc6XG4gICAgICAgICAgICAgICAgYXJncy5wb3AoKTtcbiAgICAgICAgICAgICAgICB0aGlzLnBhcmFtcyA9IDA7XG4gICAgICAgICAgICAgICAgcmV0dXJuICdldGhfbmV3QmxvY2tGaWx0ZXInO1xuICAgICAgICAgICAgY2FzZSAncGVuZGluZyc6XG4gICAgICAgICAgICAgICAgYXJncy5wb3AoKTtcbiAgICAgICAgICAgICAgICB0aGlzLnBhcmFtcyA9IDA7XG4gICAgICAgICAgICAgICAgcmV0dXJuICdldGhfbmV3UGVuZGluZ1RyYW5zYWN0aW9uRmlsdGVyJztcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgcmV0dXJuICdldGhfbmV3RmlsdGVyJztcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICB2YXIgbmV3RmlsdGVyID0gbmV3IE1ldGhvZCh7XG4gICAgICAgIG5hbWU6ICduZXdGaWx0ZXInLFxuICAgICAgICBjYWxsOiBuZXdGaWx0ZXJDYWxsLFxuICAgICAgICBwYXJhbXM6IDFcbiAgICB9KTtcblxuICAgIHZhciB1bmluc3RhbGxGaWx0ZXIgPSBuZXcgTWV0aG9kKHtcbiAgICAgICAgbmFtZTogJ3VuaW5zdGFsbEZpbHRlcicsXG4gICAgICAgIGNhbGw6ICdldGhfdW5pbnN0YWxsRmlsdGVyJyxcbiAgICAgICAgcGFyYW1zOiAxXG4gICAgfSk7XG5cbiAgICB2YXIgZ2V0TG9ncyA9IG5ldyBNZXRob2Qoe1xuICAgICAgICBuYW1lOiAnZ2V0TG9ncycsXG4gICAgICAgIGNhbGw6ICdldGhfZ2V0RmlsdGVyTG9ncycsXG4gICAgICAgIHBhcmFtczogMVxuICAgIH0pO1xuXG4gICAgdmFyIHBvbGwgPSBuZXcgTWV0aG9kKHtcbiAgICAgICAgbmFtZTogJ3BvbGwnLFxuICAgICAgICBjYWxsOiAnZXRoX2dldEZpbHRlckNoYW5nZXMnLFxuICAgICAgICBwYXJhbXM6IDFcbiAgICB9KTtcblxuICAgIHJldHVybiBbXG4gICAgICAgIG5ld0ZpbHRlcixcbiAgICAgICAgdW5pbnN0YWxsRmlsdGVyLFxuICAgICAgICBnZXRMb2dzLFxuICAgICAgICBwb2xsXG4gICAgXTtcbn07XG5cbi8vLyBAcmV0dXJucyBhbiBhcnJheSBvZiBvYmplY3RzIGRlc2NyaWJpbmcgd2ViMy5zaGgud2F0Y2ggYXBpIG1ldGhvZHNcbnZhciBzaGggPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIG5ld0ZpbHRlciA9IG5ldyBNZXRob2Qoe1xuICAgICAgICBuYW1lOiAnbmV3RmlsdGVyJyxcbiAgICAgICAgY2FsbDogJ3NoaF9uZXdGaWx0ZXInLFxuICAgICAgICBwYXJhbXM6IDFcbiAgICB9KTtcblxuICAgIHZhciB1bmluc3RhbGxGaWx0ZXIgPSBuZXcgTWV0aG9kKHtcbiAgICAgICAgbmFtZTogJ3VuaW5zdGFsbEZpbHRlcicsXG4gICAgICAgIGNhbGw6ICdzaGhfdW5pbnN0YWxsRmlsdGVyJyxcbiAgICAgICAgcGFyYW1zOiAxXG4gICAgfSk7XG5cbiAgICB2YXIgZ2V0TG9ncyA9IG5ldyBNZXRob2Qoe1xuICAgICAgICBuYW1lOiAnZ2V0TG9ncycsXG4gICAgICAgIGNhbGw6ICdzaGhfZ2V0TWVzc2FnZXMnLFxuICAgICAgICBwYXJhbXM6IDFcbiAgICB9KTtcblxuICAgIHZhciBwb2xsID0gbmV3IE1ldGhvZCh7XG4gICAgICAgIG5hbWU6ICdwb2xsJyxcbiAgICAgICAgY2FsbDogJ3NoaF9nZXRGaWx0ZXJDaGFuZ2VzJyxcbiAgICAgICAgcGFyYW1zOiAxXG4gICAgfSk7XG5cbiAgICByZXR1cm4gW1xuICAgICAgICBuZXdGaWx0ZXIsXG4gICAgICAgIHVuaW5zdGFsbEZpbHRlcixcbiAgICAgICAgZ2V0TG9ncyxcbiAgICAgICAgcG9sbFxuICAgIF07XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBldGg6IGV0aCxcbiAgICBzaGg6IHNoaFxufTtcblxuIixudWxsLCI7KGZ1bmN0aW9uIChyb290LCBmYWN0b3J5KSB7XG5cdGlmICh0eXBlb2YgZXhwb3J0cyA9PT0gXCJvYmplY3RcIikge1xuXHRcdC8vIENvbW1vbkpTXG5cdFx0bW9kdWxlLmV4cG9ydHMgPSBleHBvcnRzID0gZmFjdG9yeSgpO1xuXHR9XG5cdGVsc2UgaWYgKHR5cGVvZiBkZWZpbmUgPT09IFwiZnVuY3Rpb25cIiAmJiBkZWZpbmUuYW1kKSB7XG5cdFx0Ly8gQU1EXG5cdFx0ZGVmaW5lKFtdLCBmYWN0b3J5KTtcblx0fVxuXHRlbHNlIHtcblx0XHQvLyBHbG9iYWwgKGJyb3dzZXIpXG5cdFx0cm9vdC5DcnlwdG9KUyA9IGZhY3RvcnkoKTtcblx0fVxufSh0aGlzLCBmdW5jdGlvbiAoKSB7XG5cblx0LyoqXG5cdCAqIENyeXB0b0pTIGNvcmUgY29tcG9uZW50cy5cblx0ICovXG5cdHZhciBDcnlwdG9KUyA9IENyeXB0b0pTIHx8IChmdW5jdGlvbiAoTWF0aCwgdW5kZWZpbmVkKSB7XG5cdCAgICAvKipcblx0ICAgICAqIENyeXB0b0pTIG5hbWVzcGFjZS5cblx0ICAgICAqL1xuXHQgICAgdmFyIEMgPSB7fTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBMaWJyYXJ5IG5hbWVzcGFjZS5cblx0ICAgICAqL1xuXHQgICAgdmFyIENfbGliID0gQy5saWIgPSB7fTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBCYXNlIG9iamVjdCBmb3IgcHJvdG90eXBhbCBpbmhlcml0YW5jZS5cblx0ICAgICAqL1xuXHQgICAgdmFyIEJhc2UgPSBDX2xpYi5CYXNlID0gKGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICBmdW5jdGlvbiBGKCkge31cblxuXHQgICAgICAgIHJldHVybiB7XG5cdCAgICAgICAgICAgIC8qKlxuXHQgICAgICAgICAgICAgKiBDcmVhdGVzIGEgbmV3IG9iamVjdCB0aGF0IGluaGVyaXRzIGZyb20gdGhpcyBvYmplY3QuXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvdmVycmlkZXMgUHJvcGVydGllcyB0byBjb3B5IGludG8gdGhlIG5ldyBvYmplY3QuXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqIEByZXR1cm4ge09iamVjdH0gVGhlIG5ldyBvYmplY3QuXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqIEBzdGF0aWNcblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogICAgIHZhciBNeVR5cGUgPSBDcnlwdG9KUy5saWIuQmFzZS5leHRlbmQoe1xuXHQgICAgICAgICAgICAgKiAgICAgICAgIGZpZWxkOiAndmFsdWUnLFxuXHQgICAgICAgICAgICAgKlxuXHQgICAgICAgICAgICAgKiAgICAgICAgIG1ldGhvZDogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICAgKiAgICAgICAgIH1cblx0ICAgICAgICAgICAgICogICAgIH0pO1xuXHQgICAgICAgICAgICAgKi9cblx0ICAgICAgICAgICAgZXh0ZW5kOiBmdW5jdGlvbiAob3ZlcnJpZGVzKSB7XG5cdCAgICAgICAgICAgICAgICAvLyBTcGF3blxuXHQgICAgICAgICAgICAgICAgRi5wcm90b3R5cGUgPSB0aGlzO1xuXHQgICAgICAgICAgICAgICAgdmFyIHN1YnR5cGUgPSBuZXcgRigpO1xuXG5cdCAgICAgICAgICAgICAgICAvLyBBdWdtZW50XG5cdCAgICAgICAgICAgICAgICBpZiAob3ZlcnJpZGVzKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgc3VidHlwZS5taXhJbihvdmVycmlkZXMpO1xuXHQgICAgICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgICAgICAvLyBDcmVhdGUgZGVmYXVsdCBpbml0aWFsaXplclxuXHQgICAgICAgICAgICAgICAgaWYgKCFzdWJ0eXBlLmhhc093blByb3BlcnR5KCdpbml0JykpIHtcblx0ICAgICAgICAgICAgICAgICAgICBzdWJ0eXBlLmluaXQgPSBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHN1YnR5cGUuJHN1cGVyLmluaXQuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcblx0ICAgICAgICAgICAgICAgICAgICB9O1xuXHQgICAgICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgICAgICAvLyBJbml0aWFsaXplcidzIHByb3RvdHlwZSBpcyB0aGUgc3VidHlwZSBvYmplY3Rcblx0ICAgICAgICAgICAgICAgIHN1YnR5cGUuaW5pdC5wcm90b3R5cGUgPSBzdWJ0eXBlO1xuXG5cdCAgICAgICAgICAgICAgICAvLyBSZWZlcmVuY2Ugc3VwZXJ0eXBlXG5cdCAgICAgICAgICAgICAgICBzdWJ0eXBlLiRzdXBlciA9IHRoaXM7XG5cblx0ICAgICAgICAgICAgICAgIHJldHVybiBzdWJ0eXBlO1xuXHQgICAgICAgICAgICB9LFxuXG5cdCAgICAgICAgICAgIC8qKlxuXHQgICAgICAgICAgICAgKiBFeHRlbmRzIHRoaXMgb2JqZWN0IGFuZCBydW5zIHRoZSBpbml0IG1ldGhvZC5cblx0ICAgICAgICAgICAgICogQXJndW1lbnRzIHRvIGNyZWF0ZSgpIHdpbGwgYmUgcGFzc2VkIHRvIGluaXQoKS5cblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogQHJldHVybiB7T2JqZWN0fSBUaGUgbmV3IG9iamVjdC5cblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogQHN0YXRpY1xuXHQgICAgICAgICAgICAgKlxuXHQgICAgICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAgICAgKlxuXHQgICAgICAgICAgICAgKiAgICAgdmFyIGluc3RhbmNlID0gTXlUeXBlLmNyZWF0ZSgpO1xuXHQgICAgICAgICAgICAgKi9cblx0ICAgICAgICAgICAgY3JlYXRlOiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgICAgICB2YXIgaW5zdGFuY2UgPSB0aGlzLmV4dGVuZCgpO1xuXHQgICAgICAgICAgICAgICAgaW5zdGFuY2UuaW5pdC5hcHBseShpbnN0YW5jZSwgYXJndW1lbnRzKTtcblxuXHQgICAgICAgICAgICAgICAgcmV0dXJuIGluc3RhbmNlO1xuXHQgICAgICAgICAgICB9LFxuXG5cdCAgICAgICAgICAgIC8qKlxuXHQgICAgICAgICAgICAgKiBJbml0aWFsaXplcyBhIG5ld2x5IGNyZWF0ZWQgb2JqZWN0LlxuXHQgICAgICAgICAgICAgKiBPdmVycmlkZSB0aGlzIG1ldGhvZCB0byBhZGQgc29tZSBsb2dpYyB3aGVuIHlvdXIgb2JqZWN0cyBhcmUgY3JlYXRlZC5cblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogICAgIHZhciBNeVR5cGUgPSBDcnlwdG9KUy5saWIuQmFzZS5leHRlbmQoe1xuXHQgICAgICAgICAgICAgKiAgICAgICAgIGluaXQ6IGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgICogICAgICAgICAgICAgLy8gLi4uXG5cdCAgICAgICAgICAgICAqICAgICAgICAgfVxuXHQgICAgICAgICAgICAgKiAgICAgfSk7XG5cdCAgICAgICAgICAgICAqL1xuXHQgICAgICAgICAgICBpbml0OiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgIH0sXG5cblx0ICAgICAgICAgICAgLyoqXG5cdCAgICAgICAgICAgICAqIENvcGllcyBwcm9wZXJ0aWVzIGludG8gdGhpcyBvYmplY3QuXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwcm9wZXJ0aWVzIFRoZSBwcm9wZXJ0aWVzIHRvIG1peCBpbi5cblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgICAgICpcblx0ICAgICAgICAgICAgICogICAgIE15VHlwZS5taXhJbih7XG5cdCAgICAgICAgICAgICAqICAgICAgICAgZmllbGQ6ICd2YWx1ZSdcblx0ICAgICAgICAgICAgICogICAgIH0pO1xuXHQgICAgICAgICAgICAgKi9cblx0ICAgICAgICAgICAgbWl4SW46IGZ1bmN0aW9uIChwcm9wZXJ0aWVzKSB7XG5cdCAgICAgICAgICAgICAgICBmb3IgKHZhciBwcm9wZXJ0eU5hbWUgaW4gcHJvcGVydGllcykge1xuXHQgICAgICAgICAgICAgICAgICAgIGlmIChwcm9wZXJ0aWVzLmhhc093blByb3BlcnR5KHByb3BlcnR5TmFtZSkpIHtcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdGhpc1twcm9wZXJ0eU5hbWVdID0gcHJvcGVydGllc1twcm9wZXJ0eU5hbWVdO1xuXHQgICAgICAgICAgICAgICAgICAgIH1cblx0ICAgICAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICAgICAgLy8gSUUgd29uJ3QgY29weSB0b1N0cmluZyB1c2luZyB0aGUgbG9vcCBhYm92ZVxuXHQgICAgICAgICAgICAgICAgaWYgKHByb3BlcnRpZXMuaGFzT3duUHJvcGVydHkoJ3RvU3RyaW5nJykpIHtcblx0ICAgICAgICAgICAgICAgICAgICB0aGlzLnRvU3RyaW5nID0gcHJvcGVydGllcy50b1N0cmluZztcblx0ICAgICAgICAgICAgICAgIH1cblx0ICAgICAgICAgICAgfSxcblxuXHQgICAgICAgICAgICAvKipcblx0ICAgICAgICAgICAgICogQ3JlYXRlcyBhIGNvcHkgb2YgdGhpcyBvYmplY3QuXG5cdCAgICAgICAgICAgICAqXG5cdCAgICAgICAgICAgICAqIEByZXR1cm4ge09iamVjdH0gVGhlIGNsb25lLlxuXHQgICAgICAgICAgICAgKlxuXHQgICAgICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAgICAgKlxuXHQgICAgICAgICAgICAgKiAgICAgdmFyIGNsb25lID0gaW5zdGFuY2UuY2xvbmUoKTtcblx0ICAgICAgICAgICAgICovXG5cdCAgICAgICAgICAgIGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5pbml0LnByb3RvdHlwZS5leHRlbmQodGhpcyk7XG5cdCAgICAgICAgICAgIH1cblx0ICAgICAgICB9O1xuXHQgICAgfSgpKTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBBbiBhcnJheSBvZiAzMi1iaXQgd29yZHMuXG5cdCAgICAgKlxuXHQgICAgICogQHByb3BlcnR5IHtBcnJheX0gd29yZHMgVGhlIGFycmF5IG9mIDMyLWJpdCB3b3Jkcy5cblx0ICAgICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBzaWdCeXRlcyBUaGUgbnVtYmVyIG9mIHNpZ25pZmljYW50IGJ5dGVzIGluIHRoaXMgd29yZCBhcnJheS5cblx0ICAgICAqL1xuXHQgICAgdmFyIFdvcmRBcnJheSA9IENfbGliLldvcmRBcnJheSA9IEJhc2UuZXh0ZW5kKHtcblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBJbml0aWFsaXplcyBhIG5ld2x5IGNyZWF0ZWQgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7QXJyYXl9IHdvcmRzIChPcHRpb25hbCkgQW4gYXJyYXkgb2YgMzItYml0IHdvcmRzLlxuXHQgICAgICAgICAqIEBwYXJhbSB7bnVtYmVyfSBzaWdCeXRlcyAoT3B0aW9uYWwpIFRoZSBudW1iZXIgb2Ygc2lnbmlmaWNhbnQgYnl0ZXMgaW4gdGhlIHdvcmRzLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgd29yZEFycmF5ID0gQ3J5cHRvSlMubGliLldvcmRBcnJheS5jcmVhdGUoKTtcblx0ICAgICAgICAgKiAgICAgdmFyIHdvcmRBcnJheSA9IENyeXB0b0pTLmxpYi5Xb3JkQXJyYXkuY3JlYXRlKFsweDAwMDEwMjAzLCAweDA0MDUwNjA3XSk7XG5cdCAgICAgICAgICogICAgIHZhciB3b3JkQXJyYXkgPSBDcnlwdG9KUy5saWIuV29yZEFycmF5LmNyZWF0ZShbMHgwMDAxMDIwMywgMHgwNDA1MDYwN10sIDYpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIGluaXQ6IGZ1bmN0aW9uICh3b3Jkcywgc2lnQnl0ZXMpIHtcblx0ICAgICAgICAgICAgd29yZHMgPSB0aGlzLndvcmRzID0gd29yZHMgfHwgW107XG5cblx0ICAgICAgICAgICAgaWYgKHNpZ0J5dGVzICE9IHVuZGVmaW5lZCkge1xuXHQgICAgICAgICAgICAgICAgdGhpcy5zaWdCeXRlcyA9IHNpZ0J5dGVzO1xuXHQgICAgICAgICAgICB9IGVsc2Uge1xuXHQgICAgICAgICAgICAgICAgdGhpcy5zaWdCeXRlcyA9IHdvcmRzLmxlbmd0aCAqIDQ7XG5cdCAgICAgICAgICAgIH1cblx0ICAgICAgICB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQ29udmVydHMgdGhpcyB3b3JkIGFycmF5IHRvIGEgc3RyaW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtFbmNvZGVyfSBlbmNvZGVyIChPcHRpb25hbCkgVGhlIGVuY29kaW5nIHN0cmF0ZWd5IHRvIHVzZS4gRGVmYXVsdDogQ3J5cHRvSlMuZW5jLkhleFxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7c3RyaW5nfSBUaGUgc3RyaW5naWZpZWQgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHN0cmluZyA9IHdvcmRBcnJheSArICcnO1xuXHQgICAgICAgICAqICAgICB2YXIgc3RyaW5nID0gd29yZEFycmF5LnRvU3RyaW5nKCk7XG5cdCAgICAgICAgICogICAgIHZhciBzdHJpbmcgPSB3b3JkQXJyYXkudG9TdHJpbmcoQ3J5cHRvSlMuZW5jLlV0ZjgpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIHRvU3RyaW5nOiBmdW5jdGlvbiAoZW5jb2Rlcikge1xuXHQgICAgICAgICAgICByZXR1cm4gKGVuY29kZXIgfHwgSGV4KS5zdHJpbmdpZnkodGhpcyk7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENvbmNhdGVuYXRlcyBhIHdvcmQgYXJyYXkgdG8gdGhpcyB3b3JkIGFycmF5LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtXb3JkQXJyYXl9IHdvcmRBcnJheSBUaGUgd29yZCBhcnJheSB0byBhcHBlbmQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtXb3JkQXJyYXl9IFRoaXMgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgd29yZEFycmF5MS5jb25jYXQod29yZEFycmF5Mik7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgY29uY2F0OiBmdW5jdGlvbiAod29yZEFycmF5KSB7XG5cdCAgICAgICAgICAgIC8vIFNob3J0Y3V0c1xuXHQgICAgICAgICAgICB2YXIgdGhpc1dvcmRzID0gdGhpcy53b3Jkcztcblx0ICAgICAgICAgICAgdmFyIHRoYXRXb3JkcyA9IHdvcmRBcnJheS53b3Jkcztcblx0ICAgICAgICAgICAgdmFyIHRoaXNTaWdCeXRlcyA9IHRoaXMuc2lnQnl0ZXM7XG5cdCAgICAgICAgICAgIHZhciB0aGF0U2lnQnl0ZXMgPSB3b3JkQXJyYXkuc2lnQnl0ZXM7XG5cblx0ICAgICAgICAgICAgLy8gQ2xhbXAgZXhjZXNzIGJpdHNcblx0ICAgICAgICAgICAgdGhpcy5jbGFtcCgpO1xuXG5cdCAgICAgICAgICAgIC8vIENvbmNhdFxuXHQgICAgICAgICAgICBpZiAodGhpc1NpZ0J5dGVzICUgNCkge1xuXHQgICAgICAgICAgICAgICAgLy8gQ29weSBvbmUgYnl0ZSBhdCBhIHRpbWVcblx0ICAgICAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhhdFNpZ0J5dGVzOyBpKyspIHtcblx0ICAgICAgICAgICAgICAgICAgICB2YXIgdGhhdEJ5dGUgPSAodGhhdFdvcmRzW2kgPj4+IDJdID4+PiAoMjQgLSAoaSAlIDQpICogOCkpICYgMHhmZjtcblx0ICAgICAgICAgICAgICAgICAgICB0aGlzV29yZHNbKHRoaXNTaWdCeXRlcyArIGkpID4+PiAyXSB8PSB0aGF0Qnl0ZSA8PCAoMjQgLSAoKHRoaXNTaWdCeXRlcyArIGkpICUgNCkgKiA4KTtcblx0ICAgICAgICAgICAgICAgIH1cblx0ICAgICAgICAgICAgfSBlbHNlIHtcblx0ICAgICAgICAgICAgICAgIC8vIENvcHkgb25lIHdvcmQgYXQgYSB0aW1lXG5cdCAgICAgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRoYXRTaWdCeXRlczsgaSArPSA0KSB7XG5cdCAgICAgICAgICAgICAgICAgICAgdGhpc1dvcmRzWyh0aGlzU2lnQnl0ZXMgKyBpKSA+Pj4gMl0gPSB0aGF0V29yZHNbaSA+Pj4gMl07XG5cdCAgICAgICAgICAgICAgICB9XG5cdCAgICAgICAgICAgIH1cblx0ICAgICAgICAgICAgdGhpcy5zaWdCeXRlcyArPSB0aGF0U2lnQnl0ZXM7XG5cblx0ICAgICAgICAgICAgLy8gQ2hhaW5hYmxlXG5cdCAgICAgICAgICAgIHJldHVybiB0aGlzO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBSZW1vdmVzIGluc2lnbmlmaWNhbnQgYml0cy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgd29yZEFycmF5LmNsYW1wKCk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgY2xhbXA6IGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgLy8gU2hvcnRjdXRzXG5cdCAgICAgICAgICAgIHZhciB3b3JkcyA9IHRoaXMud29yZHM7XG5cdCAgICAgICAgICAgIHZhciBzaWdCeXRlcyA9IHRoaXMuc2lnQnl0ZXM7XG5cblx0ICAgICAgICAgICAgLy8gQ2xhbXBcblx0ICAgICAgICAgICAgd29yZHNbc2lnQnl0ZXMgPj4+IDJdICY9IDB4ZmZmZmZmZmYgPDwgKDMyIC0gKHNpZ0J5dGVzICUgNCkgKiA4KTtcblx0ICAgICAgICAgICAgd29yZHMubGVuZ3RoID0gTWF0aC5jZWlsKHNpZ0J5dGVzIC8gNCk7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENyZWF0ZXMgYSBjb3B5IG9mIHRoaXMgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1dvcmRBcnJheX0gVGhlIGNsb25lLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgY2xvbmUgPSB3b3JkQXJyYXkuY2xvbmUoKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBjbG9uZTogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICB2YXIgY2xvbmUgPSBCYXNlLmNsb25lLmNhbGwodGhpcyk7XG5cdCAgICAgICAgICAgIGNsb25lLndvcmRzID0gdGhpcy53b3Jkcy5zbGljZSgwKTtcblxuXHQgICAgICAgICAgICByZXR1cm4gY2xvbmU7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENyZWF0ZXMgYSB3b3JkIGFycmF5IGZpbGxlZCB3aXRoIHJhbmRvbSBieXRlcy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7bnVtYmVyfSBuQnl0ZXMgVGhlIG51bWJlciBvZiByYW5kb20gYnl0ZXMgdG8gZ2VuZXJhdGUuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtXb3JkQXJyYXl9IFRoZSByYW5kb20gd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBzdGF0aWNcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHdvcmRBcnJheSA9IENyeXB0b0pTLmxpYi5Xb3JkQXJyYXkucmFuZG9tKDE2KTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICByYW5kb206IGZ1bmN0aW9uIChuQnl0ZXMpIHtcblx0ICAgICAgICAgICAgdmFyIHdvcmRzID0gW107XG5cblx0ICAgICAgICAgICAgdmFyIHIgPSAoZnVuY3Rpb24gKG1fdykge1xuXHQgICAgICAgICAgICAgICAgdmFyIG1fdyA9IG1fdztcblx0ICAgICAgICAgICAgICAgIHZhciBtX3ogPSAweDNhZGU2OGIxO1xuXHQgICAgICAgICAgICAgICAgdmFyIG1hc2sgPSAweGZmZmZmZmZmO1xuXG5cdCAgICAgICAgICAgICAgICByZXR1cm4gZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICAgICAgICAgIG1feiA9ICgweDkwNjkgKiAobV96ICYgMHhGRkZGKSArIChtX3ogPj4gMHgxMCkpICYgbWFzaztcblx0ICAgICAgICAgICAgICAgICAgICBtX3cgPSAoMHg0NjUwICogKG1fdyAmIDB4RkZGRikgKyAobV93ID4+IDB4MTApKSAmIG1hc2s7XG5cdCAgICAgICAgICAgICAgICAgICAgdmFyIHJlc3VsdCA9ICgobV96IDw8IDB4MTApICsgbV93KSAmIG1hc2s7XG5cdCAgICAgICAgICAgICAgICAgICAgcmVzdWx0IC89IDB4MTAwMDAwMDAwO1xuXHQgICAgICAgICAgICAgICAgICAgIHJlc3VsdCArPSAwLjU7XG5cdCAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHJlc3VsdCAqIChNYXRoLnJhbmRvbSgpID4gLjUgPyAxIDogLTEpO1xuXHQgICAgICAgICAgICAgICAgfVxuXHQgICAgICAgICAgICB9KTtcblxuXHQgICAgICAgICAgICBmb3IgKHZhciBpID0gMCwgcmNhY2hlOyBpIDwgbkJ5dGVzOyBpICs9IDQpIHtcblx0ICAgICAgICAgICAgICAgIHZhciBfciA9IHIoKHJjYWNoZSB8fCBNYXRoLnJhbmRvbSgpKSAqIDB4MTAwMDAwMDAwKTtcblxuXHQgICAgICAgICAgICAgICAgcmNhY2hlID0gX3IoKSAqIDB4M2FkZTY3Yjc7XG5cdCAgICAgICAgICAgICAgICB3b3Jkcy5wdXNoKChfcigpICogMHgxMDAwMDAwMDApIHwgMCk7XG5cdCAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICByZXR1cm4gbmV3IFdvcmRBcnJheS5pbml0KHdvcmRzLCBuQnl0ZXMpO1xuXHQgICAgICAgIH1cblx0ICAgIH0pO1xuXG5cdCAgICAvKipcblx0ICAgICAqIEVuY29kZXIgbmFtZXNwYWNlLlxuXHQgICAgICovXG5cdCAgICB2YXIgQ19lbmMgPSBDLmVuYyA9IHt9O1xuXG5cdCAgICAvKipcblx0ICAgICAqIEhleCBlbmNvZGluZyBzdHJhdGVneS5cblx0ICAgICAqL1xuXHQgICAgdmFyIEhleCA9IENfZW5jLkhleCA9IHtcblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDb252ZXJ0cyBhIHdvcmQgYXJyYXkgdG8gYSBoZXggc3RyaW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtXb3JkQXJyYXl9IHdvcmRBcnJheSBUaGUgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge3N0cmluZ30gVGhlIGhleCBzdHJpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAc3RhdGljXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciBoZXhTdHJpbmcgPSBDcnlwdG9KUy5lbmMuSGV4LnN0cmluZ2lmeSh3b3JkQXJyYXkpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIHN0cmluZ2lmeTogZnVuY3Rpb24gKHdvcmRBcnJheSkge1xuXHQgICAgICAgICAgICAvLyBTaG9ydGN1dHNcblx0ICAgICAgICAgICAgdmFyIHdvcmRzID0gd29yZEFycmF5LndvcmRzO1xuXHQgICAgICAgICAgICB2YXIgc2lnQnl0ZXMgPSB3b3JkQXJyYXkuc2lnQnl0ZXM7XG5cblx0ICAgICAgICAgICAgLy8gQ29udmVydFxuXHQgICAgICAgICAgICB2YXIgaGV4Q2hhcnMgPSBbXTtcblx0ICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBzaWdCeXRlczsgaSsrKSB7XG5cdCAgICAgICAgICAgICAgICB2YXIgYml0ZSA9ICh3b3Jkc1tpID4+PiAyXSA+Pj4gKDI0IC0gKGkgJSA0KSAqIDgpKSAmIDB4ZmY7XG5cdCAgICAgICAgICAgICAgICBoZXhDaGFycy5wdXNoKChiaXRlID4+PiA0KS50b1N0cmluZygxNikpO1xuXHQgICAgICAgICAgICAgICAgaGV4Q2hhcnMucHVzaCgoYml0ZSAmIDB4MGYpLnRvU3RyaW5nKDE2KSk7XG5cdCAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICByZXR1cm4gaGV4Q2hhcnMuam9pbignJyk7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENvbnZlcnRzIGEgaGV4IHN0cmluZyB0byBhIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gaGV4U3RyIFRoZSBoZXggc3RyaW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7V29yZEFycmF5fSBUaGUgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBzdGF0aWNcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHdvcmRBcnJheSA9IENyeXB0b0pTLmVuYy5IZXgucGFyc2UoaGV4U3RyaW5nKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBwYXJzZTogZnVuY3Rpb24gKGhleFN0cikge1xuXHQgICAgICAgICAgICAvLyBTaG9ydGN1dFxuXHQgICAgICAgICAgICB2YXIgaGV4U3RyTGVuZ3RoID0gaGV4U3RyLmxlbmd0aDtcblxuXHQgICAgICAgICAgICAvLyBDb252ZXJ0XG5cdCAgICAgICAgICAgIHZhciB3b3JkcyA9IFtdO1xuXHQgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGhleFN0ckxlbmd0aDsgaSArPSAyKSB7XG5cdCAgICAgICAgICAgICAgICB3b3Jkc1tpID4+PiAzXSB8PSBwYXJzZUludChoZXhTdHIuc3Vic3RyKGksIDIpLCAxNikgPDwgKDI0IC0gKGkgJSA4KSAqIDQpO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgcmV0dXJuIG5ldyBXb3JkQXJyYXkuaW5pdCh3b3JkcywgaGV4U3RyTGVuZ3RoIC8gMik7XG5cdCAgICAgICAgfVxuXHQgICAgfTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBMYXRpbjEgZW5jb2Rpbmcgc3RyYXRlZ3kuXG5cdCAgICAgKi9cblx0ICAgIHZhciBMYXRpbjEgPSBDX2VuYy5MYXRpbjEgPSB7XG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQ29udmVydHMgYSB3b3JkIGFycmF5IHRvIGEgTGF0aW4xIHN0cmluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7V29yZEFycmF5fSB3b3JkQXJyYXkgVGhlIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtzdHJpbmd9IFRoZSBMYXRpbjEgc3RyaW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHN0YXRpY1xuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgbGF0aW4xU3RyaW5nID0gQ3J5cHRvSlMuZW5jLkxhdGluMS5zdHJpbmdpZnkod29yZEFycmF5KTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBzdHJpbmdpZnk6IGZ1bmN0aW9uICh3b3JkQXJyYXkpIHtcblx0ICAgICAgICAgICAgLy8gU2hvcnRjdXRzXG5cdCAgICAgICAgICAgIHZhciB3b3JkcyA9IHdvcmRBcnJheS53b3Jkcztcblx0ICAgICAgICAgICAgdmFyIHNpZ0J5dGVzID0gd29yZEFycmF5LnNpZ0J5dGVzO1xuXG5cdCAgICAgICAgICAgIC8vIENvbnZlcnRcblx0ICAgICAgICAgICAgdmFyIGxhdGluMUNoYXJzID0gW107XG5cdCAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgc2lnQnl0ZXM7IGkrKykge1xuXHQgICAgICAgICAgICAgICAgdmFyIGJpdGUgPSAod29yZHNbaSA+Pj4gMl0gPj4+ICgyNCAtIChpICUgNCkgKiA4KSkgJiAweGZmO1xuXHQgICAgICAgICAgICAgICAgbGF0aW4xQ2hhcnMucHVzaChTdHJpbmcuZnJvbUNoYXJDb2RlKGJpdGUpKTtcblx0ICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgIHJldHVybiBsYXRpbjFDaGFycy5qb2luKCcnKTtcblx0ICAgICAgICB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQ29udmVydHMgYSBMYXRpbjEgc3RyaW5nIHRvIGEgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBsYXRpbjFTdHIgVGhlIExhdGluMSBzdHJpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtXb3JkQXJyYXl9IFRoZSB3b3JkIGFycmF5LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHN0YXRpY1xuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgd29yZEFycmF5ID0gQ3J5cHRvSlMuZW5jLkxhdGluMS5wYXJzZShsYXRpbjFTdHJpbmcpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIHBhcnNlOiBmdW5jdGlvbiAobGF0aW4xU3RyKSB7XG5cdCAgICAgICAgICAgIC8vIFNob3J0Y3V0XG5cdCAgICAgICAgICAgIHZhciBsYXRpbjFTdHJMZW5ndGggPSBsYXRpbjFTdHIubGVuZ3RoO1xuXG5cdCAgICAgICAgICAgIC8vIENvbnZlcnRcblx0ICAgICAgICAgICAgdmFyIHdvcmRzID0gW107XG5cdCAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGF0aW4xU3RyTGVuZ3RoOyBpKyspIHtcblx0ICAgICAgICAgICAgICAgIHdvcmRzW2kgPj4+IDJdIHw9IChsYXRpbjFTdHIuY2hhckNvZGVBdChpKSAmIDB4ZmYpIDw8ICgyNCAtIChpICUgNCkgKiA4KTtcblx0ICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgIHJldHVybiBuZXcgV29yZEFycmF5LmluaXQod29yZHMsIGxhdGluMVN0ckxlbmd0aCk7XG5cdCAgICAgICAgfVxuXHQgICAgfTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBVVEYtOCBlbmNvZGluZyBzdHJhdGVneS5cblx0ICAgICAqL1xuXHQgICAgdmFyIFV0ZjggPSBDX2VuYy5VdGY4ID0ge1xuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENvbnZlcnRzIGEgd29yZCBhcnJheSB0byBhIFVURi04IHN0cmluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7V29yZEFycmF5fSB3b3JkQXJyYXkgVGhlIHdvcmQgYXJyYXkuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtzdHJpbmd9IFRoZSBVVEYtOCBzdHJpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAc3RhdGljXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciB1dGY4U3RyaW5nID0gQ3J5cHRvSlMuZW5jLlV0Zjguc3RyaW5naWZ5KHdvcmRBcnJheSk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgc3RyaW5naWZ5OiBmdW5jdGlvbiAod29yZEFycmF5KSB7XG5cdCAgICAgICAgICAgIHRyeSB7XG5cdCAgICAgICAgICAgICAgICByZXR1cm4gZGVjb2RlVVJJQ29tcG9uZW50KGVzY2FwZShMYXRpbjEuc3RyaW5naWZ5KHdvcmRBcnJheSkpKTtcblx0ICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuXHQgICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNYWxmb3JtZWQgVVRGLTggZGF0YScpO1xuXHQgICAgICAgICAgICB9XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENvbnZlcnRzIGEgVVRGLTggc3RyaW5nIHRvIGEgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSB1dGY4U3RyIFRoZSBVVEYtOCBzdHJpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtXb3JkQXJyYXl9IFRoZSB3b3JkIGFycmF5LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHN0YXRpY1xuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgd29yZEFycmF5ID0gQ3J5cHRvSlMuZW5jLlV0ZjgucGFyc2UodXRmOFN0cmluZyk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgcGFyc2U6IGZ1bmN0aW9uICh1dGY4U3RyKSB7XG5cdCAgICAgICAgICAgIHJldHVybiBMYXRpbjEucGFyc2UodW5lc2NhcGUoZW5jb2RlVVJJQ29tcG9uZW50KHV0ZjhTdHIpKSk7XG5cdCAgICAgICAgfVxuXHQgICAgfTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBBYnN0cmFjdCBidWZmZXJlZCBibG9jayBhbGdvcml0aG0gdGVtcGxhdGUuXG5cdCAgICAgKlxuXHQgICAgICogVGhlIHByb3BlcnR5IGJsb2NrU2l6ZSBtdXN0IGJlIGltcGxlbWVudGVkIGluIGEgY29uY3JldGUgc3VidHlwZS5cblx0ICAgICAqXG5cdCAgICAgKiBAcHJvcGVydHkge251bWJlcn0gX21pbkJ1ZmZlclNpemUgVGhlIG51bWJlciBvZiBibG9ja3MgdGhhdCBzaG91bGQgYmUga2VwdCB1bnByb2Nlc3NlZCBpbiB0aGUgYnVmZmVyLiBEZWZhdWx0OiAwXG5cdCAgICAgKi9cblx0ICAgIHZhciBCdWZmZXJlZEJsb2NrQWxnb3JpdGhtID0gQ19saWIuQnVmZmVyZWRCbG9ja0FsZ29yaXRobSA9IEJhc2UuZXh0ZW5kKHtcblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBSZXNldHMgdGhpcyBibG9jayBhbGdvcml0aG0ncyBkYXRhIGJ1ZmZlciB0byBpdHMgaW5pdGlhbCBzdGF0ZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgYnVmZmVyZWRCbG9ja0FsZ29yaXRobS5yZXNldCgpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIHJlc2V0OiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgIC8vIEluaXRpYWwgdmFsdWVzXG5cdCAgICAgICAgICAgIHRoaXMuX2RhdGEgPSBuZXcgV29yZEFycmF5LmluaXQoKTtcblx0ICAgICAgICAgICAgdGhpcy5fbkRhdGFCeXRlcyA9IDA7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIEFkZHMgbmV3IGRhdGEgdG8gdGhpcyBibG9jayBhbGdvcml0aG0ncyBidWZmZXIuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge1dvcmRBcnJheXxzdHJpbmd9IGRhdGEgVGhlIGRhdGEgdG8gYXBwZW5kLiBTdHJpbmdzIGFyZSBjb252ZXJ0ZWQgdG8gYSBXb3JkQXJyYXkgdXNpbmcgVVRGLTguXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIGJ1ZmZlcmVkQmxvY2tBbGdvcml0aG0uX2FwcGVuZCgnZGF0YScpO1xuXHQgICAgICAgICAqICAgICBidWZmZXJlZEJsb2NrQWxnb3JpdGhtLl9hcHBlbmQod29yZEFycmF5KTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBfYXBwZW5kOiBmdW5jdGlvbiAoZGF0YSkge1xuXHQgICAgICAgICAgICAvLyBDb252ZXJ0IHN0cmluZyB0byBXb3JkQXJyYXksIGVsc2UgYXNzdW1lIFdvcmRBcnJheSBhbHJlYWR5XG5cdCAgICAgICAgICAgIGlmICh0eXBlb2YgZGF0YSA9PSAnc3RyaW5nJykge1xuXHQgICAgICAgICAgICAgICAgZGF0YSA9IFV0ZjgucGFyc2UoZGF0YSk7XG5cdCAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICAvLyBBcHBlbmRcblx0ICAgICAgICAgICAgdGhpcy5fZGF0YS5jb25jYXQoZGF0YSk7XG5cdCAgICAgICAgICAgIHRoaXMuX25EYXRhQnl0ZXMgKz0gZGF0YS5zaWdCeXRlcztcblx0ICAgICAgICB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogUHJvY2Vzc2VzIGF2YWlsYWJsZSBkYXRhIGJsb2Nrcy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIFRoaXMgbWV0aG9kIGludm9rZXMgX2RvUHJvY2Vzc0Jsb2NrKG9mZnNldCksIHdoaWNoIG11c3QgYmUgaW1wbGVtZW50ZWQgYnkgYSBjb25jcmV0ZSBzdWJ0eXBlLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtib29sZWFufSBkb0ZsdXNoIFdoZXRoZXIgYWxsIGJsb2NrcyBhbmQgcGFydGlhbCBibG9ja3Mgc2hvdWxkIGJlIHByb2Nlc3NlZC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1dvcmRBcnJheX0gVGhlIHByb2Nlc3NlZCBkYXRhLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgcHJvY2Vzc2VkRGF0YSA9IGJ1ZmZlcmVkQmxvY2tBbGdvcml0aG0uX3Byb2Nlc3MoKTtcblx0ICAgICAgICAgKiAgICAgdmFyIHByb2Nlc3NlZERhdGEgPSBidWZmZXJlZEJsb2NrQWxnb3JpdGhtLl9wcm9jZXNzKCEhJ2ZsdXNoJyk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgX3Byb2Nlc3M6IGZ1bmN0aW9uIChkb0ZsdXNoKSB7XG5cdCAgICAgICAgICAgIC8vIFNob3J0Y3V0c1xuXHQgICAgICAgICAgICB2YXIgZGF0YSA9IHRoaXMuX2RhdGE7XG5cdCAgICAgICAgICAgIHZhciBkYXRhV29yZHMgPSBkYXRhLndvcmRzO1xuXHQgICAgICAgICAgICB2YXIgZGF0YVNpZ0J5dGVzID0gZGF0YS5zaWdCeXRlcztcblx0ICAgICAgICAgICAgdmFyIGJsb2NrU2l6ZSA9IHRoaXMuYmxvY2tTaXplO1xuXHQgICAgICAgICAgICB2YXIgYmxvY2tTaXplQnl0ZXMgPSBibG9ja1NpemUgKiA0O1xuXG5cdCAgICAgICAgICAgIC8vIENvdW50IGJsb2NrcyByZWFkeVxuXHQgICAgICAgICAgICB2YXIgbkJsb2Nrc1JlYWR5ID0gZGF0YVNpZ0J5dGVzIC8gYmxvY2tTaXplQnl0ZXM7XG5cdCAgICAgICAgICAgIGlmIChkb0ZsdXNoKSB7XG5cdCAgICAgICAgICAgICAgICAvLyBSb3VuZCB1cCB0byBpbmNsdWRlIHBhcnRpYWwgYmxvY2tzXG5cdCAgICAgICAgICAgICAgICBuQmxvY2tzUmVhZHkgPSBNYXRoLmNlaWwobkJsb2Nrc1JlYWR5KTtcblx0ICAgICAgICAgICAgfSBlbHNlIHtcblx0ICAgICAgICAgICAgICAgIC8vIFJvdW5kIGRvd24gdG8gaW5jbHVkZSBvbmx5IGZ1bGwgYmxvY2tzLFxuXHQgICAgICAgICAgICAgICAgLy8gbGVzcyB0aGUgbnVtYmVyIG9mIGJsb2NrcyB0aGF0IG11c3QgcmVtYWluIGluIHRoZSBidWZmZXJcblx0ICAgICAgICAgICAgICAgIG5CbG9ja3NSZWFkeSA9IE1hdGgubWF4KChuQmxvY2tzUmVhZHkgfCAwKSAtIHRoaXMuX21pbkJ1ZmZlclNpemUsIDApO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgLy8gQ291bnQgd29yZHMgcmVhZHlcblx0ICAgICAgICAgICAgdmFyIG5Xb3Jkc1JlYWR5ID0gbkJsb2Nrc1JlYWR5ICogYmxvY2tTaXplO1xuXG5cdCAgICAgICAgICAgIC8vIENvdW50IGJ5dGVzIHJlYWR5XG5cdCAgICAgICAgICAgIHZhciBuQnl0ZXNSZWFkeSA9IE1hdGgubWluKG5Xb3Jkc1JlYWR5ICogNCwgZGF0YVNpZ0J5dGVzKTtcblxuXHQgICAgICAgICAgICAvLyBQcm9jZXNzIGJsb2Nrc1xuXHQgICAgICAgICAgICBpZiAobldvcmRzUmVhZHkpIHtcblx0ICAgICAgICAgICAgICAgIGZvciAodmFyIG9mZnNldCA9IDA7IG9mZnNldCA8IG5Xb3Jkc1JlYWR5OyBvZmZzZXQgKz0gYmxvY2tTaXplKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgLy8gUGVyZm9ybSBjb25jcmV0ZS1hbGdvcml0aG0gbG9naWNcblx0ICAgICAgICAgICAgICAgICAgICB0aGlzLl9kb1Byb2Nlc3NCbG9jayhkYXRhV29yZHMsIG9mZnNldCk7XG5cdCAgICAgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgICAgIC8vIFJlbW92ZSBwcm9jZXNzZWQgd29yZHNcblx0ICAgICAgICAgICAgICAgIHZhciBwcm9jZXNzZWRXb3JkcyA9IGRhdGFXb3Jkcy5zcGxpY2UoMCwgbldvcmRzUmVhZHkpO1xuXHQgICAgICAgICAgICAgICAgZGF0YS5zaWdCeXRlcyAtPSBuQnl0ZXNSZWFkeTtcblx0ICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgIC8vIFJldHVybiBwcm9jZXNzZWQgd29yZHNcblx0ICAgICAgICAgICAgcmV0dXJuIG5ldyBXb3JkQXJyYXkuaW5pdChwcm9jZXNzZWRXb3JkcywgbkJ5dGVzUmVhZHkpO1xuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDcmVhdGVzIGEgY29weSBvZiB0aGlzIG9iamVjdC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge09iamVjdH0gVGhlIGNsb25lLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgY2xvbmUgPSBidWZmZXJlZEJsb2NrQWxnb3JpdGhtLmNsb25lKCk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgY2xvbmU6IGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgdmFyIGNsb25lID0gQmFzZS5jbG9uZS5jYWxsKHRoaXMpO1xuXHQgICAgICAgICAgICBjbG9uZS5fZGF0YSA9IHRoaXMuX2RhdGEuY2xvbmUoKTtcblxuXHQgICAgICAgICAgICByZXR1cm4gY2xvbmU7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIF9taW5CdWZmZXJTaXplOiAwXG5cdCAgICB9KTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBBYnN0cmFjdCBoYXNoZXIgdGVtcGxhdGUuXG5cdCAgICAgKlxuXHQgICAgICogQHByb3BlcnR5IHtudW1iZXJ9IGJsb2NrU2l6ZSBUaGUgbnVtYmVyIG9mIDMyLWJpdCB3b3JkcyB0aGlzIGhhc2hlciBvcGVyYXRlcyBvbi4gRGVmYXVsdDogMTYgKDUxMiBiaXRzKVxuXHQgICAgICovXG5cdCAgICB2YXIgSGFzaGVyID0gQ19saWIuSGFzaGVyID0gQnVmZmVyZWRCbG9ja0FsZ29yaXRobS5leHRlbmQoe1xuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENvbmZpZ3VyYXRpb24gb3B0aW9ucy5cblx0ICAgICAgICAgKi9cblx0ICAgICAgICBjZmc6IEJhc2UuZXh0ZW5kKCksXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBJbml0aWFsaXplcyBhIG5ld2x5IGNyZWF0ZWQgaGFzaGVyLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtPYmplY3R9IGNmZyAoT3B0aW9uYWwpIFRoZSBjb25maWd1cmF0aW9uIG9wdGlvbnMgdG8gdXNlIGZvciB0aGlzIGhhc2ggY29tcHV0YXRpb24uXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciBoYXNoZXIgPSBDcnlwdG9KUy5hbGdvLlNIQTI1Ni5jcmVhdGUoKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBpbml0OiBmdW5jdGlvbiAoY2ZnKSB7XG5cdCAgICAgICAgICAgIC8vIEFwcGx5IGNvbmZpZyBkZWZhdWx0c1xuXHQgICAgICAgICAgICB0aGlzLmNmZyA9IHRoaXMuY2ZnLmV4dGVuZChjZmcpO1xuXG5cdCAgICAgICAgICAgIC8vIFNldCBpbml0aWFsIHZhbHVlc1xuXHQgICAgICAgICAgICB0aGlzLnJlc2V0KCk7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIFJlc2V0cyB0aGlzIGhhc2hlciB0byBpdHMgaW5pdGlhbCBzdGF0ZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgaGFzaGVyLnJlc2V0KCk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgcmVzZXQ6IGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgLy8gUmVzZXQgZGF0YSBidWZmZXJcblx0ICAgICAgICAgICAgQnVmZmVyZWRCbG9ja0FsZ29yaXRobS5yZXNldC5jYWxsKHRoaXMpO1xuXG5cdCAgICAgICAgICAgIC8vIFBlcmZvcm0gY29uY3JldGUtaGFzaGVyIGxvZ2ljXG5cdCAgICAgICAgICAgIHRoaXMuX2RvUmVzZXQoKTtcblx0ICAgICAgICB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogVXBkYXRlcyB0aGlzIGhhc2hlciB3aXRoIGEgbWVzc2FnZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7V29yZEFycmF5fHN0cmluZ30gbWVzc2FnZVVwZGF0ZSBUaGUgbWVzc2FnZSB0byBhcHBlbmQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtIYXNoZXJ9IFRoaXMgaGFzaGVyLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICBoYXNoZXIudXBkYXRlKCdtZXNzYWdlJyk7XG5cdCAgICAgICAgICogICAgIGhhc2hlci51cGRhdGUod29yZEFycmF5KTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICB1cGRhdGU6IGZ1bmN0aW9uIChtZXNzYWdlVXBkYXRlKSB7XG5cdCAgICAgICAgICAgIC8vIEFwcGVuZFxuXHQgICAgICAgICAgICB0aGlzLl9hcHBlbmQobWVzc2FnZVVwZGF0ZSk7XG5cblx0ICAgICAgICAgICAgLy8gVXBkYXRlIHRoZSBoYXNoXG5cdCAgICAgICAgICAgIHRoaXMuX3Byb2Nlc3MoKTtcblxuXHQgICAgICAgICAgICAvLyBDaGFpbmFibGVcblx0ICAgICAgICAgICAgcmV0dXJuIHRoaXM7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIEZpbmFsaXplcyB0aGUgaGFzaCBjb21wdXRhdGlvbi5cblx0ICAgICAgICAgKiBOb3RlIHRoYXQgdGhlIGZpbmFsaXplIG9wZXJhdGlvbiBpcyBlZmZlY3RpdmVseSBhIGRlc3RydWN0aXZlLCByZWFkLW9uY2Ugb3BlcmF0aW9uLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtXb3JkQXJyYXl8c3RyaW5nfSBtZXNzYWdlVXBkYXRlIChPcHRpb25hbCkgQSBmaW5hbCBtZXNzYWdlIHVwZGF0ZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1dvcmRBcnJheX0gVGhlIGhhc2guXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciBoYXNoID0gaGFzaGVyLmZpbmFsaXplKCk7XG5cdCAgICAgICAgICogICAgIHZhciBoYXNoID0gaGFzaGVyLmZpbmFsaXplKCdtZXNzYWdlJyk7XG5cdCAgICAgICAgICogICAgIHZhciBoYXNoID0gaGFzaGVyLmZpbmFsaXplKHdvcmRBcnJheSk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgZmluYWxpemU6IGZ1bmN0aW9uIChtZXNzYWdlVXBkYXRlKSB7XG5cdCAgICAgICAgICAgIC8vIEZpbmFsIG1lc3NhZ2UgdXBkYXRlXG5cdCAgICAgICAgICAgIGlmIChtZXNzYWdlVXBkYXRlKSB7XG5cdCAgICAgICAgICAgICAgICB0aGlzLl9hcHBlbmQobWVzc2FnZVVwZGF0ZSk7XG5cdCAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICAvLyBQZXJmb3JtIGNvbmNyZXRlLWhhc2hlciBsb2dpY1xuXHQgICAgICAgICAgICB2YXIgaGFzaCA9IHRoaXMuX2RvRmluYWxpemUoKTtcblxuXHQgICAgICAgICAgICByZXR1cm4gaGFzaDtcblx0ICAgICAgICB9LFxuXG5cdCAgICAgICAgYmxvY2tTaXplOiA1MTIvMzIsXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBDcmVhdGVzIGEgc2hvcnRjdXQgZnVuY3Rpb24gdG8gYSBoYXNoZXIncyBvYmplY3QgaW50ZXJmYWNlLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtIYXNoZXJ9IGhhc2hlciBUaGUgaGFzaGVyIHRvIGNyZWF0ZSBhIGhlbHBlciBmb3IuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtGdW5jdGlvbn0gVGhlIHNob3J0Y3V0IGZ1bmN0aW9uLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHN0YXRpY1xuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgU0hBMjU2ID0gQ3J5cHRvSlMubGliLkhhc2hlci5fY3JlYXRlSGVscGVyKENyeXB0b0pTLmFsZ28uU0hBMjU2KTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBfY3JlYXRlSGVscGVyOiBmdW5jdGlvbiAoaGFzaGVyKSB7XG5cdCAgICAgICAgICAgIHJldHVybiBmdW5jdGlvbiAobWVzc2FnZSwgY2ZnKSB7XG5cdCAgICAgICAgICAgICAgICByZXR1cm4gbmV3IGhhc2hlci5pbml0KGNmZykuZmluYWxpemUobWVzc2FnZSk7XG5cdCAgICAgICAgICAgIH07XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENyZWF0ZXMgYSBzaG9ydGN1dCBmdW5jdGlvbiB0byB0aGUgSE1BQydzIG9iamVjdCBpbnRlcmZhY2UuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge0hhc2hlcn0gaGFzaGVyIFRoZSBoYXNoZXIgdG8gdXNlIGluIHRoaXMgSE1BQyBoZWxwZXIuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtGdW5jdGlvbn0gVGhlIHNob3J0Y3V0IGZ1bmN0aW9uLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHN0YXRpY1xuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgSG1hY1NIQTI1NiA9IENyeXB0b0pTLmxpYi5IYXNoZXIuX2NyZWF0ZUhtYWNIZWxwZXIoQ3J5cHRvSlMuYWxnby5TSEEyNTYpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIF9jcmVhdGVIbWFjSGVscGVyOiBmdW5jdGlvbiAoaGFzaGVyKSB7XG5cdCAgICAgICAgICAgIHJldHVybiBmdW5jdGlvbiAobWVzc2FnZSwga2V5KSB7XG5cdCAgICAgICAgICAgICAgICByZXR1cm4gbmV3IENfYWxnby5ITUFDLmluaXQoaGFzaGVyLCBrZXkpLmZpbmFsaXplKG1lc3NhZ2UpO1xuXHQgICAgICAgICAgICB9O1xuXHQgICAgICAgIH1cblx0ICAgIH0pO1xuXG5cdCAgICAvKipcblx0ICAgICAqIEFsZ29yaXRobSBuYW1lc3BhY2UuXG5cdCAgICAgKi9cblx0ICAgIHZhciBDX2FsZ28gPSBDLmFsZ28gPSB7fTtcblxuXHQgICAgcmV0dXJuIEM7XG5cdH0oTWF0aCkpO1xuXG5cblx0cmV0dXJuIENyeXB0b0pTO1xuXG59KSk7IiwiOyhmdW5jdGlvbiAocm9vdCwgZmFjdG9yeSwgdW5kZWYpIHtcblx0aWYgKHR5cGVvZiBleHBvcnRzID09PSBcIm9iamVjdFwiKSB7XG5cdFx0Ly8gQ29tbW9uSlNcblx0XHRtb2R1bGUuZXhwb3J0cyA9IGV4cG9ydHMgPSBmYWN0b3J5KHJlcXVpcmUoXCIuL2NvcmVcIiksIHJlcXVpcmUoXCIuL3g2NC1jb3JlXCIpKTtcblx0fVxuXHRlbHNlIGlmICh0eXBlb2YgZGVmaW5lID09PSBcImZ1bmN0aW9uXCIgJiYgZGVmaW5lLmFtZCkge1xuXHRcdC8vIEFNRFxuXHRcdGRlZmluZShbXCIuL2NvcmVcIiwgXCIuL3g2NC1jb3JlXCJdLCBmYWN0b3J5KTtcblx0fVxuXHRlbHNlIHtcblx0XHQvLyBHbG9iYWwgKGJyb3dzZXIpXG5cdFx0ZmFjdG9yeShyb290LkNyeXB0b0pTKTtcblx0fVxufSh0aGlzLCBmdW5jdGlvbiAoQ3J5cHRvSlMpIHtcblxuXHQoZnVuY3Rpb24gKE1hdGgpIHtcblx0ICAgIC8vIFNob3J0Y3V0c1xuXHQgICAgdmFyIEMgPSBDcnlwdG9KUztcblx0ICAgIHZhciBDX2xpYiA9IEMubGliO1xuXHQgICAgdmFyIFdvcmRBcnJheSA9IENfbGliLldvcmRBcnJheTtcblx0ICAgIHZhciBIYXNoZXIgPSBDX2xpYi5IYXNoZXI7XG5cdCAgICB2YXIgQ194NjQgPSBDLng2NDtcblx0ICAgIHZhciBYNjRXb3JkID0gQ194NjQuV29yZDtcblx0ICAgIHZhciBDX2FsZ28gPSBDLmFsZ287XG5cblx0ICAgIC8vIENvbnN0YW50cyB0YWJsZXNcblx0ICAgIHZhciBSSE9fT0ZGU0VUUyA9IFtdO1xuXHQgICAgdmFyIFBJX0lOREVYRVMgID0gW107XG5cdCAgICB2YXIgUk9VTkRfQ09OU1RBTlRTID0gW107XG5cblx0ICAgIC8vIENvbXB1dGUgQ29uc3RhbnRzXG5cdCAgICAoZnVuY3Rpb24gKCkge1xuXHQgICAgICAgIC8vIENvbXB1dGUgcmhvIG9mZnNldCBjb25zdGFudHNcblx0ICAgICAgICB2YXIgeCA9IDEsIHkgPSAwO1xuXHQgICAgICAgIGZvciAodmFyIHQgPSAwOyB0IDwgMjQ7IHQrKykge1xuXHQgICAgICAgICAgICBSSE9fT0ZGU0VUU1t4ICsgNSAqIHldID0gKCh0ICsgMSkgKiAodCArIDIpIC8gMikgJSA2NDtcblxuXHQgICAgICAgICAgICB2YXIgbmV3WCA9IHkgJSA1O1xuXHQgICAgICAgICAgICB2YXIgbmV3WSA9ICgyICogeCArIDMgKiB5KSAlIDU7XG5cdCAgICAgICAgICAgIHggPSBuZXdYO1xuXHQgICAgICAgICAgICB5ID0gbmV3WTtcblx0ICAgICAgICB9XG5cblx0ICAgICAgICAvLyBDb21wdXRlIHBpIGluZGV4IGNvbnN0YW50c1xuXHQgICAgICAgIGZvciAodmFyIHggPSAwOyB4IDwgNTsgeCsrKSB7XG5cdCAgICAgICAgICAgIGZvciAodmFyIHkgPSAwOyB5IDwgNTsgeSsrKSB7XG5cdCAgICAgICAgICAgICAgICBQSV9JTkRFWEVTW3ggKyA1ICogeV0gPSB5ICsgKCgyICogeCArIDMgKiB5KSAlIDUpICogNTtcblx0ICAgICAgICAgICAgfVxuXHQgICAgICAgIH1cblxuXHQgICAgICAgIC8vIENvbXB1dGUgcm91bmQgY29uc3RhbnRzXG5cdCAgICAgICAgdmFyIExGU1IgPSAweDAxO1xuXHQgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgMjQ7IGkrKykge1xuXHQgICAgICAgICAgICB2YXIgcm91bmRDb25zdGFudE1zdyA9IDA7XG5cdCAgICAgICAgICAgIHZhciByb3VuZENvbnN0YW50THN3ID0gMDtcblxuXHQgICAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IDc7IGorKykge1xuXHQgICAgICAgICAgICAgICAgaWYgKExGU1IgJiAweDAxKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgdmFyIGJpdFBvc2l0aW9uID0gKDEgPDwgaikgLSAxO1xuXHQgICAgICAgICAgICAgICAgICAgIGlmIChiaXRQb3NpdGlvbiA8IDMyKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kQ29uc3RhbnRMc3cgXj0gMSA8PCBiaXRQb3NpdGlvbjtcblx0ICAgICAgICAgICAgICAgICAgICB9IGVsc2UgLyogaWYgKGJpdFBvc2l0aW9uID49IDMyKSAqLyB7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kQ29uc3RhbnRNc3cgXj0gMSA8PCAoYml0UG9zaXRpb24gLSAzMik7XG5cdCAgICAgICAgICAgICAgICAgICAgfVxuXHQgICAgICAgICAgICAgICAgfVxuXG5cdCAgICAgICAgICAgICAgICAvLyBDb21wdXRlIG5leHQgTEZTUlxuXHQgICAgICAgICAgICAgICAgaWYgKExGU1IgJiAweDgwKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgLy8gUHJpbWl0aXZlIHBvbHlub21pYWwgb3ZlciBHRigyKTogeF44ICsgeF42ICsgeF41ICsgeF40ICsgMVxuXHQgICAgICAgICAgICAgICAgICAgIExGU1IgPSAoTEZTUiA8PCAxKSBeIDB4NzE7XG5cdCAgICAgICAgICAgICAgICB9IGVsc2Uge1xuXHQgICAgICAgICAgICAgICAgICAgIExGU1IgPDw9IDE7XG5cdCAgICAgICAgICAgICAgICB9XG5cdCAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICBST1VORF9DT05TVEFOVFNbaV0gPSBYNjRXb3JkLmNyZWF0ZShyb3VuZENvbnN0YW50TXN3LCByb3VuZENvbnN0YW50THN3KTtcblx0ICAgICAgICB9XG5cdCAgICB9KCkpO1xuXG5cdCAgICAvLyBSZXVzYWJsZSBvYmplY3RzIGZvciB0ZW1wb3JhcnkgdmFsdWVzXG5cdCAgICB2YXIgVCA9IFtdO1xuXHQgICAgKGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IDI1OyBpKyspIHtcblx0ICAgICAgICAgICAgVFtpXSA9IFg2NFdvcmQuY3JlYXRlKCk7XG5cdCAgICAgICAgfVxuXHQgICAgfSgpKTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBTSEEtMyBoYXNoIGFsZ29yaXRobS5cblx0ICAgICAqL1xuXHQgICAgdmFyIFNIQTMgPSBDX2FsZ28uU0hBMyA9IEhhc2hlci5leHRlbmQoe1xuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENvbmZpZ3VyYXRpb24gb3B0aW9ucy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBvdXRwdXRMZW5ndGhcblx0ICAgICAgICAgKiAgIFRoZSBkZXNpcmVkIG51bWJlciBvZiBiaXRzIGluIHRoZSBvdXRwdXQgaGFzaC5cblx0ICAgICAgICAgKiAgIE9ubHkgdmFsdWVzIHBlcm1pdHRlZCBhcmU6IDIyNCwgMjU2LCAzODQsIDUxMi5cblx0ICAgICAgICAgKiAgIERlZmF1bHQ6IDUxMlxuXHQgICAgICAgICAqL1xuXHQgICAgICAgIGNmZzogSGFzaGVyLmNmZy5leHRlbmQoe1xuXHQgICAgICAgICAgICBvdXRwdXRMZW5ndGg6IDUxMlxuXHQgICAgICAgIH0pLFxuXG5cdCAgICAgICAgX2RvUmVzZXQ6IGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgdmFyIHN0YXRlID0gdGhpcy5fc3RhdGUgPSBbXVxuXHQgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IDI1OyBpKyspIHtcblx0ICAgICAgICAgICAgICAgIHN0YXRlW2ldID0gbmV3IFg2NFdvcmQuaW5pdCgpO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgdGhpcy5ibG9ja1NpemUgPSAoMTYwMCAtIDIgKiB0aGlzLmNmZy5vdXRwdXRMZW5ndGgpIC8gMzI7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIF9kb1Byb2Nlc3NCbG9jazogZnVuY3Rpb24gKE0sIG9mZnNldCkge1xuXHQgICAgICAgICAgICAvLyBTaG9ydGN1dHNcblx0ICAgICAgICAgICAgdmFyIHN0YXRlID0gdGhpcy5fc3RhdGU7XG5cdCAgICAgICAgICAgIHZhciBuQmxvY2tTaXplTGFuZXMgPSB0aGlzLmJsb2NrU2l6ZSAvIDI7XG5cblx0ICAgICAgICAgICAgLy8gQWJzb3JiXG5cdCAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbkJsb2NrU2l6ZUxhbmVzOyBpKyspIHtcblx0ICAgICAgICAgICAgICAgIC8vIFNob3J0Y3V0c1xuXHQgICAgICAgICAgICAgICAgdmFyIE0yaSAgPSBNW29mZnNldCArIDIgKiBpXTtcblx0ICAgICAgICAgICAgICAgIHZhciBNMmkxID0gTVtvZmZzZXQgKyAyICogaSArIDFdO1xuXG5cdCAgICAgICAgICAgICAgICAvLyBTd2FwIGVuZGlhblxuXHQgICAgICAgICAgICAgICAgTTJpID0gKFxuXHQgICAgICAgICAgICAgICAgICAgICgoKE0yaSA8PCA4KSAgfCAoTTJpID4+PiAyNCkpICYgMHgwMGZmMDBmZikgfFxuXHQgICAgICAgICAgICAgICAgICAgICgoKE0yaSA8PCAyNCkgfCAoTTJpID4+PiA4KSkgICYgMHhmZjAwZmYwMClcblx0ICAgICAgICAgICAgICAgICk7XG5cdCAgICAgICAgICAgICAgICBNMmkxID0gKFxuXHQgICAgICAgICAgICAgICAgICAgICgoKE0yaTEgPDwgOCkgIHwgKE0yaTEgPj4+IDI0KSkgJiAweDAwZmYwMGZmKSB8XG5cdCAgICAgICAgICAgICAgICAgICAgKCgoTTJpMSA8PCAyNCkgfCAoTTJpMSA+Pj4gOCkpICAmIDB4ZmYwMGZmMDApXG5cdCAgICAgICAgICAgICAgICApO1xuXG5cdCAgICAgICAgICAgICAgICAvLyBBYnNvcmIgbWVzc2FnZSBpbnRvIHN0YXRlXG5cdCAgICAgICAgICAgICAgICB2YXIgbGFuZSA9IHN0YXRlW2ldO1xuXHQgICAgICAgICAgICAgICAgbGFuZS5oaWdoIF49IE0yaTE7XG5cdCAgICAgICAgICAgICAgICBsYW5lLmxvdyAgXj0gTTJpO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgLy8gUm91bmRzXG5cdCAgICAgICAgICAgIGZvciAodmFyIHJvdW5kID0gMDsgcm91bmQgPCAyNDsgcm91bmQrKykge1xuXHQgICAgICAgICAgICAgICAgLy8gVGhldGFcblx0ICAgICAgICAgICAgICAgIGZvciAodmFyIHggPSAwOyB4IDwgNTsgeCsrKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgLy8gTWl4IGNvbHVtbiBsYW5lc1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciB0TXN3ID0gMCwgdExzdyA9IDA7XG5cdCAgICAgICAgICAgICAgICAgICAgZm9yICh2YXIgeSA9IDA7IHkgPCA1OyB5KyspIHtcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGxhbmUgPSBzdGF0ZVt4ICsgNSAqIHldO1xuXHQgICAgICAgICAgICAgICAgICAgICAgICB0TXN3IF49IGxhbmUuaGlnaDtcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdExzdyBePSBsYW5lLmxvdztcblx0ICAgICAgICAgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgICAgICAgICAvLyBUZW1wb3JhcnkgdmFsdWVzXG5cdCAgICAgICAgICAgICAgICAgICAgdmFyIFR4ID0gVFt4XTtcblx0ICAgICAgICAgICAgICAgICAgICBUeC5oaWdoID0gdE1zdztcblx0ICAgICAgICAgICAgICAgICAgICBUeC5sb3cgID0gdExzdztcblx0ICAgICAgICAgICAgICAgIH1cblx0ICAgICAgICAgICAgICAgIGZvciAodmFyIHggPSAwOyB4IDwgNTsgeCsrKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgLy8gU2hvcnRjdXRzXG5cdCAgICAgICAgICAgICAgICAgICAgdmFyIFR4NCA9IFRbKHggKyA0KSAlIDVdO1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciBUeDEgPSBUWyh4ICsgMSkgJSA1XTtcblx0ICAgICAgICAgICAgICAgICAgICB2YXIgVHgxTXN3ID0gVHgxLmhpZ2g7XG5cdCAgICAgICAgICAgICAgICAgICAgdmFyIFR4MUxzdyA9IFR4MS5sb3c7XG5cblx0ICAgICAgICAgICAgICAgICAgICAvLyBNaXggc3Vycm91bmRpbmcgY29sdW1uc1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciB0TXN3ID0gVHg0LmhpZ2ggXiAoKFR4MU1zdyA8PCAxKSB8IChUeDFMc3cgPj4+IDMxKSk7XG5cdCAgICAgICAgICAgICAgICAgICAgdmFyIHRMc3cgPSBUeDQubG93ICBeICgoVHgxTHN3IDw8IDEpIHwgKFR4MU1zdyA+Pj4gMzEpKTtcblx0ICAgICAgICAgICAgICAgICAgICBmb3IgKHZhciB5ID0gMDsgeSA8IDU7IHkrKykge1xuXHQgICAgICAgICAgICAgICAgICAgICAgICB2YXIgbGFuZSA9IHN0YXRlW3ggKyA1ICogeV07XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIGxhbmUuaGlnaCBePSB0TXN3O1xuXHQgICAgICAgICAgICAgICAgICAgICAgICBsYW5lLmxvdyAgXj0gdExzdztcblx0ICAgICAgICAgICAgICAgICAgICB9XG5cdCAgICAgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgICAgIC8vIFJobyBQaVxuXHQgICAgICAgICAgICAgICAgZm9yICh2YXIgbGFuZUluZGV4ID0gMTsgbGFuZUluZGV4IDwgMjU7IGxhbmVJbmRleCsrKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgLy8gU2hvcnRjdXRzXG5cdCAgICAgICAgICAgICAgICAgICAgdmFyIGxhbmUgPSBzdGF0ZVtsYW5lSW5kZXhdO1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciBsYW5lTXN3ID0gbGFuZS5oaWdoO1xuXHQgICAgICAgICAgICAgICAgICAgIHZhciBsYW5lTHN3ID0gbGFuZS5sb3c7XG5cdCAgICAgICAgICAgICAgICAgICAgdmFyIHJob09mZnNldCA9IFJIT19PRkZTRVRTW2xhbmVJbmRleF07XG5cblx0ICAgICAgICAgICAgICAgICAgICAvLyBSb3RhdGUgbGFuZXNcblx0ICAgICAgICAgICAgICAgICAgICBpZiAocmhvT2Zmc2V0IDwgMzIpIHtcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHRNc3cgPSAobGFuZU1zdyA8PCByaG9PZmZzZXQpIHwgKGxhbmVMc3cgPj4+ICgzMiAtIHJob09mZnNldCkpO1xuXHQgICAgICAgICAgICAgICAgICAgICAgICB2YXIgdExzdyA9IChsYW5lTHN3IDw8IHJob09mZnNldCkgfCAobGFuZU1zdyA+Pj4gKDMyIC0gcmhvT2Zmc2V0KSk7XG5cdCAgICAgICAgICAgICAgICAgICAgfSBlbHNlIC8qIGlmIChyaG9PZmZzZXQgPj0gMzIpICovIHtcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHRNc3cgPSAobGFuZUxzdyA8PCAocmhvT2Zmc2V0IC0gMzIpKSB8IChsYW5lTXN3ID4+PiAoNjQgLSByaG9PZmZzZXQpKTtcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHRMc3cgPSAobGFuZU1zdyA8PCAocmhvT2Zmc2V0IC0gMzIpKSB8IChsYW5lTHN3ID4+PiAoNjQgLSByaG9PZmZzZXQpKTtcblx0ICAgICAgICAgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgICAgICAgICAvLyBUcmFuc3Bvc2UgbGFuZXNcblx0ICAgICAgICAgICAgICAgICAgICB2YXIgVFBpTGFuZSA9IFRbUElfSU5ERVhFU1tsYW5lSW5kZXhdXTtcblx0ICAgICAgICAgICAgICAgICAgICBUUGlMYW5lLmhpZ2ggPSB0TXN3O1xuXHQgICAgICAgICAgICAgICAgICAgIFRQaUxhbmUubG93ICA9IHRMc3c7XG5cdCAgICAgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgICAgIC8vIFJobyBwaSBhdCB4ID0geSA9IDBcblx0ICAgICAgICAgICAgICAgIHZhciBUMCA9IFRbMF07XG5cdCAgICAgICAgICAgICAgICB2YXIgc3RhdGUwID0gc3RhdGVbMF07XG5cdCAgICAgICAgICAgICAgICBUMC5oaWdoID0gc3RhdGUwLmhpZ2g7XG5cdCAgICAgICAgICAgICAgICBUMC5sb3cgID0gc3RhdGUwLmxvdztcblxuXHQgICAgICAgICAgICAgICAgLy8gQ2hpXG5cdCAgICAgICAgICAgICAgICBmb3IgKHZhciB4ID0gMDsgeCA8IDU7IHgrKykge1xuXHQgICAgICAgICAgICAgICAgICAgIGZvciAodmFyIHkgPSAwOyB5IDwgNTsgeSsrKSB7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIC8vIFNob3J0Y3V0c1xuXHQgICAgICAgICAgICAgICAgICAgICAgICB2YXIgbGFuZUluZGV4ID0geCArIDUgKiB5O1xuXHQgICAgICAgICAgICAgICAgICAgICAgICB2YXIgbGFuZSA9IHN0YXRlW2xhbmVJbmRleF07XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHZhciBUTGFuZSA9IFRbbGFuZUluZGV4XTtcblx0ICAgICAgICAgICAgICAgICAgICAgICAgdmFyIFR4MUxhbmUgPSBUWygoeCArIDEpICUgNSkgKyA1ICogeV07XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIHZhciBUeDJMYW5lID0gVFsoKHggKyAyKSAlIDUpICsgNSAqIHldO1xuXG5cdCAgICAgICAgICAgICAgICAgICAgICAgIC8vIE1peCByb3dzXG5cdCAgICAgICAgICAgICAgICAgICAgICAgIGxhbmUuaGlnaCA9IFRMYW5lLmhpZ2ggXiAoflR4MUxhbmUuaGlnaCAmIFR4MkxhbmUuaGlnaCk7XG5cdCAgICAgICAgICAgICAgICAgICAgICAgIGxhbmUubG93ICA9IFRMYW5lLmxvdyAgXiAoflR4MUxhbmUubG93ICAmIFR4MkxhbmUubG93KTtcblx0ICAgICAgICAgICAgICAgICAgICB9XG5cdCAgICAgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgICAgIC8vIElvdGFcblx0ICAgICAgICAgICAgICAgIHZhciBsYW5lID0gc3RhdGVbMF07XG5cdCAgICAgICAgICAgICAgICB2YXIgcm91bmRDb25zdGFudCA9IFJPVU5EX0NPTlNUQU5UU1tyb3VuZF07XG5cdCAgICAgICAgICAgICAgICBsYW5lLmhpZ2ggXj0gcm91bmRDb25zdGFudC5oaWdoO1xuXHQgICAgICAgICAgICAgICAgbGFuZS5sb3cgIF49IHJvdW5kQ29uc3RhbnQubG93Oztcblx0ICAgICAgICAgICAgfVxuXHQgICAgICAgIH0sXG5cblx0ICAgICAgICBfZG9GaW5hbGl6ZTogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICAvLyBTaG9ydGN1dHNcblx0ICAgICAgICAgICAgdmFyIGRhdGEgPSB0aGlzLl9kYXRhO1xuXHQgICAgICAgICAgICB2YXIgZGF0YVdvcmRzID0gZGF0YS53b3Jkcztcblx0ICAgICAgICAgICAgdmFyIG5CaXRzVG90YWwgPSB0aGlzLl9uRGF0YUJ5dGVzICogODtcblx0ICAgICAgICAgICAgdmFyIG5CaXRzTGVmdCA9IGRhdGEuc2lnQnl0ZXMgKiA4O1xuXHQgICAgICAgICAgICB2YXIgYmxvY2tTaXplQml0cyA9IHRoaXMuYmxvY2tTaXplICogMzI7XG5cblx0ICAgICAgICAgICAgLy8gQWRkIHBhZGRpbmdcblx0ICAgICAgICAgICAgZGF0YVdvcmRzW25CaXRzTGVmdCA+Pj4gNV0gfD0gMHgxIDw8ICgyNCAtIG5CaXRzTGVmdCAlIDMyKTtcblx0ICAgICAgICAgICAgZGF0YVdvcmRzWygoTWF0aC5jZWlsKChuQml0c0xlZnQgKyAxKSAvIGJsb2NrU2l6ZUJpdHMpICogYmxvY2tTaXplQml0cykgPj4+IDUpIC0gMV0gfD0gMHg4MDtcblx0ICAgICAgICAgICAgZGF0YS5zaWdCeXRlcyA9IGRhdGFXb3Jkcy5sZW5ndGggKiA0O1xuXG5cdCAgICAgICAgICAgIC8vIEhhc2ggZmluYWwgYmxvY2tzXG5cdCAgICAgICAgICAgIHRoaXMuX3Byb2Nlc3MoKTtcblxuXHQgICAgICAgICAgICAvLyBTaG9ydGN1dHNcblx0ICAgICAgICAgICAgdmFyIHN0YXRlID0gdGhpcy5fc3RhdGU7XG5cdCAgICAgICAgICAgIHZhciBvdXRwdXRMZW5ndGhCeXRlcyA9IHRoaXMuY2ZnLm91dHB1dExlbmd0aCAvIDg7XG5cdCAgICAgICAgICAgIHZhciBvdXRwdXRMZW5ndGhMYW5lcyA9IG91dHB1dExlbmd0aEJ5dGVzIC8gODtcblxuXHQgICAgICAgICAgICAvLyBTcXVlZXplXG5cdCAgICAgICAgICAgIHZhciBoYXNoV29yZHMgPSBbXTtcblx0ICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBvdXRwdXRMZW5ndGhMYW5lczsgaSsrKSB7XG5cdCAgICAgICAgICAgICAgICAvLyBTaG9ydGN1dHNcblx0ICAgICAgICAgICAgICAgIHZhciBsYW5lID0gc3RhdGVbaV07XG5cdCAgICAgICAgICAgICAgICB2YXIgbGFuZU1zdyA9IGxhbmUuaGlnaDtcblx0ICAgICAgICAgICAgICAgIHZhciBsYW5lTHN3ID0gbGFuZS5sb3c7XG5cblx0ICAgICAgICAgICAgICAgIC8vIFN3YXAgZW5kaWFuXG5cdCAgICAgICAgICAgICAgICBsYW5lTXN3ID0gKFxuXHQgICAgICAgICAgICAgICAgICAgICgoKGxhbmVNc3cgPDwgOCkgIHwgKGxhbmVNc3cgPj4+IDI0KSkgJiAweDAwZmYwMGZmKSB8XG5cdCAgICAgICAgICAgICAgICAgICAgKCgobGFuZU1zdyA8PCAyNCkgfCAobGFuZU1zdyA+Pj4gOCkpICAmIDB4ZmYwMGZmMDApXG5cdCAgICAgICAgICAgICAgICApO1xuXHQgICAgICAgICAgICAgICAgbGFuZUxzdyA9IChcblx0ICAgICAgICAgICAgICAgICAgICAoKChsYW5lTHN3IDw8IDgpICB8IChsYW5lTHN3ID4+PiAyNCkpICYgMHgwMGZmMDBmZikgfFxuXHQgICAgICAgICAgICAgICAgICAgICgoKGxhbmVMc3cgPDwgMjQpIHwgKGxhbmVMc3cgPj4+IDgpKSAgJiAweGZmMDBmZjAwKVxuXHQgICAgICAgICAgICAgICAgKTtcblxuXHQgICAgICAgICAgICAgICAgLy8gU3F1ZWV6ZSBzdGF0ZSB0byByZXRyaWV2ZSBoYXNoXG5cdCAgICAgICAgICAgICAgICBoYXNoV29yZHMucHVzaChsYW5lTHN3KTtcblx0ICAgICAgICAgICAgICAgIGhhc2hXb3Jkcy5wdXNoKGxhbmVNc3cpO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgLy8gUmV0dXJuIGZpbmFsIGNvbXB1dGVkIGhhc2hcblx0ICAgICAgICAgICAgcmV0dXJuIG5ldyBXb3JkQXJyYXkuaW5pdChoYXNoV29yZHMsIG91dHB1dExlbmd0aEJ5dGVzKTtcblx0ICAgICAgICB9LFxuXG5cdCAgICAgICAgY2xvbmU6IGZ1bmN0aW9uICgpIHtcblx0ICAgICAgICAgICAgdmFyIGNsb25lID0gSGFzaGVyLmNsb25lLmNhbGwodGhpcyk7XG5cblx0ICAgICAgICAgICAgdmFyIHN0YXRlID0gY2xvbmUuX3N0YXRlID0gdGhpcy5fc3RhdGUuc2xpY2UoMCk7XG5cdCAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgMjU7IGkrKykge1xuXHQgICAgICAgICAgICAgICAgc3RhdGVbaV0gPSBzdGF0ZVtpXS5jbG9uZSgpO1xuXHQgICAgICAgICAgICB9XG5cblx0ICAgICAgICAgICAgcmV0dXJuIGNsb25lO1xuXHQgICAgICAgIH1cblx0ICAgIH0pO1xuXG5cdCAgICAvKipcblx0ICAgICAqIFNob3J0Y3V0IGZ1bmN0aW9uIHRvIHRoZSBoYXNoZXIncyBvYmplY3QgaW50ZXJmYWNlLlxuXHQgICAgICpcblx0ICAgICAqIEBwYXJhbSB7V29yZEFycmF5fHN0cmluZ30gbWVzc2FnZSBUaGUgbWVzc2FnZSB0byBoYXNoLlxuXHQgICAgICpcblx0ICAgICAqIEByZXR1cm4ge1dvcmRBcnJheX0gVGhlIGhhc2guXG5cdCAgICAgKlxuXHQgICAgICogQHN0YXRpY1xuXHQgICAgICpcblx0ICAgICAqIEBleGFtcGxlXG5cdCAgICAgKlxuXHQgICAgICogICAgIHZhciBoYXNoID0gQ3J5cHRvSlMuU0hBMygnbWVzc2FnZScpO1xuXHQgICAgICogICAgIHZhciBoYXNoID0gQ3J5cHRvSlMuU0hBMyh3b3JkQXJyYXkpO1xuXHQgICAgICovXG5cdCAgICBDLlNIQTMgPSBIYXNoZXIuX2NyZWF0ZUhlbHBlcihTSEEzKTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBTaG9ydGN1dCBmdW5jdGlvbiB0byB0aGUgSE1BQydzIG9iamVjdCBpbnRlcmZhY2UuXG5cdCAgICAgKlxuXHQgICAgICogQHBhcmFtIHtXb3JkQXJyYXl8c3RyaW5nfSBtZXNzYWdlIFRoZSBtZXNzYWdlIHRvIGhhc2guXG5cdCAgICAgKiBAcGFyYW0ge1dvcmRBcnJheXxzdHJpbmd9IGtleSBUaGUgc2VjcmV0IGtleS5cblx0ICAgICAqXG5cdCAgICAgKiBAcmV0dXJuIHtXb3JkQXJyYXl9IFRoZSBITUFDLlxuXHQgICAgICpcblx0ICAgICAqIEBzdGF0aWNcblx0ICAgICAqXG5cdCAgICAgKiBAZXhhbXBsZVxuXHQgICAgICpcblx0ICAgICAqICAgICB2YXIgaG1hYyA9IENyeXB0b0pTLkhtYWNTSEEzKG1lc3NhZ2UsIGtleSk7XG5cdCAgICAgKi9cblx0ICAgIEMuSG1hY1NIQTMgPSBIYXNoZXIuX2NyZWF0ZUhtYWNIZWxwZXIoU0hBMyk7XG5cdH0oTWF0aCkpO1xuXG5cblx0cmV0dXJuIENyeXB0b0pTLlNIQTM7XG5cbn0pKTsiLCI7KGZ1bmN0aW9uIChyb290LCBmYWN0b3J5KSB7XG5cdGlmICh0eXBlb2YgZXhwb3J0cyA9PT0gXCJvYmplY3RcIikge1xuXHRcdC8vIENvbW1vbkpTXG5cdFx0bW9kdWxlLmV4cG9ydHMgPSBleHBvcnRzID0gZmFjdG9yeShyZXF1aXJlKFwiLi9jb3JlXCIpKTtcblx0fVxuXHRlbHNlIGlmICh0eXBlb2YgZGVmaW5lID09PSBcImZ1bmN0aW9uXCIgJiYgZGVmaW5lLmFtZCkge1xuXHRcdC8vIEFNRFxuXHRcdGRlZmluZShbXCIuL2NvcmVcIl0sIGZhY3RvcnkpO1xuXHR9XG5cdGVsc2Uge1xuXHRcdC8vIEdsb2JhbCAoYnJvd3Nlcilcblx0XHRmYWN0b3J5KHJvb3QuQ3J5cHRvSlMpO1xuXHR9XG59KHRoaXMsIGZ1bmN0aW9uIChDcnlwdG9KUykge1xuXG5cdChmdW5jdGlvbiAodW5kZWZpbmVkKSB7XG5cdCAgICAvLyBTaG9ydGN1dHNcblx0ICAgIHZhciBDID0gQ3J5cHRvSlM7XG5cdCAgICB2YXIgQ19saWIgPSBDLmxpYjtcblx0ICAgIHZhciBCYXNlID0gQ19saWIuQmFzZTtcblx0ICAgIHZhciBYMzJXb3JkQXJyYXkgPSBDX2xpYi5Xb3JkQXJyYXk7XG5cblx0ICAgIC8qKlxuXHQgICAgICogeDY0IG5hbWVzcGFjZS5cblx0ICAgICAqL1xuXHQgICAgdmFyIENfeDY0ID0gQy54NjQgPSB7fTtcblxuXHQgICAgLyoqXG5cdCAgICAgKiBBIDY0LWJpdCB3b3JkLlxuXHQgICAgICovXG5cdCAgICB2YXIgWDY0V29yZCA9IENfeDY0LldvcmQgPSBCYXNlLmV4dGVuZCh7XG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogSW5pdGlhbGl6ZXMgYSBuZXdseSBjcmVhdGVkIDY0LWJpdCB3b3JkLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtudW1iZXJ9IGhpZ2ggVGhlIGhpZ2ggMzIgYml0cy5cblx0ICAgICAgICAgKiBAcGFyYW0ge251bWJlcn0gbG93IFRoZSBsb3cgMzIgYml0cy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHg2NFdvcmQgPSBDcnlwdG9KUy54NjQuV29yZC5jcmVhdGUoMHgwMDAxMDIwMywgMHgwNDA1MDYwNyk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgaW5pdDogZnVuY3Rpb24gKGhpZ2gsIGxvdykge1xuXHQgICAgICAgICAgICB0aGlzLmhpZ2ggPSBoaWdoO1xuXHQgICAgICAgICAgICB0aGlzLmxvdyA9IGxvdztcblx0ICAgICAgICB9XG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBCaXR3aXNlIE5PVHMgdGhpcyB3b3JkLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7WDY0V29yZH0gQSBuZXcgeDY0LVdvcmQgb2JqZWN0IGFmdGVyIG5lZ2F0aW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgbmVnYXRlZCA9IHg2NFdvcmQubm90KCk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgLy8gbm90OiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgIC8vIHZhciBoaWdoID0gfnRoaXMuaGlnaDtcblx0ICAgICAgICAgICAgLy8gdmFyIGxvdyA9IH50aGlzLmxvdztcblxuXHQgICAgICAgICAgICAvLyByZXR1cm4gWDY0V29yZC5jcmVhdGUoaGlnaCwgbG93KTtcblx0ICAgICAgICAvLyB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogQml0d2lzZSBBTkRzIHRoaXMgd29yZCB3aXRoIHRoZSBwYXNzZWQgd29yZC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7WDY0V29yZH0gd29yZCBUaGUgeDY0LVdvcmQgdG8gQU5EIHdpdGggdGhpcyB3b3JkLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7WDY0V29yZH0gQSBuZXcgeDY0LVdvcmQgb2JqZWN0IGFmdGVyIEFORGluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIGFuZGVkID0geDY0V29yZC5hbmQoYW5vdGhlclg2NFdvcmQpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIC8vIGFuZDogZnVuY3Rpb24gKHdvcmQpIHtcblx0ICAgICAgICAgICAgLy8gdmFyIGhpZ2ggPSB0aGlzLmhpZ2ggJiB3b3JkLmhpZ2g7XG5cdCAgICAgICAgICAgIC8vIHZhciBsb3cgPSB0aGlzLmxvdyAmIHdvcmQubG93O1xuXG5cdCAgICAgICAgICAgIC8vIHJldHVybiBYNjRXb3JkLmNyZWF0ZShoaWdoLCBsb3cpO1xuXHQgICAgICAgIC8vIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBCaXR3aXNlIE9ScyB0aGlzIHdvcmQgd2l0aCB0aGUgcGFzc2VkIHdvcmQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge1g2NFdvcmR9IHdvcmQgVGhlIHg2NC1Xb3JkIHRvIE9SIHdpdGggdGhpcyB3b3JkLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7WDY0V29yZH0gQSBuZXcgeDY0LVdvcmQgb2JqZWN0IGFmdGVyIE9SaW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgb3JlZCA9IHg2NFdvcmQub3IoYW5vdGhlclg2NFdvcmQpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIC8vIG9yOiBmdW5jdGlvbiAod29yZCkge1xuXHQgICAgICAgICAgICAvLyB2YXIgaGlnaCA9IHRoaXMuaGlnaCB8IHdvcmQuaGlnaDtcblx0ICAgICAgICAgICAgLy8gdmFyIGxvdyA9IHRoaXMubG93IHwgd29yZC5sb3c7XG5cblx0ICAgICAgICAgICAgLy8gcmV0dXJuIFg2NFdvcmQuY3JlYXRlKGhpZ2gsIGxvdyk7XG5cdCAgICAgICAgLy8gfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIEJpdHdpc2UgWE9ScyB0aGlzIHdvcmQgd2l0aCB0aGUgcGFzc2VkIHdvcmQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge1g2NFdvcmR9IHdvcmQgVGhlIHg2NC1Xb3JkIHRvIFhPUiB3aXRoIHRoaXMgd29yZC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1g2NFdvcmR9IEEgbmV3IHg2NC1Xb3JkIG9iamVjdCBhZnRlciBYT1JpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciB4b3JlZCA9IHg2NFdvcmQueG9yKGFub3RoZXJYNjRXb3JkKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICAvLyB4b3I6IGZ1bmN0aW9uICh3b3JkKSB7XG5cdCAgICAgICAgICAgIC8vIHZhciBoaWdoID0gdGhpcy5oaWdoIF4gd29yZC5oaWdoO1xuXHQgICAgICAgICAgICAvLyB2YXIgbG93ID0gdGhpcy5sb3cgXiB3b3JkLmxvdztcblxuXHQgICAgICAgICAgICAvLyByZXR1cm4gWDY0V29yZC5jcmVhdGUoaGlnaCwgbG93KTtcblx0ICAgICAgICAvLyB9LFxuXG5cdCAgICAgICAgLyoqXG5cdCAgICAgICAgICogU2hpZnRzIHRoaXMgd29yZCBuIGJpdHMgdG8gdGhlIGxlZnQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge251bWJlcn0gbiBUaGUgbnVtYmVyIG9mIGJpdHMgdG8gc2hpZnQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtYNjRXb3JkfSBBIG5ldyB4NjQtV29yZCBvYmplY3QgYWZ0ZXIgc2hpZnRpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciBzaGlmdGVkID0geDY0V29yZC5zaGlmdEwoMjUpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIC8vIHNoaWZ0TDogZnVuY3Rpb24gKG4pIHtcblx0ICAgICAgICAgICAgLy8gaWYgKG4gPCAzMikge1xuXHQgICAgICAgICAgICAgICAgLy8gdmFyIGhpZ2ggPSAodGhpcy5oaWdoIDw8IG4pIHwgKHRoaXMubG93ID4+PiAoMzIgLSBuKSk7XG5cdCAgICAgICAgICAgICAgICAvLyB2YXIgbG93ID0gdGhpcy5sb3cgPDwgbjtcblx0ICAgICAgICAgICAgLy8gfSBlbHNlIHtcblx0ICAgICAgICAgICAgICAgIC8vIHZhciBoaWdoID0gdGhpcy5sb3cgPDwgKG4gLSAzMik7XG5cdCAgICAgICAgICAgICAgICAvLyB2YXIgbG93ID0gMDtcblx0ICAgICAgICAgICAgLy8gfVxuXG5cdCAgICAgICAgICAgIC8vIHJldHVybiBYNjRXb3JkLmNyZWF0ZShoaWdoLCBsb3cpO1xuXHQgICAgICAgIC8vIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBTaGlmdHMgdGhpcyB3b3JkIG4gYml0cyB0byB0aGUgcmlnaHQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge251bWJlcn0gbiBUaGUgbnVtYmVyIG9mIGJpdHMgdG8gc2hpZnQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcmV0dXJuIHtYNjRXb3JkfSBBIG5ldyB4NjQtV29yZCBvYmplY3QgYWZ0ZXIgc2hpZnRpbmcuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAZXhhbXBsZVxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogICAgIHZhciBzaGlmdGVkID0geDY0V29yZC5zaGlmdFIoNyk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgLy8gc2hpZnRSOiBmdW5jdGlvbiAobikge1xuXHQgICAgICAgICAgICAvLyBpZiAobiA8IDMyKSB7XG5cdCAgICAgICAgICAgICAgICAvLyB2YXIgbG93ID0gKHRoaXMubG93ID4+PiBuKSB8ICh0aGlzLmhpZ2ggPDwgKDMyIC0gbikpO1xuXHQgICAgICAgICAgICAgICAgLy8gdmFyIGhpZ2ggPSB0aGlzLmhpZ2ggPj4+IG47XG5cdCAgICAgICAgICAgIC8vIH0gZWxzZSB7XG5cdCAgICAgICAgICAgICAgICAvLyB2YXIgbG93ID0gdGhpcy5oaWdoID4+PiAobiAtIDMyKTtcblx0ICAgICAgICAgICAgICAgIC8vIHZhciBoaWdoID0gMDtcblx0ICAgICAgICAgICAgLy8gfVxuXG5cdCAgICAgICAgICAgIC8vIHJldHVybiBYNjRXb3JkLmNyZWF0ZShoaWdoLCBsb3cpO1xuXHQgICAgICAgIC8vIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBSb3RhdGVzIHRoaXMgd29yZCBuIGJpdHMgdG8gdGhlIGxlZnQuXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiBAcGFyYW0ge251bWJlcn0gbiBUaGUgbnVtYmVyIG9mIGJpdHMgdG8gcm90YXRlLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7WDY0V29yZH0gQSBuZXcgeDY0LVdvcmQgb2JqZWN0IGFmdGVyIHJvdGF0aW5nLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgcm90YXRlZCA9IHg2NFdvcmQucm90TCgyNSk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgLy8gcm90TDogZnVuY3Rpb24gKG4pIHtcblx0ICAgICAgICAgICAgLy8gcmV0dXJuIHRoaXMuc2hpZnRMKG4pLm9yKHRoaXMuc2hpZnRSKDY0IC0gbikpO1xuXHQgICAgICAgIC8vIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBSb3RhdGVzIHRoaXMgd29yZCBuIGJpdHMgdG8gdGhlIHJpZ2h0LlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHBhcmFtIHtudW1iZXJ9IG4gVGhlIG51bWJlciBvZiBiaXRzIHRvIHJvdGF0ZS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1g2NFdvcmR9IEEgbmV3IHg2NC1Xb3JkIG9iamVjdCBhZnRlciByb3RhdGluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHJvdGF0ZWQgPSB4NjRXb3JkLnJvdFIoNyk7XG5cdCAgICAgICAgICovXG5cdCAgICAgICAgLy8gcm90UjogZnVuY3Rpb24gKG4pIHtcblx0ICAgICAgICAgICAgLy8gcmV0dXJuIHRoaXMuc2hpZnRSKG4pLm9yKHRoaXMuc2hpZnRMKDY0IC0gbikpO1xuXHQgICAgICAgIC8vIH0sXG5cblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBBZGRzIHRoaXMgd29yZCB3aXRoIHRoZSBwYXNzZWQgd29yZC5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7WDY0V29yZH0gd29yZCBUaGUgeDY0LVdvcmQgdG8gYWRkIHdpdGggdGhpcyB3b3JkLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQHJldHVybiB7WDY0V29yZH0gQSBuZXcgeDY0LVdvcmQgb2JqZWN0IGFmdGVyIGFkZGluZy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIGFkZGVkID0geDY0V29yZC5hZGQoYW5vdGhlclg2NFdvcmQpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIC8vIGFkZDogZnVuY3Rpb24gKHdvcmQpIHtcblx0ICAgICAgICAgICAgLy8gdmFyIGxvdyA9ICh0aGlzLmxvdyArIHdvcmQubG93KSB8IDA7XG5cdCAgICAgICAgICAgIC8vIHZhciBjYXJyeSA9IChsb3cgPj4+IDApIDwgKHRoaXMubG93ID4+PiAwKSA/IDEgOiAwO1xuXHQgICAgICAgICAgICAvLyB2YXIgaGlnaCA9ICh0aGlzLmhpZ2ggKyB3b3JkLmhpZ2ggKyBjYXJyeSkgfCAwO1xuXG5cdCAgICAgICAgICAgIC8vIHJldHVybiBYNjRXb3JkLmNyZWF0ZShoaWdoLCBsb3cpO1xuXHQgICAgICAgIC8vIH1cblx0ICAgIH0pO1xuXG5cdCAgICAvKipcblx0ICAgICAqIEFuIGFycmF5IG9mIDY0LWJpdCB3b3Jkcy5cblx0ICAgICAqXG5cdCAgICAgKiBAcHJvcGVydHkge0FycmF5fSB3b3JkcyBUaGUgYXJyYXkgb2YgQ3J5cHRvSlMueDY0LldvcmQgb2JqZWN0cy5cblx0ICAgICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBzaWdCeXRlcyBUaGUgbnVtYmVyIG9mIHNpZ25pZmljYW50IGJ5dGVzIGluIHRoaXMgd29yZCBhcnJheS5cblx0ICAgICAqL1xuXHQgICAgdmFyIFg2NFdvcmRBcnJheSA9IENfeDY0LldvcmRBcnJheSA9IEJhc2UuZXh0ZW5kKHtcblx0ICAgICAgICAvKipcblx0ICAgICAgICAgKiBJbml0aWFsaXplcyBhIG5ld2x5IGNyZWF0ZWQgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBwYXJhbSB7QXJyYXl9IHdvcmRzIChPcHRpb25hbCkgQW4gYXJyYXkgb2YgQ3J5cHRvSlMueDY0LldvcmQgb2JqZWN0cy5cblx0ICAgICAgICAgKiBAcGFyYW0ge251bWJlcn0gc2lnQnl0ZXMgKE9wdGlvbmFsKSBUaGUgbnVtYmVyIG9mIHNpZ25pZmljYW50IGJ5dGVzIGluIHRoZSB3b3Jkcy5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHdvcmRBcnJheSA9IENyeXB0b0pTLng2NC5Xb3JkQXJyYXkuY3JlYXRlKCk7XG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHdvcmRBcnJheSA9IENyeXB0b0pTLng2NC5Xb3JkQXJyYXkuY3JlYXRlKFtcblx0ICAgICAgICAgKiAgICAgICAgIENyeXB0b0pTLng2NC5Xb3JkLmNyZWF0ZSgweDAwMDEwMjAzLCAweDA0MDUwNjA3KSxcblx0ICAgICAgICAgKiAgICAgICAgIENyeXB0b0pTLng2NC5Xb3JkLmNyZWF0ZSgweDE4MTkxYTFiLCAweDFjMWQxZTFmKVxuXHQgICAgICAgICAqICAgICBdKTtcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgd29yZEFycmF5ID0gQ3J5cHRvSlMueDY0LldvcmRBcnJheS5jcmVhdGUoW1xuXHQgICAgICAgICAqICAgICAgICAgQ3J5cHRvSlMueDY0LldvcmQuY3JlYXRlKDB4MDAwMTAyMDMsIDB4MDQwNTA2MDcpLFxuXHQgICAgICAgICAqICAgICAgICAgQ3J5cHRvSlMueDY0LldvcmQuY3JlYXRlKDB4MTgxOTFhMWIsIDB4MWMxZDFlMWYpXG5cdCAgICAgICAgICogICAgIF0sIDEwKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBpbml0OiBmdW5jdGlvbiAod29yZHMsIHNpZ0J5dGVzKSB7XG5cdCAgICAgICAgICAgIHdvcmRzID0gdGhpcy53b3JkcyA9IHdvcmRzIHx8IFtdO1xuXG5cdCAgICAgICAgICAgIGlmIChzaWdCeXRlcyAhPSB1bmRlZmluZWQpIHtcblx0ICAgICAgICAgICAgICAgIHRoaXMuc2lnQnl0ZXMgPSBzaWdCeXRlcztcblx0ICAgICAgICAgICAgfSBlbHNlIHtcblx0ICAgICAgICAgICAgICAgIHRoaXMuc2lnQnl0ZXMgPSB3b3Jkcy5sZW5ndGggKiA4O1xuXHQgICAgICAgICAgICB9XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENvbnZlcnRzIHRoaXMgNjQtYml0IHdvcmQgYXJyYXkgdG8gYSAzMi1iaXQgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge0NyeXB0b0pTLmxpYi5Xb3JkQXJyYXl9IFRoaXMgd29yZCBhcnJheSdzIGRhdGEgYXMgYSAzMi1iaXQgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEBleGFtcGxlXG5cdCAgICAgICAgICpcblx0ICAgICAgICAgKiAgICAgdmFyIHgzMldvcmRBcnJheSA9IHg2NFdvcmRBcnJheS50b1gzMigpO1xuXHQgICAgICAgICAqL1xuXHQgICAgICAgIHRvWDMyOiBmdW5jdGlvbiAoKSB7XG5cdCAgICAgICAgICAgIC8vIFNob3J0Y3V0c1xuXHQgICAgICAgICAgICB2YXIgeDY0V29yZHMgPSB0aGlzLndvcmRzO1xuXHQgICAgICAgICAgICB2YXIgeDY0V29yZHNMZW5ndGggPSB4NjRXb3Jkcy5sZW5ndGg7XG5cblx0ICAgICAgICAgICAgLy8gQ29udmVydFxuXHQgICAgICAgICAgICB2YXIgeDMyV29yZHMgPSBbXTtcblx0ICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB4NjRXb3Jkc0xlbmd0aDsgaSsrKSB7XG5cdCAgICAgICAgICAgICAgICB2YXIgeDY0V29yZCA9IHg2NFdvcmRzW2ldO1xuXHQgICAgICAgICAgICAgICAgeDMyV29yZHMucHVzaCh4NjRXb3JkLmhpZ2gpO1xuXHQgICAgICAgICAgICAgICAgeDMyV29yZHMucHVzaCh4NjRXb3JkLmxvdyk7XG5cdCAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICByZXR1cm4gWDMyV29yZEFycmF5LmNyZWF0ZSh4MzJXb3JkcywgdGhpcy5zaWdCeXRlcyk7XG5cdCAgICAgICAgfSxcblxuXHQgICAgICAgIC8qKlxuXHQgICAgICAgICAqIENyZWF0ZXMgYSBjb3B5IG9mIHRoaXMgd29yZCBhcnJheS5cblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqIEByZXR1cm4ge1g2NFdvcmRBcnJheX0gVGhlIGNsb25lLlxuXHQgICAgICAgICAqXG5cdCAgICAgICAgICogQGV4YW1wbGVcblx0ICAgICAgICAgKlxuXHQgICAgICAgICAqICAgICB2YXIgY2xvbmUgPSB4NjRXb3JkQXJyYXkuY2xvbmUoKTtcblx0ICAgICAgICAgKi9cblx0ICAgICAgICBjbG9uZTogZnVuY3Rpb24gKCkge1xuXHQgICAgICAgICAgICB2YXIgY2xvbmUgPSBCYXNlLmNsb25lLmNhbGwodGhpcyk7XG5cblx0ICAgICAgICAgICAgLy8gQ2xvbmUgXCJ3b3Jkc1wiIGFycmF5XG5cdCAgICAgICAgICAgIHZhciB3b3JkcyA9IGNsb25lLndvcmRzID0gdGhpcy53b3Jkcy5zbGljZSgwKTtcblxuXHQgICAgICAgICAgICAvLyBDbG9uZSBlYWNoIFg2NFdvcmQgb2JqZWN0XG5cdCAgICAgICAgICAgIHZhciB3b3Jkc0xlbmd0aCA9IHdvcmRzLmxlbmd0aDtcblx0ICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB3b3Jkc0xlbmd0aDsgaSsrKSB7XG5cdCAgICAgICAgICAgICAgICB3b3Jkc1tpXSA9IHdvcmRzW2ldLmNsb25lKCk7XG5cdCAgICAgICAgICAgIH1cblxuXHQgICAgICAgICAgICByZXR1cm4gY2xvbmU7XG5cdCAgICAgICAgfVxuXHQgICAgfSk7XG5cdH0oKSk7XG5cblxuXHRyZXR1cm4gQ3J5cHRvSlM7XG5cbn0pKTsiLCIvKiEgYmlnbnVtYmVyLmpzIHYyLjAuNyBodHRwczovL2dpdGh1Yi5jb20vTWlrZU1jbC9iaWdudW1iZXIuanMvTElDRU5DRSAqL1xuXG47KGZ1bmN0aW9uIChnbG9iYWwpIHtcbiAgICAndXNlIHN0cmljdCc7XG5cbiAgICAvKlxuICAgICAgYmlnbnVtYmVyLmpzIHYyLjAuN1xuICAgICAgQSBKYXZhU2NyaXB0IGxpYnJhcnkgZm9yIGFyYml0cmFyeS1wcmVjaXNpb24gYXJpdGhtZXRpYy5cbiAgICAgIGh0dHBzOi8vZ2l0aHViLmNvbS9NaWtlTWNsL2JpZ251bWJlci5qc1xuICAgICAgQ29weXJpZ2h0IChjKSAyMDE1IE1pY2hhZWwgTWNsYXVnaGxpbiA8TThjaDg4bEBnbWFpbC5jb20+XG4gICAgICBNSVQgRXhwYXQgTGljZW5jZVxuICAgICovXG5cblxuICAgIHZhciBCaWdOdW1iZXIsIGNyeXB0bywgcGFyc2VOdW1lcmljLFxuICAgICAgICBpc051bWVyaWMgPSAvXi0/KFxcZCsoXFwuXFxkKik/fFxcLlxcZCspKGVbKy1dP1xcZCspPyQvaSxcbiAgICAgICAgbWF0aGNlaWwgPSBNYXRoLmNlaWwsXG4gICAgICAgIG1hdGhmbG9vciA9IE1hdGguZmxvb3IsXG4gICAgICAgIG5vdEJvb2wgPSAnIG5vdCBhIGJvb2xlYW4gb3IgYmluYXJ5IGRpZ2l0JyxcbiAgICAgICAgcm91bmRpbmdNb2RlID0gJ3JvdW5kaW5nIG1vZGUnLFxuICAgICAgICB0b29NYW55RGlnaXRzID0gJ251bWJlciB0eXBlIGhhcyBtb3JlIHRoYW4gMTUgc2lnbmlmaWNhbnQgZGlnaXRzJyxcbiAgICAgICAgQUxQSEFCRVQgPSAnMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVokXycsXG4gICAgICAgIEJBU0UgPSAxZTE0LFxuICAgICAgICBMT0dfQkFTRSA9IDE0LFxuICAgICAgICBNQVhfU0FGRV9JTlRFR0VSID0gMHgxZmZmZmZmZmZmZmZmZiwgICAgICAgICAvLyAyXjUzIC0gMVxuICAgICAgICAvLyBNQVhfSU5UMzIgPSAweDdmZmZmZmZmLCAgICAgICAgICAgICAgICAgICAvLyAyXjMxIC0gMVxuICAgICAgICBQT1dTX1RFTiA9IFsxLCAxMCwgMTAwLCAxZTMsIDFlNCwgMWU1LCAxZTYsIDFlNywgMWU4LCAxZTksIDFlMTAsIDFlMTEsIDFlMTIsIDFlMTNdLFxuICAgICAgICBTUVJUX0JBU0UgPSAxZTcsXG5cbiAgICAgICAgLypcbiAgICAgICAgICogVGhlIGxpbWl0IG9uIHRoZSB2YWx1ZSBvZiBERUNJTUFMX1BMQUNFUywgVE9fRVhQX05FRywgVE9fRVhQX1BPUywgTUlOX0VYUCwgTUFYX0VYUCwgYW5kXG4gICAgICAgICAqIHRoZSBhcmd1bWVudHMgdG8gdG9FeHBvbmVudGlhbCwgdG9GaXhlZCwgdG9Gb3JtYXQsIGFuZCB0b1ByZWNpc2lvbiwgYmV5b25kIHdoaWNoIGFuXG4gICAgICAgICAqIGV4Y2VwdGlvbiBpcyB0aHJvd24gKGlmIEVSUk9SUyBpcyB0cnVlKS5cbiAgICAgICAgICovXG4gICAgICAgIE1BWCA9IDFFOTsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIDAgdG8gTUFYX0lOVDMyXG5cblxuICAgIC8qXG4gICAgICogQ3JlYXRlIGFuZCByZXR1cm4gYSBCaWdOdW1iZXIgY29uc3RydWN0b3IuXG4gICAgICovXG4gICAgZnVuY3Rpb24gYW5vdGhlcihjb25maWdPYmopIHtcbiAgICAgICAgdmFyIGRpdixcblxuICAgICAgICAgICAgLy8gaWQgdHJhY2tzIHRoZSBjYWxsZXIgZnVuY3Rpb24sIHNvIGl0cyBuYW1lIGNhbiBiZSBpbmNsdWRlZCBpbiBlcnJvciBtZXNzYWdlcy5cbiAgICAgICAgICAgIGlkID0gMCxcbiAgICAgICAgICAgIFAgPSBCaWdOdW1iZXIucHJvdG90eXBlLFxuICAgICAgICAgICAgT05FID0gbmV3IEJpZ051bWJlcigxKSxcblxuXG4gICAgICAgICAgICAvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqIEVESVRBQkxFIERFRkFVTFRTICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cblxuICAgICAgICAgICAgLypcbiAgICAgICAgICAgICAqIFRoZSBkZWZhdWx0IHZhbHVlcyBiZWxvdyBtdXN0IGJlIGludGVnZXJzIHdpdGhpbiB0aGUgaW5jbHVzaXZlIHJhbmdlcyBzdGF0ZWQuXG4gICAgICAgICAgICAgKiBUaGUgdmFsdWVzIGNhbiBhbHNvIGJlIGNoYW5nZWQgYXQgcnVuLXRpbWUgdXNpbmcgQmlnTnVtYmVyLmNvbmZpZy5cbiAgICAgICAgICAgICAqL1xuXG4gICAgICAgICAgICAvLyBUaGUgbWF4aW11bSBudW1iZXIgb2YgZGVjaW1hbCBwbGFjZXMgZm9yIG9wZXJhdGlvbnMgaW52b2x2aW5nIGRpdmlzaW9uLlxuICAgICAgICAgICAgREVDSU1BTF9QTEFDRVMgPSAyMCwgICAgICAgICAgICAgICAgICAgICAvLyAwIHRvIE1BWFxuXG4gICAgICAgICAgICAvKlxuICAgICAgICAgICAgICogVGhlIHJvdW5kaW5nIG1vZGUgdXNlZCB3aGVuIHJvdW5kaW5nIHRvIHRoZSBhYm92ZSBkZWNpbWFsIHBsYWNlcywgYW5kIHdoZW4gdXNpbmdcbiAgICAgICAgICAgICAqIHRvRXhwb25lbnRpYWwsIHRvRml4ZWQsIHRvRm9ybWF0IGFuZCB0b1ByZWNpc2lvbiwgYW5kIHJvdW5kIChkZWZhdWx0IHZhbHVlKS5cbiAgICAgICAgICAgICAqIFVQICAgICAgICAgMCBBd2F5IGZyb20gemVyby5cbiAgICAgICAgICAgICAqIERPV04gICAgICAgMSBUb3dhcmRzIHplcm8uXG4gICAgICAgICAgICAgKiBDRUlMICAgICAgIDIgVG93YXJkcyArSW5maW5pdHkuXG4gICAgICAgICAgICAgKiBGTE9PUiAgICAgIDMgVG93YXJkcyAtSW5maW5pdHkuXG4gICAgICAgICAgICAgKiBIQUxGX1VQICAgIDQgVG93YXJkcyBuZWFyZXN0IG5laWdoYm91ci4gSWYgZXF1aWRpc3RhbnQsIHVwLlxuICAgICAgICAgICAgICogSEFMRl9ET1dOICA1IFRvd2FyZHMgbmVhcmVzdCBuZWlnaGJvdXIuIElmIGVxdWlkaXN0YW50LCBkb3duLlxuICAgICAgICAgICAgICogSEFMRl9FVkVOICA2IFRvd2FyZHMgbmVhcmVzdCBuZWlnaGJvdXIuIElmIGVxdWlkaXN0YW50LCB0b3dhcmRzIGV2ZW4gbmVpZ2hib3VyLlxuICAgICAgICAgICAgICogSEFMRl9DRUlMICA3IFRvd2FyZHMgbmVhcmVzdCBuZWlnaGJvdXIuIElmIGVxdWlkaXN0YW50LCB0b3dhcmRzICtJbmZpbml0eS5cbiAgICAgICAgICAgICAqIEhBTEZfRkxPT1IgOCBUb3dhcmRzIG5lYXJlc3QgbmVpZ2hib3VyLiBJZiBlcXVpZGlzdGFudCwgdG93YXJkcyAtSW5maW5pdHkuXG4gICAgICAgICAgICAgKi9cbiAgICAgICAgICAgIFJPVU5ESU5HX01PREUgPSA0LCAgICAgICAgICAgICAgICAgICAgICAgLy8gMCB0byA4XG5cbiAgICAgICAgICAgIC8vIEVYUE9ORU5USUFMX0FUIDogW1RPX0VYUF9ORUcgLCBUT19FWFBfUE9TXVxuXG4gICAgICAgICAgICAvLyBUaGUgZXhwb25lbnQgdmFsdWUgYXQgYW5kIGJlbmVhdGggd2hpY2ggdG9TdHJpbmcgcmV0dXJucyBleHBvbmVudGlhbCBub3RhdGlvbi5cbiAgICAgICAgICAgIC8vIE51bWJlciB0eXBlOiAtN1xuICAgICAgICAgICAgVE9fRVhQX05FRyA9IC03LCAgICAgICAgICAgICAgICAgICAgICAgICAvLyAwIHRvIC1NQVhcblxuICAgICAgICAgICAgLy8gVGhlIGV4cG9uZW50IHZhbHVlIGF0IGFuZCBhYm92ZSB3aGljaCB0b1N0cmluZyByZXR1cm5zIGV4cG9uZW50aWFsIG5vdGF0aW9uLlxuICAgICAgICAgICAgLy8gTnVtYmVyIHR5cGU6IDIxXG4gICAgICAgICAgICBUT19FWFBfUE9TID0gMjEsICAgICAgICAgICAgICAgICAgICAgICAgIC8vIDAgdG8gTUFYXG5cbiAgICAgICAgICAgIC8vIFJBTkdFIDogW01JTl9FWFAsIE1BWF9FWFBdXG5cbiAgICAgICAgICAgIC8vIFRoZSBtaW5pbXVtIGV4cG9uZW50IHZhbHVlLCBiZW5lYXRoIHdoaWNoIHVuZGVyZmxvdyB0byB6ZXJvIG9jY3Vycy5cbiAgICAgICAgICAgIC8vIE51bWJlciB0eXBlOiAtMzI0ICAoNWUtMzI0KVxuICAgICAgICAgICAgTUlOX0VYUCA9IC0xZTcsICAgICAgICAgICAgICAgICAgICAgICAgICAvLyAtMSB0byAtTUFYXG5cbiAgICAgICAgICAgIC8vIFRoZSBtYXhpbXVtIGV4cG9uZW50IHZhbHVlLCBhYm92ZSB3aGljaCBvdmVyZmxvdyB0byBJbmZpbml0eSBvY2N1cnMuXG4gICAgICAgICAgICAvLyBOdW1iZXIgdHlwZTogIDMwOCAgKDEuNzk3NjkzMTM0ODYyMzE1N2UrMzA4KVxuICAgICAgICAgICAgLy8gRm9yIE1BWF9FWFAgPiAxZTcsIGUuZy4gbmV3IEJpZ051bWJlcignMWUxMDAwMDAwMDAnKS5wbHVzKDEpIG1heSBiZSBzbG93LlxuICAgICAgICAgICAgTUFYX0VYUCA9IDFlNywgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyAxIHRvIE1BWFxuXG4gICAgICAgICAgICAvLyBXaGV0aGVyIEJpZ051bWJlciBFcnJvcnMgYXJlIGV2ZXIgdGhyb3duLlxuICAgICAgICAgICAgRVJST1JTID0gdHJ1ZSwgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyB0cnVlIG9yIGZhbHNlXG5cbiAgICAgICAgICAgIC8vIENoYW5nZSB0byBpbnRWYWxpZGF0b3JOb0Vycm9ycyBpZiBFUlJPUlMgaXMgZmFsc2UuXG4gICAgICAgICAgICBpc1ZhbGlkSW50ID0gaW50VmFsaWRhdG9yV2l0aEVycm9ycywgICAgIC8vIGludFZhbGlkYXRvcldpdGhFcnJvcnMvaW50VmFsaWRhdG9yTm9FcnJvcnNcblxuICAgICAgICAgICAgLy8gV2hldGhlciB0byB1c2UgY3J5cHRvZ3JhcGhpY2FsbHktc2VjdXJlIHJhbmRvbSBudW1iZXIgZ2VuZXJhdGlvbiwgaWYgYXZhaWxhYmxlLlxuICAgICAgICAgICAgQ1JZUFRPID0gZmFsc2UsICAgICAgICAgICAgICAgICAgICAgICAgICAvLyB0cnVlIG9yIGZhbHNlXG5cbiAgICAgICAgICAgIC8qXG4gICAgICAgICAgICAgKiBUaGUgbW9kdWxvIG1vZGUgdXNlZCB3aGVuIGNhbGN1bGF0aW5nIHRoZSBtb2R1bHVzOiBhIG1vZCBuLlxuICAgICAgICAgICAgICogVGhlIHF1b3RpZW50IChxID0gYSAvIG4pIGlzIGNhbGN1bGF0ZWQgYWNjb3JkaW5nIHRvIHRoZSBjb3JyZXNwb25kaW5nIHJvdW5kaW5nIG1vZGUuXG4gICAgICAgICAgICAgKiBUaGUgcmVtYWluZGVyIChyKSBpcyBjYWxjdWxhdGVkIGFzOiByID0gYSAtIG4gKiBxLlxuICAgICAgICAgICAgICpcbiAgICAgICAgICAgICAqIFVQICAgICAgICAwIFRoZSByZW1haW5kZXIgaXMgcG9zaXRpdmUgaWYgdGhlIGRpdmlkZW5kIGlzIG5lZ2F0aXZlLCBlbHNlIGlzIG5lZ2F0aXZlLlxuICAgICAgICAgICAgICogRE9XTiAgICAgIDEgVGhlIHJlbWFpbmRlciBoYXMgdGhlIHNhbWUgc2lnbiBhcyB0aGUgZGl2aWRlbmQuXG4gICAgICAgICAgICAgKiAgICAgICAgICAgICBUaGlzIG1vZHVsbyBtb2RlIGlzIGNvbW1vbmx5IGtub3duIGFzICd0cnVuY2F0ZWQgZGl2aXNpb24nIGFuZCBpc1xuICAgICAgICAgICAgICogICAgICAgICAgICAgZXF1aXZhbGVudCB0byAoYSAlIG4pIGluIEphdmFTY3JpcHQuXG4gICAgICAgICAgICAgKiBGTE9PUiAgICAgMyBUaGUgcmVtYWluZGVyIGhhcyB0aGUgc2FtZSBzaWduIGFzIHRoZSBkaXZpc29yIChQeXRob24gJSkuXG4gICAgICAgICAgICAgKiBIQUxGX0VWRU4gNiBUaGlzIG1vZHVsbyBtb2RlIGltcGxlbWVudHMgdGhlIElFRUUgNzU0IHJlbWFpbmRlciBmdW5jdGlvbi5cbiAgICAgICAgICAgICAqIEVVQ0xJRCAgICA5IEV1Y2xpZGlhbiBkaXZpc2lvbi4gcSA9IHNpZ24obikgKiBmbG9vcihhIC8gYWJzKG4pKS5cbiAgICAgICAgICAgICAqICAgICAgICAgICAgIFRoZSByZW1haW5kZXIgaXMgYWx3YXlzIHBvc2l0aXZlLlxuICAgICAgICAgICAgICpcbiAgICAgICAgICAgICAqIFRoZSB0cnVuY2F0ZWQgZGl2aXNpb24sIGZsb29yZWQgZGl2aXNpb24sIEV1Y2xpZGlhbiBkaXZpc2lvbiBhbmQgSUVFRSA3NTQgcmVtYWluZGVyXG4gICAgICAgICAgICAgKiBtb2RlcyBhcmUgY29tbW9ubHkgdXNlZCBmb3IgdGhlIG1vZHVsdXMgb3BlcmF0aW9uLlxuICAgICAgICAgICAgICogQWx0aG91Z2ggdGhlIG90aGVyIHJvdW5kaW5nIG1vZGVzIGNhbiBhbHNvIGJlIHVzZWQsIHRoZXkgbWF5IG5vdCBnaXZlIHVzZWZ1bCByZXN1bHRzLlxuICAgICAgICAgICAgICovXG4gICAgICAgICAgICBNT0RVTE9fTU9ERSA9IDEsICAgICAgICAgICAgICAgICAgICAgICAgIC8vIDAgdG8gOVxuXG4gICAgICAgICAgICAvLyBUaGUgbWF4aW11bSBudW1iZXIgb2Ygc2lnbmlmaWNhbnQgZGlnaXRzIG9mIHRoZSByZXN1bHQgb2YgdGhlIHRvUG93ZXIgb3BlcmF0aW9uLlxuICAgICAgICAgICAgLy8gSWYgUE9XX1BSRUNJU0lPTiBpcyAwLCB0aGVyZSB3aWxsIGJlIHVubGltaXRlZCBzaWduaWZpY2FudCBkaWdpdHMuXG4gICAgICAgICAgICBQT1dfUFJFQ0lTSU9OID0gMTAwLCAgICAgICAgICAgICAgICAgICAgIC8vIDAgdG8gTUFYXG5cbiAgICAgICAgICAgIC8vIFRoZSBmb3JtYXQgc3BlY2lmaWNhdGlvbiB1c2VkIGJ5IHRoZSBCaWdOdW1iZXIucHJvdG90eXBlLnRvRm9ybWF0IG1ldGhvZC5cbiAgICAgICAgICAgIEZPUk1BVCA9IHtcbiAgICAgICAgICAgICAgICBkZWNpbWFsU2VwYXJhdG9yOiAnLicsXG4gICAgICAgICAgICAgICAgZ3JvdXBTZXBhcmF0b3I6ICcsJyxcbiAgICAgICAgICAgICAgICBncm91cFNpemU6IDMsXG4gICAgICAgICAgICAgICAgc2Vjb25kYXJ5R3JvdXBTaXplOiAwLFxuICAgICAgICAgICAgICAgIGZyYWN0aW9uR3JvdXBTZXBhcmF0b3I6ICdcXHhBMCcsICAgICAgLy8gbm9uLWJyZWFraW5nIHNwYWNlXG4gICAgICAgICAgICAgICAgZnJhY3Rpb25Hcm91cFNpemU6IDBcbiAgICAgICAgICAgIH07XG5cblxuICAgICAgICAvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5cbiAgICAgICAgLy8gQ09OU1RSVUNUT1JcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFRoZSBCaWdOdW1iZXIgY29uc3RydWN0b3IgYW5kIGV4cG9ydGVkIGZ1bmN0aW9uLlxuICAgICAgICAgKiBDcmVhdGUgYW5kIHJldHVybiBhIG5ldyBpbnN0YW5jZSBvZiBhIEJpZ051bWJlciBvYmplY3QuXG4gICAgICAgICAqXG4gICAgICAgICAqIG4ge251bWJlcnxzdHJpbmd8QmlnTnVtYmVyfSBBIG51bWVyaWMgdmFsdWUuXG4gICAgICAgICAqIFtiXSB7bnVtYmVyfSBUaGUgYmFzZSBvZiBuLiBJbnRlZ2VyLCAyIHRvIDY0IGluY2x1c2l2ZS5cbiAgICAgICAgICovXG4gICAgICAgIGZ1bmN0aW9uIEJpZ051bWJlciggbiwgYiApIHtcbiAgICAgICAgICAgIHZhciBjLCBlLCBpLCBudW0sIGxlbiwgc3RyLFxuICAgICAgICAgICAgICAgIHggPSB0aGlzO1xuXG4gICAgICAgICAgICAvLyBFbmFibGUgY29uc3RydWN0b3IgdXNhZ2Ugd2l0aG91dCBuZXcuXG4gICAgICAgICAgICBpZiAoICEoIHggaW5zdGFuY2VvZiBCaWdOdW1iZXIgKSApIHtcblxuICAgICAgICAgICAgICAgIC8vICdCaWdOdW1iZXIoKSBjb25zdHJ1Y3RvciBjYWxsIHdpdGhvdXQgbmV3OiB7bn0nXG4gICAgICAgICAgICAgICAgaWYgKEVSUk9SUykgcmFpc2UoIDI2LCAnY29uc3RydWN0b3IgY2FsbCB3aXRob3V0IG5ldycsIG4gKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gbmV3IEJpZ051bWJlciggbiwgYiApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyAnbmV3IEJpZ051bWJlcigpIGJhc2Ugbm90IGFuIGludGVnZXI6IHtifSdcbiAgICAgICAgICAgIC8vICduZXcgQmlnTnVtYmVyKCkgYmFzZSBvdXQgb2YgcmFuZ2U6IHtifSdcbiAgICAgICAgICAgIGlmICggYiA9PSBudWxsIHx8ICFpc1ZhbGlkSW50KCBiLCAyLCA2NCwgaWQsICdiYXNlJyApICkge1xuXG4gICAgICAgICAgICAgICAgLy8gRHVwbGljYXRlLlxuICAgICAgICAgICAgICAgIGlmICggbiBpbnN0YW5jZW9mIEJpZ051bWJlciApIHtcbiAgICAgICAgICAgICAgICAgICAgeC5zID0gbi5zO1xuICAgICAgICAgICAgICAgICAgICB4LmUgPSBuLmU7XG4gICAgICAgICAgICAgICAgICAgIHguYyA9ICggbiA9IG4uYyApID8gbi5zbGljZSgpIDogbjtcbiAgICAgICAgICAgICAgICAgICAgaWQgPSAwO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKCAoIG51bSA9IHR5cGVvZiBuID09ICdudW1iZXInICkgJiYgbiAqIDAgPT0gMCApIHtcbiAgICAgICAgICAgICAgICAgICAgeC5zID0gMSAvIG4gPCAwID8gKCBuID0gLW4sIC0xICkgOiAxO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIEZhc3QgcGF0aCBmb3IgaW50ZWdlcnMuXG4gICAgICAgICAgICAgICAgICAgIGlmICggbiA9PT0gfn5uICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZm9yICggZSA9IDAsIGkgPSBuOyBpID49IDEwOyBpIC89IDEwLCBlKysgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHguZSA9IGU7XG4gICAgICAgICAgICAgICAgICAgICAgICB4LmMgPSBbbl07XG4gICAgICAgICAgICAgICAgICAgICAgICBpZCA9IDA7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBzdHIgPSBuICsgJyc7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCAhaXNOdW1lcmljLnRlc3QoIHN0ciA9IG4gKyAnJyApICkgcmV0dXJuIHBhcnNlTnVtZXJpYyggeCwgc3RyLCBudW0gKTtcbiAgICAgICAgICAgICAgICAgICAgeC5zID0gc3RyLmNoYXJDb2RlQXQoMCkgPT09IDQ1ID8gKCBzdHIgPSBzdHIuc2xpY2UoMSksIC0xICkgOiAxO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgYiA9IGIgfCAwO1xuICAgICAgICAgICAgICAgIHN0ciA9IG4gKyAnJztcblxuICAgICAgICAgICAgICAgIC8vIEVuc3VyZSByZXR1cm4gdmFsdWUgaXMgcm91bmRlZCB0byBERUNJTUFMX1BMQUNFUyBhcyB3aXRoIG90aGVyIGJhc2VzLlxuICAgICAgICAgICAgICAgIC8vIEFsbG93IGV4cG9uZW50aWFsIG5vdGF0aW9uIHRvIGJlIHVzZWQgd2l0aCBiYXNlIDEwIGFyZ3VtZW50LlxuICAgICAgICAgICAgICAgIGlmICggYiA9PSAxMCApIHtcbiAgICAgICAgICAgICAgICAgICAgeCA9IG5ldyBCaWdOdW1iZXIoIG4gaW5zdGFuY2VvZiBCaWdOdW1iZXIgPyBuIDogc3RyICk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiByb3VuZCggeCwgREVDSU1BTF9QTEFDRVMgKyB4LmUgKyAxLCBST1VORElOR19NT0RFICk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gQXZvaWQgcG90ZW50aWFsIGludGVycHJldGF0aW9uIG9mIEluZmluaXR5IGFuZCBOYU4gYXMgYmFzZSA0NCsgdmFsdWVzLlxuICAgICAgICAgICAgICAgIC8vIEFueSBudW1iZXIgaW4gZXhwb25lbnRpYWwgZm9ybSB3aWxsIGZhaWwgZHVlIHRvIHRoZSBbRWVdWystXS5cbiAgICAgICAgICAgICAgICBpZiAoICggbnVtID0gdHlwZW9mIG4gPT0gJ251bWJlcicgKSAmJiBuICogMCAhPSAwIHx8XG4gICAgICAgICAgICAgICAgICAhKCBuZXcgUmVnRXhwKCAnXi0/JyArICggYyA9ICdbJyArIEFMUEhBQkVULnNsaWNlKCAwLCBiICkgKyAnXSsnICkgK1xuICAgICAgICAgICAgICAgICAgICAnKD86XFxcXC4nICsgYyArICcpPyQnLGIgPCAzNyA/ICdpJyA6ICcnICkgKS50ZXN0KHN0cikgKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBwYXJzZU51bWVyaWMoIHgsIHN0ciwgbnVtLCBiICk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKG51bSkge1xuICAgICAgICAgICAgICAgICAgICB4LnMgPSAxIC8gbiA8IDAgPyAoIHN0ciA9IHN0ci5zbGljZSgxKSwgLTEgKSA6IDE7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKCBFUlJPUlMgJiYgc3RyLnJlcGxhY2UoIC9eMFxcLjAqfFxcLi8sICcnICkubGVuZ3RoID4gMTUgKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vICduZXcgQmlnTnVtYmVyKCkgbnVtYmVyIHR5cGUgaGFzIG1vcmUgdGhhbiAxNSBzaWduaWZpY2FudCBkaWdpdHM6IHtufSdcbiAgICAgICAgICAgICAgICAgICAgICAgIHJhaXNlKCBpZCwgdG9vTWFueURpZ2l0cywgbiApO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gUHJldmVudCBsYXRlciBjaGVjayBmb3IgbGVuZ3RoIG9uIGNvbnZlcnRlZCBudW1iZXIuXG4gICAgICAgICAgICAgICAgICAgIG51bSA9IGZhbHNlO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHgucyA9IHN0ci5jaGFyQ29kZUF0KDApID09PSA0NSA/ICggc3RyID0gc3RyLnNsaWNlKDEpLCAtMSApIDogMTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBzdHIgPSBjb252ZXJ0QmFzZSggc3RyLCAxMCwgYiwgeC5zICk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIERlY2ltYWwgcG9pbnQ/XG4gICAgICAgICAgICBpZiAoICggZSA9IHN0ci5pbmRleE9mKCcuJykgKSA+IC0xICkgc3RyID0gc3RyLnJlcGxhY2UoICcuJywgJycgKTtcblxuICAgICAgICAgICAgLy8gRXhwb25lbnRpYWwgZm9ybT9cbiAgICAgICAgICAgIGlmICggKCBpID0gc3RyLnNlYXJjaCggL2UvaSApICkgPiAwICkge1xuXG4gICAgICAgICAgICAgICAgLy8gRGV0ZXJtaW5lIGV4cG9uZW50LlxuICAgICAgICAgICAgICAgIGlmICggZSA8IDAgKSBlID0gaTtcbiAgICAgICAgICAgICAgICBlICs9ICtzdHIuc2xpY2UoIGkgKyAxICk7XG4gICAgICAgICAgICAgICAgc3RyID0gc3RyLnN1YnN0cmluZyggMCwgaSApO1xuICAgICAgICAgICAgfSBlbHNlIGlmICggZSA8IDAgKSB7XG5cbiAgICAgICAgICAgICAgICAvLyBJbnRlZ2VyLlxuICAgICAgICAgICAgICAgIGUgPSBzdHIubGVuZ3RoO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBEZXRlcm1pbmUgbGVhZGluZyB6ZXJvcy5cbiAgICAgICAgICAgIGZvciAoIGkgPSAwOyBzdHIuY2hhckNvZGVBdChpKSA9PT0gNDg7IGkrKyApO1xuXG4gICAgICAgICAgICAvLyBEZXRlcm1pbmUgdHJhaWxpbmcgemVyb3MuXG4gICAgICAgICAgICBmb3IgKCBsZW4gPSBzdHIubGVuZ3RoOyBzdHIuY2hhckNvZGVBdCgtLWxlbikgPT09IDQ4OyApO1xuICAgICAgICAgICAgc3RyID0gc3RyLnNsaWNlKCBpLCBsZW4gKyAxICk7XG5cbiAgICAgICAgICAgIGlmIChzdHIpIHtcbiAgICAgICAgICAgICAgICBsZW4gPSBzdHIubGVuZ3RoO1xuXG4gICAgICAgICAgICAgICAgLy8gRGlzYWxsb3cgbnVtYmVycyB3aXRoIG92ZXIgMTUgc2lnbmlmaWNhbnQgZGlnaXRzIGlmIG51bWJlciB0eXBlLlxuICAgICAgICAgICAgICAgIC8vICduZXcgQmlnTnVtYmVyKCkgbnVtYmVyIHR5cGUgaGFzIG1vcmUgdGhhbiAxNSBzaWduaWZpY2FudCBkaWdpdHM6IHtufSdcbiAgICAgICAgICAgICAgICBpZiAoIG51bSAmJiBFUlJPUlMgJiYgbGVuID4gMTUgKSByYWlzZSggaWQsIHRvb01hbnlEaWdpdHMsIHgucyAqIG4gKTtcblxuICAgICAgICAgICAgICAgIGUgPSBlIC0gaSAtIDE7XG5cbiAgICAgICAgICAgICAgICAgLy8gT3ZlcmZsb3c/XG4gICAgICAgICAgICAgICAgaWYgKCBlID4gTUFYX0VYUCApIHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBJbmZpbml0eS5cbiAgICAgICAgICAgICAgICAgICAgeC5jID0geC5lID0gbnVsbDtcblxuICAgICAgICAgICAgICAgIC8vIFVuZGVyZmxvdz9cbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCBlIDwgTUlOX0VYUCApIHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBaZXJvLlxuICAgICAgICAgICAgICAgICAgICB4LmMgPSBbIHguZSA9IDAgXTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICB4LmUgPSBlO1xuICAgICAgICAgICAgICAgICAgICB4LmMgPSBbXTtcblxuICAgICAgICAgICAgICAgICAgICAvLyBUcmFuc2Zvcm0gYmFzZVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIGUgaXMgdGhlIGJhc2UgMTAgZXhwb25lbnQuXG4gICAgICAgICAgICAgICAgICAgIC8vIGkgaXMgd2hlcmUgdG8gc2xpY2Ugc3RyIHRvIGdldCB0aGUgZmlyc3QgZWxlbWVudCBvZiB0aGUgY29lZmZpY2llbnQgYXJyYXkuXG4gICAgICAgICAgICAgICAgICAgIGkgPSAoIGUgKyAxICkgJSBMT0dfQkFTRTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCBlIDwgMCApIGkgKz0gTE9HX0JBU0U7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKCBpIDwgbGVuICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGkpIHguYy5wdXNoKCArc3RyLnNsaWNlKCAwLCBpICkgKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgZm9yICggbGVuIC09IExPR19CQVNFOyBpIDwgbGVuOyApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4LmMucHVzaCggK3N0ci5zbGljZSggaSwgaSArPSBMT0dfQkFTRSApICk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIHN0ciA9IHN0ci5zbGljZShpKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGkgPSBMT0dfQkFTRSAtIHN0ci5sZW5ndGg7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpIC09IGxlbjtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGZvciAoIDsgaS0tOyBzdHIgKz0gJzAnICk7XG4gICAgICAgICAgICAgICAgICAgIHguYy5wdXNoKCArc3RyICk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgICAgIC8vIFplcm8uXG4gICAgICAgICAgICAgICAgeC5jID0gWyB4LmUgPSAwIF07XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlkID0gMDtcbiAgICAgICAgfVxuXG5cbiAgICAgICAgLy8gQ09OU1RSVUNUT1IgUFJPUEVSVElFU1xuXG5cbiAgICAgICAgQmlnTnVtYmVyLmFub3RoZXIgPSBhbm90aGVyO1xuXG4gICAgICAgIEJpZ051bWJlci5ST1VORF9VUCA9IDA7XG4gICAgICAgIEJpZ051bWJlci5ST1VORF9ET1dOID0gMTtcbiAgICAgICAgQmlnTnVtYmVyLlJPVU5EX0NFSUwgPSAyO1xuICAgICAgICBCaWdOdW1iZXIuUk9VTkRfRkxPT1IgPSAzO1xuICAgICAgICBCaWdOdW1iZXIuUk9VTkRfSEFMRl9VUCA9IDQ7XG4gICAgICAgIEJpZ051bWJlci5ST1VORF9IQUxGX0RPV04gPSA1O1xuICAgICAgICBCaWdOdW1iZXIuUk9VTkRfSEFMRl9FVkVOID0gNjtcbiAgICAgICAgQmlnTnVtYmVyLlJPVU5EX0hBTEZfQ0VJTCA9IDc7XG4gICAgICAgIEJpZ051bWJlci5ST1VORF9IQUxGX0ZMT09SID0gODtcbiAgICAgICAgQmlnTnVtYmVyLkVVQ0xJRCA9IDk7XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBDb25maWd1cmUgaW5mcmVxdWVudGx5LWNoYW5naW5nIGxpYnJhcnktd2lkZSBzZXR0aW5ncy5cbiAgICAgICAgICpcbiAgICAgICAgICogQWNjZXB0IGFuIG9iamVjdCBvciBhbiBhcmd1bWVudCBsaXN0LCB3aXRoIG9uZSBvciBtYW55IG9mIHRoZSBmb2xsb3dpbmcgcHJvcGVydGllcyBvclxuICAgICAgICAgKiBwYXJhbWV0ZXJzIHJlc3BlY3RpdmVseTpcbiAgICAgICAgICpcbiAgICAgICAgICogICBERUNJTUFMX1BMQUNFUyAge251bWJlcn0gIEludGVnZXIsIDAgdG8gTUFYIGluY2x1c2l2ZVxuICAgICAgICAgKiAgIFJPVU5ESU5HX01PREUgICB7bnVtYmVyfSAgSW50ZWdlciwgMCB0byA4IGluY2x1c2l2ZVxuICAgICAgICAgKiAgIEVYUE9ORU5USUFMX0FUICB7bnVtYmVyfG51bWJlcltdfSAgSW50ZWdlciwgLU1BWCB0byBNQVggaW5jbHVzaXZlIG9yXG4gICAgICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbaW50ZWdlciAtTUFYIHRvIDAgaW5jbC4sIDAgdG8gTUFYIGluY2wuXVxuICAgICAgICAgKiAgIFJBTkdFICAgICAgICAgICB7bnVtYmVyfG51bWJlcltdfSAgTm9uLXplcm8gaW50ZWdlciwgLU1BWCB0byBNQVggaW5jbHVzaXZlIG9yXG4gICAgICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbaW50ZWdlciAtTUFYIHRvIC0xIGluY2wuLCBpbnRlZ2VyIDEgdG8gTUFYIGluY2wuXVxuICAgICAgICAgKiAgIEVSUk9SUyAgICAgICAgICB7Ym9vbGVhbnxudW1iZXJ9ICAgdHJ1ZSwgZmFsc2UsIDEgb3IgMFxuICAgICAgICAgKiAgIENSWVBUTyAgICAgICAgICB7Ym9vbGVhbnxudW1iZXJ9ICAgdHJ1ZSwgZmFsc2UsIDEgb3IgMFxuICAgICAgICAgKiAgIE1PRFVMT19NT0RFICAgICB7bnVtYmVyfSAgICAgICAgICAgMCB0byA5IGluY2x1c2l2ZVxuICAgICAgICAgKiAgIFBPV19QUkVDSVNJT04gICB7bnVtYmVyfSAgICAgICAgICAgMCB0byBNQVggaW5jbHVzaXZlXG4gICAgICAgICAqICAgRk9STUFUICAgICAgICAgIHtvYmplY3R9ICAgICAgICAgICBTZWUgQmlnTnVtYmVyLnByb3RvdHlwZS50b0Zvcm1hdFxuICAgICAgICAgKiAgICAgIGRlY2ltYWxTZXBhcmF0b3IgICAgICAge3N0cmluZ31cbiAgICAgICAgICogICAgICBncm91cFNlcGFyYXRvciAgICAgICAgIHtzdHJpbmd9XG4gICAgICAgICAqICAgICAgZ3JvdXBTaXplICAgICAgICAgICAgICB7bnVtYmVyfVxuICAgICAgICAgKiAgICAgIHNlY29uZGFyeUdyb3VwU2l6ZSAgICAge251bWJlcn1cbiAgICAgICAgICogICAgICBmcmFjdGlvbkdyb3VwU2VwYXJhdG9yIHtzdHJpbmd9XG4gICAgICAgICAqICAgICAgZnJhY3Rpb25Hcm91cFNpemUgICAgICB7bnVtYmVyfVxuICAgICAgICAgKlxuICAgICAgICAgKiAoVGhlIHZhbHVlcyBhc3NpZ25lZCB0byB0aGUgYWJvdmUgRk9STUFUIG9iamVjdCBwcm9wZXJ0aWVzIGFyZSBub3QgY2hlY2tlZCBmb3IgdmFsaWRpdHkuKVxuICAgICAgICAgKlxuICAgICAgICAgKiBFLmcuXG4gICAgICAgICAqIEJpZ051bWJlci5jb25maWcoMjAsIDQpIGlzIGVxdWl2YWxlbnQgdG9cbiAgICAgICAgICogQmlnTnVtYmVyLmNvbmZpZyh7IERFQ0lNQUxfUExBQ0VTIDogMjAsIFJPVU5ESU5HX01PREUgOiA0IH0pXG4gICAgICAgICAqXG4gICAgICAgICAqIElnbm9yZSBwcm9wZXJ0aWVzL3BhcmFtZXRlcnMgc2V0IHRvIG51bGwgb3IgdW5kZWZpbmVkLlxuICAgICAgICAgKiBSZXR1cm4gYW4gb2JqZWN0IHdpdGggdGhlIHByb3BlcnRpZXMgY3VycmVudCB2YWx1ZXMuXG4gICAgICAgICAqL1xuICAgICAgICBCaWdOdW1iZXIuY29uZmlnID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIHYsIHAsXG4gICAgICAgICAgICAgICAgaSA9IDAsXG4gICAgICAgICAgICAgICAgciA9IHt9LFxuICAgICAgICAgICAgICAgIGEgPSBhcmd1bWVudHMsXG4gICAgICAgICAgICAgICAgbyA9IGFbMF0sXG4gICAgICAgICAgICAgICAgaGFzID0gbyAmJiB0eXBlb2YgbyA9PSAnb2JqZWN0J1xuICAgICAgICAgICAgICAgICAgPyBmdW5jdGlvbiAoKSB7IGlmICggby5oYXNPd25Qcm9wZXJ0eShwKSApIHJldHVybiAoIHYgPSBvW3BdICkgIT0gbnVsbDsgfVxuICAgICAgICAgICAgICAgICAgOiBmdW5jdGlvbiAoKSB7IGlmICggYS5sZW5ndGggPiBpICkgcmV0dXJuICggdiA9IGFbaSsrXSApICE9IG51bGw7IH07XG5cbiAgICAgICAgICAgIC8vIERFQ0lNQUxfUExBQ0VTIHtudW1iZXJ9IEludGVnZXIsIDAgdG8gTUFYIGluY2x1c2l2ZS5cbiAgICAgICAgICAgIC8vICdjb25maWcoKSBERUNJTUFMX1BMQUNFUyBub3QgYW4gaW50ZWdlcjoge3Z9J1xuICAgICAgICAgICAgLy8gJ2NvbmZpZygpIERFQ0lNQUxfUExBQ0VTIG91dCBvZiByYW5nZToge3Z9J1xuICAgICAgICAgICAgaWYgKCBoYXMoIHAgPSAnREVDSU1BTF9QTEFDRVMnICkgJiYgaXNWYWxpZEludCggdiwgMCwgTUFYLCAyLCBwICkgKSB7XG4gICAgICAgICAgICAgICAgREVDSU1BTF9QTEFDRVMgPSB2IHwgMDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJbcF0gPSBERUNJTUFMX1BMQUNFUztcblxuICAgICAgICAgICAgLy8gUk9VTkRJTkdfTU9ERSB7bnVtYmVyfSBJbnRlZ2VyLCAwIHRvIDggaW5jbHVzaXZlLlxuICAgICAgICAgICAgLy8gJ2NvbmZpZygpIFJPVU5ESU5HX01PREUgbm90IGFuIGludGVnZXI6IHt2fSdcbiAgICAgICAgICAgIC8vICdjb25maWcoKSBST1VORElOR19NT0RFIG91dCBvZiByYW5nZToge3Z9J1xuICAgICAgICAgICAgaWYgKCBoYXMoIHAgPSAnUk9VTkRJTkdfTU9ERScgKSAmJiBpc1ZhbGlkSW50KCB2LCAwLCA4LCAyLCBwICkgKSB7XG4gICAgICAgICAgICAgICAgUk9VTkRJTkdfTU9ERSA9IHYgfCAwO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcltwXSA9IFJPVU5ESU5HX01PREU7XG5cbiAgICAgICAgICAgIC8vIEVYUE9ORU5USUFMX0FUIHtudW1iZXJ8bnVtYmVyW119XG4gICAgICAgICAgICAvLyBJbnRlZ2VyLCAtTUFYIHRvIE1BWCBpbmNsdXNpdmUgb3IgW2ludGVnZXIgLU1BWCB0byAwIGluY2x1c2l2ZSwgMCB0byBNQVggaW5jbHVzaXZlXS5cbiAgICAgICAgICAgIC8vICdjb25maWcoKSBFWFBPTkVOVElBTF9BVCBub3QgYW4gaW50ZWdlcjoge3Z9J1xuICAgICAgICAgICAgLy8gJ2NvbmZpZygpIEVYUE9ORU5USUFMX0FUIG91dCBvZiByYW5nZToge3Z9J1xuICAgICAgICAgICAgaWYgKCBoYXMoIHAgPSAnRVhQT05FTlRJQUxfQVQnICkgKSB7XG5cbiAgICAgICAgICAgICAgICBpZiAoIGlzQXJyYXkodikgKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmICggaXNWYWxpZEludCggdlswXSwgLU1BWCwgMCwgMiwgcCApICYmIGlzVmFsaWRJbnQoIHZbMV0sIDAsIE1BWCwgMiwgcCApICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgVE9fRVhQX05FRyA9IHZbMF0gfCAwO1xuICAgICAgICAgICAgICAgICAgICAgICAgVE9fRVhQX1BPUyA9IHZbMV0gfCAwO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmICggaXNWYWxpZEludCggdiwgLU1BWCwgTUFYLCAyLCBwICkgKSB7XG4gICAgICAgICAgICAgICAgICAgIFRPX0VYUF9ORUcgPSAtKCBUT19FWFBfUE9TID0gKCB2IDwgMCA/IC12IDogdiApIHwgMCApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJbcF0gPSBbIFRPX0VYUF9ORUcsIFRPX0VYUF9QT1MgXTtcblxuICAgICAgICAgICAgLy8gUkFOR0Uge251bWJlcnxudW1iZXJbXX0gTm9uLXplcm8gaW50ZWdlciwgLU1BWCB0byBNQVggaW5jbHVzaXZlIG9yXG4gICAgICAgICAgICAvLyBbaW50ZWdlciAtTUFYIHRvIC0xIGluY2x1c2l2ZSwgaW50ZWdlciAxIHRvIE1BWCBpbmNsdXNpdmVdLlxuICAgICAgICAgICAgLy8gJ2NvbmZpZygpIFJBTkdFIG5vdCBhbiBpbnRlZ2VyOiB7dn0nXG4gICAgICAgICAgICAvLyAnY29uZmlnKCkgUkFOR0UgY2Fubm90IGJlIHplcm86IHt2fSdcbiAgICAgICAgICAgIC8vICdjb25maWcoKSBSQU5HRSBvdXQgb2YgcmFuZ2U6IHt2fSdcbiAgICAgICAgICAgIGlmICggaGFzKCBwID0gJ1JBTkdFJyApICkge1xuXG4gICAgICAgICAgICAgICAgaWYgKCBpc0FycmF5KHYpICkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoIGlzVmFsaWRJbnQoIHZbMF0sIC1NQVgsIC0xLCAyLCBwICkgJiYgaXNWYWxpZEludCggdlsxXSwgMSwgTUFYLCAyLCBwICkgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBNSU5fRVhQID0gdlswXSB8IDA7XG4gICAgICAgICAgICAgICAgICAgICAgICBNQVhfRVhQID0gdlsxXSB8IDA7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCBpc1ZhbGlkSW50KCB2LCAtTUFYLCBNQVgsIDIsIHAgKSApIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCB2IHwgMCApIE1JTl9FWFAgPSAtKCBNQVhfRVhQID0gKCB2IDwgMCA/IC12IDogdiApIHwgMCApO1xuICAgICAgICAgICAgICAgICAgICBlbHNlIGlmIChFUlJPUlMpIHJhaXNlKCAyLCBwICsgJyBjYW5ub3QgYmUgemVybycsIHYgKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByW3BdID0gWyBNSU5fRVhQLCBNQVhfRVhQIF07XG5cbiAgICAgICAgICAgIC8vIEVSUk9SUyB7Ym9vbGVhbnxudW1iZXJ9IHRydWUsIGZhbHNlLCAxIG9yIDAuXG4gICAgICAgICAgICAvLyAnY29uZmlnKCkgRVJST1JTIG5vdCBhIGJvb2xlYW4gb3IgYmluYXJ5IGRpZ2l0OiB7dn0nXG4gICAgICAgICAgICBpZiAoIGhhcyggcCA9ICdFUlJPUlMnICkgKSB7XG5cbiAgICAgICAgICAgICAgICBpZiAoIHYgPT09ICEhdiB8fCB2ID09PSAxIHx8IHYgPT09IDAgKSB7XG4gICAgICAgICAgICAgICAgICAgIGlkID0gMDtcbiAgICAgICAgICAgICAgICAgICAgaXNWYWxpZEludCA9ICggRVJST1JTID0gISF2ICkgPyBpbnRWYWxpZGF0b3JXaXRoRXJyb3JzIDogaW50VmFsaWRhdG9yTm9FcnJvcnM7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChFUlJPUlMpIHtcbiAgICAgICAgICAgICAgICAgICAgcmFpc2UoIDIsIHAgKyBub3RCb29sLCB2ICk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcltwXSA9IEVSUk9SUztcblxuICAgICAgICAgICAgLy8gQ1JZUFRPIHtib29sZWFufG51bWJlcn0gdHJ1ZSwgZmFsc2UsIDEgb3IgMC5cbiAgICAgICAgICAgIC8vICdjb25maWcoKSBDUllQVE8gbm90IGEgYm9vbGVhbiBvciBiaW5hcnkgZGlnaXQ6IHt2fSdcbiAgICAgICAgICAgIC8vICdjb25maWcoKSBjcnlwdG8gdW5hdmFpbGFibGU6IHtjcnlwdG99J1xuICAgICAgICAgICAgaWYgKCBoYXMoIHAgPSAnQ1JZUFRPJyApICkge1xuXG4gICAgICAgICAgICAgICAgaWYgKCB2ID09PSAhIXYgfHwgdiA9PT0gMSB8fCB2ID09PSAwICkge1xuICAgICAgICAgICAgICAgICAgICBDUllQVE8gPSAhISggdiAmJiBjcnlwdG8gJiYgdHlwZW9mIGNyeXB0byA9PSAnb2JqZWN0JyApO1xuICAgICAgICAgICAgICAgICAgICBpZiAoIHYgJiYgIUNSWVBUTyAmJiBFUlJPUlMgKSByYWlzZSggMiwgJ2NyeXB0byB1bmF2YWlsYWJsZScsIGNyeXB0byApO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoRVJST1JTKSB7XG4gICAgICAgICAgICAgICAgICAgIHJhaXNlKCAyLCBwICsgbm90Qm9vbCwgdiApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJbcF0gPSBDUllQVE87XG5cbiAgICAgICAgICAgIC8vIE1PRFVMT19NT0RFIHtudW1iZXJ9IEludGVnZXIsIDAgdG8gOSBpbmNsdXNpdmUuXG4gICAgICAgICAgICAvLyAnY29uZmlnKCkgTU9EVUxPX01PREUgbm90IGFuIGludGVnZXI6IHt2fSdcbiAgICAgICAgICAgIC8vICdjb25maWcoKSBNT0RVTE9fTU9ERSBvdXQgb2YgcmFuZ2U6IHt2fSdcbiAgICAgICAgICAgIGlmICggaGFzKCBwID0gJ01PRFVMT19NT0RFJyApICYmIGlzVmFsaWRJbnQoIHYsIDAsIDksIDIsIHAgKSApIHtcbiAgICAgICAgICAgICAgICBNT0RVTE9fTU9ERSA9IHYgfCAwO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcltwXSA9IE1PRFVMT19NT0RFO1xuXG4gICAgICAgICAgICAvLyBQT1dfUFJFQ0lTSU9OIHtudW1iZXJ9IEludGVnZXIsIDAgdG8gTUFYIGluY2x1c2l2ZS5cbiAgICAgICAgICAgIC8vICdjb25maWcoKSBQT1dfUFJFQ0lTSU9OIG5vdCBhbiBpbnRlZ2VyOiB7dn0nXG4gICAgICAgICAgICAvLyAnY29uZmlnKCkgUE9XX1BSRUNJU0lPTiBvdXQgb2YgcmFuZ2U6IHt2fSdcbiAgICAgICAgICAgIGlmICggaGFzKCBwID0gJ1BPV19QUkVDSVNJT04nICkgJiYgaXNWYWxpZEludCggdiwgMCwgTUFYLCAyLCBwICkgKSB7XG4gICAgICAgICAgICAgICAgUE9XX1BSRUNJU0lPTiA9IHYgfCAwO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcltwXSA9IFBPV19QUkVDSVNJT047XG5cbiAgICAgICAgICAgIC8vIEZPUk1BVCB7b2JqZWN0fVxuICAgICAgICAgICAgLy8gJ2NvbmZpZygpIEZPUk1BVCBub3QgYW4gb2JqZWN0OiB7dn0nXG4gICAgICAgICAgICBpZiAoIGhhcyggcCA9ICdGT1JNQVQnICkgKSB7XG5cbiAgICAgICAgICAgICAgICBpZiAoIHR5cGVvZiB2ID09ICdvYmplY3QnICkge1xuICAgICAgICAgICAgICAgICAgICBGT1JNQVQgPSB2O1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoRVJST1JTKSB7XG4gICAgICAgICAgICAgICAgICAgIHJhaXNlKCAyLCBwICsgJyBub3QgYW4gb2JqZWN0JywgdiApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJbcF0gPSBGT1JNQVQ7XG5cbiAgICAgICAgICAgIHJldHVybiByO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIGEgbmV3IEJpZ051bWJlciB3aG9zZSB2YWx1ZSBpcyB0aGUgbWF4aW11bSBvZiB0aGUgYXJndW1lbnRzLlxuICAgICAgICAgKlxuICAgICAgICAgKiBhcmd1bWVudHMge251bWJlcnxzdHJpbmd8QmlnTnVtYmVyfVxuICAgICAgICAgKi9cbiAgICAgICAgQmlnTnVtYmVyLm1heCA9IGZ1bmN0aW9uICgpIHsgcmV0dXJuIG1heE9yTWluKCBhcmd1bWVudHMsIFAubHQgKTsgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiBhIG5ldyBCaWdOdW1iZXIgd2hvc2UgdmFsdWUgaXMgdGhlIG1pbmltdW0gb2YgdGhlIGFyZ3VtZW50cy5cbiAgICAgICAgICpcbiAgICAgICAgICogYXJndW1lbnRzIHtudW1iZXJ8c3RyaW5nfEJpZ051bWJlcn1cbiAgICAgICAgICovXG4gICAgICAgIEJpZ051bWJlci5taW4gPSBmdW5jdGlvbiAoKSB7IHJldHVybiBtYXhPck1pbiggYXJndW1lbnRzLCBQLmd0ICk7IH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gYSBuZXcgQmlnTnVtYmVyIHdpdGggYSByYW5kb20gdmFsdWUgZXF1YWwgdG8gb3IgZ3JlYXRlciB0aGFuIDAgYW5kIGxlc3MgdGhhbiAxLFxuICAgICAgICAgKiBhbmQgd2l0aCBkcCwgb3IgREVDSU1BTF9QTEFDRVMgaWYgZHAgaXMgb21pdHRlZCwgZGVjaW1hbCBwbGFjZXMgKG9yIGxlc3MgaWYgdHJhaWxpbmdcbiAgICAgICAgICogemVyb3MgYXJlIHByb2R1Y2VkKS5cbiAgICAgICAgICpcbiAgICAgICAgICogW2RwXSB7bnVtYmVyfSBEZWNpbWFsIHBsYWNlcy4gSW50ZWdlciwgMCB0byBNQVggaW5jbHVzaXZlLlxuICAgICAgICAgKlxuICAgICAgICAgKiAncmFuZG9tKCkgZGVjaW1hbCBwbGFjZXMgbm90IGFuIGludGVnZXI6IHtkcH0nXG4gICAgICAgICAqICdyYW5kb20oKSBkZWNpbWFsIHBsYWNlcyBvdXQgb2YgcmFuZ2U6IHtkcH0nXG4gICAgICAgICAqICdyYW5kb20oKSBjcnlwdG8gdW5hdmFpbGFibGU6IHtjcnlwdG99J1xuICAgICAgICAgKi9cbiAgICAgICAgQmlnTnVtYmVyLnJhbmRvbSA9IChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgcG93Ml81MyA9IDB4MjAwMDAwMDAwMDAwMDA7XG5cbiAgICAgICAgICAgIC8vIFJldHVybiBhIDUzIGJpdCBpbnRlZ2VyIG4sIHdoZXJlIDAgPD0gbiA8IDkwMDcxOTkyNTQ3NDA5OTIuXG4gICAgICAgICAgICAvLyBDaGVjayBpZiBNYXRoLnJhbmRvbSgpIHByb2R1Y2VzIG1vcmUgdGhhbiAzMiBiaXRzIG9mIHJhbmRvbW5lc3MuXG4gICAgICAgICAgICAvLyBJZiBpdCBkb2VzLCBhc3N1bWUgYXQgbGVhc3QgNTMgYml0cyBhcmUgcHJvZHVjZWQsIG90aGVyd2lzZSBhc3N1bWUgYXQgbGVhc3QgMzAgYml0cy5cbiAgICAgICAgICAgIC8vIDB4NDAwMDAwMDAgaXMgMl4zMCwgMHg4MDAwMDAgaXMgMl4yMywgMHgxZmZmZmYgaXMgMl4yMSAtIDEuXG4gICAgICAgICAgICB2YXIgcmFuZG9tNTNiaXRJbnQgPSAoTWF0aC5yYW5kb20oKSAqIHBvdzJfNTMpICYgMHgxZmZmZmZcbiAgICAgICAgICAgICAgPyBmdW5jdGlvbiAoKSB7IHJldHVybiBtYXRoZmxvb3IoIE1hdGgucmFuZG9tKCkgKiBwb3cyXzUzICk7IH1cbiAgICAgICAgICAgICAgOiBmdW5jdGlvbiAoKSB7IHJldHVybiAoKE1hdGgucmFuZG9tKCkgKiAweDQwMDAwMDAwIHwgMCkgKiAweDgwMDAwMCkgK1xuICAgICAgICAgICAgICAgICAgKE1hdGgucmFuZG9tKCkgKiAweDgwMDAwMCB8IDApOyB9O1xuXG4gICAgICAgICAgICByZXR1cm4gZnVuY3Rpb24gKGRwKSB7XG4gICAgICAgICAgICAgICAgdmFyIGEsIGIsIGUsIGssIHYsXG4gICAgICAgICAgICAgICAgICAgIGkgPSAwLFxuICAgICAgICAgICAgICAgICAgICBjID0gW10sXG4gICAgICAgICAgICAgICAgICAgIHJhbmQgPSBuZXcgQmlnTnVtYmVyKE9ORSk7XG5cbiAgICAgICAgICAgICAgICBkcCA9IGRwID09IG51bGwgfHwgIWlzVmFsaWRJbnQoIGRwLCAwLCBNQVgsIDE0ICkgPyBERUNJTUFMX1BMQUNFUyA6IGRwIHwgMDtcbiAgICAgICAgICAgICAgICBrID0gbWF0aGNlaWwoIGRwIC8gTE9HX0JBU0UgKTtcblxuICAgICAgICAgICAgICAgIGlmIChDUllQVE8pIHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBCcm93c2VycyBzdXBwb3J0aW5nIGNyeXB0by5nZXRSYW5kb21WYWx1ZXMuXG4gICAgICAgICAgICAgICAgICAgIGlmICggY3J5cHRvICYmIGNyeXB0by5nZXRSYW5kb21WYWx1ZXMgKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGEgPSBjcnlwdG8uZ2V0UmFuZG9tVmFsdWVzKCBuZXcgVWludDMyQXJyYXkoIGsgKj0gMiApICk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoIDsgaSA8IGs7ICkge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gNTMgYml0czpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyAoKE1hdGgucG93KDIsIDMyKSAtIDEpICogTWF0aC5wb3coMiwgMjEpKS50b1N0cmluZygyKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIDExMTExIDExMTExMTExIDExMTExMTExIDExMTExMTExIDExMTAwMDAwIDAwMDAwMDAwIDAwMDAwMDAwXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gKChNYXRoLnBvdygyLCAzMikgLSAxKSA+Pj4gMTEpLnRvU3RyaW5nKDIpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMTExMTEgMTExMTExMTEgMTExMTExMTFcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyAweDIwMDAwIGlzIDJeMjEuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdiA9IGFbaV0gKiAweDIwMDAwICsgKGFbaSArIDFdID4+PiAxMSk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBSZWplY3Rpb24gc2FtcGxpbmc6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gMCA8PSB2IDwgOTAwNzE5OTI1NDc0MDk5MlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFByb2JhYmlsaXR5IHRoYXQgdiA+PSA5ZTE1LCBpc1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIDcxOTkyNTQ3NDA5OTIgLyA5MDA3MTk5MjU0NzQwOTkyIH49IDAuMDAwOCwgaS5lLiAxIGluIDEyNTFcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoIHYgPj0gOWUxNSApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYiA9IGNyeXB0by5nZXRSYW5kb21WYWx1ZXMoIG5ldyBVaW50MzJBcnJheSgyKSApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhW2ldID0gYlswXTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYVtpICsgMV0gPSBiWzFdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gMCA8PSB2IDw9IDg5OTk5OTk5OTk5OTk5OTlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gMCA8PSAodiAlIDFlMTQpIDw9IDk5OTk5OTk5OTk5OTk5XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMucHVzaCggdiAlIDFlMTQgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaSArPSAyO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGkgPSBrIC8gMjtcblxuICAgICAgICAgICAgICAgICAgICAvLyBOb2RlLmpzIHN1cHBvcnRpbmcgY3J5cHRvLnJhbmRvbUJ5dGVzLlxuICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCBjcnlwdG8gJiYgY3J5cHRvLnJhbmRvbUJ5dGVzICkge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBidWZmZXJcbiAgICAgICAgICAgICAgICAgICAgICAgIGEgPSBjcnlwdG8ucmFuZG9tQnl0ZXMoIGsgKj0gNyApO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBmb3IgKCA7IGkgPCBrOyApIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIDB4MTAwMDAwMDAwMDAwMCBpcyAyXjQ4LCAweDEwMDAwMDAwMDAwIGlzIDJeNDBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyAweDEwMDAwMDAwMCBpcyAyXjMyLCAweDEwMDAwMDAgaXMgMl4yNFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIDExMTExIDExMTExMTExIDExMTExMTExIDExMTExMTExIDExMTExMTExIDExMTExMTExIDExMTExMTExXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gMCA8PSB2IDwgOTAwNzE5OTI1NDc0MDk5MlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHYgPSAoICggYVtpXSAmIDMxICkgKiAweDEwMDAwMDAwMDAwMDAgKSArICggYVtpICsgMV0gKiAweDEwMDAwMDAwMDAwICkgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICggYVtpICsgMl0gKiAweDEwMDAwMDAwMCApICsgKCBhW2kgKyAzXSAqIDB4MTAwMDAwMCApICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoIGFbaSArIDRdIDw8IDE2ICkgKyAoIGFbaSArIDVdIDw8IDggKSArIGFbaSArIDZdO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCB2ID49IDllMTUgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNyeXB0by5yYW5kb21CeXRlcyg3KS5jb3B5KCBhLCBpICk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyAwIDw9ICh2ICUgMWUxNCkgPD0gOTk5OTk5OTk5OTk5OTlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYy5wdXNoKCB2ICUgMWUxNCApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpICs9IDc7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgaSA9IGsgLyA3O1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKEVSUk9SUykge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmFpc2UoIDE0LCAnY3J5cHRvIHVuYXZhaWxhYmxlJywgY3J5cHRvICk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBVc2UgTWF0aC5yYW5kb206IENSWVBUTyBpcyBmYWxzZSBvciBjcnlwdG8gaXMgdW5hdmFpbGFibGUgYW5kIEVSUk9SUyBpcyBmYWxzZS5cbiAgICAgICAgICAgICAgICBpZiAoIWkpIHtcblxuICAgICAgICAgICAgICAgICAgICBmb3IgKCA7IGkgPCBrOyApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHYgPSByYW5kb201M2JpdEludCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCB2IDwgOWUxNSApIGNbaSsrXSA9IHYgJSAxZTE0O1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgayA9IGNbLS1pXTtcbiAgICAgICAgICAgICAgICBkcCAlPSBMT0dfQkFTRTtcblxuICAgICAgICAgICAgICAgIC8vIENvbnZlcnQgdHJhaWxpbmcgZGlnaXRzIHRvIHplcm9zIGFjY29yZGluZyB0byBkcC5cbiAgICAgICAgICAgICAgICBpZiAoIGsgJiYgZHAgKSB7XG4gICAgICAgICAgICAgICAgICAgIHYgPSBQT1dTX1RFTltMT0dfQkFTRSAtIGRwXTtcbiAgICAgICAgICAgICAgICAgICAgY1tpXSA9IG1hdGhmbG9vciggayAvIHYgKSAqIHY7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gUmVtb3ZlIHRyYWlsaW5nIGVsZW1lbnRzIHdoaWNoIGFyZSB6ZXJvLlxuICAgICAgICAgICAgICAgIGZvciAoIDsgY1tpXSA9PT0gMDsgYy5wb3AoKSwgaS0tICk7XG5cbiAgICAgICAgICAgICAgICAvLyBaZXJvP1xuICAgICAgICAgICAgICAgIGlmICggaSA8IDAgKSB7XG4gICAgICAgICAgICAgICAgICAgIGMgPSBbIGUgPSAwIF07XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBSZW1vdmUgbGVhZGluZyBlbGVtZW50cyB3aGljaCBhcmUgemVybyBhbmQgYWRqdXN0IGV4cG9uZW50IGFjY29yZGluZ2x5LlxuICAgICAgICAgICAgICAgICAgICBmb3IgKCBlID0gLTEgOyBjWzBdID09PSAwOyBjLnNoaWZ0KCksIGUgLT0gTE9HX0JBU0UpO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIENvdW50IHRoZSBkaWdpdHMgb2YgdGhlIGZpcnN0IGVsZW1lbnQgb2YgYyB0byBkZXRlcm1pbmUgbGVhZGluZyB6ZXJvcywgYW5kLi4uXG4gICAgICAgICAgICAgICAgICAgIGZvciAoIGkgPSAxLCB2ID0gY1swXTsgdiA+PSAxMDsgdiAvPSAxMCwgaSsrKTtcblxuICAgICAgICAgICAgICAgICAgICAvLyBhZGp1c3QgdGhlIGV4cG9uZW50IGFjY29yZGluZ2x5LlxuICAgICAgICAgICAgICAgICAgICBpZiAoIGkgPCBMT0dfQkFTRSApIGUgLT0gTE9HX0JBU0UgLSBpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHJhbmQuZSA9IGU7XG4gICAgICAgICAgICAgICAgcmFuZC5jID0gYztcbiAgICAgICAgICAgICAgICByZXR1cm4gcmFuZDtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pKCk7XG5cblxuICAgICAgICAvLyBQUklWQVRFIEZVTkNUSU9OU1xuXG5cbiAgICAgICAgLy8gQ29udmVydCBhIG51bWVyaWMgc3RyaW5nIG9mIGJhc2VJbiB0byBhIG51bWVyaWMgc3RyaW5nIG9mIGJhc2VPdXQuXG4gICAgICAgIGZ1bmN0aW9uIGNvbnZlcnRCYXNlKCBzdHIsIGJhc2VPdXQsIGJhc2VJbiwgc2lnbiApIHtcbiAgICAgICAgICAgIHZhciBkLCBlLCBrLCByLCB4LCB4YywgeSxcbiAgICAgICAgICAgICAgICBpID0gc3RyLmluZGV4T2YoICcuJyApLFxuICAgICAgICAgICAgICAgIGRwID0gREVDSU1BTF9QTEFDRVMsXG4gICAgICAgICAgICAgICAgcm0gPSBST1VORElOR19NT0RFO1xuXG4gICAgICAgICAgICBpZiAoIGJhc2VJbiA8IDM3ICkgc3RyID0gc3RyLnRvTG93ZXJDYXNlKCk7XG5cbiAgICAgICAgICAgIC8vIE5vbi1pbnRlZ2VyLlxuICAgICAgICAgICAgaWYgKCBpID49IDAgKSB7XG4gICAgICAgICAgICAgICAgayA9IFBPV19QUkVDSVNJT047XG5cbiAgICAgICAgICAgICAgICAvLyBVbmxpbWl0ZWQgcHJlY2lzaW9uLlxuICAgICAgICAgICAgICAgIFBPV19QUkVDSVNJT04gPSAwO1xuICAgICAgICAgICAgICAgIHN0ciA9IHN0ci5yZXBsYWNlKCAnLicsICcnICk7XG4gICAgICAgICAgICAgICAgeSA9IG5ldyBCaWdOdW1iZXIoYmFzZUluKTtcbiAgICAgICAgICAgICAgICB4ID0geS5wb3coIHN0ci5sZW5ndGggLSBpICk7XG4gICAgICAgICAgICAgICAgUE9XX1BSRUNJU0lPTiA9IGs7XG5cbiAgICAgICAgICAgICAgICAvLyBDb252ZXJ0IHN0ciBhcyBpZiBhbiBpbnRlZ2VyLCB0aGVuIHJlc3RvcmUgdGhlIGZyYWN0aW9uIHBhcnQgYnkgZGl2aWRpbmcgdGhlXG4gICAgICAgICAgICAgICAgLy8gcmVzdWx0IGJ5IGl0cyBiYXNlIHJhaXNlZCB0byBhIHBvd2VyLlxuICAgICAgICAgICAgICAgIHkuYyA9IHRvQmFzZU91dCggdG9GaXhlZFBvaW50KCBjb2VmZlRvU3RyaW5nKCB4LmMgKSwgeC5lICksIDEwLCBiYXNlT3V0ICk7XG4gICAgICAgICAgICAgICAgeS5lID0geS5jLmxlbmd0aDtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gQ29udmVydCB0aGUgbnVtYmVyIGFzIGludGVnZXIuXG4gICAgICAgICAgICB4YyA9IHRvQmFzZU91dCggc3RyLCBiYXNlSW4sIGJhc2VPdXQgKTtcbiAgICAgICAgICAgIGUgPSBrID0geGMubGVuZ3RoO1xuXG4gICAgICAgICAgICAvLyBSZW1vdmUgdHJhaWxpbmcgemVyb3MuXG4gICAgICAgICAgICBmb3IgKCA7IHhjWy0ta10gPT0gMDsgeGMucG9wKCkgKTtcbiAgICAgICAgICAgIGlmICggIXhjWzBdICkgcmV0dXJuICcwJztcblxuICAgICAgICAgICAgaWYgKCBpIDwgMCApIHtcbiAgICAgICAgICAgICAgICAtLWU7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHguYyA9IHhjO1xuICAgICAgICAgICAgICAgIHguZSA9IGU7XG5cbiAgICAgICAgICAgICAgICAvLyBzaWduIGlzIG5lZWRlZCBmb3IgY29ycmVjdCByb3VuZGluZy5cbiAgICAgICAgICAgICAgICB4LnMgPSBzaWduO1xuICAgICAgICAgICAgICAgIHggPSBkaXYoIHgsIHksIGRwLCBybSwgYmFzZU91dCApO1xuICAgICAgICAgICAgICAgIHhjID0geC5jO1xuICAgICAgICAgICAgICAgIHIgPSB4LnI7XG4gICAgICAgICAgICAgICAgZSA9IHguZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgZCA9IGUgKyBkcCArIDE7XG5cbiAgICAgICAgICAgIC8vIFRoZSByb3VuZGluZyBkaWdpdCwgaS5lLiB0aGUgZGlnaXQgdG8gdGhlIHJpZ2h0IG9mIHRoZSBkaWdpdCB0aGF0IG1heSBiZSByb3VuZGVkIHVwLlxuICAgICAgICAgICAgaSA9IHhjW2RdO1xuICAgICAgICAgICAgayA9IGJhc2VPdXQgLyAyO1xuICAgICAgICAgICAgciA9IHIgfHwgZCA8IDAgfHwgeGNbZCArIDFdICE9IG51bGw7XG5cbiAgICAgICAgICAgIHIgPSBybSA8IDQgPyAoIGkgIT0gbnVsbCB8fCByICkgJiYgKCBybSA9PSAwIHx8IHJtID09ICggeC5zIDwgMCA/IDMgOiAyICkgKVxuICAgICAgICAgICAgICAgICAgICAgICA6IGkgPiBrIHx8IGkgPT0gayAmJiggcm0gPT0gNCB8fCByIHx8IHJtID09IDYgJiYgeGNbZCAtIDFdICYgMSB8fFxuICAgICAgICAgICAgICAgICAgICAgICAgIHJtID09ICggeC5zIDwgMCA/IDggOiA3ICkgKTtcblxuICAgICAgICAgICAgaWYgKCBkIDwgMSB8fCAheGNbMF0gKSB7XG5cbiAgICAgICAgICAgICAgICAvLyAxXi1kcCBvciAwLlxuICAgICAgICAgICAgICAgIHN0ciA9IHIgPyB0b0ZpeGVkUG9pbnQoICcxJywgLWRwICkgOiAnMCc7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHhjLmxlbmd0aCA9IGQ7XG5cbiAgICAgICAgICAgICAgICBpZiAocikge1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIFJvdW5kaW5nIHVwIG1heSBtZWFuIHRoZSBwcmV2aW91cyBkaWdpdCBoYXMgdG8gYmUgcm91bmRlZCB1cCBhbmQgc28gb24uXG4gICAgICAgICAgICAgICAgICAgIGZvciAoIC0tYmFzZU91dDsgKyt4Y1stLWRdID4gYmFzZU91dDsgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB4Y1tkXSA9IDA7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICggIWQgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgKytlO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhjLnVuc2hpZnQoMSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBEZXRlcm1pbmUgdHJhaWxpbmcgemVyb3MuXG4gICAgICAgICAgICAgICAgZm9yICggayA9IHhjLmxlbmd0aDsgIXhjWy0ta107ICk7XG5cbiAgICAgICAgICAgICAgICAvLyBFLmcuIFs0LCAxMSwgMTVdIGJlY29tZXMgNGJmLlxuICAgICAgICAgICAgICAgIGZvciAoIGkgPSAwLCBzdHIgPSAnJzsgaSA8PSBrOyBzdHIgKz0gQUxQSEFCRVQuY2hhckF0KCB4Y1tpKytdICkgKTtcbiAgICAgICAgICAgICAgICBzdHIgPSB0b0ZpeGVkUG9pbnQoIHN0ciwgZSApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBUaGUgY2FsbGVyIHdpbGwgYWRkIHRoZSBzaWduLlxuICAgICAgICAgICAgcmV0dXJuIHN0cjtcbiAgICAgICAgfVxuXG5cbiAgICAgICAgLy8gUGVyZm9ybSBkaXZpc2lvbiBpbiB0aGUgc3BlY2lmaWVkIGJhc2UuIENhbGxlZCBieSBkaXYgYW5kIGNvbnZlcnRCYXNlLlxuICAgICAgICBkaXYgPSAoZnVuY3Rpb24gKCkge1xuXG4gICAgICAgICAgICAvLyBBc3N1bWUgbm9uLXplcm8geCBhbmQgay5cbiAgICAgICAgICAgIGZ1bmN0aW9uIG11bHRpcGx5KCB4LCBrLCBiYXNlICkge1xuICAgICAgICAgICAgICAgIHZhciBtLCB0ZW1wLCB4bG8sIHhoaSxcbiAgICAgICAgICAgICAgICAgICAgY2FycnkgPSAwLFxuICAgICAgICAgICAgICAgICAgICBpID0geC5sZW5ndGgsXG4gICAgICAgICAgICAgICAgICAgIGtsbyA9IGsgJSBTUVJUX0JBU0UsXG4gICAgICAgICAgICAgICAgICAgIGtoaSA9IGsgLyBTUVJUX0JBU0UgfCAwO1xuXG4gICAgICAgICAgICAgICAgZm9yICggeCA9IHguc2xpY2UoKTsgaS0tOyApIHtcbiAgICAgICAgICAgICAgICAgICAgeGxvID0geFtpXSAlIFNRUlRfQkFTRTtcbiAgICAgICAgICAgICAgICAgICAgeGhpID0geFtpXSAvIFNRUlRfQkFTRSB8IDA7XG4gICAgICAgICAgICAgICAgICAgIG0gPSBraGkgKiB4bG8gKyB4aGkgKiBrbG87XG4gICAgICAgICAgICAgICAgICAgIHRlbXAgPSBrbG8gKiB4bG8gKyAoICggbSAlIFNRUlRfQkFTRSApICogU1FSVF9CQVNFICkgKyBjYXJyeTtcbiAgICAgICAgICAgICAgICAgICAgY2FycnkgPSAoIHRlbXAgLyBiYXNlIHwgMCApICsgKCBtIC8gU1FSVF9CQVNFIHwgMCApICsga2hpICogeGhpO1xuICAgICAgICAgICAgICAgICAgICB4W2ldID0gdGVtcCAlIGJhc2U7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKGNhcnJ5KSB4LnVuc2hpZnQoY2FycnkpO1xuXG4gICAgICAgICAgICAgICAgcmV0dXJuIHg7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGZ1bmN0aW9uIGNvbXBhcmUoIGEsIGIsIGFMLCBiTCApIHtcbiAgICAgICAgICAgICAgICB2YXIgaSwgY21wO1xuXG4gICAgICAgICAgICAgICAgaWYgKCBhTCAhPSBiTCApIHtcbiAgICAgICAgICAgICAgICAgICAgY21wID0gYUwgPiBiTCA/IDEgOiAtMTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuXG4gICAgICAgICAgICAgICAgICAgIGZvciAoIGkgPSBjbXAgPSAwOyBpIDwgYUw7IGkrKyApIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCBhW2ldICE9IGJbaV0gKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY21wID0gYVtpXSA+IGJbaV0gPyAxIDogLTE7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuIGNtcDtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgZnVuY3Rpb24gc3VidHJhY3QoIGEsIGIsIGFMLCBiYXNlICkge1xuICAgICAgICAgICAgICAgIHZhciBpID0gMDtcblxuICAgICAgICAgICAgICAgIC8vIFN1YnRyYWN0IGIgZnJvbSBhLlxuICAgICAgICAgICAgICAgIGZvciAoIDsgYUwtLTsgKSB7XG4gICAgICAgICAgICAgICAgICAgIGFbYUxdIC09IGk7XG4gICAgICAgICAgICAgICAgICAgIGkgPSBhW2FMXSA8IGJbYUxdID8gMSA6IDA7XG4gICAgICAgICAgICAgICAgICAgIGFbYUxdID0gaSAqIGJhc2UgKyBhW2FMXSAtIGJbYUxdO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIFJlbW92ZSBsZWFkaW5nIHplcm9zLlxuICAgICAgICAgICAgICAgIGZvciAoIDsgIWFbMF0gJiYgYS5sZW5ndGggPiAxOyBhLnNoaWZ0KCkgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8geDogZGl2aWRlbmQsIHk6IGRpdmlzb3IuXG4gICAgICAgICAgICByZXR1cm4gZnVuY3Rpb24gKCB4LCB5LCBkcCwgcm0sIGJhc2UgKSB7XG4gICAgICAgICAgICAgICAgdmFyIGNtcCwgZSwgaSwgbW9yZSwgbiwgcHJvZCwgcHJvZEwsIHEsIHFjLCByZW0sIHJlbUwsIHJlbTAsIHhpLCB4TCwgeWMwLFxuICAgICAgICAgICAgICAgICAgICB5TCwgeXosXG4gICAgICAgICAgICAgICAgICAgIHMgPSB4LnMgPT0geS5zID8gMSA6IC0xLFxuICAgICAgICAgICAgICAgICAgICB4YyA9IHguYyxcbiAgICAgICAgICAgICAgICAgICAgeWMgPSB5LmM7XG5cbiAgICAgICAgICAgICAgICAvLyBFaXRoZXIgTmFOLCBJbmZpbml0eSBvciAwP1xuICAgICAgICAgICAgICAgIGlmICggIXhjIHx8ICF4Y1swXSB8fCAheWMgfHwgIXljWzBdICkge1xuXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBuZXcgQmlnTnVtYmVyKFxuXG4gICAgICAgICAgICAgICAgICAgICAgLy8gUmV0dXJuIE5hTiBpZiBlaXRoZXIgTmFOLCBvciBib3RoIEluZmluaXR5IG9yIDAuXG4gICAgICAgICAgICAgICAgICAgICAgIXgucyB8fCAheS5zIHx8ICggeGMgPyB5YyAmJiB4Y1swXSA9PSB5Y1swXSA6ICF5YyApID8gTmFOIDpcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gUmV0dXJuIMKxMCBpZiB4IGlzIMKxMCBvciB5IGlzIMKxSW5maW5pdHksIG9yIHJldHVybiDCsUluZmluaXR5IGFzIHkgaXMgwrEwLlxuICAgICAgICAgICAgICAgICAgICAgICAgeGMgJiYgeGNbMF0gPT0gMCB8fCAheWMgPyBzICogMCA6IHMgLyAwXG4gICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgcSA9IG5ldyBCaWdOdW1iZXIocyk7XG4gICAgICAgICAgICAgICAgcWMgPSBxLmMgPSBbXTtcbiAgICAgICAgICAgICAgICBlID0geC5lIC0geS5lO1xuICAgICAgICAgICAgICAgIHMgPSBkcCArIGUgKyAxO1xuXG4gICAgICAgICAgICAgICAgaWYgKCAhYmFzZSApIHtcbiAgICAgICAgICAgICAgICAgICAgYmFzZSA9IEJBU0U7XG4gICAgICAgICAgICAgICAgICAgIGUgPSBiaXRGbG9vciggeC5lIC8gTE9HX0JBU0UgKSAtIGJpdEZsb29yKCB5LmUgLyBMT0dfQkFTRSApO1xuICAgICAgICAgICAgICAgICAgICBzID0gcyAvIExPR19CQVNFIHwgMDtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBSZXN1bHQgZXhwb25lbnQgbWF5IGJlIG9uZSBsZXNzIHRoZW4gdGhlIGN1cnJlbnQgdmFsdWUgb2YgZS5cbiAgICAgICAgICAgICAgICAvLyBUaGUgY29lZmZpY2llbnRzIG9mIHRoZSBCaWdOdW1iZXJzIGZyb20gY29udmVydEJhc2UgbWF5IGhhdmUgdHJhaWxpbmcgemVyb3MuXG4gICAgICAgICAgICAgICAgZm9yICggaSA9IDA7IHljW2ldID09ICggeGNbaV0gfHwgMCApOyBpKysgKTtcbiAgICAgICAgICAgICAgICBpZiAoIHljW2ldID4gKCB4Y1tpXSB8fCAwICkgKSBlLS07XG5cbiAgICAgICAgICAgICAgICBpZiAoIHMgPCAwICkge1xuICAgICAgICAgICAgICAgICAgICBxYy5wdXNoKDEpO1xuICAgICAgICAgICAgICAgICAgICBtb3JlID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICB4TCA9IHhjLmxlbmd0aDtcbiAgICAgICAgICAgICAgICAgICAgeUwgPSB5Yy5sZW5ndGg7XG4gICAgICAgICAgICAgICAgICAgIGkgPSAwO1xuICAgICAgICAgICAgICAgICAgICBzICs9IDI7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gTm9ybWFsaXNlIHhjIGFuZCB5YyBzbyBoaWdoZXN0IG9yZGVyIGRpZ2l0IG9mIHljIGlzID49IGJhc2UgLyAyLlxuXG4gICAgICAgICAgICAgICAgICAgIG4gPSBtYXRoZmxvb3IoIGJhc2UgLyAoIHljWzBdICsgMSApICk7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gTm90IG5lY2Vzc2FyeSwgYnV0IHRvIGhhbmRsZSBvZGQgYmFzZXMgd2hlcmUgeWNbMF0gPT0gKCBiYXNlIC8gMiApIC0gMS5cbiAgICAgICAgICAgICAgICAgICAgLy8gaWYgKCBuID4gMSB8fCBuKysgPT0gMSAmJiB5Y1swXSA8IGJhc2UgLyAyICkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoIG4gPiAxICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgeWMgPSBtdWx0aXBseSggeWMsIG4sIGJhc2UgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHhjID0gbXVsdGlwbHkoIHhjLCBuLCBiYXNlICk7XG4gICAgICAgICAgICAgICAgICAgICAgICB5TCA9IHljLmxlbmd0aDtcbiAgICAgICAgICAgICAgICAgICAgICAgIHhMID0geGMubGVuZ3RoO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgeGkgPSB5TDtcbiAgICAgICAgICAgICAgICAgICAgcmVtID0geGMuc2xpY2UoIDAsIHlMICk7XG4gICAgICAgICAgICAgICAgICAgIHJlbUwgPSByZW0ubGVuZ3RoO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIEFkZCB6ZXJvcyB0byBtYWtlIHJlbWFpbmRlciBhcyBsb25nIGFzIGRpdmlzb3IuXG4gICAgICAgICAgICAgICAgICAgIGZvciAoIDsgcmVtTCA8IHlMOyByZW1bcmVtTCsrXSA9IDAgKTtcbiAgICAgICAgICAgICAgICAgICAgeXogPSB5Yy5zbGljZSgpO1xuICAgICAgICAgICAgICAgICAgICB5ei51bnNoaWZ0KDApO1xuICAgICAgICAgICAgICAgICAgICB5YzAgPSB5Y1swXTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCB5Y1sxXSA+PSBiYXNlIC8gMiApIHljMCsrO1xuICAgICAgICAgICAgICAgICAgICAvLyBOb3QgbmVjZXNzYXJ5LCBidXQgdG8gcHJldmVudCB0cmlhbCBkaWdpdCBuID4gYmFzZSwgd2hlbiB1c2luZyBiYXNlIDMuXG4gICAgICAgICAgICAgICAgICAgIC8vIGVsc2UgaWYgKCBiYXNlID09IDMgJiYgeWMwID09IDEgKSB5YzAgPSAxICsgMWUtMTU7XG5cbiAgICAgICAgICAgICAgICAgICAgZG8ge1xuICAgICAgICAgICAgICAgICAgICAgICAgbiA9IDA7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIENvbXBhcmUgZGl2aXNvciBhbmQgcmVtYWluZGVyLlxuICAgICAgICAgICAgICAgICAgICAgICAgY21wID0gY29tcGFyZSggeWMsIHJlbSwgeUwsIHJlbUwgKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gSWYgZGl2aXNvciA8IHJlbWFpbmRlci5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICggY21wIDwgMCApIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIENhbGN1bGF0ZSB0cmlhbCBkaWdpdCwgbi5cblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbTAgPSByZW1bMF07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCB5TCAhPSByZW1MICkgcmVtMCA9IHJlbTAgKiBiYXNlICsgKCByZW1bMV0gfHwgMCApO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gbiBpcyBob3cgbWFueSB0aW1lcyB0aGUgZGl2aXNvciBnb2VzIGludG8gdGhlIGN1cnJlbnQgcmVtYWluZGVyLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4gPSBtYXRoZmxvb3IoIHJlbTAgLyB5YzAgKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vICBBbGdvcml0aG06XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gIDEuIHByb2R1Y3QgPSBkaXZpc29yICogdHJpYWwgZGlnaXQgKG4pXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gIDIuIGlmIHByb2R1Y3QgPiByZW1haW5kZXI6IHByb2R1Y3QgLT0gZGl2aXNvciwgbi0tXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gIDMuIHJlbWFpbmRlciAtPSBwcm9kdWN0XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gIDQuIGlmIHByb2R1Y3Qgd2FzIDwgcmVtYWluZGVyIGF0IDI6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gICAgNS4gY29tcGFyZSBuZXcgcmVtYWluZGVyIGFuZCBkaXZpc29yXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gICAgNi4gSWYgcmVtYWluZGVyID4gZGl2aXNvcjogcmVtYWluZGVyIC09IGRpdmlzb3IsIG4rK1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCBuID4gMSApIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBuIG1heSBiZSA+IGJhc2Ugb25seSB3aGVuIGJhc2UgaXMgMy5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG4gPj0gYmFzZSkgbiA9IGJhc2UgLSAxO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHByb2R1Y3QgPSBkaXZpc29yICogdHJpYWwgZGlnaXQuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2QgPSBtdWx0aXBseSggeWMsIG4sIGJhc2UgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvZEwgPSBwcm9kLmxlbmd0aDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVtTCA9IHJlbS5sZW5ndGg7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQ29tcGFyZSBwcm9kdWN0IGFuZCByZW1haW5kZXIuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIHByb2R1Y3QgPiByZW1haW5kZXIuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFRyaWFsIGRpZ2l0IG4gdG9vIGhpZ2guXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIG4gaXMgMSB0b28gaGlnaCBhYm91dCA1JSBvZiB0aGUgdGltZSwgYW5kIGlzIG5vdCBrbm93biB0byBoYXZlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGV2ZXIgYmVlbiBtb3JlIHRoYW4gMSB0b28gaGlnaC5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hpbGUgKCBjb21wYXJlKCBwcm9kLCByZW0sIHByb2RMLCByZW1MICkgPT0gMSApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4tLTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gU3VidHJhY3QgZGl2aXNvciBmcm9tIHByb2R1Y3QuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJ0cmFjdCggcHJvZCwgeUwgPCBwcm9kTCA/IHl6IDogeWMsIHByb2RMLCBiYXNlICk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9kTCA9IHByb2QubGVuZ3RoO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY21wID0gMTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gbiBpcyAwIG9yIDEsIGNtcCBpcyAtMS5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gSWYgbiBpcyAwLCB0aGVyZSBpcyBubyBuZWVkIHRvIGNvbXBhcmUgeWMgYW5kIHJlbSBhZ2FpbiBiZWxvdyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gc28gY2hhbmdlIGNtcCB0byAxIHRvIGF2b2lkIGl0LlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBJZiBuIGlzIDEsIGxlYXZlIGNtcCBhcyAtMSwgc28geWMgYW5kIHJlbSBhcmUgY29tcGFyZWQgYWdhaW4uXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICggbiA9PSAwICkge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBkaXZpc29yIDwgcmVtYWluZGVyLCBzbyBuIG11c3QgYmUgYXQgbGVhc3QgMS5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNtcCA9IG4gPSAxO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gcHJvZHVjdCA9IGRpdmlzb3JcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvZCA9IHljLnNsaWNlKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2RMID0gcHJvZC5sZW5ndGg7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCBwcm9kTCA8IHJlbUwgKSBwcm9kLnVuc2hpZnQoMCk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBTdWJ0cmFjdCBwcm9kdWN0IGZyb20gcmVtYWluZGVyLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnRyYWN0KCByZW0sIHByb2QsIHJlbUwsIGJhc2UgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZW1MID0gcmVtLmxlbmd0aDtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBJZiBwcm9kdWN0IHdhcyA8IHJlbWFpbmRlci5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoIGNtcCA9PSAtMSApIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBDb21wYXJlIGRpdmlzb3IgYW5kIG5ldyByZW1haW5kZXIuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIGRpdmlzb3IgPCBuZXcgcmVtYWluZGVyLCBzdWJ0cmFjdCBkaXZpc29yIGZyb20gcmVtYWluZGVyLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBUcmlhbCBkaWdpdCBuIHRvbyBsb3cuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIG4gaXMgMSB0b28gbG93IGFib3V0IDUlIG9mIHRoZSB0aW1lLCBhbmQgdmVyeSByYXJlbHkgMiB0b28gbG93LlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aGlsZSAoIGNvbXBhcmUoIHljLCByZW0sIHlMLCByZW1MICkgPCAxICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbisrO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBTdWJ0cmFjdCBkaXZpc29yIGZyb20gcmVtYWluZGVyLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VidHJhY3QoIHJlbSwgeUwgPCByZW1MID8geXogOiB5YywgcmVtTCwgYmFzZSApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVtTCA9IHJlbS5sZW5ndGg7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCBjbXAgPT09IDAgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbisrO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbSA9IFswXTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gLy8gZWxzZSBjbXAgPT09IDEgYW5kIG4gd2lsbCBiZSAwXG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIEFkZCB0aGUgbmV4dCBkaWdpdCwgbiwgdG8gdGhlIHJlc3VsdCBhcnJheS5cbiAgICAgICAgICAgICAgICAgICAgICAgIHFjW2krK10gPSBuO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBVcGRhdGUgdGhlIHJlbWFpbmRlci5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICggcmVtWzBdICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbVtyZW1MKytdID0geGNbeGldIHx8IDA7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbSA9IFsgeGNbeGldIF07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVtTCA9IDE7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0gd2hpbGUgKCAoIHhpKysgPCB4TCB8fCByZW1bMF0gIT0gbnVsbCApICYmIHMtLSApO1xuXG4gICAgICAgICAgICAgICAgICAgIG1vcmUgPSByZW1bMF0gIT0gbnVsbDtcblxuICAgICAgICAgICAgICAgICAgICAvLyBMZWFkaW5nIHplcm8/XG4gICAgICAgICAgICAgICAgICAgIGlmICggIXFjWzBdICkgcWMuc2hpZnQoKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAoIGJhc2UgPT0gQkFTRSApIHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBUbyBjYWxjdWxhdGUgcS5lLCBmaXJzdCBnZXQgdGhlIG51bWJlciBvZiBkaWdpdHMgb2YgcWNbMF0uXG4gICAgICAgICAgICAgICAgICAgIGZvciAoIGkgPSAxLCBzID0gcWNbMF07IHMgPj0gMTA7IHMgLz0gMTAsIGkrKyApO1xuICAgICAgICAgICAgICAgICAgICByb3VuZCggcSwgZHAgKyAoIHEuZSA9IGkgKyBlICogTE9HX0JBU0UgLSAxICkgKyAxLCBybSwgbW9yZSApO1xuXG4gICAgICAgICAgICAgICAgLy8gQ2FsbGVyIGlzIGNvbnZlcnRCYXNlLlxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHEuZSA9IGU7XG4gICAgICAgICAgICAgICAgICAgIHEuciA9ICttb3JlO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHJldHVybiBxO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfSkoKTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiBhIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIHZhbHVlIG9mIEJpZ051bWJlciBuIGluIGZpeGVkLXBvaW50IG9yIGV4cG9uZW50aWFsXG4gICAgICAgICAqIG5vdGF0aW9uIHJvdW5kZWQgdG8gdGhlIHNwZWNpZmllZCBkZWNpbWFsIHBsYWNlcyBvciBzaWduaWZpY2FudCBkaWdpdHMuXG4gICAgICAgICAqXG4gICAgICAgICAqIG4gaXMgYSBCaWdOdW1iZXIuXG4gICAgICAgICAqIGkgaXMgdGhlIGluZGV4IG9mIHRoZSBsYXN0IGRpZ2l0IHJlcXVpcmVkIChpLmUuIHRoZSBkaWdpdCB0aGF0IG1heSBiZSByb3VuZGVkIHVwKS5cbiAgICAgICAgICogcm0gaXMgdGhlIHJvdW5kaW5nIG1vZGUuXG4gICAgICAgICAqIGNhbGxlciBpcyBjYWxsZXIgaWQ6IHRvRXhwb25lbnRpYWwgMTksIHRvRml4ZWQgMjAsIHRvRm9ybWF0IDIxLCB0b1ByZWNpc2lvbiAyNC5cbiAgICAgICAgICovXG4gICAgICAgIGZ1bmN0aW9uIGZvcm1hdCggbiwgaSwgcm0sIGNhbGxlciApIHtcbiAgICAgICAgICAgIHZhciBjMCwgZSwgbmUsIGxlbiwgc3RyO1xuXG4gICAgICAgICAgICBybSA9IHJtICE9IG51bGwgJiYgaXNWYWxpZEludCggcm0sIDAsIDgsIGNhbGxlciwgcm91bmRpbmdNb2RlIClcbiAgICAgICAgICAgICAgPyBybSB8IDAgOiBST1VORElOR19NT0RFO1xuXG4gICAgICAgICAgICBpZiAoICFuLmMgKSByZXR1cm4gbi50b1N0cmluZygpO1xuICAgICAgICAgICAgYzAgPSBuLmNbMF07XG4gICAgICAgICAgICBuZSA9IG4uZTtcblxuICAgICAgICAgICAgaWYgKCBpID09IG51bGwgKSB7XG4gICAgICAgICAgICAgICAgc3RyID0gY29lZmZUb1N0cmluZyggbi5jICk7XG4gICAgICAgICAgICAgICAgc3RyID0gY2FsbGVyID09IDE5IHx8IGNhbGxlciA9PSAyNCAmJiBuZSA8PSBUT19FWFBfTkVHXG4gICAgICAgICAgICAgICAgICA/IHRvRXhwb25lbnRpYWwoIHN0ciwgbmUgKVxuICAgICAgICAgICAgICAgICAgOiB0b0ZpeGVkUG9pbnQoIHN0ciwgbmUgKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgbiA9IHJvdW5kKCBuZXcgQmlnTnVtYmVyKG4pLCBpLCBybSApO1xuXG4gICAgICAgICAgICAgICAgLy8gbi5lIG1heSBoYXZlIGNoYW5nZWQgaWYgdGhlIHZhbHVlIHdhcyByb3VuZGVkIHVwLlxuICAgICAgICAgICAgICAgIGUgPSBuLmU7XG5cbiAgICAgICAgICAgICAgICBzdHIgPSBjb2VmZlRvU3RyaW5nKCBuLmMgKTtcbiAgICAgICAgICAgICAgICBsZW4gPSBzdHIubGVuZ3RoO1xuXG4gICAgICAgICAgICAgICAgLy8gdG9QcmVjaXNpb24gcmV0dXJucyBleHBvbmVudGlhbCBub3RhdGlvbiBpZiB0aGUgbnVtYmVyIG9mIHNpZ25pZmljYW50IGRpZ2l0c1xuICAgICAgICAgICAgICAgIC8vIHNwZWNpZmllZCBpcyBsZXNzIHRoYW4gdGhlIG51bWJlciBvZiBkaWdpdHMgbmVjZXNzYXJ5IHRvIHJlcHJlc2VudCB0aGUgaW50ZWdlclxuICAgICAgICAgICAgICAgIC8vIHBhcnQgb2YgdGhlIHZhbHVlIGluIGZpeGVkLXBvaW50IG5vdGF0aW9uLlxuXG4gICAgICAgICAgICAgICAgLy8gRXhwb25lbnRpYWwgbm90YXRpb24uXG4gICAgICAgICAgICAgICAgaWYgKCBjYWxsZXIgPT0gMTkgfHwgY2FsbGVyID09IDI0ICYmICggaSA8PSBlIHx8IGUgPD0gVE9fRVhQX05FRyApICkge1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIEFwcGVuZCB6ZXJvcz9cbiAgICAgICAgICAgICAgICAgICAgZm9yICggOyBsZW4gPCBpOyBzdHIgKz0gJzAnLCBsZW4rKyApO1xuICAgICAgICAgICAgICAgICAgICBzdHIgPSB0b0V4cG9uZW50aWFsKCBzdHIsIGUgKTtcblxuICAgICAgICAgICAgICAgIC8vIEZpeGVkLXBvaW50IG5vdGF0aW9uLlxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGkgLT0gbmU7XG4gICAgICAgICAgICAgICAgICAgIHN0ciA9IHRvRml4ZWRQb2ludCggc3RyLCBlICk7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gQXBwZW5kIHplcm9zP1xuICAgICAgICAgICAgICAgICAgICBpZiAoIGUgKyAxID4gbGVuICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCAtLWkgPiAwICkgZm9yICggc3RyICs9ICcuJzsgaS0tOyBzdHIgKz0gJzAnICk7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpICs9IGUgLSBsZW47XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoIGkgPiAwICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICggZSArIDEgPT0gbGVuICkgc3RyICs9ICcuJztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3IgKCA7IGktLTsgc3RyICs9ICcwJyApO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gbi5zIDwgMCAmJiBjMCA/ICctJyArIHN0ciA6IHN0cjtcbiAgICAgICAgfVxuXG5cbiAgICAgICAgLy8gSGFuZGxlIEJpZ051bWJlci5tYXggYW5kIEJpZ051bWJlci5taW4uXG4gICAgICAgIGZ1bmN0aW9uIG1heE9yTWluKCBhcmdzLCBtZXRob2QgKSB7XG4gICAgICAgICAgICB2YXIgbSwgbixcbiAgICAgICAgICAgICAgICBpID0gMDtcblxuICAgICAgICAgICAgaWYgKCBpc0FycmF5KCBhcmdzWzBdICkgKSBhcmdzID0gYXJnc1swXTtcbiAgICAgICAgICAgIG0gPSBuZXcgQmlnTnVtYmVyKCBhcmdzWzBdICk7XG5cbiAgICAgICAgICAgIGZvciAoIDsgKytpIDwgYXJncy5sZW5ndGg7ICkge1xuICAgICAgICAgICAgICAgIG4gPSBuZXcgQmlnTnVtYmVyKCBhcmdzW2ldICk7XG5cbiAgICAgICAgICAgICAgICAvLyBJZiBhbnkgbnVtYmVyIGlzIE5hTiwgcmV0dXJuIE5hTi5cbiAgICAgICAgICAgICAgICBpZiAoICFuLnMgKSB7XG4gICAgICAgICAgICAgICAgICAgIG0gPSBuO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCBtZXRob2QuY2FsbCggbSwgbiApICkge1xuICAgICAgICAgICAgICAgICAgICBtID0gbjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiBtO1xuICAgICAgICB9XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gdHJ1ZSBpZiBuIGlzIGFuIGludGVnZXIgaW4gcmFuZ2UsIG90aGVyd2lzZSB0aHJvdy5cbiAgICAgICAgICogVXNlIGZvciBhcmd1bWVudCB2YWxpZGF0aW9uIHdoZW4gRVJST1JTIGlzIHRydWUuXG4gICAgICAgICAqL1xuICAgICAgICBmdW5jdGlvbiBpbnRWYWxpZGF0b3JXaXRoRXJyb3JzKCBuLCBtaW4sIG1heCwgY2FsbGVyLCBuYW1lICkge1xuICAgICAgICAgICAgaWYgKCBuIDwgbWluIHx8IG4gPiBtYXggfHwgbiAhPSB0cnVuY2F0ZShuKSApIHtcbiAgICAgICAgICAgICAgICByYWlzZSggY2FsbGVyLCAoIG5hbWUgfHwgJ2RlY2ltYWwgcGxhY2VzJyApICtcbiAgICAgICAgICAgICAgICAgICggbiA8IG1pbiB8fCBuID4gbWF4ID8gJyBvdXQgb2YgcmFuZ2UnIDogJyBub3QgYW4gaW50ZWdlcicgKSwgbiApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogU3RyaXAgdHJhaWxpbmcgemVyb3MsIGNhbGN1bGF0ZSBiYXNlIDEwIGV4cG9uZW50IGFuZCBjaGVjayBhZ2FpbnN0IE1JTl9FWFAgYW5kIE1BWF9FWFAuXG4gICAgICAgICAqIENhbGxlZCBieSBtaW51cywgcGx1cyBhbmQgdGltZXMuXG4gICAgICAgICAqL1xuICAgICAgICBmdW5jdGlvbiBub3JtYWxpc2UoIG4sIGMsIGUgKSB7XG4gICAgICAgICAgICB2YXIgaSA9IDEsXG4gICAgICAgICAgICAgICAgaiA9IGMubGVuZ3RoO1xuXG4gICAgICAgICAgICAgLy8gUmVtb3ZlIHRyYWlsaW5nIHplcm9zLlxuICAgICAgICAgICAgZm9yICggOyAhY1stLWpdOyBjLnBvcCgpICk7XG5cbiAgICAgICAgICAgIC8vIENhbGN1bGF0ZSB0aGUgYmFzZSAxMCBleHBvbmVudC4gRmlyc3QgZ2V0IHRoZSBudW1iZXIgb2YgZGlnaXRzIG9mIGNbMF0uXG4gICAgICAgICAgICBmb3IgKCBqID0gY1swXTsgaiA+PSAxMDsgaiAvPSAxMCwgaSsrICk7XG5cbiAgICAgICAgICAgIC8vIE92ZXJmbG93P1xuICAgICAgICAgICAgaWYgKCAoIGUgPSBpICsgZSAqIExPR19CQVNFIC0gMSApID4gTUFYX0VYUCApIHtcblxuICAgICAgICAgICAgICAgIC8vIEluZmluaXR5LlxuICAgICAgICAgICAgICAgIG4uYyA9IG4uZSA9IG51bGw7XG5cbiAgICAgICAgICAgIC8vIFVuZGVyZmxvdz9cbiAgICAgICAgICAgIH0gZWxzZSBpZiAoIGUgPCBNSU5fRVhQICkge1xuXG4gICAgICAgICAgICAgICAgLy8gWmVyby5cbiAgICAgICAgICAgICAgICBuLmMgPSBbIG4uZSA9IDAgXTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgbi5lID0gZTtcbiAgICAgICAgICAgICAgICBuLmMgPSBjO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gbjtcbiAgICAgICAgfVxuXG5cbiAgICAgICAgLy8gSGFuZGxlIHZhbHVlcyB0aGF0IGZhaWwgdGhlIHZhbGlkaXR5IHRlc3QgaW4gQmlnTnVtYmVyLlxuICAgICAgICBwYXJzZU51bWVyaWMgPSAoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIGJhc2VQcmVmaXggPSAvXigtPykwKFt4Ym9dKS9pLFxuICAgICAgICAgICAgICAgIGRvdEFmdGVyID0gL14oW14uXSspXFwuJC8sXG4gICAgICAgICAgICAgICAgZG90QmVmb3JlID0gL15cXC4oW14uXSspJC8sXG4gICAgICAgICAgICAgICAgaXNJbmZpbml0eU9yTmFOID0gL14tPyhJbmZpbml0eXxOYU4pJC8sXG4gICAgICAgICAgICAgICAgd2hpdGVzcGFjZU9yUGx1cyA9IC9eXFxzKlxcK3xeXFxzK3xcXHMrJC9nO1xuXG4gICAgICAgICAgICByZXR1cm4gZnVuY3Rpb24gKCB4LCBzdHIsIG51bSwgYiApIHtcbiAgICAgICAgICAgICAgICB2YXIgYmFzZSxcbiAgICAgICAgICAgICAgICAgICAgcyA9IG51bSA/IHN0ciA6IHN0ci5yZXBsYWNlKCB3aGl0ZXNwYWNlT3JQbHVzLCAnJyApO1xuXG4gICAgICAgICAgICAgICAgLy8gTm8gZXhjZXB0aW9uIG9uIMKxSW5maW5pdHkgb3IgTmFOLlxuICAgICAgICAgICAgICAgIGlmICggaXNJbmZpbml0eU9yTmFOLnRlc3QocykgKSB7XG4gICAgICAgICAgICAgICAgICAgIHgucyA9IGlzTmFOKHMpID8gbnVsbCA6IHMgPCAwID8gLTEgOiAxO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGlmICggIW51bSApIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gYmFzZVByZWZpeCA9IC9eKC0/KTAoW3hib10pKD89XFx3W1xcdy5dKiQpL2lcbiAgICAgICAgICAgICAgICAgICAgICAgIHMgPSBzLnJlcGxhY2UoIGJhc2VQcmVmaXgsIGZ1bmN0aW9uICggbSwgcDEsIHAyICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhc2UgPSAoIHAyID0gcDIudG9Mb3dlckNhc2UoKSApID09ICd4JyA/IDE2IDogcDIgPT0gJ2InID8gMiA6IDg7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuICFiIHx8IGIgPT0gYmFzZSA/IHAxIDogbTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoYikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhc2UgPSBiO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gRS5nLiAnMS4nIHRvICcxJywgJy4xJyB0byAnMC4xJ1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHMgPSBzLnJlcGxhY2UoIGRvdEFmdGVyLCAnJDEnICkucmVwbGFjZSggZG90QmVmb3JlLCAnMC4kMScgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCBzdHIgIT0gcyApIHJldHVybiBuZXcgQmlnTnVtYmVyKCBzLCBiYXNlICk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAvLyAnbmV3IEJpZ051bWJlcigpIG5vdCBhIG51bWJlcjoge259J1xuICAgICAgICAgICAgICAgICAgICAvLyAnbmV3IEJpZ051bWJlcigpIG5vdCBhIGJhc2Uge2J9IG51bWJlcjoge259J1xuICAgICAgICAgICAgICAgICAgICBpZiAoRVJST1JTKSByYWlzZSggaWQsICdub3QgYScgKyAoIGIgPyAnIGJhc2UgJyArIGIgOiAnJyApICsgJyBudW1iZXInLCBzdHIgKTtcbiAgICAgICAgICAgICAgICAgICAgeC5zID0gbnVsbDtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB4LmMgPSB4LmUgPSBudWxsO1xuICAgICAgICAgICAgICAgIGlkID0gMDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSkoKTtcblxuXG4gICAgICAgIC8vIFRocm93IGEgQmlnTnVtYmVyIEVycm9yLlxuICAgICAgICBmdW5jdGlvbiByYWlzZSggY2FsbGVyLCBtc2csIHZhbCApIHtcbiAgICAgICAgICAgIHZhciBlcnJvciA9IG5ldyBFcnJvciggW1xuICAgICAgICAgICAgICAgICduZXcgQmlnTnVtYmVyJywgICAgIC8vIDBcbiAgICAgICAgICAgICAgICAnY21wJywgICAgICAgICAgICAgICAvLyAxXG4gICAgICAgICAgICAgICAgJ2NvbmZpZycsICAgICAgICAgICAgLy8gMlxuICAgICAgICAgICAgICAgICdkaXYnLCAgICAgICAgICAgICAgIC8vIDNcbiAgICAgICAgICAgICAgICAnZGl2VG9JbnQnLCAgICAgICAgICAvLyA0XG4gICAgICAgICAgICAgICAgJ2VxJywgICAgICAgICAgICAgICAgLy8gNVxuICAgICAgICAgICAgICAgICdndCcsICAgICAgICAgICAgICAgIC8vIDZcbiAgICAgICAgICAgICAgICAnZ3RlJywgICAgICAgICAgICAgICAvLyA3XG4gICAgICAgICAgICAgICAgJ2x0JywgICAgICAgICAgICAgICAgLy8gOFxuICAgICAgICAgICAgICAgICdsdGUnLCAgICAgICAgICAgICAgIC8vIDlcbiAgICAgICAgICAgICAgICAnbWludXMnLCAgICAgICAgICAgICAvLyAxMFxuICAgICAgICAgICAgICAgICdtb2QnLCAgICAgICAgICAgICAgIC8vIDExXG4gICAgICAgICAgICAgICAgJ3BsdXMnLCAgICAgICAgICAgICAgLy8gMTJcbiAgICAgICAgICAgICAgICAncHJlY2lzaW9uJywgICAgICAgICAvLyAxM1xuICAgICAgICAgICAgICAgICdyYW5kb20nLCAgICAgICAgICAgIC8vIDE0XG4gICAgICAgICAgICAgICAgJ3JvdW5kJywgICAgICAgICAgICAgLy8gMTVcbiAgICAgICAgICAgICAgICAnc2hpZnQnLCAgICAgICAgICAgICAvLyAxNlxuICAgICAgICAgICAgICAgICd0aW1lcycsICAgICAgICAgICAgIC8vIDE3XG4gICAgICAgICAgICAgICAgJ3RvRGlnaXRzJywgICAgICAgICAgLy8gMThcbiAgICAgICAgICAgICAgICAndG9FeHBvbmVudGlhbCcsICAgICAvLyAxOVxuICAgICAgICAgICAgICAgICd0b0ZpeGVkJywgICAgICAgICAgIC8vIDIwXG4gICAgICAgICAgICAgICAgJ3RvRm9ybWF0JywgICAgICAgICAgLy8gMjFcbiAgICAgICAgICAgICAgICAndG9GcmFjdGlvbicsICAgICAgICAvLyAyMlxuICAgICAgICAgICAgICAgICdwb3cnLCAgICAgICAgICAgICAgIC8vIDIzXG4gICAgICAgICAgICAgICAgJ3RvUHJlY2lzaW9uJywgICAgICAgLy8gMjRcbiAgICAgICAgICAgICAgICAndG9TdHJpbmcnLCAgICAgICAgICAvLyAyNVxuICAgICAgICAgICAgICAgICdCaWdOdW1iZXInICAgICAgICAgIC8vIDI2XG4gICAgICAgICAgICBdW2NhbGxlcl0gKyAnKCkgJyArIG1zZyArICc6ICcgKyB2YWwgKTtcblxuICAgICAgICAgICAgZXJyb3IubmFtZSA9ICdCaWdOdW1iZXIgRXJyb3InO1xuICAgICAgICAgICAgaWQgPSAwO1xuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgIH1cblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJvdW5kIHggdG8gc2Qgc2lnbmlmaWNhbnQgZGlnaXRzIHVzaW5nIHJvdW5kaW5nIG1vZGUgcm0uIENoZWNrIGZvciBvdmVyL3VuZGVyLWZsb3cuXG4gICAgICAgICAqIElmIHIgaXMgdHJ1dGh5LCBpdCBpcyBrbm93biB0aGF0IHRoZXJlIGFyZSBtb3JlIGRpZ2l0cyBhZnRlciB0aGUgcm91bmRpbmcgZGlnaXQuXG4gICAgICAgICAqL1xuICAgICAgICBmdW5jdGlvbiByb3VuZCggeCwgc2QsIHJtLCByICkge1xuICAgICAgICAgICAgdmFyIGQsIGksIGosIGssIG4sIG5pLCByZCxcbiAgICAgICAgICAgICAgICB4YyA9IHguYyxcbiAgICAgICAgICAgICAgICBwb3dzMTAgPSBQT1dTX1RFTjtcblxuICAgICAgICAgICAgLy8gaWYgeCBpcyBub3QgSW5maW5pdHkgb3IgTmFOLi4uXG4gICAgICAgICAgICBpZiAoeGMpIHtcblxuICAgICAgICAgICAgICAgIC8vIHJkIGlzIHRoZSByb3VuZGluZyBkaWdpdCwgaS5lLiB0aGUgZGlnaXQgYWZ0ZXIgdGhlIGRpZ2l0IHRoYXQgbWF5IGJlIHJvdW5kZWQgdXAuXG4gICAgICAgICAgICAgICAgLy8gbiBpcyBhIGJhc2UgMWUxNCBudW1iZXIsIHRoZSB2YWx1ZSBvZiB0aGUgZWxlbWVudCBvZiBhcnJheSB4LmMgY29udGFpbmluZyByZC5cbiAgICAgICAgICAgICAgICAvLyBuaSBpcyB0aGUgaW5kZXggb2YgbiB3aXRoaW4geC5jLlxuICAgICAgICAgICAgICAgIC8vIGQgaXMgdGhlIG51bWJlciBvZiBkaWdpdHMgb2Ygbi5cbiAgICAgICAgICAgICAgICAvLyBpIGlzIHRoZSBpbmRleCBvZiByZCB3aXRoaW4gbiBpbmNsdWRpbmcgbGVhZGluZyB6ZXJvcy5cbiAgICAgICAgICAgICAgICAvLyBqIGlzIHRoZSBhY3R1YWwgaW5kZXggb2YgcmQgd2l0aGluIG4gKGlmIDwgMCwgcmQgaXMgYSBsZWFkaW5nIHplcm8pLlxuICAgICAgICAgICAgICAgIG91dDoge1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIEdldCB0aGUgbnVtYmVyIG9mIGRpZ2l0cyBvZiB0aGUgZmlyc3QgZWxlbWVudCBvZiB4Yy5cbiAgICAgICAgICAgICAgICAgICAgZm9yICggZCA9IDEsIGsgPSB4Y1swXTsgayA+PSAxMDsgayAvPSAxMCwgZCsrICk7XG4gICAgICAgICAgICAgICAgICAgIGkgPSBzZCAtIGQ7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gSWYgdGhlIHJvdW5kaW5nIGRpZ2l0IGlzIGluIHRoZSBmaXJzdCBlbGVtZW50IG9mIHhjLi4uXG4gICAgICAgICAgICAgICAgICAgIGlmICggaSA8IDAgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpICs9IExPR19CQVNFO1xuICAgICAgICAgICAgICAgICAgICAgICAgaiA9IHNkO1xuICAgICAgICAgICAgICAgICAgICAgICAgbiA9IHhjWyBuaSA9IDAgXTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gR2V0IHRoZSByb3VuZGluZyBkaWdpdCBhdCBpbmRleCBqIG9mIG4uXG4gICAgICAgICAgICAgICAgICAgICAgICByZCA9IG4gLyBwb3dzMTBbIGQgLSBqIC0gMSBdICUgMTAgfCAwO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgbmkgPSBtYXRoY2VpbCggKCBpICsgMSApIC8gTE9HX0JBU0UgKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCBuaSA+PSB4Yy5sZW5ndGggKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAocikge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE5lZWRlZCBieSBzcXJ0LlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3IgKCA7IHhjLmxlbmd0aCA8PSBuaTsgeGMucHVzaCgwKSApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gcmQgPSAwO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkID0gMTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaSAlPSBMT0dfQkFTRTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaiA9IGkgLSBMT0dfQkFTRSArIDE7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWsgb3V0O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbiA9IGsgPSB4Y1tuaV07XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBHZXQgdGhlIG51bWJlciBvZiBkaWdpdHMgb2Ygbi5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3IgKCBkID0gMTsgayA+PSAxMDsgayAvPSAxMCwgZCsrICk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBHZXQgdGhlIGluZGV4IG9mIHJkIHdpdGhpbiBuLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGkgJT0gTE9HX0JBU0U7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBHZXQgdGhlIGluZGV4IG9mIHJkIHdpdGhpbiBuLCBhZGp1c3RlZCBmb3IgbGVhZGluZyB6ZXJvcy5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBUaGUgbnVtYmVyIG9mIGxlYWRpbmcgemVyb3Mgb2YgbiBpcyBnaXZlbiBieSBMT0dfQkFTRSAtIGQuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaiA9IGkgLSBMT0dfQkFTRSArIGQ7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBHZXQgdGhlIHJvdW5kaW5nIGRpZ2l0IGF0IGluZGV4IGogb2Ygbi5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZCA9IGogPCAwID8gMCA6IG4gLyBwb3dzMTBbIGQgLSBqIC0gMSBdICUgMTAgfCAwO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgciA9IHIgfHwgc2QgPCAwIHx8XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gQXJlIHRoZXJlIGFueSBub24temVybyBkaWdpdHMgYWZ0ZXIgdGhlIHJvdW5kaW5nIGRpZ2l0P1xuICAgICAgICAgICAgICAgICAgICAvLyBUaGUgZXhwcmVzc2lvbiAgbiAlIHBvd3MxMFsgZCAtIGogLSAxIF0gIHJldHVybnMgYWxsIGRpZ2l0cyBvZiBuIHRvIHRoZSByaWdodFxuICAgICAgICAgICAgICAgICAgICAvLyBvZiB0aGUgZGlnaXQgYXQgaiwgZS5nLiBpZiBuIGlzIDkwODcxNCBhbmQgaiBpcyAyLCB0aGUgZXhwcmVzc2lvbiBnaXZlcyA3MTQuXG4gICAgICAgICAgICAgICAgICAgICAgeGNbbmkgKyAxXSAhPSBudWxsIHx8ICggaiA8IDAgPyBuIDogbiAlIHBvd3MxMFsgZCAtIGogLSAxIF0gKTtcblxuICAgICAgICAgICAgICAgICAgICByID0gcm0gPCA0XG4gICAgICAgICAgICAgICAgICAgICAgPyAoIHJkIHx8IHIgKSAmJiAoIHJtID09IDAgfHwgcm0gPT0gKCB4LnMgPCAwID8gMyA6IDIgKSApXG4gICAgICAgICAgICAgICAgICAgICAgOiByZCA+IDUgfHwgcmQgPT0gNSAmJiAoIHJtID09IDQgfHwgciB8fCBybSA9PSA2ICYmXG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIENoZWNrIHdoZXRoZXIgdGhlIGRpZ2l0IHRvIHRoZSBsZWZ0IG9mIHRoZSByb3VuZGluZyBkaWdpdCBpcyBvZGQuXG4gICAgICAgICAgICAgICAgICAgICAgICAoICggaSA+IDAgPyBqID4gMCA/IG4gLyBwb3dzMTBbIGQgLSBqIF0gOiAwIDogeGNbbmkgLSAxXSApICUgMTAgKSAmIDEgfHxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgcm0gPT0gKCB4LnMgPCAwID8gOCA6IDcgKSApO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmICggc2QgPCAxIHx8ICF4Y1swXSApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHhjLmxlbmd0aCA9IDA7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBDb252ZXJ0IHNkIHRvIGRlY2ltYWwgcGxhY2VzLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNkIC09IHguZSArIDE7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyAxLCAwLjEsIDAuMDEsIDAuMDAxLCAwLjAwMDEgZXRjLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhjWzBdID0gcG93czEwWyBzZCAlIExPR19CQVNFIF07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgeC5lID0gLXNkIHx8IDA7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gWmVyby5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4Y1swXSA9IHguZSA9IDA7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB4O1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gUmVtb3ZlIGV4Y2VzcyBkaWdpdHMuXG4gICAgICAgICAgICAgICAgICAgIGlmICggaSA9PSAwICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgeGMubGVuZ3RoID0gbmk7XG4gICAgICAgICAgICAgICAgICAgICAgICBrID0gMTtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5pLS07XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB4Yy5sZW5ndGggPSBuaSArIDE7XG4gICAgICAgICAgICAgICAgICAgICAgICBrID0gcG93czEwWyBMT0dfQkFTRSAtIGkgXTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gRS5nLiA1NjcwMCBiZWNvbWVzIDU2MDAwIGlmIDcgaXMgdGhlIHJvdW5kaW5nIGRpZ2l0LlxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gaiA+IDAgbWVhbnMgaSA+IG51bWJlciBvZiBsZWFkaW5nIHplcm9zIG9mIG4uXG4gICAgICAgICAgICAgICAgICAgICAgICB4Y1tuaV0gPSBqID4gMCA/IG1hdGhmbG9vciggbiAvIHBvd3MxMFsgZCAtIGogXSAlIHBvd3MxMFtqXSApICogayA6IDA7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAvLyBSb3VuZCB1cD9cbiAgICAgICAgICAgICAgICAgICAgaWYgKHIpIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgZm9yICggOyA7ICkge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gSWYgdGhlIGRpZ2l0IHRvIGJlIHJvdW5kZWQgdXAgaXMgaW4gdGhlIGZpcnN0IGVsZW1lbnQgb2YgeGMuLi5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoIG5pID09IDAgKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gaSB3aWxsIGJlIHRoZSBsZW5ndGggb2YgeGNbMF0gYmVmb3JlIGsgaXMgYWRkZWQuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoIGkgPSAxLCBqID0geGNbMF07IGogPj0gMTA7IGogLz0gMTAsIGkrKyApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBqID0geGNbMF0gKz0gaztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9yICggayA9IDE7IGogPj0gMTA7IGogLz0gMTAsIGsrKyApO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGlmIGkgIT0gayB0aGUgbGVuZ3RoIGhhcyBpbmNyZWFzZWQuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICggaSAhPSBrICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeC5lKys7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoIHhjWzBdID09IEJBU0UgKSB4Y1swXSA9IDE7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4Y1tuaV0gKz0gaztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCB4Y1tuaV0gIT0gQkFTRSApIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4Y1tuaS0tXSA9IDA7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGsgPSAxO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIFJlbW92ZSB0cmFpbGluZyB6ZXJvcy5cbiAgICAgICAgICAgICAgICAgICAgZm9yICggaSA9IHhjLmxlbmd0aDsgeGNbLS1pXSA9PT0gMDsgeGMucG9wKCkgKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBPdmVyZmxvdz8gSW5maW5pdHkuXG4gICAgICAgICAgICAgICAgaWYgKCB4LmUgPiBNQVhfRVhQICkge1xuICAgICAgICAgICAgICAgICAgICB4LmMgPSB4LmUgPSBudWxsO1xuXG4gICAgICAgICAgICAgICAgLy8gVW5kZXJmbG93PyBaZXJvLlxuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoIHguZSA8IE1JTl9FWFAgKSB7XG4gICAgICAgICAgICAgICAgICAgIHguYyA9IFsgeC5lID0gMCBdO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIHg7XG4gICAgICAgIH1cblxuXG4gICAgICAgIC8vIFBST1RPVFlQRS9JTlNUQU5DRSBNRVRIT0RTXG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gYSBuZXcgQmlnTnVtYmVyIHdob3NlIHZhbHVlIGlzIHRoZSBhYnNvbHV0ZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlci5cbiAgICAgICAgICovXG4gICAgICAgIFAuYWJzb2x1dGVWYWx1ZSA9IFAuYWJzID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIHggPSBuZXcgQmlnTnVtYmVyKHRoaXMpO1xuICAgICAgICAgICAgaWYgKCB4LnMgPCAwICkgeC5zID0gMTtcbiAgICAgICAgICAgIHJldHVybiB4O1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIGEgbmV3IEJpZ051bWJlciB3aG9zZSB2YWx1ZSBpcyB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgcm91bmRlZCB0byBhIHdob2xlXG4gICAgICAgICAqIG51bWJlciBpbiB0aGUgZGlyZWN0aW9uIG9mIEluZmluaXR5LlxuICAgICAgICAgKi9cbiAgICAgICAgUC5jZWlsID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgcmV0dXJuIHJvdW5kKCBuZXcgQmlnTnVtYmVyKHRoaXMpLCB0aGlzLmUgKyAxLCAyICk7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm5cbiAgICAgICAgICogMSBpZiB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgaXMgZ3JlYXRlciB0aGFuIHRoZSB2YWx1ZSBvZiBCaWdOdW1iZXIoeSwgYiksXG4gICAgICAgICAqIC0xIGlmIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciBpcyBsZXNzIHRoYW4gdGhlIHZhbHVlIG9mIEJpZ051bWJlcih5LCBiKSxcbiAgICAgICAgICogMCBpZiB0aGV5IGhhdmUgdGhlIHNhbWUgdmFsdWUsXG4gICAgICAgICAqIG9yIG51bGwgaWYgdGhlIHZhbHVlIG9mIGVpdGhlciBpcyBOYU4uXG4gICAgICAgICAqL1xuICAgICAgICBQLmNvbXBhcmVkVG8gPSBQLmNtcCA9IGZ1bmN0aW9uICggeSwgYiApIHtcbiAgICAgICAgICAgIGlkID0gMTtcbiAgICAgICAgICAgIHJldHVybiBjb21wYXJlKCB0aGlzLCBuZXcgQmlnTnVtYmVyKCB5LCBiICkgKTtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiB0aGUgbnVtYmVyIG9mIGRlY2ltYWwgcGxhY2VzIG9mIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciwgb3IgbnVsbCBpZiB0aGUgdmFsdWVcbiAgICAgICAgICogb2YgdGhpcyBCaWdOdW1iZXIgaXMgwrFJbmZpbml0eSBvciBOYU4uXG4gICAgICAgICAqL1xuICAgICAgICBQLmRlY2ltYWxQbGFjZXMgPSBQLmRwID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIG4sIHYsXG4gICAgICAgICAgICAgICAgYyA9IHRoaXMuYztcblxuICAgICAgICAgICAgaWYgKCAhYyApIHJldHVybiBudWxsO1xuICAgICAgICAgICAgbiA9ICggKCB2ID0gYy5sZW5ndGggLSAxICkgLSBiaXRGbG9vciggdGhpcy5lIC8gTE9HX0JBU0UgKSApICogTE9HX0JBU0U7XG5cbiAgICAgICAgICAgIC8vIFN1YnRyYWN0IHRoZSBudW1iZXIgb2YgdHJhaWxpbmcgemVyb3Mgb2YgdGhlIGxhc3QgbnVtYmVyLlxuICAgICAgICAgICAgaWYgKCB2ID0gY1t2XSApIGZvciAoIDsgdiAlIDEwID09IDA7IHYgLz0gMTAsIG4tLSApO1xuICAgICAgICAgICAgaWYgKCBuIDwgMCApIG4gPSAwO1xuXG4gICAgICAgICAgICByZXR1cm4gbjtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqICBuIC8gMCA9IElcbiAgICAgICAgICogIG4gLyBOID0gTlxuICAgICAgICAgKiAgbiAvIEkgPSAwXG4gICAgICAgICAqICAwIC8gbiA9IDBcbiAgICAgICAgICogIDAgLyAwID0gTlxuICAgICAgICAgKiAgMCAvIE4gPSBOXG4gICAgICAgICAqICAwIC8gSSA9IDBcbiAgICAgICAgICogIE4gLyBuID0gTlxuICAgICAgICAgKiAgTiAvIDAgPSBOXG4gICAgICAgICAqICBOIC8gTiA9IE5cbiAgICAgICAgICogIE4gLyBJID0gTlxuICAgICAgICAgKiAgSSAvIG4gPSBJXG4gICAgICAgICAqICBJIC8gMCA9IElcbiAgICAgICAgICogIEkgLyBOID0gTlxuICAgICAgICAgKiAgSSAvIEkgPSBOXG4gICAgICAgICAqXG4gICAgICAgICAqIFJldHVybiBhIG5ldyBCaWdOdW1iZXIgd2hvc2UgdmFsdWUgaXMgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIGRpdmlkZWQgYnkgdGhlIHZhbHVlIG9mXG4gICAgICAgICAqIEJpZ051bWJlcih5LCBiKSwgcm91bmRlZCBhY2NvcmRpbmcgdG8gREVDSU1BTF9QTEFDRVMgYW5kIFJPVU5ESU5HX01PREUuXG4gICAgICAgICAqL1xuICAgICAgICBQLmRpdmlkZWRCeSA9IFAuZGl2ID0gZnVuY3Rpb24gKCB5LCBiICkge1xuICAgICAgICAgICAgaWQgPSAzO1xuICAgICAgICAgICAgcmV0dXJuIGRpdiggdGhpcywgbmV3IEJpZ051bWJlciggeSwgYiApLCBERUNJTUFMX1BMQUNFUywgUk9VTkRJTkdfTU9ERSApO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIGEgbmV3IEJpZ051bWJlciB3aG9zZSB2YWx1ZSBpcyB0aGUgaW50ZWdlciBwYXJ0IG9mIGRpdmlkaW5nIHRoZSB2YWx1ZSBvZiB0aGlzXG4gICAgICAgICAqIEJpZ051bWJlciBieSB0aGUgdmFsdWUgb2YgQmlnTnVtYmVyKHksIGIpLlxuICAgICAgICAgKi9cbiAgICAgICAgUC5kaXZpZGVkVG9JbnRlZ2VyQnkgPSBQLmRpdlRvSW50ID0gZnVuY3Rpb24gKCB5LCBiICkge1xuICAgICAgICAgICAgaWQgPSA0O1xuICAgICAgICAgICAgcmV0dXJuIGRpdiggdGhpcywgbmV3IEJpZ051bWJlciggeSwgYiApLCAwLCAxICk7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gdHJ1ZSBpZiB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgaXMgZXF1YWwgdG8gdGhlIHZhbHVlIG9mIEJpZ051bWJlcih5LCBiKSxcbiAgICAgICAgICogb3RoZXJ3aXNlIHJldHVybnMgZmFsc2UuXG4gICAgICAgICAqL1xuICAgICAgICBQLmVxdWFscyA9IFAuZXEgPSBmdW5jdGlvbiAoIHksIGIgKSB7XG4gICAgICAgICAgICBpZCA9IDU7XG4gICAgICAgICAgICByZXR1cm4gY29tcGFyZSggdGhpcywgbmV3IEJpZ051bWJlciggeSwgYiApICkgPT09IDA7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gYSBuZXcgQmlnTnVtYmVyIHdob3NlIHZhbHVlIGlzIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciByb3VuZGVkIHRvIGEgd2hvbGVcbiAgICAgICAgICogbnVtYmVyIGluIHRoZSBkaXJlY3Rpb24gb2YgLUluZmluaXR5LlxuICAgICAgICAgKi9cbiAgICAgICAgUC5mbG9vciA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiByb3VuZCggbmV3IEJpZ051bWJlcih0aGlzKSwgdGhpcy5lICsgMSwgMyApO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIHRydWUgaWYgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIGlzIGdyZWF0ZXIgdGhhbiB0aGUgdmFsdWUgb2YgQmlnTnVtYmVyKHksIGIpLFxuICAgICAgICAgKiBvdGhlcndpc2UgcmV0dXJucyBmYWxzZS5cbiAgICAgICAgICovXG4gICAgICAgIFAuZ3JlYXRlclRoYW4gPSBQLmd0ID0gZnVuY3Rpb24gKCB5LCBiICkge1xuICAgICAgICAgICAgaWQgPSA2O1xuICAgICAgICAgICAgcmV0dXJuIGNvbXBhcmUoIHRoaXMsIG5ldyBCaWdOdW1iZXIoIHksIGIgKSApID4gMDtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiB0cnVlIGlmIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciBpcyBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gdGhlIHZhbHVlIG9mXG4gICAgICAgICAqIEJpZ051bWJlcih5LCBiKSwgb3RoZXJ3aXNlIHJldHVybnMgZmFsc2UuXG4gICAgICAgICAqL1xuICAgICAgICBQLmdyZWF0ZXJUaGFuT3JFcXVhbFRvID0gUC5ndGUgPSBmdW5jdGlvbiAoIHksIGIgKSB7XG4gICAgICAgICAgICBpZCA9IDc7XG4gICAgICAgICAgICByZXR1cm4gKCBiID0gY29tcGFyZSggdGhpcywgbmV3IEJpZ051bWJlciggeSwgYiApICkgKSA9PT0gMSB8fCBiID09PSAwO1xuXG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gdHJ1ZSBpZiB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgaXMgYSBmaW5pdGUgbnVtYmVyLCBvdGhlcndpc2UgcmV0dXJucyBmYWxzZS5cbiAgICAgICAgICovXG4gICAgICAgIFAuaXNGaW5pdGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICByZXR1cm4gISF0aGlzLmM7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gdHJ1ZSBpZiB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgaXMgYW4gaW50ZWdlciwgb3RoZXJ3aXNlIHJldHVybiBmYWxzZS5cbiAgICAgICAgICovXG4gICAgICAgIFAuaXNJbnRlZ2VyID0gUC5pc0ludCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiAhIXRoaXMuYyAmJiBiaXRGbG9vciggdGhpcy5lIC8gTE9HX0JBU0UgKSA+IHRoaXMuYy5sZW5ndGggLSAyO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIHRydWUgaWYgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIGlzIE5hTiwgb3RoZXJ3aXNlIHJldHVybnMgZmFsc2UuXG4gICAgICAgICAqL1xuICAgICAgICBQLmlzTmFOID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgcmV0dXJuICF0aGlzLnM7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gdHJ1ZSBpZiB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgaXMgbmVnYXRpdmUsIG90aGVyd2lzZSByZXR1cm5zIGZhbHNlLlxuICAgICAgICAgKi9cbiAgICAgICAgUC5pc05lZ2F0aXZlID0gUC5pc05lZyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnMgPCAwO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIHRydWUgaWYgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIGlzIDAgb3IgLTAsIG90aGVyd2lzZSByZXR1cm5zIGZhbHNlLlxuICAgICAgICAgKi9cbiAgICAgICAgUC5pc1plcm8gPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICByZXR1cm4gISF0aGlzLmMgJiYgdGhpcy5jWzBdID09IDA7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gdHJ1ZSBpZiB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgaXMgbGVzcyB0aGFuIHRoZSB2YWx1ZSBvZiBCaWdOdW1iZXIoeSwgYiksXG4gICAgICAgICAqIG90aGVyd2lzZSByZXR1cm5zIGZhbHNlLlxuICAgICAgICAgKi9cbiAgICAgICAgUC5sZXNzVGhhbiA9IFAubHQgPSBmdW5jdGlvbiAoIHksIGIgKSB7XG4gICAgICAgICAgICBpZCA9IDg7XG4gICAgICAgICAgICByZXR1cm4gY29tcGFyZSggdGhpcywgbmV3IEJpZ051bWJlciggeSwgYiApICkgPCAwO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIHRydWUgaWYgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIGlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byB0aGUgdmFsdWUgb2ZcbiAgICAgICAgICogQmlnTnVtYmVyKHksIGIpLCBvdGhlcndpc2UgcmV0dXJucyBmYWxzZS5cbiAgICAgICAgICovXG4gICAgICAgIFAubGVzc1RoYW5PckVxdWFsVG8gPSBQLmx0ZSA9IGZ1bmN0aW9uICggeSwgYiApIHtcbiAgICAgICAgICAgIGlkID0gOTtcbiAgICAgICAgICAgIHJldHVybiAoIGIgPSBjb21wYXJlKCB0aGlzLCBuZXcgQmlnTnVtYmVyKCB5LCBiICkgKSApID09PSAtMSB8fCBiID09PSAwO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogIG4gLSAwID0gblxuICAgICAgICAgKiAgbiAtIE4gPSBOXG4gICAgICAgICAqICBuIC0gSSA9IC1JXG4gICAgICAgICAqICAwIC0gbiA9IC1uXG4gICAgICAgICAqICAwIC0gMCA9IDBcbiAgICAgICAgICogIDAgLSBOID0gTlxuICAgICAgICAgKiAgMCAtIEkgPSAtSVxuICAgICAgICAgKiAgTiAtIG4gPSBOXG4gICAgICAgICAqICBOIC0gMCA9IE5cbiAgICAgICAgICogIE4gLSBOID0gTlxuICAgICAgICAgKiAgTiAtIEkgPSBOXG4gICAgICAgICAqICBJIC0gbiA9IElcbiAgICAgICAgICogIEkgLSAwID0gSVxuICAgICAgICAgKiAgSSAtIE4gPSBOXG4gICAgICAgICAqICBJIC0gSSA9IE5cbiAgICAgICAgICpcbiAgICAgICAgICogUmV0dXJuIGEgbmV3IEJpZ051bWJlciB3aG9zZSB2YWx1ZSBpcyB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgbWludXMgdGhlIHZhbHVlIG9mXG4gICAgICAgICAqIEJpZ051bWJlcih5LCBiKS5cbiAgICAgICAgICovXG4gICAgICAgIFAubWludXMgPSBQLnN1YiA9IGZ1bmN0aW9uICggeSwgYiApIHtcbiAgICAgICAgICAgIHZhciBpLCBqLCB0LCB4TFR5LFxuICAgICAgICAgICAgICAgIHggPSB0aGlzLFxuICAgICAgICAgICAgICAgIGEgPSB4LnM7XG5cbiAgICAgICAgICAgIGlkID0gMTA7XG4gICAgICAgICAgICB5ID0gbmV3IEJpZ051bWJlciggeSwgYiApO1xuICAgICAgICAgICAgYiA9IHkucztcblxuICAgICAgICAgICAgLy8gRWl0aGVyIE5hTj9cbiAgICAgICAgICAgIGlmICggIWEgfHwgIWIgKSByZXR1cm4gbmV3IEJpZ051bWJlcihOYU4pO1xuXG4gICAgICAgICAgICAvLyBTaWducyBkaWZmZXI/XG4gICAgICAgICAgICBpZiAoIGEgIT0gYiApIHtcbiAgICAgICAgICAgICAgICB5LnMgPSAtYjtcbiAgICAgICAgICAgICAgICByZXR1cm4geC5wbHVzKHkpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgeGUgPSB4LmUgLyBMT0dfQkFTRSxcbiAgICAgICAgICAgICAgICB5ZSA9IHkuZSAvIExPR19CQVNFLFxuICAgICAgICAgICAgICAgIHhjID0geC5jLFxuICAgICAgICAgICAgICAgIHljID0geS5jO1xuXG4gICAgICAgICAgICBpZiAoICF4ZSB8fCAheWUgKSB7XG5cbiAgICAgICAgICAgICAgICAvLyBFaXRoZXIgSW5maW5pdHk/XG4gICAgICAgICAgICAgICAgaWYgKCAheGMgfHwgIXljICkgcmV0dXJuIHhjID8gKCB5LnMgPSAtYiwgeSApIDogbmV3IEJpZ051bWJlciggeWMgPyB4IDogTmFOICk7XG5cbiAgICAgICAgICAgICAgICAvLyBFaXRoZXIgemVybz9cbiAgICAgICAgICAgICAgICBpZiAoICF4Y1swXSB8fCAheWNbMF0gKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gUmV0dXJuIHkgaWYgeSBpcyBub24temVybywgeCBpZiB4IGlzIG5vbi16ZXJvLCBvciB6ZXJvIGlmIGJvdGggYXJlIHplcm8uXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB5Y1swXSA/ICggeS5zID0gLWIsIHkgKSA6IG5ldyBCaWdOdW1iZXIoIHhjWzBdID8geCA6XG5cbiAgICAgICAgICAgICAgICAgICAgICAvLyBJRUVFIDc1NCAoMjAwOCkgNi4zOiBuIC0gbiA9IC0wIHdoZW4gcm91bmRpbmcgdG8gLUluZmluaXR5XG4gICAgICAgICAgICAgICAgICAgICAgUk9VTkRJTkdfTU9ERSA9PSAzID8gLTAgOiAwICk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB4ZSA9IGJpdEZsb29yKHhlKTtcbiAgICAgICAgICAgIHllID0gYml0Rmxvb3IoeWUpO1xuICAgICAgICAgICAgeGMgPSB4Yy5zbGljZSgpO1xuXG4gICAgICAgICAgICAvLyBEZXRlcm1pbmUgd2hpY2ggaXMgdGhlIGJpZ2dlciBudW1iZXIuXG4gICAgICAgICAgICBpZiAoIGEgPSB4ZSAtIHllICkge1xuXG4gICAgICAgICAgICAgICAgaWYgKCB4TFR5ID0gYSA8IDAgKSB7XG4gICAgICAgICAgICAgICAgICAgIGEgPSAtYTtcbiAgICAgICAgICAgICAgICAgICAgdCA9IHhjO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHllID0geGU7XG4gICAgICAgICAgICAgICAgICAgIHQgPSB5YztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB0LnJldmVyc2UoKTtcblxuICAgICAgICAgICAgICAgIC8vIFByZXBlbmQgemVyb3MgdG8gZXF1YWxpc2UgZXhwb25lbnRzLlxuICAgICAgICAgICAgICAgIGZvciAoIGIgPSBhOyBiLS07IHQucHVzaCgwKSApO1xuICAgICAgICAgICAgICAgIHQucmV2ZXJzZSgpO1xuICAgICAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgICAgIC8vIEV4cG9uZW50cyBlcXVhbC4gQ2hlY2sgZGlnaXQgYnkgZGlnaXQuXG4gICAgICAgICAgICAgICAgaiA9ICggeExUeSA9ICggYSA9IHhjLmxlbmd0aCApIDwgKCBiID0geWMubGVuZ3RoICkgKSA/IGEgOiBiO1xuXG4gICAgICAgICAgICAgICAgZm9yICggYSA9IGIgPSAwOyBiIDwgajsgYisrICkge1xuXG4gICAgICAgICAgICAgICAgICAgIGlmICggeGNbYl0gIT0geWNbYl0gKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB4TFR5ID0geGNbYl0gPCB5Y1tiXTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyB4IDwgeT8gUG9pbnQgeGMgdG8gdGhlIGFycmF5IG9mIHRoZSBiaWdnZXIgbnVtYmVyLlxuICAgICAgICAgICAgaWYgKHhMVHkpIHQgPSB4YywgeGMgPSB5YywgeWMgPSB0LCB5LnMgPSAteS5zO1xuXG4gICAgICAgICAgICBiID0gKCBqID0geWMubGVuZ3RoICkgLSAoIGkgPSB4Yy5sZW5ndGggKTtcblxuICAgICAgICAgICAgLy8gQXBwZW5kIHplcm9zIHRvIHhjIGlmIHNob3J0ZXIuXG4gICAgICAgICAgICAvLyBObyBuZWVkIHRvIGFkZCB6ZXJvcyB0byB5YyBpZiBzaG9ydGVyIGFzIHN1YnRyYWN0IG9ubHkgbmVlZHMgdG8gc3RhcnQgYXQgeWMubGVuZ3RoLlxuICAgICAgICAgICAgaWYgKCBiID4gMCApIGZvciAoIDsgYi0tOyB4Y1tpKytdID0gMCApO1xuICAgICAgICAgICAgYiA9IEJBU0UgLSAxO1xuXG4gICAgICAgICAgICAvLyBTdWJ0cmFjdCB5YyBmcm9tIHhjLlxuICAgICAgICAgICAgZm9yICggOyBqID4gYTsgKSB7XG5cbiAgICAgICAgICAgICAgICBpZiAoIHhjWy0tal0gPCB5Y1tqXSApIHtcbiAgICAgICAgICAgICAgICAgICAgZm9yICggaSA9IGo7IGkgJiYgIXhjWy0taV07IHhjW2ldID0gYiApO1xuICAgICAgICAgICAgICAgICAgICAtLXhjW2ldO1xuICAgICAgICAgICAgICAgICAgICB4Y1tqXSArPSBCQVNFO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHhjW2pdIC09IHljW2pdO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBSZW1vdmUgbGVhZGluZyB6ZXJvcyBhbmQgYWRqdXN0IGV4cG9uZW50IGFjY29yZGluZ2x5LlxuICAgICAgICAgICAgZm9yICggOyB4Y1swXSA9PSAwOyB4Yy5zaGlmdCgpLCAtLXllICk7XG5cbiAgICAgICAgICAgIC8vIFplcm8/XG4gICAgICAgICAgICBpZiAoICF4Y1swXSApIHtcblxuICAgICAgICAgICAgICAgIC8vIEZvbGxvd2luZyBJRUVFIDc1NCAoMjAwOCkgNi4zLFxuICAgICAgICAgICAgICAgIC8vIG4gLSBuID0gKzAgIGJ1dCAgbiAtIG4gPSAtMCAgd2hlbiByb3VuZGluZyB0b3dhcmRzIC1JbmZpbml0eS5cbiAgICAgICAgICAgICAgICB5LnMgPSBST1VORElOR19NT0RFID09IDMgPyAtMSA6IDE7XG4gICAgICAgICAgICAgICAgeS5jID0gWyB5LmUgPSAwIF07XG4gICAgICAgICAgICAgICAgcmV0dXJuIHk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIE5vIG5lZWQgdG8gY2hlY2sgZm9yIEluZmluaXR5IGFzICt4IC0gK3kgIT0gSW5maW5pdHkgJiYgLXggLSAteSAhPSBJbmZpbml0eVxuICAgICAgICAgICAgLy8gZm9yIGZpbml0ZSB4IGFuZCB5LlxuICAgICAgICAgICAgcmV0dXJuIG5vcm1hbGlzZSggeSwgeGMsIHllICk7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiAgIG4gJSAwID0gIE5cbiAgICAgICAgICogICBuICUgTiA9ICBOXG4gICAgICAgICAqICAgbiAlIEkgPSAgblxuICAgICAgICAgKiAgIDAgJSBuID0gIDBcbiAgICAgICAgICogIC0wICUgbiA9IC0wXG4gICAgICAgICAqICAgMCAlIDAgPSAgTlxuICAgICAgICAgKiAgIDAgJSBOID0gIE5cbiAgICAgICAgICogICAwICUgSSA9ICAwXG4gICAgICAgICAqICAgTiAlIG4gPSAgTlxuICAgICAgICAgKiAgIE4gJSAwID0gIE5cbiAgICAgICAgICogICBOICUgTiA9ICBOXG4gICAgICAgICAqICAgTiAlIEkgPSAgTlxuICAgICAgICAgKiAgIEkgJSBuID0gIE5cbiAgICAgICAgICogICBJICUgMCA9ICBOXG4gICAgICAgICAqICAgSSAlIE4gPSAgTlxuICAgICAgICAgKiAgIEkgJSBJID0gIE5cbiAgICAgICAgICpcbiAgICAgICAgICogUmV0dXJuIGEgbmV3IEJpZ051bWJlciB3aG9zZSB2YWx1ZSBpcyB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgbW9kdWxvIHRoZSB2YWx1ZSBvZlxuICAgICAgICAgKiBCaWdOdW1iZXIoeSwgYikuIFRoZSByZXN1bHQgZGVwZW5kcyBvbiB0aGUgdmFsdWUgb2YgTU9EVUxPX01PREUuXG4gICAgICAgICAqL1xuICAgICAgICBQLm1vZHVsbyA9IFAubW9kID0gZnVuY3Rpb24gKCB5LCBiICkge1xuICAgICAgICAgICAgdmFyIHEsIHMsXG4gICAgICAgICAgICAgICAgeCA9IHRoaXM7XG5cbiAgICAgICAgICAgIGlkID0gMTE7XG4gICAgICAgICAgICB5ID0gbmV3IEJpZ051bWJlciggeSwgYiApO1xuXG4gICAgICAgICAgICAvLyBSZXR1cm4gTmFOIGlmIHggaXMgSW5maW5pdHkgb3IgTmFOLCBvciB5IGlzIE5hTiBvciB6ZXJvLlxuICAgICAgICAgICAgaWYgKCAheC5jIHx8ICF5LnMgfHwgeS5jICYmICF5LmNbMF0gKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBCaWdOdW1iZXIoTmFOKTtcblxuICAgICAgICAgICAgLy8gUmV0dXJuIHggaWYgeSBpcyBJbmZpbml0eSBvciB4IGlzIHplcm8uXG4gICAgICAgICAgICB9IGVsc2UgaWYgKCAheS5jIHx8IHguYyAmJiAheC5jWzBdICkge1xuICAgICAgICAgICAgICAgIHJldHVybiBuZXcgQmlnTnVtYmVyKHgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoIE1PRFVMT19NT0RFID09IDkgKSB7XG5cbiAgICAgICAgICAgICAgICAvLyBFdWNsaWRpYW4gZGl2aXNpb246IHEgPSBzaWduKHkpICogZmxvb3IoeCAvIGFicyh5KSlcbiAgICAgICAgICAgICAgICAvLyByID0geCAtIHF5ICAgIHdoZXJlICAwIDw9IHIgPCBhYnMoeSlcbiAgICAgICAgICAgICAgICBzID0geS5zO1xuICAgICAgICAgICAgICAgIHkucyA9IDE7XG4gICAgICAgICAgICAgICAgcSA9IGRpdiggeCwgeSwgMCwgMyApO1xuICAgICAgICAgICAgICAgIHkucyA9IHM7XG4gICAgICAgICAgICAgICAgcS5zICo9IHM7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHEgPSBkaXYoIHgsIHksIDAsIE1PRFVMT19NT0RFICk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiB4Lm1pbnVzKCBxLnRpbWVzKHkpICk7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gYSBuZXcgQmlnTnVtYmVyIHdob3NlIHZhbHVlIGlzIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciBuZWdhdGVkLFxuICAgICAgICAgKiBpLmUuIG11bHRpcGxpZWQgYnkgLTEuXG4gICAgICAgICAqL1xuICAgICAgICBQLm5lZ2F0ZWQgPSBQLm5lZyA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciB4ID0gbmV3IEJpZ051bWJlcih0aGlzKTtcbiAgICAgICAgICAgIHgucyA9IC14LnMgfHwgbnVsbDtcbiAgICAgICAgICAgIHJldHVybiB4O1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogIG4gKyAwID0gblxuICAgICAgICAgKiAgbiArIE4gPSBOXG4gICAgICAgICAqICBuICsgSSA9IElcbiAgICAgICAgICogIDAgKyBuID0gblxuICAgICAgICAgKiAgMCArIDAgPSAwXG4gICAgICAgICAqICAwICsgTiA9IE5cbiAgICAgICAgICogIDAgKyBJID0gSVxuICAgICAgICAgKiAgTiArIG4gPSBOXG4gICAgICAgICAqICBOICsgMCA9IE5cbiAgICAgICAgICogIE4gKyBOID0gTlxuICAgICAgICAgKiAgTiArIEkgPSBOXG4gICAgICAgICAqICBJICsgbiA9IElcbiAgICAgICAgICogIEkgKyAwID0gSVxuICAgICAgICAgKiAgSSArIE4gPSBOXG4gICAgICAgICAqICBJICsgSSA9IElcbiAgICAgICAgICpcbiAgICAgICAgICogUmV0dXJuIGEgbmV3IEJpZ051bWJlciB3aG9zZSB2YWx1ZSBpcyB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgcGx1cyB0aGUgdmFsdWUgb2ZcbiAgICAgICAgICogQmlnTnVtYmVyKHksIGIpLlxuICAgICAgICAgKi9cbiAgICAgICAgUC5wbHVzID0gUC5hZGQgPSBmdW5jdGlvbiAoIHksIGIgKSB7XG4gICAgICAgICAgICB2YXIgdCxcbiAgICAgICAgICAgICAgICB4ID0gdGhpcyxcbiAgICAgICAgICAgICAgICBhID0geC5zO1xuXG4gICAgICAgICAgICBpZCA9IDEyO1xuICAgICAgICAgICAgeSA9IG5ldyBCaWdOdW1iZXIoIHksIGIgKTtcbiAgICAgICAgICAgIGIgPSB5LnM7XG5cbiAgICAgICAgICAgIC8vIEVpdGhlciBOYU4/XG4gICAgICAgICAgICBpZiAoICFhIHx8ICFiICkgcmV0dXJuIG5ldyBCaWdOdW1iZXIoTmFOKTtcblxuICAgICAgICAgICAgLy8gU2lnbnMgZGlmZmVyP1xuICAgICAgICAgICAgIGlmICggYSAhPSBiICkge1xuICAgICAgICAgICAgICAgIHkucyA9IC1iO1xuICAgICAgICAgICAgICAgIHJldHVybiB4Lm1pbnVzKHkpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgeGUgPSB4LmUgLyBMT0dfQkFTRSxcbiAgICAgICAgICAgICAgICB5ZSA9IHkuZSAvIExPR19CQVNFLFxuICAgICAgICAgICAgICAgIHhjID0geC5jLFxuICAgICAgICAgICAgICAgIHljID0geS5jO1xuXG4gICAgICAgICAgICBpZiAoICF4ZSB8fCAheWUgKSB7XG5cbiAgICAgICAgICAgICAgICAvLyBSZXR1cm4gwrFJbmZpbml0eSBpZiBlaXRoZXIgwrFJbmZpbml0eS5cbiAgICAgICAgICAgICAgICBpZiAoICF4YyB8fCAheWMgKSByZXR1cm4gbmV3IEJpZ051bWJlciggYSAvIDAgKTtcblxuICAgICAgICAgICAgICAgIC8vIEVpdGhlciB6ZXJvP1xuICAgICAgICAgICAgICAgIC8vIFJldHVybiB5IGlmIHkgaXMgbm9uLXplcm8sIHggaWYgeCBpcyBub24temVybywgb3IgemVybyBpZiBib3RoIGFyZSB6ZXJvLlxuICAgICAgICAgICAgICAgIGlmICggIXhjWzBdIHx8ICF5Y1swXSApIHJldHVybiB5Y1swXSA/IHkgOiBuZXcgQmlnTnVtYmVyKCB4Y1swXSA/IHggOiBhICogMCApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB4ZSA9IGJpdEZsb29yKHhlKTtcbiAgICAgICAgICAgIHllID0gYml0Rmxvb3IoeWUpO1xuICAgICAgICAgICAgeGMgPSB4Yy5zbGljZSgpO1xuXG4gICAgICAgICAgICAvLyBQcmVwZW5kIHplcm9zIHRvIGVxdWFsaXNlIGV4cG9uZW50cy4gRmFzdGVyIHRvIHVzZSByZXZlcnNlIHRoZW4gZG8gdW5zaGlmdHMuXG4gICAgICAgICAgICBpZiAoIGEgPSB4ZSAtIHllICkge1xuICAgICAgICAgICAgICAgIGlmICggYSA+IDAgKSB7XG4gICAgICAgICAgICAgICAgICAgIHllID0geGU7XG4gICAgICAgICAgICAgICAgICAgIHQgPSB5YztcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBhID0gLWE7XG4gICAgICAgICAgICAgICAgICAgIHQgPSB4YztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB0LnJldmVyc2UoKTtcbiAgICAgICAgICAgICAgICBmb3IgKCA7IGEtLTsgdC5wdXNoKDApICk7XG4gICAgICAgICAgICAgICAgdC5yZXZlcnNlKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGEgPSB4Yy5sZW5ndGg7XG4gICAgICAgICAgICBiID0geWMubGVuZ3RoO1xuXG4gICAgICAgICAgICAvLyBQb2ludCB4YyB0byB0aGUgbG9uZ2VyIGFycmF5LCBhbmQgYiB0byB0aGUgc2hvcnRlciBsZW5ndGguXG4gICAgICAgICAgICBpZiAoIGEgLSBiIDwgMCApIHQgPSB5YywgeWMgPSB4YywgeGMgPSB0LCBiID0gYTtcblxuICAgICAgICAgICAgLy8gT25seSBzdGFydCBhZGRpbmcgYXQgeWMubGVuZ3RoIC0gMSBhcyB0aGUgZnVydGhlciBkaWdpdHMgb2YgeGMgY2FuIGJlIGlnbm9yZWQuXG4gICAgICAgICAgICBmb3IgKCBhID0gMDsgYjsgKSB7XG4gICAgICAgICAgICAgICAgYSA9ICggeGNbLS1iXSA9IHhjW2JdICsgeWNbYl0gKyBhICkgLyBCQVNFIHwgMDtcbiAgICAgICAgICAgICAgICB4Y1tiXSAlPSBCQVNFO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoYSkge1xuICAgICAgICAgICAgICAgIHhjLnVuc2hpZnQoYSk7XG4gICAgICAgICAgICAgICAgKyt5ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gTm8gbmVlZCB0byBjaGVjayBmb3IgemVybywgYXMgK3ggKyAreSAhPSAwICYmIC14ICsgLXkgIT0gMFxuICAgICAgICAgICAgLy8geWUgPSBNQVhfRVhQICsgMSBwb3NzaWJsZVxuICAgICAgICAgICAgcmV0dXJuIG5vcm1hbGlzZSggeSwgeGMsIHllICk7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gdGhlIG51bWJlciBvZiBzaWduaWZpY2FudCBkaWdpdHMgb2YgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyLlxuICAgICAgICAgKlxuICAgICAgICAgKiBbel0ge2Jvb2xlYW58bnVtYmVyfSBXaGV0aGVyIHRvIGNvdW50IGludGVnZXItcGFydCB0cmFpbGluZyB6ZXJvczogdHJ1ZSwgZmFsc2UsIDEgb3IgMC5cbiAgICAgICAgICovXG4gICAgICAgIFAucHJlY2lzaW9uID0gUC5zZCA9IGZ1bmN0aW9uICh6KSB7XG4gICAgICAgICAgICB2YXIgbiwgdixcbiAgICAgICAgICAgICAgICB4ID0gdGhpcyxcbiAgICAgICAgICAgICAgICBjID0geC5jO1xuXG4gICAgICAgICAgICAvLyAncHJlY2lzaW9uKCkgYXJndW1lbnQgbm90IGEgYm9vbGVhbiBvciBiaW5hcnkgZGlnaXQ6IHt6fSdcbiAgICAgICAgICAgIGlmICggeiAhPSBudWxsICYmIHogIT09ICEheiAmJiB6ICE9PSAxICYmIHogIT09IDAgKSB7XG4gICAgICAgICAgICAgICAgaWYgKEVSUk9SUykgcmFpc2UoIDEzLCAnYXJndW1lbnQnICsgbm90Qm9vbCwgeiApO1xuICAgICAgICAgICAgICAgIGlmICggeiAhPSAhIXogKSB6ID0gbnVsbDtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKCAhYyApIHJldHVybiBudWxsO1xuICAgICAgICAgICAgdiA9IGMubGVuZ3RoIC0gMTtcbiAgICAgICAgICAgIG4gPSB2ICogTE9HX0JBU0UgKyAxO1xuXG4gICAgICAgICAgICBpZiAoIHYgPSBjW3ZdICkge1xuXG4gICAgICAgICAgICAgICAgLy8gU3VidHJhY3QgdGhlIG51bWJlciBvZiB0cmFpbGluZyB6ZXJvcyBvZiB0aGUgbGFzdCBlbGVtZW50LlxuICAgICAgICAgICAgICAgIGZvciAoIDsgdiAlIDEwID09IDA7IHYgLz0gMTAsIG4tLSApO1xuXG4gICAgICAgICAgICAgICAgLy8gQWRkIHRoZSBudW1iZXIgb2YgZGlnaXRzIG9mIHRoZSBmaXJzdCBlbGVtZW50LlxuICAgICAgICAgICAgICAgIGZvciAoIHYgPSBjWzBdOyB2ID49IDEwOyB2IC89IDEwLCBuKysgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKCB6ICYmIHguZSArIDEgPiBuICkgbiA9IHguZSArIDE7XG5cbiAgICAgICAgICAgIHJldHVybiBuO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIGEgbmV3IEJpZ051bWJlciB3aG9zZSB2YWx1ZSBpcyB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgcm91bmRlZCB0byBhIG1heGltdW0gb2ZcbiAgICAgICAgICogZHAgZGVjaW1hbCBwbGFjZXMgdXNpbmcgcm91bmRpbmcgbW9kZSBybSwgb3IgdG8gMCBhbmQgUk9VTkRJTkdfTU9ERSByZXNwZWN0aXZlbHkgaWZcbiAgICAgICAgICogb21pdHRlZC5cbiAgICAgICAgICpcbiAgICAgICAgICogW2RwXSB7bnVtYmVyfSBEZWNpbWFsIHBsYWNlcy4gSW50ZWdlciwgMCB0byBNQVggaW5jbHVzaXZlLlxuICAgICAgICAgKiBbcm1dIHtudW1iZXJ9IFJvdW5kaW5nIG1vZGUuIEludGVnZXIsIDAgdG8gOCBpbmNsdXNpdmUuXG4gICAgICAgICAqXG4gICAgICAgICAqICdyb3VuZCgpIGRlY2ltYWwgcGxhY2VzIG91dCBvZiByYW5nZToge2RwfSdcbiAgICAgICAgICogJ3JvdW5kKCkgZGVjaW1hbCBwbGFjZXMgbm90IGFuIGludGVnZXI6IHtkcH0nXG4gICAgICAgICAqICdyb3VuZCgpIHJvdW5kaW5nIG1vZGUgbm90IGFuIGludGVnZXI6IHtybX0nXG4gICAgICAgICAqICdyb3VuZCgpIHJvdW5kaW5nIG1vZGUgb3V0IG9mIHJhbmdlOiB7cm19J1xuICAgICAgICAgKi9cbiAgICAgICAgUC5yb3VuZCA9IGZ1bmN0aW9uICggZHAsIHJtICkge1xuICAgICAgICAgICAgdmFyIG4gPSBuZXcgQmlnTnVtYmVyKHRoaXMpO1xuXG4gICAgICAgICAgICBpZiAoIGRwID09IG51bGwgfHwgaXNWYWxpZEludCggZHAsIDAsIE1BWCwgMTUgKSApIHtcbiAgICAgICAgICAgICAgICByb3VuZCggbiwgfn5kcCArIHRoaXMuZSArIDEsIHJtID09IG51bGwgfHxcbiAgICAgICAgICAgICAgICAgICFpc1ZhbGlkSW50KCBybSwgMCwgOCwgMTUsIHJvdW5kaW5nTW9kZSApID8gUk9VTkRJTkdfTU9ERSA6IHJtIHwgMCApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gbjtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiBhIG5ldyBCaWdOdW1iZXIgd2hvc2UgdmFsdWUgaXMgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIHNoaWZ0ZWQgYnkgayBwbGFjZXNcbiAgICAgICAgICogKHBvd2VycyBvZiAxMCkuIFNoaWZ0IHRvIHRoZSByaWdodCBpZiBuID4gMCwgYW5kIHRvIHRoZSBsZWZ0IGlmIG4gPCAwLlxuICAgICAgICAgKlxuICAgICAgICAgKiBrIHtudW1iZXJ9IEludGVnZXIsIC1NQVhfU0FGRV9JTlRFR0VSIHRvIE1BWF9TQUZFX0lOVEVHRVIgaW5jbHVzaXZlLlxuICAgICAgICAgKlxuICAgICAgICAgKiBJZiBrIGlzIG91dCBvZiByYW5nZSBhbmQgRVJST1JTIGlzIGZhbHNlLCB0aGUgcmVzdWx0IHdpbGwgYmUgwrEwIGlmIGsgPCAwLCBvciDCsUluZmluaXR5XG4gICAgICAgICAqIG90aGVyd2lzZS5cbiAgICAgICAgICpcbiAgICAgICAgICogJ3NoaWZ0KCkgYXJndW1lbnQgbm90IGFuIGludGVnZXI6IHtrfSdcbiAgICAgICAgICogJ3NoaWZ0KCkgYXJndW1lbnQgb3V0IG9mIHJhbmdlOiB7a30nXG4gICAgICAgICAqL1xuICAgICAgICBQLnNoaWZ0ID0gZnVuY3Rpb24gKGspIHtcbiAgICAgICAgICAgIHZhciBuID0gdGhpcztcbiAgICAgICAgICAgIHJldHVybiBpc1ZhbGlkSW50KCBrLCAtTUFYX1NBRkVfSU5URUdFUiwgTUFYX1NBRkVfSU5URUdFUiwgMTYsICdhcmd1bWVudCcgKVxuXG4gICAgICAgICAgICAgIC8vIGsgPCAxZSsyMSwgb3IgdHJ1bmNhdGUoaykgd2lsbCBwcm9kdWNlIGV4cG9uZW50aWFsIG5vdGF0aW9uLlxuICAgICAgICAgICAgICA/IG4udGltZXMoICcxZScgKyB0cnVuY2F0ZShrKSApXG4gICAgICAgICAgICAgIDogbmV3IEJpZ051bWJlciggbi5jICYmIG4uY1swXSAmJiAoIGsgPCAtTUFYX1NBRkVfSU5URUdFUiB8fCBrID4gTUFYX1NBRkVfSU5URUdFUiApXG4gICAgICAgICAgICAgICAgPyBuLnMgKiAoIGsgPCAwID8gMCA6IDEgLyAwIClcbiAgICAgICAgICAgICAgICA6IG4gKTtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqICBzcXJ0KC1uKSA9ICBOXG4gICAgICAgICAqICBzcXJ0KCBOKSA9ICBOXG4gICAgICAgICAqICBzcXJ0KC1JKSA9ICBOXG4gICAgICAgICAqICBzcXJ0KCBJKSA9ICBJXG4gICAgICAgICAqICBzcXJ0KCAwKSA9ICAwXG4gICAgICAgICAqICBzcXJ0KC0wKSA9IC0wXG4gICAgICAgICAqXG4gICAgICAgICAqIFJldHVybiBhIG5ldyBCaWdOdW1iZXIgd2hvc2UgdmFsdWUgaXMgdGhlIHNxdWFyZSByb290IG9mIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlcixcbiAgICAgICAgICogcm91bmRlZCBhY2NvcmRpbmcgdG8gREVDSU1BTF9QTEFDRVMgYW5kIFJPVU5ESU5HX01PREUuXG4gICAgICAgICAqL1xuICAgICAgICBQLnNxdWFyZVJvb3QgPSBQLnNxcnQgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgbSwgbiwgciwgcmVwLCB0LFxuICAgICAgICAgICAgICAgIHggPSB0aGlzLFxuICAgICAgICAgICAgICAgIGMgPSB4LmMsXG4gICAgICAgICAgICAgICAgcyA9IHgucyxcbiAgICAgICAgICAgICAgICBlID0geC5lLFxuICAgICAgICAgICAgICAgIGRwID0gREVDSU1BTF9QTEFDRVMgKyA0LFxuICAgICAgICAgICAgICAgIGhhbGYgPSBuZXcgQmlnTnVtYmVyKCcwLjUnKTtcblxuICAgICAgICAgICAgLy8gTmVnYXRpdmUvTmFOL0luZmluaXR5L3plcm8/XG4gICAgICAgICAgICBpZiAoIHMgIT09IDEgfHwgIWMgfHwgIWNbMF0gKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBCaWdOdW1iZXIoICFzIHx8IHMgPCAwICYmICggIWMgfHwgY1swXSApID8gTmFOIDogYyA/IHggOiAxIC8gMCApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBJbml0aWFsIGVzdGltYXRlLlxuICAgICAgICAgICAgcyA9IE1hdGguc3FydCggK3ggKTtcblxuICAgICAgICAgICAgLy8gTWF0aC5zcXJ0IHVuZGVyZmxvdy9vdmVyZmxvdz9cbiAgICAgICAgICAgIC8vIFBhc3MgeCB0byBNYXRoLnNxcnQgYXMgaW50ZWdlciwgdGhlbiBhZGp1c3QgdGhlIGV4cG9uZW50IG9mIHRoZSByZXN1bHQuXG4gICAgICAgICAgICBpZiAoIHMgPT0gMCB8fCBzID09IDEgLyAwICkge1xuICAgICAgICAgICAgICAgIG4gPSBjb2VmZlRvU3RyaW5nKGMpO1xuICAgICAgICAgICAgICAgIGlmICggKCBuLmxlbmd0aCArIGUgKSAlIDIgPT0gMCApIG4gKz0gJzAnO1xuICAgICAgICAgICAgICAgIHMgPSBNYXRoLnNxcnQobik7XG4gICAgICAgICAgICAgICAgZSA9IGJpdEZsb29yKCAoIGUgKyAxICkgLyAyICkgLSAoIGUgPCAwIHx8IGUgJSAyICk7XG5cbiAgICAgICAgICAgICAgICBpZiAoIHMgPT0gMSAvIDAgKSB7XG4gICAgICAgICAgICAgICAgICAgIG4gPSAnMWUnICsgZTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBuID0gcy50b0V4cG9uZW50aWFsKCk7XG4gICAgICAgICAgICAgICAgICAgIG4gPSBuLnNsaWNlKCAwLCBuLmluZGV4T2YoJ2UnKSArIDEgKSArIGU7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgciA9IG5ldyBCaWdOdW1iZXIobik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHIgPSBuZXcgQmlnTnVtYmVyKCBzICsgJycgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gQ2hlY2sgZm9yIHplcm8uXG4gICAgICAgICAgICAvLyByIGNvdWxkIGJlIHplcm8gaWYgTUlOX0VYUCBpcyBjaGFuZ2VkIGFmdGVyIHRoZSB0aGlzIHZhbHVlIHdhcyBjcmVhdGVkLlxuICAgICAgICAgICAgLy8gVGhpcyB3b3VsZCBjYXVzZSBhIGRpdmlzaW9uIGJ5IHplcm8gKHgvdCkgYW5kIGhlbmNlIEluZmluaXR5IGJlbG93LCB3aGljaCB3b3VsZCBjYXVzZVxuICAgICAgICAgICAgLy8gY29lZmZUb1N0cmluZyB0byB0aHJvdy5cbiAgICAgICAgICAgIGlmICggci5jWzBdICkge1xuICAgICAgICAgICAgICAgIGUgPSByLmU7XG4gICAgICAgICAgICAgICAgcyA9IGUgKyBkcDtcbiAgICAgICAgICAgICAgICBpZiAoIHMgPCAzICkgcyA9IDA7XG5cbiAgICAgICAgICAgICAgICAvLyBOZXd0b24tUmFwaHNvbiBpdGVyYXRpb24uXG4gICAgICAgICAgICAgICAgZm9yICggOyA7ICkge1xuICAgICAgICAgICAgICAgICAgICB0ID0gcjtcbiAgICAgICAgICAgICAgICAgICAgciA9IGhhbGYudGltZXMoIHQucGx1cyggZGl2KCB4LCB0LCBkcCwgMSApICkgKTtcblxuICAgICAgICAgICAgICAgICAgICBpZiAoIGNvZWZmVG9TdHJpbmcoIHQuYyAgICkuc2xpY2UoIDAsIHMgKSA9PT0gKCBuID1cbiAgICAgICAgICAgICAgICAgICAgICAgICBjb2VmZlRvU3RyaW5nKCByLmMgKSApLnNsaWNlKCAwLCBzICkgKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIFRoZSBleHBvbmVudCBvZiByIG1heSBoZXJlIGJlIG9uZSBsZXNzIHRoYW4gdGhlIGZpbmFsIHJlc3VsdCBleHBvbmVudCxcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGUuZyAwLjAwMDk5OTkgKGUtNCkgLS0+IDAuMDAxIChlLTMpLCBzbyBhZGp1c3QgcyBzbyB0aGUgcm91bmRpbmcgZGlnaXRzXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBhcmUgaW5kZXhlZCBjb3JyZWN0bHkuXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoIHIuZSA8IGUgKSAtLXM7XG4gICAgICAgICAgICAgICAgICAgICAgICBuID0gbi5zbGljZSggcyAtIDMsIHMgKyAxICk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIFRoZSA0dGggcm91bmRpbmcgZGlnaXQgbWF5IGJlIGluIGVycm9yIGJ5IC0xIHNvIGlmIHRoZSA0IHJvdW5kaW5nIGRpZ2l0c1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gYXJlIDk5OTkgb3IgNDk5OSAoaS5lLiBhcHByb2FjaGluZyBhIHJvdW5kaW5nIGJvdW5kYXJ5KSBjb250aW51ZSB0aGVcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGl0ZXJhdGlvbi5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICggbiA9PSAnOTk5OScgfHwgIXJlcCAmJiBuID09ICc0OTk5JyApIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE9uIHRoZSBmaXJzdCBpdGVyYXRpb24gb25seSwgY2hlY2sgdG8gc2VlIGlmIHJvdW5kaW5nIHVwIGdpdmVzIHRoZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGV4YWN0IHJlc3VsdCBhcyB0aGUgbmluZXMgbWF5IGluZmluaXRlbHkgcmVwZWF0LlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICggIXJlcCApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQoIHQsIHQuZSArIERFQ0lNQUxfUExBQ0VTICsgMiwgMCApO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICggdC50aW1lcyh0KS5lcSh4KSApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHIgPSB0O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcCArPSA0O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHMgKz0gNDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAgPSAxO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIHJvdW5kaW5nIGRpZ2l0cyBhcmUgbnVsbCwgMHswLDR9IG9yIDUwezAsM30sIGNoZWNrIGZvciBleGFjdFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHJlc3VsdC4gSWYgbm90LCB0aGVuIHRoZXJlIGFyZSBmdXJ0aGVyIGRpZ2l0cyBhbmQgbSB3aWxsIGJlIHRydXRoeS5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoICErbiB8fCAhK24uc2xpY2UoMSkgJiYgbi5jaGFyQXQoMCkgPT0gJzUnICkge1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFRydW5jYXRlIHRvIHRoZSBmaXJzdCByb3VuZGluZyBkaWdpdC5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQoIHIsIHIuZSArIERFQ0lNQUxfUExBQ0VTICsgMiwgMSApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtID0gIXIudGltZXMocikuZXEoeCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiByb3VuZCggciwgci5lICsgREVDSU1BTF9QTEFDRVMgKyAxLCBST1VORElOR19NT0RFLCBtICk7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiAgbiAqIDAgPSAwXG4gICAgICAgICAqICBuICogTiA9IE5cbiAgICAgICAgICogIG4gKiBJID0gSVxuICAgICAgICAgKiAgMCAqIG4gPSAwXG4gICAgICAgICAqICAwICogMCA9IDBcbiAgICAgICAgICogIDAgKiBOID0gTlxuICAgICAgICAgKiAgMCAqIEkgPSBOXG4gICAgICAgICAqICBOICogbiA9IE5cbiAgICAgICAgICogIE4gKiAwID0gTlxuICAgICAgICAgKiAgTiAqIE4gPSBOXG4gICAgICAgICAqICBOICogSSA9IE5cbiAgICAgICAgICogIEkgKiBuID0gSVxuICAgICAgICAgKiAgSSAqIDAgPSBOXG4gICAgICAgICAqICBJICogTiA9IE5cbiAgICAgICAgICogIEkgKiBJID0gSVxuICAgICAgICAgKlxuICAgICAgICAgKiBSZXR1cm4gYSBuZXcgQmlnTnVtYmVyIHdob3NlIHZhbHVlIGlzIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciB0aW1lcyB0aGUgdmFsdWUgb2ZcbiAgICAgICAgICogQmlnTnVtYmVyKHksIGIpLlxuICAgICAgICAgKi9cbiAgICAgICAgUC50aW1lcyA9IFAubXVsID0gZnVuY3Rpb24gKCB5LCBiICkge1xuICAgICAgICAgICAgdmFyIGMsIGUsIGksIGosIGssIG0sIHhjTCwgeGxvLCB4aGksIHljTCwgeWxvLCB5aGksIHpjLFxuICAgICAgICAgICAgICAgIGJhc2UsIHNxcnRCYXNlLFxuICAgICAgICAgICAgICAgIHggPSB0aGlzLFxuICAgICAgICAgICAgICAgIHhjID0geC5jLFxuICAgICAgICAgICAgICAgIHljID0gKCBpZCA9IDE3LCB5ID0gbmV3IEJpZ051bWJlciggeSwgYiApICkuYztcblxuICAgICAgICAgICAgLy8gRWl0aGVyIE5hTiwgwrFJbmZpbml0eSBvciDCsTA/XG4gICAgICAgICAgICBpZiAoICF4YyB8fCAheWMgfHwgIXhjWzBdIHx8ICF5Y1swXSApIHtcblxuICAgICAgICAgICAgICAgIC8vIFJldHVybiBOYU4gaWYgZWl0aGVyIGlzIE5hTiwgb3Igb25lIGlzIDAgYW5kIHRoZSBvdGhlciBpcyBJbmZpbml0eS5cbiAgICAgICAgICAgICAgICBpZiAoICF4LnMgfHwgIXkucyB8fCB4YyAmJiAheGNbMF0gJiYgIXljIHx8IHljICYmICF5Y1swXSAmJiAheGMgKSB7XG4gICAgICAgICAgICAgICAgICAgIHkuYyA9IHkuZSA9IHkucyA9IG51bGw7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgeS5zICo9IHgucztcblxuICAgICAgICAgICAgICAgICAgICAvLyBSZXR1cm4gwrFJbmZpbml0eSBpZiBlaXRoZXIgaXMgwrFJbmZpbml0eS5cbiAgICAgICAgICAgICAgICAgICAgaWYgKCAheGMgfHwgIXljICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgeS5jID0geS5lID0gbnVsbDtcblxuICAgICAgICAgICAgICAgICAgICAvLyBSZXR1cm4gwrEwIGlmIGVpdGhlciBpcyDCsTAuXG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB5LmMgPSBbMF07XG4gICAgICAgICAgICAgICAgICAgICAgICB5LmUgPSAwO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgcmV0dXJuIHk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGUgPSBiaXRGbG9vciggeC5lIC8gTE9HX0JBU0UgKSArIGJpdEZsb29yKCB5LmUgLyBMT0dfQkFTRSApO1xuICAgICAgICAgICAgeS5zICo9IHgucztcbiAgICAgICAgICAgIHhjTCA9IHhjLmxlbmd0aDtcbiAgICAgICAgICAgIHljTCA9IHljLmxlbmd0aDtcblxuICAgICAgICAgICAgLy8gRW5zdXJlIHhjIHBvaW50cyB0byBsb25nZXIgYXJyYXkgYW5kIHhjTCB0byBpdHMgbGVuZ3RoLlxuICAgICAgICAgICAgaWYgKCB4Y0wgPCB5Y0wgKSB6YyA9IHhjLCB4YyA9IHljLCB5YyA9IHpjLCBpID0geGNMLCB4Y0wgPSB5Y0wsIHljTCA9IGk7XG5cbiAgICAgICAgICAgIC8vIEluaXRpYWxpc2UgdGhlIHJlc3VsdCBhcnJheSB3aXRoIHplcm9zLlxuICAgICAgICAgICAgZm9yICggaSA9IHhjTCArIHljTCwgemMgPSBbXTsgaS0tOyB6Yy5wdXNoKDApICk7XG5cbiAgICAgICAgICAgIGJhc2UgPSBCQVNFO1xuICAgICAgICAgICAgc3FydEJhc2UgPSBTUVJUX0JBU0U7XG5cbiAgICAgICAgICAgIGZvciAoIGkgPSB5Y0w7IC0taSA+PSAwOyApIHtcbiAgICAgICAgICAgICAgICBjID0gMDtcbiAgICAgICAgICAgICAgICB5bG8gPSB5Y1tpXSAlIHNxcnRCYXNlO1xuICAgICAgICAgICAgICAgIHloaSA9IHljW2ldIC8gc3FydEJhc2UgfCAwO1xuXG4gICAgICAgICAgICAgICAgZm9yICggayA9IHhjTCwgaiA9IGkgKyBrOyBqID4gaTsgKSB7XG4gICAgICAgICAgICAgICAgICAgIHhsbyA9IHhjWy0ta10gJSBzcXJ0QmFzZTtcbiAgICAgICAgICAgICAgICAgICAgeGhpID0geGNba10gLyBzcXJ0QmFzZSB8IDA7XG4gICAgICAgICAgICAgICAgICAgIG0gPSB5aGkgKiB4bG8gKyB4aGkgKiB5bG87XG4gICAgICAgICAgICAgICAgICAgIHhsbyA9IHlsbyAqIHhsbyArICggKCBtICUgc3FydEJhc2UgKSAqIHNxcnRCYXNlICkgKyB6Y1tqXSArIGM7XG4gICAgICAgICAgICAgICAgICAgIGMgPSAoIHhsbyAvIGJhc2UgfCAwICkgKyAoIG0gLyBzcXJ0QmFzZSB8IDAgKSArIHloaSAqIHhoaTtcbiAgICAgICAgICAgICAgICAgICAgemNbai0tXSA9IHhsbyAlIGJhc2U7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgemNbal0gPSBjO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoYykge1xuICAgICAgICAgICAgICAgICsrZTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgemMuc2hpZnQoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIG5vcm1hbGlzZSggeSwgemMsIGUgKTtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiBhIG5ldyBCaWdOdW1iZXIgd2hvc2UgdmFsdWUgaXMgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIHJvdW5kZWQgdG8gYSBtYXhpbXVtIG9mXG4gICAgICAgICAqIHNkIHNpZ25pZmljYW50IGRpZ2l0cyB1c2luZyByb3VuZGluZyBtb2RlIHJtLCBvciBST1VORElOR19NT0RFIGlmIHJtIGlzIG9taXR0ZWQuXG4gICAgICAgICAqXG4gICAgICAgICAqIFtzZF0ge251bWJlcn0gU2lnbmlmaWNhbnQgZGlnaXRzLiBJbnRlZ2VyLCAxIHRvIE1BWCBpbmNsdXNpdmUuXG4gICAgICAgICAqIFtybV0ge251bWJlcn0gUm91bmRpbmcgbW9kZS4gSW50ZWdlciwgMCB0byA4IGluY2x1c2l2ZS5cbiAgICAgICAgICpcbiAgICAgICAgICogJ3RvRGlnaXRzKCkgcHJlY2lzaW9uIG91dCBvZiByYW5nZToge3NkfSdcbiAgICAgICAgICogJ3RvRGlnaXRzKCkgcHJlY2lzaW9uIG5vdCBhbiBpbnRlZ2VyOiB7c2R9J1xuICAgICAgICAgKiAndG9EaWdpdHMoKSByb3VuZGluZyBtb2RlIG5vdCBhbiBpbnRlZ2VyOiB7cm19J1xuICAgICAgICAgKiAndG9EaWdpdHMoKSByb3VuZGluZyBtb2RlIG91dCBvZiByYW5nZToge3JtfSdcbiAgICAgICAgICovXG4gICAgICAgIFAudG9EaWdpdHMgPSBmdW5jdGlvbiAoIHNkLCBybSApIHtcbiAgICAgICAgICAgIHZhciBuID0gbmV3IEJpZ051bWJlcih0aGlzKTtcbiAgICAgICAgICAgIHNkID0gc2QgPT0gbnVsbCB8fCAhaXNWYWxpZEludCggc2QsIDEsIE1BWCwgMTgsICdwcmVjaXNpb24nICkgPyBudWxsIDogc2QgfCAwO1xuICAgICAgICAgICAgcm0gPSBybSA9PSBudWxsIHx8ICFpc1ZhbGlkSW50KCBybSwgMCwgOCwgMTgsIHJvdW5kaW5nTW9kZSApID8gUk9VTkRJTkdfTU9ERSA6IHJtIHwgMDtcbiAgICAgICAgICAgIHJldHVybiBzZCA/IHJvdW5kKCBuLCBzZCwgcm0gKSA6IG47XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gYSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciBpbiBleHBvbmVudGlhbCBub3RhdGlvbiBhbmRcbiAgICAgICAgICogcm91bmRlZCB1c2luZyBST1VORElOR19NT0RFIHRvIGRwIGZpeGVkIGRlY2ltYWwgcGxhY2VzLlxuICAgICAgICAgKlxuICAgICAgICAgKiBbZHBdIHtudW1iZXJ9IERlY2ltYWwgcGxhY2VzLiBJbnRlZ2VyLCAwIHRvIE1BWCBpbmNsdXNpdmUuXG4gICAgICAgICAqIFtybV0ge251bWJlcn0gUm91bmRpbmcgbW9kZS4gSW50ZWdlciwgMCB0byA4IGluY2x1c2l2ZS5cbiAgICAgICAgICpcbiAgICAgICAgICogJ3RvRXhwb25lbnRpYWwoKSBkZWNpbWFsIHBsYWNlcyBub3QgYW4gaW50ZWdlcjoge2RwfSdcbiAgICAgICAgICogJ3RvRXhwb25lbnRpYWwoKSBkZWNpbWFsIHBsYWNlcyBvdXQgb2YgcmFuZ2U6IHtkcH0nXG4gICAgICAgICAqICd0b0V4cG9uZW50aWFsKCkgcm91bmRpbmcgbW9kZSBub3QgYW4gaW50ZWdlcjoge3JtfSdcbiAgICAgICAgICogJ3RvRXhwb25lbnRpYWwoKSByb3VuZGluZyBtb2RlIG91dCBvZiByYW5nZToge3JtfSdcbiAgICAgICAgICovXG4gICAgICAgIFAudG9FeHBvbmVudGlhbCA9IGZ1bmN0aW9uICggZHAsIHJtICkge1xuICAgICAgICAgICAgcmV0dXJuIGZvcm1hdCggdGhpcyxcbiAgICAgICAgICAgICAgZHAgIT0gbnVsbCAmJiBpc1ZhbGlkSW50KCBkcCwgMCwgTUFYLCAxOSApID8gfn5kcCArIDEgOiBudWxsLCBybSwgMTkgKTtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiBhIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIGluIGZpeGVkLXBvaW50IG5vdGF0aW9uIHJvdW5kaW5nXG4gICAgICAgICAqIHRvIGRwIGZpeGVkIGRlY2ltYWwgcGxhY2VzIHVzaW5nIHJvdW5kaW5nIG1vZGUgcm0sIG9yIFJPVU5ESU5HX01PREUgaWYgcm0gaXMgb21pdHRlZC5cbiAgICAgICAgICpcbiAgICAgICAgICogTm90ZTogYXMgd2l0aCBKYXZhU2NyaXB0J3MgbnVtYmVyIHR5cGUsICgtMCkudG9GaXhlZCgwKSBpcyAnMCcsXG4gICAgICAgICAqIGJ1dCBlLmcuICgtMC4wMDAwMSkudG9GaXhlZCgwKSBpcyAnLTAnLlxuICAgICAgICAgKlxuICAgICAgICAgKiBbZHBdIHtudW1iZXJ9IERlY2ltYWwgcGxhY2VzLiBJbnRlZ2VyLCAwIHRvIE1BWCBpbmNsdXNpdmUuXG4gICAgICAgICAqIFtybV0ge251bWJlcn0gUm91bmRpbmcgbW9kZS4gSW50ZWdlciwgMCB0byA4IGluY2x1c2l2ZS5cbiAgICAgICAgICpcbiAgICAgICAgICogJ3RvRml4ZWQoKSBkZWNpbWFsIHBsYWNlcyBub3QgYW4gaW50ZWdlcjoge2RwfSdcbiAgICAgICAgICogJ3RvRml4ZWQoKSBkZWNpbWFsIHBsYWNlcyBvdXQgb2YgcmFuZ2U6IHtkcH0nXG4gICAgICAgICAqICd0b0ZpeGVkKCkgcm91bmRpbmcgbW9kZSBub3QgYW4gaW50ZWdlcjoge3JtfSdcbiAgICAgICAgICogJ3RvRml4ZWQoKSByb3VuZGluZyBtb2RlIG91dCBvZiByYW5nZToge3JtfSdcbiAgICAgICAgICovXG4gICAgICAgIFAudG9GaXhlZCA9IGZ1bmN0aW9uICggZHAsIHJtICkge1xuICAgICAgICAgICAgcmV0dXJuIGZvcm1hdCggdGhpcywgZHAgIT0gbnVsbCAmJiBpc1ZhbGlkSW50KCBkcCwgMCwgTUFYLCAyMCApXG4gICAgICAgICAgICAgID8gfn5kcCArIHRoaXMuZSArIDEgOiBudWxsLCBybSwgMjAgKTtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiBhIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIGluIGZpeGVkLXBvaW50IG5vdGF0aW9uIHJvdW5kZWRcbiAgICAgICAgICogdXNpbmcgcm0gb3IgUk9VTkRJTkdfTU9ERSB0byBkcCBkZWNpbWFsIHBsYWNlcywgYW5kIGZvcm1hdHRlZCBhY2NvcmRpbmcgdG8gdGhlIHByb3BlcnRpZXNcbiAgICAgICAgICogb2YgdGhlIEZPUk1BVCBvYmplY3QgKHNlZSBCaWdOdW1iZXIuY29uZmlnKS5cbiAgICAgICAgICpcbiAgICAgICAgICogRk9STUFUID0ge1xuICAgICAgICAgKiAgICAgIGRlY2ltYWxTZXBhcmF0b3IgOiAnLicsXG4gICAgICAgICAqICAgICAgZ3JvdXBTZXBhcmF0b3IgOiAnLCcsXG4gICAgICAgICAqICAgICAgZ3JvdXBTaXplIDogMyxcbiAgICAgICAgICogICAgICBzZWNvbmRhcnlHcm91cFNpemUgOiAwLFxuICAgICAgICAgKiAgICAgIGZyYWN0aW9uR3JvdXBTZXBhcmF0b3IgOiAnXFx4QTAnLCAgICAvLyBub24tYnJlYWtpbmcgc3BhY2VcbiAgICAgICAgICogICAgICBmcmFjdGlvbkdyb3VwU2l6ZSA6IDBcbiAgICAgICAgICogfTtcbiAgICAgICAgICpcbiAgICAgICAgICogW2RwXSB7bnVtYmVyfSBEZWNpbWFsIHBsYWNlcy4gSW50ZWdlciwgMCB0byBNQVggaW5jbHVzaXZlLlxuICAgICAgICAgKiBbcm1dIHtudW1iZXJ9IFJvdW5kaW5nIG1vZGUuIEludGVnZXIsIDAgdG8gOCBpbmNsdXNpdmUuXG4gICAgICAgICAqXG4gICAgICAgICAqICd0b0Zvcm1hdCgpIGRlY2ltYWwgcGxhY2VzIG5vdCBhbiBpbnRlZ2VyOiB7ZHB9J1xuICAgICAgICAgKiAndG9Gb3JtYXQoKSBkZWNpbWFsIHBsYWNlcyBvdXQgb2YgcmFuZ2U6IHtkcH0nXG4gICAgICAgICAqICd0b0Zvcm1hdCgpIHJvdW5kaW5nIG1vZGUgbm90IGFuIGludGVnZXI6IHtybX0nXG4gICAgICAgICAqICd0b0Zvcm1hdCgpIHJvdW5kaW5nIG1vZGUgb3V0IG9mIHJhbmdlOiB7cm19J1xuICAgICAgICAgKi9cbiAgICAgICAgUC50b0Zvcm1hdCA9IGZ1bmN0aW9uICggZHAsIHJtICkge1xuICAgICAgICAgICAgdmFyIHN0ciA9IGZvcm1hdCggdGhpcywgZHAgIT0gbnVsbCAmJiBpc1ZhbGlkSW50KCBkcCwgMCwgTUFYLCAyMSApXG4gICAgICAgICAgICAgID8gfn5kcCArIHRoaXMuZSArIDEgOiBudWxsLCBybSwgMjEgKTtcblxuICAgICAgICAgICAgaWYgKCB0aGlzLmMgKSB7XG4gICAgICAgICAgICAgICAgdmFyIGksXG4gICAgICAgICAgICAgICAgICAgIGFyciA9IHN0ci5zcGxpdCgnLicpLFxuICAgICAgICAgICAgICAgICAgICBnMSA9ICtGT1JNQVQuZ3JvdXBTaXplLFxuICAgICAgICAgICAgICAgICAgICBnMiA9ICtGT1JNQVQuc2Vjb25kYXJ5R3JvdXBTaXplLFxuICAgICAgICAgICAgICAgICAgICBncm91cFNlcGFyYXRvciA9IEZPUk1BVC5ncm91cFNlcGFyYXRvcixcbiAgICAgICAgICAgICAgICAgICAgaW50UGFydCA9IGFyclswXSxcbiAgICAgICAgICAgICAgICAgICAgZnJhY3Rpb25QYXJ0ID0gYXJyWzFdLFxuICAgICAgICAgICAgICAgICAgICBpc05lZyA9IHRoaXMucyA8IDAsXG4gICAgICAgICAgICAgICAgICAgIGludERpZ2l0cyA9IGlzTmVnID8gaW50UGFydC5zbGljZSgxKSA6IGludFBhcnQsXG4gICAgICAgICAgICAgICAgICAgIGxlbiA9IGludERpZ2l0cy5sZW5ndGg7XG5cbiAgICAgICAgICAgICAgICBpZiAoZzIpIGkgPSBnMSwgZzEgPSBnMiwgZzIgPSBpLCBsZW4gLT0gaTtcblxuICAgICAgICAgICAgICAgIGlmICggZzEgPiAwICYmIGxlbiA+IDAgKSB7XG4gICAgICAgICAgICAgICAgICAgIGkgPSBsZW4gJSBnMSB8fCBnMTtcbiAgICAgICAgICAgICAgICAgICAgaW50UGFydCA9IGludERpZ2l0cy5zdWJzdHIoIDAsIGkgKTtcblxuICAgICAgICAgICAgICAgICAgICBmb3IgKCA7IGkgPCBsZW47IGkgKz0gZzEgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpbnRQYXJ0ICs9IGdyb3VwU2VwYXJhdG9yICsgaW50RGlnaXRzLnN1YnN0ciggaSwgZzEgKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGlmICggZzIgPiAwICkgaW50UGFydCArPSBncm91cFNlcGFyYXRvciArIGludERpZ2l0cy5zbGljZShpKTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGlzTmVnKSBpbnRQYXJ0ID0gJy0nICsgaW50UGFydDtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBzdHIgPSBmcmFjdGlvblBhcnRcbiAgICAgICAgICAgICAgICAgID8gaW50UGFydCArIEZPUk1BVC5kZWNpbWFsU2VwYXJhdG9yICsgKCAoIGcyID0gK0ZPUk1BVC5mcmFjdGlvbkdyb3VwU2l6ZSApXG4gICAgICAgICAgICAgICAgICAgID8gZnJhY3Rpb25QYXJ0LnJlcGxhY2UoIG5ldyBSZWdFeHAoICdcXFxcZHsnICsgZzIgKyAnfVxcXFxCJywgJ2cnICksXG4gICAgICAgICAgICAgICAgICAgICAgJyQmJyArIEZPUk1BVC5mcmFjdGlvbkdyb3VwU2VwYXJhdG9yIClcbiAgICAgICAgICAgICAgICAgICAgOiBmcmFjdGlvblBhcnQgKVxuICAgICAgICAgICAgICAgICAgOiBpbnRQYXJ0O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gc3RyO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIGEgc3RyaW5nIGFycmF5IHJlcHJlc2VudGluZyB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgYXMgYSBzaW1wbGUgZnJhY3Rpb24gd2l0aFxuICAgICAgICAgKiBhbiBpbnRlZ2VyIG51bWVyYXRvciBhbmQgYW4gaW50ZWdlciBkZW5vbWluYXRvci4gVGhlIGRlbm9taW5hdG9yIHdpbGwgYmUgYSBwb3NpdGl2ZVxuICAgICAgICAgKiBub24temVybyB2YWx1ZSBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gdGhlIHNwZWNpZmllZCBtYXhpbXVtIGRlbm9taW5hdG9yLiBJZiBhIG1heGltdW1cbiAgICAgICAgICogZGVub21pbmF0b3IgaXMgbm90IHNwZWNpZmllZCwgdGhlIGRlbm9taW5hdG9yIHdpbGwgYmUgdGhlIGxvd2VzdCB2YWx1ZSBuZWNlc3NhcnkgdG9cbiAgICAgICAgICogcmVwcmVzZW50IHRoZSBudW1iZXIgZXhhY3RseS5cbiAgICAgICAgICpcbiAgICAgICAgICogW21kXSB7bnVtYmVyfHN0cmluZ3xCaWdOdW1iZXJ9IEludGVnZXIgPj0gMSBhbmQgPCBJbmZpbml0eS4gVGhlIG1heGltdW0gZGVub21pbmF0b3IuXG4gICAgICAgICAqXG4gICAgICAgICAqICd0b0ZyYWN0aW9uKCkgbWF4IGRlbm9taW5hdG9yIG5vdCBhbiBpbnRlZ2VyOiB7bWR9J1xuICAgICAgICAgKiAndG9GcmFjdGlvbigpIG1heCBkZW5vbWluYXRvciBvdXQgb2YgcmFuZ2U6IHttZH0nXG4gICAgICAgICAqL1xuICAgICAgICBQLnRvRnJhY3Rpb24gPSBmdW5jdGlvbiAobWQpIHtcbiAgICAgICAgICAgIHZhciBhcnIsIGQwLCBkMiwgZSwgZXhwLCBuLCBuMCwgcSwgcyxcbiAgICAgICAgICAgICAgICBrID0gRVJST1JTLFxuICAgICAgICAgICAgICAgIHggPSB0aGlzLFxuICAgICAgICAgICAgICAgIHhjID0geC5jLFxuICAgICAgICAgICAgICAgIGQgPSBuZXcgQmlnTnVtYmVyKE9ORSksXG4gICAgICAgICAgICAgICAgbjEgPSBkMCA9IG5ldyBCaWdOdW1iZXIoT05FKSxcbiAgICAgICAgICAgICAgICBkMSA9IG4wID0gbmV3IEJpZ051bWJlcihPTkUpO1xuXG4gICAgICAgICAgICBpZiAoIG1kICE9IG51bGwgKSB7XG4gICAgICAgICAgICAgICAgRVJST1JTID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgbiA9IG5ldyBCaWdOdW1iZXIobWQpO1xuICAgICAgICAgICAgICAgIEVSUk9SUyA9IGs7XG5cbiAgICAgICAgICAgICAgICBpZiAoICEoIGsgPSBuLmlzSW50KCkgKSB8fCBuLmx0KE9ORSkgKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKEVSUk9SUykge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmFpc2UoIDIyLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAnbWF4IGRlbm9taW5hdG9yICcgKyAoIGsgPyAnb3V0IG9mIHJhbmdlJyA6ICdub3QgYW4gaW50ZWdlcicgKSwgbWQgKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIEVSUk9SUyBpcyBmYWxzZTpcbiAgICAgICAgICAgICAgICAgICAgLy8gSWYgbWQgaXMgYSBmaW5pdGUgbm9uLWludGVnZXIgPj0gMSwgcm91bmQgaXQgdG8gYW4gaW50ZWdlciBhbmQgdXNlIGl0LlxuICAgICAgICAgICAgICAgICAgICBtZCA9ICFrICYmIG4uYyAmJiByb3VuZCggbiwgbi5lICsgMSwgMSApLmd0ZShPTkUpID8gbiA6IG51bGw7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoICF4YyApIHJldHVybiB4LnRvU3RyaW5nKCk7XG4gICAgICAgICAgICBzID0gY29lZmZUb1N0cmluZyh4Yyk7XG5cbiAgICAgICAgICAgIC8vIERldGVybWluZSBpbml0aWFsIGRlbm9taW5hdG9yLlxuICAgICAgICAgICAgLy8gZCBpcyBhIHBvd2VyIG9mIDEwIGFuZCB0aGUgbWluaW11bSBtYXggZGVub21pbmF0b3IgdGhhdCBzcGVjaWZpZXMgdGhlIHZhbHVlIGV4YWN0bHkuXG4gICAgICAgICAgICBlID0gZC5lID0gcy5sZW5ndGggLSB4LmUgLSAxO1xuICAgICAgICAgICAgZC5jWzBdID0gUE9XU19URU5bICggZXhwID0gZSAlIExPR19CQVNFICkgPCAwID8gTE9HX0JBU0UgKyBleHAgOiBleHAgXTtcbiAgICAgICAgICAgIG1kID0gIW1kIHx8IG4uY21wKGQpID4gMCA/ICggZSA+IDAgPyBkIDogbjEgKSA6IG47XG5cbiAgICAgICAgICAgIGV4cCA9IE1BWF9FWFA7XG4gICAgICAgICAgICBNQVhfRVhQID0gMSAvIDA7XG4gICAgICAgICAgICBuID0gbmV3IEJpZ051bWJlcihzKTtcblxuICAgICAgICAgICAgLy8gbjAgPSBkMSA9IDBcbiAgICAgICAgICAgIG4wLmNbMF0gPSAwO1xuXG4gICAgICAgICAgICBmb3IgKCA7IDsgKSAge1xuICAgICAgICAgICAgICAgIHEgPSBkaXYoIG4sIGQsIDAsIDEgKTtcbiAgICAgICAgICAgICAgICBkMiA9IGQwLnBsdXMoIHEudGltZXMoZDEpICk7XG4gICAgICAgICAgICAgICAgaWYgKCBkMi5jbXAobWQpID09IDEgKSBicmVhaztcbiAgICAgICAgICAgICAgICBkMCA9IGQxO1xuICAgICAgICAgICAgICAgIGQxID0gZDI7XG4gICAgICAgICAgICAgICAgbjEgPSBuMC5wbHVzKCBxLnRpbWVzKCBkMiA9IG4xICkgKTtcbiAgICAgICAgICAgICAgICBuMCA9IGQyO1xuICAgICAgICAgICAgICAgIGQgPSBuLm1pbnVzKCBxLnRpbWVzKCBkMiA9IGQgKSApO1xuICAgICAgICAgICAgICAgIG4gPSBkMjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgZDIgPSBkaXYoIG1kLm1pbnVzKGQwKSwgZDEsIDAsIDEgKTtcbiAgICAgICAgICAgIG4wID0gbjAucGx1cyggZDIudGltZXMobjEpICk7XG4gICAgICAgICAgICBkMCA9IGQwLnBsdXMoIGQyLnRpbWVzKGQxKSApO1xuICAgICAgICAgICAgbjAucyA9IG4xLnMgPSB4LnM7XG4gICAgICAgICAgICBlICo9IDI7XG5cbiAgICAgICAgICAgIC8vIERldGVybWluZSB3aGljaCBmcmFjdGlvbiBpcyBjbG9zZXIgdG8geCwgbjAvZDAgb3IgbjEvZDFcbiAgICAgICAgICAgIGFyciA9IGRpdiggbjEsIGQxLCBlLCBST1VORElOR19NT0RFICkubWludXMoeCkuYWJzKCkuY21wKFxuICAgICAgICAgICAgICAgICAgZGl2KCBuMCwgZDAsIGUsIFJPVU5ESU5HX01PREUgKS5taW51cyh4KS5hYnMoKSApIDwgMVxuICAgICAgICAgICAgICAgICAgICA/IFsgbjEudG9TdHJpbmcoKSwgZDEudG9TdHJpbmcoKSBdXG4gICAgICAgICAgICAgICAgICAgIDogWyBuMC50b1N0cmluZygpLCBkMC50b1N0cmluZygpIF07XG5cbiAgICAgICAgICAgIE1BWF9FWFAgPSBleHA7XG4gICAgICAgICAgICByZXR1cm4gYXJyO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciBjb252ZXJ0ZWQgdG8gYSBudW1iZXIgcHJpbWl0aXZlLlxuICAgICAgICAgKi9cbiAgICAgICAgUC50b051bWJlciA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciB4ID0gdGhpcztcblxuICAgICAgICAgICAgLy8gRW5zdXJlIHplcm8gaGFzIGNvcnJlY3Qgc2lnbi5cbiAgICAgICAgICAgIHJldHVybiAreCB8fCAoIHgucyA/IHgucyAqIDAgOiBOYU4gKTtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiBhIEJpZ051bWJlciB3aG9zZSB2YWx1ZSBpcyB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgcmFpc2VkIHRvIHRoZSBwb3dlciBuLlxuICAgICAgICAgKiBJZiBuIGlzIG5lZ2F0aXZlIHJvdW5kIGFjY29yZGluZyB0byBERUNJTUFMX1BMQUNFUyBhbmQgUk9VTkRJTkdfTU9ERS5cbiAgICAgICAgICogSWYgUE9XX1BSRUNJU0lPTiBpcyBub3QgMCwgcm91bmQgdG8gUE9XX1BSRUNJU0lPTiB1c2luZyBST1VORElOR19NT0RFLlxuICAgICAgICAgKlxuICAgICAgICAgKiBuIHtudW1iZXJ9IEludGVnZXIsIC05MDA3MTk5MjU0NzQwOTkyIHRvIDkwMDcxOTkyNTQ3NDA5OTIgaW5jbHVzaXZlLlxuICAgICAgICAgKiAoUGVyZm9ybXMgNTQgbG9vcCBpdGVyYXRpb25zIGZvciBuIG9mIDkwMDcxOTkyNTQ3NDA5OTIuKVxuICAgICAgICAgKlxuICAgICAgICAgKiAncG93KCkgZXhwb25lbnQgbm90IGFuIGludGVnZXI6IHtufSdcbiAgICAgICAgICogJ3BvdygpIGV4cG9uZW50IG91dCBvZiByYW5nZToge259J1xuICAgICAgICAgKi9cbiAgICAgICAgUC50b1Bvd2VyID0gUC5wb3cgPSBmdW5jdGlvbiAobikge1xuICAgICAgICAgICAgdmFyIGssIHksXG4gICAgICAgICAgICAgICAgaSA9IG1hdGhmbG9vciggbiA8IDAgPyAtbiA6ICtuICksXG4gICAgICAgICAgICAgICAgeCA9IHRoaXM7XG5cbiAgICAgICAgICAgIC8vIFBhc3MgwrFJbmZpbml0eSB0byBNYXRoLnBvdyBpZiBleHBvbmVudCBpcyBvdXQgb2YgcmFuZ2UuXG4gICAgICAgICAgICBpZiAoICFpc1ZhbGlkSW50KCBuLCAtTUFYX1NBRkVfSU5URUdFUiwgTUFYX1NBRkVfSU5URUdFUiwgMjMsICdleHBvbmVudCcgKSAmJlxuICAgICAgICAgICAgICAoICFpc0Zpbml0ZShuKSB8fCBpID4gTUFYX1NBRkVfSU5URUdFUiAmJiAoIG4gLz0gMCApIHx8XG4gICAgICAgICAgICAgICAgcGFyc2VGbG9hdChuKSAhPSBuICYmICEoIG4gPSBOYU4gKSApICkge1xuICAgICAgICAgICAgICAgIHJldHVybiBuZXcgQmlnTnVtYmVyKCBNYXRoLnBvdyggK3gsIG4gKSApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBUcnVuY2F0aW5nIGVhY2ggY29lZmZpY2llbnQgYXJyYXkgdG8gYSBsZW5ndGggb2YgayBhZnRlciBlYWNoIG11bHRpcGxpY2F0aW9uIGVxdWF0ZXNcbiAgICAgICAgICAgIC8vIHRvIHRydW5jYXRpbmcgc2lnbmlmaWNhbnQgZGlnaXRzIHRvIFBPV19QUkVDSVNJT04gKyBbMjgsIDQxXSwgaS5lLiB0aGVyZSB3aWxsIGJlIGFcbiAgICAgICAgICAgIC8vIG1pbmltdW0gb2YgMjggZ3VhcmQgZGlnaXRzIHJldGFpbmVkLiAoVXNpbmcgKyAxLjUgd291bGQgZ2l2ZSBbOSwgMjFdIGd1YXJkIGRpZ2l0cy4pXG4gICAgICAgICAgICBrID0gUE9XX1BSRUNJU0lPTiA/IG1hdGhjZWlsKCBQT1dfUFJFQ0lTSU9OIC8gTE9HX0JBU0UgKyAyICkgOiAwO1xuICAgICAgICAgICAgeSA9IG5ldyBCaWdOdW1iZXIoT05FKTtcblxuICAgICAgICAgICAgZm9yICggOyA7ICkge1xuXG4gICAgICAgICAgICAgICAgaWYgKCBpICUgMiApIHtcbiAgICAgICAgICAgICAgICAgICAgeSA9IHkudGltZXMoeCk7XG4gICAgICAgICAgICAgICAgICAgIGlmICggIXkuYyApIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICBpZiAoIGsgJiYgeS5jLmxlbmd0aCA+IGsgKSB5LmMubGVuZ3RoID0gaztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpID0gbWF0aGZsb29yKCBpIC8gMiApO1xuICAgICAgICAgICAgICAgIGlmICggIWkgKSBicmVhaztcblxuICAgICAgICAgICAgICAgIHggPSB4LnRpbWVzKHgpO1xuICAgICAgICAgICAgICAgIGlmICggayAmJiB4LmMgJiYgeC5jLmxlbmd0aCA+IGsgKSB4LmMubGVuZ3RoID0gaztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKCBuIDwgMCApIHkgPSBPTkUuZGl2KHkpO1xuICAgICAgICAgICAgcmV0dXJuIGsgPyByb3VuZCggeSwgUE9XX1BSRUNJU0lPTiwgUk9VTkRJTkdfTU9ERSApIDogeTtcbiAgICAgICAgfTtcblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiBhIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIHZhbHVlIG9mIHRoaXMgQmlnTnVtYmVyIHJvdW5kZWQgdG8gc2Qgc2lnbmlmaWNhbnQgZGlnaXRzXG4gICAgICAgICAqIHVzaW5nIHJvdW5kaW5nIG1vZGUgcm0gb3IgUk9VTkRJTkdfTU9ERS4gSWYgc2QgaXMgbGVzcyB0aGFuIHRoZSBudW1iZXIgb2YgZGlnaXRzXG4gICAgICAgICAqIG5lY2Vzc2FyeSB0byByZXByZXNlbnQgdGhlIGludGVnZXIgcGFydCBvZiB0aGUgdmFsdWUgaW4gZml4ZWQtcG9pbnQgbm90YXRpb24sIHRoZW4gdXNlXG4gICAgICAgICAqIGV4cG9uZW50aWFsIG5vdGF0aW9uLlxuICAgICAgICAgKlxuICAgICAgICAgKiBbc2RdIHtudW1iZXJ9IFNpZ25pZmljYW50IGRpZ2l0cy4gSW50ZWdlciwgMSB0byBNQVggaW5jbHVzaXZlLlxuICAgICAgICAgKiBbcm1dIHtudW1iZXJ9IFJvdW5kaW5nIG1vZGUuIEludGVnZXIsIDAgdG8gOCBpbmNsdXNpdmUuXG4gICAgICAgICAqXG4gICAgICAgICAqICd0b1ByZWNpc2lvbigpIHByZWNpc2lvbiBub3QgYW4gaW50ZWdlcjoge3NkfSdcbiAgICAgICAgICogJ3RvUHJlY2lzaW9uKCkgcHJlY2lzaW9uIG91dCBvZiByYW5nZToge3NkfSdcbiAgICAgICAgICogJ3RvUHJlY2lzaW9uKCkgcm91bmRpbmcgbW9kZSBub3QgYW4gaW50ZWdlcjoge3JtfSdcbiAgICAgICAgICogJ3RvUHJlY2lzaW9uKCkgcm91bmRpbmcgbW9kZSBvdXQgb2YgcmFuZ2U6IHtybX0nXG4gICAgICAgICAqL1xuICAgICAgICBQLnRvUHJlY2lzaW9uID0gZnVuY3Rpb24gKCBzZCwgcm0gKSB7XG4gICAgICAgICAgICByZXR1cm4gZm9ybWF0KCB0aGlzLCBzZCAhPSBudWxsICYmIGlzVmFsaWRJbnQoIHNkLCAxLCBNQVgsIDI0LCAncHJlY2lzaW9uJyApXG4gICAgICAgICAgICAgID8gc2QgfCAwIDogbnVsbCwgcm0sIDI0ICk7XG4gICAgICAgIH07XG5cblxuICAgICAgICAvKlxuICAgICAgICAgKiBSZXR1cm4gYSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSB2YWx1ZSBvZiB0aGlzIEJpZ051bWJlciBpbiBiYXNlIGIsIG9yIGJhc2UgMTAgaWYgYiBpc1xuICAgICAgICAgKiBvbWl0dGVkLiBJZiBhIGJhc2UgaXMgc3BlY2lmaWVkLCBpbmNsdWRpbmcgYmFzZSAxMCwgcm91bmQgYWNjb3JkaW5nIHRvIERFQ0lNQUxfUExBQ0VTIGFuZFxuICAgICAgICAgKiBST1VORElOR19NT0RFLiBJZiBhIGJhc2UgaXMgbm90IHNwZWNpZmllZCwgYW5kIHRoaXMgQmlnTnVtYmVyIGhhcyBhIHBvc2l0aXZlIGV4cG9uZW50XG4gICAgICAgICAqIHRoYXQgaXMgZXF1YWwgdG8gb3IgZ3JlYXRlciB0aGFuIFRPX0VYUF9QT1MsIG9yIGEgbmVnYXRpdmUgZXhwb25lbnQgZXF1YWwgdG8gb3IgbGVzcyB0aGFuXG4gICAgICAgICAqIFRPX0VYUF9ORUcsIHJldHVybiBleHBvbmVudGlhbCBub3RhdGlvbi5cbiAgICAgICAgICpcbiAgICAgICAgICogW2JdIHtudW1iZXJ9IEludGVnZXIsIDIgdG8gNjQgaW5jbHVzaXZlLlxuICAgICAgICAgKlxuICAgICAgICAgKiAndG9TdHJpbmcoKSBiYXNlIG5vdCBhbiBpbnRlZ2VyOiB7Yn0nXG4gICAgICAgICAqICd0b1N0cmluZygpIGJhc2Ugb3V0IG9mIHJhbmdlOiB7Yn0nXG4gICAgICAgICAqL1xuICAgICAgICBQLnRvU3RyaW5nID0gZnVuY3Rpb24gKGIpIHtcbiAgICAgICAgICAgIHZhciBzdHIsXG4gICAgICAgICAgICAgICAgbiA9IHRoaXMsXG4gICAgICAgICAgICAgICAgcyA9IG4ucyxcbiAgICAgICAgICAgICAgICBlID0gbi5lO1xuXG4gICAgICAgICAgICAvLyBJbmZpbml0eSBvciBOYU4/XG4gICAgICAgICAgICBpZiAoIGUgPT09IG51bGwgKSB7XG5cbiAgICAgICAgICAgICAgICBpZiAocykge1xuICAgICAgICAgICAgICAgICAgICBzdHIgPSAnSW5maW5pdHknO1xuICAgICAgICAgICAgICAgICAgICBpZiAoIHMgPCAwICkgc3RyID0gJy0nICsgc3RyO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHN0ciA9ICdOYU4nO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgc3RyID0gY29lZmZUb1N0cmluZyggbi5jICk7XG5cbiAgICAgICAgICAgICAgICBpZiAoIGIgPT0gbnVsbCB8fCAhaXNWYWxpZEludCggYiwgMiwgNjQsIDI1LCAnYmFzZScgKSApIHtcbiAgICAgICAgICAgICAgICAgICAgc3RyID0gZSA8PSBUT19FWFBfTkVHIHx8IGUgPj0gVE9fRVhQX1BPU1xuICAgICAgICAgICAgICAgICAgICAgID8gdG9FeHBvbmVudGlhbCggc3RyLCBlIClcbiAgICAgICAgICAgICAgICAgICAgICA6IHRvRml4ZWRQb2ludCggc3RyLCBlICk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgc3RyID0gY29udmVydEJhc2UoIHRvRml4ZWRQb2ludCggc3RyLCBlICksIGIgfCAwLCAxMCwgcyApO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmICggcyA8IDAgJiYgbi5jWzBdICkgc3RyID0gJy0nICsgc3RyO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gc3RyO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLypcbiAgICAgICAgICogUmV0dXJuIGEgbmV3IEJpZ051bWJlciB3aG9zZSB2YWx1ZSBpcyB0aGUgdmFsdWUgb2YgdGhpcyBCaWdOdW1iZXIgdHJ1bmNhdGVkIHRvIGEgd2hvbGVcbiAgICAgICAgICogbnVtYmVyLlxuICAgICAgICAgKi9cbiAgICAgICAgUC50cnVuY2F0ZWQgPSBQLnRydW5jID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgcmV0dXJuIHJvdW5kKCBuZXcgQmlnTnVtYmVyKHRoaXMpLCB0aGlzLmUgKyAxLCAxICk7XG4gICAgICAgIH07XG5cblxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFJldHVybiBhcyB0b1N0cmluZywgYnV0IGRvIG5vdCBhY2NlcHQgYSBiYXNlIGFyZ3VtZW50LlxuICAgICAgICAgKi9cbiAgICAgICAgUC52YWx1ZU9mID0gUC50b0pTT04gPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy50b1N0cmluZygpO1xuICAgICAgICB9O1xuXG5cbiAgICAgICAgLy8gQWxpYXNlcyBmb3IgQmlnRGVjaW1hbCBtZXRob2RzLlxuICAgICAgICAvL1AuYWRkID0gUC5wbHVzOyAgICAgICAgIC8vIFAuYWRkIGluY2x1ZGVkIGFib3ZlXG4gICAgICAgIC8vUC5zdWJ0cmFjdCA9IFAubWludXM7ICAgLy8gUC5zdWIgaW5jbHVkZWQgYWJvdmVcbiAgICAgICAgLy9QLm11bHRpcGx5ID0gUC50aW1lczsgICAvLyBQLm11bCBpbmNsdWRlZCBhYm92ZVxuICAgICAgICAvL1AuZGl2aWRlID0gUC5kaXY7XG4gICAgICAgIC8vUC5yZW1haW5kZXIgPSBQLm1vZDtcbiAgICAgICAgLy9QLmNvbXBhcmVUbyA9IFAuY21wO1xuICAgICAgICAvL1AubmVnYXRlID0gUC5uZWc7XG5cblxuICAgICAgICBpZiAoIGNvbmZpZ09iaiAhPSBudWxsICkgQmlnTnVtYmVyLmNvbmZpZyhjb25maWdPYmopO1xuXG4gICAgICAgIHJldHVybiBCaWdOdW1iZXI7XG4gICAgfVxuXG5cbiAgICAvLyBQUklWQVRFIEhFTFBFUiBGVU5DVElPTlNcblxuXG4gICAgZnVuY3Rpb24gYml0Rmxvb3Iobikge1xuICAgICAgICB2YXIgaSA9IG4gfCAwO1xuICAgICAgICByZXR1cm4gbiA+IDAgfHwgbiA9PT0gaSA/IGkgOiBpIC0gMTtcbiAgICB9XG5cblxuICAgIC8vIFJldHVybiBhIGNvZWZmaWNpZW50IGFycmF5IGFzIGEgc3RyaW5nIG9mIGJhc2UgMTAgZGlnaXRzLlxuICAgIGZ1bmN0aW9uIGNvZWZmVG9TdHJpbmcoYSkge1xuICAgICAgICB2YXIgcywgeixcbiAgICAgICAgICAgIGkgPSAxLFxuICAgICAgICAgICAgaiA9IGEubGVuZ3RoLFxuICAgICAgICAgICAgciA9IGFbMF0gKyAnJztcblxuICAgICAgICBmb3IgKCA7IGkgPCBqOyApIHtcbiAgICAgICAgICAgIHMgPSBhW2krK10gKyAnJztcbiAgICAgICAgICAgIHogPSBMT0dfQkFTRSAtIHMubGVuZ3RoO1xuICAgICAgICAgICAgZm9yICggOyB6LS07IHMgPSAnMCcgKyBzICk7XG4gICAgICAgICAgICByICs9IHM7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBEZXRlcm1pbmUgdHJhaWxpbmcgemVyb3MuXG4gICAgICAgIGZvciAoIGogPSByLmxlbmd0aDsgci5jaGFyQ29kZUF0KC0taikgPT09IDQ4OyApO1xuICAgICAgICByZXR1cm4gci5zbGljZSggMCwgaiArIDEgfHwgMSApO1xuICAgIH1cblxuXG4gICAgLy8gQ29tcGFyZSB0aGUgdmFsdWUgb2YgQmlnTnVtYmVycyB4IGFuZCB5LlxuICAgIGZ1bmN0aW9uIGNvbXBhcmUoIHgsIHkgKSB7XG4gICAgICAgIHZhciBhLCBiLFxuICAgICAgICAgICAgeGMgPSB4LmMsXG4gICAgICAgICAgICB5YyA9IHkuYyxcbiAgICAgICAgICAgIGkgPSB4LnMsXG4gICAgICAgICAgICBqID0geS5zLFxuICAgICAgICAgICAgayA9IHguZSxcbiAgICAgICAgICAgIGwgPSB5LmU7XG5cbiAgICAgICAgLy8gRWl0aGVyIE5hTj9cbiAgICAgICAgaWYgKCAhaSB8fCAhaiApIHJldHVybiBudWxsO1xuXG4gICAgICAgIGEgPSB4YyAmJiAheGNbMF07XG4gICAgICAgIGIgPSB5YyAmJiAheWNbMF07XG5cbiAgICAgICAgLy8gRWl0aGVyIHplcm8/XG4gICAgICAgIGlmICggYSB8fCBiICkgcmV0dXJuIGEgPyBiID8gMCA6IC1qIDogaTtcblxuICAgICAgICAvLyBTaWducyBkaWZmZXI/XG4gICAgICAgIGlmICggaSAhPSBqICkgcmV0dXJuIGk7XG5cbiAgICAgICAgYSA9IGkgPCAwO1xuICAgICAgICBiID0gayA9PSBsO1xuXG4gICAgICAgIC8vIEVpdGhlciBJbmZpbml0eT9cbiAgICAgICAgaWYgKCAheGMgfHwgIXljICkgcmV0dXJuIGIgPyAwIDogIXhjIF4gYSA/IDEgOiAtMTtcblxuICAgICAgICAvLyBDb21wYXJlIGV4cG9uZW50cy5cbiAgICAgICAgaWYgKCAhYiApIHJldHVybiBrID4gbCBeIGEgPyAxIDogLTE7XG5cbiAgICAgICAgaiA9ICggayA9IHhjLmxlbmd0aCApIDwgKCBsID0geWMubGVuZ3RoICkgPyBrIDogbDtcblxuICAgICAgICAvLyBDb21wYXJlIGRpZ2l0IGJ5IGRpZ2l0LlxuICAgICAgICBmb3IgKCBpID0gMDsgaSA8IGo7IGkrKyApIGlmICggeGNbaV0gIT0geWNbaV0gKSByZXR1cm4geGNbaV0gPiB5Y1tpXSBeIGEgPyAxIDogLTE7XG5cbiAgICAgICAgLy8gQ29tcGFyZSBsZW5ndGhzLlxuICAgICAgICByZXR1cm4gayA9PSBsID8gMCA6IGsgPiBsIF4gYSA/IDEgOiAtMTtcbiAgICB9XG5cblxuICAgIC8qXG4gICAgICogUmV0dXJuIHRydWUgaWYgbiBpcyBhIHZhbGlkIG51bWJlciBpbiByYW5nZSwgb3RoZXJ3aXNlIGZhbHNlLlxuICAgICAqIFVzZSBmb3IgYXJndW1lbnQgdmFsaWRhdGlvbiB3aGVuIEVSUk9SUyBpcyBmYWxzZS5cbiAgICAgKiBOb3RlOiBwYXJzZUludCgnMWUrMScpID09IDEgYnV0IHBhcnNlRmxvYXQoJzFlKzEnKSA9PSAxMC5cbiAgICAgKi9cbiAgICBmdW5jdGlvbiBpbnRWYWxpZGF0b3JOb0Vycm9ycyggbiwgbWluLCBtYXggKSB7XG4gICAgICAgIHJldHVybiAoIG4gPSB0cnVuY2F0ZShuKSApID49IG1pbiAmJiBuIDw9IG1heDtcbiAgICB9XG5cblxuICAgIGZ1bmN0aW9uIGlzQXJyYXkob2JqKSB7XG4gICAgICAgIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwob2JqKSA9PSAnW29iamVjdCBBcnJheV0nO1xuICAgIH1cblxuXG4gICAgLypcbiAgICAgKiBDb252ZXJ0IHN0cmluZyBvZiBiYXNlSW4gdG8gYW4gYXJyYXkgb2YgbnVtYmVycyBvZiBiYXNlT3V0LlxuICAgICAqIEVnLiBjb252ZXJ0QmFzZSgnMjU1JywgMTAsIDE2KSByZXR1cm5zIFsxNSwgMTVdLlxuICAgICAqIEVnLiBjb252ZXJ0QmFzZSgnZmYnLCAxNiwgMTApIHJldHVybnMgWzIsIDUsIDVdLlxuICAgICAqL1xuICAgIGZ1bmN0aW9uIHRvQmFzZU91dCggc3RyLCBiYXNlSW4sIGJhc2VPdXQgKSB7XG4gICAgICAgIHZhciBqLFxuICAgICAgICAgICAgYXJyID0gWzBdLFxuICAgICAgICAgICAgYXJyTCxcbiAgICAgICAgICAgIGkgPSAwLFxuICAgICAgICAgICAgbGVuID0gc3RyLmxlbmd0aDtcblxuICAgICAgICBmb3IgKCA7IGkgPCBsZW47ICkge1xuICAgICAgICAgICAgZm9yICggYXJyTCA9IGFyci5sZW5ndGg7IGFyckwtLTsgYXJyW2FyckxdICo9IGJhc2VJbiApO1xuICAgICAgICAgICAgYXJyWyBqID0gMCBdICs9IEFMUEhBQkVULmluZGV4T2YoIHN0ci5jaGFyQXQoIGkrKyApICk7XG5cbiAgICAgICAgICAgIGZvciAoIDsgaiA8IGFyci5sZW5ndGg7IGorKyApIHtcblxuICAgICAgICAgICAgICAgIGlmICggYXJyW2pdID4gYmFzZU91dCAtIDEgKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmICggYXJyW2ogKyAxXSA9PSBudWxsICkgYXJyW2ogKyAxXSA9IDA7XG4gICAgICAgICAgICAgICAgICAgIGFycltqICsgMV0gKz0gYXJyW2pdIC8gYmFzZU91dCB8IDA7XG4gICAgICAgICAgICAgICAgICAgIGFycltqXSAlPSBiYXNlT3V0O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBhcnIucmV2ZXJzZSgpO1xuICAgIH1cblxuXG4gICAgZnVuY3Rpb24gdG9FeHBvbmVudGlhbCggc3RyLCBlICkge1xuICAgICAgICByZXR1cm4gKCBzdHIubGVuZ3RoID4gMSA/IHN0ci5jaGFyQXQoMCkgKyAnLicgKyBzdHIuc2xpY2UoMSkgOiBzdHIgKSArXG4gICAgICAgICAgKCBlIDwgMCA/ICdlJyA6ICdlKycgKSArIGU7XG4gICAgfVxuXG5cbiAgICBmdW5jdGlvbiB0b0ZpeGVkUG9pbnQoIHN0ciwgZSApIHtcbiAgICAgICAgdmFyIGxlbiwgejtcblxuICAgICAgICAvLyBOZWdhdGl2ZSBleHBvbmVudD9cbiAgICAgICAgaWYgKCBlIDwgMCApIHtcblxuICAgICAgICAgICAgLy8gUHJlcGVuZCB6ZXJvcy5cbiAgICAgICAgICAgIGZvciAoIHogPSAnMC4nOyArK2U7IHogKz0gJzAnICk7XG4gICAgICAgICAgICBzdHIgPSB6ICsgc3RyO1xuXG4gICAgICAgIC8vIFBvc2l0aXZlIGV4cG9uZW50XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBsZW4gPSBzdHIubGVuZ3RoO1xuXG4gICAgICAgICAgICAvLyBBcHBlbmQgemVyb3MuXG4gICAgICAgICAgICBpZiAoICsrZSA+IGxlbiApIHtcbiAgICAgICAgICAgICAgICBmb3IgKCB6ID0gJzAnLCBlIC09IGxlbjsgLS1lOyB6ICs9ICcwJyApO1xuICAgICAgICAgICAgICAgIHN0ciArPSB6O1xuICAgICAgICAgICAgfSBlbHNlIGlmICggZSA8IGxlbiApIHtcbiAgICAgICAgICAgICAgICBzdHIgPSBzdHIuc2xpY2UoIDAsIGUgKSArICcuJyArIHN0ci5zbGljZShlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBzdHI7XG4gICAgfVxuXG5cbiAgICBmdW5jdGlvbiB0cnVuY2F0ZShuKSB7XG4gICAgICAgIG4gPSBwYXJzZUZsb2F0KG4pO1xuICAgICAgICByZXR1cm4gbiA8IDAgPyBtYXRoY2VpbChuKSA6IG1hdGhmbG9vcihuKTtcbiAgICB9XG5cblxuICAgIC8vIEVYUE9SVFxuXG5cbiAgICBCaWdOdW1iZXIgPSBhbm90aGVyKCk7XG5cbiAgICAvLyBBTUQuXG4gICAgaWYgKCB0eXBlb2YgZGVmaW5lID09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCApIHtcbiAgICAgICAgZGVmaW5lKCBmdW5jdGlvbiAoKSB7IHJldHVybiBCaWdOdW1iZXI7IH0gKTtcblxuICAgIC8vIE5vZGUgYW5kIG90aGVyIGVudmlyb25tZW50cyB0aGF0IHN1cHBvcnQgbW9kdWxlLmV4cG9ydHMuXG4gICAgfSBlbHNlIGlmICggdHlwZW9mIG1vZHVsZSAhPSAndW5kZWZpbmVkJyAmJiBtb2R1bGUuZXhwb3J0cyApIHtcbiAgICAgICAgbW9kdWxlLmV4cG9ydHMgPSBCaWdOdW1iZXI7XG4gICAgICAgIGlmICggIWNyeXB0byApIHRyeSB7IGNyeXB0byA9IHJlcXVpcmUoJ2NyeXB0bycpOyB9IGNhdGNoIChlKSB7fVxuXG4gICAgLy8gQnJvd3Nlci5cbiAgICB9IGVsc2Uge1xuICAgICAgICBnbG9iYWwuQmlnTnVtYmVyID0gQmlnTnVtYmVyO1xuICAgIH1cbn0pKHRoaXMpO1xuIiwidmFyIHdlYjMgPSByZXF1aXJlKCcuL2xpYi93ZWIzJyk7XG53ZWIzLnByb3ZpZGVycy5IdHRwUHJvdmlkZXIgPSByZXF1aXJlKCcuL2xpYi93ZWIzL2h0dHBwcm92aWRlcicpO1xud2ViMy5wcm92aWRlcnMuUXRTeW5jUHJvdmlkZXIgPSByZXF1aXJlKCcuL2xpYi93ZWIzL3F0c3luYycpO1xud2ViMy5ldGguY29udHJhY3QgPSByZXF1aXJlKCcuL2xpYi93ZWIzL2NvbnRyYWN0Jyk7XG53ZWIzLmV0aC5uYW1lcmVnID0gcmVxdWlyZSgnLi9saWIvd2ViMy9uYW1lcmVnJyk7XG53ZWIzLmV0aC5zZW5kSUJBTlRyYW5zYWN0aW9uID0gcmVxdWlyZSgnLi9saWIvd2ViMy90cmFuc2ZlcicpO1xuXG4vLyBkb250IG92ZXJyaWRlIGdsb2JhbCB2YXJpYWJsZVxuaWYgKHR5cGVvZiB3aW5kb3cgIT09ICd1bmRlZmluZWQnICYmIHR5cGVvZiB3aW5kb3cud2ViMyA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICB3aW5kb3cud2ViMyA9IHdlYjM7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gd2ViMztcblxuIl19 diff --git a/libjsqrc/ethereumjs/dist/web3.min.js b/libjsqrc/ethereumjs/dist/web3.min.js index cf92525b8..1f1fcb189 100644 --- a/libjsqrc/ethereumjs/dist/web3.min.js +++ b/libjsqrc/ethereumjs/dist/web3.min.js @@ -1,2 +1,2 @@ -require=function t(e,n,r){function i(a,s){if(!n[a]){if(!e[a]){var u="function"==typeof require&&require;if(!s&&u)return u(a,!0);if(o)return o(a,!0);var c=new Error("Cannot find module '"+a+"'");throw c.code="MODULE_NOT_FOUND",c}var f=n[a]={exports:{}};e[a][0].call(f.exports,function(t){var n=e[a][1][t];return i(n?n:t)},f,f.exports,t,e,n,r)}return n[a].exports}for(var o="function"==typeof require&&require,a=0;ao;o+=64)n.push(this._outputFormatter(new a(t.dynamicPart().substr(o+64,64))));return n}return this._outputFormatter(t)},u.prototype.sliceParam=function(t,e,n){return"bytes"===this._mode?a.decodeBytes(t,e):s(n)?a.decodeArray(t,e):a.decodeParam(t,e)};var c=function(t){this._types=t};c.prototype._requireType=function(t){var e=this._types.filter(function(e){return e.isType(t)})[0];if(!e)throw Error("invalid solidity type!: "+t);return e},c.prototype._formatInput=function(t,e){return this._requireType(t).formatInput(e,s(t))},c.prototype.encodeParam=function(t,e){return this._formatInput(t,e).encode()},c.prototype.encodeParams=function(t,e){var n=this,r=t.map(function(t,r){return n._formatInput(t,e[r])});return a.encodeList(r)},c.prototype.decodeParam=function(t,e){return this.decodeParams([t],e)[0]},c.prototype.decodeParams=function(t,e){var n=this;return t.map(function(t,r){var i=n._requireType(t),o=i.sliceParam(e,r,t);return i.formatOutput(o,s(t))})};var f=new c([new u({name:"address",match:"strict",mode:"value",inputFormatter:o.formatInputInt,outputFormatter:o.formatOutputAddress}),new u({name:"bool",match:"strict",mode:"value",inputFormatter:o.formatInputBool,outputFormatter:o.formatOutputBool}),new u({name:"int",match:"prefix",mode:"value",inputFormatter:o.formatInputInt,outputFormatter:o.formatOutputInt}),new u({name:"uint",match:"prefix",mode:"value",inputFormatter:o.formatInputInt,outputFormatter:o.formatOutputUInt}),new u({name:"bytes",match:"strict",mode:"bytes",inputFormatter:o.formatInputDynamicBytes,outputFormatter:o.formatOutputDynamicBytes}),new u({name:"bytes",match:"prefix",mode:"value",inputFormatter:o.formatInputBytes,outputFormatter:o.formatOutputBytes}),new u({name:"real",match:"prefix",mode:"value",inputFormatter:o.formatInputReal,outputFormatter:o.formatOutputReal}),new u({name:"ureal",match:"prefix",mode:"value",inputFormatter:o.formatInputReal,outputFormatter:o.formatOutputUReal})]);e.exports=f},{"../utils/utils":7,"./formatters":2,"./param":3,"bignumber.js":"bignumber.js"}],2:[function(t,e,n){var r=t("bignumber.js"),i=t("../utils/utils"),o=t("../utils/config"),a=t("./param"),s=function(t){var e=2*o.ETH_PADDING;r.config(o.ETH_BIGNUMBER_ROUNDING_MODE);var n=i.padLeft(i.toTwosComplement(t).round().toString(16),e);return new a(n)},u=function(t){var e=i.fromAscii(t,o.ETH_PADDING).substr(2);return new a(e)},c=function(t){var e=i.fromAscii(t,o.ETH_PADDING).substr(2);return new a(s(t.length).value+e,32)},f=function(t){var e="000000000000000000000000000000000000000000000000000000000000000"+(t?"1":"0");return new a(e)},l=function(t){return s(new r(t).times(new r(2).pow(128)))},p=function(t){return"1"===new r(t.substr(0,1),16).toString(2).substr(0,1)},h=function(t){var e=t.staticPart()||"0";return p(e)?new r(e,16).minus(new r("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16)).minus(1):new r(e,16)},m=function(t){var e=t.staticPart()||"0";return new r(e,16)},d=function(t){return h(t).dividedBy(new r(2).pow(128))},g=function(t){return m(t).dividedBy(new r(2).pow(128))},y=function(t){return"0000000000000000000000000000000000000000000000000000000000000001"===t.staticPart()?!0:!1},v=function(t){return i.toAscii(t.staticPart())},w=function(t){return i.toAscii(t.dynamicPart().slice(64))},b=function(t){var e=t.staticPart();return"0x"+e.slice(e.length-40,e.length)};e.exports={formatInputInt:s,formatInputBytes:u,formatInputDynamicBytes:c,formatInputBool:f,formatInputReal:l,formatOutputInt:h,formatOutputUInt:m,formatOutputReal:d,formatOutputUReal:g,formatOutputBool:y,formatOutputBytes:v,formatOutputDynamicBytes:w,formatOutputAddress:b}},{"../utils/config":5,"../utils/utils":7,"./param":3,"bignumber.js":"bignumber.js"}],3:[function(t,e,n){var r=t("../utils/utils"),i=function(t,e){this.value=t||"",this.offset=e};i.prototype.dynamicPartLength=function(){return this.dynamicPart().length/2},i.prototype.withOffset=function(t){return new i(this.value,t)},i.prototype.combine=function(t){return new i(this.value+t.value)},i.prototype.isDynamic=function(){return this.value.length>64||void 0!==this.offset},i.prototype.offsetAsBytes=function(){return this.isDynamic()?r.padLeft(r.toTwosComplement(this.offset).toString(16),64):""},i.prototype.staticPart=function(){return this.isDynamic()?this.offsetAsBytes():this.value},i.prototype.dynamicPart=function(){return this.isDynamic()?this.value:""},i.prototype.encode=function(){return this.staticPart()+this.dynamicPart()},i.encodeList=function(t){var e=32*t.length,n=t.map(function(t){if(!t.isDynamic())return t;var n=e;return e+=t.dynamicPartLength(),t.withOffset(n)});return n.reduce(function(t,e){return t+e.dynamicPart()},n.reduce(function(t,e){return t+e.staticPart()},""))},i.decodeParam=function(t,e){return e=e||0,new i(t.substr(64*e,64))};var o=function(t,e){return parseInt("0x"+t.substr(64*e,64))};i.decodeBytes=function(t,e){e=e||0;var n=o(t,e);return new i(t.substr(2*n,128),0)},i.decodeArray=function(t,e){e=e||0;var n=o(t,e),r=parseInt("0x"+t.substr(2*n,64));return new i(t.substr(2*n,64*(r+1)),0)},e.exports=i},{"../utils/utils":7}],4:[function(t,e,n){"use strict";n.XMLHttpRequest="undefined"==typeof XMLHttpRequest?{}:XMLHttpRequest},{}],5:[function(t,e,n){var r=t("bignumber.js"),i=["wei","kwei","Mwei","Gwei","szabo","finney","femtoether","picoether","nanoether","microether","milliether","nano","micro","milli","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_UNITS:i,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:r.ROUND_DOWN},ETH_POLLING_TIMEOUT:1e3,defaultBlock:"latest",defaultAccount:void 0}},{"bignumber.js":"bignumber.js"}],6:[function(t,e,n){var r=t("./utils"),i=t("crypto-js/sha3");e.exports=function(t,e){return"0x"!==t.substr(0,2)||e||(console.warn("requirement of using web3.fromAscii before sha3 is deprecated"),console.warn("new usage: 'web3.sha3(\"hello\")'"),console.warn("see https://github.com/ethereum/web3.js/pull/205"),console.warn("if you need to hash hex value, you can do 'sha3(\"0xfff\", true)'"),t=r.toAscii(t)),i(t,{outputLength:256}).toString()}},{"./utils":7,"crypto-js/sha3":33}],7:[function(t,e,n){var r=t("bignumber.js"),i={wei:"1",kwei:"1000",ada:"1000",femtoether:"1000",mwei:"1000000",babbage:"1000000",picoether:"1000000",gwei:"1000000000",shannon:"1000000000",nanoether:"1000000000",nano:"1000000000",szabo:"1000000000000",microether:"1000000000000",micro:"1000000000000",finney:"1000000000000000",milliether:"1000000000000000",milli:"1000000000000000",ether:"1000000000000000000",kether:"1000000000000000000000",grand:"1000000000000000000000",einstein:"1000000000000000000000",mether:"1000000000000000000000000",gether:"1000000000000000000000000000",tether:"1000000000000000000000000000000"},o=function(t,e,n){return new Array(e-t.length+1).join(n?n:"0")+t},a=function(t){var e="",n=0,r=t.length;for("0x"===t.substring(0,2)&&(n=2);r>n;n+=2){var i=parseInt(t.substr(n,2),16);if(0===i)break;e+=String.fromCharCode(i)}return e},s=function(t){for(var e="",n=0;nthis._inputTypes.length&&o.isObject(t[t.length-1])&&(e=t[t.length-1]),e.to=this._address,e.data="0x"+this.signature()+i.encodeParams(this._inputTypes,t),e},s.prototype.signature=function(){return a(this._name).slice(0,8)},s.prototype.unpackOutput=function(t){if(t){t=t.length>=2?t.slice(2):t;var e=i.decodeParams(this._outputTypes,t);return 1===e.length?e[0]:e}},s.prototype.call=function(){var t=Array.prototype.slice.call(arguments).filter(function(t){return void 0!==t}),e=this.extractCallback(t),n=this.toPayload(t);if(!e){var i=r.eth.call(n);return this.unpackOutput(i)}var o=this;r.eth.call(n,function(t,n){e(t,o.unpackOutput(n))})},s.prototype.sendTransaction=function(){var t=Array.prototype.slice.call(arguments).filter(function(t){return void 0!==t}),e=this.extractCallback(t),n=this.toPayload(t);return e?void r.eth.sendTransaction(n,e):r.eth.sendTransaction(n)},s.prototype.estimateGas=function(){var t=Array.prototype.slice.call(arguments),e=this.extractCallback(t),n=this.toPayload(t);return e?void r.eth.estimateGas(n,e):r.eth.estimateGas(n)},s.prototype.displayName=function(){return o.extractDisplayName(this._name)},s.prototype.typeName=function(){return o.extractTypeName(this._name)},s.prototype.request=function(){var t=Array.prototype.slice.call(arguments),e=this.extractCallback(t),n=this.toPayload(t),r=this.unpackOutput.bind(this);return{callback:e,payload:n,format:r}},s.prototype.execute=function(){var t=!this._constant;return t?this.sendTransaction.apply(this,Array.prototype.slice.call(arguments)):this.call.apply(this,Array.prototype.slice.call(arguments))},s.prototype.attachToContract=function(t){var e=this.execute.bind(this);e.request=this.request.bind(this),e.call=this.call.bind(this),e.sendTransaction=this.sendTransaction.bind(this),e.estimateGas=this.estimateGas.bind(this);var n=this.displayName();t[n]||(t[n]=e),t[n][this.typeName()]=e},e.exports=s},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"../web3":9}],19:[function(t,e,n){"use strict";var r=t("xmlhttprequest").XMLHttpRequest,i=t("./errors"),o=function(t){this.host=t||"http://localhost:8545"};o.prototype.send=function(t){var e=new r;e.open("POST",this.host,!1);try{e.send(JSON.stringify(t))}catch(n){throw i.InvalidConnection(this.host)}var o=e.responseText;try{o=JSON.parse(o)}catch(a){throw i.InvalidResponse(o)}return o},o.prototype.sendAsync=function(t,e){var n=new r;n.onreadystatechange=function(){if(4===n.readyState){var t=n.responseText,r=null;try{t=JSON.parse(t)}catch(o){r=i.InvalidResponse(t)}e(r,t)}},n.open("POST",this.host,!0);try{n.send(JSON.stringify(t))}catch(o){e(i.InvalidConnection(this.host))}},e.exports=o},{"./errors":13,xmlhttprequest:4}],20:[function(t,e,n){var r=t("../utils/utils"),i=function(t){this._iban=t};i.prototype.isValid=function(){return r.isIBAN(this._iban)},i.prototype.isDirect=function(){return 34===this._iban.length},i.prototype.isIndirect=function(){return 20===this._iban.length},i.prototype.checksum=function(){return this._iban.substr(2,2)},i.prototype.institution=function(){return this.isIndirect()?this._iban.substr(7,4):""},i.prototype.client=function(){return this.isIndirect()?this._iban.substr(11):""},i.prototype.address=function(){return this.isDirect()?this._iban.substr(4):""},e.exports=i},{"../utils/utils":7}],21:[function(t,e,n){var r=function(){return arguments.callee._singletonInstance?arguments.callee._singletonInstance:(arguments.callee._singletonInstance=this,void(this.messageId=1))};r.getInstance=function(){var t=new r;return t},r.prototype.toPayload=function(t,e){return t||console.error("jsonrpc method should be specified!"),{jsonrpc:"2.0",method:t,params:e||[],id:this.messageId++}},r.prototype.isValidResponse=function(t){return!!t&&!t.error&&"2.0"===t.jsonrpc&&"number"==typeof t.id&&void 0!==t.result},r.prototype.toBatchPayload=function(t){var e=this;return t.map(function(t){return e.toPayload(t.method,t.params)})},e.exports=r},{}],22:[function(t,e,n){var r=t("./requestmanager"),i=t("../utils/utils"),o=t("./errors"),a=function(t){this.name=t.name,this.call=t.call,this.params=t.params||0,this.inputFormatter=t.inputFormatter,this.outputFormatter=t.outputFormatter};a.prototype.getCall=function(t){return i.isFunction(this.call)?this.call(t):this.call},a.prototype.extractCallback=function(t){return i.isFunction(t[t.length-1])?t.pop():void 0},a.prototype.validateArgs=function(t){if(t.length!==this.params)throw o.InvalidNumberOfParams()},a.prototype.formatInput=function(t){return this.inputFormatter?this.inputFormatter.map(function(e,n){return e?e(t[n]):t[n]}):t},a.prototype.formatOutput=function(t){return this.outputFormatter&&null!==t?this.outputFormatter(t):t},a.prototype.attachToObject=function(t){var e=this.send.bind(this);e.request=this.request.bind(this),e.call=this.call;var n=this.name.split(".");n.length>1?(t[n[0]]=t[n[0]]||{},t[n[0]][n[1]]=e):t[n[0]]=e},a.prototype.toPayload=function(t){var e=this.getCall(t),n=this.extractCallback(t),r=this.formatInput(t);return this.validateArgs(r),{method:e,params:r,callback:n}},a.prototype.request=function(){var t=this.toPayload(Array.prototype.slice.call(arguments));return t.format=this.formatOutput.bind(this),t},a.prototype.send=function(){var t=this.toPayload(Array.prototype.slice.call(arguments));if(t.callback){var e=this;return r.getInstance().sendAsync(t,function(n,r){t.callback(n,e.formatOutput(r))})}return this.formatOutput(r.getInstance().send(t))},e.exports=a},{"../utils/utils":7,"./errors":13,"./requestmanager":27}],23:[function(t,e,n){var r=t("./contract"),i="0xc6d9d2cd449a754c494264e1809c50e34d64562b",o=[{constant:!0,inputs:[{name:"_owner",type:"address"}],name:"name",outputs:[{name:"o_name",type:"bytes32"}],type:"function"},{constant:!0,inputs:[{name:"_name",type:"bytes32"}],name:"owner",outputs:[{name:"",type:"address"}],type:"function"},{constant:!0,inputs:[{name:"_name",type:"bytes32"}],name:"content",outputs:[{name:"",type:"bytes32"}],type:"function"},{constant:!0,inputs:[{name:"_name",type:"bytes32"}],name:"addr",outputs:[{name:"",type:"address"}],type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"}],name:"reserve",outputs:[],type:"function"},{constant:!0,inputs:[{name:"_name",type:"bytes32"}],name:"subRegistrar",outputs:[{name:"o_subRegistrar",type:"address"}],type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"},{name:"_newOwner",type:"address"}],name:"transfer",outputs:[],type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"},{name:"_registrar",type:"address"}],name:"setSubRegistrar",outputs:[],type:"function"},{constant:!1,inputs:[],name:"Registrar",outputs:[],type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"},{name:"_a",type:"address"},{name:"_primary",type:"bool"}],name:"setAddress",outputs:[],type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"},{name:"_content",type:"bytes32"}],name:"setContent",outputs:[], -type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"}],name:"disown",outputs:[],type:"function"},{constant:!0,inputs:[{name:"_name",type:"bytes32"}],name:"register",outputs:[{name:"",type:"address"}],type:"function"},{anonymous:!1,inputs:[{indexed:!0,name:"name",type:"bytes32"}],name:"Changed",type:"event"},{anonymous:!1,inputs:[{indexed:!0,name:"name",type:"bytes32"},{indexed:!0,name:"addr",type:"address"}],name:"PrimaryChanged",type:"event"}];e.exports=r(o).at(i)},{"./contract":11}],24:[function(t,e,n){var r=t("../utils/utils"),i=t("./property"),o=[],a=[new i({name:"listening",getter:"net_listening"}),new i({name:"peerCount",getter:"net_peerCount",outputFormatter:r.toDecimal})];e.exports={methods:o,properties:a}},{"../utils/utils":7,"./property":25}],25:[function(t,e,n){var r=t("./requestmanager"),i=function(t){this.name=t.name,this.getter=t.getter,this.setter=t.setter,this.outputFormatter=t.outputFormatter,this.inputFormatter=t.inputFormatter};i.prototype.formatInput=function(t){return this.inputFormatter?this.inputFormatter(t):t},i.prototype.formatOutput=function(t){return this.outputFormatter&&null!==t?this.outputFormatter(t):t},i.prototype.attachToObject=function(t){var e={get:this.get.bind(this)},n=this.name.split("."),r=n[0];n.length>1&&(t[n[0]]=t[n[0]]||{},t=t[n[0]],r=n[1]),Object.defineProperty(t,r,e);var i=function(t,e){return t+e.charAt(0).toUpperCase()+e.slice(1)};t[i("get",r)]=this.getAsync.bind(this)},i.prototype.get=function(){return this.formatOutput(r.getInstance().send({method:this.getter}))},i.prototype.getAsync=function(t){var e=this;r.getInstance().sendAsync({method:this.getter},function(n,r){return n?t(n):void t(n,e.formatOutput(r))})},e.exports=i},{"./requestmanager":27}],26:[function(t,e,n){var r=function(){};r.prototype.send=function(t){var e=navigator.qt.callMethod(JSON.stringify(t));return JSON.parse(e)},e.exports=r},{}],27:[function(t,e,n){var r=t("./jsonrpc"),i=t("../utils/utils"),o=t("../utils/config"),a=t("./errors"),s=function(t){return arguments.callee._singletonInstance?arguments.callee._singletonInstance:(arguments.callee._singletonInstance=this,this.provider=t,this.polls=[],this.timeout=null,void this.poll())};s.getInstance=function(){var t=new s;return t},s.prototype.send=function(t){if(!this.provider)return console.error(a.InvalidProvider()),null;var e=r.getInstance().toPayload(t.method,t.params),n=this.provider.send(e);if(!r.getInstance().isValidResponse(n))throw a.InvalidResponse(n);return n.result},s.prototype.sendAsync=function(t,e){if(!this.provider)return e(a.InvalidProvider());var n=r.getInstance().toPayload(t.method,t.params);this.provider.sendAsync(n,function(t,n){return t?e(t):r.getInstance().isValidResponse(n)?void e(null,n.result):e(a.InvalidResponse(n))})},s.prototype.sendBatch=function(t,e){if(!this.provider)return e(a.InvalidProvider());var n=r.getInstance().toBatchPayload(t);this.provider.sendAsync(n,function(t,n){return t?e(t):i.isArray(n)?void e(t,n):e(a.InvalidResponse(n))})},s.prototype.setProvider=function(t){this.provider=t},s.prototype.startPolling=function(t,e,n,r){this.polls.push({data:t,id:e,callback:n,uninstall:r})},s.prototype.stopPolling=function(t){for(var e=this.polls.length;e--;){var n=this.polls[e];n.id===t&&this.polls.splice(e,1)}},s.prototype.reset=function(){this.polls.forEach(function(t){t.uninstall(t.id)}),this.polls=[],this.timeout&&(clearTimeout(this.timeout),this.timeout=null),this.poll()},s.prototype.poll=function(){if(this.timeout=setTimeout(this.poll.bind(this),o.ETH_POLLING_TIMEOUT),this.polls.length){if(!this.provider)return void console.error(a.InvalidProvider());var t=r.getInstance().toBatchPayload(this.polls.map(function(t){return t.data})),e=this;this.provider.sendAsync(t,function(t,n){if(!t){if(!i.isArray(n))throw a.InvalidResponse(n);n.map(function(t,n){return t.callback=e.polls[n].callback,t}).filter(function(t){var e=r.getInstance().isValidResponse(t);return e||t.callback(a.InvalidResponse(t)),e}).filter(function(t){return i.isArray(t.result)&&t.result.length>0}).forEach(function(t){t.callback(null,t.result)})}})}},e.exports=s},{"../utils/config":5,"../utils/utils":7,"./errors":13,"./jsonrpc":21}],28:[function(t,e,n){var r=t("./method"),i=t("./formatters"),o=new r({name:"post",call:"shh_post",params:1,inputFormatter:[i.inputPostFormatter]}),a=new r({name:"newIdentity",call:"shh_newIdentity",params:0}),s=new r({name:"hasIdentity",call:"shh_hasIdentity",params:1}),u=new r({name:"newGroup",call:"shh_newGroup",params:0}),c=new r({name:"addToGroup",call:"shh_addToGroup",params:0}),f=[o,a,s,u,c];e.exports={methods:f}},{"./formatters":17,"./method":22}],29:[function(t,e,n){var r=t("../web3"),i=t("./icap"),o=t("./namereg"),a=t("./contract"),s=function(t,e,n,r){var a=new i(e);if(!a.isValid())throw new Error("invalid iban address");if(a.isDirect())return u(t,a.address(),n,r);if(!r){var s=o.addr(a.institution());return c(t,s,n,a.client())}o.addr(a.insitution(),function(e,i){return c(t,i,n,a.client(),r)})},u=function(t,e,n,i){return r.eth.sendTransaction({address:e,from:t,value:n},i)},c=function(t,e,n,r,i){var o=[{constant:!1,inputs:[{name:"name",type:"bytes32"}],name:"deposit",outputs:[],type:"function"}];return a(o).at(e).deposit(r,{from:t,value:n},i)};e.exports=s},{"../web3":9,"./contract":11,"./icap":20,"./namereg":23}],30:[function(t,e,n){var r=t("./method"),i=function(){var t=function(t){var e=t[0];switch(e){case"latest":return t.pop(),this.params=0,"eth_newBlockFilter";case"pending":return t.pop(),this.params=0,"eth_newPendingTransactionFilter";default:return"eth_newFilter"}},e=new r({name:"newFilter",call:t,params:1}),n=new r({name:"uninstallFilter",call:"eth_uninstallFilter",params:1}),i=new r({name:"getLogs",call:"eth_getFilterLogs",params:1}),o=new r({name:"poll",call:"eth_getFilterChanges",params:1});return[e,n,i,o]},o=function(){var t=new r({name:"newFilter",call:"shh_newFilter",params:1}),e=new r({name:"uninstallFilter",call:"shh_uninstallFilter",params:1}),n=new r({name:"getLogs",call:"shh_getMessages",params:1}),i=new r({name:"poll",call:"shh_getFilterChanges",params:1});return[t,e,n,i]};e.exports={eth:i,shh:o}},{"./method":22}],31:[function(t,e,n){},{}],32:[function(t,e,n){!function(t,r){"object"==typeof n?e.exports=n=r():"function"==typeof define&&define.amd?define([],r):t.CryptoJS=r()}(this,function(){var t=t||function(t,e){var n={},r=n.lib={},i=r.Base=function(){function t(){}return{extend:function(e){t.prototype=this;var n=new t;return e&&n.mixIn(e),n.hasOwnProperty("init")||(n.init=function(){n.$super.init.apply(this,arguments)}),n.init.prototype=n,n.$super=this,n},create:function(){var t=this.extend();return t.init.apply(t,arguments),t},init:function(){},mixIn:function(t){for(var e in t)t.hasOwnProperty(e)&&(this[e]=t[e]);t.hasOwnProperty("toString")&&(this.toString=t.toString)},clone:function(){return this.init.prototype.extend(this)}}}(),o=r.WordArray=i.extend({init:function(t,n){t=this.words=t||[],this.sigBytes=n!=e?n:4*t.length},toString:function(t){return(t||s).stringify(this)},concat:function(t){var e=this.words,n=t.words,r=this.sigBytes,i=t.sigBytes;if(this.clamp(),r%4)for(var o=0;i>o;o++){var a=n[o>>>2]>>>24-o%4*8&255;e[r+o>>>2]|=a<<24-(r+o)%4*8}else for(var o=0;i>o;o+=4)e[r+o>>>2]=n[o>>>2];return this.sigBytes+=i,this},clamp:function(){var e=this.words,n=this.sigBytes;e[n>>>2]&=4294967295<<32-n%4*8,e.length=t.ceil(n/4)},clone:function(){var t=i.clone.call(this);return t.words=this.words.slice(0),t},random:function(e){for(var n,r=[],i=function(e){var e=e,n=987654321,r=4294967295;return function(){n=36969*(65535&n)+(n>>16)&r,e=18e3*(65535&e)+(e>>16)&r;var i=(n<<16)+e&r;return i/=4294967296,i+=.5,i*(t.random()>.5?1:-1)}},a=0;e>a;a+=4){var s=i(4294967296*(n||t.random()));n=987654071*s(),r.push(4294967296*s()|0)}return new o.init(r,e)}}),a=n.enc={},s=a.Hex={stringify:function(t){for(var e=t.words,n=t.sigBytes,r=[],i=0;n>i;i++){var o=e[i>>>2]>>>24-i%4*8&255;r.push((o>>>4).toString(16)),r.push((15&o).toString(16))}return r.join("")},parse:function(t){for(var e=t.length,n=[],r=0;e>r;r+=2)n[r>>>3]|=parseInt(t.substr(r,2),16)<<24-r%8*4;return new o.init(n,e/2)}},u=a.Latin1={stringify:function(t){for(var e=t.words,n=t.sigBytes,r=[],i=0;n>i;i++){var o=e[i>>>2]>>>24-i%4*8&255;r.push(String.fromCharCode(o))}return r.join("")},parse:function(t){for(var e=t.length,n=[],r=0;e>r;r++)n[r>>>2]|=(255&t.charCodeAt(r))<<24-r%4*8;return new o.init(n,e)}},c=a.Utf8={stringify:function(t){try{return decodeURIComponent(escape(u.stringify(t)))}catch(e){throw new Error("Malformed UTF-8 data")}},parse:function(t){return u.parse(unescape(encodeURIComponent(t)))}},f=r.BufferedBlockAlgorithm=i.extend({reset:function(){this._data=new o.init,this._nDataBytes=0},_append:function(t){"string"==typeof t&&(t=c.parse(t)),this._data.concat(t),this._nDataBytes+=t.sigBytes},_process:function(e){var n=this._data,r=n.words,i=n.sigBytes,a=this.blockSize,s=4*a,u=i/s;u=e?t.ceil(u):t.max((0|u)-this._minBufferSize,0);var c=u*a,f=t.min(4*c,i);if(c){for(var l=0;c>l;l+=a)this._doProcessBlock(r,l);var p=r.splice(0,c);n.sigBytes-=f}return new o.init(p,f)},clone:function(){var t=i.clone.call(this);return t._data=this._data.clone(),t},_minBufferSize:0}),l=(r.Hasher=f.extend({cfg:i.extend(),init:function(t){this.cfg=this.cfg.extend(t),this.reset()},reset:function(){f.reset.call(this),this._doReset()},update:function(t){return this._append(t),this._process(),this},finalize:function(t){t&&this._append(t);var e=this._doFinalize();return e},blockSize:16,_createHelper:function(t){return function(e,n){return new t.init(n).finalize(e)}},_createHmacHelper:function(t){return function(e,n){return new l.HMAC.init(t,n).finalize(e)}}}),n.algo={});return n}(Math);return t})},{}],33:[function(t,e,n){!function(r,i,o){"object"==typeof n?e.exports=n=i(t("./core"),t("./x64-core")):"function"==typeof define&&define.amd?define(["./core","./x64-core"],i):i(r.CryptoJS)}(this,function(t){return function(e){var n=t,r=n.lib,i=r.WordArray,o=r.Hasher,a=n.x64,s=a.Word,u=n.algo,c=[],f=[],l=[];!function(){for(var t=1,e=0,n=0;24>n;n++){c[t+5*e]=(n+1)*(n+2)/2%64;var r=e%5,i=(2*t+3*e)%5;t=r,e=i}for(var t=0;5>t;t++)for(var e=0;5>e;e++)f[t+5*e]=e+(2*t+3*e)%5*5;for(var o=1,a=0;24>a;a++){for(var u=0,p=0,h=0;7>h;h++){if(1&o){var m=(1<m?p^=1<t;t++)p[t]=s.create()}();var h=u.SHA3=o.extend({cfg:o.cfg.extend({outputLength:512}),_doReset:function(){for(var t=this._state=[],e=0;25>e;e++)t[e]=new s.init;this.blockSize=(1600-2*this.cfg.outputLength)/32},_doProcessBlock:function(t,e){for(var n=this._state,r=this.blockSize/2,i=0;r>i;i++){var o=t[e+2*i],a=t[e+2*i+1];o=16711935&(o<<8|o>>>24)|4278255360&(o<<24|o>>>8),a=16711935&(a<<8|a>>>24)|4278255360&(a<<24|a>>>8);var s=n[i];s.high^=a,s.low^=o}for(var u=0;24>u;u++){for(var h=0;5>h;h++){for(var m=0,d=0,g=0;5>g;g++){var s=n[h+5*g];m^=s.high,d^=s.low}var y=p[h];y.high=m,y.low=d}for(var h=0;5>h;h++)for(var v=p[(h+4)%5],w=p[(h+1)%5],b=w.high,_=w.low,m=v.high^(b<<1|_>>>31),d=v.low^(_<<1|b>>>31),g=0;5>g;g++){var s=n[h+5*g];s.high^=m,s.low^=d}for(var x=1;25>x;x++){var s=n[x],F=s.high,I=s.low,B=c[x];if(32>B)var m=F<>>32-B,d=I<>>32-B;else var m=I<>>64-B,d=F<>>64-B;var N=p[f[x]];N.high=m,N.low=d}var O=p[0],A=n[0];O.high=A.high,O.low=A.low;for(var h=0;5>h;h++)for(var g=0;5>g;g++){var x=h+5*g,s=n[x],k=p[x],P=p[(h+1)%5+5*g],T=p[(h+2)%5+5*g];s.high=k.high^~P.high&T.high,s.low=k.low^~P.low&T.low}var s=n[0],D=l[u];s.high^=D.high,s.low^=D.low}},_doFinalize:function(){var t=this._data,n=t.words,r=(8*this._nDataBytes,8*t.sigBytes),o=32*this.blockSize;n[r>>>5]|=1<<24-r%32,n[(e.ceil((r+1)/o)*o>>>5)-1]|=128,t.sigBytes=4*n.length,this._process();for(var a=this._state,s=this.cfg.outputLength/8,u=s/8,c=[],f=0;u>f;f++){var l=a[f],p=l.high,h=l.low;p=16711935&(p<<8|p>>>24)|4278255360&(p<<24|p>>>8),h=16711935&(h<<8|h>>>24)|4278255360&(h<<24|h>>>8),c.push(h),c.push(p)}return new i.init(c,s)},clone:function(){for(var t=o.clone.call(this),e=t._state=this._state.slice(0),n=0;25>n;n++)e[n]=e[n].clone();return t}});n.SHA3=o._createHelper(h),n.HmacSHA3=o._createHmacHelper(h)}(Math),t.SHA3})},{"./core":32,"./x64-core":34}],34:[function(t,e,n){!function(r,i){"object"==typeof n?e.exports=n=i(t("./core")):"function"==typeof define&&define.amd?define(["./core"],i):i(r.CryptoJS)}(this,function(t){return function(e){{var n=t,r=n.lib,i=r.Base,o=r.WordArray,a=n.x64={};a.Word=i.extend({init:function(t,e){this.high=t,this.low=e}}),a.WordArray=i.extend({init:function(t,n){t=this.words=t||[],this.sigBytes=n!=e?n:8*t.length},toX32:function(){for(var t=this.words,e=t.length,n=[],r=0;e>r;r++){var i=t[r];n.push(i.high),n.push(i.low)}return o.create(n,this.sigBytes)},clone:function(){for(var t=i.clone.call(this),e=t.words=this.words.slice(0),n=e.length,r=0;n>r;r++)e[r]=e[r].clone();return t}})}}(),t})},{"./core":32}],"bignumber.js":[function(t,e,n){!function(n){"use strict";function r(t){function e(t,r){var i,o,a,s,u,c,f=this;if(!(f instanceof e))return z&&D(26,"constructor call without new",t),new e(t,r);if(null!=r&&W(r,2,64,C,"base")){if(r=0|r,c=t+"",10==r)return f=new e(t instanceof e?t:c),S(f,L+f.e+1,j);if((s="number"==typeof t)&&0*t!=0||!new RegExp("^-?"+(i="["+x.slice(0,r)+"]+")+"(?:\\."+i+")?$",37>r?"i":"").test(c))return d(f,c,s,r);s?(f.s=0>1/t?(c=c.slice(1),-1):1,z&&c.replace(/^0\.0*|\./,"").length>15&&D(C,_,t),s=!1):f.s=45===c.charCodeAt(0)?(c=c.slice(1),-1):1,c=n(c,10,r,f.s)}else{if(t instanceof e)return f.s=t.s,f.e=t.e,f.c=(t=t.c)?t.slice():t,void(C=0);if((s="number"==typeof t)&&0*t==0){if(f.s=0>1/t?(t=-t,-1):1,t===~~t){for(o=0,a=t;a>=10;a/=10,o++);return f.e=o,f.c=[t],void(C=0)}c=t+""}else{if(!g.test(c=t+""))return d(f,c,s);f.s=45===c.charCodeAt(0)?(c=c.slice(1),-1):1}}for((o=c.indexOf("."))>-1&&(c=c.replace(".","")),(a=c.search(/e/i))>0?(0>o&&(o=a),o+=+c.slice(a+1),c=c.substring(0,a)):0>o&&(o=c.length),a=0;48===c.charCodeAt(a);a++);for(u=c.length;48===c.charCodeAt(--u););if(c=c.slice(a,u+1))if(u=c.length,s&&z&&u>15&&D(C,_,f.s*t),o=o-a-1,o>M)f.c=f.e=null;else if(G>o)f.c=[f.e=0];else{if(f.e=o,f.c=[],a=(o+1)%I,0>o&&(a+=I),u>a){for(a&&f.c.push(+c.slice(0,a)),u-=I;u>a;)f.c.push(+c.slice(a,a+=I));c=c.slice(a),a=I-c.length}else a-=u;for(;a--;c+="0");f.c.push(+c)}else f.c=[f.e=0];C=0}function n(t,n,r,i){var a,s,u,f,p,h,m,d=t.indexOf("."),g=L,y=j;for(37>r&&(t=t.toLowerCase()),d>=0&&(u=V,V=0,t=t.replace(".",""),m=new e(r),p=m.pow(t.length-d),V=u,m.c=c(l(o(p.c),p.e),10,n),m.e=m.c.length),h=c(t,r,n),s=u=h.length;0==h[--u];h.pop());if(!h[0])return"0";if(0>d?--s:(p.c=h,p.e=s,p.s=i,p=E(p,m,g,y,n),h=p.c,f=p.r,s=p.e),a=s+g+1,d=h[a],u=n/2,f=f||0>a||null!=h[a+1],f=4>y?(null!=d||f)&&(0==y||y==(p.s<0?3:2)):d>u||d==u&&(4==y||f||6==y&&1&h[a-1]||y==(p.s<0?8:7)),1>a||!h[0])t=f?l("1",-g):"0";else{if(h.length=a,f)for(--n;++h[--a]>n;)h[a]=0,a||(++s,h.unshift(1));for(u=h.length;!h[--u];);for(d=0,t="";u>=d;t+=x.charAt(h[d++]));t=l(t,s)}return t}function h(t,n,r,i){var a,s,u,c,p;if(r=null!=r&&W(r,0,8,i,b)?0|r:j,!t.c)return t.toString();if(a=t.c[0],u=t.e,null==n)p=o(t.c),p=19==i||24==i&&q>=u?f(p,u):l(p,u);else if(t=S(new e(t),n,r),s=t.e,p=o(t.c),c=p.length,19==i||24==i&&(s>=n||q>=s)){for(;n>c;p+="0",c++);p=f(p,s)}else if(n-=u,p=l(p,s),s+1>c){if(--n>0)for(p+=".";n--;p+="0");}else if(n+=s-c,n>0)for(s+1==c&&(p+=".");n--;p+="0");return t.s<0&&a?"-"+p:p}function k(t,n){var r,i,o=0;for(u(t[0])&&(t=t[0]),r=new e(t[0]);++ot||t>n||t!=p(t))&&D(r,(i||"decimal places")+(e>t||t>n?" out of range":" not an integer"),t),!0}function T(t,e,n){for(var r=1,i=e.length;!e[--i];e.pop());for(i=e[0];i>=10;i/=10,r++);return(n=r+n*I-1)>M?t.c=t.e=null:G>n?t.c=[t.e=0]:(t.e=n,t.c=e),t}function D(t,e,n){var r=new Error(["new BigNumber","cmp","config","div","divToInt","eq","gt","gte","lt","lte","minus","mod","plus","precision","random","round","shift","times","toDigits","toExponential","toFixed","toFormat","toFraction","pow","toPrecision","toString","BigNumber"][t]+"() "+e+": "+n);throw r.name="BigNumber Error",C=0,r}function S(t,e,n,r){var i,o,a,s,u,c,f,l=t.c,p=N;if(l){t:{for(i=1,s=l[0];s>=10;s/=10,i++);if(o=e-i,0>o)o+=I,a=e,u=l[c=0],f=u/p[i-a-1]%10|0;else if(c=y((o+1)/I),c>=l.length){if(!r)break t;for(;l.length<=c;l.push(0));u=f=0,i=1,o%=I,a=o-I+1}else{for(u=s=l[c],i=1;s>=10;s/=10,i++);o%=I,a=o-I+i,f=0>a?0:u/p[i-a-1]%10|0}if(r=r||0>e||null!=l[c+1]||(0>a?u:u%p[i-a-1]),r=4>n?(f||r)&&(0==n||n==(t.s<0?3:2)):f>5||5==f&&(4==n||r||6==n&&(o>0?a>0?u/p[i-a]:0:l[c-1])%10&1||n==(t.s<0?8:7)),1>e||!l[0])return l.length=0,r?(e-=t.e+1,l[0]=p[e%I],t.e=-e||0):l[0]=t.e=0,t;if(0==o?(l.length=c,s=1,c--):(l.length=c+1,s=p[I-o],l[c]=a>0?v(u/p[i-a]%p[a])*s:0),r)for(;;){if(0==c){for(o=1,a=l[0];a>=10;a/=10,o++);for(a=l[0]+=s,s=1;a>=10;a/=10,s++);o!=s&&(t.e++,l[0]==F&&(l[0]=1));break}if(l[c]+=s,l[c]!=F)break;l[c--]=0,s=1}for(o=l.length;0===l[--o];l.pop());}t.e>M?t.c=t.e=null:t.en?null!=(t=i[n++]):void 0};return a(e="DECIMAL_PLACES")&&W(t,0,A,2,e)&&(L=0|t),r[e]=L,a(e="ROUNDING_MODE")&&W(t,0,8,2,e)&&(j=0|t),r[e]=j,a(e="EXPONENTIAL_AT")&&(u(t)?W(t[0],-A,0,2,e)&&W(t[1],0,A,2,e)&&(q=0|t[0],U=0|t[1]):W(t,-A,A,2,e)&&(q=-(U=0|(0>t?-t:t)))),r[e]=[q,U],a(e="RANGE")&&(u(t)?W(t[0],-A,-1,2,e)&&W(t[1],1,A,2,e)&&(G=0|t[0],M=0|t[1]):W(t,-A,A,2,e)&&(0|t?G=-(M=0|(0>t?-t:t)):z&&D(2,e+" cannot be zero",t))),r[e]=[G,M],a(e="ERRORS")&&(t===!!t||1===t||0===t?(C=0,W=(z=!!t)?P:s):z&&D(2,e+w,t)),r[e]=z,a(e="CRYPTO")&&(t===!!t||1===t||0===t?(J=!(!t||!m||"object"!=typeof m),t&&!J&&z&&D(2,"crypto unavailable",m)):z&&D(2,e+w,t)),r[e]=J,a(e="MODULO_MODE")&&W(t,0,9,2,e)&&($=0|t),r[e]=$,a(e="POW_PRECISION")&&W(t,0,A,2,e)&&(V=0|t),r[e]=V,a(e="FORMAT")&&("object"==typeof t?X=t:z&&D(2,e+" not an object",t)),r[e]=X,r},e.max=function(){return k(arguments,R.lt)},e.min=function(){return k(arguments,R.gt)},e.random=function(){var t=9007199254740992,n=Math.random()*t&2097151?function(){return v(Math.random()*t)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)};return function(t){var r,i,o,a,s,u=0,c=[],f=new e(H);if(t=null!=t&&W(t,0,A,14)?0|t:L,a=y(t/I),J)if(m&&m.getRandomValues){for(r=m.getRandomValues(new Uint32Array(a*=2));a>u;)s=131072*r[u]+(r[u+1]>>>11),s>=9e15?(i=m.getRandomValues(new Uint32Array(2)),r[u]=i[0],r[u+1]=i[1]):(c.push(s%1e14),u+=2);u=a/2}else if(m&&m.randomBytes){for(r=m.randomBytes(a*=7);a>u;)s=281474976710656*(31&r[u])+1099511627776*r[u+1]+4294967296*r[u+2]+16777216*r[u+3]+(r[u+4]<<16)+(r[u+5]<<8)+r[u+6],s>=9e15?m.randomBytes(7).copy(r,u):(c.push(s%1e14),u+=7);u=a/7}else z&&D(14,"crypto unavailable",m);if(!u)for(;a>u;)s=n(),9e15>s&&(c[u++]=s%1e14);for(a=c[--u],t%=I,a&&t&&(s=N[I-t],c[u]=v(a/s)*s);0===c[u];c.pop(),u--);if(0>u)c=[o=0];else{for(o=-1;0===c[0];c.shift(),o-=I);for(u=1,s=c[0];s>=10;s/=10,u++);I>u&&(o-=I-u)}return f.e=o,f.c=c,f}}(),E=function(){function t(t,e,n){var r,i,o,a,s=0,u=t.length,c=e%O,f=e/O|0;for(t=t.slice();u--;)o=t[u]%O,a=t[u]/O|0,r=f*o+a*c,i=c*o+r%O*O+s,s=(i/n|0)+(r/O|0)+f*a,t[u]=i%n;return s&&t.unshift(s),t}function n(t,e,n,r){var i,o;if(n!=r)o=n>r?1:-1;else for(i=o=0;n>i;i++)if(t[i]!=e[i]){o=t[i]>e[i]?1:-1;break}return o}function r(t,e,n,r){for(var i=0;n--;)t[n]-=i,i=t[n]1;t.shift());}return function(o,a,s,u,c){var f,l,p,h,m,d,g,y,w,b,_,x,B,N,O,A,k,P=o.s==a.s?1:-1,T=o.c,D=a.c;if(!(T&&T[0]&&D&&D[0]))return new e(o.s&&a.s&&(T?!D||T[0]!=D[0]:D)?T&&0==T[0]||!D?0*P:P/0:0/0);for(y=new e(P),w=y.c=[],l=o.e-a.e,P=s+l+1,c||(c=F,l=i(o.e/I)-i(a.e/I),P=P/I|0),p=0;D[p]==(T[p]||0);p++);if(D[p]>(T[p]||0)&&l--,0>P)w.push(1),h=!0;else{for(N=T.length,A=D.length,p=0,P+=2,m=v(c/(D[0]+1)),m>1&&(D=t(D,m,c),T=t(T,m,c),A=D.length,N=T.length),B=A,b=T.slice(0,A),_=b.length;A>_;b[_++]=0);k=D.slice(),k.unshift(0),O=D[0],D[1]>=c/2&&O++;do{if(m=0,f=n(D,b,A,_),0>f){if(x=b[0],A!=_&&(x=x*c+(b[1]||0)),m=v(x/O),m>1)for(m>=c&&(m=c-1),d=t(D,m,c),g=d.length,_=b.length;1==n(d,b,g,_);)m--,r(d,g>A?k:D,g,c),g=d.length,f=1;else 0==m&&(f=m=1),d=D.slice(),g=d.length;if(_>g&&d.unshift(0),r(b,d,_,c),_=b.length,-1==f)for(;n(D,b,A,_)<1;)m++,r(b,_>A?k:D,_,c),_=b.length}else 0===f&&(m++,b=[0]);w[p++]=m,b[0]?b[_++]=T[B]||0:(b=[T[B]],_=1)}while((B++=10;P/=10,p++);S(y,s+(y.e=p+l*I-1)+1,u,h)}else y.e=l,y.r=+h;return y}}(),d=function(){var t=/^(-?)0([xbo])/i,n=/^([^.]+)\.$/,r=/^\.([^.]+)$/,i=/^-?(Infinity|NaN)$/,o=/^\s*\+|^\s+|\s+$/g;return function(a,s,u,c){var f,l=u?s:s.replace(o,"");if(i.test(l))a.s=isNaN(l)?null:0>l?-1:1;else{if(!u&&(l=l.replace(t,function(t,e,n){return f="x"==(n=n.toLowerCase())?16:"b"==n?2:8,c&&c!=f?t:e}),c&&(f=c,l=l.replace(n,"$1").replace(r,"0.$1")),s!=l))return new e(l,f);z&&D(C,"not a"+(c?" base "+c:"")+" number",s),a.s=null}a.c=a.e=null,C=0}}(),R.absoluteValue=R.abs=function(){var t=new e(this);return t.s<0&&(t.s=1),t},R.ceil=function(){return S(new e(this),this.e+1,2)},R.comparedTo=R.cmp=function(t,n){return C=1,a(this,new e(t,n))},R.decimalPlaces=R.dp=function(){var t,e,n=this.c;if(!n)return null;if(t=((e=n.length-1)-i(this.e/I))*I,e=n[e])for(;e%10==0;e/=10,t--);return 0>t&&(t=0),t},R.dividedBy=R.div=function(t,n){return C=3,E(this,new e(t,n),L,j)},R.dividedToIntegerBy=R.divToInt=function(t,n){return C=4,E(this,new e(t,n),0,1)},R.equals=R.eq=function(t,n){return C=5,0===a(this,new e(t,n))},R.floor=function(){return S(new e(this),this.e+1,3)},R.greaterThan=R.gt=function(t,n){return C=6,a(this,new e(t,n))>0},R.greaterThanOrEqualTo=R.gte=function(t,n){return C=7,1===(n=a(this,new e(t,n)))||0===n},R.isFinite=function(){return!!this.c},R.isInteger=R.isInt=function(){return!!this.c&&i(this.e/I)>this.c.length-2},R.isNaN=function(){return!this.s},R.isNegative=R.isNeg=function(){return this.s<0},R.isZero=function(){return!!this.c&&0==this.c[0]},R.lessThan=R.lt=function(t,n){return C=8,a(this,new e(t,n))<0},R.lessThanOrEqualTo=R.lte=function(t,n){return C=9,-1===(n=a(this,new e(t,n)))||0===n},R.minus=R.sub=function(t,n){var r,o,a,s,u=this,c=u.s;if(C=10,t=new e(t,n),n=t.s,!c||!n)return new e(0/0);if(c!=n)return t.s=-n,u.plus(t);var f=u.e/I,l=t.e/I,p=u.c,h=t.c;if(!f||!l){if(!p||!h)return p?(t.s=-n,t):new e(h?u:0/0);if(!p[0]||!h[0])return h[0]?(t.s=-n,t):new e(p[0]?u:3==j?-0:0)}if(f=i(f),l=i(l),p=p.slice(),c=f-l){for((s=0>c)?(c=-c,a=p):(l=f,a=h),a.reverse(),n=c;n--;a.push(0));a.reverse()}else for(o=(s=(c=p.length)<(n=h.length))?c:n,c=n=0;o>n;n++)if(p[n]!=h[n]){s=p[n]0)for(;n--;p[r++]=0);for(n=F-1;o>c;){if(p[--o]0?(u=s,r=f):(a=-a,r=c),r.reverse();a--;r.push(0));r.reverse()}for(a=c.length,n=f.length,0>a-n&&(r=f,f=c,c=r,n=a),a=0;n;)a=(c[--n]=c[n]+f[n]+a)/F|0,c[n]%=F;return a&&(c.unshift(a),++u),T(t,c,u)},R.precision=R.sd=function(t){var e,n,r=this,i=r.c;if(null!=t&&t!==!!t&&1!==t&&0!==t&&(z&&D(13,"argument"+w,t),t!=!!t&&(t=null)),!i)return null;if(n=i.length-1,e=n*I+1,n=i[n]){for(;n%10==0;n/=10,e--);for(n=i[0];n>=10;n/=10,e++);}return t&&r.e+1>e&&(e=r.e+1),e},R.round=function(t,n){var r=new e(this);return(null==t||W(t,0,A,15))&&S(r,~~t+this.e+1,null!=n&&W(n,0,8,15,b)?0|n:j),r},R.shift=function(t){var n=this;return W(t,-B,B,16,"argument")?n.times("1e"+p(t)):new e(n.c&&n.c[0]&&(-B>t||t>B)?n.s*(0>t?0:1/0):n)},R.squareRoot=R.sqrt=function(){var t,n,r,a,s,u=this,c=u.c,f=u.s,l=u.e,p=L+4,h=new e("0.5");if(1!==f||!c||!c[0])return new e(!f||0>f&&(!c||c[0])?0/0:c?u:1/0);if(f=Math.sqrt(+u),0==f||f==1/0?(n=o(c),(n.length+l)%2==0&&(n+="0"),f=Math.sqrt(n),l=i((l+1)/2)-(0>l||l%2),f==1/0?n="1e"+l:(n=f.toExponential(),n=n.slice(0,n.indexOf("e")+1)+l),r=new e(n)):r=new e(f+""),r.c[0])for(l=r.e,f=l+p,3>f&&(f=0);;)if(s=r,r=h.times(s.plus(E(u,s,p,1))),o(s.c).slice(0,f)===(n=o(r.c)).slice(0,f)){if(r.ef&&(g=b,b=_,_=g,a=f,f=h,h=a),a=f+h,g=[];a--;g.push(0));for(y=F,v=O,a=h;--a>=0;){for(r=0,m=_[a]%v,d=_[a]/v|0,u=f,s=a+u;s>a;)l=b[--u]%v,p=b[u]/v|0,c=d*l+p*m,l=m*l+c%v*v+g[s]+r,r=(l/y|0)+(c/v|0)+d*p,g[s--]=l%y;g[s]=r}return r?++o:g.shift(),T(t,g,o)},R.toDigits=function(t,n){var r=new e(this);return t=null!=t&&W(t,1,A,18,"precision")?0|t:null,n=null!=n&&W(n,0,8,18,b)?0|n:j,t?S(r,t,n):r},R.toExponential=function(t,e){return h(this,null!=t&&W(t,0,A,19)?~~t+1:null,e,19)},R.toFixed=function(t,e){return h(this,null!=t&&W(t,0,A,20)?~~t+this.e+1:null,e,20)},R.toFormat=function(t,e){var n=h(this,null!=t&&W(t,0,A,21)?~~t+this.e+1:null,e,21);if(this.c){var r,i=n.split("."),o=+X.groupSize,a=+X.secondaryGroupSize,s=X.groupSeparator,u=i[0],c=i[1],f=this.s<0,l=f?u.slice(1):u,p=l.length;if(a&&(r=o,o=a,a=r,p-=r),o>0&&p>0){for(r=p%o||o,u=l.substr(0,r);p>r;r+=o)u+=s+l.substr(r,o);a>0&&(u+=s+l.slice(r)),f&&(u="-"+u)}n=c?u+X.decimalSeparator+((a=+X.fractionGroupSize)?c.replace(new RegExp("\\d{"+a+"}\\B","g"),"$&"+X.fractionGroupSeparator):c):u}return n},R.toFraction=function(t){var n,r,i,a,s,u,c,f,l,p=z,h=this,m=h.c,d=new e(H),g=r=new e(H),y=c=new e(H);if(null!=t&&(z=!1,u=new e(t),z=p,(!(p=u.isInt())||u.lt(H))&&(z&&D(22,"max denominator "+(p?"out of range":"not an integer"),t),t=!p&&u.c&&S(u,u.e+1,1).gte(H)?u:null)),!m)return h.toString();for(l=o(m),a=d.e=l.length-h.e-1,d.c[0]=N[(s=a%I)<0?I+s:s],t=!t||u.cmp(d)>0?a>0?d:g:u,s=M,M=1/0,u=new e(l),c.c[0]=0;f=E(u,d,0,1),i=r.plus(f.times(y)),1!=i.cmp(t);)r=y,y=i,g=c.plus(f.times(i=g)),c=i,d=u.minus(f.times(i=d)),u=i;return i=E(t.minus(r),y,0,1),c=c.plus(i.times(g)),r=r.plus(i.times(y)),c.s=g.s=h.s,a*=2,n=E(g,y,a,j).minus(h).abs().cmp(E(c,r,a,j).minus(h).abs())<1?[g.toString(),y.toString()]:[c.toString(),r.toString()],M=s,n},R.toNumber=function(){var t=this;return+t||(t.s?0*t.s:0/0)},R.toPower=R.pow=function(t){var n,r,i=v(0>t?-t:+t),o=this;if(!W(t,-B,B,23,"exponent")&&(!isFinite(t)||i>B&&(t/=0)||parseFloat(t)!=t&&!(t=0/0)))return new e(Math.pow(+o,t));for(n=V?y(V/I+2):0,r=new e(H);;){if(i%2){if(r=r.times(o),!r.c)break;n&&r.c.length>n&&(r.c.length=n)}if(i=v(i/2),!i)break;o=o.times(o),n&&o.c&&o.c.length>n&&(o.c.length=n)}return 0>t&&(r=H.div(r)),n?S(r,V,j):r},R.toPrecision=function(t,e){return h(this,null!=t&&W(t,1,A,24,"precision")?0|t:null,e,24)},R.toString=function(t){var e,r=this,i=r.s,a=r.e;return null===a?i?(e="Infinity",0>i&&(e="-"+e)):e="NaN":(e=o(r.c),e=null!=t&&W(t,2,64,25,"base")?n(l(e,a),0|t,10,i):q>=a||a>=U?f(e,a):l(e,a),0>i&&r.c[0]&&(e="-"+e)),e},R.truncated=R.trunc=function(){return S(new e(this),this.e+1,1)},R.valueOf=R.toJSON=function(){return this.toString()},null!=t&&e.config(t),e}function i(t){var e=0|t;return t>0||t===e?e:e-1}function o(t){for(var e,n,r=1,i=t.length,o=t[0]+"";i>r;){for(e=t[r++]+"",n=I-e.length;n--;e="0"+e);o+=e}for(i=o.length;48===o.charCodeAt(--i););return o.slice(0,i+1||1)}function a(t,e){var n,r,i=t.c,o=e.c,a=t.s,s=e.s,u=t.e,c=e.e;if(!a||!s)return null;if(n=i&&!i[0],r=o&&!o[0],n||r)return n?r?0:-s:a;if(a!=s)return a;if(n=0>a,r=u==c,!i||!o)return r?0:!i^n?1:-1;if(!r)return u>c^n?1:-1;for(s=(u=i.length)<(c=o.length)?u:c,a=0;s>a;a++)if(i[a]!=o[a])return i[a]>o[a]^n?1:-1;return u==c?0:u>c^n?1:-1}function s(t,e,n){return(t=p(t))>=e&&n>=t}function u(t){return"[object Array]"==Object.prototype.toString.call(t)}function c(t,e,n){for(var r,i,o=[0],a=0,s=t.length;s>a;){for(i=o.length;i--;o[i]*=e);for(o[r=0]+=x.indexOf(t.charAt(a++));rn-1&&(null==o[r+1]&&(o[r+1]=0),o[r+1]+=o[r]/n|0,o[r]%=n)}return o.reverse()}function f(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(0>e?"e":"e+")+e}function l(t,e){var n,r;if(0>e){for(r="0.";++e;r+="0");t=r+t}else if(n=t.length,++e>n){for(r="0",e-=n;--e;r+="0");t+=r}else n>e&&(t=t.slice(0,e)+"."+t.slice(e));return t}function p(t){return t=parseFloat(t),0>t?y(t):v(t)}var h,m,d,g=/^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,y=Math.ceil,v=Math.floor,w=" not a boolean or binary digit",b="rounding mode",_="number type has more than 15 significant digits",x="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_",F=1e14,I=14,B=9007199254740991,N=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],O=1e7,A=1e9;if(h=r(),"function"==typeof define&&define.amd)define(function(){return h});else if("undefined"!=typeof e&&e.exports){if(e.exports=h,!m)try{m=t("crypto")}catch(k){}}else n.BigNumber=h}(this)},{crypto:31}],web3:[function(t,e,n){var r=t("./lib/web3");r.providers.HttpProvider=t("./lib/web3/httpprovider"),r.providers.QtSyncProvider=t("./lib/web3/qtsync"),r.eth.contract=t("./lib/web3/contract"),r.eth.namereg=t("./lib/web3/namereg"),r.eth.sendIBANTransaction=t("./lib/web3/transfer"),"undefined"!=typeof window&&"undefined"==typeof window.web3&&(window.web3=r),e.exports=r},{"./lib/web3":9,"./lib/web3/contract":11,"./lib/web3/httpprovider":19,"./lib/web3/namereg":23,"./lib/web3/qtsync":26,"./lib/web3/transfer":29}]},{},["web3"]); \ No newline at end of file +require=function t(e,n,r){function i(a,s){if(!n[a]){if(!e[a]){var u="function"==typeof require&&require;if(!s&&u)return u(a,!0);if(o)return o(a,!0);var c=new Error("Cannot find module '"+a+"'");throw c.code="MODULE_NOT_FOUND",c}var l=n[a]={exports:{}};e[a][0].call(l.exports,function(t){var n=e[a][1][t];return i(n?n:t)},l,l.exports,t,e,n,r)}return n[a].exports}for(var o="function"==typeof require&&require,a=0;ao;o+=64)n.push(this._outputFormatter(new a(t.dynamicPart().substr(o+64,64))));return n}return this._outputFormatter(t)},u.prototype.sliceParam=function(t,e,n){return"bytes"===this._mode?a.decodeBytes(t,e):s(n)?a.decodeArray(t,e):a.decodeParam(t,e)};var c=function(t){this._types=t};c.prototype._requireType=function(t){var e=this._types.filter(function(e){return e.isType(t)})[0];if(!e)throw Error("invalid solidity type!: "+t);return e},c.prototype._formatInput=function(t,e){return this._requireType(t).formatInput(e,s(t))},c.prototype.encodeParam=function(t,e){return this._formatInput(t,e).encode()},c.prototype.encodeParams=function(t,e){var n=this,r=t.map(function(t,r){return n._formatInput(t,e[r])});return a.encodeList(r)},c.prototype.decodeParam=function(t,e){return this.decodeParams([t],e)[0]},c.prototype.decodeParams=function(t,e){var n=this;return t.map(function(t,r){var i=n._requireType(t),o=i.sliceParam(e,r,t);return i.formatOutput(o,s(t))})};var l=new c([new u({name:"address",match:"strict",mode:"value",inputFormatter:o.formatInputInt,outputFormatter:o.formatOutputAddress}),new u({name:"bool",match:"strict",mode:"value",inputFormatter:o.formatInputBool,outputFormatter:o.formatOutputBool}),new u({name:"int",match:"prefix",mode:"value",inputFormatter:o.formatInputInt,outputFormatter:o.formatOutputInt}),new u({name:"uint",match:"prefix",mode:"value",inputFormatter:o.formatInputInt,outputFormatter:o.formatOutputUInt}),new u({name:"bytes",match:"strict",mode:"bytes",inputFormatter:o.formatInputDynamicBytes,outputFormatter:o.formatOutputDynamicBytes}),new u({name:"bytes",match:"prefix",mode:"value",inputFormatter:o.formatInputBytes,outputFormatter:o.formatOutputBytes}),new u({name:"real",match:"prefix",mode:"value",inputFormatter:o.formatInputReal,outputFormatter:o.formatOutputReal}),new u({name:"ureal",match:"prefix",mode:"value",inputFormatter:o.formatInputReal,outputFormatter:o.formatOutputUReal})]);e.exports=l},{"../utils/utils":7,"./formatters":2,"./param":3,"bignumber.js":"bignumber.js"}],2:[function(t,e,n){var r=t("bignumber.js"),i=t("../utils/utils"),o=t("../utils/config"),a=t("./param"),s=function(t){var e=2*o.ETH_PADDING;r.config(o.ETH_BIGNUMBER_ROUNDING_MODE);var n=i.padLeft(i.toTwosComplement(t).round().toString(16),e);return new a(n)},u=function(t){var e=i.fromAscii(t,o.ETH_PADDING).substr(2);return new a(e)},c=function(t){var e=i.fromAscii(t,o.ETH_PADDING).substr(2);return new a(s(t.length).value+e,32)},l=function(t){var e="000000000000000000000000000000000000000000000000000000000000000"+(t?"1":"0");return new a(e)},f=function(t){return s(new r(t).times(new r(2).pow(128)))},p=function(t){return"1"===new r(t.substr(0,1),16).toString(2).substr(0,1)},h=function(t){var e=t.staticPart()||"0";return p(e)?new r(e,16).minus(new r("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16)).minus(1):new r(e,16)},m=function(t){var e=t.staticPart()||"0";return new r(e,16)},d=function(t){return h(t).dividedBy(new r(2).pow(128))},g=function(t){return m(t).dividedBy(new r(2).pow(128))},y=function(t){return"0000000000000000000000000000000000000000000000000000000000000001"===t.staticPart()?!0:!1},v=function(t){return i.toAscii(t.staticPart())},w=function(t){return i.toAscii(t.dynamicPart().slice(64))},b=function(t){var e=t.staticPart();return"0x"+e.slice(e.length-40,e.length)};e.exports={formatInputInt:s,formatInputBytes:u,formatInputDynamicBytes:c,formatInputBool:l,formatInputReal:f,formatOutputInt:h,formatOutputUInt:m,formatOutputReal:d,formatOutputUReal:g,formatOutputBool:y,formatOutputBytes:v,formatOutputDynamicBytes:w,formatOutputAddress:b}},{"../utils/config":5,"../utils/utils":7,"./param":3,"bignumber.js":"bignumber.js"}],3:[function(t,e,n){var r=t("../utils/utils"),i=function(t,e){this.value=t||"",this.offset=e};i.prototype.dynamicPartLength=function(){return this.dynamicPart().length/2},i.prototype.withOffset=function(t){return new i(this.value,t)},i.prototype.combine=function(t){return new i(this.value+t.value)},i.prototype.isDynamic=function(){return this.value.length>64||void 0!==this.offset},i.prototype.offsetAsBytes=function(){return this.isDynamic()?r.padLeft(r.toTwosComplement(this.offset).toString(16),64):""},i.prototype.staticPart=function(){return this.isDynamic()?this.offsetAsBytes():this.value},i.prototype.dynamicPart=function(){return this.isDynamic()?this.value:""},i.prototype.encode=function(){return this.staticPart()+this.dynamicPart()},i.encodeList=function(t){var e=32*t.length,n=t.map(function(t){if(!t.isDynamic())return t;var n=e;return e+=t.dynamicPartLength(),t.withOffset(n)});return n.reduce(function(t,e){return t+e.dynamicPart()},n.reduce(function(t,e){return t+e.staticPart()},""))},i.decodeParam=function(t,e){return e=e||0,new i(t.substr(64*e,64))};var o=function(t,e){return parseInt("0x"+t.substr(64*e,64))};i.decodeBytes=function(t,e){e=e||0;var n=o(t,e);return new i(t.substr(2*n,128),0)},i.decodeArray=function(t,e){e=e||0;var n=o(t,e),r=parseInt("0x"+t.substr(2*n,64));return new i(t.substr(2*n,64*(r+1)),0)},e.exports=i},{"../utils/utils":7}],4:[function(t,e,n){"use strict";n.XMLHttpRequest="undefined"==typeof XMLHttpRequest?{}:XMLHttpRequest},{}],5:[function(t,e,n){var r=t("bignumber.js"),i=["wei","kwei","Mwei","Gwei","szabo","finney","femtoether","picoether","nanoether","microether","milliether","nano","micro","milli","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_UNITS:i,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:r.ROUND_DOWN},ETH_POLLING_TIMEOUT:1e3,defaultBlock:"latest",defaultAccount:void 0}},{"bignumber.js":"bignumber.js"}],6:[function(t,e,n){var r=t("./utils"),i=t("crypto-js/sha3");e.exports=function(t,e){return"0x"!==t.substr(0,2)||e||(console.warn("requirement of using web3.fromAscii before sha3 is deprecated"),console.warn("new usage: 'web3.sha3(\"hello\")'"),console.warn("see https://github.com/ethereum/web3.js/pull/205"),console.warn("if you need to hash hex value, you can do 'sha3(\"0xfff\", true)'"),t=r.toAscii(t)),i(t,{outputLength:256}).toString()}},{"./utils":7,"crypto-js/sha3":33}],7:[function(t,e,n){var r=t("bignumber.js"),i={wei:"1",kwei:"1000",ada:"1000",femtoether:"1000",mwei:"1000000",babbage:"1000000",picoether:"1000000",gwei:"1000000000",shannon:"1000000000",nanoether:"1000000000",nano:"1000000000",szabo:"1000000000000",microether:"1000000000000",micro:"1000000000000",finney:"1000000000000000",milliether:"1000000000000000",milli:"1000000000000000",ether:"1000000000000000000",kether:"1000000000000000000000",grand:"1000000000000000000000",einstein:"1000000000000000000000",mether:"1000000000000000000000000",gether:"1000000000000000000000000000",tether:"1000000000000000000000000000000"},o=function(t,e,n){return new Array(e-t.length+1).join(n?n:"0")+t},a=function(t){var e="",n=0,r=t.length;for("0x"===t.substring(0,2)&&(n=2);r>n;n+=2){var i=parseInt(t.substr(n,2),16);if(0===i)break;e+=String.fromCharCode(i)}return e},s=function(t){for(var e="",n=0;nthis._inputTypes.length&&o.isObject(t[t.length-1])&&(e=t[t.length-1]),e.to=this._address,e.data="0x"+this.signature()+i.encodeParams(this._inputTypes,t),e},s.prototype.signature=function(){return a(this._name).slice(0,8)},s.prototype.unpackOutput=function(t){if(t){t=t.length>=2?t.slice(2):t;var e=i.decodeParams(this._outputTypes,t);return 1===e.length?e[0]:e}},s.prototype.call=function(){var t=Array.prototype.slice.call(arguments).filter(function(t){return void 0!==t}),e=this.extractCallback(t),n=this.toPayload(t);if(!e){var i=r.eth.call(n);return this.unpackOutput(i)}var o=this;r.eth.call(n,function(t,n){e(t,o.unpackOutput(n))})},s.prototype.sendTransaction=function(){var t=Array.prototype.slice.call(arguments).filter(function(t){return void 0!==t}),e=this.extractCallback(t),n=this.toPayload(t);return e?void r.eth.sendTransaction(n,e):r.eth.sendTransaction(n)},s.prototype.estimateGas=function(){var t=Array.prototype.slice.call(arguments),e=this.extractCallback(t),n=this.toPayload(t);return e?void r.eth.estimateGas(n,e):r.eth.estimateGas(n)},s.prototype.displayName=function(){return o.extractDisplayName(this._name)},s.prototype.typeName=function(){return o.extractTypeName(this._name)},s.prototype.request=function(){var t=Array.prototype.slice.call(arguments),e=this.extractCallback(t),n=this.toPayload(t),r=this.unpackOutput.bind(this);return{callback:e,payload:n,format:r}},s.prototype.execute=function(){var t=!this._constant;return t?this.sendTransaction.apply(this,Array.prototype.slice.call(arguments)):this.call.apply(this,Array.prototype.slice.call(arguments))},s.prototype.attachToContract=function(t){var e=this.execute.bind(this);e.request=this.request.bind(this),e.call=this.call.bind(this),e.sendTransaction=this.sendTransaction.bind(this),e.estimateGas=this.estimateGas.bind(this);var n=this.displayName();t[n]||(t[n]=e),t[n][this.typeName()]=e},e.exports=s},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"../web3":9}],19:[function(t,e,n){"use strict";var r=t("xmlhttprequest").XMLHttpRequest,i=t("./errors"),o=function(t){this.host=t||"http://localhost:8545"};o.prototype.send=function(t){var e=new r;e.open("POST",this.host,!1);try{e.send(JSON.stringify(t))}catch(n){throw i.InvalidConnection(this.host)}var o=e.responseText;try{o=JSON.parse(o)}catch(a){throw i.InvalidResponse(o)}return o},o.prototype.sendAsync=function(t,e){var n=new r;n.onreadystatechange=function(){if(4===n.readyState){var t=n.responseText,r=null;try{t=JSON.parse(t)}catch(o){r=i.InvalidResponse(t)}e(r,t)}},n.open("POST",this.host,!0);try{n.send(JSON.stringify(t))}catch(o){e(i.InvalidConnection(this.host))}},e.exports=o},{"./errors":13,xmlhttprequest:4}],20:[function(t,e,n){var r=t("../utils/utils"),i=function(t){this._iban=t};i.prototype.isValid=function(){return r.isIBAN(this._iban)},i.prototype.isDirect=function(){return 34===this._iban.length},i.prototype.isIndirect=function(){return 20===this._iban.length},i.prototype.checksum=function(){return this._iban.substr(2,2)},i.prototype.institution=function(){return this.isIndirect()?this._iban.substr(7,4):""},i.prototype.client=function(){return this.isIndirect()?this._iban.substr(11):""},i.prototype.address=function(){return this.isDirect()?this._iban.substr(4):""},e.exports=i},{"../utils/utils":7}],21:[function(t,e,n){var r=function(){return arguments.callee._singletonInstance?arguments.callee._singletonInstance:(arguments.callee._singletonInstance=this,void(this.messageId=1))};r.getInstance=function(){var t=new r;return t},r.prototype.toPayload=function(t,e){return t||console.error("jsonrpc method should be specified!"),{jsonrpc:"2.0",method:t,params:e||[],id:this.messageId++}},r.prototype.isValidResponse=function(t){return!!t&&!t.error&&"2.0"===t.jsonrpc&&"number"==typeof t.id&&void 0!==t.result},r.prototype.toBatchPayload=function(t){var e=this;return t.map(function(t){return e.toPayload(t.method,t.params)})},e.exports=r},{}],22:[function(t,e,n){var r=t("./requestmanager"),i=t("../utils/utils"),o=t("./errors"),a=function(t){this.name=t.name,this.call=t.call,this.params=t.params||0,this.inputFormatter=t.inputFormatter,this.outputFormatter=t.outputFormatter};a.prototype.getCall=function(t){return i.isFunction(this.call)?this.call(t):this.call},a.prototype.extractCallback=function(t){return i.isFunction(t[t.length-1])?t.pop():void 0},a.prototype.validateArgs=function(t){if(t.length!==this.params)throw o.InvalidNumberOfParams()},a.prototype.formatInput=function(t){return this.inputFormatter?this.inputFormatter.map(function(e,n){return e?e(t[n]):t[n]}):t},a.prototype.formatOutput=function(t){return this.outputFormatter&&null!==t?this.outputFormatter(t):t},a.prototype.attachToObject=function(t){var e=this.send.bind(this);e.request=this.request.bind(this),e.call=this.call;var n=this.name.split(".");n.length>1?(t[n[0]]=t[n[0]]||{},t[n[0]][n[1]]=e):t[n[0]]=e},a.prototype.toPayload=function(t){var e=this.getCall(t),n=this.extractCallback(t),r=this.formatInput(t);return this.validateArgs(r),{method:e,params:r,callback:n}},a.prototype.request=function(){var t=this.toPayload(Array.prototype.slice.call(arguments));return t.format=this.formatOutput.bind(this),t},a.prototype.send=function(){var t=this.toPayload(Array.prototype.slice.call(arguments));if(t.callback){var e=this;return r.getInstance().sendAsync(t,function(n,r){t.callback(n,e.formatOutput(r))})}return this.formatOutput(r.getInstance().send(t))},e.exports=a},{"../utils/utils":7,"./errors":13,"./requestmanager":27}],23:[function(t,e,n){var r=t("./contract"),i="0xc6d9d2cd449a754c494264e1809c50e34d64562b",o=[{constant:!0,inputs:[{name:"_owner",type:"address"}],name:"name",outputs:[{name:"o_name",type:"bytes32"}],type:"function"},{constant:!0,inputs:[{name:"_name",type:"bytes32"}],name:"owner",outputs:[{name:"",type:"address"}],type:"function"},{constant:!0,inputs:[{name:"_name",type:"bytes32"}],name:"content",outputs:[{name:"",type:"bytes32"}],type:"function"},{constant:!0,inputs:[{name:"_name",type:"bytes32"}],name:"addr",outputs:[{name:"",type:"address"}],type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"}],name:"reserve",outputs:[],type:"function"},{constant:!0,inputs:[{name:"_name",type:"bytes32"}],name:"subRegistrar",outputs:[{name:"o_subRegistrar",type:"address"}],type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"},{name:"_newOwner",type:"address"}],name:"transfer",outputs:[],type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"},{name:"_registrar",type:"address"}],name:"setSubRegistrar",outputs:[],type:"function"},{constant:!1,inputs:[],name:"Registrar",outputs:[],type:"function"},{constant:!1, +inputs:[{name:"_name",type:"bytes32"},{name:"_a",type:"address"},{name:"_primary",type:"bool"}],name:"setAddress",outputs:[],type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"},{name:"_content",type:"bytes32"}],name:"setContent",outputs:[],type:"function"},{constant:!1,inputs:[{name:"_name",type:"bytes32"}],name:"disown",outputs:[],type:"function"},{constant:!0,inputs:[{name:"_name",type:"bytes32"}],name:"register",outputs:[{name:"",type:"address"}],type:"function"},{anonymous:!1,inputs:[{indexed:!0,name:"name",type:"bytes32"}],name:"Changed",type:"event"},{anonymous:!1,inputs:[{indexed:!0,name:"name",type:"bytes32"},{indexed:!0,name:"addr",type:"address"}],name:"PrimaryChanged",type:"event"}];e.exports=r(o).at(i)},{"./contract":11}],24:[function(t,e,n){var r=t("../utils/utils"),i=t("./property"),o=[],a=[new i({name:"listening",getter:"net_listening"}),new i({name:"peerCount",getter:"net_peerCount",outputFormatter:r.toDecimal})];e.exports={methods:o,properties:a}},{"../utils/utils":7,"./property":25}],25:[function(t,e,n){var r=t("./requestmanager"),i=function(t){this.name=t.name,this.getter=t.getter,this.setter=t.setter,this.outputFormatter=t.outputFormatter,this.inputFormatter=t.inputFormatter};i.prototype.formatInput=function(t){return this.inputFormatter?this.inputFormatter(t):t},i.prototype.formatOutput=function(t){return this.outputFormatter&&null!==t?this.outputFormatter(t):t},i.prototype.attachToObject=function(t){var e={get:this.get.bind(this)},n=this.name.split("."),r=n[0];n.length>1&&(t[n[0]]=t[n[0]]||{},t=t[n[0]],r=n[1]),Object.defineProperty(t,r,e);var i=function(t,e){return t+e.charAt(0).toUpperCase()+e.slice(1)};t[i("get",r)]=this.getAsync.bind(this)},i.prototype.get=function(){return this.formatOutput(r.getInstance().send({method:this.getter}))},i.prototype.getAsync=function(t){var e=this;r.getInstance().sendAsync({method:this.getter},function(n,r){return n?t(n):void t(n,e.formatOutput(r))})},e.exports=i},{"./requestmanager":27}],26:[function(t,e,n){var r=function(){};r.prototype.send=function(t){var e=navigator.qt.callMethod(JSON.stringify(t));return JSON.parse(e)},e.exports=r},{}],27:[function(t,e,n){var r=t("./jsonrpc"),i=t("../utils/utils"),o=t("../utils/config"),a=t("./errors"),s=function(t){return arguments.callee._singletonInstance?arguments.callee._singletonInstance:(arguments.callee._singletonInstance=this,this.provider=t,this.polls=[],this.timeout=null,void this.poll())};s.getInstance=function(){var t=new s;return t},s.prototype.send=function(t){if(!this.provider)return console.error(a.InvalidProvider()),null;var e=r.getInstance().toPayload(t.method,t.params),n=this.provider.send(e);if(!r.getInstance().isValidResponse(n))throw a.InvalidResponse(n);return n.result},s.prototype.sendAsync=function(t,e){if(!this.provider)return e(a.InvalidProvider());var n=r.getInstance().toPayload(t.method,t.params);this.provider.sendAsync(n,function(t,n){return t?e(t):r.getInstance().isValidResponse(n)?void e(null,n.result):e(a.InvalidResponse(n))})},s.prototype.sendBatch=function(t,e){if(!this.provider)return e(a.InvalidProvider());var n=r.getInstance().toBatchPayload(t);this.provider.sendAsync(n,function(t,n){return t?e(t):i.isArray(n)?void e(t,n):e(a.InvalidResponse(n))})},s.prototype.setProvider=function(t){this.provider=t},s.prototype.startPolling=function(t,e,n,r){this.polls.push({data:t,id:e,callback:n,uninstall:r})},s.prototype.stopPolling=function(t){for(var e=this.polls.length;e--;){var n=this.polls[e];n.id===t&&this.polls.splice(e,1)}},s.prototype.reset=function(){this.polls.forEach(function(t){t.uninstall(t.id)}),this.polls=[],this.timeout&&(clearTimeout(this.timeout),this.timeout=null),this.poll()},s.prototype.poll=function(){if(this.timeout=setTimeout(this.poll.bind(this),o.ETH_POLLING_TIMEOUT),this.polls.length){if(!this.provider)return void console.error(a.InvalidProvider());var t=r.getInstance().toBatchPayload(this.polls.map(function(t){return t.data})),e=this;this.provider.sendAsync(t,function(t,n){if(!t){if(!i.isArray(n))throw a.InvalidResponse(n);n.map(function(t,n){return t.callback=e.polls[n].callback,t}).filter(function(t){var e=r.getInstance().isValidResponse(t);return e||t.callback(a.InvalidResponse(t)),e}).filter(function(t){return i.isArray(t.result)&&t.result.length>0}).forEach(function(t){t.callback(null,t.result)})}})}},e.exports=s},{"../utils/config":5,"../utils/utils":7,"./errors":13,"./jsonrpc":21}],28:[function(t,e,n){var r=t("./method"),i=t("./formatters"),o=new r({name:"post",call:"shh_post",params:1,inputFormatter:[i.inputPostFormatter]}),a=new r({name:"newIdentity",call:"shh_newIdentity",params:0}),s=new r({name:"hasIdentity",call:"shh_hasIdentity",params:1}),u=new r({name:"newGroup",call:"shh_newGroup",params:0}),c=new r({name:"addToGroup",call:"shh_addToGroup",params:0}),l=[o,a,s,u,c];e.exports={methods:l}},{"./formatters":17,"./method":22}],29:[function(t,e,n){var r=t("../web3"),i=t("./icap"),o=t("./namereg"),a=t("./contract"),s=function(t,e,n,r){var a=new i(e);if(!a.isValid())throw new Error("invalid iban address");if(a.isDirect())return u(t,a.address(),n,r);if(!r){var s=o.addr(a.institution());return c(t,s,n,a.client())}o.addr(a.insitution(),function(e,i){return c(t,i,n,a.client(),r)})},u=function(t,e,n,i){return r.eth.sendTransaction({address:e,from:t,value:n},i)},c=function(t,e,n,r,i){var o=[{constant:!1,inputs:[{name:"name",type:"bytes32"}],name:"deposit",outputs:[],type:"function"}];return a(o).at(e).deposit(r,{from:t,value:n},i)};e.exports=s},{"../web3":9,"./contract":11,"./icap":20,"./namereg":23}],30:[function(t,e,n){var r=t("./method"),i=function(){var t=function(t){var e=t[0];switch(e){case"latest":return t.pop(),this.params=0,"eth_newBlockFilter";case"pending":return t.pop(),this.params=0,"eth_newPendingTransactionFilter";default:return"eth_newFilter"}},e=new r({name:"newFilter",call:t,params:1}),n=new r({name:"uninstallFilter",call:"eth_uninstallFilter",params:1}),i=new r({name:"getLogs",call:"eth_getFilterLogs",params:1}),o=new r({name:"poll",call:"eth_getFilterChanges",params:1});return[e,n,i,o]},o=function(){var t=new r({name:"newFilter",call:"shh_newFilter",params:1}),e=new r({name:"uninstallFilter",call:"shh_uninstallFilter",params:1}),n=new r({name:"getLogs",call:"shh_getMessages",params:1}),i=new r({name:"poll",call:"shh_getFilterChanges",params:1});return[t,e,n,i]};e.exports={eth:i,shh:o}},{"./method":22}],31:[function(t,e,n){},{}],32:[function(t,e,n){!function(t,r){"object"==typeof n?e.exports=n=r():"function"==typeof define&&define.amd?define([],r):t.CryptoJS=r()}(this,function(){var t=t||function(t,e){var n={},r=n.lib={},i=r.Base=function(){function t(){}return{extend:function(e){t.prototype=this;var n=new t;return e&&n.mixIn(e),n.hasOwnProperty("init")||(n.init=function(){n.$super.init.apply(this,arguments)}),n.init.prototype=n,n.$super=this,n},create:function(){var t=this.extend();return t.init.apply(t,arguments),t},init:function(){},mixIn:function(t){for(var e in t)t.hasOwnProperty(e)&&(this[e]=t[e]);t.hasOwnProperty("toString")&&(this.toString=t.toString)},clone:function(){return this.init.prototype.extend(this)}}}(),o=r.WordArray=i.extend({init:function(t,n){t=this.words=t||[],this.sigBytes=n!=e?n:4*t.length},toString:function(t){return(t||s).stringify(this)},concat:function(t){var e=this.words,n=t.words,r=this.sigBytes,i=t.sigBytes;if(this.clamp(),r%4)for(var o=0;i>o;o++){var a=n[o>>>2]>>>24-o%4*8&255;e[r+o>>>2]|=a<<24-(r+o)%4*8}else for(var o=0;i>o;o+=4)e[r+o>>>2]=n[o>>>2];return this.sigBytes+=i,this},clamp:function(){var e=this.words,n=this.sigBytes;e[n>>>2]&=4294967295<<32-n%4*8,e.length=t.ceil(n/4)},clone:function(){var t=i.clone.call(this);return t.words=this.words.slice(0),t},random:function(e){for(var n,r=[],i=function(e){var e=e,n=987654321,r=4294967295;return function(){n=36969*(65535&n)+(n>>16)&r,e=18e3*(65535&e)+(e>>16)&r;var i=(n<<16)+e&r;return i/=4294967296,i+=.5,i*(t.random()>.5?1:-1)}},a=0;e>a;a+=4){var s=i(4294967296*(n||t.random()));n=987654071*s(),r.push(4294967296*s()|0)}return new o.init(r,e)}}),a=n.enc={},s=a.Hex={stringify:function(t){for(var e=t.words,n=t.sigBytes,r=[],i=0;n>i;i++){var o=e[i>>>2]>>>24-i%4*8&255;r.push((o>>>4).toString(16)),r.push((15&o).toString(16))}return r.join("")},parse:function(t){for(var e=t.length,n=[],r=0;e>r;r+=2)n[r>>>3]|=parseInt(t.substr(r,2),16)<<24-r%8*4;return new o.init(n,e/2)}},u=a.Latin1={stringify:function(t){for(var e=t.words,n=t.sigBytes,r=[],i=0;n>i;i++){var o=e[i>>>2]>>>24-i%4*8&255;r.push(String.fromCharCode(o))}return r.join("")},parse:function(t){for(var e=t.length,n=[],r=0;e>r;r++)n[r>>>2]|=(255&t.charCodeAt(r))<<24-r%4*8;return new o.init(n,e)}},c=a.Utf8={stringify:function(t){try{return decodeURIComponent(escape(u.stringify(t)))}catch(e){throw new Error("Malformed UTF-8 data")}},parse:function(t){return u.parse(unescape(encodeURIComponent(t)))}},l=r.BufferedBlockAlgorithm=i.extend({reset:function(){this._data=new o.init,this._nDataBytes=0},_append:function(t){"string"==typeof t&&(t=c.parse(t)),this._data.concat(t),this._nDataBytes+=t.sigBytes},_process:function(e){var n=this._data,r=n.words,i=n.sigBytes,a=this.blockSize,s=4*a,u=i/s;u=e?t.ceil(u):t.max((0|u)-this._minBufferSize,0);var c=u*a,l=t.min(4*c,i);if(c){for(var f=0;c>f;f+=a)this._doProcessBlock(r,f);var p=r.splice(0,c);n.sigBytes-=l}return new o.init(p,l)},clone:function(){var t=i.clone.call(this);return t._data=this._data.clone(),t},_minBufferSize:0}),f=(r.Hasher=l.extend({cfg:i.extend(),init:function(t){this.cfg=this.cfg.extend(t),this.reset()},reset:function(){l.reset.call(this),this._doReset()},update:function(t){return this._append(t),this._process(),this},finalize:function(t){t&&this._append(t);var e=this._doFinalize();return e},blockSize:16,_createHelper:function(t){return function(e,n){return new t.init(n).finalize(e)}},_createHmacHelper:function(t){return function(e,n){return new f.HMAC.init(t,n).finalize(e)}}}),n.algo={});return n}(Math);return t})},{}],33:[function(t,e,n){!function(r,i,o){"object"==typeof n?e.exports=n=i(t("./core"),t("./x64-core")):"function"==typeof define&&define.amd?define(["./core","./x64-core"],i):i(r.CryptoJS)}(this,function(t){return function(e){var n=t,r=n.lib,i=r.WordArray,o=r.Hasher,a=n.x64,s=a.Word,u=n.algo,c=[],l=[],f=[];!function(){for(var t=1,e=0,n=0;24>n;n++){c[t+5*e]=(n+1)*(n+2)/2%64;var r=e%5,i=(2*t+3*e)%5;t=r,e=i}for(var t=0;5>t;t++)for(var e=0;5>e;e++)l[t+5*e]=e+(2*t+3*e)%5*5;for(var o=1,a=0;24>a;a++){for(var u=0,p=0,h=0;7>h;h++){if(1&o){var m=(1<m?p^=1<t;t++)p[t]=s.create()}();var h=u.SHA3=o.extend({cfg:o.cfg.extend({outputLength:512}),_doReset:function(){for(var t=this._state=[],e=0;25>e;e++)t[e]=new s.init;this.blockSize=(1600-2*this.cfg.outputLength)/32},_doProcessBlock:function(t,e){for(var n=this._state,r=this.blockSize/2,i=0;r>i;i++){var o=t[e+2*i],a=t[e+2*i+1];o=16711935&(o<<8|o>>>24)|4278255360&(o<<24|o>>>8),a=16711935&(a<<8|a>>>24)|4278255360&(a<<24|a>>>8);var s=n[i];s.high^=a,s.low^=o}for(var u=0;24>u;u++){for(var h=0;5>h;h++){for(var m=0,d=0,g=0;5>g;g++){var s=n[h+5*g];m^=s.high,d^=s.low}var y=p[h];y.high=m,y.low=d}for(var h=0;5>h;h++)for(var v=p[(h+4)%5],w=p[(h+1)%5],b=w.high,_=w.low,m=v.high^(b<<1|_>>>31),d=v.low^(_<<1|b>>>31),g=0;5>g;g++){var s=n[h+5*g];s.high^=m,s.low^=d}for(var x=1;25>x;x++){var s=n[x],F=s.high,I=s.low,B=c[x];if(32>B)var m=F<>>32-B,d=I<>>32-B;else var m=I<>>64-B,d=F<>>64-B;var N=p[l[x]];N.high=m,N.low=d}var O=p[0],A=n[0];O.high=A.high,O.low=A.low;for(var h=0;5>h;h++)for(var g=0;5>g;g++){var x=h+5*g,s=n[x],k=p[x],P=p[(h+1)%5+5*g],T=p[(h+2)%5+5*g];s.high=k.high^~P.high&T.high,s.low=k.low^~P.low&T.low}var s=n[0],D=f[u];s.high^=D.high,s.low^=D.low}},_doFinalize:function(){var t=this._data,n=t.words,r=(8*this._nDataBytes,8*t.sigBytes),o=32*this.blockSize;n[r>>>5]|=1<<24-r%32,n[(e.ceil((r+1)/o)*o>>>5)-1]|=128,t.sigBytes=4*n.length,this._process();for(var a=this._state,s=this.cfg.outputLength/8,u=s/8,c=[],l=0;u>l;l++){var f=a[l],p=f.high,h=f.low;p=16711935&(p<<8|p>>>24)|4278255360&(p<<24|p>>>8),h=16711935&(h<<8|h>>>24)|4278255360&(h<<24|h>>>8),c.push(h),c.push(p)}return new i.init(c,s)},clone:function(){for(var t=o.clone.call(this),e=t._state=this._state.slice(0),n=0;25>n;n++)e[n]=e[n].clone();return t}});n.SHA3=o._createHelper(h),n.HmacSHA3=o._createHmacHelper(h)}(Math),t.SHA3})},{"./core":32,"./x64-core":34}],34:[function(t,e,n){!function(r,i){"object"==typeof n?e.exports=n=i(t("./core")):"function"==typeof define&&define.amd?define(["./core"],i):i(r.CryptoJS)}(this,function(t){return function(e){{var n=t,r=n.lib,i=r.Base,o=r.WordArray,a=n.x64={};a.Word=i.extend({init:function(t,e){this.high=t,this.low=e}}),a.WordArray=i.extend({init:function(t,n){t=this.words=t||[],this.sigBytes=n!=e?n:8*t.length},toX32:function(){for(var t=this.words,e=t.length,n=[],r=0;e>r;r++){var i=t[r];n.push(i.high),n.push(i.low)}return o.create(n,this.sigBytes)},clone:function(){for(var t=i.clone.call(this),e=t.words=this.words.slice(0),n=e.length,r=0;n>r;r++)e[r]=e[r].clone();return t}})}}(),t})},{"./core":32}],"bignumber.js":[function(t,e,n){!function(n){"use strict";function r(t){function e(t,r){var i,o,a,s,u,c,l=this;if(!(l instanceof e))return z&&D(26,"constructor call without new",t),new e(t,r);if(null!=r&&W(r,2,64,C,"base")){if(r=0|r,c=t+"",10==r)return l=new e(t instanceof e?t:c),S(l,L+l.e+1,j);if((s="number"==typeof t)&&0*t!=0||!new RegExp("^-?"+(i="["+x.slice(0,r)+"]+")+"(?:\\."+i+")?$",37>r?"i":"").test(c))return d(l,c,s,r);s?(l.s=0>1/t?(c=c.slice(1),-1):1,z&&c.replace(/^0\.0*|\./,"").length>15&&D(C,_,t),s=!1):l.s=45===c.charCodeAt(0)?(c=c.slice(1),-1):1,c=n(c,10,r,l.s)}else{if(t instanceof e)return l.s=t.s,l.e=t.e,l.c=(t=t.c)?t.slice():t,void(C=0);if((s="number"==typeof t)&&0*t==0){if(l.s=0>1/t?(t=-t,-1):1,t===~~t){for(o=0,a=t;a>=10;a/=10,o++);return l.e=o,l.c=[t],void(C=0)}c=t+""}else{if(!g.test(c=t+""))return d(l,c,s);l.s=45===c.charCodeAt(0)?(c=c.slice(1),-1):1}}for((o=c.indexOf("."))>-1&&(c=c.replace(".","")),(a=c.search(/e/i))>0?(0>o&&(o=a),o+=+c.slice(a+1),c=c.substring(0,a)):0>o&&(o=c.length),a=0;48===c.charCodeAt(a);a++);for(u=c.length;48===c.charCodeAt(--u););if(c=c.slice(a,u+1))if(u=c.length,s&&z&&u>15&&D(C,_,l.s*t),o=o-a-1,o>M)l.c=l.e=null;else if(G>o)l.c=[l.e=0];else{if(l.e=o,l.c=[],a=(o+1)%I,0>o&&(a+=I),u>a){for(a&&l.c.push(+c.slice(0,a)),u-=I;u>a;)l.c.push(+c.slice(a,a+=I));c=c.slice(a),a=I-c.length}else a-=u;for(;a--;c+="0");l.c.push(+c)}else l.c=[l.e=0];C=0}function n(t,n,r,i){var a,s,u,l,p,h,m,d=t.indexOf("."),g=L,y=j;for(37>r&&(t=t.toLowerCase()),d>=0&&(u=V,V=0,t=t.replace(".",""),m=new e(r),p=m.pow(t.length-d),V=u,m.c=c(f(o(p.c),p.e),10,n),m.e=m.c.length),h=c(t,r,n),s=u=h.length;0==h[--u];h.pop());if(!h[0])return"0";if(0>d?--s:(p.c=h,p.e=s,p.s=i,p=E(p,m,g,y,n),h=p.c,l=p.r,s=p.e),a=s+g+1,d=h[a],u=n/2,l=l||0>a||null!=h[a+1],l=4>y?(null!=d||l)&&(0==y||y==(p.s<0?3:2)):d>u||d==u&&(4==y||l||6==y&&1&h[a-1]||y==(p.s<0?8:7)),1>a||!h[0])t=l?f("1",-g):"0";else{if(h.length=a,l)for(--n;++h[--a]>n;)h[a]=0,a||(++s,h.unshift(1));for(u=h.length;!h[--u];);for(d=0,t="";u>=d;t+=x.charAt(h[d++]));t=f(t,s)}return t}function h(t,n,r,i){var a,s,u,c,p;if(r=null!=r&&W(r,0,8,i,b)?0|r:j,!t.c)return t.toString();if(a=t.c[0],u=t.e,null==n)p=o(t.c),p=19==i||24==i&&q>=u?l(p,u):f(p,u);else if(t=S(new e(t),n,r),s=t.e,p=o(t.c),c=p.length,19==i||24==i&&(s>=n||q>=s)){for(;n>c;p+="0",c++);p=l(p,s)}else if(n-=u,p=f(p,s),s+1>c){if(--n>0)for(p+=".";n--;p+="0");}else if(n+=s-c,n>0)for(s+1==c&&(p+=".");n--;p+="0");return t.s<0&&a?"-"+p:p}function k(t,n){var r,i,o=0;for(u(t[0])&&(t=t[0]),r=new e(t[0]);++ot||t>n||t!=p(t))&&D(r,(i||"decimal places")+(e>t||t>n?" out of range":" not an integer"),t),!0}function T(t,e,n){for(var r=1,i=e.length;!e[--i];e.pop());for(i=e[0];i>=10;i/=10,r++);return(n=r+n*I-1)>M?t.c=t.e=null:G>n?t.c=[t.e=0]:(t.e=n,t.c=e),t}function D(t,e,n){var r=new Error(["new BigNumber","cmp","config","div","divToInt","eq","gt","gte","lt","lte","minus","mod","plus","precision","random","round","shift","times","toDigits","toExponential","toFixed","toFormat","toFraction","pow","toPrecision","toString","BigNumber"][t]+"() "+e+": "+n);throw r.name="BigNumber Error",C=0,r}function S(t,e,n,r){var i,o,a,s,u,c,l,f=t.c,p=N;if(f){t:{for(i=1,s=f[0];s>=10;s/=10,i++);if(o=e-i,0>o)o+=I,a=e,u=f[c=0],l=u/p[i-a-1]%10|0;else if(c=y((o+1)/I),c>=f.length){if(!r)break t;for(;f.length<=c;f.push(0));u=l=0,i=1,o%=I,a=o-I+1}else{for(u=s=f[c],i=1;s>=10;s/=10,i++);o%=I,a=o-I+i,l=0>a?0:u/p[i-a-1]%10|0}if(r=r||0>e||null!=f[c+1]||(0>a?u:u%p[i-a-1]),r=4>n?(l||r)&&(0==n||n==(t.s<0?3:2)):l>5||5==l&&(4==n||r||6==n&&(o>0?a>0?u/p[i-a]:0:f[c-1])%10&1||n==(t.s<0?8:7)),1>e||!f[0])return f.length=0,r?(e-=t.e+1,f[0]=p[e%I],t.e=-e||0):f[0]=t.e=0,t;if(0==o?(f.length=c,s=1,c--):(f.length=c+1,s=p[I-o],f[c]=a>0?v(u/p[i-a]%p[a])*s:0),r)for(;;){if(0==c){for(o=1,a=f[0];a>=10;a/=10,o++);for(a=f[0]+=s,s=1;a>=10;a/=10,s++);o!=s&&(t.e++,f[0]==F&&(f[0]=1));break}if(f[c]+=s,f[c]!=F)break;f[c--]=0,s=1}for(o=f.length;0===f[--o];f.pop());}t.e>M?t.c=t.e=null:t.en?null!=(t=i[n++]):void 0};return a(e="DECIMAL_PLACES")&&W(t,0,A,2,e)&&(L=0|t),r[e]=L,a(e="ROUNDING_MODE")&&W(t,0,8,2,e)&&(j=0|t),r[e]=j,a(e="EXPONENTIAL_AT")&&(u(t)?W(t[0],-A,0,2,e)&&W(t[1],0,A,2,e)&&(q=0|t[0],U=0|t[1]):W(t,-A,A,2,e)&&(q=-(U=0|(0>t?-t:t)))),r[e]=[q,U],a(e="RANGE")&&(u(t)?W(t[0],-A,-1,2,e)&&W(t[1],1,A,2,e)&&(G=0|t[0],M=0|t[1]):W(t,-A,A,2,e)&&(0|t?G=-(M=0|(0>t?-t:t)):z&&D(2,e+" cannot be zero",t))),r[e]=[G,M],a(e="ERRORS")&&(t===!!t||1===t||0===t?(C=0,W=(z=!!t)?P:s):z&&D(2,e+w,t)),r[e]=z,a(e="CRYPTO")&&(t===!!t||1===t||0===t?(J=!(!t||!m||"object"!=typeof m),t&&!J&&z&&D(2,"crypto unavailable",m)):z&&D(2,e+w,t)),r[e]=J,a(e="MODULO_MODE")&&W(t,0,9,2,e)&&($=0|t),r[e]=$,a(e="POW_PRECISION")&&W(t,0,A,2,e)&&(V=0|t),r[e]=V,a(e="FORMAT")&&("object"==typeof t?X=t:z&&D(2,e+" not an object",t)),r[e]=X,r},e.max=function(){return k(arguments,R.lt)},e.min=function(){return k(arguments,R.gt)},e.random=function(){var t=9007199254740992,n=Math.random()*t&2097151?function(){return v(Math.random()*t)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)};return function(t){var r,i,o,a,s,u=0,c=[],l=new e(H);if(t=null!=t&&W(t,0,A,14)?0|t:L,a=y(t/I),J)if(m&&m.getRandomValues){for(r=m.getRandomValues(new Uint32Array(a*=2));a>u;)s=131072*r[u]+(r[u+1]>>>11),s>=9e15?(i=m.getRandomValues(new Uint32Array(2)),r[u]=i[0],r[u+1]=i[1]):(c.push(s%1e14),u+=2);u=a/2}else if(m&&m.randomBytes){for(r=m.randomBytes(a*=7);a>u;)s=281474976710656*(31&r[u])+1099511627776*r[u+1]+4294967296*r[u+2]+16777216*r[u+3]+(r[u+4]<<16)+(r[u+5]<<8)+r[u+6],s>=9e15?m.randomBytes(7).copy(r,u):(c.push(s%1e14),u+=7);u=a/7}else z&&D(14,"crypto unavailable",m);if(!u)for(;a>u;)s=n(),9e15>s&&(c[u++]=s%1e14);for(a=c[--u],t%=I,a&&t&&(s=N[I-t],c[u]=v(a/s)*s);0===c[u];c.pop(),u--);if(0>u)c=[o=0];else{for(o=-1;0===c[0];c.shift(),o-=I);for(u=1,s=c[0];s>=10;s/=10,u++);I>u&&(o-=I-u)}return l.e=o,l.c=c,l}}(),E=function(){function t(t,e,n){var r,i,o,a,s=0,u=t.length,c=e%O,l=e/O|0;for(t=t.slice();u--;)o=t[u]%O,a=t[u]/O|0,r=l*o+a*c,i=c*o+r%O*O+s,s=(i/n|0)+(r/O|0)+l*a,t[u]=i%n;return s&&t.unshift(s),t}function n(t,e,n,r){var i,o;if(n!=r)o=n>r?1:-1;else for(i=o=0;n>i;i++)if(t[i]!=e[i]){o=t[i]>e[i]?1:-1;break}return o}function r(t,e,n,r){for(var i=0;n--;)t[n]-=i,i=t[n]1;t.shift());}return function(o,a,s,u,c){var l,f,p,h,m,d,g,y,w,b,_,x,B,N,O,A,k,P=o.s==a.s?1:-1,T=o.c,D=a.c;if(!(T&&T[0]&&D&&D[0]))return new e(o.s&&a.s&&(T?!D||T[0]!=D[0]:D)?T&&0==T[0]||!D?0*P:P/0:0/0);for(y=new e(P),w=y.c=[],f=o.e-a.e,P=s+f+1,c||(c=F,f=i(o.e/I)-i(a.e/I),P=P/I|0),p=0;D[p]==(T[p]||0);p++);if(D[p]>(T[p]||0)&&f--,0>P)w.push(1),h=!0;else{for(N=T.length,A=D.length,p=0,P+=2,m=v(c/(D[0]+1)),m>1&&(D=t(D,m,c),T=t(T,m,c),A=D.length,N=T.length),B=A,b=T.slice(0,A),_=b.length;A>_;b[_++]=0);k=D.slice(),k.unshift(0),O=D[0],D[1]>=c/2&&O++;do{if(m=0,l=n(D,b,A,_),0>l){if(x=b[0],A!=_&&(x=x*c+(b[1]||0)),m=v(x/O),m>1)for(m>=c&&(m=c-1),d=t(D,m,c),g=d.length,_=b.length;1==n(d,b,g,_);)m--,r(d,g>A?k:D,g,c),g=d.length,l=1;else 0==m&&(l=m=1),d=D.slice(),g=d.length;if(_>g&&d.unshift(0),r(b,d,_,c),_=b.length,-1==l)for(;n(D,b,A,_)<1;)m++,r(b,_>A?k:D,_,c),_=b.length}else 0===l&&(m++,b=[0]);w[p++]=m,b[0]?b[_++]=T[B]||0:(b=[T[B]],_=1)}while((B++=10;P/=10,p++);S(y,s+(y.e=p+f*I-1)+1,u,h)}else y.e=f,y.r=+h;return y}}(),d=function(){var t=/^(-?)0([xbo])/i,n=/^([^.]+)\.$/,r=/^\.([^.]+)$/,i=/^-?(Infinity|NaN)$/,o=/^\s*\+|^\s+|\s+$/g;return function(a,s,u,c){var l,f=u?s:s.replace(o,"");if(i.test(f))a.s=isNaN(f)?null:0>f?-1:1;else{if(!u&&(f=f.replace(t,function(t,e,n){return l="x"==(n=n.toLowerCase())?16:"b"==n?2:8,c&&c!=l?t:e}),c&&(l=c,f=f.replace(n,"$1").replace(r,"0.$1")),s!=f))return new e(f,l);z&&D(C,"not a"+(c?" base "+c:"")+" number",s),a.s=null}a.c=a.e=null,C=0}}(),R.absoluteValue=R.abs=function(){var t=new e(this);return t.s<0&&(t.s=1),t},R.ceil=function(){return S(new e(this),this.e+1,2)},R.comparedTo=R.cmp=function(t,n){return C=1,a(this,new e(t,n))},R.decimalPlaces=R.dp=function(){var t,e,n=this.c;if(!n)return null;if(t=((e=n.length-1)-i(this.e/I))*I,e=n[e])for(;e%10==0;e/=10,t--);return 0>t&&(t=0),t},R.dividedBy=R.div=function(t,n){return C=3,E(this,new e(t,n),L,j)},R.dividedToIntegerBy=R.divToInt=function(t,n){return C=4,E(this,new e(t,n),0,1)},R.equals=R.eq=function(t,n){return C=5,0===a(this,new e(t,n))},R.floor=function(){return S(new e(this),this.e+1,3)},R.greaterThan=R.gt=function(t,n){return C=6,a(this,new e(t,n))>0},R.greaterThanOrEqualTo=R.gte=function(t,n){return C=7,1===(n=a(this,new e(t,n)))||0===n},R.isFinite=function(){return!!this.c},R.isInteger=R.isInt=function(){return!!this.c&&i(this.e/I)>this.c.length-2},R.isNaN=function(){return!this.s},R.isNegative=R.isNeg=function(){return this.s<0},R.isZero=function(){return!!this.c&&0==this.c[0]},R.lessThan=R.lt=function(t,n){return C=8,a(this,new e(t,n))<0},R.lessThanOrEqualTo=R.lte=function(t,n){return C=9,-1===(n=a(this,new e(t,n)))||0===n},R.minus=R.sub=function(t,n){var r,o,a,s,u=this,c=u.s;if(C=10,t=new e(t,n),n=t.s,!c||!n)return new e(0/0);if(c!=n)return t.s=-n,u.plus(t);var l=u.e/I,f=t.e/I,p=u.c,h=t.c;if(!l||!f){if(!p||!h)return p?(t.s=-n,t):new e(h?u:0/0);if(!p[0]||!h[0])return h[0]?(t.s=-n,t):new e(p[0]?u:3==j?-0:0)}if(l=i(l),f=i(f),p=p.slice(),c=l-f){for((s=0>c)?(c=-c,a=p):(f=l,a=h),a.reverse(),n=c;n--;a.push(0));a.reverse()}else for(o=(s=(c=p.length)<(n=h.length))?c:n,c=n=0;o>n;n++)if(p[n]!=h[n]){s=p[n]0)for(;n--;p[r++]=0);for(n=F-1;o>c;){if(p[--o]0?(u=s,r=l):(a=-a,r=c),r.reverse();a--;r.push(0));r.reverse()}for(a=c.length,n=l.length,0>a-n&&(r=l,l=c,c=r,n=a),a=0;n;)a=(c[--n]=c[n]+l[n]+a)/F|0,c[n]%=F;return a&&(c.unshift(a),++u),T(t,c,u)},R.precision=R.sd=function(t){var e,n,r=this,i=r.c;if(null!=t&&t!==!!t&&1!==t&&0!==t&&(z&&D(13,"argument"+w,t),t!=!!t&&(t=null)),!i)return null;if(n=i.length-1,e=n*I+1,n=i[n]){for(;n%10==0;n/=10,e--);for(n=i[0];n>=10;n/=10,e++);}return t&&r.e+1>e&&(e=r.e+1),e},R.round=function(t,n){var r=new e(this);return(null==t||W(t,0,A,15))&&S(r,~~t+this.e+1,null!=n&&W(n,0,8,15,b)?0|n:j),r},R.shift=function(t){var n=this;return W(t,-B,B,16,"argument")?n.times("1e"+p(t)):new e(n.c&&n.c[0]&&(-B>t||t>B)?n.s*(0>t?0:1/0):n)},R.squareRoot=R.sqrt=function(){var t,n,r,a,s,u=this,c=u.c,l=u.s,f=u.e,p=L+4,h=new e("0.5");if(1!==l||!c||!c[0])return new e(!l||0>l&&(!c||c[0])?0/0:c?u:1/0);if(l=Math.sqrt(+u),0==l||l==1/0?(n=o(c),(n.length+f)%2==0&&(n+="0"),l=Math.sqrt(n),f=i((f+1)/2)-(0>f||f%2),l==1/0?n="1e"+f:(n=l.toExponential(),n=n.slice(0,n.indexOf("e")+1)+f),r=new e(n)):r=new e(l+""),r.c[0])for(f=r.e,l=f+p,3>l&&(l=0);;)if(s=r,r=h.times(s.plus(E(u,s,p,1))),o(s.c).slice(0,l)===(n=o(r.c)).slice(0,l)){if(r.el&&(g=b,b=_,_=g,a=l,l=h,h=a),a=l+h,g=[];a--;g.push(0));for(y=F,v=O,a=h;--a>=0;){for(r=0,m=_[a]%v,d=_[a]/v|0,u=l,s=a+u;s>a;)f=b[--u]%v,p=b[u]/v|0,c=d*f+p*m,f=m*f+c%v*v+g[s]+r,r=(f/y|0)+(c/v|0)+d*p,g[s--]=f%y;g[s]=r}return r?++o:g.shift(),T(t,g,o)},R.toDigits=function(t,n){var r=new e(this);return t=null!=t&&W(t,1,A,18,"precision")?0|t:null,n=null!=n&&W(n,0,8,18,b)?0|n:j,t?S(r,t,n):r},R.toExponential=function(t,e){return h(this,null!=t&&W(t,0,A,19)?~~t+1:null,e,19)},R.toFixed=function(t,e){return h(this,null!=t&&W(t,0,A,20)?~~t+this.e+1:null,e,20)},R.toFormat=function(t,e){var n=h(this,null!=t&&W(t,0,A,21)?~~t+this.e+1:null,e,21);if(this.c){var r,i=n.split("."),o=+X.groupSize,a=+X.secondaryGroupSize,s=X.groupSeparator,u=i[0],c=i[1],l=this.s<0,f=l?u.slice(1):u,p=f.length;if(a&&(r=o,o=a,a=r,p-=r),o>0&&p>0){for(r=p%o||o,u=f.substr(0,r);p>r;r+=o)u+=s+f.substr(r,o);a>0&&(u+=s+f.slice(r)),l&&(u="-"+u)}n=c?u+X.decimalSeparator+((a=+X.fractionGroupSize)?c.replace(new RegExp("\\d{"+a+"}\\B","g"),"$&"+X.fractionGroupSeparator):c):u}return n},R.toFraction=function(t){var n,r,i,a,s,u,c,l,f,p=z,h=this,m=h.c,d=new e(H),g=r=new e(H),y=c=new e(H);if(null!=t&&(z=!1,u=new e(t),z=p,(!(p=u.isInt())||u.lt(H))&&(z&&D(22,"max denominator "+(p?"out of range":"not an integer"),t),t=!p&&u.c&&S(u,u.e+1,1).gte(H)?u:null)),!m)return h.toString();for(f=o(m),a=d.e=f.length-h.e-1,d.c[0]=N[(s=a%I)<0?I+s:s],t=!t||u.cmp(d)>0?a>0?d:g:u,s=M,M=1/0,u=new e(f),c.c[0]=0;l=E(u,d,0,1),i=r.plus(l.times(y)),1!=i.cmp(t);)r=y,y=i,g=c.plus(l.times(i=g)),c=i,d=u.minus(l.times(i=d)),u=i;return i=E(t.minus(r),y,0,1),c=c.plus(i.times(g)),r=r.plus(i.times(y)),c.s=g.s=h.s,a*=2,n=E(g,y,a,j).minus(h).abs().cmp(E(c,r,a,j).minus(h).abs())<1?[g.toString(),y.toString()]:[c.toString(),r.toString()],M=s,n},R.toNumber=function(){var t=this;return+t||(t.s?0*t.s:0/0)},R.toPower=R.pow=function(t){var n,r,i=v(0>t?-t:+t),o=this;if(!W(t,-B,B,23,"exponent")&&(!isFinite(t)||i>B&&(t/=0)||parseFloat(t)!=t&&!(t=0/0)))return new e(Math.pow(+o,t));for(n=V?y(V/I+2):0,r=new e(H);;){if(i%2){if(r=r.times(o),!r.c)break;n&&r.c.length>n&&(r.c.length=n)}if(i=v(i/2),!i)break;o=o.times(o),n&&o.c&&o.c.length>n&&(o.c.length=n)}return 0>t&&(r=H.div(r)),n?S(r,V,j):r},R.toPrecision=function(t,e){return h(this,null!=t&&W(t,1,A,24,"precision")?0|t:null,e,24)},R.toString=function(t){var e,r=this,i=r.s,a=r.e;return null===a?i?(e="Infinity",0>i&&(e="-"+e)):e="NaN":(e=o(r.c),e=null!=t&&W(t,2,64,25,"base")?n(f(e,a),0|t,10,i):q>=a||a>=U?l(e,a):f(e,a),0>i&&r.c[0]&&(e="-"+e)),e},R.truncated=R.trunc=function(){return S(new e(this),this.e+1,1)},R.valueOf=R.toJSON=function(){return this.toString()},null!=t&&e.config(t),e}function i(t){var e=0|t;return t>0||t===e?e:e-1}function o(t){for(var e,n,r=1,i=t.length,o=t[0]+"";i>r;){for(e=t[r++]+"",n=I-e.length;n--;e="0"+e);o+=e}for(i=o.length;48===o.charCodeAt(--i););return o.slice(0,i+1||1)}function a(t,e){var n,r,i=t.c,o=e.c,a=t.s,s=e.s,u=t.e,c=e.e;if(!a||!s)return null;if(n=i&&!i[0],r=o&&!o[0],n||r)return n?r?0:-s:a;if(a!=s)return a;if(n=0>a,r=u==c,!i||!o)return r?0:!i^n?1:-1;if(!r)return u>c^n?1:-1;for(s=(u=i.length)<(c=o.length)?u:c,a=0;s>a;a++)if(i[a]!=o[a])return i[a]>o[a]^n?1:-1;return u==c?0:u>c^n?1:-1}function s(t,e,n){return(t=p(t))>=e&&n>=t}function u(t){return"[object Array]"==Object.prototype.toString.call(t)}function c(t,e,n){for(var r,i,o=[0],a=0,s=t.length;s>a;){for(i=o.length;i--;o[i]*=e);for(o[r=0]+=x.indexOf(t.charAt(a++));rn-1&&(null==o[r+1]&&(o[r+1]=0),o[r+1]+=o[r]/n|0,o[r]%=n)}return o.reverse()}function l(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(0>e?"e":"e+")+e}function f(t,e){var n,r;if(0>e){for(r="0.";++e;r+="0");t=r+t}else if(n=t.length,++e>n){for(r="0",e-=n;--e;r+="0");t+=r}else n>e&&(t=t.slice(0,e)+"."+t.slice(e));return t}function p(t){return t=parseFloat(t),0>t?y(t):v(t)}var h,m,d,g=/^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,y=Math.ceil,v=Math.floor,w=" not a boolean or binary digit",b="rounding mode",_="number type has more than 15 significant digits",x="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_",F=1e14,I=14,B=9007199254740991,N=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],O=1e7,A=1e9;if(h=r(),"function"==typeof define&&define.amd)define(function(){return h});else if("undefined"!=typeof e&&e.exports){if(e.exports=h,!m)try{m=t("crypto")}catch(k){}}else n.BigNumber=h}(this)},{crypto:31}],web3:[function(t,e,n){var r=t("./lib/web3");r.providers.HttpProvider=t("./lib/web3/httpprovider"),r.providers.QtSyncProvider=t("./lib/web3/qtsync"),r.eth.contract=t("./lib/web3/contract"),r.eth.namereg=t("./lib/web3/namereg"),r.eth.sendIBANTransaction=t("./lib/web3/transfer"),"undefined"!=typeof window&&"undefined"==typeof window.web3&&(window.web3=r),e.exports=r},{"./lib/web3":9,"./lib/web3/contract":11,"./lib/web3/httpprovider":19,"./lib/web3/namereg":23,"./lib/web3/qtsync":26,"./lib/web3/transfer":29}]},{},["web3"]); \ No newline at end of file diff --git a/libjsqrc/ethereumjs/gulpfile.js b/libjsqrc/ethereumjs/gulpfile.js index 360c5242b..558c1181a 100644 --- a/libjsqrc/ethereumjs/gulpfile.js +++ b/libjsqrc/ethereumjs/gulpfile.js @@ -73,7 +73,7 @@ gulp.task('buildLight', ['clean'], function () { .pipe(gulp.dest( DEST )); }); -gulp.task('buildStandalone', ['clean'], function () { +gulp.task('buildStandalone', [], function () { return browserify(browserifyOptions) .require('./' + src + '.js', {expose: 'web3'}) .require('bignumber.js') // expose it to dapp users diff --git a/libjsqrc/ethereumjs/lib/web3.js b/libjsqrc/ethereumjs/lib/web3.js index 982f75d9e..394ae450b 100644 --- a/libjsqrc/ethereumjs/lib/web3.js +++ b/libjsqrc/ethereumjs/lib/web3.js @@ -34,6 +34,7 @@ var Filter = require('./web3/filter'); var utils = require('./utils/utils'); var formatters = require('./web3/formatters'); var RequestManager = require('./web3/requestmanager'); +var Method = require('./web3/method'); var c = require('./utils/config'); var Property = require('./web3/property'); var Batch = require('./web3/batch'); @@ -161,11 +162,11 @@ setupMethods(web3.shh, shh.methods); web3.admin = {}; web3.admin.setSessionKey = function(s) { web3.admin.sessionKey = s; }; -var blockQueueStatus = new Method({ +var blockQueueStatus = new Property({ name: 'blockQueueStatus', call: 'admin_eth_blockQueueStatus', params: 1, - inputFormatter: [function() { return web3.sessionKey; }] + inputFormatter: [function() { return web3.admin.sessionKey; }] }); setupMethods(web3.admin, [blockQueueStatus]); diff --git a/libjsqrc/ethereumjs/lib/web3/property.js b/libjsqrc/ethereumjs/lib/web3/property.js index a2e7282e9..4b2b6c566 100644 --- a/libjsqrc/ethereumjs/lib/web3/property.js +++ b/libjsqrc/ethereumjs/lib/web3/property.js @@ -29,6 +29,7 @@ var Property = function (options) { this.setter = options.setter; this.outputFormatter = options.outputFormatter; this.inputFormatter = options.inputFormatter; + this.params = options.params; }; /** diff --git a/libweb3jsonrpc/WebThreeStubServer.h b/libweb3jsonrpc/WebThreeStubServer.h index 4a9dd1911..8bdeb6b7a 100644 --- a/libweb3jsonrpc/WebThreeStubServer.h +++ b/libweb3jsonrpc/WebThreeStubServer.h @@ -58,7 +58,7 @@ public: void addSession(std::string const& _session, SessionPermissions const& _p) { m_sessions[_session] = _p; } private: - bool isAdmin(std::string const& _session) const { auto it = m_sessions.find(_session); return it != m_sessions.end() && it->second.admin; } + bool isAdmin(std::string const& _session) const override { auto it = m_sessions.find(_session); return it != m_sessions.end() && it->second.admin; } virtual dev::eth::Interface* client() override; virtual std::shared_ptr face() override; diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index a0b0e1c89..f88443ee5 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -798,6 +798,83 @@ string WebThreeStubServerBase::eth_compileSerpent(string const& _source) return res; } +bool WebThreeStubServerBase::admin_web3_setVerbosity(int _v, string const& _session) +{ + if (!isAdmin(_session)) + return false; + g_logVerbosity = _v; + return true; +} + +bool WebThreeStubServerBase::admin_net_start(std::string const& _session) +{ + if (!isAdmin(_session)) + return false; + network()->startNetwork(); + return true; +} + +bool WebThreeStubServerBase::admin_net_stop(std::string const& _session) +{ + if (!isAdmin(_session)) + return false; + network()->stopNetwork(); + return true; +} + +bool WebThreeStubServerBase::admin_net_connect(std::string const& _node, std::string const& _session) +{ + if (!isAdmin(_session)) + return false; + p2p::NodeId id; + bi::tcp::endpoint ep; + if (_node.substr(0, 8) == "enode://" && _node.find('@') == 136) + { + id = p2p::NodeId(_node.substr(8, 128)); + ep = p2p::Network::resolveHost(_node.substr(137)); + } + else + ep = p2p::Network::resolveHost(_node); + network()->requirePeer(id, ep); + return true; +} + +Json::Value toJson(p2p::PeerSessionInfo const& _p) +{ + Json::Value ret; + ret["id"] = _p.id.hex(); + ret["clientVersion"] = _p.clientVersion; + ret["host"] = _p.host; + ret["port"] = _p.port; + ret["lastPing"] = (int)chrono::duration_cast(_p.lastPing).count(); + for (auto const& i: _p.notes) + ret["notes"][i.first] = i.second; + for (auto const& i: _p.caps) + ret["caps"][i.first] = (unsigned)i.second; + return ret; +} + +Json::Value WebThreeStubServerBase::admin_net_peers(std::string const& _session) +{ + if (!isAdmin(_session)) + return false; + Json::Value ret; + for (p2p::PeerSessionInfo const& i: network()->peers()) + ret.append(toJson(i)); + return ret; +} + +bool WebThreeStubServerBase::admin_eth_setMining(bool _on, std::string const& _session) +{ + if (!isAdmin(_session)) + return false; + if (_on) + client()->startMining(); + else + client()->stopMining(); + return true; +} + Json::Value WebThreeStubServerBase::eth_compileSolidity(string const& _source) { // TOOD throw here jsonrpc errors diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index 93a54cce7..aaf0a6096 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -137,12 +137,36 @@ public: virtual Json::Value shh_getFilterChanges(std::string const& _filterId); virtual Json::Value shh_getMessages(std::string const& _filterId); - virtual Json::Value admin_eth_blockQueueStatus(std::string const&) { return Json::Value(); } + virtual bool admin_web3_setVerbosity(int _v, std::string const& _session); + virtual bool admin_net_start(std::string const& _session); + virtual bool admin_net_stop(std::string const& _session); + virtual bool admin_net_connect(std::string const& _node, std::string const& _session); + virtual Json::Value admin_net_peers(std::string const& _session); + + virtual bool admin_eth_setMining(bool _on, std::string const& _session); + virtual Json::Value admin_eth_blockQueueStatus(std::string const& _session) { (void)_session; return Json::Value(); } + virtual bool admin_eth_setAskPrice(std::string const& _wei, std::string const& _session) { (void)_wei; (void)_session; return false; } + virtual bool admin_eth_setBidPrice(std::string const& _wei, std::string const& _session) { (void)_wei; (void)_session; return false; } + virtual bool admin_eth_setReferencePrice(std::string const& _wei, std::string const& _session) { (void)_wei; (void)_session; return false; } + virtual bool admin_eth_setPriority(int _percent, std::string const& _session) { (void)_percent; (void)_session; return false; } + virtual Json::Value admin_eth_findBlock(std::string const& _blockHash, std::string const& _session) { (void)_blockHash; (void)_session; return Json::Value(); } + virtual std::string admin_eth_blockQueueFirstUnknown(std::string const& _session) { (void)_session; return ""; } + virtual bool admin_eth_blockQueueRetryUnknown(std::string const& _session) { (void)_session; return false; } + virtual Json::Value admin_eth_allAccounts(std::string const& _session) { (void)_session; return Json::Value(); } + virtual Json::Value admin_eth_newAccount(const Json::Value& _info, std::string const& _session) { (void)_info; (void)_session; return Json::Value(); } + virtual bool admin_eth_setSigningKey(std::string const& _uuidOrAddress, std::string const& _session) { (void)_uuidOrAddress; (void)_session; return false; } + virtual bool admin_eth_setMiningBenefactor(std::string const& _uuidOrAddress, std::string const& _session) { (void)_uuidOrAddress; (void)_session; return false; } + virtual Json::Value admin_eth_inspect(std::string const& _address, std::string const& _session) { (void)_address; (void)_session; return Json::Value(); } + virtual Json::Value admin_eth_reprocess(std::string const& _blockNumberOrHash, std::string const& _session) { (void)_blockNumberOrHash; (void)_session; return Json::Value(); } + virtual Json::Value admin_eth_vmTrace(std::string const& _blockNumberOrHash, std::string const& _txIndex, std::string const& _session) { (void)_blockNumberOrHash; (void)_txIndex; (void)_session; return Json::Value(); } + virtual Json::Value admin_eth_getReceiptByHashAndIndex(std::string const& _blockNumberOrHash, std::string const& _txIndex, std::string const& _session) { (void)_blockNumberOrHash; (void)_txIndex; (void)_session; return Json::Value(); } void setIdentities(std::vector const& _ids); std::map const& ids() const { return m_shhIds; } protected: + virtual bool isAdmin(std::string const& _session) const { (void)_session; return false; } + virtual dev::eth::Interface* client() = 0; virtual std::shared_ptr face() = 0; virtual dev::WebThreeNetworkFace* network() = 0; diff --git a/libweb3jsonrpc/abstractwebthreestubserver.h b/libweb3jsonrpc/abstractwebthreestubserver.h index 675a03614..6b94c31da 100644 --- a/libweb3jsonrpc/abstractwebthreestubserver.h +++ b/libweb3jsonrpc/abstractwebthreestubserver.h @@ -76,7 +76,28 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerbindAndAddMethod(jsonrpc::Procedure("shh_uninstallFilter", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_uninstallFilterI); this->bindAndAddMethod(jsonrpc::Procedure("shh_getFilterChanges", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_getFilterChangesI); this->bindAndAddMethod(jsonrpc::Procedure("shh_getMessages", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::shh_getMessagesI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_web3_setVerbosity", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_web3_setVerbosityI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_net_start", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_net_startI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_net_stop", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_net_stopI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_net_connect", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_net_connectI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_net_peers", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_net_peersI); this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_blockQueueStatus", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_blockQueueStatusI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_setAskPrice", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setAskPriceI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_setBidPrice", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setBidPriceI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_setReferencePrice", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setReferencePriceI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_setPriority", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setPriorityI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_setMining", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_BOOLEAN,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setMiningI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_findBlock", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_findBlockI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_blockQueueFirstUnknown", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_blockQueueFirstUnknownI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_blockQueueRetryUnknown", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_blockQueueRetryUnknownI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_allAccounts", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_allAccountsI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_newAccount", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_OBJECT,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_newAccountI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_setSigningKey", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setSigningKeyI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_setMiningBenefactor", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setMiningBenefactorI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_inspect", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_inspectI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_reprocess", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_reprocessI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_vmTrace", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_vmTraceI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_getReceiptByHashAndIndex", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_getReceiptByHashAndIndexI); } inline virtual void web3_sha3I(const Json::Value &request, Json::Value &response) @@ -352,10 +373,94 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServershh_getMessages(request[0u].asString()); } + inline virtual void admin_web3_setVerbosityI(const Json::Value &request, Json::Value &response) + { + response = this->admin_web3_setVerbosity(request[0u].asInt(), request[1u].asString()); + } + inline virtual void admin_net_startI(const Json::Value &request, Json::Value &response) + { + response = this->admin_net_start(request[0u].asString()); + } + inline virtual void admin_net_stopI(const Json::Value &request, Json::Value &response) + { + response = this->admin_net_stop(request[0u].asString()); + } + inline virtual void admin_net_connectI(const Json::Value &request, Json::Value &response) + { + response = this->admin_net_connect(request[0u].asString(), request[1u].asString()); + } + inline virtual void admin_net_peersI(const Json::Value &request, Json::Value &response) + { + response = this->admin_net_peers(request[0u].asString()); + } inline virtual void admin_eth_blockQueueStatusI(const Json::Value &request, Json::Value &response) { response = this->admin_eth_blockQueueStatus(request[0u].asString()); } + inline virtual void admin_eth_setAskPriceI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_setAskPrice(request[0u].asString(), request[1u].asString()); + } + inline virtual void admin_eth_setBidPriceI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_setBidPrice(request[0u].asString(), request[1u].asString()); + } + inline virtual void admin_eth_setReferencePriceI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_setReferencePrice(request[0u].asString(), request[1u].asString()); + } + inline virtual void admin_eth_setPriorityI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_setPriority(request[0u].asInt(), request[1u].asString()); + } + inline virtual void admin_eth_setMiningI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_setMining(request[0u].asBool(), request[1u].asString()); + } + inline virtual void admin_eth_findBlockI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_findBlock(request[0u].asString(), request[1u].asString()); + } + inline virtual void admin_eth_blockQueueFirstUnknownI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_blockQueueFirstUnknown(request[0u].asString()); + } + inline virtual void admin_eth_blockQueueRetryUnknownI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_blockQueueRetryUnknown(request[0u].asString()); + } + inline virtual void admin_eth_allAccountsI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_allAccounts(request[0u].asString()); + } + inline virtual void admin_eth_newAccountI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_newAccount(request[0u], request[1u].asString()); + } + inline virtual void admin_eth_setSigningKeyI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_setSigningKey(request[0u].asString(), request[1u].asString()); + } + inline virtual void admin_eth_setMiningBenefactorI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_setMiningBenefactor(request[0u].asString(), request[1u].asString()); + } + inline virtual void admin_eth_inspectI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_inspect(request[0u].asString(), request[1u].asString()); + } + inline virtual void admin_eth_reprocessI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_reprocess(request[0u].asString(), request[1u].asString()); + } + inline virtual void admin_eth_vmTraceI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_vmTrace(request[0u].asString(), request[1u].asString(), request[2u].asString()); + } + inline virtual void admin_eth_getReceiptByHashAndIndexI(const Json::Value &request, Json::Value &response) + { + response = this->admin_eth_getReceiptByHashAndIndex(request[0u].asString(), request[1u].asString(), request[2u].asString()); + } virtual std::string web3_sha3(const std::string& param1) = 0; virtual std::string web3_clientVersion() = 0; virtual std::string net_version() = 0; @@ -420,7 +525,28 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerCallMethod("admin_web3_setVerbosity",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + bool admin_net_start(const std::string& param1) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + Json::Value result = this->CallMethod("admin_net_start",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + bool admin_net_stop(const std::string& param1) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + Json::Value result = this->CallMethod("admin_net_stop",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + bool admin_net_connect(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + Json::Value result = this->CallMethod("admin_net_connect",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + Json::Value admin_net_peers(const std::string& param1) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + Json::Value result = this->CallMethod("admin_net_peers",p); + if (result.isArray()) + return result; + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } Json::Value admin_eth_blockQueueStatus(const std::string& param1) throw (jsonrpc::JsonRpcException) { Json::Value p; @@ -681,6 +733,181 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } + bool admin_eth_setAskPrice(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + Json::Value result = this->CallMethod("admin_eth_setAskPrice",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + bool admin_eth_setBidPrice(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + Json::Value result = this->CallMethod("admin_eth_setBidPrice",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + bool admin_eth_setReferencePrice(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + Json::Value result = this->CallMethod("admin_eth_setReferencePrice",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + bool admin_eth_setPriority(int param1, const std::string& param2) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + Json::Value result = this->CallMethod("admin_eth_setPriority",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + bool admin_eth_setMining(bool param1, const std::string& param2) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + Json::Value result = this->CallMethod("admin_eth_setMining",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + Json::Value admin_eth_findBlock(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + Json::Value result = this->CallMethod("admin_eth_findBlock",p); + if (result.isObject()) + return result; + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + std::string admin_eth_blockQueueFirstUnknown(const std::string& param1) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + Json::Value result = this->CallMethod("admin_eth_blockQueueFirstUnknown",p); + if (result.isString()) + return result.asString(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + bool admin_eth_blockQueueRetryUnknown(const std::string& param1) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + Json::Value result = this->CallMethod("admin_eth_blockQueueRetryUnknown",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + Json::Value admin_eth_allAccounts(const std::string& param1) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + Json::Value result = this->CallMethod("admin_eth_allAccounts",p); + if (result.isArray()) + return result; + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + Json::Value admin_eth_newAccount(const Json::Value& param1, const std::string& param2) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + Json::Value result = this->CallMethod("admin_eth_newAccount",p); + if (result.isObject()) + return result; + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + bool admin_eth_setSigningKey(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + Json::Value result = this->CallMethod("admin_eth_setSigningKey",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + bool admin_eth_setMiningBenefactor(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + Json::Value result = this->CallMethod("admin_eth_setMiningBenefactor",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + Json::Value admin_eth_inspect(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + Json::Value result = this->CallMethod("admin_eth_inspect",p); + if (result.isObject()) + return result; + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + Json::Value admin_eth_reprocess(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + Json::Value result = this->CallMethod("admin_eth_reprocess",p); + if (result.isObject()) + return result; + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + Json::Value admin_eth_vmTrace(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + p.append(param3); + Json::Value result = this->CallMethod("admin_eth_vmTrace",p); + if (result.isObject()) + return result; + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + Json::Value admin_eth_getReceiptByHashAndIndex(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + p.append(param3); + Json::Value result = this->CallMethod("admin_eth_getReceiptByHashAndIndex",p); + if (result.isObject()) + return result; + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } }; #endif //JSONRPC_CPP_STUB_WEBTHREESTUBCLIENT_H_ From 7d55e9123ef44d8c7108e04f541ba41703159dfd Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 13 Jun 2015 18:11:16 +0800 Subject: [PATCH 45/69] Show import timing. --- libethereum/Client.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 657e25772..f24c88e0e 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -622,6 +622,9 @@ void Client::syncBlockQueue() boost::timer t; tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, m_syncAmount); double elapsed = t.elapsed(); + + cnote << m_syncAmount << "blocks imported in" << unsigned(elapsed * 1000) << "ms (" << (m_syncAmount / elapsed) << "blocks/s)"; + if (elapsed > 0.55 && m_syncAmount > c_syncMin) m_syncAmount = max(c_syncMin, m_syncAmount * 9 / 10); else if (elapsed < 0.45 && m_syncAmount < c_syncMax) From 425c9aaeeb60a315cbb0c13e77151d4f9a8ce799 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 14 Jun 2015 11:49:26 +0800 Subject: [PATCH 46/69] Use relative bounds to target. --- libethereum/Client.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index f24c88e0e..31c2e8026 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -613,7 +613,7 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution) unsigned static const c_syncMin = 1; unsigned static const c_syncMax = 100; -double static const c_targetDuration = 0.5; +double static const c_targetDuration = 1; void Client::syncBlockQueue() { @@ -625,9 +625,9 @@ void Client::syncBlockQueue() cnote << m_syncAmount << "blocks imported in" << unsigned(elapsed * 1000) << "ms (" << (m_syncAmount / elapsed) << "blocks/s)"; - if (elapsed > 0.55 && m_syncAmount > c_syncMin) + if (elapsed > c_targetDuration * 1.1 && m_syncAmount > c_syncMin) m_syncAmount = max(c_syncMin, m_syncAmount * 9 / 10); - else if (elapsed < 0.45 && m_syncAmount < c_syncMax) + else if (elapsed < c_targetDuration * 0.9 && m_syncAmount < c_syncMax) m_syncAmount = min(c_syncMax, m_syncAmount * 11 / 10 + 1); if (ir.first.empty()) return; From 0426393557eaa793fe5dfc5c6b9a7d60c5f58a5b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 14 Jun 2015 12:37:29 +0800 Subject: [PATCH 47/69] Include hash in client ID. --- eth/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/main.cpp b/eth/main.cpp index 259a993c9..4b23ef62a 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -685,7 +685,7 @@ int main(int argc, char** argv) VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); - std::string clientImplString = "++eth/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); + std::string clientImplString = "++eth/" + clientName + "v" + dev::Version + "-" + string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) + (ETH_CLEAN_REPO ? "" : "*") + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); dev::WebThreeDirect web3( clientImplString, dbPath, From fe011b3563af02f52ef83a1522935fb48eda2b69 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 14 Jun 2015 12:38:49 +0800 Subject: [PATCH 48/69] Avoid building neth unless made explicit, at least until build works again. --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a2ac51a5..fd3188eae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ option(ETHKEY "Build the CLI key manager component" ON) option(SOLIDITY "Build the Solidity language components" ON) option(SERPENT "Build the Serpent language components" ON) option(TOOLS "Build the tools components" ON) -option(NCURSES "Build the NCurses components" ON) +option(NCURSES "Build the NCurses components" OFF) option(GUI "Build GUI components (AlethZero, Mix)" ON) option(TESTS "Build the tests." ON) option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF) @@ -222,7 +222,7 @@ elseif (BUNDLE STREQUAL "full") set(SOLIDITY ON) set(USENPM ON) set(GUI ON) - set(NCURSES ${DECENT_PLATFORM}) +# set(NCURSES ${DECENT_PLATFORM}) set(TOOLS ON) set(TESTS ON) set(FATDB ON) @@ -249,7 +249,7 @@ elseif (BUNDLE STREQUAL "user") set(SOLIDITY OFF) set(USENPM OFF) set(GUI ON) - set(NCURSES ${DECENT_PLATFORM}) +# set(NCURSES ${DECENT_PLATFORM}) set(TOOLS ON) set(TESTS OFF) elseif (BUNDLE STREQUAL "wallet") From 9570c72e379a8620fddc59317d6b2137ce73f42d Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Sun, 14 Jun 2015 07:36:34 +0200 Subject: [PATCH 49/69] new osx dragndrop installer --- CMakeLists.txt | 2 +- appdmg.json.in | 8 ++++---- bg.png | Bin 175517 -> 0 bytes install-folder-bg.png | Bin 0 -> 14542 bytes install-folder-bg@2x.png | Bin 0 -> 35073 bytes 5 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 bg.png create mode 100644 install-folder-bg.png create mode 100644 install-folder-bg@2x.png diff --git a/CMakeLists.txt b/CMakeLists.txt index fd3188eae..71d12353c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -458,7 +458,7 @@ if (APPLE AND GUI) -DAPP_DMG_EXE=${ETH_APP_DMG} -DAPP_DMG_FILE=appdmg.json.in -DAPP_DMG_ICON="alethzero/alethzero.icns" - -DAPP_DMG_BACKGROUND="bg.png" + -DAPP_DMG_BACKGROUND="install-folder-bg.png" -DETH_BUILD_DIR="${CMAKE_BINARY_DIR}" -DETH_MIX_APP="$" -DETH_ALETHZERO_APP="$" diff --git a/appdmg.json.in b/appdmg.json.in index 4fb9a6e33..ea49e9294 100644 --- a/appdmg.json.in +++ b/appdmg.json.in @@ -2,11 +2,11 @@ "title": "Ethereum", "icon": "appdmg_icon.icns", "background": "appdmg_background.png", - "icon-size": 80, + "icon-size": 55, "contents": [ - { "x": 600, "y": 170, "type": "link", "path": "/Applications" }, - { "x": 150, "y": 90, "type": "file", "path": "${ETH_ALETHZERO_APP}" }, - { "x": 150, "y": 260, "type": "file", "path": "${ETH_MIX_APP}" } + { "x": 242, "y": 240, "type": "link", "path": "/Applications" }, + { "x": 145, "y": 125, "type": "file", "path": "${ETH_ALETHZERO_APP}" }, + { "x": 339, "y": 125, "type": "file", "path": "${ETH_MIX_APP}" } ] } diff --git a/bg.png b/bg.png deleted file mode 100644 index 869290c2b861dd148e1ae500770f3bc47879761e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 175517 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
-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�I*sMH@QHEDf(ql`);jlqV0D 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 literal 0 HcmV?d00001 diff --git a/install-folder-bg@2x.png b/install-folder-bg@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..31aac5522ee552c079977c054adaa41f8b09886f GIT binary patch literal 35073 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 literal 0 HcmV?d00001 From 30e47e38cb98e309870bb166b56d6cf6826c91f4 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 14 Jun 2015 04:05:53 -0400 Subject: [PATCH 50/69] Slightly more aggressive 'require peer', required-peer pinning, and option to disable discovery. --- eth/main.cpp | 17 +++++++++++++++++ libp2p/Host.cpp | 7 ++++--- libp2p/Network.h | 2 ++ libp2p/NodeTable.cpp | 12 ++++++++---- libp2p/NodeTable.h | 4 +++- libp2p/Peer.cpp | 2 ++ 6 files changed, 36 insertions(+), 8 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index 4b23ef62a..bc143154e 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -168,6 +168,9 @@ void help() << " --port Connect to remote port (default: 30303)." << endl << " --network-id Only connect to other hosts with this network id (default:0)." << endl << " --upnp Use UPnP for NAT (default: on)." << endl + << " --no-discovery Disable Node discovery. (experimental)" << endl + << " --pin Only connect to required (trusted) peers. (experimental)" << endl +// << " --require-peers List of required (trusted) peers. (experimental)" << endl << endl; MinerCLI::streamHelp(cout); cout @@ -304,6 +307,8 @@ int main(int argc, char** argv) unsigned short remotePort = 30303; unsigned peers = 11; bool bootstrap = false; + bool disableDiscovery = false; + bool pinning = false; unsigned networkId = 0; /// Mining params @@ -592,6 +597,16 @@ int main(int argc, char** argv) } else if (arg == "-b" || arg == "--bootstrap") bootstrap = true; + else if (arg == "--no-discovery") + if (bootstrap) + { + cerr << "-b/--bootstrap cannot be used with --no-discovery." << endl; + return -1; + } + else + disableDiscovery = true; + else if (arg == "--pin") + pinning = true; else if (arg == "-f" || arg == "--force-mining") forceMining = true; else if (arg == "-i" || arg == "--interactive") @@ -684,6 +699,8 @@ int main(int argc, char** argv) StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); + netPrefs.discovery = !disableDiscovery; + netPrefs.pin = pinning; auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); std::string clientImplString = "++eth/" + clientName + "v" + dev::Version + "-" + string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) + (ETH_CLEAN_REPO ? "" : "*") + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); dev::WebThreeDirect web3( diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 55389ed1b..b0d246458 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -307,7 +307,8 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) { clog(NetP2PNote) << "p2p.host.nodeTable.events.NodeEntryDropped " << _n; RecursiveGuard l(x_sessions); - m_peers.erase(_n); + if (m_peers.count(_n) && !m_peers[_n]->required) + m_peers.erase(_n); } } @@ -628,7 +629,7 @@ void Host::run(boost::system::error_code const&) { RecursiveGuard l(x_sessions); for (auto p: m_peers) - if (p.second->shouldReconnect() && !havePeerSession(p.second->id)) + if (p.second->shouldReconnect() && !havePeerSession(p.second->id) && (!m_netPrefs.pin || p.second->required)) toConnect.push_back(p.second); } @@ -679,7 +680,7 @@ void Host::startedWorking() else clog(NetP2PNote) << "p2p.start.notice id:" << id() << "TCP Listen port is invalid or unavailable."; - shared_ptr nodeTable(new NodeTable(m_ioService, m_alias, NodeIPEndpoint(bi::address::from_string(listenAddress()), listenPort(), listenPort()))); + shared_ptr nodeTable(new NodeTable(m_ioService, m_alias, NodeIPEndpoint(bi::address::from_string(listenAddress()), listenPort(), listenPort()), m_netPrefs.discovery)); nodeTable->setEventHandler(new HostNodeTableHandler(*this)); m_nodeTable = nodeTable; restoreNetwork(&m_restoreNetwork); diff --git a/libp2p/Network.h b/libp2p/Network.h index d02ce3cbe..97c631a7c 100644 --- a/libp2p/Network.h +++ b/libp2p/Network.h @@ -51,6 +51,8 @@ struct NetworkPreferences std::string publicIPAddress; std::string listenIPAddress; unsigned short listenPort = 30303; + bool discovery = true; // Discovery is activated with network. + bool pin = false; // Only connect to trusted ("required") peers. bool traverseNAT = true; }; diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 6344dc263..491795274 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -40,14 +40,15 @@ const char* NodeTableIngress::name() { return "<connect(); - doRefreshBuckets(boost::system::error_code()); + if (!m_disabled) + { + m_socketPointer->connect(); + doRefreshBuckets(boost::system::error_code()); + } } NodeTable::~NodeTable() diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 0ec13c828..bca14e5d4 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -130,7 +130,7 @@ public: enum NodeRelation { Unknown = 0, Known }; /// Constructor requiring host for I/O, credentials, and IP Address and port to listen on. - NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint); + NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint, bool _disabled = false); ~NodeTable(); /// Returns distance based on xor metric two node ids. Used by NodeEntry and NodeTable. @@ -271,6 +271,8 @@ private: boost::asio::deadline_timer m_bucketRefreshTimer; ///< Timer which schedules and enacts bucket refresh. boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions. + + bool m_disabled; ///< Disable discovery. }; inline std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable) diff --git a/libp2p/Peer.cpp b/libp2p/Peer.cpp index 6f368a4b4..a8b4a993d 100644 --- a/libp2p/Peer.cpp +++ b/libp2p/Peer.cpp @@ -38,6 +38,8 @@ bool Peer::shouldReconnect() const unsigned Peer::fallbackSeconds() const { + if (required) + return 5; switch (m_lastDisconnect) { case BadProtocol: From 0a0d36382b8e1d5fdfccfeac6b997571a8833603 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 14 Jun 2015 04:50:18 -0400 Subject: [PATCH 51/69] Fix bool. --- eth/main.cpp | 8 +------- libp2p/Network.h | 2 +- libp2p/NodeTable.cpp | 4 ++-- libp2p/NodeTable.h | 2 +- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index bc143154e..e914bfc31 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -598,13 +598,7 @@ int main(int argc, char** argv) else if (arg == "-b" || arg == "--bootstrap") bootstrap = true; else if (arg == "--no-discovery") - if (bootstrap) - { - cerr << "-b/--bootstrap cannot be used with --no-discovery." << endl; - return -1; - } - else - disableDiscovery = true; + disableDiscovery = true; else if (arg == "--pin") pinning = true; else if (arg == "-f" || arg == "--force-mining") diff --git a/libp2p/Network.h b/libp2p/Network.h index 97c631a7c..e70dd89ea 100644 --- a/libp2p/Network.h +++ b/libp2p/Network.h @@ -51,9 +51,9 @@ struct NetworkPreferences std::string publicIPAddress; std::string listenIPAddress; unsigned short listenPort = 30303; + bool traverseNAT = true; bool discovery = true; // Discovery is activated with network. bool pin = false; // Only connect to trusted ("required") peers. - bool traverseNAT = true; }; /** diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 491795274..1a0b11734 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -40,7 +40,7 @@ const char* NodeTableIngress::name() { return "< Date: Sun, 14 Jun 2015 06:30:20 -0400 Subject: [PATCH 52/69] ensure required peers --- libp2p/Host.cpp | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index b0d246458..596182413 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -619,26 +619,36 @@ void Host::run(boost::system::error_code const&) // updated. // disconnectLatePeers(); // todo: update peerSlotsAvailable() + + list> toConnect; + unsigned reqConn = 0; + { + RecursiveGuard l(x_sessions); + for (auto const& p: m_peers) + { + bool haveSession = havePeerSession(p.second->id); + bool required = p.second->required; + if (haveSession && required) + reqConn++; + else if (!haveSession && p.second->shouldReconnect() && (!m_netPrefs.pin || required)) + toConnect.push_back(p.second); + } + } + + for (auto p: toConnect) + if (p->required && reqConn++ < m_idealPeerCount) + connect(p); + unsigned pendingCount = 0; DEV_GUARDED(x_pendingNodeConns) pendingCount = m_pendingPeerConns.size(); - int openSlots = m_idealPeerCount - peerCount() - pendingCount; - if (openSlots > 0) + int openSlots = m_idealPeerCount - peerCount() - pendingCount + reqConn; + if (openSlots > 0 && !m_netPrefs.pin) { - list> toConnect; - { - RecursiveGuard l(x_sessions); - for (auto p: m_peers) - if (p.second->shouldReconnect() && !havePeerSession(p.second->id) && (!m_netPrefs.pin || p.second->required)) - toConnect.push_back(p.second); - } - for (auto p: toConnect) - if (openSlots--) + if (!p->required && openSlots--) connect(p); - else - break; - + m_nodeTable->discover(); } From 826858496c7847ab97ceeccf0ca4b2296df3f9f6 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 14 Jun 2015 06:42:43 -0400 Subject: [PATCH 53/69] cleanup --- libp2p/Host.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 596182413..dd1185c24 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -639,17 +639,20 @@ void Host::run(boost::system::error_code const&) if (p->required && reqConn++ < m_idealPeerCount) connect(p); - unsigned pendingCount = 0; - DEV_GUARDED(x_pendingNodeConns) - pendingCount = m_pendingPeerConns.size(); - int openSlots = m_idealPeerCount - peerCount() - pendingCount + reqConn; - if (openSlots > 0 && !m_netPrefs.pin) + if (!m_netPrefs.pin) { - for (auto p: toConnect) - if (!p->required && openSlots--) - connect(p); - - m_nodeTable->discover(); + unsigned pendingCount = 0; + DEV_GUARDED(x_pendingNodeConns) + pendingCount = m_pendingPeerConns.size(); + int openSlots = m_idealPeerCount - peerCount() - pendingCount + reqConn; + if (openSlots > 0) + { + for (auto p: toConnect) + if (!p->required && openSlots--) + connect(p); + + m_nodeTable->discover(); + } } auto runcb = [this](boost::system::error_code const& error) { run(error); }; From e923f9c4350bed0cba13286a01084fdb21cb16cc Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Mon, 15 Jun 2015 09:42:58 +0200 Subject: [PATCH 54/69] add balanceatblock and storageatblock cli interactive commands# --- eth/main.cpp | 55 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index 5d70b3fe5..0c701d615 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -102,6 +102,9 @@ void interactiveHelp() << " listaccounts List the accounts on the network." << endl << " listcontracts List the contracts on the network." << endl << " balanceat
Gives the balance of the given account." << endl + << " balanceatblock
Gives the balance of the given account." << endl + << " storageat
Gives the storage of the given account." << endl + << " storageatblock
Gives the storahe of the given account at a given blocknumber." << endl << " codeat
Gives the code of the given account." << endl #endif << " setsigningkey Set the address with which to sign transactions." << endl @@ -1338,19 +1341,48 @@ int main(int argc, char** argv) cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address)) << endl; } } - // TODO implement << operator for std::unorderd_map -// else if (c && cmd == "storageat") -// { -// if (iss.peek() != -1) -// { -// string stringHash; -// iss >> stringHash; + else if (c && cmd == "balanceatblock") + { + if (iss.peek() != -1) + { + string stringHash; + unsigned blocknumber; + iss >> stringHash >> blocknumber; -// Address address = h160(fromHex(stringHash)); + Address address = h160(fromHex(stringHash)); + + cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address, blocknumber)) << endl; + } + } + else if (c && cmd == "storageat") + { + if (iss.peek() != -1) + { + string stringHash; + iss >> stringHash; -// cout << "storage at " << stringHash << " is: " << c->storageAt(address) << endl; -// } -// } + Address address = h160(fromHex(stringHash)); + + cout << "storage at " << stringHash << " is: " << endl; + for (auto s: c->storageAt(address)) + cout << toHex(s.first) << " : " << toHex(s.second) << endl; + } + } + else if (c && cmd == "storageatblock") + { + if (iss.peek() != -1) + { + string stringHash; + unsigned blocknumber; + iss >> stringHash >> blocknumber; + + Address address = h160(fromHex(stringHash)); + + cout << "storage at " << stringHash << " is: " << endl; + for (auto s: c->storageAt(address, blocknumber)) + cout << "\"0x" << toHex(s.first) << "\" : \"0x" << toHex(s.second) << "\"," << endl; + } + } else if (c && cmd == "codeat") { if (iss.peek() != -1) @@ -1364,6 +1396,7 @@ int main(int argc, char** argv) } } #endif + else if (c && cmd == "send") { if (iss.peek() != -1) From 822103e6020d3cda16405bd25c9ccba879456d3e Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 15 Jun 2015 11:36:55 +0200 Subject: [PATCH 55/69] ET won't be sad anymore --- ethminer/MinerAux.h | 1 - 1 file changed, 1 deletion(-) diff --git a/ethminer/MinerAux.h b/ethminer/MinerAux.h index 6de3913e4..3351b90de 100644 --- a/ethminer/MinerAux.h +++ b/ethminer/MinerAux.h @@ -410,7 +410,6 @@ private: } catch (...) { - cout << "Error phoning home. ET is sad." << endl; } } #endif From c1826e463412eba285157e65dcf8d9a33e38f9c2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 15 Jun 2015 12:25:18 +0200 Subject: [PATCH 56/69] Upgrade "BadJumpDestination" to semi-recoverable "OutOfGas" exception. --- mix/MixClient.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index e2e554cb9..85f7f0a46 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -218,6 +218,8 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas")); case TransactionException::BlockGasLimitReached: BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Block gas limit reached")); + case TransactionException::BadJumpDestination: + BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Solidity exception (bad jump)")); case TransactionException::OutOfStack: BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Out of stack")); case TransactionException::StackUnderflow: @@ -225,7 +227,6 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c //these should not happen in mix case TransactionException::Unknown: case TransactionException::BadInstruction: - case TransactionException::BadJumpDestination: case TransactionException::InvalidSignature: case TransactionException::InvalidNonce: case TransactionException::BadRLP: From 9afd426e5a08c7f86dad23d95175c45d07e3c353 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 15 Jun 2015 19:14:45 +0800 Subject: [PATCH 57/69] Refactor toJson out into separate file. Implement basic admin_eth_* interfaces. --- alethzero/MainWin.cpp | 10 +- alethzero/OurWebThreeStubServer.cpp | 2 +- alethzero/OurWebThreeStubServer.h | 2 +- eth/main.cpp | 23 +- libethereum/Client.cpp | 34 +- libethereum/Client.h | 4 +- libethereum/EthereumHost.cpp | 2 +- libethereum/EthereumPeer.cpp | 10 +- libethereum/EthereumPeer.h | 3 + libethereum/Executive.h | 2 + libethereum/State.cpp | 22 +- libethereum/State.h | 12 +- libevm/ExtVMFace.h | 27 +- libp2p/Host.cpp | 19 + libp2p/Host.h | 3 + libtestutils/FixedClient.cpp | 4 +- libweb3jsonrpc/JsonHelper.cpp | 427 ++++++++++++++++++++ libweb3jsonrpc/JsonHelper.h | 104 +++++ libweb3jsonrpc/WebThreeStubServer.cpp | 242 ++++++++++- libweb3jsonrpc/WebThreeStubServer.h | 37 +- libweb3jsonrpc/WebThreeStubServerBase.cpp | 378 +---------------- libweb3jsonrpc/WebThreeStubServerBase.h | 36 +- libweb3jsonrpc/abstractwebthreestubserver.h | 12 +- libweb3jsonrpc/spec.json | 4 +- libwebthree/WebThree.cpp | 11 + libwebthree/WebThree.h | 2 + mix/MixClient.cpp | 4 +- neth/main.cpp | 3 +- test/libweb3jsonrpc/webthreestubclient.h | 4 +- 29 files changed, 993 insertions(+), 450 deletions(-) create mode 100644 libweb3jsonrpc/JsonHelper.cpp create mode 100644 libweb3jsonrpc/JsonHelper.h diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index e88086e0d..848020cbc 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -212,7 +212,7 @@ Main::Main(QWidget *parent) : m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads)); auto w3ss = new OurWebThreeStubServer(*m_httpConnector, this); m_server.reset(w3ss); - auto sessionKey = w3ss->newSession({true}); + auto sessionKey = w3ss->newSession(SessionPermissions{{Priviledge::Admin}}); connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString))); m_server->setIdentities(keysAsVector(owned())); m_server->StartListening(); @@ -1922,7 +1922,7 @@ void Main::on_clearPending_triggered() void Main::on_retryUnknown_triggered() { - ethereum()->retryUnkonwn(); + ethereum()->retryUnknown(); } void Main::on_killBlockchain_triggered() @@ -1941,11 +1941,7 @@ void Main::on_net_triggered() { ui->port->setEnabled(!ui->net->isChecked()); ui->clientName->setEnabled(!ui->net->isChecked()); - string n = string("AlethZero/v") + dev::Version; - if (ui->clientName->text().size()) - n += "/" + ui->clientName->text().toStdString(); - n += "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM); - web3()->setClientVersion(n); + web3()->setClientVersion(WebThreeDirect::composeClientVersion("AlethZero", ui->clientName->text().toStdString())); if (ui->net->isChecked()) { web3()->setIdealPeerCount(ui->idealPeers->value()); diff --git a/alethzero/OurWebThreeStubServer.cpp b/alethzero/OurWebThreeStubServer.cpp index 39ab69a19..e18cb55d5 100644 --- a/alethzero/OurWebThreeStubServer.cpp +++ b/alethzero/OurWebThreeStubServer.cpp @@ -33,7 +33,7 @@ OurWebThreeStubServer::OurWebThreeStubServer( jsonrpc::AbstractServerConnector& _conn, Main* _main ): - WebThreeStubServer(_conn, *_main->web3(), make_shared(_main), _main->owned().toVector().toStdVector(), _main->keyManager()), + WebThreeStubServer(_conn, *_main->web3(), make_shared(_main), _main->owned().toVector().toStdVector(), _main->keyManager(), *static_cast(_main->ethereum()->gasPricer().get())), m_main(_main) { } diff --git a/alethzero/OurWebThreeStubServer.h b/alethzero/OurWebThreeStubServer.h index 43985b640..cc950027e 100644 --- a/alethzero/OurWebThreeStubServer.h +++ b/alethzero/OurWebThreeStubServer.h @@ -59,7 +59,7 @@ private: Main* m_main; }; -class OurWebThreeStubServer: public QObject, public WebThreeStubServer +class OurWebThreeStubServer: public QObject, public dev::WebThreeStubServer { Q_OBJECT diff --git a/eth/main.cpp b/eth/main.cpp index 4b23ef62a..382858ae7 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -685,9 +685,8 @@ int main(int argc, char** argv) VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); - std::string clientImplString = "++eth/" + clientName + "v" + dev::Version + "-" + string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) + (ETH_CLEAN_REPO ? "" : "*") + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); dev::WebThreeDirect web3( - clientImplString, + WebThreeDirect::composeClientVersion("++eth", clientName), dbPath, killChain, nodeMode == NodeMode::Full ? set{"eth"/*, "shh"*/} : set(), @@ -800,7 +799,7 @@ int main(int argc, char** argv) // std::shared_ptr gasPricer = make_shared(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000)); std::shared_ptr gasPricer = make_shared(askPrice, bidPrice); eth::Client* c = nodeMode == NodeMode::Full ? web3.ethereum() : nullptr; - StructuredLogger::starting(clientImplString, dev::Version); + StructuredLogger::starting(WebThreeDirect::composeClientVersion("++eth", clientName), dev::Version); if (c) { c->setGasPricer(gasPricer); @@ -827,12 +826,12 @@ int main(int argc, char** argv) if (jsonrpc > -1) { jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector(), keyManager)); + jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector(), keyManager, *gasPricer)); jsonrpcServer->StartListening(); if (jsonAdmin.empty()) - jsonAdmin = jsonrpcServer->newSession(SessionPermissions{true}); + jsonAdmin = jsonrpcServer->newSession(SessionPermissions{{Priviledge::Admin}}); else - jsonrpcServer->addSession(jsonAdmin, SessionPermissions{true}); + jsonrpcServer->addSession(jsonAdmin, SessionPermissions{{Priviledge::Admin}}); cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl; } #endif @@ -982,12 +981,12 @@ int main(int argc, char** argv) if (jsonrpc < 0) jsonrpc = SensibleHttpPort; jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector(), keyManager)); + jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector(), keyManager, *gasPricer)); jsonrpcServer->StartListening(); if (jsonAdmin.empty()) - jsonAdmin = jsonrpcServer->newSession(SessionPermissions{true}); + jsonAdmin = jsonrpcServer->newSession(SessionPermissions{{Priviledge::Admin}}); else - jsonrpcServer->addSession(jsonAdmin, SessionPermissions{true}); + jsonrpcServer->addSession(jsonAdmin, SessionPermissions{{Priviledge::Admin}}); cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl; } else if (cmd == "jsonstop") @@ -1083,7 +1082,7 @@ int main(int argc, char** argv) } else if (c && cmd == "retryunknown") { - c->retryUnkonwn(); + c->retryUnknown(); } else if (cmd == "peers") { @@ -1520,7 +1519,7 @@ int main(int argc, char** argv) ofstream f; f.open(filename); - dev::eth::State state =c->state(index + 1,c->blockChain().numberHash(block)); + dev::eth::State state = c->state(index + 1,c->blockChain().numberHash(block)); if (index < state.pending().size()) { Executive e(state, c->blockChain(), 0); @@ -1712,7 +1711,7 @@ int main(int argc, char** argv) while (!g_exit) this_thread::sleep_for(chrono::milliseconds(1000)); - StructuredLogger::stopping(clientImplString, dev::Version); + StructuredLogger::stopping(WebThreeDirect::composeClientVersion("++eth", clientName), dev::Version); auto netData = web3.saveNetwork(); if (!netData.empty()) writeFile((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp", netData); diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 31c2e8026..2f742f658 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -879,7 +879,9 @@ State Client::asOf(h256 const& _block) const { try { - return State(m_stateDB, bc(), _block); + State ret(m_stateDB); + ret.populateFromChain(bc(), _block); + return ret; } catch (Exception& ex) { @@ -896,12 +898,36 @@ void Client::prepareForTransaction() State Client::state(unsigned _txi, h256 _block) const { - return State(m_stateDB, m_bc, _block).fromPending(_txi); + try + { + State ret(m_stateDB); + ret.populateFromChain(m_bc, _block); + return ret.fromPending(_txi); + } + catch (Exception& ex) + { + ex << errinfo_block(bc().block(_block)); + onBadBlock(ex); + return State(); + } } -eth::State Client::state(h256 _block) const +State Client::state(h256 const& _block, PopulationStatistics* o_stats) const { - return State(m_stateDB, m_bc, _block); + try + { + State ret(m_stateDB); + PopulationStatistics s = ret.populateFromChain(m_bc, _block); + if (o_stats) + swap(s, *o_stats); + return ret; + } + catch (Exception& ex) + { + ex << errinfo_block(bc().block(_block)); + onBadBlock(ex); + return State(); + } } eth::State Client::state(unsigned _txi) const diff --git a/libethereum/Client.h b/libethereum/Client.h index b70f01155..c8aad1670 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -147,7 +147,7 @@ public: // [PRIVATE API - only relevant for base clients, not available in general] dev::eth::State state(unsigned _txi, h256 _block) const; - dev::eth::State state(h256 _block) const; + dev::eth::State state(h256 const& _block, PopulationStatistics* o_stats = nullptr) const; dev::eth::State state(unsigned _txi) const; /// Get the object representing the current state of Ethereum. @@ -226,7 +226,7 @@ public: /// Kills the blockchain. Just for debug use. void killChain(); /// Retries all blocks with unknown parents. - void retryUnkonwn() { m_bq.retryAllUnknown(); } + void retryUnknown() { m_bq.retryAllUnknown(); } /// Get a report of activity. ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; } /// Set a JSONRPC server to which we can report bad blocks. diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 67a21d36a..bacbecc3f 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -619,7 +619,7 @@ void EthereumHost::onPeerAborting(EthereumPeer* _peer) if (_peer->isConversing()) { _peer->setIdle(); - if (_peer->isCriticalSyncing()) +// if (_peer->isCriticalSyncing()) _peer->setRude(); continueSync(); } diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 7a30f1ad9..b876bf019 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -48,7 +48,6 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i, Cap EthereumPeer::~EthereumPeer() { - clog(NetMessageSummary) << "Aborting Sync :-("; abortSync(); } @@ -57,8 +56,15 @@ bool EthereumPeer::isRude() const return repMan().isRude(*session(), name()); } +unsigned EthereumPeer::askOverride() const +{ + bytes const& d = repMan().data(*session(), name()); + return d.empty() ? c_maxBlocksAsk : RLP(d).toInt(RLP::LaisezFaire); +} + void EthereumPeer::setRude() { + repMan().setData(*session(), name(), rlp(askOverride() / 2 + 1)); repMan().noteRude(*session(), name()); session()->addNote("manners", "RUDE"); } @@ -140,7 +146,7 @@ void EthereumPeer::requestHashes(h256 const& _lastHash) void EthereumPeer::requestBlocks() { setAsking(Asking::Blocks); - auto blocks = m_sub.nextFetch(isRude() ? 1 : c_maxBlocksAsk); + auto blocks = m_sub.nextFetch(askOverride()); if (blocks.size()) { RLPStream s; diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index a12b7a197..e9d29322f 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -91,6 +91,9 @@ public: private: using p2p::Capability::sealAndSend; + /// Figure out the amount of blocks we should be asking for. + unsigned askOverride() const; + /// Interpret an incoming message. virtual bool interpret(unsigned _id, RLP const& _r); diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 4976ce03a..41a8d1961 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -53,6 +53,8 @@ public: std::string json(bool _styled = false) const; + OnOpFunc onOp() { return [&](uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM) { (*this)(_steps, _inst, _newMemSize, _gasCost, _gas, _vm, _extVM); }; } + private: bool m_showMnemonics = false; std::vector m_lastInst; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index a4b784b6f..f56c12c91 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -115,21 +115,19 @@ State::State(OverlayDB const& _db, BaseState _bs, Address _coinbaseAddress): paranoia("end of normal construction.", true); } -State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h, ImportRequirements::value _ir): - m_db(_db), - m_state(&m_db), - m_blockReward(c_blockReward) +PopulationStatistics State::populateFromChain(BlockChain const& _bc, h256 const& _h, ImportRequirements::value _ir) { - auto b = _bc.block(_h); - BlockInfo bi(b); + PopulationStatistics ret; - if (!bi) + if (!_bc.isKnown(_h)) { // Might be worth throwing here. cwarn << "Invalid block given for state population: " << _h; - return; + return ret; } + auto b = _bc.block(_h); + BlockInfo bi(b); if (bi.number) { // Non-genesis: @@ -143,10 +141,10 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h, ImportRequire m_ourAddress = bi.coinbaseAddress; boost::timer t; auto vb = BlockChain::verifyBlock(b); - cnote << "verifyBlock:" << t.elapsed(); + ret.verify = t.elapsed(); t.restart(); enact(vb, _bc, _ir); - cnote << "enact:" << t.elapsed(); + ret.enact = t.elapsed(); } else { @@ -155,6 +153,8 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h, ImportRequire m_state.init(); sync(_bc, _h, bi, _ir); } + + return ret; } State::State(State const& _s): @@ -600,7 +600,7 @@ string State::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequire { StandardTrace st; st.setShowMnemonics(); - execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, [&](uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM) { st(_steps, _inst, _newMemSize, _gasCost, _gas, _vm, _extVM); }); + execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, st.onOp()); ret += (ret.empty() ? "[" : ",") + st.json(); RLPStream receiptRLP; diff --git a/libethereum/State.h b/libethereum/State.h index 771cdb6bf..3eac63d04 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -123,6 +123,12 @@ enum class Permanence Committed }; +struct PopulationStatistics +{ + double verify; + double enact; +}; + /** * @brief Model of the current state of the ledger. * Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block). @@ -146,9 +152,6 @@ public: /// You can also set the coinbase address. explicit State(OverlayDB const& _db, BaseState _bs = BaseState::PreExisting, Address _coinbaseAddress = Address()); - /// Construct state object from arbitrary point in blockchain. - State(OverlayDB const& _db, BlockChain const& _bc, h256 _hash, ImportRequirements::value _ir = ImportRequirements::Default); - /// Copy state object. State(State const& _s); @@ -157,6 +160,9 @@ public: ~State(); + /// Construct state object from arbitrary point in blockchain. + PopulationStatistics populateFromChain(BlockChain const& _bc, h256 const& _hash, ImportRequirements::value _ir = ImportRequirements::Default); + /// Set the coinbase address for any transactions we do. /// This causes a complete reset of current block. void setAddress(Address _coinbaseAddress) { m_ourAddress = _coinbaseAddress; resetCurrent(); } diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 357292853..f6cab376a 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -63,12 +63,16 @@ using LogEntries = std::vector; struct LocalisedLogEntry: public LogEntry { LocalisedLogEntry() {} - explicit LocalisedLogEntry(LogEntry const& _le): LogEntry(_le) {}; + explicit LocalisedLogEntry(LogEntry const& _le): LogEntry(_le) {} explicit LocalisedLogEntry( LogEntry const& _le, h256 _special - ): LogEntry(_le), special(_special) {}; + ): + LogEntry(_le), + isSpecial(true), + special(_special) + {} explicit LocalisedLogEntry( LogEntry const& _le, @@ -76,15 +80,24 @@ struct LocalisedLogEntry: public LogEntry h256 _th, unsigned _ti, unsigned _li - ): LogEntry(_le), blockHash(_bi.hash()), blockNumber((BlockNumber)_bi.number), transactionHash(_th), transactionIndex(_ti), logIndex(_li), mined(true) {}; - - h256 blockHash = h256(); + ): + LogEntry(_le), + blockHash(_bi.hash()), + blockNumber((BlockNumber)_bi.number), + transactionHash(_th), + transactionIndex(_ti), + logIndex(_li), + mined(true) + {} + + h256 blockHash; BlockNumber blockNumber = 0; - h256 transactionHash = h256(); + h256 transactionHash; unsigned transactionIndex = 0; unsigned logIndex = 0; bool mined = false; - h256 special = h256(); + bool isSpecial = false; + h256 special; }; using LocalisedLogEntries = std::vector; diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 55389ed1b..1482719c6 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -79,6 +79,25 @@ bool ReputationManager::isRude(Session const& _s, std::string const& _sub) const return false; } +void ReputationManager::setData(Session const& _s, std::string const& _sub, bytes const& _data) +{ + DEV_WRITE_GUARDED(x_nodes) + m_nodes[make_pair(_s.id(), _s.info().clientVersion)].subs[_sub].data = _data; +} + +bytes ReputationManager::data(Session const& _s, std::string const& _sub) const +{ + DEV_READ_GUARDED(x_nodes) + { + auto nit = m_nodes.find(make_pair(_s.id(), _s.info().clientVersion)); + if (nit == m_nodes.end()) + return bytes(); + auto sit = nit->second.subs.find(_sub); + return sit == nit->second.subs.end() ? bytes() : sit->second.data; + } + return bytes(); +} + Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bytesConstRef _restoreNetwork): Worker("p2p", 0), m_restoreNetwork(_restoreNetwork.toBytes()), diff --git a/libp2p/Host.h b/libp2p/Host.h index e9cae509c..9523d0cca 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -83,6 +83,7 @@ struct SubReputation { bool isRude = false; int utility = 0; + bytes data; }; struct Reputation @@ -97,6 +98,8 @@ public: void noteRude(Session const& _s, std::string const& _sub = std::string()); bool isRude(Session const& _s, std::string const& _sub = std::string()) const; + void setData(Session const& _s, std::string const& _sub, bytes const& _data); + bytes data(Session const& _s, std::string const& _subs) const; private: std::unordered_map, Reputation> m_nodes; ///< Nodes that were impolite while syncing. We avoid syncing from these if possible. diff --git a/libtestutils/FixedClient.cpp b/libtestutils/FixedClient.cpp index 052141039..4237415ed 100644 --- a/libtestutils/FixedClient.cpp +++ b/libtestutils/FixedClient.cpp @@ -28,5 +28,7 @@ using namespace dev::test; eth::State FixedClient::asOf(h256 const& _h) const { ReadGuard l(x_stateDB); - return State(m_state.db(), bc(), _h); + State ret(m_state.db()); + ret.populateFromChain(bc(), _h); + return ret; } diff --git a/libweb3jsonrpc/JsonHelper.cpp b/libweb3jsonrpc/JsonHelper.cpp new file mode 100644 index 000000000..0b6cd4a1d --- /dev/null +++ b/libweb3jsonrpc/JsonHelper.cpp @@ -0,0 +1,427 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file JsonHelper.cpp + * @authors: + * Gav Wood + * @date 2014 + */ + +#include "JsonHelper.h" + +#include +#include +#include +#include +#include +#include +#include +using namespace std; +using namespace dev; +using namespace eth; + +namespace dev +{ + +Json::Value toJson(unordered_map const& _storage) +{ + Json::Value res(Json::objectValue); + for (auto i: _storage) + res[toJS(i.first)] = toJS(i.second); + return res; +} + +Json::Value toJson(map const& _storage) +{ + Json::Value res(Json::objectValue); + for (auto i: _storage) + res[toJS(i.first)] = toJS(i.second); + return res; +} + +// //////////////////////////////////////////////////////////////////////////////// +// p2p +// //////////////////////////////////////////////////////////////////////////////// +namespace p2p +{ + +Json::Value toJson(p2p::PeerSessionInfo const& _p) +{ + Json::Value ret; + ret["id"] = _p.id.hex(); + ret["clientVersion"] = _p.clientVersion; + ret["host"] = _p.host; + ret["port"] = _p.port; + ret["lastPing"] = (int)chrono::duration_cast(_p.lastPing).count(); + for (auto const& i: _p.notes) + ret["notes"][i.first] = i.second; + for (auto const& i: _p.caps) + ret["caps"][i.first] = (unsigned)i.second; + return ret; +} + +} + +// //////////////////////////////////////////////////////////////////////////////// +// eth +// //////////////////////////////////////////////////////////////////////////////// + +namespace eth +{ + +Json::Value toJson(dev::eth::BlockInfo const& _bi) +{ + Json::Value res; + if (_bi) + { + res["hash"] = toJS(_bi.hash()); + res["parentHash"] = toJS(_bi.parentHash); + res["sha3Uncles"] = toJS(_bi.sha3Uncles); + res["miner"] = toJS(_bi.coinbaseAddress); + res["stateRoot"] = toJS(_bi.stateRoot); + res["transactionsRoot"] = toJS(_bi.transactionsRoot); + res["difficulty"] = toJS(_bi.difficulty); + res["number"] = toJS(_bi.number); + res["gasUsed"] = toJS(_bi.gasUsed); + res["gasLimit"] = toJS(_bi.gasLimit); + res["timestamp"] = toJS(_bi.timestamp); + res["extraData"] = toJS(_bi.extraData); + res["nonce"] = toJS(_bi.nonce); + res["logsBloom"] = toJS(_bi.logBloom); + + res["seedHash"] = toJS(_bi.seedHash()); + res["target"] = toJS(_bi.boundary()); + } + return res; +} + +Json::Value toJson(dev::eth::Transaction const& _t, std::pair _location, BlockNumber _blockNumber) +{ + Json::Value res; + if (_t) + { + res["hash"] = toJS(_t.sha3()); + res["input"] = toJS(_t.data()); + res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.receiveAddress()); + res["from"] = toJS(_t.safeSender()); + res["gas"] = toJS(_t.gas()); + res["gasPrice"] = toJS(_t.gasPrice()); + res["nonce"] = toJS(_t.nonce()); + res["value"] = toJS(_t.value()); + res["blockHash"] = toJS(_location.first); + res["transactionIndex"] = toJS(_location.second); + res["blockNumber"] = toJS(_blockNumber); + } + return res; +} + +Json::Value toJson(dev::eth::BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, Transactions const& _ts) +{ + Json::Value res = toJson(_bi); + if (_bi) + { + res["totalDifficulty"] = toJS(_bd.totalDifficulty); + res["uncles"] = Json::Value(Json::arrayValue); + for (h256 h: _us) + res["uncles"].append(toJS(h)); + res["transactions"] = Json::Value(Json::arrayValue); + for (unsigned i = 0; i < _ts.size(); i++) + res["transactions"].append(toJson(_ts[i], std::make_pair(_bi.hash(), i), (BlockNumber)_bi.number)); + } + return res; +} + +Json::Value toJson(dev::eth::BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, TransactionHashes const& _ts) +{ + Json::Value res = toJson(_bi); + if (_bi) + { + res["totalDifficulty"] = toJS(_bd.totalDifficulty); + res["uncles"] = Json::Value(Json::arrayValue); + for (h256 h: _us) + res["uncles"].append(toJS(h)); + res["transactions"] = Json::Value(Json::arrayValue); + for (h256 const& t: _ts) + res["transactions"].append(toJS(t)); + } + return res; +} + +Json::Value toJson(dev::eth::TransactionSkeleton const& _t) +{ + Json::Value res; + res["to"] = _t.creation ? Json::Value() : toJS(_t.to); + res["from"] = toJS(_t.from); + res["gas"] = toJS(_t.gas); + res["gasPrice"] = toJS(_t.gasPrice); + res["value"] = toJS(_t.value); + res["data"] = toJS(_t.data, 32); + return res; +} + +Json::Value toJson(dev::eth::TransactionReceipt const& _t) +{ + Json::Value res; + res["stateRoot"] = toJS(_t.stateRoot()); + res["gasUsed"] = toJS(_t.gasUsed()); + res["bloom"] = toJS(_t.bloom()); + res["log"] = dev::toJson(_t.log()); + return res; +} + +Json::Value toJson(dev::eth::Transaction const& _t) +{ + Json::Value res; + res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.to()); + res["from"] = toJS(_t.from()); + res["gas"] = toJS(_t.gas()); + res["gasPrice"] = toJS(_t.gasPrice()); + res["value"] = toJS(_t.value()); + res["data"] = toJS(_t.data(), 32); + res["nonce"] = toJS(_t.nonce()); + res["hash"] = toJS(_t.sha3(WithSignature)); + res["sighash"] = toJS(_t.sha3(WithoutSignature)); + res["r"] = toJS(_t.signature().r); + res["s"] = toJS(_t.signature().s); + res["v"] = toJS(_t.signature().v); + return res; +} + +Json::Value toJson(dev::eth::LocalisedLogEntry const& _e) +{ + Json::Value res; + + if (_e.isSpecial) + res = toJS(_e.special); + else + { + res = toJson(static_cast(_e)); + if (_e.mined) + { + res["type"] = "mined"; + res["blockNumber"] = _e.blockNumber; + res["blockHash"] = toJS(_e.blockHash); + res["logIndex"] = _e.logIndex; + res["transactionHash"] = toJS(_e.transactionHash); + res["transactionIndex"] = _e.transactionIndex; + } + else + { + res["type"] = "pending"; + res["blockNumber"] = Json::Value(Json::nullValue); + res["blockHash"] = Json::Value(Json::nullValue); + res["logIndex"] = Json::Value(Json::nullValue); + res["transactionHash"] = Json::Value(Json::nullValue); + res["transactionIndex"] = Json::Value(Json::nullValue); + } + } + return res; +} + +Json::Value toJson(dev::eth::LogEntry const& _e) +{ + Json::Value res; + res["data"] = toJS(_e.data); + res["address"] = toJS(_e.address); + res["topics"] = Json::Value(Json::arrayValue); + for (auto const& t: _e.topics) + res["topics"].append(toJS(t)); + return res; +} + +TransactionSkeleton toTransactionSkeleton(Json::Value const& _json) +{ + TransactionSkeleton ret; + if (!_json.isObject() || _json.empty()) + return ret; + + if (!_json["from"].empty()) + ret.from = jsToAddress(_json["from"].asString()); + if (!_json["to"].empty() && _json["to"].asString() != "0x") + ret.to = jsToAddress(_json["to"].asString()); + else + ret.creation = true; + + if (!_json["value"].empty()) + ret.value = jsToU256(_json["value"].asString()); + + if (!_json["gas"].empty()) + ret.gas = jsToU256(_json["gas"].asString()); + + if (!_json["gasPrice"].empty()) + ret.gasPrice = jsToU256(_json["gasPrice"].asString()); + + if (!_json["data"].empty()) // ethereum.js has preconstructed the data array + ret.data = jsToBytes(_json["data"].asString()); + + if (!_json["code"].empty()) + ret.data = jsToBytes(_json["code"].asString()); + return ret; +} + +dev::eth::LogFilter toLogFilter(Json::Value const& _json) +{ + dev::eth::LogFilter filter; + if (!_json.isObject() || _json.empty()) + return filter; + + // check only !empty. it should throw exceptions if input params are incorrect + if (!_json["fromBlock"].empty()) + filter.withEarliest(jsToFixed<32>(_json["fromBlock"].asString())); + if (!_json["toBlock"].empty()) + filter.withLatest(jsToFixed<32>(_json["toBlock"].asString())); + if (!_json["address"].empty()) + { + if (_json["address"].isArray()) + for (auto i : _json["address"]) + filter.address(jsToAddress(i.asString())); + else + filter.address(jsToAddress(_json["address"].asString())); + } + if (!_json["topics"].empty()) + for (unsigned i = 0; i < _json["topics"].size(); i++) + { + if (_json["topics"][i].isArray()) + { + for (auto t: _json["topics"][i]) + if (!t.isNull()) + filter.topic(i, jsToFixed<32>(t.asString())); + } + else if (!_json["topics"][i].isNull()) // if it is anything else then string, it should and will fail + filter.topic(i, jsToFixed<32>(_json["topics"][i].asString())); + } + return filter; +} + +// TODO: this should be removed once we decide to remove backward compatibility with old log filters +dev::eth::LogFilter toLogFilter(Json::Value const& _json, Interface const& _client) // commented to avoid warning. Uncomment once in use @ PoC-7. +{ + dev::eth::LogFilter filter; + if (!_json.isObject() || _json.empty()) + return filter; + + // check only !empty. it should throw exceptions if input params are incorrect + if (!_json["fromBlock"].empty()) + filter.withEarliest(_client.hashFromNumber(jsToBlockNumber(_json["fromBlock"].asString()))); + if (!_json["toBlock"].empty()) + filter.withLatest(_client.hashFromNumber(jsToBlockNumber(_json["toBlock"].asString()))); + if (!_json["address"].empty()) + { + if (_json["address"].isArray()) + for (auto i : _json["address"]) + filter.address(jsToAddress(i.asString())); + else + filter.address(jsToAddress(_json["address"].asString())); + } + if (!_json["topics"].empty()) + for (unsigned i = 0; i < _json["topics"].size(); i++) + { + if (_json["topics"][i].isArray()) + { + for (auto t: _json["topics"][i]) + if (!t.isNull()) + filter.topic(i, jsToFixed<32>(t.asString())); + } + else if (!_json["topics"][i].isNull()) // if it is anything else then string, it should and will fail + filter.topic(i, jsToFixed<32>(_json["topics"][i].asString())); + } + return filter; +} + +} + +// //////////////////////////////////////////////////////////////////////////////////// +// shh +// //////////////////////////////////////////////////////////////////////////////////// + +namespace shh +{ + +Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m) +{ + Json::Value res; + res["hash"] = toJS(_h); + res["expiry"] = toJS(_e.expiry()); + res["sent"] = toJS(_e.sent()); + res["ttl"] = toJS(_e.ttl()); + res["workProved"] = toJS(_e.workProved()); + res["topics"] = Json::Value(Json::arrayValue); + for (auto const& t: _e.topic()) + res["topics"].append(toJS(t)); + res["payload"] = toJS(_m.payload()); + res["from"] = toJS(_m.from()); + res["to"] = toJS(_m.to()); + return res; +} + +shh::Message toMessage(Json::Value const& _json) +{ + shh::Message ret; + if (!_json["from"].empty()) + ret.setFrom(jsToPublic(_json["from"].asString())); + if (!_json["to"].empty()) + ret.setTo(jsToPublic(_json["to"].asString())); + if (!_json["payload"].empty()) + ret.setPayload(jsToBytes(_json["payload"].asString())); + return ret; +} + +shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m, Secret _from) +{ + unsigned ttl = 50; + unsigned workToProve = 50; + shh::BuildTopic bt; + + if (!_json["ttl"].empty()) + ttl = jsToInt(_json["ttl"].asString()); + + if (!_json["workToProve"].empty()) + workToProve = jsToInt(_json["workToProve"].asString()); + + if (!_json["topics"].empty()) + for (auto i: _json["topics"]) + { + if (i.isArray()) + { + for (auto j: i) + if (!j.isNull()) + bt.shift(jsToBytes(j.asString())); + } + else if (!i.isNull()) // if it is anything else then string, it should and will fail + bt.shift(jsToBytes(i.asString())); + } + + return _m.seal(_from, bt, ttl, workToProve); +} + +pair toWatch(Json::Value const& _json) +{ + shh::BuildTopic bt; + Public to; + + if (!_json["to"].empty()) + to = jsToPublic(_json["to"].asString()); + + if (!_json["topics"].empty()) + for (auto i: _json["topics"]) + bt.shift(jsToBytes(i.asString())); + + return make_pair(bt, to); +} + +} + +} diff --git a/libweb3jsonrpc/JsonHelper.h b/libweb3jsonrpc/JsonHelper.h new file mode 100644 index 000000000..d445ad0dc --- /dev/null +++ b/libweb3jsonrpc/JsonHelper.h @@ -0,0 +1,104 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file JsonHelper.h + * @authors: + * Gav Wood + * @date 2015 + */ +#pragma once + +#include +#include +#include +#include + +namespace dev +{ + +Json::Value toJson(std::map const& _storage); +Json::Value toJson(std::unordered_map const& _storage); + +namespace p2p +{ + +Json::Value toJson(PeerSessionInfo const& _p); + +} + +namespace eth +{ + +class Transaction; +class BlockDetails; +class Interface; +using Transactions = std::vector; +using UncleHashes = h256s; +using TransactionHashes = h256s; + +Json::Value toJson(BlockInfo const& _bi); +Json::Value toJson(Transaction const& _t, std::pair _location, BlockNumber _blockNumber); +Json::Value toJson(BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, Transactions const& _ts); +Json::Value toJson(BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, TransactionHashes const& _ts); +Json::Value toJson(TransactionSkeleton const& _t); +Json::Value toJson(Transaction const& _t); +Json::Value toJson(TransactionReceipt const& _t); +Json::Value toJson(LocalisedLogEntry const& _e); +Json::Value toJson(LogEntry const& _e); +TransactionSkeleton toTransactionSkeleton(Json::Value const& _json); +LogFilter toLogFilter(Json::Value const& _json); +LogFilter toLogFilter(Json::Value const& _json, Interface const& _client); // commented to avoid warning. Uncomment once in use @ PoC-7. + +} + +namespace shh +{ + +Json::Value toJson(h256 const& _h, Envelope const& _e, Message const& _m); +Message toMessage(Json::Value const& _json); +Envelope toSealed(Json::Value const& _json, Message const& _m, Secret _from); +std::pair toWatch(Json::Value const& _json); + +} + +template +Json::Value toJson(std::vector const& _es) +{ + Json::Value res(Json::arrayValue); + for (auto const& e: _es) + res.append(toJson(e)); + return res; +} + +template +Json::Value toJson(std::unordered_set const& _es) +{ + Json::Value res(Json::arrayValue); + for (auto const& e: _es) + res.append(toJson(e)); + return res; +} + +template +Json::Value toJson(std::set const& _es) +{ + Json::Value res(Json::arrayValue); + for (auto const& e: _es) + res.append(toJson(e)); + return res; +} + +} diff --git a/libweb3jsonrpc/WebThreeStubServer.cpp b/libweb3jsonrpc/WebThreeStubServer.cpp index 1bef71b59..f62fdd32a 100644 --- a/libweb3jsonrpc/WebThreeStubServer.cpp +++ b/libweb3jsonrpc/WebThreeStubServer.cpp @@ -21,21 +21,25 @@ * @date 2014 */ +#include "WebThreeStubServer.h" // Make sure boost/asio.hpp is included before windows.h. #include #include #include +#include #include +#include #include -#include "WebThreeStubServer.h" +#include "JsonHelper.h" using namespace std; using namespace dev; using namespace dev::eth; -WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, shared_ptr const& _ethAccounts, std::vector const& _shhAccounts, KeyManager& _keyMan): +WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, shared_ptr const& _ethAccounts, std::vector const& _shhAccounts, KeyManager& _keyMan, dev::eth::TrivialGasPricer& _gp): WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts), m_web3(_web3), - m_keyMan(_keyMan) + m_keyMan(_keyMan), + m_gp(_gp) { auto path = getDataDir() + "/.web3"; boost::filesystem::create_directories(path); @@ -57,23 +61,237 @@ bool WebThreeStubServer::eth_notePassword(string const& _password) return true; } +#define ADMIN requires(_session, Priviledge::Admin) + Json::Value WebThreeStubServer::admin_eth_blockQueueStatus(string const& _session) { + ADMIN; + Json::Value ret; + BlockQueueStatus bqs = m_web3.ethereum()->blockQueue().status(); + ret["importing"] = (int)bqs.importing; + ret["verified"] = (int)bqs.verified; + ret["verifying"] = (int)bqs.verifying; + ret["unverified"] = (int)bqs.unverified; + ret["future"] = (int)bqs.future; + ret["unknown"] = (int)bqs.unknown; + ret["bad"] = (int)bqs.bad; + return ret; +} + +bool WebThreeStubServer::admin_eth_setAskPrice(std::string const& _wei, std::string const& _session) +{ + ADMIN; + m_gp.setAsk(jsToU256(_wei)); + return true; +} + +bool WebThreeStubServer::admin_eth_setBidPrice(std::string const& _wei, std::string const& _session) +{ + ADMIN; + m_gp.setBid(jsToU256(_wei)); + return true; +} + +dev::eth::CanonBlockChain const& WebThreeStubServer::bc() const +{ + return m_web3.ethereum()->blockChain(); +} + +dev::eth::BlockQueue const& WebThreeStubServer::bq() const +{ + return m_web3.ethereum()->blockQueue(); +} + +Json::Value WebThreeStubServer::admin_eth_findBlock(std::string const& _blockHash, std::string const& _session) +{ + ADMIN; + h256 h(_blockHash); + if (bc().isKnown(h)) + return toJson(bc().info(h)); + switch(bq().blockStatus(h)) + { + case QueueStatus::Ready: + return "ready"; + case QueueStatus::Importing: + return "importing"; + case QueueStatus::UnknownParent: + return "unknown parent"; + case QueueStatus::Bad: + return "bad"; + default: + return "unknown"; + } +} + +std::string WebThreeStubServer::admin_eth_blockQueueFirstUnknown(std::string const& _session) +{ + ADMIN; + return bq().firstUnknown().hex(); +} + +bool WebThreeStubServer::admin_eth_blockQueueRetryUnknown(std::string const& _session) +{ + ADMIN; + m_web3.ethereum()->retryUnknown(); + return true; +} + +Json::Value WebThreeStubServer::admin_eth_allAccounts(std::string const& _session) +{ + ADMIN; + Json::Value ret; + u256 total = 0; + u256 pendingtotal = 0; + Address beneficiary; + for (auto const& i: m_keyMan.accountDetails()) + { + auto pending = m_web3.ethereum()->balanceAt(i.first, PendingBlock); + auto latest = m_web3.ethereum()->balanceAt(i.first, LatestBlock); + Json::Value a; + if (i.first == beneficiary) + a["beneficiary"] = true; + a["address"] = toJS(i.first); + a["balance"] = toJS(latest); + a["nicebalance"] = formatBalance(latest); + a["pending"] = toJS(pending); + a["nicepending"] = formatBalance(pending); + ret["accounts"][i.second.first] = a; + total += latest; + pendingtotal += pending; + } + ret["total"] = toJS(total); + ret["nicetotal"] = formatBalance(total); + ret["pendingtotal"] = toJS(pendingtotal); + ret["nicependingtotal"] = formatBalance(pendingtotal); + return ret; +} + +Json::Value WebThreeStubServer::admin_eth_newAccount(Json::Value const& _info, std::string const& _session) +{ + ADMIN; + if (!_info.isMember("name")) + throw jsonrpc::JsonRpcException("No member found: name"); + string name = _info["name"].asString(); + auto s = Secret::random(); + h128 uuid; + if (_info.isMember("password")) + { + string password = _info["password"].asString(); + string hint = _info["passwordHint"].asString(); + uuid = m_keyMan.import(s, name, password, hint); + } + else + uuid = m_keyMan.import(s, name); Json::Value ret; - if (isAdmin(_session)) + ret["account"] = toJS(toAddress(s)); + ret["uuid"] = toUUID(uuid); + return ret; +} + +bool WebThreeStubServer::admin_eth_setMiningBenefactor(std::string const& _uuidOrAddress, std::string const& _session) +{ + ADMIN; + (void)_uuidOrAddress; + return true; +} + +Json::Value WebThreeStubServer::admin_eth_inspect(std::string const& _address, std::string const& _session) +{ + ADMIN; + (void)_address; + return {}; +} + +bool isHex(std::string const& _s) +{ + unsigned i = (_s.size() >= 2 && _s.substr(0, 2) == "0x") ? 2 : 0; + for (; i < _s.size(); ++i) + if (fromHex(_s[i], WhenError::DontThrow) == -1) + return false; + return true; +} + +template bool isHash(std::string const& _hash) +{ + return (_hash.size() == T::size * 2 || (_hash.size() == T::size * 2 + 2 && _hash.substr(0, 2) == "0x")) && isHex(_hash); +} + +h256 WebThreeStubServer::blockHash(std::string const& _blockNumberOrHash) const +{ + if (isHash(_blockNumberOrHash)) + return h256(_blockNumberOrHash.substr(_blockNumberOrHash.size() - 64, 64)); + try { - BlockQueueStatus bqs = m_web3.ethereum()->blockQueue().status(); - ret["importing"] = (int)bqs.importing; - ret["verified"] = (int)bqs.verified; - ret["verifying"] = (int)bqs.verifying; - ret["unverified"] = (int)bqs.unverified; - ret["future"] = (int)bqs.future; - ret["unknown"] = (int)bqs.unknown; - ret["bad"] = (int)bqs.bad; + return bc().numberHash(stoul(_blockNumberOrHash)); } + catch (...) + { + throw jsonrpc::JsonRpcException("Invalid argument"); + } +} + +Json::Value WebThreeStubServer::admin_eth_reprocess(std::string const& _blockNumberOrHash, std::string const& _session) +{ + ADMIN; + Json::Value ret; + PopulationStatistics ps; + m_web3.ethereum()->state(blockHash(_blockNumberOrHash), &ps); + ret["enact"] = ps.enact; + ret["verify"] = ps.verify; + ret["total"] = ps.verify + ps.enact; + return ret; +} + +Json::Value WebThreeStubServer::admin_eth_vmTrace(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) +{ + ADMIN; + + Json::Value ret; + + auto c = m_web3.ethereum(); + State state = c->state(_txIndex + 1, blockHash(_blockNumberOrHash)); + + if (_txIndex < 0) + throw jsonrpc::JsonRpcException("Negative index"); + + if ((unsigned)_txIndex < state.pending().size()) + { + Executive e(state, bc(), 0); + Transaction t = state.pending()[_txIndex]; + state = state.fromPending(_txIndex); + try + { + StandardTrace st; + st.setShowMnemonics(); + e.initialize(t); + if (!e.execute()) + e.go(st.onOp()); + e.finalize(); + Json::Reader().parse(st.json(), ret); + } + catch(Exception const& _e) + { + cwarn << diagnostic_information(_e); + } + } + return ret; } +Json::Value WebThreeStubServer::admin_eth_getReceiptByHashAndIndex(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) +{ + ADMIN; + if (_txIndex < 0) + throw jsonrpc::JsonRpcException("Negative index"); + auto h = blockHash(_blockNumberOrHash); + if (!bc().isKnown(h)) + throw jsonrpc::JsonRpcException("Invalid/unknown block."); + auto rs = bc().receipts(h); + if ((unsigned)_txIndex >= rs.receipts.size()) + throw jsonrpc::JsonRpcException("Index too large."); + return toJson(rs.receipts[_txIndex]); +} + std::string WebThreeStubServer::web3_clientVersion() { return m_web3.clientVersion(); diff --git a/libweb3jsonrpc/WebThreeStubServer.h b/libweb3jsonrpc/WebThreeStubServer.h index 8bdeb6b7a..ecf8acca0 100644 --- a/libweb3jsonrpc/WebThreeStubServer.h +++ b/libweb3jsonrpc/WebThreeStubServer.h @@ -32,16 +32,19 @@ namespace dev { + class WebThreeDirect; namespace eth { class KeyManager; -} +class TrivialGasPricer; +class CanonBlockChain; +class BlockQueue; } struct SessionPermissions { - bool admin; + std::unordered_set priviledges; }; /** @@ -50,7 +53,7 @@ struct SessionPermissions class WebThreeStubServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace { public: - WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::shared_ptr const& _ethAccounts, std::vector const& _shhAccounts, dev::eth::KeyManager& _keyMan); + WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::shared_ptr const& _ethAccounts, std::vector const& _shhAccounts, dev::eth::KeyManager& _keyMan, dev::eth::TrivialGasPricer& _gp); virtual std::string web3_clientVersion() override; @@ -58,7 +61,7 @@ public: void addSession(std::string const& _session, SessionPermissions const& _p) { m_sessions[_session] = _p; } private: - bool isAdmin(std::string const& _session) const override { auto it = m_sessions.find(_session); return it != m_sessions.end() && it->second.admin; } + virtual bool hasPriviledgeLevel(std::string const& _session, Priviledge _l) const override { auto it = m_sessions.find(_session); return it != m_sessions.end() && it->second.priviledges.count(_l); } virtual dev::eth::Interface* client() override; virtual std::shared_ptr face() override; @@ -68,15 +71,37 @@ private: virtual std::string get(std::string const& _name, std::string const& _key) override; virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) override; - virtual bool eth_notePassword(std::string const& _password); - virtual Json::Value admin_eth_blockQueueStatus(std::string const& _session); + virtual bool eth_notePassword(std::string const& _password) override; + virtual Json::Value admin_eth_blockQueueStatus(std::string const& _session) override; + virtual bool admin_eth_setAskPrice(std::string const& _wei, std::string const& _session) override; + virtual bool admin_eth_setBidPrice(std::string const& _wei, std::string const& _session) override; + + virtual Json::Value admin_eth_findBlock(std::string const& _blockHash, std::string const& _session) override; + virtual std::string admin_eth_blockQueueFirstUnknown(std::string const& _session) override; + virtual bool admin_eth_blockQueueRetryUnknown(std::string const& _session) override; + + virtual Json::Value admin_eth_allAccounts(std::string const& _session) override; + virtual Json::Value admin_eth_newAccount(const Json::Value& _info, std::string const& _session) override; + virtual bool admin_eth_setMiningBenefactor(std::string const& _uuidOrAddress, std::string const& _session) override; + virtual Json::Value admin_eth_inspect(std::string const& _address, std::string const& _session) override; + virtual Json::Value admin_eth_reprocess(std::string const& _blockNumberOrHash, std::string const& _session) override; + virtual Json::Value admin_eth_vmTrace(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) override; + virtual Json::Value admin_eth_getReceiptByHashAndIndex(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) override; private: + h256 blockHash(std::string const& _blockNumberOrHash) const; + + dev::eth::CanonBlockChain const& bc() const; + dev::eth::BlockQueue const& bq() const; + dev::WebThreeDirect& m_web3; dev::eth::KeyManager& m_keyMan; + dev::eth::TrivialGasPricer& m_gp; leveldb::ReadOptions m_readOptions; leveldb::WriteOptions m_writeOptions; leveldb::DB* m_db; std::unordered_map m_sessions; }; + +} diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index f88443ee5..99c7e3425 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -21,6 +21,8 @@ * @date 2014 */ +#include "WebThreeStubServerBase.h" + // Make sure boost/asio.hpp is included before windows.h. #include @@ -41,13 +43,13 @@ #if ETH_SERPENT || !ETH_TRUE #include #endif -#include "WebThreeStubServerBase.h" #include "AccountHolder.h" - +#include "JsonHelper.h" using namespace std; using namespace jsonrpc; using namespace dev; -using namespace dev::eth; +using namespace eth; +using namespace shh; #if ETH_DEBUG const unsigned dev::SensibleHttpThreads = 1; @@ -56,302 +58,6 @@ const unsigned dev::SensibleHttpThreads = 4; #endif const unsigned dev::SensibleHttpPort = 8545; -static Json::Value toJson(dev::eth::BlockInfo const& _bi) -{ - Json::Value res; - if (_bi) - { - res["hash"] = toJS(_bi.hash()); - res["parentHash"] = toJS(_bi.parentHash); - res["sha3Uncles"] = toJS(_bi.sha3Uncles); - res["miner"] = toJS(_bi.coinbaseAddress); - res["stateRoot"] = toJS(_bi.stateRoot); - res["transactionsRoot"] = toJS(_bi.transactionsRoot); - res["difficulty"] = toJS(_bi.difficulty); - res["number"] = toJS(_bi.number); - res["gasUsed"] = toJS(_bi.gasUsed); - res["gasLimit"] = toJS(_bi.gasLimit); - res["timestamp"] = toJS(_bi.timestamp); - res["extraData"] = toJS(_bi.extraData); - res["nonce"] = toJS(_bi.nonce); - res["logsBloom"] = toJS(_bi.logBloom); - } - return res; -} - -static Json::Value toJson(dev::eth::Transaction const& _t, std::pair _location, BlockNumber _blockNumber) -{ - Json::Value res; - if (_t) - { - res["hash"] = toJS(_t.sha3()); - res["input"] = toJS(_t.data()); - res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.receiveAddress()); - res["from"] = toJS(_t.safeSender()); - res["gas"] = toJS(_t.gas()); - res["gasPrice"] = toJS(_t.gasPrice()); - res["nonce"] = toJS(_t.nonce()); - res["value"] = toJS(_t.value()); - res["blockHash"] = toJS(_location.first); - res["transactionIndex"] = toJS(_location.second); - res["blockNumber"] = toJS(_blockNumber); - } - return res; -} - -static Json::Value toJson(dev::eth::BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, Transactions const& _ts) -{ - Json::Value res = toJson(_bi); - if (_bi) - { - res["totalDifficulty"] = toJS(_bd.totalDifficulty); - res["uncles"] = Json::Value(Json::arrayValue); - for (h256 h: _us) - res["uncles"].append(toJS(h)); - res["transactions"] = Json::Value(Json::arrayValue); - for (unsigned i = 0; i < _ts.size(); i++) - res["transactions"].append(toJson(_ts[i], std::make_pair(_bi.hash(), i), (BlockNumber)_bi.number)); - } - return res; -} - -static Json::Value toJson(dev::eth::BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, TransactionHashes const& _ts) -{ - Json::Value res = toJson(_bi); - if (_bi) - { - res["totalDifficulty"] = toJS(_bd.totalDifficulty); - res["uncles"] = Json::Value(Json::arrayValue); - for (h256 h: _us) - res["uncles"].append(toJS(h)); - res["transactions"] = Json::Value(Json::arrayValue); - for (h256 const& t: _ts) - res["transactions"].append(toJS(t)); - } - return res; -} - -static Json::Value toJson(dev::eth::TransactionSkeleton const& _t) -{ - Json::Value res; - res["to"] = _t.creation ? Json::Value() : toJS(_t.to); - res["from"] = toJS(_t.from); - res["gas"] = toJS(_t.gas); - res["gasPrice"] = toJS(_t.gasPrice); - res["value"] = toJS(_t.value); - res["data"] = toJS(_t.data, 32); - return res; -} - -static Json::Value toJson(dev::eth::Transaction const& _t) -{ - Json::Value res; - res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.to()); - res["from"] = toJS(_t.from()); - res["gas"] = toJS(_t.gas()); - res["gasPrice"] = toJS(_t.gasPrice()); - res["value"] = toJS(_t.value()); - res["data"] = toJS(_t.data(), 32); - res["nonce"] = toJS(_t.nonce()); - res["hash"] = toJS(_t.sha3(WithSignature)); - res["sighash"] = toJS(_t.sha3(WithoutSignature)); - res["r"] = toJS(_t.signature().r); - res["s"] = toJS(_t.signature().s); - res["v"] = toJS(_t.signature().v); - return res; -} - -static Json::Value toJson(dev::eth::LocalisedLogEntry const& _e) -{ - Json::Value res; - if (_e.topics.size() > 0) - { - res["data"] = toJS(_e.data); - res["address"] = toJS(_e.address); - res["topics"] = Json::Value(Json::arrayValue); - for (auto const& t: _e.topics) - res["topics"].append(toJS(t)); - if (_e.mined) - { - res["type"] = "mined"; - res["blockNumber"] = _e.blockNumber; - res["blockHash"] = toJS(_e.blockHash); - res["logIndex"] = _e.logIndex; - res["transactionHash"] = toJS(_e.transactionHash); - res["transactionIndex"] = _e.transactionIndex; - } - else - { - res["type"] = "pending"; - res["blockNumber"] = Json::Value(Json::nullValue); - res["blockHash"] = Json::Value(Json::nullValue); - res["logIndex"] = Json::Value(Json::nullValue); - res["transactionHash"] = Json::Value(Json::nullValue); - res["transactionIndex"] = Json::Value(Json::nullValue); - } - } else { - res = toJS(_e.special); - } - return res; -} - -static Json::Value toJson(dev::eth::LocalisedLogEntries const& _es) -{ - Json::Value res(Json::arrayValue); - for (dev::eth::LocalisedLogEntry const& e: _es) - res.append(toJson(e)); - return res; -} - -static Json::Value toJson(map const& _storage) -{ - Json::Value res(Json::objectValue); - for (auto i: _storage) - res[toJS(i.first)] = toJS(i.second); - return res; -} - -static dev::eth::LogFilter toLogFilter(Json::Value const& _json) -{ - dev::eth::LogFilter filter; - if (!_json.isObject() || _json.empty()) - return filter; - - // check only !empty. it should throw exceptions if input params are incorrect - if (!_json["fromBlock"].empty()) - filter.withEarliest(jsToFixed<32>(_json["fromBlock"].asString())); - if (!_json["toBlock"].empty()) - filter.withLatest(jsToFixed<32>(_json["toBlock"].asString())); - if (!_json["address"].empty()) - { - if (_json["address"].isArray()) - for (auto i : _json["address"]) - filter.address(jsToAddress(i.asString())); - else - filter.address(jsToAddress(_json["address"].asString())); - } - if (!_json["topics"].empty()) - for (unsigned i = 0; i < _json["topics"].size(); i++) - { - if (_json["topics"][i].isArray()) - { - for (auto t: _json["topics"][i]) - if (!t.isNull()) - filter.topic(i, jsToFixed<32>(t.asString())); - } - else if (!_json["topics"][i].isNull()) // if it is anything else then string, it should and will fail - filter.topic(i, jsToFixed<32>(_json["topics"][i].asString())); - } - return filter; -} - -// TODO: this should be removed once we decide to remove backward compatibility with old log filters -static dev::eth::LogFilter toLogFilter(Json::Value const& _json, Interface const& _client) // commented to avoid warning. Uncomment once in use @ PoC-7. -{ - dev::eth::LogFilter filter; - if (!_json.isObject() || _json.empty()) - return filter; - - // check only !empty. it should throw exceptions if input params are incorrect - if (!_json["fromBlock"].empty()) - filter.withEarliest(_client.hashFromNumber(jsToBlockNumber(_json["fromBlock"].asString()))); - if (!_json["toBlock"].empty()) - filter.withLatest(_client.hashFromNumber(jsToBlockNumber(_json["toBlock"].asString()))); - if (!_json["address"].empty()) - { - if (_json["address"].isArray()) - for (auto i : _json["address"]) - filter.address(jsToAddress(i.asString())); - else - filter.address(jsToAddress(_json["address"].asString())); - } - if (!_json["topics"].empty()) - for (unsigned i = 0; i < _json["topics"].size(); i++) - { - if (_json["topics"][i].isArray()) - { - for (auto t: _json["topics"][i]) - if (!t.isNull()) - filter.topic(i, jsToFixed<32>(t.asString())); - } - else if (!_json["topics"][i].isNull()) // if it is anything else then string, it should and will fail - filter.topic(i, jsToFixed<32>(_json["topics"][i].asString())); - } - return filter; -} - -static shh::Message toMessage(Json::Value const& _json) -{ - shh::Message ret; - if (!_json["from"].empty()) - ret.setFrom(jsToPublic(_json["from"].asString())); - if (!_json["to"].empty()) - ret.setTo(jsToPublic(_json["to"].asString())); - if (!_json["payload"].empty()) - ret.setPayload(jsToBytes(_json["payload"].asString())); - return ret; -} - -static shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m, Secret _from) -{ - unsigned ttl = 50; - unsigned workToProve = 50; - shh::BuildTopic bt; - - if (!_json["ttl"].empty()) - ttl = jsToInt(_json["ttl"].asString()); - - if (!_json["workToProve"].empty()) - workToProve = jsToInt(_json["workToProve"].asString()); - - if (!_json["topics"].empty()) - for (auto i: _json["topics"]) - { - if (i.isArray()) - { - for (auto j: i) - if (!j.isNull()) - bt.shift(jsToBytes(j.asString())); - } - else if (!i.isNull()) // if it is anything else then string, it should and will fail - bt.shift(jsToBytes(i.asString())); - } - - return _m.seal(_from, bt, ttl, workToProve); -} - -static pair toWatch(Json::Value const& _json) -{ - shh::BuildTopic bt; - Public to; - - if (!_json["to"].empty()) - to = jsToPublic(_json["to"].asString()); - - if (!_json["topics"].empty()) - for (auto i: _json["topics"]) - bt.shift(jsToBytes(i.asString())); - - return make_pair(bt, to); -} - -static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m) -{ - Json::Value res; - res["hash"] = toJS(_h); - res["expiry"] = toJS(_e.expiry()); - res["sent"] = toJS(_e.sent()); - res["ttl"] = toJS(_e.ttl()); - res["workProved"] = toJS(_e.workProved()); - res["topics"] = Json::Value(Json::arrayValue); - for (auto const& t: _e.topic()) - res["topics"].append(toJS(t)); - res["payload"] = toJS(_m.payload()); - res["from"] = toJS(_m.from()); - res["to"] = toJS(_m.to()); - return res; -} - WebThreeStubServerBase::WebThreeStubServerBase(AbstractServerConnector& _conn, std::shared_ptr const& _ethAccounts, vector const& _sshAccounts): AbstractWebThreeStubServer(_conn), m_ethAccounts(_ethAccounts) @@ -468,7 +174,6 @@ string WebThreeStubServerBase::eth_getBlockTransactionCountByHash(string const& } } - string WebThreeStubServerBase::eth_getBlockTransactionCountByNumber(string const& _blockNumber) { try @@ -517,42 +222,12 @@ string WebThreeStubServerBase::eth_getCode(string const& _address, string const& } } -static TransactionSkeleton toTransaction(Json::Value const& _json) -{ - TransactionSkeleton ret; - if (!_json.isObject() || _json.empty()) - return ret; - - if (!_json["from"].empty()) - ret.from = jsToAddress(_json["from"].asString()); - if (!_json["to"].empty() && _json["to"].asString() != "0x") - ret.to = jsToAddress(_json["to"].asString()); - else - ret.creation = true; - - if (!_json["value"].empty()) - ret.value = jsToU256(_json["value"].asString()); - - if (!_json["gas"].empty()) - ret.gas = jsToU256(_json["gas"].asString()); - - if (!_json["gasPrice"].empty()) - ret.gasPrice = jsToU256(_json["gasPrice"].asString()); - - if (!_json["data"].empty()) // ethereum.js has preconstructed the data array - ret.data = jsToBytes(_json["data"].asString()); - - if (!_json["code"].empty()) - ret.data = jsToBytes(_json["code"].asString()); - return ret; -} - string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json) { try { string ret; - TransactionSkeleton t = toTransaction(_json); + TransactionSkeleton t = toTransactionSkeleton(_json); if (!t.from) t.from = m_ethAccounts->defaultTransactAccount(); @@ -578,7 +253,7 @@ string WebThreeStubServerBase::eth_signTransaction(Json::Value const& _json) try { string ret; - TransactionSkeleton t = toTransaction(_json); + TransactionSkeleton t = toTransactionSkeleton(_json); if (!t.from) t.from = m_ethAccounts->defaultTransactAccount(); @@ -627,7 +302,7 @@ string WebThreeStubServerBase::eth_call(Json::Value const& _json, string const& { try { - TransactionSkeleton t = toTransaction(_json); + TransactionSkeleton t = toTransactionSkeleton(_json); if (!t.from) t.from = m_ethAccounts->defaultTransactAccount(); // if (!m_accounts->isRealAccount(t.from)) @@ -798,34 +473,32 @@ string WebThreeStubServerBase::eth_compileSerpent(string const& _source) return res; } +#define ADMIN requires(_session, Priviledge::Admin) + bool WebThreeStubServerBase::admin_web3_setVerbosity(int _v, string const& _session) { - if (!isAdmin(_session)) - return false; + ADMIN; g_logVerbosity = _v; return true; } bool WebThreeStubServerBase::admin_net_start(std::string const& _session) { - if (!isAdmin(_session)) - return false; + ADMIN; network()->startNetwork(); return true; } bool WebThreeStubServerBase::admin_net_stop(std::string const& _session) { - if (!isAdmin(_session)) - return false; + ADMIN; network()->stopNetwork(); return true; } bool WebThreeStubServerBase::admin_net_connect(std::string const& _node, std::string const& _session) { - if (!isAdmin(_session)) - return false; + ADMIN; p2p::NodeId id; bi::tcp::endpoint ep; if (_node.substr(0, 8) == "enode://" && _node.find('@') == 136) @@ -839,25 +512,9 @@ bool WebThreeStubServerBase::admin_net_connect(std::string const& _node, std::st return true; } -Json::Value toJson(p2p::PeerSessionInfo const& _p) -{ - Json::Value ret; - ret["id"] = _p.id.hex(); - ret["clientVersion"] = _p.clientVersion; - ret["host"] = _p.host; - ret["port"] = _p.port; - ret["lastPing"] = (int)chrono::duration_cast(_p.lastPing).count(); - for (auto const& i: _p.notes) - ret["notes"][i.first] = i.second; - for (auto const& i: _p.caps) - ret["caps"][i.first] = (unsigned)i.second; - return ret; -} - Json::Value WebThreeStubServerBase::admin_net_peers(std::string const& _session) { - if (!isAdmin(_session)) - return false; + ADMIN; Json::Value ret; for (p2p::PeerSessionInfo const& i: network()->peers()) ret.append(toJson(i)); @@ -866,8 +523,7 @@ Json::Value WebThreeStubServerBase::admin_net_peers(std::string const& _session) bool WebThreeStubServerBase::admin_eth_setMining(bool _on, std::string const& _session) { - if (!isAdmin(_session)) - return false; + ADMIN; if (_on) client()->startMining(); else @@ -970,8 +626,6 @@ bool WebThreeStubServerBase::eth_uninstallFilter(string const& _filterId) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } - - } Json::Value WebThreeStubServerBase::eth_getFilterChanges(string const& _filterId) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index aaf0a6096..d3e16d0f4 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" @@ -58,6 +59,26 @@ public: virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) = 0; }; +enum class Priviledge +{ + Admin +}; + +} + +namespace std +{ + +template<> struct hash +{ + size_t operator()(dev::Priviledge _value) const { return (size_t)_value; } +}; + +} + +namespace dev +{ + /** * @brief JSON-RPC api implementation * @todo split these up according to subprotocol (eth, shh, db, p2p, web3) and make it /very/ clear about how to add other subprotocols. @@ -147,25 +168,28 @@ public: virtual Json::Value admin_eth_blockQueueStatus(std::string const& _session) { (void)_session; return Json::Value(); } virtual bool admin_eth_setAskPrice(std::string const& _wei, std::string const& _session) { (void)_wei; (void)_session; return false; } virtual bool admin_eth_setBidPrice(std::string const& _wei, std::string const& _session) { (void)_wei; (void)_session; return false; } - virtual bool admin_eth_setReferencePrice(std::string const& _wei, std::string const& _session) { (void)_wei; (void)_session; return false; } - virtual bool admin_eth_setPriority(int _percent, std::string const& _session) { (void)_percent; (void)_session; return false; } virtual Json::Value admin_eth_findBlock(std::string const& _blockHash, std::string const& _session) { (void)_blockHash; (void)_session; return Json::Value(); } virtual std::string admin_eth_blockQueueFirstUnknown(std::string const& _session) { (void)_session; return ""; } virtual bool admin_eth_blockQueueRetryUnknown(std::string const& _session) { (void)_session; return false; } virtual Json::Value admin_eth_allAccounts(std::string const& _session) { (void)_session; return Json::Value(); } virtual Json::Value admin_eth_newAccount(const Json::Value& _info, std::string const& _session) { (void)_info; (void)_session; return Json::Value(); } - virtual bool admin_eth_setSigningKey(std::string const& _uuidOrAddress, std::string const& _session) { (void)_uuidOrAddress; (void)_session; return false; } virtual bool admin_eth_setMiningBenefactor(std::string const& _uuidOrAddress, std::string const& _session) { (void)_uuidOrAddress; (void)_session; return false; } virtual Json::Value admin_eth_inspect(std::string const& _address, std::string const& _session) { (void)_address; (void)_session; return Json::Value(); } virtual Json::Value admin_eth_reprocess(std::string const& _blockNumberOrHash, std::string const& _session) { (void)_blockNumberOrHash; (void)_session; return Json::Value(); } - virtual Json::Value admin_eth_vmTrace(std::string const& _blockNumberOrHash, std::string const& _txIndex, std::string const& _session) { (void)_blockNumberOrHash; (void)_txIndex; (void)_session; return Json::Value(); } - virtual Json::Value admin_eth_getReceiptByHashAndIndex(std::string const& _blockNumberOrHash, std::string const& _txIndex, std::string const& _session) { (void)_blockNumberOrHash; (void)_txIndex; (void)_session; return Json::Value(); } + virtual Json::Value admin_eth_vmTrace(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) { (void)_blockNumberOrHash; (void)_txIndex; (void)_session; return Json::Value(); } + virtual Json::Value admin_eth_getReceiptByHashAndIndex(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) { (void)_blockNumberOrHash; (void)_txIndex; (void)_session; return Json::Value(); } + + // TODO REMOVE + virtual bool admin_eth_setReferencePrice(std::string const& _wei, std::string const& _session) { (void)_wei; (void)_session; return false; } + virtual bool admin_eth_setPriority(int _percent, std::string const& _session) { (void)_percent; (void)_session; return false; } + virtual bool admin_eth_setSigningKey(std::string const& _uuidOrAddress, std::string const& _session) { (void)_uuidOrAddress; (void)_session; return false; } void setIdentities(std::vector const& _ids); std::map const& ids() const { return m_shhIds; } protected: - virtual bool isAdmin(std::string const& _session) const { (void)_session; return false; } + void requires(std::string const& _session, Priviledge _l) const { if (!hasPriviledgeLevel(_session, _l)) throw jsonrpc::JsonRpcException("Invalid priviledges"); } + virtual bool hasPriviledgeLevel(std::string const& _session, Priviledge _l) const { (void)_session; (void)_l; return false; } virtual dev::eth::Interface* client() = 0; virtual std::shared_ptr face() = 0; diff --git a/libweb3jsonrpc/abstractwebthreestubserver.h b/libweb3jsonrpc/abstractwebthreestubserver.h index 6b94c31da..82864a61a 100644 --- a/libweb3jsonrpc/abstractwebthreestubserver.h +++ b/libweb3jsonrpc/abstractwebthreestubserver.h @@ -96,8 +96,8 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerbindAndAddMethod(jsonrpc::Procedure("admin_eth_setMiningBenefactor", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setMiningBenefactorI); this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_inspect", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_inspectI); this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_reprocess", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_reprocessI); - this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_vmTrace", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_vmTraceI); - this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_getReceiptByHashAndIndex", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_getReceiptByHashAndIndexI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_vmTrace", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_vmTraceI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_getReceiptByHashAndIndex", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_getReceiptByHashAndIndexI); } inline virtual void web3_sha3I(const Json::Value &request, Json::Value &response) @@ -455,11 +455,11 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServeradmin_eth_vmTrace(request[0u].asString(), request[1u].asString(), request[2u].asString()); + response = this->admin_eth_vmTrace(request[0u].asString(), request[1u].asInt(), request[2u].asString()); } inline virtual void admin_eth_getReceiptByHashAndIndexI(const Json::Value &request, Json::Value &response) { - response = this->admin_eth_getReceiptByHashAndIndex(request[0u].asString(), request[1u].asString(), request[2u].asString()); + response = this->admin_eth_getReceiptByHashAndIndex(request[0u].asString(), request[1u].asInt(), request[2u].asString()); } virtual std::string web3_sha3(const std::string& param1) = 0; virtual std::string web3_clientVersion() = 0; @@ -545,8 +545,8 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer #include #include +#include "BuildInfo.h" using namespace std; using namespace dev; using namespace dev::p2p; @@ -72,6 +73,16 @@ WebThreeDirect::~WebThreeDirect() m_ethereum.reset(); } +std::string WebThreeDirect::composeClientVersion(std::string const& _client, std::string const& _clientName) +{ +#if ETH_EVMJIT + char const* jit = "-JIT"; +#else + char const* jit = ""; +#endif + return _client + "v" + dev::Version + "-" + string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) + (ETH_CLEAN_REPO ? "" : "*") + "/" + _clientName + "/" DEV_QUOTED(ETH_BUILD_TYPE) "-" DEV_QUOTED(ETH_BUILD_PLATFORM) + jit; +} + p2p::NetworkPreferences const& WebThreeDirect::networkPreferences() const { return m_net.networkPreferences(); diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h index ece83abb8..6eefa6a4b 100644 --- a/libwebthree/WebThree.h +++ b/libwebthree/WebThree.h @@ -129,6 +129,8 @@ public: // Misc stuff: + static std::string composeClientVersion(std::string const& _client, std::string const& _name); + std::string const& clientVersion() const { return m_clientVersion; } void setClientVersion(std::string const& _name) { m_clientVersion = _name; } diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index e2e554cb9..4f5eff8b6 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -296,7 +296,9 @@ ExecutionResult MixClient::execution(unsigned _index) const State MixClient::asOf(h256 const& _block) const { ReadGuard l(x_state); - return State(m_stateDB, bc(), _block); + State ret(m_stateDB); + ret.populateFromChain(bc(), _block); + return ret; } void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto) diff --git a/neth/main.cpp b/neth/main.cpp index a6e661d2e..a55c3d559 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -577,10 +577,11 @@ int main(int argc, char** argv) #if ETH_JSONRPC || !ETH_TRUE shared_ptr jsonrpcServer; unique_ptr jsonrpcConnector; + KeyManager km; if (jsonrpc > -1) { jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, vector({us})), vector({us}))); + jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, vector({us})), vector({us})), km); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); } diff --git a/test/libweb3jsonrpc/webthreestubclient.h b/test/libweb3jsonrpc/webthreestubclient.h index 5c74e5a46..175f1958c 100644 --- a/test/libweb3jsonrpc/webthreestubclient.h +++ b/test/libweb3jsonrpc/webthreestubclient.h @@ -884,7 +884,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - Json::Value admin_eth_vmTrace(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException) + Json::Value admin_eth_vmTrace(const std::string& param1, int param2, const std::string& param3) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); @@ -896,7 +896,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - Json::Value admin_eth_getReceiptByHashAndIndex(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException) + Json::Value admin_eth_getReceiptByHashAndIndex(const std::string& param1, int param2, const std::string& param3) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); From c77ab101c46cb637eabfcafa4c3840ae9bae2fd3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 15 Jun 2015 19:26:34 +0800 Subject: [PATCH 58/69] inspect in admin interface. --- libweb3jsonrpc/WebThreeStubServer.cpp | 38 ++++++++++++++++----------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/libweb3jsonrpc/WebThreeStubServer.cpp b/libweb3jsonrpc/WebThreeStubServer.cpp index f62fdd32a..adef51033 100644 --- a/libweb3jsonrpc/WebThreeStubServer.cpp +++ b/libweb3jsonrpc/WebThreeStubServer.cpp @@ -35,6 +35,20 @@ using namespace std; using namespace dev; using namespace dev::eth; +bool isHex(std::string const& _s) +{ + unsigned i = (_s.size() >= 2 && _s.substr(0, 2) == "0x") ? 2 : 0; + for (; i < _s.size(); ++i) + if (fromHex(_s[i], WhenError::DontThrow) == -1) + return false; + return true; +} + +template bool isHash(std::string const& _hash) +{ + return (_hash.size() == T::size * 2 || (_hash.size() == T::size * 2 + 2 && _hash.substr(0, 2) == "0x")) && isHex(_hash); +} + WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, shared_ptr const& _ethAccounts, std::vector const& _shhAccounts, KeyManager& _keyMan, dev::eth::TrivialGasPricer& _gp): WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts), m_web3(_web3), @@ -198,22 +212,16 @@ bool WebThreeStubServer::admin_eth_setMiningBenefactor(std::string const& _uuidO Json::Value WebThreeStubServer::admin_eth_inspect(std::string const& _address, std::string const& _session) { ADMIN; - (void)_address; - return {}; -} + if (!isHash
(_address)) + throw jsonrpc::JsonRpcException("Invalid address given."); -bool isHex(std::string const& _s) -{ - unsigned i = (_s.size() >= 2 && _s.substr(0, 2) == "0x") ? 2 : 0; - for (; i < _s.size(); ++i) - if (fromHex(_s[i], WhenError::DontThrow) == -1) - return false; - return true; -} - -template bool isHash(std::string const& _hash) -{ - return (_hash.size() == T::size * 2 || (_hash.size() == T::size * 2 + 2 && _hash.substr(0, 2) == "0x")) && isHex(_hash); + Json::Value ret; + auto h = Address(fromHex(_address)); + ret["storage"] = toJson(m_web3.ethereum()->storageAt(h, PendingBlock)); + ret["balance"] = toJS(m_web3.ethereum()->balanceAt(h, PendingBlock)); + ret["nonce"] = toJS(m_web3.ethereum()->countAt(h, PendingBlock)); + ret["code"] = toJS(m_web3.ethereum()->codeAt(h, PendingBlock)); + return ret; } h256 WebThreeStubServer::blockHash(std::string const& _blockNumberOrHash) const From 45858c0a7838ee1b620eb4fd9210e1e0b84b6b4d Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 15 Jun 2015 13:28:58 +0200 Subject: [PATCH 59/69] OpenCL in MacosX does not like static variables Should fix #2172 --- libethash-cl/ethash_cl_miner.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp index 6c2f8269a..b160cdd94 100644 --- a/libethash-cl/ethash_cl_miner.cpp +++ b/libethash-cl/ethash_cl_miner.cpp @@ -429,7 +429,8 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook }; queue pending; - static uint32_t const c_zero = 0; + // this can't be a static because in MacOSX OpenCL implementation a segfault occurs when a static is passed to OpenCL functions + uint32_t const c_zero = 0; // update header constant buffer m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header); From 7d09d87d16d29e0306056d12414defdf0f9fc7fe Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 9 Jun 2015 14:26:08 +0200 Subject: [PATCH 60/69] Distinction between storage pointer and storage ref and type checking for conversion between storage and memory. --- libsolidity/AST.cpp | 38 +++-- libsolidity/AST.h | 80 +++++---- libsolidity/Compiler.cpp | 45 +++-- libsolidity/Compiler.h | 5 +- libsolidity/ExpressionCompiler.cpp | 29 +++- libsolidity/InterfaceHandler.cpp | 2 +- libsolidity/LValue.cpp | 6 +- libsolidity/NameAndTypeResolver.cpp | 14 +- libsolidity/Types.cpp | 156 +++++++++++++----- libsolidity/Types.h | 103 ++++++++---- .../SolidityNameAndTypeResolution.cpp | 87 ++++++++++ 11 files changed, 414 insertions(+), 151 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 5be23b7c8..dbeec858e 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -488,7 +488,7 @@ string FunctionDefinition::externalSignature() const bool VariableDeclaration::isLValue() const { // External function parameters and constant declared variables are Read-Only - return !isExternalFunctionParameter() && !m_isConstant; + return !isExternalCallableParameter() && !m_isConstant; } void VariableDeclaration::checkTypeRequirements() @@ -516,39 +516,41 @@ void VariableDeclaration::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Assignment necessary for type detection.")); m_value->checkTypeRequirements(nullptr); - TypePointer type = m_value->getType(); - if (type->getCategory() == Type::Category::IntegerConstant) - { - auto intType = dynamic_pointer_cast(type)->getIntegerType(); - if (!intType) - BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString() + ".")); - type = intType; - } + TypePointer const& type = m_value->getType(); + if ( + type->getCategory() == Type::Category::IntegerConstant && + !dynamic_pointer_cast(type)->getIntegerType() + ) + BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString() + ".")); else if (type->getCategory() == Type::Category::Void) BOOST_THROW_EXCEPTION(createTypeError("Variable cannot have void type.")); - m_type = type; + m_type = type->mobileType(); } if (m_isStateVariable && getVisibility() >= Visibility::Public && !FunctionType(*this).externalType()) BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables.")); } -bool VariableDeclaration::isFunctionParameter() const +bool VariableDeclaration::isCallableParameter() const { - auto const* function = dynamic_cast(getScope()); - if (!function) + auto const* callable = dynamic_cast(getScope()); + if (!callable) return false; - for (auto const& variable: function->getParameters() + function->getReturnParameters()) + for (auto const& variable: callable->getParameters()) if (variable.get() == this) return true; + if (callable->getReturnParameterList()) + for (auto const& variable: callable->getReturnParameterList()->getParameters()) + if (variable.get() == this) + return true; return false; } -bool VariableDeclaration::isExternalFunctionParameter() const +bool VariableDeclaration::isExternalCallableParameter() const { - auto const* function = dynamic_cast(getScope()); - if (!function || function->getVisibility() != Declaration::Visibility::External) + auto const* callable = dynamic_cast(getScope()); + if (!callable || callable->getVisibility() != Declaration::Visibility::External) return false; - for (auto const& variable: function->getParameters()) + for (auto const& variable: callable->getParameters()) if (variable.get() == this) return true; return false; diff --git a/libsolidity/AST.h b/libsolidity/AST.h index b3984840f..5f1c1281c 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -406,13 +406,43 @@ private: std::vector> m_parameters; }; -class FunctionDefinition: public Declaration, public VariableScope, public Documented, public ImplementationOptional +/** + * Base class for all nodes that define function-like objects, i.e. FunctionDefinition, + * EventDefinition and ModifierDefinition. + */ +class CallableDeclaration: public Declaration, public VariableScope +{ +public: + CallableDeclaration( + SourceLocation const& _location, + ASTPointer const& _name, + Declaration::Visibility _visibility, + ASTPointer const& _parameters, + ASTPointer const& _returnParameters = ASTPointer() + ): + Declaration(_location, _name, _visibility), + m_parameters(_parameters), + m_returnParameters(_returnParameters) + { + } + + std::vector> const& getParameters() const { return m_parameters->getParameters(); } + ParameterList const& getParameterList() const { return *m_parameters; } + ASTPointer const& getReturnParameterList() const { return m_returnParameters; } + +protected: + ASTPointer m_parameters; + ASTPointer m_returnParameters; +}; + +class FunctionDefinition: public CallableDeclaration, public Documented, public ImplementationOptional { public: FunctionDefinition( SourceLocation const& _location, ASTPointer const& _name, - Declaration::Visibility _visibility, bool _isConstructor, + Declaration::Visibility _visibility, + bool _isConstructor, ASTPointer const& _documentation, ASTPointer const& _parameters, bool _isDeclaredConst, @@ -420,14 +450,12 @@ public: ASTPointer const& _returnParameters, ASTPointer const& _body ): - Declaration(_location, _name, _visibility), + CallableDeclaration(_location, _name, _visibility, _parameters, _returnParameters), Documented(_documentation), ImplementationOptional(_body != nullptr), m_isConstructor(_isConstructor), - m_parameters(_parameters), m_isDeclaredConst(_isDeclaredConst), m_functionModifiers(_modifiers), - m_returnParameters(_returnParameters), m_body(_body) {} @@ -437,10 +465,7 @@ public: bool isConstructor() const { return m_isConstructor; } bool isDeclaredConst() const { return m_isDeclaredConst; } std::vector> const& getModifiers() const { return m_functionModifiers; } - std::vector> const& getParameters() const { return m_parameters->getParameters(); } - ParameterList const& getParameterList() const { return *m_parameters; } std::vector> const& getReturnParameters() const { return m_returnParameters->getParameters(); } - ASTPointer const& getReturnParameterList() const { return m_returnParameters; } Block const& getBody() const { return *m_body; } virtual bool isVisibleInContract() const override @@ -460,10 +485,8 @@ public: private: bool m_isConstructor; - ASTPointer m_parameters; bool m_isDeclaredConst; std::vector> m_functionModifiers; - ASTPointer m_returnParameters; ASTPointer m_body; }; @@ -512,9 +535,9 @@ public: void checkTypeRequirements(); bool isLocalVariable() const { return !!dynamic_cast(getScope()); } /// @returns true if this variable is a parameter or return parameter of a function. - bool isFunctionParameter() const; + bool isCallableParameter() const; /// @returns true if this variable is a parameter (not return parameter) of an external function. - bool isExternalFunctionParameter() const; + bool isExternalCallableParameter() const; bool isStateVariable() const { return m_isStateVariable; } bool isIndexed() const { return m_isIndexed; } bool isConstant() const { return m_isConstant; } @@ -537,22 +560,24 @@ private: /** * Definition of a function modifier. */ -class ModifierDefinition: public Declaration, public VariableScope, public Documented +class ModifierDefinition: public CallableDeclaration, public Documented { public: ModifierDefinition(SourceLocation const& _location, - ASTPointer const& _name, - ASTPointer const& _documentation, - ASTPointer const& _parameters, - ASTPointer const& _body): - Declaration(_location, _name), Documented(_documentation), - m_parameters(_parameters), m_body(_body) {} + ASTPointer const& _name, + ASTPointer const& _documentation, + ASTPointer const& _parameters, + ASTPointer const& _body + ): + CallableDeclaration(_location, _name, Visibility::Default, _parameters), + Documented(_documentation), + m_body(_body) + { + } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - std::vector> const& getParameters() const { return m_parameters->getParameters(); } - ParameterList const& getParameterList() const { return *m_parameters; } Block const& getBody() const { return *m_body; } virtual TypePointer getType(ContractDefinition const* = nullptr) const override; @@ -560,7 +585,6 @@ public: void checkTypeRequirements(); private: - ASTPointer m_parameters; ASTPointer m_body; }; @@ -591,7 +615,7 @@ private: /** * Definition of a (loggable) event. */ -class EventDefinition: public Declaration, public VariableScope, public Documented +class EventDefinition: public CallableDeclaration, public Documented { public: EventDefinition( @@ -601,16 +625,15 @@ public: ASTPointer const& _parameters, bool _anonymous = false ): - Declaration(_location, _name), + CallableDeclaration(_location, _name, Visibility::Default, _parameters), Documented(_documentation), - m_parameters(_parameters), - m_anonymous(_anonymous){} + m_anonymous(_anonymous) + { + } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - std::vector> const& getParameters() const { return m_parameters->getParameters(); } - ParameterList const& getParameterList() const { return *m_parameters; } bool isAnonymous() const { return m_anonymous; } virtual TypePointer getType(ContractDefinition const* = nullptr) const override @@ -621,7 +644,6 @@ public: void checkTypeRequirements(); private: - ASTPointer m_parameters; bool m_anonymous = false; }; diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index b2b8bfa4c..b55ae7d36 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -170,11 +170,17 @@ void Compiler::appendConstructor(FunctionDefinition const& _constructor) if (argumentSize > 0) { - m_context << u256(argumentSize); + CompilerUtils(m_context).fetchFreeMemoryPointer(); + m_context << u256(argumentSize) << eth::Instruction::DUP1; m_context.appendProgramSize(); - m_context << u256(CompilerUtils::dataStartOffset); // copy it to byte four as expected for ABI calls - m_context << eth::Instruction::CODECOPY; - appendCalldataUnpacker(FunctionType(_constructor).getParameterTypes(), true); + m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY; + m_context << eth::Instruction::ADD; + CompilerUtils(m_context).storeFreeMemoryPointer(); + appendCalldataUnpacker( + FunctionType(_constructor).getParameterTypes(), + true, + CompilerUtils::freeMemoryPointer + 0x20 + ); } _constructor.accept(*this); } @@ -232,26 +238,35 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) } } -void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory) +void Compiler::appendCalldataUnpacker( + TypePointers const& _typeParameters, + bool _fromMemory, + u256 _startOffset +) { // We do not check the calldata size, everything is zero-paddedd - m_context << u256(CompilerUtils::dataStartOffset); + if (_startOffset == u256(-1)) + _startOffset = u256(CompilerUtils::dataStartOffset); + + m_context << _startOffset; for (TypePointer const& type: _typeParameters) { - if (type->getCategory() == Type::Category::Array) + switch (type->getCategory()) + { + case Type::Category::Array: { auto const& arrayType = dynamic_cast(*type); if (arrayType.location() == ReferenceType::Location::CallData) { + solAssert(!_fromMemory, ""); if (type->isDynamicallySized()) { // put on stack: data_pointer length CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory); // stack: data_offset next_pointer //@todo once we support nested arrays, this offset needs to be dynamic. - m_context << eth::Instruction::SWAP1 << u256(CompilerUtils::dataStartOffset); - m_context << eth::Instruction::ADD; + m_context << eth::Instruction::SWAP1 << _startOffset << eth::Instruction::ADD; // stack: next_pointer data_pointer // retrieve length CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); @@ -268,13 +283,15 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool else { solAssert(arrayType.location() == ReferenceType::Location::Memory, ""); - CompilerUtils(m_context).fetchFreeMemoryPointer(); - CompilerUtils(m_context).storeInMemoryDynamic(*type); - CompilerUtils(m_context).storeFreeMemoryPointer(); + // compute data pointer + m_context << eth::Instruction::DUP1 << _startOffset << eth::Instruction::ADD; + if (!_fromMemory) + solAssert(false, "Not yet implemented."); + m_context << eth::Instruction::SWAP1 << u256(0x20) << eth::Instruction::ADD; } + break; } - else - { + default: solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true); } diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 670c74673..589efbff2 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -73,7 +73,10 @@ private: void appendFunctionSelector(ContractDefinition const& _contract); /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers. /// From memory if @a _fromMemory is true, otherwise from call data. - void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false); + /// Expects source offset on the stack. + void appendCalldataUnpacker(TypePointers const& _typeParameters, + bool _fromMemory = false, + u256 _startOffset = u256(-1)); void appendReturnValuePacker(TypePointers const& _typeParameters); void registerStateVariables(ContractDefinition const& _contract); diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index d9b6da14e..811ee60ec 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -204,7 +204,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con } else if (targetTypeCategory == Type::Category::Enum) // just clean - appendTypeConversion(_typeOnStack, *_typeOnStack.getRealType(), true); + appendTypeConversion(_typeOnStack, *_typeOnStack.mobileType(), true); else { solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, ""); @@ -232,6 +232,25 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con } } break; + case Type::Category::Array: + //@TODO + break; + case Type::Category::Struct: + { + solAssert(targetTypeCategory == stackTypeCategory, ""); + auto& targetType = dynamic_cast(_targetType); + auto& stackType = dynamic_cast(_typeOnStack); + solAssert( + targetType.location() == ReferenceType::Location::Storage && + stackType.location() == ReferenceType::Location::Storage, + "Non-storage structs not yet implemented." + ); + solAssert( + targetType.isPointer(), + "Type conversion to non-pointer struct requested." + ); + break; + } default: // All other types should not be convertible to non-equal types. solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); @@ -771,7 +790,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) TypeType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); solAssert( !type.getMembers().membersByName(_memberAccess.getMemberName()).empty(), - "Invalid member access to " + type.toString() + "Invalid member access to " + type.toString(false) ); if (dynamic_cast(type.getActualType().get())) @@ -1101,7 +1120,7 @@ void ExpressionCompiler::appendExternalFunctionCall( bool manualFunctionId = (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode) && !_arguments.empty() && - _arguments.front()->getType()->getRealType()->getCalldataEncodedSize(false) == + _arguments.front()->getType()->mobileType()->getCalldataEncodedSize(false) == CompilerUtils::dataStartOffset; if (manualFunctionId) { @@ -1225,7 +1244,7 @@ void ExpressionCompiler::encodeToMemory( TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes; solAssert(targetTypes.size() == _givenTypes.size(), ""); for (TypePointer& t: targetTypes) - t = t->getRealType()->externalType(); + t = t->mobileType()->externalType(); // Stack during operation: // ... ... @@ -1325,7 +1344,7 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, appendTypeMoveToMemory(_expectedType); } else - appendTypeMoveToMemory(*_expression.getType()->getRealType()); + appendTypeMoveToMemory(*_expression.getType()->mobileType()); } void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression) diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index a9d54cdc6..23b7ff82f 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -96,7 +96,7 @@ unique_ptr InterfaceHandler::getABIInterface(ContractDefinition const& _ { Json::Value input; input["name"] = p->getName(); - input["type"] = p->getType()->toString(); + input["type"] = p->getType()->toString(true); input["indexed"] = p->isIndexed(); params.append(input); } diff --git a/libsolidity/LValue.cpp b/libsolidity/LValue.cpp index b684e55a1..1acf0a3e8 100644 --- a/libsolidity/LValue.cpp +++ b/libsolidity/LValue.cpp @@ -198,7 +198,11 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc // stack layout: source_ref source_offset target_ref target_offset // note that we have structs, so offsets should be zero and are ignored auto const& structType = dynamic_cast(m_dataType); - solAssert(structType == _sourceType, "Struct assignment with conversion."); + solAssert( + structType.structDefinition() == + dynamic_cast(_sourceType).structDefinition(), + "Struct assignment with conversion." + ); for (auto const& member: structType.getMembers()) { // assign each member that is not a mapping diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 22232014f..e60797967 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -431,7 +431,7 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable) // They default to memory for function parameters and storage for local variables. if (auto ref = dynamic_cast(type.get())) { - if (_variable.isExternalFunctionParameter()) + if (_variable.isExternalCallableParameter()) { // force location of external function parameters (not return) to calldata if (loc != Location::Default) @@ -439,9 +439,9 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable) "Location has to be calldata for external functions " "(remove the \"memory\" or \"storage\" keyword)." )); - type = ref->copyForLocation(ReferenceType::Location::CallData); + type = ref->copyForLocation(ReferenceType::Location::CallData, true); } - else if (_variable.isFunctionParameter() && _variable.getScope()->isPublic()) + else if (_variable.isCallableParameter() && _variable.getScope()->isPublic()) { // force locations of public or external function (return) parameters to memory if (loc == VariableDeclaration::Location::Storage) @@ -449,16 +449,18 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable) "Location has to be memory for publicly visible functions " "(remove the \"storage\" keyword)." )); - type = ref->copyForLocation(ReferenceType::Location::Memory); + type = ref->copyForLocation(ReferenceType::Location::Memory, true); } else { if (loc == Location::Default) - loc = _variable.isFunctionParameter() ? Location::Memory : Location::Storage; + loc = _variable.isCallableParameter() ? Location::Memory : Location::Storage; + bool isPointer = !_variable.isStateVariable(); type = ref->copyForLocation( loc == Location::Memory ? ReferenceType::Location::Memory : - ReferenceType::Location::Storage + ReferenceType::Location::Storage, + isPointer ); } } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 6f16f5193..65acba844 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -179,6 +179,8 @@ TypePointer Type::fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType TypePointer valueType = _valueType.toType(); if (!valueType) BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name.")); + // Convert value type to storage reference. + valueType = ReferenceType::copyForLocationIfReference(ReferenceType::Location::Storage, valueType); return make_shared(keyType, valueType); } @@ -288,7 +290,7 @@ bool IntegerType::operator==(Type const& _other) const return other.m_bits == m_bits && other.m_modifier == m_modifier; } -string IntegerType::toString() const +string IntegerType::toString(bool) const { if (isAddress()) return "address"; @@ -488,7 +490,7 @@ bool IntegerConstantType::operator==(Type const& _other) const return m_value == dynamic_cast(_other).m_value; } -string IntegerConstantType::toString() const +string IntegerConstantType::toString(bool) const { return "int_const " + m_value.str(); } @@ -508,10 +510,10 @@ u256 IntegerConstantType::literalValue(Literal const*) const return value; } -TypePointer IntegerConstantType::getRealType() const +TypePointer IntegerConstantType::mobileType() const { auto intType = getIntegerType(); - solAssert(!!intType, "getRealType called with invalid integer constant " + toString()); + solAssert(!!intType, "mobileType called with invalid integer constant " + toString(false)); return intType; } @@ -668,21 +670,65 @@ TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const return _operator == Token::Delete ? make_shared() : TypePointer(); } +TypePointer ReferenceType::copyForLocationIfReference(Location _location, TypePointer const& _type) +{ + if (auto type = dynamic_cast(_type.get())) + return type->copyForLocation(_location, false); + return _type; +} + +TypePointer ReferenceType::copyForLocationIfReference(TypePointer const& _type) const +{ + return copyForLocationIfReference(m_location, _type); +} + +string ReferenceType::stringForReferencePart() const +{ + switch (m_location) + { + case Location::Storage: + return string("storage ") + (m_isPointer ? "pointer" : "ref"); + case Location::CallData: + return "calldata"; + case Location::Memory: + return "memory"; + } +} + bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const { if (_convertTo.getCategory() != getCategory()) return false; auto& convertTo = dynamic_cast(_convertTo); - // let us not allow assignment to memory arrays for now - if (convertTo.location() != Location::Storage) - return false; if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString()) return false; - if (!getBaseType()->isImplicitlyConvertibleTo(*convertTo.getBaseType())) + // memory/calldata to storage can be converted, but only to a direct storage reference + if (convertTo.location() == Location::Storage && location() != Location::Storage && convertTo.isPointer()) return false; - if (convertTo.isDynamicallySized()) + if (convertTo.location() == Location::CallData && location() != convertTo.location()) + return false; + if (convertTo.location() == Location::Storage && !convertTo.isPointer()) + { + // Less restrictive conversion, since we need to copy anyway. + if (!getBaseType()->isImplicitlyConvertibleTo(*convertTo.getBaseType())) + return false; + if (convertTo.isDynamicallySized()) + return true; + return !isDynamicallySized() && convertTo.getLength() >= getLength(); + } + else + { + // Require that the base type is the same, not only convertible. + // This disallows assignment of nested arrays from storage to memory for now. + if (*getBaseType() != *convertTo.getBaseType()) + return false; + if (isDynamicallySized() != convertTo.isDynamicallySized()) + return false; + // We also require that the size is the same. + if (!isDynamicallySized() && getLength() != convertTo.getLength()) + return false; return true; - return !isDynamicallySized() && convertTo.getLength() >= getLength(); + } } TypePointer ArrayType::unaryOperatorResult(Token::Value _operator) const @@ -698,7 +744,7 @@ bool ArrayType::operator==(Type const& _other) const return false; ArrayType const& other = dynamic_cast(_other); if ( - other.m_location != m_location || + !ReferenceType::operator==(other) || other.isByteArray() != isByteArray() || other.isString() != isString() || other.isDynamicallySized() != isDynamicallySized() @@ -751,16 +797,23 @@ unsigned ArrayType::getSizeOnStack() const return 1; } -string ArrayType::toString() const +string ArrayType::toString(bool _short) const { + string ret; if (isString()) - return "string"; + ret = "string"; else if (isByteArray()) - return "bytes"; - string ret = getBaseType()->toString() + "["; - if (!isDynamicallySized()) - ret += getLength().str(); - return ret + "]"; + ret = "bytes"; + else + { + ret = getBaseType()->toString(_short) + "["; + if (!isDynamicallySized()) + ret += getLength().str(); + ret += "]"; + } + if (!_short) + ret += " " + stringForReferencePart(); + return ret; } TypePointer ArrayType::externalType() const @@ -778,14 +831,12 @@ TypePointer ArrayType::externalType() const return std::make_shared(Location::CallData, m_baseType->externalType(), m_length); } -TypePointer ArrayType::copyForLocation(ReferenceType::Location _location) const +TypePointer ArrayType::copyForLocation(ReferenceType::Location _location, bool _isPointer) const { auto copy = make_shared(_location); + copy->m_isPointer = _isPointer; copy->m_arrayKind = m_arrayKind; - if (auto ref = dynamic_cast(m_baseType.get())) - copy->m_baseType = ref->copyForLocation(_location); - else - copy->m_baseType = m_baseType; + copy->m_baseType = copy->copyForLocationIfReference(m_baseType); copy->m_hasDynamicLength = m_hasDynamicLength; copy->m_length = m_length; return copy; @@ -801,7 +852,7 @@ bool ContractType::operator==(Type const& _other) const return other.m_contract == m_contract && other.m_super == m_super; } -string ContractType::toString() const +string ContractType::toString(bool) const { return "contract " + string(m_super ? "super " : "") + m_contract.getName(); } @@ -890,6 +941,19 @@ vector> ContractType::getState return variablesAndOffsets; } +bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const +{ + if (_convertTo.getCategory() != getCategory()) + return false; + auto& convertTo = dynamic_cast(_convertTo); + // memory/calldata to storage can be converted, but only to a direct storage reference + if (convertTo.location() == Location::Storage && location() != Location::Storage && convertTo.isPointer()) + return false; + if (convertTo.location() == Location::CallData && location() != convertTo.location()) + return false; + return this->m_struct == convertTo.m_struct; +} + TypePointer StructType::unaryOperatorResult(Token::Value _operator) const { return _operator == Token::Delete ? make_shared() : TypePointer(); @@ -900,7 +964,7 @@ bool StructType::operator==(Type const& _other) const if (_other.getCategory() != getCategory()) return false; StructType const& other = dynamic_cast(_other); - return other.m_struct == m_struct; + return ReferenceType::operator==(other) && other.m_struct == m_struct; } u256 StructType::getStorageSize() const @@ -916,9 +980,12 @@ bool StructType::canLiveOutsideStorage() const return true; } -string StructType::toString() const +string StructType::toString(bool _short) const { - return string("struct ") + m_struct.getName(); + string ret = "struct " + m_struct.getName(); + if (!_short) + ret += " " + stringForReferencePart(); + return ret; } MemberList const& StructType::getMembers() const @@ -928,16 +995,23 @@ MemberList const& StructType::getMembers() const { MemberList::MemberMap members; for (ASTPointer const& variable: m_struct.getMembers()) - members.push_back(MemberList::Member(variable->getName(), variable->getType(), variable.get())); + { + members.push_back(MemberList::Member( + variable->getName(), + copyForLocationIfReference(variable->getType()), + variable.get()) + ); + } m_members.reset(new MemberList(members)); } return *m_members; } -TypePointer StructType::copyForLocation(ReferenceType::Location _location) const +TypePointer StructType::copyForLocation(ReferenceType::Location _location, bool _isPointer) const { auto copy = make_shared(m_struct); copy->m_location = _location; + copy->m_isPointer = _isPointer; return copy; } @@ -970,7 +1044,7 @@ unsigned EnumType::getStorageBytes() const return dev::bytesRequired(elements - 1); } -string EnumType::toString() const +string EnumType::toString(bool) const { return string("enum ") + m_enum.getName(); } @@ -1114,14 +1188,14 @@ bool FunctionType::operator==(Type const& _other) const return true; } -string FunctionType::toString() const +string FunctionType::toString(bool _short) const { string name = "function ("; for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it) - name += (*it)->toString() + (it + 1 == m_parameterTypes.end() ? "" : ","); + name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ","); name += ") returns ("; for (auto it = m_returnParameterTypes.begin(); it != m_returnParameterTypes.end(); ++it) - name += (*it)->toString() + (it + 1 == m_returnParameterTypes.end() ? "" : ","); + name += (*it)->toString(_short) + (it + 1 == m_returnParameterTypes.end() ? "" : ","); return name + ")"; } @@ -1289,7 +1363,7 @@ string FunctionType::externalSignature(std::string const& _name) const for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it) { solAssert(!!(*it), "Parameter should have external type"); - ret += (*it)->toString() + (it + 1 == externalParameterTypes.cend() ? "" : ","); + ret += (*it)->toString(true) + (it + 1 == externalParameterTypes.cend() ? "" : ","); } return ret + ")"; @@ -1327,7 +1401,7 @@ vector const FunctionType::getParameterTypeNames() const { vector names; for (TypePointer const& t: m_parameterTypes) - names.push_back(t->toString()); + names.push_back(t->toString(true)); return names; } @@ -1336,7 +1410,7 @@ vector const FunctionType::getReturnParameterTypeNames() const { vector names; for (TypePointer const& t: m_returnParameterTypes) - names.push_back(t->toString()); + names.push_back(t->toString(true)); return names; } @@ -1358,9 +1432,9 @@ bool MappingType::operator==(Type const& _other) const return *other.m_keyType == *m_keyType && *other.m_valueType == *m_valueType; } -string MappingType::toString() const +string MappingType::toString(bool _short) const { - return "mapping(" + getKeyType()->toString() + " => " + getValueType()->toString() + ")"; + return "mapping(" + getKeyType()->toString(_short) + " => " + getValueType()->toString(_short) + ")"; } u256 VoidType::getStorageSize() const @@ -1445,11 +1519,11 @@ bool ModifierType::operator==(Type const& _other) const return true; } -string ModifierType::toString() const +string ModifierType::toString(bool _short) const { string name = "modifier ("; for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it) - name += (*it)->toString() + (it + 1 == m_parameterTypes.end() ? "" : ","); + name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ","); return name + ")"; } @@ -1496,7 +1570,7 @@ bool MagicType::operator==(Type const& _other) const return other.m_kind == m_kind; } -string MagicType::toString() const +string MagicType::toString(bool) const { switch (m_kind) { diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 17d30ea6c..0f86ac95f 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -198,19 +198,24 @@ public: /// i.e. it behaves differently in lvalue context and in value context. virtual bool isValueType() const { return false; } virtual unsigned getSizeOnStack() const { return 1; } - /// @returns the real type of some types, like e.g: IntegerConstant - virtual TypePointer getRealType() const { return shared_from_this(); } + /// @returns the mobile (in contrast to static) type corresponding to the given type. + /// This returns the corresponding integer type for IntegerConstantTypes and the pointer type + /// for storage reference types. + virtual TypePointer mobileType() const { return shared_from_this(); } /// Returns the list of all members of this type. Default implementation: no members. virtual MemberList const& getMembers() const { return EmptyMemberList; } /// Convenience method, returns the type of the given named member or an empty pointer if no such member exists. TypePointer getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); } - virtual std::string toString() const = 0; + virtual std::string toString(bool _short) const = 0; + std::string toString() const { return toString(false); } virtual u256 literalValue(Literal const*) const { - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Literal value requested " - "for type without literals.")); + BOOST_THROW_EXCEPTION( + InternalCompilerError() << + errinfo_comment("Literal value requested for type without literals.") + ); } /// @returns a type suitable for outside of Solidity, i.e. for contract types it returns address. @@ -249,7 +254,7 @@ public: virtual MemberList const& getMembers() const override { return isAddress() ? AddressMemberList : EmptyMemberList; } - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual TypePointer externalType() const override { return shared_from_this(); } @@ -287,9 +292,9 @@ public: virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned getSizeOnStack() const override { return 1; } - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual u256 literalValue(Literal const* _literal) const override; - virtual TypePointer getRealType() const override; + virtual TypePointer mobileType() const override; /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr getIntegerType() const; @@ -322,7 +327,7 @@ public: virtual unsigned getStorageBytes() const override { return m_bytes; } virtual bool isValueType() const override { return true; } - virtual std::string toString() const override { return "bytes" + dev::toString(m_bytes); } + virtual std::string toString(bool) const override { return "bytes" + dev::toString(m_bytes); } virtual u256 literalValue(Literal const* _literal) const override; virtual TypePointer externalType() const override { return shared_from_this(); } @@ -348,27 +353,51 @@ public: virtual unsigned getStorageBytes() const override { return 1; } virtual bool isValueType() const override { return true; } - virtual std::string toString() const override { return "bool"; } + virtual std::string toString(bool) const override { return "bool"; } virtual u256 literalValue(Literal const* _literal) const override; virtual TypePointer externalType() const override { return shared_from_this(); } }; /** - * Trait used by types which are not value types and can be stored either in storage, memory + * Base class used by types which are not value types and can be stored either in storage, memory * or calldata. This is currently used by arrays and structs. */ -class ReferenceType +class ReferenceType: public Type { public: enum class Location { Storage, CallData, Memory }; explicit ReferenceType(Location _location): m_location(_location) {} Location location() const { return m_location; } - /// @returns a copy of this type with location (recursively) changed to @a _location. - virtual TypePointer copyForLocation(Location _location) const = 0; + /// @returns a copy of this type with location (recursively) changed to @a _location, + /// whereas isPointer is only shallowly changed - the deep copy is always a bound reference. + virtual TypePointer copyForLocation(Location _location, bool _isPointer) const = 0; + + virtual TypePointer mobileType() const override { return copyForLocation(m_location, true); } + + /// Storage references can be pointers or bound references. In general, local variables are of + /// pointer type, state variables are bound references. Assignments to pointers or deleting + /// them will not modify storage (that will only change the pointer). Assignment from + /// non-storage objects to a variable of storage pointer type is not possible. + bool isPointer() const { return m_isPointer; } + + bool operator==(ReferenceType const& _other) const + { + return location() == _other.location() && isPointer() == _other.isPointer(); + } + + /// @returns a copy of @a _type having the same location as this (and is not a pointer type) + /// if _type is a reference type and an unmodified copy of _type otherwise. + /// This function is mostly useful to modify inner types appropriately. + static TypePointer copyForLocationIfReference(Location _location, TypePointer const& _type); protected: + TypePointer copyForLocationIfReference(TypePointer const& _type) const; + /// @returns a human-readable description of the reference part of the type. + std::string stringForReferencePart() const; + Location m_location = Location::Storage; + bool m_isPointer = true; }; /** @@ -378,10 +407,9 @@ protected: * one slot). Dynamically sized arrays (including byte arrays) start with their size as a uint and * thus start on their own slot. */ -class ArrayType: public Type, public ReferenceType +class ArrayType: public ReferenceType { public: - virtual Category getCategory() const override { return Category::Array; } /// Constructor for a byte array ("bytes") and string. @@ -389,16 +417,18 @@ public: ReferenceType(_location), m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes), m_baseType(std::make_shared(1)) - {} + { + } /// Constructor for a dynamically sized array type ("type[]") - ArrayType(Location _location, const TypePointer &_baseType): + ArrayType(Location _location, TypePointer const& _baseType): ReferenceType(_location), - m_baseType(_baseType) - {} + m_baseType(copyForLocationIfReference(_baseType)) + { + } /// Constructor for a fixed-size array type ("type[20]") - ArrayType(Location _location, const TypePointer &_baseType, u256 const& _length): + ArrayType(Location _location, TypePointer const& _baseType, u256 const& _length): ReferenceType(_location), - m_baseType(_baseType), + m_baseType(copyForLocationIfReference(_baseType)), m_hasDynamicLength(false), m_length(_length) {} @@ -410,7 +440,7 @@ public: virtual bool isDynamicallySized() const override { return m_hasDynamicLength; } virtual u256 getStorageSize() const override; virtual unsigned getSizeOnStack() const override; - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual MemberList const& getMembers() const override { return isString() ? EmptyMemberList : s_arrayTypeMemberList; @@ -424,7 +454,7 @@ public: TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;} u256 const& getLength() const { return m_length; } - TypePointer copyForLocation(Location _location) const override; + TypePointer copyForLocation(Location _location, bool _isPointer) const override; private: /// String is interpreted as a subtype of Bytes. @@ -460,7 +490,7 @@ public: virtual unsigned getStorageBytes() const override { return 20; } virtual bool canLiveOutsideStorage() const override { return true; } virtual bool isValueType() const override { return true; } - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual MemberList const& getMembers() const override; virtual TypePointer externalType() const override @@ -497,26 +527,29 @@ private: /** * The type of a struct instance, there is one distinct type per struct definition. */ -class StructType: public Type, public ReferenceType +class StructType: public ReferenceType { public: virtual Category getCategory() const override { return Category::Struct; } explicit StructType(StructDefinition const& _struct): //@todo only storage until we have non-storage structs ReferenceType(Location::Storage), m_struct(_struct) {} + virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; virtual u256 getStorageSize() const override; virtual bool canLiveOutsideStorage() const override; virtual unsigned getSizeOnStack() const override { return 2; } - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual MemberList const& getMembers() const override; - TypePointer copyForLocation(Location _location) const override; + TypePointer copyForLocation(Location _location, bool _isPointer) const override; std::pair const& getStorageOffsetsOfMember(std::string const& _name) const; + StructDefinition const& structDefinition() const { return m_struct; } + private: StructDefinition const& m_struct; /// List of member types, will be lazy-initialized because of recursive references. @@ -540,7 +573,7 @@ public: virtual unsigned getSizeOnStack() const override { return 1; } virtual unsigned getStorageBytes() const override; virtual bool canLiveOutsideStorage() const override { return true; } - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual bool isValueType() const override { return true; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; @@ -649,7 +682,7 @@ public: std::vector const getReturnParameterTypeNames() const; virtual bool operator==(Type const& _other) const override; - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual bool canBeStored() const override { return false; } virtual u256 getStorageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } @@ -721,7 +754,7 @@ public: m_keyType(_keyType), m_valueType(_valueType) {} virtual bool operator==(Type const& _other) const override; - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual unsigned getSizeOnStack() const override { return 2; } virtual bool canLiveOutsideStorage() const override { return false; } @@ -744,7 +777,7 @@ public: VoidType() {} virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } - virtual std::string toString() const override { return "void"; } + virtual std::string toString(bool) const override { return "void"; } virtual bool canBeStored() const override { return false; } virtual u256 getStorageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } @@ -769,7 +802,7 @@ public: virtual u256 getStorageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned getSizeOnStack() const override { return 0; } - virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } + virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } virtual MemberList const& getMembers() const override; private: @@ -796,7 +829,7 @@ public: virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned getSizeOnStack() const override { return 0; } virtual bool operator==(Type const& _other) const override; - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; private: TypePointers m_parameterTypes; @@ -826,7 +859,7 @@ public: virtual unsigned getSizeOnStack() const override { return 0; } virtual MemberList const& getMembers() const override { return m_members; } - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; private: Kind m_kind; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 3948a4a23..fced12848 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1889,6 +1889,93 @@ BOOST_AUTO_TEST_CASE(storage_location_local_variables) BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); } +BOOST_AUTO_TEST_CASE(assignment_mem_to_local_storage_variable) +{ + char const* sourceCode = R"( + contract C { + uint[] data; + function f(uint[] x) { + var dataRef = data; + dataRef = x; + } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(storage_assign_to_different_local_variable) +{ + char const* sourceCode = R"( + contract C { + uint[] data; + uint8[] otherData; + function f() { + uint8[] storage x = otherData; + uint[] storage y = data; + y = x; + // note that data = otherData works + } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(assignment_mem_storage_variable_directly) +{ + char const* sourceCode = R"( + contract C { + uint[] data; + function f(uint[] x) { + data = x; + } + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(function_argument_mem_to_storage) +{ + char const* sourceCode = R"( + contract C { + function f(uint[] storage x) private { + } + function g(uint[] x) { + f(x); + } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(function_argument_storage_to_mem) +{ + char const* sourceCode = R"( + contract C { + function f(uint[] storage x) private { + g(x); + } + function g(uint[] x) { + } + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(mem_array_assignment_changes_base_type) +{ + // Such an assignment is possible in storage, but not in memory + // (because it would incur an otherwise unnecessary copy). + // This requirement might be lifted, though. + char const* sourceCode = R"( + contract C { + function f(uint8[] memory x) private { + uint[] memory y = x; + } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + BOOST_AUTO_TEST_SUITE_END() } From 1e6b82606e00b04d92c6e5f411026dedadaf7a1e Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 12 Jun 2015 10:48:47 +0200 Subject: [PATCH 61/69] Pleased gcc. --- libsolidity/Types.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 65acba844..bedd2e7b0 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -693,6 +693,8 @@ string ReferenceType::stringForReferencePart() const case Location::Memory: return "memory"; } + solAssert(false, ""); + return ""; } bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const From 02c958973439a59404b92f38de36c9e66aa93055 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 15 Jun 2015 14:39:34 +0200 Subject: [PATCH 62/69] Style. --- libsolidity/AST.h | 3 ++- libsolidity/Compiler.h | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 5f1c1281c..ff0d708f5 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -563,7 +563,8 @@ private: class ModifierDefinition: public CallableDeclaration, public Documented { public: - ModifierDefinition(SourceLocation const& _location, + ModifierDefinition( + SourceLocation const& _location, ASTPointer const& _name, ASTPointer const& _documentation, ASTPointer const& _parameters, diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 589efbff2..60ca00e83 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -74,9 +74,11 @@ private: /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers. /// From memory if @a _fromMemory is true, otherwise from call data. /// Expects source offset on the stack. - void appendCalldataUnpacker(TypePointers const& _typeParameters, + void appendCalldataUnpacker( + TypePointers const& _typeParameters, bool _fromMemory = false, - u256 _startOffset = u256(-1)); + u256 _startOffset = u256(-1) + ); void appendReturnValuePacker(TypePointers const& _typeParameters); void registerStateVariables(ContractDefinition const& _contract); From 6ac83389927b7e869b880dfafdf4f7b1b814f1b1 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 15 Jun 2015 21:17:26 +0800 Subject: [PATCH 63/69] Extra debug notice. --- libethcore/Ethash.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 2c743f33b..f27e8e4db 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -108,7 +108,10 @@ bool Ethash::verify(BlockInfo const& _header) bool pre = preVerify(_header); #if !ETH_DEBUG if (!pre) + { + cdebug << "Fail on preVerify"; return false; + } #endif auto result = EthashAux::eval(_header); From 239dfc1293e5dd09ce2d9143849231ed972f8e88 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 15 Jun 2015 21:17:35 +0800 Subject: [PATCH 64/69] Switch to warning. --- libethcore/Ethash.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index f27e8e4db..ebf8c5615 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -109,7 +109,7 @@ bool Ethash::verify(BlockInfo const& _header) #if !ETH_DEBUG if (!pre) { - cdebug << "Fail on preVerify"; + cwarn << "Fail on preVerify"; return false; } #endif From 4fd0d70c471cabe73f4ee269841d53bdff44dd5f Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Mon, 15 Jun 2015 16:14:23 +0200 Subject: [PATCH 65/69] FixedHash::bloom() refactored --- libdevcore/FixedHash.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 2d1822b2c..ca80d745c 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -158,16 +158,17 @@ public: template inline FixedHash& shiftBloom(FixedHash const& _h) { - return (*this |= _h.template bloom()); + return (*this |= _h.template bloomPart()); } template inline bool containsBloom(FixedHash const& _h) { - return contains(_h.template bloom()); + return contains(_h.template bloomPart()); } - template inline FixedHash bloom() const + template inline FixedHash bloomPart() const { + static_assert((M & (M - 1)) == 0, "M must be power-of-two"); static const unsigned c_bloomBits = M * 8; unsigned mask = c_bloomBits - 1; unsigned bloomBytes = (dev::toLog2(c_bloomBits) + 7) / 8; From dba9faa41a0c6a4d77e7bd53797b24a5bd15e728 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 15 Jun 2015 23:46:54 +0800 Subject: [PATCH 66/69] Don't compile neth. --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 71d12353c..729f95ed6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -430,9 +430,9 @@ if (TOOLS) endif() -if (NCURSES) - add_subdirectory(neth) -endif () +#if (NCURSES) +# add_subdirectory(neth) +#endif () if (GUI) From 5c2018d1bc9611fbbce7660b6923313c4c21ed0f Mon Sep 17 00:00:00 2001 From: gluk256 Date: Mon, 15 Jun 2015 20:19:52 +0200 Subject: [PATCH 67/69] Update FixedHash.h --- libdevcore/FixedHash.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index ca80d745c..922a6ec30 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -168,7 +168,7 @@ public: template inline FixedHash bloomPart() const { - static_assert((M & (M - 1)) == 0, "M must be power-of-two"); + static_assert((M & (M - 1)) == 0, "M must be power-of-two"); static const unsigned c_bloomBits = M * 8; unsigned mask = c_bloomBits - 1; unsigned bloomBytes = (dev::toLog2(c_bloomBits) + 7) / 8; From 623a24ff9903f132ac218f1dd97f8659041f44b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 16 Jun 2015 10:38:10 +0200 Subject: [PATCH 68/69] Set code hash in FakeExtVM. --- test/libevm/vm.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/libevm/vm.cpp b/test/libevm/vm.cpp index 5bbab2e1c..ea9339f05 100644 --- a/test/libevm/vm.cpp +++ b/test/libevm/vm.cpp @@ -323,6 +323,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) fev.thisTxCode = get<3>(fev.addresses.at(fev.myAddress)); fev.code = fev.thisTxCode; } + fev.codeHash = sha3(fev.code); bytes output; bool vmExceptionOccured = false; From 1bc547dc480f573d9d78b3700697143b4dc42113 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Tue, 16 Jun 2015 12:10:48 +0200 Subject: [PATCH 69/69] check nonce --- libethereum/BlockChain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index bd6996a45..195f65f1d 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -1072,7 +1072,7 @@ VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function