// Swarm library. Copyright (C) 1996 Santa Fe Institute.
//  Swarm library. Copyright (C) 1996 Santa Fe Institute.
// This library is distributed without any warranty; 
// without even the implied warranty of merchantability 
// or fitness for a particular purpose.
// See file LICENSE for details and terms of copying.

/*
Name:            UniformUnsigned.m
Description:     Uniform distribution returning unsigned integer values
Library:         random
Original Author: Sven Thommesen
Date:            1997-01-15
*/

/*
123456789|123456789|123456789|123456789|123456789|123456789|123456789|123456789|
*/

// 
// Methods to generate the next pseudo-random number in a stream:
// 
// // Using the bit generator to create Uniform numbers.
//
// // Bit generators return values in the interval [0,maxValue].
// // For the implemented generators, maxValue is:
// //    PMMLCG:  2,147,483,645 = 2^31 - 3
// //    SWB:     4,294,967,295 = 2^32 - 1
// //    LCG:     4,294,967,295 = 2^32 - 1
// //    ACG:     4,294,967,295 = 2^32 - 1
// //    SCG:       999,999,999 = 10^9 - 1 (29.9 bits)
// 


#import <math.h>
#import <collections.h>
#import <random/UniformUnsigned.h>

@implementation UniformUnsigned

// data struct used by getState and setState:
//
typedef struct {
   // Object identification:
   unsigned magic;
   // unsigned generator_magic;	// later
   // ProbabilityDistribution variables:
   unsigned stateSize;
   BOOL optionsInitialized;
   unsigned currentCount;
   unsigned generatorMax;
   double maxDivisor;
   double duuMask;
   // Distribution specific data:
   unsigned uMin;
   unsigned uMax;
   unsigned uRange;
   unsigned uCutoff;
} state_struct_t;

// ----- internal methods -----

-resetState {

// Called by setGenerator in the superclass
// and by setMax:Min: below.

   currentCount = 0;

   return self;
}

-initState {
   UnsignedOverlayDouble duuTemp;
   // volatile double x;
   // double y;

// Called by createBegin in the superclass.

   stateSize = sizeof(state_struct_t);

   uMin   = 0;
   uMax   = 0;
   uRange   = 0;
   uCutoff = 0;
   currentCount = 0;

// State variables are allocated as instance variables 
// and initialized in resetState above.

// ----------------------------

   // Now for the libg++ magic:

   if ( sizeof(double) != 2*sizeof(unsigned) )
   [InvalidCombination raiseEvent:
   " double <> 2 unsigneds: math will fail!!! \n"];

// #if_IEEE == 1
// #ifdef _IEEE

   //   printf(" initState: IEEE is defined \n");
   duuTemp.d = 1.5;
   if (duuTemp.u[1] == 0) {	// sun word order?
     //     printf(" initState: sun word order \n");
     duuTemp.u[0] = 0x3fffffff;
     duuTemp.u[1] = 0xffffffff;
   } else {			// encore word order?
     //printf(" initState: encore word order \n");
     duuTemp.u[0] = 0xffffffff;
     duuTemp.u[1] = 0x3fffffff;
   }

/*
#else IEEE

   printf(" initState: IEEE is *not* defined \n");

   x = 1.0;
   y = 0.5;

   do {
     duuTemp.d = x;
     x += y;
     y *= 0.5;
   } while ( x != duuTemp.d && x < 2.0);

#endif IEEE
*/

   duuMask.d = 1.0;
   duuMask.u[0] ^= duuTemp.u[0];
   duuMask.u[1] ^= duuTemp.u[1];

// ----------------------------

   return self;
}

// This method returns values in the interval [0.0,1.0):

-(double) rDouble {
  double rdValue;
  unsigned bitValue;

  bitValue = [randomGenerator getUnsignedSample];
  rdValue = (double) (bitValue / maxDivisor);

  return rdValue;
}

// This method returns values in the interval (0.0,1.0):

-(double) nzrDouble {
  double rdValue;
  unsigned bitValue;

  do { bitValue = [randomGenerator getUnsignedSample]; }
  while (bitValue == 0);		// So we don't return 0.0

  rdValue = (double) (bitValue / maxDivisor);

  return rdValue;
}

// This method fills each double with two successive generator 
// values (64 bits). It will never return 0.0.

-(double) duuDouble {
   unsigned bitValue0, bitValue1;
   UnsignedOverlayDouble duuValue;

   bitValue0 = [randomGenerator getUnsignedSample];
   bitValue1 = [randomGenerator getUnsignedSample];

//   printf(" duuValue: generator inputs were %u and %u \n", 
//	bitValue0, bitValue1);

   duuValue.d = 1.0;
   duuValue.u[0] |= (bitValue0 & duuMask.u[0]);
   duuValue.u[1] |= (bitValue1 & duuMask.u[1]);
   duuValue.d -= 1.0;

   if ((duuValue.d >= 1.0) || (duuValue.d < 0))
   [InvalidCombination raiseEvent:
   "duuDouble: result was not in [0.0,1.0) -- sigh... \n"];

//   printf(" duuValue: d0=%60.50f\n", duuValue.d);

   return duuValue.d;
}

// ----- protocol UniformUnsigned -----

+create: (id) aZone setGenerator: (id) generator
	setUnsignedMin: (unsigned) minValue setMax: (unsigned) maxValue {
   UniformUnsigned * aDistribution;
   unsigned tmpMin, tmpMax;
   unsigned tmpRange, tmpCut;

   if (minValue == maxValue)
   [InvalidCombination raiseEvent:
   "UniformUnsigned: setting minValue = maxValue not allowed!\n"];

   aDistribution = [ super create: aZone setGenerator: generator ];

   // Ensure that uMax > uMin:

   if (maxValue > minValue) {
     tmpMax = maxValue;
     tmpMin = minValue;
   } else {
     tmpMax = minValue;
     tmpMin = maxValue;
   }
   tmpRange = tmpMax - tmpMin;

   if (tmpRange > (aDistribution->generatorMax-1))
    [InvalidCombination raiseEvent:
    "UniformUnsigned: Requested random number with range %u, \n 
     but your generator only supports a range of %u \n", 
     tmpRange, (aDistribution->generatorMax-1)];

   tmpRange = tmpRange + 1;
   tmpCut = aDistribution->generatorMax 
	    - (aDistribution->generatorMax % tmpRange);
   // tmpCut = (aDistribution->generatorMax / tmpRange) * tmpRange;

   aDistribution->uMax    = tmpMax;
   aDistribution->uMin    = tmpMin;
   aDistribution->uRange  = tmpRange;
   aDistribution->uCutoff = tmpCut;

   // This object is now fixed:

   aDistribution->optionsInitialized = YES;

   [ aDistribution resetState ];

   return aDistribution;
}

- (void) setUnsignedMin: (unsigned) minValue setMax: (unsigned) maxValue {

   if (optionsInitialized)
   [InvalidCombination raiseEvent:
   "UniformUnsigned: setting parameters more than once not allowed\n"];

   if (minValue == maxValue)
   [InvalidCombination raiseEvent:
   "UniformUnsigned: setting minValue = maxValue not allowed!\n"];

   // Ensure that uMax > uMin:

   if (maxValue > minValue) {
     uMax = maxValue;
     uMin = minValue;
   } else {
     uMax = minValue;
     uMin = maxValue;
   }
   uRange = uMax - uMin;

   if (uRange > generatorMax-1)
    [InvalidCombination raiseEvent:
    "UniformUnsigned: Requested random number with range %u, \n 
     but your generator only supports a range of %u \n", 
     uRange, generatorMax-1];

   uRange = uRange + 1;
   uCutoff = generatorMax - (generatorMax % uRange);
   // uCutoff = (generatorMax / uRange) * uRange;

   // This object is now fixed:

   optionsInitialized = YES;

   [ self resetState ];

   //   return self;
}

-(unsigned) getUnsignedMin {
   return uMin;
}

-(unsigned) getUnsignedMax {
  return uMax;
}

// ------------------------------------------------

-(unsigned) getUnsignedWithMin: (unsigned)minValue withMax: (unsigned)maxValue {
   unsigned tmpMax, tmpMin;
   unsigned uValue;
   unsigned tmpRange, tmpCut;

   if (optionsInitialized)
   [InvalidCombination raiseEvent:
"UniformUnsigned: getUnsignedWithMin:withMax: options already initialized\n"];

   if (minValue == maxValue)
   [InvalidCombination raiseEvent:
   "UniformUnsigned: setting minValue = maxValue not allowed!\n"];

   // Ensure tmpMax > tmpMin:

   if (maxValue > minValue) {
     tmpMax = maxValue;
     tmpMin = minValue;
   } else {
     tmpMax = minValue;
     tmpMin = maxValue;
   }
   tmpRange = tmpMax - tmpMin;

   if (tmpRange > generatorMax-1)
    [InvalidCombination raiseEvent:
    "UniformUnsigned: Requested random number with range %u, \n 
     but your generator only supports a range of %u \n", 
     tmpRange, generatorMax-1];

   tmpRange = tmpRange + 1;
   tmpCut = generatorMax - (generatorMax % tmpRange);
   // tmpCut = (generatorMax / tmpRange) * tmpRange;

   currentCount++ ;

// uCutoff is set to be a multiple of uRange
// to ensure an unbiased uniform variable.
// Suggested by Barry McMullin.

   do {
     uValue = [randomGenerator getUnsignedSample];
   } while (uValue >= tmpCut);

   uValue = (uValue % tmpRange) + tmpMin;

   return uValue;

}

// ----- protocol UnsignedDistribution -----

// Return unsigned integer value in range [min,max] (inclusive).

-(unsigned) getUnsignedSample {
   unsigned uValue;

   if (!optionsInitialized)
   [InvalidCombination raiseEvent:
   "UniformUnsigned: getUnsignedSample: options have not been initialized\n"];

   currentCount++ ;

// uCutoff is set to be a multiple of uRange
// to ensure an unbiased uniform variable.
// Suggested by Barry McMullin.

   do {
     uValue = [randomGenerator getUnsignedSample];
   } while (uValue >= uCutoff);

   uValue = (uValue % uRange) + uMin;

   return uValue;

}

-(unsigned) getAlternateUnsignedSample {
   double dValue;
   unsigned uValue;

   // dValue = [self rDouble];		// double in range [0.0,1.0)
   dValue = [self duuDouble];		// double in range [0.0,1.0)

   // Set elsewhere: uRange = (uMax-uMin+1)

   dValue = dValue*uRange + uMin;	// double in range [uMin,uMax+1)

   uValue = (unsigned) floor (dValue);	// integer in range [uMin,uMax]

   return uValue;
}

// ----- protocol InternalState -----

-(void) getState: (void *) stateBuf {		// override superclass method
   state_struct_t * internalState;
   // unsigned generator_magic;

  // obtain my generator's magic number:
  // (no way to do that yet)

  // recast the caller's pointer:
  internalState = (state_struct_t *) stateBuf;

  // fill the caller's buffer with state data:
  // object identification:
  internalState->magic = UNIFORMUNSIGNEDMAGIC;
  // internalState->generator_magic = generator_magic;
  // ProbabilityDistribution data:
  internalState->stateSize = stateSize;
  internalState->optionsInitialized = optionsInitialized;
  internalState->currentCount = currentCount;
  internalState->generatorMax = generatorMax;
  internalState->maxDivisor = maxDivisor;
  internalState->duuMask = duuMask.d;
  // distribution specific data:
  internalState->uMin = uMin;
  internalState->uMax = uMax;
  internalState->uRange = uRange;
  internalState->uCutoff = uCutoff;

  // nothing is returned from a (void) function

}

-(void) setState: (void *) stateBuf {		// override superclass method
   state_struct_t * internalState;

  // obtain my generator's magic number:
  // (no way to do that yet)

  // recast the caller's pointer:
  internalState = (state_struct_t *) stateBuf;

  // TEST the integrity of the external data:
  if (    (internalState->magic     != UNIFORMUNSIGNEDMAGIC)
       || (internalState->stateSize != stateSize)
//     || (internalState->generator_magic != generator_magic)
     )
  [InvalidCombination raiseEvent:
  "UniformDouble: you are passing bad data to setState!\n %u %u\n",
   internalState->magic, internalState->stateSize];

  // set internal state from data in caller's buffer:
  // ProbabilityDistribution data:
    // stateSize = internalState->stateSize;
  optionsInitialized = internalState->optionsInitialized;
  currentCount = internalState->currentCount;
    // generatorMax = internalState->generatorMax;
    // maxDivisor = internalState->maxDivisor;
    // duuMask.d = internalState->duuMask;
  // distribution specific data:
  uMin   = internalState->uMin;
  uMax   = internalState->uMax;
  uRange   = internalState->uRange;
  uCutoff = internalState->uCutoff;

  // nothing is returned from a (void) function

}

// ----- temporary methods -----

- (void) describe: outStream {
  char buffer[200];

  (void)sprintf(buffer," UniformUnsigned printSelf: \n");
  (void)sprintf(buffer,"      uMin = %u\n", uMin);
  (void)sprintf(buffer,"      uMax = %u\n", uMax);
  (void)sprintf(buffer,"    uRange = %u\n", uRange);
  (void)sprintf(buffer,"   uCutoff = %u\n", uCutoff);

  [ super describe: outStream ];

  [outStream catC: buffer];
  [outStream catC: "\n"];

  //  return self;
}

-(int) verifySelf {

   return 1;
}

@end
