#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;
};

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); }
		
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); }
		
private:
	string _Naziv;
};

class Zbir : public Izraz
{
public:
	Zbir( Izraz* i1, Izraz* i2 )
		: _I1(i1), _I2(i2)
		{}
	
	~Zbir()
		{
		delete _I1;
		delete _I2;
		}
		
	Zbir( const Zbir& z )
		: _I1( z._I1->Kopija() ), 
		  _I2( z._I2->Kopija() )
		{}
		
	Zbir& operator = ( const Zbir& 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 _I1->Vrednost(o) + _I2->Vrednost(o); }

	void Ispisi( ostream& ostr ) const
		{ ostr << "( PLUS " << *_I1 << ' ' << *_I2 << " )"; }

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

private:
	Izraz *_I1, *_I2;
};

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

	// izraz (x+2)
	Zbir* z1 = new Zbir( new Promenljiva("x"), new Konstanta(2));
	Izraz* i1 = new Zbir(*z1);
	cout << (*i1) << " = " << i1->Vrednost( o ) << endl;
	delete i1;
	delete z1;
	
	return 0;
}