// Copyright (c) 2017-2020 The Rhombus Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <test/util/setup_common.h>

#include <crypto/sha256.h>
#include <key/stealth.h>

#include <secp256k1.h>
#include <secp256k1_rangeproof.h>
#include <secp256k1_mlsag.h>
#include <stdint.h>

#include <boost/test/unit_test.hpp>

BOOST_FIXTURE_TEST_SUITE(ringct_tests, BasicTestingSetup)

class CTxOutValueTest
{
public:
    CPubKey pk;
    secp256k1_pedersen_commitment commitment;
    std::vector<unsigned char> vchRangeproof;
    std::vector<unsigned char> vchNonceCommitment;
};

static int memncmp(uint8_t *p, uint8_t c, size_t len)
{
    for (size_t k = 0; k < len; ++k, ++p) {
        if (*p < c) {
            return -1;
        }
        if (*p > c) {
            return 1;
        }
    }
    return 0;
}

struct S32Bytes {
  uint8_t d[32];
};

int testCommitmentSum(secp256k1_context *ctx, CAmount nValueIn,
    std::vector<CAmount> &amountsOut, CAmount nValueOutPlain, size_t nCols,
    bool fPass, bool fUnblindedOutputs=false, bool fUnblindedInputs=false)
{
    SeedInsecureRand();
    int rv;
    std::vector<CTxOutValueTest> txins(1);

    std::vector<CKey> kto_outs(amountsOut.size());
    std::vector<CPubKey> pkto_outs(amountsOut.size());
    std::vector<CTxOutValueTest> txouts(amountsOut.size());
    std::vector<S32Bytes> blind(txouts.size());

    std::vector<CKey> k_ins(1);
    InsecureNewKey(k_ins[0], true);

    txins[0].pk = k_ins[0].GetPubKey();
    std::vector<const uint8_t*> pBlinds;
    uint8_t blindsin[1][32];

    if (fUnblindedInputs) {
        memset(&blindsin[0][0], 0, 32);
    } else {
        InsecureRandBytes(&blindsin[0][0], 32);
    }

    pBlinds.push_back(&blindsin[0][0]);

    BOOST_CHECK(secp256k1_pedersen_commit(ctx, &txins[0].commitment, &blindsin[0][0], (uint64_t)nValueIn, &secp256k1_generator_const_h, &secp256k1_generator_const_g));

    size_t nBlinded = 0;
    for (size_t k = 0; k < txouts.size(); ++k) {
        InsecureNewKey(kto_outs[k], true);
        pkto_outs[k] = kto_outs[k].GetPubKey();

        CTxOutValueTest &txout = txouts[k];

        if (fUnblindedOutputs) {
            memset(&blind[nBlinded].d[0], 0, 32);
        } else {
            InsecureRandBytes(&blind[nBlinded].d[0], 32);
        }
        pBlinds.push_back(&blind[nBlinded++].d[0]);

        BOOST_CHECK(secp256k1_pedersen_commit(ctx, &txout.commitment, (uint8_t*)pBlinds.back(), (uint64_t)amountsOut[k], &secp256k1_generator_const_h, &secp256k1_generator_const_g));

        // Generate ephemeral key for ECDH nonce generation
        CKey ephemeral_key;
        InsecureNewKey(ephemeral_key, true);
        CPubKey ephemeral_pubkey = ephemeral_key.GetPubKey();
        txout.vchNonceCommitment.resize(33);
        memcpy(&txout.vchNonceCommitment[0], &ephemeral_pubkey[0], 33);

        // Generate nonce
        uint256 nonce = ephemeral_key.ECDH(pkto_outs[k]);
        CSHA256().Write(nonce.begin(), 32).Finalize(nonce.begin());
        // Create range proof
        size_t nRangeProofLen = 5134;

        // TODO: smarter min_value selection

        txout.vchRangeproof.resize(nRangeProofLen);

        uint64_t min_value = 0;
        int ct_exponent = 2;
        int ct_bits = 32;

        BOOST_REQUIRE(secp256k1_rangeproof_sign(ctx,
            &txout.vchRangeproof[0], &nRangeProofLen,
            min_value, &txout.commitment,
            pBlinds.back(), nonce.begin(),
            ct_exponent, ct_bits,
            (uint64_t)amountsOut[k],
            nullptr, 0,
            nullptr, 0,
            secp256k1_generator_h));

        txout.vchRangeproof.resize(nRangeProofLen);
    }

    size_t nRows = txins.size() + 1; // last row sums commitments
    size_t index = rand() % nCols;

    uint8_t *m = (uint8_t*) calloc(nCols * nRows, 33); // m[(col+(cols*row))*33]
    BOOST_REQUIRE(m);

    size_t haveFee = (nValueOutPlain != 0) ? 1 : 0;
    std::vector<const uint8_t*> pcm_in(nCols * txins.size()); // pcm_in[col+(cols*row)]
    std::vector<const uint8_t*> pcm_out(txouts.size() + haveFee);

    std::vector<CTxOutValueTest> tx_mixin(txins.size() * (nCols-1));

    size_t iMix = 0;
    for (size_t k = 0; k < nRows-1; ++k)
    for (size_t i = 0; i < nCols; ++i) {
        if (i == index) {
            memcpy(&m[(i+k*nCols)*33], txins[k].pk.begin(), 33);
            pcm_in[i+k*nCols] = txins[k].commitment.data;
            continue;
        }

        // Make fake input
        CKey key;
        InsecureNewKey(key, true);

        CTxOutValueTest &mixin = tx_mixin[iMix++];
        mixin.pk = key.GetPubKey();
        CAmount nValue = rand() % (5000 * COIN);
        uint8_t blind[32];
        //GetStrongRandBytes(&blind[0], 32);
        InsecureRandBytes(&blind[0], 32);
        BOOST_CHECK(secp256k1_pedersen_commit(ctx, &mixin.commitment, blind, (uint64_t)nValue, &secp256k1_generator_const_h, &secp256k1_generator_const_g));

        memcpy(&m[(i+k*nCols)*33], mixin.pk.begin(), 33);
        pcm_in[i+k*nCols] = mixin.commitment.data;
    }

    uint8_t blindSum[32];
    memset(blindSum, 0, 32);

    for (size_t k = 0; k < txouts.size(); ++k) {
        pcm_out[k] = txouts[k].commitment.data;
    }

    secp256k1_pedersen_commitment feeCommitment;
    if (nValueOutPlain != 0) {
        uint8_t zeroBlind[32];
        memset(zeroBlind, 0, 32);

        BOOST_CHECK(secp256k1_pedersen_commit(ctx, &feeCommitment, &zeroBlind[0], (uint64_t)nValueOutPlain, &secp256k1_generator_const_h, &secp256k1_generator_const_g));
        pcm_out[txouts.size()] = feeCommitment.data;
    }

    rv = secp256k1_prepare_mlsag(m, blindSum,
        txouts.size()+haveFee, txouts.size(), nCols, nRows,
        &pcm_in[0], &pcm_out[0], &pBlinds[0]);
    BOOST_REQUIRE(0 == rv);

    if (memncmp(blindSum, 0, 32) == 0) {
        BOOST_REQUIRE(fPass == (0 == memncmp(&m[(index+(nRows-1)*nCols)*33], 0, 33))); // point at infinity encoded as 0*33
        free(m);
        return 0;
    }

    CKey kTest;
    kTest.Set(blindSum, blindSum+32, true);
    BOOST_REQUIRE(kTest.IsValid());

    CPubKey pkTestFromCommitmentSum;
    pkTestFromCommitmentSum.Set(&m[(index+(nRows-1)*nCols)*33], &m[(index+(nRows-1)*nCols)*33]+33);
    BOOST_REQUIRE(pkTestFromCommitmentSum.IsValid());

    CPubKey pkTestFromBlindKey;
    pkTestFromBlindKey = kTest.GetPubKey();
    BOOST_REQUIRE(pkTestFromBlindKey.IsValid());

    BOOST_REQUIRE(fPass == (pkTestFromCommitmentSum == pkTestFromBlindKey));

    free(m);

    return 0;
};

BOOST_AUTO_TEST_CASE(ringct_test_commitment_sums)
{
    secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);

    std::vector<CAmount> amountsOut(2);
    amountsOut[0] = 30 * COIN;
    amountsOut[1] = -10 * COIN;
    testCommitmentSum(ctx, 20 * COIN, amountsOut, 0, 3, false);

    amountsOut[0] = 10 * COIN;
    amountsOut[1] = 10 * COIN;
    testCommitmentSum(ctx, 20 * COIN, amountsOut, 0, 3, true);

    amountsOut[0] = 10 * COIN;
    amountsOut[1] = 8 * COIN;
    testCommitmentSum(ctx, 20 * COIN, amountsOut, 2 * COIN, 3, true);
    testCommitmentSum(ctx, 20 * COIN, amountsOut, 3 * COIN, 3, false);

    amountsOut[0] = 10 * COIN;
    amountsOut[1] = 8 * COIN;
    testCommitmentSum(ctx, 20 * COIN, amountsOut, 2 * COIN, 3, true, true);

    amountsOut.resize(3);
    amountsOut[0] = 5 * COIN;
    amountsOut[1] = 8 * COIN;
    amountsOut[2] = 5 * COIN;
    testCommitmentSum(ctx, 20 * COIN, amountsOut, 2 * COIN, 5, true, true);
    testCommitmentSum(ctx, 20 * COIN, amountsOut, 2 * COIN, 5, true, true, true);
    testCommitmentSum(ctx, 20 * COIN, amountsOut, 2 * COIN, 5, true, false, true);
    testCommitmentSum(ctx, 20 * COIN, amountsOut, 2 * COIN, 5, true);

    testCommitmentSum(ctx, 20 * COIN, amountsOut, 1 * COIN, 5, false, true, true);

    secp256k1_context_destroy(ctx);
}

BOOST_AUTO_TEST_CASE(ringct_test)
{
    secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);

    SeedInsecureRand();
    uint32_t rseed = InsecureRand32();
    //GetStrongRandBytes((uint8_t*)&rseed, 4);
    srand(rseed);

    std::vector<CTxOutValueTest> txins(1);

    std::vector<CKey> k_ins(1);
    InsecureNewKey(k_ins[0], true);

    txins[0].pk = k_ins[0].GetPubKey();
    std::vector<const uint8_t*> pBlinds;
    uint8_t blindsin[1][32];

    //GetStrongRandBytes(&blindsin[0][0], 32);
    InsecureRandBytes(&blindsin[0][0], 32);
    pBlinds.push_back(&blindsin[0][0]);

    CAmount nValueIn = 45.69 * COIN;
    BOOST_CHECK(secp256k1_pedersen_commit(ctx, &txins[0].commitment, &blindsin[0][0], (uint64_t)nValueIn, &secp256k1_generator_const_h, &secp256k1_generator_const_g));

    std::vector<CStealthAddress> txout_addrs(2);
    std::vector<CTxOutValueTest> txouts(2);

    std::vector<CAmount> amount_outs(2);
    amount_outs[0] = 5.69 * COIN;
    amount_outs[1] = 40 * COIN;

    std::vector<CKey> kto_outs(2);
    std::vector<CPubKey> pkto_outs(2);
    std::vector<S32Bytes> blind(txouts.size());

    size_t nBlinded = 0;
    for (size_t k = 0; k < txouts.size(); ++k) {
        InsecureNewKey(kto_outs[k], true);
        pkto_outs[k] = kto_outs[k].GetPubKey();

        CTxOutValueTest &txout = txouts[k];

        //GetStrongRandBytes(&blind[nBlinded].d[0], 32);
        InsecureRandBytes(&blind[nBlinded].d[0], 32);
        pBlinds.push_back(&blind[nBlinded++].d[0]);

        BOOST_CHECK(secp256k1_pedersen_commit(ctx, &txout.commitment, (uint8_t*)pBlinds.back(), (uint64_t)amount_outs[k], &secp256k1_generator_const_h, &secp256k1_generator_const_g));

        // Generate ephemeral key for ECDH nonce generation
        CKey ephemeral_key;
        InsecureNewKey(ephemeral_key, true);
        CPubKey ephemeral_pubkey = ephemeral_key.GetPubKey();
        txout.vchNonceCommitment.resize(33);
        memcpy(&txout.vchNonceCommitment[0], &ephemeral_pubkey[0], 33);

        // Generate nonce
        uint256 nonce = ephemeral_key.ECDH(pkto_outs[k]);
        CSHA256().Write(nonce.begin(), 32).Finalize(nonce.begin());
        // Create range proof
        size_t nRangeProofLen = 5134;

        // TODO: smarter min_value selection

        txout.vchRangeproof.resize(nRangeProofLen);

        uint64_t min_value = 0;
        int ct_exponent = 2;
        int ct_bits = 32;

        BOOST_CHECK(secp256k1_rangeproof_sign(ctx,
            &txout.vchRangeproof[0], &nRangeProofLen,
            min_value, &txout.commitment,
            pBlinds.back(), nonce.begin(),
            ct_exponent, ct_bits,
            (uint64_t)amount_outs[k],
            nullptr, 0,
            nullptr, 0,
            secp256k1_generator_h));

        txout.vchRangeproof.resize(nRangeProofLen);
    }

    size_t nRows = txins.size() + 1; // last row sums commitments
    size_t nCols = 1 + (rand() % 32); // ring size

    size_t index = rand() % nCols;

    uint8_t *m = (uint8_t*) calloc(nCols * nRows, 33); // m[(col+(cols*row))*33]
    BOOST_REQUIRE(m);

    std::vector<const uint8_t*> pcm_in(nCols * txins.size()); // pcm_in[col+(cols*row)]
    std::vector<const uint8_t*> pcm_out(txouts.size());

    std::vector<CTxOutValueTest> tx_mixin(txins.size() * (nCols-1));

    size_t iMix = 0;
    for (size_t k = 0; k < nRows-1; ++k)
    for (size_t i = 0; i < nCols; ++i) {
        if (i == index) {
            memcpy(&m[(i+k*nCols)*33], txins[k].pk.begin(), 33);
            pcm_in[i+k*nCols] = txins[k].commitment.data;
            continue;
        }

        // Make fake input
        CKey key;
        InsecureNewKey(key, true);

        CTxOutValueTest &mixin = tx_mixin[iMix++];
        mixin.pk = key.GetPubKey();
        CAmount nValue = rand() % (5000 * COIN);
        uint8_t blind[32];
        //GetStrongRandBytes(&blind[0], 32);
        InsecureRandBytes(&blind[0], 32);
        BOOST_CHECK(secp256k1_pedersen_commit(ctx, &mixin.commitment, blind, (uint64_t)nValue, &secp256k1_generator_const_h, &secp256k1_generator_const_g));

        memcpy(&m[(i+k*nCols)*33], mixin.pk.begin(), 33);
        pcm_in[i+k*nCols] = mixin.commitment.data;
    }

    std::vector<const uint8_t*> sk(nRows); // pass in pointer to secret keys to keep them in secured memory

    for (size_t k = 0; k < txins.size(); ++k) {
        sk[k] = k_ins[k].begin();
    }
    uint8_t blindSum[32];
    memset(blindSum, 0, 32);
    sk[nRows-1] = blindSum;

    for (size_t k = 0; k < txouts.size(); ++k) {
        pcm_out[k] = txouts[k].commitment.data;
    }
    BOOST_CHECK(0 == secp256k1_prepare_mlsag(m, blindSum,
        txouts.size(), txouts.size(), nCols, nRows,
        &pcm_in[0], &pcm_out[0], &pBlinds[0]));
    uint8_t randSeed[32];
    InsecureRandBytes(randSeed, 32);
    //GetStrongRandBytes(randSeed, 32);
    uint8_t preimage[32];
    InsecureRandBytes(preimage, 32);
    //GetStrongRandBytes(preimage, 32);

    uint8_t pc[32];
    std::vector<uint8_t> ki(33 * txins.size());
    std::vector<uint8_t> ss(nCols * nRows * 32);

    BOOST_CHECK(0 == secp256k1_generate_mlsag(ctx, &ki[0], pc, &ss[0],
        randSeed, preimage, nCols, nRows, index,
        &sk[0], m));
    BOOST_CHECK(0 == secp256k1_verify_mlsag(ctx,
        preimage, nCols, nRows,
        m, &ki[0], pc, &ss[0]));

    free(m);

    for (size_t k = 0; k < txouts.size(); ++k) {
        CTxOutValueTest &txout = txouts[k];

        int rexp;
        int rmantissa;
        uint64_t min_value;
        uint64_t max_value;

        BOOST_CHECK(secp256k1_rangeproof_info(ctx,
            &rexp, &rmantissa,
            &min_value, &max_value,
            &txout.vchRangeproof[0], txout.vchRangeproof.size()) == 1);

        min_value = 0;
        max_value = 0;
        BOOST_CHECK(1 == secp256k1_rangeproof_verify(ctx, &min_value, &max_value,
            &txout.commitment, txout.vchRangeproof.data(), txout.vchRangeproof.size(),
            nullptr, 0,
            secp256k1_generator_h));

        CPubKey ephemeral_key(txout.vchNonceCommitment);
        BOOST_CHECK(ephemeral_key.IsValid());
        uint256 nonce = kto_outs[k].ECDH(ephemeral_key);
        CSHA256().Write(nonce.begin(), 32).Finalize(nonce.begin());

        uint8_t blindOut[32];
        unsigned char msg[4096];
        size_t msg_size = sizeof(msg);
        uint64_t amountOut;
        BOOST_CHECK(secp256k1_rangeproof_rewind(ctx,
            blindOut, &amountOut, msg, &msg_size, nonce.begin(),
            &min_value, &max_value,
            &txout.commitment, txout.vchRangeproof.data(), txout.vchRangeproof.size(),
            nullptr, 0,
            secp256k1_generator_h));
    }

    secp256k1_context_destroy(ctx);
}

static int GetDeterministicBytes(uint8_t *p, size_t len)
{
    for (size_t k = 0; k < len; ++k) {
        *(p+k) = rand() % 256;
    }
    return 0;
};

static int GetBytes(uint8_t *p, size_t len, bool fDeterministic)
{
    if (fDeterministic) {
        GetDeterministicBytes(p, len);
    } else {
        GetStrongRandBytes(p, len);
    }
    return 0;
};

int doTest(secp256k1_context *ctx, size_t nInputs, size_t nOutputs, CAmount nFee, bool fDeterministic)
{
    uint8_t tmp[64];
    std::vector<const uint8_t*> pBlinds;
    std::vector<CTxOutValueTest> txins(nInputs);
    std::vector<CKey> kIns(nInputs);

    std::vector<S32Bytes> blindsIn(nInputs);

    // Make inputs
    CAmount nInputSum = 0;
    for (size_t k = 0; k < nInputs; ++k) {
        CAmount nValueIn = rand() % 4 == 0 ? 0 : nFee * (rand() % 20000);

        // Can't use MakeNewKey if fDeterministic
        GetBytes(tmp, 32, fDeterministic);
        kIns[k].Set(tmp, true);
        txins[k].pk = kIns[k].GetPubKey();

        GetBytes(&blindsIn[k].d[0], 32, fDeterministic);
        pBlinds.push_back(&blindsIn[k].d[0]);

        // Make sure there's always enough coin
        if (k == nInputs-1
            && nInputSum < nFee * 2) {
            nValueIn = nFee * 2 + nFee * (rand() % 20000);
        }
        nInputSum += nValueIn;

        BOOST_CHECK(secp256k1_pedersen_commit(ctx, &txins[k].commitment, &blindsIn[k].d[0], (uint64_t)nValueIn, &secp256k1_generator_const_h, &secp256k1_generator_const_g));
    }

    std::vector<CStealthAddress> txoutAddrs(nOutputs);
    std::vector<CTxOutValueTest> txouts(nOutputs);

    std::vector<CAmount> amountsOut(nOutputs);
    CAmount nOutputLeft = nInputSum - nFee;
    for (size_t k = 0; k < nOutputs-1; ++k) {
        amountsOut[k] = rand() % 4 == 0 ? 0 : (rand() % nOutputLeft);
        nOutputLeft -= amountsOut[k];
    }
    amountsOut[nOutputs-1] = nOutputLeft;

    std::vector<CKey> kto_outs(nOutputs);
    std::vector<CPubKey> pkto_outs(nOutputs);
    std::vector<S32Bytes> blind(txouts.size());

    for (size_t k = 0; k < txouts.size(); ++k) {
        GetBytes(tmp, 32, fDeterministic);
        kto_outs[k].Set(tmp, true);
        pkto_outs[k] = kto_outs[k].GetPubKey();

        CTxOutValueTest &txout = txouts[k];

        GetBytes(&blind[k].d[0], 32, fDeterministic);
        pBlinds.push_back(&blind[k].d[0]);

        BOOST_CHECK(secp256k1_pedersen_commit(ctx, &txout.commitment, (uint8_t*)pBlinds.back(), amountsOut[k], &secp256k1_generator_const_h, &secp256k1_generator_const_g));

        // Generate ephemeral key for ECDH nonce generation
        CKey ephemeral_key;
        GetBytes(tmp, 32, fDeterministic);
        ephemeral_key.Set(tmp, true);
        CPubKey ephemeral_pubkey = ephemeral_key.GetPubKey();
        txout.vchNonceCommitment.resize(33);
        memcpy(&txout.vchNonceCommitment[0], &ephemeral_pubkey[0], 33);

        // Generate nonce
        uint256 nonce = ephemeral_key.ECDH(pkto_outs[k]);
        CSHA256().Write(nonce.begin(), 32).Finalize(nonce.begin());
        // Create range proof
        size_t nRangeProofLen = 5134;

        // TODO: smarter min_value selection

        txout.vchRangeproof.resize(nRangeProofLen);

        uint64_t min_value = 0;
        int ct_exponent = 2;
        int ct_bits = 32;

        BOOST_CHECK(secp256k1_rangeproof_sign(ctx,
            &txout.vchRangeproof[0], &nRangeProofLen,
            min_value, &txout.commitment,
            pBlinds.back(), nonce.begin(),
            ct_exponent, ct_bits,
            (uint64_t)amountsOut[k],
            nullptr, 0,
            nullptr, 0,
            secp256k1_generator_h));

        txout.vchRangeproof.resize(nRangeProofLen);
    }

    size_t nRows = txins.size() + 1; // last row sums commitments
    size_t nCols = 1 + (rand() % 128); // ring size
    size_t index = rand() % nCols;

    uint8_t *m = (uint8_t*) calloc(nCols * nRows, 33); // m[(col+(cols*row))*33]
    BOOST_REQUIRE(m);

    size_t haveFee = (nFee != 0) ? 1 : 0;
    std::vector<const uint8_t*> pcm_in(nCols * txins.size()); // pcm_in[col+(cols*row)]
    std::vector<const uint8_t*> pcm_out(txouts.size() + haveFee);

    std::vector<CTxOutValueTest> tx_mixin(txins.size() * (nCols-1));

    size_t iMix = 0;
    for (size_t k = 0; k < nRows-1; ++k)
    for (size_t i = 0; i < nCols; ++i) {
        if (i == index) {
            memcpy(&m[(i+k*nCols)*33], txins[k].pk.begin(), 33);
            pcm_in[i+k*nCols] = txins[k].commitment.data;
            continue;
        }

        // Make fake input
        CKey key;
        GetBytes(tmp, 32, fDeterministic);
        key.Set(tmp, true);

        CTxOutValueTest &mixin = tx_mixin[iMix++];
        mixin.pk = key.GetPubKey();
        CAmount nValue = rand() % (5000 * COIN);

        uint8_t blind[32];
        GetBytes(&blind[0], 32, fDeterministic);
        BOOST_CHECK(secp256k1_pedersen_commit(ctx, &mixin.commitment, blind, nValue, &secp256k1_generator_const_h, &secp256k1_generator_const_g));

        memcpy(&m[(i+k*nCols)*33], mixin.pk.begin(), 33);
        pcm_in[i+k*nCols] = mixin.commitment.data;
    }

    std::vector<const uint8_t*> sk(nRows); // pass in pointer to secret keys to keep them in secured memory

    for (size_t k = 0; k < txins.size(); ++k) {
        sk[k] = kIns[k].begin();
    }
    uint8_t blindSum[32]; // memory for last key (sum of blinding factors)
    memset(blindSum, 0, 32);
    sk[nRows-1] = blindSum;

    for (size_t k = 0; k < txouts.size(); ++k) {
        pcm_out[k] = txouts[k].commitment.data;
    }

    // Add commitment for fee
    secp256k1_pedersen_commitment feeCommitment;
    if (haveFee) {
        uint8_t zeroBlind[32];
        memset(zeroBlind, 0, 32);

        BOOST_CHECK(secp256k1_pedersen_commit(ctx, &feeCommitment, &zeroBlind[0], nFee, &secp256k1_generator_const_h, &secp256k1_generator_const_g));
        pcm_out[txouts.size()] = feeCommitment.data;
    }

    BOOST_CHECK(0 == secp256k1_prepare_mlsag(m, blindSum,
        txouts.size()+haveFee, txouts.size(), nCols, nRows,
        &pcm_in[0], &pcm_out[0], &pBlinds[0]));

    uint8_t randSeed[32], preimage[32], pc[32];
    std::vector<uint8_t> ki(33 * txins.size());
    std::vector<uint8_t> ss(nCols * nRows * 32);

    GetBytes(randSeed, 32, fDeterministic);
    GetBytes(preimage, 32, fDeterministic);

    BOOST_CHECK(0 == secp256k1_generate_mlsag(ctx, &ki[0], pc, &ss[0],
        randSeed, preimage, nCols, nRows, index,
        &sk[0], m));

    BOOST_CHECK(0 == secp256k1_verify_mlsag(ctx,
        preimage, nCols, nRows,
        m, &ki[0], pc, &ss[0]));

    free(m);

    for (size_t k = 0; k < txouts.size(); ++k) {
        CTxOutValueTest &txout = txouts[k];

        int rexp;
        int rmantissa;
        uint64_t min_value;
        uint64_t max_value;

        BOOST_CHECK(1 == secp256k1_rangeproof_info(ctx,
            &rexp, &rmantissa,
            &min_value, &max_value,
            &txout.vchRangeproof[0], txout.vchRangeproof.size()));

        BOOST_CHECK(1 == secp256k1_rangeproof_verify(ctx, &min_value, &max_value,
            &txout.commitment, txout.vchRangeproof.data(), txout.vchRangeproof.size(),
            nullptr, 0,
            secp256k1_generator_h));

        CPubKey ephemeral_key(txout.vchNonceCommitment);
        BOOST_CHECK(ephemeral_key.IsValid());
        uint256 nonce = kto_outs[k].ECDH(ephemeral_key);
        CSHA256().Write(nonce.begin(), 32).Finalize(nonce.begin());

        uint8_t blindOut[32];
        unsigned char msg[4096];
        size_t mlen = sizeof(msg); // input is size of buffer, output is data written
        uint64_t amountOut;
        BOOST_CHECK(secp256k1_rangeproof_rewind(ctx,
            blindOut, &amountOut, msg, &mlen, nonce.begin(),
            &min_value, &max_value,
            &txout.commitment, txout.vchRangeproof.data(), txout.vchRangeproof.size(),
            nullptr, 0,
            secp256k1_generator_h));

        BOOST_CHECK(amountsOut[k] == (CAmount) amountOut);
    }

    return 0;
}

BOOST_AUTO_TEST_CASE(ringct_test_2in_fee)
{
    secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);

    uint32_t rseed = 1;
    srand(rseed);

    doTest(ctx, 2, 3, 0.001 * COIN, true);

    rseed = InsecureRand32();
    srand(rseed);
    for (size_t k = 0; k < 10; ++k) {
        //doTest(ctx, 1 + (rand() % 15), 1 + (rand() % 15), 0.001 * COIN, false);
        doTest(ctx, 1 + (rand() % 15), 1 + (rand() % 15), 0.001 * COIN, true); // use less entropy
    }

    secp256k1_context_destroy(ctx);
}

BOOST_AUTO_TEST_CASE(ringct_test_set_have)
{
    std::set<int64_t> setHaveI;

    BOOST_CHECK(setHaveI.insert(1).second == true);
    BOOST_CHECK(setHaveI.insert(1).second == false);
    BOOST_CHECK(setHaveI.insert(2).second == true);
}

BOOST_AUTO_TEST_SUITE_END()


/* Author: Wolfgang Bangerth, Texas A&M University, 2008 */

/*    $Id: step-22.cc 27657 2012-11-21 13:19:08Z bangerth $       */
/*                                                                */
/*    Copyright (C) 2008-2012 by the deal.II authors */
/*                                                                */
/*    This file is subject to QPL and may not be  distributed     */
/*    without copyright and license information. Please refer     */
/*    to the file deal.II/doc/license.html for the  text  and     */
/*    further information on this license.                        */

// @sect3{Include files}

// As usual, we start by including some well-known files:
#include <deal.II/base/quadrature_lib.h>
#include <deal.II/base/logstream.h>
#include <deal.II/base/function.h>
#include <deal.II/base/utilities.h>

#include <deal.II/lac/block_vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/block_sparse_matrix.h>
#include <deal.II/lac/solver_cg.h>
#include <deal.II/lac/precondition.h>
#include <deal.II/lac/constraint_matrix.h>

#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/tria_accessor.h>
#include <deal.II/grid/tria_iterator.h>
#include <deal.II/grid/tria_boundary_lib.h>
#include <deal.II/grid/grid_tools.h>
#include <deal.II/grid/grid_refinement.h>

#include <deal.II/dofs/dof_handler.h>
#include <deal.II/dofs/dof_renumbering.h>
#include <deal.II/dofs/dof_accessor.h>
#include <deal.II/dofs/dof_tools.h>

#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_system.h>
#include <deal.II/fe/fe_values.h>

#include <deal.II/numerics/vector_tools.h>
#include <deal.II/numerics/matrix_tools.h>
#include <deal.II/numerics/data_out.h>
#include <deal.II/numerics/error_estimator.h>

// Then we need to include the header file for the sparse direct solver
// UMFPACK:
#include <deal.II/lac/sparse_direct.h>

// This includes the library for the incomplete LU factorization that will be
// used as a preconditioner in 3D:
#include <deal.II/lac/sparse_ilu.h>

// This is C++:
#include <fstream>
#include <sstream>

// As in all programs, the namespace dealii is included:
namespace Step22
{
  using namespace dealii;

  // @sect3{Defining the inner preconditioner type}

  // As explained in the introduction, we are going to use different
  // preconditioners for two and three space dimensions, respectively. We
  // distinguish between them by the use of the spatial dimension as a
  // template parameter. See step-4 for details on templates. We are not going
  // to create any preconditioner object here, all we do is to create class
  // that holds a local typedef determining the preconditioner class so we can
  // write our program in a dimension-independent way.
  template <int dim>
  struct InnerPreconditioner;

  // In 2D, we are going to use a sparse direct solver as preconditioner:
  template <>
  struct InnerPreconditioner<2>
  {
    typedef SparseDirectUMFPACK type;
  };

  // And the ILU preconditioning in 3D, called by SparseILU:
  template <>
  struct InnerPreconditioner<3>
  {
    typedef SparseILU<double> type;
  };

  // @sect3{The StokesProblem class template}

  // This is an adaptation of step-20, so the main class and the data types
  // are the same as used there. In this example we also use adaptive grid
  // refinement, which is handled in analogy to step-6. According to the
  // discussion in the introduction, we are also going to use the
  // ConstraintMatrix for implementing Dirichlet boundary conditions. Hence,
  // we change the name hanging_node_constraints into
  // constraints.
  template <int dim>
  class StokesProblem
  {
  public:
    StokesProblem (const unsigned int degree);
    void run ();

  private:
    void setup_dofs ();
    void assemble_system ();
    void solve ();
    void output_results (const unsigned int refinement_cycle) const;
    void refine_mesh ();

    const unsigned int   degree;

    Triangulation<dim>   triangulation;
    FESystem<dim>        fe;
    DoFHandler<dim>      dof_handler;

    ConstraintMatrix     constraints;

    BlockSparsityPattern      sparsity_pattern;
    BlockSparseMatrix<double> system_matrix;

    BlockVector<double> solution;
    BlockVector<double> system_rhs;

    // This one is new: We shall use a so-called shared pointer structure to
    // access the preconditioner. Shared pointers are essentially just a
    // convenient form of pointers. Several shared pointers can point to the
    // same object (just like regular pointers), but when the last shared
    // pointer object to point to a preconditioner object is deleted (for
    // example if a shared pointer object goes out of scope, if the class of
    // which it is a member is destroyed, or if the pointer is assigned a
    // different preconditioner object) then the preconditioner object pointed
    // to is also destroyed. This ensures that we don't have to manually track
    // in how many places a preconditioner object is still referenced, it can
    // never create a memory leak, and can never produce a dangling pointer to
    // an already destroyed object:
    std_cxx1x::shared_ptr<typename InnerPreconditioner<dim>::type> A_preconditioner;
  };

  // @sect3{Boundary values and right hand side}

  // As in step-20 and most other example programs, the next task is to define
  // the data for the PDE: For the Stokes problem, we are going to use natural
  // boundary values on parts of the boundary (i.e. homogenous Neumann-type)
  // for which we won't have to do anything special (the homogeneity implies
  // that the corresponding terms in the weak form are simply zero), and
  // boundary conditions on the velocity (Dirichlet-type) on the rest of the
  // boundary, as described in the introduction.
  //
  // In order to enforce the Dirichlet boundary values on the velocity, we
  // will use the VectorTools::interpolate_boundary_values function as usual
  // which requires us to write a function object with as many components as
  // the finite element has. In other words, we have to define the function on
  // the $(u,p)$-space, but we are going to filter out the pressure component
  // when interpolating the boundary values.

  // The following function object is a representation of the boundary values
  // described in the introduction:
  template <int dim>
  class BoundaryValues : public Function<dim>
  {
  public:
    BoundaryValues () : Function<dim>(dim+1) {}

    virtual double value (const Point<dim>   &p,
                          const unsigned int  component = 0) const;

    virtual void vector_value (const Point<dim> &p,
                               Vector<double>   &value) const;
  };

  template <int dim>
  double
  BoundaryValues<dim>::value (const Point<dim> &p,
                              const unsigned int component) const
  {
    Assert (component < this->n_components,
            ExcIndexRange (component, 0, this->n_components));

    if (component == 0)
      return (p[0] < 0 ? -1 : (p[0] > 0 ? 1 : 0));
    return 0;
  }

  template <int dim>
  void
  BoundaryValues<dim>::vector_value (const Point<dim> &p,
                                     Vector<double>   &values) const
  {
    for (unsigned int c=0; c<this->n_components; ++c)
      values(c) = BoundaryValues<dim>::value (p, c);
  }

  // We implement similar functions for the right hand side which for the
  // current example is simply zero:
  template <int dim>
  class RightHandSide : public Function<dim>
  {
  public:
    RightHandSide () : Function<dim>(dim+1) {}

    virtual double value (const Point<dim>   &p,
                          const unsigned int  component = 0) const;

    virtual void vector_value (const Point<dim> &p,
                               Vector<double>   &value) const;

  };

  template <int dim>
  double
  RightHandSide<dim>::value (const Point<dim>  & /*p*/,
                             const unsigned int /*component*/) const
  {
    return 0;
  }

  template <int dim>
  void
  RightHandSide<dim>::vector_value (const Point<dim> &p,
                                    Vector<double>   &values) const
  {
    for (unsigned int c=0; c<this->n_components; ++c)
      values(c) = RightHandSide<dim>::value (p, c);
  }

  // @sect3{Linear solvers and preconditioners}

  // The linear solvers and preconditioners are discussed extensively in the
  // introduction. Here, we create the respective objects that will be used.

  // @sect4{The InverseMatrix class template}

  // The InverseMatrix class represents the data structure for an
  // inverse matrix. It is derived from the one in step-20. The only
  // difference is that we now do include a preconditioner to the matrix since
  // we will apply this class to different kinds of matrices that will require
  // different preconditioners (in step-20 we did not use a preconditioner in
  // this class at all). The types of matrix and preconditioner are passed to
  // this class via template parameters, and matrix and preconditioner objects
  // of these types will then be passed to the constructor when an
  // InverseMatrix object is created. The member function
  // vmult is, as in step-20, a multiplication with a vector,
  // obtained by solving a linear system:
  template <class Matrix, class Preconditioner>
  class InverseMatrix : public Subscriptor
  {
  public:
    InverseMatrix (const Matrix         &m,
                   const Preconditioner &preconditioner);

    void vmult (Vector<double>       &dst,
                const Vector<double> &src) const;

  private:
    const SmartPointer<const Matrix> matrix;
    const SmartPointer<const Preconditioner> preconditioner;
  };

  template <class Matrix, class Preconditioner>
  InverseMatrix<Matrix,Preconditioner>::InverseMatrix (const Matrix &m,
                                                       const Preconditioner &preconditioner)
    :
    matrix (&m),
    preconditioner (&preconditioner)
  {}

  // This is the implementation of the vmult function.

  // In this class we use a rather large tolerance for the solver control. The
  // reason for this is that the function is used very frequently, and hence,
  // any additional effort to make the residual in the CG solve smaller makes
  // the solution more expensive. Note that we do not only use this class as a
  // preconditioner for the Schur complement, but also when forming the
  // inverse of the Laplace matrix – which is hence directly responsible
  // for the accuracy of the solution itself, so we can't choose a too large
  // tolerance, either.
  template <class Matrix, class Preconditioner>
  void InverseMatrix<Matrix,Preconditioner>::vmult (Vector<double>       &dst,
                                                    const Vector<double> &src) const
  {
    SolverControl solver_control (src.size(), 1e-6*src.l2_norm());
    SolverCG<>    cg (solver_control);

    dst = 0;

    cg.solve (*matrix, dst, src, *preconditioner);
  }

  // @sect4{The SchurComplement class template}

  // This class implements the Schur complement discussed in the introduction.
  // It is in analogy to step-20.  Though, we now call it with a template
  // parameter Preconditioner in order to access that when
  // specifying the respective type of the inverse matrix class. As a
  // consequence of the definition above, the declaration
  // InverseMatrix now contains the second template parameter for
  // a preconditioner class as above, which affects the
  // SmartPointer object m_inverse as well.
  template <class Preconditioner>
  class SchurComplement : public Subscriptor
  {
  public:
    SchurComplement (const BlockSparseMatrix<double> &system_matrix,
                     const InverseMatrix<SparseMatrix<double>, Preconditioner> &A_inverse);

    void vmult (Vector<double>       &dst,
                const Vector<double> &src) const;

  private:
    const SmartPointer<const BlockSparseMatrix<double> > system_matrix;
    const SmartPointer<const InverseMatrix<SparseMatrix<double>, Preconditioner> > A_inverse;

    mutable Vector<double> tmp1, tmp2;
  };

  template <class Preconditioner>
  SchurComplement<Preconditioner>::
  SchurComplement (const BlockSparseMatrix<double> &system_matrix,
                   const InverseMatrix<SparseMatrix<double>,Preconditioner> &A_inverse)
    :
    system_matrix (&system_matrix),
    A_inverse (&A_inverse),
    tmp1 (system_matrix.block(0,0).m()),
    tmp2 (system_matrix.block(0,0).m())
  {}

  template <class Preconditioner>
  void SchurComplement<Preconditioner>::vmult (Vector<double>       &dst,
                                               const Vector<double> &src) const
  {
    system_matrix->block(0,1).vmult (tmp1, src);
    A_inverse->vmult (tmp2, tmp1);
    system_matrix->block(1,0).vmult (dst, tmp2);
  }

  // @sect3{StokesProblem class implementation}

  // @sect4{StokesProblem::StokesProblem}

  // The constructor of this class looks very similar to the one of
  // step-20. The constructor initializes the variables for the polynomial
  // degree, triangulation, finite element system and the dof handler. The
  // underlying polynomial functions are of order degree+1 for
  // the vector-valued velocity components and of order degree
  // for the pressure.  This gives the LBB-stable element pair
  // $Q_{degree+1}^d\times Q_{degree}$, often referred to as the Taylor-Hood
  // element.
  //
  // Note that we initialize the triangulation with a MeshSmoothing argument,
  // which ensures that the refinement of cells is done in a way that the
  // approximation of the PDE solution remains well-behaved (problems arise if
  // grids are too unstructered), see the documentation of
  // Triangulation::MeshSmoothing for details.
  template <int dim>
  StokesProblem<dim>::StokesProblem (const unsigned int degree)
    :
    degree (degree),
    triangulation (Triangulation<dim>::maximum_smoothing),
    fe (FE_Q<dim>(degree+1), dim,
        FE_Q<dim>(degree), 1),
    dof_handler (triangulation)
  {}

  // @sect4{StokesProblem::setup_dofs}

  // Given a mesh, this function associates the degrees of freedom with it and
  // creates the corresponding matrices and vectors. At the beginning it also
  // releases the pointer to the preconditioner object (if the shared pointer
  // pointed at anything at all at this point) since it will definitely not be
  // needed any more after this point and will have to be re-computed after
  // assembling the matrix, and unties the sparse matrix from its sparsity
  // pattern object.
  //
  // We then proceed with distributing degrees of freedom and renumbering
  // them: In order to make the ILU preconditioner (in 3D) work efficiently,
  // it is important to enumerate the degrees of freedom in such a way that it
  // reduces the bandwidth of the matrix, or maybe more importantly: in such a
  // way that the ILU is as close as possible to a real LU decomposition. On
  // the other hand, we need to preserve the block structure of velocity and
  // pressure already seen in in step-20 and step-21. This is done in two
  // steps: First, all dofs are renumbered to improve the ILU and then we
  // renumber once again by components. Since
  // DoFRenumbering::component_wise does not touch the
  // renumbering within the individual blocks, the basic renumbering from the
  // first step remains. As for how the renumber degrees of freedom to improve
  // the ILU: deal.II has a number of algorithms that attempt to find
  // orderings to improve ILUs, or reduce the bandwidth of matrices, or
  // optimize some other aspect. The DoFRenumbering namespace shows a
  // comparison of the results we obtain with several of these algorithms
  // based on the testcase discussed here in this tutorial program. Here, we
  // will use the traditional Cuthill-McKee algorithm already used in some of
  // the previous tutorial programs.  In the section
  // on improved ILU we're going to discuss this issue in more detail.

  // There is one more change compared to previous tutorial programs: There is
  // no reason in sorting the dim velocity components
  // individually. In fact, rather than first enumerating all $x$-velocities,
  // then all $y$-velocities, etc, we would like to keep all velocities at the
  // same location together and only separate between velocities (all
  // components) and pressures. By default, this is not what the
  // DoFRenumbering::component_wise function does: it treats each vector
  // component separately; what we have to do is group several components into
  // "blocks" and pass this block structure to that function. Consequently, we
  // allocate a vector block_component with as many elements as
  // there are components and describe all velocity components to correspond
  // to block 0, while the pressure component will form block 1:
  template <int dim>
  void StokesProblem<dim>::setup_dofs ()
  {
    A_preconditioner.reset ();
    system_matrix.clear ();

    dof_handler.distribute_dofs (fe);
    DoFRenumbering::Cuthill_McKee (dof_handler);

    std::vector<unsigned int> block_component (dim+1,0);
    block_component[dim] = 1;
    DoFRenumbering::component_wise (dof_handler, block_component);

    // Now comes the implementation of Dirichlet boundary conditions, which
    // should be evident after the discussion in the introduction. All that
    // changed is that the function already appears in the setup functions,
    // whereas we were used to see it in some assembly routine. Further down
    // below where we set up the mesh, we will associate the top boundary
    // where we impose Dirichlet boundary conditions with boundary indicator
    // 1.  We will have to pass this boundary indicator as second argument to
    // the function below interpolating boundary values.  There is one more
    // thing, though.  The function describing the Dirichlet conditions was
    // defined for all components, both velocity and pressure. However, the
    // Dirichlet conditions are to be set for the velocity only.  To this end,
    // we use a ComponentMask that only selects the velocity components. The
    // component mask is obtained from the finite element by specifying the
    // particular components we want. Since we use adaptively refined grids
    // the constraint matrix needs to be first filled with hanging node
    // constraints generated from the DoF handler. Note the order of the two
    // functions — we first compute the hanging node constraints, and
    // then insert the boundary values into the constraint matrix. This makes
    // sure that we respect H1 conformity on boundaries with
    // hanging nodes (in three space dimensions), where the hanging node needs
    // to dominate the Dirichlet boundary values.
    {
      constraints.clear ();

      FEValuesExtractors::Vector velocities(0);
      DoFTools::make_hanging_node_constraints (dof_handler,
                                               constraints);
      VectorTools::interpolate_boundary_values (dof_handler,
                                                1,
                                                BoundaryValues<dim>(),
                                                constraints,
                                                fe.component_mask(velocities));
    }

    constraints.close ();

    // In analogy to step-20, we count the dofs in the individual components.
    // We could do this in the same way as there, but we want to operate on
    // the block structure we used already for the renumbering: The function
    // DoFTools::count_dofs_per_block does the same as
    // DoFTools::count_dofs_per_component, but now grouped as
    // velocity and pressure block via block_component.
    std::vector<unsigned int> dofs_per_block (2);
    DoFTools::count_dofs_per_block (dof_handler, dofs_per_block, block_component);
    const unsigned int n_u = dofs_per_block[0],
                       n_p = dofs_per_block[1];

    std::cout << "   Number of active cells: "
              << triangulation.n_active_cells()
              << std::endl
              << "   Number of degrees of freedom: "
              << dof_handler.n_dofs()
              << " (" << n_u << '+' << n_p << ')'
              << std::endl;

    // The next task is to allocate a sparsity pattern for the system matrix
    // we will create. We could do this in the same way as in step-20,
    // i.e. directly build an object of type SparsityPattern through
    // DoFTools::make_sparsity_pattern. However, there is a major reason not
    // to do so: In 3D, the function DoFTools::max_couplings_between_dofs
    // yields a conservative but rather large number for the coupling between
    // the individual dofs, so that the memory initially provided for the
    // creation of the sparsity pattern of the matrix is far too much -- so
    // much actually that the initial sparsity pattern won't even fit into the
    // physical memory of most systems already for moderately-sized 3D
    // problems, see also the discussion in step-18.  Instead, we first build
    // a temporary object that uses a different data structure that doesn't
    // require allocating more memory than necessary but isn't suitable for
    // use as a basis of SparseMatrix or BlockSparseMatrix objects; in a
    // second step we then copy this object into an object of
    // BlockSparsityPattern. This is entirely analgous to what we already did
    // in step-11 and step-18.
    //
    // There is one snag again here, though: it turns out that using the
    // CompressedSparsityPattern (or the block version
    // BlockCompressedSparsityPattern we would use here) has a bottleneck that
    // makes the algorithm to build the sparsity pattern be quadratic in the
    // number of degrees of freedom. This doesn't become noticeable until we
    // get well into the range of several 100,000 degrees of freedom, but
    // eventually dominates the setup of the linear system when we get to more
    // than a million degrees of freedom. This is due to the data structures
    // used in the CompressedSparsityPattern class, nothing that can easily be
    // changed. Fortunately, there is an easy solution: the
    // CompressedSimpleSparsityPattern class (and its block variant
    // BlockCompressedSimpleSparsityPattern) has exactly the same interface,
    // uses a different %internal data structure and is linear in the number
    // of degrees of freedom and therefore much more efficient for large
    // problems. As another alternative, we could also have chosen the class
    // BlockCompressedSetSparsityPattern that uses yet another strategy for
    // %internal memory management. Though, that class turns out to be more
    // memory-demanding than BlockCompressedSimpleSparsityPattern for this
    // example.
    //
    // Consequently, this is the class that we will use for our intermediate
    // sparsity representation. All this is done inside a new scope, which
    // means that the memory of csp will be released once the
    // information has been copied to sparsity_pattern.
    {
      BlockCompressedSimpleSparsityPattern csp (2,2);

      csp.block(0,0).reinit (n_u, n_u);
      csp.block(1,0).reinit (n_p, n_u);
      csp.block(0,1).reinit (n_u, n_p);
      csp.block(1,1).reinit (n_p, n_p);

      csp.collect_sizes();

      DoFTools::make_sparsity_pattern (dof_handler, csp, constraints, false);
      sparsity_pattern.copy_from (csp);
    }

    // Finally, the system matrix, solution and right hand side are created
    // from the block structure as in step-20:
    system_matrix.reinit (sparsity_pattern);

    solution.reinit (2);
    solution.block(0).reinit (n_u);
    solution.block(1).reinit (n_p);
    solution.collect_sizes ();

    system_rhs.reinit (2);
    system_rhs.block(0).reinit (n_u);
    system_rhs.block(1).reinit (n_p);
    system_rhs.collect_sizes ();
  }

  // @sect4{StokesProblem::assemble_system}

  // The assembly process follows the discussion in step-20 and in the
  // introduction. We use the well-known abbreviations for the data structures
  // that hold the local matrix, right hand side, and global numbering of the
  // degrees of freedom for the present cell.
  template <int dim>
  void StokesProblem<dim>::assemble_system ()
  {
    system_matrix=0;
    system_rhs=0;

    QGauss<dim>   quadrature_formula(degree+2);

    FEValues<dim> fe_values (fe, quadrature_formula,
                             update_values    |
                             update_quadrature_points  |
                             update_JxW_values |
                             update_gradients);

    const unsigned int   dofs_per_cell   = fe.dofs_per_cell;

    const unsigned int   n_q_points      = quadrature_formula.size();

    FullMatrix<double>   local_matrix (dofs_per_cell, dofs_per_cell);
    Vector<double>       local_rhs (dofs_per_cell);

    std::vector<unsigned int> local_dof_indices (dofs_per_cell);

    const RightHandSide<dim>          right_hand_side;
    std::vector<Vector<double> >      rhs_values (n_q_points,
                                                  Vector<double>(dim+1));

    // Next, we need two objects that work as extractors for the FEValues
    // object. Their use is explained in detail in the report on @ref
    // vector_valued :
    const FEValuesExtractors::Vector velocities (0);
    const FEValuesExtractors::Scalar pressure (dim);

    // As an extension over step-20 and step-21, we include a few
    // optimizations that make assembly much faster for this particular
    // problem.  The improvements are based on the observation that we do a
    // few calculations too many times when we do as in step-20: The symmetric
    // gradient actually has dofs_per_cell different values per
    // quadrature point, but we extract it
    // dofs_per_cell*dofs_per_cell times from the FEValues object
    // - for both the loop over i and the inner loop over
    // j. In 3d, that means evaluating it $89^2=7921$ instead of
    // $89$ times, a not insignificant difference.
    //
    // So what we're going to do here is to avoid such repeated calculations
    // by getting a vector of rank-2 tensors (and similarly for the divergence
    // and the basis function value on pressure) at the quadrature point prior
    // to starting the loop over the dofs on the cell. First, we create the
    // respective objects that will hold these values. Then, we start the loop
    // over all cells and the loop over the quadrature points, where we first
    // extract these values. There is one more optimization we implement here:
    // the local matrix (as well as the global one) is going to be symmetric,
    // since all the operations involved are symmetric with respect to $i$ and
    // $j$. This is implemented by simply running the inner loop not to
    // dofs_per_cell, but only up to i, the index of
    // the outer loop.
    std::vector<SymmetricTensor<2,dim> > symgrad_phi_u (dofs_per_cell);
    std::vector<double>                  div_phi_u   (dofs_per_cell);
    std::vector<double>                  phi_p       (dofs_per_cell);

    typename DoFHandler<dim>::active_cell_iterator
    cell = dof_handler.begin_active(),
    endc = dof_handler.end();
    for (; cell!=endc; ++cell)
      {
        fe_values.reinit (cell);
        local_matrix = 0;
        local_rhs = 0;

        right_hand_side.vector_value_list(fe_values.get_quadrature_points(),
                                          rhs_values);

        for (unsigned int q=0; q<n_q_points; ++q)
          {
            for (unsigned int k=0; k<dofs_per_cell; ++k)
              {
                symgrad_phi_u[k] = fe_values[velocities].symmetric_gradient (k, q);
                div_phi_u[k]     = fe_values[velocities].divergence (k, q);
                phi_p[k]         = fe_values[pressure].value (k, q);
              }

            for (unsigned int i=0; i<dofs_per_cell; ++i)
              {
                for (unsigned int j=0; j<=i; ++j)
                  {
                    local_matrix(i,j) += (symgrad_phi_u[i] * symgrad_phi_u[j]
                                          - div_phi_u[i] * phi_p[j]
                                          - phi_p[i] * div_phi_u[j]
                                          + phi_p[i] * phi_p[j])
                                         * fe_values.JxW(q);

                  }

                const unsigned int component_i =
                  fe.system_to_component_index(i).first;
                local_rhs(i) += fe_values.shape_value(i,q) *
                                rhs_values[q](component_i) *
                                fe_values.JxW(q);
              }
          }

        // Note that in the above computation of the local matrix contribution
        // we added the term  phi_p[i] * phi_p[j] , yielding a
        // pressure mass matrix in the $(1,1)$ block of the matrix as
        // discussed in the introduction. That this term only ends up in the
        // $(1,1)$ block stems from the fact that both of the factors in
        // phi_p[i] * phi_p[j] are only non-zero when all the
        // other terms vanish (and the other way around).
        //
        // Note also that operator* is overloaded for symmetric tensors,
        // yielding the scalar product between the two tensors in the first
        // line of the local matrix contribution.

        // Before we can write the local data into the global matrix (and
        // simultaneously use the ConstraintMatrix object to apply Dirichlet
        // boundary conditions and eliminate hanging node constraints, as we
        // discussed in the introduction), we have to be careful about one
        // thing, though. We have only build up half of the local matrix
        // because of symmetry, but we're going to save the full system matrix
        // in order to use the standard functions for solution. This is done
        // by flipping the indices in case we are pointing into the empty part
        // of the local matrix.
        for (unsigned int i=0; i<dofs_per_cell; ++i)
          for (unsigned int j=i+1; j<dofs_per_cell; ++j)
            local_matrix(i,j) = local_matrix(j,i);

        cell->get_dof_indices (local_dof_indices);
        constraints.distribute_local_to_global (local_matrix, local_rhs,
                                                local_dof_indices,
                                                system_matrix, system_rhs);
      }

    // Before we're going to solve this linear system, we generate a
    // preconditioner for the velocity-velocity matrix, i.e.,
    // block(0,0) in the system matrix. As mentioned above, this
    // depends on the spatial dimension. Since the two classes described by
    // the InnerPreconditioner::type typedef have the same
    // interface, we do not have to do anything different whether we want to
    // use a sparse direct solver or an ILU:
    std::cout << "   Computing preconditioner..." << std::endl << std::flush;

    A_preconditioner
      = std_cxx1x::shared_ptr<typename InnerPreconditioner<dim>::type>(new typename InnerPreconditioner<dim>::type());
    A_preconditioner->initialize (system_matrix.block(0,0),
                                  typename InnerPreconditioner<dim>::type::AdditionalData());

  }

  // @sect4{StokesProblem::solve}

  // After the discussion in the introduction and the definition of the
  // respective classes above, the implementation of the solve
  // function is rather straigt-forward and done in a similar way as in
  // step-20. To start with, we need an object of the
  // InverseMatrix class that represents the inverse of the
  // matrix A. As described in the introduction, the inverse is generated with
  // the help of an inner preconditioner of type
  // InnerPreconditioner::type.
  template <int dim>
  void StokesProblem<dim>::solve ()
  {
    const InverseMatrix<SparseMatrix<double>,
          typename InnerPreconditioner<dim>::type>
          A_inverse (system_matrix.block(0,0), *A_preconditioner);
    Vector<double> tmp (solution.block(0).size());

    // This is as in step-20. We generate the right hand side $B A^{-1} F - G$
    // for the Schur complement and an object that represents the respective
    // linear operation $B A^{-1} B^T$, now with a template parameter
    // indicating the preconditioner - in accordance with the definition of
    // the class.
    {
      Vector<double> schur_rhs (solution.block(1).size());
      A_inverse.vmult (tmp, system_rhs.block(0));
      system_matrix.block(1,0).vmult (schur_rhs, tmp);
      schur_rhs -= system_rhs.block(1);

      SchurComplement<typename InnerPreconditioner<dim>::type>
      schur_complement (system_matrix, A_inverse);

      // The usual control structures for the solver call are created...
      SolverControl solver_control (solution.block(1).size(),
                                    1e-6*schur_rhs.l2_norm());
      SolverCG<>    cg (solver_control);

      // Now to the preconditioner to the Schur complement. As explained in
      // the introduction, the preconditioning is done by a mass matrix in the
      // pressure variable.  It is stored in the $(1,1)$ block of the system
      // matrix (that is not used anywhere else but in preconditioning).
      //
      // Actually, the solver needs to have the preconditioner in the form
      // $P^{-1}$, so we need to create an inverse operation. Once again, we
      // use an object of the class InverseMatrix, which
      // implements the vmult operation that is needed by the
      // solver.  In this case, we have to invert the pressure mass matrix. As
      // it already turned out in earlier tutorial programs, the inversion of
      // a mass matrix is a rather cheap and straight-forward operation
      // (compared to, e.g., a Laplace matrix). The CG method with ILU
      // preconditioning converges in 5-10 steps, independently on the mesh
      // size.  This is precisely what we do here: We choose another ILU
      // preconditioner and take it along to the InverseMatrix object via the
      // corresponding template parameter.  A CG solver is then called within
      // the vmult operation of the inverse matrix.
      //
      // An alternative that is cheaper to build, but needs more iterations
      // afterwards, would be to choose a SSOR preconditioner with factor
      // 1.2. It needs about twice the number of iterations, but the costs for
      // its generation are almost neglible.
      SparseILU<double> preconditioner;
      preconditioner.initialize (system_matrix.block(1,1),
                                 SparseILU<double>::AdditionalData());

      InverseMatrix<SparseMatrix<double>,SparseILU<double> >
      m_inverse (system_matrix.block(1,1), preconditioner);

      // With the Schur complement and an efficient preconditioner at hand, we
      // can solve the respective equation for the pressure (i.e. block 0 in
      // the solution vector) in the usual way:
      cg.solve (schur_complement, solution.block(1), schur_rhs,
                m_inverse);

      // After this first solution step, the hanging node constraints have to
      // be distributed to the solution in order to achieve a consistent
      // pressure field.
      constraints.distribute (solution);

      std::cout << "  "
                << solver_control.last_step()
                << " outer CG Schur complement iterations for pressure"
                << std::endl;
    }

    // As in step-20, we finally need to solve for the velocity equation where
    // we plug in the solution to the pressure equation. This involves only
    // objects we already know - so we simply multiply $p$ by $B^T$, subtract
    // the right hand side and multiply by the inverse of $A$. At the end, we
    // need to distribute the constraints from hanging nodes in order to
    // obtain a constistent flow field:
    {
      system_matrix.block(0,1).vmult (tmp, solution.block(1));
      tmp *= -1;
      tmp += system_rhs.block(0);

      A_inverse.vmult (solution.block(0), tmp);

      constraints.distribute (solution);
    }
  }

  // @sect4{StokesProblem::output_results}

  // The next function generates graphical output. In this example, we are
  // going to use the VTK file format.  We attach names to the individual
  // variables in the problem: velocity to the dim
  // components of velocity and pressure to the pressure.
  //
  // Not all visualization programs have the ability to group individual
  // vector components into a vector to provide vector plots; in particular,
  // this holds for some VTK-based visualization programs. In this case, the
  // logical grouping of components into vectors should already be described
  // in the file containing the data. In other words, what we need to do is
  // provide our output writers with a way to know which of the components of
  // the finite element logically form a vector (with $d$ components in $d$
  // space dimensions) rather than letting them assume that we simply have a
  // bunch of scalar fields.  This is achieved using the members of the
  // DataComponentInterpretation namespace: as with the filename,
  // we create a vector in which the first dim components refer
  // to the velocities and are given the tag
  // DataComponentInterpretation::component_is_part_of_vector; we
  // finally push one tag
  // DataComponentInterpretation::component_is_scalar to describe
  // the grouping of the pressure variable.

  // The rest of the function is then the same as in step-20.
  template <int dim>
  void
  StokesProblem<dim>::output_results (const unsigned int refinement_cycle)  const
  {
    std::vector<std::string> solution_names (dim, "velocity");
    solution_names.push_back ("pressure");

    std::vector<DataComponentInterpretation::DataComponentInterpretation>
    data_component_interpretation
    (dim, DataComponentInterpretation::component_is_part_of_vector);
    data_component_interpretation
    .push_back (DataComponentInterpretation::component_is_scalar);

    DataOut<dim> data_out;
    data_out.attach_dof_handler (dof_handler);
    data_out.add_data_vector (solution, solution_names,
                              DataOut<dim>::type_dof_data,
                              data_component_interpretation);
    data_out.build_patches ();

    std::ostringstream filename;
    filename << "solution-"
             << Utilities::int_to_string (refinement_cycle, 2)
             << ".vtk";

    std::ofstream output (filename.str().c_str());
    data_out.write_vtk (output);
  }

  // @sect4{StokesProblem::refine_mesh}

  // This is the last interesting function of the StokesProblem
  // class.  As indicated by its name, it takes the solution to the problem
  // and refines the mesh where this is needed. The procedure is the same as
  // in the respective step in step-6, with the exception that we base the
  // refinement only on the change in pressure, i.e., we call the Kelly error
  // estimator with a mask object of type ComponentMask that selects the
  // single scalar component for the pressure that we are interested in (we
  // get such a mask from the finite element class by specifying the component
  // we want). Additionally, we do not coarsen the grid again:
  template <int dim>
  void
  StokesProblem<dim>::refine_mesh ()
  {
    Vector<float> estimated_error_per_cell (triangulation.n_active_cells());

    FEValuesExtractors::Scalar pressure(dim);
    KellyErrorEstimator<dim>::estimate (dof_handler,
                                        QGauss<dim-1>(degree+1),
                                        typename FunctionMap<dim>::type(),
                                        solution,
                                        estimated_error_per_cell,
                                        fe.component_mask(pressure));

    GridRefinement::refine_and_coarsen_fixed_number (triangulation,
                                                     estimated_error_per_cell,
                                                     0.3, 0.0);
    triangulation.execute_coarsening_and_refinement ();
  }

  // @sect4{StokesProblem::run}

  // The last step in the Stokes class is, as usual, the function that
  // generates the initial grid and calls the other functions in the
  // respective order.
  //
  // We start off with a rectangle of size $4 \times 1$ (in 2d) or $4 \times 1
  // \times 1$ (in 3d), placed in $R^2/R^3$ as $(-2,2)\times(-1,0)$ or
  // $(-2,2)\times(0,1)\times(-1,0)$, respectively. It is natural to start
  // with equal mesh size in each direction, so we subdivide the initial
  // rectangle four times in the first coordinate direction. To limit the
  // scope of the variables involved in the creation of the mesh to the range
  // where we actually need them, we put the entire block between a pair of
  // braces:
  template <int dim>
  void StokesProblem<dim>::run ()
  {
    {
      std::vector<unsigned int> subdivisions (dim, 1);
      subdivisions[0] = 4;

      const Point<dim> bottom_left = (dim == 2 ?
                                      Point<dim>(-2,-1) :
                                      Point<dim>(-2,0,-1));
      const Point<dim> top_right   = (dim == 2 ?
                                      Point<dim>(2,0) :
                                      Point<dim>(2,1,0));

      GridGenerator::subdivided_hyper_rectangle (triangulation,
                                                 subdivisions,
                                                 bottom_left,
                                                 top_right);
    }

    // A boundary indicator of 1 is set to all boundaries that are subject to
    // Dirichlet boundary conditions, i.e.  to faces that are located at 0 in
    // the last coordinate direction. See the example description above for
    // details.
    for (typename Triangulation<dim>::active_cell_iterator
         cell = triangulation.begin_active();
         cell != triangulation.end(); ++cell)
      for (unsigned int f=0; f<GeometryInfo<dim>::faces_per_cell; ++f)
        if (cell->face(f)->center()[dim-1] == 0)
          cell->face(f)->set_all_boundary_indicators(1);

    // We then apply an initial refinement before solving for the first
    // time. In 3D, there are going to be more degrees of freedom, so we
    // refine less there:
    triangulation.refine_global (4-dim);

    // As first seen in step-6, we cycle over the different refinement levels
    // and refine (except for the first cycle), setup the degrees of freedom
    // and matrices, assemble, solve and create output:
    for (unsigned int refinement_cycle = 0; refinement_cycle<6;
         ++refinement_cycle)
      {
        std::cout << "Refinement cycle " << refinement_cycle << std::endl;

        if (refinement_cycle > 0)
          refine_mesh ();

        setup_dofs ();

        std::cout << "   Assembling..." << std::endl << std::flush;
        assemble_system ();

        std::cout << "   Solving..." << std::flush;
        solve ();

        output_results (refinement_cycle);

        std::cout << std::endl;
      }
  }
}

// @sect3{The main function}

// The main function is the same as in step-20. We pass the element degree as
// a parameter and choose the space dimension at the well-known template slot.
int main ()
{
  try
    {
      using namespace dealii;
      using namespace Step22;

      deallog.depth_console (0);

      StokesProblem<2> flow_problem(1);
      flow_problem.run ();
    }
  catch (std::exception &exc)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl
                << exc.what() << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;

      return 1;
    }
  catch (...)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Unknown exception!" << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }

  return 0;
}