<commit_before>// Copyright (c) 2012-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "coins.h"

#include "consensus/consensus.h"
#include "memusage.h"
#include "random.h"

#include <assert.h>

bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; }
bool CCoinsView::HaveCoin(const COutPoint &outpoint) const { return false; }
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; }
CCoinsViewCursor *CCoinsView::Cursor() const { return 0; }

CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
bool CCoinsViewBacked::GetCoin(const COutPoint &outpoint, Coin &coin) const { return base->GetCoin(outpoint, coin); }
bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { return base->HaveCoin(outpoint); }
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); }
size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); }

SaltedOutpointHasher::SaltedOutpointHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}

CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), cachedCoinsUsage(0) {}

size_t CCoinsViewCache::DynamicMemoryUsage() const {
    return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage;
}

CCoinsMap::iterator CCoinsViewCache::FetchCoin(const COutPoint &outpoint) const {
    CCoinsMap::iterator it = cacheCoins.find(outpoint);
    if (it != cacheCoins.end())
        return it;
    Coin tmp;
    if (!base->GetCoin(outpoint, tmp))
        return cacheCoins.end();
    CCoinsMap::iterator ret = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint), std::forward_as_tuple(std::move(tmp))).first;
    if (ret->second.coin.IsSpent()) {
        // The parent only has an empty entry for this outpoint; we can consider our
        // version as fresh.
        ret->second.flags = CCoinsCacheEntry::FRESH;
    }
    cachedCoinsUsage += ret->second.coin.DynamicMemoryUsage();
    return ret;
}

bool CCoinsViewCache::GetCoin(const COutPoint &outpoint, Coin &coin) const {
    CCoinsMap::const_iterator it = FetchCoin(outpoint);
    if (it != cacheCoins.end()) {
        coin = it->second.coin;
        return true;
    }
    return false;
}

void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possible_overwrite) {
    assert(!coin.IsSpent());
    if (coin.out.scriptPubKey.IsUnspendable()) return;
    CCoinsMap::iterator it;
    bool inserted;
    std::tie(it, inserted) = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint), std::tuple<>());
    bool fresh = false;
    if (!inserted) {
        cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
    }
    if (!possible_overwrite) {
        if (!it->second.coin.IsSpent()) {
            throw std::logic_error("Adding new coin that replaces non-pruned entry");
        }
        fresh = !(it->second.flags & CCoinsCacheEntry::DIRTY);
    }
    it->second.coin = std::move(coin);
    it->second.flags |= CCoinsCacheEntry::DIRTY | (fresh ? CCoinsCacheEntry::FRESH : 0);
    cachedCoinsUsage += it->second.coin.DynamicMemoryUsage();
}

void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight) {
    bool fCoinbase = tx.IsCoinBase();
    bool fCoinstake = tx.IsCoinStake();
    const uint256& txid = tx.GetHash();
    for (size_t i = 0; i < tx.vout.size(); ++i) {
        // Pass fCoinbase as the possible_overwrite flag to AddCoin, in order to correctly
        // deal with the pre-BIP30 occurrances of duplicate coinbase transactions.
        cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase, fCoinstake), fCoinbase || fCoinstake);
    }
}

void CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) {
    CCoinsMap::iterator it = FetchCoin(outpoint);
    if (it == cacheCoins.end()) return;
    cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
    if (moveout) {
        *moveout = std::move(it->second.coin);
    }
    if (it->second.flags & CCoinsCacheEntry::FRESH) {
        cacheCoins.erase(it);
    } else {
        it->second.flags |= CCoinsCacheEntry::DIRTY;
        it->second.coin.Clear();
    }
}

double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight, CAmount &inChainInputValue) const
{
    inChainInputValue = 0;
    if (tx.IsCoinBase() || tx.IsCoinStake())
        return 0.0;
    double dResult = 0.0;
    BOOST_FOREACH(const CTxIn& txin, tx.vin)
    {
        const Coin coin = AccessCoin(txin.prevout);
        assert(!coin.IsSpent());
        if (coin.IsSpent()) continue;
        if (coin.nHeight <= nHeight) {
            dResult += (double)(coin.out.nValue) * (nHeight-coin.nHeight);
            inChainInputValue += coin.out.nValue;
        }
    }
    return tx.ComputePriority(dResult);
}

static const Coin coinEmpty;

const Coin& CCoinsViewCache::AccessCoin(const COutPoint &outpoint) const {
    CCoinsMap::const_iterator it = FetchCoin(outpoint);
    if (it == cacheCoins.end()) {
        return coinEmpty;
    } else {
        return it->second.coin;
    }
}

bool CCoinsViewCache::HaveCoin(const COutPoint &outpoint) const {
    CCoinsMap::const_iterator it = FetchCoin(outpoint);
    return (it != cacheCoins.end() && !it->second.coin.IsSpent());
}

bool CCoinsViewCache::HaveCoinInCache(const COutPoint &outpoint) const {
    CCoinsMap::const_iterator it = cacheCoins.find(outpoint);
    return it != cacheCoins.end();
}

uint256 CCoinsViewCache::GetBestBlock() const {
    if (hashBlock.IsNull())
        hashBlock = base->GetBestBlock();
    return hashBlock;
}

void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
    hashBlock = hashBlockIn;
}

bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn) {
    for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
        if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
            CCoinsMap::iterator itUs = cacheCoins.find(it->first);
            if (itUs == cacheCoins.end()) {
                // The parent cache does not have an entry, while the child does
                // We can ignore it if it's both FRESH and pruned in the child
                if (!(it->second.flags & CCoinsCacheEntry::FRESH && it->second.coin.IsSpent())) {
                    // Otherwise we will need to create it in the parent
                    // and move the data up and mark it as dirty
                    CCoinsCacheEntry& entry = cacheCoins[it->first];
                    entry.coin = std::move(it->second.coin);
                    cachedCoinsUsage += entry.coin.DynamicMemoryUsage();
                    entry.flags = CCoinsCacheEntry::DIRTY;
                    // We can mark it FRESH in the parent if it was FRESH in the child
                    // Otherwise it might have just been flushed from the parent's cache
                    // and already exist in the grandparent
                    if (it->second.flags & CCoinsCacheEntry::FRESH)
                        entry.flags |= CCoinsCacheEntry::FRESH;
                }
            } else {
                // Assert that the child cache entry was not marked FRESH if the
                // parent cache entry has unspent outputs. If this ever happens,
                // it means the FRESH flag was misapplied and there is a logic
                // error in the calling code.
                if ((it->second.flags & CCoinsCacheEntry::FRESH) && !itUs->second.coin.IsSpent())
                    throw std::logic_error("FRESH flag misapplied to cache entry for base transaction with spendable outputs");

                // Found the entry in the parent cache
                if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coin.IsSpent()) {
                    // The grandparent does not have an entry, and the child is
                    // modified and being pruned. This means we can just delete
                    // it from the parent.
                    cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
                    cacheCoins.erase(itUs);
                } else {
                    // A normal modification.
                    cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
                    itUs->second.coin = std::move(it->second.coin);
                    cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();
                    itUs->second.flags |= CCoinsCacheEntry::DIRTY;
                    // NOTE: It is possible the child has a FRESH flag here in
                    // the event the entry we found in the parent is pruned. But
                    // we must not copy that FRESH flag to the parent as that
                    // pruned state likely still needs to be communicated to the
                    // grandparent.
                }
            }
        }
        CCoinsMap::iterator itOld = it++;
        mapCoins.erase(itOld);
    }
    hashBlock = hashBlockIn;
    return true;
}

bool CCoinsViewCache::Flush() {
    bool fOk = base->BatchWrite(cacheCoins, hashBlock);
    cacheCoins.clear();
    cachedCoinsUsage = 0;
    return fOk;
}

void CCoinsViewCache::Uncache(const COutPoint& hash)
{
    CCoinsMap::iterator it = cacheCoins.find(hash);
    if (it != cacheCoins.end() && it->second.flags == 0) {
        cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
        cacheCoins.erase(it);
    }
}

unsigned int CCoinsViewCache::GetCacheSize() const {
    return cacheCoins.size();
}

CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const
{
    if (tx.IsCoinBase())
        return 0;

    CAmount nResult = 0;
    for (unsigned int i = 0; i < tx.vin.size(); i++)
        nResult += AccessCoin(tx.vin[i].prevout).out.nValue;

    return nResult;
}

bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
{
    if (!tx.IsCoinBase()) {
        for (unsigned int i = 0; i < tx.vin.size(); i++) {
            if (!HaveCoin(tx.vin[i].prevout)) {
                return false;
            }
        }
    }
    return true;
}

static const size_t MAX_OUTPUTS_PER_BLOCK = dgpMaxBlockBaseSize /  ::GetSerializeSize(CTxOut(), SER_NETWORK, PROTOCOL_VERSION); // TODO: merge with similar definition in undo.h.

const Coin& AccessByTxid(const CCoinsViewCache& view, const uint256& txid)
{
    COutPoint iter(txid, 0);
    while (iter.n < MAX_OUTPUTS_PER_BLOCK) {
        const Coin& alternate = view.AccessCoin(iter);
        if (!alternate.IsSpent()) return alternate;
        ++iter.n;
    }
    return coinEmpty;
}
<commit_msg>Completely disallow overwrite<commit_after>// Copyright (c) 2012-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "coins.h"

#include "consensus/consensus.h"
#include "memusage.h"
#include "random.h"

#include <assert.h>

bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; }
bool CCoinsView::HaveCoin(const COutPoint &outpoint) const { return false; }
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; }
CCoinsViewCursor *CCoinsView::Cursor() const { return 0; }

CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
bool CCoinsViewBacked::GetCoin(const COutPoint &outpoint, Coin &coin) const { return base->GetCoin(outpoint, coin); }
bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { return base->HaveCoin(outpoint); }
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); }
size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); }

SaltedOutpointHasher::SaltedOutpointHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}

CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), cachedCoinsUsage(0) {}

size_t CCoinsViewCache::DynamicMemoryUsage() const {
    return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage;
}

CCoinsMap::iterator CCoinsViewCache::FetchCoin(const COutPoint &outpoint) const {
    CCoinsMap::iterator it = cacheCoins.find(outpoint);
    if (it != cacheCoins.end())
        return it;
    Coin tmp;
    if (!base->GetCoin(outpoint, tmp))
        return cacheCoins.end();
    CCoinsMap::iterator ret = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint), std::forward_as_tuple(std::move(tmp))).first;
    if (ret->second.coin.IsSpent()) {
        // The parent only has an empty entry for this outpoint; we can consider our
        // version as fresh.
        ret->second.flags = CCoinsCacheEntry::FRESH;
    }
    cachedCoinsUsage += ret->second.coin.DynamicMemoryUsage();
    return ret;
}

bool CCoinsViewCache::GetCoin(const COutPoint &outpoint, Coin &coin) const {
    CCoinsMap::const_iterator it = FetchCoin(outpoint);
    if (it != cacheCoins.end()) {
        coin = it->second.coin;
        return true;
    }
    return false;
}

void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possible_overwrite) {
    assert(!coin.IsSpent());
    if (coin.out.scriptPubKey.IsUnspendable()) return;
    CCoinsMap::iterator it;
    bool inserted;
    std::tie(it, inserted) = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint), std::tuple<>());
    bool fresh = false;
    if (!inserted) {
        cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
    }
    if (!possible_overwrite) {
        if (!it->second.coin.IsSpent()) {
            throw std::logic_error("Adding new coin that replaces non-pruned entry");
        }
        fresh = !(it->second.flags & CCoinsCacheEntry::DIRTY);
    }
    it->second.coin = std::move(coin);
    it->second.flags |= CCoinsCacheEntry::DIRTY | (fresh ? CCoinsCacheEntry::FRESH : 0);
    cachedCoinsUsage += it->second.coin.DynamicMemoryUsage();
}

void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight) {
    bool fCoinbase = tx.IsCoinBase();
    bool fCoinstake = tx.IsCoinStake();
    const uint256& txid = tx.GetHash();
    for (size_t i = 0; i < tx.vout.size(); ++i) {
        // Pass fCoinbase as the possible_overwrite flag to AddCoin, in order to correctly
        // deal with the pre-BIP30 occurrances of duplicate coinbase transactions.
        cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase, fCoinstake), false);
    }
}

void CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) {
    CCoinsMap::iterator it = FetchCoin(outpoint);
    if (it == cacheCoins.end()) return;
    cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
    if (moveout) {
        *moveout = std::move(it->second.coin);
    }
    if (it->second.flags & CCoinsCacheEntry::FRESH) {
        cacheCoins.erase(it);
    } else {
        it->second.flags |= CCoinsCacheEntry::DIRTY;
        it->second.coin.Clear();
    }
}

double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight, CAmount &inChainInputValue) const
{
    inChainInputValue = 0;
    if (tx.IsCoinBase() || tx.IsCoinStake())
        return 0.0;
    double dResult = 0.0;
    BOOST_FOREACH(const CTxIn& txin, tx.vin)
    {
        const Coin coin = AccessCoin(txin.prevout);
        assert(!coin.IsSpent());
        if (coin.IsSpent()) continue;
        if (coin.nHeight <= nHeight) {
            dResult += (double)(coin.out.nValue) * (nHeight-coin.nHeight);
            inChainInputValue += coin.out.nValue;
        }
    }
    return tx.ComputePriority(dResult);
}

static const Coin coinEmpty;

const Coin& CCoinsViewCache::AccessCoin(const COutPoint &outpoint) const {
    CCoinsMap::const_iterator it = FetchCoin(outpoint);
    if (it == cacheCoins.end()) {
        return coinEmpty;
    } else {
        return it->second.coin;
    }
}

bool CCoinsViewCache::HaveCoin(const COutPoint &outpoint) const {
    CCoinsMap::const_iterator it = FetchCoin(outpoint);
    return (it != cacheCoins.end() && !it->second.coin.IsSpent());
}

bool CCoinsViewCache::HaveCoinInCache(const COutPoint &outpoint) const {
    CCoinsMap::const_iterator it = cacheCoins.find(outpoint);
    return it != cacheCoins.end();
}

uint256 CCoinsViewCache::GetBestBlock() const {
    if (hashBlock.IsNull())
        hashBlock = base->GetBestBlock();
    return hashBlock;
}

void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
    hashBlock = hashBlockIn;
}

bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn) {
    for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
        if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
            CCoinsMap::iterator itUs = cacheCoins.find(it->first);
            if (itUs == cacheCoins.end()) {
                // The parent cache does not have an entry, while the child does
                // We can ignore it if it's both FRESH and pruned in the child
                if (!(it->second.flags & CCoinsCacheEntry::FRESH && it->second.coin.IsSpent())) {
                    // Otherwise we will need to create it in the parent
                    // and move the data up and mark it as dirty
                    CCoinsCacheEntry& entry = cacheCoins[it->first];
                    entry.coin = std::move(it->second.coin);
                    cachedCoinsUsage += entry.coin.DynamicMemoryUsage();
                    entry.flags = CCoinsCacheEntry::DIRTY;
                    // We can mark it FRESH in the parent if it was FRESH in the child
                    // Otherwise it might have just been flushed from the parent's cache
                    // and already exist in the grandparent
                    if (it->second.flags & CCoinsCacheEntry::FRESH)
                        entry.flags |= CCoinsCacheEntry::FRESH;
                }
            } else {
                // Assert that the child cache entry was not marked FRESH if the
                // parent cache entry has unspent outputs. If this ever happens,
                // it means the FRESH flag was misapplied and there is a logic
                // error in the calling code.
                if ((it->second.flags & CCoinsCacheEntry::FRESH) && !itUs->second.coin.IsSpent())
                    throw std::logic_error("FRESH flag misapplied to cache entry for base transaction with spendable outputs");

                // Found the entry in the parent cache
                if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coin.IsSpent()) {
                    // The grandparent does not have an entry, and the child is
                    // modified and being pruned. This means we can just delete
                    // it from the parent.
                    cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
                    cacheCoins.erase(itUs);
                } else {
                    // A normal modification.
                    cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
                    itUs->second.coin = std::move(it->second.coin);
                    cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();
                    itUs->second.flags |= CCoinsCacheEntry::DIRTY;
                    // NOTE: It is possible the child has a FRESH flag here in
                    // the event the entry we found in the parent is pruned. But
                    // we must not copy that FRESH flag to the parent as that
                    // pruned state likely still needs to be communicated to the
                    // grandparent.
                }
            }
        }
        CCoinsMap::iterator itOld = it++;
        mapCoins.erase(itOld);
    }
    hashBlock = hashBlockIn;
    return true;
}

bool CCoinsViewCache::Flush() {
    bool fOk = base->BatchWrite(cacheCoins, hashBlock);
    cacheCoins.clear();
    cachedCoinsUsage = 0;
    return fOk;
}

void CCoinsViewCache::Uncache(const COutPoint& hash)
{
    CCoinsMap::iterator it = cacheCoins.find(hash);
    if (it != cacheCoins.end() && it->second.flags == 0) {
        cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
        cacheCoins.erase(it);
    }
}

unsigned int CCoinsViewCache::GetCacheSize() const {
    return cacheCoins.size();
}

CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const
{
    if (tx.IsCoinBase())
        return 0;

    CAmount nResult = 0;
    for (unsigned int i = 0; i < tx.vin.size(); i++)
        nResult += AccessCoin(tx.vin[i].prevout).out.nValue;

    return nResult;
}

bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
{
    if (!tx.IsCoinBase()) {
        for (unsigned int i = 0; i < tx.vin.size(); i++) {
            if (!HaveCoin(tx.vin[i].prevout)) {
                return false;
            }
        }
    }
    return true;
}

static const size_t MAX_OUTPUTS_PER_BLOCK = dgpMaxBlockBaseSize /  ::GetSerializeSize(CTxOut(), SER_NETWORK, PROTOCOL_VERSION); // TODO: merge with similar definition in undo.h.

const Coin& AccessByTxid(const CCoinsViewCache& view, const uint256& txid)
{
    COutPoint iter(txid, 0);
    while (iter.n < MAX_OUTPUTS_PER_BLOCK) {
        const Coin& alternate = view.AccessCoin(iter);
        if (!alternate.IsSpent()) return alternate;
        ++iter.n;
    }
    return coinEmpty;
}
<|endoftext|>


<commit_before>#include "acmacs-base/debug.hh"

#include "time-series-draw.hh"
#include "tree.hh"
#include "tree-draw.hh"
#include "coloring.hh"

// ----------------------------------------------------------------------

void TimeSeriesDraw::prepare()
{
    std::map<date::year_month_day, size_t> sequences_per_month;
    mTree.sequences_per_month(sequences_per_month);
    if (!sequences_per_month.empty()) {
        std::cout << "INFO: Sequences per month:" << '\n';
        for (const auto& e: sequences_per_month) {
            std::cout << "  " << e.first << " " << e.second << '\n';
        }

        const auto today = date::today();
        auto end_of_ts = today;
        if (mSettings.end.empty()) {
            for (auto ms = sequences_per_month.crbegin(); ms != sequences_per_month.crend(); ++ms) {
                if (ms->second && ms->first <= today) {
                    end_of_ts = ms->first;
                    mSettings.end = date::display(date::end_of_month(ms->first));
                    break;
                }
            }
        }

        if (mSettings.begin.empty()) {
            for (const auto& entry : sequences_per_month) {
                if (entry.second && months_between_dates(entry.first, end_of_ts) < 25) {
                    mSettings.begin = date::display(entry.first);
                    break;
                }
            }
        }

        mNumberOfMonths = static_cast<size_t>(calendar_months_between_dates_inclusive(date::from_string(*mSettings.begin), date::from_string(*mSettings.end)));
        std::cout << "INFO: dates to show: " << mSettings.begin << " .. " << mSettings.end << "  months: " << mNumberOfMonths << DEBUG_LINE_FUNC << '\n';
    }
    else {
        std::cout << "WARNING: no dates found for sequences" << DEBUG_LINE_FUNC << '\n';
        mNumberOfMonths = 0;
    }

} // TimeSeriesDraw::prepare

// ----------------------------------------------------------------------

std::vector<std::string> TimeSeriesDraw::all_months() const
{
    std::vector<std::string> result;
    auto current_month{date::from_string(*mSettings.begin)};
    for (size_t month_no = 0; month_no < mNumberOfMonths; ++month_no, date::increment_month(current_month))
        result.push_back(date::year4_month2(current_month));
    return result;

} // TimeSeriesDraw::all_months

// ----------------------------------------------------------------------

void TimeSeriesDraw::init_settings(const SettingsInitializer& /*settings_initilizer*/)
{

} // TimeSeriesDraw::init_settings

// ----------------------------------------------------------------------

void TimeSeriesDraw::draw()
{
    if (mNumberOfMonths) {
          // mSurface.border("green3", 1);
        const double month_width = mSurface.viewport().size.width / mNumberOfMonths;
        draw_labels(month_width);
        draw_month_separators(month_width);
        draw_dashes(month_width);
        draw_hz_section_lines();
    }

} // TimeSeriesDraw::draw

// ----------------------------------------------------------------------

void TimeSeriesDraw::draw_labels(double month_width)
{
    const acmacs::Size& surface_size = mSurface.viewport().size;
    const double month_max_height = mSurface.text_size("May ", Pixels{mSettings.label_size}, mSettings.label_style).width;
    double x_bearing;
    const auto big_label_size = mSurface.text_size("May 99", Pixels{mSettings.label_size}, mSettings.label_style, &x_bearing);
    const auto text_up = (month_width - big_label_size.height) * 0.5;
    const double month_year_to_timeseries_gap = mSurface.convert(Pixels{mSettings.month_year_to_timeseries_gap}).value();

    draw_labels_at_side({text_up, - big_label_size.width - x_bearing - 2 /*month_year_to_timeseries_gap*/}, month_width, month_max_height);
    draw_labels_at_side({text_up, surface_size.height + x_bearing + month_year_to_timeseries_gap}, month_width, month_max_height);

} // TimeSeriesDraw::draw_labels

// ----------------------------------------------------------------------

void TimeSeriesDraw::draw_labels_at_side(const acmacs::PointCoordinates& aOrigin, double month_width, double month_max_height)
{
    try {
        auto current_month{date::from_string(*mSettings.begin)};
        for (size_t month_no = 0; month_no < mNumberOfMonths; ++month_no, date::increment_month(current_month)) {
            const double left = aOrigin.x() + month_no * month_width;
            mSurface.text({left, aOrigin.y()}, date::month_3(current_month), 0, Pixels{mSettings.label_size}, mSettings.label_style, Rotation{M_PI_2});
            mSurface.text({left, aOrigin.y() + month_max_height}, date::year_2(current_month), 0, Pixels{mSettings.label_size}, mSettings.label_style, Rotation{M_PI_2});
        }
    }
    catch (std::exception& err) {
        std::cerr << "WARNING: " << err.what() << " (TimeSeriesDraw::draw_labels_at_side)\n";
    }

} // TimeSeriesDraw::draw_labels_at_side

// ----------------------------------------------------------------------

// for tracked antigens in antigenic maps colored by date
void TimeSeriesDraw::draw_color_scale(const std::map<std::string, Color, std::less<>>& aTrackedAntigenColorByMonth)
{
    const acmacs::Size& surface_size = mSurface.viewport().size;
    const double month_width = mSurface.viewport().size.width / mNumberOfMonths;
    const auto top = surface_size.height;
    auto current_month{date::from_string(*mSettings.begin)};
    for (size_t month_no = 0; month_no < mNumberOfMonths; ++month_no, date::increment_month(current_month)) {
        const auto left = month_no * month_width;
        mSurface.rectangle_filled({left, top}, acmacs::Size{10, 10}, PINK, Pixels{0}, aTrackedAntigenColorByMonth.find(date::year4_month2(current_month))->second);
    }

} // TimeSeriesDraw::draw_color_scale

// ----------------------------------------------------------------------

void TimeSeriesDraw::draw_month_separators(double month_width)
{
    const double bottom = mSurface.viewport().size.height;
    for (size_t month_no = 0; month_no <= mNumberOfMonths; ++month_no) {
        const double left = month_no * month_width;
        mSurface.line({left, 0}, {left, bottom}, mSettings.month_separator_color, Pixels{mSettings.month_separator_width});
    }

} // TimeSeriesDraw::draw_month_separators

// ----------------------------------------------------------------------

void TimeSeriesDraw::draw_dashes(double month_width)
{
    const double base_x = month_width * (1.0 - mSettings.dash_width) / 2;
    const Coloring& coloring = mTreeDraw.coloring();

    const auto begin{date::from_string(*mSettings.begin)}, end{date::from_string(*mSettings.end)};
    auto draw_dash = [&](const Node& aNode) {
        if (aNode.draw.shown && !aNode.data.date().empty()) {
            if (const auto node_date_s = aNode.data.date(); node_date_s.size() > 3 && node_date_s.substr(node_date_s.size() - 3) != "-00") { // ignore incomplete dates
                try {
                    if (const auto node_date{date::from_string(node_date_s)}; node_date >= begin && node_date <= end) {
                        const int month_no = date::months_between_dates(begin, node_date);
                        const acmacs::PointCoordinates a(base_x + month_width * month_no, aNode.draw.vertical_pos);
                        mSurface.line(a, {a.x() + month_width * mSettings.dash_width, a.y()}, coloring.color(aNode), Pixels{mSettings.dash_line_width}, acmacs::surface::LineCap::Round);
                    }
                }
                catch (std::exception& err) {
                    std::cerr << "WARNING: " << err.what() << " (TimeSeriesDraw::draw_dashes) Date: " << aNode.data.date() << "\n";
                }
            }
        }
    };

    try {
        tree::iterate_leaf(mTree, draw_dash);
    }
    catch (std::exception& err) {
        std::cerr << "WARNING: " << err.what() << " (TimeSeriesDraw::draw_dashes)\n";
    }

} // TimeSeriesDraw::draw_dashes

// ----------------------------------------------------------------------

void TimeSeriesDraw::draw_hz_section_lines()
{
    double previous_vertical_pos = -1e-8;
    auto draw = [&](const Node& aNode) {
        if (aNode.draw.shown) {
            if (aNode.draw.hz_section_index != NodeDrawData::HzSectionNoIndex) {
                const auto section_settings = mHzSections.sections[aNode.draw.hz_section_index];
                double y = aNode.draw.vertical_pos;
                if (section_settings->show_line) {
                    y = (previous_vertical_pos + aNode.draw.vertical_pos) / 2;
                    mSurface.line({0, y}, {mSurface.viewport().size.width, y}, mHzSections.line_color, Pixels{mHzSections.line_width});
                }
                if ((!mTreeMode || mHzSections.show_labels_in_time_series_in_tree_mode) && section_settings->show_label_in_time_series) {
                    draw_hz_section_label(aNode.draw.hz_section_index, y);
                }
            }
            previous_vertical_pos = aNode.draw.vertical_pos;
        }
    };
    tree::iterate_leaf(mTree, draw);

} // TimeSeriesDraw::draw_hz_section_lines

// ----------------------------------------------------------------------

void TimeSeriesDraw::draw_hz_section_label(size_t aSectionIndex, double aY)
{
    const auto section_settings = mHzSections.sections[aSectionIndex];
    if (section_settings->show && section_settings->show_map) {
        std::string label = mHzSections.node_refs[aSectionIndex].index; // (1, 'A' + static_cast<char>(aSectionNo));
        const acmacs::Size tsize = mSurface.text_size(label, Pixels{mHzSections.ts_label_size}, mHzSections.ts_label_style);
        mSurface.text({mSurface.viewport().size.width - tsize.width * 1.2, aY + tsize.height * 1.2}, label, mHzSections.ts_label_color, Pixels{mHzSections.ts_label_size}, mHzSections.ts_label_style);
    }

} // TimeSeriesDraw::draw_hz_section_label

// ----------------------------------------------------------------------

// void TimeSeriesDraw::hide_hz_section_labels_in_time_series()
// {
//     for (auto& section: mHzSections.sections)
//         section.show_label_in_time_series = false;

// } // TimeSeriesDraw::hide_hz_section_labels_in_time_series

// ----------------------------------------------------------------------

void TimeSeriesDrawSettings::remove_for_tree_settings()
{
    label_style.remove();

} // TimeSeriesDrawSettings::remove_for_tree_settings

// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>minor fix<commit_after>#include "acmacs-base/debug.hh"

#include "time-series-draw.hh"
#include "tree.hh"
#include "tree-draw.hh"
#include "coloring.hh"

// ----------------------------------------------------------------------

void TimeSeriesDraw::prepare()
{
    std::map<date::year_month_day, size_t> sequences_per_month;
    mTree.sequences_per_month(sequences_per_month);
    if (!sequences_per_month.empty()) {
        std::cout << "INFO: Sequences per month:" << '\n';
        for (const auto& e: sequences_per_month) {
            std::cout << "  " << e.first << " " << e.second << '\n';
        }

        const auto today = date::today();
        auto end_of_ts = today;
        if (mSettings.end.empty()) {
            for (auto ms = sequences_per_month.crbegin(); ms != sequences_per_month.crend(); ++ms) {
                if (ms->second && ms->first <= today) {
                    end_of_ts = ms->first;
                    mSettings.end = date::display(date::end_of_month(ms->first));
                    break;
                }
            }
        }

        if (mSettings.begin.empty()) {
            for (const auto& entry : sequences_per_month) {
                if (entry.second && months_between_dates(entry.first, end_of_ts) < 25) {
                    mSettings.begin = date::display(entry.first);
                    break;
                }
            }
        }

        mNumberOfMonths = static_cast<size_t>(calendar_months_between_dates_inclusive(date::from_string(*mSettings.begin), date::from_string(*mSettings.end)));
        std::cout << "INFO: dates to show: " << mSettings.begin << " .. " << mSettings.end << "  months: " << mNumberOfMonths << DEBUG_LINE_FUNC << '\n';
    }
    else {
        std::cout << "WARNING: no dates found for sequences" << DEBUG_LINE_FUNC << '\n';
        mNumberOfMonths = 0;
    }

} // TimeSeriesDraw::prepare

// ----------------------------------------------------------------------

std::vector<std::string> TimeSeriesDraw::all_months() const
{
    std::vector<std::string> result;
    auto current_month{date::from_string(*mSettings.begin)};
    for (size_t month_no = 0; month_no < mNumberOfMonths; ++month_no, date::increment_month(current_month))
        result.push_back(date::year4_month2(current_month));
    return result;

} // TimeSeriesDraw::all_months

// ----------------------------------------------------------------------

void TimeSeriesDraw::init_settings(const SettingsInitializer& /*settings_initilizer*/)
{

} // TimeSeriesDraw::init_settings

// ----------------------------------------------------------------------

void TimeSeriesDraw::draw()
{
    if (mNumberOfMonths) {
          // mSurface.border("green3", 1);
        const double month_width = mSurface.viewport().size.width / mNumberOfMonths;
        draw_labels(month_width);
        draw_month_separators(month_width);
        draw_dashes(month_width);
        draw_hz_section_lines();
    }

} // TimeSeriesDraw::draw

// ----------------------------------------------------------------------

void TimeSeriesDraw::draw_labels(double month_width)
{
    const acmacs::Size& surface_size = mSurface.viewport().size;
    const double month_max_height = mSurface.text_size("May ", Pixels{mSettings.label_size}, mSettings.label_style).width;
    double x_bearing;
    const auto big_label_size = mSurface.text_size("May 99", Pixels{mSettings.label_size}, mSettings.label_style, &x_bearing);
    const auto text_up = (month_width - big_label_size.height) * 0.5;
    const double month_year_to_timeseries_gap = mSurface.convert(Pixels{mSettings.month_year_to_timeseries_gap}).value();

    draw_labels_at_side({text_up, - big_label_size.width - x_bearing - 2 /*month_year_to_timeseries_gap*/}, month_width, month_max_height);
    draw_labels_at_side({text_up, surface_size.height + x_bearing + month_year_to_timeseries_gap}, month_width, month_max_height);

} // TimeSeriesDraw::draw_labels

// ----------------------------------------------------------------------

void TimeSeriesDraw::draw_labels_at_side(const acmacs::PointCoordinates& aOrigin, double month_width, double month_max_height)
{
    try {
        auto current_month{date::from_string(*mSettings.begin)};
        for (size_t month_no = 0; month_no < mNumberOfMonths; ++month_no, date::increment_month(current_month)) {
            const double left = aOrigin.x() + month_no * month_width;
            mSurface.text({left, aOrigin.y()}, date::month_3(current_month), 0, Pixels{mSettings.label_size}, mSettings.label_style, Rotation{M_PI_2});
            mSurface.text({left, aOrigin.y() + month_max_height}, date::year_2(current_month), 0, Pixels{mSettings.label_size}, mSettings.label_style, Rotation{M_PI_2});
        }
    }
    catch (std::exception& err) {
        std::cerr << "WARNING: " << err.what() << " (TimeSeriesDraw::draw_labels_at_side)\n";
    }

} // TimeSeriesDraw::draw_labels_at_side

// ----------------------------------------------------------------------

// for tracked antigens in antigenic maps colored by date
void TimeSeriesDraw::draw_color_scale(const std::map<std::string, Color, std::less<>>& aTrackedAntigenColorByMonth)
{
    const acmacs::Size& surface_size = mSurface.viewport().size;
    const double month_width = mSurface.viewport().size.width / mNumberOfMonths;
    const auto top = surface_size.height;
    auto current_month{date::from_string(*mSettings.begin)};
    for (size_t month_no = 0; month_no < mNumberOfMonths; ++month_no, date::increment_month(current_month)) {
        const auto left = month_no * month_width;
        mSurface.rectangle_filled({left, top}, acmacs::Size{10, 10}, PINK, Pixels{0}, aTrackedAntigenColorByMonth.find(date::year4_month2(current_month))->second);
    }

} // TimeSeriesDraw::draw_color_scale

// ----------------------------------------------------------------------

void TimeSeriesDraw::draw_month_separators(double month_width)
{
    const double bottom = mSurface.viewport().size.height;
    for (size_t month_no = 0; month_no <= mNumberOfMonths; ++month_no) {
        const double left = month_no * month_width;
        mSurface.line({left, 0}, {left, bottom}, mSettings.month_separator_color, Pixels{mSettings.month_separator_width});
    }

} // TimeSeriesDraw::draw_month_separators

// ----------------------------------------------------------------------

void TimeSeriesDraw::draw_dashes(double month_width)
{
    const double base_x = month_width * (1.0 - mSettings.dash_width) / 2;
    const Coloring& coloring = mTreeDraw.coloring();

    const auto begin{date::from_string(*mSettings.begin)}, end{date::from_string(*mSettings.end)};
    auto draw_dash = [&](const Node& aNode) {
        if (aNode.draw.shown && !aNode.data.date().empty()) {
            if (const auto node_date_s = aNode.data.date(); node_date_s.size() > 3 && node_date_s.substr(node_date_s.size() - 3) != "-00") { // ignore incomplete dates
                try {
                    if (const auto node_date{date::from_string(node_date_s)}; node_date >= begin && node_date <= end) {
                        const int month_no = date::months_between_dates(begin, node_date);
                        const acmacs::PointCoordinates a(base_x + month_width * month_no, aNode.draw.vertical_pos);
                        mSurface.line(a, {a.x() + month_width * mSettings.dash_width, a.y()}, coloring.color(aNode), Pixels{mSettings.dash_line_width}, acmacs::surface::LineCap::Round);
                    }
                }
                catch (std::exception& err) {
                    if (aNode.data.date().size() > 4)
                        std::cerr << "WARNING: " << err.what() << " (TimeSeriesDraw::draw_dashes) Date: " << aNode.data.date() << "\n";
                }
            }
        }
    };

    try {
        tree::iterate_leaf(mTree, draw_dash);
    }
    catch (std::exception& err) {
        std::cerr << "WARNING: " << err.what() << " (TimeSeriesDraw::draw_dashes)\n";
    }

} // TimeSeriesDraw::draw_dashes

// ----------------------------------------------------------------------

void TimeSeriesDraw::draw_hz_section_lines()
{
    double previous_vertical_pos = -1e-8;
    auto draw = [&](const Node& aNode) {
        if (aNode.draw.shown) {
            if (aNode.draw.hz_section_index != NodeDrawData::HzSectionNoIndex) {
                const auto section_settings = mHzSections.sections[aNode.draw.hz_section_index];
                double y = aNode.draw.vertical_pos;
                if (section_settings->show_line) {
                    y = (previous_vertical_pos + aNode.draw.vertical_pos) / 2;
                    mSurface.line({0, y}, {mSurface.viewport().size.width, y}, mHzSections.line_color, Pixels{mHzSections.line_width});
                }
                if ((!mTreeMode || mHzSections.show_labels_in_time_series_in_tree_mode) && section_settings->show_label_in_time_series) {
                    draw_hz_section_label(aNode.draw.hz_section_index, y);
                }
            }
            previous_vertical_pos = aNode.draw.vertical_pos;
        }
    };
    tree::iterate_leaf(mTree, draw);

} // TimeSeriesDraw::draw_hz_section_lines

// ----------------------------------------------------------------------

void TimeSeriesDraw::draw_hz_section_label(size_t aSectionIndex, double aY)
{
    const auto section_settings = mHzSections.sections[aSectionIndex];
    if (section_settings->show && section_settings->show_map) {
        std::string label = mHzSections.node_refs[aSectionIndex].index; // (1, 'A' + static_cast<char>(aSectionNo));
        const acmacs::Size tsize = mSurface.text_size(label, Pixels{mHzSections.ts_label_size}, mHzSections.ts_label_style);
        mSurface.text({mSurface.viewport().size.width - tsize.width * 1.2, aY + tsize.height * 1.2}, label, mHzSections.ts_label_color, Pixels{mHzSections.ts_label_size}, mHzSections.ts_label_style);
    }

} // TimeSeriesDraw::draw_hz_section_label

// ----------------------------------------------------------------------

// void TimeSeriesDraw::hide_hz_section_labels_in_time_series()
// {
//     for (auto& section: mHzSections.sections)
//         section.show_label_in_time_series = false;

// } // TimeSeriesDraw::hide_hz_section_labels_in_time_series

// ----------------------------------------------------------------------

void TimeSeriesDrawSettings::remove_for_tree_settings()
{
    label_style.remove();

} // TimeSeriesDrawSettings::remove_for_tree_settings

// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>#include "task_charset.h"
#include "cortex/class.h"
#include "math/gauss.hpp"
#include "math/clamp.hpp"
#include "math/random.hpp"
#include "tensor/numeric.hpp"
#include "text/to_string.hpp"
#include "text/to_params.hpp"
#include "text/from_params.hpp"
#include "tensor/algorithm.hpp"
#include "vision/warp.h"
#include "vision/image_io.h"
#include "vision/convolve.hpp"
#include "vision/bilinear.hpp"

#include "synth_bitstream_vera_sans_mono_bold.h"
#include "synth_bitstream_vera_sans_mono.h"
#include "synth_dejavu_sans_mono_bold.h"
#include "synth_dejavu_sans_mono.h"
#include "synth_droid_sans_mono.h"
#include "synth_liberation_mono_bold.h"
#include "synth_liberation_mono.h"
#include "synth_nimbus_mono_bold.h"
#include "synth_nimbus_mono.h"
#include "synth_oxygen_mono.h"

namespace nano
{
        static rgba_t make_random_rgba()
        {
                random_t<luma_t> rng(0, 255);
                return rgba_t{rng(), rng(), rng(), rng()};
        }

        static rgba_t make_random_opposite_rgba(const rgba_t& rgba)
        {
                random_t<int> rng(-55, +55);
                return  rgba_t
                {
                        static_cast<luma_t>(clamp(255 - static_cast<int>(rgba(0)) + rng(), 0, 255)),
                        static_cast<luma_t>(clamp(255 - static_cast<int>(rgba(1)) + rng(), 0, 255)),
                        static_cast<luma_t>(clamp(255 - static_cast<int>(rgba(2)) + rng(), 0, 255)),
                        static_cast<luma_t>(clamp(255 - static_cast<int>(rgba(3)) + rng(), 0, 255))
                };
        }

        template <>
        inline std::map<nano::charset, std::string> enum_string<nano::charset>()
        {
                return
                {
                        { nano::charset::digit,         "digit" },
                        { nano::charset::lalpha,        "lalpha" },
                        { nano::charset::ualpha,        "ualpha" },
                        { nano::charset::alpha,         "alpha" },
                        { nano::charset::alphanum,      "alphanum" }
                };
        }

        static tensor_size_t obegin(const charset cs)
        {
                switch (cs)
                {
                case charset::digit:            return 0;
                case charset::lalpha:           return 0 + 10;
                case charset::ualpha:           return 0 + 10 + 26;
                case charset::alpha:            return 10;
                case charset::alphanum:         return 0;
                default:                        assert(false); return 0;
                }
        }

        static tensor_size_t oend(const charset cs)
        {
                switch (cs)
                {
                case charset::digit:            return 10;
                case charset::lalpha:           return 10 + 26;
                case charset::ualpha:           return 10 + 26 + 26;
                case charset::alpha:            return 10 + 26 + 26;
                case charset::alphanum:         return 10 + 26 + 26;
                default:                        assert(false); return 0;
                }
        }

        static tensor_size_t osize(const charset cs)
        {
                return oend(cs) - obegin(cs);
        }

        template
        <
                typename tindex,
                typename tsize
        >
        tensor3d_t get_object_patch(const image_tensor_t& image,
                const tindex object_index, const tsize objects, const scalar_t max_offset)
        {
                nano::random_t<scalar_t> rng(-max_offset, max_offset);

                const auto icols = static_cast<int>(image.cols());
                const auto irows = static_cast<int>(image.rows());

                const auto dx = static_cast<scalar_t>(icols) / static_cast<scalar_t>(objects);

                const auto x = dx * static_cast<scalar_t>(object_index) + rng();

                const auto ppx = nano::clamp(nano::cast<int>(x), 0, icols - 1);
                const auto ppw = nano::clamp(nano::cast<int>(dx + rng()), 0, icols - ppx);

                const auto ppy = nano::clamp(nano::cast<int>(rng()), 0, irows - 1);
                const auto pph = nano::clamp(nano::cast<int>(static_cast<scalar_t>(irows) + rng()), 0, irows - ppy);

                tensor3d_t ret(4, pph, ppw);
                ret.matrix(0) = image.matrix(0).block(ppy, ppx, pph, ppw).cast<scalar_t>() / 255;
                ret.matrix(1) = image.matrix(1).block(ppy, ppx, pph, ppw).cast<scalar_t>() / 255;
                ret.matrix(2) = image.matrix(2).block(ppy, ppx, pph, ppw).cast<scalar_t>() / 255;
                ret.matrix(3) = image.matrix(3).block(ppy, ppx, pph, ppw).cast<scalar_t>() / 255;

                return ret;
        }

        tensor3d_t make_random_rgba_image(const tensor_size_t rows, const tensor_size_t cols,
                const rgba_t back_color,
                const scalar_t max_noise, const scalar_t sigma)
        {
                // noisy background
                tensor3d_t image(4, rows, cols);
                image.matrix(0).setConstant(back_color(0) / 255);
                image.matrix(1).setConstant(back_color(1) / 255);
                image.matrix(2).setConstant(back_color(2) / 255);
                image.matrix(3).setConstant(1);

                tensor::add_random(nano::make_rng<scalar_t>(-max_noise, +max_noise),
                        image.matrix(0), image.matrix(1), image.matrix(2));

                // smooth background
                const nano::gauss_kernel_t<scalar_t> back_gauss(sigma);
                nano::convolve(back_gauss, image.matrix(0));
                nano::convolve(back_gauss, image.matrix(1));
                nano::convolve(back_gauss, image.matrix(2));

                return image;
        }

        tensor3d_t alpha_blend(const tensor3d_t& mask, const tensor3d_t& img1, const tensor3d_t& img2)
        {
                const auto op = [] (const auto a, const auto v1, const auto v2)
                {
                        return (1 - a) * v1 + a * v2;
                };

                tensor3d_t imgb(4, mask.rows(), mask.cols());
                tensor::transform(mask.matrix(3), img1.matrix(0), img2.matrix(0), imgb.matrix(0), op);
                tensor::transform(mask.matrix(3), img1.matrix(1), img2.matrix(1), imgb.matrix(1), op);
                tensor::transform(mask.matrix(3), img1.matrix(2), img2.matrix(2), imgb.matrix(2), op);
                imgb.matrix(3).setConstant(1);

                return imgb;
        }

        charset_task_t::charset_task_t(const string_t& configuration) : mem_vision_task_t(
                "charset",
                nano::from_params<color_mode>(configuration, "color", color_mode::rgb),
                nano::clamp(nano::from_params<tensor_size_t>(configuration, "irows", 32), 12, 128),
                nano::clamp(nano::from_params<tensor_size_t>(configuration, "icols", 32), 12, 128),
                nano::osize(nano::from_params<charset>(configuration, "type", charset::digit)),
                1),
                m_charset(nano::from_params<charset>(configuration, "type", charset::digit)),
                m_color(nano::from_params<color_mode>(configuration, "color", color_mode::rgb)),
                m_count(nano::clamp(nano::from_params<size_t>(configuration, "count", 1000), 100, 1024 * 1024))
        {
        }

        charset_task_t::charset_task_t(const charset type, const color_mode mode,
                const tensor_size_t irows, const tensor_size_t icols, const size_t count) :
                charset_task_t(to_params("color", mode, "irows", irows, "icols", icols, "type", type, "count", count))
        {
        }

        bool charset_task_t::populate()
        {
                const string_t characters =
                        "0123456789" \
                        "abcdefghijklmnopqrstuvwxyz" \
                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

                const size_t n_chars = characters.size();

                std::vector<image_tensor_t> char_patches;
                image_tensor_t char_patch;
#define INSERT_IMAGE(name) \
                if (!nano::load_rgba_image(get_ ##name ##_name(), get_ ##name ##_data(), get_ ##name ##_size(), char_patch)) \
                { \
                        return false; \
                } \
                char_patches.push_back(char_patch);
                INSERT_IMAGE(synth_bitstream_vera_sans_mono_bold)
                INSERT_IMAGE(synth_bitstream_vera_sans_mono)
                INSERT_IMAGE(synth_dejavu_sans_mono_bold)
                INSERT_IMAGE(synth_dejavu_sans_mono)
                INSERT_IMAGE(synth_droid_sans_mono)
                INSERT_IMAGE(synth_liberation_mono_bold)
                INSERT_IMAGE(synth_liberation_mono)
                INSERT_IMAGE(synth_nimbus_mono_bold)
                INSERT_IMAGE(synth_nimbus_mono)
                INSERT_IMAGE(synth_oxygen_mono)
#undef INSERT_IMAGE

                const size_t n_fonts = char_patches.size();

                nano::random_t<tensor_size_t> rng_output(obegin(m_charset), oend(m_charset) - 1);
                nano::random_t<size_t> rng_font(1, n_fonts);
                nano::random_t<scalar_t> rng_gauss(0, 2);

                // generate samples
                for (size_t i = 0; i < m_count; ++ i)
                {
                        // random target: character
                        const tensor_index_t o = rng_output();

                        // image: original object patch
                        const tensor3d_t opatch = get_object_patch(char_patches[rng_font() - 1], o, n_chars, 0.0);

                        // image: resize to the input size
                        tensor3d_t mpatch(4, irows(), icols());
                        nano::bilinear(opatch.matrix(0), mpatch.matrix(0));
                        nano::bilinear(opatch.matrix(1), mpatch.matrix(1));
                        nano::bilinear(opatch.matrix(2), mpatch.matrix(2));
                        nano::bilinear(opatch.matrix(3), mpatch.matrix(3));

                        // image: random warping
                        mpatch = nano::warp(mpatch,
                                warp_params_t(field_type::random, scalar_t(0.1), scalar_t(4.0), scalar_t(16.0), scalar_t(2.0)));

                        // image: background & foreground layer
                        const auto bcolor = make_random_rgba();
                        const auto fcolor = make_random_opposite_rgba(bcolor);

                        const auto bnoise = scalar_t(0.1);
                        const auto fnoise = scalar_t(0.1);

                        const auto bsigma = rng_gauss();
                        const auto fsigma = rng_gauss();

                        const auto bpatch = make_random_rgba_image(irows(), icols(), bcolor, bnoise, bsigma);
                        const auto fpatch = make_random_rgba_image(irows(), icols(), fcolor, fnoise, fsigma);

                        // image: alpha-blend the background & foreground layer
                        const tensor3d_t patch = alpha_blend(mpatch, bpatch, fpatch);

                        image_t image;
                        image.from_tensor(patch);
                        switch (m_color)
                        {
                        case color_mode::luma:  image.make_luma(); break;
                        case color_mode::rgba:  image.make_rgba(); break;
                        case color_mode::rgb:   image.make_rgb(); break;
                        }

                        // generate image
                        add_chunk(image);

                        // generate sample
                        const auto fold = make_fold(0);
                        const auto target = class_target(o - nano::obegin(m_charset), nano::osize(m_charset));
                        const auto label = string_t("char") + characters[static_cast<size_t>(o)];
                        add_sample(fold, n_chunks() - 1, target, label);
                }

                return true;
        }
}
<commit_msg>fix warning<commit_after>#include "task_charset.h"
#include "cortex/class.h"
#include "math/gauss.hpp"
#include "math/clamp.hpp"
#include "math/random.hpp"
#include "tensor/numeric.hpp"
#include "text/to_string.hpp"
#include "text/to_params.hpp"
#include "text/from_params.hpp"
#include "tensor/algorithm.hpp"
#include "vision/warp.h"
#include "vision/image_io.h"
#include "vision/convolve.hpp"
#include "vision/bilinear.hpp"

#include "synth_bitstream_vera_sans_mono_bold.h"
#include "synth_bitstream_vera_sans_mono.h"
#include "synth_dejavu_sans_mono_bold.h"
#include "synth_dejavu_sans_mono.h"
#include "synth_droid_sans_mono.h"
#include "synth_liberation_mono_bold.h"
#include "synth_liberation_mono.h"
#include "synth_nimbus_mono_bold.h"
#include "synth_nimbus_mono.h"
#include "synth_oxygen_mono.h"

namespace nano
{
        static rgba_t make_random_rgba()
        {
                random_t<luma_t> rng(0, 255);
                return rgba_t{rng(), rng(), rng(), rng()};
        }

        static rgba_t make_random_opposite_rgba(const rgba_t& rgba)
        {
                random_t<int> rng(-55, +55);
                return  rgba_t
                {
                        static_cast<luma_t>(clamp(255 - static_cast<int>(rgba(0)) + rng(), 0, 255)),
                        static_cast<luma_t>(clamp(255 - static_cast<int>(rgba(1)) + rng(), 0, 255)),
                        static_cast<luma_t>(clamp(255 - static_cast<int>(rgba(2)) + rng(), 0, 255)),
                        static_cast<luma_t>(clamp(255 - static_cast<int>(rgba(3)) + rng(), 0, 255))
                };
        }

        template <>
        inline std::map<nano::charset, std::string> enum_string<nano::charset>()
        {
                return
                {
                        { nano::charset::digit,         "digit" },
                        { nano::charset::lalpha,        "lalpha" },
                        { nano::charset::ualpha,        "ualpha" },
                        { nano::charset::alpha,         "alpha" },
                        { nano::charset::alphanum,      "alphanum" }
                };
        }

        static tensor_size_t obegin(const charset cs)
        {
                switch (cs)
                {
                case charset::digit:            return 0;
                case charset::lalpha:           return 0 + 10;
                case charset::ualpha:           return 0 + 10 + 26;
                case charset::alpha:            return 10;
                case charset::alphanum:         return 0;
                default:                        assert(false); return 0;
                }
        }

        static tensor_size_t oend(const charset cs)
        {
                switch (cs)
                {
                case charset::digit:            return 10;
                case charset::lalpha:           return 10 + 26;
                case charset::ualpha:           return 10 + 26 + 26;
                case charset::alpha:            return 10 + 26 + 26;
                case charset::alphanum:         return 10 + 26 + 26;
                default:                        assert(false); return 0;
                }
        }

        static tensor_size_t osize(const charset cs)
        {
                return oend(cs) - obegin(cs);
        }

        template
        <
                typename tindex,
                typename tsize
        >
        tensor3d_t get_object_patch(const image_tensor_t& image,
                const tindex object_index, const tsize objects, const scalar_t max_offset)
        {
                nano::random_t<scalar_t> rng(-max_offset, max_offset);

                const auto icols = static_cast<int>(image.cols());
                const auto irows = static_cast<int>(image.rows());

                const auto dx = static_cast<scalar_t>(icols) / static_cast<scalar_t>(objects);

                const auto x = dx * static_cast<scalar_t>(object_index) + rng();

                const auto ppx = nano::clamp(nano::cast<int>(x), 0, icols - 1);
                const auto ppw = nano::clamp(nano::cast<int>(dx + rng()), 0, icols - ppx);

                const auto ppy = nano::clamp(nano::cast<int>(rng()), 0, irows - 1);
                const auto pph = nano::clamp(nano::cast<int>(static_cast<scalar_t>(irows) + rng()), 0, irows - ppy);

                tensor3d_t ret(4, pph, ppw);
                ret.matrix(0) = image.matrix(0).block(ppy, ppx, pph, ppw).cast<scalar_t>() / scalar_t(255);
                ret.matrix(1) = image.matrix(1).block(ppy, ppx, pph, ppw).cast<scalar_t>() / scalar_t(255);
                ret.matrix(2) = image.matrix(2).block(ppy, ppx, pph, ppw).cast<scalar_t>() / scalar_t(255);
                ret.matrix(3) = image.matrix(3).block(ppy, ppx, pph, ppw).cast<scalar_t>() / scalar_t(255);

                return ret;
        }

        tensor3d_t make_random_rgba_image(const tensor_size_t rows, const tensor_size_t cols,
                const rgba_t back_color,
                const scalar_t max_noise, const scalar_t sigma)
        {
                // noisy background
                tensor3d_t image(4, rows, cols);
                image.matrix(0).setConstant(back_color(0) / scalar_t(255));
                image.matrix(1).setConstant(back_color(1) / scalar_t(255));
                image.matrix(2).setConstant(back_color(2) / scalar_t(255));
                image.matrix(3).setConstant(1);

                tensor::add_random(nano::make_rng<scalar_t>(-max_noise, +max_noise),
                        image.matrix(0), image.matrix(1), image.matrix(2));

                // smooth background
                const nano::gauss_kernel_t<scalar_t> back_gauss(sigma);
                nano::convolve(back_gauss, image.matrix(0));
                nano::convolve(back_gauss, image.matrix(1));
                nano::convolve(back_gauss, image.matrix(2));

                return image;
        }

        tensor3d_t alpha_blend(const tensor3d_t& mask, const tensor3d_t& img1, const tensor3d_t& img2)
        {
                const auto op = [] (const auto a, const auto v1, const auto v2)
                {
                        return (1 - a) * v1 + a * v2;
                };

                tensor3d_t imgb(4, mask.rows(), mask.cols());
                tensor::transform(mask.matrix(3), img1.matrix(0), img2.matrix(0), imgb.matrix(0), op);
                tensor::transform(mask.matrix(3), img1.matrix(1), img2.matrix(1), imgb.matrix(1), op);
                tensor::transform(mask.matrix(3), img1.matrix(2), img2.matrix(2), imgb.matrix(2), op);
                imgb.matrix(3).setConstant(1);

                return imgb;
        }

        charset_task_t::charset_task_t(const string_t& configuration) : mem_vision_task_t(
                "charset",
                nano::from_params<color_mode>(configuration, "color", color_mode::rgb),
                nano::clamp(nano::from_params<tensor_size_t>(configuration, "irows", 32), 12, 128),
                nano::clamp(nano::from_params<tensor_size_t>(configuration, "icols", 32), 12, 128),
                nano::osize(nano::from_params<charset>(configuration, "type", charset::digit)),
                1),
                m_charset(nano::from_params<charset>(configuration, "type", charset::digit)),
                m_color(nano::from_params<color_mode>(configuration, "color", color_mode::rgb)),
                m_count(nano::clamp(nano::from_params<size_t>(configuration, "count", 1000), 100, 1024 * 1024))
        {
        }

        charset_task_t::charset_task_t(const charset type, const color_mode mode,
                const tensor_size_t irows, const tensor_size_t icols, const size_t count) :
                charset_task_t(to_params("color", mode, "irows", irows, "icols", icols, "type", type, "count", count))
        {
        }

        bool charset_task_t::populate()
        {
                const string_t characters =
                        "0123456789" \
                        "abcdefghijklmnopqrstuvwxyz" \
                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

                const size_t n_chars = characters.size();

                std::vector<image_tensor_t> char_patches;
                image_tensor_t char_patch;
#define INSERT_IMAGE(name) \
                if (!nano::load_rgba_image(get_ ##name ##_name(), get_ ##name ##_data(), get_ ##name ##_size(), char_patch)) \
                { \
                        return false; \
                } \
                char_patches.push_back(char_patch);
                INSERT_IMAGE(synth_bitstream_vera_sans_mono_bold)
                INSERT_IMAGE(synth_bitstream_vera_sans_mono)
                INSERT_IMAGE(synth_dejavu_sans_mono_bold)
                INSERT_IMAGE(synth_dejavu_sans_mono)
                INSERT_IMAGE(synth_droid_sans_mono)
                INSERT_IMAGE(synth_liberation_mono_bold)
                INSERT_IMAGE(synth_liberation_mono)
                INSERT_IMAGE(synth_nimbus_mono_bold)
                INSERT_IMAGE(synth_nimbus_mono)
                INSERT_IMAGE(synth_oxygen_mono)
#undef INSERT_IMAGE

                const size_t n_fonts = char_patches.size();

                nano::random_t<tensor_size_t> rng_output(obegin(m_charset), oend(m_charset) - 1);
                nano::random_t<size_t> rng_font(1, n_fonts);
                nano::random_t<scalar_t> rng_gauss(0, 2);

                // generate samples
                for (size_t i = 0; i < m_count; ++ i)
                {
                        // random target: character
                        const tensor_index_t o = rng_output();

                        // image: original object patch
                        const tensor3d_t opatch = get_object_patch(char_patches[rng_font() - 1], o, n_chars, 0.0);

                        // image: resize to the input size
                        tensor3d_t mpatch(4, irows(), icols());
                        nano::bilinear(opatch.matrix(0), mpatch.matrix(0));
                        nano::bilinear(opatch.matrix(1), mpatch.matrix(1));
                        nano::bilinear(opatch.matrix(2), mpatch.matrix(2));
                        nano::bilinear(opatch.matrix(3), mpatch.matrix(3));

                        // image: random warping
                        mpatch = nano::warp(mpatch,
                                warp_params_t(field_type::random, scalar_t(0.1), scalar_t(4.0), scalar_t(16.0), scalar_t(2.0)));

                        // image: background & foreground layer
                        const auto bcolor = make_random_rgba();
                        const auto fcolor = make_random_opposite_rgba(bcolor);

                        const auto bnoise = scalar_t(0.1);
                        const auto fnoise = scalar_t(0.1);

                        const auto bsigma = rng_gauss();
                        const auto fsigma = rng_gauss();

                        const auto bpatch = make_random_rgba_image(irows(), icols(), bcolor, bnoise, bsigma);
                        const auto fpatch = make_random_rgba_image(irows(), icols(), fcolor, fnoise, fsigma);

                        // image: alpha-blend the background & foreground layer
                        const tensor3d_t patch = alpha_blend(mpatch, bpatch, fpatch);

                        image_t image;
                        image.from_tensor(patch);
                        switch (m_color)
                        {
                        case color_mode::luma:  image.make_luma(); break;
                        case color_mode::rgba:  image.make_rgba(); break;
                        case color_mode::rgb:   image.make_rgb(); break;
                        }

                        // generate image
                        add_chunk(image);

                        // generate sample
                        const auto fold = make_fold(0);
                        const auto target = class_target(o - nano::obegin(m_charset), nano::osize(m_charset));
                        const auto label = string_t("char") + characters[static_cast<size_t>(o)];
                        add_sample(fold, n_chunks() - 1, target, label);
                }

                return true;
        }
}
<|endoftext|>


<commit_before>#include "qtkapplicationparameters.h"
#include <QFile>
#include <QDebug>
#include <QDateTime>
#ifndef VEJAM_NO_GUI
#include <QMessageBox>
#endif
#include <QJsonObject>
#include <QJsonDocument>
#include <qcoreapplication.h>

QtKApplicationParameters::QtKApplicationParameters(QObject *parent, QString appName) :
    QObject(parent)
{
    this->m_appName = appName;
}

void QtKApplicationParameters::saveParam(QString groupName, QString paramName, QString paramValue, quint16 order)
{
        QString pn;
        QMap<QString, QVariant>::const_iterator i;
        i = this->m_projectParameters.find(this->m_appName);
        if(i == this->m_projectParameters.end())
        //Creem la entrada per el projecte
        {
           this->m_projectParameters.insert(this->m_appName,this->m_appName);
        }
        pn.append(this->m_appName);
        pn.append(".");
        pn.append(groupName);
        pn.append(".");
        pn.append(paramName);

        if(order)
        {
            pn.append(".");
            pn.append(QString("%1").arg(order));
        }
        this->m_projectParameters.insert(pn,paramValue);
        qDebug() << "saveParam(): " << this->m_projectParameters;
}

QString QtKApplicationParameters::loadParam(QString groupName, QString paramName, quint16 order)
{
    QString r;
    QString pn;
    QVariant d;
    pn.append(this->m_appName);
    pn.append(".");
    pn.append(groupName);
    pn.append(".");
    pn.append(paramName);
    if(order)
    {
        pn.append(".");
        pn.append(QString("%1").arg(order));
    }
    d = this->m_projectParameters.value(pn,QVariant(QString("void")));
    r = d.toString();
    //qDebug() << "loadParam(): " << pn << d;
    return r;
}

bool QtKApplicationParameters::fileLoad(bool showAlerts)
{
        QString fileName = this->m_appName;
        fileName.append(AP_FILE_EXTENSION);
		fileName.prepend(qApp->applicationDirPath()+"/");

        QFile f(fileName);
        QByteArray fd;
        f.open(QIODevice::ReadOnly);
        fd = f.readAll();
        f.close();

        QJsonDocument doc;
        QJsonParseError err;
        QJsonObject obj;

        doc = QJsonDocument::fromJson(fd,&err);

        if(err.error !=  QJsonParseError::NoError)
        {
            if(showAlerts)
            {
#ifdef          VEJAM_NO_GUI
                qDebug() << "Error en el archivo de configuración: " << err.errorString().toLatin1().data() << "posición: " << err.offset;
#else
                QMessageBox msgBox;
                QString msg;
                msg.sprintf("Error en el archivo de configuración:\n\r[%s] - posición %d",err.errorString().toLatin1().data(),err.offset);
                msgBox.setText(msg);
                msgBox.exec();
#endif
            }

            emit applicationParametersError();
            return 1;
         }

         obj = doc.object();

         this->m_projectParameters.clear();
         this->m_projectParameters = obj.toVariantMap();
#ifdef AP_DEBUG_TRACES_ON
         qDebug() << "fileLoad(): " << this->m_projectParameters;
#endif
         if(this->m_projectParameters.contains(this->m_appName))
         {
            emit applicationParametersLoaded();
         }
#ifdef AP_DEBUG_TRACES_ON
         qDebug() << "fileLoad(END)";
#endif
         return 0; //Tot ok.
}

bool QtKApplicationParameters::fileSave()
{
	    QDateTime time =  QDateTime::currentDateTime();
        QString fileName = this->m_appName;
        fileName.append(AP_FILE_EXTENSION);
		fileName.prepend(qApp->applicationDirPath()+"/");

        saveParam(QString("Common"), QString("LastSave"),time.toString("dd.MM.yyyy - hh:mm:ss.zzz"), 0);

        QJsonDocument doc;
        QJsonObject obj;
        obj = QJsonObject::fromVariantMap(this->m_projectParameters);

        qDebug() << "fileSave(): " << obj;
        doc.setObject(obj);

        QFile f(fileName);
        f.open(QIODevice::WriteOnly);
        f.write(doc.toJson());
        f.close();

        qDebug() << "fileSave(END)";
        return 0;
}
<commit_msg>Vejam QML adaptation<commit_after>#include "qtkapplicationparameters.h"
#include <QFile>
#include <QDebug>
#include <QDateTime>
#include <QJsonObject>
#include <QJsonDocument>
#include <qcoreapplication.h>

QtKApplicationParameters::QtKApplicationParameters(QObject *parent, QString appName) :
    QObject(parent)
{
    this->m_appName = appName;
}

void QtKApplicationParameters::saveParam(QString groupName, QString paramName, QString paramValue, quint16 order)
{
        QString pn;
        QMap<QString, QVariant>::const_iterator i;
        i = this->m_projectParameters.find(this->m_appName);
        if(i == this->m_projectParameters.end())
        //Creem la entrada per el projecte
        {
           this->m_projectParameters.insert(this->m_appName,this->m_appName);
        }
        pn.append(this->m_appName);
        pn.append(".");
        pn.append(groupName);
        pn.append(".");
        pn.append(paramName);

        if(order)
        {
            pn.append(".");
            pn.append(QString("%1").arg(order));
        }
        this->m_projectParameters.insert(pn,paramValue);
        qDebug() << "saveParam() " << paramName << ":" << paramValue;
}

QString QtKApplicationParameters::loadParam(QString groupName, QString paramName, quint16 order)
{
    QString r;
    QString pn;
    QVariant d;
    pn.append(this->m_appName);
    pn.append(".");
    pn.append(groupName);
    pn.append(".");
    pn.append(paramName);
    if(order)
    {
        pn.append(".");
        pn.append(QString("%1").arg(order));
    }
    d = this->m_projectParameters.value(pn,QVariant(QString("void")));
    r = d.toString();
    qDebug() << "loadParam() " << paramName << ":" << r;
    return r;
}

bool QtKApplicationParameters::fileLoad(bool showAlerts)
{
        QString fileName = this->m_appName;
        fileName.append(AP_FILE_EXTENSION);
#ifndef ANDROID_PLATFORM
		fileName.prepend(qApp->applicationDirPath()+"/");
#endif

        QFile f(fileName);
        QByteArray fd;
        f.open(QIODevice::ReadOnly);
        if(f.error())
        {
              qDebug() << "fileLoad() Error: " << f.error() << ", " << f.errorString();
        }

        fd = f.readAll();
        f.close();

        QJsonDocument doc;
        QJsonParseError err;
        QJsonObject obj;

        doc = QJsonDocument::fromJson(fd,&err);

        if(err.error !=  QJsonParseError::NoError)
        {
            if(showAlerts)
            {
                qDebug() << "Error en el archivo de configuración: " << err.errorString().toLatin1().data() << "posición: " << err.offset;
            }

            emit applicationParametersError();
            return 1;
         }

         obj = doc.object();

         this->m_projectParameters.clear();
         this->m_projectParameters = obj.toVariantMap();
#ifdef AP_DEBUG_TRACES_ON
         qDebug() << "fileLoad(): " << this->m_projectParameters;
#endif
         if(this->m_projectParameters.contains(this->m_appName))
         {
            emit applicationParametersLoaded();
         }
#ifdef AP_DEBUG_TRACES_ON
         qDebug() << "fileLoad(END)";
#endif
         return 0; //Tot ok.
}

bool QtKApplicationParameters::fileSave()
{
	    QDateTime time =  QDateTime::currentDateTime();
        QString fileName = this->m_appName;
        fileName.append(AP_FILE_EXTENSION);
#ifndef ANDROID_PLATFORM
		fileName.prepend(qApp->applicationDirPath()+"/");
#endif

        saveParam(QString("Common"), QString("LastSave"),time.toString("dd.MM.yyyy - hh:mm:ss.zzz"), 0);

        QJsonDocument doc;
        QJsonObject obj;
        obj = QJsonObject::fromVariantMap(this->m_projectParameters);

        //qDebug() << "fileSave(): " << obj;
        doc.setObject(obj);

        QFile f(fileName);
        f.open(QIODevice::WriteOnly);
        if(f.error())
        {
              qDebug() << "fileSave() Error: " << f.error() << ", " << f.errorString();
        }

        f.write(doc.toJson());
        f.close();

        qDebug() << "fileSave(END)";
        return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 */
#include <quic/logging/FileQLogger.h>

#include <fstream>

#include <folly/json.h>

namespace quic {

void FileQLogger::addPacket(
    const RegularQuicPacket& regularPacket,
    uint64_t packetSize) {
  logs.push_back(createPacketEvent(regularPacket, packetSize));
}

void FileQLogger::addPacket(
    const RegularQuicWritePacket& writePacket,
    uint64_t packetSize) {
  logs.push_back(createPacketEvent(writePacket, packetSize));
}

void FileQLogger::addPacket(
    const VersionNegotiationPacket& versionPacket,
    uint64_t packetSize,
    bool isPacketRecvd) {
  logs.push_back(createPacketEvent(versionPacket, packetSize, isPacketRecvd));
}

void FileQLogger::addConnectionClose(
    std::string error,
    std::string reason,
    bool drainConnection,
    bool sendCloseImmediately) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);
  logs.push_back(std::make_unique<quic::QLogConnectionCloseEvent>(
      std::move(error),
      std::move(reason),
      drainConnection,
      sendCloseImmediately,
      refTime));
}

void FileQLogger::addTransportSummary(
    uint64_t totalBytesSent,
    uint64_t totalBytesRecvd,
    uint64_t sumCurWriteOffset,
    uint64_t sumMaxObservedOffset,
    uint64_t sumCurStreamBufferLen,
    uint64_t totalBytesRetransmitted,
    uint64_t totalStreamBytesCloned,
    uint64_t totalBytesCloned,
    uint64_t totalCryptoDataWritten,
    uint64_t totalCryptoDataRecvd) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogTransportSummaryEvent>(
      totalBytesSent,
      totalBytesRecvd,
      sumCurWriteOffset,
      sumMaxObservedOffset,
      sumCurStreamBufferLen,
      totalBytesRetransmitted,
      totalStreamBytesCloned,
      totalBytesCloned,
      totalCryptoDataWritten,
      totalCryptoDataRecvd,
      refTime));
}

void FileQLogger::addCongestionMetricUpdate(
    uint64_t bytesInFlight,
    uint64_t currentCwnd,
    std::string congestionEvent,
    std::string state,
    std::string recoveryState) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogCongestionMetricUpdateEvent>(
      bytesInFlight,
      currentCwnd,
      std::move(congestionEvent),
      std::move(state),
      std::move(recoveryState),
      refTime));
}

void FileQLogger::addBandwidthEstUpdate(
    uint64_t bytes,
    std::chrono::microseconds interval) {
  logs.push_back(std::make_unique<quic::QLogBandwidthEstUpdateEvent>(
      bytes,
      interval,
      std::chrono::duration_cast<std::chrono::microseconds>(
          std::chrono::steady_clock::now() - refTimePoint)));
}

void FileQLogger::addAppLimitedUpdate() {
  logs.push_back(std::make_unique<quic::QLogAppLimitedUpdateEvent>(
      true,
      std::chrono::duration_cast<std::chrono::microseconds>(
          std::chrono::steady_clock::now() - refTimePoint)));
}

void FileQLogger::addAppUnlimitedUpdate() {
  logs.push_back(std::make_unique<quic::QLogAppLimitedUpdateEvent>(
      false,
      std::chrono::duration_cast<std::chrono::microseconds>(
          std::chrono::steady_clock::now() - refTimePoint)));
}
void FileQLogger::addPacingMetricUpdate(
    uint64_t pacingBurstSizeIn,
    std::chrono::microseconds pacingIntervalIn) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogPacingMetricUpdateEvent>(
      pacingBurstSizeIn, pacingIntervalIn, refTime));
}

void FileQLogger::addPacingObservation(
    std::string actual,
    std::string expect,
    std::string conclusion) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);
  logs.push_back(std::make_unique<quic::QLogPacingObservationEvent>(
      std::move(actual), std::move(expect), std::move(conclusion), refTime));
}

void FileQLogger::addAppIdleUpdate(std::string idleEvent, bool idle) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogAppIdleUpdateEvent>(
      std::move(idleEvent), idle, refTime));
}

void FileQLogger::addPacketDrop(size_t packetSize, std::string dropReason) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogPacketDropEvent>(
      packetSize, std::move(dropReason), refTime));
}

void FileQLogger::addDatagramReceived(uint64_t dataLen) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(
      std::make_unique<quic::QLogDatagramReceivedEvent>(dataLen, refTime));
}

void FileQLogger::addLossAlarm(
    PacketNum largestSent,
    uint64_t alarmCount,
    uint64_t outstandingPackets,
    std::string type) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogLossAlarmEvent>(
      largestSent, alarmCount, outstandingPackets, std::move(type), refTime));
}

void FileQLogger::addPacketsLost(
    PacketNum largestLostPacketNum,
    uint64_t lostBytes,
    uint64_t lostPackets) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogPacketsLostEvent>(
      largestLostPacketNum, lostBytes, lostPackets, refTime));
}

void FileQLogger::addTransportStateUpdate(std::string update) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogTransportStateUpdateEvent>(
      std::move(update), refTime));
}

void FileQLogger::addPacketBuffered(
    PacketNum packetNum,
    ProtectionType protectionType,
    uint64_t packetSize) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogPacketBufferedEvent>(
      packetNum, protectionType, packetSize, refTime));
}

void FileQLogger::addPacketAck(
    PacketNumberSpace packetNumSpace,
    PacketNum packetNum) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogPacketAckEvent>(
      packetNumSpace, packetNum, refTime));
}

void FileQLogger::addMetricUpdate(
    std::chrono::microseconds latestRtt,
    std::chrono::microseconds mrtt,
    std::chrono::microseconds srtt,
    std::chrono::microseconds ackDelay) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogMetricUpdateEvent>(
      latestRtt, mrtt, srtt, ackDelay, refTime));
}

folly::dynamic FileQLogger::toDynamic() const {
  folly::dynamic dynamicObj = folly::dynamic::object;
  dynamicObj[kQLogVersionField] = kQLogVersion;
  dynamicObj[kQLogTitleField] = kQLogTitle;
  dynamicObj[kQLogDescriptionField] = kQLogDescription;

  folly::dynamic summaryObj = folly::dynamic::object;
  summaryObj[kQLogTraceCountField] =
      1; // hardcoded, we only support 1 trace right now

  // max duration is calculated like this:
  // if there is <= 1 event, max_duration is 0
  // otherwise, it is the (time of the last event - time of the  first event)
  summaryObj["max_duration"] = (logs.size() == 0)
      ? 0
      : (logs.back()->refTime - logs[0]->refTime).count();
  summaryObj["max_outgoing_loss_rate"] = "";
  summaryObj["total_event_count"] = logs.size();
  dynamicObj["summary"] = summaryObj;

  dynamicObj["traces"] = folly::dynamic::array();
  folly::dynamic dynamicTrace = folly::dynamic::object;

  dynamicTrace["vantage_point"] =
      folly::dynamic::object("type", vantagePointString(vantagePoint))(
          "name", vantagePointString(vantagePoint));
  dynamicTrace["title"] = kQLogTraceTitle;
  dynamicTrace["description"] = kQLogTraceDescription;
  dynamicTrace["configuration"] =
      folly::dynamic::object("time_offset", 0)("time_units", kQLogTimeUnits);

  std::string dcidStr = dcid.hasValue() ? dcid.value().hex() : "";
  std::string scidStr = scid.hasValue() ? scid.value().hex() : "";
  folly::dynamic commonFieldsObj = folly::dynamic::object;
  commonFieldsObj["reference_time"] = "0";
  commonFieldsObj["dcid"] = dcidStr;
  commonFieldsObj["scid"] = scidStr;
  commonFieldsObj["protocol_type"] = protocolType;
  dynamicTrace["common_fields"] = std::move(commonFieldsObj);

  // convert stored logs into folly::Dynamic event array
  auto events = folly::dynamic::array();
  for (auto& event : logs) {
    events.push_back(event->toDynamic());
  }
  dynamicTrace["events"] = events;
  dynamicTrace["event_fields"] = folly::dynamic::array(
      "relative_time", "CATEGORY", "EVENT_TYPE", "TRIGGER", "DATA");

  dynamicObj["traces"].push_back(dynamicTrace);
  return dynamicObj;
}

void FileQLogger::addStreamStateUpdate(
    quic::StreamId id,
    std::string update,
    folly::Optional<std::chrono::milliseconds> timeSinceStreamCreation) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogStreamStateUpdateEvent>(
      id,
      std::move(update),
      std::move(timeSinceStreamCreation),
      vantagePoint,
      refTime));
}

void FileQLogger::outputLogsToFile(const std::string& path, bool prettyJson) {
  if (!dcid.hasValue()) {
    LOG(ERROR) << "Error: No dcid found";
    return;
  }
  std::string outputPath =
      folly::to<std::string>(path, "/", (dcid.value()).hex(), ".qlog");
  std::ofstream fileObj(outputPath);
  if (fileObj) {
    LOG(INFO) << "Logging QLogger JSON to file: " << outputPath;
    auto qLog = prettyJson ? folly::toPrettyJson(toDynamic())
                           : folly::toJson(toDynamic());
    fileObj << qLog;
  } else {
    LOG(ERROR) << "Error: Can't write to provided path: " << path;
  }
  fileObj.close();
}

} // namespace quic
<commit_msg>Remove excessive FileQLogger logging message<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 */
#include <quic/logging/FileQLogger.h>

#include <fstream>

#include <folly/json.h>

namespace quic {

void FileQLogger::addPacket(
    const RegularQuicPacket& regularPacket,
    uint64_t packetSize) {
  logs.push_back(createPacketEvent(regularPacket, packetSize));
}

void FileQLogger::addPacket(
    const RegularQuicWritePacket& writePacket,
    uint64_t packetSize) {
  logs.push_back(createPacketEvent(writePacket, packetSize));
}

void FileQLogger::addPacket(
    const VersionNegotiationPacket& versionPacket,
    uint64_t packetSize,
    bool isPacketRecvd) {
  logs.push_back(createPacketEvent(versionPacket, packetSize, isPacketRecvd));
}

void FileQLogger::addConnectionClose(
    std::string error,
    std::string reason,
    bool drainConnection,
    bool sendCloseImmediately) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);
  logs.push_back(std::make_unique<quic::QLogConnectionCloseEvent>(
      std::move(error),
      std::move(reason),
      drainConnection,
      sendCloseImmediately,
      refTime));
}

void FileQLogger::addTransportSummary(
    uint64_t totalBytesSent,
    uint64_t totalBytesRecvd,
    uint64_t sumCurWriteOffset,
    uint64_t sumMaxObservedOffset,
    uint64_t sumCurStreamBufferLen,
    uint64_t totalBytesRetransmitted,
    uint64_t totalStreamBytesCloned,
    uint64_t totalBytesCloned,
    uint64_t totalCryptoDataWritten,
    uint64_t totalCryptoDataRecvd) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogTransportSummaryEvent>(
      totalBytesSent,
      totalBytesRecvd,
      sumCurWriteOffset,
      sumMaxObservedOffset,
      sumCurStreamBufferLen,
      totalBytesRetransmitted,
      totalStreamBytesCloned,
      totalBytesCloned,
      totalCryptoDataWritten,
      totalCryptoDataRecvd,
      refTime));
}

void FileQLogger::addCongestionMetricUpdate(
    uint64_t bytesInFlight,
    uint64_t currentCwnd,
    std::string congestionEvent,
    std::string state,
    std::string recoveryState) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogCongestionMetricUpdateEvent>(
      bytesInFlight,
      currentCwnd,
      std::move(congestionEvent),
      std::move(state),
      std::move(recoveryState),
      refTime));
}

void FileQLogger::addBandwidthEstUpdate(
    uint64_t bytes,
    std::chrono::microseconds interval) {
  logs.push_back(std::make_unique<quic::QLogBandwidthEstUpdateEvent>(
      bytes,
      interval,
      std::chrono::duration_cast<std::chrono::microseconds>(
          std::chrono::steady_clock::now() - refTimePoint)));
}

void FileQLogger::addAppLimitedUpdate() {
  logs.push_back(std::make_unique<quic::QLogAppLimitedUpdateEvent>(
      true,
      std::chrono::duration_cast<std::chrono::microseconds>(
          std::chrono::steady_clock::now() - refTimePoint)));
}

void FileQLogger::addAppUnlimitedUpdate() {
  logs.push_back(std::make_unique<quic::QLogAppLimitedUpdateEvent>(
      false,
      std::chrono::duration_cast<std::chrono::microseconds>(
          std::chrono::steady_clock::now() - refTimePoint)));
}
void FileQLogger::addPacingMetricUpdate(
    uint64_t pacingBurstSizeIn,
    std::chrono::microseconds pacingIntervalIn) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogPacingMetricUpdateEvent>(
      pacingBurstSizeIn, pacingIntervalIn, refTime));
}

void FileQLogger::addPacingObservation(
    std::string actual,
    std::string expect,
    std::string conclusion) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);
  logs.push_back(std::make_unique<quic::QLogPacingObservationEvent>(
      std::move(actual), std::move(expect), std::move(conclusion), refTime));
}

void FileQLogger::addAppIdleUpdate(std::string idleEvent, bool idle) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogAppIdleUpdateEvent>(
      std::move(idleEvent), idle, refTime));
}

void FileQLogger::addPacketDrop(size_t packetSize, std::string dropReason) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogPacketDropEvent>(
      packetSize, std::move(dropReason), refTime));
}

void FileQLogger::addDatagramReceived(uint64_t dataLen) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(
      std::make_unique<quic::QLogDatagramReceivedEvent>(dataLen, refTime));
}

void FileQLogger::addLossAlarm(
    PacketNum largestSent,
    uint64_t alarmCount,
    uint64_t outstandingPackets,
    std::string type) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogLossAlarmEvent>(
      largestSent, alarmCount, outstandingPackets, std::move(type), refTime));
}

void FileQLogger::addPacketsLost(
    PacketNum largestLostPacketNum,
    uint64_t lostBytes,
    uint64_t lostPackets) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogPacketsLostEvent>(
      largestLostPacketNum, lostBytes, lostPackets, refTime));
}

void FileQLogger::addTransportStateUpdate(std::string update) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogTransportStateUpdateEvent>(
      std::move(update), refTime));
}

void FileQLogger::addPacketBuffered(
    PacketNum packetNum,
    ProtectionType protectionType,
    uint64_t packetSize) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogPacketBufferedEvent>(
      packetNum, protectionType, packetSize, refTime));
}

void FileQLogger::addPacketAck(
    PacketNumberSpace packetNumSpace,
    PacketNum packetNum) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogPacketAckEvent>(
      packetNumSpace, packetNum, refTime));
}

void FileQLogger::addMetricUpdate(
    std::chrono::microseconds latestRtt,
    std::chrono::microseconds mrtt,
    std::chrono::microseconds srtt,
    std::chrono::microseconds ackDelay) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogMetricUpdateEvent>(
      latestRtt, mrtt, srtt, ackDelay, refTime));
}

folly::dynamic FileQLogger::toDynamic() const {
  folly::dynamic dynamicObj = folly::dynamic::object;
  dynamicObj[kQLogVersionField] = kQLogVersion;
  dynamicObj[kQLogTitleField] = kQLogTitle;
  dynamicObj[kQLogDescriptionField] = kQLogDescription;

  folly::dynamic summaryObj = folly::dynamic::object;
  summaryObj[kQLogTraceCountField] =
      1; // hardcoded, we only support 1 trace right now

  // max duration is calculated like this:
  // if there is <= 1 event, max_duration is 0
  // otherwise, it is the (time of the last event - time of the  first event)
  summaryObj["max_duration"] = (logs.size() == 0)
      ? 0
      : (logs.back()->refTime - logs[0]->refTime).count();
  summaryObj["max_outgoing_loss_rate"] = "";
  summaryObj["total_event_count"] = logs.size();
  dynamicObj["summary"] = summaryObj;

  dynamicObj["traces"] = folly::dynamic::array();
  folly::dynamic dynamicTrace = folly::dynamic::object;

  dynamicTrace["vantage_point"] =
      folly::dynamic::object("type", vantagePointString(vantagePoint))(
          "name", vantagePointString(vantagePoint));
  dynamicTrace["title"] = kQLogTraceTitle;
  dynamicTrace["description"] = kQLogTraceDescription;
  dynamicTrace["configuration"] =
      folly::dynamic::object("time_offset", 0)("time_units", kQLogTimeUnits);

  std::string dcidStr = dcid.hasValue() ? dcid.value().hex() : "";
  std::string scidStr = scid.hasValue() ? scid.value().hex() : "";
  folly::dynamic commonFieldsObj = folly::dynamic::object;
  commonFieldsObj["reference_time"] = "0";
  commonFieldsObj["dcid"] = dcidStr;
  commonFieldsObj["scid"] = scidStr;
  commonFieldsObj["protocol_type"] = protocolType;
  dynamicTrace["common_fields"] = std::move(commonFieldsObj);

  // convert stored logs into folly::Dynamic event array
  auto events = folly::dynamic::array();
  for (auto& event : logs) {
    events.push_back(event->toDynamic());
  }
  dynamicTrace["events"] = events;
  dynamicTrace["event_fields"] = folly::dynamic::array(
      "relative_time", "CATEGORY", "EVENT_TYPE", "TRIGGER", "DATA");

  dynamicObj["traces"].push_back(dynamicTrace);
  return dynamicObj;
}

void FileQLogger::addStreamStateUpdate(
    quic::StreamId id,
    std::string update,
    folly::Optional<std::chrono::milliseconds> timeSinceStreamCreation) {
  auto refTime = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - refTimePoint);

  logs.push_back(std::make_unique<quic::QLogStreamStateUpdateEvent>(
      id,
      std::move(update),
      std::move(timeSinceStreamCreation),
      vantagePoint,
      refTime));
}

void FileQLogger::outputLogsToFile(const std::string& path, bool prettyJson) {
  if (!dcid.hasValue()) {
    LOG(ERROR) << "Error: No dcid found";
    return;
  }
  std::string outputPath =
      folly::to<std::string>(path, "/", (dcid.value()).hex(), ".qlog");
  std::ofstream fileObj(outputPath);
  if (fileObj) {
    auto qLog = prettyJson ? folly::toPrettyJson(toDynamic())
                           : folly::toJson(toDynamic());
    fileObj << qLog;
  } else {
    LOG(ERROR) << "Error: Can't write to provided path: " << path;
  }
  fileObj.close();
}

} // namespace quic
<|endoftext|>