/******************************************************************************
 testmap.cc

	Test code for the JStringMap class and related code.

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

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

#include <iostream.h>

#include <JStringMap.h>
#include <JStringMapCursor.h>
#include <string.h>

//#include <JMemoryManager.h>

#include <jAssert.h>

	void PrintError(long line);

	const JSize gNumStrings = 16;

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

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

int
main()
{
	JSize i;

	cout << "Beginning JStringMap test.  No news is good news." << endl;

	const JCharacter* keyConst = "testkey";
	JCharacter* const key = new JCharacter[strlen(keyConst)+1];
	strcpy(key, keyConst);

	{
		JStringMap<int> map(kFalse);

		int v;

		if ( map.GetElement(key, &v) )
			{
			PrintError(__LINE__);
			cout << "   Found a non-existent key" << endl;
			}

		map.SetElement(key, 42);

		v = 10;
		if ( map.GetElement(key, &v) )
			{
			if (v != 42)
				{
				PrintError(__LINE__);
				cout << "   Got value " << v << ", should be 42" << endl;
				}
			}
		else
			{
			PrintError(__LINE__);
			cout << "   Could not retrieve key value" << endl;
			}

		key[0] = 'T';

	// Verify that the map is using the original key, not a copy
		if ( map.GetElement("testkey", &v) )
			{
			PrintError(__LINE__);
			cout << "   Retrieved bad key key value" << endl;
			}

		// This will fail because we've broken the table; the hash value didn't
		// change even though the key did
		if ( map.GetElement("Testkey", &v) )
			{
			PrintError(__LINE__);
			cout << "   Found a non-existent key" << endl;
			}

		// Fix table
		key[0] = 't';
		if ( !map.GetElement("testkey", &v) )
			{
			PrintError(__LINE__);
			cout << "   Couldn't find key!" << endl;
			}

		v = 10;
		if ( map.GetElement("testkey", &v) )
			{
			if (v != 42)
				{
				PrintError(__LINE__);
				cout << "   Got value " << v << ", should be 42" << endl;
				}
			}
		else
			{
			PrintError(__LINE__);
			cout << "   Could not retrieve key value" << endl;
			}

		if ( !map.RemoveElement("testkey") )
			{
			PrintError(__LINE__);
			cout << "   Unable to remove element" << endl;
			}

		if ( map.GetElement("testkey", &v) )
			{
			PrintError(__LINE__);
			cout << "   Found a non-existent key" << endl;
			}
	}

//	JMemoryManager::Instance()->PrintMemoryStats();

/***
	Play around with inserting and removing a bunch of keys
 ***/

	{
		JStringMap<int> map(0); // Ridiculous table size to really exercise resizer

		if (map.GetElementCount() != 0)
			{
			PrintError(__LINE__);
			cout << "   Map should be empty!" << endl;
			}
		if (map.GetLoadCount() != 0)
			{
			PrintError(__LINE__);
			cout << "   Map should not have deleted elements!" << endl;
			}

// Search for keys, all should fail
		for (i=0;i<gNumStrings;i++)
			{
			key[0] = 'A' + i;
			int v;
			if ( map.GetElement(key, &v) )
				{
				PrintError(__LINE__);
				cout << "   Found non-existent key \"" << key << "\"" << endl;
				}
			}
		if (map.GetElementCount() != 0)
			{
			PrintError(__LINE__);
			cout << "   Map should be empty!" << endl;
			}
		if (map.GetLoadCount() != 0)
			{
			PrintError(__LINE__);
			cout << "   Map should not have deleted elements!" << endl;
			}

// Insert keys
		for (i=0;i<gNumStrings;i++)
			{
			key[0] = 'A' + i;
	//		cout << "Inserting string \"" << key << "\" with fill/load factor "
	//		     << map.GetFillFactor() << "/" << map.GetLoadFactor() << endl;
			if ( !map.SetNewElement(key, i+42) )
				{
				PrintError(__LINE__);
				}
			}
		if (map.GetElementCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains wrong number of elements!" << endl;
			}
		if (map.GetLoadCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains extra deleted elements!" << endl;
			}
		{
// Compare with cursor
		JSize count = 0;
		JStringMapCursor<int> cursor(&map);
		while ( cursor.Next() )
			{
			const JCharacter* thisKey = cursor.GetKey();
			int value = cursor.GetValue();
			if (value - 42 != thisKey[0] - 'A')
				{
				PrintError(__LINE__);
				cout << "   Cursor found value " << value << ", should have been "
				     << (int) thisKey[0] << "!" << endl;
				}
			key[0] = value - 42 + 'A';
			if (strcmp(key, thisKey) != 0)
				{
				PrintError(__LINE__);
				cout << "   Cursor found invalid key!" << endl;
				}
			++count;
			}
		if (count != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Cursor counted wrong number of elements!" << endl;
			}
		}

// Search for keys, should all succeed
		for (i=0;i<gNumStrings;i++)
			{
			key[0] = 'A' + i;
			int v;
			if ( !map.GetElement(key, &v) )
				{
				PrintError(__LINE__);
				cout << "   Could not retrieve key \"" << key << "\"" << endl;
				}
			else
				{
				if (JSize(v) != i+42)
					{
					PrintError(__LINE__);
					cout << "   Key \"" << key << "\" has value " << v << " instead of "
					     << i+42 << endl;
					}
				}
			}
		if (map.GetElementCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains wrong number of elements!" << endl;
			}
		if (map.GetLoadCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains extra deleted elements!" << endl;
			}

// Search for other keys, all should fail
		for (i=0;i<gNumStrings;i++)
			{
			key[0] = 'a' + i;
			int v;
			if ( map.GetElement(key, &v) )
				{
				PrintError(__LINE__);
				cout << "   Found non-existent key \"" << key << "\"" << endl;
				}
			}
		if (map.GetElementCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains wrong number of elements!" << endl;
			}
		if (map.GetLoadCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains extra deleted elements!" << endl;
			}

// Modify values
		for (i=0;i<gNumStrings;i++)
			{
			key[0] = 'A' + i;
	//		cout << "Changing value of string \"" << key << "\" with fill/load factor "
	//		     << map.GetFillFactor() << "/" << map.GetLoadFactor() << endl;
			if ( !map.SetOldElement(key, 99+i) )
				{
				PrintError(__LINE__);
				}
			}
		if (map.GetElementCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains wrong number of elements!" << endl;
			}
		if (map.GetLoadCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains extra deleted elements!" << endl;
			}

// Search for keys, should all succeed
		for (i=0;i<gNumStrings;i++)
			{
			key[0] = 'A' + i;
			int v;
			if ( !map.GetElement(key, &v) )
				{
				PrintError(__LINE__);
				cout << "   Could not retrieve key \"" << key << "\"" << endl;
				}
			else
				{
				if (JSize(v) != i+99)
					{
					PrintError(__LINE__);
					cout << "   Key \"" << key << "\" has value " << v << " instead of "
					     << i+99 << endl;
					}
				}
			}
		if (map.GetElementCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains wrong number of elements!" << endl;
			}
		if (map.GetLoadCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains extra deleted elements!" << endl;
			}

// Search for other keys, all should fail
		for (i=0;i<gNumStrings;i++)
			{
			key[0] = 'a' + i;
			int v;
			if ( map.GetElement(key, &v) )
				{
				PrintError(__LINE__);
				cout << "   Found non-existent key \"" << key << "\"" << endl;
				}
			}
		if (map.GetElementCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains wrong number of elements!" << endl;
			}
		if (map.GetLoadCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains extra deleted elements!" << endl;
			}

// Remove other keys, all should fail
		for (i=0;i<gNumStrings;i++)
			{
			key[0] = 'a' + i;
			if ( map.RemoveElement(key) )
				{
				PrintError(__LINE__);
				cout << "   Removal of non-existent key succeeded!" << endl;
				}
			}
		if (map.GetElementCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains wrong number of elements!" << endl;
			}
		if (map.GetLoadCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains extra deleted elements!" << endl;
			}

// Remove keys, all should succeed
		for (i=0;i<gNumStrings;i++)
			{
			key[0] = 'A' + i;
	//		cout << "Removing string \"" << key << "\" with fill/load factor "
	//		     << map.GetFillFactor() << "/" << map.GetLoadFactor() << endl;
			if (!map.RemoveElement(key))
				{
				PrintError(__LINE__);
				cout << "   Removal failed!" << endl;
cout << "   Map still contains " << map.GetElementCount() << " keys!" << endl;
				}
			}
		if (map.GetElementCount() != 0)
			{
			PrintError(__LINE__);
			cout << "   Map still contains elements!" << endl;
			}
		if (map.GetLoadCount() != 0)
			{
			PrintError(__LINE__);
			cout << "   Map still contains deleted elements!" << endl;
			}

// Remove other keys, all should fail
		for (i=0;i<gNumStrings;i++)
			{
			key[0] = 'a' + i;
			if ( map.RemoveElement(key) )
				{
				PrintError(__LINE__);
				cout << "   Removal of non-existent key succeeded!" << endl;
				}
			}
		if (map.GetElementCount() != 0)
			{
			PrintError(__LINE__);
			cout << "   Map still contains elements!" << endl;
			}
		if (map.GetLoadCount() != 0)
			{
			PrintError(__LINE__);
			cout << "   Map still contains deleted elements!" << endl;
			}

// Re-insert
		for (i=0;i<gNumStrings;i++)
			{
			key[0] = 'A' + i;
	//		cout << "Inserting string \"" << key << "\" with fill/load factor "
	//		     << map.GetFillFactor() << "/" << map.GetLoadFactor() << endl;
			if ( !map.SetNewElement(key, -i) )
				{
				PrintError(__LINE__);
				}
			}
		if (map.GetElementCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains wrong number of elements!" << endl;
			}
		if (map.GetLoadCount() != gNumStrings)
			{
			PrintError(__LINE__);
			cout << "   Map contains extra deleted elements!" << endl;
			}

// Remove them all at once
		map.RemoveAll();
		if (map.GetElementCount() != 0)
			{
			PrintError(__LINE__);
			cout << "   Map still contains elements!" << endl;
			}
		if (map.GetLoadCount() != 0)
			{
			PrintError(__LINE__);
			cout << "   Map still contains deleted elements!" << endl;
			}
	//	cout << "RemoveAll left fill/load factor " << map.GetFillFactor()
	//	     << "/" << map.GetLoadFactor()<< endl;
	}

	delete[] key;

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

//	JMemoryManager::Instance()->SetPrintExitStats(kTrue);

	return 0;
}

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

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

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