//
// 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.
// 
#include "Random.hpp"
#include <stdlib.h>
#include <cmath>
#ifdef _MSC_VER
#  include <sys/timeb.h>
#  include <sys/types.h>
#else
#  include <sys/time.h>
#endif

namespace mgx 
{
  #ifdef WIN32
  static long int random() {
    return rand();
  }
  
  static void srandom(unsigned int seed) {
    return srand(seed);
  }
  #endif
  
  void sran(unsigned int seed) {
    return srandom(seed);
  }
  
  long int ranInt() {
    return random();
  }
  
  long int ranInt(long int n) {
    return random() % n;
  }
  
  double ran(double M)
  {
    long int i = ::random();
    return M * (double)i / (double)RAND_MAX;
  }
  
  float ran(float M)
  {
    long int i = ::random();
    return M * (float)i / (float)RAND_MAX;
  }
  
  long double ran(long double M)
  {
    long int i = ::random();
    return M * (long double)i / (long double)RAND_MAX;
  }
  
  double gaussRan(double mean, double sigma)
  {
    static float y2 = 0;
    static bool cached = false;
    if(!cached) {
      float x1, x2, w, y1;
      do {
        x1 = ran(2.0) - 1.0;
        x2 = ran(2.0) - 1.0;
        w = x1 * x1 + x2 * x2;
      } while(w >= 1.0);
  
      w = std::sqrt((-2.0 * std::log(w)) / w);
      y1 = x1 * w;
      y2 = x2 * w;
      cached = true;
      return y1 * sigma + mean;
    } else {
      cached = false;
      return y2 * sigma + mean;
    }
  }
  
  unsigned int sran_time()
  {
  #ifdef _MSC_VER
    struct _timeb t;
    _ftime(&t);
    unsigned int seed = t.millitm + t.time * 1000;
  #else
    struct timeval tv;
    struct timezone tz;
    gettimeofday(&tv, &tz);
    unsigned int seed = tv.tv_usec + tv.tv_sec * 1000000;
  #endif
    sran(seed);
    return seed;
  }
}
