/* Copyright (c) 1997-2009
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Darmstadt, Germany)
   http://www.polymake.de

   This program 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 2, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   This program 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.
--------------------------------------------------------------------------------
   $Project: polymake $$Id: random_generators.cc 9298 2009-09-02 23:10:56Z gawrilow $
*/

#include <random_generators.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <algorithm>

namespace pm {

void NormalRNG::fill()
{
   double v, u, s;
   do {
      v = 2*super::get()-1;
      u = 2*super::get()-1;
      s = u*u + v*v;
   } while (s >= 1);

   double scale = sqrt( (-2*log(s)) / s );
   x[0]=v * scale;
   x[1]=u * scale;
   index=0;
}

void UniformRNG< Vector<double> >::fill_vector()
{
   double norm;
   do {
      std::copy(NormalRNG::begin(), entire(point));
      norm = sqr(point);
   } while (norm == 0.0);	 // this occurs with very low probability
   point /= sqrt(norm);
}

void DiscreteRNG::normalize()
{
   double acc_sum=0.;
   for (Entire< Vector<double> >::iterator d=entire(distribution); !d.at_end(); ++d)
      *d=(acc_sum+=*d);
   for (Entire< Vector<double> >::iterator d=entire(distribution); !d.at_end(); ++d)
      *d/=acc_sum;
}

int DiscreteRNG::get() const
{
   const double r=rg.get();
   Vector<double>::const_iterator d=distribution.begin();
   return std::lower_bound(d,distribution.end(),r)-d;
}

namespace {

class RandomSeed {
   int rfd;
   unsigned long counter;

public:
   RandomSeed()
   {
      rfd=open("/dev/urandom", O_RDONLY);
      if (rfd<0)
	 counter=getpid();
   }

   operator unsigned long ()
   {
      if (rfd>0) {
	 int consumed=0;
	 for (;;) {
	    int s=read(rfd, reinterpret_cast<char*>(&counter)+consumed, sizeof(counter)-consumed);
	    if (s<0) break;
	    consumed+=s;
	    if (consumed==sizeof(counter)) return counter;
	 }
      }
      union {
	 struct timeval tv;
	 unsigned long s;
      } t;
      gettimeofday(&t.tv, 0);
      if (sizeof(time_t)>sizeof(unsigned long))
	 t.s ^= t.tv.tv_usec;
      return t.s + (counter+=1000);
   }
};

} // end anonymous namespace

unsigned long random_seed()
{
   static RandomSeed seed;
   return seed;
}

} // end namespace pm
