//==================================================================================================
/*!
  @file

  @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_COMMON_SCALAR_FUNCTION_REVERSEBITS_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_SCALAR_FUNCTION_REVERSEBITS_HPP_INCLUDED

#include <boost/simd/detail/dispatch/function/overload.hpp>
#include <boost/config.hpp>

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;
  BOOST_DISPATCH_OVERLOAD ( reversebits_
                          , (typename A0)
                          , bd::cpu_
                          , bd::scalar_< bd::ints8_<A0> >
                          )
  {
    BOOST_FORCEINLINE A0 operator() ( A0 a0) const BOOST_NOEXCEPT
    {
      return std::uint8_t(
        ((uint8_t(a0) * 0x0802LU & 0x22110LU)
         | (uint8_t(a0) * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16
      );
    }
  };

  BOOST_DISPATCH_OVERLOAD ( reversebits_
                          , (typename A0)
                          , bd::cpu_
                          , bd::scalar_< bd::ints64_<A0> >
                          )
  {
    BOOST_FORCEINLINE A0 operator() ( A0 a0) const BOOST_NOEXCEPT
    {
      union { A0 a; std::uint32_t b[2]; } z = {a0};
      z.b[0] = reversebits(z.b[0]);
      z.b[1] = reversebits(z.b[1]);
      std::swap(z.b[0], z.b[1]);
      return z.a;
    }
  };

  BOOST_DISPATCH_OVERLOAD ( reversebits_
                          , (typename A0)
                          , bd::cpu_
                          , bd::scalar_< bd::ints16_<A0> >
                          )
  {
    BOOST_FORCEINLINE A0 operator() ( A0 a0) const BOOST_NOEXCEPT
    {
      union { A0 a; std::uint8_t b[2]; } z = {a0};
      z.b[0] = reversebits(z.b[0]);
      z.b[1] = reversebits(z.b[1]);
      std::swap(z.b[0], z.b[1]);
      return  z.a;
    }
  };

  BOOST_DISPATCH_OVERLOAD ( reversebits_
                          , (typename A0)
                          , bd::cpu_
                          , bd::scalar_< bd::ints32_<A0> >
                          )
  {
    BOOST_FORCEINLINE A0 operator() ( A0 a0) const BOOST_NOEXCEPT
    {
      union { A0 a; std::uint16_t b[2]; } z = {a0};
      z.b[0] = reversebits(z.b[0]);
      z.b[1] = reversebits(z.b[1]);
      std::swap(z.b[0], z.b[1]);
      return z.a;
    }
  };
} } }

#endif


//==================================================================================================
/*!
  @file

  @copyright 2015 NumScale SAS
  @copyright 2015 J.T. Lapreste

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_COMMON_FUNCTION_GENERIC_REM_PIO2_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_FUNCTION_GENERIC_REM_PIO2_HPP_INCLUDED

#include <boost/simd/constant/zero.hpp>
#include <boost/simd/function/simd/rem_pio2_cephes.hpp>
#include <boost/simd/function/simd/rem_pio2_medium.hpp>
#include <boost/simd/function/simd/rem_pio2_straight.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <boost/dispatch/meta/scalar_of.hpp>
#include <boost/simd/meta/cardinal_of.hpp>
#include <boost/simd/arch/common/detail/tags.hpp>
#include <boost/dispatch/function/overload.hpp>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/fusion/tuple.hpp>

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;
  namespace bs = boost::simd;
  BOOST_DISPATCH_OVERLOAD ( rem_pio2_
                          , (typename A0)
                          ,  bd::cpu_
                          ,  bd::generic_ < bd::floating_<A0> >
                          )
  {
    using itype = bd::as_integer_t<A0>;
    using result = boost::fusion::tuple<itype,A0,A0>;

    BOOST_FORCEINLINE result operator() ( A0 const& a0) const BOOST_NOEXCEPT
    {
      A0 second, third;
      itype const first = rem_pio2(a0, second,third);
      return result(first, second, third); //brace constructor is explicit in copy initialisation.
    }
  };

  BOOST_DISPATCH_OVERLOAD (rem_pio2_
                          , (typename A0)
                          , bd::cpu_
                          , bd::generic_ < bd::floating_<A0> >
                          , bd::generic_ < bd::floating_<A0> >
                          )
  {
    using result = bd::as_integer_t<A0>;
    BOOST_FORCEINLINE result operator() ( A0 const& a0,
                                            A0 & xr) const BOOST_NOEXCEPT
    {
      A0 xc;
      return rem_pio2(a0, xr, xc);
    }
  };

  BOOST_DISPATCH_OVERLOAD ( rem_pio2_
                          , (typename A0, typename A1)
                          , bd::cpu_
                          , bd::generic_ <bd::floating_<A0>  >
                          , bd::generic_ <bd::floating_<A0>  >
                          , bd::target_ <bd::unspecified_<A1> >
                          )
  {
    using result = bd::as_integer_t<A0>;

    BOOST_FORCEINLINE result operator() ( A0 const& a0, A0 & xr, A1 const&) const BOOST_NOEXCEPT
    {
      typedef typename A1::type selector;
      return rempio2<selector, void>::rem(a0, xr);
    }

    template < class T, class dummy = void> struct rempio2
    {
      static BOOST_FORCEINLINE result rem( A0 const&, A0 &) BOOST_NOEXCEPT
      {
        BOOST_ASSERT_MSG(false, "wrong target for rem_pio2");
        return Zero<result>();
      }
    };

    template < class dummy> struct rempio2 < tag::big_tag, dummy>
    {
      static BOOST_FORCEINLINE result rem( A0 const& x, A0 & xr) BOOST_NOEXCEPT
      {
        return rem_pio2(x, xr);
      }
    };

    template < class dummy> struct rempio2 < tag::very_small_tag, dummy >
    {
      static BOOST_FORCEINLINE result rem( A0 const& x, A0 & xr) BOOST_NOEXCEPT
      {
        return rem_pio2_straight(x, xr);
      }
    };

    template < class dummy> struct rempio2 < tag::small_tag, dummy >
    {
      static BOOST_FORCEINLINE result rem( A0 const& x, A0 & xr) BOOST_NOEXCEPT
      {
        return rem_pio2_cephes(x, xr);
      }
    };

    template < class dummy> struct rempio2 < tag::medium_tag, dummy >
    {
      static BOOST_FORCEINLINE result rem( A0 const& x, A0 & xr) BOOST_NOEXCEPT
      {
        return rem_pio2_medium(x, xr);
      }
    };
  };

} } }

#endif


// Copyright (c) 2012-2013 The PPCoin developers
// Copyright (c) 2014 The BlackCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <boost/assign/list_of.hpp>
#include <index/txindex.h>

#include <pos.h>
#include <key_io.h>
#include <logging.h>
#include <validation.h>

#include <chain.h>
#include <primitives/transaction.h>
#include <consensus/validation.h>
#include <txdb.h>
#include <arith_uint256.h>
#include <hash.h>
#include <timedata.h>
#include <chainparams.h>
#include <script/sign.h>
#include <consensus/consensus.h>

static const int MODIFIER_INTERVAL_RATIO = 3;

// Get time weight
int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd)
{
    // Kernel hash weight starts from 0 at the min age
    // this change increases active coins participating the hash and helps
    // to secure the network when proof-of-stake difficulty is low

    return std::min(nIntervalEnd - nIntervalBeginning - Params().GetConsensus().nStakeMinAge, (int64_t)Params().GetConsensus().nStakeMaxAge);
}

uint256 ComputeStakeModifier(const CBlockIndex* pindexPrev, const uint256& kernel)
{
    if (!pindexPrev)
        return uint256(); // genesis block's modifier is 0

    CDataStream ss(SER_GETHASH, 0);
    ss << kernel << pindexPrev->nStakeModifier;
    return Hash(ss.begin(), ss.end());
}

bool CheckStakeKernelHash(const CBlockIndex* pindexPrev, unsigned int nBits, const CTransactionRef& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProof, bool fPrintProofOfStake)
{
    if (nTimeTx < txPrev->nTime)  // Transaction timestamp violation
        return error(" %s, nTime violation on coinstake %s", __func__, txPrev->GetHash().ToString());

    if (txPrev->nTime + Params().GetConsensus().nStakeMinAge > nTimeTx) // Min age requirement
        return error(" %s, min age violation on coinstake %s", __func__, txPrev->GetHash().ToString());

    arith_uint256 bnTarget = arith_uint256().SetCompact(nBits);
    int64_t nValueIn = txPrev->vout[prevout.n].nValue.GetAmount();
    arith_uint256 bnWeight = arith_uint256(nValueIn) * GetWeight((int64_t)txPrev->nTime, (int64_t)nTimeTx) / COIN / (24 * 60 * 60);

    CDataStream ss(SER_GETHASH, 0);
    ss << pindexPrev->nStakeModifier << txPrev->nTime << prevout.hash << prevout.n << nTimeTx;
    hashProof = Hash(ss.begin(), ss.end());

    if (fPrintProofOfStake)
    {
        LogPrintf("%s : nStakeModifier=%s, txPrev.nTime=%u, txPrev.vout.hash=%s, txPrev.vout.n=%u, nTime=%u, hashProofOfStake=%s, targetProofOfStake=%s\n", __func__, pindexPrev->nStakeModifier.GetHex().c_str(),
            txPrev->nTime, prevout.hash.ToString(), prevout.n, nTimeTx, hashProof.ToString(), (bnWeight * bnTarget).ToString());
    }

    // We need to convert type so it can be compared to target
    if (UintToArith256(hashProof) > bnWeight * bnTarget){
		if(gArgs.GetBoolArg("-debug", false))
            LogPrintf(" %s, stake hash does not meet target. \n modifier=%s \n nPrevout=%u, nTimeTx =%d , \n hashProof=%s , \n target = %s \n", __func__, pindexPrev->nStakeModifier.GetHex().c_str(),  prevout.n, nTimeTx, UintToArith256(hashProof).ToString(), (bnWeight * bnTarget).ToString());
        return false;
	}

    return true;
}

bool CheckKernel(unsigned int nBits, uint32_t nTime, const COutPoint& prevout)
{
    CBlockIndex* pindexPrev = ::ChainActive().Tip();
    uint256 hashProof;
    const Consensus::Params& params = Params().GetConsensus();

    if (!g_txindex)
        return error("%s : transaction index is not enabled", __func__);

    // Get transaction index for the previous transaction
    CDiskTxPos postx;
    if (!pblocktree->ReadTxIndex(prevout.hash, postx))
        return error("%s : transaction index not available for %s", __func__, prevout.hash.ToString());

    // Read txPrev and header of its block
    CBlockHeader header;
    CTransactionRef txPrev;
    {
        CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
        try {
            file >> header;
            fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
            file >> txPrev;
        } catch (std::exception &e) {
            return error("%s() : deserialize or I/O error in CheckKernel()", __func__);
        }
        if (txPrev->GetHash() != prevout.hash)
            return error("%s() : txid mismatch in CheckKernel()", __func__);
    }

    // Check minimum age requirement
    if (header.GetBlockTime() + params.nStakeMinAge > nTime)
        return error(" %s, stake prevout is not mature in block %s", __func__, header.GetHash().ToString());

    return CheckStakeKernelHash(pindexPrev, nBits, txPrev, prevout, nTime, hashProof, gArgs.GetBoolArg("-debug", false));
}

// Check kernel hash target and coinstake signature
bool CheckProofOfStake(CBlockIndex* pindexPrev, const CTransactionRef& tx, unsigned int nBits, uint256& hashProof, CCoinsViewCache& view)
{
    if (!tx->IsCoinStake())
        return error("CheckProofOfStake() :  called on non-coinstake %s ", tx->vin[0].prevout.hash.ToString());

    // Kernel (input 0) must match the stake hash target (nBits)
    const CTxIn& txin = tx->vin[0];

    if (!g_txindex)
        return error("%s: transaction index is not enabled ", __func__);

    // Get transaction index for the previous transaction
    CDiskTxPos postx;
    if (!pblocktree->ReadTxIndex(txin.prevout.hash, postx))
        return error("%s: transaction index not available ", __func__);

    // Read txPrev and header of its block
    CBlockHeader header;
    CTransactionRef txPrev;
    {
        CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
        try {
            file >> header;
            fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
            file >> txPrev;
        } catch (std::exception &e) {
            return error("%s() : deserialize or I/O error in CheckProofOfStake()", __PRETTY_FUNCTION__);
        }
        if (txPrev->GetHash() != txin.prevout.hash)
            return error("%s() : txid mismatch in CheckProofOfStake()", __PRETTY_FUNCTION__);
    }

    // Verify signature
    {
        unsigned int nIn = 0;
        const CTxOut& prevOut = txPrev->vout[tx->vin[nIn].prevout.n];
        const CScriptWitness* pScriptWitness = (tx->witness.vtxinwit.size() > nIn ? &tx->witness.vtxinwit[nIn].scriptWitness : nullptr);
        TransactionSignatureChecker checker(&(*tx), nIn, prevOut.nValue, PrecomputedTransactionData(*tx));
        ScriptError serror = SCRIPT_ERR_OK;

        if (!VerifyScript(tx->vin[nIn].scriptSig, prevOut.scriptPubKey, pScriptWitness, SCRIPT_VERIFY_P2SH, checker, &serror))
            return error(" VerifyScript failed on coinstake %s %s", tx->GetHash().ToString(), ScriptErrorString(serror));
    }

    return CheckStakeKernelHash(pindexPrev, nBits, txPrev, txin.prevout, tx->nTime, hashProof, gArgs.GetBoolArg("-debug", false));
}

bool CheckRecoveredPubKeyFromBlockSignature(CBlockIndex* pindexPrev, const CBlockHeader& block, CCoinsViewCache& view) {
    Coin coinPrev;
    {
		LOCK(cs_main);
        if(!view.GetCoin(block.prevoutStake, coinPrev)){
            if(!GetSpentCoinFromMainChain(pindexPrev, block.prevoutStake, &coinPrev)) {
                return error("CheckRecoveredPubKeyFromBlockSignature(): Could not find %s and it was not at the tip", block.prevoutStake.hash.GetHex());
            }
        }
    }
    uint256 hash = block.GetHashWithoutSign();
    CPubKey pubkey;

    if(block.vchBlockSig.empty()) {
        return error("CheckRecoveredPubKeyFromBlockSignature(): Signature is empty\n");
    }

    for(uint8_t recid = 0; recid <= 3; ++recid) {
        for(uint8_t compressed = 0; compressed < 2; ++compressed) {
            if(!pubkey.RecoverLaxDER(hash, block.vchBlockSig, recid, compressed)) {
                continue;
            }

            CTxDestination address;
            txnouttype txType=TX_NONSTANDARD;
            if(ExtractDestination(coinPrev.out.scriptPubKey, address, &txType)){
				//LogPrintf(" type = %s \n",GetTxnOutputType(txType));
				//LogPrintf(" destination  = %s \n",EncodeDestination(address));

                if ((txType == TX_PUBKEY || txType == TX_PUBKEYHASH) && address.type() == typeid(PKHash)) {
                    if(PKHash(pubkey) == boost::get<PKHash>(address)) {
                        return true;
                    }
                    else{
						//LogPrintf(" %s", "key mismatch  \n");
					}
                }
                else{

					LogPrintf(" %s", "unsupported type \n");
				}
            }
            else{
				LogPrintf(" %s", "could not extract dest \n");
			}
        }
    }
    return false;
}

// Check whether the coinstake timestamp meets protocol
bool CheckCoinStakeTimestamp(uint32_t nTimeBlock, uint32_t nTimeTx)
{
    return (nTimeBlock == nTimeTx);
}

// entropy bit for stake modifier if chosen by modifier
unsigned int GetStakeEntropyBit(const CBlock& block)
{
    unsigned int nEntropyBit = ((UintToArith256(block.GetHash()).GetLow64()) & 1llu);
    if (gArgs.GetBoolArg("-printstakemodifier", false))
        LogPrintf("GetStakeEntropyBit: hashBlock=%s nEntropyBit=%u\n", block.GetHash().ToString().c_str(), nEntropyBit);

    return nEntropyBit;
}


/*
 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.
*/

/*! \file models/crossassetmodel.hpp
    \brief cross asset model
    \ingroup crossassetmodel
*/

#ifndef quantext_crossasset_model_hpp
#define quantext_crossasset_model_hpp

#include <qle/models/crlgm1fparametrization.hpp>
#include <qle/models/eqbsparametrization.hpp>
#include <qle/models/fxbsparametrization.hpp>
#include <qle/models/infdkparametrization.hpp>
#include <qle/models/lgm.hpp>

#include <qle/processes/crossassetstateprocess.hpp>

#include <ql/math/distributions/normaldistribution.hpp>
#include <ql/math/integrals/integral.hpp>
#include <ql/math/matrix.hpp>
#include <ql/models/model.hpp>

#include <boost/bind.hpp>

namespace QuantExt {
using namespace QuantLib;

namespace CrossAssetModelTypes {
//! Cross Asset Type
//! \ingroup crossassetmodel
enum AssetType { IR, FX, INF, CR, EQ };
} // namespace CrossAssetModelTypes

using namespace CrossAssetModelTypes;

//! Cross Asset Model
/*! \ingroup crossassetmodel
 */
class CrossAssetModel : public LinkableCalibratedModel {
public:
    /*! Parametrizations must be given in the following order
        - IR  (first parametrization defines the domestic currency)
        - FX  (for all pairs domestic-ccy defined by the IR models)
        - INF (optionally, ccy must be a subset of the IR ccys)
        - CR  (optionally, ccy must be a subset of the IR ccys)
        - EQ  (for all names equity currency defined in Parametrization)
        If the correlation matrix is not given, it is initialized
        as the unit matrix (and can be customized after
        construction of the model).
    */
    CrossAssetModel(const std::vector<boost::shared_ptr<Parametrization> >& parametrizations,
                    const Matrix& correlation = Matrix(),
                    SalvagingAlgorithm::Type salvaging = SalvagingAlgorithm::None);

    /*! IR-FX model based constructor */
    CrossAssetModel(const std::vector<boost::shared_ptr<LinearGaussMarkovModel> >& currencyModels,
                    const std::vector<boost::shared_ptr<FxBsParametrization> >& fxParametrizations,
                    const Matrix& correlation = Matrix(),
                    SalvagingAlgorithm::Type salvaging = SalvagingAlgorithm::None);

    /*! returns the state process with a given discretization */
    const boost::shared_ptr<StochasticProcess>
    stateProcess(CrossAssetStateProcess::discretization disc = CrossAssetStateProcess::exact) const;

    /*! total dimension of model (sum of number of state variables) */
    Size dimension() const;

    /*! total number of Brownian motions (this is less or equal to dimension) */
    Size brownians() const;

    /*! total number of parameters that can be calibrated */
    Size totalNumberOfParameters() const;

    /*! number of components for an asset class */
    Size components(const AssetType t) const;

    /*! number of brownian motions for a component */
    Size brownians(const AssetType t, const Size i) const;

    /*! number of state variables for a component */
    Size stateVariables(const AssetType t, const Size i) const;

    /*! return index for currency (0 = domestic, 1 = first
      foreign currency and so on) */
    Size ccyIndex(const Currency& ccy) const;

    /*! return index for equity (0 = first equity) */
    Size eqIndex(const std::string& eqName) const;

    /*! return index for inflation (0 = first inflation index) */
    Size infIndex(const std::string& index) const;

    /*! observer and linked calibrated model interface */
    void update();
    void generateArguments();

    /*! LGM1F components, ccy=0 refers to the domestic currency */
    const boost::shared_ptr<LinearGaussMarkovModel> lgm(const Size ccy) const;

    const boost::shared_ptr<IrLgm1fParametrization> irlgm1f(const Size ccy) const;

    Real numeraire(const Size ccy, const Time t, const Real x,
                   Handle<YieldTermStructure> discountCurve = Handle<YieldTermStructure>()) const;

    Real discountBond(const Size ccy, const Time t, const Time T, const Real x,
                      Handle<YieldTermStructure> discountCurve = Handle<YieldTermStructure>()) const;

    Real reducedDiscountBond(const Size ccy, const Time t, const Time T, const Real x,
                             Handle<YieldTermStructure> discountCurve = Handle<YieldTermStructure>()) const;

    Real discountBondOption(const Size ccy, Option::Type type, const Real K, const Time t, const Time S, const Time T,
                            Handle<YieldTermStructure> discountCurve = Handle<YieldTermStructure>()) const;

    /*! FXBS components, ccy=0 referes to the first foreign currency,
        so it corresponds to ccy+1 if you want to get the corresponding
        irmgl1f component */
    const boost::shared_ptr<FxBsParametrization> fxbs(const Size ccy) const;

    /*! INF DK components */
    const boost::shared_ptr<InfDkParametrization> infdk(const Size i) const;

    /*! CR LGM 1F components */
    const boost::shared_ptr<CrLgm1fParametrization> crlgm1f(const Size i) const;

    /*! EQBS components */
    const boost::shared_ptr<EqBsParametrization> eqbs(const Size ccy) const;

    /* ... add more components here ...*/

    /*! correlation linking the different marginal models, note that
        the use of asset class pairs specific inspectors is
        recommended instead of the global matrix directly */
    const Matrix& correlation() const;

    /*! check if correlation matrix is valid */
    void checkCorrelationMatrix() const;

    /*! index of component in the parametrization vector */
    Size idx(const AssetType t, const Size i) const;

    /*! index of component in the correlation matrix, by offset */
    Size cIdx(const AssetType t, const Size i, const Size offset = 0) const;

    /*! index of component in the stochastic process array, by offset */
    Size pIdx(const AssetType t, const Size i, const Size offset = 0) const;

    /*! correlation between two components */
    const Real& correlation(const AssetType s, const Size i, const AssetType t, const Size j, const Size iOffset = 0,
                            const Size jOffset = 0) const;
    /*! set correlation */
    void correlation(const AssetType s, const Size i, const AssetType t, const Size j, const Real value,
                     const Size iOffset = 0, const Size jOffset = 0);

    /*! analytical moments require numerical integration,
      which can be customized here */
    void setIntegrationPolicy(const boost::shared_ptr<Integrator> integrator,
                              const bool usePiecewiseIntegration = true) const;
    const boost::shared_ptr<Integrator> integrator() const;

    /*! return (V(t), V^tilde(t,T)) in the notation of the book */
    std::pair<Real, Real> infdkV(const Size i, const Time t, const Time T);

    /*! return (I(t), I^tilde(t,T)) in the notation of the book, note that
        I(0) is normalized to 1 here, i.e. you have to multiply the result
        with the index value (as of the base date of the inflation ts) */
    std::pair<Real, Real> infdkI(const Size i, const Time t, const Time T, const Real z, const Real y);

    /*! return YoYIIS(t) in the notation of the book, the year on year
        swaplet price from S to T, at time t */
    Real infdkYY(const Size i, const Time t, const Time S, const Time T, const Real z, const Real y, const Real irz);

    /*! returns (S(t), S^tilde(t,T)) in the notation of the book */
    std::pair<Real, Real> crlgm1fS(const Size i, const Size ccy, const Time t, const Time T, const Real z,
                                   const Real y) const;

    /*! calibration procedures */

    /*! calibrate irlgm1f volatilities to a sequence of ir options with
        expiry times equal to step times in the parametrization */
    void calibrateIrLgm1fVolatilitiesIterative(const Size ccy,
                                               const std::vector<boost::shared_ptr<BlackCalibrationHelper> >& helpers,
                                               OptimizationMethod& method, const EndCriteria& endCriteria,
                                               const Constraint& constraint = Constraint(),
                                               const std::vector<Real>& weights = std::vector<Real>());

    /*! calibrate irlgm1f reversion to a sequence of ir options with
        maturities equal to step times in the parametrization */
    void calibrateIrLgm1fReversionsIterative(const Size ccy,
                                             const std::vector<boost::shared_ptr<BlackCalibrationHelper> >& helpers,
                                             OptimizationMethod& method, const EndCriteria& endCriteria,
                                             const Constraint& constraint = Constraint(),
                                             const std::vector<Real>& weights = std::vector<Real>());

    /*! calibrate irlgm1f parameters for one ccy globally to a set
        of ir options */
    void calibrateIrLgm1fGlobal(const Size ccy, const std::vector<boost::shared_ptr<BlackCalibrationHelper> >& helpers,
                                OptimizationMethod& method, const EndCriteria& endCriteria,
                                const Constraint& constraint = Constraint(),
                                const std::vector<Real>& weights = std::vector<Real>());

    /*! calibrate eq or fx volatilities to a sequence of options with
            expiry times equal to step times in the parametrization */
    void calibrateBsVolatilitiesIterative(const AssetType& assetType, const Size aIdx,
                                          const std::vector<boost::shared_ptr<BlackCalibrationHelper> >& helpers,
                                          OptimizationMethod& method, const EndCriteria& endCriteria,
                                          const Constraint& constraint = Constraint(),
                                          const std::vector<Real>& weights = std::vector<Real>());

    /*! calibrate eq/fx volatilities globally to a set of fx options */
    void calibrateBsVolatilitiesGlobal(const AssetType& assetType, const Size aIdx,
                                       const std::vector<boost::shared_ptr<BlackCalibrationHelper> >& helpers,
                                       OptimizationMethod& method, const EndCriteria& endCriteria,
                                       const Constraint& constraint = Constraint(),
                                       const std::vector<Real>& weights = std::vector<Real>());

    /*! calibrate infdk volatilities to a sequence of cpi options with
        expiry times equal to step times in the parametrization */
    void calibrateInfDkVolatilitiesIterative(const Size index,
                                             const std::vector<boost::shared_ptr<BlackCalibrationHelper> >& helpers,
                                             OptimizationMethod& method, const EndCriteria& endCriteria,
                                             const Constraint& constraint = Constraint(),
                                             const std::vector<Real>& weights = std::vector<Real>());

    /*! calibrate infdk reversions to a sequence of cpi options with
        maturity times equal to step times in the parametrization */
    void calibrateInfDkReversionsIterative(const Size index,
                                           const std::vector<boost::shared_ptr<BlackCalibrationHelper> >& helpers,
                                           OptimizationMethod& method, const EndCriteria& endCriteria,
                                           const Constraint& constraint = Constraint(),
                                           const std::vector<Real>& weights = std::vector<Real>());

    /*! calibrate infdk volatilities globally to a sequence of cpi cap/floors */
    void calibrateInfDkVolatilitiesGlobal(const Size index,
                                          const std::vector<boost::shared_ptr<BlackCalibrationHelper> >& helpers,
                                          OptimizationMethod& method, const EndCriteria& endCriteria,
                                          const Constraint& constraint = Constraint(),
                                          const std::vector<Real>& weights = std::vector<Real>());

    /*! calibrate infdk reversions globally to a sequence of cpi cap/floors */
    void calibrateInfDkReversionsGlobal(const Size index,
                                        const std::vector<boost::shared_ptr<BlackCalibrationHelper> >& helpers,
                                        OptimizationMethod& method, const EndCriteria& endCriteria,
                                        const Constraint& constraint = Constraint(),
                                        const std::vector<Real>& weights = std::vector<Real>());

    /*! calibrate crlgm1f volatilities to a sequence of cds options with
        expiry times equal to step times in the parametrization */
    void calibrateCrLgm1fVolatilitiesIterative(const Size index,
                                               const std::vector<boost::shared_ptr<BlackCalibrationHelper> >& helpers,
                                               OptimizationMethod& method, const EndCriteria& endCriteria,
                                               const Constraint& constraint = Constraint(),
                                               const std::vector<Real>& weights = std::vector<Real>());

    /*! calibrate crlgm1f reversions to a sequence of cds options with
        maturity times equal to step times in the parametrization */
    void calibrateCrLgm1fReversionsIterative(const Size index,
                                             const std::vector<boost::shared_ptr<BlackCalibrationHelper> >& helpers,
                                             OptimizationMethod& method, const EndCriteria& endCriteria,
                                             const Constraint& constraint = Constraint(),
                                             const std::vector<Real>& weights = std::vector<Real>());

    /* ... add more calibration procedures here ... */

protected:
    /* ctor to be used in extensions, initialize is not called */
    CrossAssetModel(const std::vector<boost::shared_ptr<Parametrization> >& parametrizations, const Matrix& correlation,
                    SalvagingAlgorithm::Type salvaging, const bool)
        : LinkableCalibratedModel(), p_(parametrizations), rho_(correlation), salvaging_(salvaging) {}

    /*! number of arguments for a component */
    Size arguments(const AssetType t, const Size i) const;

    /*! index of component in the arguments vector, by offset */
    Size aIdx(const AssetType t, const Size i, const Size offset = 0) const;

    /* init methods */
    virtual void initialize();
    virtual void initializeParametrizations();
    virtual void initializeCorrelation();
    virtual void initializeArguments();
    virtual void finalizeArguments();
    virtual void checkModelConsistency() const;
    virtual void initDefaultIntegrator();
    virtual void initStateProcess();

    /* helper function for infdkI, crlgm1fS */
    Real infV(const Size idx, const Size ccy, const Time t, const Time T) const;
    Real crV(const Size idx, const Size ccy, const Time t, const Time T) const;

    // cache for infdkI, crlgm1fS method
    struct cache_key {
        Size i, ccy;
        double t, T;
        bool operator==(const cache_key& o) const { return (i == o.i) && (ccy == o.ccy) && (t == o.t) && (T == o.T); }
    };

    struct cache_hasher : std::unary_function<cache_key, std::size_t> {
        std::size_t operator()(cache_key const& x) const {
            std::size_t seed = 0;
            boost::hash_combine(seed, x.i);
            boost::hash_combine(seed, x.ccy);
            boost::hash_combine(seed, x.t);
            boost::hash_combine(seed, x.T);
            return seed;
        }
    };

    mutable boost::unordered_map<cache_key, std::pair<Real, Real>, cache_hasher> cache_crlgm1fS_, cache_infdkI_;

    /* members */

    Size nIrLgm1f_, nFxBs_, nInfDk_, nCrLgm1f_, nEqBs_;
    Size totalNumberOfParameters_;
    std::vector<boost::shared_ptr<Parametrization> > p_;
    std::vector<boost::shared_ptr<LinearGaussMarkovModel> > lgm_;
    Matrix rho_;
    SalvagingAlgorithm::Type salvaging_;
    mutable boost::shared_ptr<Integrator> integrator_;
    boost::shared_ptr<CrossAssetStateProcess> stateProcessExact_, stateProcessEuler_;

    /* calibration constraints */

    // move parameter param (e.g. vol, reversion) of asset type component t / index
    // at step i (or at all steps if i is null)
    Disposable<std::vector<bool> > MoveParameter(const AssetType t, const Size param, const Size index, const Size i) {
        switch (t) {
        case IR:
            QL_REQUIRE(param <= 1, "irlgm1f parameter " << param << " out of bounds 0...1");
            QL_REQUIRE(i < irlgm1f(index)->parameter(param)->size(),
                       "irlgm1f parameter (" << param << ") index (" << i << ") for ccy " << index
                                             << " out of bounds 0..." << irlgm1f(index)->parameter(param)->size() - 1);
            break;
        case FX:
            QL_REQUIRE(param == 0, "fxbs parameter " << param << " out of bounds 0...0");
            QL_REQUIRE(i < fxbs(index)->parameter(param)->size(),
                       "fxbs volatility index (" << i << ") for ccy " << index << " out of bounds 0..."
                                                 << fxbs(index)->parameter(param)->size() - 1);
            break;
        case INF:
            QL_REQUIRE(param <= 1, "infdk parameter " << param << " out of bounds 0...1");
            QL_REQUIRE(i < infdk(index)->parameter(param)->size(),
                       "infdk parameter (" << param << ") index (" << i << ") for inflation component " << index
                                           << " out of bounds 0..." << infdk(index)->parameter(param)->size() - 1);
            break;
        case CR:
            QL_REQUIRE(param <= 1, "crlgm1f parameter " << param << " out of bounds 0...1");
            QL_REQUIRE(i < crlgm1f(index)->parameter(param)->size(),
                       "crlgm1f parameter (" << param << ") index (" << i << ") for credit component " << index
                                             << " out of bounds 0..." << crlgm1f(index)->parameter(param)->size() - 1);
            break;
        case EQ:
            QL_REQUIRE(param == 0, "eqbs parameter " << param << " out of bounds 0...0");
            QL_REQUIRE(i < eqbs(index)->parameter(param)->size(),
                       "eqbs volatility index (" << i << ") for index " << index << " out of bounds 0..."
                                                 << eqbs(index)->parameter(param)->size() - 1);
            break;
        default:
            QL_FAIL("asset type not recognised");
        }
        std::vector<bool> res(0);
        for (Size j = 0; j < nIrLgm1f_; ++j) {
            std::vector<bool> tmp1(p_[idx(IR, j)]->parameter(0)->size(), true);
            std::vector<bool> tmp2(p_[idx(IR, j)]->parameter(1)->size(), true);
            std::vector<std::vector<bool> > tmp;
            tmp.push_back(tmp1);
            tmp.push_back(tmp2);
            if (t == IR && index == j) {
                for (Size ii = 0; ii < tmp[param].size(); ++ii) {
                    if (i == Null<Size>() || i == ii) {
                        tmp[param][ii] = false;
                    }
                }
            }
            res.insert(res.end(), tmp[0].begin(), tmp[0].end());
            res.insert(res.end(), tmp[1].begin(), tmp[1].end());
        }
        for (Size j = 0; j < nFxBs_; ++j) {
            std::vector<bool> tmp(p_[idx(FX, j)]->parameter(0)->size(), true);
            if (t == FX && index == j) {
                for (Size ii = 0; ii < tmp.size(); ++ii) {
                    if (i == Null<Size>() || i == ii) {
                        tmp[i] = false;
                    }
                }
            }
            res.insert(res.end(), tmp.begin(), tmp.end());
        }
        for (Size j = 0; j < nInfDk_; ++j) {
            std::vector<bool> tmp1(p_[idx(INF, j)]->parameter(0)->size(), true);
            std::vector<bool> tmp2(p_[idx(INF, j)]->parameter(1)->size(), true);
            std::vector<std::vector<bool> > tmp;
            tmp.push_back(tmp1);
            tmp.push_back(tmp2);
            if (t == INF && index == j) {
                for (Size ii = 0; ii < tmp[param].size(); ++ii) {
                    if (i == Null<Size>() || i == ii) {
                        tmp[param][ii] = false;
                    }
                }
            }
            res.insert(res.end(), tmp[0].begin(), tmp[0].end());
            res.insert(res.end(), tmp[1].begin(), tmp[1].end());
        }
        for (Size j = 0; j < nCrLgm1f_; ++j) {
            std::vector<bool> tmp1(p_[idx(CR, j)]->parameter(0)->size(), true);
            std::vector<bool> tmp2(p_[idx(CR, j)]->parameter(1)->size(), true);
            std::vector<std::vector<bool> > tmp;
            tmp.push_back(tmp1);
            tmp.push_back(tmp2);
            if (t == CR && index == j) {
                for (Size ii = 0; ii < tmp[param].size(); ++ii) {
                    if (i == Null<Size>() || i == ii) {
                        tmp[param][ii] = false;
                    }
                }
            }
            res.insert(res.end(), tmp[0].begin(), tmp[0].end());
            res.insert(res.end(), tmp[1].begin(), tmp[1].end());
        }
        for (Size j = 0; j < nEqBs_; ++j) {
            std::vector<bool> tmp(p_[idx(EQ, j)]->parameter(0)->size(), true);
            if (t == EQ && index == j) {
                for (Size ii = 0; ii < tmp.size(); ++ii) {
                    if (i == Null<Size>() || i == ii) {
                        tmp[i] = false;
                    }
                }
            }
            res.insert(res.end(), tmp.begin(), tmp.end());
        }
        return res;
    }
};

// inline

inline const boost::shared_ptr<StochasticProcess>
CrossAssetModel::stateProcess(CrossAssetStateProcess::discretization disc) const {
    return disc == CrossAssetStateProcess::exact ? stateProcessExact_ : stateProcessEuler_;
}

inline Size CrossAssetModel::dimension() const {
    // this assumes specific models, as soon as other model types are added
    // this formula has to be generalized as well
    return nIrLgm1f_ * 1 + nFxBs_ * 1 + nInfDk_ * 2 + nCrLgm1f_ * 2 + nEqBs_ * 1;
}

inline Size CrossAssetModel::brownians() const {
    // this assumes specific models, as soon as other model types are added
    // this formula has to be generalized as well
    return nIrLgm1f_ * 1 + nFxBs_ * 1 + nInfDk_ * 1 + nCrLgm1f_ * 1 + nEqBs_ * 1;
}

inline Size CrossAssetModel::totalNumberOfParameters() const { return totalNumberOfParameters_; }

inline const boost::shared_ptr<LinearGaussMarkovModel> CrossAssetModel::lgm(const Size ccy) const {
    return lgm_[idx(IR, ccy)];
}

inline const boost::shared_ptr<IrLgm1fParametrization> CrossAssetModel::irlgm1f(const Size ccy) const {
    return lgm(ccy)->parametrization();
}

inline const boost::shared_ptr<InfDkParametrization> CrossAssetModel::infdk(const Size i) const {
    return boost::static_pointer_cast<InfDkParametrization>(p_[idx(INF, i)]);
}

inline const boost::shared_ptr<CrLgm1fParametrization> CrossAssetModel::crlgm1f(const Size i) const {
    return boost::static_pointer_cast<CrLgm1fParametrization>(p_[idx(CR, i)]);
}

inline const boost::shared_ptr<EqBsParametrization> CrossAssetModel::eqbs(const Size name) const {
    return boost::dynamic_pointer_cast<EqBsParametrization>(p_[idx(EQ, name)]);
}

inline Real CrossAssetModel::numeraire(const Size ccy, const Time t, const Real x,
                                       Handle<YieldTermStructure> discountCurve) const {
    return lgm(ccy)->numeraire(t, x, discountCurve);
}

inline Real CrossAssetModel::discountBond(const Size ccy, const Time t, const Time T, const Real x,
                                          Handle<YieldTermStructure> discountCurve) const {
    return lgm(ccy)->discountBond(t, T, x, discountCurve);
}

inline Real CrossAssetModel::reducedDiscountBond(const Size ccy, const Time t, const Time T, const Real x,
                                                 Handle<YieldTermStructure> discountCurve) const {
    return lgm(ccy)->reducedDiscountBond(t, T, x, discountCurve);
}

inline Real CrossAssetModel::discountBondOption(const Size ccy, Option::Type type, const Real K, const Time t,
                                                const Time S, const Time T,
                                                Handle<YieldTermStructure> discountCurve) const {
    return lgm(ccy)->discountBondOption(type, K, t, S, T, discountCurve);
}

inline const boost::shared_ptr<FxBsParametrization> CrossAssetModel::fxbs(const Size ccy) const {
    return boost::dynamic_pointer_cast<FxBsParametrization>(p_[idx(FX, ccy)]);
}

inline const Matrix& CrossAssetModel::correlation() const { return rho_; }

inline const boost::shared_ptr<Integrator> CrossAssetModel::integrator() const { return integrator_; }

} // namespace QuantExt

#endif


/**
1. Trigger when receive new map/tf (message filter new map and lidar scan)
2. use icp to match scan and merge into a new topic
**/
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <vector>
#include <string>
#include <math.h>
#include <signal.h>
#include <Eigen/Geometry>

#include <ros/ros.h>
#include <sensor_msgs/PointCloud2.h>
#include <nav_msgs/OccupancyGrid.h>
#include <message_filters/subscriber.h>
#include <message_filters/synchronizer.h>
#include <message_filters/sync_policies/approximate_time.h>

#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl_ros/point_cloud.h>
#include <pcl_ros/transforms.h>
#include <pcl/io/ply_io.h>
#include <pcl/io/io.h>
#include <pcl/io/obj_io.h>
#include <pcl/io/vtk_lib_io.h>
#include <pcl/PolygonMesh.h>
#include <pcl/common/centroid.h>
#include <pcl/common/common.h>
#include <pcl/features/normal_3d.h>
#include <pcl/filters/filter.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include <pcl/filters/radius_outlier_removal.h>
#include <pcl/filters/conditional_removal.h>
#include <pcl/point_cloud.h>
#include <pcl_conversions/pcl_conversions.h>
#include <pcl/segmentation/extract_clusters.h>
#include <pcl/kdtree/kdtree.h>
#include <pcl/registration/icp.h>

#include <tf/transform_broadcaster.h>
#include <tf/transform_datatypes.h>
#include <tf/transform_listener.h>
#include <tf/tf.h>

using namespace pcl;

typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::PointCloud2, nav_msgs::OccupancyGrid> MySyncPolicy;
typedef message_filters::Synchronizer<MySyncPolicy> Sync;

class IcpMapping{
    public:
        IcpMapping();
        void pcCallback(const sensor_msgs::PointCloud2ConstPtr &pc, const nav_msgs::OccupancyGrid::ConstPtr &dummy);
        Eigen::Matrix4f get_transfrom(std::string link_name);

    private:
        ros::Publisher icp_pub;

        boost::shared_ptr<Sync> sync_;
        message_filters::Subscriber<sensor_msgs::PointCloud2> pc_sub;
        message_filters::Subscriber<nav_msgs::OccupancyGrid> grid_sub;

        ros::NodeHandle nh;
        ros::Subscriber sub;

        Eigen::Matrix4f map_trans;
        pcl::PointCloud<PointXYZ>::Ptr icp_map;
        pcl::PointCloud<PointXYZ>::Ptr pc_input;

        sensor_msgs::PointCloud2 ros_cloud_msg;

	    tf::TransformListener listener;
};

IcpMapping::IcpMapping(){
    icp_map.reset(new PointCloud<PointXYZ>());
    ros::Duration(1).sleep();
    icp_pub = nh.advertise<sensor_msgs::PointCloud2> ("/icp_mapping", 1);
    pc_input.reset(new PointCloud<PointXYZ>());

    map_trans = get_transfrom("velodyne1");

    pc_sub.subscribe(nh, "/husky1/velodyne1/velodyne_points", 1);
    grid_sub.subscribe(nh, "/map", 1);

    sync_.reset(new Sync(MySyncPolicy(10), pc_sub, grid_sub));
    sync_->registerCallback(boost::bind(&IcpMapping::pcCallback, this, _1, _2));
}

Eigen::Matrix4f IcpMapping::get_transfrom(std::string link_name){
	tf::StampedTransform transform;
	Eigen::Matrix4f trans;

	try{
		ros::Duration five_seconds(5.0);
		listener.waitForTransform("slam_map", link_name, ros::Time(0), five_seconds);
		listener.lookupTransform("slam_map", link_name, ros::Time(0), transform);
	}
	catch (tf::TransformException ex){
		ROS_ERROR("%s",ex.what());
		return trans;
	}
	Eigen::Quaternionf q(transform.getRotation().getW(), \
		transform.getRotation().getX(), transform.getRotation().getY(), transform.getRotation().getZ());
	Eigen::Matrix3f mat = q.toRotationMatrix();
	trans << mat(0,0), mat(0,1), mat(0,2), transform.getOrigin().getX(),
			mat(1,0), mat(1,1), mat(1,2), transform.getOrigin().getY(),
			mat(2,0), mat(2,1), mat(2,2), transform.getOrigin().getZ(),
			0, 0, 0, 1;
	return trans;
}

void IcpMapping::pcCallback(const sensor_msgs::PointCloud2ConstPtr &pc, const nav_msgs::OccupancyGrid::ConstPtr &dummy){
	ROS_INFO("mapping");
	fromROSMsg(*pc, *pc_input);
    map_trans = get_transfrom("velodyne1");
	pcl::transformPointCloud (*pc_input, *pc_input, map_trans);
    ROS_INFO("transformed");

    if (icp_map->empty()){
        ROS_INFO("Empty cloud");
        *icp_map += *pc_input;
    }
    else{
        ROS_INFO("Aligning");
        IterativeClosestPoint<PointXYZ, PointXYZ> icp;
        icp.setInputSource(pc_input);
        icp.setInputTarget(icp_map);

        icp.align(*pc_input);
        *icp_map += *pc_input;
        ROS_INFO("Score: %lf", icp.getFitnessScore());
    }

	toROSMsg(*icp_map, ros_cloud_msg);
	ros_cloud_msg.header.frame_id = "slam_map";
    ROS_INFO("Publishing");

	icp_pub.publish(ros_cloud_msg);
}

int main(int argc, char** argv)
{
    ros::init(argc, argv, "icp_mapping");

    IcpMapping IM;

    ROS_INFO("\033[1;32m---->\033[0m Icp Mapping Started.");

    ros::spin();
    return 0;
}


// ---------------------------------------------------------------------------
//             Marginal Effects Multi-Logistic
// ---------------------------------------------------------------------------
/**
 * @brief State for marginal effects calculation for logistic regression
 *
 * TransitionState encapsualtes the transition state during the
 * marginal effects calculation for the logistic-regression aggregate function.
 * To the database, the state is exposed as a single DOUBLE PRECISION array,
 * to the C++ code it is a proper object containing scalars and vectors.
 *
 * Note: We assume that the DOUBLE PRECISION array is initialized by the
 * database with length at least 5, and all elemenets are 0.
 *
 */

#include <dbconnector/dbconnector.hpp>

#include <modules/shared/HandleTraits.hpp>
#include <modules/prob/boost.hpp>
#include <boost/math/distributions.hpp>
#include <modules/prob/student.hpp>
#include "mlogr_margins.hpp"

#include <vector>
#include <fstream>

namespace madlib {

// Use Eigen
using namespace dbal::eigen_integration;

namespace modules {

// Import names from other MADlib modules
using dbal::NoSolutionFoundException;

namespace regress {

template <class Handle>
class mlogregrMarginalTransitionState {

    // By §14.5.3/9: "Friend declarations shall not declare partial
    // specializations." We do access protected members in operator+=().
    template <class OtherHandle>
    friend class mlogregrMarginalTransitionState;

  public:
    mlogregrMarginalTransitionState(const AnyType &inArray)
        : mStorage(inArray.getAs<Handle>()) {
        rebind(static_cast<uint16_t>(mStorage[0]),static_cast<uint16_t>(mStorage[1]));
    }

    /**
     * @brief Convert to backend representation
     *
     * We define this function so that we can use State in the
     * argument list and as a return type.
     */
    inline operator AnyType() const {
        return mStorage;
    }

    /**
     * @brief Initialize the iteratively-reweighted-least-squares state.
     *
     * This function is only called for the first iteration, for the first row.
     */
    inline void initialize(const Allocator &inAllocator,
                           uint16_t inWidthOfX, uint16_t inNumCategories, uint16_t inRefCategory) {
        mStorage = inAllocator.allocateArray<double, dbal::AggregateContext,
                                             dbal::DoZero, dbal::ThrowBadAlloc>(arraySize(inWidthOfX, inNumCategories));
        rebind(inWidthOfX, inNumCategories);
        widthOfX = inWidthOfX;
        numCategories = inNumCategories;
        ref_category = inRefCategory;
    }

    /**
     * @brief We need to support assigning the previous state
     */
    template <class OtherHandle> mlogregrMarginalTransitionState &operator=(
        const mlogregrMarginalTransitionState<OtherHandle> &inOtherState) {
        for (size_t i = 0; i < mStorage.size(); i++)
            mStorage[i] = inOtherState.mStorage[i];
        return *this;
    }

    /**
     * @brief Merge with another State object by copying the intra-iteration
     *     fields
     */
    template <class OtherHandle> mlogregrMarginalTransitionState &operator+=(
        const mlogregrMarginalTransitionState<OtherHandle> &inOtherState) {
        if (mStorage.size() != inOtherState.mStorage.size() ||
            widthOfX != inOtherState.widthOfX)
            throw std::logic_error("Internal error: Incompatible transition "
                                   "states");

        numRows += inOtherState.numRows;
        margins_matrix += inOtherState.margins_matrix;
        X_bar += inOtherState.X_bar;
        X_transp_AX += inOtherState.X_transp_AX;
        reference_margins+= inOtherState.reference_margins;
        delta += inOtherState.delta;
        return *this;
    }

    /**
     * @brief Reset the inter-iteration fields.
     */
    inline void reset() {
        numRows = 0;
        margins_matrix.fill(0);
        X_bar.fill(0);
        X_transp_AX.fill(0);
        reference_margins.fill(0);
        delta.fill(0);
    }

  private:
    static inline uint32_t arraySize(const uint16_t inWidthOfX,
                                     const uint16_t inNumCategories) {
        return 4 + 2*inWidthOfX * inNumCategories + 2*inWidthOfX +
            2 * inNumCategories * inWidthOfX * inWidthOfX * inNumCategories;
    }

    /**
     * @brief Rebind to a new storage array
     *
     * @param inWidthOfX             The number of independent variables.
     * @param inNumCategories The number of categories of the dependant var

     * Array layout (iteration refers to one aggregate-function call):
     * Inter-iteration components (updated in final function):
     * - 0: widthOfX (number of independant variables)
     * - 1: numCategories (number of categories)
     * - 2: ref_category
     * - 3: coef (vector of coefficients)
     *
     * Intra-iteration components (updated in transition step):
     * - 3 + widthOfX*numCategories: numRows (number of rows already processed in this iteration)
     * - 4 + widthOfX * inNumCategories: margins_matrix
     */
    void rebind(uint16_t inWidthOfX = 0, uint16_t inNumCategories = 0) {
        widthOfX.rebind(&mStorage[0]);
        numCategories.rebind(&mStorage[1]);
        ref_category.rebind(&mStorage[2]);
        coef.rebind(&mStorage[3], inWidthOfX * inNumCategories);
        numRows.rebind(&mStorage[3 + inWidthOfX * inNumCategories]);
        margins_matrix.rebind(&mStorage[4 + inWidthOfX * inNumCategories],
                              inNumCategories , inWidthOfX );
        X_bar.rebind(&mStorage[4 + 2*inWidthOfX * inNumCategories], inWidthOfX);
        reference_margins.rebind(&mStorage[4 + inWidthOfX + 2*inWidthOfX*inNumCategories],
                                 inWidthOfX);
        X_transp_AX.rebind(&mStorage[4 + 2*inWidthOfX * inNumCategories + 2*inWidthOfX],
                           inNumCategories*inWidthOfX, inWidthOfX*inNumCategories);
        delta.rebind(&mStorage[4 + 2*inWidthOfX * inNumCategories + 2*inWidthOfX +
                               inNumCategories * inWidthOfX * inWidthOfX * inNumCategories],
                     inNumCategories*inWidthOfX, inWidthOfX*inNumCategories);
    }

    Handle mStorage;

  public:
    typename HandleTraits<Handle>::ReferenceToUInt16 widthOfX;
    typename HandleTraits<Handle>::ReferenceToUInt16 numCategories;

    typename HandleTraits<Handle>::ColumnVectorTransparentHandleMap coef;
    typename HandleTraits<Handle>::ReferenceToUInt64 numRows;
    typename HandleTraits<Handle>::MatrixTransparentHandleMap margins_matrix;
    typename HandleTraits<Handle>::MatrixTransparentHandleMap X_transp_AX;
    typename HandleTraits<Handle>::ColumnVectorTransparentHandleMap reference_margins;
    typename HandleTraits<Handle>::ReferenceToUInt16 ref_category;
    typename HandleTraits<Handle>::ColumnVectorTransparentHandleMap X_bar;
    typename HandleTraits<Handle>::MatrixTransparentHandleMap delta;
};

// ----------------------------------------------------------------------

AnyType
mlogregr_marginal_step_transition::run(AnyType &args) {
	mlogregrMarginalTransitionState<MutableArrayHandle<double> > state = args[0];

    if (args[1].isNull() || args[2].isNull() || args[3].isNull() ||
        args[4].isNull() || args[5].isNull()) {
        return args[0];
    }

    // Get x as a vector of double
    MappedColumnVector x;
    try{
        // an exception is raised in the backend if args[2] contains nulls
        MappedColumnVector xx = args[4].getAs<MappedColumnVector>();
        // x is a const reference, we can only rebind to change its pointer
        x.rebind(xx.memoryHandle(), xx.size());
    } catch (const ArrayWithNullException &e) {
        return args[0];
    }

    // Get the category & numCategories as integer
    int16_t category = static_cast<int16_t>(args[1].getAs<int>());
    // Number of categories after pivoting (We pivot around ref_category)
    int16_t numCategories = static_cast<int16_t>(args[2].getAs<int>() - 1);
    int32_t ref_category = args[3].getAs<int32_t>();
	MappedMatrix coefMat = args[5].getAs<MappedMatrix>();

    // The following check was added with MADLIB-138.
    if (!x.is_finite())
        throw std::domain_error("Design matrix is not finite.");

    if (state.numRows == 0) {
        if (x.size() > std::numeric_limits<uint16_t>::max())
            throw std::domain_error("Number of independent variables cannot be "
                                    "larger than 65535.");
        if (numCategories < 1)
            throw std::domain_error("Number of cateogires must be at least 2");
        if (category > numCategories)
            throw std::domain_error("You have entered a category > numCategories"
                                    "Categories must be of values {0,1... numCategories-1}");

        // Init the state (requires x.size() and category.size())
        state.initialize(*this,
                         static_cast<uint16_t>(x.size()) ,
                         static_cast<uint16_t>(numCategories),
                         static_cast<uint16_t>(ref_category));

        Matrix mat = coefMat;
        mat.transposeInPlace();
        mat.resize(coefMat.size(), 1);
        state.coef = mat;
    }

    // Now do the transition step
    state.numRows++;
    /*
      Get y: Convert to 1/0 boolean vector
      Example: Category 4 : 0 0 0 1 0 0
      Storing it in this forms helps us get a nice closed form expression
    */

    //To pivot around the specified reference category
    ColumnVector y(numCategories);
    y.fill(0);
    if (category > ref_category) {
        y(category - 1) = 1;
    } else if (category < ref_category) {
        y(category) = 1;
    }

    //    Marginal Effect calculations
    // ----------------------------------------------------------------------
    Matrix coef = state.coef;
    coef.resize(numCategories, state.widthOfX);

    // prob is vector of size # categories
    /*
      Note: The above 2 lines could have been written as:
      ColumnVector prob = -coef*x; but this creates warnings.
      See multilog for details.
    */
    ColumnVector prob(numCategories);
    prob = coef*x;

    // Calculate the odds ratio
    prob = prob.array().exp();
    double prob_sum = prob.sum();
    prob = prob / (1 + prob_sum);

    Matrix probDiag = prob.asDiagonal();
    Matrix a(numCategories,numCategories);
    a = prob * prob.transpose() - probDiag;

    // Start the Hessian calculations
    Matrix X_transp_AX(numCategories * state.widthOfX, numCategories * state.widthOfX);

    /*
      Again: The following 3 lines could have been written as
      Matrix XXTrans = x * x.transpose();
      but it creates warnings related to the type of x. Here is an easy fix
    */
    Matrix cv_x = x;
    Matrix XXTrans = trans(cv_x);
    XXTrans = cv_x * XXTrans;

    //Eigen doesn't supported outer-products for matrices, so we have to do our own.
    //This operation is also known as a tensor-product.
    for (int i1 = 0; i1 < state.widthOfX; i1++){
        for (int i2 = 0; i2 <state.widthOfX; i2++){
            int rowOffset = numCategories * i1;
            int colOffset = numCategories * i2;

            X_transp_AX.block(rowOffset, colOffset, numCategories,  numCategories) = XXTrans(i1, i2) * a;
        }
    }

    triangularView<Lower>(state.X_transp_AX) += X_transp_AX;
    int numIndepVars = static_cast<int>(state.coef.size() / state.numCategories);

    // Marginal effects (reference calculated separately)
    ColumnVector coef_trans_prob;
    coef_trans_prob = coef.transpose() * prob;
    Matrix margins_matrix = coef;
    margins_matrix.rowwise() -= coef_trans_prob.transpose();
    margins_matrix = prob.asDiagonal() * margins_matrix;

    int idx, index, e_j_J, e_k_K, e_k_K_j_J;
    for (int K=0; K < numIndepVars; K++){
        for (int J=0; J < numCategories; J++){
            idx = K*numCategories + J;
            for (int k=0; k < numIndepVars; k++){
                for (int j=0; j < numCategories; j++){
                    e_j_J = (j==J) ? 1: 0;
                    e_k_K = (k==K) ? 1: 0;
                    e_k_K_j_J = (j==J && k==K) ? 1: 0;

                    index = k*numCategories + j;
                    state.delta(index, idx) +=
                        x(K) * (e_j_J  - prob(J)) * margins_matrix(j,k);
                    state.delta(index, idx) += prob(j) *
                        (e_k_K_j_J - prob(J) * e_k_K - x(K) * margins_matrix(J,k));
                }
            }
        }
    }

    state.margins_matrix += margins_matrix;

    return state;
}

// ----------------------------------------------------------------------

/**
 * @brief Perform the perliminary aggregation function: Merge transition states
 This merge step is the same as that of logistic regression.
*/
AnyType
mlogregr_marginal_step_merge_states::run(AnyType &args) {
    mlogregrMarginalTransitionState<MutableArrayHandle<double> > stateLeft = args[0];
    mlogregrMarginalTransitionState<ArrayHandle<double> > stateRight = args[1];

    // We first handle the trivial case where this function is called with one
    // of the states being the initial state
    if (stateLeft.numRows == 0)
        return stateRight;
    else if (stateRight.numRows == 0)
        return stateLeft;

    // Merge states together and return
    stateLeft += stateRight;
    return stateLeft;
}

AnyType mlogregr_marginalstateToResult(
    const Allocator &inAllocator,
    const double numRows,
    const ColumnVector &inCoef,
    const ColumnVector &inMargins,
    const ColumnVector &inVariance
                                       ) {
    MutableNativeColumnVector margins(
        inAllocator.allocateArray<double>(inMargins.size()));
    MutableNativeColumnVector coef(
        inAllocator.allocateArray<double>(inMargins.size()));
    MutableNativeColumnVector stdErr(
        inAllocator.allocateArray<double>(inMargins.size()));
    MutableNativeColumnVector tStats(
        inAllocator.allocateArray<double>(inMargins.size()));
    MutableNativeColumnVector pValues(
        inAllocator.allocateArray<double>(inMargins.size()));

    for (Index i = 0; i < inMargins.size(); ++i) {
        margins(i) = inMargins(i);
        coef(i) = inCoef(i);
        stdErr(i) = std::sqrt(inVariance(i));
        tStats(i) = margins(i) / stdErr(i);

        // P-values only make sense if numRows > coef.size()
        if (numRows > inCoef.size())
            pValues(i) = 2. * prob::cdf( prob::normal(),
                                         -std::abs(tStats(i)));
    }

    // Return all coefficients, standard errors, etc. in a tuple
    // Note: PValues will return NULL if numRows <= coef.size
    AnyType tuple;
    tuple << margins
          << coef
          << stdErr
          << tStats
          << (numRows > inCoef.size()? pValues: Null());
    return tuple;
}

// ----------------------------------------------------------------------

/**
 * @brief Perform the logistic-regression final step
 */
AnyType
mlogregr_marginal_step_final::run(AnyType &args) {
    // We request a mutable object. Depending on the backend, this might perform
    // a deep copy.
    mlogregrMarginalTransitionState<ArrayHandle<double> > state = args[0];
    // Aggregates that haven't seen any data just return Null.
    if (state.numRows == 0)
        return Null();

    if(!state.coef.is_finite())
        throw NoSolutionFoundException("Over- or underflow in Newton step, "
                                       "while updating coefficients. Input data is likely of poor "
                                       "numerical condition.");

    // See MADLIB-138. At least on certain platforms and with certain versions,
    // LAPACK will run into an infinite loop if pinv() is called for non-finite
    // matrices. We extend the check also to the dependent variables.
    if (!state.X_transp_AX.is_finite())
        throw NoSolutionFoundException("Over- or underflow in intermediate "
                                       "calulation. Input data is likely of poor numerical condition.");

    // Include marginal effects of reference variable:
    // FIXME: They have been taken out of the output for now
    //const int size = state.coef.size() + numIndepVars;
    const size_t size = state.coef.size();

    // Variance-covariance calculation
    // ----------------------------------------------------------
    SymmetricPositiveDefiniteEigenDecomposition<Matrix> decomposition(
        -1 * state.X_transp_AX, EigenvaluesOnly, ComputePseudoInverse);

    // Precompute -(X^T * A * X)^-1
    Matrix V = decomposition.pseudoInverse();

    int numIndepVars = static_cast<int>(state.coef.size() / state.numCategories);
    int numCategories = state.numCategories;

    Matrix coef = state.coef;
    coef.resize(state.numCategories, state.widthOfX);

    // Variance & Marginal gradient
    ColumnVector marginal_gradient(size);
    ColumnVector variance(size);
    variance.setOnes();

    variance = (state.delta * V * trans(state.delta) / static_cast<double>(state.numRows*state.numRows)).diagonal();

    // Add in reference variables to all the calculations
    // ----------------------------------------------------------
    ColumnVector coef_with_ref(size);
    ColumnVector margins_with_ref(size);

    // Vectorize the margins_matrix and add the reference variable
    for (int k=0; k < numIndepVars; k++){
        for (int j=0; j < numCategories; j++){
            int index = k * numCategories + j;
            coef_with_ref(index) = coef(j,k);
            margins_with_ref(index) = state.margins_matrix(j,k) / static_cast<double>(state.numRows);
        }
    }

    return mlogregr_marginalstateToResult(*this,
                                          static_cast<double>(state.numRows),
                                          coef_with_ref,
                                          margins_with_ref,
                                          variance);
}

}
}
}


#include <chainparams.h>
#include <masternodes/masternodes.h>
#include <validation.h>

#include <test/setup_common.h>

#include <boost/test/unit_test.hpp>

inline uint256 NextTx()
{
    static int txs_counter = 1;

    std::stringstream stream;
    stream << std::hex << txs_counter++;
    return uint256S(stream.str() );
}

DCT_ID CreateToken(CCustomCSView &mnview, std::string const & symbol, uint8_t flags = (uint8_t)CToken::TokenFlags::Default)
{
    CTokenImplementation token;
    token.creationTx = NextTx();
    token.symbol = symbol;
    token.flags = flags;

    auto res = mnview.CreateToken(token, false);
    if (!res.ok) printf("%s\n", res.msg.c_str());
    BOOST_REQUIRE(res.ok);
    return *res.val;
}

// tokenz A,B,LP
std::tuple<DCT_ID, DCT_ID, DCT_ID> CreatePoolNTokens(CCustomCSView &mnview, std::string const & symbolA, std::string const & symbolB)
{
    DCT_ID idA = CreateToken(mnview, symbolA);
    DCT_ID idB = CreateToken(mnview, symbolB);

    DCT_ID idPool = CreateToken(mnview, symbolA + "-" +  symbolB, (uint8_t)CToken::TokenFlags::Default | (uint8_t)CToken::TokenFlags::DAT | (uint8_t)CToken::TokenFlags::LPS);
    {
        CPoolPair pool{};
        pool.idTokenA = idA;
        pool.idTokenB = idB;
        pool.commission = 1000000; // 1%
    //    poolMsg.ownerAddress = CScript();
        pool.status = true;
        BOOST_REQUIRE(mnview.SetPoolPair(idPool, pool).ok);
    }
    return std::tuple<DCT_ID, DCT_ID, DCT_ID>(idA, idB, idPool); // ! simple initialization list (as "{a,b,c}")  doesn't work here under ubuntu 16.04 - due to older gcc?
}

Res AddPoolLiquidity(CCustomCSView &mnview, DCT_ID idPool, CAmount amountA, CAmount amountB, CScript const & shareAddress)
{
    auto optPool = mnview.GetPoolPair(idPool);
    BOOST_REQUIRE(optPool);

    const auto res = optPool->AddLiquidity(amountA, amountB, shareAddress, [&] /*onMint*/(CScript const & to, CAmount liqAmount) -> Res {
        BOOST_CHECK(liqAmount > 0);

        auto add = mnview.AddBalance(to, { idPool, liqAmount });
        if (!add.ok) {
            return add;
        }
        //insert update ByShare index
        const auto setShare = mnview.SetShare(idPool, to);
        if (!setShare.ok) {
            return setShare;
        }
        return Res::Ok();
    });
    BOOST_REQUIRE(res.ok);
    return mnview.SetPoolPair(idPool, *optPool);
}

BOOST_FIXTURE_TEST_SUITE(liquidity_tests, TestingSetup)

BOOST_AUTO_TEST_CASE(math_liquidity_and_trade)
{
    auto FAIL_onMint = [](const CScript &, CAmount)-> Res { BOOST_REQUIRE(false); return Res::Err("it should not happen"); };
    auto FAIL_onSwap = [](const CTokenAmount &)-> Res { BOOST_REQUIRE(false); return Res::Err("it should not happen"); };

    CCustomCSView mnview(*pcustomcsview);

    DCT_ID idA, idB, idPool;
    std::tie(idA, idB, idPool) = CreatePoolNTokens(mnview, "AAA", "BBB");
    auto optPool = mnview.GetPoolPair(idPool);
    BOOST_REQUIRE(optPool);

    Res res{};
    { // basic fails
        CPoolPair pool = *optPool;
        res = pool.AddLiquidity(-1, 1000, {}, FAIL_onMint);
        BOOST_CHECK(!res.ok && res.msg == "amounts should be positive");
        res = pool.AddLiquidity(0, 1000, {}, FAIL_onMint);
        BOOST_CHECK(!res.ok && res.msg == "amounts should be positive");
        res = pool.AddLiquidity(1, 1000, {}, FAIL_onMint);
        BOOST_CHECK(!res.ok && res.msg == "liquidity too low");
        res = pool.AddLiquidity(10, 100000, {}, FAIL_onMint);
        BOOST_CHECK(!res.ok && res.msg == "liquidity too low"); // median == MINIMUM_LIQUIDITY
    }

    { // amounts a bit larger MINIMUM_LIQUIDITY
        CPoolPair pool = *optPool;
        res = pool.AddLiquidity(11, 100000, {}, [](const CScript &, CAmount liq)-> Res {
            BOOST_CHECK(liq == 48); // sqrt (11*100000) - MINIMUM_LIQUIDITY
            return Res::Ok();
        });
        BOOST_CHECK(res.ok);
        BOOST_CHECK(pool.reserveA == 11);
        BOOST_CHECK(pool.reserveB == 100000);
    }

    {   // one limit
        CPoolPair pool = *optPool;
        res = pool.AddLiquidity(std::numeric_limits<CAmount>::max(), 1, {}, [](const CScript &, CAmount liq)-> Res {
            BOOST_CHECK(liq == 3036999499); // == sqrt(limit)-MINIMUM_LIQUIDITY
            return Res::Ok();
        });
        BOOST_CHECK(res.ok);
        BOOST_CHECK(pool.reserveA == std::numeric_limits<CAmount>::max());
        BOOST_CHECK(pool.reserveB == 1);
        BOOST_CHECK(pool.totalLiquidity == 3036999499 + CPoolPair::MINIMUM_LIQUIDITY);

        // plus 1
        res = pool.AddLiquidity(1, 1, {}, FAIL_onMint);
        BOOST_CHECK(!res.ok && res.msg == "amounts too low, zero liquidity");

        // we can't swap forward even 1 satoshi
        res = pool.Swap(CTokenAmount{pool.idTokenA, 1}, PoolPrice{std::numeric_limits<CAmount>::max(), 0}, FAIL_onSwap);
        BOOST_CHECK(!res.ok && res.msg == "Lack of liquidity.");

        // and backward too
        res = pool.Swap(CTokenAmount{pool.idTokenB, 2}, PoolPrice{std::numeric_limits<CAmount>::max(), 0}, FAIL_onSwap);
        BOOST_CHECK(!res.ok && res.msg == "Lack of liquidity.");

        // thats all, we can't place anything here until removing. trading disabled due to reserveB < SLOPE_SWAP_RATE
    }

    {   // two limits
        CPoolPair pool = *optPool;
        res = pool.AddLiquidity(std::numeric_limits<CAmount>::max(), std::numeric_limits<CAmount>::max(), {}, [](const CScript &, CAmount liq)-> Res {
            BOOST_CHECK(liq == std::numeric_limits<CAmount>::max() - CPoolPair::MINIMUM_LIQUIDITY);
            return Res::Ok();
        });
        BOOST_CHECK(res.ok);
        BOOST_CHECK(pool.reserveA == std::numeric_limits<CAmount>::max());
        BOOST_CHECK(pool.reserveB == std::numeric_limits<CAmount>::max());
        BOOST_CHECK(pool.totalLiquidity == std::numeric_limits<CAmount>::max());

        res = pool.AddLiquidity(1, 1, {}, FAIL_onMint);
        BOOST_CHECK(!res.ok && res.msg.find("can't add") != res.msg.npos); // in fact we got liquidity overflows

        // thats all, we can't do anything here until removing
    }

    // trying to swap moooore than reserved on low reserves (sliding)
    // it works extremely bad on low reserves, but it's okay, just bad trade.
    {
        CPoolPair pool = *optPool;
        res = pool.AddLiquidity(1001, 1001, {}, [](const CScript &, CAmount liq)-> Res {
            BOOST_CHECK(liq == 1001 - CPoolPair::MINIMUM_LIQUIDITY);
            return Res::Ok();
        });
        res = pool.Swap(CTokenAmount{pool.idTokenA, 1000000}, PoolPrice{std::numeric_limits<CAmount>::max(), 0}, [&] (CTokenAmount const &ta) -> Res{
            BOOST_CHECK_EQUAL(ta.nValue, 1000);
            return Res::Ok();
        });
        BOOST_CHECK(res.ok);
        BOOST_CHECK_EQUAL(pool.blockCommissionA, 10000);
        BOOST_CHECK_EQUAL(pool.reserveA, 991001);
        BOOST_CHECK_EQUAL(pool.reserveB, 1);
    }

    // trying to swap moooore than reserved (sliding), but on "resonable" reserves
    {
        CPoolPair pool = *optPool;
        res = pool.AddLiquidity(COIN, COIN, {}, [](const CScript &, CAmount liq)-> Res {
            return Res::Ok();
        });
        res = pool.Swap(CTokenAmount{pool.idTokenA, 2*COIN}, PoolPrice{std::numeric_limits<CAmount>::max(), 0}, [&] (CTokenAmount const &ta) -> Res{
            BOOST_CHECK_EQUAL(ta.nValue, 66442954); // pre-optimization: 66464593
            return Res::Ok();
        });
        BOOST_CHECK(res.ok);
        BOOST_CHECK_EQUAL(pool.blockCommissionA, 2000000);
        BOOST_CHECK_EQUAL(pool.reserveA, 298000000);
        BOOST_CHECK_EQUAL(pool.reserveB, 33557046); // pre-optimization: 33535407

    }

    {
//        printf("2 COIN (1:1000)\n");
        CPoolPair pool = *optPool;
        res = pool.AddLiquidity(COIN, 1000*COIN, {}, [](const CScript &, CAmount liq)-> Res {
            return Res::Ok();
        });
        res = pool.Swap(CTokenAmount{pool.idTokenA, 2*COIN}, PoolPrice{std::numeric_limits<CAmount>::max(), 0}, [&] (CTokenAmount const &ta) -> Res{
            BOOST_CHECK_EQUAL(ta.nValue, 66442953021); // pre-optimization: 66465256146
            return Res::Ok();
        });
        BOOST_CHECK(res.ok);
        BOOST_CHECK_EQUAL(pool.blockCommissionA, 2000000);
        BOOST_CHECK_EQUAL(pool.reserveA, 298000000);
        BOOST_CHECK_EQUAL(pool.reserveB, 33557046979); // pre-optimization: 33534743854
    }
    {
//        printf("1 COIN (1:1000)\n");
        CPoolPair pool = *optPool;
        res = pool.AddLiquidity(COIN, 1000*COIN, {}, [](const CScript &, CAmount liq)-> Res {
            return Res::Ok();
        });
        res = pool.Swap(CTokenAmount{pool.idTokenA, COIN}, PoolPrice{std::numeric_limits<CAmount>::max(), 0}, [&] (CTokenAmount const &ta) -> Res{
            BOOST_CHECK_EQUAL(ta.nValue, 49748743719); // pre-optimization: 49773755285
            return Res::Ok();
        });
        BOOST_CHECK(res.ok);
        BOOST_CHECK_EQUAL(pool.blockCommissionA, 1000000);
        BOOST_CHECK_EQUAL(pool.reserveA, 199000000);
        BOOST_CHECK_EQUAL(pool.reserveB, 50251256281); // pre-optimization: 50226244715
    }
    {
//        printf("COIN/1000 (1:1000) (no slope due to commission)\n");
        CPoolPair pool = *optPool;
        res = pool.AddLiquidity(COIN, 1000*COIN, {}, [](const CScript &, CAmount liq)-> Res {
            return Res::Ok();
        });
        res = pool.Swap(CTokenAmount{pool.idTokenA, COIN/1000}, PoolPrice{std::numeric_limits<CAmount>::max(), 0}, [&] (CTokenAmount const &ta) -> Res{
            BOOST_CHECK_EQUAL(ta.nValue, 98902087); // pre-optimization: 99000000
            return Res::Ok();
        });
        BOOST_CHECK(res.ok);
        BOOST_CHECK_EQUAL(pool.blockCommissionA, 1000);
        BOOST_CHECK_EQUAL(pool.reserveA, 100099000);
        BOOST_CHECK_EQUAL(pool.reserveB, 99901097913); // pre-optimization: 99901000000

//       printf("comissionA = %ld\n", pool.blockCommissionA);
//       printf("reserveA = %ld\n", pool.reserveA);
//       printf("reserveB = %ld\n", pool.reserveB);
    }
}

void SetPoolRewardPct(CCustomCSView & mnview, DCT_ID idPool, CAmount pct)
{
    auto optPool = mnview.GetPoolPair(idPool);
    BOOST_REQUIRE(optPool);
    optPool->rewardPct = pct;
    mnview.SetPoolPair(idPool, *optPool);
}

void SetPoolTradeFees(CCustomCSView & mnview, DCT_ID idPool, CAmount A, CAmount B)
{
    auto optPool = mnview.GetPoolPair(idPool);
    BOOST_REQUIRE(optPool);
    optPool->blockCommissionA = A;
    optPool->blockCommissionB = B;
    optPool->swapEvent = true;
    mnview.SetPoolPair(idPool, *optPool);
}

BOOST_AUTO_TEST_CASE(math_rewards)
{
    const int PoolCount = 10; // less than DCT_ID_START!
    const int ProvidersCount = 10000;

    CCustomCSView mnview(*pcustomcsview);

    // create pools
    for (int i = 0; i < PoolCount; ++i) {
        DCT_ID idA, idB, idPool;
        std::tie(idA, idB, idPool) = CreatePoolNTokens(mnview, "A"+std::to_string(i), "B"+std::to_string(i));
        auto optPool = mnview.GetPoolPair(idPool);
        BOOST_REQUIRE(optPool);

    }
    // create shares
    mnview.ForEachPoolPair([&] (DCT_ID const & idPool, CPoolPair const & pool) {
//            printf("pool id = %s\n", idPool.ToString().c_str());
        for (int i = 0; i < ProvidersCount; ++i) {
            CScript shareAddress = CScript(idPool.v * ProvidersCount + i);
            Res res = AddPoolLiquidity(mnview, idPool, idPool.v*COIN, idPool.v*COIN, shareAddress);
            BOOST_CHECK(res.ok);
        }
        return true;
    });

    {
        CCustomCSView cache(mnview);

        // set pool rewards rates
        const DCT_ID RWD50 = DCT_ID{1};
        const DCT_ID RWD25 = DCT_ID{2};
        SetPoolRewardPct(cache, RWD50, COIN/2); // 50%
        SetPoolRewardPct(cache, RWD25, COIN/4); // 25%

        SetPoolRewardPct(cache, DCT_ID{3}, COIN/10); // 10%
        SetPoolRewardPct(cache, DCT_ID{4}, COIN/10); // 10%
        SetPoolRewardPct(cache, DCT_ID{5}, COIN/100);
        SetPoolRewardPct(cache, DCT_ID{6}, COIN/100);
        SetPoolRewardPct(cache, DCT_ID{7}, COIN/100);
        SetPoolRewardPct(cache, DCT_ID{8}, COIN/100);
        SetPoolRewardPct(cache, DCT_ID{9}, COIN/100);
        /// DCT_ID{10} - 0

        // set "traded fees" here too, just to estimate proc.load
        cache.ForEachPoolPair([&] (DCT_ID const & idPool, CPoolPair const & pool) {
            SetPoolTradeFees(cache, idPool, idPool.v * COIN, idPool.v * COIN*2);
            return true;
        });

        // distribute 100 coins
        CAmount totalRwd = 100*COIN;

        int64_t nTimeBegin = GetTimeMicros();
        CAmount distributed = cache.DistributeRewards(totalRwd,
            [&cache] (CScript const & owner, DCT_ID tokenID) {
                return cache.GetBalance(owner, tokenID);
            },
            [&cache] (CScript const & to, CTokenAmount amount) {
                return cache.AddBalance(to, amount);
            }
        );
        int64_t nTimeEnd = GetTimeMicros(); auto nTimeRwd = nTimeEnd - nTimeBegin;
//        printf("Rewarded %d pools with %d shares each: %.2fms \n", PoolCount, ProvidersCount, 0.001 * (nTimeRwd));

//        printf("Distributed: = %ld\n", distributed);
        BOOST_CHECK(distributed == 9999000000); // always slightly less due to MINIMUM_LIQUIDITY & rounding

        // check it
        auto rwd25 = 25 * COIN / ProvidersCount;
        auto rwd50 = 50 * COIN / ProvidersCount;
        cache.ForEachPoolShare([&] (DCT_ID const & id, CScript const & owner) {
//            if (id == RWD25/* || id == RWD50*/)
//                printf("owner = %s: %s,\n", owner.GetHex().c_str(), cache.GetBalance(owner, DCT_ID{0}).ToString().c_str());

            // check only first couple of pools and the last (zero)
            if (id == RWD25 && owner != CScript(id.v * ProvidersCount)) { // first got slightly less due to MINIMUM_LIQUIDITY
                CAmount rwd = cache.GetBalance(owner, DCT_ID{0}).nValue;
                BOOST_CHECK(rwd == rwd25);
            }
            if (id == RWD50 && owner != CScript(id.v * ProvidersCount)) { // first got slightly less due to MINIMUM_LIQUIDITY
                CAmount rwd = cache.GetBalance(owner, DCT_ID{0}).nValue;
                BOOST_CHECK(rwd == rwd50);
            }
            if (id == DCT_ID{10} ) { // first got slightly less due to MINIMUM_LIQUIDITY
                CAmount rwd = cache.GetBalance(owner, DCT_ID{0}).nValue;
                BOOST_CHECK(rwd == 0);
            }
            return true;
        });

        // check trade comissions for one of pools
        {
            DCT_ID idPool = DCT_ID{1};
            auto optPool = cache.GetPoolPair(idPool);
            cache.ForEachPoolShare([&] (DCT_ID const & id, CScript const & owner) {
                if (id != idPool)
                    return false;

                if (owner != CScript(id.v * ProvidersCount)) { // first got slightly less due to MINIMUM_LIQUIDITY
                    CAmount rwdA = cache.GetBalance(owner, DCT_ID{optPool->idTokenA}).nValue;
                    CAmount rwdB = cache.GetBalance(owner, DCT_ID{optPool->idTokenB}).nValue;
                    BOOST_CHECK(rwdA == id.v * COIN / ProvidersCount);
                    BOOST_CHECK(rwdB == id.v * COIN * 2 / ProvidersCount);
                }
                return true;
            }, PoolShareKey{idPool, {}});
        }
    }
}

BOOST_AUTO_TEST_SUITE_END()


//
// Created by przemek on 27.03.2020.
//

#include <AST/expression/base_math_expr.h>
#include <AST/expression/or_logic_expr.h>
#include <boost/test/unit_test.hpp>
#include <error/exeception.h>

using namespace vecc;
using namespace vecc::ast;

using std::make_unique;
using std::move;
using std::unique_ptr;

BOOST_AUTO_TEST_SUITE(AST_Test_Suite)

BOOST_AUTO_TEST_SUITE(Or_Logic_Expr_Test_Suite)

BOOST_AUTO_TEST_CASE(GivenMathExpr_ValueIsCorrect) {
  Variable var = Variable({1, 2, 3});
  OrLogicExpr expr(make_unique<BaseMathExpr>(var));

  BOOST_CHECK_EQUAL(expr.calculate(), var);
}

BOOST_AUTO_TEST_CASE(GivenTrueAndFalse_ValueIsCorrect) {
  Variable trueVar  = Variable({1, 2, 3});
  Variable falseVar = Variable({0, 0});

  BOOST_REQUIRE_EQUAL(true, trueVar);
  BOOST_REQUIRE_EQUAL(false, falseVar);

  OrLogicExpr expr(make_unique<BaseMathExpr>(trueVar));
  expr.addOperand(make_unique<BaseMathExpr>(falseVar));

  BOOST_CHECK_EQUAL(true, expr.calculate());
}

BOOST_AUTO_TEST_CASE(GivenFalseAndFalse_ValueIsCorrect) {
  Variable falseVar1 = Variable({0, 0, 0});
  Variable falseVar2 = Variable({0, 0});

  BOOST_REQUIRE_EQUAL(false, falseVar1);
  BOOST_REQUIRE_EQUAL(false, falseVar2);

  OrLogicExpr expr(make_unique<BaseMathExpr>(falseVar1));
  expr.addOperand(make_unique<BaseMathExpr>(falseVar2));

  BOOST_CHECK_EQUAL(false, expr.calculate());
}

BOOST_AUTO_TEST_CASE(GivenTrueAndTrue_ValueIsCorrect) {
  Variable trueVar1 = Variable({1, 1, 0});
  Variable trueVar2 = Variable({1, 0});

  BOOST_REQUIRE_EQUAL(true, trueVar1);
  BOOST_REQUIRE_EQUAL(true, trueVar2);

  OrLogicExpr expr(make_unique<BaseMathExpr>(trueVar1));
  expr.addOperand(make_unique<BaseMathExpr>(trueVar2));

  BOOST_CHECK_EQUAL(true, expr.calculate());
}

BOOST_AUTO_TEST_CASE(GivenThreeVals_ValueIsCorrect_1) {
  Variable trueVar1  = Variable({5, 1, 0});
  Variable trueVar2  = Variable({1, -9});
  Variable falseVar1 = Variable({0, 0});

  BOOST_REQUIRE_EQUAL(true, trueVar1);
  BOOST_REQUIRE_EQUAL(true, trueVar2);
  BOOST_REQUIRE_EQUAL(false, falseVar1);

  OrLogicExpr expr1(make_unique<BaseMathExpr>(trueVar1));
  expr1.addOperand(make_unique<BaseMathExpr>(trueVar2));
  expr1.addOperand(make_unique<BaseMathExpr>(falseVar1));

  OrLogicExpr expr2(make_unique<BaseMathExpr>(trueVar2));
  expr2.addOperand(make_unique<BaseMathExpr>(falseVar1));
  expr2.addOperand(make_unique<BaseMathExpr>(trueVar1));

  OrLogicExpr expr3(make_unique<BaseMathExpr>(falseVar1));
  expr3.addOperand(make_unique<BaseMathExpr>(trueVar2));
  expr3.addOperand(make_unique<BaseMathExpr>(trueVar1));

  BOOST_CHECK_EQUAL(true, expr1.calculate());
  BOOST_CHECK_EQUAL(true, expr2.calculate());
  BOOST_CHECK_EQUAL(true, expr3.calculate());
}

BOOST_AUTO_TEST_CASE(GivenThreeVals_ValueIsCorrect_2) {
  Variable trueVar1  = Variable({5, 1, 0});
  Variable falseVar1 = Variable({0, 0, 0});
  Variable falseVar2 = Variable({0, 0});

  BOOST_REQUIRE_EQUAL(true, trueVar1);
  BOOST_REQUIRE_EQUAL(false, falseVar1);
  BOOST_REQUIRE_EQUAL(false, falseVar2);

  OrLogicExpr expr1(make_unique<BaseMathExpr>(trueVar1));
  expr1.addOperand(make_unique<BaseMathExpr>(falseVar1));
  expr1.addOperand(make_unique<BaseMathExpr>(falseVar2));

  OrLogicExpr expr2(make_unique<BaseMathExpr>(falseVar2));
  expr2.addOperand(make_unique<BaseMathExpr>(falseVar1));
  expr2.addOperand(make_unique<BaseMathExpr>(trueVar1));

  OrLogicExpr expr3(make_unique<BaseMathExpr>(falseVar1));
  expr3.addOperand(make_unique<BaseMathExpr>(falseVar2));
  expr3.addOperand(make_unique<BaseMathExpr>(trueVar1));

  BOOST_CHECK_EQUAL(true, expr1.calculate());
  BOOST_CHECK_EQUAL(true, expr2.calculate());
  BOOST_CHECK_EQUAL(true, expr3.calculate());
}

BOOST_AUTO_TEST_CASE(GivenThreeVals_ValueIsCorrect_3) {
  Variable falseVar1 = Variable({0, 0, 0});
  Variable falseVar2 = Variable({0, 0});
  Variable falseVar3 = Variable({0});

  BOOST_REQUIRE_EQUAL(false, falseVar1);
  BOOST_REQUIRE_EQUAL(false, falseVar2);
  BOOST_REQUIRE_EQUAL(false, falseVar3);

  OrLogicExpr expr1(make_unique<BaseMathExpr>(falseVar1));
  expr1.addOperand(make_unique<BaseMathExpr>(falseVar2));
  expr1.addOperand(make_unique<BaseMathExpr>(falseVar3));

  OrLogicExpr expr2(make_unique<BaseMathExpr>(falseVar3));
  expr2.addOperand(make_unique<BaseMathExpr>(falseVar2));
  expr2.addOperand(make_unique<BaseMathExpr>(falseVar1));

  OrLogicExpr expr3(make_unique<BaseMathExpr>(falseVar2));
  expr3.addOperand(make_unique<BaseMathExpr>(falseVar3));
  expr3.addOperand(make_unique<BaseMathExpr>(falseVar1));

  BOOST_CHECK_EQUAL(false, expr1.calculate());
  BOOST_CHECK_EQUAL(false, expr2.calculate());
  BOOST_CHECK_EQUAL(false, expr3.calculate());
}

BOOST_AUTO_TEST_CASE(GivenFiveValues_ValueIsCorrect_1) {
  Variable falseVar1 = Variable({0, 0, 0});
  Variable falseVar2 = Variable({0, 0});
  Variable falseVar3 = Variable({0});
  Variable trueVar4  = Variable({1, 7});
  Variable trueVar5  = Variable({19, -5});

  BOOST_REQUIRE_EQUAL(false, falseVar1);
  BOOST_REQUIRE_EQUAL(false, falseVar2);
  BOOST_REQUIRE_EQUAL(false, falseVar3);
  BOOST_REQUIRE_EQUAL(true, trueVar4);
  BOOST_REQUIRE_EQUAL(true, trueVar5);

  OrLogicExpr expr(make_unique<BaseMathExpr>(falseVar1));
  expr.addOperand(make_unique<BaseMathExpr>(trueVar4));
  expr.addOperand(make_unique<BaseMathExpr>(falseVar2));
  expr.addOperand(make_unique<BaseMathExpr>(falseVar3));
  expr.addOperand(make_unique<BaseMathExpr>(trueVar5));

  BOOST_CHECK_EQUAL(true, expr.calculate());
}

BOOST_AUTO_TEST_CASE(GivenFiveValues_ValueIsCorrect_2) {
  Variable falseVar1 = Variable({0, 0, 0});
  Variable falseVar2 = Variable({0, 0});
  Variable falseVar3 = Variable({0});
  Variable trueVar1  = Variable({1, 7});
  Variable falseVar4 = Variable({0, 0});

  BOOST_REQUIRE_EQUAL(false, falseVar1);
  BOOST_REQUIRE_EQUAL(false, falseVar2);
  BOOST_REQUIRE_EQUAL(false, falseVar3);
  BOOST_REQUIRE_EQUAL(true, trueVar1);
  BOOST_REQUIRE_EQUAL(false, falseVar4);

  OrLogicExpr expr(make_unique<BaseMathExpr>(falseVar1));
  expr.addOperand(make_unique<BaseMathExpr>(falseVar2));
  expr.addOperand(make_unique<BaseMathExpr>(falseVar3));
  expr.addOperand(make_unique<BaseMathExpr>(trueVar1));
  expr.addOperand(make_unique<BaseMathExpr>(falseVar4));

  BOOST_CHECK_EQUAL(true, expr.calculate());
}

BOOST_AUTO_TEST_CASE(GivenFiveValues_ValueIsCorrect_3) {
  Variable falseVar1 = Variable({0, 0, 0});
  Variable falseVar2 = Variable({0, 0});
  Variable falseVar3 = Variable({0});
  Variable falseVar4 = Variable({0, 0, 0});
  Variable falseVar5 = Variable({0, 0});

  BOOST_REQUIRE_EQUAL(false, falseVar1);
  BOOST_REQUIRE_EQUAL(false, falseVar2);
  BOOST_REQUIRE_EQUAL(false, falseVar3);
  BOOST_REQUIRE_EQUAL(false, falseVar4);
  BOOST_REQUIRE_EQUAL(false, falseVar5);

  OrLogicExpr expr(make_unique<BaseMathExpr>(falseVar1));
  expr.addOperand(make_unique<BaseMathExpr>(falseVar2));
  expr.addOperand(make_unique<BaseMathExpr>(falseVar3));
  expr.addOperand(make_unique<BaseMathExpr>(falseVar4));
  expr.addOperand(make_unique<BaseMathExpr>(falseVar5));

  BOOST_CHECK_EQUAL(false, expr.calculate());
}

BOOST_AUTO_TEST_CASE(GivenVal_ToStringCorrect) {
  Variable var = Variable({1, 2});

  OrLogicExpr expr(make_unique<BaseMathExpr>(var));

  BOOST_CHECK_EQUAL(expr.toString(), "vec(1, 2)");
}

BOOST_AUTO_TEST_CASE(GivenThreeVals_ToStringCorrect) {
  Variable trueVar1  = Variable({5, 1, 0});
  Variable falseVar1 = Variable({0, 0, 0});
  Variable falseVar2 = Variable({0, 0});

  OrLogicExpr expr1(make_unique<BaseMathExpr>(trueVar1));
  expr1.addOperand(make_unique<BaseMathExpr>(falseVar1));
  expr1.addOperand(make_unique<BaseMathExpr>(falseVar2));

  BOOST_CHECK_EQUAL(expr1.toString(),
                    "(vec(5, 1, 0) or vec(0, 0, 0) or vec(0, 0))");
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()


//
// Copyright (C) 2013 Mateusz Loskot <mateusz@loskot.net>
//
// 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)
//
#include "spatial_index_benchmark.hpp"
#ifdef SIBENCH_RTREE_LOAD_BLK
#define BOOST_GEOMETRY_INDEX_DETAIL_EXPERIMENTAL 1
#endif
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/index/rtree.hpp>
// enable internal debugging utilities
#include "../config.h"

#include <boost/geometry/index/detail/rtree/utilities/print.hpp>
#include <boost/geometry/index/detail/rtree/utilities/statistics.hpp>

namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;

typedef bg::model::point<sibench::coord_t, 2, bg::cs::cartesian> point_t;
typedef bg::model::box<point_t> box_t;

namespace {

#ifdef SIBENCH_BGI_RTREE_PARAMS_CT
std::string const lib("bgi_ct");
#elif SIBENCH_BGI_RTREE_PARAMS_RT
std::string const lib("bgi");
#else
#error Boost.Geometry rtree parameters variant unknown
#endif

struct ArrayIndexable {
  typedef sibench::id_type V;
  typedef box_t result_type;

  ArrayIndexable(const sibench::boxes2d_t &array) : array(&array) {}

  result_type operator()(sibench::id_type id) const {
    const auto &bbox = (*array)[id];
    return result_type(point_t(bbox.min[0], bbox.min[1]),
                       point_t(bbox.max[0], bbox.max[1]));
  }

private:
  const sibench::boxes2d_t *array;
};

#ifdef SIBENCH_RTREE_SPLIT_LINEAR
template <int max_capacity, int min_capacity>
using parameters_t = bgi::linear<max_capacity, min_capacity>;
#elif SIBENCH_RTREE_SPLIT_QUADRATIC
template <int max_capacity, int min_capacity>
using parameters_t = bgi::quadratic<max_capacity, min_capacity>;
#else
template <int max_capacity, int min_capacity>
using parameters_t = bgi::rstar<max_capacity, min_capacity>;
#endif
template <int max_capacity, int min_capacity>
using rtree_t =
    bgi::rtree<sibench::id_type, parameters_t<max_capacity, min_capacity>,
               ArrayIndexable>;

template <typename T>
void print_statistics(std::ostream &os, std::string const &lib, T const &i) {
  using bgi::detail::rtree::utilities::statistics;
  auto const s = statistics(i);
  os << sibench::get_banner(lib) << " stats: levels=" << boost::get<0>(s)
     << ", nodes=" << boost::get<1>(s) << ", leaves=" << boost::get<2>(s)
     << ", values=" << boost::get<3>(s) << ", values_min=" << boost::get<4>(s)
     << ", values_max=" << boost::get<5>(s) << std::endl;
}
}

template <typename Parameters>
void setCapacity(const Parameters &parameters, sibench::result_info &info) {
  info.min_capacity = Parameters::min_elements;
  info.max_capacity = Parameters::max_elements;
}

void setCapacity(const bgi::dynamic_linear &parameters,
                 sibench::result_info &info) {
  info.min_capacity = parameters.get_min_elements();
  info.max_capacity = parameters.get_max_elements();
}

void setCapacity(const bgi::dynamic_quadratic &parameters,
                 sibench::result_info &info) {
  info.min_capacity = parameters.get_min_elements();
  info.max_capacity = parameters.get_max_elements();
}

void setCapacity(const bgi::dynamic_rstar &parameters,
                 sibench::result_info &info) {
  info.min_capacity = parameters.get_min_elements();
  info.max_capacity = parameters.get_max_elements();
}

template <class TreeClass>
sibench::result_info benchmark_load(const sibench::boxes2d_t &boxes,
                                    TreeClass &rtree) {

  sibench::result_info load_r;
  setCapacity(rtree.parameters(), load_r);

  typedef std::vector<sibench::id_type> box_values_t;
  auto const iterations = boxes.size();
  box_values_t vboxes(iterations);
  std::iota(vboxes.begin(), vboxes.end(), 0);

#ifdef SIBENCH_RTREE_LOAD_BLK
  auto const marks = sibench::benchmark(
      "load", iterations, vboxes,
      [&rtree](box_values_t const &boxes, std::size_t iterations) {
        assert(iterations <= boxes.size());
        rtree = TreeClass(boxes.cbegin(), boxes.cbegin() + iterations, //
                          rtree.parameters(), rtree.indexable_get());
      });
#else
  auto const marks = sibench::benchmark(
      "load", iterations, vboxes,
      [&rtree](box_values_t const &boxes, std::size_t iterations) {
        assert(iterations <= boxes.size());
        rtree.insert(boxes.cbegin(), boxes.cbegin() + iterations);
      });
#endif

  load_r.accumulate(marks);

#if SIBENCH_DEBUG_PRINT_INFO == 1
  sibench::print_result(std::cout, lib, marks);
  print_statistics(std::cout, lib, rtree);
#endif

  return load_r;
}

template <class TreeClass>
sibench::result_info benchmark_query(const sibench::boxes2d_t &boxes,
                                     const TreeClass &rtree) {
  std::size_t query_found = 0;

  sibench::result_info query_r;
  setCapacity(rtree.parameters(), query_r);

  auto const marks = sibench::benchmark(
      "query", sibench::max_queries, boxes,
      [&rtree, &query_found](sibench::boxes2d_t const &boxes,
                             std::size_t iterations) {
        std::vector<sibench::id_type> results;
        results.reserve(iterations);

        for (std::size_t i = 0; i < iterations; ++i) {
          results.clear();

          auto const &box = boxes[i];
          point_t p1(box.min[0] - sibench::query_size,
                     box.min[1] - sibench::query_size);
          point_t p2(box.max[0] + sibench::query_size,
                     box.max[1] + sibench::query_size);
          box_t region(p1, p2);

          rtree.query(bgi::intersects(region), std::back_inserter(results));
          query_found += results.size();
        }
      });

  query_r.accumulate(marks);

#if SIBENCH_DEBUG_PRINT_INFO == 1
  sibench::print_result(std::cout, lib, marks);
  sibench::print_query_count(std::cout, lib, query_found);
#endif
  return query_r;
}

sibench::result_info benchmark_random(const sibench::boxes2d_t &boxes,
                                      size_t iterations) {

  ArrayIndexable indexable(boxes);
  rtree_t<16, 8> tree(parameters_t<16, 8>(), indexable);

  size_t insert_count = 0, total_count = 0;

  sibench::result_info res;
  setCapacity(tree.parameters(), res);
  res.iterations = iterations;

  size_t start = 0;
  for (int i = 0; i < sibench::random_runs; ++i) {
    sibench::result_info const marks = sibench::benchmark(
        "random", iterations, boxes,
        [&tree, &start, &insert_count, &total_count](
            sibench::boxes2d_t const &boxes, std::size_t iterations) {

          tree.clear();

          start = (start + iterations) % boxes.size();

          auto end = start + iterations;

          if (end > boxes.size()) {
            start = boxes.size() - iterations;
            end = boxes.size();
          }

          assert(end <= boxes.size());
          for (size_t i = start; i < end; ++i) {
            assert(iterations <= boxes.size());
            auto const &box = boxes[i];
            point_t p1(box.min[0], box.min[1]);
            point_t p2(box.max[0], box.max[1]);
            box_t region(p1, p2);

            if (tree.query(bgi::intersects(region),
                           spatial::detail::dummy_iterator()) == 0) {
              tree.insert(i);
              ++insert_count;
            }
            ++total_count;
          }
        });
    res.accumulate(marks);
  }

#if SIBENCH_DEBUG_PRINT_INFO == 1
  sibench::print_insert_count(std::cout, lib, insert_count, total_count);
#endif

  return res;
}

void benchmark_run_rt(const sibench::boxes2d_t &boxes) {
  for (std::size_t next_capacity = 4; next_capacity <= sibench::max_capacities;
       next_capacity += sibench::capacity_step) {
    double const fill_factor = 0.5;
    std::size_t const min_capacity = next_capacity;
    std::size_t const max_capacity =
        std::size_t(std::floor(min_capacity / fill_factor));
    {
      ArrayIndexable indexable(boxes);

#ifdef SIBENCH_RTREE_SPLIT_LINEAR
      typedef bgi::dynamic_linear rtree_parameters_t;
#elif SIBENCH_RTREE_SPLIT_QUADRATIC
      typedef bgi::dynamic_quadratic rtree_parameters_t;
#else
      typedef bgi::dynamic_rstar rtree_parameters_t;
#endif
      typedef bgi::rtree<sibench::id_type, rtree_parameters_t, ArrayIndexable>
          rtree_t;

      rtree_parameters_t rtree_parameters(max_capacity, min_capacity);
      rtree_t rtree(rtree_parameters, indexable);

      sibench::result_info load_r = benchmark_load(boxes, rtree);
      sibench::result_info query_r = benchmark_query(boxes, rtree);

      // single line per run
      sibench::print_result(std::cout, lib, load_r, query_r);

    } // for capacity
  }
}

template <int max_capacity, int min_capacity>
void benchmark_run_ct(const sibench::boxes2d_t &boxes) {

  ArrayIndexable indexable(boxes);
  rtree_t<max_capacity, min_capacity> rtree(
      parameters_t<max_capacity, min_capacity>(), indexable);

  sibench::result_info load_r = benchmark_load(boxes, rtree);
  sibench::result_info query_r = benchmark_query(boxes, rtree);

  // single line per run
  sibench::print_result(std::cout, lib, load_r, query_r);
}

int main() {
  try {

    sibench::print_result_header(std::cout, lib);

    // Generate random objects for indexing
    auto const boxes = sibench::generate_boxes(sibench::max_insertions);

#ifdef SIBENCH_BGI_RTREE_PARAMS_RT
    benchmark_run_rt(boxes);
#elif SIBENCH_BGI_RTREE_PARAMS_CT
    std::size_t const max_capacity = sibench::constant_max_capacity;
    std::size_t const min_capacity = sibench::constant_min_capacity;
    benchmark_run_ct<max_capacity, min_capacity>(boxes);
    benchmark_run_ct<max_capacity * 2, min_capacity * 2>(boxes);
    benchmark_run_ct<max_capacity * 3, min_capacity * 3>(boxes);
    benchmark_run_ct<max_capacity * 4, min_capacity * 4>(boxes);
    benchmark_run_ct<max_capacity * 5, min_capacity * 5>(boxes);
    benchmark_run_ct<max_capacity * 6, min_capacity * 6>(boxes);
    benchmark_run_ct<max_capacity * 8, min_capacity * 8>(boxes);
    benchmark_run_ct<max_capacity * 9, min_capacity * 9>(boxes);
    benchmark_run_ct<max_capacity * 11, min_capacity * 11>(boxes);
    benchmark_run_ct<max_capacity * 12, min_capacity * 12>(boxes);
    benchmark_run_ct<max_capacity * 16, min_capacity * 16>(boxes);
    benchmark_run_ct<max_capacity * 20, min_capacity * 20>(boxes);
    benchmark_run_ct<max_capacity * 24, min_capacity * 24>(boxes);
    benchmark_run_ct<max_capacity * 28, min_capacity * 28>(boxes);
    benchmark_run_ct<max_capacity * 32, min_capacity * 32>(boxes);
    benchmark_run_ct<max_capacity * 36, min_capacity * 36>(boxes);
    benchmark_run_ct<max_capacity * 40, min_capacity * 40>(boxes);
#else
#error "Unknown params!"
#endif

#ifndef SIBENCH_RTREE_LOAD_BLK
    // bulk load cannot be used since only dynamic insertions
    sibench::print_insert_result_header(std::cout, lib);
    for (int insertions = 10; insertions < sibench::max_random_insertions;
         insertions += insertions * sibench::insert_step) {

      sibench::result_info random_r = benchmark_random(boxes, insertions);
      sibench::print_insert_result(std::cout, lib, random_r);
    }
#endif

    return EXIT_SUCCESS;
  } catch (std::exception const &e) {
    std::cerr << e.what() << std::endl;
  } catch (...) {
    std::cerr << "unknown error" << std::endl;
  }
  return EXIT_FAILURE;
}