//  File: hash.C
//
//  Implementation of a hash table of student records

#include <iostream.h>
#include <iomanip.h>
#include <stdlib.h>
#include <math.h>
#include "hash.h"


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

// Determine if the given number is prime.
//
static int IsPrime(int n) ;

static 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
}


//=======================================================================
// 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) {
      cerr << "Internal error: could not find prime table size" << endl ;
      exit(1) ;
   }

   // Initialize data members
   count = 0 ;

   table = new StudentList[tsize] ;
}


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

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


void HashTable::insert(const StudentRecord& x) {
   int index ;

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

   count++ ;
   table[index].append(x) ;
}


bool HashTable::find(hash_t key, StudentRecord& rec) {
   int index ;
   StudentRecord data ;

   index = hash(key) ;
   if (table[index].length() == 0) return false ;

   SLIterator it(table[index]) ;    // initialize iterator

   while (it.next(data)) {
       if (data.ssn == key) {
          rec = data ;
          return true ;
       }
   }

   return false ;
}


int HashTable::extract(hash_t key) {
   int index, num ;

   index = hash(key) ;

   num = table[index].remove(key) ;
   count = count - num ;
   return num ;  // number of items removed
}


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

   // Report internal constants
   //
   cout << "\n\n***************Begin Dump***************\n" ;
   cout << "HashTable: tsize = " << tsize
        << ", count = " << count << endl ;

   // Print out list at each non-empty index
   //
   for (i = 0 ; i < tsize ; i++) {
      if (len = table[i].length() !=  0) {
         cout << "\nHash Table entries at index=" << i
              << ", length=" << len << endl ;
         table[i].print() ;
      }
   }
   cout << "****************End Dump****************\n\n" << endl ;
}


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

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

   cout << "\n\n***************Begin Stats***************\n" ;
   cout << "Table size = " << tsize << endl ;
   cout << "Number of items = " << count << endl ;

   load = ( (float) count) / tsize ;
   cout << "Load Factor = items/size = " << load << endl ;

   // 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].length() == 0) {
         min = 0 ;
      } else {
         nz++ ;
         c = table[i].length() ;
         if (c > max) max = c ;
         if (c < min) min = c ;
         sum = sum + c ;
      }
   }
   avg = ((float) sum) / tsize ;

   cout << "Smallest linked list length: " << min << endl ;
   cout << "Largest linked list length: " << max << endl ;
   cout << "Average linked list length: " << avg << endl ;
   cout << "Average nubmer of collisions: " << ((float) sum)/nz << endl ;
   cout << "\n****************End Stats****************\n\n" << endl ;
}


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?
   cerr << "Problem with negative hash index" << endl ;
   exit(1) ;

   return 0 ;  // make the compiler shut up
}
