#ifndef RANDOM_H
#define RANDOM_H

#include <cassert>
#include <algorithm>
#include <climits>
#include <cmath>
using namespace std;

#include <point.h>
#include <typedefs.h>


/*
  About

  random00<> random10<> random01<> random11<> generate
  random numbers between 0 and 1. 

  The suffix on random indicates whether the end points 
  of the interval are included or not.  eg random11< > 
  includes the start and end points of the interval in 
  sampling.
*/

/*!
\brief Generate a random uinteger. 

The sample of random numbers generated is hundreds of times 
 smaller than what is needed for robust applications.
 However it uses C functions so is convenient for small and
 not so accurate applications.
*/
class randomgenerator
{
public:

  /** Generate a random uinteger. */
  uintc operator()() const
    { return rand(); }

  /** */ 
  void randomize_start()
    { srand( time(NULL) ); }

  /** Initialize the state of the random number generator. */
  void seed(uintc s=1);

  /** Test 3D point in interval 0 to 1 optionally with endpoints. */
  template< typename Q >
  static boolc domain(point3<Q> const & p, boolc zero=true, boolc one=true);
  /** Test 2D point in interval 0 to 1 optionally with endpoints. */
  template< typename Q >
  static boolc domain(point2<Q> const & p, boolc zero=true, boolc one=true);
  /** Test interval 0 to 1 optionally with end points. */
  template< typename T >
  static boolc domain(T const & x, boolc zero=true, boolc one=true);

};



/*!
\brief  Generating random numbers in [0,1]. 

This includes both end points.
*/
template<typename T = double , typename G = randomgenerator >
class random11
{
  /** Maximum value or end point. */
  uint nend;
  /** Change in interval length. */
  T delta;
public:

  /** Random number generator. */
  G rg;

  /** T type information exported. */
  typedef T Ttype;
  /** G type information exported. */
  typedef G Gtype;

  /** n equally spaced sample points in interval [0,1] 
      are possible. */
  random11( uintc nend_=LONG_MAX)
    : nend(nend_), delta(1) { delta /= (nend-1); }

  /** Initialize the random number generator. */
  void seed(uint s=1)
    { rg.seed(s); }

  /** Generate a random number. */
  T const operator()() const  
    { return T(rg() % nend)*delta; }

  /** Generate a random number. */
  void operator()(T& x) const 
    { x = (rg() % nend)*delta; }

  /** Access the endpoint. */
  uintc endpoint() const
    { return nend; }

};


/*!
\brief  Generating random numbers in (0,1). 

This excludes both end points.
*/
template<typename T = double , typename G = randomgenerator >
class random00
{
  uint nend;
  G rg;
  T delta;
public:

  /** T type information exported. */
  typedef T Ttype;
  /** G type information exported. */
  typedef G Gtype;

  /** n equally spaced sample points in interval (0,1) 
      are possible. */
  random00(uintc nend_=LONG_MAX)
    : nend(nend_), delta(1) { delta /= (nend+1);}

  /** Initialize the state of the random number generator. */
  void seed(uintc s=1)
    { rg.seed(s); }

  /** Generate a random number. */
  T const operator()() const
    { return T( 1+(rg() % nend))*delta; }

  /** Generate a random number. */
  void operator()(T& x) const
    { x = T( 1+(rg() % nend))*delta; }

  /** Access the endpoint. */
  uintc endpoint() const
    { return nend; }
};

/*!
\brief  Generating random numbers in (0,1]. 

The left endpoint is excluded and the right 
 endpoint is included.
*/
template<typename T = double , typename G = randomgenerator >
class random01
{
  uint nend;
  G rg;
  T delta;
public:

  /** T type information exported. */
  typedef T Ttype;
  /** G type information exported. */
  typedef G Gtype;

  /** n equally spaced sample points in interval (0,1] 
      are possible. */
  random01(uintc nend_=LONG_MAX)
    : nend(nend_), delta(1) { delta /= nend; }

  /** Initialize the state of the random number generator. */
  void seed(uintc s=1)
    { rg.seed(s); }

  /** Generate a random number. */
  T const operator()() const
    { return ( T(1+(rg() % nend)) )*delta; }

  /** Generate a random number. */
  void operator()(T& x) const 
    { x = ( T(1+(rg() % nend)) )*delta; }

  /** Access the endpoint. */
  uintc endpoint() const
    { return nend; }
};


/*!
\brief  Generating random numbers in [0,1). 

The right endpoint is excluded and the left endpoint 
 is included.
*/
template<typename T = double , typename G = randomgenerator >
class random10
{
    uint nend;
    G rg;
    T delta;
public:

  /** T type information exported. */
  typedef T Ttype;
  /** G type information exported. */
  typedef G Gtype;

  /** n equally spaced sample points in interval [0,1) 
      are possible. */
  random10(uintc nend_=LONG_MAX)
    : nend(nend_), delta(1) { delta /= nend; }

  /** Initialize the state of the random number generator. */
  void seed(uintc s=1)
    { rg.seed(s); }

  /** Generate a random number. */
  T const operator()() const 
    { return T(rg() % nend)*delta; }

  /** Generate a random number. */
  void operator()(T& x) const 
    { x = (rg() % nend)*delta; }

  /** Access the endpoint. */
  uintc endpoint() const
    { return nend; }
};

/*!
\brief Make random points.
*/
template< typename RG=random11<> >
class randompoint
{
public:

  /** Random number generator. */
  RG random;

  /** [0,1] interval. */
  randompoint() {}

  /** Initialize random before sending it in. */
  randompoint(RG random_)
    : random(random_) {}

  typedef point2< typename RG::Ttype > pt2;
  typedef point3< typename RG::Ttype > pt3;

  /** Make a 2D point. */
  pt2 makepoint2()
    { return pt2(random(),random()); }
  /** Make a 3D point. */
  pt3 makepoint3()
    { return pt3(random(),random(),random()); }

};

//----------------------------------------------------------
//  Implementation


template< typename T >
boolc randomgenerator::domain(T const & x, boolc zero, boolc one)
{
//cout << SHOW(x) << endl;
  if (zero==false)
    assertreturnfalseN( x == (T)0 );

  if (one==false)
    assertreturnfalseN( x == (T)1 );

  assertreturnfalseN( x < (T)0 );

  assertreturnfalse( x <= (T)1 ); 

  return true;
}


template< typename Q >
boolc randomgenerator::domain(point2<Q> const & p, boolc zero, boolc one)
{
  bool res=true;
  if ( domain(p.x,zero,one) == false )
    res=false;
  if ( domain(p.y,zero,one) == false )
    res=false;

  return res;
}

template< typename Q >
boolc randomgenerator::domain(point3<Q> const & p, boolc zero, boolc one)
{
  bool res=true;
  res &= domain(p.x,zero,one);
  res &= domain(p.y,zero,one);
  res &= domain(p.z,zero,one);

  return res;
}


#endif



