/* 
	*Autor: Krzysztof Gomulski
	*Grupa dziekańska: 4E2
	*Nr albumu: 214414
	*Data: 20.04.2009
	*Temat: Zastosowanie biblioteki boost::bimap

		Biblioteka boost::bimap jest rozwinięciem znanej z biblioteki standadrowej: std::map.
	Podczas gdy ta druga umożliwia tylko jednostronną relacje element - klucz, w boost::bimap ta relacja jest obustrona. 
	Przechowujemy tu w mapach (prawej i lewej) elementy, które jedocześnie są kluczami dla swoich kluczy, co w dalszej części
	jest demonstrowane. Bimap jest w większości zgodna z biblioteką standardową i obiekty typu std::map można bezpiecznie
	rzutować na lewą bądź prawą część obiektu boost::bimap.
*/

#include <boost/config.hpp>

#include <iostream>
#include <string>
#include <boost/bimap.hpp>
#include <boost/bimap/unordered_set_of.hpp>
#include <boost/bimap/multiset_of.hpp>
#include <boost/bimap/list_of.hpp>
#include <boost/bimap/set_of.hpp>
#include <boost/bimap/tags/tagged.hpp>
#include <boost/bimap/support/lambda.hpp>	//potrzebne przy uzyciu metod modify

struct imie {};		//struktury uzywane jako tagi
struct numer {};
struct adres {};

using namespace boost::bimaps;
using namespace boost;
using namespace std;

/*
 * Dobrym i przejrzystym przykładem wykorzystania bimapy jest implementacja prostej książki telefoniczej,
 * która umożliwia wyszukiwanie odpowiedniego numeru telefonu znając imię osoby do której on należy i na odwrót.
 * Poniższy kod umożliwia zapoznanie sie z różnymi sposobami inicjowania bimapy, ustalania relacji między jej elementami,
 * oraz późniejszego ich wyszukiwania
 */

int main()
{
	// Najprostszy rodzaj relacji
	{
	typedef bimap<string,long> bm_tel;
	typedef bm_tel::relation pozycja;  // typ realcji łączący obiekty;
	bm_tel telefony;
	cout<<"==== Zastosowanie biblioteki boost::bimap do stworzenia książki telefonicznej ==="<<endl;
	telefony.insert(pozycja("Magda",7822467));		//wypełnienie bimapy
	telefony.insert(pozycja("Piotrek",785556788));
	telefony.insert(pozycja("Paweł",887342577));
	telefony.insert(pozycja("Monika",889725467));
	bm_tel::left_const_iterator i = telefony.left.find("Magda");// przeszukanie lewej połowy bimapy
	cout<<i->first<<" nr: "<<i->second<<endl;	
		/*i->first zwraca wartość klucza obiektu, a i->second wartość samego obiektu. 
		 * Widać to dobrze gdy będziemy przeszukiwać prawą połówke bimapy, po odpowiednim klucz*/
	bm_tel::right_iterator j = telefony.right.find(889725467);
	cout<<"1. "<<j->first<<" to nr, który ma "<<j->second<<endl;
		//To samo otrzymujemy przy wywołaniu:
	cout<<"2. "<<889725467<<" to nr, który ma "<<telefony.right.at(889725467)<<endl;
		/* Podobny efekt uzyskamy tu stosując metode project_left().Zwraca on iteretor elementu leżącego po przeciwnej stronie bimapy
			niż ten do którego odwołuje się jej argument, ale powiązanego z nim relacją bimapy.Oczywiście istnieje także metoda pproject_right()*/
	bm_tel::left_iterator tel_iter = telefony.project_left(j);
	cout<<"3. "<<tel_iter->second<<" to nr, który ma "<<tel_iter->first<<endl;
		/*w tym przypadku bimapa szereguje malejąco swoje elementy (relacja domyślna), czyli możemy wypisać alfabetycznie liste osób i ich numery*/
	cout<<endl<<"- Lista alfabetyczna:"<<endl;		
	for(bm_tel::left_const_iterator ie = telefony.left.end(), ip= telefony.left.begin();ip!=ie;++ip)
		cout<<ip->first<<" nr: "<<ip->second<<endl;
	
		/*Bimapa dostarcza także możliwości przestawiania swoich elementów, niedostępnej w std::map i niezgodnej z STL. Jednak jest to bardzo
		 * bardzo przydatna funkcja i bezpieczna. Każde wyrzucenie wyjatku (także zdefiniwanego przez programiste) przy jej wywołaniu
		 * pozostawia bimapę bez zmian. Jej jedyną wadą jest to że jest przy tym kosztowna i każda modyfikacja elenemtu powoduje dwukrotne 
		 * kopiowanie elementu.*/		
		cout<<endl<<"===Modyfikacje==="<<endl;	
	bm_tel::left_iterator i_paw = telefony.left.find("Paweł");  //wyszukanie elementu do zmiany
	bool zmiana = telefony.left.replace_data(i_paw, 8888888);	// zmiana numeru telefonu czyli wartości elementu w bimapie  
	assert(zmiana); 												//sprawdzenie poprawności zmian
	bm_tel::right_iterator i_pio = telefony.right.find(785556788); // wyszukanie elementu z drugiej strony
	zmiana = telefony.right.replace_key(i_pio,1111111111);		//zmiana klucza
	bool zmiana2 = telefony.right.replace_data(i_pio,"Zenek, ktory byl Piotrkiem"); //zmiana watrości
	assert(zmiana & zmiana2);
		/*Szybszą ale znacznie mniej bezpieczą wersją powyższej funkcji jest metoda modify_key i modify_data.
		 * Jej główną wadą jest to że przy nieudanej modyfikacji elementu jest on często usuwany z bimapy*/
	bm_tel::left_iterator i_mon = telefony.left.find("Monika");  
	zmiana = telefony.left.modify_data(i_mon, _data=22222);	 // inne przekazanie argumentów niż w replace
	assert(zmiana); 							//zalecane sprawdzenie poprawności
	cout<<endl<<"- Lista po zmianach:"<<endl;		// wypisane nowej listy
	for(bm_tel::left_const_iterator ie = telefony.left.end(), ip= telefony.left.begin();ip!=ie;++ip)
		cout<<ip->first<<" nr: "<<ip->second<<endl;
	}
		/* W kontenerze bimap możemy podobnie jak w map z biblioteki standardowej możemy mieć kontrole nad typem kolekcji
		 w niej przechowywanej. Różnica jednak polaga na tym że bimap umożliwa taką kontrole nad swoimi obiema składowymi
		 a nie tylko nad jedną. Dzięki temu przeszukiwanie bimapy jest efektywniejsze*/
	{
	cout<<endl<<"===Własne relacje==="<<endl;	
	typedef bimap< unordered_set_of<string>,list_of<long> > bm_t; 
	typedef bm_t::value_type position;
	bm_t tel;
	tel.insert(position("Magda",7822467));
	tel.insert(position("Piotrek",785556788));
	tel.insert(position("Paweł",887342577));
	tel.insert(position("Monika",889725467));
	for(bm_t::right_const_iterator ie2 = tel.right.end(), ip2= tel.right.begin();ip2!=ie2;++ip2)
		cout<<ip2->first<<" nr: "<<ip2->second<<endl;
		/*Powyższy kod spowoduje wypisanie elementów bimapy w kolejności jakiej zostały do niej dodane, ponieważ jej prawa strona jest
		 listą. Analogiczny kod dla lewej strony bimapy wypisze jej elementy w losowej kolejności*/
	}
		/*Bimapa oferuje również  możliwość zdefiniowania typu całej kolekcji niezależnie od typu elementów  w niej przechowywanej.
		 * Przy odpowiednim doborze tych typów, można znacząco poprawić szybkość przeszukiwania bimapy. Na przykład najszybciej
		 * robi to bimapa która wiąże elementu typu unordered_set,ale nie można jej iterować. ten problem rozwiązuje ustawienie par elementów w listę.*/
	{
	typedef bimap<
		unordered_set_of<string>,
		unordered_set_of<long>,
		list_of_relation			// typ relacji
		>bm_tel_list;
	typedef bm_tel_list::value_type pozycja_l;
	bm_tel_list tel_list;
	tel_list.push_back(pozycja_l("Magda",7822467));		// ze względu na typ relacji potrzeba tu użyć 'push_back' zamiast 'insert'
	tel_list.push_back(pozycja_l("Paweł",887342577));
	/*Inne typy relacji to : left_based,right_based,set_of_ralation<>,muliset_of_relation<>,unordered_set_of_relation<>,
	unordered_multiset_of_relation<>,list_of_relation,vector_of_relation,unconstrained_set_of_relation */
	}
	
	{	/*Inną przydatną funkcją jest przypisywanie do danej pary elementów odpowiedniego komentarza - tagowanie.*/
	cout<<endl<<"===Użycie tagów==="<<endl;	
	typedef bimap<
		multiset_of<tagged<string, imie> >,      // 'imie' to odpowiedni, definiowany przez programiste typ służący do identywikacji tej skłądowej bimapy 
		set_of<tagged<long,numer> >,
		with_info<tagged<string,adres> >		// określenie typu tagu dla pary elementów tej bimapy
	> bm_tel_tag;
	typedef bm_tel_tag::value_type position_tag;
	bm_tel_tag tel_tag;
	tel_tag.insert(position_tag("Magda",7822467,"Malinowa 15"));
	tel_tag.insert(position_tag("Paweł",887342577,"Akacjowa 23"));
	bm_tel_tag::map_by<imie>::iterator it=tel_tag.by<imie>().find("Paweł"); // dostęp przez iterator 
	cout<<"Pojedyńczy wpis 1 : "<<it->get<imie>()<<" nr: "<<it->get<numer>()<<" "<<it->get<adres>()<<endl;
	cout<<"Pojedyńczy wpis 2 : "<<tel_tag.by<numer>().at(7822467)<<" nr: 7822467 "<<tel_tag.by<numer>().info_at(7822467)<<endl; // dostęp bezpośredni
	}
	
}

