diff --git a/parser.src/DataTree.cc b/parser.src/DataTree.cc
index 5a465b1ea9d005787318c8ce1f38a08454f6a783..f6d3f396232b84e6e3be3346101c01cdf3776af6 100644
--- a/parser.src/DataTree.cc
+++ b/parser.src/DataTree.cc
@@ -1,71 +1,348 @@
-/*! \file
-  \version 1.0
-  \date 04/09/2004
-  \par This file implements the DataTree class methodes.
-*/
-//------------------------------------------------------------------------------
 #include <iostream>
-#include <string>
-#include <vector>
-#include <map>
-using namespace std;
-#include <time.h>
-
-//------------------------------------------------------------------------------
-#include "DynareBison.hh"
-#include "VariableTable.hh"
-#include "NumericalConstants.hh"
+
 #include "DataTree.hh"
 
-DataTree::DataTree(SymbolTable &symbol_table_arg) :
+DataTree::DataTree(SymbolTable &symbol_table_arg, NumericalConstants &num_constants_arg) :
   symbol_table(symbol_table_arg),
+  num_constants(num_constants_arg),
   variable_table(symbol_table_arg),
-  NoOpCode(-1), NullID(NULL)
-{
-  offset = 1;
-  current_order = 0;
-
-  Zero = new MetaToken(reinterpret_cast <NodeID> (0), eNumericalConstant, NULL, -1);
-  Zero->op_name = "";
-  Zero->reference_count.resize(current_order+1,2);
-  Zero->idx = 0;
-  mModelTree.push_back(Zero);
-  mIndexOfTokens[*Zero]=Zero;
-
-  One = new MetaToken(reinterpret_cast <NodeID> (1), eNumericalConstant, NULL, -1);  One->op_name = "";
-  One->op_name = "";
-  One->reference_count.resize(current_order+1,1);
-  One->idx = 1;
-  mModelTree.push_back(One);
-  mIndexOfTokens[*One]=One;
-
-  MinusOne = new MetaToken(One, eTempResult, NULL, token::UMINUS);
-  MinusOne->op_name = OperatorTable::str(token::UMINUS);
-  MinusOne->reference_count.resize(current_order+1,1);
-  MinusOne->idx = 2;
-  mModelTree.push_back(MinusOne);
-  mIndexOfTokens[*MinusOne]=MinusOne;
-
-  // Pushing "0=0" into mModelTree
-  ZeroEqZero = new MetaToken(Zero, eTempResult, Zero, token::EQUAL);
-  ZeroEqZero->op_name = OperatorTable::str(token::EQUAL);
-  ZeroEqZero->reference_count.resize(current_order+1,1);
-  ZeroEqZero->idx = 3;
-  mModelTree.push_back(ZeroEqZero);
-  mIndexOfTokens[*ZeroEqZero]=ZeroEqZero;
-
-  // Initialise global node counter
-  nodeCounter = 4;
-
-  BeginModel = mModelTree.end();
-  BeginModel--;
-}
-
-//------------------------------------------------------------------------------
+  offset(1)
+{
+  Zero = AddNumConstant("0.0");
+  One = AddNumConstant("1.0");
+
+  MinusOne = AddUMinus(One);
+}
+
 DataTree::~DataTree()
 {
-  for (TreeIterator it = mModelTree.begin(); it != mModelTree.end(); it++)
+  for (node_list_type::iterator it = node_list.begin(); it != node_list.end(); it++)
+    delete *it;
+}
+
+NodeID
+DataTree::AddNumConstant(const string &value)
+{
+  int id = num_constants.AddConstant(value);
+
+  num_const_node_map_type::iterator it = num_const_node_map.find(id);
+  if (it != num_const_node_map.end())
+    return it->second;
+  else
+    return new NumConstNode(*this, id);
+}
+
+NodeID
+DataTree::AddVariable(const string &name, int lag)
+{
+  if (!symbol_table.Exist(name))
+    {
+      cerr << "Unknown symbol: " << name << endl;
+      exit(-1);
+    }
+
+  symbol_table.SetReferenced(name);
+
+  Type type = symbol_table.getType(name);
+  int id;
+  if (type == eEndogenous
+      || type == eExogenousDet
+      || type == eExogenous
+      || type == eRecursiveVariable)
+    id = variable_table.AddVariable(name, lag);
+  else
+    id = symbol_table.getID(name);
+
+  variable_node_map_type::iterator it = variable_node_map.find(make_pair(id, type));
+  if (it != variable_node_map.end())
+    return it->second;
+  else
+    return new VariableNode(*this, id, type);
+}
+
+NodeID
+DataTree::AddPlus(NodeID iArg1, NodeID iArg2)
+{
+  if (iArg1 != Zero && iArg2 != Zero)
+    {
+      // Simplify x+(-y) in x-y
+      UnaryOpNode *uarg2 = dynamic_cast<UnaryOpNode *>(iArg2);
+      if (uarg2 != NULL && uarg2->op_code == oUminus)
+        return AddMinus(iArg1, uarg2->arg);
+
+      // To treat commutativity of "+"
+      // Nodes iArg1 and iArg2 are sorted by index
+      if (iArg1->idx > iArg2->idx)
+        {
+          NodeID tmp = iArg1;
+          iArg1 = iArg2;
+          iArg2 = tmp;
+        }
+      return AddBinaryOp(iArg1, oPlus, iArg2);
+    }
+  else if (iArg1 != Zero)
+    return iArg1;
+  else if (iArg2 != Zero)
+    return iArg2;
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddMinus(NodeID iArg1, NodeID iArg2)
+{
+  if (iArg1 != Zero && iArg2 != Zero)
+    return AddBinaryOp(iArg1, oMinus, iArg2);
+  else if (iArg1 != Zero)
+    return iArg1;
+  else if (iArg2 != Zero)
+    return AddUMinus(iArg2);
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddUMinus(NodeID iArg1)
+{
+  if (iArg1 != Zero)
+    {
+      // Simplify -(-x) in x
+      UnaryOpNode *uarg = dynamic_cast<UnaryOpNode *>(iArg1);
+      if (uarg != NULL && uarg->op_code == oUminus)
+        return uarg->arg;
+
+      return AddUnaryOp(oUminus, iArg1);
+    }
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddTimes(NodeID iArg1, NodeID iArg2)
+{
+  if (iArg1 == MinusOne)
+    return AddUMinus(iArg2);
+  else if (iArg2 == MinusOne)
+    return AddUMinus(iArg1);
+  else if (iArg1 != Zero && iArg1 != One && iArg2 != Zero && iArg2 != One)
+    {
+      // To treat commutativity of "*"
+      // Nodes iArg1 and iArg2 are sorted by index
+      if (iArg1->idx > iArg2->idx)
+        {
+          NodeID tmp = iArg1;
+          iArg1 = iArg2;
+          iArg2 = tmp;
+        }
+      return AddBinaryOp(iArg1, oTimes, iArg2);
+    }
+  else if (iArg1 != Zero && iArg1 != One && iArg2 == One)
+    return iArg1;
+  else if (iArg2 != Zero && iArg2 != One && iArg1 == One)
+    return iArg2;
+  else if (iArg2 == One && iArg1 == One)
+    return One;
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddDivide(NodeID iArg1, NodeID iArg2)
+{
+  if (iArg1 != Zero && iArg2 != Zero && iArg2 != One)
+    return AddBinaryOp(iArg1, oDivide, iArg2);
+  else if (iArg2 == One)
+    return iArg1;
+  else if (iArg1 == Zero && iArg2 != Zero)
+    return Zero;
+  else
     {
-      if (*it != NullID)  delete *it;
+      cerr << "Division by zero!" << endl;
+      exit(-1);
     }
 }
+
+NodeID
+DataTree::AddPower(NodeID iArg1, NodeID iArg2)
+{
+  if (iArg1 != Zero && iArg2 != Zero && iArg2 != One)
+    return AddBinaryOp(iArg1, oPower, iArg2);
+  else if (iArg2 == One)
+    return iArg1;
+  else if (iArg2 == Zero)
+    return One;
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddExp(NodeID iArg1)
+{
+  if (iArg1 != Zero)
+    return AddUnaryOp(oExp, iArg1);
+  else
+    return One;
+}
+
+NodeID
+DataTree::AddLog(NodeID iArg1)
+{
+  if (iArg1 != Zero && iArg1 != One)
+    return AddUnaryOp(oLog, iArg1);
+  else if (iArg1 == One)
+    return Zero;
+  else
+    {
+      cerr << "log(0) isn't available" << endl;
+      exit(-1);
+    }
+}
+
+NodeID DataTree::AddLog10(NodeID iArg1)
+{
+  if (iArg1 != Zero && iArg1 != One)
+    return AddUnaryOp(oLog10, iArg1);
+  else if (iArg1 == One)
+    return Zero;
+  else
+    {
+      cerr << "log10(0) isn't available" << endl;
+      exit(-1);
+    }
+}
+
+NodeID
+DataTree::AddCos(NodeID iArg1)
+{
+  if (iArg1 != Zero)
+    return AddUnaryOp(oCos, iArg1);
+  else
+    return One;
+}
+
+NodeID
+DataTree::AddSin(NodeID iArg1)
+{
+  if (iArg1 != Zero)
+    return AddUnaryOp(oSin, iArg1);
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddTan(NodeID iArg1)
+{
+  if (iArg1 != Zero)
+    return AddUnaryOp(oTan, iArg1);
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddACos(NodeID iArg1)
+{
+  if (iArg1 != One)
+    return AddUnaryOp(oAcos, iArg1);
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddASin(NodeID iArg1)
+{
+  if (iArg1 != Zero)
+    return AddUnaryOp(oAsin, iArg1);
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddATan(NodeID iArg1)
+{
+  if (iArg1 != Zero)
+    return AddUnaryOp(oAtan, iArg1);
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddCosH(NodeID iArg1)
+{
+  if (iArg1 != Zero)
+    return AddUnaryOp(oCosh, iArg1);
+  else
+    return One;
+}
+
+NodeID
+DataTree::AddSinH(NodeID iArg1)
+{
+  if (iArg1 != Zero)
+    return AddUnaryOp(oSinh, iArg1);
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddTanH(NodeID iArg1)
+{
+  if (iArg1 != Zero)
+    return AddUnaryOp(oTanh, iArg1);
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddACosH(NodeID iArg1)
+{
+  if (iArg1 != One)
+    return AddUnaryOp(oAcosh, iArg1);
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddASinH(NodeID iArg1)
+{
+  if (iArg1 != Zero)
+    return AddUnaryOp(oAsinh, iArg1);
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddATanH(NodeID iArg1)
+{
+  if (iArg1 != Zero)
+    return AddUnaryOp(oAtanh, iArg1);
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddSqRt(NodeID iArg1)
+{
+  if (iArg1 != Zero)
+    return AddUnaryOp(oSqrt, iArg1);
+  else
+    return Zero;
+}
+
+NodeID
+DataTree::AddEqual(NodeID iArg1, NodeID iArg2)
+{
+  return AddBinaryOp(iArg1, oEqual, iArg2);
+}
+
+void
+DataTree::AddLocalParameter(const string &name, NodeID value) throw (LocalParameterException)
+{
+  int id = symbol_table.getID(name);
+
+  // Throw an exception if symbol already declared
+  map<int, NodeID>::iterator it = local_parameters_table.find(id);
+  if (it != local_parameters_table.end())
+    throw LocalParameterException(name);
+
+  local_parameters_table[id] = value;
+}
diff --git a/parser.src/DynareBison.yy b/parser.src/DynareBison.yy
index 5f55985f81b976607bdb5e03e1440d62ee19193d..58d3fca0493076f2138e389d387aabce23c14295 100644
--- a/parser.src/DynareBison.yy
+++ b/parser.src/DynareBison.yy
@@ -8,7 +8,7 @@ using namespace std;
 class ParsingDriver;
 
 #include "SymbolTableTypes.hh"
-#include "ModelTypes.hh"
+#include "ExprNode.hh"
 
 //! Type for semantic value of non-derivable expressions
 typedef pair<int, Type> ExpObj;
@@ -74,9 +74,6 @@ typedef pair<int, Type> ExpObj;
 %left UMINUS
 %nonassoc POWER 
 %token EXP LOG LOG10 SIN COS TAN ASIN ACOS ATAN SINH COSH TANH ASINH ACOSH ATANH SQRT
-/* isn't parsed from the *.mod file, but used to distinguish EQUAL in equation and EQUAL in assignment in    operation codes
-*/
-%token ASSIGN
 
 %type <exp_val> expression comma_expression
 %type <model_val> equation hand_side model_var
diff --git a/parser.src/ExprNode.cc b/parser.src/ExprNode.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1662179823567ff92c162dba195c49eb91715cab
--- /dev/null
+++ b/parser.src/ExprNode.cc
@@ -0,0 +1,776 @@
+#include <iostream>
+#include <iterator>
+#include <algorithm>
+
+#include "ExprNode.hh"
+#include "DataTree.hh"
+
+ExprNode::ExprNode(DataTree &datatree_arg) : datatree(datatree_arg)
+{
+  // Add myself to datatree
+  datatree.node_list.push_back(this);
+
+  // Set my index and increment counter
+  idx = datatree.node_counter++;
+}
+
+ExprNode::~ExprNode()
+{
+}
+
+NodeID
+ExprNode::getDerivative(int varID)
+{
+  // Return zero if derivative is necessarily null (using symbolic a priori)
+  set<int>::const_iterator it = non_null_derivatives.find(varID);
+  if (it == non_null_derivatives.end())
+    return datatree.Zero;
+
+  // If derivative is stored in cache, use the cached value, otherwise compute it (and cache it)
+  map<int, NodeID>::const_iterator it2 = derivatives.find(varID);
+  if (it2 != derivatives.end())
+    return it2->second;
+  else
+    {
+      NodeID d = computeDerivative(varID);
+      derivatives[varID] = d;
+      return d;
+    }
+}
+
+int
+ExprNode::precedence(const temporary_terms_type &temporary_terms) const
+{
+  // For a constant, a variable, or a unary op, the precedence is maximal
+  return 100;
+}
+
+int
+ExprNode::cost(temporary_terms_type &temporary_terms) const
+{
+  // For a terminal node, the cost is null
+  return 0;
+}
+
+void
+ExprNode::computeTemporaryTerms(map<NodeID, int> &reference_count,
+                                temporary_terms_type &temporary_terms) const
+{
+  // Nothing to do for a terminal node
+}
+
+NumConstNode::NumConstNode(DataTree &datatree_arg, int id_arg) :
+  ExprNode(datatree_arg),
+  id(id_arg)
+{
+  // Add myself to the num const map
+  datatree.num_const_node_map[id] = this;
+
+  // All derivatives are null, so non_null_derivatives is left empty
+}
+
+NodeID
+NumConstNode::computeDerivative(int varID)
+{
+  return datatree.Zero;
+}
+
+void
+NumConstNode::writeOutput(ostream &output, bool is_dynamic,
+                          const temporary_terms_type &temporary_terms) const
+{
+  temporary_terms_type::const_iterator it = temporary_terms.find(const_cast<NumConstNode *>(this));
+  if (it != temporary_terms.end())
+    output << "T" << idx;
+  else
+    output << datatree.num_constants.get(id);
+}
+
+VariableNode::VariableNode(DataTree &datatree_arg, int id_arg, Type type_arg) :
+  ExprNode(datatree_arg),
+  id(id_arg),
+  type(type_arg)
+{
+  // Add myself to the variable map
+  datatree.variable_node_map[make_pair(id, type)] = this;
+
+  // Fill in non_null_derivatives
+  switch(type)
+    {
+    case eEndogenous:
+    case eExogenous:
+    case eExogenousDet:
+    case eRecursiveVariable:
+      // For a variable, the only non-null derivative is with respect to itself
+      non_null_derivatives.insert(id);
+      break;
+    case eParameter:
+      // All derivatives are null, do nothing
+      break;
+    case eLocalParameter:
+      // Non null derivatives are those of the value of the local parameter
+      non_null_derivatives = datatree.local_parameters_table[id]->non_null_derivatives;
+      break;
+    case eNumericalConstant:
+    case eUNDEF:
+    case eTempResult:
+      // Impossible cases
+      cerr << "Incorrect symbol type used in VariableNode" << endl;
+      exit(-1);
+    }
+}
+
+NodeID
+VariableNode::computeDerivative(int varID)
+{
+  switch(type)
+    {
+    case eEndogenous:
+    case eExogenous:
+    case eExogenousDet:
+    case eRecursiveVariable:
+      if (varID == id)
+        return datatree.One;
+      else
+        return datatree.Zero;
+    case eParameter:
+      return datatree.Zero;
+    case eLocalParameter:
+      return datatree.local_parameters_table[id]->getDerivative(varID);
+    case eNumericalConstant:
+    case eUNDEF:
+    case eTempResult:
+      // Impossible cases
+      cerr << "Incorrect symbol type used in VariableNode" << endl;
+      exit(-1);
+    }
+  cerr << "Impossible case!" << endl;
+  exit(-1);
+}
+
+void
+VariableNode::writeOutput(ostream &output, bool is_dynamic,
+                          const temporary_terms_type &temporary_terms) const
+{
+  // If node is a temporary term
+  temporary_terms_type::const_iterator it = temporary_terms.find(const_cast<VariableNode *>(this));
+  if (it != temporary_terms.end())
+    {
+      output << "T" << idx;
+      return;
+    }
+
+  char &lpar = datatree.lpar;
+  char &rpar = datatree.rpar;
+
+  int idx;
+  switch(type)
+    {
+    case eParameter:
+      output << "params" << lpar << id + datatree.offset << rpar;
+      break;
+    case eLocalParameter:
+      output << datatree.symbol_table.getNameByID(eLocalParameter, id);
+      break;
+    case eEndogenous:
+      if (is_dynamic)
+        {
+          idx = datatree.variable_table.getPrintIndex(id) + datatree.offset;
+          output <<  "y" << lpar << idx << rpar;
+        }
+      else
+        {
+          idx = datatree.variable_table.getSymbolID(id) + datatree.offset;
+          output <<  "y" << lpar << idx << rpar;
+        }
+      break;
+    case eExogenous:
+      idx = datatree.variable_table.getSymbolID(id) + datatree.offset;
+      if (is_dynamic)
+          {
+            int lag = datatree.variable_table.getLag(id);
+            if (datatree.offset == 1)
+              if (lag != 0)
+                output <<  "x" << lpar << "it_ + " << lag << ", " << idx << rpar;
+              else
+                output <<  "x" << lpar << "it_, " << idx << rpar;
+            else
+              if (lag != 0)
+                output <<  "x" << lpar << "it_+" << lag << "+" << idx << "*nb_row_x" << rpar;
+              else
+                output <<  "x" << lpar << "it_+" << idx << "*nb_row_x" << rpar;
+          }
+      else
+        output << "x" << lpar << idx << rpar;
+      break;
+    case eExogenousDet:
+      idx = datatree.variable_table.getSymbolID(id) + datatree.symbol_table.exo_nbr
+        + datatree.offset;
+      if (is_dynamic)
+          {
+            int lag = datatree.variable_table.getLag(id);
+            if (datatree.offset == 1)
+              if (lag != 0)
+                output <<  "x" << lpar << "it_ + " << lag << ", " << idx << rpar;
+              else
+                output <<  "x" << lpar << "it_, " << idx << rpar;
+            else
+              if (lag != 0)
+                output <<  "x" << lpar << "it_ + " << lag << "+" << idx <<  "*nb_row_xd" << rpar;
+              else
+                output <<  "x" << lpar << "it_+" << idx << "*nb_row_xd" <<  rpar;
+          }
+      else
+        output <<  "x" << lpar << idx << rpar;
+      break;
+    case eRecursiveVariable:
+      cerr << "Recursive variable not implemented" << endl;
+      exit(-1);
+    case eTempResult:
+    case eNumericalConstant:
+    case eUNDEF:
+      // Impossible cases
+      cerr << "Incorrect symbol type used in VariableNode" << endl;
+      exit(-1);
+    }
+}
+
+UnaryOpNode::UnaryOpNode(DataTree &datatree_arg, UnaryOpcode op_code_arg, const NodeID arg_arg) :
+  ExprNode(datatree_arg),
+  arg(arg_arg),
+  op_code(op_code_arg)
+{
+  // Add myself to the unary op map
+  datatree.unary_op_node_map[make_pair(arg, op_code)] = this;
+
+  // Non-null derivatives are those of the argument
+  non_null_derivatives = arg->non_null_derivatives;
+}
+
+NodeID
+UnaryOpNode::computeDerivative(int varID)
+{
+  NodeID darg = arg->getDerivative(varID);
+
+  NodeID t11, t12, t13;
+
+  switch(op_code)
+    {
+    case oUminus:
+      return datatree.AddUMinus(darg);
+    case oExp:
+      return datatree.AddTimes(darg, this);
+    case oLog:
+      return datatree.AddDivide(darg, arg);
+    case oLog10:
+      t11 = datatree.AddExp(datatree.One);
+      t12 = datatree.AddLog10(t11);
+      t13 = datatree.AddDivide(darg, arg);
+      return datatree.AddTimes(t12, t13);
+    case oCos:
+      t11 = datatree.AddSin(arg);
+      t12 = datatree.AddUMinus(t11);
+      return datatree.AddTimes(darg, t12);
+    case oSin:
+      t11 = datatree.AddCos(arg);
+      return datatree.AddTimes(darg, t11);
+    case oTan:
+      t11 = datatree.AddTimes(this, this);
+      t12 = datatree.AddPlus(t11, datatree.One);
+      return datatree.AddTimes(darg, t12);
+    case oAcos:
+      t11 = datatree.AddSin(this);
+      t12 = datatree.AddDivide(darg, t11);
+      return datatree.AddUMinus(t12);
+    case oAsin:
+      t11 = datatree.AddCos(this);
+      return datatree.AddDivide(darg, t11);
+    case oAtan:
+      t11 = datatree.AddTimes(arg, arg);
+      t12 = datatree.AddPlus(datatree.One, t11);
+      return datatree.AddDivide(darg, t12);
+    case oCosh:
+      t11 = datatree.AddSinH(arg);
+      return datatree.AddTimes(darg, t11);
+    case oSinh:
+      t11 = datatree.AddCosH(arg);
+      return datatree.AddTimes(darg, t11);
+    case oTanh:
+      t11 = datatree.AddTimes(this, this);
+      t12 = datatree.AddMinus(datatree.One, t11);
+      return datatree.AddTimes(darg, t12);
+    case oAcosh:
+      t11 = datatree.AddSinH(this);
+      return datatree.AddDivide(darg, t11);
+    case oAsinh:
+      t11 = datatree.AddCosH(this);
+      return datatree.AddDivide(darg, t11);
+    case oAtanh:
+      t11 = datatree.AddTimes(arg, arg);
+      t12 = datatree.AddMinus(datatree.One, t11);
+      return datatree.AddTimes(darg, t12);
+    case oSqrt:
+      t11 = datatree.AddPlus(this, this);
+      return datatree.AddDivide(darg, t11);
+    }
+  cerr << "Impossible case!" << endl;
+  exit(-1);
+}
+
+int
+UnaryOpNode::cost(temporary_terms_type &temporary_terms) const
+{
+  // For a temporary term, the cost is null
+  temporary_terms_type::const_iterator it = temporary_terms.find(const_cast<UnaryOpNode *>(this));
+  if (it != temporary_terms.end())
+    return 0;
+
+  int cost = arg->cost(temporary_terms);
+
+  if (datatree.offset)
+    // Cost for Matlab files
+    switch(op_code)
+      {
+      case oUminus:
+        return cost + 70;
+      case oExp:
+        return cost + 160;
+      case oLog:
+        return cost + 300;
+      case oLog10:
+        return cost + 16000;
+      case oCos:
+      case oSin:
+      case oCosh:
+        return cost + 210;
+      case oTan:
+        return cost + 230;
+      case oAcos:
+        return cost + 300;
+      case oAsin:
+        return cost + 310;
+      case oAtan:
+        return cost + 140;
+      case oSinh:
+        return cost + 240;
+      case oTanh:
+        return cost + 190;
+      case oAcosh:
+        return cost + 770;
+      case oAsinh:
+        return cost + 460;
+      case oAtanh:
+        return cost + 350;
+      case oSqrt:
+        return cost + 570;
+      }
+  else
+    // Cost for C files
+    switch(op_code)
+      {
+      case oUminus:
+        return cost + 3;
+      case oExp:
+      case oAcosh:
+        return cost + 210;
+      case oLog:
+        return cost + 137;
+      case oLog10:
+        return cost + 139;
+      case oCos:
+      case oSin:
+        return cost + 160;
+      case oTan:
+        return cost + 170;
+      case oAcos:
+      case oAtan:
+        return cost + 190;
+      case oAsin:
+        return cost + 180;
+      case oCosh:
+      case oSinh:
+      case oTanh:
+        return cost + 240;
+      case oAsinh:
+        return cost + 220;
+      case oAtanh:
+        return cost + 150;
+      case oSqrt:
+        return cost + 90;
+      }
+  cerr << "Impossible case!" << endl;
+  exit(-1);
+}       
+
+void
+UnaryOpNode::computeTemporaryTerms(map<NodeID, int> &reference_count,
+                                   temporary_terms_type &temporary_terms) const
+{
+  NodeID this2 = const_cast<UnaryOpNode *>(this);
+
+  map<NodeID, int>::iterator it = reference_count.find(this2);
+  if (it == reference_count.end())
+    {
+      reference_count[this2] = 1;
+      arg->computeTemporaryTerms(reference_count, temporary_terms);
+    }
+  else
+    {
+      reference_count[this2]++;
+      if (reference_count[this2] * cost(temporary_terms) > datatree.min_cost)
+        temporary_terms.insert(this2);
+    }
+}
+
+void
+UnaryOpNode::writeOutput(ostream &output, bool is_dynamic,
+                         const temporary_terms_type &temporary_terms) const
+{
+  // If node is a temporary term
+  temporary_terms_type::const_iterator it = temporary_terms.find(const_cast<UnaryOpNode *>(this));
+  if (it != temporary_terms.end())
+    {
+      output << "T" << idx;
+      return;
+    }
+
+  // Always put parenthesis around uminus nodes
+  if (op_code == oUminus)
+    output << "(";
+
+  switch(op_code)
+    {
+    case oUminus:
+      output << "-";
+      break;
+    case oExp:
+      output << "exp";
+      break;
+    case oLog:
+      output << "log";
+      break;
+    case oLog10:
+      output << "log10";
+      break;
+    case oCos:
+      output << "cos";
+      break;
+    case oSin:
+      output << "sin";
+      break;
+    case oTan:
+      output << "tan";
+      break;
+    case oAcos:
+      output << "acos";
+      break;
+    case oAsin:
+      output << "asin";
+      break;
+    case oAtan:
+      output << "atan";
+      break;
+    case oCosh:
+      output << "cosh";
+      break;
+    case oSinh:
+      output << "sinh";
+      break;
+    case oTanh:
+      output << "tanh";
+      break;
+    case oAcosh:
+      output << "acosh";
+      break;
+    case oAsinh:
+      output << "asinh";
+      break;
+    case oAtanh:
+      output << "atanh";
+      break;
+    case oSqrt:
+      output << "sqrt";
+      break;
+    }
+
+  bool close_parenthesis = false;
+
+  /* Enclose argument with parentheses if:
+     - current opcode is not uminus, or
+     - current opcode is uminus and argument has lowest precedence
+  */
+  if (op_code != oUminus
+      || (op_code == oUminus && arg->precedence(temporary_terms) < precedence(temporary_terms)))
+    {
+      output << "(";
+      close_parenthesis = true;
+    }
+
+  // Write argument
+  arg->writeOutput(output, is_dynamic, temporary_terms);
+
+  if (close_parenthesis)
+    output << ")";
+
+  // Close parenthesis for uminus
+  if (op_code == oUminus)
+    output << ")";
+}
+
+BinaryOpNode::BinaryOpNode(DataTree &datatree_arg, const NodeID arg1_arg,
+                           BinaryOpcode op_code_arg, const NodeID arg2_arg) :
+  ExprNode(datatree_arg),
+  arg1(arg1_arg),
+  arg2(arg2_arg),
+  op_code(op_code_arg)
+{
+  datatree.binary_op_node_map[make_pair(make_pair(arg1, arg2), op_code)] = this;
+
+  // Non-null derivatives are the union of those of the arguments
+  // Compute set union of arg1->non_null_derivatives and arg2->non_null_derivatives
+  set_union(arg1->non_null_derivatives.begin(),
+            arg1->non_null_derivatives.end(),
+            arg2->non_null_derivatives.begin(),
+            arg2->non_null_derivatives.end(),
+            inserter(non_null_derivatives, non_null_derivatives.begin()));
+}
+
+NodeID
+BinaryOpNode::computeDerivative(int varID)
+{
+  NodeID darg1 = arg1->getDerivative(varID);
+  NodeID darg2 = arg2->getDerivative(varID);
+
+  NodeID t11, t12, t13, t14, t15;
+
+  switch(op_code)
+    {
+    case oPlus:
+      return datatree.AddPlus(darg1, darg2);
+    case oMinus:
+      return datatree.AddMinus(darg1, darg2);
+    case oTimes:
+      t11 = datatree.AddTimes(darg1, arg2);
+      t12 = datatree.AddTimes(darg2, arg1);
+      return datatree.AddPlus(t11, t12);
+    case oDivide:
+      t11 = datatree.AddTimes(darg1, arg2);
+      t12 = datatree.AddTimes(darg2, arg1);
+      t13 = datatree.AddMinus(t11, t12);
+      t14 = datatree.AddTimes(arg2, arg2);
+      return datatree.AddDivide(t13, t14);
+    case oPower:
+      if (darg2 == datatree.Zero)
+        {
+          if (darg1 == datatree.Zero)
+            return datatree.Zero;
+          else
+            {
+              t11 = datatree.AddMinus(arg2, datatree.One);
+              t12 = datatree.AddPower(arg1, t11);
+              t13 = datatree.AddTimes(arg2, t12);
+              return datatree.AddTimes(darg1, t13);
+            }
+        }
+      else
+        {
+          t11 = datatree.AddLog(arg1);
+          t12 = datatree.AddTimes(darg2, t11);
+          t13 = datatree.AddTimes(darg1, arg2);
+          t14 = datatree.AddDivide(t13, arg1);
+          t15 = datatree.AddPlus(t12, t14);
+          return datatree.AddTimes(t15, this);
+        }
+    case oEqual:
+      return datatree.AddMinus(darg1, darg2);
+    }
+  cerr << "Impossible case!" << endl;
+  exit(-1);
+}
+
+int
+BinaryOpNode::precedence(const temporary_terms_type &temporary_terms) const
+{
+  temporary_terms_type::const_iterator it = temporary_terms.find(const_cast<BinaryOpNode *>(this));
+  // A temporary term behaves as a variable
+  if (it != temporary_terms.end())
+    return 100;
+
+  switch(op_code)
+    {
+    case oEqual:
+    case oPlus:
+    case oMinus:
+      return 0;
+    case oTimes:
+    case oDivide:
+      return 1;
+    case oPower:
+      if (datatree.offset)
+        // In C, power operator is of the form pow(a, b)
+        return 100;
+      else
+        return 3;
+    }
+  cerr << "Impossible case!" << endl;
+  exit(-1);
+}
+
+int
+BinaryOpNode::cost(temporary_terms_type &temporary_terms) const
+{
+  temporary_terms_type::const_iterator it = temporary_terms.find(const_cast<BinaryOpNode *>(this));
+  // For a temporary term, the cost is null
+  if (it != temporary_terms.end())
+    return 0;
+
+  int cost = arg1->cost(temporary_terms);
+  cost += arg2->cost(temporary_terms);
+
+  if (datatree.offset)
+    // Cost for Matlab files
+    switch(op_code)
+      {
+      case oPlus:
+      case oMinus:
+      case oTimes:
+        return cost + 90;
+      case oDivide:
+        return cost + 990;
+      case oPower:
+        return cost + 1160;
+      case oEqual:
+        return cost;
+      }
+  else
+    // Cost for C files
+    switch(op_code)
+      {
+      case oPlus:
+      case oMinus:
+      case oTimes:
+        return cost + 4;
+      case oDivide:
+        return cost + 15;
+      case oPower:
+        return cost + 520;
+      case oEqual:
+        return cost;
+      }
+  cerr << "Impossible case!" << endl;
+  exit(-1);
+}
+
+void
+BinaryOpNode::computeTemporaryTerms(map<NodeID, int> &reference_count,
+                                    temporary_terms_type &temporary_terms) const
+{
+  NodeID this2 = const_cast<BinaryOpNode *>(this);
+  map<NodeID, int>::iterator it = reference_count.find(this2);
+  if (it == reference_count.end())
+    {
+      // If this node has never been encountered, set its ref count to one,
+      //  and travel through its children
+      reference_count[this2] = 1;
+      arg1->computeTemporaryTerms(reference_count, temporary_terms);
+      arg2->computeTemporaryTerms(reference_count, temporary_terms);
+    }
+  else
+    {
+      // If the node has already been encountered, increment its ref count
+      //  and declare it as a temporary term if it is too costly
+      reference_count[this2]++;
+      if (reference_count[this2] * cost(temporary_terms) > datatree.min_cost)
+        temporary_terms.insert(this2);
+    }
+}
+
+void
+BinaryOpNode::writeOutput(ostream &output, bool is_dynamic,
+                          const temporary_terms_type &temporary_terms) const
+{
+  // If current node is a temporary term
+  temporary_terms_type::const_iterator it = temporary_terms.find(const_cast<BinaryOpNode *>(this));
+  if (it != temporary_terms.end())
+    {
+      output << "T" << idx;
+      return;
+    }
+
+  // Treat special case of power operator in C
+  if (op_code == oPower && datatree.offset == 0)
+    {
+      output << "pow(";
+      arg1->writeOutput(output, is_dynamic, temporary_terms);
+      output << ",";
+      arg2->writeOutput(output, is_dynamic, temporary_terms);
+      output << ")";
+      return;
+    }
+
+  int prec = precedence(temporary_terms);
+
+  bool close_parenthesis = false;
+
+  // If left argument has a lower precedence, or if current and left argument are both power operators, add parenthesis around left argument
+  BinaryOpNode *barg1 = dynamic_cast<BinaryOpNode *>(arg1);
+  if (arg1->precedence(temporary_terms) < prec
+      || (op_code == oPower && barg1 != NULL && barg1->op_code == oPower))
+    {
+      output << "(";
+      close_parenthesis = true;
+    }
+
+  // Write left argument
+  arg1->writeOutput(output, is_dynamic, temporary_terms);
+
+  if (close_parenthesis)
+    output << ")";
+
+  // Write current operator symbol
+  switch(op_code)
+    {
+    case oPlus:
+      output << "+";
+      break;
+    case oMinus:
+      output << "-";
+      break;
+    case oTimes:
+      output << "*";
+      break;
+    case oDivide:
+      output << "/";
+      break;
+    case oPower:
+      output << "^";
+      break;
+    case oEqual:
+      output << "=";
+      break;
+    }
+
+  close_parenthesis = false;
+
+  /* Add parenthesis around right argument if:
+     - its precedence is lower than those of the current node
+     - it is a power operator and current operator is also a power operator
+     - it is a minus operator with same precedence than current operator
+     - it is a divide operator with same precedence than current operator */
+  BinaryOpNode *barg2 = dynamic_cast<BinaryOpNode *>(arg2);
+  int arg2_prec = arg2->precedence(temporary_terms);
+  if (arg2_prec < prec
+      || (op_code == oPower && barg2 != NULL && barg2->op_code == oPower)
+      || (op_code == oMinus && arg2_prec == prec)
+      || (op_code == oDivide && arg2_prec == prec))
+    {
+      output << "(";
+      close_parenthesis = true;
+    }
+
+  // Write right argument
+  arg2->writeOutput(output, is_dynamic, temporary_terms);
+
+  if (close_parenthesis)
+    output << ")";
+}
diff --git a/parser.src/Makefile b/parser.src/Makefile
index 02f1e61ab7aa165b02ef4f882d7508471e674664..55106485069c6b5bcbb42ce41dc913c2d272be4e 100644
--- a/parser.src/Makefile
+++ b/parser.src/Makefile
@@ -50,7 +50,8 @@ COMMON_OBJ=\
 	ParsingDriver.o\
 	DataTree.o \
 	ModFile.o \
-	Statement.o
+	Statement.o \
+	ExprNode.o
 
 MATLAB_OBJ = InterfaceMatlab.o
 
diff --git a/parser.src/ModFile.cc b/parser.src/ModFile.cc
index 7f0991f1b46f6eac474a857a7b12812fe2cde1e4..cd62be38b057af91034b12e11d6db5e9f5a1528e 100644
--- a/parser.src/ModFile.cc
+++ b/parser.src/ModFile.cc
@@ -1,3 +1,6 @@
+#include <iostream>
+#include <fstream>
+
 #include "ModFile.hh"
 #include "Interface.hh"
 
@@ -56,7 +59,7 @@ ModFile::computingPass()
 }
 
 void
-ModFile::writeOutputFiles(const string &basename, bool clear_all)
+ModFile::writeOutputFiles(const string &basename, bool clear_all) const
 {
   ofstream mOutputFile;
 
@@ -135,7 +138,7 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all)
   model_tree.writeDynamicFile(basename);
 
   // Print statements
-  for(vector<Statement *>::iterator it = statements.begin();
+  for(vector<Statement *>::const_iterator it = statements.begin();
       it != statements.end(); it++)
     (*it)->writeOutput(mOutputFile, basename);
 
diff --git a/parser.src/ModelTree.cc b/parser.src/ModelTree.cc
index 9b722c4f3c8b009022dabb9f23aa1dffe3aad398..4c0fc49c16cbc50250dc30d2b781d4c9401afd8d 100644
--- a/parser.src/ModelTree.cc
+++ b/parser.src/ModelTree.cc
@@ -1,43 +1,13 @@
-/*! \file
-  \version 1.0
-  \date 04/09/2004
-  \par This file implements the ModelTree class methodes.
-*/
-//------------------------------------------------------------------------------
 #include <iostream>
-#include <string>
-#include <vector>
-#include <stack>
-#include <numeric>
-#include <stdio.h>
-#include <map>
-#include <time.h>
-using namespace std;
-//------------------------------------------------------------------------------
-#include "VariableTable.hh"
-#include "DynareBison.hh"
-#include "NumericalConstants.hh"
+#include <fstream>
+#include <sstream>
+
 #include "ModelTree.hh"
 #include "Interface.hh"
 
-inline NodeID MetaToken::getDerivativeAddress(int iVarID, const ModelTree &model_tree) const
-{
-  std::map<int, NodeID, std::less<int> >::const_iterator iter = d1.find(iVarID);
-  if (iter == d1.end())
-    // No entry in map, derivative is therefore null
-    if (op_code == token::EQUAL)
-      return model_tree.ZeroEqZero;
-    else
-      return model_tree.Zero;
-  else
-    return iter->second;
-}
-
 ModelTree::ModelTree(SymbolTable &symbol_table_arg,
-                     const NumericalConstants &num_constants_arg) :
-  DataTree(symbol_table_arg),
-  num_constants(num_constants_arg),
-  eq_nbr(0),
+                     NumericalConstants &num_constants_arg) :
+  DataTree(symbol_table_arg, num_constants_arg),
   computeJacobian(false),
   computeJacobianExo(false),
   computeHessian(false),
@@ -46,7 +16,122 @@ ModelTree::ModelTree(SymbolTable &symbol_table_arg,
 }
 
 void
-ModelTree::writeStaticMFile(const string &static_basename)
+ModelTree::derive(int order)
+{
+  cout << "Processing derivation ..." << endl;
+
+  cout << "  Processing Order 1... ";
+  for(int var = 0; var < variable_table.size(); var++)
+    for(int eq = 0; eq < (int) equations.size(); eq++)
+      {
+        NodeID d1 = equations[eq]->getDerivative(var);
+        if (d1 == Zero)
+          continue;
+        first_derivatives[make_pair(eq, var)] = d1;
+      }
+  cout << "done" << endl;
+
+  if (order == 2)
+    {
+      cout << "  Processing Order 2... ";
+      for(first_derivatives_type::const_iterator it = first_derivatives.begin();
+          it != first_derivatives.end(); it++)
+        {
+          int eq = it->first.first;
+          int var1 = it->first.second;
+          NodeID d1 = it->second;
+      
+          // Store only second derivatives with var2 <= var1
+          for(int var2 = 0; var2 <= var1; var2++)
+            {
+              NodeID d2 = d1->getDerivative(var2);
+              if (d2 == Zero)
+                continue;
+              second_derivatives[make_pair(eq, make_pair(var1, var2))] = d2;
+            }
+        }
+      cout << "done" << endl;
+    }
+}
+
+void
+ModelTree::computeTemporaryTerms(int order)
+{
+  map<NodeID, int> reference_count;
+  temporary_terms.clear();
+
+  for(vector<BinaryOpNode *>::iterator it = equations.begin();
+      it != equations.end(); it++)
+    (*it)->computeTemporaryTerms(reference_count, temporary_terms);
+
+  for(first_derivatives_type::iterator it = first_derivatives.begin();
+      it != first_derivatives.end(); it++)
+    it->second->computeTemporaryTerms(reference_count, temporary_terms);
+
+  if (order == 2)
+    for(second_derivatives_type::iterator it = second_derivatives.begin();
+        it != second_derivatives.end(); it++)
+      it->second->computeTemporaryTerms(reference_count, temporary_terms);
+}
+
+void
+ModelTree::writeTemporaryTerms(ostream &output, bool is_dynamic) const
+{
+  // A copy of temporary terms
+  temporary_terms_type tt2;
+
+  for(temporary_terms_type::const_iterator it = temporary_terms.begin();
+      it != temporary_terms.end(); it++)
+    {
+      (*it)->writeOutput(output, is_dynamic, temporary_terms);
+      output << " = ";
+
+      (*it)->writeOutput(output, is_dynamic, tt2);
+
+      // Insert current node into tt2
+      tt2.insert(*it);
+
+      output << ";" << endl;
+    }
+}
+
+void
+ModelTree::writeLocalParameters(ostream &output, bool is_dynamic) const
+{
+  for(map<int, NodeID>::const_iterator it = local_parameters_table.begin();
+      it != local_parameters_table.end(); it++)
+    {
+      int id = it->first;
+      NodeID value = it->second;
+      output << symbol_table.getNameByID(eLocalParameter, id) << " = ";
+      value->writeOutput(output, is_dynamic, temporary_terms);
+      output << ";" << endl;
+    }
+}
+
+void
+ModelTree::writeModelEquations(ostream &output, bool is_dynamic) const
+{
+  for(int eq = 0; eq < (int) equations.size(); eq++)
+    {
+      BinaryOpNode *eq_node = equations[eq];
+
+      NodeID lhs = eq_node->arg1;
+      output << "lhs =";
+      lhs->writeOutput(output, is_dynamic, temporary_terms);
+      output << ";" << endl;
+
+      NodeID rhs = eq_node->arg2;
+      output << "rhs =";
+      rhs->writeOutput(output, is_dynamic, temporary_terms);
+      output << ";" << endl;
+
+      output << "residual" << lpar << eq + 1 << rpar << "= lhs-rhs;" << endl;
+    }
+}
+
+void
+ModelTree::writeStaticMFile(const string &static_basename) const
 {
   string filename = static_basename + interfaces::function_file_extension();
 
@@ -74,7 +159,7 @@ ModelTree::writeStaticMFile(const string &static_basename)
 
 
 void
-ModelTree::writeDynamicMFile(const string &dynamic_basename)
+ModelTree::writeDynamicMFile(const string &dynamic_basename) const
 {
   string filename = dynamic_basename + interfaces::function_file_extension();
 
@@ -100,7 +185,7 @@ ModelTree::writeDynamicMFile(const string &dynamic_basename)
 }
 
 void
-ModelTree::writeStaticCFile(const string &static_basename)
+ModelTree::writeStaticCFile(const string &static_basename) const
 {
   string filename = static_basename + ".c";
 
@@ -143,7 +228,7 @@ ModelTree::writeStaticCFile(const string &static_basename)
   mStaticModelFile << "  if (nlhs >= 1)\n";
   mStaticModelFile << "  {\n";
   mStaticModelFile << "      /* Set the output pointer to the output matrix residual. */\n";
-  mStaticModelFile << "      plhs[0] = mxCreateDoubleMatrix(" << eq_nbr << ",1, mxREAL);\n";
+  mStaticModelFile << "      plhs[0] = mxCreateDoubleMatrix(" << equations.size() << ",1, mxREAL);\n";
   mStaticModelFile << "     /* Create a C pointer to a copy of the output matrix residual. */\n";
   mStaticModelFile << "     residual = mxGetPr(plhs[0]);\n";
   mStaticModelFile << "  }\n\n";
@@ -151,7 +236,7 @@ ModelTree::writeStaticCFile(const string &static_basename)
   mStaticModelFile << "  if (nlhs >= 2)\n";
   mStaticModelFile << "  {\n";
   mStaticModelFile << "      /* Set the output pointer to the output matrix g1. */\n";
-  mStaticModelFile << "      plhs[1] = mxCreateDoubleMatrix(" << eq_nbr << ", " << symbol_table.endo_nbr << ", mxREAL);\n";
+  mStaticModelFile << "      plhs[1] = mxCreateDoubleMatrix(" << equations.size() << ", " << symbol_table.endo_nbr << ", mxREAL);\n";
   mStaticModelFile << "      /* Create a C pointer to a copy of the output matrix g1. */\n";
   mStaticModelFile << "      g1 = mxGetPr(plhs[1]);\n";
   mStaticModelFile << "  }\n\n";
@@ -170,7 +255,7 @@ ModelTree::writeStaticCFile(const string &static_basename)
 
  
 void
-ModelTree::writeDynamicCFile(const string &dynamic_basename)
+ModelTree::writeDynamicCFile(const string &dynamic_basename) const
 {
   string filename = dynamic_basename + ".c";
 
@@ -218,7 +303,7 @@ ModelTree::writeDynamicCFile(const string &dynamic_basename)
   mDynamicModelFile << "  if (nlhs >= 1)\n";
   mDynamicModelFile << "  {\n";
   mDynamicModelFile << "     /* Set the output pointer to the output matrix residual. */\n";
-  mDynamicModelFile << "     plhs[0] = mxCreateDoubleMatrix(" << eq_nbr << ",1, mxREAL);\n";
+  mDynamicModelFile << "     plhs[0] = mxCreateDoubleMatrix(" << equations.size() << ",1, mxREAL);\n";
   mDynamicModelFile << "     /* Create a C pointer to a copy of the output matrix residual. */\n";
   mDynamicModelFile << "     residual = mxGetPr(plhs[0]);\n";
   mDynamicModelFile << "  }\n\n";
@@ -227,9 +312,9 @@ ModelTree::writeDynamicCFile(const string &dynamic_basename)
   mDynamicModelFile << "  {\n";
   mDynamicModelFile << "     /* Set the output pointer to the output matrix g1. */\n";
   if (computeJacobianExo)
-    mDynamicModelFile << "     plhs[1] = mxCreateDoubleMatrix(" << eq_nbr << ", " << variable_table.get_dyn_var_nbr() << ", mxREAL);\n";
+    mDynamicModelFile << "     plhs[1] = mxCreateDoubleMatrix(" << equations.size() << ", " << variable_table.get_dyn_var_nbr() << ", mxREAL);\n";
   else if (computeJacobian)
-    mDynamicModelFile << "     plhs[1] = mxCreateDoubleMatrix(" << eq_nbr << ", " << variable_table.var_endo_nbr << ", mxREAL);\n";
+    mDynamicModelFile << "     plhs[1] = mxCreateDoubleMatrix(" << equations.size() << ", " << variable_table.var_endo_nbr << ", mxREAL);\n";
   mDynamicModelFile << "     /* Create a C pointer to a copy of the output matrix g1. */\n";
   mDynamicModelFile << "     g1 = mxGetPr(plhs[1]);\n";
   mDynamicModelFile << "  }\n\n";
@@ -237,7 +322,7 @@ ModelTree::writeDynamicCFile(const string &dynamic_basename)
   mDynamicModelFile << " if (nlhs >= 3)\n";
   mDynamicModelFile << "  {\n";
   mDynamicModelFile << "     /* Set the output pointer to the output matrix g2. */\n";
-  mDynamicModelFile << "     plhs[2] = mxCreateDoubleMatrix(" << eq_nbr << ", " << variable_table.get_dyn_var_nbr()*variable_table.get_dyn_var_nbr() << ", mxREAL);\n";
+  mDynamicModelFile << "     plhs[2] = mxCreateDoubleMatrix(" << equations.size() << ", " << variable_table.get_dyn_var_nbr()*variable_table.get_dyn_var_nbr() << ", mxREAL);\n";
   mDynamicModelFile << "     /* Create a C pointer to a copy of the output matrix g1. */\n";
   mDynamicModelFile << "     g2 = mxGetPr(plhs[2]);\n";
   mDynamicModelFile << "  }\n\n";
@@ -257,502 +342,70 @@ ModelTree::writeDynamicCFile(const string &dynamic_basename)
   mDynamicModelFile.close();
 }
 
-
-void ModelTree::derive(int iOrder)
-{
-  NodeID  lToken;                // To store current working token
-  NodeID    lD1, lD2;            // To store derivative arguments of current argument
-  NodeID   lArg1, lArg2;         // To store current arguments
-  Type    lType1;                // Type of first argument
-  NodeID   t1,t11,t12,t13,
-    t14, t15;                    // To store temoporary result arguments
-  TreeIterator  BeginIT;         // Iterator of the 1st token to derive
-  TreeIterator  EndIT;           // Iterator of the last token to derive
-  TreeIterator  currentIT;       // Iterator (counter) for model tree loop
-
-  vector<NodeID> EqualTokenIDs;  // IDs of "equal token" in model Tree
-  // Capturing equation IDs
-  for (currentIT = BeginModel; currentIT != mModelTree.end(); currentIT++)
-    {
-      if ((*currentIT)->op_code == token::EQUAL)
-        {
-          EqualTokenIDs.push_back(*currentIT);
-          // Equation is forced to be in Model Tree as referenced
-          // This is useful to remove symmetric elements
-          (*currentIT)->reference_count[0]++;
-        }
-    }
-  //std::cout << "size " << EqualTokenIDs.size() << "\n";
-  mDerivativeIndex.resize(iOrder);
-  // Uncomment this to print model tree data
-  /*
-  //cout << "ModelTree==================================\n";
-  for (currentIT = mModelTree.begin(); currentIT != mModelTree.end(); currentIT++)
-  {
-  lToken = *currentIT;
-  int ID = lToken->idx;
-  cout << ID << ":" << lToken << "->" << lToken->id1 << " " << lToken->type1 << " " <<
-  lToken->id2 << " " << lToken->op_code << "\n";
-  }
-  */
-
-  EndIT = mModelTree.begin();
-  EndIT--;
-  cout << "Processing derivation ...\n";
-  // loop on order of derivation
-  for(int Order = 1; Order <= iOrder; Order++)
-    {
-
-      cout << "\tProcessing Order " << Order << "... ";
-      current_order = Order;
-      BeginIT = EndIT;
-      BeginIT++;
-      EndIT = mModelTree.end();
-      EndIT--;
-      // Adding a reference counter for current order to tokens in mModelTree
-      // and updating them
-
-      for (TreeIterator it = mModelTree.begin(); it != mModelTree.end(); it++)
-        {
-          int s = (*it)->reference_count.size();
-          for (int i = s; i <= current_order; i++)
-            {
-              int rc = (*it)->reference_count[i-1];
-              (*it)->reference_count.push_back(rc);
-            }
-        }
-
-      // Loop on tokens
-      for (currentIT = BeginIT;; currentIT++)
-        {
-          lToken = *currentIT;
-
-          // Loop on variables of derivation which may give non null result for this token
-          for (vector<int>::iterator varIt = lToken->non_null_derivatives.begin();
-               varIt != lToken->non_null_derivatives.end(); varIt++)
-            {
-              int var = *varIt;
-              //cout << "Token " << (*currentIT)->idx << endl;
-              if (accumulate((*currentIT)->reference_count.begin(), (*currentIT)->reference_count.end(),0) > 0)
-                {
-                  lArg1 = lToken->id1;
-                  lArg2 = lToken->id2;
-                  lType1 = lToken->type1;
-                  lD1 = DeriveArgument(lArg1, lType1, var);
-                  lD2 = Zero;
-                  if (lArg2 != NullID)
-                    lD2 = DeriveArgument(lArg2, eTempResult, var);
-                  // Case where token is a terminal
-                  if (lToken->op_code == NoOpCode)
-                    {
-                      (*currentIT)->setDerivativeAddress(lD1, var);
-                    }
-                  else if (lD1 == Zero && lD2 == Zero)
-                    {
-                      (*currentIT)->setDerivativeAddress(Zero, var);
-                    }
-                  else
-                    {
-                      switch (lToken->op_code)
-                        {
-                        case token::UMINUS:
-                          t1 = AddUMinus(lD1);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::PLUS:
-                          t1 = AddPlus(lD1, lD2);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::MINUS:
-                          t1 = AddMinus(lD1, lD2);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::TIMES:
-                          t11 = AddTimes(lD1, lArg2);
-                          t12 = AddTimes(lD2, lArg1);
-                          t1 = AddPlus(t11, t12);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::DIVIDE:
-                          t11 = AddTimes(lD1, lArg2);
-                          t12 = AddTimes(lD2, lArg1);
-                          t13 = AddMinus(t11, t12);
-                          t14 =  AddTimes(lArg2, lArg2);
-                          t1 = AddDivide(t13, t14);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::SQRT:
-                          t11 = AddPlus(*currentIT, *currentIT);
-                          t1 = AddDivide(lD1, t11);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::POWER:
-                          if (lD2 == Zero)
-                            {
-                              if (lD1 == Zero)
-                                t1 = Zero;
-                              else
-                                {
-                                  t11 = AddMinus(lArg2, One);
-                                  t12 = AddPower(lArg1, t11);
-                                  t13 = AddTimes(lArg2, t12);
-                                  t1 = AddTimes(lD1, t13);
-                                }
-                            }
-                          else
-                            {
-                              t11 = AddLog(lArg1);
-                              t12 = AddTimes(lD2, t11);
-                              t13 = AddTimes(lD1, lArg2);
-                              t14 =  AddDivide(t13, lArg1);
-                              t15 = AddPlus(t12, t14);
-                              t1 = AddTimes(t15, *currentIT);
-                            }
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::EXP:
-                          t1 = AddTimes(lD1, *currentIT);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::LOG:
-                          t1 = AddDivide(lD1, lArg1);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::LOG10:
-                          t11 = AddExp(One);
-                          t12 = AddLog10(t11);
-                          t13 = AddDivide(lD1, lArg1);
-                          t1 = AddTimes(t12, t13);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::COS:
-                          t11 = AddSin(lArg1);
-                          t12 = AddUMinus(t11);
-                          t1 = AddTimes( lD1, t12);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::SIN:
-                          t11 = AddCos(lArg1);
-                          t1 = AddTimes(lD1,t11);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::TAN:
-                          t11 = AddTimes(*currentIT, *currentIT);
-                          t12 = AddPlus(t11, One);
-                          t1 = AddTimes(lD1, t12);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::ACOS:
-                          t11 = AddSin(*currentIT);
-                          t12 = AddDivide(lD1, t11);
-                          t1 = AddUMinus(t12);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::ASIN:
-                          t11 = AddCos(*currentIT);
-                          t1 = AddDivide(lD1, t11);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::ATAN:
-                          t11 = AddTimes(lArg1, lArg1);
-                          t12 = AddPlus(One, t11);
-                          t1 = AddDivide(lD1, t12);
-                          (*currentIT)->setDerivativeAddress(t1,var);
-                          break;
-                        case token::COSH:
-                          t11 = AddSinH(lArg1);
-                          t1 = AddTimes( lD1,t11);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::SINH:
-                          t11 = AddCosH(lArg1);
-                          t1 = AddTimes( lD1, t11);
-                          (*currentIT)->setDerivativeAddress(t1, var);
-                          break;
-                        case token::TANH:
-                          t11 = AddTimes(*currentIT, *currentIT);
-                          t12 = AddMinus(One, t11);
-                          t1 = AddTimes(lD1, t12);
-                          (*currentIT)->setDerivativeAddress(t1,var);
-                          break;
-                        case token::ACOSH:
-                          t11 = AddSinH(*currentIT);
-                          t1 = AddDivide(lD1, t11);
-                          (*currentIT)->setDerivativeAddress(t1,var);
-                          break;
-                        case token::ASINH:
-                          t11 = AddCosH(*currentIT);
-                          t1 = AddDivide(lD1, t11);
-                          (*currentIT)->setDerivativeAddress(t1,var);
-                          break;
-                        case token::ATANH:
-                          t11 = AddTimes(lArg1, lArg1);
-                          t12 = AddMinus(One, t11);
-                          t1 = AddTimes(lD1, t12);
-                          (*currentIT)->setDerivativeAddress(t1,var);
-                          break;
-                        case token::EQUAL:
-                          // Force the derivative to have zero on right hand side
-                          // (required for setStaticModel and setDynamicModel)
-                          if (lD1 == Zero && lD2 != Zero)
-                            {
-                              t11 = AddUMinus(lD2);
-                              t1 = AddEqual(t11, Zero);
-                            }
-                          else if (lD1 != Zero && lD2 == Zero)
-                            t1 = AddEqual(lD1, Zero);
-                          else
-                            {
-                              t11 = AddMinus(lD1, lD2);
-                              t1 = AddEqual(t11, Zero);
-                            }
-                          // The derivative is forced to be in Model Tree as referenced
-                          // This is useful to remove symmetric elements
-                          IncrementReferenceCount(t1);
-                          lToken->setDerivativeAddress(t1, var);
-                          break;
-                        }
-                    }
-                }
-            }
-          if (currentIT == EndIT)
-            break;
-        }
-
-      // Filling mDerivativeIndex
-      // Loop on variables of derivation, skipping symmetric elements
-      for (int var = 0; var < variable_table.size(); var++)
-        {
-          int starti = var*Order*(Order-1)*eq_nbr/2;
-          for (unsigned int i = starti; i < EqualTokenIDs.size() ; i++ )
-            {
-              t1 = EqualTokenIDs[i]->getDerivativeAddress(var, *this);
-              if (Order == 1)
-                mDerivativeIndex[0].push_back(DerivativeIndex(t1, i-starti, var));
-              else if (Order == 2)
-                {
-                  int var1 = variable_table.getSortID(i/eq_nbr);
-                  int var2 = variable_table.getSortID(var);
-                  mDerivativeIndex[1].push_back(DerivativeIndex(t1,
-                                                                i-eq_nbr*(i/eq_nbr),
-                                                                var1*variable_table.get_dyn_var_nbr()+var2));
-                }
-            }
-        }
-
-      // Uncomment to debug : prints unreferenced tokens
-      /*
-        cout << "Order : " << Order << "\n";
-        for (TokenCount = BeginModel; TokenCount < mModelTree.size() ; TokenCount++ )
-        {
-        if (accumulate(mModelTree[TokenCount].reference_count.begin(),mModelTree[TokenCount].reference_count.end(),0) == 0)
-        cout << "\tNot referenced : token ID :" << TokenCount << endl;
-        }
-      */
-      // Uncomment this to debug : mDerivative(1and2)Index data
-      // before removing unreferenced tokens
-      /*
-        cout << "Contenence of mDerivative1Index\n";
-        for (int i=0; i< mDerivativeIndex[0].size();i++)
-        //if (mDerivativeIndex[0][i].token_id != 3)
-        cout << "\t" << mDerivativeIndex[0][i].token_id << endl;
-        cout << "Contenence of mDerivative2Index\n";
-        for (int i=0; i< mDerivativeIndex[1].size();i++)
-        //if (mDerivativeIndex[1][i].token_id != 3)
-        cout << "\t" << mDerivativeIndex[1][i].token_id << endl;
-      */
-      //cout << "Removing unreferenced tokens range ids :" << CurrentID << " - " << mModelTree.size()-1 << endl;
-      // Removing unreferenced tokens in last derivative
-      // RemoveUnref(CurrentID, mModelTree.size()-1, Order);
-      // Decrementing reference couter of unreferenced tokens in last derivative
-      //DecrementUnref(CurrentID, mModelTree.size()-1, Order);
-      /*
-        cout << "Order : " << Order << "\n";
-        for (TokenCount = BeginModel; TokenCount < mModelTree.size() ; TokenCount++ )
-        {
-        if (accumulate(mModelTree[TokenCount].reference_count.begin(),mModelTree[TokenCount].reference_count.end(),0) == 0)
-        cout << "\tNot referenced : token ID :" << TokenCount << endl;
-        }
-      */
-      EqualTokenIDs.clear();
-      // Updating EqualTokenIDs
-      for (unsigned int i=0; i< mDerivativeIndex[Order-1].size();i++)
-        {
-          EqualTokenIDs.push_back(mDerivativeIndex[Order-1][i].token_id);
-        }
-
-      // Uncomment this to debug : mDerivative(1and2)Index data
-      // after removing unreferenced tokens
-      /*
-        cout << "Contenence of mDerivative1Index after removing\n";
-        for (int i=0; i< mDerivativeIndex[0].size();i++)
-        if (mDerivativeIndex[0][i].token_id != 3)
-        cout << "\t" << mDerivativeIndex[0][i].token_id << endl;
-        cout << "Contenence of mDerivative2Index after removing\n";
-        for (int i=0; i< mDerivativeIndex[1].size();i++)
-        //if (mDerivativeIndex[1][i].token_id != 3)
-        cout << "\t" << mDerivativeIndex[1][i].token_id << endl;
-      */
-      cout << "done" << endl;
-    }
-}
-
-//------------------------------------------------------------------------------
-inline NodeID ModelTree::DeriveArgument(NodeID iArg, Type iType, int iVarID)
-{
-  switch(iType)
-    {
-    case eTempResult      :
-      return iArg->getDerivativeAddress(iVarID, *this);
-    case eExogenous       :
-    case eExogenousDet      :
-    case eEndogenous      :
-    case eRecursiveVariable   :
-      if ((long int) iArg == iVarID)
-        return One;
-      else
-        {
-          return Zero;
-        }
-      break;
-    case eNumericalConstant   :
-    case eParameter       :
-      return Zero;
-    case eLocalParameter      :
-      return Zero;
-    case eUNDEF         :
-      return NullID;
-    default       :
-      cout << "ModelTree::DeriveArgument : Error: Unknown Type\n";
-      exit(-1);
-    };
-
-}
-
 void
-ModelTree::writeStaticModel(ostream &StaticOutput)
+ModelTree::writeStaticModel(ostream &StaticOutput) const
 {
   ostringstream model_output;    // Used for storing model equations
   ostringstream jacobian_output; // Used for storing jacobian equations
   ostringstream hessian_output;
   ostringstream lsymetric;       // For symmetric elements in hessian
 
-  int d = current_order;         // Minimum number of times a temparary expression apears in equations
-  // Reference count of token "0=0" is set to 0
-  // Not to be printed as a temp expression
-  fill(ZeroEqZero->reference_count.begin(),
-       ZeroEqZero->reference_count.end(),0);
-  // Writing model Equations
-  current_order = 1;
-  TreeIterator tree_it = BeginModel;
-  int lEquationNBR = 0;
-  for (; tree_it != mModelTree.end(); tree_it++)
-    {
-      if ((*tree_it)->op_code == token::EQUAL || (*tree_it)->op_code == token::ASSIGN )
-        {
-          if ((*tree_it)->id1->type1 == eLocalParameter)
-            {
-              model_output << getExpression((*tree_it)->id1, eStaticEquations);
-              model_output << " = ";
-              model_output << getExpression((*tree_it)->id2, eStaticEquations) << ";" << endl;
-            }
-          else if (lEquationNBR < eq_nbr)
-            {
-              model_output << "lhs =";
-              model_output << getExpression((*tree_it)->id1, eStaticEquations) << ";" << endl;
-              model_output << "rhs =";
-              model_output << getExpression((*tree_it)->id2, eStaticEquations) << ";" << endl;
-              model_output << "residual" << lpar << lEquationNBR+1 << rpar << "= lhs-rhs;" << endl;
-              lEquationNBR++;
-            }
-          else
-            break;
-        }
-      else if ((*tree_it)->op_code != NoOpCode)
-        {
-          if (optimize(*tree_it))
-            {
-              model_output << "T" << (*tree_it)->idx << "=" << getExpression(*tree_it, eStaticEquations) << ";" << endl;
-              (*tree_it)->tmp_status = 1;
-            }
-          else
-            {
-              (*tree_it)->tmp_status = 0;
-            }
-        }
-    }
+  writeTemporaryTerms(model_output, false);
 
-  for(; tree_it != mModelTree.end(); tree_it++)
-    {
-      if ((*tree_it)->op_code != NoOpCode
-          && (*tree_it)->op_code != token::EQUAL
-          && (*tree_it)->op_code != token::ASSIGN)
-        {
-          if (optimize(*tree_it) == 1)
-            {
-              jacobian_output << "T" << (*tree_it)->idx << "=" << getExpression(*tree_it, eStaticEquations) << ";" << endl;
-              (*tree_it)->tmp_status = 1;
-            }
-          else
-            {
-              (*tree_it)->tmp_status = 0;
-            }
-        }
-    }
+  writeLocalParameters(model_output, false);
+
+  writeModelEquations(model_output, false);
 
   // Write Jacobian w.r. to endogenous only
-  for (unsigned int i = 0; i < mDerivativeIndex[0].size(); i++)
+  for(first_derivatives_type::const_iterator it = first_derivatives.begin();
+      it != first_derivatives.end(); it++)
     {
-      if (variable_table.getType(mDerivativeIndex[0][i].derivators) == eEndogenous)
+      int eq = it->first.first;
+      int var = it->first.second;
+      NodeID d1 = it->second;
+
+      if (variable_table.getType(var) == eEndogenous)
         {
-          NodeID startJacobian = mDerivativeIndex[0][i].token_id;
-          if (startJacobian != ZeroEqZero)
-            {
-              string exp = getExpression(startJacobian->id1, eStaticDerivatives);
-              ostringstream g1;
-              g1 << "  g1" << lpar << mDerivativeIndex[0][i].equation_id+1 << ", " <<
-                variable_table.getSymbolID(mDerivativeIndex[0][i].derivators)+1 << rpar;
-              jacobian_output << g1.str() << "=" <<  g1.str() << "+" << exp << ";\n";
-            }
+          ostringstream g1;
+          g1 << "  g1" << lpar << eq + 1 << ", " << variable_table.getSymbolID(var) + 1 << rpar;
+          
+          jacobian_output << g1.str() << "=" << g1.str() << "+";
+          d1->writeOutput(jacobian_output, false, temporary_terms);
+          jacobian_output << ";" << endl;
         }
     }
 
   // Write Hessian w.r. to endogenous only
   if (computeStaticHessian)
-    {
-      for (unsigned int i = 0; i < mDerivativeIndex[1].size(); i++)
-        {
-          NodeID startHessian = mDerivativeIndex[1][i].token_id;
-          if (startHessian != ZeroEqZero)
-            {
-              string exp = getExpression(startHessian->id1, eStaticDerivatives);
-
-              int varID1 = mDerivativeIndex[1][i].derivators / variable_table.get_dyn_var_nbr();
-              int varID2 = mDerivativeIndex[1][i].derivators - varID1 * variable_table.get_dyn_var_nbr();
-
-              // Keep only derivatives w.r. to endogenous variables
-              if (variable_table.getType(varID1) != eEndogenous
-                  || variable_table.getType(varID2) != eEndogenous)
-                continue;
+    for(second_derivatives_type::const_iterator it = second_derivatives.begin();
+        it != second_derivatives.end(); it++)
+      {
+        int eq = it->first.first;
+        int var1 = it->first.second.first;
+        int var2 = it->first.second.second;
+        NodeID d2 = it->second;
+
+        // Keep only derivatives w.r. to endogenous variables
+        if (variable_table.getType(var1) == eEndogenous
+            && variable_table.getType(var2) == eEndogenous)
+          {
+            int id1 = variable_table.getSymbolID(var1);
+            int id2 = variable_table.getSymbolID(var2);
 
-              int id1 = variable_table.getSymbolID(varID1);
-              int id2 = variable_table.getSymbolID(varID2);
+            int col_nb = id1*symbol_table.endo_nbr+id2+1;
+            int col_nb_sym = id2*symbol_table.endo_nbr+id1+1;
 
-              int col_nb = id1*symbol_table.endo_nbr+id2+1;
-              int col_nb_sym = id2*symbol_table.endo_nbr+id1+1;
+            hessian_output << "  g2" << lpar << eq+1 << ", " << col_nb << rpar << " = ";
+            d2->writeOutput(hessian_output, false, temporary_terms);
+            hessian_output << ";" << endl;
 
-              hessian_output << "  g2" << lpar << mDerivativeIndex[1][i].equation_id+1 << ", " <<
-                col_nb << rpar << " = " << exp << ";\n";
-              // Treating symetric elements
-              if (varID1 != varID2)
-                lsymetric <<  "  g2" << lpar << mDerivativeIndex[1][i].equation_id+1 << ", " <<
-                  col_nb_sym << rpar << " = " <<
-                  "g2" << lpar << mDerivativeIndex[1][i].equation_id+1 << ", " <<
-                  col_nb << rpar << ";\n";
-            }
+            // Treating symetric elements
+            if (var1 != var2)
+              lsymetric <<  "  g2" << lpar << eq+1 << ", " << col_nb_sym << rpar << " = "
+                        <<  "g2" << lpar << eq+1 << ", " << col_nb << rpar << ";" << endl;
+          }
 
-        }
-    }
+      }
 
   // Writing ouputs
   if (offset == 1)
@@ -760,7 +413,7 @@ ModelTree::writeStaticModel(ostream &StaticOutput)
       StaticOutput << "global M_ \n";
       StaticOutput << "if M_.param_nbr > 0\n  params = M_.params;\nend\n";
 
-      StaticOutput << "  residual = zeros( " << eq_nbr << ", 1);\n";
+      StaticOutput << "  residual = zeros( " << equations.size() << ", 1);\n";
       StaticOutput << "\n\t"+interfaces::comment()+"\n\t"+interfaces::comment();
       StaticOutput << "Model equations\n\t";
       StaticOutput << interfaces::comment() + "\n\n";
@@ -770,7 +423,7 @@ ModelTree::writeStaticModel(ostream &StaticOutput)
       StaticOutput << "end\n";
       StaticOutput << "if nargout >= 2,\n";
       StaticOutput << "  g1 = " <<
-        "zeros(" << eq_nbr << ", " <<
+        "zeros(" << equations.size() << ", " <<
         symbol_table.endo_nbr << ");\n" ;
       StaticOutput << "\n\t"+interfaces::comment()+"\n\t"+interfaces::comment();
       StaticOutput << "Jacobian matrix\n\t";
@@ -786,7 +439,7 @@ ModelTree::writeStaticModel(ostream &StaticOutput)
           // Writing initialization instruction for matrix g2
           int ncols = symbol_table.endo_nbr * symbol_table.endo_nbr;
           StaticOutput << "  g2 = " <<
-            "sparse([],[],[]," << eq_nbr << ", " << ncols << ", " <<
+            "sparse([],[],[]," << equations.size() << ", " << ncols << ", " <<
             5*ncols << ");\n";
           StaticOutput << "\n\t"+interfaces::comment()+"\n\t"+interfaces::comment();
           StaticOutput << "Hessian matrix\n\t";
@@ -814,155 +467,69 @@ ModelTree::writeStaticModel(ostream &StaticOutput)
       StaticOutput << " }\n";
       StaticOutput << "}\n\n";
     }
-  current_order = d;
 }
 
 void
-ModelTree::writeDynamicModel(ostream &DynamicOutput)
+ModelTree::writeDynamicModel(ostream &DynamicOutput) const
 {
   ostringstream lsymetric;       // Used when writing symetric elements in Hessian
   ostringstream model_output;    // Used for storing model equations
   ostringstream jacobian_output; // Used for storing jacobian equations
   ostringstream hessian_output;  // Used for storing Hessian equations
 
-  int d = current_order;
-
-  // Reference count of token "0=0" is set to 0
-  // Not to be printed as a temp expression
-  fill(ZeroEqZero->reference_count.begin(),
-       ZeroEqZero->reference_count.end(),0);
-
-  // Getting equations from model tree
-  // Starting from the end of equation
-  // Searching for the next '=' operator,
-  current_order = 1;
-  int lEquationNBR = 0;
-  cout << "\tequations .. ";
-  TreeIterator tree_it = BeginModel;
-  for (; tree_it != mModelTree.end(); tree_it++)
-    {
-      if ((*tree_it)->op_code == token::EQUAL || (*tree_it)->op_code == token::ASSIGN)
-        {
-          if ((*tree_it)->id1->type1 == eLocalParameter)
-            {
-              model_output << getExpression((*tree_it)->id1, eStaticEquations);
-              model_output << " = ";
-              model_output << getExpression((*tree_it)->id2, eStaticEquations) << ";" << endl;
-            }
-          else if (lEquationNBR < eq_nbr)
-            {
-              model_output << "lhs =";
-              model_output << getExpression(((*tree_it)->id1), eDynamicEquations) << ";" << endl;
-              model_output << "rhs =";
-              model_output << getExpression(((*tree_it)->id2), eDynamicEquations) << ";" << endl;
-              model_output << "residual" << lpar << lEquationNBR+1 << rpar << "= lhs-rhs;" << endl;
-              lEquationNBR++;
-            }
-          else
-            break;
-        }
-      else if ((*tree_it)->op_code != NoOpCode)
-        {
-          if (optimize(*tree_it))
-            {
-              model_output << "T" << (*tree_it)->idx << "=" << getExpression(*tree_it, eDynamicEquations) << ";" << endl;
-              (*tree_it)->tmp_status = 1;
-            }
-          else
-            {
-              (*tree_it)->tmp_status = 0;
-            }
-        }
-    }
+  writeTemporaryTerms(model_output, true);
 
-  for(; tree_it != mModelTree.end(); tree_it++)
-    {
-      if ((*tree_it)->op_code != NoOpCode
-          && (*tree_it)->op_code != token::EQUAL
-          && (*tree_it)->op_code != token::ASSIGN)
-        {
-          if (optimize(*tree_it) == 1)
-            {
-              jacobian_output << "T" << (*tree_it)->idx << "=" << getExpression(*tree_it, eDynamicEquations) << ";" << endl;
-              (*tree_it)->tmp_status = 1;
-            }
-          else
-            {
-              (*tree_it)->tmp_status = 0;
-            }
-        }
-    }
-  cout << "done \n";
-  // Getting Jacobian from model tree
+  writeLocalParameters(model_output, true);
+
+  writeModelEquations(model_output, true);
+
+  // Writing Jacobian
   if (computeJacobian || computeJacobianExo)
-    {
-      cout << "\tJacobian .. ";
-      for(; tree_it != mModelTree.end(); tree_it++)
-        {
-          if ((*tree_it)->op_code != NoOpCode
-              && (*tree_it)->op_code != token::EQUAL
-              && (*tree_it)->op_code != token::ASSIGN)
-            {
-              if (optimize(*tree_it) == 1)
-                {
-                  jacobian_output << "T" << (*tree_it)->idx << "=" << getExpression(*tree_it, eDynamicEquations) << ";" << endl;
-                  (*tree_it)->tmp_status = 1;
-                }
-              else
-                {
-                  (*tree_it)->tmp_status = 0;
-                }
-            }
-        }
+    for(first_derivatives_type::const_iterator it = first_derivatives.begin();
+        it != first_derivatives.end(); it++)
+      {
+        int eq = it->first.first;
+        int var = it->first.second;
+        NodeID d1 = it->second;
 
-      for (unsigned int i = 0; i < mDerivativeIndex[0].size(); i++)
-        {
-          if (computeJacobianExo || variable_table.getType(mDerivativeIndex[0][i].derivators) == eEndogenous)
-            {
-              NodeID startJacobian = mDerivativeIndex[0][i].token_id;
-              if (startJacobian != ZeroEqZero)
-                {
-                  string exp = getExpression(startJacobian->id1, eDynamicDerivatives);
-                  ostringstream g1;
-                  g1 << "  g1" << lpar << mDerivativeIndex[0][i].equation_id+1 << ", " <<
-                    variable_table.getSortID(mDerivativeIndex[0][i].derivators)+1 << rpar;
-                  jacobian_output << g1.str() << "=" <<  g1.str() << "+" << exp << ";\n";
-                }
-            }
-        }
-      cout << "done \n";
-    }
+        if (computeJacobianExo || variable_table.getType(var) == eEndogenous)
+          {
+            ostringstream g1;
+            g1 << "  g1" << lpar << eq + 1 << ", " << variable_table.getSortID(var) + 1 << rpar;
+
+            jacobian_output << g1.str() << "=" << g1.str() << "+";
+            d1->writeOutput(jacobian_output, true, temporary_terms);
+            jacobian_output << ";" << endl;
+          }
+      }
 
+  // Writing Hessian
   if (computeHessian)
-    {
-      // Getting Hessian from model tree
-      // Starting from the end of equation
-      // Searching for the next '=' operator,
-      cout << "\tHessian .. ";
-      for (unsigned int i = 0; i < mDerivativeIndex[1].size(); i++)
-        {
-          NodeID startHessian = mDerivativeIndex[1][i].token_id;
-          //cout << "ID = " << startHessian << " exp = " << exp << "\n";
-          if (startHessian != ZeroEqZero)
-            {
-              string exp = getExpression(startHessian->id1, eDynamicDerivatives);
-
-              int varID1 = mDerivativeIndex[1][i].derivators / variable_table.get_dyn_var_nbr();
-              int varID2 = mDerivativeIndex[1][i].derivators - varID1 * variable_table.get_dyn_var_nbr();
-              hessian_output << "  g2" << lpar << mDerivativeIndex[1][i].equation_id+1 << ", " <<
-                mDerivativeIndex[1][i].derivators+1 << rpar << " = " << exp << ";\n";
-              // Treating symetric elements
-              if (varID1 != varID2)
-                lsymetric <<  "  g2" << lpar << mDerivativeIndex[1][i].equation_id+1 << ", " <<
-                  varID2*variable_table.get_dyn_var_nbr()+varID1+1 << rpar << " = " <<
-                  "g2" << lpar << mDerivativeIndex[1][i].equation_id+1 << ", " <<
-                  mDerivativeIndex[1][i].derivators+1 << rpar << ";\n";
-            }
+    for(second_derivatives_type::const_iterator it = second_derivatives.begin();
+        it != second_derivatives.end(); it++)
+      {
+        int eq = it->first.first;
+        int var1 = it->first.second.first;
+        int var2 = it->first.second.second;
+        NodeID d2 = it->second;
 
-        }
-      cout << "done \n";
-    }
-  int nrows = eq_nbr;
+        int id1 = variable_table.getSortID(var1);
+        int id2 = variable_table.getSortID(var2);
+
+        int col_nb = id1*variable_table.get_dyn_var_nbr()+id2+1;
+        int col_nb_sym = id2*variable_table.get_dyn_var_nbr()+id1+1;
+
+        hessian_output << "  g2" << lpar << eq+1 << ", " << col_nb << rpar << " = ";
+        d2->writeOutput(hessian_output, true, temporary_terms);
+        hessian_output << ";" << endl;
+
+        // Treating symetric elements
+        if (id1 != id2)
+          lsymetric <<  "  g2" << lpar << eq+1 << ", " << col_nb_sym << rpar << " = "
+                    <<  "g2" << lpar << eq+1 << ", " << col_nb << rpar << ";" << endl;
+      }
+
+  int nrows = equations.size();
   int nvars = variable_table.var_endo_nbr;
   if (computeJacobianExo)
     nvars += symbol_table.exo_nbr + symbol_table.exo_det_nbr;
@@ -1030,225 +597,6 @@ ModelTree::writeDynamicModel(ostream &DynamicOutput)
         }
       DynamicOutput << "}\n\n";
     }
-  current_order = d;
-}
-
-string
-ModelTree::getExpression(NodeID StartID, EquationType iEquationType)
-{
-
-  // Stack of tokens
-  stack <int, vector<NodeID> > stack_token;
-  ostringstream exp;
-  NodeID current_token_ID;
-
-  stack_token.push(StartID);
-  int precedence_last_op = 0;
-  int last_op_code = 0;
-  int on_the_right_of_upper_node = 0;
-
-  while (stack_token.size() > 0)
-    {
-      current_token_ID = stack_token.top();
-      // defining short hand for current op code
-      int current_op_code = current_token_ID->op_code;
-      if ( current_token_ID->tmp_status == 1)
-        {
-          exp << "T" << current_token_ID->idx;
-          // set precedence of terminal token to highest value
-          precedence_last_op = 100;
-          stack_token.pop();
-          continue;
-        }
-      // else if current token is final
-      else if ( current_op_code == NoOpCode)
-        {
-          exp << getArgument(current_token_ID->id1, current_token_ID->type1, iEquationType);
-          // set precedence of terminal token to highest value
-          precedence_last_op = 100;
-          stack_token.pop();
-          continue;
-        }
-
-      int precedence_current_op = OperatorTable::precedence(current_op_code);
-      // deal with left argument first
-      if (current_token_ID->left_done == 0)
-        {
-          // if current operator is not a temporary variable and
-          // of lesser precedence than previous one, insert '('
-          if (precedence_current_op < precedence_last_op
-              || (on_the_right_of_upper_node == 1
-                  && (last_op_code == token::MINUS || last_op_code == token::DIVIDE)
-                  && (precedence_current_op == precedence_last_op))
-              || (last_op_code == token::POWER && current_op_code == token::POWER)
-              || current_op_code == token::UMINUS)
-            {
-              exp << "(";
-              current_token_ID->close_parenthesis = 1;
-            }
-          // set flag: left argument has been explored
-          current_token_ID->left_done = 1;
-          precedence_last_op = precedence_current_op;
-          last_op_code = current_op_code;
-          if (offset == 0 && current_op_code == token::POWER)
-            {
-              exp << "pow(";
-              precedence_last_op = 0;
-              current_token_ID->close_parenthesis = 1;
-            }
-          else if (current_op_code == token::UMINUS)
-            {
-              exp << "-";
-              current_token_ID->close_parenthesis = 1;
-            }
-          else if ( OperatorTable::isfunction(current_op_code) == true)
-            {
-              exp << current_token_ID->op_name << "(";
-              precedence_last_op = 0;
-              current_token_ID->close_parenthesis = 1;
-            }
-          on_the_right_of_upper_node = 0;
-          stack_token.push(current_token_ID->id1);
-        }
-      // deal with right argument when left branch is entirely explored
-      else if ( current_token_ID->right_done == 0 )
-        {
-          current_token_ID->right_done = 1;
-          if (offset == 0 && current_op_code == token::POWER)
-            {
-              exp << ",";
-            }
-
-          if ( current_token_ID->id2 != NullID )
-            {
-              exp << current_token_ID->op_name;
-              precedence_last_op = precedence_current_op;
-              last_op_code = current_op_code;
-              on_the_right_of_upper_node = 1;
-              stack_token.push(current_token_ID->id2);
-            }
-        }
-      else
-        {
-          if ( current_token_ID->close_parenthesis == 1)
-            {
-              exp << ")";
-            }
-          precedence_last_op = precedence_current_op;
-          last_op_code = current_op_code;
-          current_token_ID->left_done=0;
-          current_token_ID->right_done=0;
-          current_token_ID->close_parenthesis=0;
-          stack_token.pop();
-        }
-    }
-  return exp.str();
-}
-
-//------------------------------------------------------------------------------
-inline string ModelTree::getArgument(NodeID id, Type type, EquationType iEquationType)
-{
-
-  stringstream  argument;
-
-  if (type == eParameter)
-    {
-      argument << "params" << lpar << (long int)id+offset << rpar;
-    }
-  else if (type == eLocalParameter)
-    {
-      argument << symbol_table.getNameByID(eLocalParameter, (long int) id);
-    }
-  else if (type == eNumericalConstant)
-    {
-      argument << num_constants.get((long int) id);
-    }
-  else if (type == eEndogenous || type == eExogenous || type == eExogenousDet)
-    if (iEquationType == eStaticEquations || iEquationType == eStaticDerivatives)
-      {
-        int idx = variable_table.getSymbolID((long int) id)+offset;
-        if (type == eEndogenous)
-          {
-            argument <<  "y" << lpar << idx << rpar;
-          }
-        else if (type == eExogenous)
-          {
-            argument << "x" << lpar << idx << rpar;
-          }
-        else if (type == eExogenousDet)
-          {
-            idx += symbol_table.exo_nbr;
-            argument <<  "x" << lpar << idx << rpar;
-          }
-      }
-    else
-      {
-        if (type == eEndogenous)
-          {
-            int idx = variable_table.getPrintIndex((long int) id) + offset;
-            argument <<  "y" << lpar << idx << rpar;
-          }
-        else if (type == eExogenous)
-          {
-            int idx = variable_table.getSymbolID((long int) id) + offset;
-            int lag = variable_table.getLag((long int) id);
-            if (offset == 1)
-              {
-                if ( lag != 0)
-                  {
-                    argument <<  "x" << lpar << "it_ + " << lag
-                             << ", " << idx << rpar;
-                  }
-                else
-                  {
-                    argument <<  "x" << lpar << "it_, " << idx << rpar;
-                  }
-              }
-            else
-              {
-                if ( lag != 0)
-                  {
-                    argument <<  "x" << lpar << "it_+" << lag
-                             << "+" << idx << "*nb_row_x" << rpar;
-                  }
-                else
-                  {
-                    argument <<  "x" << lpar << "it_+" << idx << "*nb_row_x" << rpar;
-                  }
-              }
-          }
-        else if (type == eExogenousDet)
-          {
-            int idx = variable_table.getSymbolID((long int) id) + symbol_table.exo_nbr + offset;
-            int lag = variable_table.getLag((long int) id);
-            if (offset == 1)
-              {
-                if (lag != 0)
-                  {
-                    argument <<  "x" << lpar << "it_ + " << lag
-                             << ", " << idx << rpar;
-                  }
-                else
-                  {
-                    argument <<  "x" << lpar << "it_, " << idx << rpar;
-                  }
-              }
-            else
-              {
-                if (lag != 0)
-                  {
-                    argument <<  "x" << lpar << "it_ + " << lag
-                             << "+" << idx <<  "*nb_row_xd" << rpar;
-                  }
-                else
-                  {
-                    argument <<  "x" << lpar << "it_+" << idx << "*nb_row_xd" <<  rpar;
-                  }
-              }
-
-          }
-      }
-  return argument.str();
 }
 
 void
@@ -1315,42 +663,25 @@ ModelTree::writeOutput(ostream &output) const
     output << "M_.params = zeros(" << symbol_table.parameter_nbr << ", 1);\n";
 }
 
-inline int ModelTree::optimize(NodeID node)
+void
+ModelTree::addEquation(NodeID eq)
 {
-  int cost;
-  int tmp_status = 0;
-  if (node->op_code != NoOpCode)
-    {
-      cost = OperatorTable::cost(node->op_code,offset);
-      if (node->id1 != NullID && node->id1->op_code != NoOpCode)
-        {
-          cost += OperatorTable::cost(node->id1->op_code,offset);
-        }
-      if (node->id2 != NullID && node->id2->op_code != NoOpCode)
-        {
-          cost += OperatorTable::cost(node->id2->op_code,offset);
-        }
-    }
-  cost *= node->reference_count[current_order];
-  if (cost > min_cost)
-    {
-      tmp_status = 1;
-      node->cost = 0;
-    }
-  else
+  BinaryOpNode *beq = dynamic_cast<BinaryOpNode *>(eq);
+
+  if (beq == NULL || beq->op_code != oEqual)
     {
-      tmp_status = 0;
-      node->cost = cost;
+      cerr << "ModelTree::addEquation: you didn't provide an equal node!" << endl;
+      exit(-1);
     }
-  node->tmp_status = 0;
-  return tmp_status;
+
+  equations.push_back(beq);
 }
 
 void
 ModelTree::checkPass() const
 {
   // Exit if there is no equation in model file
-  if (eq_nbr == 0)
+  if (equations.size() == 0)
     {
       cerr << "No equation found in model file" << endl;
       exit(-1);
@@ -1360,34 +691,38 @@ ModelTree::checkPass() const
 void
 ModelTree::computingPass()
 {
-  cout << eq_nbr << " equation(s) found \n";
+  cout << equations.size() << " equation(s) found" << endl;
+
   // Sorting variable table
   variable_table.Sort();
 
-  // Setting number of equations in ModelParameters class
-  // Here no derivative are computed
-  BeginModel++;
-  min_cost = 40 * OperatorTable::cost(token::PLUS, offset);
-  // Setting format of parentheses
   if (offset == 1)
     {
+      min_cost = 40 * 90;
       lpar = '(';
       rpar = ')';
     }
   else
     {
+      min_cost = 40 * 4;
       lpar = '[';
       rpar = ']';
     }
 
   if (computeHessian || computeStaticHessian)
-    derive(2);
+    {
+      derive(2);
+      computeTemporaryTerms(2);
+    }
   else
-    derive(1);
+    {
+      derive(1);
+      computeTemporaryTerms(1);
+    }
 }
 
 void
-ModelTree::writeStaticFile(const string &basename)
+ModelTree::writeStaticFile(const string &basename) const
 {
   if (offset)
     writeStaticMFile(basename + "_static");
@@ -1396,7 +731,7 @@ ModelTree::writeStaticFile(const string &basename)
 }
 
 void
-ModelTree::writeDynamicFile(const string &basename)
+ModelTree::writeDynamicFile(const string &basename) const
 {
   if (offset)
     writeDynamicMFile(basename + "_dynamic");
diff --git a/parser.src/ParsingDriver.cc b/parser.src/ParsingDriver.cc
index cb592d7c0b6ad173446e499feba92cdd9cdb83dd..d67f779d1cb12b9fa40ca04c48badf9adbc0dae3 100644
--- a/parser.src/ParsingDriver.cc
+++ b/parser.src/ParsingDriver.cc
@@ -42,7 +42,6 @@ ParsingDriver::parse(const string &f)
 {
   mod_file = new ModFile();
 
-  mod_file->model_tree.error = error;
   mod_file->symbol_table.error = error;
 
   expression.setNumericalConstants(&mod_file->num_constants);
@@ -118,16 +117,16 @@ ParsingDriver::add_expression_constant(string *constant)
 NodeID
 ParsingDriver::add_model_constant(string *constant)
 {
-  int id = mod_file->num_constants.AddConstant(*constant);
+  NodeID id = model_tree->AddNumConstant(*constant);
   delete constant;
-  return model_tree->AddTerminal((NodeID) id, eNumericalConstant);
+  return id;
 }
 
 NodeID
 ParsingDriver::add_model_variable(string *name)
 {
   check_symbol_existence(*name);
-  NodeID id = model_tree->AddTerminal(*name);
+  NodeID id = model_tree->AddVariable(*name);
   delete name;
   return id;
 }
@@ -145,7 +144,7 @@ ParsingDriver::add_model_variable(string *name, string *olag)
            << *name
            << " has lag " << lag << endl;
     }
-  NodeID id = model_tree->AddTerminal(*name, lag);
+  NodeID id = model_tree->AddVariable(*name, lag);
   delete name;
   delete olag;
   return id;
@@ -920,8 +919,8 @@ void
 ParsingDriver::end_planner_objective(NodeID expr)
 {
   // Add equation corresponding to expression
-  model_tree->AddEqual(expr, model_tree->Zero);
-  model_tree->eq_nbr++;
+  NodeID eq = model_tree->AddEqual(expr, model_tree->Zero);
+  model_tree->addEquation(eq);
 
   mod_file->addStatement(new PlannerObjectiveStatement(model_tree));
 }
@@ -938,7 +937,7 @@ NodeID
 ParsingDriver::add_model_equal(NodeID arg1, NodeID arg2)
 {
   NodeID id = model_tree->AddEqual(arg1, arg2);
-  model_tree->eq_nbr++;
+  model_tree->addEquation(id);
   return id;
 }
 
@@ -952,8 +951,14 @@ void
 ParsingDriver::declare_and_init_local_parameter(string *name, NodeID rhs)
 {
   mod_file->symbol_table.AddSymbolDeclar(*name, eLocalParameter, *name);
-  NodeID id = model_tree->AddTerminal(*name);
-  model_tree->AddAssign(id, rhs);
+  try
+    {
+      model_tree->AddLocalParameter(*name, rhs);
+    }
+  catch(DataTree::LocalParameterException &e)
+    {
+      error("Local parameter " + e.name + " declared twice");
+    }
   delete name;
 }
 
diff --git a/parser.src/SymbolTable.cc b/parser.src/SymbolTable.cc
index c710b94d9ffcea96bd779765c6a7b0506bf7d055..cd4b186045a098a8c9daf6c004fa12e98e69ad64 100644
--- a/parser.src/SymbolTable.cc
+++ b/parser.src/SymbolTable.cc
@@ -100,7 +100,7 @@ Reference SymbolTable::isReferenced(const std::string &name) const
 }
 
 void
-SymbolTable::writeOutput(ostream &output)
+SymbolTable::writeOutput(ostream &output) const
 {
   if (exo_nbr > 0)
     {
diff --git a/parser.src/include/DataTree.hh b/parser.src/include/DataTree.hh
index 0e2cf50bcf0b581eba14d1e3db6163f7a031b6fa..50d19e7861fccc9e2d2632e25435bc5475573b30 100644
--- a/parser.src/include/DataTree.hh
+++ b/parser.src/include/DataTree.hh
@@ -1,778 +1,144 @@
 #ifndef _DATATREE_HH
 #define _DATATREE_HH
-//------------------------------------------------------------------------------
-/*! \file
-  \version 1.0
-  \date 04/09/2004
-  \par This file defines the DataTree class.
-*/
-//------------------------------------------------------------------------------
-#include <iostream>
+
+using namespace std;
+
 #include <string>
-#include <list>
-#include <stack>
-#include <sstream>
-#include <fstream>
 #include <map>
-#include <stdio.h>
-//------------------------------------------------------------------------------
+#include <list>
+
 #include "SymbolTable.hh"
-#include "OperatorTable.hh"
 #include "NumericalConstants.hh"
-#include "ModelTypes.hh"
 #include "VariableTable.hh"
+#include "ExprNode.hh"
 
-typedef std::map<MToken, NodeID, MTokenLess> TreeMap;
-typedef std::list<NodeID> TreeList;
-typedef TreeList::iterator TreeIterator;
-/*!
-  \class  DataTree
-  \brief  Provides data storage for model tree
-*/
 class DataTree
 {
-protected :
+  friend class ExprNode;
+  friend class NumConstNode;
+  friend class VariableNode;
+  friend class UnaryOpNode;
+  friend class BinaryOpNode;
+protected:
   //! A reference to the symbol table
   SymbolTable &symbol_table;
+  //! Reference to numerical constants table
+  NumericalConstants &num_constants;
   //! The variable table
   VariableTable variable_table;
-
-  /*! A list of structures "token" */
-  TreeList    mModelTree;
-  /*! matches key with entry id in list of token */
-  TreeMap         mIndexOfTokens;
-  /*! A counter for filling MetatToken's idx field */
-  int nodeCounter;
-  /*! ID of first token in model tree (first tokens are "0", "1" and "0=" */
-  TreeIterator      BeginModel;
-  /*!
-    Used with field reference_count to know if
-    dealing with derivative 0, 1, 2 etc
-  */
-  int         current_order;
-  /*! Pushs token into model tree */
-  inline NodeID     PushToken(NodeID iArg1,int iOpCode, NodeID iArg2 = NULL, Type iType1 = eTempResult);
-  inline NodeID   getIDOfToken(const MToken &iToken);
-public :
-  /*! Flag for empty operator (final toekn) */
-  const int NoOpCode;
-  const NodeID NullID;
-  NodeID Zero;
-  NodeID One;
-  NodeID MinusOne;
-  NodeID ZeroEqZero;
-  /*! Type of output 0 for C and 1 for Matlab (default) , also used as matrix index offset*/
+  
+  typedef list<NodeID> node_list_type;
+  //! The list of nodes
+  node_list_type node_list;
+  //! A counter for filling ExprNode's idx field
+  int node_counter;
+
+  //! Stores local parameters value
+  map<int, NodeID> local_parameters_table;
+
+  //! Computing cost above which a node can be declared a temporary term
+  int min_cost;
+
+  //! Left indexing parenthesis
+  char lpar;
+  //! Right indexing parenthesis
+  char rpar;
+
+  typedef map<int, NodeID> num_const_node_map_type;
+  num_const_node_map_type num_const_node_map;
+  typedef map<pair<int, Type>, NodeID> variable_node_map_type;
+  variable_node_map_type variable_node_map;
+  typedef map<pair<NodeID, int>, NodeID> unary_op_node_map_type;
+  unary_op_node_map_type unary_op_node_map;
+  typedef map<pair<pair<NodeID, NodeID>, int>, NodeID> binary_op_node_map_type;
+  binary_op_node_map_type binary_op_node_map;
+
+  inline NodeID AddUnaryOp(UnaryOpcode op_code, NodeID arg);
+  inline NodeID AddBinaryOp(NodeID arg1, BinaryOpcode op_code, NodeID arg2);
+public:
+  DataTree(SymbolTable &symbol_table_arg, NumericalConstants &num_constants_arg);
+  virtual ~DataTree();
+  NodeID Zero, One, MinusOne;
+  //! Type of output 0 for C and 1 for Matlab (default), also used as matrix index offset
   int offset;
-  /*! Pointer to error function of parser class */
-  void (* error) (const char* m);
-  /*! Increment reference count of given token */
-  inline void     IncrementReferenceCount(NodeID token);
-  /*! Adds terminal token to model tree */
-  inline NodeID     AddTerminal(NodeID iArg, Type type);
-  /*! Adds terminal  token (a variable) to model tree */
-  inline NodeID     AddTerminal(std::string iArgName, int iLag = 0);
-  /*! Adds "arg1+arg2" to model tree */
-  inline NodeID     AddPlus(NodeID iArg1, NodeID iArg2);
-  /*! Adds "arg1-arg2" to model tree */
-  inline NodeID     AddMinus(NodeID iArg1, NodeID iArg2);
-  /*! Adds "-arg" to model tree */
-  inline NodeID     AddUMinus(NodeID iArg1);
-  /*! Adds "arg1*arg2" to model tree */
-  inline NodeID     AddTimes(NodeID iArg1, NodeID iArg2);
-  /*! Adds "arg1/arg2" to model tree */
-  inline NodeID     AddDivide(NodeID iArg1, NodeID iArg2);
-  /*! Adds "arg1^arg2" to model tree */
-  inline NodeID     AddPower(NodeID iArg1, NodeID iArg2);
-  /*! Adds "exp(arg)" to model tree */
-  inline NodeID     AddExp(NodeID iArg1);
-  /*! Adds "log(arg)" to model tree */
-  inline NodeID     AddLog(NodeID iArg1);
-  /*! Adds "log10(arg)" to model tree */
-  inline NodeID     AddLog10(NodeID iArg1);
-  /*! Adds "cos(arg)" to model tree */
-  inline NodeID     AddCos(NodeID iArg1);
-  /*! Adds "sin(arg)" to model tree */
-  inline NodeID     AddSin(NodeID iArg1);
-  /*! Adds "tan(arg)" to model tree */
-  inline NodeID     AddTan(NodeID iArg1);
-  /*! Adds "acos(arg)" to model tree */
-  inline NodeID     AddACos(NodeID iArg1);
-  /*! Adds "asin(arg)" to model tree */
-  inline NodeID     AddASin(NodeID iArg1);
-  /*! Adds "atan(arg)" to model tree */
-  inline NodeID     AddATan(NodeID iArg1);
-  /*! Adds "cosh(arg)" to model tree */
-  inline NodeID     AddCosH(NodeID iArg1);
-  /*! Adds "sinh(arg)" to model tree */
-  inline NodeID     AddSinH(NodeID iArg1);
-  /*! Adds "tanh(arg)" to model tree */
-  inline NodeID     AddTanH(NodeID iArg1);
-  /*! Adds "acosh(arg)" to model tree */
-  inline NodeID     AddACosH(NodeID iArg1);
-  /*! Adds "asinh(arg)" to model tree */
-  inline NodeID     AddASinH(NodeID iArg1);
-  /*! Adds "atanh(args)" to model tree */
-  inline NodeID     AddATanH(NodeID iArg1);
-  /*! Adds "sqrt(arg)" to model tree */
-  inline NodeID     AddSqRt(NodeID iArg1);
-  /*! Adds "arg1=arg2" to model tree */
-  inline NodeID     AddEqual(NodeID iArg1, NodeID iArg2);
-  /*! Adds "arg1=arg2" as assignment to model tree */
-  inline NodeID     AddAssign(NodeID iArg1, NodeID iArg2);
-public :
-  /*! Constructor */
-  DataTree(SymbolTable &symbol_table_arg);
-  /*! Destructor */
-  ~DataTree();
-};
-//------------------------------------------------------------------------------
-inline NodeID DataTree::PushToken(NodeID iArg1,int iOpCode, NodeID iArg2, Type iType1)
-{
-  MetaToken*  lToken = new MetaToken(iArg1, iType1, iArg2, iOpCode);
-
-  if (iOpCode != NoOpCode)
-    {
-      lToken->op_name = OperatorTable::str(iOpCode);
-    }
-  else
-    {
-      lToken->op_name = "";
-    }
-  lToken->reference_count.resize(current_order+1,0);
-  lToken->idx = nodeCounter++;
-  mModelTree.push_back(lToken);
-  //Updating reference counters and time costs
-  if (iType1 == eTempResult )
-    {
-      lToken->cost = OperatorTable::cost(iOpCode,offset)+iArg1->cost;
-      IncrementReferenceCount(iArg1);
-    }
-  if(iArg2 != NullID)
-    {
-      lToken->cost += iArg2->cost;
-      IncrementReferenceCount(iArg2);
-    }
-  mIndexOfTokens[*lToken] = lToken;
-
-  /*
-    std::cout << "ID = " << ID << " / " << mIndexOfTokens.size()-1<< " - " << getIDOfToken(lToken2) << " " <<
-    lToken.op_code <<  " " << lToken.id1   << " " <<
-    lToken.id2 << " " << lToken.type1<<  "\n";
-  */
-  /*
-    std::cout << "\nmIndexOfTokens------------\n";
-    for (iter=mIndexOfTokens.begin();iter != mIndexOfTokens.end(); iter++)
-    std::cout << (*iter).second << " -> " << (*iter).first << std::endl;
-  */
-  return lToken;
-}
-
-//------------------------------------------------------------------------------
-inline void DataTree::IncrementReferenceCount(NodeID token)
-{
-  int s = token->reference_count.size();
-  for (int i = s; i <= current_order; i++)
-    {
-      int rc = token->reference_count[i-1];
-      token->reference_count.push_back(rc);
-    }
-  token->reference_count[current_order]++;
-
-}
-
-//------------------------------------------------------------------------------
-inline NodeID DataTree::getIDOfToken(const MToken &iToken)
-{
-  TreeMap::iterator iter = mIndexOfTokens.find(iToken);
-  if (iter != mIndexOfTokens.end())
-    return iter->second;
-  else
-    return NullID;
-}
-
-//------------------------------------------------------------------------------
-inline NodeID DataTree::AddTerminal(NodeID iArg1, Type iType1)
-{
-
-  if (iType1 == eTempResult)
-    {
-      return iArg1;
-    }
-  else
-    {
-      MToken  lToken(iArg1, iType1, NullID, NoOpCode);
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, NoOpCode, NullID, iType1);
-    }
-
-}
-
-inline NodeID DataTree::AddTerminal(std::string iArgName, int iLag)
-{
-  int   id;
-  Type  type;
-
-  if (!symbol_table.Exist(iArgName))
-    {
-      std::string msg = "unknown symbol: " + iArgName;
-      (* error) (msg.c_str());
-      exit(-1);
-    }
-  type = symbol_table.getType(iArgName);
-  if (type != eUNDEF)
-    {
-      symbol_table.SetReferenced(iArgName);
-      if (type == eEndogenous ||
-          type == eExogenousDet  ||
-          type == eExogenous  ||
-          type == eRecursiveVariable)
-        id = variable_table.AddVariable(iArgName,iLag);
-      else
-        id = symbol_table.getID(iArgName);
-
-    }
-  else
-    {
-      std::string msg = "unknown parameter: " + iArgName;
-      (* error) (msg.c_str());
-      exit(-1);
-    }
-  return AddTerminal((NodeID) id, type);
-}
-
-inline NodeID DataTree::AddPlus(NodeID iArg1, NodeID iArg2)
-{
-
-  if (iArg1 != Zero && iArg2 != Zero)
-    {
-      MToken  lToken;
-
-      if (iArg1 <= iArg2)
-        lToken = MToken(iArg1, eTempResult, iArg2, token::PLUS);
-      else
-        lToken = MToken(iArg2, eTempResult, iArg1, token::PLUS);
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-
-      // To treat commutativity of "+"
-      // Token id (iArg1 and iArg2) are sorted
-      if (iArg1 <= iArg2)
-        return PushToken(iArg1, token::PLUS, iArg2);
-      else
-        return PushToken(iArg2, token::PLUS, iArg1);
-    }
-  else if (iArg1 != Zero)
-    {
-      return iArg1;
-    }
-  else if (iArg2 != Zero)
-    {
-      return iArg2;
-    }
-  else
-    {
-      return Zero;
-    }
-}
-
-inline NodeID DataTree::AddMinus(NodeID iArg1, NodeID iArg2)
-{
-
-  if (iArg1 != Zero && iArg2 != Zero)
-    {
-      MToken  lToken(iArg1, eTempResult, iArg2, token::MINUS);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, token::MINUS, iArg2);
-    }
-  else if (iArg1 != Zero)
-    {
-      return iArg1;
-    }
-  else if (iArg2 != Zero)
-    {
-      return AddUMinus(iArg2);
-    }
-  else
-    {
-      return Zero;
-    }
-}
-
-inline NodeID DataTree::AddUMinus(NodeID iArg1)
-{
-  if (iArg1 != Zero )
-    {
-      MToken  lToken(iArg1, eTempResult, NullID, token::UMINUS);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      if (iArg1->type1 == eTempResult &&
-          iArg1->op_code == token::UMINUS)
-        {
-          IncrementReferenceCount(iArg1->id1);
-          return iArg1->id1;
-
-        }
-      return PushToken(iArg1, token::UMINUS);
-    }
-  else
-    {
-      return Zero;
-    }
-
-}
-
-inline NodeID DataTree::AddTimes(NodeID iArg1, NodeID iArg2)
-{
-
-  if (iArg1 != Zero && iArg1 != One && iArg2 != Zero && iArg2 != One)
-    {
-      MToken  lToken;
-
-      if (iArg1 <= iArg2)
-        lToken = MToken(iArg1, eTempResult, iArg2, token::TIMES);
-      else
-        lToken = MToken(iArg2, eTempResult, iArg1, token::TIMES);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      // Testing if arg[1 or 2] is "-1", in this case add "-arg" instead of "-1*iarg"
-      // Here 2 is ID of "-1"
-      if (iArg1 == MinusOne)
-        return AddUMinus(iArg2);
-      else if (iArg2 == MinusOne)
-        return AddUMinus(iArg1);
-      else
-        {
-          // To treat commutativity of "*"
-          // Token id (iArg1 and iArg2) are sorted
-          if (iArg1 <= iArg2)
-            return PushToken(iArg1, token::TIMES, iArg2);
-          else
-            return PushToken(iArg2, token::TIMES, iArg1);
-        }
-    }
-  else if (iArg1 != Zero && iArg1 != One && iArg2 == One)
-    {
-      return iArg1;
-    }
-  else if (iArg2 != Zero && iArg2 != One && iArg1 == One)
-    {
-      return iArg2;
-    }
-  else if (iArg2 == One && iArg1 == One)
-    {
-      return One;
-    }
-  else
-    {
-      return Zero;
-    }
-}
-
-inline NodeID DataTree::AddDivide(NodeID iArg1, NodeID iArg2)
-{
-
-  if (iArg1 != Zero && iArg2 != Zero && iArg2 != One)
-    {
-      MToken  lToken(iArg1, eTempResult, iArg2, token::DIVIDE);
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, token::DIVIDE, iArg2);
-    }
-  else if (iArg2 == One)
-    {
-      return iArg1;
-    }
-  else if (iArg1 == Zero && iArg2 != Zero)
-    {
-      return Zero;
-    }
-  else
-    {
-      std::cout << "DIVIDE 0/0 non available\n";
-      exit(-1);
-    }
-}
-
-inline NodeID DataTree::AddPower(NodeID iArg1, NodeID iArg2)
-{
-  if (iArg1 != Zero && iArg2 != Zero && iArg2 != One)
-    {
-      MToken  lToken(iArg1, eTempResult, iArg2, token::POWER);
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, token::POWER, iArg2);
-    }
-  else if (iArg2 == One)
-    {
-      return iArg1;
-    }
-  else if (iArg2 == Zero)
-    {
-      return One;
-    }
-  else
-    {
-      return Zero;
-    }
-
-}
-
-inline NodeID DataTree::AddExp(NodeID iArg1)
-{
-
-  if (iArg1 != Zero)
-    {
-      MToken  lToken(iArg1, eTempResult, NullID, token::EXP);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-
-      return PushToken(iArg1, token::EXP);
-    }
-  else
-    {
-      return One;
-    }
-}
 
-inline NodeID DataTree::AddLog(NodeID iArg1)
-{
-  if (iArg1 != Zero && iArg1 != One)
-    {
-      MToken  lToken(iArg1, eTempResult, NullID, token::LOG);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, token::LOG);
-    }
-  else if (iArg1 == One)
-    {
-      return Zero;
-    }
-  else
-    {
-      std::cout << "log(0) isn't available\n";
-      exit(-1);
-    }
-}
-
-inline NodeID DataTree::AddLog10(NodeID iArg1)
-{
-  if (iArg1 != Zero && iArg1 != One)
-    {
-      MToken  lToken(iArg1, eTempResult, NullID, token::LOG10);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-
-      return PushToken(iArg1, token::LOG10);
-    }
-  else if (iArg1 == One)
-    {
-      return Zero;
-    }
-  else
-    {
-      std::cout << "log10(0) isn't available\n";
-      exit(-1);
-    }
-}
-
-inline NodeID DataTree::AddCos(NodeID iArg1)
-{
-  if (iArg1 != Zero)
-    {
-      MToken  lToken(iArg1, eTempResult, NullID, token::COS);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, token::COS);
-    }
-  else
-    {
-      return One;
-    }
-}
-
-inline NodeID DataTree::AddSin(NodeID iArg1)
-{
-  if (iArg1 != Zero)
-    {
-      MToken  lToken(iArg1, eTempResult, NullID, token::SIN);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, token::SIN);
-    }
-  else
-    {
-      return Zero;
-    }
-}
-
-inline NodeID DataTree::AddTan(NodeID iArg1)
-{
-  if (iArg1 != Zero)
-    {
-      MToken  lToken(iArg1, eTempResult, NullID, token::TAN);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, token::TAN);
-    }
-  else
-    {
-      return Zero;
-    }
-}
-
-inline NodeID DataTree::AddACos(NodeID iArg1)
-{
-  MToken  lToken(iArg1, eTempResult, NullID, token::ACOS);
-
-  NodeID ID = getIDOfToken(lToken);
-  if (ID != NullID)
-    {
-      return ID;
-    }
-  return PushToken(iArg1, token::ACOS);
-}
-
-inline NodeID DataTree::AddASin(NodeID iArg1)
-{
-  if (iArg1 != Zero)
-    {
-      MToken  lToken(iArg1, eTempResult, NullID, token::ASIN);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, token::ASIN);
-    }
-  else
-    {
-      return Zero;
-    }
-}
-
-inline NodeID DataTree::AddATan(NodeID iArg1)
-{
-  if (iArg1 != Zero)
-    {
-      MToken  lToken(iArg1, eTempResult, NullID, token::ATAN);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, token::ATAN);
-    }
-  else
-    {
-      return Zero;
-    }
-}
-
-inline NodeID DataTree::AddCosH(NodeID iArg1)
-{
-  if (iArg1 != Zero)
-    {
-      MToken  lToken(iArg1, eTempResult, NullID, token::COSH);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, token::COSH);
-    }
-  else
-    {
-      return Zero;
-    }
-}
-
-inline NodeID DataTree::AddSinH(NodeID iArg1)
-{
-  if (iArg1 != Zero)
-    {
-      MToken  lToken(iArg1, eTempResult, NullID, token::SINH);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, token::SINH);
-    }
-  else
-    {
-      return Zero;
-    }
-}
-
-inline NodeID DataTree::AddTanH(NodeID iArg1)
-{
-  if (iArg1 != Zero)
-    {
-      MToken  lToken(iArg1, eTempResult, NullID, token::TANH);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, token::TANH);
-    }
-  else
-    {
-      return Zero;
-    }
-}
-
-inline NodeID DataTree::AddACosH(NodeID iArg1)
-{
-  MToken  lToken(iArg1, eTempResult, NullID, token::ACOSH);
-
-  NodeID ID = getIDOfToken(lToken);
-  if (ID != NullID)
-    {
-      return ID;
-    }
-  return PushToken(iArg1, token::ACOSH);
-}
-
-inline NodeID DataTree::AddASinH(NodeID iArg1)
-{
-  if (iArg1 != Zero)
-    {
-      MToken  lToken(iArg1, eTempResult, NullID, token::ASINH);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, token::ASINH);
-    }
-  else
-    {
-      return Zero;
-    }
-}
-
-inline NodeID DataTree::AddATanH(NodeID iArg1)
-{
-  if (iArg1 != Zero)
-    {
-      MToken  lToken(iArg1, eTempResult, NullID, token::ATANH);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, token::ATANH);
-    }
-  else
-    {
-      return Zero;
-    }
-}
+  //! Raised when a local parameter is declared twice
+  class LocalParameterException
+  {
+  public:
+    string name;
+    LocalParameterException(const string &name_arg) : name(name_arg) {}
+  };
+
+  NodeID AddNumConstant(const string &value);
+  NodeID AddVariable(const string &name, int lag = 0);
+  //! Adds "arg1+arg2" to model tree
+  NodeID AddPlus(NodeID iArg1, NodeID iArg2);
+  //! Adds "arg1-arg2" to model tree
+  NodeID AddMinus(NodeID iArg1, NodeID iArg2);
+  //! Adds "-arg" to model tree
+  NodeID AddUMinus(NodeID iArg1);
+  //! Adds "arg1*arg2" to model tree
+  NodeID AddTimes(NodeID iArg1, NodeID iArg2);
+  //! Adds "arg1/arg2" to model tree
+  NodeID AddDivide(NodeID iArg1, NodeID iArg2);
+  //! Adds "arg1^arg2" to model tree
+  NodeID AddPower(NodeID iArg1, NodeID iArg2);
+  //! Adds "exp(arg)" to model tree
+  NodeID AddExp(NodeID iArg1);
+  //! Adds "log(arg)" to model tree
+  NodeID AddLog(NodeID iArg1);
+  //! Adds "log10(arg)" to model tree
+  NodeID AddLog10(NodeID iArg1);
+  //! Adds "cos(arg)" to model tree
+  NodeID AddCos(NodeID iArg1);
+  //! Adds "sin(arg)" to model tree
+  NodeID AddSin(NodeID iArg1);
+  //! Adds "tan(arg)" to model tree
+  NodeID AddTan(NodeID iArg1);
+  //! Adds "acos(arg)" to model tree
+  NodeID AddACos(NodeID iArg1);
+  //! Adds "asin(arg)" to model tree
+  NodeID AddASin(NodeID iArg1);
+  //! Adds "atan(arg)" to model tree
+  NodeID AddATan(NodeID iArg1);
+  //! Adds "cosh(arg)" to model tree
+  NodeID AddCosH(NodeID iArg1);
+  //! Adds "sinh(arg)" to model tree
+  NodeID AddSinH(NodeID iArg1);
+  //! Adds "tanh(arg)" to model tree
+  NodeID AddTanH(NodeID iArg1);
+  //! Adds "acosh(arg)" to model tree
+  NodeID AddACosH(NodeID iArg1);
+  //! Adds "asinh(arg)" to model tree
+  NodeID AddASinH(NodeID iArg1);
+  //! Adds "atanh(args)" to model tree
+  NodeID AddATanH(NodeID iArg1);
+  //! Adds "sqrt(arg)" to model tree
+  NodeID AddSqRt(NodeID iArg1);
+  //! Adds "arg1=arg2" to model tree
+  NodeID AddEqual(NodeID iArg1, NodeID iArg2);
+  void AddLocalParameter(const string &name, NodeID value) throw (LocalParameterException);
+};
 
-inline NodeID DataTree::AddSqRt(NodeID iArg1)
+inline NodeID
+DataTree::AddUnaryOp(UnaryOpcode op_code, NodeID arg)
 {
-  if (iArg1 != Zero)
-    {
-      MToken  lToken(iArg1, eTempResult, NullID, token::SQRT);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        {
-          return ID;
-        }
-      return PushToken(iArg1, token::SQRT);
-    }
+  unary_op_node_map_type::iterator it = unary_op_node_map.find(make_pair(arg, op_code));
+  if (it != unary_op_node_map.end())
+    return it->second;
   else
-    {
-      return Zero;
-    }
+    return new UnaryOpNode(*this, op_code, arg);
 }
 
-inline NodeID DataTree::AddEqual(NodeID iArg1, NodeID iArg2)
+inline NodeID
+DataTree::AddBinaryOp(NodeID arg1, BinaryOpcode op_code, NodeID arg2)
 {
-  if (iArg1 == Zero && iArg2 == Zero)
-    return ZeroEqZero;
+  binary_op_node_map_type::iterator it = binary_op_node_map.find(make_pair(make_pair(arg1, arg2), op_code));
+  if (it != binary_op_node_map.end())
+    return it->second;
   else
-    {
-      MToken  lToken(iArg1, eTempResult, iArg2, token::EQUAL);
-
-      NodeID ID = getIDOfToken(lToken);
-      if (ID != NullID)
-        return ID;
-
-      return PushToken(iArg1, token::EQUAL, iArg2);
-    }
-}
-
-inline NodeID DataTree::AddAssign(NodeID iArg1, NodeID iArg2)
-{
-  MToken  lToken(iArg1, eTempResult, iArg2, token::ASSIGN);
-
-  NodeID ID = getIDOfToken(lToken);
-  if (ID != NullID)
-    {
-      return ID;
-    }
-  return PushToken(iArg1, token::ASSIGN, iArg2);
+    return new BinaryOpNode(*this, arg1, op_code, arg2);
 }
 
-//------------------------------------------------------------------------------
 #endif
diff --git a/parser.src/include/ExprNode.hh b/parser.src/include/ExprNode.hh
new file mode 100644
index 0000000000000000000000000000000000000000..dfd1e16eebf6ded514d9d9774374b4fb94e88eb4
--- /dev/null
+++ b/parser.src/include/ExprNode.hh
@@ -0,0 +1,174 @@
+#ifndef _EXPR_NODE_HH
+#define _EXPR_NODE_HH
+
+using namespace std;
+
+#include <set>
+#include <map>
+
+#include "SymbolTableTypes.hh"
+
+class DataTree;
+
+typedef class ExprNode *NodeID;
+
+struct ExprNodeLess;
+
+//! Type for set of temporary terms
+/*! They are ordered by index number thanks to ExprNodeLess */
+typedef set<NodeID, ExprNodeLess> temporary_terms_type;
+
+//! Base class for expression nodes
+class ExprNode
+{
+  friend class DataTree;
+  friend class ModelTree;
+  friend class ExprNodeLess;
+  friend class NumConstNode;
+  friend class VariableNode;
+  friend class UnaryOpNode;
+  friend class BinaryOpNode;
+
+private:
+  //! Computes derivative w.r. to variable varID (but doesn't store it in derivatives map)
+  /*! You shoud use getDerivative() to get the benefit of symbolic a priori and of caching */
+  virtual NodeID computeDerivative(int varID) = 0;
+
+protected:
+  //! Reference to the enclosing DataTree
+  DataTree &datatree;
+
+  //! Index number
+  int idx;
+
+  //! Set of variable IDs with respect to which the derivative is potentially non-null
+  set<int> non_null_derivatives;
+
+  //! Used for caching of first order derivatives (when non-null)
+  map<int, NodeID> derivatives;
+
+  //! Cost of computing current node
+  /*! Nodes included in temporary_terms are considered having a null cost */
+  virtual int cost(temporary_terms_type &temporary_terms) const;
+
+public:
+  ExprNode(DataTree &datatree_arg);
+  virtual ~ExprNode();
+
+  //! Returns derivative w.r. to variable varID
+  /*! Uses a symbolic a priori to pre-detect null derivatives, and caches the result for other derivatives (to avoid computing it several times)
+   For an equal node, returns the derivative of lhs minus rhs */
+  NodeID getDerivative(int varID);
+
+  //! Returns precedence of node
+  /*! Equals 100 for constants, variables, unary ops, and temporary terms */
+  virtual int precedence(const temporary_terms_type &temporary_terms) const;
+
+  //! Fills temporary_terms set, using reference counts
+  /*! A node will be marked as a temporary term if it is referenced at least two times (i.e. has at least two parents), and has a computing cost (multiplied by reference count) greater to datatree.min_cost */
+  virtual void computeTemporaryTerms(map<NodeID, int> &reference_count, temporary_terms_type &temporary_terms) const;
+
+  //! Writes output of node, using a Txxx notation for nodes in temporary_terms
+  virtual void writeOutput(ostream &output, bool is_dynamic, const temporary_terms_type &temporary_terms) const = 0;
+};
+
+//! Object used to compare two nodes (using their indexes)
+struct ExprNodeLess
+{
+  bool operator()(NodeID arg1, NodeID arg2) const
+  {
+    return arg1->idx < arg2->idx;
+  }
+};
+
+//! Numerical constant node
+class NumConstNode : public ExprNode
+{
+private:
+  //! Id from numerical constants table
+  const int id;
+  virtual NodeID computeDerivative(int varID);
+public:
+  NumConstNode(DataTree &datatree_arg, int id_arg);
+  virtual void writeOutput(ostream &output, bool is_dynamic, const temporary_terms_type &temporary_terms) const;
+};
+
+//! Symbol or variable node
+class VariableNode : public ExprNode
+{
+private:
+  //! Id of the symbol/variable
+  /*! For an endogenous, exogenous or recursive variable, the id is taken from the variable table (before sorting). For other types of symbols, it's the id from the symbol table. */
+  const int id;
+  const Type type;
+  virtual NodeID computeDerivative(int varID);
+public:
+  VariableNode(DataTree &datatree_arg, int id_arg, Type type_arg);
+  virtual void writeOutput(ostream &output, bool is_dynamic, const temporary_terms_type &temporary_terms) const;
+};
+
+enum UnaryOpcode
+  {
+    oUminus,
+    oExp,
+    oLog,
+    oLog10,
+    oCos,
+    oSin,
+    oTan,
+    oAcos,
+    oAsin,
+    oAtan,
+    oCosh,
+    oSinh,
+    oTanh,
+    oAcosh,
+    oAsinh,
+    oAtanh,
+    oSqrt
+  };
+
+//! Unary operator node
+class UnaryOpNode : public ExprNode
+{
+  friend class DataTree;
+private:
+  const NodeID arg;
+  const UnaryOpcode op_code;
+  virtual NodeID computeDerivative(int varID);
+
+  int cost(temporary_terms_type &temporary_terms) const;
+public:
+  UnaryOpNode(DataTree &datatree_arg, UnaryOpcode op_code_arg, const NodeID arg_arg);
+  virtual void computeTemporaryTerms(map<NodeID, int> &reference_count, temporary_terms_type &temporary_terms) const;
+  virtual void writeOutput(ostream &output, bool is_dynamic, const temporary_terms_type &temporary_terms) const;
+};
+
+enum BinaryOpcode
+  {
+    oPlus,
+    oMinus,
+    oTimes,
+    oDivide,
+    oPower,
+    oEqual
+  };
+
+//! Binary operator node
+class BinaryOpNode : public ExprNode
+{
+  friend class ModelTree;
+private:
+  const NodeID arg1, arg2;
+  const BinaryOpcode op_code;
+  virtual NodeID computeDerivative(int varID);
+  int cost(temporary_terms_type &temporary_terms) const;
+public:
+  BinaryOpNode(DataTree &datatree_arg, const NodeID arg1_arg,
+               BinaryOpcode op_code_arg, const NodeID arg2_arg);
+  virtual int precedence(const temporary_terms_type &temporary_terms) const;
+  virtual void computeTemporaryTerms(map<NodeID, int> &reference_count, temporary_terms_type &temporary_terms) const;
+  virtual void writeOutput(ostream &output, bool is_dynamic, const temporary_terms_type &temporary_terms) const;
+};
+
+#endif
diff --git a/parser.src/include/ModFile.hh b/parser.src/include/ModFile.hh
index 96060c86dccb1a7bd207ed258d4556d28d7babe1..edfdd8181cefc5b8637ebfc7fa4af3735c0aaeca 100644
--- a/parser.src/include/ModFile.hh
+++ b/parser.src/include/ModFile.hh
@@ -45,7 +45,7 @@ public:
     \param clear_all Should a "clear all" instruction be written to output ?
     \todo make this method const
   */
-  void writeOutputFiles(const string &basename, bool clear_all);
+  void writeOutputFiles(const string &basename, bool clear_all) const;
 };
 
 #endif // ! MOD_FILE_HH
diff --git a/parser.src/include/ModelTree.hh b/parser.src/include/ModelTree.hh
index 378acc4b20745f29e32ae1f872ab0690f56b4c5e..c95157fef0c31a0f5244386d271128e93503b021 100644
--- a/parser.src/include/ModelTree.hh
+++ b/parser.src/include/ModelTree.hh
@@ -1,72 +1,73 @@
 #ifndef _MODELTREE_HH
 #define _MODELTREE_HH
-//------------------------------------------------------------------------------
-/*! \file
-  \version 1.0
-  \date 04/13/2003
-  \par This file defines the ModelTree class.
-*/
-//------------------------------------------------------------------------------
+
+using namespace std;
+
 #include <string>
 #include <vector>
-#include <list>
-#include <stack>
-#include <sstream>
-#include <fstream>
+#include <map>
 #include <ostream>
-//------------------------------------------------------------------------------
+
 #include "SymbolTable.hh"
 #include "NumericalConstants.hh"
-#include "ModelTypes.hh"
 #include "DataTree.hh"
-//------------------------------------------------------------------------------
-/*!
-  \class  ModelTree
-  \brief  Stores a model equations and derivatives.
-*/
+
+//! Stores a model's equations and derivatives
 class ModelTree : public DataTree
 {
-private :
-  /*! Stores ID of equations and their derivatives */
-  std::vector<std::vector<DerivativeIndex> > mDerivativeIndex;
-  /*!
-    A token is writen as a temporary expression
-    if its cost is greater than min_cost
+private:
+  //! Stores declared equations
+  vector<BinaryOpNode *> equations;
+
+  typedef map<pair<int, int>, NodeID> first_derivatives_type;
+  //! First order derivatives
+  /*! First index is equation number, second is variable w.r. to which is computed the derivative.
+    Only non-null derivatives are stored in the map.
+    Variable indexes used are those of the variable_table, before sorting.
   */
-  int           min_cost;
-  /*! left and right parentheses can be (,[ or ),] */
-  char          lpar, rpar;
-  //! Reference to numerical constants table
-  const NumericalConstants &num_constants;
+  first_derivatives_type first_derivatives;
 
-  /*! Computes argument derivative */
-  inline NodeID     DeriveArgument(NodeID iArg, Type iType, int iVarID);
-  /*! Gets output argument of terminal token */
-  inline std::string    getArgument(NodeID id, Type type, EquationType  iEquationType);
-  /*! Gets expression of part of model tree */
-  std::string getExpression(NodeID StartID, EquationType iEquationType);
-  inline int optimize(NodeID id);
-  /*! Computes derivatives of ModelTree */
-  void    derive(int iOrder);
+  typedef map<pair<int, pair<int, int> >, NodeID> second_derivatives_type;
+  //! Second order derivatives
+  /*! First index is equation number, second and third are variables w.r. to which is computed the derivative.
+    Only non-null derivatives are stored in the map.
+    Contains only second order derivatives where var1 >= var2 (for obvious symmetry reasons).
+    Variable indexes used are those of the variable_table, before sorting.
+  */
+  second_derivatives_type second_derivatives;
+
+  //! Temporary terms (those which will be noted Txxxx)
+  temporary_terms_type temporary_terms;
+
+  //! Computes derivatives of ModelTree
+  void derive(int order);
+  //! Computes temporary terms
+  void computeTemporaryTerms(int order);
+
+  //! Writes temporary terms
+  void writeTemporaryTerms(ostream &output, bool is_dynamic) const;
+  //! Writes local parameters
+  void writeLocalParameters(ostream &output, bool is_dynamic) const;
+  //! Writes model equations
+  void writeModelEquations(ostream &output, bool is_dynamic) const;
   //! Writes the static model equations and its derivatives
   /*! \todo handle hessian in C output */
-  void writeStaticModel(std::ostream &StaticOutput);
+  void writeStaticModel(ostream &StaticOutput) const;
   //! Writes the dynamic model equations and its derivatives
-  void writeDynamicModel(std::ostream &DynamicOutput);
+  void writeDynamicModel(ostream &DynamicOutput) const;
   //! Writes static model file (Matlab version)
-  void writeStaticMFile(const std::string &static_basename);
+  void writeStaticMFile(const string &static_basename) const;
   //! Writes static model file (C version)
-  void writeStaticCFile(const std::string &static_basename);
+  void writeStaticCFile(const string &static_basename) const;
   //! Writes dynamic model file (Matlab version)
-  void writeDynamicMFile(const std::string &dynamic_basename);
+  void writeDynamicMFile(const string &dynamic_basename) const;
   //! Writes dynamic model file (C version)
-  void writeDynamicCFile(const std::string &dynamic_basename);
+  void writeDynamicCFile(const string &dynamic_basename) const;
 
 public:
-  //! Constructor
-  ModelTree(SymbolTable &symbol_table_arg, const NumericalConstants &num_constants);
-  //! Number of equations contained in this model tree
-  int eq_nbr;
+  ModelTree(SymbolTable &symbol_table_arg, NumericalConstants &num_constants);
+  //! Declare a node as an equation of the model
+  void addEquation(NodeID eq);
   //! Do some checking
   void checkPass() const;
   //! Whether dynamic Jacobian (w.r. to endogenous) should be written
@@ -82,13 +83,11 @@ public:
     calling this function */
   void computingPass();
   //! Writes model initialization and lead/lag incidence matrix to output
-  void writeOutput(std::ostream &output) const;
+  void writeOutput(ostream &output) const;
   //! Writes static model file
-  /*! \todo make this method const */
-  void writeStaticFile(const std::string &basename);
+  void writeStaticFile(const string &basename) const;
   //! Writes dynamic model file
-  /*! \todo make this method const */
-  void writeDynamicFile(const std::string &basename);
+  void writeDynamicFile(const string &basename) const;
 };
-//------------------------------------------------------------------------------
+
 #endif
diff --git a/parser.src/include/ModelTypes.hh b/parser.src/include/ModelTypes.hh
deleted file mode 100644
index d7efbcd5ddded4babcbc365e26feb17c86f37489..0000000000000000000000000000000000000000
--- a/parser.src/include/ModelTypes.hh
+++ /dev/null
@@ -1,230 +0,0 @@
-#ifndef _MODELTYPES_HH
-#define _MODELTYPES_HH
-//------------------------------------------------------------------------------
-/*! \file
-  \version 1.0
-  \date 04/13/2004
-  \par This file defines types for ModelTree class.
-*/
-//------------------------------------------------------------------------------
-#include <string>
-#include <map>
-
-typedef class MetaToken* NodeID;
-
-/*!
-  \struct  DerivativeIndex
-  \brief  Derivative index structure
-*/
-//------------------------------------------------------------------------------
-struct DerivativeIndex
-{
-  /*!/ id of "equal token" in mModelTree */
-  NodeID token_id;
-  /*! id of equation being derived */
-  int equation_id;
-  /*! column number of Jacobian or Hessian matrix */
-  int derivators;
-  /*! Constructor */
-  inline DerivativeIndex(NodeID iToken_id, int iEquationID, int iDerivator)
-  {
-    token_id = iToken_id;
-    equation_id = iEquationID;
-    derivators = iDerivator;
-  }
-};
-/*!
-  \struct  MToken
-  \brief  Basic token structure, essentially for computing hash keys
-*/
-//------------------------------------------------------------------------------
-struct MToken
-{
-public :
-  /*! ID of first operand */
-  NodeID  id1;
-  /*! Type of first operand */
-  Type  type1;
-  /*! ID of second operand */
-  NodeID  id2;
-  /*! Operator code */
-  int   op_code;
-  /*! Constructor */
-  inline MToken(NodeID iId1 = NULL, Type iType1 = eUNDEF, NodeID iId2 = NULL, int iOpCode = -1)
-  {
-    id1 = iId1;
-    type1 = iType1;
-    id2 = iId2;
-    op_code = iOpCode;
-
-  }
-  /*! Copy constructor */
-  inline MToken(const MToken& t)
-  {
-    id1 = t.id1;
-    id2 = t.id2;
-    type1 = t.type1;
-    op_code = t.op_code;
-  }
-  /*! Destructor */
-  ~MToken() { }
-};
-
-/*!
-  \struct MTokenLess
-  \brief Class which defines a lexicographic order over MToken, used in std::map
-*/
-struct MTokenLess
-{
-  bool operator()(const MToken &n1, const MToken &n2) const
-  {
-    if (n1.id1 != n2.id1)
-      return(n1.id1 < n2.id1);
-    else if (n1.id2 != n2.id2)
-      return(n1.id2 < n2.id2);
-    else if (n1.type1 != n2.type1)
-      return(n1.type1 < n2.type1);
-    else
-      return(n1.op_code < n2.op_code);
-  }
-};
-
-// For MetaToken::getDerivativeAddress()
-class ModelTree;
-
-/*!
-  \struct  MetaToken
-  \brief  Meta token structure
-*/
-struct MetaToken : public MToken
-{
-private:
-  /*! Address of first order partial derivative */
-  std::map<int, NodeID, std::less<int> >  d1;
-public :
-  /*! Index of token starting from zero */
-  int             idx;
-  /*!
-    Number of times a given token is referenced?
-    Each element of vector refer to derivative 0, 1, 2, etc
-  */
-  std::vector<int>        reference_count;
-  /*! Commulative computation time cost */
-  int               cost;
-  /*!
-    If the token can be a temporary result, this flag is set to 1.
-    If it can not be a temporary result (operation with constants etc)
-    it is set to 0.
-    After writing corresponding expression for the first time it is set to -1.
-  */
-  int             tmp_status;
-  /*! Name of unkown functions */
-  std::string             func_name;
-  /*! Expression of token, exp[0] for static model
-    exp[1] for dynamic model */
-  std::string           exp[2];
-  /*! Flag : operand 1 is followed or not */
-  bool            followed1;
-  /*! Flag : operand 2 is followed or not */
-  bool            followed2;
-  /*! Operator name */
-  std::string           op_name;
-  /*! set to one when left node has been treated */
-  int left_done;
-  /*! set to one when right node has been treated */
-  int right_done;
-  /*! set to one if closing parenthesis after token */
-  int close_parenthesis;
-
-  /*! Ordered list of variable IDs with respect to which the derivative is potentially non-null */ 
-  std::vector<int> non_null_derivatives;
-
-  /*! Constructor */
-  inline MetaToken(NodeID iId1 = NULL, Type iType1 = eUNDEF,NodeID iId2= NULL, int iOpCode = -1)
-    : MToken(iId1, iType1,iId2,iOpCode)
-  {
-    cost = 0;
-    followed1 = followed2 = false;
-    tmp_status = 0;
-    left_done = 0;
-    right_done = 0;
-    close_parenthesis = 0;
-
-    switch(type1)
-      {
-      case eEndogenous:
-      case eExogenous:
-      case eExogenousDet:
-      case eRecursiveVariable:
-        // For a variable, the only non-null derivative is with respect to itself
-        non_null_derivatives.push_back((long int) id1);
-        break;
-      case eParameter:
-      case eLocalParameter:
-      case eNumericalConstant:
-      case eUNDEF:
-        // All derivatives are null
-        break;
-      case eTempResult:
-        if (!id2)
-          non_null_derivatives = id1->non_null_derivatives;
-        else
-          {
-            // Compute set union of id1->non_null_derivates and id2->non_null_derivatives
-            // The result is ordered by ascending order
-            std::vector<int>::iterator it1, it2;
-            it1 = id1->non_null_derivatives.begin();
-            it2 = id2->non_null_derivatives.begin();
-            while(it1 != id1->non_null_derivatives.end()
-                  && it2 != id2->non_null_derivatives.end())
-              {
-                if (*it1 == *it2)
-                  {
-                    non_null_derivatives.push_back(*it1++);
-                    it2++;
-                  }
-                else if (*it1 < *it2)
-                  non_null_derivatives.push_back(*it1++);
-                else
-                  non_null_derivatives.push_back(*it2++);
-              }
-            while(it1 != id1->non_null_derivatives.end())
-                non_null_derivatives.push_back(*it1++);
-            while(it2 != id2->non_null_derivatives.end())
-              non_null_derivatives.push_back(*it2++);
-          }
-        break;
-      }
-  }
-  /*! Destructor */
-  ~MetaToken()
-  {
-  }
-
-  /*! Sets derivative with respect to iVarID equal to iDerivative */
-  inline void setDerivativeAddress(NodeID iDerivative, int iVarID)
-  {
-    d1[iVarID] = iDerivative;
-  }
-
-  //! Get derivative with respect to iVarID.
-  /*
-    Defined in ModelTree.cc because it needs a reference to the enclosing ModelTree,
-    and is only used in that source file.
-
-    \param iVarID variable with respect to get derivative
-    \param model_tree enclosing ModelTree
-  */
-  inline NodeID getDerivativeAddress(int iVarID, const ModelTree &model_tree) const;
-};
-//------------------------------------------------------------------------------
-/*! Equation type enum */
-enum EquationType
-  {
-    eStaticEquations = 0,          //!< Equation of static model
-    eDynamicEquations = 1,         //!<  Equation of dynamic model
-    eStaticDerivatives = 2,        //!< Derivative of static model
-    eDynamicDerivatives = 3        //!< Derivative of dynamic model
-  };
-//------------------------------------------------------------------------------
-#endif
diff --git a/parser.src/include/SymbolTable.hh b/parser.src/include/SymbolTable.hh
index a6f0c576748e4c153a53564cc805aa7f7dbf04d4..bdd80b2a5581d372ee078f9ba4afec3e8b52bb41 100644
--- a/parser.src/include/SymbolTable.hh
+++ b/parser.src/include/SymbolTable.hh
@@ -76,15 +76,15 @@ public :
   */
   inline bool Exist(const std::string &name) const;
   /*! Gets name by type and ID */
-  inline std::string getNameByID(Type type, int id);
+  inline std::string getNameByID(Type type, int id) const;
   /*! Gets tex name by type and ID */
-  inline std::string getTexNameByID(Type type, int id);
+  inline std::string getTexNameByID(Type type, int id) const;
   /*! Gets type by name */
   inline Type getType(const std::string &name) const;
   /*! Gets ID by name */
   inline int getID(const std::string &name) const;
   //! Write output of this class
-  void writeOutput(std::ostream &output);
+  void writeOutput(std::ostream &output) const;
 };
 
 inline bool SymbolTable::Exist(const std::string &name) const
@@ -94,7 +94,7 @@ inline bool SymbolTable::Exist(const std::string &name) const
 }
 
 //------------------------------------------------------------------------------
-inline std::string  SymbolTable::getNameByID(Type type,int id)
+inline std::string  SymbolTable::getNameByID(Type type,int id) const
 {
   if (id >= 0 && (int)name_table[type].size() > id)
     return(name_table[type][id]);
@@ -102,7 +102,7 @@ inline std::string  SymbolTable::getNameByID(Type type,int id)
 }
 
 //------------------------------------------------------------------------------
-inline std::string  SymbolTable::getTexNameByID(Type type,int id)
+inline std::string  SymbolTable::getTexNameByID(Type type,int id) const
 {
   if (id >= 0 && (int)tex_name_table[type].size() > id)
     return(tex_name_table[type][id]);