From 92d3b8166ab7df1bdfabca416efad6246c4e6399 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org>
Date: Thu, 5 May 2022 18:39:27 +0200
Subject: [PATCH] C++17 modernization: use std::optional for equation line
 numbers

---
 src/DynamicModel.cc | 49 ++++++++++++++++++++++++---------------------
 src/DynamicModel.hh |  8 ++++----
 src/ModelTree.cc    | 18 ++++++++++-------
 src/ModelTree.hh    | 10 +++++----
 src/SubModel.cc     |  2 +-
 5 files changed, 48 insertions(+), 39 deletions(-)

diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc
index 15daa76c..1fa87a1f 100644
--- a/src/DynamicModel.cc
+++ b/src/DynamicModel.cc
@@ -2475,7 +2475,7 @@ vector<int>
 DynamicModel::removeEquationsHelper(set<pair<string, string>> &listed_eqs_by_tag, bool exclude_eqs,
                                     bool excluded_vars_change_type,
                                     vector<BinaryOpNode *> &all_equations,
-                                    vector<int> &all_equations_lineno,
+                                    vector<optional<int>> &all_equations_lineno,
                                     EquationTags &all_equation_tags, bool static_equations) const
 {
   if (all_equations.empty())
@@ -2507,7 +2507,7 @@ DynamicModel::removeEquationsHelper(set<pair<string, string>> &listed_eqs_by_tag
 
   // remove from equations, equations_lineno, equation_tags
   vector<BinaryOpNode *> new_equations;
-  vector<int> new_equations_lineno;
+  vector<optional<int>> new_equations_lineno;
   map<int, int> old_eqn_num_2_new;
   vector<int> excluded_vars;
   for (size_t i = 0; i < all_equations.size(); i++)
@@ -3980,7 +3980,7 @@ DynamicModel::computePacModelConsistentExpectationSubstitution(const string &nam
       auto neweq = AddEqual(const_cast<VariableNode *>(target_base_diff_node),
                             AddMinus(create_target_lag(0),
                                      create_target_lag(-1)));
-      addEquation(neweq, -1);
+      addEquation(neweq, nullopt);
       addAuxEquation(neweq);
       neqs++;
     }
@@ -3994,7 +3994,7 @@ DynamicModel::computePacModelConsistentExpectationSubstitution(const string &nam
                                                          last_aux_var->symb_id, 1);
       VariableNode *current_aux_var = AddVariable(symb_id);
       auto neweq = AddEqual(current_aux_var, AddVariable(last_aux_var->symb_id, 1));
-      addEquation(neweq, -1);
+      addEquation(neweq, nullopt);
       addAuxEquation(neweq);
       last_aux_var = current_aux_var;
       target_aux_var_to_add[i] = current_aux_var;
@@ -4023,7 +4023,7 @@ DynamicModel::computePacModelConsistentExpectationSubstitution(const string &nam
     }
   auto neweq = AddEqual(AddVariable(mce_z1_symb_id),
                         AddMinus(AddTimes(A, AddMinus(const_cast<VariableNode *>(target_base_diff_node), fs)), fp));
-  addEquation(neweq, -1);
+  addEquation(neweq, nullopt);
   /* This equation is not added to the list of auxiliary equations, because it
      is recursive, and this would in particular break dynamic_set_auxiliary_series.m */
   neqs++;
@@ -4087,7 +4087,7 @@ DynamicModel::computePacBackwardExpectationSubstitution(const string &name,
     auxname = "pac_expectation_" + name;
   int expect_var_id = symbol_table.addPacExpectationAuxiliaryVar(auxname, subExpr);
   expr_t neweq = AddEqual(AddVariable(expect_var_id), subExpr);
-  addEquation(neweq, -1);
+  addEquation(neweq, nullopt);
   addAuxEquation(neweq);
   pac_aux_var_symb_ids[name] = expect_var_id;
   pac_expectation_substitution[name] = AddVariable(expect_var_id);
@@ -4155,7 +4155,7 @@ DynamicModel::computePacBackwardExpectationSubstitutionWithComponents(const stri
 
       // Add the equation defining the auxiliary variable for this component
       expr_t neweq = AddEqual(auxvar, auxdef);
-      addEquation(neweq, -1);
+      addEquation(neweq, nullopt);
       addAuxEquation(neweq);
 
       // Update the expression to be substituted for the pac_expectation operator
@@ -4673,7 +4673,7 @@ DynamicModel::computeRamseyPolicyFOCs(const StaticModel &static_model)
 
   // Add Planner Objective to equations so that it appears in Lagrangian
   assert(static_model.equations.size() == 1);
-  addEquation(static_model.equations[0]->clone(*this), -1);
+  addEquation(static_model.equations[0]->clone(*this), nullopt);
 
   // Get max endo lead and max endo lag
   set<pair<int, int>> dynvars;
@@ -4721,14 +4721,14 @@ DynamicModel::computeRamseyPolicyFOCs(const StaticModel &static_model)
 
   // Prepare derivation of the Lagrangian
   clearEquations();
-  addEquation(AddEqual(lagrangian, Zero), -1);
+  addEquation(AddEqual(lagrangian, Zero), nullopt);
   computeDerivIDs();
 
   /* Compute Lagrangian derivatives.
      Also restore line numbers and tags for FOCs w.r.t. a Lagrange multiplier
      (i.e. a FOC identical to an equation of the original model) */
   vector<expr_t> neweqs;
-  vector<int> neweqs_lineno;
+  vector<optional<int>> neweqs_lineno;
   map<int, map<string, string>> neweqs_tags;
   for (auto &[symb_id_and_lag, deriv_id] : deriv_id_table)
     {
@@ -4748,7 +4748,7 @@ DynamicModel::computeRamseyPolicyFOCs(const StaticModel &static_model)
               neweqs_tags[neweqs.size()-1] = tags;
             }
           else
-            neweqs_lineno.push_back(-1);
+            neweqs_lineno.push_back(nullopt);
         }
     }
 
@@ -5060,8 +5060,10 @@ DynamicModel::testTrendDerivativesEqualToZero(const eval_context_t &eval_context
                     double nearZero = testeq->getDerivative(endogit.second)->eval(eval_context); // eval d F / d Trend d Endog
                     if (fabs(nearZero) > balanced_growth_test_tol)
                       {
-                        cerr << "ERROR: trends not compatible with balanced growth path; the second-order cross partial of equation " << eq + 1 << " (line "
-                             << equations_lineno[eq] << ") w.r.t. trend variable "
+                        cerr << "ERROR: trends not compatible with balanced growth path; the second-order cross partial of equation " << eq + 1;
+                        if (equations_lineno[eq])
+                          cerr << " (line " << *equations_lineno[eq] << ")";
+                        cerr << "w.r.t. trend variable "
                              << symbol_table.getName(it.first.first) << " and endogenous variable "
                              << symbol_table.getName(endogit.first.first) << " is not null (abs. value = "
                              << fabs(nearZero) << "). If you are confident that your trends are correctly specified, you can raise the value of option 'balanced_growth_test_tol' in the 'model' block." << endl;
@@ -5505,7 +5507,7 @@ DynamicModel::substituteLeadLagInternal(AuxVarType type, bool deterministic_mode
   // Add new equations
   for (auto &neweq : neweqs)
     {
-      addEquation(neweq, -1);
+      addEquation(neweq, nullopt);
       aux_equations.push_back(neweq);
     }
 
@@ -5652,7 +5654,7 @@ DynamicModel::substituteUnaryOps(const set<int> &eqnumbers, VarExpectationModelT
   // Add new equations
   for (auto &neweq : neweqs)
     {
-      addEquation(neweq, -1);
+      addEquation(neweq, nullopt);
       aux_equations.push_back(neweq);
     }
 
@@ -5707,7 +5709,7 @@ DynamicModel::substituteDiff(VarExpectationModelTable &var_expectation_model_tab
   // Add new equations
   for (auto &neweq : neweqs)
     {
-      addEquation(neweq, -1);
+      addEquation(neweq, nullopt);
       aux_equations.push_back(neweq);
     }
 
@@ -5740,7 +5742,7 @@ DynamicModel::substituteExpectation(bool partial_information_model)
   // Add new equations
   for (auto &neweq : neweqs)
     {
-      addEquation(neweq, -1);
+      addEquation(neweq, nullopt);
       aux_equations.push_back(neweq);
     }
 
@@ -5800,7 +5802,7 @@ DynamicModel::substituteLogTransform()
       */
       addAuxEquation(AddEqual(AddVariable(aux_symb_id), aux_def));
       addEquation(AddEqual(AddVariable(symb_id), AddExp(AddVariable(aux_symb_id))),
-                  -1, {});
+                  nullopt, {});
     }
 }
 
@@ -5920,14 +5922,14 @@ DynamicModel::fillEvalContext(eval_context_t &eval_context) const
 }
 
 void
-DynamicModel::addStaticOnlyEquation(expr_t eq, int lineno, const map<string, string> &eq_tags)
+DynamicModel::addStaticOnlyEquation(expr_t eq, optional<int> lineno, const map<string, string> &eq_tags)
 {
   auto beq = dynamic_cast<BinaryOpNode *>(eq);
   assert(beq && beq->op_code == BinaryOpcode::equal);
 
   static_only_equations_equation_tags.add(static_only_equations.size(), eq_tags);
   static_only_equations.push_back(beq);
-  static_only_equations_lineno.push_back(lineno);
+  static_only_equations_lineno.push_back(move(lineno));
 }
 
 size_t
@@ -5943,7 +5945,7 @@ DynamicModel::dynamicOnlyEquationsNbr() const
 }
 
 void
-DynamicModel::addOccbinEquation(expr_t eq, int lineno, const map<string, string> &eq_tags, const vector<string> &regimes_bind, const vector<string> &regimes_relax)
+DynamicModel::addOccbinEquation(expr_t eq, optional<int> lineno, const map<string, string> &eq_tags, const vector<string> &regimes_bind, const vector<string> &regimes_relax)
 {
   auto beq = dynamic_cast<BinaryOpNode *>(eq);
   assert(beq && beq->op_code == BinaryOpcode::equal);
@@ -6080,8 +6082,9 @@ DynamicModel::writeJsonAST(ostream &output) const
       if (eq != 0)
         output << ", ";
 
-      output << R"({ "number":)" << eq
-             << R"(, "line":)" << equations_lineno[eq];
+      output << R"({ "number":)" << eq;
+      if (equations_lineno[eq])
+        output << R"(, "line":)" << *equations_lineno[eq];
 
       equation_tags.writeJsonAST(output, eq);
 
diff --git a/src/DynamicModel.hh b/src/DynamicModel.hh
index d858b739..c53cf524 100644
--- a/src/DynamicModel.hh
+++ b/src/DynamicModel.hh
@@ -51,7 +51,7 @@ private:
   vector<BinaryOpNode *> static_only_equations;
 
   //! Stores line numbers of equations declared as [static]
-  vector<int> static_only_equations_lineno;
+  vector<optional<int>> static_only_equations_lineno;
 
   //! Stores the equation tags of equations declared as [static]
   EquationTags static_only_equations_equation_tags;
@@ -283,7 +283,7 @@ private:
   vector<int> removeEquationsHelper(set<pair<string, string>> &listed_eqs_by_tag,
                                     bool exclude_eqs, bool excluded_vars_change_type,
                                     vector<BinaryOpNode *> &all_equations,
-                                    vector<int> &all_equations_lineno,
+                                    vector<optional<int>> &all_equations_lineno,
                                     EquationTags &all_equation_tags,
                                     bool static_equations) const;
 
@@ -444,7 +444,7 @@ public:
   void replaceMyEquations(DynamicModel &dynamic_model) const;
 
   //! Adds an equation marked as [static]
-  void addStaticOnlyEquation(expr_t eq, int lineno, const map<string, string> &eq_tags);
+  void addStaticOnlyEquation(expr_t eq, optional<int> lineno, const map<string, string> &eq_tags);
 
   //! Returns number of static only equations
   size_t staticOnlyEquationsNbr() const;
@@ -457,7 +457,7 @@ public:
      auxiliary parameters have already been added to the symbol table.
      It also assumes that the “bind” and “relax” tags have been cleared from
      eq_tags. */
-  void addOccbinEquation(expr_t eq, int lineno, const map<string, string> &eq_tags, const vector<string> &regimes_bind, const vector<string> &regimes_relax);
+  void addOccbinEquation(expr_t eq, optional<int> lineno, const map<string, string> &eq_tags, const vector<string> &regimes_bind, const vector<string> &regimes_relax);
 
   //! Writes LaTeX file with the equations of the dynamic model
   void writeLatexFile(const string &basename, bool write_equation_tags) const;
diff --git a/src/ModelTree.cc b/src/ModelTree.cc
index 1998fbfa..d82a32b7 100644
--- a/src/ModelTree.cc
+++ b/src/ModelTree.cc
@@ -343,7 +343,10 @@ ModelTree::evaluateAndReduceJacobian(const eval_context_t &eval_context) const
             }
           catch (ExprNode::EvalException &e)
             {
-              cerr << "ERROR: evaluation of Jacobian failed for equation " << eq+1 << " (line " << equations_lineno[eq] << ") and variable " << symbol_table.getName(symb) << "(" << lag << ") [" << symb << "] !" << endl;
+              cerr << "ERROR: evaluation of Jacobian failed for equation " << eq+1;
+              if (equations_lineno[eq])
+                cerr << " (line " << *equations_lineno[eq] << ")";
+              cerr << " and variable " << symbol_table.getName(symb) << "(" << lag << ") [" << symb << "] !" << endl;
               d1->writeOutput(cerr, ExprNodeOutputType::matlabDynamicModel, {}, {});
               cerr << endl;
               exit(EXIT_FAILURE);
@@ -1539,13 +1542,13 @@ ModelTree::writeLatexModelFile(const string &mod_basename, const string &latex_b
 }
 
 void
-ModelTree::addEquation(expr_t eq, int lineno)
+ModelTree::addEquation(expr_t eq, optional<int> lineno)
 {
   auto beq = dynamic_cast<BinaryOpNode *>(eq);
   assert(beq && beq->op_code == BinaryOpcode::equal);
 
   equations.push_back(beq);
-  equations_lineno.push_back(lineno);
+  equations_lineno.push_back(move(lineno));
 }
 
 void
@@ -1558,10 +1561,10 @@ ModelTree::findConstantEquationsWithoutMcpTag(map<VariableNode *, NumConstNode *
 }
 
 void
-ModelTree::addEquation(expr_t eq, int lineno, const map<string, string> &eq_tags)
+ModelTree::addEquation(expr_t eq, optional<int> lineno, const map<string, string> &eq_tags)
 {
   equation_tags.add(equations.size(), eq_tags);
-  addEquation(eq, lineno);
+  addEquation(eq, move(lineno));
 }
 
 void
@@ -1746,8 +1749,9 @@ ModelTree::writeJsonModelEquations(ostream &output, bool residuals) const
           lhs->writeJsonOutput(output, {}, {});
           output << R"(", "rhs": ")";
           rhs->writeJsonOutput(output, {}, {});
-          output << R"(")"
-                 << R"(, "line": )" << equations_lineno[eq];
+          output << R"(")";
+          if (equations_lineno[eq])
+            output << R"(, "line": )" << *equations_lineno[eq];
 
           if (auto eqtags = getEquationTags(eq);
               !eqtags.empty())
diff --git a/src/ModelTree.hh b/src/ModelTree.hh
index c7f17aea..5aeee110 100644
--- a/src/ModelTree.hh
+++ b/src/ModelTree.hh
@@ -27,6 +27,7 @@
 #include <ostream>
 #include <array>
 #include <filesystem>
+#include <optional>
 
 #include "DataTree.hh"
 #include "EquationTags.hh"
@@ -76,8 +77,9 @@ protected:
    */
   //! Stores declared and generated auxiliary equations
   vector<BinaryOpNode *> equations;
-  //! Stores line numbers of declared equations; -1 means undefined
-  vector<int> equations_lineno;
+  /* Stores line numbers of declared equations; undefined in some cases (e.g.
+     auxiliary equations) */
+  vector<optional<int>> equations_lineno;
   //! Stores equation tags
   EquationTags equation_tags;
   /*
@@ -422,9 +424,9 @@ public:
     default value = 0 */
   int mfs{0};
   //! Declare a node as an equation of the model; also give its line number
-  void addEquation(expr_t eq, int lineno);
+  void addEquation(expr_t eq, optional<int> lineno);
   //! Declare a node as an equation of the model, also giving its tags
-  void addEquation(expr_t eq, int lineno, const map<string, string> &eq_tags);
+  void addEquation(expr_t eq, optional<int> lineno, const map<string, string> &eq_tags);
   //! Declare a node as an auxiliary equation of the model, adding it at the end of the list of auxiliary equations
   void addAuxEquation(expr_t eq);
   //! Returns the number of equations in the model
diff --git a/src/SubModel.cc b/src/SubModel.cc
index f80b3b47..cc3074e6 100644
--- a/src/SubModel.cc
+++ b/src/SubModel.cc
@@ -1273,7 +1273,7 @@ PacModelTable::transformPass(const lag_equivalence_table_t &unary_ops_nodes,
               yns = dynamic_model.AddPlus(yns, dynamic_model.AddTimes(coeff, component));
           int target_nonstationary_id = symbol_table.addPacTargetNonstationaryAuxiliaryVar(get<1>(target_info[name]), yns);
           expr_t neweq = dynamic_model.AddEqual(dynamic_model.AddVariable(target_nonstationary_id), yns);
-          dynamic_model.addEquation(neweq, -1);
+          dynamic_model.addEquation(neweq, nullopt);
           dynamic_model.addAuxEquation(neweq);
 
           /* Perform the substitution of the pac_target_nonstationary operator.
-- 
GitLab