#ifndef NODE_HPP
#define NODE_HPP

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

using namespace std;

class Node {
	protected:
		enum { CONST, VART, ADD, SUB, MUL, DIV, POW, 
				SIN, COS, EXP, LOG, ATAN, SQRT, CHS };

	public:
		virtual ~Node(void) {};
		virtual double eval(double) const = 0;
		virtual Node*  copy(void) const = 0;
		virtual Node*  deriv(void) const = 0;
		virtual string print(bool=false) const = 0;
		virtual int	   type(void) const = 0;
		virtual Node*  simp(void) const = 0;
};

class Const : public Node {
	private:
		double val;
	public:
		Const(double);
		virtual ~Const(void) {};
		virtual double eval(double) const;
		virtual Node*  copy(void) const;
		virtual Node*  deriv(void) const;
		virtual string print(bool=false) const;
		virtual int	   type(void) const { return CONST; };
		virtual Node*  simp(void) const { return copy(); };
};

class Var : public Node {
	public:
		Var(void);
		virtual ~Var(void) {};
		virtual double eval(double) const;
		virtual Node*  copy(void) const;
		virtual Node*  deriv(void) const;
		virtual string print(bool=false) const;
		virtual int	   type(void) const { return VART; };
		virtual Node*  simp(void) const { return copy(); };
};

class BinOp : public Node {
	protected:
		Node *lft;
		Node *rgt;
	public:
		BinOp(Node *l, Node *r);
		virtual ~BinOp(void);
		virtual double eval(double) const = 0;
		virtual Node*  copy(void) const = 0;
		virtual Node*  deriv(void) const = 0;
		virtual string print(bool=false) const = 0;
		virtual int	   type(void) const = 0;
		virtual Node*  simp(void) const = 0;
};

class Add : public BinOp {
	public:
		Add(Node* l, Node* r);
		virtual ~Add(void) {};
		virtual double eval(double) const;
		virtual Node*  copy(void) const;
		virtual Node*  deriv(void) const;
		virtual string print(bool=false) const;
		virtual int	   type(void) const { return ADD; };
		virtual Node*  simp(void) const { return copy(); };
};

class Sub : public BinOp {
	public:
		Sub(Node* l, Node* r);
		virtual ~Sub(void) {};
		virtual double eval(double) const;
		virtual Node*  copy(void) const;
		virtual Node*  deriv(void) const;
		virtual string print(bool=false) const;
		virtual int	   type(void) const { return SUB; };
		virtual Node*  simp(void) const { return copy(); };
};

class Mul : public BinOp {
	public:
		Mul(Node* l, Node* r);
		virtual ~Mul(void) {};
		virtual double eval(double) const;
		virtual Node*  copy(void) const;
		virtual Node*  deriv(void) const;
		virtual string print(bool=false) const;
		virtual int	   type(void) const { return MUL; };
		virtual Node*  simp(void) const;
};

class Div : public BinOp {
	public:
		Div(Node* l, Node* r);
		virtual ~Div(void) {};
		virtual double eval(double) const;
		virtual Node*  copy(void) const;
		virtual Node*  deriv(void) const;
		virtual string print(bool=false) const;
		virtual int	   type(void) const { return DIV; };
		virtual Node*  simp(void) const { return copy(); };
};

class Pow : public BinOp {
	public:
		Pow(Node* l, Node* r);
		virtual ~Pow(void) {};
		virtual double eval(double) const;
		virtual Node*  copy(void) const;
		virtual Node*  deriv(void) const;
		virtual string print(bool=false) const;
		virtual int	   type(void) const { return POW; };
		virtual Node*  simp(void) const { return copy(); };
};

class Fun : public Node {
	protected:
		Node *rgt;
	public:
		Fun(Node *r);
		virtual ~Fun(void);
		virtual double eval(double) const = 0;
		virtual Node*  copy(void) const = 0;
		virtual Node*  deriv(void) const = 0;
		virtual string print(bool=false) const= 0;
		virtual int	   type(void) const = 0;
		virtual Node*  simp(void) const = 0;
};

class Sin : public Fun {
	public:
		Sin(Node* r);
		virtual ~Sin(void) {};
		virtual double eval(double) const;
		virtual Node*  copy(void) const;
		virtual Node*  deriv(void) const;
		virtual string print(bool=false) const;
		virtual int	   type(void) const { return SIN; };
		virtual Node*  simp(void) const { return copy(); };
};

class Cos : public Fun {
	public:
		Cos(Node* r);
		virtual ~Cos(void) {};
		virtual double eval(double) const;
		virtual Node*  copy(void) const;
		virtual Node*  deriv(void) const;
		virtual string print(bool=false) const;
		virtual int	   type(void) const { return COS; };
		virtual Node*  simp(void) const { return copy(); };
};

class Exp : public Fun {
	public:
		Exp(Node* r);
		virtual ~Exp(void) {};
		virtual double eval(double) const;
		virtual Node*  copy(void) const;
		virtual Node*  deriv(void) const;
		virtual string print(bool=false) const;
		virtual int	   type(void) const { return EXP; };
		virtual Node*  simp(void) const { return copy(); };
};

class Log : public Fun {
	public:
		Log(Node* r);
		virtual ~Log(void) {};
		virtual double eval(double) const;
		virtual Node*  copy(void) const;
		virtual Node*  deriv(void) const;
		virtual string print(bool=false) const;
		virtual int	   type(void) const { return LOG; };
		virtual Node*  simp(void) const { return copy(); };
};

class Chs : public Fun {
	public:
		Chs(Node* r);
		virtual ~Chs(void) {};
		virtual double eval(double) const;
		virtual Node*  copy(void) const;
		virtual Node*  deriv(void) const;
		virtual string print(bool=false) const;
		virtual int	   type(void) const { return CHS; };
		virtual Node*  simp(void) const { return copy(); };
};

class Atan : public Fun {
	public:
		Atan(Node* r);
		virtual ~Atan(void) {};
		virtual double eval(double) const;
		virtual Node*  copy(void) const;
		virtual Node*  deriv(void) const;
		virtual string print(bool=false) const;
		virtual int	   type(void) const { return ATAN; };
		virtual Node*  simp(void) const { return copy(); };
};

class Sqrt : public Fun {
	public:
		Sqrt(Node* r);
		virtual ~Sqrt(void) {};
		virtual double eval(double) const;
		virtual Node*  copy(void) const;
		virtual Node*  deriv(void) const;
		virtual string print(bool=false) const;
		virtual int	   type(void) const { return SQRT; };
		virtual Node*  simp(void) const { return copy(); };
};

#endif
