// $Id: random.cpp,v 1.21 2018/01/03 21:33:02 mkkuhner Exp $

/*
Copyright 2002-2005 Mary K. Kuhner, Peter Beerli, and Joseph Felsenstein

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

/* Authors: Peter Beerli, Mary Kuhner, Jon Yamato and Joseph Felsenstein */


// Mary 9/1/2010 Changed to use Boost random number generator (Mersenne Twister)
// due to suspicions that the homebrew was showing too much pattern.

#include <cassert>
#include <cmath>
#include <ctime>
#include <fstream>
#include <iostream>

#include "random.h"
#include "stringx.h"

#ifdef DMALLOC_FUNC_CHECK
#include "/usr/local/include/dmalloc.h"
#endif

using namespace std;

//------------------------------------------------------------------------------------

Random::Random()
{
    m_rng.seed(static_cast<unsigned int>(time(0)));
}

//------------------------------------------------------------------------------------

Random::Random(long seed)
{
    m_rng.seed(static_cast<unsigned int>(seed));
}

//------------------------------------------------------------------------------------

void Random::Seed(long seed)
{
    m_rng.seed(static_cast<unsigned int>(seed));
} // Seed

//------------------------------------------------------------------------------------

void Random::Read(char *filename)
{
    unsigned int seed;

    ifstream file;
    file.open(filename);
    file >> seed;
    file.close();

    m_rng.seed(seed);
}

//------------------------------------------------------------------------------------

void Random::Write(char *filename)
{
    ofstream file;
    file.open(filename,ios::app);
    file << Float() << endl;
    file.close();
}

//------------------------------------------------------------------------------------

long Random::Long(long m)
{
    assert(m>0);
    // DON'T make these two structures static -- you cannot since
    // the initial one takes an argument from the enclosing method
    boost::uniform_int<> dist(0,m-1);
    boost::variate_generator<boost::mt19937&, boost::uniform_int<> >
        vg(m_rng, dist);
    return vg();
}

//------------------------------------------------------------------------------------

double Random::Float()
{
    // Making these two structures static saves us from having to
    // re-create them each time we use this method.
    static boost::uniform_real<> fng;
    static boost::variate_generator<boost::mt19937&, boost::uniform_real<> > vg(m_rng, fng);

    // the generator can return 0.0 (we've seen it 2010-10-22), so
    // we need to repeat until we get a good value. We don't want
    // exactly 0.0 or 1.0
    while(true)
    {
        double val =  vg();
        if (val > 0.0 && val < 1.0)
        {
            return val;
        }
    }
    assert(false);
    return 0.0;
}

//------------------------------------------------------------------------------------

char Random::Base()
{
    long whichbase = Long(4);
    if (whichbase == 0) return 'A';
    if (whichbase == 1) return 'C';
    if (whichbase == 2) return 'G';
    if (whichbase == 3) return 'T';

    assert(false); // unknown base in Random::Base()
    return 'X';

}

//------------------------------------------------------------------------------------

bool Random::Bool()
{
    return (Long(2) == 1L);
}

//------------------------------------------------------------------------------------

string Random::Name()
{
    string name;

    name = ToString(Base()) + ToString(Base()) + ToString(Long(1000));

    return name;

}

//------------------------------------------------------------------------------------

double Random::Normal()
// Sample from a normal distribution with mean 0 and variance 1
// using Box-Muller algorithm (see Wikipedia "Normal Distribution"
// article).
{
    // Making these two structures static saves us from having to
    // re-create them each time we use this method.
    static boost::normal_distribution<> norm(0.0, 1.0);
    static boost::variate_generator<boost::mt19937&, boost::normal_distribution<> > vg(m_rng, norm);

    return vg();
} // Normal

//____________________________________________________________________________________
