#import "Random.h"
#import <appkit/nextstd.h>
#import <limits.h>

/* External Function Declarations */
#define RAND_NORM ((float) LONG_MAX +1.0)
#define UnitRandom ((float)random() / RAND_NORM)

extern long random();
extern void srandom(int seed);
extern char *initstate(unsigned seed, char *state, int n);
extern char *setstate(char *state);

#define RANTYPE_FLAT 0
#define RANTYPE_EXP 1
#define RANTYPE_GAUSS 2

static float gaussValue()
{
    static BOOL saved = NO;
    static float extraG;
    float       fac, r, v1, v2;
    if (!saved) {
	do {
	    v1 = 2.0 * UnitRandom - 1.0;
	    v2 = 2.0 * UnitRandom - 1.0;
	    r = v1 * v1 + v2 * v2;
	} while (r >= 1.0);
				/* if (r == 0.0) r = 0.000001; */
	fac = sqrt( -2.0 * log(r) / r);
	extraG = v1 * fac;
	saved = YES;
	return (v2 * fac);
    } else {
	saved = NO;
	return (extraG);
    }
}

@implementation Random

/* Creating and freeing a Random object */

+ initialize
{
    srandom(time(0));		/* Seed Random Number Generator */
    return self;
}

+ new
{
    return [self newCount:16];
}

+ newCount:(unsigned)numSlots		/* Number of bytes of State data */
{
    static char *old;
    static char *new;
    int size;
    size = (numSlots < 8) ? 8 : numSlots;
    NX_MALLOC(new,char,size);
    old = initstate(time(0),new,size);
    self = [super new];
    range = 1.0;
    return self;
}

- seed				/* Will generate a new sequence */
{
    srandom(time(0));
    return self;
}

- seedFixed /* Will generate a fixed sequence */
{
    srandom(1);
    return self;
}

- (int)integer {
    return random();
}

- (float) unit {		/* Between 0 and 1 */
    switch(type) {
      case RANTYPE_EXP: return -log(UnitRandom);
      case RANTYPE_GAUSS: return gaussValue();
      case RANTYPE_FLAT: 
      default: return UnitRandom;
    }
}

- (float)scaleFrom:(float)nLow to:(float)nHigh
{
    low = nLow;
    range = nHigh - nLow;
    return [self flat];
}

- (float)scaleMean:(float)nMean stdDev:(float)nDev
{
    low = nMean;
    range = nDev;
    return [self gauss];
}

- (float)number
{
    return low + [self unit] * range;
}

- (int)whole
{
    return (int)floor([self number]);
}

- (float)flat
{
    type = RANTYPE_FLAT;
    return [self number];
}

- (float)exp
{
    type = RANTYPE_EXP;
    return [self number];
}

- (float)gauss
{
    type = RANTYPE_GAUSS;
    return [self number];
}

- (int)partition:(int)numTotal part:(int)num of:(int)denom
{
    range = numTotal / (denom ? denom : 1);
    low = num * range;
    return [self whole];
}

@end
