/*********************************************************************
 * This file is part of the PRAPI library.
 *
 * Copyright (C) 2001 Topi Mäenpää
 * All rights reserved.
 *
 * This program is free software. You can redistribute and/or modify
 * it under the terms of the free software licence found in the
 * accompanying file "COPYING". The licence terms must always be
 * redistributed with this source file. The above copyright notice
 * must be reproduced in all modified and unmodified copies of this
 * source file.
 *
 * $Revision: 1.8 $
 *********************************************************************/

#ifndef _RANDOM_H
#define _RANDOM_H

#include <stdlib.h>
#include <unistd.h>
#include <values.h>
#include <time.h>
#include <List.h>

namespace prapi
{
	/**
	 * A class for generating random numbers and performing
	 * randomization operations. Before any random operations are
	 * performed, it is advisable to call the static init() method.
	 **/
	class Random
	{
	public:
		/**
		 * Get a double-precision number from a uniformly distributed
		 * source of pseudo-random numbers.
		 *
		 * @return a pseudo-random number in the range [0,1]
		 **/
		static double uniform() { return drand48(); }
		/**
		 * Get a double-precision number from a uniformly distributed
		 * source of pseudo-random numbers.
		 *
		 * @return a pseudo-random number in the range [min,max]
		 **/
		static double uniform(double min, double max) { return drand48()*(max-min)+min; }
		/**
		 * Get a double-precision number from a uniformly distributed
		 * source of pseudo-random numbers.
		 *
		 * @return a pseudo-random number in the range [0,upperBound)
		 **/
		static double uniform(double upperBound) { return drand48()*upperBound-MINDOUBLE; }
		/**
		 * Get a pseudo-random, uniformly distributed integer.
		 *
		 * @return a pseudo-random integer in the range [0,upperBound-1]
		 **/
		static long uniform(long upperBound) { return lrand48() % upperBound; }
		/**
		 * Get a double-precision pseudo-random number from a Gaussian
		 * distribution with zero mean and unit variance.
		 *
		 * @return a pseudo-random number from a N(0,1) source.
		 **/
		static double gaussian();

		/**
		 * Get a double-precision pseudo-random number from a Gaussian
		 * distribution with the given mean and standard deviation.
		 *
		 * @return a pseudo-random number from a Gaussian source.
		 **/
		static double gaussian(double mean, double std) { return std*gaussian() + mean; }

		/**
		 * Initialize the random number generator from system clock. Note
		 * that successive inits within the same second have no effect.
		 * Note also that this method must be called before any random
		 * numbers are generated if you don't want a similar sequence each
		 * time the program is run.
		 **/
		static void init() { srand48(time(NULL)); }
		/**
		 * Initialize the random number generator with your favourite
		 * value.
		 **/
		static void init(long seed) { srand48(seed); }

		/**
		 * Rearrange the elements in <i>lst</i> in a random way.
		 *
		 * @param lst the list to be shuffled
		 **/
		template <class T> static void shuffle(util::List<T>& lst);
		
	private:
		static bool _bUseLast;
	};


	template <class T> void Random::shuffle(util::List<T>& lst)
	{
		for (int i=0; i<lst.getLength(); i++)
			{
				int newIndex = (int)uniform((long)lst.getLength());
				T tmp(lst[i]);
				lst[i] = lst[newIndex];
				lst[newIndex] = tmp;
			}
	}
}

#endif
