/**
 \file G3D/Random.h
 
 \maintainer Morgan McGuire, http://graphics.cs.williams.edu
 
 \created 2009-01-02
 \edited  2012-07-20

 Copyright 2000-2012, Morgan McGuire.
 All rights reserved.
 */
#ifndef G3D_Random_h
#define G3D_Random_h

#include "G3D/platform.h"
#include "G3D/g3dmath.h"
#include "G3D/GMutex.h"

namespace G3D {

/** Random number generator.

    Threadsafe.

    Useful for generating consistent random numbers across platforms
    and when multiple threads are involved.

    Uses the Fast Mersenne Twister (FMT-19937) algorithm.

    On average, uniform() runs about 2x-3x faster than rand().

    @cite http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html
    
    On OS X, Random is about 10x faster than drand48() (which is
    threadsafe) and 4x faster than rand() (which is not threadsafe).

    \sa Noise
 */
class Random {
protected:

    /** Constants (important for the algorithm; do not modify) */
    enum {
        N = 624,
        M = 397,
        R = 31,
        U = 11,
        S = 7,
        T = 15,
        L = 18,
        A = 0x9908B0DF,
        B = 0x9D2C5680,
        C = 0xEFC60000};

    /** 
        Prevents multiple overlapping calls to generate(). 
     */
    Spinlock     lock;

    /** State vector (these are the next N values that will be returned) */
    uint32*      state;

    /** Index into state */
    int          index;

    bool         m_threadsafe;

    /** Generate the next N ints, and store them for readback later.
        Called from bits() */
    virtual void generate();

    /** For subclasses.  The void* parameter is just to distinguish this from the
        public constructor.*/
    Random(void*);


private:

    Random& operator=(const Random&) {
        alwaysAssertM(false,
            "There is no copy constructor or assignment operator for Random because you "
            "probably didn't actually want to copy the state--it would "
            "be slow and duplicate the state of a pseudo-random sequence.  Maybe you could "
            "provide arguments to a member variable in the constructor, "
            "or pass the Random by reference?");
        return *this;
    }

    Random(const Random& r) {
        *this = r;
    }

public:

    /** \param threadsafe Set to false if you know that this random
        will only be used on a single thread.  This eliminates the
        lock and improves performance on some platforms.
     */
    Random(uint32 seed = 0xF018A4D2, bool threadsafe = true);

    virtual ~Random();

    virtual void reset(uint32 seed = 0xF018A4D2, bool threadsafe = true);

    /** Each bit is random.  Subclasses can choose to override just 
       this method and the other methods will all work automatically. */
    virtual uint32 bits();

    /** Uniform random integer on the range [min, max] */
    virtual int integer(int min, int max);

    /** Uniform random float on the range [min, max] */
    virtual inline float uniform(float low, float high) {
        // We could compute the ratio in double precision here for
        // about 1.5x slower performance and slightly better
        // precision.
        return low + (high - low) * ((float)bits() / (float)0xFFFFFFFFUL);
    }

    /** Uniform random float on the range [0, 1] */
    virtual inline float uniform() {
        // We could compute the ratio in double precision here for
        // about 1.5x slower performance and slightly better
        // precision.
        const float norm = 1.0f / (float)0xFFFFFFFFUL;
        return (float)bits() * norm;
    }

    /** Normally distributed reals. */
    virtual float gaussian(float mean, float stdev);

    /** Returns 3D unit vectors distributed according to 
        a cosine distribution about the positive z-axis. */
    virtual void cosHemi(float& x, float& y, float& z);

    /** Returns 3D unit vectors distributed according to 
        a cosine distribution about the z-axis. */
    virtual void cosSphere(float& x, float& y, float& z);

    /** Returns 3D unit vectors distributed according to a cosine
        power distribution (\f$ \cos^k \theta \f$) about
        the z-axis. */
    virtual void cosPowHemi(const float k, float& x, float& y, float& z);

    /** Returns 3D unit vectors uniformly distributed on the 
        hemisphere about the z-axis. */
    virtual void hemi(float& x, float& y, float& z);

    /** Returns 3D unit vectors uniformly distributed on the sphere */
    virtual void sphere(float& x, float& y, float& z);

    /**
       A shared instance for when the performance and features but not
       consistency of the class are desired.  It is slightly (10%)
       faster to use a distinct instance than to use the common one.
       
       Threadsafe.
    */
    static Random& common();
};

}

#endif
