/******************************************************************************
 testtable.cc

	Test code for the JHashTable class and related code.

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

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

#include <iostream.h>

#include <JHashTable.h>
#include <JHashCursor.h>

#include <JKLRand.h>
#include <JStrValue.h>

#include <jAssert.h>

	struct TableState {
		JHashTable< JStrValue<int> >* table;
		JSize           listLength;
		JHashValue*     hashList;
		JStrValue<int>* valueList;
		int*            countList;
	};

	void StoreRand(JHashTable< JStrValue<int> >* const table,
	           const JSize                 listLength,
	           const JHashValue* const     hashList,
	           const JStrValue<int>* const valueList,
	                 int*        const     countList);
	void StoreElement(const JSize element,
	           JHashTable< JStrValue<int> >* const table,
	           const JSize                 listLength,
	           const JHashValue* const     hashList,
	           const JStrValue<int>* const valueList,
	                 int*        const     countList);


	void SearchRand(const JHashTable< JStrValue<int> >* const table,
	            const JSize                 listLength,
	            const JHashValue* const     hashList,
	            const JStrValue<int>* const valueList,
	            const int*        const     countList);
	void SearchElement(const JSize element,
	            const JHashTable< JStrValue<int> >* const table,
	            const JSize                 listLength,
	            const JHashValue* const     hashList,
	            const JStrValue<int>* const valueList,
	            const int*        const     countList);


	void RemoveRand(JHashTable< JStrValue<int> >* const table,
	           const JSize                 listLength,
	           const JHashValue* const     hashList,
	           const JStrValue<int>* const valueList,
	                 int*        const     countList);
	void RemoveElement(const JSize element,
	           JHashTable< JStrValue<int> >* const table,
	           const JSize                 listLength,
	           const JHashValue* const     hashList,
	           const JStrValue<int>* const valueList,
	                 int*        const     countList);

	void PrintError(long line);


	// Check consistency with IsOK plus looping through entire table
	// Num elements match?
	// element counts match?
	// any counts negative?

	const JIndex kElementNum = 100;

	JKLRand gRand(192837);

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

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

int
main()
{
	cout << "Beginning JHashTable test.  No news is good news" << endl;

//	TableState experiment = CreateTableState;

	JHashTable< JStrValue<int> > table;

	// Generate hash list
	JHashValue hashList[kElementNum];
	JIndex i;
	for (i=0;i<kElementNum;i++)
		{
		hashList[i] = gRand.UniformInt32();
		}

	// Generate key list
	const JCharacter* keyList[kElementNum];
	for (i=0;i<kElementNum;i++)
		{
		keyList[i] = "Lazy key";
		}

	// Generate value list
	JStrValue<int> valueList[kElementNum];
	for (i=0;i<kElementNum;i++)
		{
		valueList[i].key =   "Lazy key";
		valueList[i].value =  gRand.UniformInt32();
		}

	// Generate storage count list
	int countList[kElementNum];
	for (i=0;i<kElementNum;i++)
		{
		countList[i] = 0;
		}

//	JSize goal = 100;
	for (i=0;i<10000;i++)
		{
		JFloat prob = gRand.UniformClosedProb();
		if (prob < 0.1)
			{
			StoreRand(&table, kElementNum, hashList, valueList, countList);
			}
		else if (prob < 0.2)
			{
			RemoveRand(&table, kElementNum, hashList, valueList, countList);
			}
		else
			{
			SearchRand(&table, kElementNum, hashList, valueList, countList);
			}
		}

//cout << "\nSecond half\n" << endl;

	for (i=0;i<10000;i++)
		{
		JFloat prob = gRand.UniformClosedProb();
		if (prob < 0.05)
			{
			StoreRand(&table, kElementNum, hashList, valueList, countList);
			}
		else if (prob < 0.15)
			{
			RemoveRand(&table, kElementNum, hashList, valueList, countList);
			}
		else
			{
			SearchRand(&table, kElementNum, hashList, valueList, countList);
			}
		}

	cout << "Finished JHashTable test.  If nothing printed out, it passed" << endl;

	cout << "\n         Size: " << table.GetTableSize() << endl;

	cout << "\nElement count: " << table.GetElementCount() << endl;
	cout <<   "   Load count: " << table.GetLoadCount() << endl;

	cout << "\nFill factor: " << table.GetFillFactor() << endl;
	cout <<   "Load factor: " << table.GetLoadFactor() << endl;

	return 0;
}

/******************************************************************************
 StoreRand

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

void
StoreRand
	(
	JHashTable< JStrValue<int> >* const table,
	const JSize                 listLength,
	const JHashValue*     const hashList,
	const JStrValue<int>* const valueList,
	      int*            const countList
	)
{
	const JIndex el = gRand.UniformLong(0, listLength-1);
	StoreElement(el, table, listLength, hashList, valueList, countList);
}

/******************************************************************************
 StoreElement

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

void
StoreElement
	(
	const JSize el,
	JHashTable< JStrValue<int> >* const table,
	const JSize                 listLength,
	const JHashValue*     const hashList,
	const JStrValue<int>* const valueList,
	      int*            const countList
	)
{
	JHashCursor< JStrValue<int> > cursor(table, hashList[el]);
	cursor.ForceNextOpen();
	if ( cursor.IsFull() )
		{
		PrintError(__LINE__);
		return;
		}
	cursor.Set(hashList[el], valueList[el]);
	++countList[el];
}

/******************************************************************************
 SearchRand

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

void
SearchRand
	(
	const JHashTable< JStrValue<int> >* const table,
	const JSize                 listLength,
	const JHashValue*     const hashList,
	const JStrValue<int>* const valueList,
	const int*            const countList
	)
{
	const JIndex el = gRand.UniformLong(0, listLength-1);
	SearchElement(el, table, listLength, hashList, valueList, countList);
}

/******************************************************************************
 SearchElement

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

void
SearchElement
	(
	const JSize el,
	const JHashTable< JStrValue<int> >* const table,
	const JSize                 listLength,
	const JHashValue*     const hashList,
	const JStrValue<int>* const valueList,
	const int*            const countList
	)
{
	JConstHashCursor< JStrValue<int> > cursor(table, hashList[el]);
	int count = 0;
	while ( cursor.NextHash() )
		{
		++count;
		if ( !cursor.IsFull() )
			{
			PrintError(__LINE__);
			}
		else
			{
			const JStrValue<int> value = cursor.GetValue();
			if (value.key != valueList[el].key)
				{
				PrintError(__LINE__);
				}
			if (value.value != valueList[el].value)
				{
				PrintError(__LINE__);
				}
			}
		}

	if (countList[el] != count)
		{
		PrintError(__LINE__);
		cout << "Count is " << count << " instead of " << countList[el] << endl;
		}
}

/******************************************************************************
 RemoveRand

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

void
RemoveRand
	(
	JHashTable< JStrValue<int> >* const table,
	const JSize                 listLength,
	const JHashValue*     const hashList,
	const JStrValue<int>* const valueList,
	      int*            const countList
	)
{
	const JIndex firstEl = gRand.UniformLong(0, listLength-1);
	JIndex el = firstEl;
	while (countList[el] == 0)
		{
		++el;
		if (el == firstEl)
			{
			if (table->GetElementCount() != 0)
				{
				PrintError(__LINE__);
				}
			return;
			}
		}

	RemoveElement(el, table, kElementNum, hashList, valueList, countList);
}

/******************************************************************************
 RemoveElement

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

void
RemoveElement
	(
	const JSize el,
	JHashTable< JStrValue<int> >* const table,
	const JSize                 listLength,
	const JHashValue*     const hashList,
	const JStrValue<int>* const valueList,
	      int*            const countList
	)
{
	JHashCursor< JStrValue<int> > cursor(table, hashList[el]);
	JSize count = 0;
	while ( cursor.NextHash() )
		{
		++count;
		if ( !cursor.IsFull() || cursor.GetHashValue() != hashList[el])
			{
			PrintError(__LINE__);
			}
		}

	if (count == 0)
		{
		return;
		}

	const JSize deleteNum = gRand.UniformLong(1, count);

	cursor.Reset();
	for (JIndex i=1;i<=deleteNum;i++)
		{
		if (!cursor.NextHash() )
			{
			PrintError(__LINE__);
			cout << "   Count = " << count << ", deleteNum = " << deleteNum << endl;
			cout << "   Error on cycle " << i << endl;
			return;
			}
		}

	cursor.Remove();
	--countList[el];
}

/******************************************************************************
 PrintError

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

void
PrintError
	(
	long line
	)
{
	cout << "*** testtable error at line " << line << endl;
}