// random.C: random-number facilities for the CSC 270 simulation example
// -- J. Clarke, March-June 1996

#include <limits.h>
#include "random.h"

// This file defines separate streams of random numbers to be used
// to ensure independent features of the "world" are modelled
// independently in simulation programs. Unfortunately Standard C
// only provides one random-number generating function, rand().
// This function does not allow (as far as I can see) independent
// streams; the best this module can do if rand() must be used is
// to make your program *look* as if there are real separate streams.
//
// However, the rand48() facility, while not part of Standard C, is
// a useful extension available at CDF, and it allows construction
// of independent streams. To select the rand48 package, make sure
// following #define is in effect; to unselect it (and use rand()),
// just comment it out.

#define RAND48

// Both rand() and the rand48() facilities are defined in stdlib.h.
// However, if you tell the compiler you're using Standard C (for
// example, with gcc's -ansi flag), then the rand48 definitions are
// omitted. To include them, __EXTENSIONS__ must be defined, and that's
// the purpose of the following conditional definition. No change is
// needed here if RAND48 is undefined.
//
// WARNING: You really shouldn't mess with compiler controls with
// names beginning "__"!

#ifdef RAND48
#define __EXTENSIONS__
#endif

// And now, at last, we're ready for stdlib!

#include <stdlib.h>

// Initialize the stream of random numbers with the seed S.

void Random::setSeed (unsigned long S)
// CHANGE: Argument type changed from unsigned to unsigned long, July 4/96.
// Pointed out by Angus Stewart.
{
#ifdef RAND48
	xsubi[0] = 0;
	xsubi[1] = (unsigned short) (S >> (CHAR_BIT * sizeof (unsigned short)));
// CHANGE: "CHAR_BIT *" added, July 4/96.
// Pointed out by Angus Stewart.
	xsubi[2] = (unsigned short) S;
#else
	// The standard random-number facility provides only one
	// stream. We will get along with just one stream.
	srand (S);
#endif
}

// Create a stream starting with the seed provided.
// Initialize the stream of random numbers.

Random::Random (unsigned seed)
{
	setSeed (seed);
}

// Create a stream with an arbitrary starting seed.
// Uses 1 as seed, since that's what rand() does.

Random::Random (void)
{
	setSeed (1);
}

// Return the next random number in this stream, unconverted.

long Random::nextRaw (void)
{
#ifdef RAND48
	return nrand48 (xsubi);
#else
	return (long) rand ();
#endif
}

// Returns the next random number in this stream, converted to a double.
// The result is in the range 0 <= result < 1.
// Reference: E. Roberts, "Art & Science of C", p. 274.

double Random::nextDouble (void)
{
#ifdef RAND48
	return erand48 (xsubi);
#else
	return ((double) rand())/((double) RAND_MAX + 1);
#endif
}

// Returns the next random number in this stream, converted to a long.
// The result is in the range lo <= result <= hi.
// Reference: E. Roberts, "Art & Science of C", p. 274.

long Random::nextInt (long lo, long hi)
{
	double d = nextDouble ();
	return lo + (long) (d * (hi - lo + 1));
}

// Randomization is trickier than you'd think. We need something "really
// random" to provide a seed, and the obvious things are the time of day
// and the process number. The "really random" thing must give DIFFERENT
// seeds--that is, unrelated seeds--for each stream that we try to
// randomize.
//
// On a PC, the process number might not mean anything, and on any system,
// the time of day might be the the same from one call to randomize to the
// next, because the time elapsed between calls might be insignificant.
// So we can only have ONE call to the "really random" source, and we have
// to use it to provide many randomizing seeds for different streams.
//
// My solution, about which I'd be happy to hear comments, especially if
// they are convincing improvements, is to have a "randomizing stream."
// On the first call to randomize, this stream is initialized from the
// time of day. Subsequent calls can't just use the value returned by
// the randomizing stream, because then the streams randomized would
// start out simply one step apart in the same sequence. We need to
// use the random values returned to determine some kind of offset,
// rather than using them "raw." I'm picking square root to do that.
//
// I'm not too happy about this, but at least square root is an independent
// pseudo-random process from the one use by rand() and the rand48()'s.
// For now.
//
// Finally, note that this is all much ado about nothing if we're using
// the standard rand() facility, which provides only one stream. In that
// situation, the calling program should make sure to do all its
// randomization of the "different streams" before beginning real use
// of the random numbers.

#include <time.h>
#include <math.h> // for sqrt

// Pick starting seeds out of the circumambient blackness.

void randomize (Random * Stream)
{
	// Randomizer is static, so its seed is only taken from "time" once.

	static Random Randomizer ((long) time ((time_t *) NULL));
	long S = (long) sqrt (Randomizer.nextRaw ());

	Stream -> setSeed (S);
}
