#include <string>
#include <map>
#include <iostream>

using namespace std;

class Okolina
{
public:
	double VrednostPromenljive( const string& s ) const
		{
		map<string,double>::const_iterator 
			f = _Promenljive.find(s);
		if( f != _Promenljive.end() )
			return f->second;
		else{
			// ovde bi trebalo da se napravi izuzetak
			return 0;
			}
		}
		
	bool DefinisanaPromenljiva( const string& s ) const
		{
		map<string,double>::const_iterator 
			f = _Promenljive.find(s);
		return f != _Promenljive.end();
		}
		
	void DodajPromenljivu( const string& s, double v )
		{ _Promenljive[s] = v; }
		
private:
	map<string,double> _Promenljive;	
};

class Izraz
{
public:
	virtual ~Izraz()
		{}
	virtual double Vrednost( const Okolina& o ) const = 0;
	virtual void Ispisi( ostream& ostr ) const = 0;
	virtual Izraz* Kopija() const = 0;
	virtual Izraz* Uprosti( const Okolina& o ) const = 0;
	virtual bool JesteKonstanta() const
		{ return false; }
	virtual Izraz* IzvodPo( const string& s ) const = 0;
};

ostream& operator << ( ostream& ostr, const Izraz& i )
{
	i.Ispisi( ostr );
	return ostr;
}

class Konstanta : public Izraz
{
public:
	Konstanta( double d )
		: _Vrednost(d)
		{}

	double Vrednost( const Okolina& o ) const
		{ return _Vrednost; }

	void Ispisi( ostream& ostr ) const
		{ ostr << _Vrednost; }
		
	Konstanta* Kopija() const
		{ return new Konstanta(*this); }

	Izraz* Uprosti( const Okolina& o ) const
		{ return Kopija(); }

	bool JesteKonstanta() const
		{ return true; }
		
	Izraz* IzvodPo( const string& s ) const
		{ return new Konstanta(0); }
	
private:
	double _Vrednost;
};

class Promenljiva : public Izraz
{
public:
	Promenljiva( const string& s )
		: _Naziv(s)
		{}

	double Vrednost( const Okolina& o ) const
		{ return o.VrednostPromenljive( _Naziv ); }

	void Ispisi( ostream& ostr ) const
		{ ostr << _Naziv; }
		
	Promenljiva* Kopija() const
		{ return new Promenljiva(*this); }
		
	Izraz* Uprosti( const Okolina& o ) const
		{
		if( o.DefinisanaPromenljiva(_Naziv) )
			return new Konstanta( o.VrednostPromenljive(_Naziv));
		else
			return Kopija(); 
		}

	Izraz* IzvodPo( const string& s ) const
		{ return new Konstanta( s==_Naziv ? 1 : 0); }
		
private:
	string _Naziv;
};

class BinarniOperator : public Izraz
{
public:
	BinarniOperator( Izraz* i1, Izraz* i2 )
		: _I1(i1), _I2(i2)
		{}
	
	~BinarniOperator()
		{
		delete _I1;
		delete _I2;
		}
		
	BinarniOperator( const BinarniOperator& z )
		: _I1( z._I1->Kopija() ), 
		  _I2( z._I2->Kopija() )
		{}
		
	BinarniOperator& operator = ( const BinarniOperator& z )
		{
		if( this != &z ){
			delete _I1;
			delete _I2;
			_I1 = z._I1->Kopija();
			_I2 = z._I2->Kopija();
			}
		return *this;
		}

	double Vrednost( const Okolina& o ) const
		{ return Izracunaj( _I1->Vrednost(o), _I2->Vrednost(o)); }

	Izraz* Uprosti( const Okolina& o ) const
		{
		Izraz* i1 = _I1->Uprosti(o);
		Izraz* i2 = _I2->Uprosti(o);
		if( i1->JesteKonstanta() && i2->JesteKonstanta() ){
			Izraz* r = new Konstanta( Izracunaj( i1->Vrednost(o), i2->Vrednost(o)) );
			delete i1;
			delete i2;
			return r;
			}
		else
			return NapraviNovi( i1, i2 );
		}

	void Ispisi( ostream& ostr ) const
		{ ostr << "( " << Naziv() << ' ' << *_I1 << ' ' << *_I2 << " )"; }
		
protected:
	virtual double Izracunaj( double x, double y ) const = 0;
	virtual Izraz* NapraviNovi( Izraz* i1, Izraz* i2 ) const = 0;
	virtual string Naziv() const = 0;
	const Izraz* Levi() const
		{ return _I1; }
	const Izraz* Desni() const
		{ return _I2; }

private:
	Izraz *_I1, *_I2;
};

class Zbir : public BinarniOperator
{
public:
	Zbir( Izraz* i1, Izraz* i2 )
		: BinarniOperator( i1, i2 )
		{}

	Zbir* Kopija() const
		{ return new Zbir(*this); }

	Izraz* IzvodPo( const string& s ) const
		{ return new Zbir( Levi()->IzvodPo(s), Desni()->IzvodPo(s)); }
	
protected:
	double Izracunaj( double x, double y ) const
		{ return x + y; }

	Izraz* NapraviNovi( Izraz* i1, Izraz* i2 ) const 
		{ return new Zbir( i1, i2 ); }
		
	string Naziv() const
		{ return "PLUS"; }
};

class Razlika : public BinarniOperator
{
public:
	Razlika( Izraz* i1, Izraz* i2 )
		: BinarniOperator( i1, i2 )
		{}

	Razlika* Kopija() const
		{ return new Razlika(*this); }

	Izraz* IzvodPo( const string& s ) const
		{ return new Razlika( Levi()->IzvodPo(s), Desni()->IzvodPo(s)); }
	
protected:
	double Izracunaj( double x, double y ) const
		{ return x - y; }

	Izraz* NapraviNovi( Izraz* i1, Izraz* i2 ) const 
		{ return new Razlika( i1, i2 ); }
		
	string Naziv() const
		{ return "MINUS"; }
};

class Proizvod : public BinarniOperator
{
public:
	Proizvod( Izraz* i1, Izraz* i2 )
		: BinarniOperator( i1, i2 )
		{}

	Proizvod* Kopija() const
		{ return new Proizvod(*this); }
	
	Izraz* IzvodPo( const string& s ) const
		{ 
		return new Zbir( 
				new Proizvod( Levi()->IzvodPo(s), Desni()->Kopija() ),
				new Proizvod( Levi()->Kopija(), Desni()->IzvodPo(s))
				); 
		}
		
protected:
	double Izracunaj( double x, double y ) const
		{ return x * y; }

	Izraz* NapraviNovi( Izraz* i1, Izraz* i2 ) const 
		{ return new Proizvod( i1, i2 ); }
		
	string Naziv() const
		{ return "PUTA"; }
};

class Kolicnik : public BinarniOperator
{
public:
	Kolicnik( Izraz* i1, Izraz* i2 )
		: BinarniOperator( i1, i2 )
		{}

	Kolicnik* Kopija() const
		{ return new Kolicnik(*this); }
	
	Izraz* IzvodPo( const string& s ) const
		{ 
		return 
		new Kolicnik(
			new Razlika( 
				new Proizvod( Levi()->IzvodPo(s), Desni()->Kopija() ),
				new Proizvod( Levi()->Kopija(), Desni()->IzvodPo(s))
				),
			new Proizvod(
				Desni()->Kopija(),
				Desni()->Kopija()				
				)
			);
		}
		
protected:
	double Izracunaj( double x, double y ) const
		{ return x / y; }

	Izraz* NapraviNovi( Izraz* i1, Izraz* i2 ) const 
		{ return new Kolicnik( i1, i2 ); }
		
	string Naziv() const
		{ return "PODELJENO"; }
};

main()
{
	Okolina o;
	o.DodajPromenljivu( "x", 5 );

	// izraz (x/2)+(y-(3.14*z))
	Izraz* i1 = new Kolicnik( new Promenljiva("x"), new Konstanta(2));
	Izraz* i2 = new Proizvod( new Konstanta(3.14), new Promenljiva("z"));
	Izraz* i3 = new Razlika( new Promenljiva("y"), i2 );
	Izraz* i4 = new Zbir( i1, i3 );
	cout << (*i4) << " = " << i4->Vrednost( o ) << endl;
	
	Izraz* i5 = i4->Uprosti(o);
	cout << (*i4) << " -> " << (*i5) << endl;

	Izraz* i6 = i4->IzvodPo("x");
	Izraz* i7 = i6->Uprosti(o);
	cout << (*i4) << " ' = " << (*i6) << " = " << *i7 << endl;
	
	delete i4;
	delete i5;
	delete i6;
	delete i7;
	
	return 0;
}