/* Copyright (C) Kimmo Kulovesi 1997, 1998

    The R250 engine first written by W. L. Maier, as described by
    S. Kirkpatrick and E. Stoll, Journal of Computational Physics,
    40, p. 517 (1981). Modified by me.

                    *  Use and modify this at will, but please do not remove
                       the names of any authors from the SOURCES, and mark all
                       the changes you have made to the sources.
                       E-mail me at "arkhan@softhome.net" for... anything. ;>

    Init_Dice(seed)           initializes the random number generator with
                              an unsigned long seed (time(NULL) for example)

    RollDice(number, sides)   returns between number  and  sides * number
    RollDice_B(number, sides)
    RollDie_B(sides)

        RollDice takes unsigned ints, and returns unsigned long. If sides is 0,
    will _crash_ for dividing by zero.

        RollDice_B takes signed ints, returns signed long. If sides is 0,
    returns 0, if below 0, subtracts from total instead of adding. A bit
    slower because of the checks, but unnoticeably for most purposes.

        RollDie_B is a version of RollDice_B optimized for use with just one
    dice. RollDie is defined as a macro in the header ("a_dice.h").

Also:

    r250()                    returns between 0 and 65536, 35% faster than
                              rand(). Works as a base for the RollDice:s.

    RDice(number, sides)      Stripped down versions. They work in the same way
    RDice_B(number, sides)    as the normal versions, but are FASTER, at the
    RDie_B(sides)             cost of being ever-so-slightly less random.

-----------------------------------------------------------------------------*/
unsigned int  r250();
void          Init_Dice(unsigned long seed);
unsigned long RollDice  (const register unsigned int num_dice, const register unsigned int num_sides);
long          RollDice_B(const register int num_dice, const int register num_sides);
int           RollDie_B (const register int num_sides);
unsigned long RDice     (const register unsigned int num_dice, const register unsigned int num_sides);
long          RDice_B   (const register int num_dice, const register int num_sides);
int           RDie_B    (const register int num_sides);

    // The RollDice:s and Init_Dice are at the bottom of this file.

/******************************************************************************
 * R250 Pseudorandom Number Engine, written by W. L. Maier.                   *

    Based on:

    S. Kirkpatrick and E. Stoll, Journal of Computational Physics,
    40, p. 517 (1981).

    Modified and optimized for this purpose by me (Kimmo Kulovesi). Most
    changes marked with "Arkhan: (blahblahblah)".
 *                                                                           *
 *****************************************************************************/

int r250_index = -1;         // Arkhan: Begin from -1, since it is
                             //         now incremented BEFORE use
unsigned int r250_buffer[250] =
{
	15301,57764,10921,56345,19316,43154,54727,49252,32360,49582,
	26124,25833,34404,11030,26232,13965,16051,63635,55860,5184,
	15931,39782,16845,11371,38624,10328,9139,1684,48668,59388,
	13297,1364,56028,15687,63279,27771,5277,44628,31973,46977,
	16327,23408,36065,52272,33610,61549,58364,3472,21367,56357,
	56345,54035,7712,55884,39774,10241,50164,47995,1718,46887,
	47892,6010,29575,54972,30458,21966,54449,10387,4492,644,
	57031,41607,61820,54588,40849,54052,59875,43128,50370,44691,
	286,12071,3574,61384,15592,45677,9711,23022,35256,45493,
	48913,146,9053,5881,36635,43280,53464,8529,34344,64955,
	38266,12730,101,16208,12607,58921,22036,8221,31337,11984,
	20290,26734,19552,48,31940,43448,34762,53344,60664,12809,
	57318,17436,44730,19375,30,17425,14117,5416,23853,55783,
	57995,32074,26526,2192,11447,11,53446,35152,64610,64883,
	26899,25357,7667,3577,39414,51161,4,58427,57342,58557,
	53233,1066,29237,36808,19370,17493,37568,3,61468,38876,
	17586,64937,21716,56472,58160,44955,55221,63880,1,32200,
	62066,22911,24090,10438,40783,36364,14999,2489,43284,9898,
	39612,9245,593,34857,41054,30162,65497,53340,27209,45417,
	37497,4612,58397,52910,56313,62716,22377,40310,15190,34471,
	64005,18090,11326,50839,62901,59284,5580,15231,9467,13161,
	58500,7259,317,50968,2962,23006,32280,6994,18751,5148,
	52739,49370,51892,18552,52264,54031,2804,17360,1919,19639,
	2323,9448,43821,11022,45500,31509,49180,35598,38883,19754,
	987,11521,55494,38056,20664,2629,50986,31009,54043,59743
};

unsigned int r250()
{
    register int i;
    register unsigned int rnum;

        // Arkhan: I optimized this a bit by checking for >= 249 only if we
        //         already know it is >= 147...

    if (r250_index >= 147)
    {
        if (r250_index >= 249)
            r250_index = 0;
        else
            r250_index++;

        i = r250_index - 147;      // Wrap pointer around
    }
    else
    {
        r250_index++;
        i = r250_index + 103;
    }

    rnum = r250_buffer[r250_index] ^= r250_buffer[i];

    return rnum;
};

void Init_Dice(unsigned long seed)
{
    int i, j;
    unsigned int msb, mask;

    r250_index = -1;

        // Arkhan: I combined the two loops into one, since I saw no reason
        //         why the numbers should be looped through twice... *shrug*

    for (i = 0; i < 250; i++)     // Fill the r250 buffer with 15-bit values
    {
        seed = seed * 0x015a4e35L + 1;
        r250_buffer[i] = ((seed>>16) & 0x7fff);

        seed = seed * 0x015a4e35L + 1;
        if (((seed>>16) & 0x7fff) > 16384)
            r250_buffer[i] |= 0x8000;   // Set some of the MS bits to 1
    }

    msb = 0x8000;       /* To turn on the diagonal bit   */
    mask = 0xffff;      /* To turn off the leftmost bits */

    for (i = 0; i < 16; i++)
    {
        j = 11 * i + 3;             /* Select a word to operate on        */
        r250_buffer[j] &= mask;     /* Turn off bits left of the diagonal */
        r250_buffer[j] |= msb;      /* Turn on the diagonal bit           */
        mask >>= 1;
        msb >>= 1;
    }
};

/*****************************************************************************
 * The Dice Rolling Simulator, Copyright (C) Kimmo Kulovesi 1997, 1998       *

   Simulates the rolling of dice, using an advanced random number generation,
   allowing for more "random" results, than with the "1 + rand() % X" syntax.
 *                                                                           *
 *****************************************************************************/

// ------
// ------ The "standard" functions for die rolling ----------------------------
// ------

unsigned long RollDice(const register unsigned int num_dice, const register unsigned int num_sides)
{
    register int i;
    register unsigned long result = 0;

    for (i = num_dice; i; i--)
        result += (((r250() / 211) & 0777777) % num_sides) + 1;

    return result;
};

long RollDice_B(const register int num_dice, const register int num_sides)
{
    register int i;
    register unsigned long result = 0;

    if (num_sides == 0 || num_dice <= 0)
        return 0;
    else if (num_sides > 0)
    {
        for (i = num_dice; i; i--)
            result += (((r250() / 211) & 0777777) % num_sides) + 1;
    }
    else
    {
        for (i = num_dice; i; i--)
            result -= (((r250() / 211) & 0777777) % -num_sides) + 1;
    }

    return result;
};

int RollDie_B(const register int num_sides)
{
    if (!num_sides)
        return 0;
    else if (num_sides > 0)
        return (((r250() / 211) & 0777777) % num_sides) + 1;
    else
        return -((((r250() / 211) & 0777777) % -num_sides) + 1);
};

// ------
// ------ Faster versions of the above, at a slight cost in "randomness" ------
// ------

unsigned long RDice(const register unsigned int num_dice, const register unsigned int num_sides)
{
    register int i;
    register unsigned long result = 0;

    for (i = num_dice; i; i--)
        result += 1 + r250() % num_sides;

    return result;
};

long RDice_B(const register int num_dice, const register int num_sides)
{
    register int i;
    register unsigned long result = 0;

    if (num_sides == 0 || num_dice <= 0)
        return 0;
    else if (num_sides > 0)
    {
        for (i = num_dice; i; i--)
            result += 1 + r250() % num_sides;
    }
    else
    {
        for (i = num_dice; i; i--)
            result -= 1 + r250() % (-num_sides);
    }

    return result;
};

int RDie_B(const register int num_sides)
{
    if (!num_sides)
        return 0;
    else if (num_sides > 0)
        return (1 + (r250() % num_sides));
    else
        return -(1 + (r250() % -num_sides));
};
