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

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; }
		
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; }
		
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 )
		{}
		
	Zbir& operator = ( const Zbir& z )
		{}

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

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

private:
	Izraz *_I1, *_I2;
};

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

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