/******************************************************************************
 * $LastChangedDate: 2010-09-16 11:23:03 -0400 (Thu, 16 Sep 2010) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Random number generator (sudo/real).
 *//*
 * LEGAL:   COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 ******************************************************************************/

#ifndef BASE_RANDOM_HH
#define BASE_RANDOM_HH 1

namespace base {

////////////////////////////////////////////////////////////////////////////////
/// @brief Random number generator (sudo/real).
///
/// Usage:
/// ------
/// Program startup should call randomize().
///
/// Generates the same sequence of random numbers on any system:
/// ------------------------------------------------------------
/// This implementation, when given the same seed, should produce
/// the same sequences of numbers on any system.
/// The implementation of random_ui() uses 32-bit integers specifically.
///
/// Maximum random number:
/// ---------------------
/// BASE_RANDOM_MAX defines the maximum random number that can be generated.
/// Do not assume it is 0xffffffff.
///
class Random
{
PREVENT_COPYING(Random)
public:
#define BASE_RANDOM_MAX            (0x7fffffffU)  // defined by random_ui() implementation
#define BASE_RANDOM_MAX_INVERSE_FP (1.0f / fp(BASE_RANDOM_MAX))

private:
    // ctor/dtor omitted.
    CLASS_METHOD void   Init( void );

public:
    // Still using old naming.
    CLASS_METHOD void   randomize( const uint seed = 0 );  // equivalent to SetSeed()
    CLASS_METHOD uint   random_ui_real( void );
    CLASS_METHOD uint   random_ui( const uint range );
    CLASS_METHOD fp     random_f( void );
    CLASS_METHOD fp     random_f( const fp range );
    CLASS_METHOD bool   random_bool( void ) { return random_ui(100) < 50; }

    // Return seed used to generate the current sequence of numbers.
    // randomize(0) is special as it means to pick a real random seed.
    CLASS_METHOD uint   GetSeed( void ) { ASSERT( msSeedSaved != 0 ); return msSeedSaved; }

    /*****************************************************************************
     * @return A sudo-random unsigned int in range {0,..,BASE_RANDOM_MAX}.
     * This function is supposed to return the same sequence of numbers on any system.
     *****************************************************************************/
    CLASS_METHOD inline
    uint random_ui( void )
    {
        //..........................................................................
        // Compute x[n + 1] = (7^5 * x[n]) mod (2^31 - 1).
        // From "Random number generators: good ones are hard to find",
        // Park and Miller, Communications of the ACM, vol 31, num 10, Oct 1988, pg 1195.
        //..........................................................................
        // Produces random numbers in range {0x0,..,0x7fffffff} (2 billion).
        // 32-bit types are used so the same random numbers are generated on any system.

        if ( UX( msSeed == 0 ) )    // seed will die as 0 (but function can return 0)
            msSeed = 123459876;
        int32 hi = msSeed / 127773;
        int32 lo = msSeed % 127773;
        msSeed = 16807 * lo - 2836 * hi;
        if ( msSeed < 0 )           // msSeed has to be int32
            msSeed += 0x7fffffff;
        return msSeed;              // seed grows into random values
    }

private:
    // Although the interface is in terms of uint, msSeed must be int32
    // because of how the function random_ui() works
    CLASS_VAR bool  msInit;             ///< if Random class was initialized
    CLASS_VAR int32 msSeed;             ///< changed every time random_*() is called
    CLASS_VAR int32 msSeedSaved;        ///< saved seed that was set by randomize()
#if OS_LINUX
    CLASS_VAR int   msDevRandomFd;      ///< file descriptor of /dev/random
    CLASS_VAR int   msDevUrandomFd;     ///< file descriptor of /dev/urandom
#endif
};

} // namespace base

#endif // BASE_RANDOM_HH
