<commit_before>///////////////////////////////////////////////////////////////////////////////
//
// CoinVault
//
// txmodel.cpp
//
// Copyright (c) 2014 Eric Lombrozo
//
// All Rights Reserved.

#include "txmodel.h"

#include <CoinDB/SynchedVault.h>

#include <CoinQ/CoinQ_script.h>

#include <stdutils/stringutils.h>

#include <QStandardItemModel>
#include <QDateTime>
#include <QMessageBox>

#include "settings.h"
#include "coinparams.h"

#include "severitylogger.h"

using namespace CoinDB;
using namespace CoinQ::Script;
using namespace std;

TxModel::TxModel(QObject* parent)
    : QStandardItemModel(parent)
{
    base58_versions[0] = getCoinParams().pay_to_pubkey_hash_version();
    base58_versions[1] = getCoinParams().pay_to_script_hash_version();

    currencySymbol = getCurrencySymbol();

    setColumns();
}

TxModel::TxModel(CoinDB::Vault* vault, const QString& accountName, QObject* parent)
    : QStandardItemModel(parent)
{
    base58_versions[0] = getCoinParams().pay_to_pubkey_hash_version();
    base58_versions[1] = getCoinParams().pay_to_script_hash_version();

    currencySymbol = getCurrencySymbol();

    setColumns();
    setVault(vault);
    setAccount(accountName);
}

void TxModel::setColumns()
{
    QStringList columns;
    columns
        << tr("Time")
        << tr("Description")
        << tr("Type")
        << (tr("Amount") + " (" + getCurrencySymbol() + ")")
        << (tr("Fee") + " (" + getCurrencySymbol() + ")")
        << (tr("Balance") + " (" + getCurrencySymbol() + ")")
        << tr("Confirmations")
        << tr("Address")
        << tr("Transaction Hash");
    setHorizontalHeaderLabels(columns);
}

void TxModel::setVault(CoinDB::Vault* vault)
{
    this->vault = vault;
    accountName.clear();
}

void TxModel::setAccount(const QString& accountName)
{
    if (!vault) {
        throw std::runtime_error("Vault is not open.");
    }

    if (!vault->accountExists(accountName.toStdString())) {
        throw std::runtime_error("Account not found.");
    }

    this->accountName = accountName;
    update();
}

void TxModel::update()
{
    QString newCurrencySymbol = getCurrencySymbol();
    if (newCurrencySymbol != currencySymbol)
    {
        currencySymbol = newCurrencySymbol;
        setColumns();
    }

    removeRows(0, rowCount());

    if (!vault || accountName.isEmpty()) return;

    std::shared_ptr<BlockHeader> bestHeader = vault->getBestBlockHeader();

    std::vector<TxOutView> txoutviews = vault->getTxOutViews(accountName.toStdString(), "", TxOut::ROLE_BOTH, TxOut::BOTH, Tx::ALL, true);
    bytes_t last_txhash;
    typedef std::pair<unsigned long, uint32_t> sorting_info_t;
    typedef std::pair<QList<QStandardItem*>, int64_t> value_row_t; // used to associate output values with rows for computing balance
    typedef std::pair<sorting_info_t, value_row_t> sortable_row_t;
    QList<sortable_row_t> rows;
    for (auto& item: txoutviews) {
        QList<QStandardItem*> row;

        QDateTime utc;
        utc.setTime_t(item.tx_timestamp);
        QString time = utc.toLocalTime().toString();

        QString description = QString::fromStdString(item.role_label());
        if (description.isEmpty()) description = "Not available";

        // The type stuff is just to test the new db schema. It's all wrong, we're not going to use TxOutViews for this.
        TxType txType;
        QString type;
        QString amount;
        QString fee;
        int64_t value = 0;
        bytes_t this_txhash = item.tx_status == Tx::UNSIGNED ? item.tx_unsigned_hash : item.tx_hash;
        switch (item.role_flags) {
        case TxOut::ROLE_NONE:
            txType = NONE;
            type = tr("None");
            break;

        case TxOut::ROLE_SENDER:
            txType = SEND;
            type = tr("Send");
            amount = "-";
            value -= item.value;
            if (item.tx_has_all_outpoints && item.tx_fee() > 0) {
                if (this_txhash != last_txhash) {
                    fee = "-";
                    //fee += QString::number(item.tx_fee()/(1.0 * currency_divisor), 'g', 8);
                    fee += getFormattedCurrencyAmount(item.tx_fee());
                    value -= item.tx_fee();
                    last_txhash = this_txhash;
                }
                else {
                    fee = "||";
                }
            }
            break;

        case TxOut::ROLE_RECEIVER:
            txType = RECEIVE;
            type = tr("Receive");
            amount = "+";
            value += item.value;
            break;

        default:
            txType = UNKNOWN;
            type = tr("Unknown");
        }

        //amount += QString::number(item.value/(1.0 * currency_divisor), 'g', 8);
        amount += getFormattedCurrencyAmount(item.value);

        uint32_t nConfirmations = 0;
        QString confirmations;
        if (item.tx_status >= Tx::PROPAGATED) {
            if (bestHeader && item.height) {
                nConfirmations = bestHeader->height() + 1 - item.height;
                confirmations = QString::number(nConfirmations);
            }
            else {
                confirmations = "0";
            }
        }
        else if (item.tx_status == Tx::UNSIGNED) {
            confirmations = tr("Unsigned");
        }
        else if (item.tx_status == Tx::UNSENT) {
            confirmations = tr("Unsent");
        }
        QStandardItem* confirmationsItem = new QStandardItem(confirmations);
        confirmationsItem->setData(item.tx_status, Qt::UserRole);

        QString address = QString::fromStdString(getAddressForTxOutScript(item.script, base58_versions));
        QString hash = QString::fromStdString(uchar_vector(this_txhash).getHex());

        row.append(new QStandardItem(time));
        row.append(new QStandardItem(description));

        QStandardItem* typeItem = new QStandardItem(type);
        typeItem->setData(txType, Qt::UserRole);
        row.append(typeItem);

        row.append(new QStandardItem(amount));
        row.append(new QStandardItem(fee));
        row.append(new QStandardItem("")); // placeholder for balance, once sorted
        row.append(confirmationsItem);
        row.append(new QStandardItem(address));

        // Store the tx hash and tx index to uniquely identify the output.
        QStandardItem* hashItem = new QStandardItem(hash);
        hashItem->setData(item.tx_index, Qt::UserRole);
        row.append(hashItem);

        rows.append(std::make_pair(std::make_pair(item.tx_id, nConfirmations), std::make_pair(row, value)));
    }

    qSort(rows.begin(), rows.end(), [](const sortable_row_t& a, const sortable_row_t& b) {
        sorting_info_t a_info = a.first;
        sorting_info_t b_info = b.first;

        // if confirmation counts are equal
        if (a_info.second == b_info.second) {
            // if one value is positive and the other is negative, sort so that running balance remains positive
            if (a.second.second < 0 && b.second.second > 0) return true;
            if (a.second.second > 0 && b.second.second < 0) return false;

            // otherwise sort by descending index
            return (a_info.first > b_info.first);
        }

        // otherwise sort by ascending confirmation count
        return (a_info.second < b_info.second);
    });

    // iterate in reverse order to compute running balance
    int64_t balance = 0;
    for (int i = rows.size() - 1; i >= 0; i--) {
        balance += rows[i].second.second;
        //(rows[i].second.first)[5]->setText(QString::number(balance/(1.0 * currency_divisor), 'g', 8));
        (rows[i].second.first)[5]->setText(getFormattedCurrencyAmount(balance));
    }

    // iterate in forward order to display
    for (auto& row: rows) appendRow(row.second.first);
}

bytes_t TxModel::getTxHash(int row) const
{
    LOGGER(trace) << "TxModel::getTxHash(" << row << ")" << std::endl;

    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());
    return txhash;
}

void TxModel::signTx(int row)
{
    LOGGER(trace) << "TxModel::signTx(" << row << ")" << std::endl;

    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* typeItem = item(row, 6);
    int type = typeItem->data(Qt::UserRole).toInt();
    if (type != CoinDB::Tx::UNSIGNED) {
        throw std::runtime_error(tr("Transaction is already signed.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    std::vector<std::string> keychainNames;
    std::shared_ptr<Tx> tx = vault->signTx(txhash, keychainNames, true);
    if (!tx) throw std::runtime_error(tr("No new signatures were added.").toStdString());

    LOGGER(trace) << "TxModel::signTx - signature(s) added. raw tx: " << uchar_vector(tx->raw()).getHex() << std::endl;
    update();

    QString msg;
    if (keychainNames.empty())
    {
        msg = tr("No new signatures were added.");
    }
    else
    {
        msg = tr("Signatures added using keychain(s) ") + QString::fromStdString(stdutils::delimited_list(keychainNames, ", ")) + tr(".");
    }
    emit txSigned(msg);
}

void TxModel::sendTx(int row, CoinDB::SynchedVault* synchedVault)
{
    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    if (!synchedVault->isConnected()) {
        throw std::runtime_error(tr("Must be connected to network to send.").toStdString());
    }

    QStandardItem* typeItem = item(row, 6);
    int type = typeItem->data(Qt::UserRole).toInt();
    if (type == CoinDB::Tx::UNSIGNED) {
        throw std::runtime_error(tr("Transaction must be fully signed before sending.").toStdString());
    }
    else if (type == CoinDB::Tx::PROPAGATED) {
        throw std::runtime_error(tr("Transaction already sent.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    std::shared_ptr<CoinDB::Tx> tx = vault->getTx(txhash);
    Coin::Transaction coin_tx = tx->toCoinCore();
    synchedVault->sendTx(coin_tx);

    // TODO: Check transaction has propagated before changing status to PROPAGATED
//    tx->updateStatus(CoinDB::Tx::PROPAGATED);
//    vault->insertTx(tx);

//    update();
    //networkSync->getTx(txhash);
}

std::shared_ptr<Tx> TxModel::getTx(int row)
{
    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    return vault->getTx(txhash);
}

void TxModel::deleteTx(int row)
{
    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    vault->deleteTx(txhash);
    update();

    emit txDeleted();
}

QVariant TxModel::data(const QModelIndex& index, int role) const
{
    // Right-align numeric fields
    if (role == Qt::TextAlignmentRole && index.column() >= 3 && index.column() <= 6)
    {
        return Qt::AlignRight;
    }
    else if (role == Qt::BackgroundRole)
    {
        QStandardItem* typeItem = item(index.row(), 2);
        int txtype = typeItem->data(Qt::UserRole).toInt();
        switch (txtype)
        {
        case SEND:      return QBrush(QColor(255, 175, 175));
        case RECEIVE:   return QBrush(QColor(175, 255, 175));
        }
    }

    return QStandardItemModel::data(index, role);
}

bool TxModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
    if (role == Qt::EditRole) {
        if (index.column() == 1) {
            // Keychain name edited.
            if (!vault) return false;

            try {
                // Get account role (sender/receiver)
                QStandardItem* typeItem = item(index.row(), 2);
                int txtype = typeItem->data(Qt::UserRole).toInt();
                if (txtype != SEND && txtype != RECEIVE) return false;

                // Get outpoint information
                QStandardItem* txHashItem = item(index.row(), 8);
                uchar_vector txhash;
                txhash.setHex(txHashItem->text().toStdString());
                uint32_t txindex = (uint32_t)txHashItem->data(Qt::UserRole).toInt();

                if (txtype == SEND)
                {
                    vault->setSendingLabel(txhash, txindex, value.toString().toStdString());
                }
                else
                {
                    vault->setReceivingLabel(txhash, txindex, value.toString().toStdString());
                }

                setItem(index.row(), index.column(), new QStandardItem(value.toString()));
                return true;
            }
            catch (const std::exception& e) {
                emit error(QString::fromStdString(e.what()));
            }
        }
        return false;
    }

    return true;
}

Qt::ItemFlags TxModel::flags(const QModelIndex& index) const
{
    if (index.column() == 1) {
        return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
    }

    return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}

<commit_msg>Lighter colors for TxModel.<commit_after>///////////////////////////////////////////////////////////////////////////////
//
// CoinVault
//
// txmodel.cpp
//
// Copyright (c) 2014 Eric Lombrozo
//
// All Rights Reserved.

#include "txmodel.h"

#include <CoinDB/SynchedVault.h>

#include <CoinQ/CoinQ_script.h>

#include <stdutils/stringutils.h>

#include <QStandardItemModel>
#include <QDateTime>
#include <QMessageBox>

#include "settings.h"
#include "coinparams.h"

#include "severitylogger.h"

using namespace CoinDB;
using namespace CoinQ::Script;
using namespace std;

TxModel::TxModel(QObject* parent)
    : QStandardItemModel(parent)
{
    base58_versions[0] = getCoinParams().pay_to_pubkey_hash_version();
    base58_versions[1] = getCoinParams().pay_to_script_hash_version();

    currencySymbol = getCurrencySymbol();

    setColumns();
}

TxModel::TxModel(CoinDB::Vault* vault, const QString& accountName, QObject* parent)
    : QStandardItemModel(parent)
{
    base58_versions[0] = getCoinParams().pay_to_pubkey_hash_version();
    base58_versions[1] = getCoinParams().pay_to_script_hash_version();

    currencySymbol = getCurrencySymbol();

    setColumns();
    setVault(vault);
    setAccount(accountName);
}

void TxModel::setColumns()
{
    QStringList columns;
    columns
        << tr("Time")
        << tr("Description")
        << tr("Type")
        << (tr("Amount") + " (" + getCurrencySymbol() + ")")
        << (tr("Fee") + " (" + getCurrencySymbol() + ")")
        << (tr("Balance") + " (" + getCurrencySymbol() + ")")
        << tr("Confirmations")
        << tr("Address")
        << tr("Transaction Hash");
    setHorizontalHeaderLabels(columns);
}

void TxModel::setVault(CoinDB::Vault* vault)
{
    this->vault = vault;
    accountName.clear();
}

void TxModel::setAccount(const QString& accountName)
{
    if (!vault) {
        throw std::runtime_error("Vault is not open.");
    }

    if (!vault->accountExists(accountName.toStdString())) {
        throw std::runtime_error("Account not found.");
    }

    this->accountName = accountName;
    update();
}

void TxModel::update()
{
    QString newCurrencySymbol = getCurrencySymbol();
    if (newCurrencySymbol != currencySymbol)
    {
        currencySymbol = newCurrencySymbol;
        setColumns();
    }

    removeRows(0, rowCount());

    if (!vault || accountName.isEmpty()) return;

    std::shared_ptr<BlockHeader> bestHeader = vault->getBestBlockHeader();

    std::vector<TxOutView> txoutviews = vault->getTxOutViews(accountName.toStdString(), "", TxOut::ROLE_BOTH, TxOut::BOTH, Tx::ALL, true);
    bytes_t last_txhash;
    typedef std::pair<unsigned long, uint32_t> sorting_info_t;
    typedef std::pair<QList<QStandardItem*>, int64_t> value_row_t; // used to associate output values with rows for computing balance
    typedef std::pair<sorting_info_t, value_row_t> sortable_row_t;
    QList<sortable_row_t> rows;
    for (auto& item: txoutviews) {
        QList<QStandardItem*> row;

        QDateTime utc;
        utc.setTime_t(item.tx_timestamp);
        QString time = utc.toLocalTime().toString();

        QString description = QString::fromStdString(item.role_label());
        if (description.isEmpty()) description = "Not available";

        // The type stuff is just to test the new db schema. It's all wrong, we're not going to use TxOutViews for this.
        TxType txType;
        QString type;
        QString amount;
        QString fee;
        int64_t value = 0;
        bytes_t this_txhash = item.tx_status == Tx::UNSIGNED ? item.tx_unsigned_hash : item.tx_hash;
        switch (item.role_flags) {
        case TxOut::ROLE_NONE:
            txType = NONE;
            type = tr("None");
            break;

        case TxOut::ROLE_SENDER:
            txType = SEND;
            type = tr("Send");
            amount = "-";
            value -= item.value;
            if (item.tx_has_all_outpoints && item.tx_fee() > 0) {
                if (this_txhash != last_txhash) {
                    fee = "-";
                    //fee += QString::number(item.tx_fee()/(1.0 * currency_divisor), 'g', 8);
                    fee += getFormattedCurrencyAmount(item.tx_fee());
                    value -= item.tx_fee();
                    last_txhash = this_txhash;
                }
                else {
                    fee = "||";
                }
            }
            break;

        case TxOut::ROLE_RECEIVER:
            txType = RECEIVE;
            type = tr("Receive");
            amount = "+";
            value += item.value;
            break;

        default:
            txType = UNKNOWN;
            type = tr("Unknown");
        }

        //amount += QString::number(item.value/(1.0 * currency_divisor), 'g', 8);
        amount += getFormattedCurrencyAmount(item.value);

        uint32_t nConfirmations = 0;
        QString confirmations;
        if (item.tx_status >= Tx::PROPAGATED) {
            if (bestHeader && item.height) {
                nConfirmations = bestHeader->height() + 1 - item.height;
                confirmations = QString::number(nConfirmations);
            }
            else {
                confirmations = "0";
            }
        }
        else if (item.tx_status == Tx::UNSIGNED) {
            confirmations = tr("Unsigned");
        }
        else if (item.tx_status == Tx::UNSENT) {
            confirmations = tr("Unsent");
        }
        QStandardItem* confirmationsItem = new QStandardItem(confirmations);
        confirmationsItem->setData(item.tx_status, Qt::UserRole);

        QString address = QString::fromStdString(getAddressForTxOutScript(item.script, base58_versions));
        QString hash = QString::fromStdString(uchar_vector(this_txhash).getHex());

        row.append(new QStandardItem(time));
        row.append(new QStandardItem(description));

        QStandardItem* typeItem = new QStandardItem(type);
        typeItem->setData(txType, Qt::UserRole);
        row.append(typeItem);

        row.append(new QStandardItem(amount));
        row.append(new QStandardItem(fee));
        row.append(new QStandardItem("")); // placeholder for balance, once sorted
        row.append(confirmationsItem);
        row.append(new QStandardItem(address));

        // Store the tx hash and tx index to uniquely identify the output.
        QStandardItem* hashItem = new QStandardItem(hash);
        hashItem->setData(item.tx_index, Qt::UserRole);
        row.append(hashItem);

        rows.append(std::make_pair(std::make_pair(item.tx_id, nConfirmations), std::make_pair(row, value)));
    }

    qSort(rows.begin(), rows.end(), [](const sortable_row_t& a, const sortable_row_t& b) {
        sorting_info_t a_info = a.first;
        sorting_info_t b_info = b.first;

        // if confirmation counts are equal
        if (a_info.second == b_info.second) {
            // if one value is positive and the other is negative, sort so that running balance remains positive
            if (a.second.second < 0 && b.second.second > 0) return true;
            if (a.second.second > 0 && b.second.second < 0) return false;

            // otherwise sort by descending index
            return (a_info.first > b_info.first);
        }

        // otherwise sort by ascending confirmation count
        return (a_info.second < b_info.second);
    });

    // iterate in reverse order to compute running balance
    int64_t balance = 0;
    for (int i = rows.size() - 1; i >= 0; i--) {
        balance += rows[i].second.second;
        //(rows[i].second.first)[5]->setText(QString::number(balance/(1.0 * currency_divisor), 'g', 8));
        (rows[i].second.first)[5]->setText(getFormattedCurrencyAmount(balance));
    }

    // iterate in forward order to display
    for (auto& row: rows) appendRow(row.second.first);
}

bytes_t TxModel::getTxHash(int row) const
{
    LOGGER(trace) << "TxModel::getTxHash(" << row << ")" << std::endl;

    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());
    return txhash;
}

void TxModel::signTx(int row)
{
    LOGGER(trace) << "TxModel::signTx(" << row << ")" << std::endl;

    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* typeItem = item(row, 6);
    int type = typeItem->data(Qt::UserRole).toInt();
    if (type != CoinDB::Tx::UNSIGNED) {
        throw std::runtime_error(tr("Transaction is already signed.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    std::vector<std::string> keychainNames;
    std::shared_ptr<Tx> tx = vault->signTx(txhash, keychainNames, true);
    if (!tx) throw std::runtime_error(tr("No new signatures were added.").toStdString());

    LOGGER(trace) << "TxModel::signTx - signature(s) added. raw tx: " << uchar_vector(tx->raw()).getHex() << std::endl;
    update();

    QString msg;
    if (keychainNames.empty())
    {
        msg = tr("No new signatures were added.");
    }
    else
    {
        msg = tr("Signatures added using keychain(s) ") + QString::fromStdString(stdutils::delimited_list(keychainNames, ", ")) + tr(".");
    }
    emit txSigned(msg);
}

void TxModel::sendTx(int row, CoinDB::SynchedVault* synchedVault)
{
    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    if (!synchedVault->isConnected()) {
        throw std::runtime_error(tr("Must be connected to network to send.").toStdString());
    }

    QStandardItem* typeItem = item(row, 6);
    int type = typeItem->data(Qt::UserRole).toInt();
    if (type == CoinDB::Tx::UNSIGNED) {
        throw std::runtime_error(tr("Transaction must be fully signed before sending.").toStdString());
    }
    else if (type == CoinDB::Tx::PROPAGATED) {
        throw std::runtime_error(tr("Transaction already sent.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    std::shared_ptr<CoinDB::Tx> tx = vault->getTx(txhash);
    Coin::Transaction coin_tx = tx->toCoinCore();
    synchedVault->sendTx(coin_tx);

    // TODO: Check transaction has propagated before changing status to PROPAGATED
//    tx->updateStatus(CoinDB::Tx::PROPAGATED);
//    vault->insertTx(tx);

//    update();
    //networkSync->getTx(txhash);
}

std::shared_ptr<Tx> TxModel::getTx(int row)
{
    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    return vault->getTx(txhash);
}

void TxModel::deleteTx(int row)
{
    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    vault->deleteTx(txhash);
    update();

    emit txDeleted();
}

QVariant TxModel::data(const QModelIndex& index, int role) const
{
    // Right-align numeric fields
    if (role == Qt::TextAlignmentRole && index.column() >= 3 && index.column() <= 6)
    {
        return Qt::AlignRight;
    }
    else if (role == Qt::BackgroundRole)
    {
        QStandardItem* typeItem = item(index.row(), 2);
        int txtype = typeItem->data(Qt::UserRole).toInt();
        switch (txtype)
        {
        case SEND:      return QBrush(QColor(255, 200, 200));
        case RECEIVE:   return QBrush(QColor(200, 255, 200));
        }
    }

    return QStandardItemModel::data(index, role);
}

bool TxModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
    if (role == Qt::EditRole) {
        if (index.column() == 1) {
            // Keychain name edited.
            if (!vault) return false;

            try {
                // Get account role (sender/receiver)
                QStandardItem* typeItem = item(index.row(), 2);
                int txtype = typeItem->data(Qt::UserRole).toInt();
                if (txtype != SEND && txtype != RECEIVE) return false;

                // Get outpoint information
                QStandardItem* txHashItem = item(index.row(), 8);
                uchar_vector txhash;
                txhash.setHex(txHashItem->text().toStdString());
                uint32_t txindex = (uint32_t)txHashItem->data(Qt::UserRole).toInt();

                if (txtype == SEND)
                {
                    vault->setSendingLabel(txhash, txindex, value.toString().toStdString());
                }
                else
                {
                    vault->setReceivingLabel(txhash, txindex, value.toString().toStdString());
                }

                setItem(index.row(), index.column(), new QStandardItem(value.toString()));
                return true;
            }
            catch (const std::exception& e) {
                emit error(QString::fromStdString(e.what()));
            }
        }
        return false;
    }

    return true;
}

Qt::ItemFlags TxModel::flags(const QModelIndex& index) const
{
    if (index.column() == 1) {
        return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
    }

    return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}

<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
//
// CoinVault
//
// txmodel.cpp
//
// Copyright (c) 2014 Eric Lombrozo
//
// All Rights Reserved.

#include "txmodel.h"

#include <CoinDB/SynchedVault.h>

#include <CoinQ/CoinQ_script.h>

#include <stdutils/stringutils.h>

#include <QStandardItemModel>
#include <QDateTime>
#include <QMessageBox>

#include "settings.h"
#include "coinparams.h"

#include "severitylogger.h"

using namespace CoinDB;
using namespace CoinQ::Script;
using namespace std;

TxModel::TxModel(QObject* parent)
    : QStandardItemModel(parent)
{
    base58_versions[0] = getCoinParams().pay_to_pubkey_hash_version();
    base58_versions[1] = getCoinParams().pay_to_script_hash_version();

    currencySymbol = getCurrencySymbol();

    setColumns();
}

TxModel::TxModel(CoinDB::Vault* vault, const QString& accountName, QObject* parent)
    : QStandardItemModel(parent)
{
    base58_versions[0] = getCoinParams().pay_to_pubkey_hash_version();
    base58_versions[1] = getCoinParams().pay_to_script_hash_version();

    currencySymbol = getCurrencySymbol();

    setColumns();
    setVault(vault);
    setAccount(accountName);
}

void TxModel::setColumns()
{
    QStringList columns;
    columns
        << tr("Time")
        << tr("Description")
        << tr("Type")
        << (tr("Amount") + " (" + getCurrencySymbol() + ")")
        << (tr("Fee") + " (" + getCurrencySymbol() + ")")
        << (tr("Balance") + " (" + getCurrencySymbol() + ")")
        << tr("Confirmations")
        << tr("Address")
        << tr("Transaction Hash");
    setHorizontalHeaderLabels(columns);
}

void TxModel::setVault(CoinDB::Vault* vault)
{
    this->vault = vault;
    accountName.clear();
}

void TxModel::setAccount(const QString& accountName)
{
    if (!vault) {
        throw std::runtime_error("Vault is not open.");
    }

    if (!vault->accountExists(accountName.toStdString())) {
        throw std::runtime_error("Account not found.");
    }

    this->accountName = accountName;
    update();
}

void TxModel::update()
{
    QString newCurrencySymbol = getCurrencySymbol();
    if (newCurrencySymbol != currencySymbol)
    {
        currencySymbol = newCurrencySymbol;
        setColumns();
    }

    removeRows(0, rowCount());

    if (!vault || accountName.isEmpty()) return;

    std::shared_ptr<BlockHeader> bestHeader = vault->getBestBlockHeader();

    std::vector<TxOutView> txoutviews = vault->getTxOutViews(accountName.toStdString(), "", TxOut::ROLE_BOTH, TxOut::BOTH, Tx::ALL, true);
    bytes_t last_txhash;
    typedef std::pair<unsigned long, uint32_t> sorting_info_t;
    typedef std::pair<QList<QStandardItem*>, int64_t> value_row_t; // used to associate output values with rows for computing balance
    typedef std::pair<sorting_info_t, value_row_t> sortable_row_t;
    QList<sortable_row_t> rows;
    for (auto& item: txoutviews) {
        QList<QStandardItem*> row;

        QDateTime utc;
        utc.setTime_t(item.tx_timestamp);
        QString time = utc.toLocalTime().toString();

        QString description = QString::fromStdString(item.role_label());
        if (description.isEmpty()) description = "Not available";

        // The type stuff is just to test the new db schema. It's all wrong, we're not going to use TxOutViews for this.
        TxType txType;
        QString type;
        QString amount;
        QString fee;
        int64_t value = 0;
        bytes_t this_txhash = item.tx_status == Tx::UNSIGNED ? item.tx_unsigned_hash : item.tx_hash;
        switch (item.role_flags) {
        case TxOut::ROLE_NONE:
            txType = NONE;
            type = tr("None");
            break;

        case TxOut::ROLE_SENDER:
            txType = SEND;
            type = tr("Send");
            amount = "-";
            value -= item.value;
            if (item.tx_has_all_outpoints && item.tx_fee() > 0) {
                if (this_txhash != last_txhash) {
                    fee = "-";
                    //fee += QString::number(item.tx_fee()/(1.0 * currency_divisor), 'g', 8);
                    fee += getFormattedCurrencyAmount(item.tx_fee());
                    value -= item.tx_fee();
                    last_txhash = this_txhash;
                }
                else {
                    fee = "||";
                }
            }
            break;

        case TxOut::ROLE_RECEIVER:
            txType = RECEIVE;
            type = tr("Receive");
            amount = "+";
            value += item.value;
            break;

        default:
            txType = UNKNOWN;
            type = tr("Unknown");
        }

        //amount += QString::number(item.value/(1.0 * currency_divisor), 'g', 8);
        amount += getFormattedCurrencyAmount(item.value);

        uint32_t nConfirmations = 0;
        QString confirmations;
        if (item.tx_status >= Tx::PROPAGATED) {
            if (bestHeader && item.height) {
                nConfirmations = bestHeader->height() + 1 - item.height;
                confirmations = QString::number(nConfirmations);
            }
            else {
                confirmations = "0";
            }
        }
        else if (item.tx_status == Tx::UNSIGNED) {
            confirmations = tr("Unsigned");
        }
        else if (item.tx_status == Tx::UNSENT) {
            confirmations = tr("Unsent");
        }
        QStandardItem* confirmationsItem = new QStandardItem(confirmations);
        confirmationsItem->setData(item.tx_status, Qt::UserRole);

        QString address = QString::fromStdString(getAddressForTxOutScript(item.script, base58_versions));
        QString hash = QString::fromStdString(uchar_vector(this_txhash).getHex());

        row.append(new QStandardItem(time));
        row.append(new QStandardItem(description));

        QStandardItem* typeItem = new QStandardItem(type);
        typeItem->setData(txType, Qt::UserRole);
        row.append(typeItem);

        row.append(new QStandardItem(amount));
        row.append(new QStandardItem(fee));
        row.append(new QStandardItem("")); // placeholder for balance, once sorted
        row.append(confirmationsItem);
        row.append(new QStandardItem(address));

        // Store the tx hash and tx index to uniquely identify the output.
        QStandardItem* hashItem = new QStandardItem(hash);
        hashItem->setData(item.tx_index, Qt::UserRole);
        row.append(hashItem);

        rows.append(std::make_pair(std::make_pair(item.tx_id, nConfirmations), std::make_pair(row, value)));
    }

    qSort(rows.begin(), rows.end(), [](const sortable_row_t& a, const sortable_row_t& b) {
        sorting_info_t a_info = a.first;
        sorting_info_t b_info = b.first;

        // if confirmation counts are equal
        if (a_info.second == b_info.second) {
            // if one value is positive and the other is negative, sort so that running balance remains positive
            if (a.second.second < 0 && b.second.second > 0) return true;
            if (a.second.second > 0 && b.second.second < 0) return false;

            // otherwise sort by descending index
            return (a_info.first > b_info.first);
        }

        // otherwise sort by ascending confirmation count
        return (a_info.second < b_info.second);
    });

    // iterate in reverse order to compute running balance
    int64_t balance = 0;
    for (int i = rows.size() - 1; i >= 0; i--) {
        balance += rows[i].second.second;
        //(rows[i].second.first)[5]->setText(QString::number(balance/(1.0 * currency_divisor), 'g', 8));
        (rows[i].second.first)[5]->setText(getFormattedCurrencyAmount(balance));
    }

    // iterate in forward order to display
    for (auto& row: rows) appendRow(row.second.first);
}

bytes_t TxModel::getTxHash(int row) const
{
    LOGGER(trace) << "TxModel::getTxHash(" << row << ")" << std::endl;

    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());
    return txhash;
}

void TxModel::signTx(int row)
{
    LOGGER(trace) << "TxModel::signTx(" << row << ")" << std::endl;

    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* typeItem = item(row, 6);
    int type = typeItem->data(Qt::UserRole).toInt();
    if (type != CoinDB::Tx::UNSIGNED) {
        throw std::runtime_error(tr("Transaction is already signed.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    std::vector<std::string> keychainNames;
    std::shared_ptr<Tx> tx = vault->signTx(txhash, keychainNames, true);
    if (!tx) throw std::runtime_error(tr("No new signatures were added.").toStdString());

    LOGGER(trace) << "TxModel::signTx - signature(s) added. raw tx: " << uchar_vector(tx->raw()).getHex() << std::endl;
    update();

    QString msg;
    if (keychainNames.empty())
    {
        msg = tr("No new signatures were added.");
    }
    else
    {
        msg = tr("Signatures added using keychain(s) ") + QString::fromStdString(stdutils::delimited_list(keychainNames, ", ")) + tr(".");
    }
    emit txSigned(msg);
}

void TxModel::sendTx(int row, CoinDB::SynchedVault* synchedVault)
{
    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    if (!synchedVault->isConnected()) {
        throw std::runtime_error(tr("Must be connected to network to send.").toStdString());
    }

    QStandardItem* typeItem = item(row, 6);
    int type = typeItem->data(Qt::UserRole).toInt();
    if (type == CoinDB::Tx::UNSIGNED) {
        throw std::runtime_error(tr("Transaction must be fully signed before sending.").toStdString());
    }
    else if (type == CoinDB::Tx::PROPAGATED) {
        throw std::runtime_error(tr("Transaction already sent.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    std::shared_ptr<CoinDB::Tx> tx = vault->getTx(txhash);
    Coin::Transaction coin_tx = tx->toCoinCore();
    synchedVault->sendTx(coin_tx);

    // TODO: Check transaction has propagated before changing status to PROPAGATED
//    tx->updateStatus(CoinDB::Tx::PROPAGATED);
//    vault->insertTx(tx);

//    update();
    //networkSync->getTx(txhash);
}

std::shared_ptr<Tx> TxModel::getTx(int row)
{
    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    return vault->getTx(txhash);
}

void TxModel::deleteTx(int row)
{
    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    vault->deleteTx(txhash);
    update();

    emit txDeleted();
}

QVariant TxModel::data(const QModelIndex& index, int role) const
{
    // Right-align numeric fields
    if (role == Qt::TextAlignmentRole && index.column() >= 3 && index.column() <= 6) {
        return Qt::AlignRight;
    }
    else {
        return QStandardItemModel::data(index, role);
    }
}

bool TxModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
    if (role == Qt::EditRole) {
        if (index.column() == 1) {
            // Keychain name edited.
            if (!vault) return false;

            try {
                // Get account role (sender/receiver)
                QStandardItem* typeItem = item(index.row(), 2);
                int txtype = typeItem->data(Qt::UserRole).toInt();
                if (txtype != SEND && txtype != RECEIVE) return false;

                // Get outpoint information
                QStandardItem* txHashItem = item(index.row(), 8);
                uchar_vector txhash;
                txhash.setHex(txHashItem->text().toStdString());
                uint32_t txindex = (uint32_t)txHashItem->data(Qt::UserRole).toInt();

                if (txtype == SEND)
                {
                    vault->setSendingLabel(txhash, txindex, value.toString().toStdString());
                }
                else
                {
                    vault->setReceivingLabel(txhash, txindex, value.toString().toStdString());
                }

                setItem(index.row(), index.column(), new QStandardItem(value.toString()));
                return true;
            }
            catch (const std::exception& e) {
                emit error(QString::fromStdString(e.what()));
            }
        }
        return false;
    }

    return true;
}

Qt::ItemFlags TxModel::flags(const QModelIndex& index) const
{
    if (index.column() == 1) {
        return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
    }

    return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}

<commit_msg>Color rows in TxModel.<commit_after>///////////////////////////////////////////////////////////////////////////////
//
// CoinVault
//
// txmodel.cpp
//
// Copyright (c) 2014 Eric Lombrozo
//
// All Rights Reserved.

#include "txmodel.h"

#include <CoinDB/SynchedVault.h>

#include <CoinQ/CoinQ_script.h>

#include <stdutils/stringutils.h>

#include <QStandardItemModel>
#include <QDateTime>
#include <QMessageBox>

#include "settings.h"
#include "coinparams.h"

#include "severitylogger.h"

using namespace CoinDB;
using namespace CoinQ::Script;
using namespace std;

TxModel::TxModel(QObject* parent)
    : QStandardItemModel(parent)
{
    base58_versions[0] = getCoinParams().pay_to_pubkey_hash_version();
    base58_versions[1] = getCoinParams().pay_to_script_hash_version();

    currencySymbol = getCurrencySymbol();

    setColumns();
}

TxModel::TxModel(CoinDB::Vault* vault, const QString& accountName, QObject* parent)
    : QStandardItemModel(parent)
{
    base58_versions[0] = getCoinParams().pay_to_pubkey_hash_version();
    base58_versions[1] = getCoinParams().pay_to_script_hash_version();

    currencySymbol = getCurrencySymbol();

    setColumns();
    setVault(vault);
    setAccount(accountName);
}

void TxModel::setColumns()
{
    QStringList columns;
    columns
        << tr("Time")
        << tr("Description")
        << tr("Type")
        << (tr("Amount") + " (" + getCurrencySymbol() + ")")
        << (tr("Fee") + " (" + getCurrencySymbol() + ")")
        << (tr("Balance") + " (" + getCurrencySymbol() + ")")
        << tr("Confirmations")
        << tr("Address")
        << tr("Transaction Hash");
    setHorizontalHeaderLabels(columns);
}

void TxModel::setVault(CoinDB::Vault* vault)
{
    this->vault = vault;
    accountName.clear();
}

void TxModel::setAccount(const QString& accountName)
{
    if (!vault) {
        throw std::runtime_error("Vault is not open.");
    }

    if (!vault->accountExists(accountName.toStdString())) {
        throw std::runtime_error("Account not found.");
    }

    this->accountName = accountName;
    update();
}

void TxModel::update()
{
    QString newCurrencySymbol = getCurrencySymbol();
    if (newCurrencySymbol != currencySymbol)
    {
        currencySymbol = newCurrencySymbol;
        setColumns();
    }

    removeRows(0, rowCount());

    if (!vault || accountName.isEmpty()) return;

    std::shared_ptr<BlockHeader> bestHeader = vault->getBestBlockHeader();

    std::vector<TxOutView> txoutviews = vault->getTxOutViews(accountName.toStdString(), "", TxOut::ROLE_BOTH, TxOut::BOTH, Tx::ALL, true);
    bytes_t last_txhash;
    typedef std::pair<unsigned long, uint32_t> sorting_info_t;
    typedef std::pair<QList<QStandardItem*>, int64_t> value_row_t; // used to associate output values with rows for computing balance
    typedef std::pair<sorting_info_t, value_row_t> sortable_row_t;
    QList<sortable_row_t> rows;
    for (auto& item: txoutviews) {
        QList<QStandardItem*> row;

        QDateTime utc;
        utc.setTime_t(item.tx_timestamp);
        QString time = utc.toLocalTime().toString();

        QString description = QString::fromStdString(item.role_label());
        if (description.isEmpty()) description = "Not available";

        // The type stuff is just to test the new db schema. It's all wrong, we're not going to use TxOutViews for this.
        TxType txType;
        QString type;
        QString amount;
        QString fee;
        int64_t value = 0;
        bytes_t this_txhash = item.tx_status == Tx::UNSIGNED ? item.tx_unsigned_hash : item.tx_hash;
        switch (item.role_flags) {
        case TxOut::ROLE_NONE:
            txType = NONE;
            type = tr("None");
            break;

        case TxOut::ROLE_SENDER:
            txType = SEND;
            type = tr("Send");
            amount = "-";
            value -= item.value;
            if (item.tx_has_all_outpoints && item.tx_fee() > 0) {
                if (this_txhash != last_txhash) {
                    fee = "-";
                    //fee += QString::number(item.tx_fee()/(1.0 * currency_divisor), 'g', 8);
                    fee += getFormattedCurrencyAmount(item.tx_fee());
                    value -= item.tx_fee();
                    last_txhash = this_txhash;
                }
                else {
                    fee = "||";
                }
            }
            break;

        case TxOut::ROLE_RECEIVER:
            txType = RECEIVE;
            type = tr("Receive");
            amount = "+";
            value += item.value;
            break;

        default:
            txType = UNKNOWN;
            type = tr("Unknown");
        }

        //amount += QString::number(item.value/(1.0 * currency_divisor), 'g', 8);
        amount += getFormattedCurrencyAmount(item.value);

        uint32_t nConfirmations = 0;
        QString confirmations;
        if (item.tx_status >= Tx::PROPAGATED) {
            if (bestHeader && item.height) {
                nConfirmations = bestHeader->height() + 1 - item.height;
                confirmations = QString::number(nConfirmations);
            }
            else {
                confirmations = "0";
            }
        }
        else if (item.tx_status == Tx::UNSIGNED) {
            confirmations = tr("Unsigned");
        }
        else if (item.tx_status == Tx::UNSENT) {
            confirmations = tr("Unsent");
        }
        QStandardItem* confirmationsItem = new QStandardItem(confirmations);
        confirmationsItem->setData(item.tx_status, Qt::UserRole);

        QString address = QString::fromStdString(getAddressForTxOutScript(item.script, base58_versions));
        QString hash = QString::fromStdString(uchar_vector(this_txhash).getHex());

        row.append(new QStandardItem(time));
        row.append(new QStandardItem(description));

        QStandardItem* typeItem = new QStandardItem(type);
        typeItem->setData(txType, Qt::UserRole);
        row.append(typeItem);

        row.append(new QStandardItem(amount));
        row.append(new QStandardItem(fee));
        row.append(new QStandardItem("")); // placeholder for balance, once sorted
        row.append(confirmationsItem);
        row.append(new QStandardItem(address));

        // Store the tx hash and tx index to uniquely identify the output.
        QStandardItem* hashItem = new QStandardItem(hash);
        hashItem->setData(item.tx_index, Qt::UserRole);
        row.append(hashItem);

        rows.append(std::make_pair(std::make_pair(item.tx_id, nConfirmations), std::make_pair(row, value)));
    }

    qSort(rows.begin(), rows.end(), [](const sortable_row_t& a, const sortable_row_t& b) {
        sorting_info_t a_info = a.first;
        sorting_info_t b_info = b.first;

        // if confirmation counts are equal
        if (a_info.second == b_info.second) {
            // if one value is positive and the other is negative, sort so that running balance remains positive
            if (a.second.second < 0 && b.second.second > 0) return true;
            if (a.second.second > 0 && b.second.second < 0) return false;

            // otherwise sort by descending index
            return (a_info.first > b_info.first);
        }

        // otherwise sort by ascending confirmation count
        return (a_info.second < b_info.second);
    });

    // iterate in reverse order to compute running balance
    int64_t balance = 0;
    for (int i = rows.size() - 1; i >= 0; i--) {
        balance += rows[i].second.second;
        //(rows[i].second.first)[5]->setText(QString::number(balance/(1.0 * currency_divisor), 'g', 8));
        (rows[i].second.first)[5]->setText(getFormattedCurrencyAmount(balance));
    }

    // iterate in forward order to display
    for (auto& row: rows) appendRow(row.second.first);
}

bytes_t TxModel::getTxHash(int row) const
{
    LOGGER(trace) << "TxModel::getTxHash(" << row << ")" << std::endl;

    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());
    return txhash;
}

void TxModel::signTx(int row)
{
    LOGGER(trace) << "TxModel::signTx(" << row << ")" << std::endl;

    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* typeItem = item(row, 6);
    int type = typeItem->data(Qt::UserRole).toInt();
    if (type != CoinDB::Tx::UNSIGNED) {
        throw std::runtime_error(tr("Transaction is already signed.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    std::vector<std::string> keychainNames;
    std::shared_ptr<Tx> tx = vault->signTx(txhash, keychainNames, true);
    if (!tx) throw std::runtime_error(tr("No new signatures were added.").toStdString());

    LOGGER(trace) << "TxModel::signTx - signature(s) added. raw tx: " << uchar_vector(tx->raw()).getHex() << std::endl;
    update();

    QString msg;
    if (keychainNames.empty())
    {
        msg = tr("No new signatures were added.");
    }
    else
    {
        msg = tr("Signatures added using keychain(s) ") + QString::fromStdString(stdutils::delimited_list(keychainNames, ", ")) + tr(".");
    }
    emit txSigned(msg);
}

void TxModel::sendTx(int row, CoinDB::SynchedVault* synchedVault)
{
    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    if (!synchedVault->isConnected()) {
        throw std::runtime_error(tr("Must be connected to network to send.").toStdString());
    }

    QStandardItem* typeItem = item(row, 6);
    int type = typeItem->data(Qt::UserRole).toInt();
    if (type == CoinDB::Tx::UNSIGNED) {
        throw std::runtime_error(tr("Transaction must be fully signed before sending.").toStdString());
    }
    else if (type == CoinDB::Tx::PROPAGATED) {
        throw std::runtime_error(tr("Transaction already sent.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    std::shared_ptr<CoinDB::Tx> tx = vault->getTx(txhash);
    Coin::Transaction coin_tx = tx->toCoinCore();
    synchedVault->sendTx(coin_tx);

    // TODO: Check transaction has propagated before changing status to PROPAGATED
//    tx->updateStatus(CoinDB::Tx::PROPAGATED);
//    vault->insertTx(tx);

//    update();
    //networkSync->getTx(txhash);
}

std::shared_ptr<Tx> TxModel::getTx(int row)
{
    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    return vault->getTx(txhash);
}

void TxModel::deleteTx(int row)
{
    if (row == -1 || row >= rowCount()) {
        throw std::runtime_error(tr("Invalid row.").toStdString());
    }

    QStandardItem* txHashItem = item(row, 8);
    uchar_vector txhash;
    txhash.setHex(txHashItem->text().toStdString());

    vault->deleteTx(txhash);
    update();

    emit txDeleted();
}

QVariant TxModel::data(const QModelIndex& index, int role) const
{
    // Right-align numeric fields
    if (role == Qt::TextAlignmentRole && index.column() >= 3 && index.column() <= 6)
    {
        return Qt::AlignRight;
    }
    else if (role == Qt::BackgroundRole)
    {
        QStandardItem* typeItem = item(index.row(), 2);
        int txtype = typeItem->data(Qt::UserRole).toInt();
        switch (txtype)
        {
        case SEND:      return QBrush(QColor(255, 175, 175));
        case RECEIVE:   return QBrush(QColor(175, 255, 175));
        }
    }

    return QStandardItemModel::data(index, role);
}

bool TxModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
    if (role == Qt::EditRole) {
        if (index.column() == 1) {
            // Keychain name edited.
            if (!vault) return false;

            try {
                // Get account role (sender/receiver)
                QStandardItem* typeItem = item(index.row(), 2);
                int txtype = typeItem->data(Qt::UserRole).toInt();
                if (txtype != SEND && txtype != RECEIVE) return false;

                // Get outpoint information
                QStandardItem* txHashItem = item(index.row(), 8);
                uchar_vector txhash;
                txhash.setHex(txHashItem->text().toStdString());
                uint32_t txindex = (uint32_t)txHashItem->data(Qt::UserRole).toInt();

                if (txtype == SEND)
                {
                    vault->setSendingLabel(txhash, txindex, value.toString().toStdString());
                }
                else
                {
                    vault->setReceivingLabel(txhash, txindex, value.toString().toStdString());
                }

                setItem(index.row(), index.column(), new QStandardItem(value.toString()));
                return true;
            }
            catch (const std::exception& e) {
                emit error(QString::fromStdString(e.what()));
            }
        }
        return false;
    }

    return true;
}

Qt::ItemFlags TxModel::flags(const QModelIndex& index) const
{
    if (index.column() == 1) {
        return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
    }

    return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}

<|endoftext|>


<commit_before>#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

int main() {
    /* Enter your code here. Read input from STDIN. Print output to STDOUT */
    int h = 0,
        v = 0,
        k = 0,
        T;
    cin >> T ;

    while(T--)
    {
        cin >> k ;
        h = ceil(k/2.0f) ;
        v = floor(k/2.0f) ;
        cout << h*v  << endl ;

    }

    return 0;
}
<commit_msg>Update hallowen-party.cpp<commit_after>//problem URL :
//https://www.hackerrank.com/challenges/halloween-party

#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

int main() {
    /* Enter your code here. Read input from STDIN. Print output to STDOUT */
    int h = 0,
        v = 0,
        k = 0,
        T;
    cin >> T ;

    while(T--)
    {
        cin >> k ;
        h = ceil(k/2.0f) ;
        v = floor(k/2.0f) ;
        cout << h*v  << endl ;

    }

    return 0;
}
<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org)             */
/*                                                                            */
/* Licensed under the Apache License, Version 2.0 (the "License"); you may    */
/* not use this file except in compliance with the License. You may obtain    */
/* a copy of the License at                                                   */
/*                                                                            */
/* http://www.apache.org/licenses/LICENSE-2.0                                 */
/*                                                                            */
/* Unless required by applicable law or agreed to in writing, software        */
/* distributed under the License is distributed on an "AS IS" BASIS,          */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/* See the License for the specific language governing permissions and        */
/* limitations under the License.                                             */
/* -------------------------------------------------------------------------- */

#include "Quota.h"

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int Quota::get_quota(const string& id, VectorAttribute ** va)
{
    map<string, Attribute *>::iterator it;
    VectorAttribute * q;

    istringstream iss(id);
    int           id_i;

    *va = 0;

    if ( id.empty() )
    {
        return -1;
    }

    iss >> id_i;

    if (iss.fail() || !iss.eof())
    {
        return -1;
    }

    for ( it = attributes.begin(); it != attributes.end(); it++)
    {
        q = static_cast<VectorAttribute *>(it->second);

        if (q->vector_value("ID") == id)
        {
            *va = q;
            return 0;
        }
    }

    return 0;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void Quota::add_to_quota(VectorAttribute * attr, const string& va_name, int num)
{
    istringstream iss;
    ostringstream oss;
    float         total;

    iss.str(attr->vector_value(va_name.c_str()));

    iss >> total;

    total += num;

    oss << total;

    attr->replace(va_name, oss.str());
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int Quota::set(vector<Attribute*> * new_quotas, string& error)
{
    vector<Attribute *>::iterator  it;

    VectorAttribute * iq;
    VectorAttribute * tq;
    string            id;

    for ( it = new_quotas->begin(); it != new_quotas->end(); it++)
    {
        iq = dynamic_cast<VectorAttribute *>(*it);

        if ( iq == 0 )
        {
            goto error_limits;
        }

        id = iq->vector_value("ID");

        if ( get_quota(id, &tq) == -1 )
        {
            goto error_limits;
        }

        if ( tq == 0 )
        {
            VectorAttribute * nq;

            if ((nq = new_quota(iq)) == 0)
            {
                goto error_limits;
            }

            add(nq);
        }
        else
        {
            if (update_limits(tq, iq) != 0)
            {
                goto error_limits;
            }
        }
    }

    return 0;

error_limits:
    ostringstream oss;

    oss <<  "Negative limits or bad format in quota " << template_name;

    if ( iq != 0 )
    {
        string * quota_str = iq->marshall(",");

        oss << " = [ " << *quota_str << " ]";

        delete quota_str;
    }

    error = oss.str();

    return -1;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

bool Quota::check_quota(const string& qid,
                        map<string, int>& usage_req,
                        string& error)
{
    VectorAttribute * q;
    map<string, int>::iterator it;

    bool check;
    int  limit;
    int  usage;

    if ( get_quota(qid, &q) == -1 )
    {
        return false;
    }

    // -------------------------------------------------------------------------
    //  Quota does not exist, create a new one
    // -------------------------------------------------------------------------
    if ( q == 0 )
    {
        map<string, string> values;

        for (int i=0; i < num_metrics; i++)
        {
            ostringstream usage_req_str;
            string        metrics_used = metrics[i];

            metrics_used += "_USED";

            it = usage_req.find(metrics[i]);

            if (it == usage_req.end())
            {
                usage_req_str << "0";
            }
            else
            {
                usage_req_str << it->second;
            }

            values.insert(make_pair(metrics[i],  "0"));
            values.insert(make_pair(metrics_used, usage_req_str.str()));
        }

        if (!qid.empty())
        {
            values.insert(make_pair("ID", qid));
        }

        add(new VectorAttribute(template_name, values));

        return true;
    }

    // -------------------------------------------------------------------------
    //  Check the quotas for each usage request
    // -------------------------------------------------------------------------
    for (int i=0; i < num_metrics; i++)
    {
        string metrics_used = metrics[i];

        metrics_used += "_USED";

        it = usage_req.find(metrics[i]);

        if (it == usage_req.end())
        {
            continue;
        }

        q->vector_value(metrics[i],   limit);
        q->vector_value(metrics_used.c_str(), usage);

        check = ( limit == 0 ) || ( ( usage + it->second ) <= limit );

        if ( !check )
        {
            ostringstream oss;

            oss << "Limit (" << limit << ") reached for " << metrics[i]
                << " in quota " << template_name;

            if ( !qid.empty() )
            {
                oss << "with ID: " << qid;
            }

            error = oss.str();

            return false;
        }
    }

    // -------------------------------------------------------------------------
    //  Add resource usage to quotas
    // -------------------------------------------------------------------------
    for (int i=0; i < num_metrics; i++)
    {
        string metrics_used = metrics[i];

        metrics_used += "_USED";

        it = usage_req.find(metrics[i]);

        if (it == usage_req.end())
        {
            continue;
        }

        add_to_quota(q, metrics_used, it->second);
    }

    return true;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void Quota::del_quota(const string& qid, map<string, int>& usage_req)
{
    VectorAttribute * q;
    map<string, int>::iterator it;

    if ( get_quota(qid, &q) == -1)
    {
        return;
    }

    if ( q == 0 )
    {
        return;
    }

    for (int i=0; i < num_metrics; i++)
    {
        string metrics_used = metrics[i];

        metrics_used += "_USED";

        it = usage_req.find(metrics[i]);

        if (it == usage_req.end())
        {
            continue;
        }

        add_to_quota(q, metrics_used, -it->second);
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int Quota::update_limits(VectorAttribute * quota, const VectorAttribute * va)
{
    string limit;
    int    limit_i;

    for (int i=0; i < num_metrics; i++)
    {
        limit = va->vector_value_str(metrics[i], limit_i);

        if ( limit_i < 0 )
        {
            return -1;
        }
        else if ( !limit.empty() )
        {
            quota->replace(metrics[i], limit);
        }
    }

    return 0;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

VectorAttribute * Quota::new_quota(VectorAttribute * va)
{
    map<string,string> limits;

    string limit;
    int    limit_i;

    for (int i=0; i < num_metrics; i++)
    {
        string metrics_used = metrics[i];

        metrics_used += "_USED";

        limit = va->vector_value_str(metrics[i], limit_i);

        if ( limit_i < 0 )
        {
            return 0;
        }
        else if ( limit.empty() )
        {
            limit = "0";
        }

        limits.insert(make_pair(metrics[i], limit));
        limits.insert(make_pair(metrics_used, "0"));
    }

    string id = va->vector_value("ID");

    if ( !id.empty() )
    {
        limits.insert(make_pair("ID", id));
    }

    return new VectorAttribute(template_name,limits);
}

<commit_msg>feature #1288: Fix setting quotas with missing limits.<commit_after>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org)             */
/*                                                                            */
/* Licensed under the Apache License, Version 2.0 (the "License"); you may    */
/* not use this file except in compliance with the License. You may obtain    */
/* a copy of the License at                                                   */
/*                                                                            */
/* http://www.apache.org/licenses/LICENSE-2.0                                 */
/*                                                                            */
/* Unless required by applicable law or agreed to in writing, software        */
/* distributed under the License is distributed on an "AS IS" BASIS,          */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/* See the License for the specific language governing permissions and        */
/* limitations under the License.                                             */
/* -------------------------------------------------------------------------- */

#include "Quota.h"

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int Quota::get_quota(const string& id, VectorAttribute ** va)
{
    map<string, Attribute *>::iterator it;
    VectorAttribute * q;

    istringstream iss(id);
    int           id_i;

    *va = 0;

    if ( id.empty() )
    {
        return -1;
    }

    iss >> id_i;

    if (iss.fail() || !iss.eof())
    {
        return -1;
    }

    for ( it = attributes.begin(); it != attributes.end(); it++)
    {
        q = static_cast<VectorAttribute *>(it->second);

        if (q->vector_value("ID") == id)
        {
            *va = q;
            return 0;
        }
    }

    return 0;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void Quota::add_to_quota(VectorAttribute * attr, const string& va_name, int num)
{
    istringstream iss;
    ostringstream oss;
    float         total;

    iss.str(attr->vector_value(va_name.c_str()));

    iss >> total;

    total += num;

    oss << total;

    attr->replace(va_name, oss.str());
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int Quota::set(vector<Attribute*> * new_quotas, string& error)
{
    vector<Attribute *>::iterator  it;

    VectorAttribute * iq;
    VectorAttribute * tq;
    string            id;

    for ( it = new_quotas->begin(); it != new_quotas->end(); it++)
    {
        iq = dynamic_cast<VectorAttribute *>(*it);

        if ( iq == 0 )
        {
            goto error_limits;
        }

        id = iq->vector_value("ID");

        if ( get_quota(id, &tq) == -1 )
        {
            goto error_limits;
        }

        if ( tq == 0 )
        {
            VectorAttribute * nq;

            if ((nq = new_quota(iq)) == 0)
            {
                goto error_limits;
            }

            add(nq);
        }
        else
        {
            if (update_limits(tq, iq) != 0)
            {
                goto error_limits;
            }
        }
    }

    return 0;

error_limits:
    ostringstream oss;

    oss <<  "Negative limits or bad format in quota " << template_name;

    if ( iq != 0 )
    {
        string * quota_str = iq->marshall(",");

        oss << " = [ " << *quota_str << " ]";

        delete quota_str;
    }

    error = oss.str();

    return -1;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

bool Quota::check_quota(const string& qid,
                        map<string, int>& usage_req,
                        string& error)
{
    VectorAttribute * q;
    map<string, int>::iterator it;

    bool check;
    int  limit;
    int  usage;

    if ( get_quota(qid, &q) == -1 )
    {
        return false;
    }

    // -------------------------------------------------------------------------
    //  Quota does not exist, create a new one
    // -------------------------------------------------------------------------
    if ( q == 0 )
    {
        map<string, string> values;

        for (int i=0; i < num_metrics; i++)
        {
            ostringstream usage_req_str;
            string        metrics_used = metrics[i];

            metrics_used += "_USED";

            it = usage_req.find(metrics[i]);

            if (it == usage_req.end())
            {
                usage_req_str << "0";
            }
            else
            {
                usage_req_str << it->second;
            }

            values.insert(make_pair(metrics[i],  "0"));
            values.insert(make_pair(metrics_used, usage_req_str.str()));
        }

        if (!qid.empty())
        {
            values.insert(make_pair("ID", qid));
        }

        add(new VectorAttribute(template_name, values));

        return true;
    }

    // -------------------------------------------------------------------------
    //  Check the quotas for each usage request
    // -------------------------------------------------------------------------
    for (int i=0; i < num_metrics; i++)
    {
        string metrics_used = metrics[i];

        metrics_used += "_USED";

        it = usage_req.find(metrics[i]);

        if (it == usage_req.end())
        {
            continue;
        }

        q->vector_value(metrics[i],   limit);
        q->vector_value(metrics_used.c_str(), usage);

        check = ( limit == 0 ) || ( ( usage + it->second ) <= limit );

        if ( !check )
        {
            ostringstream oss;

            oss << "Limit of " << limit << " reached for " << metrics[i]
                << " quota in " << template_name;

            if ( !qid.empty() )
            {
                oss << " with ID: " << qid;
            }

            error = oss.str();

            return false;
        }
    }

    // -------------------------------------------------------------------------
    //  Add resource usage to quotas
    // -------------------------------------------------------------------------
    for (int i=0; i < num_metrics; i++)
    {
        string metrics_used = metrics[i];

        metrics_used += "_USED";

        it = usage_req.find(metrics[i]);

        if (it == usage_req.end())
        {
            continue;
        }

        add_to_quota(q, metrics_used, it->second);
    }

    return true;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void Quota::del_quota(const string& qid, map<string, int>& usage_req)
{
    VectorAttribute * q;
    map<string, int>::iterator it;

    if ( get_quota(qid, &q) == -1)
    {
        return;
    }

    if ( q == 0 )
    {
        return;
    }

    for (int i=0; i < num_metrics; i++)
    {
        string metrics_used = metrics[i];

        metrics_used += "_USED";

        it = usage_req.find(metrics[i]);

        if (it == usage_req.end())
        {
            continue;
        }

        add_to_quota(q, metrics_used, -it->second);
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int Quota::update_limits(VectorAttribute * quota, const VectorAttribute * va)
{
    string limit;
    int    limit_i;

    for (int i=0; i < num_metrics; i++)
    {
        limit = va->vector_value_str(metrics[i], limit_i);

        if ( limit_i < 0 ) //No quota, NaN or negative
        {
            return -1;
        }
        else
        {
            quota->replace(metrics[i], limit);
        }
    }

    return 0;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

VectorAttribute * Quota::new_quota(VectorAttribute * va)
{
    map<string,string> limits;

    string limit;
    int    limit_i;

    for (int i=0; i < num_metrics; i++)
    {
        string metrics_used = metrics[i];

        metrics_used += "_USED";

        limit = va->vector_value_str(metrics[i], limit_i);

        if ( limit_i < 0 ) //No quota, NaN or negative
        {
            limit = "0";
        }

        limits.insert(make_pair(metrics[i], limit));
        limits.insert(make_pair(metrics_used, "0"));
    }

    string id = va->vector_value("ID");

    if ( !id.empty() )
    {
        limits.insert(make_pair("ID", id));
    }

    return new VectorAttribute(template_name,limits);
}

<|endoftext|>


<commit_before><commit_msg>Handle osl_createThread failure<commit_after><|endoftext|>


<commit_before>#include <cstdio>
#include <algorithm>
#include <string>
#include <map>
#include <cstdlib>
using namespace std;

map <string ,int> Dict;
FILE *fp;
//debug
fp=stdio;
string getword(){
	char ch;
	string s;
	while((ch=getc(fp))!=EOF){
		if(ch=='-'){

			if(getc(fp)=='\n'){
				continue;
			}else{
				fseek(fp,ftell(fp)-1,SEEK_CUR);
				break;
			}
		}

		if(ch>'A'&&ch<'Z' || ch>'a'&&ch<'z')
			s.push_back(ch);
		else
			break;
	}
}
int main(int argc, char const *argv[])
{
	/* code */
	return 0;
}
<commit_msg>update uoj1109<commit_after>#include <cstdio>
#include <algorithm>
#include <string>
#include <map>
#include <cstdlib>
using namespace std;

map <string ,int> Dict;
FILE *fp;
//debug
fp=stdin;
string getword(){
	char ch;
	string s;
	while((ch=getc(fp))!=EOF){
		if(ch=='-'){

			if(getc(fp)=='\n'){
				continue;
			}else{
				fseek(fp,ftell(fp)-1,SEEK_CUR);
				break;
			}
		}

		if(ch>'A'&&ch<'Z' || ch>'a'&&ch<'z')
			s.push_back(ch);
		else
			break;
	}
}
int main(int argc, char const *argv[])
{
	/* code */
	return 0;
}
<|endoftext|>


<commit_before>#include <cstdio>
#include <algorithm>
#include <string>
#include <map>
using namespace std;

map <string ,int> IDcache;
int main(int argc, char const *argv[])
{
	/* code */
	return 0;
}
<commit_msg>update uoj1109<commit_after>#include <cstdio>
#include <algorithm>
#include <string>
#include <map>
#include <cfile>
using namespace std;

map <string ,int> Dict;

void getword(string &s){

}
int main(int argc, char const *argv[])
{
	/* code */
	return 0;
}
<|endoftext|>


<commit_before>#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <fstream>
#include <sys/time.h>

#ifdef USE_MPI
# include <mpi.h>
#endif

#include "tapas.h"

//#define DIM (3)
const constexpr int DIM = 3;
typedef double real_t;

struct float4 {
  real_t x;
  real_t y;
  real_t z;
  real_t w;
};

#ifdef NB
const int N = NB;
#else
const int N = 1 << 10;
#endif
const real_t OPS = 20. * N * N * 1e-9;
const real_t EPS2 = 1e-6;

typedef tapas::BodyInfo<float4, 0> BodyInfo;

#ifdef USE_MPI
#include "tapas/hot.h"
typedef tapas::Tapas<DIM, real_t,
                     BodyInfo, // BT
                     float4,   // Cell attr
                     float4,   // body attr
                     tapas::HOT<DIM, tapas::sfc::Morton>,
                     tapas::threading::Default> Tapas;
#else
#include "tapas/single_node_hot.h"
typedef tapas::Tapas<DIM, real_t, BodyInfo,
                     float4,
                     float4,
                     tapas::SingleNodeHOT<DIM, tapas::sfc::Morton>,
                     tapas::threading::Default> Tapas;
#endif

double get_time() {
  struct timeval tv;
  gettimeofday(&tv,NULL);
  return double(tv.tv_sec+tv.tv_usec*1e-6);
}

void P2P(float4 *target, float4 *source, int ni, int nj, float eps2) {
#pragma omp parallel for
  for (int i=0; i<ni; i++) {
    real_t ax = 0;
    real_t ay = 0;
    real_t az = 0;
    real_t phi = 0;
    real_t xi = source[i].x;
    real_t yi = source[i].y;
    real_t zi = source[i].z;
    for (int j=0; j<nj; j++) {
      real_t dx = source[j].x - xi;
      real_t dy = source[j].y - yi;
      real_t dz = source[j].z - zi;
      real_t R2 = dx * dx + dy * dy + dz * dz + eps2;
      real_t invR = 1.0f / std::sqrt(R2);
      real_t invR3 = source[j].w * invR * invR * invR;
      phi += source[j].w * invR;
      ax += dx * invR3;
      ay += dy * invR3;
      az += dz * invR3;
    }
    target[i].w = phi;
    target[i].x = ax;
    target[i].y = ay;
    target[i].z = az;
  }
}

static real_t distR2(const float4 &p, const float4 &q) {
  real_t dx = q.x - p.x;
  real_t dy = q.y - p.y;
  real_t dz = q.z - p.z;
  return dx * dx + dy * dy + dz * dz;
}

static void ComputeForce(Tapas::BodyIterator &p1,
                         float4 approx, real_t eps2) {
  real_t dx = approx.x - p1->x;
  real_t dy = approx.y - p1->y;
  real_t dz = approx.z - p1->z;
  real_t R2 = dx * dx + dy * dy + dz * dz + eps2;
  real_t invR = 1.0 / std::sqrt(R2);
  real_t invR3 = invR * invR * invR;
  p1.attr().x += dx * invR3 * approx.w;
  p1.attr().y += dy * invR3 * approx.w;
  p1.attr().z += dz * invR3 * approx.w;
  p1.attr().w += invR * approx.w;
}

static void approximate(Tapas::Cell &c) {
#ifdef DUMP
  std::cerr << "Approximate: " << c.key() << std::endl;
#endif

  if (c.nb() == 0) {
    c.attr().w = 0.0;
#if 0
    c.attr().x = 0.0;
    c.attr().y = 0.0;
    c.attr().z = 0.0;
#endif

#ifdef DUMP
    std::cerr << "Empty" << std::endl;
#endif

  } else if (c.nb() == 1) {
    c.attr() = c.body(0);
#ifdef DUMP
    std::cerr << "One particle" << std::endl;
#endif
  } else {
    tapas::Map(approximate, c.subcells());
    float4 center = {0.0, 0.0, 0.0, 0.0};
    for (int i = 0; i < c.nsubcells(); ++i) {
      Tapas::Cell &sc = c.subcell(i);
      center.w += sc.attr().w;
      center.x += sc.attr().x * sc.attr().w;
      center.y += sc.attr().y * sc.attr().w;
      center.z += sc.attr().z * sc.attr().w;
    }
    center.x /= center.w;
    center.y /= center.w;
    center.z /= center.w;
    c.attr() = center;
  }
}

static void interact(Tapas::Cell &c1, Tapas::Cell &c2, real_t theta) {
  {
    Stderr e("interact");
    e.out() << "Head." << std::endl;
    e.out() << "\t" << Tapas::Cell::SFC::Decode(c1.key()) << std::endl;
    e.out() << "\t" << Tapas::Cell::SFC::Decode(c2.key()) << std::endl;
  }
  if (c1.nb() == 0 || c2.nb() == 0) {
    return;
  } else if (!c1.IsLeaf()) {
    tapas::Map(interact, tapas::Product(c1.subcells(), c2), theta);
  } else if (c2.IsLeaf()) {
    {
      Stderr e("interact");
      e.out() << "Leaf/leaf." << std::endl;
      e.out() << "\t" << Tapas::Cell::SFC::Decode(c1.key()) << std::endl;
      e.out() << "\t" << Tapas::Cell::SFC::Decode(c2.key()) << std::endl;
    }

    // c1 and c2 have only one particle each. Calculate direct force.
    //tapas::Map(ComputeForce, tapas::Product(c1.particles(),
    //c2.particles()));
    tapas::Map(ComputeForce, c1.bodies(), c2.body(0), EPS2);
  } else {
    // use apploximation
    const float4 &p1 = c1.body(0);
    real_t d = std::sqrt(distR2(c2.attr(), p1));
    real_t s = c2.width(0);

    if ((s/ d) < theta) {
      {
        Stderr e("interact");
        e.out() << "Leaf/branch. far enough. approximate." << std::endl;
        e.out() << "\t" << Tapas::Cell::SFC::Decode(c1.key()) << std::endl;
        e.out() << "\t" << Tapas::Cell::SFC::Decode(c2.key()) << std::endl;
      }
      tapas::Map(ComputeForce, c1.bodies(), c2.attr(), EPS2);
    } else {
      {
        Stderr e("interact");
        e.out() << "Leaf/branch. close. recursive." << std::endl;
        e.out() << "\t" << Tapas::Cell::SFC::Decode(c1.key()) << std::endl;
        e.out() << "\t" << Tapas::Cell::SFC::Decode(c2.key()) << std::endl;
      }
      tapas::Map(interact, tapas::Product(c1, c2.subcells()), theta);
    }
  }
}

typedef tapas::Vec<DIM, real_t> Vec3;

float4 *calc(float4 *p, size_t np) {
  // PartitionBSP is a function that partitions the given set of
  // particles by the binary space partitioning. The result is a
  // octree for 3D particles and a quadtree for 2D particles.
  Tapas::Region r(Vec3(0.0, 0.0, 0.0), Vec3(1.0, 1.0, 1.0));
  Tapas::Cell *root = Tapas::Partition(p, np, r, 1);

  // FIXME: this line is commented out for debugging.
  //tapas::Map(approximate, *root); // or, simply: approximate(*root);

  real_t theta = 0.5;
  tapas::Map(interact, tapas::Product(*root, *root), theta);
  float4 *out = root->body_attrs();
  return out;
}

void setRandSeed(int rank, int size) {
  int seed;
  if (rank == 0) {
    if (getenv("TAPAS_SEED")) {
      seed = atoi(getenv("TAPAS_SEED"));
      std::cerr << "Seed = " << seed << std::endl;
    } else {
      seed = 0;
    }
  }

#ifdef USE_MPI
  MPI_Bcast(&seed, 1, MPI_INT, 0, MPI_COMM_WORLD);
#endif

  srand48(seed);
}

int main(int argc, char **argv) {
  int rank = 0; // MPI rank
  int size = 1; // MPI size

#ifdef USE_MPI
  int provided, required = MPI_THREAD_MULTIPLE;
  MPI_Init_thread(&argc, &argv, required, &provided);
  assert(provided >= required);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &size);
#endif

  // ALLOCATE
  // NOTE: Total number of particles is N * size (weak scaling)
  int N_total = N * size;
  float4 *sourceHost = new float4 [N];
  float4 *targetHost = new float4 [N];

  setRandSeed(rank, size);

  for (int i = 0; i < N_total; i++) {
    double x = drand48();
    double y = drand48();
    double z = drand48();
    double w = drand48() / N_total;

    if (N*rank <= i && i < N*(rank+1)) {
      int j = i % N;
      sourceHost[j].x = x;
      sourceHost[j].y = y;
      sourceHost[j].z = z;
      sourceHost[j].w = w;
    }
  }

  if (rank == 0) {
    std::cout << std::scientific << "N      : " << N_total
              << " (" << N << " per proc)"
              << std::endl;
  }

  float4 *targetTapas = calc(sourceHost, N);

  double tic = get_time();
  P2P(targetHost,sourceHost,N,N,EPS2);
  double toc = get_time();

#ifdef DUMP
  std::ofstream ref_out("bh_ref.txt");
  std::ofstream tapas_out("bh_tapas.txt");
#endif

  std::cout << std::scientific << "No SSE : " << toc-tic << " s : " << OPS / (toc-tic) << " GFlops" << std::endl;

  // COMPARE RESULTS
  if (size == 1) {
    real_t pd = 0, pn = 0, fd = 0, fn = 0;
    for( int i=0; i<N; i++ ) {
#ifdef DUMP
      ref_out << targetHost[i].x << " " << targetHost[i].y << " "
              << targetHost[i].z << " " << targetHost[i].w << std::endl;
      tapas_out << targetTapas[i].x << " " << targetTapas[i].y << " "
                << targetTapas[i].z << " " << targetTapas[i].w << std::endl;
#endif
      targetHost[i].w -= sourceHost[i].w / sqrtf(EPS2);
      targetTapas[i].w -= sourceHost[i].w / sqrtf(EPS2);
      pd += (targetHost[i].w - targetTapas[i].w) * (targetHost[i].w - targetTapas[i].w);
      pn += targetHost[i].w * targetHost[i].w;
      fd += (targetHost[i].x - targetTapas[i].x) * (targetHost[i].x - targetTapas[i].x)
            + (targetHost[i].y - targetTapas[i].y) * (targetHost[i].y - targetTapas[i].y)
            + (targetHost[i].z - targetTapas[i].z) * (targetHost[i].z - targetTapas[i].z);
      fn += targetHost[i].x * targetHost[i].x + targetHost[i].y * targetHost[i].y + targetHost[i].z * targetHost[i].z;
    }
    std::cout << std::scientific << "P ERR  : " << sqrtf(pd/pn) << std::endl;
    std::cout << std::scientific << "F ERR  : " << sqrtf(fd/fn) << std::endl;
  } else {
    std::cout << "Skipping result check" << std::endl;
  }

// DEALLOCATE
  delete[] sourceHost;
  delete[] targetHost;
  //delete[] targetTapas;

#ifdef USE_MPI
  MPI_Finalize();
#endif
}
<commit_msg>Refactoring of barnes-hut code:  - added command line options to control data size  - commented out debug dump for release build<commit_after>#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <fstream>
#include <sys/time.h>

#ifdef USE_MPI
# include <mpi.h>
#endif

#include "tapas.h"

//#define DIM (3)
typedef double real_t;

const constexpr int DIM = 3;
const real_t EPS2 = 1e-6;

struct float4 {
  real_t x;
  real_t y;
  real_t z;
  real_t w;
};

typedef tapas::BodyInfo<float4, 0> BodyInfo;

#ifdef USE_MPI
#include "tapas/hot.h"
typedef tapas::Tapas<DIM, real_t,
                     BodyInfo, // BT
                     float4,   // Cell attr
                     float4,   // body attr
                     tapas::HOT<DIM, tapas::sfc::Morton>,
                     tapas::threading::Default> Tapas;
#else
#include "tapas/single_node_hot.h"
typedef tapas::Tapas<DIM, real_t, BodyInfo,
                     float4,
                     float4,
                     tapas::SingleNodeHOT<DIM, tapas::sfc::Morton>,
                     tapas::threading::Default> Tapas;
#endif

double get_time() {
  struct timeval tv;
  gettimeofday(&tv,NULL);
  return double(tv.tv_sec+tv.tv_usec*1e-6);
}

void P2P(float4 *target, float4 *source, int ni, int nj, float eps2) {
#pragma omp parallel for
  for (int i=0; i<ni; i++) {
    real_t ax = 0;
    real_t ay = 0;
    real_t az = 0;
    real_t phi = 0;
    real_t xi = source[i].x;
    real_t yi = source[i].y;
    real_t zi = source[i].z;
    for (int j=0; j<nj; j++) {
      real_t dx = source[j].x - xi;
      real_t dy = source[j].y - yi;
      real_t dz = source[j].z - zi;
      real_t R2 = dx * dx + dy * dy + dz * dz + eps2;
      real_t invR = 1.0f / std::sqrt(R2);
      real_t invR3 = source[j].w * invR * invR * invR;
      phi += source[j].w * invR;
      ax += dx * invR3;
      ay += dy * invR3;
      az += dz * invR3;
    }
    target[i].w = phi;
    target[i].x = ax;
    target[i].y = ay;
    target[i].z = az;
  }
}

static real_t distR2(const float4 &p, const float4 &q) {
  real_t dx = q.x - p.x;
  real_t dy = q.y - p.y;
  real_t dz = q.z - p.z;
  return dx * dx + dy * dy + dz * dz;
}

static void ComputeForce(Tapas::BodyIterator &p1,
                         float4 approx, real_t eps2) {
  real_t dx = approx.x - p1->x;
  real_t dy = approx.y - p1->y;
  real_t dz = approx.z - p1->z;
  real_t R2 = dx * dx + dy * dy + dz * dz + eps2;
  real_t invR = 1.0 / std::sqrt(R2);
  real_t invR3 = invR * invR * invR;
  p1.attr().x += dx * invR3 * approx.w;
  p1.attr().y += dy * invR3 * approx.w;
  p1.attr().z += dz * invR3 * approx.w;
  p1.attr().w += invR * approx.w;
}

static void approximate(Tapas::Cell &c) {
#ifdef DUMP
  std::cerr << "Approximate: " << c.key() << std::endl;
#endif

  if (c.nb() == 0) {
    c.attr().w = 0.0;
#if 0
    c.attr().x = 0.0;
    c.attr().y = 0.0;
    c.attr().z = 0.0;
#endif

#ifdef DUMP
    std::cerr << "Empty" << std::endl;
#endif

  } else if (c.nb() == 1) {
    c.attr() = c.body(0);
#ifdef DUMP
    std::cerr << "One particle" << std::endl;
#endif
  } else {
    tapas::Map(approximate, c.subcells());
    float4 center = {0.0, 0.0, 0.0, 0.0};
    for (int i = 0; i < c.nsubcells(); ++i) {
      Tapas::Cell &sc = c.subcell(i);
      center.w += sc.attr().w;
      center.x += sc.attr().x * sc.attr().w;
      center.y += sc.attr().y * sc.attr().w;
      center.z += sc.attr().z * sc.attr().w;
    }
    center.x /= center.w;
    center.y /= center.w;
    center.z /= center.w;
    c.attr() = center;
  }
}

static void interact(Tapas::Cell &c1, Tapas::Cell &c2, real_t theta) {
#ifdef TAPAS_DEBUG
  {
    Stderr e("interact");
    e.out() << "Head." << std::endl;
    e.out() << "\t" << Tapas::Cell::SFC::Decode(c1.key()) << std::endl;
    e.out() << "\t" << Tapas::Cell::SFC::Decode(c2.key()) << std::endl;
  }
#endif

  if (c1.nb() == 0 || c2.nb() == 0) {
    return;
  } else if (!c1.IsLeaf()) {
    tapas::Map(interact, tapas::Product(c1.subcells(), c2), theta);
  } else if (c2.IsLeaf()) {
#ifdef TAPAS_DEBUG
    {
      Stderr e("interact");
      e.out() << "Leaf/leaf." << std::endl;
      e.out() << "\t" << Tapas::Cell::SFC::Decode(c1.key()) << std::endl;
      e.out() << "\t" << Tapas::Cell::SFC::Decode(c2.key()) << std::endl;
    }
#endif

    // c1 and c2 have only one particle each. Calculate direct force.
    //tapas::Map(ComputeForce, tapas::Product(c1.particles(),
    //c2.particles()));
    tapas::Map(ComputeForce, c1.bodies(), c2.body(0), EPS2);
  } else {
    // use apploximation
    const float4 &p1 = c1.body(0);
    real_t d = std::sqrt(distR2(c2.attr(), p1));
    real_t s = c2.width(0);

    if ((s/ d) < theta) {
#ifdef TAPAS_DEBUG
      {
        Stderr e("interact");
        e.out() << "Leaf/branch. far enough. approximate." << std::endl;
        e.out() << "\t" << Tapas::Cell::SFC::Decode(c1.key()) << std::endl;
        e.out() << "\t" << Tapas::Cell::SFC::Decode(c2.key()) << std::endl;
      }
#endif
      tapas::Map(ComputeForce, c1.bodies(), c2.attr(), EPS2);
    } else {
#ifdef TAPAS_DEBUG
      {
        Stderr e("interact");
        e.out() << "Leaf/branch. close. recursive." << std::endl;
        e.out() << "\t" << Tapas::Cell::SFC::Decode(c1.key()) << std::endl;
        e.out() << "\t" << Tapas::Cell::SFC::Decode(c2.key()) << std::endl;
      }
#endif
      tapas::Map(interact, tapas::Product(c1, c2.subcells()), theta);
    }
  }
}

typedef tapas::Vec<DIM, real_t> Vec3;

float4 *calc(float4 *p, size_t np) {
#ifdef USE_MPI
  int size = 0;
  MPI_Comm_size(MPI_COMM_WORLD, &size);
#endif

  // PartitionBSP is a function that partitions the given set of
  // particles by the binary space partitioning. The result is a
  // octree for 3D particles and a quadtree for 2D particles.
  Tapas::Region r(Vec3(0.0, 0.0, 0.0), Vec3(1.0, 1.0, 1.0));
  Tapas::Cell *root = Tapas::Partition(p, np, r, 1);

  // FIXME: this line is commented out for debugging.
  if (size == 1) {
    tapas::Map(approximate, *root); // or, simply: approximate(*root);
  }

  real_t theta = 0.5;
  tapas::Map(interact, tapas::Product(*root, *root), theta);
  float4 *out = root->body_attrs();
  return out;
}

void setRandSeed(int rank, int size) {
  int seed;
  if (rank == 0) {
    if (getenv("TAPAS_SEED")) {
      seed = atoi(getenv("TAPAS_SEED"));
      std::cerr << "Seed = " << seed << std::endl;
    } else {
      seed = 0;
    }
  }

#ifdef USE_MPI
  MPI_Bcast(&seed, 1, MPI_INT, 0, MPI_COMM_WORLD);
#endif

  srand48(seed);
}

// Total number of particles.
int N_total = -1;

void parseOption(int *argc, char ***argv) {
  int result;

#ifdef USE_MPI
  int size = 0;
  MPI_Comm_size(MPI_COMM_WORLD, &size);
#endif

  while((result = getopt(*argc, *argv, "s:w:")) != -1) {
    switch(result) {
      case 'w':
        N_total = atoi(optarg) * size;
        break;
      case 's':
        N_total = atoi(optarg);
        break;
      case '?':
        std::cerr << "Usage:"
                  << "   $ " << (*argv)[0] << " -w N_per_proc -s N_total" << std::endl;
        exit(0);
        break;
    }
  }

  *argc = optind;
}

int main(int argc, char **argv) {
  int rank = 0; // MPI rank
  int size = 1; // MPI size

#ifdef USE_MPI
  int provided, required = MPI_THREAD_MULTIPLE;
  MPI_Init_thread(&argc, &argv, required, &provided);
  assert(provided >= required);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &size);
#endif

  parseOption(&argc, &argv);

  // NOTE: Total number of particles is N * size (weak scaling)
  const real_t OPS = 20. * N_total * N_total * 1e-9;
  assert(N_total % size == 0);
  int N = N_total / size;

  std::cout << "time n_total " << N_total << std::endl;
  std::cout << "time n_per_proc " << (N_total / size) << std::endl;

  float4 *sourceHost = new float4 [N];
  float4 *targetHost = new float4 [N];

  setRandSeed(rank, size);

  for (int i = 0; i < N_total; i++) {
    double x = drand48();
    double y = drand48();
    double z = drand48();
    double w = drand48() / N_total;

    if (N*rank <= i && i < N*(rank+1)) {
      int j = i % N;
      sourceHost[j].x = x;
      sourceHost[j].y = y;
      sourceHost[j].z = z;
      sourceHost[j].w = w;
    }
  }

  if (rank == 0) {
    std::cout << std::scientific << "N      : " << N_total
              << " (" << N << " per proc)"
              << std::endl;
  }

  double tic = get_time();
  float4 *targetTapas = calc(sourceHost, N);
  double toc = get_time();
  std::cout << "time total_calc "   << std::scientific << toc-tic << " s" << std::endl;
  std::cout << "time total_gflops " << std::scientific << OPS / (toc-tic) << " GFlops" << std::endl;

  if (size == 1) {
    double tic = get_time();
    P2P(targetHost,sourceHost,N,N,EPS2);
    double toc = get_time();
    std::cout << std::scientific << "No SSE : " << toc-tic << " s : "
              << OPS / (toc-tic) << " GFlops" << std::endl;
  }

#ifdef DUMP
  std::ofstream ref_out("bh_ref.txt");
  std::ofstream tapas_out("bh_tapas.txt");
#endif

  // COMPARE RESULTS
  if (size == 1) {
    real_t pd = 0, pn = 0, fd = 0, fn = 0;
    for( int i=0; i<N; i++ ) {
#ifdef DUMP
      ref_out << targetHost[i].x << " " << targetHost[i].y << " "
              << targetHost[i].z << " " << targetHost[i].w << std::endl;
      tapas_out << targetTapas[i].x << " " << targetTapas[i].y << " "
                << targetTapas[i].z << " " << targetTapas[i].w << std::endl;
#endif
      targetHost[i].w -= sourceHost[i].w / sqrtf(EPS2);
      targetTapas[i].w -= sourceHost[i].w / sqrtf(EPS2);
      pd += (targetHost[i].w - targetTapas[i].w) * (targetHost[i].w - targetTapas[i].w);
      pn += targetHost[i].w * targetHost[i].w;
      fd += (targetHost[i].x - targetTapas[i].x) * (targetHost[i].x - targetTapas[i].x)
            + (targetHost[i].y - targetTapas[i].y) * (targetHost[i].y - targetTapas[i].y)
            + (targetHost[i].z - targetTapas[i].z) * (targetHost[i].z - targetTapas[i].z);
      fn += targetHost[i].x * targetHost[i].x + targetHost[i].y * targetHost[i].y + targetHost[i].z * targetHost[i].z;
    }
    std::cout << std::scientific << "P ERR  : " << sqrtf(pd/pn) << std::endl;
    std::cout << std::scientific << "F ERR  : " << sqrtf(fd/fn) << std::endl;
  } else {
    std::cout << "Skipping result check" << std::endl;
  }

// DEALLOCATE
  delete[] sourceHost;
  delete[] targetHost;
  //delete[] targetTapas;

#ifdef USE_MPI
  MPI_Finalize();
#endif
}
<|endoftext|>


<commit_before>#pragma once

#ifndef TURRET_HPP
#define TURRET_HPP

#include "piece.hpp"
#include "gridtick_interface.hpp"

#include <ge/mesh.hpp>
#include <ge/mesh_actor.hpp>
#include <ge/texture_asset.hpp>

class turret : public piece
{
	ge::mesh_actor* mesh;

	boost::signals2::scoped_connection die_connect;

	std::array<int, 1> upgrades;
public:
	enum upgradesenum {
		DamagePlus,
		HealthPlus,
		RegenPlus,
	};
	void increment_upgrade(upgradesenum to_increment, bool positive)
	{
		if (positive)
			upgrades[to_increment]++;
		else
			upgrades[to_increment]--;
	}

	void damage(float damage) override
	{
		modify_health(-damage);
	}
	void initialize(glm::uvec3 location, Directions direction)
	{
		rotate(direction);
		piece::initialize(location);
		add_interface<turret, gridtick_interface>();
		mesh = ge::actor::factory<ge::mesh_actor>(this, "turret/turret.meshsettings").get();
		now.damage = 50;
		now.health = 100;
		die_connect = sig_die.connect([](piece* p) {
			p->set_parent(NULL);
		});
	}
	void tick_grid()
    {
		shoot();
	}
	void shoot()
	{
		int range = 3;
		auto squares = squares_in_direction(get_grid_location(), my_direction, range);
		piece* tod;
		for (int x = 0; x < range; x++)
		{
			if (squares[x].size() != 0)
			{
				tod = squares[x][0];
				break;
			}
			else if (x==range-1)
			{
				return;
			}
		}
		tod->damage(now.damage);
	}
};

#endif  // TURRET_HPP
<commit_msg>turret buffs 3.0<commit_after>#pragma once

#ifndef TURRET_HPP
#define TURRET_HPP

#include "piece.hpp"
#include "gridtick_interface.hpp"

#include <ge/mesh.hpp>
#include <ge/mesh_actor.hpp>
#include <ge/texture_asset.hpp>

class turret : public piece
{
	ge::mesh_actor* mesh;

	boost::signals2::scoped_connection die_connect;

	std::array<int, 1> upgrades;
public:
	enum upgradesenum {
		DamagePlus,
		HealthPlus,
		RegenPlus,
	};
	void increment_upgrade(upgradesenum to_increment, bool positive)
	{
		if (positive)
			upgrades[to_increment]++;
		else
			upgrades[to_increment]--;
	}

	void damage(float damage) override
	{
		modify_health(-damage);
	}
	void initialize(glm::uvec3 location, Directions direction)
	{
		rotate(direction);
		piece::initialize(location);
		add_interface<turret, gridtick_interface>();
		mesh = ge::actor::factory<ge::mesh_actor>(this, "turret/turret.meshsettings").get();
		now.damage = 50;
		inital.health = 100;
		now.health = 100;
		die_connect = sig_die.connect([](piece* p) {
			p->set_parent(NULL);
		});
	}
	void tick_grid()
    {
		shoot();
	}
	void shoot()
	{
		int range = 3;
		auto squares = squares_in_direction(get_grid_location(), my_direction, range);
		piece* tod;
		for (int x = 0; x < range; x++)
		{
			if (squares[x].size() != 0)
			{
				tod = squares[x][0];
				break;
			}
			else if (x==range-1)
			{
				return;
			}
		}
		tod->damage(now.damage);
	}
};

#endif  // TURRET_HPP
<|endoftext|>