/*
 * random.hpp
 *
 * This file is part of Algorithms library toolkit.
 * Copyright (C) 2017 Jan Travnicek (jan.travnicek@fit.cvut.cz)

 * Algorithms library toolkit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * Algorithms library toolkit 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 General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with Algorithms library toolkit.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Created on: May 28, 2015
 * Author: Jan Travnicek
 */

#pragma once

#include <random>
#include <limits>

namespace ext {

/**
 * \brief
 * Definition of randomness sources. Provided sources are c++ builtin random_device and own semirandom_device. The random device is the best randomness source but its generation is slow. Semirandom device is good enough and it provices random values fast. Given a seed the semirandom device can also provide reproducible sequence any number of times.
 */
class random_devices {
public:
	/**
	 * \brief
	 * Getter of the singleton random device.
	 *
	 * \return instance of the random device
	 */
	static std::random_device & getRandom ( ) {
		static std::random_device res;
		return res;
	}

	/**
	 * \brief
	 * The reference to singleton random device
	 */
	static std::random_device & random;

private:
	/**
	 * \brief
	 * Implementation of semirandom device with the same interface as the random device.
	 */
	class semirandom_device {
	public:
		/**
		 * \brief
		 * The type of values the random device produces
		 */
		typedef unsigned int result_type;

	private:
		/**
		 * \brief
		 * The semirandom values generator.
		 */
		std::mt19937 gen;

		/**
		 * \brief
		 * Adaptor of semirandom values generator to uniform distrubution.
		 */
		std::uniform_int_distribution<unsigned int> dis;

	public:
		/**
		 * \brief
		 * Default constructor initializing the semirandom values provider with predefined constant.
		 *
		 * Resulting semirandom device will provide the same sequence values each execution.
		 */
		semirandom_device() : gen ( 0 ) {
		}

		/**
		 * \brief
		 * New random value request method.
		 *
		 * \return semirandom value
		 */
		result_type operator()() {
			return dis(gen);
		}

		/**
		 * \brief
		 * Getter of the minimal value the semirandom device can produce.
		 *
		 * \return minimal value in the range of generated values
		 */
		static constexpr result_type min() {
			return 0;
		}

		/**
		 * \brief
		 * Getter of the maximal value the semirandom device can produce.
		 *
		 * \return maximal value in the range of generated values
		 */
		static constexpr result_type max() {
			return std::numeric_limits<result_type>::max() ;
		}

		/**
		 * \brief
		 * Allows to initialize the semirandom generator with specific value to reset the sequence of generated values.
		 *
		 * \param seed the seed value
		 */
		void seed ( unsigned int seed ) {
			gen.seed ( seed );
		}
	};

public:
	/**
	 * \brief
	 * Getter of the singleton semirandom device.
	 *
	 * \return instance of the semirandom device
	 */
	static semirandom_device & getSemirandom ( ) {
		static semirandom_device res;
		return res;
	}

	/**
	 * \brief
	 * The reference to singleton semirandom device
	 */
	static semirandom_device & semirandom;

};

} /* namespace ext */

