/**
 * =========================================================================
 * File        : rand.cpp
 * Project     : 0 A.D.
 * Description : pseudorandom number generator
 * =========================================================================
 */

// license: GPL; see lib/license.txt

#include "precompiled.h"
#include "rand.h"

// avoids several common pitfalls; see discussion at
// http://www.azillionmonkeys.com/qed/random.html

// rand() is poorly implemented (e.g. in VC7) and only returns < 16 bits;
// double that amount by concatenating 2 random numbers.
// this is not to fix poor rand() randomness - the number returned will be
// folded down to a much smaller interval anyway. instead, a larger XRAND_MAX
// decreases the probability of having to repeat the loop.
#if RAND_MAX < 65536
static const uint XRAND_MAX = (RAND_MAX+1)*(RAND_MAX+1) -1;
static uint xrand()
{
    return rand()*(RAND_MAX+1) + rand();
}
// rand() is already ok; no need to do anything.
#else
static const uint XRAND_MAX = RAND_MAX;
static uint xrand()
{
    return rand();
}
#endif

uint rand(uint min_inclusive, uint max_exclusive)
{
    const uint range = (max_exclusive-min_inclusive);
    // huge interval or min >= max
    if(range == 0 || range > XRAND_MAX)
    {
        WARN_ERR(ERR::INVALID_PARAM);
        return 0;
    }

    const uint inv_range = XRAND_MAX / range;

    // generate random number in [0, range)
    // idea: avoid skewed distributions when <range> doesn't evenly divide
    // XRAND_MAX by simply discarding values in the "remainder".
    // not expected to run often since XRAND_MAX is large.
    uint x;
    do
    x = xrand();
    while(x >= range * inv_range);
    x /= inv_range;

    x += min_inclusive;
    debug_assert(x < max_exclusive);
    return x;
}