#pragma once

#include <psemek/math/interval.hpp>

#include <limits>
#include <cmath>

namespace psemek::random
{

	template <typename T>
	struct uniform_real_distribution
	{
		static_assert(std::is_floating_point_v<T>);

		using result_type = T;

		uniform_real_distribution()
			: range_{T(0), T(1)}
		{}

		uniform_real_distribution(T min, T max)
			: range_{min, max}
		{}

		uniform_real_distribution(math::interval<T> range)
			: range_(range)
		{}

		template <typename RNG>
		T operator()(RNG && rng);

		math::interval<T> range() const { return range_; }

	private:
		math::interval<T> range_;

		template <typename RNG>
		T generate(RNG & rng);
	};

	template <typename T>
	template <typename RNG>
	T uniform_real_distribution<T>::operator()(RNG && rng)
	{
		return generate(rng) * range_.length() + range_.min;
	}

	template <typename T>
	template <typename RNG>
	T uniform_real_distribution<T>::generate(RNG & rng)
	{
		// Previously used an algorithm like in https://en.cppreference.com/w/cpp/numeric/random/generate_canonical
		// But the simple algorithm here is way faster, so...

		T const R = static_cast<T>(rng.max()) - static_cast<T>(rng.min()) + 1;

		return static_cast<T>(rng() - rng.min()) / R;
	}

}
