/* +---------------------------------------------------------------------------+
   |          The Mobile Robot Programming Toolkit (MRPT) C++ library          |
   |                                                                           |
   |                   http://mrpt.sourceforge.net/                            |
   |                                                                           |
   |   Copyright (C) 2005-2008  University of Malaga                           |
   |                                                                           |
   |    This software was written by the Perception and Robotics               |
   |      research group, University of Malaga (Spain).                        |
   |    Contact: Jose-Luis Blanco  <jlblanco@ctima.uma.es>                     |
   |                                                                           |
   |  This file is part of the MRPT project.                                   |
   |                                                                           |
   |     MRPT 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.                                   |
   |                                                                           |
   |   MRPT 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 MRPT.  If not, see <http://www.gnu.org/licenses/>.         |
   |                                                                           |
   +---------------------------------------------------------------------------+ */
#ifndef RandomGenerator_H
#define RandomGenerator_H

#include <MRPT/UTILS/utils_defs.h>
#include <MRPT/UTILS/CMatrixTemplateNumeric.h>

namespace UTILS
{
	class CMatrix;
	class CMatrixD;

	/** A namespace of pseudo-random numbers genrators of diferent distributions.
	 */
	namespace RandomGenerator
	{
		/** Generate a normalized normally distributed pseudo-random number.
		 *  \param likelihood If desired, pass a pointer to a double which will receive the likelihood of the given sample to have been obtained, that is, the value of the normal pdf at the sample value.
		 */
		double  normalizedGaussian( double *likelihood = NULL);

		/** Generate a normally distributed pseudo-random number.
		 * \param mean The mean value of desired normal distribution
		 * \param std  The standard deviation value of desired normal distribution
		 */
		double  RandomNormal( double mean = 0, double std = 1);

		/** Generate a uniformly distributed pseudo-random number.
		 */
		double  RandomUni( double min, double max);

		/** Fills the given matrix with independent, uniformly distributed samples.
		  * \sa matrixRandomNormal
		  */
		template <class T>
		void matrixRandomUni(
			CMatrixTemplateNumeric<T> &matrix,
			const  T& unif_min = 0,
			const  T& unif_max = 1 )
		{
			for (size_t r=0;r<matrix.getRowCount();r++)
				for (size_t c=0;c<matrix.getColCount();c++)
					matrix.set_unsafe(r,c, RandomUni(unif_min,unif_max) );
		}

		/** Fills the given matrix with independent, normally distributed samples.
		  * \sa matrixRandomUni
		  */
		template <class T>
		void matrixRandomNormal(
			CMatrixTemplateNumeric<T> &matrix,
			const  T& mean = 0,
			const  T& std = 1 )
		{
			for (size_t r=0;r<matrix.getRowCount();r++)
				for (size_t c=0;c<matrix.getColCount();c++)
					matrix.set_unsafe(r,c, mean + std*normalizedGaussian() );
		}

		/** Randomize the generators.
		 *   A seed can be providen, or a current-time based seed can be used (default)
		 */
		void	 Randomize(long seed);
		void	 Randomize();

		/** Returns a random permutation of a vector: all the elements of the input vector are in the output but at random positions.
		  */
		template <class T>
		void  randomPermutation(
			const std::vector<T> &in_vector,
			std::vector<T>       &out_result)
		{
			MRPT_TRY_START

			size_t i,n=in_vector.size();
			out_result.resize(n);

			// Build an auxiliary vector with the indeces that are not yet permuted:
			std::vector<size_t>	idxs( n );
			for (i=0;i<n;i++) idxs[i]=i;

			// Draw n random integers (=indexes):
			for (i=0;i<n;i++)
			{
				size_t selectedIdx = static_cast<size_t>( RandomUni(0.0f, n-i-0.01f ) );
				ASSERT_(selectedIdx<idxs.size())

				// Add to the output & remove from the idxs vector:
				out_result[i] = in_vector[ idxs[selectedIdx] ];
				idxs.erase( idxs.begin()+selectedIdx );
			}

			MRPT_TRY_END
		}

		/** Generate multidimensional random samples according to a given covariance matrix.
		 * \exception std::exception On invalid covariance matrix
		 * \sa randomNormalMultiDimensionalMany
		 */
		void  randomNormalMultiDimensional(
			const CMatrixTemplateNumeric<double>		&cov,
			std::vector<double>						&out_result);

		/** Generate multidimensional random samples according to a given covariance matrix.
		 * \exception std::exception On invalid covariance matrix
		 * \sa randomNormalMultiDimensionalMany
		 */
		void  randomNormalMultiDimensional(
			const CMatrixTemplateNumeric<float>		&cov,
			std::vector<float>						&out_result);


		/** Generate a given number of multidimensional random samples according to a given covariance matrix.
		 * \param cov The covariance matrix where to draw the samples from.
		 * \param desiredSamples The number of samples to generate.
		 * \param samplesLikelihoods If desired, set to a valid pointer to a vector, where it will be stored the likelihoods of having obtained each sample: the product of the gaussian-pdf for each independent variable.
		 * \param ret The output list of samples
		 *
		 * \exception std::exception On invalid covariance matrix
		 *
		 * \sa randomNormalMultiDimensional
		 */
		void  randomNormalMultiDimensionalMany(
			const CMatrixD				&cov,
			size_t						desiredSamples,
			std::vector<vector_double>	&ret,
			vector_double				*samplesLikelihoods = NULL);

		/** Generate a given number of multidimensional random samples according to a given covariance matrix.
		 * \param cov The covariance matrix where to draw the samples from.
		 * \param desiredSamples The number of samples to generate.
		 * \param samplesLikelihoods If desired, set to a valid pointer to a vector, where it will be stored the likelihoods of having obtained each sample: the product of the gaussian-pdf for each independent variable.
		 * \param ret The output list of samples
		 *
		 * \exception std::exception On invalid covariance matrix
		 *
		 * \sa randomNormalMultiDimensional
		 */
		void  randomNormalMultiDimensionalMany(
			const CMatrix				&cov,
			size_t						desiredSamples,
			std::vector<vector_float>	&ret,
			vector_double				*samplesLikelihoods = NULL);

	}

} // End of namespace

#endif
