//-*-c++-*-
// -------------------------------------------
// RCS data:
// $Date: 2003/06/23 14:47:24 $
// $Revision: 1.1.1.1 $
// $Source: /cvsroot/miwm/miwm/miwm/rng.cc,v $
// $Id: rng.cc,v 1.1.1.1 2003/06/23 14:47:24 bwise837 Exp $
// $RCSfile: rng.cc,v $
// -------------------------------------------
// Copyright by Ben Paul Wise.
// -------------------------------------------
// 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 of the License, or
// (at your option) any later version.
// 
// 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.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// -------------------------------------------
// a very simple random number generator
// -------------------------------------------

#include "rng.h"

#include <sys/types.h>
#include <unistd.h>

// ----------------------------------------
// random number generators are subtle and delicate.
// see the header file.
// ----------------------------------------

RNG::RNG() {
  nrkSeed = (unsigned long int)getpid(); // a fairly random number
  fill_array();
}




RNG::RNG(int seed) {
  nrkSeed = seed;
  fill_array();
}

RNG::~RNG() {
//   do nothing, for now
//   delete[] Y;
}

void
RNG::fill_array() {
  int i;

  // every occurance of "55", "54" or "24" are magic number
  // that can not be altered
  jY = 24;  // magic number!
  kY = 55;  // magic number!
  mY = 1024 * 1024; // 2^20. not magic, but it better be large
  assert (nrkSeed > 0);
  for (i=0; i<55; i++)
    Y[i] = (1000 * (i+1)) % mY;
  for (i=0; i<55; i++) 
    Y[(21*i) % 55] = (54321 * (i + nrkSeed)) % mY;

  return;
}


// see the header file for a reference to a justification
// of this very simple, but supposedly very good algorithm
float
RNG::fvalue() {

  // every occurance of "55", "54" or "24" are magic number
  // that can not be altered

  // suppose the last number generated was X[n],
  // where n is the number of times we've called the generator
  // Y[j] == X[n-24]
  // Y[k] == X[n-55]
  float rslt;

  Y[kY] = (Y[kY] + Y[jY]) % mY;
  // somehow, this keeps going negative under BSD, but
  // not under Linux or Cygwin
  if (Y[kY] < 0)
    Y[kY] =-Y[kY]; // hence the ugly hack
  rslt =  (float) ((double)(Y[kY]) / (double)(mY));
  assert (0.0 <= rslt);
  assert (rslt <= 1.0);

  // this sets jY to jY-1, and wraps around
  jY = (jY + 54) % 55;
  kY = (kY + 54) % 55;

  return rslt;

}

float 
RNG::uniform(float a, float b) {
  return a + ((b-a) * fvalue());
}

float 
RNG::normal(float mean, float stdv) {
  // this is far from ideal, but tolerable.
  int i;
  int m = 2; // 1,2,3
  int n = m * m * 12 ; 
  // n=1 makes m = 12
  // n=2 makes m = 48
  // n=3 makes m = 108
  float sum = 0;

  for (i=0; i< n ; i++)
      sum = sum + fvalue();

  // fvalue is U(0,1). mean = 1/2,
  // variance = 1/12
  // hence, sum has mean N/2, variance N/12
  // pull mean down to 0.0
  sum = sum - ( n / 2.0 );
  // rescale stdv to 1.0
  sum = sum / m ;  // == sum / sqrt( n / 12.0 ) 
  sum = sum * stdv + mean;
  return sum;
}

int
RNG::select(int a, int b)
{
  int c;
  if (a==b)
    return a;
  else {
    assert (a<b);
    c = (a+b)/2;  // floor of that!
    if ((a+b)%2 == 1)
      {
	if (fvalue() < 0.5)
	  return select(a,c);
	else
	  return select(c+1,b);
      }
    else
      {
	if (fvalue() < 0.5)
	  return select(a,c);
	else
	  return select(c,b);
      }
  }
}

// ----------------------------------------
// recommended by "Numerical Recipes"
// I can find no flaw with it...
float 
RNG::ran2(long *idum)
{
  int j;
  long k;
  static long idum2=123456789;
  static long iy=0;
  static long iv[NTAB];
  float temp;

  if (*idum <= 0) {
    if (-(*idum) < 1) *idum=1;
    else *idum = -(*idum);
    idum2=(*idum);
    for (j=NTAB+7; j>=0; j--) {
      k=(*idum)/IQ1;
      *idum=IA1*(*idum-k*IQ1)-k*IR1;
      if (*idum < 0) *idum +=IM1;
      if (j < NTAB) iv[j] = *idum;
    }
    iy = iv[0];
  }
  k=(*idum)/IQ1;
  *idum=IA1*(*idum-k*IQ1)-k*IR1;
  if (*idum < 0) *idum += IM1;
  k=idum2/IQ2;
  idum2=IA2*(idum2-k*IQ2)-k*IR2;
  if (idum2 < 0) idum2 += IM2;
  j=iy/NDIV;
  iy=iv[j]-idum2;
  iv[j] = *idum;
  if (iy < 1) iy += IMM1;
  if ((temp=AM*iy) > RNMX) return RNMX;
  else return temp;
}

// -------------------------------------------
// end of rng.cc
// -------------------------------------------



