//Marian Szczykulski. Nr Albumu 200930
//Opis kontenera hash_set. Wersja dla Visual Studio 2008
#include<hash_set>
#include<iostream>
#include <functional> //Tu znajduje sie szablon less
#include<string>
using namespace std;
//Funktor do porównywania dwóch liczb. Zamiast niego można użyć less<int>
struct eqstr
{
	bool operator()(int a, int b) const
	{
		return a < b;
	}
};
//Własna klasa, ktorej objekty zostana wrzucone do zbioru hash_set
class Obj
{
	private:
		int _w;
		std::string _txt;
	public:
		Obj():_w(0),_txt(""){}
		Obj(int w,std::string txt):_w(w), _txt(txt){}
		//Takie rzutowanie jest konieczne w przypadku uzywania
		//szablonu hash_compare (wymaga tego funkcja hash_value)
		operator size_t() const
		{
			return sizeof(Obj);
		}
		friend ostream& operator<< (ostream& os, const Obj& o);
		friend bool operator<(const Obj & o1, const Obj& o2);
};
bool operator <(const Obj& o1, const Obj& o2){
	return o1._w < o2._w;
}

ostream& operator<< (ostream& os,const Obj& o){
		os << o._w << " " << o._txt;
		return os;
}
//Definicja typu hash_set opartego na typie int
typedef stdext::hash_set<int, stdext::hash_compare<int, eqstr>> hs_int;
//Definicja typu hash_set opartego własnym zdefiniowanym typie
typedef stdext::hash_set<Obj, stdext::hash_compare<Obj, std::less<Obj> > > hs_obj;

//Funkcje pomocnicze(szablony)
//T to typ zbioru hash_set np  __gnu_cxx::hash_set<int, __gnu_cxx::hash<int>, eqstr>
//os strumien do ktorego beda przesylane dane
//zb zbior hash_set
template<class T> std::ostream & wyswietl_zbior(std::ostream & os, T & zb)
{
	os << "Elementy zbioru:"<< std::endl;
	for(T::const_iterator it = zb.begin(); //Ustawienie iteratora na poczatek zbioru
			it != zb.end();	//Dopuki nie doszlismy do konca zbioru
				it++)		//Przejdz do nastepnego elementu
	{
		os << *it << std::endl; //Zapis do strumienia
	}
	return os;
}
//Prezentacja dodawania elementu do zbioru
//T to typ zbioru hash_set np  __gnu_cxx::hash_set<int, __gnu_cxx::hash<int>, eqstr>
//T2 to typ jaki przetrzymywany jest w zbiorze hash_set np int
//zb zbior | element wstawiany element
template<class T, class T2> void dodaj_do_zbioru(T & zb, T2 element)
{
	zb.insert(element); //Dodawanie elementu do zbioru
}
//Sprawdzanie czy dany element znajduje się w zbiorze
//T to typ zbioru hash_set np  __gnu_cxx::hash_set<int, __gnu_cxx::hash<int>, eqstr>
//T2 to typ jaki przetrzymywany jest w zbiorze hash_set np int
//zb zbior | element wstawiany element
template<class T, class T2> bool czy_w_zbiorze(const T &zb, T2 element)
{
	//Wyszukanie elementu za pomocą standardowego algorytmu find
	//Zwraca on iterator na szukany element lub zb.end() jeżeli
	//elementu nie ma w zbiorze
	T::const_iterator it = zb.find(element);
	return it != zb.end();
}
//Usuwa element o podanych parametrów ze zbioru hash_set
//Usuwa element o podanych parametrów ze zbioru hash_set
//T to typ zbioru hash_set np  __gnu_cxx::hash_set<int, __gnu_cxx::hash<int>, eqstr>
//T2 to typ jaki przetrzymywany jest w zbiorze hash_set np int
//zb zbior | element wstawiany element
template<class T, class T2> void usun_ze_zbioru(T &zb, T2 element)
{
	//Wyszukanie elementu w zbiorze
	T::const_iterator it = zb.find(element);
	//Jeżeli element jest w zbiorze to usun go
	if(it != zb.end())
		zb.erase(it);
}
//Wyczyszczenie całego zbioru. Mozna to zrobić co najmniej na dwa sposoby
template<class T> void czysc_zbior(T & zb)
{
	//zb.erase(zb.begin(), zb.end());
	zb.clear();
}
int main()
{
	/*	Test działania zbioru hash_set dla typów wbudowanych */
	/*  na przykładzie int									 */
	//Utworz zbior
	std::cout<<"\tHASH_SET oparty na typie wbudowanym int\n";
	hs_int zbiorLiczb;
	//Dodaj kilka elementow
	std::cout<<"DODAWANIE NOWYCH ELEMENTOW\n";
	dodaj_do_zbioru<hs_int, int>(zbiorLiczb, 1);
	dodaj_do_zbioru<hs_int, int>(zbiorLiczb, 20);
	dodaj_do_zbioru<hs_int, int>(zbiorLiczb, 11);
	dodaj_do_zbioru<hs_int, int>(zbiorLiczb, 17);
	//Wyswietl utworzony zbior
	wyswietl_zbior<hs_int>(std::cout, zbiorLiczb);
	//Usun ze zbioru dwie liczby (ktore sa w tym zbiorze)
	std::cout<<"USUWANIE ELEMENTOW 17 i 1\n";
	usun_ze_zbioru<hs_int, int>(zbiorLiczb, 17);
	usun_ze_zbioru<hs_int, int>(zbiorLiczb, 1);
	//Wyswietl otrzymany zbior (powinno byc 20, 11)
	wyswietl_zbior<hs_int>(std::cout, zbiorLiczb);
	//Usun liczbe ktorej nie ma w zbiorze (nic się nie powinno stac)
	std::cout<<"USUWANIE ELEMENTU 1111\n";
	usun_ze_zbioru<hs_int, int>(zbiorLiczb, 1111);
	//Wyswietl otrzymany zbior (powinno byc 20, 11)
	wyswietl_zbior<hs_int>(std::cout, zbiorLiczb);
	//Dodaj do zbioru kilka liczb
	std::cout<<"DODAWANIE: 111, 202, 131, 117\n";
	dodaj_do_zbioru<hs_int, int>(zbiorLiczb, 111);
	dodaj_do_zbioru<hs_int, int>(zbiorLiczb, 202);
	dodaj_do_zbioru<hs_int, int>(zbiorLiczb, 131);
	dodaj_do_zbioru<hs_int, int>(zbiorLiczb, 117);
	wyswietl_zbior<hs_int>(std::cout, zbiorLiczb);
	//Wyszukaj w zbiorze liczby 131
	std::cout<<"131 " << (czy_w_zbiorze<hs_int, int>(zbiorLiczb, 131)? "jest w zbiorze\n": "nie ma w zbiorze\n");
	//Wyszukaj w zbiorze liczby 132
	std::cout<<"132 " << (czy_w_zbiorze<hs_int, int>(zbiorLiczb, 132)? "jest w zbiorze\n": "nie ma w zbiorze\n");
	//Te elementy juz sa wiec nie zostana dodane
	dodaj_do_zbioru<hs_int, int>(zbiorLiczb, 131);
	dodaj_do_zbioru<hs_int, int>(zbiorLiczb, 131);
	dodaj_do_zbioru<hs_int, int>(zbiorLiczb, 131);
	wyswietl_zbior<hs_int, hs_int::const_iterator >(std::cout, zbiorLiczb);
	//Liczba elementow o kluczu rownym 131
	int il = zbiorLiczb.count(131);
	std::cout<<"Liczba elementow o kluczu 131: "<<il <<std::endl;
	il = zbiorLiczb.size();
	std::cout<<"Rozmiar zbioru: "<<il <<std::endl;
	//Wyczyszczenie załego zbioru
	czysc_zbior<hs_int>(zbiorLiczb);
	wyswietl_zbior<hs_int>(std::cout, zbiorLiczb);
	/* Test zbioru hash_set zawierajacego objekty typu zdefiniowanego	*/
	/* przez użytkownika(programiste)	*/
	std::cout<<"\tHASH_SET oparty na typie zdefiniowanym przez programiste (obj)\n";
	hs_obj zbiorObjektow;
	//Dodawanie objektow
	std::cout<<"DODAWANIE NOWYCH ELEMENTOW\n";
	Obj objekt1(12, "Pierwszy");
	dodaj_do_zbioru<hs_obj, Obj>(zbiorObjektow, objekt1);
	objekt1 = Obj(99,"Drugi");
	dodaj_do_zbioru<hs_obj, Obj>(zbiorObjektow, objekt1);
	objekt1 = Obj(423,"Trzeci");
	dodaj_do_zbioru<hs_obj, Obj>(zbiorObjektow, objekt1);
	objekt1 = Obj(34,"Czwarty");
	dodaj_do_zbioru<hs_obj, Obj>(zbiorObjektow, objekt1);
	objekt1 = Obj(912,"Piaty");
	dodaj_do_zbioru<hs_obj, Obj>(zbiorObjektow, objekt1);
	objekt1 = Obj(10,"Szosty");
	dodaj_do_zbioru<hs_obj, Obj>(zbiorObjektow, objekt1);
	objekt1 = Obj(123,"Siodmy");
	dodaj_do_zbioru<hs_obj, Obj>(zbiorObjektow, objekt1);
	wyswietl_zbior<hs_obj>(std::cout, zbiorObjektow);
	//Usuwanie objektow
	std::cout<<"USUWANIE ELEMENTOW 423 i 10\n";
	objekt1 = Obj(423,"Trzeci");
	usun_ze_zbioru<hs_obj, Obj>(zbiorObjektow, objekt1);
	objekt1 = Obj(10,"Szosty");
	usun_ze_zbioru<hs_obj, Obj>(zbiorObjektow, objekt1);
	wyswietl_zbior<hs_obj>(std::cout, zbiorObjektow);
	//Wyszukaj w zbiorze objektu (912, Piaty)
	objekt1 = Obj(912,"Piaty");
	std::cout<<"(912, Piaty) " << (czy_w_zbiorze<hs_obj, Obj>(zbiorObjektow, objekt1)? "jest w zbiorze\n": "nie ma w zbiorze\n");
	//Wyszukaj w zbiorze objektu (10,"Szosty")
	objekt1 = Obj(10,"Szosty");
	std::cout<<"(10, Szosty) " << (czy_w_zbiorze<hs_obj, Obj>(zbiorObjektow, objekt1)? "jest w zbiorze\n": "nie ma w zbiorze\n");
	//Wyczyszczenie załego zbioru
	czysc_zbior<hs_obj>(zbiorObjektow);
	wyswietl_zbior<hs_obj>(std::cout, zbiorObjektow);
	return 0;
}