
#pragma once

#include <chrono>
#include <type_traits>

template <typename Clock>
class stopwatch
{
public:
	typedef Clock clock;
	typedef typename clock::time_point time_point;
	typedef typename clock::duration duration;

private:
	time_point last_;

public:
	stopwatch()
		: last_(clock::now())
	{}

	void reset()
	{
		*this = stopwatch();
	}

	time_point now() const
	{
		return clock::now();
	}

	duration elapsed() const
	{
		return now() - last_;
	}

	duration tick()
	{
		time_point dummy;
		return tick(dummy);
	}

	duration tick(time_point& now_)
	{
		now_ = now();
		auto elapsed = now_ - last_;
		last_ = now_;
		return elapsed;
	}
};

typedef stopwatch<std::chrono::steady_clock> default_stopwatch;

template <typename T, typename Rep, typename Period, 
	typename = typename std::enable_if<std::is_floating_point<T>::value>::type>
T float_cast(const std::chrono::duration<Rep, Period>& duration, T mul = T(1))
{
	return duration.count() * mul * (T(Period::num) / T(Period::den));
}


#include <cstring>
#include <algorithm>
#include <array>
#include <functional>
#include <iostream>
#include <random>

template <typename RNG>
RNG make_rng()
{
	std::random_device rd;
	std::array<typename RNG::result_type, RNG::state_size> seed_data;
	std::generate(seed_data.begin(), seed_data.end(), std::ref(rd));
	std::seed_seq seq(seed_data.begin(), seed_data.end());
	return RNG(seq);
}

template <typename RNG>
void test_rng(RNG& rng, void* buffer, std::size_t size)
{
	while (size >= sizeof(decltype(rng())))
	{
		auto r = rng();
		std::memcpy(buffer, &r, sizeof r);
		buffer = static_cast<char*>(buffer) + sizeof r;
		size -= sizeof r;
	}
}

int main()
{
	const std::size_t mb = 512;
	std::vector<char> buf(mb * 1024 * 1024);
	default_stopwatch sw;
	double t;
	auto rng1 = make_rng<std::mt19937>();
	auto rng2 = make_rng<std::mt19937_64>();
	auto rng3 = std::minstd_rand((std::random_device()()));

	sw.reset();
	test_rng(rng1, buf.data(), buf.size());
	t = float_cast<double>(sw.elapsed());
	std::cout << "mt19937: " << t << " - " << mb / t << " MB/s\n";

	sw.reset();
	test_rng(rng2, buf.data(), buf.size());
	t = float_cast<double>(sw.elapsed());
	std::cout << "mt19937_64: " << t << " - " << mb / t << " MB/s\n";

	sw.reset();
	test_rng(rng3, buf.data(), buf.size());
	t = float_cast<double>(sw.elapsed());
	std::cout << "minstd_rand: " << t << " - " << mb / t << " MB/s\n";
}