/*===========================================================================
 *
 * File:	DL_Math.CPP
 * Author:	Dave Humphrey (uesp@m0use.net)
 * Created On:	Tuesday, May 08, 2001
 *
 * Contains implementation of math related functions for Dave's Library 
 * of common code.
 *
 *=========================================================================*/

	/* Include Files */
#include "dl_math.h"
#include <time.h>
#include <string.h>
#include <limits.h>
#include <float.h>


/*===========================================================================
 *
 * Begin Local Variable Definitions
 *
 *=========================================================================*/
  DEFINE_FILE();

	/* Variables for custom random number generation */
  ulong RandomSeed = 1;
  ulong RandomAdd  = 1725; 
  ulong RandomMult = 621;

/*===========================================================================
 *		End of Local Variable Definitions
 *=========================================================================*/


/*===========================================================================
 *
 * Begin Global Hexadecimal Value Array
 *
 * Is not a static variable as it is used in the inline function
 * HexCharToInt(),found in dl_math.h.
 *
 *=========================================================================*/
  char HexCharValues[128] = { 
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
	0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };
/*===========================================================================
 *		End of Global Hexadecimal Value Array
 *=========================================================================*/


/*===========================================================================
 *
 * Begin Unit Prefixes Array
 *
 * Static array of numeric unit prefixes used by functions in this 
 * module.
 *
 *=========================================================================*/
  const static int NumUnitPrefixes = 17;

	/* LogBase10, PrefixChar, Prefix */
  unit_prefix_t UnitPrefixes[] = {
	{ -24,		'y',	"yocto" },
	{ -21,		'z',	"zepto" },
	{ -18,		'a',	"atto"  },
	{ -15,		'f',	"femto" },
	{ -12,		'p',	"pico"  },
	{  -9,		'n',	"nano"  },
	{  -6,      MU_CHAR,	"micro" },
	{  -3,		'm',	"milli" },
	{   0,		' ',	""      },
	{   3,		'k',	"kilo"  }, 
	{   6,		'M',	"Mega"  },
	{   9,		'G',	"Giga"  },
	{  12,		'T',	"Tera"  },
	{  15,		'P',	"Peta"  },
	{  18,		'E',	"Exa"   },
	{  21,		'Z',	"Zetta" },
	{  24,		'Y',	"Yotto" }
   };
/*===========================================================================
 *		End of Unit Prefixes Array
 *=========================================================================*/


/*===========================================================================
 *
 * Function - double GetNiceTickLength (AxisStart, AxisEnd, NumTicks);
 *
 * Attempts to compute a 'nice' tick length based on the given axis length.
 * A nice tick length is usually 1, 2, 4, 5, or 10 units.  The number of ticks
 * is used as a first approximate to the number of desired ticks.  The actual
 * number of ticks based on the returned tick length may be different.
 * Modifies the input Start and End Axis values to be 'nice' values as well.
 *
 *=========================================================================*/
double GetNiceTickLength (double& AxisStart, double& AxisEnd, const int NumTicks) {
  //DEFINE_FUNCTION("GetNiceTickLength()");
  double AxisWidth;
  double NewAxisStart;
  double NewAxisEnd;
  double NiceRange;
  double NiceTick;
  
  	/* Check for special cases */
  AxisWidth = AxisEnd - AxisStart;
  if (AxisWidth == 0.0) return (0.0);

	/* Compute the new nice range and ticks */
  NiceRange = NiceNumber(AxisEnd - AxisStart, 0);
  NiceTick = NiceNumber(NiceRange/(NumTicks - 1), 1);

	/* Compute the new nice start and end values */
  NewAxisStart = floor(AxisStart/NiceTick)*NiceTick;
  NewAxisEnd = ceil(AxisEnd/NiceTick)*NiceTick;
  //NumFrac = MAX(-floor(log10(NiceTick)), 0);

  AxisStart = NewAxisStart;
  AxisEnd = NewAxisEnd;
  return (NiceTick);
 }
/*===========================================================================
 *		End of Function GetNiceTickLength()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - unit_prefix_t GetUnitPrefix (OverFlow, Value);
 *
 * Attempt to return unit prefix element which best represents the given
 * value.  Returns a pointer to the prefix record. The OverFlow flag is 
 * set to TRUE if the given value is smaller than the smallest prefix, or
 * larger by a factor of 1000 than the largest prefix.
 *
 *=========================================================================*/
unit_prefix_t* GetUnitPrefix (boolean& OverFlow, const double Value) {
  //DEFINE_FUNCTION("GetUnitPrefix()");
  int LoopCounter;
  int Base10 = 0;

	/* Compute the proper base-10 of input value */
  if (Value != 0.0) Base10 = (int) floor(log10(fabs(Value)));
  OverFlow = FALSE;

	/* Check special small cases */
  if (Base10 < UnitPrefixes[0].LogBase10) {
    OverFlow = TRUE;
    return (&UnitPrefixes[0]);
   }
  
	/* Find the best prefix in the array for the value */
  for (LoopCounter = 1; LoopCounter < NumUnitPrefixes; LoopCounter++) {
    if (Base10 < UnitPrefixes[LoopCounter].LogBase10) return (&UnitPrefixes[LoopCounter-1]);
   }

	/* Use last prefix in array, check overflow case */
  if (Base10 > UnitPrefixes[NumUnitPrefixes-1].LogBase10 + 3) OverFlow = TRUE;
  return (&UnitPrefixes[NumUnitPrefixes-1]);
 }
/*===========================================================================
 *		End of Function GetUnitPrefix()
 *=========================================================================*/



/*===========================================================================
 *
 * Function - char* Metricize (Value, pUnits);
 *
 * Converts a metric type value to a string with the given units using
 * the best possible representation.  For instance:
 *	Metricize(1.23,    "\m") = "1.23 m"
 *	Metricize(0.00123, "\m") = "123 mm"
 *	Metricize(1230,    "\m") = "1.23 km"
 * Returns a pointer to the string buffer.  Uses a static buffer to
 * generate the results.
 *
 *=========================================================================*/
char* Metricize (const double Value, const char* pUnits) {
  DEFINE_FUNCTION("Metricize()");
  unit_prefix_t* pUnitPrefix;
  static char    OutputBuffer[256];
  int            Result;
  boolean        OverFlow;

	/* Ensure valid input */
  ASSERT(pUnits != NULL);

	/* Get the appropiate unit prefix structure */
  pUnitPrefix = GetUnitPrefix(OverFlow, Value);

	/* Output the modified value to the string */
  if (OverFlow)
    Result = sprintf (OutputBuffer, "%g %c%s", Value/exp10(pUnitPrefix->LogBase10), pUnitPrefix->PrefixChar, pUnits);
  else if (pUnitPrefix->LogBase10 == 0)
    Result = sprintf (OutputBuffer, "%3g %s",  Value/exp10(pUnitPrefix->LogBase10), pUnits);
  else
    Result = sprintf (OutputBuffer, "%3g %c%s",  Value/exp10(pUnitPrefix->LogBase10), pUnitPrefix->PrefixChar, pUnits);

	/* Check for errors */
  if (Result < 0) {
    ErrorHandler.AddError(ERR_SYSTEM, errno, "Error creating string buffer!");
    return (OutputBuffer);
   }

  return (OutputBuffer);
 }
/*===========================================================================
 *		End of Function Metricize()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - double NiceNumber (Value, Round);
 *
 * Taken from Graphics Gems, Volume 1 
 * (Heckbert, Paul S., Nice Numbers for Graph Labels, p. 61-63, code: p. 657-659).
 * Find a "nice" number approximately equal to Value. Round the number if 
 * Round=1, take ceiling if Round=0.
 *
 *=========================================================================*/
double NiceNumber (const double Value, const int Round) {
  int    Exponent;		/* Exponent of x */
  double Fraction;		/* Fractional part of x */
  double NiceFraction;		/* Nice, rounded fraction */

  Exponent = (int) floor(log10(Value));
  Fraction = Value/pow10((double)Exponent);	/* between 1 and 10 */

  if (Round) {
    if (Fraction < 1.5) 
      NiceFraction = 1.0;
    else if (Fraction < 3.0)
      NiceFraction = 2.0;
    else if (Fraction < 7.0)
      NiceFraction = 5.0;
    else
      NiceFraction = 10.0;
   }
  else {
    if (Fraction <= 1.0)
      NiceFraction = 1.0;
    else if (Fraction <= 2.0)
      NiceFraction = 2.0;
    else if (Fraction <= 5.0)
      NiceFraction = 5.0;
    else
      NiceFraction = 10.0;
   }

  return NiceFraction*pow10((double)Exponent);
 }
/*===========================================================================
 *		End of Function NiceNumber()
 *=========================================================================*/
  

/*===========================================================================
 *
 * Function - ulong Random (void);
 *
 * Custom random number generator.  Returns the next pseudo-random unsigned
 * long integer in the sequence (0 to ULONG_MAX).
 *
 *=========================================================================*/
ulong Random (void) {
  //DEFINE_FUNCTION("Random()");
  RandomSeed *= RandomMult;
  RandomSeed += RandomAdd;
  return (RandomSeed);
 }
/*===========================================================================
 *		End of Function Random()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - int Random (MaxNumber);
 *
 * Generates a random number between 0 and MaxNumber (inclusive).
 *
 *=========================================================================*/
int Random (const int MaxNumber) {
  //DEFINE_FUNCTION("Random()");
  return (int) ((double)Random() * ((double)sign(MaxNumber) + (double) MaxNumber) / (double) ULONG_MAX);
 }
/*===========================================================================
 *		End of Function Random()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - int Random (MaxNumber);
 *
 * Generates an unisnged long random number between 0 and MaxNumber
 * (inclusive).
 *
 *=========================================================================*/
ulong Random (const ulong MaxNumber) {
  //DEFINE_FUNCTION("Random()");
  return (ulong) ((double)Random() * ((double)1 + (double) MaxNumber) / (double) ULONG_MAX);
 }
/*===========================================================================
 *		End of Function Random()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - int Random (MinNumber, MaxNumber);
 *
 * Generates a random number between the given integer range (inclusive).
 *
 *=========================================================================*/
int Random (const int MinNumber, const int MaxNumber) {
  //DEFINE_FUNCTION("Random()");
  double Range = (double)MaxNumber - (double)MinNumber;
  return (int) ((double) Random() * ((double)sign(Range) + Range) / (double) ULONG_MAX + (double) MinNumber);
 }
/*===========================================================================
 *		End of Function Random()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void RandomizeTimer (void);
 *
 * Much like the Visual Basic function of the same name, this function
 * seeds the random number generator with the current time (number of 
 * elasped seconds since Jan 1, 1970).  Seeds both the CRT rand()
 * as well as the custom Random() functions.
 *
 *=========================================================================*/
void RandomizeTimer (void) {
  //DEFINE_FUNCTION("RandomizeTimer()");
  srand((uint)time(NULL));
  SeedRandom((ulong)time(NULL));
 }
/*===========================================================================
 *		End of Function RandomizeTimer()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void SeedRandom (NewSeed);
 *
 * Seeds the custom long integer random number generator.
 *
 *=========================================================================*/
void SeedRandom (const ulong NewSeed) {
  //DEFINE_FUNCTION("SeedRandom()");
  RandomSeed = NewSeed;
 }
/*===========================================================================
 *		End of Function SeedRandom()
 *=========================================================================*/


/*===========================================================================
 *
 * Begin Test Module Routines
 *
 * Debug build only functions for testing functions in this module.
 *
 *=========================================================================*/
#if defined(_DEBUG)

	/* Turn off several warnings associated with the test code */
#if defined(__BCPLUSPLUS__)
  #pragma warn -rch
  #pragma warn -ccc
#endif 

/*===========================================================================
 *
 * Function - void Test_HexCharToInt (void);
 *
 * Tests the HexCharToInt() macro.
 *	1. Test all hex characters to ensure success
 *
 *=========================================================================*/
void Test_HexCharToInt (void) {
  DEFINE_FUNCTION("Test_HexCharToInt()");
 
  SystemLog.Printf (stdout, "================= Testing Test_HexCharToInt() ===================");

	/* Test all hex characters to ensure success */
  ASSERT(HexCharToInt('0') == 0);
  ASSERT(HexCharToInt('1') == 1);
  ASSERT(HexCharToInt('2') == 2);
  ASSERT(HexCharToInt('3') == 3);
  ASSERT(HexCharToInt('4') == 4);
  ASSERT(HexCharToInt('5') == 5);
  ASSERT(HexCharToInt('6') == 6);
  ASSERT(HexCharToInt('7') == 7);
  ASSERT(HexCharToInt('8') == 8);
  ASSERT(HexCharToInt('9') == 9);
  ASSERT(HexCharToInt('a') == 10);
  ASSERT(HexCharToInt('b') == 11);
  ASSERT(HexCharToInt('c') == 12);
  ASSERT(HexCharToInt('d') == 13);
  ASSERT(HexCharToInt('e') == 14);
  ASSERT(HexCharToInt('f') == 15);
  ASSERT(HexCharToInt('A') == 10);
  ASSERT(HexCharToInt('B') == 11);
  ASSERT(HexCharToInt('C') == 12);
  ASSERT(HexCharToInt('D') == 13);
  ASSERT(HexCharToInt('E') == 14);
  ASSERT(HexCharToInt('F') == 15);
 }
/*===========================================================================
 *		End of Function Test_HexCharToInt()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void Test_Metricize (void);
 *
 * Tests the Metricize() function.
 *	1. Test typical positive inputs
 *	2. Test 0 input
 *	3. Test some negative inputs
 *	4. Test very small/large values
 *	5. Test with different unit strings
 *
 *=========================================================================*/
void Test_Metricize (void) {
  DEFINE_FUNCTION("Test_Metricize()");
  char* pString;
  
  SystemLog.Printf (stdout, "================= Testing Metricize() ===================");

	/* Test with typical strings */
  pString = Metricize(123456789.0f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(123456789) = '%s'", pString);

  pString = Metricize(12345678.9f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(12345678.9) = '%s'", pString);

  pString = Metricize(1234567.89f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(1234567.89) = '%s'", pString);

  pString = Metricize(123456.789f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(123456.789) = '%s'", pString);
  
  pString = Metricize(12345.6789f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(12345.6789) = '%s'", pString);

  pString = Metricize(1234.56789f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(1234.56789) = '%s'", pString);

  pString = Metricize(123.456789f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(123.456789) = '%s'", pString);
    
  pString = Metricize(12.3456789f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(12.3456789) = '%s'", pString);

  pString = Metricize(1.23456789f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(1.23456789) = '%s'", pString);

  pString = Metricize(0.12345f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(0.12345) = '%s'", pString);

  pString = Metricize(0.012345f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(0.012345) = '%s'", pString);

  pString = Metricize(0.0012345f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(0.0012345) = '%s'", pString);

  pString = Metricize(0.00012345f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(0.00012345) = '%s'", pString);

  pString = Metricize(0.000012345f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(0.000012345) = '%s'", pString);

  pString = Metricize(0.0000012345f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(0.0000012345) = '%s'", pString);

  pString = Metricize(0.00000012345f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(0.00000012345) = '%s'", pString);

  pString = Metricize(0.000000012345f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(0.000000012345) = '%s'", pString);

	/* Test 0 input */
  pString = Metricize(0.0f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(0) = '%s'", pString);

  	/* Test some negative inputs */
  pString = Metricize(-12345.6f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(-12345.6) = '%s'", pString);

  pString = Metricize(-1.23456f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(-1.23456) = '%s'", pString);

  pString = Metricize(-0.00000123456f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(-0.00000123456) = '%s'", pString);

	/* Test some very small/large values */
  pString = Metricize(1.23456e30f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(1.23456e30) = '%s'", pString);

  pString = Metricize(1.23456e-30f, "m");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(1.23456e-30) = '%s'", pString);

	/* Test with various unit strings */
  pString = Metricize(-12345.6e12f, "");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(-12345.6e12, '') = '%s'", pString);

  pString = Metricize(-12345.6e12f, "UNIT");
  ASSERT(pString != NULL);
  SystemLog.Printf (stdout, "\tMetricize(-12345.6e12, 'UNIT') = '%s'", pString);

 }
/*===========================================================================
 *		End of Function Test_Metricize()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void Test_Random (NumTests);
 *
 * Tests the Random() function.
 *	1. Outputs multiple random numbers to a test file for analyse
 *	2. Outputs the frequency of the numbers (0 to 65535)
 *
 *=========================================================================*/
void Test_Random (const size_t NumTests) {
  DEFINE_FUNCTION("Test_Random()");

  #if defined(__BCPLUSPLUS__)
    const size_t ArraySize = 4000u;
    const size_t ShiftSize = 17;
  #elif defined(__TURBOC__)
    const size_t ArraySize = 32767u;
    const size_t ShiftSize = 17;
  #else
    const size_t ArraySize = 65535u;
    const size_t ShiftSize = 16;
  #endif

  size_t* NumberCount;
  size_t* NumberCount1;
  size_t  LoopCounter;
  ulong   RandNumber;
  FILE*   pFileHandle;

  	/* Allocate arrays due to problem with large stack variables in Win16 */
  NumberCount  = (size_t *) CreateString(ArraySize * sizeof(size_t));
  NumberCount1 = (size_t *) CreateString(ArraySize * sizeof(size_t));

  SystemLog.Printf (stdout, "================= Testing Random() ===================");
  memset(NumberCount,  0, sizeof(size_t)*(size_t)ArraySize);
  memset(NumberCount1, 0, sizeof(size_t)*(size_t)ArraySize);

	/* Output random number data */
  pFileHandle = fopen("c:\\temp\\rand1.dat", "wt");
  ASSERT(pFileHandle != NULL);
  fprintf (pFileHandle, "     Index,     Random,    HiBytes,   LowBytes\n");

  for (LoopCounter = 0; LoopCounter < NumTests; LoopCounter++) {
    RandNumber = Random();
    fprintf (pFileHandle, "%10u, %10lu, %10lu, %10lu\n", LoopCounter, RandNumber, (RandNumber >> ShiftSize), RandNumber & ArraySize);

    NumberCount[size_t((RandNumber >> ShiftSize) & ArraySize)]++;
    NumberCount1[size_t(RandNumber & ArraySize)]++;
   }

  fclose (pFileHandle);

	/* Output random number frequency data */
  pFileHandle = fopen("c:\\temp\\rand2.dat", "wt");
  ASSERT(pFileHandle != NULL);
  fprintf (pFileHandle, "     Number, HighCount,  LowCount\n");

  for (LoopCounter = 0; LoopCounter < ArraySize; LoopCounter++) {
    fprintf (pFileHandle, "%10lu, %10u, %10u\n", LoopCounter, NumberCount[LoopCounter], NumberCount1[LoopCounter]);
   }

  fclose (pFileHandle);
  DestroyPointer(NumberCount);
  DestroyPointer(NumberCount1);
 }
/*===========================================================================
 *		End of Function Test_Random()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void Test_Random1 (const size_t NumTests);
 *
 * Tests the Random(int) function.
 *	1. Tests against a sample range of 1000 and outputs frequency
 *	   results to file
 *	2. Tests against a range of 0 to 1 and outputs frequency results
 *	3. Test for range 0 to -2, outputting frequency results
 *
 *=========================================================================*/
void Test_Random1 (const size_t NumTests) {
  DEFINE_FUNCTION("Test_Random1()");
  size_t LoopCounter;
  size_t NumberCount1[1001];
  size_t NumberCount2[3];
  size_t NumberCount3[4];
  FILE*  pFileHandle;
  int    RandNumber;

  SystemLog.Printf (stdout, "================= Testing Random(int) ===================");
  memset(NumberCount1, 0, sizeof(size_t)*(size_t)1001);
  memset(NumberCount2, 0, sizeof(size_t)*(size_t)3);
  memset(NumberCount3, 0, sizeof(size_t)*(size_t)4);

	/* Generate random numbers from 0 to 1000 */
  for (LoopCounter = 0; LoopCounter < NumTests; LoopCounter++) {
    RandNumber = Random(1000);
    ASSERT(RandNumber >= 0 && RandNumber <= 1000);
    NumberCount1[RandNumber]++;
   }

	/* Generate random numbers from 0 to 1 */
  for (LoopCounter = 0; LoopCounter < NumTests; LoopCounter++) {
    RandNumber = Random(1);
    ASSERT(RandNumber >= 0 && RandNumber <= 1);
    NumberCount2[RandNumber]++;
   }

   	/* Generate random numbers from 0 to -2 */
  for (LoopCounter = 0; LoopCounter < NumTests; LoopCounter++) {
    RandNumber = Random(-2);
    ASSERT(RandNumber <= 0 && RandNumber >= -2);
    NumberCount3[-RandNumber]++;
   }

	/* Output random number frequency data for 0 to 1000 */
  pFileHandle = fopen("c:\\temp\\rnd1000f.dat", "wt");
  ASSERT(pFileHandle != NULL);
  fprintf (pFileHandle, "     Number,     Count\n");

  for (LoopCounter = 0; LoopCounter < 1001; LoopCounter++) {
    fprintf (pFileHandle, "%10lu, %10u\n", LoopCounter, NumberCount1[LoopCounter]);
   }

  fclose (pFileHandle);

  	/* Output random number frequency data for 0 to 1 */
  pFileHandle = fopen("c:\\temp\\rnd1f.dat", "wt");
  ASSERT(pFileHandle != NULL);
  fprintf (pFileHandle, "     Number,     Count\n");

  for (LoopCounter = 0; LoopCounter < 2; LoopCounter++) {
    fprintf (pFileHandle, "%10lu, %10u\n", LoopCounter, NumberCount2[LoopCounter]);
   }

  fclose (pFileHandle);

    	/* Output random number frequency data for 0 to -2 */
  pFileHandle = fopen("c:\\temp\\rnd-2f.dat", "wt");
  ASSERT(pFileHandle != NULL);
  fprintf (pFileHandle, "     Number,     Count\n");

  for (LoopCounter = 0; LoopCounter < 3; LoopCounter++) {
    fprintf (pFileHandle, "-%10lu, %10u\n", LoopCounter, NumberCount3[LoopCounter]);
   }

  fclose (pFileHandle);
 }
/*===========================================================================
 *		End of Function Test_Random1()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void Test_Random2 (NumTests);
 *
 * Tests the Random(int, int) function
 *	1. Output frequency data for a range of 905 to 1905.
 *	1. Output frequency data for a range of 1001 to 1002
 *	3. Output frequency data for a range of -6105 to -6107
 *
 *=========================================================================*/
void Test_Random2 (const size_t NumTests) {
  DEFINE_FUNCTION("Test_Random2()");
  size_t LoopCounter;
  size_t NumberCount1[1001];
  size_t NumberCount2[3];
  size_t NumberCount3[4];
  FILE*  pFileHandle;
  int    RandNumber;

  SystemLog.Printf (stdout, "================= Testing Random(int, int) ===================");
  memset(NumberCount1, 0, sizeof(size_t)*(size_t)1001);
  memset(NumberCount2, 0, sizeof(size_t)*(size_t)3);
  memset(NumberCount3, 0, sizeof(size_t)*(size_t)4);

	/* Generate random numbers from 0 to 1000 */
  for (LoopCounter = 0; LoopCounter < NumTests; LoopCounter++) {
    RandNumber = Random(905, 1905);
    ASSERT(RandNumber >= 905 && RandNumber <= 1905);
    NumberCount1[RandNumber - 905]++;
   }

	/* Generate random numbers from 0 to 1 */
  for (LoopCounter = 0; LoopCounter < NumTests; LoopCounter++) {
    RandNumber = Random(1001, 1002);
    ASSERT(RandNumber >= 1001 && RandNumber <= 1002);
    NumberCount2[RandNumber - 1001]++;
   }

   	/* Generate random numbers from 0 to -2 */
  for (LoopCounter = 0; LoopCounter < NumTests; LoopCounter++) {
    RandNumber = Random(-6105, -6107);
    ASSERT(RandNumber <= -6105 && RandNumber >= -6107);
    NumberCount3[RandNumber + 6107]++;
   }

	/* Output random number frequency data for 905 to 1005 */
  pFileHandle = fopen("c:\\temp\\rnd905f.dat", "wt");
  ASSERT(pFileHandle != NULL);
  fprintf (pFileHandle, "     Number,     Count\n");

  for (LoopCounter = 0; LoopCounter < 1001; LoopCounter++) {
    fprintf (pFileHandle, "%10lu, %10u\n", LoopCounter + 905, NumberCount1[LoopCounter]);
   }

  fclose (pFileHandle);

  	/* Output random number frequency data for 1001 to 1002 */
  pFileHandle = fopen("c:\\temp\\rnd1001f.dat", "wt");
  ASSERT(pFileHandle != NULL);
  fprintf (pFileHandle, "     Number,     Count\n");

  for (LoopCounter = 0; LoopCounter < 2; LoopCounter++) {
    fprintf (pFileHandle, "%10lu, %10u\n", LoopCounter + 1001, NumberCount2[LoopCounter]);
   }

  fclose (pFileHandle);

    	/* Output random number frequency data for -6105 to -6107 */
  pFileHandle = fopen("c:\\temp\\rnd-6105f.dat", "wt");
  ASSERT(pFileHandle != NULL);
  fprintf (pFileHandle, "     Number,     Count\n");

  for (LoopCounter = 0; LoopCounter < 3; LoopCounter++) {
    fprintf (pFileHandle, "%10ld, %10u\n", (long)LoopCounter - 6107, NumberCount3[LoopCounter]);
   }

  fclose (pFileHandle);
 }
/*===========================================================================
 *		End of Function Test_Random2()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void Test_RandomRate (NumTests);
 *
 * Tests the number of times the Random() function can be called 
 * before returning to the original seed.  A random seed is chosen
 * for each test.
 *
 *=========================================================================*/
void Test_RandomRate (const size_t NumTests) {
  //DEFINE_FUNCTION("Test_RandomRate()");
  size_t LoopCounter;
  ulong  Counter;
  ulong  InitialSeed;

  SystemLog.Printf (stdout, "================= Testing Random Turnover Rate ===================");
  Random();
  SeedRandom(Random());

	/* Perform each test a number of times */
  for (LoopCounter = 0; LoopCounter < NumTests; LoopCounter++) {
    InitialSeed = RandomSeed;
    Counter = 0;
    SystemLog.Printf(stdout, "\t%u) Testing turnover rate of seed %lu...", LoopCounter+1, InitialSeed);
    
    while (Random() != InitialSeed) {
      Counter++;
      if ((Counter % 1000000ul) == 0) printf ("\t\t%lu million\n", Counter/1000000ul);
     }
     
     SystemLog.Printf(stdout, "\t\tSeed of %lu turned over in %lu calls...", InitialSeed, Counter);
   }
	
 }
/*===========================================================================
 *		End of Function Test_RandomRate()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void Test_sign (void);
 *
 * Tests the various sign() type functions.
 *	1. Ensures each version gives a valid -1/0/1 result.
 *
 *=========================================================================*/
void Test_sign (void) {
  DEFINE_FUNCTION("Test_sign()");

  SystemLog.Printf (stdout, "================= Testing sign() Functions ===================");

	/* Test positive numbers */
  ASSERT(sign((int)10) == 1);
  ASSERT(sign((int)INT_MAX) == 1);
  ASSERT(sign((long)101) == 1);
  ASSERT(sign((long)LONG_MAX) == 1);
  ASSERT(sign((float)1012.9) == 1.0);
  ASSERT(sign((float)FLT_MAX) == 1.0);
  ASSERT(sign((float)FLT_MIN) == 1.0);
  ASSERT(sign((double)10381.1) == 1.0);
  ASSERT(sign((double)DBL_MAX) == 1.0);
  ASSERT(sign((double)DBL_MIN) == 1.0);

	/* Test zero numbers */
  ASSERT(sign((int)0) == 0);
  ASSERT(sign((long)0) == 0);
  ASSERT(sign((float)0.0) == 0.0);
  ASSERT(sign((double)0.0) == 0.0);

	/* Test negative numbers */
  ASSERT(sign((int)-10) == -1);
  ASSERT(sign((int)INT_MIN) == -1);
  ASSERT(sign((long)-101) == -1);
  ASSERT(sign((long)LONG_MIN) == -1);
  ASSERT(sign((float)-1012.9) == -1.0);
  ASSERT(sign((float)-FLT_MAX) == -1.0);
  ASSERT(sign((float)-FLT_MIN) == -1.0);
  ASSERT(sign((double)-10381.1) == -1.0);
  ASSERT(sign((double)-DBL_MAX) == -1.0);
  ASSERT(sign((double)-DBL_MIN) == -1.0);
 }
/*===========================================================================
 *		End of Function Test_sign()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void Test_DL_Math (void);
 *
 * Tests all the routines in this module.
 *	1. Tests the Random() function
 *	2. Tests the Random(int) and Random(int, int) functions
 *	3. Test the various sign() functions
 *	4. Test the turnover rate of the random function
 *	5. Tests the Metricize() function
 *
 *=========================================================================*/
void Test_DL_Math (void) {
  //DEFINE_FUNCTION("Test_DL_Math()");

  Test_sign();
  Test_HexCharToInt();
  Test_Metricize();

  //RandomizeTimer();
  Test_Random();
  Test_Random1();
  Test_Random2();
  //Test_RandomRate();

 }
/*===========================================================================
 *		End of Function Test_DL_Math()
 *=========================================================================*/

	/* Restore compiler warning options */
#if defined(__BCPLUSPLUS__)
  #pragma warn .rch
  #pragma warn .ccc
#endif 

#endif
/*===========================================================================
 *		End of Test Module Routines
 *=========================================================================*/