try {
            decryptedMessage = NTP1Script::DecryptMetadata(metadataStr, key);
            break;
        } catch (...) {
        }

#include "transaction.h"

#include "base58.h"
#include "bignum.h"
#include "block.h"
#include "checkpoints.h"
#include "init.h"
#include "main.h"
#include "txindex.h"
#include "txmempool.h"
#include "util.h"
#include <boost/foreach.hpp>

void CTransaction::SetNull()
{
    nVersion = CTransaction::CURRENT_VERSION;
    nTime    = GetAdjustedTime();
    vin.clear();
    vout.clear();
    nLockTime = 0;
    nDoS      = 0; // Denial-of-service prevention
}

uint256 CTransaction::GetHash() const { return SerializeHash(*this); }

bool CTransaction::IsNewerThan(const CTransaction& old) const
{
    if (vin.size() != old.vin.size())
        return false;
    for (unsigned int i = 0; i < vin.size(); i++)
        if (vin[i].prevout != old.vin[i].prevout)
            return false;

    bool         fNewer  = false;
    unsigned int nLowest = std::numeric_limits<unsigned int>::max();
    for (unsigned int i = 0; i < vin.size(); i++) {
        if (vin[i].nSequence != old.vin[i].nSequence) {
            if (vin[i].nSequence <= nLowest) {
                fNewer  = false;
                nLowest = vin[i].nSequence;
            }
            if (old.vin[i].nSequence < nLowest) {
                fNewer  = true;
                nLowest = old.vin[i].nSequence;
            }
        }
    }
    return fNewer;
}

bool CTransaction::IsCoinStake() const
{
    // ppcoin: the coin stake transaction is marked with the first output empty
    return (vin.size() > 0 && (!vin[0].prevout.IsNull()) && vout.size() >= 2 && vout[0].IsEmpty());
}

bool CTransaction::CheckColdStake(const CScript& script) const
{
    // tx is a coinstake tx
    if (!IsCoinStake())
        return false;

    if (vin.empty())
        return false;

    const boost::optional<std::vector<uint8_t>> firstPubKey =
        vin[0].scriptSig.GetPubKeyOfP2CSScriptSig();
    if (!firstPubKey)
        return false; // this is not P2CS

    // all inputs must be P2CS and must be paying to the same pubkey
    for (unsigned int i = 1; i < vin.size(); i++) {
        if (vin[i].scriptSig.GetPubKeyOfP2CSScriptSig() != firstPubKey)
            return false;
    }

    // all outputs except first (coinstake marker)
    // have the same pubKeyScript and it matches the script we are spending
    for (unsigned int i = 1; i < vout.size(); i++)
        if (vout[i].scriptPubKey != script)
            return false;

    return true;
}

bool CTransaction::HasP2CSOutputs() const
{
    for (const CTxOut& txout : vout) {
        if (txout.scriptPubKey.IsPayToColdStaking())
            return true;
    }
    return false;
}

CAmount CTransaction::GetValueOut() const
{
    CAmount nValueOut = 0;
    BOOST_FOREACH (const CTxOut& txout, vout) {
        nValueOut += txout.nValue;
        if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut))
            throw std::runtime_error("CTransaction::GetValueOut() : value out of range");
    }
    return nValueOut;
}

std::string CTransaction::ToStringShort() const
{
    std::string str;
    str += fmt::format("{} {}", GetHash().ToString().c_str(),
                       IsCoinBase() ? "base" : (IsCoinStake() ? "stake" : "user"));
    return str;
}

std::string CTransaction::ToString() const
{
    std::string str;
    str += IsCoinBase() ? "Coinbase" : (IsCoinStake() ? "Coinstake" : "CTransaction");
    str += fmt::format("(hash={}, nTime={}, ver={}, vin.size={}, vout.size={}, nLockTime={})\n",
                       GetHash().ToString().substr(0, 10), nTime, nVersion, vin.size(), vout.size(),
                       nLockTime);
    for (unsigned int i = 0; i < vin.size(); i++)
        str += "    " + vin[i].ToString() + "\n";
    for (unsigned int i = 0; i < vout.size(); i++)
        str += "    " + vout[i].ToString() + "\n";
    return str;
}

void CTransaction::print() const { NLog.write(b_sev::info, "{}", ToString()); }

bool CTransaction::ReadFromDisk(const ITxDB& txdb, COutPoint prevout, CTxIndex& txindexRet)
{
    SetNull();
    if (!txdb.ReadTxIndex(prevout.hash, txindexRet))
        return fDebug ? NLog.error("Tx index not found for tx {}", prevout.hash.ToString()) : false;
    if (!txdb.ReadTx(txindexRet.pos, *this))
        return fDebug ? NLog.error("Tx not found for tx {}", prevout.hash.ToString()) : false;
    if (prevout.n >= vout.size()) {
        SetNull();
        return fDebug ? NLog.error("Invalid prevout with n >= vout.size()", prevout.hash.ToString())
                      : false;
    }
    return true;
}

bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout)
{
    CTxIndex txindex;
    return ReadFromDisk(txdb, prevout, txindex);
}

// Check transaction inputs, and make sure any
// pay-to-script-hash transactions are evaluating IsStandard scripts
// Why bother? To avoid denial-of-service attacks; an attacker
// can submit a standard HASH... OP_EQUAL transaction,
// which will get accepted into blocks. The redemption
// script can be anything; an attacker could use a very
// expensive-to-check-upon-redemption script like:
//   DUP CHECKSIG DROP ... repeated 100 times... OP_1
bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const
{
    if (IsCoinBase())
        return true; // Coinbases don't use vin normally

    for (unsigned int i = 0; i < vin.size(); i++) {
        const CTxOut& prev = GetOutputFor(vin[i], mapInputs);

        std::vector<std::vector<unsigned char>> vSolutions;
        txnouttype                              whichType;
        // get the scriptPubKey corresponding to this input:
        const CScript& prevScript = prev.scriptPubKey;
        if (!Solver(CTxDB(), prevScript, whichType, vSolutions))
            return false;

        // Transactions with extra stuff in their scriptSigs are
        // non-standard. Note that this EvalScript() call will
        // be quick, because if there are any operations
        // beside "push data" in the scriptSig the
        // IsStandard() call returns false
        std::vector<std::vector<unsigned char>> stack;
        if (EvalScript(stack, vin[i].scriptSig, *this, i, false, 0).isErr())
            return false;

        if (whichType == TX_SCRIPTHASH) {
            if (stack.empty())
                return false;
            CScript subscript(stack.back().begin(), stack.back().end()); // Get the redeemScript
            // Removed the check to make sure the redeemScript subscript fits one of the four standard
            // transaction types Instead, make sure that the redeemScript doesn't have too many signature
            // check Ops
            if (subscript.GetSigOpCount(true) > MAX_P2SH_SIGOPS) {
                return false;
            }
        } else {
            // Not a TX_SCRIPTHASH scriptPubKey
            int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions);
            if (nArgsExpected < 0)
                return false;
            // If stack is different than expected, not standard
            if (stack.size() != (unsigned int)nArgsExpected)
                return false;
        }
    }

    return true;
}

unsigned int CTransaction::GetLegacySigOpCount() const
{
    unsigned int nSigOps = 0;
    for (const CTxIn& txin : vin) {
        nSigOps += txin.scriptSig.GetSigOpCount(false);
    }
    for (const CTxOut& txout : vout) {
        nSigOps += txout.scriptPubKey.GetSigOpCount(false);
    }
    return nSigOps;
}

Result<void, TxValidationState> CTransaction::CheckTransaction(const ITxDB& txdb,
                                                               CBlock*      sourceBlockPtr) const
{
    // Basic checks that don't depend on any context
    if (vin.empty()) {
        DoS(10, false);
        return Err(MakeInvalidTxState(TxValidationResult::TX_CONSENSUS, "bad-txns-vin-empty"));
    }
    if (vout.empty()) {
        DoS(10, false);
        return Err(MakeInvalidTxState(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-empty"));
    }

    // Size limits
    unsigned int nSizeLimit = MaxBlockSize(txdb);
    if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > nSizeLimit) {
        DoS(100, false);
        return Err(MakeInvalidTxState(TxValidationResult::TX_CONSENSUS, "bad-txns-oversize"));
    }

    // Check for negative or overflow output values
    CAmount nValueOut = 0;
    for (unsigned int i = 0; i < vout.size(); i++) {
        const CTxOut& txout = vout[i];
        if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake()) {
            DoS(100, false);
            return Err(MakeInvalidTxState(TxValidationResult::TX_CONSENSUS, "txout-empty-for-tx"));
        }

        if (txout.nValue < 0) {
            DoS(100, false);
            return Err(MakeInvalidTxState(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-negative"));
        }

        if (txout.nValue > MAX_MONEY) {
            DoS(100, false);
            return Err(MakeInvalidTxState(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-toolarge"));
        }

        nValueOut += txout.nValue;
        if (!MoneyRange(nValueOut)) {
            DoS(100, false);
            return Err(
                MakeInvalidTxState(TxValidationResult::TX_CONSENSUS, "bad-txns-txouttotal-toolarge"));
        }

        // check cold staking enforcement (for delegations) and value out
        if (txout.scriptPubKey.IsPayToColdStaking()) {
            if (txout.nValue < Params().MinColdStakingAmount()) {
                DoS(100, false);
                return Err(MakeInvalidTxState(TxValidationResult::TX_CONSENSUS,
                                              "bad-txns-coldstake-low-amount"));
            }
        }
    }

    // Check for duplicate inputs
    {
        std::set<COutPoint> vInOutPoints;
        for (const CTxIn& txin : vin) {
            if (vInOutPoints.find(txin.prevout) != vInOutPoints.cend()) {
                if (sourceBlockPtr) {
                    sourceBlockPtr->reject = CBlockReject(REJECT_INVALID, "bad-txns-inputs-duplicate",
                                                          sourceBlockPtr->GetHash());
                }
                return Err(
                    MakeInvalidTxState(TxValidationResult::TX_CONSENSUS, "bad-txns-inputs-duplicate"));
            }
            vInOutPoints.insert(txin.prevout);
        }
    }

    if (IsCoinBase()) {
        if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) {
            if (sourceBlockPtr) {
                sourceBlockPtr->reject =
                    CBlockReject(REJECT_INVALID, "bad-cb-length", sourceBlockPtr->GetHash());
            }
            DoS(100, false);
            return Err(MakeInvalidTxState(TxValidationResult::TX_CONSENSUS, "bad-cb-length"));
        }
    } else {
        for (const CTxIn& txin : vin)
            if (txin.prevout.IsNull()) {
                DoS(10, false);
                return Err(
                    MakeInvalidTxState(TxValidationResult::TX_CONSENSUS, "bad-txns-prevout-null"));
            }
    }

    return Ok();
}

CAmount CTransaction::GetMinFee(const ITxDB& txdb, unsigned int nBlockSize, enum GetMinFee_mode mode,
                                unsigned int nBytes) const
{
    // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE
    CAmount nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE;

    unsigned int nNewBlockSize = nBlockSize + nBytes;
    CAmount      nMinFee       = (1 + (CAmount)nBytes / 1000) * nBaseFee;

    // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01
    if (nMinFee < nBaseFee) {
        for (const CTxOut& txout : vout)
            if (txout.nValue < CENT)
                nMinFee = nBaseFee;
    }

    // Raise the price as the block approaches full
    unsigned int nSizeLimit = MaxBlockSize(txdb);
    if (nBlockSize != 1 && nNewBlockSize >= nSizeLimit / 2) {
        if (nNewBlockSize >= nSizeLimit)
            return MAX_MONEY;
        nMinFee *= nSizeLimit / (nSizeLimit - nNewBlockSize);
    }

    if (!MoneyRange(nMinFee))
        nMinFee = MAX_MONEY;
    return nMinFee;
}

bool CTransaction::ReadFromDisk(CDiskTxPos pos, const ITxDB& txdb) { return txdb.ReadTx(pos, *this); }

bool CTransaction::DisconnectInputs(CTxDB& txdb)
{
    // Relinquish previous transactions' spent pointers
    if (!IsCoinBase()) {
        for (const CTxIn& txin : vin) {
            COutPoint prevout = txin.prevout;

            // Get prev txindex from disk
            CTxIndex txindex;
            if (!txdb.ReadTxIndex(prevout.hash, txindex))
                return NLog.error("DisconnectInputs() : ReadTxIndex failed");

            if (prevout.n >= txindex.vSpent.size())
                return NLog.error("DisconnectInputs() : prevout.n out of range");

            // Mark outpoint as not spent
            txindex.vSpent[prevout.n].SetNull();

            // Write back
            if (!txdb.UpdateTxIndex(prevout.hash, txindex))
                return NLog.error("DisconnectInputs() : UpdateTxIndex failed");
        }
    }

    // Remove transaction from index
    // This can fail if a duplicate of this transaction was in a chain that got
    // reorganized away. This is only possible if this transaction was completely
    // spent, so erasing it would be a no-op anyway.
    txdb.EraseTxIndex(this->GetHash());

    return true;
}

bool CTransaction::FetchInputs(const ITxDB& txdb, const std::map<uint256, CTxIndex>& mapTestPool,
                               bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid) const
{
    // FetchInputs can return false either because we just haven't seen some inputs
    // (in which case the transaction should be stored as an orphan)
    // or because the transaction is malformed (in which case the transaction should
    // be dropped).  If tx is definitely invalid, fInvalid will be set to true.
    fInvalid = false;

    if (IsCoinBase())
        return true; // Coinbase transactions have no inputs to fetch.

    for (unsigned int i = 0; i < vin.size(); i++) {
        COutPoint prevout = vin[i].prevout;
        if (inputsRet.count(prevout.hash))
            continue; // Got it already

        // Read txindex
        CTxIndex& txindex = inputsRet[prevout.hash].first;
        bool      fFound  = true;
        if ((fBlock || fMiner) && mapTestPool.count(prevout.hash)) {
            // Get txindex from current proposed changes
            txindex = mapTestPool.find(prevout.hash)->second;
        } else {
            // Read txindex from txdb
            fFound = txdb.ReadTxIndex(prevout.hash, txindex);
        }
        if (!fFound && (fBlock || fMiner))
            return fMiner ? false
                          : NLog.error("FetchInputs() : {} prev tx {} index entry not found",
                                       GetHash().ToString(), prevout.hash.ToString());

        // Read txPrev
        CTransaction& txPrev = inputsRet[prevout.hash].second;
        if (!fFound || txindex.pos == CDiskTxPos(1, 1)) {
            // Get prev tx from single transactions in memory
            if (!mempool.lookup(prevout.hash, txPrev))
                return NLog.error("FetchInputs() : {} mempool Tx prev not found {}",
                                  GetHash().ToString(), prevout.hash.ToString());
            if (!fFound)
                txindex.vSpent.resize(txPrev.vout.size());
        } else {
            // Get prev tx from disk
            if (!txPrev.ReadFromDisk(txindex.pos, txdb))
                return NLog.error("FetchInputs() : {} ReadFromDisk prev tx {} failed",
                                  GetHash().ToString(), prevout.hash.ToString());
        }
    }

    // Make sure all prevout.n indexes are valid:
    for (unsigned int i = 0; i < vin.size(); i++) {
        const COutPoint prevout = vin[i].prevout;
        assert(inputsRet.count(prevout.hash) != 0);
        const CTxIndex&     txindex = inputsRet[prevout.hash].first;
        const CTransaction& txPrev  = inputsRet[prevout.hash].second;
        if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) {
            // Revisit this if/when transaction replacement is implemented and allows
            // adding inputs:
            fInvalid = true;
            return DoS(100,
                       NLog.error("FetchInputs() : {} prevout.n out of range {} {} {}"
                                  " prev tx {}\n{}",
                                  GetHash().ToString(), prevout.n, txPrev.vout.size(),
                                  txindex.vSpent.size(), prevout.hash.ToString(), txPrev.ToString()));
        }
    }

    return true;
}

const CTxOut& CTransaction::GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const
{
    MapPrevTx::const_iterator mi = inputs.find(input.prevout.hash);
    if (mi == inputs.end())
        throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found");

    const CTransaction& txPrev = (mi->second).second;
    if (input.prevout.n >= txPrev.vout.size())
        throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range");

    return txPrev.vout[input.prevout.n];
}

CAmount CTransaction::GetValueIn(const MapPrevTx& inputs) const
{
    if (IsCoinBase())
        return 0;

    CAmount nResult = 0;
    for (unsigned int i = 0; i < vin.size(); i++) {
        nResult += GetOutputFor(vin[i], inputs).nValue;
    }
    return nResult;
}

unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const
{
    if (IsCoinBase())
        return 0;

    unsigned int nSigOps = 0;
    for (unsigned int i = 0; i < vin.size(); i++) {
        const CTxOut& prevout = GetOutputFor(vin[i], inputs);
        if (prevout.scriptPubKey.IsPayToScriptHash())
            nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig);
    }
    return nSigOps;
}

Result<void, TxValidationState>
CTransaction::ConnectInputs(const ITxDB& txdb, MapPrevTx inputs,
                            std::map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
                            const boost::optional<CBlockIndex>& pindexBlock, bool fBlock, bool fMiner,
                            CBlock* sourceBlockPtr) const
{
    // Take over previous transactions' spent pointers
    // fBlock is true when this is called from AcceptBlock when a new best-block is added to the
    // blockchain fMiner is true when called from the internal bitcoin miner
    // ... both are false when called from CTransaction::AcceptToMemoryPool
    if (!IsCoinBase()) {
        const int nCbM     = Params().CoinbaseMaturity(txdb);
        CAmount   nValueIn = 0;
        CAmount   nFees    = 0;
        for (unsigned int i = 0; i < vin.size(); i++) {
            COutPoint prevout = vin[i].prevout;
            assert(inputs.count(prevout.hash) > 0);
            CTxIndex&     txindex = inputs[prevout.hash].first;
            CTransaction& txPrev  = inputs[prevout.hash].second;
            static_assert(std::is_same<uint256, decltype(txindex.pos.nBlockPos)>::value,
                          "Expected same types");

            if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) {
                DoS(100, false);
                return Err(MakeInvalidTxState(
                    TxValidationResult::TX_INVALID_INPUTS, "bad-txns-inputs-invalid",
                    fmt::format("ConnectInputs() : {} prevout.n out of range {} {} {}"
                                " prev tx {}\n{}",
                                GetHash().ToString(), prevout.n, txPrev.vout.size(),
                                txindex.vSpent.size(), prevout.hash.ToString(), txPrev.ToString())));
            }

            // If prev is coinbase or coinstake, check that it's matured
            if (txPrev.IsCoinBase() || txPrev.IsCoinStake()) {
                const boost::optional<CBlockIndex> inputIndex =
                    txdb.ReadBlockIndex(txindex.pos.nBlockPos);
                // failed to read/find block in db
                if (!inputIndex) {
                    if (sourceBlockPtr) {
                        sourceBlockPtr->reject =
                            CBlockReject(REJECT_INVALID,
                                         "bad-txns-premature-spend-of-coinbase/coinstake-check-failed",
                                         sourceBlockPtr->GetHash());
                    }
                    const std::string msg =
                        "bad-txns-premature-spend-of-coinbase/coinstake-check-failed";
                    return Err(MakeInvalidTxState(
                        TxValidationResult::TX_PREMATURE_SPEND_CHECK_ERROR, msg,
                        fmt::format("ConnectInputs() : Failed to check coinbase/coinstake block "
                                    "maturity for spend; reading block {} failed",
                                    txindex.pos.nBlockPos.ToString())));
                }

                // check if spent before maturity
                if (pindexBlock->nHeight - inputIndex->nHeight < nCbM) {
                    if (sourceBlockPtr) {
                        sourceBlockPtr->reject = CBlockReject(
                            REJECT_INVALID, "bad-txns-premature-spend-of-coinbase/coinstake",
                            sourceBlockPtr->GetHash());
                    }
                    const std::string msg = txPrev.IsCoinBase()
                                                ? "bad-txns-premature-spend-of-coinbase"
                                                : "bad-txns-premature-spend-of-coinstake";
                    return Err(
                        MakeInvalidTxState(TxValidationResult::TX_PREMATURE_SPEND, msg,
                                           fmt::format("ConnectInputs() : tried to spend {} at depth {}",
                                                       txPrev.IsCoinBase() ? "coinbase" : "coinstake",
                                                       pindexBlock->nHeight - inputIndex->nHeight)));
                }
            }

            // ppcoin: check transaction timestamp
            if (txPrev.nTime > nTime) {
                DoS(100, false);
                return Err(MakeInvalidTxState(
                    TxValidationResult::TX_CONSENSUS, "bad-txns-input-time-order",
                    "ConnectInputs() : transaction timestamp earlier than input transaction"));
            }

            // Check for negative or overflow input values
            nValueIn += txPrev.vout[prevout.n].nValue;
            if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) {
                DoS(100, false);
                return Err(MakeInvalidTxState(TxValidationResult::TX_CONSENSUS,
                                              "bad-txns-inputvalues-outofrange",
                                              "ConnectInputs() : txin values out of range"));
            }
        }
        // The first loop above does all the inexpensive checks.
        // Only if ALL inputs pass do we perform expensive ECDSA signature checks.
        // Helps prevent CPU exhaustion attacks.
        for (unsigned int i = 0; i < vin.size(); i++) {
            COutPoint prevout = vin[i].prevout;
            assert(inputs.count(prevout.hash) > 0);
            CTxIndex&     txindex = inputs[prevout.hash].first;
            CTransaction& txPrev  = inputs[prevout.hash].second;

            // This doesn't trigger the DoS code on purpose; if it did, it would make it easier
            // for an attacker to attempt to split the network.
            if (!txindex.vSpent[prevout.n].IsNull()) {
                const auto code = TxValidationResult::TX_MISSING_INPUTS;
                const auto msg  = "bad-txns-inputs-missingorspent";
                if (fMiner) {
                    return Err(MakeInvalidTxState(code, msg));
                }
                return Err(MakeInvalidTxState(
                    code, msg,
                    fmt::format("ConnectInputs() : {} prev tx already used at {}", GetHash().ToString(),
                                txindex.vSpent[prevout.n].ToString())));
            }

            // Skip ECDSA signature verification when connecting blocks (fBlock=true)
            // before the last blockchain checkpoint. This is safe because block merkle hashes are
            // still computed and checked, and any change will be caught at the next checkpoint.
            if (!(fBlock &&
                  (txdb.GetBestChainHeight().value_or(0) < Checkpoints::GetTotalBlocksEstimate()))) {
                // Verify signature
                bool       fStrictPayToScriptHash = true;
                const auto verifyRes =
                    VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, false, 0);
                if (verifyRes.isErr()) {
                    // only during transition phase for P2SH: do not invoke anti-DoS code for
                    // potentially old clients relaying bad P2SH transactions
                    if (fStrictPayToScriptHash) {
                        const auto verifyResP2SH = VerifySignature(txPrev, *this, i, false, false, 0);
                        if (verifyResP2SH.isOk()) {
                            return Err(MakeInvalidTxState(
                                TxValidationResult::TX_NOT_STANDARD,
                                fmt::format("non-mandatory-script-verify-flag ({})",
                                            ScriptErrorString(verifyResP2SH.unwrapErr(RESULT_PRE))),
                                fmt::format("ConnectInputs() : {} P2SH VerifySignature failed",
                                            GetHash().ToString())));
                        }
                    }

                    const std::string msg =
                        fmt::format("mandatory-script-verify-flag-failed ({})",
                                    ScriptErrorString(verifyRes.unwrapErr(RESULT_PRE)));

                    if (sourceBlockPtr) {
                        sourceBlockPtr->reject =
                            CBlockReject(REJECT_INVALID, msg, sourceBlockPtr->GetHash());
                    }
                    this->reject = CTransaction::CTxReject(REJECT_INVALID, msg, GetHash());
                    DoS(100, false);
                    return Err(
                        MakeInvalidTxState(TxValidationResult::TX_CONSENSUS, msg,
                                           fmt::format("ConnectInputs() : {} VerifySignature failed",
                                                       GetHash().ToString())));
                }
            }

            // Mark outpoints as spent
            txindex.vSpent[prevout.n] = posThisTx;

            // Write back
            if (fBlock || fMiner) {
                mapTestPool[prevout.hash] = txindex;
            }
        }

        if (!IsCoinStake()) {
            if (nValueIn < GetValueOut()) {
                if (sourceBlockPtr) {
                    sourceBlockPtr->reject =
                        CBlockReject(REJECT_INVALID, "bad-txns-in-belowout", sourceBlockPtr->GetHash());
                }
                DoS(100, false);
                return Err(
                    MakeInvalidTxState(TxValidationResult::TX_CONSENSUS, "bad-txns-in-belowout",
                                       fmt::format("ConnectInputs() : {} value in ({}) < value out ({})",
                                                   GetHash().ToString(), nValueIn, GetValueOut())));
            }

            // Tally transaction fees
            CAmount nTxFee = nValueIn - GetValueOut();
            if (nTxFee < 0) {
                DoS(100, false);
                return Err(MakeInvalidTxState(
                    TxValidationResult::TX_CONSENSUS, "bad-txns-fee-outofrange1",
                    fmt::format("ConnectInputs() : {} nTxFee < 0", GetHash().ToString())));
            }

            // enforce transaction fees for every block
            if (nTxFee < GetMinFee(txdb)) {
                if (fBlock) {
                    DoS(100, NLog.error("ConnectInputs() : {} not paying required fee={}, paid={}",
                                        GetHash().ToString(), FormatMoney(GetMinFee(txdb)),
                                        FormatMoney(nTxFee)));
                }
                return Err(MakeInvalidTxState(
                    TxValidationResult::TX_CONSENSUS, "bad-txns-fee-outofrange2",
                    fmt::format("ConnectInputs() : {} nTxFee < 0", GetHash().ToString())));
            }

            nFees += nTxFee;
            if (!MoneyRange(nFees)) {
                return Err(
                    MakeInvalidTxState(TxValidationResult::TX_CONSENSUS, "bad-txns-fee-outofrange3"));
            }
        }
    }

    return Ok();
}

// ppcoin: total coin age spent in transaction, in the unit of coin-days.
// Only those coins meeting minimum age requirement counts. As those
// transactions not in main chain are not currently indexed so we
// might not find out about their coin age. Older transactions are
// guaranteed to be in main chain by sync-checkpoint. This rule is
// introduced to help nodes establish a consistent view of the coin
// age (trust score) of competing branches.
bool CTransaction::GetCoinAge(const ITxDB& txdb, uint64_t& nCoinAge) const
{
    CBigNum      bnCentSecond = 0; // coin age in the unit of cent-seconds
    unsigned int nSMA         = Params().StakeMinAge(txdb);
    nCoinAge                  = 0;

    if (IsCoinBase())
        return true;

    for (const CTxIn& txin : vin) {
        // First try finding the previous transaction in database
        CTransaction txPrev;
        CTxIndex     txindex;
        if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
            continue; // previous transaction not in main chain
        if (nTime < txPrev.nTime)
            return false; // Transaction timestamp violation

        // Read block header
        CBlock block;
        if (!block.ReadFromDisk(txindex.pos.nBlockPos, txdb, false))
            return false; // unable to read block of previous transaction
        if (block.GetBlockTime() + nSMA > nTime)
            continue; // only count coins meeting min age requirement

        CAmount nValueIn = txPrev.vout[txin.prevout.n].nValue;
        bnCentSecond += CBigNum(nValueIn) * (nTime - txPrev.nTime) / CENT;

        if (fDebug)
            NLog.write(b_sev::debug, "coin age nValueIn={} nTimeDiff={} bnCentSecond={}", nValueIn,
                       nTime - txPrev.nTime, bnCentSecond.ToString());
    }

    CBigNum bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60);
    if (fDebug)
        NLog.write(b_sev::debug, "coin age bnCoinDay={}", bnCoinDay.ToString());
    nCoinAge = bnCoinDay.getuint64();
    return true;
}

CTransaction CTransaction::FetchTxFromDisk(const uint256& txid)
{
    CTxDB txdb;
    return FetchTxFromDisk(txid, txdb);
}

CTransaction CTransaction::FetchTxFromDisk(const uint256& txid, const ITxDB& txdb)
{
    CTransaction result;
    CTxIndex     txPos;
    if (!txdb.ReadTxIndex(txid, txPos)) {
        NLog.write(b_sev::err, "Unable to read standard transaction from db: {}", txid.ToString());
        throw std::runtime_error("Unable to read standard transaction from db: " + txid.ToString());
    }
    if (!result.ReadFromDisk(txPos.pos, txdb)) {
        NLog.write(b_sev::err,
                   "Unable to read standard transaction from disk with the "
                   "index given by db: {}",
                   txid.ToString());
        throw std::runtime_error("Unable to read standard transaction from db: " + txid.ToString());
    }
    return result;
}

/**
 * Get all relevant keys of a transaction that are in our wallet. If an output number is defined, only
 * the key of that output is returned.
 * @brief CTransaction::GetThisWalletKeysOfTx
 * @param txid
 * @param outputNumber
 * @return the keys of the transaction given by txid that are in our wallet. If outputNumber is defined,
 * only that key will be returned, if it exists in our wallet
 */
std::vector<CKey> CTransaction::GetThisWalletKeysOfTx(const uint256&            txid,
                                                      boost::optional<unsigned> outputNumber)
{
    CTransaction tx;
    // first we try to find it in the mempool, if not found, we look on disk
    bool foundInMempool = mempool.lookup(txid, tx);
    if (!foundInMempool) {
        tx = CTransaction::FetchTxFromDisk(txid);
    }

    std::vector<CKey> keys;

    for (unsigned i = 0; i < tx.vout.size(); i++) {
        if (outputNumber.is_initialized() && *outputNumber != i) {
            continue;
        }
        const CTxOut&                     out = tx.vout[i];
        txnouttype                        outtype;
        std::vector<std::vector<uint8_t>> vSolutions;
        // this solution can be improved later for multiple kinds of transactions, here we only support
        // P2PKH transactions, more in CScript class's source file
        Solver(CTxDB(), out.scriptPubKey, outtype, vSolutions);
        if (outtype == TX_PUBKEYHASH) {
            CKeyID keyId = CKeyID(uint160(vSolutions[0]));
            if (!CBitcoinAddress(keyId).IsValid()) {
                continue;
            }
            CKey key;
            if (!pwalletMain->GetKey(keyId, key)) {
                continue;
            }
            // this is O(N^2), but this is OK, because the number of outputs is low
            // we're comparing public keys because CKey objects are not comparable
            if (std::find_if(keys.cbegin(), keys.cend(), [&key](const CKey& k) {
                    return k.GetPubKey() == key.GetPubKey();
                }) == keys.cend()) {
                keys.push_back(key);
            }
        }
    }

    // we don't retrieve input keys if the key of a specific output number is requested
    if (!outputNumber.is_initialized()) {
        for (unsigned i = 0; i < tx.vin.size(); i++) {
            const CTxIn&          in     = tx.vin[i];
            boost::optional<CKey> pubKey = CTransaction::GetPublicKeyFromScriptSig(in.scriptSig);
            if (!pubKey.is_initialized()) {
                continue;
            }

            CKey   key;
            CKeyID keyId = pubKey->GetPubKey().GetID();
            if (!pwalletMain->GetKey(keyId, key)) {
                continue;
            }

            // this is O(N^2), but this is OK, because the numebr of inputs is low
            // we're comparing public keys because CKey objects are not comparable
            if (std::find_if(keys.cbegin(), keys.cend(), [&key](const CKey& k) {
                    return k.GetPubKey() == key.GetPubKey();
                }) == keys.cend()) {
                keys.push_back(key);
            }
        }
    }

    return keys;
}

std::string CTransaction::DecryptMetadataOfTx(const StringViewT metadataStr, const uint256& txid,
                                              boost::optional<std::string>& error)
{
    std::vector<CKey> keysVec = GetThisWalletKeysOfTx(txid);
    if (keysVec.empty()) {
        error = "No valid keys were found in the transaction: " + txid.ToString();
        return "";
    }

    std::string decryptedMessage;
    for (const CKey& key : keysVec) {

    }
    if (decryptedMessage.empty()) {
        error = "None of the available keys in the following txid: " + txid.ToString() +
                " were able to decrypted the message: " + metadataStr.to_string();
        return "";
    }
    return decryptedMessage;
}

boost::optional<CKey> CTransaction::GetPublicKeyFromScriptSig(const CScript& scriptSig)
{
    opcodetype                 opt;
    auto                       beg = scriptSig.cbegin();
    std::vector<unsigned char> vchSig, vchPub;

    if (!scriptSig.GetOp(beg, opt, vchSig)) {
        return boost::none;
    }
    if (!scriptSig.GetOp(beg, opt, vchPub)) {
        return boost::none;
    }
    if (vchSig.empty() || !IsCanonicalSignature(vchSig)) {
        return boost::none;
    }
    if (vchPub.empty() || !IsCanonicalPubKey(vchPub)) {
        return boost::none;
    }

    CKey resultKey;
    if (!resultKey.SetPubKey(vchPub)) {
        return boost::none;
    }
    return resultKey;
}

boost::optional<CKey> CTransaction::GetOnePublicKeyFromInputs(const CTransaction& tx)
{
    for (const CTxIn& in : tx.vin) {
        boost::optional<CKey> res = GetPublicKeyFromScriptSig(in.scriptSig);
        if (res.is_initialized()) {
            return res;
        }
    }
    return boost::none;
}


try
	{
		const Token* pNext = next();
		pNext = parseFile(pNext);
		if (!isEOF(pNext))
			syntaxError("Additional tokens behind supposed EOF");
	}
	catch (SyntaxException& exc)
	{
		std::string m(exc.message());
		std::string where(_currentPath);
		where.append("(");
		where.append(NumberFormatter::format(_istr.getCurrentLineNumber()));
		where.append(")");
		throw SyntaxException(m, where);
	}

// Parser.cpp
// Library: CppParser
// Package: CppParser
// Module:  Parser

#include "Poco/CppParser/Parser.h"
#include "Poco/CppParser/CppToken.h"
#include "Poco/CppParser/Decl.h"
#include "Poco/CppParser/Enum.h"
#include "Poco/CppParser/EnumValue.h"
#include "Poco/CppParser/Function.h"
#include "Poco/CppParser/NameSpace.h"
#include "Poco/CppParser/Parameter.h"
#include "Poco/CppParser/Struct.h"
#include "Poco/CppParser/TypeDef.h"
#include "Poco/CppParser/Variable.h"
#include "Poco/CppParser/AttributesParser.h"
#include "Poco/Path.h"
#include "Poco/String.h"
#include "Poco/NumberFormatter.h"
#include "Poco/Exception.h"
#include <sstream>
#include <cctype>

using Poco::Token;
using Poco::WhitespaceToken;
using Poco::Path;
using Poco::NumberFormatter;
using Poco::SyntaxException;
using Poco::icompare;
using Poco::trimInPlace;

namespace Poco {
namespace CppParser {

Parser::Parser(NameSpace::SymbolTable& gst, const std::string& file, std::istream& istr):
	_gst(gst),
	_istr(istr),
	_tokenizer(_istr),
	_file(file),
	_inFile(false),
	_pCurrentSymbol(0),
	_access(Symbol::ACC_PUBLIC)
{
	Path p(file);
	p.makeAbsolute();
	_path = p.toString();
	_currentPath = _path;

	_nsStack.push_back(NameSpace::root());
}

Parser::~Parser()
{
}

inline bool Parser::isIdentifier(const Token* pToken)
{
	return pToken->is(Token::IDENTIFIER_TOKEN) || isOperator(pToken, OperatorToken::OP_DBL_COLON);
}

inline bool Parser::isOperator(const Token* pToken, int kind)
{
	return pToken->is(Token::OPERATOR_TOKEN) && pToken->asInteger() == kind;
}

inline bool Parser::isKeyword(const Token* pToken, int kind)
{
	return pToken->is(Token::KEYWORD_TOKEN) && pToken->asInteger() == kind;
}

inline bool Parser::isEOF(const Token* pToken)
{
	return pToken->is(Token::EOF_TOKEN);
}

void Parser::expectOperator(const Token* pToken, int kind, const std::string& msg)
{
	if (!isOperator(pToken, kind))
		syntaxError(msg + ", found " + pToken->tokenString());
}

void Parser::syntaxError(const std::string& msg)
{
	throw SyntaxException("Expected", msg);
}

inline void Parser::append(std::string& decl, const std::string& token)
{
	if (!decl.empty())
	{
		char last = decl[decl.length() - 1];
		if (token != "::" &&
			token != "." &&
			token != ")" &&
			token != "->" &&
			token != "," &&
			token != "[" &&
			token != "]" &&
			last != '~' &&
			last != ':' &&
			last != '(' &&
			last != ')' &&
			last != '[' &&
			last != ']' &&
			last != ' '
			)
			decl.append(" ");
	}
	decl.append(token);
	if (token == "const"
	 || token == "constexpr"
	 || token == "static"
	 || token == "mutable"
	 || token == "inline"
	 || token == "volatile"
	 || token == "register"
	 || token == "thread_local")
		decl.append(" ");
}

inline void Parser::append(std::string& decl, const Token* pToken)
{
	poco_check_ptr (pToken);
	append(decl, pToken->tokenString());
}

void Parser::parse()
{

}

const Token* Parser::parseFile(const Token* pNext)
{
	while (pNext->is(Token::IDENTIFIER_TOKEN) || pNext->is(Token::KEYWORD_TOKEN))
	{
		switch (pNext->asInteger())
		{
		case IdentifierToken::KW_NAMESPACE:
			pNext = parseNameSpace(pNext);
			break;
		case IdentifierToken::KW_STRUCT:
		case IdentifierToken::KW_CLASS:
		case IdentifierToken::KW_UNION:
			pNext = parseClass(pNext);
			break;
		case IdentifierToken::KW_TEMPLATE:
			pNext = parseTemplate(pNext);
			break;
		case IdentifierToken::KW_TYPEDEF:
			pNext = parseTypeDef(pNext);
			break;
		case IdentifierToken::KW_USING:
			pNext = parseUsing(pNext);
			break;
		case IdentifierToken::KW_ENUM:
			pNext = parseEnum(pNext);
			break;
		default:
			pNext = parseVarFunc(pNext);
		}
	}
	return pNext;
}

const Token* Parser::parseNameSpace(const Token* pNext)
{
	poco_assert (isKeyword(pNext, IdentifierToken::KW_NAMESPACE));

	pNext = next();
	if (pNext->is(Token::IDENTIFIER_TOKEN))
	{
		_access = Symbol::ACC_PUBLIC;
		std::string name = pNext->tokenString();
		pNext = next();
		expectOperator(pNext, OperatorToken::OP_OPENBRACE, "{");
		NameSpace* pNS = dynamic_cast<NameSpace*>(currentNameSpace()->lookup(name));
		bool undefined = (pNS == 0);
		if (undefined) pNS = new NameSpace(name, currentNameSpace());
		pushNameSpace(pNS, -1, undefined);
		pNext = next();
		while (pNext->is(Token::IDENTIFIER_TOKEN) || pNext->is(Token::KEYWORD_TOKEN))
		{
			switch (pNext->asInteger())
			{
			case IdentifierToken::KW_NAMESPACE:
				pNext = parseNameSpace(pNext);
				break;
			case IdentifierToken::KW_STRUCT:
			case IdentifierToken::KW_CLASS:
			case IdentifierToken::KW_UNION:
				pNext = parseClass(pNext);
				break;
			case IdentifierToken::KW_TEMPLATE:
				pNext = parseTemplate(pNext);
				break;
			case IdentifierToken::KW_TYPEDEF:
				pNext = parseTypeDef(pNext);
				break;
			case IdentifierToken::KW_USING:
				pNext = parseUsing(pNext);
				break;
			case IdentifierToken::KW_ENUM:
				pNext = parseEnum(pNext);
				break;
			default:
				pNext = parseVarFunc(pNext);
			}
		}
		expectOperator(pNext, OperatorToken::OP_CLOSBRACE, "}");
		pNext = next();
	}
	else syntaxError("namespace name");
	popNameSpace();
	return pNext;
}

const Token* Parser::parseClass(const Token* pNext)
{
	std::string decl;
	return parseClass(pNext, decl);
}

const Token* Parser::parseClass(const Token* pNext, std::string& decl)
{
	poco_assert (isKeyword(pNext, IdentifierToken::KW_CLASS) || isKeyword(pNext, IdentifierToken::KW_STRUCT) || isKeyword(pNext, IdentifierToken::KW_UNION));

	_pCurrentSymbol = 0;
	bool isClass = isKeyword(pNext, IdentifierToken::KW_CLASS);
	int line = _istr.getCurrentLineNumber();
	Symbol::Access prevAccess = _access;
	append(decl, pNext);
	Symbol::Access access;
	if (isKeyword(pNext, IdentifierToken::KW_CLASS))
		access = Symbol::ACC_PRIVATE;
	else
		access = Symbol::ACC_PUBLIC;
	pNext = next();
	if (pNext->is(Token::IDENTIFIER_TOKEN))
		append(decl, pNext);
	else
		syntaxError("class/struct name");
	pNext = next();
	bool isFinal = false;
	if (isIdentifier(pNext) && pNext->asString() == "final")
	{
		pNext = next();
		isFinal = true;
	}
	if (!isOperator(pNext, OperatorToken::OP_SEMICOLON))
	{
		// if we have a template specialization the next token will be a <
		if (isOperator(pNext, OperatorToken::OP_LT))
		{
			// skip all template specializations
			// skip until at { bracket, then parseBlock to ignore it
			while (!isOperator(pNext, OperatorToken::OP_OPENBRACE))
				pNext = next();
			pNext = parseBlock(pNext); // skip after }

			expectOperator(pNext, OperatorToken::OP_SEMICOLON, ";");
			pNext = next();
			_access = prevAccess;
			_pCurrentSymbol = 0;
			return pNext;
		}
		if (isOperator(pNext, OperatorToken::OP_COLON) || isOperator(pNext, OperatorToken::OP_OPENBRACE))
		{
			Struct* pClass = new Struct(decl, isClass, currentNameSpace());
			if (isFinal) pClass->makeFinal();
			pushNameSpace(pClass, line);
			_access = access;
			if (isOperator(pNext, OperatorToken::OP_COLON))
				pNext = parseBaseClassList(next(), pClass);
			expectOperator(pNext, OperatorToken::OP_OPENBRACE, "{");
			pNext = parseClassMembers(pNext, pClass);
			expectOperator(pNext, OperatorToken::OP_CLOSBRACE, "}");
			pNext = next();
			expectOperator(pNext, OperatorToken::OP_SEMICOLON, ";");
			popNameSpace();
		}
		else return parseVarFunc(pNext, decl);
	}
	pNext = next();
	_access = prevAccess;
	_pCurrentSymbol = 0;
	return pNext;
}

const Token* Parser::parseBaseClassList(const Token* pNext, Struct* pClass)
{
	while (pNext->is(Token::IDENTIFIER_TOKEN) || pNext->is(Token::KEYWORD_TOKEN))
	{
		bool isVirtual = false;
		Symbol::Access acc = _access;
		while (pNext->is(Token::KEYWORD_TOKEN))
		{
			switch (pNext->asInteger())
			{
			case IdentifierToken::KW_PUBLIC:
				acc = Symbol::ACC_PUBLIC;
				break;
			case IdentifierToken::KW_PROTECTED:
				acc = Symbol::ACC_PROTECTED;
				break;
			case IdentifierToken::KW_PRIVATE:
				acc = Symbol::ACC_PRIVATE;
				break;
			case IdentifierToken::KW_VIRTUAL:
				isVirtual = true;
				break;
			default:
				syntaxError("public, protected, private or virtual");
			}
			pNext = next();
		}
		std::string id;
		pNext = parseIdentifier(pNext, id);
		if (isOperator(pNext, OperatorToken::OP_LT))
			pNext = parseTemplateArgs(pNext, id);
		pClass->addBase(id, acc, isVirtual);
		if (isOperator(pNext, OperatorToken::OP_COMMA))
			pNext = next();
	}
	return pNext;
}

const Token* Parser::parseClassMembers(const Token* pNext, Struct* pClass)
{
	poco_assert (isOperator(pNext, OperatorToken::OP_OPENBRACE));

	pNext = next();
	while (pNext->is(Token::IDENTIFIER_TOKEN) || pNext->is(Token::KEYWORD_TOKEN) || isOperator(pNext, OperatorToken::OP_COMPL))
	{
		switch (pNext->asInteger())
		{
		case IdentifierToken::KW_PRIVATE:
		case IdentifierToken::KW_PROTECTED:
		case IdentifierToken::KW_PUBLIC:
			pNext = parseAccess(pNext);
			break;
		case IdentifierToken::KW_STRUCT:
		case IdentifierToken::KW_CLASS:
		case IdentifierToken::KW_UNION:
			pNext = parseClass(pNext);
			break;
		case IdentifierToken::KW_TEMPLATE:
			pNext = parseTemplate(pNext);
			break;
		case IdentifierToken::KW_TYPEDEF:
			pNext = parseTypeDef(pNext);
			break;
		case IdentifierToken::KW_ENUM:
			pNext = parseEnum(pNext);
			break;
		case IdentifierToken::KW_FRIEND:
			pNext = parseFriend(pNext);
			break;
		case OperatorToken::OP_COMPL:
		default:
			pNext = parseVarFunc(pNext);
		}
	}
	return pNext;
}

const Token* Parser::parseAccess(const Token* pNext)
{
	switch (pNext->asInteger())
	{
	case IdentifierToken::KW_PRIVATE:
		_access = Symbol::ACC_PRIVATE;
		break;
	case IdentifierToken::KW_PROTECTED:
		_access = Symbol::ACC_PROTECTED;
		break;
	case IdentifierToken::KW_PUBLIC:
		_access = Symbol::ACC_PUBLIC;
		break;
	}
	pNext = next();
	expectOperator(pNext, OperatorToken::OP_COLON, ":");
	return next();
}

const Token* Parser::parseTemplate(const Token* pNext)
{
	poco_assert (isKeyword(pNext, IdentifierToken::KW_TEMPLATE));

	std::string decl;
	append(decl, pNext);
	pNext = next();
	expectOperator(pNext, OperatorToken::OP_LT, "<");
	pNext = parseTemplateArgs(pNext, decl);
	if (isKeyword(pNext, IdentifierToken::KW_CLASS) || isKeyword(pNext, IdentifierToken::KW_STRUCT) || isKeyword(pNext, IdentifierToken::KW_UNION))
		return parseClass(pNext, decl);
	else
		return parseVarFunc(pNext, decl);
}

const Token* Parser::parseTemplateArgs(const Token* pNext, std::string& decl)
{
	poco_assert (isOperator(pNext, OperatorToken::OP_LT));

	append(decl, pNext);
	int depth = 1;
	pNext = next();
	while (depth > 0 && !isEOF(pNext))
	{
		append(decl, pNext);
		if (isOperator(pNext, OperatorToken::OP_LT))
			++depth;
		else if (isOperator(pNext, OperatorToken::OP_GT))
			--depth;
		pNext = next();
	}
	return pNext;
}

const Token* Parser::parseTypeDef(const Token* pNext)
{
	poco_assert (isKeyword(pNext, IdentifierToken::KW_TYPEDEF));

	_pCurrentSymbol = 0;
	int line = _istr.getCurrentLineNumber();
	std::string decl;
	while (!isOperator(pNext, OperatorToken::OP_SEMICOLON) && !isEOF(pNext))
	{
		append(decl, pNext);
		pNext = next();
	}
	TypeDef* pTypeDef = new TypeDef(decl, currentNameSpace());
	addSymbol(pTypeDef, line);
	pNext = next();
	_pCurrentSymbol = 0;
	return pNext;
}

const Token* Parser::parseUsing(const Token* pNext)
{
	poco_assert (isKeyword(pNext, IdentifierToken::KW_USING));

	pNext = next();
	if (isKeyword(pNext, IdentifierToken::KW_NAMESPACE))
	{
		pNext = next();
		if (isIdentifier(pNext))
		{
			std::string ns;
			pNext = parseIdentifier(pNext, ns);
			currentNameSpace()->importNameSpace(ns);
		}
		else syntaxError("identifier");
	}
	else
	{
		if (isIdentifier(pNext))
		{
			std::string id;
			pNext = parseIdentifier(pNext, id);
			currentNameSpace()->importSymbol(id);
		}
	}

	if (!isOperator(pNext, OperatorToken::OP_SEMICOLON))
		syntaxError("semicolon");
	return next();
}

const Token* Parser::parseFriend(const Token* pNext)
{
	poco_assert (isKeyword(pNext, IdentifierToken::KW_FRIEND));

	pNext = next();

	while (!isOperator(pNext, OperatorToken::OP_SEMICOLON) && !isEOF(pNext))
		pNext = next();

	if (isOperator(pNext, OperatorToken::OP_SEMICOLON))
		pNext = next();
	return pNext;
}

const Token* Parser::parseVarFunc(const Token* pNext)
{
	std::string decl;
	return parseVarFunc(pNext, decl);
}

const Token* Parser::parseVarFunc(const Token* pNext, std::string& decl)
{
	_pCurrentSymbol = 0;
	if (isKeyword(pNext, IdentifierToken::KW_EXTERN))
	{
		pNext = parseExtern(pNext);
	}
	else
	{
		append(decl, pNext);
		pNext = next();
		bool isOp = false;
		while (!isOperator(pNext, OperatorToken::OP_SEMICOLON) && !isOperator(pNext, OperatorToken::OP_OPENPARENT) && !isEOF(pNext))
		{
			append(decl, pNext);
			isOp = isKeyword(pNext, IdentifierToken::KW_OPERATOR);
			pNext = next();
		}
		if (isOperator(pNext, OperatorToken::OP_SEMICOLON))
		{
			std::string name = Symbol::extractName(decl);
			if (!currentNameSpace()->lookup(name))
			{
				Variable* pVar = new Variable(decl, currentNameSpace());
				addSymbol(pVar, _istr.getCurrentLineNumber());
			}
			pNext = next();
		}
		else if (isOperator(pNext, OperatorToken::OP_OPENPARENT))
		{
			if (isOp)
			{
				decl += " (";
				pNext = next();
				expectOperator(pNext, OperatorToken::OP_CLOSPARENT, ")");
				append(decl, pNext);
				pNext = next();
				expectOperator(pNext, OperatorToken::OP_OPENPARENT, "(");
			}
			pNext = parseFunc(pNext, decl);
		}
	}
	_pCurrentSymbol = 0;
	return pNext;
}

const Token* Parser::parseExtern(const Token* pNext)
{
	poco_assert (isKeyword(pNext, IdentifierToken::KW_EXTERN));

	pNext = next();
	if (pNext->is(Token::STRING_LITERAL_TOKEN))
		pNext = next();
	if (isOperator(pNext, OperatorToken::OP_OPENBRACE))
	{
		pNext = parseBlock(pNext);
	}
	else
	{
		while (!isOperator(pNext, OperatorToken::OP_SEMICOLON) && !isEOF(pNext))
			pNext = next();
	}
	if (isOperator(pNext, OperatorToken::OP_SEMICOLON))
		pNext = next();
	return pNext;
}

const Token* Parser::parseFunc(const Token* pNext, std::string& decl)
{
	poco_assert (isOperator(pNext, OperatorToken::OP_OPENPARENT));

	int line = _istr.getCurrentLineNumber();
	Function* pFunc = 0;
	std::string name = Symbol::extractName(decl);
	if (name.find(':') == std::string::npos)
	{
		pFunc = new Function(decl, currentNameSpace());
		addSymbol(pFunc, line);
	}
	pNext = parseParameters(pNext, pFunc);
	expectOperator(pNext, OperatorToken::OP_CLOSPARENT, ")");
	pNext = next();
	while (pNext->is(Poco::Token::IDENTIFIER_TOKEN) || pNext->is(Poco::Token::KEYWORD_TOKEN))
	{
		if (isKeyword(pNext, IdentifierToken::KW_CONST))
		{
			if (pFunc) pFunc->makeConst();
			pNext = next();
		}
		if (isKeyword(pNext, IdentifierToken::KW_THROW))
		{
			while (!isOperator(pNext, OperatorToken::OP_ASSIGN) && !isOperator(pNext, OperatorToken::OP_SEMICOLON) &&
				   !isOperator(pNext, OperatorToken::OP_OPENBRACE) && !isEOF(pNext))
				pNext = next();
		}
		else if (isKeyword(pNext, IdentifierToken::KW_NOEXCEPT))
		{
			if (pFunc) pFunc->makeNoexcept();
			pNext = next();
		}
		else if (isIdentifier(pNext) && pNext->asString() == "override")
		{
			if (pFunc) pFunc->makeOverride();
			pNext = next();
		}
		else if (isIdentifier(pNext) && pNext->asString() == "final")
		{
			if (pFunc) pFunc->makeFinal();
			pNext = next();
		}
		else if (isKeyword(pNext, IdentifierToken::KW_TRY))
		{
			break; // handled below
		}
	}
	if (isOperator(pNext, OperatorToken::OP_ASSIGN))
	{
		pNext = next();
		if (!pNext->is(Token::INTEGER_LITERAL_TOKEN) && !isKeyword(pNext, IdentifierToken::KW_DEFAULT) && !isKeyword(pNext, IdentifierToken::KW_DELETE))
			syntaxError("0, default or delete");
		if (isKeyword(pNext, IdentifierToken::KW_DEFAULT))
			pFunc->makeDefault();
		else if (isKeyword(pNext, IdentifierToken::KW_DELETE))
			pFunc->makeDelete();
		pNext = next();
		if (pFunc) pFunc->makePureVirtual();
		expectOperator(pNext, OperatorToken::OP_SEMICOLON, ";");
	}
	else if (isOperator(pNext, OperatorToken::OP_OPENBRACE) || isOperator(pNext, OperatorToken::OP_COLON))
	{
		while (!isOperator(pNext, OperatorToken::OP_OPENBRACE) && !isEOF(pNext))
			pNext = next();

		pNext = parseBlock(pNext);
		if (!pFunc)
			pFunc = dynamic_cast<Function*>(currentNameSpace()->lookup(name));
		if (pFunc)
			pFunc->makeInline();
	}
	else if (isKeyword(pNext, IdentifierToken::KW_TRY))
	{
		pNext = next();
		if (isOperator(pNext, OperatorToken::OP_OPENBRACE) || isOperator(pNext, OperatorToken::OP_COLON))
		{
			while (!isOperator(pNext, OperatorToken::OP_OPENBRACE) && !isEOF(pNext))
				pNext = next();

			pNext = parseBlock(pNext);

			if (isKeyword(pNext, IdentifierToken::KW_CATCH))
			{
				while (!isOperator(pNext, OperatorToken::OP_OPENBRACE) && !isEOF(pNext))
					pNext = next();

				pNext = parseBlock(pNext);
			}
			else syntaxError("expected catch block");

			if (!pFunc)
				pFunc = dynamic_cast<Function*>(currentNameSpace()->lookup(name));
			if (pFunc)
				pFunc->makeInline();
		}
		else syntaxError("expected member initialization or block");
	}
	else
	{
		expectOperator(pNext, OperatorToken::OP_SEMICOLON, ";");
	}
	if (isOperator(pNext, OperatorToken::OP_SEMICOLON))
		pNext = next();
	return pNext;
}

const Token* Parser::parseParameters(const Token* pNext, Function* pFunc)
{
	poco_assert (isOperator(pNext, OperatorToken::OP_OPENPARENT));

	pNext = next();
	while (!isOperator(pNext, OperatorToken::OP_CLOSPARENT) && !isEOF(pNext))
	{
		std::string decl;
		int depth = 0;
		int tdepth = 0;
		while ((depth > 0 || tdepth > 0 || (!isOperator(pNext, OperatorToken::OP_CLOSPARENT) && !isOperator(pNext, OperatorToken::OP_COMMA))) && !isEOF(pNext))
		{
			append(decl, pNext);
			if (isOperator(pNext, OperatorToken::OP_OPENPARENT))
				++depth;
			else if (isOperator(pNext, OperatorToken::OP_CLOSPARENT))
				--depth;
			else if (isOperator(pNext, OperatorToken::OP_LT))
				++tdepth;
			else if (isOperator(pNext, OperatorToken::OP_GT))
				--tdepth;
			pNext = next();
		}
		if (isOperator(pNext, OperatorToken::OP_COMMA))
			pNext = next();
		if (pFunc && decl != "void") // don't add the void parameter, I simply check here to avoid throwing away void*
			pFunc->addParameter(new Parameter(decl, pFunc));
	}
	return pNext;
}

const Token* Parser::parseBlock(const Token* pNext)
{
	poco_assert (isOperator(pNext, OperatorToken::OP_OPENBRACE));

	pNext = next();
	int depth = 1;
	while (depth > 0 && !isEOF(pNext))
	{
		if (isOperator(pNext, OperatorToken::OP_OPENBRACE))
			++depth;
		else if (isOperator(pNext, OperatorToken::OP_CLOSBRACE))
			--depth;
		pNext = next();
	}
	return pNext;
}

const Token* Parser::parseEnum(const Token* pNext)
{
	poco_assert (isKeyword(pNext, IdentifierToken::KW_ENUM));

	_pCurrentSymbol = 0;
	int line = _istr.getCurrentLineNumber();
	pNext = next();
	std::string name;
	if (pNext->is(Token::IDENTIFIER_TOKEN))
	{
		name = pNext->tokenString();
		pNext = next();
	}
	expectOperator(pNext, OperatorToken::OP_OPENBRACE, "{");
	Enum* pEnum = new Enum(name, currentNameSpace());
	addSymbol(pEnum, line);
	pNext = next();
	while (pNext->is(Token::IDENTIFIER_TOKEN))
	{
		pNext = parseEnumValue(pNext, pEnum);
	}
	expectOperator(pNext, OperatorToken::OP_CLOSBRACE, "}");
	pNext = next();
	expectOperator(pNext, OperatorToken::OP_SEMICOLON, ";");
	pNext = next();
	_pCurrentSymbol = 0;
	return pNext;
}

const Token* Parser::parseEnumValue(const Token* pNext, Enum* pEnum)
{
	_pCurrentSymbol = 0;
	_doc.clear();
	int line = _istr.getCurrentLineNumber();
	std::string name = pNext->tokenString();
	std::string value;
	pNext = next();
	if (isOperator(pNext, OperatorToken::OP_ASSIGN))
	{
		pNext = next();
		while (!isOperator(pNext, OperatorToken::OP_COMMA) && !isOperator(pNext, OperatorToken::OP_CLOSBRACE) && !isEOF(pNext))
		{
			append(value, pNext);
			pNext = next();
		}
	}
	EnumValue* pValue = new EnumValue(name, value, pEnum);
	addSymbol(pValue, line, true);
	if (isOperator(pNext, OperatorToken::OP_COMMA))
		pNext = next();
	else if (!isOperator(pNext, OperatorToken::OP_CLOSBRACE))
		syntaxError(", or }");
	return pNext;
}

const Token* Parser::parseIdentifier(const Token* pNext, std::string& id)
{
	poco_assert (pNext->is(Token::IDENTIFIER_TOKEN) || isOperator(pNext, OperatorToken::OP_DBL_COLON));
	id.append(pNext->tokenString());
	if (isOperator(pNext, OperatorToken::OP_DBL_COLON))
	{
		pNext = next();
		if (!pNext->is(Token::IDENTIFIER_TOKEN)) syntaxError("identifier");
		id.append(pNext->tokenString());
	}
	pNext = next();
	while (isOperator(pNext, OperatorToken::OP_DBL_COLON))
	{
		id.append("::");
		pNext = next();
		if (!pNext->is(Token::IDENTIFIER_TOKEN)) syntaxError("identifier");
		id.append(pNext->tokenString());
		pNext = next();
	}
	return pNext;
}

void Parser::addSymbol(Symbol* pSymbol, int lineNumber, bool addGST)
{
	pSymbol->setLineNumber(lineNumber);
	pSymbol->setFile(_currentPath);
	pSymbol->setPackage(_package);
	pSymbol->setLibrary(_library);
	pSymbol->setAccess(_access);
	_pCurrentSymbol = pSymbol;
	if (addGST)
		_gst.insert(NameSpace::SymbolTable::value_type(pSymbol->name(), pSymbol));
	if (!_doc.empty())
		pSymbol->addDocumentation(_doc);
	if (!_attrs.empty())
	{
		Attributes attrs;
		std::istringstream istr(_attrs);
		AttributesParser parser(attrs, istr);
		parser.parse();
		pSymbol->setAttributes(attrs);
		_attrs.clear();
	}
	_doc.clear();
}

void Parser::pushNameSpace(NameSpace* pNameSpace, int lineNumber, bool addGST)
{
	addSymbol(pNameSpace, lineNumber, addGST);
	_nsStack.push_back(pNameSpace);
}

void Parser::popNameSpace()
{
	_nsStack.pop_back();
}

NameSpace* Parser::currentNameSpace() const
{
	return _nsStack.back();
}

const Token* Parser::next()
{
	const Token* pNext = nextPreprocessed();
	while (!_inFile && !isEOF(pNext))
		pNext = nextPreprocessed();
	return pNext;
}

const Token* Parser::nextPreprocessed()
{
	const Token* pNext = nextToken();
	while (pNext->is(Token::PREPROCESSOR_TOKEN))
	{
		std::istringstream pps(pNext->tokenString());
		pps.get();
		Tokenizer ppt(pps);
		const Token* pPPT = ppt.next();
		if (pPPT->tokenString() == "line" || pPPT->is(Token::INTEGER_LITERAL_TOKEN))
		{
			if (!pPPT->is(Token::INTEGER_LITERAL_TOKEN))
				pPPT = ppt.next();
			int line = pPPT->asInteger();
			_istr.setCurrentLineNumber(line);
			pPPT = ppt.next();
			if (pPPT->is(Token::STRING_LITERAL_TOKEN))
			{
				std::string path = pPPT->asString();
				Path p(path);
				p.makeAbsolute();
				_currentPath = p.toString();
				_inFile = (icompare(_path, _currentPath) == 0);
			}
		}
		pNext = nextToken();
	}
	return pNext;
}

const Token* Parser::nextToken()
{
	const Token* pNext = _tokenizer.next();
	while (pNext->is(Token::COMMENT_TOKEN) || pNext->is(Token::SPECIAL_COMMENT_TOKEN))
	{
		if (pNext->is(Token::SPECIAL_COMMENT_TOKEN))
		{
			if (_pCurrentSymbol)
			{
				_pCurrentSymbol->addDocumentation(pNext->asString());
				_doc.clear();
			}
			else if (_inFile)
			{
				if (!_doc.empty()) _doc += "\n";
				_doc += pNext->asString();
			}
		}
		else if (pNext->is(Token::COMMENT_TOKEN) && _inFile)
		{
			const std::string& comment = pNext->tokenString();
			if (comment.compare(0, 3, "//@") == 0)
			{
				_attrs.append(comment.substr(3));
			}
			else if (comment.compare(0, 11, "// Package:") == 0)
			{
				_package = comment.substr(11);
				trimInPlace(_package);
			}
			else if (comment.compare(0, 11, "// Library:") == 0)
			{
				_library = comment.substr(11);
				trimInPlace(_library);
			}
		}
		pNext = _tokenizer.next();
	}
	return pNext;
}

} } // namespace Poco::CppParser


try {
        auto slot = std::stoull(msg.substr(0, space_pos));
        auto host = msg.substr(space_pos + 1, colon_pos - space_pos - 1);
        auto port = std::stoi(msg.substr(colon_pos + 1));

        return {slot, {host, port}};
    } catch (const std::exception &e) {
        throw ProtoError("Invalid ASK error message: " + msg);
    }

/**************************************************************************

 *************************************************************************/

#include "shards.h"

namespace sw {

namespace redis {

RedirectionError::RedirectionError(const std::string &msg): ReplyError(msg) {
    std::tie(_slot, _node) = _parse_error(msg);
}

std::pair<Slot, Node> RedirectionError::_parse_error(const std::string &msg) const {
    auto space_pos = msg.find(" ");
    auto colon_pos = msg.find(":");
    if (space_pos == std::string::npos
            || colon_pos == std::string::npos
            || colon_pos < space_pos) {
        throw ProtoError("Invalid ASK error message: " + msg);
    }


try {
			int value = lua.script(code, sol::script_default_on_error);
			// This will never be reached
			std::cout << value << std::endl;
			c_assert(value == 24);
		}
		catch (const sol::error& err) {
			std::cout << "Something went horribly wrong: thrown error" << "\n\t" << err.what() << std::endl;
		}

#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>

#include "assert.hpp"
#include <iostream>

int main(int, char**) {
	std::cout << "=== script error handling ===" << std::endl;

	sol::state lua;

	std::string code = R"(
bad&$#*$syntax
bad.code = 2
return 24
)";

	/* OPTION 1 */
	// Handling code like this can be robust
	// If you disable exceptions, then obviously you would remove
	// and then rely on the `lua_atpanic` function being called
	// and trapping errors there before exiting the application
	{
		// script_default_on_error throws / panics when the code is bad: trap the error

	}

	/* OPTION 2 */
	// Use the script_pass_on_error handler
	// this simply passes through the protected_function_result,
	// rather than throwing it or calling panic
	// This will check code validity and also whether or not it runs well
	{
		sol::protected_function_result result = lua.script(code, sol::script_pass_on_error);
		c_assert(!result.valid());
		if (!result.valid()) {
			sol::error err = result;
			sol::call_status status = result.status();
			std::cout << "Something went horribly wrong: " << sol::to_string(status) << " error" << "\n\t" << err.what() << std::endl;
		}
	}

	/* OPTION 3 */
	// This is a lower-level, more explicit way to load code
	// This explicitly loads the code, giving you access to any errors
	// plus the load status
	// then, it turns the loaded code into a sol::protected_function
	// which is then called so that the code can run
	// you can then check that too, for any errors
	// The two previous approaches are recommended
	{
		sol::load_result loaded_chunk = lua.load(code);
		c_assert(!loaded_chunk.valid());
		if (!loaded_chunk.valid()) {
			sol::error err = loaded_chunk;
			sol::load_status status = loaded_chunk.status();
			std::cout << "Something went horribly wrong loading the code: " << sol::to_string(status) << " error" << "\n\t" << err.what() << std::endl;
		}
		else {
			// Because the syntax is bad, this will never be reached
			c_assert(false);
			// If there is a runtime error (lua GC memory error, nil access, etc.)
			// it will be caught here
			sol::protected_function script_func = loaded_chunk;
			sol::protected_function_result result = script_func();
			if (!result.valid()) {
				sol::error err = result;
				sol::call_status status = result.status();
				std::cout << "Something went horribly wrong running the code: " << sol::to_string(status) << " error" << "\n\t" << err.what() << std::endl;
			}
		}
	}

	std::cout << std::endl;

	return 0;
}


try
			{
				const MonitorInfo currentMonitor = System::GetCurrentMonitor();
				const Rect workArea = System::GetCurrentMonitor().workArea;
				const Rect windowBounds = SIV3D_ENGINE(Window)->getState().bounds;
				const Point pos = (workArea.pos + (workArea.size - windowBounds.size) / 2);

				SetPos(pos);
			}
			catch (const Error&) {}

//	This file is part of the Siv3D Engine.

# include <Siv3D/Window.hpp>
# include <Siv3D/Utility.hpp>
# include <Siv3D/Error.hpp>
# include <Siv3D/Monitor.hpp>
# include <Siv3D/Scene.hpp>
# include <Siv3D/WindowState.hpp>
# include <Siv3D/Window/IWindow.hpp>
# include <Siv3D/Common/Siv3DEngine.hpp>

namespace s3d
{
	namespace Window
	{
		void SetTitle(const String& title)
		{
			SIV3D_ENGINE(Window)->setWindowTitle(title);
		}

		const String& GetTitle() noexcept
		{
			return SIV3D_ENGINE(Window)->getWindowTitle();
		}

		const WindowState& GetState() noexcept
		{
			return SIV3D_ENGINE(Window)->getState();
		}

		void SetStyle(const WindowStyle style)
		{
			SIV3D_ENGINE(Window)->setStyle(style);
		}

		void SetPos(const Point pos)
		{
			SIV3D_ENGINE(Window)->setPos(pos);
		}

		void Centering()
		{
			SIV3D_ENGINE(Window)->show();

		}

		void Maximize()
		{
			SIV3D_ENGINE(Window)->maximize();
		}

		void Restore()
		{
			SIV3D_ENGINE(Window)->restore();
		}

		void Minimize()
		{
			SIV3D_ENGINE(Window)->minimize();
		}

		bool ResizeVirtual(const Size size, const s3d::Centering centering)
		{
			if ((not InRange(size.x, 1, 8192))
				|| (not InRange(size.y, 1, 8192)))
			{
				throw Error(U"Window::ResizeVirtual(): width and height must be in the range [1, 8192]");
			}

			if (not SIV3D_ENGINE(Window)->resizeByVirtualSize(size))
			{
				return false;
			}

			if (const auto resizeMode = Scene::GetResizeMode();
				resizeMode == ResizeMode::Actual)
			{
				Scene::Resize(SIV3D_ENGINE(Window)->getState().frameBufferSize);
			}
			else if (resizeMode == ResizeMode::Virtual)
			{
				Scene::Resize(size);
			}

			if (centering)
			{
				Centering();
			}

			return true;
		}

		bool ResizeActual(const Size size, const s3d::Centering centering)
		{
			if ((not InRange(size.x, 1, 8192))
				|| (not InRange(size.y, 1, 8192)))
			{
				throw Error(U"Window::ResizeActual(): width and height must be in the range [1, 8192]");
			}

			if (not SIV3D_ENGINE(Window)->resizeByFrameBufferSize(size))
			{
				return false;
			}

			if (const auto resizeMode = Scene::GetResizeMode();
				resizeMode == ResizeMode::Actual)
			{
				Scene::Resize(SIV3D_ENGINE(Window)->getState().frameBufferSize);
			}
			else if (resizeMode == ResizeMode::Virtual)
			{
				Scene::Resize(size);
			}

			if (centering)
			{
				Centering();
			}

			return true;
		}

		void SetMinimumFrameBufferSize(const Size size)
		{
			SIV3D_ENGINE(Window)->setMinimumFrameBufferSize(size);
		}

		void SetFullscreen(const bool fullscreen, const size_t monitorIndex)
		{
			SIV3D_ENGINE(Window)->setFullscreen(fullscreen, monitorIndex);
		}

		void SetToggleFullscreenEnabled(const bool enabled)
		{
			SIV3D_ENGINE(Window)->setToggleFullscreenEnabled(enabled);
		}

		bool IsToggleFullscreenEnabled()
		{
			return SIV3D_ENGINE(Window)->isToggleFullscreenEnabled();
		}
	}

# if SIV3D_PLATFORM(WINDOWS)

	namespace Platform::Windows::Window
	{
		void* GetHWND()
		{
			return SIV3D_ENGINE(Window)->getHandle();
		}
	}

# endif
}


try {
      for (int j = 0; j < 10; ++j) {
        if (a[i] > b[j])
          throw a[i];
      }
      throw a[i];
    } catch (float f) {
      if (f > 0.1)
        throw a[i];
      return; // expected-error {{cannot return from OpenMP region}}
    }

class S {
  int a;
  S() : a(0) {}

public:
  S(int v) : a(v) {}
  S(const S &s) : a(s.a) {}
};

static int sii;
// expected-note@+1 {{defined as threadprivate or thread local}}
#pragma omp threadprivate(sii)
static int globalii;

// Currently, we cannot use "0" for global register variables.
int reg0;

int test_iteration_spaces() {
  const int N = 100;
  float a[N], b[N], c[N];
  int ii, jj, kk;
  float fii;
  double dii;
  register int reg; // expected-warning {{'register' storage class specifier is deprecated}}
#pragma omp parallel
#pragma omp for
  for (int i = 0; i < 10; i += 1) {
    c[i] = a[i] + b[i];
  }
#pragma omp parallel
#pragma omp for
  for (char i = 0; i < 10; i++) {
    c[i] = a[i] + b[i];
  }
#pragma omp parallel
#pragma omp for
  for (char i = 0; i < 10; i += '\1') {
    c[i] = a[i] + b[i];
  }
#pragma omp parallel
#pragma omp for
  for (long long i = 0; i < 10; i++) {
    c[i] = a[i] + b[i];
  }
#pragma omp parallel
// expected-error@+2 {{expression must have integral or unscoped enumeration type, not 'double'}}
#pragma omp for
  for (long long i = 0; i < 10; i += 1.5) {
    c[i] = a[i] + b[i];
  }
#pragma omp parallel
#pragma omp for
  for (long long i = 0; i < 'z'; i += 1u) {
    c[i] = a[i] + b[i];
  }
#pragma omp parallel
// expected-error@+2 {{variable must be of integer or random access iterator type}}
#pragma omp for
  for (float fi = 0; fi < 10.0; fi++) {
    c[(int)fi] = a[(int)fi] + b[(int)fi];
  }
#pragma omp parallel
// expected-error@+2 {{variable must be of integer or random access iterator type}}
#pragma omp for
  for (double fi = 0; fi < 10.0; fi++) {
    c[(int)fi] = a[(int)fi] + b[(int)fi];
  }
#pragma omp parallel
// expected-error@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
#pragma omp for
  for (int &ref = ii; ref < 10; ref++) {
  }
#pragma omp parallel
// expected-error@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
#pragma omp for
  for (int i; i < 10; i++)
    c[i] = a[i];

#pragma omp parallel
// expected-error@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
#pragma omp for
  for (int i = 0, j = 0; i < 10; ++i)
    c[i] = a[i];

#pragma omp parallel
// expected-error@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
#pragma omp for
  for (; ii < 10; ++ii)
    c[ii] = a[ii];

#pragma omp parallel
// expected-warning@+3 {{expression result unused}}
// expected-error@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
#pragma omp for
  for (ii + 1; ii < 10; ++ii)
    c[ii] = a[ii];

#pragma omp parallel
// expected-error@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
#pragma omp for
  for (c[ii] = 0; ii < 10; ++ii)
    c[ii] = a[ii];

#pragma omp parallel
// Ok to skip parenthesises.
#pragma omp for
  for (((ii)) = 0; ii < 10; ++ii)
    c[ii] = a[ii];

#pragma omp parallel
// omp4-error@+2 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', or '>=') of loop variable 'i'}} omp5-error@+2 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', '>=', or '!=') of loop variable 'i'}}
#pragma omp for
  for (int i = 0; i; i++)
    c[i] = a[i];

#pragma omp parallel
// omp4-error@+3 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', or '>=') of loop variable 'i'}} omp5-error@+3 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', '>=', or '!=') of loop variable 'i'}}
// expected-error@+2 {{increment clause of OpenMP for loop must perform simple addition or subtraction on loop variable 'i'}}
#pragma omp for
  for (int i = 0; jj < kk; ii++)
    c[i] = a[i];

#pragma omp parallel
// omp4-error@+2 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', or '>=') of loop variable 'i'}} omp5-error@+2 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', '>=', or '!=') of loop variable 'i'}}
#pragma omp for
  for (int i = 0; !!i; i++)
    c[i] = a[i];

#pragma omp parallel
// omp4-error@+2 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', or '>=') of loop variable 'i'}}
#pragma omp for
  for (int i = 0; i != 1; i++)
    c[i] = a[i];

#pragma omp parallel
// omp4-error@+2 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', or '>=') of loop variable 'i'}} omp5-error@+2 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', '>=', or '!=') of loop variable 'i'}}
#pragma omp for
  for (int i = 0;; i++)
    c[i] = a[i];

#pragma omp parallel
#pragma omp for
  for (int i = 11; i > 10; i--)
    c[i] = a[i];

#pragma omp parallel
#pragma omp for
  for (int i = 0; i < 10; ++i)
    c[i] = a[i];

#pragma omp parallel
#pragma omp for
  for (ii = 0; ii < 10; ++ii)
    c[ii] = a[ii];

#pragma omp parallel
// expected-error@+2 {{increment clause of OpenMP for loop must perform simple addition or subtraction on loop variable 'ii'}}
#pragma omp for
  for (ii = 0; ii < 10; ++jj)
    c[ii] = a[jj];

#pragma omp parallel
// expected-error@+2 {{increment clause of OpenMP for loop must perform simple addition or subtraction on loop variable 'ii'}}
#pragma omp for
  for (ii = 0; ii < 10; ++++ii)
    c[ii] = a[ii];

#pragma omp parallel
// Ok but undefined behavior (in general, cannot check that incr
// is really loop-invariant).
#pragma omp for
  for (ii = 0; ii < 10; ii = ii + ii)
    c[ii] = a[ii];

#pragma omp parallel
// expected-error@+2 {{expression must have integral or unscoped enumeration type, not 'float'}}
#pragma omp for
  for (ii = 0; ii < 10; ii = ii + 1.0f)
    c[ii] = a[ii];

#pragma omp parallel
// Ok - step was converted to integer type.
#pragma omp for
  for (ii = 0; ii < 10; ii = ii + (int)1.1f)
    c[ii] = a[ii];

#pragma omp parallel
// expected-error@+2 {{increment clause of OpenMP for loop must perform simple addition or subtraction on loop variable 'ii'}}
#pragma omp for
  for (ii = 0; ii < 10; jj = ii + 2)
    c[ii] = a[ii];

#pragma omp parallel
// expected-warning@+3 {{relational comparison result unused}}
// expected-error@+2 {{increment clause of OpenMP for loop must perform simple addition or subtraction on loop variable 'ii'}}
#pragma omp for
  for (ii = 0; ii<10; jj> kk + 2)
    c[ii] = a[ii];

#pragma omp parallel
// expected-error@+2 {{increment clause of OpenMP for loop must perform simple addition or subtraction on loop variable 'ii'}}
#pragma omp for
  for (ii = 0; ii < 10;)
    c[ii] = a[ii];

#pragma omp parallel
// expected-warning@+3 {{expression result unused}}
// expected-error@+2 {{increment clause of OpenMP for loop must perform simple addition or subtraction on loop variable 'ii'}}
#pragma omp for
  for (ii = 0; ii < 10; !ii)
    c[ii] = a[ii];

#pragma omp parallel
// expected-error@+2 {{increment clause of OpenMP for loop must perform simple addition or subtraction on loop variable 'ii'}}
#pragma omp for
  for (ii = 0; ii < 10; ii ? ++ii : ++jj)
    c[ii] = a[ii];

#pragma omp parallel
// expected-error@+2 {{increment clause of OpenMP for loop must perform simple addition or subtraction on loop variable 'ii'}}
#pragma omp for
  for (ii = 0; ii < 10; ii = ii < 10)
    c[ii] = a[ii];

#pragma omp parallel
// expected-note@+3 {{loop step is expected to be positive due to this condition}}
// expected-error@+2 {{increment expression must cause 'ii' to increase on each iteration of OpenMP for loop}}
#pragma omp for
  for (ii = 0; ii < 10; ii = ii + 0)
    c[ii] = a[ii];

#pragma omp parallel
// expected-note@+3 {{loop step is expected to be positive due to this condition}}
// expected-error@+2 {{increment expression must cause 'ii' to increase on each iteration of OpenMP for loop}}
#pragma omp for
  for (ii = 0; ii < 10; ii = ii + (int)(0.8 - 0.45))
    c[ii] = a[ii];

#pragma omp parallel
// expected-note@+3 {{loop step is expected to be positive due to this condition}}
// expected-error@+2 {{increment expression must cause 'ii' to increase on each iteration of OpenMP for loop}}
#pragma omp for
  for (ii = 0; (ii) < 10; ii -= 25)
    c[ii] = a[ii];

#pragma omp parallel
// expected-note@+3 {{loop step is expected to be positive due to this condition}}
// expected-error@+2 {{increment expression must cause 'ii' to increase on each iteration of OpenMP for loop}}
#pragma omp for
  for (ii = 0; (ii < 10); ii -= 0)
    c[ii] = a[ii];

#pragma omp parallel
// expected-note@+3 {{loop step is expected to be negative due to this condition}}
// expected-error@+2 {{increment expression must cause 'ii' to decrease on each iteration of OpenMP for loop}}
#pragma omp for
  for (ii = 0; ii > 10; (ii += 0))
    c[ii] = a[ii];

#pragma omp parallel
// expected-note@+3 {{loop step is expected to be positive due to this condition}}
// expected-error@+2 {{increment expression must cause 'ii' to increase on each iteration of OpenMP for loop}}
#pragma omp for
  for (ii = 0; ii < 10; (ii) = (1 - 1) + (ii))
    c[ii] = a[ii];

#pragma omp parallel
// expected-note@+3 {{loop step is expected to be negative due to this condition}}
// expected-error@+2 {{increment expression must cause 'ii' to decrease on each iteration of OpenMP for loop}}
#pragma omp for
  for ((ii = 0); ii > 10; (ii -= 0))
    c[ii] = a[ii];

#pragma omp parallel
// expected-note@+3 {{loop step is expected to be positive due to this condition}}
// expected-error@+2 {{increment expression must cause 'ii' to increase on each iteration of OpenMP for loop}}
#pragma omp for
  for (ii = 0; (ii < 10); (ii -= 0))
    c[ii] = a[ii];

#pragma omp parallel
// expected-error@+3 {{the loop initializer expression depends on the current loop control variable}}
// expected-error@+2 2 {{the loop condition expression depends on the current loop control variable}}
#pragma omp for
  for (ii = ii * 10 + 25; ii < ii / ii - 23; ii += 1)
    c[ii] = a[ii];

// expected-error@+3 {{expected loop invariant expression or '<invariant1> * ii + <invariant2>' kind of expression}}
#pragma omp for collapse(2)
    for (ii = 10 + 25; ii < 1000; ii += 1)
      for (kk = ii * 10 + 25; kk < ii / ii - 23; kk += 1)
        ;

// expected-error@+4 {{expected loop invariant expression or '<invariant1> * ii + <invariant2>' kind of expression}}
#pragma omp for collapse(3)
    for (ii = 10 + 25; ii < 1000; ii += 1)
      for (jj = 10 + 25; jj < 1000; jj += 1)
        for (kk = ii * 10 + 25; kk < jj - 23; kk += 1)
          ;

#pragma omp parallel
// expected-note@+2  {{defined as firstprivate}}
// expected-error@+2 {{loop iteration variable in the associated loop of 'omp for' directive may not be firstprivate, predetermined as private}}
#pragma omp for firstprivate(ii)
  for (ii = 0; ii < 10; ii++)
    c[ii] = a[ii];

#pragma omp parallel
// expected-note@+2  {{defined as linear}}
// expected-error@+2 {{loop iteration variable in the associated loop of 'omp for' directive may not be linear, predetermined as private}}
#pragma omp for linear(ii)
  for (ii = 0; ii < 10; ii++)
    c[ii] = a[ii];

#pragma omp parallel
#pragma omp for private(ii)
  for (ii = 0; ii < 10; ii++)
    c[ii] = a[ii];

#pragma omp parallel
#pragma omp for lastprivate(ii)
  for (ii = 0; ii < 10; ii++)
    c[ii] = a[ii];

#pragma omp parallel
  {
// expected-error@+2 {{loop iteration variable in the associated loop of 'omp for' directive may not be threadprivate or thread local, predetermined as private}}
#pragma omp for
    for (sii = 0; sii < 10; sii += 1)
      c[sii] = a[sii];
  }

#pragma omp parallel
  {
#pragma omp for
    for (reg0 = 0; reg0 < 10; reg0 += 1)
      c[reg0] = a[reg0];
  }

#pragma omp parallel
  {
#pragma omp for
    for (reg = 0; reg < 10; reg += 1)
      c[reg] = a[reg];
  }

#pragma omp parallel
  {
#pragma omp for
    for (globalii = 0; globalii < 10; globalii += 1)
      c[globalii] = a[globalii];
  }

#pragma omp parallel
  {
#pragma omp for collapse(2)
    for (ii = 0; ii < 10; ii += 1)
    for (globalii = 0; globalii < 10; globalii += 1)
      c[globalii] += a[globalii] + ii;
  }

#pragma omp parallel
// omp4-error@+2 {{statement after '#pragma omp for' must be a for loop}}
#pragma omp for
  for (auto &item : a) {
    item = item + 1;
  }

#pragma omp parallel
// expected-note@+3 {{loop step is expected to be positive due to this condition}}
// expected-error@+2 {{increment expression must cause 'i' to increase on each iteration of OpenMP for loop}}
#pragma omp for
  for (unsigned i = 9; i < 10; i--) {
    c[i] = a[i] + b[i];
  }

  int(*lb)[4] = nullptr;
#pragma omp parallel
#pragma omp for
  for (int(*p)[4] = lb; p < lb + 8; ++p) {
  }

#pragma omp parallel
// expected-warning@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
#pragma omp for
  for (int a{0}; a < 10; ++a) {
  }

  return 0;
}

// Iterators allowed in openmp for-loops.
namespace std {
struct random_access_iterator_tag {};
template <class Iter>
struct iterator_traits {
  typedef typename Iter::difference_type difference_type;
  typedef typename Iter::iterator_category iterator_category;
};
template <class Iter>
typename iterator_traits<Iter>::difference_type
distance(Iter first, Iter last) { return first - last; }
}
class Iter0 {
public:
  Iter0() {}
  Iter0(const Iter0 &) {}
  Iter0 operator++() { return *this; }
  Iter0 operator--() { return *this; }
  bool operator<(Iter0 a) { return true; }
};
// expected-note@+2 {{candidate function not viable: no known conversion from 'GoodIter' to 'Iter0' for 1st argument}}
// expected-note@+1 2 {{candidate function not viable: no known conversion from 'Iter1' to 'Iter0' for 1st argument}}
int operator-(Iter0 a, Iter0 b) { return 0; }
class Iter1 {
public:
  Iter1(float f = 0.0f, double d = 0.0) {}
  Iter1(const Iter1 &) {}
  Iter1 operator++() { return *this; }
  Iter1 operator--() { return *this; }
  bool operator<(Iter1 a) { return true; }
  bool operator>=(Iter1 a) { return false; }
};
class GoodIter {
public:
  GoodIter() {}
  GoodIter(const GoodIter &) {}
  GoodIter(int fst, int snd) {}
  GoodIter &operator=(const GoodIter &that) { return *this; }
  GoodIter &operator=(const Iter0 &that) { return *this; }
  GoodIter &operator+=(int x) { return *this; }
  GoodIter &operator-=(int x) { return *this; }
  explicit GoodIter(void *) {}
  GoodIter operator++() { return *this; }
  GoodIter operator--() { return *this; }
  bool operator!() { return true; }
  bool operator<(GoodIter a) { return true; }
  bool operator<=(GoodIter a) { return true; }
  bool operator>=(GoodIter a) { return false; }
  typedef int difference_type;
  typedef std::random_access_iterator_tag iterator_category;
};
class GoodIter1 {
public:
  GoodIter1() {}
  GoodIter1(const GoodIter1 &) {}
  GoodIter1 &operator++(int) { return *this; }
  GoodIter1 &operator=(const GoodIter1 &that) { return *this; }
  GoodIter1 &operator+=(int x) { return *this; }
  friend long operator-(const GoodIter1 &, const GoodIter1 &);
  GoodIter1 &operator-(int) { return *this; }
  bool operator<(GoodIter1 a) { return true; }
  typedef int difference_type;
  typedef std::random_access_iterator_tag iterator_category;
};
// expected-note@+2 {{candidate function not viable: no known conversion from 'const Iter0' to 'GoodIter' for 2nd argument}}
// expected-note@+1 2 {{candidate function not viable: no known conversion from 'Iter1' to 'GoodIter' for 1st argument}}
int operator-(GoodIter a, GoodIter b) { return 0; }
// expected-note@+1 3 {{candidate function not viable: requires single argument 'a', but 2 arguments were provided}}
GoodIter operator-(GoodIter a) { return a; }
// expected-note@+2 {{candidate function not viable: no known conversion from 'const Iter0' to 'int' for 2nd argument}}
// expected-note@+1 2 {{candidate function not viable: no known conversion from 'Iter1' to 'GoodIter' for 1st argument}}
GoodIter operator-(GoodIter a, int v) { return GoodIter(); }
// expected-note@+1 2 {{candidate function not viable: no known conversion from 'Iter0' to 'GoodIter' for 1st argument}}
GoodIter operator+(GoodIter a, int v) { return GoodIter(); }
// expected-note@+2 {{candidate function not viable: no known conversion from 'GoodIter' to 'int' for 1st argument}}
// expected-note@+1 2 {{candidate function not viable: no known conversion from 'Iter1' to 'int' for 1st argument}}
GoodIter operator-(int v, GoodIter a) { return GoodIter(); }
// expected-note@+1 2 {{candidate function not viable: no known conversion from 'Iter0' to 'int' for 1st argument}}
GoodIter operator+(int v, GoodIter a) { return GoodIter(); }

int test_with_random_access_iterator() {
  GoodIter begin, end;
  Iter0 begin0, end0;
#pragma omp parallel
#pragma omp for
  for (GoodIter I = begin; I < end; ++I)
    ++I;
#pragma omp parallel
// expected-error@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
#pragma omp for
  for (GoodIter &I = begin; I < end; ++I)
    ++I;
#pragma omp parallel
#pragma omp for
  for (GoodIter I = begin; I >= end; --I)
    ++I;
#pragma omp parallel
// expected-warning@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
#pragma omp for
  for (GoodIter I(begin); I < end; ++I)
    ++I;
#pragma omp parallel
// expected-warning@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
#pragma omp for
  for (GoodIter I(nullptr); I < end; ++I)
    ++I;
#pragma omp parallel
// expected-warning@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
#pragma omp for
  for (GoodIter I(0); I < end; ++I)
    ++I;
#pragma omp parallel
// expected-warning@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
#pragma omp for
  for (GoodIter I(1, 2); I < end; ++I)
    ++I;
#pragma omp parallel
#pragma omp for
  for (begin = GoodIter(0); begin < end; ++begin)
    ++begin;
// expected-error@+4 {{invalid operands to binary expression ('GoodIter' and 'const Iter0')}}
// expected-error@+3 {{could not calculate number of iterations calling 'operator-' with upper and lower loop bounds}}
#pragma omp parallel
#pragma omp for
  for (begin = begin0; begin < end; ++begin)
    ++begin;
#pragma omp parallel
// expected-error@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
#pragma omp for
  for (++begin; begin < end; ++begin)
    ++begin;
#pragma omp parallel
#pragma omp for
  for (begin = end; begin < end; ++begin)
    ++begin;
#pragma omp parallel
// omp4-error@+2 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', or '>=') of loop variable 'I'}} omp5-error@+2 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', '>=', or '!=') of loop variable 'I'}}
#pragma omp for
  for (GoodIter I = begin; I - I; ++I)
    ++I;
#pragma omp parallel
// omp4-error@+2 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', or '>=') of loop variable 'I'}} omp5-error@+2 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', '>=', or '!=') of loop variable 'I'}}
#pragma omp for
  for (GoodIter I = begin; begin < end; ++I)
    ++I;
#pragma omp parallel
// omp4-error@+2 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', or '>=') of loop variable 'I'}} omp5-error@+2 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', '>=', or '!=') of loop variable 'I'}}
#pragma omp for
  for (GoodIter I = begin; !I; ++I)
    ++I;
#pragma omp parallel
// expected-note@+3 {{loop step is expected to be negative due to this condition}}
// expected-error@+2 {{increment expression must cause 'I' to decrease on each iteration of OpenMP for loop}}
#pragma omp for
  for (GoodIter I = begin; I >= end; I = I + 1)
    ++I;
#pragma omp parallel
#pragma omp for
  for (GoodIter I = begin; I >= end; I = I - 1)
    ++I;
#pragma omp parallel
// expected-error@+2 {{increment clause of OpenMP for loop must perform simple addition or subtraction on loop variable 'I'}}
#pragma omp for
  for (GoodIter I = begin; I >= end; I = -I)
    ++I;
#pragma omp parallel
// expected-note@+3 {{loop step is expected to be negative due to this condition}}
// expected-error@+2 {{increment expression must cause 'I' to decrease on each iteration of OpenMP for loop}}
#pragma omp for
  for (GoodIter I = begin; I >= end; I = 2 + I)
    ++I;
#pragma omp parallel
// expected-error@+2 {{increment clause of OpenMP for loop must perform simple addition or subtraction on loop variable 'I'}}
#pragma omp for
  for (GoodIter I = begin; I >= end; I = 2 - I)
    ++I;
// In the following example, we cannot update the loop variable using '+='
// expected-error@+3 {{invalid operands to binary expression ('Iter0' and 'int')}}
#pragma omp parallel
#pragma omp for
  for (Iter0 I = begin0; I < end0; ++I)
    ++I;
#pragma omp parallel
// Initializer is constructor without params.
// expected-error@+3 {{invalid operands to binary expression ('Iter0' and 'int')}}
// expected-warning@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
#pragma omp for
  for (Iter0 I; I < end0; ++I)
    ++I;
  Iter1 begin1, end1;
// expected-error@+4 {{invalid operands to binary expression ('Iter1' and 'Iter1')}}
// expected-error@+3 {{could not calculate number of iterations calling 'operator-' with upper and lower loop bounds}}
#pragma omp parallel
#pragma omp for
  for (Iter1 I = begin1; I < end1; ++I)
    ++I;
#pragma omp parallel
// expected-note@+3 {{loop step is expected to be negative due to this condition}}
// expected-error@+2 {{increment expression must cause 'I' to decrease on each iteration of OpenMP for loop}}
#pragma omp for
  for (Iter1 I = begin1; I >= end1; ++I)
    ++I;
#pragma omp parallel
// expected-error@+5 {{invalid operands to binary expression ('Iter1' and 'float')}}
// expected-error@+4 {{could not calculate number of iterations calling 'operator-' with upper and lower loop bounds}}
// Initializer is constructor with all default params.
// expected-warning@+2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
#pragma omp for
  for (Iter1 I; I < end1; ++I) {
  }
  GoodIter1 I1, E1;
// expected-error@+4 {{expected an integer or a pointer type of the outer loop counter 'I' for non-rectangular nests}}
// expected-error@+4 {{expected an integer or a pointer type of the outer loop counter 'I' for non-rectangular nests}}
#pragma omp for collapse(3)
  for (GoodIter1 I = I1; I < E1; I++) // expected-note 2 {{'I' declared here}}
    for (int i = (I - I1) * 10 + 25; i < 23; i += 1)
      for (int j = 10 + 25; j < 23 + (I - E1); j += 1)
        ;

#pragma omp for
  for (GoodIter1 I = I1; I < E1; I++)
    ;
  return 0;
}

template <typename IT, int ST>
class TC {
  int ii, iii, kk;
public:
  enum { myconstant = 42 };
  int ub();
  int dotest_lt(IT begin, IT end) {
#pragma omp parallel
// expected-error@+3 3 {{the loop initializer expression depends on the current loop control variable}}
// expected-error@+2 6 {{the loop condition expression depends on the current loop control variable}}
#pragma omp for
  for (ii = ii * 10 + 25; ii < ii / ii - 23; ii += 1)
    ;

// Check that member function calls and enum constants in the condition is
#pragma omp for
  for (ii = 0; ii < ub() + this->myconstant; ii += 1) // expected-no-error
    ;

#pragma omp parallel
// expected-error@+4 2 {{expected loop invariant expression or '<invariant1> * ii + <invariant2>' kind of expression}}
// expected-error@+3 {{expected loop invariant expression or '<invariant1> * TC::ii + <invariant2>' kind of expression}}
#pragma omp for collapse(2)
    for (ii = 10 + 25; ii < 1000; ii += 1)
      for (iii = ii * 10 + 25; iii < ii / ii - 23; iii += 1)
        ;

#pragma omp parallel
// expected-error@+5 2 {{expected loop invariant expression or '<invariant1> * ii + <invariant2>' kind of expression}}
// expected-error@+4 {{expected loop invariant expression or '<invariant1> * TC::ii + <invariant2>' kind of expression}}
// expected-error@+4 {{expected loop invariant expression or '<invariant1> * TC::ii + <invariant2>' kind of expression}}
#pragma omp for collapse(3)
    for (ii = 10 + 25; ii < 1000; ii += 1)
      for (iii = ii * 10 + 25; iii < ii / ii - 23; iii += 1)
        for (kk = ii * 10 + 25; kk < iii - 23; kk += 1)
          ;

#pragma omp parallel
// expected-note@+3 {{loop step is expected to be positive due to this condition}}
// expected-error@+2 {{increment expression must cause 'I' to increase on each iteration of OpenMP for loop}}
#pragma omp for
    for (IT I = begin; I < end; I = I + ST) {
      ++I;
    }
#pragma omp parallel
// expected-note@+3 {{loop step is expected to be positive due to this condition}}
// expected-error@+2 {{increment expression must cause 'I' to increase on each iteration of OpenMP for loop}}
#pragma omp for
    for (IT I = begin; I <= end; I += ST) {
      ++I;
    }
#pragma omp parallel
#pragma omp for
    for (IT I = begin; I < end; ++I) {
      ++I;
    }
  }

  static IT step() {
    return IT(ST);
  }
};
template <typename IT, int ST = 0>
int dotest_gt(IT begin, IT end) {
#pragma omp parallel
// expected-note@+3 2 {{loop step is expected to be negative due to this condition}}
// expected-error@+2 2 {{increment expression must cause 'I' to decrease on each iteration of OpenMP for loop}}
#pragma omp for
  for (IT I = begin; I >= end; I = I + ST) {
    ++I;
  }
#pragma omp parallel
// expected-note@+3 2 {{loop step is expected to be negative due to this condition}}
// expected-error@+2 2 {{increment expression must cause 'I' to decrease on each iteration of OpenMP for loop}}
#pragma omp for
  for (IT I = begin; I >= end; I += ST) {
    ++I;
  }

#pragma omp parallel
// expected-note@+3 {{loop step is expected to be negative due to this condition}}
// expected-error@+2 {{increment expression must cause 'I' to decrease on each iteration of OpenMP for loop}}
#pragma omp for
  for (IT I = begin; I >= end; ++I) {
    ++I;
  }

#pragma omp parallel
#pragma omp for
  for (IT I = begin; I < end; I += TC<int, ST>::step()) {
    ++I;
  }
}

void test_with_template() {
  GoodIter begin, end;
  TC<GoodIter, 100> t1;
  TC<GoodIter, -100> t2;
  t1.dotest_lt(begin, end);         // expected-note {{in instantiation of member function 'TC<GoodIter, 100>::dotest_lt' requested here}}
  t2.dotest_lt(begin, end);         // expected-note {{in instantiation of member function 'TC<GoodIter, -100>::dotest_lt' requested here}}
  dotest_gt(begin, end);            // expected-note {{in instantiation of function template specialization 'dotest_gt<GoodIter, 0>' requested here}}
  dotest_gt<unsigned, 10>(0, 100);  // expected-note {{in instantiation of function template specialization 'dotest_gt<unsigned int, 10>' requested here}}
}

void test_loop_break() {
  const int N = 100;
  float a[N], b[N], c[N];
#pragma omp parallel
#pragma omp for
  for (int i = 0; i < 10; i++) {
    c[i] = a[i] + b[i];
    for (int j = 0; j < 10; ++j) {
      if (a[i] > b[j])
        break; // OK in nested loop
    }
    switch (i) {
    case 1:
      b[i]++;
      break;
    default:
      break;
    }
    if (c[i] > 10)
      break; // expected-error {{'break' statement cannot be used in OpenMP for loop}}

    if (c[i] > 11)
      break; // expected-error {{'break' statement cannot be used in OpenMP for loop}}
  }

#pragma omp parallel
#pragma omp for
  for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 10; j++) {
      c[i] = a[i] + b[i];
      if (c[i] > 10) {
        if (c[i] < 20) {
          break; // OK
        }
      }
    }
  }
}

void test_loop_eh() {
  const int N = 100;
  float a[N], b[N], c[N];
#pragma omp parallel
#pragma omp for
  for (int i = 0; i < 10; i++) {
    c[i] = a[i] + b[i];

    switch (i) {
    case 1:
      b[i]++;
      break;
    default:
      break;
    }
    for (int j = 0; j < 10; j++) {
      if (c[i] > 10)
        throw c[i];
    }
  }
  if (c[9] > 10)
    throw c[9]; // OK

#pragma omp parallel
#pragma omp for
  for (int i = 0; i < 10; ++i) {
    struct S {
      void g() { throw 0; }
    };
  }
}

void test_loop_firstprivate_lastprivate() {
  S s(4);
#pragma omp parallel
#pragma omp for lastprivate(s) firstprivate(s)
  for (int i = 0; i < 16; ++i)
    ;
}

void test_ordered() {
#pragma omp parallel
#pragma omp for ordered ordered // expected-error {{directive '#pragma omp for' cannot contain more than one 'ordered' clause}}
  for (int i = 0; i < 16; ++i)
    ;
}

void test_nowait() {
#pragma omp parallel
#pragma omp for nowait nowait // expected-error {{directive '#pragma omp for' cannot contain more than one 'nowait' clause}}
  for (int i = 0; i < 16; ++i)
    ;
}

void test_static_data_member() {
#pragma omp parallel
#pragma omp for
  for (int i = 0; i < 16; ++i) {
    class X {
      static int x; // expected-error {{static data member 'x' not allowed in local class 'X'}}
    };
  }
}