text:
/*
 * @CopyRight:
 * FISCO-BCOS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FISCO-BCOS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with FISCO-BCOS.  If not, see <http://www.gnu.org/licenses/>
 * (c) 2016-2018 fisco-dev contributors.
 */
/** @file MemoryTable2.cpp
 *  @author ancelmo
 *  @date 20180921
 */
#include "MemoryTable2.h"
#include "Common.h"
#include "StorageException.h"
#include "Table.h"
#include <arpa/inet.h>
#include <json/json.h>
#include <libconfig/GlobalConfigure.h>
#include <libdevcore/FixedHash.h>
#include <libdevcrypto/CryptoInterface.h>
#include <libprecompiled/Common.h>
#include <tbb/parallel_invoke.h>
#include <tbb/parallel_sort.h>
#include <boost/exception/diagnostic_information.hpp>
#include <boost/lexical_cast.hpp>
#include <algorithm>
#include <csignal>
#include <thread>
#include <vector>

using namespace std;
using namespace dev;
using namespace dev::storage;
using namespace dev::precompiled;

void prepareExit(const std::string& _key)
{
    STORAGE_LOG(ERROR) << LOG_BADGE("MemoryTable2 prepare to exit") << LOG_KV("key", _key);
    raise(SIGTERM);
    while (!g_BCOSConfig.shouldExit.load())
    {  // wait to exit
        std::this_thread::yield();
    }
    BOOST_THROW_EXCEPTION(
        StorageException(-1, string("backend DB is dead. Prepare to exit.") + _key));
}

Entries::ConstPtr MemoryTable2::select(const std::string& key, Condition::Ptr condition)
{
    return selectNoLock(key, condition);
}

void MemoryTable2::proccessLimit(
    const Condition::Ptr& condition, const Entries::Ptr& entries, const Entries::Ptr& resultEntries)
{
    int begin = condition->getOffset();
    int end = 0;

    if (g_BCOSConfig.version() < V2_1_0)
    {
        end = begin + condition->getCount();
    }
    else
    {
        end = (int)std::min((size_t)INT_MAX, (size_t)begin + (size_t)condition->getCount());
    }

    int size = entries->size();
    if (begin >= size)
    {
        return;
    }
    else
    {
        end = end > size ? size : end;
    }
    for (int i = begin; i < end; i++)
    {
        resultEntries->addEntry(entries->get(i));
    }
}

Entries::Ptr MemoryTable2::selectNoLock(const std::string& key, Condition::Ptr condition)
{
    try
    {
        auto entries = std::make_shared<Entries>();
        condition->EQ(m_tableInfo->key, key);
        if (m_remoteDB)
        {
            // query remoteDB anyway
            Entries::Ptr dbEntries = m_remoteDB->select(m_blockNum, m_tableInfo, key, condition);
            if (!dbEntries)
            {
                return entries;
            }
            std::set<uint64_t> processed;
            std::set<uint64_t> diff;
            for (size_t i = 0; i < dbEntries->size(); ++i)
            {
                auto entryIt = m_dirty.find(dbEntries->get(i)->getID());
                if (entryIt != m_dirty.end())
                {
                    processed.insert(entryIt->second->getID());
                    if (g_BCOSConfig.version() >= V2_5_0 && !condition->process(entryIt->second))
                    {
                        continue;
                    }
                    entries->addEntry(entryIt->second);
                }
                else
                {
                    entries->addEntry(dbEntries->get(i));
                }
            }
            if (g_BCOSConfig.version() >= V2_5_0)
            {
                std::set_difference(m_dirty_updated[key].begin(), m_dirty_updated[key].end(),
                    processed.begin(), processed.end(), std::inserter(diff, diff.begin()));
                for (auto id : diff)
                {
                    if (condition->process(m_dirty[id]))
                    {
                        entries->addEntry(m_dirty[id]);
                    }
                }
            }
        }

        auto it = m_newEntries.find(key);
        if (it != m_newEntries.end())
        {
            auto indices = processEntries(it->second, condition);
            for (auto& itIndex : indices)
            {
                it->second->get(itIndex)->setTempIndex(itIndex);
                entries->addEntry(it->second->get(itIndex));
            }
        }
        if (condition->getOffset() >= 0 && condition->getCount() >= 0)
        {
            Entries::Ptr resultEntries = std::make_shared<Entries>();
            proccessLimit(condition, entries, resultEntries);
            return resultEntries;
        }
        return entries;
    }
    catch (std::exception& e)
    {
        STORAGE_LOG(ERROR) << LOG_BADGE("MemoryTable2") << LOG_DESC("Table select failed")
                           << LOG_KV("what", e.what());
        m_remoteDB->stop();
        prepareExit(key);
    }

    return std::make_shared<Entries>();
}

int MemoryTable2::update(
    const std::string& key, Entry::Ptr entry, Condition::Ptr condition, AccessOptions::Ptr options)
{
    try
    {
        if (options->check && !checkAuthority(options->origin))
        {
            STORAGE_LOG(WARNING) << LOG_BADGE("MemoryTable2")
                                 << LOG_DESC("update permission denied")
                                 << LOG_KV("origin", options->origin.hex()) << LOG_KV("key", key);

            return storage::CODE_NO_AUTHORIZED;
        }

        checkField(entry);

        auto entries = selectNoLock(key, condition);
        std::vector<Change::Record> records;

        for (size_t i = 0; i < entries->size(); ++i)
        {
            Entry::Ptr updateEntry = entries->get(i);

            // if id not equals to zero and not in the m_dirty, must be new dirty entry
            if (updateEntry->getID() != 0 && m_dirty.find(updateEntry->getID()) == m_dirty.end())
            {
                m_dirty.insert(std::make_pair(updateEntry->getID(), updateEntry));
                m_dirty_updated[key].insert(updateEntry->getID());
            }

            for (auto& it : *(entry))
            {
                // _id_ always got initialized value 0 from Entry::Entry()
                // no need to update _id_ while updating entry
                if (it.first != ID_FIELD && it.first != m_tableInfo->key)
                {
                    records.emplace_back(updateEntry->getTempIndex(), it.first,
                        updateEntry->getField(it.first), updateEntry->getID());
                    updateEntry->setField(it.first, it.second);
                }
            }
        }

        m_recorder(shared_from_this(), Change::Update, key, records);

        m_hashDirty = true;
        m_dataDirty = true;
        return entries->size();
    }
    catch (std::invalid_argument& e)
    {
        BOOST_THROW_EXCEPTION(e);
    }
    catch (std::exception& e)
    {
        STORAGE_LOG(ERROR) << LOG_BADGE("MemoryTable2")
                           << LOG_DESC("Access MemoryTable2 failed for")
                           << LOG_KV("msg", boost::diagnostic_information(e));
        m_remoteDB->stop();
        prepareExit(key);
    }

    return 0;
}

int MemoryTable2::insert(const std::string& key, Entry::Ptr entry, AccessOptions::Ptr options, bool)
{
    try
    {
        if (options->check && !checkAuthority(options->origin))
        {
            STORAGE_LOG(WARNING) << LOG_BADGE("MemoryTable2")
                                 << LOG_DESC("insert permission denied")
                                 << LOG_KV("origin", options->origin.hex()) << LOG_KV("key", key);
            return storage::CODE_NO_AUTHORIZED;
        }

        checkField(entry);

        entry->setField(m_tableInfo->key, key);
        auto it = m_newEntries.find(key);

        if (it == m_newEntries.end())
        {
            Entries::Ptr entries = std::make_shared<Entries>();
            it = m_newEntries.insert(std::make_pair(key, entries)).first;
        }
        auto iter = it->second->addEntry(entry);

        // auto iter = m_newEntries->addEntry(entry);
        Change::Record record(iter);

        std::vector<Change::Record> value{record};
        m_recorder(shared_from_this(), Change::Insert, key, value);

        m_hashDirty = true;
        m_dataDirty = true;
        return 1;
    }
    catch (std::invalid_argument& e)
    {
        BOOST_THROW_EXCEPTION(e);
    }
    catch (std::exception& e)
    {
        // impossible, so exit
        STORAGE_LOG(FATAL) << LOG_BADGE("MemoryTable2")
                           << LOG_DESC("Access MemoryTable2 failed for")
                           << LOG_KV("msg", boost::diagnostic_information(e));
    }

    return 0;
}

int MemoryTable2::remove(
    const std::string& key, Condition::Ptr condition, AccessOptions::Ptr options)
{
    try
    {
        if (options->check && !checkAuthority(options->origin))
        {
            STORAGE_LOG(WARNING) << LOG_BADGE("MemoryTable2")
                                 << LOG_DESC("remove permission denied")
                                 << LOG_KV("origin", options->origin.hex()) << LOG_KV("key", key);
            return storage::CODE_NO_AUTHORIZED;
        }

        auto entries = selectNoLock(key, condition);

        std::vector<Change::Record> records;
        for (size_t i = 0; i < entries->size(); ++i)
        {
            Entry::Ptr removeEntry = entries->get(i);

            removeEntry->setStatus(1);

            // if id not equals to zero and not in the m_dirty, must be new dirty entry
            if (removeEntry->getID() != 0 && m_dirty.find(removeEntry->getID()) == m_dirty.end())
            {
                m_dirty.insert(std::make_pair(removeEntry->getID(), removeEntry));
            }

            records.emplace_back(removeEntry->getTempIndex(), "", "", removeEntry->getID());
        }

        m_recorder(shared_from_this(), Change::Remove, key, records);

        m_hashDirty = true;
        m_dataDirty = true;
        return entries->size();
    }
    catch (std::exception& e)
    {  // this catch is redundant, because selectNoLock already catch.
        // TODO: make catch simple, remove catch in selectNoLock
        STORAGE_LOG(ERROR) << LOG_BADGE("MemoryTable2")
                           << LOG_DESC("Access MemoryTable2 failed for")
                           << LOG_KV("msg", boost::diagnostic_information(e));
        m_remoteDB->stop();
        prepareExit(key);
    }

    return 0;
}

dev::h256 MemoryTable2::hash()
{
    if (m_hashDirty)
    {
        m_tableData.reset(new dev::storage::TableData());
        if (g_BCOSConfig.version() < V2_2_0)
        {
            dumpWithoutOptimize();
        }
        else
        {
            dump();
        }
    }

    return m_hash;
}

dev::storage::TableData::Ptr MemoryTable2::dumpWithoutOptimize()
{
    TIME_RECORD("MemoryTable2 Dump");
    if (m_hashDirty)
    {
        m_tableData = std::make_shared<dev::storage::TableData>();
        m_tableData->info = m_tableInfo;
        m_tableData->dirtyEntries = std::make_shared<Entries>();

        auto tempEntries = tbb::concurrent_vector<Entry::Ptr>();

        tbb::parallel_for(m_dirty.range(),
            [&](tbb::concurrent_unordered_map<uint64_t, Entry::Ptr>::range_type& range) {
                for (auto it = range.begin(); it != range.end(); ++it)
                {
                    if (!it->second->deleted())
                    {
                        m_tableData->dirtyEntries->addEntry(it->second);
                        tempEntries.push_back(it->second);
                    }
                }
            });

        m_tableData->newEntries = std::make_shared<Entries>();
        tbb::parallel_for(m_newEntries.range(),
            [&](tbb::concurrent_unordered_map<std::string, Entries::Ptr>::range_type& range) {
                for (auto it = range.begin(); it != range.end(); ++it)
                {
                    tbb::parallel_for(tbb::blocked_range<size_t>(0, it->second->size(), 1000),
                        [&](tbb::blocked_range<size_t>& rangeIndex) {
                            for (auto i = rangeIndex.begin(); i < rangeIndex.end(); ++i)
                            {
                                if (!it->second->get(i)->deleted())
                                {
                                    m_tableData->newEntries->addEntry(it->second->get(i));
                                    tempEntries.push_back(it->second->get(i));
                                }
                            }
                        });
                }
            });

        TIME_RECORD("Sort data");
        tbb::parallel_sort(tempEntries.begin(), tempEntries.end(), EntryLessNoLock(m_tableInfo));
        tbb::parallel_sort(m_tableData->dirtyEntries->begin(), m_tableData->dirtyEntries->end(),
            EntryLessNoLock(m_tableInfo));
        tbb::parallel_sort(m_tableData->newEntries->begin(), m_tableData->newEntries->end(),
            EntryLessNoLock(m_tableInfo));
        TIME_RECORD("Submmit data");
        bytes allData;
        for (size_t i = 0; i < tempEntries.size(); ++i)
        {
            auto entry = tempEntries[i];
            if (g_BCOSConfig.version() < RC3_VERSION)
            {  // RC2 STATUS is in entry fields
                entry->setField(STATUS, to_string(entry->getStatus()));
            }
            for (auto fieldIt : *(entry))
            {
                if (isHashField(fieldIt.first))
                {
                    allData.insert(allData.end(), fieldIt.first.begin(), fieldIt.first.end());
                    allData.insert(allData.end(), fieldIt.second.begin(), fieldIt.second.end());
                }
            }
            if (g_BCOSConfig.version() < RC3_VERSION)
            {
                continue;
            }
            char status = (char)entry->getStatus();
            allData.insert(allData.end(), &status, &status + sizeof(status));
        }
        if (allData.empty())
        {
            m_hash = h256();
        }

        bytesConstRef bR(allData.data(), allData.size());

        if (g_BCOSConfig.SMCrypto())
        {
            m_hash = dev::sm3(bR);
        }
        else
        {
            m_hash = dev::sha256(bR);
        }
        m_hashDirty = false;
    }

    return m_tableData;
}

void MemoryTable2::parallelGenData(
    bytes& _generatedData, std::shared_ptr<std::vector<size_t>> _offsetVec, Entries::Ptr _entries)
{
    if (_entries->size() == 0)
    {
        return;
    }
    tbb::parallel_for(tbb::blocked_range<uint64_t>(0, _offsetVec->size() - 1),
        [&](const tbb::blocked_range<uint64_t>& range) {
            for (uint64_t i = range.begin(); i < range.end(); i++)
            {
                auto entry = (*_entries)[i];
                auto startOffSet = (*_offsetVec)[i];

                for (auto& fieldIt : *(entry))
                {
                    if (isHashField(fieldIt.first))
                    {
                        memcpy(
                            &_generatedData[startOffSet], &fieldIt.first[0], fieldIt.first.size());
                        startOffSet += fieldIt.first.size();

                        memcpy(&_generatedData[startOffSet], &fieldIt.second[0],
                            fieldIt.second.size());
                        startOffSet += fieldIt.second.size();
                    }
                }
                char status = (char)entry->getStatus();
                memcpy(&_generatedData[startOffSet], &status, sizeof(status));
            }
        });
}

std::shared_ptr<std::vector<size_t>> MemoryTable2::genDataOffset(
    Entries::Ptr _entries, size_t _startOffset)
{
    std::shared_ptr<std::vector<size_t>> dataOffset = std::make_shared<std::vector<size_t>>();
    dataOffset->push_back(_startOffset);
    for (size_t i = 0; i < _entries->size(); i++)
    {
        auto entry = (*_entries)[i];
        // 1 for status field
        auto offset = (*dataOffset)[i] + entry->capacityOfHashField() + 1;
        dataOffset->push_back(offset);
    }
    return dataOffset;
}

dev::storage::TableData::Ptr MemoryTable2::dump()
{
    // >= v2.2.0
    TIME_RECORD("MemoryTable2 Dump-" + m_tableInfo->name);
    if (m_hashDirty)
    {
        tbb::atomic<size_t> allSize = 0;

        m_tableData = std::make_shared<dev::storage::TableData>();
        m_tableData->info = m_tableInfo;
        m_tableData->dirtyEntries = std::make_shared<Entries>();

        tbb::parallel_for(m_dirty.range(),
            [&](tbb::concurrent_unordered_map<uint64_t, Entry::Ptr>::range_type& range) {
                for (auto it = range.begin(); it != range.end(); ++it)
                {
                    if (!it->second->deleted())
                    {
                        m_tableData->dirtyEntries->addEntry(it->second);
                        allSize += (it->second->capacityOfHashField() + 1);  // 1 for status field
                    }
                }
            });

        m_tableData->newEntries = std::make_shared<Entries>();
        tbb::parallel_for(m_newEntries.range(),
            [&](tbb::concurrent_unordered_map<std::string, Entries::Ptr>::range_type& range) {
                for (auto it = range.begin(); it != range.end(); ++it)
                {
                    tbb::parallel_for(tbb::blocked_range<size_t>(0, it->second->size(), 1000),
                        [&](tbb::blocked_range<size_t>& rangeIndex) {
                            for (auto i = rangeIndex.begin(); i < rangeIndex.end(); ++i)
                            {
                                if (!it->second->get(i)->deleted())
                                {
                                    m_tableData->newEntries->addEntry(it->second->get(i));
                                    // 1 for status field
                                    allSize += (it->second->get(i)->capacityOfHashField() + 1);
                                }
                            }
                        });
                }
            });

        if (m_tableInfo->enableConsensus)
        {
            TIME_RECORD("Sort data");
            tbb::parallel_sort(m_tableData->dirtyEntries->begin(), m_tableData->dirtyEntries->end(),
                EntryLessNoLock(m_tableInfo));
            tbb::parallel_sort(m_tableData->newEntries->begin(), m_tableData->newEntries->end(),
                EntryLessNoLock(m_tableInfo));
            TIME_RECORD("Calc hash");

            auto startT = utcTime();
            std::shared_ptr<bytes> allData = std::make_shared<bytes>();
            allData->resize(allSize);
            // get offset for dirtyEntries
            size_t insertDataStartOffset = 0;
            std::shared_ptr<std::vector<size_t>> dirtyEntriesOffset;
            if (m_tableData->dirtyEntries->size() > 0)
            {
                dirtyEntriesOffset = genDataOffset(m_tableData->dirtyEntries, 0);
                insertDataStartOffset = (*dirtyEntriesOffset)[m_tableData->dirtyEntries->size()];
            }
            // get offset for newEntries
            std::shared_ptr<std::vector<size_t>> newEntriesOffset;
            if (m_tableData->newEntries->size() > 0)
            {
                newEntriesOffset = genDataOffset(m_tableData->newEntries, insertDataStartOffset);
            }
            // Parallel processing dirtyEntries and newEntries
            tbb::parallel_invoke(
                [this, allData, dirtyEntriesOffset]() {
                    parallelGenData(*allData, dirtyEntriesOffset, m_tableData->dirtyEntries);
                },
                [this, allData, newEntriesOffset]() {
                    parallelGenData(*allData, newEntriesOffset, m_tableData->newEntries);
                });

#if FISCO_DEBUG
            auto printEntries = [](const string& tableName, Entries::Ptr entries) {
                if (entries->size() == 0)
                {
                    STORAGE_LOG(DEBUG) << LOG_BADGE("FISCO_DEBUG") << " entries is empty!" << endl;
                    return;
                }
                stringstream ss;
                for (size_t i = 0; i < entries->size(); ++i)
                {
                    auto data = entries->get(i);
                    ss << endl << "***" << i << " [ id=" << data->getID() << " ]";
                    for (auto& it : *data)
                    {
                        ss << "[ " << it.first << "=" << it.second << " ]";
                    }
                }
                STORAGE_LOG(DEBUG)
                    << LOG_BADGE("FISCO_DEBUG") << LOG_KV("TableName", tableName) << ss.str();
            };
            printEntries(m_tableData->info->name, m_tableData->dirtyEntries);
            printEntries(m_tableData->info->name, m_tableData->newEntries);
#endif
            auto writeDataT = utcTime() - startT;
            startT = utcTime();
            bytesConstRef bR(allData->data(), allData->size());
            auto transDataT = utcTime() - startT;
            startT = utcTime();
            if (g_BCOSConfig.version() <= V2_4_0)
            {
                if (g_BCOSConfig.SMCrypto())
                {
                    m_hash = dev::sm3(bR);
                }
                else
                {
                    // in previous version(<= 2.4.0), we use sha256(...) to calculate hash of the
                    // data, for now, to keep consistent with transction's implementation, we decide
                    // to use keccak256(...) to calculate hash of the data. This `else` branch is just
                    // for compatibility.
                    m_hash = dev::sha256(bR);
                }
            }
            else
            {
                m_hash = crypto::Hash(bR);
            }
            auto getHashT = utcTime() - startT;
            STORAGE_LOG(DEBUG) << LOG_BADGE("MemoryTable2 dump") << LOG_KV("writeDataT", writeDataT)
                               << LOG_KV("transDataT", transDataT) << LOG_KV("getHashT", getHashT)
                               << LOG_KV("hash", m_hash.abridged());
        }
        else
        {
            m_hash = dev::h256();

            STORAGE_LOG(DEBUG) << "Ignore sort and hash for: " << m_tableInfo->name
                               << " hash: " << m_hash.hex();
        }
        m_hashDirty = false;
    }

    return m_tableData;
}

void MemoryTable2::rollback(const Change& _change)
{
#if 0
    LOG(TRACE) << "Before rollback newEntries size: " << m_newEntries.size();
#endif
    switch (_change.kind)
    {
    case Change::Insert:
    {
#if 0
        LOG(TRACE) << "Rollback insert record newIndex: " << _change.value[0].index;
#endif

        auto it = m_newEntries.find(_change.key);
        if (it != m_newEntries.end())
        {
            auto entry = it->second->get(_change.value[0].index);
            entry->setDeleted(true);
        }
        break;
    }
    case Change::Update:
    {
        for (auto& record : _change.value)
        {
#if 0
            LOG(TRACE) << "Rollback update record id: " << record.id
                       << " newIndex: " << record.index;
#endif

            if (record.id)
            {
                auto it = m_dirty.find(record.id);
                if (it != m_dirty.end())
                {
                    it->second->setField(record.key, record.oldValue);
                }
            }
            else
            {
                auto it = m_newEntries.find(_change.key);
                if (it != m_newEntries.end())
                {
                    auto entry = it->second->get(record.index);
                    entry->setField(record.key, record.oldValue);
                }
            }
        }
        break;
    }
    case Change::Remove:
    {
        for (auto& record : _change.value)
        {
#if 0
            LOG(TRACE) << "Rollback remove record id: " << record.id
                       << " newIndex: " << record.index;
#endif
            if (record.id)
            {
                auto it = m_dirty.find(record.id);
                if (it != m_dirty.end())
                {
                    it->second->setStatus(0);
                }
            }
            else
            {
                auto it = m_newEntries.find(_change.key);
                if (it != m_newEntries.end())
                {
                    auto entry = it->second->get(record.index);
                    entry->setStatus(0);
                }
            }
        }
        break;
    }
    case Change::Select:

    default:
        break;
    }

    // LOG(TRACE) << "After rollback newEntries size: " << m_newEntries->size();
}

max_stars_repo_name: imtypist/FISCO-BCOS

max_issues_repo_name: imtypist/FISCO-BCOS

max_forks_repo_name: imtypist/FISCO-BCOS


text:
#include "modules/variants/ploid_limit.h"

#include "modules/variants/scaffold.h"

#include <boost/icl/interval_set.hpp>
#include <boost/icl/split_interval_map.hpp>

namespace variants {

constexpr int k_ploid_debug = 0;

ploid_limiter::~ploid_limiter() { ploid_flush(); }

void ploid_limiter::on_assembly(assembly_ptr a) {
  if (k_ploid_debug) {
    std::cout << "Got assembly: " << *a << "\n";
  }
  CHECK_GE(a->left_offset, m_cur_offset);
  m_cur_offset = a->left_offset;

  output_active();
  if (m_var_active == 0 && !m_deploid_scores.empty()) {
    do_deploid();
  }
  flush_queued();

  if (!a->matches_reference) {
    ++m_var_active;
  }
  track_left_offset(a->left_offset);
  aoffset_t right_offset = a->right_offset;
  m_active.emplace(std::make_pair(right_offset, std::move(a)));

  if (k_ploid_debug) {
    std::cout << "New active: " << m_active.size() << " vars: " << m_var_active
              << " deploids: " << m_deploid_scores.size() << "\n";
  }
}

void ploid_limiter::ploid_flush() {
  if (k_ploid_debug) {
    std::cout << "Ploid flush\n";
  }
  m_cur_offset = std::numeric_limits<aoffset_t>::max();
  output_active();
  CHECK_EQ(0, m_var_active);
  if (!m_deploid_scores.empty()) {
    do_deploid();
  }
  flush_queued();

  CHECK(m_active.empty());
  CHECK(m_deploid_scores.empty());
}

void ploid_limiter::output_active() {
  while (!m_active.empty() && m_active.begin()->first <= m_cur_offset) {
    assembly_ptr a = std::move(m_active.begin()->second);
    auto score = a->score;
    m_active.erase(m_active.begin());

    if (!a->matches_reference) {
      if (k_ploid_debug) {
        std::cout << "Adding var to deploid: " << *a << "\n";
      }
      CHECK_GT(m_var_active, 0);
      --m_var_active;
      m_deploid_scores.emplace(score, std::move(a));
      continue;
    }

    if (m_var_active || !m_deploid_scores.empty()) {
      // If we have any non-reference variants, ploid-limit them
      // against this reference assembly before continuing
      if (k_ploid_debug) {
        std::cout << "Adding ref to deploid: " << *a << "\n";
      }
      m_deploid_scores.emplace(score, std::move(a));
      continue;
    }

    untrack_left_offset(a->left_offset);
    if (k_ploid_debug) {
      std::cout << "Outputting: " << *a << "\n";
    }
    sort_and_output(std::move(a));
  }
}

void ploid_limiter::flush_queued() {
  if (m_var_active || !m_deploid_scores.empty()) {
    // Cant flush until we're finish deploiding.
    return;
  }

  flush_sorted_to(m_cur_offset);
}

void ploid_limiter::do_deploid() {
  // Include all currently active reference in the deplodification process.
  for (auto it = m_active.begin(); it != m_active.end(); ++it) {
    assembly_ptr a = std::move(it->second);
    auto score = a->score;

    CHECK(a->matches_reference);
    m_deploid_scores.emplace(std::make_pair(score, std::move(a)));
  }
  m_active.clear();
  CHECK_EQ(m_var_active, 0);

  std::vector<assembly_ptr> allele_assemblies;
  allele_assemblies.reserve(m_deploid_scores.size());

  if (k_ploid_debug) {
    std::cout << "Starting deploid at " << m_cur_offset << "\n";
  }
  // Go through assemblies, best score to worst score.
  for (auto it = m_deploid_scores.rbegin(); it != m_deploid_scores.rend(); ++it) {
    assembly_ptr a = std::move(it->second);
    if (k_ploid_debug) {
      std::cout << "Considering for deploid: " << dump_assembly_and_vars(*a) << "\n";
    }
    std::vector<size_t> merged_in;
    boost::icl::interval_map<aoffset_t, size_t> conflicts;
    unsigned max_conflict_count = 0;
    for (size_t merge_idx = 0; merge_idx != allele_assemblies.size(); ++merge_idx) {
      if (!allele_assemblies[merge_idx]) {
        continue;
      }
      auto& merge_with = allele_assemblies[merge_idx];
      if (merge_with->left_offset >= a->right_offset ||
          merge_with->right_offset <= a->left_offset) {
        continue;
      }
      CHECK(a);
      if (k_ploid_debug > 1) {
        std::cout << "Attempting to merge " << *a << " with " << *merge_with << "\n";
      }

      assembly_ptr merged = merge_assemblies(*a, *merge_with);
      if (k_ploid_debug > 1) {
        if (merged) {
          std::cout << "Merge successful: " << *merged << "\n";
        } else {
          std::cout << "Merge unsuccessful\n";
        }
      }

      if (merged) {
        merged_in.push_back(merge_idx);
        untrack_left_offset(a->left_offset);
        a = std::move(merged);
        track_left_offset(a->left_offset);
      } else {
        if (k_ploid_debug) {
          std::cout << "Conflicts with: " << dump_assembly_and_vars(*merge_with) << "\n";
        }
        auto conflict_i = boost::icl::interval<aoffset_t>::open(merge_with->left_offset,
                                                                merge_with->right_offset);
        conflicts += std::make_pair(conflict_i, size_t(1));

        for (const auto& c : (conflicts & conflict_i)) {
          if (c.second > max_conflict_count) {
            max_conflict_count = c.second;
          }
          if (max_conflict_count >= m_max_ploids) {
            break;
          }
        }
      }
      CHECK(a);
      if (max_conflict_count >= m_max_ploids) {
        break;
      }
    }
    CHECK(a);

    if (max_conflict_count >= m_max_ploids) {
      if (k_ploid_debug) {
        std::cout << "Too many conflicts; discarding: " << dump_assembly_and_vars(*a) << "\n";
      }
      untrack_left_offset(a->left_offset);
      continue;
    }

    size_t new_idx = allele_assemblies.size();
    if (k_ploid_debug) {
      std::cout << "Ploid saving " << *a << " at idx " << new_idx << "\n";
    }
    allele_assemblies.push_back(std::move(a));
    for (const size_t merge : merged_in) {
      CHECK(allele_assemblies[merge]);
      auto& old_a = allele_assemblies[merge];
      untrack_left_offset(old_a->left_offset);
      allele_assemblies[merge].release_and_discard();
    }
  }

  if (k_ploid_debug) {
    std::cout << "Done deploid\n";
  }

  for (auto& a : allele_assemblies) {
    if (!a) {
      continue;
    }
    auto right_offset = a->right_offset;
    if (right_offset <= m_cur_offset || !a->matches_reference) {
      // If it extends past cur but doesn't entirely match reference,
      // it got merged with some reference assemblies.  So the portion
      // that's still active is reference only.
      //
      // TODO(nils): It isn't quite right to remove from active and
      // output; it should really stay around so that it conflicts
      // with any compound hetrozygous variants.
      if (k_ploid_debug) {
        std::cout << "deploid output: " << *a << "\n";
      }
      untrack_left_offset(a->left_offset);
      sort_and_output(std::move(a));
    } else {
      if (k_ploid_debug) {
        std::cout << "deploid return to active: " << *a << "\n";
      }
      CHECK(a->matches_reference) << "cur offset=" << m_cur_offset << " assembly: " << *a;
      m_active.emplace(right_offset, std::move(a));
    }
  }
  m_deploid_scores.clear();
  CHECK_EQ(0, m_var_active);

  if (k_ploid_debug) {
    std::cout << "After deploiding, active:\n";
    for (const auto& i : m_active) {
      std::cout << "  " << *i.second << "\n";
      CHECK(i.second->matches_reference) << *i.second;
    }
  }
}

}  // namespace variants

ext: cpp

max_stars_repo_name: spiralgenetics/biograph

max_issues_repo_name: spiralgenetics/biograph

max_forks_repo_name: spiralgenetics/biograph


text:
/*
 * Copyright (c) 2018-2022 curoky(cccuroky@gmail.com).
 *
 * This file is part of my-own-x.
 * See https://github.com/curoky/my-own-x for further info.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <catch2/catch.hpp>  // for operator==, AssertionHandler, operator""_catch_sr, StringRef, REQUIRE, TEST_CASE

#include <string>       // for to_string, string
#include <type_traits>  // for conditional, false_type, true_type
#include <utility>      // for declval

// Ref:
// https://stackoverflow.com/questions/257288/templated-check-for-the-existence-of-a-class-member-function
// https://stackoverflow.com/questions/1966362/sfinae-to-check-for-inherited-member-functions

class Foo {
 public:
  std::string to_string(int n) { return std::to_string(n); }
};

// method one
#include <experimental/type_traits>  // IWYU pragma: keep

namespace one {
template <typename T>
using to_string_t = decltype(std::declval<T &>().to_string(int()));

template <typename T>
constexpr bool has_to_string_v = std::experimental::is_detected_v<to_string_t, T>;
}  // namespace one

// method two
namespace two {
// TODO(curoky): make it good with clang
// template <typename T>
// concept has_to_string = requires(T &t) {
//   { t.to_string(int()) } -> std::same_as<std::string>;
// };  // NOLINT
// template <typename T>
// constexpr bool has_to_string_v = has_to_string<T>;
}  // namespace two

// method three
#include <boost/hana.hpp>  // for is_valid_t::operator()

namespace three {
static auto has_to_string_v =
    boost::hana::is_valid([](auto &&obj) -> decltype(obj.to_string(int())) {});
}  // namespace three

// method four
// https://www.boost.org/doc/libs/1_76_0/libs/tti/doc/html/the_type_traits_introspection_library/tti_detail_has_static_member_function.html
#include <boost/tti/has_member_function.hpp>  // for BOOST_TTI_HAS_MEMBER_FUNCTION

namespace four {
BOOST_TTI_HAS_MEMBER_FUNCTION(to_string);

template <typename T>
constexpr bool has_to_string_v =
    has_member_function_to_string<T, std::string, boost::mpl::vector<int>>::value;
}  // namespace four

// method five
namespace five {
namespace detail {
template <typename... Ts>
struct helper {};
}  // namespace detail

template <typename Container, class = void>
struct has_to_string : std::false_type {};

template <typename T>
struct has_to_string<
    T, typename std::conditional<
           false, detail::helper<decltype(std::declval<T>().to_string(int()))>, void>::type>
    : std::true_type {};

template <typename T>
constexpr bool has_to_string_v = has_to_string<T>::value;
}  // namespace five

TEST_CASE("has member", "[sfinae]") {
  REQUIRE(one::has_to_string_v<int> == false);
  REQUIRE(one::has_to_string_v<Foo> == true);
  // REQUIRE(two::has_to_string_v<int> == false);
  // REQUIRE(two::has_to_string_v<Foo> == true);
  // REQUIRE(three::has_to_string_v(int)) == true);
  // REQUIRE(three::has_to_string_v(Foo()) == true);
  REQUIRE(four::has_to_string_v<int> == false);
  REQUIRE(four::has_to_string_v<Foo> == true);
  REQUIRE(five::has_to_string_v<int> == false);
  REQUIRE(five::has_to_string_v<Foo> == true);
}

ext: cc

max_stars_repo_name: curoky/my-own-x

max_issues_repo_name: curoky/my-own-x

max_forks_repo_name: curoky/my-own-x


text:
#include <fstream>
#include <iostream>

#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/dll.hpp>
#include <boost/spirit/home/x3.hpp>
#define CATCH_CONFIG_MAIN
#define CATCH_CONFIG_ENABLE_BENCHMARKING
#include <catch2/catch.hpp>

#include "mahjong/mahjong.hpp"

using namespace mahjong;

/**
 * @brief テストケースを読み込む。
 *
 * @param[out] cases テストケース
 * @return 読み込みに成功した場合は true、そうでない場合は false を返す。
 */
bool load_constractor_testcase(
    std::vector<std::tuple<std::vector<int>, int, int, int, int, bool, bool, bool>>
        &cases)
{
    cases.clear();

    boost::filesystem::path path =
        boost::dll::program_location().parent_path() / "testcase_hand_constractor.txt";

    // ファイルを開く。
    std::ifstream ifs(path.string());
    if (!ifs) {
        std::cerr << "Failed to open " << path.string() << "." << std::endl;
        return false;
    }

    // ファイルを読み込む。
    // 形式は `<牌1> <牌2> ... <牌14> <萬子のビット値> <筒子のビット値> <索子のビット値>
    //         <字牌のビット値> <赤五萬フラグ> <赤五筒フラグ> <赤五索フラグ>`
    std::string line;
    while (std::getline(ifs, line)) {
        std::vector<std::string> tokens;
        boost::split(tokens, line, boost::is_any_of(" "));

        std::vector<int> tiles(14);
        for (int i = 0; i < 14; ++i)
            tiles[i] = std::stoi(tokens[i]);

        cases.emplace_back(tiles, std::stoi(tokens[14]), std::stoi(tokens[15]),
                           std::stoi(tokens[16]), std::stoi(tokens[17]),
                           std::stoi(tokens[18]), std::stoi(tokens[19]),
                           std::stoi(tokens[20]));
    }

    return true;
}

/**
 * @brief テストケースを読み込む。
 *
 * @param[out] cases テストケース
 * @return 読み込みに成功した場合は true、そうでない場合は false を返す。
 */
bool load_num_tiles_testcase(
    std::vector<std::tuple<std::vector<int>, std::vector<int>, int>> &cases)
{
    cases.clear();

    boost::filesystem::path path =
        boost::dll::program_location().parent_path() / "testcase_hand_num_tiles.txt";

    // ファイルを開く。
    std::ifstream ifs(path.string());
    if (!ifs) {
        std::cerr << "Failed to open " << path.string() << "." << std::endl;
        return false;
    }

    // ファイルを読み込む。
    // 形式は `<牌1> <牌2> ... <牌14> <萬子1の枚数> <萬子2の枚数> ... <赤五萬の枚数>
    // <合計枚数>`
    std::string line;
    while (std::getline(ifs, line)) {
        std::vector<std::string> tokens;
        boost::split(tokens, line, boost::is_any_of(" "));

        std::vector<int> tiles(14);
        for (int i = 0; i < 14; ++i)
            tiles[i] = std::stoi(tokens[i]);

        std::vector<int> counts(37);
        for (int i = 0; i < 37; ++i)
            counts[i] = std::stoi(tokens[14 + i]);

        int n_tiles = std::stoi(tokens[51]);

        cases.emplace_back(tiles, counts, n_tiles);
    }

    return true;
}

/**
 * @brief テストケースを読み込む。
 *
 * @param[out] cases テストケース
 * @return 読み込みに成功した場合は true、そうでない場合は false を返す。
 */
bool load_to_string_testcase(
    std::vector<std::tuple<std::vector<int>, std::string>> &cases)
{
    cases.clear();

    boost::filesystem::path path =
        boost::dll::program_location().parent_path() / "testcase_hand_to_string.txt";

    // ファイルを開く。
    std::ifstream ifs(path.string());
    if (!ifs) {
        std::cerr << "Failed to open " << path.string() << "." << std::endl;
        return false;
    }

    // ファイルを読み込む。
    // 形式は `<牌1> <牌2> ... <牌14> <文字列表記>`
    std::string line;
    while (std::getline(ifs, line)) {
        std::vector<std::string> tokens;

        namespace x3 = boost::spirit::x3;

        auto const quoted = '"' >> *~x3::char_('"') >> '"';
        auto const unquoted = *~x3::char_(' ');
        auto const segments = (quoted | unquoted) % ' ';

        if (!x3::parse(line.begin(), line.end(), segments, tokens))
            return false;

        std::vector<int> tiles(14);
        for (int i = 0; i < 14; ++i)
            tiles[i] = std::stoi(tokens[i]);

        cases.emplace_back(tiles, tokens[14]);
    }

    return true;
}

TEST_CASE("Constract Hand")
{
    SECTION("Hand::Hand()")
    {
        Hand hand;
        REQUIRE(hand.manzu == 0);
        REQUIRE(hand.pinzu == 0);
        REQUIRE(hand.sozu == 0);
        REQUIRE(hand.zihai == 0);
        REQUIRE(!hand.aka_manzu5);
        REQUIRE(!hand.aka_pinzu5);
        REQUIRE(!hand.aka_sozu5);
        REQUIRE(hand.melds.empty());
    }

    SECTION("Hand::Hand(const std::vector<int> &)")
    {
        std::vector<std::tuple<std::vector<int>, int, int, int, int, bool, bool, bool>>
            cases;
        if (!load_constractor_testcase(cases))
            return;

        for (auto &[tiles, manzu, pinzu, sozu, zihai, aka_manzu5, aka_pinzu5,
                    aka_sozu5] : cases) {
            Hand hand(tiles);
            REQUIRE(hand.manzu == manzu);
            REQUIRE(hand.pinzu == pinzu);
            REQUIRE(hand.sozu == sozu);
            REQUIRE(hand.zihai == zihai);
            REQUIRE(hand.aka_manzu5 == aka_manzu5);
            REQUIRE(hand.aka_pinzu5 == aka_pinzu5);
            REQUIRE(hand.aka_sozu5 == aka_sozu5);
            REQUIRE(hand.melds.empty());
        }
    }
}

TEST_CASE("is_menzen()")
{
    SECTION("鳴いていない場合は門前")
    {
        Hand hand({Tile::Manzu1, Tile::Manzu1, Tile::Manzu2, Tile::Manzu2,
                   Tile::AkaManzu5, Tile::Manzu6, Tile::Manzu7, Tile::Manzu8,
                   Tile::Manzu9, Tile::Pinzu1, Tile::Pinzu1, Tile::Pinzu2,
                   Tile::Pinzu2});

        REQUIRE(hand.is_menzen());
    }

    SECTION("副露ブロックに暗槓以外がない場合は門前")
    {
        MeldedBlock block1 = {
            MeldType::Pon, {Tile::Pinzu1, Tile::Pinzu1, Tile::Pinzu1}, Tile::Pinzu1, 0};
        MeldedBlock block2 = {MeldType::Ankan,
                              {Tile::Pinzu8, Tile::Pinzu8, Tile::Pinzu8, Tile::Pinzu8},
                              Tile::Pinzu1,
                              1};
        Hand hand({Tile::Manzu1, Tile::Manzu1, Tile::Manzu2, Tile::Manzu2, Tile::Manzu6,
                   Tile::Manzu7, Tile::Manzu8},
                  {block1, block2});

        REQUIRE(!hand.is_menzen());
    }

    SECTION("副露ブロックに暗槓以外がある場合は門前でない")
    {
        MeldedBlock block1 = {MeldType::Ankan,
                              {Tile::Pinzu1, Tile::Pinzu1, Tile::Pinzu1},
                              Tile::Pinzu1,
                              0};
        MeldedBlock block2 = {MeldType::Ankan,
                              {Tile::Pinzu8, Tile::Pinzu8, Tile::Pinzu8, Tile::Pinzu8},
                              Tile::Pinzu1,
                              1};
        Hand hand({Tile::Manzu1, Tile::Manzu1, Tile::Manzu2, Tile::Manzu2, Tile::Manzu6,
                   Tile::Manzu7, Tile::Manzu8},
                  {block1, block2});

        REQUIRE(hand.is_menzen());
    }
}

TEST_CASE("is_melded()")
{
    SECTION("鳴いていない")
    {
        Hand hand({Tile::Manzu1, Tile::Manzu1, Tile::Manzu2, Tile::Manzu2,
                   Tile::AkaManzu5, Tile::Manzu6, Tile::Manzu7, Tile::Manzu8,
                   Tile::Manzu9, Tile::Pinzu1, Tile::Pinzu1, Tile::Pinzu2,
                   Tile::Pinzu2});

        REQUIRE(!hand.is_melded());
    }

    SECTION("副露ブロックに暗槓以外がない")
    {
        MeldedBlock block1 = {
            MeldType::Pon, {Tile::Pinzu1, Tile::Pinzu1, Tile::Pinzu1}, Tile::Pinzu1, 0};
        MeldedBlock block2 = {MeldType::Ankan,
                              {Tile::Pinzu8, Tile::Pinzu8, Tile::Pinzu8, Tile::Pinzu8},
                              Tile::Pinzu1,
                              1};
        Hand hand({Tile::Manzu1, Tile::Manzu1, Tile::Manzu2, Tile::Manzu2, Tile::Manzu6,
                   Tile::Manzu7, Tile::Manzu8},
                  {block1, block2});

        REQUIRE(hand.is_melded());
    }

    SECTION("副露ブロックに暗槓以外がある")
    {
        MeldedBlock block1 = {MeldType::Ankan,
                              {Tile::Pinzu1, Tile::Pinzu1, Tile::Pinzu1},
                              Tile::Pinzu1,
                              0};
        MeldedBlock block2 = {MeldType::Ankan,
                              {Tile::Pinzu8, Tile::Pinzu8, Tile::Pinzu8, Tile::Pinzu8},
                              Tile::Pinzu1,
                              1};
        Hand hand({Tile::Manzu1, Tile::Manzu1, Tile::Manzu2, Tile::Manzu2, Tile::Manzu6,
                   Tile::Manzu7, Tile::Manzu8},
                  {block1, block2});

        REQUIRE(hand.is_melded());
    }
}

TEST_CASE("num_tiles()")
{
    std::vector<std::tuple<std::vector<int>, std::vector<int>, int>> cases;
    if (!load_num_tiles_testcase(cases))
        return;

    for (auto &[tiles, counts, n_tiles] : cases) {
        Hand hand(tiles);

        for (int i = 0; i < 37; ++i)
            REQUIRE(hand.num_tiles(i) == counts[i]);

        REQUIRE(hand.num_tiles() == n_tiles);
    }
}

TEST_CASE("to_string()")
{
    std::vector<std::tuple<std::vector<int>, std::string>> cases;
    if (!load_to_string_testcase(cases))
        return;

    for (auto &[tiles, str] : cases) {
        Hand hand(tiles);
        REQUIRE(hand.to_string() == str);
    }
}

max_stars_repo_name: Muratam/mahjong-cpp

max_issues_repo_name: Muratam/mahjong-cpp

max_forks_repo_name: Muratam/mahjong-cpp


text:
// copyright (c) 2013 the moorecoin core developers
// distributed under the mit software license, see the accompanying
// file copying or http://www.opensource.org/licenses/mit-license.php.

#include "consensus/validation.h"
#include "data/sighash.json.h"
#include "main.h"
#include "random.h"
#include "script/interpreter.h"
#include "script/script.h"
#include "serialize.h"
#include "test/test_moorecoin.h"
#include "util.h"
#include "version.h"

#include <iostream>

#include <boost/test/unit_test.hpp>

#include "univalue/univalue.h"

extern univalue read_json(const std::string& jsondata);

// old script.cpp signaturehash function
uint256 static signaturehashold(cscript scriptcode, const ctransaction& txto, unsigned int nin, int nhashtype)
{
    static const uint256 one(uint256s("0000000000000000000000000000000000000000000000000000000000000001"));
    if (nin >= txto.vin.size())
    {
        printf("error: signaturehash(): nin=%d out of range\n", nin);
        return one;
    }
    cmutabletransaction txtmp(txto);

    // in case concatenating two scripts ends up with two codeseparators,
    // or an extra one at the end, this prevents all those possible incompatibilities.
    scriptcode.findanddelete(cscript(op_codeseparator));

    // blank out other inputs' signatures
    for (unsigned int i = 0; i < txtmp.vin.size(); i++)
        txtmp.vin[i].scriptsig = cscript();
    txtmp.vin[nin].scriptsig = scriptcode;

    // blank out some of the outputs
    if ((nhashtype & 0x1f) == sighash_none)
    {
        // wildcard payee
        txtmp.vout.clear();

        // let the others update at will
        for (unsigned int i = 0; i < txtmp.vin.size(); i++)
            if (i != nin)
                txtmp.vin[i].nsequence = 0;
    }
    else if ((nhashtype & 0x1f) == sighash_single)
    {
        // only lock-in the txout payee at same index as txin
        unsigned int nout = nin;
        if (nout >= txtmp.vout.size())
        {
            printf("error: signaturehash(): nout=%d out of range\n", nout);
            return one;
        }
        txtmp.vout.resize(nout+1);
        for (unsigned int i = 0; i < nout; i++)
            txtmp.vout[i].setnull();

        // let the others update at will
        for (unsigned int i = 0; i < txtmp.vin.size(); i++)
            if (i != nin)
                txtmp.vin[i].nsequence = 0;
    }

    // blank out other inputs completely, not recommended for open transactions
    if (nhashtype & sighash_anyonecanpay)
    {
        txtmp.vin[0] = txtmp.vin[nin];
        txtmp.vin.resize(1);
    }

    // serialize and hash
    chashwriter ss(ser_gethash, 0);
    ss << txtmp << nhashtype;
    return ss.gethash();
}

void static randomscript(cscript &script) {
    static const opcodetype oplist[] = {op_false, op_1, op_2, op_3, op_checksig, op_if, op_verif, op_return, op_codeseparator};
    script = cscript();
    int ops = (insecure_rand() % 10);
    for (int i=0; i<ops; i++)
        script << oplist[insecure_rand() % (sizeof(oplist)/sizeof(oplist[0]))];
}

void static randomtransaction(cmutabletransaction &tx, bool fsingle) {
    tx.nversion = insecure_rand();
    tx.vin.clear();
    tx.vout.clear();
    tx.nlocktime = (insecure_rand() % 2) ? insecure_rand() : 0;
    int ins = (insecure_rand() % 4) + 1;
    int outs = fsingle ? ins : (insecure_rand() % 4) + 1;
    for (int in = 0; in < ins; in++) {
        tx.vin.push_back(ctxin());
        ctxin &txin = tx.vin.back();
        txin.prevout.hash = getrandhash();
        txin.prevout.n = insecure_rand() % 4;
        randomscript(txin.scriptsig);
        txin.nsequence = (insecure_rand() % 2) ? insecure_rand() : (unsigned int)-1;
    }
    for (int out = 0; out < outs; out++) {
        tx.vout.push_back(ctxout());
        ctxout &txout = tx.vout.back();
        txout.nvalue = insecure_rand() % 100000000;
        randomscript(txout.scriptpubkey);
    }
}

boost_fixture_test_suite(sighash_tests, basictestingsetup)

boost_auto_test_case(sighash_test)
{
    seed_insecure_rand(false);

    #if defined(print_sighash_json)
    std::cout << "[\n";
    std::cout << "\t[\"raw_transaction, script, input_index, hashtype, signature_hash (result)\"],\n";
    #endif
    int nrandomtests = 50000;

    #if defined(print_sighash_json)
    nrandomtests = 500;
    #endif
    for (int i=0; i<nrandomtests; i++) {
        int nhashtype = insecure_rand();
        cmutabletransaction txto;
        randomtransaction(txto, (nhashtype & 0x1f) == sighash_single);
        cscript scriptcode;
        randomscript(scriptcode);
        int nin = insecure_rand() % txto.vin.size();

        uint256 sh, sho;
        sho = signaturehashold(scriptcode, txto, nin, nhashtype);
        sh = signaturehash(scriptcode, txto, nin, nhashtype);
        #if defined(print_sighash_json)
        cdatastream ss(ser_network, protocol_version);
        ss << txto;

        std::cout << "\t[\"" ;
        std::cout << hexstr(ss.begin(), ss.end()) << "\", \"";
        std::cout << hexstr(scriptcode) << "\", ";
        std::cout << nin << ", ";
        std::cout << nhashtype << ", \"";
        std::cout << sho.gethex() << "\"]";
        if (i+1 != nrandomtests) {
          std::cout << ",";
        }
        std::cout << "\n";
        #endif
        boost_check(sh == sho);
    }
    #if defined(print_sighash_json)
    std::cout << "]\n";
    #endif
}

// goal: check that signaturehash generates correct hash
boost_auto_test_case(sighash_from_data)
{
    univalue tests = read_json(std::string(json_tests::sighash, json_tests::sighash + sizeof(json_tests::sighash)));

    for (unsigned int idx = 0; idx < tests.size(); idx++) {
        univalue test = tests[idx];
        std::string strtest = test.write();
        if (test.size() < 1) // allow for extra stuff (useful for comments)
        {
            boost_error("bad test: " << strtest);
            continue;
        }
        if (test.size() == 1) continue; // comment

        std::string raw_tx, raw_script, sighashhex;
        int nin, nhashtype;
        uint256 sh;
        ctransaction tx;
        cscript scriptcode = cscript();

        try {
          // deserialize test data
          raw_tx = test[0].get_str();
          raw_script = test[1].get_str();
          nin = test[2].get_int();
          nhashtype = test[3].get_int();
          sighashhex = test[4].get_str();

          uint256 sh;
          cdatastream stream(parsehex(raw_tx), ser_network, protocol_version);
          stream >> tx;

          cvalidationstate state;
          boost_check_message(checktransaction(tx, state), strtest);
          boost_check(state.isvalid());

          std::vector<unsigned char> raw = parsehex(raw_script);
          scriptcode.insert(scriptcode.end(), raw.begin(), raw.end());
        } catch (...) {
          boost_error("bad test, couldn't deserialize data: " << strtest);
          continue;
        }

        sh = signaturehash(scriptcode, tx, nin, nhashtype);
        boost_check_message(sh.gethex() == sighashhex, strtest);
    }
}
boost_auto_test_suite_end()

max_stars_repo_name: moorecoin/MooreCoinMiningAlgorithm

max_issues_repo_name: moorecoin/MooreCoinMiningAlgorithm

max_forks_repo_name: moorecoin/MooreCoinMiningAlgorithm


text:
// Copyright 2013 Daniel Parker
// Distributed under Boost license

#ifdef __linux__
#define BOOST_TEST_DYN_LINK
#endif

#include <boost/test/unit_test.hpp>
#include "jsoncons/json.hpp"
#include "jsoncons/json_serializer.hpp"
#include "jsoncons/json_reader.hpp"
#include <sstream>
#include <vector>
#include <utility>
#include <ctime>

using namespace jsoncons;

BOOST_AUTO_TEST_SUITE(error_recovery_test_suite)

class my_parse_error_handler : public parse_error_handler
{
private:
    virtual void do_warning(std::error_code ec,
                            const parsing_context& context) throw(parse_exception)
    {
    }

    virtual void do_error(std::error_code ec,
                          const parsing_context& context) throw(parse_exception)
    {
        if (ec.category() == json_error_category())
        {
            if (ec.value() != jsoncons::json_parser_errc::extra_comma && (context.current_char() == ']' || context.current_char() == '}'))
            {
                default_parse_error_handler::instance().error(ec,context);
            }
        }
    }
};

BOOST_AUTO_TEST_CASE(test_accept_trailing_value_separator)
{
    /*my_parse_error_handler err_handler;

    json val = json::parse("[1,2,3,]", err_handler);

    std::cout << val << std::endl;*/
}

BOOST_AUTO_TEST_SUITE_END()

max_stars_repo_name: liftchampion/nativejson-benchmark

max_issues_repo_name: liftchampion/nativejson-benchmark

max_forks_repo_name: liftchampion/nativejson-benchmark


text:
// AirMap Platform SDK
// Copyright © 2018 AirMap, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//   http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an AS IS BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <airmap/codec/http/query/advisories.h>

#include <airmap/codec.h>
#include <airmap/codec/json/geometry.h>
#include <airmap/date_time.h>

#include <boost/lexical_cast.hpp>

void airmap::codec::http::query::encode(std::unordered_map<std::string, std::string>& query,
                                        const Advisory::ForId::Parameters& parameters) {
  if (parameters.start)
    query["start"] = iso8601::generate(parameters.start.get());
  if (parameters.end)
    query["end"] = iso8601::generate(parameters.end.get());
}

void airmap::codec::http::query::encode(std::unordered_map<std::string, std::string>& query,
                                        const Advisory::ReportWeather::Parameters& parameters) {
  if (parameters.latitude)
    query["latitude"] = boost::lexical_cast<std::string>(parameters.latitude);
  if (parameters.longitude)
    query["longitude"] = boost::lexical_cast<std::string>(parameters.longitude);
  if (parameters.start)
    query["start"] = iso8601::generate(parameters.start.get());
  if (parameters.end)
    query["end"] = iso8601::generate(parameters.end.get());
}

max_stars_repo_name: YUNEEC/platform-sdk

max_issues_repo_name: YUNEEC/platform-sdk

max_forks_repo_name: YUNEEC/platform-sdk


text:
/*!
 * \file WtHelper.cpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 *
 * \brief
 */
#include "WtHelper.h"

#include "../Share/StrUtil.hpp"
#include <boost/filesystem.hpp>

#ifdef _MSC_VER
#include <direct.h>
#else	//UNIX
#include <unistd.h>
#endif

std::string WtHelper::_inst_dir;
std::string WtHelper::_out_dir = "./outputs_bt/";

std::string WtHelper::getCWD()
{
	static std::string _cwd;
	if(_cwd.empty())
	{
		char   buffer[255];
#ifdef _MSC_VER
		_getcwd(buffer, 255);
#else	//UNIX
		getcwd(buffer, 255);
#endif
		_cwd = buffer;
		_cwd = StrUtil::standardisePath(_cwd);
	}
	return _cwd;
}

void WtHelper::setOutputDir(const char* out_dir)
{
	_out_dir = StrUtil::standardisePath(std::string(out_dir));
}

const char* WtHelper::getOutputDir()
{
	if (!boost::filesystem::exists(_out_dir.c_str()))
        boost::filesystem::create_directories(_out_dir.c_str());
	return _out_dir.c_str();
}


text:
//
// Copyright Copyright 2009-2021, AMT – The Association For Manufacturing Technology (“AMT”)
// All rights reserved.
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
//

#pragma once

#include "adapter/adapter.hpp"
#include "adapter/adapter_pipeline.hpp"
#include "http_server/file_cache.hpp"
#include "service.hpp"
#include "utilities.hpp"

#include <dlib/logger.h>

#include <chrono>
#include <string>

namespace mtconnect
{
  namespace http_server
  {
    class Server;
  }
  class Agent;
  class Device;
  class RollingFileLogger;
  class XmlPrinter;

  using ConfigReader = dlib::config_reader::kernel_1a;

  using NamespaceFunction = void (XmlPrinter::*)(const std::string &, const std::string &,
                                                 const std::string &);
  using StyleFunction = void (XmlPrinter::*)(const std::string &);

  class AgentConfiguration : public MTConnectService
  {
  public:
    AgentConfiguration();
    virtual ~AgentConfiguration();

    // For MTConnectService
    void stop() override;
    void start() override;
    void initialize(int argc, const char *argv[]) override;

    void configureLogger(ConfigReader &reader);
    void loadConfig(std::istream &file);

    void setAgent(std::unique_ptr<Agent> &agent) { m_agent = std::move(agent); }
    const Agent *getAgent() const { return m_agent.get(); }

    const RollingFileLogger *getLogger() const { return m_loggerFile.get(); }

    void updateWorkingDirectory() { m_working = std::filesystem::current_path(); }

  protected:
    Device *defaultDevice();
    void loadAdapters(ConfigReader &reader, const ConfigOptions &options);
    void loadAllowPut(ConfigReader &reader, http_server::Server *server);
    void loadNamespace(ConfigReader &reader, const char *namespaceType,
                       http_server::FileCache *cache, XmlPrinter *printer,
                       NamespaceFunction callback);
    void loadFiles(XmlPrinter *xmlPrinter, ConfigReader &reader, http_server::FileCache *cache);
    void loadStyle(ConfigReader &reader, const char *styleName, http_server::FileCache *cache,
                   XmlPrinter *printer, StyleFunction styleFunction);
    void loadTypes(ConfigReader &reader, http_server::FileCache *cache);
    void loadHttpHeaders(ConfigReader &reader, ConfigOptions &options);

    void LoggerHook(const std::string &loggerName, const dlib::log_level &l,
                    const dlib::uint64 threadId, const char *message);

    std::optional<std::filesystem::path> checkPath(const std::string &name);

    void monitorThread();

  protected:
    std::unique_ptr<Agent> m_agent;
    pipeline::PipelineContextPtr m_pipelineContext;
    std::unique_ptr<adapter::Handler> m_adapterHandler;
    std::unique_ptr<RollingFileLogger> m_loggerFile;
    std::string m_version;
    bool m_monitorFiles = false;
    int m_minimumConfigReloadAge = 15;
    std::string m_devicesFile;
    bool m_restart = false;
    std::filesystem::path m_exePath;
    std::filesystem::path m_working;
    bool m_pretty = false;
  };
}  // namespace mtconnect

ext: hpp

max_stars_repo_name: masonhieb/cppagent

max_issues_repo_name: masonhieb/cppagent

max_forks_repo_name: masonhieb/cppagent


text:
#include <boost/variant2/variant.hpp>

int
main ()
{
  return 0;
}


text: #include <boost/spirit/home/lex/lexer/pass_flags.hpp>

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino


text:
/*
 * Copyright © 2014-2015  Klaus Reuter
 * Copyright © 2014       Felix Höfling
 * Copyright © 2014       Manuel Dibak
 * All rights reserved.
 *
 * This file is part of h5xx — a C++ wrapper for the HDF5 library.
 *
 * This software may be modified and distributed under the terms of the
 * 3-clause BSD license.  See accompanying file LICENSE for details.
 */

#ifndef H5XX_DATASET_BOOST_ARRAY
#define H5XX_DATASET_BOOST_ARRAY

#include <algorithm>

#include <h5xx/ctype.hpp>
#include <h5xx/dataset/dataset.hpp>
#include <h5xx/dataspace.hpp>
#include <h5xx/policy/storage.hpp>
#include <h5xx/utility.hpp>
#include <h5xx/error.hpp>

#include <boost/array.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/mpl/and.hpp>
#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>

namespace h5xx {

/**
 * create dataset from a boost::array of fundamental type
 **/
template <typename h5xxObject, typename T, typename StoragePolicy>
inline typename boost::enable_if< boost::mpl::and_< is_array<T>, boost::is_fundamental<typename T::value_type> >, dataset>::type
create_dataset(h5xxObject const& object, std::string const& name, T const& value,
                   StoragePolicy const& storage_policy = StoragePolicy())
{
    typedef typename T::value_type value_type;
    hid_t type_id = ctype<value_type>::hid(); // this ID must not be closed
    boost::array<hsize_t, 1> dims;
    dims[0] = value.size();
    return dataset(object, name, type_id, dataspace(dims), storage_policy);
}

/**
 * create dataset from a boost::array of fundamental type, using default storage layout
 **/
template <typename h5xxObject, typename T>
inline typename boost::enable_if< boost::mpl::and_< is_array<T>, boost::is_fundamental<typename T::value_type> >, dataset>::type
create_dataset(h5xxObject const& object, std::string const& name, T const& value)
{
    return create_dataset(object, name, value, h5xx::policy::storage::contiguous());
}

/**
 * write boost::array data to an existing dataset specified by location and name
 */
template <typename h5xxObject, typename T>
inline typename boost::enable_if< boost::mpl::and_< is_array<T>, boost::is_fundamental<typename T::value_type> >, void>::type
write_dataset(h5xxObject const& object, std::string const& name, T const& value)
{
    dataset dset(object, name);
    write_dataset(dset, value);
}

/**
 * write boost::array data to dataset
 */
template <typename T>
inline typename boost::enable_if< boost::mpl::and_< is_array<T>, boost::is_fundamental<typename T::value_type> >, void>::type
write_dataset(dataset& dset, T const& value)
{
    typedef typename T::value_type value_type;
    hid_t type_id = ctype<value_type>::hid();
    dset.write(type_id, &*value.begin());
}

/**
 * Write boost::array data to an existing dataset specified by location and name,
 * memory and file locations (hyperslabs) are passed via the dataspace objects.
 */
template <typename h5xxObject, typename T>
inline typename boost::enable_if< boost::mpl::and_< is_array<T>, boost::is_fundamental<typename T::value_type> >, void>::type
write_dataset(h5xxObject const& object, std::string const& name, T const& value,
                  dataspace const& memspace, dataspace const& filespace)
{
    dataset dset(object, name);
    write_dataset(dset, value, memspace, filespace);
}

/**
 * Write boost::array data, memory and file locations (hyperslabs) are passed via
 * the dataspace objects.
 */
template <typename T>
inline typename boost::enable_if< boost::mpl::and_< is_array<T>, boost::is_fundamental<typename T::value_type> >, void>::type
write_dataset(dataset& dset, T const& value, dataspace const& memspace, dataspace const& filespace)
{
    typedef typename T::value_type value_type;
    hid_t type_id = ctype<value_type>::hid();
    hid_t mem_space_id = memspace.hid(); //H5S_ALL;
    hid_t file_space_id = filespace.hid();
    hid_t xfer_plist_id = H5P_DEFAULT;
    dset.write(type_id, &*value.begin(), mem_space_id, file_space_id, xfer_plist_id);
}

/**
 * Write boost::array data to an existing dataset specified its location and
 * name, only the file location (hyperslab) is given via a slice object.
 */
template <typename h5xxObject, typename T>
inline typename boost::enable_if< boost::mpl::and_< is_array<T>, boost::is_fundamental<typename T::value_type> >, void>::type
write_dataset(h5xxObject const& object, std::string const& name, T const& value, slice const& file_slice)
{
    dataset dset(object, name);
    write_dataset(dset, value, file_slice);
}

/**
 * Write boost::array data to an existing dataset, only the file location
 * (hyperslab) is given via a slice object.
 */
template <typename T>
inline typename boost::enable_if< boost::mpl::and_< is_array<T>, boost::is_fundamental<typename T::value_type> >, void>::type
write_dataset(dataset& dset, T const& value, slice const& file_slice)
{
    // --- create memory dataspace for the complete input array
    h5xx::dataspace memspace = h5xx::create_dataspace(value);
    // --- create file dataspace and select the slice (hyperslab) from it
    h5xx::dataspace filespace(dset);
    filespace.select(file_slice);
    write_dataset(dset, value, memspace, filespace);
}

/**
 * Read boost::array data from an existing dataset specified by location and name.
 */
template <typename h5xxObject, typename T>
inline typename boost::enable_if< boost::mpl::and_< is_array<T>, boost::is_fundamental<typename T::value_type> >, void>::type
read_dataset(h5xxObject const& object, std::string const& name, T & value)
{
    dataset dset(object, name);
    read_dataset(dset, value);
}

/**
 * Read boost::array data from an existing dataset.
 */
template <typename T>
inline typename boost::enable_if< boost::mpl::and_< is_array<T>, boost::is_fundamental<typename T::value_type> >, void>::type
read_dataset(dataset & data_set, T & value)
{
    typedef typename T::value_type value_type;
    hid_t type_id = ctype<value_type>::hid();

    dataspace file_space(data_set);
    // Assure that the array has at least the capacity of the dataspace selection.
    if (static_cast<hsize_t>(file_space.get_select_npoints()) > value.size())
        H5XX_THROW("target array does not provide enough space to store slice");

    hid_t mem_space_id = H5S_ALL;
    hid_t file_space_id = H5S_ALL;
    hid_t xfer_plist_id = H5P_DEFAULT;

    data_set.read(type_id, &*value.begin(), mem_space_id, file_space_id, xfer_plist_id);
}

/**
 * Read boost::array data from an existing dataset specified by location and name,
 * a slice specifies the data locations to be read in file space.
 */
template <typename h5xxObject, typename T>
inline typename boost::enable_if< boost::mpl::and_< is_array<T>, boost::is_fundamental<typename T::value_type> >, void>::type
read_dataset(h5xxObject const& object, std::string const& name, T & value, slice const& file_slice)
{
    dataset data_set(object, name);
    read_dataset(data_set, value, file_slice);
}

/**
 * Read boost::array data from an existing dataset, a slice specifies the data
 * locations to be read in file space.
 */
template <typename T>
inline typename boost::enable_if< boost::mpl::and_< is_array<T>, boost::is_fundamental<typename T::value_type> >, void>::type
read_dataset(dataset & data_set, T & value, slice const& file_slice)
{
    // --- create memory dataspace for the complete input array
    h5xx::dataspace memspace = h5xx::create_dataspace(value);
    // --- create file dataspace and select the slice (hyperslab) from it
    h5xx::dataspace filespace(data_set);
    filespace.select(file_slice);
    // ---
    read_dataset(data_set, value, memspace, filespace);
}

/**
 * Read boost::array data from an existing dataset, dataspace objects for both
 * memory and file allow to specify the locations of the data.
 */
template <typename T>
inline typename boost::enable_if< boost::mpl::and_< is_array<T>, boost::is_fundamental<typename T::value_type> >, void>::type
read_dataset(dataset & data_set, T & value, dataspace const& memspace, dataspace const& filespace)
{
    // Assure that the array has at least the capacity of the dataspace selection.
    if (static_cast<hsize_t>(filespace.get_select_npoints()) > value.size())
        H5XX_THROW("target array does not provide enough space to store slice");

    typedef typename T::value_type value_type;
    hid_t type_id = ctype<value_type>::hid();

    hid_t mem_space_id = memspace.hid();
    hid_t file_space_id = filespace.hid();
    hid_t xfer_plist_id = H5P_DEFAULT;

    data_set.read(type_id, &*value.begin(), mem_space_id, file_space_id, xfer_plist_id);
}

} // namespace h5xx

#endif // ! H5XX_DATASET_BOOST_ARRAY


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

#include <boost/simd/toolbox/operator/functions/value.hpp>
#include <boost/simd/sdk/simd/logical.hpp>
#include <boost/dispatch/meta/mpl.hpp>
#include <boost/dispatch/meta/print.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION ( boost::simd::tag::value_, tag::cpu_
                                    , (A0)
                                    , (unspecified_<A0>)
                                    )
  {
    template<class Sig> struct result;

    template<class This, class T>
    struct result<This(T)> : boost::add_reference<T>
    {};

    template<class T> BOOST_FORCEINLINE T& operator()(T& a0) const
    {

      return a0;
    }
  };

  BOOST_SIMD_FUNCTOR_IMPLEMENTATION ( boost::simd::tag::value_, tag::cpu_
                                    , (A0)
                                    , (scalar_< logical_<A0> >)
                                    )
  {
    typedef bool result_type;

    BOOST_FORCEINLINE result_type operator()(A0 const& a0) const
    {
      return a0.value();
    }
  };

  BOOST_SIMD_FUNCTOR_IMPLEMENTATION ( boost::simd::tag::value_, tag::cpu_
                                    , (A0)
                                    , (mpl_integral_< unspecified_<A0> >)
                                    )
  {
    typedef typename A0::value_type result_type;

    BOOST_FORCEINLINE result_type operator()(A0 const&) const
    {
      return A0::value;
    }
  };
} } }

#endif


text:
#ifndef BOOST_THREAD_PERMIT_HPP
#define BOOST_THREAD_PERMIT_HPP

//  permit.hpp
//
//  (C) Copyright 2014 Niall Douglas
//
//  Distributed under the Boost Software License, Version 1.0. (See
//  accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

#include <boost/thread/detail/platform.hpp>
#if defined(BOOST_THREAD_PLATFORM_WIN32)
#include <boost/thread/win32/permit.hpp>
#elif defined(BOOST_THREAD_PLATFORM_PTHREAD)
#include <boost/thread/pthread/permit.hpp>
#else
#error "Boost threads unavailable on this platform"
#endif

#endif


text:
// Copyright (c) 2019 Egor Tensin <Egor.Tensin@gmail.com>
// This file is part of the "math-server" project.
// For details, see https://github.com/egor-tensin/math-server.
// Distributed under the MIT License.

#pragma once

#include <boost/asio.hpp>
#include <boost/system/error_code.hpp>

#include <cstddef>
#include <memory>
#include <string>

namespace math::server {

class SessionManager;

class Session : public std::enable_shared_from_this<Session> {
public:
    Session(SessionManager& mgr, boost::asio::io_context& io_context);

    boost::asio::ip::tcp::socket& socket();

    void start();
    void stop();

private:
    void close();

    void read();
    void write(const std::string&);

    void handle_read(const boost::system::error_code&, std::size_t);
    void handle_write(const boost::system::error_code&, std::size_t);

    std::string consume_input(std::size_t);

    SessionManager& m_session_mgr;

    boost::asio::io_context::strand m_strand;
    boost::asio::ip::tcp::socket m_socket;
    boost::asio::streambuf m_buffer;
};

} // namespace math::server

max_stars_repo_name: egor-tensin/math-server

max_issues_repo_name: egor-tensin/math-server

max_forks_repo_name: egor-tensin/math-server


text:
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <armadillo>
#include <random>

#include <mpi.h>

#include "transactions.hh"
#include "histograms.hh"

using namespace std;
using namespace arma;

int main(int argc, char **argv) {
  int mpiSize, mpiRank;

  MPI_Init(&argc, &argv);
  MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
  MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);

  if(argc <= 6) {
    if(mpiRank == 0) {
      fprintf(stderr, "Usage: %s OUTPUT-FILE NUM-AGENTS NUM-TRANSACTIONS NUM-RUNS INITIAL-MONEY [LAMBDA] [ALPHA] [S-FACTOR]\n", argv[0]);
    }
    MPI_Finalize();
    exit(0);
  }

  // read parameters
  const char *filename;
  int N;      // number of agents
  int K;      // number of transactions
  int R;      // number of runs
  double m0;  // initial money for each agent
  double l;   // lambda parameter
  double a;   // alpha parameter
  double S;   // "S-factor", affects accuracy and slowness of simulations with α≠0 or γ≠0 (default: 1.0)
  if(mpiRank == 0) {
    // mandatory arguments
    filename = argv[1];
    N = atoi(argv[2]);
    K = atoi(argv[3]);
    R = atoi(argv[4]);
    m0 = atof(argv[5]);

    // optional arguments
    l  = (argc > 6 ? atof(argv[6]) : 0.0);
    a  = (argc > 7 ? atof(argv[7]) : 0.0);
    S  = (argc > 8 ? atof(argv[8]) : 1.0);
  }

  if(mpiRank == 0) {
    fprintf(stderr, "Running with parameters:\n");
    fprintf(stderr, "  N = %d\n", N);
    fprintf(stderr, "  K = %d\n", K);
    fprintf(stderr, "  R = %d\n", R);
    fprintf(stderr, " m0 = %.3f\n", m0);
    fprintf(stderr, "  λ = %.3f\n", l);
    fprintf(stderr, "  α = %.3f\n", a);
    fprintf(stderr, "  S = %.1f\n", S);
  }

  // broadcast parameters to all nodes
  MPI_Bcast(&N, 1, MPI_INT, 0, MPI_COMM_WORLD);
  MPI_Bcast(&K, 1, MPI_INT, 0, MPI_COMM_WORLD);
  MPI_Bcast(&R, 1, MPI_INT, 0, MPI_COMM_WORLD);
  MPI_Bcast(&m0, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
  MPI_Bcast(&l, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
  MPI_Bcast(&a, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
  MPI_Bcast(&S, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);

  // reseed simulation with true random seed
  // (so that the different processes don't make the exact same simulations)
  seed_simulation();

  // list of all agents after each run
  vector<double> m(N * R, 1);

  // find start and end indices for range of runs to be done
  // by this process
  size_t runsPerProcess = R / mpiSize;
  size_t startRun = mpiRank * runsPerProcess;
  size_t endRun = startRun + runsPerProcess;
  if(endRun > R) endRun = R;
  if(mpiRank == mpiSize - 1 && endRun < R) endRun = R;

  // do all runs (parallelized)
  for(size_t r = startRun; r < endRun; r++) {
    // simulate transactions
    // (store resulting money distribution in a subvector)
    vector<double> m_dist = simulate_transactions(N, K, m0, l, S, a);

    copy(begin(m_dist), end(m_dist), begin(m) + (r * N));
  }

  // send all data to process #0
  double *mptr = m.data();
  if(mpiRank == 0) {
    for(int p = 1; p < mpiSize; p++) {
      size_t pStartRun = p * runsPerProcess;
      size_t pEndRun   = pStartRun + runsPerProcess;
      if(pEndRun > R) pEndRun = R;
      if(p == mpiSize - 1 && pEndRun < R) pEndRun = R;

      MPI_Recv(&mptr[N * pStartRun], N * (pEndRun - pStartRun), MPI_DOUBLE, p, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    }
  } else {
    MPI_Send(&mptr[N * startRun], N * (endRun - startRun), MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
  }

  // save histogram data to file (for plotting)
  if(mpiRank == 0)
    save_histogram(m, 0.01, filename);

  MPI_Finalize();
  return 0;
}