// Zbigniew Fabijanski, H1ISIII
// ZPR - praca domowa - Zad.27 - Biblioteki boost - boost::uBLAS (reprezentacje macierzy).

#include <boost/numeric/ublas/matrix.hpp> // Macierz gesta.
#include <boost/numeric/ublas/triangular.hpp> // Macierz trojkatna.
#include <boost/numeric/ublas/symmetric.hpp> // Macierz symetryczna.
#include <boost/numeric/ublas/hermitian.hpp> // Macierz Hermitowska
#include <boost/numeric/ublas/banded.hpp> // Macierz wstegowa.
#include <boost/numeric/ublas/matrix_sparse.hpp> // Macierze rzadkie.

#include <boost/numeric/ublas/io.hpp> // Operatory << i >> dla klas macierzy.


int main () {
	// import przestrzeni nazw
	using namespace boost::numeric::ublas;
	using std::cout;
	using std::endl;

	// --------------------------------------- Matrix ---------------------------------------
	{
		// Definicja macierzy gestej o 2 wierszach i 3 kolumnach.
		matrix<double> m (2, 3); // Typ rownowazny: matrix<double, row_major, unbounded_array<double> >
		try {
			std::cout << "Proba deklaracji macierzy 3x3 zapisane w kontenerze o rozmiarze 8: " << std::endl;

			// Nieprawidlowa definicja klasy z wieksza liczba elementow (3*3=9), niz okreslono w typie kontenera przechowujacego zawartosci macierzy (8).
			// matrix<double, row_major, bounded_array<double, 8> > mb (3, 3);
			// Taki rodzaj deklaracji jest rowniez dostepny dla wszystkich innych klas macierzy.
			matrix<double, row_major, bounded_array<double, 8> > mb (3, 3); // Ta definicja wygeneruje wyjatek blednego rozmiaru przydzielonej na zawartosc macierzy sterty.
		}
		catch (std::exception &e){
			std::cout << "Exception: " << e.what() << std::endl;
		}

		// Przypisanie do macierzy wartosci kolejny liczb naturalnych - wierszami.
		for (unsigned i = 0; i < m.size1 (); ++ i) // dla kazdego wiersza macierzy
			for (unsigned j = 0; j < m.size2 (); ++ j) // dla kazdej kolumny macierzy
				m (i, j) = 3 * i + j;

		// Wyswietlenie zawartosci macierzy - przeciazony operator<< dla klas macierzy.
		std::cout << "Macierz gesta:" << std::endl << m << std::endl;
	}

	// Macierz jednostkowa
	{
		// W definicji nastepuje automatyczne inicjowanie _stalych_ wartosci macierzy.
		identity_matrix<double> m (3); // Podajemy jedyny wymiar macierzy, poniewaz macierz jednostkowa jest z definicji kwadratowa (M=N).
		std::cout << "Macierz jednostkowa:"  << std::endl << m << std::endl;

		// Odkomentowanie ponizszej linijki skutkuje bledem kompilacji, bez wzgledu na to, czy naruszymy reguly wartosci elementow macierzy jednostkowej, czy nie.
		// m(0,0) = 1;
	}

	// Macierz zerowa
	{
		// Tu rowniez inicjalizacja wszystkich elementow - na wartosci zerowe.
		zero_matrix<double> m (3,4); // Mozliwy takze konstruktor z 1 argumentem - macierz kwadratowa.
		std::cout << "Macierz zerowa:"  << std::endl << m << std::endl;
	}

	// Macierz skalarna
	{
		scalar_matrix<double> m(3,3,4); // Macierz o wymiarach 3x3, wypelniona wartosciami 4.
		std::cout << "Macierz skalarna:"  << std::endl << m << std::endl;
	}


	// --------------------------------------- Triangular Matrix ---------------------------------------
	{
		// Wypelnienie kolejnymi liczbami tylko dolnej czesci macierzy i glownej przekatnej, gdyz jest to macierz dolna,
		// a wiec elementy nad glowna przekatna maja wartosci 0 przypisane na stale w definicji.
		triangular_matrix<double, lower> tml (3, 4);
		for (unsigned i = 0; i < tml.size1 (); ++ i) // dla kazdego wiersza macierzy
			for (unsigned j = 0; j <= i; ++ j) // dla kazdej kolumny macierzy, ale dla elementow pod glowna przekatna
				tml (i, j) = 3 * i + j;
		std::cout << "Dolna macierz trojkatna:" << std::endl << tml << std::endl;

		// Definicja gornej jednostkowej macierzy trojkatnej.
		triangular_matrix<double, unit_upper> tmup (3, 3);
		// Wypelnienie liczbami tylko elementow nad glowna przekatna macierzy.
		for (unsigned i = 0; i < tmup.size1 (); ++ i) // dla kazdego wiersza macierzy
			// Wartosci na glownej przekatnej zostaly juz zainicjowane przy definicji macierzy,
			// dlatego petla wewnetrzna od j=i+1, a nie j=i.
			for (unsigned j = i+1; j < tmup.size2 (); ++ j) // dla kazdej kolumny macierzy, ale dla elementow nad glowna przekatna
				tmup (i, j) = 3 * i + j;

		std::cout << "Gorna jednostkowa macierz trojkatna:" << std::endl << tmup << std::endl;

		// Proba zmiany wartosci na glownej przekatnej macierzy jednostkowej.
		try {
			std::cout << "Proba zmiany wartosci na glownej przekatnej gornej jednostkowej macierzy trojkatnej:" << std::endl;
			tmup(0,0)=-1; // Ta linijka rzuci wyjatek, poniewaz w gornej _jednostkowej_ macierzy trojkatnej
							// mozemy zmieniac tylko wartosci pod glowna przekatna.
		}
		catch (std::exception &e){
			std::cout << "Exception: " << e.what() << std::endl;
		}

	}
	// Triangular Adaptor
	{
		// Definicja macierzy gestej - adaptowanej.
		matrix<double> m(3,3);
		for (unsigned i = 0; i < m.size1 (); ++ i) // dla kazdego wiersza macierzy
			for (unsigned j = 0; j < m.size2 (); ++ j) // dla kazdej kolumny macierzy
				m (i, j) = 3 * i + j + 1; // Przypisanie macierzy adaptowanej kolejnych liczb naturalnych.

		// Definicja dolnego adaptera macierzy gestej.
		triangular_adaptor<matrix<double>, lower> tal (m);
		std::cout << "Dolny adapter trojkatny macierzy : " << m << " to: " << std::endl;
		std::cout << tal << std::endl; // Elementy nad glowna przekatna maja wartosc 0.
	}

	// --------------------------------------- Symmetric Matrix ---------------------------------------
	{
		// Dolna macierz symetryczna. Wartosci nad glowna przekatna wskazuja na odpowiednie wartosci symetrycznych pod przekatna.
		symmetric_matrix<double, lower> sml (3, 3);
		// Wystarczy przypisac wartosci czesci macierzy po jednej stronie glownej przekatnej,
		//	gdyz elementy po drugiej stronie wskazuja na swoich symetrycznych odpowiednikow.
		for (unsigned i = 0; i < sml.size1 (); ++ i) // dla kazdego wiersza macierzy
			for (unsigned j = 0; j < /*i*/sml.size2 (); ++ j) // dla kazdej kolumny macierzy, ten sam efekt, co dla petli z warunkiem j <= 
				sml(i, j) = 3 * i + j + 1;
		// Modyfikacja wartosci id(i,j) powoduje modyfikacje takze id(j,i).
		sml(1, 0)=-1; // Ten sam efekt, co instrukcja sml(0,1)=-1;
		std::cout << "Dolna macierz symetryczna: " << std::endl << sml << std::endl;
	}
	// Symmetric Adaptor
	{
		// Macierz adaptowana.
		matrix<double> m(3,3);
		for (unsigned i = 0; i < m.size1 (); ++ i) // dla kazdego wiersza macierzy
			for (unsigned j = 0; j < m.size2 (); ++ j) // dla kazdej kolumny macierzy
				m (i, j) = 3 * i + j + 1;

		// Poniewaz adaptowanie przebiega w porzadku wierszowym, a nie kolumnowym,
		//	wartosci po gornej stronie glownej przekatnej wskazuja symetryczne wartosci spod glownej przekatnej, a nie odwrotnie.
		symmetric_adaptor<matrix<double> > sa (m); // Brak 2.argumentu klasy szablonu => wartosc domyslna (lower)

		// Dopiero tutaj widac roznice pomiedzy dolna a gorna macierza symetryczna.
		// Instrukcja zmiany wartosci adaptera zmienia oba symetryczne elementy obiektu adaptera macierzy.
		// Ale poniewaz jest to gorny adapter macierzy, w macierzy adaptowanej zmienia sie tylko element nad glowna przekatna macierzy.
		sa(1,0)=-1; // A dla macierzy adaptowanej m - ten sam efekt, co instrukcja sa(0,1)=-1;
		std::cout << "Dolny adapter symetryczny stworzonej z macierzy gestej: " << m << " to: " << std::endl;
		std::cout << sa << std::endl;
	}
	// --------------------------------------- Hermitian Matrix ---------------------------------------
	{
		// Deklaracja dolnej macierzy Hermitowskiej.
		hermitian_matrix<std::complex<double>, lower> hml (3, 3);
		for (unsigned i = 0; i < hml.size1 (); ++ i) { // dla kazdego wiersza macierzy
			for (unsigned j = 0; j < i; ++ j) // dla kazdej kolumny macierzy
				hml (i, j) = std::complex<double> (3 * i + j, 3 * i + j); // Wypelniamy macierzy liczbami zespolonymi (complex)
			hml (i, i) = std::complex<double> (4 * i, 0); // wartosci na glownej przekatnej
		}
		std::cout << "Dolna macierz Hermitowska:" << std::endl << hml << std::endl;
	}
	// Hermitian Adaptor - analogicznie do innych adapterow.

	// --------------------------------------- Banded Matrix ---------------------------------------
	{
		signed l=1, u=1; // l,u - ilosc niezerowych przekatnych pod,nad przekatna glowna
		banded_matrix<double> bm (3, 3, l, u); // l=1, u=1
		// Dla wszystkich wierszy.
		for (signed i = 0; i < signed (bm.size1 ()); ++ i) // dla kazdego wiersza macierzy
			// Dla kolumn, ktorych indeks nalezy do przedzialu [-l,u) wzgledem
			for (signed j = std::max (i - l, 0); j < std::min (i + u, signed (bm.size2 ())); ++ j)
				bm (i, j) = 3 * i + j + 1;
		std::cout << "Macierz wstegowa:" << std::endl << bm << std::endl;
		//	bm(2,0)=0; // ta instrukcja generuje wyjatek
	}
	// Banded Adaptor - analogicznie do innych adapterow.
}
