// Copyright (c) 2003 Nick Mathewson.  See LICENSE for licensing information.
// $Id: rng.cpp 961 2003-12-22 04:37:34Z nickm $
// rng.cpp -- Basic prng functionality
#include <stdlib.h>
#include <math.h>
#include "rng.h"
#include <boost/random.hpp>
#include <fstream.h>

//
// If we're prototyping, use a lagged finonacci generator: it's fastest.
//
#ifdef FAST_RANDOM
// #include <boost/random/lagged_fibonacci.hpp>
#define RNGCLASS boost::lagged_fibonacci19937
#else
// #include <boost/random/mersenne_twister.hpp>
#define RNGCLASS boost::mt19937
#endif

class RNGState {
public:
  RNGCLASS _rng;
  boost::uniform_01<RNGCLASS> uniform;
  boost::normal_distribution<RNGCLASS> norm;

  RNGState() : _rng(), uniform(_rng), norm(_rng) {}
};

static RNGState state;

double
rng()
{
  return state.uniform();
}

double
normal_rng()
{
  return state.norm();
}

#define SEEDFILE "/dev/urandom"

void
seed_rng()
{
  // We use the default seed for repeatable results.
  /*
#define SEEDTYPE unsigned long
  SEEDTYPE u;
  ifstream ifs("SEEDFILE");
  for (int i = 0; i < 20; ++i) {
    ifs.get((char*)&u,sizeof(u));
    data[i] = (SEEDTYPE) u;
    state.rng._
  }
  ifs.close();
*/
}

CumulativeDist::CumulativeDist(const std::vector<double> &dist) 
  : prob(dist), cumDist(dist.size())
{
  int sz = dist.size();

  double tot = 0.0;
  for (int i = 0; i < sz; ++i) {
    tot += dist[i];
    cumDist[i] = tot;
  }
  for (int i = 0; i < sz; ++i) {
    cumDist[i] /= tot;
  }
}

int
CumulativeDist::get() const
{
  std::vector<double>::const_iterator it = 
    std::lower_bound(cumDist.begin(), cumDist.end(), rng());
  return it - cumDist.begin();
}

/*
OCumulativeDist::OCumulativeDist(const std::vector<double> &dist, 
				 double granularity)
  : prob(dist), lookupTable((int)round(1.0/granularity))
{
  std::vector<double> cumDist(dist.size());
  int sz = dist.size();

  double tot = 0.0;
  for (int i = 0; i < sz; ++i) {
    tot += dist[i];
    cumDist[i] = tot;
  }
  for (int i = 0; i < sz; ++i) {
    cumDist[i] /= tot;
  }

  for (unsigned int i = 0; i < lookupTable.size(); ++i) {
    std::vector<double>::const_iterator it = 
      std::lower_bound(cumDist.begin(), cumDist.end(), i*granularity);
    lookupTable[i] = it - cumDist.begin();
  }
}
*/

OCumulativeDist::OCumulativeDist(const std::vector<int> &dist)
  : prob(dist.size())
{
  int tot = 0;
  typedef std::vector<int>::const_iterator iter;
  for (iter it = dist.begin(); it != dist.end(); ++it) {
    assert(*it >= 0);
    tot += *it;
  }
  lookupTable.reserve(tot);
  int i = 0;
  for (iter it = dist.begin(); it < dist.end(); ++it, ++i) {
    prob[i] = ((double)(*it))/tot;
     for (int j = 0; j < *it; ++j) {
      lookupTable.push_back(i);
    }
  }
 }

