//#pragma once
#include <iostream>
#include "temlpate_power.cpp"

using namespace std;

//definicja szablonu klasy macierzy kwadratowej ktora za parametr przyjmuje rozmiar tablicy
template <int rozmiar>
class macierz
{
private:
	int rodzaj;	// 0 identycznosciowa dla operacji dodawania; 1 identycznosciowa dla operacji mnozenia
	float tablica[rozmiar][rozmiar];

public:
	//destruktor
	~macierz(void){};
	//konstruktor bezargumentowy inicjuje tablice zerami
	macierz(void)
	{
		for( int i = 0; i < rozmiar; i++)
		{
			for (int j = 0; j < rozmiar; j++)
			{
				tablica[i][j] = 0.0;
			}
		}
	};
	//konstruktor kopiujacy
	macierz(const macierz& M)
	{
		rodzaj = M.rodzaj;
		for( int i = 0; i < rozmiar; i++)
		{
			for (int j = 0; j < rozmiar; j++)
			{
				tablica[i][j] = M.tablica[i][j];
			}
		}
		//*this = M; 
	};
	//konstruktor przyjmujący argument klasy int pozwalający tworzyć macierz jednostkową: macierz<rozmiar>(1)
	//kontruktor ten tworzy macierz wypelniona zerami oprocz przekatnej gdzie znajduja sie jedynki
	//niezbedny dla wlasciwego dzialania operacji power ktora dla zerowego wykladnika zwraca wartosc __T(0) gdzie __T jest obiektem klasy dla ktorej wywolany zostal algorym power
	macierz(int x )
	{
		rodzaj = x;
		for( int i = 0; i < rozmiar; i++)
		{
			for (int j = 0; j < rozmiar; j++)
			{
				if( i == j) 
					tablica[i][j] = (float)rodzaj;
				else 
					tablica[i][j] = 0;
			}
		}
	};
	//przeladowany operator przypisania niezbedny dla poprawnego dzialania algoryty power, ktory wykorzystuje przypisanie wczasie wykonywania iloczynow czesciowych
	macierz& operator=( const macierz& M)
	{
		rodzaj = M.rodzaj;
		for( int i = 0; i < rozmiar; i++)
		{
			for (int j = 0; j < rozmiar; j++)
			{
				tablica[i][j] = M.tablica[i][j];
			}
		}
		return *this;
	};
	//przeladowany operator mnozenia niezbedny dla poprawnego dzialania algorytmu power(T x, Integer n)
	//w algorytmie (T x, Integer n, MonoidOperation op) sami mozemy zdefiniowac ktora operacja ma byc wykonywana na obiekcie x klasy T
	macierz operator*( const macierz& M) const
	{
		macierz<rozmiar> temp;
		for( int i = 0; i < rozmiar; i++)
		{
			for (int j = 0; j < rozmiar; j++)
			{
				for (int k = 0; k < rozmiar; k++)
					temp.tablica[i][j] += tablica[i][k] * M.tablica[k][j];
			}
		}
		return temp;
	};
	//przeladowany operator dodwania - przydatny przy testowaniu klasy 
	macierz operator+( const macierz& M) const
	{
		macierz<rozmiar> temp;
		for( int i = 0; i < rozmiar; i++)
		{
			for (int j = 0; j < rozmiar; j++)
			{
				temp.tablica[i][j] = tablica[i][j] + M.tablica[i][j];
			}
		}
		return temp;
	};
	//przeladowany operator na klasie ostream; sluzy do wypisania wyniku; przydatny przy testowaniu
	friend ostream& operator<<(ostream& wy, const macierz& M)
	{
		for( int i = 0; i < rozmiar; i++)
		{
			for (int j = 0; j < rozmiar; j++)
			{
				wy << M.tablica[i][j] << "\t"; 
			}
			wy << "\n";
		}
	return wy;
	};
	//metoda klasy sluzaca do ustawiania wartosci w tablicy
	//x oraz y sa z zakresu 1.. rozmiar - konwencja bardziej naturalna 
	void wstaw(int x, int y, float wartosc)
	{
		if ( ( x > 0 ) && ( x <= rozmiar ) && ( y > 0 ) && ( y <= rozmiar ) )
			tablica[x-1][y-1] = wartosc;
	};
	//macierz& operator+=( const macierz &);
	//macierz& operator*=( const macierz &);
};

//funktor - klasa definiujaca operator mnozenia dla klasy macierz. 
//wlasciwie jest tutaj wykorzystany operator * zdefiniowany wewnatrz klasy
//ale mozna utworzyc wlasna metode mnozenia, np bardziej efektywna gdybysmy uzywali specjalnego rodzaju klas ipt. i latwo podmieniać w wywolaniu algorytmu power
template <int rozmiar>
struct mnozenie //: public binary_function<macierz<rozmiar>, macierz<rozmiar>, macierz<rozmiar>> 
{
  macierz<rozmiar> operator()(const macierz<rozmiar>& x, const macierz<rozmiar>& y) const 
  { 
	  return x * y;
  }
};

//przeladowana funkcja zwracajaca element identycznosciowy dla mnozenia macierzy
//tak jak wyzej mozna przeladowac ja wiele razy w zaleznosci od operacji mnozenia
template <int rozmiar>
macierz<rozmiar>	identity_element(mnozenie<rozmiar>) {
	return macierz<rozmiar>(1);  
};
