#include <RiRandom.H>



#ifndef RIRANDOM_H
#include <RiRandom.H>
#endif

#include <stdlib.h>






/***************************************************************
CLASS
    RiDRand48Strategy
     Encapsulate drand48

DESCRIPTION
     Encapsulate drand48 for use as a RandomStrategy.  Uses the state calls to drand48 so
     each copy is independent.  Matches the SGI (MIPSpro 7.1) version of drand48

****************************************************************/

class RiDRand48Strategy : public RiRandomStrategy {
  public:
    // GROUP: Constructors and assignment
    //// Default Constructor
    RiDRand48Strategy(int seed =0);
    //// Destructor
    virtual ~RiDRand48Strategy() {}
				//// Clone me, returning a RNG with the same state.
    virtual  RiRandomStrategy  *Clone();
    // GROUP: Members
				//// Seed the Strategy
    void SetSeed(long seed = 0);
				//// Fill an array with RI_RANDOM_ARRAY_SIZE random numbers
    void Generate(RiReal *data, int num = RI_RANDOM_ARRAY_SIZE);    
  private:
    unsigned short Xi[3];
};

RiDRand48Strategy::RiDRand48Strategy(int seed)
{
    SetSeed(seed);
}

RiRandomStrategy  *RiDRand48Strategy::Clone()
{
    return new RiDRand48Strategy(*this);
}

void RiDRand48Strategy::SetSeed(long seed)
{
    
    Xi[1] = seed & 0xFFFF; 
    Xi[2] = seed >> 16; 
    Xi[0] = 0x330E;
}


void RiDRand48Strategy::Generate(RiReal *data, int num)
{
    for(int i = 0; i < num; i++) 
      data[i] = erand48(Xi);
}


const long IA = 16807;
const long IM = 2147483647;
const RiReal AM = 1.0 / IM;
const long IQ = 127773;
const long IR = 2836;
const int NTAB = 97;
const long NDIV = (1 + (IM - 1) / NTAB);
const RiReal RNMX = RiRealAlmostOne;

/***************************************************************
CLASS
    RiShuffleRandomStrategy
     The ran1 function from numerical recipes

DESCRIPTION
     Encapsulate drand48 for use as a RandomStrategy.  Uses the state calls to drand48 so
     each copy is independent.  Matches the SGI (MIPSpro 7.1) version of drand48

****************************************************************/


class RiShuffleRandomStrategy : public RiRandomStrategy {
  public:
    // GROUP: Constructors and assignment
    //// Default Constructor
    RiShuffleRandomStrategy(int seed =0);
    //// Destructor
    virtual ~RiShuffleRandomStrategy() {}
				//// Clone me, returning a RNG with the same state.
    virtual  RiRandomStrategy  *Clone();
    // GROUP: Members
				//// Seed the Strategy
    void SetSeed(long seed = 0);
				//// Fill an array with RI_RANDOM_ARRAY_SIZE random numbers
    void Generate(RiReal *data, int num = RI_RANDOM_ARRAY_SIZE);    
  private:
    long iy;
    long iv[NTAB];
    long last;
};

RiShuffleRandomStrategy::RiShuffleRandomStrategy(int seed)
{
    if(seed <= 0)
      SetSeed(1);
    else
      SetSeed(seed);
}

RiRandomStrategy  *RiShuffleRandomStrategy::Clone()
{
    return new RiShuffleRandomStrategy(*this);
}

void RiShuffleRandomStrategy::SetSeed(long seed)
{
    long k;
    iy = 0;
    for(int j = NTAB+7; j>= 0; j--) {
	k = seed / IQ;
	seed = IA * (seed - k * IQ) - IR * k;
	if(seed < 0) seed += IM;
	if(j < NTAB) iv[j] = seed;
    }
    last = seed;
    iy = iv[0];
}


void RiShuffleRandomStrategy::Generate(RiReal *data, int num)
{
    int j;
    long k;
    RiReal temp;
    
    for(int i = 0; i < num; i++) {
	k = last / IQ;
	last = IA * (last - k * IQ) - IR * k;
	if(last < 0) last += IM;
	j = iy/NDIV;
	iy = iv[j];
	iv[j] = last;
	if ((temp = AM * iy) > RNMX) data[i] = RNMX;
	else data[i] = temp;
    }
}





/***************************************************************
CLASS
    RiNonRandomStrategy
     Non random random number generator strategy return .5 always.  

DESCRIPTION
     Non random random number generator strategy.  Useful for testing and making
     RiSamplePattern orthogonal to RiRandom.  Always returns .5

****************************************************************/

class RiNonRandomStrategy : public RiRandomStrategy {
  public:
				// GROUP: Constructors and assignment
				//// Default Constructor
    RiNonRandomStrategy(int seed = 0);
				//// Clone me, returning a RNG with the same state.
    virtual  RiRandomStrategy  *Clone();
				// GROUP: Members
				//// Seed the Strategy
    virtual void SetSeed(long seed = 0);
				//// Fill an array with RI_RANDOM_ARRAY_SIZE random numbers
    virtual void Generate(RiReal *data, int num = RI_RANDOM_ARRAY_SIZE);    
};



RiNonRandomStrategy::RiNonRandomStrategy(int)
{
}

RiRandomStrategy  *RiNonRandomStrategy::Clone()
{
    return new RiNonRandomStrategy();
}

void RiNonRandomStrategy::SetSeed(long)
{
}

void RiNonRandomStrategy::Generate(RiReal *data, int num)
{
    for(int i = 0; i < num; i++) 
      data[i] = .5;
}





/**********************************************************
// RiRandom Methods
// 
// ********************************************************/




RiRandom::RiRandom(long seed, Method meth)
{
    cur = RI_RANDOM_ARRAY_SIZE;
    switch (meth) {
      case NonRandom:
	  generator = RiNonRandomStrategy(seed).Clone();
	  break;
      case Shuffle:
	  generator = RiShuffleRandomStrategy(seed).Clone();
	  break;
      case DRand48:
      default:
	  generator = RiDRand48Strategy(seed).Clone();
	  break;
    }
}

RiRandom::RiRandom(const RiRandom &rhs)
{
    cur = rhs.cur;
    for(int i = cur; i < RI_RANDOM_ARRAY_SIZE; i++)
      generated[i] = rhs.generated[i];
    generator = rhs.generator->Clone();

}

const RiRandom &RiRandom::operator=(const RiRandom &rhs)
{
    cur = rhs.cur;
    for(int i = cur; i < RI_RANDOM_ARRAY_SIZE; i++)
      generated[i] = rhs.generated[i];
    generator = rhs.generator->Clone();
    return *this;
}

RiRandom::~RiRandom()
{
    delete generator;
}

void   RiRandom::SetSeed(int seed)
{
    generator->SetSeed(seed);
    cur = RI_RANDOM_ARRAY_SIZE;
}



#ifdef RI_TESTING
#include <RiTesting.H>

bool testRiRandom()
{
    bool passed = true;

    RiRandom dr48(0);
    
    RiReal a = dr48();
    RI_TEST(a >= 0 && a < 1, passed);
				// Generate a few
    RI_TEST(a != dr48(), passed);
    RI_TEST(dr48() >= 0 && dr48() < 1, passed);

				// Copy Constructor
    RiRandom dr49 = dr48;
    RI_TEST(dr48() == dr49(), passed);
				// Assignment
    dr49 = dr48;
    RI_TEST(dr48() == dr49(), passed);

    dr48.SetSeed(0);
    RI_TEST(a == dr48(), passed);
    
				// Test NonRandom
    RiRandom  rng(0,RiRandom::NonRandom);
    RI_TEST(rng() == .5, passed);
    RI_TEST(rng(1,2) == 1.5, passed);

    return passed;
}
   

#endif /* RI_TESTING */
