//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2015 Richard S. Smith and collaborators.
//
// If you use MorphoGraphX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoGraphX is free software, and is licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
// 
#ifndef RANDOM_HPP
#define RANDOM_HPP
/**
 * \file Random.hpp
 *
 * Defines various functions to generate random numbers
 */

#include <Config.hpp>

#include <Vector.hpp>

#ifdef WIN32
static long int random() { return rand(); }

static void srandom(unsigned int seed) { return srand(seed); }
#endif

namespace mgx 
{
  /**
   * Initialize the random number with the current time of the day (in
   * microsecond)
   *
   * \returns the seed used.
   */
  mgx_EXPORT unsigned int sran_time();
  
  /**
   * Initialize the random number generator
   */
  mgx_EXPORT void sran(unsigned int seed);
  
  /**
   * Generate a random number uniformly distributed between 0 and M
   */
  mgx_EXPORT double ran(double M);
  /**
   * Generate a random number uniformly distributed between 0 and M
   */
  mgx_EXPORT long double ran(long double M);
  /**
   * Generate a random number uniformly distributed between 0 and M
   */
  mgx_EXPORT float ran(float M);
  
  /**
   * Generate a random number uniformly distributed between 0 and M
   */
  template <typename T> double ran(T M) {
    return ran(double(M));
  }
  
  /**
   * Generate a random vector uniformely distributed between Vector(0) and V
   */
  template <size_t dim, typename T> Vector<dim, T> ran(const Vector<dim, T>& V)
  {
    Vector<dim, T> result;
    for(size_t i = 0; i < dim; ++i)
      result[i] = ran(V[i]);
    return result;
  }
  
  /**
   * Generate a random number with gaussian distribution
   *
   * \param mean Mean of the gaussian distribution
   * \param sigma Standard deviation of the gaussian distribution
   */
  mgx_EXPORT double gaussRan(double mean, double sigma);
  
  /**
   * Generate a random vector with gaussian distribution
   *
   * \param mean Vector with mean of the gaussian distribution for each
   * dimension
   * \param sigma Vector with standard deviation of the gaussian distribution
   * for each dimension
   */
  template <size_t dim> Vector<dim, double> gaussRan(const Vector<dim, double>& mean, const Vector<dim, double>& sigma)
  {
    Vector<dim, double> result;
    for(size_t i = 0; i < dim; ++i)
      result[i] = gaussRan(mean[i], sigma[i]);
    return result;
  }
  
  /**
   * Returns a random number between 0 and RAND_MAX.
   */
  mgx_EXPORT long int ranInt();
  
  /**
   * Returns a random number between 0 and RAND_MAX.
   */
  template <typename T> T ranInt()
  {
    long int i = ranInt();
    return (T)i;
  }
  
  /**
   * Returns a vector of random numbers between 0 and RAND_MAX.
   */
  template <size_t dim, typename T> Vector<dim, T> ranInt()
  {
    Vector<dim, long int> result;
    for(size_t i = 0; i < dim; ++i)
      result[i] = ranInt<T>();
    return result;
  }
  
  /**
   * Returns a vector of random numbers between 0 and RAND_MAX.
   */
  template <size_t dim> Vector<dim, long int> ranInt() {
    return ranInt<dim, long int>();
  }
  
  /**
   * Returns a random number between 0 and n (excluded), for n <= RAND_MAX
   */
  mgx_EXPORT long int ranInt(long int n);
  
  /**
   * Returns a vector of random numbers
   * \param n Vector with the maximum number for each dimension
   */
  template <size_t dim, typename T> Vector<dim, T> ranInt(const Vector<dim, T>& n)
  {
    Vector<dim, T> result;
    for(size_t i = 0; i < dim; ++i)
      result[i] = ranInt<T>(n[i]);
    return result;
  }
}
#endif
