
/*
 * Prosta biblioteka implementujaca tablice asocjacyjna w ktorej
 * mozna pomiescic troche liczb rzeczywistych lub wiecej. Model
 * hasza z rozwiazywaniem kolizji metoda lancuchowa.
 * [c] piotao, 20060107
 */

#ifndef __HASH__
#define __HASH__

// ta stala okresla wielkosc tablicy, ktorej uzyjemy do indeksowania danych hasza
#define HASH_MAX 10000

// dolaczamy dodatkowe biblioteki:

// pozyteczne funkcje, ktorych nie ma zbyt wiele (w sumie jest tylko jedna)
#include "Utils.h"

// obsluge listy do przechowywania kolizyjnych danych
#include "HashLst.h"

// obsluge funkcji do wyliczania indeksu danych
#include "HashFnc.h"

// oto struktura, ktora bedzie odpowiedzialna za przechowywanie wszystkich
// danych hasza - wszystkie informacje siedza wewnatrz niej.

typedef struct {
  int size;              // rozmiar hasza liczony w elementeach zapamietanych
	int index;             // ostatnio wyliczony indeks przez funkcje haszujace
  List* hash[HASH_MAX];  // tablica z danymi (dokladniej: z listami danych)
  int (*func)(double);   // wskaznik na funkcje haszujaca
} Hash;                  // nazwa struktury


// inicjalizacja struktury naszego hasza - wywolanie tej funkcji jest potrzebne
// tylko raz na poczatku, wtedy do hasza wpisywane sa poprawne poczatkowe
// wartosci

void HashInit( Hash *H, int (*func)(double) ){
  int i = 0;
	print("Inicjalizacja hasza\n");
  if(H){
    H->size = 0;  // wielkosc ustawiamy na 0
    while( i<HASH_MAX ){ H->hash[i++] = NULL; } //zerujemy tablice z listami danych
    H->func = func;  // ustawiamy funkcje haszujaca na podana przez parametr
  }
}

// wstawienie liczby do hasza - haszem jest H, a liczba jest y.
void HashInsert( Hash *H, double y ){
	print("Wstawienie do hasza liczby: %1.3f\n",y);
	if(H){  // jezeli zdefiniowana jest struktura pamietajaca hasz
		if(H->func){  // jezeli ustawiony jest wskaznik na funkcje haszujaca
			H->index = (H->func)(y);     // oblicz indeks elementu y i zapamietaj w H->index
			print("Obliczony indeks: %i\n",H->index);
			if(H->hash[H->index] == NULL){  // jezeli pozycji [index] nic nie bylo, to wstaw
				print("O, hasz byl pusty!\n");
				H->hash[H->index] = newList(H->index,y);  // zrob w tym miejscu nowa liste i wstaw element
				print("Liczba dodana w pozycji %i, a rozmiar teraz wynosi: %i\n",H->index, H->hash[H->index]->size);
			}
			else{  // jezeli juz w tym miejscu byla jakas dana wstawiona
				print("Dodanie liczby do istniejacej pozycji (KOLIZJA)\n");
				addList(H->hash[H->index],y);  // dodaj do listy kolejna wartosc
				print("Liczba elementow kolidujacych: %i\n",H->hash[H->index]->size);
			}
			H->size++;  // zwieksz rozmiar hasza
			print("Calkowita liczba elementow wstawionych: %i\n\n",H->size);
		}
	}
}


// przeszukiwanie hasza - moze wydawac sie niepotrzebne, ale czesto chcemy po
// prostu wiedziec, czy cos w haszu juz mamy, czy nie.

int HashSearch( Hash *H, double y ){
	print("\nSzukanie w haszu liczby %1.3f\n",y);
	if(H){ // jezeli mamy hasz
		if(H->func){  // jezeli mamy funkcje haszujaca
			H->index = (H->func)(y); // obliczamy indeks elementu
			print("Obliczony indeks: %i\n",H->index);
			if(H->hash[H->index] != NULL){  // jezeli w tym indeksie w tablicy sa dane, szukamy w liscie
				return searchList(H->hash[H->index],y); // i zwracamy wynik szukania - 1 lub 0
			}
			else{
				print("Takiej liczby hasz jeszcze nie widzial, indeks wskazuje na brak listy danych\n");
			}
		}
	}
	print("\n");
	return 0;  // jezeli jeszcze tu jestemy, zwracamy 0, bo nic nie znaleziono
}


// na koniec trzeba posprzatac - ta procedura usunie wszystkie dane pamietane
// wewnatrz hasza (wszystkie listy, itp.). Zwykle sprzatanie, nie ma sie czym
// podniecac. Musimy to zrobic na koncu programu, albo gdy hasz juz nie jest
// potrzebny.  Jezeli korzystamy ze struktury hasza przydzielonej dynamicznie,
// to jej pamiec zwolnic trzeba bedzie juz samodzielnie.
void HashFree(Hash *H){
	print("Usuwanie hasza z pamieci\n");
	if(H){
		for(int i=0; i<HASH_MAX; i++){
			if(H->hash[i] != NULL) H->hash[i] = delList(H->hash[i]);
		}
		H->size = 0;
	}
}

#endif

