text:
#include <rai/node/lmdb.hpp>

#include <rai/lib/utility.hpp>
#include <rai/node/common.hpp>
#include <rai/secure/versioning.hpp>

#include <boost/polymorphic_cast.hpp>

#include <queue>

rai::mdb_env::mdb_env (bool & error_a, boost::filesystem::path const & path_a, int max_dbs)
{
	boost::system::error_code error_mkdir, error_chmod;
	if (path_a.has_parent_path ())
	{
		boost::filesystem::create_directories (path_a.parent_path (), error_mkdir);
		boost::filesystem::permissions (path_a.parent_path (), boost::filesystem::owner_all, error_chmod);
		if (!error_mkdir)
		{
			auto status1 (mdb_env_create (&environment));
			assert (status1 == 0);
			auto status2 (mdb_env_set_maxdbs (environment, max_dbs));
			assert (status2 == 0);
			auto status3 (mdb_env_set_mapsize (environment, 1ULL * 1024 * 1024 * 1024 * 128)); // 128 Gigabyte
			assert (status3 == 0);
			// It seems if there's ever more threads than mdb_env_set_maxreaders has read slots available, we get failures on transaction creation unless MDB_NOTLS is specified
			// This can happen if something like 256 io_threads are specified in the node config
			auto status4 (mdb_env_open (environment, path_a.string ().c_str (), MDB_NOSUBDIR | MDB_NOTLS, 00600));
			error_a = status4 != 0;
		}
		else
		{
			error_a = true;
			environment = nullptr;
		}
	}
	else
	{
		error_a = true;
		environment = nullptr;
	}
}

rai::mdb_env::~mdb_env ()
{
	if (environment != nullptr)
	{
		mdb_env_close (environment);
	}
}

rai::mdb_env::operator MDB_env * () const
{
	return environment;
}

rai::transaction rai::mdb_env::tx_begin (bool write_a) const
{
	return { std::make_unique<rai::mdb_txn> (*this, write_a) };
}

MDB_txn * rai::mdb_env::tx (rai::transaction const & transaction_a) const
{
	auto result (boost::polymorphic_downcast<rai::mdb_txn *> (transaction_a.impl.get ()));
	release_assert (mdb_txn_env (result->handle) == environment);
	return *result;
}

rai::mdb_val::mdb_val (rai::epoch epoch_a) :
value ({ 0, nullptr }),
epoch (epoch_a)
{
}

rai::mdb_val::mdb_val (MDB_val const & value_a, rai::epoch epoch_a) :
value (value_a),
epoch (epoch_a)
{
}

rai::mdb_val::mdb_val (size_t size_a, void * data_a) :
value ({ size_a, data_a })
{
}

rai::mdb_val::mdb_val (rai::uint128_union const & val_a) :
mdb_val (sizeof (val_a), const_cast<rai::uint128_union *> (&val_a))
{
}

rai::mdb_val::mdb_val (rai::uint256_union const & val_a) :
mdb_val (sizeof (val_a), const_cast<rai::uint256_union *> (&val_a))
{
}

rai::mdb_val::mdb_val (rai::account_info const & val_a) :
mdb_val (val_a.db_size (), const_cast<rai::account_info *> (&val_a))
{
}

rai::mdb_val::mdb_val (rai::pending_info const & val_a) :
mdb_val (sizeof (val_a.source) + sizeof (val_a.amount), const_cast<rai::pending_info *> (&val_a))
{
}

rai::mdb_val::mdb_val (rai::pending_key const & val_a) :
mdb_val (sizeof (val_a), const_cast<rai::pending_key *> (&val_a))
{
}

rai::mdb_val::mdb_val (rai::block_info const & val_a) :
mdb_val (sizeof (val_a), const_cast<rai::block_info *> (&val_a))
{
}

rai::mdb_val::mdb_val (std::shared_ptr<rai::block> const & val_a) :
buffer (std::make_shared<std::vector<uint8_t>> ())
{
	{
		rai::vectorstream stream (*buffer);
		rai::serialize_block (stream, *val_a);
	}
	value = { buffer->size (), const_cast<uint8_t *> (buffer->data ()) };
}

void * rai::mdb_val::data () const
{
	return value.mv_data;
}

size_t rai::mdb_val::size () const
{
	return value.mv_size;
}

rai::mdb_val::operator rai::account_info () const
{
	rai::account_info result;
	result.epoch = epoch;
	assert (value.mv_size == result.db_size ());
	std::copy (reinterpret_cast<uint8_t const *> (value.mv_data), reinterpret_cast<uint8_t const *> (value.mv_data) + result.db_size (), reinterpret_cast<uint8_t *> (&result));
	return result;
}

rai::mdb_val::operator rai::block_info () const
{
	rai::block_info result;
	assert (value.mv_size == sizeof (result));
	static_assert (sizeof (rai::block_info::account) + sizeof (rai::block_info::balance) == sizeof (result), "Packed class");
	std::copy (reinterpret_cast<uint8_t const *> (value.mv_data), reinterpret_cast<uint8_t const *> (value.mv_data) + sizeof (result), reinterpret_cast<uint8_t *> (&result));
	return result;
}

rai::mdb_val::operator rai::pending_info () const
{
	rai::pending_info result;
	result.epoch = epoch;
	std::copy (reinterpret_cast<uint8_t const *> (value.mv_data), reinterpret_cast<uint8_t const *> (value.mv_data) + sizeof (rai::pending_info::source) + sizeof (rai::pending_info::amount), reinterpret_cast<uint8_t *> (&result));
	return result;
}

rai::mdb_val::operator rai::pending_key () const
{
	rai::pending_key result;
	assert (value.mv_size == sizeof (result));
	static_assert (sizeof (rai::pending_key::account) + sizeof (rai::pending_key::hash) == sizeof (result), "Packed class");
	std::copy (reinterpret_cast<uint8_t const *> (value.mv_data), reinterpret_cast<uint8_t const *> (value.mv_data) + sizeof (result), reinterpret_cast<uint8_t *> (&result));
	return result;
}

rai::mdb_val::operator rai::uint128_union () const
{
	rai::uint128_union result;
	assert (size () == sizeof (result));
	std::copy (reinterpret_cast<uint8_t const *> (data ()), reinterpret_cast<uint8_t const *> (data ()) + sizeof (result), result.bytes.data ());
	return result;
}

rai::mdb_val::operator rai::uint256_union () const
{
	rai::uint256_union result;
	assert (size () == sizeof (result));
	std::copy (reinterpret_cast<uint8_t const *> (data ()), reinterpret_cast<uint8_t const *> (data ()) + sizeof (result), result.bytes.data ());
	return result;
}

rai::mdb_val::operator std::array<char, 64> () const
{
	rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.mv_data), value.mv_size);
	std::array<char, 64> result;
	rai::read (stream, result);
	return result;
}

rai::mdb_val::operator no_value () const
{
	return no_value::dummy;
}

rai::mdb_val::operator std::shared_ptr<rai::block> () const
{
	rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.mv_data), value.mv_size);
	std::shared_ptr<rai::block> result (rai::deserialize_block (stream));
	return result;
}

rai::mdb_val::operator std::shared_ptr<rai::send_block> () const
{
	rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.mv_data), value.mv_size);
	auto error (false);
	std::shared_ptr<rai::send_block> result (std::make_shared<rai::send_block> (error, stream));
	assert (!error);
	return result;
}

rai::mdb_val::operator std::shared_ptr<rai::receive_block> () const
{
	rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.mv_data), value.mv_size);
	auto error (false);
	std::shared_ptr<rai::receive_block> result (std::make_shared<rai::receive_block> (error, stream));
	assert (!error);
	return result;
}

rai::mdb_val::operator std::shared_ptr<rai::open_block> () const
{
	rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.mv_data), value.mv_size);
	auto error (false);
	std::shared_ptr<rai::open_block> result (std::make_shared<rai::open_block> (error, stream));
	assert (!error);
	return result;
}

rai::mdb_val::operator std::shared_ptr<rai::change_block> () const
{
	rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.mv_data), value.mv_size);
	auto error (false);
	std::shared_ptr<rai::change_block> result (std::make_shared<rai::change_block> (error, stream));
	assert (!error);
	return result;
}

rai::mdb_val::operator std::shared_ptr<rai::state_block> () const
{
	rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.mv_data), value.mv_size);
	auto error (false);
	std::shared_ptr<rai::state_block> result (std::make_shared<rai::state_block> (error, stream));
	assert (!error);
	return result;
}

rai::mdb_val::operator std::shared_ptr<rai::vote> () const
{
	rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.mv_data), value.mv_size);
	auto error (false);
	std::shared_ptr<rai::vote> result (std::make_shared<rai::vote> (error, stream));
	assert (!error);
	return result;
}

rai::mdb_val::operator uint64_t () const
{
	uint64_t result;
	rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.mv_data), value.mv_size);
	auto error (rai::read (stream, result));
	assert (!error);
	return result;
}

rai::mdb_val::operator MDB_val * () const
{
	// Allow passing a temporary to a non-c++ function which doesn't have constness
	return const_cast<MDB_val *> (&value);
};

rai::mdb_val::operator MDB_val const & () const
{
	return value;
}

rai::mdb_txn::mdb_txn (rai::mdb_env const & environment_a, bool write_a)
{
	auto status (mdb_txn_begin (environment_a, nullptr, write_a ? 0 : MDB_RDONLY, &handle));
	assert (status == 0);
}

rai::mdb_txn::~mdb_txn ()
{
	auto status (mdb_txn_commit (handle));
	assert (status == 0);
}

rai::mdb_txn::operator MDB_txn * () const
{
	return handle;
}

namespace rai
{
/**
	 * Fill in our predecessors
	 */
class block_predecessor_set : public rai::block_visitor
{
public:
	block_predecessor_set (rai::transaction const & transaction_a, rai::mdb_store & store_a) :
	transaction (transaction_a),
	store (store_a)
	{
	}
	virtual ~block_predecessor_set () = default;
	void fill_value (rai::block const & block_a)
	{
		auto hash (block_a.hash ());
		rai::block_type type;
		auto value (store.block_raw_get (transaction, block_a.previous (), type));
		auto version (store.block_version (transaction, block_a.previous ()));
		assert (value.mv_size != 0);
		std::vector<uint8_t> data (static_cast<uint8_t *> (value.mv_data), static_cast<uint8_t *> (value.mv_data) + value.mv_size);
		std::copy (hash.bytes.begin (), hash.bytes.end (), data.end () - hash.bytes.size ());
		store.block_raw_put (transaction, store.block_database (type, version), block_a.previous (), rai::mdb_val (data.size (), data.data ()));
	}
	void send_block (rai::send_block const & block_a) override
	{
		fill_value (block_a);
	}
	void receive_block (rai::receive_block const & block_a) override
	{
		fill_value (block_a);
	}
	void open_block (rai::open_block const & block_a) override
	{
		// Open blocks don't have a predecessor
	}
	void change_block (rai::change_block const & block_a) override
	{
		fill_value (block_a);
	}
	void state_block (rai::state_block const & block_a) override
	{
		if (!block_a.previous ().is_zero ())
		{
			fill_value (block_a);
		}
	}
	rai::transaction const & transaction;
	rai::mdb_store & store;
};
}

template <typename T, typename U>
rai::mdb_iterator<T, U>::mdb_iterator (rai::transaction const & transaction_a, MDB_dbi db_a, rai::epoch epoch_a) :
cursor (nullptr)
{
	current.first.epoch = epoch_a;
	current.second.epoch = epoch_a;
	auto status (mdb_cursor_open (tx (transaction_a), db_a, &cursor));
	assert (status == 0);
	auto status2 (mdb_cursor_get (cursor, &current.first.value, &current.second.value, MDB_FIRST));
	assert (status2 == 0 || status2 == MDB_NOTFOUND);
	if (status2 != MDB_NOTFOUND)
	{
		auto status3 (mdb_cursor_get (cursor, &current.first.value, &current.second.value, MDB_GET_CURRENT));
		assert (status3 == 0 || status3 == MDB_NOTFOUND);
		if (current.first.size () != sizeof (T))
		{
			clear ();
		}
	}
	else
	{
		clear ();
	}
}

template <typename T, typename U>
rai::mdb_iterator<T, U>::mdb_iterator (std::nullptr_t, rai::epoch epoch_a) :
cursor (nullptr)
{
	current.first.epoch = epoch_a;
	current.second.epoch = epoch_a;
}

template <typename T, typename U>
rai::mdb_iterator<T, U>::mdb_iterator (rai::transaction const & transaction_a, MDB_dbi db_a, MDB_val const & val_a, rai::epoch epoch_a) :
cursor (nullptr)
{
	current.first.epoch = epoch_a;
	current.second.epoch = epoch_a;
	auto status (mdb_cursor_open (tx (transaction_a), db_a, &cursor));
	assert (status == 0);
	current.first = val_a;
	auto status2 (mdb_cursor_get (cursor, &current.first.value, &current.second.value, MDB_SET_RANGE));
	assert (status2 == 0 || status2 == MDB_NOTFOUND);
	if (status2 != MDB_NOTFOUND)
	{
		auto status3 (mdb_cursor_get (cursor, &current.first.value, &current.second.value, MDB_GET_CURRENT));
		assert (status3 == 0 || status3 == MDB_NOTFOUND);
		if (current.first.size () != sizeof (T))
		{
			clear ();
		}
	}
	else
	{
		clear ();
	}
}

template <typename T, typename U>
rai::mdb_iterator<T, U>::mdb_iterator (rai::mdb_iterator<T, U> && other_a)
{
	cursor = other_a.cursor;
	other_a.cursor = nullptr;
	current = other_a.current;
}

template <typename T, typename U>
rai::mdb_iterator<T, U>::~mdb_iterator ()
{
	if (cursor != nullptr)
	{
		mdb_cursor_close (cursor);
	}
}

template <typename T, typename U>
rai::store_iterator_impl<T, U> & rai::mdb_iterator<T, U>::operator++ ()
{
	assert (cursor != nullptr);
	auto status (mdb_cursor_get (cursor, &current.first.value, &current.second.value, MDB_NEXT));
	if (status == MDB_NOTFOUND)
	{
		clear ();
	}
	if (current.first.size () != sizeof (T))
	{
		clear ();
	}
	return *this;
}

template <typename T, typename U>
rai::mdb_iterator<T, U> & rai::mdb_iterator<T, U>::operator= (rai::mdb_iterator<T, U> && other_a)
{
	if (cursor != nullptr)
	{
		mdb_cursor_close (cursor);
	}
	cursor = other_a.cursor;
	other_a.cursor = nullptr;
	current = other_a.current;
	other_a.clear ();
	return *this;
}

template <typename T, typename U>
std::pair<rai::mdb_val, rai::mdb_val> * rai::mdb_iterator<T, U>::operator-> ()
{
	return &current;
}

template <typename T, typename U>
bool rai::mdb_iterator<T, U>::operator== (rai::store_iterator_impl<T, U> const & base_a) const
{
	auto const other_a (boost::polymorphic_downcast<rai::mdb_iterator<T, U> const *> (&base_a));
	auto result (current.first.data () == other_a->current.first.data ());
	assert (!result || (current.first.size () == other_a->current.first.size ()));
	assert (!result || (current.second.data () == other_a->current.second.data ()));
	assert (!result || (current.second.size () == other_a->current.second.size ()));
	return result;
}

template <typename T, typename U>
void rai::mdb_iterator<T, U>::clear ()
{
	current.first = rai::mdb_val (current.first.epoch);
	current.second = rai::mdb_val (current.second.epoch);
	assert (is_end_sentinal ());
}

template <typename T, typename U>
MDB_txn * rai::mdb_iterator<T, U>::tx (rai::transaction const & transaction_a) const
{
	auto result (boost::polymorphic_downcast<rai::mdb_txn *> (transaction_a.impl.get ()));
	return *result;
}

template <typename T, typename U>
bool rai::mdb_iterator<T, U>::is_end_sentinal () const
{
	return current.first.size () == 0;
}

template <typename T, typename U>
void rai::mdb_iterator<T, U>::fill (std::pair<T, U> & value_a) const
{
	if (current.first.size () != 0)
	{
		value_a.first = static_cast<T> (current.first);
	}
	else
	{
		value_a.first = T ();
	}
	if (current.second.size () != 0)
	{
		value_a.second = static_cast<U> (current.second);
	}
	else
	{
		value_a.second = U ();
	}
}

template <typename T, typename U>
std::pair<rai::mdb_val, rai::mdb_val> * rai::mdb_merge_iterator<T, U>::operator-> ()
{
	return least_iterator ().operator-> ();
}

template <typename T, typename U>
rai::mdb_merge_iterator<T, U>::mdb_merge_iterator (rai::transaction const & transaction_a, MDB_dbi db1_a, MDB_dbi db2_a) :
impl1 (std::make_unique<rai::mdb_iterator<T, U>> (transaction_a, db1_a, rai::epoch::epoch_0)),
impl2 (std::make_unique<rai::mdb_iterator<T, U>> (transaction_a, db2_a, rai::epoch::epoch_1))
{
}

template <typename T, typename U>
rai::mdb_merge_iterator<T, U>::mdb_merge_iterator (std::nullptr_t) :
impl1 (std::make_unique<rai::mdb_iterator<T, U>> (nullptr, rai::epoch::epoch_0)),
impl2 (std::make_unique<rai::mdb_iterator<T, U>> (nullptr, rai::epoch::epoch_1))
{
}

template <typename T, typename U>
rai::mdb_merge_iterator<T, U>::mdb_merge_iterator (rai::transaction const & transaction_a, MDB_dbi db1_a, MDB_dbi db2_a, MDB_val const & val_a) :
impl1 (std::make_unique<rai::mdb_iterator<T, U>> (transaction_a, db1_a, val_a, rai::epoch::epoch_0)),
impl2 (std::make_unique<rai::mdb_iterator<T, U>> (transaction_a, db2_a, val_a, rai::epoch::epoch_1))
{
}

template <typename T, typename U>
rai::mdb_merge_iterator<T, U>::mdb_merge_iterator (rai::mdb_merge_iterator<T, U> && other_a)
{
	impl1 = std::move (other_a.impl1);
	impl2 = std::move (other_a.impl2);
}

template <typename T, typename U>
rai::mdb_merge_iterator<T, U>::~mdb_merge_iterator ()
{
}

template <typename T, typename U>
rai::store_iterator_impl<T, U> & rai::mdb_merge_iterator<T, U>::operator++ ()
{
	++least_iterator ();
	return *this;
}

template <typename T, typename U>
bool rai::mdb_merge_iterator<T, U>::is_end_sentinal () const
{
	return least_iterator ().is_end_sentinal ();
}

template <typename T, typename U>
void rai::mdb_merge_iterator<T, U>::fill (std::pair<T, U> & value_a) const
{
	auto & current (least_iterator ());
	if (current->first.size () != 0)
	{
		value_a.first = static_cast<T> (current->first);
	}
	else
	{
		value_a.first = T ();
	}
	if (current->second.size () != 0)
	{
		value_a.second = static_cast<U> (current->second);
	}
	else
	{
		value_a.second = U ();
	}
}

template <typename T, typename U>
bool rai::mdb_merge_iterator<T, U>::operator== (rai::store_iterator_impl<T, U> const & base_a) const
{
	assert ((dynamic_cast<rai::mdb_merge_iterator<T, U> const *> (&base_a) != nullptr) && "Incompatible iterator comparison");
	auto & other (static_cast<rai::mdb_merge_iterator<T, U> const &> (base_a));
	return *impl1 == *other.impl1 && *impl2 == *other.impl2;
}

template <typename T, typename U>
rai::mdb_iterator<T, U> & rai::mdb_merge_iterator<T, U>::least_iterator () const
{
	rai::mdb_iterator<T, U> * result;
	if (impl1->is_end_sentinal ())
	{
		result = impl2.get ();
	}
	else if (impl2->is_end_sentinal ())
	{
		result = impl1.get ();
	}
	else
	{
		auto key_cmp (mdb_cmp (mdb_cursor_txn (impl1->cursor), mdb_cursor_dbi (impl1->cursor), impl1->current.first, impl2->current.first));

		if (key_cmp < 0)
		{
			result = impl1.get ();
		}
		else if (key_cmp > 0)
		{
			result = impl2.get ();
		}
		else
		{
			auto val_cmp (mdb_cmp (mdb_cursor_txn (impl1->cursor), mdb_cursor_dbi (impl1->cursor), impl1->current.second, impl2->current.second));
			result = val_cmp < 0 ? impl1.get () : impl2.get ();
		}
	}
	return *result;
}

rai::wallet_value::wallet_value (rai::mdb_val const & val_a)
{
	assert (val_a.size () == sizeof (*this));
	std::copy (reinterpret_cast<uint8_t const *> (val_a.data ()), reinterpret_cast<uint8_t const *> (val_a.data ()) + sizeof (key), key.chars.begin ());
	std::copy (reinterpret_cast<uint8_t const *> (val_a.data ()) + sizeof (key), reinterpret_cast<uint8_t const *> (val_a.data ()) + sizeof (key) + sizeof (work), reinterpret_cast<char *> (&work));
}

rai::wallet_value::wallet_value (rai::uint256_union const & key_a, uint64_t work_a) :
key (key_a),
work (work_a)
{
}

rai::mdb_val rai::wallet_value::val () const
{
	static_assert (sizeof (*this) == sizeof (key) + sizeof (work), "Class not packed");
	return rai::mdb_val (sizeof (*this), const_cast<rai::wallet_value *> (this));
}

template class rai::mdb_iterator<rai::pending_key, rai::pending_info>;
template class rai::mdb_iterator<rai::uint256_union, rai::block_info>;
template class rai::mdb_iterator<rai::uint256_union, rai::uint128_union>;
template class rai::mdb_iterator<rai::uint256_union, rai::uint256_union>;
template class rai::mdb_iterator<rai::uint256_union, std::shared_ptr<rai::block>>;
template class rai::mdb_iterator<rai::uint256_union, std::shared_ptr<rai::vote>>;
template class rai::mdb_iterator<rai::uint256_union, rai::wallet_value>;
template class rai::mdb_iterator<std::array<char, 64>, rai::mdb_val::no_value>;

rai::store_iterator<rai::block_hash, rai::block_info> rai::mdb_store::block_info_begin (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
{
	rai::store_iterator<rai::block_hash, rai::block_info> result (std::make_unique<rai::mdb_iterator<rai::block_hash, rai::block_info>> (transaction_a, blocks_info, rai::mdb_val (hash_a)));
	return result;
}

rai::store_iterator<rai::block_hash, rai::block_info> rai::mdb_store::block_info_begin (rai::transaction const & transaction_a)
{
	rai::store_iterator<rai::block_hash, rai::block_info> result (std::make_unique<rai::mdb_iterator<rai::block_hash, rai::block_info>> (transaction_a, blocks_info));
	return result;
}

rai::store_iterator<rai::block_hash, rai::block_info> rai::mdb_store::block_info_end ()
{
	rai::store_iterator<rai::block_hash, rai::block_info> result (nullptr);
	return result;
}

rai::store_iterator<rai::account, rai::uint128_union> rai::mdb_store::representation_begin (rai::transaction const & transaction_a)
{
	rai::store_iterator<rai::account, rai::uint128_union> result (std::make_unique<rai::mdb_iterator<rai::account, rai::uint128_union>> (transaction_a, representation));
	return result;
}

rai::store_iterator<rai::account, rai::uint128_union> rai::mdb_store::representation_end ()
{
	rai::store_iterator<rai::account, rai::uint128_union> result (nullptr);
	return result;
}

rai::store_iterator<rai::block_hash, std::shared_ptr<rai::block>> rai::mdb_store::unchecked_begin (rai::transaction const & transaction_a)
{
	rai::store_iterator<rai::block_hash, std::shared_ptr<rai::block>> result (std::make_unique<rai::mdb_iterator<rai::account, std::shared_ptr<rai::block>>> (transaction_a, unchecked));
	return result;
}

rai::store_iterator<rai::block_hash, std::shared_ptr<rai::block>> rai::mdb_store::unchecked_begin (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
{
	rai::store_iterator<rai::block_hash, std::shared_ptr<rai::block>> result (std::make_unique<rai::mdb_iterator<rai::block_hash, std::shared_ptr<rai::block>>> (transaction_a, unchecked, rai::mdb_val (hash_a)));
	return result;
}

rai::store_iterator<rai::block_hash, std::shared_ptr<rai::block>> rai::mdb_store::unchecked_end ()
{
	rai::store_iterator<rai::block_hash, std::shared_ptr<rai::block>> result (nullptr);
	return result;
}

rai::store_iterator<rai::account, std::shared_ptr<rai::vote>> rai::mdb_store::vote_begin (rai::transaction const & transaction_a)
{
	return rai::store_iterator<rai::account, std::shared_ptr<rai::vote>> (std::make_unique<rai::mdb_iterator<rai::account, std::shared_ptr<rai::vote>>> (transaction_a, vote));
}

rai::store_iterator<rai::account, std::shared_ptr<rai::vote>> rai::mdb_store::vote_end ()
{
	return rai::store_iterator<rai::account, std::shared_ptr<rai::vote>> (nullptr);
}

rai::mdb_store::mdb_store (bool & error_a, boost::filesystem::path const & path_a, int lmdb_max_dbs) :
env (error_a, path_a, lmdb_max_dbs),
frontiers (0),
accounts_v0 (0),
accounts_v1 (0),
send_blocks (0),
receive_blocks (0),
open_blocks (0),
change_blocks (0),
state_blocks_v0 (0),
state_blocks_v1 (0),
pending_v0 (0),
pending_v1 (0),
blocks_info (0),
representation (0),
unchecked (0),
checksum (0),
vote (0),
meta (0)
{
	if (!error_a)
	{
		auto transaction (tx_begin_write ());
		error_a |= mdb_dbi_open (env.tx (transaction), "frontiers", MDB_CREATE, &frontiers) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "accounts", MDB_CREATE, &accounts_v0) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "accounts_v1", MDB_CREATE, &accounts_v1) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "send", MDB_CREATE, &send_blocks) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "receive", MDB_CREATE, &receive_blocks) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "open", MDB_CREATE, &open_blocks) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "change", MDB_CREATE, &change_blocks) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "state", MDB_CREATE, &state_blocks_v0) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "state_v1", MDB_CREATE, &state_blocks_v1) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "pending", MDB_CREATE, &pending_v0) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "pending_v1", MDB_CREATE, &pending_v1) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "blocks_info", MDB_CREATE, &blocks_info) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "representation", MDB_CREATE, &representation) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "unchecked", MDB_CREATE | MDB_DUPSORT, &unchecked) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "checksum", MDB_CREATE, &checksum) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "vote", MDB_CREATE, &vote) != 0;
		error_a |= mdb_dbi_open (env.tx (transaction), "meta", MDB_CREATE, &meta) != 0;
		if (!error_a)
		{
			do_upgrades (transaction);
			checksum_put (transaction, 0, 0, 0);
		}
	}
}

rai::transaction rai::mdb_store::tx_begin_write ()
{
	return tx_begin (true);
}

rai::transaction rai::mdb_store::tx_begin_read ()
{
	return tx_begin (false);
}

rai::transaction rai::mdb_store::tx_begin (bool write_a)
{
	return env.tx_begin (write_a);
}

void rai::mdb_store::initialize (rai::transaction const & transaction_a, rai::genesis const & genesis_a)
{
	auto hash_l (genesis_a.hash ());
	assert (latest_v0_begin (transaction_a) == latest_v0_end ());
	assert (latest_v1_begin (transaction_a) == latest_v1_end ());
	block_put (transaction_a, hash_l, *genesis_a.open);
	account_put (transaction_a, genesis_account, { hash_l, genesis_a.open->hash (), genesis_a.open->hash (), std::numeric_limits<rai::uint128_t>::max (), rai::seconds_since_epoch (), 1, rai::epoch::epoch_0 });
	representation_put (transaction_a, genesis_account, std::numeric_limits<rai::uint128_t>::max ());
	checksum_put (transaction_a, 0, 0, hash_l);
	frontier_put (transaction_a, hash_l, genesis_account);
}

void rai::mdb_store::version_put (rai::transaction const & transaction_a, int version_a)
{
	rai::uint256_union version_key (1);
	rai::uint256_union version_value (version_a);
	auto status (mdb_put (env.tx (transaction_a), meta, rai::mdb_val (version_key), rai::mdb_val (version_value), 0));
	assert (status == 0);
}

int rai::mdb_store::version_get (rai::transaction const & transaction_a)
{
	rai::uint256_union version_key (1);
	rai::mdb_val data;
	auto error (mdb_get (env.tx (transaction_a), meta, rai::mdb_val (version_key), data));
	int result (1);
	if (error != MDB_NOTFOUND)
	{
		rai::uint256_union version_value (data);
		assert (version_value.qwords[2] == 0 && version_value.qwords[1] == 0 && version_value.qwords[0] == 0);
		result = version_value.number ().convert_to<int> ();
	}
	return result;
}

rai::raw_key rai::mdb_store::get_node_id (rai::transaction const & transaction_a)
{
	rai::uint256_union node_id_mdb_key (3);
	rai::raw_key node_id;
	rai::mdb_val value;
	auto error (mdb_get (env.tx (transaction_a), meta, rai::mdb_val (node_id_mdb_key), value));
	if (!error)
	{
		rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.data ()), value.size ());
		error = rai::read (stream, node_id.data);
		assert (!error);
	}
	if (error)
	{
		rai::random_pool.GenerateBlock (node_id.data.bytes.data (), node_id.data.bytes.size ());
		error = mdb_put (env.tx (transaction_a), meta, rai::mdb_val (node_id_mdb_key), rai::mdb_val (node_id.data), 0);
	}
	assert (!error);
	return node_id;
}

void rai::mdb_store::delete_node_id (rai::transaction const & transaction_a)
{
	rai::uint256_union node_id_mdb_key (3);
	auto error (mdb_del (env.tx (transaction_a), meta, rai::mdb_val (node_id_mdb_key), nullptr));
	assert (!error || error == MDB_NOTFOUND);
}

void rai::mdb_store::do_upgrades (rai::transaction const & transaction_a)
{
	switch (version_get (transaction_a))
	{
		case 1:
			upgrade_v1_to_v2 (transaction_a);
		case 2:
			upgrade_v2_to_v3 (transaction_a);
		case 3:
			upgrade_v3_to_v4 (transaction_a);
		case 4:
			upgrade_v4_to_v5 (transaction_a);
		case 5:
			upgrade_v5_to_v6 (transaction_a);
		case 6:
			upgrade_v6_to_v7 (transaction_a);
		case 7:
			upgrade_v7_to_v8 (transaction_a);
		case 8:
			upgrade_v8_to_v9 (transaction_a);
		case 9:
			upgrade_v9_to_v10 (transaction_a);
		case 10:
			upgrade_v10_to_v11 (transaction_a);
		case 11:
			break;
		default:
			assert (false);
	}
}

void rai::mdb_store::upgrade_v1_to_v2 (rai::transaction const & transaction_a)
{
	version_put (transaction_a, 2);
	rai::account account (1);
	while (!account.is_zero ())
	{
		rai::mdb_iterator<rai::uint256_union, rai::account_info_v1> i (transaction_a, accounts_v0, rai::mdb_val (account));
		std::cerr << std::hex;
		if (i != rai::mdb_iterator<rai::uint256_union, rai::account_info_v1> (nullptr))
		{
			account = rai::uint256_union (i->first);
			rai::account_info_v1 v1 (i->second);
			rai::account_info_v5 v2;
			v2.balance = v1.balance;
			v2.head = v1.head;
			v2.modified = v1.modified;
			v2.rep_block = v1.rep_block;
			auto block (block_get (transaction_a, v1.head));
			while (!block->previous ().is_zero ())
			{
				block = block_get (transaction_a, block->previous ());
			}
			v2.open_block = block->hash ();
			auto status (mdb_put (env.tx (transaction_a), accounts_v0, rai::mdb_val (account), v2.val (), 0));
			assert (status == 0);
			account = account.number () + 1;
		}
		else
		{
			account.clear ();
		}
	}
}

void rai::mdb_store::upgrade_v2_to_v3 (rai::transaction const & transaction_a)
{
	version_put (transaction_a, 3);
	mdb_drop (env.tx (transaction_a), representation, 0);
	for (auto i (std::make_unique<rai::mdb_iterator<rai::account, rai::account_info_v5>> (transaction_a, accounts_v0)), n (std::make_unique<rai::mdb_iterator<rai::account, rai::account_info_v5>> (nullptr)); *i != *n; ++(*i))
	{
		rai::account account_l ((*i)->first);
		rai::account_info_v5 info ((*i)->second);
		representative_visitor visitor (transaction_a, *this);
		visitor.compute (info.head);
		assert (!visitor.result.is_zero ());
		info.rep_block = visitor.result;
		auto impl (boost::polymorphic_downcast<rai::mdb_iterator<rai::account, rai::account_info_v5> *> (i.get ()));
		mdb_cursor_put (impl->cursor, rai::mdb_val (account_l), info.val (), MDB_CURRENT);
		representation_add (transaction_a, visitor.result, info.balance.number ());
	}
}

void rai::mdb_store::upgrade_v3_to_v4 (rai::transaction const & transaction_a)
{
	version_put (transaction_a, 4);
	std::queue<std::pair<rai::pending_key, rai::pending_info>> items;
	for (auto i (rai::store_iterator<rai::block_hash, rai::pending_info_v3> (std::make_unique<rai::mdb_iterator<rai::block_hash, rai::pending_info_v3>> (transaction_a, pending_v0))), n (rai::store_iterator<rai::block_hash, rai::pending_info_v3> (nullptr)); i != n; ++i)
	{
		rai::block_hash hash (i->first);
		rai::pending_info_v3 info (i->second);
		items.push (std::make_pair (rai::pending_key (info.destination, hash), rai::pending_info (info.source, info.amount, rai::epoch::epoch_0)));
	}
	mdb_drop (env.tx (transaction_a), pending_v0, 0);
	while (!items.empty ())
	{
		pending_put (transaction_a, items.front ().first, items.front ().second);
		items.pop ();
	}
}

void rai::mdb_store::upgrade_v4_to_v5 (rai::transaction const & transaction_a)
{
	version_put (transaction_a, 5);
	for (auto i (rai::store_iterator<rai::account, rai::account_info_v5> (std::make_unique<rai::mdb_iterator<rai::account, rai::account_info_v5>> (transaction_a, accounts_v0))), n (rai::store_iterator<rai::account, rai::account_info_v5> (nullptr)); i != n; ++i)
	{
		rai::account_info_v5 info (i->second);
		rai::block_hash successor (0);
		auto block (block_get (transaction_a, info.head));
		while (block != nullptr)
		{
			auto hash (block->hash ());
			if (block_successor (transaction_a, hash).is_zero () && !successor.is_zero ())
			{
				block_put (transaction_a, hash, *block, successor);
			}
			successor = hash;
			block = block_get (transaction_a, block->previous ());
		}
	}
}

void rai::mdb_store::upgrade_v5_to_v6 (rai::transaction const & transaction_a)
{
	version_put (transaction_a, 6);
	std::deque<std::pair<rai::account, rai::account_info>> headers;
	for (auto i (rai::store_iterator<rai::account, rai::account_info_v5> (std::make_unique<rai::mdb_iterator<rai::account, rai::account_info_v5>> (transaction_a, accounts_v0))), n (rai::store_iterator<rai::account, rai::account_info_v5> (nullptr)); i != n; ++i)
	{
		rai::account account (i->first);
		rai::account_info_v5 info_old (i->second);
		uint64_t block_count (0);
		auto hash (info_old.head);
		while (!hash.is_zero ())
		{
			++block_count;
			auto block (block_get (transaction_a, hash));
			assert (block != nullptr);
			hash = block->previous ();
		}
		rai::account_info info (info_old.head, info_old.rep_block, info_old.open_block, info_old.balance, info_old.modified, block_count, rai::epoch::epoch_0);
		headers.push_back (std::make_pair (account, info));
	}
	for (auto i (headers.begin ()), n (headers.end ()); i != n; ++i)
	{
		account_put (transaction_a, i->first, i->second);
	}
}

void rai::mdb_store::upgrade_v6_to_v7 (rai::transaction const & transaction_a)
{
	version_put (transaction_a, 7);
	mdb_drop (env.tx (transaction_a), unchecked, 0);
}

void rai::mdb_store::upgrade_v7_to_v8 (rai::transaction const & transaction_a)
{
	version_put (transaction_a, 8);
	mdb_drop (env.tx (transaction_a), unchecked, 1);
	mdb_dbi_open (env.tx (transaction_a), "unchecked", MDB_CREATE | MDB_DUPSORT, &unchecked);
}

void rai::mdb_store::upgrade_v8_to_v9 (rai::transaction const & transaction_a)
{
	version_put (transaction_a, 9);
	MDB_dbi sequence;
	mdb_dbi_open (env.tx (transaction_a), "sequence", MDB_CREATE | MDB_DUPSORT, &sequence);
	rai::genesis genesis;
	std::shared_ptr<rai::block> block (std::move (genesis.open));
	rai::keypair junk;
	for (rai::mdb_iterator<rai::account, uint64_t> i (transaction_a, sequence), n (rai::mdb_iterator<rai::account, uint64_t> (nullptr)); i != n; ++i)
	{
		rai::bufferstream stream (reinterpret_cast<uint8_t const *> (i->second.data ()), i->second.size ());
		uint64_t sequence;
		auto error (rai::read (stream, sequence));
		// Create a dummy vote with the same sequence number for easy upgrading.  This won't have a valid signature.
		rai::vote dummy (rai::account (i->first), junk.prv, sequence, block);
		std::vector<uint8_t> vector;
		{
			rai::vectorstream stream (vector);
			dummy.serialize (stream);
		}
		auto status1 (mdb_put (env.tx (transaction_a), vote, rai::mdb_val (i->first), rai::mdb_val (vector.size (), vector.data ()), 0));
		assert (status1 == 0);
		assert (!error);
	}
	mdb_drop (env.tx (transaction_a), sequence, 1);
}

void rai::mdb_store::upgrade_v9_to_v10 (rai::transaction const & transaction_a)
{
	//std::cerr << boost::str (boost::format ("Performing database upgrade to version 10...\n"));
	version_put (transaction_a, 10);
	for (auto i (latest_v0_begin (transaction_a)), n (latest_v0_end ()); i != n; ++i)
	{
		rai::account_info info (i->second);
		if (info.block_count >= block_info_max)
		{
			rai::account account (i->first);
			//std::cerr << boost::str (boost::format ("Upgrading account %1%...\n") % account.to_account ());
			size_t block_count (1);
			auto hash (info.open_block);
			while (!hash.is_zero ())
			{
				if ((block_count % block_info_max) == 0)
				{
					rai::block_info block_info;
					block_info.account = account;
					rai::amount balance (block_balance (transaction_a, hash));
					block_info.balance = balance;
					block_info_put (transaction_a, hash, block_info);
				}
				hash = block_successor (transaction_a, hash);
				++block_count;
			}
		}
	}
}

void rai::mdb_store::upgrade_v10_to_v11 (rai::transaction const & transaction_a)
{
	version_put (transaction_a, 11);
	MDB_dbi unsynced;
	mdb_dbi_open (env.tx (transaction_a), "unsynced", MDB_CREATE | MDB_DUPSORT, &unsynced);
	mdb_drop (env.tx (transaction_a), unsynced, 1);
}

void rai::mdb_store::clear (MDB_dbi db_a)
{
	auto transaction (tx_begin_write ());
	auto status (mdb_drop (env.tx (transaction), db_a, 0));
	assert (status == 0);
}

rai::uint128_t rai::mdb_store::block_balance (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
{
	balance_visitor visitor (transaction_a, *this);
	visitor.compute (hash_a);
	return visitor.balance;
}

rai::epoch rai::mdb_store::block_version (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
{
	rai::mdb_val value;
	auto status (mdb_get (env.tx (transaction_a), state_blocks_v1, rai::mdb_val (hash_a), value));
	assert (status == 0 || status == MDB_NOTFOUND);
	return status == 0 ? rai::epoch::epoch_1 : rai::epoch::epoch_0;
}

void rai::mdb_store::representation_add (rai::transaction const & transaction_a, rai::block_hash const & source_a, rai::uint128_t const & amount_a)
{
	auto source_block (block_get (transaction_a, source_a));
	assert (source_block != nullptr);
	auto source_rep (source_block->representative ());
	auto source_previous (representation_get (transaction_a, source_rep));
	representation_put (transaction_a, source_rep, source_previous + amount_a);
}

MDB_dbi rai::mdb_store::block_database (rai::block_type type_a, rai::epoch epoch_a)
{
	if (type_a == rai::block_type::state)
	{
		assert (epoch_a == rai::epoch::epoch_0 || epoch_a == rai::epoch::epoch_1);
	}
	else
	{
		assert (epoch_a == rai::epoch::epoch_0);
	}
	MDB_dbi result;
	switch (type_a)
	{
		case rai::block_type::send:
			result = send_blocks;
			break;
		case rai::block_type::receive:
			result = receive_blocks;
			break;
		case rai::block_type::open:
			result = open_blocks;
			break;
		case rai::block_type::change:
			result = change_blocks;
			break;
		case rai::block_type::state:
			switch (epoch_a)
			{
				case rai::epoch::epoch_0:
					result = state_blocks_v0;
					break;
				case rai::epoch::epoch_1:
					result = state_blocks_v1;
					break;
				default:
					assert (false);
			}
			break;
		default:
			assert (false);
			break;
	}
	return result;
}

void rai::mdb_store::block_raw_put (rai::transaction const & transaction_a, MDB_dbi database_a, rai::block_hash const & hash_a, MDB_val value_a)
{
	auto status2 (mdb_put (env.tx (transaction_a), database_a, rai::mdb_val (hash_a), &value_a, 0));
	assert (status2 == 0);
}

void rai::mdb_store::block_put (rai::transaction const & transaction_a, rai::block_hash const & hash_a, rai::block const & block_a, rai::block_hash const & successor_a, rai::epoch epoch_a)
{
	assert (successor_a.is_zero () || block_exists (transaction_a, successor_a));
	std::vector<uint8_t> vector;
	{
		rai::vectorstream stream (vector);
		block_a.serialize (stream);
		rai::write (stream, successor_a.bytes);
	}
	block_raw_put (transaction_a, block_database (block_a.type (), epoch_a), hash_a, { vector.size (), vector.data () });
	rai::block_predecessor_set predecessor (transaction_a, *this);
	block_a.visit (predecessor);
	assert (block_a.previous ().is_zero () || block_successor (transaction_a, block_a.previous ()) == hash_a);
}

MDB_val rai::mdb_store::block_raw_get (rai::transaction const & transaction_a, rai::block_hash const & hash_a, rai::block_type & type_a)
{
	rai::mdb_val result;
	auto status (mdb_get (env.tx (transaction_a), send_blocks, rai::mdb_val (hash_a), result));
	assert (status == 0 || status == MDB_NOTFOUND);
	if (status != 0)
	{
		auto status (mdb_get (env.tx (transaction_a), receive_blocks, rai::mdb_val (hash_a), result));
		assert (status == 0 || status == MDB_NOTFOUND);
		if (status != 0)
		{
			auto status (mdb_get (env.tx (transaction_a), open_blocks, rai::mdb_val (hash_a), result));
			assert (status == 0 || status == MDB_NOTFOUND);
			if (status != 0)
			{
				auto status (mdb_get (env.tx (transaction_a), change_blocks, rai::mdb_val (hash_a), result));
				assert (status == 0 || status == MDB_NOTFOUND);
				if (status != 0)
				{
					auto status (mdb_get (env.tx (transaction_a), state_blocks_v0, rai::mdb_val (hash_a), result));
					assert (status == 0 || status == MDB_NOTFOUND);
					if (status != 0)
					{
						auto status (mdb_get (env.tx (transaction_a), state_blocks_v1, rai::mdb_val (hash_a), result));
						assert (status == 0 || status == MDB_NOTFOUND);
						if (status != 0)
						{
							// Block not found
						}
						else
						{
							type_a = rai::block_type::state;
						}
					}
					else
					{
						type_a = rai::block_type::state;
					}
				}
				else
				{
					type_a = rai::block_type::change;
				}
			}
			else
			{
				type_a = rai::block_type::open;
			}
		}
		else
		{
			type_a = rai::block_type::receive;
		}
	}
	else
	{
		type_a = rai::block_type::send;
	}
	return result;
}

template <typename T>
std::unique_ptr<rai::block> rai::mdb_store::block_random (rai::transaction const & transaction_a, MDB_dbi database)
{
	rai::block_hash hash;
	rai::random_pool.GenerateBlock (hash.bytes.data (), hash.bytes.size ());
	rai::store_iterator<rai::block_hash, std::shared_ptr<T>> existing (std::make_unique<rai::mdb_iterator<rai::block_hash, std::shared_ptr<T>>> (transaction_a, database, rai::mdb_val (hash)));
	if (existing == rai::store_iterator<rai::block_hash, std::shared_ptr<T>> (nullptr))
	{
		existing = rai::store_iterator<rai::block_hash, std::shared_ptr<T>> (std::make_unique<rai::mdb_iterator<rai::block_hash, std::shared_ptr<T>>> (transaction_a, database));
	}
	auto end (rai::store_iterator<rai::block_hash, std::shared_ptr<T>> (nullptr));
	assert (existing != end);
	return block_get (transaction_a, rai::block_hash (existing->first));
}

std::unique_ptr<rai::block> rai::mdb_store::block_random (rai::transaction const & transaction_a)
{
	auto count (block_count (transaction_a));
	auto region (rai::random_pool.GenerateWord32 (0, count.sum () - 1));
	std::unique_ptr<rai::block> result;
	if (region < count.send)
	{
		result = block_random<rai::send_block> (transaction_a, send_blocks);
	}
	else
	{
		region -= count.send;
		if (region < count.receive)
		{
			result = block_random<rai::receive_block> (transaction_a, receive_blocks);
		}
		else
		{
			region -= count.receive;
			if (region < count.open)
			{
				result = block_random<rai::open_block> (transaction_a, open_blocks);
			}
			else
			{
				region -= count.open;
				if (region < count.change)
				{
					result = block_random<rai::change_block> (transaction_a, change_blocks);
				}
				else
				{
					region -= count.change;
					if (region < count.state_v0)
					{
						result = block_random<rai::state_block> (transaction_a, state_blocks_v0);
					}
					else
					{
						result = block_random<rai::state_block> (transaction_a, state_blocks_v1);
					}
				}
			}
		}
	}
	assert (result != nullptr);
	return result;
}

rai::block_hash rai::mdb_store::block_successor (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
{
	rai::block_type type;
	auto value (block_raw_get (transaction_a, hash_a, type));
	rai::block_hash result;
	if (value.mv_size != 0)
	{
		assert (value.mv_size >= result.bytes.size ());
		rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.mv_data) + value.mv_size - result.bytes.size (), result.bytes.size ());
		auto error (rai::read (stream, result.bytes));
		assert (!error);
	}
	else
	{
		result.clear ();
	}
	return result;
}

void rai::mdb_store::block_successor_clear (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
{
	auto block (block_get (transaction_a, hash_a));
	auto version (block_version (transaction_a, hash_a));
	block_put (transaction_a, hash_a, *block, 0, version);
}

std::unique_ptr<rai::block> rai::mdb_store::block_get (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
{
	rai::block_type type;
	auto value (block_raw_get (transaction_a, hash_a, type));
	std::unique_ptr<rai::block> result;
	if (value.mv_size != 0)
	{
		rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.mv_data), value.mv_size);
		result = rai::deserialize_block (stream, type);
		assert (result != nullptr);
	}
	return result;
}

void rai::mdb_store::block_del (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
{
	auto status (mdb_del (env.tx (transaction_a), state_blocks_v1, rai::mdb_val (hash_a), nullptr));
	assert (status == 0 || status == MDB_NOTFOUND);
	if (status != 0)
	{
		auto status (mdb_del (env.tx (transaction_a), state_blocks_v0, rai::mdb_val (hash_a), nullptr));
		assert (status == 0 || status == MDB_NOTFOUND);
		if (status != 0)
		{
			auto status (mdb_del (env.tx (transaction_a), send_blocks, rai::mdb_val (hash_a), nullptr));
			assert (status == 0 || status == MDB_NOTFOUND);
			if (status != 0)
			{
				auto status (mdb_del (env.tx (transaction_a), receive_blocks, rai::mdb_val (hash_a), nullptr));
				assert (status == 0 || status == MDB_NOTFOUND);
				if (status != 0)
				{
					auto status (mdb_del (env.tx (transaction_a), open_blocks, rai::mdb_val (hash_a), nullptr));
					assert (status == 0 || status == MDB_NOTFOUND);
					if (status != 0)
					{
						auto status (mdb_del (env.tx (transaction_a), change_blocks, rai::mdb_val (hash_a), nullptr));
						assert (status == 0);
					}
				}
			}
		}
	}
}

bool rai::mdb_store::block_exists (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
{
	auto exists (true);
	rai::mdb_val junk;
	auto status (mdb_get (env.tx (transaction_a), send_blocks, rai::mdb_val (hash_a), junk));
	assert (status == 0 || status == MDB_NOTFOUND);
	exists = status == 0;
	if (!exists)
	{
		auto status (mdb_get (env.tx (transaction_a), receive_blocks, rai::mdb_val (hash_a), junk));
		assert (status == 0 || status == MDB_NOTFOUND);
		exists = status == 0;
		if (!exists)
		{
			auto status (mdb_get (env.tx (transaction_a), open_blocks, rai::mdb_val (hash_a), junk));
			assert (status == 0 || status == MDB_NOTFOUND);
			exists = status == 0;
			if (!exists)
			{
				auto status (mdb_get (env.tx (transaction_a), change_blocks, rai::mdb_val (hash_a), junk));
				assert (status == 0 || status == MDB_NOTFOUND);
				exists = status == 0;
				if (!exists)
				{
					auto status (mdb_get (env.tx (transaction_a), state_blocks_v0, rai::mdb_val (hash_a), junk));
					assert (status == 0 || status == MDB_NOTFOUND);
					exists = status == 0;
					if (!exists)
					{
						auto status (mdb_get (env.tx (transaction_a), state_blocks_v1, rai::mdb_val (hash_a), junk));
						assert (status == 0 || status == MDB_NOTFOUND);
						exists = status == 0;
					}
				}
			}
		}
	}
	return exists;
}

rai::block_counts rai::mdb_store::block_count (rai::transaction const & transaction_a)
{
	rai::block_counts result;
	MDB_stat send_stats;
	auto status1 (mdb_stat (env.tx (transaction_a), send_blocks, &send_stats));
	assert (status1 == 0);
	MDB_stat receive_stats;
	auto status2 (mdb_stat (env.tx (transaction_a), receive_blocks, &receive_stats));
	assert (status2 == 0);
	MDB_stat open_stats;
	auto status3 (mdb_stat (env.tx (transaction_a), open_blocks, &open_stats));
	assert (status3 == 0);
	MDB_stat change_stats;
	auto status4 (mdb_stat (env.tx (transaction_a), change_blocks, &change_stats));
	assert (status4 == 0);
	MDB_stat state_v0_stats;
	auto status5 (mdb_stat (env.tx (transaction_a), state_blocks_v0, &state_v0_stats));
	assert (status5 == 0);
	MDB_stat state_v1_stats;
	auto status6 (mdb_stat (env.tx (transaction_a), state_blocks_v1, &state_v1_stats));
	assert (status6 == 0);
	result.send = send_stats.ms_entries;
	result.receive = receive_stats.ms_entries;
	result.open = open_stats.ms_entries;
	result.change = change_stats.ms_entries;
	result.state_v0 = state_v0_stats.ms_entries;
	result.state_v1 = state_v1_stats.ms_entries;
	return result;
}

bool rai::mdb_store::root_exists (rai::transaction const & transaction_a, rai::uint256_union const & root_a)
{
	return block_exists (transaction_a, root_a) || account_exists (transaction_a, root_a);
}

void rai::mdb_store::account_del (rai::transaction const & transaction_a, rai::account const & account_a)
{
	auto status1 (mdb_del (env.tx (transaction_a), accounts_v1, rai::mdb_val (account_a), nullptr));
	if (status1 != 0)
	{
		assert (status1 == MDB_NOTFOUND);
		auto status2 (mdb_del (env.tx (transaction_a), accounts_v0, rai::mdb_val (account_a), nullptr));
		assert (status2 == 0);
	}
}

bool rai::mdb_store::account_exists (rai::transaction const & transaction_a, rai::account const & account_a)
{
	auto iterator (latest_begin (transaction_a, account_a));
	return iterator != latest_end () && rai::account (iterator->first) == account_a;
}

bool rai::mdb_store::account_get (rai::transaction const & transaction_a, rai::account const & account_a, rai::account_info & info_a)
{
	rai::mdb_val value;
	auto status1 (mdb_get (env.tx (transaction_a), accounts_v1, rai::mdb_val (account_a), value));
	assert (status1 == 0 || status1 == MDB_NOTFOUND);
	bool result (false);
	rai::epoch epoch;
	if (status1 == 0)
	{
		epoch = rai::epoch::epoch_1;
	}
	else
	{
		auto status2 (mdb_get (env.tx (transaction_a), accounts_v0, rai::mdb_val (account_a), value));
		assert (status2 == 0 || status2 == MDB_NOTFOUND);
		if (status2 == 0)
		{
			epoch = rai::epoch::epoch_0;
		}
		else
		{
			result = true;
		}
	}
	if (!result)
	{
		rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.data ()), value.size ());
		info_a.epoch = epoch;
		info_a.deserialize (stream);
	}
	return result;
}

void rai::mdb_store::frontier_put (rai::transaction const & transaction_a, rai::block_hash const & block_a, rai::account const & account_a)
{
	auto status (mdb_put (env.tx (transaction_a), frontiers, rai::mdb_val (block_a), rai::mdb_val (account_a), 0));
	assert (status == 0);
}

rai::account rai::mdb_store::frontier_get (rai::transaction const & transaction_a, rai::block_hash const & block_a)
{
	rai::mdb_val value;
	auto status (mdb_get (env.tx (transaction_a), frontiers, rai::mdb_val (block_a), value));
	assert (status == 0 || status == MDB_NOTFOUND);
	rai::account result (0);
	if (status == 0)
	{
		result = rai::uint256_union (value);
	}
	return result;
}

void rai::mdb_store::frontier_del (rai::transaction const & transaction_a, rai::block_hash const & block_a)
{
	auto status (mdb_del (env.tx (transaction_a), frontiers, rai::mdb_val (block_a), nullptr));
	assert (status == 0);
}

size_t rai::mdb_store::account_count (rai::transaction const & transaction_a)
{
	MDB_stat stats1;
	auto status1 (mdb_stat (env.tx (transaction_a), accounts_v0, &stats1));
	assert (status1 == 0);
	MDB_stat stats2;
	auto status2 (mdb_stat (env.tx (transaction_a), accounts_v1, &stats2));
	assert (status2 == 0);
	auto result (stats1.ms_entries + stats2.ms_entries);
	return result;
}

void rai::mdb_store::account_put (rai::transaction const & transaction_a, rai::account const & account_a, rai::account_info const & info_a)
{
	MDB_dbi db;
	switch (info_a.epoch)
	{
		case rai::epoch::invalid:
		case rai::epoch::unspecified:
			assert (false);
		case rai::epoch::epoch_0:
			db = accounts_v0;
			break;
		case rai::epoch::epoch_1:
			db = accounts_v1;
			break;
	}
	auto status (mdb_put (env.tx (transaction_a), db, rai::mdb_val (account_a), rai::mdb_val (info_a), 0));
	assert (status == 0);
}

void rai::mdb_store::pending_put (rai::transaction const & transaction_a, rai::pending_key const & key_a, rai::pending_info const & pending_a)
{
	MDB_dbi db;
	switch (pending_a.epoch)
	{
		case rai::epoch::invalid:
		case rai::epoch::unspecified:
			assert (false);
		case rai::epoch::epoch_0:
			db = pending_v0;
			break;
		case rai::epoch::epoch_1:
			db = pending_v1;
			break;
	}
	auto status (mdb_put (env.tx (transaction_a), db, rai::mdb_val (key_a), rai::mdb_val (pending_a), 0));
	assert (status == 0);
}

void rai::mdb_store::pending_del (rai::transaction const & transaction_a, rai::pending_key const & key_a)
{
	auto status1 (mdb_del (env.tx (transaction_a), pending_v1, mdb_val (key_a), nullptr));
	if (status1 != 0)
	{
		assert (status1 == MDB_NOTFOUND);
		auto status2 (mdb_del (env.tx (transaction_a), pending_v0, mdb_val (key_a), nullptr));
		assert (status2 == 0);
	}
}

bool rai::mdb_store::pending_exists (rai::transaction const & transaction_a, rai::pending_key const & key_a)
{
	auto iterator (pending_begin (transaction_a, key_a));
	return iterator != pending_end () && rai::pending_key (iterator->first) == key_a;
}

bool rai::mdb_store::pending_get (rai::transaction const & transaction_a, rai::pending_key const & key_a, rai::pending_info & pending_a)
{
	rai::mdb_val value;
	auto status1 (mdb_get (env.tx (transaction_a), pending_v1, mdb_val (key_a), value));
	assert (status1 == 0 || status1 == MDB_NOTFOUND);
	bool result (false);
	rai::epoch epoch;
	if (status1 == 0)
	{
		epoch = rai::epoch::epoch_1;
	}
	else
	{
		auto status2 (mdb_get (env.tx (transaction_a), pending_v0, mdb_val (key_a), value));
		assert (status2 == 0 || status2 == MDB_NOTFOUND);
		if (status2 == 0)
		{
			epoch = rai::epoch::epoch_0;
		}
		else
		{
			result = true;
		}
	}
	if (!result)
	{
		rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.data ()), value.size ());
		pending_a.epoch = epoch;
		pending_a.deserialize (stream);
	}
	return result;
}

rai::store_iterator<rai::pending_key, rai::pending_info> rai::mdb_store::pending_begin (rai::transaction const & transaction_a, rai::pending_key const & key_a)
{
	rai::store_iterator<rai::pending_key, rai::pending_info> result (std::make_unique<rai::mdb_merge_iterator<rai::pending_key, rai::pending_info>> (transaction_a, pending_v0, pending_v1, mdb_val (key_a)));
	return result;
}

rai::store_iterator<rai::pending_key, rai::pending_info> rai::mdb_store::pending_begin (rai::transaction const & transaction_a)
{
	rai::store_iterator<rai::pending_key, rai::pending_info> result (std::make_unique<rai::mdb_merge_iterator<rai::pending_key, rai::pending_info>> (transaction_a, pending_v0, pending_v1));
	return result;
}

rai::store_iterator<rai::pending_key, rai::pending_info> rai::mdb_store::pending_end ()
{
	rai::store_iterator<rai::pending_key, rai::pending_info> result (nullptr);
	return result;
}

rai::store_iterator<rai::pending_key, rai::pending_info> rai::mdb_store::pending_v0_begin (rai::transaction const & transaction_a, rai::pending_key const & key_a)
{
	rai::store_iterator<rai::pending_key, rai::pending_info> result (std::make_unique<rai::mdb_iterator<rai::pending_key, rai::pending_info>> (transaction_a, pending_v0, mdb_val (key_a)));
	return result;
}

rai::store_iterator<rai::pending_key, rai::pending_info> rai::mdb_store::pending_v0_begin (rai::transaction const & transaction_a)
{
	rai::store_iterator<rai::pending_key, rai::pending_info> result (std::make_unique<rai::mdb_iterator<rai::pending_key, rai::pending_info>> (transaction_a, pending_v0));
	return result;
}

rai::store_iterator<rai::pending_key, rai::pending_info> rai::mdb_store::pending_v0_end ()
{
	rai::store_iterator<rai::pending_key, rai::pending_info> result (nullptr);
	return result;
}

rai::store_iterator<rai::pending_key, rai::pending_info> rai::mdb_store::pending_v1_begin (rai::transaction const & transaction_a, rai::pending_key const & key_a)
{
	rai::store_iterator<rai::pending_key, rai::pending_info> result (std::make_unique<rai::mdb_iterator<rai::pending_key, rai::pending_info>> (transaction_a, pending_v1, mdb_val (key_a)));
	return result;
}

rai::store_iterator<rai::pending_key, rai::pending_info> rai::mdb_store::pending_v1_begin (rai::transaction const & transaction_a)
{
	rai::store_iterator<rai::pending_key, rai::pending_info> result (std::make_unique<rai::mdb_iterator<rai::pending_key, rai::pending_info>> (transaction_a, pending_v1));
	return result;
}

rai::store_iterator<rai::pending_key, rai::pending_info> rai::mdb_store::pending_v1_end ()
{
	rai::store_iterator<rai::pending_key, rai::pending_info> result (nullptr);
	return result;
}

void rai::mdb_store::block_info_put (rai::transaction const & transaction_a, rai::block_hash const & hash_a, rai::block_info const & block_info_a)
{
	auto status (mdb_put (env.tx (transaction_a), blocks_info, rai::mdb_val (hash_a), rai::mdb_val (block_info_a), 0));
	assert (status == 0);
}

void rai::mdb_store::block_info_del (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
{
	auto status (mdb_del (env.tx (transaction_a), blocks_info, rai::mdb_val (hash_a), nullptr));
	assert (status == 0);
}

bool rai::mdb_store::block_info_exists (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
{
	auto iterator (block_info_begin (transaction_a, hash_a));
	return iterator != block_info_end () && rai::block_hash (iterator->first) == hash_a;
}

bool rai::mdb_store::block_info_get (rai::transaction const & transaction_a, rai::block_hash const & hash_a, rai::block_info & block_info_a)
{
	rai::mdb_val value;
	auto status (mdb_get (env.tx (transaction_a), blocks_info, rai::mdb_val (hash_a), value));
	assert (status == 0 || status == MDB_NOTFOUND);
	bool result (true);
	if (status != MDB_NOTFOUND)
	{
		result = false;
		assert (value.size () == sizeof (block_info_a.account.bytes) + sizeof (block_info_a.balance.bytes));
		rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.data ()), value.size ());
		auto error1 (rai::read (stream, block_info_a.account));
		assert (!error1);
		auto error2 (rai::read (stream, block_info_a.balance));
		assert (!error2);
	}
	return result;
}

rai::uint128_t rai::mdb_store::representation_get (rai::transaction const & transaction_a, rai::account const & account_a)
{
	rai::mdb_val value;
	auto status (mdb_get (env.tx (transaction_a), representation, rai::mdb_val (account_a), value));
	assert (status == 0 || status == MDB_NOTFOUND);
	rai::uint128_t result = 0;
	if (status == 0)
	{
		rai::uint128_union rep;
		rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.data ()), value.size ());
		auto error (rai::read (stream, rep));
		assert (!error);
		result = rep.number ();
	}
	return result;
}

void rai::mdb_store::representation_put (rai::transaction const & transaction_a, rai::account const & account_a, rai::uint128_t const & representation_a)
{
	rai::uint128_union rep (representation_a);
	auto status (mdb_put (env.tx (transaction_a), representation, rai::mdb_val (account_a), rai::mdb_val (rep), 0));
	assert (status == 0);
}

void rai::mdb_store::unchecked_clear (rai::transaction const & transaction_a)
{
	auto status (mdb_drop (env.tx (transaction_a), unchecked, 0));
	assert (status == 0);
}

void rai::mdb_store::unchecked_put (rai::transaction const & transaction_a, rai::block_hash const & hash_a, std::shared_ptr<rai::block> const & block_a)
{
	// Checking if same unchecked block is already in database
	bool exists (false);
	auto block_hash (block_a->hash ());
	auto cached (unchecked_get (transaction_a, hash_a));
	for (auto i (cached.begin ()), n (cached.end ()); i != n && !exists; ++i)
	{
		if ((*i)->hash () == block_hash)
		{
			exists = true;
		}
	}
	// Inserting block if it wasn't found in database
	if (!exists)
	{
		mdb_val block (block_a);
		auto status (mdb_put (env.tx (transaction_a), unchecked, rai::mdb_val (hash_a), block, 0));
		assert (status == 0);
	}
}

std::shared_ptr<rai::vote> rai::mdb_store::vote_get (rai::transaction const & transaction_a, rai::account const & account_a)
{
	std::shared_ptr<rai::vote> result;
	rai::mdb_val value;
	auto status (mdb_get (env.tx (transaction_a), vote, rai::mdb_val (account_a), value));
	assert (status == 0 || status == MDB_NOTFOUND);
	if (status == 0)
	{
		std::shared_ptr<rai::vote> result (value);
		assert (result != nullptr);
		return result;
	}
	return nullptr;
}

std::vector<std::shared_ptr<rai::block>> rai::mdb_store::unchecked_get (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
{
	std::vector<std::shared_ptr<rai::block>> result;
	for (auto i (unchecked_begin (transaction_a, hash_a)), n (unchecked_begin (transaction_a, hash_a.number () + 1)); i != n; ++i)
	{
		assert (rai::block_hash (i->first) == hash_a);
		std::shared_ptr<rai::block> block (i->second);
		result.push_back (block);
	}
	return result;
}

void rai::mdb_store::unchecked_del (rai::transaction const & transaction_a, rai::block_hash const & hash_a, std::shared_ptr<rai::block> block_a)
{
	rai::mdb_val block (block_a);
	auto status (mdb_del (env.tx (transaction_a), unchecked, rai::mdb_val (hash_a), block));
	assert (status == 0 || status == MDB_NOTFOUND);
}

size_t rai::mdb_store::unchecked_count (rai::transaction const & transaction_a)
{
	MDB_stat unchecked_stats;
	auto status (mdb_stat (env.tx (transaction_a), unchecked, &unchecked_stats));
	assert (status == 0);
	auto result (unchecked_stats.ms_entries);
	return result;
}

void rai::mdb_store::checksum_put (rai::transaction const & transaction_a, uint64_t prefix, uint8_t mask, rai::uint256_union const & hash_a)
{
	assert ((prefix & 0xff) == 0);
	uint64_t key (prefix | mask);
	auto status (mdb_put (env.tx (transaction_a), checksum, rai::mdb_val (sizeof (key), &key), rai::mdb_val (hash_a), 0));
	assert (status == 0);
}

bool rai::mdb_store::checksum_get (rai::transaction const & transaction_a, uint64_t prefix, uint8_t mask, rai::uint256_union & hash_a)
{
	assert ((prefix & 0xff) == 0);
	uint64_t key (prefix | mask);
	rai::mdb_val value;
	auto status (mdb_get (env.tx (transaction_a), checksum, rai::mdb_val (sizeof (key), &key), value));
	assert (status == 0 || status == MDB_NOTFOUND);
	bool result (true);
	if (status == 0)
	{
		result = false;
		rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value.data ()), value.size ());
		auto error (rai::read (stream, hash_a));
		assert (!error);
	}
	return result;
}

void rai::mdb_store::checksum_del (rai::transaction const & transaction_a, uint64_t prefix, uint8_t mask)
{
	assert ((prefix & 0xff) == 0);
	uint64_t key (prefix | mask);
	auto status (mdb_del (env.tx (transaction_a), checksum, rai::mdb_val (sizeof (key), &key), nullptr));
	assert (status == 0);
}

void rai::mdb_store::flush (rai::transaction const & transaction_a)
{
	std::unordered_map<rai::account, std::shared_ptr<rai::vote>> sequence_cache_l;
	{
		std::lock_guard<std::mutex> lock (cache_mutex);
		sequence_cache_l.swap (vote_cache);
	}
	for (auto i (sequence_cache_l.begin ()), n (sequence_cache_l.end ()); i != n; ++i)
	{
		std::vector<uint8_t> vector;
		{
			rai::vectorstream stream (vector);
			i->second->serialize (stream);
		}
		auto status1 (mdb_put (env.tx (transaction_a), vote, rai::mdb_val (i->first), rai::mdb_val (vector.size (), vector.data ()), 0));
		assert (status1 == 0);
	}
}
std::shared_ptr<rai::vote> rai::mdb_store::vote_current (rai::transaction const & transaction_a, rai::account const & account_a)
{
	assert (!cache_mutex.try_lock ());
	std::shared_ptr<rai::vote> result;
	auto existing (vote_cache.find (account_a));
	if (existing != vote_cache.end ())
	{
		result = existing->second;
	}
	else
	{
		result = vote_get (transaction_a, account_a);
	}
	return result;
}

std::shared_ptr<rai::vote> rai::mdb_store::vote_generate (rai::transaction const & transaction_a, rai::account const & account_a, rai::raw_key const & key_a, std::shared_ptr<rai::block> block_a)
{
	std::lock_guard<std::mutex> lock (cache_mutex);
	auto result (vote_current (transaction_a, account_a));
	uint64_t sequence ((result ? result->sequence : 0) + 1);
	result = std::make_shared<rai::vote> (account_a, key_a, sequence, block_a);
	vote_cache[account_a] = result;
	return result;
}

std::shared_ptr<rai::vote> rai::mdb_store::vote_generate (rai::transaction const & transaction_a, rai::account const & account_a, rai::raw_key const & key_a, std::vector<rai::block_hash> blocks_a)
{
	std::lock_guard<std::mutex> lock (cache_mutex);
	auto result (vote_current (transaction_a, account_a));
	uint64_t sequence ((result ? result->sequence : 0) + 1);
	result = std::make_shared<rai::vote> (account_a, key_a, sequence, blocks_a);
	vote_cache[account_a] = result;
	return result;
}

std::shared_ptr<rai::vote> rai::mdb_store::vote_max (rai::transaction const & transaction_a, std::shared_ptr<rai::vote> vote_a)
{
	std::lock_guard<std::mutex> lock (cache_mutex);
	auto current (vote_current (transaction_a, vote_a->account));
	auto result (vote_a);
	if (current != nullptr && current->sequence > result->sequence)
	{
		result = current;
	}
	vote_cache[vote_a->account] = result;
	return result;
}

rai::store_iterator<rai::account, rai::account_info> rai::mdb_store::latest_begin (rai::transaction const & transaction_a, rai::account const & account_a)
{
	rai::store_iterator<rai::account, rai::account_info> result (std::make_unique<rai::mdb_merge_iterator<rai::account, rai::account_info>> (transaction_a, accounts_v0, accounts_v1, rai::mdb_val (account_a)));
	return result;
}

rai::store_iterator<rai::account, rai::account_info> rai::mdb_store::latest_begin (rai::transaction const & transaction_a)
{
	rai::store_iterator<rai::account, rai::account_info> result (std::make_unique<rai::mdb_merge_iterator<rai::account, rai::account_info>> (transaction_a, accounts_v0, accounts_v1));
	return result;
}

rai::store_iterator<rai::account, rai::account_info> rai::mdb_store::latest_end ()
{
	rai::store_iterator<rai::account, rai::account_info> result (nullptr);
	return result;
}

rai::store_iterator<rai::account, rai::account_info> rai::mdb_store::latest_v0_begin (rai::transaction const & transaction_a, rai::account const & account_a)
{
	rai::store_iterator<rai::account, rai::account_info> result (std::make_unique<rai::mdb_iterator<rai::account, rai::account_info>> (transaction_a, accounts_v0, rai::mdb_val (account_a)));
	return result;
}

rai::store_iterator<rai::account, rai::account_info> rai::mdb_store::latest_v0_begin (rai::transaction const & transaction_a)
{
	rai::store_iterator<rai::account, rai::account_info> result (std::make_unique<rai::mdb_iterator<rai::account, rai::account_info>> (transaction_a, accounts_v0));
	return result;
}

rai::store_iterator<rai::account, rai::account_info> rai::mdb_store::latest_v0_end ()
{
	rai::store_iterator<rai::account, rai::account_info> result (nullptr);
	return result;
}

rai::store_iterator<rai::account, rai::account_info> rai::mdb_store::latest_v1_begin (rai::transaction const & transaction_a, rai::account const & account_a)
{
	rai::store_iterator<rai::account, rai::account_info> result (std::make_unique<rai::mdb_iterator<rai::account, rai::account_info>> (transaction_a, accounts_v1, rai::mdb_val (account_a)));
	return result;
}

rai::store_iterator<rai::account, rai::account_info> rai::mdb_store::latest_v1_begin (rai::transaction const & transaction_a)
{
	rai::store_iterator<rai::account, rai::account_info> result (std::make_unique<rai::mdb_iterator<rai::account, rai::account_info>> (transaction_a, accounts_v1));
	return result;
}

rai::store_iterator<rai::account, rai::account_info> rai::mdb_store::latest_v1_end ()
{
	rai::store_iterator<rai::account, rai::account_info> result (nullptr);
	return result;
}

max_stars_repo_name: bitcoin-black/bitcoin-black

max_issues_repo_name: bitcoin-black/bitcoin-black


text:
////////////////////////////////////////////////////////////////////////////////////////////////////
// display_expr.cpp
//
//  Copyright 2010 Eric Niebler. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <sstream>
#include <boost/proto/v5/proto.hpp>
#include <boost/proto/v5/debug.hpp>
#include "./unit_test.hpp"

namespace mpl = boost::mpl;
namespace proto = boost::proto;
using proto::_;

struct A {};
struct B : A {};
std::ostream& operator<<( std::ostream& out, const A& ) { return out << "this is A!"; }

struct C {};

void test_display_expr()
{
    // https://svn.boost.org/trac/boost/ticket/4910
    proto::literal<int> i{0};
    std::string intname = proto::detail::name_of<int>();

    {
        std::stringstream sout;
        proto::display_expr(i + A(), sout);
        std::string Aname = proto::detail::name_of<A>();
        BOOST_CHECK_EQUAL(sout.str(), std::string(
          "plus(\n"
          "    terminal( (int) 0 ) &\n"
          "  , terminal( (" + Aname + ") this is A! )\n"
          ")\n"));
    }

    {
        std::stringstream sout;
        proto::display_expr(i + B(), sout);
        std::string Bname = proto::detail::name_of<B>();
        BOOST_CHECK_EQUAL(sout.str(), std::string(
          "plus(\n"
          "    terminal( (" + intname + ") 0 ) &\n"
          "  , terminal( (" + Bname + ") this is A! )\n"
          ")\n"));
    }

    {
        std::stringstream sout;
        std::string Cname = proto::detail::name_of<C>();
        proto::display_expr(i + C(), sout);
        BOOST_CHECK_EQUAL(sout.str(), std::string(
          "plus(\n"
          "    terminal( (" + intname + ") 0 ) &\n"
          "  , terminal( (" + Cname + ") " + Cname + " )\n"
          ")\n"));
    }
}

using namespace boost::unit_test;
////////////////////////////////////////////////////////////////////////////////////////////////////
// init_unit_test_suite
//
test_suite* init_unit_test_suite( int argc, char* argv[] )
{
    test_suite *test = BOOST_TEST_SUITE("test display_expr() function");
    test->add(BOOST_TEST_CASE(&test_display_expr));
    return test;
}

ext: cpp


text:
/*
 * solvers.cpp
 *
 *  Created on: Sep 29, 2016
 *      Author: george
 */

#include "solvers.hpp"

#include <gecode/int.hh>
#include <gecode/float.hh>
#include <gecode/search.hh>
#include <gecode/gist.hh>

#include <boost/numeric/ublas/io.hpp>

using namespace Gecode;

class LinearEqSysSolver : public Space {
protected:
  IntVarArray x;
public:
  LinearEqSysSolver(
      const std::vector<std::vector<int>>& A,
      const std::vector<int>& y) throw (InvalidArgumentException)
    : x(*this, y.size(), 1, Int::Limits::max) {

    const size_t n_actors = y.size();
    const size_t n_channels = A.size();

    for (size_t i=0; i<n_channels; i++ ) {
      IntArgs c(n_actors);
      IntVarArgs xs(n_actors);
      for (size_t j=0; j<n_actors; j++ ) {
        c[j]  = A[i][j];
        xs[j] = x[j];
      }

      linear(*this, c, xs, IRT_EQ, y[i]);
    }
    branch(*this, x, INT_VAR_SIZE_MIN(), INT_VAL_MIN());
  }

  LinearEqSysSolver(bool share, LinearEqSysSolver& s) : Space(share, s) {
    x.update(*this, share, s.x);
  }

  virtual Space* copy(bool share) {
    return new LinearEqSysSolver(share,*this);
  }

  void print(void) const {
    std::cout << x << std::endl;
  }

  vector<int> getSolution(void) const {
    vector<int> tmp_sol;
    for (int i=0; i<x.size(); i++) {
      //if x[i].assigned()
      tmp_sol.push_back(x[i].val());
      //...else something is wrong
    }
    return tmp_sol;
  }

  // constrain function
  virtual void constrain(const Space& _b) {
    const LinearEqSysSolver& b = static_cast<const LinearEqSysSolver&>(_b);

    int sum = 0;
    IntVarArgs xs(b.x.size());
    for (int i=0; i<b.x.size(); i++) {
      sum += b.x[i].val();
      xs[i] = x[i];
    }
    linear(*this, xs, IRT_LE, sum);
  }
};

class FloatSysSolver : public Space {
protected:
  FloatVarArray x;
public:
  FloatSysSolver(
      const std::vector<std::vector<int>>& A,
      const std::vector<int>& y) throw (InvalidArgumentException)
    : x(*this, y.size(), 1, Float::Limits::max) {

    const size_t n_actors = y.size();
    const size_t n_channels = A.size();

    for (size_t i=0; i<n_channels; i++ ) {
      FloatValArgs c(n_actors);
      FloatVarArgs xs(n_actors);
      for (size_t j=0; j<n_actors; j++ ) {
        c[j]  = A[i][j];
        xs[j] = x[j];
      }

      linear(*this, c, xs, FRT_EQ, y[i]);
    }
    branch(*this, x, FLOAT_VAR_SIZE_MIN(), FLOAT_VAL_SPLIT_MIN());
  }

  FloatSysSolver(bool share, FloatSysSolver& s) : Space(share, s) {
    x.update(*this, share, s.x);
  }

  virtual Space* copy(bool share) {
    return new FloatSysSolver(share,*this);
  }

  void print(void) const {
    std::cout << x << std::endl;
  }

  vector<double> getSolution(void) const {
    vector<double> tmp_sol;
    for (int i=0; i<x.size(); i++) {
      //if x[i].assigned()
      tmp_sol.push_back(x[i].val().min());
      //...else something is wrong
    }
    return tmp_sol;
  }

  // constrain function
  virtual void constrain(const Space& _b) {
    const FloatSysSolver& b = static_cast<const FloatSysSolver&>(_b);

    double sum = 0;
    FloatVarArgs xs(b.x.size());
    for (int i=0; i<b.x.size(); i++) {
      sum += b.x[i].val().max();
      xs[i] = x[i];
    }
    linear(*this, xs, FRT_LE, sum);
  }
};

vector<int> tools::solveLinearEqSys(
    const std::vector<std::vector<int>>& A,
    const std::vector<int>& y) throw (InvalidArgumentException) {

  LinearEqSysSolver* m = new LinearEqSysSolver(A,y);
  //Gist::bab(m);
  BAB<LinearEqSysSolver> e(m);
  delete m;

  std::vector<int> solutions;
  while (LinearEqSysSolver* s = e.next()) {
    s->print();
    solutions = s->getSolution();
    delete s;
  }
  //if solutions.size() = 0, SDFG is probably not consistent!
  return solutions;
}

vector<double> tools::solveFloatSys(
    const std::vector<std::vector<int>>& A,
    const std::vector<int>& y) throw (InvalidArgumentException) {

  FloatSysSolver* m = new FloatSysSolver(A,y);
  //Gist::bab(m);
  BAB<FloatSysSolver> e(m);
  delete m;

  std::vector<double> solutions;
  while (FloatSysSolver* s = e.next()) {
    s->print();
    solutions = s->getSolution();
    delete s;
  }
  //if solutions.size() = 0, SDFG is probably not consistent!
  return solutions;
}

max_stars_repo_name: forsyde/DeSyDe

max_issues_repo_name: forsyde/DeSyDe

max_forks_repo_name: forsyde/DeSyDe


text:
//---------------------------------------------------------------------------//
//!
//! \file   tstMomentumConversion.cpp
//! \author Alex Robinson
//! \brief  The momentum conversion unit tests
//!
//---------------------------------------------------------------------------//

// Std Lib Includes
#include <iostream>

// Boost Includes
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <boost/test/tools/floating_point_comparison.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/systems/si.hpp>
#include <boost/units/systems/cgs.hpp>

// FRENSIE Includes
#include "Utility_MomentumUnits.hpp"
#include "Utility_RawPhysicalConstants.hpp"

using namespace Utility::Units;

//---------------------------------------------------------------------------//
// Tests
//---------------------------------------------------------------------------//
// Check that the momentum units can be converted
BOOST_AUTO_TEST_CASE( convert )
{
  boost::units::quantity<Utility::Units::AtomicMomentum> atomic_momentum_q( 1.0*mec_momentum );

  boost::units::quantity<Utility::Units::MeCMomentum> mec_momentum_q( 1.0*atomic_momentum );

  BOOST_CHECK_CLOSE_FRACTION(
		atomic_momentum_q.value(),
		Utility::RawPhysicalConstants::inverse_fine_structure_constant,
		1e-15 );
  BOOST_CHECK_CLOSE_FRACTION(
			mec_momentum_q.value(),
			Utility::RawPhysicalConstants::fine_structure_constant,
			1e-15 );
}

//---------------------------------------------------------------------------//
// end tstMomentumConversion.cpp
//---------------------------------------------------------------------------//


text: #include <boost/mpl/aux_/preprocessed/msvc60/minus.hpp>

ext: hpp

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino


text:
#include "communication.hpp"
#include "logging.hpp"
#include <boost/mpi.hpp>
#include <cstddef>
#include <iostream>

namespace n_body::communication {

Division::Division(const boost::mpi::communicator &comm, std::size_t total) {
  const auto rank = static_cast<std::size_t>(comm.rank());
  const auto size = static_cast<std::size_t>(comm.size());

  if (total % size != 0) {
    logging::logger(logging::Level::Error)
        << "total(" << total << ")"
        << "is not divisible by rank(" << rank << ")" << std::endl;
    comm.abort(MPI_ERR_ARG);
  }

  const auto basic_local_count = total / size;
  this->begin = basic_local_count * rank;
  this->end = basic_local_count * (rank + 1);
  this->count = this->end - this->begin;

  auto &lg = logging::logger(logging::Level::Debug);
  lg << "division = ";
  lg << ".count { " << this->count << ", }, "
     << ".begin { " << this->begin << ", }, "
     << ".end { " << this->end << ", }, " << std::endl;
}

} // namespace n_body::communication

max_stars_repo_name: linyinfeng/n-body

max_issues_repo_name: linyinfeng/n-body

max_forks_repo_name: linyinfeng/n-body


text:
/**
 * This code has been downloaded from the website of Bojan Nikolic
 * see http://www.bnikolic.co.uk/blog/cpp-khachiyan-min-cov-ellipsoid.html
 * the code implements Khachiyan's algorithm to compute the Minimum volume
 * enclosing ellipsoid approximately.
 *
 * Some parts were written inefficiently so have been changed.
 */

#include "MVE.h"
#include "RMSUtils.h"
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/lu.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>
#include "cholesky.h"

namespace ublas=boost::numeric::ublas;

template<class T>
bool InvertMatrix(const ublas::matrix<T> &input, ublas::matrix<T> &inverse) {
    using namespace boost::numeric::ublas;

    typedef permutation_matrix<std::size_t> pmatrix;
    matrix<T> A(input);
    pmatrix pm(A.size1());
    int res = lu_factorize(A, pm);
    if (res != 0) return false;
    inverse.assign(identity_matrix<T>(A.size1()));
    lu_substitute(A, pm, inverse);
    return true;
}

void InvertLP(const ublas::matrix<double> &Lambdap, ublas::matrix<double> &LpInv) {
    bool res = InvertMatrix(Lambdap, LpInv);
    if (not res) {
        throw std::runtime_error("Could not invert Matrix");
    }
}

void Lift(const ublas::matrix<double> &A, ublas::matrix<double> &Ap) {
    Ap.resize(A.size1() + 1,
              A.size2());
    ublas::matrix_range<ublas::matrix<double> >
            sub(Ap,
                ublas::range(0, A.size1()),
                ublas::range(0, A.size2()));
    sub.assign(A);
    ublas::row(Ap, Ap.size1() - 1) = ublas::scalar_vector<double>(A.size2(), 1.0);

}

void genDiag(const ublas::vector<double> &p,
             ublas::matrix<double> &res) {
    res.assign(ublas::zero_matrix<double>(p.size(),
                                          p.size()));
    for (size_t i = 0; i < p.size(); ++i) {
        res(i, i) = p(i);
    }
}

void KaLambda(const ublas::matrix<double> &Ap,
              const ublas::vector<double> &p,
              ublas::matrix<double> &Lambdap) {
    //cout << "KaLambda: Ap.size1() = " << Ap.size1() << ", Ap.size2() = "
    //	 << Ap.size2() << ", p.size() = " << p.size() << endl;
    assert(p.size() == Ap.size2());

    /**
        This code is very inefficient (It is trying to allocate a matrix
        of size m x m where m = # of points which is simply infeasible
        for large data sets

        It is basically trying to compute the sum of p_i q_i q_i^t for
        i in 1 to m where m is the number of points and p is a m dimensional
        vector p = (p_1, ...., p_m)^t. Since each q_i is a n x 1 vector
        the return value of this is a n x n matrix.
     **/
    /*
    ublas::matrix<double> dp(p.size(), p.size());
    genDiag(p, dp);

    dp=ublas::prod(dp, ublas::trans(Ap));
    Lambdap=ublas::prod(Ap,
                        dp);
    */

    /* Recall that Lambda(p) is a matrix of size n x n where it is simply
       defined as Lambda(p) = sum of p_i q_i q_i^t for i from 1 to m
       and q_i are the n x 1 vectors representing the (lifted points)
       The matrix Ap is (q_i .... q_m). It is a n x m matrix
    */
    size_t n = Ap.size1();
    ublas::matrix<double> sum(n, n);
    sum.assign(ublas::zero_matrix<double>(n, n));

    /* directly invoke the correct formula for matrix product */
    for (size_t i = 0; i < p.size(); i++) {
        for (size_t j = 0; j < n; j++)
            for (size_t k = 0; k < n; k++)
                sum(j, k) += p(i) * Ap(j, i) * Ap(k, i);
    }
    Lambdap = sum;
}

double KhachiyanIter(const ublas::matrix<double> &Ap,
                     ublas::vector<double> &p) {
    /// Dimensionality of the problem
    const size_t d = Ap.size1() - 1;

    ublas::matrix<double> Lp;
    KaLambda(Ap, p, Lp);
    ublas::matrix<double> ILp(Lp.size1(), Lp.size2());
    InvertLP(Lp, ILp);

    /**
        * This code is very inefficient as it is trying to allocate
        * a matrix of size m x m where m is the number of data
        * points simply to get the diagonal elements of M !!

        * Basically it is trying to do the following:
	* M = ILp * Ap where ILp is a n x n matrix and
	* Ap is a n x m matrix (n is dimension, m is number
	* of points). The needed output is a set of m numbers
	* where the i-th number is basically <Ap_i^t, M_i>
	* i.e. the dot product of the transpose of the i-th
	* column of Ap and the i-th column of M.

    ublas::matrix<double> M;
    M=ublas::prod(ILp, Ap);
    M=ublas::prod(ublas::trans(Ap), M);

    double maxval=0;
    size_t maxi=0;
    for(size_t i=0; i<M.size1(); ++i)
    {
        if (M(i,i) > maxval)
        {
            maxval=M(i,i);
            maxi=i;
        }
    }
    **/

    double maxval = 0;
    size_t maxi = 0;
    ublas::matrix<double> M = ublas::prod(ILp, Ap);
    ublas::matrix<double> Apt = ublas::trans(Ap);
    assert(M.size1() == Apt.size2());

    for (size_t i = 0; i < Ap.size2(); i++) {
        //evaluate the product c_i^t (ILp) c_i
        ublas::matrix_column<ublas::matrix<double> > mc(M, i);
        ublas::matrix_row<ublas::matrix<double> > mr(Apt, i);

        //evaluate the left product of row and column
        double prod = 0;
        for (size_t j = 0; j < mc.size(); j++)
            prod += mr(j) * mc(j);

        if (prod > maxval) {
            maxval = prod;
            maxi = i;
        }

    }

    const double step_size = (maxval - d - 1) / ((d + 1) * (maxval - 1));
    ublas::vector<double> newp = p * (1 - step_size);
    newp(maxi) += step_size;

    const double err = ublas::norm_2(newp - p);
    p = newp;
    return err;

}

void KaInvertDual(const ublas::matrix<double> &A,
                  const ublas::vector<double> &p,
                  ublas::matrix<double> &Q,
                  ublas::vector<double> &c
) {
    const size_t d = A.size1();
    /** This part of the code is not efficient. It is trying
        to allocate a matrix dp of size m x m where m is the
        number of points

	Basically it is trying to do the following:
	Here A is a n x m matrix (where n is dimension
	and m is number of points)

	The output of this code is PN a m x n matrix where
	the row i is the transpose of the i-th column of A
	multiplied by p_i where p is a m-vector.
    **/
/*
    ublas::matrix<double> dp(p.size(), p.size());
    genDiag(p, dp);

    ublas::matrix<double> PN=ublas::prod(dp, ublas::trans(A));
*/
    //========= Begin replacement code ===============
    ublas::matrix<double> PN = ublas::trans(A);
    assert(p.size() == PN.size1());
    for (size_t i = 0; i < PN.size1(); i++)
        for (size_t j = 0; j < PN.size2(); j++)
            PN(i, j) *= p(i);
    //======== End replacement code ===================

    PN = ublas::prod(A, PN);

    ublas::vector<double> M2 = ublas::prod(A, p);
    ublas::matrix<double> M3 = ublas::outer_prod(M2, M2);

    ublas::matrix<double> invert(PN.size1(), PN.size2());
    InvertLP(PN - M3, invert);

    Q.assign(1.0 / d * invert);
    c = ublas::prod(A, p);
}

double KhachiyanAlgo(const ublas::matrix<double> &A,
                     double eps,
                     size_t maxiter,
                     ublas::matrix<double> &Q,
                     ublas::vector<double> &c) {
    ublas::vector<double> p = ublas::scalar_vector<double>(A.size2(), 1.0) * (1.0 / A.size2());

    ublas::matrix<double> Ap;
    Lift(A, Ap);

    double ceps = eps * 2;
    for (size_t i = 0; i < maxiter && ceps > eps; ++i) {
        ceps = KhachiyanIter(Ap, p);
    }

    KaInvertDual(A, p, Q, c);

    return ceps;
}

void print_matrix(const ublas::matrix<double> &A) {
    for (size_t i = 0; i < A.size1(); i++) {
        for (size_t j = 0; j < A.size2(); j++)
            cout << A(i, j) << "  ";
        cout << endl;
    }
}

void print_vector(const ublas::vector<double> &v) {
    if (v.size() == 1) {
        cout << v(0) << endl;
        return;
    }

    cout << "---     ---" << endl;
    for (size_t i = 0; i < v.size(); i++) {
        cout << "||" << " " << v(i) << " " << "||" << endl;
    }

    cout << "---     ---" << endl;
}

void MVEUtil::GetNormalizedMVE(const vector<Point> &dataP,
                               float epsilon,
                               vector<Point> &normalizedP,
                               double &outer_rad,
                               double &inner_rad
) {
    using namespace boost::numeric::ublas;

    if (dataP.size() == 0)
        return;

    size_t d = dataP[0].get_dimension();

    ublas::matrix<double> A(d, dataP.size());

    size_t j = 0;

    for (size_t i = 0; i < dataP.size(); i++) {
        for (size_t k = 0; k < d; k++)
            A(k, j) = dataP[i].get_coordinate(k);
        ++j;
    }

    //try Khachiyans algorithm
    //cout << "Running Khachiyan algorithm " << endl;

    ublas::matrix<double> Q(d, d);
    ublas::vector<double> c(d);

    size_t maxiter = 1024 * dataP.size() * d;
    double ceps = KhachiyanAlgo(A, epsilon, maxiter, Q, c);
    if (ceps > epsilon) {
        throw std::runtime_error("Khachiyan failed ... ");
    }

    /** Only for debug
    cout << "Khachiyan returned " << ceps << endl;
    cout << "Printing the matrix Q " << endl;
    print_matrix(Q);
    **/

    /**
        The equation of the ellipse that is returned
	by Khachiyan algorithm is (x - c)^T Q (x - c) <= 1
        Unfortunately all the points of the data set
	do not satisfy it (but they do satisfy it
	approximately). Now we can put (1+epsilon) * dim
	on the right hand side and the points will
	still satisfy it, but this may be too loose
	an approximation. Instead, we find the maximum
	of (x-c)^T Q (x-c) over all the data points,
	add 0.01, and take that as the RHS.
	The square root of this gives the radius of
	the outer sphere, when we apply a linear
	transform to turn the ellipsoid into a ball
    **/
    double max_val = 0;
    for (size_t i = 0; i < dataP.size(); i++) {
        ublas::vector<double> ublasp = Point::to_ublas(dataP[i]);

        //compute p - c
        ublasp = ublasp - c;

        //compute Q (p - c)
        ublas::vector<double> prod1 = ublas::prod(Q, ublasp);

        //compute (p - c)^T
        ublas::matrix<double> ublasptr(1, d);
        for (size_t j = 0; j < d; j++)
            ublasptr(0, j) = ublasp[j];

        //compute (p-c)^T Q (p-c)
        ublas::vector<double> x = ublas::prod(ublasptr, prod1);

        if (x(0) > max_val) max_val = x(0);

        /** for debug
        cout << "Printing (p-c)^T Q (p -c ) " ;
        print_vector(x);
        cout << endl;
        **/
    }

    //the radius of the outer sphere
    outer_rad = sqrt(max_val + 0.01);

    //now compute the linear transformation
    ublas::matrix<double> L(d, d);
    //compute a Cholesky factorization
    int res = cholesky_decompose(Q, L);

    if (res != 0) {
        cout << "Cholesky decomposition failed " << res << endl;
        exit(1);
    }

    /** debug
    cout << "Printing cholesky decomposition " << endl;
    print_matrix(L);
    **/

    //get the transpose of L
    ublas::matrix<double> LTr(d, d);
    LTr = ublas::trans(L);

    //ublas::matrix<double> chprod = ublas::prod(L, LTr);
    //print_matrix(chprod);

    normalizedP = dataP;

    for (size_t i = 0; i < dataP.size(); i++) {
        ublas::vector<double> ublasp = Point::to_ublas(normalizedP[i]);

        //compute the linear transformation
        ublasp = ublasp - c;
        ublasp = ublas::prod(LTr, ublasp);
        normalizedP[i] = Point::from_ublas(ublasp);
    }

    inner_rad = 1 / ((1 + epsilon) * d);

    /* save this matrix into RandomUtil class for further use */
    /* the use requires the inverse of (matrix transpose) of Ltr */
    ublas::matrix<double> IL(d, d);
    InvertLP(L, IL);
    RMSUtils::dimension = d;
    RMSUtils::transformation_matrix = IL;
    RMSUtils::center = c;

    return;
}


text:
/*
Author: Kurt Meister

Copyright (c) 2017, Kurt Meister
All rights reserved.

Redistribution and use in source and binary forms, with or without
        modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
        SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
        CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.

-------------------------------------------------------------------------------

Description:
Contains regression tests for the SDFModel class used in regression testing the ObstacleDetector
*/

#include <gtest/gtest.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <sstream>
#include "SDFModel.h"

const std::string BOX_MODEL =
        "      <pose>5.0 6.0 7.0 0 0 0</pose>\n"
        "      <link name='link'>\n"
        "        <inertial>\n"
        "          <mass>1</mass>\n"
        "          <inertia>\n"
        "            <ixx>1</ixx>\n"
        "            <ixy>0</ixy>\n"
        "            <ixz>0</ixz>\n"
        "            <iyy>1</iyy>\n"
        "            <iyz>0</iyz>\n"
        "            <izz>1</izz>\n"
        "          </inertia>\n"
        "        </inertial>\n"
        "        <collision name='collision'>\n"
        "          <geometry>\n"
        "            <box>\n"
        "              <size>1.0 2.0 3.0</size>\n"
        "            </box>\n"
        "          </geometry>\n"
        "          <max_contacts>10</max_contacts>\n"
        "          <surface>\n"
        "            <bounce/>\n"
        "            <friction>\n"
        "              <ode/>\n"
        "            </friction>\n"
        "            <contact>\n"
        "              <ode/>\n"
        "            </contact>\n"
        "          </surface>\n"
        "        </collision>\n"
        "        <visual name='visual'>\n"
        "          <geometry>\n"
        "            <box>\n"
        "              <size>1 1 1</size>\n"
        "            </box>\n"
        "          </geometry>\n"
        "          <material>\n"
        "            <script>\n"
        "              <uri>file://media/materials/scripts/gazebo.material</uri>\n"
        "              <name>Gazebo/Grey</name>\n"
        "            </script>\n"
        "          </material>\n"
        "        </visual>\n"
        "        <velocity_decay>\n"
        "          <linear>0</linear>\n"
        "          <angular>0</angular>\n"
        "        </velocity_decay>\n"
        "        <self_collide>0</self_collide>\n"
        "        <kinematic>0</kinematic>\n"
        "        <gravity>1</gravity>\n"
        "      </link>\n"
        "      <static>0</static>\n";

TEST(SDFModelTests, test_SDFModel_Constructor){
    using boost::property_tree::ptree;
    using namespace meister;
    std::stringstream ss;
    ss << BOX_MODEL;
    ptree model_tree;

    read_xml(ss, model_tree, boost::property_tree::xml_parser::trim_whitespace);

    std::shared_ptr<SDFModel> model;

    ASSERT_NO_THROW(model.reset(new SDFModel(model_tree)));
    EXPECT_EQ(5.0 , model->center_x);
    EXPECT_EQ(6.0, model->center_y);
    EXPECT_EQ(7.0, model->center_z);
    EXPECT_EQ(1.0, model->length);
    EXPECT_EQ(2.0, model->width);
    EXPECT_EQ(3.0, model->height);

}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);

    return RUN_ALL_TESTS();
}


text:
/*!
	\file RemoteDeviceMessage.h
	\author Andrew Kerr <arkerr@gatech.edu>, Gregory Diamos <gregory.diamos@gatech.edu>
	\date 26 Jan 2011
	\brief serialized message object for executive::Device interface
*/

// C++ includes
#include <iostream>

// Ocelot includes
#include <ocelot/util/interface/RemoteDeviceMessage.h>

// Boost includes
#include <boost/array.hpp>

// Hydrazine includes
#include <hydrazine/interface/Exception.h>
#include <hydrazine/interface/string.h>
#include <hydrazine/interface/debug.h>

#ifdef REPORT_BASE
#undef REPORT_BASE
#endif

// whether debugging messages are printed
#define REPORT_BASE 0

/////////////////////////////////////////////////////////////////////////////////////////////////

std::string remote::RemoteDeviceMessage::toString(const Operation &op) {
	switch (op) {
		case Memory_allocate: return "Memory_allocate";
		case Memory_copyHostToDevice: return "Memory_copyHostToDevice";
		case Memory_copyDeviceToHost: return "Memory_copyDeviceToHost";
		case Memory_copyDeviceToDevice: return "Memory_copyDeviceToDevice";
		case Memory_memset: return "Memory_memset";
		case Memory_free: return "Memory_free";
		case Device_clearMemory: return "Device_clearMemory";
		case Device_load: return "Device_load";
		case Device_unload: return "Device_unload";
		case Device_createEvent: return "Device_createEvent";
		case Device_destroyEvent: return "Device_destroyEvent";
		case Device_queryEvent: return "Device_queryEvent";
		case Device_recordEvent: return "Device_recordEvent";
		case Device_synchronizeEvent: return "Device_synchronizeEvent";
		case Device_getEventTime: return "Device_getEventTime";
		case Device_createStream: return "Device_createStream";
		case Device_destroyStream: return "Device_destroyStream";
		case Device_queryStream: return "Device_queryStream";
		case Device_synchronizeStream: return "Device_synchronizeStream";
		case Device_setStream: return "Device_setStream";
		case Device_bindTexture: return "Device_bindTexture";
		case Device_unbindTexture: return "Device_unbindTexture";
		case Device_driverVersion: return "Device_driverVersion";
		case Device_runtimeVersion: return "Device_runtimeVersion";
		case Device_launch: return "Device_launch";
		case Device_getAttributes: return "Device_getAttributes";
		case Device_getLastError: return "Device_getLastError";
		case Device_synchronize: return "Device_synchronize";
		case Device_limitWorkerThreads: return "Device_limitWorkerThreads";
		case Device_setOptimizationLevel: return "Device_setOptimizationLevel";
		case Server_enumerateDevices: return "Server_enumerateDevices";

		case Client_acknowledge: return "Client_acknowledge";
		case Client_ping: return "Client_ping";
		case Operation_invalid: return "Operation_invalid";
	}
	return "Operation unknown";
}

/////////////////////////////////////////////////////////////////////////////////////////////////

std::ostream &operator<<(std::ostream &out, remote::RemoteDeviceMessage::Header header) {
	out << "{ operation: " << remote::RemoteDeviceMessage::toString(header.operation)
		<< ", deviceId: " << header.deviceId << ", messageSize: " << header.messageSize << " }";
	return out;
}

remote::RemoteDeviceMessage::RemoteDeviceMessage() {
	header.operation = Operation_invalid;
	header.deviceId = -1;
	header.messageSize = 0;
}

remote::RemoteDeviceMessage::~RemoteDeviceMessage() {

}

/*! \brief sends this message on the indicated socket
	\return false if connection disconnected */
bool remote::RemoteDeviceMessage::send(boost::asio::ip::tcp::socket &socket) {
	report("OcelotServerConnection::send() - " << header);
	try {
		size_t len = boost::asio::write( socket, boost::asio::buffer((char *)&header, sizeof(header)));

		assert(len == sizeof(header));

		len = boost::asio::write( socket, boost::asio::buffer(message));

	}
	catch (std::exception &exp) {
		std::cerr << "RemoteDeviceMessage::send() - " << exp.what() << std::endl;
	}
	return true;
}

/*! \brief receives this message on indicated socket
	\return false if connection disconnected */
bool remote::RemoteDeviceMessage::receive(boost::asio::ip::tcp::socket &socket) {
	report("OcelotServerConnection::receive()");

	try {
		boost::array<char, sizeof(RemoteDeviceMessage::Header)> headerBuffer;
		boost::system::error_code error;

		size_t len = boost::asio::read(socket, boost::asio::buffer(headerBuffer));

		std::memcpy(&header, headerBuffer.data(), len);

		report("  header: " << header);

		resize();
		len = boost::asio::read(socket, boost::asio::buffer(message));

	}
	catch (std::exception &exp) {
		std::cerr << "RemoteDeviceMessage::receive() - " << exp.what() << std::endl;
		return false;
	}

	report("Received message: " << RemoteDeviceMessage::toString(header.operation)
		<< " [device " << header.deviceId << "]");

	return true;
}

/////////////////////////////////////////////////////////////////////////////////////////////////

max_stars_repo_name: florianjacob/gpuocelot

max_issues_repo_name: mprevot/gpuocelot

max_forks_repo_name: mprevot/gpuocelot


text:
// vi: set et ts=4 sw=2 sts=2:

#ifndef HMAT_HMATRIX_LOW_RANK_DATA_HPP
#define HMAT_HMATRIX_LOW_RANK_DATA_HPP

#include "common.hpp"
#include "hmatrix_data.hpp"
#include <armadillo>

namespace hmat {

template <typename ValueType>
class HMatrixLowRankData : public HMatrixData<ValueType> {

public:
  void apply(const arma::Mat<ValueType> &X, arma::Mat<ValueType> &Y,
             TransposeMode trans, ValueType alpha, ValueType beta) const
      override;

  void apply(const arma::subview<ValueType> &X, arma::subview<ValueType> &Y,
             TransposeMode trans, ValueType alpha, ValueType beta) const
      override;

  const arma::Mat<ValueType> &A() const;
  arma::Mat<ValueType> &A();

  const arma::Mat<ValueType> &B() const;
  arma::Mat<ValueType> &B();

  int rows() const override;
  int cols() const override;
  int rank() const override;

  typename ScalarTraits<ValueType>::RealType frobeniusNorm() const override;

  double memSizeKb() const override;

private:
  arma::Mat<ValueType> m_A;
  arma::Mat<ValueType> m_B;
};
}

#include "hmatrix_low_rank_data_impl.hpp"

#endif

max_stars_repo_name: mdavezac/bempp

max_issues_repo_name: mdavezac/bempp

max_forks_repo_name: mdavezac/bempp