#ifndef NODO_HPP
#define NODO_HPP

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

using namespace std;

class Nodo {
	protected:
		enum { NUM, VARX, ADD, SUB, CHS, MUL, DIV, POW, 
				LOG, EXP, SIN, COS, ATAN, PAT };
	public:
		Nodo(void) {};
		~Nodo(void) {};
		virtual int    type(void)  const = 0;
		virtual double eval(double=0.0)  const = 0;
		virtual string print(void) const = 0;
		virtual Nodo *copy(void)  const = 0;
		virtual Nodo *deriv(void)  const = 0;
		virtual Nodo *simp(void) const = 0;
		virtual bool compare(Nodo*, std::vector<Nodo*>&) const = 0;
};

class Const : public Nodo {
	private:
		double val;
	public:
		Const(double v);
		virtual ~Const(void) {};
		virtual int    type(void) const { return NUM; };
		virtual double eval(double=0.0) const;
		virtual string print(void) const;
		virtual Nodo *copy(void)  const;
		virtual Nodo *deriv(void) const;
		virtual Nodo *simp(void) const { return copy(); };
		virtual bool compare(Nodo*, std::vector<Nodo*>&) const;
};

class VarX : public Nodo {
	public:
		VarX(void);
		~VarX(void) {};
		virtual int    type(void) const { return VARX; };
		virtual double eval(double=0.0) const;
		virtual string print(void) const;
		virtual Nodo *copy(void)  const;
		virtual Nodo *deriv(void) const;
		virtual Nodo *simp(void) const { return copy(); };
		virtual bool compare(Nodo*, std::vector<Nodo*>&) const;
};

class OpBin : public Nodo {
	protected:
		Nodo *left, *right;
	public:
		OpBin(Nodo*, Nodo*);
		virtual ~OpBin(void);
		virtual int    type(void) const = 0;
		virtual double eval(double=0.0) const = 0;	
		virtual string print(void) const = 0;
		virtual Nodo *copy(void)  const = 0;
		virtual Nodo *deriv(void) const = 0;
		virtual Nodo *simp(void) const = 0;
		virtual bool compare(Nodo*, std::vector<Nodo*>&) const = 0;
		Nodo *Lft(void) const { return left; };
		Nodo *Rgt(void) const { return right; };
};

class Add : public OpBin {
	public:
		Add(Nodo*, Nodo*);
		virtual ~Add(void) {};
		virtual int    type(void) const { return ADD; };
		virtual double eval(double=0.0) const;	
		virtual string print(void) const;
		virtual Nodo *copy(void) const;
		virtual Nodo *deriv(void) const;
		virtual Nodo *simp(void) const;
		virtual bool compare(Nodo*, std::vector<Nodo*>&) const;
};

class Sub : public OpBin {
	public:
		Sub(Nodo*, Nodo*);
		virtual ~Sub() {};
		virtual int    type(void) const { return SUB; };
		virtual double eval(double=0.0) const;	
		virtual string print(void) const;
		virtual Nodo *copy(void) const;
		virtual Nodo *deriv(void) const;
		virtual Nodo *simp(void) const;
		virtual bool compare(Nodo*, std::vector<Nodo*>&) const;
};

class Mul : public OpBin {
	public:
		Mul(Nodo*, Nodo*);
		virtual ~Mul() {};
		virtual int    type(void) const { return MUL; };
		virtual double eval(double=0.0) const;	
		virtual string print(void) const;
		virtual Nodo *copy(void) const;
		virtual Nodo *deriv(void) const;
		virtual Nodo *simp(void) const;
		virtual bool compare(Nodo*, std::vector<Nodo*>&) const;
};

class Div : public OpBin {
	public:
		Div(Nodo*, Nodo*);
		virtual ~Div() {};
		virtual int    type(void) const { return DIV; };
		virtual double eval(double=0.0) const;	
		virtual string print(void) const;
		virtual Nodo *copy(void) const;
		virtual Nodo *deriv(void) const;
		virtual Nodo *simp(void) const;
		virtual bool compare(Nodo*, std::vector<Nodo*>&) const;
};

class Pow : public OpBin {
	public:
		Pow(Nodo*, Nodo*);
		virtual ~Pow() {};
		virtual int    type(void) const { return POW; };
		virtual double eval(double=0.0) const;	
		virtual string print(void) const;
		virtual Nodo *copy(void) const;
		virtual Nodo *deriv(void) const;
		virtual Nodo *simp(void) const;
		virtual bool compare(Nodo*, std::vector<Nodo*>&) const;
};

class Chs : public Nodo {
	private:
		Nodo *right;
	public:
		Chs(Nodo*);
		virtual ~Chs(void) { delete right; };
		virtual int    type(void) const { return CHS; };
		virtual double eval(double=0.0) const;
		virtual string print(void) const;
		virtual Nodo *copy(void) const;
		virtual Nodo *deriv(void) const;
		virtual Nodo *simp(void) const;
		virtual bool compare(Nodo*, std::vector<Nodo*>&) const;
		Nodo *Rgt(void) const { return right; };
};

class Fun : public Nodo {
	protected:
		Nodo *right;
	public:
		Fun(Nodo*);
		virtual ~Fun(void) { delete right; };
		virtual int    type(void) const = 0;
		virtual double eval(double=0.0) const = 0;	
		virtual string print(void) const = 0;
		virtual Nodo *deriv(void) const = 0;
		virtual Nodo *simp(void) const = 0;
		virtual Nodo *copy(void)  const = 0;
		virtual bool  compare(Nodo*, std::vector<Nodo*>&) const = 0;
		Nodo *Rgt(void) const { return right; };
};

class Sin : public Fun {
	public:
		Sin(Nodo*);
		virtual ~Sin(void) {};
		virtual int    type(void) const { return LOG; };
		virtual double  eval(double=0.0) const;	
		virtual string  print(void) const;
		virtual Nodo *copy(void) const;
		virtual Nodo *deriv(void) const;
		virtual Nodo *simp(void) const;
		virtual bool compare(Nodo*, std::vector<Nodo*>&) const;
};

class Cos : public Fun {
	public:
		Cos(Nodo*);
		virtual ~Cos(void) {};
		virtual int    type(void) const { return LOG; };
		virtual double eval(double=0.0) const;	
		virtual string print(void) const;
		virtual Nodo *copy(void) const;
		virtual Nodo *deriv(void) const;
		virtual Nodo *simp(void) const;
		virtual bool compare(Nodo*, std::vector<Nodo*>&) const;
};

class Atan : public Fun {
	public:
		Atan(Nodo*);
		virtual ~Atan(void) {};
		virtual int    type(void) const { return LOG; };
		virtual double eval(double=0.0) const;	
		virtual string print(void) const;
		virtual Nodo *copy(void) const;
		virtual Nodo *deriv(void) const;
		virtual Nodo *simp(void) const;
		virtual bool compare(Nodo*, std::vector<Nodo*>&) const;
};

class Exp : public Fun {
	public:
		Exp(Nodo*);
		virtual ~Exp(void) {};
		virtual int    type(void) const { return LOG; };
		virtual string print(void) const;
		virtual double eval(double=0.0) const;	
		virtual Nodo *copy(void) const;
		virtual Nodo *deriv(void) const;
		virtual Nodo *simp(void) const;
		virtual bool  compare(Nodo*, std::vector<Nodo*>&) const;
};

class Log : public Fun {
	public:
		Log(Nodo*);
		virtual ~Log(void) {};
		virtual int    type(void) const { return LOG; };
		virtual double eval(double=0.0) const;	
		virtual string print(void) const;
		virtual Nodo *copy(void) const;
		virtual Nodo *deriv(void) const;
		virtual Nodo *simp(void) const;
		virtual bool compare(Nodo*, std::vector<Nodo*>&) const;
};

class Pattern : public Nodo {
	private:
		int idx;
		Nodo* ptr;
	public:
		Pattern(int ix, Nodo* p=NULL);
		virtual ~Pattern(void);
		virtual int    type(void) const { return PAT; };
		virtual double eval(double=0.0) const;
		virtual string print(void) const;
		virtual Nodo *copy(void)  const;
		virtual Nodo *deriv(void) const;
		virtual Nodo *simp(void) const { return copy(); };
		virtual bool compare(Nodo*, std::vector<Nodo*>&) const;
		int Idx(void) const { return idx; };
};
#endif
