#ifndef NODO_HPP
#define NODO_HPP

#include <cstdlib>
#include <string>
#include <sstream>
#include <iostream>

using namespace std;

class Nodo {
	protected:
		enum { NUM, VARX, ADD, SUB, CHS, MUL, DIV, MOD, POW, LOG, EXP, SIN, COS, ATAN };
	public:
		Nodo(void) {	};
		virtual int    type(void)  const = 0;
		virtual Nodo*  copy(void)  const = 0;
		virtual string print(void) const = 0;
		virtual double eval(double)  const = 0;
		virtual Nodo*  deriv(void)  const = 0;
};

class Const : public Nodo {
	private:
		double val;
	public:
		Const(double v);
		virtual int    type(void) const { return NUM; };
		virtual Nodo*  copy(void)  const;
		virtual string print(void) const;
		virtual double eval(double) const;
		virtual Nodo*  deriv(void) const;
};

class VarX : public Nodo {
	public:
		VarX(void);
		virtual int    type(void) const { return VARX; };
		virtual Nodo*  copy(void)  const;
		virtual string print(void) const;
		virtual double eval(double) const;
		virtual Nodo*  deriv(void) const;
};

class OpBin : public Nodo {
	protected:
		Nodo *left, *right;
	public:
		OpBin(Nodo*, Nodo*);
		virtual int    type(void) const = 0;
		virtual Nodo*  copy(void)  const = 0;
		virtual string print(void) const = 0;
		virtual double eval(double) const = 0;	
		virtual Nodo*  deriv(void) const = 0;
		Nodo *Lft(void) const { return left; };
		Nodo *Rgt(void) const { return right; };
};

class Add : public OpBin {
	public:
		Add(Nodo*, Nodo*);
		virtual int    type(void) const { return ADD; };
		virtual Nodo*  copy(void) const;
		virtual string print(void) const;
		virtual double eval(double) const;	
		virtual Nodo*  deriv(void) const;
};

class Sub : public OpBin {
	public:
		Sub(Nodo*, Nodo*);
		virtual int    type(void) const { return SUB; };
		virtual Nodo*  copy(void) const;
		virtual string print(void) const;
		virtual double eval(double) const;	
		virtual Nodo*  deriv(void) const;
};

class Mul : public OpBin {
	public:
		Mul(Nodo*, Nodo*);
		virtual int    type(void) const { return MUL; };
		virtual Nodo*  copy(void) const;
		virtual string print(void) const;
		virtual double eval(double) const;	
		virtual Nodo*  deriv(void) const;
};

class Div : public OpBin {
	public:
		Div(Nodo*, Nodo*);
		virtual int    type(void) const { return DIV; };
		virtual Nodo*  copy(void) const;
		virtual string print(void) const;
		virtual double eval(double) const;	
		virtual Nodo*  deriv(void) const;
};

class Pow : public OpBin {
	public:
		Pow(Nodo*, Nodo*);
		virtual int    type(void) const { return POW; };
		virtual Nodo*  copy(void) const;
		virtual string print(void) const;
		virtual double eval(double) const;	
		virtual Nodo*  deriv(void) const;
};

class Chs : public Nodo {
	private:
		Nodo *right;
	public:
		Chs(Nodo*);
		virtual int    type(void) const { return CHS; };
		virtual Nodo*  copy(void) const;
		Nodo *Rgt(void) const { return right; };
		virtual string print(void) const;
		virtual double eval(double) const;
		virtual Nodo*  deriv(void) const;
};

class Fun : public Nodo {
	protected:
		Nodo *right;
	public:
		Fun(Nodo*);
		virtual int    type(void) const = 0;
		virtual Nodo*  copy(void)  const = 0;
		virtual string print(void) const = 0;
		virtual double eval(double) const = 0;	
		virtual Nodo*  deriv(void) const = 0;
		Nodo *Rgt(void) const { return right; };
};

class Sin : public Fun {
	public:
		Sin(Nodo*);
		virtual int    type(void) const { return LOG; };
		virtual Nodo*  copy(void) const;
		virtual string print(void) const;
		virtual double eval(double) const;	
		virtual Nodo*  deriv(void) const;
};

class Cos : public Fun {
	public:
		Cos(Nodo*);
		virtual int    type(void) const { return LOG; };
		virtual Nodo*  copy(void) const;
		virtual string print(void) const;
		virtual double eval(double) const;	
		virtual Nodo*  deriv(void) const;
};

class Atan : public Fun {
	public:
		Atan(Nodo*);
		virtual int    type(void) const { return LOG; };
		virtual Nodo*  copy(void) const;
		virtual string print(void) const;
		virtual double eval(double) const;	
		virtual Nodo*  deriv(void) const;
};

class Exp : public Fun {
	public:
		Exp(Nodo*);
		virtual int    type(void) const { return LOG; };
		virtual Nodo*  copy(void) const;
		virtual string print(void) const;
		virtual double eval(double) const;	
		virtual Nodo*  deriv(void) const;
};

class Log : public Fun {
	public:
		Log(Nodo*);
		virtual int    type(void) const { return LOG; };
		virtual Nodo*  copy(void) const;
		virtual string print(void) const;
		virtual double eval(double) const;	
		virtual Nodo*  deriv(void) const;
};

#endif
