///////////////////////////////////////////////////////////////////////////////
//
// Schema.cpp
//
// Copyright (c) 2013-2014 Eric Lombrozo
//
// All Rights Reserved.
//

#include "Schema.h"

#include <stdutils/stringutils.h>

#include <CoinCore/hash.h>
#include <CoinCore/CoinNodeData.h>
#include <CoinCore/MerkleTree.h>
#include <CoinCore/hdkeys.h>
#include <CoinCore/aes.h>
#include <CoinCore/random.h>

#include <logger/logger.h>

// support for boost serialization
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

#include <cstring>

//#define ENABLE_CRYPTO

using namespace CoinDB;

/*
 * class Keychain
 */

Keychain::Keychain(const std::string& name, const secure_bytes_t& entropy, const secure_bytes_t& lock_key)
    : name_(name), hidden_(false)
{
    if (name.empty() || name[0] == '@') throw std::runtime_error("Invalid keychain name.");
    if (entropy.size() < 16) throw std::runtime_error("At least 128 bits of entropy must be supplied.");

    Coin::HDSeed hdSeed(entropy);
    Coin::HDKeychain hdKeychain(hdSeed.getMasterKey(), hdSeed.getMasterChainCode());

    depth_ = (uint32_t)hdKeychain.depth();
    parent_fp_ = hdKeychain.parent_fp();
    child_num_ = hdKeychain.child_num();
    chain_code_ = hdKeychain.chain_code();
    pubkey_ = hdKeychain.pubkey();
    privkey_ = hdKeychain.privkey();
    if (lock_key.empty())
    {
        privkey_salt_ = 0;
        privkey_ciphertext_ = privkey_;
    }
    else
    {
        privkey_salt_ = AES::random_salt();
        privkey_ciphertext_ = AES::encrypt(lock_key, privkey_, true, privkey_salt_);
    }
    privkey_.clear();
    hash_ = hdKeychain.full_hash();
}

Keychain& Keychain::operator=(const Keychain& source)
{
    base::operator=(source);

    depth_ = source.depth_;
    parent_fp_ = source.parent_fp_;
    child_num_ = source.child_num_;
    pubkey_ = source.pubkey_;

    chain_code_ = source.chain_code_;

    privkey_ = source.privkey_;
    privkey_ciphertext_ = source.privkey_ciphertext_;
    privkey_salt_ = source.privkey_salt_;

    parent_ = source.parent_;

    derivation_path_ = source.derivation_path_;

    uchar_vector_secure hashdata = pubkey_;
    hashdata += chain_code_;
    hash_ = ripemd160(sha256(hashdata));

    hidden_ = source.hidden_;

    return *this;
}

std::shared_ptr<Keychain> Keychain::child(uint32_t i, bool get_private, const secure_bytes_t& lock_key)
{
    if (get_private && !isPrivate()) throw std::runtime_error("Cannot get private child from nonprivate keychain.");
    if (get_private)
    {
        if (privkey_.empty()) throw std::runtime_error("Private key is locked.");
        Coin::HDKeychain hdkeychain(privkey_, chain_code_, child_num_, parent_fp_, depth_);
        hdkeychain = hdkeychain.getChild(i);
        std::shared_ptr<Keychain> child(new Keychain());
        child->parent_ = get_shared_ptr();
        child->pubkey_ = hdkeychain.pubkey();
        child->chain_code_ = hdkeychain.chain_code();

        child->privkey_ = hdkeychain.privkey();
        if (lock_key.empty())
        {
            child->privkey_salt_ = 0;
            child->privkey_ciphertext_ = privkey_;
        }
        else
        {
            child->privkey_salt_ = AES::random_salt();
            child->privkey_ciphertext_ = AES::encrypt(lock_key, child->privkey_, true, child->privkey_salt_);
        }
        child->privkey_.clear();

        child->child_num_ = hdkeychain.child_num();
        child->parent_fp_ = hdkeychain.parent_fp();
        child->depth_ = hdkeychain.depth();
        child->hash_ = hdkeychain.full_hash();
        child->derivation_path_ = derivation_path_;
        child->derivation_path_.push_back(i);
        return child;
    }
    else
    {
        Coin::HDKeychain hdkeychain(pubkey_, chain_code_, child_num_, parent_fp_, depth_);
        hdkeychain = hdkeychain.getChild(i);
        std::shared_ptr<Keychain> child(new Keychain());;
        child->parent_ = get_shared_ptr();
        child->pubkey_ = hdkeychain.pubkey();
        child->chain_code_ = hdkeychain.chain_code();
        child->child_num_ = hdkeychain.child_num();
        child->parent_fp_ = hdkeychain.parent_fp();
        child->depth_ = hdkeychain.depth();
        child->hash_ = hdkeychain.full_hash();
        child->derivation_path_ = derivation_path_;
        child->derivation_path_.push_back(i);
        return child;
    }
}

void Keychain::lock() const
{
    privkey_.clear();
}

void Keychain::unlock(const secure_bytes_t& lock_key) const
{
    if (!isPrivate()) throw std::runtime_error("Cannot unlock a nonprivate keychain.");
    if (privkey_salt_ == 0)
    {
        privkey_ = privkey_ciphertext_;
    }
    else
    {
        privkey_ = AES::decrypt(lock_key, privkey_ciphertext_, true, privkey_salt_);
    }
}

bool Keychain::isLocked() const
{
    return privkey_.empty();
}

void Keychain::encrypt(const secure_bytes_t& lock_key)
{
    if (!isPrivate()) throw std::runtime_error("Cannot encrypt a nonprivate keychain.");
    if (isLocked()) throw std::runtime_error("Keychain is locked.");

    privkey_salt_ = AES::random_salt();
    privkey_ciphertext_ = AES::encrypt(lock_key, privkey_, true, privkey_salt_);
}

void Keychain::decrypt()
{
    if (!isPrivate()) throw std::runtime_error("Cannot unencrypt a nonprivate keychain.");
    if (isLocked()) throw std::runtime_error("Keychain is locked.");

    privkey_salt_ = 0;
    privkey_ciphertext_ = privkey_;
}

secure_bytes_t Keychain::getSigningPrivateKey(uint32_t i, const std::vector<uint32_t>& derivation_path) const
{
    if (!isPrivate()) throw std::runtime_error("Missing private key.");
    if (isLocked()) throw std::runtime_error("Private key is locked.");

    // Remove initial zero from privkey if necessary
    secure_bytes_t stripped_privkey = (privkey_.size() > 32) ? secure_bytes_t(privkey_.begin() + 1, privkey_.end()) : privkey_;
    Coin::HDKeychain hdkeychain(stripped_privkey, chain_code_, child_num_, parent_fp_, depth_);
    for (auto k: derivation_path) { hdkeychain = hdkeychain.getChild(k); }
    return hdkeychain.getPrivateSigningKey(i);
}

bytes_t Keychain::getSigningPublicKey(uint32_t i, bool get_compressed, const std::vector<uint32_t>& derivation_path) const
{
    Coin::HDKeychain hdkeychain(pubkey_, chain_code_, child_num_, parent_fp_, depth_);
    for (auto k: derivation_path) { hdkeychain = hdkeychain.getChild(k); }
    return hdkeychain.getPublicSigningKey(i, get_compressed);
}

secure_bytes_t Keychain::privkey() const
{
    if (!isPrivate()) throw std::runtime_error("Keychain is nonprivate.");
    if (privkey_.empty()) throw std::runtime_error("Keychain is locked.");
    return privkey_;
}

void Keychain::importPrivateKey(const Keychain& source)
{
    privkey_ciphertext_ = source.privkey_ciphertext_;
    privkey_salt_ = source.privkey_salt_;
}

void Keychain::importBIP32(const secure_bytes_t& extkey, const secure_bytes_t& lock_key)
{
    Coin::HDKeychain hdKeychain(extkey);

    depth_ = (uint32_t)hdKeychain.depth();
    parent_fp_ = hdKeychain.parent_fp();
    child_num_ = hdKeychain.child_num();
    chain_code_ = hdKeychain.chain_code();
    privkey_.clear();
    if (hdKeychain.isPrivate())
    {
        if (!lock_key.empty())
        {
            privkey_salt_ = AES::random_salt();
            privkey_ciphertext_ = AES::encrypt(lock_key, hdKeychain.key(), true, privkey_salt_);
        }
        else
        {
            privkey_salt_ = 0;
            privkey_ciphertext_ = hdKeychain.key();
        }
    }
    else
    {
        privkey_salt_ = 0;
        privkey_ciphertext_.clear();
    }

    pubkey_ = hdKeychain.pubkey();
    hash_ = hdKeychain.full_hash();
}

secure_bytes_t Keychain::exportBIP32(bool export_private) const
{
    secure_bytes_t key;

    if (export_private)
    {
        if (!isPrivate()) throw std::runtime_error("Keychain is nonprivate.");
        if (isLocked()) throw std::runtime_error("Keychain is locked.");

        // Remove initial zero from privkey if necessary
        key = (privkey_.size() > 32) ? secure_bytes_t(privkey_.begin() + 1, privkey_.end()) : privkey_;
    }
    else
    {
        key = pubkey_;
    }

    return Coin::HDKeychain(key, chain_code_, child_num_, parent_fp_, depth_).extkey();
}

void Keychain::clearPrivateKey()
{
    privkey_.clear();
    privkey_ciphertext_.clear();
    privkey_salt_ = 0;
}

/*
 * class Key
 */

Key::Key(const std::shared_ptr<Keychain>& keychain, uint32_t index, bool compressed)
{
    root_keychain_ = keychain->root();
    derivation_path_ = keychain->derivation_path();
    index_ = index;

    pubkey_ = keychain->getSigningPublicKey(index_, compressed);
    updatePrivate();
}

secure_bytes_t Key::privkey() const
{
    if (!is_private_ || root_keychain_->isLocked()) return secure_bytes_t();
    return root_keychain_->getSigningPrivateKey(index_, derivation_path_);
}

// and a version that throws exceptions
secure_bytes_t Key::try_privkey() const
{
    if (!is_private_) throw std::runtime_error("Key::privkey - cannot get private key from nonprivate key object.");
    if (root_keychain_->isLocked()) throw std::runtime_error("Key::privkey - private key is locked.");

    return root_keychain_->getSigningPrivateKey(index_, derivation_path_);
}

/*
 * class Account
 */

Account::Account(const std::string& name, unsigned int minsigs, const KeychainSet& keychains, uint32_t unused_pool_size, uint32_t time_created, bool compressed_keys)
    : name_(name), minsigs_(minsigs), keychains_(keychains), unused_pool_size_(unused_pool_size), time_created_(time_created), compressed_keys_(compressed_keys)
{
    // TODO: Use exception classes
    if (name_.empty() || name[0] == '@') throw std::runtime_error("Invalid account name.");
    if (keychains_.size() > 15) throw std::runtime_error("Account can use at most 15 keychains.");
    if (minsigs > keychains_.size()) throw std::runtime_error("Account minimum signatures cannot exceed number of keychains.");
    if (minsigs < 1) throw std::runtime_error("Account must require at least one signature.");

    updateHash();
}

void Account::updateHash()
{
    std::vector<bytes_t> keychain_hashes;
    for (auto& keychain: keychains_) { keychain_hashes.push_back(keychain->hash()); }
    std::sort(keychain_hashes.begin(), keychain_hashes.end());

    uchar_vector data;
    data.push_back((unsigned char)minsigs_);
    for (auto& keychain_hash: keychain_hashes) { data += keychain_hash; }

    if (!compressed_keys_) { data.push_back(0x00); }

    hash_ = ripemd160(sha256(data));
}

AccountInfo Account::accountInfo() const
{
    std::vector<std::string> keychain_names;
    for (auto& keychain: keychains_) { keychain_names.push_back(keychain->name()); }

    std::vector<std::string> bin_names;
    uint32_t issued_script_count = 0;
    for (auto& bin: bins_)
    {
        bin_names.push_back(bin->name());
        if (bin->next_script_index() > 0)
        {
            issued_script_count += (bin->next_script_index() - 1);
        }
    }

    return AccountInfo(id_, name_, minsigs_, keychain_names, issued_script_count, unused_pool_size_, time_created_, bin_names, compressed_keys_);
}

std::shared_ptr<AccountBin> Account::addBin(const std::string& name)
{
    uint32_t index = bins_.size() + 1;
    std::shared_ptr<AccountBin> bin(new AccountBin(shared_from_this(), index, name));
    bins_.push_back(bin);
    return bin;
}

/*
 * class AccountBin
 */

AccountBin::AccountBin(std::shared_ptr<Account> account, uint32_t index, const std::string& name)
    : account_(account), index_(index), name_(name), script_count_(0), next_script_index_(0), minsigs_(account->minsigs())
{
    if (index == 0) throw std::runtime_error("Account bin index cannot be zero.");
    if (index == CHANGE_INDEX && name != CHANGE_BIN_NAME) throw std::runtime_error("Account bin index reserved for change.");
    if (index == DEFAULT_INDEX && name != DEFAULT_BIN_NAME) throw std::runtime_error("Account bin index reserved for default.");

    updateHash();
}

std::string AccountBin::account_name() const
{
    return account() ? account()->name() : std::string("@null");
}

SigningScriptVector AccountBin::generateSigningScripts()
{
    SigningScriptVector signingscripts;
    for (uint32_t i = 0; i < next_script_index_; i++)
    {
        std::string label;
        auto it = script_label_map_.find(i);
        if (it != script_label_map_.end())   { label = it->second; }
        SigningScript::status_t status = (index_ == CHANGE_INDEX) ? SigningScript::CHANGE : SigningScript::ISSUED;
        std::shared_ptr<SigningScript> signingscript(new SigningScript(shared_from_this(), i, label, status));
        signingscripts.push_back(signingscript);
    }

    script_count_ = next_script_index_ + unused_pool_size();
    for (uint32_t i = next_script_index_; i < script_count_; i++)
    {
        std::shared_ptr<SigningScript> signingscript(new SigningScript(shared_from_this(), i));
        signingscripts.push_back(signingscript);
    }

    return signingscripts;
}

uint32_t AccountBin::unused_pool_size() const
{
    std::shared_ptr<Account> account = account_.lock();
    return account ? account->unused_pool_size() : DEFAULT_UNUSED_POOL_SIZE;
}

void AccountBin::loadKeychains() const
{
    if (!keychains__.empty()) return;
    if (!account())
    {
        // If we do not have an account for this bin we cannot derive the keychains, so use the stored values.
        keychains__ = keychains_;
    }
    else
    {
        for (auto& keychain: account()->keychains())
        {
            std::shared_ptr<Keychain> child(keychain->child(index_));
            keychains__.insert(child);
        }
    }
}

void AccountBin::updateHash()
{
    loadKeychains();

    std::vector<bytes_t> keychain_hashes;
    for (auto& keychain: keychains__) { keychain_hashes.push_back(keychain->hash()); }
    std::sort(keychain_hashes.begin(), keychain_hashes.end());

    uchar_vector data;
    data.push_back((unsigned char)minsigs_);
    for (auto& keychain_hash: keychain_hashes) { data += keychain_hash; }

    hash_ = ripemd160(sha256(data));
}

std::shared_ptr<SigningScript> AccountBin::newSigningScript(const std::string& label)
{
    std::shared_ptr<SigningScript> signingscript(new SigningScript(shared_from_this(), script_count_++, label));
    return signingscript;
}

void AccountBin::markSigningScriptIssued(uint32_t script_index)
{
    if (script_index >= next_script_index_)
        next_script_index_ = script_index + 1;
}

void AccountBin::makeExport(const std::string& name)
{
    name_ = name;
    loadKeychains();
    keychains_ = keychains__;
    for (auto& keychain: keychains_) { keychain->name(""); }
    index_ = 0;
    account_.reset();
}

void AccountBin::makeImport()
{
    updateHash();
    keychains_.clear();
}

void AccountBin::setScriptLabel(uint32_t index, const std::string& label)
{
    if (!label.empty()) { script_label_map_[index] = label; }
}

/*
 * class SigningScript
 */

// static
std::string SigningScript::getStatusString(int status)
{
    std::vector<std::string> flags;
    if (status & UNUSED) flags.push_back("UNUSED");
    if (status & CHANGE) flags.push_back("CHANGE");
    if (status & ISSUED) flags.push_back("ISSUED");
    if (status & USED) flags.push_back("USED");
    if (flags.empty()) return "NONE";

    return stdutils::delimited_list(flags, " | ");
}

// static
std::vector<SigningScript::status_t> SigningScript::getStatusFlags(int status)
{
    std::vector<status_t> flags;
    if (status & UNUSED) flags.push_back(UNUSED);
    if (status & CHANGE) flags.push_back(CHANGE);
    if (status & ISSUED) flags.push_back(ISSUED);
    if (status & USED) flags.push_back(USED);
    return flags;
}

SigningScript::SigningScript(std::shared_ptr<AccountBin> account_bin, uint32_t index, const std::string& label, status_t status)
    : account_(account_bin->account()), account_bin_(account_bin), index_(index), label_(label), status_(status)
{
    if (!account_) throw std::runtime_error("SigningScript::SigningScript() - account is null.");

    auto& keychains = account_bin_->keychains();
    for (auto& keychain: keychains)
    {
        std::shared_ptr<Key> key(new Key(keychain, index, account_->compressed_keys()));
        keys_.push_back(key);
    }

    // sort keys into canonical order
    std::sort(keys_.begin(), keys_.end(), [](std::shared_ptr<Key> key1, std::shared_ptr<Key> key2) { return key1->pubkey() < key2->pubkey(); });

    std::vector<bytes_t> pubkeys;
    for (auto& key: keys_) { pubkeys.push_back(key->pubkey()); }
    CoinQ::Script::Script script(CoinQ::Script::Script::PAY_TO_MULTISIG_SCRIPT_HASH, account_bin->minsigs(), pubkeys);
    txinscript_ = script.txinscript(CoinQ::Script::Script::EDIT);
    txoutscript_ = script.txoutscript();

    account_bin_->setScriptLabel(index, label);
}

void SigningScript::label(const std::string& label)
{
    label_ = label;
    account_bin_->setScriptLabel(index_, label);
}

void SigningScript::status(status_t status)
{
    status_ = status;
    if (status > UNUSED) account_bin_->markSigningScriptIssued(index_);
}

void SigningScript::markUsed()
{
	if (account_bin_->index() == AccountBin::CHANGE_INDEX)
	{
		status_ = CHANGE;
	}
	else
	{
		status_ = USED;
	}

	account_bin_->markSigningScriptIssued(index_);
}

/*
 * class BlockHeader
 */

void BlockHeader::fromCoinCore(const Coin::CoinBlockHeader& blockheader, uint32_t height)
{
    hash_ = blockheader.hash();
    height_ = height;
    version_ = blockheader.version();
    prevhash_ = blockheader.prevBlockHash();
    merkleroot_ = blockheader.merkleRoot();
    timestamp_ = blockheader.timestamp();
    bits_ = blockheader.bits();
    nonce_ = blockheader.nonce();
}

Coin::CoinBlockHeader BlockHeader::toCoinCore() const
{
    return Coin::CoinBlockHeader(version_, timestamp_, bits_, nonce_, prevhash_, merkleroot_);
}

std::string BlockHeader::toJson() const
{
    std::stringstream ss;
    ss << "{"
       << "\"hash\":\"" << uchar_vector(hash_).getHex() << "\","
       << "\"height\":" << height_ << ","
       << "\"version\":" << version_ << ","
       << "\"prevhash\":\"" << uchar_vector(prevhash_).getHex() << "\","
       << "\"merkleroot\":\"" << uchar_vector(merkleroot_).getHex() << "\","
       << "\"timestamp\":" << timestamp_ << ","
       << "\"bits\":" << bits_ << ","
       << "\"nonce\":" << nonce_
       << "}";
    return ss.str();
}

void BlockHeader::updateHash()
{
    hash_ = Coin::CoinBlockHeader(version_, timestamp_, bits_, nonce_, prevhash_, merkleroot_).hash();
}

/*
 * class MerkleBlock
 */

void MerkleBlock::fromCoinCore(const Coin::MerkleBlock& merkleblock, uint32_t height)
{
    blockheader_ = std::shared_ptr<BlockHeader>(new BlockHeader(merkleblock.blockHeader, height));
    txcount_ = merkleblock.nTxs;
    hashes_.assign(merkleblock.hashes.begin(), merkleblock.hashes.end());
    for (auto& hash: hashes_) { std::reverse(hash.begin(), hash.end()); }
    flags_ = merkleblock.flags;
}

Coin::MerkleBlock MerkleBlock::toCoinCore() const
{
    Coin::MerkleBlock merkleblock;
    merkleblock.blockHeader = blockheader_->toCoinCore();
    merkleblock.nTxs = txcount_;
    merkleblock.hashes.assign(hashes_.begin(), hashes_.end());
    for (auto& hash: merkleblock.hashes) { std::reverse(hash.begin(), hash.end()); }
    merkleblock.flags = flags_;
    return merkleblock;
}

std::string MerkleBlock::toJson() const
{
    std::stringstream ss;
    ss << "{"
       << "\"header\":" << blockheader_->toJson() << ","
       << "\"txcount\":" << txcount_ << ","
       << "\"hashes\":[";
    bool addComma = false;
    for (auto& hash: hashes_)
    {
        if (addComma)   { ss << ","; }
        else            { addComma = true; }
        ss << "\"" << uchar_vector(hash).getHex() << "\"";
    }
    ss << "],"
       << "\"flags\":\"" << uchar_vector(flags_).getHex() << "\""
       << "}";
    return ss.str();
}

/*
 * class TxIn
 */

TxIn::TxIn(const Coin::TxIn& coin_txin)
{
	fromCoinCore(coin_txin);
}

TxIn::TxIn(const bytes_t& raw)
{
    Coin::TxIn coin_txin(raw);
	fromCoinCore(coin_txin);
}

void TxIn::fromCoinCore(const Coin::TxIn& coin_txin)
{
    outhash_ = coin_txin.getOutpointHash();
    outindex_ = coin_txin.getOutpointIndex();
    script_ = coin_txin.scriptSig;
    sequence_ = coin_txin.sequence;
}

Coin::TxIn TxIn::toCoinCore() const
{
    Coin::TxIn coin_txin;
    coin_txin.previousOut = Coin::OutPoint(outhash_, outindex_);
    coin_txin.scriptSig = script_;
    coin_txin.sequence = sequence_;
    return coin_txin;
}

bytes_t TxIn::unsigned_script() const
{
    using namespace CoinQ::Script;

    Script script(script_);
    script.clearSigs();
    return script.txinscript(Script::EDIT);
}

bytes_t TxIn::raw() const
{
    return toCoinCore().getSerialized();
}

void TxIn::outpoint(std::shared_ptr<TxOut> outpoint)
{
    outpoint_ = outpoint;
    if (!outpoint) return;

    std::shared_ptr<Tx> tx = outpoint->tx();
    // TODO: Tx exception class
    if (outindex_ != outpoint->txindex())
        throw std::runtime_error("TxIn::outpoint - incorrect outindex.");
    if (!tx)
        throw std::runtime_error("TxIn::outpoint - tx is null.");
    if (tx->status() == Tx::UNSIGNED)
        throw std::runtime_error("TxIn::outpoint - tx is missing signatures.");
    if (outhash_ != tx->hash())
        throw std::runtime_error("TxIn::outpoint - incorrect outhash.");
}

std::string TxIn::toJson() const
{
    std::stringstream ss;
    ss << "{"
       << "\"outhash\":\"" << uchar_vector(outhash_).getHex() << "\","
       << "\"outindex\":" << outindex_ << ","
       << "\"script\":\"" << uchar_vector(script_).getHex() << "\","
       << "\"sequence\":" << sequence_
       << "}";
    return ss.str();
}

/*
 * class TxOut
 */

// static
std::string TxOut::getStatusString(int flags)
{
    std::vector<std::string> str_flags;
    if (flags & UNSPENT)    str_flags.push_back("UNSPENT");
    if (flags & SPENT)      str_flags.push_back("SPENT");
    if (str_flags.empty())  return "NEITHER";
    return stdutils::delimited_list(str_flags, " | ");
}

// static
std::vector<TxOut::status_t> TxOut::getStatusFlags(int flags)
{
    std::vector<status_t> vflags;
    if (flags & UNSPENT)    vflags.push_back(UNSPENT);
    if (flags & SPENT)      vflags.push_back(SPENT);
    return vflags;
}

// static
std::string TxOut::getRoleString(int flags)
{
    std::vector<std::string> str_flags;
    if (flags & ROLE_SENDER)        str_flags.push_back("SEND");
    if (flags & ROLE_RECEIVER)      str_flags.push_back("RECEIVE");
    if (str_flags.empty())          return "NONE";
    return stdutils::delimited_list(str_flags, " | ");
}

// static
std::vector<TxOut::role_t> TxOut::getRoleFlags(int flags)
{
    std::vector<role_t> vflags;
    if (flags & ROLE_SENDER)        vflags.push_back(ROLE_SENDER);
    if (flags & ROLE_RECEIVER)      vflags.push_back(ROLE_RECEIVER);
    return vflags;
}

TxOut::TxOut(uint64_t value, std::shared_ptr<SigningScript> signingscript)
    : value_(value), status_(UNSPENT)
{
    this->signingscript(signingscript);
}

TxOut::TxOut(const Coin::TxOut& coin_txout)
    : value_(coin_txout.value), script_(coin_txout.scriptPubKey), status_(UNSPENT)
{
}

TxOut::TxOut(const bytes_t& raw)
{
    Coin::TxOut coin_txout(raw);
    value_ = coin_txout.value;
    script_ = coin_txout.scriptPubKey;
    status_ = UNSPENT;
}

void TxOut::spent(std::shared_ptr<TxIn> spent)
{
    spent_ = spent;
    status_ = spent ? SPENT : UNSPENT;
}

void TxOut::signingscript(std::shared_ptr<SigningScript> signingscript)
{
    if (!signingscript) throw std::runtime_error("TxOut::signingscript - null signingscript.");

    script_ = signingscript->txoutscript();
    receiving_account_ = signingscript->account();
    if (receiving_label_.empty()) { receiving_label_ = signingscript->label(); }
    account_bin_ = signingscript->account_bin();
    signingscript_ = signingscript;
}

Coin::TxOut TxOut::toCoinCore() const
{
    Coin::TxOut coin_txout;
    coin_txout.value = value_;
    coin_txout.scriptPubKey = script_;
    return coin_txout;
}

bytes_t TxOut::raw() const
{
    return toCoinCore().getSerialized();
}

std::string TxOut::toJson() const
{
    std::stringstream ss;
    ss << "{"
       << "\"value\":" << value_ << ","
       << "\"script\":\"" << uchar_vector(script_).getHex() << "\","
       << "\"sending_label\":\"" << sending_label_ << "\","
       << "\"receiving_label\":\"" << receiving_label_ << "\"";

    if (signingscript_ && signingscript_->contact())
    {
        ss << ",\"sender_username\":\"" << signingscript_->contact()->username() << "\"";
    }

    ss << "}";
    return ss.str();
}

/*
 * class Tx
 */

// static
std::string Tx::getStatusString(int status, bool lowercase)
{
    std::vector<std::string> flags;
    if (status & UNSIGNED) flags.push_back(lowercase ? "unsigned" : "UNSIGNED");
    if (status & UNSENT) flags.push_back(lowercase ? "unsent" : "UNSENT");
    if (status & SENT) flags.push_back(lowercase ? "send" : "SENT");
    if (status & PROPAGATED) flags.push_back(lowercase ? "propagated" : "PROPAGATED");
    if (status & CANCELED) flags.push_back(lowercase ? "canceled" : "CANCELED");
    if (status & CONFIRMED) flags.push_back(lowercase ? "confirmed" : "CONFIRMED");
    if (flags.empty()) return lowercase ? "no_status" : "NO_STATUS";
    return stdutils::delimited_list(flags, " | ");
}

// static
std::vector<Tx::status_t> Tx::getStatusFlags(int status)
{
    std::vector<status_t> flags;
    if (status & UNSIGNED) flags.push_back(UNSIGNED);
    if (status & UNSENT) flags.push_back(UNSENT);
    if (status & SENT) flags.push_back(SENT);
    if (status & PROPAGATED) flags.push_back(PROPAGATED);
    if (status & CANCELED) flags.push_back(CANCELED);
    if (status & CONFIRMED) flags.push_back(CONFIRMED);
    return flags;
}

void Tx::set(uint32_t version, const txins_t& txins, const txouts_t& txouts, uint32_t locktime, uint32_t timestamp, status_t status, bool conflicting)
{
    version_ = version;

    int i = 0;
    txins_.clear();
    for (auto& txin: txins)
    {
        txin->tx(shared_from_this());
        txin->txindex(i++);
        txins_.push_back(txin);
    }

    i = 0;
    txouts_.clear();
    for (auto& txout: txouts)
    {
        txout->tx(shared_from_this());
        txout->txindex(i++);
        txouts_.push_back(txout);
    }

    locktime_ = locktime;
    timestamp_ = timestamp;

    Coin::Transaction coin_tx = toCoinCore();

    if (missingSigCount())  { status_ = UNSIGNED; }
    else                    { status_ = status; hash_ = coin_tx.hash(); }

    conflicting_ = conflicting;

    coin_tx.clearScriptSigs();
    unsigned_hash_ = coin_tx.hash();
    updateTotals();
}

void Tx::set(Coin::Transaction coin_tx, uint32_t timestamp, status_t status, bool conflicting)
{
    //LOGGER(trace) << "Tx::set - fromCoinCore(coin_tx);" << std::endl;
    fromCoinCore(coin_tx);

    timestamp_ = timestamp;

    if (missingSigCount())  { status_ = UNSIGNED; }
    else                    { status_ = status; hash_ = coin_tx.hash(); }

    conflicting_ = conflicting;

    coin_tx.clearScriptSigs();
    unsigned_hash_ = coin_tx.hash();
    updateTotals();
}

void Tx::set(const bytes_t& raw, uint32_t timestamp, status_t status, bool conflicting)
{
    Coin::Transaction coin_tx(raw);
    fromCoinCore(coin_tx);
    timestamp_ = timestamp;

    if (missingSigCount())  { status_ = UNSIGNED; }
    else                    { status_ = status; hash_ = coin_tx.hash(); }

    conflicting_ = conflicting;

    coin_tx.clearScriptSigs();
    unsigned_hash_ = coin_tx.hash();
    updateTotals();
}

bool Tx::updateStatus(status_t status /* = NO_STATUS */)
{
    // Tx is not signed.
    if (missingSigCount())
    {
        status_ = UNSIGNED;
        hash_ = bytes_t();
        return true;
    }

    // Tx status changed from unsigned to signed.
    if (status_ == UNSIGNED)
    {
        switch (status)
        {
        case NO_STATUS:
            status_ = UNSENT;
            break;

        case UNSIGNED:
            status_ = PROPAGATED;
            break;

        default:
            status_ = status;
        }

        hash_ = toCoinCore().hash();
        return true;
    }

    // Only update the status if the new status is valid.
    if (status && status != UNSIGNED && status != status_)
    {
        status_ = status;
        return true;
    }

    return false;
}

Coin::Transaction Tx::toCoinCore() const
{
    Coin::Transaction coin_tx;
    coin_tx.version = version_;
    for (auto& txin:  txins_) { coin_tx.inputs.push_back(txin->toCoinCore()); }
    for (auto& txout: txouts_) { coin_tx.outputs.push_back(txout->toCoinCore()); }
    coin_tx.lockTime = locktime_;
    return coin_tx;
}

void Tx::setBlock(std::shared_ptr<BlockHeader> blockheader, uint32_t blockindex)
{
    blockheader_ = blockheader;
    blockindex_ = blockindex;
}

void Tx::blockheader(std::shared_ptr<BlockHeader> blockheader)
{
    blockheader_ = blockheader;
    if (blockheader)                { status_ = CONFIRMED;  }
    else if (status_ == CONFIRMED)  { status_ = PROPAGATED; }
}

bytes_t Tx::raw() const
{
    return toCoinCore().getSerialized();
}

void Tx::updateTotals()
{
    have_all_outpoints_ = true;
    txin_total_ = 0;
    for (auto& txin: txins_)
    {
        std::shared_ptr<TxOut> outpoint = txin->outpoint();
        if (!outpoint)
        {
            have_all_outpoints_ = false;
        }
        else
        {
            txin_total_ += outpoint->value();
        }
    }

    txout_total_ = 0;
    for (auto& txout: txouts_)
    {
        txout_total_ += txout->value();
    }
}

void Tx::shuffle_txins()
{
    int i = 0;
    std::random_shuffle(txins_.begin(), txins_.end());
    for (auto& txin: txins_) { txin->txindex(i++); }
}

void Tx::shuffle_txouts()
{
    int i = 0;
    std::random_shuffle(txouts_.begin(), txouts_.end());
    for (auto& txout: txouts_) { txout->txindex(i++); }
}

void Tx::fromCoinCore(const Coin::Transaction& coin_tx)
{
    version_ = coin_tx.version;

    int i = 0;
    txins_.clear();
    for (auto& coin_txin: coin_tx.inputs)
    {
        std::shared_ptr<TxIn> txin(new TxIn(coin_txin));
        txin->tx(shared_from_this());
        txin->txindex(i++);
        txins_.push_back(txin);
    }

    i = 0;
    txouts_.clear();
    for (auto& coin_txout: coin_tx.outputs)
    {
        std::shared_ptr<TxOut> txout(new TxOut(coin_txout));
        txout->tx(shared_from_this());
        txout->txindex(i++);
        txouts_.push_back(txout);
    }

    locktime_ = coin_tx.lockTime;
}

unsigned int Tx::missingSigCount() const
{
    // Assume for now all inputs belong to the same account.
    using namespace CoinQ::Script;
    unsigned int count = 0;
    for (auto& txin: txins_)
    {
        Script script(txin->script());
        unsigned int sigsneeded = script.sigsneeded();
        if (sigsneeded > count) count = sigsneeded;
    }
    return count;
}

std::set<bytes_t> Tx::missingSigPubkeys() const
{
    using namespace CoinQ::Script;
    std::set<bytes_t> pubkeys;
    for (auto& txin: txins_)
    {
        Script script(txin->script());
        std::vector<bytes_t> txinpubkeys = script.missingsigs();
        for (auto& txinpubkey: txinpubkeys) { pubkeys.insert(txinpubkey); }
    }
    return pubkeys;
}

std::set<bytes_t> Tx::presentSigPubkeys() const
{
    std::set<bytes_t> pubkeys;

    using namespace CoinQ::Script;
    Signer signer(toCoinCore(), true);
    for (auto& script: signer.getScripts())
    {
        std::vector<bytes_t> txinpubkeys = script.presentsigs();
        for (auto& txinpubkey: txinpubkeys) { pubkeys.insert(txinpubkey); }
    }
    return pubkeys;
}

CoinQ::Script::Signer Tx::signer() const
{
    return CoinQ::Script::Signer(toCoinCore(), true);
}

std::string Tx::toJson(bool includeRawHex) const
{
    std::stringstream ss;
    ss << "{"
       << "\"version\":" << version_ << ","
       << "\"locktime\":" << locktime_ << ","
       << "\"hash\":\"" << uchar_vector(hash()).getHex() << "\","
       << "\"unsignedhash\":\"" << uchar_vector(unsigned_hash()).getHex() << "\","
       << "\"status\":\"" << getStatusString(status_) << "\","
       << "\"height\":";
    if (blockheader_)   { ss << blockheader_->height(); }
    else                { ss << "null"; }
    ss << ","
       << "\"txins\":[";
    bool addComma = false;
    for (auto& txin: txins_)
    {
        if (addComma)   { ss << ","; }
        else            { addComma = true; }
        ss << txin->toJson();
    }
    ss << "],"
       << "\"txouts\":[";
    addComma = false;
    for (auto& txout: txouts_)
    {
        if (addComma)   { ss << ","; }
        else            { addComma = true; }
        ss << txout->toJson();
    }
    ss << "]";

    if (includeRawHex)
    {
        ss << ",\"rawhex\":\"" << uchar_vector(raw()).getHex() << "\"";
    }

    ss << "}";
    return ss.str();
}

std::string Tx::toSerialized() const
{
    std::stringstream ss;
    boost::archive::text_oarchive oa(ss);
    oa << *this;
    return ss.str();
}

void Tx::fromSerialized(const std::string& serialized)
{
    std::stringstream ss;
    ss << serialized;
    boost::archive::text_iarchive ia(ss);
    ia >> *this;
}


#include <boost/bind.hpp>
#include <gazebo/gazebo.hh>
#include <gazebo/physics/physics.hh>
#include <gazebo/sensors/sensors.hh>
#include <gazebo/common/common.hh>
#include <gazebo/transport/TransportTypes.hh>
#include <gazebo/msgs/MessageTypes.hh>
#include <gazebo/common/Time.hh>
#include <stdio.h>
#include <gazebo/math/gzmath.hh>
#include "flipper_control_msgs.hh"

#include <termios.h>
#include <iostream>

#define Gp 3.0  //0.05
#define Gi 0.00 //0.00005

namespace gazebo
{
class MobileBasePlugin : public ModelPlugin
{
  transport::NodePtr node;
  physics::ModelPtr  model;
  common::Time       simTime;

  transport::SubscriberPtr velSub;
  transport::SubscriberPtr flpSub;
  transport::SubscriberPtr statsSub;
  event::ConnectionPtr updateConnection;

  physics::JointPtr hinge1;
  physics::JointPtr hinge2;
  physics::JointPtr hinge3;
  physics::JointPtr hinge4;
  physics::JointPtr hinge5;
  physics::JointPtr hinge6;
  physics::JointPtr hinge7;
  physics::JointPtr hinge8;
  physics::JointPtr hinge9;
  physics::JointPtr hinge10;
  physics::JointPtr hinge11;
  physics::JointPtr hinge12;
  physics::JointPtr hinge13;
  physics::JointPtr hinge14;
  physics::JointPtr hinge15;
  physics::JointPtr hinge16;
  physics::JointPtr hinge17;
  physics::JointPtr hinge18;
  physics::JointPtr hinge19;
  physics::JointPtr hinge20;
  physics::JointPtr hinge21;
  physics::JointPtr hinge22;
  physics::JointPtr hinge23;
  physics::JointPtr hinge24;
  physics::JointPtr hinge25;
  physics::JointPtr hinge26;
  physics::JointPtr hinge27;
  physics::JointPtr hinge28;

  //sensors::RaySensorPtr laser;
  physics::LinkPtr  sensor;

  /// Wheel speed and gain
  double THETA[30];
  double gain;

  /// Distance between wheels on the same axis (Determined from SDF)
  double wheelSeparation;

  /// Radius of the wheels (Determined from SDF)
  double wheelRadius;

  /// Flipper target angle
  double FLP_FR, FLP_FL, FLP_RR, FLP_RL;

  /// Flipper angle controller
  double dev_FLP_FR;
  double dev_FLP_FL;
  double dev_FLP_RR;
  double dev_FLP_RL;
  double dev_FLP_FR_sum;
  double dev_FLP_FL_sum;
  double dev_FLP_RR_sum;
  double dev_FLP_RL_sum;

  public:
  MobileBasePlugin(void)
  {
    FLP_FR = FLP_FL = FLP_RR = FLP_RL = M_PI / 4;
    dev_FLP_FR = dev_FLP_FL = dev_FLP_RR = dev_FLP_RL = 0;
    dev_FLP_FR_sum = dev_FLP_FL_sum = dev_FLP_RR_sum = dev_FLP_RL_sum = 0;
    wheelRadius     = 0.2;
    wheelSeparation = 1;
    for(int i = 0; i < 30; i++)
      THETA[i] = 0;
  }

  void Load(physics::ModelPtr _model, sdf::ElementPtr _sdf)
  {
    // physics::WorldPtr world = physics::get_world("default");
    this->model = _model;
    this->node = transport::NodePtr(new transport::Node());
    this->node->Init(this->model->GetWorld()->GetName());
    if(this->LoadParams(_sdf))
    {
      this->velSub = this->node->Subscribe(
      std::string("~/") + this->model->GetName() + std::string("/vel_cmd"),
      &MobileBasePlugin::OnVelMsg, this);
      this->flpSub = this->node->Subscribe(
      std::string("~/") + this->model->GetName() + std::string("/flp_cmd"),
      &MobileBasePlugin::OnFlpMsg, this);
      this->updateConnection
        = event::Events::ConnectWorldUpdateBegin(
                  boost::bind(&MobileBasePlugin::OnUpdate, this));
    }
  }

  bool LoadParams(sdf::ElementPtr _sdf)
  {
    if(!_sdf->HasElement("gain"))
    {
        gzerr << "param [gain] not found\n";
        return false;
    }
    else
        this->gain = _sdf->Get<double>("gain");
    hinge1 = this->model->GetJoint("right_front");
    hinge2 = this->model->GetJoint("right_center1");
    hinge3 = this->model->GetJoint("right_center2");
    hinge4 = this->model->GetJoint("right_rear");

    hinge5 = this->model->GetJoint("left_front");
    hinge6 = this->model->GetJoint("left_center1");
    hinge7 = this->model->GetJoint("left_center2");
    hinge8 = this->model->GetJoint("left_rear");

    hinge9 = this->model->GetJoint("right_front_arm");
    hinge10 = this->model->GetJoint("right_rear_arm");
    hinge11 = this->model->GetJoint("left_front_arm");
    hinge12 = this->model->GetJoint("left_rear_arm");

    hinge13 = this->model->GetJoint("right_front_arm_wheel_1");
    hinge14 = this->model->GetJoint("right_front_arm_wheel_2");
    hinge15 = this->model->GetJoint("right_front_arm_wheel_3");

    hinge16 = this->model->GetJoint("left_front_arm_wheel_1");
    hinge17 = this->model->GetJoint("left_front_arm_wheel_2");
    hinge18 = this->model->GetJoint("left_front_arm_wheel_3");

    hinge19 = this->model->GetJoint("right_rear_arm_wheel_1");
    hinge20 = this->model->GetJoint("right_rear_arm_wheel_2");
    hinge21 = this->model->GetJoint("right_rear_arm_wheel_3");

    hinge22 = this->model->GetJoint("left_rear_arm_wheel_1");
    hinge23 = this->model->GetJoint("left_rear_arm_wheel_2");
    hinge24 = this->model->GetJoint("left_rear_arm_wheel_3");

    hinge25 = this->model->GetJoint("right_sub2");
    hinge26 = this->model->GetJoint("right_sub3");

    hinge27 = this->model->GetJoint("left_sub2");
    hinge28 = this->model->GetJoint("left_sub3");
    return true;
  }

  /////////////////////////////////////////////////
  void OnVelMsg(ConstPosePtr &_msg)
  {
    // gzmsg << "cmd_vel: " << msg->position().x() << ", "
    //       <<msgs::Convert(msg->orientation()).GetAsEuler().z<<std::endl;
    double vel_lin = _msg->position().x() / this->wheelRadius;
#if(GAZEBO_MAJOR_VERSION == 5)
    double vel_rot = -1 * msgs::Convert(_msg->orientation()).GetAsEuler().z
                     * (this->wheelSeparation / this->wheelRadius);
#endif
#if(GAZEBO_MAJOR_VERSION == 7)
    double vel_rot = -1 * msgs::ConvertIgn(_msg->orientation()).Euler().Z()
                     * (this->wheelSeparation / this->wheelRadius);
#endif
    set_velocity(vel_lin - vel_rot, vel_lin + vel_rot);
  }

  void set_velocity(double  vr, double  vl)
  {
    THETA[1] = vr;
    THETA[2] = vr;
    THETA[3] = vr;
    THETA[4] = vr;
    THETA[13] = vr;
    THETA[14] = vr;
    THETA[15] = vr;
    THETA[19] = vr;
    THETA[20] = vr;
    THETA[21] = vr;
    THETA[25] = vr;
    THETA[26] = vr;

    THETA[5] = vl;
    THETA[6] = vl;
    THETA[7] = vl;
    THETA[8] = vl;
    THETA[16] = vl;
    THETA[17] = vl;
    THETA[18] = vl;
    THETA[22] = vl;
    THETA[23] = vl;
    THETA[24] = vl;
    THETA[27] = vl;
    THETA[28] = vl;
  }

  /////////////////////////////////////////////////
  void OnFlpMsg(ConstFlipperControlPtr &_msg)
  {
    FLP_FR = _msg->fr();
    FLP_FL = _msg->fl();
    FLP_RR = _msg->rr();
    FLP_RL = _msg->rl();
  }

  /////////////////////////////////////////////////
  void MoveWheel(void)
  {
    hinge1->SetVelocity(0, THETA[1]);
    hinge2->SetVelocity(0, THETA[2]);
    hinge3->SetVelocity(0, THETA[3]);
    hinge4->SetVelocity(0, THETA[4]);
    hinge5->SetVelocity(0, THETA[5]);
    hinge6->SetVelocity(0, THETA[6]);
    hinge7->SetVelocity(0, THETA[7]);
    hinge8->SetVelocity(0, THETA[8]);
    hinge13->SetVelocity(0, THETA[13]);
    hinge14->SetVelocity(0, THETA[14]);
    hinge15->SetVelocity(0, THETA[15]);
    hinge16->SetVelocity(0, THETA[16]);
    hinge17->SetVelocity(0, THETA[17]);
    hinge18->SetVelocity(0, THETA[18]);
    hinge19->SetVelocity(0, THETA[19]);
    hinge20->SetVelocity(0, THETA[20]);
    hinge21->SetVelocity(0, THETA[21]);
    hinge22->SetVelocity(0, THETA[22]);
    hinge23->SetVelocity(0, THETA[23]);
    hinge24->SetVelocity(0, THETA[24]);
    hinge21->SetVelocity(0, THETA[25]);
    hinge22->SetVelocity(0, THETA[26]);
    hinge23->SetVelocity(0, THETA[27]);
    hinge24->SetVelocity(0, THETA[28]);
  }

  void MoveFlipper(void)
  {
    // Get current arm angle
    double flp_fr = hinge9->GetAngle(0).Radian();
    double flp_fl = hinge11->GetAngle(0).Radian();
    double flp_rr = hinge10->GetAngle(0).Radian();
    double flp_rl = hinge12->GetAngle(0).Radian();
//printf("right_front=%6.3f right_rear=%6.3f left_front=%6.3f left_rear=%6.3f \r ", flp_fr, flp_fl, flp_rr, flp_rl);
    // Calc anguler velocity of Front Right flipper
    dev_FLP_FR = flp_fr - (-FLP_FR);
    THETA[9] = dev_FLP_FR*(-1)*Gp + dev_FLP_FR_sum*(-1)*Gi;
    dev_FLP_FR_sum += dev_FLP_FR;
    // Calc anguler velocity of Front Left flipper
    dev_FLP_FL = flp_fl - (-FLP_FL);
    THETA[11] = dev_FLP_FL*(-1)*Gp + dev_FLP_FL_sum*(-1)*Gi;
    dev_FLP_FL_sum += dev_FLP_FL;
    // Calc anguler velocity of Rear Right flipper
    dev_FLP_RR = flp_rr - FLP_RR;
    THETA[10] = dev_FLP_RR*(-1)*Gp + dev_FLP_RR_sum*(-1)*Gi;
    dev_FLP_RR_sum += dev_FLP_RR;
    // Calc anguler velocity of Rear Left flipper
    dev_FLP_RL = flp_rl - FLP_RL;
    THETA[12] = dev_FLP_RL*(-1)*Gp + dev_FLP_RL_sum*(-1)*Gi;
    dev_FLP_RL_sum += dev_FLP_RL;
    // Set flipper anguler velocity
    hinge9->SetVelocity(0, THETA[9]);
    hinge10->SetVelocity(0, THETA[10]);
    hinge11->SetVelocity(0, THETA[11]);
    hinge12->SetVelocity(0, THETA[12]);
  }

  public:
  void OnUpdate()
  {
    MoveWheel();
    MoveFlipper();
  }
};

GZ_REGISTER_MODEL_PLUGIN(MobileBasePlugin)
}


#include "lm.h"
#include <fstream>
#include <assert.h>
#include <iostream>
#include <boost/algorithm/string.hpp>

void LM::load_chars(const std::string& fname)
{
    std::string line;
    std::ifstream ifs(fname.c_str());
    std::vector<std::string> splits;
    while (std::getline(ifs, line))
    {
        boost::trim(line);
        boost::split(splits, line, boost::is_any_of(" \t\n"));
        char_map[splits[0]] = atoi(splits[1].c_str());
        std::cout << splits[0] << " -> " << char_map[splits[0]] << std::endl;
    }
    ifs.close();
}

void LM::load_words(const std::string& fname)
{
    std::string line;
    std::ifstream ifs(fname.c_str());
    while (std::getline(ifs, line))
    {
        word_list.push_back(boost::trim_copy(line));
    }
    std::cout << "Loaded " << word_list.size() << " words" << std::endl;
}

int LM::get_word_id(const std::string& word_string)
{
    if (word_to_int.find(word_string) != word_to_int.end())
        return word_to_int[word_string];
    else
        return unk;
}

void LM::read_header(std::ifstream& ifs)
{
    std::string line = "";
    std::vector<std::string> splits;
    while (boost::trim_copy(line) != "\\data\\")
        std::getline(ifs, line);
    std::getline(ifs, line);
    assert(line.find("ngram 1") != std::string::npos);
    boost::trim(line);
    boost::split(splits, line, boost::is_any_of("="));
    num_words = atoi(splits[1].c_str());
    std::getline(ifs, line);
    assert(line.find("ngram 2") != std::string::npos);
    std::getline(ifs, line);
    if (line.find("ngram 3") != std::string::npos)
        is_trigram = true;
    else
        is_trigram = false;
}

void LM::load_ug(std::ifstream& ifs)
{
    int count = 0;
    std::string line;
    std::vector<std::string> splits;
    while (line.find("\\1-grams") == std::string::npos)
        std::getline(ifs, line);
    while (true)
    {
        std::getline(ifs, line);
        //std::cout << line << std::endl;
        boost::trim(line);
        boost::split(splits, line, boost::is_any_of(" \t"));
        if (splits.size() < 2)
            break;
        word_to_int[splits[1]] = count;
        if (splits.size() == 3)
        {
            ug_probs[count] = std::make_pair(LM_CONSTS::SCALE * atof(splits[0].c_str()),
                                             LM_CONSTS::SCALE * atof(splits[2].c_str()));
        }
        else
        {
            ug_probs[count] = std::make_pair(LM_CONSTS::SCALE * atof(splits[0].c_str()),
                                             0.0f);
        }
        count += 1;
    }
    std::cout << "Loaded " << count << " unigrams" << std::endl;
}

void LM::load_bg(std::ifstream& ifs)
{
    std::string line;
    std::vector<std::string> splits;
    while (line.find("\\2-grams") == std::string::npos)
        std::getline(ifs, line);
    int count = 0;
    while (true)
    {
        std::getline(ifs, line);
        boost::trim(line);
        boost::split(splits, line, boost::is_any_of(" \t"));
        if (splits.size() < 3 || splits[0] == "\\end\\")
            break;
        // TODO Handle trigrams backoffs here
        bg_probs[std::make_pair(word_to_int[splits[1]],
                word_to_int[splits[2]])] = LM_CONSTS::SCALE * atof(splits[0].c_str());
        count += 1;
    }
    std::cout << "Loaded " << count << " bigrams" << std::endl;
}

float LM::ug_prob(int wid)
{
    return ug_probs[wid].first;
}

float LM::bg_prob(int w1, int w2)
{
    float p = bg_probs[std::make_pair(w1, w2)];
    if (p == 0.0)
    {
        p += ug_probs[w1].second + ug_probs[w2].first;
    }
    return p;
}

float LM::score_bg(const std::string& sentence)
{
    std::vector<std::string> splits;
    std::string s = boost::trim_copy(sentence);
    boost::split(splits, s, boost::is_any_of(" \t"));
    std::vector<int> word_ids;
    for (int k = 0; k < splits.size(); k++)
    {
        word_ids.push_back(get_word_id(splits[k]));
    }

    float score = 0.0;
    std::cout << bg_prob(start, word_ids[0]) << std::endl;
    score += bg_prob(start, word_ids[0]);
    for (int k = 0; k < word_ids.size() - 1; k++)
    {
        std::cout << k << " " << bg_prob(word_ids[k], word_ids[k+1]) << std::endl;
        score += bg_prob(word_ids[k], word_ids[k+1]);
    }

    std::cout << bg_prob(word_ids[word_ids.size() - 1], end) << std::endl;
    score += bg_prob(word_ids[word_ids.size() - 1], end);
    return score;
}


#ifndef abstract_primitive_hpp
#define abstract_primitive_hpp

#include <iostream>
#include <Eigen/Core>
#include <QOpenGLFunctions_2_1>

namespace threedimutil
{
    class AbstractPrimitive : protected QOpenGLFunctions_2_1
    {
    public:
        // static void Initialize();
        // static void Draw();
        // static AbstractPrimitive& GetInstance();

    protected:
        void InitializeInternal()
        {
            if (ready_)
            {
                std::cerr << "Warning: Initialize() is called but it is already initialized. This call is ignored." << std::endl;
                return;
            }

            initializeOpenGLFunctions();

            CreateVertexData();

            this->glGenBuffers(1, &vertex_vbo_);
            this->glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo_);
            this->glBufferData(GL_ARRAY_BUFFER, sizeof(GLdouble) * vertices_.size(), vertices_.data(), GL_STATIC_DRAW);

            this->glGenBuffers(1, &normal_vbo_);
            this->glBindBuffer(GL_ARRAY_BUFFER, normal_vbo_);
            this->glBufferData(GL_ARRAY_BUFFER, sizeof(GLdouble) * normals_.size(), normals_.data(), GL_STATIC_DRAW);

            ready_ = true;
        }

        void DrawInternal()
        {
            if (!ready_) { InitializeInternal(); }

            this->glEnableClientState(GL_VERTEX_ARRAY);
            this->glEnableClientState(GL_NORMAL_ARRAY);

            this->glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo_);
            this->glVertexPointer(3, GL_DOUBLE, 0, NULL);

            this->glBindBuffer(GL_ARRAY_BUFFER, normal_vbo_);
            this->glNormalPointer(GL_DOUBLE, 0, NULL);

            this->glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(vertices_.cols()));

            this->glBindBuffer(GL_ARRAY_BUFFER, 0);

            this->glDisableClientState(GL_VERTEX_ARRAY);
            this->glDisableClientState(GL_NORMAL_ARRAY);
        }

        AbstractPrimitive() = default;
        ~AbstractPrimitive()
        {
            if (ready_)
            {
                this->glDeleteBuffers(1, &vertex_vbo_);
                this->glDeleteBuffers(1, &normal_vbo_);
            }
        }
        AbstractPrimitive(const AbstractPrimitive&)            = delete;
        AbstractPrimitive& operator=(const AbstractPrimitive&) = delete;
        AbstractPrimitive(AbstractPrimitive&&)                 = delete;
        AbstractPrimitive& operator=(AbstractPrimitive&&)      = delete;

        Eigen::MatrixXd vertices_;
        Eigen::MatrixXd normals_;

    private:
        virtual void CreateVertexData() = 0;

        bool ready_ = false;

        GLuint vertex_vbo_;
        GLuint normal_vbo_;
    };
}

#endif /* abstract_primitive_hpp */


// Boost.Geometry Index
//
// R-tree R*-tree next node choosing algorithm implementation
//
// Copyright (c) 2011-2014 Adam Wulkiewicz, Lodz, Poland.
//
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_INDEX_DETAIL_RTREE_RSTAR_CHOOSE_NEXT_NODE_HPP
#define BOOST_GEOMETRY_INDEX_DETAIL_RTREE_RSTAR_CHOOSE_NEXT_NODE_HPP

#include <algorithm>

#include <boost/geometry/algorithms/expand.hpp>

#include <boost/geometry/index/detail/algorithms/content.hpp>
#include <boost/geometry/index/detail/algorithms/intersection_content.hpp>
#include <boost/geometry/index/detail/algorithms/union_content.hpp>

#include <boost/geometry/index/detail/rtree/node/node.hpp>
#include <boost/geometry/index/detail/rtree/visitors/is_leaf.hpp>

namespace geofeatures_boost {} namespace boost = geofeatures_boost; namespace geofeatures_boost { namespace geometry { namespace index {

namespace detail { namespace rtree {

template <typename Value, typename Options, typename Box, typename Allocators>
class choose_next_node<Value, Options, Box, Allocators, choose_by_overlap_diff_tag>
{
    typedef typename rtree::node<Value, typename Options::parameters_type, Box, Allocators, typename Options::node_tag>::type node;
    typedef typename rtree::internal_node<Value, typename Options::parameters_type, Box, Allocators, typename Options::node_tag>::type internal_node;
    typedef typename rtree::leaf<Value, typename Options::parameters_type, Box, Allocators, typename Options::node_tag>::type leaf;

    typedef typename rtree::elements_type<internal_node>::type children_type;
    typedef typename children_type::value_type child_type;

    typedef typename Options::parameters_type parameters_type;

    typedef typename index::detail::default_content_result<Box>::type content_type;

public:
    template <typename Indexable>
    static inline size_t apply(internal_node & n,
                               Indexable const& indexable,
                               parameters_type const& parameters,
                               size_t node_relative_level)
    {
        ::geofeatures_boost::ignore_unused_variable_warning(parameters);

        children_type & children = rtree::elements(n);

        // children are leafs
        if ( node_relative_level <= 1 )
        {
            return choose_by_minimum_overlap_cost(children, indexable, parameters.get_overlap_cost_threshold());
        }
        // children are internal nodes
        else
            return choose_by_minimum_content_cost(children, indexable);
    }

private:
    template <typename Indexable>
    static inline size_t choose_by_minimum_overlap_cost(children_type const& children,
                                                        Indexable const& indexable,
                                                        size_t overlap_cost_threshold)
    {
        const size_t children_count = children.size();

        content_type min_content_diff = (std::numeric_limits<content_type>::max)();
        content_type min_content = (std::numeric_limits<content_type>::max)();
        size_t choosen_index = 0;

        // create container of children sorted by content enlargement needed to include the new value
        typedef geofeatures_boost::tuple<size_t, content_type, content_type> child_contents;

        typename rtree::container_from_elements_type<children_type, child_contents>::type children_contents;
        children_contents.resize(children_count);

        for ( size_t i = 0 ; i < children_count ; ++i )
        {
            child_type const& ch_i = children[i];

            // expanded child node's box
            Box box_exp(ch_i.first);
            geometry::expand(box_exp, indexable);

            // areas difference
            content_type content = index::detail::content(box_exp);
            content_type content_diff = content - index::detail::content(ch_i.first);

            children_contents[i] = geofeatures_boost::make_tuple(i, content_diff, content);

            if ( content_diff < min_content_diff ||
                 (content_diff == min_content_diff && content < min_content) )
            {
                min_content_diff = content_diff;
                min_content = content;
                choosen_index = i;
            }
        }

        // is this assumption ok? if min_content_diff == 0 there is no overlap increase?

        if ( min_content_diff < -std::numeric_limits<double>::epsilon() || std::numeric_limits<double>::epsilon() < min_content_diff )
        {
            size_t first_n_children_count = children_count;
            if ( 0 < overlap_cost_threshold && overlap_cost_threshold < children.size() )
            {
                first_n_children_count = overlap_cost_threshold;
                // rearrange by content_diff
                // in order to calculate nearly minimum overlap cost
                std::nth_element(children_contents.begin(), children_contents.begin() + first_n_children_count, children_contents.end(), content_diff_less);
            }

            // calculate minimum or nearly minimum overlap cost
            choosen_index = choose_by_minimum_overlap_cost_first_n(children, indexable, first_n_children_count, children_count, children_contents);
        }

        return choosen_index;
    }

    static inline bool content_diff_less(geofeatures_boost::tuple<size_t, content_type, content_type> const& p1, geofeatures_boost::tuple<size_t, content_type, content_type> const& p2)
    {
        return geofeatures_boost::get<1>(p1) < geofeatures_boost::get<1>(p2) ||
               (geofeatures_boost::get<1>(p1) == geofeatures_boost::get<1>(p2) && geofeatures_boost::get<2>(p1) < geofeatures_boost::get<2>(p2));
    }

    template <typename Indexable, typename ChildrenContents>
    static inline size_t choose_by_minimum_overlap_cost_first_n(children_type const& children,
                                                                Indexable const& indexable,
                                                                size_t const first_n_children_count,
                                                                size_t const children_count,
                                                                ChildrenContents const& children_contents)
    {
        BOOST_GEOMETRY_INDEX_ASSERT(first_n_children_count <= children_count, "unexpected value");
        BOOST_GEOMETRY_INDEX_ASSERT(children_contents.size() == children_count, "unexpected number of elements");

        // choose index with smallest overlap change value, or content change or smallest content
        size_t choosen_index = 0;
        content_type smallest_overlap_diff = (std::numeric_limits<content_type>::max)();
        content_type smallest_content_diff = (std::numeric_limits<content_type>::max)();
        content_type smallest_content = (std::numeric_limits<content_type>::max)();

        // for each child node
        for (size_t i = 0 ; i < first_n_children_count ; ++i )
        {
            child_type const& ch_i = children[i];

            Box box_exp(ch_i.first);
            // calculate expanded box of child node ch_i
            geometry::expand(box_exp, indexable);

            content_type overlap_diff = 0;

            // calculate overlap
            for ( size_t j = 0 ; j < children_count ; ++j )
            {
                if ( i != j )
                {
                    child_type const& ch_j = children[j];

                    content_type overlap_exp = index::detail::intersection_content(box_exp, ch_j.first);
                    if ( overlap_exp < -std::numeric_limits<content_type>::epsilon() || std::numeric_limits<content_type>::epsilon() < overlap_exp )
                    {
                        overlap_diff += overlap_exp - index::detail::intersection_content(ch_i.first, ch_j.first);
                    }
                }
            }

            content_type content = geofeatures_boost::get<2>(children_contents[i]);
            content_type content_diff = geofeatures_boost::get<1>(children_contents[i]);

            // update result
            if ( overlap_diff < smallest_overlap_diff ||
                ( overlap_diff == smallest_overlap_diff && ( content_diff < smallest_content_diff ||
                ( content_diff == smallest_content_diff && content < smallest_content ) )
                ) )
            {
                smallest_overlap_diff = overlap_diff;
                smallest_content_diff = content_diff;
                smallest_content = content;
                choosen_index = i;
            }
        }

        return choosen_index;
    }

    template <typename Indexable>
    static inline size_t choose_by_minimum_content_cost(children_type const& children, Indexable const& indexable)
    {
        size_t children_count = children.size();

        // choose index with smallest content change or smallest content
        size_t choosen_index = 0;
        content_type smallest_content_diff = (std::numeric_limits<content_type>::max)();
        content_type smallest_content = (std::numeric_limits<content_type>::max)();

        // choose the child which requires smallest box expansion to store the indexable
        for ( size_t i = 0 ; i < children_count ; ++i )
        {
            child_type const& ch_i = children[i];

            // expanded child node's box
            Box box_exp(ch_i.first);
            geometry::expand(box_exp, indexable);

            // areas difference
            content_type content = index::detail::content(box_exp);
            content_type content_diff = content - index::detail::content(ch_i.first);

            // update the result
            if ( content_diff < smallest_content_diff ||
                ( content_diff == smallest_content_diff && content < smallest_content ) )
            {
                smallest_content_diff = content_diff;
                smallest_content = content;
                choosen_index = i;
            }
        }

        return choosen_index;
    }
};

}} // namespace detail::rtree

}}} // namespace geofeatures_boost::geometry::index

#endif // BOOST_GEOMETRY_INDEX_DETAIL_RTREE_RSTAR_CHOOSE_NEXT_NODE_HPP


//
//  TDF SDK
//
//  Created by Sujan Reddy on 2019/04/16
//  Copyright 2019 Virtru Corporation
//

#define BOOST_TEST_MODULE test_gcm_decoding_suite

#include "gcm_decryption.h"
#include "bytes.h"
#include "tdf_exception.h"
#include "crypto_utils.h"

#include <boost/test/included/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(test_gcm_decoding_suite)

    constexpr size_t kChunkSize = 25u;
    constexpr size_t kBufferSize = 1024u;

    using namespace std::string_literals;
    using namespace virtru::crypto;

    const static auto kPlainText =
            "Ehrsam, Meyer, Smith and Tuchman invented the Cipher Block Chaining (CBC) mode of operation in 1976. "
            "In CBC mode, each block of plaintext is XORed with the previous ciphertext block before being encrypted. "
            "This way, each ciphertext block depends on all plaintext blocks processed up to that point. "
            "To make each message unique, an initialization vector must be used in the first block."s;

    const static auto kEncryptedAes256GCMBase64 =
            "RFo0e8Q+dFtuBtMh0VGZk5u7BlXwUZvSbtPEZNJlrm5b893voEVhuqnQNF7HoPAN/Un0OL72cUJiLc8h0ufHhA6L3VtXFqSXTr+ebsphfTW2HI"
            "kV7s3p4MD2eg/XwjsAt4FxkYV/FoeQti8TSlkCfytnq6ClqQnYYw00UKNHoAb7uromnTz/IBeRRvBxQgg26arS5AvIHD9UdN4LfAvTXcsn+OfK"
            "9mWhiM9A6WAJSr/D/LXgvn6YLmtzd5txDpYrpR/2H6AI1yJDuv5CXjrsxbK1kk2gORWSCqozACQXnMT1v3nnSwr+DT6rXZ/ZdS5jl3hOdqQnCU"
            "477M2pJ1mWh87pmb6w9TyGqvwpuvc2Y36Z03LbrXPJw/sQWhRDIUtWTnPJsHKch/H+Dbw6Tdj7HnJb4o/7+IT9GpvgnDgNJ55yfZsAU655+Cag"
            "X5tCvCA+hYixxsD+/J5e+frs+SBacKQzNeq01IxA0vsVkoSryG+4sFu3nZI/gtgtu1vL693B"s;

    const static std::array<std :: uint8_t, 32u> kSymmetricKey = {
            0xf0, 0x5b, 0x5f, 0xab, 0x91, 0x60, 0xf3, 0xc6,
            0x51, 0x6a, 0x83, 0x3e, 0x82, 0xa3, 0x56, 0x62,
            0x65, 0xb1, 0x68, 0x01, 0xf2, 0x9f, 0x9a, 0x55,
            0xe2, 0x01, 0xba, 0xc6, 0x8a, 0xb8, 0x91, 0xf6
    };

    const static std::array<std :: uint8_t, 12u> kIV = {
            0x1a, 0x5f, 0x26, 0xf9, 0x5f, 0x81,
            0xf4, 0x42, 0x94, 0x60, 0x3b, 0x73
    };

    /// For testing message data authenticity (integrity).
    static std::array<std :: uint8_t, 16u> kAuthTagNoAad = {0x6c, 0x61, 0xc4, 0x6b, 0x8f, 0x80, 0x8e, 0x27, 0x67, 0x76, 0xaa, 0x24, 0xbb, 0x1d, 0x10, 0x77};
    const static std::vector<std :: uint8_t> kAuthAad = {0xeb, 0xfe, 0xa8, 0x74, 0x64, 0x28, 0x80, 0x96, 0x7b, 0x8a, 0x2a, 0x5f};
    static std::array<std :: uint8_t, 16> kAuthTagForDefinedAad = {0xf3, 0xc1, 0x92, 0x81, 0x89, 0xe8, 0x9, 0x4, 0x61, 0x5f, 0xc1, 0x38, 0x5, 0x44, 0x7a, 0xb8};

    BOOST_AUTO_TEST_CASE(test_gcm_decoding_with_no_aad) {

        auto decoder = GCMDecryption::create(toBytes(kSymmetricKey), toBytes(kIV));
        auto cipherText = base64Decode(toBytes(kEncryptedAes256GCMBase64));

        ByteArray<kBufferSize > buffer;
        auto bufferSpan = WriteableBytes{buffer};

        // This extra span is not required because decrypt updates
        // the size but to exercise some span features.
        auto outBufferSpan = bufferSpan;
        decoder->decrypt(toBytes(cipherText), outBufferSpan);

        auto authTag = WriteableBytes{ toWriteableBytes(kAuthTagNoAad) };
        decoder->finish(authTag);

        BOOST_TEST(toBytes(kPlainText) == toBytes(bufferSpan.first(outBufferSpan.size())));
    }

    BOOST_AUTO_TEST_CASE(test_gcm_decoding_with_same_io_buffer) {

        auto cipherText = base64Decode(toBytes(kEncryptedAes256GCMBase64));
        std::vector<gsl::byte> buffer(cipherText.size());
        std::transform(cipherText.begin(), cipherText.end(), buffer.begin(),
                       [] (char c) { return gsl::byte(c); });

        auto writeableBytes = toWriteableBytes(buffer);
        auto decoder = GCMDecryption::create(toBytes(kSymmetricKey), toBytes(kIV));

        /// Passing the same buffer as input and output
        decoder->decrypt(writeableBytes, writeableBytes);

        auto authTag = WriteableBytes{ toWriteableBytes(kAuthTagNoAad) };
        decoder->finish(authTag);

        std::string decryptedMessage(reinterpret_cast<const char *>(&writeableBytes[0]), writeableBytes.size());
        BOOST_TEST(kPlainText == decryptedMessage);
    }

    BOOST_AUTO_TEST_CASE(test_gcm_encoding_with_stream_and_no_aad) {

        auto cipherText = base64Decode(toBytes(kEncryptedAes256GCMBase64));
        auto decoder = GCMDecryption::create(toBytes(kSymmetricKey), toBytes(kIV));

        ByteArray<kBufferSize> buffer;
        const auto bufferSpan = WriteableBytes{buffer};

        size_t numberOfEncryptedBytes = 0u;
        size_t numberOfBytesToEncrypt = cipherText.size();

        while (numberOfBytesToEncrypt) {

            const auto inBufferSpan = toBytes(cipherText).subspan(cipherText.size() - numberOfBytesToEncrypt,
                                                                  (std::min)(kChunkSize, numberOfBytesToEncrypt));

            auto outBufferSpan = bufferSpan.subspan(numberOfEncryptedBytes);
            decoder->decrypt(inBufferSpan, outBufferSpan);

            numberOfBytesToEncrypt -= inBufferSpan.size();
            numberOfEncryptedBytes += outBufferSpan.size();
        }

        auto authTag = WriteableBytes{ toWriteableBytes(kAuthTagNoAad) };
        decoder->finish(authTag);
        BOOST_TEST(toBytes(kPlainText) == toBytes(bufferSpan.first(numberOfEncryptedBytes)));
    }

    BOOST_AUTO_TEST_CASE(test_gcm_decoding_with_small_out_buffer ) {

        auto decoder = GCMDecryption::create(toBytes(kSymmetricKey), toBytes(kIV));
        auto cipherText = base64Decode(toBytes(kEncryptedAes256GCMBase64));

        std::vector<gsl :: byte> outBuffer(cipherText.size() - 1);
        auto outBufferSpan = WriteableBytes{outBuffer};

        BOOST_CHECK_THROW(decoder->decrypt(toBytes(cipherText), outBufferSpan), virtru::Exception);
    }

    BOOST_AUTO_TEST_CASE(test_gcm_decoding_with_defined_aad) {

        auto decoder = GCMDecryption::create(toBytes(kSymmetricKey), toBytes(kIV), toBytes(kAuthAad));
        auto cipherText = base64Decode(toBytes(kEncryptedAes256GCMBase64));

        gsl::byte buffer[kBufferSize];
        auto bufferSpan = WriteableBytes { buffer };

        decoder->decrypt(toBytes(cipherText), bufferSpan);

        auto authTag = WriteableBytes{ toWriteableBytes(kAuthTagForDefinedAad)};
        decoder->finish(authTag);

        BOOST_TEST(toBytes(kPlainText) == toBytes(bufferSpan));
    }

BOOST_AUTO_TEST_SUITE_END()


/*=============================================================================
    Copyright (c) 2001-2014 Joel de Guzman
    Copyright (c) 2001-2011 Hartmut Kaiser
    Copyright (c) 2011 Jan Frederick Eick
    Copyright (c) 2011 Christopher Jefferson
    Copyright (c) 2006 Stephen Nutt
    Copyright (c) 2019 T. Zachary Laine

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/
#ifndef BOOST_PARSER_DETAIL_NUMERIC_HPP
#define BOOST_PARSER_DETAIL_NUMERIC_HPP

#include <boost/spirit/home/x3/numeric/real_policies.hpp>
#include <boost/spirit/home/x3/support/numeric_utils/detail/extract_int.hpp>
#include <boost/spirit/home/x3/support/numeric_utils/extract_real.hpp>

namespace boost { namespace parser { namespace detail_spirit_x3 {

    // Copied from
    // boost/spirit/home/x3/support/numeric_utils/detail/extract_int.hpp
    // (Boost 1.67),and modified for use with iterator, sentinel pairs:

    ///////////////////////////////////////////////////////////////////////////
    //  extract_int_impl: main code for extracting integers
    ///////////////////////////////////////////////////////////////////////////
#define BOOST_PARSER_NUMERIC_INNER_LOOP(z, x, data)                             \
        if (!spirit::x3::detail::check_max_digits<MaxDigits>::call(count + leading_zeros) \
            || it == last)                                                      \
            break;                                                              \
        ch = *it;                                                               \
        if (!radix_check::is_valid(ch) || !extractor::call(ch, count, val))     \
            break;                                                              \
        ++it;                                                                   \
        ++count;                                                                \
    /**/

    template <
        typename T, unsigned Radix, unsigned MinDigits, int MaxDigits
      , typename Accumulator = spirit::x3::detail::positive_accumulator<Radix>
      , bool Accumulate = false
    >
    struct extract_int_impl
    {
        template <typename Iterator, typename Sentinel, typename Attribute>
        inline static bool
        parse_main(
            Iterator& first
          , Sentinel last
          , Attribute& attr)
        {
            typedef spirit::x3::detail::radix_traits<Radix> radix_check;
            typedef spirit::x3::detail::int_extractor<Radix, Accumulator, MaxDigits> extractor;
            typedef typename
                boost::detail::iterator_traits<Iterator>::value_type
            char_type;

            Iterator it = first;
            std::size_t leading_zeros = 0;
            if (!Accumulate)
            {
                // skip leading zeros
                while (it != last && *it == '0' && leading_zeros < MaxDigits)
                {
                    ++it;
                    ++leading_zeros;
                }
            }

            typedef typename
                spirit::x3::traits::attribute_type<Attribute>::type
            attribute_type;

            attribute_type val = Accumulate ? attr : attribute_type(0);
            std::size_t count = 0;
            char_type ch;

            while (true)
            {
                BOOST_PP_REPEAT(
                    3
                  , BOOST_PARSER_NUMERIC_INNER_LOOP, _)
            }

            if (count + leading_zeros >= MinDigits)
            {
                spirit::x3::traits::move_to(val, attr);
                first = it;
                return true;
            }
            return false;
        }

        template <typename Iterator, typename Sentinel>
        inline static bool
        parse(
            Iterator& first
          , Sentinel last
          , spirit::x3::unused_type)
        {
            T n = 0; // must calculate value to detect over/underflow
            return parse_main(first, last, n);
        }

        template <typename Iterator, typename Sentinel, typename Attribute>
        inline static bool
        parse(
            Iterator& first
          , Sentinel last
          , Attribute& attr)
        {
            return parse_main(first, last, attr);
        }
    };
#undef BOOST_PARSER_NUMERIC_INNER_LOOP

    ///////////////////////////////////////////////////////////////////////////
    //  extract_int_impl: main code for extracting integers
    //  common case where MinDigits == 1 and MaxDigits = -1
    ///////////////////////////////////////////////////////////////////////////
#define BOOST_PARSER_NUMERIC_INNER_LOOP(z, x, data)                             \
        if (it == last)                                                         \
            break;                                                              \
        ch = *it;                                                               \
        if (!radix_check::is_valid(ch))                                         \
            break;                                                              \
        if (!extractor::call(ch, count, val))                                   \
            return false;                                                       \
        ++it;                                                                   \
        ++count;                                                                \
    /**/

    template <typename T, unsigned Radix, typename Accumulator, bool Accumulate>
    struct extract_int_impl<T, Radix, 1, -1, Accumulator, Accumulate>
    {
        template <typename Iterator, typename Sentinel, typename Attribute>
        inline static bool
        parse_main(
            Iterator& first
          , Sentinel last
          , Attribute& attr)
        {
            typedef spirit::x3::detail::radix_traits<Radix> radix_check;
            typedef spirit::x3::detail::int_extractor<Radix, Accumulator, -1> extractor;
            typedef typename
                boost::detail::iterator_traits<Iterator>::value_type
            char_type;

            Iterator it = first;
            std::size_t count = 0;
            if (!Accumulate)
            {
                // skip leading zeros
                while (it != last && *it == '0')
                {
                    ++it;
                    ++count;
                }

                if (it == last)
                {
                    if (count == 0) // must have at least one digit
                        return false;
                    attr = 0;
                    first = it;
                    return true;
                }
            }

            typedef typename
                spirit::x3::traits::attribute_type<Attribute>::type
            attribute_type;

            attribute_type val = Accumulate ? attr : attribute_type(0);
            char_type ch = *it;

            if (!radix_check::is_valid(ch) || !extractor::call(ch, 0, val))
            {
                if (count == 0) // must have at least one digit
                    return false;
                spirit::x3::traits::move_to(val, attr);
                first = it;
                return true;
            }

            count = 0;
            ++it;
            while (true)
            {
                BOOST_PP_REPEAT(
                    3
                  , BOOST_PARSER_NUMERIC_INNER_LOOP, _)
            }

            spirit::x3::traits::move_to(val, attr);
            first = it;
            return true;
        }

        template <typename Iterator, typename Sentinel>
        inline static bool
        parse(
            Iterator& first
          , Sentinel last
          , spirit::x3::unused_type)
        {
            T n = 0; // must calculate value to detect over/underflow
            return parse_main(first, last, n);
        }

        template <typename Iterator, typename Sentinel, typename Attribute>
        inline static bool
        parse(
            Iterator& first
          , Sentinel last
          , Attribute& attr)
        {
            return parse_main(first, last, attr);
        }
    };

#undef BOOST_PARSER_NUMERIC_INNER_LOOP

    // Copied from boost/spirit/home/x3/support/numeric_utils/extract_int.hpp
    // (Boost 1.67), and modified for use with iterator, sentinel pairs:

    ///////////////////////////////////////////////////////////////////////////
    //  Extract the prefix sign (- or +), return true if a '-' was found
    ///////////////////////////////////////////////////////////////////////////
    template<typename Iterator, typename Sentinel>
    inline bool extract_sign(Iterator & first, Sentinel last)
    {
        (void)last;                  // silence unused warnings
        BOOST_ASSERT(first != last); // precondition

        // Extract the sign
        bool neg = *first == '-';
        if (neg || (*first == '+'))
        {
            ++first;
            return neg;
        }
        return false;
    }

    ///////////////////////////////////////////////////////////////////////////
    // Low level unsigned integer parser
    ///////////////////////////////////////////////////////////////////////////
    template <typename T, unsigned Radix, unsigned MinDigits, int MaxDigits
      , bool Accumulate = false>
    struct extract_uint
    {
        // check template parameter 'Radix' for validity
        static_assert(
            (Radix >= 2 && Radix <= 36),
            "Error Unsupported Radix");

        template <typename Iterator, typename Sentinel>
        inline static bool call(Iterator& first, Sentinel last, T& attr)
        {
            if (first == last)
                return false;

            typedef extract_int_impl<
                T
              , Radix
              , MinDigits
              , MaxDigits
              , spirit::x3::detail::positive_accumulator<Radix>
              , Accumulate>
            extract_type;

            Iterator save = first;
            if (!extract_type::parse(first, last, attr))
            {
                first = save;
                return false;
            }
            return true;
        }
    };

    ///////////////////////////////////////////////////////////////////////////
    // Low level signed integer parser
    ///////////////////////////////////////////////////////////////////////////
    template <typename T, unsigned Radix, unsigned MinDigits, int MaxDigits>
    struct extract_int
    {
        // check template parameter 'Radix' for validity
        static_assert(
            (Radix == 2 || Radix == 8 || Radix == 10 || Radix == 16),
            "Error Unsupported Radix");

        template <typename Iterator, typename Sentinel>
        inline static bool call(Iterator& first, Sentinel last, T& attr)
        {
            if (first == last)
                return false;

            typedef extract_int_impl<
                T, Radix, MinDigits, MaxDigits>
            extract_pos_type;

            typedef extract_int_impl<
                T, Radix, MinDigits, MaxDigits, spirit::x3::detail::negative_accumulator<Radix> >
            extract_neg_type;

            Iterator save = first;
            bool hit = detail_spirit_x3::extract_sign(first, last);
            if (hit)
                hit = extract_neg_type::parse(first, last, attr);
            else
                hit = extract_pos_type::parse(first, last, attr);

            if (!hit)
            {
                first = save;
                return false;
            }
            return true;
        }
    };

    // Copied from boost/spirit/home/x3/support/numeric_utils/extract_real.hpp
    // (Boost 1.67), and modified for use with iterator, sentinel pairs:

    template <typename T, typename RealPolicies>
    struct extract_real
    {
        template <typename Iterator, typename Sentinel, typename Attribute>
        static bool
        parse(Iterator& first, Sentinel last, Attribute& attr,
            RealPolicies const& p)
        {
            if (first == last)
                return false;
            Iterator save = first;

            // Start by parsing the sign. neg will be true if
            // we got a "-" sign, false otherwise.
            bool neg = detail_spirit_x3::extract_sign(first, last);

            // Now attempt to parse an integer
            T n = 0;
            bool got_a_number = p.parse_n(first, last, n);

            // If we did not get a number it might be a NaN, Inf or a leading
            // dot.
            if (!got_a_number)
            {
                // Check whether the number to parse is a NaN or Inf
                if (p.parse_nan(first, last, n) ||
                    p.parse_inf(first, last, n))
                {
                    // If we got a negative sign, negate the number
                    spirit::x3::traits::move_to(spirit::x3::extension::negate(neg, n), attr);
                    return true;    // got a NaN or Inf, return early
                }

                // If we did not get a number and our policies do not
                // allow a leading dot, fail and return early (no-match)
                if (!p.allow_leading_dot)
                {
                    first = save;
                    return false;
                }
            }

            bool e_hit = false;
            int frac_digits = 0;

            // Try to parse the dot ('.' decimal point)
            if (p.parse_dot(first, last))
            {
                // We got the decimal point. Now we will try to parse
                // the fraction if it is there. If not, it defaults
                // to zero (0) only if we already got a number.
                Iterator savef = first;
                if (p.parse_frac_n(first, last, n))
                {
                    // Optimization note: don't compute frac_digits if T is
                    // an unused_type. This should be optimized away by the compiler.
                    if (!is_same<T, spirit::x3::unused_type>::value)
                        frac_digits =
                            static_cast<int>(std::distance(savef, first));
                    BOOST_ASSERT(frac_digits >= 0);
                }
                else if (!got_a_number || !p.allow_trailing_dot)
                {
                    // We did not get a fraction. If we still haven't got a
                    // number and our policies do not allow a trailing dot,
                    // return no-match.
                    first = save;
                    return false;
                }

                // Now, let's see if we can parse the exponent prefix
                e_hit = p.parse_exp(first, last);
            }
            else
            {
                // No dot and no number! Return no-match.
                if (!got_a_number)
                {
                    first = save;
                    return false;
                }

                // If we must expect a dot and we didn't see an exponent
                // prefix, return no-match.
                e_hit = p.parse_exp(first, last);
                if (p.expect_dot && !e_hit)
                {
                    first = save;
                    return false;
                }
            }

            if (e_hit)
            {
                // We got the exponent prefix. Now we will try to parse the
                // actual exponent. It is an error if it is not there.
                int exp = 0;
                if (p.parse_exp_n(first, last, exp))
                {
                    // Got the exponent value. Scale the number by
                    // exp-frac_digits.
                    spirit::x3::extension::scale(exp, frac_digits, n);
                }
                else
                {
                    // Oops, no exponent, return no-match.
                    first = save;
                    return false;
                }
            }
            else if (frac_digits)
            {
                // No exponent found. Scale the number by -frac_digits.
                spirit::x3::extension::scale(-frac_digits, n);
            }
            else if (spirit::x3::extension::is_equal_to_one(n))
            {
                // There is a chance of having to parse one of the 1.0#...
                // styles some implementations use for representing NaN or Inf.

                // Check whether the number to parse is a NaN or Inf
                if (p.parse_nan(first, last, n) ||
                    p.parse_inf(first, last, n))
                {
                    // If we got a negative sign, negate the number
                    spirit::x3::traits::move_to(spirit::x3::extension::negate(neg, n), attr);
                    return true;    // got a NaN or Inf, return immediately
                }
            }

            // If we got a negative sign, negate the number
            spirit::x3::traits::move_to(spirit::x3::extension::negate(neg, n), attr);

            // Success!!!
            return true;
        }
    };

}}}

#endif


#include "examples/cuttingstock.hpp"
#include "examples/multipleknapsack.hpp"
#include "examples/binpackingwithconflicts.hpp"
#include "examples/capacitatedvehiclerouting.hpp"
#include "examples/vehicleroutingwithtimewindows.hpp"
#include "examples/capacitatedopenvehiclerouting.hpp"
#include "examples/parallelschedulingwithfamilysetuptimestwct.hpp"
#include "examples/starobservationscheduling.hpp"

#include "columngenerationsolver/read_args.hpp"

#include <boost/program_options.hpp>

using namespace columngenerationsolver;

namespace po = boost::program_options;

void run(
        std::string algorithm,
        std::string columngeneration_args_string,
        const optimizationtools::Info& info,
        Parameters& p)
{
    std::vector<std::string> algorithm_args
        = boost::program_options::split_unix(algorithm);
    std::vector<char*> algorithm_argv;
    for (Counter i = 0; i < (Counter)algorithm_args.size(); ++i)
        algorithm_argv.push_back(const_cast<char*>(algorithm_args[i].c_str()));

    ColumnGenerationOptionalParameters columngeneration_parameters
        = read_columngeneration_args(columngeneration_args_string);

    if (algorithm_args[0] == "columngeneration") {
        columngeneration_parameters.info = info;
        columngeneration(p, columngeneration_parameters);
    } else if (algorithm_args[0] == "greedy") {
        GreedyOptionalParameters op;
        op.info = info;
        op.columngeneration_parameters = columngeneration_parameters;
        greedy(p, op);
    } else if (algorithm_args[0] == "limiteddiscrepancysearch") {
        auto op = read_limiteddiscrepancysearch_args(algorithm_argv);
        op.info = info;
        op.columngeneration_parameters = columngeneration_parameters;
        limiteddiscrepancysearch(p, op);
    } else if (algorithm_args[0] == "heuristictreesearch") {
        auto op = read_heuristictreesearch_args(algorithm_argv);
        op.info = info;
        op.columngeneration_parameters = columngeneration_parameters;
        heuristictreesearch(p, op);
    } else {
        std::cerr << "\033[31m" << "ERROR, unknown algorithm: '" << algorithm_args[0] << "'.\033[0m" << std::endl;
    }
}

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

    // Parse program options

    std::string problem = "cuttingstock";
    std::string instance_path = "";
    std::string output_path = "";
    std::string certificate_path = "";
    std::string format = "";
    std::string algorithm = "heuristictreesearch";
    std::string columngeneration_args_string = "";
    double time_limit = std::numeric_limits<double>::infinity();

    po::options_description desc("Allowed options");
    desc.add_options()
        ("help,h", "produce help message")
        ("problem,p", po::value<std::string>(&problem)->required(), "set problem (required)")
        ("input,i", po::value<std::string>(&instance_path)->required(), "set input path (required)")
        ("output,o", po::value<std::string>(&output_path), "set JSON output path")
        ("certificate,c", po::value<std::string>(&certificate_path), "set certificate path")
        ("format,f", po::value<std::string>(&format), "set input file format (default: orlibrary)")
        ("algorithm,a", po::value<std::string>(&algorithm), "set algorithm")
        ("column-generation-parameters,g", po::value<std::string>(&columngeneration_args_string), "set column generation parameters")
        ("time-limit,t", po::value<double>(&time_limit), "Time limit in seconds\n  ex: 3600")
        ("verbose,v", "")
        ("print-instance", "")
        ("print-solution", "")
        ;
    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    if (vm.count("help")) {
        std::cout << desc << std::endl;;
        return 1;
    }
    try {
        po::notify(vm);
    } catch (const po::required_option& e) {
        std::cout << desc << std::endl;;
        return 1;
    }

    optimizationtools::Info info = optimizationtools::Info()
        .set_verbose(vm.count("verbose"))
        .set_time_limit(time_limit)
        .set_json_output_path(output_path)
        .set_sigint_handler()
        ;

    // Run algorithm

    if (problem == "cuttingstock") {
        cuttingstock::Instance instance(instance_path, format);
        //if (vm.count("print-instance")) TODO
        //    std::cout << instance << std::endl;
        Parameters p = cuttingstock::get_parameters(instance);
        run(algorithm, columngeneration_args_string, info, p);

    } else if (problem == "multipleknapsack") {
        multipleknapsack::Instance instance(instance_path, format);
        //if (vm.count("print-instance")) TODO
        //    std::cout << instance << std::endl;
        Parameters p = multipleknapsack::get_parameters(instance);
        run(algorithm, columngeneration_args_string, info, p);

    } else if (problem == "binpackingwithconflicts") {
        binpackingwithconflicts::Instance instance(instance_path, format);
        //if (vm.count("print-instance")) TODO
        //    std::cout << instance << std::endl;
        Parameters p = binpackingwithconflicts::get_parameters(instance);
        run(algorithm, columngeneration_args_string, info, p);

    } else if (problem == "capacitatedvehiclerouting") {
        capacitatedvehiclerouting::Instance instance(instance_path, format);
        //if (vm.count("print-instance")) TODO
        //    std::cout << instance << std::endl;
        Parameters p = capacitatedvehiclerouting::get_parameters(instance);
        run(algorithm, columngeneration_args_string, info, p);

    } else if (problem == "vehicleroutingwithtimewindows") {
        vehicleroutingwithtimewindows::Instance instance(instance_path, format);
        //if (vm.count("print-instance")) TODO
        //    std::cout << instance << std::endl;
        Parameters p = vehicleroutingwithtimewindows::get_parameters(instance);
        run(algorithm, columngeneration_args_string, info, p);

    } else if (problem == "capacitatedopenvehiclerouting") {
        capacitatedopenvehiclerouting::Instance instance(instance_path, format);
        if (vm.count("print-instance"))
            std::cout << instance << std::endl;
        Parameters p = capacitatedopenvehiclerouting::get_parameters(instance);
        run(algorithm, columngeneration_args_string, info, p);

    } else if (problem == "parallelschedulingwithfamilysetuptimestwct") {
        parallelschedulingwithfamilysetuptimestwct::Instance instance(instance_path, format);
        if (vm.count("print-instance"))
            std::cout << instance << std::endl;
        Parameters p = parallelschedulingwithfamilysetuptimestwct::get_parameters(instance);
        run(algorithm, columngeneration_args_string, info, p);

    } else if (problem == "starobservationscheduling") {
        starobservationscheduling::Instance instance(instance_path, format);
        //if (vm.count("print-instance")) TODO
        //    std::cout << instance << std::endl;
        Parameters p = starobservationscheduling::get_parameters(instance);
        run(algorithm, columngeneration_args_string, info, p);

    } else {
        std::cerr << "\033[31m" << "ERROR, unknown problem: '" << problem << "'.\033[0m" << std::endl;
        return 1;
    }

    return 0;
}