// Copyright (C) 2005-2011, Ondra Kamenik

#ifndef OGP_FORMULA_PARSER_H
#define OGP_FORMULA_PARSER_H

#include "tree.h"

namespace ogp {
	using std::vector;

	/** Pure virtual class defining a minimal interface for
	 * representation of nulary terms within FormulaParser. */
	class Atoms {
	public:
		Atoms() {}
		virtual ~Atoms() {}
		/** This returns previously assigned internal index to the
		 * given atom, or returns -1 if the atom has not been assigned
		 * yet. The method can raise an exception, if the Atoms
		 * implementation is strict and the name is not among
		 * prescribed possible values. */
		virtual int check(const char* name) const = 0;
		/** This method assigns an internal index to the nulary term
		 * described by the name. The internal index is allocated by
		 * OperationTree class. */
		virtual void assign(const char* name, int t) = 0;
		/** Returns a number of variables which will be used for
		 * differentiations. */
		virtual int nvar() const = 0;
		/** Returns a vector of variable's internal indices which will
		 * be used for differentiations. */
		virtual vector<int> variables() const = 0;
		/** Debug print. */
		virtual void print() const = 0;
	};

	/** Pure virtual class defining interface for all classes able to
	 * set nulary terms to evaluation tree EvalTree. The
	 * implementations of this class will have to be connected with
	 * Atoms to have knowledge about the atoms and their indices in
	 * the tree, and will call EvalTree::set_nulary. */
	class AtomValues {
	public:
		virtual ~AtomValues() {}
		virtual void setValues(EvalTree& et) const = 0;
	};

	class FormulaDerEvaluator;
	class FoldMultiIndex;
	/** For ordering FoldMultiIndex in the std::map. */
	struct ltfmi {
		bool operator()(const FoldMultiIndex& i1, const FoldMultiIndex& i2) const;
	};

	/** This class stores derivatives (tree indices) of one formula
	 * for all orders upto a given one. It stores the derivatives as a
	 * sequence (vector) of these tree indices and sequence of the
	 * multidimensional indices of variables wrt which the derivatives
	 * were taken. In order to speed up querying for a derivative
	 * given the variables, we have a map mapping the multidimensional
	 * index to the order of the derivative in the sequence.
	 * 
	 * The only reason we do not have only this map is that the
	 * iterators of the map do not survive the insertions to the map,
	 * and implementation of the constructor has to be very difficult.
	 */
	class FormulaDerivatives {
		friend class FormulaDerEvaluator;
	protected:
		/** Vector of derivatives. This is a list of derivatives (tree
		 * indices), the ordering is given by the algorithm used to
		 * create it. Currently, it starts with zero-th derivative,
		 * the formula itself and carries with first order, second,
		 * etc. */
		vector<int> tder;
		/** Vector of multiindices corresponding to the vector of
		 * derivatives. */
		vector<FoldMultiIndex> indices;
		/** For retrieving derivatives via a multiindex, we have a map
		 * mapping a multiindex to a derivative in the tder
		 * ordering. This means that indices[ind2der[index]] == index. */
		typedef map<FoldMultiIndex, int, ltfmi> Tfmiintmap;
		Tfmiintmap ind2der;
		/** The number of variables. */
		int nvar;
		/** The maximum order of derivatives. */
		int order;
	public:
		/** The constructor allocates and fills the sequence of the
		 * indices of derivatives for a formula.
		 * @param otree the OperationTree for which all work is done
		 * and to which the derivatives are added.
		 * @param vars the vector of nulary terms in the tree; the
		 * derivatives are taken with respect to these variables in
		 * the ordering given by the vector.
		 * @param f the index of the formula being differentiated. The
		 * zero derivative is set to f.
		 * @param max_order the maximum order of differentiation.
		 */ 
		FormulaDerivatives(OperationTree& otree, const vector<int>& vars, int f, int max_order);
		/** Copy constructor. */
		FormulaDerivatives(const FormulaDerivatives& fd);
		virtual ~FormulaDerivatives(){}
		/** Random access to the derivatives via multiindex. */
		int derivative(const FoldMultiIndex& mi) const;
		/** Return the order. */
		int get_order() const
			{return order;}
		/** Debug print. */
		void print(const OperationTree& otree) const;
	};

	class FormulaEvaluator;

	/** This class is able to parse a number of formulas and
	 * differentiate them. The life cycle of the object is as follows:
	 * After it is created, a few calls to parse will add formulas
	 * (zero derivatives) to the object. Then a method differentiate()
	 * can be called and a vector of pointers to derivatives for each
	 * formula is created. After this, no one should call other
	 * parse() or differentiate(). A const reference of the object can
	 * be used in constructors of FormulaEvaluator and
	 * FormulaDerEvaluator in order to evaluate formulas (zero
	 * derivatives) and higher derivatives resp. */
	class FormulaParser {
		friend class FormulaCustomEvaluator;
		friend class FormulaDerEvaluator;
	protected:
		/** The OperationTree of all formulas, including derivatives. */
		OperationTree otree;
		/** Reference to Atoms. The Atoms are filled with nulary terms
		 * during execution of parse(). */
		Atoms& atoms;
		/** Vector of formulas (zero derivatives) in the order as they
		 * have been parsed. */
		vector<int> formulas;
		/** The vector to derivatives, each vector corresponds to a
		 * formula in the vector formulas. */
		vector<FormulaDerivatives*> ders;
	public:
		/** Construct an empty formula parser. */
		FormulaParser(Atoms& a)
			: atoms(a) {}
		/** Copy constructor using a different instance of Atoms. */
		FormulaParser(const FormulaParser& fp, Atoms& a);
		virtual ~FormulaParser();

		/** Requires an addition of the formula; called from the
		 * parser. */
		void add_formula(int t);
		/** Requires an addition of the binary operation; called from
		 * the parser. */
		int add_binary(code_t code, int t1, int t2);
		/** Requires an addition of the unary operation; called from
		 * the parser. */
		int add_unary(code_t code, int t);
		/** Requires an addition of the nulary operation given by the
		 * string. The Atoms are consulted for uniquness and are given
		 * an internal index generated by the OperationTree. This is
		 * the channel through which the Atoms are filled. */
		int add_nulary(const char* str);

		/** Adds a derivative to the tree. This just calls
		 * OperationTree::add_derivative. */
		int add_derivative(int t, int v)
			{return otree.add_derivative(t, v);}
		/** Adds a substitution. This just calls
		 * OperationTree::add_substitution. */
		int add_substitution(int t, const map<int,int>& subst)
			{return otree.add_substitution(t, subst);}
		/** Add the substitution given by the map where left sides of
		 * substitutions come from another parser. The right sides are
		 * from this object. The given t is from the given parser fp. */
		int add_substitution(int t, const map<int,int>& subst,
							 const FormulaParser& fp)
			{return otree.add_substitution(t, subst, fp.otree);}
		/** This adds formulas from the given parser with (possibly)
		 * different atoms applying substitutions from the given map
		 * mapping atoms from fp to atoms of the object. */
		void add_subst_formulas(const map<int,int>& subst, const FormulaParser& fp);
		/** Substitute formulas. For each i from 1 through all
		 * formulas, it adds a substitution of the i-th formula and
		 * make it to be i-th formula.*/
		void substitute_formulas(const std::map<int,int>& subst);
		/** This method turns the given term to nulary operation. It
		 * should be used with caution, since this method does not
		 * anything do with atoms, but usually some action is also
		 * needed (at leat to assign the tree index t to some
		 * atom). */
		void nularify(int t)
			{otree.nularify(t);}
		/** Returns a set of nulary terms of the given term. Just
		 * calls OperationTree::nulary_of_term. */
		const unordered_set<int>& nulary_of_term(int t) const
			{return otree.nulary_of_term(t);}

		/** Parse a given string containing one or more formulas. The
		 * formulas are parsed and added to the OperationTree and to
		 * the formulas vector. */
		void parse(int length, const char* stream);
		/** Processes a syntax error from bison. */
		void error(const char* mes) const;
		/** Differentiate all the formulas up to the given order. The
		 * variables with respect to which the derivatives are taken
		 * are obtained by Atoms::variables(). If the derivates exist,
		 * they are destroyed and created again (with possibly
		 * different order). */
		void differentiate(int max_order);
		/** Return i-th formula derivatives. */
		const FormulaDerivatives& derivatives(int i) const;

		/** This returns a maximum index of zero derivative formulas
		 * including all nulary terms. This is a mimumum length of the
		 * tree for which it is safe to evaluate zero derivatives of
		 * the formulas. */
		int last_formula() const;
		/** This returns a tree index of the i-th formula in the
		 * vector. */
		int formula(int i) const
			{return formulas[i];}


		/** This returns a tree index of the last formula and pops its
		 * item from the formulas vector. The number of formulas is
		 * then less by one. Returns -1 if there is no formula. If
		 * there are derivatives of the last formula, they are
		 * destroyed and the vector ders is popped from the back. */
		int pop_last_formula();

		/** This returns a number of formulas. */
		int nformulas() const
			{return (int)(formulas.size());}

		/** This returns a reference to atoms. */
		const Atoms& getAtoms() const
			{return atoms;}
		Atoms& getAtoms()
			{return atoms;}
		/** This returns the tree. */
		const OperationTree& getTree() const
			{return otree;}
		OperationTree& getTree()
			{return otree;}

		/** Debug print. */
		void print() const;
	private:
		/** Hide this copy constructor declaration by declaring it as
		 * private. */
		FormulaParser(const FormulaParser& fp);
		/** Destroy all derivatives. */
		void destroy_derivatives();
	};

	/** This is a pure virtual class defining an interface for all
	 * classes which will load the results of formula (zero
	 * derivative) evaluations. A primitive implementation of this
	 * class can be a vector of doubles. */
	class FormulaEvalLoader {
	public:
		virtual ~FormulaEvalLoader() {}
		/** Set the value res for the given formula. The formula is
		 * identified by an index corresponding to the ordering in
		 * which the formulas have been parsed (starting from
		 * zero). */
		virtual void load(int i, double res) = 0;
	};

	/** This class evaluates a selected subset of terms of the
	 * tree. In the protected constructor, one can constraint the
	 * initialization of the evaluation tree to a given number of
	 * terms in the beginning. Using this constructor, one has to make
	 * sure, that the terms in the beginning do not refer to terms
	 * behind the initial part. */
	class FormulaCustomEvaluator {
	protected:
		/** The evaluation tree. */
		EvalTree etree;
		/** The custom tree indices to be evaluated. */
		vector<int> terms;
	public:
		/** Construct from FormulaParser and given list of terms. */
		FormulaCustomEvaluator(const FormulaParser& fp, const vector<int>& ts)
			: etree(fp.otree), terms(ts)
			{}
		/** Construct from OperationTree and given list of terms. */
		FormulaCustomEvaluator(const OperationTree& ot, const vector<int>& ts)
			: etree(ot), terms(ts)
			{}
		/** Evaluate the terms using the given AtomValues and load the
		 * results using the given loader. The loader is called for
		 * each term in the order of the terms. */
		void eval(const AtomValues& av, FormulaEvalLoader& loader);
	protected:
		FormulaCustomEvaluator(const FormulaParser& fp)
			: etree(fp.otree, fp.last_formula()), terms(fp.formulas)
			{}
	};

	/** This class evaluates zero derivatives of the FormulaParser. */
	class FormulaEvaluator : public FormulaCustomEvaluator {
	public:
		/** Construct from FormulaParser. */
		FormulaEvaluator(const FormulaParser& fp)
			: FormulaCustomEvaluator(fp) {}
	};

	/** This is a pure virtual class defining an interface for all
	 * classes which will load the results of formula derivative
	 * evaluations. */
	class FormulaDerEvalLoader {
	public:
		virtual ~FormulaDerEvalLoader() {}
		/** This loads the result of the derivative of the given
		 * order. The semantics of i is the same as in
		 * FormulaEvalLoader::load. The indices of variables with
		 * respect to which the derivative was taken are stored in
		 * memory pointed by vars. These are the tree indices of the
		 * variables. */
		virtual void load(int i, int order, const int* vars, double res) = 0;
	};

	/** This class is a utility class representing the tensor
	 * multindex. It can basically increment itself, and calculate
	 * its offset in the folded tensor. */
	class FoldMultiIndex {
		/** Number of variables. */
		int nvar;
		/** Dimension. */
		int ord;
		/** The multiindex. */
		int* data;
	public:
		/** Initializes to the zero derivative. Order is 0, data is
		 * empty. */
		FoldMultiIndex(int nv);
		/** Initializes the multiindex to zeros or given i. */
		FoldMultiIndex(int nv, int order, int i = 0);
		/** Makes a new multiindex of the same order applying a given
		 * mapping to the indices. The mapping is supposed to be monotone. */
		FoldMultiIndex(int nv, const FoldMultiIndex& mi, const vector<int>& mp);
		/** Shifting constructor. This adds a given number of orders
		 * to the end, copying the last item to the newly added items,
		 * keeping the index ordered. If the index was empty (zero-th
		 * dimension), then zeros are added. */
		FoldMultiIndex(const FoldMultiIndex& fmi, int new_orders);
		/** Copy constructor. */
		FoldMultiIndex(const FoldMultiIndex& fmi);
		/** Desctructor. */
		virtual ~FoldMultiIndex()
			{delete [] data;}
		/** Assignment operator. */
		const FoldMultiIndex& operator=(const FoldMultiIndex& fmi);
		/** Operator < implementing lexicographic ordering within one
		 * order, increasing order across orders. */
		bool operator<(const FoldMultiIndex& fmi) const;
		bool operator==(const FoldMultiIndex& fmi) const;
		/** Increment the multiindex. */
		void increment();
		/** Return offset of the multiindex in the folded tensor. */ 
		int offset() const;
		const int& operator[](int i) const
			{return data[i];}
		/** Return order of the multiindex, i.e. dimension of the
		 * tensor. */ 
		int order() const
			{return ord;}
		/** Return the number of variables. */
		int nv() const
			{return nvar;}
		/** Return the data. */
		const int* ind() const
			{return data;}
		/** Return true if the end of the tensor is reached. The
		 * result of a subsequent increment should be considered
		 * unpredictable. */
		bool past_the_end() const
			{return (ord == 0) || (data[0] == nvar);}
		/** Prints the multiindex in the brackets. */
		void print() const;
	private:
		static int offset_recurse(int* data, int len, int nv);
	};

	/** This class evaluates derivatives of the FormulaParser. */
	class FormulaDerEvaluator {
		/** Its own instance of EvalTree. */
		EvalTree etree;
		/** The indices of derivatives for each formula. This is a
		 * const copy FormulaParser::ders. We do not allocate nor
		 * deallocate anything here. */
		vector<const FormulaDerivatives*> ders;
		/** A copy of tree indices corresponding to atoms to with
		 * respect the derivatives were taken. */
		vector<int> der_atoms;
	public:
		/** Construct the object from FormulaParser. */
		FormulaDerEvaluator(const FormulaParser& fp);
		/** Evaluate the derivatives from the FormulaParser wrt to all
		 * atoms in variables vector at the given AtomValues. The
		 * given loader is used for output. */
		void eval(const AtomValues& av, FormulaDerEvalLoader& loader, int order);
		/** Evaluate the derivatives from the FormulaParser wrt to a
		 * selection of atoms of the atoms in der_atoms vector at the
		 * given AtomValues. The selection is given by a monotone
		 * mapping to the indices (not values) of the der_atoms. */
		void eval(const vector<int>& mp, const AtomValues& av, FormulaDerEvalLoader& loader,
				  int order);
	};
};

#endif

// Local Variables:
// mode:C++
// End:
