//  File: hash.C
//
//  Implementation of hash tables

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "hash.h"

//=======================================================================
// Functions for this file only

// Determine if the given number is prime.
//
int IsPrime(int n) {
   int d, sq ;

   // Silly cases

   if (n <= 0 ) return 0 ;
   if (n == 1) return 1 ;
   if (n % 2 == 0) return 0 ;

   // check all numbers up to square root of n

   sq = sqrt(n) + 1 ;   // round up in case of floating point error
   for (d = 3 ; d <= sq ; d++) {
      if (n % d == 0) return 0 ;
   }

   return 1 ;       // no divisors found
}


// Crash if new return NULL
//
void CrashOnNull(void *ptr, char* mesg) {
   if (ptr == NULL) {
      fprintf(stderr, "CrashOnNull: %s\n", mesg) ;
      exit(1) ;
   }
}


//=======================================================================
// Member functions for HashTable class


// Magic Number for minimum table size
//
#define MIN_TSIZE 100


// Hash Table Constructor.
// Note: there is no default constructor, since the size of
//   hash table is required.

HashTable::HashTable(int size) {
   int i ;

   // Table size < MIN_TSIZE is ridiculously small

   if (size < MIN_TSIZE) size = MIN_TSIZE ;

   // Find prime number between size and 2*size.
   Tsize = 0 ;
   for (i = size ; i <= 2*size ; i++) {
      if (IsPrime(i)) {
         Tsize = i ;
         break ;
      }
   }

   // Sanity check for table size
   if (Tsize <= MIN_TSIZE) {
      fprintf(stderr, "Internal error: could not find prime table size\n") ;
      exit(1) ;
   }

   // Initialize data members
   count = 0 ;

   Table = new List* [Tsize] ;
   CrashOnNull(Table, "could not create new table") ;

   for (i = 0 ; i < Tsize ; i++) {
      Table[i] = NULL ;
   }
}


HashTable::~HashTable() {       // Destructor ;
  int i ;

  for (i = 0 ; i < Tsize ; i++) {
     delete Table[i] ;  // call List destructor
  }

  delete [] Table ;     // delete array of pointers
}


void HashTable::Insert(const ListItem& x) {
   int index ;

   index = hash(x.ssn) ;    // Hash by ssn

   // Create list if no list at this index
   if (Table[index] == NULL) {
      Table[index] = new List ;
      CrashOnNull(Table[index], "could not create new list") ;
   }

   count++ ;
   Table[index]->Append(x) ;
}


ListItem *HashTable::Find(hash_t key) {
   int index ;
   position pos ;

   index = hash(key) ;

   // Empty list means no such item
   if (Table[index] == NULL) return NULL ;

   pos = Table[index]->Locate(ListItem(key)) ;
   return Table[index]->ItemAt(pos) ;
}


void HashTable::Delete(hash_t key) {
   int index ;
   position pos ;

   index = hash(key) ;

   // Empty list means no such item
   if (Table[index] == NULL) return ;

   count-- ;
   pos = Table[index]->Locate(ListItem(key)) ;
   Table[index]->Delete(pos) ;

}


void HashTable::dump() {
   int i, c ;

   // Report internal constants
   //
   printf("\n\n***************Begin Dump***************\n") ;
   printf("HashTable: Tsize = %d, count = %d\n", Tsize, count) ;

   // Print out list at each non-empty index
   //
   for (i = 0 ; i < Tsize ; i++) {
      if (Table[i] == NULL) continue ;  // skip NULL entries

      if (c = Table[i]->Count() !=  0) {
         printf("\nHash Table entries at index = %d, count = %d\n", i, c) ;
         Table[i]->Print() ;
      }
   }
   printf("****************End Dump****************\n\n\n") ;
}


void HashTable::stats() {
   int c, i, max, min, sum, nz ;
   float avg, load ;

   if (count == 0) {
      printf("\n\nEmpty Hash Table!!!\n") ;
      return ;
   }

   printf("\n\n***************Begin Stats***************\n") ;
   printf("Table size = %d\n", Tsize) ;
   printf("Number of items = %d\n", count) ;

   load = ( (float) count) / Tsize ;
   printf("Load Factor = items/size = %6.4f\n", load) ;

   // Collect collision stats

   min = count + 1 ;    // bigger than any list size
   max = 0 ;
   nz = 0 ;
   sum = 0 ;

   for (i = 0 ; i < Tsize ; i++) {
      if (Table[i] == NULL) {
         min = 0 ;
      } else {
         nz++ ;
         c = Table[i]->Count() ;
         if (c > max) max = c ;
		 if (c < min) min = c ;
         sum = sum + c ;
      }
   }
   avg = ((float) sum) / Tsize ;

   printf("Smallest linked list length: %d\n", min) ;
   printf("Largest linked list length: %d\n", max) ;
   printf("Average linked list length: %5.2f\n", avg) ;
   printf("Average nubmer of collisions: %5.2f\n", ((float) sum)/nz) ;
   printf("\n****************End Stats****************\n\n\n") ;
}


int HashTable::hash(hash_t key) {
   long int temp ;

   //  hash function h(x) = x % Tsize
   //

   temp = key % Tsize ;
   if (temp >= 0) return temp ;

   // Arithmetic overflow?
   fprintf(stderr, "Problem with negative hash index\n") ;
   exit(1) ;

   return 0 ;  // make the compiler shut up
}
