//
// Tools_Histogram.cpp
//
// Copyright (C) 2003 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#include "Debugging/Asura_Assert.h"


#define DB_PRINT 0


#include <stdlib.h>
//#include <stdio.h>

#include "ImageProcessing/Tools_Histogram.h"
#include "ImageProcessing/Tools_HistogramMacros.h"
#include "ImageProcessing/Tools_HeapTemplate.h"
#define _void_ unsigned char

#if DB_PRINT
extern "C" extern int (* your_printf) (char const *, ...);

static int num_spc = 1;

char const * Spaces()
{
	char const * spc20 = "                    ";
	return spc20+(20-(num_spc < 20 ? num_spc : 0));
}
#endif

class LS_Block_Ptr
{
private:
	LS_Block * ptr;
public:
	LS_Block_Ptr(LS_Block * cptr) : ptr(cptr) {}
	LS_Block_Ptr(){}
	inline operator LS_Block * () const { return ptr; }
	inline LS_Block * operator () () const { return ptr; }
	inline int operator < (LS_Block_Ptr const & lsp2) const
		{ return ptr->grad < lsp2.ptr->grad; }
};

void AdjustLSBlocksForPalette (Lighting_Style * ls)
{
	Asura_Assert(ls->num_blocks,"");
	
	// order the blocks by gradient, lowest first
	Ordered_Heap<LS_Block_Ptr> blks;
	for (unsigned i=0; i<ls->num_blocks; ++i)
	{
		blks.add_entry(&ls->blocks[i]);
	}
	// shorten blocks whose gradients are less than ONE to make their gradients equal to ONE
	unsigned int extra_space = 0;
	while (blks.lowest_entry()()->grad < 1<<16)
	{
		LS_Block * blkP = blks.lowest_entry();
		blks.delete_lowest();
		extra_space += (blkP->end_input - blkP->begin_input) - (blkP->end_output - blkP->begin_output);
		blkP->end_input = blkP->begin_input + blkP->end_output - blkP->begin_output;
		blkP->grad = 1<<16;
		blks.add_entry(blkP);
	}
	// proportionally lengthen blocks whose output intensities are less than ONE, but do not allow their gradients to fall below ONE
	unsigned int space_to_stretch = 0;
	OHIF<LS_Block_Ptr> i_blk(&blks);
	for (; !i_blk.done(); i_blk.next())
	{
		if (i_blk()()->begin_output < 1<<16)
		{
			space_to_stretch += i_blk()()->end_input - i_blk()()->begin_input;
		}
	}
	for (i_blk.restart(); !i_blk.done(); i_blk.next())
	{
		if (i_blk()()->begin_output < 1<<16)
		{
			LS_Block * blkP = i_blk();
			unsigned int oldlen = blkP->end_input - blkP->begin_input;
			if (oldlen)
			{
				unsigned int newlen = UnsignedWideMulNarrowDiv(oldlen,space_to_stretch+extra_space,space_to_stretch);
				if (newlen > blkP->end_output - blkP->begin_output)
					newlen = blkP->end_output - blkP->begin_output;
				space_to_stretch -= oldlen;
				extra_space -= newlen-oldlen;
				blkP->end_input = blkP->begin_input + newlen;
				blkP->grad = UnsignedDivFixed(blkP->end_output - blkP->begin_output,blkP->end_input - blkP->begin_input);
			}
		}
	}
	// repeat for blocks whose output intensities are not less than ONE, but do not allow their gradients to fall below ONE
	if (extra_space) // should only be significant if all blocks lengthened above now have gradient of ONE
	{
		Asura_Assert(0==space_to_stretch,"");
		for (i_blk.restart(); !i_blk.done(); i_blk.next())
		{
			if (i_blk()()->begin_output >= 1<<16)
			{
				space_to_stretch += i_blk()()->end_input - i_blk()()->begin_input;
			}
		}
		for (i_blk.restart(); !i_blk.done(); i_blk.next())
		{
			if (i_blk()()->begin_output >= 1<<16)
			{
				LS_Block * blkP = i_blk();
				unsigned int oldlen = blkP->end_input - blkP->begin_input;
				if (oldlen)
				{
					unsigned int newlen = UnsignedWideMulNarrowDiv(oldlen,space_to_stretch+extra_space,space_to_stretch);
					if (newlen > blkP->end_output - blkP->begin_output)
						newlen = blkP->end_output - blkP->begin_output;
					space_to_stretch -= oldlen;
					extra_space -= newlen-oldlen;
					blkP->end_input = blkP->begin_input + newlen;
					blkP->grad = UnsignedDivFixed(blkP->end_output - blkP->begin_output,blkP->end_input - blkP->begin_input);
				}
			}
		}
	}
	if (extra_space) // all blocks now have gradient of ONE, so to finish the lengthening, we must allow gradients of less than ONE
	{
		Asura_Assert(0==space_to_stretch,"");
		space_to_stretch = (1<<16) - extra_space;
		for (i_blk.restart(); !i_blk.done(); i_blk.next())
		{
			LS_Block * blkP = i_blk();
			unsigned int oldlen = blkP->end_input - blkP->begin_input;
			if (oldlen)
			{
				unsigned int newlen = UnsignedWideMulNarrowDiv(oldlen,space_to_stretch+extra_space,space_to_stretch);
				space_to_stretch -= oldlen;
				extra_space -= newlen-oldlen;
				blkP->end_input = blkP->begin_input + newlen;
				blkP->grad = UnsignedDivFixed(blkP->end_output - blkP->begin_output,blkP->end_input - blkP->begin_input);
			}
		}
	}
	if (extra_space) // slight rounding error, so just lengthen the last block
	{
		LS_Block * blkP = &ls->blocks[ls->num_blocks-1];
		blkP->end_input += extra_space;
		blkP->grad = UnsignedDivFixed(blkP->end_output - blkP->begin_output,blkP->end_input - blkP->begin_input);
	}
	// now the blocks' begin_input don't correspond to the previous block's end_input, so correct this before returning
	LS_Block * prevblkP = &ls->blocks[0];
	LS_Block * thisblkP = &ls->blocks[1];
	for (int i=ls->num_blocks-1; i; --i)
	{
		unsigned int len = thisblkP->end_input - thisblkP->begin_input;
		thisblkP->begin_input = prevblkP->end_input;
		thisblkP->end_input = thisblkP->begin_input + len;
		prevblkP = thisblkP;
		++thisblkP;
	}
}

// -oOo-

void HHHashFunction::Init (unsigned long table_size)
{
	#ifndef RAND_MAX
	#error RAND_MAX is not defined
	#endif
	#if RAND_MAX < 16777216
	#define RANDOM() (rand () + (RAND_MAX+1) * rand ())
	#else
	#define RANDOM() rand ()
	#endif
	
	unsigned long hash_mask = table_size - 1;
	// check that the table size is a power of two
	Asura_Assert ( ! (hash_mask & table_size),"" );
	
	for (int i=0; i<256; ++i)
	{
		red_hash_values [i] = hash_mask & (unsigned long) RANDOM ();
		green_hash_values [i] = hash_mask & (unsigned long) RANDOM ();
		blue_hash_values [i] = hash_mask & (unsigned long) RANDOM ();
	}
}

HHHashFunction::HHHashFunction (unsigned long table_size)
{
	Init (table_size);
}

void HHHashFunction::Resize (unsigned long new_table_size)
{
	Init (new_table_size);
}

// -oOo-

/*
Define the file format of my histogram file:

Note: they will not be transferrable to machines of different endianness

1x header
{
	QWORD (8 bytes, 8x8bit ascii characters)  IDENTIFIER: "HISTGRAM"
	DWORD (4 bytes, 1x32bit unsigned integer) VERSION: 0
	DWORD (4 bytes)                           RESERVED: 0
	DWORD (4 bytes, 1x32bit unsigned integer) NUM_ENTRIES
}
NUM_ENTRIESx entry
{
	DWORD (4 bytes, 1x32bit unsigned integer)  COUNT
	BYTE  (1 byte, 1x8bit unsigned integer)    REDVAL
	BYTE  (1 byte, 1x8bit unsigned integer)    GREENVAL
	BYTE  (1 byte, 1x8bit unsigned integer)    BLUEVAL
	BYTE  (1 byte)                             RESERVED: 0
}

*/

#define HH_INITIALTABLESIZE 4096

int HHHashHistogram::Save (char const * fname) const
{
	FILE * fp = fopen (fname,"wb");
	if (!fp)
	{
		#if DB_PRINT > 0
		your_printf("%s ERROR: cannot write %s\n",Spaces(),fname);
		#endif
		return 0;
	}
	
	#if DB_PRINT > 1
	your_printf("%sSaving histogram with %lu entries ...\n",Spaces(),number_of_entries);
	++num_spc;
	#endif
	
	fwrite ("HISTGRAM",8,1,fp);
	
	unsigned long const version = 0;
	fwrite (&version,4,1,fp);
	
	unsigned long const reserved = 0;
	fwrite (&reserved,4,1,fp);
	
	fwrite (&number_of_entries,4,1,fp);
	
	HHNode const * nodeP = entries;
	for (int i=number_of_entries; i; --i, ++nodeP)
	{
		fwrite (&nodeP->n,4,1,fp);
		fwrite (&nodeP->r,1,1,fp);
		fwrite (&nodeP->g,1,1,fp);
		fwrite (&nodeP->b,1,1,fp);
		unsigned char reservedchar = 0;
		fwrite (&reservedchar,1,1,fp);
		Asura_Assert(!feof(fp) && !ferror(fp),"");
	}
	
	int rv = 1;
	if (ferror (fp))
		rv = 0;
	fclose (fp);
	#if DB_PRINT > 1
	--num_spc;
	your_printf("%s... done\n",Spaces());
	#endif
	return rv;
}
// load
HHHashHistogram::HHHashHistogram (char const * fname)
: HHHashFunction (HH_INITIALTABLESIZE)
, table_size (HH_INITIALTABLESIZE)
, table (new HHTableEntry [HH_INITIALTABLESIZE] )
, number_of_entries (0)
, max_number_of_entries (HH_INITIALTABLESIZE)
, entries (new HHNode [HH_INITIALTABLESIZE])
, r_nodeP (0)
{
	FILE * fp = fopen (fname,"rb");
	if (!fp) return;
	
	unsigned long id [2];
	fread (id,8,1,fp);
	if (id [0] != * (unsigned long const *)"HIST" || id [1] != * (unsigned long const *)"GRAM")
	{
		fclose (fp);
		return;
	}
	
	unsigned long version;
	fread (&version,4,1,fp);
	
	unsigned long reserved;
	fread (&reserved,4,1,fp);
	
	if (version>0)
	{
		fclose (fp);
		return;
	}
	
	unsigned long num_entries;
	fread (&num_entries,4,1,fp);
	
	#if DB_PRINT > 1
	your_printf("%sLoading histogram with %lu entries ...\n",Spaces(),num_entries);
	++num_spc;
	#endif
	
	if (num_entries > max_number_of_entries)
		Expand (num_entries);
		
	#if DB_PRINT > 0
	int num_null = 0;
	#endif

	while (num_entries--)
	{
		HHPixel hhp;
		fread (&hhp.n,4,1,fp);
		#if DB_PRINT > 0
		if (!hhp.n) ++num_null;
		#endif
		fread (&hhp.r,1,1,fp);
		fread (&hhp.g,1,1,fp);
		fread (&hhp.b,1,1,fp);
		unsigned char reservedchar;
		fread (&reservedchar,1,1,fp);
		
		Asura_Assert(!feof(fp) && !ferror(fp),"");
		
		HHNode * * nodePP = table [ HashFunction (hhp) ];
		HHNode * nodeP = *nodePP;
		*nodePP = & entries [number_of_entries];
		*(HHPixel *)*nodePP = hhp;
		(*nodePP)->next = nodeP;
		++number_of_entries;
	}
	#if DB_PRINT > 0
	if (num_null)
		your_printf("%sHistogram %s with %d zero entries\n",Spaces(),fname,num_null);
	#endif

	fclose (fp);
	
	#if DB_PRINT > 1
	--num_spc;
	your_printf("%s... done\n",Spaces());
	#endif
}
// new
HHHashHistogram::HHHashHistogram ()
: HHHashFunction (HH_INITIALTABLESIZE)
, table_size (HH_INITIALTABLESIZE)
, table (new HHTableEntry [HH_INITIALTABLESIZE] )
, number_of_entries (0)
, max_number_of_entries (HH_INITIALTABLESIZE)
, entries (new HHNode [HH_INITIALTABLESIZE])
, r_nodeP (0)
{
}
// delete
HHHashHistogram::~HHHashHistogram ()
{
	#if DB_PRINT > 1
	your_printf("%sDeleting %lu-entry histogram ...\n",Spaces(),max_number_of_entries);
	++num_spc;
	#endif
	if (table) delete [] table;
	if (entries) delete [] entries;
	#if DB_PRINT > 1
	--num_spc;
	your_printf("%s... done\n",Spaces());
	#endif
}

// copy
HHHashHistogram::HHHashHistogram (HHHashHistogram const & hhhh2)
: HHHashFunction (hhhh2)
, table_size (hhhh2.table_size)
, table (new HHTableEntry [hhhh2.table_size] )
, number_of_entries (hhhh2.number_of_entries)
, max_number_of_entries (hhhh2.max_number_of_entries)
, entries (new HHNode [hhhh2.max_number_of_entries])
, r_nodeP (0)
{
	#if DB_PRINT > 1
	your_printf("%sCopying histogram with %lu entries ...\n",Spaces(),hhhh2.number_of_entries);
	++num_spc;
	#endif
	
	HHTableEntry const * table_fromP = hhhh2.table;
	HHTableEntry * table_toP = table;
	long offset = (_void_*)entries - (_void_*)hhhh2.entries;
	
	Asura_Assert((_void_*)hhhh2.entries + offset == (_void_*)entries,"");
	HHNode * test_entries = hhhh2.entries;
	test_entries += offset;
	Asura_Assert(test_entries == entries,"");
	
	for (int i = table_size; i; --i, ++table_fromP, ++table_toP)
	{
		if ((HHNode *)*table_fromP)
		{
			*(HHNode * *)*table_toP = (HHNode *)((_void_*)(HHNode *)*table_fromP + offset);
		}
	}
	HHNode const * entries_fromP = hhhh2.entries;
	HHNode * entries_toP = entries;
	for (int i = number_of_entries; i; --i, ++entries_fromP, ++entries_toP)
	{
		*entries_toP = *entries_fromP;
		if (entries_toP->next)
			entries_toP->next = (HHNode *)((_void_*)entries_toP->next + offset);
	}
	
	#if DB_PRINT > 1
	--num_spc;
	your_printf("%s... done\n",Spaces());
	#endif
}								   
// construct new histogram with shades for lighting
HHHashHistogram::HHHashHistogram (HHHashHistogram const & hhhh2, const struct Lighting_Style * lights)
: HHHashFunction (hhhh2)
, table_size (hhhh2.table_size)
, table (new HHTableEntry [hhhh2.table_size] )
, number_of_entries (0)
, max_number_of_entries (hhhh2.max_number_of_entries)
, entries (new HHNode [hhhh2.max_number_of_entries])
, r_nodeP (0)
{
	Asura_Assert(lights->num_blocks,"");
	Asura_Assert(lights->darken_flag,"");
	
	INIT_INSERT_SHADES_MACRO(lights)
	
	#if DB_PRINT > 1
	your_printf("%sContructing lit histogram, starting with %lu entries ...\n",Spaces(),hhhh2.number_of_entries);
	++num_spc;
	#endif
	
	HHNode * nodeP = hhhh2.entries;
	for (int i=hhhh2.number_of_entries; i; --i, ++nodeP)
	#if 0
	{
		LS_Block const * blkP = lights->blocks;
		unsigned int n_blks = lights->num_blocks-1;
		
		unsigned int intensity = ((unsigned int)nodeP->r*77 + (unsigned int)nodeP->g*151 + (unsigned int)nodeP->b*29)*_max_i_8; // in 8.24 fixed
		unsigned int num_steps = (intensity >> (24-lights->palette_num_shades_shift)) + 1;
		unsigned int step_size = (1<<16)/num_steps;
		
		unsigned int r_unit = nodeP->r * step_size;
		unsigned int g_unit = nodeP->g * step_size;
		unsigned int b_unit = nodeP->b * step_size;
		
		unsigned int r_add = UnsignedMulFixed(r_unit, blkP->grad);
		unsigned int g_add = UnsignedMulFixed(g_unit, blkP->grad);
		unsigned int b_add = UnsignedMulFixed(b_unit, blkP->grad);
		
		unsigned int rf=1<<15,gf=1<<15,bf=1<<15,xf=step_size;
		
		unsigned int lc = num_steps;
		do
		{
			if (xf > blkP->end_input && n_blks)
			{
				unsigned int part = UnsignedMulFixed(blkP->end_input - (xf - step_size), blkP->grad);
				rf += nodeP->r * part;
				gf += nodeP->g * part;
				bf += nodeP->b * part;
				
				++ blkP;
				Asura_Assert(xf > blkP->begin_input,"");
				-- n_blks;
				r_add = UnsignedMulFixed(r_unit, blkP->grad);
				g_add = UnsignedMulFixed(g_unit, blkP->grad);
				b_add = UnsignedMulFixed(b_unit, blkP->grad);
				
				part = UnsignedMulFixed(xf - blkP->begin_input, blkP->grad);
				rf += nodeP->r * part;
				gf += nodeP->g * part;
				bf += nodeP->b * part;
			}
			else
			{
				rf += r_add;
				gf += g_add;
				bf += b_add;
			}
			xf += step_size;
			
			unsigned int rt = rf, gt = gf, bt = bf;
			RGB_CLAMP_MACRO (rt,gt,bt,255<<16);
			operator += (HHPixel ((unsigned char)(rt>>16), (unsigned char)(gt>>16), (unsigned char)(bt>>16),nodeP->count));
		}
		while (--lc);
	}
	#else
	
	#define MY_ADD_COLOUR(r,g,b,n) operator += (HHPixel((r),(g),(b),(n)))
	
	INSERT_SHADES_INTO_PALETTE_MACRO(lights,nodeP->r,nodeP->g,nodeP->b,nodeP->n,MY_ADD_COLOUR)
	
	#endif
	
	#if DB_PRINT > 1
	--num_spc;
	your_printf("%s... done\n",Spaces());
	#endif
}
// construct new histogram, passing all entries through a function
HHHashHistogram::HHHashHistogram (HHHashHistogram const & hhhh2, HHPixel (*pix_manip_func)(HHPixel const &) )
: HHHashFunction (HH_INITIALTABLESIZE)
, table_size (HH_INITIALTABLESIZE)
, table (new HHTableEntry [HH_INITIALTABLESIZE] )
, number_of_entries (0)
, max_number_of_entries (HH_INITIALTABLESIZE)
, entries (new HHNode [HH_INITIALTABLESIZE])
, r_nodeP (0)
{
	HHNode * nodeP = hhhh2.entries;
	for (int i=hhhh2.number_of_entries; i; --i, ++nodeP)
		operator += (pix_manip_func(*nodeP));
}
// copy over
HHHashHistogram & HHHashHistogram::operator = (HHHashHistogram const & hhhh2)
{
	if (&hhhh2 != this)
	{
		*(HHHashFunction *)this = (hhhh2);
		
		#if DB_PRINT > 1
		your_printf("%sAssigning to histogram with %lu entries ...\n",Spaces(),hhhh2.number_of_entries);
		++num_spc;
		#endif
		
		if (table) delete [] table;
		if (entries) delete [] entries;
		
		table_size = hhhh2.table_size;
		table = new HHTableEntry [hhhh2.table_size];
		number_of_entries = hhhh2.number_of_entries;
		max_number_of_entries = hhhh2.max_number_of_entries;
		entries = new HHNode [hhhh2.max_number_of_entries];
		r_nodeP = 0;
		
		HHTableEntry const * table_fromP = hhhh2.table;
		HHTableEntry * table_toP = table;
		long offset = (_void_*)entries - (_void_*)hhhh2.entries;
		
		Asura_Assert((_void_*)hhhh2.entries + offset == (_void_*)entries,"");
		HHNode * test_entries = hhhh2.entries;
		test_entries += offset;
		Asura_Assert(test_entries == entries,"");
		
		for (int i = table_size; i; --i, ++table_fromP, ++table_toP)
		{
			if ((HHNode *)*table_fromP)
			{
				*(HHNode * *)*table_toP = (HHNode *)((_void_*)(HHNode *)*table_fromP + offset);
			}
		}
		HHNode const * entries_fromP = hhhh2.entries;
		HHNode * entries_toP = entries;
		for (int i = number_of_entries; i; --i, ++entries_fromP, ++entries_toP)
		{
			*entries_toP = *entries_fromP;
			if (entries_toP->next)
				entries_toP->next = (HHNode *)((_void_*)entries_toP->next + offset);
		}
		#if DB_PRINT > 1
		--num_spc;
		your_printf("%s... done\n",Spaces());
		#endif
	}
	return *this;
}

// add pixel
HHHashHistogram & HHHashHistogram::operator += (HHPixel const & hhp)
{
	if (!hhp.n) return *this;
start:
	HHNode * * nodePP = table [ HashFunction (hhp) ];
	HHNode * nodeP;
	while (0!= (nodeP = *nodePP))
	{
		if (hhp != *nodeP)
			nodePP = &nodeP->next;
		else
		{
			*nodeP += hhp.n;
			return *this;
		}
	}
	if (number_of_entries >= max_number_of_entries)
	{
		Expand ();
		goto start;
	}
	else
	{
		*nodePP = & entries [number_of_entries];
		nodeP = *nodePP;
		*(HHPixel *)nodeP = hhp;
		nodeP->next = 0;
		++number_of_entries;
	}
	return *this;
}
// add histogram
HHHashHistogram & HHHashHistogram::operator += (HHHashHistogram const & hhhh2)
{
	#if DB_PRINT > 1
	your_printf("%sAdding %lu entries to %lu ...\n",Spaces(),hhhh2.number_of_entries,number_of_entries);
	++num_spc;
	#endif
	
	HHNode * nodeP = hhhh2.entries;
	for (int i=hhhh2.number_of_entries; i; --i, ++nodeP)
		operator += (*nodeP);
	#if DB_PRINT > 1
	--num_spc;
	your_printf("%s... done\n",Spaces());
	#endif
	
	return *this;
}

// add histogram from file
HHHashHistogram & HHHashHistogram::operator += (char const * fname)
{
	FILE * fp = fopen (fname,"rb");
	if (!fp) return *this;
	
	unsigned long id [2];
	fread (id,8,1,fp);
	if (id [0] != * (unsigned long const *)"HIST" || id [1] != * (unsigned long const *)"GRAM")
	{
		fclose (fp);
		return *this;
	}
	
	unsigned long version;
	fread (&version,4,1,fp);
	
	unsigned long reserved;
	fread (&reserved,4,1,fp);
	
	if (version>0)
	{
		fclose (fp);
		return *this;
	}
	
	unsigned long num_entries;
	fread (&num_entries,4,1,fp);
	
	#if DB_PRINT > 1
	your_printf("%sAdding %lu-entry file to %lu entries ...\n",Spaces(),num_entries,number_of_entries);
	++num_spc;
	#endif
	
	#if DB_PRINT > 0
	int num_null = 0;
	#endif

	while (num_entries--)
	{
		HHPixel hhp;
		fread (&hhp.n,4,1,fp);
		#if DB_PRINT > 0
		if (!hhp.n) ++num_null;
		#endif
		fread (&hhp.r,1,1,fp);
		fread (&hhp.g,1,1,fp);
		fread (&hhp.b,1,1,fp);
		unsigned char reservedchar;
		fread (&reservedchar,1,1,fp);
		
		operator += (hhp);
	}
	#if DB_PRINT > 0
	if (num_null)
		your_printf("%sHistogram %s with %d zero entries\n",Spaces(),fname,num_null);
	#endif

	fclose (fp);
	
	#if DB_PRINT > 1
	--num_spc;
	your_printf("%s... done - now %lu entries\n",Spaces(),number_of_entries);
	#endif
	
	return *this;
}

void HHHashHistogram::Expand (unsigned long esize)
{
	Asura_Assert (table_size,"");
	
	// new table size
	unsigned long new_table_size = table_size << 1;
	unsigned long new_max_entries = new_table_size << 1;
	while (esize > new_max_entries)
	{
		new_table_size <<= 1;
		new_max_entries <<= 1;
	}
	
	#if DB_PRINT > 1
	your_printf("%sExpanding %lu-entry histogram to accomodate %lu entries ...\n",Spaces(),number_of_entries,new_max_entries);
	++num_spc;
	#endif
	
	// new hash function
	HHHashFunction::Resize (new_table_size);
	// new table
	HHTableEntry * new_table = new HHTableEntry [new_table_size];
	// new entries
	HHNode * new_entries = new HHNode [new_max_entries];
	
	HHNode const * entries_fromP = entries;
	HHNode * entries_toP = new_entries;
	// copy the node data from the old table to the new,
	// reconstructing the links between nodes for the new table
	// using the new hash function
	for (int i = number_of_entries; i; --i, ++entries_fromP, ++entries_toP)
	{
		*(HHPixel *)entries_toP = *(HHPixel const *)entries_fromP;
		HHNode * * newnodePP = new_table [HashFunction (*entries_fromP)];
		entries_toP->next = *newnodePP;
		*newnodePP = entries_toP;
	}
	
	delete [] table;
	delete [] entries;
	table = new_table;
	entries = new_entries;
	table_size = new_table_size;
	max_number_of_entries = new_max_entries;

	#if DB_PRINT > 1
	--num_spc;
	your_printf("%s... done\n",Spaces());
	#endif
}

HHFileReader::HHFileReader (char const * fname)
{
	fp = fopen (fname,"rb");
	if (fp)
	{
		unsigned long id [2];
		fread (id,8,1,fp);
		if (id [0] != * (unsigned long const *)"HIST" || id [1] != * (unsigned long const *)"GRAM")
		{
			fclose (fp);
			fp = 0;
			return;
		}
		
		fread (&version,4,1,fp);
		
		unsigned long reserved;
		fread (&reserved,4,1,fp);
		
		if (version>0)
		{
			fclose (fp);
			fp = 0;
			return;
		}
		
		fread (&num_entries,4,1,fp);
	}
}

HHFileReader::~HHFileReader ()
{
	if (fp) fclose (fp);
}


