<commit_before>// Copyright (c) 2022 The Gridcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/licenses/mit-license.php.

#include <boost/test/unit_test.hpp>

#include <main.h>
#include <gridcoin/account.h>
#include <gridcoin/beacon.h>
#include <gridcoin/cpid.h>
#include <gridcoin/mrc.h>
#include <gridcoin/tally.h>
#include <key.h>
#include <miner.h>
#include <primitives/transaction.h>
#include <rpc/blockchain.h>
#include <test/test_gridcoin.h>

namespace {
struct Setup {
    CBlockIndex* pindex{nullptr};
    GRC::Cpid cpid = GRC::Cpid(InsecureRandBytes(16));
    GRC::ResearchAccount& account = GRC::Tally::CreateAccount(cpid);
    CWallet* wallet = new CWallet();
    GRC::Beacon beacon;
    CKey key;

    Setup() {
        GRC::GetBeaconRegistry().Reset();
        SelectParams(CBaseChainParams::MAIN);

        // Setup a mock chain.
        uint256* phash = new uint256(InsecureRandBytes(32));
        pindex = GRC::MockBlockIndex::InsertBlockIndex(*phash);
        pindexGenesisBlock = pindex;
        pindex->nVersion = 12;
        pindex->phashBlock = phash;
        // Needed because this code does not mock superblocks and inclusion of m_accrual
        // into the calculated accrual is dependant on a block's height being
        // lower than the last superblock's.
        // TODO(div72): Improve mockability of Tally.
        pindex->nHeight = -1;
        mapBlockIndex[pindex->GetBlockHash()] = pindex;
        for (int i = 1; i < Params().GetConsensus().MRCZeroPaymentInterval / (24 * 60 * 60) + 2; ++i) {
            phash = new uint256(InsecureRandBytes(32));
            CBlockIndex* pindexNext = GRC::MockBlockIndex::InsertBlockIndex(*phash);
            pindexNext->nHeight = i;
            pindexNext->nTime = pindex->nTime + 24 * 60 * 60;
            pindexNext->nVersion = 12;
            pindex->pnext = pindexNext;
            pindexNext->pprev = pindex;
            pindexNext->phashBlock = phash;
            pindex = pindexNext;
        }

        // These tests were written with these assumptions. If this statement
        // does not hold true in the future, please update.
        assert(pindex->nHeight >= 7);
        assert(pindex->nHeight < 179);

        CTransaction tx;
        tx.nTime = pindexGenesisBlock->nTime;

        key.MakeNewKey(false);
        wallet->AddKey(key);

        GRC::Contract contract = GRC::MakeContract<GRC::BeaconPayload>(
                GRC::ContractAction::ADD,
                cpid,
                key.GetPubKey()
        );

        account.m_first_block_ptr = pindexGenesisBlock;
        account.m_last_block_ptr = pindexGenesisBlock;

        GRC::GetBeaconRegistry().Add({contract, tx, pindexGenesisBlock});
        GRC::GetBeaconRegistry().ActivatePending({key.GetPubKey().GetID()}, tx.nTime, uint256(), 0);
        beacon = *GRC::GetBeaconRegistry().TryActive(cpid, pindex->nTime);

        SetMockTime(1);

        gArgs.ForceSetArg("forcecpid", cpid.ToString());
        GRC::Researcher::Reload({}, {});
    }

    ~Setup() {
        GRC::GetBeaconRegistry().Reset();
        gArgs.ForceSetArg("forcecpid", "");
        gArgs.ForceSetArg("email", "investor");
        GRC::Researcher::Reload();
        gArgs.ForceSetArg("email", "");
        GRC::Tally::RemoveAccount(cpid);

        SetMockTime(0);

        for (CBlockIndex* tip = pindex; tip->pprev; tip = tip->pprev) {
            mapBlockIndex.erase(tip->GetBlockHash());
            delete tip->phashBlock;
        }

        mapBlockIndex.erase(pindexGenesisBlock->GetBlockHash());
        delete pindexGenesisBlock->phashBlock;
    }
};
} // Anonymous namespace

BOOST_FIXTURE_TEST_SUITE(MRC, Setup)

BOOST_AUTO_TEST_CASE(it_properly_records_blocks)
{
    pindex->AddMRCResearcherContext(cpid, 72, 0.0);
    pindex->pprev->pprev->SetResearcherContext(cpid, 72, 0.0);
    pindex->pprev->pprev->pprev->pprev->AddMRCResearcherContext(cpid, 72, 0.0);
    pindex->pprev->pprev->pprev->pprev->pprev->pprev->SetResearcherContext(cpid, 72, 0.0);

    CBlockIndex* index{pindexGenesisBlock};
    while (index) {
        GRC::Tally::RecordRewardBlock(index);
        index = index->pnext;
    }

    BOOST_CHECK(account.m_last_block_ptr == pindex->pprev);
    GRC::Tally::ForgetRewardBlock(pindex);
    BOOST_CHECK(account.m_last_block_ptr == pindex->pprev->pprev);
    GRC::Tally::ForgetRewardBlock(pindex->pprev->pprev);
    BOOST_CHECK(account.m_last_block_ptr == pindex->pprev->pprev->pprev->pprev->pprev);
    GRC::Tally::ForgetRewardBlock(pindex->pprev->pprev->pprev->pprev);
    BOOST_CHECK(account.m_last_block_ptr == pindex->pprev->pprev->pprev->pprev->pprev->pprev);
    GRC::Tally::ForgetRewardBlock(pindex->pprev->pprev->pprev->pprev->pprev->pprev);

    BOOST_CHECK(account.m_last_block_ptr == nullptr);
    account.m_last_block_ptr = account.m_first_block_ptr;
}

BOOST_AUTO_TEST_CASE(it_has_proper_fees_for_newbies)
{
    GRC::MRC mrc;
    mrc.m_mining_id = cpid;
    mrc.m_research_subsidy = 72;

    // Before:
    mrc.m_last_block_hash = pindex->pprev->pprev->GetBlockHash();
    BOOST_CHECK_EQUAL(mrc.ComputeMRCFee(), mrc.m_research_subsidy);

    // At the border:
    mrc.m_last_block_hash = pindex->pprev->GetBlockHash();
    BOOST_CHECK_EQUAL(mrc.ComputeMRCFee(), 28);

    // After:
    mrc.m_last_block_hash = pindex->GetBlockHash();
    BOOST_CHECK_EQUAL(mrc.ComputeMRCFee(), 26);
}

BOOST_AUTO_TEST_CASE(it_rejects_invalid_claims)
{
    GRC::MRC mrc;

    mrc.m_mining_id = cpid;
    mrc.m_client_version = "6.0.0.0";
    mrc.m_last_block_hash = pindex->GetBlockHash();

    BOOST_CHECK(!mrc.WellFormed());

    mrc.m_research_subsidy = 72;
    account.m_accrual = 72;

    mrc.Sign(key);
    BOOST_CHECK(mrc.WellFormed());

    BOOST_CHECK(!ValidateMRC(pindex, mrc));

    mrc.m_fee = mrc.ComputeMRCFee();
    BOOST_CHECK(ValidateMRC(pindex, mrc));

    mrc.m_research_subsidy = 9223372036854775807; // Get rich.
    BOOST_CHECK(!ValidateMRC(pindex, mrc));
    mrc.m_research_subsidy = 72;

    mrc.m_last_block_hash = pindex->pprev->GetBlockHash(); // Older request.
    mrc.Sign(key);
    BOOST_CHECK(!ValidateMRC(pindex, mrc));
    mrc.m_last_block_hash = pindex->GetBlockHash();
    mrc.Sign(key);

    mrc.m_fee = 0; // Tax evasion.
    BOOST_CHECK(!ValidateMRC(pindex, mrc));
}

BOOST_AUTO_TEST_CASE(createmrc_creates_valid_mrcs)
{
    account.m_accrual = 72;
    GRC::MRC mrc;
    CAmount reward, fee;
    GRC::CreateMRC(pindex->pprev, mrc, reward, fee, wallet);

    BOOST_CHECK_EQUAL(reward, 72);
    BOOST_CHECK_EQUAL(fee, 28);

    BOOST_CHECK(mrc.WellFormed());
    BOOST_CHECK(ValidateMRC(pindex->pprev, mrc));
}

BOOST_AUTO_TEST_CASE(it_creates_valid_mrc_claims)
{
    CBlock block;
    block.vtx.resize(2);
    block.vtx[1].vin.resize(1);
    block.vtx[1].vout.resize(2);
    std::map<GRC::Cpid, std::pair<uint256, GRC::MRC>> mrc_map;
    std::map<GRC::Cpid, uint256> mrc_tx_map;

    account.m_accrual = 72;
    pindex->pprev->AddMRCResearcherContext(cpid, 72, 0.0);

    BOOST_CHECK(CreateRestOfTheBlock(block, pindex->pprev, mrc_map));

    GRC::MRC mrc;
    CAmount reward, fee;
    GRC::CreateMRC(pindex->pprev, mrc, reward, fee, wallet);
    mrc_map[cpid] = {uint256{}, mrc};

    GRC::Claim claim;
    BOOST_CHECK(CreateGridcoinReward(block, pindex->pprev, reward, claim));

    BOOST_CHECK(CreateMRCRewards(block, mrc_map, mrc_tx_map, claim, wallet));

    // TODO(div72): Separate this test into pieces and actually have it do
    // some useful testing by testing the validation logic against it.
    // Currently the miner code is too coupled together which makes this
    // infeasible without having a mess of spaghetti code for working
    // around this.
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Ensure cs_main is locked in mrc_tests.cpp<commit_after>// Copyright (c) 2022 The Gridcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/licenses/mit-license.php.

#include <boost/test/unit_test.hpp>

#include <main.h>
#include <gridcoin/account.h>
#include <gridcoin/beacon.h>
#include <gridcoin/cpid.h>
#include <gridcoin/mrc.h>
#include <gridcoin/tally.h>
#include <key.h>
#include <miner.h>
#include <primitives/transaction.h>
#include <rpc/blockchain.h>
#include <test/test_gridcoin.h>

namespace {
struct Setup {
    CBlockIndex* pindex{nullptr};
    GRC::Cpid cpid = GRC::Cpid(InsecureRandBytes(16));
    GRC::ResearchAccount& account = GRC::Tally::CreateAccount(cpid);
    CWallet* wallet = new CWallet();
    GRC::Beacon beacon;
    CKey key;

    Setup() {
        GRC::GetBeaconRegistry().Reset();
        SelectParams(CBaseChainParams::MAIN);

        // Setup a mock chain.
        uint256* phash = new uint256(InsecureRandBytes(32));
        pindex = GRC::MockBlockIndex::InsertBlockIndex(*phash);
        pindexGenesisBlock = pindex;
        pindex->nVersion = 12;
        pindex->phashBlock = phash;
        // Needed because this code does not mock superblocks and inclusion of m_accrual
        // into the calculated accrual is dependant on a block's height being
        // lower than the last superblock's.
        // TODO(div72): Improve mockability of Tally.
        pindex->nHeight = -1;
        mapBlockIndex[pindex->GetBlockHash()] = pindex;
        for (int i = 1; i < Params().GetConsensus().MRCZeroPaymentInterval / (24 * 60 * 60) + 2; ++i) {
            phash = new uint256(InsecureRandBytes(32));
            CBlockIndex* pindexNext = GRC::MockBlockIndex::InsertBlockIndex(*phash);
            pindexNext->nHeight = i;
            pindexNext->nTime = pindex->nTime + 24 * 60 * 60;
            pindexNext->nVersion = 12;
            pindex->pnext = pindexNext;
            pindexNext->pprev = pindex;
            pindexNext->phashBlock = phash;
            pindex = pindexNext;
        }

        // These tests were written with these assumptions. If this statement
        // does not hold true in the future, please update.
        assert(pindex->nHeight >= 7);
        assert(pindex->nHeight < 179);

        CTransaction tx;
        tx.nTime = pindexGenesisBlock->nTime;

        key.MakeNewKey(false);
        wallet->AddKey(key);

        GRC::Contract contract = GRC::MakeContract<GRC::BeaconPayload>(
                GRC::ContractAction::ADD,
                cpid,
                key.GetPubKey()
        );

        account.m_first_block_ptr = pindexGenesisBlock;
        account.m_last_block_ptr = pindexGenesisBlock;

        GRC::GetBeaconRegistry().Add({contract, tx, pindexGenesisBlock});
        GRC::GetBeaconRegistry().ActivatePending({key.GetPubKey().GetID()}, tx.nTime, uint256(), 0);
        beacon = *GRC::GetBeaconRegistry().TryActive(cpid, pindex->nTime);

        SetMockTime(1);

        gArgs.ForceSetArg("forcecpid", cpid.ToString());
        GRC::Researcher::Reload({}, {});
    }

    ~Setup() {
        GRC::GetBeaconRegistry().Reset();
        gArgs.ForceSetArg("forcecpid", "");
        gArgs.ForceSetArg("email", "investor");
        GRC::Researcher::Reload();
        gArgs.ForceSetArg("email", "");
        GRC::Tally::RemoveAccount(cpid);

        SetMockTime(0);

        for (CBlockIndex* tip = pindex; tip->pprev; tip = tip->pprev) {
            mapBlockIndex.erase(tip->GetBlockHash());
            delete tip->phashBlock;
        }

        mapBlockIndex.erase(pindexGenesisBlock->GetBlockHash());
        delete pindexGenesisBlock->phashBlock;
    }
};
} // Anonymous namespace

BOOST_FIXTURE_TEST_SUITE(MRC, Setup)

BOOST_AUTO_TEST_CASE(it_properly_records_blocks)
{
    pindex->AddMRCResearcherContext(cpid, 72, 0.0);
    pindex->pprev->pprev->SetResearcherContext(cpid, 72, 0.0);
    pindex->pprev->pprev->pprev->pprev->AddMRCResearcherContext(cpid, 72, 0.0);
    pindex->pprev->pprev->pprev->pprev->pprev->pprev->SetResearcherContext(cpid, 72, 0.0);

    CBlockIndex* index{pindexGenesisBlock};
    while (index) {
        GRC::Tally::RecordRewardBlock(index);
        index = index->pnext;
    }

    BOOST_CHECK(account.m_last_block_ptr == pindex->pprev);
    GRC::Tally::ForgetRewardBlock(pindex);
    BOOST_CHECK(account.m_last_block_ptr == pindex->pprev->pprev);
    GRC::Tally::ForgetRewardBlock(pindex->pprev->pprev);
    BOOST_CHECK(account.m_last_block_ptr == pindex->pprev->pprev->pprev->pprev->pprev);
    GRC::Tally::ForgetRewardBlock(pindex->pprev->pprev->pprev->pprev);
    BOOST_CHECK(account.m_last_block_ptr == pindex->pprev->pprev->pprev->pprev->pprev->pprev);
    GRC::Tally::ForgetRewardBlock(pindex->pprev->pprev->pprev->pprev->pprev->pprev);

    BOOST_CHECK(account.m_last_block_ptr == nullptr);
    account.m_last_block_ptr = account.m_first_block_ptr;
}

BOOST_AUTO_TEST_CASE(it_has_proper_fees_for_newbies)
{
    GRC::MRC mrc;
    mrc.m_mining_id = cpid;
    mrc.m_research_subsidy = 72;

    // Before:
    mrc.m_last_block_hash = pindex->pprev->pprev->GetBlockHash();
    BOOST_CHECK_EQUAL(mrc.ComputeMRCFee(), mrc.m_research_subsidy);

    // At the border:
    mrc.m_last_block_hash = pindex->pprev->GetBlockHash();
    BOOST_CHECK_EQUAL(mrc.ComputeMRCFee(), 28);

    // After:
    mrc.m_last_block_hash = pindex->GetBlockHash();
    BOOST_CHECK_EQUAL(mrc.ComputeMRCFee(), 26);
}

BOOST_AUTO_TEST_CASE(it_rejects_invalid_claims)
{
    GRC::MRC mrc;

    mrc.m_mining_id = cpid;
    mrc.m_client_version = "6.0.0.0";
    mrc.m_last_block_hash = pindex->GetBlockHash();

    BOOST_CHECK(!mrc.WellFormed());

    mrc.m_research_subsidy = 72;
    account.m_accrual = 72;

    mrc.Sign(key);
    BOOST_CHECK(mrc.WellFormed());

    BOOST_CHECK(!ValidateMRC(pindex, mrc));

    mrc.m_fee = mrc.ComputeMRCFee();
    BOOST_CHECK(ValidateMRC(pindex, mrc));

    mrc.m_research_subsidy = 9223372036854775807; // Get rich.
    BOOST_CHECK(!ValidateMRC(pindex, mrc));
    mrc.m_research_subsidy = 72;

    mrc.m_last_block_hash = pindex->pprev->GetBlockHash(); // Older request.
    mrc.Sign(key);
    BOOST_CHECK(!ValidateMRC(pindex, mrc));
    mrc.m_last_block_hash = pindex->GetBlockHash();
    mrc.Sign(key);

    mrc.m_fee = 0; // Tax evasion.
    BOOST_CHECK(!ValidateMRC(pindex, mrc));
}

BOOST_AUTO_TEST_CASE(createmrc_creates_valid_mrcs)
{
    account.m_accrual = 72;
    GRC::MRC mrc;
    CAmount reward, fee;
    GRC::CreateMRC(pindex->pprev, mrc, reward, fee, wallet);

    BOOST_CHECK_EQUAL(reward, 72);
    BOOST_CHECK_EQUAL(fee, 28);

    BOOST_CHECK(mrc.WellFormed());
    BOOST_CHECK(ValidateMRC(pindex->pprev, mrc));
}

BOOST_AUTO_TEST_CASE(it_creates_valid_mrc_claims)
{
    CBlock block;
    block.vtx.resize(2);
    block.vtx[1].vin.resize(1);
    block.vtx[1].vout.resize(2);
    std::map<GRC::Cpid, std::pair<uint256, GRC::MRC>> mrc_map;
    std::map<GRC::Cpid, uint256> mrc_tx_map;

    account.m_accrual = 72;
    pindex->pprev->AddMRCResearcherContext(cpid, 72, 0.0);

    BOOST_CHECK(CreateRestOfTheBlock(block, pindex->pprev, mrc_map));

    GRC::MRC mrc;
    CAmount reward, fee;
    GRC::CreateMRC(pindex->pprev, mrc, reward, fee, wallet);
    mrc_map[cpid] = {uint256{}, mrc};

    GRC::Claim claim;

    LOCK(cs_main);

    BOOST_CHECK(CreateGridcoinReward(block, pindex->pprev, reward, claim));

    BOOST_CHECK(CreateMRCRewards(block, mrc_map, mrc_tx_map, claim, wallet));

    // TODO(div72): Separate this test into pieces and actually have it do
    // some useful testing by testing the validation logic against it.
    // Currently the miner code is too coupled together which makes this
    // infeasible without having a mess of spaghetti code for working
    // around this.
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before><commit_msg>id 0 is handled differently now when playing back a sound<commit_after><|endoftext|>


<commit_before>/** Implementation of canonpy.
 */

#include <canonpy.h>

#include <Python.h>

#include <algorithm>
#include <string>
#include <vector>

#include <libcanon/perm.h>

using libcanon::Simple_perm;
using libcanon::Point;
using libcanon::Point_vec;

//
// Perm class
// ==========
//
// Internal functions
// ------------------
//
// These functions are not directly set to Python types but are used
// internally.  They are also used by other parts of this extension.
//

/** Builds a tuple object for a permutation.
 *
 * This function creates a pair, where the first field is a list of integers
 * for the preimage array, and the second field is the accompanied action,
 * which is also encoded as an integer.
 */

static PyObject* build_perm_to_tuple(const Simple_perm& perm)
{

    PyObject* pre_images = NULL;
    PyObject* res = NULL;
    PyObject* acc = NULL;
    size_t size = perm.size();

    pre_images = PyList_New(size);
    if (!pre_images)
        goto error;
    for (size_t i = 0; i < size; ++i) {
        PyObject* curr = Py_BuildValue("n", perm >> i);
        if (curr) {
            PyList_SetItem(pre_images, i, curr);
        } else {
            goto error;
        }
    }

    acc = Py_BuildValue("b", perm.acc());
    if (!acc)
        goto error;

    res = PyTuple_New(2);
    if (!res)
        goto error;

    PyTuple_SET_ITEM(res, 0, pre_images);
    PyTuple_SET_ITEM(res, 1, acc);

    return (PyObject*)res;

error:
    Py_XDECREF(pre_images);
    Py_XDECREF(res);
    Py_XDECREF(acc);
    return NULL;
}

/** Builds a permutation from its construction arguments.
 *
 * An iterable of positive integers for the pre-image array needs to be given
 * as the first argument.  The accompanied action can be optionally given as
 * another integral argument, or by the keyword ``acc``.
 *
 * If the arguments are not valid, an integer exception will be thrown and the
 * Python exception will be set.
 *
 * This function is designed to be compatible with the result from the function
 * `build_perm_to_tuple`.  However, more general input format is accepted.
 */

static Simple_perm make_perm_from_args(PyObject* args, PyObject* kwargs)
{
    PyObject* pre_images;
    char acc = 0;

    // We only need a simple internal code, actual error is set to the Python
    // stack.
    constexpr int err_code = 1;

    static char* kwlist[] = { "pre_images", "acc", NULL };

    auto args_stat = PyArg_ParseTupleAndKeywords(
        args, kwargs, "O|b", kwlist, &pre_images, &acc);

    if (!args_stat)
        throw err_code;

    Point_vec pre_images_vec{};
    std::vector<bool> image_set{};

    PyObject* pre_images_iter = PyObject_GetIter(pre_images);
    if (!pre_images_iter)
        throw err_code;

    // Iterator of pre-images is always going to be decrefed after the
    // following block.  This boolean controls if we are going to return or
    // throw.
    bool if_err = false;

    try {
        PyObject* pre_image_obj;
        while ((pre_image_obj = PyIter_Next(pre_images_iter))) {

            if (!PyLong_Check(pre_image_obj)) {
                throw err_code;
            }
            Point pre_image = PyLong_AsUnsignedLong(pre_image_obj);

            // Release reference right here since its content is already
            // extracted.  In this way, the error handling does not need to
            // worry about it any more.
            Py_DECREF(pre_image_obj);

            if (PyErr_Occurred()) {
                throw err_code;
            }

            pre_images_vec.push_back(pre_image);
            size_t req_size = pre_image + 1;
            if (image_set.size() < req_size)
                image_set.resize(req_size, false);
            if (image_set[pre_image]) {
                std::string err_msg("The image of ");
                err_msg.append(std::to_string(pre_image));
                err_msg.append(" has already been set.");
                PyErr_SetString(PyExc_ValueError, err_msg.c_str());
                throw err_code;
            } else {
                image_set[pre_image] = true;
            }
        }

        // Non StopIteration error.
        if (PyErr_Occurred()) {
            throw err_code;
        }

        auto first_not_set
            = std::find(image_set.begin(), image_set.end(), false);
        if (first_not_set != image_set.end()) {
            std::string err_msg("The image of ");
            err_msg.append(std::to_string(first_not_set - image_set.begin()));
            err_msg.append(" is not set.");
            PyErr_SetString(PyExc_ValueError, err_msg.c_str());
            throw err_code;
        }

    } catch (int) {
        if_err = true;
    }

    Py_DECREF(pre_images_iter);
    if (if_err) {
        throw err_code;
    } else {
        return Simple_perm(std::move(pre_images_vec), acc);
    }
}

//
// Interface functions
// -------------------
//

const static char* perm_getnewargs_doc
    = "Get the arguments for new to construct the Perm.";

static PyObject* perm_getnewargs(Perm_object* self)
{
    // Here we directly use the tuple format of a perm.

    return build_perm_to_tuple(self->perm);
}

/** Gets the size of the permutation domain of a Perm.
 */

static Py_ssize_t perm_length(Perm_object* self)
{
    // The type should be the same, size_t and Py_ssize_t.

    return self->perm.size();
}

/** Gets the pre-image of a point.
 *
 * A new integer object will be built by this function.
 */

static PyObject* perm_item(Perm_object* self, Py_ssize_t i)
{
    if (i < 0) {
        PyErr_SetString(PyExc_IndexError, "Points should be positive.");
        return NULL;
    }
    size_t idx = i;
    if (idx >= self->perm.size()) {
        PyErr_SetString(PyExc_IndexError, "Point outside permutation domain");
        return NULL;
    }

    return Py_BuildValue("n", self->perm >> i);
}

/** Gets the accompanied action of a Perm.
 */

static PyObject* perm_get_acc(Perm_object* self, void* closure)
{
    // Note that the accompanied action is a byte in simple perm.

    return Py_BuildValue("b", self->perm.acc());
}

/** Deallocates a perm instance.
 */

static void perm_dealloc(Perm_object* self)
{
    self->perm.~Simple_perm();

    // For subclassing.
    Py_TYPE(self)->tp_free((PyObject*)self);
}

/** Forms the string representation of a Perm object.
 */

static PyObject* perm_repr(Perm_object* self)
{
    const Simple_perm& perm = self->perm;

    std::wstring repr(L"Perm(");

    size_t size = perm.size();

    if (size > 0) {
        for (size_t i = 0; i < size; ++i) {
            if (i == 0) {
                repr.append(L"[");
            } else {
                repr.append(L", ");
            }
            repr.append(std::to_wstring(perm >> i));
        }
        repr.append(L"]");

        // Add the accompanied action only when we need.
        char acc = perm.acc();
        if (acc != 0) {
            repr.append(L", ");
            repr.append(std::to_wstring(acc));
        }
    }

    // This is used for empty or non-empty permutation.
    repr.append(L")");

    return PyUnicode_FromUnicode(repr.data(), repr.size());
}

/** Creates a new Perm object.
 *
 * The actual work is delegated to the Python/Perm interface function.
 */

static PyObject* perm_new(PyTypeObject* type, PyObject* args, PyObject* kwargs)
{
    Perm_object* self;

    // Pay attention to subclassing.
    self = (Perm_object*)type->tp_alloc(type, 0);

    if (!self)
        return NULL;

    Simple_perm perm{};
    try {
        perm = make_perm_from_args(args, kwargs);
    } catch (int) {
        Py_DECREF(self);
        return NULL;
    }

    new (&self->perm) Simple_perm(std::move(perm));
    return (PyObject*)self;
}

//
// Class definition
// ----------------
//

/** Methods for Perm objects.
 */

static PyMethodDef perm_methods[] = {
    { "__getnewargs__", (PyCFunction)perm_getnewargs, METH_NOARGS,
        perm_getnewargs_doc },
    { NULL, NULL } /* sentinel */
};

/** Sequence operations for Perm objects.
 *
 * Here we only support size and pre-image query.
 */

// clang-format off
static PySequenceMethods perm_as_sequence = {
    (lenfunc)perm_length,                       /* sq_length */
    0,                                          /* sq_concat */
    0,                                          /* sq_repeat */
    (ssizeargfunc)perm_item,                    /* sq_item */
    0,                                          /* sq_slice */
    0,                                          /* sq_ass_item */
    0,                                          /* sq_ass_slice */
    0                                           /* sq_contains */
};
// clang-format on

/** Accessors for Perms.
 *
 * The accompanied action query is made here.
 */

// clang-format off
static PyGetSetDef perm_getsets[] = {
    { "acc", (getter)perm_get_acc, NULL, "The accompanied action.", NULL },
    { NULL }
};
// clang-format on

/** Perm type doc string.
  */

static const char* perm_doc =
    R"__doc__(Permutation of points with accompanied action.

Permutations can be constructed from an iterable giving the pre-image of the
points and an optional integral value for the accompanied action.  The
accompanied action can be given positionally or by the keyword ``acc``, and it
will be manipulated according to the convention in libcanon.

Querying the length of a Perm object gives the size of the permutation domain,
while indexing it gives the pre-image of the given integral point.  The
accompanied action can be obtained by getting the attribute ``acc``.
Otherwise, this data type is mostly opaque.

)__doc__";

/** Type definition for Perm class.
 */

// clang-format off
static PyTypeObject perm_type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    "drudge.canonpy.Perm",
    sizeof(Perm_object),
    0,
    (destructor) perm_dealloc,                  /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_reserved */
    (reprfunc) perm_repr,                       /* tp_repr */
    0,                                          /* tp_as_number */
    &perm_as_sequence,                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    0, /* In main. */                           /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT,                         /* tp_flags */
    perm_doc,                                   /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    perm_methods,                               /* tp_methods */
    0,                                          /* tp_members */
    perm_getsets,                               /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    perm_new,                                   /* tp_new */
    0,                                          /* tp_free */
};
// clang-format on

//
// Python module definition
// ========================
//

/** Docstring for the canonpy module.
 */

static const char* canonpy_docstring
    = R"__doc__(Canonpy, simple wrapper over libcanon for Python.

This wrapper of libcanon is directly targeted towards usage using drudge.
Here, we have a class `Perm`, which wraps over the `Simple_perm` class in
libcanon, another class `SimsTransv`, which wraps over the `Sims_trasv` class
for `Simple_perm`.  And we also have the function `canon_eldag` to canonicalize
an Eldag.

)__doc__";

/** Methods in the canonpy module.
 */

static PyMethodDef canonpy_methods[] = { { NULL, NULL, 0, NULL } };

/** Executes the initialization of the canonpy module.
 */

static int canonpy_exec(PyObject* m)
{
    //
    // Add the class for Perm.
    //

    perm_type.tp_getattro = PyObject_GenericGetAttr;
    if (PyType_Ready(&perm_type) < 0)
        return -1;
    Py_INCREF(&perm_type);
    PyModule_AddObject(m, "Perm", (PyObject*)&perm_type);

    return 0;
}

/** Slots for for canonpy module definition.
 */

static struct PyModuleDef_Slot canonpy_slots[] = {
    { Py_mod_exec, (void*)canonpy_exec }, { 0, NULL },
};

/** Canonpy module definition.
 */

// clang-format off

static struct PyModuleDef canonpy_module = {
    PyModuleDef_HEAD_INIT,
    "drudge.canonpy",
    canonpy_docstring,
    0, // m-size
    canonpy_methods,
    canonpy_slots,
    NULL, // Transverse
    NULL, // Clear
    NULL  // Free
};

// clang-format on

/** The published canonpy function.
 */

PyMODINIT_FUNC PyInit_canonpy(void)
{
    return PyModuleDef_Init(&canonpy_module);
}
<commit_msg>Add error when point given to Perm is not integral<commit_after>/** Implementation of canonpy.
 */

#include <canonpy.h>

#include <Python.h>

#include <algorithm>
#include <string>
#include <vector>

#include <libcanon/perm.h>

using libcanon::Simple_perm;
using libcanon::Point;
using libcanon::Point_vec;

//
// Perm class
// ==========
//
// Internal functions
// ------------------
//
// These functions are not directly set to Python types but are used
// internally.  They are also used by other parts of this extension.
//

/** Builds a tuple object for a permutation.
 *
 * This function creates a pair, where the first field is a list of integers
 * for the preimage array, and the second field is the accompanied action,
 * which is also encoded as an integer.
 */

static PyObject* build_perm_to_tuple(const Simple_perm& perm)
{

    PyObject* pre_images = NULL;
    PyObject* res = NULL;
    PyObject* acc = NULL;
    size_t size = perm.size();

    pre_images = PyList_New(size);
    if (!pre_images)
        goto error;
    for (size_t i = 0; i < size; ++i) {
        PyObject* curr = Py_BuildValue("n", perm >> i);
        if (curr) {
            PyList_SetItem(pre_images, i, curr);
        } else {
            goto error;
        }
    }

    acc = Py_BuildValue("b", perm.acc());
    if (!acc)
        goto error;

    res = PyTuple_New(2);
    if (!res)
        goto error;

    PyTuple_SET_ITEM(res, 0, pre_images);
    PyTuple_SET_ITEM(res, 1, acc);

    return (PyObject*)res;

error:
    Py_XDECREF(pre_images);
    Py_XDECREF(res);
    Py_XDECREF(acc);
    return NULL;
}

/** Builds a permutation from its construction arguments.
 *
 * An iterable of positive integers for the pre-image array needs to be given
 * as the first argument.  The accompanied action can be optionally given as
 * another integral argument, or by the keyword ``acc``.
 *
 * If the arguments are not valid, an integer exception will be thrown and the
 * Python exception will be set.
 *
 * This function is designed to be compatible with the result from the function
 * `build_perm_to_tuple`.  However, more general input format is accepted.
 */

static Simple_perm make_perm_from_args(PyObject* args, PyObject* kwargs)
{
    PyObject* pre_images;
    char acc = 0;

    // We only need a simple internal code, actual error is set to the Python
    // stack.
    constexpr int err_code = 1;

    static char* kwlist[] = { "pre_images", "acc", NULL };

    auto args_stat = PyArg_ParseTupleAndKeywords(
        args, kwargs, "O|b", kwlist, &pre_images, &acc);

    if (!args_stat)
        throw err_code;

    Point_vec pre_images_vec{};
    std::vector<bool> image_set{};

    PyObject* pre_images_iter = PyObject_GetIter(pre_images);
    if (!pre_images_iter)
        throw err_code;

    // Iterator of pre-images is always going to be decrefed after the
    // following block.  This boolean controls if we are going to return or
    // throw.
    bool if_err = false;

    try {
        PyObject* pre_image_obj;
        while ((pre_image_obj = PyIter_Next(pre_images_iter))) {

            if (!PyLong_Check(pre_image_obj)) {
                PyErr_SetString(PyExc_ValueError, "Non-integral point given");
                throw err_code;
            }
            Point pre_image = PyLong_AsUnsignedLong(pre_image_obj);

            // Release reference right here since its content is already
            // extracted.  In this way, the error handling does not need to
            // worry about it any more.
            Py_DECREF(pre_image_obj);

            if (PyErr_Occurred()) {
                throw err_code;
            }

            pre_images_vec.push_back(pre_image);
            size_t req_size = pre_image + 1;
            if (image_set.size() < req_size)
                image_set.resize(req_size, false);
            if (image_set[pre_image]) {
                std::string err_msg("The image of ");
                err_msg.append(std::to_string(pre_image));
                err_msg.append(" has already been set.");
                PyErr_SetString(PyExc_ValueError, err_msg.c_str());
                throw err_code;
            } else {
                image_set[pre_image] = true;
            }
        }

        // Non StopIteration error.
        if (PyErr_Occurred()) {
            throw err_code;
        }

        auto first_not_set
            = std::find(image_set.begin(), image_set.end(), false);
        if (first_not_set != image_set.end()) {
            std::string err_msg("The image of ");
            err_msg.append(std::to_string(first_not_set - image_set.begin()));
            err_msg.append(" is not set.");
            PyErr_SetString(PyExc_ValueError, err_msg.c_str());
            throw err_code;
        }

    } catch (int) {
        if_err = true;
    }

    Py_DECREF(pre_images_iter);
    if (if_err) {
        throw err_code;
    } else {
        return Simple_perm(std::move(pre_images_vec), acc);
    }
}

//
// Interface functions
// -------------------
//

const static char* perm_getnewargs_doc
    = "Get the arguments for new to construct the Perm.";

static PyObject* perm_getnewargs(Perm_object* self)
{
    // Here we directly use the tuple format of a perm.

    return build_perm_to_tuple(self->perm);
}

/** Gets the size of the permutation domain of a Perm.
 */

static Py_ssize_t perm_length(Perm_object* self)
{
    // The type should be the same, size_t and Py_ssize_t.

    return self->perm.size();
}

/** Gets the pre-image of a point.
 *
 * A new integer object will be built by this function.
 */

static PyObject* perm_item(Perm_object* self, Py_ssize_t i)
{
    if (i < 0) {
        PyErr_SetString(PyExc_IndexError, "Points should be positive.");
        return NULL;
    }
    size_t idx = i;
    if (idx >= self->perm.size()) {
        PyErr_SetString(PyExc_IndexError, "Point outside permutation domain");
        return NULL;
    }

    return Py_BuildValue("n", self->perm >> i);
}

/** Gets the accompanied action of a Perm.
 */

static PyObject* perm_get_acc(Perm_object* self, void* closure)
{
    // Note that the accompanied action is a byte in simple perm.

    return Py_BuildValue("b", self->perm.acc());
}

/** Deallocates a perm instance.
 */

static void perm_dealloc(Perm_object* self)
{
    self->perm.~Simple_perm();

    // For subclassing.
    Py_TYPE(self)->tp_free((PyObject*)self);
}

/** Forms the string representation of a Perm object.
 */

static PyObject* perm_repr(Perm_object* self)
{
    const Simple_perm& perm = self->perm;

    std::wstring repr(L"Perm(");

    size_t size = perm.size();

    if (size > 0) {
        for (size_t i = 0; i < size; ++i) {
            if (i == 0) {
                repr.append(L"[");
            } else {
                repr.append(L", ");
            }
            repr.append(std::to_wstring(perm >> i));
        }
        repr.append(L"]");

        // Add the accompanied action only when we need.
        char acc = perm.acc();
        if (acc != 0) {
            repr.append(L", ");
            repr.append(std::to_wstring(acc));
        }
    }

    // This is used for empty or non-empty permutation.
    repr.append(L")");

    return PyUnicode_FromUnicode(repr.data(), repr.size());
}

/** Creates a new Perm object.
 *
 * The actual work is delegated to the Python/Perm interface function.
 */

static PyObject* perm_new(PyTypeObject* type, PyObject* args, PyObject* kwargs)
{
    Perm_object* self;

    // Pay attention to subclassing.
    self = (Perm_object*)type->tp_alloc(type, 0);

    if (!self)
        return NULL;

    Simple_perm perm{};
    try {
        perm = make_perm_from_args(args, kwargs);
    } catch (int) {
        Py_DECREF(self);
        return NULL;
    }

    new (&self->perm) Simple_perm(std::move(perm));
    return (PyObject*)self;
}

//
// Class definition
// ----------------
//

/** Methods for Perm objects.
 */

static PyMethodDef perm_methods[] = {
    { "__getnewargs__", (PyCFunction)perm_getnewargs, METH_NOARGS,
        perm_getnewargs_doc },
    { NULL, NULL } /* sentinel */
};

/** Sequence operations for Perm objects.
 *
 * Here we only support size and pre-image query.
 */

// clang-format off
static PySequenceMethods perm_as_sequence = {
    (lenfunc)perm_length,                       /* sq_length */
    0,                                          /* sq_concat */
    0,                                          /* sq_repeat */
    (ssizeargfunc)perm_item,                    /* sq_item */
    0,                                          /* sq_slice */
    0,                                          /* sq_ass_item */
    0,                                          /* sq_ass_slice */
    0                                           /* sq_contains */
};
// clang-format on

/** Accessors for Perms.
 *
 * The accompanied action query is made here.
 */

// clang-format off
static PyGetSetDef perm_getsets[] = {
    { "acc", (getter)perm_get_acc, NULL, "The accompanied action.", NULL },
    { NULL }
};
// clang-format on

/** Perm type doc string.
  */

static const char* perm_doc =
    R"__doc__(Permutation of points with accompanied action.

Permutations can be constructed from an iterable giving the pre-image of the
points and an optional integral value for the accompanied action.  The
accompanied action can be given positionally or by the keyword ``acc``, and it
will be manipulated according to the convention in libcanon.

Querying the length of a Perm object gives the size of the permutation domain,
while indexing it gives the pre-image of the given integral point.  The
accompanied action can be obtained by getting the attribute ``acc``.
Otherwise, this data type is mostly opaque.

)__doc__";

/** Type definition for Perm class.
 */

// clang-format off
static PyTypeObject perm_type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    "drudge.canonpy.Perm",
    sizeof(Perm_object),
    0,
    (destructor) perm_dealloc,                  /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_reserved */
    (reprfunc) perm_repr,                       /* tp_repr */
    0,                                          /* tp_as_number */
    &perm_as_sequence,                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    0, /* In main. */                           /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT,                         /* tp_flags */
    perm_doc,                                   /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    perm_methods,                               /* tp_methods */
    0,                                          /* tp_members */
    perm_getsets,                               /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    perm_new,                                   /* tp_new */
    0,                                          /* tp_free */
};
// clang-format on

//
// Python module definition
// ========================
//

/** Docstring for the canonpy module.
 */

static const char* canonpy_docstring
    = R"__doc__(Canonpy, simple wrapper over libcanon for Python.

This wrapper of libcanon is directly targeted towards usage using drudge.
Here, we have a class `Perm`, which wraps over the `Simple_perm` class in
libcanon, another class `SimsTransv`, which wraps over the `Sims_trasv` class
for `Simple_perm`.  And we also have the function `canon_eldag` to canonicalize
an Eldag.

)__doc__";

/** Methods in the canonpy module.
 */

static PyMethodDef canonpy_methods[] = { { NULL, NULL, 0, NULL } };

/** Executes the initialization of the canonpy module.
 */

static int canonpy_exec(PyObject* m)
{
    //
    // Add the class for Perm.
    //

    perm_type.tp_getattro = PyObject_GenericGetAttr;
    if (PyType_Ready(&perm_type) < 0)
        return -1;
    Py_INCREF(&perm_type);
    PyModule_AddObject(m, "Perm", (PyObject*)&perm_type);

    return 0;
}

/** Slots for for canonpy module definition.
 */

static struct PyModuleDef_Slot canonpy_slots[] = {
    { Py_mod_exec, (void*)canonpy_exec }, { 0, NULL },
};

/** Canonpy module definition.
 */

// clang-format off

static struct PyModuleDef canonpy_module = {
    PyModuleDef_HEAD_INIT,
    "drudge.canonpy",
    canonpy_docstring,
    0, // m-size
    canonpy_methods,
    canonpy_slots,
    NULL, // Transverse
    NULL, // Clear
    NULL  // Free
};

// clang-format on

/** The published canonpy function.
 */

PyMODINIT_FUNC PyInit_canonpy(void)
{
    return PyModuleDef_Init(&canonpy_module);
}
<|endoftext|>


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

#include <cassert>
#include <flens/blas.h>
#include <flens/refcounter.h>
#include <flens/hacksforgmpxx.h>

namespace flens {

//== ConstArrayView ============================================================

template <typename T>
ConstArrayView<T>::ConstArrayView(const void *storage,
                                  const T *data,
                                  int length,
                                  int stride,
                                  int firstIndex)
    : _storage(storage),
      _data(data),
      _length(length),
      _stride(stride),
      _firstIndex(firstIndex)
{
    RefCounter::attach(_storage);
}

template <typename T>
ConstArrayView<T>::ConstArrayView(const NoView &rhs)
    : _storage(rhs.data()),
      _data(rhs.data()-rhs.firstIndex()),
      _length(rhs.length()),
      _stride(rhs.stride()),
      _firstIndex(rhs.firstIndex())
{
    RefCounter::attach(_storage);
}

template <typename T>
ConstArrayView<T>::ConstArrayView(const ConstArrayView &rhs)
    : _storage(rhs._storage),
      _data(rhs._data),
      _length(rhs._length),
      _stride(rhs._stride),
      _firstIndex(rhs._firstIndex)
{
    RefCounter::attach(_storage);
}

template <typename T>
ConstArrayView<T>::ConstArrayView(const ArrayView<T> &rhs)
    : _storage(rhs.storage()),
      _data(rhs.data()-rhs.firstIndex()),
      _length(rhs.length()),
      _stride(rhs.stride()),
      _firstIndex(rhs.firstIndex())
{
    RefCounter::attach(_storage);
}

template <typename T>
ConstArrayView<T>::~ConstArrayView()
{
    if (RefCounter::detach(_storage)) {
        assert(0);  // Const view can not free memory
    }
}

template <typename T>
const T &
ConstArrayView<T>::operator()(int index) const
{
    assert(index>=_firstIndex);
    assert(index<_firstIndex+_length);

    return _data[_firstIndex + _stride*(index-_firstIndex)];
}

template <typename T>
int
ConstArrayView<T>::firstIndex() const
{
    return _firstIndex;
}

template <typename T>
int
ConstArrayView<T>::lastIndex() const
{
    return _firstIndex+_length-1;
}

template <typename T>
int
ConstArrayView<T>::length() const
{
    return _length;
}

template <typename T>
int
ConstArrayView<T>::stride() const
{
    return _stride;
}

template <typename T>
const T *
ConstArrayView<T>::data() const
{
    return &_data[_firstIndex];
}

template <typename T>
const void *
ConstArrayView<T>::storage() const
{
    return _storage;
}

template <typename T>
ConstArrayView<T>
ConstArrayView<T>::view(int from, int to, int stride, int firstViewIndex) const
{
    assert(from<=to);
    assert(stride>=1);
    return ConstArrayView<T>(_storage,                             // storage
                             &(this->operator()(from))-_firstIndex,// data
                             (to-from)/stride+1,                   // length
                             stride*_stride,                       // stride
                             firstViewIndex);                     // firstIndex
}

//== ArrayView =================================================================

template <typename T>
ArrayView<T>::ArrayView(void *storage,
                        T *data,
                        int length,
                        int stride,
                        int firstIndex)
    : _storage(storage),
      _data(data),
      _length(length),
      _stride(stride),
      _firstIndex(firstIndex)
{
    RefCounter::attach(_storage);
}

template <typename T>
ArrayView<T>::ArrayView(const ArrayView &rhs)
    : _storage(rhs._storage),
      _data(rhs._data),
      _length(rhs._length),
      _stride(rhs._stride),
      _firstIndex(rhs._firstIndex)
{
    RefCounter::attach(_storage);
}

template <typename T>
ArrayView<T>::~ArrayView()
{
    if (RefCounter::detach(_storage)) {
        free(_storage);
    }
}

template <typename T>
ArrayView<T> &
ArrayView<T>::operator=(const Array<T> &rhs)
{
    assert(length()==rhs.length());
    copy(length(), rhs.data(), rhs.stride(), data(), stride());
    return *this;
}

template <typename T>
ArrayView<T> &
ArrayView<T>::operator=(const ArrayView<T> &rhs)
{
    if (this!=&rhs) {
        assert(length()==rhs.length());
        copy(length(), rhs.data(), rhs.stride(), data(), stride());
    }
    return *this;
}

template <typename T>
ArrayView<T> &
ArrayView<T>::operator=(const ConstArrayView<T> &rhs)
{
    assert(length()==rhs.length());
    copy(length(), rhs.data(), rhs.stride(), data(), stride());
    return *this;
}

template <typename T>
const T &
ArrayView<T>::operator()(int index) const
{
    assert(index>=_firstIndex);
    assert(index<_firstIndex+_length);

    return _data[_firstIndex + _stride*(index-_firstIndex)];
}

template <typename T>
T &
ArrayView<T>::operator()(int index)
{
    assert(index>=_firstIndex);
    assert(index<_firstIndex+_length);

    return _data[_firstIndex + _stride*(index-_firstIndex)];
}

template <typename T>
int
ArrayView<T>::firstIndex() const
{
    return _firstIndex;
}

template <typename T>
int
ArrayView<T>::lastIndex() const
{
    return _firstIndex+_length-1;
}

template <typename T>
int
ArrayView<T>::length() const
{
    return _length;
}

template <typename T>
int
ArrayView<T>::stride() const
{
    return _stride;
}

template <typename T>
const T *
ArrayView<T>::data() const
{
    return &_data[_firstIndex];
}

template <typename T>
T *
ArrayView<T>::data()
{
    return &_data[_firstIndex];
}

template <typename T>
const void *
ArrayView<T>::storage() const
{
    return _storage;
}

template <typename T>
void
ArrayView<T>::resize(int length, int firstIndex)
{
    assert((length==_length) && (firstIndex==_firstIndex));
}

template <typename T>
void
ArrayView<T>::resizeOrClear(int length, int firstIndex)
{
    assert((length==_length) && (firstIndex==_firstIndex));
    std::fill_n(this->data(), _length, T(0));
}

template <typename T>
ConstArrayView<T>
ArrayView<T>::view(int from, int to, int stride, int firstViewIndex) const
{
    assert(from<=to);
    assert(stride>=1);
    return ConstArrayView<T>(_storage,                             // storage
                             &(this->operator()(from))-_firstIndex,// data
                             (to-from)/stride+1,                   // length
                             stride*_stride,                       // stride
                             firstViewIndex);
}

template <typename T>
ArrayView<T>
ArrayView<T>::view(int from, int to, int stride, int firstViewIndex)
{
    assert(from<=to);
    assert(stride>=1);
    return ArrayView<T>(_storage,                              // storage
                        &(this->operator()(from))-_firstIndex, // data
                        (to-from)/stride+1,                    // length
                        stride*_stride,                        // stride
                        firstViewIndex);
}

template <typename T>
void
ArrayView<T>::shiftIndexTo(int firstIndex)
{
    assert(_firstIndex==firstIndex);
}

//== Array =====================================================================

template <typename T>
Array<T>::Array()
    : _length(0), _firstIndex(0), _data(0)
{
}

template <typename T>
Array<T>::Array(int length, int firstIndex)
    : _length(length), _firstIndex(firstIndex), _data(0)
{
    _allocate();
}

template <typename T>
Array<T>::Array(const Array<T> &rhs)
    : _length(rhs.length()), _firstIndex(rhs.firstIndex()), _data(0)
{
    _allocate();
    copy(length(), rhs.data(), rhs.stride(), data(), stride());
}

template <typename T>
Array<T>::Array(const ArrayView<T> &rhs)
    : _length(rhs.length()), _firstIndex(rhs.firstIndex()), _data(0)
{
    _allocate();
    copy(length(), rhs.data(), rhs.stride(), data(), stride());
}

template <typename T>
Array<T>::Array(const ConstArrayView<T> &rhs)
    : _length(rhs.length()), _firstIndex(rhs.firstIndex()), _data(0)
{
    _allocate();
    copy(length(), rhs.data(), rhs.stride(), data(), stride());
}

template <typename T>
Array<T>::~Array()
{
    _release();
}

template <typename T>
Array<T> &
Array<T>::operator=(const Array<T> &rhs)
{
    if (this!=&rhs) {
        if (length()!=rhs.length()) {
            resize(rhs.length(), rhs.firstIndex());
        }
        copy(length(), rhs.data(), rhs.stride(), data(), stride());
    }
    return *this;
}

template <typename T>
Array<T> &
Array<T>::operator=(const ArrayView<T> &rhs)
{
    if (length()!=rhs.length()) {
        resize(rhs.length(), rhs.firstIndex());
    }
    copy(length(), rhs.data(), rhs.stride(), data(), stride());
    return *this;
}

template <typename T>
Array<T> &
Array<T>::operator=(const ConstArrayView<T> &rhs)
{
    if (length()!=rhs.length()) {
        resize(rhs.length(), rhs.firstIndex());
    }
    copy(length(), rhs.data(), rhs.stride(), data(), stride());
    return *this;
}

template <typename T>
ListInitializer<Array<T> >
Array<T>::operator=(const T &value)
{
    return ListInitializer<Array<T> >(data(), length(), value);
}

template <typename T>
const T &
Array<T>::operator()(int index) const
{
    assert(index>=_firstIndex);
    assert(index<_firstIndex+_length);

    return _data[index];
}

template <typename T>
T &
Array<T>::operator()(int index)
{
    assert(index>=_firstIndex);
    assert(index<_firstIndex+_length);

    return _data[index];
}

template <typename T>
int
Array<T>::firstIndex() const
{
    return _firstIndex;
}

template <typename T>
int
Array<T>::lastIndex() const
{
    return _firstIndex+_length-1;
}

template <typename T>
int
Array<T>::length() const
{
    return _length;
}

template <typename T>
int
Array<T>::stride() const
{
    return 1;
}

template <typename T>
const T *
Array<T>::data() const
{
    return &_data[_firstIndex];
}

template <typename T>
T *
Array<T>::data()
{
    return &_data[_firstIndex];
}

template <typename T>
void
Array<T>::resize(int length, int firstIndex)
{
    if (length!=_length) {
        _release();
        _length = length;
        _firstIndex = firstIndex;
        _allocate();
    } else {
        _data += _firstIndex - firstIndex;
        _firstIndex = firstIndex;
    }
}

template <typename T>
void
Array<T>::resizeOrClear(int length, int firstIndex)
{
    if (length!=_length) {
        _release();
        _length = length;
        _firstIndex = firstIndex;
        _allocate();
    } else {
        _data += _firstIndex - firstIndex;
        _firstIndex = firstIndex;
        std::fill_n(this->data(), _length, T(0));
    }
}

template <typename T>
ConstArrayView<T>
Array<T>::view(int from, int to, int stride, int firstViewIndex) const
{
    assert(from<=to);
    assert(stride>=1);
    return ConstView(data(),                                    // storage
                     &(this->operator()(from))-firstViewIndex,  // data
                     (to-from)/stride+1,                        // length
                     stride,                                    // stride
                     firstViewIndex);                           // firstIndex
}

template <typename T>
ArrayView<T>
Array<T>::view(int from, int to, int stride, int firstViewIndex)
{
    assert(from<=to);
    assert(stride>=1);
    return View(data(),                                   // storage
                &(this->operator()(from))-firstViewIndex, // data
                (to-from)/stride+1,                       // length
                stride,                                   // stride
                firstViewIndex);                          // firstIndex
}

template <typename T>
void
Array<T>::shiftIndexTo(int firstIndex)
{
    _data += _firstIndex - firstIndex;
    _firstIndex = firstIndex;
}

template <typename T>
void
Array<T>::_allocate()
{
    assert(!_data);
    // TODO: discuss with Michael.
    if (length()<=0) {
        return;
    }

    _data = static_cast<T*>(calloc(_length, sizeof(T)))-_firstIndex;
    Initializer<Array<T> >::initialize(*this);
    assert(_data+_firstIndex);
}

template <typename T>
void
Array<T>::_release()
{
    if (_data) {
         if (RefCounter::detach(data())) {
#ifdef GMP
             T *fp;
             int i;
             for (i=0, fp=data(); i<_length; ++i, ++fp) {
                 (*fp).~T();
             }
#endif
             free(data());
         }
         _data=0;
         _firstIndex = 0;
    }
}

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

#include <cassert>
#include <flens/blas.h>
#include <flens/refcounter.h>
#include <flens/hacksforgmpxx.h>

namespace flens {

//== ConstArrayView ============================================================

template <typename T>
ConstArrayView<T>::ConstArrayView(const void *storage,
                                  const T *data,
                                  int length,
                                  int stride,
                                  int firstIndex)
    : _storage(storage),
      _data(data),
      _length(length),
      _stride(stride),
      _firstIndex(firstIndex)
{
    RefCounter::attach(_storage);
}

template <typename T>
ConstArrayView<T>::ConstArrayView(const NoView &rhs)
    : _storage(rhs.data()),
      _data(rhs.data()-rhs.firstIndex()),
      _length(rhs.length()),
      _stride(rhs.stride()),
      _firstIndex(rhs.firstIndex())
{
    RefCounter::attach(_storage);
}

template <typename T>
ConstArrayView<T>::ConstArrayView(const ConstArrayView &rhs)
    : _storage(rhs._storage),
      _data(rhs._data),
      _length(rhs._length),
      _stride(rhs._stride),
      _firstIndex(rhs._firstIndex)
{
    RefCounter::attach(_storage);
}

template <typename T>
ConstArrayView<T>::ConstArrayView(const ArrayView<T> &rhs)
    : _storage(rhs.storage()),
      _data(rhs.data()-rhs.firstIndex()),
      _length(rhs.length()),
      _stride(rhs.stride()),
      _firstIndex(rhs.firstIndex())
{
    RefCounter::attach(_storage);
}

template <typename T>
ConstArrayView<T>::~ConstArrayView()
{
    if (RefCounter::detach(_storage)) {
        assert(0);  // Const view can not free memory
    }
}

template <typename T>
const T &
ConstArrayView<T>::operator()(int index) const
{
    assert(index>=_firstIndex);
    assert(index<_firstIndex+_length);

    return _data[_firstIndex + _stride*(index-_firstIndex)];
}

template <typename T>
int
ConstArrayView<T>::firstIndex() const
{
    return _firstIndex;
}

template <typename T>
int
ConstArrayView<T>::lastIndex() const
{
    return _firstIndex+_length-1;
}

template <typename T>
int
ConstArrayView<T>::length() const
{
    return _length;
}

template <typename T>
int
ConstArrayView<T>::stride() const
{
    return _stride;
}

template <typename T>
const T *
ConstArrayView<T>::data() const
{
    return &_data[_firstIndex];
}

template <typename T>
const void *
ConstArrayView<T>::storage() const
{
    return _storage;
}

template <typename T>
ConstArrayView<T>
ConstArrayView<T>::view(int from, int to, int stride, int firstViewIndex) const
{
    assert(from<=to);
    assert(stride>=1);
    return ConstArrayView<T>(_storage,                             // storage
                             &(this->operator()(from))-_firstIndex,// data
                             (to-from)/stride+1,                   // length
                             stride*_stride,                       // stride
                             firstViewIndex);                     // firstIndex
}

//== ArrayView =================================================================

template <typename T>
ArrayView<T>::ArrayView(void *storage,
                        T *data,
                        int length,
                        int stride,
                        int firstIndex)
    : _storage(storage),
      _data(data),
      _length(length),
      _stride(stride),
      _firstIndex(firstIndex)
{
    RefCounter::attach(_storage);
}

template <typename T>
ArrayView<T>::ArrayView(const ArrayView &rhs)
    : _storage(rhs._storage),
      _data(rhs._data),
      _length(rhs._length),
      _stride(rhs._stride),
      _firstIndex(rhs._firstIndex)
{
    RefCounter::attach(_storage);
}

template <typename T>
ArrayView<T>::~ArrayView()
{
    if (RefCounter::detach(_storage)) {
        free(_storage);
    }
}

template <typename T>
ArrayView<T> &
ArrayView<T>::operator=(const Array<T> &rhs)
{
    assert(length()==rhs.length());
    copy(length(), rhs.data(), rhs.stride(), data(), stride());
    return *this;
}

template <typename T>
ArrayView<T> &
ArrayView<T>::operator=(const ArrayView<T> &rhs)
{
    if (this!=&rhs) {
        assert(length()==rhs.length());
        copy(length(), rhs.data(), rhs.stride(), data(), stride());
    }
    return *this;
}

template <typename T>
ArrayView<T> &
ArrayView<T>::operator=(const ConstArrayView<T> &rhs)
{
    assert(length()==rhs.length());
    copy(length(), rhs.data(), rhs.stride(), data(), stride());
    return *this;
}

template <typename T>
const T &
ArrayView<T>::operator()(int index) const
{
    assert(index>=_firstIndex);
    assert(index<_firstIndex+_length);

    return _data[_firstIndex + _stride*(index-_firstIndex)];
}

template <typename T>
T &
ArrayView<T>::operator()(int index)
{
    assert(index>=_firstIndex);
    assert(index<_firstIndex+_length);

    return _data[_firstIndex + _stride*(index-_firstIndex)];
}

template <typename T>
int
ArrayView<T>::firstIndex() const
{
    return _firstIndex;
}

template <typename T>
int
ArrayView<T>::lastIndex() const
{
    return _firstIndex+_length-1;
}

template <typename T>
int
ArrayView<T>::length() const
{
    return _length;
}

template <typename T>
int
ArrayView<T>::stride() const
{
    return _stride;
}

template <typename T>
const T *
ArrayView<T>::data() const
{
    return &_data[_firstIndex];
}

template <typename T>
T *
ArrayView<T>::data()
{
    return &_data[_firstIndex];
}

template <typename T>
const void *
ArrayView<T>::storage() const
{
    return _storage;
}

template <typename T>
void
ArrayView<T>::resize(int length, int firstIndex)
{
    assert((length == _length) && (firstIndex == _firstIndex));
    (void) length;
    (void) firstIndex;
}

template <typename T>
void
ArrayView<T>::resizeOrClear(int length, int firstIndex)
{
    assert((length==_length) && (firstIndex==_firstIndex));
    std::fill_n(this->data(), _length, T(0));
}

template <typename T>
ConstArrayView<T>
ArrayView<T>::view(int from, int to, int stride, int firstViewIndex) const
{
    assert(from<=to);
    assert(stride>=1);
    return ConstArrayView<T>(_storage,                             // storage
                             &(this->operator()(from))-_firstIndex,// data
                             (to-from)/stride+1,                   // length
                             stride*_stride,                       // stride
                             firstViewIndex);
}

template <typename T>
ArrayView<T>
ArrayView<T>::view(int from, int to, int stride, int firstViewIndex)
{
    assert(from<=to);
    assert(stride>=1);
    return ArrayView<T>(_storage,                              // storage
                        &(this->operator()(from))-_firstIndex, // data
                        (to-from)/stride+1,                    // length
                        stride*_stride,                        // stride
                        firstViewIndex);
}

template <typename T>
void
ArrayView<T>::shiftIndexTo(int firstIndex)
{
    assert(_firstIndex==firstIndex);
}

//== Array =====================================================================

template <typename T>
Array<T>::Array()
    : _length(0), _firstIndex(0), _data(0)
{
}

template <typename T>
Array<T>::Array(int length, int firstIndex)
    : _length(length), _firstIndex(firstIndex), _data(0)
{
    _allocate();
}

template <typename T>
Array<T>::Array(const Array<T> &rhs)
    : _length(rhs.length()), _firstIndex(rhs.firstIndex()), _data(0)
{
    _allocate();
    copy(length(), rhs.data(), rhs.stride(), data(), stride());
}

template <typename T>
Array<T>::Array(const ArrayView<T> &rhs)
    : _length(rhs.length()), _firstIndex(rhs.firstIndex()), _data(0)
{
    _allocate();
    copy(length(), rhs.data(), rhs.stride(), data(), stride());
}

template <typename T>
Array<T>::Array(const ConstArrayView<T> &rhs)
    : _length(rhs.length()), _firstIndex(rhs.firstIndex()), _data(0)
{
    _allocate();
    copy(length(), rhs.data(), rhs.stride(), data(), stride());
}

template <typename T>
Array<T>::~Array()
{
    _release();
}

template <typename T>
Array<T> &
Array<T>::operator=(const Array<T> &rhs)
{
    if (this!=&rhs) {
        if (length()!=rhs.length()) {
            resize(rhs.length(), rhs.firstIndex());
        }
        copy(length(), rhs.data(), rhs.stride(), data(), stride());
    }
    return *this;
}

template <typename T>
Array<T> &
Array<T>::operator=(const ArrayView<T> &rhs)
{
    if (length()!=rhs.length()) {
        resize(rhs.length(), rhs.firstIndex());
    }
    copy(length(), rhs.data(), rhs.stride(), data(), stride());
    return *this;
}

template <typename T>
Array<T> &
Array<T>::operator=(const ConstArrayView<T> &rhs)
{
    if (length()!=rhs.length()) {
        resize(rhs.length(), rhs.firstIndex());
    }
    copy(length(), rhs.data(), rhs.stride(), data(), stride());
    return *this;
}

template <typename T>
ListInitializer<Array<T> >
Array<T>::operator=(const T &value)
{
    return ListInitializer<Array<T> >(data(), length(), value);
}

template <typename T>
const T &
Array<T>::operator()(int index) const
{
    assert(index>=_firstIndex);
    assert(index<_firstIndex+_length);

    return _data[index];
}

template <typename T>
T &
Array<T>::operator()(int index)
{
    assert(index>=_firstIndex);
    assert(index<_firstIndex+_length);

    return _data[index];
}

template <typename T>
int
Array<T>::firstIndex() const
{
    return _firstIndex;
}

template <typename T>
int
Array<T>::lastIndex() const
{
    return _firstIndex+_length-1;
}

template <typename T>
int
Array<T>::length() const
{
    return _length;
}

template <typename T>
int
Array<T>::stride() const
{
    return 1;
}

template <typename T>
const T *
Array<T>::data() const
{
    return &_data[_firstIndex];
}

template <typename T>
T *
Array<T>::data()
{
    return &_data[_firstIndex];
}

template <typename T>
void
Array<T>::resize(int length, int firstIndex)
{
    if (length!=_length) {
        _release();
        _length = length;
        _firstIndex = firstIndex;
        _allocate();
    } else {
        _data += _firstIndex - firstIndex;
        _firstIndex = firstIndex;
    }
}

template <typename T>
void
Array<T>::resizeOrClear(int length, int firstIndex)
{
    if (length!=_length) {
        _release();
        _length = length;
        _firstIndex = firstIndex;
        _allocate();
    } else {
        _data += _firstIndex - firstIndex;
        _firstIndex = firstIndex;
        std::fill_n(this->data(), _length, T(0));
    }
}

template <typename T>
ConstArrayView<T>
Array<T>::view(int from, int to, int stride, int firstViewIndex) const
{
    assert(from<=to);
    assert(stride>=1);
    return ConstView(data(),                                    // storage
                     &(this->operator()(from))-firstViewIndex,  // data
                     (to-from)/stride+1,                        // length
                     stride,                                    // stride
                     firstViewIndex);                           // firstIndex
}

template <typename T>
ArrayView<T>
Array<T>::view(int from, int to, int stride, int firstViewIndex)
{
    assert(from<=to);
    assert(stride>=1);
    return View(data(),                                   // storage
                &(this->operator()(from))-firstViewIndex, // data
                (to-from)/stride+1,                       // length
                stride,                                   // stride
                firstViewIndex);                          // firstIndex
}

template <typename T>
void
Array<T>::shiftIndexTo(int firstIndex)
{
    _data += _firstIndex - firstIndex;
    _firstIndex = firstIndex;
}

template <typename T>
void
Array<T>::_allocate()
{
    assert(!_data);
    // TODO: discuss with Michael.
    if (length()<=0) {
        return;
    }

    _data = static_cast<T*>(calloc(_length, sizeof(T)))-_firstIndex;
    Initializer<Array<T> >::initialize(*this);
    assert(_data+_firstIndex);
}

template <typename T>
void
Array<T>::_release()
{
    if (_data) {
         if (RefCounter::detach(data())) {
#ifdef GMP
             T *fp;
             int i;
             for (i=0, fp=data(); i<_length; ++i, ++fp) {
                 (*fp).~T();
             }
#endif
             free(data());
         }
         _data=0;
         _firstIndex = 0;
    }
}

} // namespace flens
<|endoftext|>


<commit_before>#include <typeinfo>
#include "../ir.h"

TLANG_NAMESPACE_BEGIN

class MakeAdjoint : public IRVisitor {
 public:
  Block *current_block;

  MakeAdjoint() {
    current_block = nullptr;
  }

  static void run(IRNode *node) {
    auto p = MakeAdjoint();
    node->accept(&p);
  }

  void visit(Block *block) override {
    std::vector<Stmt *> statements;
    // always make a copy since the list can be modified.
    for (auto &stmt : block->statements) {
      statements.push_back(stmt.get());
    }
    std::reverse(statements.begin(), statements.end());  // reverse-mode AD...
    for (auto stmt : statements) {
      current_block = block;
      stmt->accept(this);
    }
  }

  Stmt *insert_back(std::unique_ptr<Stmt> &&stmt) {
    auto ptr = stmt.get();
    current_block->insert(std::move(stmt), -1);
    return ptr;
  }

  void accumulate(Stmt *primal, Stmt *value) {
    auto alloca_ = alloc(primal);
    TC_ASSERT(alloca_->is<AllocaStmt>());
    auto alloca = alloca_->as<AllocaStmt>();
    TC_ASSERT(alloca->width() == 1);
    auto local_load =
        insert_back(Stmt::make<LocalLoadStmt>(LocalAddress(alloca, 0)));
    if (value->is<AllocaStmt>()) {
      value = insert_back(
          Stmt::make<LocalLoadStmt>(LocalAddress(value->as<AllocaStmt>(), 0)));
    }
    auto add = insert_back(
        Stmt::make<BinaryOpStmt>(BinaryOpType::add, local_load, value));
    insert_back(Stmt::make<LocalStoreStmt>(alloca, add));
  }

  Stmt *alloc(Stmt *stmt) {
    if (stmt->adjoint == nullptr) {
      // create the alloca
      auto alloca = Stmt::make<AllocaStmt>(1, DataType::unknown);
      stmt->adjoint = alloca.get();
      alloca->ret_type = stmt->ret_type;
      current_block->insert(std::move(alloca), 0);
    }
    return stmt->adjoint;
  }

  void visit(AllocaStmt *alloca) override {
    // do nothing.
  }

  void visit(UnaryOpStmt *stmt) override {
  }

  void visit(BinaryOpStmt *bin) override {
    if (bin->op_type == BinaryOpType::add) {
      accumulate(bin->lhs, alloc(bin));
      accumulate(bin->rhs, alloc(bin));
    } else if (bin->op_type == BinaryOpType::sub) {
      accumulate(bin->lhs, alloc(bin));
      auto rgrad = Stmt::make<UnaryOpStmt>(UnaryOpType::neg, alloc(bin));
      auto rptr = rgrad.get();
      insert_back(std::move(rgrad));
      accumulate(bin->rhs, rptr);
    } else if (bin->op_type == BinaryOpType::mul) {
      auto lptr = insert_back(
          Stmt::make<BinaryOpStmt>(BinaryOpType::mul, alloc(bin), bin->rhs));
      auto rptr = insert_back(
          Stmt::make<BinaryOpStmt>(BinaryOpType::mul, alloc(bin), bin->lhs));
      accumulate(bin->lhs, lptr);
      accumulate(bin->rhs, rptr);
    } else {
      TC_WARN("", binary_op_type_name(bin->op_type));
      TC_NOT_IMPLEMENTED
    }
  }

  void visit(TernaryOpStmt *stmt) override {
    TC_ASSERT(stmt->op_type == TernaryOpType::select);
  }

  void visit(IfStmt *if_stmt) override {
    TC_NOT_IMPLEMENTED
  }

  void visit(PrintStmt *print_stmt) override {
    TC_NOT_IMPLEMENTED
  }

  void visit(ConstStmt *const_stmt) override {
    // do nothing
  }

  void visit(WhileControlStmt *stmt) override {
    TC_NOT_IMPLEMENTED
  }

  void visit(WhileStmt *stmt) override {
    TC_NOT_IMPLEMENTED
  }

  void visit(RangeForStmt *for_stmt) override {
    TC_NOT_IMPLEMENTED
  }

  void visit(StructForStmt *for_stmt) override {
    for_stmt->body->accept(this);
  }

  void visit(GlobalPtrStmt *stmt) override {
    // do nothing
  }

  void visit(LocalLoadStmt *stmt) override {
    // do nothing
    TC_WARN("needs impl when loading something other loop var");
  }

  void visit(LocalStoreStmt *stmt) override {
    TC_NOT_IMPLEMENTED
  }

  void visit(GlobalLoadStmt *stmt) override {
    // issue global store to adjoint
    GlobalPtrStmt *ptr = stmt->ptr->as<GlobalPtrStmt>();
    TC_ASSERT(ptr->width() == 1);
    auto snodes = ptr->snodes;
    TC_ASSERT(snodes[0]->grad != nullptr);
    snodes[0] = snodes[0]->grad;
    auto adjoint_ptr =
        insert_back(Stmt::make<GlobalPtrStmt>(snodes, ptr->indices));
    auto adjoint_store =
        insert_back(Stmt::make<GlobalStoreStmt>(adjoint_ptr, alloc(stmt)));
  }

  void visit(GlobalStoreStmt *stmt) override {
    // erase and replace with global load adjoint
    GlobalPtrStmt *ptr = stmt->ptr->as<GlobalPtrStmt>();
    TC_ASSERT(ptr->width() == 1);
    auto snodes = ptr->snodes;
    TC_ASSERT(snodes[0]->grad != nullptr);
    snodes[0] = snodes[0]->grad;
    auto adjoint_ptr =
        insert_back(Stmt::make<GlobalPtrStmt>(snodes, ptr->indices));
    auto adjoint_load = insert_back(Stmt::make<GlobalLoadStmt>(adjoint_ptr));
    accumulate(stmt->data, adjoint_load);
    stmt->parent->erase(stmt);
  }

  void visit(ElementShuffleStmt *stmt) override {
    TC_NOT_IMPLEMENTED
  }

  void visit(RangeAssumptionStmt *stmt) override {
    // do nothing
  }
};

namespace irpass {

void make_adjoint(IRNode *root) {
  MakeAdjoint::run(root);
  print(root);
  typecheck(root);
}

}  // namespace irpass

TLANG_NAMESPACE_END
<commit_msg>simplified make_adjoint<commit_after>#include <typeinfo>
#include "../ir.h"

TLANG_NAMESPACE_BEGIN

class MakeAdjoint : public IRVisitor {
 public:
  Block *current_block;

  MakeAdjoint() {
    current_block = nullptr;
  }

  static void run(IRNode *node) {
    auto p = MakeAdjoint();
    node->accept(&p);
  }

  void visit(Block *block) override {
    std::vector<Stmt *> statements;
    // always make a copy since the list can be modified.
    for (auto &stmt : block->statements) {
      statements.push_back(stmt.get());
    }
    std::reverse(statements.begin(), statements.end());  // reverse-mode AD...
    for (auto stmt : statements) {
      current_block = block;
      stmt->accept(this);
    }
  }

  Stmt *insert_back(std::unique_ptr<Stmt> &&stmt) {
    auto ptr = stmt.get();
    current_block->insert(std::move(stmt), -1);
    return ptr;
  }

  template <typename T, typename... Args>
  Stmt *insert(Args &&... args) {
    return insert_back(Stmt::make<T>(args...));
  }

  void accumulate(Stmt *primal, Stmt *value) {
    auto alloca_ = alloc(primal);
    TC_ASSERT(alloca_->is<AllocaStmt>());
    auto alloca = alloca_->as<AllocaStmt>();
    TC_ASSERT(alloca->width() == 1);
    auto local_load = insert<LocalLoadStmt>(LocalAddress(alloca, 0));
    if (value->is<AllocaStmt>()) {
      value = insert<LocalLoadStmt>(LocalAddress(value->as<AllocaStmt>(), 0));
    }
    auto add = insert<BinaryOpStmt>(BinaryOpType::add, local_load, value);
    insert<LocalStoreStmt>(alloca, add);
  }

  Stmt *alloc(Stmt *stmt) {
    if (stmt->adjoint == nullptr) {
      // create the alloca
      auto alloca = Stmt::make<AllocaStmt>(1, DataType::unknown);
      stmt->adjoint = alloca.get();
      alloca->ret_type = stmt->ret_type;
      current_block->insert(std::move(alloca), 0);
    }
    return stmt->adjoint;
  }

  void visit(AllocaStmt *alloca) override {
    // do nothing.
  }

  void visit(UnaryOpStmt *stmt) override {
  }

  void visit(BinaryOpStmt *bin) override {
    if (bin->op_type == BinaryOpType::add) {
      accumulate(bin->lhs, alloc(bin));
      accumulate(bin->rhs, alloc(bin));
    } else if (bin->op_type == BinaryOpType::sub) {
      accumulate(bin->lhs, alloc(bin));
      auto rgrad = Stmt::make<UnaryOpStmt>(UnaryOpType::neg, alloc(bin));
      auto rptr = rgrad.get();
      insert_back(std::move(rgrad));
      accumulate(bin->rhs, rptr);
    } else if (bin->op_type == BinaryOpType::mul) {
      auto lptr = insert<BinaryOpStmt>(BinaryOpType::mul, alloc(bin), bin->rhs);
      auto rptr = insert<BinaryOpStmt>(BinaryOpType::mul, alloc(bin), bin->lhs);
      accumulate(bin->lhs, lptr);
      accumulate(bin->rhs, rptr);
    } else {
      TC_WARN("", binary_op_type_name(bin->op_type));
      TC_NOT_IMPLEMENTED
    }
  }

  void visit(TernaryOpStmt *stmt) override {
    TC_ASSERT(stmt->op_type == TernaryOpType::select);
  }

  void visit(IfStmt *if_stmt) override {
    TC_NOT_IMPLEMENTED
  }

  void visit(PrintStmt *print_stmt) override {
    TC_NOT_IMPLEMENTED
  }

  void visit(ConstStmt *const_stmt) override {
    // do nothing
  }

  void visit(WhileControlStmt *stmt) override {
    TC_NOT_IMPLEMENTED
  }

  void visit(WhileStmt *stmt) override {
    TC_NOT_IMPLEMENTED
  }

  void visit(RangeForStmt *for_stmt) override {
    TC_NOT_IMPLEMENTED
  }

  void visit(StructForStmt *for_stmt) override {
    for_stmt->body->accept(this);
  }

  void visit(GlobalPtrStmt *stmt) override {
    // do nothing
  }

  void visit(LocalLoadStmt *stmt) override {
    // do nothing
    TC_WARN("needs impl when loading something other loop var");
  }

  void visit(LocalStoreStmt *stmt) override {
    TC_NOT_IMPLEMENTED
  }

  void visit(GlobalLoadStmt *stmt) override {
    // issue global store to adjoint
    GlobalPtrStmt *ptr = stmt->ptr->as<GlobalPtrStmt>();
    TC_ASSERT(ptr->width() == 1);
    auto snodes = ptr->snodes;
    TC_ASSERT(snodes[0]->grad != nullptr);
    snodes[0] = snodes[0]->grad;
    auto adjoint = insert<GlobalPtrStmt>(snodes, ptr->indices);
    insert<GlobalStoreStmt>(adjoint, alloc(stmt));
  }

  void visit(GlobalStoreStmt *stmt) override {
    // erase and replace with global load adjoint
    GlobalPtrStmt *ptr = stmt->ptr->as<GlobalPtrStmt>();
    TC_ASSERT(ptr->width() == 1);
    auto snodes = ptr->snodes;
    TC_ASSERT(snodes[0]->grad != nullptr);
    snodes[0] = snodes[0]->grad;
    auto adjoint_ptr = insert<GlobalPtrStmt>(snodes, ptr->indices);
    accumulate(stmt->data, insert<GlobalLoadStmt>(adjoint_ptr));
    stmt->parent->erase(stmt);
  }

  void visit(ElementShuffleStmt *stmt) override {
    TC_NOT_IMPLEMENTED
  }

  void visit(RangeAssumptionStmt *stmt) override {
    // do nothing
  }
};

namespace irpass {

void make_adjoint(IRNode *root) {
  MakeAdjoint::run(root);
  print(root);
  typecheck(root);
}

}  // namespace irpass

TLANG_NAMESPACE_END
<|endoftext|>


<commit_before>#include <chrono>
#include <ctime>
#include <fstream>
#include <iostream>
#include <map>
#include <queue>
#include <ratio>
#include <string>
#include <stdlib.h>
#include <time.h>
//#include "omp.h"
#include <vector>

#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/classification.hpp>
#include "boost/lexical_cast.hpp"

using namespace boost::algorithm;

#include "pass_runner.h"

#define NUM_THREADS 8 // default number of threads
#define MAX_NUM_PER_THREAD 100
#define RERUN_COUNT 10
#define MAX_PASS_LENGTH 30

#if(0)
#ifdef DEBUG
#ifndef TIME
#define TIME
#endif
#endif
#endif

using namespace std;
using namespace std::chrono;

/*------- GLOBAL VARIABES --------*/
// vector with permutations of passorder
vector<string> passOrder;
char *binaryName;
char *binaryArgs;
char *input_combination_file;
string dir_name;

#define _SYSTEM_CALL( CMD ) ( {            \
    int err = ( system( CMD.c_str() ) );   \
    if ( err < 0  ) {                      \
        fprintf(stderr, #CMD, " failed."); \
        abort();                           \
    }                                      \
    err;                                   \
})                                         \

class BoundMap {
  public:
    BoundMap() { };

    bool in_range(){
      if(pq.size() < MAX_NUM_PER_THREAD) return true;
      return false;
    }

    void insert(string s, double d){
      if(in_range()){
        pq.push(RECORD(s,d));

      } else if(pq.top().exec_time > d) {
        pq.pop();
        pq.push(RECORD(s,d));
      }

#ifdef BMAP_DEBUG
      std::cout << "Inserting new element with time "<< d << ". Size " << pq.size() << " Worst time " << pq.top().exec_time << " Sequence: " << pq.top().pass_order << std::endl;
#endif

    } /* end of function insert */

    void insert(RECORD r){
      if(in_range()){
        pq.push(r);

      } else if(pq.top().exec_time > r.exec_time) {
        pq.pop();
        pq.push(r);
      }
    }

    PQUEUE pq;
};

inline COMMAND::COMMAND(string pass_order, long double threadId) {
  // opt -pass1 -pass2 ./bcfile -o temp.out.threadId >> /dev/null

  // combination_file_name/temp.out.input_combination_filename
  //string temp_name = dir_name + string("/temp.out.") + input_combination_file;
  std::cout << "Running command " << pass_order << std::endl;
  //opt = string("opt ") + pass_order + " ./"  + binaryName + " -o "+ temp_name  + "" + to_string(threadId) + " >> /dev/null";
  // Command to execute the file.
  //
  //    lli = string("lli ") + temp_name + to_string(threadId) + " " + binaryArgs + to_string(threadId);
  // lli = string("lli ") + temp_name + to_string(threadId) + " " + binaryArgs + to_string(threadId);
}

void cleanUpTheMess() {
  string rm_command = string("rm -rf ") + dir_name;

#ifdef DEBUG
  cout << "Cleaning temporary files.." << endl;
  cout << "Running Command: " << rm_command << endl;
#endif
  _SYSTEM_CALL ( rm_command) ;
}

vector<string>& getOptimizationPasses() {
  vector<string> *optimizationPasses = new vector<string>();
  for(std::map<int, std::string>::iterator it = A::PASS_MAP.begin(), ie = A::PASS_MAP.end(); ie != it; ++it) {
    optimizationPasses->push_back(it->second);
  }
  return *optimizationPasses;
}

void runOptimizationPasses() {
  //map<string, double> optExecMap;

  std::map<string, long> after_pass_time;

  map<long double, BoundMap> boundMaps;
  vector<string> &optimizationPasses = getOptimizationPasses();

  clock_t t1,t2;
  float diff;
  long double tid;
  string pass_name;
  int pass_length = 0;
  while(pass_length < MAX_PASS_LENGTH) {
#pragma omp parallel for private(pass_name)
    for( int i = 0; i < optimizationPasses.size() ; ++i ) {
      pass_name = optimizationPasses[i];
      std::cout << pass_name << std::endl;
      for(int j=0; j < RERUN_COUNT; j++) {
        // START TIMER HERE
        COMMAND Cmd(pass_name, tid);
        // STOP TIMER HERE
      }
#ifdef DEBUG
      cout << "Running Command: " << Cmd.opt << "\n" << Cmd.lli  << " in thread number " << tid << endl;
#endif

    }
    pass_length++;
  }

  cleanUpTheMess();
}

int main(int argc, char** argv) {

  int numThreads = NUM_THREADS;

  /*
     if(argc > 1) {
     binaryName = argv[1];
     input_combination_file = argv[2];
     binaryArgs = argv[3];
     numThreads = atoi(argv[4]);
  //        std::cout << binaryName << " " << binaryArgs << " " << numThreads << " " <<input_combination_file << endl;
  } else {
  std::cout << "Usage: pass_runner <Binary Name> <NumThreads(optional)>" << std::endl;
  abort();
  }

  // open a file in read mode.
  ifstream infile;
  infile.open(input_combination_file);

  string opt_order;
  while(getline(infile, opt_order)) {
  passOrder.push_back(opt_order);
  }
  */

  // this will potentially print the contents of events.1 file
#ifdef VERBOSE
  cout << passOrder;
#endif

  // set openmp number of threads
  //omp_set_num_threads(numThreads);

  //dir_name = string(input_combination_file) + "dir";
  //string create_dir_cmd = string("mkdir ") + dir_name;
  //_SYSTEM_CALL(create_dir_cmd );

  runOptimizationPasses();

  return EXIT_SUCCESS;
}
<commit_msg>markov opt<commit_after>#include <chrono>
#include <ctime>
#include <fstream>
#include <iostream>
#include <map>
#include <queue>
#include <ratio>
#include <string>
#include <stdlib.h>
#include <time.h>
//#include "omp.h"
#include <vector>

#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/classification.hpp>
#include "boost/lexical_cast.hpp"

using namespace boost::algorithm;

#include "pass_runner.h"

#define NUM_THREADS 8 // default number of threads
#define MAX_NUM_PER_THREAD 100
#define RERUN_COUNT 10
#define MAX_PASS_LENGTH 30

#if(0)
#ifdef DEBUG
#ifndef TIME
#define TIME
#endif
#endif
#endif

using namespace std;
using namespace std::chrono;

/*------- GLOBAL VARIABES --------*/
// vector with permutations of passorder
vector<string> passOrder;
char *binaryName;
char *binaryArgs;
char *input_combination_file;
string dir_name;

#define _SYSTEM_CALL( CMD ) ( {            \
    int err = ( system( CMD.c_str() ) );   \
    if ( err < 0  ) {                      \
        fprintf(stderr, #CMD, " failed."); \
        abort();                           \
    }                                      \
    err;                                   \
})                                         \

inline COMMAND::COMMAND(string pass_order, long double threadId) {
  // opt -pass1 -pass2 ./bcfile -o temp.out.threadId >> /dev/null

  // combination_file_name/temp.out.input_combination_filename
  string temp_name = dir_name + string("/temp.out."); //+ input_combination_file;
  std::cout << "Running command " << pass_order << std::endl;

  opt = string("opt ") + pass_order + " ./"  + binaryName + " -o "+ temp_name  + "" + to_string(threadId) + " >> /dev/null";
  // Command to execute the file.
  lli = string("lli ") + temp_name + to_string(threadId) + " " + binaryArgs + to_string(threadId);
}

struct passTime
{
  string pass_name;
  long time;
};

class PassTimeComparator
{
  public:
    bool operator()(passTime& n1, passTime& n2)
    {
      if (n1.time > n2.time)
        return true;
      else
        return false;
    }
};

void cleanUpTheMess() {
  string rm_command = string("rm -rf ") + dir_name;

#ifdef DEBUG
  cout << "Cleaning temporary files.." << endl;
  cout << "Running Command: " << rm_command << endl;
#endif
  _SYSTEM_CALL ( rm_command) ;
}

vector<string>& getOptimizationPasses() {
  vector<string> *optimizationPasses = new vector<string>();
  for(std::map<int, std::string>::iterator it = A::PASS_MAP.begin(), ie = A::PASS_MAP.end(); ie != it; ++it) {
    optimizationPasses->push_back(it->second);
  }
  return *optimizationPasses;
}

void runOptimizationPasses() {
  //map<string, double> optExecMap;
  std::map<string, long> after_pass_time;

  vector<string> &optimizationPasses = getOptimizationPasses();

  clock_t t1,t2;
  float diff;
  long double tid;
  string pass_name;
  int pass_length = 0;
  while(pass_length < MAX_PASS_LENGTH) {
    string curr_pass_order = string("");
    priority_queue< passTime, vector< passTime >, PassTimeComparator> curr_pq;
#pragma omp parallel for private(pass_name)
    for( int i = 0; i < optimizationPasses.size() ; ++i ) {
      pass_name = optimizationPasses[i];
      string temp_pass_order = curr_pass_order + string(" ") + pass_name;
      std::cout << pass_name << std::endl;

      // apply pass here
      COMMAND Cmd(pass_name, tid);
      _SYSTEM_CALL(Cmd.opt);

      // run RERUN_COUNT number of times to get the time
        // START TIMER HERE
      for(int j=0; j < RERUN_COUNT; j++) {

      }
      long time_taken_by_current_pass = 1000;
      // STOP TIMER HERE
#ifdef DEBUG
      cout << "Running Command: " << Cmd.opt << "\n" << Cmd.lli  << " in thread number " << tid << endl;
#endif
      // store the time taken by current pass
      passTime curr = {pass_name, time_taken_by_current_pass};
      curr_pq.push(curr);
    }

    curr_pass_order.append(pass_name);
    pass_length++;
  }

  cleanUpTheMess();
}

int main(int argc, char** argv) {

  int numThreads = NUM_THREADS;

  if(argc > 1) {
    binaryName = argv[1];
    binaryArgs = argv[2];
    numThreads = atoi(argv[3]);

    std::cout << binaryName << " " << binaryArgs << " " << numThreads << " " << endl;//<< input_combination_file << endl;
  } else {
    std::cout << "Usage: pass_runner <Binary Name> <NumThreads(optional)>" << std::endl;
    abort();
  }

  // set openmp number of threads
  //omp_set_num_threads(numThreads);

//dir_name = string(input_combination_file) + "dir";
  //string create_dir_cmd = string("mkdir ") + dir_name;
  //_SYSTEM_CALL(create_dir_cmd );

  runOptimizationPasses();

  return EXIT_SUCCESS;
}
<|endoftext|>