// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin developers
// Copyright (c) 2014-2015 The Dash developers
// Copyright (c) 2015-2018 The KORE developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "miner.h"
#include "amount.h"
#include "arith_uint256.h"
#include "hash.h"
#include "init.h"
#include "legacy/consensus/merkle.h"
#include "main.h"
#include "net.h"
#include "pos.h"
#include "pow.h"
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "support/csviterator.h"
#include "timedata.h"
#include "util.h"
#include "utilmoneystr.h"
#ifdef ENABLE_WALLET
#include "wallet.h"
#endif
#include "blocksignature.h"
#include "invalid.h"
#include "validationinterface.h"

#include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/filesystem.hpp>
#include <boost/program_options/detail/config_file.hpp>

#include <fstream>
#include <iostream>
#include <queue> // Legacy

using namespace std;

//////////////////////////////////////////////////////////////////////////////
//
// KOREMiner
//
//
// Unconfirmed transactions in the memory pool often depend on other
// transactions in the memory pool. When we select transactions from the
// pool, we select by highest priority or fee rate, so we might consider
// transactions that depend on transactions that aren't yet in the block.
// The COrphan class keeps track of these 'temporary orphans' while
// CreateBlock is figuring out which transactions to include.
//
class COrphan
{
public:
    const CTransaction* ptx;
    set<uint256> setDependsOn;
    CFeeRate feeRate;
    double dPriority;

    COrphan(const CTransaction* ptxIn) : ptx(ptxIn), feeRate(0), dPriority(0)
    {
    }
};

uint64_t nLastBlockTx = 0;
uint64_t nLastBlockSize = 0;

// We want to sort transactions by priority and fee rate, so:
typedef boost::tuple<double, CFeeRate, const CTransaction*> TxPriority;
class TxPriorityCompare
{
    bool byFee;

public:
    TxPriorityCompare(bool _byFee) : byFee(_byFee) {}

    bool operator()(const TxPriority& a, const TxPriority& b)
    {
        if (byFee) {
            if (a.get<1>() == b.get<1>())
                return a.get<0>() < b.get<0>();
            return a.get<1>() < b.get<1>();
        } else {
            if (a.get<0>() == b.get<0>())
                return a.get<1>() < b.get<1>();
            return a.get<0>() < b.get<0>();
        }
    }
};

class ScoreCompare //Legacy class
{
public:
    ScoreCompare() {}

    bool operator()(const CTxMemPool::txiter a, const CTxMemPool::txiter b)
    {
        return CompareTxMemPoolEntryByScore()(*b, *a); // Convert to less than
    }
};

CAmount GetBlockReward(CBlockIndex* pindexPrev)
{
    if (pindexPrev->nHeight >= Params().GetLastPoWBlock()) {
        if (pindexPrev->nMoneySupply == MAX_MONEY)
            return 0;

        double moneySupplyFloat = (double)pindexPrev->nMoneySupply / COIN;
        double rewardDouble = pow(1.44e14 - pow(moneySupplyFloat, 2), (double)1 / 2) / 1.436e7;

        CAmount reward = ceil(rewardDouble * COIN);
        if (reward + pindexPrev->nMoneySupply < MAX_MONEY)
            return reward;
        else
            return (MAX_MONEY)-pindexPrev->nMoneySupply;
    } else
        return GetBlockValue(pindexPrev->nHeight);
}

void UpdateTime(CBlockHeader* pblock, const CBlockIndex* pindexPrev, bool fProofOfStake)
{
    int64_t nOldTime = pblock->nTime;
    int64_t nNewTime = std::max(pindexPrev->GetMedianTimePast() + 1, GetAdjustedTime());

    if (nOldTime < nNewTime)
        pblock->nTime = nNewTime;

    // Updating time can change work required:
    pblock->nBits = GetNextTarget(pindexPrev, pblock, fProofOfStake);
}

inline CBlockIndex* GetParentIndex(CBlockIndex* index)
{
    return index->pprev;
}

unsigned int static DarkGravityWave3(const CBlockIndex* pindexLast, const CBlockHeader* pblock, bool fProofOfStake)
{
    /* current difficulty formula, darkcoin - DarkGravity v3, written by Evan Duffield - evan@darkcoin.io */
    const CBlockIndex* BlockLastSolved = pindexLast;
    const CBlockIndex* BlockReading = pindexLast;
    int64_t nActualTimespan = 0;
    int64_t LastBlockTime = 0;
    static int64_t PastBlocksMin = 24;
    static int64_t PastBlocksMax = 24;
    int64_t CountBlocks = 0;
    uint256 PastDifficultyAverage;
    uint256 PastDifficultyAveragePrev;
    static int64_t nTargetSpacing = Params().GetTargetSpacing();

    if (BlockLastSolved == NULL || BlockLastSolved->nHeight == 0 || BlockLastSolved->nHeight < PastBlocksMin) {
        return fProofOfStake ? Params().ProofOfStakeLimit().GetCompact() : Params().ProofOfWorkLimit().GetCompact();
    }

    uint256 bnTargetLimit = fProofOfStake ? Params().ProofOfStakeLimit() : Params().ProofOfWorkLimit();

    // loop over the past n blocks, where n == PastBlocksMax
    for (unsigned int i = 1; BlockReading && BlockReading->nHeight > 0; i++) {
        if (PastBlocksMax > 0 && i > PastBlocksMax) {
            break;
        }
        CountBlocks++;

        // Calculate average difficulty based on the blocks we iterate over in this for loop
        if (CountBlocks <= PastBlocksMin) {
            if (CountBlocks == 1)
                PastDifficultyAverage.SetCompact(BlockReading->nBits);
            else
                PastDifficultyAverage = ((PastDifficultyAveragePrev * CountBlocks) + (uint256().SetCompact(BlockReading->nBits))) / (CountBlocks + 1);

            PastDifficultyAveragePrev = PastDifficultyAverage;
        }

        // If this is the second iteration (LastBlockTime was set)
        if (LastBlockTime > 0) {
            // Calculate time difference between previous block and current block
            int64_t Diff = (LastBlockTime - BlockReading->GetBlockTime());
            // Increment the actual timespan
            nActualTimespan += Diff;
        }
        // Set LasBlockTime to the block time for the block in current iteration
        LastBlockTime = BlockReading->GetBlockTime();

        if (BlockReading->pprev == NULL) {
            assert(BlockReading);
            break;
        }
        BlockReading = BlockReading->pprev;
    }

    // bnNew is the difficulty
    uint256 bnNew(PastDifficultyAverage);

    // nTargetTimespan is the time that the CountBlocks should have taken to be generated.
    int64_t nTargetTimespan = CountBlocks * nTargetSpacing;

    // Limit the re-adjustment to 3x or 0.33x
    // We don't want to increase/decrease diff too much.
    if (nActualTimespan < nTargetTimespan / 3)
        nActualTimespan = nTargetTimespan / 3;
    if (nActualTimespan > nTargetTimespan * 3)
        nActualTimespan = nTargetTimespan * 3;

    int64_t nMyBlockSpacing = pblock->GetBlockTime() - pindexLast->GetBlockTime();
    int64_t nInterval = (nMyBlockSpacing - 2 * nTargetSpacing) / 2;
    // this only will take effect after we are 2 blocks away from the target
    int64_t pastDueSpacing = nInterval > 0 ? nInterval : 0;
    int64_t howManyDue = pastDueSpacing / nTargetSpacing;

    // Calculate the new difficulty based on actual and target timespan.
    bnNew *= (nActualTimespan + pow(nTargetSpacing, howManyDue));
    bnNew /= nTargetTimespan;

    // If calculated difficulty is lower than the minimal diff, set the new difficulty to be the minimal diff.
    if (bnNew <= 0 || bnNew > bnTargetLimit)
        bnNew = bnTargetLimit;

    // Return the new diff.
    return bnNew.GetCompact();
}

uint32_t GetNextTarget(const CBlockIndex* pindexLast, const CBlockHeader* pblock, bool fProofOfStake)
{
    // Lico
    return UseLegacyCode(pindexLast->nHeight + 1) ?
               GetNextWorkRequired_Legacy(pindexLast, pblock, fProofOfStake) :
               DarkGravityWave3(pindexLast, pblock, fProofOfStake);
}

inline CMutableTransaction CreateCoinbaseTransaction(const CScript& scriptPubKeyIn)
{
    // Create coinbase tx
    CMutableTransaction txNew;
    txNew.vin.resize(1);
    txNew.vin[0].prevout.SetNull();
    txNew.vout.resize(1);
    txNew.vout[0].scriptPubKey = scriptPubKeyIn;

    return txNew;
}

CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet, bool fProofOfStake)
{
    // Create new block
    unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate(CBlockHeader::POS_FORK_VERSION));
    if (!pblocktemplate.get())
        return NULL;

    // pointer for convenience
    CBlock* pblock = &pblocktemplate->block;

    // Set if block as proof of stake or not
    pblock->fIsProofOfStake = fProofOfStake;

    // -regtest only: allow overriding block.nVersion with
    // -blockversion=N to test forking scenarios
    if (Params().ShouldMineBlocksOnDemand())
        pblock->nVersion = GetArg("-blockversion", pblock->nVersion);

    CMutableTransaction txNew = CreateCoinbaseTransaction(scriptPubKeyIn);
    pblock->vtx.push_back(txNew);

    pblocktemplate->vTxFees.push_back(-1);   // updated at end
    pblocktemplate->vTxSigOps.push_back(-1); // updated at end

    // ppcoin: if coinstake available add coinstake tx
    if (fProofOfStake) {
        boost::this_thread::interruption_point();
        static int64_t nLastCoinStakeSearchTime = GetAdjustedTime(); // only initialized at startup
        bool fStakeFound = false;
        unsigned int nTxNewTime = 0;
        CMutableTransaction txCoinbase;
        CMutableTransaction txCoinStake;
        pblock->nTime = GetAdjustedTime();
        pblock->nBits = GetNextTarget(chainActive.Tip(), pblock, fProofOfStake);
        int64_t nSearchTime = pblock->nTime; // search to current time
        if (nSearchTime >= nLastCoinStakeSearchTime) {
            if (pwallet->CreateCoinStake(*pwallet, pblock->nBits, nSearchTime - nLastCoinStakeSearchTime, txCoinbase, txCoinStake, nTxNewTime, fProofOfStake)) {
                pblock->nTime = nTxNewTime;
                pblock->vtx[0] = CTransaction(txCoinbase);
                pblock->vtx.push_back(CTransaction(txCoinStake));
                fStakeFound = true;
            }
            nLastCoinStakeSearchTime = nSearchTime;
        }
        if (fDebug && fStakeFound)
            LogPrintf("%s(): stake found\n", __func__);

        if (!fStakeFound)
            return NULL;
    } else
        pblock->nTime = GetAdjustedTime();

    // Largest block you're willing to create:
    unsigned int nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE);

    // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity:
    unsigned int nBlockMaxSizeNetwork = MAX_BLOCK_SIZE;
    nBlockMaxSize = std::max((unsigned int)1000, std::min((nBlockMaxSizeNetwork - 1000), nBlockMaxSize));

    // How much of the block should be dedicated to high-priority transactions,
    // included regardless of the fees they pay
    unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE);
    nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize);

    // Minimum block size you want to create; block will be filled with free transactions
    // until there are no more or the block reaches this size:
    unsigned int nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE);
    nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);

    // Collect memory pool transactions into the block
    CAmount nFees = 0;

    {
        LOCK2(cs_main, mempool.cs);

        CBlockIndex* pindexPrev = chainActive.Tip();
        const int nHeight = pindexPrev->nHeight + 1;
        CCoinsViewCache view(pcoinsTip);

        // Priority order to process transactions
        list<COrphan> vOrphan; // list memory doesn't move
        map<uint256, vector<COrphan*> > mapDependers;
        bool fPrintPriority = GetBoolArg("-printpriority", false);

        // This vector will be sorted into a priority queue:
        vector<TxPriority> vecPriority;
        vecPriority.reserve(mempool.mapTx.size());
        for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin();
             mi != mempool.mapTx.end(); ++mi) {
            const CTransaction& tx = mi->GetTx();
            if (tx.IsCoinBase() || tx.IsCoinStake() || !IsFinalTx(tx, nHeight)) {
                continue;
            }

            COrphan* porphan = NULL;
            double dPriority = 0;
            CAmount nTotalIn = 0;
            bool fMissingInputs = false;
            uint256 txid = tx.GetHash();
            for (const CTxIn& txin : tx.vin) {
                // Read prev transaction
                if (!view.HaveCoins(txin.prevout.hash)) {
                    // This should never happen; all transactions in the memory
                    // pool should connect to either transactions in the chain
                    // or other transactions in the memory pool.
                    if (!mempool.mapTx.count(txin.prevout.hash)) {
                        fMissingInputs = true;
                        if (porphan)
                            vOrphan.pop_back();
                        break;
                    }

                    // Has to wait for dependencies
                    if (!porphan) {
                        // Use list for automatic deletion
                        vOrphan.push_back(COrphan(&tx));
                        porphan = &vOrphan.back();
                    }
                    mapDependers[txin.prevout.hash].push_back(porphan);
                    porphan->setDependsOn.insert(txin.prevout.hash);
                    nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue;
                    continue;
                }

                //Check for invalid/fraudulent inputs. They shouldn't make it through mempool, but check anyways.
                if (invalid_out::ContainsOutPoint(txin.prevout)) {
                    if (fDebug)
                        LogPrintf("%s : found invalid input %s in tx %s", __func__, txin.prevout.ToString(), tx.GetHash().ToString());

                    fMissingInputs = true;
                    break;
                }

                const CCoins* coins = view.AccessCoins(txin.prevout.hash);
                assert(coins);

                CAmount nValueIn = coins->vout[txin.prevout.n].nValue;
                nTotalIn += nValueIn;

                int nConf = nHeight - coins->nHeight;

                dPriority = double_safe_addition(dPriority, ((double)nValueIn * nConf));
            }
            if (fMissingInputs) continue;

            // Priority is sum(valuein * age) / modified_txsize
            unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
            dPriority = tx.ComputePriority(dPriority, nTxSize);

            uint256 hash = tx.GetHash();
            mempool.ApplyDeltas(hash, dPriority, nTotalIn);

            CFeeRate feeRate(nTotalIn - tx.GetValueOut(), nTxSize);

            if (porphan) {
                porphan->dPriority = dPriority;
                porphan->feeRate = feeRate;
            } else
                vecPriority.push_back(TxPriority(dPriority, feeRate, &mi->GetTx()));
        }

        // Collect transactions into block
        uint64_t nBlockSize = 1000;
        uint64_t nBlockTx = 0;
        int nBlockSigOps = 100;
        bool fSortedByFee = (nBlockPrioritySize <= 0);

        TxPriorityCompare comparer(fSortedByFee);
        std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);

        while (!vecPriority.empty()) {
            // Take highest priority transaction off the priority queue:
            double dPriority = vecPriority.front().get<0>();
            CFeeRate feeRate = vecPriority.front().get<1>();
            const CTransaction& tx = *(vecPriority.front().get<2>());

            std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
            vecPriority.pop_back();

            // Size limits
            unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
            if (nBlockSize + nTxSize >= nBlockMaxSize)
                continue;

            // Legacy limits on sigOps:
            unsigned int nMaxBlockSigOps = MAX_BLOCK_SIGOPS_CURRENT;
            unsigned int nTxSigOps = GetLegacySigOpCount(tx);
            if (nBlockSigOps + nTxSigOps >= nMaxBlockSigOps)
                continue;

            // Skip free transactions if we're past the minimum block size:
            const uint256& hash = tx.GetHash();
            double dPriorityDelta = 0;
            CAmount nFeeDelta = 0;
            mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
            if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < ::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
                continue;

            // Prioritise by fee once past the priority size or we run out of high-priority
            // transactions:
            if (!fSortedByFee && ((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority))) {
                fSortedByFee = true;
                comparer = TxPriorityCompare(fSortedByFee);
                std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
            }

            if (!view.HaveInputs(tx))
                continue;

            CAmount nTxFees = view.GetValueIn(tx) - tx.GetValueOut();

            nTxSigOps += GetP2SHSigOpCount(tx, view);
            if (nBlockSigOps + nTxSigOps >= nMaxBlockSigOps)
                continue;

            // Note that flags: we don't want to set mempool/IsStandard()
            // policy here, but we still have to ensure that the block we
            // create only contains transactions that are valid in new blocks.
            CValidationState state;
            if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true))
                continue;

            CTxUndo txundo;
            UpdateCoins(tx, state, view, txundo, nHeight);

            // Added
            pblock->vtx.push_back(tx);
            pblocktemplate->vTxFees.push_back(nTxFees);
            pblocktemplate->vTxSigOps.push_back(nTxSigOps);
            nBlockSize += nTxSize;
            ++nBlockTx;
            nBlockSigOps += nTxSigOps;
            nFees += nTxFees;

            if (fPrintPriority && fDebug)
                LogPrintf("priority %.1f fee %s txid %s\n", dPriority, feeRate.ToString(), tx.GetHash().ToString());

            // Add transactions that depend on this one to the priority queue
            if (mapDependers.count(hash)) {
                BOOST_FOREACH (COrphan* porphan, mapDependers[hash]) {
                    if (!porphan->setDependsOn.empty()) {
                        porphan->setDependsOn.erase(hash);
                        if (porphan->setDependsOn.empty()) {
                            vecPriority.push_back(TxPriority(porphan->dPriority, porphan->feeRate, porphan->ptx));
                            std::push_heap(vecPriority.begin(), vecPriority.end(), comparer);
                        }
                    }
                }
            }
        }

        nLastBlockTx = nBlockTx;
        nLastBlockSize = nBlockSize;
        // Compute final coinbase transaction.
        pblocktemplate->vTxFees[0] = -nFees;

        if (!fProofOfStake) {
            pblock->vtx[0] = txNew;
            pblock->vtx[0].vin[0].scriptSig = CScript() << nHeight << OP_0;
            pblock->vtx[0].vout[0].nValue += nFees;
            UpdateTime(pblock, pindexPrev, fProofOfStake);
        } else
            pblock->vtx[0].vout[1].nValue += nFees;

        // Fill in header
        pblock->hashPrevBlock = pindexPrev->GetBlockHash();
        pblock->nNonce = 0;

        pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);

        CValidationState state;
        if (!TestBlockValidity(state, *pblock, pindexPrev, false, false)) {
            mempool.clear();
            return NULL;
        }
        if (fDebug)
            LogPrintf("CreateNewBlock() : Block is VALID !!! \n");
    }

    return pblocktemplate.release();
}

inline CMutableTransaction CreateCoinbaseTransaction_Legacy(const CScript& scriptPubKeyIn, CAmount nFees, const int nHeight, bool fProofOfStake)
{
    // Create and Compute final coinbase transaction.
    CAmount reward = nFees + GetBlockValue(chainActive.Tip()->nHeight + 1);
    CAmount devsubsidy = reward * 0.1;

    CMutableTransaction txNew;
    txNew.vin.resize(1);
    txNew.vin[0].prevout.SetNull();
    txNew.vin[0].scriptSig = CScript() << nHeight << OP_0;
    txNew.nTime = GetAdjustedTime();
    txNew.SetVersion(1);

    if (fProofOfStake) {
        txNew.vout.resize(1);
        txNew.vout[0].SetEmpty();

        return txNew;
    } else {
        txNew.vout.resize(2);
        txNew.vout[0].nValue = reward - devsubsidy;
        txNew.vout[0].scriptPubKey = scriptPubKeyIn;
        txNew.vout[1].nValue = devsubsidy;
        txNew.vout[1].scriptPubKey = CScript() << ParseHex(Params().GetDevFundPubKey()) << OP_CHECKSIG;
    }

    return txNew;
}

CBlockTemplate* CreateNewBlock_Legacy(const CChainParams& chainparams, const CScript& scriptPubKeyIn, CWallet* pwallet, bool fProofOfStake)
{
    // Create new block
    unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate(CBlockHeader::CURRENT_VERSION));
    if (!pblocktemplate.get())
        return NULL;
    CBlock* pblock = &pblocktemplate->block; // pointer for convenience

    // Add dummy coinbase tx as first transaction
    pblock->vtx.push_back(CTransaction());
    pblocktemplate->vTxFees.push_back(-1);   // updated at end
    pblocktemplate->vTxSigOps.push_back(-1); // updated at end

    // Largest block you're willing to create:
    unsigned int nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE);
    // Limit to between 1K and MAX_BLOCK_SIZE-1K for sanity:
    nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE_LEGACY - 1000), nBlockMaxSize));

    // How much of the block should be dedicated to high-priority transactions,
    // included regardless of the fees they pay
    unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE);
    nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize);

    // Minimum block size you want to create; block will be filled with free transactions
    // until there are no more or the block reaches this size:
    unsigned int nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE);
    nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);

    // Collect memory pool transactions into the block
    CTxMemPool::setEntries inBlock;
    CTxMemPool::setEntries waitSet;

    // This vector will be sorted into a priority queue:
    vector<TxCoinAgePriority> vecPriority;
    TxCoinAgePriorityCompare pricomparer;
    std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash> waitPriMap;
    typedef std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash>::iterator waitPriIter;
    double actualPriority = -1;

    std::priority_queue<CTxMemPool::txiter, std::vector<CTxMemPool::txiter>, ScoreCompare> clearedTxs;
    bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY_LEGACY);
    uint64_t nBlockSize = 1000;
    uint64_t nBlockTx = 0;
    unsigned int nBlockSigOps = 100;
    int lastFewTxs = 0;
    CAmount nFees = 0;

    {
        LOCK2(cs_main, mempool.cs);
        CBlockIndex* pindexPrev = chainActive.Tip();
        const int nHeight = pindexPrev->nHeight + 1;
        pblock->nTime = GetAdjustedTime();
        const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();

        // Setting the first bit, fork preparation and setting the version as 1
        pblock->nVersion = CBlockHeader::CURRENT_VERSION; //ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
        // -regtest only: allow overriding block.nVersion with
        // -blockversion=N to test forking scenarios
        if (chainparams.ShouldMineBlocksOnDemand())
            pblock->nVersion = GetArg("-blockversion", pblock->nVersion);

        int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) ? nMedianTimePast : pblock->GetBlockTime();

        bool fPriorityBlock = nBlockPrioritySize > 0;
        if (fPriorityBlock) {
            vecPriority.reserve(mempool.mapTx.size());
            for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin();
                 mi != mempool.mapTx.end(); ++mi) {
                double dPriority = mi->GetPriority(nHeight);
                CAmount dummy;
                mempool.ApplyDeltas(mi->GetTx().GetHash(), dPriority, dummy);
                vecPriority.push_back(TxCoinAgePriority(dPriority, mi));
            }
            std::make_heap(vecPriority.begin(), vecPriority.end(), pricomparer);
        }

        CTxMemPool::indexed_transaction_set::nth_index<3>::type::iterator mi = mempool.mapTx.get<3>().begin();
        CTxMemPool::txiter iter;

        while (mi != mempool.mapTx.get<3>().end() || !clearedTxs.empty()) {
            bool priorityTx = false;
            if (fPriorityBlock && !vecPriority.empty()) { // add a tx from priority queue to fill the blockprioritysize
                priorityTx = true;
                iter = vecPriority.front().second;
                actualPriority = vecPriority.front().first;
                std::pop_heap(vecPriority.begin(), vecPriority.end(), pricomparer);
                vecPriority.pop_back();
            } else if (clearedTxs.empty()) { // add tx with next highest score
                iter = mempool.mapTx.project<0>(mi);
                mi++;
            } else { // try to add a previously postponed child tx
                iter = clearedTxs.top();
                clearedTxs.pop();
            }

            if (inBlock.count(iter))
                continue; // could have been added to the priorityBlock

            const CTransaction& tx = iter->GetTx();

            bool fOrphan = false;
            BOOST_FOREACH (CTxMemPool::txiter parent, mempool.GetMemPoolParents(iter)) {
                if (!inBlock.count(parent)) {
                    fOrphan = true;
                    break;
                }
            }
            if (fOrphan) {
                if (priorityTx)
                    waitPriMap.insert(std::make_pair(iter, actualPriority));
                else
                    waitSet.insert(iter);
                continue;
            }

            unsigned int nTxSize = iter->GetTxSize();
            if (fPriorityBlock &&
                (nBlockSize + nTxSize >= nBlockPrioritySize || !AllowFree(actualPriority))) {
                fPriorityBlock = false;
                waitPriMap.clear();
            }
            if (!priorityTx &&
                (iter->GetModifiedFee() < ::minRelayTxFee.GetFee(nTxSize) && nBlockSize >= nBlockMinSize)) {
                break;
            }
            if (nBlockSize + nTxSize >= nBlockMaxSize) {
                if (nBlockSize > nBlockMaxSize - 100 || lastFewTxs > 50) {
                    break;
                }
                // Once we're within 1000 bytes of a full block, only look at 50 more txs
                // to try to fill the remaining space.
                if (nBlockSize > nBlockMaxSize - 1000) {
                    lastFewTxs++;
                }
                continue;
            }

            if (tx.IsCoinStake() || !IsFinalTx(tx, nHeight, nLockTimeCutoff) || pblock->GetBlockTime() < (int64_t)tx.nTime)
                continue;

            unsigned int nTxSigOps = iter->GetSigOpCount();
            if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS_LEGACY) {
                if (nBlockSigOps > MAX_BLOCK_SIGOPS_LEGACY - 2) {
                    break;
                }
                continue;
            }

            CAmount nTxFees = iter->GetFee();
            // Added
            pblock->vtx.push_back(tx);
            pblocktemplate->vTxFees.push_back(nTxFees);
            pblocktemplate->vTxSigOps.push_back(nTxSigOps);
            nBlockSize += nTxSize;
            ++nBlockTx;
            nBlockSigOps += nTxSigOps;
            nFees += nTxFees;

            if (fPrintPriority) {
                double dPriority = iter->GetPriority(nHeight);
                CAmount dummy;
                mempool.ApplyDeltas(tx.GetHash(), dPriority, dummy);
                if (fDebug)
                    LogPrintf("priority %.1f fee %s txid %s\n", dPriority, CFeeRate(iter->GetModifiedFee(), nTxSize).ToString(), tx.GetHash().ToString());
            }

            inBlock.insert(iter);

            // Add transactions that depend on this one to the priority queue
            BOOST_FOREACH (CTxMemPool::txiter child, mempool.GetMemPoolChildren(iter)) {
                if (fPriorityBlock) {
                    waitPriIter wpiter = waitPriMap.find(child);
                    if (wpiter != waitPriMap.end()) {
                        vecPriority.push_back(TxCoinAgePriority(wpiter->second, child));
                        std::push_heap(vecPriority.begin(), vecPriority.end(), pricomparer);
                        waitPriMap.erase(wpiter);
                    }
                } else {
                    if (waitSet.count(child)) {
                        clearedTxs.push(child);
                        waitSet.erase(child);
                    }
                }
            }
        }
        nLastBlockTx = nBlockTx;
        nLastBlockSize = nBlockSize;

        if (fDebug)
            LogPrintf("CreateNewBlock_Legacy(): total size %u txs: %u fees: %ld sigops %d\n", nBlockSize, nBlockTx, nFees, nBlockSigOps);

        pblock->vtx[0] = CreateCoinbaseTransaction_Legacy(scriptPubKeyIn, nFees, nHeight, fProofOfStake);

        //Make payee
        if (!fProofOfStake && pblock->vtx[0].vout.size() > 1) {
            int size = pblock->vtx[0].vout.size();
            pblock->payee = pblock->vtx[0].vout[size - 1].scriptPubKey;
        }

        pblocktemplate->vTxFees[0] = -nFees;

        // Fill in header
        pblock->hashPrevBlock = pindexPrev->GetBlockHash();
        if (!fProofOfStake)
            UpdateTime(pblock, pindexPrev, fProofOfStake);

        pblock->nBits = GetNextWorkRequired_Legacy(pindexPrev, pblock, fProofOfStake);
        pblock->nNonce = 0;

        pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);

        CValidationState state;
        if (!fProofOfStake && !TestBlockValidity_Legacy(state, chainparams, *pblock, pindexPrev, false, false)) {
            throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage_Legacy(state)));
        }
    }
    return pblocktemplate.release();
}

void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
{
    // Update nExtraNonce
    static uint256 hashPrevBlock;
    if (hashPrevBlock != pblock->hashPrevBlock) {
        nExtraNonce = 0;
        hashPrevBlock = pblock->hashPrevBlock;
    }
    ++nExtraNonce;
    unsigned int nHeight = pindexPrev->nHeight + 1; // Height first in coinbase required for block.version=2
    CMutableTransaction txCoinbase(pblock->vtx[0]);
    txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS;
    assert(txCoinbase.vin[0].scriptSig.size() <= 100);

    pblock->vtx[0] = txCoinbase;
    pblock->hashMerkleRoot = pblock->BuildMerkleTree();
}

#ifdef ENABLE_WALLET
//////////////////////////////////////////////////////////////////////////////
//
// Internal miner
//
double dHashesPerMin = 0.0;
int64_t nHPSTimerStart = 0;

bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
{
    // Found a solution
    {
        LOCK(cs_main);
        if (pblock->hashPrevBlock != chainActive.Tip()->GetBlockHash())
            return error("KOREMiner : generated block is stale");
    }

    // Remove key from key pool
    reservekey.KeepKey();

    // Track how many getdata requests this block gets
    {
        LOCK(wallet.cs_wallet);
        wallet.mapRequestCount[pblock->GetHash()] = 0;
    }

    // Inform about the new block
    GetMainSignals().BlockFound(pblock->GetHash());

    // Process this block the same as if we had receiveMinerd it from another node
    CValidationState state;
    if (!ProcessNewBlock(state, NULL, pblock)) {
        return error("KOREMiner : ProcessNewBlock, block not accepted");
    }

    for (CNode* node : vNodes) {
        node->PushInventory(CInv(MSG_BLOCK, pblock->GetHash()));
    }

    return true;
}

bool ProcessBlockFound_Legacy(const CBlock* pblock, const CChainParams& chainparams)
{
    CValidationState state;

    // Found a solution
    {
        LOCK(cs_main);
        if (pblock->hashPrevBlock != chainActive.Tip()->GetBlockHash())
            return error("ProcessBlockFound(): generated/staked block is stale");
    }

    if (fDebug)
        LogPrintf("%s \n ", pblock->ToString());

    // verify hash target and signature of coinstake tx
    if (pblock->IsProofOfStake() && !CheckProofOfStake_Legacy(mapBlockIndex[pblock->hashPrevBlock], pblock->vtx[1], pblock->nBits, state))
        return false;

    if (fDebug)
        LogPrintf("%s %s\n", pblock->IsProofOfStake() ? "Stake " : "Mined ", pblock->IsProofOfStake() ? FormatMoney(pblock->vtx[1].GetValueOut()) : FormatMoney(pblock->vtx[0].GetValueOut()));

    // Inform about the new block
    GetMainSignals().BlockFound(pblock->GetHash());

    // Process this block the same as if we had received it from another node
    if (!ProcessNewBlock_Legacy(state, chainparams, NULL, pblock, true, NULL))
        return error("KoreMiner: ProcessNewBlock, block not accepted");

    return true;
}

// attempt to generate suitable proof-of-stake
bool SignBlock_Legacy(CWallet* pwallet, CBlock* pblock)
{
    // if we are trying to sign something except proof-of-stake block template
    if (!pblock->vtx[0].vout[0].IsEmpty()) {
        if (fDebug)
            LogPrintf("something except proof-of-stake block\n");
        return false;
    }

    // if we are trying to sign a complete proof-of-stake block
    if (pblock->IsProofOfStake()) {
        if (fDebug)
            LogPrintf("trying to sign a complete proof-of-stake block\n");

        return true;
    }

    static int64_t nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp

    CKey key;
    CMutableTransaction txCoinStake;
    txCoinStake.nTime = GetAdjustedTime();
    txCoinStake.nTime &= ~15;
    CAmount nFees = 0;

    int64_t nSearchTime = txCoinStake.nTime; // search to current time

    if (nSearchTime >= nLastCoinStakeSearchTime) {
        int64_t nSearchInterval = 1;
        if (pwallet->CreateCoinStake_Legacy(*pwallet, pblock, nSearchInterval, nFees, txCoinStake, key)) {
            // we have to make sure that we have no future timestamps in our transactions set
            for (vector<CTransaction>::iterator it = pblock->vtx.begin(); it != pblock->vtx.end();)
                if (it->nTime > pblock->nTime) {
                    it = pblock->vtx.erase(it);
                } else {
                    ++it;
                }

            pblock->vtx.insert(pblock->vtx.begin() + 1, txCoinStake);
            pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);

            // append a signature to our block
            return key.Sign(pblock->GetHash(), pblock->vchBlockSig);
        }
        nLastCoinStakeSearchTime = nSearchTime;
    }
    return false;
}

void KoreMinter(CWallet* pwallet)
{
    LogPrintf("KORE Minter started\n");
    SetThreadPriority(THREAD_PRIORITY_LOWEST);
    RenameThread("kore-minter");

    // Each thread has its own key and counter
    CReserveKey reservekey(pwallet);
    unsigned int nExtraNonce = 0;
    bool fMintableCoins = false;
    int nMintableLastCheck = 0;

    while (!ShutdownRequested()) {
        boost::this_thread::interruption_point();

        while (IsInitialBlockDownload())
            MilliSleep(2 * 60 * 1000);

        //control the amount of times the client will check for mintable coins
        if ((GetTime() - nMintableLastCheck > Params().GetTargetSpacing())) {
            nMintableLastCheck = GetTime();
            fMintableCoins = pwallet->MintableCoins();
        }

        while (vNodes.size() < 3 || pwallet->IsLocked() || !fMintableCoins || (pwallet->GetBalance() - nReserveBalance) < MINIMUM_STAKE_VALUE) {
            if (fDebug) {
                LogPrintf("%s(): still unable to stake.\n", __func__);
                if (vNodes.size() < 3)
                    LogPrintf("\tThere are no sufficient nodes connected;\n");
                if (pwallet->IsLocked())
                    LogPrintf("\tThe wallet is locked;\n");
                if (!fMintableCoins)
                    LogPrintf("\tThere are no mintable coins;\n");
                if (pwallet->GetBalance() - nReserveBalance < MINIMUM_STAKE_VALUE)
                    LogPrintf("\tAvailable balance to stake is less than minimun stake value;\n");
            }

            MilliSleep(5000);
            boost::this_thread::interruption_point();

            // Do a separate 1 minute check here to ensure fMintableCoins is updated
            if (!fMintableCoins && GetTime() - nMintableLastCheck > Params().GetTargetSpacing()) {
                nMintableLastCheck = GetTime();
                fMintableCoins = pwallet->MintableCoins();
            }
        }

        if (mapHashedBlocks.count(chainActive.Tip()->nHeight)) //search our map of hashed blocks, see if bestblock has been hashed yet
        {
            if (GetTime() - mapHashedBlocks[chainActive.Tip()->nHeight] < Params().GetTargetSpacingForStake() / 2) // wait half of the nHashDrift
            {
                MilliSleep(5000);
                boost::this_thread::interruption_point();
            }
        }

        if (nChainHeight < GetBestPeerHeight()) {
            MilliSleep(5000);
            continue;
        }

        unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey, pwallet, true));
        if (!pblocktemplate.get())
            continue;

        CBlock* pblock = &pblocktemplate->block;
        CBlockIndex* pindexPrev = chainActive.Tip();
        IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);

        LogPrintf("%s(): proof-of-stake block found %s \n", __func__, pblock->GetHash().ToString().c_str());

        if (!SignBlock(*pblock, *pwallet)) {
            LogPrintf("%s(): Signing new block with UTXO key failed \n", __func__);
            MilliSleep(5000);
            continue;
        }

        LogPrintf("%s(): proof-of-stake block was signed %s \n", __func__, pblock->GetHash().ToString().c_str());

        SetThreadPriority(THREAD_PRIORITY_NORMAL);
        ProcessBlockFound(pblock, *pwallet, reservekey);
        SetThreadPriority(THREAD_PRIORITY_LOWEST);
        MilliSleep(Params().GetTargetSpacingForStake() * 1000);
        continue;
    }
}

void KoreMiner()
{
    if (fDebug)
        LogPrintf("KoreMiner started\n");

    const CChainParams& chainparams = Params();
    SetThreadPriority(THREAD_PRIORITY_LOWEST);
    RenameThread("kore-pow");

    unsigned int nExtraNonce = 0;

    boost::shared_ptr<CReserveScript> coinbaseScript;
    GetMainSignals().ScriptForMining(coinbaseScript);

    try {
        // Throw an error if no script was provided.  This can happen
        // due to some internal error but also if the keypool is empty.
        // In the latter case, already the pointer is NULL.
        if (!coinbaseScript || coinbaseScript->reserveScript.empty()) {
            if (fDebug)
                LogPrintf("No coinbase script available (mining requires a wallet)\n");
            throw std::runtime_error("No coinbase script available (mining requires a wallet)");
        }

        // This thread should exit, if it has reached last
        while (!ShutdownRequested() && chainActive.Tip()->nHeight <= Params().GetLastPoWBlock()) {
            if (chainparams.DoesMiningRequiresPeers()) {
                // Busy-wait for the network to come online so we don't waste time mining
                // on an obsolete chain. In regtest mode we expect to fly solo.
                do {
                    if (fDebug)
                        LogPrintf("KoreMiner is waiting for a Peer!!! \n");

                    bool fvNodesEmpty;
                    {
                        LOCK(cs_vNodes);
                        fvNodesEmpty = vNodes.empty();
                    }
                    if (!fvNodesEmpty && !IsInitialBlockDownload())
                        break;
                    MilliSleep(1000);
                } while (true);
            }

            // Create new block
            unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
            CBlockIndex* pindexPrev = chainActive.Tip();

            unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock_Legacy(chainparams, coinbaseScript->reserveScript, NULL, false));

            if (!pblocktemplate.get()) {
                LogPrintf("Error in KoreMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
                return;
            }

            CBlock* pblock = &pblocktemplate->block;
            IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);

            if (fDebug)
                LogPrintf("KoreMiner Running with %u transactions in block (%u bytes)\n", pblock->vtx.size(), ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));

            // Search
            int64_t nStart = GetTime();
            arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
            uint256 testHash;

            for (;;) {
                unsigned int nHashesDone = 0;
                unsigned int nNonceFound = (unsigned int)-1;
                if (fDebug)
                    LogPrintf("KoreMiner Looking for a Hash Solution \n");

                for (int i = 0; i < 1; i++) {
                    pblock->nNonce = pblock->nNonce + 1;
                    testHash = pblock->CalculateBestBirthdayHash();
                    nHashesDone++;
                    if (fDebug) {
                        LogPrintf("KoreMiner testHash %s\n", testHash.ToString().c_str());
                        LogPrintf("KoreMiner Hash Target %s\n", hashTarget.ToString().c_str());
                    }

                    if (UintToArith256(testHash) < hashTarget) {
                        // Found a solution
                        nNonceFound = pblock->nNonce;
                        if (fDebug) {
                            LogPrintf("KoreMiner Found Hash %s\n", testHash.ToString().c_str());
                            LogPrintf("KoreMiner hash2 %s\n", pblock->GetHash().ToString().c_str());
                        }
                        // Found a solution
                        assert(testHash == pblock->GetHash());
                        SetThreadPriority(THREAD_PRIORITY_NORMAL);
                        ProcessBlockFound_Legacy(pblock, chainparams);
                        SetThreadPriority(THREAD_PRIORITY_LOWEST);
                        MilliSleep(Params().GetTargetSpacing() * 1000);
                        break;
                    }
                }

                // Meter hashes/sec
                static int64_t nHashCounter;
                if (nHPSTimerStart == 0) {
                    nHPSTimerStart = GetTimeMillis();
                    nHashCounter = 0;
                } else
                    nHashCounter += nHashesDone;

                if (GetTimeMillis() - nHPSTimerStart > 4000 * 60) {
                    static CCriticalSection cs;
                    {
                        LOCK(cs);
                        if (GetTimeMillis() - nHPSTimerStart > 4000 * 60) {
                            dHashesPerMin = 1000.0 * nHashCounter * 60 / (GetTimeMillis() - nHPSTimerStart);
                            nHPSTimerStart = GetTimeMillis();
                            nHashCounter = 0;
                            static int64_t nLogTime;
                            if (GetTime() - nLogTime > 30 * 60) {
                                nLogTime = GetTime();
                                LogPrintf("hashmeter %6.0f khash/s\n", dHashesPerMin / 1000.0);
                            }
                        }
                    }
                }

                // Check for stop or if block needs to be rebuilt
                boost::this_thread::interruption_point();
                // Regtest mode doesn't require peers
                if (vNodes.empty() && Params().DoesMiningRequiresPeers())
                    break;
                if (nNonceFound >= 0xffff0000)
                    break;
                if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
                    break;
                if (pindexPrev != chainActive.Tip())
                    break;

                // Update nTime every few seconds
                UpdateTime(pblock, pindexPrev);
            }
        }
        if (fDebug)
            LogPrintf("Pow Period has ended, we need to exit this thread \n");

    } catch (const boost::thread_interrupted&) {
        LogPrintf("KoreMiner Exiting at block: %d", GetnHeight(chainActive.Tip()));
        throw;

    } catch (const std::runtime_error& e) {
        LogPrintf("KoreMiner runtime error: %s\n", e.what());
        LogPrintf("KoreMiner Runtime Error : %s Exiting at block: %d", e.what(), GetnHeight(chainActive.Tip()));
        return;
    }
    if (fDebug)
        LogPrintf("Exiting kore-pow at block: %d", GetnHeight(chainActive.Tip()));
}

// ppcoin: stake minter thread
void ThreadStakeMinter()
{
    boost::this_thread::interruption_point();
    if (fDebug)
        LogPrintf("StakeMinter started\n");

    CWallet* pwallet = pwalletMain;
    try {
        KoreMinter(pwallet);
        boost::this_thread::interruption_point();
    } catch (std::exception& e) {
        LogPrintf("StakeMinter() exception \n");
    } catch (...) {
        LogPrintf("StakeMinter() error \n");
    }
    LogPrintf("StakeMinter exiting,\n");
    StakingCoins(false);
}

void updateStaking2KoreConf( bool staking )
{
    bool isStaking = GetBoolArg("-staking", false);
    std::string stakingFlag = (staking ? "1" : "0");
    mapArgs["-staking"] = stakingFlag;

    std::string strReplace = strprintf("staking=%s", (isStaking ? "1" : "0"));
    std::string strNew = strprintf("staking=%s", stakingFlag);

    boost::filesystem::ifstream streamConfig(GetConfigFile());
    boost::filesystem::path pathConfig = GetConfigFile();
    if(!streamConfig || !boost::filesystem::exists(pathConfig))
        LogPrintf("Error opening files!");

    std::set<string> setOptions;
    setOptions.insert("*");

    std::ostringstream strTemp;

    bool isStakingAlreadyinKoreConf = false;

    for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) {
        std::string strKey = it->string_key;
        std::string strValue = it->value[0];
        if (strKey == "staking") {
            // Let's put the new value
            strTemp << strNew << std::endl;
            isStakingAlreadyinKoreConf = true;
            continue;
        }
        // the key is different, se lets keep its value
        strTemp << strKey << "=" << strValue << std::endl;
    }
    if (!isStakingAlreadyinKoreConf) {
        // our key is not in the file yet, so lets add it
        strTemp << strNew << std::endl;
    }

    std::ofstream newKoreConfig;
    newKoreConfig.open(pathConfig.string().c_str(),fstream::out);

    newKoreConfig << strTemp.str();

    newKoreConfig.flush();
}

void StakingCoins(bool fStaking)
{
    static boost::thread_group* stakingThreads = NULL;
    if (fDebug) {
        LogPrintf("---------------------------------------------------- \n");
        LogPrintf("StakingCoins: %s \n", fStaking ? "Enable" : "Disable");
        LogPrintf("---------------------------------------------------- \n");
    }

    if (stakingThreads != NULL) {
        stakingThreads->interrupt_all();
        delete stakingThreads;
        stakingThreads = NULL;
    }

    if (!fStaking) {
        mapArgs["-staking"] = "0";
        return;
    }

    stakingThreads = new boost::thread_group();
    stakingThreads->create_thread(boost::bind(&TraceThread<void (*)()>, "stakemint", &ThreadStakeMinter));
}

void static ThreadKoreMiner()
{
    boost::this_thread::interruption_point();
    if (fDebug)
        LogPrintf("ThreadKoreMiner started\n");
    try {
        KoreMiner();
        boost::this_thread::interruption_point();
    } catch (std::exception& e) {
        LogPrintf("ThreadKoreMiner( %c) exception", e.what());
    } catch (...) {
        LogPrintf("ThreadKoreMiner() exception");
    }
    GenerateKores(false, 0);
    LogPrintf("ThreadKoreMiner exiting\n");
}

void GenerateKores(bool fGenerate, int nThreads)
{
    LogPrintf("GenerateKores with %d threads\n", nThreads);

    static boost::thread_group* minerThreads = NULL;

    if (nThreads < 0) {
        // In regtest threads defaults to 1
        if (Params().GetDefaultMinerThreads())
            nThreads = Params().GetDefaultMinerThreads();
        else
            nThreads = boost::thread::hardware_concurrency();
    }

    if (minerThreads != NULL) {
        minerThreads->interrupt_all();
        delete minerThreads;
        minerThreads = NULL;
    }

    if (nThreads == 0 || !fGenerate) {
        mapArgs["-gen"] = "0";
        return;
    }

    minerThreads = new boost::thread_group();
    for (int i = 0; i < nThreads; i++) {
        minerThreads->create_thread(boost::bind(&TraceThread<void (*)()>, "miner", &ThreadKoreMiner));
    }
}

CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, CWallet* pwallet, bool fProofOfStake)
{
    CPubKey pubkey;
    if (!reservekey.GetReservedKey(pubkey))
        return NULL;

    CScript scriptPubKey = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
    return CreateNewBlock(scriptPubKey, pwallet, fProofOfStake);
}

#endif // ENABLE_WALLET


/**
* MIT License
*
* Copyright (c) 2020 Daniel Ricci {@literal <thedanny09@icloud.com>}
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#include "Game/Components/CollisionComponent.hpp"
#include "Game/Components/PaddleInputComponent.hpp"
#include "Game/Components/SoundComponent.hpp"
#include "Game/Components/TransformComponent.hpp"
#include "Game/Systems/MovementSystem.hpp"

#include <Eigen/Dense>
#include <SDL.h>

#include <cmath>
#include <iostream>

MovementSystem::MovementSystem(SDL_Window& window) {
    SDL_GetWindowSize(&window, &worldWidth, &worldHeight);
}

void MovementSystem::process(const std::list<GameObject*>& gameObjects) {
    this->gameObjects = gameObjects;
    for(GameObject* gameObject : gameObjects) {
        if(gameObject != nullptr) {
            PaddleObject* paddleObject = dynamic_cast<PaddleObject*>(gameObject);
            if(paddleObject != nullptr) {
                processPaddleMovement(paddleObject);
            }
            else {
                BallObject* ballObject = dynamic_cast<BallObject*>(gameObject);
                if(ballObject) {
                    processBallMovement(ballObject);
                }
           }
        }
    }
    this->gameObjects.empty();
}

void MovementSystem::processBallMovement(BallObject* ballObject) const {
    if(ballObject != nullptr) {
        TransformComponent* ballTransformComponent = ballObject->getTransform();
        if(ballTransformComponent->positionVector.y() <= 0 || ballTransformComponent->positionVector.y() + ballTransformComponent->dimensionVector.y() >= worldHeight) {
            ballTransformComponent->invertVelocityY();
            ballTransformComponent->applyVelocity();

            // Play the sound for hitting a wall
            ballObject->playWallHitSound();
        }
        else if(ballTransformComponent->positionVector.x() <= 0 || ballTransformComponent->positionVector.x() + ballTransformComponent->dimensionVector.x() >= worldWidth) {
            // Position the ball at the center of the arena
            // The direction of the ball should point towards the player that lost the point
            ballTransformComponent->positionVector.x() = worldWidth/2 - (ballTransformComponent->dimensionVector.x() / 2);
            ballTransformComponent->positionVector.y() = worldHeight/2 - (ballTransformComponent->dimensionVector.y() / 2);
            ballTransformComponent->velocityVector.x() = BallObject::INITIAL_VELOCITY_X * (ballTransformComponent->velocityVector.x() < 0 ? -1 : 1);
            ballTransformComponent->velocityVector.y() = BallObject::INITIAL_VELOCITY_Y;
        }
        else {
            ballTransformComponent->applyVelocity();
            SDL_Rect ballRect = ballObject->getTransform()->rectangle();
            for(GameObject* gameObject : gameObjects) {
                if(gameObject != ballObject && gameObject->getComponent<CollisionComponent>() != nullptr) {
                    SDL_Rect gameObjectRect = gameObject->getTransform()->rectangle();
                    if(gameObject->getComponent<CollisionComponent>()->isCollidedAABB(ballRect, gameObjectRect)) {
                        ballTransformComponent->undoVelocity();
                        ballTransformComponent->invertVelocityX();

                        // Calculate the position of where the ball hit the paddle
                        int sectionSize = gameObjectRect.h / PADDLE_SECTIONS;
                        int hitPosition = std::abs((ballTransformComponent->positionVector.y() + (ballTransformComponent->dimensionVector.y() / 2)) - gameObjectRect.y) / sectionSize;

                        // Note: Only an upper-limit is required. If the absolute value is no longer used to determine the hit position
                        //       then a proper lower-limit is required.
                        hitPosition = std::min(hitPosition, PADDLE_SECTIONS - 1);

                        switch(hitPosition) {
                            case 0:
                                ballTransformComponent->velocityVector.y() = -5;
                                break;
                            case 1:
                                ballTransformComponent->velocityVector.y() = -2;
                                break;
                            case 2:
                                ballTransformComponent->velocityVector.y() = 0;
                                break;
                            case 3:
                                ballTransformComponent->velocityVector.y() = 2;
                                break;
                            case 4:
                                ballTransformComponent->velocityVector.y() = 5;
                                break;
                            default:
                                std::cerr << "Could not calculate hit position for " << hitPosition << std::endl;
                                break;
                        }
                        ballTransformComponent->applyVelocity();

                        // Play the sound for hitting a paddle
                        ballObject->playPaddleHitSound();
                        break;
                    }
                }
            }
        }
    }
}

void MovementSystem::processPaddleMovement(PaddleObject* paddleObject) const {

    // Calculate the direction that the paddle should go in
    int direction = 0;
    PaddleInputComponent* paddleInputComponent = paddleObject->getComponent<PaddleInputComponent>();
    if(paddleInputComponent != nullptr && paddleInputComponent->enabled) {
        direction = paddleInputComponent->getDirection();
    }
    else {
        for(GameObject* gameObject : this->gameObjects) {
            if(dynamic_cast<BallObject*>(gameObject) != nullptr) {
                int ballPositionY = gameObject->getTransform()->positionVector.y() + (gameObject->getTransform()->dimensionVector.y() / 2);
                int paddlePositionY = paddleObject->getTransform()->positionVector.y() + (paddleObject->getTransform()->dimensionVector.y() / 2);
                if(ballPositionY < paddlePositionY) {
                    direction = -1;
                }
                else if(ballPositionY > paddlePositionY) {
                    direction = 1;
                }

                break;
            }
        }
    }

    // Apply the paddle movement
    if(direction != 0) {
        TransformComponent* transform = paddleObject->getTransform();
        Eigen::Vector2f velocity = transform->velocityVector;
        velocity.y() = std::abs(velocity.y()) * direction;

        int newPosition = velocity.y() + transform->positionVector.y();
        if(newPosition >= 0 && newPosition + transform->dimensionVector.y() <= worldHeight) {
            transform->velocityVector = velocity;
            transform->applyVelocity();
        }
    }
}


//          Copyright Gavin Band 2008 - 2012.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#ifndef RELATEDNESS_COMPONENT_PCA_LOADING_COMPUTER_HPP
#define RELATEDNESS_COMPONENT_PCA_LOADING_COMPUTER_HPP

#include <Eigen/Core>
#include "genfile/SNPDataSourceProcessor.hpp"
#include "components/RelatednessComponent/KinshipCoefficientManager.hpp"

struct PCALoadingComputer: public genfile::SNPDataSourceProcessor::Callback
{
public:
	typedef Eigen::MatrixXd Matrix ;
	typedef Eigen::VectorXd Vector ;
	typedef std::auto_ptr< PCALoadingComputer > UniquePtr ;
public:
	PCALoadingComputer( int number_of_loadings ) ;
	void set_UDUT( std::size_t number_of_snps, Matrix const& udut_decomposition ) ;
	void set_number_of_loadings( std::size_t n ) ;

	void begin_processing_snps( std::size_t number_of_samples, genfile::SNPDataSource::Metadata const& ) ;
	void processed_snp( genfile::VariantIdentifyingData const&, genfile::VariantDataReader& ) ;
	void end_processing_snps() ;

	typedef boost::function< genfile::VariantEntry ( std::size_t ) > GetNames ;
	typedef boost::signals2::signal< void( genfile::VariantIdentifyingData const&, double const, double const, Eigen::VectorXd const&, GetNames ) > ResultSignal ;
	typedef ResultSignal::slot_type ResultCallback ;

	void send_results_to( ResultCallback callback ) ;
	void send_results( genfile::VariantIdentifyingData const& snp, double const, double const, Eigen::VectorXd const& data, GetNames ) ;

	std::string get_metadata() const ;
private:
	Eigen::VectorXd m_D ;
	Eigen::VectorXd m_sqrt_D_inverse ;
	Eigen::MatrixXd m_U ;
	int const m_number_of_loadings ;
	int m_number_of_snps ;
	Eigen::RowVectorXd m_loading_vectors ;
	Eigen::VectorXd m_allele_frequencies ;
	Eigen::VectorXd m_genotype_calls ;
	Eigen::VectorXd m_non_missingness ;

	ResultSignal m_result_signal ;
} ;

#endif


//==================================================================================================
/**
  Copyright 2017 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_FUNCTION_SCALAR_REC_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_SCALAR_REC_HPP_INCLUDED

#include <boost/simd/function/definition/rec.hpp>
#include <boost/simd/arch/common/scalar/function/rec.hpp>

#if defined(BOOST_HW_SIMD_X86_OR_AMD_AVAILABLE)
#  if BOOST_HW_SIMD_X86_OR_AMD >= BOOST_HW_SIMD_X86_SSE_VERSION
#    include <boost/simd/arch/x86/sse1/scalar/function/rec.hpp>
#  endif
#endif

#endif


/*
* Software License Agreement (BSD License)
*
*  Copyright (c) 2012-2015, Matthias Luber, Timm Linder, Social Robotics Lab, University of Freiburg
*  All rights reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions are met:
*
*  * Redistributions of source code must retain the above copyright notice, this
*    list of conditions and the following disclaimer.
*  * Redistributions in binary form must reproduce the above copyright notice,
*    this list of conditions and the following disclaimer in the documentation
*    and/or other materials provided with the distribution.
*  * Neither the name of the copyright holder nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
*  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
*  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
*  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
*  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
*  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
*  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
*  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <ros/ros.h>
#include <ros/package.h>

#include <tf/transform_datatypes.h>

#include <svm.h>

#include <spencer_tracking_msgs/TrackedPersons.h>
#include <spencer_social_relation_msgs/SocialRelations.h>

#include <cmath>
#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH

using namespace spencer_tracking_msgs;
using namespace spencer_social_relation_msgs;

ros::Publisher g_socialRelationsPublisher;

struct svm_model* g_svmModel;
struct svm_node* g_svmNode;
double g_maxDistance, g_maxSpeedDifference, g_maxOrientationDifference, g_minSpeedToConsiderOrientation;

/// Puts angle alpha into the interval [min..min+2*pi[
double set_angle_to_range(double alpha, double min)
{
    while (alpha >= min + 2.0 * M_PI) {
        alpha -= 2.0 * M_PI;
    }
    while (alpha < min) {
        alpha += 2.0 * M_PI;
    }
    return alpha;
}

/// Determines the minimal difference dalpha = alpha1 - alpha2 between two angles alpha1 and alpha2
double diff_angle_unwrap(double alpha1, double alpha2)
{
    double delta;

    // normalize angles alpha1 and alpha2
    alpha1 = set_angle_to_range(alpha1, 0);
    alpha2 = set_angle_to_range(alpha2, 0);

    // take difference and unwrap
    delta = alpha1 - alpha2;
    if (alpha1 > alpha2) {
        while (delta > M_PI) {
            delta -= 2.0 * M_PI;
        }
    } else if (alpha2 > alpha1) {
        while (delta < -M_PI) {
            delta += 2.0 * M_PI;
        }
    }
    return delta;
}

/// Callback when new tracks have arrived. This is where all the magic happens.
void newTrackedPersonsReceived(const TrackedPersons::ConstPtr& trackedPersons)
{
    ROS_ASSERT_MSG(trackedPersons->header.frame_id == "odom" || trackedPersons->header.frame_id == "map", "Input tracks must be in ground-plane (x y) coordinates!");

    SocialRelations::Ptr socialRelations(new SocialRelations);
    socialRelations->header = trackedPersons->header;

    // TODO: Parallelize, but watch out for SVM node which is not thread-safe (maybe need to create a copy each time!?)
    const size_t trackCount = trackedPersons->tracks.size();
    for(size_t t1Index = 0; t1Index < trackCount; t1Index++)
    {
        for(size_t t2Index = t1Index + 1; t2Index < trackCount; t2Index++)
        {
            // Extract feature values for each pair of tracks
            const TrackedPerson& t1 = trackedPersons->tracks[t1Index];
            const TrackedPerson& t2 = trackedPersons->tracks[t2Index];

            const geometry_msgs::Point& pos1 = t1.pose.pose.position;
            const geometry_msgs::Point& pos2 = t2.pose.pose.position;

            const geometry_msgs::Vector3& v1 = t1.twist.twist.linear;
            const geometry_msgs::Vector3& v2 = t2.twist.twist.linear;

            const double speed1 = hypot(v1.x, v1.y);
            const double speed2 = hypot(v2.x, v2.y);

            double theta1 = 0.0;
            double theta2 = 0.0;

            if (speed1 >= g_minSpeedToConsiderOrientation) {
                theta1 = tf::getYaw(t1.pose.pose.orientation);
            }

            if (speed2 >= g_minSpeedToConsiderOrientation) {
                theta2 = tf::getYaw(t2.pose.pose.orientation);
            }

            // Calculate final feature values
            double distance = hypot(pos1.x - pos2.x, pos1.y - pos2.y);
            double deltaspeed = fabs(speed1 - speed2);
            double deltaangle = fabs(diff_angle_unwrap(theta1, theta2));

            double positiveRelationProbability, negativeRelationProbability;

            // Gating for large distance, very different velocities, or very different angle
            if (distance > g_maxDistance ||  deltaspeed > g_maxSpeedDifference  || deltaangle > g_maxOrientationDifference) {
                positiveRelationProbability = 0.1;
                negativeRelationProbability = 0.9;

            }
            else {
                // Prepare SVM classifier
                g_svmNode[0].value = distance;
                g_svmNode[1].value = deltaspeed;
                g_svmNode[2].value = deltaangle;

                // Run SVM classifier
                double probabilityEstimates[2];
                svm_predict_probability(g_svmModel, g_svmNode, probabilityEstimates);
                positiveRelationProbability = probabilityEstimates[0];
                negativeRelationProbability = probabilityEstimates[1];
            }

            // Store results for this pair of tracks
            SocialRelation socialRelation;
            socialRelation.type = SocialRelation::TYPE_SPATIAL;
            socialRelation.strength = positiveRelationProbability;
            socialRelation.track1_id = t1.track_id;
            socialRelation.track2_id = t2.track_id;

            socialRelations->elements.push_back(socialRelation);
        }
    }

    // Publish spatial relations
    g_socialRelationsPublisher.publish(socialRelations);
}

/// Main method, creates ROS subscribers and reads parameters
int main(int argc, char **argv)
{
    ros::init(argc, argv, "spatial_relations");
    ros::NodeHandle nodeHandle("");
    ros::NodeHandle privateHandle("~");

    // Create publishers and subscribers
    std::string inputTopic = nodeHandle.resolveName("/spencer/perception/tracked_persons");
    std::string outputTopic = nodeHandle.resolveName("/spencer/perception/spatial_relations");

    ros::Subscriber tracksSubscriber = nodeHandle.subscribe<TrackedPersons>(inputTopic, 3, &newTrackedPersonsReceived);
    g_socialRelationsPublisher = nodeHandle.advertise<SocialRelations>(outputTopic, 3);

    // Read parameters
    privateHandle.param<double>("max_distance", g_maxDistance, 3.0);
    privateHandle.param<double>("max_speed_difference", g_maxSpeedDifference, 1.0);
    privateHandle.param<double>("max_orientation_difference", g_maxOrientationDifference, M_PI_4);
    privateHandle.param<double>("min_speed_to_consider_orientation", g_minSpeedToConsiderOrientation, 0.1);

    // Initialize SVM
    std::string svmFilename;
    privateHandle.param<std::string>("svm_filename", svmFilename, ros::package::getPath(ROS_PACKAGE_NAME) + "/models/groups_probabilistic_small.model");

    if ((g_svmModel = svm_load_model(svmFilename.c_str())) == NULL) {
        ROS_FATAL("Failed to open SVM model %s!", svmFilename.c_str());
        std::terminate();
    }

    ROS_ASSERT(svm_check_probability_model(g_svmModel) != 0); // make sure the provided model is probabilistic

    g_svmNode = new svm_node[4];
    g_svmNode[0].index = 1; // libSVM indices are one-based
    g_svmNode[1].index = 2;
    g_svmNode[2].index = 3;
    g_svmNode[3].index = -1; // terminator

    // Run node, wait for callbacks
    ROS_INFO_STREAM("Subscribing to tracked persons on topic " << ros::names::remap(inputTopic) << " and publishing corresponding spatial relations on output topic " << ros::names::remap(outputTopic) << ".");
    ros::spin();

    // Clean up
    delete[] g_svmNode;
    svm_free_and_destroy_model(&g_svmModel);
}


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//         Copyright 2012 - 2014 MetaScale SAS
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_IEEE_FUNCTIONS_SIMD_COMMON_SUCCESSOR_HPP_INCLUDED
#define BOOST_SIMD_IEEE_FUNCTIONS_SIMD_COMMON_SUCCESSOR_HPP_INCLUDED

#include <boost/simd/ieee/functions/successor.hpp>
#include <boost/simd/include/functions/simd/is_not_equal.hpp>
#include <boost/simd/include/functions/simd/if_allbits_else.hpp>
#include <boost/simd/include/functions/simd/seladd.hpp>
#include <boost/simd/include/functions/simd/oneplus.hpp>
#include <boost/simd/include/functions/simd/adds.hpp>
#include <boost/simd/include/functions/simd/is_gez.hpp>
#include <boost/simd/include/functions/simd/is_nan.hpp>
#include <boost/simd/include/functions/simd/bitinteger.hpp>
#include <boost/simd/include/functions/simd/bitfloating.hpp>
#include <boost/simd/include/functions/simd/minus.hpp>
#include <boost/simd/include/functions/simd/plus.hpp>
#include <boost/simd/include/functions/simd/is_greater.hpp>
#include <boost/simd/include/functions/simd/if_else.hpp>
#include <boost/simd/include/constants/valmax.hpp>
#include <boost/simd/include/constants/one.hpp>
#include <boost/simd/operator/functions/details/assert_utils.hpp>
#include <boost/dispatch/attributes.hpp>
#include <boost/assert.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT          ( successor_, tag::cpu_
                                    , (A0)(X)
                                    , ((simd_<arithmetic_<A0>,X>))
                                    )
  {
    typedef A0 result_type;
    BOOST_FORCEINLINE BOOST_SIMD_FUNCTOR_CALL(1)
    {
      return seladd(neq(a0, Valmax<A0>()), a0, One<A0>());
    }
  };

  BOOST_DISPATCH_IMPLEMENT          ( successor_, tag::cpu_
                                    , (A0)(X)
                                    , ((simd_<floating_<A0>,X>))
                                    )
  {
    typedef A0 result_type;
    BOOST_FORCEINLINE BOOST_SIMD_FUNCTOR_CALL(1)
    {
      return if_nan_else(is_nan(a0), bitfloating(oneplus(bitinteger(a0))));
    }
  };

  BOOST_DISPATCH_IMPLEMENT          ( successor_, tag::cpu_
                                    , (A0)(X)
                                    , ((simd_<integer_<A0>,X>))((simd_<integer_<A0>,X>))
                                    )
  {
    typedef A0 result_type;
    BOOST_FORCEINLINE BOOST_SIMD_FUNCTOR_CALL_REPEAT(2)
    {
      BOOST_ASSERT_MSG(assert_all(is_gez(a1)), "predecessor rank must be non negative");
      return if_else( boost::simd::gt(Valmax<A0>()-a1, a0),
                      a0+a1,
                      Valmax<A0>());
    }
  };

  BOOST_DISPATCH_IMPLEMENT          ( successor_, tag::cpu_
                                    , (A0)(A1)(X)
                                    , ((simd_<floating_<A0>,X>))((simd_<integer_<A1>,X>))
                                    )
  {
    typedef A0 result_type;
    BOOST_FORCEINLINE BOOST_SIMD_FUNCTOR_CALL(2)
    {
      BOOST_ASSERT_MSG(assert_all(is_gez(a1)), "predecessor rank must be non negative");
      return if_nan_else(is_nan(a0), bitfloating(adds(bitinteger(a0), a1)));
    }
  };
} } }

#endif


// Copyright (c) 2018, The LibertyChain Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
//    conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
//    of conditions and the following disclaimer in the documentation and/or other
//    materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
//    used to endorse or promote products derived from this software without specific
//    prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "gtest/gtest.h"

#include "crypto/crypto.h"
extern "C" {
#include "crypto/crypto-ops.h"
}
#include "crypto/hash.h"
#include <boost/algorithm/string.hpp>

static inline unsigned char *operator &(crypto::ec_point &point) {
    return &reinterpret_cast<unsigned char &>(point);
  }

static inline unsigned char *operator &(crypto::ec_scalar &scalar) {
    return &reinterpret_cast<unsigned char &>(scalar);
  }

TEST(tx_proof, prove_verify_v2)
{
    crypto::secret_key r;
    crypto::random32_unbiased(&r);

    // A = aG
    // B = bG
    crypto::secret_key a,b;
    crypto::public_key A,B;
    crypto::generate_keys(A, a, a, false);
    crypto::generate_keys(B, b, b, false);

    // R_B = rB
    crypto::public_key R_B;
    ge_p3 B_p3;
    ASSERT_EQ(ge_frombytes_vartime(&B_p3,&B), 0);
    ge_p2 R_B_p2;
    ge_scalarmult(&R_B_p2, &unwrap(r), &B_p3);
    ge_tobytes(&R_B, &R_B_p2);

    // R_G = rG
    crypto::public_key R_G;
    ASSERT_EQ(ge_frombytes_vartime(&B_p3,&B), 0);
    ge_p3 R_G_p3;
    ge_scalarmult_base(&R_G_p3, &unwrap(r));
    ge_p3_tobytes(&R_G, &R_G_p3);

    // D = rA
    crypto::public_key D;
    ge_p3 A_p3;
    ASSERT_EQ(ge_frombytes_vartime(&A_p3,&A), 0);
    ge_p2 D_p2;
    ge_scalarmult(&D_p2, &unwrap(r), &A_p3);
    ge_tobytes(&D, &D_p2);

    crypto::signature sig;

    // Message data
    crypto::hash prefix_hash;
    char data[] = "hash input";
    crypto::cn_fast_hash(data,sizeof(data)-1,prefix_hash);

    // Generate/verify valid v1 proof with standard address
    crypto::generate_tx_proof_v1(prefix_hash, R_G, A, boost::none, D, r, sig);
    ASSERT_TRUE(crypto::check_tx_proof(prefix_hash, R_G, A, boost::none, D, sig, 1));

    // Generate/verify valid v1 proof with subaddress
    crypto::generate_tx_proof_v1(prefix_hash, R_B, A, B, D, r, sig);
    ASSERT_TRUE(crypto::check_tx_proof(prefix_hash, R_B, A, B, D, sig, 1));

    // Generate/verify valid v2 proof with standard address
    crypto::generate_tx_proof(prefix_hash, R_G, A, boost::none, D, r, sig);
    ASSERT_TRUE(crypto::check_tx_proof(prefix_hash, R_G, A, boost::none, D, sig, 2));

    // Generate/verify valid v2 proof with subaddress
    crypto::generate_tx_proof(prefix_hash, R_B, A, B, D, r, sig);
    ASSERT_TRUE(crypto::check_tx_proof(prefix_hash, R_B, A, B, D, sig, 2));

    // Try to verify valid v2 proofs as v1 proof (bad)
    crypto::generate_tx_proof(prefix_hash, R_G, A, boost::none, D, r, sig);
    ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_G, A, boost::none, D, sig, 1));
    crypto::generate_tx_proof(prefix_hash, R_B, A, B, D, r, sig);
    ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_B, A, B, D, sig, 1));

    // Randomly-distributed test points
    crypto::secret_key evil_a, evil_b, evil_d, evil_r;
    crypto::public_key evil_A, evil_B, evil_D, evil_R;
    crypto::generate_keys(evil_A, evil_a, evil_a, false);
    crypto::generate_keys(evil_B, evil_b, evil_b, false);
    crypto::generate_keys(evil_D, evil_d, evil_d, false);
    crypto::generate_keys(evil_R, evil_r, evil_r, false);

    // Selectively choose bad point in v2 proof (bad)
    crypto::generate_tx_proof(prefix_hash, R_B, A, B, D, r, sig);
    ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, evil_R, A, B, D, sig, 2));
    ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_B, evil_A, B, D, sig, 2));
    ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_B, A, evil_B, D, sig, 2));
    ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_B, A, B, evil_D, sig, 2));

    // Try to verify valid v1 proofs as v2 proof (bad)
    crypto::generate_tx_proof_v1(prefix_hash, R_G, A, boost::none, D, r, sig);
    ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_G, A, boost::none, D, sig, 2));
    crypto::generate_tx_proof_v1(prefix_hash, R_B, A, B, D, r, sig);
    ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_B, A, B, D, sig, 2));
}


///////////////////////////////////////////////////////////////////////////////
// quant_compilers.hpp
//
//  Copyright 2004 Eric Niebler. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_XPRESSIVE_DETAIL_STATIC_PRODUCTIONS_QUANT_COMPILERS_HPP_EAN_10_04_2005
#define BOOST_XPRESSIVE_DETAIL_STATIC_PRODUCTIONS_QUANT_COMPILERS_HPP_EAN_10_04_2005

#include <limits.h>
#include <boost/xpressive/proto/proto.hpp>
#include <boost/xpressive/proto/compiler/branch.hpp>
#include <boost/xpressive/proto/compiler/transform.hpp>
#include <boost/xpressive/proto/compiler/conditional.hpp>
#include <boost/xpressive/detail/static/productions/quant_traits.hpp>
#include <boost/xpressive/detail/static/productions/quant_transforms.hpp>
#include <boost/xpressive/detail/static/productions/marker_transform.hpp>
#include <boost/xpressive/detail/static/productions/domain_tags.hpp>

namespace boost { namespace xpressive { namespace detail
{

    typedef proto::unary_op<epsilon_matcher, proto::noop_tag> nil_op;

    ///////////////////////////////////////////////////////////////////////////////
    // generic_quant_compiler
    template<bool Greedy, uint_t Min, uint_t Max>
    struct generic_quant_compiler
      : proto::conditional_compiler
        <
            use_simple_repeat_predicate
          , proto::branch_compiler<simple_repeat_branch<Greedy, Min, Max>, ind_tag>
          , proto::transform_compiler
            <
                proto::compose_transforms
                <
                    proto::arg_transform
                  , repeater_if_transform<Greedy, Min, Max>
                >
              , seq_tag
            >
        >
    {
    };

    // degenerate case, foo{0,0} becomes nil
    template<bool Greedy>
    struct generic_quant_compiler<Greedy, 0, 0>
      : proto::transform_compiler<proto::always_transform<nil_op>, seq_tag>
    {
    };

    // degenerate case, foo{1,1} becomes foo
    template<bool Greedy>
    struct generic_quant_compiler<Greedy, 1, 1>
      : proto::transform_compiler<proto::identity_transform, seq_tag>
    {
    };

    ///////////////////////////////////////////////////////////////////////////////
    // non_greedy_compiler
    struct non_greedy_compiler
    {
        template<typename Op, typename State, typename Visitor>
        struct apply
        {
            typedef typename proto::arg_type<Op>::type arg_type;

            // Did you apply operator- to something that wasn't a quantifier?
            BOOST_MPL_ASSERT((is_greedy_quant<arg_type>));

            typedef typename proto::tag_type<arg_type>::type tag_type;
            typedef generic_quant_compiler
            <
                false
              , min_type<tag_type>::type::value
              , max_type<tag_type>::type::value
            > compiler_type;

            typedef typename compiler_type::BOOST_NESTED_TEMPLATE apply
            <
                arg_type
              , State
              , Visitor
            >::type type;
        };

        template<typename Op, typename State, typename Visitor>
        static typename apply<Op, State, Visitor>::type
        call(Op const &op, State const &state, Visitor &visitor)
        {
            typedef typename apply<Op, State, Visitor>::compiler_type compiler_type;
            return compiler_type::call(proto::arg(op), state, visitor);
        }
    };

}}}

namespace boost { namespace proto
{

    // production for one or more quant
    template<>
    struct compiler<unary_plus_tag, xpressive::detail::seq_tag, void>
      : xpressive::detail::generic_quant_compiler<true, 1, UINT_MAX-1>
    {
    };

    // production for zero or more quant
    template<>
    struct compiler<unary_star_tag, xpressive::detail::seq_tag, void>
      : xpressive::detail::generic_quant_compiler<true, 0, UINT_MAX-1>
    {
    };

    // production for optional
    template<>
    struct compiler<logical_not_tag, xpressive::detail::seq_tag, void>
      : xpressive::detail::generic_quant_compiler<true, 0, 1>
    {
    };

    // production for generic quantifiers
    template<unsigned int Min, unsigned int Max>
    struct compiler<xpressive::detail::generic_quant_tag<Min, Max>, xpressive::detail::seq_tag, void>
      : xpressive::detail::generic_quant_compiler<true, Min, Max>
    {
    };

    // production for non-greedy quantifiers
    template<>
    struct compiler<unary_minus_tag, xpressive::detail::seq_tag, void>
      : xpressive::detail::non_greedy_compiler
    {
    };

}}

#endif


// SPDX-License-Identifier: Apache-2.0
//
// Copyright 2011-2017 Ryan Curtin (http://www.ratml.org/)
// Copyright 2017 National ICT Australia (NICTA)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------

#include <armadillo>

#include "catch.hpp"

using namespace arma;

TEST_CASE("fn_min_weird_operation")
  {
  mat a(10, 10);
  mat b(25, 10);
  a.randn();
  b.randn();

  mat output = a * b.t();

  uword real_min;
  uword operation_min;

  const double mval = output.min(real_min);
  const double other_mval = (a * b.t()).min(operation_min);

  REQUIRE( real_min == operation_min );
  REQUIRE( mval == Approx(other_mval) );
  }

TEST_CASE("fn_min_weird_sparse_operation")
  {
  sp_mat a(10, 10);
  sp_mat b(25, 10);
  a.sprandn(10, 10, 0.3);
  b.sprandn(25, 10, 0.3);

  sp_mat output = a * b.t();

  uword real_min;
  uword operation_min;

  const double mval = output.min(real_min);
  const double other_mval = (a * b.t()).min(operation_min);

  REQUIRE( real_min == operation_min );
  REQUIRE( mval == Approx(other_mval) );
  }

TEST_CASE("fn_min_sp_subview_test")
  {
  // We will assume subview.at() works and returns points within the bounds of
  // the matrix, so we just have to ensure the results are the same as
  // Mat.min()...
  for (size_t r = 50; r < 150; ++r)
    {
    sp_mat x;
    x.sprandn(r, r, 0.3);

    uword x_min;
    uword x_subview_min1;
    uword x_subview_min2;
    uword x_subview_min3;

    const double mval = x.min(x_min);
    const double mval1 = x.submat(0, 0, r - 1, r - 1).min(x_subview_min1);
    const double mval2 = x.cols(0, r - 1).min(x_subview_min2);
    const double mval3 = x.rows(0, r - 1).min(x_subview_min3);

    if (mval != 0.0)
      {
      REQUIRE( x_min == x_subview_min1 );
      REQUIRE( x_min == x_subview_min2 );
      REQUIRE( x_min == x_subview_min3 );

      REQUIRE( mval == Approx(mval1) );
      REQUIRE( mval == Approx(mval2) );
      REQUIRE( mval == Approx(mval3) );
      }
    }
  }

TEST_CASE("fn_min_spsubview_col_test")
  {
  for (size_t r = 10; r < 50; ++r)
    {
    sp_vec x;
    x.sprandn(r, 1, 0.3);

    uword x_min;
    uword x_subview_min1;
    uword x_subview_min2;

    const double mval = x.min(x_min);
    const double mval1 = x.submat(0, 0, r - 1, 0).min(x_subview_min1);
    const double mval2 = x.rows(0, r - 1).min(x_subview_min2);

    if (mval != 0.0)
      {
      REQUIRE( x_min == x_subview_min1 );
      REQUIRE( x_min == x_subview_min2 );

      REQUIRE( mval == Approx(mval1) );
      REQUIRE( mval == Approx(mval2) );
      }
    }
  }

TEST_CASE("fn_min_spsubview_row_min_test")
  {
  for (size_t r = 10; r < 50; ++r)
    {
    sp_rowvec x;
    x.sprandn(1, r, 0.3);

    uword x_min;
    uword x_subview_min1;
    uword x_subview_min2;

    const double mval = x.min(x_min);
    const double mval1 = x.submat(0, 0, 0, r - 1).min(x_subview_min1);
    const double mval2 = x.cols(0, r - 1).min(x_subview_min2);

    if (mval != 0.0)
      {
      REQUIRE( x_min == x_subview_min1 );
      REQUIRE( x_min == x_subview_min2 );

      REQUIRE( mval == Approx(mval1) );
      REQUIRE( mval == Approx(mval2) );
      }
    }
  }

TEST_CASE("fn_min_spincompletesubview_min_test")
  {
  for (size_t r = 50; r < 150; ++r)
    {
    sp_mat x;
    x.sprandn(r, r, 0.3);

    uword x_min;
    uword x_subview_min1;
    uword x_subview_min2;
    uword x_subview_min3;

    const double mval = x.min(x_min);
    const double mval1 = x.submat(1, 1, r - 2, r - 2).min(x_subview_min1);
    const double mval2 = x.cols(1, r - 2).min(x_subview_min2);
    const double mval3 = x.rows(1, r - 2).min(x_subview_min3);

    uword row, col;
    x.min(row, col);

    if (row != 0 && row != r - 1 && col != 0 && col != r - 1 && mval != 0.0)
      {
      uword srow, scol;

      srow = x_subview_min1 % (r - 2);
      scol = x_subview_min1 / (r - 2);
      REQUIRE( x_min == (srow + 1) + r * (scol + 1) );
      REQUIRE( x_min == x_subview_min2 + r );

      srow = x_subview_min3 % (r - 2);
      scol = x_subview_min3 / (r - 2);
      REQUIRE( x_min == (srow + 1) + r * scol );

      REQUIRE( mval == Approx(mval1) );
      REQUIRE( mval == Approx(mval2) );
      REQUIRE( mval == Approx(mval3) );
      }
    }
  }

TEST_CASE("fn_min_spincompletesubview_col_min_test")
  {
  for (size_t r = 10; r < 50; ++r)
    {
    sp_vec x;
    x.sprandu(r, 1, 0.3);

    uword x_min;
    uword x_subview_min1;
    uword x_subview_min2;

    const double mval = x.min(x_min);
    const double mval1 = x.submat(1, 0, r - 2, 0).min(x_subview_min1);
    const double mval2 = x.rows(1, r - 2).min(x_subview_min2);

    if (x_min != 0 && x_min != r - 1 && mval != 0.0)
      {
      REQUIRE( x_min == x_subview_min1 + 1 );
      REQUIRE( x_min == x_subview_min2 + 1 );

      REQUIRE( mval == Approx(mval1) );
      REQUIRE( mval == Approx(mval2) );
      }
    }
  }

TEST_CASE("fn_min_spincompletesubview_row_min_test")
  {
  for (size_t r = 10; r < 50; ++r)
    {
    sp_rowvec x;
    x.sprandn(1, r, 0.3);

    uword x_min;
    uword x_subview_min1;
    uword x_subview_min2;

    const double mval = x.min(x_min);
    const double mval1 = x.submat(0, 1, 0, r - 2).min(x_subview_min1);
    const double mval2 = x.cols(1, r - 2).min(x_subview_min2);

    if (mval != 0.0 && x_min != 0 && x_min != r - 1)
      {
      REQUIRE( x_min == x_subview_min1 + 1 );
      REQUIRE( x_min == x_subview_min2 + 1 );

      REQUIRE( mval == Approx(mval1) );
      REQUIRE( mval == Approx(mval2) );
      }
    }
  }

TEST_CASE("fn_min_sp_cx_subview_min_test")
  {
  // We will assume subview.at() works and returns points within the bounds of
  // the matrix, so we just have to ensure the results are the same as
  // Mat.min()...
  for (size_t r = 50; r < 150; ++r)
    {
    sp_cx_mat x;
    x.sprandn(r, r, 0.3);

    uword x_min;
    uword x_subview_min1;
    uword x_subview_min2;
    uword x_subview_min3;

    const std::complex<double> mval = x.min(x_min);
    const std::complex<double> mval1 = x.submat(0, 0, r - 1, r - 1).min(x_subview_min1);
    const std::complex<double> mval2 = x.cols(0, r - 1).min(x_subview_min2);
    const std::complex<double> mval3 = x.rows(0, r - 1).min(x_subview_min3);

    if (mval != std::complex<double>(0.0))
      {
      REQUIRE( x_min == x_subview_min1 );
      REQUIRE( x_min == x_subview_min2 );
      REQUIRE( x_min == x_subview_min3 );

      REQUIRE( mval.real() == Approx(mval1.real()) );
      REQUIRE( mval.imag() == Approx(mval1.imag()) );
      REQUIRE( mval.real() == Approx(mval2.real()) );
      REQUIRE( mval.imag() == Approx(mval2.imag()) );
      REQUIRE( mval.real() == Approx(mval3.real()) );
      REQUIRE( mval.imag() == Approx(mval3.imag()) );
      }
    }
  }

TEST_CASE("fn_min_sp_cx_subview_col_min_test")
  {
  for (size_t r = 10; r < 50; ++r)
    {
    sp_cx_vec x;
    x.sprandn(r, 1, 0.3);

    uword x_min;
    uword x_subview_min1;
    uword x_subview_min2;

    const std::complex<double> mval = x.min(x_min);
    const std::complex<double> mval1 = x.submat(0, 0, r - 1, 0).min(x_subview_min1);
    const std::complex<double> mval2 = x.rows(0, r - 1).min(x_subview_min2);

    if (mval != std::complex<double>(0.0))
      {
      REQUIRE( x_min == x_subview_min1 );
      REQUIRE( x_min == x_subview_min2 );

      REQUIRE( mval.real() == Approx(mval1.real()) );
      REQUIRE( mval.imag() == Approx(mval1.imag()) );
      REQUIRE( mval.real() == Approx(mval2.real()) );
      REQUIRE( mval.imag() == Approx(mval2.imag()) );
      }
    }
  }

TEST_CASE("fn_min_sp_cx_subview_row_min_test")
  {
  for (size_t r = 10; r < 50; ++r)
    {
    sp_cx_rowvec x;
    x.sprandn(1, r, 0.3);

    uword x_min;
    uword x_subview_min1;
    uword x_subview_min2;

    const std::complex<double> mval = x.min(x_min);
    const std::complex<double> mval1 = x.submat(0, 0, 0, r - 1).min(x_subview_min1);
    const std::complex<double> mval2 = x.cols(0, r - 1).min(x_subview_min2);

    if (mval != std::complex<double>(0.0))
      {
      REQUIRE( x_min == x_subview_min1 );
      REQUIRE( x_min == x_subview_min2 );

      REQUIRE( mval.real() == Approx(mval1.real()) );
      REQUIRE( mval.imag() == Approx(mval1.imag()) );
      REQUIRE( mval.real() == Approx(mval2.real()) );
      REQUIRE( mval.imag() == Approx(mval2.imag()) );
      }
    }
  }

TEST_CASE("fn_min_sp_cx_incomplete_subview_min_test")
  {
  for (size_t r = 50; r < 150; ++r)
    {
    sp_cx_mat x;
    x.sprandn(r, r, 0.3);

    uword x_min;
    uword x_subview_min1;
    uword x_subview_min2;
    uword x_subview_min3;

    const std::complex<double> mval = x.min(x_min);
    const std::complex<double> mval1 = x.submat(1, 1, r - 2, r - 2).min(x_subview_min1);
    const std::complex<double> mval2 = x.cols(1, r - 2).min(x_subview_min2);
    const std::complex<double> mval3 = x.rows(1, r - 2).min(x_subview_min3);

    uword row, col;
    x.min(row, col);

    if (row != 0 && row != r - 1 && col != 0 && col != r - 1 && mval != std::complex<double>(0.0))
      {
      uword srow, scol;

      srow = x_subview_min1 % (r - 2);
      scol = x_subview_min1 / (r - 2);
      REQUIRE( x_min == (srow + 1) + r * (scol + 1) );
      REQUIRE( x_min == x_subview_min2 + r );

      srow = x_subview_min3 % (r - 2);
      scol = x_subview_min3 / (r - 2);
      REQUIRE( x_min == (srow + 1) + r * scol );

      REQUIRE( mval.real() == Approx(mval1.real()) );
      REQUIRE( mval.imag() == Approx(mval1.imag()) );
      REQUIRE( mval.real() == Approx(mval2.real()) );
      REQUIRE( mval.imag() == Approx(mval2.imag()) );
      REQUIRE( mval.real() == Approx(mval3.real()) );
      REQUIRE( mval.imag() == Approx(mval3.imag()) );
      }
    }
  }

TEST_CASE("fn_min_sp_cx_incomplete_subview_col_min_test")
  {
  for (size_t r = 10; r < 50; ++r)
    {
    arma::sp_cx_vec x;
    x.sprandn(r, 1, 0.3);

    uword x_min;
    uword x_subview_min1;
    uword x_subview_min2;

    const std::complex<double> mval = x.min(x_min);
    const std::complex<double> mval1 = x.submat(1, 0, r - 2, 0).min(x_subview_min1);
    const std::complex<double> mval2 = x.rows(1, r - 2).min(x_subview_min2);

    if (x_min != 0 && x_min != r - 1 && mval != std::complex<double>(0.0))
      {
      REQUIRE( x_min == x_subview_min1 + 1 );
      REQUIRE( x_min == x_subview_min2 + 1 );

      REQUIRE( mval.real() == Approx(mval1.real()) );
      REQUIRE( mval.imag() == Approx(mval1.imag()) );
      REQUIRE( mval.real() == Approx(mval2.real()) );
      REQUIRE( mval.imag() == Approx(mval2.imag()) );
      }
    }
  }

TEST_CASE("fn_min_sp_cx_incomplete_subview_row_min_test")
  {
  for (size_t r = 10; r < 50; ++r)
    {
    sp_cx_rowvec x;
    x.sprandn(1, r, 0.3);

    uword x_min;
    uword x_subview_min1;
    uword x_subview_min2;

    const std::complex<double> mval = x.min(x_min);
    const std::complex<double> mval1 = x.submat(0, 1, 0, r - 2).min(x_subview_min1);
    const std::complex<double> mval2 = x.cols(1, r - 2).min(x_subview_min2);

    if (x_min != 0 && x_min != r - 1 && mval != std::complex<double>(0.0))
      {
      REQUIRE( x_min == x_subview_min1 + 1 );
      REQUIRE( x_min == x_subview_min2 + 1 );

      REQUIRE( mval.real() == Approx(mval1.real()) );
      REQUIRE( mval.imag() == Approx(mval1.imag()) );
      REQUIRE( mval.real() == Approx(mval2.real()) );
      REQUIRE( mval.imag() == Approx(mval2.imag()) );
      }
    }
  }