<commit_before>#include "I2PEndian.h"
#include <string.h>
#include "Log.h"
#include "NetDb.h"
#include "I2NPProtocol.h"
#include "Transports.h"
#include "RouterContext.h"
#include "TunnelEndpoint.h"

namespace i2p
{
namespace tunnel
{
	TunnelEndpoint::~TunnelEndpoint ()
	{
		for (auto it: m_IncompleteMessages)
			i2p::DeleteI2NPMessage (it.second.data);
		for (auto it: m_OutOfSequenceFragments)
			i2p::DeleteI2NPMessage (it.second.data);
	}

	void TunnelEndpoint::HandleDecryptedTunnelDataMsg (I2NPMessage * msg)
	{
		m_NumReceivedBytes += TUNNEL_DATA_MSG_SIZE;

		uint8_t * decrypted = msg->GetPayload () + 20; // 4 + 16
		uint8_t * zero = (uint8_t *)memchr (decrypted + 4, 0, TUNNEL_DATA_ENCRYPTED_SIZE - 4); // witout 4-byte checksum
		if (zero)
		{
			uint8_t * fragment = zero + 1;
			// verify checksum
			memcpy (msg->GetPayload () + TUNNEL_DATA_MSG_SIZE, msg->GetPayload () + 4, 16); // copy iv to the end
			uint8_t hash[32];
			CryptoPP::SHA256().CalculateDigest (hash, fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16); // payload + iv
			if (memcmp (hash, decrypted, 4))
			{
				LogPrint (eLogError, "TunnelMessage: checksum verification failed");
				i2p::DeleteI2NPMessage (msg);
				return;
			}
			// process fragments
			while (fragment < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
			{
				uint8_t flag = fragment[0];
				fragment++;

				bool isFollowOnFragment = flag & 0x80, isLastFragment = true;
				uint32_t msgID = 0;
				int fragmentNum = 0;
				TunnelMessageBlockEx m;
				if (!isFollowOnFragment)
				{
					// first fragment

					m.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03);
					switch (m.deliveryType)
					{
						case eDeliveryTypeLocal: // 0
						break;
					  	case eDeliveryTypeTunnel: // 1
							m.tunnelID = bufbe32toh (fragment);
							fragment += 4; // tunnelID
							m.hash = i2p::data::IdentHash (fragment);
							fragment += 32; // hash
						break;
						case eDeliveryTypeRouter: // 2
							m.hash = i2p::data::IdentHash (fragment);
							fragment += 32; // to hash
						break;
						default:
							;
					}

					bool isFragmented = flag & 0x08;
					if (isFragmented)
					{
						// Message ID
						msgID = bufbe32toh (fragment);
						fragment += 4;
						isLastFragment = false;
					}
				}
				else
				{
					// follow on
					msgID = bufbe32toh (fragment); // MessageID
					fragment += 4;
					fragmentNum = (flag >> 1) & 0x3F; // 6 bits
					isLastFragment = flag & 0x01;
				}

				uint16_t size = bufbe16toh (fragment);
				fragment += 2;

				msg->offset = fragment - msg->buf;
				msg->len = msg->offset + size;
				if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
				{
					// this is not last message. we have to copy it
					m.data = NewI2NPMessage ();
					m.data->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header
					m.data->len += TUNNEL_GATEWAY_HEADER_SIZE;
					*(m.data) = *msg;
				}
				else
					m.data = msg;

				if (!isFollowOnFragment && isLastFragment)
					HandleNextMessage (m);
				else
				{
					if (msgID) // msgID is presented, assume message is fragmented
					{
						if (!isFollowOnFragment) // create new incomlete message
						{
							m.nextFragmentNum = 1;
							auto& msg = m_IncompleteMessages[msgID];
							msg = m;
							HandleOutOfSequenceFragment (msgID, msg);
						}
						else
						{
							m.nextFragmentNum = fragmentNum;
							HandleFollowOnFragment (msgID, isLastFragment, m);
						}
					}
					else
					{
						LogPrint (eLogError, "Message is fragmented, but msgID is not presented");
						DeleteI2NPMessage (m.data);
					}
				}

				fragment += size;
			}
		}
		else
		{
			LogPrint (eLogError, "TunnelMessage: zero not found");
			i2p::DeleteI2NPMessage (msg);
		}
	}

	void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m)
	{
		auto fragment = m.data->GetBuffer ();
		auto size = m.data->GetLength ();
		auto it = m_IncompleteMessages.find (msgID);
		if (it != m_IncompleteMessages.end())
		{
			auto& msg = it->second;
			if (m.nextFragmentNum == msg.nextFragmentNum)
			{
				if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if messega is not too long
				{
					memcpy (msg.data->buf + msg.data->len, fragment, size); // concatenate fragment
					msg.data->len += size;
					if (isLastFragment)
					{
						// message complete
						HandleNextMessage (msg);
						m_IncompleteMessages.erase (it);
					}
					else
					{
						msg.nextFragmentNum++;
						HandleOutOfSequenceFragment (msgID, msg);
					}
				}
				else
				{
					LogPrint (eLogError, "Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size. Message dropped");
					i2p::DeleteI2NPMessage (msg.data);
					m_IncompleteMessages.erase (it);
				}
				i2p::DeleteI2NPMessage (m.data);
			}
			else
			{
				LogPrint (eLogInfo, "Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ". Saved");
				AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
			}
		}
		else
		{
			LogPrint (eLogInfo, "First fragment of message ", msgID, " not found. Saved");
			AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
		}
	}

	void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, I2NPMessage * data)
	{
		auto it = m_OutOfSequenceFragments.find (msgID);
		if (it == m_OutOfSequenceFragments.end ())
			m_OutOfSequenceFragments.insert (std::pair<uint32_t, Fragment> (msgID, {fragmentNum, isLastFragment, data}));
		else
			i2p::DeleteI2NPMessage (data);
	}

	void TunnelEndpoint::HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg)
	{
		auto it = m_OutOfSequenceFragments.find (msgID);
		if (it != m_OutOfSequenceFragments.end ())
		{
			if (it->second.fragmentNum == msg.nextFragmentNum)
			{
				LogPrint (eLogInfo, "Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
				auto size = it->second.data->GetLength ();
				memcpy (msg.data->buf + msg.data->len, it->second.data->GetBuffer (), size); // concatenate out-of-sync fragment
				msg.data->len += size;
				if (it->second.isLastFragment)
				{
					// message complete
					HandleNextMessage (msg);
					m_IncompleteMessages.erase (msgID);
				}
				else
					msg.nextFragmentNum++;
				i2p::DeleteI2NPMessage (it->second.data);
				m_OutOfSequenceFragments.erase (it);
			}
		}
	}

	void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg)
	{
		LogPrint (eLogInfo, "TunnelMessage: handle fragment of ", msg.data->GetLength ()," bytes. Msg type ", (int)msg.data->GetTypeID ());
		switch (msg.deliveryType)
		{
			case eDeliveryTypeLocal:
				i2p::HandleI2NPMessage (msg.data);
			break;
			case eDeliveryTypeTunnel:
				i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
			break;
			case eDeliveryTypeRouter:
				if (msg.hash == i2p::context.GetRouterInfo ().GetIdentHash ()) // check if message is sent to us
					i2p::HandleI2NPMessage (msg.data);
				else
				{
					// to somebody else
					if (!m_IsInbound) // outbound transit tunnel
					{
						auto typeID = msg.data->GetTypeID ();
						if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply )
						{
							// catch RI or reply with new list of routers
							auto ds = NewI2NPMessage ();
							*ds = *(msg.data);
							i2p::data::netdb.PostI2NPMsg (ds);
						}
						i2p::transport::transports.SendMessage (msg.hash, msg.data);
					}
					else // we shouldn't send this message. possible leakage
					{
						LogPrint (eLogError, "Message to another router arrived from an inbound tunnel. Dropped");
						i2p::DeleteI2NPMessage (msg.data);
					}
				}
			break;
			default:
			{
				LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType);
				i2p::DeleteI2NPMessage (msg.data);
			}
		};
	}
}
}
<commit_msg>check for duplicate msgID<commit_after>#include "I2PEndian.h"
#include <string.h>
#include "Log.h"
#include "NetDb.h"
#include "I2NPProtocol.h"
#include "Transports.h"
#include "RouterContext.h"
#include "TunnelEndpoint.h"

namespace i2p
{
namespace tunnel
{
	TunnelEndpoint::~TunnelEndpoint ()
	{
		for (auto it: m_IncompleteMessages)
			i2p::DeleteI2NPMessage (it.second.data);
		for (auto it: m_OutOfSequenceFragments)
			i2p::DeleteI2NPMessage (it.second.data);
	}

	void TunnelEndpoint::HandleDecryptedTunnelDataMsg (I2NPMessage * msg)
	{
		m_NumReceivedBytes += TUNNEL_DATA_MSG_SIZE;

		uint8_t * decrypted = msg->GetPayload () + 20; // 4 + 16
		uint8_t * zero = (uint8_t *)memchr (decrypted + 4, 0, TUNNEL_DATA_ENCRYPTED_SIZE - 4); // witout 4-byte checksum
		if (zero)
		{
			uint8_t * fragment = zero + 1;
			// verify checksum
			memcpy (msg->GetPayload () + TUNNEL_DATA_MSG_SIZE, msg->GetPayload () + 4, 16); // copy iv to the end
			uint8_t hash[32];
			CryptoPP::SHA256().CalculateDigest (hash, fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16); // payload + iv
			if (memcmp (hash, decrypted, 4))
			{
				LogPrint (eLogError, "TunnelMessage: checksum verification failed");
				i2p::DeleteI2NPMessage (msg);
				return;
			}
			// process fragments
			while (fragment < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
			{
				uint8_t flag = fragment[0];
				fragment++;

				bool isFollowOnFragment = flag & 0x80, isLastFragment = true;
				uint32_t msgID = 0;
				int fragmentNum = 0;
				TunnelMessageBlockEx m;
				if (!isFollowOnFragment)
				{
					// first fragment

					m.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03);
					switch (m.deliveryType)
					{
						case eDeliveryTypeLocal: // 0
						break;
					  	case eDeliveryTypeTunnel: // 1
							m.tunnelID = bufbe32toh (fragment);
							fragment += 4; // tunnelID
							m.hash = i2p::data::IdentHash (fragment);
							fragment += 32; // hash
						break;
						case eDeliveryTypeRouter: // 2
							m.hash = i2p::data::IdentHash (fragment);
							fragment += 32; // to hash
						break;
						default:
							;
					}

					bool isFragmented = flag & 0x08;
					if (isFragmented)
					{
						// Message ID
						msgID = bufbe32toh (fragment);
						fragment += 4;
						isLastFragment = false;
					}
				}
				else
				{
					// follow on
					msgID = bufbe32toh (fragment); // MessageID
					fragment += 4;
					fragmentNum = (flag >> 1) & 0x3F; // 6 bits
					isLastFragment = flag & 0x01;
				}

				uint16_t size = bufbe16toh (fragment);
				fragment += 2;

				msg->offset = fragment - msg->buf;
				msg->len = msg->offset + size;
				if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
				{
					// this is not last message. we have to copy it
					m.data = NewI2NPMessage ();
					m.data->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header
					m.data->len += TUNNEL_GATEWAY_HEADER_SIZE;
					*(m.data) = *msg;
				}
				else
					m.data = msg;

				if (!isFollowOnFragment && isLastFragment)
					HandleNextMessage (m);
				else
				{
					if (msgID) // msgID is presented, assume message is fragmented
					{
						if (!isFollowOnFragment) // create new incomlete message
						{
							m.nextFragmentNum = 1;
							auto ret = m_IncompleteMessages.insert (std::pair<uint32_t, TunnelMessageBlockEx>(msgID, m));
							if (ret.second)
								HandleOutOfSequenceFragment (msgID, ret.first->second);
							else
							{
								LogPrint (eLogError, "Incomplete message ", msgID, "already exists");
								DeleteI2NPMessage (m.data);
							}
						}
						else
						{
							m.nextFragmentNum = fragmentNum;
							HandleFollowOnFragment (msgID, isLastFragment, m);
						}
					}
					else
					{
						LogPrint (eLogError, "Message is fragmented, but msgID is not presented");
						DeleteI2NPMessage (m.data);
					}
				}

				fragment += size;
			}
		}
		else
		{
			LogPrint (eLogError, "TunnelMessage: zero not found");
			i2p::DeleteI2NPMessage (msg);
		}
	}

	void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m)
	{
		auto fragment = m.data->GetBuffer ();
		auto size = m.data->GetLength ();
		auto it = m_IncompleteMessages.find (msgID);
		if (it != m_IncompleteMessages.end())
		{
			auto& msg = it->second;
			if (m.nextFragmentNum == msg.nextFragmentNum)
			{
				if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if messega is not too long
				{
					memcpy (msg.data->buf + msg.data->len, fragment, size); // concatenate fragment
					msg.data->len += size;
					if (isLastFragment)
					{
						// message complete
						HandleNextMessage (msg);
						m_IncompleteMessages.erase (it);
					}
					else
					{
						msg.nextFragmentNum++;
						HandleOutOfSequenceFragment (msgID, msg);
					}
				}
				else
				{
					LogPrint (eLogError, "Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size. Message dropped");
					i2p::DeleteI2NPMessage (msg.data);
					m_IncompleteMessages.erase (it);
				}
				i2p::DeleteI2NPMessage (m.data);
			}
			else
			{
				LogPrint (eLogInfo, "Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ". Saved");
				AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
			}
		}
		else
		{
			LogPrint (eLogInfo, "First fragment of message ", msgID, " not found. Saved");
			AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
		}
	}

	void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, I2NPMessage * data)
	{
		auto it = m_OutOfSequenceFragments.find (msgID);
		if (it == m_OutOfSequenceFragments.end ())
			m_OutOfSequenceFragments.insert (std::pair<uint32_t, Fragment> (msgID, {fragmentNum, isLastFragment, data}));
		else
			i2p::DeleteI2NPMessage (data);
	}

	void TunnelEndpoint::HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg)
	{
		auto it = m_OutOfSequenceFragments.find (msgID);
		if (it != m_OutOfSequenceFragments.end ())
		{
			if (it->second.fragmentNum == msg.nextFragmentNum)
			{
				LogPrint (eLogInfo, "Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
				auto size = it->second.data->GetLength ();
				memcpy (msg.data->buf + msg.data->len, it->second.data->GetBuffer (), size); // concatenate out-of-sync fragment
				msg.data->len += size;
				if (it->second.isLastFragment)
				{
					// message complete
					HandleNextMessage (msg);
					m_IncompleteMessages.erase (msgID);
				}
				else
					msg.nextFragmentNum++;
				i2p::DeleteI2NPMessage (it->second.data);
				m_OutOfSequenceFragments.erase (it);
			}
		}
	}

	void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg)
	{
		LogPrint (eLogInfo, "TunnelMessage: handle fragment of ", msg.data->GetLength ()," bytes. Msg type ", (int)msg.data->GetTypeID ());
		switch (msg.deliveryType)
		{
			case eDeliveryTypeLocal:
				i2p::HandleI2NPMessage (msg.data);
			break;
			case eDeliveryTypeTunnel:
				i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
			break;
			case eDeliveryTypeRouter:
				if (msg.hash == i2p::context.GetRouterInfo ().GetIdentHash ()) // check if message is sent to us
					i2p::HandleI2NPMessage (msg.data);
				else
				{
					// to somebody else
					if (!m_IsInbound) // outbound transit tunnel
					{
						auto typeID = msg.data->GetTypeID ();
						if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply )
						{
							// catch RI or reply with new list of routers
							auto ds = NewI2NPMessage ();
							*ds = *(msg.data);
							i2p::data::netdb.PostI2NPMsg (ds);
						}
						i2p::transport::transports.SendMessage (msg.hash, msg.data);
					}
					else // we shouldn't send this message. possible leakage
					{
						LogPrint (eLogError, "Message to another router arrived from an inbound tunnel. Dropped");
						i2p::DeleteI2NPMessage (msg.data);
					}
				}
			break;
			default:
			{
				LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType);
				i2p::DeleteI2NPMessage (msg.data);
			}
		};
	}
}
}
<|endoftext|>


<commit_before>#ifndef H_EXTENSIONS_CUTEHMI_TEST_0_INCLUDE_CUTEHMI_TEST_RANDOM_HPP
#define H_EXTENSIONS_CUTEHMI_TEST_0_INCLUDE_CUTEHMI_TEST_RANDOM_HPP

#include "IsIntType.hpp"

#include <QChar>
#include <QString>
#include <QList>
#include <QMultiHash>

#include <random>
#include <chrono>
#include <limits>
#include <type_traits>

namespace cutehmi {
namespace test {

/**
 * Seeded engine. This class extends random generator class specified by @a E
 * template parameter and behaves exactly like extended class, except it seeds
 * itself within a constructor.
 * @tparam E random number generator engine.
 */
template <class E = std::mt19937>
class SeededEngine:
	public E
{
	public:
		/**
		 * Default constructor.
		 */
		SeededEngine();
};

template <class E>
SeededEngine<E>::SeededEngine()
{
	std::random_device randomDevice;

	// Some implementations do not have non-deterministic std::random_device.
	// Check out entropy of std::random_device. If it's zero, then fallback to
	// time-based seed.
	//
	// Note that sometimes it's std::random_device::entropy(), which is not
	// implemented properly, accodring to _cppreference.com_. Still use
	// time-based fallback in such cases, as we won't do workarounds.
	//
	// "This function is not fully implemented in some standard libraries. For
	//  example, LLVM libc++ always returns zero even though the device is
	//  non-deterministic. In comparison, Microsoft Visual C++ implementation
	//  always returns 32, and boost.random returns 10.
	//  The entropy of the Linux kernel device /dev/urandom may be obtained
	//  using ioctl RNDGETENTCNT - that's what std::random_device::entropy() in
	//  GNU libstdc++ uses as of version 8.1"
	//  -- https://en.cppreference.com/w/cpp/numeric/random/random_device/entropy
	if (randomDevice.entropy() != 0.0)
		E::seed(randomDevice());
	else
		E::seed(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
}

/**
 * Generate random integer using uniform distribution.
 * @tparam T integer type.
 * @tparam E random number generator engine.
 * @param from lower bound of a set of generated random numbers.
 * @param from upper bound of a set of generated random numbers.
 * @return randomly generated integer.
 */
template <typename T, typename E = SeededEngine<std::mt19937>>
typename std::enable_if<IsIntType<T>::value, T>::type rand(T from = std::numeric_limits<T>::lowest(), T to = std::numeric_limits<T>::max())
{
	static E engine;    // Use static variable to prevent frequent allocation/deallocation ("mt19937 use 5000 bytes of memory for each creation (which is bad for performance if we create it too frequently)" -- https://github.com/effolkronium/random).

	std::uniform_int_distribution<T> distribution(from, to);

	return distribution(engine);
}

/**
 * Generate random floating point number using uniform distribution. Fractions are generated within range [@p 0.0, @p 1.0) and then
 * multiplied by @p 2 raised to random exponent [@a fromExponent, @a toExponent].
 * @tparam T floating point number type.
 * @tparam E random number generator engine.
 * @param fromExponent lower bound of a set of powers of base 2 exponents to be used to multiply a randomly generated fraction from
 * [@p 0.0, @p 1.0) range.
 * @param toExponent upper bound of a set of powers of base 2 exponents to be used to multiply a randomly generated fraction from
 * [@p 0.0, @p 1.0) range.
 * @return randomly generated floating point number.
 */
template <typename T, typename E = SeededEngine<std::mt19937>>
typename std::enable_if<std::is_floating_point<T>::value, T>::type rand(int fromExponent = std::numeric_limits<T>::min_exponent, int toExponent = std::numeric_limits<T>::max_exponent)
{
	static E engine;    // Use static variable to prevent frequent allocation/deallocation ("mt19937 use 5000 bytes of memory for each creation (which is bad for performance if we create it too frequently)" -- https://github.com/effolkronium/random).

	std::uniform_real_distribution<T> fracDistribution(0.0, 1.0);	// A fraction within range [0.0, 1.0).
	std::uniform_int_distribution<int> expDistribution(fromExponent, toExponent);

	return ldexp(fracDistribution(engine), expDistribution(engine));
}

/**
 * Generate random Boolean value using Bernoulli distribution.
 * @tparam T boolean type.
 * @tparam E random number generator engine.
 * @param p propbablity of generating @p true.
 * @return one of the Boolean values: @p true or @p false.
 */
template <typename T, typename E = SeededEngine<std::mt19937>>
typename std::enable_if<std::is_same<T, bool>::value, T>::type rand(double p = 0.5)
{
	static E engine;    // Use static variable to prevent frequent allocation/deallocation ("mt19937 use 5000 bytes of memory for each creation (which is bad for performance if we create it too frequently)" -- https://github.com/effolkronium/random).

	std::bernoulli_distribution distribution(p);

	return distribution(engine);
}

/**
 * Generate random character.
 * @tparam T QChar type.
 * @tparam E random number generator engine.
 * @param category character category.
 * @return random character.
 */
template <typename T, typename E = SeededEngine<std::mt19937>>
typename std::enable_if<std::is_same<T, QChar>::value, QChar>::type rand(QChar::Category category)
{
	static E engine;    // Use static variable to prevent frequent allocation/deallocation ("mt19937 use 5000 bytes of memory for each creation (which is bad for performance if we create it too frequently)" -- https://github.com/effolkronium/random).
	static QMultiHash<int, QChar> categorySets;		// Key denotes category of specific charecters.

	if (categorySets.empty()) {
		// Initialize categorized sets of characters.
		for (char16_t c = 0; c < std::numeric_limits<char16_t>::max(); c++)
			categorySets.insert(QChar::category(c), c);
		categorySets.insert(QChar::category(std::numeric_limits<char16_t>::max()), std::numeric_limits<char16_t>::max());
	}

	return categorySets.values(category).at(rand(0, categorySets.values(category).size() - 1));
}

/**
 * Generate random string.
 * @tparam T QString type.
 * @tparam E random number generator engine.
 * @param length string length.
 * @param categories character categories of which string should be composed from.
 * @return randomly generated string.
 */
template <typename T, typename E = SeededEngine<std::mt19937>>
typename std::enable_if<std::is_same<T, QString>::value, T>::type rand(int length = rand(0, 255), QList<QChar::Category> categories = {QChar::Letter_Uppercase, QChar::Letter_Lowercase, QChar::Number_DecimalDigit})
{
	static E engine;    // Use static variable to prevent frequent allocation/deallocation ("mt19937 use 5000 bytes of memory for each creation (which is bad for performance if we create it too frequently)" -- https://github.com/effolkronium/random).

	QString result("");
	for (int i = 0; i < length; i++)
		result.append(rand<QChar>(categories.at(rand(0, categories.size() - 1))));

	return result;
}

/**
 * Generate random string list.
 * @tparam T QStringList type.
 * @tparam E random number generator engine.
 * @param size string list size.
 * @param length strings length.
 * @param categories character categories of which strings should be composed from.
 * @return randomly generated string list.
 */
template <typename T, typename E = SeededEngine<std::mt19937>>
typename std::enable_if<std::is_same<T, QStringList>::value, T>::type rand(int size = rand(0, 255), int length = rand(0, 255), QList<QChar::Category> categories = {QChar::Letter_Uppercase, QChar::Letter_Lowercase, QChar::Number_DecimalDigit})
{
	static E engine;    // Use static variable to prevent frequent allocation/deallocation ("mt19937 use 5000 bytes of memory for each creation (which is bad for performance if we create it too frequently)" -- https://github.com/effolkronium/random).

	QStringList result;
	for (int i = 0; i < size; i++)
		result.append(rand<QString>(length, categories));

	return result;
}

/**
 * Randomize array.
 * @tparam T type of array elements.
 * @tparam E random number genererator engine.
 * @tparam ARGS arguments to be passed to underlying rand() function.
 * @param ptr array pointer.
 * @param size array size.
 * @param arguments to be passed to underlying rand() function for each array
 * element.
 */
template <typename T, typename E = SeededEngine<std::mt19937>, typename... ARGS>
void rand(T * ptr, std::size_t size, ARGS... args)
{
	while (size > 0) {
		*ptr = rand<T, E>(args...);
		ptr++;
		size--;
	}
}

}
}

#endif

//(c)MP: Copyright © 2019, Michal Policht <michpolicht@gmail.com>. All rights reserved.
//(c)MP: This file is a part of CuteHMI.
//(c)MP: CuteHMI is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
//(c)MP: CuteHMI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
//(c)MP: You should have received a copy of the GNU Lesser General Public License along with CuteHMI.  If not, see <https://www.gnu.org/licenses/>.
<commit_msg>Add random sign, when generating random floating point number.<commit_after>#ifndef H_EXTENSIONS_CUTEHMI_TEST_0_INCLUDE_CUTEHMI_TEST_RANDOM_HPP
#define H_EXTENSIONS_CUTEHMI_TEST_0_INCLUDE_CUTEHMI_TEST_RANDOM_HPP

#include "IsIntType.hpp"

#include <QChar>
#include <QString>
#include <QList>
#include <QMultiHash>

#include <random>
#include <chrono>
#include <limits>
#include <type_traits>

namespace cutehmi {
namespace test {

/**
 * Seeded engine. This class extends random generator class specified by @a E
 * template parameter and behaves exactly like extended class, except it seeds
 * itself within a constructor.
 * @tparam E random number generator engine.
 */
template <class E = std::mt19937>
class SeededEngine:
	public E
{
	public:
		/**
		 * Default constructor.
		 */
		SeededEngine();
};

template <class E>
SeededEngine<E>::SeededEngine()
{
	std::random_device randomDevice;

	// Some implementations do not have non-deterministic std::random_device.
	// Check out entropy of std::random_device. If it's zero, then fallback to
	// time-based seed.
	//
	// Note that sometimes it's std::random_device::entropy(), which is not
	// implemented properly, accodring to _cppreference.com_. Still use
	// time-based fallback in such cases, as we won't do workarounds.
	//
	// "This function is not fully implemented in some standard libraries. For
	//  example, LLVM libc++ always returns zero even though the device is
	//  non-deterministic. In comparison, Microsoft Visual C++ implementation
	//  always returns 32, and boost.random returns 10.
	//  The entropy of the Linux kernel device /dev/urandom may be obtained
	//  using ioctl RNDGETENTCNT - that's what std::random_device::entropy() in
	//  GNU libstdc++ uses as of version 8.1"
	//  -- https://en.cppreference.com/w/cpp/numeric/random/random_device/entropy
	if (randomDevice.entropy() != 0.0)
		E::seed(randomDevice());
	else
		E::seed(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
}

/**
 * Generate random integer using uniform distribution.
 * @tparam T integer type.
 * @tparam E random number generator engine.
 * @param from lower bound of a set of generated random numbers.
 * @param from upper bound of a set of generated random numbers.
 * @return randomly generated integer.
 */
template <typename T, typename E = SeededEngine<std::mt19937>>
typename std::enable_if<IsIntType<T>::value, T>::type rand(T from = std::numeric_limits<T>::lowest(), T to = std::numeric_limits<T>::max())
{
	static E engine;    // Use static variable to prevent frequent allocation/deallocation ("mt19937 use 5000 bytes of memory for each creation (which is bad for performance if we create it too frequently)" -- https://github.com/effolkronium/random).

	std::uniform_int_distribution<T> distribution(from, to);

	return distribution(engine);
}

/**
 * Generate random floating point number using uniform distribution. Fractions are generated within range [@p 0.0, @p 1.0) and then
 * multiplied by @p 2 raised to random exponent [@a fromExponent, @a toExponent]. Sign is randomly applied to the resulting value.
 * @tparam T floating point number type.
 * @tparam E random number generator engine.
 * @param fromExponent lower bound of a set of powers of base 2 exponents to be used to multiply a randomly generated fraction from
 * [@p 0.0, @p 1.0) range.
 * @param toExponent upper bound of a set of powers of base 2 exponents to be used to multiply a randomly generated fraction from
 * [@p 0.0, @p 1.0) range.
 * @return randomly generated floating point number.
 */
template <typename T, typename E = SeededEngine<std::mt19937>>
typename std::enable_if<std::is_floating_point<T>::value, T>::type rand(int fromExponent = std::numeric_limits<T>::min_exponent, int toExponent = std::numeric_limits<T>::max_exponent)
{
	static E engine;    // Use static variable to prevent frequent allocation/deallocation ("mt19937 use 5000 bytes of memory for each creation (which is bad for performance if we create it too frequently)" -- https://github.com/effolkronium/random).

	std::uniform_real_distribution<T> fracDistribution(0.0, 1.0);	// A fraction within range [0.0, 1.0).
	std::uniform_int_distribution<int> expDistribution(fromExponent, toExponent);

	return rand<bool>() ? ldexp(fracDistribution(engine), expDistribution(engine)) : -ldexp(fracDistribution(engine), expDistribution(engine));
}

/**
 * Generate random Boolean value using Bernoulli distribution.
 * @tparam T boolean type.
 * @tparam E random number generator engine.
 * @param p propbablity of generating @p true.
 * @return one of the Boolean values: @p true or @p false.
 */
template <typename T, typename E = SeededEngine<std::mt19937>>
typename std::enable_if<std::is_same<T, bool>::value, T>::type rand(double p = 0.5)
{
	static E engine;    // Use static variable to prevent frequent allocation/deallocation ("mt19937 use 5000 bytes of memory for each creation (which is bad for performance if we create it too frequently)" -- https://github.com/effolkronium/random).

	std::bernoulli_distribution distribution(p);

	return distribution(engine);
}

/**
 * Generate random character.
 * @tparam T QChar type.
 * @tparam E random number generator engine.
 * @param category character category.
 * @return random character.
 */
template <typename T, typename E = SeededEngine<std::mt19937>>
typename std::enable_if<std::is_same<T, QChar>::value, QChar>::type rand(QChar::Category category)
{
	static E engine;    // Use static variable to prevent frequent allocation/deallocation ("mt19937 use 5000 bytes of memory for each creation (which is bad for performance if we create it too frequently)" -- https://github.com/effolkronium/random).
	static QMultiHash<int, QChar> categorySets;		// Key denotes category of specific charecters.

	if (categorySets.empty()) {
		// Initialize categorized sets of characters.
		for (char16_t c = 0; c < std::numeric_limits<char16_t>::max(); c++)
			categorySets.insert(QChar::category(c), c);
		categorySets.insert(QChar::category(std::numeric_limits<char16_t>::max()), std::numeric_limits<char16_t>::max());
	}

	return categorySets.values(category).at(rand(0, categorySets.values(category).size() - 1));
}

/**
 * Generate random string.
 * @tparam T QString type.
 * @tparam E random number generator engine.
 * @param length string length.
 * @param categories character categories of which string should be composed from.
 * @return randomly generated string.
 */
template <typename T, typename E = SeededEngine<std::mt19937>>
typename std::enable_if<std::is_same<T, QString>::value, T>::type rand(int length = rand(0, 255), QList<QChar::Category> categories = {QChar::Letter_Uppercase, QChar::Letter_Lowercase, QChar::Number_DecimalDigit})
{
	static E engine;    // Use static variable to prevent frequent allocation/deallocation ("mt19937 use 5000 bytes of memory for each creation (which is bad for performance if we create it too frequently)" -- https://github.com/effolkronium/random).

	QString result("");
	for (int i = 0; i < length; i++)
		result.append(rand<QChar>(categories.at(rand(0, categories.size() - 1))));

	return result;
}

/**
 * Generate random string list.
 * @tparam T QStringList type.
 * @tparam E random number generator engine.
 * @param size string list size.
 * @param length strings length.
 * @param categories character categories of which strings should be composed from.
 * @return randomly generated string list.
 */
template <typename T, typename E = SeededEngine<std::mt19937>>
typename std::enable_if<std::is_same<T, QStringList>::value, T>::type rand(int size = rand(0, 255), int length = rand(0, 255), QList<QChar::Category> categories = {QChar::Letter_Uppercase, QChar::Letter_Lowercase, QChar::Number_DecimalDigit})
{
	static E engine;    // Use static variable to prevent frequent allocation/deallocation ("mt19937 use 5000 bytes of memory for each creation (which is bad for performance if we create it too frequently)" -- https://github.com/effolkronium/random).

	QStringList result;
	for (int i = 0; i < size; i++)
		result.append(rand<QString>(length, categories));

	return result;
}

/**
 * Randomize array.
 * @tparam T type of array elements.
 * @tparam E random number genererator engine.
 * @tparam ARGS arguments to be passed to underlying rand() function.
 * @param ptr array pointer.
 * @param size array size.
 * @param arguments to be passed to underlying rand() function for each array
 * element.
 */
template <typename T, typename E = SeededEngine<std::mt19937>, typename... ARGS>
void rand(T * ptr, std::size_t size, ARGS... args)
{
	while (size > 0) {
		*ptr = rand<T, E>(args...);
		ptr++;
		size--;
	}
}

}
}

#endif

//(c)MP: Copyright © 2019, Michal Policht <michpolicht@gmail.com>. All rights reserved.
//(c)MP: This file is a part of CuteHMI.
//(c)MP: CuteHMI is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
//(c)MP: CuteHMI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
//(c)MP: You should have received a copy of the GNU Lesser General Public License along with CuteHMI.  If not, see <https://www.gnu.org/licenses/>.
<|endoftext|>


<commit_before>// Ylikuutio - A 3D game and simulation engine.
//
// Copyright (C) 2015-2021 Antti Nuortimo.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "shapeshifter_form.hpp"
#include "shapeshifter_transformation.hpp"

// Include standard headers
#include <cstddef>  // std::size_t
#include <iostream> // std::cout, std::cin, std::cerr

namespace yli::ontology
{
    class Entity;

    ShapeshifterForm::~ShapeshifterForm()
    {
        // destructor.
    }

    yli::ontology::Entity* ShapeshifterForm::get_parent() const
    {
        return this->child_of_shapeshifter_transformation.get_parent();
    }

    std::size_t ShapeshifterForm::get_number_of_children() const
    {
        return 0; // `ShapeshifterForm` has no children.
    }

    std::size_t ShapeshifterForm::get_number_of_descendants() const
    {
        return 0; // `ShapeshifterForm` has no children.
    }

    void ShapeshifterForm::render()
    {
        // Render this `ShapeshifterForm`.

        // TODO.
    }
}
<commit_msg>Remove unnecessary `#include` line.<commit_after>// Ylikuutio - A 3D game and simulation engine.
//
// Copyright (C) 2015-2021 Antti Nuortimo.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "shapeshifter_form.hpp"
#include "shapeshifter_transformation.hpp"

// Include standard headers
#include <cstddef>  // std::size_t

namespace yli::ontology
{
    class Entity;

    ShapeshifterForm::~ShapeshifterForm()
    {
        // destructor.
    }

    yli::ontology::Entity* ShapeshifterForm::get_parent() const
    {
        return this->child_of_shapeshifter_transformation.get_parent();
    }

    std::size_t ShapeshifterForm::get_number_of_children() const
    {
        return 0; // `ShapeshifterForm` has no children.
    }

    std::size_t ShapeshifterForm::get_number_of_descendants() const
    {
        return 0; // `ShapeshifterForm` has no children.
    }

    void ShapeshifterForm::render()
    {
        // Render this `ShapeshifterForm`.

        // TODO.
    }
}
<|endoftext|>


<commit_before>#include "fastlib/fastlib.h"
#include "compressed_matrix.h"

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

  CompressedVector<double, 10000> cv;
  Vector v;
  index_t length_of_vector = 3000000;
  v.Init(length_of_vector);
  for(index_t i = 0; i < length_of_vector; i++) {
    v[i] = i;
  }

  // Copy the vector to the compressed one.
  cv.Copy(v);

  // Measure the time for the compressed vector.
  fx_timer_start(fx_root, "compressed_vector_dot_product");
  double dot_product = 0;
  for(index_t i = 0; i < length_of_vector; i += 1000) {
    dot_product += math::Sqr(cv[i]);
  }
  fx_timer_stop(fx_root, "compressed_vector_dot_product");
  printf("Dot product: %g\n", dot_product);

  // Measure the time for the uncompressed vector.
  fx_timer_start(fx_root, "uncompressed_vector_dot_product");
  dot_product = 0;
  for(index_t i = 0; i < length_of_vector; i +=1000) {
    dot_product += math::Sqr(v[i]);
  }
  fx_timer_stop(fx_root, "uncompressed_vector_dot_product");
  printf("Dot product: %g\n", dot_product);

  fx_done(fx_root);
  return 0;
}
<commit_msg>Need to implement method for incrementing the vector.<commit_after>#include "fastlib/fastlib.h"
#include "compressed_matrix.h"

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

  CompressedVector<double, 100000000> cv;
  Vector v;
  index_t length_of_vector = 300000000;
  v.Init(length_of_vector);
  for(index_t i = 0; i < length_of_vector; i++) {
    v[i] = i;
  }

  // Copy the vector to the compressed one.
  cv.Copy(v);

  v.Destruct();
  // Measure the time for the uncompressed vector.
  //fx_timer_start(fx_root, "uncompressed_vector_dot_product");
  //dot_product = 0;
  //for(index_t i = 0; i < length_of_vector; i +=1000) {
  //dot_product += math::Sqr(v[i]);
  //}
  //fx_timer_stop(fx_root, "uncompressed_vector_dot_product");
  //printf("Dot product: %g\n", dot_product);

  // Measure the time for the compressed vector.
  fx_timer_start(fx_root, "compressed_vector_dot_product");
  double dot_product = 0;
  for(index_t i = 0; i < length_of_vector; i += 1000) {
    dot_product += math::Sqr(cv[i]);
  }
  fx_timer_stop(fx_root, "compressed_vector_dot_product");
  printf("Dot product: %g\n", dot_product);

  fx_done(fx_root);
  return 0;
}
<|endoftext|>


<commit_before>#include "panel_controls.h"

#include <glgui/button.h>
#include <glgui/rootpanel.h>
#include <glgui/selector.h>
#include <tinker/profiler.h>
#include <renderer/renderingcontext.h>

#include "monitor_window.h"

using namespace glgui;

CPanel_Controls::CPanel_Controls()
{
}

void CPanel_Controls::RegistrationUpdate()
{
	Layout();
}

tstring ValuePrint(float value)
{
	return pretty_float(value, 2);
}

void CPanel_Controls::Layout()
{
	m_selectors.clear();

	ClearControls();

	auto& controls = MonitorWindow()->GetViewback()->GetControls();

	float control_height = 30;

	for (size_t i = 0; i < controls.size(); i++)
	{
		auto& control = controls[i];

		switch (control.m_type)
		{
		case VB_CONTROL_BUTTON:
		{
			CControl<CButton> button = AddControl(new CButton(control.m_name));
			button->Layout_FullWidth();
			button->SetTop(GetDefaultMargin() + (float)i * (control_height + GetDefaultMargin()));
			button->SetClickedListener(this, ButtonPressed, tsprintf("%d", i));
			break;
		}

		case VB_CONTROL_SLIDER_FLOAT:
		{
			CControl<CScrollSelector<float>> slider = AddControl(new CScrollSelector<float>(control.m_name));

			if (control.slider_float.steps >= 2)
			{
				for (int n = 0; n < control.slider_float.steps; n++)
				{
					float f = RemapVal((float)n, 0.0f, (float)control.slider_float.steps - 1, control.slider_float.range_min, control.slider_float.range_max);
					slider->AddSelection(CScrollSelection<float>(f, pretty_float(f, 2)));
				}
			}
			else
			{
				float min = control.slider_float.range_min;
				float max = control.slider_float.range_max;
				slider->SetContinuousRange(CScrollSelection<float>(min, pretty_float(min, 2)), CScrollSelection<float>(max, pretty_float(max, 2)), &ValuePrint);
			}

			slider->SetValue(control.slider_float.initial_value);

			slider->Layout_FullWidth();
			slider->SetTop(GetDefaultMargin() + (float)i * (control_height + GetDefaultMargin()));
			slider->SetHeight(control_height);
			slider->SetSelectedListener(this, FloatSliderMoved, tsprintf("%d %d", i, m_selectors.size()));

			m_selectors.push_back(slider);
			break;
		}

		case VB_CONTROL_SLIDER_INT:
		{
			CControl<CScrollSelector<int>> slider = AddControl(new CScrollSelector<int>(control.m_name));

			for (int n = control.slider_int.range_min; n <= control.slider_int.range_max; n += control.slider_int.step_size)
				slider->AddSelection(CScrollSelection<int>(n, tsprintf("%d", n)));

			slider->SetValue(control.slider_int.initial_value);

			slider->Layout_FullWidth();
			slider->SetTop(GetDefaultMargin() + (float)i * (control_height + GetDefaultMargin()));
			slider->SetHeight(control_height);
			slider->SetSelectedListener(this, IntSliderMoved, tsprintf("%d %d", i, m_selectors.size()));

			m_selectors.push_back(slider);
			break;
		}

		default:
			break;
		}
	}

	BaseClass::Layout();
}

void CPanel_Controls::Think()
{
	BaseClass::Think();
}

void CPanel_Controls::Paint(float x, float y, float w, float h)
{
	TPROF("CPanel_Controls::Paint()");

	BaseClass::Paint(x, y, w, h);
}

void CPanel_Controls::ButtonPressedCallback(const tstring& sArgs)
{
	unsigned int i = stoi(sArgs);

	MonitorWindow()->GetViewback()->ControlCallback(i);
}

void CPanel_Controls::FloatSliderMovedCallback(const tstring& sArgs)
{
	tvector<tstring> tokens;
	tstrtok(sArgs, tokens);

	if (tokens.size() < 2)
		return;

	unsigned int control = stoi(tokens[0]);
	unsigned int selector = stoi(tokens[1]);

	MonitorWindow()->GetViewback()->ControlCallback(control, m_selectors[selector].DowncastStatic<CScrollSelector<float>>()->GetSelectionValue());
}

void CPanel_Controls::IntSliderMovedCallback(const tstring& sArgs)
{
	tvector<tstring> tokens;
	tstrtok(sArgs, tokens);

	if (tokens.size() < 2)
		return;

	unsigned int control = stoi(tokens[0]);
	unsigned int selector = stoi(tokens[1]);

	MonitorWindow()->GetViewback()->ControlCallback(control, m_selectors[selector].DowncastStatic<CScrollSelector<int>>()->GetSelectionValue());
}

void CPanel_Controls::ControlUpdated(size_t control_id, float f_value, int i_value)
{
	auto& controls = MonitorWindow()->GetViewback()->GetControls();

	TAssert(control_id < controls.size());

	auto the_real_control = m_selectors[control_id];

	switch (controls[control_id].m_type)
	{
	case VB_CONTROL_SLIDER_FLOAT:
	{
		CScrollSelector<float>* slider = the_real_control.DowncastStatic<CScrollSelector<float>>();
		if (!slider->IsMovingHandle())
			slider->SetValue(f_value);
		break;
	}
	case VB_CONTROL_SLIDER_INT:
	{
		CScrollSelector<int>* slider = the_real_control.DowncastStatic<CScrollSelector<int>>();
		if (!slider->IsMovingHandle())
			slider->SetValue(i_value);
		break;
	}
	}
}
<commit_msg>Add all controls to the selectors list so that its indices match the Viewback controls indices.<commit_after>#include "panel_controls.h"

#include <glgui/button.h>
#include <glgui/rootpanel.h>
#include <glgui/selector.h>
#include <tinker/profiler.h>
#include <renderer/renderingcontext.h>

#include "monitor_window.h"

using namespace glgui;

CPanel_Controls::CPanel_Controls()
{
}

void CPanel_Controls::RegistrationUpdate()
{
	Layout();
}

tstring ValuePrint(float value)
{
	return pretty_float(value, 2);
}

void CPanel_Controls::Layout()
{
	m_selectors.clear();

	ClearControls();

	auto& controls = MonitorWindow()->GetViewback()->GetControls();

	float control_height = 30;

	for (size_t i = 0; i < controls.size(); i++)
	{
		auto& control = controls[i];

		switch (control.m_type)
		{
		case VB_CONTROL_BUTTON:
		{
			CControl<CButton> button = AddControl(new CButton(control.m_name));
			button->Layout_FullWidth();
			button->SetTop(GetDefaultMargin() + (float)i * (control_height + GetDefaultMargin()));
			button->SetClickedListener(this, ButtonPressed, tsprintf("%d", i));

			m_selectors.push_back(button);
			break;
		}

		case VB_CONTROL_SLIDER_FLOAT:
		{
			CControl<CScrollSelector<float>> slider = AddControl(new CScrollSelector<float>(control.m_name));

			if (control.slider_float.steps >= 2)
			{
				for (int n = 0; n < control.slider_float.steps; n++)
				{
					float f = RemapVal((float)n, 0.0f, (float)control.slider_float.steps - 1, control.slider_float.range_min, control.slider_float.range_max);
					slider->AddSelection(CScrollSelection<float>(f, pretty_float(f, 2)));
				}
			}
			else
			{
				float min = control.slider_float.range_min;
				float max = control.slider_float.range_max;
				slider->SetContinuousRange(CScrollSelection<float>(min, pretty_float(min, 2)), CScrollSelection<float>(max, pretty_float(max, 2)), &ValuePrint);
			}

			slider->SetValue(control.slider_float.initial_value);

			slider->Layout_FullWidth();
			slider->SetTop(GetDefaultMargin() + (float)i * (control_height + GetDefaultMargin()));
			slider->SetHeight(control_height);
			slider->SetSelectedListener(this, FloatSliderMoved, tsprintf("%d %d", i, m_selectors.size()));

			m_selectors.push_back(slider);
			break;
		}

		case VB_CONTROL_SLIDER_INT:
		{
			CControl<CScrollSelector<int>> slider = AddControl(new CScrollSelector<int>(control.m_name));

			for (int n = control.slider_int.range_min; n <= control.slider_int.range_max; n += control.slider_int.step_size)
				slider->AddSelection(CScrollSelection<int>(n, tsprintf("%d", n)));

			slider->SetValue(control.slider_int.initial_value);

			slider->Layout_FullWidth();
			slider->SetTop(GetDefaultMargin() + (float)i * (control_height + GetDefaultMargin()));
			slider->SetHeight(control_height);
			slider->SetSelectedListener(this, IntSliderMoved, tsprintf("%d %d", i, m_selectors.size()));

			m_selectors.push_back(slider);
			break;
		}

		default:
			break;
		}
	}

	BaseClass::Layout();
}

void CPanel_Controls::Think()
{
	BaseClass::Think();
}

void CPanel_Controls::Paint(float x, float y, float w, float h)
{
	TPROF("CPanel_Controls::Paint()");

	BaseClass::Paint(x, y, w, h);
}

void CPanel_Controls::ButtonPressedCallback(const tstring& sArgs)
{
	unsigned int i = stoi(sArgs);

	MonitorWindow()->GetViewback()->ControlCallback(i);
}

void CPanel_Controls::FloatSliderMovedCallback(const tstring& sArgs)
{
	tvector<tstring> tokens;
	tstrtok(sArgs, tokens);

	if (tokens.size() < 2)
		return;

	unsigned int control = stoi(tokens[0]);
	unsigned int selector = stoi(tokens[1]);

	MonitorWindow()->GetViewback()->ControlCallback(control, m_selectors[selector].DowncastStatic<CScrollSelector<float>>()->GetSelectionValue());
}

void CPanel_Controls::IntSliderMovedCallback(const tstring& sArgs)
{
	tvector<tstring> tokens;
	tstrtok(sArgs, tokens);

	if (tokens.size() < 2)
		return;

	unsigned int control = stoi(tokens[0]);
	unsigned int selector = stoi(tokens[1]);

	MonitorWindow()->GetViewback()->ControlCallback(control, m_selectors[selector].DowncastStatic<CScrollSelector<int>>()->GetSelectionValue());
}

void CPanel_Controls::ControlUpdated(size_t control_id, float f_value, int i_value)
{
	auto& controls = MonitorWindow()->GetViewback()->GetControls();

	TAssert(control_id < controls.size());
	TAssert(control_id < m_selectors.size());

	auto the_real_control = m_selectors[control_id];

	switch (controls[control_id].m_type)
	{
	case VB_CONTROL_SLIDER_FLOAT:
	{
		CScrollSelector<float>* slider = the_real_control.DowncastStatic<CScrollSelector<float>>();
		if (!slider->IsMovingHandle())
			slider->SetValue(f_value);
		break;
	}
	case VB_CONTROL_SLIDER_INT:
	{
		CScrollSelector<int>* slider = the_real_control.DowncastStatic<CScrollSelector<int>>();
		if (!slider->IsMovingHandle())
			slider->SetValue(i_value);
		break;
	}
	}
}
<|endoftext|>


<commit_before>#include <mbed.h>

#include "software-spi.hpp"

#include "pinmap.h"

#include "logger.hpp"
#include "timer-api.hpp"

#define SW_SPI_TIMER            (LPC_TIM2)
#define SW_SPI_TIMER_IRQn       (TIMER2_IRQn)

namespace
{
const uint8_t timer_pconp_bit_num = 22;
const uint8_t mosi_channel = 2;
const uint8_t sck_channel = 1;
const int match_count = 8;

bool isInit = false;

void software_spi_irq_handler(void)
{
    NVIC_DisableIRQ(SW_SPI_TIMER_IRQn);

    timer_ClearMatch(SW_SPI_TIMER, mosi_channel);
    timer_ClearMatch(SW_SPI_TIMER, sck_channel);

    NVIC_ClearPendingIRQ(SW_SPI_TIMER_IRQn);
    NVIC_EnableIRQ(SW_SPI_TIMER_IRQn);
}

void setup_timer(LPC_TIM_TypeDef* timer, uint8_t match_num, bool enable_int = false)
{

    NVIC_DisableIRQ(SW_SPI_TIMER_IRQn);

    // enable power for the timer
    LPC_SC->PCONP |= 1 << timer_pconp_bit_num;
    LOG(INF1, "Power enabled for PCONP[%u]", timer_pconp_bit_num);

    // disable the timer and reset its counter for initilization
    timer_Disable(timer);
    timer_Reset(timer);

    // default to 1MHz (1 us ticks)
    uint32_t prescale_count = (SystemCoreClock / 4) / 8000000;
    timer_PrescaleSet(timer, prescale_count);
    LOG(INF1, "Prescale count set to %u for channel %u", prescale_count, match_num);

    // set the count for when to trigger the interrupt
    timer_SetMatch(timer, match_num, match_count);
    LOG(INF1, "Match count set to %u for channel %u", match_count, match_num);

    // reset the counter when (counter == cnt)
    timer_ResetOnMatchEnable(timer, match_num);
    LOG(INF1, "Counter will RESET for channel %u", match_num);

    timer_MatchDisableInt(timer, match_num);
    LOG(INF1, "Interrupt DISABLED for channel %u", match_num);

    // set to toggle the pin state on a match count - initial state of 0
    timer_ExtMatchControlSet(timer, 0, TIMER_EXTMATCH_TOGGLE, match_num);
    LOG(INF1, "Pin will TOGGLE for channel %u match", match_num);

    // LOG(INIT,
    //     "IRQ Addr:\t0x%08X\t(before remap)\r\n"
    //     "  IRQ Priority:\t%10u\t(out of %u)\r\n"
    //     "  IRQ Number:\t%10u",
    //     NVIC_GetVector(SW_SPI_TIMER_IRQn),
    //     NVIC_GetPriority(SW_SPI_TIMER_IRQn),
    //     (1 << __NVIC_PRIO_BITS),
    //     SW_SPI_TIMER_IRQn
    //    );

    // // set the location of the interrupt callback
    // NVIC_SetVector(SW_SPI_TIMER_IRQn, (uint32_t)software_spi_irq_handler);
    // NVIC_EnableIRQ(SW_SPI_TIMER_IRQn);

    // LOG(INIT,
    //     "IRQ Addr:\t0x%08X\t(after remap)\r\n"
    //     "  Expected:\t0x%08X",
    //     NVIC_GetVector(SW_SPI_TIMER_IRQn),
    //     software_spi_irq_handler
    //    );
}
}   // anonymous namespace

SoftwareSPI::SoftwareSPI(PinName mosi_pin, PinName miso_pin, PinName sck_pin, int bit_width)
{
    mosi = new DigitalOut(mosi_pin, 0);
    miso = new DigitalIn(miso_pin);
    sck = new DigitalOut(sck_pin);
    format(bit_width);

    // mosi_timer_base = pinmap_peripheral(mosi_pin, PinMap_Timer);
    // mosi_pin_mode = pinmap_function(mosi_pin, PinMap_Timer);

    // if (mosi_pin != NC) {
    //     uint32_t pin_number = (uint32_t)mosi_pin - (uint32_t)P0_0;
    //     int index = pin_number >> 4;
    //     int offset = (pin_number & 0xF) << 1;

    //     // PINCONARRAY->PINSEL[index] &= ~(0x3 << offset);
    //     // PINCONARRAY->PINSEL[index] |= mosi_pin_mode << offset;

    //     LOG(INIT,
    //         "MOSI pin channel mapped\r\n"
    //         "  Timer Base:\t0x%08X\r\n"
    //         "  Pin Mode:\t%10u",
    //         mosi_timer_base,
    //         mosi_pin_mode
    //        );
    // } else {
    //     LOG(SEVERE,
    //         "Unable to pinmap MOSI pin!\t(0x%08X)",
    //         mosi_pin
    //        );
    // }

    // sck_timer_base = pinmap_peripheral(sck_pin, PinMap_Timer);
    // sck_pin_mode = pinmap_function(sck_pin, PinMap_Timer);

    // if (sck_pin != NC) {
    //     uint32_t pin_number = (uint32_t)sck_pin - (uint32_t)P0_0;
    //     int index = pin_number >> 4;
    //     int offset = (pin_number & 0xF) << 1;

    //     PINCONARRAY->PINSEL[index] &= ~(0x3 << offset);
    //     PINCONARRAY->PINSEL[index] |= sck_pin_mode << offset;

    //     LOG(INIT,
    //         "SCK pin channel mapped\r\n"
    //         "  Timer Base:\t0x%08X\r\n"
    //         "  Pin Mode:\t%10u",
    //         sck_timer_base,
    //         sck_pin_mode
    //        );
    // } else {
    //     LOG(SEVERE,
    //         "Unable to pinmap SCK pin!\t(0x%08X)",
    //         sck_pin
    //        );
    // }
}

SoftwareSPI::~SoftwareSPI()
{
    delete mosi;
    delete miso;
    delete sck;
}

// void SoftwareSPI::setup(void)
// {
//     if (mosi_timer_base == sck_timer_base) {
//         setup_timer((LPC_TIM_TypeDef*)sck_timer_base, sck_channel);
//         setup_timer((LPC_TIM_TypeDef*)mosi_timer_base, mosi_channel, true);
//     }
//     // MOSI & SCK must be able to be controlled through a common hardware timer
//     else {
//         LOG(FATAL, "MOSI and SCK do no share the same timer");
//     }
// }

void SoftwareSPI::format(int bits, int mode)
{
    this->bits = bits;
    this->mode = mode;
    polarity = (mode >> 1) & 1;
    phase = mode & 1;
    sck->write(polarity);
}

int SoftwareSPI::write(int value)
{
    int read = 0;

    for (int bit = bits - 1; bit >= 0; --bit) {
        mosi->write( (value >> bit) & 0x01 );

        if (phase == 0) {
            if (miso->read())
                read |= (1 << bit);
        }

        sck->write(!polarity);

        if (phase == 1) {
            if (miso->read())
                read |= (1 << bit);
        }

        sck->write(polarity);
    }

    return read;
}
<commit_msg>commenting out the rest of the timer related work<commit_after>#include <mbed.h>

#include "software-spi.hpp"

#include "logger.hpp"

// #include "pinmap.h"
// #include "timer-api.hpp"

// #define SW_SPI_TIMER            (LPC_TIM2)
// #define SW_SPI_TIMER_IRQn       (TIMER2_IRQn)

// namespace
// {
// const uint8_t timer_pconp_bit_num = 22;
// const uint8_t mosi_channel = 2;
// const uint8_t sck_channel = 1;
// const int match_count = 8;

// bool isInit = false;

// void software_spi_irq_handler(void)
// {
//     NVIC_DisableIRQ(SW_SPI_TIMER_IRQn);

//     timer_ClearMatch(SW_SPI_TIMER, mosi_channel);
//     timer_ClearMatch(SW_SPI_TIMER, sck_channel);

//     NVIC_ClearPendingIRQ(SW_SPI_TIMER_IRQn);
//     NVIC_EnableIRQ(SW_SPI_TIMER_IRQn);
// }

// void setup_timer(LPC_TIM_TypeDef* timer, uint8_t match_num, bool enable_int = false)
// {

//     NVIC_DisableIRQ(SW_SPI_TIMER_IRQn);

//     // enable power for the timer
//     LPC_SC->PCONP |= 1 << timer_pconp_bit_num;
//     LOG(INF1, "Power enabled for PCONP[%u]", timer_pconp_bit_num);

//     // disable the timer and reset its counter for initilization
//     timer_Disable(timer);
//     timer_Reset(timer);

//     // default to 1MHz (1 us ticks)
//     uint32_t prescale_count = (SystemCoreClock / 4) / 8000000;
//     timer_PrescaleSet(timer, prescale_count);
//     LOG(INF1, "Prescale count set to %u for channel %u", prescale_count, match_num);

//     // set the count for when to trigger the interrupt
//     timer_SetMatch(timer, match_num, match_count);
//     LOG(INF1, "Match count set to %u for channel %u", match_count, match_num);

//     // reset the counter when (counter == cnt)
//     timer_ResetOnMatchEnable(timer, match_num);
//     LOG(INF1, "Counter will RESET for channel %u", match_num);

//     timer_MatchDisableInt(timer, match_num);
//     LOG(INF1, "Interrupt DISABLED for channel %u", match_num);

//     // set to toggle the pin state on a match count - initial state of 0
//     timer_ExtMatchControlSet(timer, 0, TIMER_EXTMATCH_TOGGLE, match_num);
//     LOG(INF1, "Pin will TOGGLE for channel %u match", match_num);

// LOG(INIT,
//     "IRQ Addr:\t0x%08X\t(before remap)\r\n"
//     "  IRQ Priority:\t%10u\t(out of %u)\r\n"
//     "  IRQ Number:\t%10u",
//     NVIC_GetVector(SW_SPI_TIMER_IRQn),
//     NVIC_GetPriority(SW_SPI_TIMER_IRQn),
//     (1 << __NVIC_PRIO_BITS),
//     SW_SPI_TIMER_IRQn
//    );

// // set the location of the interrupt callback
// NVIC_SetVector(SW_SPI_TIMER_IRQn, (uint32_t)software_spi_irq_handler);
// NVIC_EnableIRQ(SW_SPI_TIMER_IRQn);

// LOG(INIT,
//     "IRQ Addr:\t0x%08X\t(after remap)\r\n"
//     "  Expected:\t0x%08X",
//     NVIC_GetVector(SW_SPI_TIMER_IRQn),
//     software_spi_irq_handler
//    );
}
}   // anonymous namespace

SoftwareSPI::SoftwareSPI(PinName mosi_pin, PinName miso_pin, PinName sck_pin, int bit_width)
{
    mosi = new DigitalOut(mosi_pin, 0);
    miso = new DigitalIn(miso_pin);
    sck = new DigitalOut(sck_pin);
    format(bit_width);

    // mosi_timer_base = pinmap_peripheral(mosi_pin, PinMap_Timer);
    // mosi_pin_mode = pinmap_function(mosi_pin, PinMap_Timer);

    // if (mosi_pin != NC) {
    //     uint32_t pin_number = (uint32_t)mosi_pin - (uint32_t)P0_0;
    //     int index = pin_number >> 4;
    //     int offset = (pin_number & 0xF) << 1;

    //     // PINCONARRAY->PINSEL[index] &= ~(0x3 << offset);
    //     // PINCONARRAY->PINSEL[index] |= mosi_pin_mode << offset;

    //     LOG(INIT,
    //         "MOSI pin channel mapped\r\n"
    //         "  Timer Base:\t0x%08X\r\n"
    //         "  Pin Mode:\t%10u",
    //         mosi_timer_base,
    //         mosi_pin_mode
    //        );
    // } else {
    //     LOG(SEVERE,
    //         "Unable to pinmap MOSI pin!\t(0x%08X)",
    //         mosi_pin
    //        );
    // }

    // sck_timer_base = pinmap_peripheral(sck_pin, PinMap_Timer);
    // sck_pin_mode = pinmap_function(sck_pin, PinMap_Timer);

    // if (sck_pin != NC) {
    //     uint32_t pin_number = (uint32_t)sck_pin - (uint32_t)P0_0;
    //     int index = pin_number >> 4;
    //     int offset = (pin_number & 0xF) << 1;

    //     PINCONARRAY->PINSEL[index] &= ~(0x3 << offset);
    //     PINCONARRAY->PINSEL[index] |= sck_pin_mode << offset;

    //     LOG(INIT,
    //         "SCK pin channel mapped\r\n"
    //         "  Timer Base:\t0x%08X\r\n"
    //         "  Pin Mode:\t%10u",
    //         sck_timer_base,
    //         sck_pin_mode
    //        );
    // } else {
    //     LOG(SEVERE,
    //         "Unable to pinmap SCK pin!\t(0x%08X)",
    //         sck_pin
    //        );
    // }
}

SoftwareSPI::~SoftwareSPI()
{
    delete mosi;
    delete miso;
    delete sck;
}

// void SoftwareSPI::setup(void)
// {
//     if (mosi_timer_base == sck_timer_base) {
//         setup_timer((LPC_TIM_TypeDef*)sck_timer_base, sck_channel);
//         setup_timer((LPC_TIM_TypeDef*)mosi_timer_base, mosi_channel, true);
//     }
//     // MOSI & SCK must be able to be controlled through a common hardware timer
//     else {
//         LOG(FATAL, "MOSI and SCK do no share the same timer");
//     }
// }

void SoftwareSPI::format(int bits, int mode)
{
    this->bits = bits;
    this->mode = mode;
    polarity = (mode >> 1) & 1;
    phase = mode & 1;
    sck->write(polarity);
}

int SoftwareSPI::write(int value)
{
    int read = 0;

    for (int bit = bits - 1; bit >= 0; --bit) {
        mosi->write( (value >> bit) & 0x01 );

        if (phase == 0) {
            if (miso->read())
                read |= (1 << bit);
        }

        sck->write(!polarity);

        if (phase == 1) {
            if (miso->read())
                read |= (1 << bit);
        }

        sck->write(polarity);
    }

    return read;
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef DLL_CONV_LAYER_INL
#define DLL_CONV_LAYER_INL

#include "cpp_utils/assert.hpp"             //Assertions

#include "etl/etl.hpp"

#include "tmp.hpp"
#include "layer_traits.hpp"

namespace dll {

template<typename Desc>
struct conv_layer final {
    using desc = Desc;
    using weight = typename desc::weight;
    using this_type = conv_layer<desc>;

    static constexpr const std::size_t NV1 = desc::NV1;
    static constexpr const std::size_t NV2 = desc::NV2;
    static constexpr const std::size_t NH1 = desc::NH1;
    static constexpr const std::size_t NH2 = desc::NH2;
    static constexpr const std::size_t NC = desc::NC;
    static constexpr const std::size_t K = desc::K;

    static constexpr const std::size_t NW1 = NV1 - NH1 + 1; //By definition
    static constexpr const std::size_t NW2 = NV2 - NH2 + 1; //By definition

    static constexpr const bool dbn_only = layer_traits<this_type>::is_dbn_only();

    static constexpr const function activation_function = desc::activation_function;

    //Weights and biases
    etl::fast_matrix<weight, NC, K, NW1, NW2> w;        //!< Weights
    etl::fast_matrix<weight, K> b;                      //!< Hidden biases

    //No copying
    conv_layer(const conv_layer& layer) = delete;
    conv_layer& operator=(const conv_layer& layer) = delete;

    //No moving
    conv_layer(conv_layer&& layer) = delete;
    conv_layer& operator=(conv_layer&& layer) = delete;

    /*!
     * \brief Initialize a conv layer with basic weights.
     */
    conv_layer(){
        //Initialize the weights and biases following Lecun approach
        //to initialization [lecun-98b]

        //TODO Check initialization
        b = etl::normal_generator<weight>(0.0, 1.0 / std::sqrt(double(NC * NV1 * NV2)));
        w = etl::normal_generator<weight>(0.0, 1.0 / std::sqrt(double(NC * NV1 * NV2)));
    }

    static constexpr std::size_t input_size() noexcept {
        return NC * NV1 * NV2;
    }

    static constexpr std::size_t output_size() noexcept {
        return K * NH1 * NH2;
    }

    static constexpr std::size_t parameters() noexcept {
        return K * NW1 * NW2;
    }

    static std::string to_short_string(){
        char buffer[1024];
        snprintf(buffer, 1024, "Conv: %lux%lux%lu -> (%lux%lux%lu) -> %s -> %lux%lux%lu", NC, NV1, NV2, K, NW1, NW2, to_string(activation_function).c_str(), K, NH1, NH2);
        return {buffer};
    }

    void display() const {
        std::cout << to_short_string() << std::endl;
    }

    template<typename H1, typename V>
    void activate_hidden(H1&& output, const V& v) const {
        etl::fast_dyn_matrix<weight, 2, K, NH1, NH2> v_cv;      //Temporary convolution

        auto w_f = etl::force_temporary(w);

        //flip all the kernels horizontally and vertically

        for(std::size_t channel = 0; channel < NC; ++channel){
            for(size_t k = 0; k < K; ++k){
                w_f(channel)(k).fflip_inplace();
            }
        }

        v_cv(1) = 0;

        for(std::size_t channel = 0; channel < NC; ++channel){
            etl::conv_2d_valid_multi(v(channel), w_f(channel), v_cv(0));

            v_cv(1) += v_cv(0);
        }

        output = f_activate<activation_function>(etl::rep<NH1, NH2>(b) + v_cv(1));
    }

    template<typename H1, typename V>
    void batch_activate_hidden(H1&& output, const V& v) const {
        etl::fast_dyn_matrix<weight, 2, K, NH1, NH2> v_cv;      //Temporary convolution

        const auto Batch = etl::dim<0>(v);

        auto w_f = force_temporary(w);

        //flip all the kernels horizontally and vertically

        for(std::size_t channel = 0; channel < NC; ++channel){
            for(size_t k = 0; k < K; ++k){
                w_f(channel)(k).fflip_inplace();
            }
        }

        for(std::size_t batch = 0; batch < Batch; ++batch){
            v_cv(1) = 0;

            for(std::size_t channel = 0; channel < NC; ++channel){
                etl::conv_2d_valid_multi(v(batch)(channel), w_f(channel), v_cv(0));

                v_cv(1) += v_cv(0);
            }

            output(batch) = f_activate<activation_function>(etl::rep<NH1, NH2>(b) + v_cv(1));
        }
    }

    //Utilities to be used by DBNs

    using input_one_t = etl::fast_dyn_matrix<weight, NC, NV1, NV2>;
    using output_one_t = etl::fast_dyn_matrix<weight, K, NH1, NH2>;
    using input_t = std::vector<input_one_t>;
    using output_t = std::vector<output_one_t>;

    template<std::size_t B>
    using input_batch_t = etl::fast_dyn_matrix<weight, B, NC, NV1, NV2>;

    template<std::size_t B>
    using output_batch_t = etl::fast_dyn_matrix<weight, B, K, NH1, NH2>;

    template<typename Sample>
    input_one_t convert_sample(const Sample& sample) const {
        return input_one_t{sample};
    }

    template<typename Input>
    output_one_t prepare_one_output(bool /*is_last*/ = false, std::size_t /*labels*/ = 0) const {
        return {};
    }

    void activate_one(const input_one_t& input, output_one_t& h_a) const {
        activate_hidden(h_a, input);
    }
};

//Allow odr-use of the constexpr static members

template<typename Desc>
const std::size_t conv_layer<Desc>::NV1;

template<typename Desc>
const std::size_t conv_layer<Desc>::NV2;

template<typename Desc>
const std::size_t conv_layer<Desc>::NH1;

template<typename Desc>
const std::size_t conv_layer<Desc>::NH2;

template<typename Desc>
const std::size_t conv_layer<Desc>::NC;

template<typename Desc>
const std::size_t conv_layer<Desc>::NW1;

template<typename Desc>
const std::size_t conv_layer<Desc>::NW2;

template<typename Desc>
const std::size_t conv_layer<Desc>::K;

} //end of dll namespace

#endif
<commit_msg>Improve initialization<commit_after>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef DLL_CONV_LAYER_INL
#define DLL_CONV_LAYER_INL

#include "cpp_utils/assert.hpp"             //Assertions

#include "etl/etl.hpp"

#include "tmp.hpp"
#include "layer_traits.hpp"

namespace dll {

template<typename Desc>
struct conv_layer final {
    using desc = Desc;
    using weight = typename desc::weight;
    using this_type = conv_layer<desc>;

    static constexpr const std::size_t NV1 = desc::NV1;
    static constexpr const std::size_t NV2 = desc::NV2;
    static constexpr const std::size_t NH1 = desc::NH1;
    static constexpr const std::size_t NH2 = desc::NH2;
    static constexpr const std::size_t NC = desc::NC;
    static constexpr const std::size_t K = desc::K;

    static constexpr const std::size_t NW1 = NV1 - NH1 + 1; //By definition
    static constexpr const std::size_t NW2 = NV2 - NH2 + 1; //By definition

    static constexpr const bool dbn_only = layer_traits<this_type>::is_dbn_only();

    static constexpr const function activation_function = desc::activation_function;

    //Weights and biases
    etl::fast_matrix<weight, NC, K, NW1, NW2> w;        //!< Weights
    etl::fast_matrix<weight, K> b;                      //!< Hidden biases

    //No copying
    conv_layer(const conv_layer& layer) = delete;
    conv_layer& operator=(const conv_layer& layer) = delete;

    //No moving
    conv_layer(conv_layer&& layer) = delete;
    conv_layer& operator=(conv_layer&& layer) = delete;

    /*!
     * \brief Initialize a conv layer with basic weights.
     */
    conv_layer(){
        //Initialize the weights and biases following Lecun approach
        //to initialization [lecun-98b]

        //TODO Check initialization
        w = etl::normal_generator<weight>() * std::sqrt(2.0 / double(NC * NV1 * NV2));

        if(activation_function == function::RELU){
            b = etl::normal_generator<weight>() * std::sqrt(2.0 / double(NC * NV1 * NV2));
        } else {
            b = etl::normal_generator<weight>() * std::sqrt(2.0 / double(NC * NV1 * NV2));
        }
    }

    static constexpr std::size_t input_size() noexcept {
        return NC * NV1 * NV2;
    }

    static constexpr std::size_t output_size() noexcept {
        return K * NH1 * NH2;
    }

    static constexpr std::size_t parameters() noexcept {
        return K * NW1 * NW2;
    }

    static std::string to_short_string(){
        char buffer[1024];
        snprintf(buffer, 1024, "Conv: %lux%lux%lu -> (%lux%lux%lu) -> %s -> %lux%lux%lu", NC, NV1, NV2, K, NW1, NW2, to_string(activation_function).c_str(), K, NH1, NH2);
        return {buffer};
    }

    void display() const {
        std::cout << to_short_string() << std::endl;
    }

    template<typename H1, typename V>
    void activate_hidden(H1&& output, const V& v) const {
        etl::fast_dyn_matrix<weight, 2, K, NH1, NH2> v_cv;      //Temporary convolution

        auto w_f = etl::force_temporary(w);

        //flip all the kernels horizontally and vertically

        for(std::size_t channel = 0; channel < NC; ++channel){
            for(size_t k = 0; k < K; ++k){
                w_f(channel)(k).fflip_inplace();
            }
        }

        v_cv(1) = 0;

        for(std::size_t channel = 0; channel < NC; ++channel){
            etl::conv_2d_valid_multi(v(channel), w_f(channel), v_cv(0));

            v_cv(1) += v_cv(0);
        }

        output = f_activate<activation_function>(etl::rep<NH1, NH2>(b) + v_cv(1));
    }

    template<typename H1, typename V>
    void batch_activate_hidden(H1&& output, const V& v) const {
        etl::fast_dyn_matrix<weight, 2, K, NH1, NH2> v_cv;      //Temporary convolution

        const auto Batch = etl::dim<0>(v);

        auto w_f = force_temporary(w);

        //flip all the kernels horizontally and vertically

        for(std::size_t channel = 0; channel < NC; ++channel){
            for(size_t k = 0; k < K; ++k){
                w_f(channel)(k).fflip_inplace();
            }
        }

        for(std::size_t batch = 0; batch < Batch; ++batch){
            v_cv(1) = 0;

            for(std::size_t channel = 0; channel < NC; ++channel){
                etl::conv_2d_valid_multi(v(batch)(channel), w_f(channel), v_cv(0));

                v_cv(1) += v_cv(0);
            }

            output(batch) = f_activate<activation_function>(etl::rep<NH1, NH2>(b) + v_cv(1));
        }
    }

    //Utilities to be used by DBNs

    using input_one_t = etl::fast_dyn_matrix<weight, NC, NV1, NV2>;
    using output_one_t = etl::fast_dyn_matrix<weight, K, NH1, NH2>;
    using input_t = std::vector<input_one_t>;
    using output_t = std::vector<output_one_t>;

    template<std::size_t B>
    using input_batch_t = etl::fast_dyn_matrix<weight, B, NC, NV1, NV2>;

    template<std::size_t B>
    using output_batch_t = etl::fast_dyn_matrix<weight, B, K, NH1, NH2>;

    template<typename Sample>
    input_one_t convert_sample(const Sample& sample) const {
        return input_one_t{sample};
    }

    template<typename Input>
    output_one_t prepare_one_output(bool /*is_last*/ = false, std::size_t /*labels*/ = 0) const {
        return {};
    }

    void activate_one(const input_one_t& input, output_one_t& h_a) const {
        activate_hidden(h_a, input);
    }
};

//Allow odr-use of the constexpr static members

template<typename Desc>
const std::size_t conv_layer<Desc>::NV1;

template<typename Desc>
const std::size_t conv_layer<Desc>::NV2;

template<typename Desc>
const std::size_t conv_layer<Desc>::NH1;

template<typename Desc>
const std::size_t conv_layer<Desc>::NH2;

template<typename Desc>
const std::size_t conv_layer<Desc>::NC;

template<typename Desc>
const std::size_t conv_layer<Desc>::NW1;

template<typename Desc>
const std::size_t conv_layer<Desc>::NW2;

template<typename Desc>
const std::size_t conv_layer<Desc>::K;

} //end of dll namespace

#endif
<|endoftext|>


<commit_before>#pragma once

#include <map>
#include <memory>

#include <json.hpp>

#include <mos/gfx/character.hpp>
#include <mos/gfx/environment_light.hpp>
#include <mos/gfx/font.hpp>
#include <mos/gfx/mesh.hpp>
#include <mos/gfx/model.hpp>
#include <mos/gfx/spot_light.hpp>
#include <mos/gfx/texture_2d.hpp>
#include <unordered_map>

namespace mos::gfx {

/** Cache for faster loading of textures and meshes. */
class Assets final {
public:
  /** @param directory The directory where the assets exist, relative to the run
   * directory. */
  explicit Assets(std::string directory = "assets/");

  Assets(const Assets &assets) = delete;
  Assets(const Assets &&assets) = delete;
  Assets &operator=(const Assets &assets) = delete;
  Assets &operator=(const Assets &&assets) = delete;
  ~Assets() = default;

  /** Loads a Mesh from a *.mesh file and caches it internally. */
  auto mesh(const std::string &path) -> Shared_mesh;

  /** Loads Texture2D from a *.png file or *.texture and caches it internally.
   */
  auto texture(const std::string &path, bool color_data = true,
               bool mipmaps = true,
               const Texture_2D::Filter &filter = Texture_2D::Filter::Linear,
               const Texture_2D::Wrap &wrap = Texture_2D::Wrap::Repeat)
      -> Shared_texture_2D;

  /** Remove all unused assets. */
  auto clear_unused() -> void;

  /** Clear all assets. */
  auto clear() -> void;

  /** Parent directory of the cache. */
  auto directory() const -> std::string;

private:
  using Meshes = std::unordered_map<std::string, Shared_mesh>;
  using Textures = std::unordered_map<std::string, Shared_texture_2D>;

  const std::string directory_;
  Meshes meshes_;
  Textures textures_;
};
} // namespace mos::gfx
<commit_msg>Formatting<commit_after>#pragma once

#include <map>
#include <memory>
#include <unordered_map>

#include <json.hpp>

#include <mos/gfx/character.hpp>
#include <mos/gfx/environment_light.hpp>
#include <mos/gfx/font.hpp>
#include <mos/gfx/mesh.hpp>
#include <mos/gfx/model.hpp>
#include <mos/gfx/spot_light.hpp>
#include <mos/gfx/texture_2d.hpp>

namespace mos::gfx {

/** Cache for faster loading of textures and meshes. */
class Assets final {
public:
  /** @param directory The directory where the assets exist, relative to the run
   * directory. */
  explicit Assets(std::string directory = "assets/");

  Assets(const Assets &assets) = delete;
  Assets(const Assets &&assets) = delete;
  Assets &operator=(const Assets &assets) = delete;
  Assets &operator=(const Assets &&assets) = delete;
  ~Assets() = default;

  /** Loads a Mesh from a *.mesh file and caches it internally. */
  auto mesh(const std::string &path) -> Shared_mesh;

  /** Loads Texture2D from a *.png file or *.texture and caches it internally.
   */
  auto texture(const std::string &path, bool color_data = true,
               bool mipmaps = true,
               const Texture_2D::Filter &filter = Texture_2D::Filter::Linear,
               const Texture_2D::Wrap &wrap = Texture_2D::Wrap::Repeat)
      -> Shared_texture_2D;

  /** Remove all unused assets. */
  auto clear_unused() -> void;

  /** Clear all assets. */
  auto clear() -> void;

  /** Parent directory of the cache. */
  auto directory() const -> std::string;

private:
  using Meshes = std::unordered_map<std::string, Shared_mesh>;
  using Textures = std::unordered_map<std::string, Shared_texture_2D>;

  const std::string directory_;
  Meshes meshes_;
  Textures textures_;
};
} // namespace mos::gfx
<|endoftext|>