/******************************************************************************
 periodtestrand.cc

	Tests the period of the JKLRandInt Knuth-Morris random number generator.
	Because this takes so long it won't be run very often, it is a program of
	its own rather than part of testrand.

	Copyright © 1997 by Dustin Laurence.  All rights reserved.
	
	Base code generated by Codemill v0.1.0

 *****************************************************************************/

#include <iostream.h>
#include <iomanip.h>

#include <jRand.h>
#include <time.h>

#include <jAssert.h>

	const unsigned long callibrationCycles = 10000000UL;

/******************************************************************************
 main

 *****************************************************************************/

int
main()
{
	cout << "\nBeginning Knuth-Morris period test.  Be prepared to wait a while." << endl;

	JInt32 seed = 0;
	unsigned long period = 0;

	cout << "\n   Running " << callibrationCycles
	     << " iterations to estimate running time." << endl;

	const time_t startCallibrationTime = time(NULL);
	const clock_t startCallibrationClock = clock();

	do
		{
		seed = JKLRandInt32(seed);
		++period;
		}
		while(period < callibrationCycles);

	const time_t  endCallibrationTime = time(NULL);
	const clock_t endCallibrationClock = clock();

	const double deltaCallibrationClock = (1.0*endCallibrationClock-startCallibrationClock)
	                                     / CLOCKS_PER_SEC;
	time_t deltaCallibrationTime = endCallibrationTime-startCallibrationTime;
	// time() rounds off strangely, try to ensure an over- rather than under-estimate
	while (deltaCallibrationTime < deltaCallibrationClock)
		{
		++deltaCallibrationTime;
		}
	cout << "\n   Callibration run time : " << deltaCallibrationClock
	     << " processor sec in " << deltaCallibrationTime << " real sec." << endl;

	cout << "      (if the real run time was small, the following estimate\n"
	        "       can be grossly inaccurate)" << endl;

	const double maxPeriod = 1.0*ULONG_MAX + 1.0;
	const double timeMultiple = maxPeriod/callibrationCycles;

	cout << "   Running time of full test should be no more than:\n      "
	     << timeMultiple*deltaCallibrationClock << " processor sec = "
	     << timeMultiple*deltaCallibrationClock/60.0 << " processor min in\n      "
	     << timeMultiple*deltaCallibrationTime << " real sec = "
	     << timeMultiple*deltaCallibrationTime/60.0 << " real min" << endl;

	period = 0;
	JInt32 startSeed = seed;

	cout << "\n   Running full period test." << endl;

	const time_t startTime = time(NULL);
	const clock_t startClock = clock();

	do
		{
		seed = JKLRandInt32(seed);
		++period;
		}
		while(seed != startSeed);

	clock_t endClock = clock();
	time_t endTime = time(NULL);

	cout << "   Full period test finished." << endl;

	const time_t deltaTime = endTime-startTime;
	const double deltaClock = (1.0*endClock-startClock) / CLOCKS_PER_SEC;

	cout << "\n   Actual run time : " << deltaClock
	     << " processor sec in " << deltaTime << " real sec." << endl;

	if (period != 0)
		{
		cout << "\n   JKLRandInt32 has a period of " << period << endl;
		cout << "   Maximum possible period is 2^32 ~ " << maxPeriod
		     << ",\n   so the period is " << period/maxPeriod << " of ideal." << endl;
		}
	else
		{
		cout << "\n   JKLRandInt32 has a period of 2^32 ~ " << maxPeriod
		     << ", the maximum possible." << endl;
		}

	cout << "\nFinished Knuth-Morris test.  Whew, that worked up a sweat.\n" << endl;

	return 0;
}
