/*
 Copyright (C) 2016 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

#include <ored/model/crossassetmodeldata.hpp>
#include <ored/model/inflation/infdkdata.hpp>
#include <ored/model/inflation/infjydata.hpp>
#include <ored/utilities/correlationmatrix.hpp>
#include <ored/utilities/log.hpp>
#include <ored/utilities/parsers.hpp>
#include <ored/utilities/to_string.hpp>

#include <qle/models/fxbsconstantparametrization.hpp>
#include <qle/models/fxbspiecewiseconstantparametrization.hpp>
#include <qle/models/irlgm1fconstantparametrization.hpp>
#include <qle/models/irlgm1fpiecewiseconstanthullwhiteadaptor.hpp>
#include <qle/models/irlgm1fpiecewiseconstantparametrization.hpp>
#include <qle/models/irlgm1fpiecewiselinearparametrization.hpp>

#include <ql/math/optimization/levenbergmarquardt.hpp>
#include <ql/models/shortrate/calibrationhelpers/swaptionhelper.hpp>
#include <ql/quotes/simplequote.hpp>
#include <ql/utilities/dataformatters.hpp>

#include <boost/algorithm/string/case_conv.hpp>
#include <boost/lexical_cast.hpp>

namespace {

using ore::data::CorrelationFactor;
using ore::data::parseCorrelationFactor;
using ore::data::parseInteger;
using ore::data::XMLNode;
using ore::data::XMLUtils;
using std::string;

CorrelationFactor fromNode(ore::data::XMLNode* node, bool firstFactor) {

    string factorTag = firstFactor ? "factor1" : "factor2";
    string idxTag = firstFactor ? "index1" : "index2";

    CorrelationFactor factor = parseCorrelationFactor(XMLUtils::getAttribute(node, factorTag));
    string strIdx = XMLUtils::getAttribute(node, idxTag);
    if (strIdx != "") {
        factor.index = parseInteger(strIdx);
    }

    return factor;
}

} // namespace

namespace ore {
namespace data {

void InstantaneousCorrelations::fromXML(XMLNode* node) {
    // Configure correlation structure
    LOG("CrossAssetModelData: adding correlations.");
    XMLNode* correlationNode = XMLUtils::locateNode(node, "InstantaneousCorrelations");
    CorrelationMatrixBuilder cmb;
    if (correlationNode) {
        vector<XMLNode*> nodes = XMLUtils::getChildrenNodes(correlationNode, "Correlation");
        for (Size i = 0; i < nodes.size(); ++i) {
            CorrelationFactor factor_1 = fromNode(nodes[i], true);
            CorrelationFactor factor_2 = fromNode(nodes[i], false);
            Real corr = parseReal(XMLUtils::getNodeValue(nodes[i]));
            cmb.addCorrelation(factor_1, factor_2, corr);
        }
    } else {
        QL_FAIL("No InstantaneousCorrelations found in model configuration XML");
    }

    correlations_ = cmb.correlations();
}

XMLNode* InstantaneousCorrelations::toXML(XMLDocument& doc) {

    XMLNode* instantaneousCorrelationsNode = doc.allocNode("InstantaneousCorrelations");

    for (auto it = correlations_.begin(); it != correlations_.end(); it++) {

        XMLNode* node = doc.allocNode("Correlation", to_string(it->second->value()));
        XMLUtils::appendNode(instantaneousCorrelationsNode, node);

        CorrelationFactor f_1 = it->first.first;
        XMLUtils::addAttribute(doc, node, "factor1", to_string(f_1.type) + ":" + f_1.name);
        if (f_1.index != Null<Size>())
            XMLUtils::addAttribute(doc, node, "index1", to_string(f_1.index));

        CorrelationFactor f_2 = it->first.second;
        XMLUtils::addAttribute(doc, node, "factor2", to_string(f_2.type) + ":" + f_2.name);
        if (f_2.index != Null<Size>())
            XMLUtils::addAttribute(doc, node, "index2", to_string(f_2.index));
    }

    return instantaneousCorrelationsNode;
}

bool InstantaneousCorrelations::operator==(const InstantaneousCorrelations& rhs) {
    // compare correlations by value (not the handle links)
    map<CorrelationKey, Handle<Quote>>::const_iterator corr1, corr2;
    for (corr1 = correlations_.begin(), corr2 = rhs.correlations_.begin();
         corr1 != correlations_.end() && corr2 != rhs.correlations_.end(); ++corr1, ++corr2) {
        if (corr1->first != corr2->first || !close_enough(corr1->second->value(), corr2->second->value()))
            return false;
    }
    if (corr1 != correlations_.end() || corr2 != rhs.correlations_.end())
        return false;

    return true;
}

bool InstantaneousCorrelations::operator!=(const InstantaneousCorrelations& rhs) { return !(*this == rhs); }

bool CrossAssetModelData::operator==(const CrossAssetModelData& rhs) {

    if (*correlations_ != *rhs.correlations_)
        return false;

    if (domesticCurrency_ != rhs.domesticCurrency_ || currencies_ != rhs.currencies_ || equities_ != rhs.equities_ ||
        infindices_ != rhs.infindices_ || bootstrapTolerance_ != rhs.bootstrapTolerance_ ||
        irConfigs_.size() != rhs.irConfigs_.size() || fxConfigs_.size() != rhs.fxConfigs_.size() ||
        eqConfigs_.size() != rhs.eqConfigs_.size() || infConfigs_.size() != rhs.infConfigs_.size() ||
        crLgmConfigs_.size() != rhs.crLgmConfigs_.size() || crCirConfigs_.size() != rhs.crCirConfigs_.size()) {
        return false;
    }

    for (Size i = 0; i < irConfigs_.size(); i++) {
        if (*irConfigs_[i] != *(rhs.irConfigs_[i])) {
            return false;
        }
    }

    for (Size i = 0; i < fxConfigs_.size(); i++) {
        if (*fxConfigs_[i] != *(rhs.fxConfigs_[i])) {
            return false;
        }
    }

    for (Size i = 0; i < eqConfigs_.size(); i++) {
        if (*eqConfigs_[i] != *(rhs.eqConfigs_[i])) {
            return false;
        }
    }

    // Not checking inflation model data for equality. The equality operators were only written to support
    // unit testing toXML and fromXML. Questionable if it should be done this way.

    for (Size i = 0; i < crLgmConfigs_.size(); i++) {
        if (*crLgmConfigs_[i] != *(rhs.crLgmConfigs_[i])) {
            return false;
        }
    }

    for (Size i = 0; i < crCirConfigs_.size(); i++) {
        if (*crCirConfigs_[i] != *(rhs.crCirConfigs_[i])) {
            return false;
        }
    }

    return true;
}

bool CrossAssetModelData::operator!=(const CrossAssetModelData& rhs) { return !(*this == rhs); }

void CrossAssetModelData::clear() {
    currencies_.clear();
    equities_.clear();
    irConfigs_.clear();
    fxConfigs_.clear();
    eqConfigs_.clear();
    infConfigs_.clear();
    crLgmConfigs_.clear();
    crCirConfigs_.clear();
    correlations_->clear();
}

void CrossAssetModelData::validate() {
    QL_REQUIRE(irConfigs_.size() > 0, "no IR data provided");
    QL_REQUIRE(fxConfigs_.size() == irConfigs_.size() - 1, "inconsistent number of FX data provided");
    for (Size i = 0; i < fxConfigs_.size(); ++i)
        QL_REQUIRE(fxConfigs_[i]->foreignCcy() == irConfigs_[i + 1]->ccy(),
                   "currency mismatch betwee IR and FX config vectors");

    if (measure_ == "BA") {
        // ensure that the domestic LGM has shift = 0 and scaling = 1
        for (Size i = 0; i < irConfigs_.size(); ++i)
            if (irConfigs_[i]->ccy() == domesticCurrency_) {
                QL_REQUIRE(close_enough(irConfigs_[i]->scaling(), 1.0),
                           "scaling for the domestic LGM must be 1 for BA measure simulations");
                QL_REQUIRE(close_enough(irConfigs_[i]->shiftHorizon(), 0.0),
                           "shift horizon for the domestic LGM must be 0 for BA measure simulations");
            }
    }
}

std::vector<std::string> pairToStrings(std::pair<std::string, std::string> p) {
    std::vector<std::string> pair = {p.first, p.second};
    return pair;
}

void CrossAssetModelData::fromXML(XMLNode* root) {
    clear();

    XMLNode* sim = XMLUtils::locateNode(root, "Simulation");
    XMLNode* modelNode = XMLUtils::getChildNode(sim, "CrossAssetModel");
    XMLUtils::checkNode(modelNode, "CrossAssetModel");

    domesticCurrency_ = XMLUtils::getChildValue(modelNode, "DomesticCcy", true); // mandatory
    LOG("CrossAssetModelData: domesticCcy " << domesticCurrency_);

    currencies_ = XMLUtils::getChildrenValues(modelNode, "Currencies", "Currency", true);
    for (auto ccy : currencies_) {
        LOG("CrossAssetModelData: ccy " << ccy);
    }

    equities_ = XMLUtils::getChildrenValues(modelNode, "Equities", "Equity");
    for (auto eq : equities_) {
        LOG("CrossAssetModelData equity " << eq);
    }

    infindices_ = XMLUtils::getChildrenValues(modelNode, "InflationIndices", "InflationIndex");
    for (auto inf : infindices_) {
        LOG("CrossAssetModelData inflation index " << inf);
    }

    creditNames_ = XMLUtils::getChildrenValues(modelNode, "CreditNames", "CreditName");
    for (auto cr : creditNames_) {
        LOG("CrossAssetModelData credit name " << cr);
    }

    bootstrapTolerance_ = XMLUtils::getChildValueAsDouble(modelNode, "BootstrapTolerance", true);
    LOG("CrossAssetModelData: bootstrap tolerance = " << bootstrapTolerance_);

    measure_ = XMLUtils::getChildValue(modelNode, "Measure", false);
    LOG("CrossAssetModelData: measure = '" << measure_ << "'");

    // Configure IR model components

    std::map<std::string, boost::shared_ptr<IrLgmData>> irDataMap;
    XMLNode* irNode = XMLUtils::getChildNode(modelNode, "InterestRateModels");
    if (irNode) {
        for (XMLNode* child = XMLUtils::getChildNode(irNode, "LGM"); child;
             child = XMLUtils::getNextSibling(child, "LGM")) {

            boost::shared_ptr<IrLgmData> config(new IrLgmData());
            config->fromXML(child);

            for (Size i = 0; i < config->optionExpiries().size(); i++) {
                LOG("LGM calibration swaption " << config->optionExpiries()[i] << " x " << config->optionTerms()[i]
                                                << " " << config->optionStrikes()[i]);
            }

            irDataMap[config->ccy()] = config;

            LOG("CrossAssetModelData: IR config built for key " << config->ccy());

        } // end of  for (XMLNode* child = XMLUtils::getChildNode(irNode, "LGM"); child;
    }     // end of if (irNode)
    else {
        LOG("No IR model section found");
    }

    buildIrConfigs(irDataMap);

    for (Size i = 0; i < irConfigs_.size(); i++)
        LOG("CrossAssetModelData: IR config currency " << i << " = " << irConfigs_[i]->ccy());

    // Configure FX model components

    std::map<std::string, boost::shared_ptr<FxBsData>> fxDataMap;
    XMLNode* fxNode = XMLUtils::getChildNode(modelNode, "ForeignExchangeModels");
    if (fxNode) {
        for (XMLNode* child = XMLUtils::getChildNode(fxNode, "CrossCcyLGM"); child;
             child = XMLUtils::getNextSibling(child, "CrossCcyLGM")) {

            boost::shared_ptr<FxBsData> config(new FxBsData());
            config->fromXML(child);

            for (Size i = 0; i < config->optionExpiries().size(); i++) {
                LOG("CC-LGM calibration option " << config->optionExpiries()[i] << " " << config->optionStrikes()[i]);
            }

            fxDataMap[config->foreignCcy()] = config;

            LOG("CrossAssetModelData: FX config built with key (foreign ccy) " << config->foreignCcy());
        }
    } else {
        LOG("No FX Models section found");
    }

    buildFxConfigs(fxDataMap);

    for (Size i = 0; i < fxConfigs_.size(); i++)
        LOG("CrossAssetModelData: FX config currency " << i << " = " << fxConfigs_[i]->foreignCcy());

    // Configure EQ model components

    std::map<std::string, boost::shared_ptr<EqBsData>> eqDataMap;
    XMLNode* eqNode = XMLUtils::getChildNode(modelNode, "EquityModels");
    if (eqNode) {
        for (XMLNode* child = XMLUtils::getChildNode(eqNode, "CrossAssetLGM"); child;
             child = XMLUtils::getNextSibling(child, "CrossAssetLGM")) {

            boost::shared_ptr<EqBsData> config(new EqBsData());
            config->fromXML(child);

            for (Size i = 0; i < config->optionExpiries().size(); i++) {
                LOG("Cross-Asset Equity calibration option " << config->optionExpiries()[i] << " "
                                                             << config->optionStrikes()[i]);
            }

            eqDataMap[config->eqName()] = config;

            LOG("CrossAssetModelData: Equity config built with key " << config->eqName());
        }
    } else {
        LOG("No Equity Models section found");
    }

    buildEqConfigs(eqDataMap);

    for (Size i = 0; i < eqConfigs_.size(); i++)
        LOG("CrossAssetModelData: EQ config name " << i << " = " << eqConfigs_[i]->eqName());

    // Read the inflation model data.
    if (XMLNode* n = XMLUtils::getChildNode(modelNode, "InflationIndexModels")) {

        map<string, boost::shared_ptr<InflationModelData>> mp;

        // Loop over nodes and pick out any with name: LGM, DodgsonKainth or JarrowYildirim.
        for (XMLNode* cn = XMLUtils::getChildNode(n); cn; cn = XMLUtils::getNextSibling(cn)) {

            boost::shared_ptr<InflationModelData> imData;

            string nodeName = XMLUtils::getNodeName(cn);
            if (nodeName == "LGM" || nodeName == "DodgsonKainth") {
                imData = boost::make_shared<InfDkData>();
            } else if (nodeName == "JarrowYildirim") {
                imData = boost::make_shared<InfJyData>();
            } else {
                WLOG("Did not recognise InflationIndexModels node with name "
                     << nodeName << " as a valid inflation index model so skipping it.");
                continue;
            }

            const string& indexName = imData->index();
            imData->fromXML(cn);
            mp[indexName] = imData;

            LOG("CrossAssetModelData: inflation index model data built with key " << indexName);
        }

        // Align the inflation model data with the infindices_ read in above and handle defaults
        buildInfConfigs(mp);

        // Log the index number and inflation index name for each inflation index model.
        for (Size i = 0; i < infConfigs_.size(); i++)
            LOG("CrossAssetModelData: INF config name " << i << " = " << infConfigs_[i]->index());

    } else {
        LOG("No InflationIndexModels node found so no inflation models configured.");
    }

    // Configure CR model components

    std::map<std::string, boost::shared_ptr<CrLgmData>> crLgmDataMap;
    std::map<std::string, boost::shared_ptr<CrCirData>> crCirDataMap;
    XMLNode* crNode = XMLUtils::getChildNode(modelNode, "CreditModels");
    if (crNode) {
        for (XMLNode* child = XMLUtils::getChildNode(crNode, "LGM"); child;
             child = XMLUtils::getNextSibling(child, "LGM")) {

            boost::shared_ptr<CrLgmData> config(new CrLgmData());
            config->fromXML(child);

            for (Size i = 0; i < config->optionExpiries().size(); i++) {
                LOG("LGM calibration cds option " << config->optionExpiries()[i] << " x " << config->optionTerms()[i]
                                                  << " " << config->optionStrikes()[i]);
            }

            crLgmDataMap[config->name()] = config;

            LOG("CrossAssetModelData: CR LGM config built for key " << config->name());

        } // end of  for (XMLNode* child = XMLUtils::getChildNode(irNode, "LGM"); child;
        for (XMLNode* child = XMLUtils::getChildNode(crNode, "CIR"); child;
             child = XMLUtils::getNextSibling(child, "CIR")) {

            boost::shared_ptr<CrCirData> config(new CrCirData());
            config->fromXML(child);

            for (Size i = 0; i < config->optionExpiries().size(); i++) {
                LOG("CIR calibration cds option " << config->optionExpiries()[i] << " x " << config->optionTerms()[i]
                                                  << " " << config->optionStrikes()[i]);
            }

            crCirDataMap[config->name()] = config;

            LOG("CrossAssetModelData: CR CIR config built for key " << config->name());

        } // end of  for (XMLNode* child = XMLUtils::getChildNode(irNode, "CIR"); child;
    }     // end of if (irNode)
    else {
        LOG("No CR model section found");
    }

    buildCrConfigs(crLgmDataMap, crCirDataMap);

    for (Size i = 0; i < crLgmConfigs_.size(); i++)
        LOG("CrossAssetModelData: CR LGM config name " << i << " = " << crLgmConfigs_[i]->name());
    for (Size i = 0; i < crCirConfigs_.size(); i++)
        LOG("CrossAssetModelData: CR CIR config name " << i << " = " << crCirConfigs_[i]->name());

    // Configure correlation structure
    LOG("CrossAssetModelData: adding correlations.");
    correlations_ = boost::make_shared<InstantaneousCorrelations>();
    correlations_->fromXML(modelNode);

    validate();

    LOG("CrossAssetModelData loading from XML done");
}

void CrossAssetModelData::buildIrConfigs(std::map<std::string, boost::shared_ptr<IrLgmData>>& irDataMap) {
    // Append IR configurations into the irConfigs vector in the order of the currencies
    // in the currencies vector.
    // If there is an IR configuration for any of the currencies missing, then we will
    // look up the configuration with key "default" and use this instead. If this is
    // not provided either we will throw an exception.
    irConfigs_.resize(currencies_.size());
    for (Size i = 0; i < currencies_.size(); i++) {
        string ccy = currencies_[i];
        if (irDataMap.find(ccy) != irDataMap.end())
            irConfigs_[i] = irDataMap[ccy];
        else { // copy from default
            LOG("IR configuration missing for currency " << ccy << ", using default");
            if (irDataMap.find("default") == irDataMap.end()) {
                ALOG("Both default IR and " << ccy << " IR configuration missing");
                QL_FAIL("Both default IR and " << ccy << " IR configuration missing");
            }
            boost::shared_ptr<IrLgmData> def = irDataMap["default"];
            irConfigs_[i] = boost::make_shared<IrLgmData>(
                ccy, // overwrite this and keep the others
                def->calibrationType(), def->reversionType(), def->volatilityType(), def->calibrateH(),
                def->hParamType(), def->hTimes(), def->hValues(), def->calibrateA(), def->aParamType(), def->aTimes(),
                def->aValues(), def->shiftHorizon(), def->scaling(), def->optionExpiries(), def->optionTerms(),
                def->optionStrikes());
        }
        LOG("CrossAssetModelData: IR config added for ccy " << ccy << " " << irConfigs_[i]->ccy());
    }
}

void CrossAssetModelData::buildFxConfigs(std::map<std::string, boost::shared_ptr<FxBsData>>& fxDataMap) {
    // Append FX configurations into the fxConfigs vector in the order of the foreign
    // currencies in the currencies vector.
    // If there is an FX configuration for any of the foreign currencies missing,
    // then we will look up the configuration with key "default" and use this instead.
    // If this is not provided either we will throw an exception.
    for (Size i = 0; i < currencies_.size(); i++) {
        string ccy = currencies_[i];
        if (ccy == domesticCurrency_)
            continue;
        if (fxDataMap.find(ccy) != fxDataMap.end())
            fxConfigs_.push_back(fxDataMap[ccy]);
        else { // copy from default
            LOG("FX configuration missing for foreign currency " << ccy << ", using default");
            if (fxDataMap.find("default") == fxDataMap.end()) {
                ALOG("Both default FX and " << ccy << " FX configuration missing");
                QL_FAIL("Both default FX and " << ccy << " FX configuration missing");
            }
            boost::shared_ptr<FxBsData> def = fxDataMap["default"];
            boost::shared_ptr<FxBsData> fxData = boost::make_shared<FxBsData>(
                ccy, def->domesticCcy(), def->calibrationType(), def->calibrateSigma(), def->sigmaParamType(),
                def->sigmaTimes(), def->sigmaValues(), def->optionExpiries(), def->optionStrikes());

            fxConfigs_.push_back(fxData);
        }
        LOG("CrossAssetModelData: FX config added for foreign ccy " << ccy);
    }
}

void CrossAssetModelData::buildEqConfigs(std::map<std::string, boost::shared_ptr<EqBsData>>& eqDataMap) {
    // Append Eq configurations into the eqConfigs vector in the order of the equity
    // names in the equities) vector.
    // If there is an Eq configuration for any of the names missing,
    // then we will look up the configuration with key "default" and use this instead.
    // If this is not provided either we will throw an exception.
    for (Size i = 0; i < equities_.size(); i++) {
        string name = equities_[i];
        if (eqDataMap.find(name) != eqDataMap.end())
            eqConfigs_.push_back(eqDataMap[name]);
        else { // copy from default
            LOG("Equity configuration missing for name " << name << ", using default");
            if (eqDataMap.find("default") == eqDataMap.end()) {
                ALOG("Both default EQ and " << name << " EQ configuration missing");
                QL_FAIL("Both default EQ and " << name << " EQ configuration missing");
            }
            boost::shared_ptr<EqBsData> def = eqDataMap["default"];
            boost::shared_ptr<EqBsData> eqData = boost::make_shared<EqBsData>(
                name, def->currency(), def->calibrationType(), def->calibrateSigma(), def->sigmaParamType(),
                def->sigmaTimes(), def->sigmaValues(), def->optionExpiries(), def->optionStrikes());

            eqConfigs_.push_back(eqData);
        }
        LOG("CrossAssetModelData: EQ config added for name " << name);
    }
}

void CrossAssetModelData::buildInfConfigs(const map<string, boost::shared_ptr<InflationModelData>>& mp) {

    // Append inflation model data to the infConfigs_ vector in the order of the inflation indices in the infindices_
    // vector.

    // If for any of the inflation indices in the infindices_ vector, there is no inflation model data in mp then the
    // default inflation model data is used. The default inflation model data should be in mp under the key name
    // "default". If it is not provided either, an exception is thrown.

    for (const string& indexName : infindices_) {

        auto it = mp.find(indexName);
        if (it != mp.end()) {
            infConfigs_.push_back(it->second);
        } else {

            LOG("Inflation index model data missing for index " << indexName << " so attempt to use default");

            auto itDefault = mp.find("default");
            QL_REQUIRE(itDefault != mp.end(),
                       "Inflation index model data missing for index " << indexName << " and for default.");

            // Make a copy of the model data and add to vector.
            boost::shared_ptr<InflationModelData> imData = itDefault->second;
            if (auto dk = boost::dynamic_pointer_cast<InfDkData>(imData)) {
                infConfigs_.push_back(boost::make_shared<InfDkData>(*dk));
            } else if (auto jy = boost::dynamic_pointer_cast<InfJyData>(imData)) {
                infConfigs_.push_back(boost::make_shared<InfJyData>(*jy));
            } else {
                QL_FAIL("Expected inflation model data to be DK or JY.");
            }
        }

        LOG("CrossAssetModelData: INF config added for name " << indexName);
    }
}

void CrossAssetModelData::buildCrConfigs(std::map<std::string, boost::shared_ptr<CrLgmData>>& crLgmDataMap,
                                         std::map<std::string, boost::shared_ptr<CrCirData>>& crCirDataMap) {
    // Append IR configurations into the irConfigs vector in the order of the currencies
    // in the currencies vector.
    // If there is an IR configuration for any of the currencies missing, then we will
    // look up the configuration with key "default" and use this instead. If this is
    // not provided either we will throw an exception.
    crLgmConfigs_.clear();
    crCirConfigs_.clear();

    for (Size i = 0; i < creditNames_.size(); i++) {
        string name = creditNames_[i];
        if (crLgmDataMap.find(name) != crLgmDataMap.end()) {
            QL_REQUIRE(crCirDataMap.find(name) == crCirDataMap.end(), "");
            crLgmConfigs_.push_back(crLgmDataMap[name]);
        } else if (crCirDataMap.find(name) != crCirDataMap.end()) {
            crCirConfigs_.push_back(crCirDataMap[name]);
        } else { // copy from LGM default, CIR default is not used
            LOG("CR configuration missing for name " << name << ", using default");
            if (crLgmDataMap.find("default") == crLgmDataMap.end()) {
                ALOG("Both default CR LGM and " << name << " CR configuration missing");
                QL_FAIL("Both default CR and " << name << " CR configuration missing");
            }
            boost::shared_ptr<CrLgmData> def = crLgmDataMap["default"];
            crLgmConfigs_.push_back(boost::make_shared<CrLgmData>(
                name, // overwrite this and keep the others
                def->calibrationType(), def->reversionType(), def->volatilityType(), def->calibrateH(),
                def->hParamType(), def->hTimes(), def->hValues(), def->calibrateA(), def->aParamType(), def->aTimes(),
                def->aValues(), def->shiftHorizon(), def->scaling(), def->optionExpiries(), def->optionTerms(),
                def->optionStrikes()));
        }
        LOG("CrossAssetModelData: CR config added for name " << name << " " << name);
    }
}

XMLNode* CrossAssetModelData::toXML(XMLDocument& doc) {

    XMLNode* crossAssetModelNode = doc.allocNode("CrossAssetModel");

    XMLUtils::addChild(doc, crossAssetModelNode, "DomesticCcy", domesticCurrency_);
    XMLUtils::addChildren(doc, crossAssetModelNode, "Currencies", "Currency", currencies_);
    XMLUtils::addChildren(doc, crossAssetModelNode, "Equities", "Equity", equities_);
    XMLUtils::addChildren(doc, crossAssetModelNode, "InflationIndices", "InflationIndex", infindices_);
    XMLUtils::addChildren(doc, crossAssetModelNode, "CreditNames", "CreditName", creditNames_);
    XMLUtils::addChild(doc, crossAssetModelNode, "BootstrapTolerance", bootstrapTolerance_);
    XMLUtils::addChild(doc, crossAssetModelNode, "Measure", measure_);

    XMLNode* interestRateModelsNode = XMLUtils::addChild(doc, crossAssetModelNode, "InterestRateModels");
    for (Size irConfigs_Iterator = 0; irConfigs_Iterator < irConfigs_.size(); irConfigs_Iterator++) {
        XMLNode* lgmNode = irConfigs_[irConfigs_Iterator]->toXML(doc);
        XMLUtils::appendNode(interestRateModelsNode, lgmNode);
    }

    XMLNode* foreignExchangeModelsNode = XMLUtils::addChild(doc, crossAssetModelNode, "ForeignExchangeModels");
    for (Size fxConfigs_Iterator = 0; fxConfigs_Iterator < fxConfigs_.size(); fxConfigs_Iterator++) {
        XMLNode* crossCcyLgmNode = fxConfigs_[fxConfigs_Iterator]->toXML(doc);
        XMLUtils::appendNode(foreignExchangeModelsNode, crossCcyLgmNode);
    }

    XMLNode* eqModelsNode = XMLUtils::addChild(doc, crossAssetModelNode, "EquityModels");
    for (Size eqConfigs_Iterator = 0; eqConfigs_Iterator < eqConfigs_.size(); eqConfigs_Iterator++) {
        XMLNode* crossAssetEqNode = eqConfigs_[eqConfigs_Iterator]->toXML(doc);
        XMLUtils::appendNode(eqModelsNode, crossAssetEqNode);
    }

    XMLNode* infModelsNode = XMLUtils::addChild(doc, crossAssetModelNode, "InflationIndexModels");
    for (Size infConfigs_Iterator = 0; infConfigs_Iterator < infConfigs_.size(); infConfigs_Iterator++) {
        XMLNode* crossAssetInfNode = infConfigs_[infConfigs_Iterator]->toXML(doc);
        XMLUtils::appendNode(infModelsNode, crossAssetInfNode);
    }

    XMLNode* crModelsNode = XMLUtils::addChild(doc, crossAssetModelNode, "CreditModels");
    for (Size crLgmConfigs_Iterator = 0; crLgmConfigs_Iterator < crLgmConfigs_.size(); crLgmConfigs_Iterator++) {
        XMLNode* crossAssetCrLgmNode = crLgmConfigs_[crLgmConfigs_Iterator]->toXML(doc);
        XMLUtils::appendNode(crModelsNode, crossAssetCrLgmNode);
    }
    for (Size crCirConfigs_Iterator = 0; crCirConfigs_Iterator < crCirConfigs_.size(); crCirConfigs_Iterator++) {
        XMLNode* crossAssetCrCirNode = crCirConfigs_[crCirConfigs_Iterator]->toXML(doc);
        XMLUtils::appendNode(crModelsNode, crossAssetCrCirNode);
    }

    XMLNode* instantaneousCorrelationsNode = correlations_->toXML(doc);
    XMLUtils::appendNode(crossAssetModelNode, instantaneousCorrelationsNode);

    return crossAssetModelNode;
}
} // namespace data
} // namespace ore


#include "EditImage.h"
#include <QMouseEvent>
#include <qpainter.h>
#include <qmessagebox.h>
#include <qpen.h>
#include <Eigen/dense>

EditImage::EditImage(QWidget* parent):QDialog(parent)
{
	ui.setupUi(this);
	// = _ui.okButton->font();
	//::connect(_ui.pushButton, &QPushButton::clicked, this, &systemfont_dlg::on_select_font);
	//QObject::connect(_ui.okButton, &QPushButton::clicked, this, &systemfont_dlg::on_ok);
	curImg = ib._src;
	rawImg = ib._src;
	viewdlg = new ViewDlg(this, curImg,index);

	ui.scrollArea->setWidget(viewdlg);

	QObject::connect(ui.btnPointer, &QPushButton::clicked, this, [&]() {
		index = 0;
		selected = 0;
		viewdlg->update();
		//to do...
		});
	QObject::connect(ui.btnRect, &QPushButton::clicked, this, [&]() {
		index = 1;
		//to do...
		});
	QObject::connect(ui.btnMagic, &QPushButton::clicked, this, [&]() {
		index = 2;
		//to do...
		});
	QObject::connect(ui.btnPen, &QPushButton::clicked, this, [&]() {
		index = 3;
		//to do...
		});
	QObject::connect(ui.btnFill, &QPushButton::clicked, this, [&]() {
		index = 4;
		//to do...
		on_fill();
		});
	QObject::connect(ui.btnColor, &QPushButton::clicked, this, [&]() {
		index = 5;
		//to do...
		});
	QObject::connect(ui.btnBig, &QPushButton::clicked, this, [&]() {
		index = 6;
		//to do...
		if (bigX < 64) {
			bigX *= 2;
			on_resize();
			update();
		}

		});
	QObject::connect(ui.btnSrc, &QPushButton::clicked, this, [&]() {
		index = 7;
		//to do...
		if (bigX != 1) {
			bigX = 1;
			on_resize();

		}

		});
	QObject::connect(ui.btnSmall, &QPushButton::clicked, this, [&]() {
		index = 8;
		//to do...
		if (bigX > 1) {
			bigX /= 2;
			on_resize();

		}

		});
	QObject::connect(ui.btnCut, &QPushButton::clicked, this, [&]() {
		index = 9;
		//to do...
		});

	QObject::connect(ui.btnRoate, &QPushButton::clicked, this,&EditImage::on_roate);

	QObject::connect(viewdlg, &ViewDlg::on_selected, this, [&]() {selected = 1; });

}

EditImage::~EditImage()
{

}
void EditImage::paintEvent(QPaintEvent*) {
	QPainter paint(this);

}

void EditImage::do_init(Image& img) {

	curImg = img;
	rawImg = img;
	selected = 0;
	on_resize();
}

void EditImage::on_resize() {
	using namespace Eigen;
	if (bigX != 1) {
		curImg.create(rawImg.width * bigX, rawImg.height * bigX);
		using matrix_t = Array<uint, Dynamic, Dynamic, RowMajor>;
		Map<matrix_t> mat(curImg.ptr<uint>(0), curImg.height, curImg.width);
		for (int i = 0; i < rawImg.height; i++) {
			for (int j = 0; j < rawImg.width; j++) {
				mat.block(i * bigX, j * bigX, bigX, bigX) = rawImg.at<uint>(i, j);
			}
		}

	}
	else {
		curImg = rawImg;
	}
	viewdlg->update();

}

void EditImage::on_fill() {
	if (!selected)return;
	int x0 = viewdlg->select_rc.x();
	int y0 = viewdlg->select_rc.y();
	int w = viewdlg->select_rc.width();
	int h = viewdlg->select_rc.height();
	x0 /= bigX;
	y0 /= bigX;
	w /= bigX;
	h /= bigX;
	qDebug("%d %d %d %d", x0, y0, w, h);
}

void EditImage::on_roate() {
	static double theta = 0.0;
	theta += 0.2;
	int cw = sqrt(rawImg.width * rawImg.width + rawImg.height * rawImg.height);
	if (cw == 0)return;
	curImg.create(cw, cw);
	for (int y = 0; y < cw; y++) {
		for (int x = 0; x < cw; x++) {

			int rx = (x -cw/2) * cos(theta) - (y-cw/2) * sin(theta) + rawImg.width/2;
			int ry = (x -cw/2) * sin(theta) + (y-cw/2) * cos(theta) +rawImg.height/2;

			if (0 <= rx && rx < rawImg.width && 0 <= ry && ry < rawImg.height)
				curImg.at<uint>(y, x) = rawImg.at<uint>(ry, rx);
			else
				curImg.at<uint>(y, x) = 0xffffffffu;
		}
	}
	viewdlg->update();
}


//
//  automaton.hpp
//  ATL
//
//  Licensed under the MIT License <http://opensource.org/licenses/MIT>.
//  SPDX-License-Identifier: MIT
//  Copyright (c) 2020 Jinlong He.
//

#ifndef atl_detail_automaton_hpp
#define atl_detail_automaton_hpp

#include <boost/scoped_ptr.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/functional/hash.hpp>

using boost::adjacency_list;
using boost::vecS;
using boost::listS;
using boost::bidirectionalS;
using boost::graph_traits;
using boost::scoped_ptr;
using boost::graph_bundle_t;
using boost::tie;
using std::pair;
using std::string;

namespace std {
    template <class K, class C, class A>
    struct hash<unordered_set<K, C, A> > {
        size_t operator() (const unordered_set<K, C, A>& v) const {
            size_t res = 0;
            for (auto& i : v) {
                res ^= boost::hash_value(i);
            }
            return res;
        }
    };

    template <class K, class C, class A>
    struct hash<set<K, C, A> > {
        size_t operator() (const set<K, C, A>& v) const {
            size_t res = 0;
            for (auto& i : v) {
                res ^= boost::hash_value(i);
            }
            return res;
        }
    };

    template <class T>
    struct hash<vector<T> > {
        size_t operator() (const vector<T>& v) const {
            return boost::hash_range(v.begin(), v.end());
        }
    };

    template <class T1, class T2>
    struct hash<pair<T1, T2> > {
        size_t operator() (const pair<T1, T2>& p) const {
            return boost::hash_value(p);
        }
    };
}

namespace atl {
    typedef std::size_t Flag;
    typedef std::size_t ID;

    namespace detail {
        template <typename DefaultProperty, typename ExtendedProperty>
        struct Property {
            DefaultProperty default_property;
            ExtendedProperty extended_property;

            Property() {}

            Property(const DefaultProperty& dp, const ExtendedProperty& ep)
                : default_property(dp),
                  extended_property(ep) {}

            Property(long l)
                : default_property(l),
                  extended_property() {}

            bool operator== (const Property& p) const {
                return default_property == p.default_property &&
                       extended_property == p.extended_property;
            }

            friend std::ostream& operator<< (std::ostream& os, const Property& x) {
                os << x.default_property << " {" << x.extended_property << "}";
                return os;
            }
        };

        template <class TransitionProperty,
                  class StateProperty,
                  class AutomatonProperty>
        class automaton_gen {
        public:
            typedef StateProperty state_property_type;
            typedef AutomatonProperty automaton_property_type;
            typedef TransitionProperty transition_property_type;

            typedef adjacency_list<listS, vecS, bidirectionalS,
                                   StateProperty, TransitionProperty, AutomatonProperty,
                                   listS> BoostGraph;

            typedef typename graph_traits<BoostGraph>::edge_descriptor Transition;
            typedef typename graph_traits<BoostGraph>::edge_iterator TransitionIter;
            typedef typename graph_traits<BoostGraph>::in_edge_iterator InTransitionIter;
            typedef typename graph_traits<BoostGraph>::out_edge_iterator OutTransitionIter;
            typedef typename graph_traits<BoostGraph>::vertex_descriptor State;
            typedef typename graph_traits<BoostGraph>::vertex_iterator StateIter;

        public:
            automaton_gen(const AutomatonProperty& p = AutomatonProperty())
                : graph_(new BoostGraph(p)),
                  flag_(0) {}

            automaton_gen(const automaton_gen& x)
                : graph_(new BoostGraph(*x.graph_)),
                  flag_(x.flag_) {
            }

            virtual ~automaton_gen() {}

            automaton_gen&
            operator=(const automaton_gen& x) {
                if (&x != this) {
                    BoostGraphPtr ptr(new BoostGraph(*x.graph_));
                    graph_.swap(ptr);
                    flag_ = x.flag_;
                }
                return *this;
            }

            virtual void clear() {
                if (flag_ == 0) return;
                graph_ -> clear();
                flag_ = 0;
            }

            Flag
            flag() const {
                return flag_;
            }

            bool
            flag(ID i) const {
                return (flag_ & (1 << i)) == (1 << i);
            }

            void
            set_flag(Flag flag) {
                flag_ = flag;
            }

            void
            set_flag(ID i, bool b) {
                flag_ = b ? (flag_ | (1 << i)) : (flag_ & ~(1 << i));
            }

            const AutomatonProperty&
            get_property() const {
                return (*graph_)[graph_bundle_t()];
            }

            const TransitionProperty&
            get_property(Transition t) const {
                return (*graph_)[t];
            }

            const StateProperty&
            get_property(State s) const {
                return (*graph_)[s];
            }

            void
            set_property(const AutomatonProperty& p) {
                (*graph_)[graph_bundle_t()] = p;
            }

            void
            set_property(Transition t,
                         const TransitionProperty& p) {
                (*graph_)[t] = p;
            }

            void
            set_property(State s,
                         const StateProperty& p) {
                (*graph_)[s] = p;
            }

            pair<StateIter, StateIter>
            states() const {
                return boost::vertices(*graph_);
            }

            State
            source(Transition t) const {
                return boost::source(t, *graph_);
            }

            State
            target(Transition t) const {
                return boost::target(t, *graph_);
            }

            pair<TransitionIter, TransitionIter>
            transitions() const {
                return boost::edges(*graph_);
            }

            pair<InTransitionIter, InTransitionIter>
            in_transitions(State s) const {
                return boost::in_edges(s, *graph_);
            }

            pair<OutTransitionIter, OutTransitionIter>
            out_transitions(State s) const {
                return boost::out_edges(s, *graph_);
            }

            virtual State
            add_state(const StateProperty& p) {
                return boost::add_vertex(p, *graph_);
            }

            virtual State
            add_state() {
                return boost::add_vertex(*graph_);
            }

            virtual void
            clear_state(State s) {
                return boost::clear_vertex(s, *graph_);
            }

            virtual void
            clear_out_transitions(State s) {
                return boost::clear_out_edges(s, *graph_);
            }

            virtual void
            clear_in_transitions(State s) {
                return boost::clear_in_edges(s, *graph_);
            }

            virtual void
            remove_state(State s) {
                return boost::remove_vertex(s, *graph_);
            }

            virtual pair<Transition, bool>
            add_transition(State s, State t,
                           const TransitionProperty& p) {
                return boost::add_edge(s, t, p, *graph_);
            }

            virtual pair<Transition, bool>
            add_transition(State s, State t) {
                return boost::add_edge(s, t, *graph_);
            }

            virtual void
            remove_transition(Transition t) {
                return boost::remove_edge(t, *graph_);
            }

            virtual void
            remove_transition(State s, State t) {
                return boost::remove_edge(s, t, *graph_);
            }
        private:
            typedef scoped_ptr<BoostGraph> BoostGraphPtr;
            BoostGraphPtr graph_;
            Flag flag_;
        };
    }

    #define AUTOMATON_PARAMS typename TP, typename SP, typename AP
    #define AUTOMATON detail::automaton_gen<TP, SP, AP>

    template <AUTOMATON_PARAMS>
    inline const AP&
    get_property(const AUTOMATON& a) {
        return a.get_property();
    }

    template <AUTOMATON_PARAMS>
    inline const SP&
    get_property(const AUTOMATON& a,
                 typename AUTOMATON::State s) {
        return a.get_property(s);
    }

    template <AUTOMATON_PARAMS>
    inline const TP&
    get_property(const AUTOMATON& a,
                 typename AUTOMATON::Transition t) {
        return a.get_property(t);
    }

    template <AUTOMATON_PARAMS>
    inline void
    set_property(AUTOMATON& a,
                 const AP& p) {
        return a.set_property(p);
    }

    template <AUTOMATON_PARAMS>
    inline void
    set_property(AUTOMATON& a,
                 typename AUTOMATON::State s,
                 const SP& p) {
        return a.set_property(s, p);
    }

    template <AUTOMATON_PARAMS>
    inline void
    set_property(AUTOMATON& a,
                 typename AUTOMATON::Transition t,
                 const TP& p) {
        return a.set_property(t, p);
    }

    template <AUTOMATON_PARAMS>
    inline pair<typename AUTOMATON::StateIter,
                typename AUTOMATON::StateIter>
    states(const AUTOMATON& a) {
        return a.states();
    }

    template <AUTOMATON_PARAMS>
    inline typename AUTOMATON::State
    source(const AUTOMATON& a,
           typename AUTOMATON::Transition t) {
        return a.source(t);
    }

    template <AUTOMATON_PARAMS>
    inline typename AUTOMATON::State
    target(const AUTOMATON& a,
           typename AUTOMATON::Transition t) {
        return a.target(t);
    }

    template <AUTOMATON_PARAMS>
    inline pair<typename AUTOMATON::TransitionIter,
                typename AUTOMATON::TransitionIter>
    transitions(const AUTOMATON& a) {
        return a.transitions();
    }

    template <AUTOMATON_PARAMS>
    inline pair<typename AUTOMATON::InTransitionIter,
                typename AUTOMATON::InTransitionIter>
    in_transitions(const AUTOMATON& a,
                   typename AUTOMATON::State s) {
        return a.in_transitions(s);
    }

    template <AUTOMATON_PARAMS>
    inline pair<typename AUTOMATON::OutTransitionIter,
                typename AUTOMATON::OutTransitionIter>
    out_transitions(const AUTOMATON& a,
                    typename AUTOMATON::State s) {
        return a.out_transitions(s);
    }

    template <AUTOMATON_PARAMS>
    inline void
    set_modified_flag(AUTOMATON& a,
                      bool b = true) {
        a.set_flag(0, b);
    }

    template <AUTOMATON_PARAMS>
    inline bool
    is_modified(const AUTOMATON& a) {
        return a.flag(0);
    }

    template <AUTOMATON_PARAMS>
    inline typename AUTOMATON::State
    add_state(AUTOMATON& a,
              const SP& p) {
        set_modified_flag(a);
        return a.add_state(p);
    }

    template <AUTOMATON_PARAMS>
    inline typename AUTOMATON::State
    add_state(AUTOMATON& a) {
        set_modified_flag(a);
        return a.add_state();
    }

    template <AUTOMATON_PARAMS>
    inline void
    clear_state(AUTOMATON& a,
                typename AUTOMATON::State s) {
        set_modified_flag(a);
        return a.clear_state(s);
    }

    template <AUTOMATON_PARAMS>
    inline void
    clear_out_transitions(AUTOMATON& a,
                         typename AUTOMATON::State s) {
        set_modified_flag(a);
        return a.clear_out_transitions(s);
    }

    template <AUTOMATON_PARAMS>
    inline void
    clear_in_transitions(AUTOMATON& a,
                         typename AUTOMATON::State s) {
        set_modified_flag(a);
        return a.clear_in_transitions(s);
    }

    template <AUTOMATON_PARAMS>
    inline void
    remove_state(AUTOMATON& a,
                 typename AUTOMATON::State s) {
        set_modified_flag(a);
        return a.remove_state(s);
    }

    template <AUTOMATON_PARAMS>
    inline pair<typename AUTOMATON::Transition, bool>
    add_transition(AUTOMATON& a,
                   typename AUTOMATON::State s,
                   typename AUTOMATON::State t,
                   const TP& p) {
        set_modified_flag(a);
        return a.add_transition(s, t, p);
    }

    template <AUTOMATON_PARAMS>
    inline void
    remove_transition(AUTOMATON& a,
                      typename AUTOMATON::Transition t) {
        return a.remove_transition(t);
    }

    template <AUTOMATON_PARAMS>
    inline void
    remove_transition(AUTOMATON& a,
                      typename AUTOMATON::State s,
                      typename AUTOMATON::State t) {
        return a.remove_transition(s, t);
    }

    template <AUTOMATON_PARAMS>
    inline void
    clear(AUTOMATON& a) {
        a.clear();
    }
}

#endif /* atl_detail_automaton_hpp */


// Boost.Units - A C++ library for zero-overhead dimensional analysis and
// unit/quantity manipulation and conversion
//
// Copyright (C) 2003-2008 Matthias Christian Schabel
// Copyright (C) 2008 Steven Watanabe
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_UNITS_DETAIL_PUSH_FRONT_OR_ADD_HPP
#define BOOST_UNITS_DETAIL_PUSH_FRONT_OR_ADD_HPP

#include <boost/mpl/plus.hpp>
#include <boost/mpl/front.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/mpl/pop_front.hpp>
#include <boost/type_traits/is_same.hpp>

#include <boost/units/units_fwd.hpp>
#include <boost/units/detail/push_front_if.hpp>

namespace boost {

namespace units {

template<class Item, class Next>
struct list;

namespace detail {

template<class T>
struct is_empty_dim;

/// add an instantiation of dim to Sequence.
template<bool>
struct push_front_or_add_impl;

template<>
struct push_front_or_add_impl<true>
{
    template<typename Sequence, typename T>
    struct apply
    {
        typedef typename mpl::plus<T, typename Sequence::item>::type item;
        typedef typename push_front_if<!is_empty_dim<item>::value>::template apply<
            typename Sequence::next,
            item
        > type;
    };
};

template<>
struct push_front_or_add_impl<false>
{
    template<typename Sequence, typename T>
    struct apply
    {
        typedef list<T, Sequence> type;
    };
};

template<typename Sequence, typename T>
struct push_front_or_add
{
    typedef typename push_front_or_add_impl<boost::is_same<typename T::tag_type, typename Sequence::item::tag_type>::value>::template apply<
        Sequence,
        T
    >::type type;
};

template<typename T>
struct push_front_or_add<dimensionless_type, T>
{
    typedef list<T, dimensionless_type> type;
};

} // namespace detail

} // namespace units

} // namespace boost

#endif


#include "reduce.h"
#include <iostream>
#include <boost/random.hpp>
#include <vector>
#include <algorithm>
#include <cstdlib>

using namespace std;

void generateVector(int n, vector<double>& v)
{
    boost::random::mt19937 rng;
    boost::random::uniform_real_distribution<double> distr(1., 2.);

    v.resize(n);

    for (int i = 0; i < n; ++i)
        v[i] = distr(rng);
}

int main(int argc, char **argv)
{
    typedef double (*reducefunc_t)(int, const double*);
    reducefunc_t reducefunc = bitrep::reduce_3;

    if (argc > 1) {
        int arg = atoi(argv[1]);
        switch (arg) {
            case 1:
                reducefunc = bitrep::reduce_1;
                break;
            case 2:
                reducefunc = bitrep::reduce_2;
                break;
            case 3:
                reducefunc = bitrep::reduce_3;
                break;
            case 4:
                reducefunc = bitrep::reduce_4;
                break;
            default:
                cerr << "Invalid argument: " << arg << "\n";
                return 1;
        }
    }

    vector<double> input;

    for (int n = 1; n <= 1024; n *= 2) {
        generateVector(n, input);
        double result = reducefunc(n, input.data());

        for (int i = 0; i < 1024; ++i) {
            std::random_shuffle(input.begin(), input.end());
            double newresult = reducefunc(n, input.data());
            if (newresult != result)
                cerr << "Error at shuffle #" << i << endl;
        }
    }
}


#include "../deberg.hpp"
#include "../bb_point_filter.hpp"

#include <boost/test/unit_test.hpp>
#include <boost/test/test_case_template.hpp>

#include <algorithm>

BOOST_AUTO_TEST_SUITE(deberg_tests)

BOOST_AUTO_TEST_CASE(example_test)
{
    //
    //             5--6
    //            / a  \
    //        3--4      7----8
    //        |\          11 |
    //       c|d\        / \ |
    //   1____2  \     /    \|
    //   |       b\  /       |
    //   0---------10--------9
    //
    std::vector<coordinate> cs = {
        coordinate {0, 0},      // 0
        coordinate {0, 1},      // 1
        coordinate {1, 1},      // 2
        coordinate {1, 2},      // 3
        coordinate {1.5, 2},    // 4
        coordinate {2, 3},      // 5
        coordinate {2.5, 3},    // 6
        coordinate {3, 2},      // 7
        coordinate {4, 2},      // 8
        coordinate {4, 0},      // 9
        coordinate {2, 0},      // 10
        coordinate {3.5, 1.75}, // 11
    };

    std::vector<point> points = {
        {point::NO_LINE_ID, 0, coordinate {2.25, 2.75}}, // a
        {point::NO_LINE_ID, 1, coordinate {1.9, 0.05}},  // b
        {point::NO_LINE_ID, 2, coordinate {0.9, 1.1}},   // c
        {point::NO_LINE_ID, 3, coordinate {1.1, 1.1}},   // d
    };

    std::vector<poly_line> lines = {
        {0, {cs[10], cs[0], cs[1], cs[2], cs[3]}},
        {1, {cs[3], cs[10]}},
        {2, {cs[3], cs[4], cs[5], cs[6], cs[7], cs[8], cs[9]}},
        {3, {cs[10], cs[11], cs[9]}},
        {4, {cs[10], cs[9]}},
    };

    {
        auto points_copy = points;
        deberg<bb_point_filter> simplification(lines[0], std::move(points_copy));
        auto shortcuts = simplification();
        BOOST_CHECK_EQUAL(shortcuts.size(), 5);
        BOOST_CHECK_EQUAL(shortcuts[0].first, 0);
        BOOST_CHECK_EQUAL(shortcuts[0].last,  1);
        BOOST_CHECK_EQUAL(shortcuts[1].first, 1);
        BOOST_CHECK_EQUAL(shortcuts[1].last,  2);
        BOOST_CHECK_EQUAL(shortcuts[2].first, 1);
        BOOST_CHECK_EQUAL(shortcuts[2].last,  3);
        BOOST_CHECK_EQUAL(shortcuts[3].first, 2);
        BOOST_CHECK_EQUAL(shortcuts[3].last,  3);
        BOOST_CHECK_EQUAL(shortcuts[4].first, 3);
        BOOST_CHECK_EQUAL(shortcuts[4].last,  4);
    }

    {
        auto points_copy = points;
        deberg<bb_point_filter> simplification(lines[1], std::move(points_copy));
        auto shortcuts = simplification();
        BOOST_CHECK_EQUAL(shortcuts.size(), 1);
        BOOST_CHECK_EQUAL(shortcuts[0].first, 0);
        BOOST_CHECK_EQUAL(shortcuts[0].last,  1);
    }

    // TODO tets the rest of the lines
}

BOOST_AUTO_TEST_CASE(self_intersection_test)
{
    //    0
    //     \ \
    //      \  \
    //       \   \
    //        1   3
    //       /   /
    //      /  /
    //     / /
    //    2

    poly_line line { 0,
        {
            coordinate {0, 0},
            coordinate {1, -1},
            coordinate {0, -2},
            coordinate {2, -1},
            coordinate {0, 0}
        }
    };

    deberg<bb_point_filter> simplification(line, {});
    auto shortcuts = simplification();
    BOOST_CHECK_EQUAL(shortcuts.size(), 5);
    BOOST_CHECK_EQUAL(shortcuts[0].first, 0);
    BOOST_CHECK_EQUAL(shortcuts[0].last, 1);
    BOOST_CHECK_EQUAL(shortcuts[1].first, 0);
    BOOST_CHECK_EQUAL(shortcuts[1].last, 2);
    BOOST_CHECK_EQUAL(shortcuts[2].first, 1);
    BOOST_CHECK_EQUAL(shortcuts[2].last, 2);
    BOOST_CHECK_EQUAL(shortcuts[3].first, 2);
    BOOST_CHECK_EQUAL(shortcuts[3].last, 3);
    BOOST_CHECK_EQUAL(shortcuts[4].first, 3);
    BOOST_CHECK_EQUAL(shortcuts[4].last, 4);

}

BOOST_AUTO_TEST_SUITE_END()


#include "dasher.h"

#include <boost/algorithm/string.hpp>
#include <cassert>
#include <fstream>
#include <iostream>
#include <numeric>
#include <thread>

namespace construct {

EnglishModel::EnglishModel(std::string w1_file, int limit) :
	alphabet("abcdefghijklmnopqrstuvwxyz"),
	all_letters(alphabet + " .,:;!?") {

	// Parse 1-word frequency table.
	std::map<std::string, uint64_t> freq_table;

	int est_num_prefix = 0;
	int count = 0;
	std::ifstream fs(w1_file);
	while(!fs.eof() && count < limit) {
		std::string line;
		std::getline(fs, line);

		std::vector<std::string> entry;
		boost::algorithm::split(entry, line, boost::is_any_of("\t"));

		if(entry.size() != 2) {
			continue;
		}

		try {
			freq_table[entry[0]] = std::stoull(entry[1]);
			est_num_prefix += entry[0].size() + 1;
			count++;
		} catch(std::invalid_argument exc) {
			// ignore
		}
	}

	// Construct prefix table.
	const uint64_t max_char_table_size = alphabet.size() + 1;

	std::unordered_map<std::string, std::map<char, uint64_t>> prefix_table;
	prefix_table.reserve(est_num_prefix);
	for(const auto& word_freq : freq_table) {
		const std::string& word = word_freq.first;
		const uint64_t freq = word_freq.second;

		// Ignore empty word.
		if(word.empty()) {
			continue;
		}

		// "ab" -> "", "a", "ab"
		for(int i = 0; i < word.size() + 1; i++) {
			const std::string prefix = word.substr(0, i);
			const char next = (i == word.size()) ? ' ' : word[i];

			auto insertion = prefix_table.emplace(prefix,
				std::map<char, uint64_t>());

			auto insertion_p = insertion.first->second.emplace(next, 0);

			insertion_p.first->second += freq;
		}
	}

	// Punctuations.
	for(auto& prefix_e : prefix_table) {
		// Copy count from prefix -> " " to prefix -> "." etc.
		auto it = prefix_e.second.find(' ');
		if(it != prefix_e.second.end()) {
			prefix_e.second['.'] = it->second / 4;
			prefix_e.second[','] = it->second / 2;
			prefix_e.second[':'] = it->second / 4;
			prefix_e.second[';'] = it->second / 4;
			prefix_e.second['!'] = it->second / 5;
			prefix_e.second['?'] = it->second / 5;
		}
	}

	prefix_table["."] = std::map<char, uint64_t>({
		{' ', 10},
		{'.', 1},
	});

	prefix_table[","] = std::map<char, uint64_t>({
		{' ', 10},
	});

	prefix_table["!"] = std::map<char, uint64_t>({
		{' ', 10},
		{'!', 3},
		{'?', 1},
	});

	prefix_table["?"] = std::map<char, uint64_t>({
		{' ', 10},
		{'?', 2}
	});

	prefix_table[":"] = std::map<char, uint64_t>({
		{' ', 10},
	});

	prefix_table[";"] = std::map<char, uint64_t>({
		{' ', 10},
	});

	// Smooth & Normalize prefix table.
	word1_prefix_table.reserve(est_num_prefix);
	for(auto& prefix_e : prefix_table) {
		uint64_t sum_char_freq = 0;
		for(const auto& char_freq : prefix_e.second) {
			sum_char_freq += char_freq.second;
		}

		std::map<char, float> ch_table;
		for(const auto& char_freq : prefix_e.second) {
			assert(char_freq.second <= sum_char_freq);

			ch_table[char_freq.first] =
				static_cast<float>(char_freq.second) / sum_char_freq;
		}

		// Smooth distribution by giving additional 0.01 probability for all chars.
		float sum_new_prob = 0;
		for(char ch : all_letters) {
			auto insertion = ch_table.emplace(ch, 0);
			insertion.first->second += 0.01;
			sum_new_prob += insertion.first->second;
		}

		// Re-normalize.
		for(auto& char_prob : ch_table) {
			char_prob.second /= sum_new_prob;
		}

		word1_prefix_table[prefix_e.first] = ch_table;
	}

	// Create fall-back letter table.
	for(char ch : all_letters) {
		letter_table_any.push_back(std::make_pair(
			ch,
			(ch == 'e' ? 2.0 : 1.0) / (all_letters.size() + 1)));
	}
}

const std::string EnglishModel::getPunctunation() {
	return ".,:;!?";
}

const std::vector<std::pair<char, float>> EnglishModel::nextCharGivenPrefix(std::string prefix) {
	// TODO: smooth so that all characters appears with non-zero probability.
	auto char_distrib = word1_prefix_table.find(prefix);
	if(char_distrib != word1_prefix_table.end()) {
		std::vector<std::pair<char, float>> pairs;
		for(char ch : all_letters) {
			auto it = char_distrib->second.find(ch);
			if(it != char_distrib->second.end()) {
				pairs.push_back(*it);
			}
		}

		return pairs;
	} else {
		return letter_table_any;
	}
}

std::shared_ptr<ProbNode> ProbNode::create(std::shared_ptr<EnglishModel> model) {
	return std::shared_ptr<ProbNode>(new ProbNode(model, std::shared_ptr<ProbNode>(), ""));
}

std::shared_ptr<ProbNode> ProbNode::getParent(std::shared_ptr<ProbNode> node) {
	return node->parent;
}

std::string ProbNode::getWordPrefix() {
	const std::string first = getString();
	for(char ch : model->getPunctunation()) {
		if(std::string(ch, 1) == first) {
			return first;
		}
	}

	std::string result;
	ProbNode* iter = this;
	while(iter && iter->getString() != " ") {
		result = iter->getString() + result;
		iter = iter->parent.get();
	}

	return result;
}

std::vector<std::pair<float, std::shared_ptr<ProbNode>>> ProbNode::getChildren(std::shared_ptr<ProbNode> node) {
	std::vector<std::pair<float, std::shared_ptr<ProbNode>>> children;

	const auto char_table = node->model->nextCharGivenPrefix(node->getWordPrefix());
	for(const auto ch_prob : char_table) {
		children.push_back(std::make_pair(
			ch_prob.second,
			std::shared_ptr<ProbNode>(new ProbNode(
				node->model, node, std::string(1, ch_prob.first)))));
	}

	return children;
}

ProbNode::ProbNode(
	std::shared_ptr<EnglishModel> model,
	std::shared_ptr<ProbNode> parent, std::string str) :
	model(model), parent(parent), str(str) {
}

std::string ProbNode::getString() {
	return str;
}

Dasher::Dasher() : model(nullptr) {
	// Initiate model loading.
	std::thread([this]() {
		model.store(new EnglishModel("count_1w.txt"));
	}).detach();

	local_index = 0;
	local_half_span = 0.5;
}

Dasher::Dasher(std::shared_ptr<EnglishModel> model) :
	current(ProbNode::create(model)) {
	local_index = 0;
	local_half_span = 0.5;
}

void Dasher::update(float dt, float rel_index, float rel_zoom) {
	if(!current) {
		if(model.load()) {
			current = ProbNode::create(std::shared_ptr<EnglishModel>(model.load()));
		} else {
			return;
		}
	}

	const float speed = 1.0;
	local_half_span = std::max(local_half_span / 4, local_half_span + dt * local_half_span * speed * rel_zoom);
	local_index += dt * speed * (rel_index * local_half_span);
	fit();
}

void Dasher::fit() {
	assert(current);
	assert(local_half_span > 0);
	const float p0 = local_index - local_half_span;
	const float p1 = local_index + local_half_span;

	// Span doesn't fit in currrent node?
	if(p0 < 0 || p1 > 1) {
		auto parent = ProbNode::getParent(current);

		if(parent) {
			// convert coordinates to parent's.
			float accum_p = 0;
			for(auto& child : ProbNode::getChildren(parent)) {
				if(child.second->getString() == current->getString()) {
					// [0, 1] -> [accum_p, accum_p + child.first]
					local_index = accum_p + child.first * local_index;
					local_half_span = child.first * local_half_span;
					current = parent;
					fit();
					break;
				}
				accum_p += child.first;
			}
		} else {
			// clip, preserving span (this is somewhat arbitrary UI choice)
			if(p0 < 0 && 1 < p1) {
				local_index = 0.5;
				local_half_span = 0.5;
			} else if(p0 < 0) {
				local_index = local_half_span;
			} else if(1 < p1) {
				local_index = 1 - local_half_span;
			}
		}
	} else {
		// Span can fit completely in a child?
		float accum_p = 0;
		for(auto& child : ProbNode::getChildren(current)) {
			if(accum_p <= p0 && p1 < accum_p + child.first) {
				local_index = (local_index - accum_p) / child.first;
				local_half_span = local_half_span / child.first;
				current = child.second;
				fit();
				break;
			}
			accum_p += child.first;
		}
	}
}

std::string Dasher::getFixed() {
	if(!current) {
		return "";
	}

	// Traverse upward, collecting string.
	std::string result;
	std::shared_ptr<ProbNode> iter = getProbableNode();
	while(iter) {
		result = iter->getString() + result;
		iter = ProbNode::getParent(iter);
	}
	return result;
}

std::shared_ptr<ProbNode> Dasher::getProbableNode() {
	assert(current);

	// By the invariance, current always contains the center.
	std::shared_ptr<ProbNode> node = current;
	std::shared_ptr<ProbNode> prev_node = current;
	float index = local_index;
	float half_span = local_half_span;

	while(half_span <= 1) {
		float accum_p = 0;
		for(auto& child : ProbNode::getChildren(node)) {
			if(accum_p <= index && index < accum_p + child.first) {
				index = (index - accum_p) / child.first;
				half_span /= child.first;

				prev_node = node;
				node = child.second;
				break;
			}
			accum_p += child.first;
		}
	}

	return prev_node;
}

void Dasher::visualize(cairo_t* ctx) {
	cairo_save(ctx);
	cairo_scale(ctx, 250, 250);

	cairo_set_source_rgb(ctx, 1, 1, 1);
	cairo_paint(ctx);

	cairo_translate(ctx, 1, 0);
	cairo_scale(ctx, 1.0 / (2 * local_half_span), 1.0 / (2 * local_half_span));
	cairo_translate(ctx, 0, -(local_index - local_half_span));

	// draw in [-1,0] * [0,1]
	if(current) {
		drawNode(current, ctx, 0, 1);
	}

	cairo_restore(ctx);
}

std::tuple<double, double, double> Dasher::getNodeColor(
	std::shared_ptr<ProbNode> node) {

	if(static_cast<int>(node->getString()[0]) % 2 == 0) {
		return std::make_tuple(0.8, 0.8, 0.9);
	} else {
		return std::make_tuple(0.7, 0.7, 0.8);
	}
}

std::string Dasher::getDisplayString(std::shared_ptr<ProbNode> node) {
	const std::string input = node->getString();
	if(input == " ") {
		return "␣";
	} else {
		return input;
	}
}

// TODO: When a child of a node is almost 0, there's no space for characters.
void Dasher::drawNode(std::shared_ptr<ProbNode> node, cairo_t* ctx, float p0, float p1) {
	const float dp = p1 - p0;
	assert(dp <= 1.0);
	if(dp / (2 * local_half_span) < 0.01) {
		return;
	}

	// Draw box. Don't use cairo_stroke, to avoid node overlap.
	const auto color = getNodeColor(node);

	// outer
	cairo_new_path(ctx);
	cairo_rectangle(ctx, -dp, p0, dp, dp);
	cairo_set_source_rgb(ctx, 0.2, 0.2, 0.2);
	cairo_fill(ctx);

	// inner (left, top, bottom)
	const float margin = dp * 0.01;
	cairo_new_path(ctx);
	cairo_rectangle(ctx,
		-dp + margin, p0 + margin,
		dp - margin, dp - margin * 2);
	cairo_set_source_rgb(ctx,
		std::get<0>(color), std::get<1>(color), std::get<2>(color));
	cairo_fill(ctx);

	// Show text.
	cairo_save(ctx);
	cairo_set_source_rgb(ctx, 0, 0, 0);
	cairo_translate(ctx, -dp, p0);
	cairo_scale(ctx, dp * 0.1, dp * 0.1);
	cairo_translate(ctx, 0, 5);
	cairo_show_text(ctx, getDisplayString(node).c_str());
	cairo_restore(ctx);

	// Draw children.
	if(dp / (2 * local_half_span) >= 0.1) {
		float accum_p = 0;
		for(auto& child : ProbNode::getChildren(node)) {
			assert(0 < child.first && child.first <= 1);

			drawNode(child.second, ctx,
				p0 + dp * accum_p, p0 + dp * (accum_p + child.first));
			accum_p += child.first;
		}
	}
}

}  // namespace


// Copyright Louis Dionne 2015
// Distributed under the Boost Software License, Version 1.0.

#include <boost/hana.hpp>
#include <boost/hana/ext/std/type_traits.hpp>

#include <type_traits>
using namespace boost::hana;
using namespace boost::hana::literals;

// sample(printf-formats)
auto formats = make_map(
  make_pair(char_<'i'>, type<int>),
  make_pair(char_<'f'>, type<double>),
  make_pair(char_<'s'>, type<char*>)

  // ...
);
// end-sample

// sample(printf-core)
template <typename Fmt, typename ...Args>
int type_safe_printf(Fmt fmt, Args const& ...args) {
  static_assert(is_a<String>(fmt),
  "the format string must be a compile-time hana::String");

  auto format_chars = filter(to<Tuple>(fmt), [](auto c) {
    return contains(formats, c);
  });

  static_assert(length(format_chars) == sizeof...(args),
  "number of arguments not matching the number of format characters");

  auto conversions = zip(tuple_t<std::decay_t<Args>...>, format_chars);
  for_each(conversions, fuse([](auto arg_type, auto format_char) {
    static_assert(decltype(formats[format_char] == arg_type){},
    "the type of the argument does not match the format character");
  }));

  return std::printf(to<char const*>(fmt), args...);
};
// end-sample

int main() {

#if 1
// sample(printf-usage)
type_safe_printf(BOOST_HANA_STRING("%i, %f, %s"), 2, 3.4, "abcd");
// end-sample
#elif 0
// sample(printf-wrong_type)
type_safe_printf(BOOST_HANA_STRING("%i, %f, %s"), 2, 2, "abcd");
// end-sample
#elif 0
// sample(printf-wrong_narg)
type_safe_printf(BOOST_HANA_STRING("%i, %f, %s"), 2, 3.4);
// end-sample
#endif

}


#include <eosio/chain/exceptions.hpp>
#include <eosio/chain/resource_limits.hpp>
#include <eosio/chain/resource_limits_private.hpp>
#include <eosio/chain/transaction_metadata.hpp>
#include <eosio/chain/transaction.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <eosio/chain/database_utils.hpp>
#include <algorithm>
#include <eosio/chain/stake.hpp>
#include <eosio/chain/account_object.hpp>
#include <eosio/chain/global_property_object.hpp>

#include <cyberway/chaindb/storage_payer_info.hpp>

namespace eosio { namespace chain { namespace resource_limits {
using namespace stake;

using cyberway::chaindb::cursor_kind;

using resource_index_set = table_set<
   resource_usage_table,
   resource_limits_state_table,
   resource_limits_config_table
>;

static_assert( config::rate_limiting_precision > 0, "config::rate_limiting_precision must be positive" );

static uint64_t update_elastic_limit(uint64_t current_limit, uint64_t average_usage, const elastic_limit_parameters& params) {
   uint64_t result = current_limit;
   if (average_usage > params.target) {
      result = safe_prop(result, params.decrease_rate.numerator, params.decrease_rate.denominator, false);
   } else {
      result = safe_prop(result, params.increase_rate.numerator, params.increase_rate.denominator, false);
   }

   return std::min(std::max(result, params.min), params.max);
}
void elastic_limit_parameters::set(uint64_t target_, uint64_t min_, uint64_t max_, uint32_t average_window_ms, uint16_t decrease_pct, uint16_t increase_pct) {
    EOS_ASSERT(decrease_pct < config::percent_100, resource_limit_exception, "incorrect elastic limit parameter 'decrease_pct'" );
    target = target_;
    min = min_;
    max = max_;
    periods = average_window_ms / config::block_interval_ms;
    EOS_ASSERT( periods > 0, resource_limit_exception, "elastic limit parameter 'periods' cannot be zero" );
    decrease_rate.numerator = config::percent_100 - decrease_pct;
    increase_rate.numerator = config::percent_100 + increase_pct;
}

void resource_limits_state_object::update_virtual_limit(const resource_limits_config_object& cfg, resource_id res) {
   virtual_limits[res] = update_elastic_limit(virtual_limits[res], block_usage_accumulators[res].average(), cfg.limit_parameters[res]);
}

void resource_limits_manager::add_indices() {
   resource_index_set::add_tables(_chaindb);
}

void resource_limits_manager::initialize_database() {
    auto& cfg = _chaindb.emplace<resource_limits_config_object>([](resource_limits_config_object& cfg){
        cfg.limit_parameters.resize(resources_num);
        cfg.account_usage_average_windows.resize(resources_num);
        for (size_t i = 0; i < resources_num; i++) {
            cfg.limit_parameters[i].set(
                config::default_target_virtual_limits[i],
                config::default_min_virtual_limits[i],
                config::default_max_virtual_limits[i],
                config::default_usage_windows[i],
                config::default_virtual_limit_decrease_pct[i],
                config::default_virtual_limit_increase_pct[i]
            );
            cfg.account_usage_average_windows[i] = config::default_account_usage_windows[i] / config::block_interval_ms;
        }
   });
   _chaindb.emplace<resource_limits_state_object>([&cfg](resource_limits_state_object& state) {
      state.block_usage_accumulators.resize(resources_num);
      state.pending_usage.resize(resources_num, 0);
      state.virtual_limits.resize(resources_num);

      for (size_t i = 0; i < resources_num; i++) {
         state.virtual_limits[i] = cfg.limit_parameters[i].max;
      }
   });

}

void resource_limits_manager::add_to_snapshot( const snapshot_writer_ptr& snapshot ) const {
   // TODO: Removed by CyberWay
}

void resource_limits_manager::read_from_snapshot( const snapshot_reader_ptr& snapshot ) {
   // TODO: Removed by CyberWay
}

storage_payer_info resource_limits_manager::get_storage_payer(uint32_t time_slot, account_name owner) {
    return {*this, owner, owner, time_slot};
}

void resource_limits_manager::initialize_account(const account_name& account, const storage_payer_info& payer) {
   _chaindb.emplace<resource_usage_object>(account.value, payer, [&]( resource_usage_object& bu ) {
      bu.accumulators.resize(resources_num);
   });
}

void resource_limits_manager::set_limit_params(const chain_config& chain_cfg) {
    const auto& config = _chaindb.get<resource_limits_config_object>();
    _chaindb.modify(config, [&](resource_limits_config_object& cfg) {

        for (size_t i = 0; i < resources_num; i++) {
            cfg.limit_parameters[i].set(
                chain_cfg.target_virtual_limits[i],
                chain_cfg.min_virtual_limits[i],
                chain_cfg.max_virtual_limits[i],
                chain_cfg.usage_windows[i],
                chain_cfg.virtual_limit_decrease_pct[i],
                chain_cfg.virtual_limit_increase_pct[i]
            );
            cfg.account_usage_average_windows[i] = chain_cfg.account_usage_windows[i] / config::block_interval_ms;
        }
   });
}

void resource_limits_manager::update_account_usage(const flat_set<account_name>& accounts, uint32_t time_slot) {
   const auto& config = _chaindb.get<resource_limits_config_object>();
   auto usage_table = _chaindb.get_table<resource_usage_object>();
   auto owner_idx = usage_table.get_index<by_id>();
   for( const auto& a : accounts ) {
      const auto& usage = owner_idx.get( a );
      usage_table.modify( usage, [&]( auto& bu ) {
          for (size_t i = 0; i < resources_num; i++) {
              bu.accumulators[i].add(0, time_slot, config.account_usage_average_windows[i]);
          }
      });
   }
}

void resource_limits_state_object::add_pending_delta(int64_t delta, const chain_config& chain_cfg, resource_id res) {
    static auto constexpr large_number_no_overflow = std::numeric_limits<int64_t>::max() / 2;
    delta = std::max(std::min(delta, large_number_no_overflow), -large_number_no_overflow);
    auto& pending = pending_usage[res];
    pending = std::max(std::min(pending + delta, large_number_no_overflow), -large_number_no_overflow);
    decltype(delta) max = chain_cfg.max_block_usage[res];
    EOS_ASSERT(pending <= max, block_resource_exhausted,
        "Block has insufficient resources(${res}): delta = ${delta}, new_pending = ${new_pending}, max = ${max}",
        ("res", static_cast<int>(res))("delta", delta)("new_pending", pending)("max", max));
}

void resource_limits_manager::add_transaction_usage(
    const flat_set<account_name>& accounts, const ratios& prices,
    uint64_t cpu_usage, uint64_t net_usage, uint64_t ram_usage,
    fc::time_point pending_block_time, bool validate
) {
   auto state_table = _chaindb.get_table<resource_limits_state_object>();
   const auto& state = state_table.get();
   const auto& config = _chaindb.get<resource_limits_config_object>();
   auto usage_table = _chaindb.get_table<resource_usage_object>();
   auto owner_idx = usage_table.get_index<by_id>();
   auto time_slot = block_timestamp_type(pending_block_time).slot;

   for( const auto& a : accounts ) {

      const auto& usage = owner_idx.get( a );

      usage_table.modify( usage, [&]( auto& bu ) {
          bu.accumulators[CPU].add(cpu_usage, time_slot, config.account_usage_average_windows[CPU]);
          bu.accumulators[NET].add(net_usage, time_slot, config.account_usage_average_windows[NET]);
          bu.accumulators[RAM].add(ram_usage, time_slot, config.account_usage_average_windows[RAM]);
      });
      if (validate) {
          // validate the resources available
          get_account_balance(pending_block_time, a, prices, true);
      }
   }
   // account for this transaction in the block and do not exceed those limits either
   const auto& chain_cfg = _chaindb.get<global_property_object>().configuration;
   state_table.modify(state, [&](resource_limits_state_object& rls) {
      rls.add_pending_delta(cpu_usage, chain_cfg, CPU);
      rls.add_pending_delta(net_usage, chain_cfg, NET);
      rls.add_pending_delta(ram_usage, chain_cfg, RAM);
   });
}

void resource_limits_manager::add_storage_usage(
   const flat_map<account_name, int64_t>& deltas, const ratios& prices, fc::time_point pending_block_time, bool validate
) {
   if( deltas.empty() ) {
       return;
   }

   symbol_code token_code { symbol(CORE_SYMBOL).to_symbol_code() };

   const stake_param_object* param = nullptr;
   const stake_stat_object*  stat  = nullptr;

   if (!(_chaindb.find<stake_param_object>(token_code.value, cursor_kind::OneRecord)) ||
       !(stat = _chaindb.find<stake_stat_object>(token_code.value, cursor_kind::OneRecord)) ||
       !stat->enabled || stat->total_staked == 0
   ) {
      return;
   }

   auto  time_slot = block_timestamp_type(pending_block_time).slot;
   auto& config = _chaindb.get<resource_limits_config_object>();

   int64_t delta = 0;
   for( const auto& a : deltas ) {
      if( 0 == a.second ) {
          continue;
      }
      delta += a.second;

      auto& usage = _chaindb.get<resource_usage_object>(a.first);
      _chaindb.modify( usage, [&]( auto& u ) {
          u.accumulators[STORAGE].add(a.second, time_slot, config.account_usage_average_windows[STORAGE]);
      });
      if( validate && (a.second > 0 || _validate_storage_price)) {
         get_account_balance(pending_block_time, a.first, prices, true);
      }
   }

   if ( 0 == delta ) {
       return;
   }

   auto  state_table = _chaindb.get_table<resource_limits_state_object>();
   auto& state = state_table.get();
   state_table.modify(state, [&](resource_limits_state_object& rls) {
      rls.add_pending_delta(delta, _chaindb.get<global_property_object>().configuration, STORAGE);
   });
}

void resource_limits_manager::add_storage_usage(const account_name& account, int64_t delta, uint32_t time_slot) {
   if (delta == 0) {
      return;
   }

   symbol_code token_code { symbol(CORE_SYMBOL).to_symbol_code() };

   const stake_param_object* param = nullptr;
   const stake_stat_object* stat = nullptr;

   if (_chaindb.get<account_object>(account).privileged || //assignments:
       !(_chaindb.find<stake_param_object>(token_code.value, cursor_kind::OneRecord)) ||
       !(stat  = _chaindb.find<stake_stat_object>(token_code.value, cursor_kind::OneRecord)) ||
       !stat->enabled || stat->total_staked == 0
   ) {

      return;
   }

   const auto& config = _chaindb.get<resource_limits_config_object>();
   auto state_table  = _chaindb.get_table<resource_limits_state_object>();
   const auto& state = state_table.get();
   state_table.modify(state, [&](resource_limits_state_object& rls) {
      rls.add_pending_delta(delta, _chaindb.get<global_property_object>().configuration, STORAGE);
   });
   auto& usage = _chaindb.get<resource_usage_object>(account);
   _chaindb.modify( usage, [&]( auto& u ) {
      u.accumulators[STORAGE].add(delta, time_slot, config.account_usage_average_windows[STORAGE]);
   });
}

std::vector<uint64_t> resource_limits_manager::get_account_usage(const account_name& account)const {
    const auto& config = _chaindb.get<resource_limits_config_object>();
    auto usage_index = _chaindb.get_index<resource_usage_object, by_id>();
    const auto& usage = usage_index.get(account);
    std::vector<uint64_t> ret(resources_num);

    for (size_t i = 0; i < resources_num; i++) {
        ret[i] = downgrade_cast<uint64_t>(integer_divide_ceil(
                            static_cast<uint128_t>(usage.accumulators[i].value_ex) * config.account_usage_average_windows[i],
                            static_cast<uint128_t>(config::rate_limiting_precision)));
    }
    return ret;
}

void resource_limits_manager::process_block_usage(uint32_t time_slot) {
   auto state_table = _chaindb.get_table<resource_limits_state_object>();
   const auto& s = state_table.get();
   const auto& config = _chaindb.get<resource_limits_config_object>();
   state_table.modify(s, [&](resource_limits_state_object& state){
      // apply pending usage, update virtual limits and reset the pending
      //block_usage_accumulators
      for (size_t i = 0; i < resources_num; i++) {
         state.block_usage_accumulators[i].add(state.pending_usage[i], time_slot, config.limit_parameters[i].periods);
         state.update_virtual_limit(config, static_cast<resource_id>(i));
         state.pending_usage[i] = 0;
      }
   });
}

uint64_t resource_limits_manager::get_virtual_block_limit(resource_id res) const {
   const auto& state = _chaindb.get<resource_limits_state_object>();
   return state.virtual_limits[res];
}

uint64_t resource_limits_manager::get_block_limit(resource_id res, const chain_config& chain_cfg) const {
   const auto& state = _chaindb.get<resource_limits_state_object>();
   const auto& config = _chaindb.get<resource_limits_config_object>();
   uint64_t usage = std::max(state.pending_usage[res], int64_t(0));
   uint64_t max = chain_cfg.max_block_usage[res];
   EOS_ASSERT(max >= usage, resource_limit_exception, "SYSTEM: incorrect usage");
   return max - usage;
}

ratios resource_limits_manager::get_pricelist() const {

    const auto& state  = _chaindb.get<resource_limits_state_object>();
    const auto& config = _chaindb.get<resource_limits_config_object>();

    std::vector<uint64_t> used_pct(resources_num);
    for (size_t i = 0; i < resources_num; i++) {
        used_pct[i] = std::max(safe_share_to_pct(state.block_usage_accumulators[i].average(), config.limit_parameters[i].target), config::min_resource_usage_pct);
    }
    uint64_t used_pct_sum = std::accumulate(used_pct.begin(), used_pct.end(), 0);

    ratios ret(resources_num, ratio{0ll, 1ll});

    symbol_code token_code { symbol(CORE_SYMBOL).to_symbol_code() };
    const stake_param_object* param = nullptr;
    const stake_stat_object* stat = nullptr;

    if ((param = _chaindb.find<stake_param_object>(token_code.value, cursor_kind::OneRecord)) &&
        (stat  = _chaindb.find<stake_stat_object>(token_code.value, cursor_kind::OneRecord)) &&
        stat->enabled && stat->total_staked != 0
    ) {
        EOS_ASSERT(stat->total_staked > 0, resource_limit_exception, "SYSTEM: incorrect total_staked");

        for (size_t i = 0; i < resources_num; i++) {
            auto virtual_capacity_in_window = static_cast<uint128_t>(state.virtual_limits[i]) * config.account_usage_average_windows[i];
            ret[i] = ratio {
                safe_prop_ceil<uint64_t>(stat->total_staked, used_pct[i], used_pct_sum),
                static_cast<uint64_t>(std::min(virtual_capacity_in_window, static_cast<uint128_t>(UINT64_MAX)))
            };
        }
    }

    return ret;
}

ratio resource_limits_manager::get_resource_usage_by_account_cost_ratio(account_name account, resource_id res) const {
    const auto resources_usage = get_account_usage(account);
    const auto price_list = get_pricelist();

    const auto requested_resource_price = price_list.at(res);
    const auto requested_resource_usage = resources_usage.at(res);

    uint64_t total_usage_cost = 0;
    for (size_t i = 0; i < resources_num; i++) {
        total_usage_cost += safe_prop_ceil(resources_usage[i], price_list[i].numerator, price_list[i].denominator);
    }

    return ratio{safe_prop_ceil(requested_resource_usage, requested_resource_price.numerator, requested_resource_price.denominator), total_usage_cost};
}

ratio resource_limits_manager::get_account_stake_ratio(fc::time_point pending_block_time, const account_name& account, bool update_state) {
    symbol_code token_code { symbol(CORE_SYMBOL).to_symbol_code() };

    const stake_param_object* param = nullptr;
    const stake_stat_object* stat = nullptr;

    if (_chaindb.get<account_object>(account).privileged || //assignments:
        !(param = _chaindb.find<stake_param_object>(token_code.value)) ||
        !(stat  = _chaindb.find<stake_stat_object>(token_code.value)) ||
        !stat->enabled || stat->total_staked == 0) {

        return {0,0};
    }

    EOS_ASSERT(stat->total_staked > 0, resource_limit_exception, "SYSTEM: incorrect total_staked");

    auto agents_table = _chaindb.get_table<stake_agent_object>();
    auto agents_idx = agents_table.get_index<stake_agent_object::by_key>();

    uint64_t staked = 0;
    auto agent = agents_idx.find(agent_key(token_code, account), cursor_kind::OneRecord);
    if (agent != agents_idx.end()) {
        if (update_state) {
            update_proxied(_chaindb, get_storage_payer(block_timestamp_type(pending_block_time).slot, account_name()),
                pending_block_time.sec_since_epoch(), token_code, account, false);
        }
        staked = agent->get_effective_stake();
    }

    return ratio{staked, static_cast<uint64_t>(stat->total_staked)};
}

uint64_t resource_limits_manager::get_used_resources_cost(account_name account, const std::vector<ratio>& prices, uint64_t max_cost) const {
    auto res_usage = get_account_usage(account);

    uint64_t cost = 0;
    for (size_t i = 0; i < resources_num; i++) {
        auto add = safe_prop_ceil(res_usage[i], prices[i].numerator, prices[i].denominator);
        cost = (UINT64_MAX - cost) > add ? cost + add : UINT64_MAX;
    }

    EOS_ASSERT(max_cost >= cost, account_resources_exceeded,
        "account ${a} has insufficient staked tokens (${s}).\n usage: cpu ${uc}, net ${un}, ram ${ur}, storage ${us}; \n prices: cpu ${pc}, net ${pn}, ram ${pr}, storage ${ps};\n cost ${c}",
        ("a", account)("s",max_cost)
        ("uc", res_usage[CPU])("un", res_usage[NET])("ur", res_usage[RAM])("us", res_usage[STORAGE])
        ("pc", prices   [CPU])("pn", prices   [NET])("pr", prices   [RAM])("ps", prices   [STORAGE])
        ("c", cost));

    return cost;
}

uint64_t resource_limits_manager::get_account_balance(fc::time_point pending_block_time, const account_name& account, const std::vector<ratio>& prices, bool update_state) {

    const auto account_stake_ratio = get_account_stake_ratio(pending_block_time, account, update_state);
    const auto staked = account_stake_ratio.numerator;
    const auto total_staked = account_stake_ratio.denominator;

    if (total_staked == 0) {
        return UINT64_MAX;
    }
    uint64_t cost = get_used_resources_cost(account, prices, update_state ? staked : UINT64_MAX);
    return (staked > cost) ? (staked - cost) : 0;
}

} } } /// eosio::chain::resource_limits


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2014 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2014 Bruno Lalande, Paris, France.
// Copyright (c) 2014 Mateusz Loskot, London, UK.
// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland.

// This file was modified by Oracle on 2018.
// Modifications copyright (c) 2018, Oracle and/or its affiliates.

// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_MAX_SIZE_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_MAX_SIZE_HPP

#include <cstddef>

#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/coordinate_dimension.hpp>
#include <boost/geometry/util/math.hpp>

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail
{

template <typename Box, std::size_t Dimension>
struct get_max_size_box
{
    static inline typename coordinate_type<Box>::type apply(Box const& box)
    {
        typename coordinate_type<Box>::type s
            = geometry::math::abs(geometry::get<1, Dimension>(box) - geometry::get<0, Dimension>(box));

        return (std::max)(s, get_max_size_box<Box, Dimension - 1>::apply(box));
    }
};

template <typename Box>
struct get_max_size_box<Box, 0>
{
    static inline typename coordinate_type<Box>::type apply(Box const& box)
    {
        return geometry::math::abs(geometry::get<1, 0>(box) - geometry::get<0, 0>(box));
    }
};

// This might be implemented later on for other geometries too.
// Not dispatched yet.
template <typename Box>
inline typename coordinate_type<Box>::type get_max_size(Box const& box)
{
    return get_max_size_box<Box, dimension<Box>::value - 1>::apply(box);
}

} // namespace detail
#endif // DOXYGEN_NO_DETAIL

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_MAX_SIZE_HPP


/**************************************************************
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 *************************************************************/

#ifndef SD_SLIDESORTER_CONTROLLER_ANIMATION_FUNCTION_HXX
#define SD_SLIDESORTER_CONTROLLER_ANIMATION_FUNCTION_HXX

#include "model/SlsSharedPageDescriptor.hxx"
#include <basegfx/point/b2dpoint.hxx>
#include <boost/noncopyable.hpp>
#include <boost/function.hpp>
#include <tools/gen.hxx>
#include <vector>

namespace sd { namespace slidesorter { namespace view {
class SlideSorterView;
} } }

namespace sd { namespace slidesorter { namespace controller {

/** A collection of functions that are useful when creating animations.
    They are collected here until a better place is found.
*/
class AnimationFunction
    : private ::boost::noncopyable
{
public:
    /** Acceleration function that maps [0,1] to [0,1] linearly, ie it
        returns the given time value unaltered.
    */
    static double Linear (const double nTime);

    /** Acceleration function that maps [0,1] to [0,1].  Speed starts fast
        and ends slow following the sine function.
    */
    static double FastInSlowOut_Sine (const double nTime);

    /** Acceleration function that maps [0,1] to [0,1].  Speed starts fast
        and ends slow following the square root function.
    */
    static double FastInSlowOut_Root (const double nTime);

    /** Acceleration function that maps [0,1] to [0,0].  Speed starts slow,
        rises, drops and ends slow following the sine function.
    */
    static double SlowInSlowOut_0to0_Sine (const double nTime);

    /** Acceleration function that maps [0,1] to [0,0].  Speed starts slow,
        rises and drops several times and ends slow following multiple
        cycles of the the sine function.
    */
    static double Vibrate_Sine (const double nTime);

    /** Scale point linearly.
    */
    static Point ScalePoint (const Point& rPoint, const double nTime);

    /** Blend two points together according to the given weight.
    */
    static double Blend (const double nStartValue, const double nEndValue, const double nWeight);

    /** Apply a gradual visual state change.  The kind of change, i.e. the
        previous and the new states are expected to be already set.  This
        method only adjusts the blending of the visual representation from
        one state to the other.
    */
    static void ApplyVisualStateChange (
        const model::SharedPageDescriptor& rpDescriptor,
        view::SlideSorterView& rView,
        const double nTime);

    /** Apply a gradual change of a previously set offset to the location of
        a page object.
    */
    static void ApplyLocationOffsetChange (
        const model::SharedPageDescriptor& rpDescriptor,
        view::SlideSorterView& rView,
        const Point aLocationOffset);

    /** Apply a gradual change the alpha value from the old value to a
        new value (set prior to this call.)
    */
    static void ApplyButtonAlphaChange(
        const model::SharedPageDescriptor& rpDescriptor,
        view::SlideSorterView& rView,
        const double nButtonAlpha,
        const double nButtonBarAlpha);
};

class AnimationBezierFunction
{
public:
    /** Create a cubic bezier curve whose start and end points are given
        implicitly as P0=(0,0) and P3=(1,1).
    */
    AnimationBezierFunction (
        const double nX1,
        const double nY1,
        const double nX2,
        const double nY2);

    /** Create a cubic bezier curve whose start and end points are given
        implicitly as P0=(0,0) and P3=(1,1).  The second control point is
        implicitly given as P2=(1-nY1,1-nX1).
    */
    AnimationBezierFunction (
        const double nX1,
        const double nY1);

    ::basegfx::B2DPoint operator() (const double nT);

private:
    const double mnX1;
    const double mnY1;
    const double mnX2;
    const double mnY2;

    double EvaluateComponent (
        const double nT,
        const double nV1,
        const double nV2);
};

/** Turn a parametric function into one whose y-Values depend on its
    x-Values.  Note a lot of interpolation takes place.  The resulting
    accuracy should be good enough for the purpose of acceleration
    function for animations.
*/
class AnimationParametricFunction
{
public:
    typedef ::boost::function<basegfx::B2DPoint(double)> ParametricFunction;
    AnimationParametricFunction (const ParametricFunction& rFunction);

    double operator() (const double nX);

private:
    /** y-Values of the parametric function given to the constructor
        evaluated (and interpolated) for evenly spaced x-Values.
    */
    ::std::vector<double> maY;
};

} } } // end of namespace ::sd::slidesorter::controller

#endif


/*
	Copyright (C) 2003-2014 by David White <davewx7@gmail.com>

	This software is provided 'as-is', without any express or implied
	warranty. In no event will the authors be held liable for any damages
	arising from the use of this software.

	Permission is granted to anyone to use this software for any purpose,
	including commercial applications, and to alter it and redistribute it
	freely, subject to the following restrictions:

	   1. The origin of this software must not be misrepresented; you must not
	   claim that you wrote the original software. If you use this software
	   in a product, an acknowledgement in the product documentation would be
	   appreciated but is not required.

	   2. Altered source versions must be plainly marked as such, and must not be
	   misrepresented as being the original software.

	   3. This notice may not be removed or altered from any source
	   distribution.
*/

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/random/mersenne_twister.hpp>

#include "asserts.hpp"
#include "unit_test.hpp"
#include "uuid.hpp"

namespace
{
	boost::mt19937* twister_rng()
	{
		static boost::mt19937 ran;
		ran.seed(static_cast<unsigned>(boost::posix_time::microsec_clock::local_time().time_of_day().total_milliseconds()));
		return &ran;
	}

	UNIT_TEST(serialize_uuid)
	{
		for(int i = 0; i != 8; ++i) {
			boost::uuids::uuid id = generate_uuid();
			const bool succeeded = id == read_uuid(write_uuid(id));
			CHECK_EQ(succeeded, true);
		}
	}
}

boost::uuids::uuid generate_uuid()
{
	static boost::uuids::basic_random_generator<boost::mt19937> gen(twister_rng());
	return gen();
}

std::string write_uuid(const boost::uuids::uuid& id)
{
	char result[33];
	char* ptr = result;
	for(auto num : id) {
		sprintf(ptr, "%02x", static_cast<int>(num));
		ptr += 2;
	}
	return std::string(result, result+32);
}

boost::uuids::uuid read_uuid(const std::string& s)
{
	boost::uuids::uuid result;

	const std::string& nums = s;
	const char* ptr = nums.c_str();
	ASSERT_LOG(nums.size() == 32, "Trying to deserialize bad UUID: " << nums);
	for(auto itor = result.begin(); itor != result.end(); ++itor) {
		char buf[3];
		buf[0] = *ptr++;
		buf[1] = *ptr++;
		buf[2] = 0;

		*itor = static_cast<uint8_t>(strtol(buf, nullptr, 16));
	}

	return result;
}

boost::uuids::uuid addr_to_uuid(const std::string& s)
{
	auto itor = s.begin();
	if(s.size() > 2 && s[0] == '0' && s[1] == 'x') {
		itor += 2;
	}

	std::string str(itor, s.end());
	const size_t sz = str.size();
	if(sz < 32) {
		str.resize(32);
		std::fill(str.begin() + sz, str.end(), '0');
	} else {
		str.resize(32);
	}

	return read_uuid(str);
}

BENCHMARK(generate_uuid) {
	generate_uuid();
	BENCHMARK_LOOP {
		generate_uuid();
	}
}