/* Author: Adam Bielasty, H1SID
 * integer.cpp
 *
 * Biblioteka Boost Integer
 *
 * Aplikacja prezentuje możliwości biblioteki boost::integer
 */

#include <iostream>

/* Dołączenie biblioteki integer z pakietu bibliotek boost'a */
#include <boost/integer.hpp>

/* Biblioteka typedef-ów standardowych typów integer, np. int32_t lub uint_least16_t */
#include <boost/cstdint.hpp>

/* Biblioteka, dzięĸi której możemy zbadać właściwości znanych typów integer */
#include <boost/integer_traits.hpp>

/* Dzięki tej bibliotece można posługiwać się szablonami masek typu integer */
#include <boost/integer/integer_mask.hpp>

/* Dostarcza metod do wybierania max i min z dwóch wartości typu integer */
#include <boost/integer/static_min_max.hpp>


// Biblioteka boost::integer zapewnia m.in.:
// - zapewnienienie ustalonych z integer typów, takich jak: boost::int8_t, boost::uint8_t, itd., które
//   zawsze mają ten sam zakres na dowolnej platformie i dowolnym kompilatorze C++
// - dostarcza typów integer z minimalną szerokością np. boost::int_least8_t, boost::int_fast8_t, itd.
// - zapewnia szablon klas do określenia odpowiednich typów integer w programowaniu generycznym

// Biblioteka boost::integer rozwiązuje podstawowy problem w C++ z typami integer, których długość(wielkość)
// zależały od platformy i stosowanego kompilatora. Jeśli użytkownik chce pisać programy przenośne, powinien
// wykorzystać bibliotekę boost::integer

using namespace std;

int main()
{
	// w bibliotece boost::integer występuje kilka specjalnych typów integer

	// pierwsza grupa typów integer to:
	// int_least8_t
    // int_least16_t
    // int_least32_t
    // uint_least8_t
    // uint_least16_t
    // uint_least32_t
	// w nazwie typu int_least#_t lub uint_least#_t znak # to minimalny rozmiar (w sensie bitowym)
	//reprezentowanej liczby przez ten typ. Dla typów int_least#_t reprezentowana liczba posiada znak(signed)
	// a dla typów uint_least#_t ta sama liczba jest przedstawiana bez znaku (unsigned). Jest tu analogia do np. int i unsigned int

	int_least8_t il8_g1 = 122; //ma zakres od -128 do 127, liczba signed reprezentowana na minimum 8 bitach
	int_least16_t il16_g1 = 122; // liczba signed reprezentowana na minimum 16 bitach
	int_least32_t il32_g1 = 122; // liczba signed reprezentowana na minimum 32 bitach
	uint_least8_t ul8_g1 = 122; // liczba unsigned reprezentowana na minimum 8 bitach
	uint_least16_t ul16_g1 = 122; // liczba unsigned reprezentowana na minimum 16 bitach
	uint_least32_t ul32_g1 = 122; // liczba unsigned reprezentowana na minimum 32 bitach

	// wypisanie dlugosci (bitowo) poszczególnych typów integer
	std::cout<<"Typ int_least8_t ma " << boost::integer_traits<int_least8_t>::digits <<" bitów szerokości" << std::endl; //7
	std::cout<<"Typ int_least16_t ma " << boost::integer_traits<int_least16_t>::digits <<" bitów szerokości" << std::endl; //15
	std::cout<<"Typ int_least32_t ma " << boost::integer_traits<int_least32_t>::digits <<" bitów szerokości" << std::endl; //31

	std::cout<<"Typ uint_least8_t ma " << boost::integer_traits<uint_least8_t>::digits <<" bitów szerokości" << std::endl; //8
	std::cout<<"Typ uint_least16_t ma " << boost::integer_traits<uint_least16_t>::digits <<" bitów szerokości" << std::endl; //16
	std::cout<<"Typ uint_least32_t ma " << boost::integer_traits<uint_least32_t>::digits <<" bitów szerokości" << std::endl; //32


	std::cout << "|" << ul8_g1 << "|" << ul16_g1 << "|" << ul32_g1 << std::endl; // |z|122|122

	// druga grupa typów integer to:
    // int_fast8_t
    // int_fast16_t
    // int_fast32_t
    // uint_fast8_t
    // uint_fast16_t
    // uint_fast32_t
	// grupa tych typów jest bardzo podobna do poprzedniej grupy typów, z tym że jest to najszybsza
	// grupa typów integer jakie w ogóle są.

	int_fast8_t il8_g2 = 122; //ma zakres od -128 do 127, liczba signed reprezentowana na minimum 8 bitach
	int_fast16_t il16_g2 = 123; // liczba signed reprezentowana na minimum 16 bitach
	int_fast32_t il32_g2 = 122; // liczba signed reprezentowana na minimum 32 bitach
	uint_fast8_t ul8_g2 = 122; // liczba unsigned reprezentowana na minimum 8 bitach
	uint_fast16_t ul16_g2 = 122; // liczba unsigned reprezentowana na minimum 16 bitach
	uint_fast32_t ul32_g2 = 122; // liczba unsigned reprezentowana na minimum 32 bitach

	// wypisanie dlugosci (bitowo) poszczególnych typów integer
	std::cout<<"Typ int_fast8_t ma " << boost::integer_traits<int_fast8_t>::digits <<" bitów szerokości" << std::endl; //7
	std::cout<<"Typ int_fast16_t ma " << boost::integer_traits<int_fast16_t>::digits <<" bitów szerokości" << std::endl; //31
	std::cout<<"Typ int_fast32_t ma " << boost::integer_traits<int_fast32_t>::digits <<" bitów szerokości" << std::endl; //31

	std::cout<<"Typ uint_fast8_t ma " << boost::integer_traits<uint_fast8_t>::digits <<" bitów szerokości" << std::endl; //8
	std::cout<<"Typ uint_fast16_t ma " << boost::integer_traits<uint_fast16_t>::digits <<" bitów szerokości" << std::endl; //32
	std::cout<<"Typ uint_fast32_t ma " << boost::integer_traits<uint_fast32_t>::digits <<" bitów szerokości" << std::endl; //32

	// trzecia grupa typów integer to:
	// intmax_t
	// uintmax_t
	// reprezentuja dowolny typ integer znakowy (signed) lub bezznakowy (unsigned).

	intmax_t itmax = 2;
	uintmax_t utmax = 2;

	//typ intmax_t ma zawsze maksymalną dostępną szerokość (na komputerze 64 bitowym) jest to 63 bity
	std::cout<<"Typ intmax_t ma " << boost::integer_traits<intmax_t>::digits <<" bitów szerokości" << std::endl; //

	//typ uintmax_t ma zawsze maksymalną dostępną szerokość (na komputerze 64 bitowym) jest to 64 bity
	std::cout<<"Typ uintmax_t ma " << boost::integer_traits<uintmax_t>::digits <<" bitów szerokości" << std::endl; //

	// różne statyczne metody klasy integer_traits:

	//metoda zwraca wartość true dla typów integer
	std::cout << "is_specialized(): " << boost::integer_traits<int_least8_t>::is_specialized << std::endl;

	//metoda zwraca wartość minimalną dla danego typu
	std::cout << "min(): " << (int)boost::integer_traits<int_least8_t>::min() << std::endl;

	//metoda zwraca wartość maksymalną dla danego typu
	std::cout << "max(): " << (int)boost::integer_traits<int_least8_t>::max() << std::endl;

	//metoda zwraca liczbę bitów, jaką zajmuje zadany typ integer (bez znaku)
	std::cout << "digits: " << boost::integer_traits<int_least8_t>::digits << std::endl;

	//metoda zwraca true, jeśli dany typ jest typem signed
	std::cout << "is_signed: " << boost::integer_traits<int_least8_t>::is_signed << std::endl;

	//metoda zwraca true dla typów integer
	std::cout << "is_integer: " << boost::integer_traits<int_least8_t>::is_integer << std::endl;

	//metoda zwraca true dla typów integer
	std::cout << "is_integer: " << boost::integer_traits<double>::is_integer << std::endl;

	//dostępnych jest także wiele innych przydatnych metod i pól



	// W bibliotece Boost Integer jest dostępnych kilka szablonów klas do łatwiejszego manipulowania typami
	// Są to szablony klas:
	// boost::int_t;
	// boost::uint_t;
	// boost::int_max_value_t;
	// boost::int_min_value_t;
	// boost::uint_value
	// poniżej przykład wykorzystania:

	// np. boost::int_t<liczba_bitow>::least moja_wartosc;
	boost::int_t<7>::least moja_wartosc;
	// można też boost::int_t<7>::fast
	// liczba bitów to parametr i nie jest to wartość liczbowa, a oszacowana liczba bitów, którą zajmowałaby jakaś liczba.
	// Np. można sobie wyobrazić, że jakieś liczby nie będą przekraczały długości 12 bitów, więc podajemy w nawiasach liczbę <12>
	// a otrzymujemy, że najbardziej optymalnym typem będzie typ który będzie miał 15 bitów (signed)
	std::cout << "Szerokosc bitowa: " << boost::integer_traits<boost::int_t<14>::least>::digits << std::endl; //15
	std::cout << "Szerokosc bitowa: " << boost::integer_traits<boost::int_t<12>::least>::digits << std::endl; //15
	std::cout << "Szerokosc bitowa: " << boost::integer_traits<boost::int_t<3>::least>::digits << std::endl; //7
	std::cout << "Szerokosc bitowa: " << boost::integer_traits<boost::int_t<27>::least>::digits << std::endl; //31

	// Najmniejszy znakowy typ integer, dla zadanej wartości liczbowej. Argumentem powinna być liczba dodatnia.
	// Liczbę 407 można reprezentować na 15 bitach (signed)
	std::cout << "Szerokosc bitowa dla danej wartosci: " << boost::integer_traits<boost::int_max_value_t<407>::fast>::digits << std::endl; //15
	// Najmniejszy znakowy typ integer, dla zadanej wartości liczbowej. Argumentem powinna być liczba ujemna.
	// Liczbę -407 też można reprezętować na 15 bitach i będzie to najmniejsza optymalna szerokość bitowa dla tej liczby - minimalna.
	std::cout << "Szerokosc bitowa dla danej wartosci: " << boost::integer_traits<boost::int_min_value_t<-407>::fast>::digits << std::endl; //15


	// Biblioteka Boost Integer dostarcza także narzędzi ułatwiających maskowanie bitów
	// Są dwa szablony wspierające maskowanie bitów: pierwszy szablon działa na pojedynczych bitach
	// drugi szablon na grupach bitów

	//boost::high_bit_mask_t<pozycja_bitu>, gdzie
	//hdm.high_bit reprezentuje liczbe 2^liczba_bitów
	boost::high_bit_mask_t<7> hbm;
	boost::high_bit_mask_t<7>::least hbmleast;
	std::cout << "hbm.high_bit: " << static_cast<int>(hbm.high_bit) << std::endl; //128 - 8 bit (najbardziej znaczący)
	std::cout << "hbm.bit_position: " << static_cast<int>(hbm.bit_position) << std::endl; //7 - siódma pozycja licząc od zera
	std::cout << "hbm.high_bit_fast : " << static_cast<int>(hbm.high_bit_fast) << std::endl; //128

	hbmleast = 320;
	//zastosowanie maski
	// 128 bitowo:  1 0 0 0 0 0 0 0
	// 320 bitowo:1 0 1 0 0 0 0 0 0
	// po |(or)  :  1 1 0 0 0 0 0 0
	// po &(and) :  0 0 0 0 0 0 0 0
	hbmleast |= hbm.high_bit;
	std::cout << "hbmleast |(or): " << static_cast<int>(hbmleast) << std::endl; //192
	hbmleast = 320;
	hbmleast &= hbm.high_bit;
	std::cout << "hbmleast &(and): " << static_cast<int>(hbmleast) << std::endl; //0

	// drugi zestaw narzędzi obsługujący maski
	// boost::low_bits_mask_t<liczba_bitów>, gdzie
	// lbm.sig_bits reprezentuje liczbe 2^(liczba_bitów-1)
	boost::low_bits_mask_t<5> lbm;
	boost::low_bits_mask_t<5>::fast lbmleast;
	std::cout << "lbm.sig_bits: " << static_cast<int>(lbm.sig_bits) << std::endl; //127
	std::cout << "lbm.bit_count: " << static_cast<int>(lbm.bit_count) << std::endl; //7
	std::cout << "lbm.sig_bits_fast : " << static_cast<int>(lbm.sig_bits_fast) << std::endl; //127


	lbmleast = 251;
	//zastosowanie maski ([] - znak)
	// 251 bitowo: 1 1 1 1 1 0 1 1
	// 31  bitowo: 1 0 0 1 1 1 1 1
	// po |(or)  : 1 1 1 1 1 1 1 1
	// po &(and) : 1 0 0 1 1 0 1 1
	lbmleast |= lbm.sig_bits_fast;
	std::cout << "lbmleast |(or): " << static_cast<int>(lbmleast) << std::endl; //255
	lbmleast = 251;
	lbmleast &= lbm.sig_bits_fast;
	std::cout << "lbmleast &(and): " << static_cast<int>(lbmleast) << std::endl; //27

	// statyczna metoda zwracająca większą liczbę
	unsigned const long wartosc1 = 23;
	unsigned const long wartosc2 = 45;
	unsigned long max_size = boost::static_unsigned_max<wartosc1, wartosc2>::value;
	std::cout << "max_size: " << static_cast<int>(max_size) << std::endl; //27
	//dostępne są również metody
    //static_signed_min;
    //static_signed_max;
    //static_unsigned_min;

	return 0;
}
