
//Primer je testiran u Visual Studiu 6.0. Da bi primer radio 
//pod Linuxom sa g++ prevodiocem potrebno je uneti par izmena 
//koje su navedene na odgovarajucim mestima.

#include <iostream>
#include <math.h>
#define Pi 3.1415

using namespace std;

//Klasa Kompleks, za razliku od predhodnog primera ove klase, ima definisan unarni operator -
class Kompleks
{
public:

	Kompleks (double r=0, double i=0) : _Re(r),_Im(i)
	{}

	double Re() const
	{ return _Re; }

	double Im() const
	{ return _Im; }

	double Ro() const 
	{ return this->Moduo(); }

	double Fi() const 
	{
		if (Re())  return atan(Im()/Re());
		else return Im()<0 ? -Pi/2 : Pi/2;
	}

	double Moduo () const 
	{ return sqrt(Re()*Re()+Im()*Im()); }

	Kompleks operator +(const Kompleks& c) const
	{ return Kompleks(Re() + c.Re(), Im() + c.Im()); }

	Kompleks operator -(const Kompleks& c) const
	{ return Kompleks( Re() - c.Re(), Im() - c.Im() ); }


	Kompleks operator -() const
	{ return Kompleks( -Re(), -Im()); }

	Kompleks operator *(const Kompleks& c) const
	{
	return Kompleks( Re() * c.Re() - Im() * c.Im()
	,Re() * c.Im() + Im() * c.Re());
	}

	Kompleks operator * (const double d) const 
	{ return Kompleks (Re()*d,Im()*d); }

	Kompleks operator /(const Kompleks& c) const
	{
	return Kompleks( (Re() * c.Re() + Im() * c.Im())/(c.Re() * c.Re() + c.Im() * c.Im())
	,(Im() * c.Re() - Re() * c.Im())/(c.Re() * c.Re() + c.Im() * c.Im()));
	}

	Kompleks operator ~() const
	{ return Kompleks(Re() , -Im()); }
	
	istream& Citaj (istream &ul) 
	{
		char c, i;
		ul >> _Re >> c >> _Im >> i;
		if (c=='-')
		_Im=-_Im;
		return ul;
	}

	ostream& Pisi (ostream &izl) const 
	{
		char c = Im()<0 ? '-' : '+';
		return izl <<Re() << c << abs(Im()) << 'i';
	}

private:
	double _Re;
	double _Im;

};

istream& operator >> (istream& str, Kompleks& c)
{ return c.Citaj(str);}

ostream& operator << (ostream& str, const Kompleks& c)
{ return c.Pisi(str); }


//Sablon klase Stek
template<class T>
class Stek{
	public:
		// Da bi primer radio pod Linuxom potrebno je 
 		// sablon klase Element definisati sa parametrom T1
		// i svuda gde je u okviru klase Element
		// parametar T koristiti parametar T1 
		template <class T>
		class Element{
			public:
				const T& Vrednost()const{
				return _Vrednost;
				}

				Element *Sledeci() const{
				return _Sledeci;
				}

	friend class Stek<T>;

	private:
		Element(T V, Element *S=0):
					_Vrednost(V), _Sledeci(S){}
		T _Vrednost;
		Element *_Sledeci;
		

};

	// Konstruktor bez argumenata
	Stek(): _Pocetak(0), _BrElemenata(0)
	{}

	// Konstruktor kopije
	Stek(const Stek &S)
	{ init(S); }

	// Operator dodele
	Stek operator =(const Stek &S)
	{
		if ((*this)._Pocetak != S._Pocetak)
		{
			deinit();
			init(S);
		}
		return *this;
	}

	// Destruktor
	~Stek(){ deinit(); }

	// Stavlja element na vrh steka
	void Push(const T n)
	{
		_Pocetak = new Element<T>(n, _Pocetak);
		_BrElemenata++;
	}

	// Skida element sa vrha steka i vraca njegovu vrednost
	T Pop()
	{
	if (Empty())
		return 0;
	T n=_Pocetak->Vrednost();
	Element<T> *p=_Pocetak;
	_Pocetak=_Pocetak->Sledeci();
	delete p;
	_BrElemenata--;
	return n;
}

	// Vraca vrednost koja se nalazi na vrhu steka
	T Top() const
	{ return _Pocetak->Vrednost(); }

	// Proverava da li je stek prazan
	bool Empty()const
	{ return !_Pocetak; }

	// Vraca broj elemenata steka
	int Count()const
	{ return _BrElemenata; }

	// Funkcija koja obezbedjuje upis
	istream &Upis(istream &istr)
	{
	int BrEl;
	cout<<"Unesite br. novih elemenata steka: ";
	istr>>BrEl;
	for (int i=0; i<BrEl; i++)
		{
		T El;
		cout<<"Unesite element steka: ";
		istr>>El;
		Push(El);
		}
	return istr;
	}

	// Funkcija koja obezbedjuje ispis
	ostream &Ispis(ostream &ostr)const
	{
	for (Element<T> *p=_Pocetak; p; p=p->Sledeci())
		ostr << p->Vrednost()<<" ";
	return ostr;
	}

	// Skida dva elementa sa vrha steka, 
	// sabira njihove vrednosti i zbir
	// vraca na vrh steka
	Stek operator +()
	{
	if (_BrElemenata>=2) Push(Pop()+Pop());
	return *this;
	}

	// Sabira vrednost vrha steka sa celim brojem
	Stek operator +(const int &I)
	{
	if (_BrElemenata) Push(Pop()+I);
	return *this;
	}
	

	// Skida dva elementa sa vrha steka, 
	// mnozi njihove vrednosti i proizvod
	// vraca na vrh steka 
	Stek operator *()
	{
	if (_BrElemenata>=2) Push(Pop()*Pop());
	return *this;
	}


	// Mnozi vrednost vrha steka sa celim brojem
	Stek operator *(const int &I)
	{
	if (_BrElemenata) Push(Pop()*I);
	return *this;
	}


	// Menja znak elementa na vrhu steka
	Stek operator -()
	{
	if (_BrElemenata) Push(-Pop());
	return *this;
	}

private:
	void init(const Stek &S)
	{
		_BrElemenata=S.Count();
		Element<T> *stari=S._Pocetak;
		Element<T> *novi=0;
		while (stari)
		{
		Element<T> *temp=new Element<T>(stari->Vrednost());
		if (!novi)
			_Pocetak=novi=temp;
		else{
			novi->_Sledeci=temp;
			novi=novi->_Sledeci;
			}
		stari=stari->Sledeci();
		}
	}

	void deinit()
	{
		while (!Empty())
		{
		Element<T> *p=_Pocetak;
		_Pocetak=_Pocetak->Sledeci();
		delete p;
		}
	}

	Element<T> *_Pocetak;
	int _BrElemenata;
};

template <class T>
istream& operator >> (istream &istr, Stek<T> &S)
{ return S.Upis(istr); }

template <class T>
ostream& operator << (ostream &ostr, Stek<T> &S)
{ return S.Ispis(ostr); }

//Funkcija main ilustruje koriscenje sablona stek
main()
{
Stek<int> S1;
Stek<double> S2;
Stek<Kompleks> K1;


S1.Push(2);
S1.Push(3);
S1.Push(5);
S1.Push(7);
S1.Push(11);

cout << S1<<endl;

-S1;
cout << S1<<endl;

+S1;
cout << S1<<endl;

*S1;
cout << S1<<endl;

cin >> S1;
cout << S1 <<endl<<endl;

S2.Push(2.0);
S2.Push(3.9);
S2.Push(5.0);
S2.Push(7.00);
S2.Push(11.001);

cout << S2<<endl;

-S2;
cout << S2<<endl;

+S2;
cout << S2<<endl;

*S2;
cout << S2<<endl;


cin >> S2;
cout << S2 <<endl<<endl;


K1.Push(Kompleks(2,3));
K1.Push(Kompleks(4,11));
K1.Push(5);
K1.Push(7);

cout << K1<<endl;

-K1;
cout << K1<<endl;

+K1;
cout << K1<<endl;

K1+10;
cout <<K1<<endl;

K1*10;
cout <<K1<<endl;

*K1;
cout << K1<<endl;


cin >> K1;
cout << K1 <<endl;


return 0;
}

