/* hash.cpp
 *
 * Originally borrowed from Dan's implementation of Collins' Model 1. 
 *
 * $Id: hash.cpp,v 1.3 2007/12/06 14:05:26 post Exp $
 */

#include "hash.h"
#include <string.h>

using namespace std;

HashTable::HashTable(int s)
  : buckets(new Link *[s]), sizeOfTable(s), count(0) {
  for(int i = 0; i < sizeOfTable; i++)
    buckets[i] = NULL; 
}

HashTable::~HashTable() {  
  for (int i = 0; i < sizeOfTable; i++)
    delete buckets[i];
  delete [] buckets; 
}

HashTable::Link::Link(const char *str, Link *n) {
  s = new char[strlen(str) + 1];
  strcpy(s, str);
  next = n; 
}

HashTable::Link::~Link() { 
  delete [] s; delete next; 
}

int HashTable::hash(const char *s) {
  const char *p;  // assumes 8-bit char
  unsigned int h; // assumes 32-bit int
  h = 0;
  for (p = s; *p != '\0'; p++) {
    h = (h << 4) + *p;
    h = (h ^ ((h & 0xf0000000) >> 24)) & 0x0fffffff;
  }
  return h % sizeOfTable;
}

int prime(int n) {
  for (int i = 2; i * i < n; i++)
    if (n % i == 0)
      return 0;
  return 1;
}

int nextPrime(int n) {
  int i;
  for (i = n + 1; !prime(i); i++)
    ;
  return i;
}

void HashTable::rehash() {
  int oldSize = sizeOfTable;
  int i;
  Link **oldBuckets = buckets;
  sizeOfTable = nextPrime(sizeOfTable * 2);
  buckets = new Link *[sizeOfTable];
  for (i= 0; i < sizeOfTable; i++)
    buckets[i] = NULL;
  for (i = 0; i < oldSize; i++) {
    Link *cur, *next;
    for (cur = oldBuckets[i]; cur != NULL; cur = next) {
      int newIndex = hash(cur->s);
      next = cur->next;
      cur->next = buckets[newIndex];
      buckets[newIndex] = cur;
    }
  }
  delete [] oldBuckets;
}


char* HashTable::entry(const char *str) {
  if (count >= sizeOfTable)
    rehash();
  int i = hash(str);
  Link **l = buckets + i;
  //  int c = 1;  // result of strcmp, zero if string matches
  //   This commented section assumes strings in order - broken by rehash
  //  while (*l != NULL && (c = strcmp(str, (*l)->s)) > 0)
  //    l = &((*l)->next);
  //  if (c == 0)
  //    return (*l)->s;
  while (*l)
    if (!strcmp(str, (*l)->s))
      return (*l)->s;
    else
      l = &((*l)->next);
  *l = new Link(str, *l);
  count++;  /* one more string in table */
  return (*l)->s;
}

char* StringTableEntry(const char *str) {
  static HashTable stringTable; /* default size 223 buckets */
  return stringTable.entry(str);
}
