#include "hash.h"
#include <stdio.h>
#include <assert.h>

#define D	if(0)

template<class K, class V>
C hashitem<K,V> * hashtable<K,V>::getptr(C K& k) C
{ int i = getidx(k); return i>=0&&tbl[i].filled() ? &tbl[i] : 0; }

/* Getidx returns the first spot in which you could put the specified key. */
/* The result is -1 (if the table is full), or a slot that is either */
/* empty, or already has that precise key entered into it. */
template<class K, class V>
int hashtable<K,V>::getidx(C K& k) C
{
 if(n == 0)	return -1;
 assert(n > 0);
 assert(hashfunction);
 int hv = (*hashfunction)(k, n);
 C int hv0 = hv;
 for(;tbl[hv].filled() && tbl[hv].key()!=k && (hv+1)%n!=hv0;hv=(hv+1)%n)
	;
 return (hv+1)%n==hv0 ? -1 : hv;
}

template<class K, class V>
C V& hashtable<K,V>::get(C K& k) C
{ int i = getidx(k);
  assert(i>=0 && tbl[i].filled());
  return tbl[i].value();
}

template<class K, class V>
hashitem<K,V> *hashtable<K,V>::set(C K& k, C V& color)
{
 C int i = getidx(k);
 return i>=0 ? tbl[i].set(k, color) : 0;
}

template<class K, class V>
doublehash<K,V>::doublehash(int nn, int (*hashf)(C K&, int),
									int (*rhash)(C V&, int))
: hashtable<K,V>(nn, hashf), reversehash(rhash)
{ 
 rtbl = new hashitem<K,V>* [ nn];
 if(rtbl)	{
 	int i;
 	for(i=0;i<size();i++)
		rtbl[i] = 0;
	}
 }

template<class K, class V>
C hashitem<K,V> * doublehash<K,V>::r_getptr(C V& k) C
{ int i = r_getidx(k); return i>=0&&rtbl[i]&&rtbl[i]->filled() ? rtbl[i] : 0; }

/* R_getidx returns the first spot in which you could put the specified value.*/
/* The result is -1 (if the table is full), or a slot that is either */
/* empty, or already has that precise value entered into it. */
template<class K, class V>
int doublehash<K,V>::r_getidx(C V& k) C
{
 C int nn = size();
 if(nn<=0 || !rtbl)	return -1;
 unsigned hv = (*reversehash)(k, nn);
 C unsigned hv0 = hv;
 assert(rtbl && hv>=0 && hv<nn);
 for(;rtbl[hv] && rtbl[hv]->filled()
		&& rtbl[hv]->value()!=k && (hv+1)%nn!=hv0; hv=(hv+1)%nn)
	;
 return (hv+1)%nn==hv0 ? -1 : (int)hv;
}

template<class K, class V>
C K& doublehash<K,V>::r_get(C V& k) C
{ int i = r_getidx(k);
  assert(i>=0 && rtbl[i]);
  return rtbl[i]->key();
}

template<class K, class V>
C hashitem<K,V> *doublehash<K,V>::set(C K& k, C V& color)
{
 C int i_fwd = getidx(k);
 C int i_rev = r_getidx(color);

 if(i_fwd<0 && i_rev<0)	return 0;
 assert(i_fwd>=0 && i_rev>=0);

 if(tbl[i_fwd].filled() && rtbl[i_rev]==&tbl[i_fwd])	{
	assert(k==tbl[i_fwd].key() && color==tbl[i_fwd].value());
	return &tbl[i_fwd];		/* No change to the data. */
	}
 else if(!tbl[i_fwd].filled() && (!rtbl[i_rev] || !rtbl[i_rev]->filled()))	{
	rtbl[i_rev] = &tbl[i_fwd]; /* Empty, so fill it. */
	return tbl[i_fwd].set(k, color);
	}
 /* we could handle these cases, where the hash table gets rearranged,
	but it wuld be hard. */
 return 0;
}


template<class K, class V>
int hashtable<K,V>::count() C
{
 int c = 0;
 int i;
 for(i=0;i<n;i++)
	if(tbl[i].filled())
		c++;
 return c;
}

template<class K, class V>
void hash_iterator<K,V>::next()
{
 do	{
	pi++;
	} while(pi<ptop && !pi->filled());
}

template<class K, class V>
void hash_iterator<K,V>::previous()
{
 do	{
	pi--;
	} while(pi>=pbot && !pi->filled());
}

template<class K, class V>
void hash_iterator<K,V>::reset()
{
 pi = pbot-1;
 next();
}

template<class K, class V>
void hash_iterator<K,V>::end()
{
 pi = ptop;
 previous();
}

#if 0

template<class K, class V>
BOOL hashtable<K,V>::unset(C K& k)
{
 int i = getidx(k);
 if(i<0 || !tbl[i].filled())	return FALSE;

 tbl[i].unset();

 int j = (i+1)%n;
 for(;tbl[j].filled();j=(j+1)%n)
	;		/* Find first unfilled space after the hole we made. */
 if(j == (i+1)%n)
	return TRUE;

/* We have to try to fill in the hole that we just created: */
	{
	int tmp = getidx(tbl[j].key());
	if(tmp == i)	{
		tbl[i].set(tbl[j].key(), tbl[j].value());
		tbl[j].unset();
		}
	}
}
#endif

template<class K, class V>
BOOL hashtable<K,V>::rehash(int nn)
{
 BOOL ok = TRUE;
 hashtable<K,V> tmp(nn, hashfunction);
 hash_iterator<K,V> i(*this);
 for(i.reset();i.valid() && (ok=tmp.set(i.key(), i.value())!=0); i.next())
	;
 if(!ok)	return FALSE;
 steal_from(&tmp);
 return TRUE;
}

template<class K, class V>
void hashtable<K,V>::steal_from(hashtable<K,V> *p)
{
 delete [] tbl;
 tbl = p->tbl; p->tbl = 0;
 n = p->n; p->n = 0;
 hashfunction=p->hashfunction; p->hashfunction=0;
}

template<class K, class V>
hash_iterator<K,V>::hash_iterator()
{ assert(0);}

template<class K, class V>
hash_iterator<K,V>::hash_iterator(C hashtable<K,V>& a)
: p(&a), pi(p->tbl), pbot(p->tbl), ptop(&p->tbl[p->size()])
{reset();}

template<class K, class V>
hashtable<K,V>::hashtable(int nn, int(*hashf)(C K&,int))
: hashfunction(hashf), n(nn)
{tbl = new hashitem<K,V> [ nn]; if(!tbl) n = 0;}
