#include <string>
#include <iostream>
#include <iomanip>

using namespace std;

class TMatice
{
	double **m;
	int r,s;
	int p,w;
	void alok(int r, int s);
public:
	TMatice();
	TMatice(int r, int s);
	TMatice(int r, int s, double p);
	TMatice(TMatice &X);
	~TMatice();
	void set(int r,int s, double p);
	double get(int r,int s);
	int getr(void);
	int gets(void);
	void setPW(int prec, int width);
	void vypis(ostream &os);
	void Get(istream &is);
	void fsoucet(TMatice X);
	TMatice soucet(TMatice X);
	int size(void);
	TMatice rozdil(TMatice X);
	float determinant (void);
	TMatice soucin(TMatice X);

	TMatice operator+ (TMatice &M);
	TMatice operator= (TMatice &M);
	bool operator== (TMatice &M);
	TMatice operator- (void);  //unární mínus, proto je bez parametru
	float operator~ (void);

	friend istream& operator>> (istream& is, TMatice &m);
	friend ostream& operator<< (ostream& os, TMatice &m);
	
	TMatice TMatice::transpozice();
	TMatice TMatice::inverze();
	void TMatice::inverze2();
};

TMatice TMatice::operator+ (TMatice &M)
{
	if ((this->r==M.r)&&(this->s==M.s))
	{
	TMatice result(r,s);
	for (int i = 0; i < r; i++)
		for (int j = 0; j < s; j++)
			result.set(i,j,this->m[i][j]+M.get(i,j));
	return result;
	}
}
bool TMatice::operator== (TMatice &M)
{
	if ((this->r == M.getr())&&(this->s==M.gets()))
	{
		for(int i = 0; i < r; i++)
			for (int j = 0; j < s; j++)
				if (this->m[i][j]!=M.get(i,j)) return false;
		return true;
	}
}
TMatice TMatice::operator= (TMatice &M)
{
	if ((r==0)&&(s==0))
	{
		alok(M.getr(),M.gets());
		this->r = M.getr();
		this->s = M.gets();
		p = 2;
		w = 5;
	}
	else if ((r!=M.getr())||s!=M.gets())
	{
		for (int i = 0; i < r; i++)
			delete[] m[i];
		delete[] m;

		alok(M.getr(),M.gets());
		this->r = M.getr();
		this->s = M.gets();
		p = 2;
		w = 5;
	}
	for (int i = 0; i < r; i++)
		for (int j = 0; j < s; j++)
			this->m[i][j]=M.get(i,j);
	return *this;
}
TMatice TMatice::operator- (void)  //unární mínus
{
	TMatice result(r,s);
	for (int i = 0; i < r; i++)
		for (int j = 0; j < s; j++)
			result.set(i,j,-this->m[i][j]);
	return result;
}
float TMatice::operator~ (void)  //unarni determinant
{
	return this->determinant();  //kdyby fungovala funkce determinant
}
TMatice::TMatice()
	{
		r = 0;
		s = 0;
		for (int i = 0; i < r; i++) m[i] = NULL;
		p = 2;
		w = 5;
	}
TMatice::TMatice(int r, int s)
	{
		alok(r,s);
		this->r = r;
		this->s = s;
		p = 2;
		w = 5;
	}
TMatice::TMatice(int r, int s, double p)
	{
		alok(r,s);
		this->r = r;
		this->s = s;
		this->p = 2;
		this->w = 5;
		for (int i = 0; i < r; i++)
			for (int j = 0; j < s; j++)
				m[i][j] = p;
	}
TMatice::TMatice(TMatice &X)
{
	this->r = X.getr();
	this->s = X.gets();
	alok(r,s);
	p = 2;
	w = 5;
	for (int i = 0; i < r; i++)
		for (int j = 0; j < s; j++)
			m[i][j] = X.get(i,j);
}
TMatice::~TMatice()
	{
		if ((r!=0)&&(s!=0))  //nastavit lepsi podminku, aby poznal jestli je matice neprazdna
		{
			for (int i = 0; i < r; i++) delete[] m[i];
			delete[] m;
		}
	}
void TMatice::alok(int r, int s)
{
	m = new double*[r];
	for (int i = 0; i < r; i++) m[i] = new double[s];
}
void TMatice::set(int r,int s, double p)
{
	if((this->r>0)&&(this->s>0)) m[r][s] = p;
}

double TMatice::get(int r,int s)
{
	if ((this->r>0)&&(this->s>0)) return m[r][s];
}
int TMatice::getr(void)
{
	if (r>0) return r;
}
int TMatice::gets(void)
{
	if (s>0) return s;
}
void TMatice::vypis(ostream &os)
{
	//cout << h << endl;
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < s; j++)
			os << fixed << setprecision(4) << setw(13) << m[i][j];
		os << endl;
	}
	os << endl;
}
void TMatice::Get(istream &is)
{
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < s; j++)
		{
			//os << "Zadej prvek m << i << "," << j << ":";
			is >> m[i][j];
		}
	}
}
void TMatice::fsoucet(TMatice X)
{
	if ((this->r == X.r)&&(this->s == X.s))
	{
		for (int i = 0; i < this->r; i++)
			for (int j = 0; j < this->s; j++)
				this->m[i][j] += X.get(i,j);
	}
}
TMatice TMatice::soucet(TMatice X)
{
	TMatice C(this->r, this->s);
	if ((this->r == X.getr())&&(this->s == X.gets()))
	{
		for (int i = 0; i < this->r; i++)
			for (int j = 0; j < this->s; j++)
				C.set(i,j,this->m[i][j] += X.get(i,j));
	}
	return C;
}
int TMatice::size(void)
{
	if ((r>0)&&(s>0))
		return sizeof(m[0][0])*r*s;
}
TMatice TMatice::rozdil(TMatice X)
{
	TMatice C(this->r, this->s);
	if ((this->r == X.getr())&&(this->s == X.gets()))
	{
		for (int i = 0; i < this->r; i++)
			for (int j = 0; j < this->s; j++)
				C.set(i,j,this->m[i][j] -= X.get(i,j));
	}
	return C;
}
float TMatice::determinant()   //nefunguje
{
	if ((r>0)&&(s>0)&&(r==s))
	{
		double a,b,d;
		int ia, ib;
		d = 0;
		for (int j = 0; j < s; j++)
		{			
			for (int i = 0; i < r-1; i++)
			{
				if (i==0) a=m[i][i+j];
				ia = i+j+1;
				if (ia >= r) ia = ia - r;
				a *= m[i+1][ia];
				d += a;
			}
		}
		for (int j = 0; j < s; j++)
		{			
			for (int i = 0; i < r-1; i++)
			{
				if (i==0) b=m[r-1-i][i+j];
				ib = i+j+1;
				if (ib >= r) ib = ib - r;
				b *= m[r-1-i][ib];
				d -= b;
			}
		}
		return d;

	}else return -9999;
}
TMatice TMatice::soucin(TMatice X)
{
	if (this->s == X.getr())
	{
		TMatice C(this->r, X.gets());
		double a;
		for (int i = 0; i < r; i++)
		{
			for (int k = 0; k < X.gets(); k++)
			{
				a = 0;
				for(int j = 0; j < s; j++)
				{
					a += this->m[i][j]*X.get(j,k);
				}
				C.set(i,k,a);
			}
		}
		return C;
	}
}
void TMatice::setPW(int prec, int width)
{
	p = prec;
	w = width;
}
ostream& operator<< (ostream& os, TMatice &m)
{
	for (int i = 0; i < m.getr(); i++)
	{
		for (int j = 0; j < m.gets(); j++)
			os << setprecision(m.p) << setw(m.w) << setiosflags(ios::showpoint) << m.get(i,j);
		os<<endl;
	}
	return os;
}
istream& operator>> (istream& is, TMatice &m) //spratelena funkce, pokud ji napisu do casti definice tridy, potom ma pristup k privat promennym
{
	//double a;
	for (int i = 0; i < m.getr(); i++)
	{
		for (int j = 0; j < m.gets(); j++)
		{
			//is >> a;
			is >> m.m[i][j];
		}
	}
	return is;
}
TMatice TMatice::transpozice()
{
	TMatice C(s,r);

	for (int i = 0; i < r; i++)
		for (int j = 0; j < s; j++)
			C.set(j,i,m[i][j]);
	
	return C;
}
TMatice TMatice::inverze()						// inverzi lze udělat jen ze čtvercových matic -> používám všude "r" (je to jedno)
{
	double t;
	
	TMatice tmp(r, 2*s);

	for(int i = 0; i < r; i++)
		for(int j = 0; j < r; j++)
			tmp.set(i, j, m[i][j]);
		
	// přesměrování do vnořené funkce -> inverze matice tmp
	tmp.inverze2();

	TMatice C(r,r);
	
	int k;
	for (int i = 0; i < r; i++)
	{
		k = 0;
		for (int j = r; j < 2*r; j++)
		{
			C.set(i,k,tmp.m[i][j]);
			k++;
		}
	}
	return C;
}
void TMatice::inverze2()
{
	// provádí inverzi s tím, že matice má nyní rozměr r*(2*r)
	for (int i = 0; i < r; i++)
	{
		for (int j = r; j < 2*r; j++)
		{
			if (i == j-r)
				m[i][j] = 1;
			
			else
				m[i][j] = 0;
		}
	}

	for (int i = 0; i < r; i++)
	{
		double t = m[i][i];
		
		for (int j = i; j < 2*r; j++)
			m[i][j]=m[i][j]/t;
      
		for (int j = 0; j < r; j++)
		{
			if (i != j)
			{
				t = m[j][i];
            
				for (int k = 0; k < 2*r; k++)
					m[j][k]=m[j][k]-t*m[i][k];
			}
		}
	}
}