// File: crn_rand.h
// See Copyright Notice and license at the end of inc/crnlib.h
#pragma once

namespace crnlib {
class kiss99 {
 public:
  kiss99();

  void seed(uint32 i, uint32 j, uint32 k);

  inline uint32 next();

 private:
  uint32 x;
  uint32 y;
  uint32 z;
  uint32 c;
};

class well512 {
 public:
  well512();

  enum { cStateSize = 16 };
  void seed(uint32 seed[cStateSize]);
  void seed(uint32 seed);
  void seed(uint32 seed1, uint32 seed2, uint32 seed3);

  inline uint32 next();

 private:
  uint32 m_state[cStateSize];
  uint32 m_index;
};

class ranctx {
 public:
  ranctx() { seed(0xDE149737); }

  void seed(uint32 seed);

  inline uint32 next();

 private:
  uint32 a;
  uint32 b;
  uint32 c;
  uint32 d;
};

class random {
 public:
  random();
  random(uint32 i);

  void seed(uint32 i);
  void seed(uint32 i1, uint32 i2, uint32 i3);

  uint32 urand32();
  uint64 urand64();

  // "Fast" variant uses no multiplies.
  uint32 fast_urand32();

  uint32 bit();

  // Returns random between [0, 1)
  double drand(double l, double h);

  float frand(float l, float h);

  // Returns random between [l, h)
  int irand(int l, int h);

  // Returns random between [l, h]
  int irand_inclusive(int l, int h);

  double gaussian(double mean, double stddev);

  void test();

 private:
  ranctx m_ranctx;
  kiss99 m_kiss99;
  well512 m_well512;
};

// Simpler, minimal state PRNG
class fast_random {
 public:
  fast_random();
  fast_random(uint32 i);
  fast_random(const fast_random& other);
  fast_random& operator=(const fast_random& other);

  void seed(uint32 i);

  uint32 urand32();
  uint64 urand64();

  int irand(int l, int h);

  double drand(double l, double h);

  float frand(float l, float h);

 private:
  uint32 jsr;
  uint32 jcong;
};

}  // namespace crnlib
