#ifndef __FUNKCIJA_H__
#define __FUNKCIJA_H__

#include <cmath>
#include <iostream>
#include <string>
using namespace std;

class Funkcija
{
public:
	virtual Funkcija* izvod() = 0;
	virtual float vrednost(float x) = 0;
	virtual Funkcija* kompozicija(Funkcija* f) = 0;
	
	virtual void print(ostream& ostr) = 0;
	
	virtual ~Funkcija()
	{}

	virtual Funkcija* clone() = 0;
};

class KonstantnaFunkcija : public Funkcija
{
public:
	KonstantnaFunkcija(float b)
		: _b(b)
	{}
	
	virtual Funkcija* izvod()
	{	return new KonstantnaFunkcija(0);	}
	
	virtual float vrednost(float x)
	{	return _b;	}
	
	virtual Funkcija* kompozicija(Funkcija* f)
	{	return clone();	}

	virtual void print(ostream& ostr)
	{
		ostr<<_b;
	}

	virtual Funkcija* clone()
	{
		return new KonstantnaFunkcija(*this);
	}	

protected:
	float _b;
};


class IdentickaFunkcija : public Funkcija
{
public:
	virtual Funkcija* izvod()
	{	return new KonstantnaFunkcija(1);	}
	
	virtual float vrednost(float x)
	{	return x;	}
	
	virtual Funkcija* kompozicija(Funkcija* f)
	{	return f->clone();	}

	virtual void print(ostream& ostr)
	{
		ostr<<"x";
	}

	virtual Funkcija* clone()
	{
		return new IdentickaFunkcija(*this);
	}
};

class BinarniOperator : public Funkcija
{
public:
	BinarniOperator(Funkcija* op1, Funkcija* op2, char simbol)
		: _op1(op1), _op2(op2), _simbol(simbol)
	{}
	
	virtual ~BinarniOperator()
	{
		delete _op1;
		delete _op2;
	}
	
	virtual void print(ostream& ostr)
	{
		ostr<<'(';_op1->print(ostr);ostr<<')';
		ostr<<_simbol;
		ostr<<'(';_op2->print(ostr);ostr<<')';
	}

protected:
	Funkcija *_op1, *_op2;
	char _simbol;
};

class Plus : public BinarniOperator
{
public:
	Plus(Funkcija* op1, Funkcija* op2)
		: BinarniOperator(op1, op2, '+')
	{}
	
	virtual Funkcija* clone()
	{
		return new Plus(_op1->clone(), _op2->clone());
	}
	
	virtual Funkcija* izvod()
	{	
		return new Plus(_op1->izvod(), _op2->izvod());
	}
	
	virtual Funkcija* kompozicija(Funkcija* fun)
	{
		return new Plus(_op1->kompozicija(fun), _op2->kompozicija(fun));
	}
	
	virtual float vrednost(float x)
	{	return _op1->vrednost(x)+_op2->vrednost(x);	}
};

class Puta : public BinarniOperator
{
public:
	Puta(Funkcija* op1, Funkcija* op2)
		: BinarniOperator(op1, op2, '*')
	{}
	
	virtual Funkcija* clone()
	{
		return new Puta(_op1->clone(), _op2->clone());
	}
	
	virtual Funkcija* izvod()
	{	
		return new Plus(
			new Puta(_op1->izvod(), _op2->clone()),
			new Puta(_op1->clone(), _op2->izvod()));
	}
	
	virtual Funkcija* kompozicija(Funkcija* fun)
	{
		return new Puta(_op1->kompozicija(fun), _op2->kompozicija(fun));
	}
	
	virtual float vrednost(float x)
	{	return _op1->vrednost(x)*_op2->vrednost(x);	}
};

class ElementarnaFunkcija : public Funkcija
{
public:
	ElementarnaFunkcija(Funkcija* op, string name)
		: _op(op), _name(name)
	{}
	
	virtual ~ElementarnaFunkcija()
	{	delete _op;	}

	virtual void print(ostream& ostr)
	{
		ostr<<_name<<'(';
		_op->print(ostr);
		ostr<<')';
	}
		
protected:
	Funkcija* _op;
	string _name;
};

class Sin : public ElementarnaFunkcija
{
public:
	Sin(Funkcija* fun)
		: ElementarnaFunkcija(fun, "sin")
	{}
	
	virtual Funkcija* clone()
	{	return new Sin(_op->clone());	}
	
	virtual float vrednost(float x)
	{	return sin(x);	}
	
	virtual Funkcija* izvod();
	
	virtual Funkcija* kompozicija(Funkcija* fun)
	{
		return new Sin(_op->kompozicija(fun));
	}
};


class Cos : public ElementarnaFunkcija
{
public:
	Cos(Funkcija* fun)
		: ElementarnaFunkcija(fun, "cos")
	{}
	
	virtual Funkcija* clone()
	{	return new Cos(_op->clone());	}
	
	virtual float vrednost(float x)
	{	return cos(x);	}
	
	virtual Funkcija* izvod();
	
	virtual Funkcija* kompozicija(Funkcija* fun)
	{
		return new Cos(_op->kompozicija(fun));
	}
};

class Exp : public ElementarnaFunkcija
{
public:
	Exp(Funkcija* fun)
		: ElementarnaFunkcija(fun, "exp")
	{}
	
	virtual Funkcija* clone()
	{	return new Exp(_op->clone());	}
	
	virtual float vrednost(float x)
	{	return sin(x);	}
	
	virtual Funkcija* izvod()
	{	return new Puta(_op->izvod(), 
				clone());	

	}
	
	virtual Funkcija* kompozicija(Funkcija* fun)
	{
		return new Exp(_op->kompozicija(fun));
	}
};



#endif