//
// Random
//
// Copyright (C) 1992 Contemporary Design Studios. All rights reserved.
//


#import "Random.h"
#import <math.h>
#import <stdlib.h>
#import <stdio.h>


@implementation Random


//
// version
//

+ (int)version
{
    return 3;				// Version 3 = Release 2.0.
}

//
// initEngineClass:
//

- initEngineClass:aClass
{
    return [self initEngineInstance:[[aClass alloc] init]];
}


//
// initEngineInstance:
//
// DESIGNATED INITIALIZER
//

- initEngineInstance:anObject
{
    [super init];

    if(![anObject isKindOf:[RandomEngine class]]) {
        //
	// Can't do that!
	//
    }
    
    engine	= anObject;
    engineClass	= [engine class];
    unit	= [engineClass unit];
    
    bitbuffer	= (uchar *)malloc(unit);
    bytebuffer	= (uchar *)malloc(unit);
    curbit	= 0;
    curbyte	= 0;
    
    [engine makeRandom:bitbuffer];			// Make some bits.
    [engine makeRandom:bytebuffer];			// Make some bytes.
    
    return self;
}


//
// rand
//

- (ulong) rand
{
    ulong	temp = 0;
    char	*ptr = (char *)(&temp);
    ulong	i;
    
    for(i = 0; i < 4; i++) {
        if(curbyte == unit) {				// i.e., bytebuffer empty.
	    [engine makeRandom:bytebuffer];
	    curbyte = 0;
	}
	ptr[i] = bytebuffer[curbyte];
	curbyte++;
    }
    
    return temp;
}


//
// randMax:
//
// This isn't the most conservative algorithm possible. One could rotate bits and/or
// bytes to try to come up with a value in range. However, the added complexity could
// bog down the code even further. Therefore, until somebody proves that being more
// conservative is useful, or a really clever, yet clear, algorithm presents itself,
// this will suffice. Execution should be reasonably fast, and usage of random bits
// and bytes should be reasonably light with this algorithm.
//

- (ulong) randMax:(ulong)max
{
    ulong	ret		= 0;
    ulong	*ptr		= &ret;
    int		i;
    
    if(max == 0) {
        ret = 0;
    }
    else if(max == 1) {						// One bit needed.
	if(curbit == (unit * 8)) {
	    [engine makeRandom:bitbuffer];
	    curbit = 0;
	}
    
	ret = bitbuffer[curbit / 8] & (0x01 << (curbit % 8));
	curbit++;
    }
    else if(max <= 0x000000ff) {				// One byte needed.
        do {
	    if(curbyte == unit) {
		[engine makeRandom:bytebuffer];
		curbyte = 0;
	    }
	    ret = bytebuffer[curbyte];
	    curbyte++;
	} while (ret > max);
    }
    else if(max <= 0x0000ffff) {				// Two bytes needed.
        do {
	    for(i = 0; i < 2; i++) {
		if(curbyte == unit) {
		    [engine makeRandom:bytebuffer];
		    curbyte = 0;
		}
		ptr[i + 2] = bytebuffer[curbyte];
		curbyte++;
	    }
	} while (ret > max);
    }
    else if(max <= 0x00ffffff) {				// Three bytes needed.
        do {
	    for(i = 0; i < 3; i++) {
		if(curbyte == unit) {
		    [engine makeRandom:bytebuffer];
		    curbyte = 0;
		}
		ptr[i + 1] = bytebuffer[curbyte];
		curbyte++;
	    }
	} while (ret > max);
    } 
    else {							// Four bytes needed.
        do {
	    for(i = 0; i < 4; i++) {
		if(curbyte == unit) {
		    [engine makeRandom:bytebuffer];
		    curbyte = 0;
		}
		ptr[i] = bytebuffer[curbyte];
		curbyte++;
	    }
	} while (ret > max);
    }
    
    return ret;
}


//
// randMin:max:
//

- (ulong)randMin:(ulong)min max:(ulong)max
{
    return min + [self randMax:(max - min)];
}


//
// percent
//
// The information in the following exerpt was used to determine the
// construction of a double-precision number with a value between 0.0 and
// 1.0. It turns out to be easiest to construct one with a mantissa of:
//
// 1.rrrrrrrrrr rrrrrrrrrr rrrrrrrrrr rrrrrrrrrr rrrrrrrrrr rr
// 
// Where "r" stands for a random bit, and where the exponent is 0, i.e.
// E is 1023, or 01111111111. Of course, the sign bit is 0. So, the
// resulting number is:
//
// 00111111 1111rrrr rrrrrrrr rrrrrrrr rrrrrrrr rrrrrrrr rrrrrrrr rrrrrrrr
//
// Nicely enough, there is only 1 byte which is a mix of constant and
// random bits. So, the trick is to generate 7 bytes of random data, and
// then to overlay the constant 1111 into the high-order nibble of the
// 2nd byte. Then, the first byte is just the constant 00111111.
//
// Of course, this is technically platform dependant, which is a no-no,
// but I suppose if we move it to another machine, there may be a function
// to convert from XDR format (which this is) to the local format (one can
// hope), which would save us.
//
// -------------------------------- EXERPT --------------------------------
// 
// Network Working Group                             Sun Microsystems, Inc.
// Request for Comments: 1014                                     June 1987
// 
// 
//                XDR: External Data Representation Standard
// 
// ------------------------------------------------------------------------
// 
// 3.7 Double-precision Floating-point
// 
//    The standard defines the encoding for the double-precision floating-
//    point data type "double" (64 bits or 8 bytes).  The encoding used is
//    the IEEE standard for normalized double-precision floating-point
//    numbers [3].  The standard encodes the following three fields, which
//    describe the double-precision floating-point number:
// 
//       S: The sign of the number.  Values 0 and 1 represent positive and
//          negative, respectively.  One bit.
// 
//       E: The exponent of the number, base 2.  11 bits are devoted to
//          this field.  The exponent is biased by 1023.
// 
//       F: The fractional part of the number's mantissa, base 2.  52 bits
//          are devoted to this field.
// 
//    Therefore, the floating-point number is described by:
// 
//          (-1)**S * 2**(E-Bias) * 1.F
// 
//    It is declared as follows:
// 
//          double identifier;
// 
//          +------+------+------+------+------+------+------+------+
//          |byte 0|byte 1|byte 2|byte 3|byte 4|byte 5|byte 6|byte 7|
//          S|    E   |                    F                        |
//          +------+------+------+------+------+------+------+------+
//          1|<--11-->|<-----------------52 bits------------------->|
//          <-----------------------64 bits------------------------->
//                                         DOUBLE-PRECISION FLOATING-POINT
// 
//    Just as the most and least significant bytes of a number are 0 and 3,
//    the most and least significant bits of a double-precision floating-
//    point number are 0 and 63.  The beginning bit (and most significant
//    bit) offsets of S, E , and F are 0, 1, and 12, respectively.  Note
//    that these numbers refer to the mathematical positions of the bits,
//    and NOT to their actual physical locations (which vary from medium to
//    medium).
// 
//    The IEEE specifications should be consulted concerning the encoding
//    for signed zero, signed infinity (overflow), and denormalized numbers
//    (underflow) [3].  According to IEEE specifications, the "NaN" (not a
//    number) is system dependent and should not be used externally.
// 
// ------------------------------------------------------------------------
// 
//    [3]  "IEEE Standard for Binary Floating-Point Arithmetic", ANSI/IEEE
//         Standard 754-1985, Institute of Electrical and Electronics
//         Engineers, August 1985.
// 
// ------------------------------------------------------------------------
// 

- (double)percent
{
    double	temp;
    int		i;
    
    for(i = 1; i < 8; i++) {
        if(curbyte == unit) {				// i.e., bytebuffer empty.
	    [engine makeRandom:bytebuffer];
	    curbyte = 0;
	}
	((uchar *)(&temp))[i] = bytebuffer[curbyte];
	curbyte++;
    }
    
    ((ulong *)(&temp))[0] &= ((ulong)0x000fffff);
    ((ulong *)(&temp))[0] |= ((ulong)0x3ff00000);
    
    return (temp - 1.0);
}


//
// bool
//

- (BOOL)bool
{
    BOOL	temp;
    
    if(curbit == (unit * 8)) {				// i.e., bitbuffer empty.
	[engine makeRandom:bitbuffer];
	curbit = 0;
    }
    
    temp = bitbuffer[curbit / 8] & (0x01 << (curbit % 8));
    curbit++;
	    
    return (temp != 0);
}


//
// randFunc:
//

- (double)randFunc:(ddfunc)func
{
    return (*func)([self percent]);
}


//
// read:
//

- read:(NXTypedStream *)stream
{
    [super read:stream];
    
    //
    // Read in the engine:
    //
    
    NXReadTypes(stream, "@", &engine);
    
    //
    // Set related instance variables:
    //
    
    engineClass	= [engine class];
    unit	= [engineClass unit];
    bitbuffer	= (uchar *)malloc(unit);
    bytebuffer	= (uchar *)malloc(unit);
    curbit	= 0;
    curbyte	= 0;
    
    //
    // Read in the buffers:
    //
    
    [engine makeRandom:bitbuffer];			// Make some bits.
    [engine makeRandom:bytebuffer];			// Make some bytes.
    
    return self;
}


//
// write:
//

- write:(NXTypedStream *)stream
{
    [super write:stream];
    
    //
    // Write out the engine:
    //
    
    NXWriteTypes(stream, "@", &engine);
    
    //
    // Write out the buffers:
    //
    
    return self;
}


@end


//
// End of file.
//